hybrid_platforms_conductor 32.3.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (244) hide show
  1. checksums.yaml +7 -0
  2. data/bin/check-node +24 -0
  3. data/bin/deploy +12 -0
  4. data/bin/dump_nodes_json +12 -0
  5. data/bin/free_ips +23 -0
  6. data/bin/free_veids +17 -0
  7. data/bin/get_impacted_nodes +43 -0
  8. data/bin/last_deploys +56 -0
  9. data/bin/nodes_to_deploy +104 -0
  10. data/bin/report +10 -0
  11. data/bin/run +39 -0
  12. data/bin/setup +11 -0
  13. data/bin/ssh_config +14 -0
  14. data/bin/test +13 -0
  15. data/bin/topograph +54 -0
  16. data/lib/hybrid_platforms_conductor/action.rb +82 -0
  17. data/lib/hybrid_platforms_conductor/actions_executor.rb +307 -0
  18. data/lib/hybrid_platforms_conductor/bitbucket.rb +123 -0
  19. data/lib/hybrid_platforms_conductor/cmd_runner.rb +188 -0
  20. data/lib/hybrid_platforms_conductor/cmdb.rb +34 -0
  21. data/lib/hybrid_platforms_conductor/common_config_dsl/bitbucket.rb +78 -0
  22. data/lib/hybrid_platforms_conductor/common_config_dsl/confluence.rb +43 -0
  23. data/lib/hybrid_platforms_conductor/common_config_dsl/file_system_tests.rb +110 -0
  24. data/lib/hybrid_platforms_conductor/common_config_dsl/idempotence_tests.rb +38 -0
  25. data/lib/hybrid_platforms_conductor/config.rb +263 -0
  26. data/lib/hybrid_platforms_conductor/confluence.rb +119 -0
  27. data/lib/hybrid_platforms_conductor/connector.rb +84 -0
  28. data/lib/hybrid_platforms_conductor/credentials.rb +127 -0
  29. data/lib/hybrid_platforms_conductor/current_dir_monitor.rb +42 -0
  30. data/lib/hybrid_platforms_conductor/deployer.rb +598 -0
  31. data/lib/hybrid_platforms_conductor/executable.rb +145 -0
  32. data/lib/hybrid_platforms_conductor/hpc_plugins/action/bash.rb +44 -0
  33. data/lib/hybrid_platforms_conductor/hpc_plugins/action/interactive.rb +44 -0
  34. data/lib/hybrid_platforms_conductor/hpc_plugins/action/my_action.rb.sample +79 -0
  35. data/lib/hybrid_platforms_conductor/hpc_plugins/action/remote_bash.rb +63 -0
  36. data/lib/hybrid_platforms_conductor/hpc_plugins/action/ruby.rb +69 -0
  37. data/lib/hybrid_platforms_conductor/hpc_plugins/action/scp.rb +61 -0
  38. data/lib/hybrid_platforms_conductor/hpc_plugins/cmdb/config.rb +78 -0
  39. data/lib/hybrid_platforms_conductor/hpc_plugins/cmdb/host_ip.rb +104 -0
  40. data/lib/hybrid_platforms_conductor/hpc_plugins/cmdb/host_keys.rb +114 -0
  41. data/lib/hybrid_platforms_conductor/hpc_plugins/cmdb/my_cmdb.rb.sample +129 -0
  42. data/lib/hybrid_platforms_conductor/hpc_plugins/cmdb/platform_handlers.rb +66 -0
  43. data/lib/hybrid_platforms_conductor/hpc_plugins/connector/my_connector.rb.sample +156 -0
  44. data/lib/hybrid_platforms_conductor/hpc_plugins/connector/ssh.rb +702 -0
  45. data/lib/hybrid_platforms_conductor/hpc_plugins/platform_handler/platform_handler_plugin.rb.sample +292 -0
  46. data/lib/hybrid_platforms_conductor/hpc_plugins/provisioner/docker.rb +148 -0
  47. data/lib/hybrid_platforms_conductor/hpc_plugins/provisioner/my_provisioner.rb.sample +103 -0
  48. data/lib/hybrid_platforms_conductor/hpc_plugins/provisioner/podman.rb +125 -0
  49. data/lib/hybrid_platforms_conductor/hpc_plugins/provisioner/proxmox.rb +522 -0
  50. data/lib/hybrid_platforms_conductor/hpc_plugins/provisioner/proxmox/proxmox_waiter.rb +707 -0
  51. data/lib/hybrid_platforms_conductor/hpc_plugins/provisioner/proxmox/reserve_proxmox_container +122 -0
  52. data/lib/hybrid_platforms_conductor/hpc_plugins/report/confluence.rb +69 -0
  53. data/lib/hybrid_platforms_conductor/hpc_plugins/report/mediawiki.rb +164 -0
  54. data/lib/hybrid_platforms_conductor/hpc_plugins/report/my_report_plugin.rb.sample +88 -0
  55. data/lib/hybrid_platforms_conductor/hpc_plugins/report/stdout.rb +61 -0
  56. data/lib/hybrid_platforms_conductor/hpc_plugins/report/templates/confluence_inventory.html.erb +33 -0
  57. data/lib/hybrid_platforms_conductor/hpc_plugins/test/bitbucket_conf.rb +137 -0
  58. data/lib/hybrid_platforms_conductor/hpc_plugins/test/can_be_checked.rb +21 -0
  59. data/lib/hybrid_platforms_conductor/hpc_plugins/test/check_deploy_and_idempotence.rb +112 -0
  60. data/lib/hybrid_platforms_conductor/hpc_plugins/test/check_from_scratch.rb +35 -0
  61. data/lib/hybrid_platforms_conductor/hpc_plugins/test/connection.rb +28 -0
  62. data/lib/hybrid_platforms_conductor/hpc_plugins/test/deploy_freshness.rb +44 -0
  63. data/lib/hybrid_platforms_conductor/hpc_plugins/test/deploy_from_scratch.rb +36 -0
  64. data/lib/hybrid_platforms_conductor/hpc_plugins/test/deploy_removes_root_access.rb +49 -0
  65. data/lib/hybrid_platforms_conductor/hpc_plugins/test/divergence.rb +25 -0
  66. data/lib/hybrid_platforms_conductor/hpc_plugins/test/executables.rb +46 -0
  67. data/lib/hybrid_platforms_conductor/hpc_plugins/test/file_system.rb +45 -0
  68. data/lib/hybrid_platforms_conductor/hpc_plugins/test/file_system_hdfs.rb +45 -0
  69. data/lib/hybrid_platforms_conductor/hpc_plugins/test/hostname.rb +25 -0
  70. data/lib/hybrid_platforms_conductor/hpc_plugins/test/idempotence.rb +77 -0
  71. data/lib/hybrid_platforms_conductor/hpc_plugins/test/ip.rb +38 -0
  72. data/lib/hybrid_platforms_conductor/hpc_plugins/test/jenkins_ci_conf.rb +56 -0
  73. data/lib/hybrid_platforms_conductor/hpc_plugins/test/jenkins_ci_masters_ok.rb +54 -0
  74. data/lib/hybrid_platforms_conductor/hpc_plugins/test/linear_strategy.rb +47 -0
  75. data/lib/hybrid_platforms_conductor/hpc_plugins/test/local_users.rb +82 -0
  76. data/lib/hybrid_platforms_conductor/hpc_plugins/test/mounts.rb +120 -0
  77. data/lib/hybrid_platforms_conductor/hpc_plugins/test/my_test_plugin.rb.sample +143 -0
  78. data/lib/hybrid_platforms_conductor/hpc_plugins/test/orphan_files.rb +74 -0
  79. data/lib/hybrid_platforms_conductor/hpc_plugins/test/ports.rb +85 -0
  80. data/lib/hybrid_platforms_conductor/hpc_plugins/test/private_ips.rb +38 -0
  81. data/lib/hybrid_platforms_conductor/hpc_plugins/test/public_ips.rb +38 -0
  82. data/lib/hybrid_platforms_conductor/hpc_plugins/test/spectre-meltdown-checker.sh +1930 -0
  83. data/lib/hybrid_platforms_conductor/hpc_plugins/test/spectre.rb +56 -0
  84. data/lib/hybrid_platforms_conductor/hpc_plugins/test/veids.rb +31 -0
  85. data/lib/hybrid_platforms_conductor/hpc_plugins/test/vulnerabilities.rb +159 -0
  86. data/lib/hybrid_platforms_conductor/hpc_plugins/test_report/confluence.rb +122 -0
  87. data/lib/hybrid_platforms_conductor/hpc_plugins/test_report/my_test_report.rb.sample +48 -0
  88. data/lib/hybrid_platforms_conductor/hpc_plugins/test_report/stdout.rb +120 -0
  89. data/lib/hybrid_platforms_conductor/hpc_plugins/test_report/templates/_confluence_errors_status.html.erb +46 -0
  90. data/lib/hybrid_platforms_conductor/hpc_plugins/test_report/templates/_confluence_gauge.html.erb +49 -0
  91. data/lib/hybrid_platforms_conductor/hpc_plugins/test_report/templates/confluence.html.erb +242 -0
  92. data/lib/hybrid_platforms_conductor/io_router.rb +70 -0
  93. data/lib/hybrid_platforms_conductor/json_dumper.rb +88 -0
  94. data/lib/hybrid_platforms_conductor/logger_helpers.rb +319 -0
  95. data/lib/hybrid_platforms_conductor/mutex_dir +76 -0
  96. data/lib/hybrid_platforms_conductor/nodes_handler.rb +597 -0
  97. data/lib/hybrid_platforms_conductor/parallel_threads.rb +97 -0
  98. data/lib/hybrid_platforms_conductor/platform_handler.rb +188 -0
  99. data/lib/hybrid_platforms_conductor/platforms_handler.rb +118 -0
  100. data/lib/hybrid_platforms_conductor/plugin.rb +53 -0
  101. data/lib/hybrid_platforms_conductor/plugins.rb +101 -0
  102. data/lib/hybrid_platforms_conductor/provisioner.rb +181 -0
  103. data/lib/hybrid_platforms_conductor/report.rb +31 -0
  104. data/lib/hybrid_platforms_conductor/reports_handler.rb +84 -0
  105. data/lib/hybrid_platforms_conductor/services_handler.rb +274 -0
  106. data/lib/hybrid_platforms_conductor/test.rb +141 -0
  107. data/lib/hybrid_platforms_conductor/test_by_service.rb +22 -0
  108. data/lib/hybrid_platforms_conductor/test_report.rb +282 -0
  109. data/lib/hybrid_platforms_conductor/tests_runner.rb +590 -0
  110. data/lib/hybrid_platforms_conductor/thycotic.rb +92 -0
  111. data/lib/hybrid_platforms_conductor/topographer.rb +859 -0
  112. data/lib/hybrid_platforms_conductor/topographer/plugin.rb +20 -0
  113. data/lib/hybrid_platforms_conductor/topographer/plugins/graphviz.rb +127 -0
  114. data/lib/hybrid_platforms_conductor/topographer/plugins/json.rb +72 -0
  115. data/lib/hybrid_platforms_conductor/topographer/plugins/my_topographer_output_plugin.rb.sample +37 -0
  116. data/lib/hybrid_platforms_conductor/topographer/plugins/svg.rb +30 -0
  117. data/lib/hybrid_platforms_conductor/version.rb +5 -0
  118. data/spec/hybrid_platforms_conductor_test.rb +159 -0
  119. data/spec/hybrid_platforms_conductor_test/api/actions_executor/actions/bash_spec.rb +43 -0
  120. data/spec/hybrid_platforms_conductor_test/api/actions_executor/actions/interactive_spec.rb +18 -0
  121. data/spec/hybrid_platforms_conductor_test/api/actions_executor/actions/remote_bash_spec.rb +102 -0
  122. data/spec/hybrid_platforms_conductor_test/api/actions_executor/actions/ruby_spec.rb +108 -0
  123. data/spec/hybrid_platforms_conductor_test/api/actions_executor/actions/scp_spec.rb +79 -0
  124. data/spec/hybrid_platforms_conductor_test/api/actions_executor/actions_spec.rb +199 -0
  125. data/spec/hybrid_platforms_conductor_test/api/actions_executor/connection_spec.rb +212 -0
  126. data/spec/hybrid_platforms_conductor_test/api/actions_executor/connectors/ssh/cli_options_spec.rb +125 -0
  127. data/spec/hybrid_platforms_conductor_test/api/actions_executor/connectors/ssh/config_dsl_spec.rb +50 -0
  128. data/spec/hybrid_platforms_conductor_test/api/actions_executor/connectors/ssh/connectable_nodes_spec.rb +28 -0
  129. data/spec/hybrid_platforms_conductor_test/api/actions_executor/connectors/ssh/connections_spec.rb +448 -0
  130. data/spec/hybrid_platforms_conductor_test/api/actions_executor/connectors/ssh/global_helpers_spec.rb +313 -0
  131. data/spec/hybrid_platforms_conductor_test/api/actions_executor/connectors/ssh/node_helpers_spec.rb +32 -0
  132. data/spec/hybrid_platforms_conductor_test/api/actions_executor/connectors/ssh/remote_actions_spec.rb +134 -0
  133. data/spec/hybrid_platforms_conductor_test/api/actions_executor/logging_spec.rb +256 -0
  134. data/spec/hybrid_platforms_conductor_test/api/actions_executor/parallel_spec.rb +338 -0
  135. data/spec/hybrid_platforms_conductor_test/api/actions_executor/timeout_spec.rb +101 -0
  136. data/spec/hybrid_platforms_conductor_test/api/cmd_runner_spec.rb +165 -0
  137. data/spec/hybrid_platforms_conductor_test/api/config_spec.rb +238 -0
  138. data/spec/hybrid_platforms_conductor_test/api/deployer/check_spec.rb +9 -0
  139. data/spec/hybrid_platforms_conductor_test/api/deployer/deploy_spec.rb +243 -0
  140. data/spec/hybrid_platforms_conductor_test/api/deployer/parse_deploy_output_spec.rb +104 -0
  141. data/spec/hybrid_platforms_conductor_test/api/deployer/provisioner_spec.rb +131 -0
  142. data/spec/hybrid_platforms_conductor_test/api/deployer/provisioners/docker/Dockerfile +10 -0
  143. data/spec/hybrid_platforms_conductor_test/api/deployer/provisioners/docker_spec.rb +123 -0
  144. data/spec/hybrid_platforms_conductor_test/api/deployer/provisioners/podman_spec.rb +211 -0
  145. data/spec/hybrid_platforms_conductor_test/api/deployer/provisioners/proxmox/config_dsl_spec.rb +126 -0
  146. data/spec/hybrid_platforms_conductor_test/api/deployer/provisioners/proxmox/create_spec.rb +290 -0
  147. data/spec/hybrid_platforms_conductor_test/api/deployer/provisioners/proxmox/destroy_spec.rb +43 -0
  148. data/spec/hybrid_platforms_conductor_test/api/deployer/provisioners/proxmox/ip_spec.rb +60 -0
  149. data/spec/hybrid_platforms_conductor_test/api/deployer/provisioners/proxmox/proxmox.json +3 -0
  150. data/spec/hybrid_platforms_conductor_test/api/deployer/provisioners/proxmox/reserve_proxmox_container/destroy_vm_spec.rb +82 -0
  151. data/spec/hybrid_platforms_conductor_test/api/deployer/provisioners/proxmox/reserve_proxmox_container/expired_containers_spec.rb +786 -0
  152. data/spec/hybrid_platforms_conductor_test/api/deployer/provisioners/proxmox/reserve_proxmox_container/ips_assignment_spec.rb +112 -0
  153. data/spec/hybrid_platforms_conductor_test/api/deployer/provisioners/proxmox/reserve_proxmox_container/other_lxc_containers_resources_spec.rb +190 -0
  154. data/spec/hybrid_platforms_conductor_test/api/deployer/provisioners/proxmox/reserve_proxmox_container/pve_node_resources_spec.rb +200 -0
  155. data/spec/hybrid_platforms_conductor_test/api/deployer/provisioners/proxmox/reserve_proxmox_container/retries_spec.rb +35 -0
  156. data/spec/hybrid_platforms_conductor_test/api/deployer/provisioners/proxmox/reserve_proxmox_container/vm_ids_assignment_spec.rb +67 -0
  157. data/spec/hybrid_platforms_conductor_test/api/deployer/provisioners/proxmox/start_spec.rb +79 -0
  158. data/spec/hybrid_platforms_conductor_test/api/deployer/provisioners/proxmox/state_spec.rb +28 -0
  159. data/spec/hybrid_platforms_conductor_test/api/deployer/provisioners/proxmox/stop_spec.rb +41 -0
  160. data/spec/hybrid_platforms_conductor_test/api/nodes_handler/cmdbs/config_spec.rb +33 -0
  161. data/spec/hybrid_platforms_conductor_test/api/nodes_handler/cmdbs/host_ip_spec.rb +64 -0
  162. data/spec/hybrid_platforms_conductor_test/api/nodes_handler/cmdbs/host_keys_spec.rb +133 -0
  163. data/spec/hybrid_platforms_conductor_test/api/nodes_handler/cmdbs/platform_handlers_spec.rb +19 -0
  164. data/spec/hybrid_platforms_conductor_test/api/nodes_handler/cmdbs_plugins_api_spec.rb +446 -0
  165. data/spec/hybrid_platforms_conductor_test/api/nodes_handler/common_spec.rb +127 -0
  166. data/spec/hybrid_platforms_conductor_test/api/nodes_handler/git_diff_impacts_spec.rb +318 -0
  167. data/spec/hybrid_platforms_conductor_test/api/nodes_handler/nodes_selectors_spec.rb +132 -0
  168. data/spec/hybrid_platforms_conductor_test/api/nodes_handler/platform_handlers_plugins_api_spec.rb +60 -0
  169. data/spec/hybrid_platforms_conductor_test/api/nodes_handler/several_platforms_spec.rb +58 -0
  170. data/spec/hybrid_platforms_conductor_test/api/platform_handler_spec.rb +97 -0
  171. data/spec/hybrid_platforms_conductor_test/api/platforms_handler_spec.rb +104 -0
  172. data/spec/hybrid_platforms_conductor_test/api/plugins_spec.rb +243 -0
  173. data/spec/hybrid_platforms_conductor_test/api/reports_handler_spec.rb +44 -0
  174. data/spec/hybrid_platforms_conductor_test/api/services_handler/actions_to_deploy_spec.rb +121 -0
  175. data/spec/hybrid_platforms_conductor_test/api/services_handler/deploy_allowed_spec.rb +142 -0
  176. data/spec/hybrid_platforms_conductor_test/api/services_handler/log_info_spec.rb +101 -0
  177. data/spec/hybrid_platforms_conductor_test/api/services_handler/package_spec.rb +388 -0
  178. data/spec/hybrid_platforms_conductor_test/api/services_handler/parse_deploy_output_spec.rb +274 -0
  179. data/spec/hybrid_platforms_conductor_test/api/services_handler/prepare_for_deploy_spec.rb +264 -0
  180. data/spec/hybrid_platforms_conductor_test/api/tests_runner/common_spec.rb +194 -0
  181. data/spec/hybrid_platforms_conductor_test/api/tests_runner/global_spec.rb +37 -0
  182. data/spec/hybrid_platforms_conductor_test/api/tests_runner/node_check_spec.rb +194 -0
  183. data/spec/hybrid_platforms_conductor_test/api/tests_runner/node_spec.rb +137 -0
  184. data/spec/hybrid_platforms_conductor_test/api/tests_runner/node_ssh_spec.rb +257 -0
  185. data/spec/hybrid_platforms_conductor_test/api/tests_runner/platform_spec.rb +110 -0
  186. data/spec/hybrid_platforms_conductor_test/api/tests_runner/reports_spec.rb +367 -0
  187. data/spec/hybrid_platforms_conductor_test/api/tests_runner/test_plugins/bitbucket_conf_spec.rb +111 -0
  188. data/spec/hybrid_platforms_conductor_test/api/tests_runner/test_reports_plugins/confluence_spec.rb +29 -0
  189. data/spec/hybrid_platforms_conductor_test/cmdb_plugins/test_cmdb.rb +166 -0
  190. data/spec/hybrid_platforms_conductor_test/cmdb_plugins/test_cmdb2.rb +93 -0
  191. data/spec/hybrid_platforms_conductor_test/cmdb_plugins/test_cmdb_others.rb +60 -0
  192. data/spec/hybrid_platforms_conductor_test/cmdb_plugins/test_cmdb_others2.rb +58 -0
  193. data/spec/hybrid_platforms_conductor_test/executables/check-node_spec.rb +35 -0
  194. data/spec/hybrid_platforms_conductor_test/executables/deploy_spec.rb +35 -0
  195. data/spec/hybrid_platforms_conductor_test/executables/get_impacted_nodes_spec.rb +158 -0
  196. data/spec/hybrid_platforms_conductor_test/executables/last_deploys_spec.rb +173 -0
  197. data/spec/hybrid_platforms_conductor_test/executables/nodes_to_deploy_spec.rb +283 -0
  198. data/spec/hybrid_platforms_conductor_test/executables/options/actions_executor_spec.rb +28 -0
  199. data/spec/hybrid_platforms_conductor_test/executables/options/cmd_runner_spec.rb +28 -0
  200. data/spec/hybrid_platforms_conductor_test/executables/options/common_spec.rb +67 -0
  201. data/spec/hybrid_platforms_conductor_test/executables/options/deployer_spec.rb +251 -0
  202. data/spec/hybrid_platforms_conductor_test/executables/options/nodes_handler_spec.rb +111 -0
  203. data/spec/hybrid_platforms_conductor_test/executables/options/nodes_selectors_spec.rb +71 -0
  204. data/spec/hybrid_platforms_conductor_test/executables/options/reports_handler_spec.rb +54 -0
  205. data/spec/hybrid_platforms_conductor_test/executables/options/tests_runner_spec.rb +139 -0
  206. data/spec/hybrid_platforms_conductor_test/executables/report_spec.rb +60 -0
  207. data/spec/hybrid_platforms_conductor_test/executables/run_spec.rb +173 -0
  208. data/spec/hybrid_platforms_conductor_test/executables/ssh_config_spec.rb +35 -0
  209. data/spec/hybrid_platforms_conductor_test/executables/test_spec.rb +41 -0
  210. data/spec/hybrid_platforms_conductor_test/helpers/actions_executor_helpers.rb +98 -0
  211. data/spec/hybrid_platforms_conductor_test/helpers/cmd_runner_helpers.rb +92 -0
  212. data/spec/hybrid_platforms_conductor_test/helpers/cmdb_helpers.rb +37 -0
  213. data/spec/hybrid_platforms_conductor_test/helpers/config_helpers.rb +20 -0
  214. data/spec/hybrid_platforms_conductor_test/helpers/connector_ssh_helpers.rb +130 -0
  215. data/spec/hybrid_platforms_conductor_test/helpers/deployer_helpers.rb +149 -0
  216. data/spec/hybrid_platforms_conductor_test/helpers/deployer_test_helpers.rb +812 -0
  217. data/spec/hybrid_platforms_conductor_test/helpers/executables_helpers.rb +96 -0
  218. data/spec/hybrid_platforms_conductor_test/helpers/nodes_handler_helpers.rb +20 -0
  219. data/spec/hybrid_platforms_conductor_test/helpers/platform_handler_helpers.rb +35 -0
  220. data/spec/hybrid_platforms_conductor_test/helpers/platforms_handler_helpers.rb +127 -0
  221. data/spec/hybrid_platforms_conductor_test/helpers/plugins_helpers.rb +48 -0
  222. data/spec/hybrid_platforms_conductor_test/helpers/provisioner_proxmox_helpers.rb +789 -0
  223. data/spec/hybrid_platforms_conductor_test/helpers/reports_handler_helpers.rb +29 -0
  224. data/spec/hybrid_platforms_conductor_test/helpers/services_handler_helpers.rb +20 -0
  225. data/spec/hybrid_platforms_conductor_test/helpers/tests_runner_helpers.rb +38 -0
  226. data/spec/hybrid_platforms_conductor_test/mocked_lib/my_test_gem/hpc_plugins/test_plugin_type/test_plugin_id1.rb +22 -0
  227. data/spec/hybrid_platforms_conductor_test/mocked_lib/my_test_gem/hpc_plugins/test_plugin_type/test_plugin_id2.rb +22 -0
  228. data/spec/hybrid_platforms_conductor_test/mocked_lib/my_test_gem2/sub_dir/hpc_plugins/test_plugin_type/test_plugin_id3.rb +26 -0
  229. data/spec/hybrid_platforms_conductor_test/mocked_lib/my_test_gem2/sub_dir/hpc_plugins/test_plugin_type2/test_plugin_id4.rb +26 -0
  230. data/spec/hybrid_platforms_conductor_test/platform_handler_plugins/test.rb +225 -0
  231. data/spec/hybrid_platforms_conductor_test/platform_handler_plugins/test2.rb +11 -0
  232. data/spec/hybrid_platforms_conductor_test/report_plugin.rb +35 -0
  233. data/spec/hybrid_platforms_conductor_test/test_action.rb +66 -0
  234. data/spec/hybrid_platforms_conductor_test/test_connector.rb +151 -0
  235. data/spec/hybrid_platforms_conductor_test/test_plugins/global.rb +30 -0
  236. data/spec/hybrid_platforms_conductor_test/test_plugins/node.rb +53 -0
  237. data/spec/hybrid_platforms_conductor_test/test_plugins/node_check.rb +47 -0
  238. data/spec/hybrid_platforms_conductor_test/test_plugins/node_ssh.rb +42 -0
  239. data/spec/hybrid_platforms_conductor_test/test_plugins/platform.rb +50 -0
  240. data/spec/hybrid_platforms_conductor_test/test_plugins/several_checks.rb +50 -0
  241. data/spec/hybrid_platforms_conductor_test/test_provisioner.rb +95 -0
  242. data/spec/hybrid_platforms_conductor_test/tests_report_plugin.rb +49 -0
  243. data/spec/spec_helper.rb +111 -0
  244. metadata +566 -0
