openbolt 5.3.0 → 5.5.0

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.
@@ -0,0 +1,218 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'base64'
4
+ require 'concurrent/map'
5
+ require 'digest/sha2'
6
+ require 'json'
7
+ require 'securerandom'
8
+ require 'shellwords'
9
+ require_relative '../../bolt/transport/base'
10
+
11
+ module Bolt
12
+ module Transport
13
+ # Choria transport for OpenBolt. Communicates with nodes via Choria's NATS
14
+ # pub/sub messaging infrastructure using the choria-mcorpc-support gem as
15
+ # the client library. Extends Transport::Base directly (not Simple) because
16
+ # Choria's pub/sub model doesn't fit the persistent connection/shell
17
+ # abstraction that Simple assumes.
18
+ #
19
+ # Available capabilities depend on which agents are installed on the
20
+ # target node:
21
+ #
22
+ # bolt_tasks agent only: Only run_task works, via the bolt_tasks agent
23
+ # which downloads task files from an OpenVox/Puppet Server and executes
24
+ # them via task_wrapper. All other operations fail with an actionable
25
+ # error directing the user to install the shell agent.
26
+ #
27
+ # shell agent installed (>= 1.2.1): run_command, run_script, and
28
+ # run_task work. run_task uses the bolt_tasks agent by default.
29
+ # To run local tasks via the shell agent, set task-agent to 'shell'
30
+ # in project config or specify --choria-task-agent shell.
31
+ #
32
+ # Upload, download, and plans are not yet supported.
33
+ class Choria < Base
34
+ def initialize
35
+ super
36
+ @config_mutex = Mutex.new
37
+ @config_error = nil
38
+ @client_configured = false
39
+ # Serializes RPC calls across batch threads. See the comment on
40
+ # rpc_request in helpers.rb for why this is necessary.
41
+ @rpc_mutex = Mutex.new
42
+ # Multiple batch threads write to this map concurrently when we
43
+ # have more than one collective.
44
+ @agent_cache = Concurrent::Map.new
45
+ @default_collective = nil
46
+ end
47
+
48
+ # Advertise both shell and powershell so tasks with either requirement
49
+ # can be selected. The per-target selection happens in
50
+ # select_implementation below, which picks the right feature set based
51
+ # on the target's detected OS.
52
+ def provided_features
53
+ %w[shell powershell]
54
+ end
55
+
56
+ # Override to select task implementation based on the target's OS.
57
+ # Other transports rely on inventory features to pick the right
58
+ # implementation, but Choria discovers the OS at runtime via the
59
+ # os.family fact. We pass only the detected platform's feature so
60
+ # task.select_implementation picks the correct .ps1 or .sh file.
61
+ #
62
+ # @param target [Bolt::Target] Target whose OS determines the implementation
63
+ # @param task [Bolt::Task] Task with platform-specific implementations
64
+ # @return [Hash] Selected implementation hash with 'path', 'name', 'input_method', 'files' keys
65
+ def select_implementation(target, task)
66
+ features = windows_target?(target) ? ['powershell'] : ['shell']
67
+ impl = task.select_implementation(target, features)
68
+ impl['input_method'] ||= default_input_method(impl['path'])
69
+ impl
70
+ end
71
+
72
+ # Group targets by collective so each batch uses a single RPC client
73
+ # scope. MCollective RPC calls are published to a collective-specific
74
+ # NATS subject, so targets in different collectives must be in separate
75
+ # batches. Most deployments have one collective, yielding one batch.
76
+ # Bolt runs each batch in its own thread and @rpc_mutex serializes
77
+ # the RPC calls across threads to prevent response misrouting.
78
+ #
79
+ # @param targets [Array<Bolt::Target>] All targets for this operation
80
+ # @return [Array<Array<Bolt::Target>>] Targets grouped by collective
81
+ def batches(targets)
82
+ # Populates @default_collective from the Choria config so targets
83
+ # without an explicit collective are grouped correctly.
84
+ configure_client(targets.first)
85
+ targets.group_by { |target| collective_for(target) }.values
86
+ end
87
+
88
+ # Override batch_task to handle multiple targets in one thread using the RPC.
89
+ # Implementation grouping (mixed-platform support) is handled internally
90
+ # by run_task_via_bolt_tasks and run_task_via_shell.
91
+ #
92
+ # @param targets [Array<Bolt::Target>] Targets in a single collective batch
93
+ # @param task [Bolt::Task] Task to execute
94
+ # @param arguments [Hash] Task parameter names to values
95
+ # @param options [Hash] Execution options (unused currently, passed through from Base)
96
+ # @param position [Array] Positional info for result tracking
97
+ # @param callback [Proc] Called with :node_start and :node_result events
98
+ # @return [Array<Bolt::Result>] Results for all targets (successes and failures)
99
+ def batch_task(targets, task, arguments, _options = {}, position = [], &callback)
100
+ chosen_agent = targets.first.options['task-agent'] || 'bolt_tasks'
101
+ result_opts = { action: 'task', name: task.name, position: position }
102
+
103
+ # The results var here is the error results for incapable targets, to which we'll add in
104
+ # the successful results from the capable targets as we go.
105
+ capable, results = prepare_targets(targets, chosen_agent, result_opts, &callback)
106
+
107
+ logger.debug { "Task #{task.name} routing: agent: #{chosen_agent}, #{capable.size} capable / #{targets.size - capable.size} incapable" }
108
+
109
+ unless capable.empty?
110
+ capable.each { |target| callback&.call(type: :node_start, target: target) }
111
+ arguments = unwrap_sensitive_args(arguments)
112
+
113
+ results += case chosen_agent
114
+ when 'bolt_tasks'
115
+ run_task_via_bolt_tasks(capable, task, arguments, result_opts, &callback)
116
+ when 'shell'
117
+ run_task_via_shell(capable, task, arguments, result_opts, &callback)
118
+ else
119
+ raise Bolt::Error.new(
120
+ "Unsupported task-agent '#{chosen_agent}'",
121
+ 'bolt/choria-unsupported-agent'
122
+ )
123
+ end
124
+ end
125
+
126
+ results
127
+ end
128
+
129
+ # Override batch_task_with for per-target arguments. Only called
130
+ # from the run_task_with Puppet plan function (no CLI or Ruby API
131
+ # path uses this). Discovery is batched upfront, but execution is
132
+ # sequential per-target because MCollective RPC calls send the
133
+ # same arguments to all targets. A future optimization could batch
134
+ # the download/infra-setup/polling steps while keeping only the
135
+ # start step per-target.
136
+ #
137
+ # THIS IS NOT YET READY FOR PRODUCTION. The API is stable, but we don't
138
+ # yet have full plan support and this runs the task sequentially across
139
+ # targets, which is very inefficient. It had to be implemented now, though,
140
+ # in order to prevent the assert_batch_size_one from the Base interface
141
+ # from blowing things up.
142
+ #
143
+ # @param targets [Array<Bolt::Target>] Targets in a single collective batch
144
+ # @param task [Bolt::Task] Task to execute
145
+ # @param target_mapping [Hash{Bolt::Target => Hash}] Per-target argument hashes
146
+ # @param options [Hash] Execution options (passed through from Base)
147
+ # @param position [Array] Positional info for result tracking
148
+ # @param callback [Proc] Called with :node_start and :node_result events
149
+ # @return [Array<Bolt::Result>] Results for all targets
150
+ def batch_task_with(targets, task, target_mapping, options = {}, position = [], &callback)
151
+ # Pre-warm the agent cache so individual batch_task calls are cache hits
152
+ configure_client(targets.first)
153
+ discover_agents(targets)
154
+
155
+ results = []
156
+ targets.each do |target|
157
+ results += batch_task([target], task, target_mapping[target], options, position, &callback)
158
+ end
159
+ results
160
+ end
161
+
162
+ # Override batch_connected? to check all targets in one RPC call. Only
163
+ # used for wait_until_available in plans.
164
+ #
165
+ # @param targets [Array<Bolt::Target>] Targets to check connectivity for
166
+ # @return [Boolean] True if all targets responded to ping
167
+ def batch_connected?(targets)
168
+ logger.debug { "Checking connectivity for #{target_count(targets)}" }
169
+ first_target = targets.first
170
+ configure_client(first_target)
171
+
172
+ response = rpc_request('rpcutil', targets, 'rpcutil.ping') do |client|
173
+ client.ping
174
+ end
175
+ response[:responded].length == targets.length
176
+ rescue StandardError => e
177
+ raise if e.is_a?(Bolt::Error)
178
+
179
+ logger.warn { "Batch connectivity check failed: #{e.class}: #{e.message}" }
180
+ false
181
+ end
182
+
183
+ def upload(_target, _source, _destination, _options = {}, _position = [])
184
+ raise Bolt::Error.new(
185
+ 'The Choria transport does not yet support upload.',
186
+ 'bolt/choria-unsupported-operation'
187
+ )
188
+ end
189
+
190
+ def download(_target, _source, _destination, _options = {}, _position = [])
191
+ raise Bolt::Error.new(
192
+ 'The Choria transport does not yet support download.',
193
+ 'bolt/choria-unsupported-operation'
194
+ )
195
+ end
196
+
197
+ # Returns the Choria node identity for a target. Uses the transport
198
+ # 'host' config if set, falling back to target.host (which Bolt
199
+ # derives from the URI or target name).
200
+ def choria_identity(target)
201
+ target.options['host'] || target.host
202
+ end
203
+
204
+ # Returns the collective for a target, used by batches() to group
205
+ # targets. Falls back to the default collective from the loaded config.
206
+ def collective_for(target)
207
+ target.options['collective'] || @default_collective
208
+ end
209
+ end
210
+ end
211
+ end
212
+
213
+ require_relative 'choria/agent_discovery'
214
+ require_relative 'choria/bolt_tasks'
215
+ require_relative 'choria/client'
216
+ require_relative 'choria/command_builders'
217
+ require_relative 'choria/helpers'
218
+ require_relative 'choria/shell'
@@ -162,9 +162,20 @@ module Bolt
162
162
  raise Bolt::Node::FileError.new(e.message, 'WRITE_ERROR')
