hybrid_platforms_conductor 32.3.6

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 (244) hide show
  1. checksums.yaml +7 -0
  2. data/bin/check-node +24 -0
  3. data/bin/deploy +12 -0
  4. data/bin/dump_nodes_json +12 -0
  5. data/bin/free_ips +23 -0
  6. data/bin/free_veids +17 -0
  7. data/bin/get_impacted_nodes +43 -0
  8. data/bin/last_deploys +56 -0
  9. data/bin/nodes_to_deploy +104 -0
  10. data/bin/report +10 -0
  11. data/bin/run +39 -0
  12. data/bin/setup +11 -0
  13. data/bin/ssh_config +14 -0
  14. data/bin/test +13 -0
  15. data/bin/topograph +54 -0
  16. data/lib/hybrid_platforms_conductor/action.rb +82 -0
  17. data/lib/hybrid_platforms_conductor/actions_executor.rb +307 -0
  18. data/lib/hybrid_platforms_conductor/bitbucket.rb +123 -0
  19. data/lib/hybrid_platforms_conductor/cmd_runner.rb +188 -0
  20. data/lib/hybrid_platforms_conductor/cmdb.rb +34 -0
  21. data/lib/hybrid_platforms_conductor/common_config_dsl/bitbucket.rb +78 -0
  22. data/lib/hybrid_platforms_conductor/common_config_dsl/confluence.rb +43 -0
  23. data/lib/hybrid_platforms_conductor/common_config_dsl/file_system_tests.rb +110 -0
  24. data/lib/hybrid_platforms_conductor/common_config_dsl/idempotence_tests.rb +38 -0
  25. data/lib/hybrid_platforms_conductor/config.rb +263 -0
  26. data/lib/hybrid_platforms_conductor/confluence.rb +119 -0
  27. data/lib/hybrid_platforms_conductor/connector.rb +84 -0
  28. data/lib/hybrid_platforms_conductor/credentials.rb +127 -0
  29. data/lib/hybrid_platforms_conductor/current_dir_monitor.rb +42 -0
  30. data/lib/hybrid_platforms_conductor/deployer.rb +598 -0
  31. data/lib/hybrid_platforms_conductor/executable.rb +145 -0
  32. data/lib/hybrid_platforms_conductor/hpc_plugins/action/bash.rb +44 -0
  33. data/lib/hybrid_platforms_conductor/hpc_plugins/action/interactive.rb +44 -0
  34. data/lib/hybrid_platforms_conductor/hpc_plugins/action/my_action.rb.sample +79 -0
  35. data/lib/hybrid_platforms_conductor/hpc_plugins/action/remote_bash.rb +63 -0
  36. data/lib/hybrid_platforms_conductor/hpc_plugins/action/ruby.rb +69 -0
  37. data/lib/hybrid_platforms_conductor/hpc_plugins/action/scp.rb +61 -0
  38. data/lib/hybrid_platforms_conductor/hpc_plugins/cmdb/config.rb +78 -0
  39. data/lib/hybrid_platforms_conductor/hpc_plugins/cmdb/host_ip.rb +104 -0
  40. data/lib/hybrid_platforms_conductor/hpc_plugins/cmdb/host_keys.rb +114 -0
  41. data/lib/hybrid_platforms_conductor/hpc_plugins/cmdb/my_cmdb.rb.sample +129 -0
  42. data/lib/hybrid_platforms_conductor/hpc_plugins/cmdb/platform_handlers.rb +66 -0
  43. data/lib/hybrid_platforms_conductor/hpc_plugins/connector/my_connector.rb.sample +156 -0
  44. data/lib/hybrid_platforms_conductor/hpc_plugins/connector/ssh.rb +702 -0
  45. data/lib/hybrid_platforms_conductor/hpc_plugins/platform_handler/platform_handler_plugin.rb.sample +292 -0
  46. data/lib/hybrid_platforms_conductor/hpc_plugins/provisioner/docker.rb +148 -0
  47. data/lib/hybrid_platforms_conductor/hpc_plugins/provisioner/my_provisioner.rb.sample +103 -0
  48. data/lib/hybrid_platforms_conductor/hpc_plugins/provisioner/podman.rb +125 -0
  49. data/lib/hybrid_platforms_conductor/hpc_plugins/provisioner/proxmox.rb +522 -0
  50. data/lib/hybrid_platforms_conductor/hpc_plugins/provisioner/proxmox/proxmox_waiter.rb +707 -0
  51. data/lib/hybrid_platforms_conductor/hpc_plugins/provisioner/proxmox/reserve_proxmox_container +122 -0
  52. data/lib/hybrid_platforms_conductor/hpc_plugins/report/confluence.rb +69 -0
  53. data/lib/hybrid_platforms_conductor/hpc_plugins/report/mediawiki.rb +164 -0
  54. data/lib/hybrid_platforms_conductor/hpc_plugins/report/my_report_plugin.rb.sample +88 -0
  55. data/lib/hybrid_platforms_conductor/hpc_plugins/report/stdout.rb +61 -0
  56. data/lib/hybrid_platforms_conductor/hpc_plugins/report/templates/confluence_inventory.html.erb +33 -0
  57. data/lib/hybrid_platforms_conductor/hpc_plugins/test/bitbucket_conf.rb +137 -0
  58. data/lib/hybrid_platforms_conductor/hpc_plugins/test/can_be_checked.rb +21 -0
  59. data/lib/hybrid_platforms_conductor/hpc_plugins/test/check_deploy_and_idempotence.rb +112 -0
  60. data/lib/hybrid_platforms_conductor/hpc_plugins/test/check_from_scratch.rb +35 -0
  61. data/lib/hybrid_platforms_conductor/hpc_plugins/test/connection.rb +28 -0
  62. data/lib/hybrid_platforms_conductor/hpc_plugins/test/deploy_freshness.rb +44 -0
  63. data/lib/hybrid_platforms_conductor/hpc_plugins/test/deploy_from_scratch.rb +36 -0
  64. data/lib/hybrid_platforms_conductor/hpc_plugins/test/deploy_removes_root_access.rb +49 -0
  65. data/lib/hybrid_platforms_conductor/hpc_plugins/test/divergence.rb +25 -0
  66. data/lib/hybrid_platforms_conductor/hpc_plugins/test/executables.rb +46 -0
  67. data/lib/hybrid_platforms_conductor/hpc_plugins/test/file_system.rb +45 -0
  68. data/lib/hybrid_platforms_conductor/hpc_plugins/test/file_system_hdfs.rb +45 -0
  69. data/lib/hybrid_platforms_conductor/hpc_plugins/test/hostname.rb +25 -0
  70. data/lib/hybrid_platforms_conductor/hpc_plugins/test/idempotence.rb +77 -0
  71. data/lib/hybrid_platforms_conductor/hpc_plugins/test/ip.rb +38 -0
  72. data/lib/hybrid_platforms_conductor/hpc_plugins/test/jenkins_ci_conf.rb +56 -0
  73. data/lib/hybrid_platforms_conductor/hpc_plugins/test/jenkins_ci_masters_ok.rb +54 -0
  74. data/lib/hybrid_platforms_conductor/hpc_plugins/test/linear_strategy.rb +47 -0
  75. data/lib/hybrid_platforms_conductor/hpc_plugins/test/local_users.rb +82 -0
  76. data/lib/hybrid_platforms_conductor/hpc_plugins/test/mounts.rb +120 -0
  77. data/lib/hybrid_platforms_conductor/hpc_plugins/test/my_test_plugin.rb.sample +143 -0
  78. data/lib/hybrid_platforms_conductor/hpc_plugins/test/orphan_files.rb +74 -0
  79. data/lib/hybrid_platforms_conductor/hpc_plugins/test/ports.rb +85 -0
  80. data/lib/hybrid_platforms_conductor/hpc_plugins/test/private_ips.rb +38 -0
  81. data/lib/hybrid_platforms_conductor/hpc_plugins/test/public_ips.rb +38 -0
  82. data/lib/hybrid_platforms_conductor/hpc_plugins/test/spectre-meltdown-checker.sh +1930 -0
  83. data/lib/hybrid_platforms_conductor/hpc_plugins/test/spectre.rb +56 -0
  84. data/lib/hybrid_platforms_conductor/hpc_plugins/test/veids.rb +31 -0
  85. data/lib/hybrid_platforms_conductor/hpc_plugins/test/vulnerabilities.rb +159 -0
  86. data/lib/hybrid_platforms_conductor/hpc_plugins/test_report/confluence.rb +122 -0
  87. data/lib/hybrid_platforms_conductor/hpc_plugins/test_report/my_test_report.rb.sample +48 -0
  88. data/lib/hybrid_platforms_conductor/hpc_plugins/test_report/stdout.rb +120 -0
  89. data/lib/hybrid_platforms_conductor/hpc_plugins/test_report/templates/_confluence_errors_status.html.erb +46 -0
  90. data/lib/hybrid_platforms_conductor/hpc_plugins/test_report/templates/_confluence_gauge.html.erb +49 -0
  91. data/lib/hybrid_platforms_conductor/hpc_plugins/test_report/templates/confluence.html.erb +242 -0
  92. data/lib/hybrid_platforms_conductor/io_router.rb +70 -0
  93. data/lib/hybrid_platforms_conductor/json_dumper.rb +88 -0
  94. data/lib/hybrid_platforms_conductor/logger_helpers.rb +319 -0
  95. data/lib/hybrid_platforms_conductor/mutex_dir +76 -0
  96. data/lib/hybrid_platforms_conductor/nodes_handler.rb +597 -0
  97. data/lib/hybrid_platforms_conductor/parallel_threads.rb +97 -0
  98. data/lib/hybrid_platforms_conductor/platform_handler.rb +188 -0
  99. data/lib/hybrid_platforms_conductor/platforms_handler.rb +118 -0
  100. data/lib/hybrid_platforms_conductor/plugin.rb +53 -0
  101. data/lib/hybrid_platforms_conductor/plugins.rb +101 -0
  102. data/lib/hybrid_platforms_conductor/provisioner.rb +181 -0
  103. data/lib/hybrid_platforms_conductor/report.rb +31 -0
  104. data/lib/hybrid_platforms_conductor/reports_handler.rb +84 -0
  105. data/lib/hybrid_platforms_conductor/services_handler.rb +274 -0
  106. data/lib/hybrid_platforms_conductor/test.rb +141 -0
  107. data/lib/hybrid_platforms_conductor/test_by_service.rb +22 -0
  108. data/lib/hybrid_platforms_conductor/test_report.rb +282 -0
  109. data/lib/hybrid_platforms_conductor/tests_runner.rb +590 -0
  110. data/lib/hybrid_platforms_conductor/thycotic.rb +92 -0
  111. data/lib/hybrid_platforms_conductor/topographer.rb +859 -0
  112. data/lib/hybrid_platforms_conductor/topographer/plugin.rb +20 -0
  113. data/lib/hybrid_platforms_conductor/topographer/plugins/graphviz.rb +127 -0
  114. data/lib/hybrid_platforms_conductor/topographer/plugins/json.rb +72 -0
  115. data/lib/hybrid_platforms_conductor/topographer/plugins/my_topographer_output_plugin.rb.sample +37 -0
  116. data/lib/hybrid_platforms_conductor/topographer/plugins/svg.rb +30 -0
  117. data/lib/hybrid_platforms_conductor/version.rb +5 -0
  118. data/spec/hybrid_platforms_conductor_test.rb +159 -0
  119. data/spec/hybrid_platforms_conductor_test/api/actions_executor/actions/bash_spec.rb +43 -0
  120. data/spec/hybrid_platforms_conductor_test/api/actions_executor/actions/interactive_spec.rb +18 -0
  121. data/spec/hybrid_platforms_conductor_test/api/actions_executor/actions/remote_bash_spec.rb +102 -0
  122. data/spec/hybrid_platforms_conductor_test/api/actions_executor/actions/ruby_spec.rb +108 -0
  123. data/spec/hybrid_platforms_conductor_test/api/actions_executor/actions/scp_spec.rb +79 -0
  124. data/spec/hybrid_platforms_conductor_test/api/actions_executor/actions_spec.rb +199 -0
  125. data/spec/hybrid_platforms_conductor_test/api/actions_executor/connection_spec.rb +212 -0
  126. data/spec/hybrid_platforms_conductor_test/api/actions_executor/connectors/ssh/cli_options_spec.rb +125 -0
  127. data/spec/hybrid_platforms_conductor_test/api/actions_executor/connectors/ssh/config_dsl_spec.rb +50 -0
  128. data/spec/hybrid_platforms_conductor_test/api/actions_executor/connectors/ssh/connectable_nodes_spec.rb +28 -0
  129. data/spec/hybrid_platforms_conductor_test/api/actions_executor/connectors/ssh/connections_spec.rb +448 -0
  130. data/spec/hybrid_platforms_conductor_test/api/actions_executor/connectors/ssh/global_helpers_spec.rb +313 -0
  131. data/spec/hybrid_platforms_conductor_test/api/actions_executor/connectors/ssh/node_helpers_spec.rb +32 -0
  132. data/spec/hybrid_platforms_conductor_test/api/actions_executor/connectors/ssh/remote_actions_spec.rb +134 -0
  133. data/spec/hybrid_platforms_conductor_test/api/actions_executor/logging_spec.rb +256 -0
  134. data/spec/hybrid_platforms_conductor_test/api/actions_executor/parallel_spec.rb +338 -0
  135. data/spec/hybrid_platforms_conductor_test/api/actions_executor/timeout_spec.rb +101 -0
  136. data/spec/hybrid_platforms_conductor_test/api/cmd_runner_spec.rb +165 -0
  137. data/spec/hybrid_platforms_conductor_test/api/config_spec.rb +238 -0
  138. data/spec/hybrid_platforms_conductor_test/api/deployer/check_spec.rb +9 -0
  139. data/spec/hybrid_platforms_conductor_test/api/deployer/deploy_spec.rb +243 -0
  140. data/spec/hybrid_platforms_conductor_test/api/deployer/parse_deploy_output_spec.rb +104 -0
  141. data/spec/hybrid_platforms_conductor_test/api/deployer/provisioner_spec.rb +131 -0
  142. data/spec/hybrid_platforms_conductor_test/api/deployer/provisioners/docker/Dockerfile +10 -0
  143. data/spec/hybrid_platforms_conductor_test/api/deployer/provisioners/docker_spec.rb +123 -0
  144. data/spec/hybrid_platforms_conductor_test/api/deployer/provisioners/podman_spec.rb +211 -0
  145. data/spec/hybrid_platforms_conductor_test/api/deployer/provisioners/proxmox/config_dsl_spec.rb +126 -0
  146. data/spec/hybrid_platforms_conductor_test/api/deployer/provisioners/proxmox/create_spec.rb +290 -0
  147. data/spec/hybrid_platforms_conductor_test/api/deployer/provisioners/proxmox/destroy_spec.rb +43 -0
  148. data/spec/hybrid_platforms_conductor_test/api/deployer/provisioners/proxmox/ip_spec.rb +60 -0
  149. data/spec/hybrid_platforms_conductor_test/api/deployer/provisioners/proxmox/proxmox.json +3 -0
  150. data/spec/hybrid_platforms_conductor_test/api/deployer/provisioners/proxmox/reserve_proxmox_container/destroy_vm_spec.rb +82 -0
  151. data/spec/hybrid_platforms_conductor_test/api/deployer/provisioners/proxmox/reserve_proxmox_container/expired_containers_spec.rb +786 -0
  152. data/spec/hybrid_platforms_conductor_test/api/deployer/provisioners/proxmox/reserve_proxmox_container/ips_assignment_spec.rb +112 -0
  153. data/spec/hybrid_platforms_conductor_test/api/deployer/provisioners/proxmox/reserve_proxmox_container/other_lxc_containers_resources_spec.rb +190 -0
  154. data/spec/hybrid_platforms_conductor_test/api/deployer/provisioners/proxmox/reserve_proxmox_container/pve_node_resources_spec.rb +200 -0
  155. data/spec/hybrid_platforms_conductor_test/api/deployer/provisioners/proxmox/reserve_proxmox_container/retries_spec.rb +35 -0
  156. data/spec/hybrid_platforms_conductor_test/api/deployer/provisioners/proxmox/reserve_proxmox_container/vm_ids_assignment_spec.rb +67 -0
  157. data/spec/hybrid_platforms_conductor_test/api/deployer/provisioners/proxmox/start_spec.rb +79 -0
  158. data/spec/hybrid_platforms_conductor_test/api/deployer/provisioners/proxmox/state_spec.rb +28 -0
  159. data/spec/hybrid_platforms_conductor_test/api/deployer/provisioners/proxmox/stop_spec.rb +41 -0
  160. data/spec/hybrid_platforms_conductor_test/api/nodes_handler/cmdbs/config_spec.rb +33 -0
  161. data/spec/hybrid_platforms_conductor_test/api/nodes_handler/cmdbs/host_ip_spec.rb +64 -0
  162. data/spec/hybrid_platforms_conductor_test/api/nodes_handler/cmdbs/host_keys_spec.rb +133 -0
  163. data/spec/hybrid_platforms_conductor_test/api/nodes_handler/cmdbs/platform_handlers_spec.rb +19 -0
  164. data/spec/hybrid_platforms_conductor_test/api/nodes_handler/cmdbs_plugins_api_spec.rb +446 -0
  165. data/spec/hybrid_platforms_conductor_test/api/nodes_handler/common_spec.rb +127 -0
  166. data/spec/hybrid_platforms_conductor_test/api/nodes_handler/git_diff_impacts_spec.rb +318 -0
  167. data/spec/hybrid_platforms_conductor_test/api/nodes_handler/nodes_selectors_spec.rb +132 -0
  168. data/spec/hybrid_platforms_conductor_test/api/nodes_handler/platform_handlers_plugins_api_spec.rb +60 -0
  169. data/spec/hybrid_platforms_conductor_test/api/nodes_handler/several_platforms_spec.rb +58 -0
  170. data/spec/hybrid_platforms_conductor_test/api/platform_handler_spec.rb +97 -0
  171. data/spec/hybrid_platforms_conductor_test/api/platforms_handler_spec.rb +104 -0
  172. data/spec/hybrid_platforms_conductor_test/api/plugins_spec.rb +243 -0
  173. data/spec/hybrid_platforms_conductor_test/api/reports_handler_spec.rb +44 -0
  174. data/spec/hybrid_platforms_conductor_test/api/services_handler/actions_to_deploy_spec.rb +121 -0
  175. data/spec/hybrid_platforms_conductor_test/api/services_handler/deploy_allowed_spec.rb +142 -0
  176. data/spec/hybrid_platforms_conductor_test/api/services_handler/log_info_spec.rb +101 -0
  177. data/spec/hybrid_platforms_conductor_test/api/services_handler/package_spec.rb +388 -0
  178. data/spec/hybrid_platforms_conductor_test/api/services_handler/parse_deploy_output_spec.rb +274 -0
  179. data/spec/hybrid_platforms_conductor_test/api/services_handler/prepare_for_deploy_spec.rb +264 -0
  180. data/spec/hybrid_platforms_conductor_test/api/tests_runner/common_spec.rb +194 -0
  181. data/spec/hybrid_platforms_conductor_test/api/tests_runner/global_spec.rb +37 -0
  182. data/spec/hybrid_platforms_conductor_test/api/tests_runner/node_check_spec.rb +194 -0
  183. data/spec/hybrid_platforms_conductor_test/api/tests_runner/node_spec.rb +137 -0
  184. data/spec/hybrid_platforms_conductor_test/api/tests_runner/node_ssh_spec.rb +257 -0
  185. data/spec/hybrid_platforms_conductor_test/api/tests_runner/platform_spec.rb +110 -0
  186. data/spec/hybrid_platforms_conductor_test/api/tests_runner/reports_spec.rb +367 -0
  187. data/spec/hybrid_platforms_conductor_test/api/tests_runner/test_plugins/bitbucket_conf_spec.rb +111 -0
  188. data/spec/hybrid_platforms_conductor_test/api/tests_runner/test_reports_plugins/confluence_spec.rb +29 -0
  189. data/spec/hybrid_platforms_conductor_test/cmdb_plugins/test_cmdb.rb +166 -0
  190. data/spec/hybrid_platforms_conductor_test/cmdb_plugins/test_cmdb2.rb +93 -0
  191. data/spec/hybrid_platforms_conductor_test/cmdb_plugins/test_cmdb_others.rb +60 -0
  192. data/spec/hybrid_platforms_conductor_test/cmdb_plugins/test_cmdb_others2.rb +58 -0
  193. data/spec/hybrid_platforms_conductor_test/executables/check-node_spec.rb +35 -0
  194. data/spec/hybrid_platforms_conductor_test/executables/deploy_spec.rb +35 -0
  195. data/spec/hybrid_platforms_conductor_test/executables/get_impacted_nodes_spec.rb +158 -0
  196. data/spec/hybrid_platforms_conductor_test/executables/last_deploys_spec.rb +173 -0
  197. data/spec/hybrid_platforms_conductor_test/executables/nodes_to_deploy_spec.rb +283 -0
  198. data/spec/hybrid_platforms_conductor_test/executables/options/actions_executor_spec.rb +28 -0
  199. data/spec/hybrid_platforms_conductor_test/executables/options/cmd_runner_spec.rb +28 -0
  200. data/spec/hybrid_platforms_conductor_test/executables/options/common_spec.rb +67 -0
  201. data/spec/hybrid_platforms_conductor_test/executables/options/deployer_spec.rb +251 -0
  202. data/spec/hybrid_platforms_conductor_test/executables/options/nodes_handler_spec.rb +111 -0
  203. data/spec/hybrid_platforms_conductor_test/executables/options/nodes_selectors_spec.rb +71 -0
  204. data/spec/hybrid_platforms_conductor_test/executables/options/reports_handler_spec.rb +54 -0
  205. data/spec/hybrid_platforms_conductor_test/executables/options/tests_runner_spec.rb +139 -0
  206. data/spec/hybrid_platforms_conductor_test/executables/report_spec.rb +60 -0
  207. data/spec/hybrid_platforms_conductor_test/executables/run_spec.rb +173 -0
  208. data/spec/hybrid_platforms_conductor_test/executables/ssh_config_spec.rb +35 -0
  209. data/spec/hybrid_platforms_conductor_test/executables/test_spec.rb +41 -0
  210. data/spec/hybrid_platforms_conductor_test/helpers/actions_executor_helpers.rb +98 -0
  211. data/spec/hybrid_platforms_conductor_test/helpers/cmd_runner_helpers.rb +92 -0
  212. data/spec/hybrid_platforms_conductor_test/helpers/cmdb_helpers.rb +37 -0
  213. data/spec/hybrid_platforms_conductor_test/helpers/config_helpers.rb +20 -0
  214. data/spec/hybrid_platforms_conductor_test/helpers/connector_ssh_helpers.rb +130 -0
  215. data/spec/hybrid_platforms_conductor_test/helpers/deployer_helpers.rb +149 -0
  216. data/spec/hybrid_platforms_conductor_test/helpers/deployer_test_helpers.rb +812 -0
  217. data/spec/hybrid_platforms_conductor_test/helpers/executables_helpers.rb +96 -0
  218. data/spec/hybrid_platforms_conductor_test/helpers/nodes_handler_helpers.rb +20 -0
  219. data/spec/hybrid_platforms_conductor_test/helpers/platform_handler_helpers.rb +35 -0
  220. data/spec/hybrid_platforms_conductor_test/helpers/platforms_handler_helpers.rb +127 -0
  221. data/spec/hybrid_platforms_conductor_test/helpers/plugins_helpers.rb +48 -0
  222. data/spec/hybrid_platforms_conductor_test/helpers/provisioner_proxmox_helpers.rb +789 -0
  223. data/spec/hybrid_platforms_conductor_test/helpers/reports_handler_helpers.rb +29 -0
  224. data/spec/hybrid_platforms_conductor_test/helpers/services_handler_helpers.rb +20 -0
  225. data/spec/hybrid_platforms_conductor_test/helpers/tests_runner_helpers.rb +38 -0
  226. data/spec/hybrid_platforms_conductor_test/mocked_lib/my_test_gem/hpc_plugins/test_plugin_type/test_plugin_id1.rb +22 -0
  227. data/spec/hybrid_platforms_conductor_test/mocked_lib/my_test_gem/hpc_plugins/test_plugin_type/test_plugin_id2.rb +22 -0
  228. data/spec/hybrid_platforms_conductor_test/mocked_lib/my_test_gem2/sub_dir/hpc_plugins/test_plugin_type/test_plugin_id3.rb +26 -0
  229. data/spec/hybrid_platforms_conductor_test/mocked_lib/my_test_gem2/sub_dir/hpc_plugins/test_plugin_type2/test_plugin_id4.rb +26 -0
  230. data/spec/hybrid_platforms_conductor_test/platform_handler_plugins/test.rb +225 -0
  231. data/spec/hybrid_platforms_conductor_test/platform_handler_plugins/test2.rb +11 -0
  232. data/spec/hybrid_platforms_conductor_test/report_plugin.rb +35 -0
  233. data/spec/hybrid_platforms_conductor_test/test_action.rb +66 -0
  234. data/spec/hybrid_platforms_conductor_test/test_connector.rb +151 -0
  235. data/spec/hybrid_platforms_conductor_test/test_plugins/global.rb +30 -0
  236. data/spec/hybrid_platforms_conductor_test/test_plugins/node.rb +53 -0
  237. data/spec/hybrid_platforms_conductor_test/test_plugins/node_check.rb +47 -0
  238. data/spec/hybrid_platforms_conductor_test/test_plugins/node_ssh.rb +42 -0
  239. data/spec/hybrid_platforms_conductor_test/test_plugins/platform.rb +50 -0
  240. data/spec/hybrid_platforms_conductor_test/test_plugins/several_checks.rb +50 -0
  241. data/spec/hybrid_platforms_conductor_test/test_provisioner.rb +95 -0
  242. data/spec/hybrid_platforms_conductor_test/tests_report_plugin.rb +49 -0
  243. data/spec/spec_helper.rb +111 -0
  244. metadata +566 -0
