Merge pull request #4906 from svanharmelen/f-chef-attribute-file
provisioner/chef: make the Chef `attributes` param also accept a raw JSON string
This commit is contained in:
commit
7c32752332
|
@ -280,6 +280,32 @@ func TestResourceProvider_linuxCreateConfigFiles(t *testing.T) {
|
|||
`"subkey2b":{"subkey3":"value3"}}},"key2":"value2","run_list":["cookbook::recipe"]}`,
|
||||
},
|
||||
},
|
||||
|
||||
"Attributes JSON": {
|
||||
Config: testConfig(t, map[string]interface{}{
|
||||
"attributes_json": `{"key1":{"subkey1":{"subkey2a":["val1","val2","val3"],` +
|
||||
`"subkey2b":{"subkey3":"value3"}}},"key2":"value2"}`,
|
||||
"node_name": "nodename1",
|
||||
"prevent_sudo": true,
|
||||
"run_list": []interface{}{"cookbook::recipe"},
|
||||
"secret_key_path": "test-fixtures/encrypted_data_bag_secret",
|
||||
"server_url": "https://chef.local",
|
||||
"validation_client_name": "validator",
|
||||
"validation_key_path": "test-fixtures/validator.pem",
|
||||
}),
|
||||
|
||||
Commands: map[string]bool{
|
||||
"mkdir -p " + linuxConfDir: true,
|
||||
},
|
||||
|
||||
Uploads: map[string]string{
|
||||
linuxConfDir + "/client.rb": defaultLinuxClientConf,
|
||||
linuxConfDir + "/encrypted_data_bag_secret": "SECRET-KEY-FILE",
|
||||
linuxConfDir + "/validation.pem": "VALIDATOR-PEM-FILE",
|
||||
linuxConfDir + "/first-boot.json": `{"key1":{"subkey1":{"subkey2a":["val1","val2","val3"],` +
|
||||
`"subkey2b":{"subkey3":"value3"}}},"key2":"value2","run_list":["cookbook::recipe"]}`,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
r := new(ResourceProvisioner)
|
||||
|
|
|
@ -24,16 +24,18 @@ import (
|
|||
)
|
||||
|
||||
const (
|
||||
clienrb = "client.rb"
|
||||
defaultEnv = "_default"
|
||||
firstBoot = "first-boot.json"
|
||||
logfileDir = "logfiles"
|
||||
linuxChefCmd = "chef-client"
|
||||
linuxConfDir = "/etc/chef"
|
||||
secretKey = "encrypted_data_bag_secret"
|
||||
validationKey = "validation.pem"
|
||||
windowsChefCmd = "cmd /c chef-client"
|
||||
windowsConfDir = "C:/chef"
|
||||
clienrb = "client.rb"
|
||||
defaultEnv = "_default"
|
||||
firstBoot = "first-boot.json"
|
||||
logfileDir = "logfiles"
|
||||
linuxChefCmd = "chef-client"
|
||||
linuxKnifeCmd = "knife"
|
||||
linuxConfDir = "/etc/chef"
|
||||
secretKey = "encrypted_data_bag_secret"
|
||||
validationKey = "validation.pem"
|
||||
windowsChefCmd = "cmd /c chef-client"
|
||||
windowsKnifeCmd = "cmd /c knife"
|
||||
windowsConfDir = "C:/chef"
|
||||
)
|
||||
|
||||
const clientConf = `
|
||||
|
@ -74,34 +76,37 @@ ENV['no_proxy'] = "{{ join .NOProxy "," }}"
|
|||
|
||||
// Provisioner represents a specificly configured chef provisioner
|
||||
type Provisioner struct {
|
||||
Attributes interface{} `mapstructure:"attributes"`
|
||||
ClientOptions []string `mapstructure:"client_options"`
|
||||
DisableReporting bool `mapstructure:"disable_reporting"`
|
||||
Environment string `mapstructure:"environment"`
|
||||
LogToFile bool `mapstructure:"log_to_file"`
|
||||
UsePolicyfile bool `mapstructure:"use_policyfile"`
|
||||
PolicyGroup string `mapstructure:"policy_group"`
|
||||
PolicyName string `mapstructure:"policy_name"`
|
||||
HTTPProxy string `mapstructure:"http_proxy"`
|
||||
HTTPSProxy string `mapstructure:"https_proxy"`
|
||||
NOProxy []string `mapstructure:"no_proxy"`
|
||||
NodeName string `mapstructure:"node_name"`
|
||||
OhaiHints []string `mapstructure:"ohai_hints"`
|
||||
OSType string `mapstructure:"os_type"`
|
||||
PreventSudo bool `mapstructure:"prevent_sudo"`
|
||||
RunList []string `mapstructure:"run_list"`
|
||||
SecretKey string `mapstructure:"secret_key"`
|
||||
ServerURL string `mapstructure:"server_url"`
|
||||
SkipInstall bool `mapstructure:"skip_install"`
|
||||
SSLVerifyMode string `mapstructure:"ssl_verify_mode"`
|
||||
ValidationClientName string `mapstructure:"validation_client_name"`
|
||||
ValidationKey string `mapstructure:"validation_key"`
|
||||
Version string `mapstructure:"version"`
|
||||
Attributes interface{} `mapstructure:"attributes"`
|
||||
AttributesJSON string `mapstructure:"attributes_json"`
|
||||
ClientOptions []string `mapstructure:"client_options"`
|
||||
DisableReporting bool `mapstructure:"disable_reporting"`
|
||||
Environment string `mapstructure:"environment"`
|
||||
FetchChefCertificates bool `mapstructure:"fetch_chef_certificates"`
|
||||
LogToFile bool `mapstructure:"log_to_file"`
|
||||
UsePolicyfile bool `mapstructure:"use_policyfile"`
|
||||
PolicyGroup string `mapstructure:"policy_group"`
|
||||
PolicyName string `mapstructure:"policy_name"`
|
||||
HTTPProxy string `mapstructure:"http_proxy"`
|
||||
HTTPSProxy string `mapstructure:"https_proxy"`
|
||||
NOProxy []string `mapstructure:"no_proxy"`
|
||||
NodeName string `mapstructure:"node_name"`
|
||||
OhaiHints []string `mapstructure:"ohai_hints"`
|
||||
OSType string `mapstructure:"os_type"`
|
||||
PreventSudo bool `mapstructure:"prevent_sudo"`
|
||||
RunList []string `mapstructure:"run_list"`
|
||||
SecretKey string `mapstructure:"secret_key"`
|
||||
ServerURL string `mapstructure:"server_url"`
|
||||
SkipInstall bool `mapstructure:"skip_install"`
|
||||
SSLVerifyMode string `mapstructure:"ssl_verify_mode"`
|
||||
ValidationClientName string `mapstructure:"validation_client_name"`
|
||||
ValidationKey string `mapstructure:"validation_key"`
|
||||
Version string `mapstructure:"version"`
|
||||
|
||||
installChefClient func(terraform.UIOutput, communicator.Communicator) error
|
||||
createConfigFiles func(terraform.UIOutput, communicator.Communicator) error
|
||||
runChefClient func(terraform.UIOutput, communicator.Communicator) error
|
||||
useSudo bool
|
||||
installChefClient func(terraform.UIOutput, communicator.Communicator) error
|
||||
createConfigFiles func(terraform.UIOutput, communicator.Communicator) error
|
||||
fetchChefCertificates func(terraform.UIOutput, communicator.Communicator) error
|
||||
runChefClient func(terraform.UIOutput, communicator.Communicator) error
|
||||
useSudo bool
|
||||
|
||||
// Deprecated Fields
|
||||
SecretKeyPath string `mapstructure:"secret_key_path"`
|
||||
|
@ -138,11 +143,13 @@ func (r *ResourceProvisioner) Apply(
|
|||
case "linux":
|
||||
p.installChefClient = p.linuxInstallChefClient
|
||||
p.createConfigFiles = p.linuxCreateConfigFiles
|
||||
p.fetchChefCertificates = p.fetchChefCertificatesFunc(linuxChefCmd, linuxConfDir)
|
||||
p.runChefClient = p.runChefClientFunc(linuxChefCmd, linuxConfDir)
|
||||
p.useSudo = !p.PreventSudo && s.Ephemeral.ConnInfo["user"] != "root"
|
||||
case "windows":
|
||||
p.installChefClient = p.windowsInstallChefClient
|
||||
p.createConfigFiles = p.windowsCreateConfigFiles
|
||||
p.fetchChefCertificates = p.fetchChefCertificatesFunc(windowsChefCmd, windowsConfDir)
|
||||
p.runChefClient = p.runChefClientFunc(windowsChefCmd, windowsConfDir)
|
||||
p.useSudo = false
|
||||
default:
|
||||
|
@ -176,6 +183,13 @@ func (r *ResourceProvisioner) Apply(
|
|||
return err
|
||||
}
|
||||
|
||||
if p.FetchChefCertificates {
|
||||
o.Output("Fetch Chef certificates...")
|
||||
if err := p.fetchChefCertificates(o, comm); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
o.Output("Starting initial Chef-Client run...")
|
||||
if err := p.runChefClient(o, comm); err != nil {
|
||||
return err
|
||||
|
@ -222,6 +236,10 @@ func (r *ResourceProvisioner) Validate(c *terraform.ResourceConfig) (ws []string
|
|||
ws = append(ws, "secret_key_path is deprecated, please use "+
|
||||
"secret_key instead and load the key contents via file()")
|
||||
}
|
||||
if _, ok := c.Config["attributes"]; ok {
|
||||
ws = append(ws, "using map style attribute values is deprecated, "+
|
||||
" please use a single raw JSON string instead")
|
||||
}
|
||||
|
||||
return ws, es
|
||||
}
|
||||
|
@ -286,6 +304,14 @@ func (r *ResourceProvisioner) decodeConfig(c *terraform.ResourceConfig) (*Provis
|
|||
}
|
||||
}
|
||||
|
||||
if attrs, ok := c.Config["attributes_json"]; ok {
|
||||
var m map[string]interface{}
|
||||
if err := json.Unmarshal([]byte(attrs.(string)), &m); err != nil {
|
||||
return nil, fmt.Errorf("Error parsing the attributes: %v", err)
|
||||
}
|
||||
p.Attributes = m
|
||||
}
|
||||
|
||||
return p, nil
|
||||
}
|
||||
|
||||
|
@ -306,7 +332,7 @@ func rawToJSON(raw interface{}) (interface{}, error) {
|
|||
|
||||
return s[0], nil
|
||||
default:
|
||||
return raw, nil
|
||||
return s, nil
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -328,6 +354,17 @@ func retryFunc(timeout time.Duration, f func() error) error {
|
|||
}
|
||||
}
|
||||
|
||||
func (p *Provisioner) fetchChefCertificatesFunc(
|
||||
knifeCmd string,
|
||||
confDir string) func(terraform.UIOutput, communicator.Communicator) error {
|
||||
return func(o terraform.UIOutput, comm communicator.Communicator) error {
|
||||
clientrb := path.Join(confDir, clienrb)
|
||||
cmd := fmt.Sprintf("%s ssl fetch -c %s", knifeCmd, clientrb)
|
||||
|
||||
return p.runCommand(o, comm, cmd)
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Provisioner) runChefClientFunc(
|
||||
chefCmd string,
|
||||
confDir string) func(terraform.UIOutput, communicator.Communicator) error {
|
||||
|
|
|
@ -16,7 +16,6 @@ func TestResourceProvisioner_impl(t *testing.T) {
|
|||
|
||||
func TestResourceProvider_Validate_good(t *testing.T) {
|
||||
c := testConfig(t, map[string]interface{}{
|
||||
"attributes": []interface{}{"key1 { subkey1 = value1 }"},
|
||||
"environment": "_default",
|
||||
"node_name": "nodename1",
|
||||
"run_list": []interface{}{"cookbook::recipe"},
|
||||
|
@ -149,3 +148,76 @@ func TestResourceProvider_runChefClient(t *testing.T) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestResourceProvider_fetchChefCertificates(t *testing.T) {
|
||||
cases := map[string]struct {
|
||||
Config *terraform.ResourceConfig
|
||||
KnifeCmd string
|
||||
ConfDir string
|
||||
Commands map[string]bool
|
||||
}{
|
||||
"Sudo": {
|
||||
Config: testConfig(t, map[string]interface{}{
|
||||
"fetch_chef_certificates": true,
|
||||
"node_name": "nodename1",
|
||||
"run_list": []interface{}{"cookbook::recipe"},
|
||||
"server_url": "https://chef.local",
|
||||
"validation_client_name": "validator",
|
||||
"validation_key_path": "test-fixtures/validator.pem",
|
||||
}),
|
||||
|
||||
KnifeCmd: linuxKnifeCmd,
|
||||
|
||||
ConfDir: linuxConfDir,
|
||||
|
||||
Commands: map[string]bool{
|
||||
fmt.Sprintf(`sudo %s ssl fetch -c %s`,
|
||||
linuxKnifeCmd,
|
||||
path.Join(linuxConfDir, "client.rb")): true,
|
||||
},
|
||||
},
|
||||
|
||||
"NoSudo": {
|
||||
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",
|
||||
"validation_client_name": "validator",
|
||||
"validation_key_path": "test-fixtures/validator.pem",
|
||||
}),
|
||||
|
||||
KnifeCmd: windowsKnifeCmd,
|
||||
|
||||
ConfDir: windowsConfDir,
|
||||
|
||||
Commands: map[string]bool{
|
||||
fmt.Sprintf(`%s ssl fetch -c %s`,
|
||||
windowsKnifeCmd,
|
||||
path.Join(windowsConfDir, "client.rb")): 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.fetchChefCertificates = p.fetchChefCertificatesFunc(tc.KnifeCmd, tc.ConfDir)
|
||||
p.useSudo = !p.PreventSudo
|
||||
|
||||
err = p.fetchChefCertificates(o, c)
|
||||
if err != nil {
|
||||
t.Fatalf("Test %q failed: %v", k, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -196,6 +196,31 @@ func TestResourceProvider_windowsCreateConfigFiles(t *testing.T) {
|
|||
`"subkey2b":{"subkey3":"value3"}}},"key2":"value2","run_list":["cookbook::recipe"]}`,
|
||||
},
|
||||
},
|
||||
|
||||
"Attributes JSON": {
|
||||
Config: testConfig(t, map[string]interface{}{
|
||||
"attributes_json": `{"key1":{"subkey1":{"subkey2a":["val1","val2","val3"],` +
|
||||
`"subkey2b":{"subkey3":"value3"}}},"key2":"value2"}`,
|
||||
"node_name": "nodename1",
|
||||
"run_list": []interface{}{"cookbook::recipe"},
|
||||
"secret_key_path": "test-fixtures/encrypted_data_bag_secret",
|
||||
"server_url": "https://chef.local",
|
||||
"validation_client_name": "validator",
|
||||
"validation_key_path": "test-fixtures/validator.pem",
|
||||
}),
|
||||
|
||||
Commands: map[string]bool{
|
||||
fmt.Sprintf("cmd /c if not exist %q mkdir %q", windowsConfDir, windowsConfDir): true,
|
||||
},
|
||||
|
||||
Uploads: map[string]string{
|
||||
windowsConfDir + "/client.rb": defaultWindowsClientConf,
|
||||
windowsConfDir + "/encrypted_data_bag_secret": "SECRET-KEY-FILE",
|
||||
windowsConfDir + "/validation.pem": "VALIDATOR-PEM-FILE",
|
||||
windowsConfDir + "/first-boot.json": `{"key1":{"subkey1":{"subkey2a":["val1","val2","val3"],` +
|
||||
`"subkey2b":{"subkey3":"value3"}}},"key2":"value2","run_list":["cookbook::recipe"]}`,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
r := new(ResourceProvisioner)
|
||||
|
|
|
@ -25,14 +25,19 @@ available on the target machine.
|
|||
resource "aws_instance" "web" {
|
||||
...
|
||||
provisioner "chef" {
|
||||
attributes {
|
||||
"key" = "value"
|
||||
"app" {
|
||||
"cluster1" {
|
||||
"nodes" = ["webserver1", "webserver2"]
|
||||
attributes_json = <<EOF
|
||||
{
|
||||
"key": "value",
|
||||
"app": {
|
||||
"cluster1": {
|
||||
"nodes": [
|
||||
"webserver1",
|
||||
"webserver2"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
EOF
|
||||
environment = "_default"
|
||||
run_list = ["cookbook::recipe"]
|
||||
node_name = "webserver1"
|
||||
|
@ -49,11 +54,12 @@ resource "aws_instance" "web" {
|
|||
|
||||
The following arguments are supported:
|
||||
|
||||
* `attributes (map)` - (Optional) A map with initial node attributes for the new node.
|
||||
See example.
|
||||
* `attributes_json (string)` - (Optional) A raw JSON string with initial node attributes
|
||||
for the new node. These can also be loaded from a file on disk using the [`file()`
|
||||
interpolation function](/docs/configuration/interpolation.html#file_path_).
|
||||
|
||||
* `client_options (array)` - (Optional) A list of optional Chef Client configuration
|
||||
options. See the Chef Client [documentation](https://docs.chef.io/config_rb_client.html) for all available options.
|
||||
options. See the [Chef Client ](https://docs.chef.io/config_rb_client.html) documentation for all available options.
|
||||
|
||||
* `disable_reporting (boolean)` - (Optional) If true the Chef Client will not try to send
|
||||
reporting data (used by Chef Reporting) to the Chef Server (defaults false)
|
||||
|
@ -61,6 +67,10 @@ The following arguments are supported:
|
|||
* `environment (string)` - (Optional) The Chef environment the new node will be joining
|
||||
(defaults `_default`).
|
||||
|
||||
* `fetch_chef_certificates (boolean)` (Optional) If true the SSL certificates configured
|
||||
on your Chef server will be fetched and trusted. See the knife [ssl_fetch](https://docs.chef.io/knife_ssl_fetch.html)
|
||||
documentation for more details.
|
||||
|
||||
* `log_to_file (boolean)` - (Optional) If true, the output of the initial Chef Client run
|
||||
will be logged to a local file instead of the console. The file will be created in a
|
||||
subdirectory called `logfiles` created in your current directory. The filename will be
|
||||
|
@ -118,5 +128,6 @@ The following arguments are supported:
|
|||
These are supported for backwards compatibility and may be removed in a
|
||||
future version:
|
||||
|
||||
* `validation_key_path (string)` - __Deprecated: please use `validation_key` instead__.
|
||||
* `attributes (map)` - __Deprecated: please use `attributes_json` instead__.
|
||||
* `secret_key_path (string)` - __Deprecated: please use `secret_key` instead__.
|
||||
* `validation_key_path (string)` - __Deprecated: please use `validation_key` instead__.
|
||||
|
|
Loading…
Reference in New Issue