-
Notifications
You must be signed in to change notification settings - Fork 33
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
recovery deleted object to special version.
Signed-off-by: 疯魔慕薇 <[email protected]>
- Loading branch information
Showing
2 changed files
with
178 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
package cmd | ||
|
||
import ( | ||
"coscli/util" | ||
"fmt" | ||
|
||
"github.com/spf13/cobra" | ||
) | ||
|
||
var rovCmd = &cobra.Command{ | ||
Use: "rov", | ||
Short: "recovery object version", | ||
Long: `recovery object version`, | ||
Args: cobra.MaximumNArgs(1), | ||
RunE: func(cmd *cobra.Command, args []string) error { | ||
limit, _ := cmd.Flags().GetInt("limit") | ||
dryrun, _ := cmd.Flags().GetBool("dryrun") | ||
previous, _ := cmd.Flags().GetInt("previous") | ||
|
||
if limit == 0 { | ||
limit = 10000 | ||
} else if limit < 0 { | ||
return fmt.Errorf("flag --limit should be greater than 0") | ||
} | ||
|
||
cosPath := "" | ||
if len(args) != 0 { | ||
cosPath = args[0] | ||
} | ||
|
||
cosUrl, err := util.FormatUrl(cosPath) | ||
if err != nil { | ||
return fmt.Errorf("cos url format error:%v", err) | ||
} | ||
|
||
// 无参数,则列出当前账号下的所有存储桶 | ||
if cosPath == "" { | ||
return fmt.Errorf("bucket name is required") | ||
} else if cosUrl.IsCosUrl() { | ||
// 实例化cos client | ||
bucketName := cosUrl.(*util.CosUrl).Bucket | ||
c, err := util.NewClient(&config, ¶m, bucketName) | ||
if err != nil { | ||
return err | ||
} | ||
return util.RecoveryObjectVersion(c, cosUrl.(*util.CosUrl), previous, limit, dryrun) | ||
} else { | ||
return fmt.Errorf("cospath needs to contain cos://") | ||
} | ||
|
||
}, | ||
} | ||
|
||
func init() { | ||
rootCmd.AddCommand(rovCmd) | ||
|
||
rovCmd.Flags().IntP("previous", "P", 1, "previous version to recovery. 1 means the latest version, 2 means the second latest version") | ||
rovCmd.Flags().IntP("limit", "l", 10000, "limit the number of objects to list") | ||
rovCmd.Flags().BoolP("not-dryrun", "n", false, "default dryrun mode, do not actually recovery object version") | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,118 @@ | ||
package util | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
|
||
"github.com/sirupsen/logrus" | ||
"github.com/tencentyun/cos-go-sdk-v5" | ||
) | ||
|
||
// RecoveryObjectVersion recovery object version | ||
func RecoveryObjectVersion(c *cos.Client, cosUrl StorageUrl, previous int, limit int, dryrun bool) error { | ||
var total = 0 | ||
var marker = "" | ||
var versionMarker = "" | ||
var isTruncated = true | ||
|
||
var ctx = context.Background() | ||
var prefix = cosUrl.(*CosUrl).Object | ||
|
||
for isTruncated && total < limit { | ||
var opt = &cos.BucketGetObjectVersionsOptions{ | ||
Prefix: prefix, | ||
KeyMarker: marker, | ||
VersionIdMarker: versionMarker, | ||
} | ||
resp, _, err := c.Bucket.GetObjectVersions(ctx, opt) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
total += len(resp.Version) | ||
total += len(resp.DeleteMarker) | ||
|
||
var notLatestVersions = versionKeyMap(false, resp.Version) | ||
var log = logrus.WithField("versions", notLatestVersions). | ||
WithField("previous", previous). | ||
WithField("dryrun", dryrun) | ||
|
||
for key, versions := range notLatestVersions { | ||
logrus.WithField("Key", key).WithField("versions", versions).Info("find versions") | ||
} | ||
|
||
for _, marker := range resp.DeleteMarker { | ||
var log = log.WithField("Key", marker.Key). | ||
WithField("marker.VersionId", marker.VersionId) | ||
|
||
if marker.IsLatest { | ||
versionIds, has := notLatestVersions[marker.Key] | ||
if has && (len(versionIds) < previous || previous <= 0) { | ||
log.Warn("version not found") | ||
return nil | ||
} | ||
|
||
if has { | ||
err = recoveryObject(c, marker.Key, versionIds[previous-1].VersionId, dryrun) | ||
if err != nil { | ||
log.WithError(err).Warn("recovery failed") | ||
} else { | ||
log.Info("recovery success") | ||
} | ||
} else { | ||
log.Warn("not found the latest version of delete marker") | ||
} | ||
} | ||
} | ||
|
||
logrus.WithField("IsTruncated", resp.IsTruncated).WithField("NextKeyMarker", resp.NextKeyMarker).Info("DeleteMarkers") | ||
marker = resp.NextKeyMarker | ||
isTruncated = resp.IsTruncated | ||
versionMarker = resp.NextVersionIdMarker | ||
|
||
if !isTruncated || total >= limit { | ||
logrus.Info("no more") | ||
break | ||
} | ||
} | ||
return nil | ||
} | ||
|
||
// map[Key]Version | ||
func versionKeyMap(latest bool, versions []cos.ListVersionsResultVersion) map[string][]cos.ListVersionsResultVersion { | ||
var versionMap = make(map[string][]cos.ListVersionsResultVersion) | ||
for _, version := range versions { | ||
if version.IsLatest == latest { | ||
versionMap[version.Key] = append(versionMap[version.Key], version) | ||
} else { | ||
} | ||
} | ||
return versionMap | ||
} | ||
|
||
func recoveryObject(c *cos.Client, key string, versionId string, dryrun bool) (err error) { | ||
var destKey = key | ||
if versionId == "" { | ||
return nil | ||
} | ||
var srcUrl = fmt.Sprintf("%s/%s", c.BaseURL.BucketURL.Host, key) | ||
|
||
var log = logrus.WithField("srcUrl", srcUrl).WithField("VersionId", versionId).WithField("DestKey", destKey) | ||
if dryrun { | ||
log.Info("DryRun RecoveryObject") | ||
return nil | ||
} | ||
|
||
resp, _, err := c.Object.Copy(context.Background(), key, srcUrl, nil, versionId) | ||
|
||
var respVerId string | ||
if resp != nil { | ||
respVerId = resp.VersionId | ||
} | ||
|
||
log.WithError(err). | ||
WithField("respVerId", respVerId). | ||
Warn("RecoveryObject") | ||
|
||
return err | ||
} |