blobstore_client 1.3149.0 → 1.3153.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 1c55bf108ea4027d249543dd7c6716d324d34174
4
- data.tar.gz: 5903b15e591b39f2e3b0751a248c26645552f0e8
3
+ metadata.gz: 10c163e30bf30a08b4909ba6e12c7081efb1f991
4
+ data.tar.gz: ec59e8b8134b97a754eed2ec8bf6d94b5e07c123
5
5
  SHA512:
6
- metadata.gz: d6ba85dfc804a3bdb6f78959ba88810c7e535ec118114792944cdbf903b4026c9a75c75b2132cc8804d841b9b446d16dd2e8d65f8e4f296fd6745f2afb909c1c
7
- data.tar.gz: 9a5a5aeeea9675e3882972d5aef8ede458b060e4148ac36140a075011ed9df99209b58b1a802d3390f2a8a391d21b823d1c99490a66d8ae72b48fabe0c6d3a1b
6
+ metadata.gz: e0c59b863d0ec822b33133035ac5a9734963bfd11e3b1a55f08f66ad85f5fd3bdd7bbeec58203500fe4a6ef7c2aba9f801290cdd01c3428a0329f36787badd3e
7
+ data.tar.gz: b1c4581c5cc14372db13fae84d410788eb24226441c8162f48df2120020ad1f8c4d07eba2ab57a5cf16a4f671971c5afc78ac7e2f76c10efe4d862e720a547c7
@@ -1,7 +1,7 @@
1
1
  require 'openssl'
2
2
  require 'digest/sha1'
3
3
  require 'base64'
4
- require 'aws'
4
+ require 'aws-sdk-resources'
5
5
  require 'securerandom'
6
6
 
7
7
  module Bosh
@@ -9,14 +9,15 @@ module Bosh
9
9
  class S3BlobstoreClient < BaseClient
10
10
 
11
11
  ENDPOINT = 'https://s3.amazonaws.com'
12
- DEFAULT_CIPHER_NAME = 'aes-128-cbc'
12
+ DEFAULT_REGION = 'us-east-1'
13
+ # hack to get the v2 AWS SDK to behave with S3-compatible blobstores
14
+ BLANK_REGION = ' '
13
15
 
14
- attr_reader :bucket_name, :encryption_key, :simple
16
+ attr_reader :simple
15
17
 
16
- # Blobstore client for S3 with optional object encryption
18
+ # Blobstore client for S3
17
19
  # @param [Hash] options S3connection options
18
20
  # @option options [Symbol] bucket_name
19
- # @option options [Symbol, optional] encryption_key optional encryption
20
21
  # key that is applied before the object is sent to S3
21
22
  # @option options [Symbol, optional] access_key_id
22
23
  # @option options [Symbol, optional] secret_access_key
@@ -25,27 +26,23 @@ module Bosh
25
26
  # simple_blobstore_client
26
27
  def initialize(options)
27
28
  super(options)
28
- @bucket_name = @options[:bucket_name]
29
- @encryption_key = @options[:encryption_key]
30
29
 
31
- aws_options = {
30
+ @aws_options = build_aws_options({
31
+ bucket_name: @options[:bucket_name],
32
32
  use_ssl: @options.fetch(:use_ssl, true),
33
- s3_port: @options.fetch(:port, 443),
34
- s3_endpoint: @options.fetch(:host, URI.parse(S3BlobstoreClient::ENDPOINT).host),
33
+ host: @options[:host],
34
+ port: @options[:port],
35
+ region: @options.fetch(:region, DEFAULT_REGION),
35
36
  s3_force_path_style: @options.fetch(:s3_force_path_style, false),
36
- ssl_verify_peer: @options.fetch(:ssl_verify_peer, true),
37
- s3_multipart_threshold: @options.fetch(:s3_multipart_threshold, 16_777_216),
38
- }
39
-
40
- aws_options.merge!(aws_credentials)
37
+ ssl_verify_peer: @options.fetch(:ssl_verify_peer, true),
38
+ credentials_source: @options.fetch(:credentials_source, 'static'),
39
+ access_key_id: @options[:access_key_id],
40
+ secret_access_key: @options[:secret_access_key]
41
+ })
41
42
 
42
43
  # using S3 without credentials is a special case:
43
44
  # it is really the simple blobstore client with a bucket name
44
45
  if read_only?
45
- if @encryption_key
46
- raise BlobstoreError, "can't use read-only with an encryption key"
47
- end
48
-
49
46
  unless @options[:bucket_name] || @options[:bucket]
50
47
  raise BlobstoreError, 'bucket name required'
51
48
  end
@@ -53,12 +50,10 @@ module Bosh
53
50
  @options[:bucket] ||= @options[:bucket_name]
54
51
  @options[:endpoint] ||= S3BlobstoreClient::ENDPOINT
55
52
  @simple = SimpleBlobstoreClient.new(@options)
56
- else
57
- @s3 = AWS::S3.new(aws_options)
58
53
  end
59
54
 
60
- rescue AWS::Errors::Base => e
61
- raise BlobstoreError, "Failed to initialize S3 blobstore: #{e.message}"
55
+ rescue Aws::S3::Errors::ServiceError => e
56
+ raise BlobstoreError, "Failed to initialize S3 blobstore: #{e.code} : #{e.message}"
62
57
  end
63
58
 
64
59
  # @param [File] file file to store in S3
@@ -67,17 +62,13 @@ module Bosh
67
62
 
68
63
  object_id ||= generate_object_id
69
64
 
70
- file = encrypt_file(file) if @encryption_key
71
-
72
65
  # in Ruby 1.8 File doesn't respond to :path
73
66
  path = file.respond_to?(:path) ? file.path : file
74
67
  store_in_s3(path, full_oid_path(object_id))
75
68
 
76
69
  object_id
77
- rescue AWS::Errors::Base => e
78
- raise BlobstoreError, "Failed to create object, S3 response error: #{e.message}"
79
- ensure
80
- FileUtils.rm(file) if @encryption_key
70
+ rescue Aws::S3::Errors::ServiceError => e
71
+ raise BlobstoreError, "Failed to create object, S3 response error code #{e.code}: #{e.message}"
81
72
  end
82
73
 
83
74
  # @param [String] object_id object id to retrieve
@@ -86,81 +77,59 @@ module Bosh
86
77
  object_id = full_oid_path(object_id)
87
78
  return @simple.get_file(object_id, file) if @simple
88
79
 
89
- if @encryption_key
90
- cipher = OpenSSL::Cipher::Cipher.new(DEFAULT_CIPHER_NAME)
91
- cipher.decrypt
92
- cipher.key = Digest::SHA1.digest(encryption_key)[0..(cipher.key_len - 1)]
80
+ s3_object = Aws::S3::Object.new({:key => object_id}.merge(@aws_options))
81
+ s3_object.get do |chunk|
82
+ file.write(chunk)
93
83
  end
94
84
 
95
- object = get_object_from_s3(object_id)
96
- object.read do |chunk|
97
- if @encryption_key
98
- file.write(cipher.update(chunk))
99
- else
100
- file.write(chunk)
101
- end
102
- end
103
-
104
- file.write(cipher.final) if @encryption_key
105
-
106
- rescue AWS::S3::Errors::NoSuchKey => e
85
+ rescue Aws::S3::Errors::NoSuchKey => e
107
86
  raise NotFound, "S3 object '#{object_id}' not found"
108
- rescue AWS::Errors::Base => e
109
- raise BlobstoreError, "Failed to find object '#{object_id}', S3 response error: #{e.message}"
87
+ rescue Aws::S3::Errors::ServiceError => e
88
+ raise BlobstoreError, "Failed to find object '#{object_id}', S3 response error code #{e.code}: #{e.message}"
110
89
  end