@@ -0,0 +1,82 @@
1
+ require 'hybrid_platforms_conductor/logger_helpers'
2
+ require 'hybrid_platforms_conductor/plugin'
3
+
4
+ module HybridPlatformsConductor
5
+
6
+ # Base class for any action that could be run on a node.
7
+ class Action < Plugin
8
+
9
+ # Constructor
10
+ #
11
+ # Parameters::
12
+ # * *logger* (Logger): Logger to be used [default: Logger.new(STDOUT)]
13
+ # * *logger_stderr* (Logger): Logger to be used for stderr [default: Logger.new(STDERR)]
14
+ # * *config* (Config): Config to be used. [default: Config.new]
15
+ # * *cmd_runner* (CmdRunner): Command executor to be used. [default: CmdRunner.new]
16
+ # * *actions_executor* (ActionsExecutor): Actions Executor to be used. [default: ActionsExecutor.new]
17
+ # * *action_info* (Object or nil): Action info needed to setup the action, or nil if none [default: nil]
18
+ def initialize(
19
+ logger: Logger.new(STDOUT),
20
+ logger_stderr: Logger.new(STDERR),
21
+ config: Config.new,
22
+ cmd_runner: CmdRunner.new,
23
+ actions_executor: ActionsExecutor.new,
24
+ action_info: nil
25
+ )
26
+ super(logger: logger, logger_stderr: logger_stderr, config: config)
27
+ @cmd_runner = cmd_runner
28
+ @actions_executor = actions_executor
29
+ @action_info = action_info
30
+ setup(@action_info) if self.respond_to?(:setup)
31
+ end
32
+
33
+ # Do we need a connector to execute this action on a node?
34
+ #
35
+ # Result::
36
+ # * Boolean: Do we need a connector to execute this action on a node?
37
+ def need_connector?
38
+ false
39
+ end
40
+
41
+ # Prepare an action to be run for a given node in a given context.
42
+ # It is required to call this method before executing the action.
43
+ #
44
+ # Paramaters::
45
+ # * *node* (String): The node this actions is targetting
46
+ # * *connector* (Connector or nil): Connector to use to connect to this node, or nil if none
47
+ # * *timeout* (Integer or nil): Timeout this action should have (in seconds), or nil if none
48
+ # * *stdout_io* (IO): IO to log stdout to
49
+ # * *stderr_io* (IO): IO to log stderr to
50
+ def prepare_for(node, connector, timeout, stdout_io, stderr_io)
51
+ @node = node
52
+ @connector = connector
53
+ @timeout = timeout
54
+ @stdout_io = stdout_io
55
+ @stderr_io = stderr_io
56
+ @connector.prepare_for(@node, @timeout, @stdout_io, @stderr_io) if @connector
57
+ end
58
+
59
+ private
60
+
61
+ # Run a command.
62
+ # Handle the redirection of standard output and standard error to file and stdout depending on the context of the run.
63
+ #
64
+ # Parameters::
65
+ # * *cmd* (String): The command to be run
66
+ # Result::
67
+ # * Integer: Exit code
68
+ # * String: Standard output
69
+ # * String: Error output
70
+ def run_cmd(cmd)
71
+ @cmd_runner.run_cmd(
72
+ cmd,
73
+ timeout: @timeout,
74
+ log_to_stdout: false,
75
+ log_stdout_to_io: @stdout_io,
76
+ log_stderr_to_io: @stderr_io
77
+ )
78
+ end
79
+
80
+ end
81
+
82
+ end
@@ -0,0 +1,307 @@
1
+ require 'fileutils'
2
+ require 'futex'
3
+ require 'logger'
4
+ require 'securerandom'
5
+ require 'tmpdir'
6
+ require 'hybrid_platforms_conductor/action'
7
+ require 'hybrid_platforms_conductor/cmd_runner'
8
+ require 'hybrid_platforms_conductor/connector'
9
+ require 'hybrid_platforms_conductor/io_router'
10
+ require 'hybrid_platforms_conductor/logger_helpers'
11
+ require 'hybrid_platforms_conductor/nodes_handler'
12
+ require 'hybrid_platforms_conductor/plugins'
13
+
14
+ module HybridPlatformsConductor
15
+
16
+ # Gives ways to execute actions on the nodes
17
+ class ActionsExecutor
18
+
19
+ # Error class returned when the issue is due to a connection issue to the node
20
+ class ConnectionError < RuntimeError
21
+ end
22
+
23
+ include LoggerHelpers
24
+
25
+ # Maximum number of threads to spawn in parallel [default: 8]
26
+ # Integer
27
+ attr_accessor :max_threads
28
+
29
+ # Constructor
30
+ #
31
+ # Parameters::
32
+ # * *logger* (Logger): Logger to be used [default = Logger.new(STDOUT)]
33
+ # * *logger_stderr* (Logger): Logger to be used for stderr [default = Logger.new(STDERR)]
34
+ # * *config* (Config): Config to be used. [default = Config.new]
35
+ # * *cmd_runner* (CmdRunner): Command runner to be used. [default = CmdRunner.new]
36
+ # * *nodes_handler* (NodesHandler): Nodes handler to be used. [default = NodesHandler.new]
37
+ def initialize(logger: Logger.new(STDOUT), logger_stderr: Logger.new(STDERR), config: Config.new, cmd_runner: CmdRunner.new, nodes_handler: NodesHandler.new)
38
+ init_loggers(logger, logger_stderr)
39
+ @config = config
40
+ @cmd_runner = cmd_runner
41
+ @nodes_handler = nodes_handler
42
+ # Default values
43
+ @max_threads = 16
44
+ @action_plugins = Plugins.new(:action, logger: @logger, logger_stderr: @logger_stderr)
45
+ @connector_plugins = Plugins.new(
46
+ :connector,
47
+ logger: @logger,
48
+ logger_stderr: @logger_stderr,
49
+ init_plugin: proc do |plugin_class|
50
+ plugin_class.new(
51
+ logger: @logger,
52
+ logger_stderr: @logger_stderr,
53
+ config: @config,
54
+ cmd_runner: @cmd_runner,
55
+ nodes_handler: @nodes_handler
56
+ )
57
+ end
58
+ )
59
+ end
60
+
61
+ # Complete an option parser with options meant to control this Actions Executor
62
+ #
63
+ # Parameters::
64
+ # * *options_parser* (OptionParser): The option parser to complete
65
+ # * *parallel* (Boolean): Do we activate options regarding parallel execution? [default = true]
66
+ def options_parse(options_parser, parallel: true)
67
+ if parallel
68
+ options_parser.separator ''
69
+ options_parser.separator 'Actions Executor options:'
70
+ options_parser.on('-m', '--max-threads NBR', "Set the number of threads to use for concurrent queries (defaults to #{@max_threads})") do |nbr_threads|
71
+ @max_threads = nbr_threads.to_i
72
+ end
73
+ end
74
+ # Display options connectors might have
75
+ @connector_plugins.each do |connector_name, connector|
76
+ if connector.respond_to?(:options_parse)
77
+ options_parser.separator ''
78
+ options_parser.separator "Connector #{connector_name} options:"
79
+ connector.options_parse(options_parser)
80
+ end
81
+ end
82
+ end
83
+
84
+ # Validate that parsed parameters are valid
85
+ def validate_params
86
+ @connector_plugins.values.each do |connector|
87
+ connector.validate_params if connector.respond_to?(:validate_params)
88
+ end
89
+ end
90
+
91
+ # Execute actions on nodes.
92
+ #
93
+ # Parameters::
94
+ # * *actions_per_nodes* (Hash<Object, Hash<Symbol,Object> or Array< Hash<Symbol,Object> >): Actions (as a Hash of actions or a list of Hash), per nodes selector.
95
+ # See NodesHandler#select_nodes for details about possible nodes selectors.
96
+ # See each action's setup in actions directory to know about the possible action types and data.
97
+ # * *timeout* (Integer): Timeout in seconds, or nil if none. [default: nil]
98
+ # * *concurrent* (Boolean): Do we run the commands in parallel? If yes, then stdout of commands is stored in log files. [default: false]
99
+ # * *log_to_dir* (String or nil): Directory name to store log files. Can be nil to not store log files. [default: "#{@config.hybrid_platforms_dir}/run_logs"]
100
+ # * *log_to_stdout* (Boolean): Do we log the command result on stdout? [default: true]
101
+ # * *progress_name* (String): Name to display on the progress bar [default: 'Executing actions']
102
+ # Result::
103
+ # * 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.
104
+ def execute_actions(actions_per_nodes, timeout: nil, concurrent: false, log_to_dir: "#{@config.hybrid_platforms_dir}/run_logs", log_to_stdout: true, progress_name: 'Executing actions')
105
+ # Keep a list of nodes that will need remote access
106
+ nodes_needing_connectors = []
107
+ # Compute the ordered list of actions per selected node
108
+ # Hash< String, Array< [Symbol, Object ]> >
109
+ # Hash< node, Array< [action_type, action_data]> >
110
+ actions_per_node = {}
111
+ actions_per_nodes.each do |nodes_selector, nodes_actions|
112
+ # Resolved actions, as Action objects
113
+ resolved_nodes_actions = []
114
+ need_remote = false
115
+ (nodes_actions.is_a?(Array) ? nodes_actions : [nodes_actions]).each do |nodes_actions_set|
116
+ nodes_actions_set.each do |action_type, action_info|
117
+ raise 'Cannot have concurrent executions for interactive sessions' if concurrent && action_type == :interactive && action_info
118
+ raise "Unknown action type #{action_type}" unless @action_plugins.key?(action_type)
119
+ action = @action_plugins[action_type].new(
120
+ logger: @logger,
121
+ logger_stderr: @logger_stderr,
122
+ config: @config,
123
+ cmd_runner: @cmd_runner,
124
+ actions_executor: self,
125
+ action_info: action_info
126
+ )
127
+ need_remote = true if action.need_connector?
128
+ resolved_nodes_actions << action
129
+ end
130
+ end
131
+ # Resolve nodes
132
+ resolved_nodes = @nodes_handler.select_nodes(nodes_selector)
133
+ nodes_needing_connectors.concat(resolved_nodes) if need_remote
134
+ resolved_nodes.each do |node|
135
+ actions_per_node[node] = [] unless actions_per_node.key?(node)
136
+ actions_per_node[node].concat(resolved_nodes_actions)
137
+ end
138
+ end
139
+ result = Hash[actions_per_node.keys.map { |node| [node, nil] }]
140
+ with_connections_prepared_to(nodes_needing_connectors, no_exception: true) do |connected_nodes|
141
+ missing_nodes = []
142
+ connected_nodes.each do |node, connector|
143
+ if connector.is_a?(Symbol)
144
+ result[node] = [connector, '', "Unable to get a connector to #{node}"]
145
+ missing_nodes << node
146
+ end
147
+ end
148
+ accessible_nodes = actions_per_node.keys - missing_nodes
149
+ log_debug "Running actions on #{accessible_nodes.size} nodes#{log_to_dir.nil? ? '' : " (logs dumped in #{log_to_dir})"}"
150
+ # Prepare the result (stdout or nil per node)
151
+ unless accessible_nodes.empty?
152
+ # If we run in parallel then clone the connectors, so that each node has its own instance for thread-safe code.
153
+ connected_nodes = Hash[connected_nodes.map { |node, connector| [node, connector.clone] }] if concurrent
154
+ @nodes_handler.for_each_node_in(
155
+ accessible_nodes,
156
+ parallel: concurrent,
157
+ nbr_threads_max: @max_threads,
158
+ progress: progress_name
159
+ ) do |node|
160
+ node_actions = actions_per_node[node]
161
+ # If we run in parallel then clone the actions, so that each node has its own instance for thread-safe code.
162
+ node_actions.map!(&:clone) if concurrent
163
+ result[node] = execute_actions_on(
164
+ node,
165
+ node_actions,
166
+ connected_nodes[node],
167
+ timeout: timeout,
168
+ log_to_file: log_to_dir.nil? ? nil : "#{log_to_dir}/#{node}.stdout",
169
+ log_to_stdout: log_to_stdout
170
+ )
171
+ end
172
+ end
173
+ end
174
+ result
175
+ end
176
+
177
+ # Prepare connections to a set of nodes
178
+ #
179
+ # Parameters::
180
+ # * *nodes* (Array<String>): List of nodes to connect to
181
+ # * *no_exception* (Boolean): Should we continue even if some nodes can't be connected to? [default: false]
182
+ # * Proc: Code called with connections prepared
183
+ # * Parameters::
184
+ # * *connected_nodes* (Hash<String, Connector or Symbol>): Prepared connectors (or Symbol in case of failure with no_exception), per node name
185
+ def with_connections_prepared_to(nodes, no_exception: false)
186
+ # Make sure every node needing connectors finds a connector
187
+ nodes_needing_connectors = Hash[nodes.map { |node| [node, nil] }]
188
+ @connector_plugins.each do |connector_name, connector|
189
+ nodes_without_connectors = nodes_needing_connectors.select { |_node, selected_connector| selected_connector.nil? }.keys
190
+ break if nodes_without_connectors.empty?
191
+ (connector.connectable_nodes_from(nodes_without_connectors) & nodes_without_connectors).each do |node|
192
+ nodes_needing_connectors[node] = connector if nodes_needing_connectors[node].nil?
193
+ end
194
+ end
195
+ # If some nodes need connectors but can't find any, then fail
196
+ nodes_without_connectors = nodes_needing_connectors.select { |_node, selected_connector| selected_connector.nil? }.keys
197
+ unless nodes_without_connectors.empty?
198
+ message = "The following nodes have no possible connector to them: #{nodes_without_connectors.sort.join(', ')}"
199
+ log_warn message
200
+ raise message unless no_exception
201
+ end
202
+ # Prepare the connectors to operate on the nodes they have been assigned to
203
+ preparation_code = proc do |remaining_plugins_to_prepare|
204
+ connector_name = remaining_plugins_to_prepare.first
205
+ if connector_name.nil?
206
+ # All plugins have been prepared.
207
+ # Call our client code.
208
+ yield Hash[nodes_needing_connectors.map do |node, selected_connector|
209
+ [
210
+ node,
211
+ selected_connector.nil? ? :no_connector : selected_connector
212
+ ]
213
+ end]
214
+ else
215
+ connector = @connector_plugins[connector_name]
216
+ selected_nodes = nodes_needing_connectors.select { |_node, selected_connector| selected_connector == connector }.keys
217
+ if selected_nodes.empty?
218
+ preparation_code.call(remaining_plugins_to_prepare[1..-1])
219
+ else
220
+ connector.with_connection_to(selected_nodes, no_exception: no_exception) do |connected_nodes|
221
+ (selected_nodes - connected_nodes).each do |node_in_error|
222
+ nodes_needing_connectors[node_in_error] = :connection_error
223
+ end
224
+ preparation_code.call(remaining_plugins_to_prepare[1..-1])
225
+ end
226
+ end
227
+ end
228
+ end
229
+ preparation_code.call(@connector_plugins.select { |_connector_name, connector| connector.respond_to?(:with_connection_to) }.keys)
230
+ end
231
+
232
+ # Get a given connector
233
+ #
234
+ # Parameters::
235
+ # * *connector_name* (Symbol): The connector name
236
+ # Result::
237
+ # * Connector or nil: The connector, or nil if none found
238
+ def connector(connector_name)
239
+ @connector_plugins[connector_name]
240
+ end
241
+
242
+ private
243
+
244
+ # Execute a list of actions for a node, and return exit codes, stdout and stderr of those actions.
245
+ #
246
+ # Parameters::
247
+ # * *node* (String): The node
248
+ # * *actions* (Array<Action>): Ordered list of actions to perform.
249
+ # * *connector* (Connector or nil): Connector to use to connect to this node, or nil if none.
250
+ # * *timeout* (Integer): Timeout in seconds, or nil if none. [default: nil]
251
+ # * *log_to_file* (String or nil): Log file capturing stdout and stderr (or nil for none). [default: nil]
252
+ # * *log_to_stdout* (Boolean): Do we send the output to stdout and stderr? [default: true]
253
+ # Result::
254
+ # * Integer or Symbol: Exit status of the last command, or Symbol in case of error
255
+ # * String: Standard output of the commands
256
+ # * String: Standard error output of the commands
257
+ def execute_actions_on(node, actions, connector, timeout: nil, log_to_file: nil, log_to_stdout: true)
258
+ remaining_timeout = timeout
259
+ exit_status = 0
260
+ file_output =
261
+ if log_to_file
262
+ FileUtils.mkdir_p(File.dirname(log_to_file))
263
+ File.open(log_to_file, 'w')
264
+ else
265
+ nil
266
+ end
267
+ stdout_queue = Queue.new
268
+ stderr_queue = Queue.new
269
+ stdout = ''
270
+ stderr = ''
271
+ IoRouter.with_io_router(
272
+ stdout_queue => [stdout] +
273
+ (log_to_stdout ? [@logger] : []) +
274
+ (file_output.nil? ? [] : [file_output]),
275
+ stderr_queue => [stderr] +
276
+ (log_to_stdout ? [@logger_stderr] : []) +
277
+ (file_output.nil? ? [] : [file_output])
278
+ ) do
279
+ begin
280
+ log_debug "[#{node}] - Execute #{actions.size} actions on #{node}..."
281
+ actions.each do |action|
282
+ action.prepare_for(node, connector, remaining_timeout, stdout_queue, stderr_queue)
283
+ start_time = Time.now
284
+ action.execute
285
+ remaining_timeout -= Time.now - start_time unless remaining_timeout.nil?
286
+ end
287
+ rescue ConnectionError
288
+ exit_status = :connection_error
289
+ stderr_queue << "#{$!}\n"
290
+ rescue CmdRunner::UnexpectedExitCodeError
291
+ exit_status = :failed_command
292
+ stderr_queue << "#{$!}\n"
293
+ rescue CmdRunner::TimeoutError
294
+ # Error has already been logged in stderr
295
+ exit_status = :timeout
296
+ rescue
297
+ log_error "Uncaught exception while executing actions on #{node}: #{$!}\n#{$!.backtrace.join("\n")}"
298
+ stderr_queue << "#{$!}\n"
299
+ exit_status = :failed_action
300
+ end
301
+ end
302
+ [exit_status, stdout, stderr]
303
+ end
304
+
305
+ end
306
+
307
+ end
@@ -0,0 +1,123 @@
1
+ require 'json'
2
+ require 'logger'
3
+ require 'open-uri'
4
+ require 'uri'
5
+ require 'hybrid_platforms_conductor/credentials'
6
+ require 'hybrid_platforms_conductor/logger_helpers'
7
+
8
+ module HybridPlatformsConductor
9
+
10
+ # Object used to access Bitbucket API
11
+ class Bitbucket
12
+
13
+ include LoggerHelpers
14
+
15
+ # Provide a Bitbucket connector, and make sure the password is being cleaned when exiting.
16
+ #
17
+ # Parameters::
18
+ # * *bitbucket_url* (String): The Bitbucket URL
19
+ # * *logger* (Logger): Logger to be used
20
+ # * *logger_stderr* (Logger): Logger to be used for stderr
21
+ # * Proc: Code called with the Bitbucket instance.
22
+ # * *bitbucket* (Bitbucket): The Bitbucket instance to use.
23
+ def self.with_bitbucket(bitbucket_url, logger, logger_stderr)
24
+ Credentials.with_credentials_for(:bitbucket, logger, logger_stderr, url: bitbucket_url) do |bitbucket_user, bitbucket_password|
25
+ yield Bitbucket.new(bitbucket_url, bitbucket_user, bitbucket_password, logger: logger, logger_stderr: logger_stderr)
26
+ end
27
+ end
28
+
29
+ # The Bitbucket URL
30
+ # String
31
+ attr_reader :bitbucket_url
32
+
33
+ # Constructor
34
+ #
35
+ # Parameters::
36
+ # * *bitbucket_url* (String): The Bitbucket URL
37
+ # * *bitbucket_user_name* (String): Bitbucket user name to be used when querying the API
38
+ # * *bitbucket_password* (String): Bitbucket password to be used when querying the API
39
+ # * *logger* (Logger): Logger to be used [default = Logger.new(STDOUT)]
40
+ # * *logger_stderr* (Logger): Logger to be used for stderr [default = Logger.new(STDERR)]
41
+ def initialize(bitbucket_url, bitbucket_user_name, bitbucket_password, logger: Logger.new(STDOUT), logger_stderr: Logger.new(STDERR))
42
+ init_loggers(logger, logger_stderr)
43
+ @bitbucket_url = bitbucket_url
44
+ @bitbucket_user_name = bitbucket_user_name
45
+ @bitbucket_password = bitbucket_password
46
+ end
47
+
48
+ # Get the repositories of a given project.
49
+ # Limit to 1000 results max.
50
+ #
51
+ # Parameters::
52
+ # * *project* (String): Project name
53
+ # Result::
54
+ # * Object: Corresponding JSON
55
+ def repos(project)
56
+ get_api("projects/#{project}/repos?limit=1000")
57
+ end
58
+
59
+ # Get the PR settings of a given repository
60
+ #
61
+ # Parameters::
62
+ # * *project* (String): Project name
63
+ # * *repo* (String): Repository name
64
+ # Result::
65
+ # * Object: Corresponding JSON
66
+ def settings_pr(project, repo)
67
+ get_api("projects/#{project}/repos/#{repo}/settings/pull-requests")
68
+ end
69
+
70
+ # Get the default reviewers of a given repository
71
+ #
72
+ # Parameters::
73
+ # * *project* (String): Project name
74
+ # * *repo* (String): Repository name
75
+ # Result::
76
+ # * Object: Corresponding JSON
77
+ def default_reviewers(project, repo)
78
+ get_api("projects/#{project}/repos/#{repo}/conditions", api_domain: 'default-reviewers')
79
+ end
80
+
81
+ # Get the branch permissions of a given repository
82
+ #
83
+ # Parameters::
84
+ # * *project* (String): Project name
85
+ # * *repo* (String): Repository name
86
+ # Result::
87
+ # * Object: Corresponding JSON
88
+ def branch_permissions(project, repo)
89
+ # Put 3 retries here as the Bitbucket installation has a very unstable API 2.0 and often returns random 401 errors.
90
+ get_api("projects/#{project}/repos/#{repo}/restrictions", api_domain: 'branch-permissions', api_version: '2.0', retries: 3)
91
+ end
92
+
93
+ # Issue an HTTP get on the API.
94
+ # Handle authentication.
95
+ #
96
+ # Parameters::
97
+ # * *path* (String): API path to access
98
+ # * *api_domain* (String): API domain to access [default: 'api']
99
+ # * *api_version* (String): API version to access [default: '1.0']
100
+ # * *retries* (Integer): Number of retries in case of failures [default: 0]
101
+ # Result::
102
+ # * Object: Returned JSON
103
+ def get_api(path, api_domain: 'api', api_version: '1.0', retries: 0)
104
+ api_url = "#{@bitbucket_url}/rest/#{api_domain}/#{api_version}/#{path}"
105
+ log_debug "Call Bitbucket API #{@bitbucket_user_name}@#{api_url}..."
106
+ http_response = nil
107
+ loop do
108
+ begin
109
+ http_response = URI.open(api_url, http_basic_authentication: [@bitbucket_user_name, @bitbucket_password])
110
+ rescue
111
+ raise if retries == 0
112
+ log_warn "Got error #{$!} on #{@bitbucket_user_name}@#{api_url}. Will retry #{retries} times..."
113
+ retries -= 1
114
+ sleep 1
115
+ end
116
+ break unless http_response.nil?
117
+ end
118
+ JSON.parse(http_response.read)
119
+ end
120
+
121
+ end
122
+
123
+ end