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 class Chef < Base
# This is the configuration which is available through `config.chef` # This is the configuration which is available through `config.chef`
class ChefConfig < Vagrant::Config::Base 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 attr_accessor :cookbooks_path
# Shared config
attr_accessor :provisioning_path attr_accessor :provisioning_path
attr_accessor :json attr_accessor :json
def initialize def initialize
@validation_client_name = "chef-validator"
@cookbooks_path = "cookbooks" @cookbooks_path = "cookbooks"
@provisioning_path = "/tmp/vagrant-chef" @provisioning_path = "/tmp/vagrant-chef"
@json = { @json = {
@ -38,6 +47,7 @@ module Vagrant
def chown_provisioning_folder def chown_provisioning_folder
logger.info "Setting permissions on chef provisioning folder..." logger.info "Setting permissions on chef provisioning folder..."
SSH.execute do |ssh| 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}") ssh.exec!("sudo chown #{Vagrant.config.ssh.username} #{Vagrant.config.chef.provisioning_path}")
end end
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' config.package.extension = '.box'
# Chef # Chef
config.chef.chef_server_url = "http://localhost:4000"
config.chef.validation_key_path = "validation.pem"
config.chef.cookbooks_path = "cookbooks" config.chef.cookbooks_path = "cookbooks"
config.chef.provisioning_path = "/tmp/hobo-chef" config.chef.provisioning_path = "/tmp/hobo-chef"
config.chef.json = { 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 mock_config
end 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 context "shared folders" do
should "setup shared folder on VM for the cookbooks" 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") 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 end
context "permissions on provisioning folder" do 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 = 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) Vagrant::SSH.expects(:execute).yields(ssh)
@action.chown_provisioning_folder @action.chown_provisioning_folder
end end