163
163
  end
164
164
 
165
- def upload_file_smb(source, destination)
165
+ def require_ruby_smb
166
166
  # lazy-load expensive gem code
167
+ # In BinData 2.5.0+, the below commit makes things VERY noisy when loading RubySMB, so
168
+ # we temporarily disable warnings while we load it. If this ever gets fixed, get rid
169
+ # of this function and restore the plain 'require' where this function is called.
170
+ # https://github.com/dmendel/bindata/commit/2c8588a1ae5959080fffa429e07027f2ff20161c
171
+ prev = $VERBOSE
172
+ $VERBOSE = nil
167
173
  require 'ruby_smb'
174
+ $VERBOSE = prev
175
+ end
176
+
177
+ def upload_file_smb(source, destination)
178
+ require_ruby_smb
168
179
 
169
180
  win_dest = destination.tr('/', '\\')
170
181
  if (md = win_dest.match(/^([a-z]):\\(.*)/i))
@@ -215,8 +226,7 @@ module Bolt
215
226
  end
216
227
 
217
228
  def download_file_smb(source, destination)
218
- # lazy-load expensive gem code
219
- require 'ruby_smb'
229
+ require_ruby_smb
220
230
 
221
231
  win_source = source.tr('/', '\\')
222
232
  if (md = win_source.match(/^([a-z]):\\(.*)/i))
