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,208 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bolt/logger'
4
+ require 'bolt/task'
5
+
6
+ # Installs the `puppet-agent` package on targets if needed, then collects facts,
7
+ # including any custom facts found in Bolt's module path. The package is
8
+ # installed using either the configured plugin or the `task` plugin with the
9
+ # `puppet_agent::install` task.
10
+ #
11
+ # Agent installation will be skipped if the target includes the `puppet-agent`
12
+ # feature, either as a property of its transport (PCP) or by explicitly setting
13
+ # it as a feature in Bolt's inventory.
14
+ #
15
+ # > **Note:** Not available in apply block
16
+ Puppet::Functions.create_function(:apply_prep) do
17
+ # @param targets A pattern or array of patterns identifying a set of targets.
18
+ # @param options Options hash.
19
+ # @option options [Boolean] _catch_errors Whether to catch raised errors.
20
+ # @option options [Array] _required_modules An array of modules to sync to the target.
21
+ # @option options [String] _run_as User to run as using privilege escalation.
22
+ # @return [Bolt::ResultSet]
23
+ # @example Prepare targets by name.
24
+ # apply_prep('target1,target2')
25
+ dispatch :apply_prep do
26
+ param 'Boltlib::TargetSpec', :targets
27
+ optional_param 'Hash[String, Data]', :options
28
+ end
29
+
30
+ def apply_prep(target_spec, options = {})
31
+ unless Puppet[:tasks]
32
+ raise Puppet::ParseErrorWithIssue
33
+ .from_issue_and_stack(Bolt::PAL::Issues::PLAN_OPERATION_NOT_SUPPORTED_WHEN_COMPILING, action: 'apply_prep')
34
+ end
35
+
36
+ options = options.slice(*%w[_catch_errors _required_modules _run_as])
37
+ targets = inventory.get_targets(target_spec)
38
+
39
+ executor.report_function_call(self.class.name)
40
+
41
+ executor.log_action('install puppet and gather facts', targets) do
42
+ executor.without_default_logging do
43
+ install_results = install_agents(targets, options)
44
+ facts_results = get_facts(install_results.ok_set.targets, options)
45
+
46
+ Bolt::ResultSet.new(install_results.error_set.results + facts_results.results)
47
+ end
48
+ end
49
+ end
50
+
51
+ def applicator
52
+ @applicator ||= Puppet.lookup(:apply_executor)
53
+ end
54
+
55
+ def executor
56
+ @executor ||= Puppet.lookup(:bolt_executor)
57
+ end
58
+
59
+ def inventory
60
+ @inventory ||= Puppet.lookup(:bolt_inventory)
61
+ end
62
+
63
+ # Runs a task. This method is called by the puppet_library hook.
64
+ #
65
+ def run_task(targets, task, args = {}, options = {})
66
+ executor.run_task(targets, task, args, options)
67
+ end
68
+
69
+ # Returns true if the target has the puppet-agent feature defined, either from
70
+ # inventory or transport.
71
+ #
72
+ private def agent?(target)
73
+ inventory.features(target).include?('puppet-agent') ||
74
+ executor.transport(target.transport).provided_features.include?('puppet-agent') ||
75
+ target.remote?
76
+ end
77
+
78
+ # Generate the plugin tarball.
79
+ #
80
+ private def build_plugin_tarball(required_modules)
81
+ if required_modules.any?
82
+ Puppet.debug("Syncing only required modules: #{required_modules.join(',')}.")
83
+ end
84
+
85
+ tarball = applicator.build_plugin_tarball do |mod|
86
+ next unless required_modules.empty? || required_modules.include?(mod.name)
87
+ search_dirs = []
88
+ search_dirs << mod.plugins if mod.plugins?
89
+ search_dirs << mod.pluginfacts if mod.pluginfacts?
90
+ search_dirs
91
+ end
92
+
93
+ Puppet::Pops::Types::PSensitiveType::Sensitive.new(tarball)
94
+ end
95
+
96
+ # Install the puppet-agent package on targets that need it.
97
+ #
98
+ private def install_agents(targets, options)
99
+ results = []
100
+
101
+ agent_targets, agentless_targets = targets.partition { |target| agent?(target) }
102
+
103
+ agent_targets.each do |target|
104
+ Puppet.debug("Puppet Agent feature declared for #{target}")
105
+ results << Bolt::Result.new(target)
106
+ end
107
+
108
+ unless agentless_targets.empty?
109
+ hooks, errors = get_hooks(agentless_targets, options)
110
+ hook_results = run_hooks(hooks)
111
+
112
+ hook_results.each do |result|
113
+ next unless result.ok?
114
+ inventory.set_feature(result.target, 'puppet-agent')
115
+ end
116
+
117
+ results.concat(hook_results).concat(errors)
118
+ end
119
+
120
+ Bolt::ResultSet.new(results).tap do |resultset|
121
+ unless resultset.ok? || options['_catch_errors']
122
+ raise Bolt::RunFailure.new(resultset.error_set, 'apply_prep')
123
+ end
124
+ end
125
+ end
126
+
127
+ # Retrieve facts from each target and add them to inventory.
128
+ #
129
+ private def get_facts(targets, options)
130
+ return Bolt::ResultSet.new([]) unless targets.any?
131
+
132
+ task = applicator.custom_facts_task
133
+ args = { 'plugins' => build_plugin_tarball(options.delete('_required_modules').to_a) }
134
+ results = run_task(targets, task, args, options)
135
+
136
+ unless results.ok? || options['_catch_errors']
137
+ raise Bolt::RunFailure.new(results, 'run_task', task.name)
138
+ end
139
+
140
+ results.each do |result|
141
+ next unless result.ok?
142
+
143
+ if unsupported_puppet?(result['clientversion'])
144
+ Bolt::Logger.deprecate(
145
+ "unsupported_puppet",
146
+ "Detected unsupported Puppet agent version #{result['clientversion']} on target "\
147
+ "#{result.target}. Bolt supports Puppet agent 6.0.0 and higher."
148
+ )
149
+ end
150
+
151
+ inventory.add_facts(result.target, result.value)
152
+ end
153
+
154
+ results
155
+ end
156
+
157
+ # Return a list of targets and their puppet_library hooks.
158
+ #
159
+ private def get_hooks(targets, options)
160
+ hooks = []
161
+ errors = []
162
+
163
+ targets.each do |target|
164
+ plugin_opts = target.plugin_hooks.fetch('puppet_library').dup
165
+ plugin_name = plugin_opts.delete('plugin')
166
+ hook = inventory.plugins.get_hook(plugin_name, :puppet_library)
167
+
168
+ hooks << { 'target' => target,
169
+ 'proc' => hook.call(plugin_opts.merge(options), target, self) }
170
+ rescue StandardError => e
171
+ errors << Bolt::Result.from_exception(target, e)
172
+ end
173
+
174
+ [hooks, errors]
175
+ end
176
+
177
+ # Runs the puppet_library hook for each target, returning the result
178
+ # of each.
179
+ #
180
+ private def run_hooks(hooks)
181
+ require 'concurrent'
182
+ pool = Concurrent::ThreadPoolExecutor.new
183
+
184
+ futures = hooks.map do |hook|
185
+ Concurrent::Future.execute(executor: pool) do
186
+ hook['proc'].call
187
+ end
188
+ end
189
+
190
+ futures.zip(hooks).map do |future, hook|
191
+ future.value || Bolt::Result.from_exception(hook['target'], future.reason)
192
+ end
193
+ end
194
+
195
+ # Returns true if the client's major version is < 6.
196
+ #
197
+ private def unsupported_puppet?(client_version)
198
+ if client_version.nil?
199
+ false
200
+ else
201
+ begin
202
+ Integer(client_version.split('.').first) < 6
203
+ rescue StandardError
204
+ false
205
+ end
206
+ end
207
+ end
208
+ end
@@ -0,0 +1,62 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Starts a block of code in parallel with the main plan without blocking.
4
+ # Returns a Future object.
5
+ #
6
+ # > **Note:** Not available in apply block
7
+ Puppet::Functions.create_function(:background, Puppet::Functions::InternalFunction) do
8
+ # Starts a block of code in parallel with the main plan without blocking.
9
+ # Returns a Future object.
10
+ # @param name An optional name for legible logs.
11
+ # @param block The code block to run in the background.
12
+ # @return A Bolt Future object
13
+ # @example Start a long-running process
14
+ # background() || {
15
+ # run_task('superlong::task', $targets)
16
+ # }
17
+ # run_command("echo 'Continue immediately'", $targets)
18
+ dispatch :background do
19
+ scope_param
20
+ optional_param 'String[1]', :name
21
+ block_param 'Callable[0, 0]', :block
22
+ return_type 'Future'
23
+ end
24
+
25
+ def background(scope, name = nil, &block)
26
+ unless Puppet[:tasks]
27
+ raise Puppet::ParseErrorWithIssue
28
+ .from_issue_and_stack(Bolt::PAL::Issues::PLAN_OPERATION_NOT_SUPPORTED_WHEN_COMPILING, action: 'background')
29
+ end
30
+
31
+ executor = Puppet.lookup(:bolt_executor)
32
+ executor.report_function_call(self.class.name)
33
+
34
+ plan_id = executor.get_current_plan_id(fiber: Fiber.current)
35
+ executor.create_future(scope: scope, name: name, plan_id: plan_id) do |newscope|
36
+ # Catch 'return' calls inside the block
37
+ result = catch(:return) do
38
+ # Execute the block. Individual plan steps in the block will yield
39
+ # the Fiber if they haven't finished, so all this needs to do is run
40
+ # the block.
41
+ block.closure.call_by_name_with_scope(newscope, {}, true)
42
+ end
43
+
44
+ # If we got a return from the block, get its value
45
+ # Otherwise the result is the last line from the block
46
+ result = result.value if result.is_a?(Puppet::Pops::Evaluator::Return)
47
+
48
+ # Validate the result is a PlanResult
49
+ unless Puppet::Pops::Types::TypeParser.singleton.parse('Boltlib::PlanResult').instance?(result)
50
+ raise Bolt::InvalidParallelResult.new(result.to_s, *Puppet::Pops::PuppetStack.top_of_stack)
51
+ end
52
+
53
+ result
54
+ rescue Puppet::PreformattedError => e
55
+ if e.cause.is_a?(Bolt::Error)
56
+ e.cause
57
+ else
58
+ raise e
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Catches errors in a given block and returns them. This will return the
4
+ # output of the block if no errors are raised. Accepts an optional list of
5
+ # error kinds to catch.
6
+ #
7
+ # > **Note:** Not available in apply block
8
+ Puppet::Functions.create_function(:catch_errors) do
9
+ # @param error_types An array of error types to catch
10
+ # @param block The block of steps to catch errors on
11
+ # @return If an error is raised in the block then the error will be returned,
12
+ # otherwise the result will be returned
13
+ # @example Catch errors for a block
14
+ # catch_errors() || {
15
+ # run_command("whoami", $targets)
16
+ # run_command("adduser ryan", $targets)
17
+ # }
18
+ # @example Catch parse errors for a block of code
19
+ # catch_errors(['bolt/parse-error']) || {
20
+ # run_plan('canary', $targets)
21
+ # run_plan('other_plan)
22
+ # apply($targets) || {
23
+ # notify { "Hello": }
24
+ # }
25
+ # }
26
+ dispatch :catch_errors do
27
+ optional_param 'Array[String[1]]', :error_types
28
+ block_param 'Callable[0, 0]', :block
29
+ return_type 'Any'
30
+ end
31
+
32
+ def catch_errors(error_types = nil)
33
+ unless Puppet[:tasks]
34
+ raise Puppet::ParseErrorWithIssue
35
+ .from_issue_and_stack(Bolt::PAL::Issues::PLAN_OPERATION_NOT_SUPPORTED_WHEN_COMPILING,
36
+ action: self.class.name)
37
+ end
38
+
39
+ executor = Puppet.lookup(:bolt_executor)
40
+ # Send Analytics Report
41
+ executor.report_function_call(self.class.name)
42
+
43
+ begin
44
+ yield
45
+ rescue Puppet::PreformattedError => e
46
+ if e.cause.is_a?(Bolt::Error)
47
+ if error_types.nil? || error_types.include?(e.cause.to_h['kind'])
48
+ e.cause.to_puppet_error
49
+ else
50
+ raise e
51
+ end
52
+ else
53
+ raise e
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,130 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'pathname'
4
+ require 'bolt/error'
5
+
6
+ # Downloads the given file or directory from the given set of targets and saves it to a directory
7
+ # matching the target's name under the given destination directory. Returns the result from each
8
+ # download. This does nothing if the list of targets is empty.
9
+ #
10
+ # > **Note:** Existing content in the destination directory is deleted before downloading from
11
+ # > the targets.
12
+ #
13
+ # > **Note:** Not available in apply block
14
+ Puppet::Functions.create_function(:download_file, Puppet::Functions::InternalFunction) do
15
+ # Download a file or directory.
16
+ # @param source The absolute path to the file or directory on the target(s).
17
+ # @param destination The relative path to the destination directory on the local system. Expands
18
+ # relative to `<project>/downloads/`.
19
+ # @param targets A pattern identifying zero or more targets. See {get_targets} for accepted patterns.
20
+ # @param options A hash of additional options.
21
+ # @option options [Boolean] _catch_errors Whether to catch raised errors.
22
+ # @option options [String] _run_as User to run as using privilege escalation.
23
+ # @return A list of results, one entry per target, with the path to the downloaded file under the
24
+ # `path` key.
25
+ # @example Download a file from multiple Linux targets to a destination directory
26
+ # download_file('/etc/ssh/ssh_config', '~/Downloads', $targets)
27
+ # @example Download a directory from multiple Linux targets to a project downloads directory
28
+ # download_file('/etc/ssh', 'ssh', $targets)
29
+ # @example Download a file from multiple Linux targets and compare its contents to a local file
30
+ # $results = download_file($source, $destination, $targets)
31
+ #
32
+ # $local_content = file::read($source)
33
+ #
34
+ # $mismatched_files = $results.filter |$result| {
35
+ # $remote_content = file::read($result['path'])
36
+ # $remote_content == $local_content
37
+ # }
38
+ dispatch :download_file do
39
+ param 'String[1]', :source
40
+ param 'String[1]', :destination
41
+ param 'Boltlib::TargetSpec', :targets
42
+ optional_param 'Hash[String[1], Any]', :options
43
+ return_type 'ResultSet'
44
+ end
45
+
46
+ # Download a file or directory, logging the provided description.
47
+ # @param source The absolute path to the file or directory on the target(s).
48
+ # @param destination The relative path to the destination directory on the local system. Expands
49
+ # relative to `<project>/downloads/`.
50
+ # @param targets A pattern identifying zero or more targets. See {get_targets} for accepted patterns.
51
+ # @param description A description to be output when calling this function.
52
+ # @param options A hash of additional options.
53
+ # @option options [Boolean] _catch_errors Whether to catch raised errors.
54
+ # @option options [String] _run_as User to run as using privilege escalation.
55
+ # @return A list of results, one entry per target, with the path to the downloaded file under the
56
+ # `path` key.
57
+ # @example Download a file from multiple Linux targets to a destination directory
58
+ # download_file('/etc/ssh/ssh_config', '~/Downloads', $targets, 'Downloading remote SSH config')
59
+ dispatch :download_file_with_description do
60
+ param 'String[1]', :source
61
+ param 'String[1]', :destination
62
+ param 'Boltlib::TargetSpec', :targets
63
+ param 'String', :description
64
+ optional_param 'Hash[String[1], Any]', :options
65
+ return_type 'ResultSet'
66
+ end
67
+
68
+ def download_file(source, destination, targets, options = {})
69
+ download_file_with_description(source, destination, targets, nil, options)
70
+ end
71
+
72
+ def download_file_with_description(source, destination, targets, description = nil, options = {})
73
+ unless Puppet[:tasks]
74
+ raise Puppet::ParseErrorWithIssue
75
+ .from_issue_and_stack(Bolt::PAL::Issues::PLAN_OPERATION_NOT_SUPPORTED_WHEN_COMPILING, action: 'download_file')
76
+ end
77
+
78
+ options = options.select { |opt| opt.start_with?('_') }.transform_keys { |k| k.sub(/^_/, '').to_sym }
79
+ options[:description] = description if description
80
+
81
+ executor = Puppet.lookup(:bolt_executor)
82
+ inventory = Puppet.lookup(:bolt_inventory)
83
+
84
+ if (destination = destination.strip).empty?
85
+ raise Bolt::ValidationError, "Destination cannot be an empty string"
86
+ end
87
+
88
+ if (destination = Pathname.new(destination)).absolute?
89
+ raise Bolt::ValidationError, "Destination must be a relative path, received absolute path #{destination}"
90
+ end
91
+
92
+ # Prevent path traversal so downloads can't be saved outside of the project downloads directory
93
+ if (destination.each_filename.to_a & %w[. ..]).any?
94
+ raise Bolt::ValidationError, "Destination must not include path traversal, received #{destination}"
95
+ end
96
+
97
+ # Paths expand relative to the default downloads directory for the project
98
+ # e.g. ~/.puppetlabs/bolt/downloads/
99
+ destination = Puppet.lookup(:bolt_project).downloads + destination
100
+
101
+ # If the destination directory already exists, delete any existing contents
102
+ if Dir.exist?(destination)
103
+ FileUtils.rm_r(Dir.glob(destination + '*'), secure: true)
104
+ end
105
+
106
+ # Send Analytics Report
107
+ executor.report_function_call(self.class.name)
108
+
109
+ # Ensure that that given targets are all Target instances
110
+ targets = inventory.get_targets(targets)
111
+ if targets.empty?
112
+ call_function('debug', "Simulating file download of '#{source}' - no targets given - no action taken")
113
+ Bolt::ResultSet.new([])
114
+ else
115
+ file_line = Puppet::Pops::PuppetStack.top_of_stack
116
+ r = if executor.in_parallel?
117
+ executor.run_in_thread do
118
+ executor.download_file(targets, source, destination, options, file_line)
119
+ end
120
+ else
121
+ executor.download_file(targets, source, destination, options, file_line)
122
+ end
123
+
124
+ if !r.ok && !options[:catch_errors]
125
+ raise Bolt::RunFailure.new(r, 'download_file', source)
126
+ end
127
+ r
128
+ end
129
+ end
130
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bolt/error'
4
+
5
+ # Returns the facts hash for a target.
6
+ #
7
+ # Using the `facts` function does not automatically collect facts for a target,
8
+ # and will only return facts that are currently set in the inventory. To collect
9
+ # facts from a target and set them in the inventory, run the
10
+ # [facts](writing_plans.md#collect-facts-from-targets) plan or
11
+ # [puppetdb_fact](writing_plans.md#collect-facts-from-puppetdb) plan.
12
+ Puppet::Functions.create_function(:facts) do
13
+ # @param target A target.
14
+ # @return The target's facts.
15
+ # @example Getting facts
16
+ # facts($target)
17
+ dispatch :facts do
18
+ param 'Target', :target
19
+ return_type 'Hash[String, Data]'
20
+ end
21
+
22
+ def facts(target)
23
+ inventory = Puppet.lookup(:bolt_inventory)
24
+ # Bolt executor not expected when invoked from apply block
25
+ executor = Puppet.lookup(:bolt_executor) { nil }
26
+ # Send Analytics Report
27
+ executor&.report_function_call(self.class.name)
28
+
29
+ inventory.facts(target)
30
+ end
31
+ end
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bolt/error'
4
+
5
+ # Raises a `Bolt::PlanFailure` exception to signal to callers that the plan failed.
6
+ #
7
+ # Plan authors should call this function when their plan is not successful. The
8
+ # error may then be caught by another plans `run_plan` function or in Bolt itself
9
+ #
10
+ # > **Note:** Not available in apply block
11
+ Puppet::Functions.create_function(:fail_plan) do
12
+ # Fail a plan, generating an exception from the parameters.
13
+ # @param msg An error message.
14
+ # @param kind An easily matchable error kind.
15
+ # @param details Machine-parseable details about the error.
16
+ # @param issue_code Unused.
17
+ # @return Raises an exception.
18
+ # @example Raise an exception
19
+ # fail_plan('We goofed up', 'task-unexpected-result', { 'result' => 'null' })
20
+ dispatch :from_args do
21
+ param 'String[1]', :msg
22
+ optional_param 'String[1]', :kind
23
+ optional_param 'Hash[String[1], Any]', :details
24
+ optional_param 'String[1]', :issue_code
25
+ end
26
+
27
+ # Fail a plan, generating an exception from an existing Error object.
28
+ # @param error An error object.
29
+ # @return Raises an exception.
30
+ # @example Raise an exception
31
+ # fail_plan(Error('We goofed up', 'task-unexpected-result', { 'result' => 'null' }))
32
+ dispatch :from_error do
33
+ param 'Error', :error
34
+ end
35
+
36
+ def from_args(msg, kind = nil, details = nil, issue_code = nil)
37
+ unless Puppet[:tasks]
38
+ raise Puppet::ParseErrorWithIssue
39
+ .from_issue_and_stack(Bolt::PAL::Issues::PLAN_OPERATION_NOT_SUPPORTED_WHEN_COMPILING, action: 'fail_plan')
40
+ end
41
+
42
+ executor = Puppet.lookup(:bolt_executor)
43
+ # Send Analytics Report
44
+ executor.report_function_call(self.class.name)
45
+
46
+ raise Bolt::PlanFailure.new(msg, kind || 'bolt/plan-failure', details, issue_code)
47
+ end
48
+
49
+ def from_error(err)
50
+ from_args(err.message, err.kind, err.details, err.issue_code)
51
+ end
52
+ end
@@ -0,0 +1,87 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bolt/task'
4
+
5
+ # Query the state of resources on a list of targets using resource definitions in Bolt's module path.
6
+ # The results are returned as a list of hashes representing each resource.
7
+ #
8
+ # Requires the Puppet Agent be installed on the target, which can be accomplished with apply_prep
9
+ # or by directly running the `puppet_agent::install` task. In order to be able to reference types without
10
+ # string quoting (for example `get_resources($target, Package)` instead of `get_resources($target, 'Package')`),
11
+ # run the command `bolt puppetfile generate-types` to generate type references in `$Boldir/.resource_types`.
12
+ #
13
+ # > **Note:** Not available in apply block
14
+ Puppet::Functions.create_function(:get_resources) do
15
+ # @param targets A pattern or array of patterns identifying a set of targets.
16
+ # @param resources A resource type or instance, or an array of such.
17
+ # @return A result set with a list of hashes representing each resource.
18
+ # @example Collect resource states for packages and a file
19
+ # get_resources('target1,target2', [Package, File[/etc/puppetlabs]])
20
+ dispatch :get_resources do
21
+ param 'Boltlib::TargetSpec', :targets
22
+ param 'Variant[String, Type[Resource], Array[Variant[String, Type[Resource]]]]', :resources
23
+ return_type 'ResultSet'
24
+ end
25
+
26
+ def script_compiler
27
+ @script_compiler ||= Puppet::Pal::ScriptCompiler.new(closure_scope.compiler)
28
+ end
29
+
30
+ def run_task(executor, targets, name, args = {})
31
+ tasksig = script_compiler.task_signature(name)
32
+ raise Bolt::Error.new("#{name} could not be found", 'bolt/get-resources') unless tasksig
33
+
34
+ task = Bolt::Task.from_task_signature(tasksig)
35
+ results = executor.run_task(targets, task, args)
36
+ raise Bolt::RunFailure.new(results, 'run_task', task.name) unless results.ok?
37
+ results
38
+ end
39
+
40
+ def get_resources(target_spec, resources)
41
+ unless Puppet[:tasks]
42
+ raise Puppet::ParseErrorWithIssue
43
+ .from_issue_and_stack(Bolt::PAL::Issues::PLAN_OPERATION_NOT_SUPPORTED_WHEN_COMPILING, action: 'get_resources')
44
+ end
45
+
46
+ applicator = Puppet.lookup(:apply_executor)
47
+ executor = Puppet.lookup(:bolt_executor)
48
+ inventory = Puppet.lookup(:bolt_inventory)
49
+
50
+ resources = [resources].flatten
51
+
52
+ # Stringify resource types to pass to task
53
+ resources.map! { |r| r.is_a?(String) ? r : r.to_s }
54
+
55
+ resources.each do |resource|
56
+ if resource !~ /^\w+$/ && resource !~ /^\w+\[.+\]$/
57
+ raise Bolt::Error.new("#{resource} is not a valid resource type or type instance name", 'bolt/get-resources')
58
+ end
59
+ end
60
+
61
+ # Send Analytics Report
62
+ executor.report_function_call(self.class.name)
63
+
64
+ targets = inventory.get_targets(target_spec)
65
+
66
+ executor.log_action('gather resources', targets) do
67
+ executor.without_default_logging do
68
+ # Gather facts, including custom facts
69
+ plugins = applicator.build_plugin_tarball do |mod|
70
+ search_dirs = []
71
+ search_dirs << mod.plugins if mod.plugins?
72
+ search_dirs << mod.pluginfacts if mod.pluginfacts?
73
+ search_dirs
74
+ end
75
+
76
+ task = applicator.query_resources_task
77
+ arguments = {
78
+ 'resources' => resources,
79
+ 'plugins' => Puppet::Pops::Types::PSensitiveType::Sensitive.new(plugins)
80
+ }
81
+ results = executor.run_task(targets, task, arguments)
82
+ raise Bolt::RunFailure.new(results, 'run_task', task.name) unless results.ok?
83
+ results
84
+ end
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bolt/error'
4
+
5
+ # Get a single target from inventory if it exists, otherwise create a new Target.
6
+ #
7
+ # > **Note:** Calling `get_target('all')` returns an empty array.
8
+ Puppet::Functions.create_function(:get_target) do
9
+ # @param name A Target name.
10
+ # @return A single target, either new or from inventory.
11
+ # @example Create a new Target from a URI
12
+ # get_target('winrm://host2:54321')
13
+ # @example Get an existing Target from inventory
14
+ # get_target('existing-target')
15
+ dispatch :get_target do
16
+ param 'Boltlib::TargetSpec', :name
17
+ return_type 'Target'
18
+ end
19
+
20
+ def get_target(name)
21
+ inventory = Puppet.lookup(:bolt_inventory)
22
+ # Bolt executor not expected when invoked from apply block
23
+ executor = Puppet.lookup(:bolt_executor) { nil }
24
+ # Send Analytics Report
25
+ executor&.report_function_call(self.class.name)
26
+
27
+ unless inventory.version > 1
28
+ raise Puppet::ParseErrorWithIssue
29
+ .from_issue_and_stack(Bolt::PAL::Issues::UNSUPPORTED_INVENTORY_VERSION, action: 'get_target')
30
+ end
31
+
32
+ inventory.get_target(name)
33
+ end
34
+ end