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,26 @@
|
|
1
|
+
module Nucleus
|
2
|
+
module Adapters
|
3
|
+
module HttpTailClient
|
4
|
+
# Executes a request to the given URL and expects a streaming response.<br>
|
5
|
+
# Each new chunk (usually lines) will be forwarded to the client via the api_stream.
|
6
|
+
#
|
7
|
+
# @param [String] url url to call
|
8
|
+
# @param [Nucleus::API::StreamCallback] api_stream stream to which new chunks will be forwarded
|
9
|
+
# @param [Symbol] http_method HTTP method to use
|
10
|
+
def tail_http_response(url, api_stream, http_method = :get)
|
11
|
+
http_connection = EventMachine::HttpRequest.new(url, inactivity_timeout: 0)
|
12
|
+
http_client = http_connection.send(http_method, keepalive: true)
|
13
|
+
|
14
|
+
# close stream on error
|
15
|
+
http_client.on_error do
|
16
|
+
log.debug('HttpTailClient detected an error, close stream...')
|
17
|
+
api_stream.close
|
18
|
+
end
|
19
|
+
# tail and immediately push the results to the stream
|
20
|
+
http_client.stream { |chunk| api_stream.send_message(chunk) }
|
21
|
+
# return object that responds to :stop and cancels the tailing request
|
22
|
+
TailStopper.new(http_connection, :close)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Nucleus
|
2
|
+
module Adapters
|
3
|
+
# The TailStopper can be used to cancel a timer or an ongoing HTTP request,
|
4
|
+
# e.g. when the underlying connection was terminated.
|
5
|
+
class TailStopper
|
6
|
+
include Nucleus::Logging
|
7
|
+
|
8
|
+
def initialize(polling, method_to_stop)
|
9
|
+
@polling = polling
|
10
|
+
@method_to_stop = method_to_stop
|
11
|
+
end
|
12
|
+
|
13
|
+
# Stop the tailing
|
14
|
+
# @return [void]
|
15
|
+
def stop
|
16
|
+
log.debug('Stop tail updates, connection was closed')
|
17
|
+
begin
|
18
|
+
@polling.method(@method_to_stop).call
|
19
|
+
rescue
|
20
|
+
log.debug('Ignore error while closing connection')
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
module Nucleus
|
2
|
+
class StartupError < StandardError
|
3
|
+
# exit code to use when exiting the application due to this error
|
4
|
+
attr_accessor :exit_code
|
5
|
+
|
6
|
+
# initialize with default exit code of ExitCodes::STARTUP_ERROR
|
7
|
+
def initialize(message, exit_code = ExitCodes::STARTUP_ERROR)
|
8
|
+
super(message)
|
9
|
+
@exit_code = exit_code
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Nucleus
|
2
|
+
# ExitCodes describe why the application failed and was stopped.
|
3
|
+
#
|
4
|
+
# Codes beginning with ___ stand for ___:
|
5
|
+
#
|
6
|
+
# 1xx => Startup failed
|
7
|
+
#
|
8
|
+
module ExitCodes
|
9
|
+
##########################
|
10
|
+
### Startup Exceptions ###
|
11
|
+
##########################
|
12
|
+
|
13
|
+
# Unidentified startup error
|
14
|
+
STARTUP_ERROR = 100
|
15
|
+
|
16
|
+
# Custom SSH key was specified in the options but could not be loaded
|
17
|
+
INVALID_SSH_KEY_FILE = 101
|
18
|
+
|
19
|
+
# Invalid key, invalid or not of format ssh-rsa OpenSSH
|
20
|
+
INVALID_SSH_KEY = 102
|
21
|
+
|
22
|
+
# Invalid private key, we can only accept private keys without a passphrase
|
23
|
+
INVALID_SSH_KEY_FILE_PROTECTED = 103
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
module Nucleus
|
2
|
+
class ApplicationRepoSanitizer
|
3
|
+
include Nucleus::Logging
|
4
|
+
|
5
|
+
# Create a new instance of the object.
|
6
|
+
# @param [Boolean] exclude_git if true the '.git' directory won't be moved up, but will be ignored.
|
7
|
+
def initialize(exclude_git = true)
|
8
|
+
@exclude_git = exclude_git
|
9
|
+
end
|
10
|
+
|
11
|
+
# Sanitizing the repository_dir will check if the repository has more than one file / directory besides the git DB.
|
12
|
+
# If there is only one directory, all files in this directory are going to be moved one level up.
|
13
|
+
# If there was:
|
14
|
+
#
|
15
|
+
# .git
|
16
|
+
# wordfinder
|
17
|
+
#
|
18
|
+
# All contents of `wordfinder` will be moved one level up, resulting in:
|
19
|
+
#
|
20
|
+
# config
|
21
|
+
# lib
|
22
|
+
# public
|
23
|
+
# specs
|
24
|
+
# views
|
25
|
+
# README.md
|
26
|
+
# server.js
|
27
|
+
# ...
|
28
|
+
#
|
29
|
+
# @param [String] repository_dir path to the git repository that is going to be sanitized
|
30
|
+
def sanitize(repository_dir)
|
31
|
+
# no sanitizing for files
|
32
|
+
return unless File.directory?(repository_dir)
|
33
|
+
repo_entries = sanitized_dir_entries(repository_dir)
|
34
|
+
return unless repo_entries.length == 1
|
35
|
+
|
36
|
+
log.debug 'Uploaded application is wrapped in folder, fixing now by moving all contents one level up...'
|
37
|
+
dir = File.join(repository_dir, repo_entries[0])
|
38
|
+
dir_entries = sanitized_dir_entries(dir).map { |name| File.join(dir, name) }
|
39
|
+
FileUtils.mv(dir_entries, repository_dir)
|
40
|
+
# Now delete the usually empty directory
|
41
|
+
FileUtils.rm_r dir
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
def sanitized_dir_entries(dir)
|
47
|
+
Dir.entries(dir).reject do |entry|
|
48
|
+
entry == '.DS_Store' || (@exclude_git && entry == '.git') || entry == '.' || entry == '..'
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,112 @@
|
|
1
|
+
module Nucleus
|
2
|
+
class ArchiveExtractor
|
3
|
+
def initialize(exclude_git = true)
|
4
|
+
@exclude_git = exclude_git
|
5
|
+
end
|
6
|
+
|
7
|
+
# Extract the file to the destination path.
|
8
|
+
# The compression format indicates which method must be used to extract the archive.
|
9
|
+
# @param [IO] file in-memory archive file to extract
|
10
|
+
# @param [String] destination_path where the archive is going to be extracted to
|
11
|
+
# @param [String] compression_format represented by well-known file extensions, e.g. zip or tar.gz
|
12
|
+
# @raise [StandardError] if the compression_format is not supported and can't be extracted
|
13
|
+
# @return [Integer] number of extracted files
|
14
|
+
def extract(file, destination_path, compression_format)
|
15
|
+
compression_method = compression_format_method_name(compression_format)
|
16
|
+
fail StandardError, 'Unsupported compression format' unless respond_to?(compression_method, true)
|
17
|
+
|
18
|
+
# be sure that directory exists
|
19
|
+
FileUtils.mkdir_p(destination_path, verbose: false)
|
20
|
+
|
21
|
+
begin
|
22
|
+
send(compression_method, file, destination_path)
|
23
|
+
rescue Zip::Error, Zlib::GzipFile::Error
|
24
|
+
raise API::Errors::ApplicationArchiveError, "Failed to extract #{compression_format} archive"
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
# Checks if the compression format is supported and an archive of this type could be extracted.
|
29
|
+
# @param [String] compression_format represented by well-known file extensions, e.g. zip or tar.gz
|
30
|
+
# @return [Boolean] true if format is supported, false if not
|
31
|
+
def supports?(compression_format)
|
32
|
+
compression_method = compression_format_method_name(compression_format)
|
33
|
+
respond_to?(compression_method, true)
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
def compression_format_method_name(compression_format)
|
39
|
+
"un_#{compression_format.downcase.gsub(/\./, '_').underscore}".to_sym
|
40
|
+
end
|
41
|
+
|
42
|
+
def un_zip(file, destination_path)
|
43
|
+
extracted = 0
|
44
|
+
Zip::File.open(file) do |zip_file|
|
45
|
+
# Handle entries one by one
|
46
|
+
zip_file.each do |entry|
|
47
|
+
next if @exclude_git && entry.name.start_with?('.git')
|
48
|
+
dest = File.join(destination_path, entry.name)
|
49
|
+
if entry.name_is_directory?
|
50
|
+
FileUtils.mkdir_p(dest) unless File.exist?(dest)
|
51
|
+
else
|
52
|
+
# make sure parent directory exists
|
53
|
+
FileUtils.mkdir_p(File.expand_path('..', dest))
|
54
|
+
|
55
|
+
entry.extract(dest) unless File.exist?(dest)
|
56
|
+
# increase count
|
57
|
+
extracted += 1
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
extracted
|
62
|
+
end
|
63
|
+
|
64
|
+
def un_tar_gz(file, destination_path)
|
65
|
+
extracted = 0
|
66
|
+
# unzip the archive into the repo, closes resource automatically
|
67
|
+
# Thanks to Draco Ater: http://stackoverflow.com/a/19139114/1009436
|
68
|
+
Gem::Package::TarReader.new(Zlib::GzipReader.open(file)) do |tar|
|
69
|
+
dest = nil
|
70
|
+
tar.each do |entry|
|
71
|
+
# Process Longlinks and skip to next entry
|
72
|
+
if entry.full_name == '././@LongLink'
|
73
|
+
dest = File.join(destination_path, entry.read.strip)
|
74
|
+
next
|
75
|
+
end
|
76
|
+
|
77
|
+
# Process default entry types (dir, file, symlink)
|
78
|
+
full_name = entry.full_name.sub(%r{(\.\/)?}, '')
|
79
|
+
dest ||= File.join(destination_path, full_name)
|
80
|
+
next if tar_git_entry? full_name
|
81
|
+
if entry.directory?
|
82
|
+
write_tar_dir_entry(entry, dest)
|
83
|
+
elsif entry.file?
|
84
|
+
write_tar_file_entry(entry, dest)
|
85
|
+
# increase count
|
86
|
+
extracted += 1
|
87
|
+
elsif entry.header.typeflag == '2'
|
88
|
+
# handle symlinks
|
89
|
+
File.symlink(entry.header.linkname, dest)
|
90
|
+
end
|
91
|
+
dest = nil
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
alias_method :un_tgz, :un_tar_gz
|
96
|
+
|
97
|
+
def tar_git_entry?(full_name)
|
98
|
+
@exclude_git && (full_name.start_with?('._.git') || full_name.start_with?('.git'))
|
99
|
+
end
|
100
|
+
|
101
|
+
def write_tar_file_entry(entry, dest)
|
102
|
+
FileUtils.rm_rf(dest) if File.directory?(dest)
|
103
|
+
File.open(dest, 'wb') { |f| f.print entry.read }
|
104
|
+
FileUtils.chmod(entry.header.mode, dest, verbose: false)
|
105
|
+
end
|
106
|
+
|
107
|
+
def write_tar_dir_entry(entry, dest)
|
108
|
+
File.delete(dest) if File.file?(dest)
|
109
|
+
FileUtils.mkdir_p(dest, mode: entry.header.mode, verbose: false)
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
@@ -0,0 +1,91 @@
|
|
1
|
+
module Nucleus
|
2
|
+
class Archiver
|
3
|
+
def initialize(exclude_git = true)
|
4
|
+
@exclude_git = exclude_git
|
5
|
+
end
|
6
|
+
|
7
|
+
# Compress the files of the path into an archive, using the compression format,
|
8
|
+
# which indicates which method must be used to compress the archive.
|
9
|
+
# @param [String] path which directory's contents are going to be compressed into the archive
|
10
|
+
# @param [String] compression_format represented by well-known file extensions, e.g. zip or tar.gz
|
11
|
+
# @raise [StandardError] if the compression_format is not supported and the directory can't be compressed
|
12
|
+
# @return [StringIO] compressed data of the given input path
|
13
|
+
def compress(path, compression_format)
|
14
|
+
compression_method = compression_format.downcase.gsub(/\./, '_').underscore.to_sym
|
15
|
+
fail StandardError,
|
16
|
+
"Unsupported compression format #{compression_format}" unless self.respond_to?(compression_method, true)
|
17
|
+
send(compression_method, path)
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def tar(path)
|
23
|
+
string_io = StringIO.new('')
|
24
|
+
Gem::Package::TarWriter.new(string_io) do |tar|
|
25
|
+
Find.find(path) do |file|
|
26
|
+
# do not include the git files
|
27
|
+
next if @exclude_git && file.start_with?("#{path}/.git")
|
28
|
+
|
29
|
+
mode = File.stat(file).mode
|
30
|
+
relative_file = file.sub(%r{^#{Regexp.escape path}\/?}, '')
|
31
|
+
|
32
|
+
if File.directory?(file)
|
33
|
+
tar.mkdir relative_file, mode
|
34
|
+
else
|
35
|
+
tar.add_file relative_file, mode do |tf|
|
36
|
+
File.open(file, 'rb') { |f| tf.write f.read }
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
string_io.rewind
|
42
|
+
string_io
|
43
|
+
end
|
44
|
+
|
45
|
+
def tar_gz(path)
|
46
|
+
tar_file = tar(path)
|
47
|
+
begin
|
48
|
+
gz = StringIO.new('')
|
49
|
+
z = Zlib::GzipWriter.new(gz)
|
50
|
+
z.write tar_file.string
|
51
|
+
ensure
|
52
|
+
z.close unless z.nil?
|
53
|
+
end
|
54
|
+
|
55
|
+
# z was closed to write the gzip footer, so
|
56
|
+
# now we need a new StringIO
|
57
|
+
StringIO.new gz.string
|
58
|
+
end
|
59
|
+
|
60
|
+
def zip(path)
|
61
|
+
string_io = Zip::OutputStream.write_buffer do |zio|
|
62
|
+
Find.find(path) do |file|
|
63
|
+
# do not process directories && do not include the Git DB files
|
64
|
+
next if File.directory?(file) || (@exclude_git && file.start_with?("#{path}/.git"))
|
65
|
+
|
66
|
+
relative_file = file.sub(%r{^#{Regexp.escape path}\/?}, '')
|
67
|
+
zio.put_next_entry(relative_file)
|
68
|
+
File.open(file, 'rb') do |f|
|
69
|
+
zio.write f.read
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
string_io.rewind
|
74
|
+
string_io
|
75
|
+
end
|
76
|
+
|
77
|
+
def write_zip_entries(path, sub_path, io)
|
78
|
+
search_path = File.join(path, sub_path)
|
79
|
+
Find.find(path) do |file|
|
80
|
+
zip_file_path = file.sub(%r{^#{Regexp.escape search_path}\/?}, '')
|
81
|
+
next if @exclude_git && zip_file_path.start_with?('.git')
|
82
|
+
if File.directory?(file)
|
83
|
+
io.mkdir(zip_file_path)
|
84
|
+
write_zip_entries(path, zip_file_path, io)
|
85
|
+
else
|
86
|
+
io.get_output_stream(zip_file_path) { |f| f.print(File.open(file, 'rb').read) }
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
module Nucleus
|
2
|
+
class LinkGenerator
|
3
|
+
def initialize(env, api_version)
|
4
|
+
@env = env
|
5
|
+
@version = api_version
|
6
|
+
end
|
7
|
+
|
8
|
+
# Generate the link that references the resource.
|
9
|
+
# @param [Array<String>] namespaces nested namespaces that must be joined to access the resource
|
10
|
+
# @param [String] id id of the resource
|
11
|
+
# @return [String] URL to the resource
|
12
|
+
def resource(namespaces, id)
|
13
|
+
# resource can only exist for an API version
|
14
|
+
link = api_root
|
15
|
+
# combine namespace and entity ID
|
16
|
+
link << namespace(namespaces)
|
17
|
+
link << "/#{id}" unless id.nil? || id.empty?
|
18
|
+
# return the created link
|
19
|
+
link
|
20
|
+
end
|
21
|
+
|
22
|
+
# Create a link to the API root node.
|
23
|
+
#
|
24
|
+
# @return [String] link to the API root
|
25
|
+
def api_root
|
26
|
+
root_url << '/api'
|
27
|
+
end
|
28
|
+
|
29
|
+
# Get the root URL of the Nucleus API (scheme + host)
|
30
|
+
def root_url
|
31
|
+
"#{@env['rack.url_scheme']}://#{@env['HTTP_HOST']}"
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def namespace(namespaces)
|
37
|
+
if namespaces.is_a?(String) && !namespaces.empty?
|
38
|
+
"/#{namespaces}"
|
39
|
+
elsif !namespaces.nil? && !namespaces.empty?
|
40
|
+
"/#{namespaces.join('/')}"
|
41
|
+
else
|
42
|
+
''
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
module Nucleus
|
2
|
+
# Logging module for Nucleus.
|
3
|
+
# Include via
|
4
|
+
# include Nucleus::Logging
|
5
|
+
# and then log your messages:
|
6
|
+
# log.info('This is a test log message')
|
7
|
+
#
|
8
|
+
# @author Willem Buys
|
9
|
+
# Idea by Willem 'Jacob' Buys, as seen on http://stackoverflow.com/questions/917566/ruby-share-logger-instance-among-module-classes
|
10
|
+
module Logging
|
11
|
+
def log
|
12
|
+
@log ||= Logging.logger_for(self.class.name)
|
13
|
+
end
|
14
|
+
|
15
|
+
# Use a hash class-ivar to cache a unique Logger per class:
|
16
|
+
@loggers = {}
|
17
|
+
|
18
|
+
class << self
|
19
|
+
def logger_for(classname)
|
20
|
+
@loggers[classname] ||= configure_logger_for(classname)
|
21
|
+
end
|
22
|
+
|
23
|
+
def configure_logger_for(classname)
|
24
|
+
# prepare logging dir
|
25
|
+
log_dir = nucleus_config.logging.path
|
26
|
+
log_file = File.join(log_dir, 'nucleus.log')
|
27
|
+
# prepare path and create missing directories
|
28
|
+
FileUtils.mkdir_p(log_dir) unless File.directory?(log_dir)
|
29
|
+
# create the loggers
|
30
|
+
std_log = Logger.new(STDOUT)
|
31
|
+
# use rotation for x days
|
32
|
+
file_log = Logger.new(log_file, 'daily', 7)
|
33
|
+
|
34
|
+
# include custom log format that includes the request id
|
35
|
+
formatter = Nucleus::Logging::Formatter.new
|
36
|
+
|
37
|
+
[file_log, std_log].each do |logger|
|
38
|
+
# apply format
|
39
|
+
logger.formatter = formatter
|
40
|
+
# apply the classname
|
41
|
+
logger.progname = classname
|
42
|
+
end
|
43
|
+
|
44
|
+
# apply the log level from the app. configuration
|
45
|
+
multi_logger = MultiLogger.new(
|
46
|
+
level: nucleus_config.logging.key?(:level) ? nucleus_config.logging.level : Logger::Severity::WARN,
|
47
|
+
loggers: [std_log, file_log])
|
48
|
+
multi_logger
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|