baidubce-sdk 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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