From e2444cd025d13c72731d316f2c3c73f0565597f0 Mon Sep 17 00:00:00 2001 From: Julien DOCHE Date: Thu, 3 Nov 2022 14:42:21 +0100 Subject: [PATCH] Do not remove server side fields for datasources (#1802) Do not remove server side fields for datasources It does not make sense to delete server side fields for datasources, the user should be responsible for not creating loops which would cause a perpetual diff. This allows reading the status field for the generic `kubernetes_resource` datasource. Thus this PR Fixes this issue : https://github.com/hashicorp/terraform-provider-kubernetes/issues/1699 Signed-off-by: Julien DOCHE --- .changelog/1802.txt | 3 + manifest/provider/datasource.go | 5 +- .../datasource_resource_status_test.go | 83 +++++++++++++++++++ .../datasource-resource-status/step1.tf | 34 ++++++++ .../datasource-resource-status/step2.tf | 8 ++ .../datasource-resource-status/variables.tf | 16 ++++ 6 files changed, 146 insertions(+), 3 deletions(-) create mode 100644 .changelog/1802.txt create mode 100644 manifest/test/acceptance/datasource_resource_status_test.go create mode 100644 manifest/test/acceptance/testdata/datasource-resource-status/step1.tf create mode 100644 manifest/test/acceptance/testdata/datasource-resource-status/step2.tf create mode 100644 manifest/test/acceptance/testdata/datasource-resource-status/variables.tf diff --git a/.changelog/1802.txt b/.changelog/1802.txt new file mode 100644 index 0000000000..41a5e2d6d3 --- /dev/null +++ b/.changelog/1802.txt @@ -0,0 +1,3 @@ +```release-note:enhancement +The kubernetes status field is now available in the `kubernetes_resource` datasource +``` diff --git a/manifest/provider/datasource.go b/manifest/provider/datasource.go index f53b0c8fbd..dec39ed452 100644 --- a/manifest/provider/datasource.go +++ b/manifest/provider/datasource.go @@ -105,7 +105,7 @@ func (s *RawProviderServer) ReadDataSource(ctx context.Context, req *tfprotov5.R } rcl := client.Resource(gvr) - objectType, th, err := s.TFTypeFromOpenAPI(ctx, gvk, false) + objectType, th, err := s.TFTypeFromOpenAPI(ctx, gvk, true) if err != nil { resp.Diagnostics = append(resp.Diagnostics, &tfprotov5.Diagnostic{ Severity: tfprotov5.DiagnosticSeverityError, @@ -148,8 +148,7 @@ func (s *RawProviderServer) ReadDataSource(ctx context.Context, req *tfprotov5.R return resp, nil } - fo := RemoveServerSideFields(res.Object) - nobj, err := payload.ToTFValue(fo, objectType, th, tftypes.NewAttributePath()) + nobj, err := payload.ToTFValue(res.Object, objectType, th, tftypes.NewAttributePath()) if err != nil { resp.Diagnostics = append(resp.Diagnostics, &tfprotov5.Diagnostic{ Severity: tfprotov5.DiagnosticSeverityError, diff --git a/manifest/test/acceptance/datasource_resource_status_test.go b/manifest/test/acceptance/datasource_resource_status_test.go new file mode 100644 index 0000000000..98dc3aa139 --- /dev/null +++ b/manifest/test/acceptance/datasource_resource_status_test.go @@ -0,0 +1,83 @@ +//go:build acceptance +// +build acceptance + +package acceptance + +import ( + "context" + "testing" + + "github.com/hashicorp/go-hclog" + "github.com/hashicorp/terraform-provider-kubernetes/manifest/provider" + "github.com/hashicorp/terraform-provider-kubernetes/manifest/test/helper/kubernetes" + + tfstatehelper "github.com/hashicorp/terraform-provider-kubernetes/manifest/test/helper/state" +) + +func TestDataSourceKubernetesResourceStatus_Deployment(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) + } + + name := randName() + namespace := randName() + + k8shelper.CreateNamespace(t, namespace) + defer k8shelper.DeleteResource(t, namespace, kubernetes.NewGroupVersionResource("v1", "namespaces")) + + // STEP 1: Create a Deployment to use as a data source + tf := tfhelper.RequireNewWorkingDir(ctx, t) + tf.SetReattachInfo(ctx, reattachInfo) + defer func() { + tf.Destroy(ctx) + tf.Close() + k8shelper.AssertNamespacedResourceDoesNotExist(t, "apps/v1", "deployments", namespace, name) + }() + + tfvars := TFVARS{ + "name": name, + "namespace": namespace, + } + tfconfig := loadTerraformConfig(t, "datasource-resource-status/step1.tf", tfvars) + tf.SetConfig(ctx, tfconfig) + tf.Init(ctx) + tf.Apply(ctx) + + k8shelper.AssertNamespacedResourceExists(t, "apps/v1", "deployments", namespace, name) + + state, err := tf.State(ctx) + if err != nil { + t.Fatalf("Failed to retrieve terraform state: %q", err) + } + tfstate := tfstatehelper.NewHelper(state) + + // STEP 2: Read the Deployment from step 1 using a kubernetes_resource data source + 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.AssertNamespacedResourceDoesNotExist(t, "apps/v1", "deployments", namespace, name) + }() + + tfconfig = loadTerraformConfig(t, "datasource-resource-status/step2.tf", tfvars) + step2.SetConfig(ctx, string(tfconfig)) + step2.Init(ctx) + step2.Apply(ctx) + + s2, err := step2.State(ctx) + if err != nil { + t.Fatalf("Failed to retrieve terraform state: %q", err) + } + tfstate = tfstatehelper.NewHelper(s2) + + // check that the data source has the status field defined + tfstate.AssertAttributeNotEmpty(t, "data.kubernetes_resource.test_deploy.object.status") +} diff --git a/manifest/test/acceptance/testdata/datasource-resource-status/step1.tf b/manifest/test/acceptance/testdata/datasource-resource-status/step1.tf new file mode 100644 index 0000000000..385ac76097 --- /dev/null +++ b/manifest/test/acceptance/testdata/datasource-resource-status/step1.tf @@ -0,0 +1,34 @@ +resource "kubernetes_manifest" "test_deploy" { + manifest = { + "apiVersion" = "apps/v1" + "kind" = "Deployment" + "metadata" = { + "name" = var.name + "namespace" = var.namespace + } + "spec" = { + "selector" = { + "matchLabels" = { + "test" = "MyExampleApp" + } + } + + "template" = { + "metadata" = { + "labels" = { + "test" = "MyExampleApp" + } + } + + "spec" = { + "containers" = [ + { + "image" = "nginx:1.21.6" + "name" = "example" + } + ] + } + } + } + } +} diff --git a/manifest/test/acceptance/testdata/datasource-resource-status/step2.tf b/manifest/test/acceptance/testdata/datasource-resource-status/step2.tf new file mode 100644 index 0000000000..f48a415db5 --- /dev/null +++ b/manifest/test/acceptance/testdata/datasource-resource-status/step2.tf @@ -0,0 +1,8 @@ +data "kubernetes_resource" "test_deploy" { + api_version = "apps/v1" + kind = "Deployment" + metadata { + name = var.name + namespace = var.namespace + } +} diff --git a/manifest/test/acceptance/testdata/datasource-resource-status/variables.tf b/manifest/test/acceptance/testdata/datasource-resource-status/variables.tf new file mode 100644 index 0000000000..b6c33f2d02 --- /dev/null +++ b/manifest/test/acceptance/testdata/datasource-resource-status/variables.tf @@ -0,0 +1,16 @@ +# These variable declarations are only used for interactive testing. +# The test code will template in different variable declarations with a default value when running the test. +# +# To set values for interactive runs, create a var-file and set values in it. +# If the name of the var-file ends in '.auto.tfvars' (e.g. myvalues.auto.tfvars) +# it will be automatically picked up and used by Terraform. +# +# DO NOT check in any files named *.auto.tfvars when making changes to tests. + +variable "name" { + type = string +} + +variable "namespace" { + type = string +}