bits_service_client 0.2.1

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 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: []