aliyun-oss-ruby-sdk 0.4.1

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.
Files changed (61) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +95 -0
  3. data/README.md +423 -0
  4. data/examples/aliyun/oss/bucket.rb +144 -0
  5. data/examples/aliyun/oss/callback.rb +61 -0
  6. data/examples/aliyun/oss/object.rb +182 -0
  7. data/examples/aliyun/oss/resumable_download.rb +42 -0
  8. data/examples/aliyun/oss/resumable_upload.rb +49 -0
  9. data/examples/aliyun/oss/streaming.rb +124 -0
  10. data/examples/aliyun/oss/using_sts.rb +48 -0
  11. data/examples/aliyun/sts/assume_role.rb +59 -0
  12. data/lib/aliyun_sdk/common.rb +6 -0
  13. data/lib/aliyun_sdk/common/exception.rb +18 -0
  14. data/lib/aliyun_sdk/common/logging.rb +46 -0
  15. data/lib/aliyun_sdk/common/struct.rb +56 -0
  16. data/lib/aliyun_sdk/oss.rb +16 -0
  17. data/lib/aliyun_sdk/oss/bucket.rb +661 -0
  18. data/lib/aliyun_sdk/oss/client.rb +106 -0
  19. data/lib/aliyun_sdk/oss/config.rb +39 -0
  20. data/lib/aliyun_sdk/oss/download.rb +255 -0
  21. data/lib/aliyun_sdk/oss/exception.rb +108 -0
  22. data/lib/aliyun_sdk/oss/http.rb +338 -0
  23. data/lib/aliyun_sdk/oss/iterator.rb +92 -0
  24. data/lib/aliyun_sdk/oss/multipart.rb +74 -0
  25. data/lib/aliyun_sdk/oss/object.rb +15 -0
  26. data/lib/aliyun_sdk/oss/protocol.rb +1499 -0
  27. data/lib/aliyun_sdk/oss/struct.rb +208 -0
  28. data/lib/aliyun_sdk/oss/upload.rb +238 -0
  29. data/lib/aliyun_sdk/oss/util.rb +89 -0
  30. data/lib/aliyun_sdk/sts.rb +9 -0
  31. data/lib/aliyun_sdk/sts/client.rb +38 -0
  32. data/lib/aliyun_sdk/sts/config.rb +22 -0
  33. data/lib/aliyun_sdk/sts/exception.rb +53 -0
  34. data/lib/aliyun_sdk/sts/protocol.rb +130 -0
  35. data/lib/aliyun_sdk/sts/struct.rb +64 -0
  36. data/lib/aliyun_sdk/sts/util.rb +48 -0
  37. data/lib/aliyun_sdk/version.rb +7 -0
  38. data/spec/aliyun/oss/bucket_spec.rb +597 -0
  39. data/spec/aliyun/oss/client/bucket_spec.rb +554 -0
  40. data/spec/aliyun/oss/client/client_spec.rb +297 -0
  41. data/spec/aliyun/oss/client/resumable_download_spec.rb +220 -0
  42. data/spec/aliyun/oss/client/resumable_upload_spec.rb +413 -0
  43. data/spec/aliyun/oss/http_spec.rb +83 -0
  44. data/spec/aliyun/oss/multipart_spec.rb +686 -0
  45. data/spec/aliyun/oss/object_spec.rb +785 -0
  46. data/spec/aliyun/oss/service_spec.rb +142 -0
  47. data/spec/aliyun/oss/util_spec.rb +50 -0
  48. data/spec/aliyun/sts/client_spec.rb +150 -0
  49. data/spec/aliyun/sts/util_spec.rb +39 -0
  50. data/tests/config.rb +31 -0
  51. data/tests/test_content_encoding.rb +54 -0
  52. data/tests/test_content_type.rb +95 -0
  53. data/tests/test_custom_headers.rb +70 -0
  54. data/tests/test_encoding.rb +77 -0
  55. data/tests/test_large_file.rb +66 -0
  56. data/tests/test_multipart.rb +97 -0
  57. data/tests/test_object_acl.rb +49 -0
  58. data/tests/test_object_key.rb +68 -0
  59. data/tests/test_object_url.rb +69 -0
  60. data/tests/test_resumable.rb +40 -0
  61. metadata +240 -0