data/lib/bolt/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Bolt
4
- VERSION = '5.3.0'
4
+ VERSION = '5.5.0'
5
5
  end
@@ -0,0 +1,154 @@
1
+ metadata :name => "shell",
2
+ :description => "Run commands with the local shell",
3
+ :author => "Puppet Labs",
4
+ :license => "Apache-2.0",
5
+ :version => "1.2.1",
6
+ :url => "https://github.com/choria-plugins/shell-agent",
7
+ :timeout => 180
8
+
9
+ action "run", :description => "Run a command" do
10
+ display :always
11
+
12
+ input :command,
13
+ :prompt => "Command",
14
+ :description => "Command to run",
15
+ :type => :string,
16
+ :validation => '.*',
17
+ :maxlength => 10 * 1024,
18
+ :optional => false
19
+
20
+ input :user,
21
+ :prompt => "User",
22
+ :description => "User to run command as",
23
+ :type => :string,
24
+ :validation => '.*',
25
+ :maxlength => 1024,
26
+ :optional => true
27
+
28
+ input :timeout,
29
+ :prompt => "Timeout",
30
+ :description => "Timeout to wait for the command to complete",
31
+ :type => :float,
32
+ :optional => true
33
+ # TODO(richardc): validate positive. May need another validator class
34
+
35
+ output :stdout,
36
+ :description => "stdout from the command",
37
+ :display_as => "stdout"
38
+
39
+ output :stderr,
40
+ :description => "stderr from the command",
41
+ :display_as => "stderr"
42
+
43
+ output :success,
44
+ :description => "did the process exit successfully",
45
+ :display_as => "success"
46
+
47
+ output :exitcode,
48
+ :description => "exit code of the command",
49
+ :display_as => "exitcode"
50
+ end
51
+
52
+ action "start", :description => "Spawn a command" do
53
+ display :always
54
+
55
+ input :command,
56
+ :prompt => "Command",
57
+ :description => "Command to run",
58
+ :type => :string,
59
+ :validation => '.*',
60
+ :maxlength => 10 * 1024,
61
+ :optional => false
62
+
63
+ input :user,
64
+ :prompt => "User",
65
+ :description => "User to run command as",
66
+ :type => :string,
67
+ :validation => '.*',
68
+ :maxlength => 1024,
69
+ :optional => true
70
+
71
+ output :handle,
72
+ :description => "identifier to a running command",
73
+ :display_as => "handle"
74
+ end
75
+
76
+ action "status", :description => "Get status of managed command" do
77
+ display :always
78
+
79
+ input :handle,
80
+ :prompt => "Handle",
81
+ :description => "Handle of the command",
82
+ :type => :string,
83
+ :validation => '^[0-9a-z\-]*$',
84
+ :maxlength => 36,
85
+ :optional => false
86
+
87
+ input :stdout_offset,
88
+ :prompt => "stdout_offset",
89
+ :description => "stdout_offset",
90
+ :type => :integer,
91
+ :optional => true
92
+
93
+ input :stderr_offset,
94
+ :prompt => "stderr_offset",
95
+ :description => "stderr_offset",
96
+ :type => :integer,
97
+ :optional => true
98
+
99
+ # Running, Exited
100
+ output :status,
101
+ :description => "status of the command",
102
+ :display_as => "status"
103
+
104
+ # Stdout to this point - resets internal state
105
+ output :stdout,
106
+ :description => "stdout of the command",
107
+ :display_as => "stdout"
108
+
109
+ # Stderr to this point - resets internal state
110
+ output :stderr,
111
+ :description => "stderr of the command",
112
+ :display_as => "stderr"
113
+
114
+ # Only meaningful if status == Exited
115
+ output :exitcode,
116
+ :description => "exitcode of the command",
117
+ :display_as => "exitcode"
118
+
119
+ end
120
+
121
+ action "list", :description => "Get a list of all running commands" do
122
+ display :always
123
+
124
+ output :jobs,
125
+ :description => "state of managed jobs",
126
+ :display_as => "jobs"
127
+
128
+ end
129
+
130
+ action "statuses", :description => "Get status and output of multiple managed commands" do
131
+ display :always
132
+
133
+ input :handles,
134
+ :prompt => "Handles",
135
+ :description => "Array of command handles to query",
136
+ :type => :array,
137
+ :optional => false
138
+
139
+ output :statuses,
140
+ :description => "status and output keyed by handle",
141
+ :display_as => "statuses"
142
+ end
143
+
144
+ action "kill", :description => "Kill a command by handle" do
145
+ display :always
146
+
147
+ input :handle,
148
+ :prompt => "Handle",
149
+ :description => "Handle of the command",
150
+ :type => :string,
151
+ :validation => '^[0-9a-z\-]*$',
152
+ :maxlength => 36,
153
+ :optional => false
154
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: openbolt
3
3
  version: !ruby/object:Gem::Version
