2015-05-16 00:05:47 -04:00
|
|
|
package interpolate
|
|
|
|
|
|
|
|
import (
|
|
|
|
"errors"
|
2015-05-16 00:12:54 -04:00
|
|
|
"fmt"
|
2015-05-16 00:05:47 -04:00
|
|
|
"os"
|
2015-05-29 17:06:17 -04:00
|
|
|
"path/filepath"
|
2015-05-16 00:14:41 -04:00
|
|
|
"strconv"
|
2015-05-16 00:16:52 -04:00
|
|
|
"strings"
|
2015-05-16 00:05:47 -04:00
|
|
|
"text/template"
|
2015-05-16 00:12:54 -04:00
|
|
|
"time"
|
2015-05-16 00:16:52 -04:00
|
|
|
|
2018-08-08 11:03:15 -04:00
|
|
|
consulapi "github.com/hashicorp/consul/api"
|
2017-04-04 16:39:01 -04:00
|
|
|
"github.com/hashicorp/packer/common/uuid"
|
2019-12-14 06:32:38 -05:00
|
|
|
"github.com/hashicorp/packer/helper/common"
|
2017-11-21 15:59:27 -05:00
|
|
|
"github.com/hashicorp/packer/version"
|
2018-07-24 14:24:08 -04:00
|
|
|
vaultapi "github.com/hashicorp/vault/api"
|
2019-10-09 12:37:11 -04:00
|
|
|
strftime "github.com/jehiah/go-strftime"
|
2020-04-21 12:37:29 -04:00
|
|
|
awssmapi "github.com/overdrive3000/secretsmanager"
|
2015-05-16 00:05:47 -04:00
|
|
|
)
|
|
|
|
|
2015-05-16 00:14:41 -04:00
|
|
|
// InitTime is the UTC time when this package was initialized. It is
|
|
|
|
// used as the timestamp for all configuration templates so that they
|
|
|
|
// match for a single build.
|
|
|
|
var InitTime time.Time
|
|
|
|
|
|
|
|
func init() {
|
|
|
|
InitTime = time.Now().UTC()
|
|
|
|
}
|
|
|
|
|
2015-05-16 00:05:47 -04:00
|
|
|
// Funcs are the interpolation funcs that are available within interpolations.
|
2019-09-17 10:01:22 -04:00
|
|
|
var FuncGens = map[string]interface{}{
|
2020-04-21 12:37:29 -04:00
|
|
|
"build_name": funcGenBuildName,
|
|
|
|
"build_type": funcGenBuildType,
|
|
|
|
"env": funcGenEnv,
|
|
|
|
"isotime": funcGenIsotime,
|
|
|
|
"strftime": funcGenStrftime,
|
|
|
|
"pwd": funcGenPwd,
|
|
|
|
"split": funcGenSplitter,
|
|
|
|
"template_dir": funcGenTemplateDir,
|
|
|
|
"timestamp": funcGenTimestamp,
|
|
|
|
"uuid": funcGenUuid,
|
|
|
|
"user": funcGenUser,
|
|
|
|
"packer_version": funcGenPackerVersion,
|
|
|
|
"consul_key": funcGenConsul,
|
|
|
|
"vault": funcGenVault,
|
|
|
|
"sed": funcGenSed,
|
|
|
|
"build": funcGenBuild,
|
|
|
|
"aws_secretsmanager": funcGenAwsSecrets,
|
2019-09-17 10:02:05 -04:00
|
|
|
|
|
|
|
"replace": replace,
|
|
|
|
"replace_all": replace_all,
|
2015-05-16 00:16:52 -04:00
|
|
|
|
2019-09-17 10:01:22 -04:00
|
|
|
"upper": strings.ToUpper,
|
|
|
|
"lower": strings.ToLower,
|
2015-05-16 00:05:47 -04:00
|
|
|
}
|
|
|
|
|
2019-07-08 18:39:46 -04:00
|
|
|
var ErrVariableNotSetString = "Error: variable not set:"
|
2019-07-08 16:49:14 -04:00
|
|
|
|
2015-05-16 00:05:47 -04:00
|
|
|
// FuncGenerator is a function that given a context generates a template
|
|
|
|
// function for the template.
|
|
|
|
type FuncGenerator func(*Context) interface{}
|
|
|
|
|
|
|
|
// Funcs returns the functions that can be used for interpolation given
|
|
|
|
// a context.
|
|
|
|
func Funcs(ctx *Context) template.FuncMap {
|
|
|
|
result := make(map[string]interface{})
|
|
|
|
for k, v := range FuncGens {
|
2019-09-17 10:01:22 -04:00
|
|
|
switch v := v.(type) {
|
2019-09-17 10:25:44 -04:00
|
|
|
case func(*Context) interface{}:
|
2019-09-17 10:01:22 -04:00
|
|
|
result[k] = v(ctx)
|
|
|
|
default:
|
|
|
|
result[k] = v
|
|
|
|
}
|
2015-05-16 00:05:47 -04:00
|
|
|
}
|
2015-05-27 14:10:09 -04:00
|
|
|
if ctx != nil {
|
|
|
|
for k, v := range ctx.Funcs {
|
|
|
|
result[k] = v
|
|
|
|
}
|
|
|
|
}
|
2015-05-16 00:05:47 -04:00
|
|
|
|
|
|
|
return template.FuncMap(result)
|
|
|
|
}
|
|
|
|
|
2018-06-08 01:41:57 -04:00
|
|
|
func funcGenSplitter(ctx *Context) interface{} {
|
|
|
|
return func(k string, s string, i int) (string, error) {
|
|
|
|
// return func(s string) (string, error) {
|
|
|
|
split := strings.Split(k, s)
|
|
|
|
if len(split) <= i {
|
|
|
|
return "", fmt.Errorf("the substring %d was unavailable using the separator value, %s, only %d values were found", i, s, len(split))
|
|
|
|
}
|
|
|
|
return split[i], nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-06-13 16:48:35 -04:00
|
|
|
func funcGenBuildName(ctx *Context) interface{} {
|
|
|
|
return func() (string, error) {
|
|
|
|
if ctx == nil || ctx.BuildName == "" {
|
|
|
|
return "", errors.New("build_name not available")
|
|
|
|
}
|
|
|
|
|
|
|
|
return ctx.BuildName, nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func funcGenBuildType(ctx *Context) interface{} {
|
|
|
|
return func() (string, error) {
|
|
|
|
if ctx == nil || ctx.BuildType == "" {
|
2015-06-30 13:42:55 -04:00
|
|
|
return "", errors.New("build_type not available")
|
2015-06-13 16:48:35 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
return ctx.BuildType, nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-05-16 00:05:47 -04:00
|
|
|
func funcGenEnv(ctx *Context) interface{} {
|
|
|
|
return func(k string) (string, error) {
|
2015-05-23 19:06:11 -04:00
|
|
|
if !ctx.EnableEnv {
|
2015-05-16 00:05:47 -04:00
|
|
|
// The error message doesn't have to be that detailed since
|
|
|
|
// semantic checks should catch this.
|
|
|
|
return "", errors.New("env vars are not allowed here")
|
|
|
|
}
|
|
|
|
|
|
|
|
return os.Getenv(k), nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-05-16 00:12:54 -04:00
|
|
|
func funcGenIsotime(ctx *Context) interface{} {
|
|
|
|
return func(format ...string) (string, error) {
|
|
|
|
if len(format) == 0 {
|
2015-10-06 19:45:30 -04:00
|
|
|
return InitTime.Format(time.RFC3339), nil
|
2015-05-16 00:12:54 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
if len(format) > 1 {
|
|
|
|
return "", fmt.Errorf("too many values, 1 needed: %v", format)
|
|
|
|
}
|
|
|
|
|
2015-10-06 19:45:30 -04:00
|
|
|
return InitTime.Format(format[0]), nil
|
2015-05-16 00:12:54 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-10-09 12:37:11 -04:00
|
|
|
func funcGenStrftime(ctx *Context) interface{} {
|
|
|
|
return func(format string) string {
|
|
|
|
return strftime.Format(format, InitTime)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-05-16 00:10:12 -04:00
|
|
|
func funcGenPwd(ctx *Context) interface{} {
|
|
|
|
return func() (string, error) {
|
|
|
|
return os.Getwd()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-05-29 17:29:32 -04:00
|
|
|
func funcGenTemplateDir(ctx *Context) interface{} {
|
2015-05-29 16:55:59 -04:00
|
|
|
return func() (string, error) {
|
|
|
|
if ctx == nil || ctx.TemplatePath == "" {
|
|
|
|
return "", errors.New("template path not available")
|
|
|
|
}
|
|
|
|
|
2015-05-29 17:29:32 -04:00
|
|
|
path, err := filepath.Abs(filepath.Dir(ctx.TemplatePath))
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
|
|
|
|
return path, nil
|
2015-05-29 16:55:59 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-02 04:20:32 -05:00
|
|
|
func passthroughOrInterpolate(data map[interface{}]interface{}, s string) (string, error) {
|
|
|
|
if heldPlace, ok := data[s]; ok {
|
|
|
|
if hp, ok := heldPlace.(string); ok {
|
|
|
|
// 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(hp, common.PlaceholderMsg) {
|
|
|
|
return fmt.Sprintf("{{.%s}}", s), nil
|
|
|
|
} else {
|
|
|
|
return hp, nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return "", fmt.Errorf("loaded data, but couldnt find %s in it.", s)
|
|
|
|
|
|
|
|
}
|
2019-12-17 00:35:42 -05:00
|
|
|
func funcGenBuild(ctx *Context) interface{} {
|
2020-03-02 04:20:32 -05:00
|
|
|
// Depending on where the context data is coming from, it could take a few
|
|
|
|
// different map types. The following switch standardizes the map types
|
|
|
|
// so we can act on them correctly.
|
2019-12-11 18:43:11 -05:00
|
|
|
return func(s string) (string, error) {
|
2020-03-02 04:20:32 -05:00
|
|
|
switch data := ctx.Data.(type) {
|
|
|
|
case map[interface{}]interface{}:
|
|
|
|
return passthroughOrInterpolate(data, s)
|
|
|
|
case map[string]interface{}:
|
|
|
|
// convert to a map[interface{}]interface{} so we can use same
|
|
|
|
// parsing on it
|
|
|
|
passed := make(map[interface{}]interface{}, len(data))
|
|
|
|
for k, v := range data {
|
|
|
|
passed[k] = v
|
2019-12-17 14:20:57 -05:00
|
|
|
}
|
2020-03-02 04:20:32 -05:00
|
|
|
return passthroughOrInterpolate(passed, s)
|
|
|
|
case map[string]string:
|
|
|
|
// convert to a map[interface{}]interface{} so we can use same
|
|
|
|
// parsing on it
|
|
|
|
passed := make(map[interface{}]interface{}, len(data))
|
|
|
|
for k, v := range data {
|
|
|
|
passed[k] = v
|
2019-12-11 18:43:11 -05:00
|
|
|
}
|
2020-03-02 04:20:32 -05:00
|
|
|
return passthroughOrInterpolate(passed, s)
|
|
|
|
default:
|
|
|
|
return "", fmt.Errorf("Error validating build variable: the given "+
|
|
|
|
"variable %s will not be passed into your plugin.", s)
|
2019-12-11 18:43:11 -05:00
|
|
|
}
|
2019-12-11 17:26:54 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-05-16 00:14:41 -04:00
|
|
|
func funcGenTimestamp(ctx *Context) interface{} {
|
|
|
|
return func() string {
|
|
|
|
return strconv.FormatInt(InitTime.Unix(), 10)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-05-16 00:05:47 -04:00
|
|
|
func funcGenUser(ctx *Context) interface{} {
|
2017-02-04 02:15:18 -05:00
|
|
|
return func(k string) (string, error) {
|
2015-05-16 00:18:27 -04:00
|
|
|
if ctx == nil || ctx.UserVariables == nil {
|
2017-02-04 02:15:18 -05:00
|
|
|
return "", errors.New("test")
|
2015-05-16 00:18:27 -04:00
|
|
|
}
|
|
|
|
|
2019-03-08 17:49:47 -05:00
|
|
|
val, ok := ctx.UserVariables[k]
|
|
|
|
if ctx.EnableEnv {
|
|
|
|
// error and retry if we're interpolating UserVariables. But if
|
|
|
|
// we're elsewhere in the template, just return the empty string.
|
|
|
|
if !ok {
|
2019-07-08 18:39:46 -04:00
|
|
|
return "", fmt.Errorf("%s %s", ErrVariableNotSetString, k)
|
2019-03-08 17:49:47 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return val, nil
|
2015-05-16 00:05:47 -04:00
|
|
|
}
|
|
|
|
}
|
2015-05-16 00:16:52 -04:00
|
|
|
|
|
|
|
func funcGenUuid(ctx *Context) interface{} {
|
|
|
|
return func() string {
|
|
|
|
return uuid.TimeOrderedUUID()
|
|
|
|
}
|
|
|
|
}
|
2017-11-21 15:59:27 -05:00
|
|
|
|
|
|
|
func funcGenPackerVersion(ctx *Context) interface{} {
|
|
|
|
return func() string {
|
|
|
|
return version.FormattedVersion()
|
|
|
|
}
|
|
|
|
}
|
2018-08-08 11:03:15 -04:00
|
|
|
|
|
|
|
func funcGenConsul(ctx *Context) interface{} {
|
|
|
|
return func(k string) (string, error) {
|
|
|
|
if !ctx.EnableEnv {
|
|
|
|
// The error message doesn't have to be that detailed since
|
|
|
|
// semantic checks should catch this.
|
2018-08-08 13:19:27 -04:00
|
|
|
return "", errors.New("consul_key is not allowed here")
|
2018-08-08 11:03:15 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
consulConfig := consulapi.DefaultConfig()
|
|
|
|
client, err := consulapi.NewClient(consulConfig)
|
|
|
|
if err != nil {
|
|
|
|
return "", fmt.Errorf("error getting consul client: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
q := &consulapi.QueryOptions{}
|
|
|
|
kv, _, err := client.KV().Get(k, q)
|
|
|
|
if err != nil {
|
|
|
|
return "", fmt.Errorf("error reading consul key: %s", err)
|
|
|
|
}
|
|
|
|
if kv == nil {
|
|
|
|
return "", fmt.Errorf("key does not exist at the given path: %s", k)
|
|
|
|
}
|
|
|
|
|
|
|
|
value := string(kv.Value)
|
|
|
|
if value == "" {
|
|
|
|
return "", fmt.Errorf("value is empty at path %s", k)
|
|
|
|
}
|
2018-08-16 19:10:35 -04:00
|
|
|
|
|
|
|
return value, nil
|
2018-07-24 14:24:08 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func funcGenVault(ctx *Context) interface{} {
|
|
|
|
return func(path string, key string) (string, error) {
|
|
|
|
// Only allow interpolation from Vault when env vars are being read.
|
|
|
|
if !ctx.EnableEnv {
|
|
|
|
// The error message doesn't have to be that detailed since
|
|
|
|
// semantic checks should catch this.
|
|
|
|
return "", errors.New("Vault vars are only allowed in the variables section")
|
|
|
|
}
|
|
|
|
if token := os.Getenv("VAULT_TOKEN"); token == "" {
|
|
|
|
return "", errors.New("Must set VAULT_TOKEN env var in order to " +
|
|
|
|
"use vault template function")
|
|
|
|
}
|
|
|
|
// const EnvVaultAddress = "VAULT_ADDR"
|
|
|
|
// const EnvVaultToken = "VAULT_TOKEN"
|
|
|
|
vaultConfig := vaultapi.DefaultConfig()
|
|
|
|
cli, err := vaultapi.NewClient(vaultConfig)
|
|
|
|
if err != nil {
|
2020-04-27 04:35:47 -04:00
|
|
|
return "", fmt.Errorf("Error getting Vault client: %s", err)
|
2018-07-24 14:24:08 -04:00
|
|
|
}
|
|
|
|
secret, err := cli.Logical().Read(path)
|
|
|
|
if err != nil {
|
2020-04-27 04:35:47 -04:00
|
|
|
return "", fmt.Errorf("Error reading vault secret: %s", err)
|
2018-07-24 14:24:08 -04:00
|
|
|
}
|
|
|
|
if secret == nil {
|
2020-04-27 04:35:47 -04:00
|
|
|
return "", errors.New("Vault Secret does not exist at the given path")
|
2018-07-24 14:24:08 -04:00
|
|
|
}
|
|
|
|
|
2018-08-16 19:10:35 -04:00
|
|
|
data, ok := secret.Data["data"]
|
|
|
|
if !ok {
|
|
|
|
// maybe ths is v1, not v2 kv store
|
|
|
|
value, ok := secret.Data[key]
|
|
|
|
if ok {
|
|
|
|
return value.(string), nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// neither v1 nor v2 proudced a valid value
|
2020-04-27 04:35:47 -04:00
|
|
|
return "", fmt.Errorf("Vault data was empty at the "+
|
|
|
|
"given path. Warnings: %s", strings.Join(secret.Warnings, "; "))
|
2018-07-24 14:24:08 -04:00
|
|
|
}
|
2018-08-08 11:03:15 -04:00
|
|
|
|
2018-08-16 19:10:35 -04:00
|
|
|
value := data.(map[string]interface{})[key].(string)
|
2018-08-08 11:03:15 -04:00
|
|
|
return value, nil
|
|
|
|
}
|
|
|
|
}
|
2018-08-10 18:05:34 -04:00
|
|
|
|
2020-04-21 12:37:29 -04:00
|
|
|
func funcGenAwsSecrets(ctx *Context) interface{} {
|
|
|
|
return func(name string) (string, error) {
|
|
|
|
if !ctx.EnableEnv {
|
|
|
|
// The error message doesn't have to be that detailed since
|
|
|
|
// semantic checks should catch this.
|
|
|
|
return "", errors.New("AWS Secrets Manager vars are only allowed in the variables section")
|
|
|
|
}
|
|
|
|
// client uses AWS SDK CredentialChain method. So,credentials can
|
|
|
|
// be loaded from credential file, environment variables, or IAM
|
|
|
|
// roles.
|
|
|
|
client, err := awssmapi.New()
|
|
|
|
if err != nil {
|
2020-04-21 13:20:45 -04:00
|
|
|
return "", fmt.Errorf("Error getting AWS Secrets Manager client: %s", err)
|
2020-04-21 12:37:29 -04:00
|
|
|
}
|
|
|
|
secret, err := client.GetSecret(name)
|
|
|
|
if err != nil {
|
2020-04-21 13:20:45 -04:00
|
|
|
return "", fmt.Errorf("Error getting secret: %s", err)
|
2020-04-21 12:37:29 -04:00
|
|
|
}
|
|
|
|
return secret, nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-09-20 05:08:15 -04:00
|
|
|
func funcGenSed(ctx *Context) interface{} {
|
|
|
|
return func(expression string, inputString string) (string, error) {
|
|
|
|
return "", errors.New("template function `sed` is deprecated " +
|
|
|
|
"use `replace` or `replace_all` instead." +
|
2020-04-01 18:54:21 -04:00
|
|
|
"Documentation: https://www.packer.io/docs/templates/engine")
|
2019-09-20 05:08:15 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-09-17 10:02:05 -04:00
|
|
|
func replace_all(old, new, src string) string {
|
|
|
|
return strings.ReplaceAll(src, old, new)
|
|
|
|
}
|
2018-08-15 22:48:20 -04:00
|
|
|
|
2019-09-17 10:02:05 -04:00
|
|
|
func replace(old, new string, n int, src string) string {
|
|
|
|
return strings.Replace(src, old, new, n)
|
2018-08-10 18:05:34 -04:00
|
|
|
}
|