nucleus 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +3 -0
  3. data/CHANGELOG.md +18 -4
  4. data/README.md +28 -40
  5. data/Rakefile +137 -137
  6. data/config/nucleus_config.rb +0 -4
  7. data/lib/nucleus/adapter_resolver.rb +115 -115
  8. data/lib/nucleus/adapters/buildpack_translator.rb +79 -79
  9. data/lib/nucleus/adapters/v1/cloud_control/application.rb +108 -108
  10. data/lib/nucleus/adapters/v1/cloud_control/authentication.rb +27 -27
  11. data/lib/nucleus/adapters/v1/cloud_control/cloud_control.rb +153 -153
  12. data/lib/nucleus/adapters/v1/cloud_control/domains.rb +68 -68
  13. data/lib/nucleus/adapters/v1/cloud_control/logs.rb +103 -103
  14. data/lib/nucleus/adapters/v1/cloud_control/vars.rb +88 -88
  15. data/lib/nucleus/adapters/v1/cloud_foundry_v2/domains.rb +149 -149
  16. data/lib/nucleus/adapters/v1/cloud_foundry_v2/logs.rb +303 -303
  17. data/lib/nucleus/adapters/v1/cloud_foundry_v2/services.rb +286 -286
  18. data/lib/nucleus/adapters/v1/heroku/heroku.rb +2 -2
  19. data/lib/nucleus/adapters/v1/heroku/logs.rb +108 -108
  20. data/lib/nucleus/core/adapter_authentication_inductor.rb +0 -2
  21. data/lib/nucleus/core/adapter_extensions/auth/http_basic_auth_client.rb +37 -37
  22. data/lib/nucleus/core/adapter_extensions/http_client.rb +177 -177
  23. data/lib/nucleus/core/common/files/archive_extractor.rb +112 -112
  24. data/lib/nucleus/core/common/files/archiver.rb +91 -91
  25. data/lib/nucleus/core/common/logging/request_log_formatter.rb +48 -48
  26. data/lib/nucleus/core/error_messages.rb +127 -127
  27. data/lib/nucleus/core/models/abstract_model.rb +29 -29
  28. data/lib/nucleus/scripts/load_dependencies.rb +0 -1
  29. data/lib/nucleus/scripts/setup_config.rb +28 -28
  30. data/lib/nucleus/version.rb +3 -3
  31. data/nucleus.gemspec +10 -12
  32. data/spec/factories/models.rb +63 -61
  33. data/spec/integration/api/auth_spec.rb +58 -58
  34. data/spec/test_suites.rake +31 -31
  35. data/spec/unit/common/helpers/auth_helper_spec.rb +73 -73
  36. data/spec/unit/common/oauth2_auth_client_spec.rb +1 -1
  37. data/tasks/compatibility.rake +113 -113
  38. data/tasks/evaluation.rake +162 -162
  39. 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
- # use preloaded plans if available
219
- service[:free_plan] = free_plan?(service[:id], cf_service[:entity][:service_plans])
220
- else
221
- service[:free_plan] = 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
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