Skip to content

Commit b611c64

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

File tree

2 files changed

+195
-0
lines changed

2 files changed

+195
-0
lines changed

cmd/rov.go

+62
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
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+
notDryrun, _ := cmd.Flags().GetBool("not-dryrun")
18+
previous, _ := cmd.Flags().GetInt("previous")
19+
anyVersion, _ := cmd.Flags().GetBool("any-version")
20+
21+
if limit == 0 {
22+
limit = 10000
23+
} else if limit < 0 {
24+
return fmt.Errorf("flag --limit should be greater than 0")
25+
}
26+
27+
cosPath := ""
28+
if len(args) != 0 {
29+
cosPath = args[0]
30+
}
31+
32+
cosUrl, err := util.FormatUrl(cosPath)
33+
if err != nil {
34+
return fmt.Errorf("cos url format error:%v", err)
35+
}
36+
37+
// 无参数,则列出当前账号下的所有存储桶
38+
if cosPath == "" {
39+
return fmt.Errorf("bucket name is required")
40+
} else if cosUrl.IsCosUrl() {
41+
// 实例化cos client
42+
bucketName := cosUrl.(*util.CosUrl).Bucket
43+
c, err := util.NewClient(&config, &param, bucketName)
44+
if err != nil {
45+
return err
46+
}
47+
return util.RecoveryObjectVersion(c, cosUrl.(*util.CosUrl), previous, limit, !notDryrun, anyVersion)
48+
} else {
49+
return fmt.Errorf("cospath needs to contain cos://")
50+
}
51+
52+
},
53+
}
54+
55+
func init() {
56+
rootCmd.AddCommand(rovCmd)
57+
58+
rovCmd.Flags().IntP("previous", "P", 1, "previous version to recovery. 1 means the latest version, 2 means the second latest version")
59+
rovCmd.Flags().IntP("limit", "l", 10000, "limit the number of objects to list")
60+
rovCmd.Flags().BoolP("not-dryrun", "n", false, "default dryrun mode, do not actually recovery object version")
61+
rovCmd.Flags().BoolP("any-version", "a", false, "if set, recovery any version of the object, else only recovery the latest delete marker")
62+
}

util/recovery_object_version.go

+133
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
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, anyVersion 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+
if anyVersion {
45+
for key, versions := range notLatestVersions {
46+
if len(versions) < previous || previous <= 0 {
47+
log.WithField("previous", previous).Warn("version not found")
48+
return nil
49+
}
50+
err = recoveryObject(c, key, versions[previous-1].VersionId, dryrun)
51+
if err != nil {
52+
log.WithError(err).Warn("recovery failed")
53+
} else {
54+
log.Info("recovery success")
55+
}
56+
}
57+
} else {
58+
for _, marker := range resp.DeleteMarker {
59+
var log = log.WithField("Key", marker.Key).
60+
WithField("marker.VersionId", marker.VersionId)
61+
62+
if marker.IsLatest {
63+
versionIds, has := notLatestVersions[marker.Key]
64+
if has && (len(versionIds) < previous || previous <= 0) {
65+
log.Warn("version not found")
66+
return nil
67+
}
68+
69+
if has {
70+
err = recoveryObject(c, marker.Key, versionIds[previous-1].VersionId, dryrun)
71+
if err != nil {
72+
log.WithError(err).Warn("recovery failed")
73+
} else {
74+
log.Info("recovery success")
75+
}
76+
} else {
77+
log.Warn("not found the latest version of delete marker")
78+
}
79+
}
80+
}
81+
}
82+
83+
logrus.WithField("IsTruncated", resp.IsTruncated).WithField("NextKeyMarker", resp.NextKeyMarker).Info("DeleteMarkers")
84+
marker = resp.NextKeyMarker
85+
isTruncated = resp.IsTruncated
86+
versionMarker = resp.NextVersionIdMarker
87+
88+
if !isTruncated || total >= limit {
89+
logrus.Info("no more")
90+
break
91+
}
92+
}
93+
return nil
94+
}
95+
96+
// map[Key]Version
97+
func versionKeyMap(latest bool, versions []cos.ListVersionsResultVersion) map[string][]cos.ListVersionsResultVersion {
98+
var versionMap = make(map[string][]cos.ListVersionsResultVersion)
99+
for _, version := range versions {
100+
if version.IsLatest == latest {
101+
versionMap[version.Key] = append(versionMap[version.Key], version)
102+
} else {
103+
}
104+
}
105+
return versionMap
106+
}
107+
108+
func recoveryObject(c *cos.Client, key string, versionId string, dryrun bool) (err error) {
109+
var destKey = key
110+
if versionId == "" {
111+
return nil
112+
}
113+
var srcUrl = fmt.Sprintf("%s/%s", c.BaseURL.BucketURL.Host, key)
114+
115+
var log = logrus.WithField("srcUrl", srcUrl).WithField("VersionId", versionId).WithField("DestKey", destKey)
116+
if dryrun {
117+
log.Info("DryRun RecoveryObject")
118+
return nil
119+
}
120+
121+
resp, _, err := c.Object.Copy(context.Background(), key, srcUrl, nil, versionId)
122+
123+
var respVerId string
124+
if resp != nil {
125+
respVerId = resp.VersionId
126+
}
127+
128+
log.WithError(err).
129+
WithField("respVerId", respVerId).
130+
Warn("RecoveryObject")
131+
132+
return err
133+
}

0 commit comments

Comments
 (0)