Class: Bosh::Blobstore::S3BlobstoreClient

Inherits:
BaseClient show all
Defined in:
lib/blobstore_client/s3_blobstore_client.rb

Constant Summary collapse

ENDPOINT =
'https://s3.amazonaws.com'
DEFAULT_REGION =
'us-east-1'
BLANK_REGION =

hack to get the v2 AWS SDK to behave with S3-compatible blobstores

' '

Constants inherited from Client

Client::PROVIDER_NAMES, Client::VERSION

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods inherited from BaseClient

#create, #delete, #exists?, #get

Methods inherited from Client

create, safe_create

Constructor Details

#initialize(options) ⇒ S3BlobstoreClient

Note:

If access_key_id and secret_access_key are not present, the blobstore client operates in read only mode as a simple_blobstore_client

Blobstore client for S3

Parameters:

  • options (Hash)

    S3connection options

Options Hash (options):

  • bucket_name (Symbol)

    key that is applied before the object is sent to S3

  • access_key_id (Symbol, optional)
  • secret_access_key (Symbol, optional)


27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
# File 'lib/blobstore_client/s3_blobstore_client.rb', line 27

def initialize(options)
  super(options)

  @aws_options = build_aws_options({
    bucket_name: @options[:bucket_name],
    use_ssl: @options.fetch(:use_ssl, true),
    host: @options[:host],
    port: @options[:port],
    region: @options[:region] || DEFAULT_REGION,
    s3_force_path_style: @options.fetch(:s3_force_path_style, false),
    ssl_verify_peer:  @options.fetch(:ssl_verify_peer, true),
    credentials_source: @options.fetch(:credentials_source, 'static'),
    access_key_id: @options[:access_key_id],
    secret_access_key: @options[:secret_access_key],
    signature_version: @options[:signature_version]
  })

  # using S3 without credentials is a special case:
  # it is really the simple blobstore client with a bucket name
  if read_only?
    unless @options[:bucket_name] || @options[:bucket]
      raise BlobstoreError, 'bucket name required'
    end

    @options[:bucket] ||= @options[:bucket_name]
    @options[:endpoint] ||= S3BlobstoreClient::ENDPOINT
    @simple = SimpleBlobstoreClient.new(@options)
  end

rescue Aws::S3::Errors::ServiceError => e
  raise BlobstoreError, "Failed to initialize S3 blobstore: #{e.code} : #{e.message}"
end

Instance Attribute Details

#simpleObject (readonly)

Returns the value of attribute simple.



16
17
18
# File 'lib/blobstore_client/s3_blobstore_client.rb', line 16

def simple
  @simple
end

Instance Method Details

#create_file(object_id, file) ⇒ Object

Parameters:

  • file (File)

    file to store in S3



61
62
63
64
65
66
67
68
69
70
71
72
73
# File 'lib/blobstore_client/s3_blobstore_client.rb', line 61

def create_file(object_id, file)
  raise BlobstoreError, 'unsupported action' if @simple

  object_id ||= generate_object_id

  # in Ruby 1.8 File doesn't respond to :path
  path = file.respond_to?(:path) ? file.path : file
  store_in_s3(path, full_oid_path(object_id))

  object_id
rescue Aws::S3::Errors::ServiceError => e
  raise BlobstoreError, "Failed to create object, S3 response error code #{e.code}: #{e.message}"
end

#delete_object(object_id) ⇒ Object

Parameters:

  • object_id (String)

    object id to delete



93
94
95
96
97
98
99
100
101
102
103
104
105
# File 'lib/blobstore_client/s3_blobstore_client.rb', line 93

def delete_object(object_id)
  raise BlobstoreError, 'unsupported action' if @simple
  object_id = full_oid_path(object_id)

  s3_object = Aws::S3::Object.new({:key => object_id}.merge(@aws_options))
  # TODO: don't blow up if we are cannot find an object we are trying to
  # delete anyway
  raise NotFound, "Object '#{object_id}' is not found" unless s3_object.exists?

  s3_object.delete
rescue Aws::S3::Errors::ServiceError => e
  raise BlobstoreError, "Failed to delete object '#{object_id}', S3 response error code #{e.code}: #{e.message}"
end

#get_file(object_id, file) ⇒ Object

Parameters:

  • object_id (String)

    object id to retrieve

  • file (File)

    file to store the retrived object in



77
78
79
80
81
82
83
84
85
86
87
88
89
90
# File 'lib/blobstore_client/s3_blobstore_client.rb', line 77

def get_file(object_id, file)
  object_id = full_oid_path(object_id)
  return @simple.get_file(object_id, file) if @simple

  s3_object = Aws::S3::Object.new({:key => object_id}.merge(@aws_options))
  s3_object.get do |chunk|
    file.write(chunk)
  end

rescue Aws::S3::Errors::NoSuchKey => e
  raise NotFound, "S3 object '#{object_id}' not found"
rescue Aws::S3::Errors::ServiceError => e
  raise BlobstoreError, "Failed to find object '#{object_id}', S3 response error code #{e.code}: #{e.message}"
end

#object_exists?(object_id) ⇒ Boolean

Returns:

  • (Boolean)


107
108
109
110
111
112
113
114
115
116
117
118
119
120
# File 'lib/blobstore_client/s3_blobstore_client.rb', line 107

def object_exists?(object_id)
  object_id = full_oid_path(object_id)
  return simple.exists?(object_id) if simple

  # Hack to get the Aws SDK to redirect to the correct region on
  # subsequent requests
  unless @region_configured
    s3 = Aws::S3::Client.new(@aws_options.reject{|k| k == :bucket_name})
    s3.list_objects({bucket: @aws_options[:bucket_name]})
    @region_configured = true
  end

  Aws::S3::Object.new({:key => object_id}.merge(@aws_options)).exists?
end