Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support provider default labels for DCL resources #8893

Merged
merged 1 commit into from
Sep 13, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
37 changes: 36 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,14 @@ 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

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 +940,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