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,380 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../bolt/inventory'
4
+ require_relative '../bolt/executor'
5
+ require_relative '../bolt/module'
6
+ require_relative '../bolt/pal'
7
+ require_relative 'plugin/cache'
8
+ require_relative 'plugin/puppetdb'
9
+
10
+ module Bolt
11
+ class Plugin
12
+ KNOWN_HOOKS = %i[
13
+ puppet_library
14
+ resolve_reference
15
+ secret_encrypt
16
+ secret_decrypt
17
+ secret_createkeys
18
+ validate_resolve_reference
19
+ ].freeze
20
+
21
+ class PluginError < Bolt::Error
22
+ class ExecutionError < PluginError
23
+ def initialize(msg, plugin_name, location)
24
+ mess = "Error executing plugin #{plugin_name} from #{location}: #{msg}"
25
+ super(mess, 'bolt/plugin-error')
26
+ end
27
+ end
28
+
29
+ class Unknown < PluginError
30
+ def initialize(plugin_name)
31
+ super("Unknown plugin: '#{plugin_name}'", 'bolt/unknown-plugin')
32
+ end
33
+ end
34
+
35
+ class UnsupportedHook < PluginError
36
+ def initialize(plugin_name, hook)
37
+ super("Plugin #{plugin_name} does not support #{hook}", 'bolt/unsupported-hook')
38
+ end
39
+ end
40
+
41
+ class LoadingDisabled < PluginError
42
+ def initialize(plugin_name)
43
+ msg = "Cannot load plugin #{plugin_name}: plugin loading is disabled"
44
+ super(msg, 'bolt/plugin-loading-disabled', { 'plugin_name' => plugin_name })
45
+ end
46
+ end
47
+ end
48
+
49
+ class PluginContext
50
+ def initialize(config, pal, plugins)
51
+ @pal = pal
52
+ @config = config
53
+ @plugins = plugins
54
+ end
55
+
56
+ def serial_executor
57
+ @serial_executor ||= Bolt::Executor.new(1)
58
+ end
59
+ private :serial_executor
60
+
61
+ def empty_inventory
62
+ @empty_inventory ||= Bolt::Inventory.empty
63
+ end
64
+ private :empty_inventory
65
+
66
+ def with_a_compiler
67
+ # If we're already inside a pal compiler block use that compiler
68
+ # This may blow up if you try to load a task in catalog pal. Should we
69
+ # guard against that?
70
+ compiler = nil
71
+ if defined?(Puppet)
72
+ begin
73
+ compiler = Puppet.lookup(:pal_compiler)
74
+ rescue Puppet::Context::UndefinedBindingError; end # rubocop:disable Lint/SuppressedException
75
+ end
76
+
77
+ if compiler
78
+ yield compiler
79
+ else
80
+ @pal.in_bolt_compiler do |temp_compiler|
81
+ yield temp_compiler
82
+ end
83
+ end
84
+ end
85
+ private :with_a_compiler
86
+
87
+ def get_validated_task(task_name, params = nil)
88
+ with_a_compiler do |compiler|
89
+ tasksig = compiler.task_signature(task_name)
90
+
91
+ raise Bolt::Error.unknown_task(task_name) unless tasksig
92
+
93
+ Bolt::Task::Run.validate_params(tasksig, params) if params
94
+ Bolt::Task.from_task_signature(tasksig)
95
+ end
96
+ end
97
+
98
+ def validate_params(task_name, params)
99
+ with_a_compiler do |compiler|
100
+ tasksig = compiler.task_signature(task_name)
101
+
102
+ raise Bolt::Error.new("#{task_name} could not be found", 'bolt/plugin-error') unless tasksig
103
+
104
+ Bolt::Task::Run.validate_params(tasksig, params)
105
+ end
106
+ nil
107
+ end
108
+
109
+ # By passing `_` keys in params the caller can send metaparams directly to the task
110
+ # _catch_errors must be passed as an executor option not a param
111
+ def run_local_task(task, params, options)
112
+ # Make sure we're in a compiler to use the sensitive type
113
+ with_a_compiler do |_comp|
114
+ params = Bolt::Task::Run.wrap_sensitive(task, params)
115
+ Bolt::Task::Run.run_task(
116
+ task,
117
+ empty_inventory.get_targets('localhost'),
118
+ params,
119
+ options,
120
+ serial_executor
121
+ )
122
+ end
123
+ end
124
+
125
+ def boltdir
126
+ @config.project.path
127
+ end
128
+ end
129
+
130
+ RUBY_PLUGINS = %w[task prompt env_var puppetdb puppet_connect_data].freeze
131
+ BUILTIN_PLUGINS = %w[task terraform pkcs7 prompt vault aws_inventory puppetdb azure_inventory
132
+ yaml env_var gcloud_inventory].freeze
133
+ DEFAULT_PLUGIN_HOOKS = { 'puppet_library' => { 'plugin' => 'puppet_agent', 'stop_service' => true } }.freeze
134
+
135
+ attr_reader :pal, :plugin_context
136
+ attr_writer :plugin_hooks
137
+
138
+ def initialize(config, pal, analytics = Bolt::Analytics::NoopClient.new, load_plugins: true)
139
+ @config = config
140
+ @analytics = analytics
141
+ @plugin_context = PluginContext.new(config, pal, self)
142
+ @plugins = {}
143
+ @pal = pal
144
+ @load_plugins = load_plugins
145
+ @unknown = Set.new
146
+ @resolution_stack = []
147
+ @unresolved_plugin_configs = config.plugins.dup
148
+ # The puppetdb plugin config comes from the puppetdb section, not from
149
+ # the plugins section
150
+ if @unresolved_plugin_configs.key?('puppetdb')
151
+ msg = "Configuration for the PuppetDB plugin must be in the 'puppetdb' config section, not 'plugins'"
152
+ raise Bolt::Error.new(msg, 'bolt/plugin-error')
153
+ end
154
+ @unresolved_plugin_configs['puppetdb'] = config.puppetdb.merge('default' => config.default_puppetdb,
155
+ 'instances' => config.puppetdb_instances)
156
+ end
157
+
158
+ # Returns a map of configured plugin hooks. Any unresolved plugin references
159
+ # are resolved.
160
+ #
161
+ # @return [Hash[String, Hash]]
162
+ #
163
+ def plugin_hooks
164
+ @plugin_hooks ||= DEFAULT_PLUGIN_HOOKS.merge(resolve_references(@config.plugin_hooks))
165
+ end
166
+
167
+ def modules
168
+ @modules ||= Bolt::Module.discover(@pal.full_modulepath, @config.project)
169
+ end
170
+
171
+ def add_plugin(plugin)
172
+ @plugins[plugin.name] = plugin
173
+ end
174
+
175
+ def add_ruby_plugin(plugin_name)
176
+ cls_name = Bolt::Util.snake_name_to_class_name(plugin_name)
177
+ filename = "bolt/plugin/#{plugin_name}"
178
+ require filename
179
+ cls = Kernel.const_get("Bolt::Plugin::#{cls_name}")
180
+ opts = {
181
+ context: @plugin_context,
182
+ config: config_for_plugin(plugin_name)
183
+ }
184
+
185
+ plugin = cls.new(**opts)
186
+ add_plugin(plugin)
187
+ end
188
+
189
+ def add_module_plugin(plugin_name)
190
+ opts = {
191
+ context: @plugin_context,
192
+ # Make sure that the plugin's config is validated _before_ the unknown-plugin
193
+ # and loading-disabled checks. This way, we can fail early on invalid plugin
194
+ # config instead of _after_ loading the modulepath (which can be expensive).
195
+ config: config_for_plugin(plugin_name)
196
+ }
197
+
198
+ mod = modules[plugin_name]
199
+
200
+ plugin = Bolt::Plugin::Module.load(mod, opts)
201
+ add_plugin(plugin)
202
+ end
203
+
204
+ def config_for_plugin(plugin_name)
205
+ return {} unless @unresolved_plugin_configs.include?(plugin_name)
206
+ if @resolution_stack.include?(plugin_name)
207
+ msg = "Configuration for plugin '#{plugin_name}' depends on the plugin itself"
208
+ raise PluginError.new(msg, 'bolt/plugin-error')
209
+ else
210
+ @resolution_stack.push(plugin_name)
211
+ config = resolve_references(@unresolved_plugin_configs[plugin_name])
212
+ @unresolved_plugin_configs.delete(plugin_name)
213
+ @resolution_stack.pop
214
+ config
215
+ end
216
+ end
217
+
218
+ def known_plugin?(plugin_name)
219
+ @plugins.include?(plugin_name) ||
220
+ RUBY_PLUGINS.include?(plugin_name) ||
221
+ (modules.include?(plugin_name) && modules[plugin_name].plugin?)
222
+ end
223
+
224
+ def get_hook(plugin_name, hook)
225
+ plugin = by_name(plugin_name)
226
+ raise PluginError::Unknown, plugin_name unless plugin
227
+ raise PluginError::UnsupportedHook.new(plugin_name, hook) unless plugin.hooks.include?(hook)
228
+ @analytics.report_bundled_content("Plugin #{hook}", plugin_name)
229
+
230
+ plugin.method(hook)
231
+ end
232
+
233
+ # Calling by_name or get_hook will load any module based plugin automatically
234
+ def by_name(plugin_name)
235
+ if known_plugin?(plugin_name)
236
+ if @plugins.include?(plugin_name)
237
+ @plugins[plugin_name]
238
+ elsif !@load_plugins
239
+ raise PluginError::LoadingDisabled, plugin_name
240
+ elsif RUBY_PLUGINS.include?(plugin_name)
241
+ add_ruby_plugin(plugin_name)
242
+ else
243
+ add_module_plugin(plugin_name)
244
+ end
245
+ end
246
+ end
247
+
248
+ # Loads all plugins and returns a map of plugin names to hooks.
249
+ #
250
+ def list_plugins
251
+ load_all_plugins
252
+
253
+ hooks = KNOWN_HOOKS.map { |hook| [hook, {}] }.to_h
254
+
255
+ @plugins.sort.each do |name, plugin|
256
+ # Don't show the Puppet Connect plugin for now.
257
+ next if name == 'puppet_connect_data'
258
+
259
+ case plugin
260
+ when Bolt::Plugin::Module
261
+ plugin.hook_map.each do |hook, spec|
262
+ next unless hooks.include?(hook)
263
+ hooks[hook][name] = spec['task'].description
264
+ end
265
+ else
266
+ plugin.hook_descriptions.each do |hook, description|
267
+ hooks[hook][name] = description
268
+ end
269
+ end
270
+ end
271
+
272
+ hooks
273
+ end
274
+
275
+ # Loads all plugins available to the project.
276
+ #
277
+ private def load_all_plugins
278
+ modules.each do |name, mod|
279
+ next unless mod.plugin?
280
+ by_name(name)
281
+ end
282
+
283
+ RUBY_PLUGINS.each { |name| by_name(name) }
284
+ end
285
+
286
+ def puppetdb_client
287
+ by_name('puppetdb').puppetdb_client
288
+ end
289
+
290
+ # Evaluate all _plugin references in a data structure. Leaves are
291
+ # evaluated and then their parents are evaluated with references replaced
292
+ # by their values. If the result of a reference contains more references,
293
+ # they are resolved again before continuing to ascend the tree. The final
294
+ # result will not contain any references.
295
+ def resolve_references(data)
296
+ Bolt::Util.postwalk_vals(data) do |value|
297
+ reference?(value) ? resolve_references(resolve_single_reference(value)) : value
298
+ end
299
+ rescue SystemStackError
300
+ raise Bolt::Error.new("Stack depth exceeded while recursively resolving references.",
301
+ "bolt/recursive-reference-loop")
302
+ end
303
+
304
+ # Iteratively resolves "top-level" references until the result no longer
305
+ # has top-level references. A top-level reference is one which is not
306
+ # contained within another hash. It may be either the actual top-level
307
+ # result or arbitrarily nested within arrays. If parameters of the
308
+ # reference are themselves references, they will be looked. Any remaining
309
+ # references nested inside the result will *not* be evaluated once the
310
+ # top-level result is not a reference. This is used to resolve the
311
+ # `targets` and `groups` keys which are allowed to be references or
312
+ # arrays of references, but which may return data with nested references
313
+ # that should be resolved lazily. The end result will either be a single
314
+ # hash or a flat array of hashes.
315
+ def resolve_top_level_references(data)
316
+ if data.is_a?(Array)
317
+ data.flat_map { |elem| resolve_top_level_references(elem) }
318
+ elsif reference?(data)
319
+ partially_resolved = data.transform_values do |v|
320
+ resolve_references(v)
321
+ end
322
+ fully_resolved = resolve_single_reference(partially_resolved)
323
+ # The top-level reference may have returned more references, so repeat the process
324
+ resolve_top_level_references(fully_resolved)
325
+ else
326
+ data
327
+ end
328
+ end
329
+
330
+ # Evaluates a single reference. The value returned may be another
331
+ # reference.
332
+ def resolve_single_reference(reference)
333
+ plugin_cache = if cache?(reference)
334
+ cache = Bolt::Plugin::Cache.new(reference,
335
+ @config.project.plugin_cache_file,
336
+ @config.plugin_cache)
337
+ entry = cache.read_and_clean_cache
338
+ return entry unless entry.nil?
339
+
340
+ cache
341
+ end
342
+
343
+ plugin_name = reference['_plugin']
344
+ hook = get_hook(plugin_name, :resolve_reference)
345
+
346
+ begin
347
+ validate_proc = get_hook(plugin_name, :validate_resolve_reference)
348
+ rescue PluginError
349
+ validate_proc = proc { |*args| } # Nothing to do
350
+ end
351
+
352
+ validate_proc.call(reference)
353
+
354
+ result = begin
355
+ # Evaluate the plugin and then recursively evaluate any plugin returned by it.
356
+ hook.call(reference)
357
+ rescue StandardError => e
358
+ loc = "resolve_reference in #{plugin_name}"
359
+ raise PluginError::ExecutionError.new(e.message, plugin_name, loc)
360
+ end
361
+
362
+ plugin_cache.write_cache(result) if cache?(reference)
363
+
364
+ result
365
+ end
366
+ private :resolve_single_reference
367
+
368
+ private def cache?(reference)
369
+ reference.key?('_cache') || @config.plugin_cache.key?('ttl')
370
+ end
371
+
372
+ # Checks whether a given value is a _plugin reference
373
+ def reference?(input)
374
+ input.is_a?(Hash) && input.key?('_plugin')
375
+ end
376
+ end
377
+ end
378
+
379
+ # references PluginError
380
+ require_relative 'plugin/module'
@@ -0,0 +1,219 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'pathname'
4
+ require_relative '../bolt/config'
5
+ require_relative '../bolt/validator'
6
+ require_relative '../bolt/pal'
7
+ require_relative '../bolt/module'
8
+
9
+ module Bolt
10
+ class Project
11
+ BOLTDIR_NAME = 'Boltdir'
12
+ CONFIG_NAME = 'bolt-project.yaml'
13
+
14
+ attr_reader :path, :data, :inventory_file, :hiera_config,
15
+ :puppetfile, :rerunfile, :type, :resource_types, :project_file,
16
+ :downloads, :plans_path, :modulepath, :managed_moduledir,
17
+ :backup_dir, :plugin_cache_file, :plan_cache_file, :task_cache_file,
18
+ :manifests
19
+
20
+ def self.default_project
21
+ create_project(File.expand_path(File.join('~', '.puppetlabs', 'bolt')), 'user')
22
+ # If homedir isn't defined use the system config path
23
+ rescue ArgumentError
24
+ create_project(Bolt::Config.system_path, 'system')
25
+ end
26
+
27
+ # Search recursively up the directory hierarchy for the Project. Look for a
28
+ # directory called Boltdir or a file called bolt-project.yaml (for a control
29
+ # repo type Project). Otherwise, repeat the check on each directory up the
30
+ # hierarchy, falling back to the default if we reach the root.
31
+ def self.find_boltdir(dir)
32
+ dir = Pathname.new(dir)
33
+
34
+ if (dir + BOLTDIR_NAME).directory?
35
+ create_project(dir + BOLTDIR_NAME, 'embedded')
36
+ elsif (dir + CONFIG_NAME).file?
37
+ create_project(dir, 'local')
38
+ elsif dir.root?
39
+ default_project
40
+ else
41
+ Bolt::Logger.debug(
42
+ "Did not detect Boltdir or bolt-project.yaml at '#{dir}'. This directory won't be loaded as a project."
43
+ )
44
+ find_boltdir(dir.parent)
45
+ end
46
+ end
47
+
48
+ def self.create_project(path, type = 'option')
49
+ fullpath = Pathname.new(path).expand_path
50
+
51
+ if type == 'user'
52
+ begin
53
+ # This is already expanded if the type is user
54
+ FileUtils.mkdir_p(path)
55
+ rescue StandardError
56
+ Bolt::Logger.warn(
57
+ "non_writeable_project",
58
+ "Could not create default project at #{path}. Continuing without a writeable project. "\
59
+ "Log and rerun files will not be written."
60
+ )
61
+ end
62
+ end
63
+
64
+ if type == 'option' && !File.directory?(path)
65
+ raise Bolt::Error.new("Could not find project at #{path}", "bolt/project-error")
66
+ end
67
+
68
+ if !Bolt::Util.windows? && type != 'environment' && fullpath.world_writable?
69
+ raise Bolt::Error.new(
70
+ "Project directory '#{fullpath}' is world-writable which poses a security risk. Set "\
71
+ "BOLT_PROJECT='#{fullpath}' to force the use of this project directory.",
72
+ "bolt/world-writable-error"
73
+ )
74
+ end
75
+
76
+ project_file = File.join(fullpath, CONFIG_NAME)
77
+ data = Bolt::Util.read_optional_yaml_hash(File.expand_path(project_file), 'project')
78
+ default = type =~ /user|system/ ? 'default ' : ''
79
+
80
+ if File.exist?(File.expand_path(project_file))
81
+ Bolt::Logger.info("Loaded #{default}project from '#{fullpath}'")
82
+ end
83
+
84
+ Bolt::Validator.new.tap do |validator|
85
+ validator.validate(data, schema, project_file)
86
+ validator.warnings.each { |warning| Bolt::Logger.warn(warning[:id], warning[:msg]) }
87
+ validator.deprecations.each { |dep| Bolt::Logger.deprecate(dep[:id], dep[:msg]) }
88
+ end
89
+
90
+ new(data, path, type)
91
+ end
92
+
93
+ # Builds the schema for bolt-project.yaml used by the validator.
94
+ #
95
+ def self.schema
96
+ {
97
+ type: Hash,
98
+ properties: Bolt::Config::PROJECT_OPTIONS.map { |opt| [opt, _ref: opt] }.to_h,
99
+ definitions: Bolt::Config::OPTIONS
100
+ }
101
+ end
102
+
103
+ def initialize(data, path, type = 'option')
104
+ @type = type
105
+ @path = Pathname.new(path).expand_path
106
+ @project_file = @path + CONFIG_NAME
107
+ @inventory_file = @path + 'inventory.yaml'
108
+ @hiera_config = @path + 'hiera.yaml'
109
+ @puppetfile = @path + 'Puppetfile'
110
+ @rerunfile = @path + '.rerun.json'
111
+ @resource_types = @path + '.resource_types'
112
+ @downloads = @path + 'downloads'
113
+ @plans_path = @path + 'plans'
114
+ @managed_moduledir = @path + '.modules'
115
+ @backup_dir = @path + '.bolt-bak'
116
+ @plugin_cache_file = @path + '.plugin_cache.json'
117
+ @plan_cache_file = @path + '.plan_cache.json'
118
+ @task_cache_file = @path + '.task_cache.json'
119
+ @modulepath = [(@path + 'modules').to_s]
120
+ @manifests = @path + 'manifests'
121
+
122
+ if (tc = Bolt::Config::INVENTORY_OPTIONS.keys & data.keys).any?
123
+ Bolt::Logger.warn(
124
+ "project_transport_config",
125
+ "Transport configuration isn't supported in bolt-project.yaml. Ignoring keys #{tc}."
126
+ )
127
+ end
128
+
129
+ @data = data.slice(*Bolt::Config::PROJECT_OPTIONS)
130
+
131
+ if @data['rerunfile']
132
+ @rerunfile = File.expand_path(@data['rerunfile'], @path)
133
+ end
134
+
135
+ validate if project_file?
136
+ end
137
+
138
+ def to_s
139
+ @path.to_s
140
+ end
141
+
142
+ # This API is used to prepend the project as a module to Puppet's internal
143
+ # module_references list. CHANGE AT YOUR OWN RISK
144
+ def to_h
145
+ { path: @path.to_s,
146
+ name: name,
147
+ load_as_module?: load_as_module? }
148
+ end
149
+
150
+ def eql?(other)
151
+ path == other.path
152
+ end
153
+ alias == eql?
154
+
155
+ def project_file?
156
+ @project_file.file?
157
+ end
158
+
159
+ def load_as_module?
160
+ !name.nil?
161
+ end
162
+
163
+ def name
164
+ @data['name']
165
+ end
166
+
167
+ def tasks
168
+ @data['tasks']
169
+ end
170
+
171
+ def plans
172
+ @data['plans']
173
+ end
174
+
175
+ def plugin_cache
176
+ @data['plugin-cache']
177
+ end
178
+
179
+ def module_install
180
+ @data['module-install']
181
+ end
182
+
183
+ def disable_warnings
184
+ @data['disable-warnings'] || []
185
+ end
186
+
187
+ def modules
188
+ mod_data = @data['modules'] || []
189
+ @modules ||= mod_data.map do |mod|
190
+ if mod.is_a?(String)
191
+ { 'name' => mod }
192
+ else
193
+ mod
194
+ end
195
+ end
196
+ end
197
+
198
+ def validate
199
+ if name
200
+ if name !~ Bolt::Module::MODULE_NAME_REGEX
201
+ raise Bolt::ValidationError, <<~ERROR_STRING
202
+ Invalid project name '#{name}' in bolt-project.yaml; project name must begin with a lowercase letter
203
+ and can include lowercase letters, numbers, and underscores.
204
+ ERROR_STRING
205
+ elsif Dir.children(Bolt::Config::Modulepath::BOLTLIB_PATH).include?(name)
206
+ raise Bolt::ValidationError, "The project '#{name}' will not be loaded. The project name conflicts "\
207
+ "with a built-in Bolt module of the same name."
208
+ end
209
+ elsif name.nil? &&
210
+ (File.directory?(plans_path) ||
211
+ File.directory?(@path + 'tasks') ||
212
+ File.directory?(@path + 'files'))
213
+ message = "No project name is specified in bolt-project.yaml. Project-level content will not be available."
214
+
215
+ Bolt::Logger.warn("missing_project_name", message)
216
+ end
217
+ end
218
+ end
219
+ end
@@ -0,0 +1,113 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../../bolt/project_manager/migrator'
4
+
5
+ module Bolt
6
+ class ProjectManager
7
+ class ConfigMigrator < Migrator
8
+ def migrate(config_file, project_file, inventory_file, backup_dir)
9
+ bolt_yaml_to_bolt_project(config_file, project_file, inventory_file, backup_dir) &&
10
+ update_options(project_file)
11
+ end
12
+
13
+ private def bolt_yaml_to_bolt_project(config_file, project_file, inventory_file, backup_dir)
14
+ if File.exist?(project_file)
15
+ return true
16
+ end
17
+
18
+ unless File.exist?(config_file)
19
+ return true
20
+ end
21
+
22
+ @outputter.print_message "Migrating project configuration\n\n"
23
+
24
+ config_data = Bolt::Util.read_optional_yaml_hash(config_file, 'config')
25
+ transport_data, project_data = config_data.partition do |k, _|
26
+ Bolt::Config::INVENTORY_OPTIONS.keys.include?(k)
27
+ end.map(&:to_h)
28
+
29
+ if transport_data.any?
30
+ if File.exist?(inventory_file)
31
+ inventory_data = Bolt::Util.read_yaml_hash(inventory_file, 'inventory')
32
+ merged = Bolt::Util.deep_merge(transport_data, inventory_data['config'] || {})
33
+ inventory_data['config'] = merged
34
+ backup_file(inventory_file, backup_dir)
35
+ else
36
+ FileUtils.touch(inventory_file)
37
+ inventory_data = { 'config' => transport_data }
38
+ end
39
+
40
+ backup_file(config_file, backup_dir)
41
+
42
+ begin
43
+ @outputter.print_action_step(
44
+ "Moving transportation configuration options '#{transport_data.keys.join(', ')}' "\
45
+ "from bolt.yaml to inventory.yaml"
46
+ )
47
+
48
+ File.write(inventory_file, inventory_data.to_yaml)
49
+ File.write(config_file, project_data.to_yaml)
50
+ rescue StandardError => e
51
+ raise Bolt::FileError.new("#{e.message}; unable to write inventory.", inventory_file)
52
+ end
53
+ end
54
+
55
+ @outputter.print_action_step("Renaming bolt.yaml to bolt-project.yaml")
56
+ FileUtils.mv(config_file, project_file)
57
+
58
+ command = Bolt::Util.powershell? ? 'Get-Help about_bolt_project' : 'bolt guide project'
59
+ @outputter.print_action_step(
60
+ "Successfully migrated config. Please add a 'name' key to bolt-project.yaml "\
61
+ "to use project-level tasks and plans. Learn more about projects by running "\
62
+ "'#{command}'."
63
+ )
64
+
65
+ true
66
+ end
67
+
68
+ private def update_options(project_file)
69
+ return true unless File.exist?(project_file)
70
+
71
+ @outputter.print_message("Updating project configuration options\n\n")
72
+ data = Bolt::Util.read_yaml_hash(project_file, 'config')
73
+ modified = false
74
+
75
+ # Keys to update. The first element is the old key, while the second is
76
+ # the key update it to.
77
+ to_update = [
78
+ %w[apply_settings apply-settings],
79
+ %w[puppetfile module-install],
80
+ %w[plugin_hooks plugin-hooks]
81
+ ]
82
+
83
+ to_update.each do |old, new|
84
+ next unless data.key?(old)
85
+
86
+ if data.key?(new)
87
+ @outputter.print_action_step("Removing deprecated option '#{old}'")
88
+ data.delete(old)
89
+ else
90
+ @outputter.print_action_step("Updating deprecated option '#{old}' to '#{new}'")
91
+ data[new] = data.delete(old)
92
+ end
93
+
94
+ modified = true
95
+ end
96
+
97
+ if modified
98
+ begin
99
+ File.write(project_file, data.to_yaml)
100
+ rescue StandardError => e
101
+ raise Bolt::FileError.new("#{e.message}; unable to write config.", project_file)
102
+ end
103
+
104
+ @outputter.print_action_step("Successfully updated project configuration #{project_file}")
105
+ else
106
+ @outputter.print_action_step("Project configuration is up to date, nothing to do.")
107
+ end
108
+
109
+ true
110
+ end
111
+ end
112
+ end
113
+ end