hybrid_platforms_conductor 32.3.6
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/bin/check-node +24 -0
- data/bin/deploy +12 -0
- data/bin/dump_nodes_json +12 -0
- data/bin/free_ips +23 -0
- data/bin/free_veids +17 -0
- data/bin/get_impacted_nodes +43 -0
- data/bin/last_deploys +56 -0
- data/bin/nodes_to_deploy +104 -0
- data/bin/report +10 -0
- data/bin/run +39 -0
- data/bin/setup +11 -0
- data/bin/ssh_config +14 -0
- data/bin/test +13 -0
- data/bin/topograph +54 -0
- data/lib/hybrid_platforms_conductor/action.rb +82 -0
- data/lib/hybrid_platforms_conductor/actions_executor.rb +307 -0
- data/lib/hybrid_platforms_conductor/bitbucket.rb +123 -0
- data/lib/hybrid_platforms_conductor/cmd_runner.rb +188 -0
- data/lib/hybrid_platforms_conductor/cmdb.rb +34 -0
- data/lib/hybrid_platforms_conductor/common_config_dsl/bitbucket.rb +78 -0
- data/lib/hybrid_platforms_conductor/common_config_dsl/confluence.rb +43 -0
- data/lib/hybrid_platforms_conductor/common_config_dsl/file_system_tests.rb +110 -0
- data/lib/hybrid_platforms_conductor/common_config_dsl/idempotence_tests.rb +38 -0
- data/lib/hybrid_platforms_conductor/config.rb +263 -0
- data/lib/hybrid_platforms_conductor/confluence.rb +119 -0
- data/lib/hybrid_platforms_conductor/connector.rb +84 -0
- data/lib/hybrid_platforms_conductor/credentials.rb +127 -0
- data/lib/hybrid_platforms_conductor/current_dir_monitor.rb +42 -0
- data/lib/hybrid_platforms_conductor/deployer.rb +598 -0
- data/lib/hybrid_platforms_conductor/executable.rb +145 -0
- data/lib/hybrid_platforms_conductor/hpc_plugins/action/bash.rb +44 -0
- data/lib/hybrid_platforms_conductor/hpc_plugins/action/interactive.rb +44 -0
- data/lib/hybrid_platforms_conductor/hpc_plugins/action/my_action.rb.sample +79 -0
- data/lib/hybrid_platforms_conductor/hpc_plugins/action/remote_bash.rb +63 -0
- data/lib/hybrid_platforms_conductor/hpc_plugins/action/ruby.rb +69 -0
- data/lib/hybrid_platforms_conductor/hpc_plugins/action/scp.rb +61 -0
- data/lib/hybrid_platforms_conductor/hpc_plugins/cmdb/config.rb +78 -0
- data/lib/hybrid_platforms_conductor/hpc_plugins/cmdb/host_ip.rb +104 -0
- data/lib/hybrid_platforms_conductor/hpc_plugins/cmdb/host_keys.rb +114 -0
- data/lib/hybrid_platforms_conductor/hpc_plugins/cmdb/my_cmdb.rb.sample +129 -0
- data/lib/hybrid_platforms_conductor/hpc_plugins/cmdb/platform_handlers.rb +66 -0
- data/lib/hybrid_platforms_conductor/hpc_plugins/connector/my_connector.rb.sample +156 -0
- data/lib/hybrid_platforms_conductor/hpc_plugins/connector/ssh.rb +702 -0
- data/lib/hybrid_platforms_conductor/hpc_plugins/platform_handler/platform_handler_plugin.rb.sample +292 -0
- data/lib/hybrid_platforms_conductor/hpc_plugins/provisioner/docker.rb +148 -0
- data/lib/hybrid_platforms_conductor/hpc_plugins/provisioner/my_provisioner.rb.sample +103 -0
- data/lib/hybrid_platforms_conductor/hpc_plugins/provisioner/podman.rb +125 -0
- data/lib/hybrid_platforms_conductor/hpc_plugins/provisioner/proxmox.rb +522 -0
- data/lib/hybrid_platforms_conductor/hpc_plugins/provisioner/proxmox/proxmox_waiter.rb +707 -0
- data/lib/hybrid_platforms_conductor/hpc_plugins/provisioner/proxmox/reserve_proxmox_container +122 -0
- data/lib/hybrid_platforms_conductor/hpc_plugins/report/confluence.rb +69 -0
- data/lib/hybrid_platforms_conductor/hpc_plugins/report/mediawiki.rb +164 -0
- data/lib/hybrid_platforms_conductor/hpc_plugins/report/my_report_plugin.rb.sample +88 -0
- data/lib/hybrid_platforms_conductor/hpc_plugins/report/stdout.rb +61 -0
- data/lib/hybrid_platforms_conductor/hpc_plugins/report/templates/confluence_inventory.html.erb +33 -0
- data/lib/hybrid_platforms_conductor/hpc_plugins/test/bitbucket_conf.rb +137 -0
- data/lib/hybrid_platforms_conductor/hpc_plugins/test/can_be_checked.rb +21 -0
- data/lib/hybrid_platforms_conductor/hpc_plugins/test/check_deploy_and_idempotence.rb +112 -0
- data/lib/hybrid_platforms_conductor/hpc_plugins/test/check_from_scratch.rb +35 -0
- data/lib/hybrid_platforms_conductor/hpc_plugins/test/connection.rb +28 -0
- data/lib/hybrid_platforms_conductor/hpc_plugins/test/deploy_freshness.rb +44 -0
- data/lib/hybrid_platforms_conductor/hpc_plugins/test/deploy_from_scratch.rb +36 -0
- data/lib/hybrid_platforms_conductor/hpc_plugins/test/deploy_removes_root_access.rb +49 -0
- data/lib/hybrid_platforms_conductor/hpc_plugins/test/divergence.rb +25 -0
- data/lib/hybrid_platforms_conductor/hpc_plugins/test/executables.rb +46 -0
- data/lib/hybrid_platforms_conductor/hpc_plugins/test/file_system.rb +45 -0
- data/lib/hybrid_platforms_conductor/hpc_plugins/test/file_system_hdfs.rb +45 -0
- data/lib/hybrid_platforms_conductor/hpc_plugins/test/hostname.rb +25 -0
- data/lib/hybrid_platforms_conductor/hpc_plugins/test/idempotence.rb +77 -0
- data/lib/hybrid_platforms_conductor/hpc_plugins/test/ip.rb +38 -0
- data/lib/hybrid_platforms_conductor/hpc_plugins/test/jenkins_ci_conf.rb +56 -0
- data/lib/hybrid_platforms_conductor/hpc_plugins/test/jenkins_ci_masters_ok.rb +54 -0
- data/lib/hybrid_platforms_conductor/hpc_plugins/test/linear_strategy.rb +47 -0
- data/lib/hybrid_platforms_conductor/hpc_plugins/test/local_users.rb +82 -0
- data/lib/hybrid_platforms_conductor/hpc_plugins/test/mounts.rb +120 -0
- data/lib/hybrid_platforms_conductor/hpc_plugins/test/my_test_plugin.rb.sample +143 -0
- data/lib/hybrid_platforms_conductor/hpc_plugins/test/orphan_files.rb +74 -0
- data/lib/hybrid_platforms_conductor/hpc_plugins/test/ports.rb +85 -0
- data/lib/hybrid_platforms_conductor/hpc_plugins/test/private_ips.rb +38 -0
- data/lib/hybrid_platforms_conductor/hpc_plugins/test/public_ips.rb +38 -0
- data/lib/hybrid_platforms_conductor/hpc_plugins/test/spectre-meltdown-checker.sh +1930 -0
- data/lib/hybrid_platforms_conductor/hpc_plugins/test/spectre.rb +56 -0
- data/lib/hybrid_platforms_conductor/hpc_plugins/test/veids.rb +31 -0
- data/lib/hybrid_platforms_conductor/hpc_plugins/test/vulnerabilities.rb +159 -0
- data/lib/hybrid_platforms_conductor/hpc_plugins/test_report/confluence.rb +122 -0
- data/lib/hybrid_platforms_conductor/hpc_plugins/test_report/my_test_report.rb.sample +48 -0
- data/lib/hybrid_platforms_conductor/hpc_plugins/test_report/stdout.rb +120 -0
- data/lib/hybrid_platforms_conductor/hpc_plugins/test_report/templates/_confluence_errors_status.html.erb +46 -0
- data/lib/hybrid_platforms_conductor/hpc_plugins/test_report/templates/_confluence_gauge.html.erb +49 -0
- data/lib/hybrid_platforms_conductor/hpc_plugins/test_report/templates/confluence.html.erb +242 -0
- data/lib/hybrid_platforms_conductor/io_router.rb +70 -0
- data/lib/hybrid_platforms_conductor/json_dumper.rb +88 -0
- data/lib/hybrid_platforms_conductor/logger_helpers.rb +319 -0
- data/lib/hybrid_platforms_conductor/mutex_dir +76 -0
- data/lib/hybrid_platforms_conductor/nodes_handler.rb +597 -0
- data/lib/hybrid_platforms_conductor/parallel_threads.rb +97 -0
- data/lib/hybrid_platforms_conductor/platform_handler.rb +188 -0
- data/lib/hybrid_platforms_conductor/platforms_handler.rb +118 -0
- data/lib/hybrid_platforms_conductor/plugin.rb +53 -0
- data/lib/hybrid_platforms_conductor/plugins.rb +101 -0
- data/lib/hybrid_platforms_conductor/provisioner.rb +181 -0
- data/lib/hybrid_platforms_conductor/report.rb +31 -0
- data/lib/hybrid_platforms_conductor/reports_handler.rb +84 -0
- data/lib/hybrid_platforms_conductor/services_handler.rb +274 -0
- data/lib/hybrid_platforms_conductor/test.rb +141 -0
- data/lib/hybrid_platforms_conductor/test_by_service.rb +22 -0
- data/lib/hybrid_platforms_conductor/test_report.rb +282 -0
- data/lib/hybrid_platforms_conductor/tests_runner.rb +590 -0
- data/lib/hybrid_platforms_conductor/thycotic.rb +92 -0
- data/lib/hybrid_platforms_conductor/topographer.rb +859 -0
- data/lib/hybrid_platforms_conductor/topographer/plugin.rb +20 -0
- data/lib/hybrid_platforms_conductor/topographer/plugins/graphviz.rb +127 -0
- data/lib/hybrid_platforms_conductor/topographer/plugins/json.rb +72 -0
- data/lib/hybrid_platforms_conductor/topographer/plugins/my_topographer_output_plugin.rb.sample +37 -0
- data/lib/hybrid_platforms_conductor/topographer/plugins/svg.rb +30 -0
- data/lib/hybrid_platforms_conductor/version.rb +5 -0
- data/spec/hybrid_platforms_conductor_test.rb +159 -0
- data/spec/hybrid_platforms_conductor_test/api/actions_executor/actions/bash_spec.rb +43 -0
- data/spec/hybrid_platforms_conductor_test/api/actions_executor/actions/interactive_spec.rb +18 -0
- data/spec/hybrid_platforms_conductor_test/api/actions_executor/actions/remote_bash_spec.rb +102 -0
- data/spec/hybrid_platforms_conductor_test/api/actions_executor/actions/ruby_spec.rb +108 -0
- data/spec/hybrid_platforms_conductor_test/api/actions_executor/actions/scp_spec.rb +79 -0
- data/spec/hybrid_platforms_conductor_test/api/actions_executor/actions_spec.rb +199 -0
- data/spec/hybrid_platforms_conductor_test/api/actions_executor/connection_spec.rb +212 -0
- data/spec/hybrid_platforms_conductor_test/api/actions_executor/connectors/ssh/cli_options_spec.rb +125 -0
- data/spec/hybrid_platforms_conductor_test/api/actions_executor/connectors/ssh/config_dsl_spec.rb +50 -0
- data/spec/hybrid_platforms_conductor_test/api/actions_executor/connectors/ssh/connectable_nodes_spec.rb +28 -0
- data/spec/hybrid_platforms_conductor_test/api/actions_executor/connectors/ssh/connections_spec.rb +448 -0
- data/spec/hybrid_platforms_conductor_test/api/actions_executor/connectors/ssh/global_helpers_spec.rb +313 -0
- data/spec/hybrid_platforms_conductor_test/api/actions_executor/connectors/ssh/node_helpers_spec.rb +32 -0
- data/spec/hybrid_platforms_conductor_test/api/actions_executor/connectors/ssh/remote_actions_spec.rb +134 -0
- data/spec/hybrid_platforms_conductor_test/api/actions_executor/logging_spec.rb +256 -0
- data/spec/hybrid_platforms_conductor_test/api/actions_executor/parallel_spec.rb +338 -0
- data/spec/hybrid_platforms_conductor_test/api/actions_executor/timeout_spec.rb +101 -0
- data/spec/hybrid_platforms_conductor_test/api/cmd_runner_spec.rb +165 -0
- data/spec/hybrid_platforms_conductor_test/api/config_spec.rb +238 -0
- data/spec/hybrid_platforms_conductor_test/api/deployer/check_spec.rb +9 -0
- data/spec/hybrid_platforms_conductor_test/api/deployer/deploy_spec.rb +243 -0
- data/spec/hybrid_platforms_conductor_test/api/deployer/parse_deploy_output_spec.rb +104 -0
- data/spec/hybrid_platforms_conductor_test/api/deployer/provisioner_spec.rb +131 -0
- data/spec/hybrid_platforms_conductor_test/api/deployer/provisioners/docker/Dockerfile +10 -0
- data/spec/hybrid_platforms_conductor_test/api/deployer/provisioners/docker_spec.rb +123 -0
- data/spec/hybrid_platforms_conductor_test/api/deployer/provisioners/podman_spec.rb +211 -0
- data/spec/hybrid_platforms_conductor_test/api/deployer/provisioners/proxmox/config_dsl_spec.rb +126 -0
- data/spec/hybrid_platforms_conductor_test/api/deployer/provisioners/proxmox/create_spec.rb +290 -0
- data/spec/hybrid_platforms_conductor_test/api/deployer/provisioners/proxmox/destroy_spec.rb +43 -0
- data/spec/hybrid_platforms_conductor_test/api/deployer/provisioners/proxmox/ip_spec.rb +60 -0
- data/spec/hybrid_platforms_conductor_test/api/deployer/provisioners/proxmox/proxmox.json +3 -0
- data/spec/hybrid_platforms_conductor_test/api/deployer/provisioners/proxmox/reserve_proxmox_container/destroy_vm_spec.rb +82 -0
- data/spec/hybrid_platforms_conductor_test/api/deployer/provisioners/proxmox/reserve_proxmox_container/expired_containers_spec.rb +786 -0
- data/spec/hybrid_platforms_conductor_test/api/deployer/provisioners/proxmox/reserve_proxmox_container/ips_assignment_spec.rb +112 -0
- data/spec/hybrid_platforms_conductor_test/api/deployer/provisioners/proxmox/reserve_proxmox_container/other_lxc_containers_resources_spec.rb +190 -0
- data/spec/hybrid_platforms_conductor_test/api/deployer/provisioners/proxmox/reserve_proxmox_container/pve_node_resources_spec.rb +200 -0
- data/spec/hybrid_platforms_conductor_test/api/deployer/provisioners/proxmox/reserve_proxmox_container/retries_spec.rb +35 -0
- data/spec/hybrid_platforms_conductor_test/api/deployer/provisioners/proxmox/reserve_proxmox_container/vm_ids_assignment_spec.rb +67 -0
- data/spec/hybrid_platforms_conductor_test/api/deployer/provisioners/proxmox/start_spec.rb +79 -0
- data/spec/hybrid_platforms_conductor_test/api/deployer/provisioners/proxmox/state_spec.rb +28 -0
- data/spec/hybrid_platforms_conductor_test/api/deployer/provisioners/proxmox/stop_spec.rb +41 -0
- data/spec/hybrid_platforms_conductor_test/api/nodes_handler/cmdbs/config_spec.rb +33 -0
- data/spec/hybrid_platforms_conductor_test/api/nodes_handler/cmdbs/host_ip_spec.rb +64 -0
- data/spec/hybrid_platforms_conductor_test/api/nodes_handler/cmdbs/host_keys_spec.rb +133 -0
- data/spec/hybrid_platforms_conductor_test/api/nodes_handler/cmdbs/platform_handlers_spec.rb +19 -0
- data/spec/hybrid_platforms_conductor_test/api/nodes_handler/cmdbs_plugins_api_spec.rb +446 -0
- data/spec/hybrid_platforms_conductor_test/api/nodes_handler/common_spec.rb +127 -0
- data/spec/hybrid_platforms_conductor_test/api/nodes_handler/git_diff_impacts_spec.rb +318 -0
- data/spec/hybrid_platforms_conductor_test/api/nodes_handler/nodes_selectors_spec.rb +132 -0
- data/spec/hybrid_platforms_conductor_test/api/nodes_handler/platform_handlers_plugins_api_spec.rb +60 -0
- data/spec/hybrid_platforms_conductor_test/api/nodes_handler/several_platforms_spec.rb +58 -0
- data/spec/hybrid_platforms_conductor_test/api/platform_handler_spec.rb +97 -0
- data/spec/hybrid_platforms_conductor_test/api/platforms_handler_spec.rb +104 -0
- data/spec/hybrid_platforms_conductor_test/api/plugins_spec.rb +243 -0
- data/spec/hybrid_platforms_conductor_test/api/reports_handler_spec.rb +44 -0
- data/spec/hybrid_platforms_conductor_test/api/services_handler/actions_to_deploy_spec.rb +121 -0
- data/spec/hybrid_platforms_conductor_test/api/services_handler/deploy_allowed_spec.rb +142 -0
- data/spec/hybrid_platforms_conductor_test/api/services_handler/log_info_spec.rb +101 -0
- data/spec/hybrid_platforms_conductor_test/api/services_handler/package_spec.rb +388 -0
- data/spec/hybrid_platforms_conductor_test/api/services_handler/parse_deploy_output_spec.rb +274 -0
- data/spec/hybrid_platforms_conductor_test/api/services_handler/prepare_for_deploy_spec.rb +264 -0
- data/spec/hybrid_platforms_conductor_test/api/tests_runner/common_spec.rb +194 -0
- data/spec/hybrid_platforms_conductor_test/api/tests_runner/global_spec.rb +37 -0
- data/spec/hybrid_platforms_conductor_test/api/tests_runner/node_check_spec.rb +194 -0
- data/spec/hybrid_platforms_conductor_test/api/tests_runner/node_spec.rb +137 -0
- data/spec/hybrid_platforms_conductor_test/api/tests_runner/node_ssh_spec.rb +257 -0
- data/spec/hybrid_platforms_conductor_test/api/tests_runner/platform_spec.rb +110 -0
- data/spec/hybrid_platforms_conductor_test/api/tests_runner/reports_spec.rb +367 -0
- data/spec/hybrid_platforms_conductor_test/api/tests_runner/test_plugins/bitbucket_conf_spec.rb +111 -0
- data/spec/hybrid_platforms_conductor_test/api/tests_runner/test_reports_plugins/confluence_spec.rb +29 -0
- data/spec/hybrid_platforms_conductor_test/cmdb_plugins/test_cmdb.rb +166 -0
- data/spec/hybrid_platforms_conductor_test/cmdb_plugins/test_cmdb2.rb +93 -0
- data/spec/hybrid_platforms_conductor_test/cmdb_plugins/test_cmdb_others.rb +60 -0
- data/spec/hybrid_platforms_conductor_test/cmdb_plugins/test_cmdb_others2.rb +58 -0
- data/spec/hybrid_platforms_conductor_test/executables/check-node_spec.rb +35 -0
- data/spec/hybrid_platforms_conductor_test/executables/deploy_spec.rb +35 -0
- data/spec/hybrid_platforms_conductor_test/executables/get_impacted_nodes_spec.rb +158 -0
- data/spec/hybrid_platforms_conductor_test/executables/last_deploys_spec.rb +173 -0
- data/spec/hybrid_platforms_conductor_test/executables/nodes_to_deploy_spec.rb +283 -0
- data/spec/hybrid_platforms_conductor_test/executables/options/actions_executor_spec.rb +28 -0
- data/spec/hybrid_platforms_conductor_test/executables/options/cmd_runner_spec.rb +28 -0
- data/spec/hybrid_platforms_conductor_test/executables/options/common_spec.rb +67 -0
- data/spec/hybrid_platforms_conductor_test/executables/options/deployer_spec.rb +251 -0
- data/spec/hybrid_platforms_conductor_test/executables/options/nodes_handler_spec.rb +111 -0
- data/spec/hybrid_platforms_conductor_test/executables/options/nodes_selectors_spec.rb +71 -0
- data/spec/hybrid_platforms_conductor_test/executables/options/reports_handler_spec.rb +54 -0
- data/spec/hybrid_platforms_conductor_test/executables/options/tests_runner_spec.rb +139 -0
- data/spec/hybrid_platforms_conductor_test/executables/report_spec.rb +60 -0
- data/spec/hybrid_platforms_conductor_test/executables/run_spec.rb +173 -0
- data/spec/hybrid_platforms_conductor_test/executables/ssh_config_spec.rb +35 -0
- data/spec/hybrid_platforms_conductor_test/executables/test_spec.rb +41 -0
- data/spec/hybrid_platforms_conductor_test/helpers/actions_executor_helpers.rb +98 -0
- data/spec/hybrid_platforms_conductor_test/helpers/cmd_runner_helpers.rb +92 -0
- data/spec/hybrid_platforms_conductor_test/helpers/cmdb_helpers.rb +37 -0
- data/spec/hybrid_platforms_conductor_test/helpers/config_helpers.rb +20 -0
- data/spec/hybrid_platforms_conductor_test/helpers/connector_ssh_helpers.rb +130 -0
- data/spec/hybrid_platforms_conductor_test/helpers/deployer_helpers.rb +149 -0
- data/spec/hybrid_platforms_conductor_test/helpers/deployer_test_helpers.rb +812 -0
- data/spec/hybrid_platforms_conductor_test/helpers/executables_helpers.rb +96 -0
- data/spec/hybrid_platforms_conductor_test/helpers/nodes_handler_helpers.rb +20 -0
- data/spec/hybrid_platforms_conductor_test/helpers/platform_handler_helpers.rb +35 -0
- data/spec/hybrid_platforms_conductor_test/helpers/platforms_handler_helpers.rb +127 -0
- data/spec/hybrid_platforms_conductor_test/helpers/plugins_helpers.rb +48 -0
- data/spec/hybrid_platforms_conductor_test/helpers/provisioner_proxmox_helpers.rb +789 -0
- data/spec/hybrid_platforms_conductor_test/helpers/reports_handler_helpers.rb +29 -0
- data/spec/hybrid_platforms_conductor_test/helpers/services_handler_helpers.rb +20 -0
- data/spec/hybrid_platforms_conductor_test/helpers/tests_runner_helpers.rb +38 -0
- data/spec/hybrid_platforms_conductor_test/mocked_lib/my_test_gem/hpc_plugins/test_plugin_type/test_plugin_id1.rb +22 -0
- data/spec/hybrid_platforms_conductor_test/mocked_lib/my_test_gem/hpc_plugins/test_plugin_type/test_plugin_id2.rb +22 -0
- data/spec/hybrid_platforms_conductor_test/mocked_lib/my_test_gem2/sub_dir/hpc_plugins/test_plugin_type/test_plugin_id3.rb +26 -0
- data/spec/hybrid_platforms_conductor_test/mocked_lib/my_test_gem2/sub_dir/hpc_plugins/test_plugin_type2/test_plugin_id4.rb +26 -0
- data/spec/hybrid_platforms_conductor_test/platform_handler_plugins/test.rb +225 -0
- data/spec/hybrid_platforms_conductor_test/platform_handler_plugins/test2.rb +11 -0
- data/spec/hybrid_platforms_conductor_test/report_plugin.rb +35 -0
- data/spec/hybrid_platforms_conductor_test/test_action.rb +66 -0
- data/spec/hybrid_platforms_conductor_test/test_connector.rb +151 -0
- data/spec/hybrid_platforms_conductor_test/test_plugins/global.rb +30 -0
- data/spec/hybrid_platforms_conductor_test/test_plugins/node.rb +53 -0
- data/spec/hybrid_platforms_conductor_test/test_plugins/node_check.rb +47 -0
- data/spec/hybrid_platforms_conductor_test/test_plugins/node_ssh.rb +42 -0
- data/spec/hybrid_platforms_conductor_test/test_plugins/platform.rb +50 -0
- data/spec/hybrid_platforms_conductor_test/test_plugins/several_checks.rb +50 -0
- data/spec/hybrid_platforms_conductor_test/test_provisioner.rb +95 -0
- data/spec/hybrid_platforms_conductor_test/tests_report_plugin.rb +49 -0
- data/spec/spec_helper.rb +111 -0
- metadata +566 -0
@@ -0,0 +1,127 @@
|
|
1
|
+
require 'netrc'
|
2
|
+
require 'uri'
|
3
|
+
require 'hybrid_platforms_conductor/logger_helpers'
|
4
|
+
|
5
|
+
module HybridPlatformsConductor
|
6
|
+
|
7
|
+
# Give a secured and harmonized way to access credentials for a given service.
|
8
|
+
# It makes sure to remove passwords from memory for hardened security (this way if a vulnerability allows an attacker to dump the memory it won't get passwords).
|
9
|
+
# It gets credentials from the following sources:
|
10
|
+
# * Environment variables
|
11
|
+
# * Netrc file
|
12
|
+
class Credentials
|
13
|
+
|
14
|
+
include LoggerHelpers
|
15
|
+
|
16
|
+
# Get access to credentials and make sure they are wiped out from memory when client code ends.
|
17
|
+
# To ensure password safety, never store the password in a scope beyond the client code's Proc.
|
18
|
+
#
|
19
|
+
# Parameters::
|
20
|
+
# * *id* (Symbol): Credential ID
|
21
|
+
# * *logger* (Logger): Logger to be used
|
22
|
+
# * *logger_stderr* (Logger): Logger to be used for stderr
|
23
|
+
# * *url* (String or nil): The URL for which we want the credentials, or nil if not associated to a URL [default: nil]
|
24
|
+
# * Proc: Client code called with credentials provided
|
25
|
+
# * Parameters::
|
26
|
+
# * *user* (String or nil): User name, or nil if none
|
27
|
+
# * *password* (String or nil): Password, or nil if none.
|
28
|
+
# !!! Never store this password in a scope broader than the client code itself !!!
|
29
|
+
def self.with_credentials_for(id, logger, logger_stderr, url: nil)
|
30
|
+
credentials = Credentials.new(id, url: url, logger: logger, logger_stderr: logger_stderr)
|
31
|
+
begin
|
32
|
+
yield credentials.user, credentials.password
|
33
|
+
ensure
|
34
|
+
credentials.clear_password
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
# Constructor
|
39
|
+
#
|
40
|
+
# Parameters::
|
41
|
+
# * *id* (Symbol): Credential ID
|
42
|
+
# * *url* (String or nil): The URL for which we want the credentials, or nil if not associated to a URL [default: nil]
|
43
|
+
# * *logger* (Logger): Logger to be used [default = Logger.new(STDOUT)]
|
44
|
+
# * *logger_stderr* (Logger): Logger to be used for stderr [default = Logger.new(STDERR)]
|
45
|
+
def initialize(id, url: nil, logger: Logger.new(STDOUT), logger_stderr: Logger.new(STDERR))
|
46
|
+
init_loggers(logger, logger_stderr)
|
47
|
+
@id = id
|
48
|
+
@url = url
|
49
|
+
@user = nil
|
50
|
+
@password = nil
|
51
|
+
@retrieved = false
|
52
|
+
end
|
53
|
+
|
54
|
+
# Provide a helper to clear password from memory for security.
|
55
|
+
# To be used when the client knows it won't use the password anymore.
|
56
|
+
def clear_password
|
57
|
+
@password.replace('gotyou!' * 100) unless @password.nil?
|
58
|
+
GC.start
|
59
|
+
end
|
60
|
+
|
61
|
+
# Get the associated user
|
62
|
+
#
|
63
|
+
# Result::
|
64
|
+
# * String or nil: The user name, or nil if none
|
65
|
+
def user
|
66
|
+
retrieve_credentials
|
67
|
+
@user
|
68
|
+
end
|
69
|
+
|
70
|
+
# Get the associated password
|
71
|
+
#
|
72
|
+
# Result::
|
73
|
+
# * String or nil: The password, or nil if none
|
74
|
+
def password
|
75
|
+
retrieve_credentials
|
76
|
+
@password
|
77
|
+
end
|
78
|
+
|
79
|
+
private
|
80
|
+
|
81
|
+
# Retrieve credentials in @user and @password.
|
82
|
+
# Do it only once.
|
83
|
+
# Make sure the retrieved credentials are not linked to other objects in memory, so that we can remove any other trace of secrets.
|
84
|
+
def retrieve_credentials
|
85
|
+
unless @retrieved
|
86
|
+
# Check environment variables
|
87
|
+
@user = ENV["hpc_user_for_#{@id}"].dup
|
88
|
+
@password = ENV["hpc_password_for_#{@id}"].dup
|
89
|
+
if @user.nil? || @user.empty? || @password.nil? || @password.empty?
|
90
|
+
log_debug "[ Credentials for #{@id} ] - Credentials not found from environment variables."
|
91
|
+
if @url.nil?
|
92
|
+
log_debug "[ Credentials for #{@id} ] - No URL associated to this credentials, so .netrc can't be used."
|
93
|
+
else
|
94
|
+
# Check Netrc
|
95
|
+
netrc = ::Netrc.read
|
96
|
+
begin
|
97
|
+
netrc_user, netrc_password = netrc[URI.parse(@url).host.downcase]
|
98
|
+
if netrc_user.nil?
|
99
|
+
log_debug "[ Credentials for #{@id} ] - No credentials retrieved from .netrc."
|
100
|
+
# TODO: Add more credentials source if needed here
|
101
|
+
log_warn "[ Credentials for #{@id} ] - Unable to get credentials for #{@id} (URL: #{@url})."
|
102
|
+
else
|
103
|
+
@user = netrc_user.dup
|
104
|
+
@password = netrc_password.dup
|
105
|
+
log_debug "[ Credentials for #{@id} ] - Credentials retrieved from .netrc using #{@url}."
|
106
|
+
end
|
107
|
+
ensure
|
108
|
+
# Make sure the password does not stay in Netrc memory
|
109
|
+
# Wipe out any memory trace that might contain passwords in clear
|
110
|
+
netrc.instance_variable_get(:@data).each do |data_line|
|
111
|
+
data_line.each do |data_string|
|
112
|
+
data_string.replace('GotYou!!!' * 100)
|
113
|
+
end
|
114
|
+
end
|
115
|
+
netrc = nil
|
116
|
+
end
|
117
|
+
end
|
118
|
+
else
|
119
|
+
log_debug "[ Credentials for #{@id} ] - Credentials retrieved from environment variables."
|
120
|
+
end
|
121
|
+
GC.start
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
end
|
126
|
+
|
127
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'monitor'
|
2
|
+
|
3
|
+
module HybridPlatformsConductor
|
4
|
+
|
5
|
+
# Implement a global monitor to protect accesses to the current directory.
|
6
|
+
# This is needed as the OS concept of current directory is not thread-safe: it is linked to a process, and we need to have thread-safe code running (mainly for parallel deployment and tests).
|
7
|
+
module CurrentDirMonitor
|
8
|
+
|
9
|
+
class << self
|
10
|
+
attr_reader :monitor
|
11
|
+
end
|
12
|
+
|
13
|
+
@monitor = Monitor.new
|
14
|
+
|
15
|
+
# Decorate a given method with the monitor.
|
16
|
+
#
|
17
|
+
# Parameters::
|
18
|
+
# * *module_to_decorate* (Module): The module including the method to decorate
|
19
|
+
# * *method_name* (Symbol): The method name to be decorated
|
20
|
+
def self.decorate_method(module_to_decorate, method_name)
|
21
|
+
original_method_name = "__hpc__#{method_name}__undecorated__".to_sym
|
22
|
+
module_to_decorate.alias_method original_method_name, method_name
|
23
|
+
module_to_decorate.define_method(method_name) do |*args, &block|
|
24
|
+
result = nil
|
25
|
+
CurrentDirMonitor.monitor.synchronize do
|
26
|
+
# puts "TID #{Thread.current.object_id} from #{caller[2]} - Current dir monitor taken from #{Dir.pwd}"
|
27
|
+
result = self.send(original_method_name, *args, &block)
|
28
|
+
# puts "TID #{Thread.current.object_id} from #{caller[2]} - Current dir monitor released back to #{Dir.pwd}"
|
29
|
+
end
|
30
|
+
result
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
|
36
|
+
# List here all methods that need to be protected by this monitor.
|
37
|
+
CurrentDirMonitor.decorate_method(Dir.singleton_class, :chdir)
|
38
|
+
CurrentDirMonitor.decorate_method(File.singleton_class, :expand_path)
|
39
|
+
CurrentDirMonitor.decorate_method(IO.singleton_class, :popen)
|
40
|
+
CurrentDirMonitor.decorate_method(Git::Lib, :command)
|
41
|
+
|
42
|
+
end
|
@@ -0,0 +1,598 @@
|
|
1
|
+
require 'tmpdir'
|
2
|
+
require 'futex'
|
3
|
+
require 'json'
|
4
|
+
require 'securerandom'
|
5
|
+
require 'time'
|
6
|
+
require 'thread'
|
7
|
+
require 'hybrid_platforms_conductor/actions_executor'
|
8
|
+
require 'hybrid_platforms_conductor/cmd_runner'
|
9
|
+
require 'hybrid_platforms_conductor/executable'
|
10
|
+
require 'hybrid_platforms_conductor/logger_helpers'
|
11
|
+
require 'hybrid_platforms_conductor/nodes_handler'
|
12
|
+
require 'hybrid_platforms_conductor/services_handler'
|
13
|
+
require 'hybrid_platforms_conductor/plugins'
|
14
|
+
require 'hybrid_platforms_conductor/thycotic'
|
15
|
+
|
16
|
+
module HybridPlatformsConductor
|
17
|
+
|
18
|
+
# Gives ways to deploy on several nodes
|
19
|
+
class Deployer
|
20
|
+
|
21
|
+
include LoggerHelpers
|
22
|
+
|
23
|
+
# Do we use why-run mode while deploying? [default = false]
|
24
|
+
# Boolean
|
25
|
+
attr_accessor :use_why_run
|
26
|
+
|
27
|
+
# Timeout (in seconds) to be used for each deployment, or nil for no timeout [default = nil]
|
28
|
+
# Integer or nil
|
29
|
+
attr_accessor :timeout
|
30
|
+
|
31
|
+
# Concurrent execution of the deployment? [default = false]
|
32
|
+
# Boolean
|
33
|
+
attr_accessor :concurrent_execution
|
34
|
+
|
35
|
+
# The list of JSON secrets
|
36
|
+
# Array<Hash>
|
37
|
+
attr_accessor :secrets
|
38
|
+
|
39
|
+
# Are we deploying in a local environment?
|
40
|
+
# Boolean
|
41
|
+
attr_accessor :local_environment
|
42
|
+
|
43
|
+
# Number of retries to do in case of non-deterministic errors during deployment
|
44
|
+
# Integer
|
45
|
+
attr_accessor :nbr_retries_on_error
|
46
|
+
|
47
|
+
# Constructor
|
48
|
+
#
|
49
|
+
# Parameters::
|
50
|
+
# * *logger* (Logger): Logger to be used [default: Logger.new(STDOUT)]
|
51
|
+
# * *logger_stderr* (Logger): Logger to be used for stderr [default: Logger.new(STDERR)]
|
52
|
+
# * *config* (Config): Config to be used. [default: Config.new]
|
53
|
+
# * *cmd_runner* (CmdRunner): Command executor to be used. [default: CmdRunner.new]
|
54
|
+
# * *nodes_handler* (NodesHandler): Nodes handler to be used. [default: NodesHandler.new]
|
55
|
+
# * *actions_executor* (ActionsExecutor): Actions Executor to be used. [default: ActionsExecutor.new]
|
56
|
+
# * *services_handler* (ServicesHandler): Services Handler to be used. [default: ServicesHandler.new]
|
57
|
+
def initialize(
|
58
|
+
logger: Logger.new(STDOUT),
|
59
|
+
logger_stderr: Logger.new(STDERR),
|
60
|
+
config: Config.new,
|
61
|
+
cmd_runner: CmdRunner.new,
|
62
|
+
nodes_handler: NodesHandler.new,
|
63
|
+
actions_executor: ActionsExecutor.new,
|
64
|
+
services_handler: ServicesHandler.new
|
65
|
+
)
|
66
|
+
init_loggers(logger, logger_stderr)
|
67
|
+
@config = config
|
68
|
+
@cmd_runner = cmd_runner
|
69
|
+
@nodes_handler = nodes_handler
|
70
|
+
@actions_executor = actions_executor
|
71
|
+
@services_handler = services_handler
|
72
|
+
@secrets = []
|
73
|
+
@provisioners = Plugins.new(:provisioner, logger: @logger, logger_stderr: @logger_stderr)
|
74
|
+
# Default values
|
75
|
+
@use_why_run = false
|
76
|
+
@timeout = nil
|
77
|
+
@concurrent_execution = false
|
78
|
+
@local_environment = false
|
79
|
+
@nbr_retries_on_error = 0
|
80
|
+
end
|
81
|
+
|
82
|
+
# Complete an option parser with options meant to control this Deployer
|
83
|
+
#
|
84
|
+
# Parameters::
|
85
|
+
# * *options_parser* (OptionParser): The option parser to complete
|
86
|
+
# * *parallel_switch* (Boolean): Do we allow parallel execution to be switched? [default = true]
|
87
|
+
# * *why_run_switch* (Boolean): Do we allow the why-run mode to be switched? [default = false]
|
88
|
+
# * *timeout_options* (Boolean): Do we allow timeout options? [default = true]
|
89
|
+
def options_parse(options_parser, parallel_switch: true, why_run_switch: false, timeout_options: true)
|
90
|
+
options_parser.separator ''
|
91
|
+
options_parser.separator 'Deployer options:'
|
92
|
+
options_parser.on(
|
93
|
+
'-e', '--secrets SECRETS_LOCATION',
|
94
|
+
'Specify a secrets location. Can be specified several times. Location can be:',
|
95
|
+
'* Local path to a JSON file',
|
96
|
+
'* URL of the form http[s]://<url>:<secret_id> to get a secret JSON file from a Thycotic Secret Server at the given URL.'
|
97
|
+
) do |secrets_location|
|
98
|
+
@secrets << JSON.parse(
|
99
|
+
if secrets_location =~ /^(https?:\/\/.+):(\d+)$/
|
100
|
+
url = $1
|
101
|
+
secret_id = $2
|
102
|
+
secret = nil
|
103
|
+
Thycotic.with_thycotic(url, @logger, @logger_stderr) do |thycotic|
|
104
|
+
secret_file_item_id = thycotic.get_secret(secret_id).dig(:secret, :items, :secret_item, :id)
|
105
|
+
raise "Unable to fetch secret file ID #{secrets_location}" if secret_file_item_id.nil?
|
106
|
+
secret = thycotic.download_file_attachment_by_item_id(secret_id, secret_file_item_id)
|
107
|
+
raise "Unable to fetch secret file attachment from #{secrets_location}" if secret.nil?
|
108
|
+
end
|
109
|
+
secret
|
110
|
+
else
|
111
|
+
raise "Missing secret file: #{secrets_location}" unless File.exist?(secrets_location)
|
112
|
+
File.read(secrets_location)
|
113
|
+
end
|
114
|
+
)
|
115
|
+
end
|
116
|
+
options_parser.on('-p', '--parallel', 'Execute the commands in parallel (put the standard output in files <hybrid-platforms-dir>/run_logs/*.stdout)') do
|
117
|
+
@concurrent_execution = true
|
118
|
+
end if parallel_switch
|
119
|
+
options_parser.on('-t', '--timeout SECS', "Timeout in seconds to wait for each chef run. Only used in why-run mode. (defaults to #{@timeout.nil? ? 'no timeout' : @timeout})") do |nbr_secs|
|
120
|
+
@timeout = nbr_secs.to_i
|
121
|
+
end if timeout_options
|
122
|
+
options_parser.on('-W', '--why-run', 'Use the why-run mode to see what would be the result of the deploy instead of deploying it for real.') do
|
123
|
+
@use_why_run = true
|
124
|
+
end if why_run_switch
|
125
|
+
options_parser.on('--retries-on-error NBR', "Number of retries in case of non-deterministic errors (defaults to #{@nbr_retries_on_error})") do |nbr_retries|
|
126
|
+
@nbr_retries_on_error = nbr_retries.to_i
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
# Validate that parsed parameters are valid
|
131
|
+
def validate_params
|
132
|
+
raise 'Can\'t have a timeout unless why-run mode. Please don\'t use --timeout without --why-run.' if !@timeout.nil? && !@use_why_run
|
133
|
+
end
|
134
|
+
|
135
|
+
# String: File used as a Futex for packaging
|
136
|
+
PACKAGING_FUTEX_FILE = "#{Dir.tmpdir}/hpc_packaging"
|
137
|
+
|
138
|
+
# Integer: Timeout in seconds to get the packaging Futex
|
139
|
+
PACKAGING_FUTEX_TIMEOUT = 60
|
140
|
+
|
141
|
+
# Deploy on a given list of nodes selectors.
|
142
|
+
# The workflow is the following:
|
143
|
+
# 1. Package the services to be deployed, considering the nodes, services and context (options, secrets, environment...)
|
144
|
+
# 2. Deploy on the nodes (once per node to be deployed)
|
145
|
+
# 3. Save deployment logs (in case of real deployment)
|
146
|
+
#
|
147
|
+
# Parameters::
|
148
|
+
# * *nodes_selectors* (Array<Object>): The list of nodes selectors we will deploy to.
|
149
|
+
# Result::
|
150
|
+
# * Hash<String, [Integer or Symbol, String, String]>: Exit status code (or Symbol in case of error or dry run), standard output and error for each node that has been deployed.
|
151
|
+
def deploy_on(*nodes_selectors)
|
152
|
+
# Get the sorted list of services to be deployed, per node
|
153
|
+
# Hash<String, Array<String> >
|
154
|
+
services_to_deploy = Hash[@nodes_handler.select_nodes(nodes_selectors.flatten).map do |node|
|
155
|
+
[node, @nodes_handler.get_services_of(node)]
|
156
|
+
end]
|
157
|
+
|
158
|
+
# Get the secrets to be deployed
|
159
|
+
secrets = {}
|
160
|
+
@secrets.each do |secret_json|
|
161
|
+
secrets.merge!(secret_json) do |key, value1, value2|
|
162
|
+
raise "Secret #{key} has conflicting values between different secret JSON files." if value1 != value2
|
163
|
+
value1
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
# Check that we are allowed to deploy
|
168
|
+
unless @use_why_run
|
169
|
+
reason_for_interdiction = @services_handler.deploy_allowed?(
|
170
|
+
services: services_to_deploy,
|
171
|
+
secrets: secrets,
|
172
|
+
local_environment: @local_environment
|
173
|
+
)
|
174
|
+
raise "Deployment not allowed: #{reason_for_interdiction}" unless reason_for_interdiction.nil?
|
175
|
+
end
|
176
|
+
|
177
|
+
# Package the deployment
|
178
|
+
# Protect packaging by a Futex
|
179
|
+
Futex.new(PACKAGING_FUTEX_FILE, timeout: PACKAGING_FUTEX_TIMEOUT).open do
|
180
|
+
section 'Packaging deployment' do
|
181
|
+
@services_handler.package(
|
182
|
+
services: services_to_deploy,
|
183
|
+
secrets: secrets,
|
184
|
+
local_environment: @local_environment
|
185
|
+
)
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
# Prepare the deployment as a whole, before getting individual deployment actions.
|
190
|
+
# Do this after packaging, this way we ensure that services packaging cannot depend on the way deployment will be performed.
|
191
|
+
@services_handler.prepare_for_deploy(
|
192
|
+
services: services_to_deploy,
|
193
|
+
secrets: secrets,
|
194
|
+
local_environment: @local_environment,
|
195
|
+
why_run: @use_why_run
|
196
|
+
)
|
197
|
+
|
198
|
+
# Launch deployment processes
|
199
|
+
results = {}
|
200
|
+
|
201
|
+
section "#{@use_why_run ? 'Checking' : 'Deploying'} on #{services_to_deploy.keys.size} nodes" do
|
202
|
+
# Prepare all the control masters here, as they will be reused for the whole process, including mutexes, deployment and logs saving
|
203
|
+
@actions_executor.with_connections_prepared_to(services_to_deploy.keys, no_exception: true) do
|
204
|
+
|
205
|
+
nbr_retries = @nbr_retries_on_error
|
206
|
+
remaining_nodes_to_deploy = services_to_deploy.keys
|
207
|
+
while nbr_retries >= 0 && !remaining_nodes_to_deploy.empty?
|
208
|
+
last_deploy_results = deploy(services_to_deploy.slice(*remaining_nodes_to_deploy))
|
209
|
+
if nbr_retries > 0
|
210
|
+
# Check if we need to retry deployment on some nodes
|
211
|
+
# Only parse the last deployment attempt logs
|
212
|
+
retriable_nodes = Hash[
|
213
|
+
remaining_nodes_to_deploy.
|
214
|
+
map do |node|
|
215
|
+
exit_status, stdout, stderr = last_deploy_results[node]
|
216
|
+
if exit_status == 0
|
217
|
+
nil
|
218
|
+
else
|
219
|
+
retriable_errors = retriable_errors_from(node, exit_status, stdout, stderr)
|
220
|
+
if retriable_errors.empty?
|
221
|
+
nil
|
222
|
+
else
|
223
|
+
# Log the issue in the stderr of the deployment
|
224
|
+
stderr << "!!! #{retriable_errors.size} retriable errors detected in this deployment:\n#{retriable_errors.map { |error| "* #{error}" }.join("\n")}\n"
|
225
|
+
[node, retriable_errors]
|
226
|
+
end
|
227
|
+
end
|
228
|
+
end.
|
229
|
+
compact
|
230
|
+
]
|
231
|
+
unless retriable_nodes.empty?
|
232
|
+
log_warn <<~EOS.strip
|
233
|
+
Retry deployment for #{retriable_nodes.size} nodes as they got non-deterministic errors (#{nbr_retries} retries remaining):
|
234
|
+
#{retriable_nodes.map { |node, retriable_errors| " * #{node}:\n#{retriable_errors.map { |error| " - #{error}" }.join("\n")}" }.join("\n")}
|
235
|
+
EOS
|
236
|
+
end
|
237
|
+
remaining_nodes_to_deploy = retriable_nodes.keys
|
238
|
+
end
|
239
|
+
# Merge deployment results
|
240
|
+
results.merge!(last_deploy_results) do |node, (exit_status_1, stdout_1, stderr_1), (exit_status_2, stdout_2, stderr_2)|
|
241
|
+
[
|
242
|
+
exit_status_2,
|
243
|
+
<<~EOS,
|
244
|
+
#{stdout_1}
|
245
|
+
Deployment exit status code: #{exit_status_1}
|
246
|
+
!!! Retry deployment due to non-deterministic error (#{nbr_retries} remaining attempts)...
|
247
|
+
#{stdout_2}
|
248
|
+
EOS
|
249
|
+
<<~EOS
|
250
|
+
#{stderr_1}
|
251
|
+
!!! Retry deployment due to non-deterministic error (#{nbr_retries} remaining attempts)...
|
252
|
+
#{stderr_2}
|
253
|
+
EOS
|
254
|
+
]
|
255
|
+
end
|
256
|
+
nbr_retries -= 1
|
257
|
+
end
|
258
|
+
|
259
|
+
end
|
260
|
+
end
|
261
|
+
results
|
262
|
+
end
|
263
|
+
|
264
|
+
# Provision a test instance for a given node.
|
265
|
+
#
|
266
|
+
# Parameters::
|
267
|
+
# * *provisioner_id* (Symbol): The provisioner ID to be used
|
268
|
+
# * *node* (String): The node for which we want the image
|
269
|
+
# * *environment* (String): An ID to differentiate different running instances for the same node
|
270
|
+
# * *reuse_instance* (Boolean): Do we reuse an eventual existing instance? [default: false]
|
271
|
+
# * Proc: Code called when the container is ready. The container will be stopped at the end of execution.
|
272
|
+
# * Parameters::
|
273
|
+
# * *deployer* (Deployer): A new Deployer configured to override access to the node through the Docker container
|
274
|
+
# * *instance* (Provisioner): The provisioned instance
|
275
|
+
def with_test_provisioned_instance(provisioner_id, node, environment:, reuse_instance: false)
|
276
|
+
# Add the user to the environment to better track belongings on shared provisioners
|
277
|
+
environment = "#{@cmd_runner.whoami}_#{environment}"
|
278
|
+
# Add PID, TID and a random number to the ID to make sure other containers used by other runs are not being reused.
|
279
|
+
environment << "_#{Process.pid}_#{Thread.current.object_id}_#{SecureRandom.hex(8)}" unless reuse_instance
|
280
|
+
# Create different NodesHandler and Deployer to handle this Docker container in place of the real node.
|
281
|
+
sub_logger, sub_logger_stderr =
|
282
|
+
if log_debug?
|
283
|
+
[@logger, @logger_stderr]
|
284
|
+
else
|
285
|
+
[Logger.new(StringIO.new, level: :info), Logger.new(StringIO.new, level: :info)]
|
286
|
+
end
|
287
|
+
begin
|
288
|
+
sub_executable = Executable.new(logger: sub_logger, logger_stderr: sub_logger_stderr)
|
289
|
+
instance = @provisioners[provisioner_id].new(
|
290
|
+
node,
|
291
|
+
environment: environment,
|
292
|
+
logger: @logger,
|
293
|
+
logger_stderr: @logger_stderr,
|
294
|
+
config: @config,
|
295
|
+
cmd_runner: @cmd_runner,
|
296
|
+
# Here we use the NodesHandler that will be bound to the sub-Deployer only, as the node's metadata might be modified by the Provisioner.
|
297
|
+
nodes_handler: sub_executable.nodes_handler,
|
298
|
+
actions_executor: @actions_executor
|
299
|
+
)
|
300
|
+
instance.with_running_instance(stop_on_exit: true, destroy_on_exit: !reuse_instance, port: 22) do
|
301
|
+
actions_executor = sub_executable.actions_executor
|
302
|
+
deployer = sub_executable.deployer
|
303
|
+
# Setup test environment for this container
|
304
|
+
actions_executor.connector(:ssh).ssh_user = 'root'
|
305
|
+
actions_executor.connector(:ssh).passwords[node] = 'root_pwd'
|
306
|
+
deployer.local_environment = true
|
307
|
+
# Ignore secrets that might have been given: in Docker containers we always use dummy secrets
|
308
|
+
deployer.secrets = [JSON.parse(File.read("#{@config.hybrid_platforms_dir}/dummy_secrets.json"))]
|
309
|
+
yield deployer, instance
|
310
|
+
end
|
311
|
+
rescue
|
312
|
+
# Make sure Docker logs are being output to better investigate errors if we were not already outputing them in debug mode
|
313
|
+
stdouts = sub_executable.stdouts_to_s
|
314
|
+
log_error "[ #{node}/#{environment} ] - Encountered unhandled exception #{$!}\n#{$!.backtrace.join("\n")}\n-----\n#{stdouts}" unless stdouts.nil?
|
315
|
+
raise
|
316
|
+
end
|
317
|
+
end
|
318
|
+
|
319
|
+
# Get deployment info from a list of nodes selectors
|
320
|
+
#
|
321
|
+
# Parameters::
|
322
|
+
# * *nodes* (Array<String>): Nodes to get info from
|
323
|
+
# Result::
|
324
|
+
# * Hash<String, Hash<Symbol,Object>: The deployed info, per node name.
|
325
|
+
# Properties are defined by the Deployer#save_logs method, and additionally to them the following properties can be set:
|
326
|
+
# * *error* (String): Optional property set in case of error
|
327
|
+
def deployment_info_from(*nodes)
|
328
|
+
@actions_executor.max_threads = 64
|
329
|
+
Hash[@actions_executor.
|
330
|
+
execute_actions(
|
331
|
+
Hash[nodes.flatten.map do |node|
|
332
|
+
[
|
333
|
+
node,
|
334
|
+
{ remote_bash: "cd /var/log/deployments && ls -t | head -1 | xargs sed '/===== STDOUT =====/q'" }
|
335
|
+
]
|
336
|
+
end],
|
337
|
+
log_to_stdout: false,
|
338
|
+
concurrent: true,
|
339
|
+
timeout: 10,
|
340
|
+
progress_name: 'Getting deployment info'
|
341
|
+
).
|
342
|
+
map do |node, (exit_status, stdout, stderr)|
|
343
|
+
# Expected format for stdout:
|
344
|
+
# Property1: Value1
|
345
|
+
# ...
|
346
|
+
# PropertyN: ValueN
|
347
|
+
# ===== STDOUT =====
|
348
|
+
# ...
|
349
|
+
deploy_info = {}
|
350
|
+
if exit_status.is_a?(Symbol)
|
351
|
+
deploy_info[:error] = "Error: #{exit_status}\n#{stderr}"
|
352
|
+
else
|
353
|
+
stdout_lines = stdout.split("\n")
|
354
|
+
if stdout_lines.first =~ /No such file or directory/
|
355
|
+
deploy_info[:error] = '/var/log/deployments missing'
|
356
|
+
else
|
357
|
+
stdout_lines.each do |line|
|
358
|
+
if line =~ /^([^:]+): (.+)$/
|
359
|
+
key_str, value = $1, $2
|
360
|
+
key = key_str.to_sym
|
361
|
+
# Type-cast some values
|
362
|
+
case key_str
|
363
|
+
when 'date'
|
364
|
+
# Date and time values
|
365
|
+
# Thu Nov 23 18:43:01 UTC 2017
|
366
|
+
deploy_info[key] = Time.parse(value)
|
367
|
+
when 'debug'
|
368
|
+
# Boolean values
|
369
|
+
# Yes
|
370
|
+
deploy_info[key] = (value == 'Yes')
|
371
|
+
when /^diff_files_.+$/, 'services'
|
372
|
+
# Array of strings
|
373
|
+
# my_file.txt, other_file.txt
|
374
|
+
deploy_info[key] = value.split(', ')
|
375
|
+
else
|
376
|
+
deploy_info[key] = value
|
377
|
+
end
|
378
|
+
else
|
379
|
+
deploy_info[:unknown_lines] = [] unless deploy_info.key?(:unknown_lines)
|
380
|
+
deploy_info[:unknown_lines] << line
|
381
|
+
end
|
382
|
+
end
|
383
|
+
end
|
384
|
+
end
|
385
|
+
[
|
386
|
+
node,
|
387
|
+
deploy_info
|
388
|
+
]
|
389
|
+
end
|
390
|
+
]
|
391
|
+
end
|
392
|
+
|
393
|
+
# Parse stdout and stderr of a given deploy run and get the list of tasks with their status
|
394
|
+
#
|
395
|
+
# Parameters::
|
396
|
+
# * *node* (String): Node for which this deploy run has been done.
|
397
|
+
# * *stdout* (String): stdout to be parsed.
|
398
|
+
# * *stderr* (String): stderr to be parsed.
|
399
|
+
# Result::
|
400
|
+
# * Array< Hash<Symbol,Object> >: List of task properties. The following properties should be returned, among free ones:
|
401
|
+
# * *name* (String): Task name
|
402
|
+
# * *status* (Symbol): Task status. Should be on of:
|
403
|
+
# * *:changed*: The task has been changed
|
404
|
+
# * *:identical*: The task has not been changed
|
405
|
+
# * *diffs* (String): Differences, if any
|
406
|
+
def parse_deploy_output(node, stdout, stderr)
|
407
|
+
@services_handler.parse_deploy_output(stdout, stderr).map { |deploy_info| deploy_info[:tasks] }.flatten
|
408
|
+
end
|
409
|
+
|
410
|
+
private
|
411
|
+
|
412
|
+
# Get the list of retriable errors a node got from deployment logs.
|
413
|
+
# Useful to know if an error is non-deterministic (due to external and temporary factors).
|
414
|
+
#
|
415
|
+
# Parameters::
|
416
|
+
# * *node* (String): Node having the error
|
417
|
+
# * *exit_status* (Integer or Symbol): The deployment exit status
|
418
|
+
# * *stdout* (String): Deployment stdout
|
419
|
+
# * *stderr* (String): Deployment stderr
|
420
|
+
# Result::
|
421
|
+
# * Array<String>: List of retriable errors that have been matched
|
422
|
+
def retriable_errors_from(node, exit_status, stdout, stderr)
|
423
|
+
# List of retriable errors for this node, as exact string match or regexps.
|
424
|
+
# Array<String or Regexp>
|
425
|
+
retriable_errors_on_stdout = []
|
426
|
+
retriable_errors_on_stderr = []
|
427
|
+
@nodes_handler.select_confs_for_node(node, @config.retriable_errors).each do |retriable_error_info|
|
428
|
+
retriable_errors_on_stdout.concat(retriable_error_info[:errors_on_stdout]) if retriable_error_info.key?(:errors_on_stdout)
|
429
|
+
retriable_errors_on_stderr.concat(retriable_error_info[:errors_on_stderr]) if retriable_error_info.key?(:errors_on_stderr)
|
430
|
+
end
|
431
|
+
{
|
432
|
+
stdout => retriable_errors_on_stdout,
|
433
|
+
stderr => retriable_errors_on_stderr
|
434
|
+
}.map do |output, retriable_errors|
|
435
|
+
retriable_errors.map do |error|
|
436
|
+
if error.is_a?(String)
|
437
|
+
output.include?(error) ? error : nil
|
438
|
+
else
|
439
|
+
error_match = output.match error
|
440
|
+
error_match ? "/#{error.source}/ matched '#{error_match[0]}'" : nil
|
441
|
+
end
|
442
|
+
end.compact
|
443
|
+
end.flatten.uniq
|
444
|
+
end
|
445
|
+
|
446
|
+
# Deploy on all the nodes.
|
447
|
+
#
|
448
|
+
# Parameters::
|
449
|
+
# * *services* (Hash<String, Array<String>>): List of services to be deployed, per node
|
450
|
+
# Result::
|
451
|
+
# * Hash<String, [Integer or Symbol, String, String]>: Exit status code (or Symbol in case of error or dry run), standard output and error for each node.
|
452
|
+
def deploy(services)
|
453
|
+
outputs = {}
|
454
|
+
|
455
|
+
# Get the ssh user directly from the connector
|
456
|
+
ssh_user = @actions_executor.connector(:ssh).ssh_user
|
457
|
+
|
458
|
+
# Deploy for real
|
459
|
+
@nodes_handler.prefetch_metadata_of services.keys, :image
|
460
|
+
outputs = @actions_executor.execute_actions(
|
461
|
+
Hash[services.map do |node, node_services|
|
462
|
+
image_id = @nodes_handler.get_image_of(node)
|
463
|
+
# Install My_company corporate certificates if present
|
464
|
+
certificate_actions =
|
465
|
+
if @local_environment && ENV['hpc_certificates']
|
466
|
+
if File.exist?(ENV['hpc_certificates'])
|
467
|
+
log_debug "Deploy certificates from #{ENV['hpc_certificates']}"
|
468
|
+
case image_id
|
469
|
+
when 'debian_9', 'debian_10'
|
470
|
+
[
|
471
|
+
{
|
472
|
+
remote_bash: "#{ssh_user == 'root' ? '' : 'sudo '}apt update && #{ssh_user == 'root' ? '' : 'sudo '}apt install -y ca-certificates"
|
473
|
+
},
|
474
|
+
{
|
475
|
+
scp: {
|
476
|
+
ENV['hpc_certificates'] => '/usr/local/share/ca-certificates',
|
477
|
+
:sudo => ssh_user != 'root'
|
478
|
+
},
|
479
|
+
remote_bash: "#{ssh_user == 'root' ? '' : 'sudo '}update-ca-certificates"
|
480
|
+
}
|
481
|
+
]
|
482
|
+
when 'centos_7'
|
483
|
+
[
|
484
|
+
{
|
485
|
+
remote_bash: "#{ssh_user == 'root' ? '' : 'sudo '}yum install -y ca-certificates"
|
486
|
+
},
|
487
|
+
{
|
488
|
+
scp: Hash[Dir.glob("#{ENV['hpc_certificates']}/*.crt").map do |cert_file|
|
489
|
+
[
|
490
|
+
cert_file,
|
491
|
+
'/etc/pki/ca-trust/source/anchors'
|
492
|
+
]
|
493
|
+
end].merge(sudo: ssh_user != 'root'),
|
494
|
+
remote_bash: [
|
495
|
+
"#{ssh_user == 'root' ? '' : 'sudo '}update-ca-trust enable",
|
496
|
+
"#{ssh_user == 'root' ? '' : 'sudo '}update-ca-trust extract"
|
497
|
+
]
|
498
|
+
}
|
499
|
+
]
|
500
|
+
else
|
501
|
+
raise "Unknown image ID for node #{node}: #{image_id}. Check metadata for this node."
|
502
|
+
end
|
503
|
+
else
|
504
|
+
raise "Missing path referenced by the hpc_certificates environment variable: #{ENV['hpc_certificates']}"
|
505
|
+
end
|
506
|
+
else
|
507
|
+
[]
|
508
|
+
end
|
509
|
+
[
|
510
|
+
node,
|
511
|
+
[
|
512
|
+
# Install the mutex lock and acquire it
|
513
|
+
{
|
514
|
+
scp: { "#{__dir__}/mutex_dir" => '.' },
|
515
|
+
remote_bash: "while ! #{ssh_user == 'root' ? '' : 'sudo '}./mutex_dir lock /tmp/hybrid_platforms_conductor_deploy_lock \"$(ps -o ppid= -p $$)\"; do echo -e 'Another deployment is running on #{node}. Waiting for it to finish to continue...' ; sleep 5 ; done"
|
516
|
+
}
|
517
|
+
] +
|
518
|
+
certificate_actions +
|
519
|
+
@services_handler.actions_to_deploy_on(node, node_services, @use_why_run)
|
520
|
+
]
|
521
|
+
end],
|
522
|
+
timeout: @timeout,
|
523
|
+
concurrent: @concurrent_execution,
|
524
|
+
log_to_stdout: !@concurrent_execution
|
525
|
+
)
|
526
|
+
# Free eventual locks
|
527
|
+
@actions_executor.execute_actions(
|
528
|
+
Hash[services.keys.map do |node|
|
529
|
+
[
|
530
|
+
node,
|
531
|
+
{ remote_bash: "#{ssh_user == 'root' ? '' : 'sudo '}./mutex_dir unlock /tmp/hybrid_platforms_conductor_deploy_lock" }
|
532
|
+
]
|
533
|
+
end],
|
534
|
+
timeout: 10,
|
535
|
+
concurrent: true,
|
536
|
+
log_to_dir: nil
|
537
|
+
)
|
538
|
+
|
539
|
+
# Save logs
|
540
|
+
save_logs(outputs, services) if !@use_why_run && !@cmd_runner.dry_run
|
541
|
+
|
542
|
+
outputs
|
543
|
+
end
|
544
|
+
|
545
|
+
# Save some deployment logs.
|
546
|
+
# It uploads them on the nodes that have been deployed.
|
547
|
+
#
|
548
|
+
# Parameters::
|
549
|
+
# * *logs* (Hash<String, [Integer or Symbol, String, String]>): Exit status code (or Symbol in case of error or dry run), standard output and error for each node.
|
550
|
+
# * *services* (Hash<String, Array<String>>): List of services that have been deployed, per node
|
551
|
+
def save_logs(logs, services)
|
552
|
+
section "Saving deployment logs for #{logs.size} nodes" do
|
553
|
+
Dir.mktmpdir('hybrid_platforms_conductor-logs') do |tmp_dir|
|
554
|
+
ssh_user = @actions_executor.connector(:ssh).ssh_user
|
555
|
+
@actions_executor.execute_actions(
|
556
|
+
Hash[logs.map do |node, (exit_status, stdout, stderr)|
|
557
|
+
# Create a log file to be scp with all relevant info
|
558
|
+
now = Time.now.utc
|
559
|
+
log_file = "#{tmp_dir}/#{node}_#{now.strftime('%F_%H%M%S')}_#{ssh_user}"
|
560
|
+
services_info = @services_handler.log_info_for(node, services[node])
|
561
|
+
File.write(
|
562
|
+
log_file,
|
563
|
+
services_info.merge(
|
564
|
+
date: now.strftime('%F %T'),
|
565
|
+
user: ssh_user,
|
566
|
+
debug: log_debug? ? 'Yes' : 'No',
|
567
|
+
services: services[node].join(', '),
|
568
|
+
exit_status: exit_status
|
569
|
+
).map { |property, value| "#{property}: #{value}" }.join("\n") +
|
570
|
+
"\n===== STDOUT =====\n" +
|
571
|
+
(stdout || '') +
|
572
|
+
"\n===== STDERR =====\n" +
|
573
|
+
(stderr || '')
|
574
|
+
)
|
575
|
+
[
|
576
|
+
node,
|
577
|
+
{
|
578
|
+
remote_bash: "#{ssh_user == 'root' ? '' : 'sudo '}mkdir -p /var/log/deployments",
|
579
|
+
scp: {
|
580
|
+
log_file => '/var/log/deployments',
|
581
|
+
:sudo => ssh_user != 'root',
|
582
|
+
:owner => 'root',
|
583
|
+
:group => 'root'
|
584
|
+
}
|
585
|
+
}
|
586
|
+
]
|
587
|
+
end],
|
588
|
+
timeout: 10,
|
589
|
+
concurrent: true,
|
590
|
+
log_to_dir: nil
|
591
|
+
)
|
592
|
+
end
|
593
|
+
end
|
594
|
+
end
|
595
|
+
|
596
|
+
end
|
597
|
+
|
598
|
+
end
|