Remove Chef components and docs

This commit is contained in:
Wilken Rivera 2021-04-16 13:45:50 -04:00
parent 2da9c21733
commit 30bcf44c2c
12 changed files with 0 additions and 3011 deletions

View File

@ -50,8 +50,6 @@ import (
yandeximportpostprocessor "github.com/hashicorp/packer/post-processor/yandex-import"
azuredtlartifactprovisioner "github.com/hashicorp/packer/provisioner/azure-dtlartifact"
breakpointprovisioner "github.com/hashicorp/packer/provisioner/breakpoint"
chefclientprovisioner "github.com/hashicorp/packer/provisioner/chef-client"
chefsoloprovisioner "github.com/hashicorp/packer/provisioner/chef-solo"
convergeprovisioner "github.com/hashicorp/packer/provisioner/converge"
fileprovisioner "github.com/hashicorp/packer/provisioner/file"
inspecprovisioner "github.com/hashicorp/packer/provisioner/inspec"
@ -100,8 +98,6 @@ var Builders = map[string]packersdk.Builder{
var Provisioners = map[string]packersdk.Provisioner{
"azure-dtlartifact": new(azuredtlartifactprovisioner.Provisioner),
"breakpoint": new(breakpointprovisioner.Provisioner),
"chef-client": new(chefclientprovisioner.Provisioner),
"chef-solo": new(chefsoloprovisioner.Provisioner),
"converge": new(convergeprovisioner.Provisioner),
"file": new(fileprovisioner.Provisioner),
"inspec": new(inspecprovisioner.Provisioner),

View File

@ -1,782 +0,0 @@
//go:generate packer-sdc mapstructure-to-hcl2 -type Config
// This package implements a provisioner for Packer that uses
// Chef to provision the remote machine, specifically with chef-client (that is,
// with a Chef server).
package chefclient
import (
"bytes"
"context"
"encoding/json"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"strings"
"github.com/hashicorp/hcl/v2/hcldec"
"github.com/hashicorp/packer-plugin-sdk/common"
"github.com/hashicorp/packer-plugin-sdk/guestexec"
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
"github.com/hashicorp/packer-plugin-sdk/pathing"
"github.com/hashicorp/packer-plugin-sdk/template/config"
"github.com/hashicorp/packer-plugin-sdk/template/interpolate"
"github.com/hashicorp/packer-plugin-sdk/uuid"
)
type guestOSTypeConfig struct {
executeCommand string
installCommand string
knifeCommand string
stagingDir string
}
var guestOSTypeConfigs = map[string]guestOSTypeConfig{
guestexec.UnixOSType: {
executeCommand: "{{if .Sudo}}sudo {{end}}chef-client --no-color -c {{.ConfigPath}} -j {{.JsonPath}}",
installCommand: "curl -L https://omnitruck.chef.io/install.sh | {{if .Sudo}}sudo {{end}}bash -s --{{if .Version}} -v {{.Version}}{{end}}",
knifeCommand: "{{if .Sudo}}sudo {{end}}knife {{.Args}} {{.Flags}}",
stagingDir: "/tmp/packer-chef-client",
},
guestexec.WindowsOSType: {
executeCommand: "c:/opscode/chef/bin/chef-client.bat --no-color -c {{.ConfigPath}} -j {{.JsonPath}}",
installCommand: "powershell.exe -Command \". { iwr -useb https://omnitruck.chef.io/install.ps1 } | iex; Install-Project{{if .Version}} -version {{.Version}}{{end}}\"",
knifeCommand: "c:/opscode/chef/bin/knife.bat {{.Args}} {{.Flags}}",
stagingDir: "C:/Windows/Temp/packer-chef-client",
},
}
type Config struct {
common.PackerConfig `mapstructure:",squash"`
Json map[string]interface{}
ChefEnvironment string `mapstructure:"chef_environment"`
ChefLicense string `mapstructure:"chef_license"`
ClientKey string `mapstructure:"client_key"`
ConfigTemplate string `mapstructure:"config_template"`
ElevatedUser string `mapstructure:"elevated_user"`
ElevatedPassword string `mapstructure:"elevated_password"`
EncryptedDataBagSecretPath string `mapstructure:"encrypted_data_bag_secret_path"`
ExecuteCommand string `mapstructure:"execute_command"`
GuestOSType string `mapstructure:"guest_os_type"`
InstallCommand string `mapstructure:"install_command"`
KnifeCommand string `mapstructure:"knife_command"`
NodeName string `mapstructure:"node_name"`
PolicyGroup string `mapstructure:"policy_group"`
PolicyName string `mapstructure:"policy_name"`
PreventSudo bool `mapstructure:"prevent_sudo"`
RunList []string `mapstructure:"run_list"`
ServerUrl string `mapstructure:"server_url"`
SkipCleanClient bool `mapstructure:"skip_clean_client"`
SkipCleanNode bool `mapstructure:"skip_clean_node"`
SkipCleanStagingDirectory bool `mapstructure:"skip_clean_staging_directory"`
SkipInstall bool `mapstructure:"skip_install"`
SslVerifyMode string `mapstructure:"ssl_verify_mode"`
TrustedCertsDir string `mapstructure:"trusted_certs_dir"`
StagingDir string `mapstructure:"staging_directory"`
ValidationClientName string `mapstructure:"validation_client_name"`
ValidationKeyPath string `mapstructure:"validation_key_path"`
Version string `mapstructure:"version"`
ctx interpolate.Context
}
type Provisioner struct {
config Config
communicator packersdk.Communicator
guestOSTypeConfig guestOSTypeConfig
guestCommands *guestexec.GuestCommands
generatedData map[string]interface{}
}
type ConfigTemplate struct {
ChefEnvironment string
ChefLicense string
ClientKey string
EncryptedDataBagSecretPath string
NodeName string
PolicyGroup string
PolicyName string
ServerUrl string
SslVerifyMode string
TrustedCertsDir string
ValidationClientName string
ValidationKeyPath string
}
type ExecuteTemplate struct {
ConfigPath string
JsonPath string
Sudo bool
}
type InstallChefTemplate struct {
Sudo bool
Version string
}
type KnifeTemplate struct {
Sudo bool
Flags string
Args string
}
func (p *Provisioner) ConfigSpec() hcldec.ObjectSpec { return p.config.FlatMapstructure().HCL2Spec() }
func (p *Provisioner) Prepare(raws ...interface{}) error {
err := config.Decode(&p.config, &config.DecodeOpts{
PluginType: "chef-client",
Interpolate: true,
InterpolateContext: &p.config.ctx,
InterpolateFilter: &interpolate.RenderFilter{
Exclude: []string{
"execute_command",
"install_command",
"knife_command",
},
},
}, raws...)
if err != nil {
return err
}
if p.config.GuestOSType == "" {
p.config.GuestOSType = guestexec.DefaultOSType
}
p.config.GuestOSType = strings.ToLower(p.config.GuestOSType)
var ok bool
p.guestOSTypeConfig, ok = guestOSTypeConfigs[p.config.GuestOSType]
if !ok {
return fmt.Errorf("Invalid guest_os_type: \"%s\"", p.config.GuestOSType)
}
p.guestCommands, err = guestexec.NewGuestCommands(p.config.GuestOSType, !p.config.PreventSudo)
if err != nil {
return fmt.Errorf("Invalid guest_os_type: \"%s\"", p.config.GuestOSType)
}
if p.config.ExecuteCommand == "" {
p.config.ExecuteCommand = p.guestOSTypeConfig.executeCommand
}
if p.config.InstallCommand == "" {
p.config.InstallCommand = p.guestOSTypeConfig.installCommand
}
if p.config.RunList == nil {
p.config.RunList = make([]string, 0)
}
if p.config.StagingDir == "" {
p.config.StagingDir = p.guestOSTypeConfig.stagingDir
}
if p.config.KnifeCommand == "" {
p.config.KnifeCommand = p.guestOSTypeConfig.knifeCommand
}
var errs *packersdk.MultiError
if p.config.ConfigTemplate != "" {
fi, err := os.Stat(p.config.ConfigTemplate)
if err != nil {
errs = packersdk.MultiErrorAppend(
errs, fmt.Errorf("Bad config template path: %s", err))
} else if fi.IsDir() {
errs = packersdk.MultiErrorAppend(
errs, fmt.Errorf("Config template path must be a file: %s", err))
}
}
if p.config.ServerUrl == "" {
errs = packersdk.MultiErrorAppend(
errs, fmt.Errorf("server_url must be set"))
}
if p.config.SkipInstall == false && p.config.InstallCommand == p.guestOSTypeConfig.installCommand {
if p.config.ChefLicense == "" {
p.config.ChefLicense = "accept-silent"
}
}
if p.config.EncryptedDataBagSecretPath != "" {
pFileInfo, err := os.Stat(p.config.EncryptedDataBagSecretPath)
if err != nil || pFileInfo.IsDir() {
errs = packersdk.MultiErrorAppend(
errs, fmt.Errorf("Bad encrypted data bag secret '%s': %s", p.config.EncryptedDataBagSecretPath, err))
}
}
if (p.config.PolicyName != "") != (p.config.PolicyGroup != "") {
errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("If either policy_name or policy_group are set, they must both be set."))
}
jsonValid := true
for k, v := range p.config.Json {
p.config.Json[k], err = p.deepJsonFix(k, v)
if err != nil {
errs = packersdk.MultiErrorAppend(
errs, fmt.Errorf("Error processing JSON: %s", err))
jsonValid = false
}
}
if jsonValid {
// Process the user variables within the JSON and set the JSON.
// Do this early so that we can validate and show errors.
p.config.Json, err = p.processJsonUserVars()
if err != nil {
errs = packersdk.MultiErrorAppend(
errs, fmt.Errorf("Error processing user variables in JSON: %s", err))
}
}
if errs != nil && len(errs.Errors) > 0 {
return errs
}
return nil
}
func (p *Provisioner) Provision(ctx context.Context, ui packersdk.Ui, comm packersdk.Communicator, generatedData map[string]interface{}) error {
p.generatedData = generatedData
p.communicator = comm
nodeName := p.config.NodeName
if nodeName == "" {
nodeName = fmt.Sprintf("packer-%s", uuid.TimeOrderedUUID())
}
remoteValidationKeyPath := ""
serverUrl := p.config.ServerUrl
if !p.config.SkipInstall {
if err := p.installChef(ui, comm, p.config.Version); err != nil {
return fmt.Errorf("Error installing Chef: %s", err)
}
}
if err := p.createDir(ui, comm, p.config.StagingDir); err != nil {
return fmt.Errorf("Error creating staging directory: %s", err)
}
if p.config.ClientKey == "" {
p.config.ClientKey = fmt.Sprintf("%s/client.pem", p.config.StagingDir)
}
encryptedDataBagSecretPath := ""
if p.config.EncryptedDataBagSecretPath != "" {
encryptedDataBagSecretPath = fmt.Sprintf("%s/encrypted_data_bag_secret", p.config.StagingDir)
if err := p.uploadFile(ui,
comm,
encryptedDataBagSecretPath,
p.config.EncryptedDataBagSecretPath); err != nil {
return fmt.Errorf("Error uploading encrypted data bag secret: %s", err)
}
}
if p.config.ValidationKeyPath != "" {
path, err := pathing.ExpandUser(p.config.ValidationKeyPath)
if err != nil {
return fmt.Errorf("Error while expanding a tilde in the validation key: %s", err)
}
remoteValidationKeyPath = fmt.Sprintf("%s/validation.pem", p.config.StagingDir)
if err := p.uploadFile(ui, comm, remoteValidationKeyPath, path); err != nil {
return fmt.Errorf("Error copying validation key: %s", err)
}
}
configPath, err := p.createConfig(
ui,
comm,
nodeName,
serverUrl,
p.config.ClientKey,
p.config.ChefLicense,
encryptedDataBagSecretPath,
remoteValidationKeyPath,
p.config.ValidationClientName,
p.config.ChefEnvironment,
p.config.PolicyGroup,
p.config.PolicyName,
p.config.SslVerifyMode,
p.config.TrustedCertsDir)
if err != nil {
return fmt.Errorf("Error creating Chef config file: %s", err)
}
jsonPath, err := p.createJson(ui, comm)
if err != nil {
return fmt.Errorf("Error creating JSON attributes: %s", err)
}
err = p.executeChef(ui, comm, configPath, jsonPath)
if !(p.config.SkipCleanNode && p.config.SkipCleanClient) {
knifeConfigPath, knifeErr := p.createKnifeConfig(
ui, comm, nodeName, serverUrl, p.config.ClientKey, p.config.SslVerifyMode, p.config.TrustedCertsDir)
if knifeErr != nil {
return fmt.Errorf("Error creating knife config on node: %s", knifeErr)
}
if !p.config.SkipCleanNode {
if err := p.cleanNode(ui, comm, nodeName, knifeConfigPath); err != nil {
return fmt.Errorf("Error cleaning up chef node: %s", err)
}
}
if !p.config.SkipCleanClient {
if err := p.cleanClient(ui, comm, nodeName, knifeConfigPath); err != nil {
return fmt.Errorf("Error cleaning up chef client: %s", err)
}
}
}
if err != nil {
return fmt.Errorf("Error executing Chef: %s", err)
}
if !p.config.SkipCleanStagingDirectory {
if err := p.removeDir(ui, comm, p.config.StagingDir); err != nil {
return fmt.Errorf("Error removing %s: %s", p.config.StagingDir, err)
}
}
return nil
}
func (p *Provisioner) uploadFile(ui packersdk.Ui, comm packersdk.Communicator, remotePath string, localPath string) error {
ui.Message(fmt.Sprintf("Uploading %s...", localPath))
f, err := os.Open(localPath)
if err != nil {
return err
}
defer f.Close()
return comm.Upload(remotePath, f, nil)
}
func (p *Provisioner) createConfig(
ui packersdk.Ui,
comm packersdk.Communicator,
nodeName string,
serverUrl string,
clientKey string,
chefLicense string,
encryptedDataBagSecretPath,
remoteKeyPath string,
validationClientName string,
chefEnvironment string,
policyGroup string,
policyName string,
sslVerifyMode string,
trustedCertsDir string) (string, error) {
ui.Message("Creating configuration file 'client.rb'")
// Read the template
tpl := DefaultConfigTemplate
if p.config.ConfigTemplate != "" {
f, err := os.Open(p.config.ConfigTemplate)
if err != nil {
return "", err
}
defer f.Close()
tplBytes, err := ioutil.ReadAll(f)
if err != nil {
return "", err
}
tpl = string(tplBytes)
}
ictx := p.config.ctx
ictx.Data = &ConfigTemplate{
NodeName: nodeName,
ServerUrl: serverUrl,
ClientKey: clientKey,
ChefLicense: chefLicense,
ValidationKeyPath: remoteKeyPath,
ValidationClientName: validationClientName,
ChefEnvironment: chefEnvironment,
PolicyGroup: policyGroup,
PolicyName: policyName,
SslVerifyMode: sslVerifyMode,
TrustedCertsDir: trustedCertsDir,
EncryptedDataBagSecretPath: encryptedDataBagSecretPath,
}
configString, err := interpolate.Render(tpl, &ictx)
if err != nil {
return "", err
}
remotePath := filepath.ToSlash(filepath.Join(p.config.StagingDir, "client.rb"))
if err := comm.Upload(remotePath, bytes.NewReader([]byte(configString)), nil); err != nil {
return "", err
}
return remotePath, nil
}
func (p *Provisioner) createKnifeConfig(ui packersdk.Ui, comm packersdk.Communicator, nodeName string, serverUrl string, clientKey string, sslVerifyMode string, trustedCertsDir string) (string, error) {
ui.Message("Creating configuration file 'knife.rb'")
// Read the template
tpl := DefaultKnifeTemplate
ictx := p.config.ctx
ictx.Data = &ConfigTemplate{
NodeName: nodeName,
ServerUrl: serverUrl,
ClientKey: clientKey,
SslVerifyMode: sslVerifyMode,
TrustedCertsDir: trustedCertsDir,
}
configString, err := interpolate.Render(tpl, &ictx)
if err != nil {
return "", err
}
remotePath := filepath.ToSlash(filepath.Join(p.config.StagingDir, "knife.rb"))
if err := comm.Upload(remotePath, bytes.NewReader([]byte(configString)), nil); err != nil {
return "", err
}
return remotePath, nil
}
func (p *Provisioner) createJson(ui packersdk.Ui, comm packersdk.Communicator) (string, error) {
ui.Message("Creating JSON attribute file")
jsonData := make(map[string]interface{})
// Copy the configured JSON
for k, v := range p.config.Json {
jsonData[k] = v
}
// Set the run list if it was specified
if len(p.config.RunList) > 0 {
jsonData["run_list"] = p.config.RunList
}
jsonBytes, err := json.MarshalIndent(jsonData, "", " ")
if err != nil {
return "", err
}
// Upload the bytes
remotePath := filepath.ToSlash(filepath.Join(p.config.StagingDir, "first-boot.json"))
if err := comm.Upload(remotePath, bytes.NewReader(jsonBytes), nil); err != nil {
return "", err
}
return remotePath, nil
}
func (p *Provisioner) createDir(ui packersdk.Ui, comm packersdk.Communicator, dir string) error {
ctx := context.TODO()
ui.Message(fmt.Sprintf("Creating directory: %s", dir))
cmd := &packersdk.RemoteCmd{Command: p.guestCommands.CreateDir(dir)}
if err := cmd.RunWithUi(ctx, comm, ui); err != nil {
return err
}
if cmd.ExitStatus() != 0 {
return fmt.Errorf("Non-zero exit status. See output above for more info.")
}
// Chmod the directory to 0777 just so that we can access it as our user
cmd = &packersdk.RemoteCmd{Command: p.guestCommands.Chmod(dir, "0777")}
if err := cmd.RunWithUi(ctx, comm, ui); err != nil {
return err
}
if cmd.ExitStatus() != 0 {
return fmt.Errorf("Non-zero exit status. See output above for more info.")
}
return nil
}
func (p *Provisioner) cleanNode(ui packersdk.Ui, comm packersdk.Communicator, node string, knifeConfigPath string) error {
ui.Say("Cleaning up chef node...")
args := []string{"node", "delete", node}
if err := p.knifeExec(ui, comm, node, knifeConfigPath, args); err != nil {
return fmt.Errorf("Failed to cleanup node: %s", err)
}
return nil
}
func (p *Provisioner) cleanClient(ui packersdk.Ui, comm packersdk.Communicator, node string, knifeConfigPath string) error {
ui.Say("Cleaning up chef client...")
args := []string{"client", "delete", node}
if err := p.knifeExec(ui, comm, node, knifeConfigPath, args); err != nil {
return fmt.Errorf("Failed to cleanup client: %s", err)
}
return nil
}
func (p *Provisioner) knifeExec(ui packersdk.Ui, comm packersdk.Communicator, node string, knifeConfigPath string, args []string) error {
flags := []string{
"-y",
"-c", knifeConfigPath,
}
ctx := context.TODO()
p.config.ctx.Data = &KnifeTemplate{
Sudo: !p.config.PreventSudo,
Flags: strings.Join(flags, " "),
Args: strings.Join(args, " "),
}
command, err := interpolate.Render(p.config.KnifeCommand, &p.config.ctx)
if err != nil {
return err
}
cmd := &packersdk.RemoteCmd{Command: command}
if err := cmd.RunWithUi(ctx, comm, ui); err != nil {
return err
}
if cmd.ExitStatus() != 0 {
return fmt.Errorf(
"Non-zero exit status. See output above for more info.\n\n"+
"Command: %s",
command)
}
return nil
}
func (p *Provisioner) removeDir(ui packersdk.Ui, comm packersdk.Communicator, dir string) error {
ui.Message(fmt.Sprintf("Removing directory: %s", dir))
ctx := context.TODO()
cmd := &packersdk.RemoteCmd{Command: p.guestCommands.RemoveDir(dir)}
if err := cmd.RunWithUi(ctx, comm, ui); err != nil {
return err
}
return nil
}
func (p *Provisioner) executeChef(ui packersdk.Ui, comm packersdk.Communicator, config string, json string) error {
p.config.ctx.Data = &ExecuteTemplate{
ConfigPath: config,
JsonPath: json,
Sudo: !p.config.PreventSudo,
}
ctx := context.TODO()
command, err := interpolate.Render(p.config.ExecuteCommand, &p.config.ctx)
if err != nil {
return err
}
if p.config.ElevatedUser != "" {
command, err = guestexec.GenerateElevatedRunner(command, p)
if err != nil {
return err
}
}
ui.Message(fmt.Sprintf("Executing Chef: %s", command))
cmd := &packersdk.RemoteCmd{
Command: command,
}
if err := cmd.RunWithUi(ctx, comm, ui); err != nil {
return err
}
if cmd.ExitStatus() != 0 {
return fmt.Errorf("Non-zero exit status: %d", cmd.ExitStatus())
}
return nil
}
func (p *Provisioner) installChef(ui packersdk.Ui, comm packersdk.Communicator, version string) error {
ui.Message("Installing Chef...")
ctx := context.TODO()
p.config.ctx.Data = &InstallChefTemplate{
Sudo: !p.config.PreventSudo,
Version: version,
}
command, err := interpolate.Render(p.config.InstallCommand, &p.config.ctx)
if err != nil {
return err
}
ui.Message(command)
cmd := &packersdk.RemoteCmd{Command: command}
if err := cmd.RunWithUi(ctx, comm, ui); err != nil {
return err
}
if cmd.ExitStatus() != 0 {
return fmt.Errorf(
"Install script exited with non-zero exit status %d", cmd.ExitStatus())
}
return nil
}
func (p *Provisioner) deepJsonFix(key string, current interface{}) (interface{}, error) {
if current == nil {
return nil, nil
}
switch c := current.(type) {
case []interface{}:
val := make([]interface{}, len(c))
for i, v := range c {
var err error
val[i], err = p.deepJsonFix(fmt.Sprintf("%s[%d]", key, i), v)
if err != nil {
return nil, err
}
}
return val, nil
case []uint8:
return string(c), nil
case map[interface{}]interface{}:
val := make(map[string]interface{})
for k, v := range c {
ks, ok := k.(string)
if !ok {
return nil, fmt.Errorf("%s: key is not string", key)
}
var err error
val[ks], err = p.deepJsonFix(
fmt.Sprintf("%s.%s", key, ks), v)
if err != nil {
return nil, err
}
}
return val, nil
default:
return current, nil
}
}
func (p *Provisioner) processJsonUserVars() (map[string]interface{}, error) {
jsonBytes, err := json.Marshal(p.config.Json)
if err != nil {
// This really shouldn't happen since we literally just unmarshalled
panic(err)
}
// Copy the user variables so that we can restore them later, and
// make sure we make the quotes JSON-friendly in the user variables.
originalUserVars := make(map[string]string)
for k, v := range p.config.ctx.UserVariables {
originalUserVars[k] = v
}
// Make sure we reset them no matter what
defer func() {
p.config.ctx.UserVariables = originalUserVars
}()
// Make the current user variables JSON string safe.
for k, v := range p.config.ctx.UserVariables {
v = strings.Replace(v, `\`, `\\`, -1)
v = strings.Replace(v, `"`, `\"`, -1)
p.config.ctx.UserVariables[k] = v
}
// Process the bytes with the template processor
p.config.ctx.Data = nil
jsonBytesProcessed, err := interpolate.Render(string(jsonBytes), &p.config.ctx)
if err != nil {
return nil, err
}
var result map[string]interface{}
if err := json.Unmarshal([]byte(jsonBytesProcessed), &result); err != nil {
return nil, err
}
return result, nil
}
func (p *Provisioner) Communicator() packersdk.Communicator {
return p.communicator
}
func (p *Provisioner) ElevatedUser() string {
return p.config.ElevatedUser
}
func (p *Provisioner) ElevatedPassword() string {
// Replace ElevatedPassword for winrm users who used this feature
p.config.ctx.Data = p.generatedData
elevatedPassword, _ := interpolate.Render(p.config.ElevatedPassword, &p.config.ctx)
return elevatedPassword
}
var DefaultConfigTemplate = `
log_level :info
log_location STDOUT
chef_server_url "{{.ServerUrl}}"
client_key "{{.ClientKey}}"
chef_license "{{.ChefLicense}}"
{{if ne .EncryptedDataBagSecretPath ""}}
encrypted_data_bag_secret "{{.EncryptedDataBagSecretPath}}"
{{end}}
{{if ne .ValidationClientName ""}}
validation_client_name "{{.ValidationClientName}}"
{{else}}
validation_client_name "chef-validator"
{{end}}
{{if ne .ValidationKeyPath ""}}
validation_key "{{.ValidationKeyPath}}"
{{end}}
node_name "{{.NodeName}}"
{{if ne .ChefEnvironment ""}}
environment "{{.ChefEnvironment}}"
{{end}}
{{if ne .PolicyGroup ""}}
policy_group "{{.PolicyGroup}}"
{{end}}
{{if ne .PolicyName ""}}
policy_name "{{.PolicyName}}"
{{end}}
{{if ne .SslVerifyMode ""}}
ssl_verify_mode :{{.SslVerifyMode}}
{{end}}
{{if ne .TrustedCertsDir ""}}
trusted_certs_dir "{{.TrustedCertsDir}}"
{{end}}
`
var DefaultKnifeTemplate = `
log_level :info
log_location STDOUT
chef_server_url "{{.ServerUrl}}"
client_key "{{.ClientKey}}"
node_name "{{.NodeName}}"
{{if ne .SslVerifyMode ""}}
ssl_verify_mode :{{.SslVerifyMode}}
{{end}}
{{if ne .TrustedCertsDir ""}}
trusted_certs_dir "{{.TrustedCertsDir}}"
{{end}}
`

View File

@ -1,101 +0,0 @@
// Code generated by "packer-sdc mapstructure-to-hcl2"; DO NOT EDIT.
package chefclient
import (
"github.com/hashicorp/hcl/v2/hcldec"
"github.com/zclconf/go-cty/cty"
)
// FlatConfig is an auto-generated flat version of Config.
// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up.
type FlatConfig struct {
PackerBuildName *string `mapstructure:"packer_build_name" cty:"packer_build_name" hcl:"packer_build_name"`
PackerBuilderType *string `mapstructure:"packer_builder_type" cty:"packer_builder_type" hcl:"packer_builder_type"`
PackerCoreVersion *string `mapstructure:"packer_core_version" cty:"packer_core_version" hcl:"packer_core_version"`
PackerDebug *bool `mapstructure:"packer_debug" cty:"packer_debug" hcl:"packer_debug"`
PackerForce *bool `mapstructure:"packer_force" cty:"packer_force" hcl:"packer_force"`
PackerOnError *string `mapstructure:"packer_on_error" cty:"packer_on_error" hcl:"packer_on_error"`
PackerUserVars map[string]string `mapstructure:"packer_user_variables" cty:"packer_user_variables" hcl:"packer_user_variables"`
PackerSensitiveVars []string `mapstructure:"packer_sensitive_variables" cty:"packer_sensitive_variables" hcl:"packer_sensitive_variables"`
Json map[string]interface{} `cty:"json" hcl:"json"`
ChefEnvironment *string `mapstructure:"chef_environment" cty:"chef_environment" hcl:"chef_environment"`
ChefLicense *string `mapstructure:"chef_license" cty:"chef_license" hcl:"chef_license"`
ClientKey *string `mapstructure:"client_key" cty:"client_key" hcl:"client_key"`
ConfigTemplate *string `mapstructure:"config_template" cty:"config_template" hcl:"config_template"`
ElevatedUser *string `mapstructure:"elevated_user" cty:"elevated_user" hcl:"elevated_user"`
ElevatedPassword *string `mapstructure:"elevated_password" cty:"elevated_password" hcl:"elevated_password"`
EncryptedDataBagSecretPath *string `mapstructure:"encrypted_data_bag_secret_path" cty:"encrypted_data_bag_secret_path" hcl:"encrypted_data_bag_secret_path"`
ExecuteCommand *string `mapstructure:"execute_command" cty:"execute_command" hcl:"execute_command"`
GuestOSType *string `mapstructure:"guest_os_type" cty:"guest_os_type" hcl:"guest_os_type"`
InstallCommand *string `mapstructure:"install_command" cty:"install_command" hcl:"install_command"`
KnifeCommand *string `mapstructure:"knife_command" cty:"knife_command" hcl:"knife_command"`
NodeName *string `mapstructure:"node_name" cty:"node_name" hcl:"node_name"`
PolicyGroup *string `mapstructure:"policy_group" cty:"policy_group" hcl:"policy_group"`
PolicyName *string `mapstructure:"policy_name" cty:"policy_name" hcl:"policy_name"`
PreventSudo *bool `mapstructure:"prevent_sudo" cty:"prevent_sudo" hcl:"prevent_sudo"`
RunList []string `mapstructure:"run_list" cty:"run_list" hcl:"run_list"`
ServerUrl *string `mapstructure:"server_url" cty:"server_url" hcl:"server_url"`
SkipCleanClient *bool `mapstructure:"skip_clean_client" cty:"skip_clean_client" hcl:"skip_clean_client"`
SkipCleanNode *bool `mapstructure:"skip_clean_node" cty:"skip_clean_node" hcl:"skip_clean_node"`
SkipCleanStagingDirectory *bool `mapstructure:"skip_clean_staging_directory" cty:"skip_clean_staging_directory" hcl:"skip_clean_staging_directory"`
SkipInstall *bool `mapstructure:"skip_install" cty:"skip_install" hcl:"skip_install"`
SslVerifyMode *string `mapstructure:"ssl_verify_mode" cty:"ssl_verify_mode" hcl:"ssl_verify_mode"`
TrustedCertsDir *string `mapstructure:"trusted_certs_dir" cty:"trusted_certs_dir" hcl:"trusted_certs_dir"`
StagingDir *string `mapstructure:"staging_directory" cty:"staging_directory" hcl:"staging_directory"`
ValidationClientName *string `mapstructure:"validation_client_name" cty:"validation_client_name" hcl:"validation_client_name"`
ValidationKeyPath *string `mapstructure:"validation_key_path" cty:"validation_key_path" hcl:"validation_key_path"`
Version *string `mapstructure:"version" cty:"version" hcl:"version"`
}
// FlatMapstructure returns a new FlatConfig.
// FlatConfig is an auto-generated flat version of Config.
// Where the contents a fields with a `mapstructure:,squash` tag are bubbled up.
func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } {
return new(FlatConfig)
}
// HCL2Spec returns the hcl spec of a Config.
// This spec is used by HCL to read the fields of Config.
// The decoded values from this spec will then be applied to a FlatConfig.
func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
s := map[string]hcldec.Spec{
"packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false},
"packer_builder_type": &hcldec.AttrSpec{Name: "packer_builder_type", Type: cty.String, Required: false},
"packer_core_version": &hcldec.AttrSpec{Name: "packer_core_version", Type: cty.String, Required: false},
"packer_debug": &hcldec.AttrSpec{Name: "packer_debug", Type: cty.Bool, Required: false},
"packer_force": &hcldec.AttrSpec{Name: "packer_force", Type: cty.Bool, Required: false},
"packer_on_error": &hcldec.AttrSpec{Name: "packer_on_error", Type: cty.String, Required: false},
"packer_user_variables": &hcldec.AttrSpec{Name: "packer_user_variables", Type: cty.Map(cty.String), Required: false},
"packer_sensitive_variables": &hcldec.AttrSpec{Name: "packer_sensitive_variables", Type: cty.List(cty.String), Required: false},
"json": &hcldec.AttrSpec{Name: "json", Type: cty.Map(cty.String), Required: false},
"chef_environment": &hcldec.AttrSpec{Name: "chef_environment", Type: cty.String, Required: false},
"chef_license": &hcldec.AttrSpec{Name: "chef_license", Type: cty.String, Required: false},
"client_key": &hcldec.AttrSpec{Name: "client_key", Type: cty.String, Required: false},
"config_template": &hcldec.AttrSpec{Name: "config_template", Type: cty.String, Required: false},
"elevated_user": &hcldec.AttrSpec{Name: "elevated_user", Type: cty.String, Required: false},
"elevated_password": &hcldec.AttrSpec{Name: "elevated_password", Type: cty.String, Required: false},
"encrypted_data_bag_secret_path": &hcldec.AttrSpec{Name: "encrypted_data_bag_secret_path", Type: cty.String, Required: false},
"execute_command": &hcldec.AttrSpec{Name: "execute_command", Type: cty.String, Required: false},
"guest_os_type": &hcldec.AttrSpec{Name: "guest_os_type", Type: cty.String, Required: false},
"install_command": &hcldec.AttrSpec{Name: "install_command", Type: cty.String, Required: false},
"knife_command": &hcldec.AttrSpec{Name: "knife_command", Type: cty.String, Required: false},
"node_name": &hcldec.AttrSpec{Name: "node_name", Type: cty.String, Required: false},
"policy_group": &hcldec.AttrSpec{Name: "policy_group", Type: cty.String, Required: false},
"policy_name": &hcldec.AttrSpec{Name: "policy_name", Type: cty.String, Required: false},
"prevent_sudo": &hcldec.AttrSpec{Name: "prevent_sudo", Type: cty.Bool, Required: false},
"run_list": &hcldec.AttrSpec{Name: "run_list", Type: cty.List(cty.String), Required: false},
"server_url": &hcldec.AttrSpec{Name: "server_url", Type: cty.String, Required: false},
"skip_clean_client": &hcldec.AttrSpec{Name: "skip_clean_client", Type: cty.Bool, Required: false},
"skip_clean_node": &hcldec.AttrSpec{Name: "skip_clean_node", Type: cty.Bool, Required: false},
"skip_clean_staging_directory": &hcldec.AttrSpec{Name: "skip_clean_staging_directory", Type: cty.Bool, Required: false},
"skip_install": &hcldec.AttrSpec{Name: "skip_install", Type: cty.Bool, Required: false},
"ssl_verify_mode": &hcldec.AttrSpec{Name: "ssl_verify_mode", Type: cty.String, Required: false},
"trusted_certs_dir": &hcldec.AttrSpec{Name: "trusted_certs_dir", Type: cty.String, Required: false},
"staging_directory": &hcldec.AttrSpec{Name: "staging_directory", Type: cty.String, Required: false},
"validation_client_name": &hcldec.AttrSpec{Name: "validation_client_name", Type: cty.String, Required: false},
"validation_key_path": &hcldec.AttrSpec{Name: "validation_key_path", Type: cty.String, Required: false},
"version": &hcldec.AttrSpec{Name: "version", Type: cty.String, Required: false},
}
return s
}

View File

@ -1,322 +0,0 @@
package chefclient
import (
"bytes"
"io/ioutil"
"os"
"strings"
"testing"
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
)
func testConfig() map[string]interface{} {
return map[string]interface{}{
"server_url": "foo",
}
}
func TestProvisioner_Impl(t *testing.T) {
var raw interface{}
raw = &Provisioner{}
if _, ok := raw.(packersdk.Provisioner); !ok {
t.Fatalf("must be a Provisioner")
}
}
func TestProvisionerPrepare_chefEnvironment(t *testing.T) {
var p Provisioner
config := testConfig()
config["chef_environment"] = "some-env"
err := p.Prepare(config)
if err != nil {
t.Fatalf("err: %s", err)
}
if p.config.ChefEnvironment != "some-env" {
t.Fatalf("unexpected: %#v", p.config.ChefEnvironment)
}
}
func TestProvisionerPrepare_configTemplate(t *testing.T) {
var err error
var p Provisioner
// Test no config template
config := testConfig()
delete(config, "config_template")
err = p.Prepare(config)
if err != nil {
t.Fatalf("err: %s", err)
}
// Test with a file
tf, err := ioutil.TempFile("", "packer")
if err != nil {
t.Fatalf("err: %s", err)
}
defer os.Remove(tf.Name())
config = testConfig()
config["config_template"] = tf.Name()
p = Provisioner{}
err = p.Prepare(config)
if err != nil {
t.Fatalf("err: %s", err)
}
// Test with a directory
td, err := ioutil.TempDir("", "packer")
if err != nil {
t.Fatalf("err: %s", err)
}
defer os.RemoveAll(td)
config = testConfig()
config["config_template"] = td
p = Provisioner{}
err = p.Prepare(config)
if err == nil {
t.Fatal("should have err")
}
}
func TestProvisionerPrepare_commands(t *testing.T) {
commands := []string{
"execute_command",
"install_command",
"knife_command",
}
for _, command := range commands {
var p Provisioner
// Test not set
config := testConfig()
delete(config, command)
err := p.Prepare(config)
if err != nil {
t.Fatalf("err: %s", err)
}
// Test invalid template
config = testConfig()
config[command] = "{{if NOPE}}"
err = p.Prepare(config)
if err == nil {
t.Fatal("should error")
}
// Test good template
config = testConfig()
config[command] = "{{.Foo}}"
err = p.Prepare(config)
if err != nil {
t.Fatalf("err: %s", err)
}
}
}
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)
}
}
func TestProvisionerPrepare_chefLicense(t *testing.T) {
var p Provisioner
// Test not set
config := testConfig()
err := p.Prepare(config)
if err != nil {
t.Fatal("should error")
}
if p.config.ChefLicense != "accept-silent" {
t.Fatalf("unexpected: %#v", p.config.ChefLicense)
}
// Test set
config = testConfig()
config["chef_license"] = "accept"
p = Provisioner{}
err = p.Prepare(config)
if err != nil {
t.Fatalf("err: %s", err)
}
if p.config.ChefLicense != "accept" {
t.Fatalf("unexpected: %#v", p.config.ChefLicense)
}
// Test set skipInstall true
config = testConfig()
config["skip_install"] = true
p = Provisioner{}
err = p.Prepare(config)
if err != nil {
t.Fatalf("err: %s", err)
}
if p.config.ChefLicense != "" {
t.Fatalf("unexpected: %#v", "empty string")
}
// Test set installCommand true
config = testConfig()
config["install_command"] = "install chef"
p = Provisioner{}
err = p.Prepare(config)
if err != nil {
t.Fatalf("err: %s", err)
}
if p.config.ChefLicense != "" {
t.Fatalf("unexpected: %#v", "empty string")
}
}
func TestProvisionerPrepare_encryptedDataBagSecretPath(t *testing.T) {
var err error
var p Provisioner
// Test no config template
config := testConfig()
delete(config, "encrypted_data_bag_secret_path")
err = p.Prepare(config)
if err != nil {
t.Fatalf("err: %s", err)
}
// Test with a file
tf, err := ioutil.TempFile("", "packer")
if err != nil {
t.Fatalf("err: %s", err)
}
defer os.Remove(tf.Name())
config = testConfig()
config["encrypted_data_bag_secret_path"] = tf.Name()
p = Provisioner{}
err = p.Prepare(config)
if err != nil {
t.Fatalf("err: %s", err)
}
// Test with a directory
td, err := ioutil.TempDir("", "packer")
if err != nil {
t.Fatalf("err: %s", err)
}
defer os.RemoveAll(td)
config = testConfig()
config["encrypted_data_bag_secret_path"] = td
p = Provisioner{}
err = p.Prepare(config)
if err == nil {
t.Fatal("should have err")
}
}
func TestProvisioner_createDir(t *testing.T) {
for _, sudo := range []bool{true, false} {
config := testConfig()
config["prevent_sudo"] = !sudo
p := &Provisioner{}
comm := &packersdk.MockCommunicator{}
ui := &packersdk.BasicUi{
Reader: new(bytes.Buffer),
Writer: new(bytes.Buffer),
}
err := p.Prepare(config)
if err != nil {
t.Fatalf("err: %s", err)
}
if err := p.createDir(ui, comm, "/tmp/foo"); err != nil {
t.Fatalf("err: %s", err)
}
if !sudo && strings.HasPrefix(comm.StartCmd.Command, "sudo") {
t.Fatalf("createDir should not use sudo, got: \"%s\"", comm.StartCmd.Command)
}
if sudo && !strings.HasPrefix(comm.StartCmd.Command, "sudo") {
t.Fatalf("createDir should use sudo, got: \"%s\"", comm.StartCmd.Command)
}
}
}
func TestProvisioner_removeDir(t *testing.T) {
for _, sudo := range []bool{true, false} {
config := testConfig()
config["prevent_sudo"] = !sudo
p := &Provisioner{}
comm := &packersdk.MockCommunicator{}
ui := &packersdk.BasicUi{
Reader: new(bytes.Buffer),
Writer: new(bytes.Buffer),
}
err := p.Prepare(config)
if err != nil {
t.Fatalf("err: %s", err)
}
if err := p.removeDir(ui, comm, "/tmp/foo"); err != nil {
t.Fatalf("err: %s", err)
}
if !sudo && strings.HasPrefix(comm.StartCmd.Command, "sudo") {
t.Fatalf("removeDir should not use sudo, got: \"%s\"", comm.StartCmd.Command)
}
if sudo && !strings.HasPrefix(comm.StartCmd.Command, "sudo") {
t.Fatalf("removeDir should use sudo, got: \"%s\"", comm.StartCmd.Command)
}
}
}
func TestProvisionerPrepare_policy(t *testing.T) {
var p Provisioner
var policyTests = []struct {
name string
group string
success bool
}{
{"", "", true},
{"a", "b", true},
{"a", "", false},
{"", "a", false},
}
for _, tt := range policyTests {
config := testConfig()
config["policy_name"] = tt.name
config["policy_group"] = tt.group
err := p.Prepare(config)
if (err == nil) != tt.success {
t.Fatalf("wasn't expecting %+v to fail: %s", tt, err.Error())
}
}
}

