Skip to content

Commit

Permalink
Support provider default labels for DCL resources
Browse files Browse the repository at this point in the history
  • Loading branch information
zli82016 committed Sep 11, 2023
1 parent 186acac commit fe90588
Show file tree
Hide file tree
Showing 6 changed files with 346 additions and 9 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,278 @@
package clouddeploy_test

import (
"testing"

"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"

"github.com/hashicorp/terraform-provider-google/google/acctest"
"github.com/hashicorp/terraform-provider-google/google/envvar"
)

func TestAccClouddeployTarget_withProviderDefaultLabels(t *testing.T) {
// The test failed if VCR testing is enabled, because the cached provider config is used.
// Any changes in the provider default labels will not be applied.
acctest.SkipIfVcr(t)
t.Parallel()

context := map[string]interface{}{
"project_name": envvar.GetTestProjectFromEnv(),
"region": envvar.GetTestRegionFromEnv(),
"random_suffix": acctest.RandString(t, 10),
}

acctest.VcrTest(t, resource.TestCase{
PreCheck: func() { acctest.AccTestPreCheck(t) },
ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t),
CheckDestroy: testAccCheckClouddeployTargetDestroyProducer(t),
Steps: []resource.TestStep{
{
Config: testAccClouddeployTarget_withProviderDefaultLabels(context),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr("google_clouddeploy_target.primary", "labels.%", "2"),
resource.TestCheckResourceAttr("google_clouddeploy_target.primary", "labels.my_first_label", "example-label-1"),
resource.TestCheckResourceAttr("google_clouddeploy_target.primary", "labels.my_second_label", "example-label-2"),

resource.TestCheckResourceAttr("google_clouddeploy_target.primary", "terraform_labels.%", "3"),
resource.TestCheckResourceAttr("google_clouddeploy_target.primary", "terraform_labels.my_first_label", "example-label-1"),
resource.TestCheckResourceAttr("google_clouddeploy_target.primary", "terraform_labels.my_second_label", "example-label-2"),
resource.TestCheckResourceAttr("google_clouddeploy_target.primary", "terraform_labels.default_key1", "default_value1"),

resource.TestCheckResourceAttr("google_clouddeploy_target.primary", "effective_labels.%", "3"),
),
},
{
ResourceName: "google_clouddeploy_target.primary",
ImportState: true,
ImportStateVerify: true,
ImportStateVerifyIgnore: []string{"labels", "terraform_labels", "annotations"},
},
{
Config: testAccClouddeployTarget_resourceLabelsOverridesProviderDefaultLabels(context),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr("google_clouddeploy_target.primary", "labels.%", "3"),
resource.TestCheckResourceAttr("google_clouddeploy_target.primary", "labels.my_first_label", "example-label-1"),
resource.TestCheckResourceAttr("google_clouddeploy_target.primary", "labels.my_second_label", "example-label-2"),
resource.TestCheckResourceAttr("google_clouddeploy_target.primary", "labels.default_key1", "value1"),

resource.TestCheckResourceAttr("google_clouddeploy_target.primary", "terraform_labels.%", "3"),
resource.TestCheckResourceAttr("google_clouddeploy_target.primary", "terraform_labels.my_first_label", "example-label-1"),
resource.TestCheckResourceAttr("google_clouddeploy_target.primary", "terraform_labels.my_second_label", "example-label-2"),
resource.TestCheckResourceAttr("google_clouddeploy_target.primary", "terraform_labels.default_key1", "value1"),

resource.TestCheckResourceAttr("google_clouddeploy_target.primary", "effective_labels.%", "3"),
),
},
{
ResourceName: "google_clouddeploy_target.primary",
ImportState: true,
ImportStateVerify: true,
ImportStateVerifyIgnore: []string{"labels", "terraform_labels", "annotations"},
},
{
Config: testAccClouddeployTarget_moveResourceLabelToProviderDefaultLabels(context),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr("google_clouddeploy_target.primary", "labels.%", "2"),
resource.TestCheckResourceAttr("google_clouddeploy_target.primary", "labels.my_first_label", "example-label-1"),
resource.TestCheckResourceAttr("google_clouddeploy_target.primary", "labels.default_key1", "value1"),

resource.TestCheckResourceAttr("google_clouddeploy_target.primary", "terraform_labels.%", "3"),
resource.TestCheckResourceAttr("google_clouddeploy_target.primary", "terraform_labels.my_first_label", "example-label-1"),
resource.TestCheckResourceAttr("google_clouddeploy_target.primary", "terraform_labels.my_second_label", "example-label-2"),
resource.TestCheckResourceAttr("google_clouddeploy_target.primary", "terraform_labels.default_key1", "value1"),

resource.TestCheckResourceAttr("google_clouddeploy_target.primary", "effective_labels.%", "3"),
),
},
{
ResourceName: "google_clouddeploy_target.primary",
ImportState: true,
ImportStateVerify: true,
ImportStateVerifyIgnore: []string{"labels", "terraform_labels", "annotations"},
},
{
Config: testAccClouddeployTarget_resourceLabelsOverridesProviderDefaultLabels(context),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr("google_clouddeploy_target.primary", "labels.%", "3"),
resource.TestCheckResourceAttr("google_clouddeploy_target.primary", "labels.my_first_label", "example-label-1"),
resource.TestCheckResourceAttr("google_clouddeploy_target.primary", "labels.my_second_label", "example-label-2"),
resource.TestCheckResourceAttr("google_clouddeploy_target.primary", "labels.default_key1", "value1"),

resource.TestCheckResourceAttr("google_clouddeploy_target.primary", "terraform_labels.%", "3"),
resource.TestCheckResourceAttr("google_clouddeploy_target.primary", "terraform_labels.my_first_label", "example-label-1"),
resource.TestCheckResourceAttr("google_clouddeploy_target.primary", "terraform_labels.my_second_label", "example-label-2"),
resource.TestCheckResourceAttr("google_clouddeploy_target.primary", "terraform_labels.default_key1", "value1"),

resource.TestCheckResourceAttr("google_clouddeploy_target.primary", "effective_labels.%", "3"),
),
},
{
ResourceName: "google_clouddeploy_target.primary",
ImportState: true,
ImportStateVerify: true,
ImportStateVerifyIgnore: []string{"labels", "terraform_labels", "annotations"},
},
{
Config: testAccClouddeployTarget_withoutLabels(context),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckNoResourceAttr("google_clouddeploy_target.primary", "labels.%"),
resource.TestCheckNoResourceAttr("google_clouddeploy_target.primary", "terraform_labels.%"),
resource.TestCheckNoResourceAttr("google_clouddeploy_target.primary", "effective_labels.%"),
),
},
{
ResourceName: "google_clouddeploy_target.primary",
ImportState: true,
ImportStateVerify: true,
ImportStateVerifyIgnore: []string{"labels", "terraform_labels", "annotations"},
},
},
})
}

