From da927fcd088ba4f2f1aad31194fd01def2390be1 Mon Sep 17 00:00:00 2001 From: Sander van Harmelen Date: Fri, 29 Jan 2016 18:41:14 +0100 Subject: [PATCH] Make the Chef `attributes` param also accept a raw JSON string See the updated docs for more details and examples, but in short this enables the `attributes` param from the Chef provisioner to accept a raw JSON string. Fixes #3074 Fixes #3572 --- .../chef/linux_provisioner_test.go | 28 ++++++++++++++++++- .../provisioners/chef/resource_provisioner.go | 23 ++++++++++++--- .../chef/resource_provisioner_test.go | 1 - .../chef/windows_provisioner_test.go | 27 +++++++++++++++++- .../docs/provisioners/chef.html.markdown | 20 ++++++++----- 5 files changed, 85 insertions(+), 14 deletions(-) diff --git a/builtin/provisioners/chef/linux_provisioner_test.go b/builtin/provisioners/chef/linux_provisioner_test.go index 89fae2b3f..02b58c910 100644 --- a/builtin/provisioners/chef/linux_provisioner_test.go +++ b/builtin/provisioners/chef/linux_provisioner_test.go @@ -236,7 +236,33 @@ func TestResourceProvider_linuxCreateConfigFiles(t *testing.T) { }, }, - "Attributes": { + "String Attributes": { + Config: testConfig(t, map[string]interface{}{ + "attributes": `{"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"]}`, + }, + }, + + "Map Attributes": { Config: testConfig(t, map[string]interface{}{ "attributes": []map[string]interface{}{ map[string]interface{}{ diff --git a/builtin/provisioners/chef/resource_provisioner.go b/builtin/provisioners/chef/resource_provisioner.go index 34c6a5f1f..54d9f48cf 100644 --- a/builtin/provisioners/chef/resource_provisioner.go +++ b/builtin/provisioners/chef/resource_provisioner.go @@ -222,6 +222,12 @@ 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 attrs, ok := c.Config["attributes"]; ok { + if _, ok := attrs.(string); !ok { + ws = append(ws, "using map style attribute values is deprecated, "+ + " please use a single raw JSON string instead") + } + } return ws, es } @@ -280,9 +286,18 @@ func (r *ResourceProvisioner) decodeConfig(c *terraform.ResourceConfig) (*Provis } if attrs, ok := c.Config["attributes"]; ok { - p.Attributes, err = rawToJSON(attrs) - if err != nil { - return nil, fmt.Errorf("Error parsing the attributes: %v", err) + switch attrs := attrs.(type) { + case string: + var m map[string]interface{} + if err := json.Unmarshal([]byte(attrs), &m); err != nil { + return nil, fmt.Errorf("Error parsing the attributes: %v", err) + } + p.Attributes = m + default: + p.Attributes, err = rawToJSON(attrs) + if err != nil { + return nil, fmt.Errorf("Error parsing the attributes: %v", err) + } } } @@ -306,7 +321,7 @@ func rawToJSON(raw interface{}) (interface{}, error) { return s[0], nil default: - return raw, nil + return s, nil } } diff --git a/builtin/provisioners/chef/resource_provisioner_test.go b/builtin/provisioners/chef/resource_provisioner_test.go index 40625196a..f06d97f84 100644 --- a/builtin/provisioners/chef/resource_provisioner_test.go +++ b/builtin/provisioners/chef/resource_provisioner_test.go @@ -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"}, diff --git a/builtin/provisioners/chef/windows_provisioner_test.go b/builtin/provisioners/chef/windows_provisioner_test.go index 13604d6c9..506705795 100644 --- a/builtin/provisioners/chef/windows_provisioner_test.go +++ b/builtin/provisioners/chef/windows_provisioner_test.go @@ -153,7 +153,32 @@ func TestResourceProvider_windowsCreateConfigFiles(t *testing.T) { }, }, - "Attributes": { + "String Attributes": { + Config: testConfig(t, map[string]interface{}{ + "attributes": `{"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"]}`, + }, + }, + + "Map Attributes": { Config: testConfig(t, map[string]interface{}{ "attributes": []map[string]interface{}{ map[string]interface{}{ diff --git a/website/source/docs/provisioners/chef.html.markdown b/website/source/docs/provisioners/chef.html.markdown index e4b89f23f..41ec869ce 100644 --- a/website/source/docs/provisioners/chef.html.markdown +++ b/website/source/docs/provisioners/chef.html.markdown @@ -25,14 +25,19 @@ available on the target machine. resource "aws_instance" "web" { ... provisioner "chef" { - attributes { - "key" = "value" - "app" { - "cluster1" { - "nodes" = ["webserver1", "webserver2"] + attributes = <