Skip to content

Commit

Permalink
recovery deleted object to special version.
Browse files Browse the repository at this point in the history
Signed-off-by: 疯魔慕薇 <[email protected]>
  • Loading branch information
kofj committed Oct 15, 2024
1 parent 752be27 commit 294cc50
Show file tree
Hide file tree
Showing 2 changed files with 178 additions and 0 deletions.
60 changes: 60 additions & 0 deletions cmd/rov.go
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, &param, 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")
}
118 changes: 118 additions & 0 deletions util/recovery_object_version.go
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
}

0 comments on commit 294cc50

Please sign in to comment.