diff --git a/lib/puppet/provider/archive/curl.rb b/lib/puppet/provider/archive/curl.rb index 871e553..84d9d93 100644 --- a/lib/puppet/provider/archive/curl.rb +++ b/lib/puppet/provider/archive/curl.rb @@ -1,43 +1,70 @@ +require 'uri' +require 'tempfile' + Puppet::Type.type(:archive).provide(:curl, parent: :ruby) do commands curl: 'curl' defaultfor feature: :posix def curl_params(params) - account = [resource[:username], resource[:password]].compact.join(':') if resource[:username] - params += optional_switch(account, ['--user', '%s']) + if resource[:username] + create_netrcfile + params += ['--netrc-file', @netrc_file.path] + end params += optional_switch(resource[:proxy_server], ['--proxy', '%s']) params += ['--insecure'] if resource[:allow_insecure] params += resource[:download_options] if resource[:download_options] params += optional_switch(resource[:cookie], ['--cookie', '%s']) params end + def create_netrcfile + @netrc_file = Tempfile.new('.puppet_archive_curl') + machine = URI.parse(resource[:source]).host + @netrc_file.write("machine #{machine}\nlogin #{resource[:username]}\npassword #{resource[:password]}\n") + @netrc_file.close + end + + def delete_netrcfile + return if @netrc_file.nil? + + @netrc_file.unlink + @netrc_file = nil + end + def download(filepath) params = curl_params( [ resource[:source], '-o', filepath, '-fsSLg', '--max-redirs', 5 ] ) - curl(params) + begin + curl(params) + ensure + delete_netrcfile + end end def remote_checksum params = curl_params( [ resource[:checksum_url], '-fsSLg', '--max-redirs', 5 ] ) - curl(params)[%r{\b[\da-f]{32,128}\b}i] + begin + curl(params)[%r{\b[\da-f]{32,128}\b}i] + ensure + delete_netrcfile + end end end diff --git a/spec/unit/puppet/provider/archive/curl_spec.rb b/spec/unit/puppet/provider/archive/curl_spec.rb index c06aa41..ad74c08 100644 --- a/spec/unit/puppet/provider/archive/curl_spec.rb +++ b/spec/unit/puppet/provider/archive/curl_spec.rb @@ -1,172 +1,173 @@ require 'spec_helper' curl_provider = Puppet::Type.type(:archive).provider(:curl) RSpec.describe curl_provider do it_behaves_like 'an archive provider', curl_provider describe '#download' do let(:name) { '/tmp/example.zip' } let(:resource) { Puppet::Type::Archive.new(resource_properties) } let(:provider) { curl_provider.new(resource) } + let(:tempfile) { Tempfile.new('mock') } let(:default_options) do [ 'http://home.lan/example.zip', '-o', String, '-fsSLg', '--max-redirs', 5 ] end before do allow(FileUtils).to receive(:mv) allow(provider).to receive(:curl) + allow(Tempfile).to receive(:new).with('.puppet_archive_curl').and_return(tempfile) end context 'no extra properties specified' do let(:resource_properties) do { name: name, source: 'http://home.lan/example.zip' } end it 'calls curl with input, output and --max-redirects=5' do provider.download(name) expect(provider).to have_received(:curl).with(default_options) end end - context 'username specified' do + context 'username and password specified' do let(:resource_properties) do { name: name, source: 'http://home.lan/example.zip', - username: 'foo' + username: 'foo', + password: 'bar' } end - it 'calls curl with default options and username' do + it 'populates temp netrc file with credentials' do + allow(provider).to receive(:delete_netrcfile) # Don't delete the file or we won't be able to examine its contents. provider.download(name) - expect(provider).to have_received(:curl).with(default_options << '--user' << 'foo') + nettc_content = File.open(tempfile.path).read + expect(nettc_content).to eq("machine home.lan\nlogin foo\npassword bar\n") end - end - context 'username and password specified' do - let(:resource_properties) do - { - name: name, - source: 'http://home.lan/example.zip', - username: 'foo', - password: 'bar' - } + it 'calls curl with default options and path to netrc file' do + netrc_filepath = tempfile.path + provider.download(name) + expect(provider).to have_received(:curl).with(default_options << '--netrc-file' << netrc_filepath) end - it 'calls curl with default options and password' do + it 'deletes netrc file' do + netrc_filepath = tempfile.path provider.download(name) - expect(provider).to have_received(:curl).with(default_options << '--user' << 'foo:bar') + expect(File.exist?(netrc_filepath)).to eq(false) end end context 'allow_insecure true' do let(:resource_properties) do { name: name, source: 'http://home.lan/example.zip', allow_insecure: true } end it 'calls curl with default options and --insecure' do provider.download(name) expect(provider).to have_received(:curl).with(default_options << '--insecure') end end context 'cookie specified' do let(:resource_properties) do { name: name, source: 'http://home.lan/example.zip', cookie: 'foo=bar' } end it 'calls curl with default options cookie' do provider.download(name) expect(provider).to have_received(:curl).with(default_options << '--cookie' << 'foo=bar') end end context 'using proxy' do let(:resource_properties) do { name: name, source: 'http://home.lan/example.zip', proxy_server: 'https://home.lan:8080' } end it 'calls curl with proxy' do provider.download(name) expect(provider).to have_received(:curl).with(default_options << '--proxy' << 'https://home.lan:8080') end end describe '#checksum' do subject { provider.checksum } let(:url) { nil } let(:resource_properties) do { name: name, source: 'http://home.lan/example.zip' } end before do resource[:checksum_url] = url if url end context 'with a url' do let(:curl_params) do [ 'http://example.com/checksum', '-fsSLg', '--max-redirs', 5 ] end let(:url) { 'http://example.com/checksum' } context 'responds with hash' do let(:remote_hash) { 'a0c38e1aeb175201b0dacd65e2f37e187657050a' } it 'parses checksum value' do allow(provider).to receive(:curl).with(curl_params).and_return("a0c38e1aeb175201b0dacd65e2f37e187657050a README.md\n") expect(provider.checksum).to eq('a0c38e1aeb175201b0dacd65e2f37e187657050a') end end end end describe 'custom options' do let(:resource_properties) do { name: name, source: 'http://home.lan/example.zip', download_options: ['--tlsv1'] } end it 'calls curl with custom tls options' do provider.download(name) expect(provider).to have_received(:curl).with(default_options << '--tlsv1') end end end end