provisioner/chef-client: docs and validation key path
This commit is contained in:
parent
d7e6409b9a
commit
000e5b67ff
|
@ -6,10 +6,10 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
server, err := plugin.Server()
|
server, err := plugin.Server()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
server.RegisterProvisioner(new(chefclient.Provisioner))
|
server.RegisterProvisioner(new(chefclient.Provisioner))
|
||||||
server.Serve()
|
server.Serve()
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,14 +25,14 @@ type Config struct {
|
||||||
InstallCommand string `mapstructure:"install_command"`
|
InstallCommand string `mapstructure:"install_command"`
|
||||||
Json map[string]interface{}
|
Json map[string]interface{}
|
||||||
NodeName string `mapstructure:"node_name"`
|
NodeName string `mapstructure:"node_name"`
|
||||||
RunList []string `mapstructure:"run_list"`
|
|
||||||
PreventSudo bool `mapstructure:"prevent_sudo"`
|
PreventSudo bool `mapstructure:"prevent_sudo"`
|
||||||
ServerUrl string `mapstructure:"chef_server_url"`
|
RunList []string `mapstructure:"run_list"`
|
||||||
|
ServerUrl string `mapstructure:"server_url"`
|
||||||
SkipCleanClient bool `mapstructure:"skip_clean_client"`
|
SkipCleanClient bool `mapstructure:"skip_clean_client"`
|
||||||
SkipCleanNode bool `mapstructure:"skip_clean_node"`
|
SkipCleanNode bool `mapstructure:"skip_clean_node"`
|
||||||
SkipInstall bool `mapstructure:"skip_install"`
|
SkipInstall bool `mapstructure:"skip_install"`
|
||||||
StagingDir string `mapstructure:"staging_directory"`
|
StagingDir string `mapstructure:"staging_directory"`
|
||||||
ValidationCommand string `mapstructure:"validation_command"`
|
ValidationKeyPath string `mapstructure:"validation_key_path"`
|
||||||
|
|
||||||
tpl *packer.ConfigTemplate
|
tpl *packer.ConfigTemplate
|
||||||
}
|
}
|
||||||
|
@ -42,8 +42,9 @@ type Provisioner struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type ConfigTemplate struct {
|
type ConfigTemplate struct {
|
||||||
NodeName string
|
NodeName string
|
||||||
ServerUrl string
|
ServerUrl string
|
||||||
|
ValidationKeyPath string
|
||||||
}
|
}
|
||||||
|
|
||||||
type ExecuteTemplate struct {
|
type ExecuteTemplate struct {
|
||||||
|
@ -79,11 +80,6 @@ func (p *Provisioner) Prepare(raws ...interface{}) error {
|
||||||
"{{if .Sudo}}sudo {{end}}bash"
|
"{{if .Sudo}}sudo {{end}}bash"
|
||||||
}
|
}
|
||||||
|
|
||||||
if p.config.ValidationCommand == "" {
|
|
||||||
p.config.ValidationCommand = "{{if .Sudo}}sudo {{end}} mv " +
|
|
||||||
"/tmp/validation.pem /etc/chef/validation.pem"
|
|
||||||
}
|
|
||||||
|
|
||||||
if p.config.RunList == nil {
|
if p.config.RunList == nil {
|
||||||
p.config.RunList = make([]string, 0)
|
p.config.RunList = make([]string, 0)
|
||||||
}
|
}
|
||||||
|
@ -127,9 +123,8 @@ func (p *Provisioner) Prepare(raws ...interface{}) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
validates := map[string]*string{
|
validates := map[string]*string{
|
||||||
"execute_command": &p.config.ExecuteCommand,
|
"execute_command": &p.config.ExecuteCommand,
|
||||||
"install_command": &p.config.InstallCommand,
|
"install_command": &p.config.InstallCommand,
|
||||||
"validation_command": &p.config.ValidationCommand,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for n, ptr := range validates {
|
for n, ptr := range validates {
|
||||||
|
@ -150,6 +145,11 @@ func (p *Provisioner) Prepare(raws ...interface{}) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if p.config.ServerUrl == "" {
|
||||||
|
errs = packer.MultiErrorAppend(
|
||||||
|
errs, fmt.Errorf("server_url must be set"))
|
||||||
|
}
|
||||||
|
|
||||||
// Process the user variables within the JSON and set the JSON.
|
// Process the user variables within the JSON and set the JSON.
|
||||||
// Do this early so that we can validate and show errors.
|
// Do this early so that we can validate and show errors.
|
||||||
p.config.Json, err = p.processJsonUserVars()
|
p.config.Json, err = p.processJsonUserVars()
|
||||||
|
@ -166,6 +166,10 @@ func (p *Provisioner) Prepare(raws ...interface{}) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Provisioner) Provision(ui packer.Ui, comm packer.Communicator) error {
|
func (p *Provisioner) Provision(ui packer.Ui, comm packer.Communicator) error {
|
||||||
|
nodeName := p.config.NodeName
|
||||||
|
remoteValidationKeyPath := ""
|
||||||
|
serverUrl := p.config.ServerUrl
|
||||||
|
|
||||||
if !p.config.SkipInstall {
|
if !p.config.SkipInstall {
|
||||||
if err := p.installChef(ui, comm); err != nil {
|
if err := p.installChef(ui, comm); err != nil {
|
||||||
return fmt.Errorf("Error installing Chef: %s", err)
|
return fmt.Errorf("Error installing Chef: %s", err)
|
||||||
|
@ -176,14 +180,15 @@ func (p *Provisioner) Provision(ui packer.Ui, comm packer.Communicator) error {
|
||||||
return fmt.Errorf("Error creating staging directory: %s", err)
|
return fmt.Errorf("Error creating staging directory: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := p.moveValidation(ui, comm); err != nil {
|
if p.config.ValidationKeyPath != "" {
|
||||||
return fmt.Errorf("Error moving validation.pem: %s", err)
|
remoteValidationKeyPath = fmt.Sprintf("%s/validation.pem", p.config.StagingDir)
|
||||||
|
if err := p.copyValidationKey(ui, comm, remoteValidationKeyPath); err != nil {
|
||||||
|
return fmt.Errorf("Error copying validation key: %s", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
nodeName := p.config.NodeName
|
configPath, err := p.createConfig(
|
||||||
serverUrl := p.config.ServerUrl
|
ui, comm, nodeName, serverUrl, remoteValidationKeyPath)
|
||||||
|
|
||||||
configPath, err := p.createConfig(ui, comm, nodeName, serverUrl)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("Error creating Chef config file: %s", err)
|
return fmt.Errorf("Error creating Chef config file: %s", err)
|
||||||
}
|
}
|
||||||
|
@ -237,7 +242,7 @@ func (p *Provisioner) uploadDirectory(ui packer.Ui, comm packer.Communicator, ds
|
||||||
return comm.UploadDir(dst, src, nil)
|
return comm.UploadDir(dst, src, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Provisioner) createConfig(ui packer.Ui, comm packer.Communicator, nodeName string, serverUrl string) (string, error) {
|
func (p *Provisioner) createConfig(ui packer.Ui, comm packer.Communicator, nodeName string, serverUrl string, remoteKeyPath string) (string, error) {
|
||||||
ui.Message("Creating configuration file 'client.rb'")
|
ui.Message("Creating configuration file 'client.rb'")
|
||||||
|
|
||||||
// Read the template
|
// Read the template
|
||||||
|
@ -258,8 +263,9 @@ func (p *Provisioner) createConfig(ui packer.Ui, comm packer.Communicator, nodeN
|
||||||
}
|
}
|
||||||
|
|
||||||
configString, err := p.config.tpl.Process(tpl, &ConfigTemplate{
|
configString, err := p.config.tpl.Process(tpl, &ConfigTemplate{
|
||||||
NodeName: nodeName,
|
NodeName: nodeName,
|
||||||
ServerUrl: serverUrl,
|
ServerUrl: serverUrl,
|
||||||
|
ValidationKeyPath: remoteKeyPath,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
|
@ -414,26 +420,20 @@ func (p *Provisioner) installChef(ui packer.Ui, comm packer.Communicator) error
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Provisioner) moveValidation(ui packer.Ui, comm packer.Communicator) error {
|
func (p *Provisioner) copyValidationKey(ui packer.Ui, comm packer.Communicator, remotePath string) error {
|
||||||
ui.Message("Moving validation.pem...")
|
ui.Message("Uploading validation key...")
|
||||||
|
|
||||||
command, err := p.config.tpl.Process(p.config.ValidationCommand, &InstallChefTemplate{
|
// First upload the validation key to a writable location
|
||||||
Sudo: !p.config.PreventSudo,
|
f, err := os.Open(p.config.ValidationKeyPath)
|
||||||
})
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
defer f.Close()
|
||||||
|
|
||||||
cmd := &packer.RemoteCmd{Command: command}
|
if err := comm.Upload(remotePath, f); err != nil {
|
||||||
if err := cmd.StartWithUi(comm, ui); err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if cmd.ExitStatus != 0 {
|
|
||||||
return fmt.Errorf(
|
|
||||||
"Move script exited with non-zero exit status %d", cmd.ExitStatus)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -482,6 +482,9 @@ log_level :info
|
||||||
log_location STDOUT
|
log_location STDOUT
|
||||||
chef_server_url "{{.ServerUrl}}"
|
chef_server_url "{{.ServerUrl}}"
|
||||||
validation_client_name "chef-validator"
|
validation_client_name "chef-validator"
|
||||||
|
{{if ne .ValidationKeyPath ""}}
|
||||||
|
validation_key "{{.ValidationKeyPath}}"
|
||||||
|
{{end}}
|
||||||
{{if ne .NodeName ""}}
|
{{if ne .NodeName ""}}
|
||||||
node_name "{{.NodeName}}"
|
node_name "{{.NodeName}}"
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
|
@ -9,7 +9,9 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func testConfig() map[string]interface{} {
|
func testConfig() map[string]interface{} {
|
||||||
return map[string]interface{}{}
|
return map[string]interface{}{
|
||||||
|
"server_url": "foo",
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestProvisioner_Impl(t *testing.T) {
|
func TestProvisioner_Impl(t *testing.T) {
|
||||||
|
@ -67,7 +69,6 @@ func TestProvisionerPrepare_commands(t *testing.T) {
|
||||||
commands := []string{
|
commands := []string{
|
||||||
"execute_command",
|
"execute_command",
|
||||||
"install_command",
|
"install_command",
|
||||||
"validation_command",
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, command := range commands {
|
for _, command := range commands {
|
||||||
|
@ -98,3 +99,23 @@ func TestProvisionerPrepare_commands(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestProvisionerPrepare_serverUrl(t *testing.T) {
|
||||||
|
var p Provisioner
|
||||||
|
|
||||||
|
// Test not set
|
||||||
|
config := testConfig()
|
||||||
|
delete(config, "server_url")
|
||||||
|
err := p.Prepare(config)
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("should error")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test set
|
||||||
|
config = testConfig()
|
||||||
|
config["server_url"] = "foo"
|
||||||
|
err = p.Prepare(config)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("err: %s", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,152 @@
|
||||||
|
---
|
||||||
|
layout: "docs"
|
||||||
|
page_title: "Chef-Client Provisioner"
|
||||||
|
---
|
||||||
|
|
||||||
|
# Chef Client Provisioner
|
||||||
|
|
||||||
|
Type: `chef-client`
|
||||||
|
|
||||||
|
The Chef Client provisioner installs and configures software on machines built
|
||||||
|
by Packer using [chef-client](http://docs.opscode.com/chef_client.html).
|
||||||
|
Packer configures a Chef client to talk to a remote Chef Server to
|
||||||
|
provision the machine.
|
||||||
|
|
||||||
|
The provisioner will even install Chef onto your machine if it isn't already
|
||||||
|
installed, using the official Chef installers provided by Opscode.
|
||||||
|
|
||||||
|
## Basic Example
|
||||||
|
|
||||||
|
The example below is fully functional. It will install Chef onto the
|
||||||
|
remote machine and run Chef client.
|
||||||
|
|
||||||
|
<pre class="prettyprint">
|
||||||
|
{
|
||||||
|
"type": "chef-client",
|
||||||
|
"server_url": "http://mychefserver.com:4000/"
|
||||||
|
}
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
## Configuration Reference
|
||||||
|
|
||||||
|
The reference of available configuration options is listed below. No
|
||||||
|
configuration is actually required, but `node_name` is recommended
|
||||||
|
since it will allow the provisioner to clean up the node/client.
|
||||||
|
|
||||||
|
* `config_template` (string) - Path to a template that will be used for
|
||||||
|
the Chef configuration file. By default Packer only sets configuration
|
||||||
|
it needs to match the settings set in the provisioner configuration. If
|
||||||
|
you need to set configurations that the Packer provisioner doesn't support,
|
||||||
|
then you should use a custom configuration template. See the dedicated
|
||||||
|
"Chef Configuration" section below for more details.
|
||||||
|
|
||||||
|
* `execute_command` (string) - The command used to execute Chef. This has
|
||||||
|
various [configuration template variables](/docs/templates/configuration-templates.html)
|
||||||
|
available. See below for more information.
|
||||||
|
|
||||||
|
* `install_command` (string) - The command used to install Chef. This has
|
||||||
|
various [configuration template variables](/docs/templates/configuration-templates.html)
|
||||||
|
available. See below for more information.
|
||||||
|
|
||||||
|
* `json` (object) - An arbitrary mapping of JSON that will be available as
|
||||||
|
node attributes while running Chef.
|
||||||
|
|
||||||
|
* `node_name` (string) - The name of the node to register with the Chef
|
||||||
|
Server. This is optional and by defalt is empty. If you don't set this,
|
||||||
|
Packer can't clean up the node from the Chef Server using knife.
|
||||||
|
|
||||||
|
* `prevent_sudo` (boolean) - By default, the configured commands that are
|
||||||
|
executed to install and run Chef are executed with `sudo`. If this is true,
|
||||||
|
then the sudo will be omitted.
|
||||||
|
|
||||||
|
* `run_list` (array of strings) - The [run list](http://docs.opscode.com/essentials_node_object_run_lists.html)
|
||||||
|
for Chef. By default this is empty, and will use the run list sent
|
||||||
|
down by the Chef Server.
|
||||||
|
|
||||||
|
* `server_url` (string) - The URL to the Chef server. This is required.
|
||||||
|
|
||||||
|
* `skip_clean_client` (boolean) - If true, Packer won't remove the client
|
||||||
|
from the Chef server after it is done running. By default, this is false.
|
||||||
|
|
||||||
|
* `skip_clean_node` (boolean) - If true, Packer won't remove the node
|
||||||
|
from the Chef server after it is done running. By default, this is false.
|
||||||
|
This will be true by default if `node_name` is not set.
|
||||||
|
|
||||||
|
* `skip_install` (boolean) - If true, Chef will not automatically be installed
|
||||||
|
on the machine using the Opscode omnibus installers.
|
||||||
|
|
||||||
|
* `staging_directory` (string) - This is the directory where all the configuration
|
||||||
|
of Chef by Packer will be placed. By default this is "/tmp/packer-chef-solo".
|
||||||
|
This directory doesn't need to exist but must have proper permissions so that
|
||||||
|
the SSH user that Packer uses is able to create directories and write into
|
||||||
|
this folder. If the permissions are not correct, use a shell provisioner
|
||||||
|
prior to this to configure it properly.
|
||||||
|
|
||||||
|
* `validation_key_path` (string) - Path to the validation key for communicating
|
||||||
|
with the Chef Server. This will be uploaded to the remote machine. If this
|
||||||
|
is NOT set, then it is your responsibility via other means (shell provisioner,
|
||||||
|
etc.) to get a validation key to where Chef expects it.
|
||||||
|
|
||||||
|
## Chef Configuration
|
||||||
|
|
||||||
|
By default, Packer uses a simple Chef configuration file in order to set
|
||||||
|
the options specified for the provisioner. But Chef is a complex tool that
|
||||||
|
supports many configuration options. Packer allows you to specify a custom
|
||||||
|
configuration template if you'd like to set custom configurations.
|
||||||
|
|
||||||
|
The default value for the configuration template is:
|
||||||
|
|
||||||
|
```
|
||||||
|
log_level :info
|
||||||
|
log_location STDOUT
|
||||||
|
chef_server_url "{{.ServerUrl}}"
|
||||||
|
validation_client_name "chef-validator"
|
||||||
|
{{if ne .ValidationKeyPath ""}}
|
||||||
|
validation_key "{{.ValidationKeyPath}}"
|
||||||
|
{{end}}
|
||||||
|
{{if ne .NodeName ""}}
|
||||||
|
node_name "{{.NodeName}}"
|
||||||
|
{{end}}
|
||||||
|
```
|
||||||
|
|
||||||
|
This template is a [configuration template](/docs/templates/configuration-templates.html)
|
||||||
|
and has a set of variables available to use:
|
||||||
|
|
||||||
|
* `NodeName` - The node name set in the configuration.
|
||||||
|
* `ServerUrl` - The URL of the Chef Server set in the configuration.
|
||||||
|
* `ValidationKeyPath` - Path to the validation key, if it is set.
|
||||||
|
|
||||||
|
## Execute Command
|
||||||
|
|
||||||
|
By default, Packer uses the following command (broken across multiple lines
|
||||||
|
for readability) to execute Chef:
|
||||||
|
|
||||||
|
```
|
||||||
|
{{if .Sudo}}sudo {{end}}chef-client \
|
||||||
|
--no-color \
|
||||||
|
-c {{.ConfigPath}} \
|
||||||
|
-j {{.JsonPath}}
|
||||||
|
```
|
||||||
|
|
||||||
|
This command can be customized using the `execute_command` configuration.
|
||||||
|
As you can see from the default value above, the value of this configuration
|
||||||
|
can contain various template variables, defined below:
|
||||||
|
|
||||||
|
* `ConfigPath` - The path to the Chef configuration file.
|
||||||
|
file.
|
||||||
|
* `JsonPath` - The path to the JSON attributes file for the node.
|
||||||
|
* `Sudo` - A boolean of whether to `sudo` the command or not, depending on
|
||||||
|
the value of the `prevent_sudo` configuration.
|
||||||
|
|
||||||
|
## Install Command
|
||||||
|
|
||||||
|
By default, Packer uses the following command (broken across multiple lines
|
||||||
|
for readability) to install Chef. This command can be customized if you want
|
||||||
|
to install Chef in another way.
|
||||||
|
|
||||||
|
```
|
||||||
|
curl -L https://www.opscode.com/chef/install.sh | \
|
||||||
|
{{if .Sudo}}sudo{{end}} bash
|
||||||
|
```
|
||||||
|
|
||||||
|
This command can be customized using the `install_command` configuration.
|
|
@ -46,6 +46,7 @@
|
||||||
<li><a href="/docs/provisioners/shell.html">Shell Scripts</a></li>
|
<li><a href="/docs/provisioners/shell.html">Shell Scripts</a></li>
|
||||||
<li><a href="/docs/provisioners/file.html">File Uploads</a></li>
|
<li><a href="/docs/provisioners/file.html">File Uploads</a></li>
|
||||||
<li><a href="/docs/provisioners/ansible-local.html">Ansible</a></li>
|
<li><a href="/docs/provisioners/ansible-local.html">Ansible</a></li>
|
||||||
|
<li><a href="/docs/provisioners/chef-client.html">Chef Client</a></li>
|
||||||
<li><a href="/docs/provisioners/chef-solo.html">Chef Solo</a></li>
|
<li><a href="/docs/provisioners/chef-solo.html">Chef Solo</a></li>
|
||||||
<li><a href="/docs/provisioners/puppet-masterless.html">Puppet</a></li>
|
<li><a href="/docs/provisioners/puppet-masterless.html">Puppet</a></li>
|
||||||
<li><a href="/docs/provisioners/salt-masterless.html">Salt</a></li>
|
<li><a href="/docs/provisioners/salt-masterless.html">Salt</a></li>
|
||||||
|
|
Loading…
Reference in New Issue