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

Fixes empty tag handling #2944

Merged
merged 40 commits into from
Nov 7, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
11e76ba
Add more tags tests
t0yv0 Oct 27, 2023
2c11d78
Remove temporary file
t0yv0 Oct 27, 2023
f523236
Use VPC not DefaultVPC to parallelize
t0yv0 Oct 31, 2023
e24b689
Patch upstream to disable SetTagsDiff and ensure tags_all is not comp…
t0yv0 Oct 31, 2023
2ce7626
Copy tags to tagsAl
t0yv0 Oct 31, 2023
57c7c50
Relax asserts about tagsAll being computed
t0yv0 Oct 31, 2023
2027011
Extend the matrix for testing tags transitions
t0yv0 Oct 31, 2023
490b648
Merge go.mod and recreate go.sum
t0yv0 Oct 31, 2023
5b70a74
Fix error handling in shim
t0yv0 Oct 31, 2023
b13b6db
Avoid race conditions when changing tagsAll Computed value
t0yv0 Nov 1, 2023
1c466a7
Fix typo in error message
t0yv0 Nov 1, 2023
6aa101d
Verify tags_all is no longer computed
t0yv0 Nov 1, 2023
739df3a
Avoid race conditions when changing tagsAll Computed value
t0yv0 Nov 1, 2023
f239261
Fix tags tests
t0yv0 Nov 1, 2023
8b4a5e3
Copy tags schema to tags_all schema, remove exceptions
t0yv0 Nov 1, 2023
f7a749f
Compensate for tags_all schema changes at Pulumi level
t0yv0 Nov 1, 2023
043075f
make tfgen
t0yv0 Nov 1, 2023
61d6300
tagsAll is now a copy of tags, so unknowns propagate
t0yv0 Nov 2, 2023
22d6cbb
Use schema-based secrets handling for tagsAll; fix diffs
t0yv0 Nov 2, 2023
5f530d3
Pin bridge version with the fixes
t0yv0 Nov 2, 2023
b082129
Unknown tagsAll are now secret+unknown
t0yv0 Nov 3, 2023
5c5f8b9
Make tags_all attribute no longer computed for Plugin Framework
t0yv0 Nov 3, 2023
e0f1338
Revert patch
t0yv0 Nov 3, 2023
e2dfcbf
Make tags_all attribute no longer computed for Plugin Framework
t0yv0 Nov 3, 2023
97207cf
Assert tags_all is never Computed
t0yv0 Nov 3, 2023
777962b
Invert the assert - tags_all must not be Computed
t0yv0 Nov 3, 2023
c9a996b
Do not compute tagsAll for PF resources at TF level
t0yv0 Nov 3, 2023
b7a2498
Do not compute tagsAll for PF resources at TF level
t0yv0 Nov 3, 2023
0d03c22
Bump bridge reference
t0yv0 Nov 3, 2023
5a1bdb7
Add PF resources to the tags test matrix
t0yv0 Nov 3, 2023
24be8a1
Verify bucket tags via GetBucketTagging
t0yv0 Nov 6, 2023
c8e4051
Fix empty tag case
t0yv0 Nov 6, 2023
3bf53fc
Add VPC tags cross-check
t0yv0 Nov 6, 2023
d959067
Cross-check appconfig App tags
t0yv0 Nov 6, 2023
f789c48
Do not compute tags_all at TF level
t0yv0 Nov 6, 2023
9533f0c
PR feedback on when to run the script
t0yv0 Nov 7, 2023
d974073
PR feedback: remove yes/no
t0yv0 Nov 7, 2023
5b24f36
Tidy
t0yv0 Nov 7, 2023
6d94b7e
Add legacy bucket to tags tests
t0yv0 Nov 7, 2023
4505347
Add bucket checks
t0yv0 Nov 7, 2023
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
276 changes: 276 additions & 0 deletions examples/examples_go_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,19 @@
package examples

import (
"encoding/json"
"fmt"
"math/rand"
"path/filepath"
"strings"
"testing"

"github.com/aws/aws-sdk-go/aws/session"
appconfigsdk "github.com/aws/aws-sdk-go/service/appconfig"
ec2sdk "github.com/aws/aws-sdk-go/service/ec2"
s3sdk "github.com/aws/aws-sdk-go/service/s3"
"github.com/stretchr/testify/require"

"github.com/pulumi/pulumi/pkg/v3/testing/integration"
)

Expand All @@ -22,3 +32,269 @@ func TestAccWebserverGo(t *testing.T) {

integration.ProgramTest(t, &test)
}

