Merge pull request #14681 from svanharmelen/f-review
Use `helpers.shema.Provisoner` in Chef provisioner V2
This commit is contained in:
commit
7894478c8c
|
@ -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.Provisioner,
|
||||
})
|
||||
}
|
||||
|
|
|
@ -14,10 +14,7 @@ const (
|
|||
installURL = "https://www.chef.io/chef/install.sh"
|
||||
)
|
||||
|
||||
func (p *Provisioner) linuxInstallChefClient(
|
||||
o terraform.UIOutput,
|
||||
comm communicator.Communicator) error {
|
||||
|
||||
func (p *provisioner) linuxInstallChefClient(o terraform.UIOutput, comm communicator.Communicator) error {
|
||||
// Build up the command prefix
|
||||
prefix := ""
|
||||
if p.HTTPProxy != "" {
|
||||
|
@ -26,7 +23,7 @@ func (p *Provisioner) linuxInstallChefClient(
|
|||
if p.HTTPSProxy != "" {
|
||||
prefix += fmt.Sprintf("https_proxy='%s' ", p.HTTPSProxy)
|
||||
}
|
||||
if p.NOProxy != nil {
|
||||
if len(p.NOProxy) > 0 {
|
||||
prefix += fmt.Sprintf("no_proxy='%s' ", strings.Join(p.NOProxy, ","))
|
||||
}
|
||||
|
||||
|
@ -46,9 +43,7 @@ func (p *Provisioner) linuxInstallChefClient(
|
|||
return p.runCommand(o, comm, fmt.Sprintf("%srm -f install.sh", prefix))
|
||||
}
|
||||
|
||||
func (p *Provisioner) linuxCreateConfigFiles(
|
||||
o terraform.UIOutput,
|
||||
comm communicator.Communicator) error {
|
||||
func (p *provisioner) linuxCreateConfigFiles(o terraform.UIOutput, comm communicator.Communicator) error {
|
||||
// Make sure the config directory exists
|
||||
if err := p.runCommand(o, comm, "mkdir -p "+linuxConfDir); err != nil {
|
||||
return err
|
||||
|
|
|
@ -6,22 +6,23 @@ import (
|
|||
"testing"
|
||||
|
||||
"github.com/hashicorp/terraform/communicator"
|
||||
"github.com/hashicorp/terraform/helper/schema"
|
||||
"github.com/hashicorp/terraform/terraform"
|
||||
)
|
||||
|
||||
func TestResourceProvider_linuxInstallChefClient(t *testing.T) {
|
||||
cases := map[string]struct {
|
||||
Config *terraform.ResourceConfig
|
||||
Config map[string]interface{}
|
||||
Commands map[string]bool
|
||||
}{
|
||||
"Sudo": {
|
||||
Config: testConfig(t, map[string]interface{}{
|
||||
Config: map[string]interface{}{
|
||||
"node_name": "nodename1",
|
||||
"run_list": []interface{}{"cookbook::recipe"},
|
||||
"server_url": "https://chef.local",
|
||||
"user_name": "bob",
|
||||
"user_key": "USER-KEY",
|
||||
}),
|
||||
},
|
||||
|
||||
Commands: map[string]bool{
|
||||
"sudo curl -LO https://www.chef.io/chef/install.sh": true,
|
||||
|
@ -31,7 +32,7 @@ func TestResourceProvider_linuxInstallChefClient(t *testing.T) {
|
|||
},
|
||||
|
||||
"NoSudo": {
|
||||
Config: testConfig(t, map[string]interface{}{
|
||||
Config: map[string]interface{}{
|
||||
"node_name": "nodename1",
|
||||
"prevent_sudo": true,
|
||||
"run_list": []interface{}{"cookbook::recipe"},
|
||||
|
@ -39,7 +40,7 @@ func TestResourceProvider_linuxInstallChefClient(t *testing.T) {
|
|||
"server_url": "https://chef.local",
|
||||
"user_name": "bob",
|
||||
"user_key": "USER-KEY",
|
||||
}),
|
||||
},
|
||||
|
||||
Commands: map[string]bool{
|
||||
"curl -LO https://www.chef.io/chef/install.sh": true,
|
||||
|
@ -49,7 +50,7 @@ func TestResourceProvider_linuxInstallChefClient(t *testing.T) {
|
|||
},
|
||||
|
||||
"HTTPProxy": {
|
||||
Config: testConfig(t, map[string]interface{}{
|
||||
Config: map[string]interface{}{
|
||||
"http_proxy": "http://proxy.local",
|
||||
"node_name": "nodename1",
|
||||
"prevent_sudo": true,
|
||||
|
@ -57,7 +58,7 @@ func TestResourceProvider_linuxInstallChefClient(t *testing.T) {
|
|||
"server_url": "https://chef.local",
|
||||
"user_name": "bob",
|
||||
"user_key": "USER-KEY",
|
||||
}),
|
||||
},
|
||||
|
||||
Commands: map[string]bool{
|
||||
"http_proxy='http://proxy.local' curl -LO https://www.chef.io/chef/install.sh": true,
|
||||
|
@ -67,7 +68,7 @@ func TestResourceProvider_linuxInstallChefClient(t *testing.T) {
|
|||
},
|
||||
|
||||
"HTTPSProxy": {
|
||||
Config: testConfig(t, map[string]interface{}{
|
||||
Config: map[string]interface{}{
|
||||
"https_proxy": "https://proxy.local",
|
||||
"node_name": "nodename1",
|
||||
"prevent_sudo": true,
|
||||
|
@ -75,7 +76,7 @@ func TestResourceProvider_linuxInstallChefClient(t *testing.T) {
|
|||
"server_url": "https://chef.local",
|
||||
"user_name": "bob",
|
||||
"user_key": "USER-KEY",
|
||||
}),
|
||||
},
|
||||
|
||||
Commands: map[string]bool{
|
||||
"https_proxy='https://proxy.local' curl -LO https://www.chef.io/chef/install.sh": true,
|
||||
|
@ -85,7 +86,7 @@ func TestResourceProvider_linuxInstallChefClient(t *testing.T) {
|
|||
},
|
||||
|
||||
"NoProxy": {
|
||||
Config: testConfig(t, map[string]interface{}{
|
||||
Config: map[string]interface{}{
|
||||
"http_proxy": "http://proxy.local",
|
||||
"no_proxy": []interface{}{"http://local.local", "http://local.org"},
|
||||
"node_name": "nodename1",
|
||||
|
@ -94,7 +95,7 @@ func TestResourceProvider_linuxInstallChefClient(t *testing.T) {
|
|||
"server_url": "https://chef.local",
|
||||
"user_name": "bob",
|
||||
"user_key": "USER-KEY",
|
||||
}),
|
||||
},
|
||||
|
||||
Commands: map[string]bool{
|
||||
"http_proxy='http://proxy.local' no_proxy='http://local.local,http://local.org' " +
|
||||
|
@ -107,7 +108,7 @@ func TestResourceProvider_linuxInstallChefClient(t *testing.T) {
|
|||
},
|
||||
|
||||
"Version": {
|
||||
Config: testConfig(t, map[string]interface{}{
|
||||
Config: map[string]interface{}{
|
||||
"node_name": "nodename1",
|
||||
"prevent_sudo": true,
|
||||
"run_list": []interface{}{"cookbook::recipe"},
|
||||
|
@ -115,7 +116,7 @@ func TestResourceProvider_linuxInstallChefClient(t *testing.T) {
|
|||
"user_name": "bob",
|
||||
"user_key": "USER-KEY",
|
||||
"version": "11.18.6",
|
||||
}),
|
||||
},
|
||||
|
||||
Commands: map[string]bool{
|
||||
"curl -LO https://www.chef.io/chef/install.sh": true,
|
||||
|
@ -125,14 +126,15 @@ 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(
|
||||
schema.TestResourceDataRaw(t, Provisioner().(*schema.Provisioner).Schema, tc.Config),
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("Error: %v", err)
|
||||
}
|
||||
|
@ -148,12 +150,12 @@ func TestResourceProvider_linuxInstallChefClient(t *testing.T) {
|
|||
|
||||
func TestResourceProvider_linuxCreateConfigFiles(t *testing.T) {
|
||||
cases := map[string]struct {
|
||||
Config *terraform.ResourceConfig
|
||||
Config map[string]interface{}
|
||||
Commands map[string]bool
|
||||
Uploads map[string]string
|
||||
}{
|
||||
"Sudo": {
|
||||
Config: testConfig(t, map[string]interface{}{
|
||||
Config: map[string]interface{}{
|
||||
"ohai_hints": []interface{}{"test-fixtures/ohaihint.json"},
|
||||
"node_name": "nodename1",
|
||||
"run_list": []interface{}{"cookbook::recipe"},
|
||||
|
@ -161,7 +163,7 @@ func TestResourceProvider_linuxCreateConfigFiles(t *testing.T) {
|
|||
"server_url": "https://chef.local",
|
||||
"user_name": "bob",
|
||||
"user_key": "USER-KEY",
|
||||
}),
|
||||
},
|
||||
|
||||
Commands: map[string]bool{
|
||||
"sudo mkdir -p " + linuxConfDir: true,
|
||||
|
@ -188,7 +190,7 @@ func TestResourceProvider_linuxCreateConfigFiles(t *testing.T) {
|
|||
},
|
||||
|
||||
"NoSudo": {
|
||||
Config: testConfig(t, map[string]interface{}{
|
||||
Config: map[string]interface{}{
|
||||
"node_name": "nodename1",
|
||||
"prevent_sudo": true,
|
||||
"run_list": []interface{}{"cookbook::recipe"},
|
||||
|
@ -196,7 +198,7 @@ func TestResourceProvider_linuxCreateConfigFiles(t *testing.T) {
|
|||
"server_url": "https://chef.local",
|
||||
"user_name": "bob",
|
||||
"user_key": "USER-KEY",
|
||||
}),
|
||||
},
|
||||
|
||||
Commands: map[string]bool{
|
||||
"mkdir -p " + linuxConfDir: true,
|
||||
|
@ -211,7 +213,7 @@ func TestResourceProvider_linuxCreateConfigFiles(t *testing.T) {
|
|||
},
|
||||
|
||||
"Proxy": {
|
||||
Config: testConfig(t, map[string]interface{}{
|
||||
Config: map[string]interface{}{
|
||||
"http_proxy": "http://proxy.local",
|
||||
"https_proxy": "https://proxy.local",
|
||||
"no_proxy": []interface{}{"http://local.local", "https://local.local"},
|
||||
|
@ -223,7 +225,7 @@ func TestResourceProvider_linuxCreateConfigFiles(t *testing.T) {
|
|||
"ssl_verify_mode": "verify_none",
|
||||
"user_name": "bob",
|
||||
"user_key": "USER-KEY",
|
||||
}),
|
||||
},
|
||||
|
||||
Commands: map[string]bool{
|
||||
"mkdir -p " + linuxConfDir: true,
|
||||
|
@ -238,7 +240,7 @@ func TestResourceProvider_linuxCreateConfigFiles(t *testing.T) {
|
|||
},
|
||||
|
||||
"Attributes JSON": {
|
||||
Config: testConfig(t, map[string]interface{}{
|
||||
Config: map[string]interface{}{
|
||||
"attributes_json": `{"key1":{"subkey1":{"subkey2a":["val1","val2","val3"],` +
|
||||
`"subkey2b":{"subkey3":"value3"}}},"key2":"value2"}`,
|
||||
"node_name": "nodename1",
|
||||
|
@ -248,7 +250,7 @@ func TestResourceProvider_linuxCreateConfigFiles(t *testing.T) {
|
|||
"server_url": "https://chef.local",
|
||||
"user_name": "bob",
|
||||
"user_key": "USER-KEY",
|
||||
}),
|
||||
},
|
||||
|
||||
Commands: map[string]bool{
|
||||
"mkdir -p " + linuxConfDir: true,
|
||||
|
@ -264,7 +266,6 @@ func TestResourceProvider_linuxCreateConfigFiles(t *testing.T) {
|
|||
},
|
||||
}
|
||||
|
||||
r := new(ResourceProvisioner)
|
||||
o := new(terraform.MockUIOutput)
|
||||
c := new(communicator.MockCommunicator)
|
||||
|
||||
|
@ -272,7 +273,9 @@ func TestResourceProvider_linuxCreateConfigFiles(t *testing.T) {
|
|||
c.Commands = tc.Commands
|
||||
c.Uploads = tc.Uploads
|
||||
|
||||
p, err := r.decodeConfig(tc.Config)
|
||||
p, err := decodeConfig(
|
||||
schema.TestResourceDataRaw(t, Provisioner().(*schema.Provisioner).Schema, tc.Config),
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("Error: %v", err)
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ package chef
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
@ -17,10 +18,10 @@ 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"
|
||||
"github.com/mitchellh/mapstructure"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -81,81 +82,196 @@ enable_reporting false
|
|||
{{ end }}
|
||||
`
|
||||
|
||||
// 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"`
|
||||
NamedRunList string `mapstructure:"named_run_list"`
|
||||
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"`
|
||||
type provisionFn func(terraform.UIOutput, communicator.Communicator) error
|
||||
|
||||
attributes map[string]interface{}
|
||||
vaults map[string][]string
|
||||
type provisioner struct {
|
||||
Attributes map[string]interface{}
|
||||
ClientOptions []string
|
||||
DisableReporting bool
|
||||
Environment string
|
||||
FetchChefCertificates bool
|
||||
LogToFile bool
|
||||
UsePolicyfile bool
|
||||
PolicyGroup string
|
||||
PolicyName string
|
||||
HTTPProxy string
|
||||
HTTPSProxy string
|
||||
NamedRunList 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
|
||||
Vaults map[string][]string
|
||||
Version string
|
||||
|
||||
cleanupUserKeyCmd string
|
||||
createConfigFiles func(terraform.UIOutput, communicator.Communicator) error
|
||||
installChefClient func(terraform.UIOutput, communicator.Communicator) error
|
||||
fetchChefCertificates func(terraform.UIOutput, communicator.Communicator) error
|
||||
generateClientKey func(terraform.UIOutput, communicator.Communicator) error
|
||||
configureVaults func(terraform.UIOutput, communicator.Communicator) error
|
||||
runChefClient func(terraform.UIOutput, communicator.Communicator) error
|
||||
createConfigFiles provisionFn
|
||||
installChefClient provisionFn
|
||||
fetchChefCertificates provisionFn
|
||||
generateClientKey provisionFn
|
||||
configureVaults provisionFn
|
||||
runChefClient provisionFn
|
||||
useSudo bool
|
||||
|
||||
// Deprecated Fields
|
||||
ValidationClientName string `mapstructure:"validation_client_name"`
|
||||
ValidationKey string `mapstructure:"validation_key"`
|
||||
}
|
||||
|
||||
// ResourceProvisioner represents a generic chef provisioner
|
||||
type ResourceProvisioner struct{}
|
||||
// Provisioner returns a Chef provisioner
|
||||
func Provisioner() terraform.ResourceProvisioner {
|
||||
return &schema.Provisioner{
|
||||
Schema: map[string]*schema.Schema{
|
||||
"node_name": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Required: true,
|
||||
},
|
||||
"server_url": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Required: true,
|
||||
},
|
||||
"user_name": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Required: true,
|
||||
},
|
||||
"user_key": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Required: true,
|
||||
},
|
||||
|
||||
func (r *ResourceProvisioner) Stop() error {
|
||||
// Noop for now. TODO in the future.
|
||||
return nil
|
||||
"attributes_json": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
},
|
||||
"client_options": &schema.Schema{
|
||||
Type: schema.TypeList,
|
||||
Elem: &schema.Schema{Type: schema.TypeString},
|
||||
Optional: true,
|
||||
},
|
||||
"disable_reporting": &schema.Schema{
|
||||
Type: schema.TypeBool,
|
||||
Optional: true,
|
||||
},
|
||||
"environment": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
Default: defaultEnv,
|
||||
},
|
||||
"fetch_chef_certificates": &schema.Schema{
|
||||
Type: schema.TypeBool,
|
||||
Optional: true,
|
||||
},
|
||||
"log_to_file": &schema.Schema{
|
||||
Type: schema.TypeBool,
|
||||
Optional: true,
|
||||
},
|
||||
"use_policyfile": &schema.Schema{
|
||||
Type: schema.TypeBool,
|
||||
Optional: true,
|
||||
},
|
||||
"policy_group": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
},
|
||||
"policy_name": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
},
|
||||
"http_proxy": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
},
|
||||
"https_proxy": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
},
|
||||
"no_proxy": &schema.Schema{
|
||||
Type: schema.TypeList,
|
||||
Elem: &schema.Schema{Type: schema.TypeString},
|
||||
Optional: true,
|
||||
},
|
||||
"named_run_list": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
},
|
||||
"ohai_hints": &schema.Schema{
|
||||
Type: schema.TypeList,
|
||||
Elem: &schema.Schema{Type: schema.TypeString},
|
||||
Optional: true,
|
||||
},
|
||||
"os_type": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
},
|
||||
"recreate_client": &schema.Schema{
|
||||
Type: schema.TypeBool,
|
||||
Optional: true,
|
||||
},
|
||||
"prevent_sudo": &schema.Schema{
|
||||
Type: schema.TypeBool,
|
||||
Optional: true,
|
||||
},
|
||||
"run_list": &schema.Schema{
|
||||
Type: schema.TypeList,
|
||||
Elem: &schema.Schema{Type: schema.TypeString},
|
||||
Optional: true,
|
||||
},
|
||||
"secret_key": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
},
|
||||
"skip_install": &schema.Schema{
|
||||
Type: schema.TypeBool,
|
||||
Optional: true,
|
||||
},
|
||||
"skip_register": &schema.Schema{
|
||||
Type: schema.TypeBool,
|
||||
Optional: true,
|
||||
},
|
||||
"ssl_verify_mode": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
},
|
||||
"vault_json": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
},
|
||||
"version": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
},
|
||||
},
|
||||
|
||||
ApplyFunc: applyFn,
|
||||
ValidateFunc: validateFn,
|
||||
}
|
||||
}
|
||||
|
||||
// Apply executes the file provisioner
|
||||
func (r *ResourceProvisioner) Apply(
|
||||
o terraform.UIOutput,
|
||||
s *terraform.InstanceState,
|
||||
c *terraform.ResourceConfig) error {
|
||||
// TODO: Support context cancelling (Provisioner Stop)
|
||||
func applyFn(ctx context.Context) error {
|
||||
o := ctx.Value(schema.ProvOutputKey).(terraform.UIOutput)
|
||||
d := ctx.Value(schema.ProvConfigDataKey).(*schema.ResourceData)
|
||||
|
||||
// Decode the raw config for this provisioner
|
||||
p, err := r.decodeConfig(c)
|
||||
p, err := decodeConfig(d)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if p.OSType == "" {
|
||||
switch s.Ephemeral.ConnInfo["type"] {
|
||||
switch t := d.State().Ephemeral.ConnInfo["type"]; t {
|
||||
case "ssh", "": // The default connection type is ssh, so if the type is empty assume ssh
|
||||
p.OSType = "linux"
|
||||
case "winrm":
|
||||
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 +285,7 @@ func (r *ResourceProvisioner) 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,15 +300,14 @@ func (r *ResourceProvisioner) Apply(
|
|||
}
|
||||
|
||||
// Get a new communicator
|
||||
comm, err := communicator.New(s)
|
||||
comm, err := communicator.New(d.State())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Wait and retry until we establish the connection
|
||||
err = retryFunc(comm.Timeout(), func() error {
|
||||
err := comm.Connect(o)
|
||||
return err
|
||||
return comm.Connect(o)
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -234,7 +349,7 @@ func (r *ResourceProvisioner) Apply(
|
|||
}
|
||||
}
|
||||
|
||||
if p.VaultJSON != "" {
|
||||
if p.Vaults != nil {
|
||||
o.Output("Configure Chef vaults...")
|
||||
if err := p.configureVaults(o, comm); err != nil {
|
||||
return err
|
||||
|
@ -253,152 +368,27 @@ func (r *ResourceProvisioner) Apply(
|
|||
return nil
|
||||
}
|
||||
|
||||
// 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 validateFn(d *schema.ResourceData) (ws []string, es []error) {
|
||||
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"))
|
||||
}
|
||||
if p.UsePolicyfile && p.PolicyGroup == "" {
|
||||
es = append(es, errors.New("Policyfile enabled but key not found: policy_group"))
|
||||
}
|
||||
if p.UserName == "" && p.ValidationClientName == "" {
|
||||
es = append(es, errors.New(
|
||||
"One of user_name or the deprecated validation_client_name must be provided"))
|
||||
}
|
||||
if p.UserKey == "" && p.ValidationKey == "" {
|
||||
es = append(es, errors.New(
|
||||
"One of user_key or the deprecated validation_key must be provided"))
|
||||
}
|
||||
if p.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"))
|
||||
}
|
||||
if p.VaultJSON != "" {
|
||||
es = append(es, errors.New(
|
||||
"Cannot configure chef vaults using the deprecated validation_key, please provide a user_key"))
|
||||
}
|
||||
}
|
||||
|
||||
return ws, es
|
||||
}
|
||||
|
||||
func (r *ResourceProvisioner) 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
|
||||
}
|
||||
|
||||
// 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 := c.Config["attributes_json"].(string); ok && !c.IsComputed("attributes_json") {
|
||||
var m map[string]interface{}
|
||||
if err := json.Unmarshal([]byte(attrs), &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 && !c.IsComputed("vault_json") {
|
||||
var m map[string]interface{}
|
||||
if err := json.Unmarshal([]byte(vaults), &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 *Provisioner) deployConfigFiles(
|
||||
o terraform.UIOutput,
|
||||
comm communicator.Communicator,
|
||||
confDir string) error {
|
||||
func (p *provisioner) deployConfigFiles(o terraform.UIOutput, comm communicator.Communicator, confDir string) error {
|
||||
// Copy the user key to the new instance
|
||||
pk := strings.NewReader(p.UserKey)
|
||||
if err := comm.Upload(path.Join(confDir, p.UserName+".pem"), pk); err != nil {
|
||||
|
@ -433,14 +423,14 @@ func (p *Provisioner) deployConfigFiles(
|
|||
}
|
||||
|
||||
// Copy the client config to the new instance
|
||||
if err := comm.Upload(path.Join(confDir, clienrb), &buf); err != nil {
|
||||
if err = comm.Upload(path.Join(confDir, clienrb), &buf); err != nil {
|
||||
return fmt.Errorf("Uploading %s failed: %v", clienrb, err)
|
||||
}
|
||||
|
||||
// Create a map with first boot settings
|
||||
fb := make(map[string]interface{})
|
||||
if p.attributes != nil {
|
||||
fb = p.attributes
|
||||
if p.Attributes != nil {
|
||||
fb = p.Attributes
|
||||
}
|
||||
|
||||
// Check if the run_list was also in the attributes and if so log a warning
|
||||
|
@ -469,10 +459,7 @@ func (p *Provisioner) deployConfigFiles(
|
|||
return nil
|
||||
}
|
||||
|
||||
func (p *Provisioner) deployOhaiHints(
|
||||
o terraform.UIOutput,
|
||||
comm communicator.Communicator,
|
||||
hintDir string) error {
|
||||
func (p *provisioner) deployOhaiHints(o terraform.UIOutput, comm communicator.Communicator, hintDir string) error {
|
||||
for _, hint := range p.OhaiHints {
|
||||
// Open the hint file
|
||||
f, err := os.Open(hint)
|
||||
|
@ -490,7 +477,7 @@ func (p *Provisioner) deployOhaiHints(
|
|||
return nil
|
||||
}
|
||||
|
||||
func (p *Provisioner) fetchChefCertificatesFunc(
|
||||
func (p *provisioner) fetchChefCertificatesFunc(
|
||||
knifeCmd string,
|
||||
confDir string) func(terraform.UIOutput, communicator.Communicator) error {
|
||||
return func(o terraform.UIOutput, comm communicator.Communicator) error {
|
||||
|
@ -501,10 +488,7 @@ func (p *Provisioner) fetchChefCertificatesFunc(
|
|||
}
|
||||
}
|
||||
|
||||
func (p *Provisioner) generateClientKeyFunc(
|
||||
knifeCmd string,
|
||||
confDir string,
|
||||
noOutput string) func(terraform.UIOutput, communicator.Communicator) error {
|
||||
func (p *provisioner) generateClientKeyFunc(knifeCmd string, confDir string, noOutput string) provisionFn {
|
||||
return func(o terraform.UIOutput, comm communicator.Communicator) error {
|
||||
options := fmt.Sprintf("-c %s -u %s --key %s",
|
||||
path.Join(confDir, clienrb),
|
||||
|
@ -562,10 +546,7 @@ func (p *Provisioner) generateClientKeyFunc(
|
|||
}
|
||||
}
|
||||
|
||||
func (p *Provisioner) configureVaultsFunc(
|
||||
gemCmd string,
|
||||
knifeCmd string,
|
||||
confDir string) func(terraform.UIOutput, communicator.Communicator) error {
|
||||
func (p *provisioner) configureVaultsFunc(gemCmd string, knifeCmd string, confDir string) provisionFn {
|
||||
return func(o terraform.UIOutput, comm communicator.Communicator) error {
|
||||
if err := p.runCommand(o, comm, fmt.Sprintf("%s install chef-vault", gemCmd)); err != nil {
|
||||
return err
|
||||
|
@ -577,7 +558,7 @@ func (p *Provisioner) configureVaultsFunc(
|
|||
path.Join(confDir, p.UserName+".pem"),
|
||||
)
|
||||
|
||||
for vault, items := range p.vaults {
|
||||
for vault, items := range p.Vaults {
|
||||
for _, item := range items {
|
||||
updateCmd := fmt.Sprintf("%s vault update %s %s -C %s -M client %s",
|
||||
knifeCmd,
|
||||
|
@ -596,9 +577,7 @@ func (p *Provisioner) configureVaultsFunc(
|
|||
}
|
||||
}
|
||||
|
||||
func (p *Provisioner) runChefClientFunc(
|
||||
chefCmd string,
|
||||
confDir string) func(terraform.UIOutput, communicator.Communicator) error {
|
||||
func (p *provisioner) runChefClientFunc(chefCmd string, confDir string) provisionFn {
|
||||
return func(o terraform.UIOutput, comm communicator.Communicator) error {
|
||||
fb := path.Join(confDir, firstBoot)
|
||||
var cmd string
|
||||
|
@ -634,7 +613,7 @@ func (p *Provisioner) runChefClientFunc(
|
|||
}
|
||||
|
||||
// Output implementation of terraform.UIOutput interface
|
||||
func (p *Provisioner) Output(output string) {
|
||||
func (p *provisioner) Output(output string) {
|
||||
logFile := path.Join(logfileDir, p.NodeName)
|
||||
f, err := os.OpenFile(logFile, os.O_APPEND|os.O_WRONLY, 0666)
|
||||
if err != nil {
|
||||
|
@ -660,10 +639,7 @@ func (p *Provisioner) Output(output string) {
|
|||
}
|
||||
|
||||
// runCommand is used to run already prepared commands
|
||||
func (p *Provisioner) runCommand(
|
||||
o terraform.UIOutput,
|
||||
comm communicator.Communicator,
|
||||
command string) error {
|
||||
func (p *provisioner) runCommand(o terraform.UIOutput, comm communicator.Communicator, command string) error {
|
||||
// Unless prevented, prefix the command with sudo
|
||||
if p.useSudo {
|
||||
command = "sudo " + command
|
||||
|
@ -702,7 +678,7 @@ func (p *Provisioner) runCommand(
|
|||
return err
|
||||
}
|
||||
|
||||
func (p *Provisioner) copyOutput(o terraform.UIOutput, r io.Reader, doneCh chan<- struct{}) {
|
||||
func (p *provisioner) copyOutput(o terraform.UIOutput, r io.Reader, doneCh chan<- struct{}) {
|
||||
defer close(doneCh)
|
||||
lr := linereader.New(r)
|
||||
for line := range lr.Ch {
|
||||
|
@ -727,3 +703,98 @@ func retryFunc(timeout time.Duration, f func() error) error {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
func decodeConfig(d *schema.ResourceData) (*provisioner, error) {
|
||||
p := &provisioner{
|
||||
ClientOptions: getStringList(d.Get("client_options")),
|
||||
DisableReporting: d.Get("disable_reporting").(bool),
|
||||
Environment: d.Get("environment").(string),
|
||||
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")),
|
||||
NamedRunList: d.Get("named_run_list").(string),
|
||||
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),
|
||||
Version: d.Get("version").(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 {
|
||||
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))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 = Provisioner()
|
||||
}
|
||||
|
||||
func TestProvisioner(t *testing.T) {
|
||||
if err := Provisioner().(*schema.Provisioner).InternalValidate(); err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestResourceProvider_Validate_good(t *testing.T) {
|
||||
|
@ -23,8 +30,8 @@ func TestResourceProvider_Validate_good(t *testing.T) {
|
|||
"user_name": "bob",
|
||||
"user_key": "USER-KEY",
|
||||
})
|
||||
r := new(ResourceProvisioner)
|
||||
warn, errs := r.Validate(c)
|
||||
|
||||
warn, errs := Provisioner().Validate(c)
|
||||
if len(warn) > 0 {
|
||||
t.Fatalf("Warnings: %v", warn)
|
||||
}
|
||||
|
@ -37,8 +44,8 @@ func TestResourceProvider_Validate_bad(t *testing.T) {
|
|||
c := testConfig(t, map[string]interface{}{
|
||||
"invalid": "nope",
|
||||
})
|
||||
p := new(ResourceProvisioner)
|
||||
warn, errs := p.Validate(c)
|
||||
|
||||
warn, errs := Provisioner().Validate(c)
|
||||
if len(warn) > 0 {
|
||||
t.Fatalf("Warnings: %v", warn)
|
||||
}
|
||||
|
@ -59,8 +66,8 @@ func TestResourceProvider_Validate_computedValues(t *testing.T) {
|
|||
"user_key": "USER-KEY",
|
||||
"attributes_json": config.UnknownVariableValue,
|
||||
})
|
||||
r := new(ResourceProvisioner)
|
||||
warn, errs := r.Validate(c)
|
||||
|
||||
warn, errs := Provisioner().Validate(c)
|
||||
if len(warn) > 0 {
|
||||
t.Fatalf("Warnings: %v", warn)
|
||||
}
|
||||
|
@ -69,30 +76,21 @@ func TestResourceProvider_Validate_computedValues(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func testConfig(t *testing.T, c map[string]interface{}) *terraform.ResourceConfig {
|
||||
r, err := config.NewRawConfig(c)
|
||||
if err != nil {
|
||||
t.Fatalf("bad: %s", err)
|
||||
}
|
||||
|
||||
return terraform.NewResourceConfig(r)
|
||||
}
|
||||
|
||||
func TestResourceProvider_runChefClient(t *testing.T) {
|
||||
cases := map[string]struct {
|
||||
Config *terraform.ResourceConfig
|
||||
Config map[string]interface{}
|
||||
ChefCmd string
|
||||
ConfDir string
|
||||
Commands map[string]bool
|
||||
}{
|
||||
"Sudo": {
|
||||
Config: testConfig(t, map[string]interface{}{
|
||||
Config: map[string]interface{}{
|
||||
"node_name": "nodename1",
|
||||
"run_list": []interface{}{"cookbook::recipe"},
|
||||
"server_url": "https://chef.local",
|
||||
"user_name": "bob",
|
||||
"user_key": "USER-KEY",
|
||||
}),
|
||||
},
|
||||
|
||||
ChefCmd: linuxChefCmd,
|
||||
|
||||
|
@ -106,14 +104,14 @@ func TestResourceProvider_runChefClient(t *testing.T) {
|
|||
},
|
||||
|
||||
"NoSudo": {
|
||||
Config: testConfig(t, map[string]interface{}{
|
||||
Config: map[string]interface{}{
|
||||
"node_name": "nodename1",
|
||||
"prevent_sudo": true,
|
||||
"run_list": []interface{}{"cookbook::recipe"},
|
||||
"server_url": "https://chef.local",
|
||||
"user_name": "bob",
|
||||
"user_key": "USER-KEY",
|
||||
}),
|
||||
},
|
||||
|
||||
ChefCmd: linuxChefCmd,
|
||||
|
||||
|
@ -127,7 +125,7 @@ func TestResourceProvider_runChefClient(t *testing.T) {
|
|||
},
|
||||
|
||||
"Environment": {
|
||||
Config: testConfig(t, map[string]interface{}{
|
||||
Config: map[string]interface{}{
|
||||
"environment": "production",
|
||||
"node_name": "nodename1",
|
||||
"prevent_sudo": true,
|
||||
|
@ -135,7 +133,7 @@ func TestResourceProvider_runChefClient(t *testing.T) {
|
|||
"server_url": "https://chef.local",
|
||||
"user_name": "bob",
|
||||
"user_key": "USER-KEY",
|
||||
}),
|
||||
},
|
||||
|
||||
ChefCmd: windowsChefCmd,
|
||||
|
||||
|
@ -149,14 +147,15 @@ 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(
|
||||
schema.TestResourceDataRaw(t, Provisioner().(*schema.Provisioner).Schema, tc.Config),
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("Error: %v", err)
|
||||
}
|
||||
|
@ -173,20 +172,20 @@ func TestResourceProvider_runChefClient(t *testing.T) {
|
|||
|
||||
func TestResourceProvider_fetchChefCertificates(t *testing.T) {
|
||||
cases := map[string]struct {
|
||||
Config *terraform.ResourceConfig
|
||||
Config map[string]interface{}
|
||||
KnifeCmd string
|
||||
ConfDir string
|
||||
Commands map[string]bool
|
||||
}{
|
||||
"Sudo": {
|
||||
Config: testConfig(t, map[string]interface{}{
|
||||
Config: map[string]interface{}{
|
||||
"fetch_chef_certificates": true,
|
||||
"node_name": "nodename1",
|
||||
"run_list": []interface{}{"cookbook::recipe"},
|
||||
"server_url": "https://chef.local",
|
||||
"user_name": "bob",
|
||||
"user_key": "USER-KEY",
|
||||
}),
|
||||
},
|
||||
|
||||
KnifeCmd: linuxKnifeCmd,
|
||||
|
||||
|
@ -200,7 +199,7 @@ func TestResourceProvider_fetchChefCertificates(t *testing.T) {
|
|||
},
|
||||
|
||||
"NoSudo": {
|
||||
Config: testConfig(t, map[string]interface{}{
|
||||
Config: map[string]interface{}{
|
||||
"fetch_chef_certificates": true,
|
||||
"node_name": "nodename1",
|
||||
"prevent_sudo": true,
|
||||
|
@ -208,7 +207,7 @@ func TestResourceProvider_fetchChefCertificates(t *testing.T) {
|
|||
"server_url": "https://chef.local",
|
||||
"user_name": "bob",
|
||||
"user_key": "USER-KEY",
|
||||
}),
|
||||
},
|
||||
|
||||
KnifeCmd: windowsKnifeCmd,
|
||||
|
||||
|
@ -222,14 +221,15 @@ 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(
|
||||
schema.TestResourceDataRaw(t, Provisioner().(*schema.Provisioner).Schema, tc.Config),
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("Error: %v", err)
|
||||
}
|
||||
|
@ -246,14 +246,14 @@ func TestResourceProvider_fetchChefCertificates(t *testing.T) {
|
|||
|
||||
func TestResourceProvider_configureVaults(t *testing.T) {
|
||||
cases := map[string]struct {
|
||||
Config *terraform.ResourceConfig
|
||||
Config map[string]interface{}
|
||||
GemCmd string
|
||||
KnifeCmd string
|
||||
ConfDir string
|
||||
Commands map[string]bool
|
||||
}{
|
||||
"Linux Vault string": {
|
||||
Config: testConfig(t, map[string]interface{}{
|
||||
Config: map[string]interface{}{
|
||||
"node_name": "nodename1",
|
||||
"prevent_sudo": true,
|
||||
"run_list": []interface{}{"cookbook::recipe"},
|
||||
|
@ -261,7 +261,7 @@ func TestResourceProvider_configureVaults(t *testing.T) {
|
|||
"user_name": "bob",
|
||||
"user_key": "USER-KEY",
|
||||
"vault_json": `{"vault1": "item1"}`,
|
||||
}),
|
||||
},
|
||||
|
||||
GemCmd: linuxGemCmd,
|
||||
KnifeCmd: linuxKnifeCmd,
|
||||
|
@ -275,7 +275,7 @@ func TestResourceProvider_configureVaults(t *testing.T) {
|
|||
},
|
||||
|
||||
"Linux Vault []string": {
|
||||
Config: testConfig(t, map[string]interface{}{
|
||||
Config: map[string]interface{}{
|
||||
"fetch_chef_certificates": true,
|
||||
"node_name": "nodename1",
|
||||
"prevent_sudo": true,
|
||||
|
@ -284,7 +284,7 @@ func TestResourceProvider_configureVaults(t *testing.T) {
|
|||
"user_name": "bob",
|
||||
"user_key": "USER-KEY",
|
||||
"vault_json": `{"vault1": ["item1", "item2"]}`,
|
||||
}),
|
||||
},
|
||||
|
||||
GemCmd: linuxGemCmd,
|
||||
KnifeCmd: linuxKnifeCmd,
|
||||
|
@ -300,7 +300,7 @@ func TestResourceProvider_configureVaults(t *testing.T) {
|
|||
},
|
||||
|
||||
"Windows Vault string": {
|
||||
Config: testConfig(t, map[string]interface{}{
|
||||
Config: map[string]interface{}{
|
||||
"node_name": "nodename1",
|
||||
"prevent_sudo": true,
|
||||
"run_list": []interface{}{"cookbook::recipe"},
|
||||
|
@ -308,7 +308,7 @@ func TestResourceProvider_configureVaults(t *testing.T) {
|
|||
"user_name": "bob",
|
||||
"user_key": "USER-KEY",
|
||||
"vault_json": `{"vault1": "item1"}`,
|
||||
}),
|
||||
},
|
||||
|
||||
GemCmd: windowsGemCmd,
|
||||
KnifeCmd: windowsKnifeCmd,
|
||||
|
@ -322,7 +322,7 @@ func TestResourceProvider_configureVaults(t *testing.T) {
|
|||
},
|
||||
|
||||
"Windows Vault []string": {
|
||||
Config: testConfig(t, map[string]interface{}{
|
||||
Config: map[string]interface{}{
|
||||
"fetch_chef_certificates": true,
|
||||
"node_name": "nodename1",
|
||||
"prevent_sudo": true,
|
||||
|
@ -331,7 +331,7 @@ func TestResourceProvider_configureVaults(t *testing.T) {
|
|||
"user_name": "bob",
|
||||
"user_key": "USER-KEY",
|
||||
"vault_json": `{"vault1": ["item1", "item2"]}`,
|
||||
}),
|
||||
},
|
||||
|
||||
GemCmd: windowsGemCmd,
|
||||
KnifeCmd: windowsKnifeCmd,
|
||||
|
@ -347,14 +347,15 @@ 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(
|
||||
schema.TestResourceDataRaw(t, Provisioner().(*schema.Provisioner).Schema, tc.Config),
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("Error: %v", err)
|
||||
}
|
||||
|
@ -368,3 +369,12 @@ func TestResourceProvider_configureVaults(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)
|
||||
}
|
||||
|
|
|
@ -46,9 +46,7 @@ Write-Host 'Installing Chef Client...'
|
|||
Start-Process -FilePath msiexec -ArgumentList /qn, /i, $dest -Wait
|
||||
`
|
||||
|
||||
func (p *Provisioner) windowsInstallChefClient(
|
||||
o terraform.UIOutput,
|
||||
comm communicator.Communicator) error {
|
||||
func (p *provisioner) windowsInstallChefClient(o terraform.UIOutput, comm communicator.Communicator) error {
|
||||
script := path.Join(path.Dir(comm.ScriptPath()), "ChefClient.ps1")
|
||||
content := fmt.Sprintf(installScript, p.Version, p.HTTPProxy, strings.Join(p.NOProxy, ","))
|
||||
|
||||
|
@ -62,9 +60,7 @@ func (p *Provisioner) windowsInstallChefClient(
|
|||
return p.runCommand(o, comm, installCmd)
|
||||
}
|
||||
|
||||
func (p *Provisioner) windowsCreateConfigFiles(
|
||||
o terraform.UIOutput,
|
||||
comm communicator.Communicator) error {
|
||||
func (p *provisioner) windowsCreateConfigFiles(o terraform.UIOutput, comm communicator.Communicator) error {
|
||||
// Make sure the config directory exists
|
||||
cmd := fmt.Sprintf("cmd /c if not exist %q mkdir %q", windowsConfDir, windowsConfDir)
|
||||
if err := p.runCommand(o, comm, cmd); err != nil {
|
||||
|
|
|
@ -6,23 +6,24 @@ import (
|
|||
"testing"
|
||||
|
||||
"github.com/hashicorp/terraform/communicator"
|
||||
"github.com/hashicorp/terraform/helper/schema"
|
||||
"github.com/hashicorp/terraform/terraform"
|
||||
)
|
||||
|
||||
func TestResourceProvider_windowsInstallChefClient(t *testing.T) {
|
||||
cases := map[string]struct {
|
||||
Config *terraform.ResourceConfig
|
||||
Config map[string]interface{}
|
||||
Commands map[string]bool
|
||||
UploadScripts map[string]string
|
||||
}{
|
||||
"Default": {
|
||||
Config: testConfig(t, map[string]interface{}{
|
||||
Config: map[string]interface{}{
|
||||
"node_name": "nodename1",
|
||||
"run_list": []interface{}{"cookbook::recipe"},
|
||||
"server_url": "https://chef.local",
|
||||
"user_name": "bob",
|
||||
"user_key": "USER-KEY",
|
||||
}),
|
||||
},
|
||||
|
||||
Commands: map[string]bool{
|
||||
"powershell -NoProfile -ExecutionPolicy Bypass -File ChefClient.ps1": true,
|
||||
|
@ -34,7 +35,7 @@ func TestResourceProvider_windowsInstallChefClient(t *testing.T) {
|
|||
},
|
||||
|
||||
"Proxy": {
|
||||
Config: testConfig(t, map[string]interface{}{
|
||||
Config: map[string]interface{}{
|
||||
"http_proxy": "http://proxy.local",
|
||||
"no_proxy": []interface{}{"http://local.local", "http://local.org"},
|
||||
"node_name": "nodename1",
|
||||
|
@ -42,7 +43,7 @@ func TestResourceProvider_windowsInstallChefClient(t *testing.T) {
|
|||
"server_url": "https://chef.local",
|
||||
"user_name": "bob",
|
||||
"user_key": "USER-KEY",
|
||||
}),
|
||||
},
|
||||
|
||||
Commands: map[string]bool{
|
||||
"powershell -NoProfile -ExecutionPolicy Bypass -File ChefClient.ps1": true,
|
||||
|
@ -54,14 +55,14 @@ func TestResourceProvider_windowsInstallChefClient(t *testing.T) {
|
|||
},
|
||||
|
||||
"Version": {
|
||||
Config: testConfig(t, map[string]interface{}{
|
||||
Config: map[string]interface{}{
|
||||
"node_name": "nodename1",
|
||||
"run_list": []interface{}{"cookbook::recipe"},
|
||||
"server_url": "https://chef.local",
|
||||
"user_name": "bob",
|
||||
"user_key": "USER-KEY",
|
||||
"version": "11.18.6",
|
||||
}),
|
||||
},
|
||||
|
||||
Commands: map[string]bool{
|
||||
"powershell -NoProfile -ExecutionPolicy Bypass -File ChefClient.ps1": true,
|
||||
|
@ -73,7 +74,6 @@ func TestResourceProvider_windowsInstallChefClient(t *testing.T) {
|
|||
},
|
||||
}
|
||||
|
||||
r := new(ResourceProvisioner)
|
||||
o := new(terraform.MockUIOutput)
|
||||
c := new(communicator.MockCommunicator)
|
||||
|
||||
|
@ -81,7 +81,9 @@ func TestResourceProvider_windowsInstallChefClient(t *testing.T) {
|
|||
c.Commands = tc.Commands
|
||||
c.UploadScripts = tc.UploadScripts
|
||||
|
||||
p, err := r.decodeConfig(tc.Config)
|
||||
p, err := decodeConfig(
|
||||
schema.TestResourceDataRaw(t, Provisioner().(*schema.Provisioner).Schema, tc.Config),
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("Error: %v", err)
|
||||
}
|
||||
|
@ -97,12 +99,12 @@ func TestResourceProvider_windowsInstallChefClient(t *testing.T) {
|
|||
|
||||
func TestResourceProvider_windowsCreateConfigFiles(t *testing.T) {
|
||||
cases := map[string]struct {
|
||||
Config *terraform.ResourceConfig
|
||||
Config map[string]interface{}
|
||||
Commands map[string]bool
|
||||
Uploads map[string]string
|
||||
}{
|
||||
"Default": {
|
||||
Config: testConfig(t, map[string]interface{}{
|
||||
Config: map[string]interface{}{
|
||||
"ohai_hints": []interface{}{"test-fixtures/ohaihint.json"},
|
||||
"node_name": "nodename1",
|
||||
"run_list": []interface{}{"cookbook::recipe"},
|
||||
|
@ -110,7 +112,7 @@ func TestResourceProvider_windowsCreateConfigFiles(t *testing.T) {
|
|||
"server_url": "https://chef.local",
|
||||
"user_name": "bob",
|
||||
"user_key": "USER-KEY",
|
||||
}),
|
||||
},
|
||||
|
||||
Commands: map[string]bool{
|
||||
fmt.Sprintf("cmd /c if not exist %q mkdir %q", windowsConfDir, windowsConfDir): true,
|
||||
|
@ -129,7 +131,7 @@ func TestResourceProvider_windowsCreateConfigFiles(t *testing.T) {
|
|||
},
|
||||
|
||||
"Proxy": {
|
||||
Config: testConfig(t, map[string]interface{}{
|
||||
Config: map[string]interface{}{
|
||||
"http_proxy": "http://proxy.local",
|
||||
"https_proxy": "https://proxy.local",
|
||||
"no_proxy": []interface{}{"http://local.local", "https://local.local"},
|
||||
|
@ -140,7 +142,7 @@ func TestResourceProvider_windowsCreateConfigFiles(t *testing.T) {
|
|||
"ssl_verify_mode": "verify_none",
|
||||
"user_name": "bob",
|
||||
"user_key": "USER-KEY",
|
||||
}),
|
||||
},
|
||||
|
||||
Commands: map[string]bool{
|
||||
fmt.Sprintf("cmd /c if not exist %q mkdir %q", windowsConfDir, windowsConfDir): true,
|
||||
|
@ -155,7 +157,7 @@ func TestResourceProvider_windowsCreateConfigFiles(t *testing.T) {
|
|||
},
|
||||
|
||||
"Attributes JSON": {
|
||||
Config: testConfig(t, map[string]interface{}{
|
||||
Config: map[string]interface{}{
|
||||
"attributes_json": `{"key1":{"subkey1":{"subkey2a":["val1","val2","val3"],` +
|
||||
`"subkey2b":{"subkey3":"value3"}}},"key2":"value2"}`,
|
||||
"node_name": "nodename1",
|
||||
|
@ -164,7 +166,7 @@ func TestResourceProvider_windowsCreateConfigFiles(t *testing.T) {
|
|||
"server_url": "https://chef.local",
|
||||
"user_name": "bob",
|
||||
"user_key": "USER-KEY",
|
||||
}),
|
||||
},
|
||||
|
||||
Commands: map[string]bool{
|
||||
fmt.Sprintf("cmd /c if not exist %q mkdir %q", windowsConfDir, windowsConfDir): true,
|
||||
|
@ -180,7 +182,6 @@ func TestResourceProvider_windowsCreateConfigFiles(t *testing.T) {
|
|||
},
|
||||
}
|
||||
|
||||
r := new(ResourceProvisioner)
|
||||
o := new(terraform.MockUIOutput)
|
||||
c := new(communicator.MockCommunicator)
|
||||
|
||||
|
@ -188,7 +189,9 @@ func TestResourceProvider_windowsCreateConfigFiles(t *testing.T) {
|
|||
c.Commands = tc.Commands
|
||||
c.Uploads = tc.Uploads
|
||||
|
||||
p, err := r.decodeConfig(tc.Config)
|
||||
p, err := decodeConfig(
|
||||
schema.TestResourceDataRaw(t, Provisioner().(*schema.Provisioner).Schema, tc.Config),
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("Error: %v", err)
|
||||
}
|
||||
|
|
|
@ -36,6 +36,7 @@ func Provisioner() terraform.ResourceProvisioner {
|
|||
},
|
||||
|
||||
ApplyFunc: applyFn,
|
||||
ValidateFunc: validateFn,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -77,6 +78,20 @@ func applyFn(ctx context.Context) error {
|
|||
}
|
||||
}
|
||||
|
||||
func validateFn(d *schema.ResourceData) (ws []string, es []error) {
|
||||
numSrc := 0
|
||||
if _, ok := d.GetOk("source"); ok == true {
|
||||
numSrc++
|
||||
}
|
||||
if _, ok := d.GetOk("content"); ok == true {
|
||||
numSrc++
|
||||
}
|
||||
if numSrc != 1 {
|
||||
es = append(es, fmt.Errorf("Must provide one of 'content' or 'source'"))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// getSrc returns the file to use as source
|
||||
func getSrc(data *schema.ResourceData) (string, bool, error) {
|
||||
src := data.Get("source").(string)
|
||||
|
|
|
@ -4,16 +4,27 @@ 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 = Provisioner()
|
||||
}
|
||||
|
||||
func TestProvisioner(t *testing.T) {
|
||||
if err := Provisioner().(*schema.Provisioner).InternalValidate(); err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestResourceProvider_Validate_good_source(t *testing.T) {
|
||||
c := testConfig(t, map[string]interface{}{
|
||||
"source": "/tmp/foo",
|
||||
"destination": "/tmp/bar",
|
||||
})
|
||||
p := Provisioner()
|
||||
warn, errs := p.Validate(c)
|
||||
|
||||
warn, errs := Provisioner().Validate(c)
|
||||
if len(warn) > 0 {
|
||||
t.Fatalf("Warnings: %v", warn)
|
||||
}
|
||||
|
@ -27,8 +38,8 @@ func TestResourceProvider_Validate_good_content(t *testing.T) {
|
|||
"content": "value to copy",
|
||||
"destination": "/tmp/bar",
|
||||
})
|
||||
p := Provisioner()
|
||||
warn, errs := p.Validate(c)
|
||||
|
||||
warn, errs := Provisioner().Validate(c)
|
||||
if len(warn) > 0 {
|
||||
t.Fatalf("Warnings: %v", warn)
|
||||
}
|
||||
|
@ -41,8 +52,22 @@ func TestResourceProvider_Validate_bad_not_destination(t *testing.T) {
|
|||
c := testConfig(t, map[string]interface{}{
|
||||
"source": "nope",
|
||||
})
|
||||
p := Provisioner()
|
||||
warn, errs := p.Validate(c)
|
||||
|
||||
warn, errs := Provisioner().Validate(c)
|
||||
if len(warn) > 0 {
|
||||
t.Fatalf("Warnings: %v", warn)
|
||||
}
|
||||
if len(errs) == 0 {
|
||||
t.Fatalf("Should have errors")
|
||||
}
|
||||
}
|
||||
|
||||
func TestResourceProvider_Validate_bad_no_source(t *testing.T) {
|
||||
c := testConfig(t, map[string]interface{}{
|
||||
"destination": "/tmp/bar",
|
||||
})
|
||||
|
||||
warn, errs := Provisioner().Validate(c)
|
||||
if len(warn) > 0 {
|
||||
t.Fatalf("Warnings: %v", warn)
|
||||
}
|
||||
|
@ -57,8 +82,8 @@ func TestResourceProvider_Validate_bad_to_many_src(t *testing.T) {
|
|||
"content": "value to copy",
|
||||
"destination": "/tmp/bar",
|
||||
})
|
||||
p := Provisioner()
|
||||
warn, errs := p.Validate(c)
|
||||
|
||||
warn, errs := Provisioner().Validate(c)
|
||||
if len(warn) > 0 {
|
||||
t.Fatalf("Warnings: %v", warn)
|
||||
}
|
||||
|
@ -67,12 +92,11 @@ func TestResourceProvider_Validate_bad_to_many_src(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func testConfig(
|
||||
t *testing.T,
|
||||
c map[string]interface{}) *terraform.ResourceConfig {
|
||||
func testConfig(t *testing.T, c map[string]interface{}) *terraform.ResourceConfig {
|
||||
r, err := config.NewRawConfig(c)
|
||||
if err != nil {
|
||||
t.Fatalf("bad: %s", err)
|
||||
}
|
||||
|
||||
return terraform.NewResourceConfig(r)
|
||||
}
|
||||
|
|
|
@ -8,9 +8,20 @@ import (
|
|||
"time"
|
||||
|
||||
"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 = Provisioner()
|
||||
}
|
||||
|
||||
func TestProvisioner(t *testing.T) {
|
||||
if err := Provisioner().(*schema.Provisioner).InternalValidate(); err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestResourceProvider_Apply(t *testing.T) {
|
||||
defer os.Remove("test_out")
|
||||
c := testConfig(t, map[string]interface{}{
|
||||
|
@ -19,6 +30,7 @@ func TestResourceProvider_Apply(t *testing.T) {
|
|||
|
||||
output := new(terraform.MockUIOutput)
|
||||
p := Provisioner()
|
||||
|
||||
if err := p.Apply(output, nil, c); err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
@ -73,8 +85,8 @@ func TestResourceProvider_Validate_good(t *testing.T) {
|
|||
c := testConfig(t, map[string]interface{}{
|
||||
"command": "echo foo",
|
||||
})
|
||||
p := Provisioner()
|
||||
warn, errs := p.Validate(c)
|
||||
|
||||
warn, errs := Provisioner().Validate(c)
|
||||
if len(warn) > 0 {
|
||||
t.Fatalf("Warnings: %v", warn)
|
||||
}
|
||||
|
@ -85,8 +97,8 @@ func TestResourceProvider_Validate_good(t *testing.T) {
|
|||
|
||||
func TestResourceProvider_Validate_missing(t *testing.T) {
|
||||
c := testConfig(t, map[string]interface{}{})
|
||||
p := Provisioner()
|
||||
warn, errs := p.Validate(c)
|
||||
|
||||
warn, errs := Provisioner().Validate(c)
|
||||
if len(warn) > 0 {
|
||||
t.Fatalf("Warnings: %v", warn)
|
||||
}
|
||||
|
@ -95,9 +107,7 @@ func TestResourceProvider_Validate_missing(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func testConfig(
|
||||
t *testing.T,
|
||||
c map[string]interface{}) *terraform.ResourceConfig {
|
||||
func testConfig(t *testing.T, c map[string]interface{}) *terraform.ResourceConfig {
|
||||
r, err := config.NewRawConfig(c)
|
||||
if err != nil {
|
||||
t.Fatalf("bad: %s", err)
|
||||
|
|
|
@ -16,12 +16,22 @@ import (
|
|||
"github.com/hashicorp/terraform/terraform"
|
||||
)
|
||||
|
||||
func TestResourceProvisioner_impl(t *testing.T) {
|
||||
var _ terraform.ResourceProvisioner = Provisioner()
|
||||
}
|
||||
|
||||
func TestProvisioner(t *testing.T) {
|
||||
if err := Provisioner().(*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 := Provisioner()
|
||||
warn, errs := p.Validate(c)
|
||||
|
||||
warn, errs := Provisioner().Validate(c)
|
||||
if len(warn) > 0 {
|
||||
t.Fatalf("Warnings: %v", warn)
|
||||
}
|
||||
|
@ -34,8 +44,8 @@ func TestResourceProvider_Validate_bad(t *testing.T) {
|
|||
c := testConfig(t, map[string]interface{}{
|
||||
"invalid": "nope",
|
||||
})
|
||||
p := Provisioner()
|
||||
warn, errs := p.Validate(c)
|
||||
|
||||
warn, errs := Provisioner().Validate(c)
|
||||
if len(warn) > 0 {
|
||||
t.Fatalf("Warnings: %v", warn)
|
||||
}
|
||||
|
@ -50,7 +60,6 @@ exit 0
|
|||
`
|
||||
|
||||
func TestResourceProvider_generateScript(t *testing.T) {
|
||||
p := Provisioner().(*schema.Provisioner)
|
||||
conf := map[string]interface{}{
|
||||
"inline": []interface{}{
|
||||
"cd /tmp",
|
||||
|
@ -58,8 +67,10 @@ func TestResourceProvider_generateScript(t *testing.T) {
|
|||
"exit 0",
|
||||
},
|
||||
}
|
||||
out, err := generateScripts(schema.TestResourceDataRaw(
|
||||
t, p.Schema, conf))
|
||||
|
||||
out, err := generateScripts(
|
||||
schema.TestResourceDataRaw(t, Provisioner().(*schema.Provisioner).Schema, conf),
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
@ -91,7 +102,6 @@ func TestResourceProvider_generateScriptEmptyInline(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestResourceProvider_CollectScripts_inline(t *testing.T) {
|
||||
p := Provisioner().(*schema.Provisioner)
|
||||
conf := map[string]interface{}{
|
||||
"inline": []interface{}{
|
||||
"cd /tmp",
|
||||
|
@ -100,8 +110,9 @@ func TestResourceProvider_CollectScripts_inline(t *testing.T) {
|
|||
},
|
||||
}
|
||||
|
||||
scripts, err := collectScripts(schema.TestResourceDataRaw(
|
||||
t, p.Schema, conf))
|
||||
scripts, err := collectScripts(
|
||||
schema.TestResourceDataRaw(t, Provisioner().(*schema.Provisioner).Schema, conf),
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
@ -122,13 +133,13 @@ func TestResourceProvider_CollectScripts_inline(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestResourceProvider_CollectScripts_script(t *testing.T) {
|
||||
p := Provisioner().(*schema.Provisioner)
|
||||
conf := map[string]interface{}{
|
||||
"script": "test-fixtures/script1.sh",
|
||||
}
|
||||
|
||||
scripts, err := collectScripts(schema.TestResourceDataRaw(
|
||||
t, p.Schema, conf))
|
||||
scripts, err := collectScripts(
|
||||
schema.TestResourceDataRaw(t, Provisioner().(*schema.Provisioner).Schema, conf),
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
@ -149,7 +160,6 @@ func TestResourceProvider_CollectScripts_script(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestResourceProvider_CollectScripts_scripts(t *testing.T) {
|
||||
p := Provisioner().(*schema.Provisioner)
|
||||
conf := map[string]interface{}{
|
||||
"scripts": []interface{}{
|
||||
"test-fixtures/script1.sh",
|
||||
|
@ -158,8 +168,9 @@ func TestResourceProvider_CollectScripts_scripts(t *testing.T) {
|
|||
},
|
||||
}
|
||||
|
||||
scripts, err := collectScripts(schema.TestResourceDataRaw(
|
||||
t, p.Schema, conf))
|
||||
scripts, err := collectScripts(
|
||||
schema.TestResourceDataRaw(t, Provisioner().(*schema.Provisioner).Schema, conf),
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
@ -224,9 +235,7 @@ func TestRetryFunc(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func testConfig(
|
||||
t *testing.T,
|
||||
c map[string]interface{}) *terraform.ResourceConfig {
|
||||
func testConfig(t *testing.T, c map[string]interface{}) *terraform.ResourceConfig {
|
||||
r, err := config.NewRawConfig(c)
|
||||
if err != nil {
|
||||
t.Fatalf("bad: %s", err)
|
||||
|
|
|
@ -75,6 +75,7 @@ import (
|
|||
vaultprovider "github.com/hashicorp/terraform/builtin/providers/vault"
|
||||
vcdprovider "github.com/hashicorp/terraform/builtin/providers/vcd"
|
||||
vsphereprovider "github.com/hashicorp/terraform/builtin/providers/vsphere"
|
||||
chefprovisioner "github.com/hashicorp/terraform/builtin/provisioners/chef"
|
||||
fileprovisioner "github.com/hashicorp/terraform/builtin/provisioners/file"
|
||||
localexecprovisioner "github.com/hashicorp/terraform/builtin/provisioners/local-exec"
|
||||
remoteexecprovisioner "github.com/hashicorp/terraform/builtin/provisioners/remote-exec"
|
||||
|
@ -84,9 +85,6 @@ import (
|
|||
|
||||
//New Provider Builds
|
||||
opcprovider "github.com/hashicorp/terraform-provider-opc/opc"
|
||||
|
||||
// Legacy, will remove once it conforms with new structure
|
||||
chefprovisioner "github.com/hashicorp/terraform/builtin/provisioners/chef"
|
||||
)
|
||||
|
||||
var InternalProviders = map[string]plugin.ProviderFunc{
|
||||
|
@ -162,16 +160,13 @@ var InternalProviders = map[string]plugin.ProviderFunc{
|
|||
}
|
||||
|
||||
var InternalProvisioners = map[string]plugin.ProvisionerFunc{
|
||||
"chef": chefprovisioner.Provisioner,
|
||||
"file": fileprovisioner.Provisioner,
|
||||
"local-exec": localexecprovisioner.Provisioner,
|
||||
"remote-exec": remoteexecprovisioner.Provisioner,
|
||||
}
|
||||
|
||||
func init() {
|
||||
// Legacy provisioners that don't match our heuristics for auto-finding
|
||||
// built-in provisioners.
|
||||
InternalProvisioners["chef"] = func() terraform.ResourceProvisioner { return new(chefprovisioner.ResourceProvisioner) }
|
||||
|
||||
// New Provider Layouts
|
||||
InternalProviders["opc"] = func() terraform.ResourceProvider { return opcprovider.Provider() }
|
||||
}
|
||||
|
|
|
@ -41,6 +41,10 @@ type Provisioner struct {
|
|||
// information.
|
||||
ApplyFunc func(ctx context.Context) error
|
||||
|
||||
// ValidateFunc is a function for extended validation. This is optional
|
||||
// and should be used when individual field validation is not enough.
|
||||
ValidateFunc func(*ResourceData) ([]string, []error)
|
||||
|
||||
stopCtx context.Context
|
||||
stopCtxCancel context.CancelFunc
|
||||
stopOnce sync.Once
|
||||
|
@ -117,8 +121,30 @@ func (p *Provisioner) Stop() error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (p *Provisioner) Validate(c *terraform.ResourceConfig) ([]string, []error) {
|
||||
return schemaMap(p.Schema).Validate(c)
|
||||
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 {
|
||||
data := &ResourceData{
|
||||
schema: p.Schema,
|
||||
config: config,
|
||||
}
|
||||
w2, e2 := p.ValidateFunc(data)
|
||||
w = append(w, w2...)
|
||||
e = append(e, e2...)
|
||||
}
|
||||
return w, e
|
||||
}
|
||||
|
||||
// Apply implementation of terraform.ResourceProvisioner interface.
|
||||
|
|
|
@ -3,6 +3,7 @@ package schema
|
|||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
|
@ -14,13 +15,35 @@ func TestProvisioner_impl(t *testing.T) {
|
|||
var _ terraform.ResourceProvisioner = new(Provisioner)
|
||||
}
|
||||
|
||||
func noopApply(ctx context.Context) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func TestProvisionerValidate(t *testing.T) {
|
||||
cases := []struct {
|
||||
Name string
|
||||
P *Provisioner
|
||||
Config map[string]interface{}
|
||||
Err bool
|
||||
Warns []string
|
||||
}{
|
||||
{
|
||||
Name: "No ApplyFunc",
|
||||
P: &Provisioner{},
|
||||
Config: nil,
|
||||
Err: true,
|
||||
},
|
||||
{
|
||||
Name: "Incorrect schema",
|
||||
P: &Provisioner{
|
||||
Schema: map[string]*Schema{
|
||||
"foo": {},
|
||||
},
|
||||
ApplyFunc: noopApply,
|
||||
},
|
||||
Config: nil,
|
||||
Err: true,
|
||||
},
|
||||
{
|
||||
"Basic required field",
|
||||
&Provisioner{
|
||||
|
@ -30,9 +53,11 @@ func TestProvisionerValidate(t *testing.T) {
|
|||
Type: TypeString,
|
||||
},
|
||||
},
|
||||
ApplyFunc: noopApply,
|
||||
},
|
||||
nil,
|
||||
true,
|
||||
nil,
|
||||
},
|
||||
|
||||
{
|
||||
|
@ -44,11 +69,57 @@ func TestProvisionerValidate(t *testing.T) {
|
|||
Type: TypeString,
|
||||
},
|
||||
},
|
||||
ApplyFunc: noopApply,
|
||||
},
|
||||
map[string]interface{}{
|
||||
"foo": "bar",
|
||||
},
|
||||
false,
|
||||
nil,
|
||||
},
|
||||
{
|
||||
Name: "Warning from property validation",
|
||||
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
|
||||
},
|
||||
},
|
||||
},
|
||||
ApplyFunc: noopApply,
|
||||
},
|
||||
Config: map[string]interface{}{
|
||||
"foo": "",
|
||||
},
|
||||
Err: false,
|
||||
Warns: []string{"Simple warning from property validation"},
|
||||
},
|
||||
{
|
||||
Name: "No schema",
|
||||
P: &Provisioner{
|
||||
Schema: nil,
|
||||
ApplyFunc: noopApply,
|
||||
},
|
||||
Config: nil,
|
||||
Err: false,
|
||||
},
|
||||
{
|
||||
Name: "Warning from provisioner ValidateFunc",
|
||||
P: &Provisioner{
|
||||
Schema: nil,
|
||||
ApplyFunc: noopApply,
|
||||
ValidateFunc: func(*ResourceData) (ws []string, errors []error) {
|
||||
ws = append(ws, "Simple warning from provisioner ValidateFunc")
|
||||
return
|
||||
},
|
||||
},
|
||||
Config: nil,
|
||||
Err: false,
|
||||
Warns: []string{"Simple warning from provisioner ValidateFunc"},
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -59,9 +130,12 @@ func TestProvisionerValidate(t *testing.T) {
|
|||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
|
||||
_, es := tc.P.Validate(terraform.NewResourceConfig(c))
|
||||
ws, es := tc.P.Validate(terraform.NewResourceConfig(c))
|
||||
if len(es) > 0 != tc.Err {
|
||||
t.Fatalf("%d: %#v", i, es)
|
||||
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)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
@ -82,12 +82,11 @@ 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) },
|
||||
// "chef": chefprovisioner.Provisioner,
|
||||
// "file": fileprovisioner.Provisioner,
|
||||
// "local-exec": localexecprovisioner.Provisioner,
|
||||
// "remote-exec": remoteexecprovisioner.Provisioner,
|
||||
//
|
||||
// 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 {
|
||||
|
@ -273,9 +272,6 @@ IMPORTS
|
|||
|
||||
//New Provider Builds
|
||||
opcprovider "github.com/hashicorp/terraform-provider-opc/opc"
|
||||
|
||||
// Legacy, will remove once it conforms with new structure
|
||||
chefprovisioner "github.com/hashicorp/terraform/builtin/provisioners/chef"
|
||||
)
|
||||
|
||||
var InternalProviders = map[string]plugin.ProviderFunc{
|
||||
|
@ -287,12 +283,7 @@ PROVISIONERS
|
|||
}
|
||||
|
||||
func init() {
|
||||
// Legacy provisioners that don't match our heuristics for auto-finding
|
||||
// built-in provisioners.
|
||||
InternalProvisioners["chef"] = func() terraform.ResourceProvisioner { return new(chefprovisioner.ResourceProvisioner) }
|
||||
|
||||
// New Provider Layouts
|
||||
InternalProviders["opc"] = func() terraform.ResourceProvider { return opcprovider.Provider() }
|
||||
}
|
||||
|
||||
`
|
||||
|
|
|
@ -157,9 +157,3 @@ The following arguments are supported:
|
|||
|
||||
* `version (string)` - (Optional) The Chef Client version to install on the remote machine.
|
||||
If not set, the latest available version will be installed.
|
||||
|
||||
These options are supported for backwards compatibility and may be removed in a
|
||||
future version:
|
||||
|
||||
* `validation_client_name (string)` - __Deprecated: please use `user_name` instead__.
|
||||
* `validation_key (string)` - __Deprecated: please use `user_key` instead__.
|
||||
|
|
Loading…
Reference in New Issue