rrichards
5/17/2012 - 8:01 PM

rake script to switch chef servers and configure knife to use them

rake script to switch chef servers and configure knife to use them

source 'http://rubygems.org'

gem 'colored'
gem 'escape'
gem 'facets'
gem 'extensions'
gem 'quality_extensions'
gem 'rscm'
gem 'termios'
gem 'net-ssh'
gem 'net-scp'

# mixlib-cli needs to be at 1.2.0 or 1.2.4+ in order to have the --no-editor switch work
gem 'mixlib-cli', '1.2.0'

gem 'chef', '0.10.2'
gem 'rake'

#
# Rakefile for Chef Server Repository
#

begin
        require 'readline'
        include Readline
rescue LoadError
        # Fall back to a plain prompt
        def readline( text )
                $stderr.print( text.chomp )
                return $stdin.gets
        end
end

begin
        require 'rubygems'
rescue LoadError
        module Gem
                class Specification; end
        end
end

require "json"
require 'rbconfig'
require 'rake'
require 'rake/clean'
require 'pathname'
Rake.application.options.ignore_deprecate = true

### Config constants
BASEDIR       = Pathname.new( __FILE__ ).dirname.relative_path_from( Pathname.getwd )

# Load common, useful tasks from Chef.
# rake -T to see the tasks this loads.
load 'chef/tasks/chef_repo.rake'

load File.join(File.dirname(__FILE__), 'chef.rb')

# encoding: utf-8
#####################################################################
###     G L O B A L   C H E F   F U N C T I O N S
#####################################################################
require "chef/knife"
require "chef/config"
require 'net/ssh'
require 'net/scp'
  
namespace :knife do
  chef_dir = Pathname.new("~/.chef").expand_path() 
  
  desc "Create knife configuration for a server."
  task :configure, :server do |t, args|
    server = args[:server] || nil
    if server == nil
      raise "Server name is required"
    end
    
    # if the chef directory doesn't exist, make it
    chef_dir.mkdir() unless chef_dir.exist?
      
    port = 22
    if server.match(':')
      server, port = server.split(':')
    end
    log "Creating server configuration for: #{server}:#{port}"
    
    create_knife_user(chef_dir, server, port)
    switch_server(chef_dir, server)
    
    # sometimes it doesn't work the first time... o.0
    switch_server(chef_dir, server)
  end
  
  desc "Switch knife configuration to a server."
  task :switch, :server do |t, args|
    server = args[:server] || nil
    if server == nil
      raise "Server name is required"
    end
    
    switch_server(chef_dir, server)
  end
   
  def switch_server(chef_dir, server)
    ["knife.rb", "validation.pem", "#{ENV['USER']}.pem"].each do |file|
      `touch #{chef_dir+file}`
      File.delete(File.join(chef_dir.to_s, file))
      FileUtils.cp(File.join(chef_dir.to_s, file+"-#{server}"), File.join(chef_dir.to_s, file))
    end
  end
 
  def create_knife_user(chef_dir, server, port)
    # clean up any files left over
    ["#{ENV['USER']}.pem-#{server}", "validation.pem-#{server}", "knife.rb-#{server}"].each do |file|
      File.delete(chef_dir+file) unless (chef_dir+file).exist? == false
    end
    
    # copy over the pem file
    log "Connecting to: #{server}:#{port}"
    Net::SSH.start(server, "root", :password => "BBY2010", :port => port) do |ssh|
      ssh.scp.download! "/etc/chef/validation.pem", "#{ENV['HOME']}/.chef/validation.pem-#{server}"
      ssh.scp.download! "/etc/chef/webui.pem", "#{ENV['HOME']}/.chef/webui.pem-#{server}"
    end
    
    log "Removing any existing client..."
    # delete the client out of the server if it exists
    output = `knife client delete #{ENV['USER']} -V -u chef-webui -y -k #{ENV['HOME']}/.chef/webui.pem-#{server} -n -s "http://#{server}:4000"`
    if /ERROR: The object you are looking for could not be found/ =~ output
      error "No client to delete on server..."
    else
      log output
    end
      
    log "Adding client..."
    # write out the knife.rb file
    knife_rb = chef_dir + "knife.rb-#{server}"
    knife_template = File.join(File.dirname(__FILE__),'knife.rb.template')
    
    text = File.read(knife_template).gsub(/SERVER/, "#{server}")
    
    File.open(knife_rb.to_s, "w") {|file| file.puts text}
    
    # NOTE: this requires mixlib-cli version 1.2.0 only... 1.2.2 breaks -n
    log "knife client create #{ENV['USER']} -c #{ENV['HOME']}/.chef/knife.rb-#{server} -f #{chef_dir.to_s}/#{ENV['USER']}.pem-#{server} -u chef-webui -k #{chef_dir.to_s}/webui.pem-#{server} --admin -n"
    log `knife client create #{ENV['USER']} -c #{ENV['HOME']}/.chef/knife.rb-#{server} -f #{chef_dir.to_s}/#{ENV['USER']}.pem-#{server} -u chef-webui -k #{chef_dir.to_s}/webui.pem-#{server} --admin -n`

  end