4
- version: 5.3.0
4
+ version: 5.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - OpenVox Project
@@ -51,6 +51,20 @@ dependencies:
51
51
  - - ">="
52
52
  - !ruby/object:Gem::Version
53
53
  version: '2.2'
54
+ - !ruby/object:Gem::Dependency
55
+ name: choria-mcorpc-support
56
+ requirement: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - "~>"
59
+ - !ruby/object:Gem::Version
60
+ version: '2.26'
61
+ type: :runtime
62
+ prerelease: false
63
+ version_requirements: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - "~>"
66
+ - !ruby/object:Gem::Version
67
+ version: '2.26'
54
68
  - !ruby/object:Gem::Dependency
55
69
  name: concurrent-ruby
56
70
  requirement: !ruby/object:Gem::Requirement
@@ -94,7 +108,7 @@ dependencies:
94
108
  version: 3.0.0
95
109
  - - "<"
96
110
  - !ruby/object:Gem::Version
97
- version: 5.0.0
111
+ version: 6.0.0
98
112
  type: :runtime
99
113
  prerelease: false
100
114
  version_requirements: !ruby/object:Gem::Requirement
@@ -104,7 +118,7 @@ dependencies:
104
118
  version: 3.0.0
105
119
  - - "<"
106
120
  - !ruby/object:Gem::Version
