bolt 1.30.1 → 1.31.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of bolt might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/bolt-modules/boltlib/lib/puppet/functions/run_plan.rb +1 -3
- data/bolt-modules/boltlib/lib/puppet/functions/run_task.rb +1 -1
- data/lib/bolt/bolt_option_parser.rb +6 -1
- data/lib/bolt/cli.rb +1 -1
- data/lib/bolt/error.rb +6 -2
- data/lib/bolt/inventory/group2.rb +5 -5
- data/lib/bolt/module.rb +39 -0
- data/lib/bolt/pal.rb +20 -6
- data/lib/bolt/plugin.rb +167 -21
- data/lib/bolt/plugin/aws_inventory.rb +102 -0
- data/lib/bolt/plugin/install_agent.rb +3 -1
- data/lib/bolt/plugin/module.rb +238 -0
- data/lib/bolt/plugin/pkcs7.rb +11 -7
- data/lib/bolt/plugin/prompt.rb +4 -7
- data/lib/bolt/plugin/puppetdb.rb +2 -2
- data/lib/bolt/plugin/task.rb +16 -57
- data/lib/bolt/plugin/terraform.rb +3 -3
- data/lib/bolt/plugin/vault.rb +3 -3
- data/lib/bolt/secret.rb +4 -3
- data/lib/bolt/secret/base.rb +11 -7
- data/lib/bolt/task/run.rb +55 -0
- data/lib/bolt/transport/orch/connection.rb +3 -1
- data/lib/bolt/transport/winrm.rb +4 -0
- data/lib/bolt/transport/winrm/connection.rb +2 -1
- data/lib/bolt/util.rb +6 -0
- data/lib/bolt/version.rb +1 -1
- data/lib/bolt_server/file_cache.rb +10 -6
- data/lib/bolt_server/schemas/action-run_script.json +44 -0
- data/lib/bolt_server/transport_app.rb +25 -2
- metadata +7 -3
- data/lib/bolt/plugin/aws.rb +0 -103
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e2159fe8eb6cf9a69b3ff093bf61d45f00ac4dbdfab37c412165da8c642c0909
|
4
|
+
data.tar.gz: 34e56147d83f6894dbc6baa73ad571213d1964c86af61bfff9c4b8a75a248923
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 05b79d3c523be35edb9230222301ab4328fe3907a3ab1199d4f6dd3e2037b30182dbfb08436079e728f92d9beabf8890911acfdca99551755cc5dd45062dccf0
|
7
|
+
data.tar.gz: 3fd5a1a1a54b338ca99550f3f83720a50eba7a61195fff12fedffc336fd84533f23d836d1d6efca083cbba27b60f223f6c2cb2536a71515acb8569aa223beaf2
|
@@ -70,9 +70,7 @@ Puppet::Functions.create_function(:run_plan, Puppet::Functions::InternalFunction
|
|
70
70
|
|
71
71
|
# TODO: Why would we not have a private_environment_loader?
|
72
72
|
unless loader && (func = loader.load(:plan, plan_name))
|
73
|
-
raise
|
74
|
-
Puppet::Pops::Issues.issue(:UNKNOWN_PLAN) { Bolt::Error.unknown_plan(plan_name) }
|
75
|
-
)
|
73
|
+
raise Bolt::Error.unknown_plan(plan_name)
|
76
74
|
end
|
77
75
|
|
78
76
|
if (run_as = named_args['_run_as'])
|
@@ -77,7 +77,7 @@ Puppet::Functions.create_function(:run_task) do
|
|
77
77
|
# TODO: use the compiler injection once PUP-8237 lands
|
78
78
|
task_signature = Puppet::Pal::ScriptCompiler.new(closure_scope.compiler).task_signature(task_name)
|
79
79
|
if task_signature.nil?
|
80
|
-
raise
|
80
|
+
raise Bolt::Error.unknown_task(task_name)
|
81
81
|
end
|
82
82
|
|
83
83
|
task_signature.runnable_with?(use_args) do |mismatch_message|
|
@@ -62,7 +62,7 @@ module Bolt
|
|
62
62
|
{ flags: ACTION_OPTS + %w[tmpdir],
|
63
63
|
banner: SCRIPT_HELP }
|
64
64
|
when 'secret'
|
65
|
-
{ flags: OPTIONS[:global] + OPTIONS[:global_config_setters],
|
65
|
+
{ flags: OPTIONS[:global] + OPTIONS[:global_config_setters] + %w[plugin],
|
66
66
|
banner: SECRET_HELP }
|
67
67
|
when 'task'
|
68
68
|
case action
|
@@ -261,6 +261,7 @@ module Bolt
|
|
261
261
|
HELP
|
262
262
|
|
263
263
|
SECRET_HELP = <<~SECRET_HELP
|
264
|
+
Usage: bolt secret <action> <value>
|
264
265
|
Manage secrets for inventory and hiera data.
|
265
266
|
|
266
267
|
Available actions are:
|
@@ -446,6 +447,10 @@ module Bolt
|
|
446
447
|
define('--debug', 'Display debug logging') do |_|
|
447
448
|
@options[:debug] = true
|
448
449
|
end
|
450
|
+
|
451
|
+
define('--plugin PLUGIN', 'Select the plugin to use') do |plug|
|
452
|
+
@options[:plugin] = plug
|
453
|
+
end
|
449
454
|
end
|
450
455
|
|
451
456
|
def remove_excluded_opts(option_list)
|
data/lib/bolt/cli.rb
CHANGED
data/lib/bolt/error.rb
CHANGED
@@ -33,11 +33,15 @@ module Bolt
|
|
33
33
|
end
|
34
34
|
|
35
35
|
def self.unknown_task(task)
|
36
|
-
"Could not find a task named \"#{task}\". For a list of available tasks, run \"bolt task show\""
|
36
|
+
new("Could not find a task named \"#{task}\". For a list of available tasks, run \"bolt task show\"",
|
37
|
+
'bolt/unknown-task')
|
37
38
|
end
|
38
39
|
|
39
40
|
def self.unknown_plan(plan)
|
40
|
-
|
41
|
+
new(
|
42
|
+
"Could not find a plan named \"#{plan}\". For a list of available plans, run \"bolt plan show\"",
|
43
|
+
'bolt/unknown-plan'
|
44
|
+
)
|
41
45
|
end
|
42
46
|
end
|
43
47
|
|
@@ -102,13 +102,13 @@ module Bolt
|
|
102
102
|
if value.is_a?(Hash) && value.include?('_plugin')
|
103
103
|
plugin_name = value['_plugin']
|
104
104
|
begin
|
105
|
-
hook = @plugins.get_hook(plugin_name, :
|
105
|
+
hook = @plugins.get_hook(plugin_name, :resolve_reference)
|
106
106
|
rescue Bolt::Plugin::PluginError => e
|
107
107
|
raise ValidationError.new(e.message, @name)
|
108
108
|
end
|
109
109
|
|
110
110
|
begin
|
111
|
-
validate_proc = @plugins.get_hook(plugin_name, :
|
111
|
+
validate_proc = @plugins.get_hook(plugin_name, :validate_resolve_reference)
|
112
112
|
rescue Bolt::Plugin::PluginError
|
113
113
|
validate_proc = proc { |*args| }
|
114
114
|
end
|
@@ -119,7 +119,7 @@ module Bolt
|
|
119
119
|
begin
|
120
120
|
hook.call(value)
|
121
121
|
rescue StandardError => e
|
122
|
-
loc = "
|
122
|
+
loc = "resolve_reference in #{@name}"
|
123
123
|
raise Bolt::Plugin::PluginError::ExecutionError.new(e.message, plugin_name, loc)
|
124
124
|
end
|
125
125
|
end
|
@@ -213,7 +213,7 @@ module Bolt
|
|
213
213
|
|
214
214
|
def lookup_targets(lookup)
|
215
215
|
begin
|
216
|
-
hook = @plugins.get_hook(lookup['_plugin'], :
|
216
|
+
hook = @plugins.get_hook(lookup['_plugin'], :resolve_reference)
|
217
217
|
rescue Bolt::Plugin::PluginError => e
|
218
218
|
raise ValidationError.new(e.message, @name)
|
219
219
|
end
|
@@ -221,7 +221,7 @@ module Bolt
|
|
221
221
|
begin
|
222
222
|
targets = hook.call(lookup)
|
223
223
|
rescue StandardError => e
|
224
|
-
loc = "
|
224
|
+
loc = "resolve_reference in #{@name}"
|
225
225
|
raise Bolt::Plugin::PluginError::ExecutionError.new(e.message, lookup['_plugin'], loc)
|
226
226
|
end
|
227
227
|
|
data/lib/bolt/module.rb
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Is this Bolt::Pobs::Module?
|
4
|
+
module Bolt
|
5
|
+
class Module
|
6
|
+
MODULE_NAME_REGEX = /\A[a-z][a-z0-9_]*\z/.freeze
|
7
|
+
|
8
|
+
def self.discover(modulepath)
|
9
|
+
modulepath.each_with_object({}) do |path, mods|
|
10
|
+
next unless File.exist?(path) && File.directory?(path)
|
11
|
+
Dir.children(path)
|
12
|
+
.map { |dir| File.join(path, dir) }
|
13
|
+
.select { |dir| File.directory?(dir) }
|
14
|
+
.each do |dir|
|
15
|
+
module_name = File.basename(dir)
|
16
|
+
if module_name =~ MODULE_NAME_REGEX
|
17
|
+
# Puppet will load some objects from shadowed modules but this won't
|
18
|
+
mods[module_name] ||= Bolt::Module.new(module_name, dir)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
attr_reader :name, :path
|
25
|
+
|
26
|
+
def initialize(name, path)
|
27
|
+
@name = name
|
28
|
+
@path = path
|
29
|
+
end
|
30
|
+
|
31
|
+
def plugin_data_file
|
32
|
+
File.join(path, 'bolt_plugin.json')
|
33
|
+
end
|
34
|
+
|
35
|
+
def plugin?
|
36
|
+
File.exist?(plugin_data_file)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
data/lib/bolt/pal.rb
CHANGED
@@ -41,12 +41,9 @@ module Bolt
|
|
41
41
|
|
42
42
|
def initialize(modulepath, hiera_config, max_compiles = Etc.nprocessors)
|
43
43
|
# Nothing works without initialized this global state. Reinitializing
|
44
|
-
# is safe and in practice only
|
44
|
+
# is safe and in practice only happens in tests
|
45
45
|
self.class.load_puppet
|
46
46
|
|
47
|
-
# This makes sure we don't accidentally create puppet dirs
|
48
|
-
with_puppet_settings { |_| nil }
|
49
|
-
|
50
47
|
@original_modulepath = modulepath
|
51
48
|
@modulepath = [BOLTLIB_PATH, *modulepath, MODULES_PATH]
|
52
49
|
@hiera_config = hiera_config
|
@@ -56,6 +53,8 @@ module Bolt
|
|
56
53
|
if modulepath && !modulepath.empty?
|
57
54
|
@logger.info("Loading modules from #{@modulepath.join(File::PATH_SEPARATOR)}")
|
58
55
|
end
|
56
|
+
|
57
|
+
@loaded = false
|
59
58
|
end
|
60
59
|
|
61
60
|
# Puppet logging is global so this is class method to avoid confusion
|
@@ -91,6 +90,17 @@ module Bolt
|
|
91
90
|
Bolt::ResultSet.include_iterable
|
92
91
|
end
|
93
92
|
|
93
|
+
def setup
|
94
|
+
unless @loaded
|
95
|
+
# This is slow so don't do it until we have to
|
96
|
+
Bolt::PAL.load_puppet
|
97
|
+
|
98
|
+
# Make sure we don't create the puppet directories
|
99
|
+
with_puppet_settings { |_| nil }
|
100
|
+
@loaded = true
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
94
104
|
# Create a top-level alias for TargetSpec and PlanResult so that users don't have to
|
95
105
|
# namespace it with Boltlib, which is just an implementation detail. This
|
96
106
|
# allows them to feel like a built-in type in bolt, rather than
|
@@ -104,6 +114,8 @@ module Bolt
|
|
104
114
|
# exceptions thrown by the block and re-raises them ensuring they are
|
105
115
|
# Bolt::Errors since the script compiler block will squash all exceptions.
|
106
116
|
def in_bolt_compiler
|
117
|
+
# TODO: If we always call this inside a bolt_executor we can remove this here
|
118
|
+
setup
|
107
119
|
r = Puppet::Pal.in_tmp_environment('bolt', modulepath: @modulepath, facts: {}) do |pal|
|
108
120
|
pal.with_script_compiler do |compiler|
|
109
121
|
alias_types(compiler)
|
@@ -129,6 +141,7 @@ module Bolt
|
|
129
141
|
end
|
130
142
|
|
131
143
|
def with_bolt_executor(executor, inventory, pdb_client = nil, applicator = nil, &block)
|
144
|
+
setup
|
132
145
|
opts = {
|
133
146
|
bolt_executor: executor,
|
134
147
|
bolt_inventory: inventory,
|
@@ -188,6 +201,7 @@ module Bolt
|
|
188
201
|
# Parses a snippet of Puppet manifest code and returns the AST represented
|
189
202
|
# in JSON.
|
190
203
|
def parse_manifest(code, filename)
|
204
|
+
setup
|
191
205
|
Puppet::Pops::Parser::EvaluatingParser.new.parse_string(code, filename)
|
192
206
|
rescue Puppet::Error => e
|
193
207
|
raise Bolt::PAL::PALError, "Failed to parse manifest: #{e}"
|
@@ -250,7 +264,7 @@ module Bolt
|
|
250
264
|
task = task_signature(task_name)
|
251
265
|
|
252
266
|
if task.nil?
|
253
|
-
raise Bolt::Error.
|
267
|
+
raise Bolt::Error.unknown_task(task_name)
|
254
268
|
end
|
255
269
|
|
256
270
|
task.task_hash.reject { |k, _| k == 'parameters' }
|
@@ -296,7 +310,7 @@ module Bolt
|
|
296
310
|
end
|
297
311
|
|
298
312
|
if plan_info.nil?
|
299
|
-
raise Bolt::Error.
|
313
|
+
raise Bolt::Error.unknown_plan(plan_name)
|
300
314
|
end
|
301
315
|
plan_info
|
302
316
|
end
|
data/lib/bolt/plugin.rb
CHANGED
@@ -1,20 +1,26 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'bolt/inventory'
|
4
|
+
require 'bolt/executor'
|
5
|
+
require 'bolt/module'
|
6
|
+
require 'bolt/pal'
|
3
7
|
require 'bolt/plugin/puppetdb'
|
4
|
-
require 'bolt/plugin/terraform'
|
5
|
-
require 'bolt/plugin/pkcs7'
|
6
|
-
require 'bolt/plugin/prompt'
|
7
|
-
require 'bolt/plugin/task'
|
8
|
-
require 'bolt/plugin/aws'
|
9
|
-
require 'bolt/plugin/vault'
|
10
|
-
require 'bolt/plugin/install_agent'
|
11
8
|
|
12
9
|
module Bolt
|
13
10
|
class Plugin
|
11
|
+
KNOWN_HOOKS = %i[
|
12
|
+
puppet_library
|
13
|
+
resolve_reference
|
14
|
+
secret_encrypt
|
15
|
+
secret_decrypt
|
16
|
+
secret_createkeys
|
17
|
+
validate_resolve_reference
|
18
|
+
].freeze
|
19
|
+
|
14
20
|
class PluginError < Bolt::Error
|
15
21
|
class ExecutionError < PluginError
|
16
22
|
def initialize(msg, plugin_name, location)
|
17
|
-
mess = "Error executing plugin
|
23
|
+
mess = "Error executing plugin #{plugin_name} from #{location}: #{msg}"
|
18
24
|
super(mess, 'bolt/plugin-error')
|
19
25
|
end
|
20
26
|
end
|
@@ -32,40 +38,180 @@ module Bolt
|
|
32
38
|
end
|
33
39
|
end
|
34
40
|
|
35
|
-
|
36
|
-
|
41
|
+
class PluginContext
|
42
|
+
def initialize(config, pal)
|
43
|
+
@pal = pal
|
44
|
+
@config = config
|
45
|
+
end
|
46
|
+
|
47
|
+
def serial_executor
|
48
|
+
@serial_executor ||= Bolt::Executor.new(1)
|
49
|
+
end
|
50
|
+
private :serial_executor
|
51
|
+
|
52
|
+
def empty_inventory
|
53
|
+
@empty_inventory ||= Bolt::Inventory.new({}, @config)
|
54
|
+
end
|
55
|
+
private :empty_inventory
|
56
|
+
|
57
|
+
def with_a_compiler
|
58
|
+
# If we're already inside a pal compiler block use that compiler
|
59
|
+
# This may blow up if you try to load a task in catalog pal. Should we
|
60
|
+
# guard against that?
|
61
|
+
compiler = nil
|
62
|
+
if defined?(Puppet)
|
63
|
+
begin
|
64
|
+
compiler = Puppet.lookup(:pal_compiler)
|
65
|
+
rescue Puppet::Context::UndefinedBindingError; end # rubocop:disable Lint/HandleExceptions
|
66
|
+
end
|
67
|
+
|
68
|
+
if compiler
|
69
|
+
yield compiler
|
70
|
+
else
|
71
|
+
@pal.in_bolt_compiler do |temp_compiler|
|
72
|
+
yield temp_compiler
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
private :with_a_compiler
|
77
|
+
|
78
|
+
def get_validated_task(task_name, params = nil)
|
79
|
+
with_a_compiler do |compiler|
|
80
|
+
tasksig = compiler.task_signature(task_name)
|
81
|
+
|
82
|
+
raise Bolt::Error.unknown_task(task_name) unless tasksig
|
83
|
+
|
84
|
+
Bolt::Task::Run.validate_params(tasksig, params) if params
|
85
|
+
Bolt::Task.new(tasksig.task_hash)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def validate_params(task_name, params)
|
90
|
+
with_a_compiler do |compiler|
|
91
|
+
tasksig = compiler.task_signature(task_name)
|
92
|
+
|
93
|
+
raise Bolt::Error.new("#{task_name} could not be found", 'bolt/plugin-error') unless tasksig
|
94
|
+
|
95
|
+
Bolt::Task::Run.validate_params(tasksig, params)
|
96
|
+
end
|
97
|
+
nil
|
98
|
+
end
|
99
|
+
|
100
|
+
# By passing `_` keys in params the caller can send metaparams directly to the task
|
101
|
+
# _catch_errors must be passed as an executor option not a param
|
102
|
+
def run_local_task(task, params, options)
|
103
|
+
# Make sure we're in a compiler to use the sensitive type
|
104
|
+
with_a_compiler do |_comp|
|
105
|
+
params = Bolt::Task::Run.wrap_sensitive(task, params)
|
106
|
+
Bolt::Task::Run.run_task(
|
107
|
+
task,
|
108
|
+
empty_inventory.get_targets('localhost'),
|
109
|
+
params,
|
110
|
+
options,
|
111
|
+
serial_executor
|
112
|
+
)
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
def boltdir
|
117
|
+
@config.boltdir.path
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
def self.setup(config, pal, pdb_client, analytics)
|
122
|
+
plugins = new(config, pal, analytics)
|
123
|
+
# PDB is special do we want to expose the default client to the context?
|
37
124
|
plugins.add_plugin(Bolt::Plugin::Puppetdb.new(pdb_client))
|
38
|
-
|
39
|
-
plugins.
|
40
|
-
plugins.
|
41
|
-
plugins.
|
42
|
-
plugins.
|
43
|
-
plugins.
|
44
|
-
plugins.
|
125
|
+
|
126
|
+
plugins.add_ruby_plugin('Bolt::Plugin::AwsInventory')
|
127
|
+
plugins.add_ruby_plugin('Bolt::Plugin::InstallAgent')
|
128
|
+
plugins.add_ruby_plugin('Bolt::Plugin::Task')
|
129
|
+
plugins.add_ruby_plugin('Bolt::Plugin::Terraform')
|
130
|
+
plugins.add_ruby_plugin('Bolt::Plugin::Pkcs7')
|
131
|
+
plugins.add_ruby_plugin('Bolt::Plugin::Prompt')
|
132
|
+
plugins.add_ruby_plugin('Bolt::Plugin::Vault')
|
133
|
+
|
45
134
|
plugins
|
46
135
|
end
|
47
136
|
|
48
|
-
|
137
|
+
BUILTIN_PLUGINS = %w[task terraform pkcs7 prompt vault aws_inventory puppetdb].freeze
|
138
|
+
|
139
|
+
attr_reader :pal, :plugin_context
|
140
|
+
|
141
|
+
def initialize(config, pal, analytics)
|
49
142
|
@config = config
|
50
143
|
@analytics = analytics
|
144
|
+
@plugin_context = PluginContext.new(config, pal)
|
51
145
|
@plugins = {}
|
146
|
+
@unknown = Set.new
|
147
|
+
end
|
148
|
+
|
149
|
+
def modules
|
150
|
+
@modules ||= Bolt::Module.discover(@config.modulepath)
|
52
151
|
end
|
53
152
|
|
153
|
+
# Generally this is private. Puppetdb is special though
|
54
154
|
def add_plugin(plugin)
|
55
155
|
@plugins[plugin.name] = plugin
|
56
156
|
end
|
57
157
|
|
158
|
+
def add_ruby_plugin(cls_name)
|
159
|
+
snake_name = Bolt::Util.class_name_to_file_name(cls_name)
|
160
|
+
require snake_name
|
161
|
+
cls = Kernel.const_get(cls_name)
|
162
|
+
plugin_name = snake_name.split('/').last
|
163
|
+
opts = {
|
164
|
+
context: @plugin_context,
|
165
|
+
config: config_for_plugin(plugin_name)
|
166
|
+
}
|
167
|
+
|
168
|
+
plugin = cls.new(**opts)
|
169
|
+
add_plugin(plugin)
|
170
|
+
end
|
171
|
+
|
172
|
+
def add_module_plugin(plugin_name)
|
173
|
+
opts = {
|
174
|
+
context: @plugin_context,
|
175
|
+
config: config_for_plugin(plugin_name)
|
176
|
+
}
|
177
|
+
|
178
|
+
plugin = Bolt::Plugin::Module.load(plugin_name, modules, opts)
|
179
|
+
add_plugin(plugin)
|
180
|
+
end
|
181
|
+
|
182
|
+
def add_from_config
|
183
|
+
@config.plugins.keys.each do |plugin_name|
|
184
|
+
by_name(plugin_name)
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
def config_for_plugin(plugin_name)
|
189
|
+
@config.plugins[plugin_name] || {}
|
190
|
+
end
|
191
|
+
|
58
192
|
def get_hook(plugin_name, hook)
|
59
193
|
plugin = by_name(plugin_name)
|
60
194
|
raise PluginError::Unknown, plugin_name unless plugin
|
61
|
-
raise PluginError::UnsupportedHook.new(plugin_name, hook) unless plugin.
|
62
|
-
@analytics.report_bundled_content("Plugin #{hook}", plugin_name)
|
195
|
+
raise PluginError::UnsupportedHook.new(plugin_name, hook) unless plugin.hooks.include?(hook)
|
196
|
+
@analytics.report_bundled_content("Plugin #{hook}", plugin_name) if BUILTIN_PLUGINS.include?(plugin_name)
|
63
197
|
|
64
198
|
plugin.method(hook)
|
65
199
|
end
|
66
200
|
|
201
|
+
# Calling by_name or get_hook will load any module based plugin automatically
|
67
202
|
def by_name(plugin_name)
|
68
|
-
@plugins[plugin_name]
|
203
|
+
return @plugins[plugin_name] if @plugins.include?(plugin_name)
|
204
|
+
begin
|
205
|
+
unless @unknown.include?(plugin_name)
|
206
|
+
add_module_plugin(plugin_name)
|
207
|
+
end
|
208
|
+
rescue PluginError::Unknown
|
209
|
+
@unknown << plugin_name
|
210
|
+
nil
|
211
|
+
end
|
69
212
|
end
|
70
213
|
end
|
71
214
|
end
|
215
|
+
|
216
|
+
# references PluginError
|
217
|
+
require 'bolt/plugin/module'
|