hybrid_platforms_conductor 32.10.0 → 32.13.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +1103 -0
- data/LICENSE.md +31 -0
- data/README.md +395 -0
- data/bin/setup +1 -1
- data/docs/api.md +349 -0
- data/docs/config_dsl.md +315 -0
- data/docs/executables.md +226 -0
- data/docs/executables/check-node.md +155 -0
- data/docs/executables/deploy.md +198 -0
- data/docs/executables/dump_nodes_json.md +110 -0
- data/docs/executables/free_ips.md +93 -0
- data/docs/executables/free_veids.md +73 -0
- data/docs/executables/get_impacted_nodes.md +94 -0
- data/docs/executables/last_deploys.md +114 -0
- data/docs/executables/nodes_to_deploy.md +139 -0
- data/docs/executables/report.md +159 -0
- data/docs/executables/run.md +126 -0
- data/docs/executables/setup.md +92 -0
- data/docs/executables/ssh_config.md +151 -0
- data/docs/executables/test.md +213 -0
- data/docs/executables/topograph.md +139 -0
- data/docs/gen/mermaid/README.md-0.png +0 -0
- data/docs/gen/mermaid/docs/executables/check-node.md-0.png +0 -0
- data/docs/gen/mermaid/docs/executables/deploy.md-0.png +0 -0
- data/docs/gen/mermaid/docs/executables/free_ips.md-0.png +0 -0
- data/docs/gen/mermaid/docs/executables/free_veids.md-0.png +0 -0
- data/docs/gen/mermaid/docs/executables/get_impacted_nodes.md-0.png +0 -0
- data/docs/gen/mermaid/docs/executables/last_deploys.md-0.png +0 -0
- data/docs/gen/mermaid/docs/executables/nodes_to_deploy.md-0.png +0 -0
- data/docs/gen/mermaid/docs/executables/report.md-0.png +0 -0
- data/docs/gen/mermaid/docs/executables/run.md-0.png +0 -0
- data/docs/gen/mermaid/docs/executables/setup.md-0.png +0 -0
- data/docs/gen/mermaid/docs/executables/ssh_config.md-0.png +0 -0
- data/docs/gen/mermaid/docs/executables/test.md-0.png +0 -0
- data/docs/install.md +161 -0
- data/docs/plugins.md +215 -0
- data/docs/plugins/action/bash.md +37 -0
- data/docs/plugins/action/interactive.md +37 -0
- data/docs/plugins/action/remote_bash.md +67 -0
- data/docs/plugins/action/ruby.md +69 -0
- data/docs/plugins/action/scp.md +61 -0
- data/docs/plugins/cmdb/config.md +46 -0
- data/docs/plugins/cmdb/host_ip.md +33 -0
- data/docs/plugins/cmdb/host_keys.md +33 -0
- data/docs/plugins/cmdb/platform_handlers.md +33 -0
- data/docs/plugins/connector/local.md +28 -0
- data/docs/plugins/connector/ssh.md +95 -0
- data/docs/plugins/platform_handler/yaml_inventory.md +105 -0
- data/docs/plugins/provisioner/docker.md +27 -0
- data/docs/plugins/provisioner/podman.md +27 -0
- data/docs/plugins/provisioner/proxmox.md +115 -0
- data/docs/plugins/report/confluence.md +49 -0
- data/docs/plugins/report/mediawiki.md +28 -0
- data/docs/plugins/report/stdout.md +32 -0
- data/docs/plugins/test/bitbucket_conf.md +97 -0
- data/docs/plugins/test/can_be_checked.md +27 -0
- data/docs/plugins/test/check_deploy_and_idempotence.md +61 -0
- data/docs/plugins/test/check_from_scratch.md +28 -0
- data/docs/plugins/test/connection.md +27 -0
- data/docs/plugins/test/deploy_freshness.md +27 -0
- data/docs/plugins/test/deploy_from_scratch.md +28 -0
- data/docs/plugins/test/deploy_removes_root_access.md +29 -0
- data/docs/plugins/test/divergence.md +41 -0
- data/docs/plugins/test/executables.md +26 -0
- data/docs/plugins/test/file_system.md +49 -0
- data/docs/plugins/test/file_system_hdfs.md +65 -0
- data/docs/plugins/test/hostname.md +27 -0
- data/docs/plugins/test/idempotence.md +56 -0
- data/docs/plugins/test/ip.md +28 -0
- data/docs/plugins/test/jenkins_ci_conf.md +54 -0
- data/docs/plugins/test/jenkins_ci_masters_ok.md +54 -0
- data/docs/plugins/test/linear_strategy.md +26 -0
- data/docs/plugins/test/local_users.md +48 -0
- data/docs/plugins/test/mounts.md +55 -0
- data/docs/plugins/test/orphan_files.md +38 -0
- data/docs/plugins/test/ports.md +50 -0
- data/docs/plugins/test/private_ips.md +27 -0
- data/docs/plugins/test/public_ips.md +27 -0
- data/docs/plugins/test/spectre.md +26 -0
- data/docs/plugins/test/veids.md +27 -0
- data/docs/plugins/test/vulnerabilities.md +65 -0
- data/docs/plugins/test_report/confluence.md +43 -0
- data/docs/plugins/test_report/stdout.md +26 -0
- data/docs/plugins_create.md +135 -0
- data/docs/tutorial.md +57 -0
- data/docs/tutorial/01_installation.md +129 -0
- data/docs/tutorial/02_first_node.md +466 -0
- data/docs/tutorial/03_scale.md +876 -0
- data/docs/tutorial/04_test.md +965 -0
- data/docs/tutorial/05_extend_with_plugins.md +1132 -0
- data/examples/bare/Gemfile +4 -0
- data/examples/bare/hpc_config.rb +2 -0
- data/examples/localhost/Gemfile +4 -0
- data/examples/localhost/hpc_config.rb +2 -0
- data/examples/localhost/inventory.yaml +4 -0
- data/lib/hybrid_platforms_conductor/actions_executor.rb +1 -0
- data/lib/hybrid_platforms_conductor/common_config_dsl/idempotence_tests.rb +23 -1
- data/lib/hybrid_platforms_conductor/deployer.rb +3 -2
- data/lib/hybrid_platforms_conductor/hpc_plugins/action/remote_bash.rb +29 -13
- data/lib/hybrid_platforms_conductor/hpc_plugins/action/scp.rb +1 -1
- data/lib/hybrid_platforms_conductor/hpc_plugins/connector/local.rb +98 -0
- data/lib/hybrid_platforms_conductor/hpc_plugins/connector/my_connector.rb.sample +2 -2
- data/lib/hybrid_platforms_conductor/hpc_plugins/connector/ssh.rb +15 -4
- data/lib/hybrid_platforms_conductor/hpc_plugins/platform_handler/platform_handler_plugin.rb.sample +5 -5
- data/lib/hybrid_platforms_conductor/hpc_plugins/platform_handler/yaml_inventory.rb +140 -0
- data/lib/hybrid_platforms_conductor/hpc_plugins/provisioner/proxmox.rb +6 -3
- data/lib/hybrid_platforms_conductor/hpc_plugins/report/templates/confluence_inventory.html.erb +1 -1
- data/lib/hybrid_platforms_conductor/hpc_plugins/test/bitbucket_conf.rb +4 -4
- data/lib/hybrid_platforms_conductor/hpc_plugins/test/check_deploy_and_idempotence.rb +4 -1
- data/lib/hybrid_platforms_conductor/hpc_plugins/test/deploy_freshness.rb +1 -1
- data/lib/hybrid_platforms_conductor/hpc_plugins/test/deploy_removes_root_access.rb +19 -17
- data/lib/hybrid_platforms_conductor/hpc_plugins/test/divergence.rb +19 -1
- data/lib/hybrid_platforms_conductor/hpc_plugins/test/executables.rb +27 -13
- data/lib/hybrid_platforms_conductor/hpc_plugins/test/hostname.rb +2 -1
- data/lib/hybrid_platforms_conductor/hpc_plugins/test/idempotence.rb +4 -1
- data/lib/hybrid_platforms_conductor/hpc_plugins/test/ip.rb +2 -1
- data/lib/hybrid_platforms_conductor/hpc_plugins/test/local_users.rb +2 -1
- data/lib/hybrid_platforms_conductor/hpc_plugins/test/mounts.rb +4 -3
- data/lib/hybrid_platforms_conductor/hpc_plugins/test/orphan_files.rb +2 -1
- data/lib/hybrid_platforms_conductor/hpc_plugins/test/spectre.rb +1 -1
- data/lib/hybrid_platforms_conductor/hpc_plugins/test/vulnerabilities.rb +8 -7
- data/lib/hybrid_platforms_conductor/hpc_plugins/test_report/confluence.rb +1 -1
- data/lib/hybrid_platforms_conductor/hpc_plugins/test_report/templates/confluence.html.erb +1 -1
- data/lib/hybrid_platforms_conductor/json_dumper.rb +1 -1
- data/lib/hybrid_platforms_conductor/platform_handler.rb +1 -1
- data/lib/hybrid_platforms_conductor/services_handler.rb +18 -16
- data/lib/hybrid_platforms_conductor/tests_runner.rb +0 -1
- data/lib/hybrid_platforms_conductor/topographer.rb +0 -1
- data/lib/hybrid_platforms_conductor/version.rb +1 -1
- data/spec/hybrid_platforms_conductor_test/api/actions_executor/actions/remote_bash_spec.rb +16 -0
- data/spec/hybrid_platforms_conductor_test/api/actions_executor/connectors/local/connectable_nodes_spec.rb +30 -0
- data/spec/hybrid_platforms_conductor_test/api/actions_executor/connectors/local/remote_actions_spec.rb +113 -0
- data/spec/hybrid_platforms_conductor_test/api/actions_executor/connectors/ssh/cli_options_spec.rb +6 -2
- data/spec/hybrid_platforms_conductor_test/api/actions_executor/connectors/ssh/global_helpers_spec.rb +38 -1
- data/spec/hybrid_platforms_conductor_test/api/actions_executor/connectors/ssh/remote_actions_spec.rb +37 -4
- data/spec/hybrid_platforms_conductor_test/docs_spec.rb +10 -0
- data/tools/check_md +89 -0
- data/tools/generate_mermaid +75 -0
- metadata +207 -12
@@ -5,6 +5,7 @@ require 'securerandom'
|
|
5
5
|
require 'tmpdir'
|
6
6
|
require 'hybrid_platforms_conductor/action'
|
7
7
|
require 'hybrid_platforms_conductor/cmd_runner'
|
8
|
+
require 'hybrid_platforms_conductor/config'
|
8
9
|
require 'hybrid_platforms_conductor/connector'
|
9
10
|
require 'hybrid_platforms_conductor/io_router'
|
10
11
|
require 'hybrid_platforms_conductor/logger_helpers'
|
@@ -11,13 +11,24 @@ module HybridPlatformsConductor
|
|
11
11
|
# Array< Hash<Symbol, Object> >
|
12
12
|
attr_reader :ignored_idempotence_tasks
|
13
13
|
|
14
|
-
#
|
14
|
+
# List of ignored tasks info. Each info has the following properties:
|
15
|
+
# * *nodes_selectors_stack* (Array<Object>): Stack of nodes selectors impacted by this rule
|
16
|
+
# * *ignored_tasks* (Hash<String, String>): List of task names for which we ignore divergence errors, with the corresponding descriptive reason for ignore.
|
17
|
+
# Array< Hash<Symbol, Object> >
|
18
|
+
attr_reader :ignored_divergent_tasks
|
19
|
+
|
20
|
+
# Initialize the DSL
|
15
21
|
def init_idempotence_tests
|
16
22
|
# List of ignored tasks info. Each info has the following properties:
|
17
23
|
# * *nodes_selectors_stack* (Array<Object>): Stack of nodes selectors impacted by this rule
|
18
24
|
# * *ignored_tasks* (Hash<String, String>): List of task names for which we ignore idempotence errors, with the corresponding descriptive reason for ignore.
|
19
25
|
# Array< Hash<Symbol, Object> >
|
20
26
|
@ignored_idempotence_tasks = []
|
27
|
+
# List of ignored tasks info. Each info has the following properties:
|
28
|
+
# * *nodes_selectors_stack* (Array<Object>): Stack of nodes selectors impacted by this rule
|
29
|
+
# * *ignored_tasks* (Hash<String, String>): List of task names for which we ignore divergence errors, with the corresponding descriptive reason for ignore.
|
30
|
+
# Array< Hash<Symbol, Object> >
|
31
|
+
@ignored_divergent_tasks = []
|
21
32
|
end
|
22
33
|
|
23
34
|
# Ignore idempotence errors on a set of tasks
|
@@ -31,6 +42,17 @@ module HybridPlatformsConductor
|
|
31
42
|
}
|
32
43
|
end
|
33
44
|
|
45
|
+
# Ignore idempotence errors on a set of tasks
|
46
|
+
#
|
47
|
+
# Parameters::
|
48
|
+
# * *tasks_to_ignore* (Hash<String, String>): Set of tasks to ignore, along with the reason
|
49
|
+
def ignore_divergent_tasks(tasks_to_ignore)
|
50
|
+
@ignored_divergent_tasks << {
|
51
|
+
ignored_tasks: tasks_to_ignore,
|
52
|
+
nodes_selectors_stack: current_nodes_selectors_stack,
|
53
|
+
}
|
54
|
+
end
|
55
|
+
|
34
56
|
end
|
35
57
|
|
36
58
|
end
|
@@ -41,7 +41,7 @@ module HybridPlatformsConductor
|
|
41
41
|
|
42
42
|
include LoggerHelpers
|
43
43
|
|
44
|
-
Config.extend_config_dsl_with ConfigDSLExtension, :
|
44
|
+
Config.extend_config_dsl_with ConfigDSLExtension, :init_deployer_config
|
45
45
|
|
46
46
|
# Do we use why-run mode while deploying? [default = false]
|
47
47
|
# Boolean
|
@@ -336,7 +336,8 @@ module HybridPlatformsConductor
|
|
336
336
|
actions_executor.connector(:ssh).passwords[node] = 'root_pwd'
|
337
337
|
deployer.local_environment = true
|
338
338
|
# Ignore secrets that might have been given: in Docker containers we always use dummy secrets
|
339
|
-
|
339
|
+
dummy_secrets_file = "#{@config.hybrid_platforms_dir}/dummy_secrets.json"
|
340
|
+
deployer.secrets = File.exist?(dummy_secrets_file) ? [JSON.parse(File.read(dummy_secrets_file))] : []
|
340
341
|
yield deployer, instance
|
341
342
|
end
|
342
343
|
rescue
|
@@ -14,16 +14,33 @@ module HybridPlatformsConductor
|
|
14
14
|
# [API] - @actions_executor is accessible
|
15
15
|
#
|
16
16
|
# Parameters::
|
17
|
-
# * *remote_bash* (Array
|
18
|
-
# *
|
19
|
-
# *
|
20
|
-
#
|
17
|
+
# * *remote_bash* (Array or Object): List of commands (or single command) to be executed. Each command can be the following:
|
18
|
+
# * String: Simple bash command.
|
19
|
+
# * Hash<Symbol, Object>: Information about the commands to execute. Can have the following properties:
|
20
|
+
# * *commands* (Array<String> or String): List of bash commands to execute (can be a single one) [default: ''].
|
21
|
+
# * *file* (String): Name of file from which commands should be taken [optional].
|
22
|
+
# * *env* (Hash<String, String>): Environment variables to be set before executing those commands [default: {}].
|
21
23
|
def setup(remote_bash)
|
22
|
-
|
23
|
-
#
|
24
|
-
|
25
|
-
|
26
|
-
@remote_bash
|
24
|
+
# Normalize the parameters.
|
25
|
+
# Array< Hash<Symbol,Object> >: Simple array of info:
|
26
|
+
# * *commands* (Array<String>): List of bash commands to execute.
|
27
|
+
# * *env* (Hash<String, String>): Environment variables to be set before executing those commands.
|
28
|
+
@remote_bash = (remote_bash.is_a?(Array) ? remote_bash : [remote_bash]).map do |cmd_info|
|
29
|
+
if cmd_info.is_a?(String)
|
30
|
+
{
|
31
|
+
commands: [cmd_info],
|
32
|
+
env: {}
|
33
|
+
}
|
34
|
+
else
|
35
|
+
commands = []
|
36
|
+
commands.concat(cmd_info[:commands].is_a?(String) ? [cmd_info[:commands]] : cmd_info[:commands]) if cmd_info[:commands]
|
37
|
+
commands << File.read(cmd_info[:file]) if cmd_info[:file]
|
38
|
+
{
|
39
|
+
commands: commands,
|
40
|
+
env: cmd_info[:env] || {}
|
41
|
+
}
|
42
|
+
end
|
43
|
+
end
|
27
44
|
end
|
28
45
|
|
29
46
|
# Do we need a connector to execute this action on a node?
|
@@ -46,10 +63,9 @@ module HybridPlatformsConductor
|
|
46
63
|
# [API] - @stderr_io can be used to log stderr messages
|
47
64
|
# [API] - run_cmd(String) method can be used to execute a command. See CmdRunner#run_cmd to know about the result's signature.
|
48
65
|
def execute
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
bash_str = bash_commands.join("\n")
|
66
|
+
bash_str = @remote_bash.map do |cmd_info|
|
67
|
+
(cmd_info[:env].map { |var_name, var_value| "export #{var_name}='#{var_value}'" } + cmd_info[:commands]).join("\n")
|
68
|
+
end.join("\n")
|
53
69
|
log_debug "[#{@node}] - Execute remote Bash commands \"#{bash_str}\"..."
|
54
70
|
@connector.remote_bash bash_str
|
55
71
|
end
|
@@ -16,7 +16,7 @@ module HybridPlatformsConductor
|
|
16
16
|
# Parameters::
|
17
17
|
# * *mappings* (Hash<String or Symbol, Object>): Set of couples source => destination_dir to copy files or directories from the local file system to the remote file system.
|
18
18
|
# The following properties can also be used:
|
19
|
-
# * *sudo* (Boolean): Do we use sudo to make the copy? [default: false]
|
19
|
+
# * *sudo* (Boolean): Do we use sudo on the remote to make the copy? [default: false]
|
20
20
|
# * *owner* (String or nil): Owner to use for files, or nil to use current one [default: nil]
|
21
21
|
# * *group* (String or nil): Group to use for files, or nil to use current one [default: nil]
|
22
22
|
def setup(mappings)
|
@@ -0,0 +1,98 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
require 'tmpdir'
|
3
|
+
|
4
|
+
module HybridPlatformsConductor
|
5
|
+
|
6
|
+
module HpcPlugins
|
7
|
+
|
8
|
+
module Connector
|
9
|
+
|
10
|
+
# Connector executing remote commands on the local environment in dedicated workspaces (/tmp/hpc_local_workspaces)
|
11
|
+
class Local < HybridPlatformsConductor::Connector
|
12
|
+
|
13
|
+
# Select nodes where this connector can connect.
|
14
|
+
# [API] - This method is mandatory
|
15
|
+
# [API] - @cmd_runner can be used
|
16
|
+
# [API] - @nodes_handler can be used
|
17
|
+
#
|
18
|
+
# Parameters::
|
19
|
+
# * *nodes* (Array<String>): List of candidate nodes
|
20
|
+
# Result::
|
21
|
+
# * Array<String>: List of nodes we can connect to from the candidates
|
22
|
+
def connectable_nodes_from(nodes)
|
23
|
+
@nodes_handler.prefetch_metadata_of nodes, :local_node
|
24
|
+
nodes.select { |node| @nodes_handler.get_local_node_of(node) }
|
25
|
+
end
|
26
|
+
|
27
|
+
# Run bash commands on a given node.
|
28
|
+
# [API] - This method is mandatory
|
29
|
+
# [API] - If defined, then with_connection_to has been called before this method.
|
30
|
+
# [API] - @cmd_runner can be used
|
31
|
+
# [API] - @nodes_handler can be used
|
32
|
+
# [API] - @node can be used to access the node on which we execute the remote bash
|
33
|
+
# [API] - @timeout can be used to know when the action should fail
|
34
|
+
# [API] - @stdout_io can be used to send stdout output
|
35
|
+
# [API] - @stderr_io can be used to send stderr output
|
36
|
+
#
|
37
|
+
# Parameters::
|
38
|
+
# * *bash_cmds* (String): Bash commands to execute
|
39
|
+
def remote_bash(bash_cmds)
|
40
|
+
run_cmd "cd #{workspace_for(@node)} ; #{bash_cmds}"
|
41
|
+
end
|
42
|
+
|
43
|
+
# Execute an interactive shell on the remote node
|
44
|
+
# [API] - This method is mandatory
|
45
|
+
# [API] - If defined, then with_connection_to has been called before this method.
|
46
|
+
# [API] - @cmd_runner can be used
|
47
|
+
# [API] - @nodes_handler can be used
|
48
|
+
# [API] - @node can be used to access the node on which we execute the remote bash
|
49
|
+
# [API] - @timeout can be used to know when the action should fail
|
50
|
+
# [API] - @stdout_io can be used to send stdout output
|
51
|
+
# [API] - @stderr_io can be used to send stderr output
|
52
|
+
def remote_interactive
|
53
|
+
system "cd #{workspace_for(@node)} ; /bin/bash"
|
54
|
+
end
|
55
|
+
|
56
|
+
# Copy a file to the remote node in a directory
|
57
|
+
# [API] - This method is mandatory
|
58
|
+
# [API] - If defined, then with_connection_to has been called before this method.
|
59
|
+
# [API] - @cmd_runner can be used
|
60
|
+
# [API] - @nodes_handler can be used
|
61
|
+
# [API] - @node can be used to access the node on which we execute the remote bash
|
62
|
+
# [API] - @timeout can be used to know when the action should fail
|
63
|
+
# [API] - @stdout_io can be used to send stdout output
|
64
|
+
# [API] - @stderr_io can be used to send stderr output
|
65
|
+
#
|
66
|
+
# Parameters::
|
67
|
+
# * *from* (String): Local file to copy
|
68
|
+
# * *to* (String): Remote directory to copy to
|
69
|
+
# * *sudo* (Boolean): Do we use sudo to copy? [default: false]
|
70
|
+
# * *owner* (String or nil): Owner to be used when copying the files, or nil for current one [default: nil]
|
71
|
+
# * *group* (String or nil): Group to be used when copying the files, or nil for current one [default: nil]
|
72
|
+
def remote_copy(from, to, sudo: false, owner: nil, group: nil)
|
73
|
+
# If the destination is a relative path, prepend the workspace dir to it.
|
74
|
+
to = "#{workspace_for(@node)}/#{to}" unless to.start_with?('/')
|
75
|
+
FileUtils.cp_r from, to
|
76
|
+
end
|
77
|
+
|
78
|
+
private
|
79
|
+
|
80
|
+
# Create or reuse a dedicated workspace for a node
|
81
|
+
#
|
82
|
+
# Parameters::
|
83
|
+
# * *node* (String): Node for which we want a dedicated workspace
|
84
|
+
# Result::
|
85
|
+
# * String: Dedicated workspace path
|
86
|
+
def workspace_for(node)
|
87
|
+
workspace = "#{Dir.tmpdir}/hpc_local_workspaces/#{node}"
|
88
|
+
FileUtils.mkdir_p workspace
|
89
|
+
workspace
|
90
|
+
end
|
91
|
+
|
92
|
+
end
|
93
|
+
|
94
|
+
end
|
95
|
+
|
96
|
+
end
|
97
|
+
|
98
|
+
end
|
@@ -73,7 +73,7 @@ module HybridPlatformsConductor
|
|
73
73
|
# Result::
|
74
74
|
# * Array<String>: List of nodes we can connect to from the candidates
|
75
75
|
def connectable_nodes_from(nodes)
|
76
|
-
nodes.select { |node| @nodes_handler.
|
76
|
+
nodes.select { |node| @nodes_handler.get_host_ip_of(node) =~ /^192\.168\..+$/ }
|
77
77
|
end
|
78
78
|
|
79
79
|
# Prepare connections to a given set of nodes.
|
@@ -135,7 +135,7 @@ module HybridPlatformsConductor
|
|
135
135
|
# Parameters::
|
136
136
|
# * *from* (String): Local file to copy
|
137
137
|
# * *to* (String): Remote directory to copy to
|
138
|
-
# * *sudo* (Boolean): Do we use sudo to copy? [default: false]
|
138
|
+
# * *sudo* (Boolean): Do we use sudo on the remote to copy? [default: false]
|
139
139
|
# * *owner* (String or nil): Owner to be used when copying the files, or nil for current one [default: nil]
|
140
140
|
# * *group* (String or nil): Group to be used when copying the files, or nil for current one [default: nil]
|
141
141
|
def remote_copy(from, to, sudo: false, owner: nil, group: nil)
|
@@ -131,6 +131,10 @@ module HybridPlatformsConductor
|
|
131
131
|
# Default values
|
132
132
|
@ssh_user = ENV['hpc_ssh_user']
|
133
133
|
@ssh_user = ENV['USER'] if @ssh_user.nil? || @ssh_user.empty?
|
134
|
+
if @ssh_user.nil? || @ssh_user.empty?
|
135
|
+
_exit_status, stdout = @cmd_runner.run_cmd 'whoami', log_to_stdout: log_debug?
|
136
|
+
@ssh_user = stdout.strip
|
137
|
+
end
|
134
138
|
@ssh_use_control_master = true
|
135
139
|
@ssh_strict_host_key_checking = true
|
136
140
|
@passwords = {}
|
@@ -236,9 +240,9 @@ module HybridPlatformsConductor
|
|
236
240
|
ssh_cmd =
|
237
241
|
if @nodes_handler.get_ssh_session_exec_of(@node) == 'false'
|
238
242
|
# When ExecSession is disabled we need to use stdin directly
|
239
|
-
"{ cat | #{ssh_exec} #{ssh_url} -T; } <<'
|
243
|
+
"{ cat | #{ssh_exec} #{ssh_url} -T; } <<'HPC_EOF'\n#{bash_cmds}\nHPC_EOF"
|
240
244
|
else
|
241
|
-
"#{ssh_exec} #{ssh_url} /bin/bash <<'
|
245
|
+
"#{ssh_exec} #{ssh_url} /bin/bash <<'HPC_EOF'\n#{bash_cmds}\nHPC_EOF"
|
242
246
|
end
|
243
247
|
# Due to a limitation of Process.spawn, each individual argument is limited to 128KB of size.
|
244
248
|
# Therefore we need to make sure that if bash_cmds exceeds MAX_CMD_ARG_LENGTH bytes (considering EOF chars) then we use an intermediary shell script to store the commands.
|
@@ -292,13 +296,20 @@ module HybridPlatformsConductor
|
|
292
296
|
# Parameters::
|
293
297
|
# * *from* (String): Local file to copy
|
294
298
|
# * *to* (String): Remote directory to copy to
|
295
|
-
# * *sudo* (Boolean): Do we use sudo to copy? [default: false]
|
299
|
+
# * *sudo* (Boolean): Do we use sudo on the remote to copy? [default: false]
|
296
300
|
# * *owner* (String or nil): Owner to be used when copying the files, or nil for current one [default: nil]
|
297
301
|
# * *group* (String or nil): Group to be used when copying the files, or nil for current one [default: nil]
|
298
302
|
def remote_copy(from, to, sudo: false, owner: nil, group: nil)
|
299
303
|
if @nodes_handler.get_ssh_session_exec_of(@node) == 'false'
|
300
304
|
# We don't have ExecSession, so don't use ssh, but scp instead.
|
301
|
-
|
305
|
+
if sudo
|
306
|
+
# We need to first copy the file in an accessible directory, and then sudo mv
|
307
|
+
remote_bash('mkdir -p hpc_tmp_scp')
|
308
|
+
run_cmd "scp -S #{ssh_exec} #{from} #{ssh_url}:./hpc_tmp_scp"
|
309
|
+
remote_bash("#{@nodes_handler.sudo_on(@node)} mv ./hpc_tmp_scp/#{File.basename(from)} #{to}")
|
310
|
+
else
|
311
|
+
run_cmd "scp -S #{ssh_exec} #{from} #{ssh_url}:#{to}"
|
312
|
+
end
|
302
313
|
else
|
303
314
|
run_cmd <<~EOS
|
304
315
|
cd #{File.dirname(from)} && \
|
data/lib/hybrid_platforms_conductor/hpc_plugins/platform_handler/platform_handler_plugin.rb.sample
CHANGED
@@ -69,7 +69,7 @@ module HybridPlatformsConductor
|
|
69
69
|
end
|
70
70
|
|
71
71
|
# Setup the platform, install dependencies...
|
72
|
-
# [API] - This method is
|
72
|
+
# [API] - This method is optional.
|
73
73
|
# [API] - @cmd_runner is accessible.
|
74
74
|
def setup
|
75
75
|
# This method is called by the setup executable.
|
@@ -163,7 +163,7 @@ module HybridPlatformsConductor
|
|
163
163
|
end
|
164
164
|
|
165
165
|
# Package the repository, ready to be deployed on artefacts or directly to a node.
|
166
|
-
# [API] - This method is
|
166
|
+
# [API] - This method is optional.
|
167
167
|
# [API] - @cmd_runner is accessible.
|
168
168
|
# [API] - @actions_executor is accessible.
|
169
169
|
#
|
@@ -212,7 +212,7 @@ module HybridPlatformsConductor
|
|
212
212
|
# This method returns all the actions to execute to deploy on a node.
|
213
213
|
# The use_why_run switch is on if the deployment should just be simulated.
|
214
214
|
# Those actions (bash commands, scp of files, ruby code...) should be thread safe as they can be executed in parallel with other deployment actions for other nodes in case of a concurrent deployment on several nodes.
|
215
|
-
# The complete description of an action can be found in
|
215
|
+
# The complete description of an action can be found in the action plugins' documentation.
|
216
216
|
[
|
217
217
|
{
|
218
218
|
scp: {
|
@@ -229,7 +229,7 @@ module HybridPlatformsConductor
|
|
229
229
|
end
|
230
230
|
|
231
231
|
# Prepare a why-run deployment so that a JSON file describing the nodes will be output in the run_logs.
|
232
|
-
# [API] - This method is
|
232
|
+
# [API] - This method is optional.
|
233
233
|
# [API] - @cmd_runner is accessible.
|
234
234
|
# [API] - @actions_executor is accessible.
|
235
235
|
# [API] - @deployer is accessible.
|
@@ -247,7 +247,7 @@ module HybridPlatformsConductor
|
|
247
247
|
# Result::
|
248
248
|
# * Array< Hash<Symbol,Object> >: List of task properties. The following properties should be returned, among free ones:
|
249
249
|
# * *name* (String): Task name
|
250
|
-
# * *status* (Symbol): Task status. Should be
|
250
|
+
# * *status* (Symbol): Task status. Should be one of:
|
251
251
|
# * *:changed*: The task has been changed
|
252
252
|
# * *:identical*: The task has not been changed
|
253
253
|
# * *diffs* (String): Differences, if any
|
@@ -0,0 +1,140 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
require 'hybrid_platforms_conductor/platform_handler'
|
3
|
+
|
4
|
+
module HybridPlatformsConductor
|
5
|
+
|
6
|
+
module HpcPlugins
|
7
|
+
|
8
|
+
module PlatformHandler
|
9
|
+
|
10
|
+
# Basic platform handler, reading inventory and metadata from simple Yaml files.
|
11
|
+
class YamlInventory < HybridPlatformsConductor::PlatformHandler
|
12
|
+
|
13
|
+
# Initialize a new instance of this platform handler.
|
14
|
+
# [API] - This method is optional.
|
15
|
+
# [API] - @cmd_runner is accessible.
|
16
|
+
def init
|
17
|
+
# This method is called when initializing a new instance of this platform handler, for a given repository.
|
18
|
+
inv_file = "#{@repository_path}/inventory.yaml"
|
19
|
+
@inventory = File.exist?(inv_file) ? YAML.load(File.read(inv_file)) : {}
|
20
|
+
end
|
21
|
+
|
22
|
+
# Get the list of known nodes.
|
23
|
+
# [API] - This method is mandatory.
|
24
|
+
#
|
25
|
+
# Result::
|
26
|
+
# * Array<String>: List of node names
|
27
|
+
def known_nodes
|
28
|
+
@inventory.keys
|
29
|
+
end
|
30
|
+
|
31
|
+
# Get the metadata of a given node.
|
32
|
+
# [API] - This method is mandatory.
|
33
|
+
#
|
34
|
+
# Parameters::
|
35
|
+
# * *node* (String): Node to read metadata from
|
36
|
+
# Result::
|
37
|
+
# * Hash<Symbol,Object>: The corresponding metadata
|
38
|
+
def metadata_for(node)
|
39
|
+
(@inventory[node]['metadata'] || {}).transform_keys(&:to_sym)
|
40
|
+
end
|
41
|
+
|
42
|
+
# Return the services for a given node
|
43
|
+
# [API] - This method is mandatory.
|
44
|
+
#
|
45
|
+
# Parameters::
|
46
|
+
# * *node* (String): node to read configuration from
|
47
|
+
# Result::
|
48
|
+
# * Array<String>: The corresponding services
|
49
|
+
def services_for(node)
|
50
|
+
@inventory[node]['services'] || []
|
51
|
+
end
|
52
|
+
|
53
|
+
# Get the list of services we can deploy
|
54
|
+
# [API] - This method is mandatory.
|
55
|
+
#
|
56
|
+
# Result::
|
57
|
+
# * Array<String>: The corresponding services
|
58
|
+
def deployable_services
|
59
|
+
Dir.glob("#{@repository_path}/service_*.rb").map { |file| File.basename(file).match(/^service_(.*)\.rb$/)[1] }
|
60
|
+
end
|
61
|
+
|
62
|
+
# Get the list of actions to perform to deploy on a given node.
|
63
|
+
# Those actions can be executed in parallel with other deployments on other nodes. They must be thread safe.
|
64
|
+
# [API] - This method is mandatory.
|
65
|
+
# [API] - @cmd_runner is accessible.
|
66
|
+
# [API] - @actions_executor is accessible.
|
67
|
+
#
|
68
|
+
# Parameters::
|
69
|
+
# * *node* (String): Node to deploy on
|
70
|
+
# * *service* (String): Service to be deployed
|
71
|
+
# * *use_why_run* (Boolean): Do we use a why-run mode? [default = true]
|
72
|
+
# Result::
|
73
|
+
# * Array< Hash<Symbol,Object> >: List of actions to be done
|
74
|
+
def actions_to_deploy_on(node, service, use_why_run: true)
|
75
|
+
# Load the check and deploy methods in a temporary class for encapsulation
|
76
|
+
service_file = "#{@repository_path}/service_#{service}.rb"
|
77
|
+
Class.new do
|
78
|
+
|
79
|
+
include LoggerHelpers
|
80
|
+
|
81
|
+
# Constructor
|
82
|
+
#
|
83
|
+
# Parameters::
|
84
|
+
# * *platform_handler* (PlatformHandler): PlatformHandler needing this service to be deployed
|
85
|
+
# * *logger* (Logger): Logger to be used [default: Logger.new(STDOUT)]
|
86
|
+
# * *logger_stderr* (Logger): Logger to be used for stderr [default: Logger.new(STDERR)]
|
87
|
+
# * *config* (Config): Config to be used. [default: Config.new]
|
88
|
+
# * *nodes_handler* (NodesHandler): NodesHandler to be used [default: NodesHandler.new]
|
89
|
+
# * *cmd_runner* (CmdRunner): CmdRunner to be used [default: CmdRunner.new]
|
90
|
+
def initialize(
|
91
|
+
platform_handler,
|
92
|
+
logger: Logger.new(STDOUT),
|
93
|
+
logger_stderr: Logger.new(STDERR),
|
94
|
+
config: Config.new,
|
95
|
+
nodes_handler: NodesHandler.new,
|
96
|
+
cmd_runner: CmdRunner.new
|
97
|
+
)
|
98
|
+
init_loggers(logger, logger_stderr)
|
99
|
+
@platform_handler = platform_handler
|
100
|
+
@config = config
|
101
|
+
@nodes_handler = nodes_handler
|
102
|
+
@cmd_runner = cmd_runner
|
103
|
+
end
|
104
|
+
|
105
|
+
class_eval(File.read(service_file))
|
106
|
+
|
107
|
+
end.new(
|
108
|
+
self,
|
109
|
+
logger: @logger,
|
110
|
+
logger_stderr: @logger_stderr,
|
111
|
+
config: @config,
|
112
|
+
nodes_handler: @nodes_handler,
|
113
|
+
cmd_runner: @cmd_runner
|
114
|
+
).send(use_why_run ? :check : :deploy, node)
|
115
|
+
end
|
116
|
+
|
117
|
+
# Parse stdout and stderr of a given deploy run and get the list of tasks with their status
|
118
|
+
# [API] - This method is mandatory.
|
119
|
+
#
|
120
|
+
# Parameters::
|
121
|
+
# * *stdout* (String): stdout to be parsed
|
122
|
+
# * *stderr* (String): stderr to be parsed
|
123
|
+
# Result::
|
124
|
+
# * Array< Hash<Symbol,Object> >: List of task properties. The following properties should be returned, among free ones:
|
125
|
+
# * *name* (String): Task name
|
126
|
+
# * *status* (Symbol): Task status. Should be one of:
|
127
|
+
# * *:changed*: The task has been changed
|
128
|
+
# * *:identical*: The task has not been changed
|
129
|
+
# * *diffs* (String): Differences, if any
|
130
|
+
def parse_deploy_output(stdout, stderr)
|
131
|
+
[]
|
132
|
+
end
|
133
|
+
|
134
|
+
end
|
135
|
+
|
136
|
+
end
|
137
|
+
|
138
|
+
end
|
139
|
+
|
140
|
+
end
|