From 0e422737baa8f23919d5a7fc2c1e2772d3a32d14 Mon Sep 17 00:00:00 2001 From: Sander van Harmelen Date: Fri, 19 May 2017 20:42:14 +0200 Subject: [PATCH] Fix and refactor the Chef provisioner The tests did pass, but that was because they only tested part of the changes. By using the `schema.TestResourceDataRaw` function the schema and config are better tested and so they pointed out a problem with the schema of the Chef provisioner. The `Elem` fields did not have a `*schema.Schema` but a `schema.Schema` and in an `Elem` schema only the `Type` field may (and must) be set. Any other fields like `Optional` are not allowed here. Next to fixing that problem I also did a little refactoring and cleaning up. Mainly making the `ProvisionerS` private (`provisioner`) and removing the deprecated fields. --- .../provisioners/chef/linux_provisioner.go | 11 +- .../chef/linux_provisioner_test.go | 53 ++- .../provisioners/chef/resource_provisioner.go | 430 +++++++----------- .../chef/resource_provisioner_test.go | 84 ++-- .../provisioners/chef/windows_provisioner.go | 8 +- .../chef/windows_provisioner_test.go | 37 +- .../provisioners/file/resource_provisioner.go | 5 +- .../file/resource_provisioner_test.go | 25 +- .../local-exec/resource_provisioner_test.go | 13 +- .../remote-exec/resource_provisioner_test.go | 37 +- helper/schema/provisioner.go | 5 +- helper/schema/testing.go | 7 - .../docs/provisioners/chef.html.markdown | 6 - 13 files changed, 310 insertions(+), 411 deletions(-) diff --git a/builtin/provisioners/chef/linux_provisioner.go b/builtin/provisioners/chef/linux_provisioner.go index a9ad806a0..6d87cfe8a 100644 --- a/builtin/provisioners/chef/linux_provisioner.go +++ b/builtin/provisioners/chef/linux_provisioner.go @@ -14,10 +14,7 @@ const ( installURL = "https://www.chef.io/chef/install.sh" ) -func (p *ProvisionerS) linuxInstallChefClient( - o terraform.UIOutput, - comm communicator.Communicator) error { - +func (p *provisioner) linuxInstallChefClient(o terraform.UIOutput, comm communicator.Communicator) error { // Build up the command prefix prefix := "" if p.HTTPProxy != "" { @@ -26,7 +23,7 @@ func (p *ProvisionerS) linuxInstallChefClient( if p.HTTPSProxy != "" { prefix += fmt.Sprintf("https_proxy='%s' ", p.HTTPSProxy) } - if p.NOProxy != nil && len(p.NOProxy) > 0 { + if len(p.NOProxy) > 0 { prefix += fmt.Sprintf("no_proxy='%s' ", strings.Join(p.NOProxy, ",")) } @@ -46,9 +43,7 @@ func (p *ProvisionerS) linuxInstallChefClient( return p.runCommand(o, comm, fmt.Sprintf("%srm -f install.sh", prefix)) } -func (p *ProvisionerS) linuxCreateConfigFiles( - o terraform.UIOutput, - comm communicator.Communicator) error { +func (p *provisioner) linuxCreateConfigFiles(o terraform.UIOutput, comm communicator.Communicator) error { // Make sure the config directory exists if err := p.runCommand(o, comm, "mkdir -p "+linuxConfDir); err != nil { return err diff --git a/builtin/provisioners/chef/linux_provisioner_test.go b/builtin/provisioners/chef/linux_provisioner_test.go index 444a0f39a..578bb69ff 100644 --- a/builtin/provisioners/chef/linux_provisioner_test.go +++ b/builtin/provisioners/chef/linux_provisioner_test.go @@ -6,22 +6,23 @@ import ( "testing" "github.com/hashicorp/terraform/communicator" + "github.com/hashicorp/terraform/helper/schema" "github.com/hashicorp/terraform/terraform" ) func TestResourceProvider_linuxInstallChefClient(t *testing.T) { cases := map[string]struct { - Config *terraform.ResourceConfig + Config map[string]interface{} Commands map[string]bool }{ "Sudo": { - Config: testConfig(t, map[string]interface{}{ + Config: map[string]interface{}{ "node_name": "nodename1", "run_list": []interface{}{"cookbook::recipe"}, "server_url": "https://chef.local", "user_name": "bob", "user_key": "USER-KEY", - }), + }, Commands: map[string]bool{ "sudo curl -LO https://www.chef.io/chef/install.sh": true, @@ -31,7 +32,7 @@ func TestResourceProvider_linuxInstallChefClient(t *testing.T) { }, "NoSudo": { - Config: testConfig(t, map[string]interface{}{ + Config: map[string]interface{}{ "node_name": "nodename1", "prevent_sudo": true, "run_list": []interface{}{"cookbook::recipe"}, @@ -39,7 +40,7 @@ func TestResourceProvider_linuxInstallChefClient(t *testing.T) { "server_url": "https://chef.local", "user_name": "bob", "user_key": "USER-KEY", - }), + }, Commands: map[string]bool{ "curl -LO https://www.chef.io/chef/install.sh": true, @@ -49,7 +50,7 @@ func TestResourceProvider_linuxInstallChefClient(t *testing.T) { }, "HTTPProxy": { - Config: testConfig(t, map[string]interface{}{ + Config: map[string]interface{}{ "http_proxy": "http://proxy.local", "node_name": "nodename1", "prevent_sudo": true, @@ -57,7 +58,7 @@ func TestResourceProvider_linuxInstallChefClient(t *testing.T) { "server_url": "https://chef.local", "user_name": "bob", "user_key": "USER-KEY", - }), + }, Commands: map[string]bool{ "http_proxy='http://proxy.local' curl -LO https://www.chef.io/chef/install.sh": true, @@ -67,7 +68,7 @@ func TestResourceProvider_linuxInstallChefClient(t *testing.T) { }, "HTTPSProxy": { - Config: testConfig(t, map[string]interface{}{ + Config: map[string]interface{}{ "https_proxy": "https://proxy.local", "node_name": "nodename1", "prevent_sudo": true, @@ -75,7 +76,7 @@ func TestResourceProvider_linuxInstallChefClient(t *testing.T) { "server_url": "https://chef.local", "user_name": "bob", "user_key": "USER-KEY", - }), + }, Commands: map[string]bool{ "https_proxy='https://proxy.local' curl -LO https://www.chef.io/chef/install.sh": true, @@ -85,7 +86,7 @@ func TestResourceProvider_linuxInstallChefClient(t *testing.T) { }, "NoProxy": { - Config: testConfig(t, map[string]interface{}{ + Config: map[string]interface{}{ "http_proxy": "http://proxy.local", "no_proxy": []interface{}{"http://local.local", "http://local.org"}, "node_name": "nodename1", @@ -94,7 +95,7 @@ func TestResourceProvider_linuxInstallChefClient(t *testing.T) { "server_url": "https://chef.local", "user_name": "bob", "user_key": "USER-KEY", - }), + }, Commands: map[string]bool{ "http_proxy='http://proxy.local' no_proxy='http://local.local,http://local.org' " + @@ -107,7 +108,7 @@ func TestResourceProvider_linuxInstallChefClient(t *testing.T) { }, "Version": { - Config: testConfig(t, map[string]interface{}{ + Config: map[string]interface{}{ "node_name": "nodename1", "prevent_sudo": true, "run_list": []interface{}{"cookbook::recipe"}, @@ -115,7 +116,7 @@ func TestResourceProvider_linuxInstallChefClient(t *testing.T) { "user_name": "bob", "user_key": "USER-KEY", "version": "11.18.6", - }), + }, Commands: map[string]bool{ "curl -LO https://www.chef.io/chef/install.sh": true, @@ -131,7 +132,9 @@ func TestResourceProvider_linuxInstallChefClient(t *testing.T) { for k, tc := range cases { c.Commands = tc.Commands - p, err := decodeConfig(getTestResourceData(tc.Config)) + p, err := decodeConfig( + schema.TestResourceDataRaw(t, Provisioner().(*schema.Provisioner).Schema, tc.Config), + ) if err != nil { t.Fatalf("Error: %v", err) } @@ -147,12 +150,12 @@ func TestResourceProvider_linuxInstallChefClient(t *testing.T) { func TestResourceProvider_linuxCreateConfigFiles(t *testing.T) { cases := map[string]struct { - Config *terraform.ResourceConfig + Config map[string]interface{} Commands map[string]bool Uploads map[string]string }{ "Sudo": { - Config: testConfig(t, map[string]interface{}{ + Config: map[string]interface{}{ "ohai_hints": []interface{}{"test-fixtures/ohaihint.json"}, "node_name": "nodename1", "run_list": []interface{}{"cookbook::recipe"}, @@ -160,7 +163,7 @@ func TestResourceProvider_linuxCreateConfigFiles(t *testing.T) { "server_url": "https://chef.local", "user_name": "bob", "user_key": "USER-KEY", - }), + }, Commands: map[string]bool{ "sudo mkdir -p " + linuxConfDir: true, @@ -187,7 +190,7 @@ func TestResourceProvider_linuxCreateConfigFiles(t *testing.T) { }, "NoSudo": { - Config: testConfig(t, map[string]interface{}{ + Config: map[string]interface{}{ "node_name": "nodename1", "prevent_sudo": true, "run_list": []interface{}{"cookbook::recipe"}, @@ -195,7 +198,7 @@ func TestResourceProvider_linuxCreateConfigFiles(t *testing.T) { "server_url": "https://chef.local", "user_name": "bob", "user_key": "USER-KEY", - }), + }, Commands: map[string]bool{ "mkdir -p " + linuxConfDir: true, @@ -210,7 +213,7 @@ func TestResourceProvider_linuxCreateConfigFiles(t *testing.T) { }, "Proxy": { - Config: testConfig(t, map[string]interface{}{ + Config: map[string]interface{}{ "http_proxy": "http://proxy.local", "https_proxy": "https://proxy.local", "no_proxy": []interface{}{"http://local.local", "https://local.local"}, @@ -222,7 +225,7 @@ func TestResourceProvider_linuxCreateConfigFiles(t *testing.T) { "ssl_verify_mode": "verify_none", "user_name": "bob", "user_key": "USER-KEY", - }), + }, Commands: map[string]bool{ "mkdir -p " + linuxConfDir: true, @@ -237,7 +240,7 @@ func TestResourceProvider_linuxCreateConfigFiles(t *testing.T) { }, "Attributes JSON": { - Config: testConfig(t, map[string]interface{}{ + Config: map[string]interface{}{ "attributes_json": `{"key1":{"subkey1":{"subkey2a":["val1","val2","val3"],` + `"subkey2b":{"subkey3":"value3"}}},"key2":"value2"}`, "node_name": "nodename1", @@ -247,7 +250,7 @@ func TestResourceProvider_linuxCreateConfigFiles(t *testing.T) { "server_url": "https://chef.local", "user_name": "bob", "user_key": "USER-KEY", - }), + }, Commands: map[string]bool{ "mkdir -p " + linuxConfDir: true, @@ -270,7 +273,9 @@ func TestResourceProvider_linuxCreateConfigFiles(t *testing.T) { c.Commands = tc.Commands c.Uploads = tc.Uploads - p, err := decodeConfig(getTestResourceData(tc.Config)) + p, err := decodeConfig( + schema.TestResourceDataRaw(t, Provisioner().(*schema.Provisioner).Schema, tc.Config), + ) if err != nil { t.Fatalf("Error: %v", err) } diff --git a/builtin/provisioners/chef/resource_provisioner.go b/builtin/provisioners/chef/resource_provisioner.go index 28302ecac..7df7e93ca 100644 --- a/builtin/provisioners/chef/resource_provisioner.go +++ b/builtin/provisioners/chef/resource_provisioner.go @@ -2,6 +2,7 @@ package chef import ( "bytes" + "context" "encoding/json" "errors" "fmt" @@ -15,7 +16,6 @@ import ( "text/template" "time" - "context" "github.com/hashicorp/terraform/communicator" "github.com/hashicorp/terraform/communicator/remote" "github.com/hashicorp/terraform/helper/schema" @@ -82,9 +82,10 @@ enable_reporting false {{ end }} ` -// ProvisionerS represents a Chef provisioner -type ProvisionerS struct { - AttributesJSON string +type provisionFn func(terraform.UIOutput, communicator.Communicator) error + +type provisioner struct { + Attributes map[string]interface{} ClientOptions []string DisableReporting bool Environment string @@ -110,180 +111,153 @@ type ProvisionerS struct { SSLVerifyMode string UserName string UserKey string - VaultJSON string + Vaults map[string][]string Version string - attributes map[string]interface{} - vaults map[string][]string - cleanupUserKeyCmd string - createConfigFiles func(terraform.UIOutput, communicator.Communicator) error - installChefClient func(terraform.UIOutput, communicator.Communicator) error - fetchChefCertificates func(terraform.UIOutput, communicator.Communicator) error - generateClientKey func(terraform.UIOutput, communicator.Communicator) error - configureVaults func(terraform.UIOutput, communicator.Communicator) error - runChefClient func(terraform.UIOutput, communicator.Communicator) error + createConfigFiles provisionFn + installChefClient provisionFn + fetchChefCertificates provisionFn + generateClientKey provisionFn + configureVaults provisionFn + runChefClient provisionFn useSudo bool - - // Deprecated Fields - ValidationClientName string - ValidationKey string } +// Provisioner returns a Chef provisioner func Provisioner() terraform.ResourceProvisioner { return &schema.Provisioner{ Schema: map[string]*schema.Schema{ - "attributes_json": { - Type: schema.TypeString, - Optional: true, - }, - "client_options": { - Type: schema.TypeList, - Elem: schema.Schema{ - Type: schema.TypeString, - }, - Optional: true, - }, - "disable_reporting": { - Type: schema.TypeBool, - Optional: true, - }, - "environment": { - Type: schema.TypeString, - Optional: true, - }, - "fetch_chef_certificates": { - Type: schema.TypeBool, - Optional: true, - }, - "log_to_file": { - Type: schema.TypeBool, - Optional: true, - }, - "use_policyfile": { - Type: schema.TypeBool, - Optional: true, - }, - "policy_group": { - Type: schema.TypeString, - Optional: true, - }, - "policy_name": { - Type: schema.TypeString, - Optional: true, - }, - "http_proxy": { - Type: schema.TypeString, - Optional: true, - }, - "https_proxy": { - Type: schema.TypeString, - Optional: true, - }, - "no_proxy": { - Type: schema.TypeList, - Elem: schema.Schema{ - Type: schema.TypeString, - Optional: true, - }, - Optional: true, - }, - "named_run_list": { - Type: schema.TypeString, - Optional: true, - }, - "node_name": { + "node_name": &schema.Schema{ Type: schema.TypeString, Required: true, }, - "ohai_hints": { - Type: schema.TypeList, - Elem: schema.Schema{ - Type: schema.TypeString, - Optional: true, - }, - Optional: true, - }, - "os_type": { - Type: schema.TypeString, - Optional: true, - }, - "recreate_client": { - Type: schema.TypeBool, - Optional: true, - }, - "prevent_sudo": { - Type: schema.TypeBool, - Optional: true, - }, - "run_list": { - Type: schema.TypeList, - Elem: schema.Schema{ - Type: schema.TypeString, - Optional: true, - }, - Optional: true, - }, - "secret_key": { - Type: schema.TypeString, - Optional: true, - }, - "server_url": { + "server_url": &schema.Schema{ Type: schema.TypeString, Required: true, }, - "skip_install": { + "user_name": &schema.Schema{ + Type: schema.TypeString, + Required: true, + }, + "user_key": &schema.Schema{ + Type: schema.TypeString, + Required: true, + }, + + "attributes_json": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + }, + "client_options": &schema.Schema{ + Type: schema.TypeList, + Elem: &schema.Schema{Type: schema.TypeString}, + Optional: true, + }, + "disable_reporting": &schema.Schema{ Type: schema.TypeBool, Optional: true, }, - "skip_register": { + "environment": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + Default: defaultEnv, + }, + "fetch_chef_certificates": &schema.Schema{ Type: schema.TypeBool, Optional: true, }, - "ssl_verify_mode": { + "log_to_file": &schema.Schema{ + Type: schema.TypeBool, + Optional: true, + }, + "use_policyfile": &schema.Schema{ + Type: schema.TypeBool, + Optional: true, + }, + "policy_group": &schema.Schema{ Type: schema.TypeString, Optional: true, }, - "user_name": { + "policy_name": &schema.Schema{ Type: schema.TypeString, Optional: true, }, - "user_key": { + "http_proxy": &schema.Schema{ Type: schema.TypeString, Optional: true, }, - "vault_json": { + "https_proxy": &schema.Schema{ Type: schema.TypeString, Optional: true, }, - "version": { + "no_proxy": &schema.Schema{ + Type: schema.TypeList, + Elem: &schema.Schema{Type: schema.TypeString}, + Optional: true, + }, + "named_run_list": &schema.Schema{ Type: schema.TypeString, Optional: true, }, - - // Deprecated - "validation_client_name": { - Type: schema.TypeString, - Deprecated: "Please use user_name instead", - Optional: true, + "ohai_hints": &schema.Schema{ + Type: schema.TypeList, + Elem: &schema.Schema{Type: schema.TypeString}, + Optional: true, }, - - "validation_key": { - Type: schema.TypeString, - Deprecated: "Please use user_key instead", - Optional: true, + "os_type": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + }, + "recreate_client": &schema.Schema{ + Type: schema.TypeBool, + Optional: true, + }, + "prevent_sudo": &schema.Schema{ + Type: schema.TypeBool, + Optional: true, + }, + "run_list": &schema.Schema{ + Type: schema.TypeList, + Elem: &schema.Schema{Type: schema.TypeString}, + Optional: true, + }, + "secret_key": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + }, + "skip_install": &schema.Schema{ + Type: schema.TypeBool, + Optional: true, + }, + "skip_register": &schema.Schema{ + Type: schema.TypeBool, + Optional: true, + }, + "ssl_verify_mode": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + }, + "vault_json": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + }, + "version": &schema.Schema{ + Type: schema.TypeString, + Optional: true, }, }, - ApplyFunc: Apply, - ValidateFunc: Validate, + + ApplyFunc: applyFn, + ValidateFunc: validateFn, } } // TODO: Support context cancelling (Provisioner Stop) -// Apply executes the file provisioner -func Apply(ctx context.Context) error { +func applyFn(ctx context.Context) error { o := ctx.Value(schema.ProvOutputKey).(terraform.UIOutput) d := ctx.Value(schema.ProvConfigDataKey).(*schema.ResourceData) + // Decode the raw config for this provisioner p, err := decodeConfig(d) if err != nil { @@ -291,8 +265,7 @@ func Apply(ctx context.Context) error { } if p.OSType == "" { - t := d.State().Ephemeral.ConnInfo["type"] - switch t { + switch t := d.State().Ephemeral.ConnInfo["type"]; t { case "ssh", "": // The default connection type is ssh, so if the type is empty assume ssh p.OSType = "linux" case "winrm": @@ -334,8 +307,7 @@ func Apply(ctx context.Context) error { // Wait and retry until we establish the connection err = retryFunc(comm.Timeout(), func() error { - err := comm.Connect(o) - return err + return comm.Connect(o) }) if err != nil { return err @@ -377,7 +349,7 @@ func Apply(ctx context.Context) error { } } - if p.VaultJSON != "" { + if p.Vaults != nil { o.Output("Configure Chef vaults...") if err := p.configureVaults(o, comm); err != nil { return err @@ -396,8 +368,7 @@ func Apply(ctx context.Context) error { return nil } -// Validate checks if the required arguments are configured -func Validate(d *schema.ResourceData) (ws []string, es []error) { +func validateFn(d *schema.ResourceData) (ws []string, es []error) { p, err := decodeConfig(d) if err != nil { es = append(es, err) @@ -413,94 +384,11 @@ func Validate(d *schema.ResourceData) (ws []string, es []error) { if p.UsePolicyfile && p.PolicyGroup == "" { es = append(es, errors.New("Policyfile enabled but key not found: policy_group")) } - if p.UserName == "" && p.ValidationClientName == "" { - es = append(es, errors.New( - "One of user_name or the deprecated validation_client_name must be provided")) - } - if p.UserKey == "" && p.ValidationKey == "" { - es = append(es, errors.New( - "One of user_key or the deprecated validation_key must be provided")) - } - if p.ValidationKey != "" { - if p.RecreateClient { - es = append(es, errors.New( - "Cannot use recreate_client=true with the deprecated validation_key, please provide a user_key")) - } - if p.VaultJSON != "" { - es = append(es, errors.New( - "Cannot configure chef vaults using the deprecated validation_key, please provide a user_key")) - } - } return ws, es } -func decodeConfig(d *schema.ResourceData) (*ProvisionerS, error) { - p := decodeDataToProvisioner(d) - - // Make sure the supplied URL has a trailing slash - p.ServerURL = strings.TrimSuffix(p.ServerURL, "/") + "/" - - if p.Environment == "" { - p.Environment = defaultEnv - } - - for i, hint := range p.OhaiHints { - hintPath, err := homedir.Expand(hint) - if err != nil { - return nil, fmt.Errorf("Error expanding the path %s: %v", hint, err) - } - p.OhaiHints[i] = hintPath - } - - if p.UserName == "" && p.ValidationClientName != "" { - p.UserName = p.ValidationClientName - } - - if p.UserKey == "" && p.ValidationKey != "" { - p.UserKey = p.ValidationKey - } - - if attrs, ok := d.GetOk("attributes_json"); ok { - var m map[string]interface{} - if err := json.Unmarshal([]byte(attrs.(string)), &m); err != nil { - return nil, fmt.Errorf("Error parsing attributes_json: %v", err) - } - p.attributes = m - } - - if vaults, ok := d.GetOk("vault_json"); ok { - var m map[string]interface{} - if err := json.Unmarshal([]byte(vaults.(string)), &m); err != nil { - return nil, fmt.Errorf("Error parsing vault_json: %v", err) - } - - 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 -} - -func (p *ProvisionerS) deployConfigFiles( - o terraform.UIOutput, - comm communicator.Communicator, - confDir string) error { +func (p *provisioner) deployConfigFiles(o terraform.UIOutput, comm communicator.Communicator, confDir string) error { // Copy the user key to the new instance pk := strings.NewReader(p.UserKey) if err := comm.Upload(path.Join(confDir, p.UserName+".pem"), pk); err != nil { @@ -535,14 +423,14 @@ func (p *ProvisionerS) deployConfigFiles( } // Copy the client config to the new instance - if err := comm.Upload(path.Join(confDir, clienrb), &buf); err != nil { + if err = comm.Upload(path.Join(confDir, clienrb), &buf); err != nil { return fmt.Errorf("Uploading %s failed: %v", clienrb, err) } // Create a map with first boot settings fb := make(map[string]interface{}) - if p.attributes != nil { - fb = p.attributes + if p.Attributes != nil { + fb = p.Attributes } // Check if the run_list was also in the attributes and if so log a warning @@ -571,10 +459,7 @@ func (p *ProvisionerS) deployConfigFiles( return nil } -func (p *ProvisionerS) deployOhaiHints( - o terraform.UIOutput, - comm communicator.Communicator, - hintDir string) error { +func (p *provisioner) deployOhaiHints(o terraform.UIOutput, comm communicator.Communicator, hintDir string) error { for _, hint := range p.OhaiHints { // Open the hint file f, err := os.Open(hint) @@ -592,7 +477,7 @@ func (p *ProvisionerS) deployOhaiHints( return nil } -func (p *ProvisionerS) fetchChefCertificatesFunc( +func (p *provisioner) fetchChefCertificatesFunc( knifeCmd string, confDir string) func(terraform.UIOutput, communicator.Communicator) error { return func(o terraform.UIOutput, comm communicator.Communicator) error { @@ -603,10 +488,7 @@ func (p *ProvisionerS) fetchChefCertificatesFunc( } } -func (p *ProvisionerS) generateClientKeyFunc( - knifeCmd string, - confDir string, - noOutput string) func(terraform.UIOutput, communicator.Communicator) error { +func (p *provisioner) generateClientKeyFunc(knifeCmd string, confDir string, noOutput string) provisionFn { return func(o terraform.UIOutput, comm communicator.Communicator) error { options := fmt.Sprintf("-c %s -u %s --key %s", path.Join(confDir, clienrb), @@ -664,10 +546,7 @@ func (p *ProvisionerS) generateClientKeyFunc( } } -func (p *ProvisionerS) configureVaultsFunc( - gemCmd string, - knifeCmd string, - confDir string) func(terraform.UIOutput, communicator.Communicator) error { +func (p *provisioner) configureVaultsFunc(gemCmd string, knifeCmd string, confDir string) provisionFn { return func(o terraform.UIOutput, comm communicator.Communicator) error { if err := p.runCommand(o, comm, fmt.Sprintf("%s install chef-vault", gemCmd)); err != nil { return err @@ -679,7 +558,7 @@ func (p *ProvisionerS) configureVaultsFunc( path.Join(confDir, p.UserName+".pem"), ) - for vault, items := range p.vaults { + for vault, items := range p.Vaults { for _, item := range items { updateCmd := fmt.Sprintf("%s vault update %s %s -C %s -M client %s", knifeCmd, @@ -698,9 +577,7 @@ func (p *ProvisionerS) configureVaultsFunc( } } -func (p *ProvisionerS) runChefClientFunc( - chefCmd string, - confDir string) func(terraform.UIOutput, communicator.Communicator) error { +func (p *provisioner) runChefClientFunc(chefCmd string, confDir string) provisionFn { return func(o terraform.UIOutput, comm communicator.Communicator) error { fb := path.Join(confDir, firstBoot) var cmd string @@ -736,7 +613,7 @@ func (p *ProvisionerS) runChefClientFunc( } // Output implementation of terraform.UIOutput interface -func (p *ProvisionerS) Output(output string) { +func (p *provisioner) Output(output string) { logFile := path.Join(logfileDir, p.NodeName) f, err := os.OpenFile(logFile, os.O_APPEND|os.O_WRONLY, 0666) if err != nil { @@ -762,10 +639,7 @@ func (p *ProvisionerS) Output(output string) { } // runCommand is used to run already prepared commands -func (p *ProvisionerS) runCommand( - o terraform.UIOutput, - comm communicator.Communicator, - command string) error { +func (p *provisioner) runCommand(o terraform.UIOutput, comm communicator.Communicator, command string) error { // Unless prevented, prefix the command with sudo if p.useSudo { command = "sudo " + command @@ -804,7 +678,7 @@ func (p *ProvisionerS) runCommand( return err } -func (p *ProvisionerS) copyOutput(o terraform.UIOutput, r io.Reader, doneCh chan<- struct{}) { +func (p *provisioner) copyOutput(o terraform.UIOutput, r io.Reader, doneCh chan<- struct{}) { defer close(doneCh) lr := linereader.New(r) for line := range lr.Ch { @@ -830,9 +704,8 @@ func retryFunc(timeout time.Duration, f func() error) error { } } -func decodeDataToProvisioner(d *schema.ResourceData) *ProvisionerS { - return &ProvisionerS{ - AttributesJSON: d.Get("attributes_json").(string), +func decodeConfig(d *schema.ResourceData) (*provisioner, error) { + p := &provisioner{ ClientOptions: getStringList(d.Get("client_options")), DisableReporting: d.Get("disable_reporting").(bool), Environment: d.Get("environment").(string), @@ -858,13 +731,54 @@ func decodeDataToProvisioner(d *schema.ResourceData) *ProvisionerS { SSLVerifyMode: d.Get("ssl_verify_mode").(string), UserName: d.Get("user_name").(string), UserKey: d.Get("user_key").(string), - VaultJSON: d.Get("vault_json").(string), Version: d.Get("version").(string), - - // Deprecated - ValidationClientName: d.Get("validation_client_name").(string), - ValidationKey: d.Get("validation_key").(string), } + + // Make sure the supplied URL has a trailing slash + p.ServerURL = strings.TrimSuffix(p.ServerURL, "/") + "/" + + for i, hint := range p.OhaiHints { + hintPath, err := homedir.Expand(hint) + if err != nil { + return nil, fmt.Errorf("Error expanding the path %s: %v", hint, err) + } + p.OhaiHints[i] = hintPath + } + + if attrs, ok := d.GetOk("attributes_json"); ok { + var m map[string]interface{} + if err := json.Unmarshal([]byte(attrs.(string)), &m); err != nil { + return nil, fmt.Errorf("Error parsing attributes_json: %v", err) + } + p.Attributes = m + } + + if vaults, ok := d.GetOk("vault_json"); ok { + var m map[string]interface{} + if err := json.Unmarshal([]byte(vaults.(string)), &m); err != nil { + return nil, fmt.Errorf("Error parsing vault_json: %v", err) + } + + 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 } func getStringList(v interface{}) []string { diff --git a/builtin/provisioners/chef/resource_provisioner_test.go b/builtin/provisioners/chef/resource_provisioner_test.go index 3bb728d5d..f9e9f33fe 100644 --- a/builtin/provisioners/chef/resource_provisioner_test.go +++ b/builtin/provisioners/chef/resource_provisioner_test.go @@ -30,8 +30,8 @@ func TestResourceProvider_Validate_good(t *testing.T) { "user_name": "bob", "user_key": "USER-KEY", }) - r := Provisioner() - warn, errs := r.Validate(c) + + warn, errs := Provisioner().Validate(c) if len(warn) > 0 { t.Fatalf("Warnings: %v", warn) } @@ -44,8 +44,8 @@ func TestResourceProvider_Validate_bad(t *testing.T) { c := testConfig(t, map[string]interface{}{ "invalid": "nope", }) - p := Provisioner() - warn, errs := p.Validate(c) + + warn, errs := Provisioner().Validate(c) if len(warn) > 0 { t.Fatalf("Warnings: %v", warn) } @@ -66,8 +66,8 @@ func TestResourceProvider_Validate_computedValues(t *testing.T) { "user_key": "USER-KEY", "attributes_json": config.UnknownVariableValue, }) - r := Provisioner() - warn, errs := r.Validate(c) + + warn, errs := Provisioner().Validate(c) if len(warn) > 0 { t.Fatalf("Warnings: %v", warn) } @@ -76,30 +76,21 @@ func TestResourceProvider_Validate_computedValues(t *testing.T) { } } -func testConfig(t *testing.T, c map[string]interface{}) *terraform.ResourceConfig { - r, err := config.NewRawConfig(c) - if err != nil { - t.Fatalf("bad: %s", err) - } - - return terraform.NewResourceConfig(r) -} - func TestResourceProvider_runChefClient(t *testing.T) { cases := map[string]struct { - Config *terraform.ResourceConfig + Config map[string]interface{} ChefCmd string ConfDir string Commands map[string]bool }{ "Sudo": { - Config: testConfig(t, map[string]interface{}{ + Config: map[string]interface{}{ "node_name": "nodename1", "run_list": []interface{}{"cookbook::recipe"}, "server_url": "https://chef.local", "user_name": "bob", "user_key": "USER-KEY", - }), + }, ChefCmd: linuxChefCmd, @@ -113,14 +104,14 @@ func TestResourceProvider_runChefClient(t *testing.T) { }, "NoSudo": { - Config: testConfig(t, map[string]interface{}{ + Config: 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", - }), + }, ChefCmd: linuxChefCmd, @@ -134,7 +125,7 @@ func TestResourceProvider_runChefClient(t *testing.T) { }, "Environment": { - Config: testConfig(t, map[string]interface{}{ + Config: map[string]interface{}{ "environment": "production", "node_name": "nodename1", "prevent_sudo": true, @@ -142,7 +133,7 @@ func TestResourceProvider_runChefClient(t *testing.T) { "server_url": "https://chef.local", "user_name": "bob", "user_key": "USER-KEY", - }), + }, ChefCmd: windowsChefCmd, @@ -162,7 +153,9 @@ func TestResourceProvider_runChefClient(t *testing.T) { for k, tc := range cases { c.Commands = tc.Commands - p, err := decodeConfig(getTestResourceData(tc.Config)) + p, err := decodeConfig( + schema.TestResourceDataRaw(t, Provisioner().(*schema.Provisioner).Schema, tc.Config), + ) if err != nil { t.Fatalf("Error: %v", err) } @@ -179,20 +172,20 @@ func TestResourceProvider_runChefClient(t *testing.T) { func TestResourceProvider_fetchChefCertificates(t *testing.T) { cases := map[string]struct { - Config *terraform.ResourceConfig + Config map[string]interface{} KnifeCmd string ConfDir string Commands map[string]bool }{ "Sudo": { - Config: testConfig(t, map[string]interface{}{ + Config: map[string]interface{}{ "fetch_chef_certificates": true, "node_name": "nodename1", "run_list": []interface{}{"cookbook::recipe"}, "server_url": "https://chef.local", "user_name": "bob", "user_key": "USER-KEY", - }), + }, KnifeCmd: linuxKnifeCmd, @@ -206,7 +199,7 @@ func TestResourceProvider_fetchChefCertificates(t *testing.T) { }, "NoSudo": { - Config: testConfig(t, map[string]interface{}{ + Config: map[string]interface{}{ "fetch_chef_certificates": true, "node_name": "nodename1", "prevent_sudo": true, @@ -214,7 +207,7 @@ func TestResourceProvider_fetchChefCertificates(t *testing.T) { "server_url": "https://chef.local", "user_name": "bob", "user_key": "USER-KEY", - }), + }, KnifeCmd: windowsKnifeCmd, @@ -234,7 +227,9 @@ func TestResourceProvider_fetchChefCertificates(t *testing.T) { for k, tc := range cases { c.Commands = tc.Commands - p, err := decodeConfig(getTestResourceData(tc.Config)) + p, err := decodeConfig( + schema.TestResourceDataRaw(t, Provisioner().(*schema.Provisioner).Schema, tc.Config), + ) if err != nil { t.Fatalf("Error: %v", err) } @@ -251,14 +246,14 @@ func TestResourceProvider_fetchChefCertificates(t *testing.T) { func TestResourceProvider_configureVaults(t *testing.T) { cases := map[string]struct { - Config *terraform.ResourceConfig + Config map[string]interface{} GemCmd string KnifeCmd string ConfDir string Commands map[string]bool }{ "Linux Vault string": { - Config: testConfig(t, map[string]interface{}{ + Config: map[string]interface{}{ "node_name": "nodename1", "prevent_sudo": true, "run_list": []interface{}{"cookbook::recipe"}, @@ -266,7 +261,7 @@ func TestResourceProvider_configureVaults(t *testing.T) { "user_name": "bob", "user_key": "USER-KEY", "vault_json": `{"vault1": "item1"}`, - }), + }, GemCmd: linuxGemCmd, KnifeCmd: linuxKnifeCmd, @@ -280,7 +275,7 @@ func TestResourceProvider_configureVaults(t *testing.T) { }, "Linux Vault []string": { - Config: testConfig(t, map[string]interface{}{ + Config: map[string]interface{}{ "fetch_chef_certificates": true, "node_name": "nodename1", "prevent_sudo": true, @@ -289,7 +284,7 @@ func TestResourceProvider_configureVaults(t *testing.T) { "user_name": "bob", "user_key": "USER-KEY", "vault_json": `{"vault1": ["item1", "item2"]}`, - }), + }, GemCmd: linuxGemCmd, KnifeCmd: linuxKnifeCmd, @@ -305,7 +300,7 @@ func TestResourceProvider_configureVaults(t *testing.T) { }, "Windows Vault string": { - Config: testConfig(t, map[string]interface{}{ + Config: map[string]interface{}{ "node_name": "nodename1", "prevent_sudo": true, "run_list": []interface{}{"cookbook::recipe"}, @@ -313,7 +308,7 @@ func TestResourceProvider_configureVaults(t *testing.T) { "user_name": "bob", "user_key": "USER-KEY", "vault_json": `{"vault1": "item1"}`, - }), + }, GemCmd: windowsGemCmd, KnifeCmd: windowsKnifeCmd, @@ -327,7 +322,7 @@ func TestResourceProvider_configureVaults(t *testing.T) { }, "Windows Vault []string": { - Config: testConfig(t, map[string]interface{}{ + Config: map[string]interface{}{ "fetch_chef_certificates": true, "node_name": "nodename1", "prevent_sudo": true, @@ -336,7 +331,7 @@ func TestResourceProvider_configureVaults(t *testing.T) { "user_name": "bob", "user_key": "USER-KEY", "vault_json": `{"vault1": ["item1", "item2"]}`, - }), + }, GemCmd: windowsGemCmd, KnifeCmd: windowsKnifeCmd, @@ -358,7 +353,9 @@ func TestResourceProvider_configureVaults(t *testing.T) { for k, tc := range cases { c.Commands = tc.Commands - p, err := decodeConfig(getTestResourceData(tc.Config)) + p, err := decodeConfig( + schema.TestResourceDataRaw(t, Provisioner().(*schema.Provisioner).Schema, tc.Config), + ) if err != nil { t.Fatalf("Error: %v", err) } @@ -373,6 +370,11 @@ func TestResourceProvider_configureVaults(t *testing.T) { } } -func getTestResourceData(c *terraform.ResourceConfig) *schema.ResourceData { - return schema.TestResourceDataConfig(Provisioner().(*schema.Provisioner).Schema, c) +func testConfig(t *testing.T, c map[string]interface{}) *terraform.ResourceConfig { + r, err := config.NewRawConfig(c) + if err != nil { + t.Fatalf("bad: %s", err) + } + + return terraform.NewResourceConfig(r) } diff --git a/builtin/provisioners/chef/windows_provisioner.go b/builtin/provisioners/chef/windows_provisioner.go index 773d1b0f2..8713affcd 100644 --- a/builtin/provisioners/chef/windows_provisioner.go +++ b/builtin/provisioners/chef/windows_provisioner.go @@ -46,9 +46,7 @@ Write-Host 'Installing Chef Client...' Start-Process -FilePath msiexec -ArgumentList /qn, /i, $dest -Wait ` -func (p *ProvisionerS) windowsInstallChefClient( - o terraform.UIOutput, - comm communicator.Communicator) error { +func (p *provisioner) windowsInstallChefClient(o terraform.UIOutput, comm communicator.Communicator) error { script := path.Join(path.Dir(comm.ScriptPath()), "ChefClient.ps1") content := fmt.Sprintf(installScript, p.Version, p.HTTPProxy, strings.Join(p.NOProxy, ",")) @@ -62,9 +60,7 @@ func (p *ProvisionerS) windowsInstallChefClient( return p.runCommand(o, comm, installCmd) } -func (p *ProvisionerS) windowsCreateConfigFiles( - o terraform.UIOutput, - comm communicator.Communicator) error { +func (p *provisioner) windowsCreateConfigFiles(o terraform.UIOutput, comm communicator.Communicator) error { // Make sure the config directory exists cmd := fmt.Sprintf("cmd /c if not exist %q mkdir %q", windowsConfDir, windowsConfDir) if err := p.runCommand(o, comm, cmd); err != nil { diff --git a/builtin/provisioners/chef/windows_provisioner_test.go b/builtin/provisioners/chef/windows_provisioner_test.go index 0bb0dc126..566f8c457 100644 --- a/builtin/provisioners/chef/windows_provisioner_test.go +++ b/builtin/provisioners/chef/windows_provisioner_test.go @@ -6,23 +6,24 @@ import ( "testing" "github.com/hashicorp/terraform/communicator" + "github.com/hashicorp/terraform/helper/schema" "github.com/hashicorp/terraform/terraform" ) func TestResourceProvider_windowsInstallChefClient(t *testing.T) { cases := map[string]struct { - Config *terraform.ResourceConfig + Config map[string]interface{} Commands map[string]bool UploadScripts map[string]string }{ "Default": { - Config: testConfig(t, map[string]interface{}{ + Config: map[string]interface{}{ "node_name": "nodename1", "run_list": []interface{}{"cookbook::recipe"}, "server_url": "https://chef.local", "user_name": "bob", "user_key": "USER-KEY", - }), + }, Commands: map[string]bool{ "powershell -NoProfile -ExecutionPolicy Bypass -File ChefClient.ps1": true, @@ -34,7 +35,7 @@ func TestResourceProvider_windowsInstallChefClient(t *testing.T) { }, "Proxy": { - Config: testConfig(t, map[string]interface{}{ + Config: map[string]interface{}{ "http_proxy": "http://proxy.local", "no_proxy": []interface{}{"http://local.local", "http://local.org"}, "node_name": "nodename1", @@ -42,7 +43,7 @@ func TestResourceProvider_windowsInstallChefClient(t *testing.T) { "server_url": "https://chef.local", "user_name": "bob", "user_key": "USER-KEY", - }), + }, Commands: map[string]bool{ "powershell -NoProfile -ExecutionPolicy Bypass -File ChefClient.ps1": true, @@ -54,14 +55,14 @@ func TestResourceProvider_windowsInstallChefClient(t *testing.T) { }, "Version": { - Config: testConfig(t, map[string]interface{}{ + Config: map[string]interface{}{ "node_name": "nodename1", "run_list": []interface{}{"cookbook::recipe"}, "server_url": "https://chef.local", "user_name": "bob", "user_key": "USER-KEY", "version": "11.18.6", - }), + }, Commands: map[string]bool{ "powershell -NoProfile -ExecutionPolicy Bypass -File ChefClient.ps1": true, @@ -80,7 +81,9 @@ func TestResourceProvider_windowsInstallChefClient(t *testing.T) { c.Commands = tc.Commands c.UploadScripts = tc.UploadScripts - p, err := decodeConfig(getTestResourceData(tc.Config)) + p, err := decodeConfig( + schema.TestResourceDataRaw(t, Provisioner().(*schema.Provisioner).Schema, tc.Config), + ) if err != nil { t.Fatalf("Error: %v", err) } @@ -96,12 +99,12 @@ func TestResourceProvider_windowsInstallChefClient(t *testing.T) { func TestResourceProvider_windowsCreateConfigFiles(t *testing.T) { cases := map[string]struct { - Config *terraform.ResourceConfig + Config map[string]interface{} Commands map[string]bool Uploads map[string]string }{ "Default": { - Config: testConfig(t, map[string]interface{}{ + Config: map[string]interface{}{ "ohai_hints": []interface{}{"test-fixtures/ohaihint.json"}, "node_name": "nodename1", "run_list": []interface{}{"cookbook::recipe"}, @@ -109,7 +112,7 @@ func TestResourceProvider_windowsCreateConfigFiles(t *testing.T) { "server_url": "https://chef.local", "user_name": "bob", "user_key": "USER-KEY", - }), + }, Commands: map[string]bool{ fmt.Sprintf("cmd /c if not exist %q mkdir %q", windowsConfDir, windowsConfDir): true, @@ -128,7 +131,7 @@ func TestResourceProvider_windowsCreateConfigFiles(t *testing.T) { }, "Proxy": { - Config: testConfig(t, map[string]interface{}{ + Config: map[string]interface{}{ "http_proxy": "http://proxy.local", "https_proxy": "https://proxy.local", "no_proxy": []interface{}{"http://local.local", "https://local.local"}, @@ -139,7 +142,7 @@ func TestResourceProvider_windowsCreateConfigFiles(t *testing.T) { "ssl_verify_mode": "verify_none", "user_name": "bob", "user_key": "USER-KEY", - }), + }, Commands: map[string]bool{ fmt.Sprintf("cmd /c if not exist %q mkdir %q", windowsConfDir, windowsConfDir): true, @@ -154,7 +157,7 @@ func TestResourceProvider_windowsCreateConfigFiles(t *testing.T) { }, "Attributes JSON": { - Config: testConfig(t, map[string]interface{}{ + Config: map[string]interface{}{ "attributes_json": `{"key1":{"subkey1":{"subkey2a":["val1","val2","val3"],` + `"subkey2b":{"subkey3":"value3"}}},"key2":"value2"}`, "node_name": "nodename1", @@ -163,7 +166,7 @@ func TestResourceProvider_windowsCreateConfigFiles(t *testing.T) { "server_url": "https://chef.local", "user_name": "bob", "user_key": "USER-KEY", - }), + }, Commands: map[string]bool{ fmt.Sprintf("cmd /c if not exist %q mkdir %q", windowsConfDir, windowsConfDir): true, @@ -186,7 +189,9 @@ func TestResourceProvider_windowsCreateConfigFiles(t *testing.T) { c.Commands = tc.Commands c.Uploads = tc.Uploads - p, err := decodeConfig(getTestResourceData(tc.Config)) + p, err := decodeConfig( + schema.TestResourceDataRaw(t, Provisioner().(*schema.Provisioner).Schema, tc.Config), + ) if err != nil { t.Fatalf("Error: %v", err) } diff --git a/builtin/provisioners/file/resource_provisioner.go b/builtin/provisioners/file/resource_provisioner.go index eb29424b4..2c9c7298a 100644 --- a/builtin/provisioners/file/resource_provisioner.go +++ b/builtin/provisioners/file/resource_provisioner.go @@ -36,7 +36,7 @@ func Provisioner() terraform.ResourceProvisioner { }, ApplyFunc: applyFn, - ValidateFunc: Validate, + ValidateFunc: validateFn, } } @@ -78,8 +78,7 @@ func applyFn(ctx context.Context) error { } } -// Validate checks if the required arguments are configured -func Validate(d *schema.ResourceData) (ws []string, es []error) { +func validateFn(d *schema.ResourceData) (ws []string, es []error) { numSrc := 0 if _, ok := d.GetOk("source"); ok == true { numSrc++ diff --git a/builtin/provisioners/file/resource_provisioner_test.go b/builtin/provisioners/file/resource_provisioner_test.go index 96afc519f..2dacab35e 100644 --- a/builtin/provisioners/file/resource_provisioner_test.go +++ b/builtin/provisioners/file/resource_provisioner_test.go @@ -23,8 +23,8 @@ func TestResourceProvider_Validate_good_source(t *testing.T) { "source": "/tmp/foo", "destination": "/tmp/bar", }) - p := Provisioner() - warn, errs := p.Validate(c) + + warn, errs := Provisioner().Validate(c) if len(warn) > 0 { t.Fatalf("Warnings: %v", warn) } @@ -38,8 +38,8 @@ func TestResourceProvider_Validate_good_content(t *testing.T) { "content": "value to copy", "destination": "/tmp/bar", }) - p := Provisioner() - warn, errs := p.Validate(c) + + warn, errs := Provisioner().Validate(c) if len(warn) > 0 { t.Fatalf("Warnings: %v", warn) } @@ -52,8 +52,8 @@ func TestResourceProvider_Validate_bad_not_destination(t *testing.T) { c := testConfig(t, map[string]interface{}{ "source": "nope", }) - p := Provisioner() - warn, errs := p.Validate(c) + + warn, errs := Provisioner().Validate(c) if len(warn) > 0 { t.Fatalf("Warnings: %v", warn) } @@ -66,8 +66,8 @@ func TestResourceProvider_Validate_bad_no_source(t *testing.T) { c := testConfig(t, map[string]interface{}{ "destination": "/tmp/bar", }) - p := Provisioner() - warn, errs := p.Validate(c) + + warn, errs := Provisioner().Validate(c) if len(warn) > 0 { t.Fatalf("Warnings: %v", warn) } @@ -82,8 +82,8 @@ func TestResourceProvider_Validate_bad_to_many_src(t *testing.T) { "content": "value to copy", "destination": "/tmp/bar", }) - p := Provisioner() - warn, errs := p.Validate(c) + + warn, errs := Provisioner().Validate(c) if len(warn) > 0 { t.Fatalf("Warnings: %v", warn) } @@ -92,12 +92,11 @@ func TestResourceProvider_Validate_bad_to_many_src(t *testing.T) { } } -func testConfig( - t *testing.T, - c map[string]interface{}) *terraform.ResourceConfig { +func testConfig(t *testing.T, c map[string]interface{}) *terraform.ResourceConfig { r, err := config.NewRawConfig(c) if err != nil { t.Fatalf("bad: %s", err) } + return terraform.NewResourceConfig(r) } diff --git a/builtin/provisioners/local-exec/resource_provisioner_test.go b/builtin/provisioners/local-exec/resource_provisioner_test.go index 85fb6abcf..2fa17efc5 100644 --- a/builtin/provisioners/local-exec/resource_provisioner_test.go +++ b/builtin/provisioners/local-exec/resource_provisioner_test.go @@ -30,6 +30,7 @@ func TestResourceProvider_Apply(t *testing.T) { output := new(terraform.MockUIOutput) p := Provisioner() + if err := p.Apply(output, nil, c); err != nil { t.Fatalf("err: %v", err) } @@ -84,8 +85,8 @@ func TestResourceProvider_Validate_good(t *testing.T) { c := testConfig(t, map[string]interface{}{ "command": "echo foo", }) - p := Provisioner() - warn, errs := p.Validate(c) + + warn, errs := Provisioner().Validate(c) if len(warn) > 0 { t.Fatalf("Warnings: %v", warn) } @@ -96,8 +97,8 @@ func TestResourceProvider_Validate_good(t *testing.T) { func TestResourceProvider_Validate_missing(t *testing.T) { c := testConfig(t, map[string]interface{}{}) - p := Provisioner() - warn, errs := p.Validate(c) + + warn, errs := Provisioner().Validate(c) if len(warn) > 0 { t.Fatalf("Warnings: %v", warn) } @@ -106,9 +107,7 @@ func TestResourceProvider_Validate_missing(t *testing.T) { } } -func testConfig( - t *testing.T, - c map[string]interface{}) *terraform.ResourceConfig { +func testConfig(t *testing.T, c map[string]interface{}) *terraform.ResourceConfig { r, err := config.NewRawConfig(c) if err != nil { t.Fatalf("bad: %s", err) diff --git a/builtin/provisioners/remote-exec/resource_provisioner_test.go b/builtin/provisioners/remote-exec/resource_provisioner_test.go index 53e943680..67faf1fe4 100644 --- a/builtin/provisioners/remote-exec/resource_provisioner_test.go +++ b/builtin/provisioners/remote-exec/resource_provisioner_test.go @@ -30,8 +30,8 @@ func TestResourceProvider_Validate_good(t *testing.T) { c := testConfig(t, map[string]interface{}{ "inline": "echo foo", }) - p := Provisioner() - warn, errs := p.Validate(c) + + warn, errs := Provisioner().Validate(c) if len(warn) > 0 { t.Fatalf("Warnings: %v", warn) } @@ -44,8 +44,8 @@ func TestResourceProvider_Validate_bad(t *testing.T) { c := testConfig(t, map[string]interface{}{ "invalid": "nope", }) - p := Provisioner() - warn, errs := p.Validate(c) + + warn, errs := Provisioner().Validate(c) if len(warn) > 0 { t.Fatalf("Warnings: %v", warn) } @@ -60,7 +60,6 @@ exit 0 ` func TestResourceProvider_generateScript(t *testing.T) { - p := Provisioner().(*schema.Provisioner) conf := map[string]interface{}{ "inline": []interface{}{ "cd /tmp", @@ -68,8 +67,10 @@ func TestResourceProvider_generateScript(t *testing.T) { "exit 0", }, } - out, err := generateScripts(schema.TestResourceDataRaw( - t, p.Schema, conf)) + + out, err := generateScripts( + schema.TestResourceDataRaw(t, Provisioner().(*schema.Provisioner).Schema, conf), + ) if err != nil { t.Fatalf("err: %v", err) } @@ -101,7 +102,6 @@ func TestResourceProvider_generateScriptEmptyInline(t *testing.T) { } func TestResourceProvider_CollectScripts_inline(t *testing.T) { - p := Provisioner().(*schema.Provisioner) conf := map[string]interface{}{ "inline": []interface{}{ "cd /tmp", @@ -110,8 +110,9 @@ func TestResourceProvider_CollectScripts_inline(t *testing.T) { }, } - scripts, err := collectScripts(schema.TestResourceDataRaw( - t, p.Schema, conf)) + scripts, err := collectScripts( + schema.TestResourceDataRaw(t, Provisioner().(*schema.Provisioner).Schema, conf), + ) if err != nil { t.Fatalf("err: %v", err) } @@ -132,13 +133,13 @@ func TestResourceProvider_CollectScripts_inline(t *testing.T) { } func TestResourceProvider_CollectScripts_script(t *testing.T) { - p := Provisioner().(*schema.Provisioner) conf := map[string]interface{}{ "script": "test-fixtures/script1.sh", } - scripts, err := collectScripts(schema.TestResourceDataRaw( - t, p.Schema, conf)) + scripts, err := collectScripts( + schema.TestResourceDataRaw(t, Provisioner().(*schema.Provisioner).Schema, conf), + ) if err != nil { t.Fatalf("err: %v", err) } @@ -159,7 +160,6 @@ func TestResourceProvider_CollectScripts_script(t *testing.T) { } func TestResourceProvider_CollectScripts_scripts(t *testing.T) { - p := Provisioner().(*schema.Provisioner) conf := map[string]interface{}{ "scripts": []interface{}{ "test-fixtures/script1.sh", @@ -168,8 +168,9 @@ func TestResourceProvider_CollectScripts_scripts(t *testing.T) { }, } - scripts, err := collectScripts(schema.TestResourceDataRaw( - t, p.Schema, conf)) + scripts, err := collectScripts( + schema.TestResourceDataRaw(t, Provisioner().(*schema.Provisioner).Schema, conf), + ) if err != nil { t.Fatalf("err: %v", err) } @@ -234,9 +235,7 @@ func TestRetryFunc(t *testing.T) { } } -func testConfig( - t *testing.T, - c map[string]interface{}) *terraform.ResourceConfig { +func testConfig(t *testing.T, c map[string]interface{}) *terraform.ResourceConfig { r, err := config.NewRawConfig(c) if err != nil { t.Fatalf("bad: %s", err) diff --git a/helper/schema/provisioner.go b/helper/schema/provisioner.go index 926df8893..856c6758a 100644 --- a/helper/schema/provisioner.go +++ b/helper/schema/provisioner.go @@ -41,9 +41,8 @@ type Provisioner struct { // information. ApplyFunc func(ctx context.Context) error - // ValidateFunc is the function for extended validation. This is optional. - // It is given a resource data. - // Should be provided when Scheme is not enough. + // ValidateFunc is a function for extended validation. This is optional + // and should be used when individual field validation is not enough. ValidateFunc func(*ResourceData) ([]string, []error) stopCtx context.Context diff --git a/helper/schema/testing.go b/helper/schema/testing.go index bbc7b0cb8..9765bdbc6 100644 --- a/helper/schema/testing.go +++ b/helper/schema/testing.go @@ -28,10 +28,3 @@ func TestResourceDataRaw( return result } - -func TestResourceDataConfig(schema map[string]*Schema, config *terraform.ResourceConfig) *ResourceData { - return &ResourceData{ - schema: schema, - config: config, - } -} diff --git a/website/source/docs/provisioners/chef.html.markdown b/website/source/docs/provisioners/chef.html.markdown index 8b9557f81..34fff2cd2 100644 --- a/website/source/docs/provisioners/chef.html.markdown +++ b/website/source/docs/provisioners/chef.html.markdown @@ -157,9 +157,3 @@ The following arguments are supported: * `version (string)` - (Optional) The Chef Client version to install on the remote machine. If not set, the latest available version will be installed. - -These options are supported for backwards compatibility and may be removed in a -future version: - -* `validation_client_name (string)` - __Deprecated: please use `user_name` instead__. -* `validation_key (string)` - __Deprecated: please use `user_key` instead__.