Skip to content

Commit 7a27729

Browse files
committed
(PUP-9747) Relax validation for bolt
Bolt always provides all computed connection info attributes to tasks, instead of only the ones the user provided. Therefore we can't be strict when validating the passed in arguments. To limit the impact to bolt users, this change makes the relaxed validation trigger only when a `'remote-transport'` key is specified, as that is always provided by bolt. This also contains the docs changes from puppetlabs/puppet-specifications#142
1 parent 4836e85 commit 7a27729

File tree

4 files changed

+101
-32
lines changed

4 files changed

+101
-32
lines changed

README.md

Lines changed: 20 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -239,31 +239,26 @@ Puppet::ResourceAPI.register_transport(
239239

240240
### Transport Schema keywords
241241

242-
Please note that within the transport schema, the following keywords are reserved words:
243-
244-
#### Usable within the schema
245-
246-
The following keywords are encouraged within the Transport schema:
247-
248-
* `uri` - Use when you need to specify a specific URL to connect to. All of the following keys will be computed from the `uri` if possible. In the future more url parts may be computed from the URI as well.
249-
* `host` - Use to specify and IP or address to connect to.
250-
* `protocol` - Use to specify which protocol the transport should use for example `http`, `https`, `ssh` or `tcp`
251-
* `user` - The user the transport should connect as.
252-
* `port` - The port the transport should connect to.
253-
254-
#### Non-Usable within the schema
255-
256-
The following keywords are keywords that must not be used by the transport schema:
257-
258-
* `name` - transports should use `uri` instead of name.
259-
* `path` - reserved as a uri part
260-
* `query` - reserved as a uri part
261-
* `run-on` - This is used by bolt to determine which target to proxy to. Transports should not rely on this key.
262-
* `remote-transport` - This is used to determine which transport to load. It should always be the transport class name "declassified".
263-
* `remote-*` Any key starting with `remote-` is reserved for future use.
264-
* `implementations`: reserved by bolt
265-
266-
Note: Currently bolt inventory requires that a name be set for every target and always uses that name as the URI. This means there is no way to specify `host` separately from the host section of the `name` when parsed as a URI.
242+
To align with [Bolt's inventory file](https://puppet.com/docs/bolt/latest/inventory_file.html), a transport schema prefers the following keywords (when relevant):
243+
244+
* `uri`: use when you need to specify a specific URL to connect to. Bolt will compute the following keys from the `uri` when possible. In the future more url parts may be computed from the URI.
245+
* `protocol`: use to specify which protocol the transport should use for example `http`, `https`, `ssh` or `tcp`.
246+
* `host`: use to specify an IP or address to connect to.
247+
* `port`: the port the transport should connect to.
248+
* `user`: the user the transport should connect as.
249+
* `password`: the password for the specified user.
250+
251+
Do not use the following keywords when writing a schema:
252+
253+
* `implementations`: reserved by Bolt.
254+
* `name`: transports should use `uri` instead of name.
255+
* `path`: reserved as a uri part.
256+
* `query`: reserved as a uri part.
257+
* `remote-*`: any key starting with `remote-` is reserved for future use.
258+
* `remote-transport`: determines which transport to load. It is always the transport class named "declassified".
259+
* `run-on`: Bolt uses this keyword to determine which target to proxy to. Transports should not rely on this key.
260+
261+
> Note: Bolt inventory requires you to set a name for every target and always use it for the URI. This means that there is no way to specify `host` separately from the host section of the `name` when parsed as a URI.
267262
268263
After the device class, transport class and transport schema have been implemented, `puppet device` will be able to use the new provider, and supply it (through the device class) with the URL specified in the [`device.conf`](https://puppet.com/docs/puppet/5.3/config_file_device.html).
269264

Rakefile

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ end
1414
require 'rspec/core/rake_task'
1515

1616
RSpec::Core::RakeTask.new(:spec) do |t|
17-
Rake::Task[:spec_prep].invoke
1817
# thanks to the fixtures/modules/ symlinks this needs to exclude fixture modules explicitely
1918
excludes = ['fixtures/**/*.rb,fixtures/modules/*/**/*.rb']
2019
if RUBY_PLATFORM == 'java'
@@ -24,12 +23,20 @@ RSpec::Core::RakeTask.new(:spec) do |t|
2423
t.exclude_pattern = "spec/{#{excludes.join ','}}"
2524
end
2625

26+
task :spec => :spec_prep
27+
2728
namespace :spec do
2829
desc 'Run RSpec code examples with coverage collection'
2930
task :coverage do
3031
ENV['COVERAGE'] = 'yes'
3132
Rake::Task['spec'].execute
3233
end
34+
35+
RSpec::Core::RakeTask.new(:unit) do |t|
36+
t.pattern = "spec/puppet/**/*_spec.rb,spec/integration/**/*_spec.rb"
37+
end
38+
39+
task :unit => :spec_prep
3340
end
3441

3542
#### LICENSE_FINDER ####

lib/puppet/resource_api/transport.rb

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,11 @@ def self.validate(name, connection_info)
5353
environment: current_environment,
5454
}
5555
end
56+
57+
if connection_info.key?(:"remote-transport")
58+
clean_bolt_attributes(transport_schema, connection_info)
59+
end
60+
5661
message_prefix = 'The connection info provided does not match the Transport Schema'
5762
transport_schema.check_schema(connection_info, message_prefix)
5863
transport_schema.validate(connection_info)
@@ -93,4 +98,36 @@ def self.current_environment
9398
end
9499
end
95100
private_class_method :current_environment
101+
102+
def self.clean_bolt_attributes(transport_schema, connection_info)
103+
context = get_context(transport_schema.name)
104+
105+
# Attributes we expect from bolt, but want to ignore if the transport does not expect them
106+
[:uri, :host, :protocol, :user, :port, :password].each do |attribute_name|
107+
if connection_info.key?(attribute_name) && !transport_schema.attributes.key?(attribute_name)
108+
context.info('Discarding superfluous bolt attribute: %{attribute_name}' % { attribute_name: attribute_name })
109+
connection_info.delete(attribute_name)
110+
end
111+
end
112+
113+
# Attributes that bolt emits, but we want to ignore if the transport does not expect them
114+
([:name, :path, :query, :"run-on", :"remote-transport", :implementations] + connection_info.keys.select { |k| k.to_s.start_with? 'remote-' }).each do |attribute_name|
115+
if connection_info.key?(attribute_name) && !transport_schema.attributes.key?(attribute_name)
116+
context.debug('Discarding bolt metaparameter: %{attribute_name}' % { attribute_name: attribute_name })
117+
connection_info.delete(attribute_name)
118+
end
119+
end
120+
121+
# remove any other attributes the transport is not prepared to handle
122+
connection_info.keys.each do |attribute_name|
123+
if connection_info.key?(attribute_name) && !transport_schema.attributes.key?(attribute_name)
124+
context.warning('Discarding unknown attribute: %{attribute_name}' % { attribute_name: attribute_name })
125+
connection_info.delete(attribute_name)
126+
end
127+
end
128+
129+
# don't return a value as we've already modified the hash
130+
nil
131+
end
132+
private_class_method :clean_bolt_attributes
96133
end

spec/puppet/resource_api/transport_spec.rb

Lines changed: 36 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -294,20 +294,50 @@ class Wibble; end
294294
end
295295
end
296296

297-
context 'when the transport being validated has been registered' do
298-
let(:schema) { { name: 'validate', desc: 'a minimal connection', connection_info: {} } }
297+
context 'with a registered transport' do
298+
let(:attributes) { {} }
299+
let(:schema) { { name: 'validate', desc: 'a minimal connection', connection_info: attributes } }
299300
let(:schema_def) { instance_double('Puppet::ResourceApi::TransportSchemaDef', 'schema_def') }
300301

301-
it 'validates the connection_info' do
302+
before(:each) do
302303
allow(Puppet::ResourceApi::TransportSchemaDef).to receive(:new).with(schema).and_return(schema_def)
304+
allow(schema_def).to receive(:attributes).with(no_args).and_return(attributes)
305+
allow(schema_def).to receive(:name).with(no_args).and_return(schema[:name])
303306

304307
described_class.register(schema)
308+
end
305309

310+
it 'validates the connection_info' do
306311
expect(described_class).not_to receive(:require).with('puppet/transport/schema/validate')
307-
expect(schema_def).to receive(:check_schema).with('connection_info', kind_of(String)).and_return(nil)
308-
expect(schema_def).to receive(:validate).with('connection_info').and_return(nil)
312+
expect(schema_def).to receive(:check_schema).with({}, kind_of(String)).and_return(nil)
313+
expect(schema_def).to receive(:validate).with({}).and_return(nil)
309314

310-
described_class.send :validate, 'validate', 'connection_info'
315+
described_class.send :validate, 'validate', {}
316+
end
317+
318+
context 'when validating bolt _target information' do
319+
let(:attributes) { { host: {}, foo: {} } }
320+
let(:context) { instance_double(Puppet::ResourceApi::PuppetContext, 'context') }
321+
322+
it 'cleans the connection_info' do
323+
allow(described_class).to receive(:get_context).with('validate').and_return(context)
324+
325+
expect(schema_def).to receive(:check_schema).with({ host: 'host value', foo: 'foo value' }, kind_of(String)).and_return(nil)
326+
expect(schema_def).to receive(:validate).with(host: 'host value', foo: 'foo value').and_return(nil)
327+
328+
expect(context).to receive(:debug).with('Discarding bolt metaparameter: query')
329+
expect(context).to receive(:debug).with('Discarding bolt metaparameter: remote-transport')
330+
expect(context).to receive(:debug).with('Discarding bolt metaparameter: remote-reserved')
331+
expect(context).to receive(:info).with('Discarding superfluous bolt attribute: user')
332+
expect(context).to receive(:warning).with('Discarding unknown attribute: bar')
333+
described_class.send :validate, 'validate', :"remote-transport" => 'validate',
334+
:host => 'host value',
335+
:foo => 'foo value',
336+
:user => 'superfluous bolt value',
337+
:query => 'metaparameter value',
338+
:"remote-reserved" => 'reserved value',
339+
:bar => 'unknown attribute'
340+
end
311341
end
312342
end
313343
end

0 commit comments

Comments
 (0)