reorganize placeholder data call to live with provisioner implementation; force users to use the generated function, therefore forcing validation, for all variables except winrmpassword, by doing a simple string check against the placeholder data.
This commit is contained in:
parent
ac570e0cc0
commit
82367a88f8
|
@ -10,7 +10,6 @@ import (
|
|||
"runtime"
|
||||
"strings"
|
||||
|
||||
"github.com/hashicorp/packer/common"
|
||||
"github.com/hashicorp/packer/common/shell"
|
||||
configHelper "github.com/hashicorp/packer/helper/config"
|
||||
"github.com/hashicorp/packer/packer"
|
||||
|
@ -52,7 +51,7 @@ type Config struct {
|
|||
func Decode(config *Config, raws ...interface{}) error {
|
||||
// Create passthrough for build-generated data so we can fill it in once we know
|
||||
// it
|
||||
config.ctx.Data = common.PlaceholderData()
|
||||
config.ctx.Data = packer.BasicPlaceholderData()
|
||||
|
||||
err := configHelper.Decode(&config, &configHelper.DecodeOpts{
|
||||
Interpolate: true,
|
||||
|
|
|
@ -22,34 +22,6 @@ import (
|
|||
// Produces:
|
||||
// <nothing>
|
||||
|
||||
// Provisioners interpolate most of their fields in the prepare stage; this
|
||||
// placeholder map helps keep fields that are only generated at build time from
|
||||
// accidentally being interpolated into empty strings at prepare time.
|
||||
func PlaceholderData() map[string]string {
|
||||
placeholderData := map[string]string{}
|
||||
placeholderData["ID"] = "{{.ID}}"
|
||||
// The following correspond to communicator-agnostic functions that are
|
||||
// part of the SSH and WinRM communicator implementations. These functions
|
||||
// are not part of the communicator interface, but are stored on the
|
||||
// Communicator Config and return the appropriate values rather than
|
||||
// depending on the actual communicator config values. E.g "Password"
|
||||
// reprosents either WinRMPassword or SSHPassword, which makes this more
|
||||
// useful if a template contains multiple builds.
|
||||
placeholderData["Host"] = "{{.Host}}"
|
||||
placeholderData["Port"] = "{{.Port}}"
|
||||
placeholderData["User"] = "{{.User}}"
|
||||
placeholderData["Password"] = "{{.Password}}"
|
||||
placeholderData["ConnType"] = "{{.Type}}"
|
||||
placeholderData["PACKER_RUN_UUID"] = "{{.PACKER_RUN_UUID}}"
|
||||
placeholderData["SSHPublicKey"] = "{{.SSHPublicKey}}"
|
||||
placeholderData["SSHPrivateKey"] = "{{.SSHPrivateKey}}"
|
||||
|
||||
// Backwards-compatability:
|
||||
placeholderData["WinRMPassword"] = "{{.WinRMPassword}}"
|
||||
|
||||
return placeholderData
|
||||
}
|
||||
|
||||
func PopulateProvisionHookData(state multistep.StateBag) map[string]interface{} {
|
||||
hookData := map[string]interface{}{}
|
||||
// instance_id is placed in state by the builders.
|
||||
|
|
|
@ -7,6 +7,11 @@ import (
|
|||
"path/filepath"
|
||||
)
|
||||
|
||||
// This is used in the BasicPlaceholderData() func in the packer/provisioner.go
|
||||
// To force users to access generated data via the "generated" func.
|
||||
const PlaceholderMsg = "To set this dynamically in the Packer template, " +
|
||||
"you must use the `generated` function"
|
||||
|
||||
// Used to set variables which we need to access later in the build, where
|
||||
// state bag and config information won't work
|
||||
func sharedStateFilename(suffix string, buildName string) string {
|
||||
|
|
|
@ -6,6 +6,8 @@ import (
|
|||
"log"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/packer/helper/common"
|
||||
)
|
||||
|
||||
// A provisioner is responsible for installing and configuring software
|
||||
|
@ -37,6 +39,42 @@ type ProvisionHook struct {
|
|||
Provisioners []*HookedProvisioner
|
||||
}
|
||||
|
||||
// Provisioners interpolate most of their fields in the prepare stage; this
|
||||
// placeholder map helps keep fields that are only generated at build time from
|
||||
// accidentally being interpolated into empty strings at prepare time.
|
||||
// This helper function generates the most basic placeholder data which should
|
||||
// be accessible to the provisioners. It is used to initialize provisioners, to
|
||||
// force validation using the `generated` template function. In the future,
|
||||
// custom generated data could be passed into provisioners from builders to
|
||||
// enable specialized builder-specific (but still validated!!) access to builder
|
||||
// data.
|
||||
func BasicPlaceholderData() map[string]string {
|
||||
placeholderData := map[string]string{}
|
||||
msg := "Generated_%s. " + common.PlaceholderMsg
|
||||
placeholderData["ID"] = fmt.Sprintf(msg, "ID")
|
||||
// The following correspond to communicator-agnostic functions that are
|
||||
// part of the SSH and WinRM communicator implementations. These functions
|
||||
// are not part of the communicator interface, but are stored on the
|
||||
// Communicator Config and return the appropriate values rather than
|
||||
// depending on the actual communicator config values. E.g "Password"
|
||||
// reprosents either WinRMPassword or SSHPassword, which makes this more
|
||||
// useful if a template contains multiple builds.
|
||||
placeholderData["Host"] = fmt.Sprintf(msg, "Host")
|
||||
placeholderData["Port"] = fmt.Sprintf(msg, "Port")
|
||||
placeholderData["User"] = fmt.Sprintf(msg, "User")
|
||||
placeholderData["Password"] = fmt.Sprintf(msg, "Password")
|
||||
placeholderData["ConnType"] = fmt.Sprintf(msg, "Type")
|
||||
placeholderData["PACKER_RUN_UUID"] = fmt.Sprintf(msg, "PACKER_RUN_UUID")
|
||||
placeholderData["SSHPublicKey"] = fmt.Sprintf(msg, "SSHPublicKey")
|
||||
placeholderData["SSHPrivateKey"] = fmt.Sprintf(msg, "SSHPrivateKey")
|
||||
|
||||
// Backwards-compatability: WinRM Password can get through without forcing
|
||||
// the generated func validation.
|
||||
placeholderData["WinRMPassword"] = "{{.WinRMPassword}}"
|
||||
|
||||
return placeholderData
|
||||
}
|
||||
|
||||
// Runs the provisioners in order.
|
||||
func (h *ProvisionHook) Run(ctx context.Context, name string, ui Ui, comm Communicator, data interface{}) error {
|
||||
// Shortcut
|
||||
|
|
|
@ -81,7 +81,7 @@ func (p *Provisioner) Prepare(raws ...interface{}) error {
|
|||
p.done = make(chan struct{})
|
||||
|
||||
// Create passthrough for build-generated data
|
||||
p.config.ctx.Data = common.PlaceholderData()
|
||||
p.config.ctx.Data = packer.BasicPlaceholderData()
|
||||
|
||||
err := config.Decode(&p.config, &config.DecodeOpts{
|
||||
Interpolate: true,
|
||||
|
|
|
@ -121,7 +121,7 @@ type KnifeTemplate struct {
|
|||
|
||||
func (p *Provisioner) Prepare(raws ...interface{}) error {
|
||||
// Create passthrough for build-generated data
|
||||
p.config.ctx.Data = common.PlaceholderData()
|
||||
p.config.ctx.Data = packer.BasicPlaceholderData()
|
||||
err := config.Decode(&p.config, &config.DecodeOpts{
|
||||
Interpolate: true,
|
||||
InterpolateContext: &p.config.ctx,
|
||||
|
|
|
@ -90,7 +90,7 @@ func (p *Provisioner) defaultExecuteCommand() string {
|
|||
|
||||
func (p *Provisioner) Prepare(raws ...interface{}) error {
|
||||
// Create passthrough for build-generated data
|
||||
p.config.ctx.Data = common.PlaceholderData()
|
||||
p.config.ctx.Data = packer.BasicPlaceholderData()
|
||||
|
||||
err := config.Decode(&p.config, &config.DecodeOpts{
|
||||
Interpolate: true,
|
||||
|
|
|
@ -148,7 +148,7 @@ type ExecuteTemplate struct {
|
|||
|
||||
func (p *Provisioner) Prepare(raws ...interface{}) error {
|
||||
// Create passthrough for build-generated data
|
||||
p.config.ctx.Data = common.PlaceholderData()
|
||||
p.config.ctx.Data = packer.BasicPlaceholderData()
|
||||
err := config.Decode(&p.config, &config.DecodeOpts{
|
||||
Interpolate: true,
|
||||
InterpolateContext: &p.config.ctx,
|
||||
|
|
|
@ -142,7 +142,7 @@ type ExecuteTemplate struct {
|
|||
|
||||
func (p *Provisioner) Prepare(raws ...interface{}) error {
|
||||
// Create passthrough for build-generated data
|
||||
p.config.ctx.Data = common.PlaceholderData()
|
||||
p.config.ctx.Data = packer.BasicPlaceholderData()
|
||||
|
||||
err := config.Decode(&p.config, &config.DecodeOpts{
|
||||
Interpolate: true,
|
||||
|
|
|
@ -12,6 +12,7 @@ import (
|
|||
|
||||
consulapi "github.com/hashicorp/consul/api"
|
||||
"github.com/hashicorp/packer/common/uuid"
|
||||
"github.com/hashicorp/packer/helper/common"
|
||||
"github.com/hashicorp/packer/version"
|
||||
vaultapi "github.com/hashicorp/vault/api"
|
||||
strftime "github.com/jehiah/go-strftime"
|
||||
|
@ -166,11 +167,17 @@ func funcGenTemplateDir(ctx *Context) interface{} {
|
|||
func funcGenGenerated(ctx *Context) interface{} {
|
||||
return func(s string) (string, error) {
|
||||
if data, ok := ctx.Data.(map[string]string); ok {
|
||||
// PlaceholderData has been passed into generator, and we can check
|
||||
// the value against the placeholder data to make sure that it is
|
||||
// valid
|
||||
// PlaceholderData has been passed into generator, so if the given
|
||||
// key already exists in data, then we know it's an "allowed" key
|
||||
if heldPlace, ok := data[s]; ok {
|
||||
return heldPlace, nil
|
||||
// If we're in the first interpolation pass, the goal is to
|
||||
// make sure that we pass the value through.
|
||||
// TODO match against an actual string constant
|
||||
if strings.Contains(heldPlace, common.PlaceholderMsg) {
|
||||
return fmt.Sprintf("{{.%s}}", s), nil
|
||||
} else {
|
||||
return heldPlace, nil
|
||||
}
|
||||
} else {
|
||||
return "", fmt.Errorf("loaded data, but couldnt find %s in it", s)
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue