require 'open-uri' require 'socket' require 'yaml' require 'rest-client' require 'core-ext' STDOUT.sync = true class Runpuppet def self.with_lock(lock_file) recently_locked = (File.exists?(lock_file) and File.mtime(lock_file) > Time.now - 15*60) if recently_locked puts "can not run, lockfile #{lock_file} exists" else begin `touch #{lock_file}` yield ensure `rm #{lock_file}` end end end # simplified copy of rake`s sh def self.sh(cmd) puts cmd IO.popen(cmd) do |pipe| while str = pipe.gets puts str end end $?.success? end def self.get(path, options={}) path += (path.include?('?') ? '&' : '?') path += "hostname=#{Socket.gethostname}&ip=#{Config.local_ip}" begin url = Config.puppet_controller_url + path puts "Getting #{url}" if options[:verbose] RestClient.get(url) rescue Exception => e puts e.inspect puts "WARNING: error connecting in GET (PuppetMaster)" end end def self.post(path, params) params[:hostname] = Socket.gethostname params[:ip] = Config.local_ip begin RestClient.post "#{Config.puppet_controller_url}/puppet/#{path}", params rescue Exception => e puts e.inspect puts "WARNING: error connecting in POST (PuppetMaster)" end end def self.run(options={}) with_lock(Config.lock_file) do time = Time.now.utc.strftime("%Y-%m-%d %H:%M:%S") todo, branch = get("/puppet/run", options).read.split('-') rescue [] puts "got #{todo} with branch #{branch}" if options[:verbose] branch ||= (options[:branch] || Config.default_branch) if todo == 'run' or !options[:try] # try allows not running get '/puppet/status?status=started', options cmd = Config.command.gsub('@@branch@@', branch) puts "#{time}: run #{branch}" if sh(cmd) get '/puppet/status?status=finished', options else get '/puppet/status?status=error', options exit 2 end else puts "#{time}: nothing to do" end end end def self.report_facts(options={}) require 'facter/application' facts = Facter::Application.run([]) post('facts', :facts => facts.to_hash) end class Config def self.config home = File.expand_path('~') ["#{home}/.runpuppet.yml", '/etc/runpuppet.yml'].each do |file| return YAML.load(File.read(file)) if File.exist?(file) end raise "No runpuppet.yml found in /etc or #{home}" end def self.puppet_controller_url config['puppet_controller_url'].sub(%r{/$},'') end def self.puppet_controller_auth extract_auth_from_url!(config['puppet_controller_url']) end def self.extract_auth_from_url!(url) url.sub!(%r{//(.*?):(.*?)@}, '//') auth = [$1, $2].compact auth.empty? ? nil : auth end def self.lock_file config['lock_file'] || '/tmp/runpuppet.lock' end def self.command config['command'] || "cd /etc/puppet/repo && git fetch && git checkout -f origin/@@branch@@ && puppet -v --debug --logdest console --modulepath /etc/puppet/repo/modules /etc/puppet/repo/manifests/site.pp" end def self.default_branch config['branch'] || 'master' end def self.local_ip if is_ec2? require 'facter' require 'facter/ec2' ip = Facter.value("ec2_public_ipv4") elsif config['local_ip_interface'] parse_local_ip_from_interface else %x(ifconfig)[/192.168.\d+.\d+/] || config['local_ip'] end end def self.parse_local_ip_from_interface # %x(ifconfig #{config['local_ip_interface']}).lines.grep(/inet addr/).first.match(/addr:(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})?/) %x(ip addr list #{config['local_ip_interface']}).match(/inet (\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})?/) return $1 end def self.is_ec2? %x(arp -n -i eth0) =~ /fe:ff:ff:ff:ff/ end end end