blobstore_client 0.3.13 → 0.4.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.
- data/Rakefile +0 -1
- data/lib/blobstore_client.rb +10 -6
- data/lib/blobstore_client/base.rb +5 -4
- data/lib/blobstore_client/s3_blobstore_client.rb +54 -6
- data/lib/blobstore_client/simple_blobstore_client.rb +2 -1
- data/lib/blobstore_client/version.rb +1 -1
- data/spec/unit/blobstore_client_spec.rb +5 -0
- data/spec/unit/s3_blobstore_client_spec.rb +52 -0
- metadata +56 -15
    
        data/Rakefile
    CHANGED
    
    
    
        data/lib/blobstore_client.rb
    CHANGED
    
    | @@ -2,6 +2,7 @@ | |
| 2 2 |  | 
| 3 3 | 
             
            module Bosh; module Blobstore; end; end
         | 
| 4 4 |  | 
| 5 | 
            +
            require "common/common"
         | 
| 5 6 | 
             
            require "blobstore_client/version"
         | 
| 6 7 | 
             
            require "blobstore_client/errors"
         | 
| 7 8 |  | 
| @@ -23,15 +24,18 @@ module Bosh | |
| 23 24 | 
             
                    "local" => LocalClient
         | 
| 24 25 | 
             
                  }
         | 
| 25 26 |  | 
| 26 | 
            -
                  def self.create( | 
| 27 | 
            -
                     | 
| 28 | 
            -
             | 
| 29 | 
            -
             | 
| 30 | 
            -
                    else
         | 
| 27 | 
            +
                  def self.create(blobstore_provider, options = {})
         | 
| 28 | 
            +
                    provider = PROVIDER_MAP[blobstore_provider]
         | 
| 29 | 
            +
             | 
| 30 | 
            +
                    unless provider
         | 
| 31 31 | 
             
                      providers = PROVIDER_MAP.keys.sort.join(", ")
         | 
| 32 | 
            -
                      raise  | 
| 32 | 
            +
                      raise BlobstoreError,
         | 
| 33 | 
            +
                        "Invalid client provider, available providers are: #{providers}"
         | 
| 33 34 | 
             
                    end
         | 
| 35 | 
            +
             | 
| 36 | 
            +
                    provider.new(options)
         | 
| 34 37 | 
             
                  end
         | 
| 38 | 
            +
             | 
| 35 39 | 
             
                end
         | 
| 36 40 | 
             
              end
         | 
| 37 41 | 
             
            end
         | 
| @@ -7,7 +7,7 @@ module Bosh | |
| 7 7 | 
             
                class BaseClient < Client
         | 
| 8 8 |  | 
| 9 9 | 
             
                  def initialize(options)
         | 
| 10 | 
            -
                    @options = symbolize_keys(options)
         | 
| 10 | 
            +
                    @options = Bosh::Common.symbolize_keys(options)
         | 
| 11 11 | 
             
                  end
         | 
| 12 12 |  | 
| 13 13 | 
             
                  def symbolize_keys(hash)
         | 
| @@ -34,7 +34,7 @@ module Bosh | |
| 34 34 | 
             
                          File.open(path, "w") do |file|
         | 
| 35 35 | 
             
                            file.write(contents)
         | 
| 36 36 | 
             
                          end
         | 
| 37 | 
            -
                          create_file(File.open(path, "r"))
         | 
| 37 | 
            +
                          return create_file(File.open(path, "r"))
         | 
| 38 38 | 
             
                        rescue BlobstoreError => e
         | 
| 39 39 | 
             
                          raise e
         | 
| 40 40 | 
             
                        rescue Exception => e
         | 
| @@ -72,9 +72,10 @@ module Bosh | |
| 72 72 | 
             
                  def temp_path
         | 
| 73 73 | 
             
                    path = File.join(Dir::tmpdir, "temp-path-#{UUIDTools::UUID.random_create}")
         | 
| 74 74 | 
             
                    begin
         | 
| 75 | 
            -
                      yield path
         | 
| 75 | 
            +
                      yield path if block_given?
         | 
| 76 | 
            +
                      path
         | 
| 76 77 | 
             
                    ensure
         | 
| 77 | 
            -
                      FileUtils.rm_f(path)
         | 
| 78 | 
            +
                      FileUtils.rm_f(path) if block_given?
         | 
| 78 79 | 
             
                    end
         | 
| 79 80 | 
             
                  end
         | 
| 80 81 |  | 
| @@ -11,10 +11,21 @@ module Bosh | |
| 11 11 |  | 
| 12 12 | 
             
                class S3BlobstoreClient < BaseClient
         | 
| 13 13 |  | 
| 14 | 
            +
                  ENDPOINT = "https://s3.amazonaws.com"
         | 
| 14 15 | 
             
                  DEFAULT_CIPHER_NAME = "aes-128-cbc"
         | 
| 15 16 |  | 
| 16 17 | 
             
                  attr_reader :bucket_name, :encryption_key
         | 
| 17 18 |  | 
| 19 | 
            +
                  # Blobstore client for S3 with optional object encryption
         | 
| 20 | 
            +
                  # @param [Hash] options S3connection options
         | 
| 21 | 
            +
                  # @option options [Symbol] bucket_name
         | 
| 22 | 
            +
                  # @option options [Symbol, optional] encryption_key optional encryption
         | 
| 23 | 
            +
                  #   key that is applied before the object is sent to S3
         | 
| 24 | 
            +
                  # @option options [Symbol, optional] access_key_id
         | 
| 25 | 
            +
                  # @option options [Symbol, optional] secret_access_key
         | 
| 26 | 
            +
                  # @note If access_key_id and secret_access_key are not present, the
         | 
| 27 | 
            +
                  #   blobstore client operates in read only mode as a
         | 
| 28 | 
            +
                  #   simple_blobstore_client
         | 
| 18 29 | 
             
                  def initialize(options)
         | 
| 19 30 | 
             
                    super(options)
         | 
| 20 31 | 
             
                    @bucket_name    = @options[:bucket_name]
         | 
| @@ -27,21 +38,45 @@ module Bosh | |
| 27 38 | 
             
                      :port              => 443
         | 
| 28 39 | 
             
                    }
         | 
