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,276 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../../bolt/task/run'
4
+
5
+ module Bolt
6
+ class Plugin
7
+ class Module
8
+ class InvalidPluginData < Bolt::Plugin::PluginError
9
+ def initialize(msg, plugin)
10
+ msg = "Invalid Plugin Data for #{plugin}: #{msg}"
11
+ super(msg, 'bolt/invalid-plugin-data')
12
+ end
13
+ end
14
+
15
+ # mod should not be nil
16
+ def self.load(mod, opts)
17
+ if mod.plugin?
18
+ opts[:mod] = mod
19
+ plugin = Bolt::Plugin::Module.new(**opts)
20
+ plugin.setup
21
+ plugin
22
+ else
23
+ raise PluginError::Unknown, mod.name
24
+ end
25
+ end
26
+
27
+ attr_reader :config, :hook_map
28
+
29
+ def initialize(mod:, context:, config:, **_opts)
30
+ @module = mod
31
+ @config = config
32
+ @context = context
33
+ end
34
+
35
+ # This method interacts with the module on disk so it's separate from initialize
36
+ def setup
37
+ @data = load_data
38
+ @hook_map = find_hooks(@data['hooks'] || {})
39
+
40
+ if @data['config']
41
+ msg = <<~MSG.chomp
42
+ Found unsupported key 'config' in bolt_plugin.json. Config for a plugin is inferred
43
+ from task parameters, with config values passed as parameters.
44
+ MSG
45
+ raise InvalidPluginData.new(msg, name)
46
+ end
47
+
48
+ # Validate againsts the intersection of all task schemas.
49
+ @config_schema = process_schema(extract_task_parameter_schema)
50
+
51
+ validate_config(@config, @config_schema)
52
+ end
53
+
54
+ def name
55
+ @module.name
56
+ end
57
+
58
+ def hooks
59
+ (@hook_map.keys + [:validate_resolve_reference]).uniq
60
+ end
61
+
62
+ def load_data
63
+ JSON.parse(File.read(@module.plugin_data_file))
64
+ rescue JSON::ParserError => e
65
+ raise InvalidPluginData.new(e.message, name)
66
+ end
67
+
68
+ def process_schema(schema)
69
+ raise InvalidPluginData.new('config specification is not an object', name) unless schema.is_a?(Hash)
70
+ schema.each do |key, val|
71
+ unless key =~ /\A[a-z][a-z0-9_]*\z/
72
+ raise InvalidPluginData.new("config specification key, '#{key}', is not allowed", name)
73
+ end
74
+
75
+ unless val.is_a?(Hash) && (val['type'] || '').is_a?(String)
76
+ raise InvalidPluginData.new("config specification #{val.to_json} is not allowed", name)
77
+ end
78
+
79
+ type_string = val['type'] || 'Any'
80
+ begin
81
+ val['pcore_type'] = Puppet::Pops::Types::TypeParser.singleton.parse(type_string)
82
+ if val['pcore_type'].is_a? Puppet::Pops::Types::PTypeReferenceType
83
+ raise InvalidPluginData.new("Could not find type '#{type_string}' for #{key}", name)
84
+ end
85
+ rescue Puppet::ParseError
86
+ raise InvalidPluginData.new("Could not parse type '#{type_string}' for #{key}", name)
87
+ end
88
+ end
89
+
90
+ schema
91
+ end
92
+
93
+ def validate_config(config, config_schema)
94
+ config.each_key do |key|
95
+ msg = "Config for #{name} plugin contains unexpected key #{key}"
96
+ raise Bolt::ValidationError, msg unless config_schema.include?(key)
97
+ end
98
+
99
+ config_schema.each do |key, spec|
100
+ val = config[key]
101
+
102
+ unless spec['pcore_type'].instance?(val)
103
+ raise Bolt::ValidationError, "#{name} plugin expects a #{spec['type']} for key #{key}, got: #{val}"
104
+ end
105
+ val.nil?
106
+ end
107
+ nil
108
+ end
109
+
110
+ def find_hooks(hook_data)
111
+ raise InvalidPluginData.new("'hooks' must be a hash", name) unless hook_data.is_a?(Hash)
112
+
113
+ hooks = {}
114
+ # Load hooks specified in the config
115
+ hook_data.each do |hook_name, hook_spec|
116
+ unless hook_spec.is_a?(Hash) && hook_spec['task'].is_a?(String)
117
+ msg = "Unexpected hook specification #{hook_spec.to_json} in #{@name} for hook #{hook_name}"
118
+ raise InvalidPluginData.new(msg, name)
119
+ end
120
+
121
+ begin
122
+ task = @context.get_validated_task(hook_spec['task'])
123
+ rescue Bolt::Error => e
124
+ msg = if e.kind == 'bolt/unknown-task'
125
+ "Plugin #{name} specified an unkown task '#{hook_spec['task']}' for a hook"
126
+ else
127
+ "Plugin #{name} could not load task '#{hook_spec['task']}': #{e.message}"
128
+ end
129
+ raise InvalidPluginData.new(msg, name)
130
+ end
131
+
132
+ hooks[hook_name.to_sym] = { 'task' => task }
133
+ end
134
+
135
+ # Check for tasks for any hooks not already defined
136
+ (Set.new(KNOWN_HOOKS.map) - hooks.keys).each do |hook_name|
137
+ task_name = "#{name}::#{hook_name}"
138
+ begin
139
+ task = @context.get_validated_task(task_name)
140
+ rescue Bolt::Error => e
141
+ raise e unless e.kind == 'bolt/unknown-task'
142
+ end
143
+ hooks[hook_name] = { 'task' => task } if task
144
+ end
145
+
146
+ Bolt::Util.symbolize_top_level_keys(hooks)
147
+ end
148
+
149
+ def validate_params(task, params)
150
+ @context.validate_params(task.name, params)
151
+ end
152
+
153
+ def process_params(task, opts)
154
+ # opts are passed directly from inventory but all of the _ options are
155
+ # handled previously. That may not always be the case so filter them
156
+ # out now.
157
+ meta, params = opts.partition { |key, _val| key.start_with?('_') }.map(&:to_h)
158
+
159
+ # Reject parameters from config that are not accepted by the task and
160
+ # merge in parameter defaults
161
+ params = if task.parameters
162
+ task.parameter_defaults
163
+ .merge(config.slice(*task.parameters.keys))
164
+ .merge(params)
165
+ else
166
+ config.merge(params)
167
+ end
168
+
169
+ validate_params(task, params)
170
+
171
+ meta['_boltdir'] = @context.boltdir.to_s
172
+
173
+ [params, meta]
174
+ end
175
+
176
+ def extract_task_parameter_schema
177
+ # Get the intersection of expected types (using Set)
178
+ type_set = @hook_map.each_with_object({}) do |(_hook, task), acc|
179
+ next unless (schema = task['task'].metadata['parameters'])
180
+ schema.each do |param, scheme|
181
+ next unless scheme['type'].is_a?(String)
182
+ scheme['type'] = Set.new([scheme['type']])
183
+ if acc.dig(param, 'type').is_a?(Set)
184
+ scheme['type'].merge(acc[param]['type'])
185
+ end
186
+ end
187
+ acc.merge!(schema)
188
+ end
189
+ # Convert Set to string
190
+ type_set.each do |_param, schema|
191
+ next unless schema['type']
192
+ schema['type'] = if schema['type'].size > 1
193
+ "Optional[Variant[#{schema['type'].to_a.join(', ')}]]"
194
+ else
195
+ "Optional[#{schema['type'].to_a.first}]"
196
+ end
197
+ end
198
+ end
199
+
200
+ def run_task(task, opts)
201
+ opts = opts.reject { |key, _val| key.start_with?('_') }
202
+ params, metaparams = process_params(task, opts)
203
+ params = params.merge(metaparams)
204
+
205
+ # There are no executor options to pass now.
206
+ options = { catch_errors: true }
207
+
208
+ result = @context.run_local_task(task,
209
+ params,
210
+ options).first
211
+
212
+ raise Bolt::Error.new(result.error_hash['msg'], result.error_hash['kind']) unless result.ok
213
+ result.value
214
+ end
215
+
216
+ def run_hook(hook_name, opts, value = true)
217
+ hook = @hook_map[hook_name]
218
+ # This shouldn't happen if the Plugin api is used
219
+ raise PluginError::UnsupportedHook.new(name, hook_name) unless hook
220
+ result = run_task(hook['task'], opts)
221
+
222
+ if value
223
+ unless result.include?('value')
224
+ msg = "Plugin #{name} result did not include a value, got #{result}"
225
+ raise Bolt::Plugin::PluginError::ExecutionError.new(msg, name, hook_name)
226
+ end
227
+
228
+ result['value']
229
+ end
230
+ end
231
+
232
+ def validate_resolve_reference(opts)
233
+ task = @hook_map[:resolve_reference]['task']
234
+ params, _metaparams = process_params(task, opts)
235
+
236
+ if task
237
+ validate_params(task, params)
238
+ end
239
+
240
+ if @hook_map.include?(:validate_resolve_reference)
241
+ run_hook(:validate_resolve_reference, opts, false)
242
+ end
243
+ end
244
+
245
+ # These are all the same but are defined explicitly for clarity
246
+ def resolve_reference(opts)
247
+ run_hook(__method__, opts)
248
+ end
249
+
250
+ def secret_encrypt(opts)
251
+ run_hook(__method__, opts)
252
+ end
253
+
254
+ def secret_decrypt(opts)
255
+ run_hook(__method__, opts)
256
+ end
257
+
258
+ def secret_createkeys(opts = {})
259
+ run_hook(__method__, opts)
260
+ end
261
+
262
+ def puppet_library(opts, target, apply_prep)
263
+ task = @hook_map[:puppet_library]['task']
264
+
265
+ params, meta_params = process_params(task, opts)
266
+
267
+ options = {}
268
+ options[:run_as] = meta_params['_run_as'] if meta_params['_run_as']
269
+
270
+ proc do
271
+ apply_prep.run_task([target], task, params, options).first
272
+ end
273
+ end
274
+ end
275
+ end
276
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bolt
4
+ class Plugin
5
+ class Prompt
6
+ def initialize(*_args); end
7
+
8
+ def name
9
+ 'prompt'
10
+ end
11
+
12
+ def hooks
13
+ hook_descriptions.keys
14
+ end
15
+
16
+ def hook_descriptions
17
+ {
18
+ resolve_reference: 'Prompt the user for a sensitive value.',
19
+ validate_resolve_reference: nil
20
+ }
21
+ end
22
+
23
+ def validate_resolve_reference(opts)
24
+ raise Bolt::ValidationError, "Prompt requires a 'message'" unless opts['message']
25
+ end
26
+
27
+ def resolve_reference(opts)
28
+ $stderr.print("#{opts['message']}: ")
29
+ value = $stdin.noecho(&:gets).to_s.chomp
30
+ $stderr.puts
31
+
32
+ value
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,84 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bolt
4
+ class Plugin
5
+ class PuppetConnectData
6
+ INPUT_DATA_VAR = 'PUPPET_CONNECT_INPUT_DATA'
7
+
8
+ def initialize(context:, **_opts)
9
+ if ENV.key?(INPUT_DATA_VAR)
10
+ # The user provided input data that they will copy-paste into the Puppet Connect UI
11
+ # for inventory syncing. This environment variable will likely be set when invoking a
12
+ # general "test Puppet Connect input data" command. That command tests that parsing
13
+ # the inventory with the given input data results in connectable targets. Part of
14
+ # that requires validating that the input data contains all of the referenced keys,
15
+ # which is what this plugin will do in validate_resolve_reference.
16
+ @input_data_path = ENV[INPUT_DATA_VAR]
17
+ data_path = @input_data_path
18
+ else
19
+ # The user is using this plugin during a regular Bolt invocation, so fetch the (minimal)
20
+ # required data from the default location. This data should typically be non-autoloadable
21
+ # secrets like WinRM passwords.
22
+ #
23
+ # Note that any unspecified keys will be resolved to nil.
24
+ data_path = File.join(context.boltdir, 'puppet_connect_data.yaml')
25
+ end
26
+
27
+ @data = Bolt::Util.read_optional_yaml_hash(
28
+ data_path,
29
+ File.basename(data_path)
30
+ )
31
+
32
+ if @input_data_path
33
+ # Validate that the data does not contain any plugin-reference
34
+ # values
35
+ @data.each do |key, toplevel_value|
36
+ # Use walk_vals to check for nested plugin references
37
+ Bolt::Util.walk_vals(toplevel_value) do |current_value|
38
+ if current_value.is_a?(Hash) && current_value.key?('_plugin')
39
+ raise invalid_input_data_err("the #{key} key's value contains a plugin reference")
40
+ end
41
+ current_value
42
+ end
43
+ end
44
+ end
45
+ end
46
+
47
+ def name
48
+ 'puppet_connect_data'
49
+ end
50
+
51
+ def hooks
52
+ hook_descriptions.keys
53
+ end
54
+
55
+ def hook_descriptions
56
+ {
57
+ resolve_reference: nil,
58
+ validate_resolve_reference: nil
59
+ }
60
+ end
61
+
62
+ def resolve_reference(opts)
63
+ key = opts['key']
64
+ @data[key]
65
+ end
66
+
67
+ def validate_resolve_reference(opts)
68
+ unless opts['key']
69
+ raise Bolt::ValidationError,
70
+ "puppet_connect_data plugin requires that 'key' be specified"
71
+ end
72
+ if @input_data_path && !@data.key?(opts['key'])
73
+ # Input data for Puppet Connect was provided and opts['key'] does not have a
74
+ # value specified. Raise an error for this case.
75
+ raise invalid_input_data_err("a value for the #{opts['key']} key is not specified")
76
+ end
77
+ end
78
+
79
+ def invalid_input_data_err(msg)
80
+ Bolt::ValidationError.new("invalid input data #{@input_data_path}: #{msg}")
81
+ end
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,124 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bolt
4
+ class Plugin
5
+ class Puppetdb
6
+ class FactLookupError < Bolt::Error
7
+ def initialize(fact, err = nil)
8
+ m = String.new("Fact lookup '#{fact}' contains an invalid factname")
9
+ m << ": #{err}" unless err.nil?
10
+ super(m, 'bolt.plugin/fact-lookup-error')
11
+ end
12
+ end
13
+
14
+ TEMPLATE_OPTS = %w[alias config facts features name uri vars].freeze
15
+ PLUGIN_OPTS = %w[_plugin _cache query target_mapping instance].freeze
16
+
17
+ attr_reader :puppetdb_client
18
+
19
+ def initialize(config:, context:)
20
+ @puppetdb_client = Bolt::PuppetDB::Client.new(default: config.delete('default'),
21
+ instances: config.delete('instances') || {},
22
+ config: config,
23
+ project: context.boltdir)
24
+
25
+ @logger = Bolt::Logger.logger(self)
26
+ end
27
+
28
+ def name
29
+ 'puppetdb'
30
+ end
31
+
32
+ def hooks
33
+ hook_descriptions.keys
34
+ end
35
+
36
+ def hook_descriptions
37
+ {
38
+ resolve_reference: 'Query PuppetDB for a group of targets.'
39
+ }
40
+ end
41
+
42
+ def warn_missing_fact(certname, fact)
43
+ Bolt::Logger.warn("puppetdb_missing_fact", "Could not find fact #{fact} for node #{certname}")
44
+ end
45
+
46
+ def fact_path(raw_fact)
47
+ fact_path = raw_fact.split(".")
48
+ fact_path = fact_path.map do |segment|
49
+ # Turn it into an integer if we can
50
+ Integer(segment)
51
+ rescue ArgumentError
52
+ # Otherwise return the value
53
+ segment
54
+ end
55
+ if fact_path[0] == 'facts'
56
+ fact_path.drop(1)
57
+ elsif fact_path == ['certname']
58
+ fact_path
59
+ else
60
+ raise FactLookupError.new(raw_fact, "fact lookups must start with 'facts.'")
61
+ end
62
+ end
63
+
64
+ def resolve_reference(opts)
65
+ targets = @puppetdb_client.query_certnames(opts['query'], opts['instance'])
66
+ facts = []
67
+
68
+ template = opts.delete('target_mapping') || {}
69
+
70
+ keys = Set.new(TEMPLATE_OPTS) & opts.keys
71
+ unless keys.empty?
72
+ raise Bolt::ValidationError, "PuppetDB plugin expects keys #{keys.to_a} to be set under 'target_mapping'"
73
+ end
74
+
75
+ keys = Set.new(opts.keys) - PLUGIN_OPTS
76
+ unless keys.empty?
77
+ raise Bolt::ValidationError, "Unknown keys in PuppetDB plugin: #{keys.to_a}"
78
+ end
79
+
80
+ Bolt::Util.walk_vals(template) do |value|
81
+ # This is done in parts instead of in place so that we only need to
82
+ # make one puppetDB query. Don't gather certname since we already
83
+ # have that and it's not a fact.
84
+ if value.is_a?(String) && value != 'certname'
85
+ facts << fact_path(value)
86
+ end
87
+ value
88
+ end
89
+
90
+ facts.uniq!
91
+ # Returns {'mycertname' => [{'path' => ['nested', 'fact'], 'value' => val'}], ... }
92
+ fact_values = @puppetdb_client.fact_values(targets, facts, opts['instance'])
93
+
94
+ targets.map do |certname|
95
+ target_data = fact_values[certname]
96
+ target = resolve_facts(template, certname, target_data) || {}
97
+ target['uri'] = certname unless target['uri'] || target['name']
98
+
99
+ target
100
+ end
101
+ end
102
+
103
+ def resolve_facts(config, certname, target_data)
104
+ Bolt::Util.walk_vals(config) do |value|
105
+ case value
106
+ when String
107
+ if value == 'certname'
108
+ certname
109
+ else
110
+ data = target_data&.detect { |d| d['path'] == fact_path(value) }
111
+ warn_missing_fact(certname, value) if data.nil?
112
+ # If there's no fact data this will be nil
113
+ data&.fetch('value', nil)
114
+ end
115
+ when Array, Hash
116
+ value
117
+ else
118
+ raise FactLookupError.new(value, "fact lookups must be a string")
119
+ end
120
+ end
121
+ end
122
+ end
123
+ end
124
+ end
@@ -0,0 +1,72 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bolt
4
+ class Plugin
5
+ class Task
6
+ def hooks
7
+ hook_descriptions.keys
8
+ end
9
+
10
+ def hook_descriptions
11
+ {
12
+ puppet_library: 'Run a task to install the Puppet agent package.',
13
+ resolve_reference: 'Run a task as a plugin.',
14
+ validate_resolve_reference: nil
15
+ }
16
+ end
17
+
18
+ def name
19
+ 'task'
20
+ end
21
+
22
+ attr_accessor :pal, :executor, :inventory
23
+
24
+ def initialize(context:, **_opts)
25
+ @context = context
26
+ end
27
+
28
+ def run_task(opts)
29
+ params = opts['parameters'] || {}
30
+ options = { catch_errors: true }
31
+
32
+ raise Bolt::ValidationError, "Task plugin requires that the 'task' is specified" unless opts['task']
33
+ task = @context.get_validated_task(opts['task'], params)
34
+
35
+ result = @context.run_local_task(task, params, options).first
36
+
37
+ raise Bolt::Error.new(result.error_hash['msg'], result.error_hash['kind']) if result.error_hash
38
+ result
39
+ end
40
+
41
+ def validate_options(opts)
42
+ raise Bolt::ValidationError, "Task plugin requires that the 'task' is specified" unless opts['task']
43
+ @context.get_validated_task(opts['task'], opts['parameters'] || {})
44
+ end
45
+ alias validate_resolve_reference validate_options
46
+
47
+ def resolve_reference(opts)
48
+ result = run_task(opts)
49
+
50
+ unless result.value.include?('value')
51
+ raise Bolt::ValidationError, "Task result did not return 'value': #{result.value}"
52
+ end
53
+
54
+ result['value']
55
+ end
56
+
57
+ def puppet_library(opts, target, apply_prep)
58
+ params = opts['parameters'] || {}
59
+ run_opts = {}
60
+ run_opts[:run_as] = opts['_run_as'] if opts['_run_as']
61
+ begin
62
+ task = @context.get_validated_task(opts['task'], params)
63
+ rescue Bolt::Error => e
64
+ raise Bolt::Plugin::PluginError::ExecutionError.new(e.message, name, 'puppet_library')
65
+ end
66
+ proc do
67
+ apply_prep.run_task([target], task, params, run_opts).first
68
+ end
69
+ end
70
+ end
71
+ end
72
+ end