Skip to content

Commit 0234919

Browse files
committed
apply device module code
1 parent 0cb9a1a commit 0234919

File tree

5 files changed

+237
-26
lines changed

5 files changed

+237
-26
lines changed

lib/bolt/inventory.rb

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,17 @@ def data_hash
136136
}
137137
end
138138

139+
# TODO: This does two things because the applicator bypasses run_task
140+
# It would probably be cleaner to give the exectutor access to inventory
141+
# and handle this there.
142+
def run_on_target(target, params)
143+
if target.remote?
144+
[get_target(target.run_on || 'localhost'), params.merge('_target' => target.hash)]
145+
else
146+
[target, params]
147+
end
148+
end
149+
139150
#### PRIVATE ####
140151
#
141152
# For debugging only now

libexec/apply_catalog.rb

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,23 @@
3131

3232
Puppet[:default_file_terminus] = :file_server
3333

34+
if args['_target']
35+
# TODO: move some of this logic to puppet
36+
special_keys = ['type', 'debug']
37+
connection = conn_info.reject { |k, _| special_keys.include?(k) }
38+
device = OpenStruct.new(connection)
39+
device.provider = conn_info['type']
40+
device.options[:debug] = true if conn_info['debug']
41+
Puppet[:certname] = device.name
42+
Puppet::Util::NetworkDevice.init(device)
43+
44+
Puppet[:facts_terminus] = :network_device
45+
Puppet[:node_terminus] = :plain
46+
Puppet[:catalog_terminus] = :compiler
47+
# TODO this shouldn't be device speciifc
48+
Puppet[:catalog_cache_terminus] = nil
49+
end
50+
3451
exit_code = 0
3552
begin
3653
# This happens implicitly when running the Configurer, but we make it explicit here. It creates the
@@ -49,6 +66,7 @@
4966
end
5067

5168
# Ensure custom facts are available for provider suitability tests
69+
# TODO: skip this for devices?
5270
facts = Puppet::Node::Facts.indirection.find(SecureRandom.uuid, environment: env)
5371

5472
report = if Puppet::Util::Package.versioncmp(Puppet.version, '5.0.0') > 0
@@ -57,8 +75,11 @@
5775
Puppet::Transaction::Report.new('apply')
5876
end
5977

60-
Puppet.override(current_environment: env,
61-
loaders: Puppet::Pops::Loaders.new(env)) do
78+
overrides = { current_environment: env,
79+
loaders: Puppet::Pops::Loaders.new(env) }
80+
ovierrides[:network_device] = true if args['_target']
81+
82+
Puppet.override(overrides) do
6283
catalog = Puppet::Resource::Catalog.from_data_hash(args['catalog'])
6384
catalog.environment = env.name.to_s
6485
catalog.environment_instance = env

libexec/custom_facts.rb

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
require 'json'
55
require 'puppet'
66
require 'puppet/module_tool/tar'
7+
require 'puppet/util/network_device'
78
require 'tempfile'
89

910
args = JSON.parse(STDIN.read)
@@ -19,6 +20,19 @@
1920
cli << '--modulepath' << moduledir
2021
Puppet.initialize_settings(cli)
2122

23+
if (conn_info = args['_target'])
24+
special_keys = ['type', 'debug']
25+
connection = conn_info.reject { |k, _| special_keys.include?(k) }
26+
device = OpenStruct.new(connection)
27+
device.provider = conn_info['type']
28+
device.options[:debug] = true if conn_info['debug']
29+
Puppet[:facts_terminus] = :network_device
30+
Puppet[:certname] = device.name
31+
Puppet::Util::NetworkDevice.init(device)
32+
puts "device: #{device}"
33+
exit 1
34+
end
35+
2236
Tempfile.open('plugins.tar.gz') do |plugins|
2337
File.binwrite(plugins, Base64.decode64(args['plugins']))
2438
Puppet::ModuleTool::Tar.instance.unpack(plugins, moduledir, Etc.getlogin || Etc.getpwuid.name)
@@ -30,6 +44,8 @@
3044
end
3145

3246
facts = Puppet::Node::Facts.indirection.find(SecureRandom.uuid, environment: env)
47+
# TODO: the device command does this should we?
48+
facts.name = facts.values['clientcert']
3349
puts(facts.values.to_json)
3450
end
3551

spec/integration/apply_spec.rb

Lines changed: 65 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -60,29 +60,29 @@ def agent_version_inventory
6060
end
6161