View File

@ -1,13 +0,0 @@
package version
import (
"github.com/hashicorp/packer-plugin-sdk/version"
packerVersion "github.com/hashicorp/packer/version"
)
var ChefClientPluginVersion *version.PluginVersion
func init() {
ChefClientPluginVersion = version.InitializePluginVersion(
packerVersion.Version, packerVersion.VersionPrerelease)
}

View File

@ -1,611 +0,0 @@
//go:generate packer-sdc mapstructure-to-hcl2 -type Config
// This package implements a provisioner for Packer that uses
// Chef to provision the remote machine, specifically with chef-solo (that is,
// without a Chef server).
package chefsolo
import (
"bytes"
"context"
"encoding/json"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"strings"
"github.com/hashicorp/hcl/v2/hcldec"
"github.com/hashicorp/packer-plugin-sdk/common"
"github.com/hashicorp/packer-plugin-sdk/guestexec"
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
"github.com/hashicorp/packer-plugin-sdk/template/config"
"github.com/hashicorp/packer-plugin-sdk/template/interpolate"
)
type guestOSTypeConfig struct {
executeCommand string
installCommand string
stagingDir string
}
var guestOSTypeConfigs = map[string]guestOSTypeConfig{
guestexec.UnixOSType: {
executeCommand: "{{if .Sudo}}sudo {{end}}chef-solo --no-color -c {{.ConfigPath}} -j {{.JsonPath}}",
installCommand: "curl -L https://omnitruck.chef.io/install.sh | {{if .Sudo}}sudo {{end}}bash -s --{{if .Version}} -v {{.Version}}{{end}}",
stagingDir: "/tmp/packer-chef-solo",
},
guestexec.WindowsOSType: {
executeCommand: "c:/opscode/chef/bin/chef-solo.bat --no-color -c {{.ConfigPath}} -j {{.JsonPath}}",
installCommand: "powershell.exe -Command \". { iwr -useb https://omnitruck.chef.io/install.ps1 } | iex; Install-Project{{if .Version}} -version {{.Version}}{{end}}\"",
stagingDir: "C:/Windows/Temp/packer-chef-solo",
},
}
type Config struct {
common.PackerConfig `mapstructure:",squash"`
ChefEnvironment string `mapstructure:"chef_environment"`
ChefLicense string `mapstructure:"chef_license"`
ConfigTemplate string `mapstructure:"config_template"`
CookbookPaths []string `mapstructure:"cookbook_paths"`
RolesPath string `mapstructure:"roles_path"`
DataBagsPath string `mapstructure:"data_bags_path"`
EncryptedDataBagSecretPath string `mapstructure:"encrypted_data_bag_secret_path"`
EnvironmentsPath string `mapstructure:"environments_path"`
ExecuteCommand string `mapstructure:"execute_command"`
InstallCommand string `mapstructure:"install_command"`
RemoteCookbookPaths []string `mapstructure:"remote_cookbook_paths"`
// HCL cannot be decoded into an interface so for HCL templates you must use the JsonString option,
// To be used with https://www.packer.io/docs/templates/hcl_templates/functions/encoding/jsonencode
// ref: https://github.com/hashicorp/hcl/issues/291#issuecomment-496347585
JsonString string `mapstructure:"json_string"`
// For JSON templates we keep the map[string]interface{}
Json map[string]interface{} `mapstructure:"json" mapstructure-to-hcl2:",skip"`
PreventSudo bool `mapstructure:"prevent_sudo"`
RunList []string `mapstructure:"run_list"`
SkipInstall bool `mapstructure:"skip_install"`
StagingDir string `mapstructure:"staging_directory"`
GuestOSType string `mapstructure:"guest_os_type"`
Version string `mapstructure:"version"`
ctx interpolate.Context
}
type Provisioner struct {
config Config
guestOSTypeConfig guestOSTypeConfig
guestCommands *guestexec.GuestCommands
}
type ConfigTemplate struct {
CookbookPaths string
DataBagsPath string
EncryptedDataBagSecretPath string
RolesPath string
EnvironmentsPath string
ChefEnvironment string
ChefLicense string
// Templates don't support boolean statements until Go 1.2. In the
// mean time, we do this.
// TODO(mitchellh): Remove when Go 1.2 is released
HasDataBagsPath bool
HasEncryptedDataBagSecretPath bool
HasRolesPath bool
HasEnvironmentsPath bool
}
type ExecuteTemplate struct {
ConfigPath string
JsonPath string
Sudo bool
}
type InstallChefTemplate struct {
Sudo bool
Version string
}
func (p *Provisioner) ConfigSpec() hcldec.ObjectSpec { return p.config.FlatMapstructure().HCL2Spec() }
func (p *Provisioner) Prepare(raws ...interface{}) error {
err := config.Decode(&p.config, &config.DecodeOpts{
PluginType: "chef-solo",
Interpolate: true,
InterpolateContext: &p.config.ctx,
InterpolateFilter: &interpolate.RenderFilter{
Exclude: []string{
"execute_command",
"install_command",
},
},
}, raws...)
if err != nil {
return err
}
if p.config.JsonString != "" {
if err := json.Unmarshal([]byte(p.config.JsonString), &p.config.Json); err != nil {
return fmt.Errorf("Failed to unmarshal 'json_string': %s", err.Error())
}
}
if p.config.GuestOSType == "" {
p.config.GuestOSType = guestexec.DefaultOSType
}
p.config.GuestOSType = strings.ToLower(p.config.GuestOSType)
var ok bool
p.guestOSTypeConfig, ok = guestOSTypeConfigs[p.config.GuestOSType]
if !ok {
return fmt.Errorf("Invalid guest_os_type: \"%s\"", p.config.GuestOSType)
}
p.guestCommands, err = guestexec.NewGuestCommands(p.config.GuestOSType, !p.config.PreventSudo)
if err != nil {
return fmt.Errorf("Invalid guest_os_type: \"%s\"", p.config.GuestOSType)
}
if p.config.ExecuteCommand == "" {
p.config.ExecuteCommand = p.guestOSTypeConfig.executeCommand
}
if p.config.InstallCommand == "" {
p.config.InstallCommand = p.guestOSTypeConfig.installCommand
}
if p.config.RunList == nil {
p.config.RunList = make([]string, 0)
}
if p.config.StagingDir == "" {
p.config.StagingDir = p.guestOSTypeConfig.stagingDir
}
if p.config.SkipInstall == false && p.config.InstallCommand == p.guestOSTypeConfig.installCommand {
if p.config.ChefLicense == "" {
p.config.ChefLicense = "accept-silent"
}
}
var errs *packersdk.MultiError
if p.config.ConfigTemplate != "" {
fi, err := os.Stat(p.config.ConfigTemplate)
if err != nil {
errs = packersdk.MultiErrorAppend(
errs, fmt.Errorf("Bad config template path: %s", err))
} else if fi.IsDir() {
errs = packersdk.MultiErrorAppend(
errs, fmt.Errorf("Config template path must be a file: %s", err))
}
}
for _, path := range p.config.CookbookPaths {
pFileInfo, err := os.Stat(path)
if err != nil || !pFileInfo.IsDir() {
errs = packersdk.MultiErrorAppend(
errs, fmt.Errorf("Bad cookbook path '%s': %s", path, err))
}
}
if p.config.RolesPath != "" {
pFileInfo, err := os.Stat(p.config.RolesPath)
if err != nil || !pFileInfo.IsDir() {
errs = packersdk.MultiErrorAppend(
errs, fmt.Errorf("Bad roles path '%s': %s", p.config.RolesPath, err))
}
}
if p.config.DataBagsPath != "" {
pFileInfo, err := os.Stat(p.config.DataBagsPath)
if err != nil || !pFileInfo.IsDir() {
errs = packersdk.MultiErrorAppend(
errs, fmt.Errorf("Bad data bags path '%s': %s", p.config.DataBagsPath, err))
}
}
if p.config.EncryptedDataBagSecretPath != "" {
pFileInfo, err := os.Stat(p.config.EncryptedDataBagSecretPath)
if err != nil || pFileInfo.IsDir() {
errs = packersdk.MultiErrorAppend(
errs, fmt.Errorf("Bad encrypted data bag secret '%s': %s", p.config.EncryptedDataBagSecretPath, err))
}
}
if p.config.EnvironmentsPath != "" {
pFileInfo, err := os.Stat(p.config.EnvironmentsPath)
if err != nil || !pFileInfo.IsDir() {
errs = packersdk.MultiErrorAppend(
errs, fmt.Errorf("Bad environments path '%s': %s", p.config.EnvironmentsPath, err))
}
}
jsonValid := true
for k, v := range p.config.Json {
p.config.Json[k], err = p.deepJsonFix(k, v)
if err != nil {
errs = packersdk.MultiErrorAppend(
errs, fmt.Errorf("Error processing JSON: %s", err))
jsonValid = false
}
}
if jsonValid {
// Process the user variables within the JSON and set the JSON.
// Do this early so that we can validate and show errors.
p.config.Json, err = p.processJsonUserVars()
if err != nil {
errs = packersdk.MultiErrorAppend(
errs, fmt.Errorf("Error processing user variables in JSON: %s", err))
}
}
if errs != nil && len(errs.Errors) > 0 {
return errs
}
return nil
}
func (p *Provisioner) Provision(ctx context.Context, ui packersdk.Ui, comm packersdk.Communicator, _ map[string]interface{}) error {
ui.Say("Provisioning with chef-solo")
if !p.config.SkipInstall {
if err := p.installChef(ui, comm, p.config.Version); err != nil {
return fmt.Errorf("Error installing Chef: %s", err)
}
}
if err := p.createDir(ui, comm, p.config.StagingDir); err != nil {
return fmt.Errorf("Error creating staging directory: %s", err)
}
cookbookPaths := make([]string, 0, len(p.config.CookbookPaths))
for i, path := range p.config.CookbookPaths {
targetPath := fmt.Sprintf("%s/cookbooks-%d", p.config.StagingDir, i)
if err := p.uploadDirectory(ui, comm, targetPath, path); err != nil {
return fmt.Errorf("Error uploading cookbooks: %s", err)
}
cookbookPaths = append(cookbookPaths, targetPath)
}
rolesPath := ""
if p.config.RolesPath != "" {
rolesPath = fmt.Sprintf("%s/roles", p.config.StagingDir)
if err := p.uploadDirectory(ui, comm, rolesPath, p.config.RolesPath); err != nil {
return fmt.Errorf("Error uploading roles: %s", err)
}
}
dataBagsPath := ""
if p.config.DataBagsPath != "" {
dataBagsPath = fmt.Sprintf("%s/data_bags", p.config.StagingDir)
if err := p.uploadDirectory(ui, comm, dataBagsPath, p.config.DataBagsPath); err != nil {
return fmt.Errorf("Error uploading data bags: %s", err)
}
}
encryptedDataBagSecretPath := ""
if p.config.EncryptedDataBagSecretPath != "" {
encryptedDataBagSecretPath = fmt.Sprintf("%s/encrypted_data_bag_secret", p.config.StagingDir)
if err := p.uploadFile(ui, comm, encryptedDataBagSecretPath, p.config.EncryptedDataBagSecretPath); err != nil {
return fmt.Errorf("Error uploading encrypted data bag secret: %s", err)
}
}
environmentsPath := ""
if p.config.EnvironmentsPath != "" {
environmentsPath = fmt.Sprintf("%s/environments", p.config.StagingDir)
if err := p.uploadDirectory(ui, comm, environmentsPath, p.config.EnvironmentsPath); err != nil {
return fmt.Errorf("Error uploading environments: %s", err)
}
}
configPath, err := p.createConfig(ui, comm, cookbookPaths, rolesPath, dataBagsPath, encryptedDataBagSecretPath, environmentsPath, p.config.ChefEnvironment, p.config.ChefLicense)
if err != nil {
return fmt.Errorf("Error creating Chef config file: %s", err)
}
jsonPath, err := p.createJson(ui, comm)
if err != nil {
return fmt.Errorf("Error creating JSON attributes: %s", err)
}
if err := p.executeChef(ui, comm, configPath, jsonPath); err != nil {
return fmt.Errorf("Error executing Chef: %s", err)
}
return nil
}
func (p *Provisioner) uploadDirectory(ui packersdk.Ui, comm packersdk.Communicator, dst string, src string) error {
if err := p.createDir(ui, comm, dst); err != nil {
return err
}
// Make sure there is a trailing "/" so that the directory isn't
// created on the other side.
if src[len(src)-1] != '/' {
src = src + "/"
}
return comm.UploadDir(dst, src, nil)
}
func (p *Provisioner) uploadFile(ui packersdk.Ui, comm packersdk.Communicator, dst string, src string) error {
f, err := os.Open(src)
if err != nil {
return err
}
defer f.Close()
return comm.Upload(dst, f, nil)
}
func (p *Provisioner) createConfig(ui packersdk.Ui, comm packersdk.Communicator, localCookbooks []string, rolesPath string, dataBagsPath string, encryptedDataBagSecretPath string, environmentsPath string, chefEnvironment string, chefLicense string) (string, error) {
ui.Message("Creating configuration file 'solo.rb'")
cookbook_paths := make([]string, len(p.config.RemoteCookbookPaths)+len(localCookbooks))
for i, path := range p.config.RemoteCookbookPaths {
cookbook_paths[i] = fmt.Sprintf(`"%s"`, path)
}
for i, path := range localCookbooks {
i = len(p.config.RemoteCookbookPaths) + i
cookbook_paths[i] = fmt.Sprintf(`"%s"`, path)
}
// Read the template
tpl := DefaultConfigTemplate
if p.config.ConfigTemplate != "" {
f, err := os.Open(p.config.ConfigTemplate)
if err != nil {
return "", err
}
defer f.Close()
tplBytes, err := ioutil.ReadAll(f)
if err != nil {
return "", err
}
tpl = string(tplBytes)
}
p.config.ctx.Data = &ConfigTemplate{
CookbookPaths: strings.Join(cookbook_paths, ","),
RolesPath: rolesPath,
DataBagsPath: dataBagsPath,
EncryptedDataBagSecretPath: encryptedDataBagSecretPath,
EnvironmentsPath: environmentsPath,
HasRolesPath: rolesPath != "",
HasDataBagsPath: dataBagsPath != "",
HasEncryptedDataBagSecretPath: encryptedDataBagSecretPath != "",
HasEnvironmentsPath: environmentsPath != "",
ChefEnvironment: chefEnvironment,
ChefLicense: chefLicense,
}
configString, err := interpolate.Render(tpl, &p.config.ctx)
if err != nil {
return "", err
}
remotePath := filepath.ToSlash(filepath.Join(p.config.StagingDir, "solo.rb"))
if err := comm.Upload(remotePath, bytes.NewReader([]byte(configString)), nil); err != nil {
return "", err
}
return remotePath, nil
}
func (p *Provisioner) createJson(ui packersdk.Ui, comm packersdk.Communicator) (string, error) {
ui.Message("Creating JSON attribute file")
jsonData := make(map[string]interface{})
// Copy the configured JSON
for k, v := range p.config.Json {
jsonData[k] = v
}
// Set the run list if it was specified
if len(p.config.RunList) > 0 {
jsonData["run_list"] = p.config.RunList
}
jsonBytes, err := json.MarshalIndent(jsonData, "", " ")
if err != nil {
return "", err
}
// Upload the bytes
remotePath := filepath.ToSlash(filepath.Join(p.config.StagingDir, "node.json"))
if err := comm.Upload(remotePath, bytes.NewReader(jsonBytes), nil); err != nil {
return "", err
}
return remotePath, nil
}
func (p *Provisioner) createDir(ui packersdk.Ui, comm packersdk.Communicator, dir string) error {
ui.Message(fmt.Sprintf("Creating directory: %s", dir))
ctx := context.TODO()
cmd := &packersdk.RemoteCmd{Command: p.guestCommands.CreateDir(dir)}
if err := cmd.RunWithUi(ctx, comm, ui); err != nil {
return err
}
if cmd.ExitStatus() != 0 {
return fmt.Errorf("Non-zero exit status. See output above for more info.")
}
// Chmod the directory to 0777 just so that we can access it as our user
cmd = &packersdk.RemoteCmd{Command: p.guestCommands.Chmod(dir, "0777")}
if err := cmd.RunWithUi(ctx, comm, ui); err != nil {
return err
}
if cmd.ExitStatus() != 0 {
return fmt.Errorf("Non-zero exit status. See output above for more info.")
}
return nil
}
func (p *Provisioner) executeChef(ui packersdk.Ui, comm packersdk.Communicator, config string, json string) error {
p.config.ctx.Data = &ExecuteTemplate{
ConfigPath: config,
JsonPath: json,
Sudo: !p.config.PreventSudo,
}
command, err := interpolate.Render(p.config.ExecuteCommand, &p.config.ctx)
if err != nil {
return err
}
ui.Message(fmt.Sprintf("Executing Chef: %s", command))
cmd := &packersdk.RemoteCmd{
Command: command,
}
ctx := context.TODO()
if err := cmd.RunWithUi(ctx, comm, ui); err != nil {
return err
}
if cmd.ExitStatus() != 0 {
return fmt.Errorf("Non-zero exit status: %d", cmd.ExitStatus())
}
return nil
}
func (p *Provisioner) installChef(ui packersdk.Ui, comm packersdk.Communicator, version string) error {
ui.Message("Installing Chef...")
ctx := context.TODO()
p.config.ctx.Data = &InstallChefTemplate{
Sudo: !p.config.PreventSudo,
Version: version,
}
command, err := interpolate.Render(p.config.InstallCommand, &p.config.ctx)
if err != nil {
return err
}
cmd := &packersdk.RemoteCmd{Command: command}
if err := cmd.RunWithUi(ctx, comm, ui); err != nil {
return err
}
if cmd.ExitStatus() != 0 {
return fmt.Errorf(
"Install script exited with non-zero exit status %d", cmd.ExitStatus())
}
return nil
}
func (p *Provisioner) deepJsonFix(key string, current interface{}) (interface{}, error) {
if current == nil {
return nil, nil
}
switch c := current.(type) {
case []interface{}:
val := make([]interface{}, len(c))
for i, v := range c {
var err error
val[i], err = p.deepJsonFix(fmt.Sprintf("%s[%d]", key, i), v)
if err != nil {
return nil, err
}
}
return val, nil
case []uint8:
return string(c), nil
case map[interface{}]interface{}:
val := make(map[string]interface{})
for k, v := range c {
ks, ok := k.(string)
if !ok {
return nil, fmt.Errorf("%s: key is not string", key)
}
var err error
val[ks], err = p.deepJsonFix(
fmt.Sprintf("%s.%s", key, ks), v)
if err != nil {
return nil, err
}
}
return val, nil
default:
return current, nil
}
}
func (p *Provisioner) processJsonUserVars() (map[string]interface{}, error) {
jsonBytes, err := json.Marshal(p.config.Json)
if err != nil {
// This really shouldn't happen since we literally just unmarshalled
panic(err)
}
// Copy the user variables so that we can restore them later, and
// make sure we make the quotes JSON-friendly in the user variables.
originalUserVars := make(map[string]string)
for k, v := range p.config.ctx.UserVariables {
originalUserVars[k] = v
}
// Make sure we reset them no matter what
defer func() {
p.config.ctx.UserVariables = originalUserVars
}()
// Make the current user variables JSON string safe.
for k, v := range p.config.ctx.UserVariables {
v = strings.Replace(v, `\`, `\\`, -1)
v = strings.Replace(v, `"`, `\"`, -1)
p.config.ctx.UserVariables[k] = v
}
// Process the bytes with the template processor
p.config.ctx.Data = nil
jsonBytesProcessed, err := interpolate.Render(string(jsonBytes), &p.config.ctx)
if err != nil {
return nil, err
}
var result map[string]interface{}
if err := json.Unmarshal([]byte(jsonBytesProcessed), &result); err != nil {
return nil, err
}
return result, nil
}
var DefaultConfigTemplate = `
chef_license "{{.ChefLicense}}"
cookbook_path [{{.CookbookPaths}}]
{{if .HasRolesPath}}
role_path "{{.RolesPath}}"
{{end}}
{{if .HasDataBagsPath}}
data_bag_path "{{.DataBagsPath}}"
{{end}}
{{if .HasEncryptedDataBagSecretPath}}
encrypted_data_bag_secret "{{.EncryptedDataBagSecretPath}}"
{{end}}
{{if .HasEnvironmentsPath}}
environment_path "{{.EnvironmentsPath}}"
environment "{{.ChefEnvironment}}"
{{end}}
`

View File

@ -1,81 +0,0 @@
// Code generated by "packer-sdc mapstructure-to-hcl2"; DO NOT EDIT.
package chefsolo
import (
"github.com/hashicorp/hcl/v2/hcldec"
"github.com/zclconf/go-cty/cty"
)
// FlatConfig is an auto-generated flat version of Config.
// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up.
type FlatConfig struct {
PackerBuildName *string `mapstructure:"packer_build_name" cty:"packer_build_name" hcl:"packer_build_name"`
PackerBuilderType *string `mapstructure:"packer_builder_type" cty:"packer_builder_type" hcl:"packer_builder_type"`
PackerCoreVersion *string `mapstructure:"packer_core_version" cty:"packer_core_version" hcl:"packer_core_version"`
PackerDebug *bool `mapstructure:"packer_debug" cty:"packer_debug" hcl:"packer_debug"`
PackerForce *bool `mapstructure:"packer_force" cty:"packer_force" hcl:"packer_force"`
PackerOnError *string `mapstructure:"packer_on_error" cty:"packer_on_error" hcl:"packer_on_error"`
PackerUserVars map[string]string `mapstructure:"packer_user_variables" cty:"packer_user_variables" hcl:"packer_user_variables"`
PackerSensitiveVars []string `mapstructure:"packer_sensitive_variables" cty:"packer_sensitive_variables" hcl:"packer_sensitive_variables"`
ChefEnvironment *string `mapstructure:"chef_environment" cty:"chef_environment" hcl:"chef_environment"`
ChefLicense *string `mapstructure:"chef_license" cty:"chef_license" hcl:"chef_license"`
ConfigTemplate *string `mapstructure:"config_template" cty:"config_template" hcl:"config_template"`
CookbookPaths []string `mapstructure:"cookbook_paths" cty:"cookbook_paths" hcl:"cookbook_paths"`
RolesPath *string `mapstructure:"roles_path" cty:"roles_path" hcl:"roles_path"`
DataBagsPath *string `mapstructure:"data_bags_path" cty:"data_bags_path" hcl:"data_bags_path"`
EncryptedDataBagSecretPath *string `mapstructure:"encrypted_data_bag_secret_path" cty:"encrypted_data_bag_secret_path" hcl:"encrypted_data_bag_secret_path"`
EnvironmentsPath *string `mapstructure:"environments_path" cty:"environments_path" hcl:"environments_path"`
ExecuteCommand *string `mapstructure:"execute_command" cty:"execute_command" hcl:"execute_command"`
InstallCommand *string `mapstructure:"install_command" cty:"install_command" hcl:"install_command"`
RemoteCookbookPaths []string `mapstructure:"remote_cookbook_paths" cty:"remote_cookbook_paths" hcl:"remote_cookbook_paths"`
JsonString *string `mapstructure:"json_string" cty:"json_string" hcl:"json_string"`
PreventSudo *bool `mapstructure:"prevent_sudo" cty:"prevent_sudo" hcl:"prevent_sudo"`
RunList []string `mapstructure:"run_list" cty:"run_list" hcl:"run_list"`
SkipInstall *bool `mapstructure:"skip_install" cty:"skip_install" hcl:"skip_install"`
StagingDir *string `mapstructure:"staging_directory" cty:"staging_directory" hcl:"staging_directory"`
GuestOSType *string `mapstructure:"guest_os_type" cty:"guest_os_type" hcl:"guest_os_type"`
Version *string `mapstructure:"version" cty:"version" hcl:"version"`
}
// FlatMapstructure returns a new FlatConfig.
// FlatConfig is an auto-generated flat version of Config.
// Where the contents a fields with a `mapstructure:,squash` tag are bubbled up.
func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } {
return new(FlatConfig)
}
// HCL2Spec returns the hcl spec of a Config.
// This spec is used by HCL to read the fields of Config.
// The decoded values from this spec will then be applied to a FlatConfig.
func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
s := map[string]hcldec.Spec{
"packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false},
"packer_builder_type": &hcldec.AttrSpec{Name: "packer_builder_type", Type: cty.String, Required: false},
"packer_core_version": &hcldec.AttrSpec{Name: "packer_core_version", Type: cty.String, Required: false},
"packer_debug": &hcldec.AttrSpec{Name: "packer_debug", Type: cty.Bool, Required: false},
"packer_force": &hcldec.AttrSpec{Name: "packer_force", Type: cty.Bool, Required: false},
"packer_on_error": &hcldec.AttrSpec{Name: "packer_on_error", Type: cty.String, Required: false},
"packer_user_variables": &hcldec.AttrSpec{Name: "packer_user_variables", Type: cty.Map(cty.String), Required: false},
"packer_sensitive_variables": &hcldec.AttrSpec{Name: "packer_sensitive_variables", Type: cty.List(cty.String), Required: false},
"chef_environment": &hcldec.AttrSpec{Name: "chef_environment", Type: cty.String, Required: false},
"chef_license": &hcldec.AttrSpec{Name: "chef_license", Type: cty.String, Required: false},
"config_template": &hcldec.AttrSpec{Name: "config_template", Type: cty.String, Required: false},
"cookbook_paths": &hcldec.AttrSpec{Name: "cookbook_paths", Type: cty.List(cty.String), Required: false},
"roles_path": &hcldec.AttrSpec{Name: "roles_path", Type: cty.String, Required: false},
"data_bags_path": &hcldec.AttrSpec{Name: "data_bags_path", Type: cty.String, Required: false},
"encrypted_data_bag_secret_path": &hcldec.AttrSpec{Name: "encrypted_data_bag_secret_path", Type: cty.String, Required: false},
"environments_path": &hcldec.AttrSpec{Name: "environments_path", Type: cty.String, Required: false},
"execute_command": &hcldec.AttrSpec{Name: "execute_command", Type: cty.String, Required: false},
"install_command": &hcldec.AttrSpec{Name: "install_command", Type: cty.String, Required: false},
"remote_cookbook_paths": &hcldec.AttrSpec{Name: "remote_cookbook_paths", Type: cty.List(cty.String), Required: false},
"json_string": &hcldec.AttrSpec{Name: "json_string", Type: cty.String, Required: false},
"prevent_sudo": &hcldec.AttrSpec{Name: "prevent_sudo", Type: cty.Bool, Required: false},
"run_list": &hcldec.AttrSpec{Name: "run_list", Type: cty.List(cty.String), Required: false},
"skip_install": &hcldec.AttrSpec{Name: "skip_install", Type: cty.Bool, Required: false},
"staging_directory": &hcldec.AttrSpec{Name: "staging_directory", Type: cty.String, Required: false},
"guest_os_type": &hcldec.AttrSpec{Name: "guest_os_type", Type: cty.String, Required: false},
"version": &hcldec.AttrSpec{Name: "version", Type: cty.String, Required: false},
}
return s
}

View File

@ -1,390 +0,0 @@
package chefsolo
import (
"io/ioutil"
"os"
"testing"
"github.com/hashicorp/packer-plugin-sdk/common"
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
)
func testConfig() map[string]interface{} {
return map[string]interface{}{}
}
func TestProvisioner_Impl(t *testing.T) {
var raw interface{}
raw = &Provisioner{}
if _, ok := raw.(packersdk.Provisioner); !ok {
t.Fatalf("must be a Provisioner")
}
}
func TestProvisionerPrepare_chefEnvironment(t *testing.T) {
var p Provisioner
config := testConfig()
config["chef_environment"] = "some-env"
err := p.Prepare(config)
if err != nil {
t.Fatalf("err: %s", err)
}
if p.config.ChefEnvironment != "some-env" {
t.Fatalf("unexpected: %#v", p.config.ChefEnvironment)
}
}
func TestProvisionerPrepare_chefLicense(t *testing.T) {
var p Provisioner
// Test not set
config := testConfig()
err := p.Prepare(config)
if err != nil {
t.Fatal("should error")
}
if p.config.ChefLicense != "accept-silent" {
t.Fatalf("unexpected: %#v", p.config.ChefLicense)
}
// Test set
config = testConfig()
config["chef_license"] = "accept"
p = Provisioner{}
err = p.Prepare(config)
if err != nil {
t.Fatalf("err: %s", err)
}
if p.config.ChefLicense != "accept" {
t.Fatalf("unexpected: %#v", p.config.ChefLicense)
}
// Test set skipInstall true
config = testConfig()
config["skip_install"] = true
p = Provisioner{}
err = p.Prepare(config)
if err != nil {
t.Fatalf("err: %s", err)
}
if p.config.ChefLicense != "" {
t.Fatalf("unexpected: %#v", "empty string")
}
// Test set installCommand true
config = testConfig()
config["install_command"] = "install chef"
p = Provisioner{}
err = p.Prepare(config)
if err != nil {
t.Fatalf("err: %s", err)
}
if p.config.ChefLicense != "" {
t.Fatalf("unexpected: %#v", "empty string")
}
}
func TestProvisionerPrepare_configTemplate(t *testing.T) {
var err error
var p Provisioner
// Test no config template
config := testConfig()
delete(config, "config_template")
err = p.Prepare(config)
if err != nil {
t.Fatalf("err: %s", err)
}
// Test with a file
tf, err := ioutil.TempFile("", "packer")
if err != nil {
t.Fatalf("err: %s", err)
}
defer os.Remove(tf.Name())
config = testConfig()
config["config_template"] = tf.Name()
p = Provisioner{}
err = p.Prepare(config)
if err != nil {
t.Fatalf("err: %s", err)
}
// Test with a directory
td, err := ioutil.TempDir("", "packer")
if err != nil {
t.Fatalf("err: %s", err)
}
defer os.RemoveAll(td)
config = testConfig()
config["config_template"] = td
p = Provisioner{}
err = p.Prepare(config)
if err == nil {
t.Fatal("should have err")
}
}
func TestProvisionerPrepare_cookbookPaths(t *testing.T) {
var p Provisioner
path1, err := ioutil.TempDir("", "cookbooks_one")
if err != nil {
t.Fatalf("err: %s", err)
}
path2, err := ioutil.TempDir("", "cookbooks_two")
if err != nil {
t.Fatalf("err: %s", err)
}
rolesPath, err := ioutil.TempDir("", "roles")
if err != nil {
t.Fatalf("err: %s", err)
}
dataBagsPath, err := ioutil.TempDir("", "data_bags")
if err != nil {
t.Fatalf("err: %s", err)
}
defer os.Remove(path1)
defer os.Remove(path2)
defer os.Remove(rolesPath)
defer os.Remove(dataBagsPath)
config := testConfig()
config["cookbook_paths"] = []string{path1, path2}
config["roles_path"] = rolesPath
config["data_bags_path"] = dataBagsPath
err = p.Prepare(config)
if err != nil {
t.Fatalf("err: %s", err)
}
if len(p.config.CookbookPaths) != 2 {
t.Fatalf("unexpected: %#v", p.config.CookbookPaths)
}
if p.config.CookbookPaths[0] != path1 || p.config.CookbookPaths[1] != path2 {
t.Fatalf("unexpected: %#v", p.config.CookbookPaths)
}
if p.config.RolesPath != rolesPath {
t.Fatalf("unexpected: %#v", p.config.RolesPath)
}
if p.config.DataBagsPath != dataBagsPath {
t.Fatalf("unexpected: %#v", p.config.DataBagsPath)
}
}
func TestProvisionerPrepare_dataBagsPath(t *testing.T) {
var p Provisioner
dataBagsPath, err := ioutil.TempDir("", "data_bags")
if err != nil {
t.Fatalf("err: %s", err)
}
defer os.Remove(dataBagsPath)
config := testConfig()
config["data_bags_path"] = dataBagsPath
err = p.Prepare(config)
if err != nil {
t.Fatalf("err: %s", err)
}
if p.config.DataBagsPath != dataBagsPath {
t.Fatalf("unexpected: %#v", p.config.DataBagsPath)
}
}
func TestProvisionerPrepare_encryptedDataBagSecretPath(t *testing.T) {
var err error
var p Provisioner
// Test no config template
config := testConfig()
delete(config, "encrypted_data_bag_secret_path")
err = p.Prepare(config)
if err != nil {
t.Fatalf("err: %s", err)
}
// Test with a file
tf, err := ioutil.TempFile("", "packer")
if err != nil {
t.Fatalf("err: %s", err)
}
defer os.Remove(tf.Name())
config = testConfig()
config["encrypted_data_bag_secret_path"] = tf.Name()
p = Provisioner{}
err = p.Prepare(config)
if err != nil {
t.Fatalf("err: %s", err)
}
// Test with a directory
td, err := ioutil.TempDir("", "packer")
if err != nil {
t.Fatalf("err: %s", err)
}
defer os.RemoveAll(td)
config = testConfig()
config["encrypted_data_bag_secret_path"] = td
p = Provisioner{}
err = p.Prepare(config)
if err == nil {
t.Fatal("should have err")
}
}
func TestProvisionerPrepare_environmentsPath(t *testing.T) {
var p Provisioner
environmentsPath, err := ioutil.TempDir("", "environments")
if err != nil {
t.Fatalf("err: %s", err)
}
defer os.Remove(environmentsPath)
config := testConfig()
config["environments_path"] = environmentsPath
err = p.Prepare(config)
if err != nil {
t.Fatalf("err: %s", err)
}
if p.config.EnvironmentsPath != environmentsPath {
t.Fatalf("unexpected: %#v", p.config.EnvironmentsPath)
}
}
func TestProvisionerPrepare_rolesPath(t *testing.T) {
var p Provisioner
rolesPath, err := ioutil.TempDir("", "roles")
if err != nil {
t.Fatalf("err: %s", err)
}
defer os.Remove(rolesPath)
config := testConfig()
config["roles_path"] = rolesPath
err = p.Prepare(config)
if err != nil {
t.Fatalf("err: %s", err)
}
if p.config.RolesPath != rolesPath {
t.Fatalf("unexpected: %#v", p.config.RolesPath)
}
}
func TestProvisionerPrepare_json(t *testing.T) {
config := testConfig()
config["json"] = map[string]interface{}{
"foo": "{{ user `foo` }}",
}
config[common.UserVariablesConfigKey] = map[string]string{
"foo": `"bar\baz"`,
}
var p Provisioner
err := p.Prepare(config)
if err != nil {
t.Fatalf("err: %s", err)
}
if p.config.Json["foo"] != `"bar\baz"` {
t.Fatalf("bad: %#v", p.config.Json)
}
}
func TestProvisionerPrepare_jsonNested(t *testing.T) {
config := testConfig()
config["json"] = map[string]interface{}{
"foo": map[interface{}]interface{}{
"bar": []uint8("baz"),
},
"bar": []interface{}{
"foo",
map[interface{}]interface{}{
"bar": "baz",
},
},
"bFalse": false,
"bTrue": true,
"bNil": nil,
"bStr": []uint8("bar"),
"bInt": 1,
"bFloat": 4.5,
}
var p Provisioner
err := p.Prepare(config)
if err != nil {
t.Fatalf("err: %s", err)
}
fooMap := p.config.Json["foo"].(map[string]interface{})
if fooMap["bar"] != "baz" {
t.Fatalf("nope: %#v", fooMap["bar"])
}
if p.config.Json["bStr"] != "bar" {
t.Fatalf("nope: %#v", fooMap["bar"])
}
}
func TestProvisionerPrepare_jsonstring(t *testing.T) {
config := testConfig()
config["json_string"] = `{
"foo": {
"bar": "baz"
},
"bar": {
"bar": "baz"
},
"bFalse": false,
"bTrue": true,
"bStr": "bar",
"bNil": null,
"bInt": 1,
"bFloat": 4.5
}`
var p Provisioner
err := p.Prepare(config)
if err != nil {
t.Fatalf("err: %s", err)
}
fooMap := p.config.Json["foo"].(map[string]interface{})
if fooMap["bar"] != "baz" {
t.Fatalf("nope: %#v", fooMap["bar"])
}
if p.config.Json["bStr"] != "bar" {
t.Fatalf("nope: %#v", fooMap["bar"])
}
}