func TestTagsCombinationsGo(t *testing.T) {
type testCase struct {
name string
s1 tagsState
s2 tagsState
}

testCases := []testCase{
{
"maintain a simple tag",
tagsState{ResourceTags: map[string]string{"x": "s"}},
tagsState{ResourceTags: map[string]string{"x": "s"}},
},
{
"add a simple tag",
tagsState{},
tagsState{ResourceTags: map[string]string{"x": "s"}},
},
{
"add an empty tag",
tagsState{},
tagsState{ResourceTags: map[string]string{"x": ""}},
},
{
"replace tags with empty",
tagsState{
DefaultTags: map[string]string{"x": "s"},
ResourceTags: nil,
},
tagsState{
DefaultTags: map[string]string{"x": "", "y": "s"},
ResourceTags: map[string]string{"x": "", "y": ""},
},
},
{
"maintain tags",
tagsState{
DefaultTags: map[string]string{"x": "s", "y": "s"},
ResourceTags: map[string]string{"x": "s", "y": "s"},
},
tagsState{
DefaultTags: map[string]string{"x": "s", "y": "s"},
ResourceTags: map[string]string{"x": "s", "y": "s"},
},
},
}

for _, tc := range testCases {
tc := tc
t.Run(tc.name, func(t *testing.T) {
t.Parallel()
tc.s1.validateTransitionTo(t, tc.s2)
})
}
}

func TestRandomTagsCombinationsGo(t *testing.T) {
tagValues := []string{"", "s"} // empty values are conflated with unknowns in TF internals, must test

tagsValues := []map[string]string{
nil,
{},
}

for _, tag := range tagValues {
m := map[string]string{"x": tag}
tagsValues = append(tagsValues, m)
}

for _, tag1 := range tagValues {
for _, tag2 := range tagValues {
m := map[string]string{
"x": tag1,
"y": tag2,
}
tagsValues = append(tagsValues, m)
}
}

states := []tagsState{}

for _, tags1 := range tagsValues {
for _, tags2 := range tagsValues {
states = append(states, tagsState{
DefaultTags: tags1,
ResourceTags: tags2,
})
}
}

t.Logf("total state space: %v states", len(states))
t.Logf("random-sampling 100 state transitions")

for i := 0; i < 100; i++ {
t.Run(fmt.Sprintf("test-%d", i), func(t *testing.T) {
t.Parallel()
i := rand.Intn(len(states))
j := rand.Intn(len(states))
state1, state2 := states[i], states[j]
state1.validateTransitionTo(t, state2)
})
}
}

type tagsState struct {
DefaultTags map[string]string `json:"defaultTags"`
ResourceTags map[string]string `json:"resourceTags"`
}

func (st tagsState) serialize(t *testing.T) string {
bytes, err := json.Marshal(st)
require.NoError(t, err)
return string(bytes)
}

func (st tagsState) validateTransitionTo(t *testing.T, st2 tagsState) {
t.Logf("state1 = %v", st.serialize(t))
t.Logf("state2 = %v", st2.serialize(t))

integration.ProgramTest(t, &integration.ProgramTestOptions{
Dir: "tags-combinations-go",
ExtraRuntimeValidation: st.validateStateResult(1),
EditDirs: []integration.EditDir{{
Dir: filepath.Join("tags-combinations-go", "step1"),
Additive: true,
ExtraRuntimeValidation: st2.validateStateResult(2),
}},
Config: map[string]string{
"aws:region": getEnvRegion(t),
"state1": st.serialize(t),
"state2": st2.serialize(t),
},
Quick: true,
DestroyOnCleanup: true,
})
}

func (st tagsState) expectedTags() map[string]string {
r := map[string]string{}
for k, v := range st.DefaultTags {
r[k] = v
}
for k, v := range st.ResourceTags {
r[k] = v
}
return r
}

