move salt-masterless provisioner out (#11229)

* move salt-masterless provisioner out
* go get github.com/hashicorp/packer-plugin-salt
pull/11238/head
Adrien Delorme 5 years ago committed by GitHub
parent eb39a3120e
commit acb01cb800
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -24,7 +24,6 @@ import (
fileprovisioner "github.com/hashicorp/packer/provisioner/file"
inspecprovisioner "github.com/hashicorp/packer/provisioner/inspec"
powershellprovisioner "github.com/hashicorp/packer/provisioner/powershell"
saltmasterlessprovisioner "github.com/hashicorp/packer/provisioner/salt-masterless"
shellprovisioner "github.com/hashicorp/packer/provisioner/shell"
shelllocalprovisioner "github.com/hashicorp/packer/provisioner/shell-local"
sleepprovisioner "github.com/hashicorp/packer/provisioner/sleep"
@ -46,7 +45,6 @@ var Provisioners = map[string]packersdk.Provisioner{
"file": new(fileprovisioner.Provisioner),
"inspec": new(inspecprovisioner.Provisioner),
"powershell": new(powershellprovisioner.Provisioner),
"salt-masterless": new(saltmasterlessprovisioner.Provisioner),
"shell": new(shellprovisioner.Provisioner),
"shell-local": new(shelllocalprovisioner.Provisioner),
"sleep": new(sleepprovisioner.Provisioner),

@ -62,6 +62,7 @@ import (
puppetmasterlessprovisioner "github.com/hashicorp/packer-plugin-puppet/provisioner/puppet-masterless"
puppetserverprovisioner "github.com/hashicorp/packer-plugin-puppet/provisioner/puppet-server"
qemubuilder "github.com/hashicorp/packer-plugin-qemu/builder/qemu"
saltmasterlessprovisioner "github.com/hashicorp/packer-plugin-salt/provisioner/salt-masterless"
scalewaybuilder "github.com/hashicorp/packer-plugin-scaleway/builder/scaleway"
tencentcloudcvmbuilder "github.com/hashicorp/packer-plugin-tencentcloud/builder/tencentcloud/cvm"
tritonbuilder "github.com/hashicorp/packer-plugin-triton/builder/triton"
@ -157,6 +158,7 @@ var VendoredProvisioners = map[string]packersdk.Provisioner{
"converge": new(convergeprovisioner.Provisioner),
"puppet-masterless": new(puppetmasterlessprovisioner.Provisioner),
"puppet-server": new(puppetserverprovisioner.Provisioner),
"salt-masterless": new(saltmasterlessprovisioner.Provisioner),
}
// VendoredPostProcessors are post-processor components that were once bundled with the

@ -47,6 +47,7 @@ require (
github.com/hashicorp/packer-plugin-proxmox v1.0.0
github.com/hashicorp/packer-plugin-puppet v1.0.0
github.com/hashicorp/packer-plugin-qemu v1.0.0
github.com/hashicorp/packer-plugin-salt v0.0.2
github.com/hashicorp/packer-plugin-scaleway v1.0.1
github.com/hashicorp/packer-plugin-sdk v0.2.3
github.com/hashicorp/packer-plugin-tencentcloud v1.0.1
@ -71,7 +72,7 @@ require (
github.com/shirou/gopsutil v3.21.1+incompatible
github.com/stretchr/testify v1.7.0
github.com/ulikunitz/xz v0.5.8
github.com/zclconf/go-cty v1.9.0
github.com/zclconf/go-cty v1.9.1
github.com/zclconf/go-cty-yaml v1.0.1
golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a
golang.org/x/mod v0.4.1

@ -105,8 +105,9 @@ github.com/Microsoft/go-winio v0.4.16 h1:FtSW/jqD+l4ba5iPBj9CODVtgfYAD8w2wS923g/
github.com/Microsoft/go-winio v0.4.16/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0=
github.com/Microsoft/hcsshim v0.8.9/go.mod h1:5692vkUqntj1idxauYlpoINNKeqCiG6Sg38RRsjT5y8=
github.com/NaverCloudPlatform/ncloud-sdk-go-v2 v1.1.0/go.mod h1:P+3VS0ETiQPyWOx3vB/oeC8J3qd7jnVZLYAFwWgGRt8=
github.com/NaverCloudPlatform/ncloud-sdk-go-v2 v1.1.7 h1:Kpnbe19WkzVPpLLdAus4LkpNJ+pzLpfAViOUuvPcCqA=
github.com/NaverCloudPlatform/ncloud-sdk-go-v2 v1.1.7/go.mod h1:P+3VS0ETiQPyWOx3vB/oeC8J3qd7jnVZLYAFwWgGRt8=
github.com/NaverCloudPlatform/ncloud-sdk-go-v2 v1.2.0 h1:c7GgSBfMt51UGM4SI1F7IFOokOVZO+uxNcJL3Xsmkp4=
github.com/NaverCloudPlatform/ncloud-sdk-go-v2 v1.2.0/go.mod h1:P+3VS0ETiQPyWOx3vB/oeC8J3qd7jnVZLYAFwWgGRt8=
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7 h1:YoJbenK9C67SkzkDfmQuVln04ygHj3vjZfd9FL+GmQQ=
github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7/go.mod h1:z4/9nQmJSSwwds7ejkxaJwO37dru3geImFUdJlaLzQo=
@ -732,13 +733,16 @@ github.com/hashicorp/packer v1.6.7-0.20210217093213-201869d627bf/go.mod h1:+EWPP
github.com/hashicorp/packer v1.7.0/go.mod h1:3KRJcwOctl2JaAGpQMI1bWQRArfWNWqcYjO6AOsVVGQ=
github.com/hashicorp/packer v1.7.1/go.mod h1:ApnmMINvuhhnfPyTVqZu6jznDWPVYDJUw7e188DFCmo=
github.com/hashicorp/packer v1.7.2/go.mod h1:c/QB/DWK5fSdtNWrTb9etWacmbm01UY23ZILpGundCY=
github.com/hashicorp/packer v1.7.4/go.mod h1:uD7xkJ8yYOLUHEIOasoOXrIEmpdOibOIdwHUYroi6zc=
github.com/hashicorp/packer-plugin-alicloud v1.0.0 h1:f1wyIqgSQQlpYs116Auq/FLYtrW3GbpTzxXxZWmNc1A=
github.com/hashicorp/packer-plugin-alicloud v1.0.0/go.mod h1:wDWvfgD8JWdrGnAzO6SkcJJiAL+64SCEVY+DWcefNas=
github.com/hashicorp/packer-plugin-amazon v0.0.1/go.mod h1:12c9msibyHdId+Mk/pCbdRb1KaLIhaNyxeJ6n8bZt30=
github.com/hashicorp/packer-plugin-amazon v1.0.0/go.mod h1:A4cjwkca0op3y/IWV3z9vTCHeJY2nLwBnwRxRFlFw6Y=
github.com/hashicorp/packer-plugin-amazon v1.0.1-dev h1:dtCHPa+TfpbdbfqOm81PQ+kgF3gg9SdsWQcWRHaHAVw=
github.com/hashicorp/packer-plugin-amazon v1.0.1-dev/go.mod h1:njc7Y7niDlf72TxK9Wl/ntfxbAF+R932sHjP5Kq8Tis=
github.com/hashicorp/packer-plugin-ansible v1.0.0 h1:YU+k3JgwojL4Aw/yq6F9+b56B2wrhRfhQQgVCRRAZck=
github.com/hashicorp/packer-plugin-ansible v1.0.0/go.mod h1:wbOfX8U2pcZdsChjwvvK62HF4C1hM4qcn1x5gjp87v0=
github.com/hashicorp/packer-plugin-azure v1.0.0/go.mod h1:zBhmvyDu5SxpGa97JKGtSmw5GObhW/BGZVHIt2YmlKo=
github.com/hashicorp/packer-plugin-azure v1.0.1 h1:Vup22Vfcwmhm2nNQLQiVuZY47gnW6XlF1CTimdFJVac=
github.com/hashicorp/packer-plugin-azure v1.0.1/go.mod h1:uFZJBJu9HDnHcG/laZNTmIUuqpSTvs+WtBwpLSH/Vkc=
github.com/hashicorp/packer-plugin-chef v1.0.1 h1:sMTPzIAOaTlp4E05VZG6QILYUqR6IeBuszfHq3cdbUU=
@ -770,6 +774,7 @@ github.com/hashicorp/packer-plugin-lxc v1.0.0 h1:VZ98rBVCLpt5T/Z1DQYc+J/p7Fi6Rf3
github.com/hashicorp/packer-plugin-lxc v1.0.0/go.mod h1:fHqoE7MMNphPc8dZBs9dJZpfP+JciHQCXIq4bLS/SnE=
github.com/hashicorp/packer-plugin-lxd v1.0.0 h1:hjSQGBWerBhtIZndw0iG4ubmRyb9Ih5KS8mng1YAu0w=
github.com/hashicorp/packer-plugin-lxd v1.0.0/go.mod h1:jkBB4GnsoOrXZUFUiA1SOzj5Auk01MhpJiU6yIs5WJ4=
github.com/hashicorp/packer-plugin-ncloud v1.0.0/go.mod h1:kbCrWMogizFg0k+2x+vV74Nht5nak76g78h+N2sWOMA=
github.com/hashicorp/packer-plugin-ncloud v1.0.1 h1:0oELREIthQEFSyLmoQxLwLhuf8JBVqsJ+PZnJ+zrsvc=
github.com/hashicorp/packer-plugin-ncloud v1.0.1/go.mod h1:NH7KStfUBhi+Dky9KsJ8f2VvEefbKNyGBJvdpFC824E=
github.com/hashicorp/packer-plugin-oneandone v1.0.0 h1:byuTophmj3/0faHfUQXEisxbZETuFWN3XTjmkoLxfKY=
@ -790,6 +795,8 @@ github.com/hashicorp/packer-plugin-puppet v1.0.0 h1:KxyUnFBIzF+nXPNtf+I+5p+IwaLs
github.com/hashicorp/packer-plugin-puppet v1.0.0/go.mod h1:HRS/GxrY3iBINuTBgzOGR6jah+SRfqb37ixftZjZtJw=
github.com/hashicorp/packer-plugin-qemu v1.0.0 h1:EiWFadbgahXzY15a6G1BJ49wsmqVYQViw/t9GtJibTo=
github.com/hashicorp/packer-plugin-qemu v1.0.0/go.mod h1:U+IMWD5NjkCBNQIzttMPOQp+9Xzi4q/atkUJs2Nfv4s=
github.com/hashicorp/packer-plugin-salt v0.0.2 h1:MaXDObwd/Dwu0a/XX4AMr+6uL2Ms3AyUHrNWSn3sTgc=
github.com/hashicorp/packer-plugin-salt v0.0.2/go.mod h1:XYee5OuwqEaSoZGz2gUuT7KHKa08WEMEly4R+lMp380=
github.com/hashicorp/packer-plugin-scaleway v1.0.1 h1:eklSsFnY/r94TilgPGTH2HOIYG1bZKBaofRxHBPo2zI=
github.com/hashicorp/packer-plugin-scaleway v1.0.1/go.mod h1:kYoCKzb2GFDjqa4wPXGLjUM5lNzF/W7kcIoO13syJSY=
github.com/hashicorp/packer-plugin-sdk v0.0.6/go.mod h1:Nvh28f+Jmpp2rcaN79bULTouNkGNDRfHckhHKTAXtyU=
@ -827,10 +834,12 @@ github.com/hashicorp/serf v0.9.2 h1:yJoyfZXo4Pk2p/M/viW+YLibBFiIbKoP79gu7kDAFP0=
github.com/hashicorp/serf v0.9.2/go.mod h1:UWDWwZeL5cuWDJdl0C6wrvrUwEqtQ4ZKBKKENpqIUyk=
github.com/hashicorp/vault/api v1.0.4/go.mod h1:gDcqh3WGcR1cpF5AJz/B1UFheUEneMoIospckxBxk6Q=
github.com/hashicorp/vault/api v1.0.5-0.20200519221902-385fac77e20f/go.mod h1:euTFbi2YJgwcju3imEt919lhJKF68nN1cQPq3aA+kBE=
github.com/hashicorp/vault/api v1.1.0/go.mod h1:R3Umvhlxi2TN7Ex2hzOowyeNb+SfbVWI973N+ctaFMk=
github.com/hashicorp/vault/api v1.1.1 h1:907ld+Z9cALyvbZK2qUX9cLwvSaEQsMVQB3x2KE8+AI=
github.com/hashicorp/vault/api v1.1.1/go.mod h1:29UXcn/1cLOPHQNMWA7bCz2By4PSd0VKPAydKXS5yN0=
github.com/hashicorp/vault/sdk v0.1.13/go.mod h1:B+hVj7TpuQY1Y/GPbCpffmgd+tSEwvhkWnjtSYCaS2M=
github.com/hashicorp/vault/sdk v0.1.14-0.20200519221530-14615acda45f/go.mod h1:WX57W2PwkrOPQ6rVQk+dy5/htHIaB4aBM70EwKThu10=
github.com/hashicorp/vault/sdk v0.1.14-0.20200519221838-e0cfd64bc267/go.mod h1:WX57W2PwkrOPQ6rVQk+dy5/htHIaB4aBM70EwKThu10=
github.com/hashicorp/vault/sdk v0.2.1 h1:S4O6Iv/dyKlE9AUTXGa7VOvZmsCvg36toPKgV4f2P4M=
github.com/hashicorp/vault/sdk v0.2.1/go.mod h1:WfUiO1vYzfBkz1TmoE4ZGU7HD0T0Cl/rZwaxjBkgN4U=
github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM=
@ -1233,8 +1242,9 @@ github.com/zclconf/go-cty v1.7.0/go.mod h1:VDR4+I79ubFBGm1uJac1226K5yANQFHeauxPB
github.com/zclconf/go-cty v1.8.0/go.mod h1:vVKLxnk3puL4qRAv72AO+W99LUD4da90g3uUAzyuvAk=
github.com/zclconf/go-cty v1.8.1/go.mod h1:vVKLxnk3puL4qRAv72AO+W99LUD4da90g3uUAzyuvAk=
github.com/zclconf/go-cty v1.8.3/go.mod h1:vVKLxnk3puL4qRAv72AO+W99LUD4da90g3uUAzyuvAk=
github.com/zclconf/go-cty v1.9.0 h1:IgJxw5b4LPXCPeqFjjhLaNEA8NKXMyaEUdAd399acts=
github.com/zclconf/go-cty v1.9.0/go.mod h1:vVKLxnk3puL4qRAv72AO+W99LUD4da90g3uUAzyuvAk=
github.com/zclconf/go-cty v1.9.1 h1:viqrgQwFl5UpSxc046qblj78wZXVDFnSOufaOTER+cc=
github.com/zclconf/go-cty v1.9.1/go.mod h1:vVKLxnk3puL4qRAv72AO+W99LUD4da90g3uUAzyuvAk=
github.com/zclconf/go-cty-debug v0.0.0-20191215020915-b22d67c1ba0b/go.mod h1:ZRKQfBXbGkpdV6QMzT3rU1kSTAnfu1dO8dPKjYprgj8=
github.com/zclconf/go-cty-yaml v1.0.1 h1:up11wlgAaDvlAGENcFDnZgkn0qUJurso7k6EpURKNF8=
github.com/zclconf/go-cty-yaml v1.0.1/go.mod h1:IP3Ylp0wQpYm50IHK8OZWKMu6sPJIUgKa8XhiVHura0=

@ -1,606 +0,0 @@
//go:generate packer-sdc mapstructure-to-hcl2 -type Config
// This package implements a provisioner for Packer that executes a
// saltstack state within the remote machine
package saltmasterless
import (
"bytes"
"context"
"errors"
"fmt"
"os"
"path/filepath"
"regexp"
"strings"
"github.com/hashicorp/go-getter/v2"
"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 Config struct {
common.PackerConfig `mapstructure:",squash"`
// If true, run the salt-bootstrap script
SkipBootstrap bool `mapstructure:"skip_bootstrap"`
BootstrapArgs string `mapstructure:"bootstrap_args"`
DisableSudo bool `mapstructure:"disable_sudo"`
// Custom state to run instead of highstate
CustomState string `mapstructure:"custom_state"`
// Local path to the minion config
MinionConfig string `mapstructure:"minion_config"`
// Local path to the minion grains
GrainsFile string `mapstructure:"grains_file"`
// Local path to the salt state tree
LocalStateTree string `mapstructure:"local_state_tree"`
// Local path to the salt pillar roots
LocalPillarRoots string `mapstructure:"local_pillar_roots"`
// Remote path to the salt state tree
RemoteStateTree string `mapstructure:"remote_state_tree"`
// Remote path to the salt pillar roots
RemotePillarRoots string `mapstructure:"remote_pillar_roots"`
// Where files will be copied before moving to the /srv/salt directory
TempConfigDir string `mapstructure:"temp_config_dir"`
// Don't exit packer if salt-call returns an error code
NoExitOnFailure bool `mapstructure:"no_exit_on_failure"`
// Set the logging level for the salt-call run
LogLevel string `mapstructure:"log_level"`
// Arguments to pass to salt-call
SaltCallArgs string `mapstructure:"salt_call_args"`
// Directory containing salt-call
SaltBinDir string `mapstructure:"salt_bin_dir"`
// Command line args passed onto salt-call
CmdArgs string `mapstructure-to-hcl2:",skip"`
// The Guest OS Type (unix or windows)
GuestOSType string `mapstructure:"guest_os_type"`
// An array of private or community git source formulas
Formulas []string `mapstructure:"formulas"`
ctx interpolate.Context
}
type Provisioner struct {
config Config
guestOSTypeConfig guestOSTypeConfig
guestCommands *guestexec.GuestCommands
}
type guestOSTypeConfig struct {
tempDir string
stateRoot string
pillarRoot string
configDir string
bootstrapFetchCmd string
bootstrapRunCmd string
}
var guestOSTypeConfigs = map[string]guestOSTypeConfig{
guestexec.UnixOSType: {
configDir: "/etc/salt",
tempDir: "/tmp/salt",
stateRoot: "/srv/salt",
pillarRoot: "/srv/pillar",
bootstrapFetchCmd: "curl -L https://bootstrap.saltproject.io -o /tmp/install_salt.sh || wget -O /tmp/install_salt.sh https://bootstrap.saltproject.io",
bootstrapRunCmd: "sh /tmp/install_salt.sh",
},
guestexec.WindowsOSType: {
configDir: "C:/salt/conf",
tempDir: "C:/Windows/Temp/salt/",
stateRoot: "C:/salt/state",
pillarRoot: "C:/salt/pillar/",
bootstrapFetchCmd: "powershell -Command \"[System.Net.ServicePointManager]::SecurityProtocol = [System.Net.SecurityProtocolType]'Tls,Tls11,Tls12'; Invoke-WebRequest -Uri 'https://winbootstrap.saltproject.io' -OutFile 'C:/Windows/Temp/bootstrap-salt.ps1'\"",
bootstrapRunCmd: "Powershell C:/Windows/Temp/bootstrap-salt.ps1",
},
}
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: "salt-masterless",
Interpolate: true,
InterpolateContext: &p.config.ctx,
InterpolateFilter: &interpolate.RenderFilter{
Exclude: []string{},
},
}, raws...)
if err != nil {
return err
}
if p.config.GuestOSType == "" {
p.config.GuestOSType = guestexec.DefaultOSType
} else {
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.DisableSudo)
if err != nil {
return fmt.Errorf("Invalid guest_os_type: \"%s\"", p.config.GuestOSType)
}
if p.config.TempConfigDir == "" {
p.config.TempConfigDir = p.guestOSTypeConfig.tempDir
}
var errs *packersdk.MultiError
// require a salt state tree
err = validateDirConfig(p.config.LocalStateTree, "local_state_tree", true)
if err != nil {
errs = packersdk.MultiErrorAppend(errs, err)
}
if p.config.Formulas != nil && len(p.config.Formulas) > 0 {
validURLs := hasValidFormulaURLs(p.config.Formulas)
if !validURLs {
errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("Invalid formula URL. Please verify the git URLs also contain a '//' subdir"))
}
}
err = validateDirConfig(p.config.LocalPillarRoots, "local_pillar_roots", false)
if err != nil {
errs = packersdk.MultiErrorAppend(errs, err)
}
err = validateFileConfig(p.config.MinionConfig, "minion_config", false)
if err != nil {
errs = packersdk.MultiErrorAppend(errs, err)
}
if p.config.MinionConfig != "" && (p.config.RemoteStateTree != "" || p.config.RemotePillarRoots != "") {
errs = packersdk.MultiErrorAppend(errs,
errors.New("remote_state_tree and remote_pillar_roots only apply when minion_config is not used"))
}
err = validateFileConfig(p.config.GrainsFile, "grains_file", false)
if err != nil {
errs = packersdk.MultiErrorAppend(errs, err)
}
// build the command line args to pass onto salt
var cmd_args bytes.Buffer
if p.config.CustomState == "" {
cmd_args.WriteString(" state.highstate")
} else {
cmd_args.WriteString(" state.sls ")
cmd_args.WriteString(p.config.CustomState)
}
if p.config.MinionConfig == "" {
// pass --file-root and --pillar-root if no minion_config is supplied
if p.config.RemoteStateTree != "" {
cmd_args.WriteString(" --file-root=")
cmd_args.WriteString(p.config.RemoteStateTree)
} else {
cmd_args.WriteString(" --file-root=")
cmd_args.WriteString(p.guestOSTypeConfig.stateRoot)
}
if p.config.RemotePillarRoots != "" {
cmd_args.WriteString(" --pillar-root=")
cmd_args.WriteString(p.config.RemotePillarRoots)
} else {
cmd_args.WriteString(" --pillar-root=")
cmd_args.WriteString(p.guestOSTypeConfig.pillarRoot)
}
}
if !p.config.NoExitOnFailure {
cmd_args.WriteString(" --retcode-passthrough")
}
if p.config.LogLevel == "" {
cmd_args.WriteString(" -l info")
} else {
cmd_args.WriteString(" -l ")
cmd_args.WriteString(p.config.LogLevel)
}
if p.config.SaltCallArgs != "" {
cmd_args.WriteString(" ")
cmd_args.WriteString(p.config.SaltCallArgs)
}
p.config.CmdArgs = cmd_args.String()
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 {
var err error
var src, dst string
var formulas []string
if p.config.Formulas != nil && len(p.config.Formulas) > 0 {
ui.Say("Downloading Salt formulas...")
client := new(getter.Client)
for _, i := range p.config.Formulas {
req := getter.Request{
Src: i,
}
// Use //subdirectory name when creating in local_state_tree directory
state := strings.Split(i, "//")
last := state[len(state)-1]
path := filepath.Join(p.config.LocalStateTree, last)
formulas = append(formulas, path)
if _, err := os.Stat(path); os.IsNotExist(err) {
ui.Message(fmt.Sprintf("%s => %s", i, path))
if err = os.Mkdir(path, 0755); err != nil {
return fmt.Errorf("Unable to create Salt state directory: %s", err)
}
req.Dst = path
req.GetMode = getter.ModeAny
if _, err := client.Get(ctx, &req); err != nil {
return fmt.Errorf("Unable to download Salt formula from %s: %s", i, err)
}
} else {
ui.Message(fmt.Sprintf("Found existing formula at: %s", path))
}
}
}
ui.Say("Provisioning with Salt...")
if !p.config.SkipBootstrap {
cmd := &packersdk.RemoteCmd{
// Fallback on wget if curl failed for any reason (such as not being installed)
Command: fmt.Sprintf(p.guestOSTypeConfig.bootstrapFetchCmd),
}
ui.Message(fmt.Sprintf("Downloading saltstack bootstrap to /tmp/install_salt.sh"))
if err = cmd.RunWithUi(ctx, comm, ui); err != nil {
return fmt.Errorf("Unable to download Salt: %s", err)
}
cmd = &packersdk.RemoteCmd{
Command: fmt.Sprintf("%s %s", p.sudo(p.guestOSTypeConfig.bootstrapRunCmd), p.config.BootstrapArgs),
}
ui.Message(fmt.Sprintf("Installing Salt with command %s", cmd.Command))
if err = cmd.RunWithUi(ctx, comm, ui); err != nil {
return fmt.Errorf("Unable to install Salt: %s", err)
}
}
ui.Message(fmt.Sprintf("Creating remote temporary directory: %s", p.config.TempConfigDir))
if err := p.createDir(ui, comm, p.config.TempConfigDir); err != nil {
return fmt.Errorf("Error creating remote temporary directory: %s", err)
}
if p.config.MinionConfig != "" {
ui.Message(fmt.Sprintf("Uploading minion config: %s", p.config.MinionConfig))
src = p.config.MinionConfig
dst = filepath.ToSlash(filepath.Join(p.config.TempConfigDir, "minion"))
if err = p.uploadFile(ui, comm, dst, src); err != nil {
return fmt.Errorf("Error uploading local minion config file to remote: %s", err)
}
// move minion config into /etc/salt
ui.Message(fmt.Sprintf("Make sure directory %s exists", p.guestOSTypeConfig.configDir))
if err := p.createDir(ui, comm, p.guestOSTypeConfig.configDir); err != nil {
return fmt.Errorf("Error creating remote salt configuration directory: %s", err)
}
src = filepath.ToSlash(filepath.Join(p.config.TempConfigDir, "minion"))
dst = filepath.ToSlash(filepath.Join(p.guestOSTypeConfig.configDir, "minion"))
if err = p.moveFile(ui, comm, dst, src); err != nil {
return fmt.Errorf("Unable to move %s/minion to %s/minion: %s", p.config.TempConfigDir, p.guestOSTypeConfig.configDir, err)
}
}
if p.config.GrainsFile != "" {
ui.Message(fmt.Sprintf("Uploading grains file: %s", p.config.GrainsFile))
src = p.config.GrainsFile
dst = filepath.ToSlash(filepath.Join(p.config.TempConfigDir, "grains"))
if err = p.uploadFile(ui, comm, dst, src); err != nil {
return fmt.Errorf("Error uploading local grains file to remote: %s", err)
}
// move grains file into /etc/salt
ui.Message(fmt.Sprintf("Make sure directory %s exists", p.guestOSTypeConfig.configDir))
if err := p.createDir(ui, comm, p.guestOSTypeConfig.configDir); err != nil {
return fmt.Errorf("Error creating remote salt configuration directory: %s", err)
}
src = filepath.ToSlash(filepath.Join(p.config.TempConfigDir, "grains"))
dst = filepath.ToSlash(filepath.Join(p.guestOSTypeConfig.configDir, "grains"))
if err = p.moveFile(ui, comm, dst, src); err != nil {
return fmt.Errorf("Unable to move %s/grains to %s/grains: %s", p.config.TempConfigDir, p.guestOSTypeConfig.configDir, err)
}
}
ui.Message(fmt.Sprintf("Uploading local state tree: %s", p.config.LocalStateTree))
src = p.config.LocalStateTree
dst = filepath.ToSlash(filepath.Join(p.config.TempConfigDir, "states"))
if err = p.uploadDir(ui, comm, dst, src, []string{".git"}); err != nil {
return fmt.Errorf("Error uploading local state tree to remote: %s", err)
}
// move state tree from temporary directory
src = filepath.ToSlash(filepath.Join(p.config.TempConfigDir, "states"))
if p.config.RemoteStateTree != "" {
dst = p.config.RemoteStateTree
} else {
dst = p.guestOSTypeConfig.stateRoot
}
if err = p.statPath(ui, comm, dst); err != nil {
if err = p.removeDir(ui, comm, dst); err != nil {
return fmt.Errorf("Unable to clear salt tree: %s", err)
}
}
if err = p.moveFile(ui, comm, dst, src); err != nil {
return fmt.Errorf("Unable to move %s/states to %s: %s", p.config.TempConfigDir, dst, err)
}
// Remove the local Salt formulas if present
if p.config.Formulas != nil {
for _, f := range formulas {
if _, err := os.Stat(f); !os.IsNotExist(err) && f != p.config.LocalStateTree {
ui.Message(fmt.Sprintf("Removing Salt formula: %s", f))
defer os.RemoveAll(f)
}
}
}
if p.config.LocalPillarRoots != "" {
ui.Message(fmt.Sprintf("Uploading local pillar roots: %s", p.config.LocalPillarRoots))
src = p.config.LocalPillarRoots
dst = filepath.ToSlash(filepath.Join(p.config.TempConfigDir, "pillar"))
if err = p.uploadDir(ui, comm, dst, src, []string{".git"}); err != nil {
return fmt.Errorf("Error uploading local pillar roots to remote: %s", err)
}
// move pillar root from temporary directory
src = filepath.ToSlash(filepath.Join(p.config.TempConfigDir, "pillar"))
if p.config.RemotePillarRoots != "" {
dst = p.config.RemotePillarRoots
} else {
dst = p.guestOSTypeConfig.pillarRoot
}
if err = p.statPath(ui, comm, dst); err != nil {
if err = p.removeDir(ui, comm, dst); err != nil {
return fmt.Errorf("Unable to clear pillar root: %s", err)
}
}
if err = p.moveFile(ui, comm, dst, src); err != nil {
return fmt.Errorf("Unable to move %s/pillar to %s: %s", p.config.TempConfigDir, dst, err)
}
}
if p.config.GuestOSType == guestexec.WindowsOSType {
ui.Message("Downloading Git for Windows")
cmd1 := &packersdk.RemoteCmd{Command: fmt.Sprintf("powershell [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; Invoke-WebRequest -Uri https://github.com/git-for-windows/git/releases/download/v2.28.0.windows.1/Git-2.28.0-64-bit.exe -OutFile $env:TEMP/Git.exe")}
if err = cmd1.RunWithUi(ctx, comm, ui); (err != nil || cmd1.ExitStatus() != 0) && !p.config.NoExitOnFailure {
if err == nil {
err = fmt.Errorf("Bad exit status: %d", cmd1.ExitStatus())
}
return fmt.Errorf("Unable to Download Git for Windows: %s", err)
}
ui.Message("Installing Git for Windows")
cmd2 := &packersdk.RemoteCmd{Command: fmt.Sprintf("powershell Start-Process -FilePath $env:TEMP/Git.exe /SILENT -Wait")}
if err = cmd2.RunWithUi(ctx, comm, ui); (err != nil || cmd2.ExitStatus() != 0) && !p.config.NoExitOnFailure {
if err == nil {
err = fmt.Errorf("Bad exit status: %d", cmd2.ExitStatus())
}
return fmt.Errorf("Unable to Install Git for Windows: %s", err)
}
ui.Message("Cleaning Up After Git for Windows")
cmd3 := &packersdk.RemoteCmd{Command: fmt.Sprintf("powershell Remove-Item $env:TEMP/Git.exe")}
if err = cmd3.RunWithUi(ctx, comm, ui); (err != nil || cmd3.ExitStatus() != 0) && !p.config.NoExitOnFailure {
if err == nil {
err = fmt.Errorf("Bad exit status: %d", cmd3.ExitStatus())
}
return fmt.Errorf("Unable to Clean-up After Git for Windows: %s", err)
}
ui.Message("Running salt-call --local winrepo.update_git_repos")
cmd4 := &packersdk.RemoteCmd{Command: fmt.Sprintf("%s --local winrepo.update_git_repos", filepath.Join(p.config.SaltBinDir, "salt-call"))}
if err = cmd4.RunWithUi(ctx, comm, ui); (err != nil || cmd4.ExitStatus() != 0) && !p.config.NoExitOnFailure {
if err == nil {
err = fmt.Errorf("Bad exit status: %d", cmd4.ExitStatus())
}
return fmt.Errorf("Error executing salt-call --local winrepo.update_git_repos: %s", err)
}
ui.Message("Running salt-call --local pkg.refresh_db")
cmd5 := &packersdk.RemoteCmd{Command: fmt.Sprintf("%s --local pkg.refresh_db", filepath.Join(p.config.SaltBinDir, "salt-call"))}
if err = cmd5.RunWithUi(ctx, comm, ui); (err != nil || cmd5.ExitStatus() != 0) && !p.config.NoExitOnFailure {
if err == nil {
err = fmt.Errorf("Bad exit status: %d", cmd5.ExitStatus())
}
return fmt.Errorf("Error executing salt-call --local pkg.refresh_db: %s", err)
}
}
ui.Message(fmt.Sprintf("Running: salt-call --local %s", p.config.CmdArgs))
cmd := &packersdk.RemoteCmd{Command: p.sudo(fmt.Sprintf("%s --local %s", filepath.Join(p.config.SaltBinDir, "salt-call"), p.config.CmdArgs))}
if err = cmd.RunWithUi(ctx, comm, ui); (err != nil || cmd.ExitStatus() != 0) && !p.config.NoExitOnFailure {
if err == nil {
err = fmt.Errorf("Bad exit status: %d", cmd.ExitStatus())
}
return fmt.Errorf("Error executing salt-call: %s", err)
}
return nil
}
// Prepends sudo to supplied command if config says to
func (p *Provisioner) sudo(cmd string) string {
if p.config.DisableSudo || (p.config.GuestOSType == guestexec.WindowsOSType) {
return cmd
}
return "sudo " + cmd
}
func validateDirConfig(path string, name string, required bool) error {
if required && path == "" {
return fmt.Errorf("%s cannot be empty", name)
} else if required == false && path == "" {
return nil
}
info, err := os.Stat(path)
if err != nil {
return fmt.Errorf("%s: path '%s' is invalid: %s", name, path, err)
} else if !info.IsDir() {
return fmt.Errorf("%s: path '%s' must point to a directory", name, path)
}
return nil
}
func validateFileConfig(path string, name string, required bool) error {
if required == true && path == "" {
return fmt.Errorf("%s cannot be empty", name)
} else if required == false && path == "" {
return nil
}
info, err := os.Stat(path)
if err != nil {
return fmt.Errorf("%s: path '%s' is invalid: %s", name, path, err)
} else if info.IsDir() {
return fmt.Errorf("%s: path '%s' must point to a file", name, path)
}
return nil
}
func hasValidFormulaURLs(s []string) bool {
re := regexp.MustCompile(`^(.*).git\/\/[a-zA-Z0-9-_]+(\?.*)?$`)
for _, u := range s {
if !re.MatchString(u) {
return false
}
}
return true
}
func (p *Provisioner) uploadFile(ui packersdk.Ui, comm packersdk.Communicator, dst, src string) error {
f, err := os.Open(src)
if err != nil {
return fmt.Errorf("Error opening: %s", err)
}
defer f.Close()
_, temp_dst := filepath.Split(dst)
if err = comm.Upload(temp_dst, f, nil); err != nil {
return fmt.Errorf("Error uploading %s: %s", src, err)
}
err = p.moveFile(ui, comm, dst, temp_dst)
if err != nil {
return fmt.Errorf("Error moving file to destination: %s", err)
}
return nil
}
func (p *Provisioner) moveFile(ui packersdk.Ui, comm packersdk.Communicator, dst string, src string) error {
ctx := context.TODO()
ui.Message(fmt.Sprintf("Moving %s to %s", src, dst))
cmd := &packersdk.RemoteCmd{
Command: p.sudo(p.guestCommands.MovePath(src, dst)),
}
if err := cmd.RunWithUi(ctx, comm, ui); err != nil || cmd.ExitStatus() != 0 {
if err == nil {
err = fmt.Errorf("Bad exit status: %d", cmd.ExitStatus())
}
return fmt.Errorf("Unable to move %s to %s: %s", src, dst, err)
}
return nil
}
func (p *Provisioner) createDir(ui packersdk.Ui, comm packersdk.Communicator, dir string) error {
ui.Message(fmt.Sprintf("Creating directory: %s", dir))
cmd := &packersdk.RemoteCmd{
Command: p.guestCommands.CreateDir(dir),
}
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.")
}
return nil
}
func (p *Provisioner) statPath(ui packersdk.Ui, comm packersdk.Communicator, path string) error {
ctx := context.TODO()
ui.Message(fmt.Sprintf("Verifying Path: %s", path))
cmd := &packersdk.RemoteCmd{
Command: p.guestCommands.StatPath(path),
}
if err := cmd.RunWithUi(ctx, comm, ui); err != nil {
return err
}
if cmd.ExitStatus() != 0 {
return fmt.Errorf("Non-zero exit status.")
}
return nil
}
func (p *Provisioner) removeDir(ui packersdk.Ui, comm packersdk.Communicator, dir string) error {
ctx := context.TODO()
ui.Message(fmt.Sprintf("Removing directory: %s", dir))
cmd := &packersdk.RemoteCmd{
Command: p.guestCommands.RemoveDir(dir),
}
if err := cmd.RunWithUi(ctx, comm, ui); err != nil {
return err
}
if cmd.ExitStatus() != 0 {
return fmt.Errorf("Non-zero exit status.")
}
return nil
}
func (p *Provisioner) uploadDir(ui packersdk.Ui, comm packersdk.Communicator, dst, src string, ignore []string) error {
_, temp_dst := filepath.Split(dst)
if err := comm.UploadDir(temp_dst, src, ignore); err != nil {
return err
}
return p.moveFile(ui, comm, dst, temp_dst)
}

@ -1,79 +0,0 @@
// Code generated by "packer-sdc mapstructure-to-hcl2"; DO NOT EDIT.
package saltmasterless
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"`
SkipBootstrap *bool `mapstructure:"skip_bootstrap" cty:"skip_bootstrap" hcl:"skip_bootstrap"`
BootstrapArgs *string `mapstructure:"bootstrap_args" cty:"bootstrap_args" hcl:"bootstrap_args"`
DisableSudo *bool `mapstructure:"disable_sudo" cty:"disable_sudo" hcl:"disable_sudo"`
CustomState *string `mapstructure:"custom_state" cty:"custom_state" hcl:"custom_state"`
MinionConfig *string `mapstructure:"minion_config" cty:"minion_config" hcl:"minion_config"`
GrainsFile *string `mapstructure:"grains_file" cty:"grains_file" hcl:"grains_file"`
LocalStateTree *string `mapstructure:"local_state_tree" cty:"local_state_tree" hcl:"local_state_tree"`
LocalPillarRoots *string `mapstructure:"local_pillar_roots" cty:"local_pillar_roots" hcl:"local_pillar_roots"`
RemoteStateTree *string `mapstructure:"remote_state_tree" cty:"remote_state_tree" hcl:"remote_state_tree"`
RemotePillarRoots *string `mapstructure:"remote_pillar_roots" cty:"remote_pillar_roots" hcl:"remote_pillar_roots"`
TempConfigDir *string `mapstructure:"temp_config_dir" cty:"temp_config_dir" hcl:"temp_config_dir"`
NoExitOnFailure *bool `mapstructure:"no_exit_on_failure" cty:"no_exit_on_failure" hcl:"no_exit_on_failure"`
LogLevel *string `mapstructure:"log_level" cty:"log_level" hcl:"log_level"`
SaltCallArgs *string `mapstructure:"salt_call_args" cty:"salt_call_args" hcl:"salt_call_args"`
SaltBinDir *string `mapstructure:"salt_bin_dir" cty:"salt_bin_dir" hcl:"salt_bin_dir"`
GuestOSType *string `mapstructure:"guest_os_type" cty:"guest_os_type" hcl:"guest_os_type"`
Formulas []string `mapstructure:"formulas" cty:"formulas" hcl:"formulas"`
}
// 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},
"skip_bootstrap": &hcldec.AttrSpec{Name: "skip_bootstrap", Type: cty.Bool, Required: false},
"bootstrap_args": &hcldec.AttrSpec{Name: "bootstrap_args", Type: cty.String, Required: false},
"disable_sudo": &hcldec.AttrSpec{Name: "disable_sudo", Type: cty.Bool, Required: false},
"custom_state": &hcldec.AttrSpec{Name: "custom_state", Type: cty.String, Required: false},
"minion_config": &hcldec.AttrSpec{Name: "minion_config", Type: cty.String, Required: false},
"grains_file": &hcldec.AttrSpec{Name: "grains_file", Type: cty.String, Required: false},
"local_state_tree": &hcldec.AttrSpec{Name: "local_state_tree", Type: cty.String, Required: false},
"local_pillar_roots": &hcldec.AttrSpec{Name: "local_pillar_roots", Type: cty.String, Required: false},
"remote_state_tree": &hcldec.AttrSpec{Name: "remote_state_tree", Type: cty.String, Required: false},
"remote_pillar_roots": &hcldec.AttrSpec{Name: "remote_pillar_roots", Type: cty.String, Required: false},
"temp_config_dir": &hcldec.AttrSpec{Name: "temp_config_dir", Type: cty.String, Required: false},
"no_exit_on_failure": &hcldec.AttrSpec{Name: "no_exit_on_failure", Type: cty.Bool, Required: false},
"log_level": &hcldec.AttrSpec{Name: "log_level", Type: cty.String, Required: false},
"salt_call_args": &hcldec.AttrSpec{Name: "salt_call_args", Type: cty.String, Required: false},
"salt_bin_dir": &hcldec.AttrSpec{Name: "salt_bin_dir", Type: cty.String, Required: false},
"guest_os_type": &hcldec.AttrSpec{Name: "guest_os_type", Type: cty.String, Required: false},
"formulas": &hcldec.AttrSpec{Name: "formulas", Type: cty.List(cty.String), Required: false},
}
return s
}

