nucleus 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (224) hide show
  1. checksums.yaml +7 -0
  2. data/.gitattributes +1 -0
  3. data/.gitignore +19 -0
  4. data/.rspec +3 -0
  5. data/.rubocop.yml +44 -0
  6. data/.travis.yml +21 -0
  7. data/CHANGELOG.md +19 -0
  8. data/CONTRIBUTING.md +13 -0
  9. data/Gemfile +16 -0
  10. data/Guardfile +22 -0
  11. data/LICENSE +21 -0
  12. data/README.md +675 -0
  13. data/Rakefile +137 -0
  14. data/bin/nucleus +91 -0
  15. data/bin/nucleus.bat +1 -0
  16. data/config.ru +18 -0
  17. data/config/adapters/cloud_control.yml +32 -0
  18. data/config/adapters/cloud_foundry_v2.yml +61 -0
  19. data/config/adapters/heroku.yml +13 -0
  20. data/config/adapters/openshift_v2.yml +20 -0
  21. data/config/nucleus_config.rb +47 -0
  22. data/lib/nucleus.rb +13 -0
  23. data/lib/nucleus/adapter_resolver.rb +115 -0
  24. data/lib/nucleus/adapters/base_adapter.rb +109 -0
  25. data/lib/nucleus/adapters/buildpack_translator.rb +79 -0
  26. data/lib/nucleus/adapters/v1/cloud_control/application.rb +108 -0
  27. data/lib/nucleus/adapters/v1/cloud_control/authentication.rb +27 -0
  28. data/lib/nucleus/adapters/v1/cloud_control/buildpacks.rb +23 -0
  29. data/lib/nucleus/adapters/v1/cloud_control/cloud_control.rb +153 -0
  30. data/lib/nucleus/adapters/v1/cloud_control/data.rb +76 -0
  31. data/lib/nucleus/adapters/v1/cloud_control/domains.rb +68 -0
  32. data/lib/nucleus/adapters/v1/cloud_control/lifecycle.rb +27 -0
  33. data/lib/nucleus/adapters/v1/cloud_control/log_poller.rb +71 -0
  34. data/lib/nucleus/adapters/v1/cloud_control/logs.rb +103 -0
  35. data/lib/nucleus/adapters/v1/cloud_control/regions.rb +32 -0
  36. data/lib/nucleus/adapters/v1/cloud_control/scaling.rb +17 -0
  37. data/lib/nucleus/adapters/v1/cloud_control/semantic_errors.rb +31 -0
  38. data/lib/nucleus/adapters/v1/cloud_control/services.rb +162 -0
  39. data/lib/nucleus/adapters/v1/cloud_control/token.rb +17 -0
  40. data/lib/nucleus/adapters/v1/cloud_control/vars.rb +88 -0
  41. data/lib/nucleus/adapters/v1/cloud_foundry_v2/app_states.rb +28 -0
  42. data/lib/nucleus/adapters/v1/cloud_foundry_v2/application.rb +111 -0
  43. data/lib/nucleus/adapters/v1/cloud_foundry_v2/authentication.rb +17 -0
  44. data/lib/nucleus/adapters/v1/cloud_foundry_v2/buildpacks.rb +23 -0
  45. data/lib/nucleus/adapters/v1/cloud_foundry_v2/cloud_foundry_v2.rb +141 -0
  46. data/lib/nucleus/adapters/v1/cloud_foundry_v2/data.rb +97 -0
  47. data/lib/nucleus/adapters/v1/cloud_foundry_v2/domains.rb +149 -0
  48. data/lib/nucleus/adapters/v1/cloud_foundry_v2/lifecycle.rb +41 -0
  49. data/lib/nucleus/adapters/v1/cloud_foundry_v2/logs.rb +303 -0
  50. data/lib/nucleus/adapters/v1/cloud_foundry_v2/regions.rb +33 -0
  51. data/lib/nucleus/adapters/v1/cloud_foundry_v2/scaling.rb +15 -0
  52. data/lib/nucleus/adapters/v1/cloud_foundry_v2/semantic_errors.rb +27 -0
  53. data/lib/nucleus/adapters/v1/cloud_foundry_v2/services.rb +286 -0
  54. data/lib/nucleus/adapters/v1/cloud_foundry_v2/vars.rb +80 -0
  55. data/lib/nucleus/adapters/v1/heroku/app_states.rb +57 -0
  56. data/lib/nucleus/adapters/v1/heroku/application.rb +93 -0
  57. data/lib/nucleus/adapters/v1/heroku/authentication.rb +27 -0
  58. data/lib/nucleus/adapters/v1/heroku/buildpacks.rb +27 -0
  59. data/lib/nucleus/adapters/v1/heroku/data.rb +78 -0
  60. data/lib/nucleus/adapters/v1/heroku/domains.rb +43 -0
  61. data/lib/nucleus/adapters/v1/heroku/heroku.rb +146 -0
  62. data/lib/nucleus/adapters/v1/heroku/lifecycle.rb +51 -0
  63. data/lib/nucleus/adapters/v1/heroku/logs.rb +108 -0
  64. data/lib/nucleus/adapters/v1/heroku/regions.rb +42 -0
  65. data/lib/nucleus/adapters/v1/heroku/scaling.rb +28 -0
  66. data/lib/nucleus/adapters/v1/heroku/semantic_errors.rb +23 -0
  67. data/lib/nucleus/adapters/v1/heroku/services.rb +168 -0
  68. data/lib/nucleus/adapters/v1/heroku/vars.rb +65 -0
  69. data/lib/nucleus/adapters/v1/openshift_v2/app_states.rb +68 -0
  70. data/lib/nucleus/adapters/v1/openshift_v2/application.rb +108 -0
  71. data/lib/nucleus/adapters/v1/openshift_v2/authentication.rb +21 -0
  72. data/lib/nucleus/adapters/v1/openshift_v2/data.rb +96 -0
  73. data/lib/nucleus/adapters/v1/openshift_v2/domains.rb +37 -0
  74. data/lib/nucleus/adapters/v1/openshift_v2/lifecycle.rb +60 -0
  75. data/lib/nucleus/adapters/v1/openshift_v2/logs.rb +106 -0
  76. data/lib/nucleus/adapters/v1/openshift_v2/openshift_v2.rb +125 -0
  77. data/lib/nucleus/adapters/v1/openshift_v2/regions.rb +58 -0
  78. data/lib/nucleus/adapters/v1/openshift_v2/scaling.rb +39 -0
  79. data/lib/nucleus/adapters/v1/openshift_v2/semantic_errors.rb +40 -0
  80. data/lib/nucleus/adapters/v1/openshift_v2/services.rb +173 -0
  81. data/lib/nucleus/adapters/v1/openshift_v2/vars.rb +49 -0
  82. data/lib/nucleus/adapters/v1/stub_adapter.rb +464 -0
  83. data/lib/nucleus/core/adapter_authentication_inductor.rb +62 -0
  84. data/lib/nucleus/core/adapter_extensions/auth/auth_client.rb +44 -0
  85. data/lib/nucleus/core/adapter_extensions/auth/authentication_retry_wrapper.rb +79 -0
  86. data/lib/nucleus/core/adapter_extensions/auth/expiring_token_auth_client.rb +53 -0
  87. data/lib/nucleus/core/adapter_extensions/auth/http_basic_auth_client.rb +37 -0
  88. data/lib/nucleus/core/adapter_extensions/auth/o_auth2_auth_client.rb +95 -0
  89. data/lib/nucleus/core/adapter_extensions/auth/token_auth_client.rb +36 -0
  90. data/lib/nucleus/core/adapter_extensions/http_client.rb +177 -0
  91. data/lib/nucleus/core/adapter_extensions/http_tail_client.rb +26 -0
  92. data/lib/nucleus/core/adapter_extensions/tail_stopper.rb +25 -0
  93. data/lib/nucleus/core/common/errors/ambiguous_adapter_error.rb +7 -0
  94. data/lib/nucleus/core/common/errors/file_existence_error.rb +7 -0
  95. data/lib/nucleus/core/common/errors/startup_error.rb +12 -0
  96. data/lib/nucleus/core/common/exit_codes.rb +25 -0
  97. data/lib/nucleus/core/common/files/application_repo_sanitizer.rb +52 -0
  98. data/lib/nucleus/core/common/files/archive_extractor.rb +112 -0
  99. data/lib/nucleus/core/common/files/archiver.rb +91 -0
  100. data/lib/nucleus/core/common/link_generator.rb +46 -0
  101. data/lib/nucleus/core/common/logging/logging.rb +52 -0
  102. data/lib/nucleus/core/common/logging/multi_logger.rb +59 -0
  103. data/lib/nucleus/core/common/logging/request_log_formatter.rb +48 -0
  104. data/lib/nucleus/core/common/ssh_handler.rb +108 -0
  105. data/lib/nucleus/core/common/stream_callback.rb +27 -0
  106. data/lib/nucleus/core/common/thread_config_accessor.rb +85 -0
  107. data/lib/nucleus/core/common/url_converter.rb +28 -0
  108. data/lib/nucleus/core/enums/application_states.rb +26 -0
  109. data/lib/nucleus/core/enums/logfile_types.rb +28 -0
  110. data/lib/nucleus/core/error_messages.rb +127 -0
  111. data/lib/nucleus/core/errors/adapter_error.rb +13 -0
  112. data/lib/nucleus/core/errors/adapter_missing_implementation_error.rb +12 -0
  113. data/lib/nucleus/core/errors/adapter_request_error.rb +10 -0
  114. data/lib/nucleus/core/errors/adapter_resource_not_found_error.rb +10 -0
  115. data/lib/nucleus/core/errors/endpoint_authentication_error.rb +10 -0
  116. data/lib/nucleus/core/errors/platform_specific_semantic_error.rb +12 -0
  117. data/lib/nucleus/core/errors/platform_timeout_error.rb +10 -0
  118. data/lib/nucleus/core/errors/platform_unavailable_error.rb +10 -0
  119. data/lib/nucleus/core/errors/semantic_adapter_request_error.rb +19 -0
  120. data/lib/nucleus/core/errors/unknown_adapter_call_error.rb +10 -0
  121. data/lib/nucleus/core/file_handling/archive_converter.rb +29 -0
  122. data/lib/nucleus/core/file_handling/file_manager.rb +64 -0
  123. data/lib/nucleus/core/file_handling/git_deployer.rb +133 -0
  124. data/lib/nucleus/core/file_handling/git_repo_analyzer.rb +23 -0
  125. data/lib/nucleus/core/import/adapter_configuration.rb +53 -0
  126. data/lib/nucleus/core/import/vendor_parser.rb +28 -0
  127. data/lib/nucleus/core/import/version_detector.rb +18 -0
  128. data/lib/nucleus/core/models/abstract_model.rb +29 -0
  129. data/lib/nucleus/core/models/endpoint.rb +30 -0
  130. data/lib/nucleus/core/models/provider.rb +26 -0
  131. data/lib/nucleus/core/models/vendor.rb +22 -0
  132. data/lib/nucleus/ext/kernel.rb +5 -0
  133. data/lib/nucleus/ext/regexp.rb +49 -0
  134. data/lib/nucleus/os.rb +15 -0
  135. data/lib/nucleus/root_dir.rb +13 -0
  136. data/lib/nucleus/scripts/finalize.rb +8 -0
  137. data/lib/nucleus/scripts/initialize.rb +9 -0
  138. data/lib/nucleus/scripts/initialize_config_defaults.rb +26 -0
  139. data/lib/nucleus/scripts/load.rb +17 -0
  140. data/lib/nucleus/scripts/load_dependencies.rb +43 -0
  141. data/lib/nucleus/scripts/setup_config.rb +28 -0
  142. data/lib/nucleus/scripts/shutdown.rb +11 -0
  143. data/lib/nucleus/version.rb +3 -0
  144. data/nucleus.gemspec +88 -0
  145. data/public/robots.txt +2 -0
  146. data/public/swagger-ui/css/reset.css +125 -0
  147. data/public/swagger-ui/css/screen.css +1224 -0
  148. data/public/swagger-ui/images/apple-touch-icon-114x114.png +0 -0
  149. data/public/swagger-ui/images/apple-touch-icon-120x120.png +0 -0
  150. data/public/swagger-ui/images/apple-touch-icon-144x144.png +0 -0
  151. data/public/swagger-ui/images/apple-touch-icon-152x152.png +0 -0
  152. data/public/swagger-ui/images/apple-touch-icon-57x57.png +0 -0
  153. data/public/swagger-ui/images/apple-touch-icon-60x60.png +0 -0
  154. data/public/swagger-ui/images/apple-touch-icon-72x72.png +0 -0
  155. data/public/swagger-ui/images/apple-touch-icon-76x76.png +0 -0
  156. data/public/swagger-ui/images/explorer_icons.png +0 -0
  157. data/public/swagger-ui/images/favicon-128.png +0 -0
  158. data/public/swagger-ui/images/favicon-16x16.png +0 -0
  159. data/public/swagger-ui/images/favicon-196x196.png +0 -0
  160. data/public/swagger-ui/images/favicon-32x32.png +0 -0
  161. data/public/swagger-ui/images/favicon-96x96.png +0 -0
  162. data/public/swagger-ui/images/favicon.ico +0 -0
  163. data/public/swagger-ui/images/logo_small.png +0 -0
  164. data/public/swagger-ui/images/mstile-144x144.png +0 -0
  165. data/public/swagger-ui/images/mstile-150x150.png +0 -0
  166. data/public/swagger-ui/images/mstile-310x150.png +0 -0
  167. data/public/swagger-ui/images/mstile-310x310.png +0 -0
  168. data/public/swagger-ui/images/mstile-70x70.png +0 -0
  169. data/public/swagger-ui/images/pet_store_api.png +0 -0
  170. data/public/swagger-ui/images/throbber.gif +0 -0
  171. data/public/swagger-ui/images/wordnik_api.png +0 -0
  172. data/public/swagger-ui/index.html +107 -0
  173. data/public/swagger-ui/lib/backbone-min.js +38 -0
  174. data/public/swagger-ui/lib/handlebars-1.0.0.js +2278 -0
  175. data/public/swagger-ui/lib/highlight.7.3.pack.js +1 -0
  176. data/public/swagger-ui/lib/jquery-1.8.0.min.js +2 -0
  177. data/public/swagger-ui/lib/jquery.ba-bbq.min.js +18 -0
  178. data/public/swagger-ui/lib/jquery.slideto.min.js +1 -0
  179. data/public/swagger-ui/lib/jquery.wiggle.min.js +8 -0
  180. data/public/swagger-ui/lib/shred.bundle.js +2765 -0
  181. data/public/swagger-ui/lib/shred/content.js +193 -0
  182. data/public/swagger-ui/lib/swagger-oauth.js +211 -0
  183. data/public/swagger-ui/lib/swagger.js +1653 -0
  184. data/public/swagger-ui/lib/underscore-min.js +32 -0
  185. data/public/swagger-ui/o2c.html +15 -0
  186. data/public/swagger-ui/redirect.html +14 -0
  187. data/public/swagger-ui/swagger-ui.js +2324 -0
  188. data/public/swagger-ui/swagger-ui.min.js +1 -0
  189. data/schemas/api.adapter.schema.yml +31 -0
  190. data/schemas/api.requirements.schema.yml +17 -0
  191. data/spec/factories/models.rb +61 -0
  192. data/spec/integration/api/auth_spec.rb +58 -0
  193. data/spec/integration/api/endpoints_spec.rb +167 -0
  194. data/spec/integration/api/errors_spec.rb +47 -0
  195. data/spec/integration/api/providers_spec.rb +157 -0
  196. data/spec/integration/api/swagger_schema_spec.rb +64 -0
  197. data/spec/integration/api/vendors_spec.rb +45 -0
  198. data/spec/integration/integration_spec_helper.rb +27 -0
  199. data/spec/integration/test_data_generator.rb +55 -0
  200. data/spec/nucleus_git_key.pem +51 -0
  201. data/spec/spec_helper.rb +98 -0
  202. data/spec/support/shared_example_request_types.rb +99 -0
  203. data/spec/test_suites.rake +31 -0
  204. data/spec/unit/adapters/archive_converter_spec.rb +25 -0
  205. data/spec/unit/adapters/file_manager_spec.rb +93 -0
  206. data/spec/unit/adapters/git_deployer_spec.rb +262 -0
  207. data/spec/unit/adapters/v1/stub_spec.rb +14 -0
  208. data/spec/unit/common/helpers/auth_helper_spec.rb +73 -0
  209. data/spec/unit/common/oauth2_auth_client_spec.rb +108 -0
  210. data/spec/unit/common/regexp_spec.rb +33 -0
  211. data/spec/unit/common/request_log_formatter_spec.rb +108 -0
  212. data/spec/unit/common/thread_config_accessor_spec.rb +97 -0
  213. data/spec/unit/models/endpoint_spec.rb +83 -0
  214. data/spec/unit/models/provider_spec.rb +102 -0
  215. data/spec/unit/models/vendor_spec.rb +100 -0
  216. data/spec/unit/schemas/adapter_schema_spec.rb +16 -0
  217. data/spec/unit/schemas/adapter_validation_spec.rb +56 -0
  218. data/spec/unit/schemas/requirements_schema_spec.rb +16 -0
  219. data/spec/unit/unit_spec_helper.rb +11 -0
  220. data/tasks/compatibility.rake +113 -0
  221. data/tasks/evaluation.rake +162 -0
  222. data/wiki/adapter_tests.md +99 -0
  223. data/wiki/implement_new_adapter.md +155 -0
  224. 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