hybrid_platforms_conductor 32.12.0 → 32.13.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (134) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1103 -0
  3. data/LICENSE.md +31 -0
  4. data/README.md +395 -0
  5. data/bin/setup +1 -1
  6. data/docs/api.md +349 -0
  7. data/docs/config_dsl.md +315 -0
  8. data/docs/executables.md +226 -0
  9. data/docs/executables/check-node.md +155 -0
  10. data/docs/executables/deploy.md +198 -0
  11. data/docs/executables/dump_nodes_json.md +110 -0
  12. data/docs/executables/free_ips.md +93 -0
  13. data/docs/executables/free_veids.md +73 -0
  14. data/docs/executables/get_impacted_nodes.md +94 -0
  15. data/docs/executables/last_deploys.md +114 -0
  16. data/docs/executables/nodes_to_deploy.md +139 -0
  17. data/docs/executables/report.md +159 -0
  18. data/docs/executables/run.md +126 -0
  19. data/docs/executables/setup.md +92 -0
  20. data/docs/executables/ssh_config.md +151 -0
  21. data/docs/executables/test.md +213 -0
  22. data/docs/executables/topograph.md +139 -0
  23. data/docs/gen/mermaid/README.md-0.png +0 -0
  24. data/docs/gen/mermaid/docs/executables/check-node.md-0.png +0 -0
  25. data/docs/gen/mermaid/docs/executables/deploy.md-0.png +0 -0
  26. data/docs/gen/mermaid/docs/executables/free_ips.md-0.png +0 -0
  27. data/docs/gen/mermaid/docs/executables/free_veids.md-0.png +0 -0
  28. data/docs/gen/mermaid/docs/executables/get_impacted_nodes.md-0.png +0 -0
  29. data/docs/gen/mermaid/docs/executables/last_deploys.md-0.png +0 -0
  30. data/docs/gen/mermaid/docs/executables/nodes_to_deploy.md-0.png +0 -0
  31. data/docs/gen/mermaid/docs/executables/report.md-0.png +0 -0
  32. data/docs/gen/mermaid/docs/executables/run.md-0.png +0 -0
  33. data/docs/gen/mermaid/docs/executables/setup.md-0.png +0 -0
  34. data/docs/gen/mermaid/docs/executables/ssh_config.md-0.png +0 -0
  35. data/docs/gen/mermaid/docs/executables/test.md-0.png +0 -0
  36. data/docs/install.md +161 -0
  37. data/docs/plugins.md +215 -0
  38. data/docs/plugins/action/bash.md +37 -0
  39. data/docs/plugins/action/interactive.md +37 -0
  40. data/docs/plugins/action/remote_bash.md +67 -0
  41. data/docs/plugins/action/ruby.md +69 -0
  42. data/docs/plugins/action/scp.md +61 -0
  43. data/docs/plugins/cmdb/config.md +46 -0
  44. data/docs/plugins/cmdb/host_ip.md +33 -0
  45. data/docs/plugins/cmdb/host_keys.md +33 -0
  46. data/docs/plugins/cmdb/platform_handlers.md +33 -0
  47. data/docs/plugins/connector/local.md +28 -0
  48. data/docs/plugins/connector/ssh.md +95 -0
  49. data/docs/plugins/platform_handler/yaml_inventory.md +105 -0
  50. data/docs/plugins/provisioner/docker.md +27 -0
  51. data/docs/plugins/provisioner/podman.md +27 -0
  52. data/docs/plugins/provisioner/proxmox.md +115 -0
  53. data/docs/plugins/report/confluence.md +49 -0
  54. data/docs/plugins/report/mediawiki.md +28 -0
  55. data/docs/plugins/report/stdout.md +32 -0
  56. data/docs/plugins/test/bitbucket_conf.md +97 -0
  57. data/docs/plugins/test/can_be_checked.md +27 -0
  58. data/docs/plugins/test/check_deploy_and_idempotence.md +61 -0
  59. data/docs/plugins/test/check_from_scratch.md +28 -0
  60. data/docs/plugins/test/connection.md +27 -0
  61. data/docs/plugins/test/deploy_freshness.md +27 -0
  62. data/docs/plugins/test/deploy_from_scratch.md +28 -0
  63. data/docs/plugins/test/deploy_removes_root_access.md +29 -0
  64. data/docs/plugins/test/divergence.md +41 -0
  65. data/docs/plugins/test/executables.md +26 -0
  66. data/docs/plugins/test/file_system.md +49 -0
  67. data/docs/plugins/test/file_system_hdfs.md +65 -0
  68. data/docs/plugins/test/hostname.md +27 -0
  69. data/docs/plugins/test/idempotence.md +56 -0
  70. data/docs/plugins/test/ip.md +28 -0
  71. data/docs/plugins/test/jenkins_ci_conf.md +54 -0
  72. data/docs/plugins/test/jenkins_ci_masters_ok.md +54 -0
  73. data/docs/plugins/test/linear_strategy.md +26 -0
  74. data/docs/plugins/test/local_users.md +48 -0
  75. data/docs/plugins/test/mounts.md +55 -0
  76. data/docs/plugins/test/orphan_files.md +38 -0
  77. data/docs/plugins/test/ports.md +50 -0
  78. data/docs/plugins/test/private_ips.md +27 -0
  79. data/docs/plugins/test/public_ips.md +27 -0
  80. data/docs/plugins/test/spectre.md +26 -0
  81. data/docs/plugins/test/veids.md +27 -0
  82. data/docs/plugins/test/vulnerabilities.md +65 -0
  83. data/docs/plugins/test_report/confluence.md +43 -0
  84. data/docs/plugins/test_report/stdout.md +26 -0
  85. data/docs/plugins_create.md +135 -0
  86. data/docs/tutorial.md +57 -0
  87. data/docs/tutorial/01_installation.md +129 -0
  88. data/docs/tutorial/02_first_node.md +466 -0
  89. data/docs/tutorial/03_scale.md +876 -0
  90. data/docs/tutorial/04_test.md +965 -0
  91. data/docs/tutorial/05_extend_with_plugins.md +1132 -0
  92. data/examples/bare/Gemfile +4 -0
  93. data/examples/bare/hpc_config.rb +2 -0
  94. data/examples/localhost/Gemfile +4 -0
  95. data/examples/localhost/hpc_config.rb +2 -0
  96. data/examples/localhost/inventory.yaml +4 -0
  97. data/lib/hybrid_platforms_conductor/actions_executor.rb +1 -0
  98. data/lib/hybrid_platforms_conductor/deployer.rb +3 -2
  99. data/lib/hybrid_platforms_conductor/hpc_plugins/action/remote_bash.rb +29 -13
  100. data/lib/hybrid_platforms_conductor/hpc_plugins/action/scp.rb +1 -1
  101. data/lib/hybrid_platforms_conductor/hpc_plugins/connector/local.rb +98 -0
  102. data/lib/hybrid_platforms_conductor/hpc_plugins/connector/my_connector.rb.sample +2 -2
  103. data/lib/hybrid_platforms_conductor/hpc_plugins/connector/ssh.rb +7 -3
  104. data/lib/hybrid_platforms_conductor/hpc_plugins/platform_handler/platform_handler_plugin.rb.sample +5 -5
  105. data/lib/hybrid_platforms_conductor/hpc_plugins/platform_handler/yaml_inventory.rb +140 -0
  106. data/lib/hybrid_platforms_conductor/hpc_plugins/provisioner/proxmox.rb +5 -2
  107. data/lib/hybrid_platforms_conductor/hpc_plugins/test/bitbucket_conf.rb +4 -4
  108. data/lib/hybrid_platforms_conductor/hpc_plugins/test/deploy_freshness.rb +1 -1
  109. data/lib/hybrid_platforms_conductor/hpc_plugins/test/deploy_removes_root_access.rb +19 -17
  110. data/lib/hybrid_platforms_conductor/hpc_plugins/test/divergence.rb +3 -0
  111. data/lib/hybrid_platforms_conductor/hpc_plugins/test/hostname.rb +2 -1
  112. data/lib/hybrid_platforms_conductor/hpc_plugins/test/ip.rb +2 -1
  113. data/lib/hybrid_platforms_conductor/hpc_plugins/test/local_users.rb +2 -1
  114. data/lib/hybrid_platforms_conductor/hpc_plugins/test/mounts.rb +4 -3
  115. data/lib/hybrid_platforms_conductor/hpc_plugins/test/orphan_files.rb +2 -1
  116. data/lib/hybrid_platforms_conductor/hpc_plugins/test/spectre.rb +1 -1
  117. data/lib/hybrid_platforms_conductor/hpc_plugins/test/vulnerabilities.rb +8 -7
  118. data/lib/hybrid_platforms_conductor/hpc_plugins/test_report/confluence.rb +1 -1
  119. data/lib/hybrid_platforms_conductor/json_dumper.rb +1 -1
  120. data/lib/hybrid_platforms_conductor/platform_handler.rb +1 -1
  121. data/lib/hybrid_platforms_conductor/services_handler.rb +18 -16
  122. data/lib/hybrid_platforms_conductor/tests_runner.rb +0 -1
  123. data/lib/hybrid_platforms_conductor/topographer.rb +0 -1
  124. data/lib/hybrid_platforms_conductor/version.rb +1 -1
  125. data/spec/hybrid_platforms_conductor_test/api/actions_executor/actions/remote_bash_spec.rb +16 -0
  126. data/spec/hybrid_platforms_conductor_test/api/actions_executor/connectors/local/connectable_nodes_spec.rb +30 -0
  127. data/spec/hybrid_platforms_conductor_test/api/actions_executor/connectors/local/remote_actions_spec.rb +113 -0
  128. data/spec/hybrid_platforms_conductor_test/api/actions_executor/connectors/ssh/cli_options_spec.rb +6 -2
  129. data/spec/hybrid_platforms_conductor_test/api/actions_executor/connectors/ssh/global_helpers_spec.rb +38 -1
  130. data/spec/hybrid_platforms_conductor_test/api/actions_executor/connectors/ssh/remote_actions_spec.rb +8 -8
  131. data/spec/hybrid_platforms_conductor_test/docs_spec.rb +10 -0
  132. data/tools/check_md +89 -0
  133. data/tools/generate_mermaid +75 -0
  134. metadata +207 -12
