From: Ashley Penney Date: Mon, 7 Apr 2014 18:06:47 +0000 (-0400) Subject: Merge pull request #282 from laurenrother/security X-Git-Tag: 1.5.0~12 X-Git-Url: https://review.fuel-infra.org/gitweb?a=commitdiff_plain;h=d946753dc4c2ac376e3d1edae92d6884516ebe3a;hp=d7da1cef383663bb2ecee3d8afa612aea74219f7;p=puppet-modules%2Fpuppetlabs-apt.git Merge pull request #282 from laurenrother/security Add security warning re: short keys --- diff --git a/.travis.yml b/.travis.yml index 582efdf..c4bb092 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,37 +5,15 @@ branches: language: ruby bundler_args: --without development script: bundle exec rake spec SPEC_OPTS='--format documentation' -after_success: - - git clone -q git://github.com/puppetlabs/ghpublisher.git .forge-release - - .forge-release/publish rvm: - 1.8.7 - 1.9.3 - 2.0.0 env: matrix: - - PUPPET_GEM_VERSION="~> 2.7.0" - - PUPPET_GEM_VERSION="~> 3.0.0" - - PUPPET_GEM_VERSION="~> 3.1.0" - - PUPPET_GEM_VERSION="~> 3.2.0" - global: - - PUBLISHER_LOGIN=puppetlabs - - secure: |- - ipB/CV1rVSTXU9ZDuzrFOlzJrRmJob36tKns2xszuH4r9s5P9qivNAngRGdV - msb69xvOlzQykM0WRF+4kJ6TZ7AbMiDI+VZ8GDtsRaU5/q3BpsvFe8aato+6 - QeyFtBG62OsosTEhGws4mqiFsPDu3dHlakuJc9zevlTuhNwbKSs= + - PUPPET_GEM_VERSION="~> 3.3.0" + - PUPPET_GEM_VERSION="~> 3.4.0" matrix: fast_finish: true - exclude: - - rvm: 1.9.3 - env: PUPPET_GEM_VERSION="~> 2.7.0" - - rvm: 2.0.0 - env: PUPPET_GEM_VERSION="~> 2.7.0" - - rvm: 2.0.0 - env: PUPPET_GEM_VERSION="~> 3.0.0" - - rvm: 2.0.0 - env: PUPPET_GEM_VERSION="~> 3.1.0" - - rvm: 1.8.7 - env: PUPPET_GEM_VERSION="~> 3.2.0" notifications: email: false diff --git a/Gemfile b/Gemfile index 1e359d0..1af60fc 100644 --- a/Gemfile +++ b/Gemfile @@ -1,7 +1,7 @@ source ENV['GEM_SOURCE'] || 'https://rubygems.org' group :development, :test do - gem 'rake', :require => false + gem 'rake', '10.1.1', :require => false gem 'pry', :require => false gem 'rspec-puppet', :require => false gem 'puppet-lint', :require => false diff --git a/README.md b/README.md index 7a4e0df..8230f14 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,6 @@ Setup * NOTE: Setting the `purge_sources_list` and `purge_sources_list_d` parameters to 'true' will destroy any existing content that was not declared with Puppet. The default for these parameters is 'false'. * system repositories * authentication keys -* wget (optional) ### Beginning with APT @@ -77,9 +76,31 @@ Forces a package to be installed from a specific release. This class is particu require => Apt::Source['debian_unstable'], } +### apt_key + +A native Puppet type and provider for managing GPG keys for APT is provided by +this module. + + apt_key { 'puppetlabs': + ensure => 'present', + id => '4BD6EC30', + } + +You can additionally set the following attributes: + + * `source`: HTTP, HTTPS or FTP location of a GPG key or path to a file on the + target host; + * `content`: Instead of pointing to a file, pass the key in as a string; + * `server`: The GPG key server to use. It defaults to *keyserver.ubuntu.com*; + * `keyserver_options`: Additional options to pass to `--keyserver`. + +Because it is a native type it can be used in and queried for with MCollective. + ### apt::key -Adds a key to the list of keys used by APT to authenticate packages. +Adds a key to the list of keys used by APT to authenticate packages. This type +uses the aforementioned `apt_key` native type. As such it no longer requires +the wget command that the old implementation depended on. apt::key { 'puppetlabs': key => '4BD6EC30', @@ -91,8 +112,6 @@ Adds a key to the list of keys used by APT to authenticate packages. key_source => 'http://pkg.jenkins-ci.org/debian/jenkins-ci.org.key', } -Note that use of `key_source` requires wget to be installed and working. - ### apt::pin Adds an apt pin for a certain release. @@ -111,6 +130,49 @@ Note you can also specifying more complex pins using distribution properties. label => 'Debian' } +If you wish to pin a number of packages you may specify the packages as a space +delimited string using the `packages` attribute or pass in an array of package +names. + +### apt::hold + +When you wish to hold a package in Puppet is should be done by passing in +'held' as the ensure attribute to the package resource. However, a lot of +public modules do not take this into account and generally do not work well +with an ensure of 'held'. + +There is an additional issue that when Puppet is told to hold a package, it +will hold it at the current version installed, there is no way to tell it in +one go to install a specific version and then hold that version without using +an exec resource that wraps `dpkg --set-selections` or `apt-mark`. + +At first glance this could also be solved by just passing the version required +to the ensure attribute but that only means that Puppet will install that +version once it processes that package. It does not inform apt that we want +this package to be held. In other words; if another package somehow wants to +upgrade this one (because of a version requirement in a dependency), apt +should not allow it. + +In order to solve this you can use apt::hold. It's implemented by creating +a preferences file with a priority of 1001, meaning that under normal +circumstances this preference will always win. Because the priority is > 1000 +apt will interpret this as 'this should be the version installed and I am +allowed to downgrade the current package if needed'. + +With this you can now set a package's ensure attribute to 'latest' but still +get the version specified by apt::hold. You can do it like this: + + apt::hold { 'vim': + version => '2:7.3.547-7', + } + +Since you might just want to hold Vim at version 7.3 and not care about the +rest you can also pass in a version with a glob: + + apt::hold { 'vim': + version => '2:7.3.*', + } + ### apt::ppa Adds a ppa repository using `add-apt-repository`. @@ -149,6 +211,27 @@ If you would like to configure your system so the source is the Puppet Labs APT key_server => 'pgp.mit.edu', } + +#### Hiera example +
+apt::sources:
+  'debian_unstable':
+      location: 'http://debian.mirror.iweb.ca/debian/'
+      release: 'unstable'
+      repos: 'main contrib non-free'
+      required_packages: 'debian-keyring debian-archive-keyring'
+      key: '55BE302B'
+      key_server: 'subkeys.pgp.net'
+      pin: '-10'
+      include_src: 'true'
+
+  'puppetlabs':
+      location: 'http://apt.puppetlabs.com'
+      repos: 'main'
+      key: '4BD6EC30'
+      key_server: 'pgp.mit.edu'
+
+ ### Testing The APT module is mostly a collection of defined resource types, which provide reusable logic that can be leveraged to manage APT. It does provide smoke tests for testing functionality on a target system, as well as spec tests for checking a compiled catalog against an expected set of resources. @@ -186,6 +269,10 @@ Implementation Adds the necessary components to get backports for Ubuntu and Debian. The release name defaults to `$lsbdistcodename`. Setting this manually can cause undefined behavior (read: universe exploding). +By default this class drops a Pin-file for Backports pinning it to a priority of 200, lower than the normal Debian archive which gets a priority of 500 to ensure your packages with `ensure => latest` don't get magically upgraded from Backports without your explicit say-so. + +If you raise the priority through the `pin_priority` parameter to *500*, identical to the rest of the Debian mirrors, normal policy goes into effect and the newest version wins/becomes the candidate apt will want to install or upgrade to. This means that if a package is available from Backports it and its dependencies will be pulled in from Backports unless you explicitly set the `ensure` attribute of the `package` resource to `installed`/`present` or a specific version. + Limitations ----------- @@ -216,6 +303,7 @@ A lot of great people have contributed to this module. A somewhat current list f * Branan Purvine-Riley * Christian G. Warden * Dan Bode +* Daniel Tremblay * Garrett Honeycutt * Jeff Wallace * Ken Barber @@ -230,3 +318,4 @@ A lot of great people have contributed to this module. A somewhat current list f * Spencer Krum * William Van Hevelingen * Zach Leslie +* Daniele Sluijters diff --git a/lib/puppet/provider/apt_key/apt_key.rb b/lib/puppet/provider/apt_key/apt_key.rb new file mode 100644 index 0000000..f8d4072 --- /dev/null +++ b/lib/puppet/provider/apt_key/apt_key.rb @@ -0,0 +1,179 @@ +require 'date' +require 'open-uri' +require 'net/ftp' +require 'tempfile' + +if RUBY_VERSION == '1.8.7' + # Mothers cry, puppies die and Ruby 1.8.7's open-uri needs to be + # monkeypatched to support passing in :ftp_passive_mode. + require File.expand_path(File.join(File.dirname(__FILE__), '..', '..', '..', + 'puppet_x', 'apt_key', 'patch_openuri.rb')) + OpenURI::Options.merge!({:ftp_active_mode => false,}) +end + +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, :ftp_active_mode => false).read + rescue OpenURI::HTTPError, Net::FTPPermError => 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 diff --git a/lib/puppet/type/apt_key.rb b/lib/puppet/type/apt_key.rb new file mode 100644 index 0000000..fa7b0c6 --- /dev/null +++ b/lib/puppet/type/apt_key.rb @@ -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, ftp://, http:// or https://' + newvalues(/\Ahttps?:\/\//, /\Aftp:\/\//, /\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 diff --git a/lib/puppet_x/apt_key/patch_openuri.rb b/lib/puppet_x/apt_key/patch_openuri.rb new file mode 100644 index 0000000..722c7bd --- /dev/null +++ b/lib/puppet_x/apt_key/patch_openuri.rb @@ -0,0 +1,63 @@ +require 'uri' +require 'stringio' +require 'time' + +module URI + class FTP + def buffer_open(buf, proxy, options) # :nodoc: + if proxy + OpenURI.open_http(buf, self, proxy, options) + return + end + require 'net/ftp' + + directories = self.path.split(%r{/}, -1) + directories.shift if directories[0] == '' # strip a field before leading slash + directories.each {|d| + d.gsub!(/%([0-9A-Fa-f][0-9A-Fa-f])/) { [$1].pack("H2") } + } + unless filename = directories.pop + raise ArgumentError, "no filename: #{self.inspect}" + end + directories.each {|d| + if /[\r\n]/ =~ d + raise ArgumentError, "invalid directory: #{d.inspect}" + end + } + if /[\r\n]/ =~ filename + raise ArgumentError, "invalid filename: #{filename.inspect}" + end + typecode = self.typecode + if typecode && /\A[aid]\z/ !~ typecode + raise ArgumentError, "invalid typecode: #{typecode.inspect}" + end + + # The access sequence is defined by RFC 1738 + ftp = Net::FTP.open(self.host) + ftp.passive = true if !options[:ftp_active_mode] + # todo: extract user/passwd from .netrc. + user = 'anonymous' + passwd = nil + user, passwd = self.userinfo.split(/:/) if self.userinfo + ftp.login(user, passwd) + directories.each {|cwd| + ftp.voidcmd("CWD #{cwd}") + } + if typecode + # xxx: typecode D is not handled. + ftp.voidcmd("TYPE #{typecode.upcase}") + end + if options[:content_length_proc] + options[:content_length_proc].call(ftp.size(filename)) + end + ftp.retrbinary("RETR #{filename}", 4096) { |str| + buf << str + options[:progress_proc].call(buf.size) if options[:progress_proc] + } + ftp.close + buf.io.rewind + end + + include OpenURI::OpenRead + end +end diff --git a/manifests/backports.pp b/manifests/backports.pp index 9cfa1c0..eafa506 100644 --- a/manifests/backports.pp +++ b/manifests/backports.pp @@ -6,6 +6,12 @@ # The ubuntu/debian release name. Defaults to $lsbdistcodename. Setting this # manually can cause undefined behavior. (Read: universe exploding) # +# [*pin_priority*] +# _default_: 200 +# +# The priority that should be awarded by default to all packages coming from +# the Debian Backports project. +# # == Examples # # include apt::backports @@ -23,10 +29,15 @@ # # Copyright 2011 Puppet Labs Inc, unless otherwise noted. class apt::backports( - $release = $::lsbdistcodename, - $location = $apt::params::backports_location + $release = $::lsbdistcodename, + $location = $::apt::params::backports_location, + $pin_priority = 200, ) inherits apt::params { + if ! is_integer($pin_priority) { + fail('$pin_priority must be an integer') + } + $release_real = downcase($release) $key = $::lsbdistid ? { 'debian' => '46925553', @@ -43,6 +54,6 @@ class apt::backports( repos => $repos, key => $key, key_server => 'pgp.mit.edu', - pin => '200', + pin => $pin_priority, } } diff --git a/manifests/force.pp b/manifests/force.pp index 70b7d47..152bb67 100644 --- a/manifests/force.pp +++ b/manifests/force.pp @@ -2,7 +2,7 @@ # force a package from a specific release define apt::force( - $release = 'testing', + $release = false, $version = false, $timeout = 300 ) { diff --git a/manifests/hold.pp b/manifests/hold.pp new file mode 100644 index 0000000..c4016c6 --- /dev/null +++ b/manifests/hold.pp @@ -0,0 +1,54 @@ +# == Define apt::hold +# +# This defined type allows you to hold a package based on the version you +# require. It's implemented by dropping an apt preferences file pinning the +# package to the version you require. +# +# === Parameters +# +# [*version*] +# The version at which you wish to pin a package. +# +# This can either be the full version, such as 4:2.11.8.1-5, or +# a partial version, such as 4:2.11.* +# +# [*package*] +# _default_: +$title+, the title/name of the resource. +# +# Name of the package that apt is to hold. +# +# [*priority*] +# _default_: +1001+ +# +# The default priority of 1001 causes this preference to always win. By +# setting the priority to a number greater than 1000 apt will always install +# this version even if it means downgrading the currently installed version. +define apt::hold( + $version, + $ensure = 'present', + $package = $title, + $priority = 1001, +){ + + validate_string($title) + validate_re($ensure, ['^present|absent',]) + validate_string($package) + validate_string($version) + + if ! is_integer($priority) { + fail('$priority must be an integer') + } + + if $ensure == 'present' { + ::apt::pin { "hold ${package} at ${version}": + packages => $package, + version => $version, + priority => $priority, + } + } else { + ::apt::pin { "hold ${package} at ${version}": + ensure => 'absent', + } + } + +} diff --git a/manifests/init.pp b/manifests/init.pp index 364ce8c..85055c8 100644 --- a/manifests/init.pp +++ b/manifests/init.pp @@ -14,6 +14,9 @@ # to true, Puppet will purge all unmanaged entries from sources.list.d # update_timeout - Overrides the exec timeout in seconds for apt-get update. # If not set defaults to Exec's default (300) +# update_tries - Number of times that `apt-get update` will be tried. Use this +# to work around transient DNS and HTTP errors. By default, the command +# will only be run once. # # Actions: # @@ -29,20 +32,35 @@ class apt( $proxy_port = '8080', $purge_sources_list = false, $purge_sources_list_d = false, + $purge_preferences = false, $purge_preferences_d = false, - $update_timeout = undef + $update_timeout = undef, + $update_tries = undef, + $sources = undef ) { + if $::osfamily != 'Debian' { + fail('This module only works on Debian or derivatives like Ubuntu') + } + include apt::params include apt::update - validate_bool($purge_sources_list, $purge_sources_list_d, $purge_preferences_d) + validate_bool($purge_sources_list, $purge_sources_list_d, + $purge_preferences, $purge_preferences_d) $sources_list_content = $purge_sources_list ? { false => undef, true => "# Repos managed by puppet.\n", } + $preferences_content = $purge_preferences ? { + false => undef, + true => "Explanation: Preferences managed by Puppet\n +Explanation: We need a bogus package line because of Debian Bug #732746\n +Package: bogus-package\n", + } + if $always_apt_update == true { Exec <| title=='apt_update' |> { refreshonly => false, @@ -75,6 +93,15 @@ class apt( notify => Exec['apt_update'], } + file { 'apt-preferences': + ensure => present, + path => "${root}/preferences", + owner => root, + group => root, + mode => '0644', + content => $preferences_content, + } + file { 'preferences.d': ensure => directory, path => $preferences_d, @@ -110,12 +137,21 @@ class apt( file { 'configure-apt-proxy': ensure => $proxy_set, path => "${apt_conf_d}/proxy", - content => "Acquire::http::Proxy \"http://${proxy_host}:${proxy_port}\";", + content => "Acquire::http::Proxy \"http://${proxy_host}:${proxy_port}\";\n", notify => Exec['apt_update'], + mode => '0644', + owner => root, + group => root, } # Need anchor to provide containment for dependencies. anchor { 'apt::update': require => Class['apt::update'], } + + # manage sources if present + if $sources != undef { + validate_hash($sources) + create_resources('apt::source', $sources) + } } diff --git a/manifests/key.pp b/manifests/key.pp index c78bf65..9ccbfcb 100644 --- a/manifests/key.pp +++ b/manifests/key.pp @@ -1,90 +1,121 @@ +# == Define: apt::key +# +# The apt::key defined type allows for keys to be added to apt's keyring +# which is used for package validation. This defined type uses the apt_key +# native type to manage keys. This is a simple wrapper around apt_key with +# a few safeguards in place. +# +# === Parameters +# +# [*key*] +# _default_: +$title+, the title/name of the resource +# +# Is a GPG key ID. This key ID is validated with a regex enforcing it +# to only contain valid hexadecimal characters, be precisely 8 or 16 +# characters long and optionally prefixed with 0x. +# +# [*ensure*] +# _default_: +present+ +# +# The state we want this key in, may be either one of: +# * +present+ +# * +absent+ +# +# [*key_content*] +# _default_: +undef+ +# +# This parameter can be used to pass in a GPG key as a +# string in case it cannot be fetched from a remote location +# and using a file resource is for other reasons inconvenient. +# +# [*key_source*] +# _default_: +undef+ +# +# This parameter can be used to pass in the location of a GPG +# key. This URI can take the form of a: +# * +URL+: ftp, http or https +# * +path+: absolute path to a file on the target system. +# +# [*key_server*] +# _default_: +undef+ +# +# The keyserver from where to fetch our GPG key. It defaults to +# undef which results in apt_key's default keyserver being used, +# currently +keyserver.ubuntu.com+. +# +# [*key_options*] +# _default_: +undef+ +# +# Additional options to pass on to `apt-key adv --keyserver-options`. define apt::key ( - $key = $title, - $ensure = present, - $key_content = false, - $key_source = false, - $key_server = 'keyserver.ubuntu.com', - $key_options = false + $key = $title, + $ensure = present, + $key_content = undef, + $key_source = undef, + $key_server = undef, + $key_options = undef, ) { - include apt::params - - $upkey = upcase($key) - # trim the key to the last 8 chars so we can match longer keys with apt-key list too - $trimmedkey = regsubst($upkey, '^.*(.{8})$', '\1') + validate_re($key, ['\A(0x)?[0-9a-fA-F]{8}\Z', '\A(0x)?[0-9a-fA-F]{16}\Z']) + validate_re($ensure, ['\Aabsent|present\Z',]) if $key_content { - $method = 'content' - } elsif $key_source { - $method = 'source' - } elsif $key_server { - $method = 'server' + validate_string($key_content) } - # This is a hash of the parts of the key definition that we care about. - # It is used as a unique identifier for this instance of apt::key. It gets - # hashed to ensure that the resource name doesn't end up being pages and - # pages (e.g. in the situation where key_content is specified). - $digest = sha1("${upkey}/${key_content}/${key_source}/${key_server}/") - - # Allow multiple ensure => present for the same key to account for many - # apt::source resources that all reference the same key. - case $ensure { - present: { - - anchor { "apt::key/${title}": } + if $key_source { + validate_re($key_source, ['\Ahttps?:\/\/', '\Aftp:\/\/', '\A\/\w+']) + } - if defined(Exec["apt::key ${upkey} absent"]) { - fail("Cannot ensure Apt::Key[${upkey}] present; ${upkey} already ensured absent") - } + if $key_server { + if !is_domain_name($key_server) { + fail('$key_server must be a valid domain name') + } + } - if !defined(Anchor["apt::key ${upkey} present"]) { - anchor { "apt::key ${upkey} present": } - } + if $key_options { + validate_string($key_options) + } - if $key_options{ - $options_string = "--keyserver-options ${key_options}" - } - else{ - $options_string = '' + case $ensure { + present: { + if defined(Anchor["apt_key ${key} absent"]){ + fail("key with id ${key} already ensured as absent") } - if !defined(Exec[$digest]) { - $digest_command = $method ? { - 'content' => "echo '${key_content}' | /usr/bin/apt-key add -", - 'source' => "wget -q '${key_source}' -O- | apt-key add -", - 'server' => "apt-key adv --keyserver '${key_server}' ${options_string} --recv-keys '${upkey}'", - } - exec { $digest: - command => $digest_command, - path => '/bin:/usr/bin', - unless => "/usr/bin/apt-key list | /bin/grep '${trimmedkey}'", - logoutput => 'on_failure', - before => Anchor["apt::key ${upkey} present"], - } + if !defined(Anchor["apt_key ${key} present"]) { + apt_key { $title: + ensure => $ensure, + id => $key, + source => $key_source, + content => $key_content, + server => $key_server, + keyserver_options => $key_options, + } -> + anchor { "apt_key ${key} present": } } - - Anchor["apt::key ${upkey} present"] -> Anchor["apt::key/${title}"] - } - absent: { - if defined(Anchor["apt::key ${upkey} present"]) { - fail("Cannot ensure Apt::Key[${upkey}] absent; ${upkey} already ensured present") + absent: { + if defined(Anchor["apt_key ${key} present"]){ + fail("key with id ${key} already ensured as present") } - exec { "apt::key ${upkey} absent": - command => "apt-key del '${upkey}'", - path => '/bin:/usr/bin', - onlyif => "apt-key list | grep '${trimmedkey}'", - user => 'root', - group => 'root', - logoutput => 'on_failure', + if !defined(Anchor["apt_key ${key} absent"]){ + apt_key { $title: + ensure => $ensure, + id => $key, + source => $key_source, + content => $key_content, + server => $key_server, + keyserver_options => $key_options, + } -> + anchor { "apt_key ${key} absent": } } } default: { - fail "Invalid 'ensure' value '${ensure}' for aptkey" + fail "Invalid 'ensure' value '${ensure}' for apt::key" } } } diff --git a/manifests/params.pp b/manifests/params.pp index b35bb1c..4e974ba 100644 --- a/manifests/params.pp +++ b/manifests/params.pp @@ -10,33 +10,46 @@ class apt::params { case $::lsbdistcodename { 'squeeze': { $backports_location = 'http://backports.debian.org/debian-backports' + $legacy_origin = true + $origins = ['${distro_id} oldstable', + '${distro_id} ${distro_codename}-security'] } 'wheezy': { $backports_location = 'http://ftp.debian.org/debian/' + $legacy_origin = false + $origins = ['origin=Debian,archive=stable,label=Debian-Security'] } default: { $backports_location = 'http://http.debian.net/debian/' + $legacy_origin = false + $origins = ['origin=Debian,archive=stable,label=Debian-Security'] } } } 'ubuntu': { case $::lsbdistcodename { - 'hardy','maverick','natty','oneiric','precise': { + 'lucid': { $backports_location = 'http://us.archive.ubuntu.com/ubuntu' - $ppa_options = '-y' + $ppa_options = undef + $legacy_origin = true + $origins = ['${distro_id} ${distro_codename}-security'] } - 'lucid': { + 'precise', 'trusty': { $backports_location = 'http://us.archive.ubuntu.com/ubuntu' - $ppa_options = undef + $ppa_options = '-y' + $legacy_origin = true + $origins = ['${distro_id}:${distro_codename}-security'] } default: { $backports_location = 'http://old-releases.ubuntu.com/ubuntu' - $ppa_options = '-y' + $ppa_options = '-y' + $legacy_origin = true + $origins = ['${distro_id}:${distro_codename}-security'] } } } default: { - fail("Unsupported osfamily (${::osfamily}) or lsbdistid (${::lsbdistid})") + fail("Unsupported lsbdistid (${::lsbdistid})") } } } diff --git a/manifests/pin.pp b/manifests/pin.pp index 402e79e..2563fc4 100644 --- a/manifests/pin.pp +++ b/manifests/pin.pp @@ -3,7 +3,7 @@ define apt::pin( $ensure = present, - $explanation = "${::caller_module_name}: ${name}", + $explanation = "${caller_module_name}: ${name}", $order = '', $packages = '*', $priority = 0, @@ -37,7 +37,13 @@ define apt::pin( # Read the manpage 'apt_preferences(5)', especially the chapter # 'Thea Effect of APT Preferences' to understand the following logic # and the difference between specific and general form - if $packages != '*' { # specific form + if is_array($packages) { + $packages_string = join($packages, ' ') + } else { + $packages_string = $packages + } + + if $packages_string != '*' { # specific form if ( $pin_release != '' and ( $origin != '' or $version != '' )) or ( $origin != '' and ( $pin_release != '' or $version != '' )) or @@ -62,7 +68,17 @@ define apt::pin( '' => "${preferences_d}/${name}.pref", default => "${preferences_d}/${order}-${name}.pref", } - file { "${name}.pref": + + # According to man 5 apt_preferences: + # The files have either no or "pref" as filename extension + # and only contain alphanumeric, hyphen (-), underscore (_) and period + # (.) characters. Otherwise APT will print a notice that it has ignored a + # file, unless that file matches a pattern in the + # Dir::Ignore-Files-Silently configuration list - in which case it will + # be silently ignored. + $file_name = regsubst($title, '[^0-9a-z\-_\.]', '_', 'IG') + + file { "${file_name}.pref": ensure => $ensure, path => $path, owner => root, diff --git a/manifests/ppa.pp b/manifests/ppa.pp index f262980..ab79b94 100644 --- a/manifests/ppa.pp +++ b/manifests/ppa.pp @@ -1,10 +1,10 @@ # ppa.pp define apt::ppa( + $ensure = 'present', $release = $::lsbdistcodename, $options = $apt::params::ppa_options, ) { - $ensure = 'present' include apt::params include apt::update @@ -69,7 +69,7 @@ define apt::ppa( ensure => 'absent', mode => '0644', owner => 'root', - gruop => 'root', + group => 'root', notify => Exec['apt_update'], } } diff --git a/manifests/source.pp b/manifests/source.pp index bc93ad9..196fc92 100644 --- a/manifests/source.pp +++ b/manifests/source.pp @@ -8,10 +8,10 @@ define apt::source( $repos = 'main', $include_src = true, $required_packages = false, - $key = false, + $key = undef, $key_server = 'keyserver.ubuntu.com', - $key_content = false, - $key_source = false, + $key_content = undef, + $key_source = undef, $pin = false, $architecture = undef ) { @@ -69,7 +69,7 @@ define apt::source( } # We do not want to remove keys when the source is absent. - if ($key != false) and ($ensure == 'present') { + if $key and ($ensure == 'present') { apt::key { "Add key: ${key} from Apt::Source ${title}": ensure => present, key => $key, diff --git a/manifests/unattended_upgrades.pp b/manifests/unattended_upgrades.pp index b0bd8ab..c57a9ee 100644 --- a/manifests/unattended_upgrades.pp +++ b/manifests/unattended_upgrades.pp @@ -14,30 +14,29 @@ # file and in /etc/cron.daily/apt # class apt::unattended_upgrades ( - $origins = ['${distro_id}:${distro_codename}-security'], - $blacklist = [], - $update = "1", - $download = "1", - $upgrade = "1", - $autoclean = "7", - $auto_fix = true, - $minimal_steps = false, + $origins = $::apt::params::origins, + $blacklist = [], + $update = "1", + $download = "1", + $upgrade = "1", + $autoclean = "7", + $auto_fix = true, + $minimal_steps = false, $install_on_shutdown = false, - $mail_to = "NONE", - $mail_only_on_error = false, - $remove_unused = true, - $auto_reboot = false, - $dl_limit = "NONE", - $enable = "1", - $backup_interval = "0", - $backup_level = "3", - $max_age = "0", - $min_age = "0", - $max_size = "0", - $download_delta = "0", - $verbose = "0", -) { - include apt::params + $mail_to = "NONE", + $mail_only_on_error = false, + $remove_unused = true, + $auto_reboot = false, + $dl_limit = "NONE", + $enable = "1", + $backup_interval = "0", + $backup_level = "3", + $max_age = "0", + $min_age = "0", + $max_size = "0", + $download_delta = "0", + $verbose = "0", +) inherits ::apt::params { validate_bool( $auto_fix, @@ -47,6 +46,7 @@ class apt::unattended_upgrades ( $remove_unused, $auto_reboot ) + validate_array($origins) package { 'unattended-upgrades': ensure => present, diff --git a/manifests/update.pp b/manifests/update.pp index ce0b78f..725bb3b 100644 --- a/manifests/update.pp +++ b/manifests/update.pp @@ -6,5 +6,7 @@ class apt::update { logoutput => 'on_failure', refreshonly => true, timeout => $apt::update_timeout, + tries => $apt::update_tries, + try_sleep => 1 } } diff --git a/spec/acceptance/apt_key_provider_spec.rb b/spec/acceptance/apt_key_provider_spec.rb new file mode 100644 index 0000000..1d39a97 --- /dev/null +++ b/spec/acceptance/apt_key_provider_spec.rb @@ -0,0 +1,446 @@ +require 'spec_helper_acceptance' + +PUPPETLABS_GPG_KEY_ID = '4BD6EC30' +PUPPETLABS_APT_URL = 'apt.puppetlabs.com' +PUPPETLABS_GPG_KEY_FILE = 'pubkey.gpg' +CENTOS_GPG_KEY_ID = 'C105B9DE' +CENTOS_REPO_URL = 'ftp.cvut.cz/centos' +CENTOS_GPG_KEY_FILE = 'RPM-GPG-KEY-CentOS-6' + +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 'ftp://' do + before(:each) do + shell("apt-key del #{CENTOS_GPG_KEY_ID}", + :acceptable_exit_codes => [0,1,2]) + end + + it 'works' do + pp = <<-EOS + apt_key { 'CentOS 6': + id => '#{CENTOS_GPG_KEY_ID}', + ensure => 'present', + source => 'ftp://#{CENTOS_REPO_URL}/#{CENTOS_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 #{CENTOS_GPG_KEY_ID}") + end + + it 'fails with a 550' do + pp = <<-EOS + apt_key { 'CentOS 6': + id => '#{CENTOS_GPG_KEY_ID}', + ensure => 'present', + source => 'ftp://#{CENTOS_REPO_URL}/herpderp.gpg', + } + EOS + + apply_manifest(pp, :expect_failures => true) do |r| + expect(r.stderr).to match(/550 Failed to open/) + end + end + + it 'fails with a socket error' do + pp = <<-EOS + apt_key { 'puppetlabs': + id => '#{PUPPETLABS_GPG_KEY_ID}', + ensure => 'present', + source => 'ftp://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 diff --git a/spec/acceptance/apt_ppa_spec.rb b/spec/acceptance/apt_ppa_spec.rb index c11da91..b665c5c 100644 --- a/spec/acceptance/apt_ppa_spec.rb +++ b/spec/acceptance/apt_ppa_spec.rb @@ -50,12 +50,51 @@ if fact('operatingsystem') == 'Ubuntu' end end + context 'ensure' do + context 'present' do + it 'works without failure' do + pp = <<-EOS + include '::apt' + apt::ppa { 'ppa:canonical-kernel-team/ppa': ensure => present } + EOS + + apply_manifest(pp, :catch_failures => true) + end + + describe 'contains the source file' do + it 'contains a kernel ppa source' do + shell('ls /etc/apt/sources.list.d/canonical-kernel-team-ppa-*', :acceptable_exit_codes => [0]) + end + end + end + end + + context 'ensure' do + context 'absent' do + it 'works without failure' do + pp = <<-EOS + include '::apt' + apt::ppa { 'ppa:canonical-kernel-team/ppa': ensure => absent } + EOS + + apply_manifest(pp, :catch_failures => true) + end + + describe 'doesnt contain the source file' do + it 'fails' do + shell('ls /etc/apt/sources.list.d/canonical-kernel-team-ppa-*', :acceptable_exit_codes => [2]) + end + end + end + end + context 'release' do context 'precise' do it 'works without failure' do pp = <<-EOS include '::apt' apt::ppa { 'ppa:canonical-kernel-team/ppa': + ensure => present, release => precise, } EOS @@ -76,6 +115,7 @@ if fact('operatingsystem') == 'Ubuntu' pp = <<-EOS include '::apt' apt::ppa { 'ppa:canonical-kernel-team/ppa': + ensure => present, release => precise, options => '-y', } diff --git a/spec/acceptance/apt_spec.rb b/spec/acceptance/apt_spec.rb index 7751391..84b7f88 100644 --- a/spec/acceptance/apt_spec.rb +++ b/spec/acceptance/apt_spec.rb @@ -175,6 +175,50 @@ describe 'apt class', :unless => UNSUPPORTED_PLATFORMS.include?(fact('osfamily') end end + context 'purge_preferences' do + context 'false' do + it 'creates a preferences file' do + shell("echo 'original' > /etc/apt/preferences") + end + + it 'should work with no errors' do + pp = <<-EOS + class { 'apt': purge_preferences => false } + EOS + + apply_manifest(pp, :catch_failures => true) + end + + describe file('/etc/apt/preferences') do + it { should be_file } + it 'is not managed by Puppet' do + shell("grep 'original' /etc/apt/preferences", {:acceptable_exit_codes => 0}) + end + end + end + + context 'true' do + it 'creates a preferences file' do + shell('touch /etc/apt/preferences') + end + + it 'should work with no errors' do + pp = <<-EOS + class { 'apt': purge_preferences => true } + EOS + + apply_manifest(pp, :catch_failures => true) + end + + describe file('/etc/apt/preferences') do + it { should be_file } + it 'is managed by Puppet' do + shell("grep 'Explanation' /etc/apt/preferences", {:acceptable_exit_codes => 0}) + end + end + end + end + context 'purge_preferences_d' do context 'false' do it 'creates a preferences file' do diff --git a/spec/acceptance/backports_spec.rb b/spec/acceptance/backports_spec.rb index 6d3f7f0..78f21fd 100644 --- a/spec/acceptance/backports_spec.rb +++ b/spec/acceptance/backports_spec.rb @@ -49,6 +49,20 @@ describe 'apt::backports class', :unless => UNSUPPORTED_PLATFORMS.include?(fact( end end + context 'pin_priority' do + it 'should work with no errors' do + pp = <<-EOS + class { 'apt::backports': pin_priority => 500, } + EOS + + apply_manifest(pp, :catch_failures => true) + end + describe file('/etc/apt/preferences.d/backports.pref') do + it { should be_file } + it { should contain "Pin-Priority: 500" } + end + end + context 'reset' do it 'deletes backport files' do shell('rm -rf /etc/apt/sources.list.d/backports.list') diff --git a/spec/acceptance/force_spec.rb b/spec/acceptance/force_spec.rb index 00572ea..a439023 100644 --- a/spec/acceptance/force_spec.rb +++ b/spec/acceptance/force_spec.rb @@ -7,7 +7,7 @@ describe 'apt::force define', :unless => UNSUPPORTED_PLATFORMS.include?(fact('os it 'should work with no errors' do pp = <<-EOS include apt - apt::force { 'vim': release => false, } + apt::force { 'vim': } EOS shell('apt-get remove -y vim') @@ -41,7 +41,7 @@ describe 'apt::force define', :unless => UNSUPPORTED_PLATFORMS.include?(fact('os it 'should work with no errors' do pp = <<-EOS include apt - apt::force { 'vim': version => '1.1.1', release => false, } + apt::force { 'vim': version => '1.1.1' } EOS shell('apt-get remove -y vim') @@ -59,7 +59,7 @@ describe 'apt::force define', :unless => UNSUPPORTED_PLATFORMS.include?(fact('os it 'should work with no errors' do pp = <<-EOS include apt - apt::force { 'vim': release => false, timeout => '1' } + apt::force { 'vim': timeout => '1' } EOS shell('apt-get clean') diff --git a/spec/acceptance/nodesets/default.yml b/spec/acceptance/nodesets/default.yml index a5f38f7..45af989 100644 --- a/spec/acceptance/nodesets/default.yml +++ b/spec/acceptance/nodesets/default.yml @@ -8,3 +8,4 @@ HOSTS: hypervisor : vagrant CONFIG: type: foss + vagrant_ssh_port_random: true diff --git a/spec/acceptance/pin_spec.rb b/spec/acceptance/pin_spec.rb index 6de1174..07fc607 100644 --- a/spec/acceptance/pin_spec.rb +++ b/spec/acceptance/pin_spec.rb @@ -91,6 +91,26 @@ describe 'apt::pin define', :unless => UNSUPPORTED_PLATFORMS.include?(fact('osfa it { should contain 'Pin: release a=vim-puppet' } end end + + context 'array' do + it 'should work with no errors' do + pp = <<-EOS + include apt + apt::pin { 'array': + ensure => present, + packages => ['apache', 'ntop'], + } + EOS + + apply_manifest(pp, :catch_failures => true) + end + + describe file('/etc/apt/preferences.d/array.pref') do + it { should be_file } + it { should contain 'Package: apache ntop' } + it { should contain 'Pin: release a=array' } + end + end end context 'release' do diff --git a/spec/acceptance/unsupported_spec.rb b/spec/acceptance/unsupported_spec.rb index 08dca76..3f74685 100644 --- a/spec/acceptance/unsupported_spec.rb +++ b/spec/acceptance/unsupported_spec.rb @@ -5,6 +5,6 @@ describe 'unsupported distributions and OSes', :if => UNSUPPORTED_PLATFORMS.incl pp = <<-EOS class { 'apt': } EOS - expect(apply_manifest(pp, :expect_failures => true).stderr).to match(/unsupported/i) + expect(apply_manifest(pp, :expect_failures => true).stderr).to match(/This module only works on Debian or derivatives like Ubuntu/i) end end diff --git a/spec/classes/apt_spec.rb b/spec/classes/apt_spec.rb index 080bc81..0b67756 100644 --- a/spec/classes/apt_spec.rb +++ b/spec/classes/apt_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' describe 'apt', :type => :class do - let(:facts) { { :lsbdistid => 'Debian' } } + let(:facts) { { :lsbdistid => 'Debian', :osfamily => 'Debian' } } let :default_params do { :disable_keys => :undef, @@ -19,6 +19,10 @@ describe 'apt', :type => :class do :purge_sources_list => true, :purge_sources_list_d => true, }, + { + :purge_preferences => true, + :purge_preferences_d => true, + }, { :disable_keys => false } @@ -86,6 +90,49 @@ describe 'apt', :type => :class do }) end } + it { + if param_hash[:purge_preferences] + should create_file('apt-preferences').with({ + :ensure => 'present', + :path => '/etc/apt/preferences', + :owner => 'root', + :group => 'root', + :mode => '0644', + :content => /Explanation/, + }) + else + should create_file('apt-preferences').with({ + :ensure => 'present', + :path => '/etc/apt/preferences', + :owner => 'root', + :group => 'root', + :mode => '0644', + :content => nil, + }) + end + } + + it { + if param_hash[:purge_preferences_d] + should create_file("preferences.d").with({ + 'path' => "/etc/apt/preferences.d", + 'ensure' => "directory", + 'owner' => "root", + 'group' => "root", + 'purge' => true, + 'recurse' => true, + }) + else + should create_file("preferences.d").with({ + 'path' => "/etc/apt/preferences.d", + 'ensure' => "directory", + 'owner' => "root", + 'group' => "root", + 'purge' => false, + 'recurse' => false, + }) + end + } it { should contain_exec("apt_update").with({ @@ -117,7 +164,7 @@ describe 'apt', :type => :class do if param_hash[:proxy_host] should contain_file('configure-apt-proxy').with( 'path' => '/etc/apt/apt.conf.d/proxy', - 'content' => "Acquire::http::Proxy \"http://#{param_hash[:proxy_host]}:#{param_hash[:proxy_port]}\";", + 'content' => "Acquire::http::Proxy \"http://#{param_hash[:proxy_host]}:#{param_hash[:proxy_port]}\";\n", 'notify' => "Exec[apt_update]" ) else diff --git a/spec/classes/backports_spec.rb b/spec/classes/backports_spec.rb index 98ad873..2f67aa4 100644 --- a/spec/classes/backports_spec.rb +++ b/spec/classes/backports_spec.rb @@ -1,6 +1,36 @@ require 'spec_helper' describe 'apt::backports', :type => :class do + describe 'when asigning a custom priority to backports' do + let :facts do + { + 'lsbdistcodename' => 'Karmic', + 'lsbdistid' => 'Ubuntu' + } + end + + context 'integer priority' do + let :params do { :pin_priority => 500 } end + + it { should contain_apt__source('backports').with({ + 'location' => 'http://old-releases.ubuntu.com/ubuntu', + 'release' => 'karmic-backports', + 'repos' => 'main universe multiverse restricted', + 'key' => '437D05B5', + 'key_server' => 'pgp.mit.edu', + 'pin' => 500, + }) + } + end + + context 'invalid priority' do + let :params do { :pin_priority => 'banana' } end + it 'should fail' do + expect { subject }.to raise_error(/must be an integer/) + end + end + end + describe 'when turning on backports for ubuntu karmic' do let :facts do @@ -16,7 +46,7 @@ describe 'apt::backports', :type => :class do 'repos' => 'main universe multiverse restricted', 'key' => '437D05B5', 'key_server' => 'pgp.mit.edu', - 'pin' => '200', + 'pin' => 200, }) } end @@ -36,7 +66,7 @@ describe 'apt::backports', :type => :class do 'repos' => 'main contrib non-free', 'key' => '46925553', 'key_server' => 'pgp.mit.edu', - 'pin' => '200', + 'pin' => 200, }) } end @@ -64,7 +94,7 @@ describe 'apt::backports', :type => :class do 'repos' => 'main contrib non-free', 'key' => '46925553', 'key_server' => 'pgp.mit.edu', - 'pin' => '200', + 'pin' => 200, }) } end diff --git a/spec/classes/debian_testing_spec.rb b/spec/classes/debian_testing_spec.rb index 2048733..6e03326 100644 --- a/spec/classes/debian_testing_spec.rb +++ b/spec/classes/debian_testing_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' describe 'apt::debian::testing', :type => :class do - let(:facts) { { :lsbdistid => 'Debian' } } + let(:facts) { { :lsbdistid => 'Debian', :osfamily => 'Debian' } } it { should contain_apt__source("debian_testing").with({ "location" => "http://debian.mirror.iweb.ca/debian/", diff --git a/spec/classes/debian_unstable_spec.rb b/spec/classes/debian_unstable_spec.rb index 70724f9..8477a39 100644 --- a/spec/classes/debian_unstable_spec.rb +++ b/spec/classes/debian_unstable_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' describe 'apt::debian::unstable', :type => :class do - let(:facts) { { :lsbdistid => 'Debian' } } + let(:facts) { { :lsbdistid => 'Debian', :osfamily => 'Debian' } } it { should contain_apt__source("debian_unstable").with({ "location" => "http://debian.mirror.iweb.ca/debian/", diff --git a/spec/classes/init_spec.rb b/spec/classes/init_spec.rb new file mode 100644 index 0000000..120b7e8 --- /dev/null +++ b/spec/classes/init_spec.rb @@ -0,0 +1,57 @@ +require 'spec_helper' +describe 'apt' do + context 'with sources defined on valid osfamily' do + let :facts do + { :osfamily => 'Debian', + :lsbdistcodename => 'precise', + :lsbdistid => 'Debian', + } + end + let(:params) { { :sources => { + 'debian_unstable' => { + 'location' => 'http://debian.mirror.iweb.ca/debian/', + 'release' => 'unstable', + 'repos' => 'main contrib non-free', + 'required_packages' => 'debian-keyring debian-archive-keyring', + 'key' => '55BE302B', + 'key_server' => 'subkeys.pgp.net', + 'pin' => '-10', + 'include_src' => true + }, + 'puppetlabs' => { + 'location' => 'http://apt.puppetlabs.com', + 'repos' => 'main', + 'key' => '4BD6EC30', + 'key_server' => 'pgp.mit.edu', + } + } } } + + it { + should contain_file('debian_unstable.list').with({ + 'ensure' => 'present', + 'path' => '/etc/apt/sources.list.d/debian_unstable.list', + 'owner' => 'root', + 'group' => 'root', + 'mode' => '0644', + 'notify' => 'Exec[apt_update]', + }) + } + + it { should contain_file('debian_unstable.list').with_content(/^deb http:\/\/debian.mirror.iweb.ca\/debian\/ unstable main contrib non-free$/) } + it { should contain_file('debian_unstable.list').with_content(/^deb-src http:\/\/debian.mirror.iweb.ca\/debian\/ unstable main contrib non-free$/) } + + it { + should contain_file('puppetlabs.list').with({ + 'ensure' => 'present', + 'path' => '/etc/apt/sources.list.d/puppetlabs.list', + 'owner' => 'root', + 'group' => 'root', + 'mode' => '0644', + 'notify' => 'Exec[apt_update]', + }) + } + + it { should contain_file('puppetlabs.list').with_content(/^deb http:\/\/apt.puppetlabs.com precise main$/) } + it { should contain_file('puppetlabs.list').with_content(/^deb-src http:\/\/apt.puppetlabs.com precise main$/) } + end +end diff --git a/spec/classes/params_spec.rb b/spec/classes/params_spec.rb index 2d3ec3c..aa330bb 100644 --- a/spec/classes/params_spec.rb +++ b/spec/classes/params_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' describe 'apt::params', :type => :class do - let(:facts) { { :lsbdistid => 'Debian' } } + let(:facts) { { :lsbdistid => 'Debian', :osfamily => 'Debian' } } let (:title) { 'my_package' } it { should contain_apt__params } @@ -11,4 +11,17 @@ describe 'apt::params', :type => :class do it "Should not contain any resources" do subject.resources.size.should == 4 end + + describe "With unknown lsbdistid" do + + let(:facts) { { :lsbdistid => 'CentOS' } } + let (:title) { 'my_package' } + + it do + expect { + should compile + }.to raise_error(Puppet::Error, /Unsupported lsbdistid/) + end + + end end diff --git a/spec/classes/unattended_upgrades_spec.rb b/spec/classes/unattended_upgrades_spec.rb index f5cad53..a2fb48b 100644 --- a/spec/classes/unattended_upgrades_spec.rb +++ b/spec/classes/unattended_upgrades_spec.rb @@ -25,16 +25,102 @@ describe 'apt::unattended_upgrades', :type => :class do } describe "origins" do - describe "with param defaults" do - let(:params) {{ }} - it { should contain_file(file_unattended).with_content(/^Unattended-Upgrade::Allowed-Origins \{\n\t"\$\{distro_id\}:\$\{distro_codename\}-security";\n\};$/) } + describe 'on Debian' do + default_facts = { :lsbdistid => 'Debian' } + context 'defaults' do + let :facts do default_facts end + it { + should contain_file(file_unattended).with_content( + /^Unattended-Upgrade::Origins-Pattern/ + ).with_content( + /"origin=Debian,archive=stable,label=Debian-Security";/ + ) + } + end + context 'defaults with custom origin' do + let :facts do default_facts end + let :params do { :origins => ['bananana']} end + it { + should contain_file(file_unattended).with_content( + /^Unattended-Upgrade::Origins-Pattern/ + ).with_content( + /"bananana";/ + ) + } + end + context 'defaults with invalid origin' do + let :facts do default_facts end + let :params do { :origins => 'bananana'} end + it { + expect {subject}.to raise_error(/is not an Array/) + } + end + context 'squeeze' do + let :facts do default_facts.merge({:lsbdistcodename => 'squeeze'}) end + it { + should contain_file(file_unattended).with_content( + /^Unattended-Upgrade::Allowed-Origins/ + ).with_content( + /"\${distro_id} \${distro_codename}-security";/ + ).with_content( + /"\${distro_id} oldstable";/ + ) + } + end + context 'wheezy' do + let :facts do default_facts.merge({:lsbdistcodename => 'wheezy'}) end + it { + should contain_file(file_unattended).with_content( + /^Unattended-Upgrade::Origins-Pattern/ + ).with_content( + /"origin=Debian,archive=stable,label=Debian-Security";/ + ) + } + end end - describe "with origins => ['ubuntu:precise-security']" do - let :params do - { :origins => ['ubuntu:precise-security'] } + describe 'on Ubuntu' do + default_facts = { :lsbdistid => 'Ubuntu' } + context 'default' do + let :facts do default_facts end + it { + should contain_file(file_unattended).with_content( + /^Unattended-Upgrade::Allowed-Origins/ + ).with_content( + /"\${distro_id}\:\${distro_codename}-security";/ + ) + } + end + context 'lucid' do + let :facts do default_facts.merge({:lsbdistcodename => 'lucid'}) end + it { + should contain_file(file_unattended).with_content( + /^Unattended-Upgrade::Allowed-Origins/ + ).with_content( + /"\${distro_id} \${distro_codename}-security";/ + ) + } + end + context 'precise' do + let :facts do default_facts.merge({:lsbdistcodename => 'precise'}) end + it { + should contain_file(file_unattended).with_content( + /^Unattended-Upgrade::Allowed-Origins/ + ).with_content( + /"\${distro_id}\:\${distro_codename}-security";/ + ) + } + end + context 'trusty' do + let :facts do default_facts.merge({:lsbdistcodename => 'trusty'}) end + it { + should contain_file(file_unattended).with_content( + /^Unattended-Upgrade::Allowed-Origins/ + ).with_content( + /"\${distro_id}\:\${distro_codename}-security";/ + ) + } end - it { should contain_file(file_unattended).with_content(/^Unattended-Upgrade::Allowed-Origins \{\n\t"ubuntu:precise-security";\n\};$/) } end end diff --git a/spec/defines/force_spec.rb b/spec/defines/force_spec.rb index b8665e6..0d3d6e5 100644 --- a/spec/defines/force_spec.rb +++ b/spec/defines/force_spec.rb @@ -11,7 +11,7 @@ describe 'apt::force', :type => :define do let :default_params do { - :release => 'testing', + :release => false, :version => false } end @@ -20,18 +20,18 @@ describe 'apt::force', :type => :define do let :params do default_params end - it { should contain_exec("/usr/bin/apt-get -y -t #{params[:release]} install #{title}").with( - :unless => "/usr/bin/test \$(/usr/bin/apt-cache policy -t #{params[:release]} #{title} | /bin/grep -E 'Installed|Candidate' | /usr/bin/uniq -s 14 | /usr/bin/wc -l) -eq 1", + it { should contain_exec("/usr/bin/apt-get -y install #{title}").with( + :unless => "/usr/bin/dpkg -s #{title} | grep -q 'Status: install'", :timeout => '300' ) } end - describe "when specifying false release parameter" do + describe "when specifying release parameter" do let :params do - default_params.merge(:release => false) + default_params.merge(:release => 'testing') end - it { should contain_exec("/usr/bin/apt-get -y install #{title}").with( - :unless => "/usr/bin/dpkg -s #{title} | grep -q 'Status: install'" + it { should contain_exec("/usr/bin/apt-get -y -t #{params[:release]} install #{title}").with( + :unless => "/usr/bin/test \$(/usr/bin/apt-cache policy -t #{params[:release]} #{title} | /bin/grep -E 'Installed|Candidate' | /usr/bin/uniq -s 14 | /usr/bin/wc -l) -eq 1" ) } end @@ -39,20 +39,20 @@ describe 'apt::force', :type => :define do let :params do default_params.merge(:version => '1') end - it { should contain_exec("/usr/bin/apt-get -y -t #{params[:release]} install #{title}=#{params[:version]}").with( - :unless => "/usr/bin/apt-cache policy -t #{params[:release]} #{title} | /bin/grep -q 'Installed: #{params[:version]}'" + it { should contain_exec("/usr/bin/apt-get -y install #{title}=#{params[:version]}").with( + :unless => "/usr/bin/dpkg -s #{title} | grep -q 'Version: #{params[:version]}'" ) } end - describe "when specifying false release and version parameters" do + describe "when specifying release and version parameters" do let :params do default_params.merge( - :release => false, + :release => 'testing', :version => '1' ) end - it { should contain_exec("/usr/bin/apt-get -y install #{title}=1").with( - :unless => "/usr/bin/dpkg -s #{title} | grep -q 'Version: #{params[:version]}'" + it { should contain_exec("/usr/bin/apt-get -y -t #{params[:release]} install #{title}=1").with( + :unless => "/usr/bin/apt-cache policy -t #{params[:release]} #{title} | /bin/grep -q 'Installed: #{params[:version]}'" ) } end end diff --git a/spec/defines/hold_spec.rb b/spec/defines/hold_spec.rb new file mode 100644 index 0000000..731e218 --- /dev/null +++ b/spec/defines/hold_spec.rb @@ -0,0 +1,100 @@ +require 'spec_helper' +describe 'apt::hold' do + let :facts do { + :osfamily => 'Debian', + :lsbdistid => 'Debian', + :lsbrelease => 'wheezy', + } end + + let :title do + 'vim' + end + + let :default_params do { + :version => '1.1.1', + } end + + describe 'default params' do + let :params do default_params end + + it 'creates an apt preferences file' do + should contain_apt__hold(title).with({ + :ensure => 'present', + :package => title, + :version => params[:version], + :priority => 1001, + }) + + should contain_apt__pin("hold #{title} at #{params[:version]}").with({ + :ensure => 'present', + :packages => title, + :version => params[:version], + :priority => 1001, + }) + end + end + + describe 'ensure => absent' do + let :params do default_params.merge({:ensure => 'absent',}) end + + it 'creates an apt preferences file' do + should contain_apt__hold(title).with({ + :ensure => params[:ensure], + }) + + should contain_apt__pin("hold #{title} at #{params[:version]}").with({ + :ensure => params[:ensure], + }) + end + end + + describe 'priority => 990' do + let :params do default_params.merge({:priority => 990,}) end + + it 'creates an apt preferences file' do + should contain_apt__hold(title).with({ + :ensure => 'present', + :package => title, + :version => params[:version], + :priority => params[:priority], + }) + + should contain_apt__pin("hold #{title} at #{params[:version]}").with({ + :ensure => 'present', + :packages => title, + :version => params[:version], + :priority => params[:priority], + }) + end + end + + describe 'validation' do + context 'version => {}' do + let :params do { :version => {}, } end + it 'should fail' do + expect { subject }.to raise_error(/is not a string/) + end + end + + context 'ensure => bananana' do + let :params do default_params.merge({:ensure => 'bananana',}) end + it 'should fail' do + expect { subject }.to raise_error(/does not match/) + end + end + + context 'package => []' do + let :params do default_params.merge({:package => [],}) end + it 'should fail' do + expect { subject }.to raise_error(/is not a string/) + end + end + + context 'priority => bananana' do + let :params do default_params.merge({:priority => 'bananana',}) end + it 'should fail' do + expect { subject }.to raise_error(/must be an integer/) + end + end + end +end diff --git a/spec/defines/key_spec.rb b/spec/defines/key_spec.rb index 4ba7b87..a85d171 100644 --- a/spec/defines/key_spec.rb +++ b/spec/defines/key_spec.rb @@ -1,124 +1,285 @@ require 'spec_helper' + describe 'apt::key', :type => :define do let(:facts) { { :lsbdistid => 'Debian' } } + GPG_KEY_ID = '4BD6EC30' + let :title do - '8347A27F' + GPG_KEY_ID end - let :default_params do - { - :key => title, - :ensure => 'present', - :key_server => "keyserver.ubuntu.com", - :key_source => false, - :key_content => false - } - end + describe 'normal operation' do + describe 'default options' do + it 'contains the apt::key' do + should contain_apt__key(title).with({ + :key => title, + :ensure => 'present', + }) + end + it 'contains the apt_key' do + should contain_apt_key(title).with({ + :id => title, + :ensure => 'present', + :source => nil, + :server => nil, + :content => nil, + :keyserver_options => nil, + }) + end + it 'contains the apt_key present anchor' do + should contain_anchor("apt_key #{title} present") + end + end + + describe 'title and key =>' do + let :title do + 'puppetlabs' + end + + let :params do { + :key => GPG_KEY_ID, + } end + + it 'contains the apt::key' do + should contain_apt__key(title).with({ + :key => GPG_KEY_ID, + :ensure => 'present', + }) + end + it 'contains the apt_key' do + should contain_apt_key(title).with({ + :id => GPG_KEY_ID, + :ensure => 'present', + :source => nil, + :server => nil, + :content => nil, + :keyserver_options => nil, + }) + end + it 'contains the apt_key present anchor' do + should contain_anchor("apt_key #{GPG_KEY_ID} present") + end + end - [{}, - { - :ensure => 'absent' - }, - { - :ensure => 'random' - }, - { - :key_source => 'ftp://ftp.example.org/key', - }, - { - :key_content => 'deadbeef', - } - ].each do |param_set| - - let :param_hash do - param_hash = default_params.merge(param_set) - param_hash[:key].upcase! if param_hash[:key] - param_hash + describe 'ensure => absent' do + let :params do { + :ensure => 'absent', + } end + + it 'contains the apt::key' do + should contain_apt__key(title).with({ + :key => title, + :ensure => 'absent', + }) + end + it 'contains the apt_key' do + should contain_apt_key(title).with({ + :id => title, + :ensure => 'absent', + :source => nil, + :server => nil, + :content => nil, + :keyserver_options => nil, + }) + end + it 'contains the apt_key absent anchor' do + should contain_anchor("apt_key #{title} absent") + end end - let :params do - param_set + describe 'key_content =>' do + let :params do { + :key_content => 'GPG key content', + } end + + it 'contains the apt::key' do + should contain_apt__key(title).with({ + :key => title, + :ensure => 'present', + :key_content => params[:key_content], + }) + end + it 'contains the apt_key' do + should contain_apt_key(title).with({ + :id => title, + :ensure => 'present', + :source => nil, + :server => nil, + :content => params[:key_content], + :keyserver_options => nil, + }) + end + it 'contains the apt_key present anchor' do + should contain_anchor("apt_key #{title} present") + end end - let :digest do - str = String.new - str << param_hash[:key].to_s << '/' - str << param_hash[:key_content].to_s << '/' - str << param_hash[:key_source].to_s << '/' - str << param_hash[:key_server].to_s << '/' - Digest::SHA1.hexdigest(str) + describe 'key_source =>' do + let :params do { + :key_source => 'http://apt.puppetlabs.com/pubkey.gpg', + } end + + it 'contains the apt::key' do + should contain_apt__key(title).with({ + :key => title, + :ensure => 'present', + :key_source => params[:key_source], + }) + end + it 'contains the apt_key' do + should contain_apt_key(title).with({ + :id => title, + :ensure => 'present', + :source => params[:key_source], + :server => nil, + :content => nil, + :keyserver_options => nil, + }) + end + it 'contains the apt_key present anchor' do + should contain_anchor("apt_key #{title} present") + end + end + + describe 'key_server =>' do + let :params do { + :key_server => 'pgp.mit.edu', + } end + + it 'contains the apt::key' do + should contain_apt__key(title).with({ + :key => title, + :ensure => 'present', + :key_server => 'pgp.mit.edu', + }) + end + it 'contains the apt_key' do + should contain_apt_key(title).with({ + :id => title, + :ensure => 'present', + :source => nil, + :server => params[:key_server], + :content => nil, + :keyserver_options => nil, + }) + end + it 'contains the apt_key present anchor' do + should contain_anchor("apt_key #{title} present") + end end - describe "when #{param_set == {} ? "using default" : "specifying"} define parameters" do - - it { - if [:present, 'present', :absent, 'absent'].include? param_hash[:ensure] - should contain_apt__params - end - } - - it { - if [:present, 'present'].include? param_hash[:ensure] - should_not contain_exec("apt::key #{param_hash[:key]} absent") - should contain_anchor("apt::key #{param_hash[:key]} present") - should contain_exec(digest).with({ - "path" => "/bin:/usr/bin", - "unless" => "/usr/bin/apt-key list | /bin/grep '#{param_hash[:key]}'" - }) - elsif [:absent, 'absent'].include? param_hash[:ensure] - should_not contain_anchor("apt::key #{param_hash[:key]} present") - should contain_exec("apt::key #{param_hash[:key]} absent").with({ - "path" => "/bin:/usr/bin", - "onlyif" => "apt-key list | grep '#{param_hash[:key]}'", - "command" => "apt-key del '#{param_hash[:key]}'" - }) - else - expect { should raise_error(Puppet::Error) } - end - } - - it { - if [:present, 'present'].include? param_hash[:ensure] - if param_hash[:key_content] - should contain_exec(digest).with({ - "command" => "echo '#{param_hash[:key_content]}' | /usr/bin/apt-key add -" - }) - elsif param_hash[:key_source] - should contain_exec(digest).with({ - "command" => "wget -q '#{param_hash[:key_source]}' -O- | apt-key add -" - }) - elsif param_hash[:key_server] - should contain_exec(digest).with({ - "command" => "apt-key adv --keyserver '#{param_hash[:key_server]}' --recv-keys '#{param_hash[:key]}'" - }) - end - end - } + describe 'key_options =>' do + let :params do { + :key_options => 'debug', + } end + it 'contains the apt::key' do + should contain_apt__key(title).with({ + :key => title, + :ensure => 'present', + :key_options => 'debug', + }) + end + it 'contains the apt_key' do + should contain_apt_key(title).with({ + :id => title, + :ensure => 'present', + :source => nil, + :server => nil, + :content => nil, + :keyserver_options => params[:key_options], + }) + end + it 'contains the apt_key present anchor' do + should contain_anchor("apt_key #{title} present") + end end end - [{ :ensure => 'present' }, { :ensure => 'absent' }].each do |param_set| - describe "should correctly handle duplicate definitions" do + describe 'validation' do + context 'invalid key' do + let :title do + 'Out of rum. Why? Why are we out of rum?' + end + it 'fails' do + expect { subject }.to raise_error(/does not match/) + end + end - let :pre_condition do - "apt::key { 'duplicate': key => '#{title}'; }" + context 'invalid source' do + let :params do { + :key_source => 'afp://puppetlabs.com/key.gpg', + } end + it 'fails' do + expect { subject }.to raise_error(/does not match/) end + end - let(:params) { param_set } + context 'invalid content' do + let :params do { + :key_content => [], + } end + it 'fails' do + expect { subject }.to raise_error(/is not a string/) + end + end - it { - if param_set[:ensure] == 'present' - should contain_anchor("apt::key #{title} present") - should contain_apt__key(title) - should contain_apt__key("duplicate") - elsif param_set[:ensure] == 'absent' - expect { should raise_error(Puppet::Error) } - end - } + context 'invalid server' do + let :params do { + :key_server => 'two bottles of rum', + } end + it 'fails' do + expect { subject }.to raise_error(/must be a valid domain name/) + end + end + context 'invalid keyserver_options' do + let :params do { + :key_options => {}, + } end + it 'fails' do + expect { subject }.to raise_error(/is not a string/) + end end end -end + describe 'duplication' do + context 'two apt::key resources for same key, different titles' do + let :pre_condition do + "apt::key { 'duplicate': key => #{title}, }" + end + it 'contains two apt::key resources' do + should contain_apt__key('duplicate').with({ + :key => title, + :ensure => 'present', + }) + should contain_apt__key(title).with({ + :key => title, + :ensure => 'present', + }) + end + + it 'contains only a single apt_key' do + should contain_apt_key('duplicate').with({ + :id => title, + :ensure => 'present', + :source => nil, + :server => nil, + :content => nil, + :keyserver_options => nil, + }) + should_not contain_apt_key(title) + end + end + + context 'two apt::key resources, different ensure' do + let :pre_condition do + "apt::key { 'duplicate': key => #{title}, ensure => 'absent', }" + end + it 'informs the user of the impossibility' do + expect { subject }.to raise_error(/already ensured as absent/) + end + end + end +end diff --git a/spec/defines/pin_spec.rb b/spec/defines/pin_spec.rb index 78a9b12..d79462c 100644 --- a/spec/defines/pin_spec.rb +++ b/spec/defines/pin_spec.rb @@ -15,14 +15,14 @@ describe 'apt::pin', :type => :define do [ { :params => {}, - :content => "# my_pin\nExplanation: : my_pin\nPackage: *\nPin: release a=my_pin\nPin-Priority: 0\n" + :content => "Explanation: : my_pin\nPackage: *\nPin: release a=my_pin\nPin-Priority: 0\n" }, { :params => { :packages => 'apache', :priority => '1' }, - :content => "# my_pin\nExplanation: : my_pin\nPackage: apache\nPin: release a=my_pin\nPin-Priority: 1\n" + :content => "Explanation: : my_pin\nPackage: apache\nPin: release a=my_pin\nPin-Priority: 1\n" }, { :params => { @@ -30,7 +30,7 @@ describe 'apt::pin', :type => :define do :packages => 'apache', :priority => '1' }, - :content => "# my_pin\nExplanation: : my_pin\nPackage: apache\nPin: release a=my_pin\nPin-Priority: 1\n" + :content => "Explanation: : my_pin\nPackage: apache\nPin: release a=my_pin\nPin-Priority: 1\n" }, { :params => { @@ -38,7 +38,7 @@ describe 'apt::pin', :type => :define do :packages => 'apache', :priority => '1' }, - :content => "# my_pin\nExplanation: : my_pin\nPackage: apache\nPin: release a=my_pin\nPin-Priority: 1\n" + :content => "Explanation: : my_pin\nPackage: apache\nPin: release a=my_pin\nPin-Priority: 1\n" }, { :params => { @@ -46,7 +46,7 @@ describe 'apt::pin', :type => :define do :priority => '1', :release => 'my_newpin' }, - :content => "# my_pin\nExplanation: : my_pin\nPackage: apache\nPin: release a=my_newpin\nPin-Priority: 1\n" + :content => "Explanation: : my_pin\nPackage: apache\nPin: release a=my_newpin\nPin-Priority: 1\n" }, { :params => { @@ -54,19 +54,19 @@ describe 'apt::pin', :type => :define do :priority => '1', :version => '2.2.16*' }, - :content => "# my_pin\nExplanation: : my_pin\nPackage: apache\nPin: version 2.2.16*\nPin-Priority: 1\n" + :content => "Explanation: : my_pin\nPackage: apache\nPin: version 2.2.16*\nPin-Priority: 1\n" }, { :params => { :priority => '1', :origin => 'ftp.de.debian.org' }, - :content => "# my_pin\nExplanation: : my_pin\nPackage: *\nPin: origin ftp.de.debian.org\nPin-Priority: 1\n" + :content => "Explanation: : my_pin\nPackage: *\nPin: origin ftp.de.debian.org\nPin-Priority: 1\n" }, { :params => { :packages => 'apache', - :priority => '1', + :priority => '1', :release => 'stable', :codename => 'wheezy', :release_version => '3.0', @@ -74,7 +74,13 @@ describe 'apt::pin', :type => :define do :originator => 'Debian', :label => 'Debian' }, - :content => "# my_pin\nExplanation: : my_pin\nPackage: apache\nPin: release a=stable, n=wheezy, v=3.0, c=main, o=Debian, l=Debian\nPin-Priority: 1\n" + :content => "Explanation: : my_pin\nPackage: apache\nPin: release a=stable, n=wheezy, v=3.0, c=main, o=Debian, l=Debian\nPin-Priority: 1\n" + }, + { + :params => { + :packages => ['apache', 'ntop'], + }, + :content => "Explanation: : my_pin\nPackage: apache ntop\nPin: release a=my_pin\nPin-Priority: 0\n" }, ].each do |param_set| describe "when #{param_set == {} ? "using default" : "specifying"} define parameters" do @@ -99,4 +105,16 @@ describe 'apt::pin', :type => :define do } end end + + describe 'resource title with invalid chars' do + context 'spaces' do + let(:title) { 'oh my god this is not valid' } + it { should contain_file('oh_my_god_this_is_not_valid.pref') } + end + + context '#$&*$' do + let(:title) { 'so && many $* invalid @! things' } + it { should contain_file('so____many____invalid____things.pref') } + end + end end diff --git a/spec/defines/ppa_spec.rb b/spec/defines/ppa_spec.rb index 0c3bd75..6944f9b 100644 --- a/spec/defines/ppa_spec.rb +++ b/spec/defines/ppa_spec.rb @@ -23,6 +23,7 @@ describe 'apt::ppa', :type => :define do :lsbdistcodename => platform[:lsbdistcodename], :operatingsystem => platform[:operatingsystem], :lsbdistid => platform[:lsbdistid], + :osfamily => 'Debian', } end let :release do @@ -134,7 +135,8 @@ describe 'apt::ppa', :type => :define do let :facts do {:lsbdistcodename => '#{platform[:lsbdistcodename]}', :operatingsystem => 'Ubuntu', - :lsbdistid => 'Ubuntu'} + :lsbdistid => 'Ubuntu', + :osfamily => 'Debian'} end let(:title) { "ppa" } let(:release) { "#{platform[:lsbdistcodename]}" } diff --git a/spec/defines/source_spec.rb b/spec/defines/source_spec.rb index 9da8b23..34b3942 100644 --- a/spec/defines/source_spec.rb +++ b/spec/defines/source_spec.rb @@ -1,6 +1,9 @@ require 'spec_helper' + describe 'apt::source', :type => :define do let(:facts) { { :lsbdistid => 'Debian' } } + GPG_KEY_ID = '4BD6EC30' + let :title do 'my_source' end @@ -14,7 +17,7 @@ describe 'apt::source', :type => :define do :include_src => true, :required_packages => false, :key => false, - :key_server => 'keyserver.ubuntu.com', + :key_server => false, :key_content => false, :key_source => false, :pin => false @@ -28,15 +31,14 @@ describe 'apt::source', :type => :define do :repos => 'security', :include_src => false, :required_packages => 'apache', - :key => 'key_name', + :key => GPG_KEY_ID, :key_server => 'keyserver.debian.com', :pin => '600', :key_content => 'ABCD1234' }, { - :key => 'key_name', + :key => GPG_KEY_ID, :key_server => 'keyserver.debian.com', - :key_content => false, }, { :ensure => 'absent', @@ -135,13 +137,16 @@ describe 'apt::source', :type => :define do } it { + key_server = param_hash[:key_server] || nil + key_content = param_hash[:key_content] || nil + key_source = param_hash[:key_source] || nil if param_hash[:key] should contain_apt__key("Add key: #{param_hash[:key]} from Apt::Source #{title}").with({ "key" => param_hash[:key], "ensure" => :present, - "key_server" => param_hash[:key_server], - "key_content" => param_hash[:key_content], - "key_source" => param_hash[:key_source], + "key_server" => key_server, + "key_content" => key_content, + "key_source" => key_source, "before" => "File[#{title}.list]" }) else diff --git a/spec/unit/puppet/type/apt_key_spec.rb b/spec/unit/puppet/type/apt_key_spec.rb new file mode 100644 index 0000000..c29f82b --- /dev/null +++ b/spec/unit/puppet/type/apt_key_spec.rb @@ -0,0 +1,160 @@ +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 the ftp URI scheme in source' do + expect { Puppet::Type.type(:apt_key).new( + :id => '4BD6EC30', + :source => 'ftp://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 diff --git a/templates/50unattended-upgrades.erb b/templates/50unattended-upgrades.erb index 4df0f74..1177922 100644 --- a/templates/50unattended-upgrades.erb +++ b/templates/50unattended-upgrades.erb @@ -1,5 +1,9 @@ // Automatically upgrade packages from these (origin:archive) pairs +<%- if @legacy_origin -%> Unattended-Upgrade::Allowed-Origins { +<%- else -%> +Unattended-Upgrade::Origins-Pattern { +<%- end -%> <% @origins.each do |origin| -%> "<%= origin %>"; <% end -%> diff --git a/templates/pin.pref.erb b/templates/pin.pref.erb index eed0c10..26b2516 100644 --- a/templates/pin.pref.erb +++ b/templates/pin.pref.erb @@ -15,8 +15,7 @@ elsif @origin.length > 0 @pin = "origin #{@origin}" end -%> -# <%= @name %> Explanation: <%= @explanation %> -Package: <%= @packages %> +Package: <%= @packages_string %> Pin: <%= @pin %> Pin-Priority: <%= @priority %>