nucleus 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +3 -0
- data/CHANGELOG.md +18 -4
- data/README.md +28 -40
- data/Rakefile +137 -137
- data/config/nucleus_config.rb +0 -4
- data/lib/nucleus/adapter_resolver.rb +115 -115
- data/lib/nucleus/adapters/buildpack_translator.rb +79 -79
- data/lib/nucleus/adapters/v1/cloud_control/application.rb +108 -108
- data/lib/nucleus/adapters/v1/cloud_control/authentication.rb +27 -27
- data/lib/nucleus/adapters/v1/cloud_control/cloud_control.rb +153 -153
- data/lib/nucleus/adapters/v1/cloud_control/domains.rb +68 -68
- data/lib/nucleus/adapters/v1/cloud_control/logs.rb +103 -103
- data/lib/nucleus/adapters/v1/cloud_control/vars.rb +88 -88
- data/lib/nucleus/adapters/v1/cloud_foundry_v2/domains.rb +149 -149
- data/lib/nucleus/adapters/v1/cloud_foundry_v2/logs.rb +303 -303
- data/lib/nucleus/adapters/v1/cloud_foundry_v2/services.rb +286 -286
- data/lib/nucleus/adapters/v1/heroku/heroku.rb +2 -2
- data/lib/nucleus/adapters/v1/heroku/logs.rb +108 -108
- data/lib/nucleus/core/adapter_authentication_inductor.rb +0 -2
- data/lib/nucleus/core/adapter_extensions/auth/http_basic_auth_client.rb +37 -37
- data/lib/nucleus/core/adapter_extensions/http_client.rb +177 -177
- data/lib/nucleus/core/common/files/archive_extractor.rb +112 -112
- data/lib/nucleus/core/common/files/archiver.rb +91 -91
- data/lib/nucleus/core/common/logging/request_log_formatter.rb +48 -48
- data/lib/nucleus/core/error_messages.rb +127 -127
- data/lib/nucleus/core/models/abstract_model.rb +29 -29
- data/lib/nucleus/scripts/load_dependencies.rb +0 -1
- data/lib/nucleus/scripts/setup_config.rb +28 -28
- data/lib/nucleus/version.rb +3 -3
- data/nucleus.gemspec +10 -12
- data/spec/factories/models.rb +63 -61
- data/spec/integration/api/auth_spec.rb +58 -58
- data/spec/test_suites.rake +31 -31
- data/spec/unit/common/helpers/auth_helper_spec.rb +73 -73
- data/spec/unit/common/oauth2_auth_client_spec.rb +1 -1
- data/tasks/compatibility.rake +113 -113
- data/tasks/evaluation.rake +162 -162
- metadata +16 -30
@@ -1,286 +1,286 @@
|
|
1
|
-
module Nucleus
|
2
|
-
module Adapters
|
3
|
-
module V1
|
4
|
-
class CloudFoundryV2 < Stub
|
5
|
-
# Cloud Foundry, operations for the application's addons
|
6
|
-
module Services
|
7
|
-
# @see Stub#services
|
8
|
-
def services
|
9
|
-
get('/v2/services?inline-relations-depth=1').body[:resources].collect do |service|
|
10
|
-
# show only services that are both, active and bindable
|
11
|
-
next unless service[:entity][:active] && service[:entity][:bindable]
|
12
|
-
to_nucleus_service(service)
|
13
|
-
end.compact
|
14
|
-
end
|
15
|
-
|
16
|
-
# @see Stub#service
|
17
|
-
def service(service_id_or_name)
|
18
|
-
service_guid = service_guid(service_id_or_name)
|
19
|
-
to_nucleus_service(get("/v2/services/#{service_guid}?inline-relations-depth=1").body)
|
20
|
-
end
|
21
|
-
|
22
|
-
# @see Stub#service_plans
|
23
|
-
def service_plans(service_id_or_name)
|
24
|
-
service_guid = service_guid(service_id_or_name)
|
25
|
-
load_plans(service_guid).collect { |plan| to_nucleus_plan(plan) }
|
26
|
-
end
|
27
|
-
|
28
|
-
# @see Stub#service_plan
|
29
|
-
def service_plan(service_id_or_name, plan_id)
|
30
|
-
service_guid = service_guid(service_id_or_name)
|
31
|
-
plan_guid = plan_guid(service_guid, plan_id, Errors::AdapterResourceNotFoundError)
|
32
|
-
to_nucleus_plan(get("/v2/service_plans/#{plan_guid}").body)
|
33
|
-
end
|
34
|
-
|
35
|
-
# @see Stub#installed_services
|
36
|
-
def installed_services(application_name_or_id)
|
37
|
-
app_guid = app_guid(application_name_or_id)
|
38
|
-
get("/v2/apps/#{app_guid}/service_bindings?inline-relations-depth=1").body[:resources].collect do |binding|
|
39
|
-
to_nucleus_installed_service(binding)
|
40
|
-
end
|
41
|
-
end
|
42
|
-
|
43
|
-
# @see Stub#installed_service
|
44
|
-
def installed_service(application_name_or_id, service_id_or_name)
|
45
|
-
app_guid = app_guid(application_name_or_id)
|
46
|
-
service_guid = service_guid(service_id_or_name)
|
47
|
-
cf_binding = binding(app_guid, service_guid)
|
48
|
-
# make sure there is a binding
|
49
|
-
fail Errors::AdapterResourceNotFoundError,
|
50
|
-
"No such service '#{service_id_or_name}' for application '#{application_name_or_id}'" unless cf_binding
|
51
|
-
to_nucleus_installed_service(cf_binding)
|
52
|
-
end
|
53
|
-
|
54
|
-
# @see Stub#add_service
|
55
|
-
def add_service(application_name_or_id, service_entity, plan_entity)
|
56
|
-
app_guid = app_guid(application_name_or_id)
|
57
|
-
service_guid = service_guid(service_entity[:id], Errors::SemanticAdapterRequestError)
|
58
|
-
cf_service = load_allowed_service(service_entity, service_guid)
|
59
|
-
|
60
|
-
# get the plan, throws 422 if the plan could not be found
|
61
|
-
plan_guid = plan_guid(service_guid, plan_entity[:id])
|
62
|
-
|
63
|
-
# create new service instance
|
64
|
-
instance_request_body = { space_guid: user_space_guid, service_plan_guid: plan_guid,
|
65
|
-
name: "#{cf_service[:entity][:label]}-#{application_name_or_id}-nucleus" }
|
66
|
-
cf_instance = post('/v2/service_instances', body: instance_request_body).body
|
67
|
-
|
68
|
-
# bind the created service instance to the application
|
69
|
-
binding_request_body = { service_instance_guid: cf_instance[:metadata][:guid], app_guid: app_guid }
|
70
|
-
cf_binding = post('/v2/service_bindings', body: binding_request_body).body
|
71
|
-
|
72
|
-
# created service presentation
|
73
|
-
to_nucleus_installed_service(cf_binding, cf_service, cf_instance)
|
74
|
-
end
|
75
|
-
|
76
|
-
# @see Stub#change_service
|
77
|
-
def change_service(application_name_or_id, service_id, plan_entity)
|
78
|
-
app_guid = app_guid(application_name_or_id)
|
79
|
-
service_guid = service_guid(service_id)
|
80
|
-
cf_service = get("/v2/services/#{service_guid}").body
|
81
|
-
fail_with(:service_not_updateable, [service_id]) unless cf_service[:entity][:plan_updateable]
|
82
|
-
|
83
|
-
cf_binding = binding(app_guid, service_guid)
|
84
|
-
|
85
|
-
# get the plan, throws 422 if the plan could not be found
|
86
|
-
plan_guid = plan_guid(service_guid, plan_entity[:id])
|
87
|
-
cf_instance = put("/v2/service_instances/#{cf_binding[:entity][:service_instance_guid]}",
|
88
|
-
body: { service_plan_guid: plan_guid }).body
|
89
|
-
to_nucleus_installed_service(cf_binding, cf_service, cf_instance)
|
90
|
-
end
|
91
|
-
|
92
|
-
# @see Stub#remove_service
|
93
|
-
def remove_service(application_name_or_id, service_id)
|
94
|
-
app_guid = app_guid(application_name_or_id)
|
95
|
-
service_guid = service_guid(service_id)
|
96
|
-
# sadly we can't resolve the binding and instance from the service_id with ease
|
97
|
-
# we therefore setup a chain to resolve the binding and instance from the active pla
|
98
|
-
binding = binding(app_guid, service_guid)
|
99
|
-
|
100
|
-
# now remove the binding from the application
|
101
|
-
delete("/v2/apps/#{app_guid}/service_bindings/#{binding[:metadata][:guid]}", expects: [201])
|
102
|
-
# and finally delete the service instance
|
103
|
-
delete("/v2/service_instances/#{binding[:entity][:service_instance_guid]}")
|
104
|
-
end
|
105
|
-
|
106
|
-
private
|
107
|
-
|
108
|
-
def load_allowed_service(service_entity, service_guid)
|
109
|
-
begin
|
110
|
-
cf_service = get("/v2/services/#{service_guid}").body
|
111
|
-
rescue Errors::AdapterResourceNotFoundError
|
112
|
-
# convert to semantic error with the service being a body, not a path entity
|
113
|
-
raise Errors::SemanticAdapterRequestError,
|
114
|
-
"Invalid service: Could not find service with the ID '#{service_entity[:id]}'"
|
115
|
-
end
|
116
|
-
|
117
|
-
# must be active and bindable?
|
118
|
-
# currently we focus only on bindable services
|
119
|
-
fail_with(:service_not_bindable, [service_entity[:id]]) unless cf_service[:entity][:bindable]
|
120
|
-
# service must be active, otherwise we can't create the instance
|
121
|
-
fail_with(:service_not_active, [service_entity[:id]]) unless cf_service[:entity][:active]
|
122
|
-
# service seems to be valid, return
|
123
|
-
cf_service
|
124
|
-
end
|
125
|
-
|
126
|
-
def remove_all_services(app_guid)
|
127
|
-
get("/v2/apps/#{app_guid}/service_bindings").body[:resources].collect do |binding|
|
128
|
-
# remove the binding from the application
|
129
|
-
delete("/v2/apps/#{app_guid}/service_bindings/#{binding[:metadata][:guid]}", expects: [201])
|
130
|
-
# and delete the service instance to prevent orphans
|
131
|
-
delete("/v2/service_instances/#{binding[:entity][:service_instance_guid]}")
|
132
|
-
end
|
133
|
-
end
|
134
|
-
|
135
|
-
def service_guid(service_id_or_name, error_class = Errors::AdapterResourceNotFoundError)
|
136
|
-
return service_id_or_name if guid?(service_id_or_name)
|
137
|
-
# list all available services
|
138
|
-
services = get('/v2/services').body[:resources]
|
139
|
-
# find a match and use the service's guid
|
140
|
-
service_match = services.find { |service| service[:entity][:label] == service_id_or_name }
|
141
|
-
fail error_class,
|
142
|
-
"Invalid service: Could not find service with name '#{service_id_or_name}'" unless service_match
|
143
|
-
service_match[:metadata][:guid]
|
144
|
-
end
|
145
|
-
|
146
|
-
def binding(app_guid, service_id)
|
147
|
-
service_plans = get("/v2/services/#{service_id}/service_plans").body[:resources]
|
148
|
-
service_plan_ids = service_plans.collect { |plan| plan[:metadata][:guid] }
|
149
|
-
app_bindings = get("/v2/apps/#{app_guid}/service_bindings?inline-relations-depth=1").body[:resources]
|
150
|
-
# the plan must be bound to the app via an instance
|
151
|
-
app_bindings.find do |binding|
|
152
|
-
service_plan_ids.include?(binding[:entity][:service_instance][:entity][:service_plan_guid])
|
153
|
-
end
|
154
|
-
end
|
155
|
-
|
156
|
-
def plan_guid(service_id, plan_name_or_id, error_class = Errors::SemanticAdapterRequestError)
|
157
|
-
return plan_name_or_id if guid?(plan_name_or_id)
|
158
|
-
# list all plans for the service
|
159
|
-
plans = get("/v2/services/#{service_id}/service_plans").body[:resources]
|
160
|
-
# find a match and use the plan's guid
|
161
|
-
plan_match = plans.find { |plan| plan[:entity][:name] == plan_name_or_id }
|
162
|
-
fail error_class,
|
163
|
-
"Invalid plan: No such plan '#{plan_name_or_id}' for service '#{service_id}'" unless plan_match
|
164
|
-
plan_match[:metadata][:guid]
|
165
|
-
end
|
166
|
-
|
167
|
-
def load_plans(service_id)
|
168
|
-
get("/v2/services/#{service_id}/service_plans").body[:resources]
|
169
|
-
end
|
170
|
-
|
171
|
-
# Memoize this detection.
|
172
|
-
# The information is not critical, but takes some time to evaluate.
|
173
|
-
# Values are not expected to change often.
|
174
|
-
def free_plan?(service_id, plans = nil)
|
175
|
-
@free_plans ||= {}
|
176
|
-
return @free_plans[service_id] if @free_plans.key?(service_id)
|
177
|
-
plans = load_plans(service_id) unless plans
|
178
|
-
@free_plans[service_id] = plans.any? { |plan| plan[:entity][:free] }
|
179
|
-
@free_plans[service_id]
|
180
|
-
end
|
181
|
-
|
182
|
-
def to_nucleus_plan(cf_plan)
|
183
|
-
plan = cf_plan[:entity]
|
184
|
-
plan[:id] = cf_plan[:metadata][:guid]
|
185
|
-
plan[:created_at] = cf_plan[:metadata][:created_at]
|
186
|
-
plan[:updated_at] = cf_plan[:metadata][:updated_at]
|
187
|
-
plan[:costs] = []
|
188
|
-
plan
|
189
|
-
|
190
|
-
# TODO: determine prices for CF services
|
191
|
-
# we know how IBM handles the costs, but can't determine the country
|
192
|
-
# we know how Pivotal IO handles the costs
|
193
|
-
# but what do the others???
|
194
|
-
|
195
|
-
# extra = Oj.load(plan[:extra])
|
196
|
-
# if plan[:free]
|
197
|
-
# plan[:costs] = { period: '', per_instance: false, price: { amount: 0.00, currency: nil} }
|
198
|
-
# elsif endpoint_url.include?('pivotal.io')
|
199
|
-
# # show prices for Pivotal Web Services
|
200
|
-
# # see for an explanation: http://docs.pivotal.io/pivotalcf/services/catalog-metadata.html
|
201
|
-
# plan[:costs] = extra[:costs].collect do |cost|
|
202
|
-
# prices = cost[:amount].collect { |currency, amount| { currency: currency, amount: amount } }
|
203
|
-
# { per_instance: false, period: cost[:unit], price: prices }
|
204
|
-
# end
|
205
|
-
# elsif endpoint_url.include?('bluemix.net')
|
206
|
-
# # show prices for IBM Bluemix
|
207
|
-
# else
|
208
|
-
# # fallback, unknown CF system
|
209
|
-
# end
|
210
|
-
end
|
211
|
-
|
212
|
-
def to_nucleus_service(cf_service)
|
213
|
-
service = cf_service[:entity]
|
214
|
-
service = apply_metadata(service, cf_service)
|
215
|
-
service[:name] = service.delete(:label)
|
216
|
-
service[:release] = service.delete(:version)
|
217
|
-
if cf_service[:entity].key?(:service_plans)
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
# CF does not have service dependencies
|
224
|
-
service[:required_services] = service.delete(:requires)
|
225
|
-
# description and documentation_url should already be set
|
226
|
-
service
|
227
|
-
end
|
228
|
-
|
229
|
-
# Show the installed service. Therefore we need:<br>
|
230
|
-
# <ul>
|
231
|
-
# <li>the classic service for the basic information (version, name, ...)</li>
|
232
|
-
# <li>the binding for the properties and metadata (id, timestamps, ...)</li>
|
233
|
-
# <li>the bound instance for the active plan and web_url</li>
|
234
|
-
# </ul>
|
235
|
-
def to_nucleus_installed_service(cf_binding, cf_service = nil, cf_instance = nil)
|
236
|
-
# load if not provided
|
237
|
-
cf_instance = load_instance(cf_binding) unless cf_instance
|
238
|
-
cf_service = load_service(cf_instance) unless cf_service
|
239
|
-
# load if not provided
|
240
|
-
unless cf_service
|
241
|
-
cf_service = get("/v2/service_plans/#{cf_instance[:entity][:service_plan_guid]}"\
|
242
|
-
'?inline-relations-depth=1').body[:entity][:service]
|
243
|
-
end
|
244
|
-
|
245
|
-
# active_plan, web_url, properties
|
246
|
-
service = to_nucleus_service(cf_service)
|
247
|
-
# use the metadata of the binding, is more future proof than instance metadata
|
248
|
-
apply_metadata(service, cf_binding)
|
249
|
-
service[:active_plan] = cf_instance[:entity][:service_plan_guid]
|
250
|
-
service[:web_url] = cf_instance[:entity][:dashboard_url]
|
251
|
-
service[:properties] = binding_properties(cf_binding)
|
252
|
-
service
|
253
|
-
end
|
254
|
-
|
255
|
-
def load_instance(cf_binding)
|
256
|
-
if cf_binding[:entity].key?(:service_instance)
|
257
|
-
# use if nested property is available
|
258
|
-
cf_binding[:entity][:service_instance]
|
259
|
-
else
|
260
|
-
get("/v2/service_instances/#{cf_binding[:entity][:service_instance_guid]}").body
|
261
|
-
end
|
262
|
-
end
|
263
|
-
|
264
|
-
def load_service(cf_instance)
|
265
|
-
get("/v2/service_plans/#{cf_instance[:entity][:service_plan_guid]}"\
|
266
|
-
'?inline-relations-depth=1').body[:entity][:service]
|
267
|
-
end
|
268
|
-
|
269
|
-
def binding_properties(binding)
|
270
|
-
# in the credentials there are information such as: hostname, username, password, license keys, ...
|
271
|
-
binding[:entity][:credentials].collect do |key, value|
|
272
|
-
{ key: key, value: value, description: nil }
|
273
|
-
end
|
274
|
-
end
|
275
|
-
|
276
|
-
def apply_metadata(apply_to, cf_object)
|
277
|
-
apply_to[:id] = cf_object[:metadata][:guid]
|
278
|
-
apply_to[:created_at] = cf_object[:metadata][:created_at]
|
279
|
-
apply_to[:updated_at] = cf_object[:metadata][:updated_at]
|
280
|
-
apply_to
|
281
|
-
end
|
282
|
-
end
|
283
|
-
end
|
284
|
-
end
|
285
|
-
end
|
286
|
-
end
|
1
|
+
module Nucleus
|
2
|
+
module Adapters
|
3
|
+
module V1
|
4
|
+
class CloudFoundryV2 < Stub
|
5
|
+
# Cloud Foundry, operations for the application's addons
|
6
|
+
module Services
|
7
|
+
# @see Stub#services
|
8
|
+
def services
|
9
|
+
get('/v2/services?inline-relations-depth=1').body[:resources].collect do |service|
|
10
|
+
# show only services that are both, active and bindable
|
11
|
+
next unless service[:entity][:active] && service[:entity][:bindable]
|
12
|
+
to_nucleus_service(service)
|
13
|
+
end.compact
|
14
|
+
end
|
15
|
+
|
16
|
+
# @see Stub#service
|
17
|
+
def service(service_id_or_name)
|
18
|
+
service_guid = service_guid(service_id_or_name)
|
19
|
+
to_nucleus_service(get("/v2/services/#{service_guid}?inline-relations-depth=1").body)
|
20
|
+
end
|
21
|
+
|
22
|
+
# @see Stub#service_plans
|
23
|
+
def service_plans(service_id_or_name)
|
24
|
+
service_guid = service_guid(service_id_or_name)
|
25
|
+
load_plans(service_guid).collect { |plan| to_nucleus_plan(plan) }
|
26
|
+
end
|
27
|
+
|
28
|
+
# @see Stub#service_plan
|
29
|
+
def service_plan(service_id_or_name, plan_id)
|
30
|
+
service_guid = service_guid(service_id_or_name)
|
31
|
+
plan_guid = plan_guid(service_guid, plan_id, Errors::AdapterResourceNotFoundError)
|
32
|
+
to_nucleus_plan(get("/v2/service_plans/#{plan_guid}").body)
|
33
|
+
end
|
34
|
+
|
35
|
+
# @see Stub#installed_services
|
36
|
+
def installed_services(application_name_or_id)
|
37
|
+
app_guid = app_guid(application_name_or_id)
|
38
|
+
get("/v2/apps/#{app_guid}/service_bindings?inline-relations-depth=1").body[:resources].collect do |binding|
|
39
|
+
to_nucleus_installed_service(binding)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
# @see Stub#installed_service
|
44
|
+
def installed_service(application_name_or_id, service_id_or_name)
|
45
|
+
app_guid = app_guid(application_name_or_id)
|
46
|
+
service_guid = service_guid(service_id_or_name)
|
47
|
+
cf_binding = binding(app_guid, service_guid)
|
48
|
+
# make sure there is a binding
|
49
|
+
fail Errors::AdapterResourceNotFoundError,
|
50
|
+
"No such service '#{service_id_or_name}' for application '#{application_name_or_id}'" unless cf_binding
|
51
|
+
to_nucleus_installed_service(cf_binding)
|
52
|
+
end
|
53
|
+
|
54
|
+
# @see Stub#add_service
|
55
|
+
def add_service(application_name_or_id, service_entity, plan_entity)
|
56
|
+
app_guid = app_guid(application_name_or_id)
|
57
|
+
service_guid = service_guid(service_entity[:id], Errors::SemanticAdapterRequestError)
|
58
|
+
cf_service = load_allowed_service(service_entity, service_guid)
|
59
|
+
|
60
|
+
# get the plan, throws 422 if the plan could not be found
|
61
|
+
plan_guid = plan_guid(service_guid, plan_entity[:id])
|
62
|
+
|
63
|
+
# create new service instance
|
64
|
+
instance_request_body = { space_guid: user_space_guid, service_plan_guid: plan_guid,
|
65
|
+
name: "#{cf_service[:entity][:label]}-#{application_name_or_id}-nucleus" }
|
66
|
+
cf_instance = post('/v2/service_instances', body: instance_request_body).body
|
67
|
+
|
68
|
+
# bind the created service instance to the application
|
69
|
+
binding_request_body = { service_instance_guid: cf_instance[:metadata][:guid], app_guid: app_guid }
|
70
|
+
cf_binding = post('/v2/service_bindings', body: binding_request_body).body
|
71
|
+
|
72
|
+
# created service presentation
|
73
|
+
to_nucleus_installed_service(cf_binding, cf_service, cf_instance)
|
74
|
+
end
|
75
|
+
|
76
|
+
# @see Stub#change_service
|
77
|
+
def change_service(application_name_or_id, service_id, plan_entity)
|
78
|
+
app_guid = app_guid(application_name_or_id)
|
79
|
+
service_guid = service_guid(service_id)
|
80
|
+
cf_service = get("/v2/services/#{service_guid}").body
|
81
|
+
fail_with(:service_not_updateable, [service_id]) unless cf_service[:entity][:plan_updateable]
|
82
|
+
|
83
|
+
cf_binding = binding(app_guid, service_guid)
|
84
|
+
|
85
|
+
# get the plan, throws 422 if the plan could not be found
|
86
|
+
plan_guid = plan_guid(service_guid, plan_entity[:id])
|
87
|
+
cf_instance = put("/v2/service_instances/#{cf_binding[:entity][:service_instance_guid]}",
|
88
|
+
body: { service_plan_guid: plan_guid }).body
|
89
|
+
to_nucleus_installed_service(cf_binding, cf_service, cf_instance)
|
90
|
+
end
|
91
|
+
|
92
|
+
# @see Stub#remove_service
|
93
|
+
def remove_service(application_name_or_id, service_id)
|
94
|
+
app_guid = app_guid(application_name_or_id)
|
95
|
+
service_guid = service_guid(service_id)
|
96
|
+
# sadly we can't resolve the binding and instance from the service_id with ease
|
97
|
+
# we therefore setup a chain to resolve the binding and instance from the active pla
|
98
|
+
binding = binding(app_guid, service_guid)
|
99
|
+
|
100
|
+
# now remove the binding from the application
|
101
|
+
delete("/v2/apps/#{app_guid}/service_bindings/#{binding[:metadata][:guid]}", expects: [201])
|
102
|
+
# and finally delete the service instance
|
103
|
+
delete("/v2/service_instances/#{binding[:entity][:service_instance_guid]}")
|
104
|
+
end
|
105
|
+
|
106
|
+
private
|
107
|
+
|
108
|
+
def load_allowed_service(service_entity, service_guid)
|
109
|
+
begin
|
110
|
+
cf_service = get("/v2/services/#{service_guid}").body
|
111
|
+
rescue Errors::AdapterResourceNotFoundError
|
112
|
+
# convert to semantic error with the service being a body, not a path entity
|
113
|
+
raise Errors::SemanticAdapterRequestError,
|
114
|
+
"Invalid service: Could not find service with the ID '#{service_entity[:id]}'"
|
115
|
+
end
|
116
|
+
|
117
|
+
# must be active and bindable?
|
118
|
+
# currently we focus only on bindable services
|
119
|
+
fail_with(:service_not_bindable, [service_entity[:id]]) unless cf_service[:entity][:bindable]
|
120
|
+
# service must be active, otherwise we can't create the instance
|
121
|
+
fail_with(:service_not_active, [service_entity[:id]]) unless cf_service[:entity][:active]
|
122
|
+
# service seems to be valid, return
|
123
|
+
cf_service
|
124
|
+
end
|
125
|
+
|
126
|
+
def remove_all_services(app_guid)
|
127
|
+
get("/v2/apps/#{app_guid}/service_bindings").body[:resources].collect do |binding|
|
128
|
+
# remove the binding from the application
|
129
|
+
delete("/v2/apps/#{app_guid}/service_bindings/#{binding[:metadata][:guid]}", expects: [201])
|
130
|
+
# and delete the service instance to prevent orphans
|
131
|
+
delete("/v2/service_instances/#{binding[:entity][:service_instance_guid]}")
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
def service_guid(service_id_or_name, error_class = Errors::AdapterResourceNotFoundError)
|
136
|
+
return service_id_or_name if guid?(service_id_or_name)
|
137
|
+
# list all available services
|
138
|
+
services = get('/v2/services').body[:resources]
|
139
|
+
# find a match and use the service's guid
|
140
|
+
service_match = services.find { |service| service[:entity][:label] == service_id_or_name }
|
141
|
+
fail error_class,
|
142
|
+
"Invalid service: Could not find service with name '#{service_id_or_name}'" unless service_match
|
143
|
+
service_match[:metadata][:guid]
|
144
|
+
end
|
145
|
+
|
146
|
+
def binding(app_guid, service_id)
|
147
|
+
service_plans = get("/v2/services/#{service_id}/service_plans").body[:resources]
|
148
|
+
service_plan_ids = service_plans.collect { |plan| plan[:metadata][:guid] }
|
149
|
+
app_bindings = get("/v2/apps/#{app_guid}/service_bindings?inline-relations-depth=1").body[:resources]
|
150
|
+
# the plan must be bound to the app via an instance
|
151
|
+
app_bindings.find do |binding|
|
152
|
+
service_plan_ids.include?(binding[:entity][:service_instance][:entity][:service_plan_guid])
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
def plan_guid(service_id, plan_name_or_id, error_class = Errors::SemanticAdapterRequestError)
|
157
|
+
return plan_name_or_id if guid?(plan_name_or_id)
|
158
|
+
# list all plans for the service
|
159
|
+
plans = get("/v2/services/#{service_id}/service_plans").body[:resources]
|
160
|
+
# find a match and use the plan's guid
|
161
|
+
plan_match = plans.find { |plan| plan[:entity][:name] == plan_name_or_id }
|
162
|
+
fail error_class,
|
163
|
+
"Invalid plan: No such plan '#{plan_name_or_id}' for service '#{service_id}'" unless plan_match
|
164
|
+
plan_match[:metadata][:guid]
|
165
|
+
end
|
166
|
+
|
167
|
+
def load_plans(service_id)
|
168
|
+
get("/v2/services/#{service_id}/service_plans").body[:resources]
|
169
|
+
end
|
170
|
+
|
171
|
+
# Memoize this detection.
|
172
|
+
# The information is not critical, but takes some time to evaluate.
|
173
|
+
# Values are not expected to change often.
|
174
|
+
def free_plan?(service_id, plans = nil)
|
175
|
+
@free_plans ||= {}
|
176
|
+
return @free_plans[service_id] if @free_plans.key?(service_id)
|
177
|
+
plans = load_plans(service_id) unless plans
|
178
|
+
@free_plans[service_id] = plans.any? { |plan| plan[:entity][:free] }
|
179
|
+
@free_plans[service_id]
|
180
|
+
end
|
181
|
+
|
182
|
+
def to_nucleus_plan(cf_plan)
|
183
|
+
plan = cf_plan[:entity]
|
184
|
+
plan[:id] = cf_plan[:metadata][:guid]
|
185
|
+
plan[:created_at] = cf_plan[:metadata][:created_at]
|
186
|
+
plan[:updated_at] = cf_plan[:metadata][:updated_at]
|
187
|
+
plan[:costs] = []
|
188
|
+
plan
|
189
|
+
|
190
|
+
# TODO: determine prices for CF services
|
191
|
+
# we know how IBM handles the costs, but can't determine the country
|
192
|
+
# we know how Pivotal IO handles the costs
|
193
|
+
# but what do the others???
|
194
|
+
|
195
|
+
# extra = Oj.load(plan[:extra])
|
196
|
+
# if plan[:free]
|
197
|
+
# plan[:costs] = { period: '', per_instance: false, price: { amount: 0.00, currency: nil} }
|
198
|
+
# elsif endpoint_url.include?('pivotal.io')
|
199
|
+
# # show prices for Pivotal Web Services
|
200
|
+
# # see for an explanation: http://docs.pivotal.io/pivotalcf/services/catalog-metadata.html
|
201
|
+
# plan[:costs] = extra[:costs].collect do |cost|
|
202
|
+
# prices = cost[:amount].collect { |currency, amount| { currency: currency, amount: amount } }
|
203
|
+
# { per_instance: false, period: cost[:unit], price: prices }
|
204
|
+
# end
|
205
|
+
# elsif endpoint_url.include?('bluemix.net')
|
206
|
+
# # show prices for IBM Bluemix
|
207
|
+
# else
|
208
|
+
# # fallback, unknown CF system
|
209
|
+
# end
|
210
|
+
end
|
211
|
+
|
212
|
+
def to_nucleus_service(cf_service)
|
213
|
+
service = cf_service[:entity]
|
214
|
+
service = apply_metadata(service, cf_service)
|
215
|
+
service[:name] = service.delete(:label)
|
216
|
+
service[:release] = service.delete(:version)
|
217
|
+
service[:free_plan] = if cf_service[:entity].key?(:service_plans)
|
218
|
+
# use preloaded plans if available
|
219
|
+
free_plan?(service[:id], cf_service[:entity][:service_plans])
|
220
|
+
else
|
221
|
+
free_plan?(service[:id])
|
222
|
+
end
|
223
|
+
# CF does not have service dependencies
|
224
|
+
service[:required_services] = service.delete(:requires)
|
225
|
+
# description and documentation_url should already be set
|
226
|
+
service
|
227
|
+
end
|
228
|
+
|
229
|
+
# Show the installed service. Therefore we need:<br>
|
230
|
+
# <ul>
|
231
|
+
# <li>the classic service for the basic information (version, name, ...)</li>
|
232
|
+
# <li>the binding for the properties and metadata (id, timestamps, ...)</li>
|
233
|
+
# <li>the bound instance for the active plan and web_url</li>
|
234
|
+
# </ul>
|
235
|
+
def to_nucleus_installed_service(cf_binding, cf_service = nil, cf_instance = nil)
|
236
|
+
# load if not provided
|
237
|
+
cf_instance = load_instance(cf_binding) unless cf_instance
|
238
|
+
cf_service = load_service(cf_instance) unless cf_service
|
239
|
+
# load if not provided
|
240
|
+
unless cf_service
|
241
|
+
cf_service = get("/v2/service_plans/#{cf_instance[:entity][:service_plan_guid]}"\
|
242
|
+
'?inline-relations-depth=1').body[:entity][:service]
|
243
|
+
end
|
244
|
+
|
245
|
+
# active_plan, web_url, properties
|
246
|
+
service = to_nucleus_service(cf_service)
|
247
|
+
# use the metadata of the binding, is more future proof than instance metadata
|
248
|
+
apply_metadata(service, cf_binding)
|
249
|
+
service[:active_plan] = cf_instance[:entity][:service_plan_guid]
|
250
|
+
service[:web_url] = cf_instance[:entity][:dashboard_url]
|
251
|
+
service[:properties] = binding_properties(cf_binding)
|
252
|
+
service
|
253
|
+
end
|
254
|
+
|
255
|
+
def load_instance(cf_binding)
|
256
|
+
if cf_binding[:entity].key?(:service_instance)
|
257
|
+
# use if nested property is available
|
258
|
+
cf_binding[:entity][:service_instance]
|
259
|
+
else
|
260
|
+
get("/v2/service_instances/#{cf_binding[:entity][:service_instance_guid]}").body
|
261
|
+
end
|
262
|
+
end
|
263
|
+
|
264
|
+
def load_service(cf_instance)
|
265
|
+
get("/v2/service_plans/#{cf_instance[:entity][:service_plan_guid]}"\
|
266
|
+
'?inline-relations-depth=1').body[:entity][:service]
|
267
|
+
end
|
268
|
+
|
269
|
+
def binding_properties(binding)
|
270
|
+
# in the credentials there are information such as: hostname, username, password, license keys, ...
|
271
|
+
binding[:entity][:credentials].collect do |key, value|
|
272
|
+
{ key: key, value: value, description: nil }
|
273
|
+
end
|
274
|
+
end
|
275
|
+
|
276
|
+
def apply_metadata(apply_to, cf_object)
|
277
|
+
apply_to[:id] = cf_object[:metadata][:guid]
|
278
|
+
apply_to[:created_at] = cf_object[:metadata][:created_at]
|
279
|
+
apply_to[:updated_at] = cf_object[:metadata][:updated_at]
|
280
|
+
apply_to
|
281
|
+
end
|
282
|
+
end
|
283
|
+
end
|
284
|
+
end
|
285
|
+
end
|
286
|
+
end
|