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,190 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bolt_spec/bolt_context'
4
+ require 'bolt_spec/plans/mock_executor'
5
+ require 'bolt/pal'
6
+
7
+ # These helpers are intended to be used for plan unit testing without calling
8
+ # out to targets. It uses the BoltContext helper to set up a mock executor
9
+ # which allows calls to run_* functions to be stubbed for testing. The context
10
+ # helper also loads Bolt datatypes and plan functions to be used by the code
11
+ # being tested.
12
+ #
13
+ # Example:
14
+ # describe "my_plan" do
15
+ # it 'should return' do
16
+ # allow_task('my_task').always_return({'result_key' => 10})
17
+ # expect(run_plan('my_plan', { 'param1' => 10 })).to be
18
+ # end
19
+ #
20
+ # it 'should call task with param1' do
21
+ # expect_task('my_task').with_params('param1' => 10).always_return({'result_key' => 10})
22
+ # expect(run_plan('my_plan', { 'param1' => 10 })).to eq(10)
23
+ # end
24
+ #
25
+ # it 'should call task with param1 once' do
26
+ # expect_task('my_task').with_params('param1' => 10).always_return({'result_key' => 10}).be_called_times(1)
27
+ # expect(run_plan('my_plan', { 'param1' => 10 })).to eq(10)
28
+ # end
29
+ #
30
+ # it 'should not_call task with 100' do
31
+ # allow_task('my_task').always_return({'result_key' => 10})
32
+ # # Any call with param1 => 100 will match this since it's added second
33
+ # expect_task('my_task').with_params('param1' => 100).not_be_called
34
+ # expect(run_plan('my_plan', { 'param1' => 10 })).to eq(10)
35
+ # end
36
+ #
37
+ # it 'should be called on both node1 and node2' do
38
+ # expect_task('my_task').with_targets(['node1', 'node2']).always_return({'result_key' => 10})
39
+ # expect(run_plan('my_plan', { 'param1' => 10 })).to eq(10)
40
+ # end
41
+ #
42
+ # it 'should average results from targets' do
43
+ # expect_task('my_task').return_for_targets({
44
+ # 'node1' => {'result_key' => 20},
45
+ # 'node2' => {'result_key' => 6} })
46
+ # expect(run_plan('my_plan', { 'param1' => 10 })).to eq(13)
47
+ # end
48
+ #
49
+ # it 'should construct a custom return value' do
50
+ # expect_task('my_task').return do |targets:, task:, params:|
51
+ # Bolt::ResultSet.new(targets.map { |targ| Bolt::Result.new(targ, {'result_key' => 10'})})
52
+ # end
53
+ # expect(run_plan('my_plan', { 'param1' => 10 })).to eq(10)
54
+ # end
55
+ #
56
+ # it 'expects multiple messages to out::message' do
57
+ # expect_out_message.be_called_times(2).with_params(message)
58
+ # result = run_plan(plan_name, 'messages' => [message, message])
59
+ # expect(result).to be_ok
60
+ # end
61
+ #
62
+ # it 'expects a sub-plan to be called' do
63
+ # expect_plan('module::sub_plan').with_params('targets' => ['foo']).be_called_times(1)
64
+ # result = run_plan('module::main_plan', 'targets' => ['foo'])
65
+ # expect(result).to be_ok
66
+ # expect(result.class).to eq(Bolt::PlanResult)
67
+ # expect(result.value).to eq('foo' => 'is_good')
68
+ # expect(result.status).to eq('success')
69
+ # end
70
+ #
71
+ # it 'error when sub-plan is called' do
72
+ # execute_no_plan
73
+ # err = 'Unexpected call to 'run_plan(module::sub_plan, {\"targets\"=>[\"foo\"]})'
74
+ # expect { run_plan('module::main_plan', 'targets' => ['foo']) }
75
+ # .to raise_error(RuntimeError, err)
76
+ # end
77
+ #
78
+ # it 'errors when plan calls fail_plan()' do
79
+ # result = run_plan('module::calls_fail_plan', {})
80
+ # expect(result).not_to be_ok
81
+ # expect(result.class).to eq(Bolt::PlanResult)
82
+ # expect(result.status).to eq('failure')
83
+ # expect(result.value.class).to eq(Bolt::PlanFailure)
84
+ # expect(result.value.msg).to eq('failure message passed to fail_plan()')
85
+ # expect(result.value.kind).to eq('bolt/plan-failure')
86
+ # end
87
+ # end
88
+ #
89
+ # See spec/bolt_spec/plan_spec.rb for more examples.
90
+ module BoltSpec
91
+ module Plans
92
+ include BoltSpec::BoltContext
93
+
94
+ def self.init
95
+ # Ensure tasks are enabled when rspec-puppet sets up an environment so we get task loaders.
96
+ # Note that this is probably not safe to do in modules that also test Puppet manifest code.
97
+ Bolt::PAL.load_puppet
98
+ Puppet[:tasks] = true
99
+
100
+ # Ensure logger is initialized with Puppet levels so 'notice' works when running plan specs.
101
+ Logging.init :trace, :debug, :info, :notice, :warn, :error, :fatal
102
+ end
103
+
104
+ # Provided as a class so expectations can be placed on it.
105
+ class MockPuppetDBClient
106
+ def initialize(config)
107
+ @instance = MockPuppetDBInstance.new(config)
108
+ end
109
+
110
+ def instance(_instance)
111
+ @instance
112
+ end
113
+ end
114
+
115
+ class MockPuppetDBInstance
116
+ attr_reader :config
117
+
118
+ def initialize(config)
119
+ @config = config
120
+ end
121
+ end
122
+
123
+ def puppetdb_client
124
+ @puppetdb_client ||= MockPuppetDBClient.new({})
125
+ end
126
+
127
+ def run_plan(name, params)
128
+ pal = Bolt::PAL.new(
129
+ Bolt::Config::Modulepath.new(config.modulepath),
130
+ config.hiera_config,
131
+ config.project.resource_types,
132
+ config.compile_concurrency,
133
+ config.trusted_external,
134
+ config.apply_settings,
135
+ config.project
136
+ )
137
+
138
+ result = executor.with_plan_allowed_exec(name, params) do
139
+ pal.run_plan(name, params, executor, inventory, puppetdb_client)
140
+ end
141
+
142
+ if executor.error_message
143
+ raise executor.error_message
144
+ end
145
+
146
+ begin
147
+ executor.assert_call_expectations
148
+ rescue StandardError => e
149
+ raise "#{e.message}\nPlan result: #{result}\n#{e.backtrace.join("\n")}"
150
+ end
151
+
152
+ result
153
+ end
154
+
155
+ def allow_apply_prep
156
+ allow_task('apply_helpers::custom_facts')
157
+ nil
158
+ end
159
+
160
+ def allow_apply
161
+ executor.stub_apply
162
+ nil
163
+ end
164
+
165
+ def allow_get_resources
166
+ allow_task('apply_helpers::query_resources')
167
+ nil
168
+ end
169
+
170
+ # Flag for the default behavior of executing sub-plans during testing
171
+ # By *default* we allow any sub-plan to be executed, no mocking required.
172
+ # Users can still mock out plans in this mode and the mocks will check for
173
+ # parameters and return values like normal. However, if a plan isn't explicitly
174
+ # mocked out, it will be executed.
175
+ def execute_any_plan
176
+ executor.execute_any_plan = true
177
+ end
178
+
179
+ # If you want to explicitly mock out all of the sub-plan calls, then
180
+ # call this prior to calling `run_plan()` along with setting up any
181
+ # mocks that you require.
182
+ # In this mode, any plan that is not explicitly mocked out will not be executed
183
+ # and an error will be thrown.
184
+ def execute_no_plan
185
+ executor.execute_any_plan = false
186
+ end
187
+
188
+ # intended to be private below here
189
+ end
190
+ end
@@ -0,0 +1,246 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bolt/analytics'
4
+ require 'bolt/config'
5
+ require 'bolt/executor'
6
+ require 'bolt/inventory'
7
+ require 'bolt/pal'
8
+ require 'bolt/plugin'
9
+ require 'bolt/puppetdb'
10
+ require 'bolt/util'
11
+ require 'bolt/logger'
12
+
13
+ # This is intended to provide a relatively stable method of executing bolt in process from tests.
14
+ module BoltSpec
15
+ module Run
16
+ def run_task(task_name, targets, params, config: nil, inventory: nil)
17
+ if config.nil? && defined?(bolt_config)
18
+ config = bolt_config
19
+ end
20
+
21
+ if inventory.nil? && defined?(bolt_inventory)
22
+ inventory = bolt_inventory
23
+ end
24
+
25
+ result = BoltRunner.with_runner(config, inventory) do |runner|
26
+ runner.run_task(task_name, targets, params)
27
+ end
28
+ result = result.to_a
29
+ Bolt::Util.walk_keys(result, &:to_s)
30
+ end
31
+
32
+ def run_plan(plan_name, params, config: nil, inventory: nil)
33
+ if config.nil? && defined?(bolt_config)
34
+ config = bolt_config
35
+ end
36
+
37
+ if inventory.nil? && defined?(bolt_inventory)
38
+ inventory = bolt_inventory
39
+ end
40
+
41
+ # Users copying code from run_task may forget that targets is not a parameter for run plan
42
+ raise ArgumentError, "params must be a hash" unless params.is_a?(Hash)
43
+
44
+ result = BoltRunner.with_runner(config, inventory) do |runner|
45
+ runner.run_plan(plan_name, params)
46
+ end
47
+
48
+ { "status" => result.status,
49
+ "value" => JSON.parse(result.value.to_json) }
50
+ end
51
+
52
+ def run_command(command, targets, options: {}, config: nil, inventory: nil)
53
+ if config.nil? && defined?(bolt_config)
54
+ config = bolt_config
55
+ end
56
+
57
+ if inventory.nil? && defined?(bolt_inventory)
58
+ inventory = bolt_inventory
59
+ end
60
+
61
+ result = BoltRunner.with_runner(config, inventory) do |runner|
62
+ runner.run_command(command, targets, options)
63
+ end
64
+ result = result.to_a
65
+ Bolt::Util.walk_keys(result, &:to_s)
66
+ end
67
+
68
+ def run_script(script, targets, arguments, options: {}, config: nil, inventory: nil)
69
+ if config.nil? && defined?(bolt_config)
70
+ config = bolt_config
71
+ end
72
+
73
+ if inventory.nil? && defined?(bolt_inventory)
74
+ inventory = bolt_inventory
75
+ end
76
+
77
+ result = BoltRunner.with_runner(config, inventory) do |runner|
78
+ runner.run_script(script, targets, arguments, options)
79
+ end
80
+ result = result.to_a
81
+ Bolt::Util.walk_keys(result, &:to_s)
82
+ end
83
+
84
+ def download_file(source, dest, targets, options: {}, config: nil, inventory: nil)
85
+ if config.nil? && defined?(bolt_config)
86
+ config = bolt_config
87
+ end
88
+
89
+ if inventory.nil? && defined?(bolt_inventory)
90
+ inventory = bolt_inventory
91
+ end
92
+
93
+ result = BoltRunner.with_runner(config, inventory) do |runner|
94
+ runner.download_file(source, dest, targets, options)
95
+ end
96
+ result = result.to_a
97
+ Bolt::Util.walk_keys(result, &:to_s)
98
+ end
99
+
100
+ def upload_file(source, dest, targets, options: {}, config: nil, inventory: nil)
101
+ if config.nil? && defined?(bolt_config)
102
+ config = bolt_config
103
+ end
104
+
105
+ if inventory.nil? && defined?(bolt_inventory)
106
+ inventory = bolt_inventory
107
+ end
108
+
109
+ result = BoltRunner.with_runner(config, inventory) do |runner|
110
+ runner.upload_file(source, dest, targets, options)
111
+ end
112
+ result = result.to_a
113
+ Bolt::Util.walk_keys(result, &:to_s)
114
+ end
115
+
116
+ def apply_manifest(manifest, targets, execute: false, noop: false, config: nil, inventory: nil)
117
+ if config.nil? && defined?(bolt_config)
118
+ config = bolt_config
119
+ end
120
+
121
+ if inventory.nil? && defined?(bolt_inventory)
122
+ inventory = bolt_inventory
123
+ end
124
+
125
+ # The execute parameter is equivalent to the --execute option
126
+ if execute
127
+ code = manifest
128
+ else
129
+ begin
130
+ unless File.stat(manifest).readable?
131
+ raise Bolt::FileError.new("The manifest '#{manifest}' is unreadable", manifest)
132
+ end
133
+ rescue Errno::ENOENT
134
+ raise Bolt::FileError.new("The manifest '#{manifest}' does not exist", manifest)
135
+ end
136
+ code = File.read(File.expand_path(manifest))
137
+ filename = manifest
138
+ end
139
+ result = BoltRunner.with_runner(config, inventory) do |runner|
140
+ runner.apply_manifest(code, targets, filename, noop)
141
+ end
142
+ JSON.parse(result.to_json)
143
+ end
144
+
145
+ class BoltRunner
146
+ # Creates a temporary project so no settings are picked up
147
+ # WARNING: puppetdb config and orch config which do not use the project may
148
+ # still be loaded
149
+ def self.with_runner(config_data, inventory_data)
150
+ Dir.mktmpdir do |project_path|
151
+ runner = new(Bolt::Util.deep_clone(config_data), Bolt::Util.deep_clone(inventory_data), project_path)
152
+ yield runner
153
+ end
154
+ end
155
+
156
+ def initialize(config_data, inventory_data, project_path)
157
+ Bolt::Logger.initialize_logging
158
+
159
+ @config_data = config_data || {}
160
+ @inventory_data = inventory_data || {}
161
+ @project_path = project_path
162
+ @analytics = Bolt::Analytics::NoopClient.new
163
+ end
164
+
165
+ def config
166
+ @config ||= Bolt::Config.new(Bolt::Project.create_project(@project_path), @config_data)
167
+ end
168
+
169
+ def inventory
170
+ @inventory ||= Bolt::Inventory.create_version(@inventory_data, config.transport, config.transports, plugins)
171
+ end
172
+
173
+ def plugins
174
+ @plugins ||= Bolt::Plugin.new(config, pal)
175
+ end
176
+
177
+ def puppetdb_client
178
+ plugins.puppetdb_client
179
+ end
180
+
181
+ def pal
182
+ @pal ||= Bolt::PAL.new(Bolt::Config::Modulepath.new(config.modulepath),
183
+ config.hiera_config,
184
+ config.project.resource_types,
185
+ config.compile_concurrency,
186
+ config.trusted_external,
187
+ config.apply_settings,
188
+ config.project)
189
+ end
190
+
191
+ def resolve_targets(target_spec)
192
+ @inventory.get_targets(target_spec).map(&:name)
193
+ end
194
+
195
+ # Adapted from CLI
196
+ def run_task(task_name, targets, params, noop: false)
197
+ executor = Bolt::Executor.new(config.concurrency, @analytics, noop)
198
+ pal.run_task(task_name, targets, params, executor, inventory, nil) { |_ev| nil }
199
+ end
200
+
201
+ # Adapted from CLI does not handle nodes or plan_job reporting
202
+ def run_plan(plan_name, params, noop: false)
203
+ executor = Bolt::Executor.new(config.concurrency, @analytics, noop)
204
+ pal.run_plan(plan_name, params, executor, inventory, puppetdb_client)
205
+ end
206
+
207
+ def run_command(command, targets, options)
208
+ executor = Bolt::Executor.new(config.concurrency, @analytics)
209
+ targets = inventory.get_targets(targets)
210
+ executor.run_command(targets, command, options)
211
+ end
212
+
213
+ def run_script(script, targets, arguments, options = {})
214
+ executor = Bolt::Executor.new(config.concurrency, @analytics)
215
+ targets = inventory.get_targets(targets)
216
+ executor.run_script(targets, script, arguments, options)
217
+ end
218
+
219
+ def download_file(source, dest, targets, options = {})
220
+ executor = Bolt::Executor.new(config.concurrency, @analytics)
221
+ targets = inventory.get_targets(targets)
222
+ executor.download_file(targets, source, dest, options)
223
+ end
224
+
225
+ def upload_file(source, dest, targets, options = {})
226
+ executor = Bolt::Executor.new(config.concurrency, @analytics)
227
+ targets = inventory.get_targets(targets)
228
+ executor.upload_file(targets, source, dest, options)
229
+ end
230
+
231
+ def apply_manifest(code, targets, filename = nil, noop = false)
232
+ ast = pal.parse_manifest(code, filename)
233
+ executor = Bolt::Executor.new(config.concurrency, @analytics, noop)
234
+ targets = inventory.get_targets(targets)
235
+
236
+ pal.in_plan_compiler(executor, inventory, puppetdb_client) do |compiler|
237
+ compiler.call_function('apply_prep', targets)
238
+ end
239
+
240
+ pal.with_bolt_executor(executor, inventory, puppetdb_client) do
241
+ Puppet.lookup(:apply_executor).apply_ast(ast, targets, catch_errors: true, noop: noop)
242
+ end
243
+ end
244
+ end
245
+ end
246
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'logging'
4
+
5
+ module LoggingExtensions
6
+ # the logging gem always sets itself up to initialize little-plugger
7
+ # https://github.com/TwP/logging/commit/5aeeffaaa9fe483c2258a23d3b9e92adfafb3b2e
8
+ # little-plugger calls Gem.find_files, incurring an expensive gem scan
9
+ def initialize_plugins; end
10
+ end
11
+
12
+ # monkey patch Logging to override the extended method with a no-op
13
+ Logging.extend(LoggingExtensions)
@@ -0,0 +1,130 @@
1
+ #! /opt/puppetlabs/puppet/bin/ruby
2
+ # frozen_string_literal: true
3
+
4
+ require 'fileutils'
5
+ require 'json'
6
+ require 'puppet'
7
+ require 'puppet/configurer'
8
+ require 'puppet/module_tool/tar'
9
+ require 'securerandom'
10
+ require 'tempfile'
11
+
12
+ args = JSON.parse(ARGV[0] ? File.read(ARGV[0]) : $stdin.read)
13
+
14
+ # Create temporary directories for all core Puppet settings so we don't clobber
15
+ # existing state or read from puppet.conf. Also create a temporary modulepath.
16
+ # Additionally include rundir, which gets its own initialization.
17
+ puppet_root = Dir.mktmpdir
18
+ moduledir = File.join(puppet_root, 'modules')
19
+ Dir.mkdir(moduledir)
20
+ cli = (Puppet::Settings::REQUIRED_APP_SETTINGS + [:rundir]).flat_map do |setting|
21
+ ["--#{setting}", File.join(puppet_root, setting.to_s.chomp('dir'))]
22
+ end
23
+ cli << '--modulepath' << moduledir
24
+ Puppet.initialize_settings(cli)
25
+
26
+ # Avoid extraneous output
27
+ Puppet[:report] = false
28
+
29
+ # Make sure to apply the catalog
30
+ Puppet[:noop] = args['_noop'] || false
31
+ args['apply_settings'].each do |setting, value|
32
+ Puppet[setting.to_sym] = value
33
+ end
34
+
35
+ Puppet[:default_file_terminus] = :file_server
36
+
37
+ exit_code = 0
38
+ begin
39
+ # This happens implicitly when running the Configurer, but we make it explicit here. It creates the
40
+ # directories we configured earlier.
41
+ Puppet.settings.use(:main)
42
+
43
+ Tempfile.open('plugins.tar.gz') do |plugins|
44
+ File.binwrite(plugins, Base64.decode64(args['plugins']))
45
+ user = Etc.getpwuid.nil? ? Etc.getlogin : Etc.getpwuid.name
46
+ Puppet::ModuleTool::Tar.instance.unpack(plugins, moduledir, user)
47
+ end
48
+
49
+ env = Puppet.lookup(:environments).get('production')
50
+ # Needed to ensure features are loaded
51
+ env.each_plugin_directory do |dir|
52
+ $LOAD_PATH << dir unless $LOAD_PATH.include?(dir)
53
+ end
54
+
55
+ # In the case we are applying on a bolt runner and using bundled-ruby over local transport
56
+ # we will want to load code shipped with bolt. This is last on the load path and therefore
57
+ # explicitly packaged plugins should take precedence
58
+ args['bolt_builtin_content'].each do |builtin_dir|
59
+ next unless Dir.exist?(builtin_dir)
60
+ Dir.foreach(builtin_dir) do |dir|
61
+ unless ['.', '..'].include? dir
62
+ full_path = File.join(builtin_dir, dir, 'lib')
63
+ $LOAD_PATH << full_path unless $LOAD_PATH.include?(full_path)
64
+ end
65
+ end
66
+ end
67
+
68
+ if (conn_info = args['_target'])
69
+ unless (type = conn_info['remote-transport'])
70
+ puts "Cannot execute a catalog for a remote target without knowing it's the remote-transport type."
71
+ exit 1
72
+ end
73
+
74
+ begin
75
+ require 'puppet/resource_api/transport'
76
+ rescue LoadError
77
+ msg = "Could not load 'puppet/resource_api/transport', puppet-resource_api "\
78
+ "gem version 1.8.0 or greater is required on the proxy target"
79
+ puts msg
80
+ exit 1
81
+ end
82
+
83
+ # Transport.connect will modify this hash!
84
+ transport_conn_info = conn_info.transform_keys(&:to_sym)
85
+
86
+ transport = Puppet::ResourceApi::Transport.connect(type, transport_conn_info)
87
+ Puppet::ResourceApi::Transport.inject_device(type, transport)
88
+
89
+ Puppet[:facts_terminus] = :network_device
90
+ Puppet[:certname] = conn_info['name']
91
+ end
92
+
93
+ # Ensure custom facts are available for provider suitability tests
94
+ facts = Puppet::Node::Facts.indirection.find(SecureRandom.uuid, environment: env)
95
+
96
+ report = if Puppet::Util::Package.versioncmp(Puppet.version, '5.0.0') > 0
97
+ Puppet::Transaction::Report.new
98
+ else
99
+ Puppet::Transaction::Report.new('apply')
100
+ end
101
+
102
+ overrides = { current_environment: env,
103
+ loaders: Puppet::Pops::Loaders.new(env) }
104
+ overrides[:network_device] = true if args['_target']
105
+
106
+ Puppet.override(overrides) do
107
+ catalog = Puppet::Resource::Catalog.from_data_hash(args['catalog'])
108
+ catalog.environment = env.name.to_s
109
+ catalog.environment_instance = env
110
+ if defined?(Puppet::Pops::Evaluator::DeferredResolver)
111
+ # Only available in Puppet 6
112
+ Puppet::Pops::Evaluator::DeferredResolver.resolve_and_replace(facts, catalog)
113
+ end
114
+ catalog = catalog.to_ral
115
+
116
+ configurer = Puppet::Configurer.new
117
+ configurer.run(catalog: catalog, report: report, pluginsync: false)
118
+ end
119
+
120
+ puts JSON.pretty_generate(report.to_data_hash)
121
+ exit_code = report.exit_status != 1
122
+ ensure
123
+ begin
124
+ FileUtils.remove_dir(puppet_root)
125
+ rescue Errno::ENOTEMPTY => e
126
+ $stderr.puts("Could not cleanup temporary directory: #{e}")
127
+ end
128
+ end
129
+
130
+ exit exit_code
@@ -0,0 +1,68 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require 'bolt'
5
+ require 'bolt/catalog'
6
+ require 'json'
7
+
8
+ # This accepts a catalog request on stdin:
9
+ # { "code_ast": "JSON serialized Puppet AST",
10
+ # "code_string": "String of code, ignored if AST is provided",
11
+ # "modulepath": "Array of directories to use as the modulepath for catalog compilation",
12
+ # "pdb_config": "A hash of PDB client config options as supplied to Bolt",
13
+ # "hiera_config": "File path to hiera config file",
14
+ # "target": {
15
+ # "name": "the name of the node usually fqdn fro url",
16
+ # "facts": "Hash of facts to use for the node",
17
+ # "variables": "Hash of variables to use for compilation",
18
+ # "trusted": "Hash of trusted data for the node"
19
+ # },
20
+ # "inventory": JSON serialized information about inventory {
21
+ # "data": Data stored in inventory @data instance variable,
22
+ # "target_hash": {
23
+ # "target_vars": Vars that may have been updated plan,
24
+ # "target_facts": Facts that may have been updated in plan,
25
+ # "target_features": Features that may have been updated in plan,
26
+ # },
27
+ # "config": {
28
+ # "transport": Transport to use,
29
+ # "transports": Array of transport configs (note that transport keys are stringified)
30
+ # }
31
+ # }
32
+ # }
33
+
34
+ command = ARGV[0]
35
+ case command
36
+ when "parse"
37
+ code = File.open(ARGV[1], &:read)
38
+ puts JSON.pretty_generate(Bolt::Catalog.new.generate_ast(code, ARGV[1]))
39
+ when "compile"
40
+ request = if ARGV[1]
41
+ File.open(ARGV[1]) { |fh| JSON.parse(fh.read) }
42
+ else
43
+ JSON.parse($stdin.read)
44
+ end
45
+ begin
46
+ catalog = Bolt::Catalog.new.compile_catalog(request)
47
+ # This seems to be a string in ruby 2.3
48
+ if catalog.is_a?(String)
49
+ catalog = JSON.parse(catalog)
50
+ end
51
+ puts JSON.pretty_generate(catalog)
52
+ rescue Puppet::PreformattedError => e
53
+ message = if e.cause
54
+ location_info = Puppet::Util::Errors.error_location_with_space(e.file, e.line, e.pos)
55
+ "#{e.cause.message}#{location_info}"
56
+ else
57
+ e.message
58
+ end
59
+ puts({ message: message, backtrace: e.backtrace }.to_json)
60
+ exit 1
61
+ rescue StandardError => e
62
+ puts({ message: e.message, backtrace: e.backtrace }.to_json)
63
+ exit 1
64
+ end
65
+ else
66
+ puts "USAGE: run 'bolt_catalog compile' with a request on STDIN " \
67
+ "or 'bolt_catalog parse manifest.pp' to generate JSON AST"
68
+ end