baidubce-sdk 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,25 @@
1
+ # Copyright 2017 Baidu, Inc.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
4
+ # except in compliance with the License. You may obtain a copy of the License at
5
+ #
6
+ # http://www.apache.org/licenses/LICENSE-2.0
7
+ #
8
+ # Unless required by applicable law or agreed to in writing, software distributed under the
9
+ # License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
10
+ # either express or implied. See the License for the specific language governing permissions
11
+ # and limitations under the License.
12
+
13
+ # This module provide constants for BOS services.
14
+
15
+ module Baidubce
16
+ module Services
17
+
18
+ MAX_PUT_OBJECT_LENGTH = 5 * 1024 * 1024 * 1024
19
+ MAX_APPEND_OBJECT_LENGTH = 5 * 1024 * 1024 * 1024
20
+ MAX_USER_METADATA_SIZE = 2 * 1024
21
+ MIN_PART_NUMBER = 1
22
+ MAX_PART_NUMBER = 10000
23
+
24
+ end
25
+ end
@@ -0,0 +1,38 @@
1
+ # Copyright (c) 2014 Baidu.com, Inc. All Rights Reserved
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
4
+ # except in compliance with the License. You may obtain a copy of the License at
5
+ #
6
+ # http://www.apache.org/licenses/LICENSE-2.0
7
+ #
8
+ # Unless required by applicable law or agreed to in writing, software distributed under the
9
+ # License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
10
+ # either express or implied. See the License for the specific language governing permissions
11
+ # and limitations under the License.
12
+
13
+ # This module provides a client for STS.
14
+
15
+ require_relative '../../bce_base_client'
16
+
17
+ module Baidubce
18
+ module Services
19
+
20
+ class StsClient < BceBaseClient
21
+
22
+ STS_URL_PREFIX = "/"
23
+ GET_SESSION_TOKEN_VERSION = "v1"
24
+ GET_SESSION_TOKEN_PATH = "sessionToken"
25
+
26
+ def get_session_token(acl, duration_seconds=nil)
27
+ params = duration_seconds.nil? ? {} : { durationSeconds: duration_seconds }
28
+ headers = { CONTENT_TYPE => JSON_TYPE }
29
+ body = acl.to_json
30
+ path = Utils.append_uri(STS_URL_PREFIX, GET_SESSION_TOKEN_VERSION, GET_SESSION_TOKEN_PATH)
31
+ body, headers = @http_client.send_request(@config, @signer, POST, path, params, headers, body)
32
+ Utils.generate_response(headers, body, false)
33
+ end
34
+
35
+ end
36
+ end
37
+ end
38
+
@@ -0,0 +1,51 @@
1
+ # Copyright 2017 Baidu, Inc.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
4
+ # except in compliance with the License. You may obtain a copy of the License at
5
+ #
6
+ # http://www.apache.org/licenses/LICENSE-2.0
7
+ #
8
+ # Unless required by applicable law or agreed to in writing, software distributed under the
9
+ # License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
10
+ # either express or implied. See the License for the specific language governing permissions
11
+ # and limitations under the License.
12
+
13
+ # This module provide logger utils for bce client.
14
+
15
+ require 'logger'
16
+
17
+ module Baidubce
18
+
19
+ module Log
20
+ DEFAULT_LOG_FILE = "./baidubce-sdk.log"
21
+ MAX_NUM_LOG = 100
22
+ LOG_FILE_SIZE = 10 * 1024 * 1024
23
+
24
+ def logger
25
+ Log.logger
26
+ end
27
+
28
+ # level : Logger::DEBUG | Logger::INFO | Logger::ERROR | Logger::FATAL
29
+ def self.set_log_level(level)
30
+ Log.logger.level = level
31
+ end
32
+
33
+ def self.set_log_file(file)
34
+ @log_file = file
35
+ end
36
+
37
+ private
38
+
39
+ def self.logger
40
+ unless @logger
41
+ @logger = Logger.new(
42
+ @log_file ||= DEFAULT_LOG_FILE, MAX_NUM_LOG, LOG_FILE_SIZE)
43
+ @logger.level = Logger::INFO
44
+ end
45
+ @logger
46
+ end
47
+
48
+ end
49
+
50
+ end
51
+
@@ -0,0 +1,124 @@
1
+ # Copyright 2017 Baidu, Inc.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
4
+ # except in compliance with the License. You may obtain a copy of the License at
5
+ #
6
+ # http://www.apache.org/licenses/LICENSE-2.0
7
+ #
8
+ # Unless required by applicable law or agreed to in writing, software distributed under the
9
+ # License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
10
+ # either express or implied. See the License for the specific language governing permissions
11
+ # and limitations under the License.
12
+
13
+ # This module provide some utils for bce client.
14
+
15
+ require "erb"
16
+ require "uri"
17
+ require 'json'
18
+ require 'digest/md5'
19
+
20
+ require_relative "../http/http_constants"
21
+
22
+ module Baidubce
23
+
24
+ class Utils
25
+
26
+ # parse protocol, host, port from endpoint in config.
27
+ def self.parse_url_host(config)
28
+ endpoint = config.endpoint
29
+ unless endpoint.include?"://"
30
+ protocol = config.protocol.downcase
31
+ raise "Invalid protocol #{protocol}." if protocol != "http" && protocol != 'https'
32
+ endpoint = sprintf("%s://%s", protocol, endpoint)
33
+ end
34
+ parsed_endpoint = URI.parse(endpoint)
35
+ scheme = parsed_endpoint.scheme.downcase
36
+ raise "Invalid endpoint #{endpoint}, unsupported scheme #{scheme}." if scheme != "http" && protocol != 'https'
37
+ host = parsed_endpoint.host
38
+ port = parsed_endpoint.port
39
+ host += ":#{port}" unless scheme == 'http' && port == 80 || scheme == 'https' && port == 443
40
+ return "#{scheme}://#{host}", host
41
+ end
42
+
43
+ # Append path_components to the end of base_uri in order.
44
+ def self.append_uri(base_uri, *path_components)
45
+ uri = [base_uri]
46
+ path_components.reject(&:empty?)
47
+ path_components.each { |path| uri << path }
48
+
49
+ unless uri.empty?
50
+ uri[0].gsub!(/([\/]*$)/, '')
51
+ uri[-1].gsub!(/(^[\/]*)/, '')
52
+ uri.each { |u| u.gsub!(/(^[\/]*)|([\/]*$)/, '') }
53
+ end
54
+
55
+ uri.join("/")
56
+ end
57
+
58
+ def self.url_encode_except_slash(path)
59
+ encoded_path = ERB::Util.url_encode(path)
60
+ encoded_path.gsub('%2F', '/')
61
+ end
62
+
63
+ def self.get_canonical_querystring(params, for_signature)
64
+ return '' if params.nil? || params.empty?
65
+
66
+ arr = []
67
+ params.each do |key, value|
68
+ if !for_signature || key.downcase != Http::AUTHORIZATION.downcase
69
+ value = '' if value.nil?
70
+ str = ERB::Util.url_encode(key) + "=" + ERB::Util.url_encode(value)
71
+ arr << str
72
+ end
73
+ end
74
+ arr.sort!
75
+ arr.join("&")
76
+ end
77
+
78
+ def self.get_md5_from_file(file_name, content_length, buf_size=8192)
79
+
80
+ md5 = Digest::MD5.new
81
+ left_size = content_length
82
+ File.open(file_name, 'rb') do |io|
83
+ bytes_to_read = left_size > buf_size ? buf_size : left_size
84
+ until left_size <= 0
85
+ md5.update(io.read(bytes_to_read))
86
+ left_size -= bytes_to_read
87
+ end
88
+ end
89
+ md5.base64digest
90
+ end
91
+
92
+ def self.generate_response(headers, body, return_body)
93
+ return body if return_body
94
+ return generate_headers(headers) if body.empty?
95
+ ret = JSON.parse(body)
96
+ return ret
97
+ rescue JSON::ParserError
98
+ return body
99
+ end
100
+
101
+ def self.generate_headers(headers)
102
+ user_metadata = {}
103
+
104
+ resp_headers = headers.inject({}) do |ret, (k, v)|
105
+ key = k.to_s.tr('_', '-')
106
+ if key.start_with?(Http::BCE_USER_METADATA_PREFIX)
107
+ key.slice!(Http::BCE_USER_METADATA_PREFIX)
108
+ user_metadata[key] = v
109
+ elsif key.downcase == Http::ETAG.downcase
110
+ ret[key] = v.delete('\"')
111
+ elsif key.downcase == Http::CONTENT_LENGTH.downcase
112
+ ret[key] = v.to_i
113
+ else
114
+ ret[key] = v
115
+ end
116
+ ret
117
+ end
118
+ resp_headers['user-metadata'] = user_metadata unless user_metadata.empty?
119
+ resp_headers
120
+ end
121
+
122
+ end
123
+
124
+ end
@@ -0,0 +1,7 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ module Baidubce
4
+
5
+ VERSION = "0.9.0"
6
+
7
+ end
@@ -0,0 +1,376 @@
1
+ # Copyright 2017 Baidu, Inc.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
4
+ # the License. You may obtain a copy of the License at
5
+ #
6
+ # http://www.apache.org/licenses/LICENSE-2.0
7
+ #
8
+ # Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
9
+ # an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
10
+ # specific language governing permissions and limitations under the License.
11
+
12
+ # Samples for bos client.
13
+
14
+ $LOAD_PATH.unshift(File.expand_path("../../../lib", __FILE__))
15
+
16
+ require 'baidubce/services/bos/bos_client'
17
+
18
+ include Baidubce
19
+
20
+ # debug
21
+ credentials = Auth::BceCredentials.new(
22
+ # "your ak",
23
+ # "your sk"
24
+ )
25
+
26
+ conf = BceClientConfiguration.new(
27
+ credentials,
28
+ "http://bj.bcebos.com"
29
+ )
30
+
31
+ client = Services::BosClient.new(conf)
32
+
33
+ bucket_name = "ruby-test-bucket"
34
+
35
+ # log config
36
+ Log.set_log_file("./test.log")
37
+ Log.set_log_level(Logger::DEBUG)
38
+
39
+ def demo(msg)
40
+ puts "--------- #{msg} --------"
41
+ puts
42
+ yield
43
+ puts "----------- end --------------"
44
+ puts
45
+ end
46
+
47
+ demo "list buckets" do
48
+ puts client.list_buckets()
49
+ end
50
+
51
+ demo "delete bucket" do
52
+ # Only can delete the bucket you are owner and it is empty.
53
+ # puts client.delete_bucket("test-bucket") if client.does_bucket_exist("test-bucket")
54
+ end
55
+
56
+ demo "create bucket" do
57
+ puts client.create_bucket(bucket_name) unless client.does_bucket_exist(bucket_name)
58
+ end
59
+
60
+ demo "get bucket location" do
61
+ puts client.get_bucket_location(bucket_name)
62
+ end
63
+
64
+ demo "set/get bucket acl" do
65
+ client.set_bucket_canned_acl(bucket_name, "private")
66
+ puts "before set bucket acl"
67
+ puts client.get_bucket_acl(bucket_name)
68
+
69
+ acl = [{'grantee' => [{'id' => 'b124deeaf6f641c9ac27700b41a350a8'},
70
+ {'id' => 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'}],
71
+ 'permission' => ['FULL_CONTROL'],
72
+ 'condition' => {
73
+ 'referer' => {
74
+ 'stringLike' => ['http://www.abc.com/*'],
75
+ 'stringEquals' => ['http://www.abc.com']
76
+ },
77
+ "ipAddress" => [
78
+ '192.168.0.0/16',
79
+ '192.169.0.*',
80
+ '192.170.0.5'
81
+ ]
82
+ }
83
+ }]
84
+ client.set_bucket_acl(bucket_name, acl)
85
+ puts "after set bucket acl"
86
+ puts client.get_bucket_acl(bucket_name)
87
+ end
88
+
89
+ demo "put/get bucket lifecycle" do
90
+ puts "before put bucket lifecycle"
91
+ lifecycle = [
92
+ {
93
+ id: "rule-id",
94
+ status: "enabled",
95
+ resource: [
96
+ "ruby-test-bucket/prefix/*"
97
+ ],
98
+ condition: {
99
+ time: {
100
+ dateGreaterThan: "2016-09-07T00:00:00Z"
101
+ }
102
+ },
103
+ action: {
104
+ name: "DeleteObject"
105
+ }
106
+ }
107
+ ]
108
+
109
+ client.put_bucket_lifecycle(bucket_name, lifecycle)
110
+ puts "after put bucket lifecycle"
111
+ puts client.get_bucket_lifecycle(bucket_name)
112
+
113
+ puts "after delete bucket lifecycle"
114
+ puts client.delete_bucket_lifecycle(bucket_name)
115
+ end
116
+
117
+ demo "put/get bucket cors" do
118
+ puts "before put bucket cors"
119
+ cors = [
120
+ {
121
+ allowedOrigins: [
122
+ "http://www.example.com",
123
+ "www.example2.com"
124
+ ],
125
+ allowedMethods: [
126
+ "GET",
127
+ "HEAD",
128
+ "DELETE"
129
+ ],
130
+ allowedHeaders: [
131
+ "Authorization",
132
+ "x-bce-test",
133
+ "x-bce-test2"
134
+ ],
135
+ allowedExposeHeaders: [
136
+ "user-custom-expose-header"
137
+ ],
138
+ maxAgeSeconds: 3600
139
+ }
140
+ ]
141
+
142
+ client.put_bucket_cors(bucket_name, cors)
143
+ puts "after put bucket cors"
144
+ puts client.get_bucket_cors(bucket_name)
145
+
146
+ puts "after delete bucket cors"
147
+ puts client.delete_bucket_cors(bucket_name)
148
+ end
149
+
150
+ demo "put/get bucket logging" do
151
+
152
+ client.put_bucket_logging(bucket_name, bucket_name, "prefix")
153
+ puts "after put bucket logging"
154
+ puts client.get_bucket_logging(bucket_name)
155
+
156
+ puts "after delete bucket logging"
157
+ puts client.delete_bucket_logging(bucket_name)
158
+ end
159
+
160
+ demo "put/get bucket storage_class" do
161
+ puts "before put bucket storage_class"
162
+ puts client.get_bucket_storageclass(bucket_name)
163
+
164
+ client.put_bucket_storageclass(bucket_name, "STANDARD_IA")
165
+ puts "after put bucket storage_class"
166
+ puts client.get_bucket_storageclass(bucket_name)
167
+
168
+ client.put_bucket_storageclass(bucket_name, "STANDARD")
169
+ end
170
+
171
+ demo "put object" do
172
+
173
+ user_metadata = { "key1" => "value1" }
174
+ options = { Http::CONTENT_TYPE => 'string',
175
+ "key1" => "value1",
176
+ 'Content-Disposition' => 'inline',
177
+ 'user-metadata' => user_metadata
178
+ }
179
+
180
+ client.put_object_from_string(bucket_name, "obj.txt", "obj", options)
181
+ puts client.get_object_as_string(bucket_name, "obj.txt")
182
+ puts client.get_object_to_file(bucket_name, "obj.txt", "obj_file.txt")
183
+
184
+ # put cold storage class object
185
+ file_path = "obj.txt"
186
+ client.put_object_from_file(bucket_name, "obj_cold.txt", file_path, 'x-bce-storage-class' => 'COLD')
187
+ puts client.get_object_as_string(bucket_name, "obj_cold.txt")
188
+
189
+ # put object with content_length
190
+ file_path = "obj.txt"
191
+ client.put_object_from_file(bucket_name, "obj_with_content_length.txt", file_path, 'Content-Length' => 2)
192
+ puts client.get_object_as_string(bucket_name, "obj_with_content_length.txt")
193
+
194
+ end
195
+
196
+ demo "list objects" do
197
+
198
+ options = { prefix: 'obj',
199
+ maxKeys: 10,
200
+ delimiter: '',
201
+ marker: ''
202
+ }
203
+ puts client.list_objects(bucket_name, options)
204
+ end
205
+
206
+ demo "get object" do
207
+ puts client.put_object_from_string(bucket_name, "obj.txt", "object%123456")
208
+ puts client.get_object_as_string(bucket_name, "obj.txt", [0,2])
209
+ puts client.get_object_meta_data(bucket_name, "obj.txt")
210
+ end
211
+
212
+ demo "append object" do
213
+ puts client.append_object_from_string(bucket_name, "append.txt", "append")
214
+ puts client.get_object_as_string(bucket_name, "append.txt")
215
+ puts client.append_object_from_string(bucket_name, "append.txt", "append", 'offset' => 6)
216
+ puts client.get_object_as_string(bucket_name, "append.txt")
217
+ end
218
+
219
+ demo "delete object" do
220
+ object_key = "delete_obj.txt"
221
+ puts client.put_object_from_string(bucket_name, object_key, "object%123456")
222
+ puts client.get_object_as_string(bucket_name, object_key)
223
+ puts client.delete_object(bucket_name, object_key)
224
+ end
225
+
226
+ demo "delete multiple objects" do
227
+ object_list = ["multi_obj0.txt", "multi_obj1.txt","multi_obj2.txt"]
228
+ object_list.each { |key| client.put_object_from_string(bucket_name, key, "content") }
229
+ object_list << "other.txt"
230
+ puts client.delete_multiple_objects(bucket_name, object_list)
231
+ end
232
+
233
+ demo "generate pre signed url" do
234
+
235
+ options = { 'expiration_in_seconds' => 360,
236
+ 'timestamp' => Time.now.to_i,
237
+ 'headers_to_sign' => ["host", "content-md5", "content-length"]
238
+ }
239
+
240
+ puts client.generate_pre_signed_url(bucket_name, 'obj.txt', options)
241
+ end
242
+
243
+ demo "copy object" do
244
+ user_metadata = { "key1" => "value1" }
245
+
246
+ options = { Http::CONTENT_TYPE => 'string',
247
+ Http::CONTENT_MD5 => 'kkkkkkkk',
248
+ 'user-metadata' => user_metadata
249
+ }
250
+ client.copy_object(bucket_name, "obj.txt", bucket_name, 'obj2.txt', options)
251
+ puts client.get_object_meta_data(bucket_name, "obj2.txt")
252
+ end
253
+
254
+ demo "set/get/delete object acl" do
255
+ # puts "before set object acl"
256
+ key = "obj.txt"
257
+ # client.get_object_acl(bucket_name, key)
258
+ acl = [{'grantee' => [{'id' => 'b124deeaf6f641c9ac27700b41a350a8'},
259
+ {'id' => 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'}],
260
+ 'permission' => ['FULL_CONTROL']}]
261
+
262
+ puts "after set object canned x-bce-acl: private"
263
+ client.set_object_canned_acl(bucket_name, key, 'x-bce-acl' => 'private')
264
+ puts client.get_object_acl(bucket_name, key)
265
+
266
+ puts "after set object canned x-bce-grant-read"
267
+ id_permission = "id=\"6c47a952db4444c5a097b41be3f24c94\",id=\"8c47a952db4444c5a097b41be3f24c94\",id=\"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\""
268
+ client.set_object_canned_acl(bucket_name, key, 'x-bce-grant-read' => id_permission)
269
+ puts client.get_object_acl(bucket_name, key)
270
+
271
+ puts "after set object body acl"
272
+ client.set_object_acl(bucket_name, key, acl)
273
+ puts client.get_object_acl(bucket_name, key)
274
+ client.delete_object_acl(bucket_name, key)
275
+ end
276
+
277
+ # create a 18MB file for multi upload
278
+ multi_file = "multi_upload.txt"
279
+
280
+ demo "multi-upload" do
281
+ # step 1: init multi-upload
282
+ key = "multi_file"
283
+ upload_id = client.initiate_multipart_upload(bucket_name, key)["uploadId"]
284
+ # step 2: upload file part by part
285
+ left_size = File.open(multi_file, "r").size()
286
+ offset = 0
287
+ part_number = 1
288
+ part_list = []
289
+
290
+ while left_size > 0 do
291
+ part_size = 5 * 1024 * 1024
292
+ if left_size < part_size
293
+ part_size = left_size
294
+ end
295
+
296
+ puts "offset: #{offset}, part_number: #{part_number}, part_list: #{part_list}, left_size: #{left_size}, part_size: #{part_size}"
297
+ response = client.upload_part_from_file(
298
+ bucket_name, key, upload_id, part_number, part_size, multi_file, offset)
299
+ left_size -= part_size
300
+ offset += part_size
301
+ # your should store every part number and etag to invoke complete multi-upload
302
+ part_list << {
303
+ "partNumber" => part_number,
304
+ "eTag" => response['etag']
305
+ }
306
+ part_number += 1
307
+ end
308
+
309
+ # list parts
310
+ puts "------------------ list parts ---------------"
311
+ puts client.list_parts(bucket_name, key, upload_id)
312
+
313
+ # SuperFile step 3: complete multi-upload
314
+ user_metadata = { "key1" => "value1" }
315
+ options = {
316
+ 'user-metadata' => user_metadata
317
+ }
318
+
319
+ client.complete_multipart_upload(bucket_name, key, upload_id, part_list, options)
320
+
321
+ end
322
+
323
+ demo "multi-copy" do
324
+ # step 1: init multi-upload
325
+ key = "multi_file"
326
+ upload_id = client.initiate_multipart_upload(bucket_name, key+"_copy")["uploadId"]
327
+ # step 2: copy a object part by part
328
+ left_size = client.get_object_meta_data(bucket_name, key)['content-length']
329
+ offset = 0
330
+ part_number = 1
331
+ part_list = []
332
+
333
+ while left_size > 0 do
334
+ part_size = 5 * 1024 * 1024
335
+ if left_size < part_size
336
+ part_size = left_size
337
+ end
338
+
339
+ puts "offset: #{offset}, part_number: #{part_number}, part_list: #{part_list}, left_size: #{left_size}, part_size: #{part_size}"
340
+ response = client.upload_part_copy(
341
+ bucket_name, key, bucket_name, key+"_copy", upload_id, part_number, part_size, offset)
342
+ puts response
343
+ left_size -= part_size
344
+ offset += part_size
345
+ # your should store every part number and etag to invoke complete multi-upload
346
+ part_list << {
347
+ "partNumber" => part_number,
348
+ "eTag" => response["eTag"]
349
+ }
350
+ part_number += 1
351
+ end
352
+
353
+ # list parts
354
+ puts "------------------ list parts ---------------"
355
+ puts client.list_parts(bucket_name, key+"_copy", upload_id)
356
+
357
+ # SuperFile step 3: complete multi-upload
358
+
359
+ user_metadata = { "key1" => "value1" }
360
+ options = {
361
+ 'user-metadata' => user_metadata
362
+ }
363
+ client.complete_multipart_upload(bucket_name, key+"_copy", upload_id, part_list, options)
364
+ end
365
+
366
+ demo "abort-multi-upload" do
367
+ key = "multi_file"
368
+ upload_id_abort = client.initiate_multipart_upload(bucket_name, key + "_abort")["uploadId"]
369
+
370
+ # list multi-uploads
371
+ puts "------------------ list multi-uploads ---------------"
372
+ puts client.list_multipart_uploads(bucket_name)
373
+
374
+ # abort multi-upload
375
+ client.abort_multipart_upload(bucket_name, key + "_abort", upload_id_abort)
376
+ end