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,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