View File

@ -1,13 +0,0 @@
package version
import (
"github.com/hashicorp/packer-plugin-sdk/version"
packerVersion "github.com/hashicorp/packer/version"
)
var ChefSoloPluginVersion *version.PluginVersion
func init() {
ChefSoloPluginVersion = version.InitializePluginVersion(
packerVersion.Version, packerVersion.VersionPrerelease)
}

View File

@ -1,430 +0,0 @@
---
description: >
The chef-client Packer provisioner installs and configures software on
machines
built by Packer using chef-client. Packer configures a Chef client to talk to
a
remote Chef Server to provision the machine.
page_title: Chef Client - Provisioners
---
# Chef Client Provisioner
@include 'provisioners/unmaintained-plugin.mdx'
Type: `chef-client`
The Chef Client Packer provisioner installs and configures software on machines
built by Packer using [chef-client](https://docs.chef.io/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 Chef.
## Basic Example
The example below is fully functional. It will install Chef onto the remote
machine and run Chef client.
```json
{
"type": "chef-client",
"server_url": "https://mychefserver.com/"
}
```
Note: to properly clean up the Chef node and client the machine on which packer
is running must have knife on the path and configured globally, i.e,
`~/.chef/knife.rb` must be present and configured for the target chef server
## Configuration Reference
The reference of available configuration options is listed below. No
configuration is actually required.
- `chef_environment` (string) - The name of the chef_environment sent to the
Chef server. By default this is empty and will not use an environment.
- `chef_license` (string) - As of Chef v15, Chef requires users to accept a
license. Defaults to `accept-silent` when `skip_install` is false and
`install_command` is unset. Possible values are `accept`,
`accept-silent` and `accept-no-persist`. For details see [Accepting the
Chef License](https://docs.chef.io/chef_license_accept.html).
This is a [template engine](/docs/templates/legacy_json_templates/engine.html). Therefore, you
may use user variables and template functions in this field.
- `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.
- `elevated_user` and `elevated_password` (string) - If specified, Chef will
be run with elevated privileges using the given Windows user. See the
[powershell](/docs/provisioners/powershell) provisioner for the full
details.
- `encrypted_data_bag_secret_path` (string) - The path to the file containing
the secret for encrypted data bags. By default, this is empty, so no secret
will be available.
- `execute_command` (string) - The command used to execute Chef. This has
various [configuration template variables](/docs/templates/legacy_json_templates/engine)
available. See below for more information.
- `guest_os_type` (string) - The target guest OS type, either "unix" or
"windows". Setting this to "windows" will cause the provisioner to use
Windows friendly paths and commands. By default, this is "unix".
- `install_command` (string) - The command used to install Chef. This has
various [configuration template variables](/docs/templates/legacy_json_templates/engine)
available. See below for more information.
- `json` (object) - An arbitrary mapping of JSON that will be available as
node attributes while running Chef.
- `knife_command` (string) - The command used to run Knife during node
clean-up. This has various [configuration template
variables](/docs/templates/legacy_json_templates/engine) available. See below for more
information.
- `node_name` (string) - The name of the node to register with the Chef
Server. This is optional and by default is `packer-{{uuid}}`.
- `policy_group` (string) - The name of a policy group that exists on the
Chef server. `policy_name` must also be specified.
- `policy_name` (string) - The name of a policy, as identified by the name
setting in a `Policyfile.rb` file. `policy_group` must also be specified.
- `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. This has no effect when guest_os_type is
windows.
- `run_list` (array of strings) - The [run
list](https://docs.chef.io/run_lists) 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.
- `skip_clean_staging_directory` (boolean) - If true, Packer won't remove the
Chef staging directory from the machine after it is done running. By
default, this is false.
- `skip_install` (boolean) - If true, Chef will not automatically be
installed on the machine using the Chef omnibus installers.
- `ssl_verify_mode` (string) - Set to "verify_none" to skip validation of
SSL certificates. If not set, this defaults to "verify_peer" which
validates all SSL certifications.
- `trusted_certs_dir` (string) - This is a directory that contains additional
SSL certificates to trust. Any certificates in this directory will be added
to whatever CA bundle ruby is using. Use this to add self-signed certs for
your Chef Server or local HTTP file servers.
- `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-client` when guest_os_type unix and
`$env:TEMP/packer-chef-client` when windows. This directory doesn't need to
exist but must have proper permissions so that the user that Packer uses is
able to create directories and write into this folder. By default the
provisioner will create and chmod 0777 this directory.
- `client_key` (string) - Path to client key. If not set, this defaults to a
file named client.pem in `staging_directory`.
- `validation_client_name` (string) - Name of the validation client. If not
set, this won't be set in the configuration and the default that Chef uses
will be used.
- `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.
- `version` (string) - The version of Chef to be installed. By default this
is empty which will install the latest version of Chef.
@include 'provisioners/common-config.mdx'
## 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:
```liquid
log_level :info
log_location STDOUT
chef_server_url "{{.ServerUrl}}"
client_key "{{.ClientKey}}"
chef_license "{{.ChefLicense}}"
{{if ne .EncryptedDataBagSecretPath ""}}
encrypted_data_bag_secret "{{.EncryptedDataBagSecretPath}}"
{{end}}
{{if ne .ValidationClientName ""}}
validation_client_name "{{.ValidationClientName}}"
{{else}}
validation_client_name "chef-validator"
{{end}}
{{if ne .ValidationKeyPath ""}}
validation_key "{{.ValidationKeyPath}}"
{{end}}
node_name "{{.NodeName}}"
{{if ne .ChefEnvironment ""}}
environment "{{.ChefEnvironment}}"
{{end}}
{{if ne .PolicyGroup ""}}
policy_group "{{.PolicyGroup}}"
{{end}}
{{if ne .PolicyName ""}}
policy_name "{{.PolicyName}}"
{{end}}
{{if ne .SslVerifyMode ""}}
ssl_verify_mode :{{.SslVerifyMode}}
{{end}}
{{if ne .TrustedCertsDir ""}}
trusted_certs_dir :{{.TrustedCertsDir}}
{{end}}
```
This template is a [configuration template](/docs/templates/legacy_json_templates/engine) and
has a set of variables available to use:
- `ChefEnvironment` - The Chef environment name.
- `ChefLicense` - The Chef license acceptance value.
- `EncryptedDataBagSecretPath` - The path to the secret key file to decrypt
encrypted data bags.
- `NodeName` - The node name set in the configuration.
- `ServerUrl` - The URL of the Chef Server set in the configuration.
- `SslVerifyMode` - Whether Chef SSL verify mode is on or off.
- `TrustedCertsDir` - Path to dir with trusted certificates.
- `ValidationClientName` - The name of the client used for validation.
- `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:
```liquid
{{if .Sudo}}sudo {{end}}chef-client \
--no-color \
-c {{.ConfigPath}} \
-j {{.JsonPath}}
```
When guest_os_type is set to "windows", Packer uses the following command to
execute Chef. The full path to Chef is required because the PATH environment
variable changes don't immediately propagate to running processes.
```liquid
c:/opscode/chef/bin/chef-client.bat \
--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.
- `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.
```text
curl -L https://omnitruck.chef.io/chef/install.sh | \
{{if .Sudo}}sudo{{end}} bash
```
When guest_os_type is set to "windows", Packer uses the following command to
install the latest version of Chef:
```text
powershell.exe -Command "(New-Object System.Net.WebClient).DownloadFile('http://chef.io/chef/install.msi', 'C:\\Windows\\Temp\\chef.msi');Start-Process 'msiexec' -ArgumentList '/qb /i C:\\Windows\\Temp\\chef.msi' -NoNewWindow -Wait"
```
This command can be customized using the `install_command` configuration.
## Knife Command
By default, Packer uses the following command (broken across multiple lines for
readability) to execute Chef:
```liquid
{{if .Sudo}}sudo {{end}}knife \
{{.Args}} \
{{.Flags}}
```
When guest_os_type is set to "windows", Packer uses the following command to
execute Chef. The full path to Chef is required because the PATH environment
variable changes don't immediately propagate to running processes.
```liquid
c:/opscode/chef/bin/knife.bat \
{{.Args}} \
{{.Flags}}
```
This command can be customized using the `knife_command` configuration. As you
can see from the default value above, the value of this configuration can
contain various template variables, defined below:
- `Args` - The command arguments that are getting passed to the Knife
command.
- `Flags` - The command flags that are getting passed to the Knife command..
- `Sudo` - A boolean of whether to `sudo` the command or not, depending on
the value of the `prevent_sudo` configuration.
## Folder Permissions
!> The `chef-client` provisioner will chmod the directory with your Chef
keys to 777. This is to ensure that Packer can upload and make use of that
directory. However, once the machine is created, you usually don't want to keep
these directories with those permissions. To change the permissions on the
directories, append a shell provisioner after Chef to modify them.
## Examples
### Chef Client Local Mode - Simple
The following example shows how to run the `chef-client` provisioner in local
mode.
**Packer variables**
Set the necessary Packer variables using environment variables or provide a
[var file](/docs/templates/legacy_json_templates/user-variables).
```json
"variables": {
"chef_dir": "/tmp/packer-chef-client"
}
```
**Setup the** `chef-client` **provisioner**
Make sure we have the correct directories and permissions for the `chef-client`
provisioner. You will need to bootstrap the Chef run by providing the necessary
cookbooks using Berkshelf or some other means.
```json
"provisioners": [
...
{ "type": "shell", "inline": [ "mkdir -p {{user `chef_dir`}}" ] },
{ "type": "file", "source": "./roles", "destination": "{{user `chef_dir`}}" },
{ "type": "file", "source": "./cookbooks", "destination": "{{user `chef_dir`}}" },
{ "type": "file", "source": "./data_bags", "destination": "{{user `chef_dir`}}" },
{ "type": "file", "source": "./environments", "destination": "{{user `chef_dir`}}" },
{ "type": "file", "source": "./scripts/install_chef.sh", "destination": "{{user `chef_dir`}}/install_chef.sh" },
{
"type": "chef-client",
"install_command": "sudo bash {{user `chef_dir`}}/install_chef.sh",
"server_url": "http://localhost:8889",
"config_template": "./config/client.rb.template",
"run_list": [ "role[testing]" ],
"skip_clean_node": true,
"skip_clean_client": true
}
...
]
```
And ./config/client.rb.template referenced by the above configuration:
```ruby
log_level :info
log_location STDOUT
local_mode true
chef_zero.enabled true
ssl_verify_mode "verify_peer"
role_path "{{user `chef_dir`}}/roles"
data_bag_path "{{user `chef_dir`}}/data_bags"
environment_path "{{user `chef_dir`}}/environments"
cookbook_path [ "{{user `chef_dir`}}/cookbooks" ]
```
### Chef Client Local Mode - Passing variables
The following example shows how to run the `chef-client` provisioner in local
mode, while passing a `run_list` using a variable.
**Local environment variables**
# Machine's Chef directory
export PACKER_CHEF_DIR=/var/chef-packer
# Comma separated run_list
export PACKER_CHEF_RUN_LIST="recipe[apt],recipe[nginx]"
**Packer variables**
Set the necessary Packer variables using environment variables or provide a
[var file](/docs/templates/legacy_json_templates/user-variables).
```json
"variables": {
"chef_dir": "{{env `PACKER_CHEF_DIR`}}",
"chef_run_list": "{{env `PACKER_CHEF_RUN_LIST`}}",
"chef_client_config_tpl": "{{env `PACKER_CHEF_CLIENT_CONFIG_TPL`}}",
"packer_chef_bootstrap_dir": "{{env `PACKER_CHEF_BOOTSTRAP_DIR`}}" ,
"packer_uid": "{{env `PACKER_UID`}}",
"packer_gid": "{{env `PACKER_GID`}}"
}
```
**Setup the** `chef-client` **provisioner**
Make sure we have the correct directories and permissions for the `chef-client`
provisioner. You will need to bootstrap the Chef run by providing the necessary
cookbooks using Berkshelf or some other means.
```json
({
"type": "file",
"source": "{{user `packer_chef_bootstrap_dir`}}",
"destination": "/tmp/bootstrap"
},
{
"type": "shell",
"inline": [
"sudo mkdir -p {{user `chef_dir`}}",
"sudo mkdir -p /tmp/packer-chef-client",
"sudo chown {{user `packer_uid`}}.{{user `packer_gid`}} /tmp/packer-chef-client",
"sudo sh /tmp/bootstrap/bootstrap.sh"
]
},
{
"type": "chef-client",
"server_url": "http://localhost:8889",
"config_template": "{{user `chef_client_config_tpl`}}/client.rb.tpl",
"skip_clean_node": true,
"skip_clean_client": true,
"run_list": "{{user `chef_run_list`}}"
})
```

View File

@ -1,256 +0,0 @@
---
description: |
The chef-solo Packer provisioner installs and configures software on machines
built by Packer using chef-solo. Cookbooks can be uploaded from your local
machine to the remote machine or remote paths can be used.
page_title: Chef Solo - Provisioners
---
# Chef Solo Provisioner
@include 'provisioners/unmaintained-plugin.mdx'
Type: `chef-solo`
The Chef solo Packer provisioner installs and configures software on machines
built by Packer using [chef-solo](https://docs.chef.io/chef_solo.html).
Cookbooks can be uploaded from your local machine to the remote machine or
remote paths can be used.
The provisioner will even install Chef onto your machine if it isn't already
installed, using the official Chef installers provided by Chef Inc.
## Basic Example
The example below is fully functional and expects cookbooks in the "cookbooks"
directory relative to your working directory.
<Tabs>
<Tab heading="HCL2">
```hcl
provisioner "chef-solo" {
cookbook_paths = ["cookbooks"]
}
```
</Tab>
<Tab heading="JSON">
```json
"provisioners":[{
"type": "chef-solo",
"cookbook_paths": ["cookbooks"]
}]
```
</Tab>
</Tabs>
## Configuration Reference
The reference of available configuration options is listed below. No
configuration is actually required, but at least `run_list` is recommended.
- `chef_environment` (string) - The name of the `chef_environment` sent to
the Chef server. By default this is empty and will not use an environment
- `chef_license` (string) - As of Chef v15, Chef requires users to accept a
license. Defaults to `accept-silent` when `skip_install` is false and
`install_command` is unset. Possible values are `accept`,
`accept-silent` and `accept-no-persist`. For details see [Accepting the
Chef License](https://docs.chef.io/chef_license_accept.html).
- `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.
- `cookbook_paths` (array of strings) - This is an array of paths to
"cookbooks" directories on your local filesystem. These will be uploaded to
the remote machine in the directory specified by the `staging_directory`.
By default, this is empty.
- `data_bags_path` (string) - The path to the "data_bags" directory on your
local filesystem. These will be uploaded to the remote machine in the
directory specified by the `staging_directory`. By default, this is empty.
- `encrypted_data_bag_secret_path` (string) - The path to the file containing
the secret for encrypted data bags. By default, this is empty, so no secret
will be available.
- `environments_path` (string) - The path to the "environments" directory on
your local filesystem. These will be uploaded to the remote machine in the
directory specified by the `staging_directory`. By default, this is empty.
- `execute_command` (string) - The command used to execute Chef. This has
various [configuration template variables](/docs/templates/legacy_json_templates/engine)
available. See below for more information.
- `guest_os_type` (string) - The target guest OS type, either "unix" or
"windows". Setting this to "windows" will cause the provisioner to use
Windows friendly paths and commands. By default, this is "unix".
- `install_command` (string) - The command used to install Chef. This has
various [configuration template variables](/docs/templates/legacy_json_templates/engine)
available. See below for more information.
- `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. This has no effect when guest_os_type is
windows.
- `remote_cookbook_paths` (array of strings) - A list of paths on the remote
machine where cookbooks will already exist. These may exist from a previous
provisioner or step. If specified, Chef will be configured to look for
cookbooks here. By default, this is empty.
- `roles_path` (string) - The path to the "roles" directory on your local
filesystem. These will be uploaded to the remote machine in the directory
specified by the `staging_directory`. By default, this is empty.
- `run_list` (array of strings) - The [run
list](https://docs.chef.io/run_lists.html) for Chef. By default this is
empty.
- `skip_install` (boolean) - If true, Chef will not automatically be
installed on the machine using the Chef 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` when guest_os_type unix and
`$env:TEMP/packer-chef-solo` when windows. This directory doesn't need to
exist but must have proper permissions so that the 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.
- `version` (string) - The version of Chef to be installed. By default this
is empty which will install the latest version of Chef.
@include 'provisioners/common-config.mdx'
##### Node Attribute Mapping
An arbitrary mapping of JSON that will be available as node attributes while running Chef.
<Tabs>
<Tab heading="HCL2">
- `json_string` (string) - The JSON string can be encoded using the [jsonencode](/docs/templates/hcl_templates/functions/encoding/jsonencode)
template function.
```hcl
provisioner "chef-solo" {
json_string = jsonencode({
"a" = "b"
"foo" = {
"bar" = "val"
"number" = 1
}
})
}
```
</Tab>
<Tab heading="JSON">
- `json` (object) - This option is only available to old-style JSON templates.
```json
"provisioners":[{
"type": "chef-solo",
"json": {
"a": "b",
"foo": {
"bar": "val",
"number": 1
}
}
}]
```
</Tab>
</Tabs>
## 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:
```liquid
cookbook_path [{{.CookbookPaths}}]
```
This template is a [configuration template](/docs/templates/legacy_json_templates/engine) and
has a set of variables available to use:
- `ChefEnvironment` - The current enabled environment. Only non-empty if the
environment path is set.
- `ChefLicense` - The Chef license acceptance value.
- `CookbookPaths` is the set of cookbook paths ready to embedded directly
into a Ruby array to configure Chef.
- `DataBagsPath` is the path to the data bags folder.
- `EncryptedDataBagSecretPath` - The path to the encrypted data bag secret
- `EnvironmentsPath` - The path to the environments folder.
- `RolesPath` - The path to the roles folder.
## Execute Command
By default, Packer uses the following command (broken across multiple lines for
readability) to execute Chef:
```liquid
{{if .Sudo}}sudo {{end}}chef-solo \
--no-color \
-c {{.ConfigPath}} \
-j {{.JsonPath}}
```
When guest_os_type is set to "windows", Packer uses the following command to
execute Chef. The full path to Chef is required because the PATH environment
variable changes don't immediately propagate to running processes.
```liquid
c:/opscode/chef/bin/chef-solo.bat \
--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.
- `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.
```text
curl -L https://omnitruck.chef.io/install.sh | \
{{if .Sudo}}sudo{{end}} bash -s --{{if .Version}} -v {{.Version}}{{end}}
```
When guest_os_type is set to "windows", Packer uses the following command to
install the latest version of Chef:
```powershell
powershell.exe -Command \". { iwr -useb https://omnitruck.chef.io/install.ps1 } | iex; install\"
```
This command can be customized using the `install_command` configuration.

View File

@ -828,14 +828,6 @@
"title": "Breakpoint",
"path": "provisioners/breakpoint"
},
{
"title": "Chef Client",
"path": "provisioners/chef-client"
},
{
"title": "Chef Solo",
"path": "provisioners/chef-solo"
},
{
"title": "Converge",
"path": "provisioners/converge"