hula 0.7.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 +7 -0
- data/LEGAL +13 -0
- data/lib/hula/bosh_director.rb +246 -0
- data/lib/hula/bosh_manifest/job.rb +35 -0
- data/lib/hula/bosh_manifest.rb +102 -0
- data/lib/hula/cloud_foundry/service_broker.rb +36 -0
- data/lib/hula/cloud_foundry.rb +308 -0
- data/lib/hula/command_runner.rb +38 -0
- data/lib/hula/helpers/socket_tools.rb +44 -0
- data/lib/hula/helpers/timeout_tools.rb +27 -0
- data/lib/hula/http_proxy_upstream_socks.rb +72 -0
- data/lib/hula/service_broker/api.rb +123 -0
- data/lib/hula/service_broker/catalog.rb +43 -0
- data/lib/hula/service_broker/client.rb +69 -0
- data/lib/hula/service_broker/errors.rb +24 -0
- data/lib/hula/service_broker/http_json_client.rb +90 -0
- data/lib/hula/service_broker/instance_binding.rb +30 -0
- data/lib/hula/service_broker/plan.rb +32 -0
- data/lib/hula/service_broker/service.rb +47 -0
- data/lib/hula/service_broker/service_instance.rb +24 -0
- data/lib/hula/socks4_proxy_ssh.rb +118 -0
- data/lib/hula/version.rb +13 -0
- data/lib/hula.rb +16 -0
- metadata +235 -0
@@ -0,0 +1,308 @@
|
|
1
|
+
# Copyright (c) 2014-2015 Pivotal Software, Inc.
|
2
|
+
# All rights reserved.
|
3
|
+
# THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
4
|
+
# INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
5
|
+
# PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
6
|
+
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
7
|
+
# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
8
|
+
# USE OR OTHER DEALINGS IN THE SOFTWARE.
|
9
|
+
#
|
10
|
+
|
11
|
+
require 'tmpdir'
|
12
|
+
require 'json'
|
13
|
+
require 'open3'
|
14
|
+
require 'tempfile'
|
15
|
+
|
16
|
+
require 'hula/command_runner'
|
17
|
+
require 'hula/cloud_foundry/service_broker'
|
18
|
+
|
19
|
+
module Hula
|
20
|
+
class CloudFoundry
|
21
|
+
attr_reader :current_organization, :current_space, :domain, :api_url
|
22
|
+
|
23
|
+
def initialize(args)
|
24
|
+
@domain = args.fetch(:domain)
|
25
|
+
@api_url = args.fetch(:api_url)
|
26
|
+
@logger = args.fetch(:logger, default_logger)
|
27
|
+
@command_runner = args.fetch(:command_runner, default_command_runner)
|
28
|
+
|
29
|
+
target_and_login = args.fetch(:target_and_login, true)
|
30
|
+
if target_and_login
|
31
|
+
target(api_url)
|
32
|
+
login(args.fetch(:username), args.fetch(:password))
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def url_for_app(app_name)
|
37
|
+
"https://#{app_name}.#{domain}"
|
38
|
+
end
|
39
|
+
|
40
|
+
def target(cloud_controller_url)
|
41
|
+
cf("api #{cloud_controller_url} --skip-ssl-validation")
|
42
|
+
end
|
43
|
+
|
44
|
+
def app_vcap_services(app_name)
|
45
|
+
app_environment(app_name)["VCAP_SERVICES"]
|
46
|
+
end
|
47
|
+
|
48
|
+
def login(username, password, allow_failure = true)
|
49
|
+
cf("auth #{username} #{password}", allow_failure: allow_failure)
|
50
|
+
end
|
51
|
+
|
52
|
+
def service_brokers
|
53
|
+
output = cf('service-brokers')
|
54
|
+
|
55
|
+
if output.include?('No service brokers found')
|
56
|
+
[]
|
57
|
+
else
|
58
|
+
output.split("\n").drop(3).map do |row|
|
59
|
+
name, url = row.split(/\s+/)
|
60
|
+
ServiceBroker.new(name: name, url: url)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def create_and_target_org(name)
|
66
|
+
create_org(name)
|
67
|
+
sleep 1
|
68
|
+
target_org(name)
|
69
|
+
end
|
70
|
+
|
71
|
+
def create_org(name)
|
72
|
+
cf("create-org #{name}")
|
73
|
+
end
|
74
|
+
|
75
|
+
def target_org(name)
|
76
|
+
cf("target -o #{name}")
|
77
|
+
@current_organization = name
|
78
|
+
end
|
79
|
+
|
80
|
+
def create_and_target_space(name)
|
81
|
+
create_space(name)
|
82
|
+
target_space(name)
|
83
|
+
end
|
84
|
+
|
85
|
+
def create_space(name)
|
86
|
+
cf("create-space #{name}")
|
87
|
+
end
|
88
|
+
|
89
|
+
def target_space(name)
|
90
|
+
cf("target -s #{name}")
|
91
|
+
@current_space = name
|
92
|
+
end
|
93
|
+
|
94
|
+
def space_exists?(name)
|
95
|
+
spaces = cf('spaces').lines[3..-1]
|
96
|
+
spaces.map(&:strip).include?(name)
|
97
|
+
end
|
98
|
+
|
99
|
+
def org_exists?(name)
|
100
|
+
orgs = cf('orgs').lines[3..-1]
|
101
|
+
orgs.map(&:strip).include?(name)
|
102
|
+
end
|
103
|
+
|
104
|
+
def setup_permissive_security_group(org, space)
|
105
|
+
rules = [{
|
106
|
+
'destination' => '0.0.0.0-255.255.255.255',
|
107
|
+
'protocol' => 'all'
|
108
|
+
}]
|
109
|
+
|
110
|
+
rule_file = Tempfile.new('default_security_group.json')
|
111
|
+
rule_file.write(rules.to_json)
|
112
|
+
rule_file.close
|
113
|
+
|
114
|
+
cf("create-security-group prof-test #{rule_file.path}")
|
115
|
+
cf("bind-security-group prof-test #{org} #{space}")
|
116
|
+
cf('bind-staging-security-group prof-test')
|
117
|
+
cf('bind-running-security-group prof-test')
|
118
|
+
|
119
|
+
rule_file.unlink
|
120
|
+
end
|
121
|
+
|
122
|
+
def delete_space(name, options = {})
|
123
|
+
allow_failure = options.fetch(:allow_failure, true)
|
124
|
+
cf("delete-space #{name} -f", allow_failure: allow_failure)
|
125
|
+
end
|
126
|
+
|
127
|
+
def delete_org(name, options = {})
|
128
|
+
allow_failure = options.fetch(:allow_failure, true)
|
129
|
+
cf("delete-org #{name} -f", allow_failure: allow_failure)
|
130
|
+
end
|
131
|
+
|
132
|
+
alias_method :reset!, :delete_org
|
133
|
+
|
134
|
+
def add_public_service_broker(service_name, _service_label, url, username, password)
|
135
|
+
cf("create-service-broker #{service_name} #{username} #{password} #{url}")
|
136
|
+
|
137
|
+
service_plans = JSON.parse(cf('curl /v2/service_plans -X GET'))
|
138
|
+
guids = service_plans['resources'].map do |resource|
|
139
|
+
resource['metadata']['guid']
|
140
|
+
end
|
141
|
+
|
142
|
+
guids.each do |guid|
|
143
|
+
cf(%(curl /v2/service_plans/#{guid} -X PUT -d '{"public":true}'))
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
def remove_service_broker(service_name, options = {})
|
148
|
+
allow_failure = options.fetch(:allow_failure, true)
|
149
|
+
cf("delete-service-broker #{service_name} -f", allow_failure: allow_failure)
|
150
|
+
end
|
151
|
+
|
152
|
+
def assert_broker_is_in_marketplace(type)
|
153
|
+
output = marketplace
|
154
|
+
unless output.include?(type)
|
155
|
+
fail "Broker #{type} not found in marketplace"
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
def marketplace
|
160
|
+
cf('marketplace')
|
161
|
+
end
|
162
|
+
|
163
|
+
def create_service_instance(type, name, plan)
|
164
|
+
cf("create-service #{type} #{plan} #{name}")
|
165
|
+
end
|
166
|
+
|
167
|
+
def delete_service_instance_and_unbind(name, options = {})
|
168
|
+
allow_failure = options.fetch(:allow_failure, true)
|
169
|
+
cf("delete-service -f #{name}", allow_failure: allow_failure)
|
170
|
+
end
|
171
|
+
|
172
|
+
def assert_instance_is_in_services_list(service_name)
|
173
|
+
output = cf('services')
|
174
|
+
unless output.include?(service_name)
|
175
|
+
fail "Instance #{service_name} not found in services list"
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
def push_app_and_start(app_path, name)
|
180
|
+
push_app(app_path, name)
|
181
|
+
start_app(name)
|
182
|
+
end
|
183
|
+
|
184
|
+
def push_app(app_path, name)
|
185
|
+
cf("push #{name} -p #{app_path} -n #{name} -d #{domain} --no-start")
|
186
|
+
end
|
187
|
+
|
188
|
+
def enable_diego_for_app(name)
|
189
|
+
cf("enable-diego #{name}")
|
190
|
+
end
|
191
|
+
|
192
|
+
def delete_app(name, options = {})
|
193
|
+
allow_failure = options.fetch(:allow_failure, true)
|
194
|
+
cf("delete #{name} -f", allow_failure: allow_failure)
|
195
|
+
end
|
196
|
+
|
197
|
+
def bind_app_to_service(app_name, service_name)
|
198
|
+
cf("bind-service #{app_name} #{service_name}")
|
199
|
+
end
|
200
|
+
|
201
|
+
def unbind_app_from_service(app_name, service_name)
|
202
|
+
cf("unbind-service #{app_name} #{service_name}")
|
203
|
+
end
|
204
|
+
|
205
|
+
def list_service_keys(service_instance_name)
|
206
|
+
cf("service-keys #{service_instance_name}")
|
207
|
+
end
|
208
|
+
|
209
|
+
def create_service_key(service_instance_name, key_name)
|
210
|
+
cf("create-service-key #{service_instance_name} #{key_name}")
|
211
|
+
end
|
212
|
+
|
213
|
+
def delete_service_key(service_instance_name, key_name)
|
214
|
+
cf("delete-service-key #{service_instance_name} #{key_name} -f")
|
215
|
+
end
|
216
|
+
|
217
|
+
def service_key(service_instance_name, key_name)
|
218
|
+
cf("service-key #{service_instance_name} #{key_name}")
|
219
|
+
end
|
220
|
+
|
221
|
+
def restart_app(name)
|
222
|
+
stop_app(name)
|
223
|
+
start_app(name)
|
224
|
+
end
|
225
|
+
|
226
|
+
def start_app(name)
|
227
|
+
cf("start #{name}")
|
228
|
+
rescue => start_exception
|
229
|
+
begin
|
230
|
+
cf("logs --recent #{name}")
|
231
|
+
ensure
|
232
|
+
raise start_exception
|
233
|
+
end
|
234
|
+
end
|
235
|
+
|
236
|
+
def stop_app(name)
|
237
|
+
cf("stop #{name}")
|
238
|
+
end
|
239
|
+
|
240
|
+
def app_env(app_name)
|
241
|
+
cf("env #{app_name}")
|
242
|
+
end
|
243
|
+
|
244
|
+
def create_user(username, password)
|
245
|
+
cf("create-user #{username} #{password}")
|
246
|
+
end
|
247
|
+
|
248
|
+
def delete_user(username)
|
249
|
+
cf("delete-user -f #{username}")
|
250
|
+
end
|
251
|
+
|
252
|
+
def user_exists?(username, org)
|
253
|
+
output = cf("org-users #{org}")
|
254
|
+
output.lines.select { |l| l.start_with? ' ' }.map(&:strip).uniq.include?(username)
|
255
|
+
end
|
256
|
+
|
257
|
+
def set_org_role(username, org, role)
|
258
|
+
cf("set-org-role #{username} #{org} #{role}")
|
259
|
+
end
|
260
|
+
|
261
|
+
def version
|
262
|
+
cf('-v')
|
263
|
+
end
|
264
|
+
|
265
|
+
private
|
266
|
+
|
267
|
+
attr_reader :logger, :command_runner
|
268
|
+
|
269
|
+
def app_environment(app_name)
|
270
|
+
env_output = cf("env #{app_name}")
|
271
|
+
response = env_output[/^\{.*}$/m].split(/^\n/)
|
272
|
+
response = response.map { |json| JSON.parse(json) }
|
273
|
+
response.inject({}) { |result, current| result.merge(current) }
|
274
|
+
end
|
275
|
+
|
276
|
+
def default_logger
|
277
|
+
@default_logger ||= begin
|
278
|
+
STDOUT.sync = true
|
279
|
+
require 'logger'
|
280
|
+
Logger.new(STDOUT)
|
281
|
+
end
|
282
|
+
end
|
283
|
+
|
284
|
+
def default_command_runner
|
285
|
+
@default_command_runner ||= CommandRunner.new(environment: env)
|
286
|
+
end
|
287
|
+
|
288
|
+
def cf(command, options = {})
|
289
|
+
allow_failure = options.fetch(:allow_failure, false)
|
290
|
+
cf_command = "cf #{command}"
|
291
|
+
|
292
|
+
logger.info(cf_command)
|
293
|
+
|
294
|
+
command_runner.run(cf_command, allow_failure: allow_failure)
|
295
|
+
end
|
296
|
+
|
297
|
+
def env
|
298
|
+
@env ||= ENV.to_hash.merge(
|
299
|
+
'PATH' => clean_path,
|
300
|
+
'CF_HOME' => Dir.mktmpdir('cf-home')
|
301
|
+
)
|
302
|
+
end
|
303
|
+
|
304
|
+
def clean_path
|
305
|
+
'/usr/local/bin:/usr/local/sbin:/usr/bin:/usr/sbin:/bin/:sbin'
|
306
|
+
end
|
307
|
+
end
|
308
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# Copyright (c) 2014-2015 Pivotal Software, Inc.
|
2
|
+
# All rights reserved.
|
3
|
+
# THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
4
|
+
# INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
5
|
+
# PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
6
|
+
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
7
|
+
# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
8
|
+
# USE OR OTHER DEALINGS IN THE SOFTWARE.
|
9
|
+
#
|
10
|
+
|
11
|
+
require 'open3'
|
12
|
+
|
13
|
+
module Hula
|
14
|
+
class CommandFailedError < StandardError; end
|
15
|
+
|
16
|
+
class CommandRunner
|
17
|
+
def initialize(environment: ENV)
|
18
|
+
@environment = environment
|
19
|
+
end
|
20
|
+
|
21
|
+
def run(command, allow_failure: false)
|
22
|
+
stdout_and_stderr, status = Open3.capture2e(environment, command)
|
23
|
+
|
24
|
+
if !allow_failure && !status.success?
|
25
|
+
message = "Command failed! - #{command}\n\n#{stdout_and_stderr}\n\nexit status: #{status.exitstatus}"
|
26
|
+
fail CommandFailedError, message
|
27
|
+
end
|
28
|
+
|
29
|
+
stdout_and_stderr
|
30
|
+
rescue => e
|
31
|
+
raise CommandFailedError, e
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
attr_reader :environment
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
# Copyright (c) 2014-2015 Pivotal Software, Inc.
|
2
|
+
# All rights reserved.
|
3
|
+
# THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
4
|
+
# INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
5
|
+
# PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
6
|
+
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
7
|
+
# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
8
|
+
# USE OR OTHER DEALINGS IN THE SOFTWARE.
|
9
|
+
#
|
10
|
+
|
11
|
+
require 'hula/helpers/timeout_tools'
|
12
|
+
|
13
|
+
require 'socket'
|
14
|
+
|
15
|
+
module Hula
|
16
|
+
module Helpers
|
17
|
+
module SocketTools
|
18
|
+
module_function def wait_for_port(host:, port:, timeout_seconds: 20)
|
19
|
+
error = "Failed to connect to #{host}:#{port} within #{timeout_seconds} seconds"
|
20
|
+
TimeoutTools.wait_for(error: error, timeout_seconds: timeout_seconds) do
|
21
|
+
port_open?(host: host, port: port)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
module_function def port_open?(host:, port:)
|
26
|
+
socket = TCPSocket.new(host, port)
|
27
|
+
socket.close unless socket.nil?
|
28
|
+
true
|
29
|
+
rescue Errno::ECONNREFUSED
|
30
|
+
false
|
31
|
+
end
|
32
|
+
|
33
|
+
module_function def free_port
|
34
|
+
socket = Socket.new(:INET, :STREAM, 0)
|
35
|
+
socket.bind(Addrinfo.tcp('127.0.0.1', 0))
|
36
|
+
socket.local_address.ip_port
|
37
|
+
ensure
|
38
|
+
socket.close
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# Copyright (c) 2014-2015 Pivotal Software, Inc.
|
2
|
+
# All rights reserved.
|
3
|
+
# THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
4
|
+
# INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
5
|
+
# PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
6
|
+
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
7
|
+
# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
8
|
+
# USE OR OTHER DEALINGS IN THE SOFTWARE.
|
9
|
+
#
|
10
|
+
|
11
|
+
require 'timeout'
|
12
|
+
|
13
|
+
module Hula
|
14
|
+
module Helpers
|
15
|
+
module TimeoutTools
|
16
|
+
module_function def wait_for(error: nil, timeout_seconds:, &condition_block)
|
17
|
+
Timeout::timeout(timeout_seconds) do
|
18
|
+
until condition_block.call do
|
19
|
+
sleep 0.1
|
20
|
+
end
|
21
|
+
end
|
22
|
+
rescue Timeout::Error => e
|
23
|
+
error ? raise(error) : raise(e)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
# Copyright (c) 2014-2015 Pivotal Software, Inc.
|
2
|
+
# All rights reserved.
|
3
|
+
# THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
4
|
+
# INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
5
|
+
# PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
6
|
+
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
7
|
+
# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
8
|
+
# USE OR OTHER DEALINGS IN THE SOFTWARE.
|
9
|
+
#
|
10
|
+
|
11
|
+
require 'hula/helpers/socket_tools'
|
12
|
+
|
13
|
+
module Hula
|
14
|
+
class HttpProxyUpstreamSocks
|
15
|
+
include Helpers::SocketTools
|
16
|
+
|
17
|
+
def initialize(
|
18
|
+
polipo_bin: 'polipo',
|
19
|
+
socks_proxy:,
|
20
|
+
http_host: 'localhost',
|
21
|
+
http_port: free_port
|
22
|
+
)
|
23
|
+
@socks_proxy_host = socks_proxy.socks_host
|
24
|
+
@socks_proxy_port = socks_proxy.socks_port
|
25
|
+
@http_host = http_host
|
26
|
+
@http_port = http_port
|
27
|
+
@polipo_bin = polipo_bin
|
28
|
+
|
29
|
+
check_polipo_bin!
|
30
|
+
end
|
31
|
+
|
32
|
+
attr_reader :http_host, :http_port
|
33
|
+
|
34
|
+
def start
|
35
|
+
@process ||= start_polipo_process
|
36
|
+
end
|
37
|
+
|
38
|
+
def stop
|
39
|
+
return unless @process
|
40
|
+
|
41
|
+
Process.kill('TERM', @process) rescue Errno::ESRCH
|
42
|
+
Process.wait(@process) rescue Errno::ECHILD
|
43
|
+
@process = nil
|
44
|
+
end
|
45
|
+
|
46
|
+
private
|
47
|
+
|
48
|
+
attr_reader :socks_proxy_host, :socks_proxy_port, :polipo_bin
|
49
|
+
|
50
|
+
|
51
|
+
def start_polipo_process
|
52
|
+
pid = Process.spawn(polipo_command)
|
53
|
+
at_exit { stop }
|
54
|
+
wait_for_port(host: http_host, port: http_port)
|
55
|
+
Process.detach(pid)
|
56
|
+
pid
|
57
|
+
end
|
58
|
+
|
59
|
+
def polipo_command
|
60
|
+
"#{polipo_bin} diskCacheRoot='' \
|
61
|
+
proxyPort=#{http_port} \
|
62
|
+
socksParentProxy=#{socks_proxy_host}:#{socks_proxy_port} \
|
63
|
+
socksProxyType=socks4a"
|
64
|
+
end
|
65
|
+
|
66
|
+
def check_polipo_bin!
|
67
|
+
unless system("which #{polipo_bin} > /dev/null 2>&1")
|
68
|
+
raise "Could not run polipo (#{polipo_bin}). Please install, or put in PATH"
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,123 @@
|
|
1
|
+
# Copyright (c) 2014-2015 Pivotal Software, Inc.
|
2
|
+
# All rights reserved.
|
3
|
+
# THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
4
|
+
# INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
5
|
+
# PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
6
|
+
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
7
|
+
# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
8
|
+
# USE OR OTHER DEALINGS IN THE SOFTWARE.
|
9
|
+
#
|
10
|
+
|
11
|
+
require 'forwardable'
|
12
|
+
require 'hula/service_broker/catalog'
|
13
|
+
require 'hula/service_broker/instance_binding'
|
14
|
+
require 'hula/service_broker/http_json_client'
|
15
|
+
require 'hula/service_broker/service_instance'
|
16
|
+
|
17
|
+
require 'securerandom'
|
18
|
+
|
19
|
+
module Hula
|
20
|
+
module ServiceBroker
|
21
|
+
class Api
|
22
|
+
extend Forwardable
|
23
|
+
def_delegators :catalog, :service_plan
|
24
|
+
|
25
|
+
def initialize(url:, username:, password:, http_client: HttpJsonClient.new)
|
26
|
+
@http_client = http_client
|
27
|
+
|
28
|
+
@url = URI(url)
|
29
|
+
@username = username
|
30
|
+
@password = password
|
31
|
+
end
|
32
|
+
|
33
|
+
attr_reader :url
|
34
|
+
|
35
|
+
def catalog
|
36
|
+
json = http_client.get(url_for('/v2/catalog'), auth: { username: username, password: password })
|
37
|
+
Catalog.new(json)
|
38
|
+
end
|
39
|
+
|
40
|
+
def provision_instance(plan, service_instance_id: SecureRandom.uuid)
|
41
|
+
http_provision_instance(
|
42
|
+
service_id: plan.service_id,
|
43
|
+
plan_id: plan.id,
|
44
|
+
service_instance_id: service_instance_id
|
45
|
+
)
|
46
|
+
|
47
|
+
ServiceInstance.new(id: service_instance_id)
|
48
|
+
end
|
49
|
+
|
50
|
+
def deprovision_instance(service_instance)
|
51
|
+
http_deprovision_service(service_instance_id: service_instance.id)
|
52
|
+
end
|
53
|
+
|
54
|
+
def bind_instance(service_instance, binding_id: SecureRandom.uuid)
|
55
|
+
result = http_bind_instance(
|
56
|
+
service_instance_id: service_instance.id,
|
57
|
+
binding_id: binding_id
|
58
|
+
)
|
59
|
+
|
60
|
+
InstanceBinding.new(
|
61
|
+
id: binding_id,
|
62
|
+
credentials: result.fetch(:credentials),
|
63
|
+
service_instance: service_instance
|
64
|
+
)
|
65
|
+
end
|
66
|
+
|
67
|
+
def unbind_instance(instance_binding)
|
68
|
+
http_unbind_instance(
|
69
|
+
service_instance_id: instance_binding.service_instance.id,
|
70
|
+
binding_id: instance_binding.id
|
71
|
+
)
|
72
|
+
end
|
73
|
+
|
74
|
+
def debug
|
75
|
+
http_client.get(url_for('/debug'), auth: { username: username, password: password })
|
76
|
+
end
|
77
|
+
|
78
|
+
private
|
79
|
+
|
80
|
+
def http_provision_instance(service_instance_id:, service_id:, plan_id:)
|
81
|
+
http_client.put(
|
82
|
+
url_for("/v2/service_instances/#{service_instance_id}"),
|
83
|
+
body: {
|
84
|
+
service_id: service_id,
|
85
|
+
plan_id: plan_id,
|
86
|
+
},
|
87
|
+
auth: { username: username, password: password }
|
88
|
+
)
|
89
|
+
end
|
90
|
+
|
91
|
+
def http_deprovision_service(service_instance_id:)
|
92
|
+
http_client.delete(
|
93
|
+
url_for("/v2/service_instances/#{service_instance_id}"),
|
94
|
+
auth: {
|
95
|
+
username: username,
|
96
|
+
password: password
|
97
|
+
}
|
98
|
+
)
|
99
|
+
end
|
100
|
+
|
101
|
+
def http_bind_instance(service_instance_id:, binding_id:)
|
102
|
+
http_client.put(
|
103
|
+
url_for("/v2/service_instances/#{service_instance_id}/service_bindings/#{binding_id}"),
|
104
|
+
body: {},
|
105
|
+
auth: { username: username, password: password }
|
106
|
+
)
|
107
|
+
end
|
108
|
+
|
109
|
+
def http_unbind_instance(service_instance_id:, binding_id:)
|
110
|
+
http_client.delete(
|
111
|
+
url_for("/v2/service_instances/#{service_instance_id}/service_bindings/#{binding_id}"),
|
112
|
+
auth: { username: username, password: password }
|
113
|
+
)
|
114
|
+
end
|
115
|
+
|
116
|
+
def url_for(path)
|
117
|
+
url.dup.tap { |uri| uri.path += path }
|
118
|
+
end
|
119
|
+
|
120
|
+
attr_reader :http_client, :username, :password
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
# Copyright (c) 2014-2015 Pivotal Software, Inc.
|
2
|
+
# All rights reserved.
|
3
|
+
# THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
4
|
+
# INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
5
|
+
# PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
6
|
+
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
7
|
+
# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
8
|
+
# USE OR OTHER DEALINGS IN THE SOFTWARE.
|
9
|
+
#
|
10
|
+
|
11
|
+
require 'hula/service_broker/errors'
|
12
|
+
require 'hula/service_broker/service'
|
13
|
+
|
14
|
+
module Hula
|
15
|
+
module ServiceBroker
|
16
|
+
|
17
|
+
class Catalog
|
18
|
+
attr_reader :services
|
19
|
+
|
20
|
+
def initialize(args = {})
|
21
|
+
@services = args.fetch(:services).map { |s| Service.new(s) }
|
22
|
+
end
|
23
|
+
|
24
|
+
def ==(other)
|
25
|
+
is_a?(other.class) &&
|
26
|
+
services == other.services
|
27
|
+
end
|
28
|
+
|
29
|
+
def service(service_name)
|
30
|
+
services.find { |s| s.name == service_name } or
|
31
|
+
fail(ServiceNotFoundError, [
|
32
|
+
%{Unknown service with name: #{service_name.inspect}},
|
33
|
+
" Known service names: #{services.map(&:name).inspect}"
|
34
|
+
].join("\n")
|
35
|
+
)
|
36
|
+
end
|
37
|
+
|
38
|
+
def service_plan(service_name, plan_name)
|
39
|
+
service(service_name).plan(plan_name)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|