hybrid_platforms_conductor 32.12.0 → 32.13.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (199) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1133 -0
  3. data/LICENSE.md +31 -0
  4. data/README.md +402 -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 +61 -0
  87. data/docs/tutorial/01_installation.md +131 -0
  88. data/docs/tutorial/02_first_node.md +468 -0
  89. data/docs/tutorial/03_scale.md +878 -0
  90. data/docs/tutorial/04_test.md +977 -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/examples/tutorial/01_installation/my-platforms/Gemfile +3 -0
  98. data/examples/tutorial/01_installation/my-platforms/hpc_config.rb +0 -0
  99. data/examples/tutorial/02_first_node/my-platforms/Gemfile +3 -0
  100. data/examples/tutorial/02_first_node/my-platforms/hpc_config.rb +1 -0
  101. data/examples/tutorial/02_first_node/my-service-conf-repo/inventory.yaml +13 -0
  102. data/examples/tutorial/02_first_node/my-service-conf-repo/my-service.conf.erb +3 -0
  103. data/examples/tutorial/02_first_node/my-service-conf-repo/service_my-service.rb +58 -0
  104. data/examples/tutorial/02_first_node/node/my-service.conf +4 -0
  105. data/examples/tutorial/03_scale/my-platforms/Gemfile +3 -0
  106. data/examples/tutorial/03_scale/my-platforms/hpc_config.rb +1 -0
  107. data/examples/tutorial/03_scale/my-platforms/my_commands.bash +2 -0
  108. data/examples/tutorial/03_scale/my-service-conf-repo/inventory.yaml +90 -0
  109. data/examples/tutorial/03_scale/my-service-conf-repo/my-service.conf.erb +3 -0
  110. data/examples/tutorial/03_scale/my-service-conf-repo/service_my-service.rb +58 -0
  111. data/examples/tutorial/03_scale/my-service-conf-repo/service_web-hello.rb +43 -0
  112. data/examples/tutorial/03_scale/node/my-service.conf +4 -0
  113. data/examples/tutorial/03_scale/web_docker_image/Dockerfile +33 -0
  114. data/examples/tutorial/03_scale/web_docker_image/hello_world.txt +1 -0
  115. data/examples/tutorial/03_scale/web_docker_image/hpc_root.key +27 -0
  116. data/examples/tutorial/03_scale/web_docker_image/hpc_root.key.pub +1 -0
  117. data/examples/tutorial/03_scale/web_docker_image/main.go +43 -0
  118. data/examples/tutorial/03_scale/web_docker_image/start.sh +7 -0
  119. data/examples/tutorial/03_scale/web_docker_image/test.bash +6 -0
  120. data/examples/tutorial/04_test/my-platforms/Gemfile +3 -0
  121. data/examples/tutorial/04_test/my-platforms/hpc_config.rb +12 -0
  122. data/examples/tutorial/04_test/my-platforms/images/debian_10/Dockerfile +13 -0
  123. data/examples/tutorial/04_test/my-platforms/my_commands.bash +2 -0
  124. data/examples/tutorial/04_test/my-service-conf-repo/inventory.yaml +100 -0
  125. data/examples/tutorial/04_test/my-service-conf-repo/my-service.conf.erb +3 -0
  126. data/examples/tutorial/04_test/my-service-conf-repo/service_my-service.rb +58 -0
  127. data/examples/tutorial/04_test/my-service-conf-repo/service_web-hello.rb +43 -0
  128. data/examples/tutorial/04_test/node/my-service.conf +4 -0
  129. data/examples/tutorial/04_test/web_docker_image/Dockerfile +33 -0
  130. data/examples/tutorial/04_test/web_docker_image/hello_world.txt +1 -0
  131. data/examples/tutorial/04_test/web_docker_image/hpc_root.key +27 -0
  132. data/examples/tutorial/04_test/web_docker_image/hpc_root.key.pub +1 -0
  133. data/examples/tutorial/04_test/web_docker_image/main.go +43 -0
  134. data/examples/tutorial/04_test/web_docker_image/start.sh +7 -0
  135. data/examples/tutorial/04_test/web_docker_image/test.bash +6 -0
  136. data/examples/tutorial/05_extend_with_plugins/dev-servers-conf-repo/hosts.json +12 -0
  137. data/examples/tutorial/05_extend_with_plugins/dev-servers-conf-repo/install-gcc.bash +14 -0
  138. data/examples/tutorial/05_extend_with_plugins/dev-servers-conf-repo/install-python.bash +14 -0
  139. data/examples/tutorial/05_extend_with_plugins/dev_docker_image/Dockerfile +20 -0
  140. data/examples/tutorial/05_extend_with_plugins/dev_docker_image/hpc_root.key +27 -0
  141. data/examples/tutorial/05_extend_with_plugins/dev_docker_image/hpc_root.key.pub +1 -0
  142. data/examples/tutorial/05_extend_with_plugins/my-platforms/Gemfile +4 -0
  143. data/examples/tutorial/05_extend_with_plugins/my-platforms/hpc_config.rb +13 -0
  144. data/examples/tutorial/05_extend_with_plugins/my-platforms/images/debian_10/Dockerfile +13 -0
  145. data/examples/tutorial/05_extend_with_plugins/my-platforms/my_commands.bash +2 -0
  146. data/examples/tutorial/05_extend_with_plugins/my-service-conf-repo/inventory.yaml +100 -0
  147. data/examples/tutorial/05_extend_with_plugins/my-service-conf-repo/my-service.conf.erb +3 -0
  148. data/examples/tutorial/05_extend_with_plugins/my-service-conf-repo/service_my-service.rb +58 -0
  149. data/examples/tutorial/05_extend_with_plugins/my-service-conf-repo/service_web-hello.rb +43 -0
  150. data/examples/tutorial/05_extend_with_plugins/my_hpc_plugins/lib/my_hpc_plugins/hpc_plugins/platform_handler/json_bash.rb +115 -0
  151. data/examples/tutorial/05_extend_with_plugins/my_hpc_plugins/lib/my_hpc_plugins/hpc_plugins/report/web_report.rb +52 -0
  152. data/examples/tutorial/05_extend_with_plugins/my_hpc_plugins/lib/my_hpc_plugins/hpc_plugins/test/root_space.rb +44 -0
  153. data/examples/tutorial/05_extend_with_plugins/my_hpc_plugins/my_hpc_plugins.gemspec +15 -0
  154. data/examples/tutorial/05_extend_with_plugins/node/my-service.conf +4 -0
  155. data/examples/tutorial/05_extend_with_plugins/web_docker_image/Dockerfile +33 -0
  156. data/examples/tutorial/05_extend_with_plugins/web_docker_image/hello_world.txt +1 -0
  157. data/examples/tutorial/05_extend_with_plugins/web_docker_image/hpc_root.key +27 -0
  158. data/examples/tutorial/05_extend_with_plugins/web_docker_image/hpc_root.key.pub +1 -0
  159. data/examples/tutorial/05_extend_with_plugins/web_docker_image/main.go +43 -0
  160. data/examples/tutorial/05_extend_with_plugins/web_docker_image/start.sh +7 -0
  161. data/examples/tutorial/05_extend_with_plugins/web_docker_image/test.bash +6 -0
  162. data/lib/hybrid_platforms_conductor/actions_executor.rb +1 -0
  163. data/lib/hybrid_platforms_conductor/deployer.rb +3 -2
  164. data/lib/hybrid_platforms_conductor/hpc_plugins/action/remote_bash.rb +29 -13
  165. data/lib/hybrid_platforms_conductor/hpc_plugins/action/scp.rb +1 -1
  166. data/lib/hybrid_platforms_conductor/hpc_plugins/connector/local.rb +98 -0
  167. data/lib/hybrid_platforms_conductor/hpc_plugins/connector/my_connector.rb.sample +2 -2
  168. data/lib/hybrid_platforms_conductor/hpc_plugins/connector/ssh.rb +7 -3
  169. data/lib/hybrid_platforms_conductor/hpc_plugins/platform_handler/platform_handler_plugin.rb.sample +5 -5
  170. data/lib/hybrid_platforms_conductor/hpc_plugins/platform_handler/yaml_inventory.rb +140 -0
  171. data/lib/hybrid_platforms_conductor/hpc_plugins/provisioner/proxmox.rb +5 -2
  172. data/lib/hybrid_platforms_conductor/hpc_plugins/test/bitbucket_conf.rb +4 -4
  173. data/lib/hybrid_platforms_conductor/hpc_plugins/test/deploy_freshness.rb +1 -1
  174. data/lib/hybrid_platforms_conductor/hpc_plugins/test/deploy_removes_root_access.rb +19 -17
  175. data/lib/hybrid_platforms_conductor/hpc_plugins/test/divergence.rb +3 -0
  176. data/lib/hybrid_platforms_conductor/hpc_plugins/test/hostname.rb +2 -1
  177. data/lib/hybrid_platforms_conductor/hpc_plugins/test/ip.rb +2 -1
  178. data/lib/hybrid_platforms_conductor/hpc_plugins/test/local_users.rb +2 -1
  179. data/lib/hybrid_platforms_conductor/hpc_plugins/test/mounts.rb +4 -3
  180. data/lib/hybrid_platforms_conductor/hpc_plugins/test/orphan_files.rb +2 -1
  181. data/lib/hybrid_platforms_conductor/hpc_plugins/test/spectre.rb +1 -1
  182. data/lib/hybrid_platforms_conductor/hpc_plugins/test/vulnerabilities.rb +9 -7
  183. data/lib/hybrid_platforms_conductor/hpc_plugins/test_report/confluence.rb +1 -1
  184. data/lib/hybrid_platforms_conductor/json_dumper.rb +1 -1
  185. data/lib/hybrid_platforms_conductor/platform_handler.rb +1 -1
  186. data/lib/hybrid_platforms_conductor/services_handler.rb +18 -16
  187. data/lib/hybrid_platforms_conductor/tests_runner.rb +0 -1
  188. data/lib/hybrid_platforms_conductor/topographer.rb +0 -1
  189. data/lib/hybrid_platforms_conductor/version.rb +1 -1
  190. data/spec/hybrid_platforms_conductor_test/api/actions_executor/actions/remote_bash_spec.rb +16 -0
  191. data/spec/hybrid_platforms_conductor_test/api/actions_executor/connectors/local/connectable_nodes_spec.rb +30 -0
  192. data/spec/hybrid_platforms_conductor_test/api/actions_executor/connectors/local/remote_actions_spec.rb +113 -0
  193. data/spec/hybrid_platforms_conductor_test/api/actions_executor/connectors/ssh/cli_options_spec.rb +6 -2
  194. data/spec/hybrid_platforms_conductor_test/api/actions_executor/connectors/ssh/global_helpers_spec.rb +38 -1
  195. data/spec/hybrid_platforms_conductor_test/api/actions_executor/connectors/ssh/remote_actions_spec.rb +8 -8
  196. data/spec/hybrid_platforms_conductor_test/docs_spec.rb +10 -0
  197. data/tools/check_md +89 -0
  198. data/tools/generate_mermaid +75 -0
  199. metadata +337 -12
@@ -53,7 +53,7 @@ module HybridPlatformsConductor
53
53
 
54
54
  private
55
55
 
56
- TEMPLATES_PATH = File.expand_path("#{File.dirname(__FILE__)}/templates")
56
+ TEMPLATES_PATH = File.expand_path("#{__dir__}/templates")
57
57
 
58
58
  # Render a given ERB template into a String
59
59
  #
@@ -56,7 +56,7 @@ module HybridPlatformsConductor
56
56
  nodes = @nodes_handler.select_nodes(nodes_selectors)
57
57
  unless @skip_run
58
58
  nodes.map { |node| @nodes_handler.platform_for(node) }.uniq.each.each do |platform_handler|
59
- platform_handler.prepare_why_run_deploy_for_json_dump
59
+ platform_handler.prepare_why_run_deploy_for_json_dump if platform_handler.respond_to?(:prepare_why_run_deploy_for_json_dump)
60
60
  end
61
61
  @deployer.concurrent_execution = true
62
62
  @deployer.use_why_run = true
@@ -142,7 +142,7 @@ module HybridPlatformsConductor
142
142
  git_status = git.status
143
143
  git_commit = git.log.first
144
144
  {
145
- repo_name: File.basename(git.remotes.first.url).gsub(/\.git$/, ''),
145
+ repo_name: git.remotes.empty? ? File.basename(@repository_path) : File.basename(git.remotes.first.url).gsub(/\.git$/, ''),
146
146
  commit: {
147
147
  id: git_commit.sha,
148
148
  ref: git_commit.name,
@@ -108,23 +108,25 @@ module HybridPlatformsConductor
108
108
  local_environment:
109
109
  )
110
110
  platforms_for(services).each do |platform, platform_services|
111
- platform_name = platform.name
112
- # Compute the package ID that is unique to this packaging, so that we don't mix it with others if needed.
113
- package_id = {
114
- platform_name: platform_name,
115
- services: Hash[platform_services.map { |node, node_services| [node, node_services.sort] }].sort,
116
- secrets: secrets.sort,
117
- local_environment: local_environment
118
- }
119
- if ServicesHandler.packaged_deployments.include?(package_id)
120
- log_debug "Platform #{platform_name} has already been packaged for this deployment (package ID #{package_id}). Won't package it another time."
121
- else
122
- platform.package(
123
- services: platform_services,
124
- secrets: secrets,
111
+ if platform.respond_to?(:package)
112
+ platform_name = platform.name
113
+ # Compute the package ID that is unique to this packaging, so that we don't mix it with others if needed.
114
+ package_id = {
115
+ platform_name: platform_name,
116
+ services: Hash[platform_services.map { |node, node_services| [node, node_services.sort] }].sort,
117
+ secrets: secrets.sort,
125
118
  local_environment: local_environment
126
- )
127
- ServicesHandler.packaged_deployments << package_id
119
+ }
120
+ if ServicesHandler.packaged_deployments.include?(package_id)
121
+ log_debug "Platform #{platform_name} has already been packaged for this deployment (package ID #{package_id}). Won't package it another time."
122
+ else
123
+ platform.package(
124
+ services: platform_services,
125
+ secrets: secrets,
126
+ local_environment: local_environment
127
+ )
128
+ ServicesHandler.packaged_deployments << package_id
129
+ end
128
130
  end
129
131
  end
130
132
  end
@@ -452,7 +452,6 @@ module HybridPlatformsConductor
452
452
  end]
453
453
  section "Run test commands on #{@test_cmds.keys.size} connected nodes (timeout to #{timeout} secs)" do
454
454
  start_time = Time.now
455
- nbr_secs = nil
456
455
  @actions_executor.max_threads = @max_threads_connection_on_nodes
457
456
  @actions_result = @actions_executor.execute_actions(
458
457
  @test_cmds,
@@ -110,7 +110,6 @@ module HybridPlatformsConductor
110
110
  @skip_run = false
111
111
 
112
112
  # Parse plugins
113
- root_path = File.expand_path("#{File.dirname(__FILE__)}/..")
114
113
  @plugins = Hash[Dir.
115
114
  glob("#{File.dirname(__FILE__)}/topographer/plugins/*.rb").
116
115
  map do |file_name|
@@ -1,5 +1,5 @@
1
1
  module HybridPlatformsConductor
2
2
 
3
- VERSION = '32.12.0'
3
+ VERSION = '32.13.4'
4
4
 
5
5
  end
@@ -79,6 +79,22 @@ describe HybridPlatformsConductor::ActionsExecutor do
79
79
  end
80
80
  end
81
81
 
82
+ it 'executes remote Bash code both from commands and a file in sequence' do
83
+ with_test_platform_for_action_plugins do |repository|
84
+ File.write("#{repository}/commands.txt", "bash_cmd3.bash\nbash_cmd4.bash")
85
+ test_actions_executor.execute_actions('node' => { remote_bash: [
86
+ 'bash_cmd1.bash',
87
+ 'bash_cmd2.bash',
88
+ { file: "#{repository}/commands.txt" }
89
+ ] })
90
+ expect(test_actions_executor.connector(:test_connector).calls).to eq [
91
+ [:connectable_nodes_from, ['node']],
92
+ [:with_connection_to, ['node'], { no_exception: true }],
93
+ [:remote_bash, "bash_cmd1.bash\nbash_cmd2.bash\nbash_cmd3.bash\nbash_cmd4.bash"]
94
+ ]
95
+ end
96
+ end
97
+
82
98
  it 'executes remote Bash code with environment variables set' do
83
99
  with_test_platform_for_action_plugins do
84
100
  test_actions_executor.execute_actions('node' => { remote_bash: {
@@ -0,0 +1,30 @@
1
+ describe HybridPlatformsConductor::ActionsExecutor do
2
+
3
+ context 'checking connector plugin local' do
4
+
5
+ context 'checking connectable nodes selection' do
6
+
7
+ # Return the connector to be tested
8
+ #
9
+ # Result::
10
+ # * Connector: Connector to be tested
11
+ def test_connector
12
+ test_actions_executor.connector(:local)
13
+ end
14
+
15
+ it 'selects connectable nodes correctly' do
16
+ with_test_platform(nodes: {
17
+ 'node1' => { meta: { host_ip: '192.168.42.42' } },
18
+ 'node2' => {},
19
+ 'node3' => { meta: { host_ip: '127.0.0.1', local_node: true } },
20
+ 'node4' => { meta: { local_node: true } }
21
+ }) do
22
+ expect(test_connector.connectable_nodes_from(%w[node1 node2 node3 node4]).sort).to eq %w[node3 node4].sort
23
+ end
24
+ end
25
+
26
+ end
27
+
28
+ end
29
+
30
+ end
@@ -0,0 +1,113 @@
1
+ describe HybridPlatformsConductor::ActionsExecutor do
2
+
3
+ context 'checking connector plugin local' do
4
+
5
+ context 'checking remote actions' do
6
+
7
+ # Return the connector to be tested
8
+ #
9
+ # Result::
10
+ # * Connector: Connector to be tested
11
+ def test_connector
12
+ test_actions_executor.connector(:local)
13
+ end
14
+
15
+ # Get a test platform and the connector prepared the same way Actions Executor does before calling remote_* methods
16
+ #
17
+ # Parameters::
18
+ # * *expected_cmds* (Array< [String or Regexp, Proc] >): The expected commands that should be used, and their corresponding mocked code [default: []]
19
+ # * *expected_stdout* (String): Expected stdout after client code execution [default: '']
20
+ # * *expected_stderr* (String): Expected stderr after client code execution [default: '']
21
+ # * *timeout* (Integer or nil): Timeout to prepare the connector for [default: nil]
22
+ # * *password* (String or nil): Password to set for the node, or nil for none [default: nil]
23
+ # * *additional_config* (String): Additional config [default: '']
24
+ # * Proc: Client code to execute testing
25
+ def with_test_platform_for_remote_testing(
26
+ expected_cmds: [],
27
+ expected_stdout: '',
28
+ expected_stderr: '',
29
+ timeout: nil,
30
+ password: nil,
31
+ additional_config: ''
32
+ )
33
+ with_test_platform(
34
+ { nodes: { 'node' => { meta: { local_node: true } } } },
35
+ false,
36
+ additional_config
37
+ ) do
38
+ with_cmd_runner_mocked(expected_cmds) do
39
+ test_connector.with_connection_to(['node']) do
40
+ stdout = ''
41
+ stderr = ''
42
+ test_connector.prepare_for('node', timeout, stdout, stderr)
43
+ yield
44
+ expect(stdout).to eq expected_stdout
45
+ expect(stderr).to eq expected_stderr
46
+ end
47
+ end
48
+ end
49
+ end
50
+
51
+ it 'executes bash commands remotely' do
52
+ with_test_platform_for_remote_testing(
53
+ expected_cmds: [['cd /tmp/hpc_local_workspaces/node ; bash_cmd.bash', proc { [0, 'Bash commands executed on node', ''] }]],
54
+ expected_stdout: 'Bash commands executed on node'
55
+ ) do
56
+ test_connector.remote_bash('bash_cmd.bash')
57
+ end
58
+ end
59
+
60
+ it 'executes bash commands remotely with timeout' do
61
+ with_test_platform_for_remote_testing(
62
+ expected_cmds: [
63
+ [
64
+ 'cd /tmp/hpc_local_workspaces/node ; bash_cmd.bash',
65
+ proc do |cmd, log_to_file: nil, log_to_stdout: true, log_stdout_to_io: nil, log_stderr_to_io: nil, expected_code: 0, timeout: nil, no_exception: false|
66
+ expect(timeout).to eq 5
67
+ [0, '', '']
68
+ end
69
+ ]
70
+ ],
71
+ timeout: 5
72
+ ) do
73
+ test_connector.remote_bash('bash_cmd.bash')
74
+ end
75
+ end
76
+
77
+ it 'executes interactive commands remotely' do
78
+ with_test_platform_for_remote_testing do
79
+ expect(test_connector).to receive(:system) do |cmd|
80
+ expect(cmd).to eq 'cd /tmp/hpc_local_workspaces/node ; /bin/bash'
81
+ end
82
+ test_connector.remote_interactive
83
+ end
84
+ end
85
+
86
+ it 'copies files remotely' do
87
+ with_test_platform_for_remote_testing do
88
+ expect(FileUtils).to receive(:cp_r).with('/path/to/src.file', '/remote_path/to/dst.dir')
89
+ test_connector.remote_copy('/path/to/src.file', '/remote_path/to/dst.dir')
90
+ end
91
+ end
92
+
93
+ it 'copies files remotely with timeout' do
94
+ with_test_platform_for_remote_testing(
95
+ timeout: 5
96
+ ) do
97
+ expect(FileUtils).to receive(:cp_r).with('/path/to/src.file', '/remote_path/to/dst.dir')
98
+ test_connector.remote_copy('/path/to/src.file', '/remote_path/to/dst.dir')
99
+ end
100
+ end
101
+
102
+ it 'copies relative files remotely' do
103
+ with_test_platform_for_remote_testing do
104
+ expect(FileUtils).to receive(:cp_r).with('/path/to/src.file', '/tmp/hpc_local_workspaces/node/to/dst.dir')
105
+ test_connector.remote_copy('/path/to/src.file', 'to/dst.dir')
106
+ end
107
+ end
108
+
109
+ end
110
+
111
+ end
112
+
113
+ end
@@ -97,11 +97,15 @@ describe HybridPlatformsConductor::ActionsExecutor do
97
97
  end
98
98
  end
99
99
 
100
- it 'fails if no user name has been given, either through environment or command-line' do
100
+ it 'fails if no user name has been given, either through environment, command-line or using whoami' do
101
101
  ENV.delete 'hpc_ssh_user'
102
102
  ENV.delete 'USER'
103
103
  with_test_platform_for_cli do
104
- expect { run 'run', '--node', 'node', '--command', 'echo Hello' }.to raise_error(RuntimeError, 'No SSH user name specified. Please use --ssh-user option or hpc_ssh_user environment variable to set it.')
104
+ with_cmd_runner_mocked([
105
+ ['whoami', proc { [0, '', ''] }]
106
+ ]) do
107
+ expect { run 'run', '--node', 'node', '--command', 'echo Hello' }.to raise_error(RuntimeError, 'No SSH user name specified. Please use --ssh-user option or hpc_ssh_user environment variable to set it.')
108
+ end
105
109
  end
106
110
  end
107
111
 
@@ -47,7 +47,7 @@ describe HybridPlatformsConductor::ActionsExecutor do
47
47
  end.join("\n") + "\n"
48
48
  end
49
49
 
50
- it 'generates a global configuration with user from environment' do
50
+ it 'generates a global configuration with user from hpc_ssh_user environment variable' do
51
51
  with_test_platform do
52
52
  ENV['hpc_ssh_user'] = 'test_user'
53
53
  expect(ssh_config_for(nil)).to eq <<~EOS
@@ -59,6 +59,43 @@ describe HybridPlatformsConductor::ActionsExecutor do
59
59
  end
60
60
  end
61
61
 
62
+ it 'generates a global configuration with user from USER environment variable' do
63
+ with_test_platform do
64
+ ENV['USER'] = 'test_user'
65
+ expect(ssh_config_for(nil)).to eq <<~EOS
66
+ Host *
67
+ User test_user
68
+ ControlPath #{Dir.tmpdir}/hpc_ssh/hpc_ssh_mux_%h_%p_%r
69
+ PubkeyAcceptedKeyTypes +ssh-dss
70
+ EOS
71
+ end
72
+ end
73
+
74
+ it 'generates a global configuration with user taken from whoami when no env variable is set' do
75
+ with_test_platform do
76
+ original_user = ENV['USER']
77
+ begin
78
+ ENV.delete 'USER'
79
+ ENV.delete 'hpc_ssh_user'
80
+ with_cmd_runner_mocked(
81
+ [
82
+ ['ssh -V 2>&1', proc { [0, "OpenSSH_7.4p1 Debian-10+deb9u7, OpenSSL 1.0.2u 20 Dec 2019\n", ''] }],
83
+ ['whoami', proc { [0, 'test_whoami_user', ''] }]
84
+ ]
85
+ ) do
86
+ expect(ssh_config_for(nil)).to eq <<~EOS
87
+ Host *
88
+ User test_whoami_user
89
+ ControlPath #{Dir.tmpdir}/hpc_ssh/hpc_ssh_mux_%h_%p_%r
90
+ PubkeyAcceptedKeyTypes +ssh-dss
91
+ EOS
92
+ end
93
+ ensure
94
+ ENV['USER'] = original_user
95
+ end
96
+ end
97
+ end
98
+
62
99
  it 'generates a global configuration with user from setting' do
63
100
  with_test_platform do
64
101
  test_connector.ssh_user = 'test_user'
@@ -6,7 +6,7 @@ describe HybridPlatformsConductor::ActionsExecutor do
6
6
 
7
7
  it 'executes bash commands remotely' do
8
8
  with_test_platform_for_remote_testing(
9
- expected_cmds: [[/.+\/ssh hpc\.node \/bin\/bash <<'EOF'\nbash_cmd.bash\nEOF/, proc { [0, 'Bash commands executed on node', ''] }]],
9
+ expected_cmds: [[/.+\/ssh hpc\.node \/bin\/bash <<'HPC_EOF'\nbash_cmd.bash\nHPC_EOF/, proc { [0, 'Bash commands executed on node', ''] }]],
10
10
  expected_stdout: 'Bash commands executed on node'
11
11
  ) do
12
12
  test_connector.remote_bash('bash_cmd.bash')
@@ -17,7 +17,7 @@ describe HybridPlatformsConductor::ActionsExecutor do
17
17
  with_test_platform_for_remote_testing(
18
18
  expected_cmds: [
19
19
  [
20
- /.+\/ssh hpc\.node \/bin\/bash <<'EOF'\nbash_cmd.bash\nEOF/,
20
+ /.+\/ssh hpc\.node \/bin\/bash <<'HPC_EOF'\nbash_cmd.bash\nHPC_EOF/,
21
21
  proc do |cmd, log_to_file: nil, log_to_stdout: true, log_stdout_to_io: nil, log_stderr_to_io: nil, expected_code: 0, timeout: nil, no_exception: false|
22
22
  expect(timeout).to eq 5
23
23
  [0, '', '']
@@ -76,7 +76,7 @@ describe HybridPlatformsConductor::ActionsExecutor do
76
76
  [
77
77
  /.+\/hpc_temp_cmds_.+\.sh$/,
78
78
  proc do |received_cmd|
79
- expect(File.read(received_cmd)).to match /.+\/ssh hpc\.node \/bin\/bash <<'EOF'\n#{Regexp.escape(cmd)}\nEOF/
79
+ expect(File.read(received_cmd)).to match /.+\/ssh hpc\.node \/bin\/bash <<'HPC_EOF'\n#{Regexp.escape(cmd)}\nHPC_EOF/
80
80
  [0, 'Bash commands executed on node', '']
81
81
  end
82
82
  ]
@@ -143,7 +143,7 @@ describe HybridPlatformsConductor::ActionsExecutor do
143
143
 
144
144
  it 'executes bash commands remotely without Session Exec capabilities' do
145
145
  with_test_platform_for_remote_testing(
146
- expected_cmds: [[/^\{ cat \| .+\/ssh hpc\.node -T; } <<'EOF'\nbash_cmd.bash\nEOF$/, proc { [0, 'Bash commands executed on node', ''] }]],
146
+ expected_cmds: [[/^\{ cat \| .+\/ssh hpc\.node -T; } <<'HPC_EOF'\nbash_cmd.bash\nHPC_EOF$/, proc { [0, 'Bash commands executed on node', ''] }]],
147
147
  expected_stdout: 'Bash commands executed on node',
148
148
  session_exec: false
149
149
  ) do
@@ -168,12 +168,12 @@ describe HybridPlatformsConductor::ActionsExecutor do
168
168
  it 'copies files remotely without Session Exec capabilities and with sudo' do
169
169
  with_test_platform_for_remote_testing(
170
170
  expected_cmds: [
171
- [/^\{ cat \| .+\/ssh hpc\.node -T; } <<'EOF'\nmkdir -p hpc_tmp_scp\nEOF$/, proc { [0, '', ''] }],
171
+ [/^\{ cat \| .+\/ssh hpc\.node -T; } <<'HPC_EOF'\nmkdir -p hpc_tmp_scp\nHPC_EOF$/, proc { [0, '', ''] }],
172
172
  [
173
173
  /^scp -S .+\/ssh \/path\/to\/src.file hpc\.node:\.\/hpc_tmp_scp$/,
174
174
  proc { [0, '', ''] }
175
175
  ],
176
- [/^\{ cat \| .+\/ssh hpc\.node -T; } <<'EOF'\nsudo -u root mv \.\/hpc_tmp_scp\/src\.file \/remote_path\/to\/dst\.dir\nEOF$/, proc { [0, '', ''] }]
176
+ [/^\{ cat \| .+\/ssh hpc\.node -T; } <<'HPC_EOF'\nsudo -u root mv \.\/hpc_tmp_scp\/src\.file \/remote_path\/to\/dst\.dir\nHPC_EOF$/, proc { [0, '', ''] }]
177
177
  ],
178
178
  session_exec: false
179
179
  ) do
@@ -184,12 +184,12 @@ describe HybridPlatformsConductor::ActionsExecutor do
184
184
  it 'copies files remotely without Session Exec capabilities and with a different sudo' do
185
185
  with_test_platform_for_remote_testing(
186
186
  expected_cmds: [
187
- [/^\{ cat \| .+\/ssh hpc\.node -T; } <<'EOF'\nmkdir -p hpc_tmp_scp\nEOF$/, proc { [0, '', ''] }],
187
+ [/^\{ cat \| .+\/ssh hpc\.node -T; } <<'HPC_EOF'\nmkdir -p hpc_tmp_scp\nHPC_EOF$/, proc { [0, '', ''] }],
188
188
  [
189
189
  /^scp -S .+\/ssh \/path\/to\/src.file hpc\.node:\.\/hpc_tmp_scp$/,
190
190
  proc { [0, '', ''] }
191
191
  ],
192
- [/^\{ cat \| .+\/ssh hpc\.node -T; } <<'EOF'\nother_sudo --user root mv \.\/hpc_tmp_scp\/src\.file \/remote_path\/to\/dst\.dir\nEOF$/, proc { [0, '', ''] }]
192
+ [/^\{ cat \| .+\/ssh hpc\.node -T; } <<'HPC_EOF'\nother_sudo --user root mv \.\/hpc_tmp_scp\/src\.file \/remote_path\/to\/dst\.dir\nHPC_EOF$/, proc { [0, '', ''] }]
193
193
  ],
194
194
  additional_config: 'sudo_for { |user| "other_sudo --user #{user}" }',
195
195
  session_exec: false
@@ -0,0 +1,10 @@
1
+ describe 'Documentation' do
2
+
3
+ it 'makes sure all Markdown links are valid' do
4
+ check_stdout = `bundle exec tools/check_md README.md #{Dir.glob('docs/**/*.md').join(' ')}`.split("\n")
5
+ summary_idx = check_stdout.index { |line| line =~ /^\d+ errors:$/ }
6
+ expect(summary_idx).not_to eq(nil), "Could not parse check output: #{check_stdout.join("\n")}"
7
+ expect(check_stdout[summary_idx]).to eq('0 errors:'), "Invalid links found: #{check_stdout[summary_idx..-1].join("\n")}"
8
+ end
9
+
10
+ end
data/tools/check_md ADDED
@@ -0,0 +1,89 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # Parsed information, per file name.
4
+ # Information is:
5
+ # * *anchors* (Array<String>): List of anchors
6
+ # * *links* (Array<String>): List of links
7
+ @info = {}
8
+
9
+ # Get a file name from a link
10
+ #
11
+ # Parameters::
12
+ # * *link* (String): The link to find the file from
13
+ # * *file* (String): The file from which the link is taken
14
+ # Result::
15
+ # * String: Resulting file
16
+ def link_to_file(link, file)
17
+ link_file = link.split('#').first
18
+ if link_file == ''
19
+ file
20
+ elsif link_file.start_with?('/')
21
+ link_file[1..-1]
22
+ else
23
+ File.expand_path("#{File.dirname(file)}/#{link_file}").gsub("#{Dir.pwd}/", '')
24
+ end
25
+ end
26
+
27
+ # Parse a Markdown file
28
+ #
29
+ # Parameters::
30
+ # * *file* (String): Markdown file to be parsed
31
+ def parse_md(file)
32
+ unless @info.key?(file)
33
+ puts "Parsing #{file}..."
34
+ content = File.read(file)
35
+ @info[file] = {
36
+ anchors: content.scan(/<a name="([^"]*)"><\/a>/).map { |(anchor)| anchor },
37
+ links: content.scan(/\[[^\]]*\]\(([^\)]*)\)/).map { |(link)| link }
38
+ }
39
+ # Parse linked files
40
+ @info[file][:links].each do |link|
41
+ puts "Found #{file} => #{link}"
42
+ end
43
+ @info[file][:links].each do |link|
44
+ unless link.start_with?('http')
45
+ linked_file = link_to_file(link, file)
46
+ parse_md(linked_file) if File.exist?(linked_file) && linked_file.end_with?('.md')
47
+ end
48
+ end
49
+ end
50
+ end
51
+
52
+ ARGV.each { |file| parse_md(file) }
53
+
54
+ # Check links
55
+ errors = []
56
+ @info.sort_by { |file, _info| file }.each do |file, info|
57
+ puts "= Links from #{file}"
58
+ info[:links].sort.uniq.each do |link|
59
+ puts "[#{
60
+ if link.start_with?('http')
61
+ '*'
62
+ else
63
+ anchor = link.split('#')[1]
64
+ linked_file = link_to_file(link, file)
65
+ if @info.key?(linked_file)
66
+ if anchor.nil?
67
+ '*'
68
+ elsif @info[linked_file][:anchors].include?(anchor)
69
+ '*'
70
+ else
71
+ errors << "[#{file} -> #{link}] - Destination has no anchor named #{anchor}"
72
+ ' '
73
+ end
74
+ elsif File.exist?(linked_file)
75
+ '*'
76
+ else
77
+ errors << "[#{file} -> #{link}] - Destination file does not exist"
78
+ ' '
79
+ end
80
+ end
81
+ }] #{link}"
82
+ end
83
+ end
84
+
85
+ puts
86
+ puts "#{errors.size} errors:"
87
+ errors.each do |error|
88
+ puts error
89
+ end