bits_service_client 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 2f4eb4e5a23a650a3ad048bf27d642ab8226a8a2
4
+ data.tar.gz: d60a71be08a7c33e674f8e816af57e8c12fc1626
5
+ SHA512:
6
+ metadata.gz: 4158d05769b200bffd1fd94c1df4b5f94ef9da8a9f3292752b53251e5484ae5c3bc75dbe72850dfd050d53657e1890ce17fc325fbcd34182451abc8fefcdca5c
7
+ data.tar.gz: 0ac0d9c991dcba9d2b95da18f992ca0f0e1d8e78e194366a16432674d51e2e941432a70f2b0229efa221f0863e892a1c7e08fc640a28203147839a465a1c145c
data/.gitignore ADDED
@@ -0,0 +1,9 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --color --tty --format=documentation
data/Gemfile ADDED
@@ -0,0 +1,2 @@
1
+ source 'https://rubygems.org'
2
+ gemspec
data/Guardfile ADDED
@@ -0,0 +1,18 @@
1
+ guard :bundler do
2
+ require 'guard/bundler'
3
+ require 'guard/bundler/verify'
4
+ helper = Guard::Bundler::Verify.new
5
+
6
+ files = ['Gemfile']
7
+ files += Dir['*.gemspec'] if files.any? { |f| helper.uses_gemspec?(f) }
8
+
9
+ # Assume files are symlinked from somewhere
10
+ files.each { |file| watch(helper.real_path(file)) }
11
+ end
12
+
13
+ guard :rspec, cmd: 'bundle exec rspec' do
14
+ watch(%r{^spec/.+_spec\.rb$})
15
+ watch(%r{^lib/bits_service_client/(.+)\.rb$}){|m| "spec/unit/bits_service_client/#{m[1]}_spec.rb"}
16
+ watch(%r{^lib/bits_service_client/client.rb$}){|m| "spec/unit/bits_service_client/bits_service_client_spec.rb"}
17
+ watch('spec/spec_helper.rb') { 'spec' }
18
+ end
data/README.md ADDED
@@ -0,0 +1,12 @@
1
+ # Bits-Service Client
2
+
3
+ Ruby client for [bits-service](https://github.com/cloudfoundry-incubator/bits-service).
4
+
5
+ # Changelog
6
+
7
+ * 0.2.0:
8
+ - Add `vcap_request_id` to `BitsService::Client` and `BitsService::ResourcePool`
9
+ - Add `request_timeout_in_seconds` to `BitsService::ResourcePool`
10
+ - Move unit tests from CloudController to this gem
11
+
12
+ * 0.1.0: Initial version
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rspec/core/rake_task'
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
@@ -0,0 +1,32 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'bits_service_client/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "bits_service_client"
8
+ spec.version = BitsServiceClient::VERSION
9
+ spec.authors = ['Rizwan Reza', 'Steffen Uhlig', 'Peter Goetz']
10
+ spec.email = ["rizwanreza@gmail.com", 'steffen.uhlig@de.ibm.com', 'peter.gtz@gmail.com']
11
+
12
+ spec.summary = %q{Bits Services client for Cloud Foundry}
13
+ spec.homepage = "http://github.com/cloudfoundry/bits_service_client"
14
+
15
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
16
+ spec.require_paths = ["lib"]
17
+
18
+ spec.add_dependency "steno"
19
+ spec.add_dependency "activesupport"
20
+
21
+ spec.add_development_dependency "bundler", "~> 1.11"
22
+ spec.add_development_dependency "rake", "~> 10.0"
23
+ spec.add_development_dependency "rspec", "~> 3.0"
24
+ spec.add_development_dependency "webmock", '= 1.20.4'
25
+ spec.add_development_dependency "multipart-post"
26
+ spec.add_development_dependency "rack-test"
27
+ spec.add_development_dependency "guard"
28
+ spec.add_development_dependency "guard-bundler"
29
+ spec.add_development_dependency "guard-rspec"
30
+ spec.add_development_dependency 'terminal-notifier-guard'
31
+ spec.add_development_dependency 'terminal-notifier'
32
+ end
@@ -0,0 +1,15 @@
1
+ module BitsService
2
+ class Blob
3
+ attr_reader :guid, :public_download_url, :internal_download_url
4
+
5
+ def initialize(guid:, public_download_url:, internal_download_url:)
6
+ @guid = guid
7
+ @public_download_url = public_download_url
8
+ @internal_download_url = internal_download_url
9
+ end
10
+
11
+ def attributes(*_)
12
+ []
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,164 @@
1
+ require 'active_support/inflector'
2
+ require 'bits_service_client/logging_http_client'
3
+
4
+ module BitsService
5
+ class Client
6
+ ResourceTypeNotPresent = Class.new(StandardError)
7
+ ConfigurationError = Class.new(StandardError)
8
+
9
+ def initialize(bits_service_options:, resource_type:, vcap_request_id: '')
10
+ @username = validated(bits_service_options, :username)
11
+ @password = validated(bits_service_options, :password)
12
+ @private_endpoint = validated_http_url(bits_service_options, :private_endpoint)
13
+ @public_endpoint = validated_http_url(bits_service_options, :public_endpoint)
14
+
15
+ raise ResourceTypeNotPresent.new('Must specify resource type') unless resource_type
16
+ @resource_type = resource_type
17
+ @vcap_request_id = vcap_request_id
18
+
19
+ @private_http_client = LoggingHttpClient.new(Net::HTTP.new(@private_endpoint.host, @private_endpoint.port))
20
+ @public_http_client = LoggingHttpClient.new(Net::HTTP.new(@public_endpoint.host, @public_endpoint.port))
21
+ end
22
+
23
+ def local?
24
+ false
25
+ end
26
+
27
+ def exists?(key)
28
+ response = @private_http_client.head(resource_path(key), @vcap_request_id)
29
+ validate_response_code!([200, 302, 404], response)
30
+
31
+ response.code.to_i != 404
32
+ end
33
+
34
+ def cp_to_blobstore(source_path, destination_key)
35
+ filename = File.basename(source_path)
36
+ with_file_attachment!(source_path, filename) do |file_attachment|
37
+ body = { :"#{@resource_type.to_s.singularize}" => file_attachment }
38
+ response = @private_http_client.do_request(Net::HTTP::Put::Multipart.new(resource_path(destination_key), body), @vcap_request_id)
39
+ validate_response_code!(201, response)
40
+ end
41
+ end
42
+
43
+ def download_from_blobstore(source_key, destination_path, mode: nil)
44
+ FileUtils.mkdir_p(File.dirname(destination_path))
45
+ File.open(destination_path, 'wb') do |file|
46
+ response = @private_http_client.get(resource_path(source_key), @vcap_request_id)
47
+ validate_response_code!(200, response)
48
+ file.write(response.body)
49
+ file.chmod(mode) if mode
50
+ end
51
+ end
52
+
53
+ def cp_file_between_keys(source_key, destination_key)
54
+ temp_downloaded_file = Tempfile.new('foo')
55
+ download_from_blobstore(source_key, temp_downloaded_file.path)
56
+ cp_to_blobstore(temp_downloaded_file.path, destination_key)
57
+ end
58
+
59
+ def delete(key)
60
+ response = @private_http_client.delete(resource_path(key), @vcap_request_id)
61
+ validate_response_code!([204, 404], response)
62
+
63
+ if response.code.to_i == 404
64
+ raise FileNotFound.new("Could not find object '#{key}', #{response.code}/#{response.body}")
65
+ end
66
+ end
67
+
68
+ def blob(key)
69
+ response = @private_http_client.get('/sign' + resource_path(key), @vcap_request_id, {username: @username, password: @password})
70
+ validate_response_code!([200, 302], response)
71
+
72
+ response.tap do |response|
73
+ response.body = response['location'] if response.code.to_i == 302
74
+ end
75
+
76
+ Blob.new(
77
+ guid: key,
78
+ public_download_url: response.body,
79
+ internal_download_url: generate_private_url(key)
80
+ )
81
+ end
82
+
83
+ def delete_blob(blob)
84
+ delete(blob.guid)
85
+ end
86
+
87
+ def delete_all(_=nil)
88
+ raise NotImplementedError unless :buildpack_cache == resource_type
89
+
90
+ @private_http_client.delete(resource_path(''), @vcap_request_id).tap do |response|
91
+ validate_response_code!(204, response)
92
+ end
93
+ end
94
+
95
+ def delete_all_in_path(path)
96
+ raise NotImplementedError unless :buildpack_cache == resource_type
97
+
98
+ @private_http_client.delete(resource_path(path.to_s), @vcap_request_id).tap do |response|
99
+ validate_response_code!(204, response)
100
+ end
101
+ end
102
+
103
+ private
104
+
105
+ attr_reader :resource_type
106
+
107
+ def generate_private_url(guid)
108
+ path = resource_path(guid)
109
+
110
+ @private_http_client.head(path, @vcap_request_id).tap do |response|
111
+ return response['location'] if response.code.to_i == 302
112
+ end
113
+
114
+ File.join(@private_endpoint.to_s, path)
115
+ end
116
+
117
+ def validate_response_code!(expected_codes, response)
118
+ return if Array(expected_codes).include?(response.code.to_i)
119
+
120
+ error = {
121
+ response_code: response.code,
122
+ response_body: response.body,
123
+ response: response
124
+ }.to_json
125
+
126
+ logger.error("UnexpectedResponseCode: expected '#{expected_codes}' got #{error}")
127
+
128
+ fail BlobstoreError.new(error)
129
+ end
130
+
131
+ def resource_path(guid)
132
+ prefix = resource_type == :buildpack_cache ? 'buildpack_cache/entries/' : resource_type
133
+ File.join('/', prefix.to_s, guid.to_s)
134
+ end
135
+
136
+ def with_file_attachment!(file_path, filename, &block)
137
+ raise Errors::FileDoesNotExist.new("Could not find file: #{file_path}") unless File.exist?(file_path)
138
+
139
+ File.open(file_path) do |file|
140
+ attached_file = UploadIO.new(file, 'application/octet-stream', filename)
141
+ yield attached_file
142
+ end
143
+ end
144
+
145
+ def endpoint(http_client)
146
+ http_client == @public_http_client ? @public_endpoint : @private_endpoint
147
+ end
148
+
149
+ def logger
150
+ @logger ||= Steno.logger('cc.bits_service_client')
151
+ end
152
+
153
+ def validated_http_url(bits_service_options, attribute)
154
+ URI.parse(validated(bits_service_options, attribute)).tap do |uri|
155
+ raise ConfigurationError.new("Please provide valid http(s) #{attribute}") unless uri.scheme&.match /https?/
156
+ end
157
+ end
158
+
159
+ def validated(bits_service_options, attribute)
160
+ raise ConfigurationError.new("Please provide #{attribute}") unless bits_service_options[attribute]
161
+ bits_service_options[attribute]
162
+ end
163
+ end
164
+ end
@@ -0,0 +1,7 @@
1
+ module BitsService
2
+ module Errors
3
+ class Error < StandardError; end
4
+ class FileDoesNotExist < Error; end
5
+ class UnexpectedResponseCode < Error; end
6
+ end
7
+ end
@@ -0,0 +1,45 @@
1
+ module BitsService
2
+ class LoggingHttpClient
3
+ def initialize(http_client)
4
+ @http_client = http_client
5
+ @logger = Steno.logger('cc.bits_service_client')
6
+ end
7
+
8
+ def get(path, vcap_request_id, credentials=nil)
9
+ req = Net::HTTP::Get.new(path)
10
+ if credentials
11
+ req.basic_auth(credentials[:username], credentials[:password])
12
+ end
13
+ do_request(req, vcap_request_id)
14
+ end
15
+
16
+ def head(path, vcap_request_id)
17
+ do_request(Net::HTTP::Head.new(path), vcap_request_id)
18
+ end
19
+
20
+ def put(path, body, vcap_request_id)
21
+ do_request(Net::HTTP::Put::Multipart.new(path, body), vcap_request_id)
22
+ end
23
+
24
+ def delete(path, vcap_request_id)
25
+ do_request(Net::HTTP::Delete.new(path), vcap_request_id)
26
+ end
27
+
28
+ def do_request(request, vcap_request_id)
29
+ @logger.info('Request', {
30
+ method: request.method,
31
+ path: request.path,
32
+ address: @http_client.address,
33
+ port: @http_client.port,
34
+ vcap_request_id: vcap_request_id,
35
+ })
36
+
37
+ request.add_field('X_VCAP_REQUEST_ID', vcap_request_id)
38
+
39
+ @http_client.request(request).tap do |response|
40
+ @logger.info('Response', { code: response.code, vcap_request_id: vcap_request_id })
41
+ end
42
+ end
43
+
44
+ end
45
+ end
@@ -0,0 +1,99 @@
1
+ module BitsService
2
+ class ResourcePool
3
+ def initialize(endpoint:, request_timeout_in_seconds:, vcap_request_id: '')
4
+ @endpoint = URI.parse(endpoint)
5
+ @request_timeout_in_seconds = request_timeout_in_seconds
6
+ @vcap_request_id = vcap_request_id
7
+ @logger = Steno.logger('cc.bits_service_client')
8
+ end
9
+
10
+ def matches(resources_json)
11
+ post('/app_stash/matches', resources_json, @vcap_request_id).tap do |response|
12
+ validate_response_code!(200, response)
13
+ end
14
+ end
15
+
16
+ def upload_entries(entries_path)
17
+ with_file_attachment!(entries_path, 'entries.zip') do |file_attachment|
18
+ body = { application: file_attachment }
19
+ multipart_post('/app_stash/entries', body, @vcap_request_id)
20
+ end
21
+ end
22
+
23
+ def bundles(resources_json)
24
+ post('/app_stash/bundles', resources_json, @vcap_request_id).tap do |response|
25
+ validate_response_code!(200, response)
26
+ end
27
+ end
28
+
29
+ private
30
+
31
+ attr_reader :endpoint
32
+
33
+ def validate_response_code!(expected, response)
34
+ return if expected.to_i == response.code.to_i
35
+
36
+ error = {
37
+ response_code: response.code,
38
+ response_body: response.body,
39
+ response: response
40
+ }.to_json
41
+
42
+ @logger.error("UnexpectedResponseCode: expected #{expected} got #{error}")
43
+
44
+ fail Errors::UnexpectedResponseCode.new(error)
45
+ end
46
+
47
+ def with_file_attachment!(file_path, filename, &block)
48
+ validate_file! file_path
49
+
50
+ File.open(file_path) do |file|
51
+ attached_file = UploadIO.new(file, 'application/octet-stream', filename)
52
+ yield attached_file
53
+ end
54
+ end
55
+
56
+ def validate_file!(file_path)
57
+ return if File.exist?(file_path)
58
+
59
+ raise Errors::FileDoesNotExist.new("Could not find file: #{file_path}")
60
+ end
61
+
62
+ def post(path, body, vcap_request_id)
63
+ request = Net::HTTP::Post.new(path)
64
+
65
+ request.body = body
66
+ do_request(http_client, request, vcap_request_id)
67
+ end
68
+
69
+ def multipart_post(path, body, vcap_request_id)
70
+ request = Net::HTTP::Post::Multipart.new(path, body)
71
+ do_request(http_client, request, vcap_request_id).tap do |response|
72
+ validate_response_code!(201, response)
73
+ end
74
+ end
75
+
76
+ def do_request(http_client, request, vcap_request_id)
77
+ @logger.info('Request', {
78
+ method: request.method,
79
+ path: request.path,
80
+ address: http_client.address,
81
+ port: http_client.port,
82
+ vcap_request_id: vcap_request_id,
83
+ })
84
+
85
+ request.add_field('X_VCAP_REQUEST_ID', vcap_request_id)
86
+
87
+ http_client.request(request).tap do |response|
88
+ @logger.info('Response', {
89
+ code: response.code,
90
+ vcap_request_id: vcap_request_id,
91
+ })
92
+ end
93
+ end
94
+
95
+ def http_client
96
+ @http_client ||= Net::HTTP.new(endpoint.host, endpoint.port).tap { |c| c.read_timeout = @request_timeout_in_seconds }
97
+ end
98
+ end
99
+ end
@@ -0,0 +1,3 @@
1
+ module BitsServiceClient
2
+ VERSION = '0.2.1'
3
+ end
@@ -0,0 +1,13 @@
1
+ require 'json'
2
+ require 'net/http/post/multipart'
3
+
4
+ require "bits_service_client/version"
5
+ require "bits_service_client/blob"
6
+ require "bits_service_client/errors"
7
+ require "bits_service_client/client"
8
+ require "bits_service_client/resource_pool"
9
+
10
+ module BitsService
11
+ BlobstoreError = Class.new(StandardError)
12
+ FileNotFound = Class.new(StandardError)
13
+ end
metadata ADDED
@@ -0,0 +1,243 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: bits_service_client
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.2.1
5
+ platform: ruby
6
+ authors:
7
+ - Rizwan Reza
8
+ - Steffen Uhlig
9
+ - Peter Goetz
10
+ autorequire:
11
+ bindir: bin
12
+ cert_chain: []
13
+ date: 2016-11-16 00:00:00.000000000 Z
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: steno
17
+ requirement: !ruby/object:Gem::Requirement
18
+ requirements:
19
+ - - ">="
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ requirements:
26
+ - - ">="
27
+ - !ruby/object:Gem::Version
28
+ version: '0'
29
+ - !ruby/object:Gem::Dependency
30
+ name: activesupport
31
+ requirement: !ruby/object:Gem::Requirement
32
+ requirements:
33
+ - - ">="
34
+ - !ruby/object:Gem::Version
35
+ version: '0'
36
+ type: :runtime
37
+ prerelease: false
38
+ version_requirements: !ruby/object:Gem::Requirement
39
+ requirements:
40
+ - - ">="
41
+ - !ruby/object:Gem::Version
42
+ version: '0'
43
+ - !ruby/object:Gem::Dependency
44
+ name: bundler
45
+ requirement: !ruby/object:Gem::Requirement
46
+ requirements:
47
+ - - "~>"
48
+ - !ruby/object:Gem::Version
49
+ version: '1.11'
50
+ type: :development
51
+ prerelease: false
52
+ version_requirements: !ruby/object:Gem::Requirement
53
+ requirements:
54
+ - - "~>"
55
+ - !ruby/object:Gem::Version
56
+ version: '1.11'
57
+ - !ruby/object:Gem::Dependency
58
+ name: rake
59
+ requirement: !ruby/object:Gem::Requirement
60
+ requirements:
61
+ - - "~>"
62
+ - !ruby/object:Gem::Version
63
+ version: '10.0'
64
+ type: :development
65
+ prerelease: false
66
+ version_requirements: !ruby/object:Gem::Requirement
67
+ requirements:
68
+ - - "~>"
69
+ - !ruby/object:Gem::Version
70
+ version: '10.0'
71
+ - !ruby/object:Gem::Dependency
72
+ name: rspec
73
+ requirement: !ruby/object:Gem::Requirement
74
+ requirements:
75
+ - - "~>"
76
+ - !ruby/object:Gem::Version
77
+ version: '3.0'
78
+ type: :development
79
+ prerelease: false
80
+ version_requirements: !ruby/object:Gem::Requirement
81
+ requirements:
82
+ - - "~>"
83
+ - !ruby/object:Gem::Version
84
+ version: '3.0'
85
+ - !ruby/object:Gem::Dependency
86
+ name: webmock
87
+ requirement: !ruby/object:Gem::Requirement
88
+ requirements:
89
+ - - '='
90
+ - !ruby/object:Gem::Version
91
+ version: 1.20.4
92
+ type: :development
93
+ prerelease: false
94
+ version_requirements: !ruby/object:Gem::Requirement
95
+ requirements:
96
+ - - '='
97
+ - !ruby/object:Gem::Version
98
+ version: 1.20.4
99
+ - !ruby/object:Gem::Dependency
100
+ name: multipart-post
101
+ requirement: !ruby/object:Gem::Requirement
102
+ requirements:
103
+ - - ">="
104
+ - !ruby/object:Gem::Version
105
+ version: '0'
106
+ type: :development
107
+ prerelease: false
108
+ version_requirements: !ruby/object:Gem::Requirement
109
+ requirements:
110
+ - - ">="
111
+ - !ruby/object:Gem::Version
112
+ version: '0'
113
+ - !ruby/object:Gem::Dependency
114
+ name: rack-test
115
+ requirement: !ruby/object:Gem::Requirement
116
+ requirements:
117
+ - - ">="
118
+ - !ruby/object:Gem::Version
119
+ version: '0'
120
+ type: :development
121
+ prerelease: false
122
+ version_requirements: !ruby/object:Gem::Requirement
123
+ requirements:
124
+ - - ">="
125
+ - !ruby/object:Gem::Version
126
+ version: '0'
127
+ - !ruby/object:Gem::Dependency
128
+ name: guard
129
+ requirement: !ruby/object:Gem::Requirement
130
+ requirements:
131
+ - - ">="
132
+ - !ruby/object:Gem::Version
133
+ version: '0'
134
+ type: :development
135
+ prerelease: false
136
+ version_requirements: !ruby/object:Gem::Requirement
137
+ requirements:
138
+ - - ">="
139
+ - !ruby/object:Gem::Version
140
+ version: '0'
141
+ - !ruby/object:Gem::Dependency
142
+ name: guard-bundler
143
+ requirement: !ruby/object:Gem::Requirement
144
+ requirements:
145
+ - - ">="
146
+ - !ruby/object:Gem::Version
147
+ version: '0'
148
+ type: :development
149
+ prerelease: false
150
+ version_requirements: !ruby/object:Gem::Requirement
151
+ requirements:
152
+ - - ">="
153
+ - !ruby/object:Gem::Version
154
+ version: '0'
155
+ - !ruby/object:Gem::Dependency
156
+ name: guard-rspec
157
+ requirement: !ruby/object:Gem::Requirement
158
+ requirements:
159
+ - - ">="
160
+ - !ruby/object:Gem::Version
161
+ version: '0'
162
+ type: :development
163
+ prerelease: false
164
+ version_requirements: !ruby/object:Gem::Requirement
165
+ requirements:
166
+ - - ">="
167
+ - !ruby/object:Gem::Version
168
+ version: '0'
169
+ - !ruby/object:Gem::Dependency
170
+ name: terminal-notifier-guard
171
+ requirement: !ruby/object:Gem::Requirement
172
+ requirements:
173
+ - - ">="
174
+ - !ruby/object:Gem::Version
175
+ version: '0'
176
+ type: :development
177
+ prerelease: false
178
+ version_requirements: !ruby/object:Gem::Requirement
179
+ requirements:
180
+ - - ">="
181
+ - !ruby/object:Gem::Version
182
+ version: '0'
183
+ - !ruby/object:Gem::Dependency
184
+ name: terminal-notifier
185
+ requirement: !ruby/object:Gem::Requirement
186
+ requirements:
187
+ - - ">="
188
+ - !ruby/object:Gem::Version
189
+ version: '0'
190
+ type: :development
191
+ prerelease: false
192
+ version_requirements: !ruby/object:Gem::Requirement
193
+ requirements:
194
+ - - ">="
195
+ - !ruby/object:Gem::Version
196
+ version: '0'
197
+ description:
198
+ email:
199
+ - rizwanreza@gmail.com
200
+ - steffen.uhlig@de.ibm.com
201
+ - peter.gtz@gmail.com
202
+ executables: []
203
+ extensions: []
204
+ extra_rdoc_files: []
205
+ files:
206
+ - ".gitignore"
207
+ - ".rspec"
208
+ - Gemfile
209
+ - Guardfile
210
+ - README.md
211
+ - Rakefile
212
+ - bits_service_client.gemspec
213
+ - lib/bits_service_client.rb
214
+ - lib/bits_service_client/blob.rb
215
+ - lib/bits_service_client/client.rb
216
+ - lib/bits_service_client/errors.rb
217
+ - lib/bits_service_client/logging_http_client.rb
218
+ - lib/bits_service_client/resource_pool.rb
219
+ - lib/bits_service_client/version.rb
220
+ homepage: http://github.com/cloudfoundry/bits_service_client
221
+ licenses: []
222
+ metadata: {}
223
+ post_install_message:
224
+ rdoc_options: []
225
+ require_paths:
226
+ - lib
227
+ required_ruby_version: !ruby/object:Gem::Requirement
228
+ requirements:
229
+ - - ">="
230
+ - !ruby/object:Gem::Version
231
+ version: '0'
232
+ required_rubygems_version: !ruby/object:Gem::Requirement
233
+ requirements:
234
+ - - ">="
235
+ - !ruby/object:Gem::Version
236
+ version: '0'
237
+ requirements: []
238
+ rubyforge_project:
239
+ rubygems_version: 2.5.1
240
+ signing_key:
241
+ specification_version: 4
242
+ summary: Bits Services client for Cloud Foundry
243
+ test_files: []