6262
context "when running against puppet 5 or puppet 6" do
63-
before(:all) do
64-
# install puppet5
65-
result = run_task('puppet_agent::install', 'puppet_5', { 'collection' => 'puppet5' },
66-
config: root_config, inventory: agent_version_inventory)
67-
expect(result.count).to eq(1)
68-
expect(result[0]['status']).to eq('success')
69-
70-
result = run_task('puppet_agent::version', 'puppet_5', inventory: agent_version_inventory)
71-
expect(result.count).to eq(1)
72-
expect(result[0]['status']).to eq('success')
73-
expect(result[0]['result']['version']).to match(/^5/)
74-
75-
# install puppet6
76-
result = run_task('puppet_agent::install', 'puppet_6', { 'collection' => 'puppet6' },
77-
config: root_config, inventory: agent_version_inventory)
78-
expect(result.count).to eq(1)
79-
expect(result[0]['status']).to eq('success')
80-
81-
result = run_task('puppet_agent::version', 'puppet_6', inventory: agent_version_inventory)
82-
expect(result.count).to eq(1)
83-
expect(result[0]['status']).to eq('success')
84-
expect(result[0]['result']['version']).to match(/^6/)
85-
end
63+
#before(:all) do
64+
# # install puppet5
65+
# result = run_task('puppet_agent::install', 'puppet_5', { 'collection' => 'puppet5' },
66+
# config: root_config, inventory: agent_version_inventory)
67+
# expect(result.count).to eq(1)
68+
# expect(result[0]).to include('status' => 'success')
69+
70+
# result = run_task('puppet_agent::version', 'puppet_5', inventory: agent_version_inventory)
71+
# expect(result.count).to eq(1)
72+
# expect(result[0]['status']).to eq('success')
73+
# expect(result[0]['result']['version']).to match(/^5/)
74+
75+
# # install puppet6
76+
# result = run_task('puppet_agent::install', 'puppet_6', { 'collection' => 'puppet6' },
77+
# config: root_config, inventory: agent_version_inventory)
78+
# expect(result.count).to eq(1)
79+
# expect(result[0]['status']).to eq('success')
80+
81+
# result = run_task('puppet_agent::version', 'puppet_6', inventory: agent_version_inventory)
82+
# expect(result.count).to eq(1)
83+
# expect(result[0]['status']).to eq('success')
84+
# expect(result[0]['result']['version']).to match(/^6/)
85+
#end
8686

