Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

apt_key: Support fetching keys over FTP. #229

Merged
merged 1 commit into from Feb 25, 2014
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
12 changes: 10 additions & 2 deletions lib/puppet/provider/apt_key/apt_key.rb
@@ -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
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
@@ -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
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
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