diff --git a/lib/puppet/application/module.rb b/lib/puppet/application/module.rb new file mode 100644 index 000000000..0b1c20ef6 --- /dev/null +++ b/lib/puppet/application/module.rb @@ -0,0 +1,3 @@ +require 'puppet/application/face_base' + +class Puppet::Application::Module < Puppet::Application::FaceBase; end diff --git a/lib/puppet/face/module.rb b/lib/puppet/face/module.rb new file mode 100644 index 000000000..bab553c2f --- /dev/null +++ b/lib/puppet/face/module.rb @@ -0,0 +1,12 @@ +require 'puppet/face' +require 'puppet/module_tool' + +Puppet::Face.define(:module, '1.0.0') do + copyright "Puppet Labs", 2011 + license "Apache 2 license; see COPYING" + + summary "Creates, installs and searches for modules on the Puppet Forge." + description <<-EOT + Creates, installs and searches for modules on the Puppet Forge. + EOT +end diff --git a/spec/unit/face/module/build_spec.rb b/spec/unit/face/module/build_spec.rb new file mode 100644 index 000000000..f4dc7f795 --- /dev/null +++ b/spec/unit/face/module/build_spec.rb @@ -0,0 +1,30 @@ +require 'spec_helper' +require 'puppet/face' + +describe "puppet module build" do + subject { Puppet::Face[:module, :current] } + + describe "option validation" do + context "without any options" do + it "should require a path" do + pattern = /wrong number of arguments/ + expect { subject.build }.to raise_error ArgumentError, pattern + end + end + end + + describe "inline documentation" do + subject { Puppet::Face[:module, :current].get_action :build } + + its(:summary) { should =~ /build.*module/im } + its(:description) { should =~ /build.*module/im } + its(:returns) { should =~ /pathname/i } + its(:examples) { should_not be_empty } + + %w{ license copyright summary description returns examples }.each do |doc| + context "of the" do + its(doc.to_sym) { should_not =~ /(FIXME|REVISIT|TODO)/ } + end + end + end +end diff --git a/spec/unit/face/module/changes_spec.rb b/spec/unit/face/module/changes_spec.rb new file mode 100644 index 000000000..eefa9d2a6 --- /dev/null +++ b/spec/unit/face/module/changes_spec.rb @@ -0,0 +1,30 @@ +require 'spec_helper' +require 'puppet/face' + +describe "puppet module changes" do + subject { Puppet::Face[:module, :current] } + + describe "option validation" do + context "without any options" do + it "should require a path" do + pattern = /wrong number of arguments/ + expect { subject.changes }.to raise_error ArgumentError, pattern + end + end + end + + describe "inline documentation" do + subject { Puppet::Face[:module, :current].get_action :changes } + + its(:summary) { should =~ /modified.*module/im } + its(:description) { should =~ /modified.*module/im } + its(:returns) { should =~ /array/i } + its(:examples) { should_not be_empty } + + %w{ license copyright summary description returns examples }.each do |doc| + context "of the" do + its(doc.to_sym) { should_not =~ /(FIXME|REVISIT|TODO)/ } + end + end + end +end diff --git a/spec/unit/face/module/clean_spec.rb b/spec/unit/face/module/clean_spec.rb new file mode 100644 index 000000000..708aba155 --- /dev/null +++ b/spec/unit/face/module/clean_spec.rb @@ -0,0 +1,30 @@ +require 'spec_helper' +require 'puppet/face' + +describe "puppet module clean" do + subject { Puppet::Face[:module, :current] } + + describe "option validation" do + context "without any options" do + it "should not require any arguments" do + Puppet::Module::Tool::Applications::Cleaner.expects(:run).once + subject.clean + end + end + end + + describe "inline documentation" do + subject { Puppet::Face[:module, :current].get_action :clean } + + its(:summary) { should =~ /clean.*module/im } + its(:description) { should =~ /clean.*module/im } + its(:returns) { should =~ /hash/i } + its(:examples) { should_not be_empty } + + %w{ license copyright summary description returns examples }.each do |doc| + context "of the" do + its(doc.to_sym) { should_not =~ /(FIXME|REVISIT|TODO)/ } + end + end + end +end diff --git a/spec/unit/face/module/generate_spec.rb b/spec/unit/face/module/generate_spec.rb new file mode 100644 index 000000000..1e5a420b1 --- /dev/null +++ b/spec/unit/face/module/generate_spec.rb @@ -0,0 +1,30 @@ +require 'spec_helper' +require 'puppet/face' + +describe "puppet module generate" do + subject { Puppet::Face[:module, :current] } + + describe "option validation" do + context "without any options" do + it "should require name" do + pattern = /wrong number of arguments/ + expect { subject.generate }.to raise_error ArgumentError, pattern + end + end + end + + describe "inline documentation" do + subject { Puppet::Face[:module, :current].get_action :generate } + + its(:summary) { should =~ /generate.*module/im } + its(:description) { should =~ /generate.*module/im } + its(:returns) { should =~ /array/i } + its(:examples) { should_not be_empty } + + %w{ license copyright summary description returns examples }.each do |doc| + context "of the" do + its(doc.to_sym) { should_not =~ /(FIXME|REVISIT|TODO)/ } + end + end + end +end diff --git a/spec/unit/face/module/install_spec.rb b/spec/unit/face/module/install_spec.rb new file mode 100644 index 000000000..681109793 --- /dev/null +++ b/spec/unit/face/module/install_spec.rb @@ -0,0 +1,75 @@ +require 'spec_helper' +require 'puppet/face' +require 'puppet/module_tool' + +describe "puppet module install" do + subject { Puppet::Face[:module, :current] } + + let(:options) do + {} + end + + describe "option validation" do + let(:expected_options) do + { + :install_dir => File.expand_path("/dev/null/modules"), + :module_repository => "http://forge.puppetlabs.com", + } + end + + context "without any options" do + it "should require a name" do + pattern = /wrong number of arguments/ + expect { subject.install }.to raise_error ArgumentError, pattern + end + + it "should not require any options" do + Puppet::Module::Tool::Applications::Installer.expects(:run).with("puppetlabs-apache", expected_options).once + subject.install("puppetlabs-apache") + end + end + + it "should accept the --force option" do + options[:force] = true + expected_options.merge!(options) + Puppet::Module::Tool::Applications::Installer.expects(:run).with("puppetlabs-apache", expected_options).once + subject.install("puppetlabs-apache", options) + end + + it "should accept the --install-dir option" do + options[:install_dir] = "/foo/puppet/modules" + expected_options.merge!(options) + Puppet::Module::Tool::Applications::Installer.expects(:run).with("puppetlabs-apache", expected_options).once + subject.install("puppetlabs-apache", options) + end + + it "should accept the --module-repository option" do + options[:module_repository] = "http://forge.example.com" + expected_options.merge!(options) + Puppet::Module::Tool::Applications::Installer.expects(:run).with("puppetlabs-apache", expected_options).once + subject.install("puppetlabs-apache", options) + end + + it "should accept the --version option" do + options[:version] = "0.0.1" + expected_options.merge!(options) + Puppet::Module::Tool::Applications::Installer.expects(:run).with("puppetlabs-apache", expected_options).once + subject.install("puppetlabs-apache", options) + end + end + + describe "inline documentation" do + subject { Puppet::Face[:module, :current].get_action :install } + + its(:summary) { should =~ /install.*module/im } + its(:description) { should =~ /install.*module/im } + its(:returns) { should =~ /pathname/i } + its(:examples) { should_not be_empty } + + %w{ license copyright summary description returns examples }.each do |doc| + context "of the" do + its(doc.to_sym) { should_not =~ /(FIXME|REVISIT|TODO)/ } + end + end + end +end diff --git a/spec/unit/face/module/list_spec.rb b/spec/unit/face/module/list_spec.rb new file mode 100644 index 000000000..1a2258c78 --- /dev/null +++ b/spec/unit/face/module/list_spec.rb @@ -0,0 +1,100 @@ +require 'spec_helper' +require 'puppet/face' +require 'puppet/module_tool' + +describe "puppet module list" do + include PuppetSpec::Files + + before do + dir = tmpdir("deep_path") + + @modpath1 = File.join(dir, "modpath1") + @modpath2 = File.join(dir, "modpath2") + + FileUtils.mkdir_p(@modpath1) + FileUtils.mkdir_p(@modpath2) + end + + def mkmodule(name, path) + mod_path = File.join(path, name) + FileUtils.mkdir_p(mod_path) + mod_path + end + + it "should return an empty list per dir in path if there are no modules" do + Puppet.settings[:modulepath] = "#{@modpath1}#{File::PATH_SEPARATOR}#{@modpath2}" + Puppet::Face[:module, :current].list.should == { + @modpath1 => [], + @modpath2 => [] + } + end + + it "should include modules separated by the environment's modulepath" do + foomod1 = mkmodule('foo', @modpath1) + barmod1 = mkmodule('bar', @modpath1) + foomod2 = mkmodule('foo', @modpath2) + + env = Puppet::Node::Environment.new + env.modulepath = [@modpath1, @modpath2] + + Puppet::Face[:module, :current].list.should == { + @modpath1 => [ + Puppet::Module.new('bar', :environment => env, :path => barmod1), + Puppet::Module.new('foo', :environment => env, :path => foomod1) + ], + @modpath2 => [Puppet::Module.new('foo', :environment => env, :path => foomod2)] + } + end + + it "should use the specified environment" do + foomod1 = mkmodule('foo', @modpath1) + barmod1 = mkmodule('bar', @modpath1) + + usedenv = Puppet::Node::Environment.new('useme') + usedenv.modulepath = [@modpath1, @modpath2] + + Puppet::Face[:module, :current].list(:env => 'useme').should == { + @modpath1 => [ + Puppet::Module.new('bar', :environment => usedenv), + Puppet::Module.new('foo', :environment => usedenv) + ], + @modpath2 => [] + } + end + + it "should use the specified modulepath" do + foomod1 = mkmodule('foo', @modpath1) + barmod2 = mkmodule('bar', @modpath2) + + Puppet::Face[:module, :current].list(:modulepath => "#{@modpath1}#{File::PATH_SEPARATOR}#{@modpath2}").should == { + @modpath1 => [ Puppet::Module.new('foo') ], + @modpath2 => [ Puppet::Module.new('bar') ] + } + end + + it "should use the specified modulepath over the specified environment in place of the environment's default path" do + foomod1 = mkmodule('foo', @modpath1) + barmod2 = mkmodule('bar', @modpath2) + env = Puppet::Node::Environment.new('myenv') + env.modulepath = ['/tmp/notused'] + + list = Puppet::Face[:module, :current].list(:env => 'myenv', :modulepath => "#{@modpath1}#{File::PATH_SEPARATOR}#{@modpath2}") + + # Changing Puppet[:modulepath] causes Puppet::Node::Environment.new('myenv') + # to have a different object_id than the env above + env = Puppet::Node::Environment.new('myenv') + list.should == { + @modpath1 => [ Puppet::Module.new('foo', :environment => env, :path => foomod1) ], + @modpath2 => [ Puppet::Module.new('bar', :environment => env, :path => barmod2) ] + } + end + + describe "inline documentation" do + subject { Puppet::Face[:module, :current].get_action :list } + + its(:summary) { should =~ /list.*module/im } + its(:description) { should =~ /list.*module/im } + its(:returns) { should =~ /hash of paths to module objects/i } + its(:examples) { should_not be_empty } + end +end diff --git a/spec/unit/face/module/search_spec.rb b/spec/unit/face/module/search_spec.rb new file mode 100644 index 000000000..250c5e0b1 --- /dev/null +++ b/spec/unit/face/module/search_spec.rb @@ -0,0 +1,40 @@ +require 'spec_helper' +require 'puppet/face' + +describe "puppet module search" do + subject { Puppet::Face[:module, :current] } + + let(:options) do + {} + end + + describe "option validation" do + context "without any options" do + it "should require a search term" do + pattern = /wrong number of arguments/ + expect { subject.search }.to raise_error ArgumentError, pattern + end + end + + it "should accept the --module-repository option" do + options[:module_repository] = "http://forge.example.com" + Puppet::Module::Tool::Applications::Searcher.expects(:run).with("puppetlabs-apache", options).once + subject.search("puppetlabs-apache", options) + end + end + + describe "inline documentation" do + subject { Puppet::Face[:module, :current].get_action :search } + + its(:summary) { should =~ /search.*module/im } + its(:description) { should =~ /search.*module/im } + its(:returns) { should =~ /array/i } + its(:examples) { should_not be_empty } + + %w{ license copyright summary description returns examples }.each do |doc| + context "of the" do + its(doc.to_sym) { should_not =~ /(FIXME|REVISIT|TODO)/ } + end + end + end +end diff --git a/spec/unit/face/module/uninstall_spec.rb b/spec/unit/face/module/uninstall_spec.rb new file mode 100644 index 000000000..c145b1b66 --- /dev/null +++ b/spec/unit/face/module/uninstall_spec.rb @@ -0,0 +1,47 @@ +require 'spec_helper' +require 'puppet/face' +require 'puppet/module_tool' + +describe "puppet module uninstall" do + subject { Puppet::Face[:module, :current] } + + let(:options) do + {} + end + + describe "option validation" do + context "without any options" do + it "should require a name" do + pattern = /wrong number of arguments/ + expect { subject.uninstall }.to raise_error ArgumentError, pattern + end + + it "should not require any options" do + Puppet::Module::Tool::Applications::Uninstaller.expects(:run).once + subject.uninstall("puppetlabs-apache") + end + end + + it "should accept the --target-directory option" do + options[:target_directory] = "/foo/puppet/modules" + expected_options = { :target_directories => ["/foo/puppet/modules"] } + Puppet::Module::Tool::Applications::Uninstaller.expects(:run).with("puppetlabs-apache", expected_options).once + subject.uninstall("puppetlabs-apache", options) + end + end + + describe "inline documentation" do + subject { Puppet::Face[:module, :current].get_action :uninstall } + + its(:summary) { should =~ /uninstall.*module/im } + its(:description) { should =~ /uninstall.*module/im } + its(:returns) { should =~ /array of strings/i } + its(:examples) { should_not be_empty } + + %w{ license copyright summary description returns examples }.each do |doc| + context "of the" do + its(doc.to_sym) { should_not =~ /(FIXME|REVISIT|TODO)/ } + end + end + end +end