Skip to content

Commit

Permalink
Merge pull request #35981 from hashicorp/jbardin/ephemeral-funcs
Browse files Browse the repository at this point in the history
Functions that allow marks must also deal with unknown values
  • Loading branch information
jbardin authored Nov 11, 2024
2 parents 5d41e01 + 297972e commit 19536b2
Show file tree
Hide file tree
Showing 15 changed files with 203 additions and 90 deletions.
22 changes: 13 additions & 9 deletions internal/lang/funcs/collection.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ var LengthFunc = function.New(&function.Spec{
coll := args[0]
collTy := args[0].Type()
marks := coll.Marks()

switch {
case collTy == cty.DynamicPseudoType:
return cty.UnknownVal(cty.Number).WithMarks(marks), nil
Expand Down Expand Up @@ -222,14 +223,16 @@ var IndexFunc = function.New(&function.Spec{
var LookupFunc = function.New(&function.Spec{
Params: []function.Parameter{
{
Name: "inputMap",
Type: cty.DynamicPseudoType,
AllowMarked: true,
Name: "inputMap",
Type: cty.DynamicPseudoType,
AllowMarked: true,
AllowUnknown: true,
},
{
Name: "key",
Type: cty.String,
AllowMarked: true,
Name: "key",
Type: cty.String,
AllowMarked: true,
AllowUnknown: true,
},
},
VarParam: &function.Parameter{
Expand Down Expand Up @@ -276,7 +279,7 @@ var LookupFunc = function.New(&function.Spec{
}
},
Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
var defaultVal cty.Value
defaultVal := cty.NullVal(retType)
defaultValueSet := false

if len(args) == 3 {
Expand All @@ -297,12 +300,13 @@ var LookupFunc = function.New(&function.Spec{
if len(keyMarks) > 0 {
markses = append(markses, keyMarks)
}
lookupKey := keyVal.AsString()

if !mapVar.IsKnown() {
if !(mapVar.IsKnown() && keyVal.IsKnown()) {
return cty.UnknownVal(retType).WithMarks(markses...), nil
}

lookupKey := keyVal.AsString()

if mapVar.Type().IsObjectType() {
if mapVar.Type().HasAttribute(lookupKey) {
return mapVar.GetAttr(lookupKey).WithMarks(markses...), nil
Expand Down
13 changes: 13 additions & 0 deletions internal/lang/funcs/collection_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,10 @@ func TestLength(t *testing.T) {
}).Mark("secret"),
cty.NumberIntVal(3).Mark("secret"),
},
{ // Marked objects return a marked length
cty.UnknownVal(cty.String).Mark("secret"),
cty.UnknownVal(cty.Number).Refine().NotNull().NumberRangeLowerBound(cty.NumberIntVal(0), true).NewValue().Mark("secret"),
},
{ // Marks on object attribute values do not propagate
cty.ObjectVal(map[string]cty.Value{
"a": cty.StringVal("hello").Mark("a"),
Expand Down Expand Up @@ -884,6 +888,15 @@ func TestLookup(t *testing.T) {
cty.StringVal("beep").Mark("a"),
false,
},
{ // propagate marks from unknown map
[]cty.Value{
cty.UnknownVal(cty.Map(cty.String)).Mark("a"),
cty.StringVal("boop").Mark("b"),
cty.StringVal("nope"),
},
cty.UnknownVal(cty.String).Mark("a").Mark("b"),
false,
},
}

for _, test := range tests {
Expand Down
7 changes: 5 additions & 2 deletions internal/lang/funcs/conversion.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ func MakeToFunc(wantTy cty.Type) function.Function {
AllowNull: true,
AllowMarked: true,
AllowDynamicType: true,
AllowUnknown: true,
},
},
Type: func(args []cty.Value) (cty.Type, error) {
Expand All @@ -61,8 +62,10 @@ func MakeToFunc(wantTy cty.Type) function.Function {
return wantTy, nil
},
Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
// We didn't set "AllowUnknown" on our argument, so it is guaranteed
// to be known here but may still be null.
if !args[0].IsKnown() {
return cty.UnknownVal(retType).WithSameMarks(args[0]), nil
}

ret, err := convert.Convert(args[0], retType)
if err != nil {
val, _ := args[0].UnmarkDeep()
Expand Down
24 changes: 22 additions & 2 deletions internal/lang/funcs/conversion_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,24 @@ func TestTo(t *testing.T) {
cty.DynamicVal,
`incompatible object type for conversion: attribute "foo" is required`,
},
{
cty.UnknownVal(cty.Object(map[string]cty.Type{"foo": cty.String})).Mark(marks.Ephemeral).Mark("boop"),
cty.Map(cty.String),
cty.UnknownVal(cty.Map(cty.String)).Mark(marks.Ephemeral).Mark("boop"),
``,
},
{
cty.ObjectVal(map[string]cty.Value{
"foo": cty.StringVal("hello"),
"bar": cty.StringVal("world").Mark("beep"),
}).Mark("boop"),
cty.Map(cty.String),
cty.MapVal(map[string]cty.Value{
"foo": cty.StringVal("hello"),
"bar": cty.StringVal("world").Mark("beep"),
}).Mark("boop"),
``,
},
}

for _, test := range tests {
Expand Down Expand Up @@ -322,13 +340,15 @@ func TestEphemeralAsNull(t *testing.T) {
{
cty.ObjectVal(map[string]cty.Value{
"addr": cty.StringVal("127.0.0.1:12654").Mark(marks.Ephemeral),
"greet": cty.StringVal("hello"),
"greet": cty.StringVal("hello").Mark(marks.Sensitive),
"happy": cty.True,
"both": cty.NumberIntVal(2).WithMarks(cty.NewValueMarks(marks.Sensitive, marks.Ephemeral)),
}),
cty.ObjectVal(map[string]cty.Value{
"addr": cty.NullVal(cty.String),
"greet": cty.StringVal("hello"),
"greet": cty.StringVal("hello").Mark(marks.Sensitive),
"happy": cty.True,
"both": cty.NullVal(cty.Number).Mark(marks.Sensitive),
}),
},
}
Expand Down
11 changes: 8 additions & 3 deletions internal/lang/funcs/encoding.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,20 @@ import (
var Base64DecodeFunc = function.New(&function.Spec{
Params: []function.Parameter{
{
Name: "str",
Type: cty.String,
AllowMarked: true,
Name: "str",
Type: cty.String,
AllowMarked: true,
AllowUnknown: true,
},
},
Type: function.StaticReturnType(cty.String),
RefineResult: refineNotNull,
Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
str, strMarks := args[0].Unmark()
if !str.IsKnown() {
return cty.UnknownVal(cty.String).WithMarks(strMarks), nil
}

s := str.AsString()
sDec, err := base64.StdEncoding.DecodeString(s)
if err != nil {
Expand Down
6 changes: 6 additions & 0 deletions internal/lang/funcs/encoding_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,12 @@ func TestBase64Decode(t *testing.T) {
cty.UnknownVal(cty.String),
true,
},
// unknown marked
{
cty.UnknownVal(cty.String).Mark("a").Mark("b"),
cty.UnknownVal(cty.String).RefineNotNull().Mark("a").Mark("b"),
false,
},
}

for _, test := range tests {
Expand Down
70 changes: 49 additions & 21 deletions internal/lang/funcs/filesystem.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,15 +28,21 @@ func MakeFileFunc(baseDir string, encBase64 bool) function.Function {
return function.New(&function.Spec{
Params: []function.Parameter{
{
Name: "path",
Type: cty.String,
AllowMarked: true,
Name: "path",
Type: cty.String,
AllowMarked: true,
AllowUnknown: true,
},
},
Type: function.StaticReturnType(cty.String),
RefineResult: refineNotNull,
Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
pathArg, pathMarks := args[0].Unmark()

if !pathArg.IsKnown() {
return cty.UnknownVal(cty.String).WithMarks(pathMarks), nil
}

path := pathArg.AsString()
src, err := readFileBytes(baseDir, path, pathMarks)
if err != nil {
Expand Down Expand Up @@ -94,13 +100,16 @@ func MakeTemplateFileFunc(baseDir string, funcsCb func() (funcs map[string]funct
return function.New(&function.Spec{
Params: []function.Parameter{
{
Name: "path",
Type: cty.String,
AllowMarked: true,
Name: "path",
Type: cty.String,
AllowMarked: true,
AllowUnknown: true,
},
{
Name: "vars",
Type: cty.DynamicPseudoType,
Name: "vars",
Type: cty.DynamicPseudoType,
AllowMarked: true,
AllowUnknown: true,
},
},
Type: func(args []cty.Value) (cty.Type, error) {
Expand All @@ -125,12 +134,19 @@ func MakeTemplateFileFunc(baseDir string, funcsCb func() (funcs map[string]funct
},
Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
pathArg, pathMarks := args[0].Unmark()

vars, varsMarks := args[1].UnmarkDeep()

if !pathArg.IsKnown() {
return cty.UnknownVal(retType).WithMarks(pathMarks, varsMarks), nil
}

expr, tmplMarks, err := loadTmpl(pathArg.AsString(), pathMarks)
if err != nil {
return cty.DynamicVal, err
}
result, err := renderTmpl(expr, args[1])
return result.WithMarks(tmplMarks), err
result, err := renderTmpl(expr, vars)
return result.WithMarks(tmplMarks, varsMarks), err
},
})

Expand All @@ -142,15 +158,21 @@ func MakeFileExistsFunc(baseDir string) function.Function {
return function.New(&function.Spec{
Params: []function.Parameter{
{
Name: "path",
Type: cty.String,
AllowMarked: true,
Name: "path",
Type: cty.String,
AllowMarked: true,
AllowUnknown: true,
},
},
Type: function.StaticReturnType(cty.Bool),
RefineResult: refineNotNull,
Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
pathArg, pathMarks := args[0].Unmark()

if !pathArg.IsKnown() {
return cty.UnknownVal(cty.Bool).WithMarks(pathMarks), nil
}

path := pathArg.AsString()
path, err := homedir.Expand(path)
if err != nil {
Expand Down Expand Up @@ -210,24 +232,30 @@ func MakeFileSetFunc(baseDir string) function.Function {
return function.New(&function.Spec{
Params: []function.Parameter{
{
Name: "path",
Type: cty.String,
AllowMarked: true,
Name: "path",
Type: cty.String,
AllowMarked: true,
AllowUnknown: true,
},
{
Name: "pattern",
Type: cty.String,
AllowMarked: true,
Name: "pattern",
Type: cty.String,
AllowMarked: true,
AllowUnknown: true,
},
},
Type: function.StaticReturnType(cty.Set(cty.String)),
RefineResult: refineNotNull,
Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
pathArg, pathMarks := args[0].Unmark()
path := pathArg.AsString()
patternArg, patternMarks := args[1].Unmark()
pattern := patternArg.AsString()

if !pathArg.IsKnown() || !patternArg.IsKnown() {
return cty.UnknownVal(retType).WithMarks(pathMarks, patternMarks), nil
}

path := pathArg.AsString()
pattern := patternArg.AsString()
marks := []cty.ValueMarks{pathMarks, patternMarks}

if !filepath.IsAbs(path) {
Expand Down
54 changes: 54 additions & 0 deletions internal/lang/funcs/filesystem_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,11 @@ func TestFile(t *testing.T) {
cty.StringVal("Hello World"),
``,
},
{
cty.UnknownVal(cty.String).Mark(marks.Sensitive),
cty.UnknownVal(cty.String).RefineNotNull().Mark(marks.Sensitive),
``,
},
{
cty.StringVal("testdata/icon.png"),
cty.NilVal,
Expand Down Expand Up @@ -199,6 +204,38 @@ func TestTemplateFile(t *testing.T) {
cty.StringVal("Hello World").Mark(marks.Sensitive),
``,
},
{
cty.StringVal("testdata/list.tmpl").Mark("path"),
cty.ObjectVal(map[string]cty.Value{
"list": cty.ListVal([]cty.Value{
cty.StringVal("a"),
cty.StringVal("b").Mark("var"),
cty.StringVal("c"),
}),
}),
cty.StringVal("- a\n- b\n- c\n").Mark("path").Mark("var"),
``,
},
{
cty.StringVal("testdata/list.tmpl").Mark("path"),
cty.ObjectVal(map[string]cty.Value{
"list": cty.ListVal([]cty.Value{
cty.StringVal("a"),
cty.UnknownVal(cty.String).Mark("var"),
cty.StringVal("c"),
}),
}),
cty.UnknownVal(cty.String).RefineNotNull().Mark("path").Mark("var"),
``,
},
{
cty.UnknownVal(cty.String).Mark("path"),
cty.ObjectVal(map[string]cty.Value{
"key": cty.StringVal("value").Mark("var"),
}),
cty.DynamicVal.Mark("path").Mark("var"),
``,
},
}

funcs := map[string]function.Function{
Expand Down Expand Up @@ -277,6 +314,11 @@ func TestFileExists(t *testing.T) {
cty.BoolVal(false),
`failed to stat (sensitive value)`,
},
{
cty.UnknownVal(cty.String).Mark(marks.Sensitive),
cty.UnknownVal(cty.Bool).RefineNotNull().Mark(marks.Sensitive),
``,
},
}

// Ensure "unreadable" directory cannot be listed during the test run
Expand Down Expand Up @@ -509,6 +551,18 @@ func TestFileSet(t *testing.T) {
}),
``,
},
{
cty.StringVal("testdata"),
cty.UnknownVal(cty.String),
cty.UnknownVal(cty.Set(cty.String)).RefineNotNull(),
``,
},
{
cty.StringVal("testdata"),
cty.UnknownVal(cty.String).Mark(marks.Sensitive),
cty.UnknownVal(cty.Set(cty.String)).RefineNotNull().Mark(marks.Sensitive),
``,
},
}

for _, test := range tests {
Expand Down
Loading

0 comments on commit 19536b2

Please sign in to comment.