summaryrefslogtreecommitdiff
path: root/bin/puppet_command
diff options
context:
space:
mode:
Diffstat (limited to 'bin/puppet_command')
-rwxr-xr-xbin/puppet_command191
1 files changed, 191 insertions, 0 deletions
diff --git a/bin/puppet_command b/bin/puppet_command
new file mode 100755
index 00000000..a6cd5a69
--- /dev/null
+++ b/bin/puppet_command
@@ -0,0 +1,191 @@
+#!/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'
+DEFAULT_TAGS = 'leap_base,leap_service'
+
+def main
+ process_command_line_arguments
+ with_lockfile do
+ @commands.each do |command|
+ self.send(command)
+ end
+ end
+end
+
+def puts(str)
+ $stdout.puts str
+ $stdout.flush
+end
+
+def process_command_line_arguments
+ @commands = []
+ @verbosity = 1
+ @tags = DEFAULT_TAGS
+ 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?
+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: replace setup.pp with https://github.com/lutter/ruby-augeas
+ # or try this: http://www.puppetcookbook.com/posts/override-a-facter-fact.html
+ if (line !~ /Finished catalog run/ || @verbosity > 2) &&
+ (line !~ /dnsdomainname: Name or service not known/) &&
+ (line !~ /warning: Could not retrieve fact fqdn/)
+ 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