2015-05-16 00:05:47 -04:00
|
|
|
package interpolate
|
|
|
|
|
|
|
|
import (
|
|
|
|
"os"
|
2015-05-29 17:29:32 -04:00
|
|
|
"path/filepath"
|
2015-05-16 00:14:41 -04:00
|
|
|
"strconv"
|
2017-11-21 15:59:27 -05:00
|
|
|
"strings"
|
2015-05-16 00:05:47 -04:00
|
|
|
"testing"
|
2015-05-16 00:12:54 -04:00
|
|
|
"time"
|
2017-11-21 15:59:27 -05:00
|
|
|
|
2019-09-17 10:02:05 -04:00
|
|
|
"github.com/google/go-cmp/cmp"
|
2019-12-17 16:41:48 -05:00
|
|
|
"github.com/hashicorp/packer/helper/common"
|
2017-11-21 15:59:27 -05:00
|
|
|
"github.com/hashicorp/packer/version"
|
2015-05-16 00:05:47 -04:00
|
|
|
)
|
|
|
|
|
2015-06-13 16:48:35 -04:00
|
|
|
func TestFuncBuildName(t *testing.T) {
|
|
|
|
cases := []struct {
|
|
|
|
Input string
|
|
|
|
Output string
|
|
|
|
}{
|
|
|
|
{
|
|
|
|
`{{build_name}}`,
|
|
|
|
"foo",
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
ctx := &Context{BuildName: "foo"}
|
|
|
|
for _, tc := range cases {
|
|
|
|
i := &I{Value: tc.Input}
|
|
|
|
result, err := i.Render(ctx)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("Input: %s\n\nerr: %s", tc.Input, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if result != tc.Output {
|
|
|
|
t.Fatalf("Input: %s\n\nGot: %s", tc.Input, result)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestFuncBuildType(t *testing.T) {
|
|
|
|
cases := []struct {
|
|
|
|
Input string
|
|
|
|
Output string
|
|
|
|
}{
|
|
|
|
{
|
|
|
|
`{{build_type}}`,
|
|
|
|
"foo",
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
ctx := &Context{BuildType: "foo"}
|
|
|
|
for _, tc := range cases {
|
|
|
|
i := &I{Value: tc.Input}
|
|
|
|
result, err := i.Render(ctx)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("Input: %s\n\nerr: %s", tc.Input, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if result != tc.Output {
|
|
|
|
t.Fatalf("Input: %s\n\nGot: %s", tc.Input, result)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-05-16 00:05:47 -04:00
|
|
|
func TestFuncEnv(t *testing.T) {
|
|
|
|
cases := []struct {
|
|
|
|
Input string
|
|
|
|
Output string
|
|
|
|
}{
|
|
|
|
{
|
|
|
|
`{{env "PACKER_TEST_ENV"}}`,
|
|
|
|
`foo`,
|
|
|
|
},
|
|
|
|
|
|
|
|
{
|
|
|
|
`{{env "PACKER_TEST_ENV_NOPE"}}`,
|
|
|
|
``,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
os.Setenv("PACKER_TEST_ENV", "foo")
|
|
|
|
defer os.Setenv("PACKER_TEST_ENV", "")
|
|
|
|
|
2015-05-23 19:06:11 -04:00
|
|
|
ctx := &Context{EnableEnv: true}
|
2015-05-16 00:05:47 -04:00
|
|
|
for _, tc := range cases {
|
|
|
|
i := &I{Value: tc.Input}
|
|
|
|
result, err := i.Render(ctx)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("Input: %s\n\nerr: %s", tc.Input, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if result != tc.Output {
|
|
|
|
t.Fatalf("Input: %s\n\nGot: %s", tc.Input, result)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestFuncEnv_disable(t *testing.T) {
|
|
|
|
cases := []struct {
|
|
|
|
Input string
|
|
|
|
Output string
|
|
|
|
Error bool
|
|
|
|
}{
|
|
|
|
{
|
|
|
|
`{{env "PACKER_TEST_ENV"}}`,
|
|
|
|
"",
|
|
|
|
true,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
2015-05-23 19:06:11 -04:00
|
|
|
ctx := &Context{EnableEnv: false}
|
2015-05-16 00:05:47 -04:00
|
|
|
for _, tc := range cases {
|
|
|
|
i := &I{Value: tc.Input}
|
|
|
|
result, err := i.Render(ctx)
|
|
|
|
if (err != nil) != tc.Error {
|
|
|
|
t.Fatalf("Input: %s\n\nerr: %s", tc.Input, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if result != tc.Output {
|
|
|
|
t.Fatalf("Input: %s\n\nGot: %s", tc.Input, result)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2015-05-16 00:10:12 -04:00
|
|
|
|
2015-05-16 00:12:54 -04:00
|
|
|
func TestFuncIsotime(t *testing.T) {
|
|
|
|
ctx := &Context{}
|
|
|
|
i := &I{Value: "{{isotime}}"}
|
|
|
|
result, err := i.Render(ctx)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
val, err := time.Parse(time.RFC3339, result)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
currentTime := time.Now().UTC()
|
|
|
|
if currentTime.Sub(val) > 2*time.Second {
|
2016-12-06 19:37:30 -05:00
|
|
|
t.Fatalf("val: %v (current: %v)", val, currentTime)
|
2015-05-16 00:12:54 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-10-09 12:37:11 -04:00
|
|
|
func TestFuncStrftime(t *testing.T) {
|
|
|
|
ctx := &Context{}
|
|
|
|
i := &I{Value: "{{strftime \"%Y-%m-%dT%H:%M:%S%z\"}}"}
|
|
|
|
result, err := i.Render(ctx)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
val, err := time.Parse("2006-01-02T15:04:05-0700", result)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
currentTime := time.Now().UTC()
|
|
|
|
if currentTime.Sub(val) > 2*time.Second {
|
|
|
|
t.Fatalf("val: %v (current: %v)", val, currentTime)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-05-16 00:10:12 -04:00
|
|
|
func TestFuncPwd(t *testing.T) {
|
|
|
|
wd, err := os.Getwd()
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
cases := []struct {
|
|
|
|
Input string
|
|
|
|
Output string
|
|
|
|
}{
|
|
|
|
{
|
|
|
|
`{{pwd}}`,
|
|
|
|
wd,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
ctx := &Context{}
|
|
|
|
for _, tc := range cases {
|
|
|
|
i := &I{Value: tc.Input}
|
|
|
|
result, err := i.Render(ctx)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("Input: %s\n\nerr: %s", tc.Input, err)
|
|
|
|
}
|
|
|
|
|
2015-05-29 16:55:59 -04:00
|
|
|
if result != tc.Output {
|
|
|
|
t.Fatalf("Input: %s\n\nGot: %s", tc.Input, result)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestFuncTemplatePath(t *testing.T) {
|
2015-05-29 17:29:32 -04:00
|
|
|
path := "foo/bar"
|
|
|
|
expected, _ := filepath.Abs(filepath.Dir(path))
|
|
|
|
|
2015-05-29 16:55:59 -04:00
|
|
|
cases := []struct {
|
|
|
|
Input string
|
|
|
|
Output string
|
|
|
|
}{
|
|
|
|
{
|
2015-05-29 17:29:32 -04:00
|
|
|
`{{template_dir}}`,
|
|
|
|
expected,
|
2015-05-29 16:55:59 -04:00
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
ctx := &Context{
|
2015-05-29 17:29:32 -04:00
|
|
|
TemplatePath: path,
|
2015-05-29 16:55:59 -04:00
|
|
|
}
|
|
|
|
for _, tc := range cases {
|
|
|
|
i := &I{Value: tc.Input}
|
|
|
|
result, err := i.Render(ctx)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("Input: %s\n\nerr: %s", tc.Input, err)
|
|
|
|
}
|
|
|
|
|
2015-05-16 00:10:12 -04:00
|
|
|
if result != tc.Output {
|
|
|
|
t.Fatalf("Input: %s\n\nGot: %s", tc.Input, result)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2015-05-16 00:14:41 -04:00
|
|
|
|
2018-06-08 01:41:57 -04:00
|
|
|
func TestFuncSplit(t *testing.T) {
|
|
|
|
cases := []struct {
|
2018-08-24 18:18:47 -04:00
|
|
|
Input string
|
|
|
|
Output string
|
|
|
|
ErrorExpected bool
|
2018-06-08 01:41:57 -04:00
|
|
|
}{
|
|
|
|
{
|
|
|
|
`{{split build_name "-" 0}}`,
|
|
|
|
"foo",
|
2018-08-24 18:18:47 -04:00
|
|
|
false,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
`{{split build_name "-" 1}}`,
|
|
|
|
"bar",
|
|
|
|
false,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
`{{split build_name "-" 2}}`,
|
|
|
|
"",
|
|
|
|
true,
|
2018-06-08 01:41:57 -04:00
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
ctx := &Context{BuildName: "foo-bar"}
|
|
|
|
for _, tc := range cases {
|
|
|
|
i := &I{Value: tc.Input}
|
|
|
|
result, err := i.Render(ctx)
|
2018-08-24 18:18:47 -04:00
|
|
|
if (err == nil) == tc.ErrorExpected {
|
2018-06-08 01:41:57 -04:00
|
|
|
t.Fatalf("Input: %s\n\nerr: %s", tc.Input, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if result != tc.Output {
|
|
|
|
t.Fatalf("Input: %s\n\nGot: %s", tc.Input, result)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-05-16 00:14:41 -04:00
|
|
|
func TestFuncTimestamp(t *testing.T) {
|
|
|
|
expected := strconv.FormatInt(InitTime.Unix(), 10)
|
|
|
|
|
|
|
|
cases := []struct {
|
|
|
|
Input string
|
|
|
|
Output string
|
|
|
|
}{
|
|
|
|
{
|
|
|
|
`{{timestamp}}`,
|
|
|
|
expected,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
ctx := &Context{}
|
|
|
|
for _, tc := range cases {
|
|
|
|
i := &I{Value: tc.Input}
|
|
|
|
result, err := i.Render(ctx)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("Input: %s\n\nerr: %s", tc.Input, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if result != tc.Output {
|
|
|
|
t.Fatalf("Input: %s\n\nGot: %s", tc.Input, result)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2015-05-16 00:18:27 -04:00
|
|
|
|
|
|
|
func TestFuncUser(t *testing.T) {
|
|
|
|
cases := []struct {
|
|
|
|
Input string
|
|
|
|
Output string
|
|
|
|
}{
|
|
|
|
{
|
|
|
|
`{{user "foo"}}`,
|
|
|
|
`foo`,
|
|
|
|
},
|
|
|
|
|
|
|
|
{
|
|
|
|
`{{user "what"}}`,
|
|
|
|
``,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
ctx := &Context{
|
|
|
|
UserVariables: map[string]string{
|
|
|
|
"foo": "foo",
|
|
|
|
},
|
|
|
|
}
|
|
|
|
for _, tc := range cases {
|
|
|
|
i := &I{Value: tc.Input}
|
|
|
|
result, err := i.Render(ctx)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("Input: %s\n\nerr: %s", tc.Input, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if result != tc.Output {
|
|
|
|
t.Fatalf("Input: %s\n\nGot: %s", tc.Input, result)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2017-11-21 15:59:27 -05:00
|
|
|
|
2019-12-17 16:41:48 -05:00
|
|
|
func TestFuncPackerBuild(t *testing.T) {
|
|
|
|
type cases struct {
|
|
|
|
DataMap interface{}
|
|
|
|
ErrExpected bool
|
|
|
|
Template string
|
|
|
|
OutVal string
|
|
|
|
}
|
|
|
|
|
|
|
|
testCases := []cases{
|
|
|
|
// Data map is empty; there should be an error.
|
|
|
|
{
|
|
|
|
DataMap: nil,
|
|
|
|
ErrExpected: true,
|
|
|
|
Template: "{{ build `PartyVar` }}",
|
|
|
|
OutVal: "",
|
|
|
|
},
|
|
|
|
// Data map is a map[string]string and contains value
|
|
|
|
{
|
|
|
|
DataMap: map[string]string{"PartyVar": "PartyVal"},
|
|
|
|
ErrExpected: false,
|
|
|
|
Template: "{{ build `PartyVar` }}",
|
|
|
|
OutVal: "PartyVal",
|
|
|
|
},
|
|
|
|
// Data map is a map[string]string and contains value
|
|
|
|
{
|
|
|
|
DataMap: map[string]string{"PartyVar": "PartyVal"},
|
|
|
|
ErrExpected: false,
|
|
|
|
Template: "{{ build `PartyVar` }}",
|
|
|
|
OutVal: "PartyVal",
|
|
|
|
},
|
|
|
|
// Data map is a map[string]string and contains value with placeholder.
|
|
|
|
{
|
|
|
|
DataMap: map[string]string{"PartyVar": "PartyVal" + common.PlaceholderMsg},
|
|
|
|
ErrExpected: false,
|
|
|
|
Template: "{{ build `PartyVar` }}",
|
|
|
|
OutVal: "{{.PartyVar}}",
|
|
|
|
},
|
|
|
|
// Data map is a map[interface{}]interface{} and contains value
|
|
|
|
{
|
|
|
|
DataMap: map[interface{}]interface{}{"PartyVar": "PartyVal"},
|
|
|
|
ErrExpected: false,
|
|
|
|
Template: "{{ build `PartyVar` }}",
|
|
|
|
OutVal: "PartyVal",
|
|
|
|
},
|
|
|
|
// Data map is a map[interface{}]interface{} and contains value
|
|
|
|
{
|
|
|
|
DataMap: map[interface{}]interface{}{"PartyVar": "PartyVal"},
|
|
|
|
ErrExpected: false,
|
|
|
|
Template: "{{ build `PartyVar` }}",
|
|
|
|
OutVal: "PartyVal",
|
|
|
|
},
|
|
|
|
// Data map is a map[interface{}]interface{} and contains value with placeholder.
|
|
|
|
{
|
|
|
|
DataMap: map[interface{}]interface{}{"PartyVar": "PartyVal" + common.PlaceholderMsg},
|
|
|
|
ErrExpected: false,
|
|
|
|
Template: "{{ build `PartyVar` }}",
|
|
|
|
OutVal: "{{.PartyVar}}",
|
|
|
|
},
|
|
|
|
// Data map is a map[interface{}]interface{} and doesn't have value.
|
|
|
|
{
|
|
|
|
DataMap: map[interface{}]interface{}{"BadVar": "PartyVal" + common.PlaceholderMsg},
|
|
|
|
ErrExpected: true,
|
|
|
|
Template: "{{ build `MissingVar` }}",
|
|
|
|
OutVal: "",
|
|
|
|
},
|
2020-03-02 04:20:32 -05:00
|
|
|
// Data map is a map[string]interface and contains value
|
|
|
|
{
|
|
|
|
DataMap: map[string]interface{}{"PartyVar": "PartyVal"},
|
|
|
|
ErrExpected: false,
|
|
|
|
Template: "{{ build `PartyVar` }}",
|
|
|
|
OutVal: "PartyVal",
|
|
|
|
},
|
2019-12-17 16:41:48 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
for _, tc := range testCases {
|
|
|
|
ctx := &Context{}
|
|
|
|
ctx.Data = tc.DataMap
|
|
|
|
i := &I{Value: tc.Template}
|
|
|
|
|
|
|
|
result, err := i.Render(ctx)
|
|
|
|
if (err != nil) != tc.ErrExpected {
|
|
|
|
t.Fatalf("Input: %s\n\nerr: %s", tc.Template, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if ok := strings.Compare(result, tc.OutVal); ok != 0 {
|
|
|
|
t.Fatalf("Expected input to include: %s\n\nGot: %s",
|
|
|
|
tc.OutVal, result)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-11-21 15:59:27 -05:00
|
|
|
func TestFuncPackerVersion(t *testing.T) {
|
|
|
|
template := `{{packer_version}}`
|
|
|
|
|
|
|
|
ctx := &Context{}
|
|
|
|
i := &I{Value: template}
|
|
|
|
|
|
|
|
result, err := i.Render(ctx)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("Input: %s\n\nerr: %s", template, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Only match the X.Y.Z portion of the whole version string.
|
|
|
|
if !strings.HasPrefix(result, version.Version) {
|
|
|
|
t.Fatalf("Expected input to include: %s\n\nGot: %s",
|
|
|
|
version.Version, result)
|
|
|
|
}
|
|
|
|
}
|
2018-08-10 18:05:34 -04:00
|
|
|
|
2019-09-17 10:02:05 -04:00
|
|
|
func TestReplaceFuncs(t *testing.T) {
|
2018-08-10 18:05:34 -04:00
|
|
|
cases := []struct {
|
|
|
|
Input string
|
|
|
|
Output string
|
|
|
|
}{
|
2019-09-17 10:02:05 -04:00
|
|
|
|
|
|
|
{
|
|
|
|
`{{ "foo-bar-baz" | replace "-" "/" 1}}`,
|
|
|
|
`foo/bar-baz`,
|
|
|
|
},
|
|
|
|
|
2018-08-10 18:05:34 -04:00
|
|
|
{
|
2019-09-17 10:02:05 -04:00
|
|
|
`{{ replace "-" "/" 1 "foo-bar-baz" }}`,
|
|
|
|
`foo/bar-baz`,
|
2018-08-10 18:05:34 -04:00
|
|
|
},
|
|
|
|
|
|
|
|
{
|
2019-09-17 10:02:05 -04:00
|
|
|
`{{ "I Am Henry VIII" | replace_all " " "-" }}`,
|
|
|
|
`I-Am-Henry-VIII`,
|
2018-08-10 18:05:34 -04:00
|
|
|
},
|
|
|
|
|
|
|
|
{
|
2019-09-17 10:02:05 -04:00
|
|
|
`{{ replace_all " " "-" "I Am Henry VIII" }}`,
|
|
|
|
`I-Am-Henry-VIII`,
|
2018-08-10 18:05:34 -04:00
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
ctx := &Context{
|
|
|
|
UserVariables: map[string]string{
|
2019-09-17 10:02:05 -04:00
|
|
|
"fee": "-foo-",
|
2018-08-10 18:05:34 -04:00
|
|
|
},
|
|
|
|
}
|
|
|
|
for _, tc := range cases {
|
|
|
|
i := &I{Value: tc.Input}
|
|
|
|
result, err := i.Render(ctx)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("Input: %s\n\nerr: %s", tc.Input, err)
|
|
|
|
}
|
|
|
|
|
2019-09-17 10:02:05 -04:00
|
|
|
if diff := cmp.Diff(tc.Output, result); diff != "" {
|
|
|
|
t.Fatalf("Unexpected output: %s", diff)
|
2018-08-10 18:05:34 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|