107
- version: 5.0.0
121
+ version: 6.0.0
108
122
  - !ruby/object:Gem::Dependency
109
123
  name: json
110
124
  requirement: !ruby/object:Gem::Requirement
@@ -244,7 +258,7 @@ dependencies:
244
258
  version: '5.0'
245
259
  - - "<"
246
260
  - !ruby/object:Gem::Version
247
- version: '7'
261
+ version: '8'
248
262
  type: :runtime
249
263
  prerelease: false
250
264
  version_requirements: !ruby/object:Gem::Requirement
@@ -254,7 +268,7 @@ dependencies:
254
268
  version: '5.0'
255
269
  - - "<"
256
270
  - !ruby/object:Gem::Version
257
- version: '7'
271
+ version: '8'
258
272
  - !ruby/object:Gem::Dependency
259
273
  name: puppetfile-resolver
260
274
  requirement: !ruby/object:Gem::Requirement
@@ -327,16 +341,16 @@ dependencies:
327
341
  name: terminal-table
328
342
  requirement: !ruby/object:Gem::Requirement
329
343
  requirements:
330
- - - ">="
344
+ - - "~>"
331
345
  - !ruby/object:Gem::Version
332
- version: '3.0'
346
+ version: '4.0'
333
347
  type: :runtime
334
348
  prerelease: false
335
349
  version_requirements: !ruby/object:Gem::Requirement
336
350
  requirements:
337
- - - ">="
351
+ - - "~>"
338
352
  - !ruby/object:Gem::Version
339
- version: '3.0'
353
+ version: '4.0'
340
354
  - !ruby/object:Gem::Dependency
341
355
  name: winrm
342
356
  requirement: !ruby/object:Gem::Requirement
@@ -445,14 +459,14 @@ dependencies:
445
459
  requirements:
446
460
  - - "~>"
447
461
  - !ruby/object:Gem::Version
448
- version: 5.0.0
462
+ version: 5.2.0
449
463
  type: :development
450
464
  prerelease: false
451
465
  version_requirements: !ruby/object:Gem::Requirement
452
466
  requirements:
453
467
  - - "~>"
454
468
  - !ruby/object:Gem::Version
455
- version: 5.0.0
469
+ version: 5.2.0
456
470
  description: Execute commands remotely over SSH and WinRM
457
471
  email:
458
472
  - openvox@voxpupuli.org
@@ -550,6 +564,7 @@ files:
550
564
  - lib/bolt/config/modulepath.rb
551
565
  - lib/bolt/config/options.rb
552
566
  - lib/bolt/config/transport/base.rb