@ -1,358 +0,0 @@
package saltmasterless
import (
"io/ioutil"
"os"
"strings"
"testing"
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
)
func testConfig() map[string]interface{} {
return map[string]interface{}{
"local_state_tree": os.TempDir(),
}
}
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_Defaults(t *testing.T) {
var p Provisioner
config := testConfig()
err := p.Prepare(config)
if err != nil {
t.Fatalf("err: %s", err)
}
if p.config.TempConfigDir != p.guestOSTypeConfig.tempDir {
t.Errorf("unexpected temp config dir: %s", p.config.TempConfigDir)
}
}
func TestProvisionerPrepare_InvalidKey(t *testing.T) {
var p Provisioner
config := testConfig()
// Add a random key
config["i_should_not_be_valid"] = true
err := p.Prepare(config)
if err == nil {
t.Fatal("should have error")
}
}
func TestProvisionerPrepare_CustomState(t *testing.T) {
var p Provisioner
config := testConfig()
err := p.Prepare(config)
if err != nil {
t.Fatalf("err: %s", err)
}
if !strings.Contains(p.config.CmdArgs, "state.") {
t.Fatal("a state should be specified in CmdArgs")
}
config["custom_state"] = "birb"
err = p.Prepare(config)
if err != nil {
t.Fatalf("err: %s", err)
}
if !strings.Contains(p.config.CmdArgs, "state.sls birb") {
t.Fatal("birb state should be specified in CmdArgs")
}
}
func TestProvisionerPrepare_MinionConfig(t *testing.T) {
var p Provisioner
config := testConfig()
config["minion_config"] = "/i/dont/exist/i/think"
err := p.Prepare(config)
if err == nil {
t.Fatal("should have error")
}
tf, err := ioutil.TempFile("", "minion")
if err != nil {
t.Fatalf("error tempfile: %s", err)
}
defer os.Remove(tf.Name())
config["minion_config"] = tf.Name()
err = p.Prepare(config)
if err != nil {
t.Fatalf("err: %s", err)
}
}
func TestProvisionerPrepare_MinionConfig_RemoteStateTree(t *testing.T) {
var p Provisioner
config := testConfig()
config["minion_config"] = "/i/dont/exist/i/think"
config["remote_state_tree"] = "/i/dont/exist/remote_state_tree"
err := p.Prepare(config)
if err == nil {
t.Fatal("minion_config and remote_state_tree should cause error")
}
}
func TestProvisionerPrepare_MinionConfig_RemotePillarRoots(t *testing.T) {
var p Provisioner
config := testConfig()
config["minion_config"] = "/i/dont/exist/i/think"
config["remote_pillar_roots"] = "/i/dont/exist/remote_pillar_roots"
err := p.Prepare(config)
if err == nil {
t.Fatal("minion_config and remote_pillar_roots should cause error")
}
}
func TestProvisionerPrepare_GrainsFile(t *testing.T) {
var p Provisioner
config := testConfig()
config["grains_file"] = "/i/dont/exist/i/think"
err := p.Prepare(config)
if err == nil {
t.Fatal("should have error")
}
tf, err := ioutil.TempFile("", "grains")
if err != nil {
t.Fatalf("error tempfile: %s", err)
}
defer os.Remove(tf.Name())
config["grains_file"] = tf.Name()
err = p.Prepare(config)
if err != nil {
t.Fatalf("err: %s", err)
}
}
func TestProvisionerPrepare_LocalStateTree(t *testing.T) {
var p Provisioner
config := testConfig()
config["local_state_tree"] = "/i/dont/exist/i/think"
err := p.Prepare(config)
if err == nil {
t.Fatal("should have error")
}
config["local_state_tree"] = os.TempDir()
err = p.Prepare(config)
if err != nil {
t.Fatalf("err: %s", err)
}
}
func TestProvisionerPrepare_LocalPillarRoots(t *testing.T) {
var p Provisioner
config := testConfig()
config["local_pillar_roots"] = "/i/dont/exist/i/think"
err := p.Prepare(config)
if err == nil {
t.Fatal("should have error")
}
config["local_pillar_roots"] = os.TempDir()
err = p.Prepare(config)
if err != nil {
t.Fatalf("err: %s", err)
}
}
func TestProvisionerSudo(t *testing.T) {
var p Provisioner
config := testConfig()
err := p.Prepare(config)
if err != nil {
t.Fatalf("err: %s", err)
}
withSudo := p.sudo("echo hello")
if withSudo != "sudo echo hello" {
t.Fatalf("sudo command not generated correctly")
}
config["disable_sudo"] = true
err = p.Prepare(config)
if err != nil {
t.Fatalf("err: %s", err)
}
withoutSudo := p.sudo("echo hello")
if withoutSudo != "echo hello" {
t.Fatalf("sudo-less command not generated correctly")
}
}
func TestProvisionerPrepare_RemoteStateTree(t *testing.T) {
var p Provisioner
config := testConfig()
config["remote_state_tree"] = "/remote_state_tree"
err := p.Prepare(config)
if err != nil {
t.Fatalf("err: %s", err)
}
if !strings.Contains(p.config.CmdArgs, "--file-root=/remote_state_tree") {
t.Fatal("--file-root should be set in CmdArgs")
}
}
func TestProvisionerPrepare_RemotePillarRoots(t *testing.T) {
var p Provisioner
config := testConfig()
config["remote_pillar_roots"] = "/remote_pillar_roots"
err := p.Prepare(config)
if err != nil {
t.Fatalf("err: %s", err)
}
if !strings.Contains(p.config.CmdArgs, "--pillar-root=/remote_pillar_roots") {
t.Fatal("--pillar-root should be set in CmdArgs")
}
}
func TestProvisionerPrepare_RemoteStateTree_Default(t *testing.T) {
var p Provisioner
config := testConfig()
// no minion_config, no remote_state_tree
err := p.Prepare(config)
if err != nil {
t.Fatalf("err: %s", err)
}
if !strings.Contains(p.config.CmdArgs, "--file-root=/srv/salt") {
t.Fatal("--file-root should be set in CmdArgs")
}
}
func TestProvisionerPrepare_RemotePillarRoots_Default(t *testing.T) {
var p Provisioner
config := testConfig()
// no minion_config, no remote_pillar_roots
err := p.Prepare(config)
if err != nil {
t.Fatalf("err: %s", err)
}
if !strings.Contains(p.config.CmdArgs, "--pillar-root=/srv/pillar") {
t.Fatal("--pillar-root should be set in CmdArgs")
}
}
func TestProvisionerPrepare_NoExitOnFailure(t *testing.T) {
var p Provisioner
config := testConfig()
err := p.Prepare(config)
if err != nil {
t.Fatalf("err: %s", err)
}
if !strings.Contains(p.config.CmdArgs, "--retcode-passthrough") {
t.Fatal("--retcode-passthrough should be set in CmdArgs")
}
config["no_exit_on_failure"] = true
err = p.Prepare(config)
if err != nil {
t.Fatalf("err: %s", err)
}
if strings.Contains(p.config.CmdArgs, "--retcode-passthrough") {
t.Fatal("--retcode-passthrough should not be set in CmdArgs")
}
}
func TestProvisionerPrepare_LogLevel(t *testing.T) {
var p Provisioner
config := testConfig()
err := p.Prepare(config)
if err != nil {
t.Fatalf("err: %s", err)
}
if !strings.Contains(p.config.CmdArgs, "-l info") {
t.Fatal("-l info should be set in CmdArgs")
}
config["log_level"] = "debug"
err = p.Prepare(config)
if err != nil {
t.Fatalf("err: %s", err)
}
if !strings.Contains(p.config.CmdArgs, "-l debug") {
t.Fatal("-l debug should be set in CmdArgs")
}
}
func TestProvisionerPrepare_GuestOSType(t *testing.T) {
var p Provisioner
config := testConfig()
config["guest_os_type"] = "Windows"
err := p.Prepare(config)
if err != nil {
t.Fatalf("err: %s", err)
}
if p.config.GuestOSType != "windows" {
t.Fatalf("GuestOSType should be 'windows'")
}
}
func TestProvisionerPrepare_BadFormulaURL(t *testing.T) {
var p Provisioner
config := testConfig()
config["formulas"] = []string{
"git::https://github.com/org/some-formula.git//",
}
err := p.Prepare(config)
if err == nil {
t.Fatalf("Expected invalid formula URL: %s", err)
}
}
func TestProvisionerPrepare_ValidFormulaURLs(t *testing.T) {
var p Provisioner
config := testConfig()
config["formulas"] = []string{
"git::https://github.com/org/some-formula.git//example",
"git@github.com:org/some-formula.git//example",
"git::https://github.com/org/some-formula.git//example?ref=example",
}
err := p.Prepare(config)
if err != nil {
t.Fatalf("Unexpected error in formula URLs: %s", err)
}
}

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