func (st tagsState) validateStateResult(phase int) func(
t *testing.T,
stack integration.RuntimeValidationStackInfo,
) {
return func(t *testing.T, stack integration.RuntimeValidationStackInfo) {
legacyBucketTags := fetchBucketTags(t, stack.Outputs["legacy-bucket-name"].(string))
actualBucketTags := fetchBucketTags(t, stack.Outputs["bucket-name"].(string))
actualVpcTags := fetchVpcTags(t, stack.Outputs["vpc-id"].(string))
appTags := fetchAppConfigTags(t, stack.Outputs["appconfig-app-arn"].(string))
for k, v := range stack.Outputs {
switch k {
case "bucket-name", "legacy-bucket-name", "vpc-id", "appconfig-app-arn":
continue
}

actualTagsJSON := v.(string)
var actualTags map[string]string
err := json.Unmarshal([]byte(actualTagsJSON), &actualTags)
require.NoError(t, err)
t.Logf("phase: %d", phase)
t.Logf("state: %v", st.serialize(t))
require.Equalf(t, st.expectedTags(), actualTags, "key=%s", k)
t.Logf("key=%s tags are as expected: %v", k, actualTagsJSON)

if k == "bucket" {
require.Equalf(t, st.expectedTags(), actualBucketTags, "bad bucket tags")
}
if k == "legacy-bucket" {
require.Equalf(t, st.expectedTags(), legacyBucketTags, "bad legacy bucket tags")
}
if k == "vpc" {
require.Equalf(t, st.expectedTags(), actualVpcTags, "bad vpc tags")
}
if k == "appconfig-app" {
require.Equalf(t, st.expectedTags(), appTags, "bad appconfig app tags")
}
}
}
}

func fetchBucketTags(t *testing.T, awsBucket string) map[string]string {
sess := session.Must(session.NewSessionWithOptions(session.Options{
SharedConfigState: session.SharedConfigEnable,
}))

client := s3sdk.New(sess)

input := &s3sdk.GetBucketTaggingInput{
Bucket: &awsBucket,
}

result, err := client.GetBucketTagging(input)
if err != nil && strings.Contains(err.Error(), "NoSuchTagSet") {
return map[string]string{}
}
require.NoError(t, err)

tags := make(map[string]string)
for _, tag := range result.TagSet {
tags[*tag.Key] = *tag.Value
}

return tags
}

func fetchVpcTags(t *testing.T, vpc string) map[string]string {
ptr := func(x string) *string {
return &x
}

sess := session.Must(session.NewSessionWithOptions(session.Options{
SharedConfigState: session.SharedConfigEnable,
}))

client := ec2sdk.New(sess)

tagsOut, err := client.DescribeTags(&ec2sdk.DescribeTagsInput{
Filters: []*ec2sdk.Filter{
{
Name: ptr("resource-type"),
Values: []*string{
ptr("vpc"),
},
},
{
Name: ptr("resource-id"),
Values: []*string{
ptr(vpc),
},
},
},
})
require.NoError(t, err)

res := map[string]string{}
for _, tag := range tagsOut.Tags {
res[*tag.Key] = *tag.Value
}

return res
}

