Skip to content

Commit

Permalink
Fix empty labels handling in Cluster resource (#2441)
Browse files Browse the repository at this point in the history
This fixes the empty label handling in the GCP Cluster resource.

In the fix for #2372
(#2386 and
pulumi/pulumi-terraform-bridge#2417) we did not
know that the labels property in GCP is sometimes overloaded, ex GCP
Custer.

For the Cluster resource, the GCP labels are under `resource_labels`,
not `labels`

This PR adds the logic to the empty labels fix and adds a regression
test.

fixes #2395
  • Loading branch information
VenelinMartinov authored Sep 25, 2024
1 parent 00557a0 commit c55b95a
Show file tree
Hide file tree
Showing 3 changed files with 68 additions and 59 deletions.
7 changes: 6 additions & 1 deletion provider/labels.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,11 @@ func fixEmptyLabels(_ context.Context, req shimv2.PlanStateEditRequest) (cty.Val
// effective_labels can include labels read from the cloud provider.
programLabels := property.Map{}

labelsPropertyName := "labels"
if req.TfToken == "google_container_cluster" {
labelsPropertyName = "resourceLabels"
}

// Apply default labels first.
if pConfig := resource.FromResourcePropertyValue(resource.NewProperty(req.ProviderConfig)); pConfig.IsMap() {
l := pConfig.AsMap()["defaultLabels"]
Expand All @@ -33,7 +38,7 @@ func fixEmptyLabels(_ context.Context, req shimv2.PlanStateEditRequest) (cty.Val
}

// Apply labels next, allowing labels to override defaultLabels.
if inputs, ok := (resource.PropertyPath{"labels"}.Get(resource.NewProperty(req.NewInputs))); ok {
if inputs, ok := (resource.PropertyPath{labelsPropertyName}.Get(resource.NewProperty(req.NewInputs))); ok {
if labels := resource.FromResourcePropertyValue(inputs); labels.IsMap() {
for k, v := range labels.AsMap() {
programLabels[k] = v
Expand Down
89 changes: 31 additions & 58 deletions provider/provider_yaml_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1132,28 +1132,10 @@ func TestFirestoreDatabaseAutoname(t *testing.T) {

func TestEmptyLabels(t *testing.T) {
tests := []struct {
program string
previewStdout autogold.Value
upOutputs autogold.Value
program string
upOutputs autogold.Value
}{
{"empty-label", autogold.Expect(`Previewing update (test):
+ pulumi:pulumi:Stack empty-label-test create
+ gcp:kms:KeyRing ring create
+ gcp:kms:CryptoKey key create
+ pulumi:pulumi:Stack empty-label-test create
Outputs:
effectiveLabels: [secret]
labels : {
empty : ""
static: "value"
}
pulumiLabels : [secret]
Resources:
+ 3 to create
`), autogold.Expect(auto.OutputMap{
{"empty-label", autogold.Expect(auto.OutputMap{
"effectiveLabels": auto.OutputValue{
Value: map[string]interface{}{
"empty": "",
Expand All @@ -1175,23 +1157,7 @@ Resources:
Secret: true,
},
})},
{"empty-alone-label", autogold.Expect(`Previewing update (test):
+ pulumi:pulumi:Stack empty-alone-label-test create
+ gcp:kms:KeyRing ring create
+ gcp:kms:CryptoKey key create
+ pulumi:pulumi:Stack empty-alone-label-test create
Outputs:
effectiveLabels: [secret]
labels : {
empty: ""
}
pulumiLabels : [secret]
Resources:
+ 3 to create
`), autogold.Expect(auto.OutputMap{
{"empty-alone-label", autogold.Expect(auto.OutputMap{
"effectiveLabels": auto.OutputValue{
Value: map[string]interface{}{
"empty": "",
Expand All @@ -1208,23 +1174,7 @@ Resources:
Secret: true,
},
})},
{"empty-default-label", autogold.Expect(`Previewing update (test):
+ pulumi:pulumi:Stack empty-default-label-test create
+ gcp:kms:KeyRing ring create
+ gcp:kms:CryptoKey key create
+ pulumi:pulumi:Stack empty-default-label-test create
Outputs:
effectiveLabels: [secret]
labels : {
static: "value"
}
pulumiLabels : [secret]
Resources:
+ 3 to create
`), autogold.Expect(auto.OutputMap{
{"empty-default-label", autogold.Expect(auto.OutputMap{
"effectiveLabels": auto.OutputValue{
Value: map[string]interface{}{
"empty-default": "",
Expand All @@ -1243,6 +1193,32 @@ Resources:
Secret: true,
},
})},
{
// Cluster overloads the labels field and instead uses resourceLabels for GCP labels.
"empty-label-cluster",
autogold.Expect(auto.OutputMap{
"effectiveLabels": auto.OutputValue{
Value: map[string]interface{}{
"environment": "dev",
"goog-pulumi-provisioned": "true",
"test": "",
},
Secret: true,
},
"labels": auto.OutputValue{Value: map[string]interface{}{
"environment": "dev",
"test": "",
}},
"pulumiLabels": auto.OutputValue{
Value: map[string]interface{}{
"environment": "dev",
"goog-pulumi-provisioned": "true",
"test": "",
},
Secret: true,
},
}),
},
}

for _, tt := range tests {
Expand All @@ -1253,9 +1229,6 @@ Resources:
proj := getProject()
pt.SetConfig("gcpProj", proj)

previewResult := pt.Preview(optpreview.SuppressProgress())
tt.previewStdout.Equal(t, previewResult.StdOut)

upResult := pt.Up(optup.SuppressProgress())
tt.upOutputs.Equal(t, upResult.Outputs)

Expand Down
31 changes: 31 additions & 0 deletions provider/test-programs/empty-label-cluster/Pulumi.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
name: empty-label-cluster
runtime: yaml
resources:
random-account-id:
type: random:RandomString
properties:
length: 10
special: false
upper: false
number: false
serviceAccount:
type: gcp:serviceaccount:Account
properties:
accountId: ${random-account-id.result}
primary:
type: gcp:container:Cluster
properties:
location: us-central1
# We can't create a cluster with no node pool defined, but we want to only use
# separately managed node pools. So we create the smallest possible default
# node pool and immediately delete it.
removeDefaultNodePool: true
initialNodeCount: 1
deletionProtection: false
resourceLabels:
environment: "dev"
test: ""
outputs:
labels: ${primary.resourceLabels}
effectiveLabels: ${primary.effectiveLabels}
pulumiLabels: ${primary.pulumiLabels}

0 comments on commit c55b95a

Please sign in to comment.