diff options
| -rwxr-xr-x | bin/puppet_command | 183 | 
1 files changed, 183 insertions, 0 deletions
| diff --git a/bin/puppet_command b/bin/puppet_command new file mode 100755 index 00000000..672a3699 --- /dev/null +++ b/bin/puppet_command @@ -0,0 +1,183 @@ +#!/usr/bin/ruby + +# +# This is a wrapper script around the puppet command used by the LEAP platform. +# +# We do this in order to make it faster and easier to control puppet remotely +# (exit codes, lockfile, multiple manifests, etc) +# + +$puppet_bin = '/usr/bin/puppet' +$puppet_directory = '/srv/leap' +$puppet_parameters = '--color=false --detailed-exitcodes --libdir=puppet/lib --confdir=puppet' +$site_manifest = 'puppet/manifests/site.pp' +$setup_manifest = 'puppet/manifests/setup.pp' + +def main +  @commands = [] +  loop do +    case ARGV[0] +      when 'apply'        then ARGV.shift; @commands << 'apply' +      when 'set_hostname' then ARGV.shift; @commands << 'set_hostname' +      when '--verbosity'  then ARGV.shift; @verbosity = ARGV.shift.to_i +      when '--force'      then ARGV.shift; remove_lockfile +      when '--tags'       then ARGV.shift; @tags      = ARGV.shift +      when /^-/           then usage("Unknown option: #{ARGV[0].inspect}") +      else break +    end +  end +  usage("No command given") unless @commands.any? +  with_lockfile do +    @commands.each do |command| +      self.send(command) +    end +  end +end + +def puts(str) +  $stdout.puts str +  $stdout.flush +end + +def apply +  exit_code = puppet_apply do |line| +    puts line +  end +  puts "Puppet apply complete (#{exitcode_description(exit_code)})." +end + +def set_hostname +  exit_code = puppet_apply(:manifest => $setup_manifest, :tags => '') do |line| +    # todo: how to suppress this? +    # dnsdomainname: Name or service not known +    # warning: Could not retrieve fact fqdn +    if line !~ /Finished catalog run/ || @verbosity > 2 +      puts line +    end +  end +  if exit_code == 2 +    puts "Hostname updated." +  elsif exit_code == 4 || exit_code == 6 +    puts "ERROR: could not update hostname." +  elsif exit_code == 0 && @verbosity > 1 +    puts "No change to hostname." +  end +end + +# +# each line of output is yielded. the exit code is returned. +# +def puppet_apply(options={}, &block) +  options = {:verbosity => @verbosity, :tags => @tags}.merge(options) +  manifest = options[:manifest] || $site_manifest +  Dir.chdir($puppet_directory) do +    return run("#{$puppet_bin} apply #{custom_parameters(options)} #{$puppet_parameters} #{manifest}", &block) +  end +end + +def custom_parameters(options) +  params = [] +  if options[:tags] && options[:tags].chars.any? +    params << "--tags #{options[:tags]}" +  end +  if options[:verbosity] +    case options[:verbosity] +      when 3 then params << '--verbose' +      when 4 then params << '--verbose --debug' +      when 5 then params << '--verbose --debug --trace' +    end +  end +  params.join(' ') +end + +def exitcode_description(code) +  case code +    when 0 then "no changes" +    when 1 then "failed" +    when 2 then "changes made" +    when 4 then "failed" +    when 6 then "changes and failures" +    else code +  end +end + +def usage(s) +  $stderr.puts(s) +  $stderr.puts +  $stderr.puts("Usage: #{File.basename($0)} COMMAND [OPTIONS]") +  $stderr.puts +  $stderr.puts("COMMAND may be one or more of: +  set_hostname     -- set the hostname of this server +  apply            -- apply puppet manifests") +  $stderr.puts +  $stderr.puts("OPTIONS may be one or more of: +  --verbosity VERB -- set the verbosity level 0..5 +  --tags TAGS      -- set the tags to pass through to puppet +  --force          -- run even when lockfile is present") +  exit(2) +end + +## +## Simple lock file +## + +require 'fileutils' +DEFAULT_LOCKFILE = '/tmp/puppet.lock' + +def remove_lockfile(lock_file_path=DEFAULT_LOCKFILE) +  FileUtils.remove_file(lock_file_path, true) +end + +def with_lockfile(lock_file_path=DEFAULT_LOCKFILE) +  begin +    File.open(lock_file_path, File::CREAT | File::EXCL | File::WRONLY) do |o| +      o.write(Process.pid) +    end +    yield +    remove_lockfile +  rescue Errno::EEXIST +    puts("ERROR: the lock file '#{lock_file_path}' already exists. Wait a minute for the process to die, or run with --force to ignore. Bailing out.") +    exit(1) +  rescue IOError => exc +    puts("ERROR: problem with lock file '#{lock_file_path}' (#{exc}). Bailing out.") +    exit(1) +  end +end + +## +## simple pass through process runner (to ensure output is not buffered and return exit code) +## this only works under ruby 1.9 +## + +require "pty" + +def run(cmd) +  puts cmd if @verbosity >= 3 +  PTY.spawn("#{cmd}") do |output, input, pid| +    begin +      while line = output.gets do +        yield line +        #$stdout.puts line +        #$stdout.flush +      end +    rescue Errno::EIO +    end +    Process.wait(pid) # only works in ruby 1.9, required to capture the exit status. +  end +  status = $?.exitstatus +  #yield status if block_given? +  return status +rescue PTY::ChildExited +end + +## +## RUN MAIN +## + +Signal.trap("EXIT") do +  remove_lockfile # clean up the lockfile when process is terminated. +                  # this will remove the lockfile if ^C killed the process +                  # but only after the child puppet process is also dead (I think). +end + +main()
\ No newline at end of file | 
