Skip to content

Commit fc44c20

Browse files
authored
Merge pull request #182 from DavidS/pup-9747-bolt-attribute-filtering
(PUP-9747) Relax validation for bolt
2 parents 4836e85 + 9d80178 commit fc44c20

File tree

3 files changed

+93
-31
lines changed

3 files changed

+93
-31
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

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)