Chef server provisioner

This commit is contained in:
Mitchell Hashimoto 2010-03-10 21:57:51 -08:00
parent df648803dd
commit df2e80140d
6 changed files with 226 additions and 2 deletions

View File

@ -6,11 +6,20 @@ module Vagrant
class Chef < Base
# This is the configuration which is available through `config.chef`
class ChefConfig < Vagrant::Config::Base
# Chef server specific config
attr_accessor :chef_server_url
attr_accessor :validation_key_path
attr_accessor :validation_client_name
# Chef solo specific config
attr_accessor :cookbooks_path
# Shared config
attr_accessor :provisioning_path
attr_accessor :json
def initialize
@validation_client_name = "chef-validator"
@cookbooks_path = "cookbooks"
@provisioning_path = "/tmp/vagrant-chef"
@json = {
@ -38,6 +47,7 @@ module Vagrant
def chown_provisioning_folder
logger.info "Setting permissions on chef provisioning folder..."
SSH.execute do |ssh|
ssh.exec!("sudo mkdir -p #{Vagrant.config.chef.provisioning_path}")
ssh.exec!("sudo chown #{Vagrant.config.ssh.username} #{Vagrant.config.chef.provisioning_path}")
end
end

View File

@ -0,0 +1,76 @@
module Vagrant
module Provisioners
# This class implements provisioning via chef-client, allowing provisioning
# with a chef server.
class ChefServer < Chef
def prepare
if Vagrant.config.chef.validation_key_path.nil?
raise Actions::ActionException.new(<<-msg)
Chef server provisioning requires that the `config.chef.validation_key_path` configuration
be set to a path on your local machine of the validation key used to register the
VM with the chef server.
msg
end
if Vagrant.config.chef.chef_server_url.nil?
raise Actions::ActionException.new(<<-msg)
Chef server provisioning requires that the `config.chef.chef_server_url` be set to the
URL of your chef server. Examples include "http://12.12.12.12:4000" and
"http://myserver.com:4000" (the port of course can be different, but 4000 is the default)
msg
end
end
def provision!
chown_provisioning_folder
upload_validation_key
setup_json
setup_config
run_chef_client
end
def upload_validation_key
logger.info "Uploading chef client validation key..."
SSH.upload!(Vagrant.config.chef.validation_key_path, guest_validation_key_path)
end
def setup_config
solo_file = <<-solo
log_level :info
log_location STDOUT
ssl_verify_mode :verify_none
chef_server_url "#{Vagrant.config.chef.chef_server_url}"
validation_client_name "#{Vagrant.config.chef.validation_client_name}"
validation_key "#{guest_validation_key_path}"
client_key "/etc/chef/client.pem"
file_store_path "/srv/chef/file_store"
file_cache_path "/srv/chef/cache"
pid_file "/var/run/chef/chef-client.pid"
Mixlib::Log::Formatter.show_time = true
solo
logger.info "Uploading chef-client configuration script..."
SSH.upload!(StringIO.new(solo_file), File.join(Vagrant.config.chef.provisioning_path, "client.rb"))
end
def run_chef_client
logger.info "Running chef-client..."
SSH.execute do |ssh|
ssh.exec!("cd #{Vagrant.config.chef.provisioning_path} && sudo chef-client -c client.rb -j dna.json") do |channel, data, stream|
# TODO: Very verbose. It would be easier to save the data and only show it during
# an error, or when verbosity level is set high
logger.info("#{stream}: #{data}")
end
end
end
def guest_validation_key_path
File.join(Vagrant.config.chef.provisioning_path, "validation.pem")
end
end
end
end

View File

@ -43,6 +43,8 @@ class Test::Unit::TestCase
config.package.extension = '.box'
# Chef
config.chef.chef_server_url = "http://localhost:4000"
config.chef.validation_key_path = "validation.pem"
config.chef.cookbooks_path = "cookbooks"
config.chef.provisioning_path = "/tmp/hobo-chef"
config.chef.json = {

View File

@ -0,0 +1,123 @@
require File.join(File.dirname(__FILE__), '..', '..', 'test_helper')
class ChefServerProvisionerTest < Test::Unit::TestCase
setup do
@action = Vagrant::Provisioners::ChefServer.new
Vagrant::SSH.stubs(:execute)
Vagrant::SSH.stubs(:upload!)
mock_config
end
context "provisioning" do
should "run the proper sequence of methods in order" do
prov_seq = sequence("prov_seq")
@action.expects(:chown_provisioning_folder).once.in_sequence(prov_seq)
@action.expects(:upload_validation_key).once.in_sequence(prov_seq)
@action.expects(:setup_json).once.in_sequence(prov_seq)
@action.expects(:setup_config).once.in_sequence(prov_seq)
@action.expects(:run_chef_client).once.in_sequence(prov_seq)
@action.provision!
end
end
context "preparing" do
should "not raise an exception if validation_key_path is set" do
mock_config do |config|
config.chef.validation_key_path = "7"
end
assert_nothing_raised { @action.prepare }
end
should "raise an exception if validation_key_path is nil" do
mock_config do |config|
config.chef.validation_key_path = nil
end
assert_raises(Vagrant::Actions::ActionException) {
@action.prepare
}
end
should "not raise an exception if chef_server_url is set" do
mock_config do |config|
config.chef.chef_server_url = "7"
end
assert_nothing_raised { @action.prepare }
end
should "raise an exception if chef_server_url is nil" do
mock_config do |config|
config.chef.chef_server_url = nil
end
assert_raises(Vagrant::Actions::ActionException) {
@action.prepare
}
end
end
context "uploading the validation key" do
should "upload the validation key to the provisioning path" do
@action.expects(:guest_validation_key_path).once.returns("bar")
Vagrant::SSH.expects(:upload!).with(Vagrant.config.chef.validation_key_path, "bar").once
@action.upload_validation_key
end
end
context "the guest validation key path" do
should "be the provisioning path joined with validation.pem" do
result = mock("result")
File.expects(:join).with(Vagrant.config.chef.provisioning_path, "validation.pem").once.returns(result)
assert_equal result, @action.guest_validation_key_path
end
end
context "generating and uploading chef client configuration file" do
setup do
@action.stubs(:guest_validation_key_path).returns("foo")
end
should "upload properly generate the configuration file using configuration data" do
expected_config = <<-config
log_level :info
log_location STDOUT
ssl_verify_mode :verify_none
chef_server_url "#{Vagrant.config.chef.chef_server_url}"
validation_client_name "#{Vagrant.config.chef.validation_client_name}"
validation_key "#{@action.guest_validation_key_path}"
client_key "/etc/chef/client.pem"
file_store_path "/srv/chef/file_store"
file_cache_path "/srv/chef/cache"
pid_file "/var/run/chef/chef-client.pid"
Mixlib::Log::Formatter.show_time = true
config
StringIO.expects(:new).with(expected_config).once
@action.setup_config
end
should "upload this file as client.rb to the provisioning folder" do
StringIO.expects(:new).returns("foo")
File.expects(:join).with(Vagrant.config.chef.provisioning_path, "client.rb").once.returns("bar")
Vagrant::SSH.expects(:upload!).with("foo", "bar").once
@action.setup_config
end
end
context "running chef client" do
should "cd into the provisioning directory and run chef client" do
ssh = mock("ssh")
ssh.expects(:exec!).with("cd #{Vagrant.config.chef.provisioning_path} && sudo chef-client -c client.rb -j dna.json").once
Vagrant::SSH.expects(:execute).yields(ssh)
@action.run_chef_client
end
end
end

View File

@ -10,6 +10,17 @@ class ChefSoloProvisionerTest < Test::Unit::TestCase
mock_config
end
context "provisioning" do
should "run the proper sequence of methods in order" do
prov_seq = sequence("prov_seq")
@action.expects(:chown_provisioning_folder).once.in_sequence(prov_seq)
@action.expects(:setup_json).once.in_sequence(prov_seq)
@action.expects(:setup_solo_config).once.in_sequence(prov_seq)
@action.expects(:run_chef_solo).once.in_sequence(prov_seq)
@action.provision!
end
end
context "shared folders" do
should "setup shared folder on VM for the cookbooks" do
File.expects(:expand_path).with(Vagrant.config.chef.cookbooks_path, Vagrant::Env.root_path).returns("foo")

View File

@ -31,9 +31,11 @@ class ChefProvisionerTest < Test::Unit::TestCase
end
context "permissions on provisioning folder" do
should "chown the folder to the ssh user" do
should "create and chown the folder to the ssh user" do
ssh_seq = sequence("ssh_seq")
ssh = mock("ssh")
ssh.expects(:exec!).with("sudo chown #{Vagrant.config.ssh.username} #{Vagrant.config.chef.provisioning_path}")
ssh.expects(:exec!).with("sudo mkdir -p #{Vagrant.config.chef.provisioning_path}").once.in_sequence(ssh_seq)
ssh.expects(:exec!).with("sudo chown #{Vagrant.config.ssh.username} #{Vagrant.config.chef.provisioning_path}").once.in_sequence(ssh_seq)
Vagrant::SSH.expects(:execute).yields(ssh)
@action.chown_provisioning_folder
end