oss/lib/du.go (397 lines of code) (raw):
package lib
import (
"fmt"
"runtime"
"strconv"
"strings"
"sync"
oss "github.com/aliyun/aliyun-oss-go-sdk/oss"
)
var specChineseDu = SpecText{
synopsisText: "获取bucket或者指定前缀(目录)所占的存储空间大小",
paramText: "bucket_url [options]",
syntaxText: `
ossutil du oss://bucket[/prefix] [options]
`,
detailHelpText: `
该命令会获取bucket或者指定前缀(目录)所占的存储空间大小,包括未完成上传object的块大小
用法:
该命令只有一种用法:
1) ossutil du oss://bucket[/prefix] [options]
查询bucket或者指定前缀(目录)所占存储空间大小
`,
sampleText: `
1) 查询bucket占用存储空间大小
ossutil du oss://bucket
2) 查询指定前缀(目录)占用存储空间大小
ossutil du oss://bucket/prefix
3) 查询指定前缀(目录)占用存储空间大小, 包括多版本object
ossutil du oss://bucket/prefix --all-versions
4) 统计结果以KB为单位显示, 支持MB, GB, TB
ossutil du oss://bucket/prefix --block-size KB
`,
}
var specEnglishDu = SpecText{
synopsisText: "Get the bucket or the specified prefix(directory) storage size",
paramText: "bucket_url [options]",
syntaxText: `
ossutil du oss://bucket[/prefix] [options]
`,
detailHelpText: `
This command gets the bucket or the specified prefix(directory) storage size,including uncompleted part size
Usages:
There is only one usage for this command:
1) ossutil du oss://bucket[/prefix] [options]
Gets the bucket or the specified prefix(directory) storage size
`,
sampleText: `
1) get the bucket storage size
ossutil du oss://bucket
2) get the prefix(directory) stroage size
ossutil du oss://bucket/prefix
3) get the prefix(directory) stroage size, including all versioning objects
ossutil du oss://bucket/prefix --all-versions
4) The du results are displayed in KB block size, Support MB, GB, TB
ossutil du oss://bucket/prefix --block-size KB
`,
}
type MultiPartObject struct {
objectName string
uploadId string
}
type duSizeOptionType struct {
bucketName string
object string
payer string
countTypeMap map[string]int64
sizeTypeMap map[string]int64
totalObjectCount int64
sumObjectSize int64
totalPartCount int64
sumPartSize int64
mutex sync.Mutex
displayUnit string
blockSize int64
}
type DuCommand struct {
command Command
duOption duSizeOptionType
}
var duSizeCommand = DuCommand{
command: Command{
name: "du",
nameAlias: []string{"du"},
minArgc: 1,
maxArgc: 1,
specChinese: specChineseDu,
specEnglish: specEnglishDu,
group: GroupTypeNormalCommand,
validOptionNames: []string{
OptionConfigFile,
OptionEndpoint,
OptionAccessKeyID,
OptionAccessKeySecret,
OptionSTSToken,
OptionProxyHost,
OptionProxyUser,
OptionProxyPwd,
OptionLogLevel,
OptionRequestPayer,
OptionAllversions,
OptionPassword,
OptionBlockSize,
OptionMode,
OptionECSRoleName,
OptionTokenTimeout,
OptionRamRoleArn,
OptionExternalId,
OptionRoleSessionName,
OptionReadTimeout,
OptionConnectTimeout,
OptionSTSRegion,
OptionSkipVerifyCert,
OptionUserAgent,
OptionSignVersion,
OptionRegion,
OptionCloudBoxID,
OptionForcePathStyle,
},
},
}
// function for FormatHelper interface
func (duc *DuCommand) formatHelpForWhole() string {
return duc.command.formatHelpForWhole()
}
func (duc *DuCommand) formatIndependHelp() string {
return duc.command.formatIndependHelp()
}
// Init simulate inheritance, and polymorphism
func (duc *DuCommand) Init(args []string, options OptionMapType) error {
return duc.command.Init(args, options, duc)
}
// RunCommand simulate inheritance, and polymorphism
func (duc *DuCommand) RunCommand() error {
// clear for go tests
duc.duOption.countTypeMap = make(map[string]int64)
duc.duOption.sizeTypeMap = make(map[string]int64)
duc.duOption.totalObjectCount = 0
duc.duOption.sumObjectSize = 0
duc.duOption.totalPartCount = 0
duc.duOption.sumPartSize = 0
blockSizeMap := make(map[string]int64)
blockSizeMap["byte"] = 1
blockSizeMap["KB"] = 1024
blockSizeMap["MB"] = 1024 * 1024
blockSizeMap["GB"] = 1024 * 1024 * 1024
blockSizeMap["TB"] = 1024 * 1024 * 1024 * 1024
encodingType, _ := GetString(OptionEncodingType, duc.command.options)
srcBucketUrL, err := GetCloudUrl(duc.command.args[0], encodingType)
if err != nil {
return err
}
payer, _ := GetString(OptionRequestPayer, duc.command.options)
if payer != "" {
if strings.ToLower(payer) != strings.ToLower(string(oss.Requester)) &&
strings.ToLower(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)))
}
}
allVersions, _ := GetBool(OptionAllversions, duc.command.options)
strBlockSize, _ := GetString(OptionBlockSize, duc.command.options)
strBlockSize = strings.ToUpper(strBlockSize)
if strBlockSize == "" {
strBlockSize = "byte"
}
if strBlockSize != "byte" && strBlockSize != "KB" && strBlockSize != "MB" && strBlockSize != "GB" && strBlockSize != "TB" {
return fmt.Errorf("-B value must be KB, MB, GB or TB")
}
duc.duOption.displayUnit = strBlockSize
duc.duOption.blockSize = blockSizeMap[strBlockSize]
duc.duOption.bucketName = srcBucketUrL.bucket
duc.duOption.object = srcBucketUrL.object
duc.duOption.payer = payer
bucket, err := duc.command.ossBucket(duc.duOption.bucketName)
if err != nil {
return err
}
// first:get all object size
if allVersions {
err = duc.getAllObjectVersionsSize(bucket)
} else {
err = duc.getAllObjectSize(bucket)
}
if err != nil {
return err
}
printHeader := false
for k, v := range duc.duOption.countTypeMap {
if !printHeader {
fmt.Printf("\r ")
fmt.Printf("\r%-14s\t%-20s\t%-30s\n", "storage class", "object count", "sum size(byte)")
fmt.Printf("----------------------------------------------------------\n")
printHeader = true
}
fmt.Printf("%-14s\t%-20d\t%-30d\n", k, v, duc.duOption.sizeTypeMap[k])
}
if !printHeader {
fmt.Printf("\r")
} else {
fmt.Printf("----------------------------------------------------------\n")
}
fmt.Printf("%-20s%-20d\t%-23s%d\n", "total object count:", duc.duOption.totalObjectCount, "total object sum size:", duc.duOption.sumObjectSize)
//second:get all part size
err = duc.GetAllPartSize(bucket)
if err != nil {
return err
}
fmt.Printf("\r ")
fmt.Printf("\r%-20s%-20d\t%-23s%d\n\n", "total part count:", duc.duOption.totalPartCount, "total part sum size:", duc.duOption.sumPartSize)
if duc.duOption.blockSize == int64(1) {
displaySize := (duc.duOption.sumObjectSize + duc.duOption.sumPartSize) / duc.duOption.blockSize
fmt.Printf("total du size(%s):%d\n", duc.duOption.displayUnit, displaySize)
} else {
displaySize := float64(duc.duOption.sumObjectSize+duc.duOption.sumPartSize) / float64(duc.duOption.blockSize)
fmt.Printf("total du size(%s):%.4f\n", duc.duOption.displayUnit, displaySize)
}
return nil
}
func (duc *DuCommand) getAllObjectSize(bucket *oss.Bucket) error {
pre := oss.Prefix(duc.duOption.object)
marker := oss.Marker("")
listOptions := []oss.Option{pre, marker, oss.MaxKeys(1000)}
if duc.duOption.payer != "" {
listOptions = append(listOptions, oss.RequestPayer(oss.PayerType(duc.duOption.payer)))
}
for i := 1; ; i++ {
lor, err := duc.command.ossListObjectsRetry(bucket, listOptions...)
if err != nil {
return err
}
duc.duOption.totalObjectCount += int64(len(lor.Objects))
for _, object := range lor.Objects {
duc.duOption.sumObjectSize += object.Size
if _, ok := duc.duOption.countTypeMap[object.StorageClass]; ok {
duc.duOption.countTypeMap[object.StorageClass]++
duc.duOption.sizeTypeMap[object.StorageClass] += object.Size
} else {
duc.duOption.countTypeMap[object.StorageClass] = 1
duc.duOption.sizeTypeMap[object.StorageClass] = object.Size
}
}
fmt.Printf("\robject count:%d\tobject sum size:%d", duc.duOption.totalObjectCount, duc.duOption.sumObjectSize)
pre = oss.Prefix(lor.Prefix)
marker = oss.Marker(lor.NextMarker)
listOptions = []oss.Option{pre, marker, oss.MaxKeys(1000)}
if duc.duOption.payer != "" {
listOptions = append(listOptions, oss.RequestPayer(oss.PayerType(duc.duOption.payer)))
}
if !lor.IsTruncated {
break
}
}
return nil
}
func (duc *DuCommand) getAllObjectVersionsSize(bucket *oss.Bucket) error {
// Delete Object Versions and DeleteMarks
pre := oss.Prefix(duc.duOption.object)
keyMarker := oss.KeyMarker("")
versionIdMarker := oss.VersionIdMarker("")
listOptions := []oss.Option{pre, keyMarker, versionIdMarker, oss.MaxKeys(1000)}
if duc.duOption.payer != "" {
listOptions = append(listOptions, oss.RequestPayer(oss.PayerType(duc.duOption.payer)))
}
for {
lor, err := bucket.ListObjectVersions(listOptions...)
if err != nil {
return err
}
duc.duOption.totalObjectCount += int64(len(lor.ObjectVersions))
for _, object := range lor.ObjectVersions {
duc.duOption.sumObjectSize += object.Size
if _, ok := duc.duOption.countTypeMap[object.StorageClass]; ok {
duc.duOption.countTypeMap[object.StorageClass]++
duc.duOption.sizeTypeMap[object.StorageClass] += object.Size
} else {
duc.duOption.countTypeMap[object.StorageClass] = 1
duc.duOption.sizeTypeMap[object.StorageClass] = object.Size
}
}
fmt.Printf("\robject count:%d\tobject sum size:%d", duc.duOption.totalObjectCount, duc.duOption.sumObjectSize)
keyMarker = oss.KeyMarker(lor.NextKeyMarker)
versionIdMarker := oss.VersionIdMarker(lor.NextVersionIdMarker)
listOptions = []oss.Option{pre, keyMarker, versionIdMarker, oss.MaxKeys(1000)}
if duc.duOption.payer != "" {
listOptions = append(listOptions, oss.RequestPayer(oss.PayerType(duc.duOption.payer)))
}
if !lor.IsTruncated {
break
}
}
return nil
}
func (duc *DuCommand) GetAllPartSize(bucket *oss.Bucket) error {
routineCount := runtime.NumCPU()
chObjects := make(chan MultiPartObject, ChannelBuf)
chError := make(chan error, routineCount)
chListError := make(chan error, 1)
go duc.uploadIdProducer(bucket, chObjects, chListError)
for i := 0; i < routineCount; i++ {
go duc.uploadIdConsumer(bucket, chObjects, chError)
}
return duc.waitRoutinueComplete(chError, chListError, routineCount)
}
func (duc *DuCommand) uploadIdConsumer(bucket *oss.Bucket, chObjects chan MultiPartObject, chError chan error) error {
for object := range chObjects {
err := duc.statPartSize(bucket, object)
if err != nil {
chError <- err
return err
}
}
chError <- nil
return nil
}
func (duc *DuCommand) statPartSize(bucket *oss.Bucket, object MultiPartObject) error {
var imur oss.InitiateMultipartUploadResult
imur.Bucket = duc.duOption.bucketName
imur.Key = object.objectName
imur.UploadID = object.uploadId
partNumberMarker := 0
for i := 0; ; i++ {
lpOptions := []oss.Option{}
lpOptions = append(lpOptions, oss.MaxParts(1000))
lpOptions = append(lpOptions, oss.PartNumberMarker(partNumberMarker))
if duc.duOption.payer != "" {
lpOptions = append(lpOptions, oss.RequestPayer(oss.PayerType(duc.duOption.payer)))
}
lpRes, err := bucket.ListUploadedParts(imur, lpOptions...)
if err != nil {
serviceError, ok := err.(oss.ServiceError)
if ok && serviceError.StatusCode == 404 {
// ignore 404 error; the uploadid maybe abort or completed
return nil
} else {
return err
}
} else {
duc.duOption.mutex.Lock()
duc.duOption.totalPartCount += int64(len(lpRes.UploadedParts))
for _, v := range lpRes.UploadedParts {
duc.duOption.sumPartSize += int64(v.Size)
}
fmt.Printf("\rpart count:%d\tpart sum size:%d", duc.duOption.totalPartCount, duc.duOption.sumPartSize)
duc.duOption.mutex.Unlock()
}
if lpRes.IsTruncated {
partNumberMarker, _ = strconv.Atoi(lpRes.NextPartNumberMarker)
} else {
break
}
}
return nil
}
func (duc *DuCommand) uploadIdProducer(bucket *oss.Bucket, chObjects chan MultiPartObject, chListError chan error) {
defer close(chObjects)
prefix := duc.duOption.object
keyMarker := ""
uploadIdMarker := ""
for i := 0; ; i++ {
lpOptions := []oss.Option{}
lpOptions = append(lpOptions, oss.MaxParts(1000))
lpOptions = append(lpOptions, oss.Prefix(prefix))
lpOptions = append(lpOptions, oss.KeyMarker(keyMarker))
lpOptions = append(lpOptions, oss.UploadIDMarker(uploadIdMarker))
if duc.duOption.payer != "" {
lpOptions = append(lpOptions, oss.RequestPayer(oss.PayerType(duc.duOption.payer)))
}
lpRes, err := bucket.ListMultipartUploads(lpOptions...)
if err != nil {
chListError <- err
return
}
for _, v := range lpRes.Uploads {
var object MultiPartObject
object.objectName = v.Key
object.uploadId = v.UploadID
chObjects <- object
}
if lpRes.IsTruncated {
keyMarker = lpRes.NextKeyMarker
uploadIdMarker = lpRes.NextUploadIDMarker
} else {
break
}
}
chListError <- nil
}
func (duc *DuCommand) waitRoutinueComplete(chError, chListError <-chan error, routineCount int) error {
completed := 0
for completed <= routineCount {
select {
case err := <-chListError:
if err != nil {
return err
}
completed++
case err := <-chError:
if err != nil {
return err
}
completed++
}
}
return nil
}