func fetchAppConfigTags(t *testing.T, arn string) map[string]string {
sess := session.Must(session.NewSessionWithOptions(session.Options{
SharedConfigState: session.SharedConfigEnable,
}))
client := appconfigsdk.New(sess)
out, err := client.ListTagsForResource(&appconfigsdk.ListTagsForResourceInput{
ResourceArn: &arn,
})
require.NoError(t, err)
res := map[string]string{}
for k, v := range out.Tags {
res[k] = *v
}
return res
}
14 changes: 9 additions & 5 deletions examples/examples_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -421,11 +421,15 @@ func TestRegressUnknownTags(t *testing.T) {
"name": "example-ng-tags-ng2-nodeSecurityGroup-8012419",
"revokeRulesOnDelete": true,
"vpcId": "vpc-4b82e033",
"tags": "04da6b54-80e4-46f7-96ec-b56ff0331ba9"
}
}
}
]
"tags": "04da6b54-80e4-46f7-96ec-b56ff0331ba9",
"tagsAll": {
"4dabf18193072939515e22adb298388d": "1b47061264138c4ac30d75fd1eb44270",
"value": "04da6b54-80e4-46f7-96ec-b56ff0331ba9"
}
}
}
}
]
`
replay(t, repro)
}
Expand Down
4 changes: 2 additions & 2 deletions examples/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ go 1.21.0
require (
github.com/aws/aws-sdk-go v1.47.1
github.com/pulumi/pulumi-aws/provider/v6 v6.0.0-00010101000000-000000000000
github.com/pulumi/pulumi-terraform-bridge/pf v0.18.3
github.com/pulumi/pulumi-terraform-bridge/pf v0.18.4-0.20231103010134-1e955db434c6
github.com/pulumi/pulumi-terraform-bridge/testing v0.0.2-0.20230927165309-e3fd9503f2d3
github.com/pulumi/pulumi/pkg/v3 v3.91.1
github.com/pulumi/pulumi/sdk/v3 v3.91.1
Expand Down Expand Up @@ -263,7 +263,7 @@ require (
github.com/pkg/term v1.1.0 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/pulumi/esc v0.5.6 // indirect
github.com/pulumi/pulumi-terraform-bridge/v3 v3.63.2 // indirect
github.com/pulumi/pulumi-terraform-bridge/v3 v3.63.3-0.20231103010134-1e955db434c6 // indirect
github.com/pulumi/pulumi-terraform-bridge/x/muxer v0.0.7-0.20230801203955-5d215c892096 // indirect
github.com/pulumi/terraform-diff-reader v0.0.2 // indirect
github.com/rivo/uniseg v0.4.4 // indirect
Expand Down
8 changes: 4 additions & 4 deletions examples/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -2336,12 +2336,12 @@ github.com/pulumi/esc v0.5.6 h1:4WV3X7OEVcChIwbSG+JxhZDdmq/q7lFPaSjHRYlPwmI=
github.com/pulumi/esc v0.5.6/go.mod h1:wpwNfVS5fV7Kd51j4dJ6FWYlKfxdqyppgp0gtkzqH04=
github.com/pulumi/providertest v0.0.2 h1:XtnO603irWnSgfIbz6OK8hffIJ0GIrKRl3tYMJwI1H8=
github.com/pulumi/providertest v0.0.2/go.mod h1:kZYBA14iemv3X4G4xsBKaa72zVbn//IyL5HTYKpLuy0=
github.com/pulumi/pulumi-terraform-bridge/pf v0.18.3 h1:Y41ChQl59yNIM7r2oeIAdB3ysV9uHQcIio9yEcqeEdI=
github.com/pulumi/pulumi-terraform-bridge/pf v0.18.3/go.mod h1:apif6z4X21j859e6+gKmw4WoaalaF8TyKMVSa8kgjAI=
github.com/pulumi/pulumi-terraform-bridge/pf v0.18.4-0.20231103010134-1e955db434c6 h1:0esYXSEITMI/b1Xkdq4x0CSLpmJfyhxyyO3lG2EMGz0=
github.com/pulumi/pulumi-terraform-bridge/pf v0.18.4-0.20231103010134-1e955db434c6/go.mod h1:apif6z4X21j859e6+gKmw4WoaalaF8TyKMVSa8kgjAI=
github.com/pulumi/pulumi-terraform-bridge/testing v0.0.2-0.20230927165309-e3fd9503f2d3 h1:bBWWeAtSPPYpKYlPZr2h0BiYgWQpHRIk0HO/MQmB+jc=
github.com/pulumi/pulumi-terraform-bridge/testing v0.0.2-0.20230927165309-e3fd9503f2d3/go.mod h1:vAQ7DeddebQ7FHdRaSG6ijuS28FS9PC4j8Y9wUuue0c=
github.com/pulumi/pulumi-terraform-bridge/v3 v3.63.2 h1:6JRJz3Wk7O9OhNFbxCrNvPVQAK+prBm6XBuSlIVqRnU=
github.com/pulumi/pulumi-terraform-bridge/v3 v3.63.2/go.mod h1:ye7JUFqTNbBh6ohcr1KpyXNv+kYFYvZAIqXqts4Ialc=
github.com/pulumi/pulumi-terraform-bridge/v3 v3.63.3-0.20231103010134-1e955db434c6 h1:XJSxdQXV91GvTBEAYgqJkpVZPXOZ5bBcAPRIDoRL5KU=
github.com/pulumi/pulumi-terraform-bridge/v3 v3.63.3-0.20231103010134-1e955db434c6/go.mod h1:ye7JUFqTNbBh6ohcr1KpyXNv+kYFYvZAIqXqts4Ialc=
github.com/pulumi/pulumi-terraform-bridge/x/muxer v0.0.7-0.20230801203955-5d215c892096 h1:1nzT9XuyTHdcWJboYNMPPdW0B0mQdXYg8Az5tF96MXY=
github.com/pulumi/pulumi-terraform-bridge/x/muxer v0.0.7-0.20230801203955-5d215c892096/go.mod h1:1pLAP9kryYta3Xrw99oh7BmxY6PYb+z2m7ENNCJMIRQ=
github.com/pulumi/pulumi/pkg/v3 v3.91.1 h1:xHnyEwJO9we2zCiM9gHTkJxjZ6a6yi5vYCwWHCYRj9Y=
Expand Down
3 changes: 3 additions & 0 deletions examples/tags-combinations-go/Pulumi.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
name: tags-combinations-go
description: Testing provider capability to tag resources correctly
runtime: go
Loading
Loading