Refactored quite a few things after review...
Also renamed the provisioner to just `chef` as it’s out intention to end up with one provisioner for all types of `chef` clients.
This commit is contained in:
parent
d4150d5b1a
commit
c19d92fb67
|
@ -1,7 +1,7 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/hashicorp/terraform/builtin/provisioners/chef-client"
|
"github.com/hashicorp/terraform/builtin/provisioners/chef"
|
||||||
"github.com/hashicorp/terraform/plugin"
|
"github.com/hashicorp/terraform/plugin"
|
||||||
"github.com/hashicorp/terraform/terraform"
|
"github.com/hashicorp/terraform/terraform"
|
||||||
)
|
)
|
||||||
|
@ -9,7 +9,7 @@ import (
|
||||||
func main() {
|
func main() {
|
||||||
plugin.Serve(&plugin.ServeOpts{
|
plugin.Serve(&plugin.ServeOpts{
|
||||||
ProvisionerFunc: func() terraform.ResourceProvisioner {
|
ProvisionerFunc: func() terraform.ResourceProvisioner {
|
||||||
return new(chefclient.ResourceProvisioner)
|
return new(chef.ResourceProvisioner)
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
package chefclient
|
package chef
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
@ -22,9 +22,12 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
clienrb = "client.rb"
|
||||||
|
defaultChefEnv = "_default"
|
||||||
firstBoot = "first-boot.json"
|
firstBoot = "first-boot.json"
|
||||||
logfileDir = "logfiles"
|
logfileDir = "logfiles"
|
||||||
linuxConfDir = "/etc/chef"
|
linuxConfDir = "/etc/chef"
|
||||||
|
validationKey = "validation.pem"
|
||||||
windowsConfDir = "C:/chef"
|
windowsConfDir = "C:/chef"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -52,7 +55,7 @@ ENV['HTTPS_PROXY'] = "{{ .HTTPSProxy }}"
|
||||||
|
|
||||||
// Provisioner represents a specificly configured chef provisioner
|
// Provisioner represents a specificly configured chef provisioner
|
||||||
type Provisioner struct {
|
type Provisioner struct {
|
||||||
Attributes interface{} `mapstructure:"-"`
|
Attributes interface{} `mapstructure:"attributes"`
|
||||||
Environment string `mapstructure:"environment"`
|
Environment string `mapstructure:"environment"`
|
||||||
LogToFile bool `mapstructure:"log_to_file"`
|
LogToFile bool `mapstructure:"log_to_file"`
|
||||||
HTTPProxy string `mapstructure:"http_proxy"`
|
HTTPProxy string `mapstructure:"http_proxy"`
|
||||||
|
@ -169,6 +172,7 @@ func (r *ResourceProvisioner) decodeConfig(c *terraform.ResourceConfig) (*Provis
|
||||||
p := new(Provisioner)
|
p := new(Provisioner)
|
||||||
|
|
||||||
decConf := &mapstructure.DecoderConfig{
|
decConf := &mapstructure.DecoderConfig{
|
||||||
|
ErrorUnused: true,
|
||||||
WeaklyTypedInput: true,
|
WeaklyTypedInput: true,
|
||||||
Result: p,
|
Result: p,
|
||||||
}
|
}
|
||||||
|
@ -182,7 +186,7 @@ func (r *ResourceProvisioner) decodeConfig(c *terraform.ResourceConfig) (*Provis
|
||||||
}
|
}
|
||||||
|
|
||||||
if p.Environment == "" {
|
if p.Environment == "" {
|
||||||
p.Environment = "_default"
|
p.Environment = defaultChefEnv
|
||||||
}
|
}
|
||||||
|
|
||||||
if attrs, ok := c.Raw["attributes"]; ok {
|
if attrs, ok := c.Raw["attributes"]; ok {
|
||||||
|
@ -241,7 +245,7 @@ func (p *Provisioner) runChefClientFunc(
|
||||||
cmd := fmt.Sprintf("chef-client -j %q -E %q", fb, p.Environment)
|
cmd := fmt.Sprintf("chef-client -j %q -E %q", fb, p.Environment)
|
||||||
|
|
||||||
if p.LogToFile {
|
if p.LogToFile {
|
||||||
if err := os.MkdirAll(logfileDir, 0777); err != nil {
|
if err := os.MkdirAll(logfileDir, 0755); err != nil {
|
||||||
return fmt.Errorf("Error creating logfile directory %s: %v", logfileDir, err)
|
return fmt.Errorf("Error creating logfile directory %s: %v", logfileDir, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -290,16 +294,16 @@ 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 .pem file
|
// Open the validation key file
|
||||||
f, err := os.Open(p.ValidationKeyPath)
|
f, err := os.Open(p.ValidationKeyPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer f.Close()
|
defer f.Close()
|
||||||
|
|
||||||
// Copy the validation .pem to the new instance
|
// Copy the validation key to the new instance
|
||||||
if err := comm.Upload(path.Join(confDir, "validation.pem"), f); err != nil {
|
if err := comm.Upload(path.Join(confDir, validationKey), f); err != nil {
|
||||||
return fmt.Errorf("Uploading validation.pem failed: %v", err)
|
return fmt.Errorf("Uploading %s failed: %v", validationKey, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make strings.Join available for use within the template
|
// Make strings.Join available for use within the template
|
||||||
|
@ -307,18 +311,18 @@ func (p *Provisioner) deployConfigFiles(
|
||||||
"join": strings.Join,
|
"join": strings.Join,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a new template and parse the client.rb into it
|
// Create a new template and parse the client config into it
|
||||||
t := template.Must(template.New("client.rb").Funcs(funcMap).Parse(clientConf))
|
t := template.Must(template.New(clienrb).Funcs(funcMap).Parse(clientConf))
|
||||||
|
|
||||||
var buf bytes.Buffer
|
var buf bytes.Buffer
|
||||||
err = t.Execute(&buf, p)
|
err = t.Execute(&buf, p)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("Error executing client.rb template: %s", err)
|
return fmt.Errorf("Error executing %s template: %s", clienrb, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Copy the client.rb to the new instance
|
// Copy the client config to the new instance
|
||||||
if err := comm.Upload(path.Join(confDir, "client.rb"), &buf); err != nil {
|
if err := comm.Upload(path.Join(confDir, clienrb), &buf); err != nil {
|
||||||
return fmt.Errorf("Uploading client.rb failed: %v", err)
|
return fmt.Errorf("Uploading %s failed: %v", clienrb, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a map with first boot settings
|
// Create a map with first boot settings
|
||||||
|
@ -327,6 +331,13 @@ func (p *Provisioner) deployConfigFiles(
|
||||||
fb = p.Attributes.(map[string]interface{})
|
fb = p.Attributes.(map[string]interface{})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check if the run_list was also in the attributes and if so log a warning
|
||||||
|
// that it will be overwritten with the value of the run_list argument.
|
||||||
|
if _, found := fb["run_list"]; found {
|
||||||
|
log.Printf("[WARNING] Found a 'run_list' specified in the configured attributes! " +
|
||||||
|
"This value will be overwritten by the value of the `run_list` argument!")
|
||||||
|
}
|
||||||
|
|
||||||
// Add the initial runlist to the first boot settings
|
// Add the initial runlist to the first boot settings
|
||||||
fb["run_list"] = p.RunList
|
fb["run_list"] = p.RunList
|
||||||
|
|
||||||
|
@ -338,7 +349,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(path.Join(confDir, firstBoot), bytes.NewReader(d)); err != nil {
|
||||||
return fmt.Errorf("Uploading first-boot.json failed: %v", err)
|
return fmt.Errorf("Uploading %s failed: %v", firstBoot, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
@ -369,6 +380,7 @@ func (p *Provisioner) runCommand(
|
||||||
Stderr: errW,
|
Stderr: errW,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
log.Printf("[DEBUG] Executing remote command: %q", cmd.Command)
|
||||||
if err := comm.Start(cmd); err != nil {
|
if err := comm.Start(cmd); err != nil {
|
||||||
return fmt.Errorf("Error executing command %q: %v", cmd.Command, err)
|
return fmt.Errorf("Error executing command %q: %v", cmd.Command, err)
|
||||||
}
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
package chefclient
|
package chef
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
|
@ -1,42 +1,44 @@
|
||||||
package chefclient
|
package chef
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/hashicorp/terraform/communicator"
|
"github.com/hashicorp/terraform/communicator"
|
||||||
"github.com/hashicorp/terraform/terraform"
|
"github.com/hashicorp/terraform/terraform"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
installURL = "https://www.chef.io/chef/install.sh"
|
||||||
|
)
|
||||||
|
|
||||||
func (p *Provisioner) sshInstallChefClient(
|
func (p *Provisioner) sshInstallChefClient(
|
||||||
o terraform.UIOutput,
|
o terraform.UIOutput,
|
||||||
comm communicator.Communicator) error {
|
comm communicator.Communicator) error {
|
||||||
var installCmd bytes.Buffer
|
|
||||||
|
|
||||||
// Build up a single command based on the given config options
|
// Build up the command prefix
|
||||||
installCmd.WriteString("curl")
|
prefix := ""
|
||||||
if p.HTTPProxy != "" {
|
if p.HTTPProxy != "" {
|
||||||
installCmd.WriteString(" --proxy " + p.HTTPProxy)
|
prefix += fmt.Sprintf("proxy_http='%s' ", p.HTTPProxy)
|
||||||
}
|
}
|
||||||
if p.NOProxy != nil {
|
if p.NOProxy != nil {
|
||||||
installCmd.WriteString(" --noproxy " + strings.Join(p.NOProxy, ","))
|
prefix += fmt.Sprintf("no_proxy='%s' ", strings.Join(p.NOProxy, ","))
|
||||||
}
|
}
|
||||||
installCmd.WriteString(" -LO https://www.chef.io/chef/install.sh 2>/dev/null &&")
|
|
||||||
if !p.PreventSudo {
|
|
||||||
installCmd.WriteString(" sudo")
|
|
||||||
}
|
|
||||||
installCmd.WriteString(" bash ./install.sh")
|
|
||||||
if p.Version != "" {
|
|
||||||
installCmd.WriteString(" -v " + p.Version)
|
|
||||||
}
|
|
||||||
installCmd.WriteString(" &&")
|
|
||||||
if !p.PreventSudo {
|
|
||||||
installCmd.WriteString(" sudo")
|
|
||||||
}
|
|
||||||
installCmd.WriteString(" rm -f install.sh")
|
|
||||||
|
|
||||||
// Execute the command to install Chef Client
|
// First download the install.sh script from Chef
|
||||||
return p.runCommand(o, comm, installCmd.String())
|
err := p.runCommand(o, comm, fmt.Sprintf("%scurl -LO %s", prefix, installURL))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Then execute the install.sh scrip to download and install Chef Client
|
||||||
|
err = p.runCommand(o, comm, fmt.Sprintf("%sbash ./install.sh -v %s", prefix, p.Version))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// And finally cleanup the install.sh script again
|
||||||
|
return p.runCommand(o, comm, fmt.Sprintf("%srm -f install.sh", prefix))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Provisioner) sshCreateConfigFiles(
|
func (p *Provisioner) sshCreateConfigFiles(
|
|
@ -1,4 +1,4 @@
|
||||||
package chefclient
|
package chef
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
@ -22,8 +22,9 @@ func TestResourceProvider_sshInstallChefClient(t *testing.T) {
|
||||||
}),
|
}),
|
||||||
|
|
||||||
Commands: map[string]bool{
|
Commands: map[string]bool{
|
||||||
"sudo curl -LO https://www.chef.io/chef/install.sh 2>/dev/null && " +
|
"sudo curl -LO https://www.chef.io/chef/install.sh": true,
|
||||||
"sudo bash ./install.sh && sudo rm -f install.sh": true,
|
"sudo bash ./install.sh -v ": true,
|
||||||
|
"sudo rm -f install.sh": true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -38,8 +39,9 @@ func TestResourceProvider_sshInstallChefClient(t *testing.T) {
|
||||||
}),
|
}),
|
||||||
|
|
||||||
Commands: map[string]bool{
|
Commands: map[string]bool{
|
||||||
"curl -LO https://www.chef.io/chef/install.sh 2>/dev/null && " +
|
"curl -LO https://www.chef.io/chef/install.sh": true,
|
||||||
"bash ./install.sh && rm -f install.sh": true,
|
"bash ./install.sh -v ": true,
|
||||||
|
"rm -f install.sh": true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -55,8 +57,9 @@ func TestResourceProvider_sshInstallChefClient(t *testing.T) {
|
||||||
}),
|
}),
|
||||||
|
|
||||||
Commands: map[string]bool{
|
Commands: map[string]bool{
|
||||||
"curl --proxy http://proxy.local -LO https://www.chef.io/chef/install.sh 2>/dev/null && " +
|
"proxy_http='http://proxy.local' curl -LO https://www.chef.io/chef/install.sh": true,
|
||||||
"bash ./install.sh && rm -f install.sh": true,
|
"proxy_http='http://proxy.local' bash ./install.sh -v ": true,
|
||||||
|
"proxy_http='http://proxy.local' rm -f install.sh": true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -73,8 +76,11 @@ func TestResourceProvider_sshInstallChefClient(t *testing.T) {
|
||||||
}),
|
}),
|
||||||
|
|
||||||
Commands: map[string]bool{
|
Commands: map[string]bool{
|
||||||
"curl --proxy http://proxy.local --noproxy http://local.local,http://local.org -LO " +
|
"proxy_http='http://proxy.local' no_proxy='http://local.local,http://local.org' " +
|
||||||
"https://www.chef.io/chef/install.sh 2>/dev/null && bash ./install.sh && " +
|
"curl -LO https://www.chef.io/chef/install.sh": true,
|
||||||
|
"proxy_http='http://proxy.local' no_proxy='http://local.local,http://local.org' " +
|
||||||
|
"bash ./install.sh -v ": true,
|
||||||
|
"proxy_http='http://proxy.local' no_proxy='http://local.local,http://local.org' " +
|
||||||
"rm -f install.sh": true,
|
"rm -f install.sh": true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -91,8 +97,9 @@ func TestResourceProvider_sshInstallChefClient(t *testing.T) {
|
||||||
}),
|
}),
|
||||||
|
|
||||||
Commands: map[string]bool{
|
Commands: map[string]bool{
|
||||||
"curl -LO https://www.chef.io/chef/install.sh 2>/dev/null && " +
|
"curl -LO https://www.chef.io/chef/install.sh": true,
|
||||||
"bash ./install.sh -v 11.18.6 && rm -f install.sh": true,
|
"bash ./install.sh -v 11.18.6": true,
|
||||||
|
"rm -f install.sh": true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
package chefclient
|
package chef
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
|
@ -1,4 +1,4 @@
|
||||||
package chefclient
|
package chef
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
|
@ -1,16 +1,22 @@
|
||||||
---
|
---
|
||||||
layout: "docs"
|
layout: "docs"
|
||||||
page_title: "Provisioner: chef-client"
|
page_title: "Provisioner: chef"
|
||||||
sidebar_current: "docs-provisioners-chef-client"
|
sidebar_current: "docs-provisioners-chef"
|
||||||
description: |-
|
description: |-
|
||||||
The `chef-client` provisioner invokes a Chef Client run on a remote resource after first installing and configuring Chef Client on the remote resource. The `chef-client` provisioner supports both `ssh` and `winrm` type connections.
|
The `chef` provisioner invokes a Chef Client run on a remote resource after first installing and configuring Chef Client on the remote resource. The `chef` provisioner supports both `ssh` and `winrm` type connections.
|
||||||
---
|
---
|
||||||
|
|
||||||
# chef Provisioner
|
# Chef Provisioner
|
||||||
|
|
||||||
The `chef-client` provisioner invokes a Chef Client run on a remote resource after first
|
The `chef` provisioner invokes a Chef Client run on a remote resource after first installing
|
||||||
installing and configuring Chef Client on the remote resource. The `chef-client` provisioner
|
and configuring Chef Client on the remote resource. The `chef` provisioner supports both `ssh`
|
||||||
supports both `ssh` and `winrm` type [connections](/docs/provisioners/connection.html).
|
and `winrm` type [connections](/docs/provisioners/connection.html).
|
||||||
|
|
||||||
|
## Requirements
|
||||||
|
|
||||||
|
In order for the `chef` provisioner to work properly, you need either `cURL` (when using
|
||||||
|
a `ssh` type connection) or `PowerShell 2.0` (when using a `winrm` type connection) to be
|
||||||
|
available on the target machine.
|
||||||
|
|
||||||
## Example usage
|
## Example usage
|
||||||
|
|
||||||
|
@ -18,7 +24,7 @@ supports both `ssh` and `winrm` type [connections](/docs/provisioners/connection
|
||||||
# Start a initial chef run on a resource
|
# Start a initial chef run on a resource
|
||||||
resource "aws_instance" "web" {
|
resource "aws_instance" "web" {
|
||||||
...
|
...
|
||||||
provisioner "chef-client" {
|
provisioner "chef" {
|
||||||
attributes {
|
attributes {
|
||||||
"key" = "value"
|
"key" = "value"
|
||||||
"app" {
|
"app" {
|
||||||
|
@ -73,7 +79,7 @@ The following arguments are supported:
|
||||||
the organization. See the example.
|
the organization. See the example.
|
||||||
|
|
||||||
* `skip_install (boolean)` - (Optional) Skip the installation of Chef Client on the remote
|
* `skip_install (boolean)` - (Optional) Skip the installation of Chef Client on the remote
|
||||||
machine. This assumes Chef Client is already installed when you run the `chef-client`
|
machine. This assumes Chef Client is already installed when you run the `chef`
|
||||||
provisioner.
|
provisioner.
|
||||||
|
|
||||||
* `ssl_verify_mode (string)` - (Optional) Use to set the verify mode for Chef Client HTTPS
|
* `ssl_verify_mode (string)` - (Optional) Use to set the verify mode for Chef Client HTTPS
|
|
@ -178,9 +178,9 @@
|
||||||
<li<%= sidebar_current("docs-provisioners") %>>
|
<li<%= sidebar_current("docs-provisioners") %>>
|
||||||
<a href="/docs/provisioners/index.html">Provisioners</a>
|
<a href="/docs/provisioners/index.html">Provisioners</a>
|
||||||
<ul class="nav">
|
<ul class="nav">
|
||||||
<li<%= sidebar_current("docs-provisioners-chef-client") %>>
|
<li<%= sidebar_current("docs-provisioners-chef") %>>
|
||||||
+ <a href="/docs/provisioners/chef-client.html">chef-client</a>
|
<a href="/docs/provisioners/chef.html">chef</a>
|
||||||
+ </li>
|
</li>
|
||||||
|
|
||||||
<li<%= sidebar_current("docs-provisioners-connection") %>>
|
<li<%= sidebar_current("docs-provisioners-connection") %>>
|
||||||
<a href="/docs/provisioners/connection.html">connection</a>
|
<a href="/docs/provisioners/connection.html">connection</a>
|
||||||
|
|
Loading…
Reference in New Issue