Merge branch 'sethvargo/chef_apply'
This commit is contained in:
commit
5483cadb87
|
@ -0,0 +1,68 @@
|
|||
module VagrantPlugins
|
||||
module Chef
|
||||
module Config
|
||||
class ChefApply < Vagrant.plugin("2", :config)
|
||||
extend Vagrant::Util::Counter
|
||||
|
||||
# The raw recipe text (as a string) to execute via chef-apply.
|
||||
# @return [String]
|
||||
attr_accessor :recipe
|
||||
|
||||
# The path (on the guest) where the uploaded apply recipe should be
|
||||
# written (/tmp/vagrant-chef-apply-#.rb).
|
||||
# @return [String]
|
||||
attr_accessor :upload_path
|
||||
|
||||
# The Chef log level.
|
||||
# @return [String]
|
||||
attr_accessor :log_level
|
||||
|
||||
def initialize
|
||||
@recipe = UNSET_VALUE
|
||||
|
||||
@log_level = UNSET_VALUE
|
||||
@upload_path = UNSET_VALUE
|
||||
end
|
||||
|
||||
def finalize!
|
||||
@recipe = nil if @recipe == UNSET_VALUE
|
||||
|
||||
if @log_level == UNSET_VALUE
|
||||
@log_level = :info
|
||||
else
|
||||
@log_level = @log_level.to_sym
|
||||
end
|
||||
|
||||
if @upload_path == UNSET_VALUE
|
||||
counter = self.class.get_and_update_counter(:chef_apply)
|
||||
@upload_path = "/tmp/vagrant-chef-apply-#{counter}"
|
||||
end
|
||||
end
|
||||
|
||||
def validate(machine)
|
||||
errors = _detected_errors
|
||||
|
||||
if missing(recipe)
|
||||
errors << I18n.t("vagrant.provisioners.chef.recipe_empty")
|
||||
end
|
||||
|
||||
if missing(log_level)
|
||||
errors << I18n.t("vagrant.provisioners.chef.log_level_empty")
|
||||
end
|
||||
|
||||
if missing(upload_path)
|
||||
errors << I18n.t("vagrant.provisioners.chef.upload_path_empty")
|
||||
end
|
||||
|
||||
{ "chef apply provisioner" => errors }
|
||||
end
|
||||
|
||||
# Determine if the given string is "missing" (blank)
|
||||
# @return [true, false]
|
||||
def missing(obj)
|
||||
obj.to_s.strip.empty?
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -16,23 +16,23 @@ module VagrantPlugins
|
|||
def initialize
|
||||
super
|
||||
|
||||
@chef_server_url = UNSET_VALUE
|
||||
@client_key_path = UNSET_VALUE
|
||||
@delete_client = UNSET_VALUE
|
||||
@delete_node = UNSET_VALUE
|
||||
@validation_key_path = UNSET_VALUE
|
||||
@validation_client_name = UNSET_VALUE
|
||||
@chef_server_url = UNSET_VALUE
|
||||
@client_key_path = UNSET_VALUE
|
||||
@delete_client = UNSET_VALUE
|
||||
@delete_node = UNSET_VALUE
|
||||
@validation_key_path = UNSET_VALUE
|
||||
@validation_client_name = UNSET_VALUE
|
||||
end
|
||||
|
||||
def finalize!
|
||||
super
|
||||
|
||||
@chef_server_url = nil if @chef_server_url == UNSET_VALUE
|
||||
@chef_server_url = nil if @chef_server_url == UNSET_VALUE
|
||||
@client_key_path = "/etc/chef/client.pem" if @client_key_path == UNSET_VALUE
|
||||
@delete_client = false if @delete_client == UNSET_VALUE
|
||||
@delete_node = false if @delete_node == UNSET_VALUE
|
||||
@delete_client = false if @delete_client == UNSET_VALUE
|
||||
@delete_node = false if @delete_node == UNSET_VALUE
|
||||
@validation_client_name = "chef-validator" if @validation_client_name == UNSET_VALUE
|
||||
@validation_key_path = nil if @validation_key_path == UNSET_VALUE
|
||||
@validation_key_path = nil if @validation_key_path == UNSET_VALUE
|
||||
end
|
||||
|
||||
def validate(machine)
|
||||
|
|
|
@ -10,12 +10,12 @@ module VagrantPlugins
|
|||
name "chef"
|
||||
description <<-DESC
|
||||
Provides support for provisioning your virtual machines with
|
||||
Chef via `chef-solo` or `chef-client`.
|
||||
Chef via `chef-solo`, `chef-client`, or `chef-apply`.
|
||||
DESC
|
||||
|
||||
config(:chef_solo, :provisioner) do
|
||||
require_relative "config/chef_solo"
|
||||
Config::ChefSolo
|
||||
config(:chef_apply, :provisioner) do
|
||||
require_relative "config/chef_apply"
|
||||
Config::ChefApply
|
||||
end
|
||||
|
||||
config(:chef_client, :provisioner) do
|
||||
|
@ -23,14 +23,19 @@ module VagrantPlugins
|
|||
Config::ChefClient
|
||||
end
|
||||
|
||||
config(:chef_solo, :provisioner) do
|
||||
require_relative "config/chef_solo"
|
||||
Config::ChefSolo
|
||||
end
|
||||
|
||||
config(:chef_zero, :provisioner) do
|
||||
require_relative "config/chef_zero"
|
||||
Config::ChefZero
|
||||
end
|
||||
|
||||
provisioner(:chef_solo) do
|
||||
require_relative "provisioner/chef_solo"
|
||||
Provisioner::ChefSolo
|
||||
provisioner(:chef_apply) do
|
||||
require_relative "provisioner/chef_apply"
|
||||
Provisioner::ChefApply
|
||||
end
|
||||
|
||||
provisioner(:chef_client) do
|
||||
|
@ -38,6 +43,11 @@ module VagrantPlugins
|
|||
Provisioner::ChefClient
|
||||
end
|
||||
|
||||
provisioner(:chef_solo) do
|
||||
require_relative "provisioner/chef_solo"
|
||||
Provisioner::ChefSolo
|
||||
end
|
||||
|
||||
provisioner(:chef_zero) do
|
||||
require_relative "provisioner/chef_zero"
|
||||
Provisioner::ChefZero
|
||||
|
|
|
@ -0,0 +1,56 @@
|
|||
require "tempfile"
|
||||
|
||||
module VagrantPlugins
|
||||
module Chef
|
||||
module Provisioner
|
||||
class ChefApply < Vagrant.plugin("2", :provisioner)
|
||||
def provision
|
||||
command = "chef-apply"
|
||||
command << " --log-level #{config.log_level}"
|
||||
command << " #{config.upload_path}"
|
||||
|
||||
user = @machine.ssh_info[:username]
|
||||
|
||||
# Reset upload path permissions for the current ssh user
|
||||
@machine.communicate.sudo("mkdir -p #{config.upload_path}")
|
||||
@machine.communicate.sudo("chown -R #{user} #{config.upload_path}")
|
||||
|
||||
# Upload the recipe
|
||||
upload_recipe
|
||||
|
||||
@machine.ui.info(I18n.t("vagrant.provisioners.chef.running_chef_apply",
|
||||
script: config.path)
|
||||
)
|
||||
|
||||
# Execute it with sudo
|
||||
@machine.communicate.sudo(command) do |type, data|
|
||||
if [:stderr, :stdout].include?(type)
|
||||
# Output the data with the proper color based on the stream.
|
||||
color = (type == :stdout) ? :green : :red
|
||||
|
||||
# Chomp the data to avoid the newlines that the Chef outputs
|
||||
@machine.env.ui.info(data.chomp, color: color, prefix: false)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Write the raw recipe contents to a tempfile and upload that to the
|
||||
# machine.
|
||||
def upload_recipe
|
||||
# Write the raw recipe contents to a tempfile
|
||||
file = Tempfile.new(["vagrant-chef-apply", ".rb"])
|
||||
file.write(config.recipe)
|
||||
file.rewind
|
||||
|
||||
# Upload the tempfile to the guest
|
||||
destination = File.join(config.upload_path, "recipe.rb")
|
||||
@machine.communicate.upload(file.path, destination)
|
||||
ensure
|
||||
# Delete our template
|
||||
file.close
|
||||
file.unlink
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1754,10 +1754,18 @@ en:
|
|||
"The cookbook path '%{path}' doesn't exist. Ignoring..."
|
||||
json: "Generating chef JSON and uploading..."
|
||||
client_key_folder: "Creating folder to hold client key..."
|
||||
log_level_empty: |-
|
||||
The Chef provisioner requires a log level. If you did not set a
|
||||
log level, this is probably a bug and should be reported.
|
||||
upload_validation_key: "Uploading chef client validation key..."
|
||||
upload_encrypted_data_bag_secret_key: "Uploading chef encrypted data bag secret key..."
|
||||
recipe_empty: |-
|
||||
Chef Apply provisioning requires that the `config.chef.recipe` be set
|
||||
to a string containing the recipe contents you want to execute on the
|
||||
guest.
|
||||
running_client: "Running chef-client..."
|
||||
running_client_again: "Running chef-client again (failed to converge)..."
|
||||
running_client_apply: "Running chef-apply..."
|
||||
running_solo: "Running chef-solo..."
|
||||
running_solo_again: "Running chef-solo again (failed to converge)..."
|
||||
missing_shared_folders: |-
|
||||
|
@ -1784,6 +1792,9 @@ en:
|
|||
server_validation_key_doesnt_exist: |-
|
||||
The validation key set for `config.chef.validation_key_path` does not exist! This
|
||||
file needs to exist so it can be uploaded to the virtual machine.
|
||||
upload_path_empty: |-
|
||||
The Chef Apply provisioner requires that the `config.chef.upload_path`
|
||||
be set to a non-nil, non-empty value.
|
||||
deleting_from_server: "Deleting %{deletable} \"%{name}\" from Chef server..."
|
||||
|
||||
file:
|
||||
|
|
|
@ -0,0 +1,98 @@
|
|||
require_relative "../../../../base"
|
||||
|
||||
require Vagrant.source_root.join("plugins/provisioners/chef/config/chef_apply")
|
||||
|
||||
describe VagrantPlugins::Chef::Config::ChefApply do
|
||||
include_context "unit"
|
||||
|
||||
subject { described_class.new }
|
||||
|
||||
let(:machine) { double("machine") }
|
||||
|
||||
def chef_error(key, options = {})
|
||||
I18n.t("vagrant.provisioners.chef.#{key}", options)
|
||||
end
|
||||
|
||||
describe "#recipe" do
|
||||
it "defaults to nil" do
|
||||
subject.finalize!
|
||||
expect(subject.recipe).to be(nil)
|
||||
end
|
||||
end
|
||||
|
||||
describe "#log_level" do
|
||||
it "defaults to :info" do
|
||||
subject.finalize!
|
||||
expect(subject.log_level).to be(:info)
|
||||
end
|
||||
|
||||
it "is converted to a symbol" do
|
||||
subject.log_level = "foo"
|
||||
subject.finalize!
|
||||
expect(subject.log_level).to eq(:foo)
|
||||
end
|
||||
end
|
||||
|
||||
describe "#upload_path" do
|
||||
it "defaults to /tmp/vagrant-chef-apply.rb" do
|
||||
subject.finalize!
|
||||
expect(subject.upload_path).to match(%r{/tmp/vagrant-chef-apply-\d+})
|
||||
end
|
||||
end
|
||||
|
||||
describe "#validate" do
|
||||
before do
|
||||
allow(machine).to receive(:env)
|
||||
.and_return(double("env",
|
||||
root_path: "",
|
||||
))
|
||||
|
||||
subject.recipe = <<-EOH
|
||||
package "foo"
|
||||
EOH
|
||||
end
|
||||
|
||||
let(:result) { subject.validate(machine) }
|
||||
let(:errors) { result["chef apply provisioner"] }
|
||||
|
||||
context "when the recipe is nil" do
|
||||
it "returns an error" do
|
||||
subject.recipe = nil
|
||||
subject.finalize!
|
||||
expect(errors).to include chef_error("recipe_empty")
|
||||
end
|
||||
end
|
||||
|
||||
context "when the recipe is empty" do
|
||||
it "returns an error" do
|
||||
subject.recipe = " "
|
||||
subject.finalize!
|
||||
expect(errors).to include chef_error("recipe_empty")
|
||||
end
|
||||
end
|
||||
|
||||
context "when the log_level is an empty array" do
|
||||
it "returns an error" do
|
||||
subject.log_level = " "
|
||||
subject.finalize!
|
||||
expect(errors).to include chef_error("log_level_empty")
|
||||
end
|
||||
end
|
||||
|
||||
context "when the upload_path is nil" do
|
||||
it "returns an error" do
|
||||
subject.upload_path = nil
|
||||
subject.finalize!
|
||||
expect(errors).to include chef_error("upload_path_empty")
|
||||
end
|
||||
end
|
||||
|
||||
context "when the upload_path is an empty array" do
|
||||
it "returns an error" do
|
||||
subject.upload_path = " "
|
||||
subject.finalize!
|
||||
expect(errors).to include chef_error("upload_path_empty")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -174,6 +174,7 @@
|
|||
<li<%= sidebar_current("provisioning-cfengine") %>><a href="/v2/provisioning/cfengine.html">CFEngine</a></li>
|
||||
<li<%= sidebar_current("provisioning-chefsolo") %>><a href="/v2/provisioning/chef_solo.html">Chef Solo</a></li>
|
||||
<li<%= sidebar_current("provisioning-chefclient") %>><a href="/v2/provisioning/chef_client.html">Chef Client</a></li>
|
||||
<li<%= sidebar_current("provisioning-chefapply") %>><a href="/v2/provisioning/chef_apply.html">Chef Apply</a></li>
|
||||
<li<%= sidebar_current("provisioning-docker") %>><a href="/v2/provisioning/docker.html">Docker</a></li>
|
||||
<li<%= sidebar_current("provisioning-puppetapply") %>><a href="/v2/provisioning/puppet_apply.html">Puppet Apply</a></li>
|
||||
<li<%= sidebar_current("provisioning-puppetagent") %>><a href="/v2/provisioning/puppet_agent.html">Puppet Agent</a></li>
|
||||
|
|
|
@ -0,0 +1,102 @@
|
|||
---
|
||||
page_title: "Chef Apply - Provisioning"
|
||||
sidebar_current: "provisioning-chefapply"
|
||||
---
|
||||
|
||||
# Chef Apply Provisioner
|
||||
|
||||
**Provisioner name: `chef_apply`**
|
||||
|
||||
The Chef Apply provisioner allows you to provision the guest using
|
||||
[Chef](https://www.getchef.com/), specifically with
|
||||
[Chef Apply](https://docs.getchef.com/ctl_chef_apply.html).
|
||||
|
||||
Chef Apply is ideal for people who are already experienced with Chef and the
|
||||
Chef ecosystem. Specifically, this documentation page does not cover how use
|
||||
Chef or how to write Chef recipes.
|
||||
|
||||
<div class="alert alert-warn">
|
||||
<p>
|
||||
<strong>Warning:</strong> If you are not familiar with Chef and Vagrant already,
|
||||
we recommend starting with the <a href="/v2/provisioning/shell.html">shell
|
||||
provisioner</a>.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
## Options
|
||||
|
||||
This section lists the complete set of available options for the Chef Apply
|
||||
provisioner. More detailed examples of how to use the provisioner are
|
||||
available below this section.
|
||||
|
||||
Due to the unqiue nature of Chef Apply, the Chef Apply provisioner does not
|
||||
inherit the [common options for other Chef provisioners](/v2/provisioning/chef_common.html).
|
||||
|
||||
* `recipe` (string) - The raw recipe contents to execute using Chef Apply on
|
||||
the guest.
|
||||
|
||||
* `log_level` (string) - The log level to use while executing `chef-apply`. The
|
||||
default value is "info".
|
||||
|
||||
* `upload_path` (string) - **Advanced!** The location on the guest where the
|
||||
generated recipe file should be stored. For most use cases, it is unlikely you
|
||||
will need to customize this value. The default value is
|
||||
`/tmp/vagrant-chef-apply-#` where `#` is a unique counter generated by
|
||||
Vagrant to prevent collisions.
|
||||
|
||||
## Specifying a Recipe
|
||||
|
||||
The easiest way to get started with the Chef Apply provisioner is to just
|
||||
specify an inline
|
||||
[Chef recipe](https://docs.getchef.com/essentials_cookbook_recipes.html). For
|
||||
example:
|
||||
|
||||
```ruby
|
||||
Vagrant.configure("2") do |config|
|
||||
config.vm.provision "chef_apply" do |chef|
|
||||
chef.recipe = "package[apache2]"
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
This causes Vagrant to run Chef Apply with the given recipe contents. If you are
|
||||
familiar with Chef, you know this will install the apache2 package from the
|
||||
system package provider.
|
||||
|
||||
Since single-line Chef recipes are rare, you can also specify the recipe using a
|
||||
"heredoc":
|
||||
|
||||
```ruby
|
||||
Vagrant.configure("2") do |config|
|
||||
config.vm.provision "chef_apply" do |chef|
|
||||
chef.recipe = <<-RECIPE
|
||||
package "apache2"
|
||||
|
||||
template "/etc/apache2/my.config" do
|
||||
# ...
|
||||
end
|
||||
RECIPE
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
Finally, if you would prefer to store the recipe as plain-text, you can set the
|
||||
recipe to the contents of a file:
|
||||
|
||||
```ruby
|
||||
Vagrant.configure("2") do |config|
|
||||
config.vm.provision "chef_apply" do |chef|
|
||||
chef.recipe = File.read("/path/to/my/recipe.rb")
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
## Roles
|
||||
|
||||
The Vagrant Chef Apply provisioner does not support roles. Please use the a
|
||||
different Vagrant Chef provisioner if you need support for roles.
|
||||
|
||||
## Data Bags
|
||||
|
||||
The Vagrant Chef Apply provisioner does not support data_bags. Please use the a
|
||||
different Vagrant Chef provisioner if you need support for data_bags.
|
Loading…
Reference in New Issue