Skip to content

Commit

Permalink
Merge pull request #229 from daenney/apt-key-ftp
Browse files Browse the repository at this point in the history
apt_key: Support fetching keys over FTP.
  • Loading branch information
hunner committed Feb 25, 2014
2 parents b1a6d08 + c3b3f5b commit cb53175
Show file tree
Hide file tree
Showing 5 changed files with 134 additions and 4 deletions.
12 changes: 10 additions & 2 deletions lib/puppet/provider/apt_key/apt_key.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,15 @@
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 'puppet_x/apt_key/patch_openuri'
OpenURI::Options.merge!({:ftp_active_mode => false,})
end

Puppet::Type.type(:apt_key).provide(:apt_key) do

KEY_LINE = {
Expand Down Expand Up @@ -99,8 +107,8 @@ def source_to_file(value)
value
else
begin
key = open(value).read
rescue OpenURI::HTTPError => e
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]}")
Expand Down
4 changes: 2 additions & 2 deletions lib/puppet/type/apt_key.rb
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,8 @@
end

newparam(:source) do
desc 'Location of a GPG key file, /path/to/file, http:// or https://'
newvalues(/\Ahttps?:\/\//, /\A\/\w+/)
desc 'Location of a GPG key file, /path/to/file, ftp://, http:// or https://'
newvalues(/\Ahttps?:\/\//, /\Aftp:\/\//, /\A\/\w+/)
end

autorequire(:file) do
Expand Down
63 changes: 63 additions & 0 deletions lib/puppet_x/apt_key/patch_openuri.rb
Original file line number Diff line number Diff line change
@@ -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
52 changes: 52 additions & 0 deletions spec/acceptance/apt_key_provider_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
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
Expand Down Expand Up @@ -251,6 +254,55 @@
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
Expand Down
7 changes: 7 additions & 0 deletions spec/unit/puppet/type/apt_key_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,13 @@
)}.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',
Expand Down

0 comments on commit cb53175

Please sign in to comment.