func testAccClouddeployTarget_withProviderDefaultLabels(context map[string]interface{}) string {
return acctest.Nprintf(`
provider "google" {
default_labels = {
default_key1 = "default_value1"
}
}
resource "google_clouddeploy_target" "primary" {
location = "%{region}"
name = "tf-test-target%{random_suffix}"
deploy_parameters = {
deployParameterKey = "deployParameterValue"
}
description = "basic description"
gke {
cluster = "projects/%{project_name}/locations/%{region}/clusters/example-cluster-name"
}
project = "%{project_name}"
require_approval = false
annotations = {
my_first_annotation = "example-annotation-1"
my_second_annotation = "example-annotation-2"
}
labels = {
my_first_label = "example-label-1"
my_second_label = "example-label-2"
}
}
`, context)
}

func testAccClouddeployTarget_resourceLabelsOverridesProviderDefaultLabels(context map[string]interface{}) string {
return acctest.Nprintf(`
provider "google" {
default_labels = {
default_key1 = "default_value1"
}
}
resource "google_clouddeploy_target" "primary" {
location = "%{region}"
name = "tf-test-target%{random_suffix}"
deploy_parameters = {
deployParameterKey = "deployParameterValue"
}
description = "basic description"
gke {
cluster = "projects/%{project_name}/locations/%{region}/clusters/example-cluster-name"
}
project = "%{project_name}"
require_approval = false
annotations = {
my_first_annotation = "example-annotation-1"
my_second_annotation = "example-annotation-2"
}
labels = {
my_first_label = "example-label-1"
my_second_label = "example-label-2"
default_key1 = "value1"
}
}
`, context)
}

