Various built-in provisioners improvements:
1. Migrate `chef` provisioner to `schema.Provisioner`: * `chef.Provisioner` structure was renamed to `ProvisionerS`and now it's decoded from `schema.ResourceData` instead of `terraform.ResourceConfig` using simple copy-paste-based solution; * Added simple schema without any validation yet. 2. Support `ValidateFunc` validate function : implemented in `file` and `chef` provisioners.
This commit is contained in:
parent
622bc1aec2
commit
f5449a62e0
|
@ -3,13 +3,10 @@ package main
|
||||||
import (
|
import (
|
||||||
"github.com/hashicorp/terraform/builtin/provisioners/chef"
|
"github.com/hashicorp/terraform/builtin/provisioners/chef"
|
||||||
"github.com/hashicorp/terraform/plugin"
|
"github.com/hashicorp/terraform/plugin"
|
||||||
"github.com/hashicorp/terraform/terraform"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
plugin.Serve(&plugin.ServeOpts{
|
plugin.Serve(&plugin.ServeOpts{
|
||||||
ProvisionerFunc: func() terraform.ResourceProvisioner {
|
ProvisionerFunc: chef.Provisioner,
|
||||||
return new(chef.ResourceProvisioner)
|
|
||||||
},
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,7 +14,7 @@ const (
|
||||||
installURL = "https://www.chef.io/chef/install.sh"
|
installURL = "https://www.chef.io/chef/install.sh"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (p *Provisioner) linuxInstallChefClient(
|
func (p *ProvisionerS) linuxInstallChefClient(
|
||||||
o terraform.UIOutput,
|
o terraform.UIOutput,
|
||||||
comm communicator.Communicator) error {
|
comm communicator.Communicator) error {
|
||||||
|
|
||||||
|
@ -26,7 +26,7 @@ func (p *Provisioner) 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 {
|
if p.NOProxy != nil && 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,7 +46,7 @@ func (p *Provisioner) 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 *Provisioner) linuxCreateConfigFiles(
|
func (p *ProvisionerS) linuxCreateConfigFiles(
|
||||||
o terraform.UIOutput,
|
o terraform.UIOutput,
|
||||||
comm communicator.Communicator) error {
|
comm communicator.Communicator) error {
|
||||||
// Make sure the config directory exists
|
// Make sure the config directory exists
|
||||||
|
|
|
@ -125,14 +125,13 @@ func TestResourceProvider_linuxInstallChefClient(t *testing.T) {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
r := new(ResourceProvisioner)
|
|
||||||
o := new(terraform.MockUIOutput)
|
o := new(terraform.MockUIOutput)
|
||||||
c := new(communicator.MockCommunicator)
|
c := new(communicator.MockCommunicator)
|
||||||
|
|
||||||
for k, tc := range cases {
|
for k, tc := range cases {
|
||||||
c.Commands = tc.Commands
|
c.Commands = tc.Commands
|
||||||
|
|
||||||
p, err := r.decodeConfig(tc.Config)
|
p, err := decodeConfig(getTestResourceData(tc.Config))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Error: %v", err)
|
t.Fatalf("Error: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -264,7 +263,6 @@ func TestResourceProvider_linuxCreateConfigFiles(t *testing.T) {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
r := new(ResourceProvisioner)
|
|
||||||
o := new(terraform.MockUIOutput)
|
o := new(terraform.MockUIOutput)
|
||||||
c := new(communicator.MockCommunicator)
|
c := new(communicator.MockCommunicator)
|
||||||
|
|
||||||
|
@ -272,7 +270,7 @@ func TestResourceProvider_linuxCreateConfigFiles(t *testing.T) {
|
||||||
c.Commands = tc.Commands
|
c.Commands = tc.Commands
|
||||||
c.Uploads = tc.Uploads
|
c.Uploads = tc.Uploads
|
||||||
|
|
||||||
p, err := r.decodeConfig(tc.Config)
|
p, err := decodeConfig(getTestResourceData(tc.Config))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Error: %v", err)
|
t.Fatalf("Error: %v", err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,12 +15,13 @@ 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/terraform"
|
"github.com/hashicorp/terraform/terraform"
|
||||||
"github.com/mitchellh/go-homedir"
|
"github.com/mitchellh/go-homedir"
|
||||||
"github.com/mitchellh/go-linereader"
|
"github.com/mitchellh/go-linereader"
|
||||||
"github.com/mitchellh/mapstructure"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -81,36 +82,36 @@ enable_reporting false
|
||||||
{{ end }}
|
{{ end }}
|
||||||
`
|
`
|
||||||
|
|
||||||
// Provisioner represents a Chef provisioner
|
// ProvisionerS represents a Chef provisioner
|
||||||
type Provisioner struct {
|
type ProvisionerS struct {
|
||||||
AttributesJSON string `mapstructure:"attributes_json"`
|
AttributesJSON string
|
||||||
ClientOptions []string `mapstructure:"client_options"`
|
ClientOptions []string
|
||||||
DisableReporting bool `mapstructure:"disable_reporting"`
|
DisableReporting bool
|
||||||
Environment string `mapstructure:"environment"`
|
Environment string
|
||||||
FetchChefCertificates bool `mapstructure:"fetch_chef_certificates"`
|
FetchChefCertificates bool
|
||||||
LogToFile bool `mapstructure:"log_to_file"`
|
LogToFile bool
|
||||||
UsePolicyfile bool `mapstructure:"use_policyfile"`
|
UsePolicyfile bool
|
||||||
PolicyGroup string `mapstructure:"policy_group"`
|
PolicyGroup string
|
||||||
PolicyName string `mapstructure:"policy_name"`
|
PolicyName string
|
||||||
HTTPProxy string `mapstructure:"http_proxy"`
|
HTTPProxy string
|
||||||
HTTPSProxy string `mapstructure:"https_proxy"`
|
HTTPSProxy string
|
||||||
NamedRunList string `mapstructure:"named_run_list"`
|
NamedRunList string
|
||||||
NOProxy []string `mapstructure:"no_proxy"`
|
NOProxy []string
|
||||||
NodeName string `mapstructure:"node_name"`
|
NodeName string
|
||||||
OhaiHints []string `mapstructure:"ohai_hints"`
|
OhaiHints []string
|
||||||
OSType string `mapstructure:"os_type"`
|
OSType string
|
||||||
RecreateClient bool `mapstructure:"recreate_client"`
|
RecreateClient bool
|
||||||
PreventSudo bool `mapstructure:"prevent_sudo"`
|
PreventSudo bool
|
||||||
RunList []string `mapstructure:"run_list"`
|
RunList []string
|
||||||
SecretKey string `mapstructure:"secret_key"`
|
SecretKey string
|
||||||
ServerURL string `mapstructure:"server_url"`
|
ServerURL string
|
||||||
SkipInstall bool `mapstructure:"skip_install"`
|
SkipInstall bool
|
||||||
SkipRegister bool `mapstructure:"skip_register"`
|
SkipRegister bool
|
||||||
SSLVerifyMode string `mapstructure:"ssl_verify_mode"`
|
SSLVerifyMode string
|
||||||
UserName string `mapstructure:"user_name"`
|
UserName string
|
||||||
UserKey string `mapstructure:"user_key"`
|
UserKey string
|
||||||
VaultJSON string `mapstructure:"vault_json"`
|
VaultJSON string
|
||||||
Version string `mapstructure:"version"`
|
Version string
|
||||||
|
|
||||||
attributes map[string]interface{}
|
attributes map[string]interface{}
|
||||||
vaults map[string][]string
|
vaults map[string][]string
|
||||||
|
@ -125,37 +126,179 @@ type Provisioner struct {
|
||||||
useSudo bool
|
useSudo bool
|
||||||
|
|
||||||
// Deprecated Fields
|
// Deprecated Fields
|
||||||
ValidationClientName string `mapstructure:"validation_client_name"`
|
ValidationClientName string
|
||||||
ValidationKey string `mapstructure:"validation_key"`
|
ValidationKey string
|
||||||
}
|
}
|
||||||
|
|
||||||
// ResourceProvisioner represents a generic chef provisioner
|
func Provisioner() terraform.ResourceProvisioner {
|
||||||
type ResourceProvisioner struct{}
|
return &schema.Provisioner{
|
||||||
|
Schema: map[string]*schema.Schema{
|
||||||
|
"attributes_json": {
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Optional: true,
|
||||||
|
},
|
||||||
|
"client_options": {
|
||||||
|
Type: schema.TypeList,
|
||||||
|
Elem: schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
},
|
||||||
|
Optional: true,
|
||||||
|
},
|
||||||
|
"disable_reporting": {
|
||||||
|
Type: schema.TypeBool,
|
||||||
|
Optional: true,
|
||||||
|
},
|
||||||
|
"environment": {
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Optional: true,
|
||||||
|
},
|
||||||
|
"fetch_chef_certificates": {
|
||||||
|
Type: schema.TypeBool,
|
||||||
|
Optional: true,
|
||||||
|
},
|
||||||
|
"log_to_file": {
|
||||||
|
Type: schema.TypeBool,
|
||||||
|
Optional: true,
|
||||||
|
},
|
||||||
|
"use_policyfile": {
|
||||||
|
Type: schema.TypeBool,
|
||||||
|
Optional: true,
|
||||||
|
},
|
||||||
|
"policy_group": {
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Optional: true,
|
||||||
|
},
|
||||||
|
"policy_name": {
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Optional: true,
|
||||||
|
},
|
||||||
|
"http_proxy": {
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Optional: true,
|
||||||
|
},
|
||||||
|
"https_proxy": {
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Optional: true,
|
||||||
|
},
|
||||||
|
"no_proxy": {
|
||||||
|
Type: schema.TypeList,
|
||||||
|
Elem: schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Optional: true,
|
||||||
|
},
|
||||||
|
Optional: true,
|
||||||
|
},
|
||||||
|
"named_run_list": {
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Optional: true,
|
||||||
|
},
|
||||||
|
"node_name": {
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Required: true,
|
||||||
|
},
|
||||||
|
"ohai_hints": {
|
||||||
|
Type: schema.TypeList,
|
||||||
|
Elem: schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Optional: true,
|
||||||
|
},
|
||||||
|
Optional: true,
|
||||||
|
},
|
||||||
|
"os_type": {
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Optional: true,
|
||||||
|
},
|
||||||
|
"recreate_client": {
|
||||||
|
Type: schema.TypeBool,
|
||||||
|
Optional: true,
|
||||||
|
},
|
||||||
|
"prevent_sudo": {
|
||||||
|
Type: schema.TypeBool,
|
||||||
|
Optional: true,
|
||||||
|
},
|
||||||
|
"run_list": {
|
||||||
|
Type: schema.TypeList,
|
||||||
|
Elem: schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Optional: true,
|
||||||
|
},
|
||||||
|
Optional: true,
|
||||||
|
},
|
||||||
|
"secret_key": {
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Optional: true,
|
||||||
|
},
|
||||||
|
"server_url": {
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Required: true,
|
||||||
|
},
|
||||||
|
"skip_install": {
|
||||||
|
Type: schema.TypeBool,
|
||||||
|
Optional: true,
|
||||||
|
},
|
||||||
|
"skip_register": {
|
||||||
|
Type: schema.TypeBool,
|
||||||
|
Optional: true,
|
||||||
|
},
|
||||||
|
"ssl_verify_mode": {
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Optional: true,
|
||||||
|
},
|
||||||
|
"user_name": {
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Optional: true,
|
||||||
|
},
|
||||||
|
"user_key": {
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Optional: true,
|
||||||
|
},
|
||||||
|
"vault_json": {
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Optional: true,
|
||||||
|
},
|
||||||
|
"version": {
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Optional: true,
|
||||||
|
},
|
||||||
|
|
||||||
func (r *ResourceProvisioner) Stop() error {
|
// Deprecated
|
||||||
// Noop for now. TODO in the future.
|
"validation_client_name": {
|
||||||
return nil
|
Type: schema.TypeString,
|
||||||
|
Deprecated: "Please use user_name instead",
|
||||||
|
Optional: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
"validation_key": {
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Deprecated: "Please use user_key instead",
|
||||||
|
Optional: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ApplyFunc: Apply,
|
||||||
|
ValidateFunc: Validate,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Support context cancelling (Provisioner Stop)
|
||||||
// Apply executes the file provisioner
|
// Apply executes the file provisioner
|
||||||
func (r *ResourceProvisioner) Apply(
|
func Apply(ctx context.Context) error {
|
||||||
o terraform.UIOutput,
|
o := ctx.Value(schema.ProvOutputKey).(terraform.UIOutput)
|
||||||
s *terraform.InstanceState,
|
d := ctx.Value(schema.ProvConfigDataKey).(*schema.ResourceData)
|
||||||
c *terraform.ResourceConfig) error {
|
|
||||||
// Decode the raw config for this provisioner
|
// Decode the raw config for this provisioner
|
||||||
p, err := r.decodeConfig(c)
|
p, err := decodeConfig(d)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if p.OSType == "" {
|
if p.OSType == "" {
|
||||||
switch s.Ephemeral.ConnInfo["type"] {
|
t := d.State().Ephemeral.ConnInfo["type"]
|
||||||
|
switch t {
|
||||||
case "ssh", "": // The default connection type is ssh, so if the type is empty assume ssh
|
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":
|
||||||
p.OSType = "windows"
|
p.OSType = "windows"
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf("Unsupported connection type: %s", s.Ephemeral.ConnInfo["type"])
|
return fmt.Errorf("Unsupported connection type: %s", t)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -169,7 +312,7 @@ func (r *ResourceProvisioner) Apply(
|
||||||
p.generateClientKey = p.generateClientKeyFunc(linuxKnifeCmd, linuxConfDir, linuxNoOutput)
|
p.generateClientKey = p.generateClientKeyFunc(linuxKnifeCmd, linuxConfDir, linuxNoOutput)
|
||||||
p.configureVaults = p.configureVaultsFunc(linuxGemCmd, linuxKnifeCmd, linuxConfDir)
|
p.configureVaults = p.configureVaultsFunc(linuxGemCmd, linuxKnifeCmd, linuxConfDir)
|
||||||
p.runChefClient = p.runChefClientFunc(linuxChefCmd, 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":
|
case "windows":
|
||||||
p.cleanupUserKeyCmd = fmt.Sprintf("cd %s && del /F /Q %s", windowsConfDir, p.UserName+".pem")
|
p.cleanupUserKeyCmd = fmt.Sprintf("cd %s && del /F /Q %s", windowsConfDir, p.UserName+".pem")
|
||||||
p.createConfigFiles = p.windowsCreateConfigFiles
|
p.createConfigFiles = p.windowsCreateConfigFiles
|
||||||
|
@ -184,7 +327,7 @@ func (r *ResourceProvisioner) Apply(
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get a new communicator
|
// Get a new communicator
|
||||||
comm, err := communicator.New(s)
|
comm, err := communicator.New(d.State())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -254,22 +397,16 @@ func (r *ResourceProvisioner) Apply(
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate checks if the required arguments are configured
|
// Validate checks if the required arguments are configured
|
||||||
func (r *ResourceProvisioner) Validate(c *terraform.ResourceConfig) (ws []string, es []error) {
|
func Validate(d *schema.ResourceData) (ws []string, es []error) {
|
||||||
p, err := r.decodeConfig(c)
|
p, err := decodeConfig(d)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
es = append(es, err)
|
es = append(es, err)
|
||||||
return ws, es
|
return ws, es
|
||||||
}
|
}
|
||||||
|
|
||||||
if p.NodeName == "" {
|
|
||||||
es = append(es, errors.New("Key not found: node_name"))
|
|
||||||
}
|
|
||||||
if !p.UsePolicyfile && p.RunList == nil {
|
if !p.UsePolicyfile && p.RunList == nil {
|
||||||
es = append(es, errors.New("Key not found: run_list"))
|
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 == "" {
|
if p.UsePolicyfile && p.PolicyName == "" {
|
||||||
es = append(es, errors.New("Policyfile enabled but key not found: policy_name"))
|
es = append(es, errors.New("Policyfile enabled but key not found: policy_name"))
|
||||||
}
|
}
|
||||||
|
@ -284,12 +421,7 @@ func (r *ResourceProvisioner) Validate(c *terraform.ResourceConfig) (ws []string
|
||||||
es = append(es, errors.New(
|
es = append(es, errors.New(
|
||||||
"One of user_key or the deprecated validation_key must be provided"))
|
"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 != "" {
|
if p.ValidationKey != "" {
|
||||||
ws = append(ws, "validation_key is deprecated, please use user_key instead")
|
|
||||||
|
|
||||||
if p.RecreateClient {
|
if p.RecreateClient {
|
||||||
es = append(es, errors.New(
|
es = append(es, errors.New(
|
||||||
"Cannot use recreate_client=true with the deprecated validation_key, please provide a user_key"))
|
"Cannot use recreate_client=true with the deprecated validation_key, please provide a user_key"))
|
||||||
|
@ -303,38 +435,8 @@ func (r *ResourceProvisioner) Validate(c *terraform.ResourceConfig) (ws []string
|
||||||
return ws, es
|
return ws, es
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *ResourceProvisioner) decodeConfig(c *terraform.ResourceConfig) (*Provisioner, error) {
|
func decodeConfig(d *schema.ResourceData) (*ProvisionerS, error) {
|
||||||
p := new(Provisioner)
|
p := decodeDataToProvisioner(d)
|
||||||
|
|
||||||
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
|
// Make sure the supplied URL has a trailing slash
|
||||||
p.ServerURL = strings.TrimSuffix(p.ServerURL, "/") + "/"
|
p.ServerURL = strings.TrimSuffix(p.ServerURL, "/") + "/"
|
||||||
|
@ -359,17 +461,17 @@ func (r *ResourceProvisioner) decodeConfig(c *terraform.ResourceConfig) (*Provis
|
||||||
p.UserKey = p.ValidationKey
|
p.UserKey = p.ValidationKey
|
||||||
}
|
}
|
||||||
|
|
||||||
if attrs, ok := c.Config["attributes_json"].(string); ok && !c.IsComputed("attributes_json") {
|
if attrs, ok := d.GetOk("attributes_json"); ok {
|
||||||
var m map[string]interface{}
|
var m map[string]interface{}
|
||||||
if err := json.Unmarshal([]byte(attrs), &m); err != nil {
|
if err := json.Unmarshal([]byte(attrs.(string)), &m); err != nil {
|
||||||
return nil, fmt.Errorf("Error parsing attributes_json: %v", err)
|
return nil, fmt.Errorf("Error parsing attributes_json: %v", err)
|
||||||
}
|
}
|
||||||
p.attributes = m
|
p.attributes = m
|
||||||
}
|
}
|
||||||
|
|
||||||
if vaults, ok := c.Config["vault_json"].(string); ok && !c.IsComputed("vault_json") {
|
if vaults, ok := d.GetOk("vault_json"); ok {
|
||||||
var m map[string]interface{}
|
var m map[string]interface{}
|
||||||
if err := json.Unmarshal([]byte(vaults), &m); err != nil {
|
if err := json.Unmarshal([]byte(vaults.(string)), &m); err != nil {
|
||||||
return nil, fmt.Errorf("Error parsing vault_json: %v", err)
|
return nil, fmt.Errorf("Error parsing vault_json: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -395,7 +497,7 @@ func (r *ResourceProvisioner) decodeConfig(c *terraform.ResourceConfig) (*Provis
|
||||||
return p, nil
|
return p, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Provisioner) deployConfigFiles(
|
func (p *ProvisionerS) deployConfigFiles(
|
||||||
o terraform.UIOutput,
|
o terraform.UIOutput,
|
||||||
comm communicator.Communicator,
|
comm communicator.Communicator,
|
||||||
confDir string) error {
|
confDir string) error {
|
||||||
|
@ -469,7 +571,7 @@ func (p *Provisioner) deployConfigFiles(
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Provisioner) deployOhaiHints(
|
func (p *ProvisionerS) deployOhaiHints(
|
||||||
o terraform.UIOutput,
|
o terraform.UIOutput,
|
||||||
comm communicator.Communicator,
|
comm communicator.Communicator,
|
||||||
hintDir string) error {
|
hintDir string) error {
|
||||||
|
@ -490,7 +592,7 @@ func (p *Provisioner) deployOhaiHints(
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Provisioner) fetchChefCertificatesFunc(
|
func (p *ProvisionerS) 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 {
|
||||||
|
@ -501,7 +603,7 @@ func (p *Provisioner) fetchChefCertificatesFunc(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Provisioner) generateClientKeyFunc(
|
func (p *ProvisionerS) generateClientKeyFunc(
|
||||||
knifeCmd string,
|
knifeCmd string,
|
||||||
confDir string,
|
confDir string,
|
||||||
noOutput string) func(terraform.UIOutput, communicator.Communicator) error {
|
noOutput string) func(terraform.UIOutput, communicator.Communicator) error {
|
||||||
|
@ -562,7 +664,7 @@ func (p *Provisioner) generateClientKeyFunc(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Provisioner) configureVaultsFunc(
|
func (p *ProvisionerS) configureVaultsFunc(
|
||||||
gemCmd string,
|
gemCmd string,
|
||||||
knifeCmd string,
|
knifeCmd string,
|
||||||
confDir string) func(terraform.UIOutput, communicator.Communicator) error {
|
confDir string) func(terraform.UIOutput, communicator.Communicator) error {
|
||||||
|
@ -596,7 +698,7 @@ func (p *Provisioner) configureVaultsFunc(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Provisioner) runChefClientFunc(
|
func (p *ProvisionerS) runChefClientFunc(
|
||||||
chefCmd string,
|
chefCmd 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 {
|
||||||
|
@ -634,7 +736,7 @@ func (p *Provisioner) runChefClientFunc(
|
||||||
}
|
}
|
||||||
|
|
||||||
// Output implementation of terraform.UIOutput interface
|
// Output implementation of terraform.UIOutput interface
|
||||||
func (p *Provisioner) Output(output string) {
|
func (p *ProvisionerS) 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 {
|
||||||
|
@ -660,7 +762,7 @@ func (p *Provisioner) Output(output string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// runCommand is used to run already prepared commands
|
// runCommand is used to run already prepared commands
|
||||||
func (p *Provisioner) runCommand(
|
func (p *ProvisionerS) runCommand(
|
||||||
o terraform.UIOutput,
|
o terraform.UIOutput,
|
||||||
comm communicator.Communicator,
|
comm communicator.Communicator,
|
||||||
command string) error {
|
command string) error {
|
||||||
|
@ -702,7 +804,7 @@ func (p *Provisioner) runCommand(
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Provisioner) copyOutput(o terraform.UIOutput, r io.Reader, doneCh chan<- struct{}) {
|
func (p *ProvisionerS) 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 {
|
||||||
|
@ -727,3 +829,58 @@ func retryFunc(timeout time.Duration, f func() error) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func decodeDataToProvisioner(d *schema.ResourceData) *ProvisionerS {
|
||||||
|
return &ProvisionerS{
|
||||||
|
AttributesJSON: d.Get("attributes_json").(string),
|
||||||
|
ClientOptions: getStringList(d.Get("client_options")),
|
||||||
|
DisableReporting: d.Get("disable_reporting").(bool),
|
||||||
|
Environment: d.Get("environment").(string),
|
||||||
|
FetchChefCertificates: d.Get("fetch_chef_certificates").(bool),
|
||||||
|
LogToFile: d.Get("log_to_file").(bool),
|
||||||
|
UsePolicyfile: d.Get("use_policyfile").(bool),
|
||||||
|
PolicyGroup: d.Get("policy_group").(string),
|
||||||
|
PolicyName: d.Get("policy_name").(string),
|
||||||
|
HTTPProxy: d.Get("http_proxy").(string),
|
||||||
|
HTTPSProxy: d.Get("https_proxy").(string),
|
||||||
|
NOProxy: getStringList(d.Get("no_proxy")),
|
||||||
|
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),
|
||||||
|
VaultJSON: d.Get("vault_json").(string),
|
||||||
|
Version: d.Get("version").(string),
|
||||||
|
|
||||||
|
// Deprecated
|
||||||
|
ValidationClientName: d.Get("validation_client_name").(string),
|
||||||
|
ValidationKey: d.Get("validation_key").(string),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func getStringList(v interface{}) []string {
|
||||||
|
if v == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
switch l := v.(type) {
|
||||||
|
case []string:
|
||||||
|
return l
|
||||||
|
case []interface{}:
|
||||||
|
arr := make([]string, len(l))
|
||||||
|
for i, x := range l {
|
||||||
|
arr[i] = x.(string)
|
||||||
|
}
|
||||||
|
return arr
|
||||||
|
default:
|
||||||
|
panic(fmt.Sprintf("Unsupported type: %T", v))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -7,11 +7,18 @@ import (
|
||||||
|
|
||||||
"github.com/hashicorp/terraform/communicator"
|
"github.com/hashicorp/terraform/communicator"
|
||||||
"github.com/hashicorp/terraform/config"
|
"github.com/hashicorp/terraform/config"
|
||||||
|
"github.com/hashicorp/terraform/helper/schema"
|
||||||
"github.com/hashicorp/terraform/terraform"
|
"github.com/hashicorp/terraform/terraform"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestResourceProvisioner_impl(t *testing.T) {
|
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) {
|
func TestResourceProvider_Validate_good(t *testing.T) {
|
||||||
|
@ -23,7 +30,7 @@ func TestResourceProvider_Validate_good(t *testing.T) {
|
||||||
"user_name": "bob",
|
"user_name": "bob",
|
||||||
"user_key": "USER-KEY",
|
"user_key": "USER-KEY",
|
||||||
})
|
})
|
||||||
r := new(ResourceProvisioner)
|
r := Provisioner()
|
||||||
warn, errs := r.Validate(c)
|
warn, errs := r.Validate(c)
|
||||||
if len(warn) > 0 {
|
if len(warn) > 0 {
|
||||||
t.Fatalf("Warnings: %v", warn)
|
t.Fatalf("Warnings: %v", warn)
|
||||||
|
@ -37,7 +44,7 @@ func TestResourceProvider_Validate_bad(t *testing.T) {
|
||||||
c := testConfig(t, map[string]interface{}{
|
c := testConfig(t, map[string]interface{}{
|
||||||
"invalid": "nope",
|
"invalid": "nope",
|
||||||
})
|
})
|
||||||
p := new(ResourceProvisioner)
|
p := Provisioner()
|
||||||
warn, errs := p.Validate(c)
|
warn, errs := p.Validate(c)
|
||||||
if len(warn) > 0 {
|
if len(warn) > 0 {
|
||||||
t.Fatalf("Warnings: %v", warn)
|
t.Fatalf("Warnings: %v", warn)
|
||||||
|
@ -59,7 +66,7 @@ func TestResourceProvider_Validate_computedValues(t *testing.T) {
|
||||||
"user_key": "USER-KEY",
|
"user_key": "USER-KEY",
|
||||||
"attributes_json": config.UnknownVariableValue,
|
"attributes_json": config.UnknownVariableValue,
|
||||||
})
|
})
|
||||||
r := new(ResourceProvisioner)
|
r := Provisioner()
|
||||||
warn, errs := r.Validate(c)
|
warn, errs := r.Validate(c)
|
||||||
if len(warn) > 0 {
|
if len(warn) > 0 {
|
||||||
t.Fatalf("Warnings: %v", warn)
|
t.Fatalf("Warnings: %v", warn)
|
||||||
|
@ -149,14 +156,13 @@ func TestResourceProvider_runChefClient(t *testing.T) {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
r := new(ResourceProvisioner)
|
|
||||||
o := new(terraform.MockUIOutput)
|
o := new(terraform.MockUIOutput)
|
||||||
c := new(communicator.MockCommunicator)
|
c := new(communicator.MockCommunicator)
|
||||||
|
|
||||||
for k, tc := range cases {
|
for k, tc := range cases {
|
||||||
c.Commands = tc.Commands
|
c.Commands = tc.Commands
|
||||||
|
|
||||||
p, err := r.decodeConfig(tc.Config)
|
p, err := decodeConfig(getTestResourceData(tc.Config))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Error: %v", err)
|
t.Fatalf("Error: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -222,14 +228,13 @@ func TestResourceProvider_fetchChefCertificates(t *testing.T) {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
r := new(ResourceProvisioner)
|
|
||||||
o := new(terraform.MockUIOutput)
|
o := new(terraform.MockUIOutput)
|
||||||
c := new(communicator.MockCommunicator)
|
c := new(communicator.MockCommunicator)
|
||||||
|
|
||||||
for k, tc := range cases {
|
for k, tc := range cases {
|
||||||
c.Commands = tc.Commands
|
c.Commands = tc.Commands
|
||||||
|
|
||||||
p, err := r.decodeConfig(tc.Config)
|
p, err := decodeConfig(getTestResourceData(tc.Config))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Error: %v", err)
|
t.Fatalf("Error: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -347,14 +352,13 @@ func TestResourceProvider_configureVaults(t *testing.T) {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
r := new(ResourceProvisioner)
|
|
||||||
o := new(terraform.MockUIOutput)
|
o := new(terraform.MockUIOutput)
|
||||||
c := new(communicator.MockCommunicator)
|
c := new(communicator.MockCommunicator)
|
||||||
|
|
||||||
for k, tc := range cases {
|
for k, tc := range cases {
|
||||||
c.Commands = tc.Commands
|
c.Commands = tc.Commands
|
||||||
|
|
||||||
p, err := r.decodeConfig(tc.Config)
|
p, err := decodeConfig(getTestResourceData(tc.Config))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Error: %v", err)
|
t.Fatalf("Error: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -368,3 +372,7 @@ func TestResourceProvider_configureVaults(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getTestResourceData(c *terraform.ResourceConfig) *schema.ResourceData {
|
||||||
|
return schema.TestResourceDataConfig(Provisioner().(*schema.Provisioner).Schema, c)
|
||||||
|
}
|
||||||
|
|
|
@ -46,7 +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 *Provisioner) windowsInstallChefClient(
|
func (p *ProvisionerS) windowsInstallChefClient(
|
||||||
o terraform.UIOutput,
|
o terraform.UIOutput,
|
||||||
comm communicator.Communicator) error {
|
comm communicator.Communicator) error {
|
||||||
script := path.Join(path.Dir(comm.ScriptPath()), "ChefClient.ps1")
|
script := path.Join(path.Dir(comm.ScriptPath()), "ChefClient.ps1")
|
||||||
|
@ -62,7 +62,7 @@ func (p *Provisioner) windowsInstallChefClient(
|
||||||
return p.runCommand(o, comm, installCmd)
|
return p.runCommand(o, comm, installCmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Provisioner) windowsCreateConfigFiles(
|
func (p *ProvisionerS) windowsCreateConfigFiles(
|
||||||
o terraform.UIOutput,
|
o terraform.UIOutput,
|
||||||
comm communicator.Communicator) error {
|
comm communicator.Communicator) error {
|
||||||
// Make sure the config directory exists
|
// Make sure the config directory exists
|
||||||
|
|
|
@ -73,7 +73,6 @@ func TestResourceProvider_windowsInstallChefClient(t *testing.T) {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
r := new(ResourceProvisioner)
|
|
||||||
o := new(terraform.MockUIOutput)
|
o := new(terraform.MockUIOutput)
|
||||||
c := new(communicator.MockCommunicator)
|
c := new(communicator.MockCommunicator)
|
||||||
|
|
||||||
|
@ -81,7 +80,7 @@ func TestResourceProvider_windowsInstallChefClient(t *testing.T) {
|
||||||
c.Commands = tc.Commands
|
c.Commands = tc.Commands
|
||||||
c.UploadScripts = tc.UploadScripts
|
c.UploadScripts = tc.UploadScripts
|
||||||
|
|
||||||
p, err := r.decodeConfig(tc.Config)
|
p, err := decodeConfig(getTestResourceData(tc.Config))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Error: %v", err)
|
t.Fatalf("Error: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -180,7 +179,6 @@ func TestResourceProvider_windowsCreateConfigFiles(t *testing.T) {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
r := new(ResourceProvisioner)
|
|
||||||
o := new(terraform.MockUIOutput)
|
o := new(terraform.MockUIOutput)
|
||||||
c := new(communicator.MockCommunicator)
|
c := new(communicator.MockCommunicator)
|
||||||
|
|
||||||
|
@ -188,7 +186,7 @@ func TestResourceProvider_windowsCreateConfigFiles(t *testing.T) {
|
||||||
c.Commands = tc.Commands
|
c.Commands = tc.Commands
|
||||||
c.Uploads = tc.Uploads
|
c.Uploads = tc.Uploads
|
||||||
|
|
||||||
p, err := r.decodeConfig(tc.Config)
|
p, err := decodeConfig(getTestResourceData(tc.Config))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Error: %v", err)
|
t.Fatalf("Error: %v", err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,7 +35,8 @@ func Provisioner() terraform.ResourceProvisioner {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
ApplyFunc: applyFn,
|
ApplyFunc: applyFn,
|
||||||
|
ValidateFunc: Validate,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -77,6 +78,21 @@ func applyFn(ctx context.Context) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Validate checks if the required arguments are configured
|
||||||
|
func Validate(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
|
// getSrc returns the file to use as source
|
||||||
func getSrc(data *schema.ResourceData) (string, bool, error) {
|
func getSrc(data *schema.ResourceData) (string, bool, error) {
|
||||||
src := data.Get("source").(string)
|
src := data.Get("source").(string)
|
||||||
|
|
|
@ -4,9 +4,20 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/hashicorp/terraform/config"
|
"github.com/hashicorp/terraform/config"
|
||||||
|
"github.com/hashicorp/terraform/helper/schema"
|
||||||
"github.com/hashicorp/terraform/terraform"
|
"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) {
|
func TestResourceProvider_Validate_good_source(t *testing.T) {
|
||||||
c := testConfig(t, map[string]interface{}{
|
c := testConfig(t, map[string]interface{}{
|
||||||
"source": "/tmp/foo",
|
"source": "/tmp/foo",
|
||||||
|
@ -51,6 +62,20 @@ func TestResourceProvider_Validate_bad_not_destination(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestResourceProvider_Validate_bad_no_source(t *testing.T) {
|
||||||
|
c := testConfig(t, map[string]interface{}{
|
||||||
|
"destination": "/tmp/bar",
|
||||||
|
})
|
||||||
|
p := Provisioner()
|
||||||
|
warn, errs := p.Validate(c)
|
||||||
|
if len(warn) > 0 {
|
||||||
|
t.Fatalf("Warnings: %v", warn)
|
||||||
|
}
|
||||||
|
if len(errs) == 0 {
|
||||||
|
t.Fatalf("Should have errors")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestResourceProvider_Validate_bad_to_many_src(t *testing.T) {
|
func TestResourceProvider_Validate_bad_to_many_src(t *testing.T) {
|
||||||
c := testConfig(t, map[string]interface{}{
|
c := testConfig(t, map[string]interface{}{
|
||||||
"source": "nope",
|
"source": "nope",
|
||||||
|
|
|
@ -8,9 +8,20 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/hashicorp/terraform/config"
|
"github.com/hashicorp/terraform/config"
|
||||||
|
"github.com/hashicorp/terraform/helper/schema"
|
||||||
"github.com/hashicorp/terraform/terraform"
|
"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) {
|
func TestResourceProvider_Apply(t *testing.T) {
|
||||||
defer os.Remove("test_out")
|
defer os.Remove("test_out")
|
||||||
c := testConfig(t, map[string]interface{}{
|
c := testConfig(t, map[string]interface{}{
|
||||||
|
|
|
@ -16,6 +16,16 @@ import (
|
||||||
"github.com/hashicorp/terraform/terraform"
|
"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) {
|
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",
|
||||||
|
|
|
@ -41,6 +41,11 @@ 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.
|
||||||
|
// It is given a resource data.
|
||||||
|
// Should be provided when Scheme is not enough.
|
||||||
|
ValidateFunc func(*ResourceData) ([]string, []error)
|
||||||
|
|
||||||
stopCtx context.Context
|
stopCtx context.Context
|
||||||
stopCtxCancel context.CancelFunc
|
stopCtxCancel context.CancelFunc
|
||||||
stopOnce sync.Once
|
stopOnce sync.Once
|
||||||
|
@ -117,8 +122,30 @@ func (p *Provisioner) Stop() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Provisioner) Validate(c *terraform.ResourceConfig) ([]string, []error) {
|
func (p *Provisioner) Validate(config *terraform.ResourceConfig) ([]string, []error) {
|
||||||
return schemaMap(p.Schema).Validate(c)
|
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.
|
// Apply implementation of terraform.ResourceProvisioner interface.
|
||||||
|
|
|
@ -3,6 +3,7 @@ package schema
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
@ -14,13 +15,35 @@ func TestProvisioner_impl(t *testing.T) {
|
||||||
var _ terraform.ResourceProvisioner = new(Provisioner)
|
var _ terraform.ResourceProvisioner = new(Provisioner)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func noopApply(ctx context.Context) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func TestProvisionerValidate(t *testing.T) {
|
func TestProvisionerValidate(t *testing.T) {
|
||||||
cases := []struct {
|
cases := []struct {
|
||||||
Name string
|
Name string
|
||||||
P *Provisioner
|
P *Provisioner
|
||||||
Config map[string]interface{}
|
Config map[string]interface{}
|
||||||
Err bool
|
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",
|
"Basic required field",
|
||||||
&Provisioner{
|
&Provisioner{
|
||||||
|
@ -30,9 +53,11 @@ func TestProvisionerValidate(t *testing.T) {
|
||||||
Type: TypeString,
|
Type: TypeString,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
ApplyFunc: noopApply,
|
||||||
},
|
},
|
||||||
nil,
|
nil,
|
||||||
true,
|
true,
|
||||||
|
nil,
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
|
@ -44,11 +69,57 @@ func TestProvisionerValidate(t *testing.T) {
|
||||||
Type: TypeString,
|
Type: TypeString,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
ApplyFunc: noopApply,
|
||||||
},
|
},
|
||||||
map[string]interface{}{
|
map[string]interface{}{
|
||||||
"foo": "bar",
|
"foo": "bar",
|
||||||
},
|
},
|
||||||
false,
|
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)
|
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 {
|
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)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,3 +28,10 @@ func TestResourceDataRaw(
|
||||||
|
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestResourceDataConfig(schema map[string]*Schema, config *terraform.ResourceConfig) *ResourceData {
|
||||||
|
return &ResourceData{
|
||||||
|
schema: schema,
|
||||||
|
config: config,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue