hybrid_platforms_conductor 32.17.0 → 33.0.2

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 (283) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +36 -0
  3. data/README.md +6 -3
  4. data/bin/check-node +0 -1
  5. data/bin/deploy +0 -1
  6. data/bin/get_impacted_nodes +0 -1
  7. data/bin/last_deploys +12 -8
  8. data/bin/nodes_to_deploy +6 -6
  9. data/bin/setup +6 -6
  10. data/bin/topograph +1 -1
  11. data/docs/config_dsl.md +45 -1
  12. data/docs/executables.md +6 -7
  13. data/docs/executables/check-node.md +3 -3
  14. data/docs/executables/deploy.md +3 -3
  15. data/docs/executables/dump_nodes_json.md +3 -3
  16. data/docs/executables/test.md +3 -3
  17. data/docs/executables/topograph.md +3 -3
  18. data/docs/gen/mermaid/README.md-0.png +0 -0
  19. data/docs/gen/mermaid/docs/executables/check-node.md-0.png +0 -0
  20. data/docs/gen/mermaid/docs/executables/deploy.md-0.png +0 -0
  21. data/docs/gen/mermaid/docs/executables/free_ips.md-0.png +0 -0
  22. data/docs/gen/mermaid/docs/executables/get_impacted_nodes.md-0.png +0 -0
  23. data/docs/gen/mermaid/docs/executables/last_deploys.md-0.png +0 -0
  24. data/docs/gen/mermaid/docs/executables/nodes_to_deploy.md-0.png +0 -0
  25. data/docs/gen/mermaid/docs/executables/report.md-0.png +0 -0
  26. data/docs/gen/mermaid/docs/executables/run.md-0.png +0 -0
  27. data/docs/gen/mermaid/docs/executables/ssh_config.md-0.png +0 -0
  28. data/docs/gen/mermaid/docs/executables/test.md-0.png +0 -0
  29. data/docs/plugins.md +46 -0
  30. data/docs/plugins/connector/ssh.md +1 -1
  31. data/docs/plugins/log/remote_fs.md +26 -0
  32. data/docs/plugins/secrets_reader/cli.md +31 -0
  33. data/docs/plugins/secrets_reader/thycotic.md +46 -0
  34. data/docs/plugins/test/check_deploy_and_idempotence.md +1 -1
  35. data/docs/plugins/test/connection.md +1 -0
  36. data/docs/plugins/test/deploy_removes_root_access.md +1 -1
  37. data/docs/plugins/test/file_system.md +1 -0
  38. data/docs/plugins/test/hostname.md +1 -0
  39. data/docs/plugins/test/ip.md +1 -0
  40. data/docs/plugins/test/local_users.md +1 -0
  41. data/docs/plugins/test/mounts.md +1 -0
  42. data/docs/plugins/test/orphan_files.md +1 -0
  43. data/docs/plugins/test/ports.md +1 -0
  44. data/docs/plugins/test/spectre.md +1 -0
  45. data/docs/plugins/test/vulnerabilities.md +1 -0
  46. data/lib/hybrid_platforms_conductor/action.rb +4 -4
  47. data/lib/hybrid_platforms_conductor/actions_executor.rb +45 -43
  48. data/lib/hybrid_platforms_conductor/bitbucket.rb +5 -4
  49. data/lib/hybrid_platforms_conductor/cmd_runner.rb +13 -12
  50. data/lib/hybrid_platforms_conductor/cmdb.rb +2 -2
  51. data/lib/hybrid_platforms_conductor/common_config_dsl/bitbucket.rb +2 -1
  52. data/lib/hybrid_platforms_conductor/common_config_dsl/confluence.rb +2 -1
  53. data/lib/hybrid_platforms_conductor/common_config_dsl/file_system_tests.rb +5 -4
  54. data/lib/hybrid_platforms_conductor/common_config_dsl/github.rb +4 -3
  55. data/lib/hybrid_platforms_conductor/common_config_dsl/idempotence_tests.rb +2 -2
  56. data/lib/hybrid_platforms_conductor/config.rb +8 -4
  57. data/lib/hybrid_platforms_conductor/confluence.rb +1 -1
  58. data/lib/hybrid_platforms_conductor/connector.rb +5 -2
  59. data/lib/hybrid_platforms_conductor/core_extensions/cleanroom/fix_kwargs.rb +116 -0
  60. data/lib/hybrid_platforms_conductor/core_extensions/symbol/zero.rb +24 -0
  61. data/lib/hybrid_platforms_conductor/credentials.rb +39 -36
  62. data/lib/hybrid_platforms_conductor/current_dir_monitor.rb +4 -1
  63. data/lib/hybrid_platforms_conductor/deployer.rb +275 -224
  64. data/lib/hybrid_platforms_conductor/executable.rb +20 -15
  65. data/lib/hybrid_platforms_conductor/hpc_plugins/cmdb/config.rb +10 -7
  66. data/lib/hybrid_platforms_conductor/hpc_plugins/cmdb/host_ip.rb +1 -1
  67. data/lib/hybrid_platforms_conductor/hpc_plugins/cmdb/host_keys.rb +2 -2
  68. data/lib/hybrid_platforms_conductor/hpc_plugins/cmdb/platform_handlers.rb +4 -4
  69. data/lib/hybrid_platforms_conductor/hpc_plugins/connector/local.rb +2 -0
  70. data/lib/hybrid_platforms_conductor/hpc_plugins/connector/ssh.rb +45 -49
  71. data/lib/hybrid_platforms_conductor/hpc_plugins/log/my_log_plugin.rb.sample +100 -0
  72. data/lib/hybrid_platforms_conductor/hpc_plugins/log/remote_fs.rb +180 -0
  73. data/lib/hybrid_platforms_conductor/hpc_plugins/platform_handler/serverless_chef.rb +68 -66
  74. data/lib/hybrid_platforms_conductor/hpc_plugins/platform_handler/serverless_chef/dsl_parser.rb +13 -0
  75. data/lib/hybrid_platforms_conductor/hpc_plugins/platform_handler/serverless_chef/recipes_tree_builder.rb +39 -38
  76. data/lib/hybrid_platforms_conductor/hpc_plugins/platform_handler/yaml_inventory.rb +5 -4
  77. data/lib/hybrid_platforms_conductor/hpc_plugins/provisioner/docker.rb +43 -45
  78. data/lib/hybrid_platforms_conductor/hpc_plugins/provisioner/podman.rb +18 -20
  79. data/lib/hybrid_platforms_conductor/hpc_plugins/provisioner/proxmox.rb +118 -117
  80. data/lib/hybrid_platforms_conductor/hpc_plugins/provisioner/proxmox/proxmox_waiter.rb +39 -43
  81. data/lib/hybrid_platforms_conductor/hpc_plugins/provisioner/proxmox/reserve_proxmox_container +9 -13
  82. data/lib/hybrid_platforms_conductor/hpc_plugins/report/confluence.rb +2 -2
  83. data/lib/hybrid_platforms_conductor/hpc_plugins/report/mediawiki.rb +28 -21
  84. data/lib/hybrid_platforms_conductor/hpc_plugins/report/stdout.rb +26 -22
  85. data/lib/hybrid_platforms_conductor/hpc_plugins/secrets_reader/cli.rb +77 -0
  86. data/lib/hybrid_platforms_conductor/hpc_plugins/secrets_reader/my_secrets_reader_plugin.rb.sample +46 -0
  87. data/lib/hybrid_platforms_conductor/hpc_plugins/secrets_reader/thycotic.rb +90 -0
  88. data/lib/hybrid_platforms_conductor/hpc_plugins/test/bitbucket_conf.rb +3 -3
  89. data/lib/hybrid_platforms_conductor/hpc_plugins/test/check_deploy_and_idempotence.rb +4 -2
  90. data/lib/hybrid_platforms_conductor/hpc_plugins/test/connection.rb +3 -1
  91. data/lib/hybrid_platforms_conductor/hpc_plugins/test/deploy_freshness.rb +7 -21
  92. data/lib/hybrid_platforms_conductor/hpc_plugins/test/deploy_removes_root_access.rb +21 -19
  93. data/lib/hybrid_platforms_conductor/hpc_plugins/test/divergence.rb +2 -2
  94. data/lib/hybrid_platforms_conductor/hpc_plugins/test/executables.rb +2 -2
  95. data/lib/hybrid_platforms_conductor/hpc_plugins/test/file_system.rb +21 -22
  96. data/lib/hybrid_platforms_conductor/hpc_plugins/test/file_system_hdfs.rb +19 -21
  97. data/lib/hybrid_platforms_conductor/hpc_plugins/test/github_ci.rb +2 -3
  98. data/lib/hybrid_platforms_conductor/hpc_plugins/test/hostname.rb +3 -1
  99. data/lib/hybrid_platforms_conductor/hpc_plugins/test/idempotence.rb +2 -2
  100. data/lib/hybrid_platforms_conductor/hpc_plugins/test/ip.rb +4 -2
  101. data/lib/hybrid_platforms_conductor/hpc_plugins/test/jenkins_ci_conf.rb +21 -22
  102. data/lib/hybrid_platforms_conductor/hpc_plugins/test/jenkins_ci_masters_ok.rb +10 -12
  103. data/lib/hybrid_platforms_conductor/hpc_plugins/test/linear_strategy.rb +9 -9
  104. data/lib/hybrid_platforms_conductor/hpc_plugins/test/local_users.rb +5 -3
  105. data/lib/hybrid_platforms_conductor/hpc_plugins/test/mounts.rb +5 -3
  106. data/lib/hybrid_platforms_conductor/hpc_plugins/test/orphan_files.rb +13 -10
  107. data/lib/hybrid_platforms_conductor/hpc_plugins/test/ports.rb +5 -3
  108. data/lib/hybrid_platforms_conductor/hpc_plugins/test/private_ips.rb +5 -5
  109. data/lib/hybrid_platforms_conductor/hpc_plugins/test/public_ips.rb +5 -5
  110. data/lib/hybrid_platforms_conductor/hpc_plugins/test/spectre.rb +7 -7
  111. data/lib/hybrid_platforms_conductor/hpc_plugins/test/veids.rb +3 -3
  112. data/lib/hybrid_platforms_conductor/hpc_plugins/test/vulnerabilities.rb +27 -25
  113. data/lib/hybrid_platforms_conductor/hpc_plugins/test_report/confluence.rb +2 -2
  114. data/lib/hybrid_platforms_conductor/hpc_plugins/test_report/stdout.rb +8 -6
  115. data/lib/hybrid_platforms_conductor/io_router.rb +14 -13
  116. data/lib/hybrid_platforms_conductor/json_dumper.rb +2 -2
  117. data/lib/hybrid_platforms_conductor/log.rb +31 -0
  118. data/lib/hybrid_platforms_conductor/logger_helpers.rb +19 -16
  119. data/lib/hybrid_platforms_conductor/nodes_handler.rb +89 -71
  120. data/lib/hybrid_platforms_conductor/parallel_threads.rb +7 -11
  121. data/lib/hybrid_platforms_conductor/platform_handler.rb +7 -7
  122. data/lib/hybrid_platforms_conductor/platforms_handler.rb +5 -3
  123. data/lib/hybrid_platforms_conductor/plugin.rb +2 -2
  124. data/lib/hybrid_platforms_conductor/plugins.rb +14 -8
  125. data/lib/hybrid_platforms_conductor/provisioner.rb +4 -4
  126. data/lib/hybrid_platforms_conductor/report.rb +2 -2
  127. data/lib/hybrid_platforms_conductor/reports_handler.rb +3 -2
  128. data/lib/hybrid_platforms_conductor/secrets_reader.rb +31 -0
  129. data/lib/hybrid_platforms_conductor/services_handler.rb +32 -29
  130. data/lib/hybrid_platforms_conductor/test_only_remote_node.rb +18 -0
  131. data/lib/hybrid_platforms_conductor/test_report.rb +15 -18
  132. data/lib/hybrid_platforms_conductor/tests_runner.rb +116 -118
  133. data/lib/hybrid_platforms_conductor/thycotic.rb +28 -19
  134. data/lib/hybrid_platforms_conductor/topographer.rb +200 -190
  135. data/lib/hybrid_platforms_conductor/topographer/plugins/graphviz.rb +8 -8
  136. data/lib/hybrid_platforms_conductor/topographer/plugins/json.rb +4 -4
  137. data/lib/hybrid_platforms_conductor/version.rb +1 -1
  138. data/spec/hybrid_platforms_conductor_test.rb +33 -12
  139. data/spec/hybrid_platforms_conductor_test/api/actions_executor/actions/bash_spec.rb +18 -11
  140. data/spec/hybrid_platforms_conductor_test/api/actions_executor/actions/interactive_spec.rb +2 -2
  141. data/spec/hybrid_platforms_conductor_test/api/actions_executor/actions/remote_bash_spec.rb +32 -21
  142. data/spec/hybrid_platforms_conductor_test/api/actions_executor/actions/ruby_spec.rb +75 -49
  143. data/spec/hybrid_platforms_conductor_test/api/actions_executor/actions/scp_spec.rb +27 -15
  144. data/spec/hybrid_platforms_conductor_test/api/actions_executor/actions_spec.rb +90 -59
  145. data/spec/hybrid_platforms_conductor_test/api/actions_executor/connection_spec.rb +46 -44
  146. data/spec/hybrid_platforms_conductor_test/api/actions_executor/connectors/local/connectable_nodes_spec.rb +12 -8
  147. data/spec/hybrid_platforms_conductor_test/api/actions_executor/connectors/local/remote_actions_spec.rb +4 -7
  148. data/spec/hybrid_platforms_conductor_test/api/actions_executor/connectors/ssh/cli_options_spec.rb +21 -22
  149. data/spec/hybrid_platforms_conductor_test/api/actions_executor/connectors/ssh/config_dsl_spec.rb +23 -24
  150. data/spec/hybrid_platforms_conductor_test/api/actions_executor/connectors/ssh/connectable_nodes_spec.rb +10 -6
  151. data/spec/hybrid_platforms_conductor_test/api/actions_executor/connectors/ssh/connections_spec.rb +106 -75
  152. data/spec/hybrid_platforms_conductor_test/api/actions_executor/connectors/ssh/global_helpers_spec.rb +145 -126
  153. data/spec/hybrid_platforms_conductor_test/api/actions_executor/connectors/ssh/node_helpers_spec.rb +3 -3
  154. data/spec/hybrid_platforms_conductor_test/api/actions_executor/connectors/ssh/remote_actions_spec.rb +29 -25
  155. data/spec/hybrid_platforms_conductor_test/api/actions_executor/logging_spec.rb +167 -142
  156. data/spec/hybrid_platforms_conductor_test/api/actions_executor/parallel_spec.rb +272 -244
  157. data/spec/hybrid_platforms_conductor_test/api/actions_executor/timeout_spec.rb +16 -16
  158. data/spec/hybrid_platforms_conductor_test/api/cmd_runner_spec.rb +36 -36
  159. data/spec/hybrid_platforms_conductor_test/api/config_spec.rb +24 -22
  160. data/spec/hybrid_platforms_conductor_test/api/deployer/check_spec.rb +4 -2
  161. data/spec/hybrid_platforms_conductor_test/api/deployer/config_dsl_spec.rb +43 -5
  162. data/spec/hybrid_platforms_conductor_test/api/deployer/deploy_spec.rb +199 -216
  163. data/spec/hybrid_platforms_conductor_test/api/deployer/log_plugins/remote_fs_spec.rb +223 -0
  164. data/spec/hybrid_platforms_conductor_test/api/deployer/parse_deploy_output_spec.rb +55 -59
  165. data/spec/hybrid_platforms_conductor_test/api/deployer/provisioner_spec.rb +36 -62
  166. data/spec/hybrid_platforms_conductor_test/api/deployer/provisioners/podman_spec.rb +17 -17
  167. data/spec/hybrid_platforms_conductor_test/api/deployer/provisioners/proxmox/config_dsl_spec.rb +4 -4
  168. data/spec/hybrid_platforms_conductor_test/api/deployer/provisioners/proxmox/create_spec.rb +44 -51
  169. data/spec/hybrid_platforms_conductor_test/api/deployer/provisioners/proxmox/destroy_spec.rb +3 -3
  170. data/spec/hybrid_platforms_conductor_test/api/deployer/provisioners/proxmox/ip_spec.rb +12 -16
  171. data/spec/hybrid_platforms_conductor_test/api/deployer/provisioners/proxmox/reserve_proxmox_container/destroy_vm_spec.rb +31 -19
  172. data/spec/hybrid_platforms_conductor_test/api/deployer/provisioners/proxmox/reserve_proxmox_container/expired_containers_spec.rb +324 -266
  173. data/spec/hybrid_platforms_conductor_test/api/deployer/provisioners/proxmox/reserve_proxmox_container/ips_assignment_spec.rb +89 -61
  174. data/spec/hybrid_platforms_conductor_test/api/deployer/provisioners/proxmox/reserve_proxmox_container/other_lxc_containers_resources_spec.rb +117 -93
  175. data/spec/hybrid_platforms_conductor_test/api/deployer/provisioners/proxmox/reserve_proxmox_container/pve_node_resources_spec.rb +71 -54
  176. data/spec/hybrid_platforms_conductor_test/api/deployer/provisioners/proxmox/reserve_proxmox_container/retries_spec.rb +10 -8
  177. data/spec/hybrid_platforms_conductor_test/api/deployer/provisioners/proxmox/reserve_proxmox_container/vm_ids_assignment_spec.rb +80 -60
  178. data/spec/hybrid_platforms_conductor_test/api/deployer/provisioners/proxmox/start_spec.rb +1 -1
  179. data/spec/hybrid_platforms_conductor_test/api/deployer/provisioners/proxmox/state_spec.rb +1 -1
  180. data/spec/hybrid_platforms_conductor_test/api/deployer/provisioners/proxmox/stop_spec.rb +1 -1
  181. data/spec/hybrid_platforms_conductor_test/api/deployer/secrets_reader_plugins/cli_spec.rb +64 -0
  182. data/spec/hybrid_platforms_conductor_test/api/deployer/secrets_reader_plugins/thycotic_spec.rb +268 -0
  183. data/spec/hybrid_platforms_conductor_test/api/nodes_handler/cmdbs/config_spec.rb +8 -10
  184. data/spec/hybrid_platforms_conductor_test/api/nodes_handler/cmdbs/host_ip_spec.rb +33 -24
  185. data/spec/hybrid_platforms_conductor_test/api/nodes_handler/cmdbs/host_keys_spec.rb +64 -51
  186. data/spec/hybrid_platforms_conductor_test/api/nodes_handler/cmdbs/platform_handlers_spec.rb +3 -3
  187. data/spec/hybrid_platforms_conductor_test/api/nodes_handler/cmdbs_plugins_api_spec.rb +50 -51
  188. data/spec/hybrid_platforms_conductor_test/api/nodes_handler/common_spec.rb +91 -81
  189. data/spec/hybrid_platforms_conductor_test/api/nodes_handler/config_dsl_spec.rb +14 -16
  190. data/spec/hybrid_platforms_conductor_test/api/nodes_handler/git_diff_impacts_spec.rb +51 -75
  191. data/spec/hybrid_platforms_conductor_test/api/nodes_handler/nodes_selectors_spec.rb +35 -26
  192. data/spec/hybrid_platforms_conductor_test/api/nodes_handler/platform_handlers_plugins_api_spec.rb +24 -16
  193. data/spec/hybrid_platforms_conductor_test/api/nodes_handler/several_platforms_spec.rb +29 -19
  194. data/spec/hybrid_platforms_conductor_test/api/platform_handler_spec.rb +4 -4
  195. data/spec/hybrid_platforms_conductor_test/api/platform_handlers/serverless_chef/config_dsl_spec.rb +2 -2
  196. data/spec/hybrid_platforms_conductor_test/api/platform_handlers/serverless_chef/deploy_output_parsing_spec.rb +6 -6
  197. data/spec/hybrid_platforms_conductor_test/api/platform_handlers/serverless_chef/diff_impacts_spec.rb +57 -99
  198. data/spec/hybrid_platforms_conductor_test/api/platform_handlers/serverless_chef/inventory_spec.rb +4 -4
  199. data/spec/hybrid_platforms_conductor_test/api/platform_handlers/serverless_chef/packaging_spec.rb +32 -35
  200. data/spec/hybrid_platforms_conductor_test/api/platform_handlers/serverless_chef/services_deployment_spec.rb +10 -10
  201. data/spec/hybrid_platforms_conductor_test/api/platforms_handler_spec.rb +38 -27
  202. data/spec/hybrid_platforms_conductor_test/api/plugins_spec.rb +46 -52
  203. data/spec/hybrid_platforms_conductor_test/api/reports_handler_spec.rb +2 -2
  204. data/spec/hybrid_platforms_conductor_test/api/services_handler/actions_to_deploy_spec.rb +90 -58
  205. data/spec/hybrid_platforms_conductor_test/api/services_handler/deploy_allowed_spec.rb +38 -34
  206. data/spec/hybrid_platforms_conductor_test/api/services_handler/log_info_spec.rb +11 -9
  207. data/spec/hybrid_platforms_conductor_test/api/services_handler/package_spec.rb +193 -171
  208. data/spec/hybrid_platforms_conductor_test/api/services_handler/parse_deploy_output_spec.rb +66 -54
  209. data/spec/hybrid_platforms_conductor_test/api/services_handler/prepare_for_deploy_spec.rb +147 -133
  210. data/spec/hybrid_platforms_conductor_test/api/tests_runner/common_spec.rb +69 -49
  211. data/spec/hybrid_platforms_conductor_test/api/tests_runner/global_spec.rb +5 -4
  212. data/spec/hybrid_platforms_conductor_test/api/tests_runner/node_check_spec.rb +8 -5
  213. data/spec/hybrid_platforms_conductor_test/api/tests_runner/node_spec.rb +8 -5
  214. data/spec/hybrid_platforms_conductor_test/api/tests_runner/node_ssh_spec.rb +30 -27
  215. data/spec/hybrid_platforms_conductor_test/api/tests_runner/platform_spec.rb +12 -9
  216. data/spec/hybrid_platforms_conductor_test/api/tests_runner/reports_spec.rb +48 -47
  217. data/spec/hybrid_platforms_conductor_test/api/tests_runner/test_plugins/bitbucket_conf_spec.rb +5 -5
  218. data/spec/hybrid_platforms_conductor_test/api/tests_runner/test_plugins/github_ci_spec.rb +5 -5
  219. data/spec/hybrid_platforms_conductor_test/api/tests_runner/test_reports_plugins/confluence_spec.rb +5 -5
  220. data/spec/hybrid_platforms_conductor_test/cmdb_plugins/test_cmdb.rb +9 -9
  221. data/spec/hybrid_platforms_conductor_test/cmdb_plugins/{test_cmdb2.rb → test_cmdb_2.rb} +6 -6
  222. data/spec/hybrid_platforms_conductor_test/cmdb_plugins/test_cmdb_others.rb +3 -3
  223. data/spec/hybrid_platforms_conductor_test/cmdb_plugins/{test_cmdb_others2.rb → test_cmdb_others_2.rb} +2 -2
  224. data/spec/hybrid_platforms_conductor_test/docs_spec.rb +1 -1
  225. data/spec/hybrid_platforms_conductor_test/executables/{check-node_spec.rb → check_node_spec.rb} +4 -6
  226. data/spec/hybrid_platforms_conductor_test/executables/deploy_spec.rb +4 -6
  227. data/spec/hybrid_platforms_conductor_test/executables/get_impacted_nodes_spec.rb +76 -77
  228. data/spec/hybrid_platforms_conductor_test/executables/last_deploys_spec.rb +159 -113
  229. data/spec/hybrid_platforms_conductor_test/executables/nodes_to_deploy_spec.rb +299 -160
  230. data/spec/hybrid_platforms_conductor_test/executables/options/actions_executor_spec.rb +4 -6
  231. data/spec/hybrid_platforms_conductor_test/executables/options/cmd_runner_spec.rb +3 -5
  232. data/spec/hybrid_platforms_conductor_test/executables/options/common_spec.rb +8 -8
  233. data/spec/hybrid_platforms_conductor_test/executables/options/deployer_spec.rb +12 -196
  234. data/spec/hybrid_platforms_conductor_test/executables/options/nodes_handler_spec.rb +9 -10
  235. data/spec/hybrid_platforms_conductor_test/executables/options/nodes_selectors_spec.rb +9 -10
  236. data/spec/hybrid_platforms_conductor_test/executables/options/reports_handler_spec.rb +1 -1
  237. data/spec/hybrid_platforms_conductor_test/executables/options/tests_runner_spec.rb +22 -22
  238. data/spec/hybrid_platforms_conductor_test/executables/report_spec.rb +22 -16
  239. data/spec/hybrid_platforms_conductor_test/executables/run_spec.rb +32 -32
  240. data/spec/hybrid_platforms_conductor_test/executables/ssh_config_spec.rb +7 -9
  241. data/spec/hybrid_platforms_conductor_test/executables/test_spec.rb +3 -5
  242. data/spec/hybrid_platforms_conductor_test/helpers/actions_executor_helpers.rb +2 -2
  243. data/spec/hybrid_platforms_conductor_test/helpers/cmd_runner_helpers.rb +4 -3
  244. data/spec/hybrid_platforms_conductor_test/helpers/cmdb_helpers.rb +2 -2
  245. data/spec/hybrid_platforms_conductor_test/helpers/config_helpers.rb +1 -1
  246. data/spec/hybrid_platforms_conductor_test/helpers/connector_ssh_helpers.rb +12 -13
  247. data/spec/hybrid_platforms_conductor_test/helpers/deployer_helpers.rb +245 -56
  248. data/spec/hybrid_platforms_conductor_test/helpers/executables_helpers.rb +11 -11
  249. data/spec/hybrid_platforms_conductor_test/helpers/nodes_handler_helpers.rb +1 -1
  250. data/spec/hybrid_platforms_conductor_test/helpers/platforms_handler_helpers.rb +39 -28
  251. data/spec/hybrid_platforms_conductor_test/helpers/plugins_helpers.rb +1 -1
  252. data/spec/hybrid_platforms_conductor_test/helpers/provisioner_proxmox_helpers.rb +86 -111
  253. data/spec/hybrid_platforms_conductor_test/helpers/reports_handler_helpers.rb +1 -1
  254. data/spec/hybrid_platforms_conductor_test/helpers/serverless_chef_helpers.rb +3 -3
  255. data/spec/hybrid_platforms_conductor_test/helpers/services_handler_helpers.rb +1 -1
  256. data/spec/hybrid_platforms_conductor_test/helpers/tests_runner_helpers.rb +1 -1
  257. data/spec/hybrid_platforms_conductor_test/mocked_lib/my_test_gem/hpc_plugins/test_plugin_type/{test_plugin_id1.rb → test_plugin_id_1.rb} +0 -0
  258. data/spec/hybrid_platforms_conductor_test/mocked_lib/my_test_gem/hpc_plugins/test_plugin_type/{test_plugin_id2.rb → test_plugin_id_2.rb} +0 -0
  259. data/spec/hybrid_platforms_conductor_test/mocked_lib/my_test_gem2/sub_dir/hpc_plugins/test_plugin_type/{test_plugin_id3.rb → test_plugin_id_3.rb} +0 -0
  260. data/spec/hybrid_platforms_conductor_test/mocked_lib/my_test_gem2/sub_dir/hpc_plugins/{test_plugin_type2/test_plugin_id4.rb → test_plugin_type_2/test_plugin_id_4.rb} +0 -0
  261. data/spec/hybrid_platforms_conductor_test/platform_handler_plugins/test.rb +1 -1
  262. data/spec/hybrid_platforms_conductor_test/platform_handler_plugins/{test2.rb → test_2.rb} +0 -0
  263. data/spec/hybrid_platforms_conductor_test/rubocop_spec.rb +31 -0
  264. data/spec/hybrid_platforms_conductor_test/serverless_chef_repositories/1_node/nodes/node.json +3 -3
  265. data/spec/hybrid_platforms_conductor_test/serverless_chef_repositories/data_bags/nodes/node.json +3 -3
  266. data/spec/hybrid_platforms_conductor_test/serverless_chef_repositories/recipes/cookbooks/test_cookbook_2/libraries/default.rb +1 -0
  267. data/spec/hybrid_platforms_conductor_test/serverless_chef_repositories/several_cookbooks/other_cookbooks/test_cookbook_2/libraries/default.rb +1 -0
  268. data/spec/hybrid_platforms_conductor_test/shared_examples/deployer.rb +952 -0
  269. data/spec/hybrid_platforms_conductor_test/test_connector.rb +3 -3
  270. data/spec/hybrid_platforms_conductor_test/test_log_no_read_plugin.rb +84 -0
  271. data/spec/hybrid_platforms_conductor_test/test_log_plugin.rb +105 -0
  272. data/spec/hybrid_platforms_conductor_test/test_plugins/global.rb +1 -0
  273. data/spec/hybrid_platforms_conductor_test/test_plugins/node.rb +1 -0
  274. data/spec/hybrid_platforms_conductor_test/test_plugins/node_check.rb +1 -0
  275. data/spec/hybrid_platforms_conductor_test/test_plugins/platform.rb +1 -0
  276. data/spec/hybrid_platforms_conductor_test/test_plugins/several_checks.rb +2 -2
  277. data/spec/hybrid_platforms_conductor_test/test_secrets_reader_plugin.rb +45 -0
  278. data/spec/hybrid_platforms_conductor_test/tests_report_plugin.rb +5 -6
  279. data/spec/spec_helper.rb +17 -18
  280. data/tools/check_md +16 -20
  281. data/tools/generate_mermaid +1 -1
  282. metadata +195 -144
  283. data/spec/hybrid_platforms_conductor_test/helpers/deployer_test_helpers.rb +0 -916
