diff --git a/terraform/addrs/resource.go b/terraform/addrs/resource.go index 358eb1e93..6b2f71c42 100644 --- a/terraform/addrs/resource.go +++ b/terraform/addrs/resource.go @@ -51,8 +51,6 @@ func (r ResourceInstance) ContainingResource() Resource { // resource lifecycle has a slightly different address format. type ResourceMode rune -//go:generate go run golang.org/x/tools/cmd/stringer -type ResourceMode - const ( // InvalidResourceMode is the zero value of ResourceMode and is not // a valid resource mode. diff --git a/terraform/lang/funcs/datetime.go b/terraform/lang/funcs/datetime.go index fbd7c0b27..373d12795 100644 --- a/terraform/lang/funcs/datetime.go +++ b/terraform/lang/funcs/datetime.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package funcs import ( @@ -17,6 +20,16 @@ var TimestampFunc = function.New(&function.Spec{ }, }) +// PlantimestampFunc constructs a function that returns the time of the plan. +// TFLint always treats this value as unknown. +var PlantimestampFunc = function.New(&function.Spec{ + Params: []function.Parameter{}, + Type: function.StaticReturnType(cty.String), + Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) { + return cty.UnknownVal(cty.String), nil + }, +}) + // TimeAddFunc constructs a function that adds a duration to a timestamp, returning a new timestamp. var TimeAddFunc = function.New(&function.Spec{ Params: []function.Parameter{ diff --git a/terraform/lang/funcs/string.go b/terraform/lang/funcs/string.go index 9ef709c7f..ea7ada1be 100644 --- a/terraform/lang/funcs/string.go +++ b/terraform/lang/funcs/string.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package funcs import ( @@ -103,3 +106,29 @@ var ReplaceFunc = function.New(&function.Spec{ func Replace(str, substr, replace cty.Value) (cty.Value, error) { return ReplaceFunc.Call([]cty.Value{str, substr, replace}) } + +// StrContainsFunc searches a given string for another given substring, +// if found the function returns true, otherwise returns false. +var StrContainsFunc = function.New(&function.Spec{ + Params: []function.Parameter{ + { + Name: "str", + Type: cty.String, + }, + { + Name: "substr", + Type: cty.String, + }, + }, + Type: function.StaticReturnType(cty.Bool), + Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) { + str := args[0].AsString() + substr := args[1].AsString() + + if strings.Contains(str, substr) { + return cty.True, nil + } + + return cty.False, nil + }, +}) diff --git a/terraform/lang/funcs/string_test.go b/terraform/lang/funcs/string_test.go index 7b44a2762..d5c5996d9 100644 --- a/terraform/lang/funcs/string_test.go +++ b/terraform/lang/funcs/string_test.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package funcs import ( @@ -71,3 +74,66 @@ func TestReplace(t *testing.T) { }) } } + +func TestStrContains(t *testing.T) { + tests := []struct { + String cty.Value + Substr cty.Value + Want cty.Value + Err bool + }{ + { + cty.StringVal("hello"), + cty.StringVal("hel"), + cty.BoolVal(true), + false, + }, + { + cty.StringVal("hello"), + cty.StringVal("lo"), + cty.BoolVal(true), + false, + }, + { + cty.StringVal("hello1"), + cty.StringVal("1"), + cty.BoolVal(true), + false, + }, + { + cty.StringVal("hello1"), + cty.StringVal("heo"), + cty.BoolVal(false), + false, + }, + { + cty.StringVal("hello1"), + cty.NumberIntVal(1), + cty.UnknownVal(cty.Bool), + true, + }, + } + + for _, test := range tests { + t.Run(fmt.Sprintf("includes(%#v, %#v)", test.String, test.Substr), func(t *testing.T) { + got, err := StrContains(test.String, test.Substr) + + if test.Err { + if err == nil { + t.Fatal("succeeded; want error") + } + return + } else if err != nil { + t.Fatalf("unexpected error: %s", err) + } + + if !got.RawEquals(test.Want) { + t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, test.Want) + } + }) + } +} + +func StrContains(str, substr cty.Value) (cty.Value, error) { + return StrContainsFunc.Call([]cty.Value{str, substr}) +} diff --git a/terraform/lang/functions.go b/terraform/lang/functions.go index ff029b3d6..c4a498beb 100644 --- a/terraform/lang/functions.go +++ b/terraform/lang/functions.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package lang import ( @@ -93,6 +96,7 @@ func (s *Scope) Functions() map[string]function.Function { "one": funcs.OneFunc, "parseint": stdlib.ParseIntFunc, "pathexpand": funcs.PathExpandFunc, + "plantimestamp": funcs.PlantimestampFunc, "pow": stdlib.PowFunc, "range": stdlib.RangeFunc, "regex": stdlib.RegexFunc, @@ -114,6 +118,7 @@ func (s *Scope) Functions() map[string]function.Function { "sort": stdlib.SortFunc, "split": stdlib.SplitFunc, "startswith": funcs.StartsWithFunc, + "strcontains": funcs.StrContainsFunc, "strrev": stdlib.ReverseFunc, "substr": stdlib.SubstrFunc, "sum": funcs.SumFunc, diff --git a/terraform/lang/functions_test.go b/terraform/lang/functions_test.go index 726f747f1..280c98c15 100644 --- a/terraform/lang/functions_test.go +++ b/terraform/lang/functions_test.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package lang import ( @@ -638,6 +641,13 @@ func TestFunctions(t *testing.T) { }, }, + "plantimestamp": { + { + `plantimestamp()`, + cty.UnknownVal(cty.String), + }, + }, + "pow": { { `pow(1,0)`, @@ -837,6 +847,17 @@ func TestFunctions(t *testing.T) { }, }, + "strcontains": { + { + `strcontains("hello", "llo")`, + cty.BoolVal(true), + }, + { + `strcontains("hello", "a")`, + cty.BoolVal(false), + }, + }, + "strrev": { { `strrev("hello world")`,