blobstore_client 1.3149.0 → 1.3153.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.
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