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,81 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'logging'
4
+ require_relative '../../../bolt/node/errors'
5
+
6
+ module Bolt
7
+ module Transport
8
+ class Jail < Simple
9
+ class 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 = @target.user || ENV['USER'] || Etc.getlogin
16
+ @logger = Bolt::Logger.logger(target.safe_name)
17
+ @jail_info = {}
18
+ @logger.trace("Initializing jail connection to #{target.safe_name}")
19
+ end
20
+
21
+ def shell
22
+ @shell ||= Bolt::Shell::Bash.new(target, self)
23
+ end
24
+
25
+ def reset_cwd?
26
+ true
27
+ end
28
+
29
+ def jail_id
30
+ @jail_info['jid'].to_s
31
+ end
32
+
33
+ def jail_path
34
+ @jail_info['path']
35
+ end
36
+
37
+ def connect
38
+ output = JSON.parse(`jls --libxo=json`)
39
+ @jail_info = output['jail-information']['jail'].select { |jail| jail['hostname'] == target.host }.first
40
+ raise "Could not find a jail with name matching #{target.host}" if @jail_info.nil?
41
+ @logger.trace { "Opened session" }
42
+ true
43
+ rescue StandardError => e
44
+ raise Bolt::Node::ConnectError.new(
45
+ "Failed to connect to #{target.safe_name}: #{e.message}",
46
+ 'CONNECT_ERROR'
47
+ )
48
+ end
49
+
50
+ def execute(command)
51
+ args = ['-lU', @user]
52
+
53
+ jail_command = %w[jexec] + args + [jail_id] + Shellwords.split(command)
54
+ @logger.trace { "Executing #{jail_command.join(' ')}" }
55
+
56
+ Open3.popen3({}, *jail_command)
57
+ rescue StandardError
58
+ @logger.trace { "Command aborted" }
59
+ raise
60
+ end
61
+
62
+ def upload_file(source, destination)
63
+ @logger.trace { "Uploading #{source} to #{destination}" }
64
+ jail_destination = File.join(jail_path, destination)
65
+ FileUtils.cp(source, jail_destination)
66
+ rescue StandardError => e
67
+ raise Bolt::Node::FileError.new(e.message, 'WRITE_ERROR')
68
+ end
69
+
70
+ def download_file(source, destination, _download)
71
+ @logger.trace { "Downloading #{source} to #{destination}" }
72
+ jail_source = File.join(jail_path, source)
73
+ FileUtils.mkdir_p(destination)
74
+ FileUtils.cp(jail_source, destination)
75
+ rescue StandardError => e
76
+ raise Bolt::Node::FileError.new(e.message, 'WRITE_ERROR')
77
+ end
78
+ end
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../../bolt/transport/simple'
4
+
5
+ module Bolt
6
+ module Transport
7
+ class Jail < Simple
8
+ def provided_features
9
+ ['shell']
10
+ end
11
+
12
+ def with_connection(target)
13
+ conn = Connection.new(target)
14
+ conn.connect
15
+ yield conn
16
+ end
17
+ end
18
+ end
19
+ end
20
+
21
+ require_relative 'jail/connection'
@@ -0,0 +1,106 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'open3'
4
+ require 'fileutils'
5
+ require 'tempfile'
6
+ require_relative '../../../bolt/node/output'
7
+ require_relative '../../../bolt/util'
8
+
9
+ module Bolt
10
+ module Transport
11
+ class Local < Simple
12
+ class Connection
13
+ RUBY_ENV_VARS = %w[GEM_PATH GEM_HOME RUBYLIB RUBYLIB_PREFIX RUBYOPT RUBYPATH RUBYSHELL].freeze
14
+
15
+ attr_accessor :user, :logger, :target
16
+
17
+ def initialize(target)
18
+ @target = target
19
+ # The familiar problem: Etc.getlogin is broken on osx
20
+ @user = ENV['USER'] || Etc.getlogin
21
+ @logger = Bolt::Logger.logger(self)
22
+ end
23
+
24
+ def shell
25
+ @shell ||= if Bolt::Util.windows?
26
+ Bolt::Shell::Powershell.new(target, self)
27
+ else
28
+ Bolt::Shell::Bash.new(target, self)
29
+ end
30
+ end
31
+
32
+ def upload_file(source, dest)
33
+ @logger.trace { "Uploading #{source} to #{dest}" }
34
+ if source.is_a?(StringIO)
35
+ Tempfile.create(File.basename(dest)) do |f|
36
+ f.write(source.read)
37
+ f.close
38
+ FileUtils.mv(f, dest)
39
+ end
40
+ else
41
+ # Mimic the behavior of `cp --remove-destination`
42
+ # since the flag isn't supported on MacOS
43
+ FileUtils.cp_r(source, dest, remove_destination: true)
44
+ end
45
+ rescue StandardError => e
46
+ message = "Could not copy file to #{dest}: #{e}"
47
+ raise Bolt::Node::FileError.new(message, 'COPY_ERROR')
48
+ end
49
+
50
+ def download_file(source, dest, _download)
51
+ @logger.trace { "Downloading #{source} to #{dest}" }
52
+ # Create the destination directory for the target, or the
53
+ # copied file will have the target's name
54
+ FileUtils.mkdir_p(dest)
55
+ # Mimic the behavior of `cp --remove-destination`
56
+ # since the flag isn't supported on MacOS
57
+ FileUtils.cp_r(source, dest, remove_destination: true)
58
+ rescue StandardError => e
59
+ message = "Could not download file to #{dest}: #{e}"
60
+ raise Bolt::Node::FileError.new(message, 'DOWNLOAD_ERROR')
61
+ end
62
+
63
+ def execute(command)
64
+ if Bolt::Util.windows?
65
+ # If it's already a powershell command then invoke it normally.
66
+ # Otherwise, wrap it in powershell.exe.
67
+ unless command.start_with?('powershell.exe')
68
+ cmd = Bolt::Shell::Powershell::Snippets.exit_with_code(command)
69
+ command = ['powershell.exe', *Bolt::Shell::Powershell::PS_ARGS, '-Command', cmd]
70
+ end
71
+ end
72
+
73
+ # Only do this if bundled-ruby is set to false, not nil
74
+ ruby_env_vars = if target.transport_config['bundled-ruby'] == false
75
+ RUBY_ENV_VARS.each_with_object({}) do |e, acc|
76
+ if ENV["BOLT_ORIG_#{e}"] && !ENV["BOLT_ORIG_#{e}"].empty?
77
+ acc[e] = ENV["BOLT_ORIG_#{e}"]
78
+ end
79
+ end
80
+ end
81
+
82
+ if target.transport_config['bundled-ruby'] == false &&
83
+ Gem.loaded_specs.keys.include?('bundler')
84
+ Bundler.with_unbundled_env do
85
+ Open3.popen3(ruby_env_vars || {}, *command)
86
+ end
87
+ else
88
+ Open3.popen3(ruby_env_vars || {}, *command)
89
+ end
90
+ end
91
+
92
+ # This is used by the Bash shell to decide whether to `cd` before
93
+ # executing commands as a run-as user
94
+ def reset_cwd?
95
+ false
96
+ end
97
+
98
+ def max_command_length
99
+ if Bolt::Util.windows?
100
+ 32000
101
+ end
102
+ end
103
+ end
104
+ end
105
+ end
106
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../../bolt/logger'
4
+ require_relative '../../bolt/transport/simple'
5
+
6
+ module Bolt
7
+ module Transport
8
+ class Local < Simple
9
+ def connected?(_target)
10
+ true
11
+ end
12
+
13
+ def with_connection(target)
14
+ yield Connection.new(target)
15
+ end
16
+ end
17
+ end
18
+ end
19
+
20
+ require_relative 'local/connection'
@@ -0,0 +1,115 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'logging'
4
+ require_relative '../../../bolt/node/errors'
5
+
6
+ module Bolt
7
+ module Transport
8
+ class LXD < Simple
9
+ class Connection
10
+ attr_reader :user, :target
11
+
12
+ def initialize(target, options)
13
+ raise Bolt::ValidationError, "Target #{target.safe_name} does not have a host" unless target.host
14
+
15
+ @target = target
16
+ @user = ENV['USER'] || Etc.getlogin
17
+ @options = options
18
+ @logger = Bolt::Logger.logger(target.safe_name)
19
+ @logger.trace("Initializing LXD connection to #{target.safe_name}")
20
+ end
21
+
22
+ def shell
23
+ Bolt::Shell::Bash.new(target, self)
24
+ end
25
+
26
+ def reset_cwd?
27
+ true
28
+ end
29
+
30
+ def container_id
31
+ "#{@target.transport_config['remote']}:#{@target.host}"
32
+ end
33
+
34
+ def connect
35
+ out, err, status = execute_local_command(%W[list #{container_id} --format json])
36
+ unless status.exitstatus.zero?
37
+ raise "Error listing available containers: #{err}"
38
+ end
39
+ containers = JSON.parse(out)
40
+ if containers.empty?
41
+ raise "Could not find a container with name or ID matching '#{container_id}'"
42
+ end
43
+ @logger.trace("Opened session")
44
+ true
45
+ rescue StandardError => e
46
+ raise Bolt::Node::ConnectError.new(
47
+ "Failed to connect to #{container_id}: #{e.message}",
48
+ 'CONNECT_ERROR'
49
+ )
50
+ end
51
+
52
+ def add_env_vars(env_vars)
53
+ @env_vars = env_vars.each_with_object([]) do |env_var, acc|
54
+ acc << "--env"
55
+ acc << "#{env_var[0]}=#{Shellwords.shellescape(env_var[1])}"
56
+ end
57
+ end
58
+
59
+ def execute(command)
60
+ lxc_command = %W[lxc exec #{container_id}]
61
+ lxc_command += ['--mode', target.options['tty'].to_s.empty? ? 'non-interactive' : 'interactive']
62
+ lxc_command += @env_vars if @env_vars
63
+ lxc_command << '--'
64
+
65
+ if target.options['shell-command'].to_s.empty?
66
+ lxc_command += Shellwords.split(command)
67
+ else
68
+ lxc_command += Shellwords.split(target.options['shell-command'])
69
+ lxc_command << command
70
+ end
71
+
72
+ @logger.trace { "Executing: #{lxc_command.join(' ')}" }
73
+
74
+ Open3.popen3(*lxc_command)
75
+ rescue StandardError
76
+ @logger.trace { "Command aborted" }
77
+ raise
78
+ end
79
+
80
+ private def execute_local_command(command)
81
+ Open3.capture3('lxc', *command, { binmode: true })
82
+ end
83
+
84
+ def upload_file(source, destination)
85
+ @logger.trace { "Uploading #{source} to #{destination}" }
86
+ args = %w[--create-dirs]
87
+ if File.directory?(source)
88
+ args << '--recursive'
89
+ # If we don't do this, LXD will upload to
90
+ # /tmp/d2020-11/d2020-11/dir instead of /tmp/d2020-11/dir
91
+ destination = Pathname.new(destination).dirname.to_s
92
+ end
93
+ cmd = %w[file push] + args + %W[#{source} #{container_id}#{destination}]
94
+ _out, err, stat = execute_local_command(cmd)
95
+ unless stat.exitstatus.zero?
96
+ raise "Error writing to #{container_id}: #{err}"
97
+ end
98
+ rescue StandardError => e
99
+ raise Bolt::Node::FileError.new(e.message, 'WRITE_ERROR')
100
+ end
101
+
102
+ def download_file(source, destination, _download)
103
+ @logger.trace { "Downloading #{source} to #{destination}" }
104
+ FileUtils.mkdir_p(destination)
105
+ _out, err, stat = execute_local_command(%W[file pull --recursive #{container_id}#{source} #{destination}])
106
+ unless stat.exitstatus.zero?
107
+ raise "Error downloading content from container #{container_id}: #{err}"
108
+ end
109
+ rescue StandardError => e
110
+ raise Bolt::Node::FileError.new(e.message, 'WRITE_ERROR')
111
+ end
112
+ end
113
+ end
114
+ end
115
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../../bolt/logger'
4
+ require_relative '../../bolt/node/errors'
5
+ require_relative '../../bolt/transport/simple'
6
+
7
+ module Bolt
8
+ module Transport
9
+ class LXD < Simple
10
+ def provided_features
11
+ ['shell']
12
+ end
13
+
14
+ def with_connection(target, options = {})
15
+ Bolt::Logger.warn_once("lxd_experimental",
16
+ "The LXD transport is experimental, and might "\
17
+ "include breaking changes between minor versions.")
18
+ conn = Connection.new(target, options)
19
+ conn.connect
20
+ yield conn
21
+ end
22
+ end
23
+ end
24
+ end
25
+
26
+ require_relative 'lxd/connection'
@@ -0,0 +1,111 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bolt
4
+ module Transport
5
+ class Orch < Base
6
+ class Connection
7
+ attr_reader :logger, :key
8
+
9
+ CONTEXT_KEYS = Set.new(%i[plan_name description params sensitive]).freeze
10
+
11
+ def self.get_key(opts)
12
+ [
13
+ opts['service-url'],
14
+ opts['task-environment'],
15
+ opts['token-file']
16
+ ].join('-')
17
+ end
18
+
19
+ def initialize(opts, plan_context, logger)
20
+ require 'addressable/uri'
21
+
22
+ @logger = logger
23
+ @key = self.class.get_key(opts)
24
+ client_opts = opts.slice('token-file', 'cacert', 'job-poll-interval', 'job-poll-timeout', 'read-timeout')
25
+
26
+ if opts['service-url']
27
+ uri = Addressable::URI.parse(opts['service-url'])
28
+ uri&.port ||= 8143
29
+ client_opts['service-url'] = uri.to_s
30
+ end
31
+
32
+ client_opts['User-Agent'] = "Bolt/#{VERSION}"
33
+
34
+ %w[token-file cacert].each do |f|
35
+ client_opts[f] = File.expand_path(client_opts[f]) if client_opts[f]
36
+ end
37
+ logger.debug("Creating orchestrator client for #{client_opts}")
38
+ @client = OrchestratorClient.new(client_opts, true)
39
+ @plan_context = plan_context
40
+ @plan_job = start_plan(@plan_context)
41
+ logger.debug("Started plan #{@plan_job}")
42
+ @environment = opts["task-environment"]
43
+ end
44
+
45
+ def start_plan(plan_context)
46
+ if plan_context
47
+ begin
48
+ opts = plan_context.select { |k, _| CONTEXT_KEYS.include? k }
49
+ opts[:params] = opts[:params].reject { |k, _| plan_context[:sensitive].include?(k) }
50
+ @client.command.plan_start(opts)['name']
51
+ rescue OrchestratorClient::ApiError => e
52
+ if e.code == '404'
53
+ @logger.debug("Orchestrator #{key} does not support plans")
54
+ else
55
+ @logger.error("Failed to start a plan with orchestrator #{key}: #{e.message}")
56
+ end
57
+ nil
58
+ end
59
+ end
60
+ end
61
+
62
+ def finish_plan(plan_result)
63
+ if @plan_job
64
+ @client.command.plan_finish(
65
+ plan_job: @plan_job,
66
+ result: plan_result.value || '',
67
+ status: plan_result.status
68
+ )
69
+ end
70
+ end
71
+
72
+ def get_certnames(targets)
73
+ targets.map { |t| t.host || t.name }
74
+ end
75
+
76
+ def build_request(targets, task, arguments, description = nil)
77
+ body = { task: task.name,
78
+ environment: @environment,
79
+ noop: arguments['_noop'],
80
+ params: arguments.reject { |k, _| k.start_with?('_') },
81
+ scope: {
82
+ nodes: get_certnames(targets)
83
+ } }
84
+ body[:description] = description if description
85
+ body[:plan_job] = @plan_job if @plan_job
86
+ body
87
+ end
88
+
89
+ def run_task(targets, task, arguments, options)
90
+ body = build_request(targets, task, arguments, options[:description])
91
+ @client.run_task(body)
92
+ rescue OrchestratorClient::ApiError => e
93
+ if e.data['kind'] == 'puppetlabs.orchestrator/plan-already-finished'
94
+ @logger.debug("Retrying the task")
95
+ # Instead of recursing, just retry once
96
+ @plan_job = start_plan(@plan_context)
97
+ # Rebuild the request with the new plan job ID
98
+ body = build_request(targets, task, arguments, options[:description])
99
+ @client.run_task(body)
100
+ else
101
+ raise e
102
+ end
103
+ end
104
+
105
+ def query_inventory(targets)
106
+ @client.post('inventory', nodes: get_certnames(targets))
107
+ end
108
+ end
109
+ end
110
+ end
111
+ end