111
90
 
112
91
  # @param [String] object_id object id to delete
113
92
  def delete_object(object_id)
114
93
  raise BlobstoreError, 'unsupported action' if @simple
115
94
  object_id = full_oid_path(object_id)
116
- object = get_object_from_s3(object_id)
117
- raise NotFound, "Object '#{object_id}' is not found" unless object.exists?
118
95
 
119
- object.delete
120
- rescue AWS::Errors::Base => e
121
- raise BlobstoreError, "Failed to delete object '#{object_id}', S3 response error: #{e.message}"
96
+ s3_object = Aws::S3::Object.new({:key => object_id}.merge(@aws_options))
97
+ # TODO: don't blow up if we are cannot find an object we are trying to
98
+ # delete anyway
99
+ raise NotFound, "Object '#{object_id}' is not found" unless s3_object.exists?
100
+
101
+ s3_object.delete
102
+ rescue Aws::S3::Errors::ServiceError => e
103
+ raise BlobstoreError, "Failed to delete object '#{object_id}', S3 response error code #{e.code}: #{e.message}"
122
104
  end
123
105
 
124
106
  def object_exists?(object_id)
125
107
  object_id = full_oid_path(object_id)
126
108
  return simple.exists?(object_id) if simple
127
109
 
128
- get_object_from_s3(object_id).exists?
110
+ # Hack to get the Aws SDK to redirect to the correct region on
111
+ # subsequent requests
112
+ unless @region_configured
113
+ s3 = Aws::S3::Client.new(@aws_options.reject{|k| k == :bucket_name})
114
+ s3.list_objects({bucket: @aws_options[:bucket_name]})
115
+ @region_configured = true
116
+ end
117
+
118
+ Aws::S3::Object.new({:key => object_id}.merge(@aws_options)).exists?
129
119
  end
130
120
 
131
121
  protected
132
122
 
133
- # @param [String] oid object id
134
- # @return [AWS::S3::S3Object] S3 object
135
- def get_object_from_s3(oid)
136
- @s3.buckets[bucket_name].objects[oid]
137
- end
138
-
139
123
  # @param [String] path path to file which will be stored in S3
140
124
  # @param [String] oid object id
141
125
  # @return [void]
142
126
  def store_in_s3(path, oid)
143
- s3_object = get_object_from_s3(oid)
144
- raise BlobstoreError, "object id #{oid} is already in use" if s3_object.exists?
145
- File.open(path, 'r') do |temp_file|
146
- s3_object.write(temp_file, content_type: "application/octet-stream")
147
- end
148
- end
149
-
150
- def encrypt_file(file)
151
- cipher = OpenSSL::Cipher::Cipher.new(DEFAULT_CIPHER_NAME)
152
- cipher.encrypt
153
- cipher.key = Digest::SHA1.digest(encryption_key)[0..(cipher.key_len - 1)]
127
+ raise BlobstoreError, "object id #{oid} is already in use" if object_exists?(oid)
154
128
 
155
- path = temp_path
156
- File.open(path, 'w') do |temp_file|
157
- while (block = file.read(32768))
158
- temp_file.write(cipher.update(block))
159
- end
160
- temp_file.write(cipher.final)
161
- end
162
-
163
- path
129
+ s3_object = Aws::S3::Object.new({:key => oid}.merge(@aws_options))
130
+ multipart_threshold = @options.fetch(:s3_multipart_threshold, 16_777_216)
131
+ s3_object.upload_file(path, {content_type: "application/octet-stream", multipart_threshold: multipart_threshold})
132
+ nil
164
133
  end
165
134
 
166
135
  def read_only?
@@ -174,19 +143,24 @@ module Bosh
174
143
  @options[:folder] ? @options[:folder] + '/' + object_id : object_id
175
144
  end
176
145
 
