From 73ce6d184a70bfaf9f2ad2c6835056c1c3820116 Mon Sep 17 00:00:00 2001 From: Paul Hinze Date: Thu, 12 Nov 2015 15:17:51 -0600 Subject: [PATCH] chef: read key contents instead of paths Builds on the work of #3846, shifting the Chef provisioner's configuration options from `secret_key_path` and `validation_key_path` over to `secret_key` and `validation_key`. --- .../provisioners/chef/resource_provisioner.go | 73 ++++++++++--------- .../chef/resource_provisioner_test.go | 2 +- .../docs/provisioners/chef.html.markdown | 20 +++-- 3 files changed, 53 insertions(+), 42 deletions(-) diff --git a/builtin/provisioners/chef/resource_provisioner.go b/builtin/provisioners/chef/resource_provisioner.go index 50b5666ee134..c0e040b5f987 100644 --- a/builtin/provisioners/chef/resource_provisioner.go +++ b/builtin/provisioners/chef/resource_provisioner.go @@ -8,7 +8,7 @@ import ( "io" "log" "os" - "path" + "path/filepath" "regexp" "strings" "text/template" @@ -16,6 +16,7 @@ import ( "github.com/hashicorp/terraform/communicator" "github.com/hashicorp/terraform/communicator/remote" + "github.com/hashicorp/terraform/helper/pathorcontents" "github.com/hashicorp/terraform/terraform" "github.com/mitchellh/go-homedir" "github.com/mitchellh/go-linereader" @@ -79,18 +80,22 @@ type Provisioner struct { OSType string `mapstructure:"os_type"` PreventSudo bool `mapstructure:"prevent_sudo"` RunList []string `mapstructure:"run_list"` - SecretKeyPath string `mapstructure:"secret_key_path"` + SecretKey string `mapstructure:"secret_key"` ServerURL string `mapstructure:"server_url"` SkipInstall bool `mapstructure:"skip_install"` SSLVerifyMode string `mapstructure:"ssl_verify_mode"` ValidationClientName string `mapstructure:"validation_client_name"` - ValidationKeyPath string `mapstructure:"validation_key_path"` + ValidationKey string `mapstructure:"validation_key"` Version string `mapstructure:"version"` installChefClient func(terraform.UIOutput, communicator.Communicator) error createConfigFiles func(terraform.UIOutput, communicator.Communicator) error runChefClient func(terraform.UIOutput, communicator.Communicator) error useSudo bool + + // Deprecated Fields + SecretKeyPath string `mapstructure:"secret_key_path"` + ValidationKeyPath string `mapstructure:"validation_key_path"` } // ResourceProvisioner represents a generic chef provisioner @@ -189,8 +194,9 @@ func (r *ResourceProvisioner) Validate(c *terraform.ResourceConfig) (ws []string if p.ValidationClientName == "" { es = append(es, fmt.Errorf("Key not found: validation_client_name")) } - if p.ValidationKeyPath == "" { - es = append(es, fmt.Errorf("Key not found: validation_key_path")) + if p.ValidationKey == "" && p.ValidationKeyPath == "" { + es = append(es, fmt.Errorf( + "One of validation_key or the deprecated validation_key_path must be provided")) } if p.UsePolicyfile && p.PolicyName == "" { es = append(es, fmt.Errorf("Policyfile enabled but key not found: policy_name")) @@ -198,6 +204,14 @@ func (r *ResourceProvisioner) Validate(c *terraform.ResourceConfig) (ws []string if p.UsePolicyfile && p.PolicyGroup == "" { es = append(es, fmt.Errorf("Policyfile enabled but key not found: policy_group")) } + if p.ValidationKeyPath != "" { + ws = append(ws, "validation_key_path is deprecated, please use "+ + "validation_key instead and load the key contents via file()") + } + if p.SecretKeyPath != "" { + ws = append(ws, "secret_key_path is deprecated, please use "+ + "secret_key instead and load the key contents via file()") + } return ws, es } @@ -247,20 +261,12 @@ func (r *ResourceProvisioner) decodeConfig(c *terraform.ResourceConfig) (*Provis p.OhaiHints[i] = hintPath } - if p.ValidationKeyPath != "" { - keyPath, err := homedir.Expand(p.ValidationKeyPath) - if err != nil { - return nil, fmt.Errorf("Error expanding the validation key path: %v", err) - } - p.ValidationKeyPath = keyPath + if p.ValidationKey == "" && p.ValidationKeyPath != "" { + p.ValidationKey = p.ValidationKeyPath } - if p.SecretKeyPath != "" { - keyPath, err := homedir.Expand(p.SecretKeyPath) - if err != nil { - return nil, fmt.Errorf("Error expanding the secret key path: %v", err) - } - p.SecretKeyPath = keyPath + if p.SecretKey == "" && p.SecretKeyPath != "" { + p.SecretKey = p.SecretKeyPath } if attrs, ok := c.Config["attributes"]; ok { @@ -316,7 +322,7 @@ func (p *Provisioner) runChefClientFunc( chefCmd string, confDir string) func(terraform.UIOutput, communicator.Communicator) error { return func(o terraform.UIOutput, comm communicator.Communicator) error { - fb := path.Join(confDir, firstBoot) + fb := filepath.Join(confDir, firstBoot) var cmd string // Policyfiles do not support chef environments, so don't pass the `-E` flag. @@ -331,8 +337,8 @@ func (p *Provisioner) runChefClientFunc( return fmt.Errorf("Error creating logfile directory %s: %v", logfileDir, err) } - logFile := path.Join(logfileDir, p.NodeName) - f, err := os.Create(path.Join(logFile)) + logFile := filepath.Join(logfileDir, p.NodeName) + f, err := os.Create(filepath.Join(logFile)) if err != nil { return fmt.Errorf("Error creating logfile %s: %v", logFile, err) } @@ -348,7 +354,7 @@ func (p *Provisioner) runChefClientFunc( // Output implementation of terraform.UIOutput interface func (p *Provisioner) Output(output string) { - logFile := path.Join(logfileDir, p.NodeName) + logFile := filepath.Join(logfileDir, p.NodeName) f, err := os.OpenFile(logFile, os.O_APPEND|os.O_WRONLY, 0666) if err != nil { log.Printf("Error creating logfile %s: %v", logFile, err) @@ -376,28 +382,25 @@ func (p *Provisioner) deployConfigFiles( o terraform.UIOutput, comm communicator.Communicator, confDir string) error { - // Open the validation key file - f, err := os.Open(p.ValidationKeyPath) + contents, _, err := pathorcontents.Read(p.ValidationKey) if err != nil { return err } - defer f.Close() + f := strings.NewReader(contents) // Copy the validation key to the new instance - if err := comm.Upload(path.Join(confDir, validationKey), f); err != nil { + if err := comm.Upload(filepath.Join(confDir, validationKey), f); err != nil { return fmt.Errorf("Uploading %s failed: %v", validationKey, err) } - if p.SecretKeyPath != "" { - // Open the secret key file - s, err := os.Open(p.SecretKeyPath) + if p.SecretKey != "" { + contents, _, err := pathorcontents.Read(p.SecretKey) if err != nil { return err } - defer s.Close() - + s := strings.NewReader(contents) // Copy the secret key to the new instance - if err := comm.Upload(path.Join(confDir, secretKey), s); err != nil { + if err := comm.Upload(filepath.Join(confDir, secretKey), s); err != nil { return fmt.Errorf("Uploading %s failed: %v", secretKey, err) } } @@ -417,7 +420,7 @@ 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(filepath.Join(confDir, clienrb), &buf); err != nil { return fmt.Errorf("Uploading %s failed: %v", clienrb, err) } @@ -446,7 +449,7 @@ func (p *Provisioner) deployConfigFiles( } // Copy the first-boot.json to the new instance - if err := comm.Upload(path.Join(confDir, firstBoot), bytes.NewReader(d)); err != nil { + if err := comm.Upload(filepath.Join(confDir, firstBoot), bytes.NewReader(d)); err != nil { return fmt.Errorf("Uploading %s failed: %v", firstBoot, err) } @@ -466,8 +469,8 @@ func (p *Provisioner) deployOhaiHints( defer f.Close() // Copy the hint to the new instance - if err := comm.Upload(path.Join(hintDir, path.Base(hint)), f); err != nil { - return fmt.Errorf("Uploading %s failed: %v", path.Base(hint), err) + if err := comm.Upload(filepath.Join(hintDir, filepath.Base(hint)), f); err != nil { + return fmt.Errorf("Uploading %s failed: %v", filepath.Base(hint), err) } } diff --git a/builtin/provisioners/chef/resource_provisioner_test.go b/builtin/provisioners/chef/resource_provisioner_test.go index 78c44c7ea316..40625196a760 100644 --- a/builtin/provisioners/chef/resource_provisioner_test.go +++ b/builtin/provisioners/chef/resource_provisioner_test.go @@ -22,7 +22,7 @@ func TestResourceProvider_Validate_good(t *testing.T) { "run_list": []interface{}{"cookbook::recipe"}, "server_url": "https://chef.local", "validation_client_name": "validator", - "validation_key_path": "validator.pem", + "validation_key": "contentsofsomevalidator.pem", }) r := new(ResourceProvisioner) warn, errs := r.Validate(c) diff --git a/website/source/docs/provisioners/chef.html.markdown b/website/source/docs/provisioners/chef.html.markdown index a1a7e7ba90c8..60f6d577a0a3 100644 --- a/website/source/docs/provisioners/chef.html.markdown +++ b/website/source/docs/provisioners/chef.html.markdown @@ -36,10 +36,10 @@ resource "aws_instance" "web" { environment = "_default" run_list = ["cookbook::recipe"] node_name = "webserver1" - secret_key_path = "../encrypted_data_bag_secret" + secret_key = "${file("../encrypted_data_bag_secret")}" server_url = "https://chef.company.com/organizations/org1" validation_client_name = "chef-validator" - validation_key_path = "../chef-validator.pem" + validation_key = "${file("../chef-validator.pem")}" version = "12.4.1" } } @@ -83,9 +83,10 @@ The following arguments are supported: Chef Client run. The run-list will also be saved to the Chef Server after a successful initial run. -* `secret_key_path (string)` - (Optional) The path to the secret key that is used +* `secret_key (string)` - (Optional) The contents of the secret key that is used by the client to decrypt data bags on the Chef Server. The key will be uploaded to the remote - machine. + machine. These can be loaded from a file on disk using the [`file()` interpolation + function](/docs/configuration/interpolation.html#file_path_). * `server_url (string)` - (Required) The URL to the Chef server. This includes the path to the organization. See the example. @@ -100,9 +101,16 @@ The following arguments are supported: * `validation_client_name (string)` - (Required) The name of the validation client to use for the initial communication with the Chef Server. -* `validation_key_path (string)` - (Required) The path to the validation key that is needed +* `validation_key (string)` - (Required) The contents of the validation key that is needed by the node to register itself with the Chef Server. The key will be uploaded to the remote - machine. + machine. These can be loaded from a file on disk using the [`file()` + interpolation function](/docs/configuration/interpolation.html#file_path_). * `version (string)` - (Optional) The Chef Client version to install on the remote machine. If not set the latest available version will be installed. + +These are supported for backwards compatibility and may be removed in a +future version: + +* `validation_key_path (string)` - __Deprecated: please use `validation_key` instead__. +* `secret_key_path (string)` - __Deprecated: please use `secret_key` instead__.