8787
it 'runs a ruby task' do
8888
with_tempfile_containing('inventory', YAML.dump(agent_version_inventory), '.yaml') do |inv|
@@ -116,6 +116,47 @@ def agent_version_inventory
116116
expect(result['result']['stdout']).to match(/not found/)
117117
end
118118
end
119+
120+
context "when running against device targets" do
121+
let(:device_url) { "file:///tmp/#{SecureRandom.uuid}.json" }
122+
let(:device_inventory) do
123+
device_group = { 'name' => 'device_targets',
124+
'nodes' => [
125+
{ 'name' => "puppet5_device",
126+
'config' => { 'remote' => { 'run-on' => 'puppet_5' }},
127+
},
128+
{ 'name' => "puppet6_device",
129+
'config' => { 'remote' => { 'run-on' => 'puppet_6' }},
130+
},
131+
],
132+
'config' => {
133+
'transport' => 'remote',
134+
'remote' => {
135+
'device-type' => 'fake',
136+
'url' => device_url,
137+
}
138+
}
139+
}
140+
inv = agent_version_inventory
141+
inv['groups'] << device_group
142+
inv
143+
end
144+
145+
it 'gathers facts from devices' do
146+
with_tempfile_containing('inventory', YAML.dump(device_inventory), '.yaml') do |inv|
147+
results = run_cli_json(%W[plan run device_test::facts --nodes device_targets
148+
--modulepath #{modulepath} --inventoryfile #{inv.path}])
149+
150+
require 'pry'; binding.pry
151+
expect(result).to eq([])
152+
results.each do |result|
153+
expect(result['status']).to eq('success')
154+
report = result['result']['report']
155+
expect(report['resource_statuses']).to include("Notify[Apply: Hi!]")
156+
end
157+
end
158+
end
159+
end
119160
end
120161

121162
context "when installing puppet" do
@@ -303,7 +344,7 @@ def config
303344
result = run_task('puppet_agent::install', conn_uri('winrm'),
304345
{ 'collection' => 'puppet6' }, config: config)
305346
expect(result.count).to eq(1)
306-
expect(result[0]['status']).to eq('success')
347+
expect(result[0]).to include('status' => 'success')
307348

308349
result = run_task('puppet_agent::version', conn_uri('winrm'), config: config)
309350
expect(result.count).to eq(1)

spec/integration/device_spec.rb

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
# frozen_string_literal: true
2+
3+
require 'spec_helper'
4+
require 'bolt_spec/conn'
5+
require 'bolt_spec/files'
6+
require 'bolt_spec/integration'
7+
require 'bolt_spec/run'
8+
9+
describe "devices" do
10+
include BoltSpec::Conn
11+
include BoltSpec::Files
12+
include BoltSpec::Integration
13+
include BoltSpec::Run
14+
15+
let(:modulepath) { File.join(__dir__, '../fixtures/apply') }
16+
let(:config_flags) { %W[--format json --nodes #{uri} --password #{password} --modulepath #{modulepath}] + tflags }
17+
18+
describe 'over ssh', ssh: true do
19+
let(:uri) { conn_uri('ssh') }
20+
let(:password) { conn_info('ssh')[:password] }
21+
let(:tflags) { %W[--no-host-key-check --run-as root --sudo-password #{password}] }
22+
23+
let(:device_url) { "file:///tmp/#{SecureRandom.uuid}.json" }
24+
25+
def root_config
26+
{ 'modulepath' => File.join(__dir__, '../fixtures/apply'),
27+
'ssh' => {
28+
'run-as' => 'root',
29+
'sudo-password' => conn_info('ssh')[:password],
30+
'host-key-check' => false
31+
} }
32+
end
33+
34+
def agent_version_inventory
35+
{ 'groups' => [
36+
{ 'name' => 'agent_targets',
37+
'groups' => [
38+
{ 'name' => 'puppet_5',
39+
'nodes' => [conn_uri('ssh', override_port: 20023)],
40+
'config' => { 'ssh' => { 'port' => 20023 } } },
41+
{ 'name' => 'puppet_6',
42+
'nodes' => [conn_uri('ssh', override_port: 20024)],
43+
'config' => { 'ssh' => { 'port' => 20024 } } }
44+
],
45+
'config' => {
46+
'ssh' => { 'host' => conn_info('ssh')[:host],
47+
'host-key-check' => false,
48+
'user' => conn_info('ssh')[:user],
49+
'password' => conn_info('ssh')[:password],
50+
'key' => conn_info('ssh')[:key] }
51+
} },
52+
] }
53+
end
54+
55+
let(:device_inventory) do
56+
device_group = { 'name' => 'device_targets',
57+
'nodes' => [
58+
# TODO map name to url in target?
59+
{ 'name' => 'p5_device',
60+
'url' => device_url,
61+
'device-type' => 'fake',
62+
'run-on' => 'puppet_5',
63+
},
64+
{ 'name' => 'p6_device',
65+
'url' => device_url,
66+
'device-type' => 'fake',
67+
'run-on' => 'puppet_5',
68+
},
69+
]
70+
}
71+
agent_version_inventory['groups'] << device_group
72+
end
73+
74+
after(:all) do
75+
# TODO: Extract into test helper if needed in more files
76+
uri = conn_uri('ssh')
77+
inventory_data = conn_inventory
78+
config_data = root_config
79+
uninstall = '/opt/puppetlabs/bin/puppet resource package puppet-agent ensure=absent'
80+
run_command(uninstall, uri, config: config_data, inventory: inventory_data)
81+
end
82+
83+
context "when running against puppet 5 or puppet 6" do
84+
before(:all) do
85+
# install puppet5
86+
result = run_task('puppet_agent::install', 'puppet_5', { 'collection' => 'puppet5' },
87+
config: root_config, inventory: agent_version_inventory)
88+
expect(result.count).to eq(1)
89+
expect(result[0]['status']).to eq('success')
90+
91+
result = run_task('puppet_agent::version', 'puppet_5', inventory: agent_version_inventory)
92+
expect(result.count).to eq(1)
93+
expect(result[0]['status']).to eq('success')
94+
expect(result[0]['result']['version']).to match(/^5/)
95+
96+
# install puppet6
97+
result = run_task('puppet_agent::install', 'puppet_6', { 'collection' => 'puppet6' },
98+
config: root_config, inventory: agent_version_inventory)
99+
expect(result.count).to eq(1)
100+
expect(result[0]['status']).to eq('success')
101+
102+
result = run_task('puppet_agent::version', 'puppet_6', inventory: agent_version_inventory)
103+
expect(result.count).to eq(1)
104+
expect(result[0]['status']).to eq('success')
105+
expect(result[0]['result']['version']).to match(/^6/)
106+
end
107+
108+
it 'runs an apply plan' do
109+
with_tempfile_containing('inventory', YAML.dump(device_inventory), '.yaml') do |inv|
110+
results = run_cli_json(%W[plan run device_test::facts --nodes device_targets
111+
--modulepath #{modulepath} --inventoryfile #{inv.path}])
112+
require 'pry'; binding.pry
113+
results.each do |result|
114+
expect(result['status']).to eq('success')
115+
report = result['result']['report']
116+
expect(report['resource_statuses']).to include("Notify[Apply: Hi!]")
117+
end
118+
end
119+
end
120+
end
121+
end
122+
end

0 commit comments

Comments
 (0)