nucleus 0.1.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.
- checksums.yaml +7 -0
- data/.gitattributes +1 -0
- data/.gitignore +19 -0
- data/.rspec +3 -0
- data/.rubocop.yml +44 -0
- data/.travis.yml +21 -0
- data/CHANGELOG.md +19 -0
- data/CONTRIBUTING.md +13 -0
- data/Gemfile +16 -0
- data/Guardfile +22 -0
- data/LICENSE +21 -0
- data/README.md +675 -0
- data/Rakefile +137 -0
- data/bin/nucleus +91 -0
- data/bin/nucleus.bat +1 -0
- data/config.ru +18 -0
- data/config/adapters/cloud_control.yml +32 -0
- data/config/adapters/cloud_foundry_v2.yml +61 -0
- data/config/adapters/heroku.yml +13 -0
- data/config/adapters/openshift_v2.yml +20 -0
- data/config/nucleus_config.rb +47 -0
- data/lib/nucleus.rb +13 -0
- data/lib/nucleus/adapter_resolver.rb +115 -0
- data/lib/nucleus/adapters/base_adapter.rb +109 -0
- data/lib/nucleus/adapters/buildpack_translator.rb +79 -0
- data/lib/nucleus/adapters/v1/cloud_control/application.rb +108 -0
- data/lib/nucleus/adapters/v1/cloud_control/authentication.rb +27 -0
- data/lib/nucleus/adapters/v1/cloud_control/buildpacks.rb +23 -0
- data/lib/nucleus/adapters/v1/cloud_control/cloud_control.rb +153 -0
- data/lib/nucleus/adapters/v1/cloud_control/data.rb +76 -0
- data/lib/nucleus/adapters/v1/cloud_control/domains.rb +68 -0
- data/lib/nucleus/adapters/v1/cloud_control/lifecycle.rb +27 -0
- data/lib/nucleus/adapters/v1/cloud_control/log_poller.rb +71 -0
- data/lib/nucleus/adapters/v1/cloud_control/logs.rb +103 -0
- data/lib/nucleus/adapters/v1/cloud_control/regions.rb +32 -0
- data/lib/nucleus/adapters/v1/cloud_control/scaling.rb +17 -0
- data/lib/nucleus/adapters/v1/cloud_control/semantic_errors.rb +31 -0
- data/lib/nucleus/adapters/v1/cloud_control/services.rb +162 -0
- data/lib/nucleus/adapters/v1/cloud_control/token.rb +17 -0
- data/lib/nucleus/adapters/v1/cloud_control/vars.rb +88 -0
- data/lib/nucleus/adapters/v1/cloud_foundry_v2/app_states.rb +28 -0
- data/lib/nucleus/adapters/v1/cloud_foundry_v2/application.rb +111 -0
- data/lib/nucleus/adapters/v1/cloud_foundry_v2/authentication.rb +17 -0
- data/lib/nucleus/adapters/v1/cloud_foundry_v2/buildpacks.rb +23 -0
- data/lib/nucleus/adapters/v1/cloud_foundry_v2/cloud_foundry_v2.rb +141 -0
- data/lib/nucleus/adapters/v1/cloud_foundry_v2/data.rb +97 -0
- data/lib/nucleus/adapters/v1/cloud_foundry_v2/domains.rb +149 -0
- data/lib/nucleus/adapters/v1/cloud_foundry_v2/lifecycle.rb +41 -0
- data/lib/nucleus/adapters/v1/cloud_foundry_v2/logs.rb +303 -0
- data/lib/nucleus/adapters/v1/cloud_foundry_v2/regions.rb +33 -0
- data/lib/nucleus/adapters/v1/cloud_foundry_v2/scaling.rb +15 -0
- data/lib/nucleus/adapters/v1/cloud_foundry_v2/semantic_errors.rb +27 -0
- data/lib/nucleus/adapters/v1/cloud_foundry_v2/services.rb +286 -0
- data/lib/nucleus/adapters/v1/cloud_foundry_v2/vars.rb +80 -0
- data/lib/nucleus/adapters/v1/heroku/app_states.rb +57 -0
- data/lib/nucleus/adapters/v1/heroku/application.rb +93 -0
- data/lib/nucleus/adapters/v1/heroku/authentication.rb +27 -0
- data/lib/nucleus/adapters/v1/heroku/buildpacks.rb +27 -0
- data/lib/nucleus/adapters/v1/heroku/data.rb +78 -0
- data/lib/nucleus/adapters/v1/heroku/domains.rb +43 -0
- data/lib/nucleus/adapters/v1/heroku/heroku.rb +146 -0
- data/lib/nucleus/adapters/v1/heroku/lifecycle.rb +51 -0
- data/lib/nucleus/adapters/v1/heroku/logs.rb +108 -0
- data/lib/nucleus/adapters/v1/heroku/regions.rb +42 -0
- data/lib/nucleus/adapters/v1/heroku/scaling.rb +28 -0
- data/lib/nucleus/adapters/v1/heroku/semantic_errors.rb +23 -0
- data/lib/nucleus/adapters/v1/heroku/services.rb +168 -0
- data/lib/nucleus/adapters/v1/heroku/vars.rb +65 -0
- data/lib/nucleus/adapters/v1/openshift_v2/app_states.rb +68 -0
- data/lib/nucleus/adapters/v1/openshift_v2/application.rb +108 -0
- data/lib/nucleus/adapters/v1/openshift_v2/authentication.rb +21 -0
- data/lib/nucleus/adapters/v1/openshift_v2/data.rb +96 -0
- data/lib/nucleus/adapters/v1/openshift_v2/domains.rb +37 -0
- data/lib/nucleus/adapters/v1/openshift_v2/lifecycle.rb +60 -0
- data/lib/nucleus/adapters/v1/openshift_v2/logs.rb +106 -0
- data/lib/nucleus/adapters/v1/openshift_v2/openshift_v2.rb +125 -0
- data/lib/nucleus/adapters/v1/openshift_v2/regions.rb +58 -0
- data/lib/nucleus/adapters/v1/openshift_v2/scaling.rb +39 -0
- data/lib/nucleus/adapters/v1/openshift_v2/semantic_errors.rb +40 -0
- data/lib/nucleus/adapters/v1/openshift_v2/services.rb +173 -0
- data/lib/nucleus/adapters/v1/openshift_v2/vars.rb +49 -0
- data/lib/nucleus/adapters/v1/stub_adapter.rb +464 -0
- data/lib/nucleus/core/adapter_authentication_inductor.rb +62 -0
- data/lib/nucleus/core/adapter_extensions/auth/auth_client.rb +44 -0
- data/lib/nucleus/core/adapter_extensions/auth/authentication_retry_wrapper.rb +79 -0
- data/lib/nucleus/core/adapter_extensions/auth/expiring_token_auth_client.rb +53 -0
- data/lib/nucleus/core/adapter_extensions/auth/http_basic_auth_client.rb +37 -0
- data/lib/nucleus/core/adapter_extensions/auth/o_auth2_auth_client.rb +95 -0
- data/lib/nucleus/core/adapter_extensions/auth/token_auth_client.rb +36 -0
- data/lib/nucleus/core/adapter_extensions/http_client.rb +177 -0
- data/lib/nucleus/core/adapter_extensions/http_tail_client.rb +26 -0
- data/lib/nucleus/core/adapter_extensions/tail_stopper.rb +25 -0
- data/lib/nucleus/core/common/errors/ambiguous_adapter_error.rb +7 -0
- data/lib/nucleus/core/common/errors/file_existence_error.rb +7 -0
- data/lib/nucleus/core/common/errors/startup_error.rb +12 -0
- data/lib/nucleus/core/common/exit_codes.rb +25 -0
- data/lib/nucleus/core/common/files/application_repo_sanitizer.rb +52 -0
- data/lib/nucleus/core/common/files/archive_extractor.rb +112 -0
- data/lib/nucleus/core/common/files/archiver.rb +91 -0
- data/lib/nucleus/core/common/link_generator.rb +46 -0
- data/lib/nucleus/core/common/logging/logging.rb +52 -0
- data/lib/nucleus/core/common/logging/multi_logger.rb +59 -0
- data/lib/nucleus/core/common/logging/request_log_formatter.rb +48 -0
- data/lib/nucleus/core/common/ssh_handler.rb +108 -0
- data/lib/nucleus/core/common/stream_callback.rb +27 -0
- data/lib/nucleus/core/common/thread_config_accessor.rb +85 -0
- data/lib/nucleus/core/common/url_converter.rb +28 -0
- data/lib/nucleus/core/enums/application_states.rb +26 -0
- data/lib/nucleus/core/enums/logfile_types.rb +28 -0
- data/lib/nucleus/core/error_messages.rb +127 -0
- data/lib/nucleus/core/errors/adapter_error.rb +13 -0
- data/lib/nucleus/core/errors/adapter_missing_implementation_error.rb +12 -0
- data/lib/nucleus/core/errors/adapter_request_error.rb +10 -0
- data/lib/nucleus/core/errors/adapter_resource_not_found_error.rb +10 -0
- data/lib/nucleus/core/errors/endpoint_authentication_error.rb +10 -0
- data/lib/nucleus/core/errors/platform_specific_semantic_error.rb +12 -0
- data/lib/nucleus/core/errors/platform_timeout_error.rb +10 -0
- data/lib/nucleus/core/errors/platform_unavailable_error.rb +10 -0
- data/lib/nucleus/core/errors/semantic_adapter_request_error.rb +19 -0
- data/lib/nucleus/core/errors/unknown_adapter_call_error.rb +10 -0
- data/lib/nucleus/core/file_handling/archive_converter.rb +29 -0
- data/lib/nucleus/core/file_handling/file_manager.rb +64 -0
- data/lib/nucleus/core/file_handling/git_deployer.rb +133 -0
- data/lib/nucleus/core/file_handling/git_repo_analyzer.rb +23 -0
- data/lib/nucleus/core/import/adapter_configuration.rb +53 -0
- data/lib/nucleus/core/import/vendor_parser.rb +28 -0
- data/lib/nucleus/core/import/version_detector.rb +18 -0
- data/lib/nucleus/core/models/abstract_model.rb +29 -0
- data/lib/nucleus/core/models/endpoint.rb +30 -0
- data/lib/nucleus/core/models/provider.rb +26 -0
- data/lib/nucleus/core/models/vendor.rb +22 -0
- data/lib/nucleus/ext/kernel.rb +5 -0
- data/lib/nucleus/ext/regexp.rb +49 -0
- data/lib/nucleus/os.rb +15 -0
- data/lib/nucleus/root_dir.rb +13 -0
- data/lib/nucleus/scripts/finalize.rb +8 -0
- data/lib/nucleus/scripts/initialize.rb +9 -0
- data/lib/nucleus/scripts/initialize_config_defaults.rb +26 -0
- data/lib/nucleus/scripts/load.rb +17 -0
- data/lib/nucleus/scripts/load_dependencies.rb +43 -0
- data/lib/nucleus/scripts/setup_config.rb +28 -0
- data/lib/nucleus/scripts/shutdown.rb +11 -0
- data/lib/nucleus/version.rb +3 -0
- data/nucleus.gemspec +88 -0
- data/public/robots.txt +2 -0
- data/public/swagger-ui/css/reset.css +125 -0
- data/public/swagger-ui/css/screen.css +1224 -0
- data/public/swagger-ui/images/apple-touch-icon-114x114.png +0 -0
- data/public/swagger-ui/images/apple-touch-icon-120x120.png +0 -0
- data/public/swagger-ui/images/apple-touch-icon-144x144.png +0 -0
- data/public/swagger-ui/images/apple-touch-icon-152x152.png +0 -0
- data/public/swagger-ui/images/apple-touch-icon-57x57.png +0 -0
- data/public/swagger-ui/images/apple-touch-icon-60x60.png +0 -0
- data/public/swagger-ui/images/apple-touch-icon-72x72.png +0 -0
- data/public/swagger-ui/images/apple-touch-icon-76x76.png +0 -0
- data/public/swagger-ui/images/explorer_icons.png +0 -0
- data/public/swagger-ui/images/favicon-128.png +0 -0
- data/public/swagger-ui/images/favicon-16x16.png +0 -0
- data/public/swagger-ui/images/favicon-196x196.png +0 -0
- data/public/swagger-ui/images/favicon-32x32.png +0 -0
- data/public/swagger-ui/images/favicon-96x96.png +0 -0
- data/public/swagger-ui/images/favicon.ico +0 -0
- data/public/swagger-ui/images/logo_small.png +0 -0
- data/public/swagger-ui/images/mstile-144x144.png +0 -0
- data/public/swagger-ui/images/mstile-150x150.png +0 -0
- data/public/swagger-ui/images/mstile-310x150.png +0 -0
- data/public/swagger-ui/images/mstile-310x310.png +0 -0
- data/public/swagger-ui/images/mstile-70x70.png +0 -0
- data/public/swagger-ui/images/pet_store_api.png +0 -0
- data/public/swagger-ui/images/throbber.gif +0 -0
- data/public/swagger-ui/images/wordnik_api.png +0 -0
- data/public/swagger-ui/index.html +107 -0
- data/public/swagger-ui/lib/backbone-min.js +38 -0
- data/public/swagger-ui/lib/handlebars-1.0.0.js +2278 -0
- data/public/swagger-ui/lib/highlight.7.3.pack.js +1 -0
- data/public/swagger-ui/lib/jquery-1.8.0.min.js +2 -0
- data/public/swagger-ui/lib/jquery.ba-bbq.min.js +18 -0
- data/public/swagger-ui/lib/jquery.slideto.min.js +1 -0
- data/public/swagger-ui/lib/jquery.wiggle.min.js +8 -0
- data/public/swagger-ui/lib/shred.bundle.js +2765 -0
- data/public/swagger-ui/lib/shred/content.js +193 -0
- data/public/swagger-ui/lib/swagger-oauth.js +211 -0
- data/public/swagger-ui/lib/swagger.js +1653 -0
- data/public/swagger-ui/lib/underscore-min.js +32 -0
- data/public/swagger-ui/o2c.html +15 -0
- data/public/swagger-ui/redirect.html +14 -0
- data/public/swagger-ui/swagger-ui.js +2324 -0
- data/public/swagger-ui/swagger-ui.min.js +1 -0
- data/schemas/api.adapter.schema.yml +31 -0
- data/schemas/api.requirements.schema.yml +17 -0
- data/spec/factories/models.rb +61 -0
- data/spec/integration/api/auth_spec.rb +58 -0
- data/spec/integration/api/endpoints_spec.rb +167 -0
- data/spec/integration/api/errors_spec.rb +47 -0
- data/spec/integration/api/providers_spec.rb +157 -0
- data/spec/integration/api/swagger_schema_spec.rb +64 -0
- data/spec/integration/api/vendors_spec.rb +45 -0
- data/spec/integration/integration_spec_helper.rb +27 -0
- data/spec/integration/test_data_generator.rb +55 -0
- data/spec/nucleus_git_key.pem +51 -0
- data/spec/spec_helper.rb +98 -0
- data/spec/support/shared_example_request_types.rb +99 -0
- data/spec/test_suites.rake +31 -0
- data/spec/unit/adapters/archive_converter_spec.rb +25 -0
- data/spec/unit/adapters/file_manager_spec.rb +93 -0
- data/spec/unit/adapters/git_deployer_spec.rb +262 -0
- data/spec/unit/adapters/v1/stub_spec.rb +14 -0
- data/spec/unit/common/helpers/auth_helper_spec.rb +73 -0
- data/spec/unit/common/oauth2_auth_client_spec.rb +108 -0
- data/spec/unit/common/regexp_spec.rb +33 -0
- data/spec/unit/common/request_log_formatter_spec.rb +108 -0
- data/spec/unit/common/thread_config_accessor_spec.rb +97 -0
- data/spec/unit/models/endpoint_spec.rb +83 -0
- data/spec/unit/models/provider_spec.rb +102 -0
- data/spec/unit/models/vendor_spec.rb +100 -0
- data/spec/unit/schemas/adapter_schema_spec.rb +16 -0
- data/spec/unit/schemas/adapter_validation_spec.rb +56 -0
- data/spec/unit/schemas/requirements_schema_spec.rb +16 -0
- data/spec/unit/unit_spec_helper.rb +11 -0
- data/tasks/compatibility.rake +113 -0
- data/tasks/evaluation.rake +162 -0
- data/wiki/adapter_tests.md +99 -0
- data/wiki/implement_new_adapter.md +155 -0
- metadata +836 -0
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# The MultiLogger allows to log messages not only to a file OR the stdout,
|
|
2
|
+
# but to both or even more loggers at the same time.
|
|
3
|
+
# The severity defaults to WARN but can be specified when instantiating the MultiLogger.
|
|
4
|
+
#
|
|
5
|
+
# log_1 = Logger.new(STDOUT)
|
|
6
|
+
# log_2 = Logger.new(File.open('/tmp/foo'))
|
|
7
|
+
# multi_logger = MultiLogger.new(:level => Logger::WARN, :loggers => log_1)
|
|
8
|
+
# multi_logger.add_logger(log_2)
|
|
9
|
+
# multi_logger.warn('Something interesting happened.')
|
|
10
|
+
#
|
|
11
|
+
# By Chris Lowder, see https://gist.github.com/clowder/3639600
|
|
12
|
+
class MultiLogger
|
|
13
|
+
attr_reader :level
|
|
14
|
+
|
|
15
|
+
# Initialize the MultiLogger, specify the severity level for all loggers
|
|
16
|
+
# and add one or more loggers.
|
|
17
|
+
#
|
|
18
|
+
# @param [Hash] args the options to create a message with.
|
|
19
|
+
# @option args [Integer] :level (2) The severity level
|
|
20
|
+
# @option args [Array<Logger>] :loggers ([]) The loggers that are initially to be added
|
|
21
|
+
# @return the object
|
|
22
|
+
def initialize(args = {})
|
|
23
|
+
@level = args[:level] || Logger::Severity::WARN
|
|
24
|
+
@loggers = []
|
|
25
|
+
|
|
26
|
+
Array(args[:loggers]).each { |logger| add_logger(logger) }
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# Add a logger to the MultiLogger and adjust its level to the MultiLogger's current level.
|
|
30
|
+
#
|
|
31
|
+
# @param [Logger] logger the logger to add to the MultiLogger instance
|
|
32
|
+
def add_logger(logger)
|
|
33
|
+
logger.level = level
|
|
34
|
+
@loggers << logger
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# Adjust the MultiLogger's current level.
|
|
38
|
+
#
|
|
39
|
+
# @param [Integer] level the severity level to apply to the MultiLogger instance
|
|
40
|
+
def level=(level)
|
|
41
|
+
@level = level
|
|
42
|
+
@loggers.each { |logger| logger.level = level }
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# Close each Logger of the MultiLogger instance
|
|
46
|
+
def close
|
|
47
|
+
@loggers.map(&:close)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
Logger::Severity.constants.each do |level|
|
|
51
|
+
define_method(level.downcase) do |*args|
|
|
52
|
+
@loggers.each { |logger| logger.send(level.downcase, args) }
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
define_method("#{level.downcase}?".to_sym) do
|
|
56
|
+
@level <= Logger::Severity.const_get(level)
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
require 'English'
|
|
2
|
+
|
|
3
|
+
module Nucleus
|
|
4
|
+
module Logging
|
|
5
|
+
class Formatter
|
|
6
|
+
FORMAT = "%s, %38s [%s#%d] %5s -- %s: %s\n"
|
|
7
|
+
|
|
8
|
+
attr_accessor :datetime_format
|
|
9
|
+
|
|
10
|
+
def initialize
|
|
11
|
+
@datetime_format = nil
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def call(severity, time, progname, msg)
|
|
15
|
+
if Thread.current[:nucleus_request_id].nil?
|
|
16
|
+
# if there is no request id, then fill up the space
|
|
17
|
+
request_part = "[#{'*' * 36}]"
|
|
18
|
+
else
|
|
19
|
+
request_part = "[#{Thread.current[:nucleus_request_id]}]"
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
format(FORMAT, severity[0..0], request_part, format_datetime(time),
|
|
23
|
+
$PID, severity, progname, msg2str(msg))
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
private
|
|
27
|
+
|
|
28
|
+
def format_datetime(time)
|
|
29
|
+
if @datetime_format.nil?
|
|
30
|
+
format(time.strftime('%Y-%m-%dT%H:%M:%S.') << '%06d ', time.usec)
|
|
31
|
+
else
|
|
32
|
+
time.strftime(@datetime_format)
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def msg2str(msg)
|
|
37
|
+
case msg
|
|
38
|
+
when ::String
|
|
39
|
+
msg
|
|
40
|
+
when ::Exception
|
|
41
|
+
"#{msg.message} (#{msg.class})\n" << (msg.backtrace || []).join("\n")
|
|
42
|
+
else
|
|
43
|
+
msg.inspect
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
module Nucleus
|
|
2
|
+
class SSHHandler
|
|
3
|
+
include Nucleus::Logging
|
|
4
|
+
|
|
5
|
+
attr_reader :key_file
|
|
6
|
+
|
|
7
|
+
# Setup the SSHHandler.
|
|
8
|
+
# @param [String] custom_ssh_key_file path to the key file
|
|
9
|
+
# @return [Nucleus::SSHHandler] the created instance
|
|
10
|
+
def initialize(custom_ssh_key_file = nil)
|
|
11
|
+
@custom_ssh_key_file = custom_ssh_key_file
|
|
12
|
+
@key_file = @custom_ssh_key_file
|
|
13
|
+
@key_file_history = []
|
|
14
|
+
@agent_file_history = []
|
|
15
|
+
|
|
16
|
+
if custom_ssh_key_file
|
|
17
|
+
# file must not be accessible by others, otherwise usage will be forbidden by git.
|
|
18
|
+
FileUtils.chmod(0600, custom_ssh_key_file) if OS.unix?
|
|
19
|
+
cache_public_key
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# create the initial agent / key combination
|
|
23
|
+
create_agent_key_combo
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# Get the public key that shall be used for authentication.
|
|
27
|
+
# Furthermore, the method assures that the agent, which was registered in the GIT_SSH variable, is available.
|
|
28
|
+
# @return [String] ssh public key in full format (type, value, comment)
|
|
29
|
+
def public_key
|
|
30
|
+
return @public_key if File.exist?(@agent_file) && File.exist?(@key_file)
|
|
31
|
+
# create the agent and key if at least one file does not exist (anymore)
|
|
32
|
+
create_agent_key_combo
|
|
33
|
+
@public_key
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# Cleanup all created tmp files. Usually to be invoked before shutdown.
|
|
37
|
+
def cleanup
|
|
38
|
+
@agent_file_history.each { |af| FileUtils.rm_f(af) }
|
|
39
|
+
@key_file_history.each { |kf| FileUtils.rm_f(kf) }
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
private
|
|
43
|
+
|
|
44
|
+
def create_agent_key_combo
|
|
45
|
+
log.debug('(Re-) creating the nucleus ssh agent')
|
|
46
|
+
|
|
47
|
+
# create a new private key
|
|
48
|
+
create_private_key
|
|
49
|
+
|
|
50
|
+
# save the agent file
|
|
51
|
+
create_agent
|
|
52
|
+
|
|
53
|
+
# add to history so that it will be cleaned up
|
|
54
|
+
add_history_entry
|
|
55
|
+
|
|
56
|
+
# finally apply the custom SSH script
|
|
57
|
+
Git.configure { |config| config.git_ssh = @agent_file }
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def create_private_key
|
|
61
|
+
# only create a new tmp key if no custom key location was specified
|
|
62
|
+
return if @custom_ssh_key_file
|
|
63
|
+
|
|
64
|
+
log.debug('Create new private key file')
|
|
65
|
+
@key_file = File.expand_path(File.join(Dir.tmpdir, 'nucleus', 'ssh', 'key', "#{SecureRandom.uuid}.pem"))
|
|
66
|
+
# make sure key file path exists
|
|
67
|
+
FileUtils.mkdir_p(File.dirname(@key_file))
|
|
68
|
+
|
|
69
|
+
# generates default key of type RSA and with 2048 bits
|
|
70
|
+
File.write(@key_file, SSHKey.generate(type: 'RSA', bits: 2048, comment: 'tmp_key_4_nucleus').private_key)
|
|
71
|
+
cache_public_key
|
|
72
|
+
|
|
73
|
+
# file must not be accessible by others, otherwise usage will be forbidden by git.
|
|
74
|
+
FileUtils.chmod(0600, @key_file) if OS.unix?
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def create_agent
|
|
78
|
+
# use uuid so that more than one instance can run at the same time
|
|
79
|
+
@agent_file = File.expand_path(File.join(Dir.tmpdir, 'nucleus', 'ssh', 'agent', SecureRandom.uuid))
|
|
80
|
+
# windows requires the extension, otherwise git complains that it can't spawn such a file
|
|
81
|
+
@agent_file = "#{@agent_file}.bat" if OS.windows?
|
|
82
|
+
# make sure agent file path exists
|
|
83
|
+
FileUtils.mkdir_p(File.dirname(@agent_file))
|
|
84
|
+
|
|
85
|
+
# adapt the agent file to OS specific requirements
|
|
86
|
+
if OS.unix?
|
|
87
|
+
File.write(@agent_file, "ssh -i #{@key_file} -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no $*")
|
|
88
|
+
FileUtils.chmod(0700, @agent_file)
|
|
89
|
+
else
|
|
90
|
+
File.write(@agent_file, "@echo off\r\nssh -i #{@key_file} -o UserKnownHostsFile=NUL "\
|
|
91
|
+
'-o StrictHostKeyChecking=no %*')
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def add_history_entry
|
|
96
|
+
@key_file_history.push(@key_file) unless @custom_ssh_key_file
|
|
97
|
+
@agent_file_history.push(@agent_file)
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def cache_public_key
|
|
101
|
+
@public_key = SSHKey.new(File.read(@key_file), comment: 'Nucleus').ssh_public_key
|
|
102
|
+
rescue
|
|
103
|
+
msg = "Invalid custom SSH key '#{@key_file}', must be of type ssh-rsa."
|
|
104
|
+
STDERR.puts msg
|
|
105
|
+
raise Nucleus::StartupError.new(msg, Nucleus::ExitCodes::INVALID_SSH_KEY)
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
module Nucleus
|
|
2
|
+
class StreamCallback
|
|
3
|
+
include Nucleus::Logging
|
|
4
|
+
|
|
5
|
+
attr_accessor :closed
|
|
6
|
+
|
|
7
|
+
def initialize(stream)
|
|
8
|
+
@stream = stream
|
|
9
|
+
@closed = false
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
# Send a message via the stream to the client
|
|
13
|
+
# @param [String] message content to send to the client
|
|
14
|
+
def send_message(message)
|
|
15
|
+
log.debug "New streamed message part: #{message}"
|
|
16
|
+
@stream.chunk message
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# Close the stream
|
|
20
|
+
# @return [void]
|
|
21
|
+
def close
|
|
22
|
+
log.debug 'Close API stream, invoked by adapter callback'
|
|
23
|
+
# close API stream of the Rack server unless it was already closed
|
|
24
|
+
@stream.close unless @closed
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
# Redefines the Class and provides the thread_lib_accessor.
|
|
2
|
+
#
|
|
3
|
+
# If invoked with a variable name, the class and its instances can be used
|
|
4
|
+
# as a key-value config for that variable name.
|
|
5
|
+
# E.g.:
|
|
6
|
+
# class ThreadedConfig
|
|
7
|
+
# thread_config_accessor :some_setting, :default => 5
|
|
8
|
+
# end
|
|
9
|
+
#
|
|
10
|
+
# class TestThreadedConfig < Test::Unit::TestCase
|
|
11
|
+
# def test_that_the_accessors_work!
|
|
12
|
+
# # create instance
|
|
13
|
+
# config = ThreadedConfig.new
|
|
14
|
+
# # assign value to the class
|
|
15
|
+
# ThreadedConfig.setting_a = 1
|
|
16
|
+
#
|
|
17
|
+
# # value is equal for both, class and instance
|
|
18
|
+
# assert_equal 1, ThreadedConfig.setting_a
|
|
19
|
+
# assert_equal 1, config.setting_a
|
|
20
|
+
#
|
|
21
|
+
# # create new Thread, which should NOT have the values assigned
|
|
22
|
+
# Thread.new {
|
|
23
|
+
# config.setting_a = 2
|
|
24
|
+
# assert_equal 2, ThreadedConfig.setting_a
|
|
25
|
+
# assert_equal 2, config.setting_a
|
|
26
|
+
# }.join
|
|
27
|
+
#
|
|
28
|
+
# # create new Thread and assert the default value was assigned
|
|
29
|
+
# Thread.new { assert_equal 5, ThreadedConfig.setting_a }.join
|
|
30
|
+
#
|
|
31
|
+
# assert_equal 1, ThreadedConfig.setting_a
|
|
32
|
+
# end
|
|
33
|
+
# end
|
|
34
|
+
#
|
|
35
|
+
# By coderrr (Steve), see https://coderrr.wordpress.com/2008/04/10/lets-stop-polluting-the-threadcurrent-hash/
|
|
36
|
+
class Class
|
|
37
|
+
# Binds accessors to the class and its instances and allows to use them as thread-bound config
|
|
38
|
+
def thread_config_accessor(name, options = {})
|
|
39
|
+
mod = Module.new
|
|
40
|
+
mod.module_eval do
|
|
41
|
+
class_variable_set :"@@#{name}", Hash.new { |h, k| h[k] = options[:default] }
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# use finalizer to prevent memory leaks and clean-up when threads die
|
|
45
|
+
mod.module_eval %{
|
|
46
|
+
FINALIZER = lambda {|id| @@#{name}.delete id }
|
|
47
|
+
|
|
48
|
+
def #{name}
|
|
49
|
+
@@#{name}[Thread.current.object_id]
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def #{name}=(val)
|
|
53
|
+
ObjectSpace.define_finalizer Thread.current, FINALIZER unless @@#{name}.has_key? Thread.current.object_id
|
|
54
|
+
@@#{name}[Thread.current.object_id] = val
|
|
55
|
+
end
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
class_eval do
|
|
59
|
+
include mod
|
|
60
|
+
extend mod
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# Binds accessors to the class and its instances and allows to use them as (read-only) thread-bound config
|
|
65
|
+
def thread_config_accessor_readonly(name, options = {})
|
|
66
|
+
mod = Module.new
|
|
67
|
+
mod.module_eval do
|
|
68
|
+
class_variable_set :"@@#{name}", Hash.new { |h, k| h[k] = options[:default] }
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
# use finalizer to prevent memory leaks and clean-up when threads die
|
|
72
|
+
mod.module_eval %{(
|
|
73
|
+
FINALIZER = lambda {|id| @@#{name}.delete id }
|
|
74
|
+
|
|
75
|
+
def #{name}
|
|
76
|
+
@@#{name}[Thread.current.object_id]
|
|
77
|
+
end
|
|
78
|
+
)}
|
|
79
|
+
|
|
80
|
+
class_eval do
|
|
81
|
+
include mod
|
|
82
|
+
extend mod
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
module Nucleus
|
|
2
|
+
module UrlConverter
|
|
3
|
+
# Convert the URL to the secure 'HTTPS' scheme. Passed URLs must be in one of the following forms:
|
|
4
|
+
# {scheme}://{prefix.}host.domain
|
|
5
|
+
# {prefix.}host.domain
|
|
6
|
+
#
|
|
7
|
+
# An url that would raise an {::ArgumentError} is
|
|
8
|
+
# /path/to/somewhere
|
|
9
|
+
#
|
|
10
|
+
# @param [String] url_to_secure url that shall be converted to use HTTPS
|
|
11
|
+
# @raise ArgumentError if url is not absolute, starts with a '/'
|
|
12
|
+
# @return [String] url with HTTPS scheme
|
|
13
|
+
def secure_url(url_to_secure)
|
|
14
|
+
# return if URL already is secure
|
|
15
|
+
return url_to_secure if url_to_secure =~ /\A#{URI.regexp(['https'])}\z/
|
|
16
|
+
throw ArgumentError, "Invalid URL '#{url_to_secure}', can't secure relative URL" if url_to_secure.start_with?('/')
|
|
17
|
+
uri = URI.parse(url_to_secure)
|
|
18
|
+
if uri.scheme.nil?
|
|
19
|
+
uri = "https://#{url_to_secure}"
|
|
20
|
+
secured_url = uri.to_s
|
|
21
|
+
elsif uri.scheme != 'https'
|
|
22
|
+
uri.scheme = 'https'
|
|
23
|
+
secured_url = uri.to_s
|
|
24
|
+
end
|
|
25
|
+
secured_url
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
module Nucleus
|
|
2
|
+
module Enums
|
|
3
|
+
# All states that an application can obtain according to the lifecycle of Nucleus apps.
|
|
4
|
+
module ApplicationStates
|
|
5
|
+
# Application is created, no data has been deployed yet to any of the instances.
|
|
6
|
+
CREATED = :created
|
|
7
|
+
# Application crashed, none of the instances is running.
|
|
8
|
+
# here was an error while starting or running the application-
|
|
9
|
+
CRASHED = :crashed
|
|
10
|
+
# All instances of the application were idled by the platform.
|
|
11
|
+
IDLE = :idle
|
|
12
|
+
# At least one instance of the application is running.
|
|
13
|
+
RUNNING = :running
|
|
14
|
+
# All instances of the already deployed application are stopped.
|
|
15
|
+
STOPPED = :stopped
|
|
16
|
+
# The application data has been deployed, but the application was not started yet. No instance is running.
|
|
17
|
+
DEPLOYED = :deployed
|
|
18
|
+
|
|
19
|
+
# List all application states.
|
|
20
|
+
# @return [Array<Symbol>] Symbols representing an application state
|
|
21
|
+
def self.all
|
|
22
|
+
constants
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
module Nucleus
|
|
2
|
+
module Enums
|
|
3
|
+
# All types of logs that are distinguished by Nucleus.
|
|
4
|
+
module ApplicationLogfileType
|
|
5
|
+
# The API log aggregates all messages that changed the application state, e.g. updating an application.
|
|
6
|
+
API = :api
|
|
7
|
+
# The application log includes all messages of the application itself
|
|
8
|
+
APPLICATION = :application
|
|
9
|
+
# The build log shows the information of the recent build process(es).
|
|
10
|
+
BUILD = :build
|
|
11
|
+
# The error log shows all logged error messages
|
|
12
|
+
ERROR = :error
|
|
13
|
+
# All logs marked as +other+ can't be assigned to any of the other states
|
|
14
|
+
OTHER = :other
|
|
15
|
+
# The request log shows all requests that were made to the application
|
|
16
|
+
REQUEST = :request
|
|
17
|
+
# System logs aggregate all system relevant outputs,
|
|
18
|
+
# e.g. calling lifecycle operations on application instances
|
|
19
|
+
SYSTEM = :system
|
|
20
|
+
|
|
21
|
+
# List all types of log files.
|
|
22
|
+
# @return [Array<Symbol>] Symbols representing a log file type
|
|
23
|
+
def self.all
|
|
24
|
+
constants
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
module Nucleus
|
|
2
|
+
# The {ErrorMessages} module groups all error definitions that can be returned by the RESTful API.
|
|
3
|
+
# With its constants, it provides the skeleton to create error messages that comply with the error schema of Nucleus.
|
|
4
|
+
module ErrorMessages
|
|
5
|
+
#################
|
|
6
|
+
# CLIENT ERRORS #
|
|
7
|
+
#################
|
|
8
|
+
|
|
9
|
+
ENDPOINT_BAD_REQUEST = {
|
|
10
|
+
status: 400,
|
|
11
|
+
error_code: 400_001,
|
|
12
|
+
message: 'Bad Request'
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
AUTH_BAD_REQUEST = {
|
|
16
|
+
status: 400,
|
|
17
|
+
error_code: 400_002,
|
|
18
|
+
message: 'Bad Authentication Request'
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
BAD_REQUEST_VALIDATION = {
|
|
22
|
+
status: 400,
|
|
23
|
+
error_code: 400_003,
|
|
24
|
+
message: 'Bad Request: Parameter validation failed'
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
BAD_REQUEST_APP_ARCHIVE = {
|
|
28
|
+
status: 400,
|
|
29
|
+
error_code: 400_004,
|
|
30
|
+
message: 'Bad Request: Application archive is damaged or did not match the declared file format'
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
AUTH_UNAUTHORIZED = {
|
|
34
|
+
status: 401,
|
|
35
|
+
error_code: 401_000,
|
|
36
|
+
message: 'Unauthorized: Authentication failed'
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
ENDPOINT_AUTH_FAILED = {
|
|
40
|
+
status: 401,
|
|
41
|
+
error_code: 401_001,
|
|
42
|
+
message: 'Authentication failed, endpoint rejected authentication attempt'
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
NOT_FOUND = {
|
|
46
|
+
status: 404,
|
|
47
|
+
error_code: 404_000,
|
|
48
|
+
message: 'The resource could not be found'
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
ENDPOINT_NOT_FOUND = {
|
|
52
|
+
status: 404,
|
|
53
|
+
error_code: 404_001,
|
|
54
|
+
message: 'The resource could not be found'
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
INVALID_ACCEPT_HEADER = {
|
|
58
|
+
status: 406,
|
|
59
|
+
error_code: 406_000,
|
|
60
|
+
message: 'Invalid Accept header, vendor or version not found'
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
BAD_REQUEST_ENTITY = {
|
|
64
|
+
status: 422,
|
|
65
|
+
error_code: 422_000,
|
|
66
|
+
message: 'Unprocessable Entity: Request was valid, but has been rejected by the endpoint, '\
|
|
67
|
+
'saying the message was semantically false. Check the dev_message for detailed error analysis'
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
# All platform specific semantic errors should have a unique error code!
|
|
71
|
+
PLATFORM_SPECIFIC_ERROR_ENTITY = {
|
|
72
|
+
status: 422,
|
|
73
|
+
error_code: 422_001,
|
|
74
|
+
message: 'Unprocessable Entity: Request format was valid, but has been rejected by the endpoint, '\
|
|
75
|
+
'saying the message contains data that can not be processed by this specific platform.'
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
# Quota violations are a common issue and therefore deserve their own message ;)
|
|
79
|
+
PLATFORM_QUOTA_ERROR = {
|
|
80
|
+
status: 422,
|
|
81
|
+
error_code: 422_002,
|
|
82
|
+
message: 'Unprocessable Entity: Request format was valid, but has been rejected by the endpoint. '\
|
|
83
|
+
'Your account would exceed its quota limits. Please check your account and its billing status.'
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
#################
|
|
87
|
+
# SERVER ERRORS #
|
|
88
|
+
#################
|
|
89
|
+
|
|
90
|
+
RESCUED = {
|
|
91
|
+
status: 500,
|
|
92
|
+
error_code: 500_000,
|
|
93
|
+
message: 'Oops, something went terribly wrong here :/'
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
RESCUED_ADAPTER_CALL = {
|
|
97
|
+
status: 500,
|
|
98
|
+
error_code: 500_001,
|
|
99
|
+
message: 'Endpoint call failed with unforeseen cause'
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
RESCUED_ADAPTER_CALL_SERVER = {
|
|
103
|
+
status: 500,
|
|
104
|
+
error_code: 500_002,
|
|
105
|
+
message: 'Endpoint crashed with server error'
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
MISSING_IMPLEMENTATION = {
|
|
109
|
+
status: 501,
|
|
110
|
+
error_code: 501_000,
|
|
111
|
+
message: 'Not Implemented'
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
UNAVAILABLE = {
|
|
115
|
+
status: 503,
|
|
116
|
+
error_code: 503_000,
|
|
117
|
+
message: 'Service Unavailable'
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
PLATFORM_GATEWAY_TIMEOUT = {
|
|
121
|
+
status: 504,
|
|
122
|
+
error_code: 504_000,
|
|
123
|
+
message: 'Gateway Timeout. The platform raised an internal Timeout error. We don\'t know to what '\
|
|
124
|
+
'degree the request has been processed, or if it wasn\'t executed at all.'
|
|
125
|
+
}
|
|
126
|
+
end
|
|
127
|
+
end
|