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.
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