nucleus 0.2.0 → 0.3.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 +4 -4
- data/.rubocop.yml +3 -0
- data/CHANGELOG.md +9 -0
- data/README.md +43 -72
- data/lib/nucleus/adapter_resolver.rb +3 -3
- data/lib/nucleus/adapters/base_adapter.rb +109 -109
- data/lib/nucleus/adapters/v1/cloud_foundry_v2/application.rb +111 -111
- data/lib/nucleus/adapters/v1/cloud_foundry_v2/cloud_foundry_v2.rb +141 -141
- data/lib/nucleus/adapters/v1/cloud_foundry_v2/data.rb +97 -97
- data/lib/nucleus/adapters/v1/cloud_foundry_v2/domains.rb +5 -5
- data/lib/nucleus/adapters/v1/cloud_foundry_v2/lifecycle.rb +41 -41
- data/lib/nucleus/adapters/v1/cloud_foundry_v2/logs.rb +6 -6
- data/lib/nucleus/adapters/v1/cloud_foundry_v2/regions.rb +33 -33
- data/lib/nucleus/adapters/v1/cloud_foundry_v2/services.rb +6 -6
- data/lib/nucleus/adapters/v1/cloud_foundry_v2/vars.rb +80 -80
- data/lib/nucleus/adapters/v1/heroku/app_states.rb +57 -57
- data/lib/nucleus/adapters/v1/heroku/data.rb +78 -78
- data/lib/nucleus/adapters/v1/heroku/heroku.rb +146 -146
- data/lib/nucleus/adapters/v1/heroku/lifecycle.rb +51 -51
- data/lib/nucleus/adapters/v1/heroku/logs.rb +2 -2
- data/lib/nucleus/adapters/v1/heroku/regions.rb +42 -42
- data/lib/nucleus/adapters/v1/heroku/services.rb +168 -168
- data/lib/nucleus/adapters/v1/heroku/vars.rb +65 -65
- data/lib/nucleus/adapters/v1/openshift_v2/app_states.rb +68 -68
- data/lib/nucleus/adapters/v1/openshift_v2/application.rb +1 -1
- data/lib/nucleus/adapters/v1/openshift_v2/data.rb +96 -96
- data/lib/nucleus/adapters/v1/openshift_v2/lifecycle.rb +60 -60
- data/lib/nucleus/adapters/v1/openshift_v2/logs.rb +106 -106
- data/lib/nucleus/adapters/v1/openshift_v2/openshift_v2.rb +125 -125
- data/lib/nucleus/adapters/v1/openshift_v2/regions.rb +58 -58
- data/lib/nucleus/adapters/v1/openshift_v2/services.rb +173 -173
- data/lib/nucleus/adapters/v1/openshift_v2/vars.rb +49 -49
- data/lib/nucleus/adapters/v1/stub_adapter.rb +464 -464
- data/lib/nucleus/core/adapter_extensions/auth/auth_client.rb +44 -44
- data/lib/nucleus/core/adapter_extensions/auth/expiring_token_auth_client.rb +53 -53
- data/lib/nucleus/core/adapter_extensions/auth/http_basic_auth_client.rb +3 -3
- data/lib/nucleus/core/adapter_extensions/auth/o_auth2_auth_client.rb +95 -95
- data/lib/nucleus/core/adapter_extensions/auth/token_auth_client.rb +36 -36
- data/lib/nucleus/core/adapter_extensions/http_client.rb +5 -5
- data/lib/nucleus/core/common/files/archive_extractor.rb +1 -1
- data/lib/nucleus/core/common/files/archiver.rb +2 -2
- data/lib/nucleus/core/file_handling/file_manager.rb +64 -64
- data/lib/nucleus/core/file_handling/git_deployer.rb +133 -133
- data/lib/nucleus/core/import/adapter_configuration.rb +53 -53
- data/lib/nucleus/scripts/initialize_config_defaults.rb +26 -26
- data/lib/nucleus/version.rb +1 -1
- data/nucleus.gemspec +2 -2
- data/spec/integration/api/auth_spec.rb +3 -3
- data/spec/spec_helper.rb +98 -98
- data/spec/test_suites.rake +1 -1
- data/spec/unit/adapters/git_deployer_spec.rb +262 -262
- data/spec/unit/common/helpers/auth_helper_spec.rb +1 -1
- data/tasks/evaluation.rake +1 -1
- data/wiki/adapter_tests.md +0 -7
- data/wiki/implement_new_adapter.md +1 -1
- metadata +4 -20
- data/config/adapters/cloud_control.yml +0 -32
- data/lib/nucleus/adapters/v1/cloud_control/application.rb +0 -108
- data/lib/nucleus/adapters/v1/cloud_control/authentication.rb +0 -27
- data/lib/nucleus/adapters/v1/cloud_control/buildpacks.rb +0 -23
- data/lib/nucleus/adapters/v1/cloud_control/cloud_control.rb +0 -153
- data/lib/nucleus/adapters/v1/cloud_control/data.rb +0 -76
- data/lib/nucleus/adapters/v1/cloud_control/domains.rb +0 -68
- data/lib/nucleus/adapters/v1/cloud_control/lifecycle.rb +0 -27
- data/lib/nucleus/adapters/v1/cloud_control/log_poller.rb +0 -71
- data/lib/nucleus/adapters/v1/cloud_control/logs.rb +0 -103
- data/lib/nucleus/adapters/v1/cloud_control/regions.rb +0 -32
- data/lib/nucleus/adapters/v1/cloud_control/scaling.rb +0 -17
- data/lib/nucleus/adapters/v1/cloud_control/semantic_errors.rb +0 -31
- data/lib/nucleus/adapters/v1/cloud_control/services.rb +0 -162
- data/lib/nucleus/adapters/v1/cloud_control/token.rb +0 -17
- data/lib/nucleus/adapters/v1/cloud_control/vars.rb +0 -88
@@ -1,111 +1,111 @@
|
|
1
|
-
module Nucleus
|
2
|
-
module Adapters
|
3
|
-
module V1
|
4
|
-
class CloudFoundryV2 < Stub
|
5
|
-
module Application
|
6
|
-
# @see Stub#applications
|
7
|
-
def applications
|
8
|
-
response = get('/v2/apps')
|
9
|
-
apps = []
|
10
|
-
response.body[:resources].each do |application_resource|
|
11
|
-
apps << to_nucleus_app(application_resource)
|
12
|
-
end
|
13
|
-
apps
|
14
|
-
end
|
15
|
-
|
16
|
-
# @see Stub#application
|
17
|
-
def application(application_name_or_id)
|
18
|
-
app_guid = app_guid(application_name_or_id)
|
19
|
-
get_response = get("/v2/apps/#{app_guid}")
|
20
|
-
to_nucleus_app(get_response.body)
|
21
|
-
end
|
22
|
-
|
23
|
-
# @see Stub#create_application
|
24
|
-
def create_application(application)
|
25
|
-
if application.key? :region
|
26
|
-
unless application[:region].casecmp('default') == 0
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
end
|
31
|
-
# there is no region in Cloud Foundry --> remove from request
|
32
|
-
application.delete :region
|
33
|
-
end
|
34
|
-
|
35
|
-
apply_buildpack(application)
|
36
|
-
|
37
|
-
# apply default values, if not overridden by custom params
|
38
|
-
default_params = { space_guid: user_space_guid }
|
39
|
-
application = default_params.merge(application)
|
40
|
-
|
41
|
-
# WORKAROUND: requires numeric input, but rack-test provides characters :/
|
42
|
-
application['memory'] = application['memory'].to_i if application.key?('memory')
|
43
|
-
|
44
|
-
response = post('/v2/apps', body: application).body
|
45
|
-
|
46
|
-
# now create the default route (similar to when using an UI, e.g. Pivotal, Stackato or Bluemix) == web_url
|
47
|
-
create_cf_domain(response[:metadata][:guid], @endpoint_app_domain, response[:metadata][:guid])
|
48
|
-
|
49
|
-
# finally build the application response
|
50
|
-
application(response[:metadata][:guid])
|
51
|
-
end
|
52
|
-
|
53
|
-
# @see Stub#update_application
|
54
|
-
def update_application(application_name_or_id, application_form)
|
55
|
-
app_guid = app_guid(application_name_or_id)
|
56
|
-
apply_buildpack(application_form)
|
57
|
-
# update by guid
|
58
|
-
update_response = put("/v2/apps/#{app_guid}", body: application_form)
|
59
|
-
to_nucleus_app(update_response.body)
|
60
|
-
end
|
61
|
-
|
62
|
-
# @see Stub#delete_application
|
63
|
-
def delete_application(application_name_or_id)
|
64
|
-
app_guid = app_guid(application_name_or_id)
|
65
|
-
# first delete all service bindings
|
66
|
-
remove_all_services(app_guid)
|
67
|
-
# then delete the default route (otherwise it would remain as orphaned route)
|
68
|
-
routes = get("/v2/apps/#{app_guid}/routes?q=host:#{app_guid}&inline-relations-depth=1").body[:resources]
|
69
|
-
default_route = routes.find { |route| route[:entity][:domain][:entity][:name] == @endpoint_app_domain }
|
70
|
-
delete("/v2/routes/#{default_route[:metadata][:guid]}") if default_route
|
71
|
-
# and finally delete the app
|
72
|
-
delete("/v2/apps/#{app_guid}")
|
73
|
-
end
|
74
|
-
|
75
|
-
private
|
76
|
-
|
77
|
-
def apply_buildpack(application)
|
78
|
-
# handle desired runtime(s)
|
79
|
-
runtimes = application.delete(:runtimes)
|
80
|
-
return unless runtimes
|
81
|
-
fail_with(:only_one_runtime) if runtimes.length > 1
|
82
|
-
|
83
|
-
buildpack = find_runtime(runtimes[0])
|
84
|
-
# use the translated buildpack name if available, otherwise pass on the given runtime name
|
85
|
-
application[:buildpack] = buildpack ? buildpack : runtimes[0]
|
86
|
-
end
|
87
|
-
|
88
|
-
def to_nucleus_app(app_resource)
|
89
|
-
metadata = app_resource[:metadata]
|
90
|
-
app = app_resource[:entity]
|
91
|
-
|
92
|
-
app[:id] = metadata[:guid]
|
93
|
-
app[:created_at] = metadata[:created_at]
|
94
|
-
app[:updated_at] = metadata[:updated_at] || metadata[:created_at]
|
95
|
-
app[:state] = application_state(app_resource)
|
96
|
-
app[:web_url] = "http://#{app_web_url(metadata[:guid])}"
|
97
|
-
# route could have been deleted by the user
|
98
|
-
app[:web_url] = nil unless domain?(metadata[:guid], app[:web_url])
|
99
|
-
# Stackato does support autoscaling
|
100
|
-
app[:autoscaled] = app.delete(:autoscale_enabled) || false
|
101
|
-
app[:region] = 'default'
|
102
|
-
app[:active_runtime] = app[:detected_buildpack]
|
103
|
-
app[:runtimes] = app[:buildpack] ? [app[:buildpack]] : []
|
104
|
-
app[:release_version] = app.delete(:version)
|
105
|
-
app
|
106
|
-
end
|
107
|
-
end
|
108
|
-
end
|
109
|
-
end
|
110
|
-
end
|
111
|
-
end
|
1
|
+
module Nucleus
|
2
|
+
module Adapters
|
3
|
+
module V1
|
4
|
+
class CloudFoundryV2 < Stub
|
5
|
+
module Application
|
6
|
+
# @see Stub#applications
|
7
|
+
def applications
|
8
|
+
response = get('/v2/apps')
|
9
|
+
apps = []
|
10
|
+
response.body[:resources].each do |application_resource|
|
11
|
+
apps << to_nucleus_app(application_resource)
|
12
|
+
end
|
13
|
+
apps
|
14
|
+
end
|
15
|
+
|
16
|
+
# @see Stub#application
|
17
|
+
def application(application_name_or_id)
|
18
|
+
app_guid = app_guid(application_name_or_id)
|
19
|
+
get_response = get("/v2/apps/#{app_guid}")
|
20
|
+
to_nucleus_app(get_response.body)
|
21
|
+
end
|
22
|
+
|
23
|
+
# @see Stub#create_application
|
24
|
+
def create_application(application)
|
25
|
+
if application.key? :region
|
26
|
+
unless application[:region].casecmp('default') == 0
|
27
|
+
raise Errors::SemanticAdapterRequestError,
|
28
|
+
"Region '#{application[:region]}' does not exist at the endpoint. "\
|
29
|
+
'Please check which regions are actually available on this endpoint.'
|
30
|
+
end
|
31
|
+
# there is no region in Cloud Foundry --> remove from request
|
32
|
+
application.delete :region
|
33
|
+
end
|
34
|
+
|
35
|
+
apply_buildpack(application)
|
36
|
+
|
37
|
+
# apply default values, if not overridden by custom params
|
38
|
+
default_params = { space_guid: user_space_guid }
|
39
|
+
application = default_params.merge(application)
|
40
|
+
|
41
|
+
# WORKAROUND: requires numeric input, but rack-test provides characters :/
|
42
|
+
application['memory'] = application['memory'].to_i if application.key?('memory')
|
43
|
+
|
44
|
+
response = post('/v2/apps', body: application).body
|
45
|
+
|
46
|
+
# now create the default route (similar to when using an UI, e.g. Pivotal, Stackato or Bluemix) == web_url
|
47
|
+
create_cf_domain(response[:metadata][:guid], @endpoint_app_domain, response[:metadata][:guid])
|
48
|
+
|
49
|
+
# finally build the application response
|
50
|
+
application(response[:metadata][:guid])
|
51
|
+
end
|
52
|
+
|
53
|
+
# @see Stub#update_application
|
54
|
+
def update_application(application_name_or_id, application_form)
|
55
|
+
app_guid = app_guid(application_name_or_id)
|
56
|
+
apply_buildpack(application_form)
|
57
|
+
# update by guid
|
58
|
+
update_response = put("/v2/apps/#{app_guid}", body: application_form)
|
59
|
+
to_nucleus_app(update_response.body)
|
60
|
+
end
|
61
|
+
|
62
|
+
# @see Stub#delete_application
|
63
|
+
def delete_application(application_name_or_id)
|
64
|
+
app_guid = app_guid(application_name_or_id)
|
65
|
+
# first delete all service bindings
|
66
|
+
remove_all_services(app_guid)
|
67
|
+
# then delete the default route (otherwise it would remain as orphaned route)
|
68
|
+
routes = get("/v2/apps/#{app_guid}/routes?q=host:#{app_guid}&inline-relations-depth=1").body[:resources]
|
69
|
+
default_route = routes.find { |route| route[:entity][:domain][:entity][:name] == @endpoint_app_domain }
|
70
|
+
delete("/v2/routes/#{default_route[:metadata][:guid]}") if default_route
|
71
|
+
# and finally delete the app
|
72
|
+
delete("/v2/apps/#{app_guid}")
|
73
|
+
end
|
74
|
+
|
75
|
+
private
|
76
|
+
|
77
|
+
def apply_buildpack(application)
|
78
|
+
# handle desired runtime(s)
|
79
|
+
runtimes = application.delete(:runtimes)
|
80
|
+
return unless runtimes
|
81
|
+
fail_with(:only_one_runtime) if runtimes.length > 1
|
82
|
+
|
83
|
+
buildpack = find_runtime(runtimes[0])
|
84
|
+
# use the translated buildpack name if available, otherwise pass on the given runtime name
|
85
|
+
application[:buildpack] = buildpack ? buildpack : runtimes[0]
|
86
|
+
end
|
87
|
+
|
88
|
+
def to_nucleus_app(app_resource)
|
89
|
+
metadata = app_resource[:metadata]
|
90
|
+
app = app_resource[:entity]
|
91
|
+
|
92
|
+
app[:id] = metadata[:guid]
|
93
|
+
app[:created_at] = metadata[:created_at]
|
94
|
+
app[:updated_at] = metadata[:updated_at] || metadata[:created_at]
|
95
|
+
app[:state] = application_state(app_resource)
|
96
|
+
app[:web_url] = "http://#{app_web_url(metadata[:guid])}"
|
97
|
+
# route could have been deleted by the user
|
98
|
+
app[:web_url] = nil unless domain?(metadata[:guid], app[:web_url])
|
99
|
+
# Stackato does support autoscaling
|
100
|
+
app[:autoscaled] = app.delete(:autoscale_enabled) || false
|
101
|
+
app[:region] = 'default'
|
102
|
+
app[:active_runtime] = app[:detected_buildpack]
|
103
|
+
app[:runtimes] = app[:buildpack] ? [app[:buildpack]] : []
|
104
|
+
app[:release_version] = app.delete(:version)
|
105
|
+
app
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
@@ -1,141 +1,141 @@
|
|
1
|
-
module Nucleus
|
2
|
-
module Adapters
|
3
|
-
module V1
|
4
|
-
# The {CloudFoundryV2} adapter is designed to support the Cloud Foundry API and uses only commands
|
5
|
-
# of the API version 2.<br>
|
6
|
-
# <br>
|
7
|
-
# Besides native Cloud Foundry installations, this adapter shall also work with forks, such as Stackato 3.4.2.<br>
|
8
|
-
# <br>
|
9
|
-
# The Nucleus API is fully supported, there are no known issues.
|
10
|
-
# @see http://apidocs.cloudfoundry.org The latest Cloud Foundry API documentation
|
11
|
-
class CloudFoundryV2 < Stub
|
12
|
-
include Nucleus::Logging
|
13
|
-
include Nucleus::Adapters::V1::CloudFoundryV2::Authentication
|
14
|
-
include Nucleus::Adapters::V1::CloudFoundryV2::AppStates
|
15
|
-
include Nucleus::Adapters::V1::CloudFoundryV2::Buildpacks
|
16
|
-
include Nucleus::Adapters::V1::CloudFoundryV2::Application
|
17
|
-
include Nucleus::Adapters::V1::CloudFoundryV2::Domains
|
18
|
-
include Nucleus::Adapters::V1::CloudFoundryV2::Data
|
19
|
-
include Nucleus::Adapters::V1::CloudFoundryV2::Lifecycle
|
20
|
-
include Nucleus::Adapters::V1::CloudFoundryV2::Logs
|
21
|
-
include Nucleus::Adapters::V1::CloudFoundryV2::Regions
|
22
|
-
include Nucleus::Adapters::V1::CloudFoundryV2::Scaling
|
23
|
-
include Nucleus::Adapters::V1::CloudFoundryV2::SemanticErrors
|
24
|
-
include Nucleus::Adapters::V1::CloudFoundryV2::Services
|
25
|
-
include Nucleus::Adapters::V1::CloudFoundryV2::Vars
|
26
|
-
|
27
|
-
def initialize(endpoint_url, endpoint_app_domain = nil, check_certificates = true)
|
28
|
-
super(endpoint_url, endpoint_app_domain, check_certificates)
|
29
|
-
end
|
30
|
-
|
31
|
-
def handle_error(error)
|
32
|
-
cf_error = error.body.is_a?(Hash) ? error.body[:code] : nil
|
33
|
-
case error.status
|
34
|
-
when 400
|
35
|
-
handle_400_error(error, cf_error)
|
36
|
-
when 404
|
37
|
-
|
38
|
-
else
|
39
|
-
if [1001].include? cf_error
|
40
|
-
|
41
|
-
elsif [10_002].include?(cf_error) || error.status == 401
|
42
|
-
|
43
|
-
end
|
44
|
-
end
|
45
|
-
# error still unhandled, will result in a 500, server error
|
46
|
-
log.warn "Cloud Foundry error still unhandled: #{error}"
|
47
|
-
end
|
48
|
-
|
49
|
-
private
|
50
|
-
|
51
|
-
def handle_400_error(error, cf_error)
|
52
|
-
if cf_error == 150_001 || cf_error == 160_001 || cf_error > 100_000 && cf_error < 109_999
|
53
|
-
# Indicating semantically invalid parameters
|
54
|
-
|
55
|
-
elsif cf_error == 170_002
|
56
|
-
fail_with(:build_in_progress)
|
57
|
-
elsif cf_error == 60_002
|
58
|
-
|
59
|
-
end
|
60
|
-
end
|
61
|
-
|
62
|
-
def guid?(name_or_id)
|
63
|
-
Regexp::UUID_PATTERN.match(name_or_id) ? true : false
|
64
|
-
end
|
65
|
-
|
66
|
-
def default_organization_guid
|
67
|
-
get("/v2/spaces/#{user_space_guid}").body[:entity][:organization_guid]
|
68
|
-
end
|
69
|
-
|
70
|
-
def app_guid(app_name_or_id)
|
71
|
-
# app name is a UUID and therefore most likely the CF GUID
|
72
|
-
return app_name_or_id if guid?(app_name_or_id)
|
73
|
-
find_app_guid_by_name(app_name_or_id)
|
74
|
-
end
|
75
|
-
|
76
|
-
def find_app_guid_by_name(application_name)
|
77
|
-
filtered_list_response = get('/v2/apps', query: { q: "name:#{application_name}" })
|
78
|
-
if filtered_list_response.body[:resources].
|
79
|
-
|
80
|
-
|
81
|
-
end
|
82
|
-
# return the found guid
|
83
|
-
filtered_list_response.body[:resources][0][:metadata][:guid]
|
84
|
-
end
|
85
|
-
|
86
|
-
def find_app_id_by_name(application_name, previous_response)
|
87
|
-
filtered_list_response = get('/v2/apps', query: { q: "name:#{application_name}" })
|
88
|
-
# fail as expected if the app can also not be found by its name
|
89
|
-
|
90
|
-
|
91
|
-
# return the found guid
|
92
|
-
filtered_list_response.body[:resources][0][:metadata][:guid]
|
93
|
-
end
|
94
|
-
|
95
|
-
def endpoint_info
|
96
|
-
get('/v2/info', headers: {}).body
|
97
|
-
end
|
98
|
-
|
99
|
-
def user_info
|
100
|
-
get("#{endpoint_info[:authorization_endpoint]}/userinfo").body
|
101
|
-
end
|
102
|
-
|
103
|
-
def user
|
104
|
-
get("/v2/users/#{user_info[:user_id]}").body
|
105
|
-
end
|
106
|
-
|
107
|
-
def user_space_guid
|
108
|
-
users_spaces = get('/v2/spaces').body[:resources]
|
109
|
-
# only once space accessible
|
110
|
-
return users_spaces[0][:metadata][:guid] if users_spaces.length == 1
|
111
|
-
# use default space (stackato feature)
|
112
|
-
default_space = users_spaces.detect { |space_resource| space_resource[:entity][:is_default] == true }
|
113
|
-
return default_space[:metadata][:guid] if default_space
|
114
|
-
# check the users spaces for default
|
115
|
-
user_default_space_guid = user[:entity][:default_space_guid]
|
116
|
-
return user_default_space_guid if user_default_space_guid
|
117
|
-
# TODO: find a more suitable approach to detect the right space !?
|
118
|
-
# multiple spaces and no default space (dammit), choose the first one...
|
119
|
-
return users_spaces[0][:metadata][:guid] if users_spaces
|
120
|
-
# user has no space assigned, fail since we cant determine a space guid
|
121
|
-
fail_with(:no_space_assigned)
|
122
|
-
end
|
123
|
-
|
124
|
-
def headers
|
125
|
-
super.merge('Basic' => 'Y2Y6', 'Content-Type' => 'application/json')
|
126
|
-
end
|
127
|
-
|
128
|
-
def deployed?(application_guid)
|
129
|
-
response = head("/v2/apps/#{application_guid}/download", follow_redirects: false, expects: [200, 302, 404])
|
130
|
-
return true if response.status == 200 || response.status == 302
|
131
|
-
return false if response.status == 404
|
132
|
-
# if the response is neither one of the codes, the call fails anyway...
|
133
|
-
end
|
134
|
-
|
135
|
-
def app_web_url(app_guid)
|
136
|
-
"#{app_guid}.#{@endpoint_app_domain}" if @endpoint_app_domain
|
137
|
-
end
|
138
|
-
end
|
139
|
-
end
|
140
|
-
end
|
141
|
-
end
|
1
|
+
module Nucleus
|
2
|
+
module Adapters
|
3
|
+
module V1
|
4
|
+
# The {CloudFoundryV2} adapter is designed to support the Cloud Foundry API and uses only commands
|
5
|
+
# of the API version 2.<br>
|
6
|
+
# <br>
|
7
|
+
# Besides native Cloud Foundry installations, this adapter shall also work with forks, such as Stackato 3.4.2.<br>
|
8
|
+
# <br>
|
9
|
+
# The Nucleus API is fully supported, there are no known issues.
|
10
|
+
# @see http://apidocs.cloudfoundry.org The latest Cloud Foundry API documentation
|
11
|
+
class CloudFoundryV2 < Stub
|
12
|
+
include Nucleus::Logging
|
13
|
+
include Nucleus::Adapters::V1::CloudFoundryV2::Authentication
|
14
|
+
include Nucleus::Adapters::V1::CloudFoundryV2::AppStates
|
15
|
+
include Nucleus::Adapters::V1::CloudFoundryV2::Buildpacks
|
16
|
+
include Nucleus::Adapters::V1::CloudFoundryV2::Application
|
17
|
+
include Nucleus::Adapters::V1::CloudFoundryV2::Domains
|
18
|
+
include Nucleus::Adapters::V1::CloudFoundryV2::Data
|
19
|
+
include Nucleus::Adapters::V1::CloudFoundryV2::Lifecycle
|
20
|
+
include Nucleus::Adapters::V1::CloudFoundryV2::Logs
|
21
|
+
include Nucleus::Adapters::V1::CloudFoundryV2::Regions
|
22
|
+
include Nucleus::Adapters::V1::CloudFoundryV2::Scaling
|
23
|
+
include Nucleus::Adapters::V1::CloudFoundryV2::SemanticErrors
|
24
|
+
include Nucleus::Adapters::V1::CloudFoundryV2::Services
|
25
|
+
include Nucleus::Adapters::V1::CloudFoundryV2::Vars
|
26
|
+
|
27
|
+
def initialize(endpoint_url, endpoint_app_domain = nil, check_certificates = true)
|
28
|
+
super(endpoint_url, endpoint_app_domain, check_certificates)
|
29
|
+
end
|
30
|
+
|
31
|
+
def handle_error(error)
|
32
|
+
cf_error = error.body.is_a?(Hash) ? error.body[:code] : nil
|
33
|
+
case error.status
|
34
|
+
when 400
|
35
|
+
handle_400_error(error, cf_error)
|
36
|
+
when 404
|
37
|
+
raise Errors::AdapterResourceNotFoundError, error.body[:description] if cf_error > 10_000
|
38
|
+
else
|
39
|
+
if [1001].include? cf_error
|
40
|
+
raise Errors::AdapterRequestError, "#{error.body[:description]} (#{cf_error} - #{error.body[:error_code]})"
|
41
|
+
elsif [10_002].include?(cf_error) || error.status == 401
|
42
|
+
raise Errors::EndpointAuthenticationError, 'Endpoint authentication failed with OAuth2 token'
|
43
|
+
end
|
44
|
+
end
|
45
|
+
# error still unhandled, will result in a 500, server error
|
46
|
+
log.warn "Cloud Foundry error still unhandled: #{error}"
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
def handle_400_error(error, cf_error)
|
52
|
+
if cf_error == 150_001 || cf_error == 160_001 || cf_error > 100_000 && cf_error < 109_999
|
53
|
+
# Indicating semantically invalid parameters
|
54
|
+
raise Errors::SemanticAdapterRequestError, error.body[:description]
|
55
|
+
elsif cf_error == 170_002
|
56
|
+
fail_with(:build_in_progress)
|
57
|
+
elsif cf_error == 60_002
|
58
|
+
raise Errors::SemanticAdapterRequestError, 'Service is already assigned to the application'
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def guid?(name_or_id)
|
63
|
+
Regexp::UUID_PATTERN.match(name_or_id) ? true : false
|
64
|
+
end
|
65
|
+
|
66
|
+
def default_organization_guid
|
67
|
+
get("/v2/spaces/#{user_space_guid}").body[:entity][:organization_guid]
|
68
|
+
end
|
69
|
+
|
70
|
+
def app_guid(app_name_or_id)
|
71
|
+
# app name is a UUID and therefore most likely the CF GUID
|
72
|
+
return app_name_or_id if guid?(app_name_or_id)
|
73
|
+
find_app_guid_by_name(app_name_or_id)
|
74
|
+
end
|
75
|
+
|
76
|
+
def find_app_guid_by_name(application_name)
|
77
|
+
filtered_list_response = get('/v2/apps', query: { q: "name:#{application_name}" })
|
78
|
+
if filtered_list_response.body[:resources].empty?
|
79
|
+
raise Errors::AdapterResourceNotFoundError,
|
80
|
+
"Couldn't find app with name '#{application_name}' on the platform"
|
81
|
+
end
|
82
|
+
# return the found guid
|
83
|
+
filtered_list_response.body[:resources][0][:metadata][:guid]
|
84
|
+
end
|
85
|
+
|
86
|
+
def find_app_id_by_name(application_name, previous_response)
|
87
|
+
filtered_list_response = get('/v2/apps', query: { q: "name:#{application_name}" })
|
88
|
+
# fail as expected if the app can also not be found by its name
|
89
|
+
raise Errors::AdapterResourceNotFoundError,
|
90
|
+
previous_response.body[:description] if filtered_list_response.body[:resources].empty?
|
91
|
+
# return the found guid
|
92
|
+
filtered_list_response.body[:resources][0][:metadata][:guid]
|
93
|
+
end
|
94
|
+
|
95
|
+
def endpoint_info
|
96
|
+
get('/v2/info', headers: {}).body
|
97
|
+
end
|
98
|
+
|
99
|
+
def user_info
|
100
|
+
get("#{endpoint_info[:authorization_endpoint]}/userinfo").body
|
101
|
+
end
|
102
|
+
|
103
|
+
def user
|
104
|
+
get("/v2/users/#{user_info[:user_id]}").body
|
105
|
+
end
|
106
|
+
|
107
|
+
def user_space_guid
|
108
|
+
users_spaces = get('/v2/spaces').body[:resources]
|
109
|
+
# only once space accessible
|
110
|
+
return users_spaces[0][:metadata][:guid] if users_spaces.length == 1
|
111
|
+
# use default space (stackato feature)
|
112
|
+
default_space = users_spaces.detect { |space_resource| space_resource[:entity][:is_default] == true }
|
113
|
+
return default_space[:metadata][:guid] if default_space
|
114
|
+
# check the users spaces for default
|
115
|
+
user_default_space_guid = user[:entity][:default_space_guid]
|
116
|
+
return user_default_space_guid if user_default_space_guid
|
117
|
+
# TODO: find a more suitable approach to detect the right space !?
|
118
|
+
# multiple spaces and no default space (dammit), choose the first one...
|
119
|
+
return users_spaces[0][:metadata][:guid] if users_spaces
|
120
|
+
# user has no space assigned, fail since we cant determine a space guid
|
121
|
+
fail_with(:no_space_assigned)
|
122
|
+
end
|
123
|
+
|
124
|
+
def headers
|
125
|
+
super.merge('Basic' => 'Y2Y6', 'Content-Type' => 'application/json')
|
126
|
+
end
|
127
|
+
|
128
|
+
def deployed?(application_guid)
|
129
|
+
response = head("/v2/apps/#{application_guid}/download", follow_redirects: false, expects: [200, 302, 404])
|
130
|
+
return true if response.status == 200 || response.status == 302
|
131
|
+
return false if response.status == 404
|
132
|
+
# if the response is neither one of the codes, the call fails anyway...
|
133
|
+
end
|
134
|
+
|
135
|
+
def app_web_url(app_guid)
|
136
|
+
"#{app_guid}.#{@endpoint_app_domain}" if @endpoint_app_domain
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|