Skip to content

Commit aa2b34c

Browse files
seanmilmichaeltlombardi
authored andcommitted
(puppetlabsGH-231) Add default to transport attributes
Add the ability to specify a default to transport connection_info attributes which will be used in the event no value for the given connection_info attribute is provided.
1 parent ab1a564 commit aa2b34c

File tree

7 files changed

+196
-5
lines changed

7 files changed

+196
-5
lines changed

lib/puppet/resource_api/transport.rb

+13
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,7 @@ def self.validate(name, connection_info)
103103
clean_bolt_attributes(transport_schema, connection_info)
104104
end
105105

106+
apply_defaults(transport_schema, connection_info)
106107
message_prefix = 'The connection info provided does not match the Transport Schema'
107108
transport_schema.check_schema(connection_info, message_prefix)
108109
transport_schema.validate(connection_info)
@@ -128,6 +129,18 @@ def self.wrap_sensitive(name, connection_info)
128129
end
129130
private_class_method :wrap_sensitive
130131

132+
def self.apply_defaults(transport_schema, connection_info)
133+
context = get_context(transport_schema.name)
134+
transport_schema.attributes.each do |attr_name, options|
135+
if options.key?(:default) && connection_info.key?(attr_name) == false
136+
context.debug('Using default value for attribute: %{attribute_name}, value: %{default_value}' % { attribute_name: attr_name, default_value: options[:default].inspect })
137+
connection_info[attr_name] = options[:default]
138+
end
139+
end
140+
connection_info
141+
end
142+
private_class_method :apply_defaults
143+
131144
def self.transports
132145
@transports ||= {}
133146
end
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
# frozen_string_literal: true
2+
3+
require 'spec_helper'
4+
require 'tempfile'
5+
require 'open3'
6+
7+
RSpec.describe 'a transport' do
8+
let(:common_args) { '--verbose --trace --debug --strict=error --modulepath spec/fixtures' }
9+
10+
before(:all) do
11+
FileUtils.mkdir_p(File.expand_path('~/.puppetlabs/opt/puppet/cache/devices/the_node/state'))
12+
end
13+
14+
describe 'using `puppet device`' do
15+
let(:common_args) { super() + ' --target the_node' }
16+
let(:device_conf) { Tempfile.new('device.conf') }
17+
let(:device_conf_content) do
18+
<<DEVICE_CONF
19+
[the_node]
20+
type test_device_default
21+
url file://#{device_credentials.path}
22+
DEVICE_CONF
23+
end
24+
let(:device_credentials) { Tempfile.new('credentials.txt') }
25+
26+
def is_device_apply_supported?
27+
Gem::Version.new(Puppet::PUPPETVERSION) >= Gem::Version.new('5.3.6') && Gem::Version.new(Puppet::PUPPETVERSION) != Gem::Version.new('5.4.0')
28+
end
29+
30+
before(:each) do
31+
skip "No device --apply in puppet before v5.3.6 nor in v5.4.0 (v#{Puppet::PUPPETVERSION} is installed)" unless is_device_apply_supported?
32+
device_conf.write(device_conf_content)
33+
device_conf.close
34+
35+
device_credentials.write(device_credentials_content)
36+
device_credentials.close
37+
end
38+
39+
after(:each) do
40+
device_conf.unlink
41+
device_credentials.unlink
42+
end
43+
44+
context 'when all values are supplied' do
45+
let(:device_credentials_content) do
46+
<<DEVICE_CREDS
47+
{
48+
username: foo
49+
default_string: other
50+
optional_default: bar
51+
array_default: [z]
52+
}
53+
DEVICE_CREDS
54+
end
55+
56+
it 'does not use default values' do
57+
Tempfile.create('apply_success') do |f|
58+
f.write 'notify { "foo": }'
59+
f.close
60+
61+
stdout_str, status = Open3.capture2e("puppet device #{common_args} --deviceconfig #{device_conf.path} --apply #{f.path}")
62+
expect(status.exitstatus).to eq 0
63+
expect(stdout_str).not_to match %r{Value type mismatch}
64+
expect(stdout_str).not_to match %r{Error}
65+
66+
expect(stdout_str).to match %r{transport connection_info:}
67+
expect(stdout_str).to match %r{:username=>"foo"}
68+
expect(stdout_str).to match %r{:default_string=>"other"}
69+
expect(stdout_str).to match %r{:optional_default=>"bar"}
70+
expect(stdout_str).to match %r{:array_default=>\["z"\]}
71+
end
72+
end
73+
end
74+
75+
context 'when no values supplied for parameters with defaults' do
76+
let(:device_credentials_content) do
77+
<<DEVICE_CREDS
78+
{
79+
username: foo
80+
}
81+
DEVICE_CREDS
82+
end
83+
84+
it 'uses the defaults specified' do
85+
Tempfile.create('apply_success') do |f|
86+
f.write 'notify { "foo": }'
87+
f.close
88+
89+
stdout_str, status = Open3.capture2e("puppet device #{common_args} --deviceconfig #{device_conf.path} --apply #{f.path}")
90+
expect(stdout_str).not_to match %r{Value type mismatch}
91+
expect(stdout_str).not_to match %r{Error}
92+
93+
expect(stdout_str).to match %r{Debug: test_device_default: Using default value for attribute: default_string, value: "default_value"}
94+
expect(stdout_str).to match %r{Debug: test_device_default: Using default value for attribute: optional_default, value: "another_default_value"}
95+
expect(stdout_str).to match %r{Debug: test_device_default: Using default value for attribute: array_default, value: \["a", "b"\]}
96+
expect(stdout_str).to match %r{transport connection_info:}
97+
expect(stdout_str).to match %r{:username=>"foo"}
98+
expect(stdout_str).to match %r{:default_string=>"default_value"}
99+
expect(stdout_str).to match %r{:optional_default=>"another_default_value"}
100+
expect(stdout_str).to match %r{:array_default=>\["a", "b"\]}
101+
102+
expect(status.exitstatus).to eq 0
103+
end
104+
end
105+
end
106+
end
107+
end
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
require 'puppet/resource_api'
2+
3+
Puppet::ResourceApi.register_transport(
4+
name: 'test_device_default', # points at class Puppet::Transport::TestDeviceDefault
5+
desc: 'Connects to a device',
6+
connection_info: {
7+
username: {
8+
type: 'String',
9+
desc: 'The name of the resource you want to manage.',
10+
},
11+
default_string: {
12+
type: 'String',
13+
desc: 'A string with a default.',
14+
default: 'default_value',
15+
},
16+
optional_default: {
17+
type: 'Optional[String]',
18+
desc: 'An optional string with a default.',
19+
default: 'another_default_value',
20+
},
21+
array_default: {
22+
type: 'Optional[Array[String]]',
23+
desc: 'An array of defaults.',
24+
default: ['a', 'b']
25+
},
26+
},
27+
)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
module Puppet::Transport
2+
# a transport for a test_device
3+
class TestDeviceDefault
4+
def initialize(_context, connection_info);
5+
puts "transport connection_info: #{connection_info}"
6+
end
7+
8+
def facts(_context)
9+
{ 'foo' => 'bar' }
10+
end
11+
12+
def verify(_context)
13+
return true
14+
end
15+
16+
def close(_context)
17+
return
18+
end
19+
end
20+
21+
end
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
require 'puppet/resource_api/transport/wrapper'
2+
3+
class Puppet::Util::NetworkDevice; end
4+
5+
module Puppet::Util::NetworkDevice::Test_device_default # rubocop:disable Style/ClassAndModuleCamelCase
6+
# The main class for handling the connection and command parsing to the IOS Catalyst device
7+
class Device < Puppet::ResourceApi::Transport::Wrapper
8+
def initialize(url_or_config, _options = {})
9+
super('test_device_default', url_or_config)
10+
end
11+
end
12+
end

