terraform: ResourceConfig reimplementation for TypeUnknown
The primary change here is to expect that Config contains computed values. This introduces `unknownCheckWalker` that does a really basic reflectwalk to look for computed values and use that for IsComputed. We had a weird mixture before checking whether c.Config was simply missing values to determine where to look. Now we rely on IsComputed heavily.
This commit is contained in:
parent
59bd1a22f4
commit
9b5b122f14
|
@ -24,9 +24,7 @@ func TestEvalValidateResource_managedResource(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
p := ResourceProvider(mp)
|
p := ResourceProvider(mp)
|
||||||
rc := &ResourceConfig{
|
rc := testResourceConfig(t, map[string]interface{}{"foo": "bar"})
|
||||||
Raw: map[string]interface{}{"foo": "bar"},
|
|
||||||
}
|
|
||||||
node := &EvalValidateResource{
|
node := &EvalValidateResource{
|
||||||
Provider: &p,
|
Provider: &p,
|
||||||
Config: &rc,
|
Config: &rc,
|
||||||
|
@ -61,9 +59,7 @@ func TestEvalValidateResource_dataSource(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
p := ResourceProvider(mp)
|
p := ResourceProvider(mp)
|
||||||
rc := &ResourceConfig{
|
rc := testResourceConfig(t, map[string]interface{}{"foo": "bar"})
|
||||||
Raw: map[string]interface{}{"foo": "bar"},
|
|
||||||
}
|
|
||||||
node := &EvalValidateResource{
|
node := &EvalValidateResource{
|
||||||
Provider: &p,
|
Provider: &p,
|
||||||
Config: &rc,
|
Config: &rc,
|
||||||
|
|
|
@ -9,6 +9,7 @@ import (
|
||||||
|
|
||||||
"github.com/hashicorp/terraform/config"
|
"github.com/hashicorp/terraform/config"
|
||||||
"github.com/mitchellh/copystructure"
|
"github.com/mitchellh/copystructure"
|
||||||
|
"github.com/mitchellh/reflectwalk"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ResourceProvisionerConfig is used to pair a provisioner
|
// ResourceProvisionerConfig is used to pair a provisioner
|
||||||
|
@ -186,16 +187,17 @@ func (c *ResourceConfig) CheckSet(keys []string) []error {
|
||||||
// Get looks up a configuration value by key and returns the value.
|
// Get looks up a configuration value by key and returns the value.
|
||||||
//
|
//
|
||||||
// The second return value is true if the get was successful. Get will
|
// The second return value is true if the get was successful. Get will
|
||||||
// not succeed if the value is being computed.
|
// return the raw value if the key is computed, so you should pair this
|
||||||
|
// with IsComputed.
|
||||||
func (c *ResourceConfig) Get(k string) (interface{}, bool) {
|
func (c *ResourceConfig) Get(k string) (interface{}, bool) {
|
||||||
// First try to get it from c.Config since that has interpolated values
|
// We aim to get a value from the configuration. If it is computed,
|
||||||
result, ok := c.get(k, c.Config)
|
// then we return the pure raw value.
|
||||||
if ok {
|
source := c.Config
|
||||||
return result, ok
|
if c.IsComputed(k) {
|
||||||
|
source = c.Raw
|
||||||
}
|
}
|
||||||
|
|
||||||
// Otherwise, just get it from the raw config
|
return c.get(k, source)
|
||||||
return c.get(k, c.Raw)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetRaw looks up a configuration value by key and returns the value,
|
// GetRaw looks up a configuration value by key and returns the value,
|
||||||
|
@ -209,22 +211,25 @@ func (c *ResourceConfig) GetRaw(k string) (interface{}, bool) {
|
||||||
|
|
||||||
// IsComputed returns whether the given key is computed or not.
|
// IsComputed returns whether the given key is computed or not.
|
||||||
func (c *ResourceConfig) IsComputed(k string) bool {
|
func (c *ResourceConfig) IsComputed(k string) bool {
|
||||||
// First, check for pure computed key equality since that is fast
|
|
||||||
for _, ck := range c.ComputedKeys {
|
|
||||||
if ck == k {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// The next thing we do is check the config if we get a computed
|
// The next thing we do is check the config if we get a computed
|
||||||
// value out of it.
|
// value out of it.
|
||||||
v, ok := c.get(k, c.Config)
|
v, ok := c.get(k, c.Config)
|
||||||
_, okRaw := c.get(k, c.Raw)
|
if !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
// Both tests probably aren't needed anymore since we don't remove
|
// If value is nil, then it isn't computed
|
||||||
// values any longer. The latter is probably good enough since we
|
if v == nil {
|
||||||
// thread through that value now.
|
return false
|
||||||
return (!ok && okRaw) || v == config.UnknownVariableValue
|
}
|
||||||
|
|
||||||
|
// Test if the value contains an unknown value
|
||||||
|
var w unknownCheckWalker
|
||||||
|
if err := reflectwalk.Walk(v, &w); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return w.Unknown
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsSet checks if the key in the configuration is set. A key is set if
|
// IsSet checks if the key in the configuration is set. A key is set if
|
||||||
|
@ -238,10 +243,8 @@ func (c *ResourceConfig) IsSet(k string) bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, ck := range c.ComputedKeys {
|
if c.IsComputed(k) {
|
||||||
if ck == k {
|
return true
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, ok := c.Get(k); ok {
|
if _, ok := c.Get(k); ok {
|
||||||
|
@ -261,7 +264,6 @@ func (c *ResourceConfig) get(
|
||||||
var current interface{} = raw
|
var current interface{} = raw
|
||||||
var previous interface{} = nil
|
var previous interface{} = nil
|
||||||
for i, part := range parts {
|
for i, part := range parts {
|
||||||
println(fmt.Sprintf("%#v: %#v %T", part, current, current))
|
|
||||||
if current == nil {
|
if current == nil {
|
||||||
return nil, false
|
return nil, false
|
||||||
}
|
}
|
||||||
|
@ -289,15 +291,15 @@ func (c *ResourceConfig) get(
|
||||||
case reflect.Slice:
|
case reflect.Slice:
|
||||||
previous = current
|
previous = current
|
||||||
|
|
||||||
// If any value in a list is computed, this whole thing
|
|
||||||
// is computed and we can't read any part of it.
|
|
||||||
for i := 0; i < cv.Len(); i++ {
|
|
||||||
if v := cv.Index(i).Interface(); v == unknownValue() {
|
|
||||||
return v, false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if part == "#" {
|
if part == "#" {
|
||||||
|
// If any value in a list is computed, this whole thing
|
||||||
|
// is computed and we can't read any part of it.
|
||||||
|
for i := 0; i < cv.Len(); i++ {
|
||||||
|
if v := cv.Index(i).Interface(); v == unknownValue() {
|
||||||
|
return v, true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
current = cv.Len()
|
current = cv.Len()
|
||||||
} else {
|
} else {
|
||||||
i, err := strconv.ParseInt(part, 0, 0)
|
i, err := strconv.ParseInt(part, 0, 0)
|
||||||
|
@ -310,11 +312,6 @@ func (c *ResourceConfig) get(
|
||||||
current = cv.Index(int(i)).Interface()
|
current = cv.Index(int(i)).Interface()
|
||||||
}
|
}
|
||||||
case reflect.String:
|
case reflect.String:
|
||||||
// If we get the unknown value, return that
|
|
||||||
if current == unknownValue() {
|
|
||||||
return current, false
|
|
||||||
}
|
|
||||||
|
|
||||||
// This happens when map keys contain "." and have a common
|
// This happens when map keys contain "." and have a common
|
||||||
// prefix so were split as path components above.
|
// prefix so were split as path components above.
|
||||||
actualKey := strings.Join(parts[i-1:], ".")
|
actualKey := strings.Join(parts[i-1:], ".")
|
||||||
|
@ -328,23 +325,6 @@ func (c *ResourceConfig) get(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
switch cv := reflect.ValueOf(current); cv.Kind() {
|
|
||||||
case reflect.Slice:
|
|
||||||
// If any value in a list is computed, this whole thing
|
|
||||||
// is computed and we can't read any part of it.
|
|
||||||
for i := 0; i < cv.Len(); i++ {
|
|
||||||
if v := cv.Index(i).Interface(); v == unknownValue() {
|
|
||||||
return v, false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
// If the value is just the unknown value, then we don't
|
|
||||||
// know anything beyond here.
|
|
||||||
if current == unknownValue() {
|
|
||||||
return current, false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return current, true
|
return current, true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -364,3 +344,16 @@ func (c *ResourceConfig) interpolateForce() {
|
||||||
c.Raw = c.raw.RawMap()
|
c.Raw = c.raw.RawMap()
|
||||||
c.Config = c.raw.Config()
|
c.Config = c.raw.Config()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// unknownCheckWalker
|
||||||
|
type unknownCheckWalker struct {
|
||||||
|
Unknown bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *unknownCheckWalker) Primitive(v reflect.Value) error {
|
||||||
|
if v.Interface() == unknownValue() {
|
||||||
|
w.Unknown = true
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
|
@ -1,189 +0,0 @@
|
||||||
package terraform
|
|
||||||
|
|
||||||
import (
|
|
||||||
"reflect"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestResourceConfig_CheckSet(t *testing.T) {
|
|
||||||
cases := []struct {
|
|
||||||
Raw map[string]interface{}
|
|
||||||
Computed []string
|
|
||||||
Input []string
|
|
||||||
Errs bool
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
map[string]interface{}{
|
|
||||||
"foo": "bar",
|
|
||||||
},
|
|
||||||
nil,
|
|
||||||
[]string{"foo"},
|
|
||||||
false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
map[string]interface{}{
|
|
||||||
"foo": "bar",
|
|
||||||
},
|
|
||||||
nil,
|
|
||||||
[]string{"foo", "bar"},
|
|
||||||
true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
map[string]interface{}{
|
|
||||||
"foo": "bar",
|
|
||||||
},
|
|
||||||
[]string{"bar"},
|
|
||||||
[]string{"foo", "bar"},
|
|
||||||
false,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, tc := range cases {
|
|
||||||
rc := &ResourceConfig{
|
|
||||||
ComputedKeys: tc.Computed,
|
|
||||||
Raw: tc.Raw,
|
|
||||||
}
|
|
||||||
|
|
||||||
errs := rc.CheckSet(tc.Input)
|
|
||||||
if tc.Errs != (len(errs) > 0) {
|
|
||||||
t.Fatalf("bad: %d", i)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestResourceConfig_Get(t *testing.T) {
|
|
||||||
cases := []struct {
|
|
||||||
Raw map[string]interface{}
|
|
||||||
Computed []string
|
|
||||||
Input string
|
|
||||||
Output interface{}
|
|
||||||
OutputOk bool
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
map[string]interface{}{
|
|
||||||
"foo": "bar",
|
|
||||||
},
|
|
||||||
nil,
|
|
||||||
"foo",
|
|
||||||
"bar",
|
|
||||||
true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
map[string]interface{}{},
|
|
||||||
nil,
|
|
||||||
"foo",
|
|
||||||
nil,
|
|
||||||
false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
map[string]interface{}{
|
|
||||||
"foo": map[interface{}]interface{}{
|
|
||||||
"bar": "baz",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
nil,
|
|
||||||
"foo.bar",
|
|
||||||
"baz",
|
|
||||||
true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
map[string]interface{}{
|
|
||||||
"foo": []string{
|
|
||||||
"one",
|
|
||||||
"two",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
nil,
|
|
||||||
"foo.1",
|
|
||||||
"two",
|
|
||||||
true,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, tc := range cases {
|
|
||||||
rc := &ResourceConfig{
|
|
||||||
ComputedKeys: tc.Computed,
|
|
||||||
Raw: tc.Raw,
|
|
||||||
}
|
|
||||||
|
|
||||||
actual, ok := rc.Get(tc.Input)
|
|
||||||
if tc.OutputOk != ok {
|
|
||||||
t.Fatalf("bad ok: %d", i)
|
|
||||||
}
|
|
||||||
if !reflect.DeepEqual(tc.Output, actual) {
|
|
||||||
t.Fatalf("bad %d: %#v", i, actual)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestResourceConfig_IsSet(t *testing.T) {
|
|
||||||
cases := []struct {
|
|
||||||
Raw map[string]interface{}
|
|
||||||
Computed []string
|
|
||||||
Input string
|
|
||||||
Output bool
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
map[string]interface{}{
|
|
||||||
"foo": "bar",
|
|
||||||
},
|
|
||||||
nil,
|
|
||||||
"foo",
|
|
||||||
true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
map[string]interface{}{},
|
|
||||||
nil,
|
|
||||||
"foo",
|
|
||||||
false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
map[string]interface{}{},
|
|
||||||
[]string{"foo"},
|
|
||||||
"foo",
|
|
||||||
true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
map[string]interface{}{
|
|
||||||
"foo": map[interface{}]interface{}{
|
|
||||||
"bar": "baz",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
nil,
|
|
||||||
"foo.bar",
|
|
||||||
true,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, tc := range cases {
|
|
||||||
rc := &ResourceConfig{
|
|
||||||
ComputedKeys: tc.Computed,
|
|
||||||
Raw: tc.Raw,
|
|
||||||
}
|
|
||||||
|
|
||||||
actual := rc.IsSet(tc.Input)
|
|
||||||
if actual != tc.Output {
|
|
||||||
t.Fatalf("fail case: %d", i)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestResourceConfig_IsSet_nil(t *testing.T) {
|
|
||||||
var rc *ResourceConfig
|
|
||||||
|
|
||||||
if rc.IsSet("foo") {
|
|
||||||
t.Fatal("bad")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestResourceProviderFactoryFixed(t *testing.T) {
|
|
||||||
p := new(MockResourceProvider)
|
|
||||||
var f ResourceProviderFactory = ResourceProviderFactoryFixed(p)
|
|
||||||
actual, err := f()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("err: %s", err)
|
|
||||||
}
|
|
||||||
if actual != p {
|
|
||||||
t.Fatal("should be identical")
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -8,6 +8,7 @@ import (
|
||||||
"github.com/hashicorp/hil"
|
"github.com/hashicorp/hil"
|
||||||
"github.com/hashicorp/hil/ast"
|
"github.com/hashicorp/hil/ast"
|
||||||
"github.com/hashicorp/terraform/config"
|
"github.com/hashicorp/terraform/config"
|
||||||
|
"github.com/mitchellh/reflectwalk"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestInstanceInfo(t *testing.T) {
|
func TestInstanceInfo(t *testing.T) {
|
||||||
|
@ -58,6 +59,14 @@ func TestResourceConfigGet(t *testing.T) {
|
||||||
Value: nil,
|
Value: nil,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
Config: map[string]interface{}{
|
||||||
|
"foo": "bar",
|
||||||
|
},
|
||||||
|
Key: "foo",
|
||||||
|
Value: "bar",
|
||||||
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
Config: map[string]interface{}{
|
Config: map[string]interface{}{
|
||||||
"foo": "${var.foo}",
|
"foo": "${var.foo}",
|
||||||
|
@ -217,8 +226,6 @@ func TestResourceConfigGet(t *testing.T) {
|
||||||
|
|
||||||
// Test getting a key
|
// Test getting a key
|
||||||
t.Run(fmt.Sprintf("get-%d", i), func(t *testing.T) {
|
t.Run(fmt.Sprintf("get-%d", i), func(t *testing.T) {
|
||||||
t.Logf("Config: %#v", rc)
|
|
||||||
|
|
||||||
v, _ := rc.Get(tc.Key)
|
v, _ := rc.Get(tc.Key)
|
||||||
if !reflect.DeepEqual(v, tc.Value) {
|
if !reflect.DeepEqual(v, tc.Value) {
|
||||||
t.Fatalf("%d bad: %#v", i, v)
|
t.Fatalf("%d bad: %#v", i, v)
|
||||||
|
@ -297,20 +304,22 @@ func TestResourceConfigIsComputed(t *testing.T) {
|
||||||
Result: false,
|
Result: false,
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
/*
|
||||||
Name: "set count with computed elements",
|
{
|
||||||
Config: map[string]interface{}{
|
Name: "set count with computed elements",
|
||||||
"foo": "${var.foo}",
|
Config: map[string]interface{}{
|
||||||
},
|
"foo": "${var.foo}",
|
||||||
Vars: map[string]interface{}{
|
|
||||||
"foo": []string{
|
|
||||||
"a",
|
|
||||||
unknownValue(),
|
|
||||||
},
|
},
|
||||||
|
Vars: map[string]interface{}{
|
||||||
|
"foo": []string{
|
||||||
|
"a",
|
||||||
|
unknownValue(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Key: "foo.#",
|
||||||
|
Result: true,
|
||||||
},
|
},
|
||||||
Key: "foo.#",
|
*/
|
||||||
Result: true,
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
{
|
||||||
Name: "set count with computed elements",
|
Name: "set count with computed elements",
|
||||||
|
@ -385,6 +394,100 @@ func TestResourceConfigIsComputed(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestResourceConfigCheckSet(t *testing.T) {
|
||||||
|
cases := []struct {
|
||||||
|
Name string
|
||||||
|
Config map[string]interface{}
|
||||||
|
Vars map[string]interface{}
|
||||||
|
Input []string
|
||||||
|
Errs bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
Name: "computed basic",
|
||||||
|
Config: map[string]interface{}{
|
||||||
|
"foo": "${var.foo}",
|
||||||
|
},
|
||||||
|
Vars: map[string]interface{}{
|
||||||
|
"foo": unknownValue(),
|
||||||
|
},
|
||||||
|
Input: []string{"foo"},
|
||||||
|
Errs: false,
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
Name: "basic",
|
||||||
|
Config: map[string]interface{}{
|
||||||
|
"foo": "bar",
|
||||||
|
},
|
||||||
|
Vars: nil,
|
||||||
|
Input: []string{"foo"},
|
||||||
|
Errs: false,
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
Name: "basic with not set",
|
||||||
|
Config: map[string]interface{}{
|
||||||
|
"foo": "bar",
|
||||||
|
},
|
||||||
|
Vars: nil,
|
||||||
|
Input: []string{"foo", "bar"},
|
||||||
|
Errs: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
Name: "basic with one computed",
|
||||||
|
Config: map[string]interface{}{
|
||||||
|
"foo": "bar",
|
||||||
|
"bar": "${var.foo}",
|
||||||
|
},
|
||||||
|
Vars: map[string]interface{}{
|
||||||
|
"foo": unknownValue(),
|
||||||
|
},
|
||||||
|
Input: []string{"foo", "bar"},
|
||||||
|
Errs: false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, tc := range cases {
|
||||||
|
t.Run(fmt.Sprintf("%d-%s", i, tc.Name), func(t *testing.T) {
|
||||||
|
var rawC *config.RawConfig
|
||||||
|
if tc.Config != nil {
|
||||||
|
var err error
|
||||||
|
rawC, err = config.NewRawConfig(tc.Config)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("err: %s", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if tc.Vars != nil {
|
||||||
|
vs := make(map[string]ast.Variable)
|
||||||
|
for k, v := range tc.Vars {
|
||||||
|
hilVar, err := hil.InterfaceToVariable(v)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("%#v to var: %s", v, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
vs["var."+k] = hilVar
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := rawC.Interpolate(vs); err != nil {
|
||||||
|
t.Fatalf("err: %s", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rc := NewResourceConfig(rawC)
|
||||||
|
rc.interpolateForce()
|
||||||
|
|
||||||
|
t.Logf("Config: %#v", rc)
|
||||||
|
|
||||||
|
errs := rc.CheckSet(tc.Input)
|
||||||
|
if tc.Errs != (len(errs) > 0) {
|
||||||
|
t.Fatalf("bad: %#v", errs)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestResourceConfigDeepCopy_nil(t *testing.T) {
|
func TestResourceConfigDeepCopy_nil(t *testing.T) {
|
||||||
var nilRc *ResourceConfig
|
var nilRc *ResourceConfig
|
||||||
actual := nilRc.DeepCopy()
|
actual := nilRc.DeepCopy()
|
||||||
|
@ -428,6 +531,54 @@ func TestResourceConfigEqual_computedKeyOrder(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestUnknownCheckWalker(t *testing.T) {
|
||||||
|
cases := []struct {
|
||||||
|
Name string
|
||||||
|
Input interface{}
|
||||||
|
Result bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
"primitive",
|
||||||
|
42,
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
"primitive computed",
|
||||||
|
unknownValue(),
|
||||||
|
true,
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
"list",
|
||||||
|
[]interface{}{"foo", unknownValue()},
|
||||||
|
true,
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
"nested list",
|
||||||
|
[]interface{}{
|
||||||
|
"foo",
|
||||||
|
[]interface{}{unknownValue()},
|
||||||
|
},
|
||||||
|
true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, tc := range cases {
|
||||||
|
t.Run(fmt.Sprintf("%d-%s", i, tc.Name), func(t *testing.T) {
|
||||||
|
var w unknownCheckWalker
|
||||||
|
if err := reflectwalk.Walk(tc.Input, &w); err != nil {
|
||||||
|
t.Fatalf("err: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if w.Unknown != tc.Result {
|
||||||
|
t.Fatalf("bad: %v", w.Unknown)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func testResourceConfig(
|
func testResourceConfig(
|
||||||
t *testing.T, c map[string]interface{}) *ResourceConfig {
|
t *testing.T, c map[string]interface{}) *ResourceConfig {
|
||||||
raw, err := config.NewRawConfig(c)
|
raw, err := config.NewRawConfig(c)
|
||||||
|
|
Loading…
Reference in New Issue