@@ -0,0 +1,96 @@
1
+ module HybridPlatformsConductorTest
2
+
3
+ module Helpers
4
+
5
+ module ExecutablesHelpers
6
+
7
+ # Run an executable and get its output
8
+ #
9
+ # Parameters::
10
+ # * *executable* (String): Executable name
11
+ # * *args* (Array<String>): Arguments to give the executable
12
+ # Result::
13
+ # * Integer: Exit code
14
+ # * String: stdout
15
+ # * String: stderr
16
+ def run(executable, *args)
17
+ stdout_file = "#{Dir.tmpdir}/hpc_test/run.stdout"
18
+ stderr_file = "#{Dir.tmpdir}/hpc_test/run.stderr"
19
+ File.open(stdout_file, 'w') { |f| f.truncate(0) }
20
+ File.open(stderr_file, 'w') { |f| f.truncate(0) }
21
+ logger_stdout = Logger.new(stdout_file, level: :info)
22
+ logger_stderr = Logger.new(stderr_file, level: :info)
23
+ # Mock the Executable creation to redirect stdout and stderr correctly
24
+ expect(HybridPlatformsConductor::Executable).to receive(:new).once.and_wrap_original do |original_method,
25
+ check_options: true,
26
+ nodes_selection_options: true,
27
+ parallel_options: true,
28
+ timeout_options: true,
29
+ deploy_options: true,
30
+ original_logger: Logger.new(STDOUT, level: :info),
31
+ original_logger_stderr: Logger.new(STDERR, level: :info),
32
+ &opts_block|
33
+ original_method.call(
34
+ check_options: check_options,
35
+ nodes_selection_options: nodes_selection_options,
36
+ parallel_options: parallel_options,
37
+ timeout_options: timeout_options,
38
+ deploy_options: deploy_options,
39
+ logger: logger_stdout,
40
+ logger_stderr: logger_stderr,
41
+ &opts_block
42
+ )
43
+ end
44
+ # Get a simple list of all the components that should be mocked when being used by the executables, per class to be mocked.
45
+ components_to_mock = {
46
+ HybridPlatformsConductor::ActionsExecutor => test_actions_executor,
47
+ HybridPlatformsConductor::CmdRunner => test_cmd_runner,
48
+ HybridPlatformsConductor::Config => test_config,
49
+ HybridPlatformsConductor::Deployer => test_deployer,
50
+ HybridPlatformsConductor::NodesHandler => test_nodes_handler,
51
+ HybridPlatformsConductor::PlatformsHandler => test_platforms_handler,
52
+ HybridPlatformsConductor::ReportsHandler => test_reports_handler,
53
+ HybridPlatformsConductor::TestsRunner => test_tests_runner
54
+ }
55
+ # Make sure the tested components use the same loggers as the executable.
56
+ components_to_mock.values.each do |component|
57
+ component.stdout_device = stdout_file
58
+ component.stderr_device = stderr_file
59
+ end
60
+ # Make sure that when the executable creates components it uses ours
61
+ components_to_mock.each do |component_class, component|
62
+ allow(component_class).to receive(:new).once { component }
63
+ end
64
+ # Run the executable
65
+ args.concat(['--debug']) if ENV['TEST_DEBUG'] == '1'
66
+ ARGV.replace(args)
67
+ old_0 = $0
68
+ $0 = executable
69
+ begin
70
+ exit_code = nil
71
+ begin
72
+ load "#{__dir__}/../../../bin/#{executable}"
73
+ exit_code = 0
74
+ rescue SystemExit
75
+ exit_code = $!.status
76
+ end
77
+ ensure
78
+ $0 = old_0
79
+ end
80
+ stdout = File.read(stdout_file)
81
+ stderr = File.read(stderr_file)
82
+ if ENV['TEST_DEBUG'] == '1'
83
+ puts "> #{executable} #{args.join(' ')}"
84
+ puts '===== STDOUT ====='
85
+ puts stdout
86
+ puts '===== STDERR ====='
87
+ puts stderr
88
+ end
89
+ [exit_code, stdout, stderr]
90
+ end
91
+
92
+ end
93
+
94
+ end
95
+
96
+ end
@@ -0,0 +1,20 @@
1
+ module HybridPlatformsConductorTest
2
+
3
+ module Helpers
4
+
5
+ module NodesHandlerHelpers
6
+
7
+ # Get a test NodesHandler
8
+ #
9
+ # Result::
10
+ # * NodesHandler: NodesHandler on which we can do testing
11
+ def test_nodes_handler
12
+ @nodes_handler = HybridPlatformsConductor::NodesHandler.new logger: logger, logger_stderr: logger, config: test_config, cmd_runner: test_cmd_runner, platforms_handler: test_platforms_handler unless @nodes_handler
13
+ @nodes_handler
14
+ end
15
+
16
+ end
17
+
18
+ end
19
+
20
+ end
@@ -0,0 +1,35 @@
1
+ module HybridPlatformsConductorTest
2
+
3
+ module Helpers
4
+
5
+ module PlatformHandlerHelpers
6
+
7
+ # Return the test platforms info, used by the test PlatformHandler
8
+ #
9
+ # Result::
10
+ # * *Hash<String, Hash>: Platforms info, per platform name (see TestPlatformHandler#platforms_info for details)
11
+ def test_platforms_info
12
+ HybridPlatformsConductorTest::PlatformHandlerPlugins::Test.platforms_info
13
+ end
14
+
15
+ # Set the test platforms info, used by the test PlatformHandler
16
+ #
17
+ # Parameters::
18
+ # * *platforms_info* (Hash<String, Hash>): Platforms info, per platform name (see TestPlatformHandler#platforms_info for details)
19
+ def test_platforms_info=(platforms_info)
20
+ HybridPlatformsConductorTest::PlatformHandlerPlugins::Test.platforms_info = platforms_info
21
+ end
22
+
23
+ # Register the given platform handler classes
24
+ #
25
+ # Parameters::
26
+ # * *platform_handlers* (Hash<Symbol,Class>): The platform handler classes, per platform type name
27
+ def register_platform_handlers(platform_handlers)
28
+ register_plugins(:platform_handler, platform_handlers)
29
+ end
30
+
31
+ end
32
+
33
+ end
34
+
35
+ end
@@ -0,0 +1,127 @@
1
+ module HybridPlatformsConductorTest
2
+
3
+ module Helpers
4
+
5
+ module PlatformsHandlerHelpers
6
+
7
+ # Setup several test repositories.
8
+ # Clean-up at the end.
9
+ #
10
+ # Parameters::
11
+ # * *names* (Array<String>): Name of the directories to be used [default = []]
12
+ # * *as_git* (Boolean): Do we initialize those repositories as Git repositories? [default: false]
13
+ # * Proc: Code called with the repositories created.
14
+ # * Parameters::
15
+ # * *repositories* (Hash<String,String>): Path to the repositories, per repository name
16
+ def with_repositories(names = [], as_git: false)
17
+ repositories = Hash[names.map { |name| [name, "#{Dir.tmpdir}/hpc_test/#{name}"] }]
18
+ repositories.values.each do |dir|
19
+ FileUtils.rm_rf dir
20
+ FileUtils.mkdir_p dir
21
+ if as_git
22
+ git = Git.init(dir)
23
+ FileUtils.touch("#{dir}/test_file")
24
+ git.add('test_file')
25
+ git.config('user.name', 'Thats Me')
26
+ git.config('user.email', 'email@email.com')
27
+ git.commit('Test commit')
28
+ git.add_remote('origin', "https://my_remote.com/path/to/#{File.basename(dir)}.git")
29
+ end
30
+ end
31
+ begin
32
+ yield repositories
33
+ ensure
34
+ repositories.values.each do |dir|
35
+ FileUtils.rm_rf dir
36
+ end
37
+ end
38
+ end
39
+
40
+ # Setup a test repository.
41
+ # Clean-up at the end.
42
+ #
43
+ # Parameters::
44
+ # * *name* (String): Name of the directory to be used [default = 'platform_repo']
45
+ # * *as_git* (Boolean): Do we initialize those repositories as Git repositories? [default: false]
46
+ # * Proc: Code called with the repository created.
47
+ # * Parameters::
48
+ # * *repository* (String): Path to the repository
49
+ def with_repository(name = 'platform_repo', as_git: false)
50
+ with_repositories([name], as_git: as_git) do |repositories|
51
+ yield repositories[name]
52
+ end
53
+ end
54
+
55
+ # Setup a hpc_config.rb with a given content and call code when it's ready.
56
+ # Automatically sets the hpc_platforms env variable so that processes can then use it.
57
+ # Clean-up at the end.
58
+ #
59
+ # Parameters::
60
+ # * *content* (String): hpc_config.rb's content
61
+ # * Proc: Code called with the platforms.rb file created.
62
+ # * Parameters::
63
+ # * *hybrid_platforms_dir* (String): The hybrid-platforms directory
64
+ def with_platforms(content)
65
+ with_repository('hybrid-platforms') do |hybrid_platforms_dir|
66
+ File.write("#{hybrid_platforms_dir}/hpc_config.rb", content)
67
+ ENV['hpc_platforms'] = hybrid_platforms_dir
68
+ yield hybrid_platforms_dir
69
+ end
70
+ end
71
+
72
+ # Instantiate a test environment with several test platforms, ready to run tests
73
+ # Clean-up at the end.
74
+ #
75
+ # Parameters::
76
+ # * *platforms_info* (Hash<String,Object>): Platforms info for the test platform [default = {}]
77
+ # * *as_git* (Boolean): Do we initialize those repositories as Git repositories? [default = false]
78
+ # * *additional_platforms_content* (String): Additional platforms content to be added [default = '']
79
+ # * Proc: Code called with the environment ready
80
+ # * Parameters::
81
+ # * *repositories* (Hash<String,String>): Path to the repositories, per repository name
82
+ def with_test_platforms(platforms_info = {}, as_git = false, additional_platforms_content = '')
83
+ with_repositories(platforms_info.keys, as_git: as_git) do |repositories|
84
+ platform_types = []
85
+ with_platforms(repositories.map do |platform, dir|
86
+ platform_type = platforms_info[platform].key?(:platform_type) ? platforms_info[platform][:platform_type] : :test
87
+ platform_types << platform_type unless platform_types.include?(platform_type)
88
+ "#{platform_type}_platform path: '#{dir}'"
89
+ end.join("\n") + "\n#{additional_platforms_content}") do
90
+ register_platform_handlers(Hash[platform_types.map { |platform_type| [platform_type, HybridPlatformsConductorTest::PlatformHandlerPlugins.const_get(platform_type.to_s.split('_').collect(&:capitalize).join.to_sym)] }])
91
+ self.test_platforms_info = platforms_info
92
+ yield repositories
93
+ end
94
+ end
95
+ end
96
+
97
+ # Instantiate a test environment with a test platform handler, ready to run tests
98
+ # Clean-up at the end.
99
+ #
100
+ # Parameters::
101
+ # * *platform_info* (Hash<Symbol,Object>): Platform info for the test platform [default = {}]
102
+ # * *as_git* (Boolean): Do we initialize those repositories as Git repositories? [default = false]
103
+ # * *additional_platforms_content* (String): Additional platforms content to be added [default = '']
104
+ # * Proc: Code called with the environment ready
105
+ # * Parameters::
106
+ # * *repository* (String): Path to the repository
107
+ def with_test_platform(platform_info = {}, as_git = false, additional_platforms_content = '')
108
+ platform_name = as_git ? 'my_remote_platform' : 'platform'
109
+ with_test_platforms({ platform_name => platform_info }, as_git, additional_platforms_content) do |repositories|
110
+ yield repositories[platform_name]
111
+ end
112
+ end
113
+
114
+ # Get a test PlatformsHandler
115
+ #
116
+ # Result::
117
+ # * PlatformsHandler: PlatformsHandler on which we can do testing
118
+ def test_platforms_handler
119
+ @platforms_handler = HybridPlatformsConductor::PlatformsHandler.new logger: logger, logger_stderr: logger, config: test_config, cmd_runner: test_cmd_runner unless @platforms_handler
120
+ @platforms_handler
121
+ end
122
+
123
+ end
124
+
125
+ end
126
+
127
+ end
@@ -0,0 +1,48 @@
1
+ module HybridPlatformsConductorTest
2
+
3
+ module Helpers
4
+
5
+ module PluginsHelpers
6
+
7
+ # Register given plugins for a given plugin type
8
+ #
9
+ # Parameters::
10
+ # * *plugin_type* (Symbol): The plugin type
11
+ # * *plugins* (Hash<Symbol,Class>): The plugin classes, per plugin ID
12
+ # * *replace* (Boolean): Should we replace the plugins with the mocked ones, or only add them? [default: true]
13
+ def register_plugins(plugin_type, plugins, replace: true)
14
+ unless defined?(@plugins_to_mock)
15
+ # First time we invoke it: mock the call to Plugins
16
+ # List of plugins information to mock, per plugin type
17
+ # * *plugins* (Hash<Symbol,Class>): The mocked plugins
18
+ # * *replace* (Boolean): Should we replace the plugins or add them?
19
+ # Hash< Symbol, Hash<Symbol, Object> >
20
+ @plugins_to_mock = {}
21
+ allow(HybridPlatformsConductor::Plugins).to receive(:new).and_wrap_original do |original_new, plugins_type, init_plugin: nil, parse_gems: true, logger: Logger.new(STDOUT), logger_stderr: Logger.new(STDERR)|
22
+ # If this plugin type is to be mocked, then don't parse gems and provide the mocked plugins instead
23
+ mocked_plugins = original_new.call(
24
+ plugins_type,
25
+ init_plugin: init_plugin,
26
+ parse_gems: @plugins_to_mock.key?(plugins_type) && @plugins_to_mock[plugins_type][:replace] ? false : parse_gems,
27
+ logger: logger,
28
+ logger_stderr: logger_stderr
29
+ )
30
+ if @plugins_to_mock.key?(plugins_type)
31
+ @plugins_to_mock[plugins_type][:plugins].each do |plugin_id, plugin_class|
32
+ mocked_plugins[plugin_id] = plugin_class
33
+ end
34
+ end
35
+ mocked_plugins
36
+ end
37
+ end
38
+ @plugins_to_mock[plugin_type] = {
39
+ plugins: plugins,
40
+ replace: replace
41
+ }
42
+ end
43
+
44
+ end
45
+
46
+ end
47
+
48
+ end
@@ -0,0 +1,789 @@
1
+ module HybridPlatformsConductorTest
2
+
3
+ module Helpers
4
+
5
+ module ProvisionerProxmoxHelpers
6
+
7
+ # Setup a test platform for Proxmox tests
8
+ #
9
+ # Parameters::
10
+ # * *node_metadata* (Hash<Symbol,Object>): Extra node metadata [default: {}]
11
+ # * *environment* (String): Environment to be used [default: 'test']
12
+ # * Proc: Code called when everything is setup
13
+ # * Parameters::
14
+ # * *instance* (Provisioner): A new Provisioner instance targeting the Proxmox instance
15
+ # * *repository* (String): The platforms' repository
16
+ def with_test_proxmox_platform(node_metadata: {}, environment: 'test')
17
+ with_repository('platform') do |repository|
18
+ os_image_path = "#{repository}/os_image"
19
+ FileUtils.mkdir_p os_image_path
20
+ FileUtils.cp "#{__dir__}/../api/deployer/provisioners/proxmox/proxmox.json", "#{os_image_path}/proxmox.json"
21
+ with_platforms("
22
+ os_image :test_image, '#{os_image_path}'
23
+ test_platform path: '#{repository}'
24
+ proxmox(
25
+ api_url: 'https://my-proxmox.my-domain.com:8006',
26
+ sync_node: 'node',
27
+ test_config: {
28
+ pve_nodes: ['pve_node_name'],
29
+ vm_ips_list: %w[
30
+ 192.168.0.100
31
+ 192.168.0.101
32
+ ],
33
+ vm_ids_range: [1000, 1100],
34
+ coeff_ram_consumption: 10,
35
+ coeff_disk_consumption: 1,
36
+ expiration_period_secs: 24 * 60 * 60,
37
+ limits: {
38
+ nbr_vms_max: 5,
39
+ cpu_loads_thresholds: [10, 10, 10],
40
+ ram_percent_used_max: 0.75,
41
+ disk_percent_used_max: 0.75
42
+ }
43
+ },
44
+ vm_config: {
45
+ vm_dns_servers: ['8.8.8.8'],
46
+ vm_search_domain: 'my-domain.com',
47
+ vm_gateway: '192.168.0.1'
48
+ }
49
+ )
50
+ ") do
51
+ register_platform_handlers test: HybridPlatformsConductorTest::PlatformHandlerPlugins::Test
52
+ self.test_platforms_info = { 'platform' => {
53
+ nodes: { 'node' => { meta: { host_ip: '192.168.42.42', image: 'test_image' }.merge(node_metadata) } }
54
+ } }
55
+ instance = HybridPlatformsConductor::HpcPlugins::Provisioner::Proxmox.new(
56
+ 'node',
57
+ environment: environment,
58
+ logger: logger,
59
+ logger_stderr: logger,
60
+ config: test_config,
61
+ cmd_runner: test_cmd_runner,
62
+ nodes_handler: test_nodes_handler,
63
+ actions_executor: test_actions_executor
64
+ )
65
+ yield instance, repository
66
+ end
67
+ end
68
+ end
69
+
70
+ # Get a mocking code corresponding to a call to Proxmox.new.
71
+ # This code should mock Proxmox getting info about the existing nodes
72
+ #
73
+ # Parameters::
74
+ # * *proxmox_user* (String or nil): Proxmox user used to connect to Proxmox API [default: nil]
75
+ # * *proxmox_password* (String or nil): Proxmox password used to connect to Proxmox API [default: nil]
76
+ # * *proxmox_realm* (String or nil): Proxmox realm used to connect to Proxmox API [default: 'pam']
77
+ # * *nodes_info* (Array<Hash>): Nodes info returned by the Proxmox API [default: []]
78
+ # * *extra_expects* (Proc or nil): Code called for additional expectations on the proxmox instance, or nil if none [default: nil]
79
+ # * Parameters::
80
+ # * *proxmox* (Double): The mocked Proxmox instance
81
+ # Result::
82
+ # * Proc: Code called in place of Proxmox.new. Signature is the same as Proxmox.new.
83
+ def mock_proxmox_to_get_nodes_info(proxmox_user: nil, proxmox_password: nil, proxmox_realm: 'pam', nodes_info: [], extra_expects: nil)
84
+ proc do |url, pve_node, user, password, realm, options|
85
+ expect(url).to eq 'https://my-proxmox.my-domain.com:8006/api2/json/'
86
+ expect(pve_node).to eq 'my-proxmox'
87
+ expect(user).to eq proxmox_user
88
+ expect(password).to eq proxmox_password
89
+ expect(realm).to eq proxmox_realm
90
+ expect(options[:verify_ssl]).to eq false
91
+ proxmox = double 'Proxmox info instance'
92
+ # Mock initialization
93
+ expect(proxmox).to receive(:logger=) do
94
+ # Nothing
95
+ end
96
+ expect(proxmox).to receive(:logger_stderr=) do
97
+ # Nothing
98
+ end
99
+ # Mock checking existing nodes
100
+ expect(proxmox).to receive(:get).with('nodes') do
101
+ nodes_info
102
+ end
103
+ extra_expects.call(proxmox) unless extra_expects.nil?
104
+ proxmox
105
+ end
106
+ end
107
+
108
+ # Get a mocking code corresponding to a call to Proxmox.new.
109
+ # This code should mock Proxmox starting a node
110
+ #
111
+ # Parameters::
112
+ # * *proxmox_user* (String or nil): Proxmox user used to connect to Proxmox API [default: nil]
113
+ # * *proxmox_password* (String or nil): Proxmox password used to connect to Proxmox API [default: nil]
114
+ # * *task_name* (String): Proxmox start task name [default: 'UPID:pve_node_name:0000A504:6DEABF24:5F44669B:start::root@pam:']
115
+ # * *task_status* (String or nil): Proxmox start task status, or nil if no task status query is to be expected [default: 'OK']
116
+ # * *nbr_api_errors* (Integer): Number of API errors 500 to mock before getting a successful query [defaults: 0]
117
+ # Result::
118
+ # * Proc: Code called in place of Proxmox.new. Signature is the same as Proxmox.new.
119
+ def mock_proxmox_to_start_node(
120
+ proxmox_user: nil,
121
+ proxmox_password: nil,
122
+ task_name: 'UPID:pve_node_name:0000A504:6DEABF24:5F44669B:start::root@pam:',
123
+ task_status: 'OK',
124
+ nbr_api_errors: 0
125
+ )
126
+ proc do |url, pve_node, user, password, realm, options|
127
+ expect(url).to eq 'https://my-proxmox.my-domain.com:8006/api2/json/'
128
+ expect(pve_node).to eq 'my-proxmox'
129
+ expect(user).to eq proxmox_user
130
+ expect(password).to eq proxmox_password
131
+ expect(realm).to eq 'pam'
132
+ expect(options[:verify_ssl]).to eq false
133
+ proxmox = double 'Proxmox create instance'
134
+ # Mock initialization
135
+ expect(proxmox).to receive(:logger=) do
136
+ # Nothing
137
+ end
138
+ expect(proxmox).to receive(:logger_stderr=) do
139
+ # Nothing
140
+ end
141
+ # Mock start a container
142
+ idx_try = 0
143
+ expect(proxmox).to receive(:post).exactly(nbr_api_errors + (task_status.nil? ? 0 : 1)).times.with('nodes/pve_node_name/lxc/1024/status/start') do
144
+ idx_try += 1
145
+ idx_try <= nbr_api_errors ? 'NOK: error code = 500' : task_name
146
+ end
147
+ # Mock checking task status
148
+ unless task_status.nil?
149
+ # Mock checking task status
150
+ expect(proxmox).to receive(:get).with("nodes/pve_node_name/tasks/#{task_name}/status") do
151
+ { 'status' => task_status }
152
+ end
153
+ end
154
+ proxmox
155
+ end
156
+ end
157
+
158
+ # Get a mocking code corresponding to a call to Proxmox.new.
159
+ # This code should mock Proxmox stopping a node
160
+ #
161
+ # Parameters::
162
+ # * *proxmox_user* (String or nil): Proxmox user used to connect to Proxmox API [default: nil]
163
+ # * *proxmox_password* (String or nil): Proxmox password used to connect to Proxmox API [default: nil]
164
+ # * *task_status* (String): Proxmox stop task status [default: 'OK']
165
+ # Result::
166
+ # * Proc: Code called in place of Proxmox.new. Signature is the same as Proxmox.new.
167
+ def mock_proxmox_to_stop_node(
168
+ proxmox_user: nil,
169
+ proxmox_password: nil,
170
+ task_status: 'OK'
171
+ )
172
+ proc do |url, pve_node, user, password, realm, options|
173
+ expect(url).to eq 'https://my-proxmox.my-domain.com:8006/api2/json/'
174
+ expect(pve_node).to eq 'my-proxmox'
175
+ expect(user).to eq proxmox_user
176
+ expect(password).to eq proxmox_password
177
+ expect(realm).to eq 'pam'
178
+ expect(options[:verify_ssl]).to eq false
179
+ proxmox = double 'Proxmox create instance'
180
+ # Mock initialization
181
+ expect(proxmox).to receive(:logger=) do
182
+ # Nothing
183
+ end
184
+ expect(proxmox).to receive(:logger_stderr=) do
185
+ # Nothing
186
+ end
187
+ # Mock start a container
188
+ expect(proxmox).to receive(:post).with('nodes/pve_node_name/lxc/1024/status/stop') do
189
+ 'UPID:pve_node_name:0000A504:6DEABF24:5F44669B:stop::root@pam:'
190
+ end
191
+ # Mock checking task status
192
+ expect(proxmox).to receive(:get).with('nodes/pve_node_name/tasks/UPID:pve_node_name:0000A504:6DEABF24:5F44669B:stop::root@pam:/status') do
193
+ { 'status' => task_status }
194
+ end
195
+ proxmox
196
+ end
197
+ end
198
+
199
+ # Get a mocking code corresponding to a call to Proxmox.new.
200
+ # This code should mock Proxmox destroying a node
201
+ #
202
+ # Parameters::
203
+ # * *proxmox_user* (String or nil): Proxmox user used to connect to Proxmox API [default: nil]
204
+ # * *proxmox_password* (String or nil): Proxmox password used to connect to Proxmox API [default: nil]
205
+ # * *task_status* (String): Proxmox destroy task status [default: 'OK']
206
+ # Result::
207
+ # * Proc: Code called in place of Proxmox.new. Signature is the same as Proxmox.new.
208
+ def mock_proxmox_to_destroy_node(
209
+ proxmox_user: nil,
210
+ proxmox_password: nil,
211
+ task_status: 'OK'
212
+ )
213
+ proc do |url, pve_node, user, password, realm, options|
214
+ expect(url).to eq 'https://my-proxmox.my-domain.com:8006/api2/json/'
215
+ expect(pve_node).to eq 'my-proxmox'
216
+ expect(user).to eq proxmox_user
217
+ expect(password).to eq proxmox_password
218
+ expect(realm).to eq 'pam'
219
+ expect(options[:verify_ssl]).to eq false
220
+ proxmox = double 'Proxmox create instance'
221
+ # Mock initialization
222
+ expect(proxmox).to receive(:logger=) do
223
+ # Nothing
224
+ end
225
+ expect(proxmox).to receive(:logger_stderr=) do
226
+ # Nothing
227
+ end
228
+ # Mock start a container
229
+ expect(proxmox).to receive(:delete).with('nodes/pve_node_name/lxc/1024') do
230
+ 'UPID:pve_node_name:0000A504:6DEABF24:5F44669B:destroy::root@pam:'
231
+ end
232
+ # Mock checking task status
233
+ expect(proxmox).to receive(:get).with('nodes/pve_node_name/tasks/UPID:pve_node_name:0000A504:6DEABF24:5F44669B:destroy::root@pam:/status') do
234
+ { 'status' => task_status }
235
+ end
236
+ proxmox
237
+ end
238
+ end
239
+
240
+ # Get a mocking code corresponding to a call to Proxmox.new.
241
+ # This code should mock Proxmox getting the status a node
242
+ #
243
+ # Parameters::
244
+ # * *proxmox_user* (String or nil): Proxmox user used to connect to Proxmox API [default: nil]
245
+ # * *proxmox_password* (String or nil): Proxmox password used to connect to Proxmox API [default: nil]
246
+ # * *status* (String): Mocked status [default: 'created']
247
+ # Result::
248
+ # * Proc: Code called in place of Proxmox.new. Signature is the same as Proxmox.new.
249
+ def mock_proxmox_to_status_node(
250
+ proxmox_user: nil,
251
+ proxmox_password: nil,
252
+ task_status: 'OK'
253
+ )
254
+ proc do |url, pve_node, user, password, realm, options|
255
+ expect(url).to eq 'https://my-proxmox.my-domain.com:8006/api2/json/'
256
+ expect(pve_node).to eq 'my-proxmox'
257
+ expect(user).to eq proxmox_user
258
+ expect(password).to eq proxmox_password
259
+ expect(realm).to eq 'pam'
260
+ expect(options[:verify_ssl]).to eq false
261
+ proxmox = double 'Proxmox create instance'
262
+ # Mock initialization
263
+ expect(proxmox).to receive(:logger=) do
264
+ # Nothing
265
+ end
266
+ expect(proxmox).to receive(:logger_stderr=) do
267
+ # Nothing
268
+ end
269
+ # Mock getting status of a container
270
+ expect(proxmox).to receive(:get).with('nodes/pve_node_name/lxc') do
271
+ [
272
+ {
273
+ 'vmid' => '1024'
274
+ }
275
+ ]
276
+ end
277
+ expect(proxmox).to receive(:get).with('nodes/pve_node_name/lxc/1024/status/current') do
278
+ {
279
+ 'status' => 'created'
280
+ }
281
+ end
282
+ proxmox
283
+ end
284
+ end
285
+
286
+ # Mock a call to the reserve_proxmox_container sync node
287
+ #
288
+ # Parameters::
289
+ # * *proxmox_user* (String or nil): Proxmox user used to connect to Proxmox API [default: nil]
290
+ # * *proxmox_password* (String or nil): Proxmox password used to connect to Proxmox API [default: nil]
291
+ # * *proxmox_realm* (String): Proxmox realm used to connect to Proxmox API [default: 'pam']
292
+ # * *error_on_create* (String or nil): Error to be mocked by reserve_proxmox_container create, or nil in case of success [default: nil]
293
+ # * *error_on_destroy* (String or nil): Error to be mocked by reserve_proxmox_container destroy, or nil in case of success [default: nil]
294
+ # * *destroy_vm* (Boolean): Should we expect also a VM destruction? [default: false]
295
+ # * *expected_file_id* (String): The expected config file IDs used [default: 'node_test']
296
+ # * *expected_sudo* (Boolean): Is sudo to be expected? [default: true]
297
+ def mock_call_to_reserve_proxmox_container(
298
+ proxmox_user: nil,
299
+ proxmox_password: nil,
300
+ proxmox_realm: 'pam',
301
+ error_on_create: nil,
302
+ error_on_destroy: nil,
303
+ destroy_vm: false,
304
+ expected_file_id: 'node_test',
305
+ expected_sudo: true
306
+ )
307
+ runs = [
308
+ proc do |actions|
309
+ expect(actions.keys).to eq ['node']
310
+ expect(actions['node'].size).to eq 4
311
+ # First action should be to copy the reserve_proxmox_container code
312
+ expect(actions['node'][0].keys).to eq [:scp]
313
+ expect(actions['node'][0][:scp].first[0]).to match /^.+\/hpc_plugins\/provisioner\/proxmox\/$/
314
+ expect(actions['node'][0][:scp].first[1]).to eq '.'
315
+ # Second action should be to create directories
316
+ expect(actions['node'][1]).to eq({
317
+ remote_bash: "mkdir -p ./proxmox/config\nmkdir -p ./proxmox/create"
318
+ })
319
+ # Next actions should be to copy the config/create/destroy files
320
+ expect(actions['node'][2].keys).to eq [:scp]
321
+ expect(actions['node'][2][:scp].first[0]).to match /^.+\/create_#{Regexp.escape(expected_file_id)}\.json$/
322
+ expect(actions['node'][2][:scp].first[1]).to eq './proxmox/create'
323
+ expect(actions['node'][3].keys).to eq [:scp]
324
+ expect(actions['node'][3][:scp].first[0]).to match /^.+\/config_#{Regexp.escape(expected_file_id)}\.json$/
325
+ expect(actions['node'][3][:scp].first[1]).to eq './proxmox/config'
326
+ @proxmox_create_options = JSON.parse(File.read(actions['node'][2][:scp].first[0]))
327
+ { 'node' => [0, '', ''] }
328
+ end,
329
+ proc do |actions|
330
+ expect(actions).to eq({
331
+ 'node' => {
332
+ remote_bash: {
333
+ commands: "#{expected_sudo ? 'sudo -E ' : ''}./proxmox/reserve_proxmox_container --create ./proxmox/create/create_#{expected_file_id}.json --config ./proxmox/config/config_#{expected_file_id}.json",
334
+ env: {
335
+ 'hpc_user_for_proxmox' => proxmox_user,
336
+ 'hpc_password_for_proxmox' => proxmox_password,
337
+ 'hpc_realm_for_proxmox' => proxmox_realm
338
+ }
339
+ }
340
+ }
341
+ })
342
+ result =
343
+ if error_on_create
344
+ { error: error_on_create }
345
+ else
346
+ {
347
+ pve_node: 'pve_node_name',
348
+ vm_id: 1024,
349
+ vm_ip: '192.168.0.100'
350
+ }
351
+ end
352
+ { 'node' => [0, <<~EOS, ''] }
353
+ ===== JSON =====
354
+ #{JSON.pretty_generate(result)}
355
+ EOS
356
+ end
357
+ ]
358
+ if destroy_vm
359
+ runs.concat [
360
+ proc do |actions|
361
+ expect(actions.keys).to eq ['node']
362
+ expect(actions['node'].size).to eq 4
363
+ # First action should be to copy the reserve_proxmox_container code
364
+ expect(actions['node'][0].keys).to eq [:scp]
365
+ expect(actions['node'][0][:scp].first[0]).to match /^.+\/hpc_plugins\/provisioner\/proxmox\/$/
366
+ expect(actions['node'][0][:scp].first[1]).to eq '.'
367
+ # Second action should be to create directories
368
+ expect(actions['node'][1]).to eq({
369
+ remote_bash: "mkdir -p ./proxmox/config\nmkdir -p ./proxmox/destroy"
370
+ })
371
+ # Next actions should be to copy the config/create/destroy files
372
+ expect(actions['node'][2].keys).to eq [:scp]
373
+ expect(actions['node'][2][:scp].first[0]).to match /^.+\/destroy_#{Regexp.escape(expected_file_id)}\.json$/
374
+ expect(actions['node'][2][:scp].first[1]).to eq './proxmox/destroy'
375
+ expect(actions['node'][3].keys).to eq [:scp]
376
+ expect(actions['node'][3][:scp].first[0]).to match /^.+\/config_#{Regexp.escape(expected_file_id)}\.json$/
377
+ expect(actions['node'][3][:scp].first[1]).to eq './proxmox/config'
378
+ @proxmox_destroy_options = JSON.parse(File.read(actions['node'][2][:scp].first[0]))
379
+ { 'node' => [0, '', ''] }
380
+ end,
381
+ proc do |actions|
382
+ expect(actions).to eq({
383
+ 'node' => {
384
+ remote_bash: {
385
+ commands: "#{expected_sudo ? 'sudo -E ' : ''}./proxmox/reserve_proxmox_container --destroy ./proxmox/destroy/destroy_#{expected_file_id}.json --config ./proxmox/config/config_#{expected_file_id}.json",
386
+ env: {
387
+ 'hpc_user_for_proxmox' => proxmox_user,
388
+ 'hpc_password_for_proxmox' => proxmox_password,
389
+ 'hpc_realm_for_proxmox' => proxmox_realm
390
+ }
391
+ }
392
+ }
393
+ })
394
+ result =
395
+ if error_on_destroy
396
+ { error: error_on_destroy }
397
+ else
398
+ {
399
+ pve_node: 'pve_node_name',
400
+ vm_id: 1024,
401
+ vm_ip: '192.168.0.100'
402
+ }
403
+ end
404
+ { 'node' => [0, <<~EOS, ''] }
405
+ ===== JSON =====
406
+ #{JSON.pretty_generate(result)}
407
+ EOS
408
+ end
409
+ ]
410
+ end
411
+ expect_actions_executor_runs runs
412
+ end
413
+
414
+ # Mock a series of Proxmox calls, and expect them to occur.
415
+ #
416
+ # Parameters::
417
+ # * *calls* (Array<Proc>): List of mocked calls
418
+ # * *proxmox_user* (String or nil): Proxmox user used to connect to Proxmox API [default: nil]
419
+ # * *proxmox_password* (String or nil): Proxmox password used to connect to Proxmox API [default: nil]
420
+ # * *proxmox_realm* (String): Proxmox realm used to connect to Proxmox API [default: 'pam']
421
+ # * *error_on_create* (String or nil): Error to be mocked by reserve_proxmox_container create, or nil in case of success [default: nil]
422
+ # * *error_on_destroy* (String or nil): Error to be mocked by reserve_proxmox_container destroy, or nil in case of success [default: nil]
423
+ # * *reserve* (Boolean): Do we expect the resource reservation to occur? [default: true]
424
+ # * *destroy_vm* (Boolean): Should we expect also a VM destruction? [default: false]
425
+ # * *expected_file_id* (String): The expected config file IDs used [default: 'node_test']
426
+ # * *expected_sudo* (Boolean): Is sudo to be expected? [default: true]
427
+ def mock_proxmox_calls_with(
428
+ calls,
429
+ proxmox_user: nil,
430
+ proxmox_password: nil,
431
+ proxmox_realm: 'pam',
432
+ error_on_create: nil,
433
+ error_on_destroy: nil,
434
+ reserve: true,
435
+ destroy_vm: false,
436
+ expected_file_id: 'node_test',
437
+ expected_sudo: true
438
+ )
439
+ if reserve || destroy_vm
440
+ # Mock querying reserve_proxmox_container
441
+ mock_call_to_reserve_proxmox_container(
442
+ proxmox_user: proxmox_user,
443
+ proxmox_password: proxmox_password,
444
+ proxmox_realm: proxmox_realm,
445
+ error_on_create: error_on_create,
446
+ error_on_destroy: error_on_destroy,
447
+ destroy_vm: destroy_vm,
448
+ expected_file_id: expected_file_id,
449
+ expected_sudo: expected_sudo
450
+ )
451
+ end
452
+ expect(::Proxmox::Proxmox).to receive(:new).exactly(calls.size).times do |url, pve_node, user, password, realm, options|
453
+ calls.shift.call(url, pve_node, user, password, realm, options)
454
+ end
455
+ end
456
+
457
+ # Mock the Proxmox API calls to map a given Proxmox status.
458
+ # Mock any call to the paths of the API to serve a given nodes information.
459
+ # Mock also calls to stop and destroy containers, and log those actions so that they can be checked by the test case later.
460
+ #
461
+ # Parameters::
462
+ # * *proxmox_user* (String): Proxmox user to be used for the API, or nil if none [default: nil]
463
+ # * *proxmox_password* (String): Proxmox password to be used for the API, or nil if none [default: nil]
464
+ # * *proxmox_realm* (String): Proxmox realm to be used for the API, or nil if none [default: 'pam']
465
+ # * *mocked_pve_nodes* (Array< Hash< String, Hash<Symbol,Object> > > or Hash< String, Hash<Symbol,Object> >):
466
+ # List of (or single) PVE node information, per PVE node name. [default: { 'pve_node_name' => {} }]
467
+ # If used as a list, it is expected that the Proxmox API be used as many times as the number of items in the list, and the mocked info will follow the list order.
468
+ # Here are the properties of a PVE node:
469
+ # * *loadavg* ([Float, Float, Float]): Load average of the node [default: [0.1, 0.2, 0.3]]
470
+ # * *memory_total* (Integer): Bytes of RAM of this PVE node [default: 16 * 1024 * 1024 * 1024]
471
+ # * *storage_total* (Integer): Bytes of disk of this PVE node [default: 100 * 1024 * 1024 * 1024]
472
+ # * *lxc_containers* (Hash<Integer, Hash<Symbol,Object> >): LXC containers info, per VM ID [default: {}]
473
+ # * *maxdisk* (Integer): Bytes of disk allocated to this VM [default: 1024 * 1024 * 1024]
474
+ # * *maxmem* (Integer): Bytes of RAM allocated to this VM [default: 1024 * 1024 * 1024]
475
+ # * *cpus* (Integer): CPUs allocated to this VM [default: 1]
476
+ # * *ip* (String): IP allocated to this node [default: 192.168.0.<vmid % 254 + 1>]
477
+ # * *status* (String): Status of this node [default: 'running']
478
+ # * *debug* (Boolean): Do we mark the VM as debug? [default: false]
479
+ # * *node* (String): Node for which this given container has been created [default: 'test_node']
480
+ # * *environment* (String): Environment for which this given container has been created [default: 'test_env']
481
+ def mock_proxmox(
482
+ proxmox_user: nil,
483
+ proxmox_password: nil,
484
+ proxmox_realm: 'pam',
485
+ mocked_pve_nodes: { 'pve_node_name' => {} }
486
+ )
487
+ # List of proxmox actions that have been mocked and their corresponding properties
488
+ # Array< [Symbol, Object] >
489
+ @proxmox_actions = []
490
+ mocked_pve_nodes = [mocked_pve_nodes] unless mocked_pve_nodes.is_a?(Array)
491
+ mock_proxmox_calls_with(
492
+ mocked_pve_nodes.map do |pve_nodes|
493
+ # Complete pve_nodes with default values
494
+ pve_nodes = Hash[pve_nodes.map do |pve_node_name, pve_node_info|
495
+ pve_node_info[:lxc_containers] = Hash[(pve_node_info.key?(:lxc_containers) ? pve_node_info[:lxc_containers] : {}).map do |vm_id, vm_info|
496
+ [
497
+ vm_id,
498
+ {
499
+ maxdisk: 1024 * 1024 * 1024,
500
+ maxmem: 1024 * 1024 * 1024,
501
+ cpus: 1,
502
+ ip: "192.168.0.#{(vm_id % 254) + 1}",
503
+ status: 'running',
504
+ creation_date: (Time.now - 60).utc,
505
+ debug: false,
506
+ node: 'test_node',
507
+ environment: 'test_env'
508
+ }.merge(vm_info)
509
+ ]
510
+ end]
511
+ [
512
+ pve_node_name,
513
+ {
514
+ loadavg: [0.1, 0.2, 0.3],
515
+ memory_total: 16 * 1024 * 1024 * 1024,
516
+ storage_total: 100 * 1024 * 1024 * 1024
517
+ }.merge(pve_node_info)
518
+ ]
519
+ end]
520
+ proc do |url, pve_node, user, password, realm, options|
521
+ expect(url).to eq 'https://my-proxmox.my-domain.com:8006/api2/json/'
522
+ expect(pve_node).to eq 'my-proxmox'
523
+ expect(user).to eq proxmox_user
524
+ expect(password).to eq proxmox_password
525
+ expect(realm).to eq proxmox_realm
526
+ expect(options[:verify_ssl]).to eq false
527
+ proxmox = double 'Proxmox create instance'
528
+ # Mock getting status of a container
529
+ allow(proxmox).to receive(:get) do |path|
530
+ case path
531
+ when 'nodes'
532
+ pve_nodes.keys.map { |pve_node_name| { 'node' => pve_node_name } }
533
+ when /^nodes\/([^\/]+)\/status$/
534
+ pve_node_name = $1
535
+ {
536
+ 'loadavg' => pve_nodes[pve_node_name][:loadavg].map(&:to_s),
537
+ 'memory' => {
538
+ 'total' => pve_nodes[pve_node_name][:memory_total]
539
+ }
540
+ }
541
+ when /^nodes\/([^\/]+)\/storage$/
542
+ pve_node_name = $1
543
+ [
544
+ {
545
+ 'storage' => 'local-lvm',
546
+ 'total' => pve_nodes[pve_node_name][:storage_total]
547
+ }
548
+ ]
549
+ when /^nodes\/([^\/]+)\/lxc$/
550
+ pve_node_name = $1
551
+ pve_nodes[pve_node_name][:lxc_containers].map do |vm_id, vm_info|
552
+ {
553
+ 'vmid' => vm_id.to_s,
554
+ 'maxdisk' => vm_info[:maxdisk],
555
+ 'maxmem' => vm_info[:maxmem],
556
+ 'cpus' => vm_info[:cpus]
557
+ }
558
+ end
559
+ when /^nodes\/([^\/]+)\/lxc\/([^\/]+)\/config$/
560
+ pve_node_name = $1
561
+ vmid = $2
562
+ {
563
+ 'net0' => "ip=#{pve_nodes[pve_node_name][:lxc_containers][Integer(vmid)][:ip]}/32",
564
+ 'description' => <<~EOS
565
+ ===== HPC info =====
566
+ node: #{pve_nodes[pve_node_name][:lxc_containers][Integer(vmid)][:node]}
567
+ environment: #{pve_nodes[pve_node_name][:lxc_containers][Integer(vmid)][:environment]}
568
+ debug: #{pve_nodes[pve_node_name][:lxc_containers][Integer(vmid)][:debug] ? 'true' : 'false'}
569
+ creation_date: #{pve_nodes[pve_node_name][:lxc_containers][Integer(vmid)][:creation_date].strftime('%FT%T')}
570
+ EOS
571
+ }
572
+ when /^nodes\/([^\/]+)\/lxc\/([^\/]+)\/status\/current$/
573
+ pve_node_name = $1
574
+ vmid = $2
575
+ {
576
+ 'status' => pve_nodes[pve_node_name][:lxc_containers][Integer(vmid)][:status]
577
+ }
578
+ when /^nodes\/([^\/]+)\/tasks\/([^\/]+)\/status$/
579
+ pve_node_name = $1
580
+ task = $2
581
+ # Mock tasks completion
582
+ {
583
+ 'status' => 'OK'
584
+ }
585
+ else
586
+ raise "Unknown Proxmox API get call: #{path}. Please adapt the test framework."
587
+ end
588
+ end
589
+ # Mock some post actions
590
+ allow(proxmox).to receive(:post) do |path, args|
591
+ @proxmox_actions << [:post, path, args].compact
592
+ case path
593
+ when /^nodes\/([^\/]+)\/lxc$/
594
+ pve_node_name = $1
595
+ "UPID:#{pve_node_name}:0000A504:6DEABF24:5F44669B:create::root@pam:"
596
+ when /^nodes\/([^\/]+)\/lxc\/([^\/]+)\/status\/stop$/
597
+ pve_node_name = $1
598
+ vmid = $2
599
+ "UPID:#{pve_node_name}:0000A504:6DEABF24:5F44669B:stop_#{vmid}::root@pam:"
600
+ else
601
+ raise "Unknown Proxmox API post call: #{path}. Please adapt the test framework."
602
+ end
603
+ end
604
+ # Mock some delete actions
605
+ allow(proxmox).to receive(:delete) do |path|
606
+ @proxmox_actions << [:delete, path]
607
+ case path
608
+ when /^nodes\/([^\/]+)\/lxc\/([^\/]+)$/
609
+ pve_node_name = $1
610
+ vmid = $2
611
+ # Make sure we delete the mocked information as well
612
+ pve_nodes[pve_node_name][:lxc_containers].delete(Integer(vmid))
613
+ "UPID:#{pve_node_name}:0000A504:6DEABF24:5F44669B:destroy_#{vmid}::root@pam:"
614
+ else
615
+ raise "Unknown Proxmox API post call: #{path}. Please adapt the test framework."
616
+ end
617
+ end
618
+ proxmox
619
+ end
620
+ end,
621
+ reserve: false
622
+ )
623
+ end
624
+
625
+ # Prepare a repository to test reserve_proxmox_container
626
+ #
627
+ # Parameters::
628
+ # * Proc: Code to be called with repository setup
629
+ def with_sync_node
630
+ with_repository('sync_node') do |repository|
631
+ @repository = repository
632
+ yield
633
+ end
634
+ end
635
+
636
+ # Call the reserve_proxmox_container script using a given ARGV.
637
+ # Prerequisite: This is called within a with_sync_node session.
638
+ #
639
+ # Parameters::
640
+ # * *config* (Hash): Configuration overriding defaults to store in the config file [default: {}]
641
+ # * *max_retries* (Integer): Specify the max number of retries [default: 1]
642
+ # * *wait_before_retry* (Integer): Specify the number of seconds to wait before retry [default: 0]
643
+ # * *create* (Hash or nil): Create file content, or nil if none [default: nil]
644
+ # * *destroy* (Hash or nil): Destroy file content, or nil if none [default: nil]
645
+ # Result::
646
+ # * Hash: JSON result of the call
647
+ def call_reserve_proxmox_container_with(config: {}, max_retries: 1, wait_before_retry: 0, create: nil, destroy: nil)
648
+ # Make sure we set default values in the config
649
+ config = {
650
+ proxmox_api_url: 'https://my-proxmox.my-domain.com:8006',
651
+ futex_file: "#{@repository}/proxmox/allocations.futex",
652
+ logs_dir: "#{Dir.tmpdir}/hpc_test_proxmox_waiter_logs",
653
+ pve_nodes: ['pve_node_name'],
654
+ vm_ips_list: %w[
655
+ 192.168.0.100
656
+ 192.168.0.101
657
+ 192.168.0.102
658
+ ],
659
+ vm_ids_range: [1000, 1100],
660
+ coeff_ram_consumption: 10,
661
+ coeff_disk_consumption: 1,
662
+ expiration_period_secs: 24 * 60 * 60,
663
+ expire_stopped_vm_timeout_secs: 3,
664
+ limits: {
665
+ nbr_vms_max: 5,
666
+ cpu_loads_thresholds: [10, 10, 10],
667
+ ram_percent_used_max: 0.75,
668
+ disk_percent_used_max: 0.75
669
+ }
670
+ }.merge(config)
671
+ FileUtils.cp_r "#{__dir__}/../../../lib/hybrid_platforms_conductor/hpc_plugins/provisioner/proxmox", @repository
672
+ File.write("#{@repository}/proxmox/config.json", config.to_json)
673
+ script_args = [
674
+ '--max-retries', max_retries.to_s,
675
+ '--wait-before-retry', wait_before_retry.to_s
676
+ ]
677
+ unless create.nil?
678
+ create_file = "#{@repository}/proxmox/create_vm.json"
679
+ File.write(create_file, create.to_json)
680
+ script_args.concat(['--create', create_file])
681
+ end
682
+ unless destroy.nil?
683
+ destroy_file = "#{@repository}/proxmox/destroy_vm.json"
684
+ File.write(destroy_file, destroy.to_json)
685
+ script_args.concat(['--destroy', destroy_file])
686
+ end
687
+ # Call the script by loading the Ruby file mocking the ARGV and ENV variables
688
+ old_argv = ARGV.dup
689
+ old_stdout = $stdout
690
+ ARGV.replace(script_args)
691
+ $stdout = StringIO.new unless logger.debug?
692
+ begin
693
+ load "#{@repository}/proxmox/reserve_proxmox_container"
694
+ if logger.debug?
695
+ raise 'This test can\'t run in debug mode.'
696
+ else
697
+ @stdout = $stdout.string
698
+ end
699
+ ensure
700
+ ARGV.replace old_argv
701
+ $stdout = old_stdout
702
+ end
703
+ stdout_lines = @stdout.split("\n")
704
+ JSON.parse(stdout_lines[stdout_lines.index('===== JSON =====') + 1..-1].join("\n")).transform_keys(&:to_sym)
705
+ end
706
+
707
+ # Call the reserve_proxmox_container script and get its result as JSON.
708
+ # Prerequisite: This is called within a with_sync_node session.
709
+ #
710
+ # Parameters::
711
+ # * *cpus* (Integer): Required CPUs
712
+ # * *ram_mb* (Integer): Required RAM MB
713
+ # * *disk_gb* (Integer): Required Disk GB
714
+ # * *config* (Hash): Configuration overriding defaults to store in the config file [default: {}]
715
+ # * *max_retries* (Integer): Specify the max number of retries [default: 1]
716
+ # * *wait_before_retry* (Integer): Specify the number of seconds to wait before retry [default: 0]
717
+ # Result::
718
+ # * Hash: JSON result of the call
719
+ def call_reserve_proxmox_container(cpus, ram_mb, disk_gb, config: {}, max_retries: 1, wait_before_retry: 0)
720
+ call_reserve_proxmox_container_with(
721
+ config: config,
722
+ max_retries: max_retries,
723
+ wait_before_retry: wait_before_retry,
724
+ create: {
725
+ ostemplate: 'test_template.iso',
726
+ hostname: 'test.hostname.my-domain.com',
727
+ description: "===== HPC info =====\nnode: test_node\nenvironment: test_env\n",
728
+ cores: cpus,
729
+ cpulimit: cpus,
730
+ memory: ram_mb,
731
+ rootfs: "local-lvm:#{disk_gb}",
732
+ net0: 'name=eth0,bridge=vmbr0,gw=172.16.16.16'
733
+ }
734
+ )
735
+ end
736
+
737
+ # Call the reserve_proxmox_container script to release a VM and get its result as JSON.
738
+ # Prerequisite: This is called within a with_sync_node session.
739
+ #
740
+ # Parameters::
741
+ # * *vm_id* (Integer): VM ID to release
742
+ # * *node* (String): Node for which the VM has been reserved
743
+ # * *environment* (String): Environment for which the VM has been reserved
744
+ # * *config* (Hash): Configuration overriding defaults to store in the config file [default: {}]
745
+ # * *max_retries* (Integer): Specify the max number of retries [default: 1]
746
+ # Result::
747
+ # * Hash: JSON result of the call
748
+ def call_release_proxmox_container(vm_id, node, environment, config: {}, max_retries: 1)
749
+ call_reserve_proxmox_container_with(
750
+ config: config,
751
+ max_retries: max_retries,
752
+ destroy: {
753
+ vm_id: vm_id,
754
+ node: node,
755
+ environment: environment
756
+ }
757
+ )
758
+ end
759
+
760
+ # Expect a list of Proxmox API calls to match a given list.
761
+ # Handle Regexp in the expectation.
762
+ #
763
+ # Parameters::
764
+ # * *expected_proxmox_actions* (Array<Array>): Expected Proxmox actions
765
+ def expect_proxmox_actions_to_be(expected_proxmox_actions)
766
+ expect(@proxmox_actions.size).to eq expected_proxmox_actions.size
767
+ @proxmox_actions.zip(expected_proxmox_actions).each do |proxmox_action, expected_proxmox_action|
768
+ expect(proxmox_action.size).to eq expected_proxmox_action.size
769
+ expect(proxmox_action[0..1]).to eq expected_proxmox_action[0..1]
770
+ if proxmox_action.size >= 3
771
+ # The third argument is a Hash that might have Regexp in the expectation
772
+ expect(proxmox_action[2].keys.sort).to eq expected_proxmox_action[2].keys.sort
773
+ proxmox_action[2].each do |property, value|
774
+ expected_value = expected_proxmox_action[2][property]
775
+ if expected_value.is_a?(Regexp)
776
+ expect(value).to match expected_value
777
+ else
778
+ expect(value).to eq expected_value
779
+ end
780
+ end
781
+ end
782
+ end
783
+ end
784
+
785
+ end
786
+
787
+ end
788
+
789
+ end