Class: Bolt::Plugin
- Inherits:
-
Object
- Object
- Bolt::Plugin
- Defined in:
- lib/bolt/plugin.rb,
lib/bolt/plugin/task.rb,
lib/bolt/plugin/cache.rb,
lib/bolt/plugin/module.rb,
lib/bolt/plugin/prompt.rb,
lib/bolt/plugin/env_var.rb,
lib/bolt/plugin/puppetdb.rb,
lib/bolt/plugin/puppet_connect_data.rb
Defined Under Namespace
Classes: Cache, EnvVar, Module, PluginContext, PluginError, Prompt, PuppetConnectData, Puppetdb, Task
Constant Summary collapse
- KNOWN_HOOKS =
%i[ puppet_library resolve_reference secret_encrypt secret_decrypt secret_createkeys validate_resolve_reference ].freeze
- RUBY_PLUGINS =
%w[task prompt env_var puppetdb puppet_connect_data].freeze
- BUILTIN_PLUGINS =
%w[task terraform pkcs7 prompt vault aws_inventory puppetdb azure_inventory yaml env_var gcloud_inventory].freeze
- DEFAULT_PLUGIN_HOOKS =
{ 'puppet_library' => { 'plugin' => 'puppet_agent', 'stop_service' => true } }.freeze
Instance Attribute Summary collapse
-
#pal ⇒ Object
readonly
Returns the value of attribute pal.
-
#plugin_context ⇒ Object
readonly
Returns the value of attribute plugin_context.
-
#plugin_hooks ⇒ Hash[String, Hash]
Returns a map of configured plugin hooks.
Instance Method Summary collapse
- #add_module_plugin(plugin_name) ⇒ Object
- #add_plugin(plugin) ⇒ Object
- #add_ruby_plugin(plugin_name) ⇒ Object
-
#by_name(plugin_name) ⇒ Object
Calling by_name or get_hook will load any module based plugin automatically.
- #config_for_plugin(plugin_name) ⇒ Object
- #get_hook(plugin_name, hook) ⇒ Object
-
#initialize(config, pal, analytics = Bolt::Analytics::NoopClient.new, load_plugins: true) ⇒ Plugin
constructor
A new instance of Plugin.
- #known_plugin?(plugin_name) ⇒ Boolean
-
#list_plugins ⇒ Object
Loads all plugins and returns a map of plugin names to hooks.
- #modules ⇒ Object
- #puppetdb_client ⇒ Object
-
#reference?(input) ⇒ Boolean
Checks whether a given value is a _plugin reference.
-
#resolve_references(data) ⇒ Object
Evaluate all _plugin references in a data structure.
-
#resolve_top_level_references(data) ⇒ Object
Iteratively resolves “top-level” references until the result no longer has top-level references.
Constructor Details
#initialize(config, pal, analytics = Bolt::Analytics::NoopClient.new, load_plugins: true) ⇒ Plugin
Returns a new instance of Plugin.
138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 |
# File 'lib/bolt/plugin.rb', line 138 def initialize(config, pal, analytics = Bolt::Analytics::NoopClient.new, load_plugins: true) @config = config @analytics = analytics @plugin_context = PluginContext.new(config, pal, self) @plugins = {} @pal = pal @load_plugins = load_plugins @unknown = Set.new @resolution_stack = [] @unresolved_plugin_configs = config.plugins.dup # The puppetdb plugin config comes from the puppetdb section, not from # the plugins section if @unresolved_plugin_configs.key?('puppetdb') msg = "Configuration for the PuppetDB plugin must be in the 'puppetdb' config section, not 'plugins'" raise Bolt::Error.new(msg, 'bolt/plugin-error') end @unresolved_plugin_configs['puppetdb'] = config.puppetdb if config.puppetdb end |
Instance Attribute Details
#pal ⇒ Object (readonly)
Returns the value of attribute pal.
135 136 137 |
# File 'lib/bolt/plugin.rb', line 135 def pal @pal end |
#plugin_context ⇒ Object (readonly)
Returns the value of attribute plugin_context.
135 136 137 |
# File 'lib/bolt/plugin.rb', line 135 def plugin_context @plugin_context end |
#plugin_hooks ⇒ Hash[String, Hash]
Returns a map of configured plugin hooks. Any unresolved plugin references are resolved.
162 163 164 |
# File 'lib/bolt/plugin.rb', line 162 def plugin_hooks @plugin_hooks ||= DEFAULT_PLUGIN_HOOKS.merge(resolve_references(@config.plugin_hooks)) end |
Instance Method Details
#add_module_plugin(plugin_name) ⇒ Object
188 189 190 191 192 193 194 195 196 197 198 199 200 201 |
# File 'lib/bolt/plugin.rb', line 188 def add_module_plugin(plugin_name) opts = { context: @plugin_context, # Make sure that the plugin's config is validated _before_ the unknown-plugin # and loading-disabled checks. This way, we can fail early on invalid plugin # config instead of _after_ loading the modulepath (which can be expensive). config: config_for_plugin(plugin_name) } mod = modules[plugin_name] plugin = Bolt::Plugin::Module.load(mod, opts) add_plugin(plugin) end |
#add_plugin(plugin) ⇒ Object
170 171 172 |
# File 'lib/bolt/plugin.rb', line 170 def add_plugin(plugin) @plugins[plugin.name] = plugin end |
#add_ruby_plugin(plugin_name) ⇒ Object
174 175 176 177 178 179 180 181 182 183 184 185 186 |
# File 'lib/bolt/plugin.rb', line 174 def add_ruby_plugin(plugin_name) cls_name = Bolt::Util.snake_name_to_class_name(plugin_name) filename = "bolt/plugin/#{plugin_name}" require filename cls = Kernel.const_get("Bolt::Plugin::#{cls_name}") opts = { context: @plugin_context, config: config_for_plugin(plugin_name) } plugin = cls.new(**opts) add_plugin(plugin) end |
#by_name(plugin_name) ⇒ Object
Calling by_name or get_hook will load any module based plugin automatically
233 234 235 236 237 238 239 240 241 242 243 244 245 |
# File 'lib/bolt/plugin.rb', line 233 def by_name(plugin_name) if known_plugin?(plugin_name) if @plugins.include?(plugin_name) @plugins[plugin_name] elsif !@load_plugins raise PluginError::LoadingDisabled, plugin_name elsif RUBY_PLUGINS.include?(plugin_name) add_ruby_plugin(plugin_name) else add_module_plugin(plugin_name) end end end |
#config_for_plugin(plugin_name) ⇒ Object
203 204 205 206 207 208 209 210 211 212 213 214 215 |
# File 'lib/bolt/plugin.rb', line 203 def config_for_plugin(plugin_name) return {} unless @unresolved_plugin_configs.include?(plugin_name) if @resolution_stack.include?(plugin_name) msg = "Configuration for plugin '#{plugin_name}' depends on the plugin itself" raise PluginError.new(msg, 'bolt/plugin-error') else @resolution_stack.push(plugin_name) config = resolve_references(@unresolved_plugin_configs[plugin_name]) @unresolved_plugin_configs.delete(plugin_name) @resolution_stack.pop config end end |
#get_hook(plugin_name, hook) ⇒ Object
223 224 225 226 227 228 229 230 |
# File 'lib/bolt/plugin.rb', line 223 def get_hook(plugin_name, hook) plugin = by_name(plugin_name) raise PluginError::Unknown, plugin_name unless plugin raise PluginError::UnsupportedHook.new(plugin_name, hook) unless plugin.hooks.include?(hook) @analytics.report_bundled_content("Plugin #{hook}", plugin_name) plugin.method(hook) end |
#known_plugin?(plugin_name) ⇒ Boolean
217 218 219 220 221 |
# File 'lib/bolt/plugin.rb', line 217 def known_plugin?(plugin_name) @plugins.include?(plugin_name) || RUBY_PLUGINS.include?(plugin_name) || (modules.include?(plugin_name) && modules[plugin_name].plugin?) end |
#list_plugins ⇒ Object
Loads all plugins and returns a map of plugin names to hooks.
249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 |
# File 'lib/bolt/plugin.rb', line 249 def list_plugins load_all_plugins hooks = KNOWN_HOOKS.map { |hook| [hook, {}] }.to_h @plugins.sort.each do |name, plugin| # Don't show the Puppet Connect plugin for now. next if name == 'puppet_connect_data' case plugin when Bolt::Plugin::Module plugin.hook_map.each do |hook, spec| next unless hooks.include?(hook) hooks[hook][name] = spec['task'].description end else plugin.hook_descriptions.each do |hook, description| hooks[hook][name] = description end end end hooks end |
#modules ⇒ Object
166 167 168 |
# File 'lib/bolt/plugin.rb', line 166 def modules @modules ||= Bolt::Module.discover(@pal.full_modulepath, @config.project) end |
#puppetdb_client ⇒ Object
285 286 287 |
# File 'lib/bolt/plugin.rb', line 285 def puppetdb_client by_name('puppetdb').puppetdb_client end |
#reference?(input) ⇒ Boolean
Checks whether a given value is a _plugin reference
372 373 374 |
# File 'lib/bolt/plugin.rb', line 372 def reference?(input) input.is_a?(Hash) && input.key?('_plugin') end |
#resolve_references(data) ⇒ Object
Evaluate all _plugin references in a data structure. Leaves are evaluated and then their parents are evaluated with references replaced by their values. If the result of a reference contains more references, they are resolved again before continuing to ascend the tree. The final result will not contain any references.
294 295 296 297 298 299 300 301 |
# File 'lib/bolt/plugin.rb', line 294 def resolve_references(data) Bolt::Util.postwalk_vals(data) do |value| reference?(value) ? resolve_references(resolve_single_reference(value)) : value end rescue SystemStackError raise Bolt::Error.new("Stack depth exceeded while recursively resolving references.", "bolt/recursive-reference-loop") end |
#resolve_top_level_references(data) ⇒ Object
Iteratively resolves “top-level” references until the result no longer has top-level references. A top-level reference is one which is not contained within another hash. It may be either the actual top-level result or arbitrarily nested within arrays. If parameters of the reference are themselves references, they will be looked. Any remaining references nested inside the result will not be evaluated once the top-level result is not a reference. This is used to resolve the ‘targets` and `groups` keys which are allowed to be references or arrays of references, but which may return data with nested references that should be resolved lazily. The end result will either be a single hash or a flat array of hashes.
314 315 316 317 318 319 320 321 322 323 324 325 326 327 |
# File 'lib/bolt/plugin.rb', line 314 def resolve_top_level_references(data) if data.is_a?(Array) data.flat_map { |elem| resolve_top_level_references(elem) } elsif reference?(data) partially_resolved = data.transform_values do |v| resolve_references(v) end fully_resolved = resolve_single_reference(partially_resolved) # The top-level reference may have returned more references, so repeat the process resolve_top_level_references(fully_resolved) else data end end |