Chef-client provisioner: Add encrypted data bag secret support (Fixes #1945)

This commit is contained in:
Eric Herot 2015-08-21 15:06:24 -07:00
parent cf8933e374
commit f625c985af
2 changed files with 111 additions and 33 deletions

View File

@ -22,23 +22,24 @@ import (
type Config struct { type Config struct {
common.PackerConfig `mapstructure:",squash"` common.PackerConfig `mapstructure:",squash"`
ChefEnvironment string `mapstructure:"chef_environment"` ChefEnvironment string `mapstructure:"chef_environment"`
SslVerifyMode string `mapstructure:"ssl_verify_mode"` EncryptedDataBagSecretPath string `mapstructure:"encrypted_data_bag_secret_path"`
ConfigTemplate string `mapstructure:"config_template"` SslVerifyMode string `mapstructure:"ssl_verify_mode"`
ExecuteCommand string `mapstructure:"execute_command"` ConfigTemplate string `mapstructure:"config_template"`
InstallCommand string `mapstructure:"install_command"` ExecuteCommand string `mapstructure:"execute_command"`
Json map[string]interface{} InstallCommand string `mapstructure:"install_command"`
NodeName string `mapstructure:"node_name"` Json map[string]interface{}
PreventSudo bool `mapstructure:"prevent_sudo"` NodeName string `mapstructure:"node_name"`
RunList []string `mapstructure:"run_list"` PreventSudo bool `mapstructure:"prevent_sudo"`
ServerUrl string `mapstructure:"server_url"` RunList []string `mapstructure:"run_list"`
SkipCleanClient bool `mapstructure:"skip_clean_client"` ServerUrl string `mapstructure:"server_url"`
SkipCleanNode bool `mapstructure:"skip_clean_node"` SkipCleanClient bool `mapstructure:"skip_clean_client"`
SkipInstall bool `mapstructure:"skip_install"` SkipCleanNode bool `mapstructure:"skip_clean_node"`
StagingDir string `mapstructure:"staging_directory"` SkipInstall bool `mapstructure:"skip_install"`
ClientKey string `mapstructure:"client_key"` StagingDir string `mapstructure:"staging_directory"`
ValidationKeyPath string `mapstructure:"validation_key_path"` ClientKey string `mapstructure:"client_key"`
ValidationClientName string `mapstructure:"validation_client_name"` ValidationKeyPath string `mapstructure:"validation_key_path"`
ValidationClientName string `mapstructure:"validation_client_name"`
ctx interpolate.Context ctx interpolate.Context
} }
@ -48,13 +49,15 @@ type Provisioner struct {
} }
type ConfigTemplate struct { type ConfigTemplate struct {
NodeName string NodeName string
ServerUrl string ServerUrl string
ClientKey string ClientKey string
ValidationKeyPath string ValidationKeyPath string
ValidationClientName string ValidationClientName string
ChefEnvironment string EncryptedDataBagSecretPath string
SslVerifyMode string ChefEnvironment string
SslVerifyMode string
HasEncryptedDataBagSecretPath bool
} }
type ExecuteTemplate struct { type ExecuteTemplate struct {
@ -118,6 +121,15 @@ func (p *Provisioner) Prepare(raws ...interface{}) error {
errs, fmt.Errorf("server_url must be set")) errs, fmt.Errorf("server_url must be set"))
} }
if p.config.EncryptedDataBagSecretPath != "" {
pFileInfo, err := os.Stat(p.config.EncryptedDataBagSecretPath)
if err != nil || pFileInfo.IsDir() {
errs = packer.MultiErrorAppend(
errs, fmt.Errorf("Bad encrypted data bag secret '%s': %s", p.config.EncryptedDataBagSecretPath, err))
}
}
jsonValid := true jsonValid := true
for k, v := range p.config.Json { for k, v := range p.config.Json {
p.config.Json[k], err = p.deepJsonFix(k, v) p.config.Json[k], err = p.deepJsonFix(k, v)
@ -175,8 +187,16 @@ func (p *Provisioner) Provision(ui packer.Ui, comm packer.Communicator) error {
} }
} }
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)
}
}
configPath, err := p.createConfig( configPath, err := p.createConfig(
ui, comm, nodeName, serverUrl, p.config.ClientKey, remoteValidationKeyPath, p.config.ValidationClientName, p.config.ChefEnvironment, p.config.SslVerifyMode) ui, comm, nodeName, serverUrl, p.config.ClientKey, remoteValidationKeyPath, p.config.ValidationClientName, encryptedDataBagSecretPath, p.config.ChefEnvironment, p.config.SslVerifyMode)
if err != nil { if err != nil {
return fmt.Errorf("Error creating Chef config file: %s", err) return fmt.Errorf("Error creating Chef config file: %s", err)
} }
@ -236,7 +256,17 @@ func (p *Provisioner) uploadDirectory(ui packer.Ui, comm packer.Communicator, ds
return comm.UploadDir(dst, src, nil) return comm.UploadDir(dst, src, nil)
} }
func (p *Provisioner) createConfig(ui packer.Ui, comm packer.Communicator, nodeName string, serverUrl string, clientKey string, remoteKeyPath string, validationClientName string, chefEnvironment string, sslVerifyMode string) (string, error) { func (p *Provisioner) uploadFile(ui packer.Ui, comm packer.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 packer.Ui, comm packer.Communicator, nodeName string, serverUrl string, clientKey string, remoteKeyPath string, validationClientName string, encryptedDataBagSecretPath string, chefEnvironment string, sslVerifyMode string) (string, error) {
ui.Message("Creating configuration file 'client.rb'") ui.Message("Creating configuration file 'client.rb'")
// Read the template // Read the template
@ -258,13 +288,15 @@ func (p *Provisioner) createConfig(ui packer.Ui, comm packer.Communicator, nodeN
ctx := p.config.ctx ctx := p.config.ctx
ctx.Data = &ConfigTemplate{ ctx.Data = &ConfigTemplate{
NodeName: nodeName, NodeName: nodeName,
ServerUrl: serverUrl, ServerUrl: serverUrl,
ClientKey: clientKey, ClientKey: clientKey,
ValidationKeyPath: remoteKeyPath, ValidationKeyPath: remoteKeyPath,
ValidationClientName: validationClientName, ValidationClientName: validationClientName,
ChefEnvironment: chefEnvironment, ChefEnvironment: chefEnvironment,
SslVerifyMode: sslVerifyMode, SslVerifyMode: sslVerifyMode,
EncryptedDataBagSecretPath: encryptedDataBagSecretPath,
HasEncryptedDataBagSecretPath: encryptedDataBagSecretPath != "",
} }
configString, err := interpolate.Render(tpl, &ctx) configString, err := interpolate.Render(tpl, &ctx)
if err != nil { if err != nil {
@ -587,6 +619,9 @@ log_level :info
log_location STDOUT log_location STDOUT
chef_server_url "{{.ServerUrl}}" chef_server_url "{{.ServerUrl}}"
client_key "{{.ClientKey}}" client_key "{{.ClientKey}}"
{{if .HasEncryptedDataBagSecretPath}}
encrypted_data_bag_secret "{{.EncryptedDataBagSecretPath}}"
{{end}}
{{if ne .ValidationClientName ""}} {{if ne .ValidationClientName ""}}
validation_client_name "{{.ValidationClientName}}" validation_client_name "{{.ValidationClientName}}"
{{else}} {{else}}

View File

@ -138,6 +138,49 @@ func TestProvisionerPrepare_serverUrl(t *testing.T) {
} }
} }
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) { func TestProvisioner_createDir(t *testing.T) {
p1 := &Provisioner{config: Config{PreventSudo: true}} p1 := &Provisioner{config: Config{PreventSudo: true}}
p2 := &Provisioner{config: Config{PreventSudo: false}} p2 := &Provisioner{config: Config{PreventSudo: false}}