567
+ - lib/bolt/config/transport/choria.rb
553
568
  - lib/bolt/config/transport/docker.rb
554
569
  - lib/bolt/config/transport/jail.rb
555
570
  - lib/bolt/config/transport/local.rb
@@ -618,7 +633,6 @@ files:
618
633
  - lib/bolt/plugin/env_var.rb
619
634
  - lib/bolt/plugin/module.rb
620
635
  - lib/bolt/plugin/prompt.rb
621
- - lib/bolt/plugin/puppet_connect_data.rb
622
636
  - lib/bolt/plugin/puppetdb.rb
623
637
  - lib/bolt/plugin/task.rb
624
638
  - lib/bolt/project.rb
@@ -646,6 +660,13 @@ files:
646
660
  - lib/bolt/task/puppet_server.rb
647
661
  - lib/bolt/task/run.rb
648
662
  - lib/bolt/transport/base.rb
663
+ - lib/bolt/transport/choria.rb
664
+ - lib/bolt/transport/choria/agent_discovery.rb
665
+ - lib/bolt/transport/choria/bolt_tasks.rb
666
+ - lib/bolt/transport/choria/client.rb
667
+ - lib/bolt/transport/choria/command_builders.rb
668
+ - lib/bolt/transport/choria/helpers.rb
669
+ - lib/bolt/transport/choria/shell.rb
649
670
  - lib/bolt/transport/docker.rb
650
671
  - lib/bolt/transport/docker/connection.rb
651
672
  - lib/bolt/transport/jail.rb
@@ -681,6 +702,7 @@ files:
681
702
  - lib/bolt_spec/plans/publish_stub.rb
682
703
  - lib/bolt_spec/run.rb
683
704
  - lib/logging_extensions/logging.rb
705
+ - lib/mcollective/agent/shell.ddl
684
706
  - libexec/apply_catalog.rb
685
707
  - libexec/bolt_catalog
686
708
  - libexec/custom_facts.rb
@@ -694,7 +716,6 @@ files:
694
716
  - modules/canary/lib/puppet/functions/canary/random_split.rb
695
717
  - modules/canary/lib/puppet/functions/canary/skip.rb
696
718
  - modules/canary/plans/init.pp
697
- - modules/puppet_connect/plans/test_input_data.pp
698
719
  - modules/puppetdb_fact/plans/init.pp
699
720
  - resources/bolt_bash_completion.sh
700
721
  homepage: https://github.com/OpenVoxProject/openbolt/
@@ -715,7 +736,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
715
736
  - !ruby/object:Gem::Version
716
737
  version: '0'
717
738
  requirements: []
718
- rubygems_version: 3.6.9
739
+ rubygems_version: 4.0.6
719
740
  specification_version: 4
720
741
  summary: Execute commands remotely over SSH and WinRM
721
742
  test_files: []
@@ -1,85 +0,0 @@
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
-
42
- current_value
43
- end
44
- end
45
- end
46
- end
47
-
48
- def name
49
- 'puppet_connect_data'
50
- end
51
-
52
- def hooks
53
- hook_descriptions.keys
54
- end
55
-
56
- def hook_descriptions
57
- {
58
- resolve_reference: nil,
59
- validate_resolve_reference: nil
60
- }
61
- end
62
-
63
- def resolve_reference(opts)
64
- key = opts['key']
65
- @data[key]
66
- end
67
-
68
- def validate_resolve_reference(opts)
69
- unless opts['key']
70
- raise Bolt::ValidationError,
71
- "puppet_connect_data plugin requires that 'key' be specified"
72
- end
73
- if @input_data_path && !@data.key?(opts['key'])
74
- # Input data for Puppet Connect was provided and opts['key'] does not have a
75
- # value specified. Raise an error for this case.
76
- raise invalid_input_data_err("a value for the #{opts['key']} key is not specified")
77
- end
78
- end
79
-
80
- def invalid_input_data_err(msg)
81
- Bolt::ValidationError.new("invalid input data #{@input_data_path}: #{msg}")
82
- end
83
- end
84
- end
85
- end