Skip to content
This repository has been archived by the owner on Jun 11, 2024. It is now read-only.

(FM 7115) return device certificates with task results #15

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ group :system_tests do
gem "beaker", *location_for(ENV['BEAKER_VERSION'] || '>= 3')
gem "beaker-pe", require: false
gem "beaker-rspec"
gem "beaker-task_helper"
gem "beaker-hostgenerator"
gem "beaker-abs", *location_for(ENV['BEAKER_ABS_VERSION'] || '~> 0.1')
gem "puppet-blacksmith", '~> 3.4', require: false
Expand Down
5 changes: 2 additions & 3 deletions manifests/run.pp
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,9 @@
$user = '--user=root'
}

# TODO: Consider removing multiple spaces using join(), delete(), rstrip().
$arguments = "device --waitforcert=0 ${user} --verbose"
$arguments = "device ${user} --waitforcert=0 --verbose"

# PUP-7412 Puppet 5.0.0 introduces '--target=root'.
# PUP-7412 Puppet 5.0.0 introduces '--target=<device>'.
$targetable = (versioncmp($::puppetversion, '5.0.0') >= 0)

$random_minute = sprintf('%02d', fqdn_rand(59, 'device_manager'))
Expand Down
64 changes: 45 additions & 19 deletions spec/acceptance/configure_spec.rb
Original file line number Diff line number Diff line change
@@ -1,38 +1,64 @@
require 'spec_helper_acceptance'

describe 'configure' do
context 'basic setup' do
it 'edit site.pp and run the agent' do
context 'device management' do
it 'define device management in site.pp on the master' do
fqdn = fact('fqdn')
pp = <<-EOS
node '#{fqdn}' {
device_manager {'bigip.example.com':
type => 'f5',
url => 'https://admin:[email protected]/',
run_interval => 30,
}
}

manifest = <<-EOS
node '#{fqdn}' {
device_manager {'bigip.example.com':
type => 'f5',
url => 'https://admin:[email protected]/',
run_interval => 30,
}

device_manager {'cisco.example.com':
type => 'cisco_ios',
credentials => {
address => '10.64.21.10',
port => 22,
username => 'root',
password => 'eq3e2jM6m8AVvT9',
enable_password => 'eq3e2jM6m8AVvT9',
},
}
}
node default {}
EOS
make_site_pp(pp)
run_agent(allow_changes: true)
run_agent(allow_changes: false)

define_site_pp(manifest)
end

it 'define device management on the proxy agent' do
run_puppet_agent(allow_changes: true)
run_puppet_agent(allow_changes: false)
end

# check device.conf is created
describe file('/etc/puppetlabs/puppet/device.conf') do
it { is_expected.to be_file }
it { is_expected.to contain %r{[bigip.example.com]} }
it { is_expected.to contain %r{type f5} }
it { is_expected.to contain %r{[cisco.example.com]} }
it { is_expected.to contain %r{type cisco_ios} }
end

# check crontab has an entry
it 'crontab entry' do
describe file('/etc/puppetlabs/puppet/devices/cisco.example.com.conf') do
it { is_expected.to be_file }
it { is_expected.to contain %r{address} }
end

it 'cron for device with run_interval on the proxy agent' do
result = on(default, 'crontab -l').stdout
expect(result).to match(%r{puppet device})
expect(result).to match(%r{bigip.example.com})
end
end

it 'run puppet device' do
# check the cert is created
# sign the cert
context 'device certificate' do
it 'purge device on the master and the proxy agent' do
run_puppet_node_purge('cisco.example.com')
reset_agent_device_cache('cisco.example.com')
end
end
end
4 changes: 3 additions & 1 deletion spec/acceptance/nodesets/vmpooler.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ HOSTS:
pe_upgrade_ver:
hypervisor: vmpooler
# hypervisor: none
# ip: m80hs0ndpo6lki0.delivery.puppetlabs.net
# ip: xxxxxxxxxxxxxxx.delivery.puppetlabs.net
platform: el-7-x86_64
template: centos-7-x86_64
roles:
Expand All @@ -19,3 +19,5 @@ CONFIG:
nfs_server: none
consoleport: 443
pooling_api: http://vmpooler.delivery.puppetlabs.net/
ssh:
keys: "~/.ssh/id_rsa-acceptance"
28 changes: 28 additions & 0 deletions spec/acceptance/run_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
require 'spec_helper_acceptance'

