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 156 |
# 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.merge('default' => config.default_puppetdb, 'instances' => config.puppetdb_instances) 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.
163 164 165 |
# File 'lib/bolt/plugin.rb', line 163 def plugin_hooks @plugin_hooks ||= DEFAULT_PLUGIN_HOOKS.merge(resolve_references(@config.plugin_hooks)) end |
Instance Method Details
#add_module_plugin(plugin_name) ⇒ Object
189 190 191 192 193 194 195 196 197 198 199 200 201 202 |
# File 'lib/bolt/plugin.rb', line 189 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
171 172 173 |
# File 'lib/bolt/plugin.rb', line 171 def add_plugin(plugin) @plugins[plugin.name] = plugin end |
#add_ruby_plugin(plugin_name) ⇒ Object
175 176 177 178 179 180 181 182 183 184 185 186 187 |
# File 'lib/bolt/plugin.rb', line 175 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
234 235 236 237 238 239 240 241 242 243 244 245 246 |
# File 'lib/bolt/plugin.rb', line 234 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
204 205 206 207 208 209 210 211 212 213 214 215 216 |
# File 'lib/bolt/plugin.rb', line 204 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
224 225 226 227 228 229 230 231 |
# File 'lib/bolt/plugin.rb', line 224 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
218 219 220 221 222 |
# File 'lib/bolt/plugin.rb', line 218 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.
250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 |
# File 'lib/bolt/plugin.rb', line 250 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
167 168 169 |
# File 'lib/bolt/plugin.rb', line 167 def modules @modules ||= Bolt::Module.discover(@pal.full_modulepath, @config.project) end |
#puppetdb_client ⇒ Object
286 287 288 |
# File 'lib/bolt/plugin.rb', line 286 def puppetdb_client by_name('puppetdb').puppetdb_client end |
#reference?(input) ⇒ Boolean
Checks whether a given value is a _plugin reference
373 374 375 |
# File 'lib/bolt/plugin.rb', line 373 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.
295 296 297 298 299 300 301 302 |
# File 'lib/bolt/plugin.rb', line 295 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.
315 316 317 318 319 320 321 322 323 324 325 326 327 328 |
# File 'lib/bolt/plugin.rb', line 315 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 |