@@ -42,7 +42,7 @@ module HybridPlatformsConductor
42
42
  # * *url* (String or nil): The URL for which we want the credentials, or nil if not associated to a URL [default: nil]
43
43
  # * *logger* (Logger): Logger to be used [default = Logger.new(STDOUT)]
44
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))
45
+ def initialize(id, url: nil, logger: Logger.new($stdout), logger_stderr: Logger.new($stderr))
46
46
  init_loggers(logger, logger_stderr)
47
47
  @id = id
48
48
  @url = url
@@ -54,7 +54,7 @@ module HybridPlatformsConductor
54
54
  # Provide a helper to clear password from memory for security.
55
55
  # To be used when the client knows it won't use the password anymore.
56
56
  def clear_password
57
- @password.replace('gotyou!' * 100) unless @password.nil?
57
+ @password&.replace('gotyou!' * 100)
58
58
  GC.start
59
59
  end
60
60
 
@@ -82,46 +82,49 @@ module HybridPlatformsConductor
82
82
  # Do it only once.
83
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
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
85
+ return if @retrieved
86
+
87
+ # Check environment variables
88
+ @user = ENV["hpc_user_for_#{@id}"].dup
89
+ @password = ENV["hpc_password_for_#{@id}"].dup
90
+ if @user.nil? || @user.empty? || @password.nil? || @password.empty?
91
+ log_debug "[ Credentials for #{@id} ] - Credentials not found from environment variables."
92
+ if @url.nil?
93
+ log_debug "[ Credentials for #{@id} ] - No URL associated to this credentials, so .netrc can't be used."
94
+ else
95
+ # Check Netrc
96
+ netrc = ::Netrc.read
97
+ begin
98
+ netrc_user, netrc_password = netrc[URI.parse(@url).host.downcase]
99
+ if netrc_user.nil?
100
+ log_debug "[ Credentials for #{@id} ] - No credentials retrieved from .netrc."
101
+ # TODO: Add more credentials source if needed here
102
+ log_warn "[ Credentials for #{@id} ] - Unable to get credentials for #{@id} (URL: #{@url})."
103
+ else
104
+ @user = netrc_user.dup
105
+ @password = netrc_password.dup
106
+ log_debug "[ Credentials for #{@id} ] - Credentials retrieved from .netrc using #{@url}."
107
+ end
108
+ ensure
109
+ # Make sure the password does not stay in Netrc memory
110
+ # Wipe out any memory trace that might contain passwords in clear
111
+ netrc.instance_variable_get(:@data).each do |data_line|
112
+ data_line.each do |data_string|
113
+ data_string.replace('GotYou!!!' * 100)
114
114
  end
