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"
})