openbolt 5.0.0.rc1

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 (258) hide show
  1. checksums.yaml +7 -0
  2. data/Puppetfile +52 -0
  3. data/bolt-modules/boltlib/lib/puppet/datatypes/applyresult.rb +60 -0
  4. data/bolt-modules/boltlib/lib/puppet/datatypes/containerresult.rb +51 -0
  5. data/bolt-modules/boltlib/lib/puppet/datatypes/future.rb +25 -0
  6. data/bolt-modules/boltlib/lib/puppet/datatypes/resourceinstance.rb +71 -0
  7. data/bolt-modules/boltlib/lib/puppet/datatypes/result.rb +55 -0
  8. data/bolt-modules/boltlib/lib/puppet/datatypes/resultset.rb +65 -0
  9. data/bolt-modules/boltlib/lib/puppet/datatypes/target.rb +93 -0
  10. data/bolt-modules/boltlib/lib/puppet/functions/add_facts.rb +33 -0
  11. data/bolt-modules/boltlib/lib/puppet/functions/add_to_group.rb +38 -0
  12. data/bolt-modules/boltlib/lib/puppet/functions/apply_prep.rb +208 -0
  13. data/bolt-modules/boltlib/lib/puppet/functions/background.rb +62 -0
  14. data/bolt-modules/boltlib/lib/puppet/functions/catch_errors.rb +57 -0
  15. data/bolt-modules/boltlib/lib/puppet/functions/download_file.rb +130 -0
  16. data/bolt-modules/boltlib/lib/puppet/functions/facts.rb +31 -0
  17. data/bolt-modules/boltlib/lib/puppet/functions/fail_plan.rb +52 -0
  18. data/bolt-modules/boltlib/lib/puppet/functions/get_resources.rb +87 -0
  19. data/bolt-modules/boltlib/lib/puppet/functions/get_target.rb +34 -0
  20. data/bolt-modules/boltlib/lib/puppet/functions/get_targets.rb +35 -0
  21. data/bolt-modules/boltlib/lib/puppet/functions/parallelize.rb +74 -0
  22. data/bolt-modules/boltlib/lib/puppet/functions/puppetdb_command.rb +97 -0
  23. data/bolt-modules/boltlib/lib/puppet/functions/puppetdb_fact.rb +47 -0
  24. data/bolt-modules/boltlib/lib/puppet/functions/puppetdb_query.rb +52 -0
  25. data/bolt-modules/boltlib/lib/puppet/functions/remove_from_group.rb +40 -0
  26. data/bolt-modules/boltlib/lib/puppet/functions/resolve_references.rb +42 -0
  27. data/bolt-modules/boltlib/lib/puppet/functions/resource.rb +53 -0
  28. data/bolt-modules/boltlib/lib/puppet/functions/run_command.rb +106 -0
  29. data/bolt-modules/boltlib/lib/puppet/functions/run_container.rb +162 -0
  30. data/bolt-modules/boltlib/lib/puppet/functions/run_plan.rb +291 -0
  31. data/bolt-modules/boltlib/lib/puppet/functions/run_script.rb +145 -0
  32. data/bolt-modules/boltlib/lib/puppet/functions/run_task.rb +164 -0
  33. data/bolt-modules/boltlib/lib/puppet/functions/run_task_with.rb +211 -0
  34. data/bolt-modules/boltlib/lib/puppet/functions/set_config.rb +48 -0
  35. data/bolt-modules/boltlib/lib/puppet/functions/set_feature.rb +43 -0
  36. data/bolt-modules/boltlib/lib/puppet/functions/set_resources.rb +145 -0
  37. data/bolt-modules/boltlib/lib/puppet/functions/set_var.rb +38 -0
  38. data/bolt-modules/boltlib/lib/puppet/functions/upload_file.rb +101 -0
  39. data/bolt-modules/boltlib/lib/puppet/functions/vars.rb +29 -0
  40. data/bolt-modules/boltlib/lib/puppet/functions/wait.rb +131 -0
  41. data/bolt-modules/boltlib/lib/puppet/functions/wait_until_available.rb +59 -0
  42. data/bolt-modules/boltlib/lib/puppet/functions/without_default_logging.rb +39 -0
  43. data/bolt-modules/boltlib/lib/puppet/functions/write_file.rb +50 -0
  44. data/bolt-modules/boltlib/types/planresult.pp +18 -0
  45. data/bolt-modules/boltlib/types/targetspec.pp +7 -0
  46. data/bolt-modules/ctrl/lib/puppet/functions/ctrl/do_until.rb +42 -0
  47. data/bolt-modules/ctrl/lib/puppet/functions/ctrl/sleep.rb +20 -0
  48. data/bolt-modules/dir/lib/puppet/functions/dir/children.rb +35 -0
  49. data/bolt-modules/file/lib/puppet/functions/file/delete.rb +21 -0
  50. data/bolt-modules/file/lib/puppet/functions/file/exists.rb +28 -0
  51. data/bolt-modules/file/lib/puppet/functions/file/join.rb +20 -0
  52. data/bolt-modules/file/lib/puppet/functions/file/read.rb +33 -0
  53. data/bolt-modules/file/lib/puppet/functions/file/readable.rb +28 -0
  54. data/bolt-modules/file/lib/puppet/functions/file/write.rb +24 -0
  55. data/bolt-modules/log/lib/puppet/functions/log/debug.rb +39 -0
  56. data/bolt-modules/log/lib/puppet/functions/log/error.rb +40 -0
  57. data/bolt-modules/log/lib/puppet/functions/log/fatal.rb +40 -0
  58. data/bolt-modules/log/lib/puppet/functions/log/info.rb +39 -0
  59. data/bolt-modules/log/lib/puppet/functions/log/trace.rb +39 -0
  60. data/bolt-modules/log/lib/puppet/functions/log/warn.rb +41 -0
  61. data/bolt-modules/out/lib/puppet/functions/out/message.rb +36 -0
  62. data/bolt-modules/out/lib/puppet/functions/out/verbose.rb +35 -0
  63. data/bolt-modules/prompt/lib/puppet/functions/prompt/menu.rb +103 -0
  64. data/bolt-modules/prompt/lib/puppet/functions/prompt.rb +65 -0
  65. data/bolt-modules/system/lib/puppet/functions/system/env.rb +20 -0
  66. data/exe/bolt +17 -0
  67. data/guides/debugging.yaml +27 -0
  68. data/guides/inventory.yaml +23 -0
  69. data/guides/links.yaml +12 -0
  70. data/guides/logging.yaml +17 -0
  71. data/guides/module.yaml +18 -0
  72. data/guides/modulepath.yaml +24 -0
  73. data/guides/project.yaml +21 -0
  74. data/guides/targets.yaml +28 -0
  75. data/guides/transports.yaml +22 -0
  76. data/lib/bolt/analytics.rb +233 -0
  77. data/lib/bolt/application.rb +806 -0
  78. data/lib/bolt/applicator.rb +368 -0
  79. data/lib/bolt/apply_inventory.rb +93 -0
  80. data/lib/bolt/apply_result.rb +154 -0
  81. data/lib/bolt/apply_target.rb +90 -0
  82. data/lib/bolt/bolt_option_parser.rb +1226 -0
  83. data/lib/bolt/catalog/logging.rb +15 -0
  84. data/lib/bolt/catalog.rb +144 -0
  85. data/lib/bolt/cli.rb +949 -0
  86. data/lib/bolt/config/modulepath.rb +30 -0
  87. data/lib/bolt/config/options.rb +673 -0
  88. data/lib/bolt/config/transport/base.rb +133 -0
  89. data/lib/bolt/config/transport/docker.rb +34 -0
  90. data/lib/bolt/config/transport/jail.rb +33 -0
  91. data/lib/bolt/config/transport/local.rb +39 -0
  92. data/lib/bolt/config/transport/lxd.rb +34 -0
  93. data/lib/bolt/config/transport/options.rb +431 -0
  94. data/lib/bolt/config/transport/orch.rb +41 -0
  95. data/lib/bolt/config/transport/podman.rb +33 -0
  96. data/lib/bolt/config/transport/remote.rb +24 -0
  97. data/lib/bolt/config/transport/ssh.rb +138 -0
  98. data/lib/bolt/config/transport/winrm.rb +63 -0
  99. data/lib/bolt/config.rb +515 -0
  100. data/lib/bolt/container_result.rb +105 -0
  101. data/lib/bolt/error.rb +194 -0
  102. data/lib/bolt/executor.rb +539 -0
  103. data/lib/bolt/fiber_executor.rb +190 -0
  104. data/lib/bolt/inventory/group.rb +446 -0
  105. data/lib/bolt/inventory/inventory.rb +391 -0
  106. data/lib/bolt/inventory/options.rb +139 -0
  107. data/lib/bolt/inventory/target.rb +293 -0
  108. data/lib/bolt/inventory.rb +120 -0
  109. data/lib/bolt/logger.rb +252 -0
  110. data/lib/bolt/module.rb +54 -0
  111. data/lib/bolt/module_installer/installer.rb +44 -0
  112. data/lib/bolt/module_installer/puppetfile/forge_module.rb +54 -0
  113. data/lib/bolt/module_installer/puppetfile/git_module.rb +37 -0
  114. data/lib/bolt/module_installer/puppetfile/module.rb +26 -0
  115. data/lib/bolt/module_installer/puppetfile.rb +131 -0
  116. data/lib/bolt/module_installer/resolver.rb +129 -0
  117. data/lib/bolt/module_installer/specs/forge_spec.rb +91 -0
  118. data/lib/bolt/module_installer/specs/git_spec.rb +150 -0
  119. data/lib/bolt/module_installer/specs/id/base.rb +116 -0
  120. data/lib/bolt/module_installer/specs/id/gitclone.rb +120 -0
  121. data/lib/bolt/module_installer/specs/id/github.rb +90 -0
  122. data/lib/bolt/module_installer/specs/id/gitlab.rb +92 -0
  123. data/lib/bolt/module_installer/specs.rb +95 -0
  124. data/lib/bolt/module_installer.rb +208 -0
  125. data/lib/bolt/node/errors.rb +55 -0
  126. data/lib/bolt/node/output.rb +29 -0
  127. data/lib/bolt/outputter/human.rb +958 -0
  128. data/lib/bolt/outputter/json.rb +205 -0
  129. data/lib/bolt/outputter/logger.rb +76 -0
  130. data/lib/bolt/outputter/rainbow.rb +118 -0
  131. data/lib/bolt/outputter.rb +57 -0
  132. data/lib/bolt/pal/issues.rb +19 -0
  133. data/lib/bolt/pal/logging.rb +17 -0
  134. data/lib/bolt/pal/yaml_plan/evaluator.rb +83 -0
  135. data/lib/bolt/pal/yaml_plan/loader.rb +94 -0
  136. data/lib/bolt/pal/yaml_plan/parameter.rb +63 -0
  137. data/lib/bolt/pal/yaml_plan/step/command.rb +45 -0
  138. data/lib/bolt/pal/yaml_plan/step/download.rb +37 -0
  139. data/lib/bolt/pal/yaml_plan/step/eval.rb +42 -0
  140. data/lib/bolt/pal/yaml_plan/step/message.rb +31 -0
  141. data/lib/bolt/pal/yaml_plan/step/plan.rb +42 -0
  142. data/lib/bolt/pal/yaml_plan/step/resources.rb +170 -0
  143. data/lib/bolt/pal/yaml_plan/step/script.rb +62 -0
  144. data/lib/bolt/pal/yaml_plan/step/task.rb +42 -0
  145. data/lib/bolt/pal/yaml_plan/step/upload.rb +37 -0
  146. data/lib/bolt/pal/yaml_plan/step/verbose.rb +31 -0
  147. data/lib/bolt/pal/yaml_plan/step.rb +223 -0
  148. data/lib/bolt/pal/yaml_plan/transpiler.rb +90 -0
  149. data/lib/bolt/pal/yaml_plan.rb +172 -0
  150. data/lib/bolt/pal.rb +847 -0
  151. data/lib/bolt/plan_creator.rb +219 -0
  152. data/lib/bolt/plan_future.rb +86 -0
  153. data/lib/bolt/plan_result.rb +44 -0
  154. data/lib/bolt/plugin/cache.rb +76 -0
  155. data/lib/bolt/plugin/env_var.rb +54 -0
  156. data/lib/bolt/plugin/module.rb +276 -0
  157. data/lib/bolt/plugin/prompt.rb +36 -0
  158. data/lib/bolt/plugin/puppet_connect_data.rb +84 -0
  159. data/lib/bolt/plugin/puppetdb.rb +124 -0
  160. data/lib/bolt/plugin/task.rb +72 -0
  161. data/lib/bolt/plugin.rb +380 -0
  162. data/lib/bolt/project.rb +219 -0
  163. data/lib/bolt/project_manager/config_migrator.rb +113 -0
  164. data/lib/bolt/project_manager/inventory_migrator.rb +67 -0
  165. data/lib/bolt/project_manager/migrator.rb +39 -0
  166. data/lib/bolt/project_manager/module_migrator.rb +203 -0
  167. data/lib/bolt/project_manager.rb +221 -0
  168. data/lib/bolt/puppetdb/client.rb +153 -0
  169. data/lib/bolt/puppetdb/config.rb +176 -0
  170. data/lib/bolt/puppetdb/instance.rb +146 -0
  171. data/lib/bolt/puppetdb.rb +15 -0
  172. data/lib/bolt/r10k_log_proxy.rb +30 -0
  173. data/lib/bolt/rerun.rb +55 -0
  174. data/lib/bolt/resource_instance.rb +133 -0
  175. data/lib/bolt/result.rb +247 -0
  176. data/lib/bolt/result_set.rb +128 -0
  177. data/lib/bolt/shell/bash/tmpdir.rb +62 -0
  178. data/lib/bolt/shell/bash.rb +516 -0
  179. data/lib/bolt/shell/powershell/snippets.rb +181 -0
  180. data/lib/bolt/shell/powershell.rb +365 -0
  181. data/lib/bolt/shell.rb +105 -0
  182. data/lib/bolt/target.rb +174 -0
  183. data/lib/bolt/task/puppet_server.rb +27 -0
  184. data/lib/bolt/task/run.rb +55 -0
  185. data/lib/bolt/task.rb +163 -0
  186. data/lib/bolt/transport/base.rb +252 -0
  187. data/lib/bolt/transport/docker/connection.rb +150 -0
  188. data/lib/bolt/transport/docker.rb +23 -0
  189. data/lib/bolt/transport/jail/connection.rb +81 -0
  190. data/lib/bolt/transport/jail.rb +21 -0
  191. data/lib/bolt/transport/local/connection.rb +106 -0
  192. data/lib/bolt/transport/local.rb +20 -0
  193. data/lib/bolt/transport/lxd/connection.rb +115 -0
  194. data/lib/bolt/transport/lxd.rb +26 -0
  195. data/lib/bolt/transport/orch/connection.rb +111 -0
  196. data/lib/bolt/transport/orch.rb +271 -0
  197. data/lib/bolt/transport/podman/connection.rb +102 -0
  198. data/lib/bolt/transport/podman.rb +19 -0
  199. data/lib/bolt/transport/remote.rb +41 -0
  200. data/lib/bolt/transport/simple.rb +54 -0
  201. data/lib/bolt/transport/ssh/connection.rb +321 -0
  202. data/lib/bolt/transport/ssh/exec_connection.rb +140 -0
  203. data/lib/bolt/transport/ssh.rb +48 -0
  204. data/lib/bolt/transport/winrm/connection.rb +378 -0
  205. data/lib/bolt/transport/winrm.rb +33 -0
  206. data/lib/bolt/util/format.rb +68 -0
  207. data/lib/bolt/util/puppet_log_level.rb +21 -0
  208. data/lib/bolt/util.rb +465 -0
  209. data/lib/bolt/validator.rb +227 -0
  210. data/lib/bolt/version.rb +5 -0
  211. data/lib/bolt.rb +8 -0
  212. data/lib/bolt_server/acl.rb +39 -0
  213. data/lib/bolt_server/base_config.rb +112 -0
  214. data/lib/bolt_server/config.rb +64 -0
  215. data/lib/bolt_server/file_cache.rb +200 -0
  216. data/lib/bolt_server/request_error.rb +11 -0
  217. data/lib/bolt_server/schemas/action-check_node_connections.json +14 -0
  218. data/lib/bolt_server/schemas/action-run_command.json +12 -0
  219. data/lib/bolt_server/schemas/action-run_script.json +47 -0
  220. data/lib/bolt_server/schemas/action-run_task.json +20 -0
  221. data/lib/bolt_server/schemas/action-upload_file.json +47 -0
  222. data/lib/bolt_server/schemas/partials/target-any.json +10 -0
  223. data/lib/bolt_server/schemas/partials/target-ssh.json +88 -0
  224. data/lib/bolt_server/schemas/partials/target-winrm.json +67 -0
  225. data/lib/bolt_server/schemas/partials/task.json +94 -0
  226. data/lib/bolt_server/schemas/transport-ssh.json +25 -0
  227. data/lib/bolt_server/schemas/transport-winrm.json +19 -0
  228. data/lib/bolt_server/transport_app.rb +554 -0
  229. data/lib/bolt_spec/bolt_context.rb +226 -0
  230. data/lib/bolt_spec/plans/action_stubs/command_stub.rb +51 -0
  231. data/lib/bolt_spec/plans/action_stubs/download_stub.rb +66 -0
  232. data/lib/bolt_spec/plans/action_stubs/plan_stub.rb +55 -0
  233. data/lib/bolt_spec/plans/action_stubs/script_stub.rb +59 -0
  234. data/lib/bolt_spec/plans/action_stubs/task_stub.rb +57 -0
  235. data/lib/bolt_spec/plans/action_stubs/upload_stub.rb +65 -0
  236. data/lib/bolt_spec/plans/action_stubs.rb +196 -0
  237. data/lib/bolt_spec/plans/mock_executor.rb +361 -0
  238. data/lib/bolt_spec/plans/publish_stub.rb +49 -0
  239. data/lib/bolt_spec/plans.rb +190 -0
  240. data/lib/bolt_spec/run.rb +246 -0
  241. data/lib/logging_extensions/logging.rb +13 -0
  242. data/libexec/apply_catalog.rb +130 -0
  243. data/libexec/bolt_catalog +68 -0
  244. data/libexec/custom_facts.rb +63 -0
  245. data/libexec/query_resources.rb +75 -0
  246. data/modules/aggregate/lib/puppet/functions/aggregate/count.rb +21 -0
  247. data/modules/aggregate/lib/puppet/functions/aggregate/nodes.rb +22 -0
  248. data/modules/aggregate/lib/puppet/functions/aggregate/targets.rb +21 -0
  249. data/modules/aggregate/plans/count.pp +56 -0
  250. data/modules/aggregate/plans/targets.pp +56 -0
  251. data/modules/canary/lib/puppet/functions/canary/merge.rb +13 -0
  252. data/modules/canary/lib/puppet/functions/canary/random_split.rb +22 -0
  253. data/modules/canary/lib/puppet/functions/canary/skip.rb +25 -0
  254. data/modules/canary/plans/init.pp +100 -0
  255. data/modules/puppet_connect/plans/test_input_data.pp +94 -0
  256. data/modules/puppetdb_fact/plans/init.pp +20 -0
  257. data/resources/bolt_bash_completion.sh +214 -0
  258. metadata +735 -0
