-
Notifications
You must be signed in to change notification settings - Fork 100
/
rm.go
764 lines (663 loc) · 25.2 KB
/
rm.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
package lib
import (
"fmt"
"strings"
oss "github.com/aliyun/aliyun-oss-go-sdk/oss"
)
type uploadIdInfoType struct {
key string
uploadId string
}
type removeOptionType struct {
recursive bool
force bool
typeSet int64
}
var specChineseRemove = SpecText{
synopsisText: "删除Bucket或Objects",
paramText: "cloud_url [options]",
syntaxText: `
ossutil rm oss://bucket[/prefix] [-r] [-b] [-f] [-c file]
`,
detailHelpText: `
该命令删除Bucket或objects,在某些情况下可一并删除二者。请小心使用该命令!!
在删除objects前确定objects可以删除,在删除bucket前确定整个bucket连同其下的所有
objects都可以删除!
(1)删除单个object,参考用法1)
(2)删除bucket,不删除objects,参考用法2)
(3)删除objects,不删除bucket,参考用法3)
(4)删除bucket和objects,参考用法4)
对bucket进行删除,都需要添加--bucket选项。
如果指定了--force选项,则删除前不会进行询问提示。
结果:显示命令耗时前未报错,则表示成功删除。
默认情况下,删除object时,不包括以指定object名称进行的未complete的Multipart Upload
事件。如果用户需要删除指定object名称下的所有未complete的Multipart Upload事件,需要
指定--multipart选项(ossutil会删除所有匹配的Multipart Upload事件,但不支持删除特定
的某个Multipart Upload事件)。
如果要同时删除object和相应的Multipart Upload事件,需要指定--all-type选项。
注意:删除未complete的Multipart Upload事件可能造成下次上传相同的UploadId失败,由于
cp命令使用Multipart来进行断点续传,删除未complete的Multipart Upload事件可能造成cp
命令断点续传失败(报错:NoSuchUpload),这种时候如果想要重新上传整个文件,请删除
checkpoint目录中相应的文件。
用法:
该命令有四种用法:
1) ossutil rm oss://bucket/object [-m] [-a]
(删除单个object)
如果未指定--recursive和--bucket选项,删除指定的单个object,此时请确保cloud_url
精确指定了待删除的object,ossutil不会进行前缀匹配。无论是否指定--force选项,ossutil
都不会进行询问提示。如果此时指定了--bucket选项,将会报错,单独删除bucket参考用法4)。
如果指定了--multipart选项, 删除指定object下未complete的Multipart Upload事件。
如果指定了--all-type选项, 删除指定object以及其下未complete的Multipart Upload事件。
2) ossutil rm oss://bucket -b [-f]
(删除bucket,不删除objects)
如果指定了--bucket选项,未指定--recursive选项,ossutil删除指定的bucket,但并不去
删除该bucket下的objects。此时请确保cloud_url精确匹配待删除的bucket,并且指定的bucket
内容为空,否则会报错。如果指定了--force选项,则删除前不会进行询问提示。
3) ossutil rm oss://bucket[/prefix] -r [-m] [-a] [-f]
(删除objects,不删除bucket)
如果指定了--recursive选项,未指定--bucket选项。则可以进行objects的批量删除。该用
法查找与指定cloud_url前缀匹配的所有objects(prefix为空代表bucket下的所有objects),删
除这些objects。由于未指定--bucket选项,则ossutil保留bucket。如果指定了--force选项,则
删除前不会进行询问提示。
如果指定了--multipart选项,删除以指定prefix开头的所有object下的未complete的Multipart
Upload任务。
如果指定了--all-type,删除以指定prefix开头的所有object,以及其下的所有未complete
的Multipart Upload任务。
4) ossutil rm oss://bucket[/prefix] -r -b [-m] [-a] [-f]
(删除bucket和objects)
如果同时指定了--bucket和--recursive选项,ossutil进行批量删除后会尝试去一并删除
bucket。当用户想要删除某个bucket连同其中的所有objects时,可采用该操作。如果指定了
--force选项,则删除前不会进行询问提示。
如果指定了--multipart选项,删除以指定prefix开头的所有object下的未complete的Multipart
Upload任务。
如果指定了--all-type, 删除以指定prefix开头的所有object,以及其下的所有未complete
的Multipart Upload任务。
不支持的用法:
1) ossutil rm oss://bucket/object -m -b [-f]
不能尝试删除一个指定object名称下未complete的Multipart Upload任务后紧接着删除该bucket。
2) ossutil rm oss://bucket/object -a -b [-f]
不能尝试删除一个指定的object和其下所有未complete的Multipart Upload任务后紧接着删除该bucket。
`,
sampleText: `
ossutil rm oss://bucket1/obj1
ossutil rm oss://bucket1/obj1 -m
ossutil rm oss://bucket1/obj1 -a
ossutil rm oss://bucket1/objdir -r
ossutil rm oss://bucket1/multidir -m -r
ossutil rm oss://bucket1/dir -a -r
ossutil rm oss://bucket1 -b
ossutil rm oss://bucket2 -r -b -f
ossutil rm oss://bucket2 -a -r -b -f
ossutil rm oss://bucket2/%e4%b8%ad%e6%96%87 --encoding-type url
`,
}
var specEnglishRemove = SpecText{
synopsisText: "Remove Bucket or Objects",
paramText: "cloud_url [options]",
syntaxText: `
ossutil rm oss://bucket[/prefix] [-r] [-b] [-f] [-c file]
`,
detailHelpText: `
The command remove bucket or objects, in some case remove both. Please use the
command carefully!!
Make sure the objects can be removed before useing the command to remove objects!
Make sure the bucket and objects inside can be removed before useing the command
to remove bucket!
(1) Remove single object, see usage 1)
(2) Remove bucket, don't remove objects inside, see usage 2)
(3) Batch remove many objects, reserve bucket, see usage 3)
(4) Remove bucket and objects inside, see usage 4)
When remove bucket, the --bucket option must be specified.
If --force option is specified, remove silently without asking user to confirm the
operation.
Result: if no error displayed before show elasped time, then the target is removed
successfully.
By default, when remove object, ossutil will reserve the uncompleted multipart upload
tasks whose object name match the specified cloud_url, if you want to remove those multipart
upload tasks, please specify --multipart option. Note: ossutil will remove all the multipart
upload tasks of the specified cloud_url, remove a special single multipart upload task
is unsupported.
If you need to remove object and the multipart upload tasks whose object name match the
specified cloud_url meanwhile, please use --all-type option.
Note: remove the multipart upload tasks uncompleted will cause upload the part fail next
time. Because cp command use multipart upload to realize resume upload/download/copy, so
remove the multipart upload tasks uncompleted may cause resume upload/download/copy fail
the next time(Error: NoSuchUpload). If you want to reupload/download/copy the entire file
again, please remove the checkpoint file in checkpoint directory.
Usage:
There are four usages:
1) ossutil rm oss://bucket/object
(Remove single object)
If you remove without --recursive and --bucket option, ossutil remove the single
object specified in cloud_url. In the usage, please make sure cloud_url exactly specified
the object you want to remove, ossutil will not treat object as prefix and remove prefix
matching objects. No matter --force is specified or not, ossutil will not show prompt
question.
If --multipart option is specified, ossutil will remove the multipart upload tasks
of the specified object.
If --all-type option is specified, ossutil will remove the specified object along
with the multipart upload tasks of the specified object.
2) ossutil rm oss://bucket -b [-f]
(Remove bucket, don't remove objects inside)
If you remove with --bucket option, without --recursive option, ossutil try to
remove the bucket, if the bucket is not empty, error occurs. In the usage, please make
sure cloud_url exactly specified the bucket you want to remove, or error occurs. If --force
option is specified, ossutil will not show prompt question.
3) ossutil rm oss://bucket[/prefix] -r [-m] [-a] [-f]
(Remove objects, reserve bucket)
If you remove with --recursive option, without --bucket option, ossutil remove all
the objects that prefix-matching the cloud_url you specified(empty prefix means all
objects in the bucket), bucket will be reserved because of missing --bucket option.
If --multipart option is specified, ossutil will remove the multipart upload tasks
whose object name start with the specified prefix.
If --all-type option is specified, ossutil will remove the objects with the specified
prefix along with the multipart upload tasks whose object name start with the specified
prefix.
4) ossutil rm oss://bucket[/prefix] -r -b [-a] [-f]
(Remove bucket and objects inside)
If you remove with both --recursive and --bucket option, after ossutil removed all
the prefix-matching objects, ossutil will try to remove the bucket together. If user want
to remove bucket and objects inside, the usage is recommended. If --force option is
specified, ossutil will not show prompt question.
If --multipart option is specified, ossutil will remove the multipart upload tasks
whose object name start with the specified prefix.
If --all-type option is specified, ossutil will remove the objects with the specified
prefix along with the multipart upload tasks whose object name start with the specified
prefix.
Invalid Usage:
1) ossutil rm oss://bucket/object -m -b [-f]
It's invalid to remove the bucket right after remove uncompleted upload tasks of single
object.
2) ossutil rm oss://bucket/object -a -b [-f]
It's invalid to remove the bucket right after remove the object and uncompleted upload
tasks of the single object you specified.
`,
sampleText: `
ossutil rm oss://bucket1/obj1
ossutil rm oss://bucket1/obj1 -m
ossutil rm oss://bucket1/obj1 -a
ossutil rm oss://bucket1/objdir -r
ossutil rm oss://bucket1/multidir -m -r
ossutil rm oss://bucket1/dir -a -r
ossutil rm oss://bucket1 -b
ossutil rm oss://bucket2 -r -b -f
ossutil rm oss://bucket2 -a -r -b -f
ossutil rm oss://bucket2/%e4%b8%ad%e6%96%87 --encoding-type url
`,
}
// RemoveCommand is the command remove bucket or objects
type RemoveCommand struct {
monitor RMMonitor //Put first for atomic op on some fileds
command Command
rmOption removeOptionType
}
var removeCommand = RemoveCommand{
command: Command{
name: "rm",
nameAlias: []string{"remove", "delete", "del"},
minArgc: 1,
maxArgc: 1,
specChinese: specChineseRemove,
specEnglish: specEnglishRemove,
group: GroupTypeNormalCommand,
validOptionNames: []string{
OptionRecursion,
OptionBucket,
OptionForce,
OptionMultipart,
OptionAllType,
OptionEncodingType,
OptionConfigFile,
OptionEndpoint,
OptionAccessKeyID,
OptionAccessKeySecret,
OptionSTSToken,
OptionRetryTimes,
OptionLogLevel,
},
},
}
// function for FormatHelper interface
func (rc *RemoveCommand) formatHelpForWhole() string {
return rc.command.formatHelpForWhole()
}
func (rc *RemoveCommand) formatIndependHelp() string {
return rc.command.formatIndependHelp()
}
// Init simulate inheritance, and polymorphism
func (rc *RemoveCommand) Init(args []string, options OptionMapType) error {
return rc.command.Init(args, options, rc)
}
// RunCommand simulate inheritance, and polymorphism
func (rc *RemoveCommand) RunCommand() error {
rc.monitor.init()
encodingType, _ := GetString(OptionEncodingType, rc.command.options)
cloudURL, err := CloudURLFromString(rc.command.args[0], encodingType)
if err != nil {
return err
}
if cloudURL.bucket == "" {
return fmt.Errorf("invalid cloud url: %s, miss bucket", rc.command.args[0])
}
bucket, err := rc.command.ossBucket(cloudURL.bucket)
if err != nil {
return err
}
// assembleOption
if err := rc.assembleOption(cloudURL); err != nil {
return err
}
// confirm remove objects/multiparts/allTypes before statistic
if !rc.confirmRemoveObject(cloudURL) {
return nil
}
// start progressbar
go rc.entryStatistic(bucket, cloudURL)
exitStat := normalExit
if err = rc.removeEntry(bucket, cloudURL); err != nil {
exitStat = errExit
}
fmt.Printf(rc.monitor.progressBar(true, exitStat))
return err
}
func (rc *RemoveCommand) assembleOption(cloudURL CloudURL) error {
rc.rmOption.recursive, _ = GetBool(OptionRecursion, rc.command.options)
rc.rmOption.force, _ = GetBool(OptionForce, rc.command.options)
isMultipart, _ := GetBool(OptionMultipart, rc.command.options)
isAllType, _ := GetBool(OptionAllType, rc.command.options)
toBucket, _ := GetBool(OptionBucket, rc.command.options)
if err := rc.checkOption(cloudURL, isMultipart, isAllType, toBucket); err != nil {
return err
}
rc.rmOption.typeSet = 0
if isMultipart {
rc.rmOption.typeSet |= multipartType
}
if isAllType {
rc.rmOption.typeSet |= allType
}
if toBucket {
rc.rmOption.typeSet |= bucketType
}
if !rc.rmOption.recursive {
if rc.rmOption.typeSet == 0 {
rc.rmOption.typeSet |= objectType
}
} else {
if rc.rmOption.typeSet&allType == 0 {
rc.rmOption.typeSet |= objectType
}
}
return nil
}
func (rc *RemoveCommand) checkOption(cloudURL CloudURL, isMultipart, isAllType, toBucket bool) error {
if !rc.rmOption.recursive {
if !toBucket {
// "rm -a/m" miss object, invalid
if cloudURL.object == "" {
return fmt.Errorf("remove bucket, miss --bucket option, if you mean remove object, invalid url: %s, miss object", rc.command.args[0])
}
} else {
if isMultipart || isAllType {
// "rm -mb" and "rm -ab", with or without object, both invalid
if cloudURL.object == "" {
return fmt.Errorf("remove bucket redundant option: --multipart or --all-type, if you mean remove all objects and the bucket meanwhile, you should add --recursive option")
} else {
return fmt.Errorf("remove object redundant option: --bucket, remove bucket after remove single object is not supported")
}
} else if cloudURL.object != "" {
// "rm -b" with object, invalid
return fmt.Errorf("remove bucket invalid url: %s, object not empty, if you mean remove object, you should not use --bucket option", rc.command.args[0])
}
}
}
return nil
}
func (rc *RemoveCommand) confirmRemoveObject(cloudURL CloudURL) bool {
if !rc.rmOption.force && rc.rmOption.recursive && rc.rmOption.typeSet&allType != 0 {
stringList := []string{}
if rc.rmOption.typeSet&objectType != 0 {
stringList = append(stringList, "objects")
}
if rc.rmOption.typeSet&multipartType != 0 {
stringList = append(stringList, "multipart uploadIds")
}
var val string
fmt.Printf("Do you really mean to remove recursively %s of %s(y or N)? ", strings.Join(stringList, " and "), rc.command.args[0])
if _, err := fmt.Scanln(&val); err != nil || (strings.ToLower(val) != "yes" && strings.ToLower(val) != "y") {
fmt.Println("operation is canceled.")
return false
}
return true
}
return true
}
func (rc *RemoveCommand) entryStatistic(bucket *oss.Bucket, cloudURL CloudURL) {
if rc.rmOption.typeSet&objectType != 0 {
rc.objectStatistic(bucket, cloudURL)
}
if rc.rmOption.typeSet&multipartType != 0 {
rc.multipartUploadsStatistic(bucket, cloudURL)
}
rc.monitor.setScanEnd()
}
func (rc *RemoveCommand) objectStatistic(bucket *oss.Bucket, cloudURL CloudURL) error {
// single object statistic before remove
if rc.rmOption.recursive {
return rc.batchObjectStatistic(bucket, cloudURL)
}
return nil
}
func (rc *RemoveCommand) touchObject(bucket *oss.Bucket, cloudURL CloudURL) (bool, error) {
exist, err := rc.ossIsObjectExistRetry(bucket, cloudURL.object)
if err != nil {
rc.monitor.setScanError(err)
} else if exist {
rc.monitor.updateScanNum(1)
}
return exist, err
}
func (rc *RemoveCommand) ossIsObjectExistRetry(bucket *oss.Bucket, object string) (bool, error) {
retryTimes, _ := GetInt(OptionRetryTimes, rc.command.options)
for i := 1; ; i++ {
exist, err := bucket.IsObjectExist(object)
if err == nil {
return exist, err
}
if int64(i) >= retryTimes {
return false, ObjectError{err, bucket.BucketName, object}
}
}
}
func (rc *RemoveCommand) batchObjectStatistic(bucket *oss.Bucket, cloudURL CloudURL) error {
pre := oss.Prefix(cloudURL.object)
marker := oss.Marker("")
for {
lor, err := rc.command.ossListObjectsRetry(bucket, marker, pre)
if err != nil {
rc.monitor.setScanError(err)
return err
}
rc.monitor.updateScanNum(int64(len(lor.Objects)))
pre = oss.Prefix(lor.Prefix)
marker = oss.Marker(lor.NextMarker)
if !lor.IsTruncated {
break
}
}
return nil
}
func (rc *RemoveCommand) multipartUploadsStatistic(bucket *oss.Bucket, cloudURL CloudURL) error {
pre := oss.Prefix(cloudURL.object)
keyMarker := oss.KeyMarker("")
uploadIdMarker := oss.UploadIDMarker("")
for {
lmr, err := rc.command.ossListMultipartUploadsRetry(bucket, keyMarker, uploadIdMarker, pre)
if err != nil {
rc.monitor.setScanError(err)
return err
}
if rc.rmOption.recursive {
rc.monitor.updateScanUploadIdNum(int64(len(lmr.Uploads)))
} else {
for _, uploadId := range lmr.Uploads {
if uploadId.Key == cloudURL.object {
rc.monitor.updateScanUploadIdNum(1)
} else {
break
}
}
}
pre = oss.Prefix(lmr.Prefix)
keyMarker = oss.KeyMarker(lmr.NextKeyMarker)
uploadIdMarker = oss.UploadIDMarker(lmr.NextUploadIDMarker)
if !lmr.IsTruncated {
break
}
}
return nil
}
func (rc *RemoveCommand) removeEntry(bucket *oss.Bucket, cloudURL CloudURL) error {
// op control whether to show progress bar of the type,
// but do not control whether to record the ok/error num of the type,
// so the show and record can be separated.
rc.monitor.updateOP(rc.rmOption.typeSet & allType)
if rc.rmOption.typeSet&objectType != 0 {
if err := rc.removeObjectEntry(bucket, cloudURL); err != nil {
return err
}
}
if rc.rmOption.typeSet&multipartType != 0 {
if err := rc.removeMultipartUploadsEntry(bucket, cloudURL); err != nil {
return err
}
}
if rc.rmOption.typeSet&bucketType != 0 {
return rc.removeBucket(bucket, cloudURL)
}
return nil
}
func (rc *RemoveCommand) removeObjectEntry(bucket *oss.Bucket, cloudURL CloudURL) error {
if !rc.rmOption.recursive {
return rc.removeObject(bucket, cloudURL)
} else {
return rc.batchDeleteObjects(bucket, cloudURL)
}
}
func (rc *RemoveCommand) removeObject(bucket *oss.Bucket, cloudURL CloudURL) error {
// single object statistic before remove to avoid inconsistency
exist, err := rc.touchObject(bucket, cloudURL)
if err != nil || exist {
err = rc.deleteObjectWithMonitor(bucket, cloudURL.object)
if err != nil && rc.monitor.op == objectType {
// remove single object error, return error information, do not print progressbar
rc.monitor.setOP(0)
}
return err
}
return nil
}
func (rc *RemoveCommand) deleteObjectWithMonitor(bucket *oss.Bucket, object string) error {
err := rc.ossDeleteObjectRetry(bucket, object)
if err == nil {
rc.updateObjectMonitor(1, 0)
} else {
rc.updateObjectMonitor(0, 1)
}
return err
}
func (rc *RemoveCommand) ossDeleteObjectRetry(bucket *oss.Bucket, object string) error {
retryTimes, _ := GetInt(OptionRetryTimes, rc.command.options)
for i := 1; ; i++ {
err := bucket.DeleteObject(object)
if err == nil {
return err
}
if int64(i) >= retryTimes {
return ObjectError{err, bucket.BucketName, object}
}
}
}
func (rc *RemoveCommand) updateObjectMonitor(okNum, errNum int64) {
rc.monitor.updateObjectNum(okNum)
rc.monitor.updateErrObjectNum(errNum)
fmt.Printf(rc.monitor.progressBar(false, normalExit))
}
func (rc *RemoveCommand) batchDeleteObjects(bucket *oss.Bucket, cloudURL CloudURL) error {
// list objects
pre := oss.Prefix(cloudURL.object)
marker := oss.Marker("")
for {
lor, err := rc.command.ossListObjectsRetry(bucket, marker, pre)
if err != nil {
return err
}
// batch delete
delNum, err := rc.ossBatchDeleteObjectsRetry(bucket, rc.getObjectsFromListResult(lor))
rc.updateObjectMonitor(int64(delNum), int64(len(lor.Objects)-delNum))
if err != nil {
return err
}
pre = oss.Prefix(lor.Prefix)
marker = oss.Marker(lor.NextMarker)
if !lor.IsTruncated {
break
}
}
return nil
}
func (rc *RemoveCommand) ossBatchDeleteObjectsRetry(bucket *oss.Bucket, objects []string) (int, error) {
retryTimes, _ := GetInt(OptionRetryTimes, rc.command.options)
num := len(objects)
if num <= 0 {
return 0, nil
}
for i := 1; ; i++ {
delRes, err := bucket.DeleteObjects(objects, oss.DeleteObjectsQuiet(true))
if err == nil && len(delRes.DeletedObjects) == 0 {
return num, nil
}
if int64(i) >= retryTimes {
if err != nil {
return num - len(objects), err
}
return num - len(delRes.DeletedObjects), fmt.Errorf("delete objects: %s failed", delRes.DeletedObjects)
}
objects = delRes.DeletedObjects
}
}
func (rc *RemoveCommand) getObjectsFromListResult(lor oss.ListObjectsResult) []string {
objects := []string{}
for _, object := range lor.Objects {
objects = append(objects, object.Key)
}
return objects
}
func (rc *RemoveCommand) removeMultipartUploadsEntry(bucket *oss.Bucket, cloudURL CloudURL) error {
routines := 1
chUploadIds := make(chan uploadIdInfoType, ChannelBuf)
chError := make(chan error, routines+1)
chListError := make(chan error, 1)
go rc.multipartUploadsProducer(bucket, cloudURL, chUploadIds, chListError)
for i := 0; i < routines; i++ {
go rc.abortMultipartUploadConsumer(bucket, chUploadIds, chError)
}
completed := 0
for completed <= routines {
select {
case err := <-chListError:
if err != nil {
return err
}
completed++
case err := <-chError:
if err != nil {
return err
}
completed++
}
}
return nil
}
func (rc *RemoveCommand) multipartUploadsProducer(bucket *oss.Bucket, cloudURL CloudURL, chUploadIds chan<- uploadIdInfoType, chListError chan<- error) {
pre := oss.Prefix(cloudURL.object)
keyMarker := oss.KeyMarker("")
uploadIdMarker := oss.UploadIDMarker("")
for {
lmr, err := rc.command.ossListMultipartUploadsRetry(bucket, keyMarker, uploadIdMarker, pre)
if err != nil {
chListError <- err
break
}
for _, uploadId := range lmr.Uploads {
if !rc.rmOption.recursive && uploadId.Key != cloudURL.object {
break
}
chUploadIds <- uploadIdInfoType{uploadId.Key, uploadId.UploadID}
}
pre = oss.Prefix(lmr.Prefix)
keyMarker = oss.KeyMarker(lmr.NextKeyMarker)
uploadIdMarker = oss.UploadIDMarker(lmr.NextUploadIDMarker)
if !lmr.IsTruncated {
break
}
}
defer close(chUploadIds)
chListError <- nil
}
func (rc *RemoveCommand) abortMultipartUploadConsumer(bucket *oss.Bucket, chUploadIds <-chan uploadIdInfoType, chError chan<- error) {
for uploadIdInfo := range chUploadIds {
err := rc.ossAbortMultipartUploadRetry(bucket, uploadIdInfo.key, uploadIdInfo.uploadId)
rc.updateUploadIdMonitor(err)
if err != nil {
chError <- err
return
}
}
chError <- nil
}
func (rc *RemoveCommand) updateUploadIdMonitor(err error) {
if err == nil {
rc.monitor.updateUploadIdNum(1)
} else {
rc.monitor.updateErrUploadIdNum(1)
}
fmt.Printf(rc.monitor.progressBar(false, normalExit))
}
func (rc *RemoveCommand) ossAbortMultipartUploadRetry(bucket *oss.Bucket, key, uploadId string) error {
var imur = oss.InitiateMultipartUploadResult{Bucket: bucket.BucketName, Key: key, UploadID: uploadId}
retryTimes, _ := GetInt(OptionRetryTimes, rc.command.options)
for i := 1; ; i++ {
err := bucket.AbortMultipartUpload(imur)
if err == nil {
return err
}
switch err.(type) {
case oss.ServiceError:
if err.(oss.ServiceError).Code == "NoSuchUpload" {
return nil
}
}
if int64(i) >= retryTimes {
return ObjectError{err, bucket.BucketName, key}
}
}
}
func (rc *RemoveCommand) removeBucket(bucket *oss.Bucket, cloudURL CloudURL) error {
if !rc.confirmRemoveBucket(cloudURL) {
return nil
}
rc.monitor.updateOP(bucketType)
err := rc.ossDeleteBucketRetry(&bucket.Client, cloudURL.bucket)
if err == nil {
rc.monitor.updateRemovedBucket(cloudURL.bucket)
}
return err
}
func (rc *RemoveCommand) confirmRemoveBucket(cloudURL CloudURL) bool {
if !rc.rmOption.force {
var val string
fmt.Printf(getClearStr(fmt.Sprintf("Do you really mean to remove the Bucket: %s(y or N)? ", cloudURL.bucket)))
if _, err := fmt.Scanln(&val); err != nil || (strings.ToLower(val) != "yes" && strings.ToLower(val) != "y") {
fmt.Println("operation is canceled.")
return false
}
return true
}
return true
}
func (rc *RemoveCommand) ossDeleteBucketRetry(client *oss.Client, bucket string) error {
retryTimes, _ := GetInt(OptionRetryTimes, rc.command.options)
for i := 1; ; i++ {
err := client.DeleteBucket(bucket)
if err == nil {
return err
}
if int64(i) >= retryTimes {
if strings.Contains(err.Error(), "bucket you tried to delete is not empty") {
fmt.Printf("\nWhether new objects were uploaded during the deletion?\n\n")
}
return BucketError{err, bucket}
}
}
}