115
- netrc = nil
116
115
  end
116
+ # We don this assignment on purpose so that GC can remove sensitive data later
117
+ # rubocop:disable Lint/UselessAssignment
118
+ netrc = nil
119
+ # rubocop:enable Lint/UselessAssignment
117
120
  end
118
- else
119
- log_debug "[ Credentials for #{@id} ] - Credentials retrieved from environment variables."
120
121
  end
121
- GC.start
122
+ else
123
+ log_debug "[ Credentials for #{@id} ] - Credentials retrieved from environment variables."
122
124
  end
125
+ GC.start
123
126
  end
124
127
 
125
128
  end
126
129
 
127
- end
130
+ end
@@ -1,5 +1,6 @@
1
1
  require 'monitor'
2
2
 
3
+ # Decorate methods changing the process' current directory with a mutex to ensure they have an exclusive access
3
4
  module HybridPlatformsConductor
4
5
 
5
6
  # Implement a global monitor to protect accesses to the current directory.
@@ -7,7 +8,9 @@ module HybridPlatformsConductor
7
8
  module CurrentDirMonitor
8
9
 
9
10
  class << self
11
+
10
12
  attr_reader :monitor
13
+
11
14
  end
12
15
 
13
16
  @monitor = Monitor.new
@@ -24,7 +27,7 @@ module HybridPlatformsConductor
24
27
  result = nil
