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:
parent
b6aed3fec3
commit
73ce6d184a
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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__.
|
||||||
|
|
Loading…
Reference in New Issue