hybrid_platforms_conductor 32.3.6

Sign up to get free protection for your applications and to get access to all the features.
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