packer-cn/common/template/funcs.go

116 lines
3.2 KiB
Go

package template
import (
"errors"
"fmt"
"log"
"os"
"strings"
"sync"
consulapi "github.com/hashicorp/consul/api"
awssmapi "github.com/hashicorp/packer/packer-plugin-sdk/template/interpolate/aws/secretsmanager"
vaultapi "github.com/hashicorp/vault/api"
)
// DeprecatedTemplateFunc wraps a template func to warn users that it's
// deprecated. The deprecation warning is called only once.
func DeprecatedTemplateFunc(funcName, useInstead string, deprecated func(string) string) func(string) string {
once := sync.Once{}
return func(in string) string {
once.Do(func() {
log.Printf("[WARN]: the `%s` template func is deprecated, please use %s instead",
funcName, useInstead)
})
return deprecated(in)
}
}
// Vault retrieves a secret from a HashiCorp Vault KV store.
// It assumes the necessary environment variables are set.
func Vault(path string, key string) (string, error) {
if token := os.Getenv("VAULT_TOKEN"); token == "" {
return "", errors.New("Must set VAULT_TOKEN env var in order to use vault template function")
}
vaultConfig := vaultapi.DefaultConfig()
cli, err := vaultapi.NewClient(vaultConfig)
if err != nil {
return "", fmt.Errorf("Error getting Vault client: %s", err)
}
secret, err := cli.Logical().Read(path)
if err != nil {
return "", fmt.Errorf("Error reading vault secret: %s", err)
}
if secret == nil {
return "", errors.New("Vault Secret does not exist at the given path")
}
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
return "", fmt.Errorf("Vault data was empty at the given path. Warnings: %s", strings.Join(secret.Warnings, "; "))
}
if val, ok := data.(map[string]interface{})[key]; ok {
return val.(string), nil
}
return "", errors.New("Vault path does not contain the requested key")
}
// Consul retrieves a value from a HashiCorp Consul KV store.
// It assumes the necessary environment variables are set.
func Consul(k string) (string, error) {
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)
}
return value, nil
}
// GetAwsSecret retrieves a value from an AWS Secrets Manager.
// It assumes that credentials are properly set in the AWS SDK's credential
// chain.
func GetAWSSecret(name, key string) (string, error) {
// Check if at least 1 parameter has been used
if len(name) == 0 {
return "", errors.New("At least one secret name must be provided")
}
// client uses AWS SDK CredentialChain method. So,credentials can
// be loaded from credential file, environment variables, or IAM
// roles.
client := awssmapi.New(
&awssmapi.AWSConfig{},
)
spec := &awssmapi.SecretSpec{
Name: name,
Key: key,
}
return client.GetSecret(spec)
}