Skip to content

Commit 294cc50

Browse files
committed
recovery deleted object to special version.
Signed-off-by: 疯魔慕薇 <[email protected]>
1 parent 752be27 commit 294cc50

File tree

2 files changed

+178
-0
lines changed

2 files changed

+178
-0
lines changed

cmd/rov.go

+60
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
package cmd
2+
3+
import (
4+
"coscli/util"
5+
"fmt"
6+
7+
"github.com/spf13/cobra"
8+
)
9+
10+
var rovCmd = &cobra.Command{
11+
Use: "rov",
12+
Short: "recovery object version",
13+
Long: `recovery object version`,
14+
Args: cobra.MaximumNArgs(1),
15+
RunE: func(cmd *cobra.Command, args []string) error {
16+
limit, _ := cmd.Flags().GetInt("limit")
17+
dryrun, _ := cmd.Flags().GetBool("dryrun")
18+
previous, _ := cmd.Flags().GetInt("previous")
19+
20+
if limit == 0 {
21+
limit = 10000
22+
} else if limit < 0 {
23+
return fmt.Errorf("flag --limit should be greater than 0")
24+
}
25+
26+
cosPath := ""
27+
if len(args) != 0 {
28+
cosPath = args[0]
29+
}
30+
31+
cosUrl, err := util.FormatUrl(cosPath)
32+
if err != nil {
33+
return fmt.Errorf("cos url format error:%v", err)
34+
}
35+
36+
// 无参数,则列出当前账号下的所有存储桶
37+
if cosPath == "" {
38+
return fmt.Errorf("bucket name is required")
39+
} else if cosUrl.IsCosUrl() {
40+
// 实例化cos client
41+
bucketName := cosUrl.(*util.CosUrl).Bucket
42+
c, err := util.NewClient(&config, &param, bucketName)
43+
if err != nil {
44+
return err
45+
}
46+
return util.RecoveryObjectVersion(c, cosUrl.(*util.CosUrl), previous, limit, dryrun)
47+
} else {
48+
return fmt.Errorf("cospath needs to contain cos://")
49+
}
50+
51+
},
52+
}
53+
54+
func init() {
55+
rootCmd.AddCommand(rovCmd)
56+
57+
rovCmd.Flags().IntP("previous", "P", 1, "previous version to recovery. 1 means the latest version, 2 means the second latest version")
58+
rovCmd.Flags().IntP("limit", "l", 10000, "limit the number of objects to list")
59+
rovCmd.Flags().BoolP("not-dryrun", "n", false, "default dryrun mode, do not actually recovery object version")
60+
}

util/recovery_object_version.go

+118
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
package util
2+
3+
import (
4+
"context"
5+
"fmt"
6+
7+
"github.com/sirupsen/logrus"
8+
"github.com/tencentyun/cos-go-sdk-v5"
9+
)
10+
11+
// RecoveryObjectVersion recovery object version
12+
func RecoveryObjectVersion(c *cos.Client, cosUrl StorageUrl, previous int, limit int, dryrun bool) error {
13+
var total = 0
14+
var marker = ""
15+
var versionMarker = ""
16+
var isTruncated = true
17+
18+
var ctx = context.Background()
19+
var prefix = cosUrl.(*CosUrl).Object
20+
21+
for isTruncated && total < limit {
22+
var opt = &cos.BucketGetObjectVersionsOptions{
23+
Prefix: prefix,
24+
KeyMarker: marker,
25+
VersionIdMarker: versionMarker,
26+
}
27+
resp, _, err := c.Bucket.GetObjectVersions(ctx, opt)
28+
if err != nil {
29+
return err
30+
}
31+
32+
total += len(resp.Version)
33+
total += len(resp.DeleteMarker)
34+
35+
var notLatestVersions = versionKeyMap(false, resp.Version)
36+
var log = logrus.WithField("versions", notLatestVersions).
37+
WithField("previous", previous).
38+
WithField("dryrun", dryrun)
39+
40+
for key, versions := range notLatestVersions {
41+
logrus.WithField("Key", key).WithField("versions", versions).Info("find versions")
42+
}
43+
44+
for _, marker := range resp.DeleteMarker {
45+
var log = log.WithField("Key", marker.Key).
46+
WithField("marker.VersionId", marker.VersionId)
47+
48+
if marker.IsLatest {
49+
versionIds, has := notLatestVersions[marker.Key]
50+
if has && (len(versionIds) < previous || previous <= 0) {
51+
log.Warn("version not found")
52+
return nil
53+
}
54+
55+
if has {
56+
err = recoveryObject(c, marker.Key, versionIds[previous-1].VersionId, dryrun)
57+
if err != nil {
58+
log.WithError(err).Warn("recovery failed")
59+
} else {
60+
log.Info("recovery success")
61+
}
62+
} else {
63+
log.Warn("not found the latest version of delete marker")
64+
}
65+
}
66+
}
67+
68+
logrus.WithField("IsTruncated", resp.IsTruncated).WithField("NextKeyMarker", resp.NextKeyMarker).Info("DeleteMarkers")
69+
marker = resp.NextKeyMarker
70+
isTruncated = resp.IsTruncated
71+
versionMarker = resp.NextVersionIdMarker
72+
73+
if !isTruncated || total >= limit {
74+
logrus.Info("no more")
75+
break
76+
}
77+
}
78+
return nil
79+
}
80+
81+
// map[Key]Version
82+
func versionKeyMap(latest bool, versions []cos.ListVersionsResultVersion) map[string][]cos.ListVersionsResultVersion {
83+
var versionMap = make(map[string][]cos.ListVersionsResultVersion)
84+
for _, version := range versions {
85+
if version.IsLatest == latest {
86+
versionMap[version.Key] = append(versionMap[version.Key], version)
87+
} else {
88+
}
89+
}
90+
return versionMap
91+
}
92+
93+
func recoveryObject(c *cos.Client, key string, versionId string, dryrun bool) (err error) {
94+
var destKey = key
95+
if versionId == "" {
96+
return nil
97+
}
98+
var srcUrl = fmt.Sprintf("%s/%s", c.BaseURL.BucketURL.Host, key)
99+
100+
var log = logrus.WithField("srcUrl", srcUrl).WithField("VersionId", versionId).WithField("DestKey", destKey)
101+
if dryrun {
102+
log.Info("DryRun RecoveryObject")
103+
return nil
104+
}
105+
106+
resp, _, err := c.Object.Copy(context.Background(), key, srcUrl, nil, versionId)
107+
108+
var respVerId string
109+
if resp != nil {
110+
respVerId = resp.VersionId
111+
}
112+
113+
log.WithError(err).
114+
WithField("respVerId", respVerId).
115+
Warn("RecoveryObject")
116+
117+
return err
118+
}

0 commit comments

Comments
 (0)