@ -1,121 +0,0 @@
---
description: |
The salt-masterless Packer provisioner provisions machines built by Packer
using Salt states, without connecting to a Salt master.
page_title: Salt Masterless - Provisioners
---
# Salt Masterless Provisioner
@include 'provisioners/unmaintained-plugin.mdx'
Type: `salt-masterless`
The `salt-masterless` Packer provisioner provisions machines built by Packer
using [Salt](https://saltproject.io/) states, without connecting to a Salt
master.
## Basic Example
The example below is fully functional.
<Tabs>
<Tab heading="HCL2">
```hcl
provisioner "salt-masterless" {
local_state_tree = "/Users/me/salt"
}
```
</Tab>
<Tab heading="JSON">
```json
{
"type": "salt-masterless",
"local_state_tree": "/Users/me/salt"
}
```
</Tab>
</Tabs>
## Configuration Reference
The reference of available configuration options is listed below. The only
required element is "local_state_tree".
Required:
- `local_state_tree` (string) - The path to your local [state
tree](http://docs.saltproject.io/en/latest/ref/states/highstate.html#the-salt-state-tree).
This will be uploaded to the `remote_state_tree` on the remote.
Optional:
- `bootstrap_args` (string) - Arguments to send to the bootstrap script.
Usage is somewhat documented on
[github](https://github.com/saltstack/salt-bootstrap), but the [script
itself](https://github.com/saltstack/salt-bootstrap/blob/develop/bootstrap-salt.sh)
has more detailed usage instructions. By default, no arguments are sent to
the script.
- `disable_sudo` (boolean) - By default, the bootstrap install command is
prefixed with `sudo`. When using a Docker builder, you will likely want to
pass `true` since `sudo` is often not pre-installed.
- `remote_pillar_roots` (string) - The path to your remote [pillar
roots](http://docs.saltproject.io/en/latest/ref/configuration/master.html#pillar-configuration).
default: `/srv/pillar`. This option cannot be used with `minion_config`.
- `remote_state_tree` (string) - The path to your remote [state
tree](http://docs.saltproject.io/en/latest/ref/states/highstate.html#the-salt-state-tree).
default: `/srv/salt`. This option cannot be used with `minion_config`.
- `local_pillar_roots` (string) - The path to your local [pillar
roots](http://docs.saltproject.io/en/latest/ref/configuration/master.html#pillar-configuration).
This will be uploaded to the `remote_pillar_roots` on the remote.
- `custom_state` (string) - A state to be run instead of `state.highstate`.
Defaults to `state.highstate` if unspecified.
- `minion_config` (string) - The path to your local [minion config
file](http://docs.saltproject.io/en/latest/ref/configuration/minion.html). This will
be uploaded to the `/etc/salt` on the remote. This option overrides the
`remote_state_tree` or `remote_pillar_roots` options.
- `grains_file` (string) - The path to your local [grains
file](https://docs.saltproject.io/en/latest/topics/grains). This will be
uploaded to `/etc/salt/grains` on the remote.
- `skip_bootstrap` (boolean) - By default the salt provisioner runs [salt
bootstrap](https://github.com/saltstack/salt-bootstrap) to install salt.
Set this to true to skip this step.
- `temp_config_dir` (string) - Where your local state tree will be copied
before moving to the `/srv/salt` directory. Default is `/tmp/salt`.
- `no_exit_on_failure` (boolean) - Packer will exit if the `salt-call`
command fails. Set this option to true to ignore Salt failures.
- `log_level` (string) - Set the logging level for the `salt-call` run.
- `salt_call_args` (string) - Additional arguments to pass directly to
`salt-call`. See
[salt-call](https://docs.saltproject.io/en/latest/ref/cli/salt-call.html)
documentation for more information. By default no additional arguments
(besides the ones Packer generates) are passed to `salt-call`.
- `salt_bin_dir` (string) - Path to the `salt-call` executable. Useful if it
is not on the PATH.
- `guest_os_type` (string) - The target guest OS type, either "unix" or
"windows".
- `formulas` (array of strings) - An array of git source formulas to be downloaded to the local
state tree prior to moving to the remote state tree. Note: `//directory` must be included in
the URL to download the appropriate formula directory. Example:
`git::https://github.com/saltstack-formulas/vault-formula.git//vault?ref=v1.2.3`
@include 'provisioners/common-config.mdx'

@ -737,10 +737,6 @@
"title": "PowerShell",
"path": "provisioners/powershell"
},
{
"title": "Salt Masterless",
"path": "provisioners/salt-masterless"
},
{
"title": "Shell",
"path": "provisioners/shell"

@ -202,6 +202,13 @@
"repo": "hashicorp/packer-plugin-qemu",
"version": "latest"
},
{
"title": "Salt",
"path": "salt",
"repo": "hashicorp/packer-plugin-salt",
"pluginTier": "community",
"version": "latest"
},
{
"title": "Scaleway",
"path": "scaleway",

Loading…
Cancel
Save