describe 'run' do
context 'puppet device' do
it 'generate certificate request for device on the proxy agent' do
run_puppet_device_generate_csr('cisco.example.com')
end
it 'sign certificate request on the master' do
run_puppet_cert_sign('cisco.example.com')
end
it 'run puppet device on the proxy agent' do
run_puppet_device('cisco.example.com', allow_changes: false)
end
end

context 'puppet task' do
it 'run device_manager::run_puppet_device task on the master' do
host_cert_name = fact('fqdn')
device_cert_name = 'cisco.example.com'
params = "target=#{device_cert_name}"
device_cert_fingerprint = run_puppet_cert_fingerprint(device_cert_name)
# Note: run_puppet_task from beaker-task_helper executes "puppet task" "on(master".
result = run_puppet_task(task_name: 'device_manager::run_puppet_device', host: host_cert_name, params: params)
expect(result).to match(%r{status : success})
expect(result).to match(%r{fingerprint : #{device_cert_fingerprint}})
end
end
end
6 changes: 3 additions & 3 deletions spec/defines/init_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@
it { is_expected.to contain_device_manager__run__via_cron__device(title) }
it {
is_expected.to contain_cron('run puppet device').with(
'command' => '/opt/puppetlabs/puppet/bin/puppet device --waitforcert=0 --user=root --verbose',
'command' => '/opt/puppetlabs/puppet/bin/puppet device --user=root --waitforcert=0 --verbose',
)
}
end
Expand Down Expand Up @@ -137,7 +137,7 @@
it { is_expected.to contain_device_manager__run__via_cron__device(title) }
it {
is_expected.to contain_cron("run puppet device target #{title}").with(
'command' => "/opt/puppetlabs/puppet/bin/puppet device --waitforcert=0 --user=root --verbose --target=#{title}",
'command' => "/opt/puppetlabs/puppet/bin/puppet device --user=root --waitforcert=0 --verbose --target=#{title}",
'hour' => '*',
)
}
Expand Down Expand Up @@ -207,7 +207,7 @@
it { is_expected.to contain_device_manager__run__via_exec__device(title) }
it {
is_expected.to contain_exec("run puppet device target #{title}").with(
'command' => %("/opt/puppetlabs/puppet/bin/puppet" device --waitforcert=0 --user=root --verbose --target=#{title}),
'command' => %("/opt/puppetlabs/puppet/bin/puppet" device --user=root --waitforcert=0 --verbose --target=#{title}),
)
}
end
Expand Down
109 changes: 69 additions & 40 deletions spec/spec_helper_acceptance.rb
Original file line number Diff line number Diff line change
@@ -1,33 +1,83 @@
require 'beaker-rspec/spec_helper'
require 'beaker-rspec/helpers/serverspec'
require 'beaker-task_helper'
require 'beaker/puppet_install_helper'
require 'beaker/module_install_helper'
require 'pry'

run_puppet_install_helper
install_module_on(hosts)
install_module_dependencies_on(hosts)
if ENV['BEAKER_provision'] != 'no'
run_puppet_install_helper
install_module_on(hosts)
install_module_dependencies_on(hosts)
end

RSpec.configure do |c|
c.before :suite do
run_puppet_access_login(user: 'admin')
unless ENV['BEAKER_TESTMODE'] == 'local'
unless ENV['BEAKER_provision'] == 'no'
install_module_from_forge('puppetlabs-cisco_ios', '0.2.0')
install_module_from_forge('f5-f5', '1.8.0')
end
hosts.each do |host|
end
end
end
end

def make_site_pp(pp)
base_path = '/etc/puppetlabs/code/environments/production/'
path = File.join(base_path, 'manifests')
def define_site_pp(manifest)
path = '/etc/puppetlabs/code/environments/production/manifests'
on master, "mkdir -p #{path}"
create_remote_file(master, File.join(path, 'site.pp'), pp)
return if ENV['PUPPET_INSTALL_TYPE'] != 'foss'
create_remote_file(master, File.join(path, 'site.pp'), manifest)
return unless ENV['PUPPET_INSTALL_TYPE'] == 'foss'
on master, "chown -R #{master['user']}:#{master['group']} #{path}"
on master, "chmod -R 0755 #{path}"
on master, "service #{master['puppetservice']} restart"
wait_for_master(3)
end

def run_device(options = { allow_changes: true })
acceptable_exit_codes = if options[:allow_changes] == false
0
else
[0, 2]
end
on(default, puppet('device', '--verbose', '--trace'), acceptable_exit_codes: acceptable_exit_codes) do |result|
# on(default, puppet('device','--verbose','--color','false','--user','root','--trace','--server',master.to_s), { :acceptable_exit_codes => acceptable_exit_codes }) do |result|
def run_puppet_node_purge(cert_name)
on(master, puppet('node', 'purge', cert_name), acceptable_exit_codes: [0, 1]).stdout
end

def run_puppet_cert_sign(cert_name = nil)
if cert_name
on(master, puppet('cert', 'sign', cert_name), acceptable_exit_codes: [0, 1]).stdout
else
on(master, puppet('cert', 'sign', '--all'), acceptable_exit_codes: [0, 1]).stdout
end
end

def run_puppet_cert_fingerprint(cert_name)
fingerprint = nil
result = on(master, puppet('cert', 'fingerprint', cert_name), acceptable_exit_codes: 0).stdout
if (matched = result.chomp.match(%r{\(\w+\) (?<fingerprint>.*)$}))
fingerprint = matched[:fingerprint]
end
fingerprint
end

def reset_agent_device_cache(cert_name)
on default, "rm -rf /opt/puppetlabs/puppet/cache/devices/#{cert_name}"
end

def run_puppet_agent(options = { allow_changes: true })
acceptable_exit_codes = (options[:allow_changes] == false) ? 0 : [0, 2]
on(default, puppet('agent', '-t'), acceptable_exit_codes: acceptable_exit_codes)
end

def run_puppet_device_generate_csr(cert_name)
acceptable_exit_codes = 1
on(default, puppet('device', '--verbose', '--waitforcert=0', '--target', cert_name), acceptable_exit_codes: acceptable_exit_codes) do |result|
expect(result.stdout).to match(%r{Exiting; no certificate found and waitforcert is disabled})
end
end

# Use '--trace', '--color', 'false' for more information.

def run_puppet_device(cert_name, options = { allow_changes: true })
acceptable_exit_codes = (options[:allow_changes] == false) ? 0 : [0, 2]
on(default, puppet('device', '--verbose', '--waitforcert=0', '--target', cert_name), acceptable_exit_codes: acceptable_exit_codes) do |result|
if options[:allow_changes] == false
expect(result.stdout).not_to match(%r{^Notice: /Stage\[main\]})
end
Expand All @@ -36,31 +86,10 @@ def run_device(options = { allow_changes: true })
end
end

def run_resource(resource_type, resource_title = nil)
def run_puppet_device_resource(cert_name, resource_type, resource_title = nil)
if resource_title
on(master, puppet('device', '--target', 'target', '--resource', resource_type, resource_title, '--trace'), acceptable_exit_codes: [0, 1]).stdout
on(default, puppet('device', '--trace', '--target', cert_name, '--resource', resource_type, resource_title), acceptable_exit_codes: [0, 1]).stdout
else
on(master, puppet('device', '--target', 'target', '--resource', resource_type, '--trace'), acceptable_exit_codes: [0, 1]).stdout
end
end

def run_agent(options = { allow_changes: true })
acceptable_exit_codes = if options[:allow_changes] == false
0
else
[0, 2]
end
on(default, puppet('agent', '-t'), acceptable_exit_codes: acceptable_exit_codes)
end

RSpec.configure do |c|
c.before :suite do
unless ENV['BEAKER_TESTMODE'] == 'local'
unless ENV['BEAKER_provision'] == 'no'
install_module_from_forge('f5-f5', '1.8.0')
end
hosts.each do |host|
end
end
on(default, puppet('device', '--trace', '--target', cert_name, '--resource', resource_type), acceptable_exit_codes: [0, 1]).stdout
end
end
45 changes: 38 additions & 7 deletions tasks/run_puppet_device.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
require 'json'
require 'open3'
require 'puppet'
require 'puppet/ssl/certificate'
require 'puppet/ssl/host'
require 'puppet/util/network_device/config'
require 'timeout'

Expand All @@ -26,9 +28,31 @@ def read_device_configuration(target)
devices
end

# Read a device certificate and return its fingerprints.

def read_device_certificate_fingerprints(cert_name)
cert_file = File.join(Puppet[:devicedir], cert_name, 'ssl', 'certs', "#{cert_name}.pem")
if File.file?(cert_file)
begin
certificate = Puppet::SSL::Certificate.from_s(Puppet::FileSystem.read(cert_file))
rescue OpenSSL::X509::CertificateError
certificate = nil
end
end
return nil unless certificate
fingerprints = {}
fingerprints['default'] = certificate.fingerprint
ssl_host = Puppet::SSL::Host.new
mdas = ssl_host.suitable_message_digest_algorithms
mdas.each do |mda|
fingerprints[mda.to_s] = certificate.fingerprint(mda)
end
fingerprints
end

# Run 'puppet device' for each device, or just the target device.

def run_device_manager(devices, noop, timeout)
def run_puppet_device(devices, noop, timeout)
os = Facter.value(:os) || {}
osfamily = os['family']
if osfamily == 'windows'
Expand All @@ -37,6 +61,8 @@ def run_device_manager(devices, noop, timeout)
else
puppet_command = '/opt/puppetlabs/puppet/bin/puppet'
end
# PUP-1391 Puppet 5.4.0 does not require '--user=root'.
user = (Gem::Version.new(Puppet.version) > Gem::Version.new('5.4.0')) ? '' : '--user=root'
results = {}
results['error_count'] = 0

Expand All @@ -50,7 +76,7 @@ def run_device_manager(devices, noop, timeout)
result = ''

begin
Open3.popen2e(puppet_command, 'device', '--waitforcert=0', '--user=root', '--verbose', target, noop) do |_, oe, w|
Open3.popen2e(puppet_command, 'device', user, '--waitforcert=0', '--verbose', target, noop) do |_, oe, w|
begin
Timeout.timeout(timeout) do
until oe.eof?
Expand Down Expand Up @@ -87,10 +113,15 @@ def run_device_manager(devices, noop, timeout)
results['error_count'] = results['error_count'] + 1
end

results[device_name] = {
status: status,
result: result,
}
results[device_name] = {}
fingerprints = read_device_certificate_fingerprints(device_name)
if fingerprints
results[device_name]['fingerprint'] = fingerprints['default']
# Returning all fingerprints obscures other results.
# results[device_name]['fingerprints'] = fingerprints
end
results[device_name]['status'] = status
results[device_name]['result'] = result
end

results
Expand Down Expand Up @@ -151,6 +182,6 @@ def return_results(params, results)
if devices.count.zero?
return_configuration_error(params)
else
results = run_device_manager(devices, noop, timeout)
results = run_puppet_device(devices, noop, timeout)
return_results(params, results)
end