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

@ -23,6 +23,7 @@ type Config struct {
common.PackerConfig `mapstructure:",squash"` common.PackerConfig `mapstructure:",squash"`
ChefEnvironment string `mapstructure:"chef_environment"` ChefEnvironment string `mapstructure:"chef_environment"`
EncryptedDataBagSecretPath string `mapstructure:"encrypted_data_bag_secret_path"`
SslVerifyMode string `mapstructure:"ssl_verify_mode"` SslVerifyMode string `mapstructure:"ssl_verify_mode"`
ConfigTemplate string `mapstructure:"config_template"` ConfigTemplate string `mapstructure:"config_template"`
ExecuteCommand string `mapstructure:"execute_command"` ExecuteCommand string `mapstructure:"execute_command"`
@ -53,8 +54,10 @@ type ConfigTemplate struct {
ClientKey string ClientKey string
ValidationKeyPath string ValidationKeyPath string
ValidationClientName string ValidationClientName string
EncryptedDataBagSecretPath string
ChefEnvironment string ChefEnvironment string
SslVerifyMode 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
@ -265,6 +295,8 @@ func (p *Provisioner) createConfig(ui packer.Ui, comm packer.Communicator, nodeN
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}}