nucleus 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
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