Remove Chef components and docs
This commit is contained in:
parent
2da9c21733
commit
30bcf44c2c
|
@ -50,8 +50,6 @@ import (
|
||||||
yandeximportpostprocessor "github.com/hashicorp/packer/post-processor/yandex-import"
|
yandeximportpostprocessor "github.com/hashicorp/packer/post-processor/yandex-import"
|
||||||
azuredtlartifactprovisioner "github.com/hashicorp/packer/provisioner/azure-dtlartifact"
|
azuredtlartifactprovisioner "github.com/hashicorp/packer/provisioner/azure-dtlartifact"
|
||||||
breakpointprovisioner "github.com/hashicorp/packer/provisioner/breakpoint"
|
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"
|
convergeprovisioner "github.com/hashicorp/packer/provisioner/converge"
|
||||||
fileprovisioner "github.com/hashicorp/packer/provisioner/file"
|
fileprovisioner "github.com/hashicorp/packer/provisioner/file"
|
||||||
inspecprovisioner "github.com/hashicorp/packer/provisioner/inspec"
|
inspecprovisioner "github.com/hashicorp/packer/provisioner/inspec"
|
||||||
|
@ -100,8 +98,6 @@ var Builders = map[string]packersdk.Builder{
|
||||||
var Provisioners = map[string]packersdk.Provisioner{
|
var Provisioners = map[string]packersdk.Provisioner{
|
||||||
"azure-dtlartifact": new(azuredtlartifactprovisioner.Provisioner),
|
"azure-dtlartifact": new(azuredtlartifactprovisioner.Provisioner),
|
||||||
"breakpoint": new(breakpointprovisioner.Provisioner),
|
"breakpoint": new(breakpointprovisioner.Provisioner),
|
||||||
"chef-client": new(chefclientprovisioner.Provisioner),
|
|
||||||
"chef-solo": new(chefsoloprovisioner.Provisioner),
|
|
||||||
"converge": new(convergeprovisioner.Provisioner),
|
"converge": new(convergeprovisioner.Provisioner),
|
||||||
"file": new(fileprovisioner.Provisioner),
|
"file": new(fileprovisioner.Provisioner),
|
||||||
"inspec": new(inspecprovisioner.Provisioner),
|
"inspec": new(inspecprovisioner.Provisioner),
|
||||||
|
|
|
@ -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}}
|
|
||||||
`
|
|
|
@ -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
|
|
||||||
}
|
|
|
@ -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())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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)
|
|
||||||
}
|
|
|
@ -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}}
|
|
||||||
`
|
|
|
@ -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
|
|
||||||
}
|
|
|
@ -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"])
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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)
|
|
||||||
}
|
|
|
@ -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`}}"
|
|
||||||
})
|
|
||||||
```
|
|
|
@ -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.
|
|
|
@ -828,14 +828,6 @@
|
||||||
"title": "Breakpoint",
|
"title": "Breakpoint",
|
||||||
"path": "provisioners/breakpoint"
|
"path": "provisioners/breakpoint"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"title": "Chef Client",
|
|
||||||
"path": "provisioners/chef-client"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"title": "Chef Solo",
|
|
||||||
"path": "provisioners/chef-solo"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"title": "Converge",
|
"title": "Converge",
|
||||||
"path": "provisioners/converge"
|
"path": "provisioners/converge"
|
||||||
|
|
Loading…
Reference in New Issue