25
28
  CurrentDirMonitor.monitor.synchronize do
26
29
  # 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)
30
+ result = send(original_method_name, *args, &block)
28
31
  # puts "TID #{Thread.current.object_id} from #{caller[2]} - Current dir monitor released back to #{Dir.pwd}"
29
32
  end
30
33
  result
@@ -3,7 +3,6 @@ require 'futex'
3
3
  require 'json'
4
4
  require 'securerandom'
5
5
  require 'time'
6
- require 'thread'
7
6
  require 'hybrid_platforms_conductor/actions_executor'
8
7
  require 'hybrid_platforms_conductor/cmd_runner'
9
8
  require 'hybrid_platforms_conductor/executable'
@@ -11,7 +10,6 @@ require 'hybrid_platforms_conductor/logger_helpers'
11
10
  require 'hybrid_platforms_conductor/nodes_handler'
12
11
  require 'hybrid_platforms_conductor/services_handler'
13
12
  require 'hybrid_platforms_conductor/plugins'
14
- require 'hybrid_platforms_conductor/thycotic'
15
13
 
16
14
  module HybridPlatformsConductor
17
15
 
@@ -21,12 +19,26 @@ module HybridPlatformsConductor
21
19
  # Extend the Config DSL
22
20
  module ConfigDSLExtension
