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