openbolt 5.4.0 → 5.6.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.4.0'
4
+ VERSION = '5.6.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.4.0
4
+ version: 5.6.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
@@ -123,16 +137,22 @@ dependencies:
123
137
  name: jwt
124
138
  requirement: !ruby/object:Gem::Requirement
125
139
  requirements:
126
- - - "~>"
140
+ - - ">="
127
141
  - !ruby/object:Gem::Version
128
142
  version: '2.2'
143
+ - - "<"
144
+ - !ruby/object:Gem::Version
145
+ version: '4.0'
129
146
  type: :runtime
130
147
  prerelease: false
131
148
  version_requirements: !ruby/object:Gem::Requirement
132
149
  requirements:
133
- - - "~>"
150
+ - - ">="
134
151
  - !ruby/object:Gem::Version
135
152
  version: '2.2'
153
+ - - "<"
154
+ - !ruby/object:Gem::Version
155
+ version: '4.0'
136
156
  - !ruby/object:Gem::Dependency
137
157
  name: logging
138
158
  requirement: !ruby/object:Gem::Requirement
@@ -327,16 +347,16 @@ dependencies:
327
347
  name: terminal-table
328
348
  requirement: !ruby/object:Gem::Requirement
329
349
  requirements:
330
- - - ">="
350
+ - - "~>"
331
351
  - !ruby/object:Gem::Version
332
- version: '3.0'
352
+ version: '4.0'
333
353
  type: :runtime
334
354
  prerelease: false
335
355
  version_requirements: !ruby/object:Gem::Requirement
336
356
  requirements:
337
- - - ">="
357
+ - - "~>"
338
358
  - !ruby/object:Gem::Version
339
- version: '3.0'
359
+ version: '4.0'
340
360
  - !ruby/object:Gem::Dependency
341
361
  name: winrm
342
362
  requirement: !ruby/object:Gem::Requirement
@@ -445,14 +465,14 @@ dependencies:
445
465
  requirements:
446
466
  - - "~>"
447
467
  - !ruby/object:Gem::Version
448
- version: 5.0.0
468
+ version: 5.2.0
449
469
  type: :development
450
470
  prerelease: false
451
471
  version_requirements: !ruby/object:Gem::Requirement
452
472
  requirements:
453
473
  - - "~>"
454
474
  - !ruby/object:Gem::Version
455
- version: 5.0.0
475
+ version: 5.2.0
456
476
  description: Execute commands remotely over SSH and WinRM
457
477
  email:
458
478
  - openvox@voxpupuli.org
@@ -550,6 +570,7 @@ files:
550
570
  - lib/bolt/config/modulepath.rb
551
571
  - lib/bolt/config/options.rb
552
572
  - lib/bolt/config/transport/base.rb
573
+ - lib/bolt/config/transport/choria.rb
553
574
  - lib/bolt/config/transport/docker.rb
554
575
  - lib/bolt/config/transport/jail.rb
555
576
  - lib/bolt/config/transport/local.rb
@@ -618,7 +639,6 @@ files:
618
639
  - lib/bolt/plugin/env_var.rb
619
640
  - lib/bolt/plugin/module.rb
620
641
  - lib/bolt/plugin/prompt.rb
621
- - lib/bolt/plugin/puppet_connect_data.rb
622
642
  - lib/bolt/plugin/puppetdb.rb
623
643
  - lib/bolt/plugin/task.rb
624
644
  - lib/bolt/project.rb
@@ -646,6 +666,13 @@ files:
646
666
  - lib/bolt/task/puppet_server.rb
647
667
  - lib/bolt/task/run.rb
648
668
  - lib/bolt/transport/base.rb
669
+ - lib/bolt/transport/choria.rb
670
+ - lib/bolt/transport/choria/agent_discovery.rb
671
+ - lib/bolt/transport/choria/bolt_tasks.rb
672
+ - lib/bolt/transport/choria/client.rb
673
+ - lib/bolt/transport/choria/command_builders.rb
674
+ - lib/bolt/transport/choria/helpers.rb
675
+ - lib/bolt/transport/choria/shell.rb
649
676
  - lib/bolt/transport/docker.rb
650
677
  - lib/bolt/transport/docker/connection.rb
651
678
  - lib/bolt/transport/jail.rb
@@ -681,6 +708,7 @@ files:
681
708
  - lib/bolt_spec/plans/publish_stub.rb
682
709
  - lib/bolt_spec/run.rb
683
710
  - lib/logging_extensions/logging.rb
711
+ - lib/mcollective/agent/shell.ddl
684
712
  - libexec/apply_catalog.rb
685
713
  - libexec/bolt_catalog
686
714
  - libexec/custom_facts.rb
@@ -694,7 +722,6 @@ files:
694
722
  - modules/canary/lib/puppet/functions/canary/random_split.rb
695
723
  - modules/canary/lib/puppet/functions/canary/skip.rb
696
724
  - modules/canary/plans/init.pp
697
- - modules/puppet_connect/plans/test_input_data.pp
698
725
  - modules/puppetdb_fact/plans/init.pp
699
726
  - resources/bolt_bash_completion.sh
700
727
  homepage: https://github.com/OpenVoxProject/openbolt/
@@ -715,7 +742,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
715
742
  - !ruby/object:Gem::Version
716
743
  version: '0'
717
744
  requirements: []
718
- rubygems_version: 4.0.3
745
+ rubygems_version: 4.0.10
719
746
  specification_version: 4
720
747
  summary: Execute commands remotely over SSH and WinRM
721
748
  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