| 29 40 |  | 
| 30 | 
            -
                     | 
| 41 | 
            +
                    # using S3 without credentials is a special case:
         | 
| 42 | 
            +
                    # it is really the simple blobstore client with a bucket name
         | 
| 43 | 
            +
                    if read_only?
         | 
| 44 | 
            +
                      unless @options[:bucket_name] || @options[:bucket]
         | 
| 45 | 
            +
                        raise BlobstoreError, "bucket name required"
         | 
| 46 | 
            +
                      end
         | 
| 47 | 
            +
                      @options[:bucket] ||= @options[:bucket_name]
         | 
| 48 | 
            +
                      @options[:endpoint] ||= S3BlobstoreClient::ENDPOINT
         | 
| 49 | 
            +
                      @simple = SimpleBlobstoreClient.new(@options)
         | 
| 50 | 
            +
                    else
         | 
| 51 | 
            +
                      AWS::S3::Base.establish_connection!(aws_options)
         | 
| 52 | 
            +
                    end
         | 
| 53 | 
            +
             | 
| 31 54 | 
             
                  rescue AWS::S3::S3Exception => e
         | 
| 32 55 | 
             
                    raise BlobstoreError, "Failed to initialize S3 blobstore: #{e.message}"
         | 
| 33 56 | 
             
                  end
         | 
| 34 57 |  | 
| 35 58 | 
             
                  def create_file(file)
         | 
| 59 | 
            +
                    raise BlobstoreError, "unsupported action" if @simple
         | 
| 60 | 
            +
             | 
| 36 61 | 
             
                    object_id = generate_object_id
         | 
| 37 | 
            -
             | 
| 38 | 
            -
             | 
| 39 | 
            -
             | 
| 62 | 
            +
             | 
| 63 | 
            +
                    if @encryption_key
         | 
| 64 | 
            +
                      temp_path do |path|
         | 
| 65 | 
            +
                        File.open(path, "w") do |temp_file|
         | 
| 66 | 
            +
                          encrypt_stream(file, temp_file)
         | 
| 67 | 
            +
                        end
         | 
| 68 | 
            +
                        File.open(path, "r") do |temp_file|
         | 
| 69 | 
            +
                          AWS::S3::S3Object.store(object_id, temp_file, bucket_name)
         | 
| 70 | 
            +
                        end
         | 
| 40 71 | 
             
                      end
         | 
| 41 | 
            -
             | 
| 72 | 
            +
                    elsif file.is_a?(String)
         | 
| 73 | 
            +
                      File.open(file, "r") do |temp_file|
         | 
| 42 74 | 
             
                        AWS::S3::S3Object.store(object_id, temp_file, bucket_name)
         | 
| 43 75 | 
             
                      end
         | 
