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`.
This commit is contained in:
Paul Hinze 2015-11-12 15:17:51 -06:00
parent b6aed3fec3
commit 73ce6d184a
3 changed files with 53 additions and 42 deletions

View File

@ -8,7 +8,7 @@ import (
"io" "io"
"log" "log"
"os" "os"
"path" "path/filepath"
"regexp" "regexp"
"strings" "strings"
"text/template" "text/template"
@ -16,6 +16,7 @@ import (
"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/pathorcontents"
"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"
@ -79,18 +80,22 @@ type Provisioner struct {
OSType string `mapstructure:"os_type"` OSType string `mapstructure:"os_type"`
PreventSudo bool `mapstructure:"prevent_sudo"` PreventSudo bool `mapstructure:"prevent_sudo"`
RunList []string `mapstructure:"run_list"` RunList []string `mapstructure:"run_list"`
SecretKeyPath string `mapstructure:"secret_key_path"` SecretKey string `mapstructure:"secret_key"`
ServerURL string `mapstructure:"server_url"` ServerURL string `mapstructure:"server_url"`
SkipInstall bool `mapstructure:"skip_install"` SkipInstall bool `mapstructure:"skip_install"`
SSLVerifyMode string `mapstructure:"ssl_verify_mode"` SSLVerifyMode string `mapstructure:"ssl_verify_mode"`
ValidationClientName string `mapstructure:"validation_client_name"` ValidationClientName string `mapstructure:"validation_client_name"`
ValidationKeyPath string `mapstructure:"validation_key_path"` ValidationKey string `mapstructure:"validation_key"`
Version string `mapstructure:"version"` Version string `mapstructure:"version"`
installChefClient func(terraform.UIOutput, communicator.Communicator) error installChefClient func(terraform.UIOutput, communicator.Communicator) error
createConfigFiles func(terraform.UIOutput, communicator.Communicator) error createConfigFiles func(terraform.UIOutput, communicator.Communicator) error
runChefClient func(terraform.UIOutput, communicator.Communicator) error runChefClient func(terraform.UIOutput, communicator.Communicator) error
useSudo bool useSudo bool
// Deprecated Fields
SecretKeyPath string `mapstructure:"secret_key_path"`
ValidationKeyPath string `mapstructure:"validation_key_path"`
} }
// ResourceProvisioner represents a generic chef provisioner // ResourceProvisioner represents a generic chef provisioner
@ -189,8 +194,9 @@ func (r *ResourceProvisioner) Validate(c *terraform.ResourceConfig) (ws []string
if p.ValidationClientName == "" { if p.ValidationClientName == "" {
es = append(es, fmt.Errorf("Key not found: validation_client_name")) es = append(es, fmt.Errorf("Key not found: validation_client_name"))
} }
if p.ValidationKeyPath == "" { if p.ValidationKey == "" && p.ValidationKeyPath == "" {
es = append(es, fmt.Errorf("Key not found: validation_key_path")) es = append(es, fmt.Errorf(
"One of validation_key or the deprecated validation_key_path must be provided"))
} }
if p.UsePolicyfile && p.PolicyName == "" { if p.UsePolicyfile && p.PolicyName == "" {
es = append(es, fmt.Errorf("Policyfile enabled but key not found: policy_name")) 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 == "" { if p.UsePolicyfile && p.PolicyGroup == "" {
es = append(es, fmt.Errorf("Policyfile enabled but key not found: policy_group")) 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 return ws, es
} }
@ -247,20 +261,12 @@ func (r *ResourceProvisioner) decodeConfig(c *terraform.ResourceConfig) (*Provis
p.OhaiHints[i] = hintPath p.OhaiHints[i] = hintPath
} }
if p.ValidationKeyPath != "" { if p.ValidationKey == "" && p.ValidationKeyPath != "" {
keyPath, err := homedir.Expand(p.ValidationKeyPath) p.ValidationKey = p.ValidationKeyPath
if err != nil {
return nil, fmt.Errorf("Error expanding the validation key path: %v", err)
}
p.ValidationKeyPath = keyPath
} }
if p.SecretKeyPath != "" { if p.SecretKey == "" && p.SecretKeyPath != "" {
keyPath, err := homedir.Expand(p.SecretKeyPath) p.SecretKey = p.SecretKeyPath
if err != nil {
return nil, fmt.Errorf("Error expanding the secret key path: %v", err)
}
p.SecretKeyPath = keyPath
} }
if attrs, ok := c.Config["attributes"]; ok { if attrs, ok := c.Config["attributes"]; ok {
@ -316,7 +322,7 @@ func (p *Provisioner) 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 {
fb := path.Join(confDir, firstBoot) fb := filepath.Join(confDir, firstBoot)
var cmd string var cmd string
// Policyfiles do not support chef environments, so don't pass the `-E` flag. // 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) return fmt.Errorf("Error creating logfile directory %s: %v", logfileDir, err)
} }
logFile := path.Join(logfileDir, p.NodeName) logFile := filepath.Join(logfileDir, p.NodeName)
f, err := os.Create(path.Join(logFile)) f, err := os.Create(filepath.Join(logFile))
if err != nil { if err != nil {
return fmt.Errorf("Error creating logfile %s: %v", logFile, err) return fmt.Errorf("Error creating logfile %s: %v", logFile, err)
} }
@ -348,7 +354,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 *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) f, err := os.OpenFile(logFile, os.O_APPEND|os.O_WRONLY, 0666)
if err != nil { if err != nil {
log.Printf("Error creating logfile %s: %v", logFile, err) log.Printf("Error creating logfile %s: %v", logFile, err)
@ -376,28 +382,25 @@ func (p *Provisioner) deployConfigFiles(
o terraform.UIOutput, o terraform.UIOutput,
comm communicator.Communicator, comm communicator.Communicator,
confDir string) error { confDir string) error {
// Open the validation key file contents, _, err := pathorcontents.Read(p.ValidationKey)
f, err := os.Open(p.ValidationKeyPath)
if err != nil { if err != nil {
return err return err
} }
defer f.Close() f := strings.NewReader(contents)
// Copy the validation key to the new instance // 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) return fmt.Errorf("Uploading %s failed: %v", validationKey, err)
} }
if p.SecretKeyPath != "" { if p.SecretKey != "" {
// Open the secret key file contents, _, err := pathorcontents.Read(p.SecretKey)
s, err := os.Open(p.SecretKeyPath)
if err != nil { if err != nil {
return err return err
} }
defer s.Close() s := strings.NewReader(contents)
// Copy the secret key to the new instance // 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) 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 // 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) 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 // 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) return fmt.Errorf("Uploading %s failed: %v", firstBoot, err)
} }
@ -466,8 +469,8 @@ func (p *Provisioner) deployOhaiHints(
defer f.Close() defer f.Close()
// Copy the hint to the new instance // Copy the hint to the new instance
if err := comm.Upload(path.Join(hintDir, path.Base(hint)), f); err != nil { if err := comm.Upload(filepath.Join(hintDir, filepath.Base(hint)), f); err != nil {
return fmt.Errorf("Uploading %s failed: %v", path.Base(hint), err) return fmt.Errorf("Uploading %s failed: %v", filepath.Base(hint), err)
} }
} }

View File

@ -22,7 +22,7 @@ func TestResourceProvider_Validate_good(t *testing.T) {
"run_list": []interface{}{"cookbook::recipe"}, "run_list": []interface{}{"cookbook::recipe"},
"server_url": "https://chef.local", "server_url": "https://chef.local",
"validation_client_name": "validator", "validation_client_name": "validator",
"validation_key_path": "validator.pem", "validation_key": "contentsofsomevalidator.pem",
}) })
r := new(ResourceProvisioner) r := new(ResourceProvisioner)
warn, errs := r.Validate(c) warn, errs := r.Validate(c)

View File

@ -36,10 +36,10 @@ resource "aws_instance" "web" {
environment = "_default" environment = "_default"
run_list = ["cookbook::recipe"] run_list = ["cookbook::recipe"]
node_name = "webserver1" 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" server_url = "https://chef.company.com/organizations/org1"
validation_client_name = "chef-validator" validation_client_name = "chef-validator"
validation_key_path = "../chef-validator.pem" validation_key = "${file("../chef-validator.pem")}"
version = "12.4.1" 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 Chef Client run. The run-list will also be saved to the Chef Server after a successful
initial run. 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 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 * `server_url (string)` - (Required) The URL to the Chef server. This includes the path to
the organization. See the example. 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 * `validation_client_name (string)` - (Required) The name of the validation client to use
for the initial communication with the Chef Server. 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 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. * `version (string)` - (Optional) The Chef Client version to install on the remote machine.
If not set the latest available version will be installed. If not set the latest available version will be installed.
These 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__.