@@ -0,0 +1,271 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'base64'
4
+ require 'find'
5
+ require 'json'
6
+ require 'pathname'
7
+ require_relative '../../bolt/transport/base'
8
+ require_relative 'orch/connection'
9
+
10
+ module Bolt
11
+ module Transport
12
+ class Orch < Base
13
+ BOLT_COMMAND_TASK = Struct.new(:name).new('bolt_shim::command').freeze
14
+ BOLT_SCRIPT_TASK = Struct.new(:name).new('bolt_shim::script').freeze
15
+ BOLT_UPLOAD_TASK = Struct.new(:name).new('bolt_shim::upload').freeze
16
+
17
+ attr_writer :plan_context
18
+
19
+ def provided_features
20
+ ['puppet-agent']
21
+ end
22
+
23
+ def initialize(*args)
24
+ # lazy-load expensive gem code
25
+ require 'orchestrator_client'
26
+
27
+ @connections = {}
28
+ super
29
+ end
30
+
31
+ def finish_plan(result)
32
+ if result.is_a? Bolt::PlanResult
33
+ @connections.each_value do |conn|
34
+ conn.finish_plan(result)
35
+ rescue StandardError => e
36
+ @logger.trace("Failed to finish plan on #{conn.key}: #{e.message}")
37
+ end
38
+ end
39
+ end
40
+
41
+ # It's safe to create connections here for now because the
42
+ # batches/threads are per connection.
43
+ def get_connection(conn_opts)
44
+ key = Connection.get_key(conn_opts)
45
+ unless (conn = @connections[key])
46
+ conn = @connections[key] = Connection.new(conn_opts, @plan_context, logger)
47
+ end
48
+ conn
49
+ end
50
+
51
+ def process_run_results(targets, results, task_name, position = [])
52
+ targets_by_name = Hash[targets.map { |t| t.host || t.name }.zip(targets)]
53
+ results.map do |node_result|
54
+ target = targets_by_name[node_result['name']]
55
+ state = node_result['state']
56
+ result = node_result['result']
57
+
58
+ # If it's finished or already has a proper error simply pass it to the
59
+ # the result otherwise make sure an error is generated
60
+ if state == 'finished' || (result && result['_error'])
61
+ if result['_error']
62
+ unless result['_error'].is_a?(Hash)
63
+ result['_error'] = { 'kind' => 'puppetlabs.tasks/task-error',
64
+ 'issue_code' => 'TASK_ERROR',
65
+ 'msg' => result['_error'],
66
+ 'details' => {} }
67
+ end
68
+
69
+ result['_error']['details'] ||= {}
70
+ unless result['_error']['details'].is_a?(Hash)
71
+ deets = result['_error']['details']
72
+ result['_error']['details'] = { 'msg' => deets }
73
+ end
74
+ file_line = %w[file line].zip(position).to_h.compact
75
+ result['_error']['details'].merge!(file_line) unless result['_error']['details']['file']
76
+ end
77
+
78
+ Bolt::Result.new(target, value: result, action: 'task', object: task_name)
79
+ elsif state == 'skipped'
80
+ details = %w[file line].zip(position).to_h.compact
81
+ Bolt::Result.new(
82
+ target,
83
+ value: { '_error' => {
84
+ 'kind' => 'puppetlabs.tasks/skipped-node',
85
+ 'msg' => "Target #{target.safe_name} was skipped",
86
+ 'details' => details
87
+ } },
88
+ action: 'task', object: task_name
89
+ )
90
+ else
91
+ # Make a generic error with a unkown exit_code
92
+ Bolt::Result.for_task(target, result.to_json, '', 'unknown', task_name, position)
93
+ end
94
+ end
95
+ end
96
+
97
+ def batch_command(targets, command, options = {}, position = [], &callback)
98
+ if options[:env_vars] && !options[:env_vars].empty?
99
+ raise NotImplementedError, "pcp transport does not support setting environment variables"
100
+ end
101
+
102
+ params = {
103
+ 'command' => command
104
+ }
105
+ results = run_task_job(targets,
106
+ BOLT_COMMAND_TASK,
107
+ params,
108
+ options,
109
+ position,
110
+ &callback)
111
+ callback ||= proc {}
112
+ results.map! { |result| unwrap_bolt_result(result.target, result, 'command', command) }
113
+ results.each do |result|
114
+ callback.call(type: :node_result, result: result)
115
+ end
116
+ end
117
+
118
+ def batch_script(targets, script, arguments, options = {}, position = [], &callback)
119
+ if options[:env_vars] && !options[:env_vars].empty?
120
+ raise NotImplementedError, "pcp transport does not support setting environment variables"
121
+ end
122
+
123
+ content = File.open(script, &:read)
124
+ content = Base64.encode64(content)
125
+ params = {
126
+ 'content' => content,
127
+ 'arguments' => arguments,
128
+ 'name' => Pathname(script).basename.to_s
129
+ }
130
+ callback ||= proc {}
131
+ results = run_task_job(targets, BOLT_SCRIPT_TASK, params, options, position, &callback)
132
+ results.map! { |result| unwrap_bolt_result(result.target, result, 'script', script) }
133
+ results.each do |result|
134
+ callback.call(type: :node_result, result: result)
135
+ end
136
+ end
137
+
138
+ def pack(directory)
139
+ # lazy-load expensive gem code
140
+ require 'minitar'
141
+ require 'zlib'
142
+
143
+ start_time = Time.now
144
+ io = StringIO.new
145
+ output = Minitar::Output.new(Zlib::GzipWriter.new(io))
146
+ Find.find(directory) do |file|
147
+ next unless File.file?(file)
148
+
149
+ tar_path = Pathname.new(file).relative_path_from(Pathname.new(directory))
150
+ @logger.trace("Packing #{file} to #{tar_path}")
151
+ stat = File.stat(file)
152
+ content = File.binread(file)
153
+ output.tar.add_file_simple(
154
+ tar_path.to_s,
155
+ data: content,
156
+ size: content.size,
157
+ mode: stat.mode & 0o777,
158
+ mtime: stat.mtime
159
+ )
160
+ end
161
+
162
+ duration = Time.now - start_time
163
+ @logger.trace("Packed upload in #{duration * 1000} ms")
164
+
165
+ output.close
166
+ io.string
167
+ ensure
168
+ # Closes both tar and sgz.
169
+ output&.close
170
+ end
171
+
172
+ def batch_upload(targets, source, destination, options = {}, position = [], &callback)
173
+ stat = File.stat(source)
174
+ content = if stat.directory?
175
+ pack(source)
176
+ else
177
+ File.open(source, &:read)
178
+ end
179
+ content = Base64.encode64(content)
180
+ mode = File.stat(source).mode
181
+ params = {
182
+ 'path' => destination,
183
+ 'content' => content,
184
+ 'mode' => mode,
185
+ 'directory' => stat.directory?
186
+ }
187
+ callback ||= proc {}
188
+ results = run_task_job(targets, BOLT_UPLOAD_TASK, params, options, position, &callback)
189
+ results.map! do |result|
190
+ if result.error_hash
191
+ result
192
+ else
193
+ Bolt::Result.for_upload(result.target, source, destination)
194
+ end
195
+ end
196
+ results.each do |result|
197
+ callback&.call(type: :node_result, result: result)
198
+ end
199
+ end
200
+
201
+ def batch_download(targets, *_args)
202
+ error = {
203
+ 'kind' => 'bolt/not-supported-error',
204
+ 'msg' => 'pcp transport does not support downloading files',
205
+ 'details' => {}
206
+ }
207
+
208
+ targets.map do |target|
209
+ Bolt::Result.new(target, error: error, action: 'download')
210
+ end
211
+ end
212
+
213
+ def batches(targets)
214
+ targets.group_by { |target| Connection.get_key(target.options) }.values
215
+ end
216
+
217
+ def run_task_job(targets, task, arguments, options, position)
218
+ targets.each do |target|
219
+ yield(type: :node_start, target: target) if block_given?
220
+ end
221
+
222
+ begin
223
+ # unpack any Sensitive data
224
+ arguments = unwrap_sensitive_args(arguments)
225
+ results = get_connection(targets.first.options).run_task(targets, task, arguments, options)
226
+
227
+ process_run_results(targets, results, task.name, position)
228
+ rescue OrchestratorClient::ApiError => e
229
+ targets.map do |target|
230
+ Bolt::Result.new(target, error: e.data)
231
+ end
232
+ rescue StandardError => e
233
+ targets.map do |target|
234
+ Bolt::Result.from_exception(target, e, action: 'task')
235
+ end
236
+ end
237
+ end
238
+
239
+ def batch_task(targets, task, arguments, options = {}, position = [], &callback)
240
+ callback ||= proc {}
241
+ results = run_task_job(targets, task, arguments, options, position, &callback)
242
+ results.each do |result|
243
+ callback.call(type: :node_result, result: result)
244
+ end
245
+ end
246
+
247
+ def batch_task_with(_targets, _task, _target_mapping, _options = {}, _position = [])
248
+ raise NotImplementedError, "pcp transport does not support run_task_with()"
249
+ end
250
+
251
+ def batch_connected?(targets)
252
+ resp = get_connection(targets.first.options).query_inventory(targets)
253
+ resp['items'].all? { |node| node['connected'] }
254
+ end
255
+
256
+ # run_task generates a result that makes sense for a generic task which
257
+ # needs to be unwrapped to extract stdout/stderr/exitcode.
258
+ #
259
+ def unwrap_bolt_result(target, result, action, obj)
260
+ if result.error_hash
261
+ # something went wrong return the failure
262
+ return result
263
+ end
264
+
265
+ # If we get here, there's no error so we don't need the file or line
266
+ # number
267
+ Bolt::Result.for_command(target, result.value, action, obj, [])
268
+ end
269
+ end
270
+ end
271
+ end
@@ -0,0 +1,102 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'logging'
4
+ require_relative '../../../bolt/node/errors'
5
+
6
+ module Bolt
7
+ module Transport
8
+ class Podman < Docker
9
+ class Connection < Connection
10
+ attr_reader :user, :target
11
+
12
+ def initialize(target)
13
+ raise Bolt::ValidationError, "Target #{target.safe_name} does not have a host" unless target.host
14
+ @target = target
15
+ @user = ENV['USER'] || Etc.getlogin
16
+ @logger = Bolt::Logger.logger(target.safe_name)
17
+ @container_info = {}
18
+ @logger.trace("Initializing podman connection to #{target.safe_name}")
19
+ end
20
+
21
+ def run_cmd(cmd, env_vars)
22
+ Bolt::Util.exec_podman(cmd, env_vars)
23
+ end
24
+
25
+ def shell
26
+ @shell ||= if Bolt::Util.windows?
27
+ Bolt::Shell::Powershell.new(target, self)
28
+ else
29
+ Bolt::Shell::Bash.new(target, self)
30
+ end
31
+ end
32
+
33
+ def reset_cwd?
34
+ true
35
+ end
36
+
37
+ def connect
38
+ # We don't actually have a connection, but we do need to
39
+ # check that the container exists and is running.
40
+ ps = execute_local_json_command('ps')
41
+ container = Array(ps).find { |item|
42
+ item["ID"].to_s.eql?(@target.host) ||
43
+ item["Id"].to_s.start_with?(@target.host) ||
44
+ Array(item["Names"]).include?(@target.host)
45
+ }
46
+ raise "Could not find a container with name or ID matching '#{@target.host}'" if container.nil?
47
+ # Now find the indepth container information
48
+ id = container["ID"] || container["Id"]
49
+ output = execute_local_json_command('inspect', [id])
50
+ # Store the container information for later
51
+ @container_info = output.first
52
+ @logger.trace { "Opened session" }
53
+ true
54
+ rescue StandardError => e
55
+ raise Bolt::Node::ConnectError.new(
56
+ "Failed to connect to #{target.safe_name}: #{e.message}",
57
+ 'CONNECT_ERROR'
58
+ )
59
+ end
60
+
61
+ # Executes a command inside the target container. This is called from the shell class.
62
+ #
63
+ # @param command [string] The command to run
64
+ def execute(command)
65
+ args = []
66
+ args += %w[--interactive]
67
+ args += %w[--tty] if target.options['tty']
68
+ args += @env_vars if @env_vars
69
+
70
+ if target.options['shell-command'] && !target.options['shell-command'].empty?
71
+ # escape any double quotes in command
72
+ command = command.gsub('"', '\"')
73
+ command = "#{target.options['shell-command']} \"#{command}\""
74
+ end
75
+
76
+ podman_command = %w[podman exec] + args + [container_id] + Shellwords.split(command)
77
+ @logger.trace { "Executing: #{podman_command.join(' ')}" }
78
+
79
+ Open3.popen3(*podman_command)
80
+ rescue StandardError
81
+ @logger.trace { "Command aborted" }
82
+ raise
83
+ end
84
+
85
+ # Converts the JSON encoded STDOUT string from the podman cli into ruby objects
86
+ #
87
+ # @param stdout [String] The string to convert
88
+ # @return [Object] Ruby object representation of the JSON string
89
+ private def extract_json(stdout)
90
+ # Podman renders the output in pretty JSON, which results in a newline
91
+ # appearing in the output before the closing bracket.
92
+ # should we only get a single line with no newline at all, we also
93
+ # assume it is a single minified JSON object
94
+ stdout.strip!
95
+ newline = stdout.index("\n") || -1
96
+ bracket = stdout.index('}') || -1
97
+ JSON.parse(stdout) if bracket > newline
98
+ end
99
+ end
100
+ end
101
+ end
102
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'json'
4
+ require 'shellwords'
5
+ require_relative '../../bolt/transport/base'
6
+
7
+ module Bolt
8
+ module Transport
9
+ class Podman < Docker
10
+ def with_connection(target)
11
+ conn = Connection.new(target)
12
+ conn.connect
13
+ yield conn
14
+ end
15
+ end
16
+ end
17
+ end
18
+
19
+ require_relative 'podman/connection'
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../../bolt/task'
4
+ require_relative '../../bolt/transport/base'
5
+
6
+ module Bolt
7
+ module Transport
8
+ class Remote < Base
9
+ # TODO: this should have access to inventory so target doesn't have to
10
+ def initialize(executor)
11
+ super()
12
+
13
+ @executor = executor
14
+ end
15
+
16
+ def get_proxy(target)
17
+ inventory = target.inventory
18
+ raise "Target was created without inventory? Not get_targets?" unless inventory
19
+ proxy = inventory.get_targets(target.options['run-on'] || 'localhost').first
20
+
21
+ if proxy.transport == 'remote'
22
+ msg = "#{proxy.name} is not a valid run-on target for #{target.name} since is also remote."
23
+ raise Bolt::Error.new(msg, 'bolt/invalid-remote-target')
24
+ end
25
+ proxy
26
+ end
27
+
28
+ # Cannot batch because arugments differ
29
+ def run_task(target, task, arguments, options = {}, position = [])
30
+ proxy_target = get_proxy(target)
31
+ transport = @executor.transport(proxy_target.transport)
32
+ arguments = arguments.merge('_target' => target.to_h.compact)
33
+
34
+ remote_task = task.remote_instance
35
+
36
+ result = transport.run_task(proxy_target, remote_task, arguments, options, position)
37
+ Bolt::Result.new(target, value: result.value, action: 'task', object: task.name)
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'logging'
4
+ require_relative '../../bolt/result'
5
+ require_relative '../../bolt/shell'
6
+ require_relative '../../bolt/transport/base'
7
+
8
+ module Bolt
9
+ module Transport
10
+ # A simple transport has a single connection per target and delegates its
11
+ # operation to a target-specific shell.
12
+ class Simple < Base
13
+ def with_connection(_target)
14
+ raise NotImplementedError, "with_connection() must be implemented by the transport class"
15
+ end
16
+
17
+ def connected?(target)
18
+ with_connection(target) { true }
19
+ rescue Bolt::Node::ConnectError
20
+ false
21
+ end
22
+
23
+ def run_command(target, command, options = {}, position = [])
24
+ with_connection(target) do |conn|
25
+ conn.shell.run_command(command, options, position)
26
+ end
27
+ end
28
+
29
+ def upload(target, source, destination, options = {})
30
+ with_connection(target) do |conn|
31
+ conn.shell.upload(source, destination, options)
32
+ end
33
+ end
34
+
35
+ def download(target, source, destination, options = {})
36
+ with_connection(target) do |conn|
37
+ conn.shell.download(source, destination, options)
38
+ end
39
+ end
40
+
41
+ def run_script(target, script, arguments, options = {}, position = [])
42
+ with_connection(target) do |conn|
43
+ conn.shell.run_script(script, arguments, options, position)
44
+ end
45
+ end
46
+
47
+ def run_task(target, task, arguments, options = {}, position = [])
48
+ with_connection(target) do |conn|
49
+ conn.shell.run_task(task, arguments, options, position)
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end