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,141 @@
1
+ require 'hybrid_platforms_conductor/logger_helpers'
2
+ require 'hybrid_platforms_conductor/plugin'
3
+
4
+ module HybridPlatformsConductor
5
+
6
+ # Common ancestor to any test class
7
+ class Test < Plugin
8
+
9
+ class << self
10
+
11
+ # A NodesHandler instance that can be useful for test classes that need to access nodes information
12
+ attr_accessor :nodes_handler
13
+
14
+ end
15
+
16
+ # Get errors encountered
17
+ # Array<String>
18
+ attr_reader :errors
19
+
20
+ # Get the test name
21
+ # String
22
+ attr_reader :name
23
+
24
+ # Get the platform being tested, or nil for global tests
25
+ # PlatformHandler or nil
26
+ attr_reader :platform
27
+
28
+ # Get the node name being tested, or nil for global and platform tests
29
+ # String or nil
30
+ attr_reader :node
31
+
32
+ # Expected failure, or nil if not expected to fail
33
+ # String or nil
34
+ attr_reader :expected_failure
35
+
36
+ # Constructor
37
+ #
38
+ # Parameters::
39
+ # * *logger* (Logger): Logger to be used
40
+ # * *logger_stderr* (Logger): Logger to be used for stderr
41
+ # * *config* (Config): Config to be used.
42
+ # * *cmd_runner* (CmdRunner): CmdRunner that can be used by tests
43
+ # * *nodes_handler* (NodesHandler): Nodes handler that can be used by tests
44
+ # * *deployer* (Deployer): Deployer that can be used by tests
45
+ # * *name* (String): Name of the test being instantiated [default: 'unknown_test']
46
+ # * *platform* (PlatformHandler): Platform handler for which the test is instantiated, or nil if global or node specific [default: nil]
47
+ # * *node* (String): Node name for which the test is instantiated, or nil if global or platform specific [default: nil]
48
+ # * *expected_failure* (String or nil): Expected failure, or nil if not expected to fail [default: nil]
49
+ def initialize(logger, logger_stderr, config, cmd_runner, nodes_handler, deployer, name: 'unknown_test', platform: nil, node: nil, expected_failure: nil)
50
+ super(logger: logger, logger_stderr: logger_stderr, config: config)
51
+ @cmd_runner = cmd_runner
52
+ @nodes_handler = nodes_handler
53
+ @deployer = deployer
54
+ @name = name
55
+ @platform = platform
56
+ @node = node
57
+ @expected_failure = expected_failure
58
+ @errors = []
59
+ @executed = false
60
+ end
61
+
62
+ # Get a String identifier of this test, useful for outputing messages
63
+ #
64
+ # Result::
65
+ # * String: Identifier of this test
66
+ def to_s
67
+ test_desc =
68
+ if !node.nil?
69
+ "Node #{@node}"
70
+ elsif !platform.nil?
71
+ "Platform #{@platform.name}"
72
+ else
73
+ 'Global'
74
+ end
75
+ "#< Test #{name} - #{test_desc} >"
76
+ end
77
+
78
+ # Assert an equality
79
+ #
80
+ # Parameters::
81
+ # * *tested_object* (Object): The object being tested
82
+ # * *expected_object* (Object): The object being expected
83
+ # * *error_msg* (String): Error message to associate in case of inequality
84
+ # * *details* (String or nil): Additional details, or nil if none [default = nil]
85
+ def assert_equal(tested_object, expected_object, error_msg, details = nil)
86
+ error error_msg, details unless tested_object == expected_object
87
+ end
88
+
89
+ # Assert a String match
90
+ #
91
+ # Parameters::
92
+ # * *tested_object* (String): The object being tested
93
+ # * *expected_object* (Regex): The object being expected
94
+ # * *error_msg* (String): Error message to associate in case of inequality
95
+ # * *details* (String or nil): Additional details, or nil if none [default = nil]
96
+ def assert_match(tested_object, expected_object, error_msg, details = nil)
97
+ error error_msg, details unless tested_object =~ expected_object
98
+ end
99
+
100
+ # Register an error
101
+ #
102
+ # Parameters::
103
+ # * *message* (String): The error message
104
+ # * *details* (String or nil): Additional details, or nil if none [default = nil]
105
+ def error(message, details = nil)
106
+ log_error "[ #{self} ] - #{message}#{details.nil? ? '' : "\n#{details}"}" if @expected_failure.nil?
107
+ @errors << message
108
+ end
109
+
110
+ # Mark the test has being executed
111
+ def executed
112
+ @executed = true
113
+ end
114
+
115
+ # Has the test been executed?
116
+ #
117
+ # Result::
118
+ # * Boolean: Has the test been executed?
119
+ def executed?
120
+ @executed
121
+ end
122
+
123
+ # Limit the list of platform types for these tests.
124
+ #
125
+ # Result::
126
+ # * Array<Symbol> or nil: List of platform types allowed for this test, or nil for all
127
+ def self.only_on_platforms
128
+ nil
129
+ end
130
+
131
+ # Limit the list of nodes for these tests.
132
+ #
133
+ # Result::
134
+ # * Array<String or Regex> or nil: List of nodes allowed for this test, or nil for all. Regular expressions matching node names can also be used.
135
+ def self.only_on_nodes
136
+ nil
137
+ end
138
+
139
+ end
140
+
141
+ end
@@ -0,0 +1,22 @@
1
+ module HybridPlatformsConductor
2
+
3
+ # Ancestor for all tests that should be run just once per service
4
+ class TestByService < Test
5
+
6
+ # Limit the list of nodes for these tests.
7
+ #
8
+ # Result::
9
+ # * Array<String or Regex> or nil: List of nodes allowed for this test, or nil for all. Regular expressions matching node names can also be used.
10
+ def self.only_on_nodes
11
+ # Just 1 node per service and platform
12
+ Test.nodes_handler.prefetch_metadata_of Test.nodes_handler.known_nodes, :services
13
+ Test.nodes_handler.
14
+ known_nodes.
15
+ sort.
16
+ group_by { |node| Test.nodes_handler.get_services_of(node).sort }.
17
+ map { |(_service, _platform), nodes| nodes.first }
18
+ end
19
+
20
+ end
21
+
22
+ end
@@ -0,0 +1,282 @@
1
+ require 'hybrid_platforms_conductor/logger_helpers'
2
+ require 'hybrid_platforms_conductor/plugin'
3
+
4
+ module HybridPlatformsConductor
5
+
6
+ # Base class for test reports plugins
7
+ class TestReport < Plugin
8
+
9
+ # Constructor
10
+ #
11
+ # Parameters::
12
+ # * *logger* (Logger): Logger to be used
13
+ # * *logger_stderr* (Logger): Logger to be used for stderr
14
+ # * *config* (Config): Config to be used.
15
+ # * *nodes_handler* (NodesHandler): Nodes handler that has been used by tests.
16
+ # * *tested_nodes* (Array<String>): List of nodes tests were run on.
17
+ # * *tested_platforms* (Array<PlatformHandler>): List of platforms tests were run on.
18
+ # * *tests* (Array<Test>): List of tests.
19
+ def initialize(logger, logger_stderr, config, nodes_handler, tested_nodes, tested_platforms, tests)
20
+ super(logger: logger, logger_stderr: logger_stderr, config: config)
21
+ @nodes_handler = nodes_handler
22
+ @tested_nodes = tested_nodes.uniq.sort
23
+ @tested_platforms = tested_platforms
24
+ @tests = tests
25
+ # Set additional variables that might get handy for reports
26
+ @global_test_names = global_tests.map(&:name).uniq.sort
27
+ @platform_test_names = platform_tests.map(&:name).uniq.sort
28
+ @node_test_names = node_tests.map(&:name).uniq.sort
29
+ # Always put global first
30
+ [@node_test_names, @platform_test_names, @global_test_names].each do |names_list|
31
+ if names_list.include?(:global)
32
+ names_list.delete(:global)
33
+ names_list.insert(0, :global)
34
+ end
35
+ end
36
+ end
37
+
38
+ private
39
+
40
+ # Return global tests
41
+ #
42
+ # Result::
43
+ # * Array<Test>: Global tests
44
+ def global_tests
45
+ @tests.select { |test| test.platform.nil? && test.node.nil? }
46
+ end
47
+
48
+ # Return platform tests
49
+ #
50
+ # Result::
51
+ # * Array<Test>: List of platform tests
52
+ def platform_tests
53
+ @tests.select { |test| !test.platform.nil? }
54
+ end
55
+
56
+ # Return node tests
57
+ #
58
+ # Result::
59
+ # * Array<Test>: List of node tests
60
+ def node_tests
61
+ @tests.select { |test| !test.node.nil? }
62
+ end
63
+
64
+ # Select tests corresponding to a given criteria
65
+ #
66
+ # Parameters::
67
+ # * *name* (String): Test name
68
+ # * *node* (String or nil): Node name, or nil for global/platform tests [default = nil]
69
+ # * *platform* (String or nil): Platform repository name, or nil for global/node tests. Ignored if node is set. [default = nil]
70
+ # Result::
71
+ # * Array<Test>: List of selected tests
72
+ def select_tests(name, node: nil, platform: nil)
73
+ @tests.select do |search_test|
74
+ search_test.name == name &&
75
+ search_test.node == node &&
76
+ search_test.platform == platform
77
+ end
78
+ end
79
+
80
+ # Is a given test supposed to have run?
81
+ #
82
+ # Parameters::
83
+ # * *name* (String): Test name
84
+ # * *node* (String or nil): Node name, or nil for global/platform tests [default = nil]
85
+ # * *platform* (String or nil): Platform repository name, or nil for global/node tests. Ignored if node is set. [default = nil]
86
+ # Result::
87
+ # * Boolean: Is a given test supposed to have run?
88
+ def should_have_been_tested?(name, node: nil, platform: nil)
89
+ !select_tests(name, node: node, platform: platform).empty?
90
+ end
91
+
92
+ # Does a given test on a given node have tests that have not been executed?
93
+ #
94
+ # Parameters::
95
+ # * *name* (String): Test name
96
+ # * *node* (String or nil): Node name, or nil for global/platform tests [default = nil]
97
+ # * *platform* (String or nil): Platform repository name, or nil for global/node tests. Ignored if node is set. [default = nil]
98
+ # Result::
99
+ # * Boolean: Does a given test on a given node have tests that have not been executed?
100
+ def missing_tests_for(name, node: nil, platform: nil)
101
+ select_tests(name, node: node, platform: platform).any? { |test| !test.executed? }
102
+ end
103
+
104
+ # Get the errors for a given test on a given node
105
+ #
106
+ # Parameters::
107
+ # * *name* (String): Test name
108
+ # * *node* (String or nil): Node name, or nil for global/platform tests [default = nil]
109
+ # * *platform* (String or nil): Platform repository name, or nil for global/node tests. Ignored if node is set. [default = nil]
110
+ # Result::
111
+ # * Array<String>: List of errors
112
+ def errors_for(name, node: nil, platform: nil)
113
+ select_tests(name, node: node, platform: platform).inject([]) { |errors, test| errors + test.errors }
114
+ end
115
+
116
+ # Return errors grouped by a given criteria from a list of tests.
117
+ # Don't create groups having no errors.
118
+ # Sort group keys.
119
+ #
120
+ # Parameters::
121
+ # * *tests* (Array<Test>): List of tests to group errors from
122
+ # * *group_criterias* (Symbol or Proc or Array<Symbol or Proc>): Ordered list (or single item) of group by criterias. Each criteria applies on a list of tests and can be one of the following:
123
+ # * Symbol: Named criteria. Can be one of the following:
124
+ # * test_name: Group by test name
125
+ # * platform: Group by platform
126
+ # * node: Group by node
127
+ # * Proc: Code given directly to the group_by method of an Array<test>:
128
+ # * Parameters::
129
+ # * *test* (Test): Test to extract group by criteria from
130
+ # * Result::
131
+ # * Object: The group by criteria
132
+ # * *filter* (Symbol or nil): Filter errors to be returned, or nil for no filter. Values can be: [default: nil]
133
+ # * *only_as_expected*: Only report errors that were expected
134
+ # * *only_as_non_expected*: Only report errors that were not expected.
135
+ # Result::
136
+ # * Hash or Array<String>: Resulting tree structure, following the group by criterias, giving as leaves the grouped list of errors. If the criterias are empty, return the list of errors.
137
+ def group_errors(tests, *group_criterias, filter: nil)
138
+ if group_criterias.empty?
139
+ tests.inject([]) do |errors, test|
140
+ errors +
141
+ case filter
142
+ when nil
143
+ test.errors
144
+ when :only_as_expected
145
+ test.expected_failure ? test.errors : []
146
+ when :only_as_non_expected
147
+ !test.expected_failure ? test.errors : []
148
+ else
149
+ raise "Unknown errors filter: #{fiter}"
150
+ end
151
+ end
152
+ else
153
+ first_criteria = group_criterias.first
154
+ if first_criteria.is_a?(Symbol)
155
+ first_criteria =
156
+ case first_criteria
157
+ when :test_name
158
+ proc { |test| test.name }
159
+ when :platform
160
+ proc { |test| test.platform }
161
+ when :node
162
+ proc { |test| test.node }
163
+ else
164
+ raise "Unknown group criteria name: #{first_criteria}"
165
+ end
166
+ end
167
+ groups = {}
168
+ tests.group_by(&first_criteria).each do |first_group, grouped_tests|
169
+ next_grouped_errors = group_errors(grouped_tests, *group_criterias[1..-1], filter: filter)
170
+ groups[first_group] = next_grouped_errors unless next_grouped_errors.empty?
171
+ end
172
+ Hash[groups.sort]
173
+ end
174
+ end
175
+
176
+ # Get nodes associated to nodes lists.
177
+ # Also include 2 special lists: 'No list' and 'All'.
178
+ #
179
+ # Result::
180
+ # * Hash< String, Hash<Symbol,Object> >: For each nodes list, we have the following properties:
181
+ # * *nodes* (Array<String>): Nodes in the list
182
+ # * *tested_nodes* (Array<String>): Tested nodes in the list
183
+ # * *tested_nodes_in_error* (Array<String>): Tested nodes in error in the list
184
+ # * *tested_nodes_in_error_as_expected* (Array<String>): Tested nodes in error in the list that are part of the expected failures
185
+ def nodes_by_nodes_list
186
+ no_list_nodes = @nodes_handler.known_nodes
187
+ Hash[(
188
+ @nodes_handler.known_nodes_lists.sort.map do |nodes_list|
189
+ nodes_from_list = @nodes_handler.nodes_from_list(nodes_list, ignore_unknowns: true)
190
+ no_list_nodes -= nodes_from_list
191
+ [nodes_list, nodes_from_list]
192
+ end + [
193
+ ['No list', no_list_nodes],
194
+ ['All', @nodes_handler.known_nodes]
195
+ ]
196
+ ).map do |list_name, list_nodes|
197
+ [
198
+ list_name,
199
+ {
200
+ nodes: list_nodes,
201
+ tested_nodes: list_nodes & @tested_nodes,
202
+ tested_nodes_in_error: list_nodes & group_errors(node_tests, :node).keys,
203
+ tested_nodes_in_error_as_expected: list_nodes & group_errors(node_tests, :node, filter: :only_as_expected).keys
204
+ }
205
+ ]
206
+ end]
207
+ end
208
+
209
+ # Flatten a tree hash.
210
+ # For example:
211
+ # flatten_hash(
212
+ # foo: 'bar',
213
+ # hello: {
214
+ # world: 'Hello World',
215
+ # bro: 'What's up dude?'
216
+ # },
217
+ # a: {
218
+ # b: {
219
+ # c: 'd'
220
+ # }
221
+ # }
222
+ # )
223
+ # will give
224
+ # {
225
+ # :foo => 'bar',
226
+ # :'hello.world' => 'Hello World',
227
+ # :'hello.bro' => 'What's up dude?',
228
+ # :'a.b.c' => 'd'
229
+ # }
230
+ #
231
+ # Parameters::
232
+ # * *hash* (Hash): The tree hash to flatten
233
+ # Result::
234
+ # * Hash: Flatten tree hash
235
+ def flatten_hash(hash)
236
+ hash.each_with_object({}) do |(k, v), h|
237
+ if v.is_a? Hash
238
+ flatten_hash(v).map { |h_k, h_v| h["#{k}.#{h_k}".to_sym] = h_v }
239
+ else
240
+ h[k] = v
241
+ end
242
+ end
243
+ end
244
+
245
+ # Classify a given list of tests by their statuses
246
+ #
247
+ # Parameters::
248
+ # * *tests* (Array<Test>): List of tests to group
249
+ # Result::
250
+ # * Hash<Symbol, Object >: Info for this list of tests. Properties are:
251
+ # * *success* (Array<Test>): Successful tests
252
+ # * *unexpected_error* (Array<Test>): Tests in unexpected error
253
+ # * *expected_error* (Array<Test>): Tests in expected error
254
+ # * *not_run* (Array<Test>): Tests that were not run
255
+ # * *status* (Symbol): The global status of those tests (only the first matching status from this ordered list is returned):
256
+ # * *success*: All tests are successful
257
+ # * *unexpected_error*: Some tests have unexpected errors
258
+ # * *expected_error*: Some tests have expected errors
259
+ # * *not_run*: All non-successful tests have not been run
260
+ def classify_tests(tests)
261
+ info = {
262
+ not_run: tests.select { |test| !test.executed? },
263
+ success: tests.select { |test| test.executed? && test.errors.empty? },
264
+ unexpected_error: tests.select { |test| test.executed? && !test.errors.empty? && test.expected_failure.nil? },
265
+ expected_error: tests.select { |test| test.executed? && !test.errors.empty? && !test.expected_failure.nil? }
266
+ }
267
+ info[:status] =
268
+ if info[:success].size == tests.size
269
+ :success
270
+ elsif !info[:unexpected_error].empty?
271
+ :unexpected_error
272
+ elsif !info[:expected_error].empty?
273
+ :expected_error
274
+ else
275
+ :not_run
276
+ end
277
+ info
278
+ end
279
+
280
+ end
281
+
282
+ end
@@ -0,0 +1,590 @@
1
+ require 'logger'
2
+ require 'hybrid_platforms_conductor/actions_executor'
3
+ require 'hybrid_platforms_conductor/logger_helpers'
4
+ require 'hybrid_platforms_conductor/nodes_handler'
5
+ require 'hybrid_platforms_conductor/parallel_threads'
6
+ require 'hybrid_platforms_conductor/plugins'
7
+ require 'hybrid_platforms_conductor/test'
8
+ require 'hybrid_platforms_conductor/test_report'
9
+
10
+ module HybridPlatformsConductor
11
+
12
+ # Class running tests
13
+ class TestsRunner
14
+
15
+ include LoggerHelpers, ParallelThreads
16
+
17
+ # List of tests to execute [default: []]
18
+ # Array<Symbol>
19
+ attr_accessor :tests
20
+
21
+ # List of reports to use [default: []]
22
+ # Array<Symbol>
23
+ attr_accessor :reports
24
+
25
+ # Do we skip running check-node? [default: false]
26
+ # Boolean
27
+ attr_accessor :skip_run
28
+
29
+ # Number of threads max to use for tests connecting to nodes [default: 64]
30
+ # Integer
31
+ attr_accessor :max_threads_connection_on_nodes
32
+
33
+ # Number of threads max to use for tests running at node level [default: 8]
34
+ # Integer
35
+ attr_accessor :max_threads_nodes
36
+
37
+ # Number of threads max to use for tests running at platform level [default: 8]
38
+ # Integer
39
+ attr_accessor :max_threads_platforms
40
+
41
+ # Constructor
42
+ #
43
+ # Parameters::
44
+ # * *logger* (Logger): Logger to be used [default: Logger.new(STDOUT)]
45
+ # * *logger_stderr* (Logger): Logger to be used for stderr [default: Logger.new(STDERR)]
46
+ # * *config* (Config): Config to be used. [default: Config.new]
47
+ # * *cmd_runner* (Cmdrunner): CmdRunner to be used [default: CmdRunner.new]
48
+ # * *platforms_handler* (PlatformsHandler): Platforms handler to be used [default: PlatformsHandler.new]
49
+ # * *nodes_handler* (NodesHandler): Nodes handler to be used [default: NodesHandler.new]
50
+ # * *actions_executor* (ActionsExecutor): Actions Executor to be used for the tests [default: ActionsExecutor.new]
51
+ # * *deployer* (Deployer): Deployer to be used for the tests needed why-run deployments [default: Deployer.new]
52
+ def initialize(
53
+ logger: Logger.new(STDOUT),
54
+ logger_stderr: Logger.new(STDERR),
55
+ config: Config.new,
56
+ cmd_runner: CmdRunner.new,
57
+ platforms_handler: PlatformsHandler.new,
58
+ nodes_handler: NodesHandler.new,
59
+ actions_executor: ActionsExecutor.new,
60
+ deployer: Deployer.new
61
+ )
62
+ init_loggers(logger, logger_stderr)
63
+ @config = config
64
+ @cmd_runner = cmd_runner
65
+ @platforms_handler = platforms_handler
66
+ @nodes_handler = nodes_handler
67
+ @actions_executor = actions_executor
68
+ @deployer = deployer
69
+ @platforms_handler.inject_dependencies(nodes_handler: @nodes_handler, actions_executor: @actions_executor)
70
+ Test.nodes_handler = nodes_handler
71
+ @tests_plugins = Plugins.new(:test, logger: @logger, logger_stderr: @logger_stderr)
72
+ # The list of tests reports plugins, with their associated class
73
+ # Hash< Symbol, Class >
74
+ @reports_plugins = Plugins.new(:test_report, logger: @logger, logger_stderr: @logger_stderr)
75
+ # Register test classes from platforms
76
+ @platforms_handler.known_platforms.each do |platform|
77
+ if platform.respond_to?(:tests)
78
+ platform.tests.each do |test_name, test_class|
79
+ @tests_plugins[test_name] = test_class
80
+ end
81
+ end
82
+ end
83
+ # Do we skip running check-node?
84
+ @skip_run = false
85
+ # List of tests to be performed
86
+ @tests = []
87
+ # List of reports to be used
88
+ @reports = []
89
+ @max_threads_connection_on_nodes = 64
90
+ @max_threads_nodes = 8
91
+ @max_threads_platforms = 8
92
+ end
93
+
94
+ # Complete an option parser with options meant to control this tests runner
95
+ #
96
+ # Parameters::
97
+ # * *options_parser* (OptionParser): The option parser to complete
98
+ def options_parse(options_parser)
99
+ options_parser.separator ''
100
+ options_parser.separator 'Tests runner options:'
101
+ options_parser.on('-i', '--tests-list FILE_NAME', 'Specify a tests file name. The file should contain a list of tests name (1 per line). Can be used several times.') do |file_name|
102
+ @tests.concat(
103
+ File.read(file_name).
104
+ split("\n").
105
+ reject { |line| line.strip.empty? || line =~ /^#.+/ }.
106
+ map(&:to_sym)
107
+ )
108
+ end
109
+ options_parser.on('-k', '--skip-run', 'Skip running the check-node commands for real, and just analyze existing run logs.') do
110
+ @skip_run = true
111
+ end
112
+ options_parser.on('-r', '--report REPORT', "Specify a report name. Can be used several times. Can be all for all reports. Possible values: #{@reports_plugins.keys.sort.join(', ')} (defaults to stdout).") do |report|
113
+ @reports << report.to_sym
114
+ end
115
+ options_parser.on('-t', '--test TEST', "Specify a test name. Can be used several times. Can be all for all tests. Possible values: #{@tests_plugins.keys.sort.join(', ')} (defaults to all).") do |test_name|
116
+ @tests << test_name.to_sym
117
+ end
118
+ options_parser.on('--max-threads-connections NBR_THREADS', "Specify the max number of threads to parallelize tests connecting on nodes (defaults to #{@max_threads_connection_on_nodes}).") do |nbr_threads|
119
+ @max_threads_connection_on_nodes = Integer(nbr_threads)
120
+ end
121
+ options_parser.on('--max-threads-nodes NBR_THREADS', "Specify the max number of threads to parallelize tests at node level (defaults to #{@max_threads_nodes}).") do |nbr_threads|
122
+ @max_threads_nodes = Integer(nbr_threads)
123
+ end
124
+ options_parser.on('--max-threads-platforms NBR_THREADS', "Specify the max number of threads to parallelize tests at platform level (defaults to #{@max_threads_platforms}).") do |nbr_threads|
125
+ @max_threads_platforms = Integer(nbr_threads)
126
+ end
127
+ end
128
+
129
+ # Run the tests for a defined list of nodes selectors
130
+ #
131
+ # Parameters::
132
+ # * *nodes_selectors* (Array<Object>): List of nodes selectors on which tests should be run
133
+ # Result::
134
+ # * Integer: An exit code:
135
+ # * 0: Successful.
136
+ # * 1: Some tests have failed.
137
+ def run_tests(nodes_selectors)
138
+ # Compute the resolved list of tests to perform
139
+ @tests << :all if @tests.empty?
140
+ @tests = @tests_plugins.keys if @tests.include?(:all)
141
+ @tests.uniq!
142
+ @tests.sort!
143
+ @reports = [:stdout] if @reports.empty?
144
+ @reports = @reports_plugins.keys if @reports.include?(:all)
145
+ @reports.uniq!
146
+ @reports.sort!
147
+ unknown_tests = @tests - @tests_plugins.keys
148
+ raise "Unknown test names: #{unknown_tests.join(', ')}" unless unknown_tests.empty?
149
+ @nodes = @nodes_handler.select_nodes(nodes_selectors).uniq.sort
150
+
151
+ # Resolve the expected failures from the config.
152
+ # Expected failures at node level
153
+ # Hash< Symbol, Hash< String, String > >
154
+ # Hash< test_name, Hash< node, reason > >
155
+ @node_expected_failures = {}
156
+ @config.expected_failures.each do |expected_failure_info|
157
+ selected_nodes = @nodes_handler.select_from_nodes_selector_stack(expected_failure_info[:nodes_selectors_stack])
158
+ expected_failure_info[:tests].each do |test_name|
159
+ @node_expected_failures[test_name] = {} unless @node_expected_failures.key?(test_name)
160
+ selected_nodes.each do |node|
161
+ if @node_expected_failures[test_name].key?(node)
162
+ @node_expected_failures[test_name][node] += " + #{expected_failure_info[:reason]}"
163
+ else
164
+ @node_expected_failures[test_name][node] = expected_failure_info[:reason]
165
+ end
166
+ end
167
+ end
168
+ end
169
+ # Expected failures at platform level
170
+ # Hash< Symbol, Hash< String, String > >
171
+ # Hash< test_name, Hash< platform, reason > >
172
+ @platform_expected_failures = {}
173
+ @platforms_handler.known_platforms.each do |platform|
174
+ platform_nodes = platform.known_nodes
175
+ @node_expected_failures.each do |test_name, expected_failures_for_test|
176
+ if (platform_nodes - expected_failures_for_test.keys).empty?
177
+ # We have an expected failure for this test
178
+ @platform_expected_failures[test_name] = {} unless @platform_expected_failures.key?(test_name)
179
+ @platform_expected_failures[test_name][platform.name] = expected_failures_for_test.values.uniq.join(' + ')
180
+ end
181
+ end
182
+ end
183
+
184
+ # Keep a list of all tests that have run for the report
185
+ # Array< Test >
186
+ @tests_run = []
187
+
188
+ run_tests_global
189
+ run_tests_platform
190
+ run_tests_for_nodes
191
+ run_tests_connection_on_nodes
192
+ run_tests_on_check_nodes
193
+
194
+ @tested_platforms = @tests_run.map { |test| test.platform }.compact.uniq.sort
195
+
196
+ # Check that tests that were expected to fail did not succeed.
197
+ @tests_run.each do |test|
198
+ if test.executed?
199
+ expected_failure = test.expected_failure
200
+ if expected_failure
201
+ if test.errors.empty?
202
+ # Should have failed
203
+ error(
204
+ "Test #{test} was marked to fail (#{expected_failure}) but it succeeded. Please remove it from the expected failures in case the issue has been resolved.",
205
+ platform: test.platform,
206
+ node: test.node,
207
+ force_failure: true
208
+ )
209
+ else
210
+ out "Expected failure for #{test} (#{expected_failure}):\n#{test.errors.map { |error| " - #{error}" }.join("\n")}".yellow
211
+ end
212
+ end
213
+ end
214
+ end
215
+ # If all tests were executed, make sure that there are no expected failures that have not even been tested.
216
+ if @tests_plugins.keys - @tests == []
217
+ @node_expected_failures.each do |test_name, test_expected_failures|
218
+ test_expected_failures.each do |node, expected_failure|
219
+ # Check that a test has been run for this expected failure
220
+ unless @tests_run.find do |test|
221
+ test.name == test_name &&
222
+ (
223
+ (test.node.nil? && node == '') ||
224
+ (!test.node.nil? && node == test.node)
225
+ )
226
+ end
227
+ error("A test named #{test_name} for node #{node} was expected to fail (#{expected_failure}), but no test has been run. Please remove it from the expected failures if this expected failure is obsolete.")
228
+ end
229
+ end
230
+ end
231
+ end
232
+
233
+ # Produce reports
234
+ @reports.each do |report|
235
+ begin
236
+ @reports_plugins[report].new(@logger, @logger_stderr, @config, @nodes_handler, @nodes, @tested_platforms, @tests_run).report
237
+ rescue
238
+ log_error "Uncaught exception while producing report #{report}: #{$!}\n#{$!.backtrace.join("\n")}"
239
+ end
240
+ end
241
+
242
+ out
243
+ if @tests_run.all? { |test| test.errors.empty? || !test.expected_failure.nil? }
244
+ out '===== No unexpected errors ====='.green.bold
245
+ 0
246
+ else
247
+ out '===== Some errors were found. Check output. ====='.red.bold
248
+ 1
249
+ end
250
+ end
251
+
252
+ private
253
+
254
+ # Report an error, linked eventually to a given platform or node
255
+ #
256
+ # Parameters::
257
+ # * *message* (String): Error to be logged
258
+ # * *platform* (PlatformHandler or nil): PlatformHandler for a platform's test, or nil for a global or node test [default: nil]
259
+ # * *node* (String): Node for which the test is instantiated, or nil if global or platform [default: nil]
260
+ # * *force_failure* (Boolean): If true, then ignore expected failures for this error [default: false]
261
+ def error(message, platform: nil, node: nil, force_failure: false)
262
+ global_test = new_test(nil, platform: platform, node: node, ignore_expected_failure: force_failure)
263
+ global_test.errors << message
264
+ global_test.executed
265
+ @tests_run << global_test
266
+ end
267
+
268
+ # Instantiate a new test
269
+ #
270
+ # Parameters::
271
+ # * *test_name* (Symbol or nil): Test name to instantiate, or nil for unnamed tests
272
+ # * *platform* (PlatformHandler or nil): PlatformHandler for a platform's test, or nil for a global or node test [default: nil]
273
+ # * *node* (String or nil): Node for a node's test, or nil for a global or platform test [default: nil]
274
+ # * *ignore_expected_failure* (Boolean): If true, then ignore expected failures for this error [default: false]
275
+ # Result::
276
+ # * Test: Corresponding test
277
+ def new_test(test_name, platform: nil, node: nil, ignore_expected_failure: false)
278
+ (test_name.nil? ? Test : @tests_plugins[test_name]).new(
279
+ @logger,
280
+ @logger_stderr,
281
+ @config,
282
+ @cmd_runner,
283
+ @nodes_handler,
284
+ @deployer,
285
+ name: test_name.nil? ? :global : test_name,
286
+ platform: platform,
287
+ node: node,
288
+ expected_failure: if ignore_expected_failure
289
+ nil
290
+ elsif !node.nil?
291
+ # Node test
292
+ @node_expected_failures.dig(test_name.nil? ? 'global' : test_name, node)
293
+ elsif !platform.nil?
294
+ # Platform test
295
+ @platform_expected_failures.dig(test_name.nil? ? 'global' : test_name, platform.name)
296
+ else
297
+ # Global test
298
+ nil
299
+ end
300
+ )
301
+ end
302
+
303
+ # Run a test method on a set of test subjects.
304
+ # Provide harmonized logging, timings, exception handling...
305
+ # Make sure the tests should be run before running it.
306
+ #
307
+ # Parameters::
308
+ # * *title* (String): The title of such tests
309
+ # * *test_method* (Symbol): The test method to run (defined in tests plugins)
310
+ # * *test_subjects* (Array< Hash<Symbol,Object> >): List of test subjects. A test subject is defined as properties mapping the signature of the should_test_be_run_on and new_test methods.
311
+ # * *nbr_threads_max* (Integer): If > 1 then run the tests in parallel (with a limit in nuber of threads fixed by the value). Only when debug mode is false. [default: 1]
312
+ # * *tests_preparation* (Proc or nil): Code called to prepare tests, once test subjects have been selected, or nil if none [default: nil]
313
+ # * Parameters::
314
+ # * *selected_tests* (Array<Test>): List of selected tests.
315
+ # * *test_execution* (Proc): Code called to execute a test. Defaults to calling the test_method method on the test instance
316
+ # * Parameters::
317
+ # * *test* (Test): The test instance to be executed
318
+ def run_tests_on_subjects(
319
+ title,
320
+ test_method,
321
+ test_subjects,
322
+ nbr_threads_max: 1,
323
+ tests_preparation: nil,
324
+ test_execution: proc { |test| test.send(test_method) }
325
+ )
326
+ # Gather the list of tests to execute
327
+ tests_to_run = @tests.map do |test_name|
328
+ if @tests_plugins[test_name].method_defined?(test_method)
329
+ test_subjects.map do |test_subject|
330
+ should_test_be_run_on(test_name, **test_subject) ? new_test(test_name, **test_subject) : nil
331
+ end.compact
332
+ else
333
+ []
334
+ end
335
+ end.flatten
336
+ unless tests_to_run.empty?
337
+ section "Run #{tests_to_run.size} #{title}" do
338
+ tests_preparation.call(tests_to_run) unless tests_preparation.nil?
339
+ for_each_element_in(
340
+ tests_to_run,
341
+ parallel: !log_debug? && nbr_threads_max > 1,
342
+ nbr_threads_max: nbr_threads_max,
343
+ progress: "Run #{title}"
344
+ ) do |test|
345
+ test_category =
346
+ if test.platform.nil? && test.node.nil?
347
+ 'Global'
348
+ elsif test.node.nil?
349
+ "Platform #{test.platform.name}"
350
+ elsif test.platform.nil?
351
+ "Node #{test.node}"
352
+ else
353
+ "Platform #{test.platform.name} / Node #{test.node}"
354
+ end
355
+ out "[ #{Time.now.utc.strftime('%F %T')} ] - [ #{test_category} ] - [ #{test.name} ] - Start test..."
356
+ begin_time = Time.now
357
+ begin
358
+ test_execution.call(test)
359
+ rescue
360
+ test.error "Uncaught exception during test: #{$!}", $!.backtrace.join("\n")
361
+ end
362
+ end_time = Time.now
363
+ test.executed
364
+ out "[ #{Time.now.utc.strftime('%F %T')} ] - [ #{test_category} ] - [ #{test.name} ] - Test finished in #{end_time - begin_time} seconds."
365
+ end
366
+ @tests_run.concat(tests_to_run)
367
+ end
368
+ end
369
+ end
370
+
371
+ # Run tests that are global
372
+ def run_tests_global
373
+ run_tests_on_subjects(
374
+ 'global tests',
375
+ :test,
376
+ [{}]
377
+ )
378
+ end
379
+
380
+ # Run tests that are platform specific
381
+ def run_tests_platform
382
+ run_tests_on_subjects(
383
+ 'platform tests',
384
+ :test_on_platform,
385
+ @platforms_handler.known_platforms.map { |platform| { platform: platform } },
386
+ nbr_threads_max: @max_threads_platforms
387
+ )
388
+ end
389
+
390
+ # Timeout in seconds given to the connection itself
391
+ # Integer
392
+ CONNECTION_TIMEOUT = 20
393
+
394
+ # Timeout in seconds given to a command by default
395
+ # Integer
396
+ DEFAULT_CMD_TIMEOUT = 5
397
+
398
+ # Separator used to differentiate different commands executed in stdout.
399
+ # It's important that this separator could not be the result of any command output.
400
+ # String
401
+ CMD_SEPARATOR = '===== TEST COMMAND EXECUTION ===== Separator generated by Hybrid Platforms Conductor test framework ====='
402
+
403
+ # Run tests that are node specific and require a connection to the node
404
+ def run_tests_connection_on_nodes
405
+ run_tests_on_subjects(
406
+ 'connected tests',
407
+ :test_on_node,
408
+ @nodes.map { |node| { node: node } },
409
+ tests_preparation: proc do |selected_tests|
410
+ # Gather the list of commands to be run on each node with their corresponding test info, per node
411
+ # Hash< String, Array< [ String, Hash<Symbol,Object> ] > >
412
+ @cmds_to_run = {}
413
+ selected_tests.each do |test|
414
+ begin
415
+ test.test_on_node.each do |cmd, test_info|
416
+ test_info_normalized = test_info.is_a?(Hash) ? test_info.clone : { validator: test_info }
417
+ test_info_normalized[:timeout] = DEFAULT_CMD_TIMEOUT unless test_info_normalized.key?(:timeout)
418
+ test_info_normalized[:test] = test
419
+ @cmds_to_run[test.node] = [] unless @cmds_to_run.key?(test.node)
420
+ @cmds_to_run[test.node] << [
421
+ cmd,
422
+ test_info_normalized
423
+ ]
424
+ end
425
+ rescue
426
+ test.error "Uncaught exception during test preparation: #{$!}", $!.backtrace.join("\n")
427
+ test.executed
428
+ end
429
+ end
430
+ # Compute the timeout that will be applied, from the max timeout sum for every node that has tests to run
431
+ timeout = CONNECTION_TIMEOUT + @cmds_to_run.map do |_node, cmds_list|
432
+ cmds_list.inject(0) { |total_timeout, (_cmd, test_info)| test_info[:timeout] + total_timeout }
433
+ end.max
434
+ # Run commands on nodes, in grouped way to avoid too many connections, per node
435
+ # Hash< String, Array<String> >
436
+ @test_cmds = Hash[@cmds_to_run.map do |node, cmds_list|
437
+ [
438
+ node,
439
+ {
440
+ remote_bash: cmds_list.map do |(cmd, _test_info)|
441
+ [
442
+ "echo '#{CMD_SEPARATOR}'",
443
+ ">&2 echo '#{CMD_SEPARATOR}'",
444
+ cmd,
445
+ "echo \"$?\""
446
+ ]
447
+ end.flatten
448
+ }
449
+ ]
450
+ end]
451
+ section "Run test commands on #{@test_cmds.keys.size} connected nodes (timeout to #{timeout} secs)" do
452
+ start_time = Time.now
453
+ nbr_secs = nil
454
+ @actions_executor.max_threads = @max_threads_connection_on_nodes
455
+ @actions_result = @actions_executor.execute_actions(
456
+ @test_cmds,
457
+ concurrent: !log_debug?,
458
+ log_to_dir: nil,
459
+ log_to_stdout: log_debug?,
460
+ timeout: timeout
461
+ )
462
+ log_debug "----- Total commands executed in #{(Time.now - start_time).round(1)} secs"
463
+ end
464
+ end,
465
+ test_execution: proc do |test|
466
+ exit_status, stdout, stderr = @actions_result[test.node]
467
+ if exit_status.is_a?(Symbol)
468
+ test.error "Error while executing tests: #{exit_status}: #{stderr}"
469
+ else
470
+ log_debug <<~EOS
471
+ ----- Commands for #{test.node}:
472
+ #{@test_cmds[test.node][:remote_bash].join("\n")}
473
+ ----- STDOUT:
474
+ #{stdout}
475
+ ----- STDERR:
476
+ #{stderr}
477
+ -----
478
+ EOS
479
+ # Skip the first section, as it can contain SSH banners
480
+ cmd_stdouts = stdout.split("#{CMD_SEPARATOR}\n")[1..-1]
481
+ cmd_stdouts = [] if cmd_stdouts.nil?
482
+ cmd_stderrs = stderr.split("#{CMD_SEPARATOR}\n")[1..-1]
483
+ cmd_stderrs = [] if cmd_stderrs.nil?
484
+ @cmds_to_run[test.node].zip(cmd_stdouts, cmd_stderrs).each do |(cmd, test_info), cmd_stdout, cmd_stderr|
485
+ # Find the section that corresponds to this test
486
+ if test_info[:test] == test
487
+ cmd_stdout = '' if cmd_stdout.nil?
488
+ cmd_stderr = '' if cmd_stderr.nil?
489
+ stdout_lines = cmd_stdout.split("\n")
490
+ # Last line of stdout is the return code
491
+ return_code = stdout_lines.empty? ? :command_cant_run : Integer(stdout_lines.last)
492
+ test.error "Command '#{cmd}' returned error code #{return_code}", "----- STDOUT:\n#{stdout_lines[0..-2].join("\n")}\n----- STDERR:\n#{cmd_stderr}" unless return_code == 0
493
+ test_info[:validator].call(stdout_lines[0..-2], cmd_stderr.split("\n"), return_code)
494
+ end
495
+ end
496
+ end
497
+ end
498
+ )
499
+ end
500
+
501
+ # Run tests that are node specific
502
+ def run_tests_for_nodes
503
+ run_tests_on_subjects(
504
+ 'node tests',
505
+ :test_for_node,
506
+ @nodes.map { |node| { node: node } },
507
+ nbr_threads_max: @max_threads_nodes
508
+ )
509
+ end
510
+
511
+ # Timeout in seconds given to a check-node run
512
+ # Integer
513
+ CHECK_NODE_TIMEOUT = 30 * 60 # 30 minutes
514
+
515
+ # Run tests that use check-node results
516
+ def run_tests_on_check_nodes
517
+ run_tests_on_subjects(
518
+ 'check-node tests',
519
+ :test_on_check_node,
520
+ @nodes.map { |node| { node: node } },
521
+ tests_preparation: proc do |selected_tests|
522
+ nodes_to_test = selected_tests.map { |test| test.node }.uniq.sort
523
+ @outputs =
524
+ if @skip_run
525
+ Hash[nodes_to_test.map do |node|
526
+ run_log_file_name = "#{@config.hybrid_platforms_dir}/run_logs/#{node}.stdout"
527
+ [
528
+ node,
529
+ # TODO: Find a way to also save stderr and the status code
530
+ [0, File.exists?(run_log_file_name) ? File.read(run_log_file_name) : nil, '']
531
+ ]
532
+ end]
533
+ else
534
+ # Why-run deploy on all nodes
535
+ @deployer.concurrent_execution = !log_debug?
536
+ @deployer.use_why_run = true
537
+ @deployer.timeout = CHECK_NODE_TIMEOUT
538
+ begin
539
+ @deployer.deploy_on(nodes_to_test)
540
+ rescue
541
+ # If an exception occurred, make sure all concerned nodes are reporting the error
542
+ nodes_to_test.each do |node|
543
+ error "Error while checking check-node output: #{$!}#{log_debug? ? "\n#{$!.backtrace.join("\n")}" : ''}", node: node
544
+ end
545
+ {}
546
+ end
547
+ end
548
+ end,
549
+ test_execution: proc do |test|
550
+ exit_status, stdout, stderr = @outputs[test.node]
551
+ if stdout.nil?
552
+ test.error 'No check-node log file found despite the run of check-node.'
553
+ elsif stdout.is_a?(Symbol)
554
+ test.error "Check-node run failed: #{stdout}."
555
+ else
556
+ test.error "Check-node returned error code #{exit_status}" unless exit_status == 0
557
+ begin
558
+ test.test_on_check_node(stdout, stderr, exit_status)
559
+ rescue
560
+ test.error "Uncaught exception during test: #{$!}", $!.backtrace.join("\n")
561
+ end
562
+ end
563
+ end
564
+ )
565
+ end
566
+
567
+ # Should the given test name be run on a given node or platform?
568
+ #
569
+ # Parameters::
570
+ # * *test_name* (String): The test name.
571
+ # * *node* (String or nil): Node name, or nil for a platform or global test. [default: nil]
572
+ # * *platform* (PlatformHandler or nil): Platform or nil for a node or global test. [default: nil]
573
+ # Result::
574
+ # * Boolean: Should the given test name be run on a given node or platform?
575
+ def should_test_be_run_on(test_name, node: nil, platform: nil)
576
+ if !node.nil?
577
+ (@tests_plugins[test_name].only_on_nodes || [node]).any? do |allowed_node|
578
+ allowed_node.is_a?(String) ? allowed_node == node : node.match(allowed_node)
579
+ end
580
+ elsif !platform.nil?
581
+ (@tests_plugins[test_name].only_on_platforms || [platform.platform_type]).include?(platform.platform_type)
582
+ else
583
+ # Global tests should always be run
584
+ true
585
+ end
586
+ end
587
+
588
+ end
589
+
590
+ end