├── .travis.yml
├── CHANGELOG.md
├── LICENSE
├── README-CN.md
├── README.md
├── go.mod
├── go.sum
├── lib
├── allpart_size.go
├── allpart_size_test.go
├── append_file.go
├── append_file_test.go
├── bucket_access_monitor.go
├── bucket_access_monitor_test.go
├── bucket_cname.go
├── bucket_cname_test.go
├── bucket_cors.go
├── bucket_cors_test.go
├── bucket_encryption.go
├── bucket_encryption_test.go
├── bucket_inventory.go
├── bucket_inventory_test.go
├── bucket_lifecycle.go
├── bucket_lifecycle_test.go
├── bucket_logging.go
├── bucket_logging_test.go
├── bucket_policy.go
├── bucket_policy_test.go
├── bucket_qos.go
├── bucket_qos_test.go
├── bucket_referer.go
├── bucket_referer_test.go
├── bucket_replication.go
├── bucket_replication_test.go
├── bucket_resource_group.go
├── bucket_resource_group_test.go
├── bucket_style.go
├── bucket_style_test.go
├── bucket_tagging.go
├── bucket_tagging_test.go
├── bucket_versioning.go
├── bucket_versioning_test.go
├── bucket_website.go
├── bucket_website_test.go
├── bucket_worm.go
├── bucket_worm_test.go
├── cat.go
├── cat_test.go
├── command.go
├── command_manager.go
├── command_test.go
├── config.go
├── config_helper.go
├── config_test.go
├── const.go
├── cors_options.go
├── cors_options_test.go
├── cp.go
├── cp_test.go
├── create_symlink.go
├── du.go
├── du_test.go
├── ecs_role.go
├── ecs_role_test.go
├── error.go
├── hash.go
├── hash_test.go
├── help.go
├── help_test.go
├── lang.go
├── lang_test.go
├── lang_windows.go
├── lang_windows_test.go
├── lcb.go
├── listpart.go
├── listpart_test.go
├── lrb.go
├── lrb_test.go
├── ls.go
├── ls_test.go
├── mb.go
├── mb_test.go
├── mkdir.go
├── mkdir_test.go
├── monitor.go
├── monitor_test.go
├── object_tagging.go
├── object_tagging_test.go
├── option.go
├── ossutil_log.go
├── ossutil_log_test.go
├── probe.go
├── probe_test.go
├── read_symlink.go
├── report_helper.go
├── request_payment.go
├── request_payment_test.go
├── restore.go
├── restore_test.go
├── revert_versioning.go
├── revert_versioning_test.go
├── rm.go
├── rm_test.go
├── set_acl.go
├── set_acl_test.go
├── set_meta.go
├── set_meta_test.go
├── signurl.go
├── signurl_test.go
├── stat.go
├── stat_test.go
├── storage_url.go
├── symlink_test.go
├── sync.go
├── sync_test.go
├── update.go
├── update_test.go
├── user_qos.go
├── user_qos_test.go
├── util.go
├── util_sts.go
└── util_sts_test.go
├── ossutil.go
└── scripts
├── build_all.sh
└── ossutil.bat
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: go
2 | go:
3 | - 1.11.8
4 | install:
5 | - go get golang.org/x/tools/cmd/cover
6 | - go get github.com/mattn/goveralls
7 | - go get gopkg.in/check.v1
8 | - go get github.com/droundy/goopt
9 | - go get github.com/alyu/configparser
10 | - go get github.com/aliyun/aliyun-oss-go-sdk/oss
11 | - go get github.com/syndtr/goleveldb/leveldb
12 | - go get github.com/satori/go.uuid
13 | script:
14 | # - cd lib
15 | # - travis_wait 120 go test -v -timeout 120m -covermode=count -coverprofile=coverage.out
16 | # - "$HOME/gopath/bin/goveralls -coverprofile=coverage.out -service=travis-ci"
17 | env:
18 | global:
19 | - secure: YO0OXWMcm0O+VH5xrQ66kKN/346bYnlLuk6hSbsR1Uvkg2OUkAXQVcIxnNkzCjSu2iYW8mvnx20GenjCLphlLK4n7vRBmRMhDm6UbHfi4ivVG7rx9q2Rl6q5Apd+TItafK9mxMaVxY7mSgMEgU4Mq9BMPP3bI7qfIvo5jJlpu6Lzr2PZKqLkVRzf0H+XoOdvKKz/HeneOztcMixhhKdq7IE60YG7lA+US67B43Ztgrw7B/LnieZGYtVqHnVvqc8LCBEuBYlSM1k3uPQZ8e8Un9An6KwpRcj4Ei94guvhN3SKuglVG93EUF2VluS+kuvHKQFsRHDOwU9vzPKUhmJ27HUSC4QC9IduiZ1NhWuCH8+yuaqjmeVmWEZu9XSSYn6/VIxD/DXPDTvfeyni256RatxIQLzgRrcmxzfHpfb7qQRQt9h0sqLOSeVDbyCUaufWZFR7WzPHGMVNEH2Z01zbjb7RTjdPPMP25PPPaNyERe4Udp1ezBuEdtaY3/UYMBxKqQNqZF23zXlJrl2srPZr1tASxBvU+vy7IIJUBVWYrWoPfoAGMMjXb8FhFeT/Ci/pTRnzBEBxL05Sz8T1KYRPWxQUtuFuV5JGcaP3epBSVGyMvI+p8cU2JnJC6d97lM5SAG9cWgevgvL7N1yBojK6w4lfi4kypEuhPGAOSCLhcGc=
20 | - secure: R1DGbPdXHg6jMy9OfZBCpIU4+ksB/6GzTuVayGx5QGHUWIgZ7KJeaYQBNms+Gh615bGFKK4POl4F/TS/2eRjefhbF+vk7OGa101gZYt/H7eGWrDbmtL+kd/RU8f+jSXLV/qnakv8Owx02+ovSONLn++0jSWb4AnH+8uQV1z3gsGAIfaCmtkIRVWjnBbL6zS9drwRFW/jrldh3ivfK2/0CCJvJe2n0rv0pE0QzXI23IXHS5wlw8cQTU3Sxw4ozcPytu7+52vXRejKdqnWCUeDbcVvayVzuOX3UeHkm719aMncagw3M5V2lbGE9GhnSxN06fpwkxhsGXNAQhqz9qA8O8+3rK8Ibtq3vfGc6sNQPF+Q/13FwUStOof4lhV+wPKO9nBMp3tkMeoMw4t4zMw6vY3ZXsQqwQRpItLX7qSE93qy70Nmq9ZNFNYLD9YE4whV4sBbR1KFjXUOe4HacURN42wAaTJv3ZpSt7TTQULgRWYehQ+pVenmqzQQ9PON0VTvWmmV780+FdcqZPaTYfB3C/aFQZotuKiwFFzcK9gxrqtKj4g/6hFA8TtME46aeK5NskI1jyeCjLc/0PBL3Ff6r/IKHdZHGBDjHuep89ZXeh9NBC4hzijSVEywClk+sO97BQYXPe5rbGJysF1bp/4Kqwy/ZVeTWJAS2enxGdSfk/Q=
21 | - secure: kLAGI8kNWrWCEPJDCyMAmKr6Z9TkgRSdR+eh93g8gfUjsEpePpP0PWBx+onnt0Ale62Ic9BZlj0Li8VUOgM1Cr6CRVDBiNj0Dj3PkdmovNWJwZG+C9KlD2UmMbaovOEBjoA4qw6TxdoY9hx9GWX3fduz2yDnK/lGuF6jb6gh1AjYYYMeQIFZhJ1WOgYAvdUEoXtolXQgv+HMhN13TU7v6VbNpkS3v3qP0vFoNhkFgIGS6Sa+NnEIB+YQGrQ8CoSUOh4CbJqT0LpUE7YQPRJsegGgQfQAVIB//fDsICGMiPI/QmBiKk3QuZpAViyGRc3CJm+GxYBJ6mEPRGxgSuPWH60YT+OWNzyiw07dT1u3XrqxAeyFGh9pCtW7UP5L5bWvFiOtZZhhBSPfaZE7RaukS9MCm2j7m3iPhqbu3TDFxwbD4UOXcuNTJa6UZWuwyereZjkiIFXfzYTGazvHKbx+8T627sXg5luKvuhAL9M+iEJOlezVtkCIEdkIVEC2q19I2KoZzc7Bzl+4lbq9gomDnul//sKbVQqjWoYQNwJmSRqxIkZf9Y8MIlyEwK3W8zyvi73oasBJJuxfEtlbN2bc0Su7HFnIQoEoG/+F5l7dZAKWS0qDy/Br/jGovnM9HoWAV6CQ+wyY9MmSxGO/PbK2XGWKmOXOs6VCXdaRO0Nz0DY=
22 | - secure: NUcfrTX8QcD8k5qWe6DLkbVWkcTlm2GGzSzs8byp557ctmIqk/OWyzeWKHZZbzkKxKcwj3vdSPLldwE9XpfqB0zomZPus3NKcex3izvW4TCAevbqdDE1QbZiqQ9rUh9I3CrEmYkB7nCQ1LnNvXI0RRZkei6N9/AeUFE2NcxW+p2hp1BJvivT1EziEB1VjPpBHyKkikwdvqcpWCosB2ehZmt1EDuEZSVWDz3mxcjk8UBXbs4St7Pm6QWF9598DxY3EwCbvPtqjkPns92afrWshPpXhB+ioqUyyV6jn3JXV6tT8DinUW7u065YoSEURBb2XjTp+A2sYBnSAEvYNwsDkTqlcA0mwxZPolyPUaOXIxJmUt0GMVEB73rVZ16asZ0Zb2QSXN0lEg6nwOS4jRXOHkhCpYPhHDYdihkNb6D5Z6Mk1EbMhOJiq2Mql4FtRn/YcoKU1jFr0FSe4mrynS3yrDsMshn1VYTslp2UbQZGoZCe8VnTogJwC47vramO3/oDA00ZKSZW+kRznikhE/wD8Zh+J5jwIVqvYHFqxn0ZNAVSYz5miDO9Oa5hBl+FitQQgk67v9GMcJXxN3Z03IQrD3TK+djXsi8aX8HDv5SujXTmfgjTJMdPXR76gr/XsBVokmkXZYZDRhWQdZUqFPOQ1/87QyV86AQEdGAbabQ0eI0=
23 | - secure: t0DjGUs3JBZOK8LvMwsVRKL23EJGFPhUcBrFxBVgt0ZPUfe5vMh/QBYesMhAZFNajKWE8g2UJa0ecURXSsnm5xpa7XF+f/12DRCR2/RqflY12/i8D8rc3V7hEmuK3TyBcu4ki+d3vgGdTm6EKgrk0n5x4U29O0hYApctPFCIJXZbU+J85KH8VVNL0uo6tnihzDcEgGe5o4SrCGqpzT4upEhw7jXeoy07118EgGCRvT6GeS6lJi6TbuIsAop7K+uVmEG6HXOpo7jzWcdf7aUGIWoVYwVeTcm9twUv6isbde5mVkkUGHMwDrzi+wv5QL/uv5qkk4fz+zYWqbWNN5QuS4kSgsinsGDNVkx69KaqoqXaYaxLH/TWvlQsvigA32vpKQXdb3NcEeZFABAOEgTl08bygJIAHYOOy9XPl5bEmHboyr7oMSkkqgHGn+rITNRFLhvXUKe/U9SYq4Rth3jcI0OzbSIW1ErNalC6ZW4g8kBKFDvYapE5W3EB8EmW2GkRWd3WgB2USPra5DwsLyq+7kLM06Otg8ZIzcYHNqU5HWpScB/xLrPDBZXYzF2/vjSoweSRCHoTQkEj4PecxMn8YE9cyGSC83HzzIwtQ1kmWoZ6kUtcYTENhq0SnRIK0JrdqbtNYx68ODhZAkFSBACG44ssjBuBEeHV7hXv+DKMIlc=
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | ## 版本号:v1.7.19日期:2023-11-19
2 | ### 变更内容
3 | - 增加: sign 命令支持 v4签名
4 | - 更新: golang.org/x/crypto v0.17.0
5 | - 更新: alyun-oss-sdk-go v3.0.2
6 | - 更新: ecs ram provider 支持返回错误
7 |
8 | ## 版本号:v1.7.18日期:2023-10-30
9 | ### 变更内容
10 | - 增加: 支持更多的probe 测试项
11 | - 增加: cp 命令支持 start-time 和 end-time 选项.
12 |
13 | ## 版本号:v1.7.17日期:2023-09-18
14 | ### 变更内容
15 | - 增加: 支持 path-style 访问方式
16 |
17 | ## 版本号:v1.7.16日期:2023-05-19
18 | ### 变更内容
19 | - 增加: 支持资源组接口
20 | - 增加: 支持图片样式接口
21 | - 增加: 支持更多的区域复制接口
22 | - 增加:支持更多的canme接口
23 | - 修复:arm环境下,update 命令更新不正确的问题
24 | - 增加:mb命令支持服务端加密参数
25 |
26 | ## 版本号:v1.7.15日期:2023-01-12
27 | ### 变更内容
28 | - 增加: 支持访问跟踪接口
29 | - 增加: 配置文件新增Default节,支持配置通用参数
30 | - 增加: sign命令支持query-param 选项,支持更多的参数设置
31 | - 修复:优化对symlink类型object下载, 对象大小获取不正确时的处理
32 |
33 | ## 版本号:v1.7.14日期:2022-09-14
34 | ### 变更内容
35 | - 增加: 分片上传支持X-Oss-Notification 参数
36 | - 增加: 过滤 mime type 接口返回的 charset= 信息
37 | - 增加: config 配置文件支持 user-agent 参数
38 | - 增加: sign 命令支持更多的凭证设置参数
39 | - 修复:修复sync命令在不同文件系统下重名失败的问题
40 | - 修复:修复 set meta 接口 不支持通用 header 参数设置
41 | - 修复:windows平台下, 不使用uname 方式获取平台信息
42 |
43 | ## 版本号:v1.7.13日期:2022-05-25
44 | ### 变更内容
45 | - 增加: cname命令支持创建、删除等操作
46 | - 增加: 清单命令使用xml的api
47 | - 修复:修复lcb命令显示不齐问题
48 |
49 |
50 | ## 版本号:v1.7.12日期:2022-05-13
51 | ### 变更内容
52 | - 增加: 支持cloud box
53 | - 增加: 支持v4签名
54 |
55 | ## 版本号:v1.7.11日期:2022-04-22
56 | ### 变更内容
57 | - 增加: restore和set-meta支持传入文件进行批量操作
58 |
59 |
60 | ## 版本号:v1.7.10日期:2022-03-24
61 | ### 变更内容
62 | - 增加: lifecycle命令使用新接口,直接返回服务端的xml内容
63 |
64 |
65 | ## 版本号:v1.7.9日期:2022-01-21
66 | ### 变更内容
67 | - 增加: 取消对于meta格式的限制,只要以x-oss开头即可
68 |
69 | ## 版本号:v1.7.8日期:2021-12-15
70 | ### 变更内容
71 | - 增加: cp下载支持传入meta信息,比如Accept-Encoding
72 | - 修复:set-acl修改bucket权限属性时候,如果bucket不存在行为从“自动创建”改成报错
73 | - 增加: 创建bucket支持传入配置文件,配置文件内容为bucket的创建属性
74 | - 增加: cp命令批量上传文件,如果扫描本地文件出错支持暂时忽略
75 | - 增加: 增加--ua选项,支持用户自定义user agent,会加在工具设置的user agent后面
76 |
77 | ## 版本号:v1.7.7日期:2021-08-26
78 | ### 变更内容
79 | - 增加: 支持cname查询
80 | - 增加:lifecycle配置支持prefix overlap
81 |
82 | ## 版本号:v1.7.6日期:2021-08-05
83 | ### 变更内容
84 | - 增加: 支持限速下载
85 | - 增加:支持同步边管理功能
86 |
87 | ## 版本号:v1.7.5 日期:2021-07-09
88 | ### 变更内容
89 | - 增加: 增加lrb命令
90 | - 增加:支持选择跳过服务端证书校验
91 |
92 | ## 版本号:v1.7.4 日期:2021-07-30
93 | ### 变更内容
94 | - 增加: 支持多种鉴权模式
95 |
96 | ## 版本号:v1.7.3 日期:2021-04-09
97 | ### 变更内容
98 | - 增加: du命令支持不同单位显示大小
99 |
100 | ## 版本号:v1.7.2 日期:2021-03-24
101 | ### 变更内容
102 | - 增加: 支持从键盘读取accessKeySecret
103 |
104 | ## 版本号:v1.7.1 日期:2021-01-13
105 | ### 变更内容
106 | - 增加: 增加worm命令
107 |
108 | ## 版本号:v1.7.0 日期:2020-11-19
109 | ### 变更内容
110 | - 增加: 增加sync命令
111 | - 增加:restore归档object配置文件支持传入天数参数
112 |
113 | ## 版本号:v1.6.19 日期:2020-09-03
114 | ### 变更内容
115 | - 修复: set-meta命令的更新模式会将object的acl重置为default
116 |
117 | ## 版本号:v1.6.18 日期:2020-07-24
118 | ### 变更内容
119 | - 修复: 使用oss go sdk v2.1.4重新编译,lifecycle支持输入NoncurrentVersionTransition数组
120 |
121 | ## 版本号:v1.6.17 日期:2020-07-11
122 | ### 变更内容
123 | - 修复: 使用oss go sdk v2.1.3重新编译,lifecycle支持冷归档(ColdArchive)
124 |
125 | ## 版本号:v1.6.16 日期:2020-07-06
126 | ### 变更内容
127 | - 新增: 优化对symlink类型object下载,object size取指向的目标object size
128 |
129 | ## 版本号:v1.6.15 日期:2020-06-24
130 | ### 变更内容
131 | - 新增: 增加revert-versioning命令
132 |
133 | ## 版本号:v1.6.14 日期:2020-06-04
134 | ### 变更内容
135 | - 新增: 支持国密(SM4)及其BYOK功能
136 |
137 | ## 版本号:v1.6.13 日期:2020-05-09
138 | ### 变更内容
139 | - 修复:windows vpn某些场景下获取home目录比较耗时
140 | - 修复:优化user agent获取频率, 仅启动时候获取一次
141 | - 修复: 下载时候对于目录已经存在情况不显示确认信息
142 |
143 | ## 版本号:v1.6.12 日期:2020-04-21
144 | ### 变更内容
145 | - 增加:支持清单、冷归档
146 | - 修复:cp命令进度条超过100%修复
147 |
148 | ## 版本号:v1.6.11 日期:2020-04-09
149 | ### 变更内容
150 | - 增加:du命令支持多版本
151 | - 增加:cp命令支持设置tagging
152 | - 增加: cp命令支持是否忽略错误选项
153 |
154 | ## 版本号:v1.6.10 日期:2020-01-03
155 | ### 变更内容
156 | - 增加:增加选项--disable-all-symlink, 上传时可以忽略所有的链接文件以及链接子目录
157 | - 增加:签名url支持访问者付费
158 | - 修复: 修复统计链接文件FileSize为0的bug
159 |
160 |
161 | ## 版本号:v1.6.9 日期:2019-11-13
162 | ### 变更内容
163 | - 增加:优化config命令的提示
164 | - 增加:支持创建3AZ的bucket
165 |
166 | ## 版本号:v1.6.8 日期:2019-10-23
167 | ### 变更内容
168 | - 增加:支持上传链接子目录
169 | - 增加:支持单级目录而非递归目录上传、下载以及copy
170 | - 增加:支持工具绑定指定ip地址(针对用户多网卡场景)
171 | - 增加:支持本地循环链接文件或者目录检测
172 | - 增加:支持并发带宽上传和下载测速
173 | - 增加:sign命令可以选择将path中的符号/不进行编码
174 | - 增加:cp命令上传时可以选择不为目录生成object
175 | - 修复: cp命令的失败重试会导致进度条超过100%
176 |
177 | ## 版本号:v1.6.7 日期:2019-09-17
178 | ### 变更内容
179 | - 修复:使用最新oss go sdk v2.0.3编译,修复无法分块上传归档object的bug
180 |
181 | ## 版本号:v1.6.6 日期:2019-08-06
182 | ### 变更内容
183 | - 增加:支持http/https/socks5代理
184 | - 增加:增加du、policy、request-payment、object-tagging命令
185 | - 增加:appendfromfile,cat,symlink,ls,restore,rm,stat支持访问者付费模式
186 | - 增加:url签名支持http连接限速参数x-oss-traffic-limit
187 | - 增加:read-symlink支持多版本
188 | - 增加:修复批量rm objects异常时,错误信息提示错误
189 |
190 | ## 版本号:v1.6.5 日期:2019-07-18
191 | ### 变更内容
192 | - 增加:cherry-pick之前的多版本特性
193 |
194 | ## 版本号:v1.6.4 日期:2019-07-12
195 | ### 变更内容
196 | - 增加:增加user-qos, bucket-qos命令
197 | - 增加: 支持利用ecs绑定的角色操作
198 | - 增加: ls & rm命令支持include、exclude选项
199 | - 修复: cp命令传输速度统计不准
200 | - 修复: windows平台配置项长度不能超过256字符
201 | - 修复: 无法删除key中带有特殊字符的object
202 |
203 | ## 版本号:v1.6.3 日期:2019-06-19
204 | ### 变更内容
205 | - 增加:增加lifecycle, website, cors-options命令
206 |
207 | ## 版本号:v1.6.2 日期:2019-06-06
208 | ### 变更内容
209 | - 增加:增加多版本versioning特性
210 |
211 | ## 版本号:v1.6.1 日期:2019-05-29
212 | ### 变更内容
213 | - 增加:增加bucket-tagging命令
214 | - 增加:增加bucket-encryption命令
215 | - 增加:为下载支持snapshot-path选项
216 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2016 aliyun
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README-CN.md:
--------------------------------------------------------------------------------
1 | # Aliyun OSSUTIL
2 |
3 | [](https://badge.fury.io/gh/aliyun%2Fossutil)
4 | [](https://travis-ci.org/aliyun/ossutil)
5 | [](https://coveralls.io/github/aliyun/ossutil?branch=master)
6 |
7 | ### [README of English](https://github.com/aliyun/ossutil/blob/master/README.md)
8 |
9 | ## 关于
10 |
11 | - 此工具采用go语言,基于OSS[阿里云对象存储服务](http://www.aliyun.com/product/oss/)官方GO SDK 构建。
12 | - 阿里云对象存储(Object Storage Service,简称OSS),是阿里云对外提供的海量,安全,低成本,高可靠的云存储服务。
13 | - OSS适合存放任意文件类型,适合各种网站、开发企业及开发者使用。
14 | - 该工具旨在为用户提供一个方便的,以命令行方式管理OSS数据的途径。
15 | - 当前版本提供了列举和删除Multipart Uploads功能。
16 | - 当前版本未提供Bucket管理功能功能,相关功能会在后续版本中开发。
17 |
18 | ## 版本
19 |
20 | - 当前版本:v1.7.19
21 |
22 | ## 运行环境
23 |
24 | - Linux
25 | - Windows
26 | - Mac
27 |
28 | ## 依赖的库
29 |
30 | - goopt (github.com/droundy/goopt)
31 | - configparser (github.com/alyu/configparser)
32 | - leveldb (github.com/syndtr/goleveldb/leveldb)
33 | - oss (github.com/aliyun/aliyun-oss-go-sdk/oss)
34 | - gopkg.in/check.v1 (gopkg.in/check.v1)
35 |
36 | ## 快速使用
37 |
38 | #### 获取命令列表
39 |
40 | ```go
41 | ./ossutil
42 | 或 ./ossutil help
43 | ```
44 |
45 | #### 查看某命令的帮助文档
46 |
47 | ```go
48 | ./ossutil help cmd
49 | ```
50 |
51 | #### 配置ossutil
52 |
53 | ```go
54 | ./ossutil config
55 | ```
56 |
57 | #### 列举Buckets
58 |
59 | ```go
60 | ./ossutil ls
61 | 或 ./ossutil ls oss://
62 | ```
63 |
64 | #### 列举objects和Multipart Uploads
65 |
66 | ```go
67 | ./ossutil ls -a
68 | 或 ./ossutil ls oss:// -a
69 | ```
70 |
71 | #### 上传文件
72 |
73 | ```go
74 | ./ossutil cp localfile oss://bucket
75 | ```
76 |
77 | #### 其它
78 |
79 | 请使用./ossutil help cmd来查看想要使用的命令的帮助文档。
80 |
81 | ## 注意事项
82 |
83 | ### 运行
84 |
85 | - 首先配置您的go工程目录。
86 | - go get该工程依赖的库。
87 | - go get github.com/aliyun/ossutil。
88 | - 进入go工程目录下的src目录,build代码生成ossutil工具,例如:在linux下可以运行go build github.com/aliyun/ossutil/ossutil.go。
89 | - 参考上面示例运行ossutil工具。
90 |
91 | ### 测试
92 |
93 | - 进入go工程目录下的src目录,修改github.com/aliyun/ossutil/lib/command_test.go里的endpoint、AccessKeyId、AccessKeySecret、STSToken等配置。
94 | - 请在lib目录下执行`go test`。
95 |
96 | ## 联系我们
97 |
98 | - [阿里云OSS官方网站](http://oss.aliyun.com)
99 | - [阿里云OSS官方论坛](http://bbs.aliyun.com)
100 | - [阿里云OSS官方文档中心](http://www.aliyun.com/product/oss#Docs)
101 |
102 | ## 作者
103 |
104 | - [Ting Zhang](https://github.com/dengwu12)
105 |
106 | ## License
107 |
108 | - [MIT](https://github.com/aliyun/ossutil/blob/master/LICENSE)
109 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Alibaba Cloud OSSUTIL
2 |
3 | [](https://badge.fury.io/gh/aliyun%2Fossutil)
4 | [](https://travis-ci.org/aliyun/ossutil)
5 | [](https://coveralls.io/github/aliyun/ossutil?branch=master)
6 |
7 | ### [README of Chinese](https://github.com/aliyun/ossutil/blob/master/README-CN.md)
8 |
9 | ## About
10 |
11 | - This tool is developed with Go and built on the official GO SDK of OSS [Alibaba Cloud Object Storage Service](http://www.aliyun.com/product/oss/).
12 | - OSS is a cloud storage service provided by Alibaba Cloud, featuring massive capacity, security, low cost, and high reliability.
13 | - OSS can store any type of files. It applies to various websites, development enterprises and developers.
14 | - This tool aims to provide a convenient-to-use command line for users to manage data in OSS.
15 | - The current version provides to list and delete multipart upload tasks.
16 | - The current version does not support bucket management. The feature will be available in future versions.
17 |
18 | ## Version
19 |
20 | - Current version: v1.7.19
21 |
22 | ## Run environment
23 |
24 | - Linux
25 | - Windows
26 | - Mac
27 |
28 | ## Dependent libraries
29 |
30 | - goopt (github.com/droundy/goopt)
31 | - configparser (github.com/alyu/configparser)
32 | - leveldb (github.com/syndtr/goleveldb/leveldb)
33 | - oss (github.com/aliyun/aliyun-oss-go-sdk/oss)
34 | - gopkg.in/check.v1 (gopkg.in/check.v1)
35 |
36 | ## Quick use
37 |
38 | #### Get the command list
39 |
40 | ```go
41 | ./ossutil
42 | or ./ossutil help
43 | ```
44 |
45 | #### View the help documentation for a command
46 |
47 | ```go
48 | ./ossutil help cmd
49 | ```
50 |
51 | #### Configure OSSUTIL
52 |
53 | ```go
54 | ./ossutil config
55 | ```
56 |
57 | #### List buckets
58 |
59 | ```go
60 | ./ossutil ls
61 | or ./ossutil ls oss://
62 | ```
63 |
64 | #### List objects and multipart upload tasks
65 |
66 | ```go
67 | ./ossutil ls -a
68 | or ./ossutil ls oss:// -a
69 | ```
70 |
71 | #### Upload a file
72 |
73 | ```go
74 | ./ossutil cp localfile oss://bucket
75 | ```
76 |
77 | #### Others
78 |
79 | You can use `./ossutil help cmd` to view the help documentation for the command you want to use.
80 |
81 | ## Notes
82 |
83 | ### Run OSSUTIL
84 |
85 | - First, configure your Go project directory.
86 | - Use `go get` to get the library that ossutil depends on.
87 | - Run `go get github.com/aliyun/ossutil`.
88 | - Enter the *src* directory under the Go project directory and build to generate the OSSUTIL tool. For example, on Linux, you can run `go build github.com/aliyun/ossutil/ossutil.go`.
89 | - Refer to the example above to run the OSSUTIL tool.
90 |
91 | ### Test
92 |
93 | - Enter the *src* directory under the Go project directory and modify the endpoint, AccessKeyId, AccessKeySecret and STSToken configuration items in the *github.com/aliyun/ossutil/lib/command_test.go*.
94 | - Run `go test` under the *lib* directory.
95 |
96 | ## Contact us
97 |
98 | - [Alibaba Cloud OSS official website](http://oss.aliyun.com).
99 | - [Alibaba Cloud OSS official forum](http://bbs.aliyun.com).
100 | - [Alibaba Cloud OSS official documentation center](http://www.aliyun.com/product/oss#Docs).
101 |
102 | ## Author
103 |
104 | - [Ting Zhang](https://github.com/dengwu12)
105 |
106 | ## License
107 |
108 | - [MIT](https://github.com/aliyun/ossutil/blob/master/LICENSE)
109 |
--------------------------------------------------------------------------------
/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/aliyun/ossutil
2 |
3 | go 1.13
4 |
5 | require (
6 | github.com/aliyun/aliyun-oss-go-sdk v3.0.2+incompatible
7 | github.com/alyu/configparser v0.0.0-20191103060215-744e9a66e7bc
8 | github.com/droundy/goopt v0.0.0-20220217183150-48d6390ad4d1
9 | github.com/syndtr/goleveldb v1.0.0
10 | golang.org/x/crypto v0.17.0
11 | golang.org/x/time v0.0.0-20220722155302-e5dcc9cfc0b9 // indirect
12 | gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c
13 | )
14 |
--------------------------------------------------------------------------------
/go.sum:
--------------------------------------------------------------------------------
1 | github.com/aliyun/aliyun-oss-go-sdk v3.0.2+incompatible h1:8psS8a+wKfiLt1iVDX79F7Y6wUM49Lcha2FMXt4UM8g=
2 | github.com/aliyun/aliyun-oss-go-sdk v3.0.2+incompatible/go.mod h1:T/Aws4fEfogEE9v+HPhhw+CntffsBHJ8nXQCwKr0/g8=
3 | github.com/alyu/configparser v0.0.0-20191103060215-744e9a66e7bc h1:eN2FUvn4J1A31pICABioDYukoh1Tmlei6L3ImZUin/I=
4 | github.com/alyu/configparser v0.0.0-20191103060215-744e9a66e7bc/go.mod h1:BYq/NZTroWuzkvsTPJgRBqSHGxKMHCz06gtlfY/W5RU=
5 | github.com/droundy/goopt v0.0.0-20220217183150-48d6390ad4d1 h1:6PKU05V7zJIJlTBq7AnEIrLVEUIYF4NjTU2a28Ho6ko=
6 | github.com/droundy/goopt v0.0.0-20220217183150-48d6390ad4d1/go.mod h1:ytRJ64WkuW4kf6/tuYqBATBCRFUP8X9+LDtgcvE+koI=
7 | github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
8 | github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
9 | github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM=
10 | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
11 | github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db h1:woRePGFeVFfLKN/pOkfl+p/TAqKOfFu+7KPlMVpok/w=
12 | github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
13 | github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
14 | github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
15 | github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI=
16 | github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
17 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
18 | github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
19 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
20 | github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
21 | github.com/onsi/ginkgo v1.7.0 h1:WSHQ+IS43OoUrWtD1/bbclrwK8TTH5hzp+umCiuxHgs=
22 | github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
23 | github.com/onsi/gomega v1.4.3 h1:RE1xgDvH7imwFD45h+u2SgIfERHlS2yNG4DObb5BSKU=
24 | github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
25 | github.com/syndtr/goleveldb v1.0.0 h1:fBdIW9lB4Iz0n9khmH8w27SJ3QEJ7+IgjPEwGSZiFdE=
26 | github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ=
27 | github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
28 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
29 | golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
30 | golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k=
31 | golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
32 | golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
33 | golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
34 | golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
35 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
36 | golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
37 | golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
38 | golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
39 | golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M=
40 | golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
41 | golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
42 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
43 | golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
44 | golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
45 | golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
46 | golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
47 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
48 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
49 | golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
50 | golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
51 | golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
52 | golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
53 | golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
54 | golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc=
55 | golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
56 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
57 | golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
58 | golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
59 | golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
60 | golang.org/x/term v0.15.0 h1:y/Oo/a/q3IXu26lQgl04j/gjuBDOBlx7X6Om1j2CPW4=
61 | golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0=
62 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
63 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
64 | golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
65 | golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
66 | golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
67 | golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
68 | golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
69 | golang.org/x/time v0.0.0-20220722155302-e5dcc9cfc0b9 h1:ftMN5LMiBFjbzleLqtoBZk7KdJwhuybIU+FckUHgoyQ=
70 | golang.org/x/time v0.0.0-20220722155302-e5dcc9cfc0b9/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
71 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
72 | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
73 | golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
74 | golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
75 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
76 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
77 | gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
78 | gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
79 | gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
80 | gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
81 | gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
82 | gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
83 | gopkg.in/yaml.v2 v2.2.1 h1:mUhvW9EsL+naU5Q3cakzfE91YhliOondGd6ZrsDBHQE=
84 | gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
85 |
--------------------------------------------------------------------------------
/lib/allpart_size.go:
--------------------------------------------------------------------------------
1 | package lib
2 |
3 | import (
4 | "fmt"
5 | "net/url"
6 | "strconv"
7 |
8 | oss "github.com/aliyun/aliyun-oss-go-sdk/oss"
9 | )
10 |
11 | var specChineseAllPartSize = SpecText{
12 | synopsisText: "获取bucket所有未完成上传的multipart object的分块大小以及总和",
13 |
14 | paramText: "bucket_url [options]",
15 |
16 | syntaxText: `
17 | ossutil getallpartsize oss://bucket [options]
18 | `,
19 |
20 | detailHelpText: `
21 | 该命令会获取bucket所有未完成上传的multipart object的分块大小以及总和
22 |
23 |
24 | 用法:
25 |
26 | 该命令只有一种用法:
27 |
28 | 1) ossutil getallpartsize oss://bucket [options]
29 | 查询bucket的所有未完成上传的multipart object的块大小信息以及总和
30 | `,
31 |
32 | sampleText: `
33 | 1) 根据bucket查询所有未完成上传的multipart object的块大小信息以及总和
34 | ossutil getallpartsize oss://bucket
35 | `,
36 | }
37 |
38 | var specEnglishAllPartSize = SpecText{
39 | synopsisText: "Get bucket all uncompleted multipart objects's parts size and sum size",
40 |
41 | paramText: "bucket_url [options]",
42 |
43 | syntaxText: `
44 | ossutil getallpartsize oss://bucket [options]
45 | `,
46 |
47 | detailHelpText: `
48 | This command will list every uncompleted multipart objects's part size and sum size
49 |
50 |
51 | Usages:
52 |
53 | There is only one usage for this command:
54 |
55 | 1) ossutil getallpartsize oss://bucket [options]
56 | Get bucket all uncompleted mulitpart objects's parts size and sum size
57 | `,
58 |
59 | sampleText: `
60 | 1) Get bucket all uncompleted multipart objects's parts size and sum size
61 | ossutil getallpartsize oss://bucket
62 | `,
63 | }
64 |
65 | type allPartSizeOptionType struct {
66 | bucketName string
67 | encodingType string
68 | headLineShowed bool
69 | statList []StatPartInfo
70 | }
71 |
72 | type AllPartSizeCommand struct {
73 | command Command
74 | apOption allPartSizeOptionType
75 | }
76 |
77 | var allPartSizeCommand = AllPartSizeCommand{
78 | command: Command{
79 | name: "getallpartsize",
80 | nameAlias: []string{"getallpartsize"},
81 | minArgc: 1,
82 | maxArgc: 1,
83 | specChinese: specChineseAllPartSize,
84 | specEnglish: specEnglishAllPartSize,
85 | group: GroupTypeNormalCommand,
86 | validOptionNames: []string{
87 | OptionConfigFile,
88 | OptionEndpoint,
89 | OptionAccessKeyID,
90 | OptionAccessKeySecret,
91 | OptionSTSToken,
92 | OptionProxyHost,
93 | OptionProxyUser,
94 | OptionProxyPwd,
95 | OptionEncodingType,
96 | OptionLogLevel,
97 | OptionPassword,
98 | OptionMode,
99 | OptionECSRoleName,
100 | OptionTokenTimeout,
101 | OptionRamRoleArn,
102 | OptionRoleSessionName,
103 | OptionReadTimeout,
104 | OptionConnectTimeout,
105 | OptionSTSRegion,
106 | OptionSkipVerifyCert,
107 | OptionUserAgent,
108 | OptionSignVersion,
109 | OptionRegion,
110 | OptionCloudBoxID,
111 | OptionForcePathStyle,
112 | },
113 | },
114 | }
115 |
116 | type StatPartInfo struct {
117 | objectName string
118 | uploadId string
119 | }
120 |
121 | // function for FormatHelper interface
122 | func (apc *AllPartSizeCommand) formatHelpForWhole() string {
123 | return apc.command.formatHelpForWhole()
124 | }
125 |
126 | func (apc *AllPartSizeCommand) formatIndependHelp() string {
127 | return apc.command.formatIndependHelp()
128 | }
129 |
130 | // Init simulate inheritance, and polymorphism
131 | func (apc *AllPartSizeCommand) Init(args []string, options OptionMapType) error {
132 | return apc.command.Init(args, options, apc)
133 | }
134 |
135 | // RunCommand simulate inheritance, and polymorphism
136 | func (apc *AllPartSizeCommand) RunCommand() error {
137 | srcBucketUrL, err := GetCloudUrl(apc.command.args[0], "")
138 | if err != nil {
139 | return err
140 | }
141 | apc.apOption.bucketName = srcBucketUrL.bucket
142 | apc.apOption.encodingType, _ = GetString(OptionEncodingType, apc.command.options)
143 |
144 | // first:get all object uploadid
145 | err = apc.GetAllStatInfo()
146 | if err != nil {
147 | return err
148 | }
149 |
150 | //second:stat every object parts
151 | client, err := apc.command.ossClient(apc.apOption.bucketName)
152 | if err != nil {
153 | return err
154 | }
155 |
156 | bucket, err := client.Bucket(apc.apOption.bucketName)
157 | if err != nil {
158 | return err
159 | }
160 |
161 | var totalPartCount int64 = 0
162 | var totalPartSize int64 = 0
163 | for _, v := range apc.apOption.statList {
164 | partCount, partSize, err := apc.GetObjectPartsSize(bucket, v)
165 | if err != nil {
166 | return err
167 | }
168 | totalPartCount += partCount
169 | totalPartSize += partSize
170 | }
171 |
172 | if totalPartSize > 0 {
173 | fmt.Printf("\ntotal part count:%d\ttotal part size(MB):%.2f\n\n", totalPartCount, float64(totalPartSize/1024)/1024)
174 | }
175 |
176 | return nil
177 | }
178 |
179 | func (apc *AllPartSizeCommand) GetAllStatInfo() error {
180 | client, err := apc.command.ossClient(apc.apOption.bucketName)
181 | if err != nil {
182 | return err
183 | }
184 |
185 | bucket, err := client.Bucket(apc.apOption.bucketName)
186 | if err != nil {
187 | return err
188 | }
189 |
190 | keyMarker := ""
191 | uploadIdMarker := ""
192 | for i := 0; ; i++ {
193 | lpOptions := []oss.Option{}
194 | lpOptions = append(lpOptions, oss.MaxParts(1000))
195 | lpOptions = append(lpOptions, oss.KeyMarker(keyMarker))
196 | lpOptions = append(lpOptions, oss.UploadIDMarker(uploadIdMarker))
197 |
198 | lpRes, err := bucket.ListMultipartUploads(lpOptions...)
199 | if err != nil {
200 | return err
201 | }
202 |
203 | for _, v := range lpRes.Uploads {
204 | var statPartInfo StatPartInfo
205 | statPartInfo.objectName = v.Key
206 | statPartInfo.uploadId = v.UploadID
207 | apc.apOption.statList = append(apc.apOption.statList, statPartInfo)
208 | }
209 |
210 | if lpRes.IsTruncated {
211 | keyMarker = lpRes.NextKeyMarker
212 | uploadIdMarker = lpRes.NextUploadIDMarker
213 | } else {
214 | break
215 | }
216 | }
217 | return nil
218 | }
219 |
220 | func (apc *AllPartSizeCommand) GetObjectPartsSize(bucket *oss.Bucket, statInfo StatPartInfo) (int64, int64, error) {
221 | var imur oss.InitiateMultipartUploadResult
222 | imur.Bucket = apc.apOption.bucketName
223 | imur.Key = statInfo.objectName
224 | imur.UploadID = statInfo.uploadId
225 |
226 | partNumberMarker := 0
227 | var totalPartCount int64 = 0
228 | var totalPartSize int64 = 0
229 | var cloudUrl CloudURL
230 | for i := 0; ; i++ {
231 | lpOptions := []oss.Option{}
232 | lpOptions = append(lpOptions, oss.MaxParts(1000))
233 | lpOptions = append(lpOptions, oss.PartNumberMarker(partNumberMarker))
234 |
235 | lpRes, err := bucket.ListUploadedParts(imur, lpOptions...)
236 | if err != nil {
237 | return 0, 0, err
238 | } else {
239 | totalPartCount += int64(len(lpRes.UploadedParts))
240 | if !apc.apOption.headLineShowed && len(lpRes.UploadedParts) > 0 {
241 | fmt.Printf("%-10s\t%-32s\t%-10s\t%s\n", "PartNumber", "UploadId", "Size(Byte)", "Path")
242 | apc.apOption.headLineShowed = true
243 | }
244 | }
245 |
246 | for _, v := range lpRes.UploadedParts {
247 | cloudUrl.bucket = apc.apOption.bucketName
248 | if apc.apOption.encodingType == URLEncodingType {
249 | cloudUrl.object = url.QueryEscape(imur.Key)
250 | } else {
251 | cloudUrl.object = imur.Key
252 | }
253 |
254 | //PartNumber,uploadId,Size,Path
255 | fmt.Printf("%-10d\t%-32s\t%-10d\t%s\n", v.PartNumber, imur.UploadID, v.Size, cloudUrl.ToString())
256 | totalPartSize += int64(v.Size)
257 | }
258 |
259 | if lpRes.IsTruncated {
260 | partNumberMarker, err = strconv.Atoi(lpRes.NextPartNumberMarker)
261 | if err != nil {
262 | return 0, 0, err
263 | }
264 | } else {
265 | break
266 | }
267 | }
268 | return totalPartCount, totalPartSize, nil
269 |
270 | }
271 |
--------------------------------------------------------------------------------
/lib/allpart_size_test.go:
--------------------------------------------------------------------------------
1 | package lib
2 |
3 | import (
4 | "fmt"
5 | "os"
6 | "strings"
7 |
8 | oss "github.com/aliyun/aliyun-oss-go-sdk/oss"
9 | . "gopkg.in/check.v1"
10 | )
11 |
12 | func (s *OssutilCommandSuite) TestAllPartSize(c *C) {
13 | // create bucket
14 | bucketName := bucketNamePrefix + randLowStr(12)
15 | s.putBucket(bucketName, c)
16 |
17 | // object name
18 | objectName := "test-ossutil-object-" + randLowStr(10)
19 |
20 | // create file
21 | fileName := "test-ossutil-appendfile" + randLowStr(5)
22 | strText := randLowStr(1024 * 10)
23 | s.createFile(fileName, strText, c)
24 |
25 | // prepare chunks
26 | chunks, err := oss.SplitFileByPartNum(fileName, 10)
27 | c.Assert(err, IsNil)
28 |
29 | fd, err := os.Open(fileName)
30 | c.Assert(err, IsNil)
31 | defer fd.Close()
32 |
33 | // begin upload part
34 | client, err := oss.New(endpoint, accessKeyID, accessKeySecret)
35 | c.Assert(err, IsNil)
36 |
37 | bucket, err := client.Bucket(bucketName)
38 | c.Assert(err, IsNil)
39 |
40 | imur, err := bucket.InitiateMultipartUpload(objectName)
41 | c.Assert(err, IsNil)
42 |
43 | var parts []oss.UploadPart
44 | for _, chunk := range chunks {
45 | fd.Seek(chunk.Offset, os.SEEK_SET)
46 | part, err := bucket.UploadPart(imur, fd, chunk.Size, chunk.Number)
47 | c.Assert(err, IsNil)
48 | parts = append(parts, part)
49 | }
50 |
51 | // not CompleteMultipartUpload
52 |
53 | // begin getallpartsize
54 | var str string
55 | options := OptionMapType{
56 | "endpoint": &str,
57 | "accessKeyID": &str,
58 | "accessKeySecret": &str,
59 | "stsToken": &str,
60 | "configFile": &configFile,
61 | }
62 |
63 | // output to file
64 | outputFile := "test-file-" + randLowStr(5)
65 | testResultFile, err = os.OpenFile(outputFile, os.O_RDWR|os.O_TRUNC|os.O_CREATE, 0664)
66 | c.Assert(err, IsNil)
67 |
68 | oldStdout := os.Stdout
69 | os.Stdout = testResultFile
70 |
71 | alArgs := []string{CloudURLToString(bucketName, "")}
72 | _, err = cm.RunCommand("getallpartsize", alArgs, options)
73 | if err != nil {
74 | fmt.Printf("error:%s\n", err.Error())
75 | }
76 | c.Assert(err, IsNil)
77 | testResultFile.Close()
78 |
79 | os.Stdout = oldStdout
80 |
81 | // check file content
82 | outBody := s.readFile(outputFile, c)
83 | c.Assert(strings.Contains(outBody, "total part count:10"), Equals, true)
84 |
85 | os.Remove(outputFile)
86 | os.Remove(fileName)
87 | s.removeBucket(bucketName, true, c)
88 | }
89 |
90 | func (s *OssutilCommandSuite) TestAllPartSizeBucketError(c *C) {
91 |
92 | var str string
93 | options := OptionMapType{
94 | "endpoint": &str,
95 | "accessKeyID": &str,
96 | "accessKeySecret": &str,
97 | "stsToken": &str,
98 | "configFile": &configFile,
99 | }
100 |
101 | alArgs := []string{"oss:////"}
102 | _, err := cm.RunCommand("getallpartsize", alArgs, options)
103 | c.Assert(err, NotNil)
104 | }
105 |
106 | func (s *OssutilCommandSuite) TestAllPartSizeEmptyEndpoint(c *C) {
107 | bucketName := bucketNamePrefix + randLowStr(12)
108 | s.putBucket(bucketName, c)
109 |
110 | cfile := randStr(10)
111 | data := "[Credentials]" + "\n" + "language=CH" + "\n" + "accessKeyID=123" + "\n" + "accessKeySecret=456" + "\n" + "endpoint="
112 | s.createFile(cfile, data, c)
113 |
114 | var str string
115 | options := OptionMapType{
116 | "endpoint": &str,
117 | "accessKeyID": &str,
118 | "accessKeySecret": &str,
119 | "stsToken": &str,
120 | "configFile": &cfile,
121 | }
122 |
123 | alArgs := []string{CloudURLToString(bucketName, "")}
124 | _, err := cm.RunCommand("getallpartsize", alArgs, options)
125 | c.Assert(err, NotNil)
126 |
127 | os.Remove(cfile)
128 |
129 | s.removeBucket(bucketName, true, c)
130 | }
131 |
132 | func (s *OssutilCommandSuite) TestAllPartSizeHelp(c *C) {
133 | // mkdir command test
134 | options := OptionMapType{}
135 |
136 | mkArgs := []string{"getallpartsize"}
137 | _, err := cm.RunCommand("help", mkArgs, options)
138 | c.Assert(err, IsNil)
139 |
140 | mkArgs = []string{}
141 | _, err = cm.RunCommand("help", mkArgs, options)
142 | c.Assert(err, IsNil)
143 | }
144 |
--------------------------------------------------------------------------------
/lib/bucket_resource_group_test.go:
--------------------------------------------------------------------------------
1 | package lib
2 |
3 | import (
4 | "encoding/xml"
5 | "github.com/aliyun/aliyun-oss-go-sdk/oss"
6 | . "gopkg.in/check.v1"
7 | "os"
8 | )
9 |
10 | func (s *OssutilCommandSuite) TestResourceGroupHelpInfo(c *C) {
11 | // mkdir command test
12 | options := OptionMapType{}
13 |
14 | mkArgs := []string{"resource-group"}
15 | _, err := cm.RunCommand("help", mkArgs, options)
16 | c.Assert(err, IsNil)
17 |
18 | mkArgs = []string{}
19 | _, err = cm.RunCommand("help", mkArgs, options)
20 | c.Assert(err, IsNil)
21 | }
22 |
23 | func (s *OssutilCommandSuite) TestPutBucketResourceGroupError(c *C) {
24 | bucketName := bucketNamePrefix + randLowStr(12)
25 | s.putBucket(bucketName, c)
26 |
27 | resourceFileName := "resource-group" + randLowStr(12)
28 |
29 | // resource group command test
30 | var str string
31 | strMethod := ""
32 | options := OptionMapType{
33 | "endpoint": &str,
34 | "accessKeyID": &str,
35 | "accessKeySecret": &str,
36 | "stsToken": &str,
37 | "configFile": &configFile,
38 | "method": &strMethod,
39 | }
40 |
41 | // method is empty
42 | resourceArgs := []string{CloudURLToString(bucketName, ""), resourceFileName}
43 | _, err := cm.RunCommand("resource-group", resourceArgs, options)
44 | c.Assert(err, NotNil)
45 |
46 | //method is error
47 | strMethod = "puttt"
48 | options["method"] = &strMethod
49 | _, err = cm.RunCommand("resource-group", resourceArgs, options)
50 | c.Assert(err, NotNil)
51 |
52 | // cloudurl is error
53 | strMethod = "put"
54 | options["method"] = &strMethod
55 | resourceArgs = []string{"http://mybucket", resourceFileName}
56 | _, err = cm.RunCommand("resource-group", resourceArgs, options)
57 | c.Assert(err, NotNil)
58 |
59 | // local file is emtpy
60 | resourceArgs = []string{CloudURLToString(bucketName, ""), resourceFileName}
61 | _, err = cm.RunCommand("resource-group", resourceArgs, options)
62 | c.Assert(err, NotNil)
63 |
64 | //local file is not exist
65 | os.Remove(resourceFileName)
66 | resourceArgs = []string{CloudURLToString(bucketName, ""), resourceFileName}
67 | _, err = cm.RunCommand("resource-group", resourceArgs, options)
68 | c.Assert(err, NotNil)
69 |
70 | // local file is dir
71 | err = os.MkdirAll(resourceFileName, 0755)
72 | c.Assert(err, IsNil)
73 | resourceArgs = []string{CloudURLToString(bucketName, ""), resourceFileName}
74 | _, err = cm.RunCommand("resource-group", resourceArgs, options)
75 | c.Assert(err, NotNil)
76 | os.Remove(resourceFileName)
77 |
78 | //local file is empty
79 | s.createFile(resourceFileName, "", c)
80 | _, err = cm.RunCommand("resource-group", resourceArgs, options)
81 | c.Assert(err, NotNil)
82 | os.Remove(resourceFileName)
83 |
84 | //local file is not xml file
85 | s.createFile(resourceFileName, "aaa", c)
86 | _, err = cm.RunCommand("resource-group", resourceArgs, options)
87 | c.Assert(err, NotNil)
88 | os.Remove(resourceFileName)
89 |
90 | // StorageURLFromString error
91 | resourceArgs = []string{"oss:///1.jpg"}
92 | _, err = cm.RunCommand("resource-group", resourceArgs, options)
93 | c.Assert(err, NotNil)
94 |
95 | // bucketname is error
96 | resourceArgs = []string{"oss:///"}
97 | _, err = cm.RunCommand("resource-group", resourceArgs, options)
98 | c.Assert(err, NotNil)
99 |
100 | //missing parameter
101 | resourceArgs = []string{CloudURLToString(bucketName, "")}
102 | _, err = cm.RunCommand("resource-group", resourceArgs, options)
103 | c.Assert(err, NotNil)
104 |
105 | // bucketname not exist
106 | resourceArgs = []string{CloudURLToString("my-bucket", "")}
107 | _, err = cm.RunCommand("resource-group", resourceArgs, options)
108 | c.Assert(err, NotNil)
109 |
110 | os.Remove(resourceFileName)
111 | s.removeBucket(bucketName, true, c)
112 | }
113 |
114 | func (s *OssutilCommandSuite) TestPutBucketResourceGroup(c *C) {
115 | resourceXml := `
116 |
117 | rg-acfmy7mo47b3adq
118 | `
119 |
120 | accessConfigSrc := oss.PutBucketResourceGroup{}
121 | err := xml.Unmarshal([]byte(resourceXml), &accessConfigSrc)
122 | c.Assert(err, IsNil)
123 |
124 | resourceFileName := randLowStr(12)
125 | s.createFile(resourceFileName, resourceXml, c)
126 |
127 | bucketName := bucketNamePrefix + randLowStr(12)
128 | s.putBucket(bucketName, c)
129 |
130 | // resource group command test
131 | var str string
132 | strMethod := "put"
133 | options := OptionMapType{
134 | "endpoint": &str,
135 | "accessKeyID": &str,
136 | "accessKeySecret": &str,
137 | "stsToken": &str,
138 | "configFile": &configFile,
139 | "method": &strMethod,
140 | }
141 |
142 | command := "resource-group"
143 | resourceArgs := []string{CloudURLToString(bucketName, ""), resourceFileName}
144 | _, err = cm.RunCommand(command, resourceArgs, options)
145 | c.Assert(err, IsNil)
146 |
147 | // check,get resource group
148 | resourceDownName := resourceFileName + "-down"
149 | strMethod = "get"
150 | options = OptionMapType{
151 | "endpoint": &str,
152 | "accessKeyID": &str,
153 | "accessKeySecret": &str,
154 | "stsToken": &str,
155 | "configFile": &configFile,
156 | "method": &strMethod,
157 | }
158 |
159 | resourceArgs = []string{CloudURLToString(bucketName, ""), resourceDownName}
160 | _, err = cm.RunCommand(command, resourceArgs, options)
161 | c.Assert(err, IsNil)
162 |
163 | // check resource group DownName
164 | _, err = os.Stat(resourceDownName)
165 | c.Assert(err, IsNil)
166 |
167 | accessBody := s.readFile(resourceDownName, c)
168 |
169 | var out oss.GetBucketResourceGroupResult
170 | err = xml.Unmarshal([]byte(accessBody), &out)
171 | c.Assert(err, IsNil)
172 |
173 | c.Assert(accessConfigSrc.ResourceGroupId, Equals, out.ResourceGroupId)
174 |
175 | os.Remove(resourceFileName)
176 | os.Remove(resourceDownName)
177 | s.removeBucket(bucketName, true, c)
178 | }
179 |
180 | func (s *OssutilCommandSuite) TestGetBucketResourceGroupConfirm(c *C) {
181 | bucketName := bucketNamePrefix + randLowStr(12)
182 | s.putBucket(bucketName, c)
183 |
184 | resourceFileName := inputFileName + randLowStr(5)
185 | // get resource group
186 | resourceDownName := resourceFileName + "-down"
187 | var str string
188 | strMethod := "get"
189 | options := OptionMapType{
190 | "endpoint": &str,
191 | "accessKeyID": &str,
192 | "accessKeySecret": &str,
193 | "configFile": &configFile,
194 | "method": &strMethod,
195 | }
196 |
197 | command := "resource-group"
198 | resourceArgs := []string{CloudURLToString(bucketName, ""), resourceDownName}
199 | _, err := cm.RunCommand(command, resourceArgs, options)
200 | c.Assert(err, IsNil)
201 | accessBody := s.readFile(resourceDownName, c)
202 |
203 | var out oss.GetBucketResourceGroupResult
204 | err = xml.Unmarshal([]byte(accessBody), &out)
205 | c.Assert(err, IsNil)
206 | id := out.ResourceGroupId
207 |
208 | os.Remove(resourceDownName)
209 |
210 | resourceXml := `
211 |
212 | ` + id + `
213 | `
214 |
215 | s.createFile(resourceFileName, resourceXml, c)
216 |
217 | // resource group command test
218 | options[OptionMethod] = &strMethod
219 |
220 | command = "resource-group"
221 | resourceArgs = []string{CloudURLToString(bucketName, ""), resourceFileName}
222 | _, err = cm.RunCommand(command, resourceArgs, options)
223 | c.Assert(err, IsNil)
224 |
225 | // get resource group
226 | strMethod = "get"
227 | options[OptionMethod] = &strMethod
228 |
229 | resourceArgs = []string{CloudURLToString(bucketName, ""), resourceDownName}
230 | _, err = cm.RunCommand(command, resourceArgs, options)
231 | c.Assert(err, IsNil)
232 |
233 | resourceArgs = []string{CloudURLToString(bucketName, ""), resourceDownName}
234 | _, err = cm.RunCommand(command, resourceArgs, options)
235 | c.Assert(err, IsNil)
236 |
237 | resourceArgs = []string{CloudURLToString(bucketName, "")}
238 | _, err = cm.RunCommand(command, resourceArgs, options)
239 | c.Assert(err, IsNil)
240 |
241 | os.Remove(resourceFileName)
242 | os.Remove(resourceDownName)
243 | s.removeBucket(bucketName, true, c)
244 | }
245 |
--------------------------------------------------------------------------------
/lib/bucket_tagging.go:
--------------------------------------------------------------------------------
1 | package lib
2 |
3 | import (
4 | "fmt"
5 | "strings"
6 |
7 | "github.com/aliyun/aliyun-oss-go-sdk/oss"
8 | )
9 |
10 | var specChineseBucketTag = SpecText{
11 | synopsisText: "设置、查询或者删除bucket的tag配置",
12 |
13 | paramText: "bucket_url [tag_parameter] [options]",
14 |
15 | syntaxText: `
16 | ossutil bucket-tagging --method put oss://bucket key#value
17 | ossutil bucket-tagging --method get oss://bucket
18 | ossutil bucket-tagging --method delete oss://bucket
19 | `,
20 | detailHelpText: `
21 | bucket-tagging命令通过设置method选项值为put、get、delete,可以设置、查询或者删除bucket的tag配置
22 | 每个tag的key和value必须以字符'#'分隔,最多可以连续输入10个tag信息
23 |
24 | 用法:
25 | 该命令有三种用法:
26 |
27 | 1) ossutil bucket-tagging --method put oss://bucket tagkey#tagvalue
28 | 这个命令设置bucket的tag配置,key和value分别为tagkey、tagvalue
29 |
30 | 2) ossutil bucket-tagging --method get oss://bucket
31 | 这个命令查询bucket的tag配置
32 |
33 | 3) ossutil bucket-tagging --method delete oss://bucket
34 | 这个命令删除bucket的tag配置
35 | `,
36 | sampleText: `
37 | 1) 设置bucket的tag配置
38 | ossutil bucket-tagging --method put oss://bucket tagkey#tagvalue
39 |
40 | 2) 设置bucket的多个tag配置
41 | ossutil bucket-tagging --method put oss://bucket tagkey1#tagvalue1 tagkey2#tagvalue2
42 |
43 | 3) 查询bucket的tag配置
44 | ossutil bucket-tagging --method get oss://bucket
45 |
46 | 4) 删除bucket的tag配置
47 | ossutil bucket-tagging --method delete oss://bucket
48 | `,
49 | }
50 |
51 | var specEnglishBucketTag = SpecText{
52 | synopsisText: "Set, get or delete bucket tag configuration",
53 |
54 | paramText: "bucket_url [tag_parameter] [options]",
55 |
56 | syntaxText: `
57 | ossutil bucket-tagging --method put oss://bucket key#value
58 | ossutil bucket-tagging --method get oss://bucket
59 | ossutil bucket-tagging --method delete oss://bucket
60 | `,
61 | detailHelpText: `
62 | bucket-tagging command can set, get and delete the tag configuration of the oss bucket by set method option value to put, get, delete
63 | the key and value of each tag must be separated by the character '#', you can enter up to 10 tag parameters.
64 | Usage:
65 | There are three usages for this command:
66 |
67 | 1) ossutil bucket-tagging --method put oss://bucket tagkey#tagvalue
68 | The command sets the tag configuration of the bucket. The key and value are tagkey and tagvalue
69 |
70 | 2) ossutil bucket-tagging --method get oss://bucket
71 | The command gets the tag configuration of bucket
72 |
73 | 3) ossutil bucket-tagging --method delete oss://bucket
74 | The command deletes the tag configuration of bucket
75 | `,
76 | sampleText: `
77 | 1) set bucket tag configuration with one tag
78 | ossutil bucket-tagging --method put oss://bucket tagkey#tagvalue
79 |
80 | 2) set bucket tag configuration with serveral tags
81 | ossutil bucket-tagging --method put oss://bucket tagkey1#tagvalue1 tagkey2#tagvalue2
82 |
83 | 3) get bucket tag configuration
84 | ossutil bucket-tagging --method get oss://bucket
85 |
86 | 4) delete bucket tag configuration
87 | ossutil bucket-tagging --method delete oss://bucket
88 | `,
89 | }
90 |
91 | type BucketTagCommand struct {
92 | command Command
93 | bucketName string
94 | tagResult oss.GetBucketTaggingResult
95 | }
96 |
97 | var bucketTagCommand = BucketTagCommand{
98 | command: Command{
99 | name: "bucket-tagging",
100 | nameAlias: []string{"bucket-tagging"},
101 | minArgc: 1,
102 | maxArgc: 11,
103 | specChinese: specChineseBucketTag,
104 | specEnglish: specEnglishBucketTag,
105 | group: GroupTypeNormalCommand,
106 | validOptionNames: []string{
107 | OptionConfigFile,
108 | OptionEndpoint,
109 | OptionAccessKeyID,
110 | OptionAccessKeySecret,
111 | OptionSTSToken,
112 | OptionProxyHost,
113 | OptionProxyUser,
114 | OptionProxyPwd,
115 | OptionMethod,
116 | OptionLogLevel,
117 | OptionPassword,
118 | OptionMode,
119 | OptionECSRoleName,
120 | OptionTokenTimeout,
121 | OptionRamRoleArn,
122 | OptionRoleSessionName,
123 | OptionReadTimeout,
124 | OptionConnectTimeout,
125 | OptionSTSRegion,
126 | OptionSkipVerifyCert,
127 | OptionUserAgent,
128 | OptionSignVersion,
129 | OptionRegion,
130 | OptionCloudBoxID,
131 | OptionForcePathStyle,
132 | },
133 | },
134 | }
135 |
136 | // function for FormatHelper interface
137 | func (btc *BucketTagCommand) formatHelpForWhole() string {
138 | return btc.command.formatHelpForWhole()
139 | }
140 |
141 | func (btc *BucketTagCommand) formatIndependHelp() string {
142 | return btc.command.formatIndependHelp()
143 | }
144 |
145 | // Init simulate inheritance, and polymorphism
146 | func (btc *BucketTagCommand) Init(args []string, options OptionMapType) error {
147 | return btc.command.Init(args, options, btc)
148 | }
149 |
150 | // RunCommand simulate inheritance, and polymorphism
151 | func (btc *BucketTagCommand) RunCommand() error {
152 | strMethod, _ := GetString(OptionMethod, btc.command.options)
153 | if strMethod == "" {
154 | return fmt.Errorf("--method value is empty")
155 | }
156 |
157 | strMethod = strings.ToLower(strMethod)
158 | if strMethod != "put" && strMethod != "get" && strMethod != "delete" {
159 | return fmt.Errorf("--method value is not in the optional value:put|get|delete")
160 | }
161 |
162 | srcBucketUrL, err := GetCloudUrl(btc.command.args[0], "")
163 | if err != nil {
164 | return err
165 | }
166 |
167 | btc.bucketName = srcBucketUrL.bucket
168 |
169 | if strMethod == "put" {
170 | err = btc.PutBucketTag()
171 | } else if strMethod == "get" {
172 | err = btc.GetBucketTag()
173 | } else if strMethod == "delete" {
174 | err = btc.DeleteBucketTag()
175 | }
176 | return err
177 | }
178 |
179 | func (btc *BucketTagCommand) PutBucketTag() error {
180 | if len(btc.command.args) < 2 {
181 | return fmt.Errorf("missing parameter,the tag value is empty")
182 | }
183 |
184 | var tagging oss.Tagging
185 | tagList := btc.command.args[1:len(btc.command.args)]
186 | for _, tag := range tagList {
187 | pSlice := strings.Split(tag, "#")
188 | if len(pSlice) != 2 {
189 | return fmt.Errorf("%s error,tag name and tag value must be separated by #", tag)
190 | }
191 | tagging.Tags = append(tagging.Tags, oss.Tag{Key: pSlice[0], Value: pSlice[1]})
192 | }
193 |
194 | // put bucket tag
195 | client, err := btc.command.ossClient(btc.bucketName)
196 | if err != nil {
197 | return err
198 | }
199 |
200 | return client.SetBucketTagging(btc.bucketName, tagging)
201 | }
202 |
203 | func (btc *BucketTagCommand) GetBucketTag() error {
204 | client, err := btc.command.ossClient(btc.bucketName)
205 | if err != nil {
206 | return err
207 | }
208 |
209 | btc.tagResult, err = client.GetBucketTagging(btc.bucketName)
210 | if err != nil {
211 | return err
212 | }
213 |
214 | if len(btc.tagResult.Tags) > 0 {
215 | fmt.Printf("%-10s%s\t%s\n", "index", "tag key", "tag value")
216 | fmt.Printf("---------------------------------------------------\n")
217 | }
218 |
219 | for index, tag := range btc.tagResult.Tags {
220 | fmt.Printf("%-10d\"%s\"\t\"%s\"\n", index, tag.Key, tag.Value)
221 | }
222 |
223 | fmt.Printf("\n\n")
224 |
225 | return nil
226 | }
227 |
228 | func (btc *BucketTagCommand) DeleteBucketTag() error {
229 | client, err := btc.command.ossClient(btc.bucketName)
230 | if err != nil {
231 | return err
232 | }
233 | return client.DeleteBucketTagging(btc.bucketName)
234 | }
235 |
--------------------------------------------------------------------------------
/lib/bucket_tagging_test.go:
--------------------------------------------------------------------------------
1 | package lib
2 |
3 | import (
4 | "os"
5 |
6 | . "gopkg.in/check.v1"
7 | )
8 |
9 | func (s *OssutilCommandSuite) TestBucketTaggingPutSuccess(c *C) {
10 | // put tagging
11 | bucketName := bucketNamePrefix + randLowStr(12)
12 | s.putBucket(bucketName, c)
13 |
14 | // tagging command test
15 | var str string
16 | strMethod := "put"
17 | options := OptionMapType{
18 | "endpoint": &str,
19 | "accessKeyID": &str,
20 | "accessKeySecret": &str,
21 | "stsToken": &str,
22 | "configFile": &configFile,
23 | "method": &strMethod,
24 | }
25 |
26 | tagInfo := "key1#value1"
27 | tagArgs := []string{CloudURLToString(bucketName, ""), tagInfo}
28 | _, err := cm.RunCommand("bucket-tagging", tagArgs, options)
29 | c.Assert(err, IsNil)
30 |
31 | // check,get tag
32 | strMethod = "get"
33 | tagArgs = []string{CloudURLToString(bucketName, "")}
34 | _, err = cm.RunCommand("bucket-tagging", tagArgs, options)
35 | c.Assert(err, IsNil)
36 | c.Assert(len(bucketTagCommand.tagResult.Tags), Equals, 1)
37 | c.Assert(bucketTagCommand.tagResult.Tags[0].Key, Equals, "key1")
38 | c.Assert(bucketTagCommand.tagResult.Tags[0].Value, Equals, "value1")
39 |
40 | s.removeBucket(bucketName, true, c)
41 | }
42 |
43 | func (s *OssutilCommandSuite) TestBucketTaggingDeleteSuccess(c *C) {
44 | // put tagging
45 | bucketName := bucketNamePrefix + randLowStr(12)
46 | s.putBucket(bucketName, c)
47 |
48 | // tagging command test
49 | var str string
50 | strMethod := "put"
51 | options := OptionMapType{
52 | "endpoint": &str,
53 | "accessKeyID": &str,
54 | "accessKeySecret": &str,
55 | "stsToken": &str,
56 | "configFile": &configFile,
57 | "method": &strMethod,
58 | }
59 |
60 | tagInfo := "key1#value1"
61 | tagArgs := []string{CloudURLToString(bucketName, ""), tagInfo}
62 | _, err := cm.RunCommand("bucket-tagging", tagArgs, options)
63 | c.Assert(err, IsNil)
64 |
65 | // check,get tag
66 | strMethod = "get"
67 | tagArgs = []string{CloudURLToString(bucketName, "")}
68 | _, err = cm.RunCommand("bucket-tagging", tagArgs, options)
69 | c.Assert(err, IsNil)
70 | c.Assert(len(bucketTagCommand.tagResult.Tags), Equals, 1)
71 | c.Assert(bucketTagCommand.tagResult.Tags[0].Key, Equals, "key1")
72 | c.Assert(bucketTagCommand.tagResult.Tags[0].Value, Equals, "value1")
73 |
74 | // delete bucket tagging
75 | strMethod = "delete"
76 | _, err = cm.RunCommand("bucket-tagging", tagArgs, options)
77 | c.Assert(err, IsNil)
78 |
79 | // get bucket tagging again:error
80 | strMethod = "get"
81 | _, err = cm.RunCommand("bucket-tagging", tagArgs, options)
82 | c.Assert(err, IsNil)
83 | c.Assert(len(bucketTagCommand.tagResult.Tags), Equals, 0)
84 |
85 | s.removeBucket(bucketName, true, c)
86 | }
87 |
88 | func (s *OssutilCommandSuite) TestBucketTaggingError(c *C) {
89 | bucketName := bucketNamePrefix + randLowStr(12)
90 | s.putBucket(bucketName, c)
91 |
92 | // bucket-tagging command test
93 | var str string
94 | options := OptionMapType{
95 | "endpoint": &str,
96 | "accessKeyID": &str,
97 | "accessKeySecret": &str,
98 | "stsToken": &str,
99 | "configFile": &configFile,
100 | }
101 |
102 | // method is empty
103 | tagInfo := "key1#value1"
104 | tagArgs := []string{CloudURLToString(bucketName, ""), tagInfo}
105 | _, err := cm.RunCommand("bucket-tagging", tagArgs, options)
106 | c.Assert(err, NotNil)
107 |
108 | // method is error
109 | strMethod := "puttt"
110 | options["method"] = &strMethod
111 | _, err = cm.RunCommand("bucket-tagging", tagArgs, options)
112 | c.Assert(err, NotNil)
113 |
114 | // args is empty
115 | strMethod = "put"
116 | tagArgs = []string{CloudURLToString(bucketName, "")}
117 | _, err = cm.RunCommand("bucket-tagging", tagArgs, options)
118 | c.Assert(err, NotNil)
119 |
120 | //value is error
121 | tagInfo = "key1:value1"
122 | tagArgs = []string{CloudURLToString(bucketName, ""), tagInfo}
123 | _, err = cm.RunCommand("bucket-tagging", tagArgs, options)
124 | c.Assert(err, NotNil)
125 |
126 | s.removeBucket(bucketName, true, c)
127 |
128 | }
129 |
130 | func (s *OssutilCommandSuite) TestBucketTaggingPutEmptyEndpoint(c *C) {
131 | bucketName := bucketNamePrefix + randLowStr(12)
132 | s.putBucket(bucketName, c)
133 |
134 | cfile := randStr(10)
135 | data := "[Credentials]" + "\n" + "language=CH" + "\n" + "accessKeyID=123" + "\n" + "accessKeySecret=456" + "\n" + "endpoint="
136 | s.createFile(cfile, data, c)
137 |
138 | // bucket-tagging command test
139 | var str string
140 | strMethod := "put"
141 | options := OptionMapType{
142 | "endpoint": &str,
143 | "accessKeyID": &str,
144 | "accessKeySecret": &str,
145 | "stsToken": &str,
146 | "configFile": &cfile,
147 | "method": &strMethod,
148 | }
149 |
150 | tagInfo := "key1#value1"
151 | tagArgs := []string{CloudURLToString(bucketName, ""), tagInfo}
152 | _, err := cm.RunCommand("bucket-tagging", tagArgs, options)
153 | c.Assert(err, NotNil)
154 |
155 | os.Remove(cfile)
156 | s.removeBucket(bucketName, true, c)
157 | }
158 |
159 | func (s *OssutilCommandSuite) TestBucketTaggingGetEmptyEndpoint(c *C) {
160 | bucketName := bucketNamePrefix + randLowStr(12)
161 | s.putBucket(bucketName, c)
162 |
163 | cfile := randStr(10)
164 | data := "[Credentials]" + "\n" + "language=CH" + "\n" + "accessKeyID=123" + "\n" + "accessKeySecret=456" + "\n" + "endpoint="
165 | s.createFile(cfile, data, c)
166 |
167 | var str string
168 | strMethod := "get"
169 | options := OptionMapType{
170 | "endpoint": &str,
171 | "accessKeyID": &str,
172 | "accessKeySecret": &str,
173 | "stsToken": &str,
174 | "configFile": &cfile,
175 | "method": &strMethod,
176 | }
177 |
178 | tagArgs := []string{CloudURLToString(bucketName, "")}
179 | _, err := cm.RunCommand("bucket-tagging", tagArgs, options)
180 | c.Assert(err, NotNil)
181 |
182 | os.Remove(cfile)
183 | s.removeBucket(bucketName, true, c)
184 | }
185 |
186 | func (s *OssutilCommandSuite) TestBucketTaggingHelpInfo(c *C) {
187 | options := OptionMapType{}
188 |
189 | mkArgs := []string{"bucket-tagging"}
190 | _, err := cm.RunCommand("help", mkArgs, options)
191 | c.Assert(err, IsNil)
192 |
193 | mkArgs = []string{}
194 | _, err = cm.RunCommand("help", mkArgs, options)
195 | c.Assert(err, IsNil)
196 |
197 | }
198 |
--------------------------------------------------------------------------------
/lib/bucket_versioning.go:
--------------------------------------------------------------------------------
1 | package lib
2 |
3 | import (
4 | "fmt"
5 | "strings"
6 |
7 | "github.com/aliyun/aliyun-oss-go-sdk/oss"
8 | )
9 |
10 | var specChineseBucketVersioning = SpecText{
11 | synopsisText: "设置、查询bucket的versioning配置",
12 |
13 | paramText: "bucket_url [versioning_parameter] [options]",
14 |
15 | syntaxText: `
16 | ossutil bucket-versioning --method put oss://bucket versioning_parameter
17 | ossutil bucket-versioning --method get oss://bucket
18 | `,
19 | detailHelpText: `
20 | bucket-versioning命令通过设置method选项值为put、get、可以设置、查询bucket的versioning配置
21 | 选项--method为put时,versioning状态参数只能为enabled、suspended
22 |
23 | 用法:
24 | 该命令有三种用法:
25 |
26 | 1) ossutil bucket-versioning --method put oss://bucket enabled
27 | 这个命令开通bucket的versioning功能
28 |
29 | 2) ossutil bucket-versioning --method put oss://bucket suspended
30 | 这个命令关闭bucket的versioning功能
31 |
32 | 3) ossutil bucket-versioning --method get oss://bucket
33 | 这个命令查询bucket的vesioning状态
34 | `,
35 | sampleText: `
36 | 1) 开通bucket的versioning功能
37 | ossutil bucket-versioning --method put oss://bucket enabled
38 |
39 | 2) 关闭bucket的versioning功能
40 | ossutil bucket-versioning --method put oss://bucket suspended
41 |
42 | 3) 查询bucket的versioning状态
43 | ossutil bucket-versioning --method get oss://bucket
44 | `,
45 | }
46 |
47 | var specEnglishBucketVersioning = SpecText{
48 | synopsisText: "Set, get bucket versioning configuration",
49 |
50 | paramText: "bucket_url [versioning_parameter] [options]",
51 |
52 | syntaxText: `
53 | ossutil bucket-versioning --method put oss://bucket versioning_parameter
54 | ossutil bucket-versioning --method get oss://bucket
55 | `,
56 | detailHelpText: `
57 | bucket-versioning command can set, get the versioning configuration of the oss bucket by set method option value to put, get
58 | If the --method option value is put,the versioning status value can only be enabled, suspended,
59 | Usage:
60 | There are three usages for this command:
61 |
62 | 1) ossutil bucket-versioning --method put oss://bucket enabled
63 | This command enables the bucket versioning
64 |
65 | 2) ossutil bucket-versioning --method put oss://bucket suspended
66 | This command disables the bucket versioning
67 |
68 | 3) ossutil bucket-versioning --method get oss://bucket
69 | This command query the bucket versioning status
70 | `,
71 | sampleText: `
72 | 1) set bucket versioning enabled
73 | ossutil bucket-versioning --method put oss://bucket enabled
74 |
75 | 2) set bucket versioning disable
76 | ossutil bucket-versioning --method put oss://bucket suspended
77 |
78 | 3) get bucket versioning status
79 | ossutil bucket-versioning --method get oss://bucket
80 | `,
81 | }
82 |
83 | type BucketVersioningCommand struct {
84 | command Command
85 | bucketName string
86 | versioningResult oss.GetBucketVersioningResult
87 | }
88 |
89 | var bucketVersioningCommand = BucketVersioningCommand{
90 | command: Command{
91 | name: "bucket-versioning",
92 | nameAlias: []string{"bucket-versioning"},
93 | minArgc: 1,
94 | maxArgc: 2,
95 | specChinese: specChineseBucketVersioning,
96 | specEnglish: specEnglishBucketVersioning,
97 | group: GroupTypeNormalCommand,
98 | validOptionNames: []string{
99 | OptionConfigFile,
100 | OptionEndpoint,
101 | OptionAccessKeyID,
102 | OptionAccessKeySecret,
103 | OptionSTSToken,
104 | OptionProxyHost,
105 | OptionProxyUser,
106 | OptionProxyPwd,
107 | OptionMethod,
108 | OptionLogLevel,
109 | OptionPassword,
110 | OptionMode,
111 | OptionECSRoleName,
112 | OptionTokenTimeout,
113 | OptionRamRoleArn,
114 | OptionRoleSessionName,
115 | OptionReadTimeout,
116 | OptionConnectTimeout,
117 | OptionSTSRegion,
118 | OptionSkipVerifyCert,
119 | OptionUserAgent,
120 | OptionSignVersion,
121 | OptionRegion,
122 | OptionCloudBoxID,
123 | OptionForcePathStyle,
124 | },
125 | },
126 | }
127 |
128 | // function for FormatHelper interface
129 | func (bvc *BucketVersioningCommand) formatHelpForWhole() string {
130 | return bvc.command.formatHelpForWhole()
131 | }
132 |
133 | func (bvc *BucketVersioningCommand) formatIndependHelp() string {
134 | return bvc.command.formatIndependHelp()
135 | }
136 |
137 | // Init simulate inheritance, and polymorphism
138 | func (bvc *BucketVersioningCommand) Init(args []string, options OptionMapType) error {
139 | return bvc.command.Init(args, options, bvc)
140 | }
141 |
142 | // RunCommand simulate inheritance, and polymorphism
143 | func (bvc *BucketVersioningCommand) RunCommand() error {
144 | strMethod, _ := GetString(OptionMethod, bvc.command.options)
145 | if strMethod == "" {
146 | return fmt.Errorf("--method value is empty")
147 | }
148 |
149 | strMethod = strings.ToLower(strMethod)
150 | if strMethod != "put" && strMethod != "get" {
151 | return fmt.Errorf("--method value is not in the optional value:put|get")
152 | }
153 |
154 | srcBucketUrL, err := GetCloudUrl(bvc.command.args[0], "")
155 | if err != nil {
156 | return err
157 | }
158 |
159 | bvc.bucketName = srcBucketUrL.bucket
160 |
161 | if strMethod == "put" {
162 | err = bvc.PutBucketVersioning()
163 | } else if strMethod == "get" {
164 | err = bvc.GetBucketVersioning()
165 | }
166 |
167 | return err
168 | }
169 |
170 | func (bvc *BucketVersioningCommand) PutBucketVersioning() error {
171 |
172 | if len(bvc.command.args) < 2 {
173 | return fmt.Errorf("missing parameter,versioning status is empty")
174 | }
175 |
176 | strVersion := bvc.command.args[1]
177 |
178 | if strings.ToUpper(strVersion) != strings.ToUpper(string(oss.VersionEnabled)) &&
179 | strings.ToUpper(strVersion) != strings.ToUpper(string(oss.VersionSuspended)) {
180 | return fmt.Errorf("version status must be %s or %s", string(oss.VersionEnabled),
181 | string(oss.VersionSuspended))
182 | }
183 |
184 | // put bucket versioning
185 | client, err := bvc.command.ossClient(bvc.bucketName)
186 | if err != nil {
187 | return err
188 | }
189 |
190 | var versioningConfig oss.VersioningConfig
191 | if strings.ToUpper(strVersion) == strings.ToUpper(string(oss.VersionEnabled)) {
192 | versioningConfig.Status = string(oss.VersionEnabled)
193 | } else if strings.ToUpper(strVersion) == strings.ToUpper(string(oss.VersionSuspended)) {
194 | versioningConfig.Status = string(oss.VersionSuspended)
195 |
196 | }
197 | return client.SetBucketVersioning(bvc.bucketName, versioningConfig)
198 | }
199 |
200 | func (bvc *BucketVersioningCommand) GetBucketVersioning() error {
201 | client, err := bvc.command.ossClient(bvc.bucketName)
202 | if err != nil {
203 | return err
204 | }
205 |
206 | bvc.versioningResult, err = client.GetBucketVersioning(bvc.bucketName)
207 | if err != nil {
208 | return err
209 | }
210 |
211 | if bvc.versioningResult.Status == "" {
212 | bvc.versioningResult.Status = "null"
213 | }
214 |
215 | fmt.Printf("\nbucket versioning status:%s\n", bvc.versioningResult.Status)
216 |
217 | return nil
218 | }
219 |
--------------------------------------------------------------------------------
/lib/bucket_versioning_test.go:
--------------------------------------------------------------------------------
1 | package lib
2 |
3 | import (
4 | "os"
5 | "time"
6 |
7 | . "gopkg.in/check.v1"
8 | )
9 |
10 | func (s *OssutilCommandSuite) TestBucketVersioningPutSuccess(c *C) {
11 | bucketName := bucketNamePrefix + randLowStr(12)
12 | s.putBucket(bucketName, c)
13 |
14 | // versioning command test
15 | var str string
16 | strMethod := "get"
17 | options := OptionMapType{
18 | "endpoint": &str,
19 | "accessKeyID": &str,
20 | "accessKeySecret": &str,
21 | "stsToken": &str,
22 | "configFile": &configFile,
23 | "method": &strMethod,
24 | }
25 |
26 | versioningArgs := []string{CloudURLToString(bucketName, "")}
27 | _, err := cm.RunCommand("bucket-versioning", versioningArgs, options)
28 | c.Assert(err, IsNil)
29 | c.Assert(bucketVersioningCommand.versioningResult.Status, Equals, "null")
30 |
31 | // set bucket versioning enabled
32 | strMethod = "put"
33 | versioningArgs = []string{CloudURLToString(bucketName, ""), "enabled"}
34 | _, err = cm.RunCommand("bucket-versioning", versioningArgs, options)
35 | c.Assert(err, IsNil)
36 | time.Sleep(time.Second * 3)
37 |
38 | // check
39 | strMethod = "get"
40 | versioningArgs = []string{CloudURLToString(bucketName, "")}
41 | _, err = cm.RunCommand("bucket-versioning", versioningArgs, options)
42 | c.Assert(err, IsNil)
43 | c.Assert(bucketVersioningCommand.versioningResult.Status, Equals, "Enabled")
44 | time.Sleep(time.Second * 3)
45 |
46 | // set bucket versioning suspend
47 | strMethod = "put"
48 | versioningArgs = []string{CloudURLToString(bucketName, ""), "suspended"}
49 | _, err = cm.RunCommand("bucket-versioning", versioningArgs, options)
50 | c.Assert(err, IsNil)
51 | time.Sleep(time.Second * 3)
52 |
53 | // check
54 | strMethod = "get"
55 | versioningArgs = []string{CloudURLToString(bucketName, "")}
56 | _, err = cm.RunCommand("bucket-versioning", versioningArgs, options)
57 | c.Assert(err, IsNil)
58 | c.Assert(bucketVersioningCommand.versioningResult.Status, Equals, "Suspended")
59 |
60 | s.removeBucket(bucketName, true, c)
61 | }
62 |
63 | func (s *OssutilCommandSuite) TestBucketVersioningError(c *C) {
64 | bucketName := bucketNamePrefix + randLowStr(12)
65 | s.putBucket(bucketName, c)
66 |
67 | // bucket-versioning command test
68 | var str string
69 | options := OptionMapType{
70 | "endpoint": &str,
71 | "accessKeyID": &str,
72 | "accessKeySecret": &str,
73 | "stsToken": &str,
74 | "configFile": &configFile,
75 | }
76 |
77 | // method is empty
78 | versioningArgs := []string{CloudURLToString(bucketName, "")}
79 | _, err := cm.RunCommand("bucket-versioning", versioningArgs, options)
80 | c.Assert(err, NotNil)
81 |
82 | // method is error
83 | strMethod := "puttt"
84 | options["method"] = &strMethod
85 | _, err = cm.RunCommand("bucket-versioning", versioningArgs, options)
86 | c.Assert(err, NotNil)
87 |
88 | // args is empty
89 | strMethod = "put"
90 | versioningArgs = []string{CloudURLToString(bucketName, "")}
91 | _, err = cm.RunCommand("bucket-versioning", versioningArgs, options)
92 | c.Assert(err, NotNil)
93 |
94 | //value is error
95 | versioningArgs = []string{CloudURLToString(bucketName, ""), "disabled"}
96 | _, err = cm.RunCommand("bucket-versioning", versioningArgs, options)
97 | c.Assert(err, NotNil)
98 |
99 | s.removeBucket(bucketName, true, c)
100 | }
101 |
102 | func (s *OssutilCommandSuite) TestBucketVersioningPutEmptyEndpoint(c *C) {
103 | bucketName := bucketNamePrefix + randLowStr(12)
104 | s.putBucket(bucketName, c)
105 |
106 | cfile := randStr(10)
107 | data := "[Credentials]" + "\n" + "language=CH" + "\n" + "accessKeyID=123" + "\n" + "accessKeySecret=456" + "\n" + "endpoint="
108 | s.createFile(cfile, data, c)
109 |
110 | // bucket-versioing command test
111 | var str string
112 | strMethod := "put"
113 | options := OptionMapType{
114 | "endpoint": &str,
115 | "accessKeyID": &str,
116 | "accessKeySecret": &str,
117 | "stsToken": &str,
118 | "configFile": &cfile,
119 | "method": &strMethod,
120 | }
121 |
122 | versioningArgs := []string{CloudURLToString(bucketName, ""), "enabled"}
123 | _, err := cm.RunCommand("bucket-versioning", versioningArgs, options)
124 | c.Assert(err, NotNil)
125 |
126 | os.Remove(cfile)
127 | s.removeBucket(bucketName, true, c)
128 | }
129 |
130 | func (s *OssutilCommandSuite) TestBucketVersioningGetEmptyEndpoint(c *C) {
131 | bucketName := bucketNamePrefix + randLowStr(12)
132 | s.putBucket(bucketName, c)
133 |
134 | cfile := randStr(10)
135 | data := "[Credentials]" + "\n" + "language=CH" + "\n" + "accessKeyID=123" + "\n" + "accessKeySecret=456" + "\n" + "endpoint="
136 | s.createFile(cfile, data, c)
137 |
138 | var str string
139 | strMethod := "get"
140 | options := OptionMapType{
141 | "endpoint": &str,
142 | "accessKeyID": &str,
143 | "accessKeySecret": &str,
144 | "stsToken": &str,
145 | "configFile": &configFile,
146 | "method": &strMethod,
147 | }
148 |
149 | versioingArgs := []string{CloudURLToString(bucketName, "")}
150 | _, err := cm.RunCommand("bucket-versioing", versioingArgs, options)
151 | c.Assert(err, NotNil)
152 |
153 | os.Remove(cfile)
154 | s.removeBucket(bucketName, true, c)
155 | }
156 |
157 | func (s *OssutilCommandSuite) TestBucketVersioningHelpInfo(c *C) {
158 | options := OptionMapType{}
159 |
160 | mkArgs := []string{"bucket-versioning"}
161 | _, err := cm.RunCommand("help", mkArgs, options)
162 | c.Assert(err, IsNil)
163 |
164 | mkArgs = []string{}
165 | _, err = cm.RunCommand("help", mkArgs, options)
166 | c.Assert(err, IsNil)
167 |
168 | }
169 |
--------------------------------------------------------------------------------
/lib/cat.go:
--------------------------------------------------------------------------------
1 | package lib
2 |
3 | import (
4 | "fmt"
5 | "io"
6 | "os"
7 | "strings"
8 |
9 | oss "github.com/aliyun/aliyun-oss-go-sdk/oss"
10 | )
11 |
12 | var specChineseCat = SpecText{
13 | synopsisText: "将文件内容输出到标准输出",
14 |
15 | paramText: "object [options]",
16 |
17 | syntaxText: `
18 | ossutil cat oss://bucket/object [--payer requester] [--version-id versionId]
19 | `,
20 | detailHelpText: `
21 | cat命令可以将oss的object内容输出到标准输出,object内容最好是文本格式
22 |
23 | 用法:
24 | 该命令仅有一种用法:
25 |
26 | 1) ossutil cat oss://bucket/object [--version-id versionId] [--payer requester]
27 | 将object内容输出到标准输出
28 | `,
29 | sampleText: `
30 | 1) 将object内容输出到标准输出
31 | ossutil cat oss://bucket/object
32 |
33 | 2) 将object指定版本内容输出到标准输出
34 | ossutil cat oss://bucket/object --version-id versionId
35 |
36 | 3) 访问者付费模式
37 | ossutil cat oss://bucket/object --payer requester
38 | `,
39 | }
40 |
41 | var specEnglishCat = SpecText{
42 | synopsisText: "Output object content to standard output",
43 |
44 | paramText: "object [options]",
45 |
46 | syntaxText: `
47 | ossutil cat oss://bucket/object [--payer requester] [--version-id versionId]
48 | `,
49 | detailHelpText: `
50 | The cat command can output the object content of oss to standard output
51 | The object content is preferably text format
52 |
53 | Usage:
54 | There is only one usage for this command:
55 |
56 | 1) ossutil cat oss://bucket/object [--version-id versionId] [--payer requester]
57 | The command output object content to standard output
58 | `,
59 | sampleText: `
60 | 1) output object content to standard output
61 | ossutil cat oss://bucket/object
62 |
63 | 2) output the object's specified version content to standard output
64 | ossutil cat oss://bucket/object --version-id versionId
65 |
66 | 3) output object content with requester payment
67 | ossutil cat oss://bucket/object --payer requester
68 | `,
69 | }
70 |
71 | type catOptionType struct {
72 | bucketName string
73 | objectName string
74 | encodingType string
75 | }
76 |
77 | type CatCommand struct {
78 | command Command
79 | catOption catOptionType
80 | commonOptions []oss.Option
81 | }
82 |
83 | var catCommand = CatCommand{
84 | command: Command{
85 | name: "cat",
86 | nameAlias: []string{"cat"},
87 | minArgc: 1,
88 | maxArgc: 1,
89 | specChinese: specChineseCat,
90 | specEnglish: specEnglishCat,
91 | group: GroupTypeNormalCommand,
92 | validOptionNames: []string{
93 | OptionConfigFile,
94 | OptionEndpoint,
95 | OptionAccessKeyID,
96 | OptionAccessKeySecret,
97 | OptionSTSToken,
98 | OptionProxyHost,
99 | OptionProxyUser,
100 | OptionProxyPwd,
101 | OptionEncodingType,
102 | OptionLogLevel,
103 | OptionVersionId,
104 | OptionRequestPayer,
105 | OptionPassword,
106 | OptionMode,
107 | OptionECSRoleName,
108 | OptionTokenTimeout,
109 | OptionRamRoleArn,
110 | OptionRoleSessionName,
111 | OptionReadTimeout,
112 | OptionConnectTimeout,
113 | OptionSTSRegion,
114 | OptionSkipVerifyCert,
115 | OptionUserAgent,
116 | OptionSignVersion,
117 | OptionRegion,
118 | OptionCloudBoxID,
119 | OptionForcePathStyle,
120 | },
121 | },
122 | }
123 |
124 | // function for FormatHelper interface
125 | func (catc *CatCommand) formatHelpForWhole() string {
126 | return catc.command.formatHelpForWhole()
127 | }
128 |
129 | func (catc *CatCommand) formatIndependHelp() string {
130 | return catc.command.formatIndependHelp()
131 | }
132 |
133 | // Init simulate inheritance, and polymorphism
134 | func (catc *CatCommand) Init(args []string, options OptionMapType) error {
135 | return catc.command.Init(args, options, catc)
136 | }
137 |
138 | // RunCommand simulate inheritance, and polymorphism
139 | func (catc *CatCommand) RunCommand() error {
140 | catc.catOption.encodingType, _ = GetString(OptionEncodingType, catc.command.options)
141 | srcBucketUrL, err := GetCloudUrl(catc.command.args[0], catc.catOption.encodingType)
142 | if err != nil {
143 | return err
144 | }
145 |
146 | if srcBucketUrL.object == "" {
147 | return fmt.Errorf("object key is empty")
148 | }
149 |
150 | catc.catOption.bucketName = srcBucketUrL.bucket
151 | catc.catOption.objectName = srcBucketUrL.object
152 |
153 | // check object exist or not
154 | client, err := catc.command.ossClient(catc.catOption.bucketName)
155 | if err != nil {
156 | return err
157 | }
158 |
159 | bucket, err := client.Bucket(catc.catOption.bucketName)
160 | if err != nil {
161 | return err
162 | }
163 |
164 | payer, _ := GetString(OptionRequestPayer, catc.command.options)
165 | if payer != "" {
166 | if payer != strings.ToLower(string(oss.Requester)) {
167 | return fmt.Errorf("invalid request payer: %s, please check", payer)
168 | }
169 | catc.commonOptions = append(catc.commonOptions, oss.RequestPayer(oss.PayerType(payer)))
170 | }
171 |
172 | var options []oss.Option
173 | options = append(options, catc.commonOptions...)
174 |
175 | versionId, _ := GetString(OptionVersionId, catc.command.options)
176 | if len(versionId) > 0 {
177 | options = append(options, oss.VersionId(versionId))
178 | }
179 |
180 | body, err := bucket.GetObject(catc.catOption.objectName, options...)
181 | if err != nil {
182 | return err
183 | }
184 |
185 | defer body.Close()
186 | io.Copy(os.Stdout, body)
187 | fmt.Printf("\n")
188 |
189 | return err
190 | }
191 |
--------------------------------------------------------------------------------
/lib/command_manager.go:
--------------------------------------------------------------------------------
1 | package lib
2 |
3 | import (
4 | "fmt"
5 | "os"
6 | "reflect"
7 | "runtime"
8 | "strings"
9 | "time"
10 |
11 | oss "github.com/aliyun/aliyun-oss-go-sdk/oss"
12 | )
13 |
14 | var commandLine string
15 |
16 | func LogEnd(startT time.Time) {
17 | LogInfo("ossutil run end,cost:%d(ms).\n", time.Now().UnixNano()/1000/1000-startT.UnixNano()/1000/1000)
18 | UnInitLogger()
19 | }
20 |
21 | // ParseAndRunCommand parse command line user input, get command and options, then run command
22 | func ParseAndRunCommand() error {
23 | ts := time.Now().UnixNano()
24 |
25 | commandLine = getCommandLine()
26 |
27 | clearEnv()
28 |
29 | args, options, err := ParseArgOptions()
30 | if err != nil {
31 | return err
32 | }
33 |
34 | var level = oss.LogOff
35 | strLevel, err := getLoglevelFromOptions(options)
36 | if strLevel == "info" {
37 | level = oss.Info
38 | } else if strLevel == "debug" {
39 | level = oss.Debug
40 | } else if len(strLevel) > 0 {
41 | return fmt.Errorf("loglevel must be:info|debug")
42 | }
43 |
44 | if level > oss.LogOff {
45 | InitLogger(level, logName)
46 | }
47 |
48 | startT := time.Now()
49 | LogInfo("ossutil run begin,cmd:%s\n", commandLine)
50 | LogInfo("ossutil version is %s\n", Version)
51 | LogInfo("oss go sdk version is %s\n", oss.Version)
52 | LogInfo("go version is %s\n", runtime.Version())
53 | LogInfo("runtime.NumCPU is %d\n", runtime.NumCPU())
54 |
55 | defer LogEnd(startT)
56 |
57 | showElapse, err := RunCommand(args, options)
58 | if err != nil {
59 | LogError("%s.\n", err.Error())
60 | return err
61 | }
62 | if showElapse {
63 | te := time.Now().UnixNano()
64 | fmt.Printf("\n%.6f(s) elapsed\n", float64(te-ts)/1e9)
65 | return nil
66 | }
67 | return nil
68 | }
69 |
70 | func getCommandLine() string {
71 | return strings.Join(os.Args, " ")
72 | }
73 |
74 | func clearEnv() {
75 | if runtime.GOOS == "windows" {
76 | _, renameFilePath := getBinaryPath()
77 | os.Remove(renameFilePath)
78 | }
79 | }
80 |
81 | func RunCommand(args []string, options OptionMapType) (bool, error) {
82 | if len(args) == 0 {
83 | if val, _ := GetBool(OptionVersion, options); val {
84 | fmt.Printf("ossutil version: %s\n", Version)
85 | return false, nil
86 | }
87 | args = append(args, "help")
88 | }
89 | command := args[0]
90 | args = args[1:]
91 |
92 | cm := CommandManager{}
93 | cm.Init()
94 | showElapse, err := cm.RunCommand(command, args, options)
95 | return showElapse, err
96 | }
97 |
98 | // CommandManager is used to manager commands, such as build command map and run command
99 | type CommandManager struct {
100 | commandMap map[string]interface{}
101 | }
102 |
103 | // Init build command map
104 | func (cm *CommandManager) Init() {
105 | commandList := GetAllCommands()
106 | cm.commandMap = make(map[string]interface{}, len(commandList))
107 |
108 | for _, cmd := range commandList {
109 | name := reflect.ValueOf(cmd).Elem().FieldByName("command").FieldByName("name").String()
110 | cm.commandMap[name] = cmd
111 | }
112 | }
113 |
114 | // RunCommand select command from command map, initialize command and run command
115 | func (cm *CommandManager) RunCommand(commandName string, args []string, options OptionMapType) (bool, error) {
116 |
117 | if cmd, ok := cm.commandMap[commandName]; ok {
118 | if err := cmd.(Commander).Init(args, options); err != nil {
119 | return false, err
120 | }
121 | if err := cmd.(Commander).RunCommand(); err != nil {
122 | return false, err
123 | }
124 | group := reflect.ValueOf(cmd).Elem().FieldByName("command").FieldByName("group").String()
125 | return group == GroupTypeNormalCommand, nil
126 | }
127 | return false, fmt.Errorf("no such command: \"%s\", please try \"help\" for more information", commandName)
128 | }
129 |
130 | func getLoglevelFromOptions(options OptionMapType) (string, error) {
131 | strLevel, err := GetString(OptionLogLevel, options)
132 | if err != nil {
133 | return "", err
134 | }
135 | if strLevel != "" {
136 | return strLevel, nil
137 | }
138 |
139 | configFile, _ := GetString(OptionConfigFile, options)
140 |
141 | strLevel, err = readLoglevelFromFile(configFile)
142 | if err != nil {
143 | return "", err
144 | }
145 |
146 | return strLevel, nil
147 | }
148 |
--------------------------------------------------------------------------------
/lib/cors_options.go:
--------------------------------------------------------------------------------
1 | package lib
2 |
3 | import (
4 | "os"
5 | "strings"
6 |
7 | oss "github.com/aliyun/aliyun-oss-go-sdk/oss"
8 | )
9 |
10 | var specChineseOptions = SpecText{
11 | synopsisText: "向oss发送http options请求,用于CORS检测",
12 |
13 | paramText: "oss_url [options]",
14 |
15 | syntaxText: `
16 | ossutil cors-options --acr-method --origin --acr-headers oss://bucket/[object] [options]
17 | `,
18 | detailHelpText: `
19 | cors-options命令向oss发送http options请求
20 | --acr-method、--origin、--acr-headers分别对应http header:Access-Control-Request-Method、Origin、Access-Control-Request-Headers
21 | --acr-headers如果有多个取值,各个header用逗号分隔,再加上双引号,比如 --acr-headers "header1,header2,header3"
22 |
23 | 用法:
24 | 该命令有一种用法:
25 |
26 | 1) ossutil cors-options --acr-method PUT --origin "www.aliyuncs.com" --acr-header x-oss-meta-author oss://bucket/ [options]
27 | 向oss发送options请求,Origin、Access-Control-Request-Method、Access-Control-Request-Headers分别为www.aliyuncs.com、PUT、x-oss-meta-author
28 | `,
29 | sampleText: `
30 | 1) 发送options请求,Access-Control-Request-Method为PUT
31 | ossutil cors-options --acr-method PUT --origin "www.aliyuncs.com" --acr-header x-oss-meta-author oss://bucket/
32 |
33 | 2) 发送options请求,有多个header参数,Access-Control-Request-Method为GET
34 | ossutil cors-options --acr-method GET --origin "www.aliyuncs.com" --acr-header "x-oss-meta-author1,x-oss-meta-author2" oss://bucket/
35 | `,
36 | }
37 |
38 | var specEnglishOptions = SpecText{
39 | synopsisText: "Send http options request to oss for CORS detection",
40 |
41 | paramText: "oss_url [options]",
42 |
43 | syntaxText: `
44 | ossutil cors-options --acr-method --origin --acr-headers oss://bucket/[object] [options]
45 | `,
46 | detailHelpText: `
47 | The cors-options command sends an http options request to oss
48 | --acr-method, --origin, --acr-headers correspond to http header:Access-Control-Request-Method, Origin, Access-Control-Request-Headers
49 | If --acr-headers have multiple values, each header is separated by a comma, followed by double quotes, for example: --acr-headers "header1,header2,header3"
50 |
51 | Usage:
52 | There are one usage for this command:
53 |
54 | 1) ossutil cors-options --acr-method PUT --origin "www.aliyuncs.com" --acr-header x-oss-meta-author oss://bucket/ [options]
55 | sends an http options request to oss,Origin、Access-Control-Request-Method、Access-Control-Request-Headers values are www.aliyuncs.com、PUT、x-oss-meta-author
56 | `,
57 | sampleText: `
58 | 1) sends an http options request,Access-Control-Request-Method value is PUT
59 | ossutil cors-options --acr-method PUT --origin "www.aliyuncs.com" --acr-header x-oss-meta-author oss://bucket/
60 |
61 | 2) sends an http options request,there are multipule values for --acr-header,Access-Control-Request-Method value is GET
62 | ossutil cors-options --acr-method GET --origin "www.aliyuncs.com" --acr-header "x-oss-meta-author1,x-oss-meta-author2" oss://bucket/
63 | `,
64 | }
65 |
66 | type OptionsCommand struct {
67 | command Command
68 | }
69 |
70 | var corsOptionsCommand = OptionsCommand{
71 | command: Command{
72 | name: "cors-options",
73 | nameAlias: []string{"cors-options"},
74 | minArgc: 1,
75 | maxArgc: 1,
76 | specChinese: specChineseOptions,
77 | specEnglish: specEnglishOptions,
78 | group: GroupTypeNormalCommand,
79 | validOptionNames: []string{
80 | OptionConfigFile,
81 | OptionEndpoint,
82 | OptionAccessKeyID,
83 | OptionAccessKeySecret,
84 | OptionSTSToken,
85 | OptionProxyHost,
86 | OptionProxyUser,
87 | OptionProxyPwd,
88 | OptionLogLevel,
89 | OptionEncodingType,
90 | OptionOrigin,
91 | OptionAcrMethod,
92 | OptionAcrHeaders,
93 | OptionPassword,
94 | OptionMode,
95 | OptionECSRoleName,
96 | OptionTokenTimeout,
97 | OptionRamRoleArn,
98 | OptionRoleSessionName,
99 | OptionReadTimeout,
100 | OptionConnectTimeout,
101 | OptionSTSRegion,
102 | OptionSkipVerifyCert,
103 | OptionUserAgent,
104 | OptionSignVersion,
105 | OptionRegion,
106 | OptionCloudBoxID,
107 | OptionForcePathStyle,
108 | },
109 | },
110 | }
111 |
112 | // function for FormatHelper interface
113 | func (opsc *OptionsCommand) formatHelpForWhole() string {
114 | return opsc.command.formatHelpForWhole()
115 | }
116 |
117 | func (opsc *OptionsCommand) formatIndependHelp() string {
118 | return opsc.command.formatIndependHelp()
119 | }
120 |
121 | // Init simulate inheritance, and polymorphism
122 | func (opsc *OptionsCommand) Init(args []string, options OptionMapType) error {
123 | return opsc.command.Init(args, options, opsc)
124 | }
125 |
126 | // RunCommand simulate inheritance, and polymorphism
127 | func (opsc *OptionsCommand) RunCommand() error {
128 | strOrigin, _ := GetString(OptionOrigin, opsc.command.options)
129 | strMethod, _ := GetString(OptionAcrMethod, opsc.command.options)
130 | strAcrHeaders, _ := GetString(OptionAcrHeaders, opsc.command.options)
131 |
132 | strEncodingType, _ := GetString(OptionEncodingType, opsc.command.options)
133 | srcBucketUrL, err := GetCloudUrl(opsc.command.args[0], strEncodingType)
134 | if err != nil {
135 | return err
136 | }
137 |
138 | options := []oss.Option{}
139 |
140 | if len(strOrigin) > 0 {
141 | options = append(options, oss.Origin(strOrigin))
142 | }
143 | if len(strMethod) > 0 {
144 | options = append(options, oss.ACReqMethod(strings.ToUpper(strMethod)))
145 | }
146 | if len(strAcrHeaders) > 0 {
147 | options = append(options, oss.ACReqHeaders(strAcrHeaders))
148 | }
149 |
150 | objectName := srcBucketUrL.object
151 |
152 | client, err := opsc.command.ossClient(srcBucketUrL.bucket)
153 | if err != nil {
154 | return err
155 | }
156 |
157 | bucket, err := client.Bucket(srcBucketUrL.bucket)
158 | if err != nil {
159 | return err
160 | }
161 |
162 | respHeader, err := bucket.OptionsMethod(objectName, options...)
163 | if err != nil {
164 | return err
165 | }
166 |
167 | exclude := map[string]bool{}
168 | exclude["Connection"] = true
169 | exclude["ontent-Length"] = true
170 | exclude["Date"] = true
171 | exclude["Server"] = true
172 | exclude["X-Oss-Request-Id"] = true
173 | exclude["X-Oss-Server-Time"] = true
174 | exclude["Content-Length"] = true
175 |
176 | respHeader.WriteSubset(os.Stdout, exclude)
177 |
178 | return nil
179 | }
180 |
--------------------------------------------------------------------------------
/lib/cors_options_test.go:
--------------------------------------------------------------------------------
1 | package lib
2 |
3 | import (
4 | "encoding/xml"
5 | "os"
6 |
7 | oss "github.com/aliyun/aliyun-oss-go-sdk/oss"
8 | . "gopkg.in/check.v1"
9 | )
10 |
11 | func (s *OssutilCommandSuite) TestCorsOptionsSuccess(c *C) {
12 | bucketName := bucketNamePrefix + randLowStr(12)
13 | s.putBucket(bucketName, c)
14 |
15 | corsXml := `
16 |
17 |
18 | www.aliyun.com
19 | PUT
20 | x-oss-meta-author
21 | x-oss-meta-name
22 | 10001
23 |
24 | `
25 |
26 | rulesConfigSrc := oss.CORSXML{}
27 | err := xml.Unmarshal([]byte(corsXml), &rulesConfigSrc)
28 | c.Assert(err, IsNil)
29 |
30 | corsFileName := "ossutil_test." + randLowStr(12)
31 | s.createFile(corsFileName, corsXml, c)
32 |
33 | // cors command test
34 | var str string
35 | strMethod := "put"
36 | options := OptionMapType{
37 | "endpoint": &str,
38 | "accessKeyID": &str,
39 | "accessKeySecret": &str,
40 | "stsToken": &str,
41 | "configFile": &configFile,
42 | "method": &strMethod,
43 | }
44 |
45 | corsArgs := []string{CloudURLToString(bucketName, ""), corsFileName}
46 | _, err = cm.RunCommand("cors", corsArgs, options)
47 | c.Assert(err, IsNil)
48 |
49 | // cors-options success
50 | strOrigin := "www.aliyun.com"
51 | strAcrHeaders := "x-oss-meta-author"
52 | options["origin"] = &strOrigin
53 | options["acrHeaders"] = &strAcrHeaders
54 | options["acrMethod"] = &strMethod
55 | delete(options, "method")
56 |
57 | corsArgs = []string{CloudURLToString(bucketName, "")}
58 | _, err = cm.RunCommand("cors-options", corsArgs, options)
59 | c.Assert(err, IsNil)
60 |
61 | // cors-options error
62 | strOrigin = "www.test.com"
63 | _, err = cm.RunCommand("cors-options", corsArgs, options)
64 | c.Assert(err, NotNil)
65 |
66 | os.Remove(corsFileName)
67 | s.removeBucket(bucketName, true, c)
68 | }
69 |
70 | func (s *OssutilCommandSuite) TestCorsOptionsError(c *C) {
71 | bucketName := bucketNamePrefix + randLowStr(12)
72 | s.putBucket(bucketName, c)
73 |
74 | corsFileName := "ossutil_test_corsfile_" + randLowStr(12)
75 |
76 | // cors command test
77 | var str string
78 | strMethod := ""
79 | options := OptionMapType{
80 | "endpoint": &str,
81 | "accessKeyID": &str,
82 | "accessKeySecret": &str,
83 | "stsToken": &str,
84 | "configFile": &configFile,
85 | "acrMethod": &strMethod,
86 | }
87 |
88 | // method is empty
89 | corsArgs := []string{CloudURLToString(bucketName, ""), corsFileName}
90 | _, err := cm.RunCommand("cors-options", corsArgs, options)
91 | c.Assert(err, NotNil)
92 |
93 | //method is error
94 | strMethod = "puttt"
95 | _, err = cm.RunCommand("cors-options", corsArgs, options)
96 | c.Assert(err, NotNil)
97 |
98 | // cloudurl is error
99 | strMethod = "put"
100 | corsArgs = []string{"http://mybucket", corsFileName}
101 | _, err = cm.RunCommand("cors-options", corsArgs, options)
102 | c.Assert(err, NotNil)
103 |
104 | // StorageURLFromString error
105 | corsArgs = []string{"oss:///1.jpg"}
106 | _, err = cm.RunCommand("cors-options", corsArgs, options)
107 | c.Assert(err, NotNil)
108 |
109 | os.Remove(corsFileName)
110 | s.removeBucket(bucketName, true, c)
111 | }
112 |
113 | func (s *OssutilCommandSuite) TestCorsOptionsEmptyEndpoint(c *C) {
114 | bucketName := bucketNamePrefix + randLowStr(12)
115 | s.putBucket(bucketName, c)
116 |
117 | cfile := randStr(10)
118 | data := "[Credentials]" + "\n" + "language=CH" + "\n" + "accessKeyID=123" + "\n" + "accessKeySecret=456" + "\n" + "endpoint="
119 | s.createFile(cfile, data, c)
120 |
121 | var str string
122 | strMethod := "get"
123 | options := OptionMapType{
124 | "endpoint": &str,
125 | "accessKeyID": &str,
126 | "accessKeySecret": &str,
127 | "stsToken": &str,
128 | "configFile": &cfile,
129 | "acrMethod": &strMethod,
130 | }
131 |
132 | versioingArgs := []string{CloudURLToString(bucketName, "")}
133 | _, err := cm.RunCommand("cors-options", versioingArgs, options)
134 | c.Assert(err, NotNil)
135 |
136 | os.Remove(cfile)
137 | s.removeBucket(bucketName, true, c)
138 | }
139 |
140 | func (s *OssutilCommandSuite) TestCorsOptionsHelpInfo(c *C) {
141 | // mkdir command test
142 | options := OptionMapType{}
143 |
144 | mkArgs := []string{"cors-options"}
145 | _, err := cm.RunCommand("help", mkArgs, options)
146 | c.Assert(err, IsNil)
147 |
148 | mkArgs = []string{}
149 | _, err = cm.RunCommand("help", mkArgs, options)
150 | c.Assert(err, IsNil)
151 |
152 | }
153 |
--------------------------------------------------------------------------------
/lib/create_symlink.go:
--------------------------------------------------------------------------------
1 | package lib
2 |
3 | import (
4 | "fmt"
5 | "strings"
6 |
7 | oss "github.com/aliyun/aliyun-oss-go-sdk/oss"
8 | )
9 |
10 | var specChineseCreateSymlink = SpecText{
11 |
12 | synopsisText: "创建符号链接",
13 |
14 | paramText: "cloud_url target_url [options]",
15 |
16 | syntaxText: `
17 | ossutil create-symlink cloud_url target_object [--encoding-type url] [--payer requester] [-c file]
18 | `,
19 |
20 | detailHelpText: `
21 | 该命令在oss上创建符号链接文件,链接的目标文件必须为相同bucket下的文件,且文件类型非符
22 | 号链接。即,cloud_url必须为形如oss://bucket/object的cloud_url,target_object为object名。
23 |
24 | 创建符号链接时:
25 | 不检查目标文件是否存在,
26 | 不检查目标文件类型是否合法,
27 | 不检查目标文件是否有权限访问,
28 | 以上检查,都推迟到GetObject等需要访问目标文件的API。
29 | 如果试图添加的文件已经存在,并且有访问权限。新添加的文件将覆盖原来的文件。
30 |
31 | 通过stat命令可以查看符号链接的目标文件。
32 |
33 | 更多信息见官网文档:https://help.aliyun.com/document_detail/45126.html?spm=5176.doc31979.6.870.x3Tqsh
34 |
35 | 用法:
36 |
37 | ossutil create-symlink oss://bucket/symlink-object target-object
38 | `,
39 |
40 | sampleText: `
41 | ossutil create-symlink oss://bucket1/object1 object2
42 | 创建从指向object2的符号链接object1。
43 |
44 | ossutil create-symlink oss://bucket1/object1 object2 --payer requester
45 | 以访问者付费模式,创建从指向object2的符号链接object1
46 | `,
47 | }
48 |
49 | var specEnglishCreateSymlink = SpecText{
50 |
51 | synopsisText: "Create symlink of object",
52 |
53 | paramText: "cloud_url target_url [options]",
54 |
55 | syntaxText: `
56 | ossutil create-symlink cloud_url target_object [--encoding-type url] [--payer requester] [-c file]
57 | `,
58 |
59 | detailHelpText: `
60 | The command create symlink of object in oss, the target object must be object in the
61 | same bucket of symlink object, and the file type of target object must not be symlink.
62 | So, cloud_url must be in format: oss://bucket/object, and target_object is the object
63 | name of target object.
64 |
65 | When create symlink:
66 | Will not check whether target object exists;
67 | Will not check whether target object type is valid;
68 | Will not check whether if have access permission of target object.
69 | The check will be done when visiting GetObject, etc.
70 |
71 | If the symlink object exist, and has access permission, the object newly created will
72 | cover the old object.
73 |
74 | We can use stat command to query the target object of symlink object.
75 |
76 | More information about symlink see: https://help.aliyun.com/document_detail/45126.html?spm=5176.doc31979.6.870.x3Tqsh
77 |
78 | Usage:
79 |
80 | ossutil create-symlink oss://bucket/symlink-object target-object
81 | `,
82 |
83 | sampleText: `
84 | ossutil create-symlink oss://bucket1/object1 object2
85 | Create symlink object named object1, which point to object2.
86 |
87 | ossutil create-symlink oss://bucket1/object1 object2 --payer requester
88 | Create symlink object named object1, which point to object2 with requester payment mode
89 | `,
90 | }
91 |
92 | // CreateSymlinkCommand is the command list buckets or objects
93 | type CreateSymlinkCommand struct {
94 | command Command
95 | commonOptions []oss.Option
96 | }
97 |
98 | var createSymlinkCommand = CreateSymlinkCommand{
99 | command: Command{
100 | name: "create-symlink",
101 | nameAlias: []string{},
102 | minArgc: 2,
103 | maxArgc: 2,
104 | specChinese: specChineseCreateSymlink,
105 | specEnglish: specEnglishCreateSymlink,
106 | group: GroupTypeNormalCommand,
107 | validOptionNames: []string{
108 | OptionEncodingType,
109 | OptionConfigFile,
110 | OptionEndpoint,
111 | OptionAccessKeyID,
112 | OptionAccessKeySecret,
113 | OptionSTSToken,
114 | OptionProxyHost,
115 | OptionProxyUser,
116 | OptionProxyPwd,
117 | OptionRetryTimes,
118 | OptionLogLevel,
119 | OptionRequestPayer,
120 | OptionPassword,
121 | OptionMode,
122 | OptionECSRoleName,
123 | OptionTokenTimeout,
124 | OptionRamRoleArn,
125 | OptionRoleSessionName,
126 | OptionReadTimeout,
127 | OptionConnectTimeout,
128 | OptionSTSRegion,
129 | OptionSkipVerifyCert,
130 | OptionUserAgent,
131 | OptionSignVersion,
132 | OptionRegion,
133 | OptionCloudBoxID,
134 | OptionForcePathStyle,
135 | },
136 | },
137 | }
138 |
139 | // function for FormatHelper interface
140 | func (cc *CreateSymlinkCommand) formatHelpForWhole() string {
141 | return cc.command.formatHelpForWhole()
142 | }
143 |
144 | func (cc *CreateSymlinkCommand) formatIndependHelp() string {
145 | return cc.command.formatIndependHelp()
146 | }
147 |
148 | // Init simulate inheritance, and polymorphism
149 | func (cc *CreateSymlinkCommand) Init(args []string, options OptionMapType) error {
150 | return cc.command.Init(args, options, cc)
151 | }
152 |
153 | // RunCommand simulate inheritance, and polymorphism
154 | func (cc *CreateSymlinkCommand) RunCommand() error {
155 | encodingType, _ := GetString(OptionEncodingType, cc.command.options)
156 | cloudURL, err := CloudURLFromString(cc.command.args[0], encodingType)
157 | if err != nil {
158 | return err
159 | }
160 |
161 | targetURL, err := StorageURLFromString(cc.command.args[1], encodingType)
162 | if err != nil {
163 | return err
164 | }
165 |
166 | if err := cc.checkArgs(cloudURL, targetURL); err != nil {
167 | return err
168 | }
169 |
170 | targetObject := targetURL.ToString()
171 | if targetURL.IsCloudURL() {
172 | targetObject = targetURL.(CloudURL).object
173 | }
174 |
175 | bucket, err := cc.command.ossBucket(cloudURL.bucket)
176 | if err != nil {
177 | return err
178 | }
179 |
180 | payer, _ := GetString(OptionRequestPayer, cc.command.options)
181 | if payer != "" {
182 | if payer != strings.ToLower(string(oss.Requester)) {
183 | return fmt.Errorf("invalid request payer: %s, please check", payer)
184 | }
185 | cc.commonOptions = append(cc.commonOptions, oss.RequestPayer(oss.PayerType(payer)))
186 | }
187 |
188 | return cc.ossCreateSymlinkRetry(bucket, cloudURL.object, targetObject)
189 | }
190 |
191 | func (cc *CreateSymlinkCommand) checkArgs(symlinkURL CloudURL, targetURL StorageURLer) error {
192 | if symlinkURL.bucket == "" {
193 | return fmt.Errorf("invalid cloud url: %s, miss bucket", cc.command.args[0])
194 | }
195 | if symlinkURL.object == "" {
196 | return fmt.Errorf("invalid cloud url: %s, miss object, symlink object can't be empty", cc.command.args[0])
197 | }
198 | if targetURL.IsCloudURL() {
199 | if targetURL.(CloudURL).bucket == "" {
200 | return fmt.Errorf("invalid cloud url: %s, miss bucket", cc.command.args[1])
201 | }
202 | if targetURL.(CloudURL).bucket != symlinkURL.bucket {
203 | return fmt.Errorf("the bucket of target object: %s must be the same with the bucket of symlink object: %s", targetURL.(CloudURL).bucket, symlinkURL.bucket)
204 | }
205 | if targetURL.(CloudURL).object == "" {
206 | return fmt.Errorf("invalid cloud url: %s, miss object, target object can't be empty", cc.command.args[1])
207 | }
208 | }
209 | return nil
210 | }
211 |
212 | func (cc *CreateSymlinkCommand) ossCreateSymlinkRetry(bucket *oss.Bucket, symlinkObject, targetObject string) error {
213 | retryTimes, _ := GetInt(OptionRetryTimes, cc.command.options)
214 | for i := 1; ; i++ {
215 | err := bucket.PutSymlink(symlinkObject, targetObject, cc.commonOptions...)
216 | if err == nil {
217 | return err
218 | }
219 | if int64(i) >= retryTimes {
220 | return ObjectError{err, bucket.BucketName, symlinkObject}
221 | }
222 | }
223 | }
224 |
--------------------------------------------------------------------------------
/lib/ecs_role.go:
--------------------------------------------------------------------------------
1 | package lib
2 |
3 | import (
4 | "encoding/json"
5 | "fmt"
6 | "io/ioutil"
7 | "net/http"
8 | "strings"
9 | "sync"
10 | "time"
11 |
12 | oss "github.com/aliyun/aliyun-oss-go-sdk/oss"
13 | )
14 |
15 | const (
16 | AdvanceSeconds int64 = 60
17 | )
18 |
19 | type STSAkJson struct {
20 | AccessKeyId string `json:"AccessKeyId,omitempty"`
21 | AccessKeySecret string `json:"AccessKeySecret,omitempty"`
22 | SecurityToken string `json:"SecurityToken,omitempty"`
23 | Expiration string `json:"Expiration,omitempty"`
24 | LastUpDated string `json:"LastUpDated,omitempty"`
25 | Code string `json:"Code,omitempty"`
26 | }
27 |
28 | func (stsJson *STSAkJson) String() string {
29 | return fmt.Sprintf("AccessKeyId:%s,AccessKeySecret:%s,SecurityToken:%s,Expiration:%s,LastUpDated:%s",
30 | stsJson.AccessKeyId, stsJson.AccessKeySecret, stsJson.SecurityToken, stsJson.Expiration, stsJson.LastUpDated)
31 | }
32 |
33 | type EcsRoleAK struct {
34 | AccessKeyId string
35 | AccessKeySecret string
36 | SecurityToken string
37 | }
38 |
39 | func (ecsRole *EcsRoleAK) GetAccessKeyID() string {
40 | return ecsRole.AccessKeyId
41 | }
42 |
43 | func (ecsRole *EcsRoleAK) GetAccessKeySecret() string {
44 | return ecsRole.AccessKeySecret
45 | }
46 |
47 | func (ecsRole *EcsRoleAK) GetSecurityToken() string {
48 | return ecsRole.SecurityToken
49 | }
50 |
51 | // for ecs bind ram and get ak by ossutil automaticly
52 | type EcsRoleAKBuild struct {
53 | lock sync.Mutex
54 | HasGet bool
55 | url string //url for get ak,such as http://100.100.100.200/latest/meta-data/Ram/security-credentials/RamRoleName
56 | AccessKeyId string
57 | AccessKeySecret string
58 | SecurityToken string
59 | Expiration string
60 | LastUpDated string
61 | }
62 |
63 | func (roleBuild *EcsRoleAKBuild) GetCredentials() oss.Credentials {
64 | cred, _ := roleBuild.GetCredentialsE()
65 | return cred
66 | }
67 |
68 | func (roleBuild *EcsRoleAKBuild) GetCredentialsE() (oss.Credentials, error) {
69 | roleBuild.lock.Lock()
70 | defer roleBuild.lock.Unlock()
71 |
72 | akJson := STSAkJson{}
73 | var err error = nil
74 | bTimeOut := false
75 |
76 | if !roleBuild.HasGet {
77 | bTimeOut = true
78 | } else {
79 | bTimeOut = roleBuild.IsTimeOut()
80 | }
81 |
82 | if bTimeOut {
83 | tStart := time.Now().UnixNano() / 1000 / 1000
84 | akJson, err = roleBuild.HttpReqAk()
85 | tEnd := time.Now().UnixNano() / 1000 / 1000
86 |
87 | if err != nil {
88 | return &EcsRoleAK{}, err
89 | } else {
90 | roleBuild.AccessKeyId = akJson.AccessKeyId
91 | roleBuild.AccessKeySecret = akJson.AccessKeySecret
92 | roleBuild.SecurityToken = akJson.SecurityToken
93 | roleBuild.Expiration = akJson.Expiration
94 | LogInfo("get sts ak success,%s,cost:%d(ms)\n", akJson.String(), tEnd-tStart)
95 | }
96 | }
97 | return &EcsRoleAK{
98 | AccessKeyId: roleBuild.AccessKeyId,
99 | AccessKeySecret: roleBuild.AccessKeySecret,
100 | SecurityToken: roleBuild.SecurityToken,
101 | }, nil
102 | }
103 |
104 | func (roleBuild *EcsRoleAKBuild) IsTimeOut() bool {
105 | if roleBuild.Expiration == "" {
106 | return false
107 | }
108 |
109 | // attention: can't use time.ParseInLocation(),ecsRole.Expiration is UTC time
110 | utcExpirationTime, _ := time.Parse("2006-01-02T15:04:05Z", roleBuild.Expiration)
111 |
112 | // Now() returns the current local time
113 | nowLocalTime := time.Now()
114 |
115 | // Unix() returns the number of seconds elapsedsince January 1, 1970 UTC.
116 | if utcExpirationTime.Unix()-nowLocalTime.Unix()-AdvanceSeconds <= 0 {
117 | return true
118 | }
119 | return false
120 | }
121 |
122 | func (roleBuild *EcsRoleAKBuild) HttpReqAk() (STSAkJson, error) {
123 | akJson := STSAkJson{}
124 |
125 | //http time out
126 | c := &http.Client{
127 | Timeout: 15 * time.Second,
128 | }
129 |
130 | resp, err := c.Get(roleBuild.url)
131 | if err != nil {
132 | LogError("insight getAK,http client get error,url is %s,%s\n", roleBuild.url, err.Error())
133 | return akJson, err
134 | }
135 | defer resp.Body.Close()
136 | body, err := ioutil.ReadAll(resp.Body)
137 | if err != nil {
138 | return akJson, err
139 | }
140 |
141 | err = json.Unmarshal(body, &akJson)
142 | if err != nil {
143 | LogError("insight getAK,json.Unmarshal error,body is %s,%s\n", string(body), err.Error())
144 | return akJson, err
145 | }
146 |
147 | // parsar json,such as
148 | //{
149 | // "AccessKeyId" : "XXXXXXXXX",
150 | // "AccessKeySecret" : "XXXXXXXXX",
151 | // "Expiration" : "2017-11-01T05:20:01Z",
152 | // "SecurityToken" : "XXXXXXXXX",
153 | // "LastUpdated" : "2017-10-31T23:20:01Z",
154 | // "Code" : "Success"
155 | // }
156 |
157 | if akJson.Code != "" && strings.ToUpper(akJson.Code) != "SUCCESS" {
158 | LogError("insight getAK,get sts ak error,code:%s\n", akJson.Code)
159 | return akJson, fmt.Errorf("insight getAK,get sts ak error,code:%s", akJson.Code)
160 | }
161 |
162 | if akJson.AccessKeyId == "" || akJson.AccessKeySecret == "" {
163 | LogError("insight getAK,parsar http json body error:\n%s\n", string(body))
164 | return akJson, fmt.Errorf("insight getAK,parsar http json body error:\n%s\n", string(body))
165 | }
166 |
167 | if akJson.Expiration != "" {
168 | _, err := time.Parse("2006-01-02T15:04:05Z", akJson.Expiration)
169 | if err != nil {
170 | LogError("time.Parse error,Expiration is %s,%s\n", akJson.Expiration, err.Error())
171 | return akJson, err
172 | }
173 | }
174 |
175 | roleBuild.HasGet = true
176 | return akJson, nil
177 | }
178 |
--------------------------------------------------------------------------------
/lib/error.go:
--------------------------------------------------------------------------------
1 | package lib
2 |
3 | import (
4 | "fmt"
5 | )
6 |
7 | // CommandError happens when use command in invalid way
8 | type CommandError struct {
9 | command string
10 | reason string
11 | }
12 |
13 | func (e CommandError) Error() string {
14 | return fmt.Sprintf("invalid usage of \"%s\" command, reason: %s, please try \"help %s\" for more information", e.command, e.reason, e.command)
15 | }
16 |
17 | // BucketError happens when access bucket error
18 | type BucketError struct {
19 | err error
20 | bucket string
21 | }
22 |
23 | func (e BucketError) Error() string {
24 | return fmt.Sprintf("%s, Bucket=%s", e.err.Error(), e.bucket)
25 | }
26 |
27 | // ObjectError happens when access object error
28 | type ObjectError struct {
29 | err error
30 | bucket string
31 | object string
32 | }
33 |
34 | func (e ObjectError) Error() string {
35 | return fmt.Sprintf("%s, Bucket=%s, Object=%s", e.err.Error(), e.bucket, e.object)
36 | }
37 |
38 | // FileError happens when access file error
39 | type FileError struct {
40 | err error
41 | file string
42 | }
43 |
44 | func (e FileError) Error() string {
45 | return fmt.Sprintf("%s, File=%s", e.err.Error(), e.file)
46 | }
47 |
48 | type CopyError struct {
49 | err error
50 | }
51 |
52 | func (e CopyError) Error() string {
53 | return e.err.Error()
54 | }
55 |
--------------------------------------------------------------------------------
/lib/hash.go:
--------------------------------------------------------------------------------
1 | package lib
2 |
3 | import (
4 | "crypto/md5"
5 | "encoding/base64"
6 | "fmt"
7 | "hash"
8 | "hash/crc64"
9 | "io"
10 | "os"
11 | "strings"
12 | )
13 |
14 | var specChineseHash = SpecText{
15 |
16 | synopsisText: "计算本地文件的crc64或md5",
17 |
18 | paramText: "file_url [options]",
19 |
20 | syntaxText: `
21 | ossutil hash file_url [--type=hashtype]
22 | `,
23 |
24 | detailHelpText: `
25 | 该命令计算本地文件的crc64值或md5/content-md5值, 可以通过--type选项来控制计算的类型,
26 | 可选类型值为crc64或md5, 默认为` + DefaultHashType + `。
27 |
28 | 注意:oss文件的crc64和content-md5值一般可通过stat命令查看到,参考` + StatCRC64 + `
29 | 字段和` + StatContentMD5 + `字段。若文件在oss支持crc64功能之前上传,则stat命令不支持查看crc64值;
30 | 对于append和multipart类型的文件,stat命令不支持查看content-md5值。
31 |
32 | crc64的计算标准参考ECMA-182标准(http://www.ecma-international.org/publications/standards/Ecma-182.htm)。
33 |
34 | 计算类型为md5时,会同时输出文件的md5以及content-md5值。content-md5值其实是先计算md5
35 | 值获得128比特位数字,然后对该数字进行base64编码得到的值。关于content-md5的更多信息,
36 | 请参考https://tools.ietf.org/html/rfc1864。
37 |
38 | 用法:
39 |
40 | ossutil hash file_url [--type=hashtype]
41 | `,
42 |
43 | sampleText: `
44 | 1) 计算本地文件的crc64:
45 | ossutil hash test.txt 或
46 | ossutil hash test.txt --type=crc64
47 |
48 | 输出:
49 | CRC64-ECMA : 295992936743767023
50 |
51 | 2) 计算本地文件的md5:
52 | ossutil hash test.txt --type=md5
53 |
54 | 输出:
55 | MD5 : 01C3C45C03B2AF225EFAD9F911A33D73
56 | Content-MD5 : AcPEXAOyryJe+tn5EaM9cw==
57 | `,
58 | }
59 |
60 | var specEnglishHash = SpecText{
61 |
62 | synopsisText: "Get crc64 or md5 of local file",
63 |
64 | paramText: "file_url [options]",
65 |
66 | syntaxText: `
67 | ossutil hash file_url [--type=hashtype]
68 | `,
69 |
70 | detailHelpText: `
71 | The command calculate crc64 or md5/content-md5 value of the specified local file,
72 | specify the hashtype by --type, default hashtype is ` + DefaultHashType + `.
73 |
74 | Warning: user can use stat command to check the crc64 or md5/content-md5 value of
75 | normal oss object, see the ` + StatCRC64 + ` and ` + StatContentMD5 + ` field. If the object
76 | was uploaded to oss before oss support crc64 feature, stat result will not show
77 | ` + StatCRC64 + `, if the object is append file type or multipart, stat result
78 | will not show ` + StatContentMD5 + `.
79 |
80 | Crc64 is calcuated according to ECMA-182(http://www.ecma-international.org/publications/standards/Ecma-182.htm).
81 |
82 | When hashtype is md5, it will output both md5 and content-md5 of local file.
83 | Content-md5 is base64 encoded string of md5. For more detial about content-md5,
84 | please refer to https://tools.ietf.org/html/rfc1864.
85 |
86 | Usage:
87 |
88 | ossutil hash file_url [--type=hashtype]
89 | `,
90 |
91 | sampleText: `
92 | 1) Get crc64 of local file:
93 | ossutil hash test.txt or
94 | ossutil hash test.txt --type=crc64
95 |
96 | output:
97 | CRC64-ECMA : 295992936743767023
98 |
99 | 2) Get md5 of local file:
100 | ossutil hash test.txt --type=md5
101 |
102 | output:
103 | MD5 : 01C3C45C03B2AF225EFAD9F911A33D73
104 | Content-MD5 : AcPEXAOyryJe+tn5EaM9cw==
105 | `,
106 | }
107 |
108 | // HashCommand is the command to get crc64/md5 of local file
109 | type HashCommand struct {
110 | command Command
111 | }
112 |
113 | var hashCommand = HashCommand{
114 | command: Command{
115 | name: "hash",
116 | nameAlias: []string{""},
117 | minArgc: 1,
118 | maxArgc: 1,
119 | specChinese: specChineseHash,
120 | specEnglish: specEnglishHash,
121 | group: GroupTypeAdditionalCommand,
122 | validOptionNames: []string{
123 | OptionHashType,
124 | OptionLogLevel,
125 | },
126 | },
127 | }
128 |
129 | // function for RewriteLoadConfiger interface
130 | func (hc *HashCommand) rewriteLoadConfig(configFile string) error {
131 | // read config file, if error exist, do not print error
132 | var err error
133 | if hc.command.configOptions, err = LoadConfig(configFile); err != nil {
134 | hc.command.configOptions = OptionMapType{}
135 | }
136 | return nil
137 | }
138 |
139 | // function for FormatHelper interface
140 | func (hc *HashCommand) formatHelpForWhole() string {
141 | return hc.command.formatHelpForWhole()
142 | }
143 |
144 | func (hc *HashCommand) formatIndependHelp() string {
145 | return hc.command.formatIndependHelp()
146 | }
147 |
148 | // Init simulate inheritance, and polymorphism
149 | func (hc *HashCommand) Init(args []string, options OptionMapType) error {
150 | return hc.command.Init(args, options, hc)
151 | }
152 |
153 | // RunCommand simulate inheritance, and polymorphism
154 | func (hc *HashCommand) RunCommand() error {
155 | hashType, _ := GetString(OptionHashType, hc.command.options)
156 | path := hc.command.args[0]
157 |
158 | f, err := os.Open(path)
159 | if err != nil {
160 | return err
161 | }
162 | defer f.Close()
163 | f.Seek(0, os.SEEK_SET)
164 |
165 | switch strings.ToLower(hashType) {
166 | case MD5HashType:
167 | return hashMD5(f)
168 | default:
169 | return hashCRC64(f)
170 | }
171 | }
172 |
173 | func hashMD5(f io.Reader) error {
174 | md5Ins := md5.New()
175 | w, _ := md5Ins.(hash.Hash)
176 | if _, err := io.Copy(w, f); err != nil {
177 | return err
178 | }
179 |
180 | result := md5Ins.Sum(nil)
181 | fmt.Printf("%-28s: %X\n", HashMD5, result)
182 |
183 | encoded := base64.StdEncoding.EncodeToString(result)
184 | fmt.Printf("%-28s: %s\n", HashContentMD5, encoded)
185 | return nil
186 | }
187 |
188 | func hashCRC64(f io.Reader) error {
189 | crc64Ins := crc64.New(crc64.MakeTable(crc64.ECMA))
190 | w, _ := crc64Ins.(hash.Hash)
191 | if _, err := io.Copy(w, f); err != nil {
192 | return err
193 | }
194 |
195 | result := crc64Ins.Sum64()
196 | fmt.Printf("%-28s: %d\n", HashCRC64, result)
197 | return nil
198 | }
199 |
--------------------------------------------------------------------------------
/lib/hash_test.go:
--------------------------------------------------------------------------------
1 | package lib
2 |
3 | import (
4 | . "gopkg.in/check.v1"
5 | "os"
6 | )
7 |
8 | func (s *OssutilCommandSuite) TestErrorInputFile(c *C) {
9 | command := "hash"
10 | fakeFileName := randStr(10)
11 | args := []string{fakeFileName}
12 | hashType := DefaultHashType
13 | options := OptionMapType{
14 | "hash": &hashType,
15 | }
16 | showElapse, err := cm.RunCommand(command, args, options)
17 | c.Assert(err, NotNil)
18 | c.Assert(showElapse, Equals, false)
19 | }
20 |
21 | func (s *OssutilCommandSuite) TestErrorHashType(c *C) {
22 | command := "hash"
23 | hashType := "crc256"
24 | fakeFileName := randStr(10)
25 | args := []string{fakeFileName}
26 | options := OptionMapType{
27 | "hashType": &hashType,
28 | }
29 | showElapse, err := cm.RunCommand(command, args, options)
30 | c.Assert(err, NotNil)
31 | c.Assert(showElapse, Equals, false)
32 | }
33 |
34 | func (s *OssutilCommandSuite) TestCrc64(c *C) {
35 | command := "hash"
36 | content = "this is content"
37 | s.createFile(inputFileName, content, c)
38 |
39 | args := []string{inputFileName}
40 | hashType := DefaultHashType
41 | options := OptionMapType{
42 | "hashType": &hashType,
43 | }
44 |
45 | testResultFile, _ = os.OpenFile(resultPath, os.O_RDWR|os.O_TRUNC|os.O_CREATE, 0664)
46 |
47 | saveOut := os.Stdout
48 | os.Stdout = testResultFile
49 |
50 | showElapse, err := cm.RunCommand(command, args, options)
51 | c.Assert(err, IsNil)
52 | c.Assert(showElapse, Equals, false)
53 |
54 | hashStat := s.getHashResults(c)
55 | c.Assert(hashStat[HashCRC64], Equals, "2863152195715871371")
56 |
57 | os.Stdout = saveOut
58 |
59 | os.Remove(inputFileName)
60 | }
61 |
62 | func (s *OssutilCommandSuite) TestMd5(c *C) {
63 | command := "hash"
64 | content = "this is content"
65 | s.createFile(inputFileName, content, c)
66 |
67 | args := []string{inputFileName}
68 | hashType := MD5HashType
69 | options := OptionMapType{
70 | "hashType": &hashType,
71 | }
72 |
73 | testResultFile, _ = os.OpenFile(resultPath, os.O_RDWR|os.O_TRUNC|os.O_CREATE, 0664)
74 |
75 | saveOut := os.Stdout
76 | os.Stdout = testResultFile
77 |
78 | showElapse, err := cm.RunCommand(command, args, options)
79 | c.Assert(err, IsNil)
80 | c.Assert(showElapse, Equals, false)
81 |
82 | hashStat := s.getHashResults(c)
83 | c.Assert(hashStat[HashMD5], Equals, "B7FCEF7FE745F2A95560FF5F550E3B8F")
84 | c.Assert(hashStat[HashContentMD5], Equals, "t/zvf+dF8qlVYP9fVQ47jw==")
85 |
86 | os.Stdout = saveOut
87 |
88 | os.Remove(inputFileName)
89 | }
90 |
--------------------------------------------------------------------------------
/lib/help.go:
--------------------------------------------------------------------------------
1 | package lib
2 |
3 | import (
4 | "fmt"
5 | "reflect"
6 | "strings"
7 | )
8 |
9 | // global public variable for formating help text
10 | const (
11 | FormatTAB = " "
12 | MaxCommandNameLen = 18
13 | UsageTextChinese = "用法: ossutil [command] [args...] [options...]\n请使用ossutil help command来显示command命令的帮助"
14 | UsageTextEnglish = "Usage: ossutil [command] [args...] [options...]\nPlease use 'ossutil help command' to show help of command"
15 | )
16 |
17 | var specChineseHelp = SpecText{
18 |
19 | synopsisText: "获取命令的帮助文档",
20 |
21 | paramText: "[command]",
22 |
23 | syntaxText: `
24 | ossutil help [command]
25 | `,
26 |
27 | detailHelpText: `
28 | 该命令提供ossutil所有命令的帮助文档,或者针对用户输入的某具体命令提供它的帮助文档。
29 |
30 | 用法:
31 |
32 | 该命令有两种用法:
33 |
34 | 1) ossutil help
35 | 该用法提供ossutil支持的所有命令的简介,对每个命令显示该命令的摘要和语法简介。
36 |
37 | 2) ossutil help command
38 | 该用法提供指定命令(command)的帮助文档,包括该命令的详细介绍、示例、可选参数。
39 | `,
40 |
41 | sampleText: `
42 | ossutil help
43 | ossutil help help
44 | ossutil help ls
45 | `,
46 | }
47 |
48 | var specEnglishHelp = SpecText{
49 |
50 | synopsisText: "Get help about commands",
51 |
52 | paramText: "[command]",
53 |
54 | syntaxText: `
55 | ossutil help [command]
56 | `,
57 |
58 | detailHelpText: `
59 | The command provide the usage of all commands on which help is available,
60 | or the usage of the specified command.
61 |
62 | Usage:
63 |
64 | There are two usages:
65 |
66 | 1) ossutil help
67 | The usage provides a summary of all commands, each command shows the
68 | synopsis and a simplified expression of the syntax.
69 |
70 | 2) ossutil help command
71 | The usage provides help about the specified command, which contains
72 | a detailed description of the command, include samples and optional options.
73 | `,
74 |
75 | sampleText: `
76 | ossutil help
77 | ossutil help help
78 | ossutil help ls
79 | `,
80 | }
81 |
82 | // HelpCommand is the command format help text
83 | type HelpCommand struct {
84 | command Command
85 | }
86 |
87 | var helpCommand = HelpCommand{
88 | command: Command{
89 | name: "help",
90 | nameAlias: []string{},
91 | minArgc: 0,
92 | maxArgc: 1,
93 | specChinese: specChineseHelp,
94 | specEnglish: specEnglishHelp,
95 | group: GroupTypeAdditionalCommand,
96 | validOptionNames: []string{
97 | OptionLanguage,
98 | OptionLogLevel,
99 | },
100 | },
101 | }
102 |
103 | // function for RewriteLoadConfiger interface
104 | func (hc *HelpCommand) rewriteLoadConfig(configFile string) error {
105 | // read config file, if error exist, do not print error
106 | var err error
107 | if hc.command.configOptions, err = LoadConfig(configFile); err != nil {
108 | hc.command.configOptions = OptionMapType{}
109 | }
110 | return nil
111 | }
112 |
113 | // function for FormatHelper interface
114 | func (hc *HelpCommand) formatHelpForWhole() string {
115 | return hc.command.formatHelpForWhole()
116 | }
117 |
118 | func (hc *HelpCommand) formatIndependHelp() string {
119 | return hc.command.formatIndependHelp()
120 | }
121 |
122 | // Init simulate inheritance, and polymorphism
123 | func (hc *HelpCommand) Init(args []string, options OptionMapType) error {
124 | return hc.command.Init(args, options, hc)
125 | }
126 |
127 | // RunCommand simulate inheritance, and polymorphism
128 | func (hc *HelpCommand) RunCommand() error {
129 | groupCommandMap, subCommandMap := hc.getCommandMap()
130 | if len(hc.command.args) == 0 {
131 | // ossutil help
132 | text := hc.formatWholeHelp(groupCommandMap)
133 | Output(text)
134 | } else {
135 | //ossutil help command
136 | if text, err := hc.formatCommandHelp(subCommandMap); err == nil {
137 | Output(text)
138 | } else {
139 | return err
140 | }
141 | }
142 | return nil
143 | }
144 |
145 | func (hc *HelpCommand) getCommandMap() (map[string][]interface{}, map[string]interface{}) {
146 | commandList := GetAllCommands()
147 | groupCommandMap := map[string][]interface{}{}
148 | subCommandMap := map[string]interface{}{}
149 | for _, cmd := range commandList {
150 | group := reflect.ValueOf(cmd).Elem().FieldByName("command").FieldByName("group").String()
151 | groupCommandMap[group] = append(groupCommandMap[group], cmd)
152 | name := reflect.ValueOf(cmd).Elem().FieldByName("command").FieldByName("name").String()
153 | subCommandMap[name] = cmd
154 | }
155 | return groupCommandMap, subCommandMap
156 | }
157 |
158 | func (hc *HelpCommand) formatWholeHelp(groupCommandMap map[string][]interface{}) string {
159 | if len(groupCommandMap) == 0 {
160 | return ""
161 | }
162 |
163 | commandsText := ""
164 | for _, group := range CommandGroups {
165 | commandList := groupCommandMap[group]
166 | if len(commandList) == 0 {
167 | continue
168 | }
169 |
170 | commandsText += group
171 | for _, cmd := range commandList {
172 | commandsText += cmd.(FormatHelper).formatHelpForWhole()
173 | }
174 | }
175 | return fmt.Sprintf("%s\n%s", hc.getUsageText(), commandsText)
176 | }
177 |
178 | func (hc *HelpCommand) getUsageText() string {
179 | val, _ := GetString(OptionLanguage, helpCommand.command.options)
180 | switch strings.ToLower(val) {
181 | case LEnglishLanguage:
182 | return UsageTextEnglish
183 | default:
184 | return UsageTextChinese
185 | }
186 |
187 | }
188 |
189 | func (hc *HelpCommand) formatCommandHelp(subCommandMap map[string]interface{}) (string, error) {
190 | subCommandName := hc.command.args[0]
191 | if cmd, ok := subCommandMap[subCommandName]; ok {
192 | return cmd.(FormatHelper).formatIndependHelp(), nil
193 | }
194 | return "", fmt.Errorf("no such command: \"%s\", please try \"help\" for more information", subCommandName)
195 | }
196 |
--------------------------------------------------------------------------------
/lib/help_test.go:
--------------------------------------------------------------------------------
1 | package lib
2 |
3 | import (
4 | "fmt"
5 | "os"
6 |
7 | . "gopkg.in/check.v1"
8 | )
9 |
10 | type OssutilHelpSuite struct{}
11 |
12 | var _ = Suite(&OssutilHelpSuite{})
13 |
14 | // Run once when the suite starts running
15 | func (s *OssutilHelpSuite) SetUpSuite(c *C) {
16 | fmt.Printf("set up suite OssutilHelpSuite\n")
17 | testLogger.Println("test help started")
18 | os.Stdout = testLogFile
19 | os.Stderr = testLogFile
20 | SetUpCredential()
21 | cm.Init()
22 | }
23 |
24 | // Run before each test or benchmark starts running
25 | func (s *OssutilHelpSuite) TearDownSuite(c *C) {
26 | fmt.Printf("tear down suite OssutilHelpSuite\n")
27 | testLogger.Println("test help completed")
28 | os.Remove(configFile)
29 | os.Stdout = out
30 | os.Stderr = errout
31 | }
32 |
33 | // Run after each test or benchmark runs
34 | func (s *OssutilHelpSuite) SetUpTest(c *C) {
35 | fmt.Printf("set up test:%s\n", c.TestName())
36 | }
37 |
38 | // Run once after all tests or benchmarks have finished running
39 | func (s *OssutilHelpSuite) TearDownTest(c *C) {
40 | fmt.Printf("tear down test:%s\n", c.TestName())
41 | }
42 |
43 | // test "help"
44 | func (s *OssutilHelpSuite) TestHelp(c *C) {
45 | command := "help"
46 | var args []string
47 | var options OptionMapType
48 | showElapse, err := cm.RunCommand(command, args, options)
49 | c.Assert(showElapse, Equals, false)
50 | c.Assert(err, IsNil)
51 | }
52 |
53 | // test "help"
54 | func (s *OssutilHelpSuite) TestHelpChinese(c *C) {
55 | command := "help"
56 | var args []string
57 | language := DefaultLanguage
58 | options := OptionMapType{
59 | "language": &language,
60 | }
61 | showElapse, err := cm.RunCommand(command, args, options)
62 | c.Assert(showElapse, Equals, false)
63 | c.Assert(err, IsNil)
64 | }
65 |
66 | // test "help"
67 | func (s *OssutilHelpSuite) TestHelpEnglish(c *C) {
68 | command := "help"
69 | var args []string
70 | language := EnglishLanguage
71 | options := OptionMapType{
72 | "language": &language,
73 | }
74 | showElapse, err := cm.RunCommand(command, args, options)
75 | c.Assert(showElapse, Equals, false)
76 | c.Assert(err, IsNil)
77 | }
78 |
79 | func (s *OssutilHelpSuite) TestHelpLEnglish(c *C) {
80 | command := "help"
81 | var args []string
82 | language := LEnglishLanguage
83 | options := OptionMapType{
84 | "language": &language,
85 | }
86 | showElapse, err := cm.RunCommand(command, args, options)
87 | c.Assert(showElapse, Equals, false)
88 | c.Assert(err, IsNil)
89 | }
90 |
91 | // test "help options"
92 | func (s *OssutilHelpSuite) TestHelpOption(c *C) {
93 | command := "help"
94 | var args []string
95 | for _, option := range OptionMap {
96 | str := "abc"
97 | options := OptionMapType{option.name: &str}
98 | showElapse, err := cm.RunCommand(command, args, options)
99 | c.Assert(showElapse, Equals, false)
100 | c.Assert(err, Equals, CommandError{command: "help", reason: fmt.Sprintf("the command does not support option: \"%s\"", option.name)})
101 | }
102 | }
103 |
104 | // test "help options" with not exist options
105 | var notExistOptions = []string{"notexist", "abc", "CONFIG_FILE", "ACCESSKEY_ID", "accesskey_id"}
106 |
107 | func (s *OssutilHelpSuite) TestHelpNotExistOption(c *C) {
108 | command := "help"
109 | var args []string
110 | for _, option := range notExistOptions {
111 | str := "abc"
112 | options := OptionMapType{option: &str}
113 | showElapse, err := cm.RunCommand(command, args, options)
114 | c.Assert(showElapse, Equals, false)
115 | c.Assert(err, Equals, CommandError{command: "help", reason: fmt.Sprintf("the command does not support option: \"%s\"", option)})
116 | }
117 | }
118 |
119 | // test "help command"
120 | type helpCommandTestCase struct {
121 | subCommand string
122 | showElapse bool
123 | err error
124 | }
125 |
126 | var subCommands = []string{"help", "config", "hash", "update", "mb", "ls", "rm", "stat", "set-acl", "set-meta", "cp", "restore", "create-symlink", "read-symlink"}
127 |
128 | func (s *OssutilHelpSuite) TestHelpCommand(c *C) {
129 | command := "help"
130 | for _, language := range []string{DefaultLanguage, EnglishLanguage, LEnglishLanguage} {
131 | options := OptionMapType{
132 | "language": &language,
133 | }
134 | for _, subCmd := range subCommands {
135 | args := []string{subCmd}
136 | showElapse, err := cm.RunCommand(command, args, options)
137 | c.Assert(showElapse, Equals, false)
138 | c.Assert(err, IsNil)
139 | }
140 | }
141 | }
142 |
143 | var notExistSubCommands = []string{"hp", "pb", "list", "remove", "delete", "info", "set_acl", "set_meta", "copy", "notexit"}
144 |
145 | func (s *OssutilHelpSuite) TestHelpNotExistCommand(c *C) {
146 | command := "help"
147 | var options OptionMapType
148 | for _, subCmd := range notExistSubCommands {
149 | args := []string{subCmd}
150 | showElapse, err := cm.RunCommand(command, args, options)
151 | c.Assert(showElapse, Equals, false)
152 | c.Assert(err, NotNil)
153 | }
154 | }
155 |
156 | // test "help command options"
157 | func (s *OssutilHelpSuite) TestHelpCommandOption(c *C) {
158 | command := "help"
159 | for _, subCmd := range subCommands {
160 | for _, option := range OptionMap {
161 | args := []string{subCmd}
162 | str := "abc"
163 | options := OptionMapType{option.name: &str}
164 | showElapse, err := cm.RunCommand(command, args, options)
165 | c.Assert(showElapse, Equals, false)
166 | c.Assert(err, NotNil)
167 | }
168 | }
169 | }
170 |
171 | func (s *OssutilHelpSuite) TestHelpLoadConfig(c *C) {
172 | fileName := "notexistconfigfile"
173 | err := helpCommand.rewriteLoadConfig(fileName)
174 | c.Assert(err, IsNil)
175 |
176 | err = helpCommand.rewriteLoadConfig(configFile)
177 | c.Assert(err, IsNil)
178 | }
179 |
--------------------------------------------------------------------------------
/lib/lang.go:
--------------------------------------------------------------------------------
1 | // This is for Condition Compling, which means it will be built on all non-windows platform.
2 |
3 | //go:build !windows
4 | // +build !windows
5 |
6 | package lib
7 |
8 | import (
9 | "os"
10 | "strings"
11 | )
12 |
13 | func getOsLang() string {
14 | lang := os.Getenv("LANG")
15 | langstr := strings.Split(lang, ".")
16 |
17 | if langstr[0] == "zh_CN" {
18 | return ChineseLanguage
19 | }
20 | return EnglishLanguage
21 | }
22 |
--------------------------------------------------------------------------------
/lib/lang_test.go:
--------------------------------------------------------------------------------
1 | //go:build !windows
2 | // +build !windows
3 |
4 | package lib
5 |
6 | import (
7 | . "gopkg.in/check.v1"
8 | "os"
9 | )
10 |
11 | func (s *OssutilCommandSuite) TestGetOsLang(c *C) {
12 | lang := os.Getenv("LANG")
13 |
14 | os.Setenv("LANG", "zh_CN.GB2312")
15 | c.Assert(getOsLang(), Equals, ChineseLanguage)
16 | os.Setenv("LANG", "zh_CN")
17 | c.Assert(getOsLang(), Equals, ChineseLanguage)
18 | os.Setenv("LANG", "en_US.UTF-8")
19 | c.Assert(getOsLang(), Equals, EnglishLanguage)
20 | os.Setenv("LANG", "es_ES.UTF-8")
21 | c.Assert(getOsLang(), Equals, EnglishLanguage)
22 | os.Setenv("LANG", "da_DK")
23 | c.Assert(getOsLang(), Equals, EnglishLanguage)
24 | os.Setenv("LANG", "zh_TW")
25 | c.Assert(getOsLang(), Equals, EnglishLanguage)
26 |
27 | os.Setenv("LANG", lang)
28 | }
29 |
--------------------------------------------------------------------------------
/lib/lang_windows.go:
--------------------------------------------------------------------------------
1 | // This filename is for Condition Compling, which means it will be built only on windows platform.
2 |
3 | package lib
4 |
5 | import (
6 | "syscall"
7 | )
8 |
9 | func getOsLang() string {
10 | var mod = syscall.NewLazyDLL("kernel32.dll")
11 | var proc = mod.NewProc("GetUserDefaultUILanguage")
12 | ret, _, _ := proc.Call()
13 |
14 | /* Refer following link about LanggId values
15 | * https://msdn.microsoft.com/en-us/library/bb165625(v=vs.90).aspx
16 | */
17 | if ret == 2052 { // 2052 is User Language ID, means Chinese (Simplified)
18 | return ChineseLanguage
19 | }
20 | return EnglishLanguage
21 | }
22 |
--------------------------------------------------------------------------------
/lib/lang_windows_test.go:
--------------------------------------------------------------------------------
1 | //go:build windows
2 | // +build windows
3 |
4 | package lib
5 |
6 | import (
7 | "fmt"
8 | "syscall"
9 |
10 | . "gopkg.in/check.v1"
11 | )
12 |
13 | func (s *OssutilCommandSuite) TestGetOsLang(c *C) {
14 | var mod = syscall.NewLazyDLL("kernel32.dll")
15 |
16 | var proc = mod.NewProc("GetUserDefaultUILanguage")
17 | err := proc.Find()
18 | if err != nil {
19 | fmt.Printf("%s", err.Error())
20 | return
21 | }
22 |
23 | var sproc = mod.NewProc("SetUserDefaultUILanguage")
24 | err = sproc.Find()
25 | if err != nil {
26 | fmt.Printf("%s", err.Error())
27 | return
28 | }
29 |
30 | oriLang, _, _ := proc.Call()
31 |
32 | sproc.Call(1033)
33 | c.Assert(getOsLang(), Equals, EnglishLanguage)
34 |
35 | sproc.Call(2052)
36 | c.Assert(getOsLang(), Equals, ChineseLanguage)
37 |
38 | sproc.Call(oriLang)
39 | }
40 |
--------------------------------------------------------------------------------
/lib/lcb.go:
--------------------------------------------------------------------------------
1 | package lib
2 |
3 | import (
4 | "fmt"
5 |
6 | oss "github.com/aliyun/aliyun-oss-go-sdk/oss"
7 | )
8 |
9 | var specChineseListCloudBox = SpecText{
10 |
11 | synopsisText: "列举云盒信息",
12 |
13 | paramText: "[options]",
14 |
15 | syntaxText: `
16 | ossutil lcb [-e endpoint]
17 | `,
18 |
19 | detailHelpText: `
20 | 该命令列举云盒的详细信息
21 | `,
22 |
23 | sampleText: `
24 | 1) ossutil lcb --sign-version v4 --region cn-hangzhou --cloudbox-id cb-abcdef
25 | `,
26 | }
27 |
28 | var specEnglishListCloudBox = SpecText{
29 |
30 | synopsisText: "List cloud box information",
31 |
32 | paramText: "[options]",
33 |
34 | syntaxText: `
35 | ossutil lcb [-e endpoint]
36 | `,
37 |
38 | detailHelpText: `
39 | This command lists cloud box information
40 | `,
41 |
42 | sampleText: `
43 | 1) ossutil lcb --sign-version v4 --region cn-hangzhou --cloudbox-id cb-abcdef
44 | `,
45 | }
46 |
47 | // LcbCommand is the command list region buckets or objects
48 | type LcbCommand struct {
49 | command Command
50 | }
51 |
52 | var lcbCommand = LcbCommand{
53 | command: Command{
54 | name: "lcb",
55 | nameAlias: []string{"lcb"},
56 | minArgc: 0,
57 | maxArgc: 1,
58 | specChinese: specChineseListCloudBox,
59 | specEnglish: specEnglishListCloudBox,
60 | group: GroupTypeNormalCommand,
61 | validOptionNames: []string{
62 | OptionConfigFile,
63 | OptionEndpoint,
64 | OptionAccessKeyID,
65 | OptionAccessKeySecret,
66 | OptionSTSToken,
67 | OptionProxyHost,
68 | OptionProxyUser,
69 | OptionProxyPwd,
70 | OptionRetryTimes,
71 | OptionLogLevel,
72 | OptionPassword,
73 | OptionMode,
74 | OptionECSRoleName,
75 | OptionTokenTimeout,
76 | OptionRamRoleArn,
77 | OptionRoleSessionName,
78 | OptionReadTimeout,
79 | OptionConnectTimeout,
80 | OptionSTSRegion,
81 | OptionSkipVerifyCert,
82 | OptionUserAgent,
83 | OptionRegion,
84 | OptionSignVersion,
85 | OptionLimitedNum,
86 | OptionMarker,
87 | OptionCloudBoxID,
88 | OptionForcePathStyle,
89 | },
90 | },
91 | }
92 |
93 | // function for FormatHelper interface
94 | func (lc *LcbCommand) formatHelpForWhole() string {
95 | return lc.command.formatHelpForWhole()
96 | }
97 |
98 | func (lc *LcbCommand) formatIndependHelp() string {
99 | return lc.command.formatIndependHelp()
100 | }
101 |
102 | // Init simulate inheritance, and polymorphism
103 | func (lc *LcbCommand) Init(args []string, options OptionMapType) error {
104 | return lc.command.Init(args, options, lc)
105 | }
106 |
107 | // RunCommand simulate inheritance, and polymorphism
108 | func (lc *LcbCommand) RunCommand() error {
109 | prefix := ""
110 | if len(lc.command.args) > 0 {
111 | cloudURL, err := CloudURLFromString(lc.command.args[0], "")
112 | if err != nil {
113 | return err
114 | }
115 | prefix = cloudURL.bucket
116 | }
117 |
118 | limitedNum, _ := GetInt(OptionLimitedNum, lc.command.options)
119 | vmarker, _ := GetString(OptionMarker, lc.command.options)
120 | if vmarker, err := lc.command.getRawMarker(vmarker); err != nil {
121 | return fmt.Errorf("invalid marker: %s, marker is not url encoded, %s", vmarker, err.Error())
122 | }
123 |
124 | var num int64
125 | num = 0
126 |
127 | client, err := lc.command.ossClient("")
128 | if err != nil {
129 | return err
130 | }
131 |
132 | // list all cloudbox
133 | pre := oss.Prefix(prefix)
134 | marker := oss.Marker(vmarker)
135 | for limitedNum < 0 || num < limitedNum {
136 | lcr, err := lc.ossListCloudBoxesRetry(client, pre, marker)
137 | if err != nil {
138 | return err
139 | }
140 | pre = oss.Prefix(lcr.Prefix)
141 | marker = oss.Marker(lcr.NextMarker)
142 | for _, box := range lcr.CloudBoxes {
143 | if limitedNum >= 0 && num >= limitedNum {
144 | break
145 | }
146 | fmt.Printf("%-15s:%d\n", "No", num)
147 | fmt.Printf("%-15s:%s\n", "Id", box.ID)
148 | fmt.Printf("%-15s:%s\n", "Name", box.Name)
149 | fmt.Printf("%-15s:%s\n", "Owner", lcr.Owner)
150 | fmt.Printf("%-15s:%s\n", "Region", box.Region)
151 | fmt.Printf("%-15s:%s\n", "ControlEndpoint", box.ControlEndpoint)
152 | fmt.Printf("%-15s:%s\n", "DataEndpoint", box.DataEndpoint)
153 | fmt.Printf("----------------------------------------------------------------------\n")
154 | num++
155 | }
156 | if !lcr.IsTruncated {
157 | break
158 | }
159 | }
160 | return nil
161 | }
162 |
163 | func (lc *LcbCommand) ossListCloudBoxesRetry(client *oss.Client, options ...oss.Option) (oss.ListCloudBoxResult, error) {
164 | retryTimes, _ := GetInt(OptionRetryTimes, lc.command.options)
165 | for i := 1; ; i++ {
166 | lbr, err := client.ListCloudBoxes(options...)
167 | if err == nil || int64(i) >= retryTimes {
168 | return lbr, err
169 | }
170 | }
171 | }
172 |
--------------------------------------------------------------------------------
/lib/listpart.go:
--------------------------------------------------------------------------------
1 | package lib
2 |
3 | import (
4 | "fmt"
5 | "strconv"
6 |
7 | oss "github.com/aliyun/aliyun-oss-go-sdk/oss"
8 | )
9 |
10 | var specChineseListPart = SpecText{
11 | synopsisText: "列出没有完成分块上传的object的分块信息",
12 |
13 | paramText: "oss_object uploadid [options]",
14 |
15 | syntaxText: `
16 | ossutil listpart oss://bucket/object uploadid [options]
17 | `,
18 |
19 | detailHelpText: `
20 | 可以通过ls命令查看bucket的object和uploadid信息,在用本命令查看详细信息
21 |
22 |
23 | 用法:
24 |
25 | 该命令只有一种用法:
26 |
27 | 1) ossutil listpart oss://bucket/object uploadid [options]
28 | 根据object和uploadid查询块信息
29 | `,
30 |
31 | sampleText: `
32 | 1) 根据object和uploadid查询块信息
33 | ossutil listpart oss://bucket/object 8A1912289A705A5F0503FCA71DABFD5A
34 | `,
35 | }
36 |
37 | var specEnglishListPart = SpecText{
38 | synopsisText: "List parts information of uncompleted multipart object",
39 |
40 | paramText: "oss_object uploadid [options]",
41 |
42 | syntaxText: `
43 | ossutil listpart oss://bucket/object uploadid [options]
44 | `,
45 |
46 | detailHelpText: `
47 | You can use the ls command to view the object and uploadid information of a bucket.
48 | Then Use this command to view detailed part information.
49 |
50 | Usages:
51 |
52 | There is only one usage for this command:
53 |
54 | 1) ossutil listpart oss://bucket/object uploadid [options]
55 |
56 | Query parts information according to object and uploadid
57 | `,
58 |
59 | sampleText: `
60 | 1) Query parts information according to object and uploadid
61 |
62 | ossutil listpart oss://bucket/object 8A1912289A705A5F0503FCA71DABFD5A
63 | `,
64 | }
65 |
66 | type listPartOptionType struct {
67 | cloudUrl CloudURL
68 | uploadId string
69 | encodingType string
70 | }
71 |
72 | type ListPartCommand struct {
73 | command Command
74 | lpOption listPartOptionType
75 | }
76 |
77 | var listPartCommand = ListPartCommand{
78 | command: Command{
79 | name: "listpart",
80 | nameAlias: []string{"listpart"},
81 | minArgc: 2,
82 | maxArgc: 2,
83 | specChinese: specChineseListPart,
84 | specEnglish: specEnglishListPart,
85 | group: GroupTypeNormalCommand,
86 | validOptionNames: []string{
87 | OptionConfigFile,
88 | OptionEndpoint,
89 | OptionAccessKeyID,
90 | OptionAccessKeySecret,
91 | OptionSTSToken,
92 | OptionProxyHost,
93 | OptionProxyUser,
94 | OptionProxyPwd,
95 | OptionEncodingType,
96 | OptionLogLevel,
97 | OptionPassword,
98 | OptionMode,
99 | OptionECSRoleName,
100 | OptionTokenTimeout,
101 | OptionRamRoleArn,
102 | OptionRoleSessionName,
103 | OptionReadTimeout,
104 | OptionConnectTimeout,
105 | OptionSTSRegion,
106 | OptionSkipVerifyCert,
107 | OptionUserAgent,
108 | OptionSignVersion,
109 | OptionRegion,
110 | OptionCloudBoxID,
111 | OptionForcePathStyle,
112 | },
113 | },
114 | }
115 |
116 | // function for FormatHelper interface
117 | func (lpc *ListPartCommand) formatHelpForWhole() string {
118 | return lpc.command.formatHelpForWhole()
119 | }
120 |
121 | func (lpc *ListPartCommand) formatIndependHelp() string {
122 | return lpc.command.formatIndependHelp()
123 | }
124 |
125 | // Init simulate inheritance, and polymorphism
126 | func (lpc *ListPartCommand) Init(args []string, options OptionMapType) error {
127 | return lpc.command.Init(args, options, lpc)
128 | }
129 |
130 | // RunCommand simulate inheritance, and polymorphism
131 | func (lpc *ListPartCommand) RunCommand() error {
132 | lpc.lpOption.encodingType, _ = GetString(OptionEncodingType, lpc.command.options)
133 | srcBucketUrL, err := GetCloudUrl(lpc.command.args[0], lpc.lpOption.encodingType)
134 | if err != nil {
135 | return err
136 | }
137 |
138 | if srcBucketUrL.object == "" {
139 | return fmt.Errorf("object name is empty")
140 | }
141 |
142 | lpc.lpOption.cloudUrl = *srcBucketUrL
143 | lpc.lpOption.uploadId = lpc.command.args[1]
144 |
145 | return lpc.ListPart()
146 | }
147 |
148 | func (lpc *ListPartCommand) ListPart() error {
149 | client, err := lpc.command.ossClient(lpc.lpOption.cloudUrl.bucket)
150 | if err != nil {
151 | return err
152 | }
153 |
154 | bucket, err := client.Bucket(lpc.lpOption.cloudUrl.bucket)
155 | if err != nil {
156 | return err
157 | }
158 |
159 | var imur oss.InitiateMultipartUploadResult
160 | imur.Bucket = lpc.lpOption.cloudUrl.bucket
161 | imur.Key = lpc.lpOption.cloudUrl.object
162 | imur.UploadID = lpc.lpOption.uploadId
163 |
164 | partNumberMarker := 0
165 | totalPartCount := 0
166 | var totalPartSize int64 = 0
167 | for i := 0; ; i++ {
168 | lpOptions := []oss.Option{}
169 | lpOptions = append(lpOptions, oss.MaxParts(1000))
170 | lpOptions = append(lpOptions, oss.PartNumberMarker(partNumberMarker))
171 |
172 | lpRes, err := bucket.ListUploadedParts(imur, lpOptions...)
173 | if err != nil {
174 | return err
175 | } else {
176 | totalPartCount += len(lpRes.UploadedParts)
177 | if i == 0 && len(lpRes.UploadedParts) > 0 {
178 | fmt.Printf("%-10s\t%-32s\t%-10s\t%s\n", "PartNumber", "Etag", "Size(Byte)", "LastModifyTime")
179 | }
180 | }
181 |
182 | for _, v := range lpRes.UploadedParts {
183 | //PartNumber,ETag,Size,LastModified
184 | fmt.Printf("%-10d\t%-32s\t%-10d\t%s\n", v.PartNumber, v.ETag, v.Size, v.LastModified.Format("2006-01-02 15:04:05"))
185 | totalPartSize += int64(v.Size)
186 | }
187 |
188 | if lpRes.IsTruncated {
189 | partNumberMarker, err = strconv.Atoi(lpRes.NextPartNumberMarker)
190 | if err != nil {
191 | return err
192 | }
193 | } else {
194 | if totalPartCount > 0 {
195 | fmt.Printf("\ntotal part count:%d\ttotal part size(MB):%.2f\n\n", totalPartCount, float64(totalPartSize/1024)/1024)
196 | }
197 | break
198 | }
199 | }
200 | return nil
201 | }
202 |
--------------------------------------------------------------------------------
/lib/listpart_test.go:
--------------------------------------------------------------------------------
1 | package lib
2 |
3 | import (
4 | "fmt"
5 | "os"
6 | "strings"
7 |
8 | "github.com/aliyun/aliyun-oss-go-sdk/oss"
9 | . "gopkg.in/check.v1"
10 | )
11 |
12 | func (s *OssutilCommandSuite) TestListPartSuccess(c *C) {
13 | // create bucket
14 | bucketName := bucketNamePrefix + randLowStr(12)
15 | s.putBucket(bucketName, c)
16 |
17 | // object name
18 | objectName := "test-ossutil-object-" + randLowStr(10)
19 |
20 | // create file
21 | fileName := "test-ossutil-appendfile" + randLowStr(5)
22 | strText := randLowStr(1024 * 10)
23 | s.createFile(fileName, strText, c)
24 |
25 | // prepare chunks
26 | chunks, err := oss.SplitFileByPartNum(fileName, 10)
27 | c.Assert(err, IsNil)
28 |
29 | fd, err := os.Open(fileName)
30 | c.Assert(err, IsNil)
31 | defer fd.Close()
32 |
33 | // begin upload part
34 | client, err := oss.New(endpoint, accessKeyID, accessKeySecret)
35 | c.Assert(err, IsNil)
36 |
37 | bucket, err := client.Bucket(bucketName)
38 | c.Assert(err, IsNil)
39 |
40 | imur, err := bucket.InitiateMultipartUpload(objectName)
41 | c.Assert(err, IsNil)
42 |
43 | var parts []oss.UploadPart
44 | for _, chunk := range chunks {
45 | fd.Seek(chunk.Offset, os.SEEK_SET)
46 | part, err := bucket.UploadPart(imur, fd, chunk.Size, chunk.Number)
47 | c.Assert(err, IsNil)
48 | parts = append(parts, part)
49 | }
50 |
51 | // not CompleteMultipartUpload
52 |
53 | // begin listpart
54 | var str string
55 | options := OptionMapType{
56 | "endpoint": &str,
57 | "accessKeyID": &str,
58 | "accessKeySecret": &str,
59 | "stsToken": &str,
60 | "configFile": &configFile,
61 | }
62 |
63 | // output to file
64 | outputFile := "test-file-" + randLowStr(5)
65 | testResultFile, err = os.OpenFile(outputFile, os.O_RDWR|os.O_TRUNC|os.O_CREATE, 0664)
66 | c.Assert(err, IsNil)
67 |
68 | oldStdout := os.Stdout
69 | os.Stdout = testResultFile
70 |
71 | listArgs := []string{CloudURLToString(bucketName, objectName), imur.UploadID}
72 | _, err = cm.RunCommand("listpart", listArgs, options)
73 | if err != nil {
74 | fmt.Printf("error:%s\n", err.Error())
75 | }
76 | c.Assert(err, IsNil)
77 | testResultFile.Close()
78 |
79 | os.Stdout = oldStdout
80 |
81 | // check file content
82 | outBody := s.readFile(outputFile, c)
83 | c.Assert(strings.Contains(outBody, "total part count:10"), Equals, true)
84 |
85 | os.Remove(outputFile)
86 | os.Remove(fileName)
87 | s.removeBucket(bucketName, true, c)
88 | }
89 |
90 | func (s *OssutilCommandSuite) TestListPartError(c *C) {
91 | // create bucket
92 | bucketName := bucketNamePrefix + randLowStr(12)
93 | s.putBucket(bucketName, c)
94 |
95 | // create file
96 | fileName := "test-ossutil-appendfile" + randLowStr(5)
97 | strText := randLowStr(1024 * 10)
98 | s.createFile(fileName, strText, c)
99 |
100 | // object name
101 | objectName := "test-ossutil-object-" + randLowStr(10)
102 | s.PutObject(bucketName, objectName, strText, c)
103 |
104 | // begin listpart
105 | var str string
106 | options := OptionMapType{
107 | "endpoint": &str,
108 | "accessKeyID": &str,
109 | "accessKeySecret": &str,
110 | "stsToken": &str,
111 | "configFile": &configFile,
112 | }
113 |
114 | // cloud url error
115 | listArgs := []string{"oss:///1.jpg", "aaaaaaaaaa"}
116 | _, err := cm.RunCommand("listpart", listArgs, options)
117 | c.Assert(err, NotNil)
118 |
119 | // object is empty
120 | listArgs = []string{CloudURLToString(bucketName, ""), "aaaaaaaaaa"}
121 | _, err = cm.RunCommand("listpart", listArgs, options)
122 | c.Assert(err, NotNil)
123 |
124 | // uploadid is not exist
125 | listArgs = []string{CloudURLToString(bucketName, objectName), "aaaaaaaaaa"}
126 | _, err = cm.RunCommand("listpart", listArgs, options)
127 | c.Assert(err, NotNil)
128 |
129 | os.Remove(fileName)
130 | s.removeBucket(bucketName, true, c)
131 | }
132 |
133 | func (s *OssutilCommandSuite) TestListPartClientError(c *C) {
134 | // create bucket
135 | bucketName := bucketNamePrefix + randLowStr(12)
136 | s.putBucket(bucketName, c)
137 |
138 | // create file
139 | fileName := "test-ossutil-listpart" + randLowStr(5)
140 | strText := randLowStr(1024)
141 | s.createFile(fileName, strText, c)
142 |
143 | // object name
144 | objectName := "test-ossutil-object-" + randLowStr(10)
145 |
146 | cfile := randStr(10)
147 | data := "[Credentials]" + "\n" + "language=CH" + "\n" + "accessKeyID=123" + "\n" + "accessKeySecret=456" + "\n" + "endpoint="
148 | s.createFile(cfile, data, c)
149 |
150 | // begin listpart
151 | var str string
152 | options := OptionMapType{
153 | "endpoint": &str,
154 | "accessKeyID": &str,
155 | "accessKeySecret": &str,
156 | "stsToken": &str,
157 | "configFile": &cfile,
158 | }
159 |
160 | listArgs := []string{CloudURLToString(bucketName, objectName), "aaaaaaaaaa"}
161 | _, err := cm.RunCommand("listpart", listArgs, options)
162 | c.Assert(err, NotNil)
163 |
164 | os.Remove(cfile)
165 | os.Remove(fileName)
166 | s.removeBucket(bucketName, true, c)
167 | }
168 |
169 | func (s *OssutilCommandSuite) TestListPartHelp(c *C) {
170 | // mkdir command test
171 | options := OptionMapType{}
172 |
173 | mkArgs := []string{"listpart"}
174 | _, err := cm.RunCommand("help", mkArgs, options)
175 | c.Assert(err, IsNil)
176 |
177 | mkArgs = []string{}
178 | _, err = cm.RunCommand("help", mkArgs, options)
179 | c.Assert(err, IsNil)
180 | }
181 |
--------------------------------------------------------------------------------
/lib/lrb.go:
--------------------------------------------------------------------------------
1 | package lib
2 |
3 | import (
4 | "bufio"
5 | "fmt"
6 | "io"
7 | "os"
8 | "strings"
9 |
10 | oss "github.com/aliyun/aliyun-oss-go-sdk/oss"
11 | )
12 |
13 | var specChineseListRegionBucket = SpecText{
14 |
15 | synopsisText: "列举某个region下的Buckets",
16 |
17 | paramText: "[conf_file] [options]",
18 |
19 | syntaxText: `
20 | ossutil lrb [conf_file] [-e endpoint]
21 | `,
22 |
23 | detailHelpText: `
24 | 该命令列举单个region或者多个region下的bucket列表, 多个region的endpoint信息在配置文件里面配置
25 | `,
26 |
27 | sampleText: `
28 | 1) ossutil lrb conf_file
29 |
30 | 2) ossutil lrb -e oss-cn-shenzhen.aliyuncs.com
31 |
32 | 3) ossutil lrb
33 | `,
34 | }
35 |
36 | var specEnglishListRegionBucket = SpecText{
37 |
38 | synopsisText: "List region buckets",
39 |
40 | paramText: "[conf_file] [options]",
41 |
42 | syntaxText: `
43 | ossutil lrb [conf_file] [-e endpoint]
44 | `,
45 |
46 | detailHelpText: `
47 | This command lists buckets under a single region or multiple regions
48 | The multiple regions's endpoints are configured in the configuration file
49 | `,
50 |
51 | sampleText: `
52 | 1) ossutil lrb conf_file
53 |
54 | 2) ossutil lrb -e oss-cn-shenzhen.aliyuncs.com
55 |
56 | 3) ossutil lrb
57 | `,
58 | }
59 |
60 | // LrbCommand is the command list region buckets or objects
61 | type LrbCommand struct {
62 | command Command
63 | listResult []oss.ListBucketsResult
64 | errorEndpoints []string
65 | err error
66 | }
67 |
68 | var lrbCommand = LrbCommand{
69 | command: Command{
70 | name: "lrb",
71 | nameAlias: []string{"lrb"},
72 | minArgc: 0,
73 | maxArgc: 1,
74 | specChinese: specChineseListRegionBucket,
75 | specEnglish: specEnglishListRegionBucket,
76 | group: GroupTypeNormalCommand,
77 | validOptionNames: []string{
78 | OptionConfigFile,
79 | OptionEndpoint,
80 | OptionAccessKeyID,
81 | OptionAccessKeySecret,
82 | OptionSTSToken,
83 | OptionProxyHost,
84 | OptionProxyUser,
85 | OptionProxyPwd,
86 | OptionLogLevel,
87 | OptionPassword,
88 | OptionMode,
89 | OptionECSRoleName,
90 | OptionTokenTimeout,
91 | OptionRamRoleArn,
92 | OptionRoleSessionName,
93 | OptionReadTimeout,
94 | OptionConnectTimeout,
95 | OptionSTSRegion,
96 | OptionSkipVerifyCert,
97 | OptionUserAgent,
98 | OptionSignVersion,
99 | OptionRegion,
100 | OptionCloudBoxID,
101 | OptionForcePathStyle,
102 | },
103 | },
104 | }
105 |
106 | // function for FormatHelper interface
107 | func (lc *LrbCommand) formatHelpForWhole() string {
108 | return lc.command.formatHelpForWhole()
109 | }
110 |
111 | func (lc *LrbCommand) formatIndependHelp() string {
112 | return lc.command.formatIndependHelp()
113 | }
114 |
115 | // Init simulate inheritance, and polymorphism
116 | func (lc *LrbCommand) Init(args []string, options OptionMapType) error {
117 | return lc.command.Init(args, options, lc)
118 | }
119 |
120 | // RunCommand simulate inheritance, and polymorphism
121 | func (lc *LrbCommand) RunCommand() error {
122 | var err error
123 | if len(lc.command.args) == 0 {
124 | lc.err = lc.listBuckets("")
125 | return lc.display()
126 | }
127 | // read all endpoints from conf file
128 | fileName := lc.command.args[0]
129 | rf, err := os.OpenFile(fileName, os.O_RDONLY, 0600)
130 | if err != nil {
131 | return err
132 | }
133 | defer rf.Close()
134 |
135 | rd := bufio.NewReader(rf)
136 | for {
137 | endpoint, err := rd.ReadString('\n')
138 | if endpoint == "" && io.EOF == err {
139 | break
140 | }
141 | endpoint = strings.TrimSpace(endpoint)
142 | endpoint = strings.Trim(endpoint, "\r")
143 | if strings.HasPrefix(endpoint, "#") {
144 | continue
145 | }
146 | err = lc.listBuckets(endpoint)
147 | if err != nil {
148 | lc.errorEndpoints = append(lc.errorEndpoints, endpoint)
149 | lc.err = err
150 | }
151 | }
152 | return lc.display()
153 | }
154 |
155 | func (lc *LrbCommand) listBuckets(endpoint string) error {
156 | if endpoint != "" {
157 | lc.command.options[OptionEndpoint] = &endpoint
158 | }
159 |
160 | var err error
161 | client, err := lc.command.ossClient("")
162 | if err != nil {
163 | return err
164 | }
165 |
166 | // list region bucket
167 | pre := oss.Prefix("")
168 | marker := oss.Marker("")
169 |
170 | for {
171 | lbr, err := client.ListBuckets(pre, marker, oss.AddParam("regionList", ""))
172 | if err != nil {
173 | return err
174 | }
175 | lc.listResult = append(lc.listResult, lbr)
176 | pre = oss.Prefix(lbr.Prefix)
177 | marker = oss.Marker(lbr.NextMarker)
178 | if !lbr.IsTruncated {
179 | break
180 | }
181 | }
182 | return nil
183 | }
184 |
185 | func (lc *LrbCommand) display() error {
186 | count := 0
187 | for _, result := range lc.listResult {
188 | for _, bucket := range result.Buckets {
189 | if count == 0 {
190 | fmt.Printf("%-30s %20s%s%12s%s%s\n", "CreationTime", "Region", FormatTAB, "StorageClass", FormatTAB, "BucketName")
191 | }
192 | fmt.Printf("%-30s %20s%s%12s%s%s\n", utcToLocalTime(bucket.CreationDate), bucket.Location, FormatTAB, bucket.StorageClass, FormatTAB, CloudURLToString(bucket.Name, ""))
193 | count = count + 1
194 | }
195 | }
196 |
197 | fmt.Printf("\nBucket Number is: %d\n\n", count)
198 | if len(lc.errorEndpoints) > 0 {
199 | fmt.Printf("list bucket failure from these endpoint:\n")
200 | for _, endpoint := range lc.errorEndpoints {
201 | fmt.Printf("%s\n", endpoint)
202 | }
203 | fmt.Printf("\n")
204 | }
205 | return lc.err
206 | }
207 |
--------------------------------------------------------------------------------
/lib/mkdir.go:
--------------------------------------------------------------------------------
1 | package lib
2 |
3 | import (
4 | "fmt"
5 | "strings"
6 | )
7 |
8 | var specChineseMkdir = SpecText{
9 | synopsisText: "创建一个目录,在oss中目录名字有后缀字符'/'",
10 |
11 | paramText: "dir_name [options]",
12 |
13 | syntaxText: `
14 | ossutil mkdir oss://bucket/dir_name
15 | `,
16 |
17 | detailHelpText: `
18 | 1) 如果输入的参数没有以后缀字符'/'结尾,工具会自动添加
19 | 2) 如果目录已经存在,会提示报错
20 | 3) 如果输入的参数包含多级目录,只会创建最后的那一个目录
21 | `,
22 |
23 | sampleText: `
24 | 1) 创建一个目录
25 | ossutil mkdir oss://bucket/dir
26 |
27 | 2) 创建一个多级目录
28 | ossutil mkdir oss://bucket/dir1/dir2
29 | `,
30 | }
31 |
32 | var specEnglishMkdir = SpecText{
33 | synopsisText: "Create a oss directory whose object name has the suffix character '/'",
34 |
35 | paramText: "dir_name [options]",
36 |
37 | syntaxText: `
38 | ossutil mkdir oss://bucket/dir_name
39 | `,
40 | detailHelpText: `
41 | 1) If the input parameter does not end with the suffix character '/', the tool will automatically add
42 | 2) If the directory already exists, you will be prompted with an error.
43 | 3) If the input parameter contains multiple levels of directories, only the last directory will be created.
44 | `,
45 | sampleText: `
46 | 1) create a diretory
47 | ossutil mkdir oss://bucket/dir
48 |
49 | 2) create a multi-level directory
50 | ossutil mkdir oss://bucket/dir1/dir2
51 | `,
52 | }
53 |
54 | type mkOptionType struct {
55 | encodingType string
56 | }
57 |
58 | type MkdirCommand struct {
59 | command Command
60 | mkOption mkOptionType
61 | }
62 |
63 | var mkdirCommand = MkdirCommand{
64 | command: Command{
65 | name: "mkdir",
66 | nameAlias: []string{"mkdir"},
67 | minArgc: 1,
68 | maxArgc: 1,
69 | specChinese: specChineseMkdir,
70 | specEnglish: specEnglishMkdir,
71 | group: GroupTypeNormalCommand,
72 | validOptionNames: []string{
73 | OptionConfigFile,
74 | OptionEndpoint,
75 | OptionAccessKeyID,
76 | OptionAccessKeySecret,
77 | OptionSTSToken,
78 | OptionProxyHost,
79 | OptionProxyUser,
80 | OptionProxyPwd,
81 | OptionLogLevel,
82 | OptionEncodingType,
83 | OptionPassword,
84 | OptionMode,
85 | OptionECSRoleName,
86 | OptionTokenTimeout,
87 | OptionRamRoleArn,
88 | OptionRoleSessionName,
89 | OptionReadTimeout,
90 | OptionConnectTimeout,
91 | OptionSTSRegion,
92 | OptionSkipVerifyCert,
93 | OptionUserAgent,
94 | OptionSignVersion,
95 | OptionRegion,
96 | OptionCloudBoxID,
97 | OptionForcePathStyle,
98 | },
99 | },
100 | }
101 |
102 | // function for FormatHelper interface
103 | func (mkc *MkdirCommand) formatHelpForWhole() string {
104 | return mkc.command.formatHelpForWhole()
105 | }
106 |
107 | func (mkc *MkdirCommand) formatIndependHelp() string {
108 | return mkc.command.formatIndependHelp()
109 | }
110 |
111 | // Init simulate inheritance, and polymorphism
112 | func (mkc *MkdirCommand) Init(args []string, options OptionMapType) error {
113 | return mkc.command.Init(args, options, mkc)
114 | }
115 |
116 | // RunCommand simulate inheritance, and polymorphism
117 | func (mkc *MkdirCommand) RunCommand() error {
118 | mkc.mkOption.encodingType, _ = GetString(OptionEncodingType, mkc.command.options)
119 |
120 | dirUrL, err := StorageURLFromString(mkc.command.args[0], mkc.mkOption.encodingType)
121 | if err != nil {
122 | return fmt.Errorf("StorageURLFromString error")
123 | }
124 |
125 | if !dirUrL.IsCloudURL() {
126 | return fmt.Errorf("parameter is not a cloud url,url is %s", dirUrL.ToString())
127 | }
128 |
129 | cloudUrl := dirUrL.(CloudURL)
130 |
131 | if cloudUrl.bucket == "" {
132 | return fmt.Errorf("bucket name is empty,url is %s", cloudUrl.ToString())
133 | }
134 |
135 | if cloudUrl.object == "" {
136 | return fmt.Errorf("object name is empty,url is %s", cloudUrl.ToString())
137 | }
138 |
139 | if !strings.HasSuffix(cloudUrl.object, "/") {
140 | cloudUrl.object += "/"
141 | }
142 |
143 | return mkc.MkBucketDir(cloudUrl)
144 | }
145 |
146 | func (mkc *MkdirCommand) MkBucketDir(dirUrl CloudURL) error {
147 | bucket, err := mkc.command.ossBucket(dirUrl.bucket)
148 | if err != nil {
149 | return err
150 | }
151 |
152 | bExist, err := bucket.IsObjectExist(dirUrl.object)
153 | if err != nil {
154 | return err
155 | }
156 |
157 | if bExist {
158 | return fmt.Errorf("%s already exists", dirUrl.object)
159 | }
160 |
161 | return bucket.PutObject(dirUrl.object, strings.NewReader(""))
162 | }
163 |
--------------------------------------------------------------------------------
/lib/mkdir_test.go:
--------------------------------------------------------------------------------
1 | package lib
2 |
3 | import (
4 | "io/ioutil"
5 | "strings"
6 |
7 | oss "github.com/aliyun/aliyun-oss-go-sdk/oss"
8 | . "gopkg.in/check.v1"
9 | )
10 |
11 | func (s *OssutilCommandSuite) TestMkdirAll(c *C) {
12 | // create bucket
13 | client, err := oss.New(endpoint, accessKeyID, accessKeySecret)
14 | c.Assert(err, IsNil)
15 |
16 | bucketName := bucketNamePrefix + randLowStr(12)
17 | err = client.CreateBucket(bucketName)
18 | c.Assert(err, IsNil)
19 |
20 | bucket, err := client.Bucket(bucketName)
21 | c.Assert(err, IsNil)
22 |
23 | // mkdir command test
24 | var str string
25 | options := OptionMapType{
26 | "endpoint": &str,
27 | "accessKeyID": &str,
28 | "accessKeySecret": &str,
29 | "stsToken": &str,
30 | "configFile": &configFile,
31 | }
32 |
33 | // error,not a cloud url
34 | mkArgs := []string{"http://www.nn.com/test.jpg"}
35 | _, err = cm.RunCommand("mkdir", mkArgs, options)
36 | c.Assert(err, NotNil)
37 |
38 | // error,bucket is empty
39 | mkArgs = []string{CloudURLToString("", "")}
40 | _, err = cm.RunCommand("mkdir", mkArgs, options)
41 | c.Assert(err, NotNil)
42 |
43 | // error,object is empty
44 | mkArgs = []string{CloudURLToString(bucketName, "")}
45 | _, err = cm.RunCommand("mkdir", mkArgs, options)
46 | c.Assert(err, NotNil)
47 |
48 | // success
49 | dirNameA := randLowStr(12)
50 | mkArgs = []string{CloudURLToString(bucketName, dirNameA)}
51 | _, err = cm.RunCommand("mkdir", mkArgs, options)
52 | c.Assert(err, IsNil)
53 |
54 | //mkdir again ,error
55 | mkArgs = []string{CloudURLToString(bucketName, dirNameA)}
56 | _, err = cm.RunCommand("mkdir", mkArgs, options)
57 | c.Assert(err, NotNil)
58 |
59 | // dirname/
60 | dirNameB := randLowStr(12) + "/"
61 | mkArgs = []string{CloudURLToString(bucketName, dirNameB)}
62 | _, err = cm.RunCommand("mkdir", mkArgs, options)
63 | c.Assert(err, IsNil)
64 |
65 | // dirname/dirname
66 | dirNameC := randLowStr(12) + "/" + randLowStr(12)
67 | mkArgs = []string{CloudURLToString(bucketName, dirNameC)}
68 | _, err = cm.RunCommand("mkdir", mkArgs, options)
69 | c.Assert(err, IsNil)
70 |
71 | //check the exist dirname
72 | dirList := []string{dirNameA, dirNameB, dirNameC}
73 | for _, v := range dirList {
74 | if !strings.HasSuffix(v, "/") {
75 | v += "/"
76 | }
77 | body, err := bucket.GetObject(v)
78 | c.Assert(err, IsNil)
79 |
80 | data, err := ioutil.ReadAll(body)
81 | body.Close()
82 | c.Assert(err, IsNil)
83 | c.Assert(string(data), Equals, "")
84 | }
85 | s.removeBucket(bucketName, true, c)
86 | }
87 |
88 | func (s *OssutilCommandSuite) TestMkdirAllEncodingError(c *C) {
89 | // mkdir command test
90 | var str string
91 | strEncode := "url"
92 | options := OptionMapType{
93 | "endpoint": &str,
94 | "accessKeyID": &str,
95 | "accessKeySecret": &str,
96 | "stsToken": &str,
97 | "configFile": &configFile,
98 | "encodingType": &strEncode,
99 | }
100 |
101 | // url encoding error
102 | mkArgs := []string{"http%3a%3a%2%2faaada%5ct"}
103 | _, err := cm.RunCommand("mkdir", mkArgs, options)
104 | c.Assert(err, NotNil)
105 | }
106 |
107 | func (s *OssutilCommandSuite) TestMkdirHelpInfo(c *C) {
108 | // mkdir command test
109 | options := OptionMapType{}
110 |
111 | mkArgs := []string{"mkdir"}
112 | _, err := cm.RunCommand("help", mkArgs, options)
113 | c.Assert(err, IsNil)
114 |
115 | mkArgs = []string{}
116 | _, err = cm.RunCommand("help", mkArgs, options)
117 | c.Assert(err, IsNil)
118 |
119 | }
120 |
--------------------------------------------------------------------------------
/lib/ossutil_log.go:
--------------------------------------------------------------------------------
1 | package lib
2 |
3 | import (
4 | "bytes"
5 | "fmt"
6 | "log"
7 | "os"
8 | "path/filepath"
9 |
10 | oss "github.com/aliyun/aliyun-oss-go-sdk/oss"
11 | )
12 |
13 | var logName = "ossutil.log"
14 | var logLevel = oss.LogOff
15 | var utilLogger *log.Logger
16 | var logFile *os.File
17 |
18 | func openLogFile() (*os.File, error) {
19 | dir, err := filepath.Abs(filepath.Dir(os.Args[0]))
20 | if err != nil {
21 | dir = "."
22 | }
23 | absLogName := dir + string(os.PathSeparator) + logName
24 | f, err := os.OpenFile(absLogName, os.O_CREATE|os.O_APPEND|os.O_RDWR, 0660)
25 | if err != nil {
26 | fmt.Printf("open %s error,info:%s.\n", absLogName, err.Error())
27 | } else {
28 | fmt.Printf("log file is %s\n", absLogName)
29 | }
30 | return f, err
31 | }
32 |
33 | func InitLogger(level int, name string) {
34 | logLevel = level
35 | logName = name
36 | f, err := openLogFile()
37 | if err != nil {
38 | return
39 | }
40 | utilLogger = log.New(f, "", log.LstdFlags|log.Lmicroseconds)
41 | logFile = f
42 | }
43 |
44 | func UnInitLogger() {
45 | if logFile == nil {
46 | return
47 | }
48 |
49 | logFile.Close()
50 | logFile = nil
51 | utilLogger = nil
52 | }
53 |
54 | func writeLog(level int, format string, a ...interface{}) {
55 | if utilLogger == nil {
56 | return
57 | }
58 |
59 | var logBuffer bytes.Buffer
60 | logBuffer.WriteString(oss.LogTag[level-1])
61 | logBuffer.WriteString(fmt.Sprintf(format, a...))
62 | utilLogger.Printf("%s", logBuffer.String())
63 | return
64 | }
65 |
66 | func LogError(format string, a ...interface{}) {
67 | if logLevel < oss.Error {
68 | return
69 | }
70 | writeLog(oss.Error, format, a...)
71 | }
72 |
73 | func LogWarn(format string, a ...interface{}) {
74 | if logLevel < oss.Warn {
75 | return
76 | }
77 | writeLog(oss.Warn, format, a...)
78 | }
79 |
80 | func LogInfo(format string, a ...interface{}) {
81 |
82 | if logLevel < oss.Info {
83 | return
84 | }
85 | writeLog(oss.Info, format, a...)
86 | }
87 |
88 | func LogDebug(format string, a ...interface{}) {
89 | if logLevel < oss.Debug {
90 | return
91 | }
92 | writeLog(oss.Debug, format, a...)
93 | }
94 |
--------------------------------------------------------------------------------
/lib/ossutil_log_test.go:
--------------------------------------------------------------------------------
1 | package lib
2 |
3 | import (
4 | "fmt"
5 | "io/ioutil"
6 | "os"
7 | "path/filepath"
8 | "strings"
9 |
10 | oss "github.com/aliyun/aliyun-oss-go-sdk/oss"
11 | . "gopkg.in/check.v1"
12 | )
13 |
14 | type OssUtilLogSuite struct {
15 | testLogName string
16 | testLogLevel int
17 | }
18 |
19 | var _ = Suite(&OssUtilLogSuite{})
20 |
21 | // Run once when the suite starts running
22 | func (s *OssUtilLogSuite) SetUpSuite(c *C) {
23 | fmt.Printf("set up suite OssUtilLogSuite\n")
24 | }
25 |
26 | // Run before each test or benchmark starts running
27 | func (s *OssUtilLogSuite) TearDownSuite(c *C) {
28 | fmt.Printf("tear down OssUtilLogSuite\n")
29 | }
30 |
31 | // Run after each test or benchmark runs
32 | func (s *OssUtilLogSuite) SetUpTest(c *C) {
33 | fmt.Printf("set up test:%s\n", c.TestName())
34 | s.testLogName = logName
35 | s.testLogLevel = logLevel
36 |
37 | dir, err := filepath.Abs(filepath.Dir(os.Args[0]))
38 | if err != nil {
39 | dir = ""
40 | }
41 | absLogName := dir + string(os.PathSeparator) + logName
42 | os.Remove(absLogName)
43 | }
44 |
45 | // Run once after all tests or benchmarks have finished running
46 | func (s *OssUtilLogSuite) TearDownTest(c *C) {
47 | fmt.Printf("tear down test:%s\n", c.TestName())
48 | logName = s.testLogName
49 | logLevel = s.testLogLevel
50 |
51 | dir, err := filepath.Abs(filepath.Dir(os.Args[0]))
52 | if err != nil {
53 | dir = ""
54 | }
55 | absLogName := dir + string(os.PathSeparator) + logName
56 | os.Remove(absLogName)
57 | }
58 |
59 | // test "config"
60 | func (s *OssUtilLogSuite) TestLogLevel(c *C) {
61 | dir, err := filepath.Abs(filepath.Dir(os.Args[0]))
62 | if err != nil {
63 | dir = ""
64 | }
65 | absLogName := dir + string(os.PathSeparator) + logName
66 |
67 | fmt.Printf("absLogName:%s.\n", absLogName)
68 |
69 | // nologLevel
70 | logLevel = oss.LogOff
71 | InitLogger(logLevel, logName)
72 |
73 | errorContext := "i am error log.\n"
74 | LogError(errorContext)
75 | LogWarn(errorContext)
76 | LogInfo(errorContext)
77 | LogDebug(errorContext)
78 |
79 | contents, err := ioutil.ReadFile(absLogName)
80 | LogContent := string(contents)
81 | c.Assert(strings.Contains(LogContent, "[error]"+errorContext), Equals, false)
82 | c.Assert(strings.Contains(LogContent, "[warn]"+errorContext), Equals, false)
83 | c.Assert(strings.Contains(LogContent, "[info]"+errorContext), Equals, false)
84 | c.Assert(strings.Contains(LogContent, "[debug]"+errorContext), Equals, false)
85 | UnInitLogger()
86 | os.Remove(absLogName)
87 |
88 | // errorLevel
89 | logLevel = oss.Error
90 | InitLogger(logLevel, logName)
91 | LogError(errorContext)
92 | LogWarn(errorContext)
93 | LogInfo(errorContext)
94 | LogDebug(errorContext)
95 |
96 | contents, err = ioutil.ReadFile(absLogName)
97 | LogContent = string(contents)
98 | c.Assert(strings.Contains(LogContent, "[error]"+errorContext), Equals, true)
99 | c.Assert(strings.Contains(LogContent, "[warn]"+errorContext), Equals, false)
100 | c.Assert(strings.Contains(LogContent, "[info]"+errorContext), Equals, false)
101 | c.Assert(strings.Contains(LogContent, "[debug]"+errorContext), Equals, false)
102 | UnInitLogger()
103 | os.Remove(absLogName)
104 |
105 | // normalLevel
106 | logLevel = oss.Warn
107 | InitLogger(logLevel, logName)
108 | normalContext := "i am normal log.\n"
109 | LogError(normalContext)
110 | LogWarn(normalContext)
111 | LogInfo(normalContext)
112 | LogDebug(normalContext)
113 |
114 | contents, err = ioutil.ReadFile(absLogName)
115 | LogContent = string(contents)
116 | c.Assert(strings.Contains(LogContent, "[error]"+normalContext), Equals, true)
117 | c.Assert(strings.Contains(LogContent, "[warn]"+normalContext), Equals, true)
118 | c.Assert(strings.Contains(LogContent, "[info]"+normalContext), Equals, false)
119 | c.Assert(strings.Contains(LogContent, "[debug]"+normalContext), Equals, false)
120 | UnInitLogger()
121 | os.Remove(absLogName)
122 |
123 | // infolevel
124 | logLevel = oss.Info
125 | InitLogger(logLevel, logName)
126 | infoContext := "i am info log.\n"
127 | LogError(infoContext)
128 | LogWarn(infoContext)
129 | LogInfo(infoContext)
130 | LogDebug(infoContext)
131 |
132 | contents, err = ioutil.ReadFile(absLogName)
133 | LogContent = string(contents)
134 | c.Assert(strings.Contains(LogContent, "[error]"+infoContext), Equals, true)
135 | c.Assert(strings.Contains(LogContent, "[warn]"+infoContext), Equals, true)
136 | c.Assert(strings.Contains(LogContent, "[info]"+infoContext), Equals, true)
137 | c.Assert(strings.Contains(LogContent, "[debug]"+infoContext), Equals, false)
138 | UnInitLogger()
139 | os.Remove(absLogName)
140 |
141 | // debuglevel
142 | logLevel = oss.Debug
143 | InitLogger(logLevel, logName)
144 | debugContext := "i am debug log.\n"
145 | LogError(debugContext)
146 | LogWarn(debugContext)
147 | LogInfo(debugContext)
148 | LogDebug(debugContext)
149 |
150 | contents, err = ioutil.ReadFile(absLogName)
151 | LogContent = string(contents)
152 | c.Assert(strings.Contains(LogContent, "[error]"+debugContext), Equals, true)
153 | c.Assert(strings.Contains(LogContent, "[warn]"+debugContext), Equals, true)
154 | c.Assert(strings.Contains(LogContent, "[info]"+debugContext), Equals, true)
155 | c.Assert(strings.Contains(LogContent, "[debug]"+debugContext), Equals, true)
156 | UnInitLogger()
157 | os.Remove(absLogName)
158 | }
159 |
160 | func (s *OssUtilLogSuite) TestOpenFileError(c *C) {
161 | oldLogName := logName
162 | logName = ""
163 | _, err := openLogFile()
164 | c.Assert(err, NotNil)
165 | logName = oldLogName
166 | }
167 |
168 | func (s *OssUtilLogSuite) TestInitLoggerError(c *C) {
169 | oldLogName := logName
170 | oldLogFile := logFile
171 | InitLogger(oss.Info, "")
172 | c.Assert(logFile, IsNil)
173 |
174 | logName = oldLogName
175 | logFile = oldLogFile
176 | }
177 |
--------------------------------------------------------------------------------
/lib/read_symlink.go:
--------------------------------------------------------------------------------
1 | package lib
2 |
3 | import (
4 | "fmt"
5 | "net/http"
6 | "sort"
7 | "strings"
8 | "time"
9 |
10 | oss "github.com/aliyun/aliyun-oss-go-sdk/oss"
11 | )
12 |
13 | var specChineseReadSymlink = SpecText{
14 |
15 | synopsisText: "读取符号链接文件的描述信息",
16 |
17 | paramText: "cloud_url [options]",
18 |
19 | syntaxText: `
20 | ossutil read-symlink oss://bucket/object [--encoding-type url] [--version-id versionId] [--payer requester] [-c file]
21 | `,
22 |
23 | detailHelpText: `
24 | 该命令获取指定符号链接object的描述信息,此操作要求用户对该符号链接有读权限。
25 | 如果需要读取符合链接object的文件内容,请使用cp命令下载该object。
26 |
27 | 返回的项中X-Oss-Symlink-Target表示符号链接的目标文件。
28 |
29 | 如果object并非符号链接文件,该操作返回错误:NotSymlink。
30 |
31 | 更多信息见官网API文档:https://help.aliyun.com/document_detail/45146.html?spm=5176.doc31968.6.871.24y1VX
32 |
33 | 用法:
34 |
35 | ossutil read-symlink oss://bucket/symlink-object [--version-id versionId] [--payer requester]
36 | `,
37 |
38 | sampleText: `
39 | ossutil read-symlink oss://bucket1/object1
40 | Etag : 455E20DBFFF1D588B67D092C46B16DB6
41 | Last-Modified : 2017-04-17 14:49:42 +0800 CST
42 | X-Oss-Symlink-Target : a
43 |
44 | ossutil read-symlink oss://bucket1/object --version-id versionId
45 |
46 | ossutil read-symlink oss://bucket1/object --payer requester
47 | `,
48 | }
49 |
50 | var specEnglishReadSymlink = SpecText{
51 |
52 | synopsisText: "Display meta information of symlink object",
53 |
54 | paramText: "cloud_url [options]",
55 |
56 | syntaxText: `
57 | ossutil read-symlink oss://bucket/object [--encoding-type url] [--payer requester] [-c file]
58 | `,
59 |
60 | detailHelpText: `
61 | The command display the meta information of symlink object. The operation
62 | requires that the user have read permission of the symlink object. If you
63 | want to get the file data of symlink object, please use cp command to download
64 | the symlink object.
65 |
66 | The item X-Oss-Symlink-Target shows the target object of the symlink object.
67 |
68 | If the object is not symlink object, ossutil return error: NotSymlink.
69 |
70 | More information about symlink see: https://help.aliyun.com/document_detail/45146.html?spm=5176.doc31968.6.871.24y1VX
71 |
72 | Usage:
73 |
74 | ossutil read-symlink oss://bucket/symlink-object [--version-id versionId] [--payer requester]
75 | `,
76 |
77 | sampleText: `
78 | ossutil read-symlink oss://bucket1/object1
79 | Etag : 455E20DBFFF1D588B67D092C46B16DB6
80 | Last-Modified : 2017-04-17 14:49:42 +0800 CST
81 | X-Oss-Symlink-Target : a
82 |
83 | ossutil read-symlink oss://bucket1/object --version-id versionId
84 |
85 | ossutil read-symlink oss://bucket1/object --payer requester
86 | `,
87 | }
88 |
89 | // ReadSymlinkCommand is the command list buckets or objects
90 | type ReadSymlinkCommand struct {
91 | command Command
92 | commonOptions []oss.Option
93 | }
94 |
95 | var readSymlinkCommand = ReadSymlinkCommand{
96 | command: Command{
97 | name: "read-symlink",
98 | nameAlias: []string{},
99 | minArgc: 1,
100 | maxArgc: 1,
101 | specChinese: specChineseReadSymlink,
102 | specEnglish: specEnglishReadSymlink,
103 | group: GroupTypeNormalCommand,
104 | validOptionNames: []string{
105 | OptionEncodingType,
106 | OptionConfigFile,
107 | OptionEndpoint,
108 | OptionAccessKeyID,
109 | OptionAccessKeySecret,
110 | OptionSTSToken,
111 | OptionProxyHost,
112 | OptionProxyUser,
113 | OptionProxyPwd,
114 | OptionRetryTimes,
115 | OptionLogLevel,
116 | OptionVersionId,
117 | OptionRequestPayer,
118 | OptionPassword,
119 | OptionMode,
120 | OptionECSRoleName,
121 | OptionTokenTimeout,
122 | OptionRamRoleArn,
123 | OptionRoleSessionName,
124 | OptionReadTimeout,
125 | OptionConnectTimeout,
126 | OptionSTSRegion,
127 | OptionSkipVerifyCert,
128 | OptionUserAgent,
129 | OptionSignVersion,
130 | OptionRegion,
131 | OptionCloudBoxID,
132 | OptionForcePathStyle,
133 | },
134 | },
135 | }
136 |
137 | // function for FormatHelper interface
138 | func (rc *ReadSymlinkCommand) formatHelpForWhole() string {
139 | return rc.command.formatHelpForWhole()
140 | }
141 |
142 | func (rc *ReadSymlinkCommand) formatIndependHelp() string {
143 | return rc.command.formatIndependHelp()
144 | }
145 |
146 | // Init simulate inheritance, and polymorphism
147 | func (rc *ReadSymlinkCommand) Init(args []string, options OptionMapType) error {
148 | return rc.command.Init(args, options, rc)
149 | }
150 |
151 | // RunCommand simulate inheritance, and polymorphism
152 | func (rc *ReadSymlinkCommand) RunCommand() error {
153 | encodingType, _ := GetString(OptionEncodingType, rc.command.options)
154 | cloudURL, err := ObjectURLFromString(rc.command.args[0], encodingType)
155 | if err != nil {
156 | return err
157 | }
158 |
159 | versionId, _ := GetString(OptionVersionId, rc.command.options)
160 | symlinkOptions := []oss.Option{}
161 | if len(versionId) > 0 {
162 | symlinkOptions = append(symlinkOptions, oss.VersionId(versionId))
163 | }
164 |
165 | payer, _ := GetString(OptionRequestPayer, rc.command.options)
166 | if payer != "" {
167 | if payer != strings.ToLower(string(oss.Requester)) {
168 | return fmt.Errorf("invalid request payer: %s, please check", payer)
169 | }
170 | rc.commonOptions = append(rc.commonOptions, oss.RequestPayer(oss.PayerType(payer)))
171 | }
172 |
173 | symlinkOptions = append(symlinkOptions, rc.commonOptions...)
174 | bucket, err := rc.command.ossBucket(cloudURL.bucket)
175 | if err != nil {
176 | return err
177 | }
178 |
179 | return rc.linkStat(bucket, cloudURL, symlinkOptions...)
180 | }
181 |
182 | func (rc *ReadSymlinkCommand) linkStat(bucket *oss.Bucket, cloudURL CloudURL, options ...oss.Option) error {
183 | // normal info
184 | props, err := rc.ossGetSymlinkRetry(bucket, cloudURL.object, options...)
185 | if err != nil {
186 | return err
187 | }
188 |
189 | sortNames := []string{}
190 | attrMap := map[string]string{}
191 | for name := range props {
192 | ln := strings.ToLower(name)
193 | if ln != strings.ToLower(oss.HTTPHeaderDate) &&
194 | ln != strings.ToLower(oss.HTTPHeaderOssRequestID) &&
195 | ln != strings.ToLower(oss.HTTPHeaderServer) &&
196 | ln != strings.ToLower(oss.HTTPHeaderContentLength) &&
197 | ln != "x-oss-server-time" &&
198 | ln != "connection" {
199 | sortNames = append(sortNames, name)
200 | attrMap[name] = props.Get(name)
201 | }
202 | }
203 |
204 | if lm, err := time.Parse(http.TimeFormat, attrMap[StatLastModified]); err == nil {
205 | attrMap[StatLastModified] = fmt.Sprintf("%s", utcToLocalTime(lm.UTC()))
206 | }
207 |
208 | sort.Strings(sortNames)
209 |
210 | for _, name := range sortNames {
211 | if strings.ToLower(name) != "etag" {
212 | fmt.Printf("%-24s: %s\n", name, attrMap[name])
213 | } else {
214 | fmt.Printf("%-24s: %s\n", name, strings.Trim(attrMap[name], "\""))
215 | }
216 | }
217 | return nil
218 | }
219 |
220 | func (rc *ReadSymlinkCommand) ossGetSymlinkRetry(bucket *oss.Bucket, symlinkObject string, options ...oss.Option) (http.Header, error) {
221 | retryTimes, _ := GetInt(OptionRetryTimes, rc.command.options)
222 | for i := 1; ; i++ {
223 | props, err := bucket.GetSymlink(symlinkObject, options...)
224 | if err == nil {
225 | return props, err
226 | }
227 | if int64(i) >= retryTimes {
228 | return props, ObjectError{err, bucket.BucketName, symlinkObject}
229 | }
230 | }
231 | }
232 |
--------------------------------------------------------------------------------
/lib/report_helper.go:
--------------------------------------------------------------------------------
1 | package lib
2 |
3 | import (
4 | "fmt"
5 | "log"
6 | "os"
7 | "time"
8 | )
9 |
10 | type Reporter struct {
11 | rlogger *log.Logger
12 | written bool
13 | prompted bool
14 | path string
15 | comment string
16 | outputDir string
17 | createDir bool
18 | fileHandle *os.File
19 | }
20 |
21 | func (re *Reporter) Init(outputDir, comment string) error {
22 | if outputDir == "" {
23 | outputDir = DefaultOutputDir
24 | }
25 | re.outputDir = outputDir
26 | re.createDir = false
27 | if _, err := os.Stat(outputDir); err != nil && os.IsNotExist(err) {
28 | re.createDir = true
29 | }
30 | if err := os.MkdirAll(outputDir, 0755); err != nil {
31 | return err
32 | }
33 | re.path = re.outputDir + string(os.PathSeparator) + ReportPrefix + time.Now().Format("20060102_150405") + ReportSuffix
34 | re.comment = comment
35 | re.written = false
36 | re.prompted = false
37 | f, err := os.OpenFile(re.path, os.O_RDWR|os.O_APPEND|os.O_CREATE, 0664)
38 | if err != nil {
39 | return fmt.Errorf("Create reporter file error: %s", err.Error())
40 | }
41 | re.fileHandle = f
42 | re.rlogger = log.New(f, "", log.Ldate|log.Ltime)
43 | re.Comment()
44 | re.rlogger.SetFlags(log.Ldate | log.Ltime)
45 | return nil
46 | }
47 |
48 | func (re *Reporter) Clear() {
49 | if re != nil && re.fileHandle != nil {
50 | re.fileHandle.Close()
51 | }
52 |
53 | if re != nil && !re.written {
54 | os.Remove(re.path)
55 | if re.createDir {
56 | os.RemoveAll(re.outputDir)
57 | }
58 | }
59 | }
60 |
61 | func (re *Reporter) HasPrompt() bool {
62 | if re == nil {
63 | return false
64 | }
65 | return re.prompted == false
66 | }
67 |
68 | func (re *Reporter) Comment() {
69 | if re != nil && !re.written {
70 | re.rlogger.SetFlags(0)
71 | re.rlogger.SetPrefix("# ")
72 | re.rlogger.Println(re.comment)
73 | }
74 | }
75 |
76 | func (re *Reporter) ReportError(msg string) {
77 | if re != nil && re.rlogger != nil {
78 | re.written = true
79 | re.rlogger.SetPrefix("[Error] ")
80 | re.rlogger.Println(msg)
81 | }
82 | }
83 |
84 | func (re *Reporter) Prompt(err error) {
85 | if re != nil && re.written && re.HasPrompt() {
86 | re.prompted = true
87 | fmt.Printf("\r%s\rError occurs, message: %s. See more information in file: %s\n", clearStr, err.Error(), re.path)
88 | }
89 | }
90 |
91 | func GetReporter(need bool, outputDir, comment string) (*Reporter, error) {
92 | if need {
93 | var reporter Reporter
94 | if err := reporter.Init(outputDir, comment); err != nil {
95 | return nil, err
96 | }
97 | return &reporter, nil
98 | }
99 | return nil, nil
100 | }
101 |
--------------------------------------------------------------------------------
/lib/request_payment.go:
--------------------------------------------------------------------------------
1 | package lib
2 |
3 | import (
4 | "fmt"
5 | "strings"
6 |
7 | "github.com/aliyun/aliyun-oss-go-sdk/oss"
8 | )
9 |
10 | var specChineseRequestPayment = SpecText{
11 | synopsisText: "设置、查询bucket的访问者付费配置",
12 |
13 | paramText: "bucket_url [payment_parameter] [options]",
14 |
15 | syntaxText: `
16 | ossutil request-payment --method put oss://bucket payment_parameter
17 | ossutil request-payment --method get oss://bucket
18 | `,
19 | detailHelpText: `
20 | request-payment命令通过设置method选项值为put、get, 可以设置、查询bucket的访问者付费配置
21 | 选项--method为put时,参数只能为Requester, BucketOwner
22 |
23 | 用法:
24 | 该命令有三种用法:
25 |
26 | 1) ossutil request-payment --method put oss://bucket Requester
27 | 这个命令设置由bucket的访问者付费
28 |
29 | 2) ossutil request-payment --method put oss://bucket BucketOwner
30 | 这个命令设置由bucket的拥有者付费
31 |
32 | 3) ossutil request-payment --method get oss://bucket
33 | 这个命令查询bucket的付费配置
34 | `,
35 | sampleText: `
36 | 1) 设置由bucket的访问者付费
37 | ossutil request-payment --method put oss://bucket Requester
38 |
39 | 2) 设置由bucket的拥有者付费
40 | ossutil request-payment --method put oss://bucket BucketOwner
41 |
42 | 3) 查询bucket的付费配置
43 | ossutil request-payment --method get oss://bucket
44 | `,
45 | }
46 |
47 | var specEnglishRequestPayment = SpecText{
48 | synopsisText: "Set, get bucket request payment configuration",
49 |
50 | paramText: "bucket_url [payment_parameter] [options]",
51 |
52 | syntaxText: `
53 | ossutil request-payment --method put oss://bucket payment_parameter
54 | ossutil request-payment --method get oss://bucket
55 | `,
56 | detailHelpText: `
57 | request-payment command can set, get the bucket request payment configuration by set method option value to put, get
58 | If the --method option value is put, the parameter can only be Requester, BucketOwner
59 | Usage:
60 | There are three usages for this command:
61 |
62 | 1) ossutil request-payment --method put oss://bucket Requester
63 | This command sets that request is paid by the requester of the bucket
64 |
65 | 2) ossutil request-payment --method put oss://bucket BucketOwner
66 | This command sets that request is paid by the owner of the bucket
67 |
68 | 3) ossutil request-payment --method get oss://bucket
69 | This command query the bucket request payment configuration
70 | `,
71 | sampleText: `
72 | 1) setting request is paid by the requester of the bucket
73 | ossutil request-payment --method put oss://bucket Requester
74 |
75 | 2) setting request is paid by the owner of the bucket
76 | ossutil request-payment --method put oss://bucket BucketOwner
77 |
78 | 3) query the bucket request payment configuration
79 | ossutil request-payment --method get oss://bucket
80 | `,
81 | }
82 |
83 | type RequestPaymentCommand struct {
84 | command Command
85 | bucketName string
86 | paymentResult oss.RequestPaymentConfiguration
87 | }
88 |
89 | var requestPaymentCommand = RequestPaymentCommand{
90 | command: Command{
91 | name: "request-payment",
92 | nameAlias: []string{"request-payment"},
93 | minArgc: 1,
94 | maxArgc: 2,
95 | specChinese: specChineseRequestPayment,
96 | specEnglish: specEnglishRequestPayment,
97 | group: GroupTypeNormalCommand,
98 | validOptionNames: []string{
99 | OptionConfigFile,
100 | OptionEndpoint,
101 | OptionAccessKeyID,
102 | OptionAccessKeySecret,
103 | OptionSTSToken,
104 | OptionProxyHost,
105 | OptionProxyUser,
106 | OptionProxyPwd,
107 | OptionMethod,
108 | OptionLogLevel,
109 | OptionPassword,
110 | OptionMode,
111 | OptionECSRoleName,
112 | OptionTokenTimeout,
113 | OptionRamRoleArn,
114 | OptionRoleSessionName,
115 | OptionReadTimeout,
116 | OptionConnectTimeout,
117 | OptionSTSRegion,
118 | OptionSkipVerifyCert,
119 | OptionUserAgent,
120 | OptionSignVersion,
121 | OptionRegion,
122 | OptionCloudBoxID,
123 | OptionForcePathStyle,
124 | },
125 | },
126 | }
127 |
128 | // function for FormatHelper interface
129 | func (reqpc *RequestPaymentCommand) formatHelpForWhole() string {
130 | return reqpc.command.formatHelpForWhole()
131 | }
132 |
133 | func (reqpc *RequestPaymentCommand) formatIndependHelp() string {
134 | return reqpc.command.formatIndependHelp()
135 | }
136 |
137 | // Init simulate inheritance, and polymorphism
138 | func (reqpc *RequestPaymentCommand) Init(args []string, options OptionMapType) error {
139 | return reqpc.command.Init(args, options, reqpc)
140 | }
141 |
142 | // RunCommand simulate inheritance, and polymorphism
143 | func (reqpc *RequestPaymentCommand) RunCommand() error {
144 | strMethod, _ := GetString(OptionMethod, reqpc.command.options)
145 | if strMethod == "" {
146 | return fmt.Errorf("--method value is empty")
147 | }
148 |
149 | strMethod = strings.ToLower(strMethod)
150 | if strMethod != "put" && strMethod != "get" {
151 | return fmt.Errorf("--method value is not in the optional value:put|get")
152 | }
153 |
154 | srcBucketUrL, err := GetCloudUrl(reqpc.command.args[0], "")
155 | if err != nil {
156 | return err
157 | }
158 |
159 | reqpc.bucketName = srcBucketUrL.bucket
160 |
161 | if strMethod == "put" {
162 | err = reqpc.PutRequestPayment()
163 | } else if strMethod == "get" {
164 | err = reqpc.GetRequestPayment()
165 | }
166 |
167 | return err
168 | }
169 |
170 | func (reqpc *RequestPaymentCommand) PutRequestPayment() error {
171 | if len(reqpc.command.args) < 2 {
172 | return fmt.Errorf("missing parameter,payment parameter is empty")
173 | }
174 |
175 | strPayment := strings.ToLower(reqpc.command.args[1])
176 |
177 | if strPayment != strings.ToLower(string(oss.Requester)) &&
178 | strPayment != strings.ToLower(string(oss.BucketOwner)) {
179 | return fmt.Errorf("payment parameter must be %s or %s", string(oss.Requester), string(oss.BucketOwner))
180 | }
181 |
182 | // put bucket payment
183 | client, err := reqpc.command.ossClient(reqpc.bucketName)
184 | if err != nil {
185 | return err
186 | }
187 |
188 | var paymentConfig oss.RequestPaymentConfiguration
189 | if strPayment == strings.ToLower(string(oss.Requester)) {
190 | paymentConfig.Payer = string(oss.Requester)
191 | } else if strPayment == strings.ToLower(string(oss.BucketOwner)) {
192 | paymentConfig.Payer = string(oss.BucketOwner)
193 |
194 | }
195 | return client.SetBucketRequestPayment(reqpc.bucketName, paymentConfig)
196 | }
197 |
198 | func (reqpc *RequestPaymentCommand) GetRequestPayment() error {
199 | client, err := reqpc.command.ossClient(reqpc.bucketName)
200 | if err != nil {
201 | return err
202 | }
203 |
204 | reqpc.paymentResult, err = client.GetBucketRequestPayment(reqpc.bucketName)
205 | if err != nil {
206 | return err
207 | }
208 |
209 | fmt.Printf("\n%s\n", string(reqpc.paymentResult.Payer))
210 |
211 | return nil
212 | }
213 |
--------------------------------------------------------------------------------
/lib/request_payment_test.go:
--------------------------------------------------------------------------------
1 | package lib
2 |
3 | import (
4 | "os"
5 | "time"
6 |
7 | . "gopkg.in/check.v1"
8 | )
9 |
10 | func (s *OssutilCommandSuite) TestRequestPaymentPutSuccess(c *C) {
11 | bucketName := bucketNamePrefix + randLowStr(12)
12 | s.putBucket(bucketName, c)
13 |
14 | // request command test
15 | var str string
16 | strMethod := "get"
17 | options := OptionMapType{
18 | "endpoint": &str,
19 | "accessKeyID": &str,
20 | "accessKeySecret": &str,
21 | "stsToken": &str,
22 | "configFile": &configFile,
23 | "method": &strMethod,
24 | }
25 |
26 | requestArgs := []string{CloudURLToString(bucketName, "")}
27 | _, err := cm.RunCommand("request-payment", requestArgs, options)
28 | c.Assert(err, IsNil)
29 | c.Assert(requestPaymentCommand.paymentResult.Payer, Equals, "BucketOwner")
30 |
31 | // set bucket request enabled
32 | strMethod = "put"
33 | requestArgs = []string{CloudURLToString(bucketName, ""), "Requester"}
34 | _, err = cm.RunCommand("request-payment", requestArgs, options)
35 | c.Assert(err, IsNil)
36 |
37 | time.Sleep(5 * time.Second)
38 |
39 | // check
40 | strMethod = "get"
41 | requestArgs = []string{CloudURLToString(bucketName, "")}
42 | _, err = cm.RunCommand("request-payment", requestArgs, options)
43 | c.Assert(err, IsNil)
44 | c.Assert(requestPaymentCommand.paymentResult.Payer, Equals, "Requester")
45 |
46 | // set bucket request suspend
47 | strMethod = "put"
48 | requestArgs = []string{CloudURLToString(bucketName, ""), "BucketOwner"}
49 | _, err = cm.RunCommand("request-payment", requestArgs, options)
50 | c.Assert(err, IsNil)
51 | time.Sleep(5 * time.Second)
52 |
53 | // check
54 | strMethod = "get"
55 | requestArgs = []string{CloudURLToString(bucketName, "")}
56 | _, err = cm.RunCommand("request-payment", requestArgs, options)
57 | c.Assert(err, IsNil)
58 | c.Assert(requestPaymentCommand.paymentResult.Payer, Equals, "BucketOwner")
59 |
60 | s.removeBucket(bucketName, true, c)
61 | }
62 |
63 | func (s *OssutilCommandSuite) TestRequestPaymentError(c *C) {
64 | bucketName := bucketNamePrefix + randLowStr(12)
65 | s.putBucket(bucketName, c)
66 |
67 | // request-payment command test
68 | var str string
69 | options := OptionMapType{
70 | "endpoint": &str,
71 | "accessKeyID": &str,
72 | "accessKeySecret": &str,
73 | "stsToken": &str,
74 | "configFile": &configFile,
75 | }
76 |
77 | // method is empty
78 | requestArgs := []string{CloudURLToString(bucketName, "")}
79 | _, err := cm.RunCommand("request-payment", requestArgs, options)
80 | c.Assert(err, NotNil)
81 |
82 | // method is error
83 | strMethod := "puttt"
84 | options["method"] = &strMethod
85 | _, err = cm.RunCommand("request-payment", requestArgs, options)
86 | c.Assert(err, NotNil)
87 |
88 | // args is empty
89 | strMethod = "put"
90 | requestArgs = []string{CloudURLToString(bucketName, "")}
91 | _, err = cm.RunCommand("request-payment", requestArgs, options)
92 | c.Assert(err, NotNil)
93 |
94 | //value is error
95 | requestArgs = []string{CloudURLToString(bucketName, ""), "Bucket-Owner"}
96 | _, err = cm.RunCommand("request-payment", requestArgs, options)
97 | c.Assert(err, NotNil)
98 |
99 | s.removeBucket(bucketName, true, c)
100 | }
101 |
102 | func (s *OssutilCommandSuite) TestRequestPaymentPutEmptyEndpoint(c *C) {
103 | bucketName := bucketNamePrefix + randLowStr(12)
104 | s.putBucket(bucketName, c)
105 |
106 | cfile := randStr(10)
107 | data := "[Credentials]" + "\n" + "language=CH" + "\n" + "accessKeyID=123" + "\n" + "accessKeySecret=456" + "\n" + "endpoint="
108 | s.createFile(cfile, data, c)
109 |
110 | // request-payment command test
111 | var str string
112 | strMethod := "put"
113 | options := OptionMapType{
114 | "endpoint": &str,
115 | "accessKeyID": &str,
116 | "accessKeySecret": &str,
117 | "stsToken": &str,
118 | "configFile": &cfile,
119 | "method": &strMethod,
120 | }
121 |
122 | requestArgs := []string{CloudURLToString(bucketName, ""), "Requester"}
123 | _, err := cm.RunCommand("request-payment", requestArgs, options)
124 | c.Assert(err, NotNil)
125 |
126 | os.Remove(cfile)
127 | s.removeBucket(bucketName, true, c)
128 | }
129 |
130 | func (s *OssutilCommandSuite) TestRequestPaymentGetEmptyEndpoint(c *C) {
131 | bucketName := bucketNamePrefix + randLowStr(12)
132 | s.putBucket(bucketName, c)
133 |
134 | cfile := randStr(10)
135 | data := "[Credentials]" + "\n" + "language=CH" + "\n" + "accessKeyID=123" + "\n" + "accessKeySecret=456" + "\n" + "endpoint="
136 | s.createFile(cfile, data, c)
137 |
138 | var str string
139 | strMethod := "get"
140 | options := OptionMapType{
141 | "endpoint": &str,
142 | "accessKeyID": &str,
143 | "accessKeySecret": &str,
144 | "stsToken": &str,
145 | "configFile": &cfile,
146 | "method": &strMethod,
147 | }
148 |
149 | versioingArgs := []string{CloudURLToString(bucketName, "")}
150 | _, err := cm.RunCommand("request-payment", versioingArgs, options)
151 | c.Assert(err, NotNil)
152 |
153 | os.Remove(cfile)
154 | s.removeBucket(bucketName, true, c)
155 | }
156 |
157 | func (s *OssutilCommandSuite) TestRequestPaymentHelpInfo(c *C) {
158 | options := OptionMapType{}
159 |
160 | mkArgs := []string{"request-payment"}
161 | _, err := cm.RunCommand("help", mkArgs, options)
162 | c.Assert(err, IsNil)
163 |
164 | mkArgs = []string{}
165 | _, err = cm.RunCommand("help", mkArgs, options)
166 | c.Assert(err, IsNil)
167 |
168 | }
169 |
--------------------------------------------------------------------------------
/lib/storage_url.go:
--------------------------------------------------------------------------------
1 | package lib
2 |
3 | import (
4 | "fmt"
5 | "net/url"
6 | "os"
7 | "strings"
8 | )
9 |
10 | // SchemePrefix is the prefix of oss url
11 | const SchemePrefix string = "oss://"
12 |
13 | type CloudURLType string
14 |
15 | const (
16 | CloudURLNone CloudURLType = "none"
17 | CloudURLService CloudURLType = "service"
18 | CloudURLBucket CloudURLType = "bucket"
19 | CloudURLObject CloudURLType = "object"
20 | )
21 |
22 | // StorageURLer is the interface for all url
23 | type StorageURLer interface {
24 | IsCloudURL() bool
25 | IsFileURL() bool
26 | ToString() string
27 | }
28 |
29 | // CloudURL describes oss url
30 | type CloudURL struct {
31 | urlStr string
32 | bucket string
33 | object string
34 | }
35 |
36 | // Init is used to create a cloud url from a user input url
37 | func (cu *CloudURL) Init(urlStr, encodingType string) error {
38 | cu.urlStr = urlStr
39 | if err := cu.parseBucketObject(encodingType); err != nil {
40 | return err
41 | }
42 | if err := cu.checkBucketObject(encodingType); err != nil {
43 | return err
44 | }
45 | return nil
46 | }
47 |
48 | func (cu *CloudURL) parseBucketObject(encodingType string) error {
49 | var err error
50 | path := cu.urlStr
51 |
52 | if strings.HasPrefix(strings.ToLower(path), SchemePrefix) {
53 | path = string(path[len(SchemePrefix):])
54 | } else {
55 | // deal with the url: /bucket/object
56 | if strings.HasPrefix(path, "/") {
57 | path = string(path[1:])
58 | }
59 | }
60 |
61 | sli := strings.SplitN(path, "/", 2)
62 | cu.bucket = sli[0]
63 | if len(sli) > 1 {
64 | cu.object = sli[1]
65 | if encodingType == URLEncodingType {
66 | if cu.object, err = url.QueryUnescape(cu.object); err != nil {
67 | return fmt.Errorf("invalid cloud url: %s, object name is not url encoded, %s", cu.urlStr, err.Error())
68 | }
69 | }
70 | }
71 | return nil
72 | }
73 |
74 | func (cu *CloudURL) checkBucketObject(encodingType string) error {
75 | if cu.bucket == "" && cu.object != "" {
76 | return fmt.Errorf("invalid cloud url: %s, miss bucket", cu.urlStr)
77 | }
78 | if encodingType == URLEncodingType && cu.bucket != "" && cu.object == "" {
79 | if bucket, err := url.QueryUnescape(cu.bucket); err == nil && bucket != cu.bucket {
80 | return fmt.Errorf("invalid cloud url: %s, bucket url do not support --encoding-type option", cu.urlStr)
81 | }
82 | }
83 | return nil
84 | }
85 |
86 | func (cu *CloudURL) checkObjectPrefix() error {
87 | if strings.HasPrefix(cu.object, "/") {
88 | return fmt.Errorf("invalid cloud url: %s, object name should not begin with \"/\"", cu.urlStr)
89 | }
90 | if strings.HasPrefix(cu.object, "\\") {
91 | return fmt.Errorf("invalid cloud url: %s, object name should not begin with \"\\\"", cu.urlStr)
92 | }
93 | return nil
94 | }
95 |
96 | func (cu *CloudURL) checkIsObjectURL() error {
97 | if cu.bucket == "" {
98 | return fmt.Errorf("invalid cloud url: %s, miss bucket", cu.urlStr)
99 | }
100 | if cu.object == "" {
101 | return fmt.Errorf("invalid cloud url: %s, miss object", cu.urlStr)
102 | }
103 | return nil
104 | }
105 |
106 | // IsCloudURL shows if the url is a cloud url
107 | func (cu CloudURL) IsCloudURL() bool {
108 | return true
109 | }
110 |
111 | // IsFileURL shows if the url is a file url
112 | func (cu CloudURL) IsFileURL() bool {
113 | return false
114 | }
115 |
116 | // ToString reconstruct url
117 | func (cu CloudURL) ToString() string {
118 | if cu.object == "" {
119 | return fmt.Sprintf("%s%s", SchemePrefix, cu.bucket)
120 | }
121 | return fmt.Sprintf("%s%s/%s", SchemePrefix, cu.bucket, cu.object)
122 | }
123 |
124 | // FileURL describes file url
125 | type FileURL struct {
126 | urlStr string
127 | }
128 |
129 | // Init simulate inheritance, and polymorphism
130 | func (fu *FileURL) Init(urlStr, encodingType string) error {
131 | if encodingType == URLEncodingType {
132 | vurl, err := url.QueryUnescape(urlStr)
133 | if err != nil {
134 | return fmt.Errorf("invalid cloud url: %s, file name is not url encoded, %s", urlStr, err.Error())
135 | }
136 | urlStr = vurl
137 | }
138 |
139 | if len(urlStr) >= 2 && urlStr[:2] == "~"+string(os.PathSeparator) {
140 | homeDir := currentHomeDir()
141 | if homeDir != "" {
142 | urlStr = strings.Replace(urlStr, "~", homeDir, 1)
143 | } else {
144 | return fmt.Errorf("current home dir is empty")
145 | }
146 | }
147 | fu.urlStr = urlStr
148 | return nil
149 | }
150 |
151 | // IsCloudURL simulate inheritance, and polymorphism
152 | func (fu FileURL) IsCloudURL() bool {
153 | return false
154 | }
155 |
156 | // IsFileURL simulate inheritance, and polymorphism
157 | func (fu FileURL) IsFileURL() bool {
158 | return true
159 | }
160 |
161 | // ToString simulate inheritance, and polymorphism
162 | func (fu FileURL) ToString() string {
163 | return fu.urlStr
164 | }
165 |
166 | // StorageURLFromString analysis input url type and build a storage url from the url
167 | func StorageURLFromString(urlStr, encodingType string) (StorageURLer, error) {
168 | if strings.HasPrefix(strings.ToLower(urlStr), SchemePrefix) {
169 | var cloudURL CloudURL
170 | if err := cloudURL.Init(urlStr, encodingType); err != nil {
171 | return nil, err
172 | }
173 | return cloudURL, nil
174 | }
175 | var fileURL FileURL
176 | if err := fileURL.Init(urlStr, encodingType); err != nil {
177 | return nil, err
178 | }
179 | return fileURL, nil
180 | }
181 |
182 | // CloudURLFromString get a oss url from url, if url is not a cloud url, return error
183 | func CloudURLFromString(urlStr, encodingType string) (CloudURL, error) {
184 | storageURL, err := StorageURLFromString(urlStr, encodingType)
185 | if err != nil {
186 | return CloudURL{}, err
187 | }
188 | if !storageURL.IsCloudURL() {
189 | return CloudURL{}, fmt.Errorf("invalid cloud url: \"%s\", please make sure the url starts with: \"%s\"", urlStr, SchemePrefix)
190 | }
191 | return storageURL.(CloudURL), nil
192 | }
193 |
194 | // ObjectURLFromString get a oss url from url, if url is not a cloud url, return error
195 | func ObjectURLFromString(urlStr, encodingType string) (CloudURL, error) {
196 | cloudURL, err := CloudURLFromString(urlStr, encodingType)
197 | if err != nil {
198 | return cloudURL, err
199 | }
200 | return cloudURL, cloudURL.checkIsObjectURL()
201 | }
202 |
203 | // CloudURLToString format url string from input
204 | func CloudURLToString(bucket string, object string) string {
205 | cloudURL := CloudURL{
206 | bucket: bucket,
207 | object: object,
208 | }
209 | return cloudURL.ToString()
210 | }
211 |
--------------------------------------------------------------------------------
/lib/update_test.go:
--------------------------------------------------------------------------------
1 | package lib
2 |
3 | import (
4 | "io/ioutil"
5 | "os"
6 | "os/exec"
7 | "runtime"
8 | "strconv"
9 | "strings"
10 |
11 | . "gopkg.in/check.v1"
12 | )
13 |
14 | func (s *OssutilCommandSuite) rawUpdate(force bool, language string) (bool, error) {
15 | command := "update"
16 | var args []string
17 | options := OptionMapType{
18 | OptionForce: &force,
19 | OptionLanguage: &language,
20 | }
21 | showElapse, err := cm.RunCommand(command, args, options)
22 | return showElapse, err
23 | }
24 |
25 | func (s *OssutilCommandSuite) TestUpdate(c *C) {
26 | showElapse, err := s.rawUpdate(false, "ch")
27 | c.Assert(err, IsNil)
28 | c.Assert(showElapse, Equals, false)
29 |
30 | showElapse, err = s.rawUpdate(false, "En")
31 | c.Assert(err, IsNil)
32 | c.Assert(showElapse, Equals, false)
33 |
34 | cmdline := []string{"ossutil", "update"}
35 | os.Args = cmdline
36 |
37 | showElapse, err = s.rawUpdate(true, "ch")
38 |
39 | showElapse, err = s.rawUpdate(true, "En")
40 |
41 | err = updateCommand.updateVersion(Version, "ch")
42 |
43 | err = updateCommand.updateVersion("1.0.0.Beta", "ch")
44 |
45 | fileName := "ossutil_test_not_exist"
46 | err = updateCommand.rewriteLoadConfig(fileName)
47 | c.Assert(err, IsNil)
48 | }
49 |
50 | func (s *OssutilCommandSuite) TestUpdateDiffVersion(c *C) {
51 | // error get lastest version
52 | ue := vUpdateBucket
53 | vUpdateBucket = "abc"
54 | version, err := updateCommand.getLastestVersion()
55 | c.Assert(err, NotNil)
56 |
57 | vUpdateBucket = ue
58 | version, err = updateCommand.getLastestVersion()
59 | c.Assert(err, IsNil)
60 | vVersion = version
61 |
62 | cmdline := []string{"ossutil", "update"}
63 | os.Args = cmdline
64 |
65 | err = updateCommand.RunCommand()
66 | c.Assert(err, IsNil)
67 | vVersion = version + "123"
68 | updateCommand.RunCommand()
69 | vVersion = Version
70 | }
71 |
72 | func (s *OssutilCommandSuite) TestRevertRename(c *C) {
73 | filePath := ".ossutil_tempf" + randStr(5)
74 | renameFilePath := ".ossutil_tempr" + randStr(5)
75 |
76 | s.createFile(filePath, filePath+"i", c)
77 | s.createFile(renameFilePath, renameFilePath+"i", c)
78 |
79 | updateCommand.revertRename(filePath, renameFilePath)
80 | _, err := os.Stat(renameFilePath)
81 | c.Assert(err, NotNil)
82 |
83 | str := s.readFile(filePath, c)
84 | c.Assert(str, Equals, renameFilePath+"i")
85 |
86 | os.Remove(filePath)
87 | os.Remove(renameFilePath)
88 |
89 | renameFilePath = ".ossutil_notexist"
90 | err = updateCommand.revertRename(filePath, renameFilePath)
91 | c.Assert(err, NotNil)
92 | }
93 |
94 | func (s *OssutilCommandSuite) TestDownloadLastestBinary(c *C) {
95 | tempBinaryFile := ".ossutil_test_update.temp"
96 | err := updateCommand.getBinary(tempBinaryFile, "1.0.0.Beta")
97 | c.Assert(err, IsNil)
98 |
99 | os.Remove(tempBinaryFile)
100 | }
101 |
102 | func (s *OssutilCommandSuite) TestAnonymousGetToFileError(c *C) {
103 | bucket := bucketNameNotExist
104 | object := "TestAnonymousGetToFileError"
105 | err := updateCommand.anonymousGetToFileRetry(bucket, object, object)
106 | c.Assert(err, NotNil)
107 |
108 | bucket = bucketNameDest
109 | s.putObject(bucket, object, uploadFileName, c)
110 | fileName := "*"
111 | err = updateCommand.anonymousGetToFileRetry(bucket, object, fileName)
112 | c.Assert(err, NotNil)
113 | }
114 |
115 | func (s *OssutilCommandSuite) TestUpdateSuccess(c *C) {
116 | nowVersion, err := updateCommand.getLastestVersion()
117 | c.Assert(err, IsNil)
118 |
119 | // get a version below current
120 | pSlice := strings.Split(nowVersion, ".")
121 | for index := len(pSlice) - 1; index >= 0; index-- {
122 | if pSlice[index] > "0" {
123 | b, err := strconv.Atoi(pSlice[index])
124 | c.Assert(err, IsNil)
125 | pSlice[index] = strconv.Itoa(b - 1)
126 | break
127 | }
128 | }
129 |
130 | lowVersion := ""
131 | for k, v := range pSlice {
132 | if k == len(pSlice)-1 {
133 | lowVersion = lowVersion + v
134 | } else {
135 | lowVersion = lowVersion + v + "."
136 | }
137 | }
138 |
139 | // set path enviroment
140 | oldPathValue := os.Getenv("PATH")
141 | currentDiretory, _ := os.Getwd()
142 | if runtime.GOOS == "windows" {
143 | os.Setenv("PATH", currentDiretory+";"+oldPathValue)
144 | } else {
145 | os.Setenv("PATH", currentDiretory+":"+oldPathValue)
146 | }
147 |
148 | // binaryName file must be exist
149 | binaryName := updateCommand.getBinaryName()
150 | ioutil.WriteFile(binaryName, []byte("test-binary"), 0744)
151 |
152 | cmdline := []string{binaryName, "update", "-f"}
153 | os.Args = cmdline
154 | err = updateCommand.updateVersion(lowVersion, "ch")
155 | c.Assert(err, IsNil)
156 | os.Remove(binaryName)
157 | os.Remove(".temp_" + binaryName)
158 | }
159 |
160 | func (s *OssutilCommandSuite) TestUpdateWithLinuxArm(c *C) {
161 | nowVersion, err := updateCommand.getLastestVersion()
162 | c.Assert(err, IsNil)
163 |
164 | // get a version below current
165 | pSlice := strings.Split(nowVersion, ".")
166 | for index := len(pSlice) - 1; index >= 0; index-- {
167 | if pSlice[index] > "0" {
168 | b, err := strconv.Atoi(pSlice[index])
169 | c.Assert(err, IsNil)
170 | pSlice[index] = strconv.Itoa(b - 1)
171 | break
172 | }
173 | }
174 |
175 | lowVersion := ""
176 | for k, v := range pSlice {
177 | if k == len(pSlice)-1 {
178 | lowVersion = lowVersion + v
179 | } else {
180 | lowVersion = lowVersion + v + "."
181 | }
182 | }
183 |
184 | // set path enviroment
185 | oldPathValue := os.Getenv("PATH")
186 | currentDiretory, _ := os.Getwd()
187 | if runtime.GOOS == "windows" {
188 | os.Setenv("PATH", currentDiretory+";"+oldPathValue)
189 | } else {
190 | os.Setenv("PATH", currentDiretory+":"+oldPathValue)
191 | }
192 |
193 | // binaryName file must be exist
194 | binaryName := updateCommand.getBinaryName()
195 | testLogger.Print("ossutil name:" + binaryName)
196 | ioutil.WriteFile(binaryName, []byte("test-binary"), 0744)
197 |
198 | cmdline := []string{binaryName, "update", "-f"}
199 | os.Args = cmdline
200 | err = updateCommand.updateVersion(lowVersion, "ch")
201 | c.Assert(err, IsNil)
202 |
203 | cmd := exec.Command(binaryName, "-h")
204 | cmdOut, err := cmd.CombinedOutput()
205 | c.Assert(err, IsNil)
206 |
207 | testLogger.Print(string(cmdOut))
208 |
209 | cmd = exec.Command("go", "version")
210 | cmdOut, err = cmd.CombinedOutput()
211 | c.Assert(err, IsNil)
212 | if strings.Contains(string(cmdOut), "arm64") {
213 | c.Assert(binaryName, Equals, updateBinaryLinuxArm64)
214 | }
215 |
216 | if strings.Contains(string(cmdOut), "arm") && !strings.Contains(string(cmdOut), "arm64") {
217 | c.Assert(binaryName, Equals, updateBinaryLinuxArm32)
218 | }
219 |
220 | cmdline = []string{binaryName, "update", "-f"}
221 | os.Args = cmdline
222 | err = updateCommand.updateVersion(nowVersion, "ch")
223 | c.Assert(err, IsNil)
224 | if strings.Contains(string(cmdOut), "arm64") {
225 | c.Assert(binaryName, Equals, updateBinaryLinuxArm64)
226 | }
227 |
228 | if strings.Contains(string(cmdOut), "arm") && !strings.Contains(string(cmdOut), "arm64") {
229 | c.Assert(binaryName, Equals, updateBinaryLinuxArm32)
230 | }
231 |
232 | cmd = exec.Command(binaryName, "configggg")
233 | cmdOut, err = cmd.CombinedOutput()
234 | c.Assert(err, NotNil)
235 |
236 | testLogger.Print(string(cmdOut))
237 |
238 | cmd = exec.Command(binaryName, "-v")
239 | cmdOut, err = cmd.CombinedOutput()
240 | c.Assert(err, IsNil)
241 |
242 | testLogger.Print(string(cmdOut))
243 |
244 | os.Remove(binaryName)
245 | os.Remove(".temp_" + binaryName)
246 | }
247 |
--------------------------------------------------------------------------------
/lib/user_qos.go:
--------------------------------------------------------------------------------
1 | package lib
2 |
3 | import (
4 | "encoding/xml"
5 | "fmt"
6 | "os"
7 | "strings"
8 | )
9 |
10 | var specChineseUserQos = SpecText{
11 | synopsisText: "查询用户的qos配置",
12 |
13 | paramText: "[local_file] [options]",
14 |
15 | syntaxText: `
16 | ossutil user-qos --method get [local_file] [options]
17 | `,
18 | detailHelpText: `
19 | user-qos命令通过设置method选项值为get可以查询用户的qos配置
20 |
21 | 用法:
22 | 该命令只有一种用法:
23 | 1) ossutil user-qos --method get [local_xml_file] [options]
24 | 这个命令查询用户的qos配置,如果输入参数local_xml_file,qos配置将输出到该文件,否则输出到屏幕上
25 | `,
26 | sampleText: `
27 | 1) 查询用户的qos配置,结果输出到标准输出
28 | ossutil user-qos --method get
29 |
30 | 2) 查询用户的qos配置,结果输出到本地文件
31 | ossutil user-qos --method get local_xml_file
32 | `,
33 | }
34 |
35 | var specEnglishUserQos = SpecText{
36 | synopsisText: "Get user's qos configuration",
37 |
38 | paramText: "[local_file] [options]",
39 |
40 | syntaxText: `
41 | ossutil user-qos --method get [local_xml_file] [options]
42 | `,
43 | detailHelpText: `
44 | user-qos command can get the user's qos configuration by set method option value to get
45 |
46 | Usage:
47 | There are only one usage for this command:
48 |
49 | 1) ossutil user-qos --method get [local_xml_file] [options]
50 | The command gets the user's qos configuration
51 | If you input parameter local_xml_file,the configuration will be output to local_xml_file
52 | If you don't input parameter local_xml_file,the configuration will be output to stdout
53 | `,
54 | sampleText: `
55 | 1) get user's qos configuration to stdout
56 | ossutil user-qos --method get
57 |
58 | 2) get user's qos configuration to local file
59 | ossutil user-qos --method get local_xml_file
60 | `,
61 | }
62 |
63 | type UserQosCommand struct {
64 | command Command
65 | }
66 |
67 | var userQosCommand = UserQosCommand{
68 | command: Command{
69 | name: "user-qos",
70 | nameAlias: []string{"user-qos"},
71 | minArgc: 0,
72 | maxArgc: 1,
73 | specChinese: specChineseUserQos,
74 | specEnglish: specEnglishUserQos,
75 | group: GroupTypeNormalCommand,
76 | validOptionNames: []string{
77 | OptionConfigFile,
78 | OptionEndpoint,
79 | OptionAccessKeyID,
80 | OptionAccessKeySecret,
81 | OptionSTSToken,
82 | OptionProxyHost,
83 | OptionProxyUser,
84 | OptionProxyPwd,
85 | OptionLogLevel,
86 | OptionMethod,
87 | OptionPassword,
88 | OptionMode,
89 | OptionECSRoleName,
90 | OptionTokenTimeout,
91 | OptionRamRoleArn,
92 | OptionRoleSessionName,
93 | OptionReadTimeout,
94 | OptionConnectTimeout,
95 | OptionSTSRegion,
96 | OptionSkipVerifyCert,
97 | OptionUserAgent,
98 | OptionSignVersion,
99 | OptionRegion,
100 | OptionCloudBoxID,
101 | OptionForcePathStyle,
102 | },
103 | },
104 | }
105 |
106 | // function for FormatHelper interface
107 | func (uqc *UserQosCommand) formatHelpForWhole() string {
108 | return uqc.command.formatHelpForWhole()
109 | }
110 |
111 | func (uqc *UserQosCommand) formatIndependHelp() string {
112 | return uqc.command.formatIndependHelp()
113 | }
114 |
115 | // Init simulate inheritance, and polymorphism
116 | func (uqc *UserQosCommand) Init(args []string, options OptionMapType) error {
117 | return uqc.command.Init(args, options, uqc)
118 | }
119 |
120 | // RunCommand simulate inheritance, and polymorphism
121 | func (uqc *UserQosCommand) RunCommand() error {
122 | strMethod, _ := GetString(OptionMethod, uqc.command.options)
123 | if strMethod == "" {
124 | return fmt.Errorf("--method value is empty")
125 | }
126 |
127 | strMethod = strings.ToLower(strMethod)
128 | if strMethod != "get" {
129 | return fmt.Errorf("--method value must be get")
130 | }
131 |
132 | return uqc.GetUserQos()
133 | }
134 |
135 | func (uqc *UserQosCommand) confirm(str string) bool {
136 | var val string
137 | fmt.Printf(getClearStr(fmt.Sprintf("user qos: overwrite \"%s\"(y or N)? ", str)))
138 | if _, err := fmt.Scanln(&val); err != nil || (strings.ToLower(val) != "yes" && strings.ToLower(val) != "y") {
139 | return false
140 | }
141 | return true
142 | }
143 |
144 | func (uqc *UserQosCommand) GetUserQos() error {
145 | client, err := uqc.command.ossClient("")
146 | if err != nil {
147 | return err
148 | }
149 |
150 | qosRes, err := client.GetUserQoSInfo()
151 | if err != nil {
152 | return err
153 | }
154 |
155 | output, err := xml.MarshalIndent(qosRes, " ", " ")
156 | if err != nil {
157 | return err
158 | }
159 |
160 | var outFile *os.File
161 | if len(uqc.command.args) >= 1 {
162 | fileName := uqc.command.args[0]
163 | _, err = os.Stat(fileName)
164 | if err == nil {
165 | bConitnue := uqc.confirm(fileName)
166 | if !bConitnue {
167 | return nil
168 | }
169 | }
170 |
171 | outFile, err = os.OpenFile(fileName, os.O_CREATE|os.O_TRUNC|os.O_RDWR, 0660)
172 | if err != nil {
173 | return err
174 | }
175 | defer outFile.Close()
176 | } else {
177 | outFile = os.Stdout
178 | }
179 |
180 | outFile.Write([]byte(xml.Header))
181 | outFile.Write(output)
182 |
183 | fmt.Printf("\n\n")
184 |
185 | return nil
186 | }
187 |
--------------------------------------------------------------------------------
/lib/user_qos_test.go:
--------------------------------------------------------------------------------
1 | package lib
2 |
3 | import (
4 | "os"
5 |
6 | . "gopkg.in/check.v1"
7 | )
8 |
9 | func (s *OssutilCommandSuite) TestUserQosGetError(c *C) {
10 | bucketName := bucketNamePrefix + randLowStr(12)
11 | s.putBucket(bucketName, c)
12 |
13 | // command test
14 | var str string
15 | strMethod := ""
16 | options := OptionMapType{
17 | "endpoint": &str,
18 | "accessKeyID": &str,
19 | "accessKeySecret": &str,
20 | "stsToken": &str,
21 | "configFile": &configFile,
22 | "method": &strMethod,
23 | }
24 |
25 | // method is empty
26 | qosArgs := []string{CloudURLToString(bucketName, "")}
27 | _, err := cm.RunCommand("user-qos", qosArgs, options)
28 | c.Assert(err, NotNil)
29 |
30 | // method is error
31 | strMethod = "gett"
32 | _, err = cm.RunCommand("user-qos", qosArgs, options)
33 | c.Assert(err, NotNil)
34 |
35 | s.removeBucket(bucketName, true, c)
36 | }
37 |
38 | func (s *OssutilCommandSuite) TestUserQosOptionsEmptyEndpoint(c *C) {
39 | bucketName := bucketNamePrefix + randLowStr(12)
40 | s.putBucket(bucketName, c)
41 |
42 | cfile := randStr(10)
43 | data := "[Credentials]" + "\n" + "language=CH" + "\n" + "accessKeyID=123" + "\n" + "accessKeySecret=456" + "\n" + "endpoint="
44 | s.createFile(cfile, data, c)
45 |
46 | var str string
47 | strMethod := "get"
48 | options := OptionMapType{
49 | "endpoint": &str,
50 | "accessKeyID": &str,
51 | "accessKeySecret": &str,
52 | "stsToken": &str,
53 | "configFile": &cfile,
54 | "method": &strMethod,
55 | }
56 |
57 | qosArgs := []string{}
58 | _, err := cm.RunCommand("user-qos", qosArgs, options)
59 | c.Assert(err, NotNil)
60 |
61 | os.Remove(cfile)
62 | s.removeBucket(bucketName, true, c)
63 | }
64 |
65 | func (s *OssutilCommandSuite) TestUserQosGetConfirm(c *C) {
66 | bucketName := bucketNamePrefix + randLowStr(12)
67 | s.putBucket(bucketName, c)
68 |
69 | // user-qos command test
70 | var str string
71 | strMethod := "put"
72 | options := OptionMapType{
73 | "endpoint": &str,
74 | "accessKeyID": &str,
75 | "accessKeySecret": &str,
76 | "stsToken": &str,
77 | "configFile": &configFile,
78 | "method": &strMethod,
79 | }
80 |
81 | qosArgs := []string{}
82 | _, err := cm.RunCommand("user-qos", qosArgs, options)
83 | c.Assert(err, NotNil)
84 |
85 | // get qos
86 | qosDownName := "ossutil-test-file-" + randLowStr(12) + "-down"
87 | strMethod = "get"
88 | options = OptionMapType{
89 | "endpoint": &str,
90 | "accessKeyID": &str,
91 | "accessKeySecret": &str,
92 | "stsToken": &str,
93 | "configFile": &configFile,
94 | "method": &strMethod,
95 | }
96 |
97 | qosArgs = []string{}
98 | _, err = cm.RunCommand("user-qos", qosArgs, options)
99 | c.Assert(err, IsNil)
100 |
101 | qosArgs = []string{qosDownName}
102 | _, err = cm.RunCommand("user-qos", qosArgs, options)
103 | c.Assert(err, IsNil)
104 |
105 | qosArgs = []string{qosDownName}
106 | _, err = cm.RunCommand("user-qos", qosArgs, options)
107 | c.Assert(err, IsNil)
108 |
109 | os.Remove(qosDownName)
110 | s.removeBucket(bucketName, true, c)
111 | }
112 |
113 | func (s *OssutilCommandSuite) TestUserQosHelpInfo(c *C) {
114 | // mkdir command test
115 | options := OptionMapType{}
116 |
117 | mkArgs := []string{"user-qos"}
118 | _, err := cm.RunCommand("help", mkArgs, options)
119 | c.Assert(err, IsNil)
120 |
121 | mkArgs = []string{}
122 | _, err = cm.RunCommand("help", mkArgs, options)
123 | c.Assert(err, IsNil)
124 | }
125 |
--------------------------------------------------------------------------------
/lib/util_sts.go:
--------------------------------------------------------------------------------
1 | package lib
2 |
3 | import (
4 | "crypto/hmac"
5 | "crypto/sha1"
6 | "crypto/tls"
7 | "encoding/base64"
8 | "encoding/json"
9 | "fmt"
10 | "io/ioutil"
11 | "net/http"
12 | "net/url"
13 | "strconv"
14 | "strings"
15 | "time"
16 | )
17 |
18 | // Client sts client
19 | type Client struct {
20 | AccessKeyId string
21 | AccessKeySecret string
22 | RoleArn string
23 | SessionName string
24 | }
25 |
26 | // ServiceError sts service error
27 | type ServiceError struct {
28 | Code string
29 | Message string
30 | RequestId string
31 | HostId string
32 | RawMessage string
33 | StatusCode int
34 | }
35 |
36 | // Credentials the credentials obtained by AssumedRole,
37 | // used for the peration of Alibaba Cloud service.
38 | type Credentials struct {
39 | AccessKeyId string
40 | AccessKeySecret string
41 | Expiration time.Time
42 | SecurityToken string
43 | }
44 |
45 | // AssumedRoleUser the user to AssumedRole
46 | type AssumedRoleUser struct {
47 | Arn string
48 | AssumedRoleId string
49 | }
50 |
51 | // Response the response of AssumeRole
52 | type Response struct {
53 | Credentials Credentials
54 | AssumedRoleUser AssumedRoleUser
55 | RequestId string
56 | }
57 |
58 | // Error implement interface error
59 | func (e *ServiceError) Error() string {
60 | return fmt.Sprintf("oss: service returned error: StatusCode=%d, ErrorCode=%s, ErrorMessage=%s, RequestId=%s",
61 | e.StatusCode, e.Code, e.Message, e.RequestId)
62 | }
63 |
64 | // NewClient New STS Client
65 | func NewClient(accessKeyId, accessKeySecret, roleArn, sessionName string) *Client {
66 | return &Client{
67 | AccessKeyId: accessKeyId,
68 | AccessKeySecret: accessKeySecret,
69 | RoleArn: roleArn,
70 | SessionName: sessionName,
71 | }
72 | }
73 |
74 | const (
75 | // StsSignVersion sts sign version
76 | StsSignVersion = "1.0"
77 | // StsAPIVersion sts api version
78 | StsAPIVersion = "2015-04-01"
79 |
80 | // // StsHost sts host
81 | // StsHost = "https://sts.aliyuncs.com/"
82 |
83 | // TimeFormat time fomrat
84 | TimeFormat = "2006-01-02T15:04:05Z"
85 | // RespBodyFormat respone body format
86 | RespBodyFormat = "JSON"
87 | // PercentEncode '/'
88 | PercentEncode = "%2F"
89 | // HTTPGet http get method
90 | HTTPGet = "GET"
91 | )
92 |
93 | // StsHost sts host
94 | var StsHost = "https://sts.aliyuncs.com/"
95 |
96 | // AssumeRole assume role
97 | func (c *Client) AssumeRole(tokenTimeout uint, stsEndPoint string) (*Response, error) {
98 | if stsEndPoint != "" {
99 | StsHost = stsEndPoint
100 | }
101 |
102 | url, err := c.generateSignedURL(tokenTimeout)
103 | if err != nil {
104 | return nil, err
105 | }
106 |
107 | body, status, err := c.sendRequest(url)
108 | if err != nil {
109 | return nil, err
110 | }
111 |
112 | return c.handleResponse(body, status)
113 | }
114 |
115 | // Private function
116 | func (c *Client) generateSignedURL(expiredTime uint) (string, error) {
117 | randId := strings.ToUpper(randStr(24))
118 |
119 | queryStr := "SignatureVersion=" + StsSignVersion
120 | queryStr += "&Format=" + RespBodyFormat
121 | queryStr += "&Timestamp=" + url.QueryEscape(time.Now().UTC().Format(TimeFormat))
122 | queryStr += "&RoleArn=" + url.QueryEscape(c.RoleArn)
123 | queryStr += "&RoleSessionName=" + c.SessionName
124 | queryStr += "&AccessKeyId=" + c.AccessKeyId
125 | queryStr += "&SignatureMethod=HMAC-SHA1"
126 | queryStr += "&Version=" + StsAPIVersion
127 | queryStr += "&Action=AssumeRole"
128 | queryStr += "&SignatureNonce=" + randId
129 | queryStr += "&DurationSeconds=" + strconv.FormatUint((uint64)(expiredTime), 10)
130 |
131 | // Sort query string
132 | queryParams, err := url.ParseQuery(queryStr)
133 | if err != nil {
134 | return "", err
135 | }
136 | result := queryParams.Encode()
137 |
138 | strToSign := HTTPGet + "&" + PercentEncode + "&" + url.QueryEscape(result)
139 |
140 | // Generate signature
141 | hashSign := hmac.New(sha1.New, []byte(c.AccessKeySecret+"&"))
142 | hashSign.Write([]byte(strToSign))
143 | signature := base64.StdEncoding.EncodeToString(hashSign.Sum(nil))
144 |
145 | // Build url
146 | assumeURL := StsHost + "?" + queryStr + "&Signature=" + url.QueryEscape(signature)
147 |
148 | return assumeURL, nil
149 | }
150 |
151 | func (c *Client) sendRequest(url string) ([]byte, int, error) {
152 | tr := &http.Transport{
153 | TLSClientConfig: &tls.Config{},
154 | }
155 | client := &http.Client{Transport: tr}
156 |
157 | resp, err := client.Get(url)
158 | if err != nil {
159 | return nil, -1, err
160 | }
161 | defer resp.Body.Close()
162 |
163 | body, err := ioutil.ReadAll(resp.Body)
164 | return body, resp.StatusCode, err
165 | }
166 |
167 | func (c *Client) handleResponse(responseBody []byte, statusCode int) (*Response, error) {
168 | if statusCode != http.StatusOK {
169 | se := ServiceError{StatusCode: statusCode, RawMessage: string(responseBody)}
170 | err := json.Unmarshal(responseBody, &se)
171 | if err != nil {
172 | return nil, err
173 | }
174 | return nil, &se
175 | }
176 |
177 | resp := Response{}
178 | err := json.Unmarshal(responseBody, &resp)
179 | if err != nil {
180 | return nil, err
181 | }
182 | return &resp, nil
183 | }
184 |
--------------------------------------------------------------------------------
/lib/util_sts_test.go:
--------------------------------------------------------------------------------
1 | package lib
2 |
3 | import (
4 | "fmt"
5 | "log"
6 | "os"
7 | "time"
8 |
9 | . "gopkg.in/check.v1"
10 | )
11 |
12 | type StsTestSuite struct {
13 | }
14 |
15 | var _ = Suite(&StsTestSuite{})
16 |
17 | // Run once when the suite starts running
18 | func (s *StsTestSuite) SetUpSuite(c *C) {
19 | stsAccessID = os.Getenv("OSS_TEST_STS_ID")
20 | stsAccessKeySecret = os.Getenv("OSS_TEST_STS_KEY")
21 | stsARN = os.Getenv("OSS_TEST_STS_ARN")
22 | }
23 |
24 | // Run after each test or benchmark starts running
25 | func (s *StsTestSuite) TearDownSuite(c *C) {
26 | }
27 |
28 | func (s *StsTestSuite) TestSendRequest(c *C) {
29 | client := NewClient("", "", "", "")
30 | _, _, err := client.sendRequest(StsHost)
31 | c.Assert(err, IsNil)
32 |
33 | // negative
34 | _, _, err = client.sendRequest("https//x.y.z.com")
35 | c.Assert(err, NotNil)
36 | }
37 |
38 | func (s *StsTestSuite) TestHandleResponse(c *C) {
39 | client := NewClient("", "", "", "")
40 |
41 | body := "{\"RequestId\":\"784B99C1-895F-426C-8E1F-008955D418FB\"," +
42 | "\"HostId\":\"sts.aliyuncs.com\"," +
43 | "\"Code\":\"NoPermission\"," +
44 | "\"Message\":\"Roles may not be assumed by root accounts.\"}"
45 | resp, err := client.handleResponse([]byte(body), 400)
46 | _, isSuc := err.(*ServiceError)
47 | c.Assert(isSuc, Equals, true)
48 | c.Assert(resp, IsNil)
49 |
50 | body = "{{}}"
51 | resp, err = client.handleResponse([]byte(body), 400)
52 | _, isSuc = err.(*ServiceError)
53 | c.Assert(isSuc, Equals, false)
54 | c.Assert(resp, IsNil)
55 |
56 | body = "{\"RequestId\":\"4AB89022-25A3-4427-84A5-4C7E72BD63BE\"}"
57 | resp, err = client.handleResponse([]byte(body), 200)
58 | c.Assert(err, IsNil)
59 | c.Assert(resp, NotNil)
60 |
61 | body = "{{}}"
62 | resp, err = client.handleResponse([]byte(body), 200)
63 | _, isSuc = err.(*ServiceError)
64 | c.Assert(isSuc, Equals, false)
65 | c.Assert(resp, IsNil)
66 | }
67 |
68 | func (s *StsTestSuite) TestAssumeRoleSuccess(c *C) {
69 | now := time.Now()
70 | client := NewClient(stsAccessID, stsAccessKeySecret, stsARN, "sts_test")
71 |
72 | resp, err := client.AssumeRole(900, "")
73 | if err != nil {
74 | fmt.Println(err)
75 | } else {
76 | fmt.Println("success!")
77 | }
78 | c.Assert(err, IsNil)
79 |
80 | c.Assert(resp.RequestId, Not(Equals), "")
81 |
82 | c.Assert(resp.AssumedRoleUser.Arn, Not(Equals), "")
83 | c.Assert(resp.AssumedRoleUser.AssumedRoleId, Not(Equals), "")
84 |
85 | c.Assert(resp.Credentials.AccessKeyId, Not(Equals), "")
86 | c.Assert(resp.Credentials.AccessKeySecret, Not(Equals), "")
87 | c.Assert(resp.Credentials.SecurityToken, Not(Equals), "")
88 | c.Assert(resp.Credentials.Expiration.After(now), Equals, true)
89 | }
90 |
91 | func (s *StsTestSuite) TestAssumeRoleNegative(c *C) {
92 | // AccessKeyID invalid
93 | client := NewClient("", accessKeySecret, stsARN, "sts_test")
94 | resp, err := client.AssumeRole(900, "")
95 | c.Assert(resp, IsNil)
96 | c.Assert(err, NotNil)
97 | log.Println("Error:", err)
98 |
99 | srvErr, isSuc := err.(*ServiceError)
100 | c.Assert(isSuc, Equals, true)
101 | c.Assert(srvErr.StatusCode, Equals, 400)
102 | log.Println("ServiceError:", srvErr)
103 |
104 | // AccessKeySecret invalid
105 | client = NewClient(stsAccessID, stsAccessKeySecret+" ", stsARN, "sts_test")
106 | resp, err = client.AssumeRole(900, "")
107 | c.Assert(resp, IsNil)
108 | c.Assert(err, NotNil)
109 |
110 | srvErr, isSuc = err.(*ServiceError)
111 | c.Assert(isSuc, Equals, true)
112 | c.Assert(srvErr.StatusCode, Equals, 400)
113 | c.Assert(srvErr.Code, Equals, "SignatureDoesNotMatch")
114 | log.Println("ServiceError:", srvErr)
115 |
116 | // SessionName invalid
117 | client = NewClient(stsAccessID, stsAccessKeySecret, stsARN, "x")
118 |
119 | resp, err = client.AssumeRole(900, "")
120 | c.Assert(resp, IsNil)
121 | c.Assert(err, NotNil)
122 |
123 | srvErr, isSuc = err.(*ServiceError)
124 | c.Assert(isSuc, Equals, true)
125 | c.Assert(srvErr.StatusCode, Equals, 400)
126 | c.Assert(srvErr.Code, Equals, "InvalidParameter.RoleSessionName")
127 | log.Println("ServiceError:", srvErr)
128 | }
129 |
--------------------------------------------------------------------------------
/ossutil.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | "os"
6 | "strings"
7 |
8 | "github.com/aliyun/ossutil/lib"
9 | )
10 |
11 | func main() {
12 | if err := lib.ParseAndRunCommand(); err != nil {
13 | fmt.Printf("Error: %s\n", err)
14 | if strings.Contains(err.Error(), "ErrorCode=NoSuchUpload") {
15 | fmt.Printf("Will remove checkpoint dir '%s' automatically. Please try again.\n", lib.CheckpointDir)
16 | os.RemoveAll(lib.CheckpointDir)
17 | }
18 | if strings.Contains(err.Error(), ": EOF,") {
19 | fmt.Printf("Connection has been closed by remote peer. Please check the network. If you download/upload large file, You can reduce concurrency with the --parallel option and reduce part-size with --part-size (it must greater than the file size divided by 10000. By default, it will retry 10 times when failed, you can increse the retry times with --retry-times option.).\n")
20 | }
21 | os.Exit(1)
22 | }
23 | os.Exit(0)
24 | }
25 |
--------------------------------------------------------------------------------
/scripts/build_all.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | rootdir=$(cd `dirname $0`; cd ..; pwd)
3 | output=$rootdir/dist
4 | version=`cat ./lib/const.go | grep -E "Version.+ string =" | cut -d"=" -f2 | xargs`
5 |
6 | #output folder
7 | mkdir -p $output
8 |
9 | #ossutil-$version-mac-amd64.zip & ossutil-mac-amd64.zip, files: ossutil,ossutilmac64
10 | echo "start build ossutil for darwin on amd64"
11 | cd $rootdir
12 | env GOOS=darwin GOARCH=amd64 go build -o $output/ossutilmac64
13 |
14 | cd $output
15 | mkdir -p ossutil-$version-mac-amd64
16 | cp -f ossutilmac64 ossutil-$version-mac-amd64/ossutil
17 | cp -f ossutilmac64 ossutil-$version-mac-amd64/ossutilmac64
18 | zip -r ossutil-$version-mac-amd64.zip ossutil-$version-mac-amd64
19 | mv -f ossutil-$version-mac-amd64 ossutil-mac-amd64
20 | zip -r ossutil-mac-amd64.zip ossutil-mac-amd64
21 | rm -rf ossutil-mac-amd64
22 | echo "ossutil for darwin on amd64 built successfully"
23 |
24 | #ossutil-$version-mac-arm64.zip & ossutil-mac-arm64.zip, files: ossutil,ossutilmac64
25 | echo "start build ossutil for darwin on arm64"
26 | cd $rootdir
27 | env CGO_ENABLED=0 GOOS=darwin GOARCH=arm64 go build -o $output/ossutilmacarm64
28 |
29 | cd $output
30 | mkdir -p ossutil-$version-mac-arm64
31 | cp -f ossutilmacarm64 ossutil-$version-mac-arm64/ossutil
32 | cp -f ossutilmacarm64 ossutil-$version-mac-arm64/ossutilmac64
33 | zip -r ossutil-$version-mac-arm64.zip ossutil-$version-mac-arm64
34 | mv -f ossutil-$version-mac-arm64 ossutil-mac-arm64
35 | zip -r ossutil-mac-arm64.zip ossutil-mac-arm64
36 | rm -rf ossutil-mac-arm64
37 |
38 | echo "ossutil for darwin on arm64 built successfully"
39 |
40 | #ossutil-$version-windows-386 & ossutil-windows-386, files: ossutil.bat,ossutil.exe,ossutil32.exe
41 | echo "start build ossutil for windows on 386"
42 | cd $rootdir
43 | env GOOS=windows GOARCH=386 go build -o $output/ossutil32.exe
44 |
45 | cd $output
46 | mkdir -p ossutil-$version-windows-386
47 | cp -f ossutil32.exe ossutil-$version-windows-386/ossutil.exe
48 | cp -f ossutil32.exe ossutil-$version-windows-386/ossutil32.exe
49 | cp -f $rootdir/scripts/ossutil.bat ossutil-$version-windows-386/ossutil.bat
50 | zip -r ossutil-$version-windows-386.zip ossutil-$version-windows-386
51 | mv -f ossutil-$version-windows-386 ossutil32
52 | rm -f ossutil32/ossutil.exe
53 | zip -r ossutil32.zip ossutil32
54 | rm -rf ossutil32
55 |
56 | echo "ossutil for windows on 386 built successfully"
57 |
58 |
59 | #ossutil-$version-windows-amd64 & ossutil-windows-amd64, files: ossutil.bat,ossutil.exe,ossutil64.exe
60 | echo "start build ossutil for windows on amd64"
61 | cd $rootdir
62 | env GOOS=windows GOARCH=amd64 go build -o $output/ossutil64.exe
63 |
64 | cd $output
65 | mkdir -p ossutil-$version-windows-amd64
66 | cp -f ossutil64.exe ossutil-$version-windows-amd64/ossutil.exe
67 | cp -f ossutil64.exe ossutil-$version-windows-amd64/ossutil64.exe
68 | cp -f $rootdir/scripts/ossutil.bat ossutil-$version-windows-amd64/ossutil.bat
69 | zip -r ossutil-$version-windows-amd64.zip ossutil-$version-windows-amd64
70 | mv ossutil-$version-windows-amd64 ossutil64
71 | rm -f ossutil64/ossutil.exe
72 | zip -r ossutil64.zip ossutil64
73 | rm -rf ossutil64
74 |
75 | echo "ossutil for windows on amd64 built successfully"
76 |
77 | #ossutil-$version-linux-386 & ossutil-linux-386, files: ossutil,ossutil32
78 | echo "start build ossutil for linux on 386"
79 | cd $rootdir
80 | env CGO_ENABLED=0 GOOS=linux GOARCH=386 go build -o $output/ossutil32
81 |
82 | cd $output
83 | mkdir -p ossutil-$version-linux-386
84 | cp -f ossutil32 ossutil-$version-linux-386/ossutil
85 | cp -f ossutil32 ossutil-$version-linux-386/ossutil32
86 | zip -r ossutil-$version-linux-386.zip ossutil-$version-linux-386
87 | mv -f ossutil-$version-linux-386 ossutil-linux-386
88 | zip -r ossutil-linux-386.zip ossutil-linux-386
89 | rm -rf ossutil-linux-386
90 |
91 | echo "ossutil for linux on 386 built successfully"
92 |
93 | #ossutil-$version-linux-amd64 & ossutil-linux-amd64, files: ossutil,ossutil64
94 | echo "start build ossutil for linux on amd64"
95 | cd $rootdir
96 | env CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o $output/ossutil64
97 |
98 | cd $output
99 | mkdir -p ossutil-$version-linux-amd64
100 | cp -f ossutil64 ossutil-$version-linux-amd64/ossutil
101 | cp -f ossutil64 ossutil-$version-linux-amd64/ossutil64
102 | zip -r ossutil-$version-linux-amd64.zip ossutil-$version-linux-amd64
103 | mv -f ossutil-$version-linux-amd64 ossutil-linux-amd64
104 | zip -r ossutil-linux-amd64.zip ossutil-linux-amd64
105 | rm -rf ossutil-linux-amd64
106 |
107 | echo "ossutil for linux on amd64 built successfully"
108 |
109 | #ossutil-$version-linux-arm & ossutil-linux-arm, files: ossutil,ossutil32
110 | echo "start build ossutil for linux on arm"
111 | cd $rootdir
112 | env CGO_ENABLED=0 GOOS=linux GOARCH=arm go build -o $output/ossutilarm32
113 |
114 | cd $output
115 | mkdir -p ossutil-$version-linux-arm
116 | cp -f ossutilarm32 ossutil-$version-linux-arm/ossutil
117 | cp -f ossutilarm32 ossutil-$version-linux-arm/ossutil32
118 | zip -r ossutil-$version-linux-arm.zip ossutil-$version-linux-arm
119 | mv -f ossutil-$version-linux-arm ossutil-linux-arm
120 | zip -r ossutil-linux-arm.zip ossutil-linux-arm
121 | rm -rf ossutil-linux-arm
122 |
123 | echo "ossutil for linux on arm built successfully"
124 |
125 | #ossutil-$version-linux-arm64 & ossutil-linux-arm64, files: ossutil,ossutil64
126 | echo "start build ossutil for linux on arm64"
127 | cd $rootdir
128 | env CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build -o $output/ossutilarm64
129 |
130 | cd $output
131 | mkdir -p ossutil-$version-linux-arm64
132 | cp -f ossutilarm64 ossutil-$version-linux-arm64/ossutil
133 | cp -f ossutilarm64 ossutil-$version-linux-arm64/ossutil64
134 | zip -r ossutil-$version-linux-arm64.zip ossutil-$version-linux-arm64
135 | mv -f ossutil-$version-linux-arm64 ossutil-linux-arm64
136 | zip -r ossutil-linux-arm64.zip ossutil-linux-arm64
137 | rm -rf ossutil-linux-arm64
138 |
139 | echo "ossutil for linux on arm64 built successfully"
140 |
141 | #calc hash for zip files
142 | cd $output
143 | for file in $(ls *.zip); do
144 | sha256sum $file >> sha256sum.log
145 | done
--------------------------------------------------------------------------------
/scripts/ossutil.bat:
--------------------------------------------------------------------------------
1 | @ECHO OFF
2 | cmd /k cd /d .
3 | set PATH=.;%PATH%
4 |
--------------------------------------------------------------------------------