end

namespace :deploy do
  desc "Upload everything to the current chef server."
  task :all => [:databags, :cookbooks, :roles, :environments] do |t|
    log "All projects updated."
  end
  
  desc "Upload databags to the current chef server."
  task :databags do |t|
    upload_databags get_sitedirs().select{|x| File.directory?(x) && x.match(/base_managed|application/)}
  end
  
  desc "Upload cookbooks to the current chef server."
  task :cookbooks do |t|
    upload_cookbooks get_sitedirs().select{|x| File.directory?(x) && x.match(/base_managed|application/)}
  end

  desc "Upload roles to the current chef server."
  task :roles do |t|
    upload_roles get_sitedirs().select{|x| File.directory?(x) && x.match(/base_managed|application/)}
  end

  desc "Upload environments to the current chef server."
  task :environments do |t|
    upload_environments get_sitedirs().select{|x| File.directory?(x) && x.match(/base_managed|application/)}
  end
      
  def get_sitedirs()
    site_dirs = []
    if (ENV['HOME'] && File.exist?(File.join(ENV['HOME'], '.chef', 'knife.rb')))
      Chef::Config.from_file(File.join(ENV['HOME'], '.chef', 'knife.rb'))
      
      if Chef::Config.cookbook_path.is_a?(String)
        config_cookbook_path = [ Chef::Config.cookbook_path ]
      else
        config_cookbook_path = Chef::Config.cookbook_path
      end
    end
    return config_cookbook_path
  end
  
  def upload_all(directories)
    directories.each do |site_dir|
      upload_databags site_dir
      upload_cookbooks site_dir
      upload_roles site_dir
      upload_environments site_dir
    end
  end
  
  def upload_databags(directories)
    directories.each do |site_dir|
      log "*** #{site_dir} ***"
      log "* Uploading data bags *"
      p = Pathname.new(site_dir) + "../data_bags"
      Dir.glob("#{p.to_s}/**/*.json").each do |data_bag_path|
        upload_data_bag data_bag_path
      end
    end
  end
 
  def upload_cookbooks(directories)
    directories.each do |site_dir|
      log "* Uploading cookbooks *"
      log `knife cookbook upload -a -o #{site_dir}`
    end
  end
  
  def upload_roles(directories)
    directories.each do |site_dir|
      log "* Uploading roles *"
      p = Pathname.new(site_dir) + "../roles"
      Dir.glob("#{p.to_s}/*.rb").each do |role_path|
        log `knife role from file #{role_path}`
      end
    end
  end
  
  def upload_environments(directories)
    directories.each do |site_dir|
      log "* Uploading environments *"
      p = Pathname.new(site_dir) + "../environments"
      Dir.glob("#{p.to_s}/*.rb").each do |env_path|
        log `knife environment from file #{env_path}`
      end
    end
  end
 
  def update_cookbook_version(directory, even, forced_version, verbose = false)
    text = File.read("#{directory}/metadata.rb")
    if text =~ /(^version.*)(\d+.\d+.\d+)/
      version = $2
      if forced_version == nil
        x = version.split('.')
        ver = Integer(x[2]) +1
        x[2] = ver.to_s()
        forced_version = x.join('.')
      end
      
      if verbose
        log "Updating cookbook #{directory} to #{forced_version}"
      end
      replace = text.gsub(/^(version.*)(\d+.\d+.\d+)/, "\\1#{forced_version}")
      File.open("#{directory}/metadata.rb", "w") {|file| file.puts replace}
    end
    return forced_version
  end
  
  def upload_data_bag(data_bag_path)
    name = data_bag_path.match(/.*\/(\w+)\/\w+.json$/)[1]
    if verbose
      trace "  Uploading databag: #{name}"
    end
    check_name = `knife data bag show #{name}`
    if check_name =~ /^ERROR:/
      log `knife data bag create #{name}`
    end
    
    log `knife data bag from file #{name} #{data_bag_path}`
  end
end
client_key               "#{ENV['HOME']}/.chef/#{ENV['USER']}.pem"
validation_key           "#{ENV['HOME']}/.chef/validator.pem"
chef_server_url          'http://SERVER:4000'
log_level                :info
log_location             STDOUT
node_name                "#{ENV['USER']}"
validation_client_name   "chef-validator"
cache_type               'BasicFile'
cache_options( :path => "#{ENV['HOME']}/.chef/checksums" )
cookbook_path           [
    "#{ENV['CHEF_REPO']}/cookbooks",
    "#{ENV['CHEF_REPO']}/site-cookbooks",
]

knife({
  :environment => "_default"
})