oss/lib/revert_versioning.go (249 lines of code) (raw):
package lib
import (
"fmt"
"os"
"strings"
oss "github.com/aliyun/aliyun-oss-go-sdk/oss"
)
var specChineseRevert = SpecText{
synopsisText: "将object从删除状态恢复成最近的多版本状态",
paramText: "cloud_url [options]",
syntaxText: `
ossutil revert-versioning oss://bucket[/prefix] [--encoding-type encodeType] [-r] [--start-time startTime] [--end-time endTime] [--include include-pattern] [--exclude exclude-pattern] [--payer requester]
`,
detailHelpText: `
该命令通过删除最新的删除标记,使object从删除状态恢复成最近的多版本状态
--recursive选项
如果输入--recursive或者-r,表示批量操作匹配prefix的所有objects, 否则只操作key为prefix的单个object
--start-time
时间戳, 既从1970年1月1日(UTC/GMT的午夜)开始所经过的秒数
如果输入这个选项, object的删除时间小于该时间戳将被忽略
--end-time
时间戳, 既从1970年1月1日(UTC/GMT的午夜)开始所经过的秒数
如果输入这个选项, object的删除时间大于该时间戳将被忽略
用法:
该命令只有一种用法:
1) ossutil revert-versioning oss://bucket[/prefix] [--encoding-type encodeType] [-r] [--start-time startTime] [--end-time endTime] [--include include-pattern] [--exclude exclude-pattern] [--payer requester]
恢复bucket下面满足前缀为prefix的object为多版本状态
`,
sampleText: `
1) 恢复整个bucket的处于删除状态的objects为最近的多版本状态
ossutil revert-versioning oss://bucket -r
2) 恢复单个处于删除状态的object为最近多版本状态
ossutil revert-versioning oss://bucket/object
3) 恢复处于删除状态的objects为最近的多版本状态, key的后缀满足输入的过滤条件
ossutil revert-versioning oss://bucket/prefix -r --include *.jpg --exclude *.txt
4) 恢复处于删除状态的objects为最近的多版本状态, object的最后删除时间必须在输入范围内
起始时间为北京时间2020/6/16 16:22:58, 结束时间为北京时间2020/6/16 16:39:38
ossutil revert-versioning oss://bucket/prefix -r --start-time 1592295778 --end-time 1592296778
5) 访问者付费模式
ossutil revert-versioning oss://bucket/prefix -r --payer requester
`,
}
var specEnglishRevert = SpecText{
synopsisText: "Revert the deleted object to the latest versioning state",
paramText: "cloud_url [options]",
syntaxText: `
ossutil revert-versioning oss://bucket[/prefix] [--encoding-type encodeType] [-r] [--start-time startTime] [--end-time endTime] [--include include-pattern] [--exclude exclude-pattern] [--payer requester]
`,
detailHelpText: `
This command revert the object from the deleted state to the latest versioning state by deleting the latest delete mark
Usages:
There is only one usage for this command:
1) ossutil revert-versioning oss://bucket[/prefix] [--encoding-type encodeType] [-r] [--start-time startTime] [--end-time endTime] [--include include-pattern] [--exclude exclude-pattern] [--payer requester]
Revert the bucket's objects whose prefix are "prefix" to the versioning state
`,
sampleText: `
1) Revert the bucket's deleted objects to the latest versioning state
ossutil revert-versioning oss://bucket -r
2) Revert a single deleted object to the latest versioning state
ossutil revert-versioning oss://bucket/object
3) Revert deleted objects to the latest versioning state, the key suffix meets the input filter conditions
ossutil revert-versioning oss://bucket/prefix -r --include *.jpg --exclude *.txt
4) Revert deleted objects to the latest versioning state, the last deletion time of objects must be within the input range
The start time is Beijing time 2020/6/16 16:22:58, and the end time is Beijing time 2020/6/16 16:39:38
ossutil revert-versioning oss://bucket/prefix -r --start-time 1592295778 --end-time 1592296778
5) Use requester to pay mode
ossutil revert-versioning oss://bucket/prefix -r --payer requester
`,
}
type revertOptionType struct {
bucketName string
object string
startTime int64
endTime int64
payer string
filters []filterOptionType
options []oss.Option
recursive bool
revertCount int64
}
type RevertCommand struct {
command Command
revertOption revertOptionType
}
var revertCommand = RevertCommand{
command: Command{
name: "revert-versioning",
nameAlias: []string{"revert-versioning"},
minArgc: 1,
maxArgc: 1,
specChinese: specChineseRevert,
specEnglish: specEnglishRevert,
group: GroupTypeNormalCommand,
validOptionNames: []string{
OptionConfigFile,
OptionEndpoint,
OptionAccessKeyID,
OptionAccessKeySecret,
OptionSTSToken,
OptionProxyHost,
OptionProxyUser,
OptionProxyPwd,
OptionLogLevel,
OptionRecursion,
OptionRequestPayer,
OptionStartTime,
OptionEndTime,
OptionInclude,
OptionExclude,
OptionEncodingType,
OptionPassword,
OptionMode,
OptionECSRoleName,
OptionTokenTimeout,
OptionRamRoleArn,
OptionRoleSessionName,
OptionExternalId,
OptionReadTimeout,
OptionConnectTimeout,
OptionSTSRegion,
OptionSkipVerifyCert,
OptionUserAgent,
OptionSignVersion,
OptionRegion,
OptionCloudBoxID,
OptionForcePathStyle,
},
},
}
// function for FormatHelper interface
func (revert *RevertCommand) formatHelpForWhole() string {
return revert.command.formatHelpForWhole()
}
func (revert *RevertCommand) formatIndependHelp() string {
return revert.command.formatIndependHelp()
}
// Init simulate inheritance, and polymorphism
func (revert *RevertCommand) Init(args []string, options OptionMapType) error {
return revert.command.Init(args, options, revert)
}
// RunCommand simulate inheritance, and polymorphism
func (revert *RevertCommand) RunCommand() error {
encodingType, _ := GetString(OptionEncodingType, revert.command.options)
srcBucketUrL, err := GetCloudUrl(revert.command.args[0], encodingType)
if err != nil {
return err
}
revert.revertOption.bucketName = srcBucketUrL.bucket
revert.revertOption.object = srcBucketUrL.object
revert.revertOption.recursive, _ = GetBool(OptionRecursion, revert.command.options)
if !revert.revertOption.recursive && revert.revertOption.object == "" {
return fmt.Errorf("please input object key when option recursive is false")
}
revert.revertOption.startTime, _ = GetInt(OptionStartTime, revert.command.options)
revert.revertOption.endTime, _ = GetInt(OptionEndTime, revert.command.options)
if revert.revertOption.endTime > 0 && revert.revertOption.startTime > revert.revertOption.endTime {
return fmt.Errorf("start time %d is larger than end time %d", revert.revertOption.startTime, revert.revertOption.endTime)
}
revert.revertOption.payer, _ = GetString(OptionRequestPayer, revert.command.options)
if revert.revertOption.payer != "" {
if strings.ToLower(revert.revertOption.payer) != strings.ToLower(string(oss.Requester)) &&
strings.ToLower(revert.revertOption.payer) != strings.ToLower(string(oss.BucketOwner)) {
return fmt.Errorf("option payer value must be %s or %s",
strings.ToLower(string(oss.Requester)), strings.ToLower(string(oss.BucketOwner)))
}
revert.revertOption.options = append(revert.revertOption.options, oss.RequestPayer(oss.PayerType(revert.revertOption.payer)))
}
var res bool
res, revert.revertOption.filters = getFilter(os.Args)
if !res {
return fmt.Errorf("--include or --exclude does not support format containing dir info")
}
bucket, err := revert.command.ossBucket(revert.revertOption.bucketName)
if err != nil {
return err
}
return revert.revertObjects(bucket)
}
func (revert *RevertCommand) revertObjects(bucket *oss.Bucket) error {
pre := oss.Prefix(revert.revertOption.object)
keyMarker := oss.KeyMarker("")
versionIdMarker := oss.VersionIdMarker("")
listOptions := []oss.Option{pre, keyMarker, versionIdMarker, oss.MaxKeys(1000)}
if revert.revertOption.payer != "" {
listOptions = append(listOptions, oss.RequestPayer(oss.PayerType(revert.revertOption.payer)))
}
bStopped := false
batchCount := 0
for {
if bStopped {
break
}
batchCount++
lor, err := bucket.ListObjectVersions(listOptions...)
if err != nil {
return err
}
var objectVersions []oss.DeleteObject
for _, deleteMarker := range lor.ObjectDeleteMarkers {
if !revert.revertOption.recursive && deleteMarker.Key != revert.revertOption.object {
bStopped = true
break
}
if deleteMarker.IsLatest && revert.filterDeleteMarker(&deleteMarker) {
objectVersions = append(objectVersions, oss.DeleteObject{
Key: deleteMarker.Key,
VersionId: deleteMarker.VersionId,
})
}
}
if len(objectVersions) > 0 {
deleteOptions := append(revert.revertOption.options, oss.DeleteObjectsQuiet(true))
delRes, err := bucket.DeleteObjectVersions(objectVersions, deleteOptions...)
if err != nil {
return err
}
if len(delRes.DeletedObjectsDetail) > 0 {
fmt.Printf("\n")
for _, object := range delRes.DeletedObjectsDetail {
fmt.Printf("delete deleteMarker failure, key:%s,version:%s\n", object.Key, object.VersionId)
}
return fmt.Errorf("delete deleteMarker failure")
}
revert.revertOption.revertCount += int64(len(objectVersions))
for _, object := range objectVersions {
LogInfo("revert %s %s\n", object.Key, object.VersionId)
}
}
keyMarker = oss.KeyMarker(lor.NextKeyMarker)
versionIdMarker := oss.VersionIdMarker(lor.NextVersionIdMarker)
listOptions = []oss.Option{pre, keyMarker, versionIdMarker, oss.MaxKeys(1000)}
if revert.revertOption.payer != "" {
listOptions = append(listOptions, oss.RequestPayer(oss.PayerType(revert.revertOption.payer)))
}
fmt.Printf("\rrevert versioning object count is %d, batch list count is %d", revert.revertOption.revertCount, batchCount)
if !lor.IsTruncated {
break
}
}
fmt.Printf("\n")
return nil
}
func (revert *RevertCommand) filterDeleteMarker(deleteMarker *oss.ObjectDeleteMarkerProperties) bool {
if !doesSingleObjectMatchPatterns(deleteMarker.Key, revert.revertOption.filters) {
return false
}
if (revert.revertOption.startTime > 0 && deleteMarker.LastModified.Unix() < revert.revertOption.startTime) ||
(revert.revertOption.endTime > 0 && deleteMarker.LastModified.Unix() > revert.revertOption.endTime) {
return false
}
return true
}