Better Windows support in puppet-server

Reworking the puppet-server provisioner based on chef-client.
This commit is contained in:
c22 2017-01-13 11:45:18 +11:00
parent 0ab790eb1a
commit 1c592f291e
2 changed files with 90 additions and 44 deletions

View File

@ -1,4 +1,4 @@
// This package implements a provisioner for Packer that executes
// Package puppetserver implements a provisioner for Packer that executes
// Puppet on the remote machine connecting to a Puppet master.
package puppetserver
@ -7,12 +7,48 @@ import (
"os"
"strings"
"github.com/hashicorp/packer/common"
"github.com/hashicorp/packer/helper/config"
"github.com/hashicorp/packer/packer"
"github.com/hashicorp/packer/template/interpolate"
"github.com/mitchellh/packer/common"
"github.com/mitchellh/packer/helper/config"
"github.com/mitchellh/packer/packer"
"github.com/mitchellh/packer/provisioner"
"github.com/mitchellh/packer/template/interpolate"
)
type guestOSTypeConfig struct {
executeCommand string
facterVarsFmt string
stagingDir string
}
var guestOSTypeConfigs = map[string]guestOSTypeConfig{
provisioner.UnixOSType: {
executeCommand: "{{.FacterVars}} {{if .Sudo}}sudo -E {{end}}" +
"{{if ne .PuppetBinDir \"\"}}{{.PuppetBinDir}}/{{end}}puppet agent " +
"--onetime --no-daemonize " +
"{{if ne .PuppetServer \"\"}}--server='{{.PuppetServer}}' {{end}}" +
"{{if ne .Options \"\"}}{{.Options}} {{end}}" +
"{{if ne .PuppetNode \"\"}}--certname={{.PuppetNode}} {{end}}" +
"{{if ne .ClientCertPath \"\"}}--certdir='{{.ClientCertPath}}' {{end}}" +
"{{if ne .ClientPrivateKeyPath \"\"}}--privatekeydir='{{.ClientPrivateKeyPath}}' {{end}}" +
"--detailed-exitcodes",
facterVarsFmt: "FACTER_%s='%s'",
stagingDir: "/tmp/packer-puppet-server",
},
provisioner.WindowsOSType: {
executeCommand: "{{.FacterVars}} " +
"{{if ne .PuppetBinDir \"\"}}{{.PuppetBinDir}}/{{end}}puppet agent " +
"--onetime --no-daemonize " +
"{{if ne .PuppetServer \"\"}}--server='{{.PuppetServer}}' {{end}}" +
"{{if ne .Options \"\"}}{{.Options}} {{end}}" +
"{{if ne .PuppetNode \"\"}}--certname={{.PuppetNode}} {{end}}" +
"{{if ne .ClientCertPath \"\"}}--certdir='{{.ClientCertPath}}' {{end}}" +
"{{if ne .ClientPrivateKeyPath \"\"}}--privatekeydir='{{.ClientPrivateKeyPath}}' {{end}}" +
"--detailed-exitcodes",
facterVarsFmt: "SET \"FACTER_%s=%s\" &",
stagingDir: "C:/Windows/Temp/packer-puppet-server",
},
}
type Config struct {
common.PackerConfig `mapstructure:",squash"`
ctx interpolate.Context
@ -20,6 +56,9 @@ type Config struct {
// The command used to execute Puppet.
ExecuteCommand string `mapstructure:"execute_command"`
// The Guest OS Type (unix or windows)
GuestOSType string `mapstructure:"guest_os_type"`
// Additional facts to set when executing Puppet
Facter map[string]string
@ -54,7 +93,9 @@ type Config struct {
}
type Provisioner struct {
config Config
config Config
guestOSTypeConfig guestOSTypeConfig
guestCommands *provisioner.GuestCommands
}
type ExecuteTemplate struct {
@ -82,12 +123,28 @@ func (p *Provisioner) Prepare(raws ...interface{}) error {
return err
}
if p.config.GuestOSType == "" {
p.config.GuestOSType = provisioner.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 = provisioner.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.commandTemplate()
p.config.ExecuteCommand = p.guestOSTypeConfig.executeCommand
}
if p.config.StagingDir == "" {
p.config.StagingDir = "/tmp/packer-puppet-server"
p.config.StagingDir = p.guestOSTypeConfig.stagingDir
}
if p.config.Facter == nil {
@ -160,7 +217,7 @@ func (p *Provisioner) Provision(ui packer.Ui, comm packer.Communicator) error {
// Compile the facter variables
facterVars := make([]string, 0, len(p.config.Facter))
for k, v := range p.config.Facter {
facterVars = append(facterVars, fmt.Sprintf("FACTER_%s='%s'", k, v))
facterVars = append(facterVars, fmt.Sprintf(p.guestOSTypeConfig.facterVarsFmt, k, v))
}
// Execute Puppet
@ -202,16 +259,23 @@ func (p *Provisioner) Cancel() {
}
func (p *Provisioner) createDir(ui packer.Ui, comm packer.Communicator, dir string) error {
cmd := &packer.RemoteCmd{
Command: fmt.Sprintf("mkdir -p '%s'", dir),
}
ui.Message(fmt.Sprintf("Creating directory: %s", dir))
cmd := &packer.RemoteCmd{Command: p.guestCommands.CreateDir(dir)}
if err := cmd.StartWithUi(comm, ui); err != nil {
return err
}
if cmd.ExitStatus != 0 {
return fmt.Errorf("Non-zero exit status.")
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 = &packer.RemoteCmd{Command: p.guestCommands.Chmod(dir, "0777")}
if err := cmd.StartWithUi(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
@ -230,15 +294,3 @@ func (p *Provisioner) uploadDirectory(ui packer.Ui, comm packer.Communicator, ds
return comm.UploadDir(dst, src, nil)
}
func (p *Provisioner) commandTemplate() string {
return "{{.FacterVars}} {{if .Sudo}} sudo -E {{end}}" +
"{{if ne .PuppetBinDir \"\"}}{{.PuppetBinDir}}/{{end}}puppet agent " +
"--onetime --no-daemonize " +
"{{if ne .PuppetServer \"\"}}--server='{{.PuppetServer}}' {{end}}" +
"{{if ne .Options \"\"}}{{.Options}} {{end}}" +
"{{if ne .PuppetNode \"\"}}--certname={{.PuppetNode}} {{end}}" +
"{{if ne .ClientCertPath \"\"}}--certdir='{{.ClientCertPath}}' {{end}}" +
"{{if ne .ClientPrivateKeyPath \"\"}}--privatekeydir='{{.ClientPrivateKeyPath}}' {{end}}" +
"--detailed-exitcodes"
}

View File

@ -69,12 +69,13 @@ listed below:
- `puppet_server` (string) - Hostname of the Puppet server. By default
"puppet" will be used.
- `staging_dir` (string) - This is the directory where all the
configuration of Puppet by Packer will be placed. By default this
is /tmp/packer-puppet-server. This directory doesn't need to exist but
must have proper permissions so that the SSH user that Packer uses is able
to create directories and write into this folder. If the permissions are not
correct, use a shell provisioner prior to this to configure it properly.
- `staging_dir` (string) - This is the directory where all the configuration
of Puppet by Packer will be placed. By default this is "/tmp/packer-puppet-server"
when guest_os_type unix and "C:/Windows/Temp/packer-puppet-server" when windows.
This directory doesn't need to exist but must have proper permissions so that
the SSH user that Packer uses is able to create directories and write into this
folder. If the permissions are not correct, use a shell provisioner prior to this
to configure it properly.
- `puppet_bin_dir` (string) - The path to the directory that contains the puppet
binary for running `puppet agent`. Usually, this would be found via the `$PATH`
@ -83,19 +84,12 @@ listed below:
- `execute_command` (string) - This is optional. The command used to execute Puppet. This has
various [configuration template
variables](/docs/templates/engine.html) available. See
below for more information. By default, Packer uses the following command:
variables](/docs/templates/configuration-templates.html) available. See
below for more information.
``` liquid
{{.FacterVars}} {{if .Sudo}} sudo -E {{end}} \
{{if ne .PuppetBinDir \"\"}}{{.PuppetBinDir}}/{{end}}puppet agent --onetime --no-daemonize \
{{if ne .PuppetServer \"\"}}--server='{{.PuppetServer}}' {{end}} \
{{if ne .Options \"\"}}{{.Options}} {{end}} \
{{if ne .PuppetNode \"\"}}--certname={{.PuppetNode}} {{end}} \
{{if ne .ClientCertPath \"\"}}--certdir='{{.ClientCertPath}}' {{end}} \
{{if ne .ClientPrivateKeyPath \"\"}}--privatekeydir='{{.ClientPrivateKeyPath}}' \
{{end}} --detailed-exitcodes
```
- `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".
## Default Facts