| 76 | 
            +
                    else # Ruby 1.8 passes a File
         | 
| 77 | 
            +
                      AWS::S3::S3Object.store(object_id, file, bucket_name)
         | 
| 44 78 | 
             
                    end
         | 
| 79 | 
            +
             | 
| 45 80 | 
             
                    object_id
         | 
| 46 81 | 
             
                  rescue AWS::S3::S3Exception => e
         | 
| 47 82 | 
             
                    raise BlobstoreError,
         | 
| @@ -49,6 +84,8 @@ module Bosh | |
| 49 84 | 
             
                  end
         | 
| 50 85 |  | 
| 51 86 | 
             
                  def get_file(object_id, file)
         | 
| 87 | 
            +
                    return @simple.get_file(object_id, file) if @simple
         | 
| 88 | 
            +
             | 
| 52 89 | 
             
                    object = AWS::S3::S3Object.find(object_id, bucket_name)
         | 
| 53 90 | 
             
                    from = lambda { |callback|
         | 
| 54 91 | 
             
                      object.value { |segment|
         | 
| @@ -59,7 +96,12 @@ module Bosh | |
| 59 96 | 
             
                        end
         | 
| 60 97 | 
             
                      }
         | 
| 61 98 | 
             
                    }
         | 
| 62 | 
            -
                     | 
| 99 | 
            +
                    if @encryption_key
         | 
| 100 | 
            +
                      decrypt_stream(from, file)
         | 
| 101 | 
            +
                    else
         | 
| 102 | 
            +
                      to_stream = write_stream(file)
         | 
| 103 | 
            +
                      read_stream(from) { |segment| to_stream.call(segment) }
         | 
| 104 | 
            +
                    end
         | 
| 63 105 | 
             
                  rescue AWS::S3::NoSuchKey => e
         | 
| 64 106 | 
             
                    raise NotFound, "S3 object '#{object_id}' not found"
         | 
| 65 107 | 
             
                  rescue AWS::S3::S3Exception => e
         | 
| @@ -68,6 +110,8 @@ module Bosh | |
| 68 110 | 
             
                  end
         | 
| 69 111 |  | 
| 70 112 | 
             
                  def delete(object_id)
         | 
| 113 | 
            +
                    raise BlobstoreError, "unsupported action" if @simple
         | 
| 114 | 
            +
             | 
| 71 115 | 
             
                    AWS::S3::S3Object.delete(object_id, bucket_name)
         | 
| 72 116 | 
             
                  rescue AWS::S3::S3Exception => e
         | 
| 73 117 | 
             
                    raise BlobstoreError,
         | 
| @@ -122,6 +166,10 @@ module Bosh | |
| 122 166 | 
             
                    end
         | 
| 123 167 | 
             
                  end
         | 
| 124 168 |  | 
| 169 | 
            +
                  def read_only?
         | 
| 170 | 
            +
                    @options[:access_key_id].nil? && @options[:secret_access_key].nil?
         | 
| 171 | 
            +
                  end
         | 
| 172 | 
            +
             | 
| 125 173 | 
             
                end
         | 
| 126 174 | 
             
              end
         | 
| 127 175 | 
             
            end
         | 
| @@ -10,6 +10,7 @@ module Bosh | |
| 10 10 | 
             
                    super(options)
         | 
| 11 11 | 
             
                    @client = HTTPClient.new
         | 
| 12 12 | 
             
                    @endpoint = @options[:endpoint]
         | 
| 13 | 
            +
                    @bucket = @options[:bucket] || "resources"
         | 
| 13 14 | 
             
                    @headers = {}
         | 
| 14 15 | 
             
                    user = @options[:user]
         | 
| 15 16 | 
             
                    password = @options[:password]
         | 
| @@ -20,7 +21,7 @@ module Bosh | |
| 20 21 | 
             
                  end
         | 
| 21 22 |  | 
| 22 23 | 
             
                  def url(id=nil)
         | 
