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,127 @@
1
+ require 'netrc'
2
+ require 'uri'
3
+ require 'hybrid_platforms_conductor/logger_helpers'
4
+
5
+ module HybridPlatformsConductor
6
+
7
+ # Give a secured and harmonized way to access credentials for a given service.
8
+ # It makes sure to remove passwords from memory for hardened security (this way if a vulnerability allows an attacker to dump the memory it won't get passwords).
9
+ # It gets credentials from the following sources:
10
+ # * Environment variables
11
+ # * Netrc file
12
+ class Credentials
13
+
14
+ include LoggerHelpers
15
+
16
+ # Get access to credentials and make sure they are wiped out from memory when client code ends.
17
+ # To ensure password safety, never store the password in a scope beyond the client code's Proc.
18
+ #
19
+ # Parameters::
20
+ # * *id* (Symbol): Credential ID
21
+ # * *logger* (Logger): Logger to be used
22
+ # * *logger_stderr* (Logger): Logger to be used for stderr
23
+ # * *url* (String or nil): The URL for which we want the credentials, or nil if not associated to a URL [default: nil]
24
+ # * Proc: Client code called with credentials provided
25
+ # * Parameters::
26
+ # * *user* (String or nil): User name, or nil if none
27
+ # * *password* (String or nil): Password, or nil if none.
28
+ # !!! Never store this password in a scope broader than the client code itself !!!
29
+ def self.with_credentials_for(id, logger, logger_stderr, url: nil)
30
+ credentials = Credentials.new(id, url: url, logger: logger, logger_stderr: logger_stderr)
31
+ begin
32
+ yield credentials.user, credentials.password
33
+ ensure
34
+ credentials.clear_password
35
+ end
36
+ end
37
+
38
+ # Constructor
39
+ #
40
+ # Parameters::
41
+ # * *id* (Symbol): Credential ID
42
+ # * *url* (String or nil): The URL for which we want the credentials, or nil if not associated to a URL [default: nil]
43
+ # * *logger* (Logger): Logger to be used [default = Logger.new(STDOUT)]
44
+ # * *logger_stderr* (Logger): Logger to be used for stderr [default = Logger.new(STDERR)]
45
+ def initialize(id, url: nil, logger: Logger.new(STDOUT), logger_stderr: Logger.new(STDERR))
46
+ init_loggers(logger, logger_stderr)
47
+ @id = id
48
+ @url = url
49
+ @user = nil
50
+ @password = nil
51
+ @retrieved = false
52
+ end
53
+
54
+ # Provide a helper to clear password from memory for security.
55
+ # To be used when the client knows it won't use the password anymore.
56
+ def clear_password
57
+ @password.replace('gotyou!' * 100) unless @password.nil?
58
+ GC.start
59
+ end
60
+
61
+ # Get the associated user
62
+ #
63
+ # Result::
64
+ # * String or nil: The user name, or nil if none
65
+ def user
66
+ retrieve_credentials
67
+ @user
68
+ end
69
+
70
+ # Get the associated password
71
+ #
72
+ # Result::
73
+ # * String or nil: The password, or nil if none
74
+ def password
75
+ retrieve_credentials
76
+ @password
77
+ end
78
+
79
+ private
80
+
81
+ # Retrieve credentials in @user and @password.
82
+ # Do it only once.
83
+ # Make sure the retrieved credentials are not linked to other objects in memory, so that we can remove any other trace of secrets.
84
+ def retrieve_credentials
85
+ unless @retrieved
86
+ # Check environment variables
87
+ @user = ENV["hpc_user_for_#{@id}"].dup
88
+ @password = ENV["hpc_password_for_#{@id}"].dup
89
+ if @user.nil? || @user.empty? || @password.nil? || @password.empty?
90
+ log_debug "[ Credentials for #{@id} ] - Credentials not found from environment variables."
91
+ if @url.nil?
92
+ log_debug "[ Credentials for #{@id} ] - No URL associated to this credentials, so .netrc can't be used."
93
+ else
94
+ # Check Netrc
95
+ netrc = ::Netrc.read
96
+ begin
97
+ netrc_user, netrc_password = netrc[URI.parse(@url).host.downcase]
98
+ if netrc_user.nil?
99
+ log_debug "[ Credentials for #{@id} ] - No credentials retrieved from .netrc."
100
+ # TODO: Add more credentials source if needed here
101
+ log_warn "[ Credentials for #{@id} ] - Unable to get credentials for #{@id} (URL: #{@url})."
102
+ else
103
+ @user = netrc_user.dup
104
+ @password = netrc_password.dup
105
+ log_debug "[ Credentials for #{@id} ] - Credentials retrieved from .netrc using #{@url}."
106
+ end
107
+ ensure
108
+ # Make sure the password does not stay in Netrc memory
109
+ # Wipe out any memory trace that might contain passwords in clear
110
+ netrc.instance_variable_get(:@data).each do |data_line|
111
+ data_line.each do |data_string|
112
+ data_string.replace('GotYou!!!' * 100)
113
+ end
114
+ end
115
+ netrc = nil
116
+ end
117
+ end
118
+ else
119
+ log_debug "[ Credentials for #{@id} ] - Credentials retrieved from environment variables."
120
+ end
121
+ GC.start
122
+ end
123
+ end
124
+
125
+ end
126
+
127
+ end
@@ -0,0 +1,42 @@
1
+ require 'monitor'
2
+
3
+ module HybridPlatformsConductor
4
+
5
+ # Implement a global monitor to protect accesses to the current directory.
6
+ # This is needed as the OS concept of current directory is not thread-safe: it is linked to a process, and we need to have thread-safe code running (mainly for parallel deployment and tests).
7
+ module CurrentDirMonitor
8
+
9
+ class << self
10
+ attr_reader :monitor
11
+ end
12
+
13
+ @monitor = Monitor.new
14
+
15
+ # Decorate a given method with the monitor.
16
+ #
17
+ # Parameters::
18
+ # * *module_to_decorate* (Module): The module including the method to decorate
19
+ # * *method_name* (Symbol): The method name to be decorated
20
+ def self.decorate_method(module_to_decorate, method_name)
21
+ original_method_name = "__hpc__#{method_name}__undecorated__".to_sym
22
+ module_to_decorate.alias_method original_method_name, method_name
23
+ module_to_decorate.define_method(method_name) do |*args, &block|
24
+ result = nil
25
+ CurrentDirMonitor.monitor.synchronize do
26
+ # puts "TID #{Thread.current.object_id} from #{caller[2]} - Current dir monitor taken from #{Dir.pwd}"
27
+ result = self.send(original_method_name, *args, &block)
28
+ # puts "TID #{Thread.current.object_id} from #{caller[2]} - Current dir monitor released back to #{Dir.pwd}"
29
+ end
30
+ result
31
+ end
32
+ end
33
+
34
+ end
35
+
36
+ # List here all methods that need to be protected by this monitor.
37
+ CurrentDirMonitor.decorate_method(Dir.singleton_class, :chdir)
38
+ CurrentDirMonitor.decorate_method(File.singleton_class, :expand_path)
39
+ CurrentDirMonitor.decorate_method(IO.singleton_class, :popen)
40
+ CurrentDirMonitor.decorate_method(Git::Lib, :command)
41
+
42
+ end
@@ -0,0 +1,598 @@
1
+ require 'tmpdir'
2
+ require 'futex'
3
+ require 'json'
4
+ require 'securerandom'
5
+ require 'time'
6
+ require 'thread'
7
+ require 'hybrid_platforms_conductor/actions_executor'
8
+ require 'hybrid_platforms_conductor/cmd_runner'
9
+ require 'hybrid_platforms_conductor/executable'
10
+ require 'hybrid_platforms_conductor/logger_helpers'
11
+ require 'hybrid_platforms_conductor/nodes_handler'
12
+ require 'hybrid_platforms_conductor/services_handler'
13
+ require 'hybrid_platforms_conductor/plugins'
14
+ require 'hybrid_platforms_conductor/thycotic'
15
+
16
+ module HybridPlatformsConductor
17
+
18
+ # Gives ways to deploy on several nodes
19
+ class Deployer
20
+
21
+ include LoggerHelpers
22
+
23
+ # Do we use why-run mode while deploying? [default = false]
24
+ # Boolean
25
+ attr_accessor :use_why_run
26
+
27
+ # Timeout (in seconds) to be used for each deployment, or nil for no timeout [default = nil]
28
+ # Integer or nil
29
+ attr_accessor :timeout
30
+
31
+ # Concurrent execution of the deployment? [default = false]
32
+ # Boolean
33
+ attr_accessor :concurrent_execution
34
+
35
+ # The list of JSON secrets
36
+ # Array<Hash>
37
+ attr_accessor :secrets
38
+
39
+ # Are we deploying in a local environment?
40
+ # Boolean
41
+ attr_accessor :local_environment
42
+
43
+ # Number of retries to do in case of non-deterministic errors during deployment
44
+ # Integer
45
+ attr_accessor :nbr_retries_on_error
46
+
47
+ # Constructor
48
+ #
49
+ # Parameters::
50
+ # * *logger* (Logger): Logger to be used [default: Logger.new(STDOUT)]
51
+ # * *logger_stderr* (Logger): Logger to be used for stderr [default: Logger.new(STDERR)]
52
+ # * *config* (Config): Config to be used. [default: Config.new]
53
+ # * *cmd_runner* (CmdRunner): Command executor to be used. [default: CmdRunner.new]
54
+ # * *nodes_handler* (NodesHandler): Nodes handler to be used. [default: NodesHandler.new]
55
+ # * *actions_executor* (ActionsExecutor): Actions Executor to be used. [default: ActionsExecutor.new]
56
+ # * *services_handler* (ServicesHandler): Services Handler to be used. [default: ServicesHandler.new]
57
+ def initialize(
58
+ logger: Logger.new(STDOUT),
59
+ logger_stderr: Logger.new(STDERR),
60
+ config: Config.new,
61
+ cmd_runner: CmdRunner.new,
62
+ nodes_handler: NodesHandler.new,
63
+ actions_executor: ActionsExecutor.new,
64
+ services_handler: ServicesHandler.new
65
+ )
66
+ init_loggers(logger, logger_stderr)
67
+ @config = config
68
+ @cmd_runner = cmd_runner
69
+ @nodes_handler = nodes_handler
70
+ @actions_executor = actions_executor
71
+ @services_handler = services_handler
72
+ @secrets = []
73
+ @provisioners = Plugins.new(:provisioner, logger: @logger, logger_stderr: @logger_stderr)
74
+ # Default values
75
+ @use_why_run = false
76
+ @timeout = nil
77
+ @concurrent_execution = false
78
+ @local_environment = false
79
+ @nbr_retries_on_error = 0
80
+ end
81
+
82
+ # Complete an option parser with options meant to control this Deployer
83
+ #
84
+ # Parameters::
85
+ # * *options_parser* (OptionParser): The option parser to complete
86
+ # * *parallel_switch* (Boolean): Do we allow parallel execution to be switched? [default = true]
87
+ # * *why_run_switch* (Boolean): Do we allow the why-run mode to be switched? [default = false]
88
+ # * *timeout_options* (Boolean): Do we allow timeout options? [default = true]
89
+ def options_parse(options_parser, parallel_switch: true, why_run_switch: false, timeout_options: true)
90
+ options_parser.separator ''
91
+ options_parser.separator 'Deployer options:'
92
+ options_parser.on(
93
+ '-e', '--secrets SECRETS_LOCATION',
94
+ 'Specify a secrets location. Can be specified several times. Location can be:',
95
+ '* Local path to a JSON file',
96
+ '* URL of the form http[s]://<url>:<secret_id> to get a secret JSON file from a Thycotic Secret Server at the given URL.'
97
+ ) do |secrets_location|
98
+ @secrets << JSON.parse(
99
+ if secrets_location =~ /^(https?:\/\/.+):(\d+)$/
100
+ url = $1
101
+ secret_id = $2
102
+ secret = nil
103
+ Thycotic.with_thycotic(url, @logger, @logger_stderr) do |thycotic|
104
+ secret_file_item_id = thycotic.get_secret(secret_id).dig(:secret, :items, :secret_item, :id)
105
+ raise "Unable to fetch secret file ID #{secrets_location}" if secret_file_item_id.nil?
106
+ secret = thycotic.download_file_attachment_by_item_id(secret_id, secret_file_item_id)
107
+ raise "Unable to fetch secret file attachment from #{secrets_location}" if secret.nil?
108
+ end
109
+ secret
110
+ else
111
+ raise "Missing secret file: #{secrets_location}" unless File.exist?(secrets_location)
112
+ File.read(secrets_location)
113
+ end
114
+ )
115
+ end
116
+ options_parser.on('-p', '--parallel', 'Execute the commands in parallel (put the standard output in files <hybrid-platforms-dir>/run_logs/*.stdout)') do
117
+ @concurrent_execution = true
118
+ end if parallel_switch
119
+ options_parser.on('-t', '--timeout SECS', "Timeout in seconds to wait for each chef run. Only used in why-run mode. (defaults to #{@timeout.nil? ? 'no timeout' : @timeout})") do |nbr_secs|
120
+ @timeout = nbr_secs.to_i
121
+ end if timeout_options
122
+ options_parser.on('-W', '--why-run', 'Use the why-run mode to see what would be the result of the deploy instead of deploying it for real.') do
123
+ @use_why_run = true
124
+ end if why_run_switch
125
+ options_parser.on('--retries-on-error NBR', "Number of retries in case of non-deterministic errors (defaults to #{@nbr_retries_on_error})") do |nbr_retries|
126
+ @nbr_retries_on_error = nbr_retries.to_i
127
+ end
128
+ end
129
+
130
+ # Validate that parsed parameters are valid
131
+ def validate_params
132
+ raise 'Can\'t have a timeout unless why-run mode. Please don\'t use --timeout without --why-run.' if !@timeout.nil? && !@use_why_run
133
+ end
134
+
135
+ # String: File used as a Futex for packaging
136
+ PACKAGING_FUTEX_FILE = "#{Dir.tmpdir}/hpc_packaging"
137
+
138
+ # Integer: Timeout in seconds to get the packaging Futex
139
+ PACKAGING_FUTEX_TIMEOUT = 60
140
+
141
+ # Deploy on a given list of nodes selectors.
142
+ # The workflow is the following:
143
+ # 1. Package the services to be deployed, considering the nodes, services and context (options, secrets, environment...)
144
+ # 2. Deploy on the nodes (once per node to be deployed)
145
+ # 3. Save deployment logs (in case of real deployment)
146
+ #
147
+ # Parameters::
148
+ # * *nodes_selectors* (Array<Object>): The list of nodes selectors we will deploy to.
149
+ # Result::
150
+ # * Hash<String, [Integer or Symbol, String, String]>: Exit status code (or Symbol in case of error or dry run), standard output and error for each node that has been deployed.
151
+ def deploy_on(*nodes_selectors)
152
+ # Get the sorted list of services to be deployed, per node
153
+ # Hash<String, Array<String> >
154
+ services_to_deploy = Hash[@nodes_handler.select_nodes(nodes_selectors.flatten).map do |node|
155
+ [node, @nodes_handler.get_services_of(node)]
156
+ end]
157
+
158
+ # Get the secrets to be deployed
159
+ secrets = {}
160
+ @secrets.each do |secret_json|
161
+ secrets.merge!(secret_json) do |key, value1, value2|
162
+ raise "Secret #{key} has conflicting values between different secret JSON files." if value1 != value2
163
+ value1
164
+ end
165
+ end
166
+
167
+ # Check that we are allowed to deploy
168
+ unless @use_why_run
169
+ reason_for_interdiction = @services_handler.deploy_allowed?(
170
+ services: services_to_deploy,
171
+ secrets: secrets,
172
+ local_environment: @local_environment
173
+ )
174
+ raise "Deployment not allowed: #{reason_for_interdiction}" unless reason_for_interdiction.nil?
175
+ end
176
+
177
+ # Package the deployment
178
+ # Protect packaging by a Futex
179
+ Futex.new(PACKAGING_FUTEX_FILE, timeout: PACKAGING_FUTEX_TIMEOUT).open do
180
+ section 'Packaging deployment' do
181
+ @services_handler.package(
182
+ services: services_to_deploy,
183
+ secrets: secrets,
184
+ local_environment: @local_environment
185
+ )
186
+ end
187
+ end
188
+
189
+ # Prepare the deployment as a whole, before getting individual deployment actions.
190
+ # Do this after packaging, this way we ensure that services packaging cannot depend on the way deployment will be performed.
191
+ @services_handler.prepare_for_deploy(
192
+ services: services_to_deploy,
193
+ secrets: secrets,
194
+ local_environment: @local_environment,
195
+ why_run: @use_why_run
196
+ )
197
+
198
+ # Launch deployment processes
199
+ results = {}
200
+
201
+ section "#{@use_why_run ? 'Checking' : 'Deploying'} on #{services_to_deploy.keys.size} nodes" do
202
+ # Prepare all the control masters here, as they will be reused for the whole process, including mutexes, deployment and logs saving
203
+ @actions_executor.with_connections_prepared_to(services_to_deploy.keys, no_exception: true) do
204
+
205
+ nbr_retries = @nbr_retries_on_error
206
+ remaining_nodes_to_deploy = services_to_deploy.keys
207
+ while nbr_retries >= 0 && !remaining_nodes_to_deploy.empty?
208
+ last_deploy_results = deploy(services_to_deploy.slice(*remaining_nodes_to_deploy))
209
+ if nbr_retries > 0
210
+ # Check if we need to retry deployment on some nodes
211
+ # Only parse the last deployment attempt logs
212
+ retriable_nodes = Hash[
213
+ remaining_nodes_to_deploy.
214
+ map do |node|
215
+ exit_status, stdout, stderr = last_deploy_results[node]
216
+ if exit_status == 0
217
+ nil
218
+ else
219
+ retriable_errors = retriable_errors_from(node, exit_status, stdout, stderr)
220
+ if retriable_errors.empty?
221
+ nil
222
+ else
223
+ # Log the issue in the stderr of the deployment
224
+ stderr << "!!! #{retriable_errors.size} retriable errors detected in this deployment:\n#{retriable_errors.map { |error| "* #{error}" }.join("\n")}\n"
225
+ [node, retriable_errors]
226
+ end
227
+ end
228
+ end.
229
+ compact
230
+ ]
231
+ unless retriable_nodes.empty?
232
+ log_warn <<~EOS.strip
233
+ Retry deployment for #{retriable_nodes.size} nodes as they got non-deterministic errors (#{nbr_retries} retries remaining):
234
+ #{retriable_nodes.map { |node, retriable_errors| " * #{node}:\n#{retriable_errors.map { |error| " - #{error}" }.join("\n")}" }.join("\n")}
235
+ EOS
236
+ end
237
+ remaining_nodes_to_deploy = retriable_nodes.keys
238
+ end
239
+ # Merge deployment results
240
+ results.merge!(last_deploy_results) do |node, (exit_status_1, stdout_1, stderr_1), (exit_status_2, stdout_2, stderr_2)|
241
+ [
242
+ exit_status_2,
243
+ <<~EOS,
244
+ #{stdout_1}
245
+ Deployment exit status code: #{exit_status_1}
246
+ !!! Retry deployment due to non-deterministic error (#{nbr_retries} remaining attempts)...
247
+ #{stdout_2}
248
+ EOS
249
+ <<~EOS
250
+ #{stderr_1}
251
+ !!! Retry deployment due to non-deterministic error (#{nbr_retries} remaining attempts)...
252
+ #{stderr_2}
253
+ EOS
254
+ ]
255
+ end
256
+ nbr_retries -= 1
257
+ end
258
+
259
+ end
260
+ end
261
+ results
262
+ end
263
+
264
+ # Provision a test instance for a given node.
265
+ #
266
+ # Parameters::
267
+ # * *provisioner_id* (Symbol): The provisioner ID to be used
268
+ # * *node* (String): The node for which we want the image
269
+ # * *environment* (String): An ID to differentiate different running instances for the same node
270
+ # * *reuse_instance* (Boolean): Do we reuse an eventual existing instance? [default: false]
271
+ # * Proc: Code called when the container is ready. The container will be stopped at the end of execution.
272
+ # * Parameters::
273
+ # * *deployer* (Deployer): A new Deployer configured to override access to the node through the Docker container
274
+ # * *instance* (Provisioner): The provisioned instance
275
+ def with_test_provisioned_instance(provisioner_id, node, environment:, reuse_instance: false)
276
+ # Add the user to the environment to better track belongings on shared provisioners
277
+ environment = "#{@cmd_runner.whoami}_#{environment}"
278
+ # Add PID, TID and a random number to the ID to make sure other containers used by other runs are not being reused.
279
+ environment << "_#{Process.pid}_#{Thread.current.object_id}_#{SecureRandom.hex(8)}" unless reuse_instance
280
+ # Create different NodesHandler and Deployer to handle this Docker container in place of the real node.
281
+ sub_logger, sub_logger_stderr =
282
+ if log_debug?
283
+ [@logger, @logger_stderr]
284
+ else
285
+ [Logger.new(StringIO.new, level: :info), Logger.new(StringIO.new, level: :info)]
286
+ end
287
+ begin
288
+ sub_executable = Executable.new(logger: sub_logger, logger_stderr: sub_logger_stderr)
289
+ instance = @provisioners[provisioner_id].new(
290
+ node,
291
+ environment: environment,
292
+ logger: @logger,
293
+ logger_stderr: @logger_stderr,
294
+ config: @config,
295
+ cmd_runner: @cmd_runner,
296
+ # Here we use the NodesHandler that will be bound to the sub-Deployer only, as the node's metadata might be modified by the Provisioner.
297
+ nodes_handler: sub_executable.nodes_handler,
298
+ actions_executor: @actions_executor
299
+ )
300
+ instance.with_running_instance(stop_on_exit: true, destroy_on_exit: !reuse_instance, port: 22) do
301
+ actions_executor = sub_executable.actions_executor
302
+ deployer = sub_executable.deployer
303
+ # Setup test environment for this container
304
+ actions_executor.connector(:ssh).ssh_user = 'root'
305
+ actions_executor.connector(:ssh).passwords[node] = 'root_pwd'
306
+ deployer.local_environment = true
307
+ # Ignore secrets that might have been given: in Docker containers we always use dummy secrets
308
+ deployer.secrets = [JSON.parse(File.read("#{@config.hybrid_platforms_dir}/dummy_secrets.json"))]
309
+ yield deployer, instance
310
+ end
311
+ rescue
312
+ # Make sure Docker logs are being output to better investigate errors if we were not already outputing them in debug mode
313
+ stdouts = sub_executable.stdouts_to_s
314
+ log_error "[ #{node}/#{environment} ] - Encountered unhandled exception #{$!}\n#{$!.backtrace.join("\n")}\n-----\n#{stdouts}" unless stdouts.nil?
315
+ raise
316
+ end
317
+ end
318
+
319
+ # Get deployment info from a list of nodes selectors
320
+ #
321
+ # Parameters::
322
+ # * *nodes* (Array<String>): Nodes to get info from
323
+ # Result::
324
+ # * Hash<String, Hash<Symbol,Object>: The deployed info, per node name.
325
+ # Properties are defined by the Deployer#save_logs method, and additionally to them the following properties can be set:
326
+ # * *error* (String): Optional property set in case of error
327
+ def deployment_info_from(*nodes)
328
+ @actions_executor.max_threads = 64
329
+ Hash[@actions_executor.
330
+ execute_actions(
331
+ Hash[nodes.flatten.map do |node|
332
+ [
333
+ node,
334
+ { remote_bash: "cd /var/log/deployments && ls -t | head -1 | xargs sed '/===== STDOUT =====/q'" }
335
+ ]
336
+ end],
337
+ log_to_stdout: false,
338
+ concurrent: true,
339
+ timeout: 10,
340
+ progress_name: 'Getting deployment info'
341
+ ).
342
+ map do |node, (exit_status, stdout, stderr)|
343
+ # Expected format for stdout:
344
+ # Property1: Value1
345
+ # ...
346
+ # PropertyN: ValueN
347
+ # ===== STDOUT =====
348
+ # ...
349
+ deploy_info = {}
350
+ if exit_status.is_a?(Symbol)
351
+ deploy_info[:error] = "Error: #{exit_status}\n#{stderr}"
352
+ else
353
+ stdout_lines = stdout.split("\n")
354
+ if stdout_lines.first =~ /No such file or directory/
355
+ deploy_info[:error] = '/var/log/deployments missing'
356
+ else
357
+ stdout_lines.each do |line|
358
+ if line =~ /^([^:]+): (.+)$/
359
+ key_str, value = $1, $2
360
+ key = key_str.to_sym
361
+ # Type-cast some values
362
+ case key_str
363
+ when 'date'
364
+ # Date and time values
365
+ # Thu Nov 23 18:43:01 UTC 2017
366
+ deploy_info[key] = Time.parse(value)
367
+ when 'debug'
368
+ # Boolean values
369
+ # Yes
370
+ deploy_info[key] = (value == 'Yes')
371
+ when /^diff_files_.+$/, 'services'
372
+ # Array of strings
373
+ # my_file.txt, other_file.txt
374
+ deploy_info[key] = value.split(', ')
375
+ else
376
+ deploy_info[key] = value
377
+ end
378
+ else
379
+ deploy_info[:unknown_lines] = [] unless deploy_info.key?(:unknown_lines)
380
+ deploy_info[:unknown_lines] << line
381
+ end
382
+ end
383
+ end
384
+ end
385
+ [
386
+ node,
387
+ deploy_info
388
+ ]
389
+ end
390
+ ]
391
+ end
392
+
393
+ # Parse stdout and stderr of a given deploy run and get the list of tasks with their status
394
+ #
395
+ # Parameters::
396
+ # * *node* (String): Node for which this deploy run has been done.
397
+ # * *stdout* (String): stdout to be parsed.
398
+ # * *stderr* (String): stderr to be parsed.
399
+ # Result::
400
+ # * Array< Hash<Symbol,Object> >: List of task properties. The following properties should be returned, among free ones:
401
+ # * *name* (String): Task name
402
+ # * *status* (Symbol): Task status. Should be on of:
403
+ # * *:changed*: The task has been changed
404
+ # * *:identical*: The task has not been changed
405
+ # * *diffs* (String): Differences, if any
406
+ def parse_deploy_output(node, stdout, stderr)
407
+ @services_handler.parse_deploy_output(stdout, stderr).map { |deploy_info| deploy_info[:tasks] }.flatten
408
+ end
409
+
410
+ private
411
+
412
+ # Get the list of retriable errors a node got from deployment logs.
413
+ # Useful to know if an error is non-deterministic (due to external and temporary factors).
414
+ #
415
+ # Parameters::
416
+ # * *node* (String): Node having the error
417
+ # * *exit_status* (Integer or Symbol): The deployment exit status
418
+ # * *stdout* (String): Deployment stdout
419
+ # * *stderr* (String): Deployment stderr
420
+ # Result::
421
+ # * Array<String>: List of retriable errors that have been matched
422
+ def retriable_errors_from(node, exit_status, stdout, stderr)
423
+ # List of retriable errors for this node, as exact string match or regexps.
424
+ # Array<String or Regexp>
425
+ retriable_errors_on_stdout = []
426
+ retriable_errors_on_stderr = []
427
+ @nodes_handler.select_confs_for_node(node, @config.retriable_errors).each do |retriable_error_info|
428
+ retriable_errors_on_stdout.concat(retriable_error_info[:errors_on_stdout]) if retriable_error_info.key?(:errors_on_stdout)
429
+ retriable_errors_on_stderr.concat(retriable_error_info[:errors_on_stderr]) if retriable_error_info.key?(:errors_on_stderr)
430
+ end
431
+ {
432
+ stdout => retriable_errors_on_stdout,
433
+ stderr => retriable_errors_on_stderr
434
+ }.map do |output, retriable_errors|
435
+ retriable_errors.map do |error|
436
+ if error.is_a?(String)
437
+ output.include?(error) ? error : nil
438
+ else
439
+ error_match = output.match error
440
+ error_match ? "/#{error.source}/ matched '#{error_match[0]}'" : nil
441
+ end
442
+ end.compact
443
+ end.flatten.uniq
444
+ end
445
+
446
+ # Deploy on all the nodes.
447
+ #
448
+ # Parameters::
449
+ # * *services* (Hash<String, Array<String>>): List of services to be deployed, per node
450
+ # Result::
451
+ # * Hash<String, [Integer or Symbol, String, String]>: Exit status code (or Symbol in case of error or dry run), standard output and error for each node.
452
+ def deploy(services)
453
+ outputs = {}
454
+
455
+ # Get the ssh user directly from the connector
456
+ ssh_user = @actions_executor.connector(:ssh).ssh_user
457
+
458
+ # Deploy for real
459
+ @nodes_handler.prefetch_metadata_of services.keys, :image
460
+ outputs = @actions_executor.execute_actions(
461
+ Hash[services.map do |node, node_services|
462
+ image_id = @nodes_handler.get_image_of(node)
463
+ # Install My_company corporate certificates if present
464
+ certificate_actions =
465
+ if @local_environment && ENV['hpc_certificates']
466
+ if File.exist?(ENV['hpc_certificates'])
467
+ log_debug "Deploy certificates from #{ENV['hpc_certificates']}"
468
+ case image_id
469
+ when 'debian_9', 'debian_10'
470
+ [
471
+ {
472
+ remote_bash: "#{ssh_user == 'root' ? '' : 'sudo '}apt update && #{ssh_user == 'root' ? '' : 'sudo '}apt install -y ca-certificates"
473
+ },
474
+ {
475
+ scp: {
476
+ ENV['hpc_certificates'] => '/usr/local/share/ca-certificates',
477
+ :sudo => ssh_user != 'root'
478
+ },
479
+ remote_bash: "#{ssh_user == 'root' ? '' : 'sudo '}update-ca-certificates"
480
+ }
481
+ ]
482
+ when 'centos_7'
483
+ [
484
+ {
485
+ remote_bash: "#{ssh_user == 'root' ? '' : 'sudo '}yum install -y ca-certificates"
486
+ },
487
+ {
488
+ scp: Hash[Dir.glob("#{ENV['hpc_certificates']}/*.crt").map do |cert_file|
489
+ [
490
+ cert_file,
491
+ '/etc/pki/ca-trust/source/anchors'
492
+ ]
493
+ end].merge(sudo: ssh_user != 'root'),
494
+ remote_bash: [
495
+ "#{ssh_user == 'root' ? '' : 'sudo '}update-ca-trust enable",
496
+ "#{ssh_user == 'root' ? '' : 'sudo '}update-ca-trust extract"
497
+ ]
498
+ }
499
+ ]
500
+ else
501
+ raise "Unknown image ID for node #{node}: #{image_id}. Check metadata for this node."
502
+ end
503
+ else
504
+ raise "Missing path referenced by the hpc_certificates environment variable: #{ENV['hpc_certificates']}"
505
+ end
506
+ else
507
+ []
508
+ end
509
+ [
510
+ node,
511
+ [
512
+ # Install the mutex lock and acquire it
513
+ {
514
+ scp: { "#{__dir__}/mutex_dir" => '.' },
515
+ remote_bash: "while ! #{ssh_user == 'root' ? '' : 'sudo '}./mutex_dir lock /tmp/hybrid_platforms_conductor_deploy_lock \"$(ps -o ppid= -p $$)\"; do echo -e 'Another deployment is running on #{node}. Waiting for it to finish to continue...' ; sleep 5 ; done"
516
+ }
517
+ ] +
518
+ certificate_actions +
519
+ @services_handler.actions_to_deploy_on(node, node_services, @use_why_run)
520
+ ]
521
+ end],
522
+ timeout: @timeout,
523
+ concurrent: @concurrent_execution,
524
+ log_to_stdout: !@concurrent_execution
525
+ )
526
+ # Free eventual locks
527
+ @actions_executor.execute_actions(
528
+ Hash[services.keys.map do |node|
529
+ [
530
+ node,
531
+ { remote_bash: "#{ssh_user == 'root' ? '' : 'sudo '}./mutex_dir unlock /tmp/hybrid_platforms_conductor_deploy_lock" }
532
+ ]
533
+ end],
534
+ timeout: 10,
535
+ concurrent: true,
536
+ log_to_dir: nil
537
+ )
538
+
539
+ # Save logs
540
+ save_logs(outputs, services) if !@use_why_run && !@cmd_runner.dry_run
541
+
542
+ outputs
543
+ end
544
+
545
+ # Save some deployment logs.
546
+ # It uploads them on the nodes that have been deployed.
547
+ #
548
+ # Parameters::
549
+ # * *logs* (Hash<String, [Integer or Symbol, String, String]>): Exit status code (or Symbol in case of error or dry run), standard output and error for each node.
550
+ # * *services* (Hash<String, Array<String>>): List of services that have been deployed, per node
551
+ def save_logs(logs, services)
552
+ section "Saving deployment logs for #{logs.size} nodes" do
553
+ Dir.mktmpdir('hybrid_platforms_conductor-logs') do |tmp_dir|
554
+ ssh_user = @actions_executor.connector(:ssh).ssh_user
555
+ @actions_executor.execute_actions(
556
+ Hash[logs.map do |node, (exit_status, stdout, stderr)|
557
+ # Create a log file to be scp with all relevant info
558
+ now = Time.now.utc
559
+ log_file = "#{tmp_dir}/#{node}_#{now.strftime('%F_%H%M%S')}_#{ssh_user}"
560
+ services_info = @services_handler.log_info_for(node, services[node])
561
+ File.write(
562
+ log_file,
563
+ services_info.merge(
564
+ date: now.strftime('%F %T'),
565
+ user: ssh_user,
566
+ debug: log_debug? ? 'Yes' : 'No',
567
+ services: services[node].join(', '),
568
+ exit_status: exit_status
569
+ ).map { |property, value| "#{property}: #{value}" }.join("\n") +
570
+ "\n===== STDOUT =====\n" +
571
+ (stdout || '') +
572
+ "\n===== STDERR =====\n" +
573
+ (stderr || '')
574
+ )
575
+ [
576
+ node,
577
+ {
578
+ remote_bash: "#{ssh_user == 'root' ? '' : 'sudo '}mkdir -p /var/log/deployments",
579
+ scp: {
580
+ log_file => '/var/log/deployments',
581
+ :sudo => ssh_user != 'root',
582
+ :owner => 'root',
583
+ :group => 'root'
584
+ }
585
+ }
586
+ ]
587
+ end],
588
+ timeout: 10,
589
+ concurrent: true,
590
+ log_to_dir: nil
591
+ )
592
+ end
593
+ end
594
+ end
595
+
596
+ end
597
+
598
+ end