Add a type and provider to manage apt keys.

This commits introduces:
 * The apt_key type;
 * The apt_key provider;
 * Unit tests for the type;
 * Beaker/acceptance tests for the type/provider.

The idea behind apt_key is that apt::key will simply become a wrapper
that uses apt_key. Being a native type/provider apt_key is a lot less
error prone than the current exec behaviour of apt::key and adds a few
nice bonuses like inventory capabilities for mcollective users.
This commit is contained in:
Daniele Sluijters 2014-01-17 09:11:56 +01:00
parent a797bd7412
commit 374101612c
4 changed files with 829 additions and 0 deletions

View file

@ -0,0 +1,170 @@
require 'date'
require 'open-uri'
require 'tempfile'
Puppet::Type.type(:apt_key).provide(:apt_key) do
KEY_LINE = {
:date => '[0-9]{4}-[0-9]{2}-[0-9]{2}',
:key_type => '(R|D)',
:key_size => '\d{4}',
:key_id => '[0-9a-fA-F]+',
:expires => 'expire(d|s)',
}
confine :osfamily => :debian
defaultfor :osfamily => :debian
commands :apt_key => 'apt-key'
def self.instances
key_array = apt_key('list').split("\n").collect do |line|
line_hash = key_line_hash(line)
next unless line_hash
expired = false
if line_hash[:key_expiry]
expired = Date.today > Date.parse(line_hash[:key_expiry])
end
new(
:name => line_hash[:key_id],
:id => line_hash[:key_id],
:ensure => :present,
:expired => expired,
:expiry => line_hash[:key_expiry],
:size => line_hash[:key_size],
:type => line_hash[:key_type] == 'R' ? :rsa : :dsa,
:created => line_hash[:key_created]
)
end
key_array.compact!
end
def self.prefetch(resources)
apt_keys = instances
resources.keys.each do |name|
if provider = apt_keys.find{ |key| key.name == name }
resources[name].provider = provider
end
end
end
def self.key_line_hash(line)
line_array = line.match(key_line_regexp).to_a
return nil if line_array.length < 5
return_hash = {
:key_id => line_array[3],
:key_size => line_array[1],
:key_type => line_array[2],
:key_created => line_array[4],
:key_expiry => nil,
}
return_hash[:key_expiry] = line_array[7] if line_array.length == 8
return return_hash
end
def self.key_line_regexp
# This regexp is trying to match the following output
# pub 4096R/4BD6EC30 2010-07-10 [expires: 2016-07-08]
# pub 1024D/CD2EFD2A 2009-12-15
regexp = /\A
pub # match only the public key, not signatures
\s+ # bunch of spaces after that
(#{KEY_LINE[:key_size]}) # size of the key, usually a multiple of 1024
#{KEY_LINE[:key_type]} # type of the key, usually R or D
\/ # separator between key_type and key_id
(#{KEY_LINE[:key_id]}) # hex id of the key
\s+ # bunch of spaces after that
(#{KEY_LINE[:date]}) # date the key was added to the keyring
# following an optional block which indicates if the key has an expiration
# date and if it has expired yet
(
\s+ # again with thes paces
\[ # we open with a square bracket
#{KEY_LINE[:expires]} # expires or expired
\: # a colon
\s+ # more spaces
(#{KEY_LINE[:date]}) # date indicating key expiry
\] # we close with a square bracket
)? # end of the optional block
\Z/x
regexp
end
def source_to_file(value)
if URI::parse(value).scheme.nil?
fail("The file #{value} does not exist") unless File.exists?(value)
value
else
begin
key = open(value).read
rescue OpenURI::HTTPError => e
fail("#{e.message} for #{resource[:source]}")
rescue SocketError
fail("could not resolve #{resource[:source]}")
else
tempfile(key)
end
end
end
def tempfile(content)
file = Tempfile.new('apt_key')
file.write content
file.close
file.path
end
def exists?
@property_hash[:ensure] == :present
end
def create
command = []
if resource[:source].nil? and resource[:content].nil?
# Breaking up the command like this is needed because it blows up
# if --recv-keys isn't the last argument.
command.push('adv', '--keyserver', resource[:server])
unless resource[:keyserver_options].nil?
command.push('--keyserver-options', resource[:keyserver_options])
end
command.push('--recv-keys', resource[:id])
elsif resource[:content]
command.push('add', tempfile(resource[:content]))
elsif resource[:source]
command.push('add', source_to_file(resource[:source]))
# In case we really screwed up, better safe than sorry.
else
fail("an unexpected condition occurred while trying to add the key: #{resource[:id]}")
end
apt_key(command)
@property_hash[:ensure] = :present
end
def destroy
apt_key('del', resource[:id])
@property_hash.clear
end
def read_only(value)
fail('This is a read-only property.')
end
mk_resource_methods
# Needed until PUP-1470 is fixed and we can drop support for Puppet versions
# before that.
def expired
@property_hash[:expired]
end
# Alias the setters of read-only properties
# to the read_only function.
alias :created= :read_only
alias :expired= :read_only
alias :expiry= :read_only
alias :size= :read_only
alias :type= :read_only
end

112
lib/puppet/type/apt_key.rb Normal file
View file

@ -0,0 +1,112 @@
require 'pathname'
Puppet::Type.newtype(:apt_key) do
@doc = <<-EOS
This type provides Puppet with the capabilities to manage GPG keys needed
by apt to perform package validation. Apt has it's own GPG keyring that can
be manipulated through the `apt-key` command.
apt_key { '4BD6EC30':
source => 'http://apt.puppetlabs.com/pubkey.gpg'
}
**Autorequires**:
If Puppet is given the location of a key file which looks like an absolute
path this type will autorequire that file.
EOS
ensurable
validate do
if self[:content] and self[:source]
fail('The properties content and source are mutually exclusive.')
end
end
newparam(:id, :namevar => true) do
desc 'The ID of the key you want to manage.'
# GPG key ID's should be either 32-bit (short) or 64-bit (long) key ID's
# and may start with the optional 0x
newvalues(/\A(0x)?[0-9a-fA-F]{8}\Z/, /\A(0x)?[0-9a-fA-F]{16}\Z/)
munge do |value|
if value.start_with?('0x')
id = value.partition('0x').last.upcase
else
id = value.upcase
end
if id.length == 16
id[8..-1]
else
id
end
end
end
newparam(:content) do
desc 'The content of, or string representing, a GPG key.'
end
newparam(:source) do
desc 'Location of a GPG key file, /path/to/file, http:// or https://'
newvalues(/\Ahttps?:\/\//, /\A\/\w+/)
end
autorequire(:file) do
if self[:source] and Pathname.new(self[:source]).absolute?
self[:source]
end
end
newparam(:server) do
desc 'The key server to fetch the key from based on the ID.'
defaultto :'keyserver.ubuntu.com'
# Need to validate this, preferably through stdlib is_fqdn
# but still working on getting to that.
end
newparam(:keyserver_options) do
desc 'Additional options to pass to apt-key\'s --keyserver-options.'
end
newproperty(:expired) do
desc <<-EOS
Indicates if the key has expired.
This property is read-only.
EOS
end
newproperty(:expiry) do
desc <<-EOS
The date the key will expire, or nil if it has no expiry date.
This property is read-only.
EOS
end
newproperty(:size) do
desc <<-EOS
The key size, usually a multiple of 1024.
This property is read-only.
EOS
end
newproperty(:type) do
desc <<-EOS
The key type, either RSA or DSA.
This property is read-only.
EOS
end
newproperty(:created) do
desc <<-EOS
Date the key was created.
This property is read-only.
EOS
end
end

View file

@ -0,0 +1,394 @@
require 'spec_helper_acceptance'
PUPPETLABS_GPG_KEY_ID = '4BD6EC30'
PUPPETLABS_APT_URL = 'apt.puppetlabs.com'
PUPPETLABS_GPG_KEY_FILE = 'pubkey.gpg'
describe 'apt_key' do
before(:each) do
shell("apt-key del #{PUPPETLABS_GPG_KEY_ID}",
:acceptable_exit_codes => [0,1,2])
end
describe 'default options' do
key_versions = {
'32bit key id' => '4BD6EC30',
'64bit key id' => '1054B7A24BD6EC30',
'32bit lowercase key id' => '4bd6ec30',
'64bit lowercase key id' => '1054b7a24bd6ec30',
'0x formatted 32bit key id' => '0x4BD6EC30',
'0x formatted 64bit key id' => '0x1054B7A24BD6EC30',
'0x formatted 32bit lowercase key id' => '0x4bd6ec30',
'0x formatted 64bit lowercase key id' => '0x1054b7a24bd6ec30',
}
key_versions.each do |key, value|
context "#{key}" do
it 'works' do
pp = <<-EOS
apt_key { 'puppetlabs':
id => '#{value}',
ensure => 'present',
}
EOS
apply_manifest(pp, :catch_failures => true)
expect(apply_manifest(pp, :catch_failures => true).exit_code).to be_zero
shell("apt-key list | grep #{PUPPETLABS_GPG_KEY_ID}")
end
end
end
context 'invalid length key id' do
it 'fails' do
pp = <<-EOS
apt_key { 'puppetlabs':
id => '4B7A24BD6EC30',
}
EOS
apply_manifest(pp, :expect_failures => true) do |r|
expect(r.stderr).to match(/Valid values match/)
end
end
end
end
describe 'ensure =>' do
context 'absent' do
it 'is removed' do
pp = <<-EOS
apt_key { 'puppetlabs':
id => '#{PUPPETLABS_GPG_KEY_ID}',
ensure => 'absent',
}
EOS
# Install the key first
shell("apt-key adv --keyserver keyserver.ubuntu.com \
--recv-keys #{PUPPETLABS_GPG_KEY_ID}")
shell("apt-key list | grep #{PUPPETLABS_GPG_KEY_ID}")
# Time to remove it using Puppet
apply_manifest(pp, :catch_failures => true)
expect(apply_manifest(pp, :catch_failures => true).exit_code).to be_zero
shell("apt-key list | grep #{PUPPETLABS_GPG_KEY_ID}",
:acceptable_exit_codes => [1])
end
end
end
describe 'content =>' do
context 'puppetlabs gpg key' do
it 'works' do
pp = <<-EOS
apt_key { 'puppetlabs':
id => '#{PUPPETLABS_GPG_KEY_ID}',
ensure => 'present',
content => "-----BEGIN PGP PUBLIC KEY BLOCK-----
Version: GnuPG v1.4.12 (GNU/Linux)
Comment: GPGTools - http://gpgtools.org
mQINBEw3u0ABEAC1+aJQpU59fwZ4mxFjqNCgfZgDhONDSYQFMRnYC1dzBpJHzI6b
fUBQeaZ8rh6N4kZ+wq1eL86YDXkCt4sCvNTP0eF2XaOLbmxtV9bdpTIBep9bQiKg
5iZaz+brUZlFk/MyJ0Yz//VQ68N1uvXccmD6uxQsVO+gx7rnarg/BGuCNaVtGwy+
S98g8Begwxs9JmGa8pMCcSxtC7fAfAEZ02cYyrw5KfBvFI3cHDdBqrEJQKwKeLKY
GHK3+H1TM4ZMxPsLuR/XKCbvTyl+OCPxU2OxPjufAxLlr8BWUzgJv6ztPe9imqpH
Ppp3KuLFNorjPqWY5jSgKl94W/CO2x591e++a1PhwUn7iVUwVVe+mOEWnK5+Fd0v
VMQebYCXS+3dNf6gxSvhz8etpw20T9Ytg4EdhLvCJRV/pYlqhcq+E9le1jFOHOc0
Nc5FQweUtHGaNVyn8S1hvnvWJBMxpXq+Bezfk3X8PhPT/l9O2lLFOOO08jo0OYiI
wrjhMQQOOSZOb3vBRvBZNnnxPrcdjUUm/9cVB8VcgI5KFhG7hmMCwH70tpUWcZCN
NlI1wj/PJ7Tlxjy44f1o4CQ5FxuozkiITJvh9CTg+k3wEmiaGz65w9jRl9ny2gEl
f4CR5+ba+w2dpuDeMwiHJIs5JsGyJjmA5/0xytB7QvgMs2q25vWhygsmUQARAQAB
tEdQdXBwZXQgTGFicyBSZWxlYXNlIEtleSAoUHVwcGV0IExhYnMgUmVsZWFzZSBL
ZXkpIDxpbmZvQHB1cHBldGxhYnMuY29tPokCPgQTAQIAKAUCTDe7QAIbAwUJA8Jn
AAYLCQgHAwIGFQgCCQoLBBYCAwECHgECF4AACgkQEFS3okvW7DAZaw//aLmE/eob
pXpIUVyCUWQxEvPtM/h/SAJsG3KoHN9u216ews+UHsL/7F91ceVXQQdD2e8CtYWF
eLNM0RSM9i/KM60g4CvIQlmNqdqhi1HsgGqInZ72/XLAXun0gabfC36rLww2kel+
aMpRf58SrSuskY321NnMEJl4OsHV2hfNtAIgw2e/zm9RhoMpGKxoHZCvFhnP7u2M
2wMq7iNDDWb6dVsLpzdlVf242zCbubPCxxQXOpA56rzkUPuJ85mdVw4i19oPIFIZ
VL5owit1SxCOxBg4b8oaMS36hEl3qtZG834rtLfcqAmqjhx6aJuJLOAYN84QjDEU
3NI5IfNRMvluIeTcD4Dt5FCYahN045tW1Rc6s5GAR8RW45GYwQDzG+kkkeeGxwEh
qCW7nOHuwZIoVJufNhd28UFn83KGJHCQt4NBBr3K5TcY6bDQEIrpSplWSDBbd3p1
IaoZY1WSDdP9OTVOSbsz0JiglWmUWGWCdd/CMSW/D7/3VUOJOYRDwptvtSYcjJc8
1UV+1zB+rt5La/OWe4UOORD+jU1ATijQEaFYxBbqBBkFboAEXq9btRQyegqk+eVp
HhzacP5NYFTMThvHuTapNytcCso5au/cMywqCgY1DfcMJyjocu4bCtrAd6w4kGKN
MUdwNDYQulHZDI+UjJInhramyngdzZLjdeGJARwEEAECAAYFAkw3wEYACgkQIVr+
UOQUcDKvEwgAoBuOPnPioBwYp8oHVPTo/69cJn1225kfraUYGebCcrRwuoKd8Iyh
R165nXYJmD8yrAFBk8ScUVKsQ/pSnqNrBCrlzQD6NQvuIWVFegIdjdasrWX6Szj+
N1OllbzIJbkE5eo0WjCMEKJVI/GTY2AnTWUAm36PLQC5HnSATykqwxeZDsJ/s8Rc
kd7+QN5sBVytG3qb45Q7jLJpLcJO6KYH4rz9ZgN7LzyyGbu9DypPrulADG9OrL7e
lUnsGDG4E1M8Pkgk9Xv9MRKao1KjYLD5zxOoVtdeoKEQdnM+lWMJin1XvoqJY7FT
DJk6o+cVqqHkdKL+sgsscFVQljgCEd0EgIkCHAQQAQgABgUCTPlA6QAKCRBcE9bb
kwUuAxdYD/40FxAeNCYByxkr/XRT0gFT+NCjPuqPWCM5tf2NIhSapXtb2+32WbAf
DzVfqWjC0G0RnQBve+vcjpY4/rJu4VKIDGIT8CtnKOIyEcXTNFOehi65xO4ypaei
BPSb3ip3P0of1iZZDQrNHMW5VcyL1c+PWT/6exXSGsePtO/89tc6mupqZtC05f5Z
XG4jswMF0U6Q5s3S0tG7Y+oQhKNFJS4sH4rHe1o5CxKwNRSzqccA0hptKy3MHUZ2
+zeHzuRdRWGjb2rUiVxnIvPPBGxF2JHhB4ERhGgbTxRZ6wZbdW06BOE8r7pGrUpU
fCw/WRT3gGXJHpGPOzFAvr3Xl7VcDUKTVmIajnpd3SoyD1t2XsvJlSQBOWbViucH
dvE4SIKQ77vBLRlZIoXXVb6Wu7Vq+eQs1ybjwGOhnnKjz8llXcMnLzzN86STpjN4
qGTXQy/E9+dyUP1sXn3RRwb+ZkdI77m1YY95QRNgG/hqh77IuWWg1MtTSgQnP+F2
7mfo0/522hObhdAe73VO3ttEPiriWy7tw3bS9daP2TAVbYyFqkvptkBb1OXRUSzq
UuWjBmZ35UlXjKQsGeUHlOiEh84aondF90A7gx0X/ktNIPRrfCGkHJcDu+HVnR7x
Kk+F0qb9+/pGLiT3rqeQTr8fYsb4xLHT7uEg1gVFB1g0kd+RQHzV74kCPgQTAQIA
KAIbAwYLCQgHAwIGFQgCCQoLBBYCAwECHgECF4AFAk/x5PoFCQtIMjoACgkQEFS3
okvW7DAIKQ/9HvZyf+LHVSkCk92Kb6gckniin3+5ooz67hSr8miGBfK4eocqQ0H7
bdtWjAILzR/IBY0xj6OHKhYP2k8TLc7QhQjt0dRpNkX+Iton2AZryV7vUADreYz4
4B0bPmhiE+LL46ET5IThLKu/KfihzkEEBa9/t178+dO9zCM2xsXaiDhMOxVE32gX
vSZKP3hmvnK/FdylUY3nWtPedr+lHpBLoHGaPH7cjI+MEEugU3oAJ0jpq3V8n4w0
jIq2V77wfmbD9byIV7dXcxApzciK+ekwpQNQMSaceuxLlTZKcdSqo0/qmS2A863Y
ZQ0ZBe+Xyf5OI33+y+Mry+vl6Lre2VfPm3udgR10E4tWXJ9Q2CmG+zNPWt73U1FD
7xBI7PPvOlyzCX4QJhy2Fn/fvzaNjHp4/FSiCw0HvX01epcersyun3xxPkRIjwwR
M9m5MJ0o4hhPfa97zibXSh8XXBnosBQxeg6nEnb26eorVQbqGx0ruu/W2m5/JpUf
REsFmNOBUbi8xlKNS5CZypH3Zh88EZiTFolOMEh+hT6s0l6znBAGGZ4m/Unacm5y
DHmg7unCk4JyVopQ2KHMoqG886elu+rm0ASkhyqBAk9sWKptMl3NHiYTRE/m9VAk
ugVIB2pi+8u84f+an4Hml4xlyijgYu05pqNvnLRyJDLd61hviLC8GYU=
=a34C
-----END PGP PUBLIC KEY BLOCK-----",
}
EOS
apply_manifest(pp, :catch_failures => true)
expect(apply_manifest(pp, :catch_failures => true).exit_code).to be_zero
shell("apt-key list | grep #{PUPPETLABS_GPG_KEY_ID}")
end
end
context 'bogus key' do
it 'fails' do
pp = <<-EOS
apt_key { 'puppetlabs':
id => '#{PUPPETLABS_GPG_KEY_ID}',
ensure => 'present',
content => 'For posterity: such content, much bogus, wow',
}
EOS
apply_manifest(pp, :expect_failures => true) do |r|
expect(r.stderr).to match(/no valid OpenPGP data found/)
end
end
end
end
describe 'server =>' do
context 'pgp.mit.edu' do
it 'works' do
pp = <<-EOS
apt_key { 'puppetlabs':
id => '#{PUPPETLABS_GPG_KEY_ID}',
ensure => 'present',
server => 'pgp.mit.edu',
}
EOS
apply_manifest(pp, :catch_failures => true)
expect(apply_manifest(pp, :catch_failures => true).exit_code).to be_zero
shell("apt-key list | grep #{PUPPETLABS_GPG_KEY_ID}")
end
end
context 'nonexistant.key.server' do
it 'fails' do
pp = <<-EOS
apt_key { 'puppetlabs':
id => '#{PUPPETLABS_GPG_KEY_ID}',
ensure => 'present',
server => 'nonexistant.key.server',
}
EOS
apply_manifest(pp, :expect_failures => true) do |r|
expect(r.stderr).to match(/Host not found/)
end
end
end
end
describe 'source =>' do
context 'http://' do
it 'works' do
pp = <<-EOS
apt_key { 'puppetlabs':
id => '#{PUPPETLABS_GPG_KEY_ID}',
ensure => 'present',
source => 'http://#{PUPPETLABS_APT_URL}/#{PUPPETLABS_GPG_KEY_FILE}',
}
EOS
apply_manifest(pp, :catch_failures => true)
expect(apply_manifest(pp, :catch_failures => true).exit_code).to be_zero
shell("apt-key list | grep #{PUPPETLABS_GPG_KEY_ID}")
end
it 'fails with a 404' do
pp = <<-EOS
apt_key { 'puppetlabs':
id => '#{PUPPETLABS_GPG_KEY_ID}',
ensure => 'present',
source => 'http://#{PUPPETLABS_APT_URL}/herpderp.gpg',
}
EOS
apply_manifest(pp, :expect_failures => true) do |r|
expect(r.stderr).to match(/404 Not Found/)
end
end
it 'fails with a socket error' do
pp = <<-EOS
apt_key { 'puppetlabs':
id => '#{PUPPETLABS_GPG_KEY_ID}',
ensure => 'present',
source => 'http://apt.puppetlabss.com/herpderp.gpg',
}
EOS
apply_manifest(pp, :expect_failures => true) do |r|
expect(r.stderr).to match(/could not resolve/)
end
end
end
context 'https://' do
it 'works' do
pp = <<-EOS
apt_key { 'puppetlabs':
id => '#{PUPPETLABS_GPG_KEY_ID}',
ensure => 'present',
source => 'https://#{PUPPETLABS_APT_URL}/#{PUPPETLABS_GPG_KEY_FILE}',
}
EOS
apply_manifest(pp, :catch_failures => true)
expect(apply_manifest(pp, :catch_failures => true).exit_code).to be_zero
shell("apt-key list | grep #{PUPPETLABS_GPG_KEY_ID}")
end
it 'fails with a 404' do
pp = <<-EOS
apt_key { 'puppetlabs':
id => '4BD6EC30',
ensure => 'present',
source => 'https://#{PUPPETLABS_APT_URL}/herpderp.gpg',
}
EOS
apply_manifest(pp, :expect_failures => true) do |r|
expect(r.stderr).to match(/404 Not Found/)
end
end
it 'fails with a socket error' do
pp = <<-EOS
apt_key { 'puppetlabs':
id => '4BD6EC30',
ensure => 'present',
source => 'https://apt.puppetlabss.com/herpderp.gpg',
}
EOS
apply_manifest(pp, :expect_failures => true) do |r|
expect(r.stderr).to match(/could not resolve/)
end
end
end
context '/path/that/exists' do
before(:each) do
shell("curl -o /tmp/puppetlabs-pubkey.gpg \
http://#{PUPPETLABS_APT_URL}/#{PUPPETLABS_GPG_KEY_FILE}")
end
after(:each) do
shell('rm /tmp/puppetlabs-pubkey.gpg')
end
it 'works' do
pp = <<-EOS
apt_key { 'puppetlabs':
id => '4BD6EC30',
ensure => 'present',
source => '/tmp/puppetlabs-pubkey.gpg',
}
EOS
apply_manifest(pp, :catch_failures => true)
expect(apply_manifest(pp, :catch_failures => true).exit_code).to be_zero
shell("apt-key list | grep #{PUPPETLABS_GPG_KEY_ID}")
end
end
context '/path/that/does/not/exist' do
it 'fails' do
pp = <<-EOS
apt_key { 'puppetlabs':
id => '#{PUPPETLABS_GPG_KEY_ID}',
ensure => 'present',
source => '/tmp/totally_bogus.file',
}
EOS
apply_manifest(pp, :expect_failures => true) do |r|
expect(r.stderr).to match(/does not exist/)
end
end
end
context '/path/that/exists/with/bogus/content' do
before(:each) do
shell('echo "here be dragons" > /tmp/fake-key.gpg')
end
after(:each) do
shell('rm /tmp/fake-key.gpg')
end
it 'fails' do
pp = <<-EOS
apt_key { 'puppetlabs':
id => '#{PUPPETLABS_GPG_KEY_ID}',
ensure => 'present',
source => '/tmp/fake-key.gpg',
}
EOS
apply_manifest(pp, :expect_failures => true) do |r|
expect(r.stderr).to match(/no valid OpenPGP data found/)
end
end
end
end
describe 'keyserver_options =>' do
context 'debug' do
it 'works' do
pp = <<-EOS
apt_key { 'puppetlabs':
id => '#{PUPPETLABS_GPG_KEY_ID}',
ensure => 'present',
keyserver_options => 'debug',
}
EOS
apply_manifest(pp, :catch_failures => true)
expect(apply_manifest(pp, :catch_failures => true).exit_code).to be_zero
shell("apt-key list | grep #{PUPPETLABS_GPG_KEY_ID}")
end
it 'fails on invalid options' do
pp = <<-EOS
apt_key { 'puppetlabs':
id => '#{PUPPETLABS_GPG_KEY_ID}',
ensure => 'present',
keyserver_options => 'this is totally bonkers',
}
EOS
apply_manifest(pp, :expect_failures => true) do |r|
expect(r.stderr).to match(/--keyserver-options this is totally/)
end
end
end
end
end

View file

@ -0,0 +1,153 @@
require 'spec_helper'
require 'puppet'
describe Puppet::Type::type(:apt_key) do
context 'only namevar 32bit key id' do
let(:resource) { Puppet::Type.type(:apt_key).new(
:id => '4BD6EC30'
)}
it 'id is set' do
resource[:id].should eq '4BD6EC30'
end
it 'name is set to id' do
resource[:name].should eq '4BD6EC30'
end
it 'keyserver is default' do
resource[:server].should eq :'keyserver.ubuntu.com'
end
it 'source is not set' do
resource[:source].should eq nil
end
it 'content is not set' do
resource[:content].should eq nil
end
end
context 'with a lowercase 32bit key id' do
let(:resource) { Puppet::Type.type(:apt_key).new(
:id => '4bd6ec30'
)}
it 'id is set' do
resource[:id].should eq '4BD6EC30'
end
end
context 'with a 64bit key id' do
let(:resource) { Puppet::Type.type(:apt_key).new(
:id => 'FFFFFFFF4BD6EC30'
)}
it 'id is set' do
resource[:id].should eq '4BD6EC30'
end
end
context 'with a 0x formatted key id' do
let(:resource) { Puppet::Type.type(:apt_key).new(
:id => '0x4BD6EC30'
)}
it 'id is set' do
resource[:id].should eq '4BD6EC30'
end
end
context 'with a 0x formatted lowercase key id' do
let(:resource) { Puppet::Type.type(:apt_key).new(
:id => '0x4bd6ec30'
)}
it 'id is set' do
resource[:id].should eq '4BD6EC30'
end
end
context 'with a 0x formatted 64bit key id' do
let(:resource) { Puppet::Type.type(:apt_key).new(
:id => '0xFFFFFFFF4BD6EC30'
)}
it 'id is set' do
resource[:id].should eq '4BD6EC30'
end
end
context 'with source' do
let(:resource) { Puppet::Type.type(:apt_key).new(
:id => '4BD6EC30',
:source => 'http://apt.puppetlabs.com/pubkey.gpg'
)}
it 'source is set to the URL' do
resource[:source].should eq 'http://apt.puppetlabs.com/pubkey.gpg'
end
end
context 'with content' do
let(:resource) { Puppet::Type.type(:apt_key).new(
:id => '4BD6EC30',
:content => 'http://apt.puppetlabs.com/pubkey.gpg'
)}
it 'content is set to the string' do
resource[:content].should eq 'http://apt.puppetlabs.com/pubkey.gpg'
end
end
context 'with keyserver' do
let(:resource) { Puppet::Type.type(:apt_key).new(
:id => '4BD6EC30',
:server => 'http://keyring.debian.org'
)}
it 'keyserver is set to Debian' do
resource[:server].should eq 'http://keyring.debian.org'
end
end
context 'validation' do
it 'raises an error if content and source are set' do
expect { Puppet::Type.type(:apt_key).new(
:id => '4BD6EC30',
:source => 'http://apt.puppetlabs.com/pubkey.gpg',
:content => 'Completely invalid as a GPG key'
)}.to raise_error(/content and source are mutually exclusive/)
end
it 'raises an error if a weird length key is used' do
expect { Puppet::Type.type(:apt_key).new(
:id => 'F4BD6EC30',
:source => 'http://apt.puppetlabs.com/pubkey.gpg',
:content => 'Completely invalid as a GPG key'
)}.to raise_error(/Valid values match/)
end
it 'raises an error when an invalid URI scheme is used in source' do
expect { Puppet::Type.type(:apt_key).new(
:id => '4BD6EC30',
:source => 'hkp://pgp.mit.edu'
)}.to raise_error(/Valid values match/)
end
it 'allows the http URI scheme in source' do
expect { Puppet::Type.type(:apt_key).new(
:id => '4BD6EC30',
:source => 'http://pgp.mit.edu'
)}.to_not raise_error
end
it 'allows the https URI scheme in source' do
expect { Puppet::Type.type(:apt_key).new(
:id => '4BD6EC30',
:source => 'https://pgp.mit.edu'
)}.to_not raise_error
end
it 'allows an absolute path in source' do
expect { Puppet::Type.type(:apt_key).new(
:id => '4BD6EC30',
:source => '/path/to/a/file'
)}.to_not raise_error
end
end
end