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,103 +1,103 @@
1
- module Nucleus
2
- module Adapters
3
- module V1
4
- class CloudControl < Stub
5
- # cloud control application's log management operations
6
- module Logs
7
- # Cloud control log types. The +key+ and +id+ shall match the Nucleus definitions of log files,
8
- # whereas the +name+ shall match the cloud control log id.
9
- LOG_TYPES = {
10
- all: { id: 'all', name: 'all', type: Enums::ApplicationLogfileType::OTHER },
11
- request: { id: 'request', name: 'access', type: Enums::ApplicationLogfileType::REQUEST },
12
- application: { id: 'application', name: 'error', type: Enums::ApplicationLogfileType::APPLICATION },
13
- api: { id: 'api', name: 'deploy', type: Enums::ApplicationLogfileType::API },
14
- system: { id: 'system', name: 'worker', type: Enums::ApplicationLogfileType::SYSTEM }
15
- }
16
-
17
- # @see Stub#logs
18
- def logs(application_name)
19
- # fails with 404 if application is not available and serves for timestamps
20
- app = get("/app/#{application_name}").body
21
-
22
- LOG_TYPES.values.collect do |log|
23
- log[:created_at] = app[:date_created]
24
- log[:updated_at] = app[:date_modified]
25
- log
26
- end
27
- end
28
-
29
- # @see Stub#log?
30
- def log?(application_name, log_id)
31
- # fails with 404 if application is not available
32
- get("/app/#{application_name}")
33
-
34
- LOG_TYPES.key? log_id.to_sym
35
- end
36
-
37
- # cloud control shows the last 500 log messages if applicable
38
- # @see Stub#tail
39
- def tail(application_name, log_id, stream)
40
- # cache headers as they are bound to a request and could be lost with the next tick
41
- headers_to_use = headers
42
- logs_to_poll = log_id == 'all' ? LOG_TYPES.keys - [:all] : [log_id]
43
- poller = LogPoller.new(self, headers_to_use)
44
- poller.start(application_name, logs_to_poll, stream)
45
- TailStopper.new(poller, :stop)
46
- end
47
-
48
- # cloud control shows the last 500 log messages if applicable
49
- # @see Stub#log_entries
50
- def log_entries(application_name, log_id)
51
- unless log?(application_name, log_id)
52
- fail Errors::AdapterResourceNotFoundError,
53
- "Invalid log file '#{log_id}', not available for application '#{application_name}'"
54
- end
55
- if log_id == 'all'
56
- fetched_lines = []
57
- (LOG_TYPES.keys - [:all]).each do |current_log_id|
58
- cc_log_entries(application_name, current_log_id).each do |line|
59
- line[:nucleus_origin] = current_log_id
60
- fetched_lines.push(line)
61
- end
62
- end
63
- fetched_lines.sort_by! { |line| line[:time] }
64
- fetched_lines.collect { |line| format_log_entry(line[:nucleus_origin], line) }
65
- else
66
- cc_log_entries(application_name, log_id).collect { |line| format_log_entry(log_id, line) }
67
- end
68
- end
69
-
70
- private
71
-
72
- def cc_log_entries(app_name, log_id, time = nil, headers_to_use = nil)
73
- log_name = LOG_TYPES[log_id.to_sym][:name]
74
- # Hack, do not create fresh headers (which would fail) when in a deferred action
75
- headers_to_use = headers unless headers_to_use
76
- if time
77
- get("/app/#{app_name}/deployment/#{NUCLEUS_DEPLOYMENT}/log/#{log_name}?timestamp=#{time}",
78
- headers: headers_to_use).body
79
- else
80
- get("/app/#{app_name}/deployment/#{NUCLEUS_DEPLOYMENT}/log/#{log_name}", headers: headers_to_use).body
81
- end
82
- end
83
-
84
- def format_log_entry(log_id, line)
85
- # format according to: https://github.com/cloudControl/cctrl/blob/master/cctrl/output.py
86
- case log_id.to_sym
87
- when :request
88
- "#{line[:remote_host]} #{line[:remote_user]} #{line[:remote_logname]} [#{Time.at(line[:time]).iso8601}] "\
89
- "#{line[:first_request_line]} #{line[:status]} #{line[:response_size_CLF]} #{line[:referer]} "\
90
- "#{line[:user_agent]}"
91
- when :system
92
- "#{line[:time]} #{line[:wrk_id]} #{line[:message]}"
93
- when :build
94
- "#{Time.at(line[:time]).iso8601} [#{line[:hostname]}/#{line[:depl_id]}] #{line[:level]} #{line[:message]}"
95
- when :error
96
- "#{line[:time]} #{line[:type]} #{line[:message]}"
97
- end
98
- end
99
- end
100
- end
101
- end
102
- end
103
- end
1
+ module Nucleus
2
+ module Adapters
3
+ module V1
4
+ class CloudControl < Stub
5
+ # cloud control application's log management operations
6
+ module Logs
7
+ # Cloud control log types. The +key+ and +id+ shall match the Nucleus definitions of log files,
8
+ # whereas the +name+ shall match the cloud control log id.
9
+ LOG_TYPES = {
10
+ all: { id: 'all', name: 'all', type: Enums::ApplicationLogfileType::OTHER },
11
+ request: { id: 'request', name: 'access', type: Enums::ApplicationLogfileType::REQUEST },
12
+ application: { id: 'application', name: 'error', type: Enums::ApplicationLogfileType::APPLICATION },
13
+ api: { id: 'api', name: 'deploy', type: Enums::ApplicationLogfileType::API },
14
+ system: { id: 'system', name: 'worker', type: Enums::ApplicationLogfileType::SYSTEM }
15
+ }.freeze
16
+
17
+ # @see Stub#logs
18
+ def logs(application_name)
19
+ # fails with 404 if application is not available and serves for timestamps
20
+ app = get("/app/#{application_name}").body
21
+
22
+ LOG_TYPES.values.collect do |log|
23
+ log[:created_at] = app[:date_created]
24
+ log[:updated_at] = app[:date_modified]
25
+ log
26
+ end
27
+ end
28
+
29
+ # @see Stub#log?
30
+ def log?(application_name, log_id)
31
+ # fails with 404 if application is not available
32
+ get("/app/#{application_name}")
33
+
34
+ LOG_TYPES.key? log_id.to_sym
35
+ end
36
+
37
+ # cloud control shows the last 500 log messages if applicable
38
+ # @see Stub#tail
39
+ def tail(application_name, log_id, stream)
40
+ # cache headers as they are bound to a request and could be lost with the next tick
41
+ headers_to_use = headers
42
+ logs_to_poll = log_id == 'all' ? LOG_TYPES.keys - [:all] : [log_id]
43
+ poller = LogPoller.new(self, headers_to_use)
44
+ poller.start(application_name, logs_to_poll, stream)
45
+ TailStopper.new(poller, :stop)
46
+ end
47
+
48
+ # cloud control shows the last 500 log messages if applicable
49
+ # @see Stub#log_entries
50
+ def log_entries(application_name, log_id)
51
+ unless log?(application_name, log_id)
52
+ fail Errors::AdapterResourceNotFoundError,
53
+ "Invalid log file '#{log_id}', not available for application '#{application_name}'"
54
+ end
55
+ if log_id == 'all'
56
+ fetched_lines = []
57
+ (LOG_TYPES.keys - [:all]).each do |current_log_id|
58
+ cc_log_entries(application_name, current_log_id).each do |line|
59
+ line[:nucleus_origin] = current_log_id
60
+ fetched_lines.push(line)
61
+ end
62
+ end
63
+ fetched_lines.sort_by! { |line| line[:time] }
64
+ fetched_lines.collect { |line| format_log_entry(line[:nucleus_origin], line) }
65
+ else
66
+ cc_log_entries(application_name, log_id).collect { |line| format_log_entry(log_id, line) }
67
+ end
68
+ end
69
+
70
+ private
71
+
72
+ def cc_log_entries(app_name, log_id, time = nil, headers_to_use = nil)
73
+ log_name = LOG_TYPES[log_id.to_sym][:name]
74
+ # Hack, do not create fresh headers (which would fail) when in a deferred action
75
+ headers_to_use = headers unless headers_to_use
76
+ if time
77
+ get("/app/#{app_name}/deployment/#{NUCLEUS_DEPLOYMENT}/log/#{log_name}?timestamp=#{time}",
78
+ headers: headers_to_use).body
79
+ else
80
+ get("/app/#{app_name}/deployment/#{NUCLEUS_DEPLOYMENT}/log/#{log_name}", headers: headers_to_use).body
81
+ end
82
+ end
83
+
84
+ def format_log_entry(log_id, line)
85
+ # format according to: https://github.com/cloudControl/cctrl/blob/master/cctrl/output.py
86
+ case log_id.to_sym
87
+ when :request
88
+ "#{line[:remote_host]} #{line[:remote_user]} #{line[:remote_logname]} [#{Time.at(line[:time]).iso8601}] "\
89
+ "#{line[:first_request_line]} #{line[:status]} #{line[:response_size_CLF]} #{line[:referer]} "\
90
+ "#{line[:user_agent]}"
91
+ when :system
92
+ "#{line[:time]} #{line[:wrk_id]} #{line[:message]}"
93
+ when :build
94
+ "#{Time.at(line[:time]).iso8601} [#{line[:hostname]}/#{line[:depl_id]}] #{line[:level]} #{line[:message]}"
95
+ when :error
96
+ "#{line[:time]} #{line[:type]} #{line[:message]}"
97
+ end
98
+ end
99
+ end
100
+ end
101
+ end
102
+ end
103
+ end
@@ -1,88 +1,88 @@
1
- module Nucleus
2
- module Adapters
3
- module V1
4
- class CloudControl < Stub
5
- # cloud control, CRUD operations for the application's environment variable object,
6
- # which is referred to as +config addon+ on the platform.
7
- module Vars
8
- # @see Stub#env_vars
9
- def env_vars(application_id)
10
- cc_vars = cc_vars(application_id)
11
- cc_vars.collect do |key, value|
12
- { id: key, key: key, value: value }
13
- end
14
- end
15
-
16
- # @see Stub#env_var
17
- def env_var(application_id, env_var_key)
18
- cc_vars = cc_vars(application_id)
19
- fail Errors::AdapterResourceNotFoundError,
20
- "Env. var key '#{env_var_key}' does not exist" unless env_var?(application_id, env_var_key, cc_vars)
21
- { id: env_var_key, key: env_var_key, value: cc_vars[env_var_key.to_sym] }
22
- end
23
-
24
- # @see Stub#create_env_var
25
- def create_env_var(application_id, env_var)
26
- cc_vars = cc_vars(application_id)
27
- fail Errors::SemanticAdapterRequestError,
28
- "Env. var key '#{env_var[:key]}' already taken" if env_var?(application_id, env_var[:key], cc_vars)
29
- set_var(application_id, env_var[:key], env_var[:value])
30
- end
31
-
32
- # @see Stub#update_env_var
33
- def update_env_var(application_id, env_var_key, env_var)
34
- cc_vars = cc_vars(application_id)
35
- fail Errors::AdapterResourceNotFoundError,
36
- "Env. var key '#{env_var_key}' does not exist" unless env_var?(application_id, env_var_key, cc_vars)
37
- set_var(application_id, env_var_key, env_var[:value])
38
- end
39
-
40
- # @see Stub#delete_env_var
41
- def delete_env_var(application_id, env_var_key)
42
- cc_vars = cc_vars(application_id)
43
- fail Errors::AdapterResourceNotFoundError,
44
- "Env. var key '#{env_var_key}' does not exist" unless env_var?(application_id, env_var_key, cc_vars)
45
- set_var(application_id, env_var_key, nil)
46
- end
47
-
48
- private
49
-
50
- def cc_vars(application_id)
51
- cc_vars_response = get("app/#{application_id}/deployment/#{NUCLEUS_DEPLOYMENT}/addon/config.free")
52
- cc_vars_response.body[:settings][:CONFIG_VARS]
53
- end
54
-
55
- # Set the variable value (create or update)
56
- # @param [String] application_id id of the cloud control application
57
- # @param [Symbol] key variable key name
58
- # @param [String, Integer, Float, Double] value value to apply to the variable
59
- # @return [Hash] Nucleus representation of the modified variable
60
- def set_var(application_id, key, value)
61
- if value.nil?
62
- # delete the var, set to 'null'
63
- settings = "{\"#{key}\":null}"
64
- else
65
- settings = "{\"#{key}\":\"#{value}\"}"
66
- end
67
- response = put("/app/#{application_id}/deployment/#{NUCLEUS_DEPLOYMENT}/addon/config.free",
68
- body: { addon: 'config.free',
69
- settings: settings,
70
- force: true })
71
- all_vars = response.body[:settings][:CONFIG_VARS]
72
- { id: key, key: key, value: all_vars[key.to_sym] }
73
- end
74
-
75
- # Checks if a variable with the env_var_key already exists.
76
- # @param [String] application_id id of the cloud control application
77
- # @param [Symbol] env_var_key key name that shall be checked for existence
78
- # @param [Hash] all_vars collection of currently existing variables for the application
79
- # @return [Boolean] true if there is a variable with the env_var_key, otherwise false
80
- def env_var?(application_id, env_var_key, all_vars = nil)
81
- all_vars = cc_vars(application_id) if all_vars.nil?
82
- all_vars.key? env_var_key.to_sym
83
- end
84
- end
85
- end
86
- end
87
- end
88
- end
1
+ module Nucleus
2
+ module Adapters
3
+ module V1
4
+ class CloudControl < Stub
5
+ # cloud control, CRUD operations for the application's environment variable object,
6
+ # which is referred to as +config addon+ on the platform.
7
+ module Vars
8
+ # @see Stub#env_vars
9
+ def env_vars(application_id)
10
+ cc_vars = cc_vars(application_id)
11
+ cc_vars.collect do |key, value|
12
+ { id: key, key: key, value: value }
13
+ end
14
+ end
15
+
16
+ # @see Stub#env_var
17
+ def env_var(application_id, env_var_key)
18
+ cc_vars = cc_vars(application_id)
19
+ fail Errors::AdapterResourceNotFoundError,
20
+ "Env. var key '#{env_var_key}' does not exist" unless env_var?(application_id, env_var_key, cc_vars)
21
+ { id: env_var_key, key: env_var_key, value: cc_vars[env_var_key.to_sym] }
22
+ end
23
+
24
+ # @see Stub#create_env_var
25
+ def create_env_var(application_id, env_var)
26
+ cc_vars = cc_vars(application_id)
27
+ fail Errors::SemanticAdapterRequestError,
28
+ "Env. var key '#{env_var[:key]}' already taken" if env_var?(application_id, env_var[:key], cc_vars)
29
+ set_var(application_id, env_var[:key], env_var[:value])
30
+ end
31
+
32
+ # @see Stub#update_env_var
33
+ def update_env_var(application_id, env_var_key, env_var)
34
+ cc_vars = cc_vars(application_id)
35
+ fail Errors::AdapterResourceNotFoundError,
36
+ "Env. var key '#{env_var_key}' does not exist" unless env_var?(application_id, env_var_key, cc_vars)
37
+ set_var(application_id, env_var_key, env_var[:value])
38
+ end
39
+
40
+ # @see Stub#delete_env_var
41
+ def delete_env_var(application_id, env_var_key)
42
+ cc_vars = cc_vars(application_id)
43
+ fail Errors::AdapterResourceNotFoundError,
44
+ "Env. var key '#{env_var_key}' does not exist" unless env_var?(application_id, env_var_key, cc_vars)
45
+ set_var(application_id, env_var_key, nil)
46
+ end
47
+
48
+ private
49
+
50
+ def cc_vars(application_id)
51
+ cc_vars_response = get("app/#{application_id}/deployment/#{NUCLEUS_DEPLOYMENT}/addon/config.free")
52
+ cc_vars_response.body[:settings][:CONFIG_VARS]
53
+ end
54
+
55
+ # Set the variable value (create or update)
56
+ # @param [String] application_id id of the cloud control application
57
+ # @param [Symbol] key variable key name
58
+ # @param [String, Integer, Float, Double] value value to apply to the variable
59
+ # @return [Hash] Nucleus representation of the modified variable
60
+ def set_var(application_id, key, value)
61
+ settings = if value.nil?
62
+ # delete the var, set to 'null'
63
+ "{\"#{key}\":null}"
64
+ else
65
+ "{\"#{key}\":\"#{value}\"}"
66
+ end
67
+ response = put("/app/#{application_id}/deployment/#{NUCLEUS_DEPLOYMENT}/addon/config.free",
68
+ body: { addon: 'config.free',
69
+ settings: settings,
70
+ force: true })
71
+ all_vars = response.body[:settings][:CONFIG_VARS]
72
+ { id: key, key: key, value: all_vars[key.to_sym] }
73
+ end
74
+
75
+ # Checks if a variable with the env_var_key already exists.
76
+ # @param [String] application_id id of the cloud control application
77
+ # @param [Symbol] env_var_key key name that shall be checked for existence
78
+ # @param [Hash] all_vars collection of currently existing variables for the application
79
+ # @return [Boolean] true if there is a variable with the env_var_key, otherwise false
80
+ def env_var?(application_id, env_var_key, all_vars = nil)
81
+ all_vars = cc_vars(application_id) if all_vars.nil?
82
+ all_vars.key? env_var_key.to_sym
83
+ end
84
+ end
85
+ end
86
+ end
87
+ end
88
+ end
@@ -1,149 +1,149 @@
1
- module Nucleus
2
- module Adapters
3
- module V1
4
- class CloudFoundryV2 < Stub
5
- # Application domain / route functionality to support the Cloud Foundry API.<br>
6
- module Domains
7
- # @see Stub#domains
8
- def domains(domain_id)
9
- app_guid = app_guid(domain_id)
10
- assigned_routes = get("/v2/apps/#{app_guid}/routes").body
11
- domains = []
12
- assigned_routes[:resources].each do |assigned_route|
13
- nucleus_domain = route_to_nucleus_domain(assigned_route)
14
- domains.push(nucleus_domain) unless nucleus_domain[:name] == app_web_url(app_guid)
15
- end
16
- domains
17
- end
18
-
19
- # @see Stub#domain
20
- def domain(application_name_or_id, domain_id)
21
- app_guid = app_guid(application_name_or_id)
22
- assigned_routes = get("/v2/apps/#{app_guid}/routes").body
23
- assigned_routes[:resources].each do |assigned_route|
24
- return route_to_nucleus_domain(assigned_route) if assigned_route[:metadata][:guid] == domain_id
25
- end
26
- end
27
-
28
- # @see Stub#create_domain
29
- def create_domain(application_name_or_id, domain)
30
- domains(application_name_or_id).each do |existing_domain|
31
- if existing_domain[:name] == domain[:name]
32
- fail Errors::SemanticAdapterRequestError,
33
- "Domain '#{domain[:name]}' is already assigned to the application"
34
- end
35
- end
36
-
37
- app_guid = app_guid(application_name_or_id)
38
- # extract the hostname and the domain name from the FQDN
39
- /(?<domain_host>([-\w]+\.)*)(?<domain_name>([-\w]+\.[-\w]+))/ =~ domain[:name]
40
- domain_host.chomp!('.') unless domain_host.nil?
41
-
42
- # finally build the response
43
- route_to_nucleus_domain(create_cf_domain(app_guid, domain_name, domain_host))
44
- end
45
-
46
- # @see Stub#delete_domain
47
- def delete_domain(application_name_or_id, route_id)
48
- app_guid = app_guid(application_name_or_id)
49
- # remove route from the app
50
- delete_response = delete("/v2/apps/#{app_guid}/routes/#{route_id}", expects: [201, 400])
51
- if delete_response.status == 400
52
- cf_error = delete_response.body[:code]
53
- if cf_error == 1002
54
- fail Errors::AdapterResourceNotFoundError, 'Domain not found. '\
55
- 'CF context specific: Route does not exist or is not assigned with this application'
56
- else
57
- # delete failed with 400, but not due to invalid domain
58
- fail Errors::AdapterRequestError,
59
- "#{delete_response.body[:description]} (#{cf_error} - #{delete_response.body[:error_code]})"
60
- end
61
- end
62
-
63
- # check route usage
64
- route_in_apps = get("/v2/routes/#{route_id}/apps").body
65
- return unless route_in_apps[:total_results] == 0
66
-
67
- # route is no longer needed, delete
68
- delete("/v2/routes/#{route_id}")
69
- end
70
-
71
- private
72
-
73
- def domain?(application_name_or_id, domain_name)
74
- app_guid = app_guid(application_name_or_id)
75
- domain_without_protocol = %r{([a-zA-Z]+://)?([-\.\w]*)}.match(domain_name)[2]
76
- assigned_routes = get("/v2/apps/#{app_guid}/routes").body
77
- assigned_routes[:resources].each do |route|
78
- route_domain = get(route[:entity][:domain_url]).body
79
- return true if "#{route[:entity][:host]}.#{route_domain[:entity][:name]}" == domain_without_protocol
80
- end
81
- false
82
- end
83
-
84
- def route_to_nucleus_domain(route_resource)
85
- route_entity = route_resource[:entity]
86
- route_metadata = route_resource[:metadata]
87
- assigned_domain = get(route_entity[:domain_url]).body
88
- domain = { id: route_metadata[:guid], created_at: route_metadata[:created_at] }
89
- if route_metadata[:updated_at].to_s == ''
90
- domain[:updated_at] = route_metadata[:created_at]
91
- else
92
- domain[:updated_at] = route_metadata[:updated_at]
93
- end
94
-
95
- if route_entity[:host].to_s == ''
96
- domain[:name] = assigned_domain[:entity][:name]
97
- else
98
- domain[:name] = "#{route_entity[:host]}.#{assigned_domain[:entity][:name]}"
99
- end
100
- domain
101
- end
102
-
103
- def cf_domain(domain_name)
104
- %w(private shared).each do |domain_type|
105
- response = get("/v2/#{domain_type}_domains").body
106
- response[:resources].each do |domain|
107
- return domain if domain[:entity][:name] == domain_name
108
- end
109
- end
110
- nil
111
- end
112
-
113
- def cf_route(domain_guid, domain_host)
114
- # There is no way to check if a root domain (empty hostname) is already taken.
115
- # Therefore we must iterate through all routes and find matches...
116
- all_routes = get('/v2/routes').body[:resources]
117
- all_routes.each do |route|
118
- return route if route[:entity][:domain_guid] == domain_guid && route[:entity][:host] == domain_host
119
- end
120
- nil
121
- end
122
-
123
- def create_cf_domain(app_guid, domain_name, domain_host)
124
- created_domain = cf_domain(domain_name)
125
- unless created_domain
126
- # domain does not exist, create!
127
- domain_request_body = { name: domain_name, owning_organization_guid: default_organization_guid }
128
- created_domain = post('/v2/private_domains', body: domain_request_body).body
129
- end
130
-
131
- created_route = cf_route(created_domain[:metadata][:guid], domain_host)
132
- unless created_route
133
- # route does not exist, create!
134
- route_request_body = { domain_guid: created_domain[:metadata][:guid],
135
- host: domain_host, space_guid: user_space_guid }
136
- created_route = post('/v2/routes', body: route_request_body).body
137
- end
138
-
139
- # assign the route to the application
140
- put("/v2/apps/#{app_guid}/routes/#{created_route[:metadata][:guid]}").body
141
-
142
- # return the actual route, not the association response
143
- created_route
144
- end
145
- end
146
- end
147
- end
148
- end
149
- end
1
+ module Nucleus
2
+ module Adapters
3
+ module V1
4
+ class CloudFoundryV2 < Stub
5
+ # Application domain / route functionality to support the Cloud Foundry API.<br>
6
+ module Domains
7
+ # @see Stub#domains
8
+ def domains(domain_id)
9
+ app_guid = app_guid(domain_id)
10
+ assigned_routes = get("/v2/apps/#{app_guid}/routes").body
11
+ domains = []
12
+ assigned_routes[:resources].each do |assigned_route|
13
+ nucleus_domain = route_to_nucleus_domain(assigned_route)
14
+ domains.push(nucleus_domain) unless nucleus_domain[:name] == app_web_url(app_guid)
15
+ end
16
+ domains
17
+ end
18
+
19
+ # @see Stub#domain
20
+ def domain(application_name_or_id, domain_id)
21
+ app_guid = app_guid(application_name_or_id)
22
+ assigned_routes = get("/v2/apps/#{app_guid}/routes").body
23
+ assigned_routes[:resources].each do |assigned_route|
24
+ return route_to_nucleus_domain(assigned_route) if assigned_route[:metadata][:guid] == domain_id
25
+ end
26
+ end
27
+
28
+ # @see Stub#create_domain
29
+ def create_domain(application_name_or_id, domain)
30
+ domains(application_name_or_id).each do |existing_domain|
31
+ if existing_domain[:name] == domain[:name]
32
+ fail Errors::SemanticAdapterRequestError,
33
+ "Domain '#{domain[:name]}' is already assigned to the application"
34
+ end
35
+ end
36
+
37
+ app_guid = app_guid(application_name_or_id)
38
+ # extract the hostname and the domain name from the FQDN
39
+ /(?<domain_host>([-\w]+\.)*)(?<domain_name>([-\w]+\.[-\w]+))/ =~ domain[:name]
40
+ domain_host.chomp!('.') unless domain_host.nil?
41
+
42
+ # finally build the response
43
+ route_to_nucleus_domain(create_cf_domain(app_guid, domain_name, domain_host))
44
+ end
45
+
46
+ # @see Stub#delete_domain
47
+ def delete_domain(application_name_or_id, route_id)
48
+ app_guid = app_guid(application_name_or_id)
49
+ # remove route from the app
50
+ delete_response = delete("/v2/apps/#{app_guid}/routes/#{route_id}", expects: [201, 400])
51
+ if delete_response.status == 400
52
+ cf_error = delete_response.body[:code]
53
+ if cf_error == 1002
54
+ fail Errors::AdapterResourceNotFoundError, 'Domain not found. '\
55
+ 'CF context specific: Route does not exist or is not assigned with this application'
56
+ else
57
+ # delete failed with 400, but not due to invalid domain
58
+ fail Errors::AdapterRequestError,
59
+ "#{delete_response.body[:description]} (#{cf_error} - #{delete_response.body[:error_code]})"
60
+ end
61
+ end
62
+
63
+ # check route usage
64
+ route_in_apps = get("/v2/routes/#{route_id}/apps").body
65
+ return unless route_in_apps[:total_results] == 0
66
+
67
+ # route is no longer needed, delete
68
+ delete("/v2/routes/#{route_id}")
69
+ end
70
+
71
+ private
72
+
73
+ def domain?(application_name_or_id, domain_name)
74
+ app_guid = app_guid(application_name_or_id)
75
+ domain_without_protocol = %r{([a-zA-Z]+://)?([-\.\w]*)}.match(domain_name)[2]
76
+ assigned_routes = get("/v2/apps/#{app_guid}/routes").body
77
+ assigned_routes[:resources].each do |route|
78
+ route_domain = get(route[:entity][:domain_url]).body
79
+ return true if "#{route[:entity][:host]}.#{route_domain[:entity][:name]}" == domain_without_protocol
80
+ end
81
+ false
82
+ end
83
+
84
+ def route_to_nucleus_domain(route_resource)
85
+ route_entity = route_resource[:entity]
86
+ route_metadata = route_resource[:metadata]
87
+ assigned_domain = get(route_entity[:domain_url]).body
88
+ domain = { id: route_metadata[:guid], created_at: route_metadata[:created_at] }
89
+ domain[:updated_at] = if route_metadata[:updated_at].to_s == ''
90
+ route_metadata[:created_at]
91
+ else
92
+ route_metadata[:updated_at]
93
+ end
94
+
95
+ domain[:name] = if route_entity[:host].to_s == ''
96
+ assigned_domain[:entity][:name]
97
+ else
98
+ "#{route_entity[:host]}.#{assigned_domain[:entity][:name]}"
99
+ end
100
+ domain
101
+ end
102
+
103
+ def cf_domain(domain_name)
104
+ %w(private shared).each do |domain_type|
105
+ response = get("/v2/#{domain_type}_domains").body
106
+ response[:resources].each do |domain|
107
+ return domain if domain[:entity][:name] == domain_name
108
+ end
109
+ end
110
+ nil
111
+ end
112
+
113
+ def cf_route(domain_guid, domain_host)
114
+ # There is no way to check if a root domain (empty hostname) is already taken.
115
+ # Therefore we must iterate through all routes and find matches...
116
+ all_routes = get('/v2/routes').body[:resources]
117
+ all_routes.each do |route|
118
+ return route if route[:entity][:domain_guid] == domain_guid && route[:entity][:host] == domain_host
119
+ end
120
+ nil
121
+ end
122
+
123
+ def create_cf_domain(app_guid, domain_name, domain_host)
124
+ created_domain = cf_domain(domain_name)
125
+ unless created_domain
126
+ # domain does not exist, create!
127
+ domain_request_body = { name: domain_name, owning_organization_guid: default_organization_guid }
128
+ created_domain = post('/v2/private_domains', body: domain_request_body).body
129
+ end
130
+
131
+ created_route = cf_route(created_domain[:metadata][:guid], domain_host)
132
+ unless created_route
133
+ # route does not exist, create!
134
+ route_request_body = { domain_guid: created_domain[:metadata][:guid],
135
+ host: domain_host, space_guid: user_space_guid }
136
+ created_route = post('/v2/routes', body: route_request_body).body
137
+ end
138
+
139
+ # assign the route to the application
140
+ put("/v2/apps/#{app_guid}/routes/#{created_route[:metadata][:guid]}").body
141
+
142
+ # return the actual route, not the association response
143
+ created_route
144
+ end
145
+ end
146
+ end
147
+ end
148
+ end
149
+ end