@@ -0,0 +1,4 @@
1
+ source 'http://rubygems.org'
2
+
3
+ # Orchestrate all the platforms with Hybrid Platforms Conductor
4
+ gem 'hybrid_platforms_conductor', path: '../..'
@@ -0,0 +1,2 @@
1
+ # Just a bare configuration.
2
+ # It should work already like this.
@@ -0,0 +1,4 @@
1
+ source 'http://rubygems.org'
2
+
3
+ # Orchestrate all the platforms with Hybrid Platforms Conductor
4
+ gem 'hybrid_platforms_conductor', path: '../..'
@@ -0,0 +1,2 @@
1
+ # This configuration file just defines 1 node being localhost
2
+ yaml_inventory_platform path: '.'
@@ -0,0 +1,4 @@
1
+ ---
2
+ local:
3
+ metadata:
4
+ host_ip: 127.0.0.1
@@ -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'
@@ -41,7 +41,7 @@ module HybridPlatformsConductor
41
41
 
42
42
  include LoggerHelpers
43
43
 
44
- Config.extend_config_dsl_with ConfigDSLExtension, :init_nodes_handler_config
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
- deployer.secrets = [JSON.parse(File.read("#{@config.hybrid_platforms_dir}/dummy_secrets.json"))]
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< Hash<Symbol, Object> or Array<String> or String>): List of bash actions to execute. Each action can have the following properties:
18
- # * *commands* (Array<String> or String): List of bash commands to execute (can be a single one). This is the default property also that allows to not use the Hash form for brevity.
19
- # * *file* (String): Name of file from which commands should be taken.
20
- # * *env* (Hash<String, String>): Environment variables to be set before executing those commands.
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
- @remote_bash = remote_bash
23
- # Normalize the parameters
24
- @remote_bash = [@remote_bash] if @remote_bash.is_a?(String)
25
- @remote_bash = { commands: @remote_bash } if @remote_bash.is_a?(Array)
26
- @remote_bash[:commands] = [@remote_bash[:commands]] if @remote_bash[:commands].is_a?(String)
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
- bash_commands = (@remote_bash[:env] || {}).map { |var_name, var_value| "export #{var_name}='#{var_value}'" }
50
- bash_commands.concat(@remote_bash[:commands].clone) if @remote_bash.key?(:commands)
51
- bash_commands << File.read(@remote_bash[:file]) if @remote_bash.key?(:file)
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.get_ip_of(node) =~ /^192\.168\..+$/ }
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; } <<'EOF'\n#{bash_cmds}\nEOF"
243
+ "{ cat | #{ssh_exec} #{ssh_url} -T; } <<'HPC_EOF'\n#{bash_cmds}\nHPC_EOF"
240
244
  else
241
- "#{ssh_exec} #{ssh_url} /bin/bash <<'EOF'\n#{bash_cmds}\nEOF"
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,7 +296,7 @@ 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)
@@ -69,7 +69,7 @@ module HybridPlatformsConductor
69
69
  end
70
70
 
71
71
  # Setup the platform, install dependencies...
72
- # [API] - This method is mandatory.
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 mandatory.
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 actions_executor.rb file, in the execute_actions_on method description.
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 mandatory.
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 on of:
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