diff --git a/lib/puppet/face/plugin.rb b/lib/puppet/face/plugin.rb index 13ea32aa9..73792ada6 100644 --- a/lib/puppet/face/plugin.rb +++ b/lib/puppet/face/plugin.rb @@ -1,61 +1,64 @@ require 'puppet/face' Puppet::Face.define(:plugin, '0.0.1') do copyright "Puppet Labs", 2011 license "Apache 2 license; see COPYING" summary "Interact with the Puppet plugin system." description <<-'EOT' This subcommand provides network access to the puppet master's store of plugins. The puppet master serves Ruby code collected from the `lib` directories of its modules. These plugins can be used on agent nodes to extend Facter and implement custom types and providers. Plugins are normally downloaded by puppet agent during the course of a run. EOT action :download do summary "Download plugins from the puppet master." description <<-'EOT' Downloads plugins from the configured puppet master. Any plugins downloaded in this way will be used in all subsequent Puppet activity. This action modifies files on disk. EOT returns <<-'EOT' A list of the files downloaded, or a confirmation that no files were downloaded. When used from the Ruby API, this action returns an array of the files downloaded, which will be empty if none were retrieved. EOT examples <<-'EOT' Retrieve plugins from the puppet master: $ puppet plugin download Retrieve plugins from the puppet master (API example): $ Puppet::Face[:plugin, '0.0.1'].download EOT when_invoked do |options| require 'puppet/configurer/downloader' + remote_environment_for_plugins = Puppet::Node::Environment.remote(Puppet[:environment]) Puppet::Configurer::Downloader.new("plugin", Puppet[:plugindest], Puppet[:pluginsource], - Puppet[:pluginsignore]).evaluate + Puppet[:pluginsignore], + remote_environment_for_plugins).evaluate if Puppet.features.external_facts? Puppet::Configurer::Downloader.new("pluginfacts", Puppet[:pluginfactdest], Puppet[:pluginfactsource], - Puppet[:pluginsignore]).evaluate + Puppet[:pluginsignore], + remote_environment_for_plugins).evaluate end end when_rendering :console do |value| if value.empty? then "No plugins downloaded." else "Downloaded these plugins: #{value.join(', ')}" end end end end diff --git a/spec/integration/faces/plugin_spec.rb b/spec/integration/faces/plugin_spec.rb new file mode 100644 index 000000000..1d7cf3015 --- /dev/null +++ b/spec/integration/faces/plugin_spec.rb @@ -0,0 +1,52 @@ +require 'spec_helper' +require 'puppet/face' +require 'puppet/file_serving/metadata' +require 'puppet/file_serving/content' +require 'puppet/indirector/memory' + +describe Puppet::Face[:plugin, '0.0.1'] do + INDIRECTORS = [ + Puppet::Indirector::FileMetadata, + Puppet::Indirector::FileContent, + ] + + INDIRECTED_CLASSES = [ + Puppet::FileServing::Metadata, + Puppet::FileServing::Content, + Puppet::Node::Facts, + ] + + INDIRECTORS.each do |indirector| + class indirector::Memory < Puppet::Indirector::Memory + def find(request) + model.new('/dev/null', { 'type' => 'directory' }) + end + end + end + + before do + FileUtils.mkdir(Puppet[:vardir]) + @termini_classes = {} + INDIRECTED_CLASSES.each do |indirected| + @termini_classes[indirected] = indirected.indirection.terminus_class + indirected.indirection.terminus_class = :memory + end + end + + after do + FileUtils.rmdir(File.join(Puppet[:vardir],'lib')) + FileUtils.rmdir(File.join(Puppet[:vardir],'facts.d')) + FileUtils.rmdir(Puppet[:vardir]) + INDIRECTED_CLASSES.each do |indirected| + indirected.indirection.terminus_class = @termini_classes[indirected] + indirected.indirection.termini.clear + end + end + + it "processes a download request without logging errors" do + Puppet[:trace] = true + result = subject.download + expect(result).to eq([File.join(Puppet[:vardir],'facts.d')]) + expect(@logs.select { |l| l.level == :err }).to eq([]) + end +end diff --git a/spec/unit/configurer/downloader_spec.rb b/spec/unit/configurer/downloader_spec.rb index 55901edba..9e1d3a29b 100755 --- a/spec/unit/configurer/downloader_spec.rb +++ b/spec/unit/configurer/downloader_spec.rb @@ -1,210 +1,211 @@ #! /usr/bin/env ruby require 'spec_helper' require 'puppet/configurer/downloader' describe Puppet::Configurer::Downloader do require 'puppet_spec/files' include PuppetSpec::Files it "should require a name" do lambda { Puppet::Configurer::Downloader.new }.should raise_error(ArgumentError) end it "should require a path and a source at initialization" do lambda { Puppet::Configurer::Downloader.new("name") }.should raise_error(ArgumentError) end it "should set the name, path and source appropriately" do dler = Puppet::Configurer::Downloader.new("facts", "path", "source") dler.name.should == "facts" dler.path.should == "path" dler.source.should == "source" end describe "when creating the file that does the downloading" do before do @dler = Puppet::Configurer::Downloader.new("foo", "path", "source") end it "should create a file instance with the right path and source" do Puppet::Type.type(:file).expects(:new).with { |opts| opts[:path] == "path" and opts[:source] == "source" } @dler.file end it "should tag the file with the downloader name" do Puppet::Type.type(:file).expects(:new).with { |opts| opts[:tag] == "foo" } @dler.file end it "should always recurse" do Puppet::Type.type(:file).expects(:new).with { |opts| opts[:recurse] == true } @dler.file end it "should always purge" do Puppet::Type.type(:file).expects(:new).with { |opts| opts[:purge] == true } @dler.file end it "should never be in noop" do Puppet::Type.type(:file).expects(:new).with { |opts| opts[:noop] == false } @dler.file end it "should set source_permissions to ignore" do Puppet::Type.type(:file).expects(:new).with { |opts| opts[:source_permissions] == :ignore } @dler.file end describe "on POSIX", :as_platform => :posix do it "should always set the owner to the current UID" do Process.expects(:uid).returns 51 Puppet::Type.type(:file).expects(:new).with { |opts| opts[:owner] == 51 } @dler.file end it "should always set the group to the current GID" do Process.expects(:gid).returns 61 Puppet::Type.type(:file).expects(:new).with { |opts| opts[:group] == 61 } @dler.file end end describe "on Windows", :as_platform => :windows do it "should omit the owner" do Puppet::Type.type(:file).expects(:new).with { |opts| opts[:owner] == nil } @dler.file end it "should omit the group" do Puppet::Type.type(:file).expects(:new).with { |opts| opts[:group] == nil } @dler.file end end it "should always force the download" do Puppet::Type.type(:file).expects(:new).with { |opts| opts[:force] == true } @dler.file end it "should never back up when downloading" do Puppet::Type.type(:file).expects(:new).with { |opts| opts[:backup] == false } @dler.file end it "should support providing an 'ignore' parameter" do Puppet::Type.type(:file).expects(:new).with { |opts| opts[:ignore] == [".svn"] } @dler = Puppet::Configurer::Downloader.new("foo", "path", "source", ".svn") @dler.file end it "should split the 'ignore' parameter on whitespace" do Puppet::Type.type(:file).expects(:new).with { |opts| opts[:ignore] == %w{.svn CVS} } @dler = Puppet::Configurer::Downloader.new("foo", "path", "source", ".svn CVS") @dler.file end end describe "when creating the catalog to do the downloading" do before do @path = make_absolute("/download/path") @dler = Puppet::Configurer::Downloader.new("foo", @path, make_absolute("source")) end it "should create a catalog and add the file to it" do catalog = @dler.catalog catalog.resources.size.should == 1 catalog.resources.first.class.should == Puppet::Type::File catalog.resources.first.name.should == @path end it "should specify that it is not managing a host catalog" do @dler.catalog.host_config.should == false end end describe "when downloading" do before do @dl_name = tmpfile("downloadpath") source_name = tmpfile("source") File.open(source_name, 'w') {|f| f.write('hola mundo') } - @dler = Puppet::Configurer::Downloader.new("foo", @dl_name, source_name) + env = Puppet::Node::Environment.remote('foo') + @dler = Puppet::Configurer::Downloader.new("foo", @dl_name, source_name, Puppet[:pluginsignore], env) end it "should not skip downloaded resources when filtering on tags" do Puppet[:tags] = 'maytag' @dler.evaluate Puppet::FileSystem.exist?(@dl_name).should be_true end it "should log that it is downloading" do Puppet.expects(:info) Timeout.stubs(:timeout) @dler.evaluate end it "should set a timeout for the download using the `configtimeout` setting" do Puppet[:configtimeout] = 50 Timeout.expects(:timeout).with(50) @dler.evaluate end it "should apply the catalog within the timeout block" do catalog = mock 'catalog' @dler.expects(:catalog).returns(catalog) Timeout.expects(:timeout).yields catalog.expects(:apply) @dler.evaluate end it "should return all changed file paths" do trans = mock 'transaction' catalog = mock 'catalog' @dler.expects(:catalog).returns(catalog) catalog.expects(:apply).yields(trans) Timeout.expects(:timeout).yields resource = mock 'resource' resource.expects(:[]).with(:path).returns "/changed/file" trans.expects(:changed?).returns([resource]) @dler.evaluate.should == %w{/changed/file} end it "should yield the resources if a block is given" do trans = mock 'transaction' catalog = mock 'catalog' @dler.expects(:catalog).returns(catalog) catalog.expects(:apply).yields(trans) Timeout.expects(:timeout).yields resource = mock 'resource' resource.expects(:[]).with(:path).returns "/changed/file" trans.expects(:changed?).returns([resource]) yielded = nil @dler.evaluate { |r| yielded = r } yielded.should == resource end it "should catch and log exceptions" do Puppet.expects(:err) Timeout.stubs(:timeout).raises(Puppet::Error, "testing") lambda { @dler.evaluate }.should_not raise_error end end end