From 859d741278471aa7be4451edd917609078ac7e20 Mon Sep 17 00:00:00 2001 From: Mikhail Shilkov Date: Wed, 18 Dec 2024 16:49:25 +0100 Subject: [PATCH 1/5] Implement autonaming configuration protocol --- provider/pkg/autonaming/application.go | 76 +++++++++--- provider/pkg/autonaming/application_test.go | 115 +++++++++++++++--- provider/pkg/autonaming/semantics.go | 3 + provider/pkg/provider/provider.go | 22 +++- provider/pkg/provider/provider_2e2_test.go | 14 +++ provider/pkg/provider/provider_test.go | 58 +++++++++ .../provider/testdata/autonaming/Pulumi.yaml | 24 ++++ provider/pkg/resources/cfn_custom_resource.go | 3 +- .../pkg/resources/cfn_custom_resource_test.go | 19 +-- provider/pkg/resources/custom.go | 4 +- provider/pkg/resources/extension_resource.go | 6 +- .../pkg/resources/mock_custom_resource.go | 9 +- 12 files changed, 299 insertions(+), 54 deletions(-) create mode 100644 provider/pkg/provider/testdata/autonaming/Pulumi.yaml diff --git a/provider/pkg/autonaming/application.go b/provider/pkg/autonaming/application.go index 498d09091f..9c11fef4cb 100644 --- a/provider/pkg/autonaming/application.go +++ b/provider/pkg/autonaming/application.go @@ -15,10 +15,26 @@ type AutoNamingConfig struct { RandomSuffixMinLength int `json:"randomSuffixMinLength"` } +// EngineAutonamingConfiguration contains autonaming parameters passed to the provider from the engine. +type EngineAutonamingConfiguration struct { + RandomSeed []byte + AutonamingMode *EngineAutonamingMode + ProposedName string +} + +// EngineAutonamingMode is the mode of autonaming to apply to the resource. +type EngineAutonamingMode int32 + +const ( + EngineAutonamingModePropose EngineAutonamingMode = iota + EngineAutonamingModeEnforce + EngineAutonamingModeDisable +) + func ApplyAutoNaming( spec *metadata.AutoNamingSpec, urn resource.URN, - randomSeed []byte, + engineAutonaming EngineAutonamingConfiguration, olds, news resource.PropertyMap, config *AutoNamingConfig, @@ -27,11 +43,13 @@ func ApplyAutoNaming( return nil } // Auto-name fields if not already specified - val, err := getDefaultName(randomSeed, urn, spec, olds, news, config) + val, ok, err := getDefaultName(urn, engineAutonaming, spec, olds, news, config) if err != nil { return err } - news[resource.PropertyKey(spec.SdkName)] = val + if ok { + news[resource.PropertyKey(spec.SdkName)] = *val + } return nil } @@ -41,29 +59,58 @@ func ApplyAutoNaming( // based on its URN name, It ensures the name meets the length constraints, if known. // Defaults to the name followed by 7 random hex characters separated by a '-'. func getDefaultName( - randomSeed []byte, urn resource.URN, + engineAutonaming EngineAutonamingConfiguration, autoNamingSpec *metadata.AutoNamingSpec, olds, news resource.PropertyMap, config *AutoNamingConfig, -) (resource.PropertyValue, error) { +) (*resource.PropertyValue, bool, error) { sdkName := autoNamingSpec.SdkName // Prefer explicitly specified name if v, ok := news[resource.PropertyKey(sdkName)]; ok { - return v, nil + return &v, true, nil } // Fallback to previous name if specified/set. if v, ok := olds[resource.PropertyKey(sdkName)]; ok { - return v, nil + return &v, true, nil } // Generate naming trivia for the resource. namingTriviaApplies, namingTrivia, err := CheckNamingTrivia(sdkName, news, autoNamingSpec.TriviaSpec) if err != nil { - return resource.PropertyValue{}, err + return nil, false, err + } + + if engineAutonaming.AutonamingMode != nil { + //panic("yes") + switch *engineAutonaming.AutonamingMode { + case EngineAutonamingModeDisable: + return nil, false, nil + case EngineAutonamingModeEnforce: + v := resource.NewStringProperty(engineAutonaming.ProposedName) + return &v, true, nil + case EngineAutonamingModePropose: + proposedName := engineAutonaming.ProposedName + + // Apply naming trivia to the generated name. + if namingTriviaApplies { + proposedName = ApplyTrivia(namingTrivia, proposedName) + } + + // Validate the proposed name against the length constraints. + if autoNamingSpec.MaxLength > 0 && len(proposedName) > autoNamingSpec.MaxLength { + return nil, false, fmt.Errorf("proposed name %q exceeds max length of %d", proposedName, autoNamingSpec.MaxLength) + } + if autoNamingSpec.MinLength > 0 && len(proposedName) < autoNamingSpec.MinLength { + return nil, false, fmt.Errorf("proposed name %q is shorter than min length of %d", proposedName, autoNamingSpec.MinLength) + } + + v := resource.NewStringProperty(proposedName) + return &v, true, nil + } } var autoTrim bool @@ -94,7 +141,7 @@ func getDefaultName( if left <= 0 && autoTrim { autoTrimMaxLength := autoNamingSpec.MaxLength - namingTrivia.Length() - randomSuffixMinLength if autoTrimMaxLength <= 0 { - return resource.PropertyValue{}, fmt.Errorf("failed to auto-generate value for %[1]q."+ + return nil, false, fmt.Errorf("failed to auto-generate value for %[1]q."+ " Prefix: %[2]q is too large to fix max length constraint of %[3]d"+ " with required suffix length %[4]d. Please provide a value for %[1]q", sdkName, prefix, autoNamingSpec.MaxLength, randomSuffixMinLength) @@ -106,12 +153,12 @@ func getDefaultName( if left <= 0 { if namingTrivia.Length() > 0 { - return resource.PropertyValue{}, fmt.Errorf("failed to auto-generate value for %[1]q."+ + return nil, false, fmt.Errorf("failed to auto-generate value for %[1]q."+ " Prefix: %[2]q is too large to fix max length constraint of %[3]d"+ " with required suffix %[4]q. Please provide a value for %[1]q", sdkName, prefix, autoNamingSpec.MaxLength, namingTrivia.Suffix) } else { - return resource.PropertyValue{}, fmt.Errorf("failed to auto-generate value for %[1]q."+ + return nil, false, fmt.Errorf("failed to auto-generate value for %[1]q."+ " Prefix: %[2]q is too large to fix max length constraint of %[3]d. Please provide a value for %[1]q", sdkName, prefix, autoNamingSpec.MaxLength) } @@ -123,9 +170,9 @@ func getDefaultName( } // Resource name is URN name + "-" + random suffix. - random, err := resource.NewUniqueName(randomSeed, prefix, randLength, maxLength, nil) + random, err := resource.NewUniqueName(engineAutonaming.RandomSeed, prefix, randLength, maxLength, nil) if err != nil { - return resource.PropertyValue{}, err + return nil, false, err } // Apply naming trivia to the generated name. @@ -133,7 +180,8 @@ func getDefaultName( random = ApplyTrivia(namingTrivia, random) } - return resource.NewStringProperty(random), nil + v := resource.NewStringProperty(random) + return &v, true, nil } // trimName will trim the prefix to fit within the max length constraint. diff --git a/provider/pkg/autonaming/application_test.go b/provider/pkg/autonaming/application_test.go index 05e38ea55c..6da33c1e6c 100644 --- a/provider/pkg/autonaming/application_test.go +++ b/provider/pkg/autonaming/application_test.go @@ -14,14 +14,19 @@ import ( func Test_getDefaultName(t *testing.T) { const sdkName = "autoName" + p := func(v EngineAutonamingMode) *EngineAutonamingMode { + return &v + } tests := []struct { - name string - minLength int - maxLength int - olds resource.PropertyMap - news resource.PropertyMap - err error - comparison func(t *testing.T, actual resource.PropertyValue) bool + name string + minLength int + maxLength int + olds resource.PropertyMap + news resource.PropertyMap + err error + comparison func(t *testing.T, actual *resource.PropertyValue) bool + engineAutonaming EngineAutonamingConfiguration + noName bool }{ { name: "Name specified explicitly", @@ -63,6 +68,61 @@ func Test_getDefaultName(t *testing.T) { maxLength: 13, comparison: within(13, 13), }, + { + name: "Autoname with AutonamingMode=Propose", + engineAutonaming: EngineAutonamingConfiguration{ + AutonamingMode: p(EngineAutonamingModePropose), + ProposedName: "proposed-name", + }, + comparison: equals(resource.NewStringProperty("proposed-name")), + }, + { + name: "Autoname with AutonamingMode=Enforce", + engineAutonaming: EngineAutonamingConfiguration{ + AutonamingMode: p(EngineAutonamingModeEnforce), + ProposedName: "enforced-name", + }, + comparison: equals(resource.NewStringProperty("enforced-name")), + }, + { + name: "Autoname with AutonamingMode=Disable", + engineAutonaming: EngineAutonamingConfiguration{ + AutonamingMode: p(EngineAutonamingModeDisable), + }, + comparison: func(t *testing.T, actual *resource.PropertyValue) bool { + return actual == nil + }, + noName: true, + }, + { + name: "Autoname with Propose mode and max length constraints", + engineAutonaming: EngineAutonamingConfiguration{ + AutonamingMode: p(EngineAutonamingModePropose), + ProposedName: "very-long-proposed-name", + }, + maxLength: 10, + err: fmt.Errorf("proposed name %q exceeds max length of %d", "very-long-proposed-name", 10), + }, + { + name: "Autoname with Propose mode and min length constraints", + engineAutonaming: EngineAutonamingConfiguration{ + AutonamingMode: p(EngineAutonamingModePropose), + ProposedName: "too-short-proposed-name", + }, + minLength: 25, + err: fmt.Errorf("proposed name %q is shorter than min length of %d", "too-short-proposed-name", 25), + }, + { + name: "Autoname with Propose mode and trivia", + engineAutonaming: EngineAutonamingConfiguration{ + AutonamingMode: p(EngineAutonamingModePropose), + ProposedName: "proposed-name", + }, + news: resource.PropertyMap{ + "flag": resource.NewBoolProperty(true), + }, + comparison: equals(resource.NewStringProperty("proposed-name-trivia-value")), + }, } urn := resource.URN("urn:pulumi:dev::test::test-provider:testModule:TestResource::myName") @@ -73,16 +133,29 @@ func Test_getDefaultName(t *testing.T) { SdkName: "autoName", MinLength: tt.minLength, MaxLength: tt.maxLength, + TriviaSpec: &metadata.NamingTriviaSpec{ + Rule: &metadata.NamingRule{ + Field: "flag", + Condition: &metadata.NamingCondition{ + Predicate: metadata.NamingPredicateEquals, + Value: resource.NewBoolProperty(true), + }, + }, + Trivia: &metadata.NamingTrivia{ + Suffix: "-trivia-value", + }, + }, } - got, err := getDefaultName(nil, urn, autoNamingSpec, tt.olds, tt.news, nil) + got, has, err := getDefaultName(urn, tt.engineAutonaming, autoNamingSpec, tt.olds, tt.news, nil) if tt.err != nil { require.EqualError(t, err, tt.err.Error()) return } else { require.NoError(t, err) + require.Equal(t, tt.noName, !has) } if !tt.comparison(t, got) { - t.Errorf("getDefaultName() = %v for spec: %+v", got, autoNamingSpec) + t.Errorf("getDefaultName() = %v for spec: %+v", *got, autoNamingSpec) } t.Logf("getDefaultName() = %v for spec: %+v", got, autoNamingSpec) }) @@ -100,7 +173,7 @@ func Test_getDefaultName_withAutoNameConfig(t *testing.T) { olds resource.PropertyMap news resource.PropertyMap err error - comparison func(t *testing.T, actual resource.PropertyValue) bool + comparison func(t *testing.T, actual *resource.PropertyValue) bool }{ { name: "Name specified explicitly", @@ -190,35 +263,37 @@ func Test_getDefaultName_withAutoNameConfig(t *testing.T) { } autoNameConfig := tt.autoNameConfig - got, err := getDefaultName(nil, urn, autoNamingSpec, tt.olds, tt.news, &autoNameConfig) + engineAutonaming := EngineAutonamingConfiguration{} + got, has, err := getDefaultName(urn, engineAutonaming, autoNamingSpec, tt.olds, tt.news, &autoNameConfig) if tt.err != nil { require.EqualError(t, err, tt.err.Error()) return } else { require.NoError(t, err) + require.True(t, has) } if !tt.comparison(t, got) { - t.Errorf("getDefaultName() = %v for spec: %+v", got, autoNamingSpec) + t.Errorf("getDefaultName() = %v for spec: %+v", *got, autoNamingSpec) } - t.Logf("getDefaultName() = %v for spec: %+v", got, autoNamingSpec) + t.Logf("getDefaultName() = %v for spec: %+v", *got, autoNamingSpec) }) } } -func equals(expected resource.PropertyValue) func(t *testing.T, actual resource.PropertyValue) bool { - return func(t *testing.T, actual resource.PropertyValue) bool { - return expected == actual +func equals(expected resource.PropertyValue) func(t *testing.T, actual *resource.PropertyValue) bool { + return func(t *testing.T, actual *resource.PropertyValue) bool { + return expected == *actual } } -func startsWith(prefix string, randomLen int) func(t *testing.T, actual resource.PropertyValue) bool { - return func(t *testing.T, actual resource.PropertyValue) bool { +func startsWith(prefix string, randomLen int) func(t *testing.T, actual *resource.PropertyValue) bool { + return func(t *testing.T, actual *resource.PropertyValue) bool { return assert.Regexp(t, fmt.Sprintf("^%s-[a-z0-9]{%d}", prefix, randomLen), actual.StringValue()) } } -func within(min, max int) func(t *testing.T, value resource.PropertyValue) bool { - return func(t *testing.T, actual resource.PropertyValue) bool { +func within(min, max int) func(t *testing.T, value *resource.PropertyValue) bool { + return func(t *testing.T, actual *resource.PropertyValue) bool { l := len(actual.V.(string)) return min <= l && l <= max } diff --git a/provider/pkg/autonaming/semantics.go b/provider/pkg/autonaming/semantics.go index 90614f6dca..0f84d23b43 100644 --- a/provider/pkg/autonaming/semantics.go +++ b/provider/pkg/autonaming/semantics.go @@ -27,6 +27,9 @@ func CheckNamingTrivia(sdkName string, props resource.PropertyMap, spec *metadat if err != nil { return false, nil, err } + if !namingTriviaApplies { + return false, nil, nil + } namingTrivia := *spec.Trivia return namingTriviaApplies, &namingTrivia, nil diff --git a/provider/pkg/provider/provider.go b/provider/pkg/provider/provider.go index fea04da65c..c500c22a93 100644 --- a/provider/pkg/provider/provider.go +++ b/provider/pkg/provider/provider.go @@ -532,8 +532,9 @@ func (p *cfnProvider) Configure(ctx context.Context, req *pulumirpc.ConfigureReq p.configured = true return &pulumirpc.ConfigureResponse{ - AcceptSecrets: true, - SupportsPreview: true, + AcceptSecrets: true, + SupportsPreview: true, + SupportsAutonamingConfiguration: true, }, nil } @@ -744,7 +745,7 @@ func (p *cfnProvider) Check(ctx context.Context, req *pulumirpc.CheckRequest) (* var failures []resources.ValidationFailure resourceToken := string(urn.Type()) if customResource, ok := p.customResources[resourceToken]; ok { - newInputs, failures, err = customResource.Check(ctx, urn, req.RandomSeed, newInputs, olds, p.defaultTags) + newInputs, failures, err = customResource.Check(ctx, urn, engineAutonaming(req), newInputs, olds, p.defaultTags) if err != nil { return nil, fmt.Errorf("failed to check custom resource %q: %w", resourceToken, err) } @@ -754,7 +755,8 @@ func (p *cfnProvider) Check(ctx context.Context, req *pulumirpc.CheckRequest) (* return nil, errors.Errorf("resource type %s not found", resourceToken) } - if err := autonaming.ApplyAutoNaming(spec.AutoNamingSpec, urn, req.RandomSeed, olds, newInputs, p.autoNamingConfig); err != nil { + if err := autonaming.ApplyAutoNaming(spec.AutoNamingSpec, urn, engineAutonaming(req), + olds, newInputs, p.autoNamingConfig); err != nil { return nil, fmt.Errorf("failed to apply auto-naming: %w", err) } @@ -792,6 +794,18 @@ func (p *cfnProvider) Check(ctx context.Context, req *pulumirpc.CheckRequest) (* return &pulumirpc.CheckResponse{Failures: checkFailures}, nil } +func engineAutonaming(req *pulumirpc.CheckRequest) autonaming.EngineAutonamingConfiguration { + engineAutonaming := autonaming.EngineAutonamingConfiguration{ + RandomSeed: req.RandomSeed, + } + if req.Autonaming != nil { + mode := autonaming.EngineAutonamingMode(req.Autonaming.Mode) + engineAutonaming.AutonamingMode = &mode + engineAutonaming.ProposedName = req.Autonaming.ProposedName + } + return engineAutonaming +} + func (p *cfnProvider) Diff(ctx context.Context, req *pulumirpc.DiffRequest) (*pulumirpc.DiffResponse, error) { urn := resource.URN(req.GetUrn()) label := fmt.Sprintf("%s.Diff(%s)", p.name, urn) diff --git a/provider/pkg/provider/provider_2e2_test.go b/provider/pkg/provider/provider_2e2_test.go index e0d8db44e7..964f81fd7b 100644 --- a/provider/pkg/provider/provider_2e2_test.go +++ b/provider/pkg/provider/provider_2e2_test.go @@ -14,6 +14,7 @@ import ( "github.com/pulumi/providertest/pulumitest/opttest" "github.com/pulumi/pulumi-aws-native/provider/pkg/provider" pulumirpc "github.com/pulumi/pulumi/sdk/v3/proto/go" + "github.com/stretchr/testify/assert" ) func TestE2eSnapshots(t *testing.T) { @@ -30,6 +31,19 @@ func TestE2eSnapshots(t *testing.T) { }) } +func TestAutonaming(t *testing.T) { + t.Parallel() + pt := newAwsTest(t, filepath.Join("testdata", "autonaming"), opttest.Env("PULUMI_EXPERIMENTAL", "1")) + pt.Preview(t) + up := pt.Up(t) + logGroupName, ok := up.Outputs["logGroupName"].Value.(string) + assert.True(t, ok) + assert.Contains(t, logGroupName, "autonaming-log-") // project + name + random suffix + fifoQueueName, ok := up.Outputs["fifoQueueName"].Value.(string) + assert.True(t, ok) + assert.Contains(t, fifoQueueName, "queue.fifo") // verbatim name + resource's autonaming trivia suffix +} + func testUpgradeFrom(t *testing.T, test *pulumitest.PulumiTest, version string) { result := providertest.PreviewProviderUpgrade(t, test, "aws-native", version) assertpreview.HasNoChanges(t, result) diff --git a/provider/pkg/provider/provider_test.go b/provider/pkg/provider/provider_test.go index 34b61221bb..bc2241cd26 100644 --- a/provider/pkg/provider/provider_test.go +++ b/provider/pkg/provider/provider_test.go @@ -102,6 +102,64 @@ func TestConfigure(t *testing.T) { }) } +func TestEngineAutonaming(t *testing.T) { + t.Parallel() + + t.Run("WithoutAutonaming", func(t *testing.T) { + req := &pulumirpc.CheckRequest{ + RandomSeed: []byte("test-seed"), + } + + config := engineAutonaming(req) + assert.Equal(t, []byte("test-seed"), config.RandomSeed) + assert.Nil(t, config.AutonamingMode) + assert.Empty(t, config.ProposedName) + }) + + tests := []struct { + name string + mode pulumirpc.CheckRequest_AutonamingOptions_Mode + proposedName string + want autonaming.EngineAutonamingMode + }{ + { + name: "Disable", + mode: pulumirpc.CheckRequest_AutonamingOptions_DISABLE, + want: autonaming.EngineAutonamingModeDisable, + }, + { + name: "Enforce", + mode: pulumirpc.CheckRequest_AutonamingOptions_ENFORCE, + proposedName: "test-resource", + want: autonaming.EngineAutonamingModeEnforce, + }, + { + name: "Propose", + mode: pulumirpc.CheckRequest_AutonamingOptions_PROPOSE, + proposedName: "test-resource", + want: autonaming.EngineAutonamingModePropose, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + req := &pulumirpc.CheckRequest{ + RandomSeed: []byte("test-seed"), + Autonaming: &pulumirpc.CheckRequest_AutonamingOptions{ + Mode: tt.mode, + ProposedName: tt.proposedName, + }, + } + + config := engineAutonaming(req) + assert.Equal(t, []byte("test-seed"), config.RandomSeed) + require.NotNil(t, config.AutonamingMode) + assert.Equal(t, tt.want, *config.AutonamingMode) + assert.Equal(t, tt.proposedName, config.ProposedName) + }) + } +} + func TestCreatePreview(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() diff --git a/provider/pkg/provider/testdata/autonaming/Pulumi.yaml b/provider/pkg/provider/testdata/autonaming/Pulumi.yaml new file mode 100644 index 0000000000..69926f4016 --- /dev/null +++ b/provider/pkg/provider/testdata/autonaming/Pulumi.yaml @@ -0,0 +1,24 @@ +name: autonaming +description: Autonaming configuration example +runtime: yaml +config: + pulumi:autonaming: + value: + providers: + aws-native: + pattern: ${project}-${name}-${alphanum(6)} + resources: + aws-native:sqs:Queue: + pattern: ${name} +resources: + log: + type: aws-native:logs:LogGroup + properties: + retentionInDays: 90 + queue: + type: aws-native:sqs:Queue + properties: + fifoQueue: true +outputs: + logGroupName: ${log.arn} + fifoQueueName: ${queue.queueName} \ No newline at end of file diff --git a/provider/pkg/resources/cfn_custom_resource.go b/provider/pkg/resources/cfn_custom_resource.go index 482109377d..240f6e7b72 100644 --- a/provider/pkg/resources/cfn_custom_resource.go +++ b/provider/pkg/resources/cfn_custom_resource.go @@ -10,6 +10,7 @@ import ( "github.com/aws/aws-lambda-go/cfn" "github.com/golang/glog" "github.com/google/uuid" + "github.com/pulumi/pulumi-aws-native/provider/pkg/autonaming" "github.com/pulumi/pulumi-aws-native/provider/pkg/client" "github.com/pulumi/pulumi-aws-native/provider/pkg/naming" "github.com/pulumi/pulumi-go-provider/resourcex" @@ -232,7 +233,7 @@ type customResourceInvokeData struct { // Check validates the inputs of the resource and applies default values if necessary. // It returns the inputs, validation failures, and an error if the inputs cannot be unmarshalled. -func (c *cfnCustomResource) Check(ctx context.Context, urn urn.URN, randomSeed []byte, inputs resource.PropertyMap, state resource.PropertyMap, defaultTags map[string]string) (resource.PropertyMap, []ValidationFailure, error) { +func (c *cfnCustomResource) Check(ctx context.Context, urn urn.URN, _ autonaming.EngineAutonamingConfiguration, inputs resource.PropertyMap, state resource.PropertyMap, defaultTags map[string]string) (resource.PropertyMap, []ValidationFailure, error) { var typedInputs CfnCustomResourceInputs _, err := resourcex.Unmarshal(&typedInputs, inputs, resourcex.UnmarshalOptions{}) if err != nil { diff --git a/provider/pkg/resources/cfn_custom_resource_test.go b/provider/pkg/resources/cfn_custom_resource_test.go index 4b666be90e..ab267a7237 100644 --- a/provider/pkg/resources/cfn_custom_resource_test.go +++ b/provider/pkg/resources/cfn_custom_resource_test.go @@ -11,6 +11,7 @@ import ( "time" "github.com/aws/aws-lambda-go/cfn" + "github.com/pulumi/pulumi-aws-native/provider/pkg/autonaming" "github.com/pulumi/pulumi-aws-native/provider/pkg/client" "github.com/pulumi/pulumi-aws-native/provider/pkg/naming" "github.com/pulumi/pulumi/sdk/v3/go/common/resource" @@ -89,12 +90,12 @@ func TestCfnCustomResource_Check(t *testing.T) { { name: "Unknown inputs", inputs: resource.PropertyMap{ - "serviceToken": resource.MakeComputed(resource.NewStringProperty("")), - "stackId": resource.MakeComputed(resource.NewStringProperty("")), + "serviceToken": resource.MakeComputed(resource.NewStringProperty("")), + "stackId": resource.MakeComputed(resource.NewStringProperty("")), }, expectedInputs: resource.PropertyMap{ - "serviceToken": resource.MakeComputed(resource.NewStringProperty("")), - "stackId": resource.MakeComputed(resource.NewStringProperty("")), + "serviceToken": resource.MakeComputed(resource.NewStringProperty("")), + "stackId": resource.MakeComputed(resource.NewStringProperty("")), }, }, { @@ -117,11 +118,13 @@ func TestCfnCustomResource_Check(t *testing.T) { c := &cfnCustomResource{} ctx := context.Background() urn := urn.URN("urn:pulumi:testProject::test::aws-native:cloudformation:CfnCustomResource::dummy") - randomSeed := []byte{} + engineAutonaming := autonaming.EngineAutonamingConfiguration{ + RandomSeed: []byte{}, + } state := resource.PropertyMap{} defaultTags := map[string]string{} - newInputs, failures, err := c.Check(ctx, urn, randomSeed, tt.inputs, state, defaultTags) + newInputs, failures, err := c.Check(ctx, urn, engineAutonaming, tt.inputs, state, defaultTags) if tt.expectedError != nil { assert.Error(t, err) @@ -233,7 +236,7 @@ func TestCfnCustomResource_Create(t *testing.T) { }, }, { - name: "CustomResource without response data", + name: "CustomResource without response data", customResourceData: nil, customResourceInputs: map[string]interface{}{ "key1": "value1", @@ -624,7 +627,7 @@ func TestCfnCustomResource_Update(t *testing.T) { newCustomResourceData: map[string]interface{}{"new": "value"}, }, { - name: "CustomResource without response data", + name: "CustomResource without response data", newCustomResourceData: nil, }, } diff --git a/provider/pkg/resources/custom.go b/provider/pkg/resources/custom.go index e67ef0491e..028dcf5b2c 100644 --- a/provider/pkg/resources/custom.go +++ b/provider/pkg/resources/custom.go @@ -6,13 +6,15 @@ import ( "context" "time" + "github.com/pulumi/pulumi-aws-native/provider/pkg/autonaming" "github.com/pulumi/pulumi/sdk/v3/go/common/resource" ) //go:generate mockgen -package resources -source custom.go -destination mock_custom_resource.go CustomResource type CustomResource interface { // Check validates and transforms the inputs of the resource. - Check(ctx context.Context, urn resource.URN, randomSeed []byte, inputs, state resource.PropertyMap, defaultTags map[string]string) (resource.PropertyMap, []ValidationFailure, error) + Check(ctx context.Context, urn resource.URN, engineAutonaming autonaming.EngineAutonamingConfiguration, + inputs, state resource.PropertyMap, defaultTags map[string]string) (resource.PropertyMap, []ValidationFailure, error) // Create creates a new resource in the cloud provider and returns its unique identifier and outputs. Create(ctx context.Context, urn resource.URN, inputs resource.PropertyMap, timeout time.Duration) (identifier *string, outputs resource.PropertyMap, err error) // Read returns the outputs and the updated inputs of the resource. diff --git a/provider/pkg/resources/extension_resource.go b/provider/pkg/resources/extension_resource.go index 8e05c139b2..4770937dce 100644 --- a/provider/pkg/resources/extension_resource.go +++ b/provider/pkg/resources/extension_resource.go @@ -45,7 +45,9 @@ func NewExtensionResource(client client.CloudControlClient) *extensionResource { } } -func (r *extensionResource) Check(ctx context.Context, urn resource.URN, randomSeed []byte, inputs, state resource.PropertyMap, defaultTags map[string]string) (resource.PropertyMap, []ValidationFailure, error) { +func (r *extensionResource) Check(ctx context.Context, urn resource.URN, + engineAutonaming autonaming.EngineAutonamingConfiguration, inputs, state resource.PropertyMap, + defaultTags map[string]string) (resource.PropertyMap, []ValidationFailure, error) { var typedInputs ExtensionResourceInputs _, err := resourcex.Unmarshal(&typedInputs, inputs, resourcex.UnmarshalOptions{}) if err != nil { @@ -64,7 +66,7 @@ func (r *extensionResource) Check(ctx context.Context, urn resource.URN, randomS SdkName: typedInputs.AutoNaming.PropertyName, MinLength: typedInputs.AutoNaming.MinLength, MaxLength: typedInputs.AutoNaming.MaxLength, - }, urn, randomSeed, state, inputs, nil); err != nil { + }, urn, engineAutonaming, state, inputs, nil); err != nil { return nil, nil, fmt.Errorf("failed to apply auto-naming: %w", err) } } diff --git a/provider/pkg/resources/mock_custom_resource.go b/provider/pkg/resources/mock_custom_resource.go index ce398063dd..5eab71693b 100644 --- a/provider/pkg/resources/mock_custom_resource.go +++ b/provider/pkg/resources/mock_custom_resource.go @@ -14,6 +14,7 @@ import ( reflect "reflect" time "time" + autonaming "github.com/pulumi/pulumi-aws-native/provider/pkg/autonaming" resource "github.com/pulumi/pulumi/sdk/v3/go/common/resource" gomock "go.uber.org/mock/gomock" ) @@ -43,9 +44,9 @@ func (m *MockCustomResource) EXPECT() *MockCustomResourceMockRecorder { } // Check mocks base method. -func (m *MockCustomResource) Check(ctx context.Context, urn resource.URN, randomSeed []byte, inputs, state resource.PropertyMap, defaultTags map[string]string) (resource.PropertyMap, []ValidationFailure, error) { +func (m *MockCustomResource) Check(ctx context.Context, urn resource.URN, engineAutonaming autonaming.EngineAutonamingConfiguration, inputs, state resource.PropertyMap, defaultTags map[string]string) (resource.PropertyMap, []ValidationFailure, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Check", ctx, urn, randomSeed, inputs, state, defaultTags) + ret := m.ctrl.Call(m, "Check", ctx, urn, engineAutonaming, inputs, state, defaultTags) ret0, _ := ret[0].(resource.PropertyMap) ret1, _ := ret[1].([]ValidationFailure) ret2, _ := ret[2].(error) @@ -53,9 +54,9 @@ func (m *MockCustomResource) Check(ctx context.Context, urn resource.URN, random } // Check indicates an expected call of Check. -func (mr *MockCustomResourceMockRecorder) Check(ctx, urn, randomSeed, inputs, state, defaultTags any) *gomock.Call { +func (mr *MockCustomResourceMockRecorder) Check(ctx, urn, engineAutonaming, inputs, state, defaultTags any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Check", reflect.TypeOf((*MockCustomResource)(nil).Check), ctx, urn, randomSeed, inputs, state, defaultTags) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Check", reflect.TypeOf((*MockCustomResource)(nil).Check), ctx, urn, engineAutonaming, inputs, state, defaultTags) } // Create mocks base method. From 5bc1cf7e27b88e4278ffd15b9c79eb1e83f5a298 Mon Sep 17 00:00:00 2001 From: Mikhail Shilkov Date: Wed, 18 Dec 2024 17:19:08 +0100 Subject: [PATCH 2/5] Add AWS credentials to run end2end tests --- .github/workflows/run-acceptance-tests.yml | 9 +++++++++ provider/pkg/autonaming/application.go | 1 - 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/.github/workflows/run-acceptance-tests.yml b/.github/workflows/run-acceptance-tests.yml index ee4b4d1ff9..3e4d35f5df 100644 --- a/.github/workflows/run-acceptance-tests.yml +++ b/.github/workflows/run-acceptance-tests.yml @@ -140,6 +140,15 @@ jobs: with: name: pulumi-${{ env.PROVIDER }}-provider.tar.gz path: ${{ github.workspace }}/bin/provider.tar.gz + - name: Configure AWS Credentials + uses: aws-actions/configure-aws-credentials@e3dd6a429d7300a6a4c196c26e071d42e0343502 # v4.0.2 + with: + aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} + aws-region: ${{ env.AWS_REGION }} + aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + role-duration-seconds: 3600 + role-session-name: ${{ env.PROVIDER }}@githubActions + role-to-assume: ${{ secrets.AWS_CI_ROLE_ARN }} - name: Test Provider Library run: make test_provider - name: Upload coverage reports to Codecov diff --git a/provider/pkg/autonaming/application.go b/provider/pkg/autonaming/application.go index 9c11fef4cb..42794969b0 100644 --- a/provider/pkg/autonaming/application.go +++ b/provider/pkg/autonaming/application.go @@ -85,7 +85,6 @@ func getDefaultName( } if engineAutonaming.AutonamingMode != nil { - //panic("yes") switch *engineAutonaming.AutonamingMode { case EngineAutonamingModeDisable: return nil, false, nil From 76f51cebc0301f79f4aba236a415d1a7c24df87f Mon Sep 17 00:00:00 2001 From: Mikhail Shilkov Date: Thu, 19 Dec 2024 11:29:28 +0100 Subject: [PATCH 3/5] Simplify getDefaultName signature --- provider/pkg/autonaming/application.go | 32 ++++++++++----------- provider/pkg/autonaming/application_test.go | 8 +++--- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/provider/pkg/autonaming/application.go b/provider/pkg/autonaming/application.go index 42794969b0..ae792912d4 100644 --- a/provider/pkg/autonaming/application.go +++ b/provider/pkg/autonaming/application.go @@ -43,11 +43,11 @@ func ApplyAutoNaming( return nil } // Auto-name fields if not already specified - val, ok, err := getDefaultName(urn, engineAutonaming, spec, olds, news, config) + val, err := getDefaultName(urn, engineAutonaming, spec, olds, news, config) if err != nil { return err } - if ok { + if val != nil { news[resource.PropertyKey(spec.SdkName)] = *val } return nil @@ -65,32 +65,32 @@ func getDefaultName( olds, news resource.PropertyMap, config *AutoNamingConfig, -) (*resource.PropertyValue, bool, error) { +) (*resource.PropertyValue, error) { sdkName := autoNamingSpec.SdkName // Prefer explicitly specified name if v, ok := news[resource.PropertyKey(sdkName)]; ok { - return &v, true, nil + return &v, nil } // Fallback to previous name if specified/set. if v, ok := olds[resource.PropertyKey(sdkName)]; ok { - return &v, true, nil + return &v, nil } // Generate naming trivia for the resource. namingTriviaApplies, namingTrivia, err := CheckNamingTrivia(sdkName, news, autoNamingSpec.TriviaSpec) if err != nil { - return nil, false, err + return nil, err } if engineAutonaming.AutonamingMode != nil { switch *engineAutonaming.AutonamingMode { case EngineAutonamingModeDisable: - return nil, false, nil + return nil, nil case EngineAutonamingModeEnforce: v := resource.NewStringProperty(engineAutonaming.ProposedName) - return &v, true, nil + return &v, nil case EngineAutonamingModePropose: proposedName := engineAutonaming.ProposedName @@ -101,14 +101,14 @@ func getDefaultName( // Validate the proposed name against the length constraints. if autoNamingSpec.MaxLength > 0 && len(proposedName) > autoNamingSpec.MaxLength { - return nil, false, fmt.Errorf("proposed name %q exceeds max length of %d", proposedName, autoNamingSpec.MaxLength) + return nil, fmt.Errorf("proposed name %q exceeds max length of %d", proposedName, autoNamingSpec.MaxLength) } if autoNamingSpec.MinLength > 0 && len(proposedName) < autoNamingSpec.MinLength { - return nil, false, fmt.Errorf("proposed name %q is shorter than min length of %d", proposedName, autoNamingSpec.MinLength) + return nil, fmt.Errorf("proposed name %q is shorter than min length of %d", proposedName, autoNamingSpec.MinLength) } v := resource.NewStringProperty(proposedName) - return &v, true, nil + return &v, nil } } @@ -140,7 +140,7 @@ func getDefaultName( if left <= 0 && autoTrim { autoTrimMaxLength := autoNamingSpec.MaxLength - namingTrivia.Length() - randomSuffixMinLength if autoTrimMaxLength <= 0 { - return nil, false, fmt.Errorf("failed to auto-generate value for %[1]q."+ + return nil, fmt.Errorf("failed to auto-generate value for %[1]q."+ " Prefix: %[2]q is too large to fix max length constraint of %[3]d"+ " with required suffix length %[4]d. Please provide a value for %[1]q", sdkName, prefix, autoNamingSpec.MaxLength, randomSuffixMinLength) @@ -152,12 +152,12 @@ func getDefaultName( if left <= 0 { if namingTrivia.Length() > 0 { - return nil, false, fmt.Errorf("failed to auto-generate value for %[1]q."+ + return nil, fmt.Errorf("failed to auto-generate value for %[1]q."+ " Prefix: %[2]q is too large to fix max length constraint of %[3]d"+ " with required suffix %[4]q. Please provide a value for %[1]q", sdkName, prefix, autoNamingSpec.MaxLength, namingTrivia.Suffix) } else { - return nil, false, fmt.Errorf("failed to auto-generate value for %[1]q."+ + return nil, fmt.Errorf("failed to auto-generate value for %[1]q."+ " Prefix: %[2]q is too large to fix max length constraint of %[3]d. Please provide a value for %[1]q", sdkName, prefix, autoNamingSpec.MaxLength) } @@ -171,7 +171,7 @@ func getDefaultName( // Resource name is URN name + "-" + random suffix. random, err := resource.NewUniqueName(engineAutonaming.RandomSeed, prefix, randLength, maxLength, nil) if err != nil { - return nil, false, err + return nil, err } // Apply naming trivia to the generated name. @@ -180,7 +180,7 @@ func getDefaultName( } v := resource.NewStringProperty(random) - return &v, true, nil + return &v, nil } // trimName will trim the prefix to fit within the max length constraint. diff --git a/provider/pkg/autonaming/application_test.go b/provider/pkg/autonaming/application_test.go index 6da33c1e6c..11bec297d0 100644 --- a/provider/pkg/autonaming/application_test.go +++ b/provider/pkg/autonaming/application_test.go @@ -146,13 +146,13 @@ func Test_getDefaultName(t *testing.T) { }, }, } - got, has, err := getDefaultName(urn, tt.engineAutonaming, autoNamingSpec, tt.olds, tt.news, nil) + got, err := getDefaultName(urn, tt.engineAutonaming, autoNamingSpec, tt.olds, tt.news, nil) if tt.err != nil { require.EqualError(t, err, tt.err.Error()) return } else { require.NoError(t, err) - require.Equal(t, tt.noName, !has) + require.Equal(t, tt.noName, got == nil) } if !tt.comparison(t, got) { t.Errorf("getDefaultName() = %v for spec: %+v", *got, autoNamingSpec) @@ -264,13 +264,13 @@ func Test_getDefaultName_withAutoNameConfig(t *testing.T) { autoNameConfig := tt.autoNameConfig engineAutonaming := EngineAutonamingConfiguration{} - got, has, err := getDefaultName(urn, engineAutonaming, autoNamingSpec, tt.olds, tt.news, &autoNameConfig) + got, err := getDefaultName(urn, engineAutonaming, autoNamingSpec, tt.olds, tt.news, &autoNameConfig) if tt.err != nil { require.EqualError(t, err, tt.err.Error()) return } else { require.NoError(t, err) - require.True(t, has) + require.True(t, got != nil) } if !tt.comparison(t, got) { t.Errorf("getDefaultName() = %v for spec: %+v", *got, autoNamingSpec) From dae1852224ed8c1f87fc691cfa4286d069d3872b Mon Sep 17 00:00:00 2001 From: Mikhail Shilkov Date: Fri, 20 Dec 2024 15:19:07 +0100 Subject: [PATCH 4/5] Conflict if both engine and provider config are specified. Also, unify naming --- provider/pkg/autonaming/application.go | 70 ++++++++++--------- provider/pkg/autonaming/application_test.go | 65 ++++++++++------- provider/pkg/provider/provider.go | 12 ++-- provider/pkg/provider/provider_test.go | 4 +- provider/pkg/resources/cfn_custom_resource.go | 2 +- .../pkg/resources/cfn_custom_resource_test.go | 2 +- provider/pkg/resources/custom.go | 2 +- provider/pkg/resources/extension_resource.go | 4 +- .../pkg/resources/mock_custom_resource.go | 2 +- 9 files changed, 91 insertions(+), 72 deletions(-) diff --git a/provider/pkg/autonaming/application.go b/provider/pkg/autonaming/application.go index ae792912d4..aa564ddb0a 100644 --- a/provider/pkg/autonaming/application.go +++ b/provider/pkg/autonaming/application.go @@ -10,13 +10,14 @@ import ( "github.com/pulumi/pulumi/sdk/v3/go/common/resource" ) -type AutoNamingConfig struct { +// ProviderAutoNamingConfig contains autonaming parameters configured for the provider. +type ProviderAutoNamingConfig struct { AutoTrim bool `json:"autoTrim"` RandomSuffixMinLength int `json:"randomSuffixMinLength"` } -// EngineAutonamingConfiguration contains autonaming parameters passed to the provider from the engine. -type EngineAutonamingConfiguration struct { +// EngineAutoNamingConfig contains autonaming parameters passed to the provider from the engine. +type EngineAutoNamingConfig struct { RandomSeed []byte AutonamingMode *EngineAutonamingMode ProposedName string @@ -34,16 +35,16 @@ const ( func ApplyAutoNaming( spec *metadata.AutoNamingSpec, urn resource.URN, - engineAutonaming EngineAutonamingConfiguration, + engineConfig EngineAutoNamingConfig, + providerConfig *ProviderAutoNamingConfig, olds, news resource.PropertyMap, - config *AutoNamingConfig, ) error { if spec == nil { return nil } // Auto-name fields if not already specified - val, err := getDefaultName(urn, engineAutonaming, spec, olds, news, config) + val, err := getDefaultName(urn, engineConfig, providerConfig, spec, olds, news) if err != nil { return err } @@ -60,13 +61,13 @@ func ApplyAutoNaming( // Defaults to the name followed by 7 random hex characters separated by a '-'. func getDefaultName( urn resource.URN, - engineAutonaming EngineAutonamingConfiguration, - autoNamingSpec *metadata.AutoNamingSpec, + engineConfig EngineAutoNamingConfig, + providerConfig *ProviderAutoNamingConfig, + propertySpec *metadata.AutoNamingSpec, olds, news resource.PropertyMap, - config *AutoNamingConfig, ) (*resource.PropertyValue, error) { - sdkName := autoNamingSpec.SdkName + sdkName := propertySpec.SdkName // Prefer explicitly specified name if v, ok := news[resource.PropertyKey(sdkName)]; ok { @@ -79,20 +80,25 @@ func getDefaultName( } // Generate naming trivia for the resource. - namingTriviaApplies, namingTrivia, err := CheckNamingTrivia(sdkName, news, autoNamingSpec.TriviaSpec) + namingTriviaApplies, namingTrivia, err := CheckNamingTrivia(sdkName, news, propertySpec.TriviaSpec) if err != nil { return nil, err } - if engineAutonaming.AutonamingMode != nil { - switch *engineAutonaming.AutonamingMode { + if engineConfig.AutonamingMode != nil { + // Engine autonaming conflicts with provider autonaming. If both are specified, return an error. + if providerConfig != nil { + return nil, fmt.Errorf("pulumi:autonaming conflicts with provider autonaming configuration, please specify only one") + } + + switch *engineConfig.AutonamingMode { case EngineAutonamingModeDisable: return nil, nil case EngineAutonamingModeEnforce: - v := resource.NewStringProperty(engineAutonaming.ProposedName) + v := resource.NewStringProperty(engineConfig.ProposedName) return &v, nil case EngineAutonamingModePropose: - proposedName := engineAutonaming.ProposedName + proposedName := engineConfig.ProposedName // Apply naming trivia to the generated name. if namingTriviaApplies { @@ -100,11 +106,11 @@ func getDefaultName( } // Validate the proposed name against the length constraints. - if autoNamingSpec.MaxLength > 0 && len(proposedName) > autoNamingSpec.MaxLength { - return nil, fmt.Errorf("proposed name %q exceeds max length of %d", proposedName, autoNamingSpec.MaxLength) + if propertySpec.MaxLength > 0 && len(proposedName) > propertySpec.MaxLength { + return nil, fmt.Errorf("proposed name %q exceeds max length of %d", proposedName, propertySpec.MaxLength) } - if autoNamingSpec.MinLength > 0 && len(proposedName) < autoNamingSpec.MinLength { - return nil, fmt.Errorf("proposed name %q is shorter than min length of %d", proposedName, autoNamingSpec.MinLength) + if propertySpec.MinLength > 0 && len(proposedName) < propertySpec.MinLength { + return nil, fmt.Errorf("proposed name %q is shorter than min length of %d", proposedName, propertySpec.MinLength) } v := resource.NewStringProperty(proposedName) @@ -115,10 +121,10 @@ func getDefaultName( var autoTrim bool // resource.NewUniqueName does not allow for a random suffix shorter than 1. randomSuffixMinLength := 1 - if config != nil { - autoTrim = config.AutoTrim - if config.RandomSuffixMinLength != 0 { - randomSuffixMinLength = config.RandomSuffixMinLength + if providerConfig != nil { + autoTrim = providerConfig.AutoTrim + if providerConfig.RandomSuffixMinLength != 0 { + randomSuffixMinLength = providerConfig.RandomSuffixMinLength } } @@ -129,21 +135,21 @@ func getDefaultName( if randomSuffixMinLength > randLength { randLength = randomSuffixMinLength } - if len(prefix)+namingTrivia.Length()+randLength < autoNamingSpec.MinLength { - randLength = autoNamingSpec.MinLength - len(prefix) - namingTrivia.Length() + if len(prefix)+namingTrivia.Length()+randLength < propertySpec.MinLength { + randLength = propertySpec.MinLength - len(prefix) - namingTrivia.Length() } maxLength := 0 - if autoNamingSpec.MaxLength > 0 { - left := autoNamingSpec.MaxLength - len(prefix) - namingTrivia.Length() + if propertySpec.MaxLength > 0 { + left := propertySpec.MaxLength - len(prefix) - namingTrivia.Length() if left <= 0 && autoTrim { - autoTrimMaxLength := autoNamingSpec.MaxLength - namingTrivia.Length() - randomSuffixMinLength + autoTrimMaxLength := propertySpec.MaxLength - namingTrivia.Length() - randomSuffixMinLength if autoTrimMaxLength <= 0 { return nil, fmt.Errorf("failed to auto-generate value for %[1]q."+ " Prefix: %[2]q is too large to fix max length constraint of %[3]d"+ " with required suffix length %[4]d. Please provide a value for %[1]q", - sdkName, prefix, autoNamingSpec.MaxLength, randomSuffixMinLength) + sdkName, prefix, propertySpec.MaxLength, randomSuffixMinLength) } prefix = trimName(prefix, autoTrimMaxLength) randLength = randomSuffixMinLength @@ -155,11 +161,11 @@ func getDefaultName( return nil, fmt.Errorf("failed to auto-generate value for %[1]q."+ " Prefix: %[2]q is too large to fix max length constraint of %[3]d"+ " with required suffix %[4]q. Please provide a value for %[1]q", - sdkName, prefix, autoNamingSpec.MaxLength, namingTrivia.Suffix) + sdkName, prefix, propertySpec.MaxLength, namingTrivia.Suffix) } else { return nil, fmt.Errorf("failed to auto-generate value for %[1]q."+ " Prefix: %[2]q is too large to fix max length constraint of %[3]d. Please provide a value for %[1]q", - sdkName, prefix, autoNamingSpec.MaxLength) + sdkName, prefix, propertySpec.MaxLength) } } if left < randLength { @@ -169,7 +175,7 @@ func getDefaultName( } // Resource name is URN name + "-" + random suffix. - random, err := resource.NewUniqueName(engineAutonaming.RandomSeed, prefix, randLength, maxLength, nil) + random, err := resource.NewUniqueName(engineConfig.RandomSeed, prefix, randLength, maxLength, nil) if err != nil { return nil, err } diff --git a/provider/pkg/autonaming/application_test.go b/provider/pkg/autonaming/application_test.go index 11bec297d0..a42d9bbce6 100644 --- a/provider/pkg/autonaming/application_test.go +++ b/provider/pkg/autonaming/application_test.go @@ -18,15 +18,16 @@ func Test_getDefaultName(t *testing.T) { return &v } tests := []struct { - name string - minLength int - maxLength int - olds resource.PropertyMap - news resource.PropertyMap - err error - comparison func(t *testing.T, actual *resource.PropertyValue) bool - engineAutonaming EngineAutonamingConfiguration - noName bool + name string + minLength int + maxLength int + olds resource.PropertyMap + news resource.PropertyMap + err error + comparison func(t *testing.T, actual *resource.PropertyValue) bool + engineConfig EngineAutoNamingConfig + providerConfig *ProviderAutoNamingConfig + noName bool }{ { name: "Name specified explicitly", @@ -70,7 +71,7 @@ func Test_getDefaultName(t *testing.T) { }, { name: "Autoname with AutonamingMode=Propose", - engineAutonaming: EngineAutonamingConfiguration{ + engineConfig: EngineAutoNamingConfig{ AutonamingMode: p(EngineAutonamingModePropose), ProposedName: "proposed-name", }, @@ -78,7 +79,7 @@ func Test_getDefaultName(t *testing.T) { }, { name: "Autoname with AutonamingMode=Enforce", - engineAutonaming: EngineAutonamingConfiguration{ + engineConfig: EngineAutoNamingConfig{ AutonamingMode: p(EngineAutonamingModeEnforce), ProposedName: "enforced-name", }, @@ -86,7 +87,7 @@ func Test_getDefaultName(t *testing.T) { }, { name: "Autoname with AutonamingMode=Disable", - engineAutonaming: EngineAutonamingConfiguration{ + engineConfig: EngineAutoNamingConfig{ AutonamingMode: p(EngineAutonamingModeDisable), }, comparison: func(t *testing.T, actual *resource.PropertyValue) bool { @@ -96,7 +97,7 @@ func Test_getDefaultName(t *testing.T) { }, { name: "Autoname with Propose mode and max length constraints", - engineAutonaming: EngineAutonamingConfiguration{ + engineConfig: EngineAutoNamingConfig{ AutonamingMode: p(EngineAutonamingModePropose), ProposedName: "very-long-proposed-name", }, @@ -105,7 +106,7 @@ func Test_getDefaultName(t *testing.T) { }, { name: "Autoname with Propose mode and min length constraints", - engineAutonaming: EngineAutonamingConfiguration{ + engineConfig: EngineAutoNamingConfig{ AutonamingMode: p(EngineAutonamingModePropose), ProposedName: "too-short-proposed-name", }, @@ -114,7 +115,7 @@ func Test_getDefaultName(t *testing.T) { }, { name: "Autoname with Propose mode and trivia", - engineAutonaming: EngineAutonamingConfiguration{ + engineConfig: EngineAutoNamingConfig{ AutonamingMode: p(EngineAutonamingModePropose), ProposedName: "proposed-name", }, @@ -123,6 +124,18 @@ func Test_getDefaultName(t *testing.T) { }, comparison: equals(resource.NewStringProperty("proposed-name-trivia-value")), }, + { + name: "Engine autonaming config conflicts with provider autonaming config", + engineConfig: EngineAutoNamingConfig{ + AutonamingMode: p(EngineAutonamingModePropose), + ProposedName: "proposed-name", + }, + providerConfig: &ProviderAutoNamingConfig{ + AutoTrim: true, + RandomSuffixMinLength: 5, + }, + err: fmt.Errorf("pulumi:autonaming conflicts with provider autonaming configuration, please specify only one"), + }, } urn := resource.URN("urn:pulumi:dev::test::test-provider:testModule:TestResource::myName") @@ -146,7 +159,7 @@ func Test_getDefaultName(t *testing.T) { }, }, } - got, err := getDefaultName(urn, tt.engineAutonaming, autoNamingSpec, tt.olds, tt.news, nil) + got, err := getDefaultName(urn, tt.engineConfig, tt.providerConfig, autoNamingSpec, tt.olds, tt.news) if tt.err != nil { require.EqualError(t, err, tt.err.Error()) return @@ -167,7 +180,7 @@ func Test_getDefaultName_withAutoNameConfig(t *testing.T) { tests := []struct { name string resourceName string - autoNameConfig AutoNamingConfig + autoNameConfig ProviderAutoNamingConfig minLength int maxLength int olds resource.PropertyMap @@ -181,7 +194,7 @@ func Test_getDefaultName_withAutoNameConfig(t *testing.T) { news: resource.PropertyMap{ resource.PropertyKey(sdkName): resource.NewStringProperty("newName"), }, - autoNameConfig: AutoNamingConfig{ + autoNameConfig: ProviderAutoNamingConfig{ AutoTrim: true, RandomSuffixMinLength: 1, }, @@ -194,7 +207,7 @@ func Test_getDefaultName_withAutoNameConfig(t *testing.T) { olds: resource.PropertyMap{ resource.PropertyKey(sdkName): resource.NewStringProperty("oldName"), }, - autoNameConfig: AutoNamingConfig{ + autoNameConfig: ProviderAutoNamingConfig{ AutoTrim: true, RandomSuffixMinLength: 1, }, @@ -205,7 +218,7 @@ func Test_getDefaultName_withAutoNameConfig(t *testing.T) { resourceName: "myReallyLongName", name: "Autoname with constraints on max length", maxLength: 12, - autoNameConfig: AutoNamingConfig{ + autoNameConfig: ProviderAutoNamingConfig{ AutoTrim: true, }, comparison: startsWith("myReagName", 1), @@ -214,7 +227,7 @@ func Test_getDefaultName_withAutoNameConfig(t *testing.T) { resourceName: "myReallyLongName", name: "Autoname with odd constraints on max length", maxLength: 13, - autoNameConfig: AutoNamingConfig{ + autoNameConfig: ProviderAutoNamingConfig{ AutoTrim: true, }, comparison: startsWith("myRealgName", 1), @@ -223,7 +236,7 @@ func Test_getDefaultName_withAutoNameConfig(t *testing.T) { name: "Autoname with max length too small and no auto trim", resourceName: "myName", maxLength: 4, - autoNameConfig: AutoNamingConfig{ + autoNameConfig: ProviderAutoNamingConfig{ RandomSuffixMinLength: 2, }, comparison: within(15, 15), @@ -233,7 +246,7 @@ func Test_getDefaultName_withAutoNameConfig(t *testing.T) { name: "Autoname with constraints on min and max length", minLength: 13, resourceName: "myReallyLongName", - autoNameConfig: AutoNamingConfig{ + autoNameConfig: ProviderAutoNamingConfig{ RandomSuffixMinLength: 2, AutoTrim: true, }, @@ -243,7 +256,7 @@ func Test_getDefaultName_withAutoNameConfig(t *testing.T) { { name: "Autoname with long random suffix", resourceName: "myReallyLongName", - autoNameConfig: AutoNamingConfig{ + autoNameConfig: ProviderAutoNamingConfig{ RandomSuffixMinLength: 14, AutoTrim: true, }, @@ -263,8 +276,8 @@ func Test_getDefaultName_withAutoNameConfig(t *testing.T) { } autoNameConfig := tt.autoNameConfig - engineAutonaming := EngineAutonamingConfiguration{} - got, err := getDefaultName(urn, engineAutonaming, autoNamingSpec, tt.olds, tt.news, &autoNameConfig) + engineAutonaming := EngineAutoNamingConfig{} + got, err := getDefaultName(urn, engineAutonaming, &autoNameConfig, autoNamingSpec, tt.olds, tt.news) if tt.err != nil { require.EqualError(t, err, tt.err.Error()) return diff --git a/provider/pkg/provider/provider.go b/provider/pkg/provider/provider.go index c500c22a93..07856d1b53 100644 --- a/provider/pkg/provider/provider.go +++ b/provider/pkg/provider/provider.go @@ -108,7 +108,7 @@ type cfnProvider struct { partition partition resourceMap *metadata.CloudAPIMetadata roleArn *string - autoNamingConfig *autonaming.AutoNamingConfig + autoNamingConfig *autonaming.ProviderAutoNamingConfig allowedAccountIds []string forbiddenAccountIds []string defaultTags map[string]string @@ -522,7 +522,7 @@ func (p *cfnProvider) Configure(ctx context.Context, req *pulumirpc.ConfigureReq } if autoNaming, ok := vars["aws-native:config:autoNaming"]; ok { - var autoNamingConfig autonaming.AutoNamingConfig + var autoNamingConfig autonaming.ProviderAutoNamingConfig if err := json.Unmarshal([]byte(autoNaming), &autoNamingConfig); err != nil { return nil, fmt.Errorf("failed to unmarshal 'autoNaming' config: %w", err) } @@ -755,8 +755,8 @@ func (p *cfnProvider) Check(ctx context.Context, req *pulumirpc.CheckRequest) (* return nil, errors.Errorf("resource type %s not found", resourceToken) } - if err := autonaming.ApplyAutoNaming(spec.AutoNamingSpec, urn, engineAutonaming(req), - olds, newInputs, p.autoNamingConfig); err != nil { + if err := autonaming.ApplyAutoNaming(spec.AutoNamingSpec, urn, engineAutonaming(req), p.autoNamingConfig, + olds, newInputs); err != nil { return nil, fmt.Errorf("failed to apply auto-naming: %w", err) } @@ -794,8 +794,8 @@ func (p *cfnProvider) Check(ctx context.Context, req *pulumirpc.CheckRequest) (* return &pulumirpc.CheckResponse{Failures: checkFailures}, nil } -func engineAutonaming(req *pulumirpc.CheckRequest) autonaming.EngineAutonamingConfiguration { - engineAutonaming := autonaming.EngineAutonamingConfiguration{ +func engineAutonaming(req *pulumirpc.CheckRequest) autonaming.EngineAutoNamingConfig { + engineAutonaming := autonaming.EngineAutoNamingConfig{ RandomSeed: req.RandomSeed, } if req.Autonaming != nil { diff --git a/provider/pkg/provider/provider_test.go b/provider/pkg/provider/provider_test.go index bc2241cd26..2861aabd0c 100644 --- a/provider/pkg/provider/provider_test.go +++ b/provider/pkg/provider/provider_test.go @@ -66,7 +66,7 @@ func TestConfigure(t *testing.T) { assert.NoError(t, err) assert.NotNil(t, provider.autoNamingConfig) - assert.Equal(t, autonaming.AutoNamingConfig{ + assert.Equal(t, autonaming.ProviderAutoNamingConfig{ AutoTrim: true, RandomSuffixMinLength: 5, }, *provider.autoNamingConfig) @@ -85,7 +85,7 @@ func TestConfigure(t *testing.T) { assert.NoError(t, err) assert.NotNil(t, provider.autoNamingConfig) - assert.Equal(t, autonaming.AutoNamingConfig{}, *provider.autoNamingConfig) + assert.Equal(t, autonaming.ProviderAutoNamingConfig{}, *provider.autoNamingConfig) }) t.Run("AutoNaming config invalid", func(t *testing.T) { diff --git a/provider/pkg/resources/cfn_custom_resource.go b/provider/pkg/resources/cfn_custom_resource.go index 240f6e7b72..2768d7f9a8 100644 --- a/provider/pkg/resources/cfn_custom_resource.go +++ b/provider/pkg/resources/cfn_custom_resource.go @@ -233,7 +233,7 @@ type customResourceInvokeData struct { // Check validates the inputs of the resource and applies default values if necessary. // It returns the inputs, validation failures, and an error if the inputs cannot be unmarshalled. -func (c *cfnCustomResource) Check(ctx context.Context, urn urn.URN, _ autonaming.EngineAutonamingConfiguration, inputs resource.PropertyMap, state resource.PropertyMap, defaultTags map[string]string) (resource.PropertyMap, []ValidationFailure, error) { +func (c *cfnCustomResource) Check(ctx context.Context, urn urn.URN, _ autonaming.EngineAutoNamingConfig, inputs resource.PropertyMap, state resource.PropertyMap, defaultTags map[string]string) (resource.PropertyMap, []ValidationFailure, error) { var typedInputs CfnCustomResourceInputs _, err := resourcex.Unmarshal(&typedInputs, inputs, resourcex.UnmarshalOptions{}) if err != nil { diff --git a/provider/pkg/resources/cfn_custom_resource_test.go b/provider/pkg/resources/cfn_custom_resource_test.go index ab267a7237..f50f8576e3 100644 --- a/provider/pkg/resources/cfn_custom_resource_test.go +++ b/provider/pkg/resources/cfn_custom_resource_test.go @@ -118,7 +118,7 @@ func TestCfnCustomResource_Check(t *testing.T) { c := &cfnCustomResource{} ctx := context.Background() urn := urn.URN("urn:pulumi:testProject::test::aws-native:cloudformation:CfnCustomResource::dummy") - engineAutonaming := autonaming.EngineAutonamingConfiguration{ + engineAutonaming := autonaming.EngineAutoNamingConfig{ RandomSeed: []byte{}, } state := resource.PropertyMap{} diff --git a/provider/pkg/resources/custom.go b/provider/pkg/resources/custom.go index 028dcf5b2c..f8486fdb37 100644 --- a/provider/pkg/resources/custom.go +++ b/provider/pkg/resources/custom.go @@ -13,7 +13,7 @@ import ( //go:generate mockgen -package resources -source custom.go -destination mock_custom_resource.go CustomResource type CustomResource interface { // Check validates and transforms the inputs of the resource. - Check(ctx context.Context, urn resource.URN, engineAutonaming autonaming.EngineAutonamingConfiguration, + Check(ctx context.Context, urn resource.URN, engineAutonaming autonaming.EngineAutoNamingConfig, inputs, state resource.PropertyMap, defaultTags map[string]string) (resource.PropertyMap, []ValidationFailure, error) // Create creates a new resource in the cloud provider and returns its unique identifier and outputs. Create(ctx context.Context, urn resource.URN, inputs resource.PropertyMap, timeout time.Duration) (identifier *string, outputs resource.PropertyMap, err error) diff --git a/provider/pkg/resources/extension_resource.go b/provider/pkg/resources/extension_resource.go index 4770937dce..a9cf2b585c 100644 --- a/provider/pkg/resources/extension_resource.go +++ b/provider/pkg/resources/extension_resource.go @@ -46,7 +46,7 @@ func NewExtensionResource(client client.CloudControlClient) *extensionResource { } func (r *extensionResource) Check(ctx context.Context, urn resource.URN, - engineAutonaming autonaming.EngineAutonamingConfiguration, inputs, state resource.PropertyMap, + engineAutonaming autonaming.EngineAutoNamingConfig, inputs, state resource.PropertyMap, defaultTags map[string]string) (resource.PropertyMap, []ValidationFailure, error) { var typedInputs ExtensionResourceInputs _, err := resourcex.Unmarshal(&typedInputs, inputs, resourcex.UnmarshalOptions{}) @@ -66,7 +66,7 @@ func (r *extensionResource) Check(ctx context.Context, urn resource.URN, SdkName: typedInputs.AutoNaming.PropertyName, MinLength: typedInputs.AutoNaming.MinLength, MaxLength: typedInputs.AutoNaming.MaxLength, - }, urn, engineAutonaming, state, inputs, nil); err != nil { + }, urn, engineAutonaming, nil, state, inputs); err != nil { return nil, nil, fmt.Errorf("failed to apply auto-naming: %w", err) } } diff --git a/provider/pkg/resources/mock_custom_resource.go b/provider/pkg/resources/mock_custom_resource.go index 5eab71693b..0aa29c6e70 100644 --- a/provider/pkg/resources/mock_custom_resource.go +++ b/provider/pkg/resources/mock_custom_resource.go @@ -44,7 +44,7 @@ func (m *MockCustomResource) EXPECT() *MockCustomResourceMockRecorder { } // Check mocks base method. -func (m *MockCustomResource) Check(ctx context.Context, urn resource.URN, engineAutonaming autonaming.EngineAutonamingConfiguration, inputs, state resource.PropertyMap, defaultTags map[string]string) (resource.PropertyMap, []ValidationFailure, error) { +func (m *MockCustomResource) Check(ctx context.Context, urn resource.URN, engineAutonaming autonaming.EngineAutoNamingConfig, inputs, state resource.PropertyMap, defaultTags map[string]string) (resource.PropertyMap, []ValidationFailure, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Check", ctx, urn, engineAutonaming, inputs, state, defaultTags) ret0, _ := ret[0].(resource.PropertyMap) From 9afde90331cfca73fba007ebaa3320008a9d1a89 Mon Sep 17 00:00:00 2001 From: Mikhail Shilkov Date: Fri, 20 Dec 2024 15:25:02 +0100 Subject: [PATCH 5/5] Skip e2e test if short --- provider/pkg/provider/provider_2e2_test.go | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/provider/pkg/provider/provider_2e2_test.go b/provider/pkg/provider/provider_2e2_test.go index 964f81fd7b..55bfd99b95 100644 --- a/provider/pkg/provider/provider_2e2_test.go +++ b/provider/pkg/provider/provider_2e2_test.go @@ -32,7 +32,7 @@ func TestE2eSnapshots(t *testing.T) { } func TestAutonaming(t *testing.T) { - t.Parallel() + skipIfShort(t) pt := newAwsTest(t, filepath.Join("testdata", "autonaming"), opttest.Env("PULUMI_EXPERIMENTAL", "1")) pt.Preview(t) up := pt.Up(t) @@ -76,3 +76,9 @@ func testProviderServer() (pulumirpc.ResourceProviderServer, error) { } return provider.NewAwsNativeProvider(nil, "aws-native", "0.1.0", schemaBytes, metadataBytes) } + +func skipIfShort(t *testing.T) { + if testing.Short() { + t.Skip("skipping long-running test in short mode") + } +}