@@ -0,0 +1,142 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ require 'spec_helper'
4
+ require 'yaml'
5
+ require 'nokogiri'
6
+
7
+ module AliyunSDK
8
+ module OSS
9
+
10
+ describe "Service" do
11
+ before :all do
12
+ @endpoint = 'oss.aliyuncs.com'
13
+ @protocol = Protocol.new(
14
+ Config.new(:endpoint => @endpoint,
15
+ :access_key_id => 'xxx', :access_key_secret => 'yyy'))
16
+
17
+ @all_buckets = []
18
+ (1..10).each do |i|
19
+ name = "rubysdk-bucket-#{i.to_s.rjust(3, '0')}"
20
+ @all_buckets << Bucket.new(
21
+ :name => name,
22
+ :location => 'oss-cn-hangzhou',
23
+ :creation_time => Time.now)
24
+ end
25
+ end
26
+
27
+ # 生成list_buckets返回的响应,包含bucket列表和more信息
28
+ def mock_response(buckets, more)
29
+ builder = Nokogiri::XML::Builder.new do |xml|
30
+ xml.ListAllMyBucketsResult {
31
+ xml.Owner {
32
+ xml.ID 'owner_id'
33
+ xml.DisplayName 'owner_name'
34
+ }
35
+ xml.Buckets {
36
+ buckets.each do |b|
37
+ xml.Bucket {
38
+ xml.Location b.location
39
+ xml.Name b.name
40
+ xml.CreationDate b.creation_time.to_s
41
+ }
42
+ end
43
+ }
44
+
45
+ unless more.empty?
46
+ xml.Prefix more[:prefix]
47
+ xml.Marker more[:marker]
48
+ xml.MaxKeys more[:limit].to_s
49
+ xml.NextMarker more[:next_marker]
50
+ xml.IsTruncated more[:truncated]
51
+ end
52
+ }
53
+ end
54
+
55
+ builder.to_xml
56
+ end
57
+
58
+ context "List all buckets" do
59
+ # 测试list_buckets正确地发送了HTTP请求
60
+ it "should send correct request" do
61
+ stub_request(:get, @endpoint)
62
+
63
+ @protocol.list_buckets
64
+
65
+ expect(WebMock).to have_requested(:get, @endpoint).
66
+ with(:body => nil, :query => {})
67
+ end
68
+
69
+ # 测试list_buckets正确地解析了list_buckets的返回
70
+ it "should correctly parse response" do
71
+ stub_request(:get, @endpoint).to_return(
72
+ {:body => mock_response(@all_buckets, {})})
73
+
74
+ buckets, more = @protocol.list_buckets
75
+ bucket_names = buckets.map {|b| b.name}
76
+
77
+ all_bucket_names = @all_buckets.map {|b| b.name}
78
+ expect(bucket_names).to match_array(all_bucket_names)
79
+
80
+ expect(more).to be_empty
81
+ end
82
+ end
83
+
84
+ context "Paging buckets" do
85
+ # 测试list_buckets的请求中包含prefix/marker/maxkeys等信息
86
+ it "should set prefix/max-keys param" do
87
+ prefix = 'rubysdk-bucket-00'
88
+ marker = 'rubysdk-bucket-002'
89
+ limit = 5
90
+
91
+ stub_request(:get, @endpoint).with(
92
+ :query => {'prefix' => prefix, 'marker' => marker, 'max-keys' => limit})
93
+
94
+ @protocol.list_buckets(
95
+ :prefix => prefix, :limit => limit, :marker => marker)
96
+
97
+ expect(WebMock).to have_requested(:get, @endpoint).
98
+ with(:body => nil,
99
+ :query => {'prefix' => prefix,
100
+ 'marker' => marker,
101
+ 'max-keys' => limit})
102
+ end
103
+
104
+ # 测试list_buckets正确地解析了HTTP响应,包含more信息
105
+ it "should parse next marker" do
106
+ prefix = 'rubysdk-bucket-00'
107
+ marker = 'rubysdk-bucket-002'
108
+ limit = 5
109
+ # returns ['rubysdk-bucket-003', ..., 'rubysdk-bucket-007']
110
+ return_buckets = @all_buckets[2, 5]
111
+ next_marker = 'rubysdk-bucket-007'
112
+
113
+ more = {:prefix => prefix, :marker => marker, :limit => limit,
114
+ :next_marker => next_marker, :truncated => true}
115
+
116
+ stub_request(:get, @endpoint).with(
117
+ :query => {'prefix' => prefix, 'marker' => marker, 'max-keys' => limit}
118
+ ).to_return(
119
+ {:body => mock_response(return_buckets, more)})
120
+
121
+ buckets, more = @protocol.list_buckets(
122
+ :prefix => prefix,
123
+ :limit => limit,
124
+ :marker => marker)
125
+
126
+ bucket_names = buckets.map {|b| b.name}
127
+ return_bucket_names = return_buckets.map {|b| b.name}
128
+ expect(bucket_names).to match_array(return_bucket_names)
129
+
130
+ expect(more).to eq({
131
+ :prefix => prefix,
132
+ :marker => marker,
133
+ :limit => limit,
134
+ :next_marker => next_marker,
135
+ :truncated => true})
136
+ end
137
+
138
+ end
139
+
140
+ end # Bucket
141
+ end # OSS
142
+ end # Aliyun
@@ -0,0 +1,50 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ require 'spec_helper'
4
+
5
+ module AliyunSDK
6
+ module OSS
7
+
8
+ describe Util do
9
+ # 测试对body content的md5编码是否正确
10
+ it "should get correct content md5" do
11
+ content = ""
12
+
13
+ md5 = Util.get_content_md5(content)
14
+ expect(md5).to eq("1B2M2Y8AsgTpgAmY7PhCfg==")
15
+
16
+ content = "hello world"
17
+ md5 = Util.get_content_md5(content)
18
+ expect(md5).to eq("XrY7u+Ae7tCTyyK7j1rNww==")
19
+ end
20
+
21
+ # 测试签名是否正确
22
+ it "should get correct signature" do
23
+ key = 'helloworld'
24
+ date = 'Fri, 30 Oct 2015 07:21:00 GMT'
25
+
26
+ signature = Util.get_signature(key, 'GET', {'date' => date}, {})
27
+ expect(signature).to eq("u8QKAAj/axKX4JhHXa5DYfYSPxE=")
28
+
29
+ signature = Util.get_signature(
30
+ key, 'PUT', {'date' => date}, {:path => '/bucket'})
31
+ expect(signature).to eq("lMKrMCJIuGygd8UsdMA+S0QOAsQ=")
32
+
33
+ signature = Util.get_signature(
34
+ key, 'PUT',
35
+ {'date' => date, 'x-oss-copy-source' => '/bucket/object-old'},
36
+ {:path => '/bucket/object-new'})
37
+ expect(signature).to eq("McYUmBaErN//yvE9voWRhCgvsIc=")
38
+
39
+ signature = Util.get_signature(
40
+ key, 'PUT',
41
+ {'date' => date},
42
+ {:path => '/bucket/object-new',
43
+ :sub_res => {'append' => nil, 'position' => 0}})
44
+ expect(signature).to eq("7Oh2wobzeg6dw/cWYbF/2m6s6qc=")
45
+ end
46
+
47
+ end # Util
48
+
49
+ end # OSS
50
+ end # Aliyun
@@ -0,0 +1,150 @@
1
+ require 'spec_helper'
2
+ require 'yaml'
3
+ require 'nokogiri'
4
+
5
+ module AliyunSDK
6
+ module STS
7
+
8
+ describe Client do
9
+
10
+ context "construct" do
11
+ it "should setup a/k" do
12
+ client = Client.new(
13
+ :access_key_id => ' xxx', :access_key_secret => ' yyy ')
14
+
15
+ config = client.instance_variable_get('@config')
16
+ expect(config.access_key_id).to eq('xxx')
17
+ expect(config.access_key_secret).to eq('yyy')
18
+ end
19
+ end
20
+
21
+ def mock_sts(id, key, token, expiration)
22
+ Nokogiri::XML::Builder.new do |xml|
23
+ xml.AssumeRoleResponse {
24
+ xml.RequestId '0000'
25
+ xml.AssumedRoleUser {
26
+ xml.arn 'arn-001'
27
+ xml.AssumedRoleUserId 'id-001'
28
+ }
29
+ xml.Credentials {
30
+ xml.AccessKeyId id
31
+ xml.AccessKeySecret key
32
+ xml.SecurityToken token
33
+ xml.Expiration expiration.utc.iso8601
34
+ }
35
+ }
36
+ end.to_xml
37
+ end
38
+
39
+ def mock_error(code, message)
40
+ Nokogiri::XML::Builder.new do |xml|
41
+ xml.Error {
42
+ xml.Code code
43
+ xml.Message message
44
+ xml.RequestId '0000'
45
+ }
46
+ end.to_xml
47
+ end
48
+
49
+ def err(msg, reqid = '0000')
50
+ "#{msg} RequestId: #{reqid}"
51
+ end
52
+
53
+ before :all do
54
+ @url = 'https://sts.aliyuncs.com'
55
+ @client = Client.new(access_key_id: 'xxx', access_key_secret: 'yyy')
56
+ end
57
+
58
+ context "assume role" do
59
+ it "should assume role" do
60
+ expiration = Time.parse(Time.now.utc.iso8601)
61
+
62
+ stub_request(:post, @url)
63
+ .to_return(:body => mock_sts(
64
+ 'sts_id', 'sts_key', 'sts_token', expiration))
65
+
66
+ token = @client.assume_role('role-1', 'app-1')
67
+
68
+ rbody = nil
69
+ expect(WebMock).to have_requested(:post, @url)
70
+ .with { |req| rbody = req.body }
71
+ params = rbody.split('&').reduce({}) { |h, i|
72
+ v = i.split('=')
73
+ h.merge({v[0] => v[1]})
74
+ }
75
+ expect(params['Action']).to eq('AssumeRole')
76
+ expect(params['RoleArn']).to eq('role-1')
77
+ expect(params['RoleSessionName']).to eq('app-1')
78
+ expect(params['DurationSeconds']).to eq('3600')
79
+ expect(params['Format']).to eq('XML')
80
+ expect(params['Version']).to eq('2015-04-01')
81
+ expect(params['AccessKeyId']).to eq('xxx')
82
+ expect(params.key?('Signature')).to be true
83
+ expect(params.key?('SignatureNonce')).to be true
84
+ expect(params['SignatureMethod']).to eq('HMAC-SHA1')
85
+
86
+ expect(token.access_key_id).to eq('sts_id')
87
+ expect(token.access_key_secret).to eq('sts_key')
88
+ expect(token.security_token).to eq('sts_token')
89
+ expect(token.expiration).to eq(expiration)
90
+ end
91
+
92
+ it "should raise error" do
93
+ code = "InvalidParameter"
94
+ message = "Bla bla bla."
95
+
96
+ stub_request(:post, @url)
97
+ .to_return(:status => 400,
98
+ :body => mock_error(code, message))
99
+
100
+
101
+ expect {
102
+ @client.assume_role('role-1', 'app-1')
103
+ }.to raise_error(ServerError, err(message))
104
+
105
+ end
106
+
107
+ it "should set policy and duration" do
108
+ expiration = Time.parse(Time.now.utc.iso8601)
109
+
110
+ stub_request(:post, @url)
111
+ .to_return(:body => mock_sts(
112
+ 'sts_id', 'sts_key', 'sts_token', expiration))
113
+
114
+ policy = Policy.new
115
+ policy.allow(
116
+ ['oss:Get*', 'oss:PutObject'],
117
+ ['acs:oss:*:*:bucket', 'acs::oss:*:*:bucket/*'])
118
+ duration = 300
119
+ token = @client.assume_role('role-1', 'app-1', policy, duration)
120
+
121
+ rbody = nil
122
+ expect(WebMock).to have_requested(:post, @url)
123
+ .with { |req| rbody = req.body }
124
+ params = rbody.split('&').reduce({}) { |h, i|
125
+ v = i.split('=')
126
+ h.merge({v[0] => CGI.unescape(v[1])})
127
+ }
128
+ expect(params['Action']).to eq('AssumeRole')
129
+ expect(params['RoleArn']).to eq('role-1')
130
+ expect(params['RoleSessionName']).to eq('app-1')
131
+ expect(params['DurationSeconds']).to eq('300')
132
+ expect(params['Format']).to eq('XML')
133
+ expect(params['Version']).to eq('2015-04-01')
134
+ expect(params['AccessKeyId']).to eq('xxx')
135
+ expect(params.key?('Signature')).to be true
136
+ expect(params.key?('SignatureNonce')).to be true
137
+ expect(params['SignatureMethod']).to eq('HMAC-SHA1')
138
+ expect(params['Policy']).to eq(policy.serialize)
139
+
140
+ expect(token.access_key_id).to eq('sts_id')
141
+ expect(token.access_key_secret).to eq('sts_key')
142
+ expect(token.security_token).to eq('sts_token')
143
+ expect(token.expiration).to eq(expiration)
144
+ end
145
+ end
146
+
147
+ end # Client
148
+
149
+ end # OSS
150
+ end # Aliyun
@@ -0,0 +1,39 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ require 'spec_helper'
4
+ require 'yaml'
5
+ require 'nokogiri'
6
+
7
+ module AliyunSDK
8
+ module STS
9
+
10
+ describe Util do
11
+
12
+ it "should get correct signature" do
13
+ key = 'helloworld'
14
+ ts = '2015-12-07T07:18:41Z'
15
+
16
+ params = {
17
+ 'Action' => 'AssumeRole',
18
+ 'RoleArn' => 'role-1',
19
+ 'RoleSessionName' => 'app-1',
20
+ 'DurationSeconds' => '300',
21
+ 'Format' => 'XML',
22
+ 'Version' => '2015-04-01',
23
+ 'AccessKeyId' => 'xxx',
24
+ 'SignatureMethod' => 'HMAC-SHA1',
25
+ 'SignatureVersion' => '1.0',
26
+ 'SignatureNonce' => '3.14159',
27
+ 'Timestamp' => ts
28
+ }
29
+
30
+ signature = Util.get_signature('POST', params, key)
31
+ expect(signature).to eq("92ta30QopCT4YTbRCaWtS31kyeg=")
32
+
33
+ signature = Util.get_signature('GET', params, key)
34
+ expect(signature).to eq("nvMmnOSxGrfK+1zf0oFR5RB2M7k=")
35
+ end
36
+
37
+ end # Util
38
+ end # OSS
39
+ end # Aliyun
@@ -0,0 +1,31 @@
1
+ class TestConf
2
+ class << self
3
+ def creds
4
+ {
5
+ access_key_id: ENV['RUBY_SDK_OSS_ID'],
6
+ access_key_secret: ENV['RUBY_SDK_OSS_KEY'],
7
+ endpoint: ENV['RUBY_SDK_OSS_ENDPOINT']
8
+ }
9
+ end
10
+
11
+ def bucket
12
+ ENV['RUBY_SDK_OSS_BUCKET']
13
+ end
14
+
15
+ def sts_creds
16
+ {
17
+ access_key_id: ENV['RUBY_SDK_STS_ID'],
18
+ access_key_secret: ENV['RUBY_SDK_STS_KEY'],
19
+ endpoint: ENV['RUBY_SDK_STS_ENDPOINT']
20
+ }
21
+ end
22
+
23
+ def sts_role
24
+ ENV['RUBY_SDK_STS_ROLE']
25
+ end
26
+
27
+ def sts_bucket
28
+ ENV['RUBY_SDK_STS_BUCKET']
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,54 @@
1
+ require 'minitest/autorun'
2
+ require 'yaml'
3
+ $LOAD_PATH.unshift(File.expand_path("../../lib", __FILE__))
4
+ require 'aliyun/oss'
5
+ require 'zlib'
6
+ require_relative 'config'
7
+
8
+ class TestContentEncoding < Minitest::Test
9
+ def setup
10
+ AliyunSDK::Common::Logging.set_log_level(Logger::DEBUG)
11
+ client = AliyunSDK::OSS::Client.new(TestConf.creds)
12
+ @bucket = client.get_bucket(TestConf.bucket)
13
+
14
+ @prefix = "tests/content_encoding/"
15
+ end
16
+
17
+ def get_key(k)
18
+ "#{@prefix}#{k}"
19
+ end
20
+
21
+ def test_gzip_encoding
22
+ key = get_key('gzip')
23
+ File.open('/tmp/x', 'w') do |f|
24
+ 1000.times { f.write 'hello world' * 1024 }
25
+ end
26
+
27
+ @bucket.put_object(
28
+ key, file: '/tmp/x', content_type: 'text/plain')
29
+
30
+ @bucket.get_object(
31
+ key, file: '/tmp/y', headers: {'accept-encoding' => 'gzip'})
32
+
33
+ assert File.exist?('/tmp/y')
34
+ diff = `diff /tmp/x /tmp/y`
35
+ assert diff.empty?
36
+ end
37
+
38
+ def test_deflate_encoding
39
+ key = get_key('deflate')
40
+ File.open('/tmp/x', 'w') do |f|
41
+ 1000.times { f.write 'hello world' * 1024 }
42
+ end
43
+
44
+ @bucket.put_object(
45
+ key, file: '/tmp/x', content_type: 'text/plain')
46
+
47
+ @bucket.get_object(
48
+ key, file: '/tmp/y', headers: {'accept-encoding' => 'deflate'})
49
+
50
+ assert File.exist?('/tmp/y')
51
+ diff = `diff /tmp/x /tmp/y`
52
+ assert diff.empty?
53
+ end
54
+ end