diff --git a/.changelog/2800.txt b/.changelog/2800.txt new file mode 100644 index 0000000000..d32da3bb00 --- /dev/null +++ b/.changelog/2800.txt @@ -0,0 +1,3 @@ +```release-note:bug +`resource/kubernetes_manifest`: fix an issue with the provider panic'ing when working with complex manifests +``` diff --git a/manifest/morph/scaffold.go b/manifest/morph/scaffold.go index 853a3bc4c8..c4e10ac190 100644 --- a/manifest/morph/scaffold.go +++ b/manifest/morph/scaffold.go @@ -75,6 +75,7 @@ func DeepUnknown(t tftypes.Type, v tftypes.Value, p *tftypes.AttributePath) (tft if err != nil { return tftypes.Value{}, p.NewError(err) } + ntypes := make([]tftypes.Type, len(atts)) for i, et := range atts { np := p.WithElementKeyInt(i) nv, err := DeepUnknown(et, vals[i], np) @@ -82,8 +83,9 @@ func DeepUnknown(t tftypes.Type, v tftypes.Value, p *tftypes.AttributePath) (tft return tftypes.Value{}, np.NewError(err) } vals[i] = nv + ntypes[i] = nv.Type() } - return tftypes.NewValue(tftypes.Tuple{ElementTypes: atts}, vals), nil + return tftypes.NewValue(tftypes.Tuple{ElementTypes: ntypes}, vals), nil case t.Is(tftypes.List{}) || t.Is(tftypes.Set{}): if v.IsNull() { return tftypes.NewValue(t, tftypes.UnknownValue), nil diff --git a/manifest/morph/scaffold_test.go b/manifest/morph/scaffold_test.go index 63636fb952..151fde026a 100644 --- a/manifest/morph/scaffold_test.go +++ b/manifest/morph/scaffold_test.go @@ -299,6 +299,61 @@ func TestDeepUnknown(t *testing.T) { }, ), }, + "tuple-with-dynamic": { + In: deepUnknownTestSampleInput{ + T: tftypes.Tuple{ + ElementTypes: []tftypes.Type{ + tftypes.DynamicPseudoType, + }, + }, + V: tftypes.NewValue( + tftypes.Tuple{ + ElementTypes: []tftypes.Type{ + tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "name": tftypes.String, + }, + }, + }, + }, + []tftypes.Value{ + tftypes.NewValue( + tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "name": tftypes.String, + }, + }, + map[string]tftypes.Value{ + "name": tftypes.NewValue(tftypes.String, "test"), + }, + ), + }, + ), + }, + Out: tftypes.NewValue( + tftypes.Tuple{ + ElementTypes: []tftypes.Type{ + tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "name": tftypes.String, + }, + }, + }, + }, + []tftypes.Value{ + tftypes.NewValue( + tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "name": tftypes.String, + }, + }, + map[string]tftypes.Value{ + "name": tftypes.NewValue(tftypes.String, "test"), + }, + ), + }, + ), + }, } for n, s := range samples { t.Run(n, func(t *testing.T) { diff --git a/manifest/test/acceptance/strategic_patch_test.go b/manifest/test/acceptance/strategic_patch_test.go new file mode 100644 index 0000000000..9b6c18dc90 --- /dev/null +++ b/manifest/test/acceptance/strategic_patch_test.go @@ -0,0 +1,81 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +//go:build acceptance +// +build acceptance + +package acceptance + +import ( + "context" + "fmt" + "strings" + "testing" + "time" + + "github.com/hashicorp/go-hclog" + "github.com/hashicorp/terraform-provider-kubernetes/manifest/provider" +) + +func TestKubernetesManifest_StrategicPatch(t *testing.T) { + ctx := context.Background() + + reattachInfo, err := provider.ServeTest(ctx, hclog.Default(), t) + if err != nil { + t.Errorf("Failed to create provider instance: %q", err) + } + + kind := "Strategic" + plural := "strategics" + group := "terraform.io" + version := "v1" + groupVersion := group + "/" + version + crd := fmt.Sprintf("%s.%s", plural, group) + + name := strings.ToLower(randName()) + namespace := "default" + + tfvars := TFVARS{ + "name": name, + "namespace": namespace, + "kind": kind, + "plural": plural, + "group": group, + "group_version": groupVersion, + "cr_version": version, + } + + step1 := tfhelper.RequireNewWorkingDir(ctx, t) + step1.SetReattachInfo(ctx, reattachInfo) + defer func() { + step1.Destroy(ctx) + step1.Close() + k8shelper.AssertResourceDoesNotExist(t, "apiextensions.k8s.io/v1", "customresourcedefinitions", crd) + }() + + crdTfConfig := loadTerraformConfig(t, "StrategicPatch/crd.tf", tfvars) + step1.SetConfig(ctx, string(crdTfConfig)) + step1.Init(ctx) + step1.Apply(ctx) + k8shelper.AssertResourceExists(t, "apiextensions.k8s.io/v1", "customresourcedefinitions", crd) + + // wait for API to finish ingesting the CRD + time.Sleep(5 * time.Second) //lintignore:R018 + + reattachInfo2, err := provider.ServeTest(ctx, hclog.Default(), t) + if err != nil { + t.Errorf("Failed to create additional provider instance: %q", err) + } + step2 := tfhelper.RequireNewWorkingDir(ctx, t) + step2.SetReattachInfo(ctx, reattachInfo2) + defer func() { + step2.Destroy(ctx) + step2.Close() + k8shelper.AssertResourceDoesNotExist(t, groupVersion, kind, name) + }() + + tfconfig := loadTerraformConfig(t, "StrategicPatch/strategic_patch.tf", tfvars) + step2.SetConfig(ctx, string(tfconfig)) + step2.Init(ctx) + step2.Apply(ctx) +} diff --git a/manifest/test/acceptance/testdata/StrategicPatch/crd.tf b/manifest/test/acceptance/testdata/StrategicPatch/crd.tf new file mode 100644 index 0000000000..356f382190 --- /dev/null +++ b/manifest/test/acceptance/testdata/StrategicPatch/crd.tf @@ -0,0 +1,58 @@ +resource "kubernetes_manifest" "crd" { + manifest = { + apiVersion = "apiextensions.k8s.io/v1" + kind = "CustomResourceDefinition" + metadata = { + name = "${var.plural}.${var.group}" + } + spec = { + group = "${var.group}" + names = { + kind = "${var.kind}" + plural = "${var.plural}" + } + scope = "Namespaced" + versions = [ + { + name = "${var.cr_version}" + served = true + storage = true + schema = { + openAPIV3Schema = { + type = "object" + properties = { + spec = { + type = "object" + properties = { + patchStrategicMerge = { + type = "object" + properties = { + containers = { + type = "array" + "x-kubernetes-list-type" = "map" + "x-kubernetes-list-map-keys" = ["name"] + items = { + type = "object" + required = ["name"] + properties = { + name = { + type = "string" + } + image = { + type = "string" + } + } + } + } + } + } + } + } + } + } + } + }, + ] + } + } +} diff --git a/manifest/test/acceptance/testdata/StrategicPatch/strategic_patch.tf b/manifest/test/acceptance/testdata/StrategicPatch/strategic_patch.tf new file mode 100644 index 0000000000..8dd8e48a50 --- /dev/null +++ b/manifest/test/acceptance/testdata/StrategicPatch/strategic_patch.tf @@ -0,0 +1,20 @@ +resource "kubernetes_manifest" "test" { + manifest = { + apiVersion = "${var.group_version}" + kind = "${var.kind}" + metadata = { + name = "${var.name}" + namespace = "${var.namespace}" + } + spec = { + patchStrategicMerge = { + containers = [ + { + name = "nginx" + image = "nginx:latest" + }, + ] + } + } + } +} \ No newline at end of file