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.
This commit is contained in:
Sander van Harmelen 2017-05-19 20:42:14 +02:00
parent df4342bc3d
commit 0e422737ba
13 changed files with 310 additions and 411 deletions

View File

@ -14,10 +14,7 @@ const (
installURL = "https://www.chef.io/chef/install.sh" installURL = "https://www.chef.io/chef/install.sh"
) )
func (p *ProvisionerS) linuxInstallChefClient( func (p *provisioner) linuxInstallChefClient(o terraform.UIOutput, comm communicator.Communicator) error {
o terraform.UIOutput,
comm communicator.Communicator) error {
// Build up the command prefix // Build up the command prefix
prefix := "" prefix := ""
if p.HTTPProxy != "" { if p.HTTPProxy != "" {
@ -26,7 +23,7 @@ func (p *ProvisionerS) linuxInstallChefClient(
if p.HTTPSProxy != "" { if p.HTTPSProxy != "" {
prefix += fmt.Sprintf("https_proxy='%s' ", 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, ",")) 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)) return p.runCommand(o, comm, fmt.Sprintf("%srm -f install.sh", prefix))
} }
func (p *ProvisionerS) linuxCreateConfigFiles( func (p *provisioner) linuxCreateConfigFiles(o terraform.UIOutput, comm communicator.Communicator) error {
o terraform.UIOutput,
comm communicator.Communicator) error {
// Make sure the config directory exists // Make sure the config directory exists
if err := p.runCommand(o, comm, "mkdir -p "+linuxConfDir); err != nil { if err := p.runCommand(o, comm, "mkdir -p "+linuxConfDir); err != nil {
return err return err

View File

@ -6,22 +6,23 @@ import (
"testing" "testing"
"github.com/hashicorp/terraform/communicator" "github.com/hashicorp/terraform/communicator"
"github.com/hashicorp/terraform/helper/schema"
"github.com/hashicorp/terraform/terraform" "github.com/hashicorp/terraform/terraform"
) )
func TestResourceProvider_linuxInstallChefClient(t *testing.T) { func TestResourceProvider_linuxInstallChefClient(t *testing.T) {
cases := map[string]struct { cases := map[string]struct {
Config *terraform.ResourceConfig Config map[string]interface{}
Commands map[string]bool Commands map[string]bool
}{ }{
"Sudo": { "Sudo": {
Config: testConfig(t, map[string]interface{}{ Config: map[string]interface{}{
"node_name": "nodename1", "node_name": "nodename1",
"run_list": []interface{}{"cookbook::recipe"}, "run_list": []interface{}{"cookbook::recipe"},
"server_url": "https://chef.local", "server_url": "https://chef.local",
"user_name": "bob", "user_name": "bob",
"user_key": "USER-KEY", "user_key": "USER-KEY",
}), },
Commands: map[string]bool{ Commands: map[string]bool{
"sudo curl -LO https://www.chef.io/chef/install.sh": true, "sudo curl -LO https://www.chef.io/chef/install.sh": true,
@ -31,7 +32,7 @@ func TestResourceProvider_linuxInstallChefClient(t *testing.T) {
}, },
"NoSudo": { "NoSudo": {
Config: testConfig(t, map[string]interface{}{ Config: map[string]interface{}{
"node_name": "nodename1", "node_name": "nodename1",
"prevent_sudo": true, "prevent_sudo": true,
"run_list": []interface{}{"cookbook::recipe"}, "run_list": []interface{}{"cookbook::recipe"},
@ -39,7 +40,7 @@ func TestResourceProvider_linuxInstallChefClient(t *testing.T) {
"server_url": "https://chef.local", "server_url": "https://chef.local",
"user_name": "bob", "user_name": "bob",
"user_key": "USER-KEY", "user_key": "USER-KEY",
}), },
Commands: map[string]bool{ Commands: map[string]bool{
"curl -LO https://www.chef.io/chef/install.sh": true, "curl -LO https://www.chef.io/chef/install.sh": true,
@ -49,7 +50,7 @@ func TestResourceProvider_linuxInstallChefClient(t *testing.T) {
}, },
"HTTPProxy": { "HTTPProxy": {
Config: testConfig(t, map[string]interface{}{ Config: map[string]interface{}{
"http_proxy": "http://proxy.local", "http_proxy": "http://proxy.local",
"node_name": "nodename1", "node_name": "nodename1",
"prevent_sudo": true, "prevent_sudo": true,
@ -57,7 +58,7 @@ func TestResourceProvider_linuxInstallChefClient(t *testing.T) {
"server_url": "https://chef.local", "server_url": "https://chef.local",
"user_name": "bob", "user_name": "bob",
"user_key": "USER-KEY", "user_key": "USER-KEY",
}), },
Commands: map[string]bool{ Commands: map[string]bool{
"http_proxy='http://proxy.local' curl -LO https://www.chef.io/chef/install.sh": true, "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": { "HTTPSProxy": {
Config: testConfig(t, map[string]interface{}{ Config: map[string]interface{}{
"https_proxy": "https://proxy.local", "https_proxy": "https://proxy.local",
"node_name": "nodename1", "node_name": "nodename1",
"prevent_sudo": true, "prevent_sudo": true,
@ -75,7 +76,7 @@ func TestResourceProvider_linuxInstallChefClient(t *testing.T) {
"server_url": "https://chef.local", "server_url": "https://chef.local",
"user_name": "bob", "user_name": "bob",
"user_key": "USER-KEY", "user_key": "USER-KEY",
}), },
Commands: map[string]bool{ Commands: map[string]bool{
"https_proxy='https://proxy.local' curl -LO https://www.chef.io/chef/install.sh": true, "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": { "NoProxy": {
Config: testConfig(t, map[string]interface{}{ Config: map[string]interface{}{
"http_proxy": "http://proxy.local", "http_proxy": "http://proxy.local",
"no_proxy": []interface{}{"http://local.local", "http://local.org"}, "no_proxy": []interface{}{"http://local.local", "http://local.org"},
"node_name": "nodename1", "node_name": "nodename1",
@ -94,7 +95,7 @@ func TestResourceProvider_linuxInstallChefClient(t *testing.T) {
"server_url": "https://chef.local", "server_url": "https://chef.local",
"user_name": "bob", "user_name": "bob",
"user_key": "USER-KEY", "user_key": "USER-KEY",
}), },
Commands: map[string]bool{ Commands: map[string]bool{
"http_proxy='http://proxy.local' no_proxy='http://local.local,http://local.org' " + "http_proxy='http://proxy.local' no_proxy='http://local.local,http://local.org' " +
@ -107,7 +108,7 @@ func TestResourceProvider_linuxInstallChefClient(t *testing.T) {
}, },
"Version": { "Version": {
Config: testConfig(t, map[string]interface{}{ Config: map[string]interface{}{
"node_name": "nodename1", "node_name": "nodename1",
"prevent_sudo": true, "prevent_sudo": true,
"run_list": []interface{}{"cookbook::recipe"}, "run_list": []interface{}{"cookbook::recipe"},
@ -115,7 +116,7 @@ func TestResourceProvider_linuxInstallChefClient(t *testing.T) {
"user_name": "bob", "user_name": "bob",
"user_key": "USER-KEY", "user_key": "USER-KEY",
"version": "11.18.6", "version": "11.18.6",
}), },
Commands: map[string]bool{ Commands: map[string]bool{
"curl -LO https://www.chef.io/chef/install.sh": true, "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 { for k, tc := range cases {
c.Commands = tc.Commands 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 { if err != nil {
t.Fatalf("Error: %v", err) t.Fatalf("Error: %v", err)
} }
@ -147,12 +150,12 @@ func TestResourceProvider_linuxInstallChefClient(t *testing.T) {
func TestResourceProvider_linuxCreateConfigFiles(t *testing.T) { func TestResourceProvider_linuxCreateConfigFiles(t *testing.T) {
cases := map[string]struct { cases := map[string]struct {
Config *terraform.ResourceConfig Config map[string]interface{}
Commands map[string]bool Commands map[string]bool
Uploads map[string]string Uploads map[string]string
}{ }{
"Sudo": { "Sudo": {
Config: testConfig(t, map[string]interface{}{ Config: map[string]interface{}{
"ohai_hints": []interface{}{"test-fixtures/ohaihint.json"}, "ohai_hints": []interface{}{"test-fixtures/ohaihint.json"},
"node_name": "nodename1", "node_name": "nodename1",
"run_list": []interface{}{"cookbook::recipe"}, "run_list": []interface{}{"cookbook::recipe"},
@ -160,7 +163,7 @@ func TestResourceProvider_linuxCreateConfigFiles(t *testing.T) {
"server_url": "https://chef.local", "server_url": "https://chef.local",
"user_name": "bob", "user_name": "bob",
"user_key": "USER-KEY", "user_key": "USER-KEY",
}), },
Commands: map[string]bool{ Commands: map[string]bool{
"sudo mkdir -p " + linuxConfDir: true, "sudo mkdir -p " + linuxConfDir: true,
@ -187,7 +190,7 @@ func TestResourceProvider_linuxCreateConfigFiles(t *testing.T) {
}, },
"NoSudo": { "NoSudo": {
Config: testConfig(t, map[string]interface{}{ Config: map[string]interface{}{
"node_name": "nodename1", "node_name": "nodename1",
"prevent_sudo": true, "prevent_sudo": true,
"run_list": []interface{}{"cookbook::recipe"}, "run_list": []interface{}{"cookbook::recipe"},
@ -195,7 +198,7 @@ func TestResourceProvider_linuxCreateConfigFiles(t *testing.T) {
"server_url": "https://chef.local", "server_url": "https://chef.local",
"user_name": "bob", "user_name": "bob",
"user_key": "USER-KEY", "user_key": "USER-KEY",
}), },
Commands: map[string]bool{ Commands: map[string]bool{
"mkdir -p " + linuxConfDir: true, "mkdir -p " + linuxConfDir: true,
@ -210,7 +213,7 @@ func TestResourceProvider_linuxCreateConfigFiles(t *testing.T) {
}, },
"Proxy": { "Proxy": {
Config: testConfig(t, map[string]interface{}{ Config: map[string]interface{}{
"http_proxy": "http://proxy.local", "http_proxy": "http://proxy.local",
"https_proxy": "https://proxy.local", "https_proxy": "https://proxy.local",
"no_proxy": []interface{}{"http://local.local", "https://local.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", "ssl_verify_mode": "verify_none",
"user_name": "bob", "user_name": "bob",
"user_key": "USER-KEY", "user_key": "USER-KEY",
}), },
Commands: map[string]bool{ Commands: map[string]bool{
"mkdir -p " + linuxConfDir: true, "mkdir -p " + linuxConfDir: true,
@ -237,7 +240,7 @@ func TestResourceProvider_linuxCreateConfigFiles(t *testing.T) {
}, },
"Attributes JSON": { "Attributes JSON": {
Config: testConfig(t, map[string]interface{}{ Config: map[string]interface{}{
"attributes_json": `{"key1":{"subkey1":{"subkey2a":["val1","val2","val3"],` + "attributes_json": `{"key1":{"subkey1":{"subkey2a":["val1","val2","val3"],` +
`"subkey2b":{"subkey3":"value3"}}},"key2":"value2"}`, `"subkey2b":{"subkey3":"value3"}}},"key2":"value2"}`,
"node_name": "nodename1", "node_name": "nodename1",
@ -247,7 +250,7 @@ func TestResourceProvider_linuxCreateConfigFiles(t *testing.T) {
"server_url": "https://chef.local", "server_url": "https://chef.local",
"user_name": "bob", "user_name": "bob",
"user_key": "USER-KEY", "user_key": "USER-KEY",
}), },
Commands: map[string]bool{ Commands: map[string]bool{
"mkdir -p " + linuxConfDir: true, "mkdir -p " + linuxConfDir: true,
@ -270,7 +273,9 @@ func TestResourceProvider_linuxCreateConfigFiles(t *testing.T) {
c.Commands = tc.Commands c.Commands = tc.Commands
c.Uploads = tc.Uploads 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 { if err != nil {
t.Fatalf("Error: %v", err) t.Fatalf("Error: %v", err)
} }

View File

@ -2,6 +2,7 @@ package chef
import ( import (
"bytes" "bytes"
"context"
"encoding/json" "encoding/json"
"errors" "errors"
"fmt" "fmt"
@ -15,7 +16,6 @@ import (
"text/template" "text/template"
"time" "time"
"context"
"github.com/hashicorp/terraform/communicator" "github.com/hashicorp/terraform/communicator"
"github.com/hashicorp/terraform/communicator/remote" "github.com/hashicorp/terraform/communicator/remote"
"github.com/hashicorp/terraform/helper/schema" "github.com/hashicorp/terraform/helper/schema"
@ -82,9 +82,10 @@ enable_reporting false
{{ end }} {{ end }}
` `
// ProvisionerS represents a Chef provisioner type provisionFn func(terraform.UIOutput, communicator.Communicator) error
type ProvisionerS struct {
AttributesJSON string type provisioner struct {
Attributes map[string]interface{}
ClientOptions []string ClientOptions []string
DisableReporting bool DisableReporting bool
Environment string Environment string
@ -110,180 +111,153 @@ type ProvisionerS struct {
SSLVerifyMode string SSLVerifyMode string
UserName string UserName string
UserKey string UserKey string
VaultJSON string Vaults map[string][]string
Version string Version string
attributes map[string]interface{}
vaults map[string][]string
cleanupUserKeyCmd string cleanupUserKeyCmd string
createConfigFiles func(terraform.UIOutput, communicator.Communicator) error createConfigFiles provisionFn
installChefClient func(terraform.UIOutput, communicator.Communicator) error installChefClient provisionFn
fetchChefCertificates func(terraform.UIOutput, communicator.Communicator) error fetchChefCertificates provisionFn
generateClientKey func(terraform.UIOutput, communicator.Communicator) error generateClientKey provisionFn
configureVaults func(terraform.UIOutput, communicator.Communicator) error configureVaults provisionFn
runChefClient func(terraform.UIOutput, communicator.Communicator) error runChefClient provisionFn
useSudo bool useSudo bool
// Deprecated Fields
ValidationClientName string
ValidationKey string
} }
// Provisioner returns a Chef provisioner
func Provisioner() terraform.ResourceProvisioner { func Provisioner() terraform.ResourceProvisioner {
return &schema.Provisioner{ return &schema.Provisioner{
Schema: map[string]*schema.Schema{ Schema: map[string]*schema.Schema{
"attributes_json": { "node_name": &schema.Schema{
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": {
Type: schema.TypeString, Type: schema.TypeString,
Required: true, Required: true,
}, },
"ohai_hints": { "server_url": &schema.Schema{
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, Type: schema.TypeString,
Required: true, 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, Type: schema.TypeBool,
Optional: true, Optional: true,
}, },
"skip_register": { "environment": &schema.Schema{
Type: schema.TypeString,
Optional: true,
Default: defaultEnv,
},
"fetch_chef_certificates": &schema.Schema{
Type: schema.TypeBool, Type: schema.TypeBool,
Optional: true, 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, Type: schema.TypeString,
Optional: true, Optional: true,
}, },
"user_name": { "policy_name": &schema.Schema{
Type: schema.TypeString, Type: schema.TypeString,
Optional: true, Optional: true,
}, },
"user_key": { "http_proxy": &schema.Schema{
Type: schema.TypeString, Type: schema.TypeString,
Optional: true, Optional: true,
}, },
"vault_json": { "https_proxy": &schema.Schema{
Type: schema.TypeString, Type: schema.TypeString,
Optional: true, 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, Type: schema.TypeString,
Optional: true, Optional: true,
}, },
"ohai_hints": &schema.Schema{
// Deprecated Type: schema.TypeList,
"validation_client_name": { Elem: &schema.Schema{Type: schema.TypeString},
Type: schema.TypeString, Optional: true,
Deprecated: "Please use user_name instead",
Optional: true,
}, },
"os_type": &schema.Schema{
"validation_key": { Type: schema.TypeString,
Type: schema.TypeString, Optional: true,
Deprecated: "Please use user_key instead", },
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) // TODO: Support context cancelling (Provisioner Stop)
// Apply executes the file provisioner func applyFn(ctx context.Context) error {
func Apply(ctx context.Context) error {
o := ctx.Value(schema.ProvOutputKey).(terraform.UIOutput) o := ctx.Value(schema.ProvOutputKey).(terraform.UIOutput)
d := ctx.Value(schema.ProvConfigDataKey).(*schema.ResourceData) d := ctx.Value(schema.ProvConfigDataKey).(*schema.ResourceData)
// Decode the raw config for this provisioner // Decode the raw config for this provisioner
p, err := decodeConfig(d) p, err := decodeConfig(d)
if err != nil { if err != nil {
@ -291,8 +265,7 @@ func Apply(ctx context.Context) error {
} }
if p.OSType == "" { if p.OSType == "" {
t := d.State().Ephemeral.ConnInfo["type"] switch t := d.State().Ephemeral.ConnInfo["type"]; t {
switch t {
case "ssh", "": // The default connection type is ssh, so if the type is empty assume ssh case "ssh", "": // The default connection type is ssh, so if the type is empty assume ssh
p.OSType = "linux" p.OSType = "linux"
case "winrm": case "winrm":
@ -334,8 +307,7 @@ func Apply(ctx context.Context) error {
// Wait and retry until we establish the connection // Wait and retry until we establish the connection
err = retryFunc(comm.Timeout(), func() error { err = retryFunc(comm.Timeout(), func() error {
err := comm.Connect(o) return comm.Connect(o)
return err
}) })
if err != nil { if err != nil {
return err return err
@ -377,7 +349,7 @@ func Apply(ctx context.Context) error {
} }
} }
if p.VaultJSON != "" { if p.Vaults != nil {
o.Output("Configure Chef vaults...") o.Output("Configure Chef vaults...")
if err := p.configureVaults(o, comm); err != nil { if err := p.configureVaults(o, comm); err != nil {
return err return err
@ -396,8 +368,7 @@ func Apply(ctx context.Context) error {
return nil return nil
} }
// Validate checks if the required arguments are configured func validateFn(d *schema.ResourceData) (ws []string, es []error) {
func Validate(d *schema.ResourceData) (ws []string, es []error) {
p, err := decodeConfig(d) p, err := decodeConfig(d)
if err != nil { if err != nil {
es = append(es, err) es = append(es, err)
@ -413,94 +384,11 @@ func Validate(d *schema.ResourceData) (ws []string, es []error) {
if p.UsePolicyfile && p.PolicyGroup == "" { if p.UsePolicyfile && p.PolicyGroup == "" {
es = append(es, errors.New("Policyfile enabled but key not found: policy_group")) 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 return ws, es
} }
func decodeConfig(d *schema.ResourceData) (*ProvisionerS, error) { func (p *provisioner) deployConfigFiles(o terraform.UIOutput, comm communicator.Communicator, confDir string) 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 {
// Copy the user key to the new instance // Copy the user key to the new instance
pk := strings.NewReader(p.UserKey) pk := strings.NewReader(p.UserKey)
if err := comm.Upload(path.Join(confDir, p.UserName+".pem"), pk); err != nil { 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 // 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) return fmt.Errorf("Uploading %s failed: %v", clienrb, err)
} }
// Create a map with first boot settings // Create a map with first boot settings
fb := make(map[string]interface{}) fb := make(map[string]interface{})
if p.attributes != nil { if p.Attributes != nil {
fb = p.attributes fb = p.Attributes
} }
// Check if the run_list was also in the attributes and if so log a warning // 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 return nil
} }
func (p *ProvisionerS) deployOhaiHints( func (p *provisioner) deployOhaiHints(o terraform.UIOutput, comm communicator.Communicator, hintDir string) error {
o terraform.UIOutput,
comm communicator.Communicator,
hintDir string) error {
for _, hint := range p.OhaiHints { for _, hint := range p.OhaiHints {
// Open the hint file // Open the hint file
f, err := os.Open(hint) f, err := os.Open(hint)
@ -592,7 +477,7 @@ func (p *ProvisionerS) deployOhaiHints(
return nil return nil
} }
func (p *ProvisionerS) fetchChefCertificatesFunc( func (p *provisioner) fetchChefCertificatesFunc(
knifeCmd string, knifeCmd string,
confDir string) func(terraform.UIOutput, communicator.Communicator) error { confDir string) func(terraform.UIOutput, communicator.Communicator) error {
return func(o terraform.UIOutput, comm communicator.Communicator) error { return func(o terraform.UIOutput, comm communicator.Communicator) error {
@ -603,10 +488,7 @@ func (p *ProvisionerS) fetchChefCertificatesFunc(
} }
} }
func (p *ProvisionerS) generateClientKeyFunc( func (p *provisioner) generateClientKeyFunc(knifeCmd string, confDir string, noOutput string) provisionFn {
knifeCmd string,
confDir string,
noOutput string) func(terraform.UIOutput, communicator.Communicator) error {
return func(o terraform.UIOutput, comm communicator.Communicator) error { return func(o terraform.UIOutput, comm communicator.Communicator) error {
options := fmt.Sprintf("-c %s -u %s --key %s", options := fmt.Sprintf("-c %s -u %s --key %s",
path.Join(confDir, clienrb), path.Join(confDir, clienrb),
@ -664,10 +546,7 @@ func (p *ProvisionerS) generateClientKeyFunc(
} }
} }
func (p *ProvisionerS) configureVaultsFunc( func (p *provisioner) configureVaultsFunc(gemCmd string, knifeCmd string, confDir string) provisionFn {
gemCmd string,
knifeCmd string,
confDir string) func(terraform.UIOutput, communicator.Communicator) error {
return func(o terraform.UIOutput, comm communicator.Communicator) error { return func(o terraform.UIOutput, comm communicator.Communicator) error {
if err := p.runCommand(o, comm, fmt.Sprintf("%s install chef-vault", gemCmd)); err != nil { if err := p.runCommand(o, comm, fmt.Sprintf("%s install chef-vault", gemCmd)); err != nil {
return err return err
@ -679,7 +558,7 @@ func (p *ProvisionerS) configureVaultsFunc(
path.Join(confDir, p.UserName+".pem"), path.Join(confDir, p.UserName+".pem"),
) )
for vault, items := range p.vaults { for vault, items := range p.Vaults {
for _, item := range items { for _, item := range items {
updateCmd := fmt.Sprintf("%s vault update %s %s -C %s -M client %s", updateCmd := fmt.Sprintf("%s vault update %s %s -C %s -M client %s",
knifeCmd, knifeCmd,
@ -698,9 +577,7 @@ func (p *ProvisionerS) configureVaultsFunc(
} }
} }
func (p *ProvisionerS) runChefClientFunc( func (p *provisioner) runChefClientFunc(chefCmd string, confDir string) provisionFn {
chefCmd string,
confDir string) func(terraform.UIOutput, communicator.Communicator) error {
return func(o terraform.UIOutput, comm communicator.Communicator) error { return func(o terraform.UIOutput, comm communicator.Communicator) error {
fb := path.Join(confDir, firstBoot) fb := path.Join(confDir, firstBoot)
var cmd string var cmd string
@ -736,7 +613,7 @@ func (p *ProvisionerS) runChefClientFunc(
} }
// Output implementation of terraform.UIOutput interface // Output implementation of terraform.UIOutput interface
func (p *ProvisionerS) Output(output string) { func (p *provisioner) Output(output string) {
logFile := path.Join(logfileDir, p.NodeName) logFile := path.Join(logfileDir, p.NodeName)
f, err := os.OpenFile(logFile, os.O_APPEND|os.O_WRONLY, 0666) f, err := os.OpenFile(logFile, os.O_APPEND|os.O_WRONLY, 0666)
if err != nil { if err != nil {
@ -762,10 +639,7 @@ func (p *ProvisionerS) Output(output string) {
} }
// runCommand is used to run already prepared commands // runCommand is used to run already prepared commands
func (p *ProvisionerS) runCommand( func (p *provisioner) runCommand(o terraform.UIOutput, comm communicator.Communicator, command string) error {
o terraform.UIOutput,
comm communicator.Communicator,
command string) error {
// Unless prevented, prefix the command with sudo // Unless prevented, prefix the command with sudo
if p.useSudo { if p.useSudo {
command = "sudo " + command command = "sudo " + command
@ -804,7 +678,7 @@ func (p *ProvisionerS) runCommand(
return err 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) defer close(doneCh)
lr := linereader.New(r) lr := linereader.New(r)
for line := range lr.Ch { for line := range lr.Ch {
@ -830,9 +704,8 @@ func retryFunc(timeout time.Duration, f func() error) error {
} }
} }
func decodeDataToProvisioner(d *schema.ResourceData) *ProvisionerS { func decodeConfig(d *schema.ResourceData) (*provisioner, error) {
return &ProvisionerS{ p := &provisioner{
AttributesJSON: d.Get("attributes_json").(string),
ClientOptions: getStringList(d.Get("client_options")), ClientOptions: getStringList(d.Get("client_options")),
DisableReporting: d.Get("disable_reporting").(bool), DisableReporting: d.Get("disable_reporting").(bool),
Environment: d.Get("environment").(string), Environment: d.Get("environment").(string),
@ -858,13 +731,54 @@ func decodeDataToProvisioner(d *schema.ResourceData) *ProvisionerS {
SSLVerifyMode: d.Get("ssl_verify_mode").(string), SSLVerifyMode: d.Get("ssl_verify_mode").(string),
UserName: d.Get("user_name").(string), UserName: d.Get("user_name").(string),
UserKey: d.Get("user_key").(string), UserKey: d.Get("user_key").(string),
VaultJSON: d.Get("vault_json").(string),
Version: d.Get("version").(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 { func getStringList(v interface{}) []string {

View File

@ -30,8 +30,8 @@ func TestResourceProvider_Validate_good(t *testing.T) {
"user_name": "bob", "user_name": "bob",
"user_key": "USER-KEY", "user_key": "USER-KEY",
}) })
r := Provisioner()
warn, errs := r.Validate(c) warn, errs := Provisioner().Validate(c)
if len(warn) > 0 { if len(warn) > 0 {
t.Fatalf("Warnings: %v", warn) t.Fatalf("Warnings: %v", warn)
} }
@ -44,8 +44,8 @@ func TestResourceProvider_Validate_bad(t *testing.T) {
c := testConfig(t, map[string]interface{}{ c := testConfig(t, map[string]interface{}{
"invalid": "nope", "invalid": "nope",
}) })
p := Provisioner()
warn, errs := p.Validate(c) warn, errs := Provisioner().Validate(c)
if len(warn) > 0 { if len(warn) > 0 {
t.Fatalf("Warnings: %v", warn) t.Fatalf("Warnings: %v", warn)
} }
@ -66,8 +66,8 @@ func TestResourceProvider_Validate_computedValues(t *testing.T) {
"user_key": "USER-KEY", "user_key": "USER-KEY",
"attributes_json": config.UnknownVariableValue, "attributes_json": config.UnknownVariableValue,
}) })
r := Provisioner()
warn, errs := r.Validate(c) warn, errs := Provisioner().Validate(c)
if len(warn) > 0 { if len(warn) > 0 {
t.Fatalf("Warnings: %v", warn) 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) { func TestResourceProvider_runChefClient(t *testing.T) {
cases := map[string]struct { cases := map[string]struct {
Config *terraform.ResourceConfig Config map[string]interface{}
ChefCmd string ChefCmd string
ConfDir string ConfDir string
Commands map[string]bool Commands map[string]bool
}{ }{
"Sudo": { "Sudo": {
Config: testConfig(t, map[string]interface{}{ Config: map[string]interface{}{
"node_name": "nodename1", "node_name": "nodename1",
"run_list": []interface{}{"cookbook::recipe"}, "run_list": []interface{}{"cookbook::recipe"},
"server_url": "https://chef.local", "server_url": "https://chef.local",
"user_name": "bob", "user_name": "bob",
"user_key": "USER-KEY", "user_key": "USER-KEY",
}), },
ChefCmd: linuxChefCmd, ChefCmd: linuxChefCmd,
@ -113,14 +104,14 @@ func TestResourceProvider_runChefClient(t *testing.T) {
}, },
"NoSudo": { "NoSudo": {
Config: testConfig(t, map[string]interface{}{ Config: map[string]interface{}{
"node_name": "nodename1", "node_name": "nodename1",
"prevent_sudo": true, "prevent_sudo": true,
"run_list": []interface{}{"cookbook::recipe"}, "run_list": []interface{}{"cookbook::recipe"},
"server_url": "https://chef.local", "server_url": "https://chef.local",
"user_name": "bob", "user_name": "bob",
"user_key": "USER-KEY", "user_key": "USER-KEY",
}), },
ChefCmd: linuxChefCmd, ChefCmd: linuxChefCmd,
@ -134,7 +125,7 @@ func TestResourceProvider_runChefClient(t *testing.T) {
}, },
"Environment": { "Environment": {
Config: testConfig(t, map[string]interface{}{ Config: map[string]interface{}{
"environment": "production", "environment": "production",
"node_name": "nodename1", "node_name": "nodename1",
"prevent_sudo": true, "prevent_sudo": true,
@ -142,7 +133,7 @@ func TestResourceProvider_runChefClient(t *testing.T) {
"server_url": "https://chef.local", "server_url": "https://chef.local",
"user_name": "bob", "user_name": "bob",
"user_key": "USER-KEY", "user_key": "USER-KEY",
}), },
ChefCmd: windowsChefCmd, ChefCmd: windowsChefCmd,
@ -162,7 +153,9 @@ func TestResourceProvider_runChefClient(t *testing.T) {
for k, tc := range cases { for k, tc := range cases {
c.Commands = tc.Commands 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 { if err != nil {
t.Fatalf("Error: %v", err) t.Fatalf("Error: %v", err)
} }
@ -179,20 +172,20 @@ func TestResourceProvider_runChefClient(t *testing.T) {
func TestResourceProvider_fetchChefCertificates(t *testing.T) { func TestResourceProvider_fetchChefCertificates(t *testing.T) {
cases := map[string]struct { cases := map[string]struct {
Config *terraform.ResourceConfig Config map[string]interface{}
KnifeCmd string KnifeCmd string
ConfDir string ConfDir string
Commands map[string]bool Commands map[string]bool
}{ }{
"Sudo": { "Sudo": {
Config: testConfig(t, map[string]interface{}{ Config: map[string]interface{}{
"fetch_chef_certificates": true, "fetch_chef_certificates": true,
"node_name": "nodename1", "node_name": "nodename1",
"run_list": []interface{}{"cookbook::recipe"}, "run_list": []interface{}{"cookbook::recipe"},
"server_url": "https://chef.local", "server_url": "https://chef.local",
"user_name": "bob", "user_name": "bob",
"user_key": "USER-KEY", "user_key": "USER-KEY",
}), },
KnifeCmd: linuxKnifeCmd, KnifeCmd: linuxKnifeCmd,
@ -206,7 +199,7 @@ func TestResourceProvider_fetchChefCertificates(t *testing.T) {
}, },
"NoSudo": { "NoSudo": {
Config: testConfig(t, map[string]interface{}{ Config: map[string]interface{}{
"fetch_chef_certificates": true, "fetch_chef_certificates": true,
"node_name": "nodename1", "node_name": "nodename1",
"prevent_sudo": true, "prevent_sudo": true,
@ -214,7 +207,7 @@ func TestResourceProvider_fetchChefCertificates(t *testing.T) {
"server_url": "https://chef.local", "server_url": "https://chef.local",
"user_name": "bob", "user_name": "bob",
"user_key": "USER-KEY", "user_key": "USER-KEY",
}), },
KnifeCmd: windowsKnifeCmd, KnifeCmd: windowsKnifeCmd,
@ -234,7 +227,9 @@ func TestResourceProvider_fetchChefCertificates(t *testing.T) {
for k, tc := range cases { for k, tc := range cases {
c.Commands = tc.Commands 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 { if err != nil {
t.Fatalf("Error: %v", err) t.Fatalf("Error: %v", err)
} }
@ -251,14 +246,14 @@ func TestResourceProvider_fetchChefCertificates(t *testing.T) {
func TestResourceProvider_configureVaults(t *testing.T) { func TestResourceProvider_configureVaults(t *testing.T) {
cases := map[string]struct { cases := map[string]struct {
Config *terraform.ResourceConfig Config map[string]interface{}
GemCmd string GemCmd string
KnifeCmd string KnifeCmd string
ConfDir string ConfDir string
Commands map[string]bool Commands map[string]bool
}{ }{
"Linux Vault string": { "Linux Vault string": {
Config: testConfig(t, map[string]interface{}{ Config: map[string]interface{}{
"node_name": "nodename1", "node_name": "nodename1",
"prevent_sudo": true, "prevent_sudo": true,
"run_list": []interface{}{"cookbook::recipe"}, "run_list": []interface{}{"cookbook::recipe"},
@ -266,7 +261,7 @@ func TestResourceProvider_configureVaults(t *testing.T) {
"user_name": "bob", "user_name": "bob",
"user_key": "USER-KEY", "user_key": "USER-KEY",
"vault_json": `{"vault1": "item1"}`, "vault_json": `{"vault1": "item1"}`,
}), },
GemCmd: linuxGemCmd, GemCmd: linuxGemCmd,
KnifeCmd: linuxKnifeCmd, KnifeCmd: linuxKnifeCmd,
@ -280,7 +275,7 @@ func TestResourceProvider_configureVaults(t *testing.T) {
}, },
"Linux Vault []string": { "Linux Vault []string": {
Config: testConfig(t, map[string]interface{}{ Config: map[string]interface{}{
"fetch_chef_certificates": true, "fetch_chef_certificates": true,
"node_name": "nodename1", "node_name": "nodename1",
"prevent_sudo": true, "prevent_sudo": true,
@ -289,7 +284,7 @@ func TestResourceProvider_configureVaults(t *testing.T) {
"user_name": "bob", "user_name": "bob",
"user_key": "USER-KEY", "user_key": "USER-KEY",
"vault_json": `{"vault1": ["item1", "item2"]}`, "vault_json": `{"vault1": ["item1", "item2"]}`,
}), },
GemCmd: linuxGemCmd, GemCmd: linuxGemCmd,
KnifeCmd: linuxKnifeCmd, KnifeCmd: linuxKnifeCmd,
@ -305,7 +300,7 @@ func TestResourceProvider_configureVaults(t *testing.T) {
}, },
"Windows Vault string": { "Windows Vault string": {
Config: testConfig(t, map[string]interface{}{ Config: map[string]interface{}{
"node_name": "nodename1", "node_name": "nodename1",
"prevent_sudo": true, "prevent_sudo": true,
"run_list": []interface{}{"cookbook::recipe"}, "run_list": []interface{}{"cookbook::recipe"},
@ -313,7 +308,7 @@ func TestResourceProvider_configureVaults(t *testing.T) {
"user_name": "bob", "user_name": "bob",
"user_key": "USER-KEY", "user_key": "USER-KEY",
"vault_json": `{"vault1": "item1"}`, "vault_json": `{"vault1": "item1"}`,
}), },
GemCmd: windowsGemCmd, GemCmd: windowsGemCmd,
KnifeCmd: windowsKnifeCmd, KnifeCmd: windowsKnifeCmd,
@ -327,7 +322,7 @@ func TestResourceProvider_configureVaults(t *testing.T) {
}, },
"Windows Vault []string": { "Windows Vault []string": {
Config: testConfig(t, map[string]interface{}{ Config: map[string]interface{}{
"fetch_chef_certificates": true, "fetch_chef_certificates": true,
"node_name": "nodename1", "node_name": "nodename1",
"prevent_sudo": true, "prevent_sudo": true,
@ -336,7 +331,7 @@ func TestResourceProvider_configureVaults(t *testing.T) {
"user_name": "bob", "user_name": "bob",
"user_key": "USER-KEY", "user_key": "USER-KEY",
"vault_json": `{"vault1": ["item1", "item2"]}`, "vault_json": `{"vault1": ["item1", "item2"]}`,
}), },
GemCmd: windowsGemCmd, GemCmd: windowsGemCmd,
KnifeCmd: windowsKnifeCmd, KnifeCmd: windowsKnifeCmd,
@ -358,7 +353,9 @@ func TestResourceProvider_configureVaults(t *testing.T) {
for k, tc := range cases { for k, tc := range cases {
c.Commands = tc.Commands 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 { if err != nil {
t.Fatalf("Error: %v", err) t.Fatalf("Error: %v", err)
} }
@ -373,6 +370,11 @@ func TestResourceProvider_configureVaults(t *testing.T) {
} }
} }
func getTestResourceData(c *terraform.ResourceConfig) *schema.ResourceData { func testConfig(t *testing.T, c map[string]interface{}) *terraform.ResourceConfig {
return schema.TestResourceDataConfig(Provisioner().(*schema.Provisioner).Schema, c) r, err := config.NewRawConfig(c)
if err != nil {
t.Fatalf("bad: %s", err)
}
return terraform.NewResourceConfig(r)
} }

View File

@ -46,9 +46,7 @@ Write-Host 'Installing Chef Client...'
Start-Process -FilePath msiexec -ArgumentList /qn, /i, $dest -Wait Start-Process -FilePath msiexec -ArgumentList /qn, /i, $dest -Wait
` `
func (p *ProvisionerS) windowsInstallChefClient( func (p *provisioner) windowsInstallChefClient(o terraform.UIOutput, comm communicator.Communicator) error {
o terraform.UIOutput,
comm communicator.Communicator) error {
script := path.Join(path.Dir(comm.ScriptPath()), "ChefClient.ps1") script := path.Join(path.Dir(comm.ScriptPath()), "ChefClient.ps1")
content := fmt.Sprintf(installScript, p.Version, p.HTTPProxy, strings.Join(p.NOProxy, ",")) 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) return p.runCommand(o, comm, installCmd)
} }
func (p *ProvisionerS) windowsCreateConfigFiles( func (p *provisioner) windowsCreateConfigFiles(o terraform.UIOutput, comm communicator.Communicator) error {
o terraform.UIOutput,
comm communicator.Communicator) error {
// Make sure the config directory exists // Make sure the config directory exists
cmd := fmt.Sprintf("cmd /c if not exist %q mkdir %q", windowsConfDir, windowsConfDir) cmd := fmt.Sprintf("cmd /c if not exist %q mkdir %q", windowsConfDir, windowsConfDir)
if err := p.runCommand(o, comm, cmd); err != nil { if err := p.runCommand(o, comm, cmd); err != nil {

View File

@ -6,23 +6,24 @@ import (
"testing" "testing"
"github.com/hashicorp/terraform/communicator" "github.com/hashicorp/terraform/communicator"
"github.com/hashicorp/terraform/helper/schema"
"github.com/hashicorp/terraform/terraform" "github.com/hashicorp/terraform/terraform"
) )
func TestResourceProvider_windowsInstallChefClient(t *testing.T) { func TestResourceProvider_windowsInstallChefClient(t *testing.T) {
cases := map[string]struct { cases := map[string]struct {
Config *terraform.ResourceConfig Config map[string]interface{}
Commands map[string]bool Commands map[string]bool
UploadScripts map[string]string UploadScripts map[string]string
}{ }{
"Default": { "Default": {
Config: testConfig(t, map[string]interface{}{ Config: map[string]interface{}{
"node_name": "nodename1", "node_name": "nodename1",
"run_list": []interface{}{"cookbook::recipe"}, "run_list": []interface{}{"cookbook::recipe"},
"server_url": "https://chef.local", "server_url": "https://chef.local",
"user_name": "bob", "user_name": "bob",
"user_key": "USER-KEY", "user_key": "USER-KEY",
}), },
Commands: map[string]bool{ Commands: map[string]bool{
"powershell -NoProfile -ExecutionPolicy Bypass -File ChefClient.ps1": true, "powershell -NoProfile -ExecutionPolicy Bypass -File ChefClient.ps1": true,
@ -34,7 +35,7 @@ func TestResourceProvider_windowsInstallChefClient(t *testing.T) {
}, },
"Proxy": { "Proxy": {
Config: testConfig(t, map[string]interface{}{ Config: map[string]interface{}{
"http_proxy": "http://proxy.local", "http_proxy": "http://proxy.local",
"no_proxy": []interface{}{"http://local.local", "http://local.org"}, "no_proxy": []interface{}{"http://local.local", "http://local.org"},
"node_name": "nodename1", "node_name": "nodename1",
@ -42,7 +43,7 @@ func TestResourceProvider_windowsInstallChefClient(t *testing.T) {
"server_url": "https://chef.local", "server_url": "https://chef.local",
"user_name": "bob", "user_name": "bob",
"user_key": "USER-KEY", "user_key": "USER-KEY",
}), },
Commands: map[string]bool{ Commands: map[string]bool{
"powershell -NoProfile -ExecutionPolicy Bypass -File ChefClient.ps1": true, "powershell -NoProfile -ExecutionPolicy Bypass -File ChefClient.ps1": true,
@ -54,14 +55,14 @@ func TestResourceProvider_windowsInstallChefClient(t *testing.T) {
}, },
"Version": { "Version": {
Config: testConfig(t, map[string]interface{}{ Config: map[string]interface{}{
"node_name": "nodename1", "node_name": "nodename1",
"run_list": []interface{}{"cookbook::recipe"}, "run_list": []interface{}{"cookbook::recipe"},
"server_url": "https://chef.local", "server_url": "https://chef.local",
"user_name": "bob", "user_name": "bob",
"user_key": "USER-KEY", "user_key": "USER-KEY",
"version": "11.18.6", "version": "11.18.6",
}), },
Commands: map[string]bool{ Commands: map[string]bool{
"powershell -NoProfile -ExecutionPolicy Bypass -File ChefClient.ps1": true, "powershell -NoProfile -ExecutionPolicy Bypass -File ChefClient.ps1": true,
@ -80,7 +81,9 @@ func TestResourceProvider_windowsInstallChefClient(t *testing.T) {
c.Commands = tc.Commands c.Commands = tc.Commands
c.UploadScripts = tc.UploadScripts 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 { if err != nil {
t.Fatalf("Error: %v", err) t.Fatalf("Error: %v", err)
} }
@ -96,12 +99,12 @@ func TestResourceProvider_windowsInstallChefClient(t *testing.T) {
func TestResourceProvider_windowsCreateConfigFiles(t *testing.T) { func TestResourceProvider_windowsCreateConfigFiles(t *testing.T) {
cases := map[string]struct { cases := map[string]struct {
Config *terraform.ResourceConfig Config map[string]interface{}
Commands map[string]bool Commands map[string]bool
Uploads map[string]string Uploads map[string]string
}{ }{
"Default": { "Default": {
Config: testConfig(t, map[string]interface{}{ Config: map[string]interface{}{
"ohai_hints": []interface{}{"test-fixtures/ohaihint.json"}, "ohai_hints": []interface{}{"test-fixtures/ohaihint.json"},
"node_name": "nodename1", "node_name": "nodename1",
"run_list": []interface{}{"cookbook::recipe"}, "run_list": []interface{}{"cookbook::recipe"},
@ -109,7 +112,7 @@ func TestResourceProvider_windowsCreateConfigFiles(t *testing.T) {
"server_url": "https://chef.local", "server_url": "https://chef.local",
"user_name": "bob", "user_name": "bob",
"user_key": "USER-KEY", "user_key": "USER-KEY",
}), },
Commands: map[string]bool{ Commands: map[string]bool{
fmt.Sprintf("cmd /c if not exist %q mkdir %q", windowsConfDir, windowsConfDir): true, fmt.Sprintf("cmd /c if not exist %q mkdir %q", windowsConfDir, windowsConfDir): true,
@ -128,7 +131,7 @@ func TestResourceProvider_windowsCreateConfigFiles(t *testing.T) {
}, },
"Proxy": { "Proxy": {
Config: testConfig(t, map[string]interface{}{ Config: map[string]interface{}{
"http_proxy": "http://proxy.local", "http_proxy": "http://proxy.local",
"https_proxy": "https://proxy.local", "https_proxy": "https://proxy.local",
"no_proxy": []interface{}{"http://local.local", "https://local.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", "ssl_verify_mode": "verify_none",
"user_name": "bob", "user_name": "bob",
"user_key": "USER-KEY", "user_key": "USER-KEY",
}), },
Commands: map[string]bool{ Commands: map[string]bool{
fmt.Sprintf("cmd /c if not exist %q mkdir %q", windowsConfDir, windowsConfDir): true, 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": { "Attributes JSON": {
Config: testConfig(t, map[string]interface{}{ Config: map[string]interface{}{
"attributes_json": `{"key1":{"subkey1":{"subkey2a":["val1","val2","val3"],` + "attributes_json": `{"key1":{"subkey1":{"subkey2a":["val1","val2","val3"],` +
`"subkey2b":{"subkey3":"value3"}}},"key2":"value2"}`, `"subkey2b":{"subkey3":"value3"}}},"key2":"value2"}`,
"node_name": "nodename1", "node_name": "nodename1",
@ -163,7 +166,7 @@ func TestResourceProvider_windowsCreateConfigFiles(t *testing.T) {
"server_url": "https://chef.local", "server_url": "https://chef.local",
"user_name": "bob", "user_name": "bob",
"user_key": "USER-KEY", "user_key": "USER-KEY",
}), },
Commands: map[string]bool{ Commands: map[string]bool{
fmt.Sprintf("cmd /c if not exist %q mkdir %q", windowsConfDir, windowsConfDir): true, 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.Commands = tc.Commands
c.Uploads = tc.Uploads 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 { if err != nil {
t.Fatalf("Error: %v", err) t.Fatalf("Error: %v", err)
} }

View File

@ -36,7 +36,7 @@ func Provisioner() terraform.ResourceProvisioner {
}, },
ApplyFunc: applyFn, 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 validateFn(d *schema.ResourceData) (ws []string, es []error) {
func Validate(d *schema.ResourceData) (ws []string, es []error) {
numSrc := 0 numSrc := 0
if _, ok := d.GetOk("source"); ok == true { if _, ok := d.GetOk("source"); ok == true {
numSrc++ numSrc++

View File

@ -23,8 +23,8 @@ func TestResourceProvider_Validate_good_source(t *testing.T) {
"source": "/tmp/foo", "source": "/tmp/foo",
"destination": "/tmp/bar", "destination": "/tmp/bar",
}) })
p := Provisioner()
warn, errs := p.Validate(c) warn, errs := Provisioner().Validate(c)
if len(warn) > 0 { if len(warn) > 0 {
t.Fatalf("Warnings: %v", warn) t.Fatalf("Warnings: %v", warn)
} }
@ -38,8 +38,8 @@ func TestResourceProvider_Validate_good_content(t *testing.T) {
"content": "value to copy", "content": "value to copy",
"destination": "/tmp/bar", "destination": "/tmp/bar",
}) })
p := Provisioner()
warn, errs := p.Validate(c) warn, errs := Provisioner().Validate(c)
if len(warn) > 0 { if len(warn) > 0 {
t.Fatalf("Warnings: %v", warn) t.Fatalf("Warnings: %v", warn)
} }
@ -52,8 +52,8 @@ func TestResourceProvider_Validate_bad_not_destination(t *testing.T) {
c := testConfig(t, map[string]interface{}{ c := testConfig(t, map[string]interface{}{
"source": "nope", "source": "nope",
}) })
p := Provisioner()
warn, errs := p.Validate(c) warn, errs := Provisioner().Validate(c)
if len(warn) > 0 { if len(warn) > 0 {
t.Fatalf("Warnings: %v", warn) t.Fatalf("Warnings: %v", warn)
} }
@ -66,8 +66,8 @@ func TestResourceProvider_Validate_bad_no_source(t *testing.T) {
c := testConfig(t, map[string]interface{}{ c := testConfig(t, map[string]interface{}{
"destination": "/tmp/bar", "destination": "/tmp/bar",
}) })
p := Provisioner()
warn, errs := p.Validate(c) warn, errs := Provisioner().Validate(c)
if len(warn) > 0 { if len(warn) > 0 {
t.Fatalf("Warnings: %v", warn) t.Fatalf("Warnings: %v", warn)
} }
@ -82,8 +82,8 @@ func TestResourceProvider_Validate_bad_to_many_src(t *testing.T) {
"content": "value to copy", "content": "value to copy",
"destination": "/tmp/bar", "destination": "/tmp/bar",
}) })
p := Provisioner()
warn, errs := p.Validate(c) warn, errs := Provisioner().Validate(c)
if len(warn) > 0 { if len(warn) > 0 {
t.Fatalf("Warnings: %v", warn) t.Fatalf("Warnings: %v", warn)
} }
@ -92,12 +92,11 @@ func TestResourceProvider_Validate_bad_to_many_src(t *testing.T) {
} }
} }
func testConfig( func testConfig(t *testing.T, c map[string]interface{}) *terraform.ResourceConfig {
t *testing.T,
c map[string]interface{}) *terraform.ResourceConfig {
r, err := config.NewRawConfig(c) r, err := config.NewRawConfig(c)
if err != nil { if err != nil {
t.Fatalf("bad: %s", err) t.Fatalf("bad: %s", err)
} }
return terraform.NewResourceConfig(r) return terraform.NewResourceConfig(r)
} }

View File

@ -30,6 +30,7 @@ func TestResourceProvider_Apply(t *testing.T) {
output := new(terraform.MockUIOutput) output := new(terraform.MockUIOutput)
p := Provisioner() p := Provisioner()
if err := p.Apply(output, nil, c); err != nil { if err := p.Apply(output, nil, c); err != nil {
t.Fatalf("err: %v", err) t.Fatalf("err: %v", err)
} }
@ -84,8 +85,8 @@ func TestResourceProvider_Validate_good(t *testing.T) {
c := testConfig(t, map[string]interface{}{ c := testConfig(t, map[string]interface{}{
"command": "echo foo", "command": "echo foo",
}) })
p := Provisioner()
warn, errs := p.Validate(c) warn, errs := Provisioner().Validate(c)
if len(warn) > 0 { if len(warn) > 0 {
t.Fatalf("Warnings: %v", warn) t.Fatalf("Warnings: %v", warn)
} }
@ -96,8 +97,8 @@ func TestResourceProvider_Validate_good(t *testing.T) {
func TestResourceProvider_Validate_missing(t *testing.T) { func TestResourceProvider_Validate_missing(t *testing.T) {
c := testConfig(t, map[string]interface{}{}) c := testConfig(t, map[string]interface{}{})
p := Provisioner()
warn, errs := p.Validate(c) warn, errs := Provisioner().Validate(c)
if len(warn) > 0 { if len(warn) > 0 {
t.Fatalf("Warnings: %v", warn) t.Fatalf("Warnings: %v", warn)
} }
@ -106,9 +107,7 @@ func TestResourceProvider_Validate_missing(t *testing.T) {
} }
} }
func testConfig( func testConfig(t *testing.T, c map[string]interface{}) *terraform.ResourceConfig {
t *testing.T,
c map[string]interface{}) *terraform.ResourceConfig {
r, err := config.NewRawConfig(c) r, err := config.NewRawConfig(c)
if err != nil { if err != nil {
t.Fatalf("bad: %s", err) t.Fatalf("bad: %s", err)

View File

@ -30,8 +30,8 @@ func TestResourceProvider_Validate_good(t *testing.T) {
c := testConfig(t, map[string]interface{}{ c := testConfig(t, map[string]interface{}{
"inline": "echo foo", "inline": "echo foo",
}) })
p := Provisioner()
warn, errs := p.Validate(c) warn, errs := Provisioner().Validate(c)
if len(warn) > 0 { if len(warn) > 0 {
t.Fatalf("Warnings: %v", warn) t.Fatalf("Warnings: %v", warn)
} }
@ -44,8 +44,8 @@ func TestResourceProvider_Validate_bad(t *testing.T) {
c := testConfig(t, map[string]interface{}{ c := testConfig(t, map[string]interface{}{
"invalid": "nope", "invalid": "nope",
}) })
p := Provisioner()
warn, errs := p.Validate(c) warn, errs := Provisioner().Validate(c)
if len(warn) > 0 { if len(warn) > 0 {
t.Fatalf("Warnings: %v", warn) t.Fatalf("Warnings: %v", warn)
} }
@ -60,7 +60,6 @@ exit 0
` `
func TestResourceProvider_generateScript(t *testing.T) { func TestResourceProvider_generateScript(t *testing.T) {
p := Provisioner().(*schema.Provisioner)
conf := map[string]interface{}{ conf := map[string]interface{}{
"inline": []interface{}{ "inline": []interface{}{
"cd /tmp", "cd /tmp",
@ -68,8 +67,10 @@ func TestResourceProvider_generateScript(t *testing.T) {
"exit 0", "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 { if err != nil {
t.Fatalf("err: %v", err) t.Fatalf("err: %v", err)
} }
@ -101,7 +102,6 @@ func TestResourceProvider_generateScriptEmptyInline(t *testing.T) {
} }
func TestResourceProvider_CollectScripts_inline(t *testing.T) { func TestResourceProvider_CollectScripts_inline(t *testing.T) {
p := Provisioner().(*schema.Provisioner)
conf := map[string]interface{}{ conf := map[string]interface{}{
"inline": []interface{}{ "inline": []interface{}{
"cd /tmp", "cd /tmp",
@ -110,8 +110,9 @@ func TestResourceProvider_CollectScripts_inline(t *testing.T) {
}, },
} }
scripts, err := collectScripts(schema.TestResourceDataRaw( scripts, err := collectScripts(
t, p.Schema, conf)) schema.TestResourceDataRaw(t, Provisioner().(*schema.Provisioner).Schema, conf),
)
if err != nil { if err != nil {
t.Fatalf("err: %v", err) t.Fatalf("err: %v", err)
} }
@ -132,13 +133,13 @@ func TestResourceProvider_CollectScripts_inline(t *testing.T) {
} }
func TestResourceProvider_CollectScripts_script(t *testing.T) { func TestResourceProvider_CollectScripts_script(t *testing.T) {
p := Provisioner().(*schema.Provisioner)
conf := map[string]interface{}{ conf := map[string]interface{}{
"script": "test-fixtures/script1.sh", "script": "test-fixtures/script1.sh",
} }
scripts, err := collectScripts(schema.TestResourceDataRaw( scripts, err := collectScripts(
t, p.Schema, conf)) schema.TestResourceDataRaw(t, Provisioner().(*schema.Provisioner).Schema, conf),
)
if err != nil { if err != nil {
t.Fatalf("err: %v", err) t.Fatalf("err: %v", err)
} }
@ -159,7 +160,6 @@ func TestResourceProvider_CollectScripts_script(t *testing.T) {
} }
func TestResourceProvider_CollectScripts_scripts(t *testing.T) { func TestResourceProvider_CollectScripts_scripts(t *testing.T) {
p := Provisioner().(*schema.Provisioner)
conf := map[string]interface{}{ conf := map[string]interface{}{
"scripts": []interface{}{ "scripts": []interface{}{
"test-fixtures/script1.sh", "test-fixtures/script1.sh",
@ -168,8 +168,9 @@ func TestResourceProvider_CollectScripts_scripts(t *testing.T) {
}, },
} }
scripts, err := collectScripts(schema.TestResourceDataRaw( scripts, err := collectScripts(
t, p.Schema, conf)) schema.TestResourceDataRaw(t, Provisioner().(*schema.Provisioner).Schema, conf),
)
if err != nil { if err != nil {
t.Fatalf("err: %v", err) t.Fatalf("err: %v", err)
} }
@ -234,9 +235,7 @@ func TestRetryFunc(t *testing.T) {
} }
} }
func testConfig( func testConfig(t *testing.T, c map[string]interface{}) *terraform.ResourceConfig {
t *testing.T,
c map[string]interface{}) *terraform.ResourceConfig {
r, err := config.NewRawConfig(c) r, err := config.NewRawConfig(c)
if err != nil { if err != nil {
t.Fatalf("bad: %s", err) t.Fatalf("bad: %s", err)

View File

@ -41,9 +41,8 @@ type Provisioner struct {
// information. // information.
ApplyFunc func(ctx context.Context) error ApplyFunc func(ctx context.Context) error
// ValidateFunc is the function for extended validation. This is optional. // ValidateFunc is a function for extended validation. This is optional
// It is given a resource data. // and should be used when individual field validation is not enough.
// Should be provided when Scheme is not enough.
ValidateFunc func(*ResourceData) ([]string, []error) ValidateFunc func(*ResourceData) ([]string, []error)
stopCtx context.Context stopCtx context.Context

View File

@ -28,10 +28,3 @@ func TestResourceDataRaw(
return result return result
} }
func TestResourceDataConfig(schema map[string]*Schema, config *terraform.ResourceConfig) *ResourceData {
return &ResourceData{
schema: schema,
config: config,
}
}

View File

@ -157,9 +157,3 @@ The following arguments are supported:
* `version (string)` - (Optional) The Chef Client version to install on the remote machine. * `version (string)` - (Optional) The Chef Client version to install on the remote machine.
If not set, the latest available version will be installed. 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__.