spec/integration/resource_api/transport_spec.rb

+2-2
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,9 @@
2020
it 'can be called twice' do
2121
expect(Puppet::ResourceApi::Transport.list).to be_empty
2222
Puppet::ResourceApi::Transport.list_all_transports('rp_env')
23-
expect(Puppet::ResourceApi::Transport.list.length).to eq 2
23+
expect(Puppet::ResourceApi::Transport.list.length).to eq 3
2424
Puppet::ResourceApi::Transport.list_all_transports('rp_env')
25-
expect(Puppet::ResourceApi::Transport.list.length).to eq 2
25+
expect(Puppet::ResourceApi::Transport.list.length).to eq 3
2626
end
2727
end
2828
end

spec/puppet/resource_api/transport_spec.rb

+14-3
Original file line numberDiff line numberDiff line change
@@ -279,11 +279,13 @@ class Wibble; end
279279
let(:attributes) { {} }
280280
let(:schema) { { name: 'validate', desc: 'a minimal connection', connection_info: attributes } }
281281
let(:schema_def) { instance_double('Puppet::ResourceApi::TransportSchemaDef', 'schema_def') }
282+
let(:context) { instance_double(Puppet::ResourceApi::PuppetContext, 'context') }
282283

283284
before(:each) do
284285
allow(Puppet::ResourceApi::TransportSchemaDef).to receive(:new).with(schema).and_return(schema_def)
285286
allow(schema_def).to receive(:attributes).with(no_args).and_return(attributes)
286287
allow(schema_def).to receive(:name).with(no_args).and_return(schema[:name])
288+
allow(described_class).to receive(:get_context).with('validate').and_return(context)
287289

288290
described_class.register(schema)
289291
end
@@ -298,11 +300,8 @@ class Wibble; end
298300

299301
context 'when validating bolt _target information' do
300302
let(:attributes) { { host: {}, foo: {} } }
301-
let(:context) { instance_double(Puppet::ResourceApi::PuppetContext, 'context') }
302303

303304
it 'cleans the connection_info' do
304-
allow(described_class).to receive(:get_context).with('validate').and_return(context)
305-
306305
expect(schema_def).to receive(:check_schema).with({ host: 'host value', foo: 'foo value' }, kind_of(String)).and_return(nil)
307306
expect(schema_def).to receive(:validate).with(host: 'host value', foo: 'foo value').and_return(nil)
308307

@@ -320,6 +319,18 @@ class Wibble; end
320319
bar: 'unknown attribute'
321320
end
322321
end
322+
323+
context 'when applying defaults' do
324+
let(:attributes) { { host: { default: 'example.com' }, port: { default: 443 } } }
325+
326+
it 'sets defaults in the connection info' do
327+
expect(schema_def).to receive(:check_schema).with({ host: 'host value', port: 443 }, kind_of(String)).and_return(nil)
328+
expect(schema_def).to receive(:validate).with(host: 'host value', port: 443).and_return(nil)
329+
330+
expect(context).to receive(:debug).with('Using default value for attribute: port, value: 443')
331+
described_class.send :validate, 'validate', host: 'host value'
332+
end
333+
end
323334
end
324335
end
325336

0 commit comments

Comments
 (0)