23
21
 
22
+ # List of log plugins. Each info has the following properties:
23
+ # * *nodes_selectors_stack* (Array<Object>): Stack of nodes selectors impacted by this rule.
24
+ # * *log_plugins* (Array<Symbol>): List of log plugins to be used to store deployment logs.
25
+ # Array< Hash<Symbol, Object> >
26
+ attr_reader :deployment_logs
27
+
28
+ # List of secrets reader plugins. Each info has the following properties:
29
+ # * *nodes_selectors_stack* (Array<Object>): Stack of nodes selectors impacted by this rule.
30
+ # * *secrets_readers* (Array<Symbol>): List of log plugins to be used to store deployment logs.
31
+ # Array< Hash<Symbol, Object> >
32
+ attr_reader :secrets_readers
33
+
24
34
  # Integer: Timeout (in seconds) for packaging repositories
25
35
  attr_reader :packaging_timeout_secs
26
36
 
27
37
  # Mixin initializer
28
38
  def init_deployer_config
29
39
  @packaging_timeout_secs = 60
40
+ @deployment_logs = []
41
+ @secrets_readers = []
30
42
  end
31
43
 
32
44
  # Set the packaging timeout
@@ -37,6 +49,28 @@ module HybridPlatformsConductor
37
49
  @packaging_timeout_secs = packaging_timeout_secs
38
50
  end
39
51
 
52
+ # Set the deployment log plugins to be used
53
+ #
54
+ # Parameters::
55
+ # * *log_plugins* (Symbol or Array<Symbol>): The list of (or single) log plugins to be used
56
+ def send_logs_to(*log_plugins)
57
+ @deployment_logs << {
58
+ nodes_selectors_stack: current_nodes_selectors_stack,
59
+ log_plugins: log_plugins.flatten
60
+ }
61
+ end
62
+
63
+ # Set the secrets readers
64
+ #
65
+ # Parameters::
66
+ # * *secrets_readers* (Symbol or Array<Symbol>): The list of (or single) secrets readers plugins to be used
67
+ def read_secrets_from(*secrets_readers)
68
+ @secrets_readers << {
69
+ nodes_selectors_stack: current_nodes_selectors_stack,
70
+ secrets_readers: secrets_readers.flatten
71
+ }
72
+ end
73
+
40
74
  end
41
75
 
42
76
  include LoggerHelpers
@@ -55,10 +89,6 @@ module HybridPlatformsConductor
55
89
  # Boolean
56
90
  attr_accessor :concurrent_execution
57
91
 
58
- # The list of JSON secrets
59
- # Array<Hash>
60
- attr_accessor :secrets
61
-
62
92
  # Are we deploying in a local environment?
63
93
  # Boolean
64
94
  attr_accessor :local_environment
@@ -78,8 +108,8 @@ module HybridPlatformsConductor
78
108
  # * *actions_executor* (ActionsExecutor): Actions Executor to be used. [default: ActionsExecutor.new]
79
109
  # * *services_handler* (ServicesHandler): Services Handler to be used. [default: ServicesHandler.new]