| 23 | 
            -
                    ["#{@endpoint} | 
| 24 | 
            +
                    ["#{@endpoint}/#{@bucket}", id].compact.join("/")
         | 
| 24 25 | 
             
                  end
         | 
| 25 26 |  | 
| 26 27 | 
             
                  def create_file(file)
         | 
| @@ -25,6 +25,11 @@ describe Bosh::Blobstore::Client do | |
| 25 25 | 
             
                bs.should be_instance_of Bosh::Blobstore::S3BlobstoreClient
         | 
| 26 26 | 
             
              end
         | 
| 27 27 |  | 
| 28 | 
            +
              it "should pick S3 provider when S3 is used without credentials" do
         | 
| 29 | 
            +
                bs = Bosh::Blobstore::Client.create('s3', {:bucket_name => "foo"})
         | 
| 30 | 
            +
                bs.should be_instance_of Bosh::Blobstore::S3BlobstoreClient
         | 
| 31 | 
            +
              end
         | 
| 32 | 
            +
             | 
| 28 33 | 
             
              it "should raise an exception on an unknown client" do
         | 
| 29 34 | 
             
                lambda {
         | 
| 30 35 | 
             
                  bs = Bosh::Blobstore::Client.create('foobar', {})
         | 
| @@ -15,6 +15,34 @@ describe Bosh::Blobstore::S3BlobstoreClient do | |
| 15 15 | 
             
                Bosh::Blobstore::S3BlobstoreClient.new(options)
         | 
| 16 16 | 
             
              end
         | 
| 17 17 |  | 
| 18 | 
            +
              describe "read only mode" do
         | 
| 19 | 
            +
                it "does not establish S3 connection on creation" do
         | 
| 20 | 
            +
                  AWS::S3::Base.should_not_receive(:establish_connection!)
         | 
| 21 | 
            +
                  @client = s3_blobstore("bucket_name" => "test")
         | 
| 22 | 
            +
                end
         | 
| 23 | 
            +
             | 
| 24 | 
            +
                it "should raise an error on deletion" do
         | 
| 25 | 
            +
                  @client = s3_blobstore("bucket_name" => "test")
         | 
| 26 | 
            +
                  lambda {
         | 
| 27 | 
            +
                    @client.delete("id")
         | 
| 28 | 
            +
                  }.should raise_error "unsupported action"
         | 
| 29 | 
            +
                end
         | 
| 30 | 
            +
             | 
| 31 | 
            +
                it "should raise an error on creation" do
         | 
| 32 | 
            +
                  @client = s3_blobstore("bucket_name" => "test")
         | 
| 33 | 
            +
                  lambda {
         | 
| 34 | 
            +
                    @client.create("id")
         | 
| 35 | 
            +
                  }.should raise_error "unsupported action"
         | 
| 36 | 
            +
                end
         | 
| 37 | 
            +
             | 
| 38 | 
            +
                it "should fetch objects" do
         | 
| 39 | 
            +
                  simple = mock("simple", :to_ary => nil, :get_file => %w[foo id])
         | 
| 40 | 
            +
                  Bosh::Blobstore::SimpleBlobstoreClient.should_receive(:new).and_return(simple)
         | 
| 41 | 
            +
                  @client = s3_blobstore("bucket_name" => "test")
         | 
| 42 | 
            +
                  @client.get_file("foo", "id")
         | 
| 43 | 
            +
                end
         | 
| 44 | 
            +
              end
         | 
| 45 | 
            +
             | 
| 18 46 | 
             
              describe "options" do
         | 
| 19 47 |  | 
| 20 48 | 
             
                it "establishes S3 connection on creation" do
         | 
| @@ -73,6 +101,17 @@ describe Bosh::Blobstore::S3BlobstoreClient do | |
| 73 101 | 
             
                    @client.create("some content").should eql("object_id")
         | 
| 74 102 | 
             
                  end
         | 
| 75 103 |  | 
| 104 | 
            +
                  it "should not encrypt when encryption key is missing" do
         | 
| 105 | 
            +
                    client = s3_blobstore(:bucket_name       => "test",
         | 
| 106 | 
            +
                                          :access_key_id     => "KEY",
         | 
| 107 | 
            +
                                          :secret_access_key => "SECRET")
         | 
| 108 | 
            +
                    client.should_receive(:generate_object_id).and_return("object_id")
         | 
| 109 | 
            +
                    client.should_not_receive(:encrypt_stream)
         | 
| 110 | 
            +
             | 
| 111 | 
            +
                    AWS::S3::S3Object.should_receive(:store)
         | 
| 112 | 
            +
                    client.create("some content").should eql("object_id")
         | 
| 113 | 
            +
                  end
         | 
| 114 | 
            +
             | 
| 76 115 | 
             
                  it "should raise an exception when there is an error creating an object" do
         | 
| 77 116 | 
             
                    encrypted_file = nil
         | 
| 78 117 | 
             
                    @client.should_receive(:generate_object_id).and_return("object_id")
         | 
| @@ -114,6 +153,19 @@ describe Bosh::Blobstore::S3BlobstoreClient do | |
| 114 153 | 
             
                    @client.get("object_id").should == "stuff"
         | 
| 115 154 | 
             
                  end
         | 
| 116 155 |  | 
| 156 | 
            +
                  it "should not decrypt when encryption key is missing" do
         | 
| 157 | 
            +
                    client = s3_blobstore(:bucket_name       => "test",
         | 
| 158 | 
            +
                                          :access_key_id     => "KEY",
         | 
| 159 | 
            +
                                          :secret_access_key => "SECRET")
         | 
| 160 | 
            +
             | 
| 161 | 
            +
                    mock_s3_object = mock("s3_object")
         | 
| 162 | 
            +
                    mock_s3_object.stub!(:value).and_yield("stuff")
         | 
| 163 | 
            +
                    AWS::S3::S3Object.should_receive(:find).with("object_id", "test").and_return(mock_s3_object)
         | 
| 164 | 
            +
                    client.should_not_receive(:decrypt_stream)
         | 
| 165 | 
            +
             | 
| 166 | 
            +
                    client.get("object_id").should == "stuff"
         | 
| 167 | 
            +
                  end
         | 
| 168 | 
            +
             | 
| 117 169 | 
             
                  it "should raise an exception when there is an error fetching an object" do
         | 
| 118 170 | 
             
                    AWS::S3::S3Object.should_receive(:find).with("object_id", "test").and_raise(AWS::S3::S3Exception.new("Epic Fail"))
         | 
| 119 171 | 
             
                    lambda {
         | 
    
        metadata
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            --- !ruby/object:Gem::Specification
         | 
| 2 2 | 
             
            name: blobstore_client
         | 
| 3 3 | 
             
            version: !ruby/object:Gem::Version
         | 
| 4 | 
            -
              version: 0. | 
| 4 | 
            +
              version: 0.4.0
         | 
| 5 5 | 
             
              prerelease: 
         | 
| 6 6 | 
             
            platform: ruby
         | 
| 7 7 | 
             
            authors:
         | 
| @@ -9,11 +9,11 @@ authors: | |
| 9 9 | 
             
            autorequire: 
         | 
| 10 10 | 
             
            bindir: bin
         | 
| 11 11 | 
             
            cert_chain: []
         | 
| 12 | 
            -
            date: 2012- | 
| 12 | 
            +
            date: 2012-08-16 00:00:00.000000000 Z
         | 
| 13 13 | 
             
            dependencies:
         | 
| 14 14 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 15 15 | 
             
              name: aws-s3
         | 
| 16 | 
            -
              requirement:  | 
| 16 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 17 17 | 
             
                none: false
         | 
| 18 18 | 
             
                requirements:
         | 
| 19 19 | 
             
                - - ~>
         | 
| @@ -21,10 +21,15 @@ dependencies: | |
| 21 21 | 
             
                    version: 0.6.2
         | 
| 22 22 | 
             
              type: :runtime
         | 
| 23 23 | 
             
              prerelease: false
         | 
| 24 | 
            -
              version_requirements:  | 
| 24 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 25 | 
            +
                none: false
         | 
| 26 | 
            +
                requirements:
         | 
| 27 | 
            +
                - - ~>
         | 
| 28 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 29 | 
            +
                    version: 0.6.2
         | 
| 25 30 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 26 31 | 
             
              name: httpclient
         | 
| 27 | 
            -
              requirement:  | 
| 32 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 28 33 | 
             
                none: false
         | 
| 29 34 | 
             
                requirements:
         | 
| 30 35 | 
             
                - - ! '>='
         | 
| @@ -32,10 +37,15 @@ dependencies: | |
| 32 37 | 
             
                    version: '2.2'
         | 
| 33 38 | 
             
              type: :runtime
         | 
| 34 39 | 
             
              prerelease: false
         | 
| 35 | 
            -
              version_requirements:  | 
| 40 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 41 | 
            +
                none: false
         | 
| 42 | 
            +
                requirements:
         | 
| 43 | 
            +
                - - ! '>='
         | 
| 44 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 45 | 
            +
                    version: '2.2'
         | 
| 36 46 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 37 47 | 
             
              name: multi_json
         | 
| 38 | 
            -
              requirement:  | 
| 48 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 39 49 | 
             
                none: false
         | 
| 40 50 | 
             
                requirements:
         | 
| 41 51 | 
             
                - - ~>
         | 
| @@ -43,10 +53,15 @@ dependencies: | |
| 43 53 | 
             
                    version: 1.1.0
         | 
| 44 54 | 
             
              type: :runtime
         | 
| 45 55 | 
             
              prerelease: false
         | 
| 46 | 
            -
              version_requirements:  | 
| 56 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 57 | 
            +
                none: false
         | 
| 58 | 
            +
                requirements:
         | 
| 59 | 
            +
                - - ~>
         | 
| 60 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 61 | 
            +
                    version: 1.1.0
         | 
| 47 62 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 48 63 | 
             
              name: ruby-atmos-pure
         | 
| 49 | 
            -
              requirement:  | 
| 64 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 50 65 | 
             
                none: false
         | 
| 51 66 | 
             
                requirements:
         | 
| 52 67 | 
             
                - - ~>
         | 
| @@ -54,18 +69,44 @@ dependencies: | |
| 54 69 | 
             
                    version: 1.0.5
         | 
| 55 70 | 
             
              type: :runtime
         | 
| 56 71 | 
             
              prerelease: false
         | 
| 57 | 
            -
              version_requirements:  | 
| 72 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 73 | 
            +
                none: false
         | 
| 74 | 
            +
                requirements:
         | 
| 75 | 
            +
                - - ~>
         | 
| 76 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 77 | 
            +
                    version: 1.0.5
         | 
| 58 78 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 59 79 | 
             
              name: uuidtools
         | 
| 60 | 
            -
              requirement:  | 
| 80 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 81 | 
            +
                none: false
         | 
| 82 | 
            +
                requirements:
         | 
| 83 | 
            +
                - - ~>
         | 
| 84 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 85 | 
            +
                    version: 2.1.2
         | 
| 86 | 
            +
              type: :runtime
         | 
| 87 | 
            +
              prerelease: false
         | 
| 88 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 61 89 | 
             
                none: false
         | 
| 62 90 | 
             
                requirements:
         | 
| 63 91 | 
             
                - - ~>
         | 
| 64 92 | 
             
                  - !ruby/object:Gem::Version
         | 
| 65 93 | 
             
                    version: 2.1.2
         | 
| 94 | 
            +
            - !ruby/object:Gem::Dependency
         | 
| 95 | 
            +
              name: bosh_common
         | 
| 96 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 97 | 
            +
                none: false
         | 
| 98 | 
            +
                requirements:
         | 
| 99 | 
            +
                - - ~>
         | 
| 100 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 101 | 
            +
                    version: '0.5'
         | 
| 66 102 | 
             
              type: :runtime
         | 
| 67 103 | 
             
              prerelease: false
         | 
| 68 | 
            -
              version_requirements:  | 
| 104 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 105 | 
            +
                none: false
         | 
| 106 | 
            +
                requirements:
         | 
| 107 | 
            +
                - - ~>
         | 
| 108 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 109 | 
            +
                    version: '0.5'
         | 
| 69 110 | 
             
            description: BOSH blobstore client
         | 
| 70 111 | 
             
            email: support@vmware.com
         | 
| 71 112 | 
             
            executables: []
         | 
| @@ -104,7 +145,7 @@ required_ruby_version: !ruby/object:Gem::Requirement | |
| 104 145 | 
             
                  version: '0'
         | 
| 105 146 | 
             
                  segments:
         | 
| 106 147 | 
             
                  - 0
         | 
| 107 | 
            -
                  hash:  | 
| 148 | 
            +
                  hash: 4134087797262691141
         | 
| 108 149 | 
             
            required_rubygems_version: !ruby/object:Gem::Requirement
         | 
| 109 150 | 
             
              none: false
         | 
| 110 151 | 
             
              requirements:
         | 
| @@ -113,10 +154,10 @@ required_rubygems_version: !ruby/object:Gem::Requirement | |
| 113 154 | 
             
                  version: '0'
         | 
| 114 155 | 
             
                  segments:
         | 
| 115 156 | 
             
                  - 0
         | 
| 116 | 
            -
                  hash:  | 
| 157 | 
            +
                  hash: 4134087797262691141
         | 
| 117 158 | 
             
            requirements: []
         | 
| 118 159 | 
             
            rubyforge_project: 
         | 
| 119 | 
            -
            rubygems_version: 1.8. | 
| 160 | 
            +
            rubygems_version: 1.8.24
         | 
| 120 161 | 
             
            signing_key: 
         | 
| 121 162 | 
             
            specification_version: 3
         | 
| 122 163 | 
             
            summary: BOSH blobstore client
         |