aliyun-oss-ruby-sdk 0.4.1

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