func testAccClouddeployTarget_moveResourceLabelToProviderDefaultLabels(context map[string]interface{}) string {
return acctest.Nprintf(`
provider "google" {
default_labels = {
default_key1 = "default_value1"
my_second_label = "example-label-2"
}
}
resource "google_clouddeploy_target" "primary" {
location = "%{region}"
name = "tf-test-target%{random_suffix}"
deploy_parameters = {
deployParameterKey = "deployParameterValue"
}
description = "basic description"
gke {
cluster = "projects/%{project_name}/locations/%{region}/clusters/example-cluster-name"
}
project = "%{project_name}"
require_approval = false
annotations = {
my_first_annotation = "example-annotation-1"
my_second_annotation = "example-annotation-2"
}
labels = {
my_first_label = "example-label-1"
default_key1 = "value1"
}
}
`, context)
}

func testAccClouddeployTarget_withoutLabels(context map[string]interface{}) string {
return acctest.Nprintf(`
resource "google_clouddeploy_target" "primary" {
location = "%{region}"
name = "tf-test-target%{random_suffix}"
deploy_parameters = {
deployParameterKey = "deployParameterValue"
}
description = "basic description"
gke {
cluster = "projects/%{project_name}/locations/%{region}/clusters/example-cluster-name"
}
project = "%{project_name}"
require_approval = false
annotations = {
my_first_annotation = "example-annotation-1"
my_second_annotation = "example-annotation-2"
}
}
`, context)
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,9 @@ func TestAccDataprocWorkflowTemplate_basic(t *testing.T) {
ImportState: true,
ImportStateVerify: true,
// The "labels" field in the state are decided by the configuration.
// During importing, as the configuration is unavailableafter, the "labels" field in the state will be empty.
// During importing, as the configuration is unavailable, the "labels" field in the state will be empty.
// So add the "labels" to the ImportStateVerifyIgnore list.
ImportStateVerifyIgnore: []string{"labels"},
ImportStateVerifyIgnore: []string{"labels", "terraform_labels"},
ResourceName: "google_dataproc_workflow_template.template",
},
},
Expand Down
38 changes: 37 additions & 1 deletion tpgtools/property.go
Original file line number Diff line number Diff line change
Expand Up @@ -452,6 +452,10 @@ func (p Property) IsResourceAnnotations() bool {
return p.Name() == "annotations" && p.parent == nil
}

func (p Property) ShouldShowUpInSamples() bool {
return (p.Settable && p.Name() != "effective_labels" && p.Name() != "effective_annotations") || p.IsResourceLabels() || p.IsResourceAnnotations()
}

