Accept both slices and strings in vault_json (#9114)

Fixes #9105 by allowing the `vault_json` to contain either slices or strings.

And fixes #8932 by changing to way we cleanup the user key.
This commit is contained in:
Sander van Harmelen 2016-10-01 00:35:27 +02:00 committed by GitHub
parent de3a7b5d20
commit c307dc9557
2 changed files with 166 additions and 15 deletions

View File

@ -11,6 +11,7 @@ import (
"path" "path"
"regexp" "regexp"
"strings" "strings"
"sync"
"text/template" "text/template"
"time" "time"
@ -110,7 +111,7 @@ type Provisioner struct {
Version string `mapstructure:"version"` Version string `mapstructure:"version"`
attributes map[string]interface{} attributes map[string]interface{}
vaults map[string]string vaults map[string][]string
cleanupUserKeyCmd string cleanupUserKeyCmd string
createConfigFiles func(terraform.UIOutput, communicator.Communicator) error createConfigFiles func(terraform.UIOutput, communicator.Communicator) error
@ -192,12 +193,14 @@ func (r *ResourceProvisioner) Apply(
defer comm.Disconnect() defer comm.Disconnect()
// Make sure we always delete the user key from the new node! // Make sure we always delete the user key from the new node!
defer func() { var once sync.Once
cleanupUserKey := func() {
o.Output("Cleanup user key...") o.Output("Cleanup user key...")
if err := p.runCommand(o, comm, p.cleanupUserKeyCmd); err != nil { if err := p.runCommand(o, comm, p.cleanupUserKeyCmd); err != nil {
o.Output("WARNING: Failed to cleanup user key on new node: " + err.Error()) o.Output("WARNING: Failed to cleanup user key on new node: " + err.Error())
} }
}() }
defer once.Do(cleanupUserKey)
if !p.SkipInstall { if !p.SkipInstall {
if err := p.installChefClient(o, comm); err != nil { if err := p.installChefClient(o, comm); err != nil {
@ -229,6 +232,10 @@ func (r *ResourceProvisioner) Apply(
} }
} }
// Cleanup the user key before we run Chef-Client to prevent issues
// with rights caused by changing settings during the run.
once.Do(cleanupUserKey)
o.Output("Starting initial Chef-Client run...") o.Output("Starting initial Chef-Client run...")
if err := p.runChefClient(o, comm); err != nil { if err := p.runChefClient(o, comm); err != nil {
return err return err
@ -352,11 +359,28 @@ func (r *ResourceProvisioner) decodeConfig(c *terraform.ResourceConfig) (*Provis
} }
if vaults, ok := c.Config["vault_json"].(string); ok { if vaults, ok := c.Config["vault_json"].(string); ok {
var m map[string]string var m map[string]interface{}
if err := json.Unmarshal([]byte(vaults), &m); err != nil { if err := json.Unmarshal([]byte(vaults), &m); err != nil {
return nil, fmt.Errorf("Error parsing vault_json: %v", err) return nil, fmt.Errorf("Error parsing vault_json: %v", err)
} }
p.vaults = m
v := make(map[string][]string)
for vault, items := range m {
switch items := items.(type) {
case []interface{}:
for _, item := range items {
if item, ok := item.(string); ok {
v[vault] = append(v[vault], item)
}
}
case interface{}:
if item, ok := items.(string); ok {
v[vault] = append(v[vault], item)
}
}
}
p.vaults = v
} }
return p, nil return p, nil
@ -544,16 +568,18 @@ func (p *Provisioner) configureVaultsFunc(
path.Join(confDir, p.UserName+".pem"), path.Join(confDir, p.UserName+".pem"),
) )
for vault, item := range p.vaults { for vault, items := range p.vaults {
updateCmd := fmt.Sprintf("%s vault update %s %s -A %s -M client %s", for _, item := range items {
knifeCmd, updateCmd := fmt.Sprintf("%s vault update %s %s -A %s -M client %s",
vault, knifeCmd,
item, vault,
p.NodeName, item,
options, p.NodeName,
) options,
if err := p.runCommand(o, comm, updateCmd); err != nil { )
return err if err := p.runCommand(o, comm, updateCmd); err != nil {
return err
}
} }
} }

View File

@ -221,3 +221,128 @@ func TestResourceProvider_fetchChefCertificates(t *testing.T) {
} }
} }
} }
func TestResourceProvider_configureVaults(t *testing.T) {
cases := map[string]struct {
Config *terraform.ResourceConfig
GemCmd string
KnifeCmd string
ConfDir string
Commands map[string]bool
}{
"Linux Vault string": {
Config: testConfig(t, map[string]interface{}{
"node_name": "nodename1",
"prevent_sudo": true,
"run_list": []interface{}{"cookbook::recipe"},
"server_url": "https://chef.local",
"user_name": "bob",
"user_key": "USER-KEY",
"vault_json": `{"vault1": "item1"}`,
}),
GemCmd: linuxGemCmd,
KnifeCmd: linuxKnifeCmd,
ConfDir: linuxConfDir,
Commands: map[string]bool{
fmt.Sprintf("%s install chef-vault", linuxGemCmd): true,
fmt.Sprintf("%s vault update vault1 item1 -A nodename1 -M client -c %s/client.rb "+
"-u bob --key %s/bob.pem", linuxKnifeCmd, linuxConfDir, linuxConfDir): true,
},
},
"Linux Vault []string": {
Config: testConfig(t, map[string]interface{}{
"fetch_chef_certificates": true,
"node_name": "nodename1",
"prevent_sudo": true,
"run_list": []interface{}{"cookbook::recipe"},
"server_url": "https://chef.local",
"user_name": "bob",
"user_key": "USER-KEY",
"vault_json": `{"vault1": ["item1", "item2"]}`,
}),
GemCmd: linuxGemCmd,
KnifeCmd: linuxKnifeCmd,
ConfDir: linuxConfDir,
Commands: map[string]bool{
fmt.Sprintf("%s install chef-vault", linuxGemCmd): true,
fmt.Sprintf("%s vault update vault1 item1 -A nodename1 -M client -c %s/client.rb "+
"-u bob --key %s/bob.pem", linuxKnifeCmd, linuxConfDir, linuxConfDir): true,
fmt.Sprintf("%s vault update vault1 item2 -A nodename1 -M client -c %s/client.rb "+
"-u bob --key %s/bob.pem", linuxKnifeCmd, linuxConfDir, linuxConfDir): true,
},
},
"Windows Vault string": {
Config: testConfig(t, map[string]interface{}{
"node_name": "nodename1",
"prevent_sudo": true,
"run_list": []interface{}{"cookbook::recipe"},
"server_url": "https://chef.local",
"user_name": "bob",
"user_key": "USER-KEY",
"vault_json": `{"vault1": "item1"}`,
}),
GemCmd: windowsGemCmd,
KnifeCmd: windowsKnifeCmd,
ConfDir: windowsConfDir,
Commands: map[string]bool{
fmt.Sprintf("%s install chef-vault", windowsGemCmd): true,
fmt.Sprintf("%s vault update vault1 item1 -A nodename1 -M client -c %s/client.rb "+
"-u bob --key %s/bob.pem", windowsKnifeCmd, windowsConfDir, windowsConfDir): true,
},
},
"Windows Vault []string": {
Config: testConfig(t, map[string]interface{}{
"fetch_chef_certificates": true,
"node_name": "nodename1",
"prevent_sudo": true,
"run_list": []interface{}{"cookbook::recipe"},
"server_url": "https://chef.local",
"user_name": "bob",
"user_key": "USER-KEY",
"vault_json": `{"vault1": ["item1", "item2"]}`,
}),
GemCmd: windowsGemCmd,
KnifeCmd: windowsKnifeCmd,
ConfDir: windowsConfDir,
Commands: map[string]bool{
fmt.Sprintf("%s install chef-vault", windowsGemCmd): true,
fmt.Sprintf("%s vault update vault1 item1 -A nodename1 -M client -c %s/client.rb "+
"-u bob --key %s/bob.pem", windowsKnifeCmd, windowsConfDir, windowsConfDir): true,
fmt.Sprintf("%s vault update vault1 item2 -A nodename1 -M client -c %s/client.rb "+
"-u bob --key %s/bob.pem", windowsKnifeCmd, windowsConfDir, windowsConfDir): true,
},
},
}
r := new(ResourceProvisioner)
o := new(terraform.MockUIOutput)
c := new(communicator.MockCommunicator)
for k, tc := range cases {
c.Commands = tc.Commands
p, err := r.decodeConfig(tc.Config)
if err != nil {
t.Fatalf("Error: %v", err)
}
p.configureVaults = p.configureVaultsFunc(tc.GemCmd, tc.KnifeCmd, tc.ConfDir)
p.useSudo = !p.PreventSudo
err = p.configureVaults(o, c)
if err != nil {
t.Fatalf("Test %q failed: %v", k, err)
}
}
}