Extract converge provisioner

This commit is contained in:
Wilken Rivera 2021-04-21 14:19:34 -04:00
parent 4be2c350bf
commit bb511e9592
11 changed files with 12 additions and 606 deletions

View File

@ -42,7 +42,6 @@ import (
yandeximportpostprocessor "github.com/hashicorp/packer/post-processor/yandex-import"
azuredtlartifactprovisioner "github.com/hashicorp/packer/provisioner/azure-dtlartifact"
breakpointprovisioner "github.com/hashicorp/packer/provisioner/breakpoint"
convergeprovisioner "github.com/hashicorp/packer/provisioner/converge"
fileprovisioner "github.com/hashicorp/packer/provisioner/file"
inspecprovisioner "github.com/hashicorp/packer/provisioner/inspec"
powershellprovisioner "github.com/hashicorp/packer/provisioner/powershell"
@ -81,7 +80,6 @@ var Builders = map[string]packersdk.Builder{
var Provisioners = map[string]packersdk.Provisioner{
"azure-dtlartifact": new(azuredtlartifactprovisioner.Provisioner),
"breakpoint": new(breakpointprovisioner.Provisioner),
"converge": new(convergeprovisioner.Provisioner),
"file": new(fileprovisioner.Provisioner),
"inspec": new(inspecprovisioner.Provisioner),
"powershell": new(powershellprovisioner.Provisioner),

View File

@ -1,6 +1,7 @@
package command
import (
convergeprovisioner "github.com/hashicorp/packer-plugin-converge/provisioner/converge"
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
// Previously core-bundled components, split into their own plugins but
@ -116,6 +117,7 @@ var VendoredProvisioners = map[string]packersdk.Provisioner{
"ansible-local": new(ansiblelocalprovisioner.Provisioner),
"chef-client": new(chefclientprovisioner.Provisioner),
"chef-solo": new(chefsoloprovisioner.Provisioner),
"converge": new(convergeprovisioner.Provisioner),
"puppet-masterless": new(puppetmasterlessprovisioner.Provisioner),
"puppet-server": new(puppetserverprovisioner.Provisioner),
}

1
go.mod
View File

@ -42,6 +42,7 @@ require (
github.com/hashicorp/packer-plugin-ansible v0.0.2
github.com/hashicorp/packer-plugin-chef v0.0.1
github.com/hashicorp/packer-plugin-cloudstack v0.0.1
github.com/hashicorp/packer-plugin-converge v0.0.1
github.com/hashicorp/packer-plugin-docker v0.0.7
github.com/hashicorp/packer-plugin-googlecompute v0.0.1
github.com/hashicorp/packer-plugin-hyperone v0.0.1

2
go.sum
View File

@ -499,6 +499,8 @@ github.com/hashicorp/packer-plugin-chef v0.0.1 h1:1zQwnnvftwg9PJyWjMfHfDyzfWDdb0
github.com/hashicorp/packer-plugin-chef v0.0.1/go.mod h1:4iSyWfvrb4QwUDZqJ3iCb+kIsnDwOTL1yTEDXBtk3Ew=
github.com/hashicorp/packer-plugin-cloudstack v0.0.1 h1:BF9nXRlA0xQV5W/+CoLjWn0aLO60gTbsxnLi/o37ktc=
github.com/hashicorp/packer-plugin-cloudstack v0.0.1/go.mod h1:fx13TY2szz6cm2e99xzU3gQzKdGVwysxY2TyKr0r8MQ=
github.com/hashicorp/packer-plugin-converge v0.0.1 h1:cjrNt2Q/BuSH2o2bpNV91DhWYSTN7vb4LwxwFXULcok=
github.com/hashicorp/packer-plugin-converge v0.0.1/go.mod h1:3Rm0fAiVwFriSRrwt3dsuKInYYzuOa6tqPFGMOW7noI=
github.com/hashicorp/packer-plugin-docker v0.0.7 h1:hMTrH7vrkFIjphtbbtpuzffTzSjMNgxayo2DPLz9y+c=
github.com/hashicorp/packer-plugin-docker v0.0.7/go.mod h1:IpeKlwOSy2kdgQcysqd3gCsoqjME9jtmpFoKxn7RRNI=
github.com/hashicorp/packer-plugin-googlecompute v0.0.1 h1:Shjio88MraB+ocj0VI5+M65r4UBKbYI4eCqLNyPXKEo=

View File

@ -1,241 +0,0 @@
//go:generate packer-sdc mapstructure-to-hcl2 -type Config,ModuleDir
// This package implements a provisioner for Packer that executes
// Converge to provision a remote machine
package converge
import (
"bytes"
"context"
"errors"
"fmt"
"strings"
"encoding/json"
"github.com/hashicorp/hcl/v2/hcldec"
"github.com/hashicorp/packer-plugin-sdk/common"
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
"github.com/hashicorp/packer-plugin-sdk/template/config"
"github.com/hashicorp/packer-plugin-sdk/template/interpolate"
)
// Config for Converge provisioner
type Config struct {
common.PackerConfig `mapstructure:",squash"`
// Bootstrapping
Bootstrap bool `mapstructure:"bootstrap"`
Version string `mapstructure:"version"`
BootstrapCommand string `mapstructure:"bootstrap_command"`
PreventBootstrapSudo bool `mapstructure:"prevent_bootstrap_sudo"`
// Modules
ModuleDirs []ModuleDir `mapstructure:"module_dirs"`
// Execution
Module string `mapstructure:"module"`
WorkingDirectory string `mapstructure:"working_directory"`
Params map[string]string `mapstructure:"params"`
ExecuteCommand string `mapstructure:"execute_command"`
PreventSudo bool `mapstructure:"prevent_sudo"`
ctx interpolate.Context
}
// ModuleDir is a directory to transfer to the remote system
type ModuleDir struct {
Source string `mapstructure:"source"`
Destination string `mapstructure:"destination"`
Exclude []string `mapstructure:"exclude"`
}
// Provisioner for Converge
type Provisioner struct {
config Config
}
func (p *Provisioner) ConfigSpec() hcldec.ObjectSpec { return p.config.FlatMapstructure().HCL2Spec() }
func (p *Provisioner) Prepare(raws ...interface{}) error {
err := config.Decode(
&p.config,
&config.DecodeOpts{
PluginType: "converge",
Interpolate: true,
InterpolateContext: &p.config.ctx,
InterpolateFilter: &interpolate.RenderFilter{
Exclude: []string{
"execute_command",
"bootstrap_command",
},
},
},
raws...,
)
if err != nil {
return err
}
// require a single module
if p.config.Module == "" {
return errors.New("Converge requires a module to provision the system")
}
// set defaults
if p.config.WorkingDirectory == "" {
p.config.WorkingDirectory = "/tmp"
}
if p.config.ExecuteCommand == "" {
p.config.ExecuteCommand = "cd {{.WorkingDirectory}} && {{if .Sudo}}sudo {{end}}converge apply --local --log-level=WARNING --paramsJSON '{{.ParamsJSON}}' {{.Module}}"
}
if p.config.BootstrapCommand == "" {
p.config.BootstrapCommand = "curl -s https://get.converge.sh | {{if .Sudo}}sudo {{end}}sh {{if ne .Version \"\"}}-s -- -v {{.Version}}{{end}}"
}
// validate sources and destinations
for i, dir := range p.config.ModuleDirs {
if dir.Source == "" {
return fmt.Errorf("Source (\"source\" key) is required in Converge module dir #%d", i)
}
if dir.Destination == "" {
return fmt.Errorf("Destination (\"destination\" key) is required in Converge module dir #%d", i)
}
}
return err
}
// Provision node somehow. TODO: actual docs
func (p *Provisioner) Provision(ctx context.Context, ui packersdk.Ui, comm packersdk.Communicator, _ map[string]interface{}) error {
ui.Say("Provisioning with Converge")
// bootstrapping
if err := p.maybeBootstrap(ui, comm); err != nil {
return err // error messages are already user-friendly
}
// send module directories to the remote host
if err := p.sendModuleDirectories(ui, comm); err != nil {
return err // error messages are already user-friendly
}
// apply all the modules
if err := p.applyModules(ui, comm); err != nil {
return err // error messages are already user-friendly
}
return nil
}
func (p *Provisioner) maybeBootstrap(ui packersdk.Ui, comm packersdk.Communicator) error {
ctx := context.TODO()
if !p.config.Bootstrap {
return nil
}
ui.Message("bootstrapping converge")
p.config.ctx.Data = struct {
Version string
Sudo bool
}{
Version: p.config.Version,
Sudo: !p.config.PreventBootstrapSudo,
}
command, err := interpolate.Render(p.config.BootstrapCommand, &p.config.ctx)
if err != nil {
return fmt.Errorf("Could not interpolate bootstrap command: %s", err)
}
var out, outErr bytes.Buffer
cmd := &packersdk.RemoteCmd{
Command: command,
Stdin: nil,
Stdout: &out,
Stderr: &outErr,
}
if err = comm.Start(ctx, cmd); err != nil {
return fmt.Errorf("Error bootstrapping converge: %s", err)
}
cmd.Wait()
if cmd.ExitStatus() != 0 {
ui.Error(out.String())
ui.Error(outErr.String())
return errors.New("Error bootstrapping converge")
}
ui.Message(strings.TrimSpace(out.String()))
return nil
}
func (p *Provisioner) sendModuleDirectories(ui packersdk.Ui, comm packersdk.Communicator) error {
for _, dir := range p.config.ModuleDirs {
if err := comm.UploadDir(dir.Destination, dir.Source, dir.Exclude); err != nil {
return fmt.Errorf("Could not upload %q: %s", dir.Source, err)
}
ui.Message(fmt.Sprintf("transferred %q to %q", dir.Source, dir.Destination))
}
return nil
}
func (p *Provisioner) applyModules(ui packersdk.Ui, comm packersdk.Communicator) error {
ctx := context.TODO()
// create params JSON file
params, err := json.Marshal(p.config.Params)
if err != nil {
return fmt.Errorf("Could not marshal parameters as JSON: %s", err)
}
p.config.ctx.Data = struct {
ParamsJSON, WorkingDirectory, Module string
Sudo bool
}{
ParamsJSON: string(params),
WorkingDirectory: p.config.WorkingDirectory,
Module: p.config.Module,
Sudo: !p.config.PreventSudo,
}
command, err := interpolate.Render(p.config.ExecuteCommand, &p.config.ctx)
if err != nil {
return fmt.Errorf("Could not interpolate execute command: %s", err)
}
// run Converge in the specified directory
var runOut, runErr bytes.Buffer
cmd := &packersdk.RemoteCmd{
Command: command,
Stdin: nil,
Stdout: &runOut,
Stderr: &runErr,
}
if err := comm.Start(ctx, cmd); err != nil {
return fmt.Errorf("Error applying %q: %s", p.config.Module, err)
}
cmd.Wait()
if cmd.ExitStatus() == 127 {
ui.Error("Could not find Converge. Is it installed and in PATH?")
if !p.config.Bootstrap {
ui.Error("Bootstrapping was disabled for this run. That might be why Converge isn't present.")
}
return errors.New("Could not find Converge")
} else if cmd.ExitStatus() != 0 {
ui.Error(strings.TrimSpace(runOut.String()))
ui.Error(strings.TrimSpace(runErr.String()))
ui.Error(fmt.Sprintf("Exited with error code %d.", cmd.ExitStatus()))
return fmt.Errorf("Error applying %q", p.config.Module)
}
ui.Message(strings.TrimSpace(runOut.String()))
return nil
}

View File

@ -1,92 +0,0 @@
// Code generated by "packer-sdc mapstructure-to-hcl2"; DO NOT EDIT.
package converge
import (
"github.com/hashicorp/hcl/v2/hcldec"
"github.com/zclconf/go-cty/cty"
)
// FlatConfig is an auto-generated flat version of Config.
// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up.
type FlatConfig struct {
PackerBuildName *string `mapstructure:"packer_build_name" cty:"packer_build_name" hcl:"packer_build_name"`
PackerBuilderType *string `mapstructure:"packer_builder_type" cty:"packer_builder_type" hcl:"packer_builder_type"`
PackerCoreVersion *string `mapstructure:"packer_core_version" cty:"packer_core_version" hcl:"packer_core_version"`
PackerDebug *bool `mapstructure:"packer_debug" cty:"packer_debug" hcl:"packer_debug"`
PackerForce *bool `mapstructure:"packer_force" cty:"packer_force" hcl:"packer_force"`
PackerOnError *string `mapstructure:"packer_on_error" cty:"packer_on_error" hcl:"packer_on_error"`
PackerUserVars map[string]string `mapstructure:"packer_user_variables" cty:"packer_user_variables" hcl:"packer_user_variables"`
PackerSensitiveVars []string `mapstructure:"packer_sensitive_variables" cty:"packer_sensitive_variables" hcl:"packer_sensitive_variables"`
Bootstrap *bool `mapstructure:"bootstrap" cty:"bootstrap" hcl:"bootstrap"`
Version *string `mapstructure:"version" cty:"version" hcl:"version"`
BootstrapCommand *string `mapstructure:"bootstrap_command" cty:"bootstrap_command" hcl:"bootstrap_command"`
PreventBootstrapSudo *bool `mapstructure:"prevent_bootstrap_sudo" cty:"prevent_bootstrap_sudo" hcl:"prevent_bootstrap_sudo"`
ModuleDirs []FlatModuleDir `mapstructure:"module_dirs" cty:"module_dirs" hcl:"module_dirs"`
Module *string `mapstructure:"module" cty:"module" hcl:"module"`
WorkingDirectory *string `mapstructure:"working_directory" cty:"working_directory" hcl:"working_directory"`
Params map[string]string `mapstructure:"params" cty:"params" hcl:"params"`
ExecuteCommand *string `mapstructure:"execute_command" cty:"execute_command" hcl:"execute_command"`
PreventSudo *bool `mapstructure:"prevent_sudo" cty:"prevent_sudo" hcl:"prevent_sudo"`
}
// FlatMapstructure returns a new FlatConfig.
// FlatConfig is an auto-generated flat version of Config.
// Where the contents a fields with a `mapstructure:,squash` tag are bubbled up.
func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } {
return new(FlatConfig)
}
// HCL2Spec returns the hcl spec of a Config.
// This spec is used by HCL to read the fields of Config.
// The decoded values from this spec will then be applied to a FlatConfig.
func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
s := map[string]hcldec.Spec{
"packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false},
"packer_builder_type": &hcldec.AttrSpec{Name: "packer_builder_type", Type: cty.String, Required: false},
"packer_core_version": &hcldec.AttrSpec{Name: "packer_core_version", Type: cty.String, Required: false},
"packer_debug": &hcldec.AttrSpec{Name: "packer_debug", Type: cty.Bool, Required: false},
"packer_force": &hcldec.AttrSpec{Name: "packer_force", Type: cty.Bool, Required: false},
"packer_on_error": &hcldec.AttrSpec{Name: "packer_on_error", Type: cty.String, Required: false},
"packer_user_variables": &hcldec.AttrSpec{Name: "packer_user_variables", Type: cty.Map(cty.String), Required: false},
"packer_sensitive_variables": &hcldec.AttrSpec{Name: "packer_sensitive_variables", Type: cty.List(cty.String), Required: false},
"bootstrap": &hcldec.AttrSpec{Name: "bootstrap", Type: cty.Bool, Required: false},
"version": &hcldec.AttrSpec{Name: "version", Type: cty.String, Required: false},
"bootstrap_command": &hcldec.AttrSpec{Name: "bootstrap_command", Type: cty.String, Required: false},
"prevent_bootstrap_sudo": &hcldec.AttrSpec{Name: "prevent_bootstrap_sudo", Type: cty.Bool, Required: false},
"module_dirs": &hcldec.BlockListSpec{TypeName: "module_dirs", Nested: hcldec.ObjectSpec((*FlatModuleDir)(nil).HCL2Spec())},
"module": &hcldec.AttrSpec{Name: "module", Type: cty.String, Required: false},
"working_directory": &hcldec.AttrSpec{Name: "working_directory", Type: cty.String, Required: false},
"params": &hcldec.AttrSpec{Name: "params", Type: cty.Map(cty.String), Required: false},
"execute_command": &hcldec.AttrSpec{Name: "execute_command", Type: cty.String, Required: false},
"prevent_sudo": &hcldec.AttrSpec{Name: "prevent_sudo", Type: cty.Bool, Required: false},
}
return s
}
// FlatModuleDir is an auto-generated flat version of ModuleDir.
// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up.
type FlatModuleDir struct {
Source *string `mapstructure:"source" cty:"source" hcl:"source"`
Destination *string `mapstructure:"destination" cty:"destination" hcl:"destination"`
Exclude []string `mapstructure:"exclude" cty:"exclude" hcl:"exclude"`
}
// FlatMapstructure returns a new FlatModuleDir.
// FlatModuleDir is an auto-generated flat version of ModuleDir.
// Where the contents a fields with a `mapstructure:,squash` tag are bubbled up.
func (*ModuleDir) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } {
return new(FlatModuleDir)
}
// HCL2Spec returns the hcl spec of a ModuleDir.
// This spec is used by HCL to read the fields of ModuleDir.
// The decoded values from this spec will then be applied to a FlatModuleDir.
func (*FlatModuleDir) HCL2Spec() map[string]hcldec.Spec {
s := map[string]hcldec.Spec{
"source": &hcldec.AttrSpec{Name: "source", Type: cty.String, Required: false},
"destination": &hcldec.AttrSpec{Name: "destination", Type: cty.String, Required: false},
"exclude": &hcldec.AttrSpec{Name: "exclude", Type: cty.List(cty.String), Required: false},
}
return s
}

View File

@ -1,119 +0,0 @@
package converge
import (
"testing"
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
)
func testConfig() map[string]interface{} {
return map[string]interface{}{
"module_dirs": []map[string]interface{}{
{
"source": "from",
"destination": "/opt/converge",
},
},
"module": "/opt/converge/test.hcl",
}
}
func TestProvisioner_Impl(t *testing.T) {
var raw interface{}
raw = &Provisioner{}
if _, ok := raw.(packersdk.Provisioner); !ok {
t.Fatal("must be a Provisioner")
}
}
func TestProvisionerPrepare(t *testing.T) {
t.Run("defaults", func(t *testing.T) {
t.Run("working_directory", func(t *testing.T) {
var p Provisioner
config := testConfig()
delete(config, "working_directory")
if err := p.Prepare(config); err != nil {
t.Fatalf("err: %s", err)
}
if p.config.WorkingDirectory != "/tmp" {
t.Fatalf("unexpected module directory: %s", p.config.WorkingDirectory)
}
})
t.Run("execute_command", func(t *testing.T) {
var p Provisioner
config := testConfig()
delete(config, "execute_command")
if err := p.Prepare(config); err != nil {
t.Fatalf("err: %s", err)
}
if p.config.ExecuteCommand == "" {
t.Fatal("execute command unexpectedly blank")
}
})
t.Run("bootstrap_command", func(t *testing.T) {
var p Provisioner
config := testConfig()
delete(config, "bootstrap_command")
if err := p.Prepare(config); err != nil {
t.Fatalf("err: %s", err)
}
if p.config.BootstrapCommand == "" {
t.Fatal("bootstrap command unexpectedly blank")
}
})
})
t.Run("validate", func(t *testing.T) {
t.Run("module dir", func(t *testing.T) {
t.Run("missing source", func(t *testing.T) {
var p Provisioner
config := testConfig()
delete(config["module_dirs"].([]map[string]interface{})[0], "source")
err := p.Prepare(config)
if err == nil {
t.Error("expected error")
} else if err.Error() != "Source (\"source\" key) is required in Converge module dir #0" {
t.Errorf("bad error message: %s", err)
}
})
t.Run("missing destination", func(t *testing.T) {
var p Provisioner
config := testConfig()
delete(config["module_dirs"].([]map[string]interface{})[0], "destination")
err := p.Prepare(config)
if err == nil {
t.Error("expected error")
} else if err.Error() != "Destination (\"destination\" key) is required in Converge module dir #0" {
t.Errorf("bad error message: %s", err)
}
})
})
t.Run("no module specified", func(t *testing.T) {
var p Provisioner
config := testConfig()
delete(config, "module")
err := p.Prepare(config)
if err == nil {
t.Error("expected error")
} else if err.Error() != "Converge requires a module to provision the system" {
t.Errorf("bad error message: %s", err)
}
})
})
}

View File

@ -1,13 +0,0 @@
package version
import (
"github.com/hashicorp/packer-plugin-sdk/version"
packerVersion "github.com/hashicorp/packer/version"
)
var ConvergePluginVersion *version.PluginVersion
func init() {
ConvergePluginVersion = version.InitializePluginVersion(
packerVersion.Version, packerVersion.VersionPrerelease)
}

View File

@ -1,134 +0,0 @@
---
description: >-
The converge Packer provisioner uses Converge modules to provision the
machine.
page_title: Converge - Provisioners
---
# Converge Provisioner
@include 'provisioners/unmaintained-plugin.mdx'
Type: `converge`
The [Converge](http://converge.aster.is) Packer provisioner uses Converge
modules to provision the machine. It uploads module directories to use as
source, or you can use remote modules.
The provisioner can optionally bootstrap the Converge client/server binary onto
new images.
## Basic Example
The example below is fully functional.
```json
{
"type": "converge",
"module": "https://raw.githubusercontent.com/asteris-llc/converge/master/samples/fileContent.hcl",
"params": {
"message": "Hello, Packer!"
}
}
```
## Configuration Reference
The reference of available configuration options is listed below. The only
required element is "module". Every other option is optional.
- `module` (string) - Path (or URL) to the root module that Converge will
apply.
Optional parameters:
- `bootstrap` (boolean, defaults to false) - Set to allow the provisioner to
download the latest Converge bootstrap script and the specified `version`
of Converge from the internet.
- `version` (string) - Set to a [released Converge
version](https://github.com/asteris-llc/converge/releases) for bootstrap.
- `module_dirs` (array of directory specifications) - Module directories to
transfer to the remote host for execution. See below for the specification.
- `working_directory` (string) - The directory that Converge will change to
before execution.
- `params` (maps of string to string) - parameters to pass into the root
module.
- `execute_command` (string) - the command used to execute Converge. This is a
[configuration template variables](/docs/templates/legacy_json_templates/engine).
See below for detailed usage instructions.
- `prevent_sudo` (boolean) - stop Converge from running with administrator
privileges via sudo
- `bootstrap_command` (string) - the command used to bootstrap Converge. This
is a [template engine](/docs/templates/legacy_json_templates/engine). Therefore, you may use
user variables and template functions in this field. The following extra
variables are also avilable in this engine:
- `Version`: The version of Converge to use.
- `Sudo`: Boolean; intended to say whether to use sudo or not.
By default, this is `"curl -s https://get.converge.sh | {{if .Sudo}}sudo {{end}}sh {{if ne .Version \"\"}}-s -- -v {{.Version}}{{end}}"`
- `prevent_bootstrap_sudo` (boolean) - stop Converge from bootstrapping with
administrator privileges via sudo
@include 'provisioners/common-config.mdx'
### Module Directories
The provisioner can transfer module directories to the remote host for
provisioning. Of these fields, `source` and `destination` are required in every
directory.
- `source` (string) - the path to the folder on the local machine.
- `destination` (string) - the path to the folder on the remote machine.
Parent directories will not be created; use the shell module to do this.
- `exclude` (array of string) - files and directories to exclude from
transfer.
### Execute Command
By default, Packer uses the following command (broken across multiple lines for
readability) to execute Converge:
```liquid
cd {{.WorkingDirectory}} && \
{{if .Sudo}}sudo {{end}}converge apply \
--local \
--log-level=WARNING \
--paramsJSON '{{.ParamsJSON}}' \
{{.Module}}
```
This command can be customized using the `execute_command` configuration. As
you can see from the default value above, the value of this configuration can
contain various template variables:
- `WorkingDirectory` - `directory` from the configuration.
- `Sudo` - the opposite of `prevent_sudo` from the configuration.
- `ParamsJSON` - The unquoted JSONified form of `params` from the
configuration.
- `Module` - `module` from the configuration.
### Bootstrap Command
By default, Packer uses the following command to bootstrap Converge:
```liquid
curl -s https://get.converge.sh | {{if .Sudo}}sudo {{end}}sh {{if ne .Version ""}}-s -- -v {{.Version}}{{end}}
```
This command can be customized using the `bootstrap_command` configuration. As
you can see from the default values above, the value of this configuration can
contain various template variables:
- `Sudo` - the opposite of `prevent_bootstrap_sudo` from the configuration.
- `Version` - `version` from the configuration.

View File

@ -791,10 +791,6 @@
"title": "Breakpoint",
"path": "provisioners/breakpoint"
},
{
"title": "Converge",
"path": "provisioners/converge"
},
{
"title": "File",
"path": "provisioners/file"

View File

@ -22,7 +22,6 @@
"title": "Chef",
"path": "chef",
"repo": "hashicorp/packer-plugin-chef",
"version": "latest",
"pluginTier": "community",
"version": "latest"
},
@ -33,6 +32,13 @@
"pluginTier": "community",
"version": "latest"
},
{
"title": "Converge",
"path": "converge",
"repo": "hashicorp/packer-plugin-converge",
"pluginTier": "community",
"version": "latest"
},
{
"title": "Docker",
"path": "docker",