Signed-off-by: Brice Figureau <brice-pup...@daysofwonder.com>
---
 bin/puppetrun                       |  243 +--------------------------------
 lib/puppet/application/puppetrun.rb |  228 ++++++++++++++++++++++++++++++
 spec/unit/application/puppetrun.rb  |  259 +++++++++++++++++++++++++++++++++++
 3 files changed, 489 insertions(+), 241 deletions(-)
 create mode 100644 lib/puppet/application/puppetrun.rb
 create mode 100755 spec/unit/application/puppetrun.rb

diff --git a/bin/puppetrun b/bin/puppetrun
index 28f72d9..da4f24a 100755
--- a/bin/puppetrun
+++ b/bin/puppetrun
@@ -125,245 +125,6 @@
 # Copyright (c) 2005 Reductive Labs, LLC
 # Licensed under the GNU Public License
 
-[:INT, :TERM].each do |signal|
-    trap(signal) do
-        $stderr.puts "Cancelling"
-        exit(1)
-    end
-end
-
-begin
-    require 'rubygems'
-rescue LoadError
-    # Nothing; we were just doing this just in case
-end
-
-begin
-    require 'ldap'
-rescue LoadError
-    $stderr.puts "Failed to load ruby LDAP library. LDAP functionality will 
not be available"
-end
-
-require 'puppet'
-require 'puppet/network/client'
-require 'puppet/util/ldap/connection'
-require 'getoptlong'
-
-flags = [
-    [ "--all",      "-a",              GetoptLong::NO_ARGUMENT ],
-    [ "--tag",      "-t",           GetoptLong::REQUIRED_ARGUMENT ],
-    [ "--class",       "-c",                   GetoptLong::REQUIRED_ARGUMENT ],
-    [ "--foreground", "-f",            GetoptLong::NO_ARGUMENT ],
-    [ "--debug",       "-d",                   GetoptLong::NO_ARGUMENT ],
-    [ "--help",                "-h",                   GetoptLong::NO_ARGUMENT 
],
-    [ "--host",                                GetoptLong::REQUIRED_ARGUMENT ],
-    [ "--parallel", "-p",                      GetoptLong::REQUIRED_ARGUMENT ],
-    [ "--ping", "-P",                  GetoptLong::NO_ARGUMENT ],
-    [ "--no-fqdn",  "-n",                      GetoptLong::NO_ARGUMENT ],
-    [ "--test",                     GetoptLong::NO_ARGUMENT ],
-    [ "--version",  "-V",           GetoptLong::NO_ARGUMENT ]
-]
-
-# Add all of the config parameters as valid options.
-Puppet.settings.addargs(flags)
-
-result = GetoptLong.new(*flags)
-
-options = {
-    :ignoreschedules => false,
-    :foreground => false,
-    :parallel => 1,
-    :ping => false,
-    :debug => false,
-    :test => false,
-    :all => false,
-    :verbose => true,
-    :fqdn => true
-}
-
-hosts = []
-classes = []
-tags = []
-
-Puppet::Util::Log.newdestination(:console)
-
-begin
-    result.each { |opt,arg|
-        case opt
-            when "--version"
-                puts "%s" % Puppet.version
-                exit
-            when "--ignoreschedules"
-                options[:ignoreschedules] = true
-            when "--no-fqdn"
-                options[:fqdn] = false
-            when "--all"
-                options[:all] = true
-            when "--test"
-                options[:test] = true
-            when "--tag"
-                tags << arg
-            when "--class"
-                classes << arg
-            when "--host"
-                hosts << arg
-            when "--help"
-                if Puppet.features.usage?
-                    RDoc::usage && exit
-                else
-                    puts "No help available unless you have RDoc::usage 
installed"
-                    exit
-                end
-            when "--parallel"
-                begin
-                    options[:parallel] = Integer(arg)
-                rescue
-                    $stderr.puts "Could not convert %s to an integer" % 
arg.inspect
-                    exit(23)
-                end
-            when "--ping"
-                 options[:ping] = true 
-            when "--foreground"
-                options[:foreground] = true
-            when "--debug"
-                options[:debug] = true
-            else
-                Puppet.settings.handlearg(opt, arg)
-        end
-    }
-rescue GetoptLong::InvalidOption => detail
-    $stderr.puts "Try '#{$0} --help'"
-    exit(1)
-end
-
-if options[:debug]
-    Puppet::Util::Log.level = :debug
-else
-    Puppet::Util::Log.level = :info
-end
-
-# Now parse the config
-Puppet.parse_config
-
-if Puppet[:node_terminus] == "ldap" and (options[:all] or classes)
-    if options[:all]
-        hosts = Puppet::Node.search("whatever").collect { |node| node.name }
-        puts "all: %s" % hosts.join(", ")
-    else
-        hosts = []
-        classes.each do |klass|
-            list = Puppet::Node.search("whatever", :class => klass).collect { 
|node| node.name }
-            puts "%s: %s" % [klass, list.join(", ")]
-
-            hosts += list
-        end
-    end
-elsif ! classes.empty?
-    $stderr.puts "You must be using LDAP to specify host classes"
-    exit(24)
-end
-
-if tags.empty?
-    tags = ""
-else
-    tags = tags.join(",")
-end
-
-children = {}
-
-# If we get a signal, then kill all of our children and get out.
-[:INT, :TERM].each do |signal|
-    trap(signal) do
-        Puppet.notice "Caught #{signal}; shutting down"
-        children.each do |pid, host|
-            Process.kill("INT", pid)
-        end
-
-        waitall
-
-        exit(1)
-    end
-end
-
-if options[:test]
-    puts "Skipping execution in test mode"
-    exit(0)
-end
-
-todo = hosts.dup
-
-failures = []
-
-# Now do the actual work
-go = true
-while go
-    # If we don't have enough children in process and we still have hosts left 
to
-    # do, then do the next host.
-    if children.length < options[:parallel] and ! todo.empty?
-        host = todo.shift
-        pid = fork do
-            if options[:ping]
-              out = %x{ping -c 1 #{host}}
-              unless $? == 0
-                  $stderr.print "Could not contact %s\n" % host
-                  next
-              end
-            end
-            client = Puppet::Network::Client.runner.new(
-                :Server => host,
-                :Port => Puppet[:puppetport]
-            )
-
-            print "Triggering %s\n" % host
-            begin
-                result = client.run(tags, options[:ignoreschedules], 
options[:foreground])
-            rescue => detail
-                $stderr.puts "Host %s failed: %s\n" % [host, detail]
-                exit(2)
-            end
-            
-            case result
-            when "success": exit(0)
-            when "running":
-                $stderr.puts "Host %s is already running" % host
-                exit(3)
-            else
-                $stderr.puts "Host %s returned unknown answer '%s'" % [host, 
result]
-                exit(12)
-            end
-        end
-        children[pid] = host
-    else
-        # Else, see if we can reap a process.
-        begin
-            pid = Process.wait
-
-            if host = children[pid]
-                # Remove our host from the list of children, so the 
parallelization
-                # continues working.
-                children.delete(pid)
-                if $?.exitstatus != 0
-                    failures << host
-                end
-                print "%s finished with exit code %s\n" % [host, $?.exitstatus]
-            else
-                $stderr.puts "Could not find host for PID %s with status %s" %
-                    [pid, $?.exitstatus]
-            end
-        rescue Errno::ECHILD
-            # There are no children left, so just exit unless there are still
-            # children left to do.
-            next unless todo.empty?
-
-            if failures.empty?
-                puts "Finished"
-                exit(0)
-            else
-                puts "Failed: %s" % failures.join(", ")
-                exit(3)
-            end
-        end
-    end
-end
-
+require 'puppet/application/puppetrun'
+Puppet::Application[:puppetrun].run
 
diff --git a/lib/puppet/application/puppetrun.rb 
b/lib/puppet/application/puppetrun.rb
new file mode 100644
index 0000000..eaaf961
--- /dev/null
+++ b/lib/puppet/application/puppetrun.rb
@@ -0,0 +1,228 @@
+begin
+    require 'rubygems'
+rescue LoadError
+    # Nothing; we were just doing this just in case
+end
+
+begin
+    require 'ldap'
+rescue LoadError
+    $stderr.puts "Failed to load ruby LDAP library. LDAP functionality will 
not be available"
+end
+
+require 'puppet'
+require 'puppet/application'
+
+puppetrun_options = [
+    [ "--all",      "-a",       GetoptLong::NO_ARGUMENT ],
+    [ "--tag",      "-t",       GetoptLong::REQUIRED_ARGUMENT ],
+    [ "--class",    "-c",       GetoptLong::REQUIRED_ARGUMENT ],
+    [ "--foreground", "-f",     GetoptLong::NO_ARGUMENT ],
+    [ "--debug",    "-d",       GetoptLong::NO_ARGUMENT ],
+    [ "--help",     "-h",       GetoptLong::NO_ARGUMENT ],
+    [ "--host",                 GetoptLong::REQUIRED_ARGUMENT ],
+    [ "--parallel", "-p",       GetoptLong::REQUIRED_ARGUMENT ],
+    [ "--ping",     "-P",       GetoptLong::NO_ARGUMENT ],
+    [ "--no-fqdn",  "-n",       GetoptLong::NO_ARGUMENT ],
+    [ "--test",                 GetoptLong::NO_ARGUMENT ],
+    [ "--version",  "-V",       GetoptLong::NO_ARGUMENT ]
+]
+
+Puppet::Application.new(:puppetrun, puppetrun_options) do
+
+    should_not_parse_config
+
+    attr_accessor :hosts, :tags, :classes
+
+    dispatch do
+        options[:test] ? :test : :main
+    end
+
+    command(:test) do
+        puts "Skipping execution in test mode"
+        exit(0)
+    end
+
+    command(:main) do
+        require 'puppet/network/client'
+        require 'puppet/util/ldap/connection'
+
+        todo = @hosts.dup
+
+        failures = []
+
+        # Now do the actual work
+        go = true
+        while go
+            # If we don't have enough children in process and we still have 
hosts left to
+            # do, then do the next host.
+            if @children.length < options[:parallel] and ! todo.empty?
+                host = todo.shift
+                pid = fork do
+                    run_for_host(host)
+                end
+                @children[pid] = host
+            else
+                # Else, see if we can reap a process.
+                begin
+                    pid = Process.wait
+
+                    if host = @children[pid]
+                        # Remove our host from the list of children, so the 
parallelization
+                        # continues working.
+                        @children.delete(pid)
+                        if $?.exitstatus != 0
+                            failures << host
+                        end
+                        print "%s finished with exit code %s\n" % [host, 
$?.exitstatus]
+                    else
+                        $stderr.puts "Could not find host for PID %s with 
status %s" %
+                            [pid, $?.exitstatus]
+                    end
+                rescue Errno::ECHILD
+                    # There are no children left, so just exit unless there 
are still
+                    # children left to do.
+                    next unless todo.empty?
+
+                    if failures.empty?
+                        puts "Finished"
+                        exit(0)
+                    else
+                        puts "Failed: %s" % failures.join(", ")
+                        exit(3)
+                    end
+                end
+            end
+        end
+    end
+
+    def run_for_host(host)
+        if options[:ping]
+            out = %x{ping -c 1 #{host}}
+            unless $? == 0
+                $stderr.print "Could not contact %s\n" % host
+                next
+            end
+        end
+        client = Puppet::Network::Client.runner.new(
+            :Server => host,
+            :Port => Puppet[:puppetport]
+        )
+
+        print "Triggering %s\n" % host
+        begin
+            result = client.run(@tags, options[:ignoreschedules], 
options[:foreground])
+        rescue => detail
+            $stderr.puts "Host %s failed: %s\n" % [host, detail]
+            exit(2)
+        end
+
+        case result
+        when "success": exit(0)
+        when "running":
+            $stderr.puts "Host %s is already running" % host
+            exit(3)
+        else
+            $stderr.puts "Host %s returned unknown answer '%s'" % [host, 
result]
+            exit(12)
+        end
+    end
+
+    preinit do
+        [:INT, :TERM].each do |signal|
+            trap(signal) do
+                $stderr.puts "Cancelling"
+                exit(1)
+            end
+        end
+        options[:parallel] = 1
+        options[:verbose] = true
+        options[:fqdn] = true
+
+        @hosts = []
+        @classes = []
+        @tags = []
+    end
+
+    setup do
+        if options[:debug]
+            Puppet::Util::Log.level = :debug
+        else
+            Puppet::Util::Log.level = :info
+        end
+
+        # Now parse the config
+        Puppet.parse_config
+
+        if Puppet[:node_terminus] == "ldap" and (options[:all] or @classes)
+            if options[:all]
+                @hosts = Puppet::Node.search("whatever").collect { |node| 
node.name }
+                puts "all: %s" % @hosts.join(", ")
+            else
+                @hosts = []
+                @classes.each do |klass|
+                    list = Puppet::Node.search("whatever", :class => 
klass).collect { |node| node.name }
+                    puts "%s: %s" % [klass, list.join(", ")]
+
+                    @hosts += list
+                end
+            end
+        elsif ! @classes.empty?
+            $stderr.puts "You must be using LDAP to specify host classes"
+            exit(24)
+        end
+
+        if @tags.empty?
+            @tags = ""
+        else
+            @tags = @tags.join(",")
+        end
+
+        @children = {}
+
+        # If we get a signal, then kill all of our children and get out.
+        [:INT, :TERM].each do |signal|
+            trap(signal) do
+                Puppet.notice "Caught #{signal}; shutting down"
+                @children.each do |pid, host|
+                    Process.kill("INT", pid)
+                end
+
+                waitall
+
+                exit(1)
+            end
+        end
+
+    end
+
+    option(:version) do |arg|
+        puts "%s" % Puppet.version
+        exit
+    end
+
+    option(:host) do |arg|
+        @hosts << arg
+    end
+
+    option(:tag) do |arg|
+        @tags << arg
+    end
+
+    option(:class) do |arg|
+        @classes << arg
+    end
+
+    option(:no_fqdn) do |arg|
+        options[:fqdn] = false
+    end
+
+    option(:parallel) do |arg|
+        begin
+            options[:parallel] = Integer(arg)
+        rescue
+            $stderr.puts "Could not convert %s to an integer" % arg.inspect
+            exit(23)
+        end
+    end
+end
diff --git a/spec/unit/application/puppetrun.rb 
b/spec/unit/application/puppetrun.rb
new file mode 100755
index 0000000..0e10bf0
--- /dev/null
+++ b/spec/unit/application/puppetrun.rb
@@ -0,0 +1,259 @@
+#!/usr/bin/env ruby
+
+require File.dirname(__FILE__) + '/../../spec_helper'
+
+require 'puppet/util/ldap/connection'
+require 'puppet/application/puppetrun'
+
+describe "puppetrun" do
+    before :each do
+        Puppet::Util::Ldap::Connection.stubs(:new).returns(stub_everything)
+        @puppetrun = Puppet::Application[:puppetrun]
+    end
+
+    it "should ask Puppet::Application to not parse Puppet configuration file" 
do
+        @puppetrun.should_parse_config?.should be_false
+    end
+
+    it "should declare a main command" do
+        @puppetrun.should respond_to(:main)
+    end
+
+    it "should declare a test command" do
+        @puppetrun.should respond_to(:test)
+    end
+
+    it "should declare a preinit block" do
+        @puppetrun.should respond_to(:run_preinit)
+    end
+
+    describe "during preinit" do
+        before :each do
+            @puppetrun.stubs(:trap)
+        end
+
+        it "should catch INT and TERM" do
+            @puppetrun.stubs(:trap).with { |arg,block| arg == :INT or arg == 
:TERM }
+
+            @puppetrun.run_preinit
+        end
+
+        it "should set parallel option to 1" do
+            @puppetrun.run_preinit
+
+            @puppetrun.options[:parallel].should == 1
+        end
+
+        it "should set verbose by default" do
+            @puppetrun.run_preinit
+
+            @puppetrun.options[:verbose].should be_true
+        end
+
+        it "should set fqdn by default" do
+            @puppetrun.run_preinit
+
+            @puppetrun.options[:fqdn].should be_true
+        end
+    end
+
+    describe "when applying options" do
+        it "should exit after printing the version" do
+            @puppetrun.stubs(:puts)
+
+            lambda { @puppetrun.handle_version(nil) }.should 
raise_error(SystemExit)
+        end
+
+        it "should add to the host list with the host option" do
+            @puppetrun.handle_host('host')
+
+            @puppetrun.hosts.should == ['host']
+        end
+
+        it "should add to the tag list with the tag option" do
+            @puppetrun.handle_tag('tag')
+
+            @puppetrun.tags.should == ['tag']
+        end
+
+        it "should add to the class list with the class option" do
+            @puppetrun.handle_class('class')
+
+            @puppetrun.classes.should == ['class']
+        end
+    end
+
+    describe "during setup" do
+
+        before :each do
+            @puppetrun.classes = []
+            @puppetrun.tags = []
+            @puppetrun.hosts = []
+            Puppet::Log.stubs(:level=)
+            @puppetrun.stubs(:trap)
+            @puppetrun.stubs(:puts)
+            Puppet.stubs(:parse_config)
+
+            @puppetrun.options.stubs(:[]).with(any_parameters)
+        end
+
+        it "should set log level to debug if --debug was passed" do
+            @puppetrun.options.stubs(:[]).with(:debug).returns(true)
+
+            Puppet::Log.expects(:level=).with(:debug)
+
+            @puppetrun.run_setup
+        end
+
+        it "should set log level to info if --verbose was passed" do
+            @puppetrun.options.stubs(:[]).with(:verbose).returns(true)
+
+            Puppet::Log.expects(:level=).with(:info)
+
+            @puppetrun.run_setup
+        end
+
+        it "should Parse puppet config" do
+            Puppet.expects(:parse_config)
+
+            @puppetrun.run_setup
+        end
+
+        describe "when using the ldap node terminus" do
+            before :each do
+                Puppet.stubs(:[]).with(:node_terminus).returns("ldap")
+            end
+
+            it "should search for all nodes if --all" do
+                @puppetrun.options.stubs(:[]).with(:all).returns(true)
+                @puppetrun.stubs(:puts)
+
+                Puppet::Node.expects(:search).with("whatever").returns([])
+
+                @puppetrun.run_setup
+            end
+
+            it "should search for nodes including given classes" do
+                @puppetrun.options.stubs(:[]).with(:all).returns(false)
+                @puppetrun.stubs(:puts)
+                @puppetrun.classes = ['class']
+
+                Puppet::Node.expects(:search).with("whatever", :class => 
"class").returns([])
+
+                @puppetrun.run_setup
+            end
+        end
+
+        describe "when using regular nodes" do
+            it "should fail if some classes have been specified" do
+                $stderr.stubs(:puts)
+                @puppetrun.classes = ['class']
+
+                @puppetrun.expects(:exit).with(24)
+
+                @puppetrun.run_setup
+            end
+        end
+    end
+
+    describe "when running" do
+        before :each do
+            @puppetrun.stubs(:puts)
+        end
+
+        it "should dispatch to test if --test is used" do
+            @puppetrun.options.stubs(:[]).with(:test).returns(true)
+
+            @puppetrun.get_command.should == :test
+        end
+
+        it "should dispatch to main if --test is not used" do
+            @puppetrun.options.stubs(:[]).with(:test).returns(false)
+
+            @puppetrun.get_command.should == :main
+        end
+
+        describe "the test command" do
+            it "should exit with exit code 0 " do
+                @puppetrun.expects(:exit).with(0)
+
+                @puppetrun.test
+            end
+        end
+
+        describe "the main command" do
+            before :each do
+                @client = stub_everything 'client'
+                @client.stubs(:run).returns("success")
+                Puppet::Network::Client.runner.stubs(:new).returns(@client)
+                @puppetrun.options.stubs(:[]).with(:parallel).returns(1)
+                @puppetrun.options.stubs(:[]).with(:ping).returns(false)
+                
@puppetrun.options.stubs(:[]).with(:ignoreschedules).returns(false)
+                @puppetrun.options.stubs(:[]).with(:foreground).returns(false)
+                @puppetrun.stubs(:print)
+                @puppetrun.stubs(:exit)
+                $stderr.stubs(:puts)
+            end
+
+            it "should create as much childs as --parallel" do
+                @puppetrun.options.stubs(:[]).with(:parallel).returns(3)
+                @puppetrun.hosts = ['host1', 'host2', 'host3']
+                @puppetrun.stubs(:exit).raises(SystemExit)
+                
Process.stubs(:wait).returns(1).then.returns(2).then.returns(3).then.raises(Errno::ECHILD)
+
+                
@puppetrun.expects(:fork).times(3).returns(1).then.returns(2).then.returns(3)
+
+                lambda { @puppetrun.main }.should raise_error
+            end
+
+            it "should delegate to run_for_host per host" do
+                @puppetrun.hosts = ['host1', 'host2']
+                @puppetrun.stubs(:exit).raises(SystemExit)
+                @puppetrun.stubs(:fork).returns(1).yields
+                Process.stubs(:wait).returns(1).then.raises(Errno::ECHILD)
+
+                @puppetrun.expects(:run_for_host).times(2)
+
+                lambda { @puppetrun.main }.should raise_error
+            end
+
+            describe "during call of run_for_host" do
+                it "should create a Runner Client per given host" do
+                    
Puppet::Network::Client.runner.expects(:new).returns(@client)
+
+                    @puppetrun.run_for_host('host')
+                end
+
+                it "should call Client.run for the given host" do
+                    @client.expects(:run)
+
+                    @puppetrun.run_for_host('host')
+                end
+
+                it "should exit the child with 0 on success" do
+                    @client.stubs(:run).returns("success")
+
+                    @puppetrun.expects(:exit).with(0)
+
+                    @puppetrun.run_for_host('host')
+                end
+
+                it "should exit the child with 3 on running" do
+                    @client.stubs(:run).returns("running")
+
+                    @puppetrun.expects(:exit).with(3)
+
+                    @puppetrun.run_for_host('host')
+                end
+
+                it "should exit the child with 12 on unknown answer" do
+                    @client.stubs(:run).returns("whatever")
+
+                    @puppetrun.expects(:exit).with(12)
+
+                    @puppetrun.run_for_host('host')
+                end
+            end
+        end
+    end
+end
-- 
1.6.0.2


--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups 
"Puppet Developers" group.
To post to this group, send email to puppet-dev@googlegroups.com
To unsubscribe from this group, send email to 
puppet-dev+unsubscr...@googlegroups.com
For more options, visit this group at 
http://groups.google.com/group/puppet-dev?hl=en
-~----------~----~----~----~------~----~------~--~---

Reply via email to