// collapsedProperties returns the input list of properties with nested objects collapsed if needed.
func collapsedProperties(props []Property) (collapsed []Property) {
for _, v := range props {
Expand Down Expand Up @@ -897,7 +901,15 @@ func createPropertiesFromSchema(schema *openapi.Schema, typeFetcher *TypeFetcher
note := "**Note**: This field is non-authoritative, and will only manage the labels present in your configuration. " +
"Please refer to the field `effective_labels` for all of the labels present on the resource."
p.Description = fmt.Sprintf("%s\n\n%s", p.Description, note)
p.Settable = false
p.StateGetter = nil
p.ForceNew = false

props = append(props, build_effective_labels_field(p, resource, parent))

if p.IsResourceLabels() {
props = append(props, build_terraform_labels_field(p, resource, parent))
}
}

props = append(props, p)
Expand Down Expand Up @@ -929,14 +941,38 @@ func build_effective_labels_field(p Property, resource *Resource, parent *Proper
description := fmt.Sprintf("All of %s (key/value pairs) present on the resource in GCP, including the %s configured through Terraform, other clients and services.", p.title, p.title)
stateSetter := fmt.Sprintf("d.Set(%q, res.%s)", title, p.PackageName)

return Property{
effectiveLabels := Property{
title: title,
Type: p.Type,
Description: description,
resource: resource,
parent: parent,
Optional: false,
Computed: true,
ForceNew: p.ForceNew, // Add ForceNew property if labels field has it
PackageName: p.PackageName,
Settable: true,
StateSetter: &stateSetter,
}

stateGetter := effectiveLabels.DefaultStateGetter()
effectiveLabels.StateGetter = &stateGetter
return effectiveLabels
}

func build_terraform_labels_field(p Property, resource *Resource, parent *Property) Property {
title := fmt.Sprintf("terraform_%s", p.title)
description := fmt.Sprintf("The combination of %s configured directly on the resource and default %s configured on the provider.", p.title, p.title)
stateSetter := fmt.Sprintf("d.Set(%q, flatten%sTerraform%s(res.%s, d))", title, p.resource.PathType(), p.PackagePath(), p.PackageName)

return Property{
title: title,
Type: p.Type,
Description: description,
resource: resource,
parent: parent,
Computed: true,
PackageName: p.PackageName,
StateSetter: &stateSetter,
}
}
14 changes: 11 additions & 3 deletions tpgtools/resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -586,6 +586,14 @@ func createResource(schema *openapi.Schema, info *openapi.Info, typeFetcher *Typ
res.CustomizeDiff = cdiff.Functions
}

if res.HasLabels() {
res.CustomizeDiff = append(res.CustomizeDiff, "tpgresource.SetLabelsDiff")
}

if res.HasAnnotations() {
res.CustomizeDiff = append(res.CustomizeDiff, "tpgresource.SetAnnotationsDiff")
}

// ListFields
if parameters, ok := typeFetcher.doc.Paths["list"]; ok {
for _, param := range parameters.Parameters {
Expand Down Expand Up @@ -835,7 +843,7 @@ func (r *Resource) loadHandWrittenSamples() []Sample {
// During importing, as the configuration is unavailableafter, the "labels" and "annotations" fields in the state will be empty.
// So add the "labels" and the "annotations" fields to the ImportStateVerifyIgnore list.
if r.HasLabels() {
sample.IgnoreRead = append(sample.IgnoreRead, "labels")
sample.IgnoreRead = append(sample.IgnoreRead, "labels", "terraform_labels")
}

if r.HasAnnotations() {
Expand Down Expand Up @@ -930,10 +938,10 @@ func (r *Resource) loadDCLSamples() []Sample {
sample.TestSlug = RenderedString(sampleNameToTitleCase(*sample.Name).titlecase())

// The "labels" and "annotations" fields in the state are decided by the configuration.
// During importing, as the configuration is unavailableafter, the "labels" and "annotations" fields in the state will be empty.
// During importing, as the configuration is unavailable, the "labels" and "annotations" fields in the state will be empty.
// So add the "labels" and the "annotations" fields to the ImportStateVerifyIgnore list.
if r.HasLabels() {
sample.IgnoreRead = append(sample.IgnoreRead, "labels")
sample.IgnoreRead = append(sample.IgnoreRead, "labels", "terraform_labels")
}

if r.HasAnnotations() {
Expand Down
Loading

0 comments on commit fe90588

Please sign in to comment.