80
110
  def initialize(
81
- logger: Logger.new(STDOUT),
82
- logger_stderr: Logger.new(STDERR),
111
+ logger: Logger.new($stdout),
112
+ logger_stderr: Logger.new($stderr),
83
113
  config: Config.new,
84
114
  cmd_runner: CmdRunner.new,
85
115
  nodes_handler: NodesHandler.new,
@@ -92,8 +122,36 @@ module HybridPlatformsConductor
92
122
  @nodes_handler = nodes_handler
93
123
  @actions_executor = actions_executor
94
124
  @services_handler = services_handler
95
- @secrets = []
125
+ @override_secrets = nil
126
+ @secrets_readers = Plugins.new(
127
+ :secrets_reader,
128
+ logger: @logger,
129
+ logger_stderr: @logger_stderr,
130
+ init_plugin: proc do |plugin_class|
131
+ plugin_class.new(
132
+ logger: @logger,
133
+ logger_stderr: @logger_stderr,
134
+ config: @config,
135
+ cmd_runner: @cmd_runner,
136
+ nodes_handler: @nodes_handler
137
+ )
138
+ end
139
+ )
96
140
  @provisioners = Plugins.new(:provisioner, logger: @logger, logger_stderr: @logger_stderr)
141
+ @log_plugins = Plugins.new(
142
+ :log,
143
+ logger: @logger,
144
+ logger_stderr: @logger_stderr,
145
+ init_plugin: proc do |plugin_class|
146
+ plugin_class.new(
147
+ logger: @logger,
148
+ logger_stderr: @logger_stderr,
149
+ config: @config,
150
+ nodes_handler: @nodes_handler,
151
+ actions_executor: @actions_executor
152
+ )
153
+ end
154
+ )
97
155
  # Default values
98
156
  @use_why_run = false
99
157
  @timeout = nil
@@ -112,42 +170,32 @@ module HybridPlatformsConductor
112
170
  def options_parse(options_parser, parallel_switch: true, why_run_switch: false, timeout_options: true)
113
171
  options_parser.separator ''
114
172
  options_parser.separator 'Deployer options:'
115
- options_parser.on(
116
- '-e', '--secrets SECRETS_LOCATION',
117
- 'Specify a secrets location. Can be specified several times. Location can be:',
118
- '* Local path to a JSON file',
119
- '* URL of the form http[s]://<url>:<secret_id> to get a secret JSON file from a Thycotic Secret Server at the given URL.'
120
- ) do |secrets_location|
121
- @secrets << JSON.parse(
122
- if secrets_location =~ /^(https?:\/\/.+):(\d+)$/
123
- url = $1
124
- secret_id = $2
125
- secret = nil
126
- Thycotic.with_thycotic(url, @logger, @logger_stderr) do |thycotic|
127
- secret_file_item_id = thycotic.get_secret(secret_id).dig(:secret, :items, :secret_item, :id)
128
- raise "Unable to fetch secret file ID #{secrets_location}" if secret_file_item_id.nil?
129
- secret = thycotic.download_file_attachment_by_item_id(secret_id, secret_file_item_id)
130
- raise "Unable to fetch secret file attachment from #{secrets_location}" if secret.nil?
131
- end
132
- secret
133
- else
134
- raise "Missing secret file: #{secrets_location}" unless File.exist?(secrets_location)
135
- File.read(secrets_location)
136
- end
137
- )
173
+ if parallel_switch
174
+ options_parser.on('-p', '--parallel', 'Execute the commands in parallel (put the standard output in files <hybrid-platforms-dir>/run_logs/*.stdout)') do
175
+ @concurrent_execution = true
176
+ end
177
+ end
178
+ if timeout_options
179
+ 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|
180
+ @timeout = nbr_secs.to_i
181
+ end
182
+ end
183
+ if why_run_switch
184
+ 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
185
+ @use_why_run = true
186
+ end
138
187
  end
139
- options_parser.on('-p', '--parallel', 'Execute the commands in parallel (put the standard output in files <hybrid-platforms-dir>/run_logs/*.stdout)') do
140
- @concurrent_execution = true
141
- end if parallel_switch
142
- 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|
143
- @timeout = nbr_secs.to_i
144
- end if timeout_options
145
- 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
146
- @use_why_run = true
147
- end if why_run_switch
148
188
  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|
149
189
  @nbr_retries_on_error = nbr_retries.to_i
150
190
  end
191
+ # Display options secrets readers might have
192
+ @secrets_readers.each do |secret_reader_name, secret_reader|
193
+ next unless secret_reader.respond_to?(:options_parse)
194
+
195
+ options_parser.separator ''
196
+ options_parser.separator "Secrets reader #{secret_reader_name} options:"
197
+ secret_reader.options_parse(options_parser)
198
+ end
151
199
  end
152
200
 
153
201
  # Validate that parsed parameters are valid
@@ -158,6 +206,16 @@ module HybridPlatformsConductor
158
206
  # String: File used as a Futex for packaging
159
207
  PACKAGING_FUTEX_FILE = "#{Dir.tmpdir}/hpc_packaging"
160
208
 
209
+ # Override the secrets with a given JSON.
210
+ # When using this method with a secrets Hash, further deployments will not query secrets readers, but will use those secrets directly.
211
+ # Useful to override secrets in test conditions when using dummy secrets for example.
212
+ #
213
+ # Parameters::
214
+ # * *secrets* (Hash or nil): Secrets to take into account in place of secrets readers, or nil to cancel a previous overriding and use secrets readers instead.
215
+ def override_secrets(secrets)
216
+ @override_secrets = secrets
217
+ end
218
+
161
219
  # Deploy on a given list of nodes selectors.
162
220
  # The workflow is the following:
163
221
  # 1. Package the services to be deployed, considering the nodes, services and context (options, secrets, environment...)
@@ -171,16 +229,26 @@ module HybridPlatformsConductor
171
229
  def deploy_on(*nodes_selectors)
172
230
  # Get the sorted list of services to be deployed, per node
173
231
  # Hash<String, Array<String> >
174
- services_to_deploy = Hash[@nodes_handler.select_nodes(nodes_selectors.flatten).map do |node|
232
+ services_to_deploy = @nodes_handler.select_nodes(nodes_selectors.flatten).map do |node|
175
233
  [node, @nodes_handler.get_services_of(node)]
176
- end]
234
+ end.to_h
177
235
 
178
236
  # Get the secrets to be deployed
179
237
  secrets = {}
180
- @secrets.each do |secret_json|
181
- secrets.merge!(secret_json) do |key, value1, value2|
182
- raise "Secret #{key} has conflicting values between different secret JSON files." if value1 != value2
183
- value1
238
+ if @override_secrets
239
+ secrets = @override_secrets
240
+ else
241
+ services_to_deploy.each do |node, services|
242
+ # If there is no config for secrets, just use cli
243
+ (@config.secrets_readers.empty? ? [{ secrets_readers: %i[cli] }] : @nodes_handler.select_confs_for_node(node, @config.secrets_readers)).inject([]) do |secrets_readers, secrets_readers_info|
244
+ secrets_readers + secrets_readers_info[:secrets_readers]
245
+ end.sort.uniq.each do |secrets_reader|
246
+ services.each do |service|
247
+ node_secrets = @secrets_readers[secrets_reader].secrets_for(node, service)
248
+ conflicting_path = safe_merge(secrets, node_secrets)
249
+ raise "Secret set at path #{conflicting_path.join('->')} by #{secrets_reader} for service #{service} on node #{node} has conflicting values (#{log_debug? ? "#{node_secrets.dig(*conflicting_path)} != #{secrets.dig(*conflicting_path)}" : 'set debug for value details'})." unless conflicting_path.nil?
250
+ end
251
+ end
184
252
  end
185
253
  end
186
254
 
@@ -188,7 +256,6 @@ module HybridPlatformsConductor
188
256
  unless @use_why_run
189
257
  reason_for_interdiction = @services_handler.deploy_allowed?(
190
258
  services: services_to_deploy,
191
- secrets: secrets,
192
259
  local_environment: @local_environment
193
260
  )
194
261
  raise "Deployment not allowed: #{reason_for_interdiction}" unless reason_for_interdiction.nil?
@@ -226,51 +293,50 @@ module HybridPlatformsConductor
226
293
  remaining_nodes_to_deploy = services_to_deploy.keys
227
294
  while nbr_retries >= 0 && !remaining_nodes_to_deploy.empty?
228
295
  last_deploy_results = deploy(services_to_deploy.slice(*remaining_nodes_to_deploy))
229
- if nbr_retries > 0
296
+ if nbr_retries.positive?
230
297
  # Check if we need to retry deployment on some nodes
231
298
  # Only parse the last deployment attempt logs
232
- retriable_nodes = Hash[
233
- remaining_nodes_to_deploy.
234
- map do |node|
235
- exit_status, stdout, stderr = last_deploy_results[node]
236
- if exit_status == 0
299
+ retriable_nodes = remaining_nodes_to_deploy.
300
+ map do |node|
301
+ exit_status, stdout, stderr = last_deploy_results[node]
302
+ if exit_status.zero?
303
+ nil
304
+ else
305
+ retriable_errors = retriable_errors_from(node, exit_status, stdout, stderr)
306
+ if retriable_errors.empty?
237
307
  nil
238
308
  else
239
- retriable_errors = retriable_errors_from(node, exit_status, stdout, stderr)
240
- if retriable_errors.empty?
241
- nil
242
- else
243
- # Log the issue in the stderr of the deployment
244
- stderr << "!!! #{retriable_errors.size} retriable errors detected in this deployment:\n#{retriable_errors.map { |error| "* #{error}" }.join("\n")}\n"
245
- [node, retriable_errors]
246
- end
309
+ # Log the issue in the stderr of the deployment
310
+ stderr << "!!! #{retriable_errors.size} retriable errors detected in this deployment:\n#{retriable_errors.map { |error| "* #{error}" }.join("\n")}\n"
311
+ [node, retriable_errors]
247
312
  end
248
- end.
249
- compact
250
- ]
313
+ end
314
+ end.
315
+ compact.
316
+ to_h
251
317
  unless retriable_nodes.empty?
252
- log_warn <<~EOS.strip
318
+ log_warn <<~EO_LOG.strip
253
319
  Retry deployment for #{retriable_nodes.size} nodes as they got non-deterministic errors (#{nbr_retries} retries remaining):
254
320
  #{retriable_nodes.map { |node, retriable_errors| " * #{node}:\n#{retriable_errors.map { |error| " - #{error}" }.join("\n")}" }.join("\n")}
255
- EOS
321
+ EO_LOG
256
322
  end
257
323
  remaining_nodes_to_deploy = retriable_nodes.keys
258
324
  end
259
325
  # Merge deployment results
260
- results.merge!(last_deploy_results) do |node, (exit_status_1, stdout_1, stderr_1), (exit_status_2, stdout_2, stderr_2)|
326
+ results.merge!(last_deploy_results) do |_node, (exit_status_1, stdout_1, stderr_1), (exit_status_2, stdout_2, stderr_2)|
261
327
  [
262
328
  exit_status_2,
263
- <<~EOS,
329
+ <<~EO_STDOUT,
264
330
  #{stdout_1}
265
331
  Deployment exit status code: #{exit_status_1}
266
332
  !!! Retry deployment due to non-deterministic error (#{nbr_retries} remaining attempts)...
267
333
  #{stdout_2}
268
- EOS
269
- <<~EOS
334
+ EO_STDOUT
335
+ <<~EO_STDERR
270
336
  #{stderr_1}
271
337
  !!! Retry deployment due to non-deterministic error (#{nbr_retries} remaining attempts)...
272
338
  #{stderr_2}
273
- EOS
339
+ EO_STDERR
274
340
  ]
275
341
  end
276
342
  nbr_retries -= 1
@@ -325,7 +391,7 @@ module HybridPlatformsConductor
325
391
  sub_executable.config.sudo_procs.replace(sub_executable.config.sudo_procs.map do |sudo_proc_info|
326
392
  {
327
393
  nodes_selectors_stack: sudo_proc_info[:nodes_selectors_stack].map do |nodes_selector|
328
- @nodes_handler.select_nodes(nodes_selector).select { |selected_node| selected_node != node }
394
+ @nodes_handler.select_nodes(nodes_selector).reject { |selected_node| selected_node == node }
329
395
  end,
330
396
  sudo_proc: sudo_proc_info[:sudo_proc]
331
397
  }
@@ -338,13 +404,13 @@ module HybridPlatformsConductor
338
404
  deployer.local_environment = true
339
405
  # Ignore secrets that might have been given: in Docker containers we always use dummy secrets
340
406
  dummy_secrets_file = "#{@config.hybrid_platforms_dir}/dummy_secrets.json"
341
- deployer.secrets = File.exist?(dummy_secrets_file) ? [JSON.parse(File.read(dummy_secrets_file))] : []
407
+ deployer.override_secrets(File.exist?(dummy_secrets_file) ? JSON.parse(File.read(dummy_secrets_file)) : {})
342
408
  yield deployer, instance
343
409
  end
344
410
  rescue
345
411
  # Make sure Docker logs are being output to better investigate errors if we were not already outputing them in debug mode
346
412
  stdouts = sub_executable.stdouts_to_s
347
- log_error "[ #{node}/#{environment} ] - Encountered unhandled exception #{$!}\n#{$!.backtrace.join("\n")}\n-----\n#{stdouts}" unless stdouts.nil?
413
+ log_error "[ #{node}/#{environment} ] - Encountered unhandled exception #{$ERROR_INFO}\n#{$ERROR_INFO.backtrace.join("\n")}\n-----\n#{stdouts}" unless stdouts.nil?
348
414
  raise
349
415
  end
350
416
  end
@@ -355,72 +421,31 @@ module HybridPlatformsConductor
355
421
  # * *nodes* (Array<String>): Nodes to get info from
356
422
  # Result::
357
423
  # * Hash<String, Hash<Symbol,Object>: The deployed info, per node name.
358
- # Properties are defined by the Deployer#save_logs method, and additionally to them the following properties can be set:
359
- # * *error* (String): Optional property set in case of error
424
+ # * *error* (String): Error string in case deployment logs could not be retrieved. If set then further properties will be ignored. [optional]
425
+ # * *services* (Array<String>): List of services deployed on the node
426
+ # * *deployment_info* (Hash<Symbol,Object>): Deployment metadata
427
+ # * *exit_status* (Integer or Symbol): Deployment exit status
428
+ # * *stdout* (String): Deployment stdout
429
+ # * *stderr* (String): Deployment stderr
360
430
  def deployment_info_from(*nodes)
431
+ nodes = nodes.flatten
361
432
  @actions_executor.max_threads = 64
362
- Hash[@actions_executor.
363
- execute_actions(
364
- Hash[nodes.flatten.map do |node|
365
- [
366
- node,
367
- { remote_bash: "cd /var/log/deployments && ls -t | head -1 | xargs sed '/===== STDOUT =====/q'" }
368
- ]
369
- end],
370
- log_to_stdout: false,
371
- concurrent: true,
372
- timeout: 10,
373
- progress_name: 'Getting deployment info'
374
- ).
375
- map do |node, (exit_status, stdout, stderr)|
376
- # Expected format for stdout:
377
- # Property1: Value1
378
- # ...
379
- # PropertyN: ValueN
380
- # ===== STDOUT =====
381
- # ...
382
- deploy_info = {}
383
- if exit_status.is_a?(Symbol)
384
- deploy_info[:error] = "Error: #{exit_status}\n#{stderr}"
385
- else
386
- stdout_lines = stdout.split("\n")
387
- if stdout_lines.first =~ /No such file or directory/
388
- deploy_info[:error] = '/var/log/deployments missing'
389
- else
390
- stdout_lines.each do |line|
391
- if line =~ /^([^:]+): (.+)$/
392
- key_str, value = $1, $2
393
- key = key_str.to_sym
394
- # Type-cast some values
395
- case key_str
396
- when 'date'
397
- # Date and time values
398
- # Thu Nov 23 18:43:01 UTC 2017
399
- deploy_info[key] = Time.parse(value)
400
- when 'debug'
401
- # Boolean values
402
- # Yes
403
- deploy_info[key] = (value == 'Yes')
404
- when /^diff_files_.+$/, 'services'
405
- # Array of strings
406
- # my_file.txt, other_file.txt
407
- deploy_info[key] = value.split(', ')
408
- else
409
- deploy_info[key] = value
410
- end
411
- else
412
- deploy_info[:unknown_lines] = [] unless deploy_info.key?(:unknown_lines)
413
- deploy_info[:unknown_lines] << line
414
- end
415
- end
416
- end
417
- end
418
- [
419
- node,
420
- deploy_info
421
- ]
422
- end
423
- ]
433
+ read_actions_results = @actions_executor.execute_actions(
434
+ nodes.map do |node|
435
+ master_log_plugin = @log_plugins[log_plugins_for(node).first]
436
+ master_log_plugin.respond_to?(:actions_to_read_logs) ? [node, master_log_plugin.actions_to_read_logs(node)] : nil
437
+ end.compact.to_h,
438
+ log_to_stdout: false,
439
+ concurrent: true,
440
+ timeout: 10,
441
+ progress_name: 'Read deployment logs'
442
+ )
443
+ nodes.map do |node|
444
+ [
445
+ node,
446
+ @log_plugins[log_plugins_for(node).first].logs_for(node, *(read_actions_results[node] || [nil, nil, nil]))
447
+ ]
448
+ end.to_h
424
449
  end
425
450
 
426
451
  # Parse stdout and stderr of a given deploy run and get the list of tasks with their status
@@ -436,12 +461,41 @@ module HybridPlatformsConductor
436
461
  # * *:changed*: The task has been changed
437
462
  # * *:identical*: The task has not been changed
438
463
  # * *diffs* (String): Differences, if any
439
- def parse_deploy_output(node, stdout, stderr)
464
+ def parse_deploy_output(_node, stdout, stderr)
440
465
  @services_handler.parse_deploy_output(stdout, stderr).map { |deploy_info| deploy_info[:tasks] }.flatten
441
466
  end
442
467
 
443
468
  private
444
469
 
470
+ # Safe-merge 2 hashes.
471
+ # Safe-merging is done by:
472
+ # * Merging values that are hashes.
473
+ # * Reporting errors when values conflict.
474
+ # When values are conflicting, the initial hash won't modify those conflicting values and will stop the merge.
475
+ #
476
+ # Parameters::
477
+ # * *hash* (Hash): Hash to be modified merging hash_to_merge
478
+ # * *hash_to_merge* (Hash): Hash to be merged into hash
479
+ # Result::
480
+ # * nil or Array<Object>: nil in case of success, or the keys path leading to a conflicting value in case of error
481
+ def safe_merge(hash, hash_to_merge)
482
+ conflicting_path = nil
483
+ hash_to_merge.each do |key, value_to_merge|
484
+ if hash.key?(key)
485
+ if hash[key].is_a?(Hash) && value_to_merge.is_a?(Hash)
486
+ sub_conflicting_path = safe_merge(hash[key], value_to_merge)
487
+ conflicting_path = [key] + sub_conflicting_path unless sub_conflicting_path.nil?
488
+ elsif hash[key] != value_to_merge
489
+ conflicting_path = [key]
490
+ end
491
+ else
492
+ hash[key] = value_to_merge
493
+ end
494
+ break unless conflicting_path.nil?
495
+ end
496
+ conflicting_path
497
+ end
498
+
445
499
  # Get the list of retriable errors a node got from deployment logs.
446
500
  # Useful to know if an error is non-deterministic (due to external and temporary factors).
447
501
  #
@@ -452,7 +506,7 @@ module HybridPlatformsConductor
452
506
  # * *stderr* (String): Deployment stderr
453
507
  # Result::
454
508
  # * Array<String>: List of retriable errors that have been matched
455
- def retriable_errors_from(node, exit_status, stdout, stderr)
509
+ def retriable_errors_from(node, _exit_status, stdout, stderr)
456
510
  # List of retriable errors for this node, as exact string match or regexps.
457
511
  # Array<String or Regexp>
458
512
  retriable_errors_on_stdout = []
@@ -483,59 +537,55 @@ module HybridPlatformsConductor
483
537
  # Result::
484
538
  # * 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.
485
539
  def deploy(services)
486
- outputs = {}
487
-
488
540
  # Get the ssh user directly from the connector
489
541
  ssh_user = @actions_executor.connector(:ssh).ssh_user
490
542
 
491
543
  # Deploy for real
492
544
  @nodes_handler.prefetch_metadata_of services.keys, :image
493
545
  outputs = @actions_executor.execute_actions(
494
- Hash[services.map do |node, node_services|
546
+ services.map do |node, node_services|
495
547
  image_id = @nodes_handler.get_image_of(node)
496
548
  sudo = (ssh_user == 'root' ? '' : "#{@nodes_handler.sudo_on(node)} ")
497
- # Install My_company corporate certificates if present
549
+ # Install corporate certificates if present
498
550
  certificate_actions =
499
551
  if @local_environment && ENV['hpc_certificates']
500
- if File.exist?(ENV['hpc_certificates'])
501
- log_debug "Deploy certificates from #{ENV['hpc_certificates']}"
502
- case image_id
503
- when 'debian_9', 'debian_10'
504
- [
505
- {
506
- remote_bash: "#{sudo}apt update && #{sudo}apt install -y ca-certificates"
507
- },
508
- {
509
- scp: {
510
- ENV['hpc_certificates'] => '/usr/local/share/ca-certificates',
511
- :sudo => ssh_user != 'root'
512
- },
513
- remote_bash: "#{sudo}update-ca-certificates"
514
- }
515
- ]
516
- when 'centos_7'
517
- [
518
- {
519
- remote_bash: "#{sudo}yum install -y ca-certificates"
552
+ raise "Missing path referenced by the hpc_certificates environment variable: #{ENV['hpc_certificates']}" unless File.exist?(ENV['hpc_certificates'])
553
+
554
+ log_debug "Deploy certificates from #{ENV['hpc_certificates']}"
555
+ case image_id
556
+ when 'debian_9', 'debian_10'
557
+ [
558
+ {
559
+ remote_bash: "#{sudo}apt update && #{sudo}apt install -y ca-certificates"
560
+ },
561
+ {
562
+ scp: {
563
+ ENV['hpc_certificates'] => '/usr/local/share/ca-certificates',
564
+ :sudo => ssh_user != 'root'
520
565
  },
521
- {
522
- scp: Hash[Dir.glob("#{ENV['hpc_certificates']}/*.crt").map do |cert_file|
523
- [
524
- cert_file,
525
- '/etc/pki/ca-trust/source/anchors'
526
- ]
527
- end].merge(sudo: ssh_user != 'root'),
528
- remote_bash: [
529
- "#{sudo}update-ca-trust enable",
530
- "#{sudo}update-ca-trust extract"
566
+ remote_bash: "#{sudo}update-ca-certificates"
567
+ }
568
+ ]
569
+ when 'centos_7'
570
+ [
571
+ {
572
+ remote_bash: "#{sudo}yum install -y ca-certificates"
573
+ },
574
+ {
575
+ scp: Dir.glob("#{ENV['hpc_certificates']}/*.crt").map do |cert_file|
576
+ [
577
+ cert_file,
578
+ '/etc/pki/ca-trust/source/anchors'
531
579
  ]
532
- }
533
- ]
534
- else
535
- raise "Unknown image ID for node #{node}: #{image_id}. Check metadata for this node."
536
- end
580
+ end.to_h.merge(sudo: ssh_user != 'root'),
581
+ remote_bash: [
582
+ "#{sudo}update-ca-trust enable",
583
+ "#{sudo}update-ca-trust extract"
584
+ ]
585
+ }
586
+ ]
537
587
  else
538
- raise "Missing path referenced by the hpc_certificates environment variable: #{ENV['hpc_certificates']}"
588
+ raise "Unknown image ID for node #{node}: #{image_id}. Check metadata for this node."
539
589
  end
540
590
  else
541
591
  []
@@ -552,19 +602,19 @@ module HybridPlatformsConductor
552
602
  certificate_actions +
553
603
  @services_handler.actions_to_deploy_on(node, node_services, @use_why_run)
554
604
  ]
555
- end],
605
+ end.to_h,
556
606
  timeout: @timeout,
557
607
  concurrent: @concurrent_execution,
558
608
  log_to_stdout: !@concurrent_execution
559
609
  )
560
610
  # Free eventual locks
561
611
  @actions_executor.execute_actions(
562
- Hash[services.keys.map do |node|
612
+ services.keys.map do |node|
563
613
  [
564
614
  node,
565
615
  { remote_bash: "#{ssh_user == 'root' ? '' : "#{@nodes_handler.sudo_on(node)} "}./mutex_dir unlock /tmp/hybrid_platforms_conductor_deploy_lock" }
566
616
  ]
567
- end],
617
+ end.to_h,
568
618
  timeout: 10,
569
619
  concurrent: true,
570
620
  log_to_dir: nil
@@ -584,47 +634,48 @@ module HybridPlatformsConductor
584
634
  # * *services* (Hash<String, Array<String>>): List of services that have been deployed, per node
585
635
  def save_logs(logs, services)
586
636
  section "Saving deployment logs for #{logs.size} nodes" do
587
- Dir.mktmpdir('hybrid_platforms_conductor-logs') do |tmp_dir|
588
- ssh_user = @actions_executor.connector(:ssh).ssh_user
589
- @actions_executor.execute_actions(
590
- Hash[logs.map do |node, (exit_status, stdout, stderr)|
591
- # Create a log file to be scp with all relevant info
592
- now = Time.now.utc
593
- log_file = "#{tmp_dir}/#{node}_#{now.strftime('%F_%H%M%S')}_#{ssh_user}"
594
- services_info = @services_handler.log_info_for(node, services[node])
595
- File.write(
596
- log_file,
597
- services_info.merge(
598
- date: now.strftime('%F %T'),
599
- user: ssh_user,
600
- debug: log_debug? ? 'Yes' : 'No',
601
- services: services[node].join(', '),
602
- exit_status: exit_status
603
- ).map { |property, value| "#{property}: #{value}" }.join("\n") +
604
- "\n===== STDOUT =====\n" +
605
- (stdout || '') +
606
- "\n===== STDERR =====\n" +
607
- (stderr || '')
608
- )
609
- [
610
- node,
611
- {
612
- remote_bash: "#{ssh_user == 'root' ? '' : "#{@nodes_handler.sudo_on(node)} "}mkdir -p /var/log/deployments",
613
- scp: {
614
- log_file => '/var/log/deployments',
615
- :sudo => ssh_user != 'root',
616
- :owner => 'root',
617
- :group => 'root'
618
- }
619
- }
620
- ]
621
- end],
622
- timeout: 10,
623
- concurrent: true,
624
- log_to_dir: nil
625
- )
626
- end
637
+ ssh_user = @actions_executor.connector(:ssh).ssh_user
638
+ @actions_executor.execute_actions(
639
+ logs.map do |node, (exit_status, stdout, stderr)|
640
+ [
641
+ node,
642
+ log_plugins_for(node).
643
+ map do |log_plugin|
644
+ @log_plugins[log_plugin].actions_to_save_logs(
645
+ node,
646
+ services[node],
647
+ @services_handler.log_info_for(node, services[node]).merge(
648
+ date: Time.now.utc.strftime('%F %T'),
649
+ user: ssh_user
650
+ ),
651
+ exit_status,
652
+ stdout,
653
+ stderr
654
+ )
655
+ end.
656
+ flatten(1)
657
+ ]
658
+ end.to_h,
659
+ timeout: 10,
660
+ concurrent: true,
661
+ log_to_dir: nil,
662
+ progress_name: 'Saving logs'
663
+ )
664
+ end
665
+ end
666
+
667
+ # Get the list of log plugins to be used for a given node
668
+ #
669
+ # Parameters::
670
+ # * *node* (String): The node for which log plugins are queried
671
+ # Result::
672
+ # * Array<Symbol>: The list of log plugins
673
+ def log_plugins_for(node)
674
+ node_log_plugins = @nodes_handler.select_confs_for_node(node, @config.deployment_logs).inject([]) do |log_plugins, deployment_logs_info|
675
+ log_plugins + deployment_logs_info[:log_plugins]
627
676
  end
677
+ node_log_plugins << :remote_fs if node_log_plugins.empty?
678
+ node_log_plugins
628
679
  end
629
680
 
630
681
  end