177
- def aws_credentials
146
+ def use_v4_signing?(region)
147
+ (region == 'eu-central-1' ||
148
+ region == 'cn-north-1')
149
+ end
150
+
151
+ def aws_credentials(credentials_source, access_key_id, secret_access_key)
178
152
  creds = {}
179
153
  # credentials_source could be static (default) or env_or_profile
180
154
  # static credentials must be included in aws_properties
181
- # env_or_profile credentials will use the AWS DefaultCredentialsProvider
182
- # to find AWS credentials in environment variables or EC2 instance profiles
183
- case @options.fetch(:credentials_source, 'static')
155
+ # env_or_profile credentials will use the Aws DefaultCredentialsProvider
156
+ # to find Aws credentials in environment variables or EC2 instance profiles
157
+ case credentials_source
184
158
  when 'static'
185
- creds[:access_key_id] = @options[:access_key_id]
186
- creds[:secret_access_key] = @options[:secret_access_key]
159
+ creds[:access_key_id] = access_key_id
160
+ creds[:secret_access_key] = secret_access_key
187
161
 
188
162
  when 'env_or_profile'
189
- if !@options[:access_key_id].nil? || !@options[:secret_access_key].nil?
163
+ if !access_key_id.nil? || !secret_access_key.nil?
190
164
  raise BlobstoreError, "can't use access_key_id or secret_access_key with env_or_profile credentials_source"
191
165
  end
192
166
  else
@@ -194,6 +168,30 @@ module Bosh
194
168
  end
195
169
  return creds
196
170
  end
171
+
172
+ def build_aws_options(options)
173
+ aws_options = {
174
+ bucket_name: options[:bucket_name],
175
+ region: options[:region],
176
+ force_path_style: options[:s3_force_path_style],
177
+ ssl_verify_peer: options[:ssl_verify_peer],
178
+ }
179
+
180
+ unless options[:host].nil?
181
+ host = options[:host]
182
+ protocol = options[:use_ssl] ? 'https' : 'http'
183
+ uri = options[:port].nil? ? host : "#{host}:#{options[:port]}"
184
+ aws_options[:endpoint] = "#{protocol}://#{uri}"
185
+ aws_options[:region] = BLANK_REGION
186
+ end
187
+
188
+ aws_options[:signature_version] = 's3' unless use_v4_signing?(options[:region])
189
+
190
+ creds = aws_credentials(options[:credentials_source], options[:access_key_id], options[:secret_access_key])
191
+ aws_options.merge!(creds)
192
+
193
+ aws_options
194
+ end
197
195
  end
198
196
  end
199
197
  end
@@ -1,7 +1,7 @@
1
1
  module Bosh
2
2
  module Blobstore
3
3
  class Client
4
- VERSION = '1.3149.0'
4
+ VERSION = '1.3153.0'
5
5
  end
6
6
  end
7
7
  end
metadata CHANGED
@@ -1,29 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: blobstore_client
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.3149.0
4
+ version: 1.3153.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - VMware
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-12-07 00:00:00.000000000 Z
11
+ date: 2015-12-09 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
- name: aws-sdk
14
+ name: aws-sdk-resources
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
17
  - - '='
18
18
  - !ruby/object:Gem::Version
19
- version: 1.60.2
19
+ version: 2.2.0
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - '='
25
25
  - !ruby/object:Gem::Version
26
- version: 1.60.2
26
+ version: 2.2.0
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: httpclient
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -58,14 +58,14 @@ dependencies:
58
58
  requirements:
59
59
  - - "~>"
60
60
  - !ruby/object:Gem::Version
61
- version: 1.3149.0
61
+ version: 1.3153.0
62
62
  type: :runtime
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
66
  - - "~>"
67
67
  - !ruby/object:Gem::Version
68
- version: 1.3149.0
68
+ version: 1.3153.0
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: rspec
71
71
  requirement: !ruby/object:Gem::Requirement