From e87df48f969f27cb850e7b648ecf435b820d6f3f Mon Sep 17 00:00:00 2001 From: Vladislav Rassokhin Date: Tue, 29 Nov 2016 23:00:26 +0300 Subject: [PATCH 1/9] Add `schema.Provisioner` helper --- helper/schema/provisioner.go | 66 ++++++++++++++++++++++++ helper/schema/provisioner_test.go | 85 +++++++++++++++++++++++++++++++ 2 files changed, 151 insertions(+) create mode 100644 helper/schema/provisioner.go create mode 100644 helper/schema/provisioner_test.go diff --git a/helper/schema/provisioner.go b/helper/schema/provisioner.go new file mode 100644 index 000000000000..50306f99067d --- /dev/null +++ b/helper/schema/provisioner.go @@ -0,0 +1,66 @@ +package schema + +import ( + "errors" + "fmt" + "github.com/hashicorp/go-multierror" + "github.com/hashicorp/terraform/terraform" +) + +type Provisioner struct { + Schema map[string]*Schema + ValidateFunc ValidateFunc + ApplyFunc ApplyFunc +} + +type ValidateFunc func(*terraform.ResourceConfig) ([]string, []error) +type ApplyFunc func(terraform.UIOutput, *terraform.InstanceState, *terraform.ResourceConfig) error + +// InternalValidate should be called to validate the structure +// of the provisioner. +// +// This should be called in a unit test for any provisioner to verify +// before release that a provisioner is properly configured for use with +// this library. +func (p *Provisioner) InternalValidate() error { + if p == nil { + return errors.New("provisioner is nil") + } + + var validationErrors error + sm := schemaMap(p.Schema) + if err := sm.InternalValidate(sm); err != nil { + validationErrors = multierror.Append(validationErrors, err) + } + + return validationErrors +} + +func (p *Provisioner) Validate(config *terraform.ResourceConfig) ([]string, []error) { + if err := p.InternalValidate(); err != nil { + return nil, []error{fmt.Errorf( + "Internal validation of the provisioner failed! This is always a bug\n"+ + "with the provisioner itself, and not a user issue. Please report\n"+ + "this bug:\n\n%s", err)} + } + w := []string{} + e := []error{} + if p.Schema != nil { + w2, e2 := schemaMap(p.Schema).Validate(config) + w = append(w, w2...) + e = append(e, e2...) + } + if p.ValidateFunc != nil { + w2, e2 := p.ValidateFunc(config) + w = append(w, w2...) + e = append(e, e2...) + } + return w, e +} + +func (p *Provisioner) Apply(ui terraform.UIOutput, state *terraform.InstanceState, config *terraform.ResourceConfig) error { + if p.ApplyFunc == nil { + panic("ApplyFunc should be specified in provisioner") + } + return p.ApplyFunc(ui, state, config) +} diff --git a/helper/schema/provisioner_test.go b/helper/schema/provisioner_test.go new file mode 100644 index 000000000000..1594e89ca1cb --- /dev/null +++ b/helper/schema/provisioner_test.go @@ -0,0 +1,85 @@ +package schema + +import ( + "github.com/hashicorp/terraform/config" + "github.com/hashicorp/terraform/terraform" + "reflect" + "testing" +) + +func TestProvisioner_init(t *testing.T) { + var _ terraform.ResourceProvisioner = new(Provisioner) +} + +func TestProvisioner_Validate(t *testing.T) { + cases := []struct { + P *Provisioner + Config map[string]interface{} + Warns []string + Err bool + }{ + { + // Incorrect schema + P: &Provisioner{ + Schema: map[string]*Schema{ + "foo": {}, + }, + }, + Config: nil, + Err: true, + }, + { + P: &Provisioner{ + Schema: map[string]*Schema{ + "foo": { + Type: TypeString, + Optional: true, + ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) { + ws = append(ws, "Simple warning from property validation") + return + }, + }, + }, + }, + Config: map[string]interface{}{ + "foo": "", + }, + Err: false, + Warns: []string{"Simple warning from property validation"}, + }, + { + P: &Provisioner{ + Schema: nil, + }, + Config: nil, + Err: false, + }, + { + P: &Provisioner{ + Schema: nil, + ValidateFunc: func(*terraform.ResourceConfig) (ws []string, errors []error) { + ws = append(ws, "Simple warning from provisioner ValidateFunc") + return + }, + }, + Config: nil, + Err: false, + Warns: []string{"Simple warning from provisioner ValidateFunc"}, + }, + } + + for i, tc := range cases { + c, err := config.NewRawConfig(tc.Config) + if err != nil { + t.Fatalf("err: %s", err) + } + + ws, es := tc.P.Validate(terraform.NewResourceConfig(c)) + if len(es) > 0 != tc.Err { + t.Fatalf("%d: %#v %s", i, es, es) + } + if (tc.Warns != nil || len(ws) != 0) && !reflect.DeepEqual(ws, tc.Warns) { + t.Fatalf("%d: warnings mismatch, actual: %#v", i, ws) + } + } +} From 12e68d0e9a613cbecd57b822cc00c164358ce76c Mon Sep 17 00:00:00 2001 From: Vladislav Rassokhin Date: Tue, 29 Nov 2016 23:01:39 +0300 Subject: [PATCH 2/9] Use `schema.Provisioner` helper in built-in provisioner: chef --- builtin/bins/provisioner-chef/main.go | 5 +---- .../chef/linux_provisioner_test.go | 6 ++--- .../provisioners/chef/resource_provisioner.go | 20 +++++++++++------ .../chef/resource_provisioner_test.go | 22 +++++++++++-------- .../chef/windows_provisioner_test.go | 6 ++--- 5 files changed, 31 insertions(+), 28 deletions(-) diff --git a/builtin/bins/provisioner-chef/main.go b/builtin/bins/provisioner-chef/main.go index a12c65cf7607..06da8df19e7b 100644 --- a/builtin/bins/provisioner-chef/main.go +++ b/builtin/bins/provisioner-chef/main.go @@ -3,13 +3,10 @@ package main import ( "github.com/hashicorp/terraform/builtin/provisioners/chef" "github.com/hashicorp/terraform/plugin" - "github.com/hashicorp/terraform/terraform" ) func main() { plugin.Serve(&plugin.ServeOpts{ - ProvisionerFunc: func() terraform.ResourceProvisioner { - return new(chef.ResourceProvisioner) - }, + ProvisionerFunc: chef.ResourceProvisioner, }) } diff --git a/builtin/provisioners/chef/linux_provisioner_test.go b/builtin/provisioners/chef/linux_provisioner_test.go index 7d2339e04f67..e0e536f22058 100644 --- a/builtin/provisioners/chef/linux_provisioner_test.go +++ b/builtin/provisioners/chef/linux_provisioner_test.go @@ -125,14 +125,13 @@ func TestResourceProvider_linuxInstallChefClient(t *testing.T) { }, } - 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) + p, err := decodeConfig(tc.Config) if err != nil { t.Fatalf("Error: %v", err) } @@ -264,7 +263,6 @@ func TestResourceProvider_linuxCreateConfigFiles(t *testing.T) { }, } - r := new(ResourceProvisioner) o := new(terraform.MockUIOutput) c := new(communicator.MockCommunicator) @@ -272,7 +270,7 @@ func TestResourceProvider_linuxCreateConfigFiles(t *testing.T) { c.Commands = tc.Commands c.Uploads = tc.Uploads - p, err := r.decodeConfig(tc.Config) + p, err := decodeConfig(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 0e693974ef37..b35d6a4f89e4 100644 --- a/builtin/provisioners/chef/resource_provisioner.go +++ b/builtin/provisioners/chef/resource_provisioner.go @@ -17,6 +17,7 @@ import ( "github.com/hashicorp/terraform/communicator" "github.com/hashicorp/terraform/communicator/remote" + "github.com/hashicorp/terraform/helper/schema" "github.com/hashicorp/terraform/terraform" "github.com/mitchellh/go-homedir" "github.com/mitchellh/go-linereader" @@ -128,16 +129,21 @@ type Provisioner struct { ValidationKey string `mapstructure:"validation_key"` } -// ResourceProvisioner represents a generic chef provisioner -type ResourceProvisioner struct{} +func ResourceProvisioner() terraform.ResourceProvisioner { + return &schema.Provisioner{ + Schema: nil, // TODO: Fill from Provisioner struct, note 'required' tag + ApplyFunc: Apply, + ValidateFunc: Validate, + } +} // Apply executes the file provisioner -func (r *ResourceProvisioner) Apply( +func Apply( o terraform.UIOutput, s *terraform.InstanceState, c *terraform.ResourceConfig) error { // Decode the raw config for this provisioner - p, err := r.decodeConfig(c) + p, err := decodeConfig(c) if err != nil { return err } @@ -248,8 +254,8 @@ func (r *ResourceProvisioner) Apply( } // Validate checks if the required arguments are configured -func (r *ResourceProvisioner) Validate(c *terraform.ResourceConfig) (ws []string, es []error) { - p, err := r.decodeConfig(c) +func Validate(c *terraform.ResourceConfig) (ws []string, es []error) { + p, err := decodeConfig(c) if err != nil { es = append(es, err) return ws, es @@ -297,7 +303,7 @@ func (r *ResourceProvisioner) Validate(c *terraform.ResourceConfig) (ws []string return ws, es } -func (r *ResourceProvisioner) decodeConfig(c *terraform.ResourceConfig) (*Provisioner, error) { +func decodeConfig(c *terraform.ResourceConfig) (*Provisioner, error) { p := new(Provisioner) decConf := &mapstructure.DecoderConfig{ diff --git a/builtin/provisioners/chef/resource_provisioner_test.go b/builtin/provisioners/chef/resource_provisioner_test.go index 692c89f92f5c..4da0645a2017 100644 --- a/builtin/provisioners/chef/resource_provisioner_test.go +++ b/builtin/provisioners/chef/resource_provisioner_test.go @@ -7,11 +7,18 @@ import ( "github.com/hashicorp/terraform/communicator" "github.com/hashicorp/terraform/config" + "github.com/hashicorp/terraform/helper/schema" "github.com/hashicorp/terraform/terraform" ) func TestResourceProvisioner_impl(t *testing.T) { - var _ terraform.ResourceProvisioner = new(ResourceProvisioner) + var _ terraform.ResourceProvisioner = ResourceProvisioner() +} + +func TestProvisioner(t *testing.T) { + if err := ResourceProvisioner().(*schema.Provisioner).InternalValidate(); err != nil { + t.Fatalf("err: %s", err) + } } func TestResourceProvider_Validate_good(t *testing.T) { @@ -23,7 +30,7 @@ func TestResourceProvider_Validate_good(t *testing.T) { "user_name": "bob", "user_key": "USER-KEY", }) - r := new(ResourceProvisioner) + r := ResourceProvisioner() warn, errs := r.Validate(c) if len(warn) > 0 { t.Fatalf("Warnings: %v", warn) @@ -37,7 +44,7 @@ func TestResourceProvider_Validate_bad(t *testing.T) { c := testConfig(t, map[string]interface{}{ "invalid": "nope", }) - p := new(ResourceProvisioner) + p := ResourceProvisioner() warn, errs := p.Validate(c) if len(warn) > 0 { t.Fatalf("Warnings: %v", warn) @@ -127,14 +134,13 @@ func TestResourceProvider_runChefClient(t *testing.T) { }, } - 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) + p, err := decodeConfig(tc.Config) if err != nil { t.Fatalf("Error: %v", err) } @@ -200,14 +206,13 @@ func TestResourceProvider_fetchChefCertificates(t *testing.T) { }, } - 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) + p, err := decodeConfig(tc.Config) if err != nil { t.Fatalf("Error: %v", err) } @@ -325,14 +330,13 @@ func TestResourceProvider_configureVaults(t *testing.T) { }, } - 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) + p, err := decodeConfig(tc.Config) if err != nil { t.Fatalf("Error: %v", err) } diff --git a/builtin/provisioners/chef/windows_provisioner_test.go b/builtin/provisioners/chef/windows_provisioner_test.go index 659953d978b6..41cef64b110e 100644 --- a/builtin/provisioners/chef/windows_provisioner_test.go +++ b/builtin/provisioners/chef/windows_provisioner_test.go @@ -73,7 +73,6 @@ func TestResourceProvider_windowsInstallChefClient(t *testing.T) { }, } - r := new(ResourceProvisioner) o := new(terraform.MockUIOutput) c := new(communicator.MockCommunicator) @@ -81,7 +80,7 @@ func TestResourceProvider_windowsInstallChefClient(t *testing.T) { c.Commands = tc.Commands c.UploadScripts = tc.UploadScripts - p, err := r.decodeConfig(tc.Config) + p, err := decodeConfig(tc.Config) if err != nil { t.Fatalf("Error: %v", err) } @@ -180,7 +179,6 @@ func TestResourceProvider_windowsCreateConfigFiles(t *testing.T) { }, } - r := new(ResourceProvisioner) o := new(terraform.MockUIOutput) c := new(communicator.MockCommunicator) @@ -188,7 +186,7 @@ func TestResourceProvider_windowsCreateConfigFiles(t *testing.T) { c.Commands = tc.Commands c.Uploads = tc.Uploads - p, err := r.decodeConfig(tc.Config) + p, err := decodeConfig(tc.Config) if err != nil { t.Fatalf("Error: %v", err) } From ee5ffc89ff5b4ad0a9146cbc72ebe5b24e6f4565 Mon Sep 17 00:00:00 2001 From: Vladislav Rassokhin Date: Tue, 29 Nov 2016 23:01:52 +0300 Subject: [PATCH 3/9] Use `schema.Provisioner` helper in built-in provisioner: file --- builtin/bins/provisioner-file/main.go | 5 +-- .../provisioners/file/resource_provisioner.go | 39 ++++++++++++++----- .../file/resource_provisioner_test.go | 17 +++++--- 3 files changed, 43 insertions(+), 18 deletions(-) diff --git a/builtin/bins/provisioner-file/main.go b/builtin/bins/provisioner-file/main.go index 592ff53a64d5..8b27a10aee2a 100644 --- a/builtin/bins/provisioner-file/main.go +++ b/builtin/bins/provisioner-file/main.go @@ -3,13 +3,10 @@ package main import ( "github.com/hashicorp/terraform/builtin/provisioners/file" "github.com/hashicorp/terraform/plugin" - "github.com/hashicorp/terraform/terraform" ) func main() { plugin.Serve(&plugin.ServeOpts{ - ProvisionerFunc: func() terraform.ResourceProvisioner { - return new(file.ResourceProvisioner) - }, + ProvisionerFunc: file.ResourceProvisioner, }) } diff --git a/builtin/provisioners/file/resource_provisioner.go b/builtin/provisioners/file/resource_provisioner.go index 2cd060b63e43..02ada5864a09 100644 --- a/builtin/provisioners/file/resource_provisioner.go +++ b/builtin/provisioners/file/resource_provisioner.go @@ -8,15 +8,36 @@ import ( "time" "github.com/hashicorp/terraform/communicator" + "github.com/hashicorp/terraform/helper/schema" "github.com/hashicorp/terraform/terraform" "github.com/mitchellh/go-homedir" ) -// ResourceProvisioner represents a file provisioner -type ResourceProvisioner struct{} +func ResourceProvisioner() terraform.ResourceProvisioner { + return &schema.Provisioner{ + Schema: map[string]*schema.Schema{ + "source": { + Type: schema.TypeString, + Optional: true, + ConflictsWith: []string{"content"}, + }, + "content": { + Type: schema.TypeString, + Optional: true, + ConflictsWith: []string{"source"}, + }, + "destination": { + Type: schema.TypeString, + Required: true, + }, + }, + ApplyFunc: Apply, + ValidateFunc: Validate, + } +} // Apply executes the file provisioner -func (p *ResourceProvisioner) Apply( +func Apply( o terraform.UIOutput, s *terraform.InstanceState, c *terraform.ResourceConfig) error { @@ -27,7 +48,7 @@ func (p *ResourceProvisioner) Apply( } // Get the source - src, deleteSource, err := p.getSrc(c) + src, deleteSource, err := getSrc(c) if err != nil { return err } @@ -41,11 +62,11 @@ func (p *ResourceProvisioner) Apply( if !ok { return fmt.Errorf("Unsupported 'destination' type! Must be string.") } - return p.copyFiles(comm, src, dst) + return copyFiles(comm, src, dst) } // Validate checks if the required arguments are configured -func (p *ResourceProvisioner) Validate(c *terraform.ResourceConfig) (ws []string, es []error) { +func Validate(c *terraform.ResourceConfig) (ws []string, es []error) { numDst := 0 numSrc := 0 for name := range c.Raw { @@ -59,13 +80,13 @@ func (p *ResourceProvisioner) Validate(c *terraform.ResourceConfig) (ws []string } } if numSrc != 1 || numDst != 1 { - es = append(es, fmt.Errorf("Must provide one of 'content' or 'source' and 'destination' to file")) + es = append(es, fmt.Errorf("Must provide one of 'content' or 'source' and 'destination' to file")) } return } // getSrc returns the file to use as source -func (p *ResourceProvisioner) getSrc(c *terraform.ResourceConfig) (string, bool, error) { +func getSrc(c *terraform.ResourceConfig) (string, bool, error) { var src string sRaw, ok := c.Config["source"] @@ -98,7 +119,7 @@ func (p *ResourceProvisioner) getSrc(c *terraform.ResourceConfig) (string, bool, } // copyFiles is used to copy the files from a source to a destination -func (p *ResourceProvisioner) copyFiles(comm communicator.Communicator, src, dst string) error { +func copyFiles(comm communicator.Communicator, src, dst string) error { // Wait and retry until we establish the connection err := retryFunc(comm.Timeout(), func() error { err := comm.Connect(nil) diff --git a/builtin/provisioners/file/resource_provisioner_test.go b/builtin/provisioners/file/resource_provisioner_test.go index 713c8aa0df8e..19d37f6f8a91 100644 --- a/builtin/provisioners/file/resource_provisioner_test.go +++ b/builtin/provisioners/file/resource_provisioner_test.go @@ -4,11 +4,18 @@ import ( "testing" "github.com/hashicorp/terraform/config" + "github.com/hashicorp/terraform/helper/schema" "github.com/hashicorp/terraform/terraform" ) func TestResourceProvisioner_impl(t *testing.T) { - var _ terraform.ResourceProvisioner = new(ResourceProvisioner) + var _ terraform.ResourceProvisioner = ResourceProvisioner() +} + +func TestProvisioner(t *testing.T) { + if err := ResourceProvisioner().(*schema.Provisioner).InternalValidate(); err != nil { + t.Fatalf("err: %s", err) + } } func TestResourceProvider_Validate_good_source(t *testing.T) { @@ -16,7 +23,7 @@ func TestResourceProvider_Validate_good_source(t *testing.T) { "source": "/tmp/foo", "destination": "/tmp/bar", }) - p := new(ResourceProvisioner) + p := ResourceProvisioner() warn, errs := p.Validate(c) if len(warn) > 0 { t.Fatalf("Warnings: %v", warn) @@ -31,7 +38,7 @@ func TestResourceProvider_Validate_good_content(t *testing.T) { "content": "value to copy", "destination": "/tmp/bar", }) - p := new(ResourceProvisioner) + p := ResourceProvisioner() warn, errs := p.Validate(c) if len(warn) > 0 { t.Fatalf("Warnings: %v", warn) @@ -45,7 +52,7 @@ func TestResourceProvider_Validate_bad_not_destination(t *testing.T) { c := testConfig(t, map[string]interface{}{ "source": "nope", }) - p := new(ResourceProvisioner) + p := ResourceProvisioner() warn, errs := p.Validate(c) if len(warn) > 0 { t.Fatalf("Warnings: %v", warn) @@ -61,7 +68,7 @@ func TestResourceProvider_Validate_bad_to_many_src(t *testing.T) { "content": "value to copy", "destination": "/tmp/bar", }) - p := new(ResourceProvisioner) + p := ResourceProvisioner() warn, errs := p.Validate(c) if len(warn) > 0 { t.Fatalf("Warnings: %v", warn) From 8feaae7f0019ff494d231d1ebff117734f7707a6 Mon Sep 17 00:00:00 2001 From: Vladislav Rassokhin Date: Tue, 29 Nov 2016 23:02:06 +0300 Subject: [PATCH 4/9] Use `schema.Provisioner` helper in built-in provisioner: local-exec --- builtin/bins/provisioner-local-exec/main.go | 5 +---- .../local-exec/resource_provisioner.go | 22 ++++++++++++++----- .../local-exec/resource_provisioner_test.go | 15 +++++++++---- 3 files changed, 29 insertions(+), 13 deletions(-) diff --git a/builtin/bins/provisioner-local-exec/main.go b/builtin/bins/provisioner-local-exec/main.go index 87a70c6ce53c..3c75cbd0dca6 100644 --- a/builtin/bins/provisioner-local-exec/main.go +++ b/builtin/bins/provisioner-local-exec/main.go @@ -3,13 +3,10 @@ package main import ( "github.com/hashicorp/terraform/builtin/provisioners/local-exec" "github.com/hashicorp/terraform/plugin" - "github.com/hashicorp/terraform/terraform" ) func main() { plugin.Serve(&plugin.ServeOpts{ - ProvisionerFunc: func() terraform.ResourceProvisioner { - return new(localexec.ResourceProvisioner) - }, + ProvisionerFunc: localexec.ResourceProvisioner, }) } diff --git a/builtin/provisioners/local-exec/resource_provisioner.go b/builtin/provisioners/local-exec/resource_provisioner.go index 88e5e0045778..24a81c98ceb3 100644 --- a/builtin/provisioners/local-exec/resource_provisioner.go +++ b/builtin/provisioners/local-exec/resource_provisioner.go @@ -8,6 +8,7 @@ import ( "github.com/armon/circbuf" "github.com/hashicorp/terraform/helper/config" + "github.com/hashicorp/terraform/helper/schema" "github.com/hashicorp/terraform/terraform" "github.com/mitchellh/go-linereader" ) @@ -19,9 +20,20 @@ const ( maxBufSize = 8 * 1024 ) -type ResourceProvisioner struct{} +func ResourceProvisioner() terraform.ResourceProvisioner { + return &schema.Provisioner{ + Schema: map[string]*schema.Schema{ + "command": { + Type: schema.TypeString, + Required: true, + }, + }, + ApplyFunc: Apply, + ValidateFunc: Validate, + } +} -func (p *ResourceProvisioner) Apply( +func Apply( o terraform.UIOutput, s *terraform.InstanceState, c *terraform.ResourceConfig) error { @@ -49,7 +61,7 @@ func (p *ResourceProvisioner) Apply( // Setup the reader that will read the lines from the command pr, pw := io.Pipe() copyDoneCh := make(chan struct{}) - go p.copyOutput(o, pr, copyDoneCh) + go copyOutput(o, pr, copyDoneCh) // Setup the command cmd := exec.Command(shell, flag, command) @@ -78,14 +90,14 @@ func (p *ResourceProvisioner) Apply( return nil } -func (p *ResourceProvisioner) Validate(c *terraform.ResourceConfig) ([]string, []error) { +func Validate(c *terraform.ResourceConfig) ([]string, []error) { validator := config.Validator{ Required: []string{"command"}, } return validator.Validate(c) } -func (p *ResourceProvisioner) copyOutput( +func copyOutput( o terraform.UIOutput, r io.Reader, doneCh chan<- struct{}) { defer close(doneCh) lr := linereader.New(r) diff --git a/builtin/provisioners/local-exec/resource_provisioner_test.go b/builtin/provisioners/local-exec/resource_provisioner_test.go index 9158c333e13d..785b14b1058d 100644 --- a/builtin/provisioners/local-exec/resource_provisioner_test.go +++ b/builtin/provisioners/local-exec/resource_provisioner_test.go @@ -7,11 +7,18 @@ import ( "testing" "github.com/hashicorp/terraform/config" + "github.com/hashicorp/terraform/helper/schema" "github.com/hashicorp/terraform/terraform" ) func TestResourceProvisioner_impl(t *testing.T) { - var _ terraform.ResourceProvisioner = new(ResourceProvisioner) + var _ terraform.ResourceProvisioner = ResourceProvisioner() +} + +func TestProvisioner(t *testing.T) { + if err := ResourceProvisioner().(*schema.Provisioner).InternalValidate(); err != nil { + t.Fatalf("err: %s", err) + } } func TestResourceProvider_Apply(t *testing.T) { @@ -21,7 +28,7 @@ func TestResourceProvider_Apply(t *testing.T) { }) output := new(terraform.MockUIOutput) - p := new(ResourceProvisioner) + p := ResourceProvisioner() if err := p.Apply(output, nil, c); err != nil { t.Fatalf("err: %v", err) } @@ -43,7 +50,7 @@ func TestResourceProvider_Validate_good(t *testing.T) { c := testConfig(t, map[string]interface{}{ "command": "echo foo", }) - p := new(ResourceProvisioner) + p := ResourceProvisioner() warn, errs := p.Validate(c) if len(warn) > 0 { t.Fatalf("Warnings: %v", warn) @@ -55,7 +62,7 @@ func TestResourceProvider_Validate_good(t *testing.T) { func TestResourceProvider_Validate_missing(t *testing.T) { c := testConfig(t, map[string]interface{}{}) - p := new(ResourceProvisioner) + p := ResourceProvisioner() warn, errs := p.Validate(c) if len(warn) > 0 { t.Fatalf("Warnings: %v", warn) From 68c556a5ac1ef3f31b95158ca60e60588eb30760 Mon Sep 17 00:00:00 2001 From: Vladislav Rassokhin Date: Tue, 29 Nov 2016 23:02:20 +0300 Subject: [PATCH 5/9] Use `schema.Provisioner` helper in built-in provisioner: remote-exec --- builtin/bins/provisioner-remote-exec/main.go | 5 +- .../remote-exec/resource_provisioner.go | 50 ++++++++++++++----- .../remote-exec/resource_provisioner_test.go | 25 ++++++---- 3 files changed, 52 insertions(+), 28 deletions(-) diff --git a/builtin/bins/provisioner-remote-exec/main.go b/builtin/bins/provisioner-remote-exec/main.go index e9874cbbe268..7919f6fed801 100644 --- a/builtin/bins/provisioner-remote-exec/main.go +++ b/builtin/bins/provisioner-remote-exec/main.go @@ -3,13 +3,10 @@ package main import ( "github.com/hashicorp/terraform/builtin/provisioners/remote-exec" "github.com/hashicorp/terraform/plugin" - "github.com/hashicorp/terraform/terraform" ) func main() { plugin.Serve(&plugin.ServeOpts{ - ProvisionerFunc: func() terraform.ResourceProvisioner { - return new(remoteexec.ResourceProvisioner) - }, + ProvisionerFunc: remoteexec.ResourceProvisioner, }) } diff --git a/builtin/provisioners/remote-exec/resource_provisioner.go b/builtin/provisioners/remote-exec/resource_provisioner.go index 2f784a4eaa0f..f85f86c7d3aa 100644 --- a/builtin/provisioners/remote-exec/resource_provisioner.go +++ b/builtin/provisioners/remote-exec/resource_provisioner.go @@ -12,15 +12,39 @@ import ( "github.com/hashicorp/terraform/communicator" "github.com/hashicorp/terraform/communicator/remote" + "github.com/hashicorp/terraform/helper/schema" "github.com/hashicorp/terraform/terraform" "github.com/mitchellh/go-linereader" ) -// ResourceProvisioner represents a remote exec provisioner -type ResourceProvisioner struct{} +func ResourceProvisioner() terraform.ResourceProvisioner { + return &schema.Provisioner{ + Schema: map[string]*schema.Schema{ + "script": { + Type: schema.TypeString, + Optional: true, + ConflictsWith: []string{"scripts", "inline"}, + }, + "scripts": { + Type: schema.TypeList, + Optional: true, + Elem: &schema.Schema{Type: schema.TypeString}, + ConflictsWith: []string{"script", "inline"}, + }, + "inline": { + Type: schema.TypeString, + Optional: true, + ConflictsWith: []string{"script", "scripts"}, + }, + }, + ApplyFunc: Apply, + ValidateFunc: Validate, + } + +} // Apply executes the remote exec provisioner -func (p *ResourceProvisioner) Apply( +func Apply( o terraform.UIOutput, s *terraform.InstanceState, c *terraform.ResourceConfig) error { @@ -31,7 +55,7 @@ func (p *ResourceProvisioner) Apply( } // Collect the scripts - scripts, err := p.collectScripts(c) + scripts, err := collectScripts(c) if err != nil { return err } @@ -40,14 +64,14 @@ func (p *ResourceProvisioner) Apply( } // Copy and execute each script - if err := p.runScripts(o, comm, scripts); err != nil { + if err := runScripts(o, comm, scripts); err != nil { return err } return nil } // Validate checks if the required arguments are configured -func (p *ResourceProvisioner) Validate(c *terraform.ResourceConfig) (ws []string, es []error) { +func Validate(c *terraform.ResourceConfig) (ws []string, es []error) { num := 0 for name := range c.Raw { switch name { @@ -65,7 +89,7 @@ func (p *ResourceProvisioner) Validate(c *terraform.ResourceConfig) (ws []string // generateScript takes the configuration and creates a script to be executed // from the inline configs -func (p *ResourceProvisioner) generateScript(c *terraform.ResourceConfig) (string, error) { +func generateScript(c *terraform.ResourceConfig) (string, error) { var lines []string command, ok := c.Config["inline"] if ok { @@ -93,11 +117,11 @@ func (p *ResourceProvisioner) generateScript(c *terraform.ResourceConfig) (strin // collectScripts is used to collect all the scripts we need // to execute in preparation for copying them. -func (p *ResourceProvisioner) collectScripts(c *terraform.ResourceConfig) ([]io.ReadCloser, error) { +func collectScripts(c *terraform.ResourceConfig) ([]io.ReadCloser, error) { // Check if inline _, ok := c.Config["inline"] if ok { - script, err := p.generateScript(c) + script, err := generateScript(c) if err != nil { return nil, err } @@ -153,7 +177,7 @@ func (p *ResourceProvisioner) collectScripts(c *terraform.ResourceConfig) ([]io. } // runScripts is used to copy and execute a set of scripts -func (p *ResourceProvisioner) runScripts( +func runScripts( o terraform.UIOutput, comm communicator.Communicator, scripts []io.ReadCloser) error { @@ -173,8 +197,8 @@ func (p *ResourceProvisioner) runScripts( errR, errW := io.Pipe() outDoneCh := make(chan struct{}) errDoneCh := make(chan struct{}) - go p.copyOutput(o, outR, outDoneCh) - go p.copyOutput(o, errR, errDoneCh) + go copyOutput(o, outR, outDoneCh) + go copyOutput(o, errR, errDoneCh) remotePath := comm.ScriptPath() err = retryFunc(comm.Timeout(), func() error { @@ -224,7 +248,7 @@ func (p *ResourceProvisioner) runScripts( return nil } -func (p *ResourceProvisioner) copyOutput( +func copyOutput( o terraform.UIOutput, r io.Reader, doneCh chan<- struct{}) { defer close(doneCh) lr := linereader.New(r) diff --git a/builtin/provisioners/remote-exec/resource_provisioner_test.go b/builtin/provisioners/remote-exec/resource_provisioner_test.go index a10520fb816c..7a8a229214c1 100644 --- a/builtin/provisioners/remote-exec/resource_provisioner_test.go +++ b/builtin/provisioners/remote-exec/resource_provisioner_test.go @@ -6,18 +6,25 @@ import ( "testing" "github.com/hashicorp/terraform/config" + "github.com/hashicorp/terraform/helper/schema" "github.com/hashicorp/terraform/terraform" ) func TestResourceProvisioner_impl(t *testing.T) { - var _ terraform.ResourceProvisioner = new(ResourceProvisioner) + var _ terraform.ResourceProvisioner = ResourceProvisioner() +} + +func TestProvisioner(t *testing.T) { + if err := ResourceProvisioner().(*schema.Provisioner).InternalValidate(); err != nil { + t.Fatalf("err: %s", err) + } } func TestResourceProvider_Validate_good(t *testing.T) { c := testConfig(t, map[string]interface{}{ "inline": "echo foo", }) - p := new(ResourceProvisioner) + p := ResourceProvisioner() warn, errs := p.Validate(c) if len(warn) > 0 { t.Fatalf("Warnings: %v", warn) @@ -31,7 +38,7 @@ func TestResourceProvider_Validate_bad(t *testing.T) { c := testConfig(t, map[string]interface{}{ "invalid": "nope", }) - p := new(ResourceProvisioner) + p := ResourceProvisioner() warn, errs := p.Validate(c) if len(warn) > 0 { t.Fatalf("Warnings: %v", warn) @@ -47,7 +54,6 @@ exit 0 ` func TestResourceProvider_generateScript(t *testing.T) { - p := new(ResourceProvisioner) conf := testConfig(t, map[string]interface{}{ "inline": []interface{}{ "cd /tmp", @@ -55,7 +61,7 @@ func TestResourceProvider_generateScript(t *testing.T) { "exit 0", }, }) - out, err := p.generateScript(conf) + out, err := generateScript(conf) if err != nil { t.Fatalf("err: %v", err) } @@ -66,7 +72,6 @@ func TestResourceProvider_generateScript(t *testing.T) { } func TestResourceProvider_CollectScripts_inline(t *testing.T) { - p := new(ResourceProvisioner) conf := testConfig(t, map[string]interface{}{ "inline": []interface{}{ "cd /tmp", @@ -75,7 +80,7 @@ func TestResourceProvider_CollectScripts_inline(t *testing.T) { }, }) - scripts, err := p.collectScripts(conf) + scripts, err := collectScripts(conf) if err != nil { t.Fatalf("err: %v", err) } @@ -96,12 +101,11 @@ func TestResourceProvider_CollectScripts_inline(t *testing.T) { } func TestResourceProvider_CollectScripts_script(t *testing.T) { - p := new(ResourceProvisioner) conf := testConfig(t, map[string]interface{}{ "script": "test-fixtures/script1.sh", }) - scripts, err := p.collectScripts(conf) + scripts, err := collectScripts(conf) if err != nil { t.Fatalf("err: %v", err) } @@ -122,7 +126,6 @@ func TestResourceProvider_CollectScripts_script(t *testing.T) { } func TestResourceProvider_CollectScripts_scripts(t *testing.T) { - p := new(ResourceProvisioner) conf := testConfig(t, map[string]interface{}{ "scripts": []interface{}{ "test-fixtures/script1.sh", @@ -131,7 +134,7 @@ func TestResourceProvider_CollectScripts_scripts(t *testing.T) { }, }) - scripts, err := p.collectScripts(conf) + scripts, err := collectScripts(conf) if err != nil { t.Fatalf("err: %v", err) } From 66a0ebca218b0ded9c8e852ad4d9036b05c06035 Mon Sep 17 00:00:00 2001 From: Vladislav Rassokhin Date: Tue, 29 Nov 2016 23:03:21 +0300 Subject: [PATCH 6/9] Regenerate plugin list since provisioners were changed in previous commits --- command/internal_plugin_list.go | 9 ++++----- scripts/generate-plugins.go | 15 ++++++--------- scripts/generate-plugins_test.go | 8 ++++---- 3 files changed, 14 insertions(+), 18 deletions(-) diff --git a/command/internal_plugin_list.go b/command/internal_plugin_list.go index 8727578b833b..83be6c7cdfae 100644 --- a/command/internal_plugin_list.go +++ b/command/internal_plugin_list.go @@ -62,7 +62,6 @@ import ( remoteexecresourceprovisioner "github.com/hashicorp/terraform/builtin/provisioners/remote-exec" "github.com/hashicorp/terraform/plugin" - "github.com/hashicorp/terraform/terraform" ) var InternalProviders = map[string]plugin.ProviderFunc{ @@ -119,8 +118,8 @@ var InternalProviders = map[string]plugin.ProviderFunc{ } var InternalProvisioners = map[string]plugin.ProvisionerFunc{ - "chef": func() terraform.ResourceProvisioner { return new(chefresourceprovisioner.ResourceProvisioner) }, - "file": func() terraform.ResourceProvisioner { return new(fileresourceprovisioner.ResourceProvisioner) }, - "local-exec": func() terraform.ResourceProvisioner { return new(localexecresourceprovisioner.ResourceProvisioner) }, - "remote-exec": func() terraform.ResourceProvisioner { return new(remoteexecresourceprovisioner.ResourceProvisioner) }, + "chef": chefresourceprovisioner.ResourceProvisioner, + "file": fileresourceprovisioner.ResourceProvisioner, + "local-exec": localexecresourceprovisioner.ResourceProvisioner, + "remote-exec": remoteexecresourceprovisioner.ResourceProvisioner, } diff --git a/scripts/generate-plugins.go b/scripts/generate-plugins.go index 0867f97559b7..8183dd633865 100644 --- a/scripts/generate-plugins.go +++ b/scripts/generate-plugins.go @@ -82,16 +82,14 @@ func makeProviderMap(items []plugin) string { // makeProvisionerMap creates a map of provisioners like this: // -// "file": func() terraform.ResourceProvisioner { return new(file.ResourceProvisioner) }, -// "local-exec": func() terraform.ResourceProvisioner { return new(localexec.ResourceProvisioner) }, -// "remote-exec": func() terraform.ResourceProvisioner { return new(remoteexec.ResourceProvisioner) }, +// "file": fileresourceprovisioner.GetProvisioner, +// "local-exec": localexecresourceprovisioner.GetProvisioner, +// "remote-exec": remoteexecresourceprovisioner.GetProvisioner, // -// This is more verbose than the Provider case because there is no corresponding -// Provisioner function. func makeProvisionerMap(items []plugin) string { output := "" for _, item := range items { - output += fmt.Sprintf("\t\"%s\": func() terraform.ResourceProvisioner { return new(%s.%s) },\n", item.PluginName, item.ImportName, item.TypeName) + output += fmt.Sprintf("\t\"%s\": %s.%s,\n", item.PluginName, item.ImportName, item.TypeName) } return output } @@ -254,8 +252,8 @@ func discoverProviders() ([]plugin, error) { func discoverProvisioners() ([]plugin, error) { path := "./builtin/provisioners" - typeID := "ResourceProvisioner" - typeName := "" + typeID := "terraform.ResourceProvisioner" + typeName := "ResourceProvisioner" return discoverTypesInPath(path, typeID, typeName) } @@ -269,7 +267,6 @@ package command import ( IMPORTS "github.com/hashicorp/terraform/plugin" - "github.com/hashicorp/terraform/terraform" ) var InternalProviders = map[string]plugin.ProviderFunc{ diff --git a/scripts/generate-plugins_test.go b/scripts/generate-plugins_test.go index bbb3fce18089..998ffe2c2c4c 100644 --- a/scripts/generate-plugins_test.go +++ b/scripts/generate-plugins_test.go @@ -27,9 +27,9 @@ func TestMakeProvisionerMap(t *testing.T) { }, }) - expected := ` "file": func() terraform.ResourceProvisioner { return new(fileresourceprovisioner.ResourceProvisioner) }, - "local-exec": func() terraform.ResourceProvisioner { return new(localexecresourceprovisioner.ResourceProvisioner) }, - "remote-exec": func() terraform.ResourceProvisioner { return new(remoteexecresourceprovisioner.ResourceProvisioner) }, + expected := ` "file": fileresourceprovisioner.ResourceProvisioner, + "local-exec": localexecresourceprovisioner.ResourceProvisioner, + "remote-exec": remoteexecresourceprovisioner.ResourceProvisioner, ` if p != expected { @@ -86,7 +86,7 @@ func TestDiscoverTypesProviders(t *testing.T) { } func TestDiscoverTypesProvisioners(t *testing.T) { - plugins, err := discoverTypesInPath("../builtin/provisioners", "ResourceProvisioner", "") + plugins, err := discoverTypesInPath("../builtin/provisioners", "terraform.ResourceProvisioner", "ResourceProvisioner") if err != nil { t.Fatalf(err.Error()) } From a3ed11ca88fd9d8ae73a59e582ae242f4a6e72ea Mon Sep 17 00:00:00 2001 From: Vladislav Rassokhin Date: Thu, 1 Dec 2016 23:41:59 +0300 Subject: [PATCH 7/9] Migrate provisioners functions to `schema.ResourceData` Known problems: * had to introduce `ResourceData.GetRawConfig()` since chef provisioner parses configuration manually; * In `remote-exec` provisioner `inline` can no longer accept 'string' value, only list of strings allowed. - due to migration to schema. There's a workaround like it;s done in `chef` provisioner, but I'd like to keep code as is. --- .../provisioners/chef/resource_provisioner.go | 18 +++---- .../provisioners/file/resource_provisioner.go | 52 +++++++----------- .../local-exec/resource_provisioner.go | 23 ++------ .../remote-exec/resource_provisioner.go | 53 ++++++------------- .../remote-exec/resource_provisioner_test.go | 12 +++-- helper/schema/provisioner.go | 24 +++++++-- helper/schema/provisioner_test.go | 2 +- helper/schema/resource_data.go | 5 ++ 8 files changed, 81 insertions(+), 108 deletions(-) diff --git a/builtin/provisioners/chef/resource_provisioner.go b/builtin/provisioners/chef/resource_provisioner.go index b35d6a4f89e4..7f86e6fd2c30 100644 --- a/builtin/provisioners/chef/resource_provisioner.go +++ b/builtin/provisioners/chef/resource_provisioner.go @@ -140,22 +140,22 @@ func ResourceProvisioner() terraform.ResourceProvisioner { // Apply executes the file provisioner func Apply( o terraform.UIOutput, - s *terraform.InstanceState, - c *terraform.ResourceConfig) error { + d *schema.ResourceData) error { // Decode the raw config for this provisioner - p, err := decodeConfig(c) + p, err := decodeConfig(d.GetRawConfig()) // TODO: Get rid of GetRawConfig if err != nil { return err } if p.OSType == "" { - switch s.Ephemeral.ConnInfo["type"] { + t := d.State().Ephemeral.ConnInfo["type"] + switch t { case "ssh", "": // The default connection type is ssh, so if the type is empty assume ssh p.OSType = "linux" case "winrm": p.OSType = "windows" default: - return fmt.Errorf("Unsupported connection type: %s", s.Ephemeral.ConnInfo["type"]) + return fmt.Errorf("Unsupported connection type: %s", t) } } @@ -169,7 +169,7 @@ func Apply( p.generateClientKey = p.generateClientKeyFunc(linuxKnifeCmd, linuxConfDir, linuxNoOutput) p.configureVaults = p.configureVaultsFunc(linuxGemCmd, linuxKnifeCmd, linuxConfDir) p.runChefClient = p.runChefClientFunc(linuxChefCmd, linuxConfDir) - p.useSudo = !p.PreventSudo && s.Ephemeral.ConnInfo["user"] != "root" + p.useSudo = !p.PreventSudo && d.State().Ephemeral.ConnInfo["user"] != "root" case "windows": p.cleanupUserKeyCmd = fmt.Sprintf("cd %s && del /F /Q %s", windowsConfDir, p.UserName+".pem") p.createConfigFiles = p.windowsCreateConfigFiles @@ -184,7 +184,7 @@ func Apply( } // Get a new communicator - comm, err := communicator.New(s) + comm, err := communicator.New(d.State()) if err != nil { return err } @@ -254,8 +254,8 @@ func Apply( } // Validate checks if the required arguments are configured -func Validate(c *terraform.ResourceConfig) (ws []string, es []error) { - p, err := decodeConfig(c) +func Validate(d *schema.ResourceData) (ws []string, es []error) { + p, err := decodeConfig(d.GetRawConfig()) // TODO: Get rid of GetRawConfig if err != nil { es = append(es, err) return ws, es diff --git a/builtin/provisioners/file/resource_provisioner.go b/builtin/provisioners/file/resource_provisioner.go index 02ada5864a09..0e83058f6792 100644 --- a/builtin/provisioners/file/resource_provisioner.go +++ b/builtin/provisioners/file/resource_provisioner.go @@ -31,24 +31,23 @@ func ResourceProvisioner() terraform.ResourceProvisioner { Required: true, }, }, - ApplyFunc: Apply, - ValidateFunc: Validate, + ApplyFunc: Apply, + //ValidateFunc: Validate, } } // Apply executes the file provisioner func Apply( o terraform.UIOutput, - s *terraform.InstanceState, - c *terraform.ResourceConfig) error { + d *schema.ResourceData) error { // Get a new communicator - comm, err := communicator.New(s) + comm, err := communicator.New(d.State()) if err != nil { return err } // Get the source - src, deleteSource, err := getSrc(c) + src, deleteSource, err := getSrc(d) if err != nil { return err } @@ -57,57 +56,42 @@ func Apply( } // Get destination - dRaw := c.Config["destination"] - dst, ok := dRaw.(string) - if !ok { - return fmt.Errorf("Unsupported 'destination' type! Must be string.") - } + dst := d.Get("destination").(string) return copyFiles(comm, src, dst) } // Validate checks if the required arguments are configured -func Validate(c *terraform.ResourceConfig) (ws []string, es []error) { - numDst := 0 +func Validate(d *schema.ResourceData) (ws []string, es []error) { numSrc := 0 - for name := range c.Raw { - switch name { - case "destination": - numDst++ - case "source", "content": - numSrc++ - default: - es = append(es, fmt.Errorf("Unknown configuration '%s'", name)) - } + if _, ok := d.GetOk("source"); ok == true { + numSrc++ + } + if _, ok := d.GetOk("content"); ok == true { + numSrc++ } - if numSrc != 1 || numDst != 1 { + if numSrc != 1 { es = append(es, fmt.Errorf("Must provide one of 'content' or 'source' and 'destination' to file")) } return } // getSrc returns the file to use as source -func getSrc(c *terraform.ResourceConfig) (string, bool, error) { +func getSrc(d *schema.ResourceData) (string, bool, error) { var src string - sRaw, ok := c.Config["source"] + source, ok := d.GetOk("source") if ok { - if src, ok = sRaw.(string); !ok { - return "", false, fmt.Errorf("Unsupported 'source' type! Must be string.") - } + src = source.(string) } - content, ok := c.Config["content"] + content, ok := d.GetOk("content") if ok { file, err := ioutil.TempFile("", "tf-file-content") if err != nil { return "", true, err } - contentStr, ok := content.(string) - if !ok { - return "", true, fmt.Errorf("Unsupported 'content' type! Must be string.") - } - if _, err = file.WriteString(contentStr); err != nil { + if _, err = file.WriteString(content.(string)); err != nil { return "", true, err } diff --git a/builtin/provisioners/local-exec/resource_provisioner.go b/builtin/provisioners/local-exec/resource_provisioner.go index 24a81c98ceb3..efac2ef6ec10 100644 --- a/builtin/provisioners/local-exec/resource_provisioner.go +++ b/builtin/provisioners/local-exec/resource_provisioner.go @@ -7,7 +7,6 @@ import ( "runtime" "github.com/armon/circbuf" - "github.com/hashicorp/terraform/helper/config" "github.com/hashicorp/terraform/helper/schema" "github.com/hashicorp/terraform/terraform" "github.com/mitchellh/go-linereader" @@ -28,25 +27,16 @@ func ResourceProvisioner() terraform.ResourceProvisioner { Required: true, }, }, - ApplyFunc: Apply, - ValidateFunc: Validate, + ApplyFunc: Apply, } } func Apply( o terraform.UIOutput, - s *terraform.InstanceState, - c *terraform.ResourceConfig) error { + d *schema.ResourceData) error { // Get the command - commandRaw, ok := c.Config["command"] - if !ok { - return fmt.Errorf("local-exec provisioner missing 'command'") - } - command, ok := commandRaw.(string) - if !ok { - return fmt.Errorf("local-exec provisioner command must be a string") - } + command := d.Get("command").(string) // Execute the command using a shell var shell, flag string @@ -90,13 +80,6 @@ func Apply( return nil } -func Validate(c *terraform.ResourceConfig) ([]string, []error) { - validator := config.Validator{ - Required: []string{"command"}, - } - return validator.Validate(c) -} - func copyOutput( o terraform.UIOutput, r io.Reader, doneCh chan<- struct{}) { defer close(doneCh) diff --git a/builtin/provisioners/remote-exec/resource_provisioner.go b/builtin/provisioners/remote-exec/resource_provisioner.go index f85f86c7d3aa..1c3d40a3f540 100644 --- a/builtin/provisioners/remote-exec/resource_provisioner.go +++ b/builtin/provisioners/remote-exec/resource_provisioner.go @@ -30,15 +30,17 @@ func ResourceProvisioner() terraform.ResourceProvisioner { Optional: true, Elem: &schema.Schema{Type: schema.TypeString}, ConflictsWith: []string{"script", "inline"}, + // TODO: Maybe ValidateFunc could call collectScripts to ensure scripts exist on disk }, + // Could be either string of list of strings "inline": { - Type: schema.TypeString, + Type: schema.TypeList, Optional: true, + Elem: &schema.Schema{Type: schema.TypeString}, ConflictsWith: []string{"script", "scripts"}, }, }, - ApplyFunc: Apply, - ValidateFunc: Validate, + ApplyFunc: Apply, } } @@ -46,16 +48,15 @@ func ResourceProvisioner() terraform.ResourceProvisioner { // Apply executes the remote exec provisioner func Apply( o terraform.UIOutput, - s *terraform.InstanceState, - c *terraform.ResourceConfig) error { + d *schema.ResourceData) error { // Get a new communicator - comm, err := communicator.New(s) + comm, err := communicator.New(d.State()) if err != nil { return err } // Collect the scripts - scripts, err := collectScripts(c) + scripts, err := collectScripts(d) if err != nil { return err } @@ -70,28 +71,11 @@ func Apply( return nil } -// Validate checks if the required arguments are configured -func Validate(c *terraform.ResourceConfig) (ws []string, es []error) { - num := 0 - for name := range c.Raw { - switch name { - case "scripts", "script", "inline": - num++ - default: - es = append(es, fmt.Errorf("Unknown configuration '%s'", name)) - } - } - if num != 1 { - es = append(es, fmt.Errorf("Must provide one of 'scripts', 'script' or 'inline' to remote-exec")) - } - return -} - // generateScript takes the configuration and creates a script to be executed // from the inline configs -func generateScript(c *terraform.ResourceConfig) (string, error) { +func generateScript(d *schema.ResourceData) (string, error) { var lines []string - command, ok := c.Config["inline"] + command, ok := d.GetOk("inline") if ok { switch cmd := command.(type) { case string: @@ -117,11 +101,11 @@ func generateScript(c *terraform.ResourceConfig) (string, error) { // collectScripts is used to collect all the scripts we need // to execute in preparation for copying them. -func collectScripts(c *terraform.ResourceConfig) ([]io.ReadCloser, error) { +func collectScripts(d *schema.ResourceData) ([]io.ReadCloser, error) { // Check if inline - _, ok := c.Config["inline"] + _, ok := d.GetOk("inline") if ok { - script, err := generateScript(c) + script, err := generateScript(d) if err != nil { return nil, err } @@ -131,16 +115,12 @@ func collectScripts(c *terraform.ResourceConfig) ([]io.ReadCloser, error) { // Collect scripts var scripts []string - s, ok := c.Config["script"] + script, ok := d.GetOk("script") if ok { - sStr, ok := s.(string) - if !ok { - return nil, fmt.Errorf("Unsupported 'script' type! Must be a string.") - } - scripts = append(scripts, sStr) + scripts = append(scripts, script.(string)) } - sl, ok := c.Config["scripts"] + sl, ok := d.GetOk("scripts") if ok { switch slt := sl.(type) { case []string: @@ -157,6 +137,7 @@ func collectScripts(c *terraform.ResourceConfig) ([]io.ReadCloser, error) { default: return nil, fmt.Errorf("Unsupported 'scripts' type! Must be list of strings.") } + } // Open all the scripts diff --git a/builtin/provisioners/remote-exec/resource_provisioner_test.go b/builtin/provisioners/remote-exec/resource_provisioner_test.go index 7a8a229214c1..a02ec2daa5d5 100644 --- a/builtin/provisioners/remote-exec/resource_provisioner_test.go +++ b/builtin/provisioners/remote-exec/resource_provisioner_test.go @@ -61,7 +61,7 @@ func TestResourceProvider_generateScript(t *testing.T) { "exit 0", }, }) - out, err := generateScript(conf) + out, err := generateScript(getTestResourceData(conf)) if err != nil { t.Fatalf("err: %v", err) } @@ -80,7 +80,7 @@ func TestResourceProvider_CollectScripts_inline(t *testing.T) { }, }) - scripts, err := collectScripts(conf) + scripts, err := collectScripts(getTestResourceData(conf)) if err != nil { t.Fatalf("err: %v", err) } @@ -105,7 +105,7 @@ func TestResourceProvider_CollectScripts_script(t *testing.T) { "script": "test-fixtures/script1.sh", }) - scripts, err := collectScripts(conf) + scripts, err := collectScripts(getTestResourceData(conf)) if err != nil { t.Fatalf("err: %v", err) } @@ -134,7 +134,7 @@ func TestResourceProvider_CollectScripts_scripts(t *testing.T) { }, }) - scripts, err := collectScripts(conf) + scripts, err := collectScripts(getTestResourceData(conf)) if err != nil { t.Fatalf("err: %v", err) } @@ -166,3 +166,7 @@ func testConfig( return terraform.NewResourceConfig(r) } + +func getTestResourceData(c *terraform.ResourceConfig) *schema.ResourceData { + return ResourceProvisioner().(*schema.Provisioner).TestResourceData(c) +} diff --git a/helper/schema/provisioner.go b/helper/schema/provisioner.go index 50306f99067d..384343ada141 100644 --- a/helper/schema/provisioner.go +++ b/helper/schema/provisioner.go @@ -13,8 +13,8 @@ type Provisioner struct { ApplyFunc ApplyFunc } -type ValidateFunc func(*terraform.ResourceConfig) ([]string, []error) -type ApplyFunc func(terraform.UIOutput, *terraform.InstanceState, *terraform.ResourceConfig) error +type ValidateFunc func(*ResourceData) ([]string, []error) +type ApplyFunc func(terraform.UIOutput, *ResourceData) error // InternalValidate should be called to validate the structure // of the provisioner. @@ -51,7 +51,11 @@ func (p *Provisioner) Validate(config *terraform.ResourceConfig) ([]string, []er e = append(e, e2...) } if p.ValidateFunc != nil { - w2, e2 := p.ValidateFunc(config) + data := &ResourceData{ + schema: p.Schema, + config: config, + } + w2, e2 := p.ValidateFunc(data) w = append(w, w2...) e = append(e, e2...) } @@ -62,5 +66,17 @@ func (p *Provisioner) Apply(ui terraform.UIOutput, state *terraform.InstanceStat if p.ApplyFunc == nil { panic("ApplyFunc should be specified in provisioner") } - return p.ApplyFunc(ui, state, config) + data := &ResourceData{ + schema: p.Schema, + config: config, + state: state, + } + return p.ApplyFunc(ui, data) +} + +func (p *Provisioner) TestResourceData(config *terraform.ResourceConfig) *ResourceData { + return &ResourceData{ + schema: p.Schema, + config: config, + } } diff --git a/helper/schema/provisioner_test.go b/helper/schema/provisioner_test.go index 1594e89ca1cb..3cde5ae22061 100644 --- a/helper/schema/provisioner_test.go +++ b/helper/schema/provisioner_test.go @@ -57,7 +57,7 @@ func TestProvisioner_Validate(t *testing.T) { { P: &Provisioner{ Schema: nil, - ValidateFunc: func(*terraform.ResourceConfig) (ws []string, errors []error) { + ValidateFunc: func(*ResourceData) (ws []string, errors []error) { ws = append(ws, "Simple warning from provisioner ValidateFunc") return }, diff --git a/helper/schema/resource_data.go b/helper/schema/resource_data.go index b040b63ee54b..ea53662a1545 100644 --- a/helper/schema/resource_data.go +++ b/helper/schema/resource_data.go @@ -463,3 +463,8 @@ func (d *ResourceData) get(addr []string, source getSource) getResult { Schema: schema, } } + +// TODO: Get rid of usages and remove. Added to fix chef provisioner +func (d *ResourceData) GetRawConfig() *terraform.ResourceConfig { + return d.config +} From b4d8728c428c1932147503217b2ca954223208e9 Mon Sep 17 00:00:00 2001 From: Vladislav Rassokhin Date: Fri, 2 Dec 2016 18:28:11 +0300 Subject: [PATCH 8/9] Migrate `chef` provisioner `schema.Schema` * `chef.Provisioner` structure left intact but now it's decoded from `schema.ResourceData` instead of `terraform.ResourceConfig` suing simple copy-paste-bsed solution; * Added simple schema without any validation yet; * `ResourceData.GetRawConfig` removed since it's not longer needed. --- .../provisioners/chef/linux_provisioner.go | 2 +- .../chef/linux_provisioner_test.go | 4 +- .../provisioners/chef/resource_provisioner.go | 309 +++++++++++++----- .../chef/resource_provisioner_test.go | 10 +- .../chef/windows_provisioner_test.go | 4 +- helper/schema/resource_data.go | 5 - 6 files changed, 241 insertions(+), 93 deletions(-) diff --git a/builtin/provisioners/chef/linux_provisioner.go b/builtin/provisioners/chef/linux_provisioner.go index ebfe729795ba..802681ea57a6 100644 --- a/builtin/provisioners/chef/linux_provisioner.go +++ b/builtin/provisioners/chef/linux_provisioner.go @@ -26,7 +26,7 @@ func (p *Provisioner) linuxInstallChefClient( if p.HTTPSProxy != "" { prefix += fmt.Sprintf("https_proxy='%s' ", p.HTTPSProxy) } - if p.NOProxy != nil { + if p.NOProxy != nil && len(p.NOProxy) > 0 { prefix += fmt.Sprintf("no_proxy='%s' ", strings.Join(p.NOProxy, ",")) } diff --git a/builtin/provisioners/chef/linux_provisioner_test.go b/builtin/provisioners/chef/linux_provisioner_test.go index e0e536f22058..444a0f39ac3a 100644 --- a/builtin/provisioners/chef/linux_provisioner_test.go +++ b/builtin/provisioners/chef/linux_provisioner_test.go @@ -131,7 +131,7 @@ func TestResourceProvider_linuxInstallChefClient(t *testing.T) { for k, tc := range cases { c.Commands = tc.Commands - p, err := decodeConfig(tc.Config) + p, err := decodeConfig(getTestResourceData(tc.Config)) if err != nil { t.Fatalf("Error: %v", err) } @@ -270,7 +270,7 @@ func TestResourceProvider_linuxCreateConfigFiles(t *testing.T) { c.Commands = tc.Commands c.Uploads = tc.Uploads - p, err := decodeConfig(tc.Config) + p, err := decodeConfig(getTestResourceData(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 7f86e6fd2c30..c122604d1252 100644 --- a/builtin/provisioners/chef/resource_provisioner.go +++ b/builtin/provisioners/chef/resource_provisioner.go @@ -21,7 +21,6 @@ import ( "github.com/hashicorp/terraform/terraform" "github.com/mitchellh/go-homedir" "github.com/mitchellh/go-linereader" - "github.com/mitchellh/mapstructure" ) const ( @@ -84,33 +83,33 @@ enable_reporting false // Provisioner represents a Chef provisioner type Provisioner struct { - 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"` - RecreateClient bool `mapstructure:"recreate_client"` - 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"` - SkipRegister bool `mapstructure:"skip_register"` - SSLVerifyMode string `mapstructure:"ssl_verify_mode"` - UserName string `mapstructure:"user_name"` - UserKey string `mapstructure:"user_key"` - VaultJSON string `mapstructure:"vault_json"` - Version string `mapstructure:"version"` + AttributesJSON string + ClientOptions []string + DisableReporting bool + Environment string + FetchChefCertificates bool + LogToFile bool + UsePolicyfile bool + PolicyGroup string + PolicyName string + HTTPProxy string + HTTPSProxy string + NOProxy []string + NodeName string + OhaiHints []string + OSType string + RecreateClient bool + PreventSudo bool + RunList []string + SecretKey string + ServerURL string + SkipInstall bool + SkipRegister bool + SSLVerifyMode string + UserName string + UserKey string + VaultJSON string + Version string attributes map[string]interface{} vaults map[string][]string @@ -125,13 +124,150 @@ type Provisioner struct { useSudo bool // Deprecated Fields - ValidationClientName string `mapstructure:"validation_client_name"` - ValidationKey string `mapstructure:"validation_key"` + ValidationClientName string + ValidationKey string } func ResourceProvisioner() terraform.ResourceProvisioner { return &schema.Provisioner{ - Schema: nil, // TODO: Fill from Provisioner struct, note 'required' tag + 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, + }, + "node_name": { + 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": { + Type: schema.TypeString, + Required: true, + }, + "skip_install": { + Type: schema.TypeBool, + Optional: true, + }, + "skip_register": { + Type: schema.TypeBool, + Optional: true, + }, + "ssl_verify_mode": { + Type: schema.TypeString, + Optional: true, + }, + "user_name": { + Type: schema.TypeString, + Optional: true, + }, + "user_key": { + Type: schema.TypeString, + Optional: true, + }, + "vault_json": { + Type: schema.TypeString, + Optional: true, + }, + "version": { + Type: schema.TypeString, + Optional: true, + }, + + // Deprecated + "validation_client_name": { + Type: schema.TypeString, + Deprecated: "Please use user_name instead", + Optional: true, + }, + + "validation_key": { + Type: schema.TypeString, + Deprecated: "Please use user_key instead", + Optional: true, + }, + }, ApplyFunc: Apply, ValidateFunc: Validate, } @@ -142,7 +278,7 @@ func Apply( o terraform.UIOutput, d *schema.ResourceData) error { // Decode the raw config for this provisioner - p, err := decodeConfig(d.GetRawConfig()) // TODO: Get rid of GetRawConfig + p, err := decodeConfig(d) if err != nil { return err } @@ -255,21 +391,15 @@ func Apply( // Validate checks if the required arguments are configured func Validate(d *schema.ResourceData) (ws []string, es []error) { - p, err := decodeConfig(d.GetRawConfig()) // TODO: Get rid of GetRawConfig + p, err := decodeConfig(d) if err != nil { es = append(es, err) return ws, es } - if p.NodeName == "" { - es = append(es, errors.New("Key not found: node_name")) - } if !p.UsePolicyfile && p.RunList == nil { es = append(es, errors.New("Key not found: run_list")) } - if p.ServerURL == "" { - es = append(es, errors.New("Key not found: server_url")) - } if p.UsePolicyfile && p.PolicyName == "" { es = append(es, errors.New("Policyfile enabled but key not found: policy_name")) } @@ -284,12 +414,7 @@ func Validate(d *schema.ResourceData) (ws []string, es []error) { es = append(es, errors.New( "One of user_key or the deprecated validation_key must be provided")) } - if p.ValidationClientName != "" { - ws = append(ws, "validation_client_name is deprecated, please use user_name instead") - } if p.ValidationKey != "" { - ws = append(ws, "validation_key is deprecated, please use user_key instead") - if p.RecreateClient { es = append(es, errors.New( "Cannot use recreate_client=true with the deprecated validation_key, please provide a user_key")) @@ -303,38 +428,8 @@ func Validate(d *schema.ResourceData) (ws []string, es []error) { return ws, es } -func decodeConfig(c *terraform.ResourceConfig) (*Provisioner, error) { - p := new(Provisioner) - - decConf := &mapstructure.DecoderConfig{ - ErrorUnused: true, - WeaklyTypedInput: true, - Result: p, - } - dec, err := mapstructure.NewDecoder(decConf) - if err != nil { - return nil, err - } - - // We need to merge both configs into a single map first. Order is - // important as we need to make sure interpolated values are used - // over raw values. This makes sure that all values are there even - // if some still need to be interpolated later on. Without this - // the validation will fail when using a variable for a required - // parameter (the node_name for example). - m := make(map[string]interface{}) - - for k, v := range c.Raw { - m[k] = v - } - - for k, v := range c.Config { - m[k] = v - } - - if err := dec.Decode(m); err != nil { - return nil, err - } +func decodeConfig(d *schema.ResourceData) (*Provisioner, error) { + p := decodeDataToProvisioner(d) // Make sure the supplied URL has a trailing slash p.ServerURL = strings.TrimSuffix(p.ServerURL, "/") + "/" @@ -359,17 +454,17 @@ func decodeConfig(c *terraform.ResourceConfig) (*Provisioner, error) { p.UserKey = p.ValidationKey } - if attrs, ok := c.Config["attributes_json"].(string); ok { + if attrs, ok := d.GetOk("attributes_json"); ok { var m map[string]interface{} - if err := json.Unmarshal([]byte(attrs), &m); err != nil { + 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 := c.Config["vault_json"].(string); ok { + if vaults, ok := d.GetOk("vault_json"); ok { var m map[string]interface{} - if err := json.Unmarshal([]byte(vaults), &m); err != nil { + if err := json.Unmarshal([]byte(vaults.(string)), &m); err != nil { return nil, fmt.Errorf("Error parsing vault_json: %v", err) } @@ -724,3 +819,57 @@ func retryFunc(timeout time.Duration, f func() error) error { } } } + +func decodeDataToProvisioner(d *schema.ResourceData) *Provisioner { + return &Provisioner{ + AttributesJSON: d.Get("attributes_json").(string), + ClientOptions: getStringList(d.Get("client_options")), + DisableReporting: d.Get("disable_reporting").(bool), + Environment: d.Get("environment").(string), + FetchChefCertificates: d.Get("fetch_chef_certificates").(bool), + LogToFile: d.Get("log_to_file").(bool), + UsePolicyfile: d.Get("use_policyfile").(bool), + PolicyGroup: d.Get("policy_group").(string), + PolicyName: d.Get("policy_name").(string), + HTTPProxy: d.Get("http_proxy").(string), + HTTPSProxy: d.Get("https_proxy").(string), + NOProxy: getStringList(d.Get("no_proxy")), + NodeName: d.Get("node_name").(string), + OhaiHints: getStringList(d.Get("ohai_hints")), + OSType: d.Get("os_type").(string), + RecreateClient: d.Get("recreate_client").(bool), + PreventSudo: d.Get("prevent_sudo").(bool), + RunList: getStringList(d.Get("run_list")), + SecretKey: d.Get("secret_key").(string), + ServerURL: d.Get("server_url").(string), + SkipInstall: d.Get("skip_install").(bool), + SkipRegister: d.Get("skip_register").(bool), + 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), + } +} + +func getStringList(v interface{}) []string { + if v == nil { + return nil + } + switch l := v.(type) { + case []string: + return l + case []interface{}: + arr := make([]string, len(l)) + for i, x := range l { + arr[i] = x.(string) + } + return arr + default: + panic(fmt.Sprintf("Unsupported type: %T", v)) + } +} diff --git a/builtin/provisioners/chef/resource_provisioner_test.go b/builtin/provisioners/chef/resource_provisioner_test.go index 4da0645a2017..4bfbb4352a93 100644 --- a/builtin/provisioners/chef/resource_provisioner_test.go +++ b/builtin/provisioners/chef/resource_provisioner_test.go @@ -140,7 +140,7 @@ func TestResourceProvider_runChefClient(t *testing.T) { for k, tc := range cases { c.Commands = tc.Commands - p, err := decodeConfig(tc.Config) + p, err := decodeConfig(getTestResourceData(tc.Config)) if err != nil { t.Fatalf("Error: %v", err) } @@ -212,7 +212,7 @@ func TestResourceProvider_fetchChefCertificates(t *testing.T) { for k, tc := range cases { c.Commands = tc.Commands - p, err := decodeConfig(tc.Config) + p, err := decodeConfig(getTestResourceData(tc.Config)) if err != nil { t.Fatalf("Error: %v", err) } @@ -336,7 +336,7 @@ func TestResourceProvider_configureVaults(t *testing.T) { for k, tc := range cases { c.Commands = tc.Commands - p, err := decodeConfig(tc.Config) + p, err := decodeConfig(getTestResourceData(tc.Config)) if err != nil { t.Fatalf("Error: %v", err) } @@ -350,3 +350,7 @@ func TestResourceProvider_configureVaults(t *testing.T) { } } } + +func getTestResourceData(c *terraform.ResourceConfig) *schema.ResourceData { + return ResourceProvisioner().(*schema.Provisioner).TestResourceData(c) +} diff --git a/builtin/provisioners/chef/windows_provisioner_test.go b/builtin/provisioners/chef/windows_provisioner_test.go index 41cef64b110e..0bb0dc126aa4 100644 --- a/builtin/provisioners/chef/windows_provisioner_test.go +++ b/builtin/provisioners/chef/windows_provisioner_test.go @@ -80,7 +80,7 @@ func TestResourceProvider_windowsInstallChefClient(t *testing.T) { c.Commands = tc.Commands c.UploadScripts = tc.UploadScripts - p, err := decodeConfig(tc.Config) + p, err := decodeConfig(getTestResourceData(tc.Config)) if err != nil { t.Fatalf("Error: %v", err) } @@ -186,7 +186,7 @@ func TestResourceProvider_windowsCreateConfigFiles(t *testing.T) { c.Commands = tc.Commands c.Uploads = tc.Uploads - p, err := decodeConfig(tc.Config) + p, err := decodeConfig(getTestResourceData(tc.Config)) if err != nil { t.Fatalf("Error: %v", err) } diff --git a/helper/schema/resource_data.go b/helper/schema/resource_data.go index ea53662a1545..b040b63ee54b 100644 --- a/helper/schema/resource_data.go +++ b/helper/schema/resource_data.go @@ -463,8 +463,3 @@ func (d *ResourceData) get(addr []string, source getSource) getResult { Schema: schema, } } - -// TODO: Get rid of usages and remove. Added to fix chef provisioner -func (d *ResourceData) GetRawConfig() *terraform.ResourceConfig { - return d.config -} From bcd52d9a372cab676d12ffea6e4c633028169761 Mon Sep 17 00:00:00 2001 From: Vladislav Rassokhin Date: Fri, 2 Dec 2016 20:08:38 +0300 Subject: [PATCH 9/9] Rename test in 'remote-exec' provisioner and make it pass --- .../provisioners/remote-exec/resource_provisioner_test.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/builtin/provisioners/remote-exec/resource_provisioner_test.go b/builtin/provisioners/remote-exec/resource_provisioner_test.go index a02ec2daa5d5..261a4db2bec6 100644 --- a/builtin/provisioners/remote-exec/resource_provisioner_test.go +++ b/builtin/provisioners/remote-exec/resource_provisioner_test.go @@ -20,7 +20,7 @@ func TestProvisioner(t *testing.T) { } } -func TestResourceProvider_Validate_good(t *testing.T) { +func TestResourceProvider_Validate_bad_old_inline_as_string(t *testing.T) { c := testConfig(t, map[string]interface{}{ "inline": "echo foo", }) @@ -29,8 +29,8 @@ func TestResourceProvider_Validate_good(t *testing.T) { if len(warn) > 0 { t.Fatalf("Warnings: %v", warn) } - if len(errs) > 0 { - t.Fatalf("Errors: %v", errs) + if len(errs) == 0 { + t.Fatalf("Should have errors") } }