From 7b3ace6c3f3b585eed79a704f9e843ab7a386d3f Mon Sep 17 00:00:00 2001 From: Mikhail Shilkov Date: Thu, 12 Dec 2024 14:30:39 +0100 Subject: [PATCH] Autonaming configuration in Configure and Check (#2675) This PR implements the bridge part of [Autonaming Configuration](https://github.com/pulumi/pulumi/issues/1518). See [RFC](https://github.com/pulumi/pulumi/discussions/17592) for the full design. In short, [Protobuf definition for autonaming configuration](https://github.com/pulumi/pulumi/pull/17810) introduced the protobuf changes required for the provider-side implementation. With those, a provider can: - Declare that it supports autonaming configurations with a response flag in Configure - Accept two extra properties in CheckRequest: a proposed name and a mode to apply it This PR implements autonaming configuration for all bridged providers. It passes the configuration from CheckRequest all the way down to the autonaming module. The module itself is now able to understand the modes and use the proposed name appropriately: - For `disable` mode, it will disable autonaming and throw an error if no explicit name was provided by a user - For `enforce` mode, it will use the proposed name verbatim. Note that all checks on that name are ignored: this is by design, so that users would be able to override the provider's settings with `enforce: true` if they need to - For `propose` mode, it will try using the proposed name but can also modify and validate it. The bridge applies transformations (e.g. lowercasing the name) and checks max length/character set requirements. See the test cases for details. No changes are needed to the provider themselves beyond rolling to the new version of TF bridge once it's published. The PR is split into three commits: (1) all the manual changes, (2) mechanical updates of replay tests, (3) end-to-end tests. Resolves https://github.com/pulumi/pulumi-terraform-bridge/issues/2722 --- dynamic/provider_test.go | 1 + pkg/pf/internal/defaults/defaults.go | 1 + pkg/pf/internal/defaults/defaults_test.go | 27 ++ pkg/pf/internal/plugin/provider_context.go | 14 +- pkg/pf/internal/plugin/provider_server.go | 16 +- pkg/pf/tests/autonaming_test.go | 57 ++++ pkg/pf/tests/provider_check_test.go | 6 +- pkg/pf/tests/provider_configure_test.go | 15 +- pkg/pf/tests/provider_read_test.go | 6 +- .../genrandom/random-delete-preview.json | 3 +- .../genrandom/random-delete-update.json | 3 +- .../genrandom/random-empty-preview.json | 3 +- .../genrandom/random-empty-update.json | 3 +- .../genrandom/random-initial-preview.json | 3 +- .../genrandom/random-initial-update.json | 3 +- .../genrandom/random-replace-preview.json | 3 +- .../genrandom/random-replace-update.json | 3 +- pkg/pf/tests/testdata/updateprogram.json | 24 +- pkg/pf/tfbridge/provider_check.go | 3 + pkg/tests/autonaming_test.go | 53 ++++ pkg/tfbridge/info/autonaming.go | 42 ++- pkg/tfbridge/info/autonaming_test.go | 249 ++++++++++++++++++ pkg/tfbridge/info/info.go | 27 ++ pkg/tfbridge/names.go | 1 + pkg/tfbridge/names_test.go | 27 ++ pkg/tfbridge/provider.go | 15 +- pkg/tfbridge/provider_test.go | 69 ++++- pkg/tfbridge/schema.go | 2 + pkg/x/muxer/muxer.go | 11 +- pkg/x/muxer/tests/muxer_test.go | 9 +- x/muxer/tests/muxer_test.go | 23 +- 31 files changed, 666 insertions(+), 56 deletions(-) create mode 100644 pkg/pf/tests/autonaming_test.go create mode 100644 pkg/tests/autonaming_test.go create mode 100644 pkg/tfbridge/info/autonaming_test.go diff --git a/dynamic/provider_test.go b/dynamic/provider_test.go index 41753566c..c921fba03 100644 --- a/dynamic/provider_test.go +++ b/dynamic/provider_test.go @@ -234,6 +234,7 @@ func TestConfigure(t *testing.T) { }), }, noParallel, expect(autogold.Expect(`{ "acceptResources": true, + "supportsAutonamingConfiguration": true, "supportsPreview": true }`)))(t) diff --git a/pkg/pf/internal/defaults/defaults.go b/pkg/pf/internal/defaults/defaults.go index 1dde80e5d..628e928ed 100644 --- a/pkg/pf/internal/defaults/defaults.go +++ b/pkg/pf/internal/defaults/defaults.go @@ -153,6 +153,7 @@ func getDefaultValue( URN: cdOptions.URN, Properties: cdOptions.Properties, Seed: cdOptions.Seed, + Autonaming: cdOptions.Autonaming, }) if err != nil { msg := fmt.Errorf("Failed computing a default value for property '%s': %w", diff --git a/pkg/pf/internal/defaults/defaults_test.go b/pkg/pf/internal/defaults/defaults_test.go index db8e06c84..b95ce46eb 100644 --- a/pkg/pf/internal/defaults/defaults_test.go +++ b/pkg/pf/internal/defaults/defaults_test.go @@ -23,6 +23,7 @@ import ( "github.com/stretchr/testify/require" "github.com/pulumi/pulumi-terraform-bridge/v3/pkg/tfbridge" + "github.com/pulumi/pulumi-terraform-bridge/v3/pkg/tfbridge/info" shim "github.com/pulumi/pulumi-terraform-bridge/v3/pkg/tfshim" "github.com/pulumi/pulumi-terraform-bridge/v3/pkg/tfshim/schema" ) @@ -66,6 +67,10 @@ func TestApplyDefaultInfoValues(t *testing.T) { return resource.NewStringProperty(unique), err } + testFromAutoname := func(res *tfbridge.PulumiResource) (interface{}, error) { + return resource.NewStringProperty(res.Autonaming.ProposedName), nil + } + testComputeDefaults := func( t *testing.T, expectPriorValue resource.PropertyValue, @@ -238,6 +243,28 @@ func TestApplyDefaultInfoValues(t *testing.T) { "stringProp": resource.NewStringProperty("n1-453"), }, }, + { + name: "From function can compute defaults with autoname", + fieldInfos: map[string]*tfbridge.SchemaInfo{ + "string_prop": { + Default: &tfbridge.DefaultInfo{ + From: testFromAutoname, + }, + }, + }, + computeDefaultOptions: tfbridge.ComputeDefaultOptions{ + URN: "urn:pulumi:test::test::pkgA:index:t1::n1", + Properties: resource.PropertyMap{}, + Seed: []byte(`123`), + Autonaming: &info.ComputeDefaultAutonamingOptions{ + ProposedName: "n1-777", + Mode: info.ComputeDefaultAutonamingModePropose, + }, + }, + expected: resource.PropertyMap{ + "stringProp": resource.NewStringProperty("n1-777"), + }, + }, { name: "ComputeDefaults function can compute nested defaults", fieldInfos: map[string]*tfbridge.SchemaInfo{ diff --git a/pkg/pf/internal/plugin/provider_context.go b/pkg/pf/internal/plugin/provider_context.go index b38c539ae..bdb5db555 100644 --- a/pkg/pf/internal/plugin/provider_context.go +++ b/pkg/pf/internal/plugin/provider_context.go @@ -25,6 +25,8 @@ import ( "github.com/pulumi/pulumi/sdk/v3/go/common/workspace" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" + + "github.com/pulumi/pulumi-terraform-bridge/v3/pkg/tfbridge/info" ) // A version of Provider interface that is enhanced by giving access to the request Context. @@ -44,7 +46,8 @@ type ProviderWithContext interface { ConfigureWithContext(ctx context.Context, inputs resource.PropertyMap) error CheckWithContext(ctx context.Context, urn resource.URN, olds, news resource.PropertyMap, - allowUnknowns bool, randomSeed []byte) (resource.PropertyMap, []p.CheckFailure, error) + allowUnknowns bool, randomSeed []byte, autonaming *info.ComputeDefaultAutonamingOptions, + ) (resource.PropertyMap, []p.CheckFailure, error) DiffWithContext(ctx context.Context, urn resource.URN, id resource.ID, olds resource.PropertyMap, news resource.PropertyMap, allowUnknowns bool, ignoreChanges []string) (p.DiffResult, error) @@ -148,8 +151,15 @@ func (prov *provider) Configure( func (prov *provider) Check( ctx context.Context, req plugin.CheckRequest, ) (plugin.CheckResponse, error) { + var autonaming *info.ComputeDefaultAutonamingOptions + if req.Autonaming != nil { + autonaming = &info.ComputeDefaultAutonamingOptions{ + ProposedName: req.Autonaming.ProposedName, + Mode: info.ComputeDefaultAutonamingOptionsMode(req.Autonaming.Mode), + } + } c, f, err := prov.ProviderWithContext.CheckWithContext( - ctx, req.URN, req.Olds, req.News, req.AllowUnknowns, req.RandomSeed) + ctx, req.URN, req.Olds, req.News, req.AllowUnknowns, req.RandomSeed, autonaming) return plugin.CheckResponse{Properties: c, Failures: f}, err } diff --git a/pkg/pf/internal/plugin/provider_server.go b/pkg/pf/internal/plugin/provider_server.go index 3b2de812c..4580c5354 100644 --- a/pkg/pf/internal/plugin/provider_server.go +++ b/pkg/pf/internal/plugin/provider_server.go @@ -29,6 +29,8 @@ import ( pulumirpc "github.com/pulumi/pulumi/sdk/v3/proto/go" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" + + "github.com/pulumi/pulumi-terraform-bridge/v3/pkg/tfbridge/info" ) type providerServer struct { @@ -333,6 +335,10 @@ func (p *providerServer) Configure(ctx context.Context, // reason about data flow within the underlying provider (TF), we allow // the engine to apply its own heuristics. AcceptSecrets: false, + + // Check will accept a configuration property for engine to propose auto-naming format and mode + // when user opts in to control it. + SupportsAutonamingConfiguration: true, }, nil } @@ -349,7 +355,15 @@ func (p *providerServer) Check(ctx context.Context, req *pulumirpc.CheckRequest) return nil, err } - newInputs, failures, err := p.provider.CheckWithContext(ctx, urn, state, inputs, true, req.RandomSeed) + var autonaming *info.ComputeDefaultAutonamingOptions + if req.Autonaming != nil { + autonaming = &info.ComputeDefaultAutonamingOptions{ + ProposedName: req.Autonaming.ProposedName, + Mode: info.ComputeDefaultAutonamingOptionsMode(req.Autonaming.Mode), + } + } + + newInputs, failures, err := p.provider.CheckWithContext(ctx, urn, state, inputs, true, req.RandomSeed, autonaming) if err != nil { return nil, err } diff --git a/pkg/pf/tests/autonaming_test.go b/pkg/pf/tests/autonaming_test.go new file mode 100644 index 000000000..963192a32 --- /dev/null +++ b/pkg/pf/tests/autonaming_test.go @@ -0,0 +1,57 @@ +package tfbridgetests + +import ( + "testing" + + rschema "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/pulumi/providertest/pulumitest/opttest" + "github.com/stretchr/testify/require" + + "github.com/pulumi/pulumi-terraform-bridge/v3/pkg/pf/tests/internal/providerbuilder" + "github.com/pulumi/pulumi-terraform-bridge/v3/pkg/pf/tests/pulcheck" + "github.com/pulumi/pulumi-terraform-bridge/v3/pkg/tfbridge" +) + +func TestAutonaming(t *testing.T) { + t.Parallel() + provBuilder := providerbuilder.NewProvider( + providerbuilder.NewProviderArgs{ + AllResources: []providerbuilder.Resource{ + providerbuilder.NewResource(providerbuilder.NewResourceArgs{ + ResourceSchema: rschema.Schema{ + Attributes: map[string]rschema.Attribute{ + "name": rschema.StringAttribute{Optional: true}, + }, + }, + }), + }, + }) + + prov := bridgedProvider(provBuilder) + prov.Resources["testprovider_test"] = &tfbridge.ResourceInfo{ + Tok: "testprovider:index:Test", + Fields: map[string]*tfbridge.SchemaInfo{ + "name": tfbridge.AutoName("name", 50, "-"), + }, + } + program := ` +name: test +runtime: yaml +config: + pulumi:autonaming: + value: + pattern: ${project}-${name} +resources: + hello: + type: testprovider:index:Test +outputs: + testOut: ${hello.name} +` + opts := []opttest.Option{ + opttest.Env("PULUMI_EXPERIMENTAL", "true"), + } + pt, err := pulcheck.PulCheck(t, prov, program, opts...) + require.NoError(t, err) + res := pt.Up(t) + require.Equal(t, "test-hello", res.Outputs["testOut"].Value) +} diff --git a/pkg/pf/tests/provider_check_test.go b/pkg/pf/tests/provider_check_test.go index 30fc24d8d..87e12764c 100644 --- a/pkg/pf/tests/provider_check_test.go +++ b/pkg/pf/tests/provider_check_test.go @@ -190,7 +190,8 @@ func TestCheck(t *testing.T) { }, "response": { "supportsPreview": true, - "acceptResources": true + "acceptResources": true, + "supportsAutonamingConfiguration": true } }, { @@ -238,7 +239,8 @@ func TestCheck(t *testing.T) { }, "response": { "supportsPreview": true, - "acceptResources": true + "acceptResources": true, + "supportsAutonamingConfiguration": true } }, { diff --git a/pkg/pf/tests/provider_configure_test.go b/pkg/pf/tests/provider_configure_test.go index c294f2e66..a1602b2df 100644 --- a/pkg/pf/tests/provider_configure_test.go +++ b/pkg/pf/tests/provider_configure_test.go @@ -491,7 +491,8 @@ func TestConfigureToCreate(t *testing.T) { }, "response": { "supportsPreview": true, - "acceptResources": true + "acceptResources": true, + "supportsAutonamingConfiguration": true } }, { @@ -527,7 +528,8 @@ func TestConfigureBooleans(t *testing.T) { }, "response": { "supportsPreview": true, - "acceptResources": true + "acceptResources": true, + "supportsAutonamingConfiguration": true } }`) } @@ -610,7 +612,8 @@ func TestJSONNestedConfigure(t *testing.T) { }, "response": { "supportsPreview": true, - "acceptResources": true + "acceptResources": true, + "supportsAutonamingConfiguration": true } }`) } @@ -635,7 +638,8 @@ func TestJSONNestedConfigureWithSecrets(t *testing.T) { }, "response": { "supportsPreview": true, - "acceptResources": true + "acceptResources": true, + "supportsAutonamingConfiguration": true } }, { @@ -687,7 +691,8 @@ func TestConfigureWithSecrets(t *testing.T) { }, "response": { "supportsPreview": true, - "acceptResources": true + "acceptResources": true, + "supportsAutonamingConfiguration": true } }, { diff --git a/pkg/pf/tests/provider_read_test.go b/pkg/pf/tests/provider_read_test.go index 6012f9b91..0688fd5fa 100644 --- a/pkg/pf/tests/provider_read_test.go +++ b/pkg/pf/tests/provider_read_test.go @@ -57,7 +57,8 @@ func TestReadFromRefresh(t *testing.T) { }, "response": { "supportsPreview": true, - "acceptResources": true + "acceptResources": true, + "supportsAutonamingConfiguration": true }, "metadata": { "kind": "resource", @@ -433,7 +434,8 @@ func TestRefreshSupportsCustomID(t *testing.T) { }, "response": { "supportsPreview": true, - "acceptResources": true + "acceptResources": true, + "supportsAutonamingConfiguration": true }, "metadata": { "kind": "resource", diff --git a/pkg/pf/tests/testdata/genrandom/random-delete-preview.json b/pkg/pf/tests/testdata/genrandom/random-delete-preview.json index 6a7264150..78dac0ff5 100644 --- a/pkg/pf/tests/testdata/genrandom/random-delete-preview.json +++ b/pkg/pf/tests/testdata/genrandom/random-delete-preview.json @@ -328,7 +328,8 @@ }, "response": { "supportsPreview": true, - "acceptResources": true + "acceptResources": true, + "supportsAutonamingConfiguration": true }, "metadata": { "kind": "resource", diff --git a/pkg/pf/tests/testdata/genrandom/random-delete-update.json b/pkg/pf/tests/testdata/genrandom/random-delete-update.json index 5681bea1f..6bc378219 100644 --- a/pkg/pf/tests/testdata/genrandom/random-delete-update.json +++ b/pkg/pf/tests/testdata/genrandom/random-delete-update.json @@ -327,7 +327,8 @@ }, "response": { "supportsPreview": true, - "acceptResources": true + "acceptResources": true, + "supportsAutonamingConfiguration": true }, "metadata": { "kind": "resource", diff --git a/pkg/pf/tests/testdata/genrandom/random-empty-preview.json b/pkg/pf/tests/testdata/genrandom/random-empty-preview.json index 219e35808..c40350f99 100644 --- a/pkg/pf/tests/testdata/genrandom/random-empty-preview.json +++ b/pkg/pf/tests/testdata/genrandom/random-empty-preview.json @@ -373,7 +373,8 @@ }, "response": { "supportsPreview": true, - "acceptResources": true + "acceptResources": true, + "supportsAutonamingConfiguration": true }, "metadata": { "kind": "resource", diff --git a/pkg/pf/tests/testdata/genrandom/random-empty-update.json b/pkg/pf/tests/testdata/genrandom/random-empty-update.json index d78547677..65c4715f6 100644 --- a/pkg/pf/tests/testdata/genrandom/random-empty-update.json +++ b/pkg/pf/tests/testdata/genrandom/random-empty-update.json @@ -373,7 +373,8 @@ }, "response": { "supportsPreview": true, - "acceptResources": true + "acceptResources": true, + "supportsAutonamingConfiguration": true }, "metadata": { "kind": "resource", diff --git a/pkg/pf/tests/testdata/genrandom/random-initial-preview.json b/pkg/pf/tests/testdata/genrandom/random-initial-preview.json index 3cc612a36..51ff10f5e 100644 --- a/pkg/pf/tests/testdata/genrandom/random-initial-preview.json +++ b/pkg/pf/tests/testdata/genrandom/random-initial-preview.json @@ -348,7 +348,8 @@ }, "response": { "supportsPreview": true, - "acceptResources": true + "acceptResources": true, + "supportsAutonamingConfiguration": true }, "metadata": { "kind": "resource", diff --git a/pkg/pf/tests/testdata/genrandom/random-initial-update.json b/pkg/pf/tests/testdata/genrandom/random-initial-update.json index e0b41e54f..4065ec8ff 100644 --- a/pkg/pf/tests/testdata/genrandom/random-initial-update.json +++ b/pkg/pf/tests/testdata/genrandom/random-initial-update.json @@ -348,7 +348,8 @@ }, "response": { "supportsPreview": true, - "acceptResources": true + "acceptResources": true, + "supportsAutonamingConfiguration": true }, "metadata": { "kind": "resource", diff --git a/pkg/pf/tests/testdata/genrandom/random-replace-preview.json b/pkg/pf/tests/testdata/genrandom/random-replace-preview.json index d3f47b733..f8396d4ff 100644 --- a/pkg/pf/tests/testdata/genrandom/random-replace-preview.json +++ b/pkg/pf/tests/testdata/genrandom/random-replace-preview.json @@ -373,7 +373,8 @@ }, "response": { "supportsPreview": true, - "acceptResources": true + "acceptResources": true, + "supportsAutonamingConfiguration": true }, "metadata": { "kind": "resource", diff --git a/pkg/pf/tests/testdata/genrandom/random-replace-update.json b/pkg/pf/tests/testdata/genrandom/random-replace-update.json index 705f91859..a1086188a 100644 --- a/pkg/pf/tests/testdata/genrandom/random-replace-update.json +++ b/pkg/pf/tests/testdata/genrandom/random-replace-update.json @@ -373,7 +373,8 @@ }, "response": { "supportsPreview": true, - "acceptResources": true + "acceptResources": true, + "supportsAutonamingConfiguration": true }, "metadata": { "kind": "resource", diff --git a/pkg/pf/tests/testdata/updateprogram.json b/pkg/pf/tests/testdata/updateprogram.json index 580db34cb..84436a6ad 100644 --- a/pkg/pf/tests/testdata/updateprogram.json +++ b/pkg/pf/tests/testdata/updateprogram.json @@ -124,7 +124,8 @@ }, "response": { "supportsPreview": true, - "acceptResources": true + "acceptResources": true, + "supportsAutonamingConfiguration": true }, "metadata": { "kind": "resource", @@ -376,7 +377,8 @@ }, "response": { "supportsPreview": true, - "acceptResources": true + "acceptResources": true, + "supportsAutonamingConfiguration": true }, "metadata": { "kind": "resource", @@ -558,7 +560,8 @@ }, "response": { "supportsPreview": true, - "acceptResources": true + "acceptResources": true, + "supportsAutonamingConfiguration": true }, "metadata": { "kind": "resource", @@ -844,7 +847,8 @@ }, "response": { "supportsPreview": true, - "acceptResources": true + "acceptResources": true, + "supportsAutonamingConfiguration": true }, "metadata": { "kind": "resource", @@ -1099,7 +1103,8 @@ }, "response": { "supportsPreview": true, - "acceptResources": true + "acceptResources": true, + "supportsAutonamingConfiguration": true }, "metadata": { "kind": "resource", @@ -1200,7 +1205,8 @@ }, "response": { "supportsPreview": true, - "acceptResources": true + "acceptResources": true, + "supportsAutonamingConfiguration": true }, "metadata": { "kind": "resource", @@ -1536,7 +1542,8 @@ }, "response": { "supportsPreview": true, - "acceptResources": true + "acceptResources": true, + "supportsAutonamingConfiguration": true }, "metadata": { "kind": "resource", @@ -1839,7 +1846,8 @@ }, "response": { "supportsPreview": true, - "acceptResources": true + "acceptResources": true, + "supportsAutonamingConfiguration": true }, "metadata": { "kind": "resource", diff --git a/pkg/pf/tfbridge/provider_check.go b/pkg/pf/tfbridge/provider_check.go index 77cae635a..d6334a9bb 100644 --- a/pkg/pf/tfbridge/provider_check.go +++ b/pkg/pf/tfbridge/provider_check.go @@ -26,6 +26,7 @@ import ( "github.com/pulumi/pulumi-terraform-bridge/v3/pkg/convert" "github.com/pulumi/pulumi-terraform-bridge/v3/pkg/pf/internal/defaults" "github.com/pulumi/pulumi-terraform-bridge/v3/pkg/tfbridge" + "github.com/pulumi/pulumi-terraform-bridge/v3/pkg/tfbridge/info" ) // Check validates the given resource inputs from the user program and computes checked inputs that fill out default @@ -37,6 +38,7 @@ func (p *provider) CheckWithContext( inputs resource.PropertyMap, allowUnknowns bool, randomSeed []byte, + autonaming *info.ComputeDefaultAutonamingOptions, ) (resource.PropertyMap, []plugin.CheckFailure, error) { ctx = p.initLogging(ctx, p.logSink, urn) @@ -71,6 +73,7 @@ func (p *provider) CheckWithContext( Properties: checkedInputs, Seed: randomSeed, PriorState: priorState, + Autonaming: autonaming, }, PropertyMap: checkedInputs, ProviderConfig: p.lastKnownProviderConfig, diff --git a/pkg/tests/autonaming_test.go b/pkg/tests/autonaming_test.go new file mode 100644 index 000000000..7e8d8f38a --- /dev/null +++ b/pkg/tests/autonaming_test.go @@ -0,0 +1,53 @@ +package tests + +import ( + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/pulumi/providertest/pulumitest/opttest" + "github.com/stretchr/testify/require" + + "github.com/pulumi/pulumi-terraform-bridge/v3/pkg/internal/tests/pulcheck" + "github.com/pulumi/pulumi-terraform-bridge/v3/pkg/tfbridge" +) + +func TestAutonaming(t *testing.T) { + t.Parallel() + resMap := map[string]*schema.Resource{ + "prov_test": { + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Optional: true, + }, + }, + }, + } + tfp := &schema.Provider{ResourcesMap: resMap} + bridgedProvider := pulcheck.BridgedProvider(t, "prov", tfp) + bridgedProvider.Resources["prov_test"] = &tfbridge.ResourceInfo{ + Tok: "prov:index:Test", + Fields: map[string]*tfbridge.SchemaInfo{ + "name": tfbridge.AutoName("name", 50, "-"), + }, + } + program := ` +name: test +runtime: yaml +config: + pulumi:autonaming: + value: + pattern: ${name}-world +resources: + hello: + type: prov:index:Test +outputs: + testOut: ${hello.name} +` + opts := []opttest.Option{ + opttest.Env("PULUMI_EXPERIMENTAL", "true"), + } + pt := pulcheck.PulCheck(t, bridgedProvider, program, opts...) + res := pt.Up(t) + require.Equal(t, "hello-world", res.Outputs["testOut"].Value) +} diff --git a/pkg/tfbridge/info/autonaming.go b/pkg/tfbridge/info/autonaming.go index 5aa2ab395..370f7bfc8 100644 --- a/pkg/tfbridge/info/autonaming.go +++ b/pkg/tfbridge/info/autonaming.go @@ -17,6 +17,7 @@ package info import ( "context" "fmt" + "strings" "github.com/golang/glog" "github.com/pkg/errors" @@ -170,7 +171,45 @@ func ComputeAutoNameDefault( if options.Transform != nil { vs = options.Transform(vs) } - if options.Randlen > 0 { + if defaultOptions.Autonaming != nil { + switch defaultOptions.Autonaming.Mode { + case ComputeDefaultAutonamingModePropose: + // In propose mode, we can use the proposed name as a suggestion + vs = defaultOptions.Autonaming.ProposedName + if options.Transform != nil { + vs = options.Transform(vs) + } + // Apply maxlen constraint if specified + if options.Maxlen > 0 && len(vs) > options.Maxlen { + return nil, fmt.Errorf("calculated name '%s' exceeds maximum length of %d", vs, options.Maxlen) + } + // Apply charset constraint if specified + if len(options.Charset) > 0 { + charsetStr := string(options.Charset) + + // Replace separators that aren't in the valid charset + if !strings.ContainsRune(charsetStr, '-') { + vs = strings.ReplaceAll(vs, "-", options.Separator) + } + if !strings.ContainsRune(charsetStr, '_') { + vs = strings.ReplaceAll(vs, "_", options.Separator) + } + + for _, c := range vs { + if !strings.ContainsRune(charsetStr, c) { + return nil, fmt.Errorf("calculated name '%s' contains invalid character '%c' not in charset '%s'", + vs, c, charsetStr) + } + } + } + case ComputeDefaultAutonamingModeEnforce: + // In enforce mode, we must use exactly the proposed name, ignoring all resource options + return defaultOptions.Autonaming.ProposedName, nil + case ComputeDefaultAutonamingModeDisable: + // In disable mode, we should return an error if no explicit name was provided + return nil, fmt.Errorf("automatic naming is disabled but no explicit name was provided") + } + } else if options.Randlen > 0 { uniqueHex, err := resource.NewUniqueName( defaultOptions.Seed, vs+options.Separator, options.Randlen, options.Maxlen, options.Charset) if err != nil { @@ -183,6 +222,7 @@ func ComputeAutoNameDefault( URN: defaultOptions.URN, Properties: defaultOptions.Properties, Seed: defaultOptions.Seed, + Autonaming: defaultOptions.Autonaming, }, vs) } return vs, nil diff --git a/pkg/tfbridge/info/autonaming_test.go b/pkg/tfbridge/info/autonaming_test.go new file mode 100644 index 000000000..b4d70c13b --- /dev/null +++ b/pkg/tfbridge/info/autonaming_test.go @@ -0,0 +1,249 @@ +// Copyright 2016-2024, Pulumi Corporation. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package info + +import ( + "context" + "testing" + + "github.com/pulumi/pulumi/sdk/v3/go/common/resource" + "github.com/stretchr/testify/assert" +) + +func TestComputeAutoNameDefault(t *testing.T) { + t.Parallel() + ctx := context.Background() + + t.Run("basic", func(t *testing.T) { + opts := ComputeDefaultOptions{ + URN: resource.URN("urn:pulumi:stack::project::type::name"), + } + + result, err := ComputeAutoNameDefault(ctx, AutoNameOptions{}, opts) + assert.NoError(t, err) + assert.Equal(t, "name", result) + }) + + t.Run("with separator and random suffix", func(t *testing.T) { + opts := ComputeDefaultOptions{ + URN: resource.URN("urn:pulumi:stack::project::type::name"), + Seed: []byte("test-seed"), + } + + result, err := ComputeAutoNameDefault(ctx, AutoNameOptions{ + Separator: "-", + Randlen: 4, + }, opts) + assert.NoError(t, err) + assert.Regexp(t, "^name-[0-9a-f]{4}$", result) + }) + + t.Run("respects prior state", func(t *testing.T) { + opts := ComputeDefaultOptions{ + URN: resource.URN("urn:pulumi:stack::project::type::name"), + PriorState: resource.PropertyMap{ + "name": resource.NewStringProperty("existing-name"), + }, + PriorValue: resource.NewStringProperty("existing-name"), + } + + result, err := ComputeAutoNameDefault(ctx, AutoNameOptions{}, opts) + assert.NoError(t, err) + assert.Equal(t, "existing-name", result) + }) + + t.Run("propose mode", func(t *testing.T) { + opts := ComputeDefaultOptions{ + URN: resource.URN("urn:pulumi:stack::project::type::name"), + Seed: []byte("test-seed"), + Autonaming: &ComputeDefaultAutonamingOptions{ + ProposedName: "proposed-name", + Mode: ComputeDefaultAutonamingModePropose, + }, + } + + result, err := ComputeAutoNameDefault(ctx, AutoNameOptions{}, opts) + assert.NoError(t, err) + assert.Equal(t, "proposed-name", result) + }) + + t.Run("propose mode with transform", func(t *testing.T) { + opts := ComputeDefaultOptions{ + URN: resource.URN("urn:pulumi:stack::project::type::name"), + Seed: []byte("test-seed"), + Autonaming: &ComputeDefaultAutonamingOptions{ + ProposedName: "proposed-name", + Mode: ComputeDefaultAutonamingModePropose, + }, + } + + result, err := ComputeAutoNameDefault(ctx, AutoNameOptions{ + Transform: func(s string) string { + return s + "-transformed" + }, + }, opts) + assert.NoError(t, err) + assert.Equal(t, "proposed-name-transformed", result) + }) + + t.Run("propose mode with maxlen", func(t *testing.T) { + opts := ComputeDefaultOptions{ + URN: resource.URN("urn:pulumi:stack::project::type::name"), + Seed: []byte("test-seed"), + Autonaming: &ComputeDefaultAutonamingOptions{ + ProposedName: "this-is-a-very-long-proposed-name", + Mode: ComputeDefaultAutonamingModePropose, + }, + } + + _, err := ComputeAutoNameDefault(ctx, AutoNameOptions{ + Maxlen: 10, + }, opts) + assert.Error(t, err) + assert.Contains(t, err.Error(), "exceeds maximum length") + }) + + t.Run("propose mode with charset", func(t *testing.T) { + opts := ComputeDefaultOptions{ + URN: resource.URN("urn:pulumi:stack::project::type::name"), + Seed: []byte("test-seed"), + Autonaming: &ComputeDefaultAutonamingOptions{ + ProposedName: "name-123", + Mode: ComputeDefaultAutonamingModePropose, + }, + } + + _, err := ComputeAutoNameDefault(ctx, AutoNameOptions{ + Charset: []rune("abcdefghijklmnopqrstuvwxyz-"), + }, opts) + assert.Error(t, err) + assert.Contains(t, err.Error(), "contains invalid character") + }) + + t.Run("propose mode ignores separator if no charset specified", func(t *testing.T) { + opts := ComputeDefaultOptions{ + URN: resource.URN("urn:pulumi:stack::project::type::name"), + Seed: []byte("test-seed"), + Autonaming: &ComputeDefaultAutonamingOptions{ + ProposedName: "name-with-dashes", + Mode: ComputeDefaultAutonamingModePropose, + }, + } + + result, err := ComputeAutoNameDefault(ctx, AutoNameOptions{ + Separator: "_", + }, opts) + assert.NoError(t, err) + assert.Equal(t, "name-with-dashes", result) + }) + + t.Run("propose mode with separator replacement and charset", func(t *testing.T) { + opts := ComputeDefaultOptions{ + URN: resource.URN("urn:pulumi:stack::project::type::name"), + Seed: []byte("test-seed"), + Autonaming: &ComputeDefaultAutonamingOptions{ + ProposedName: "name-with_mixed-separators", + Mode: ComputeDefaultAutonamingModePropose, + }, + } + + result, err := ComputeAutoNameDefault(ctx, AutoNameOptions{ + Separator: ".", + Charset: []rune("abcdefghijklmnopqrstuvwxyz."), + }, opts) + assert.NoError(t, err) + assert.Equal(t, "name.with.mixed.separators", result) + }) + + t.Run("propose mode with separator in charset", func(t *testing.T) { + opts := ComputeDefaultOptions{ + URN: resource.URN("urn:pulumi:stack::project::type::name"), + Seed: []byte("test-seed"), + Autonaming: &ComputeDefaultAutonamingOptions{ + ProposedName: "name-with-dashes", + Mode: ComputeDefaultAutonamingModePropose, + }, + } + + result, err := ComputeAutoNameDefault(ctx, AutoNameOptions{ + Separator: "-", + Charset: []rune("abcdefghijklmnopqrstuvwxyz-"), + }, opts) + assert.NoError(t, err) + // Should preserve dashes since they're in the charset + assert.Equal(t, "name-with-dashes", result) + }) + + t.Run("propose mode with mixed separators and partial charset", func(t *testing.T) { + opts := ComputeDefaultOptions{ + URN: resource.URN("urn:pulumi:stack::project::type::name"), + Seed: []byte("test-seed"), + Autonaming: &ComputeDefaultAutonamingOptions{ + ProposedName: "name-with_mixed-separators", + Mode: ComputeDefaultAutonamingModePropose, + }, + } + + result, err := ComputeAutoNameDefault(ctx, AutoNameOptions{ + Separator: "+", + // Only include - in charset, _ should still be replaced + Charset: []rune("abcdefghijklmnopqrstuvwxyz+-"), + }, opts) + assert.NoError(t, err) + // Should preserve - but replace _ with + + assert.Equal(t, "name-with+mixed-separators", result) + }) + + t.Run("enforce mode", func(t *testing.T) { + opts := ComputeDefaultOptions{ + URN: resource.URN("urn:pulumi:stack::project::type::name"), + Seed: []byte("test-seed"), + Autonaming: &ComputeDefaultAutonamingOptions{ + ProposedName: "proposed-name", + Mode: ComputeDefaultAutonamingModeEnforce, + }, + } + + result, err := ComputeAutoNameDefault(ctx, AutoNameOptions{ + // All of these options are ignored by design when mode is enforce. + Transform: func(s string) string { + return s + "-transformed" + }, + PostTransform: func(res *PulumiResource, s string) (string, error) { + return s + "-posttransformed", nil + }, + Maxlen: 5, + Charset: []rune("abc"), + Separator: "_", + }, opts) + assert.NoError(t, err) + // In enforce mode, the transform should be ignored and proposed name used exactly + assert.Equal(t, "proposed-name", result) + }) + + t.Run("disable mode", func(t *testing.T) { + opts := ComputeDefaultOptions{ + URN: resource.URN("urn:pulumi:stack::project::type::name"), + Seed: []byte("test-seed"), + Autonaming: &ComputeDefaultAutonamingOptions{ + ProposedName: "proposed-name", + Mode: ComputeDefaultAutonamingModeDisable, + }, + } + + _, err := ComputeAutoNameDefault(ctx, AutoNameOptions{}, opts) + assert.Error(t, err) + assert.Contains(t, err.Error(), "automatic naming is disabled") + }) +} diff --git a/pkg/tfbridge/info/info.go b/pkg/tfbridge/info/info.go index 26037c941..5c3ebdaa1 100644 --- a/pkg/tfbridge/info/info.go +++ b/pkg/tfbridge/info/info.go @@ -666,6 +666,29 @@ type Default struct { EnvVars []string } +// ComputeDefaultAutonamingOptionsMode is the mode that controls how the provider handles the proposed name. If not +// specified, defaults to `Propose`. +type ComputeDefaultAutonamingOptionsMode int32 + +const ( + // ComputeDefaultAutonamingModePropose means the provider may use the proposed name as a suggestion but is free + // to modify it. + ComputeDefaultAutonamingModePropose ComputeDefaultAutonamingOptionsMode = iota + // ComputeDefaultAutonamingModeEnforce means the provider must use exactly the proposed name (if present) + // or return an error if the proposed name is invalid. + ComputeDefaultAutonamingModeEnforce ComputeDefaultAutonamingOptionsMode = 1 + // ComputeDefaultAutonamingModeDisable means the provider should disable automatic naming and return an error + // if no explicit name is provided by user's program. + ComputeDefaultAutonamingModeDisable ComputeDefaultAutonamingOptionsMode = 2 +) + +// ComputeDefaultAutonamingOptions controls how auto-naming behaves when the engine provides explicit naming +// preferences. This is used by the engine to pass user preference for naming patterns. +type ComputeDefaultAutonamingOptions struct { + ProposedName string + Mode ComputeDefaultAutonamingOptionsMode +} + // Configures [Default.ComputeDefault]. type ComputeDefaultOptions struct { // URN identifying the Resource. Set when computing default properties for a Resource, and unset for functions. @@ -685,6 +708,9 @@ type ComputeDefaultOptions struct { // example, that random values generated across "pulumi preview" and "pulumi up" in the same deployment are // consistent. This currently is only available for resource changes. Seed []byte + + // The engine can provide auto-naming options if the user configured an explicit preference for it. + Autonaming *ComputeDefaultAutonamingOptions } // PulumiResource is just a little bundle that carries URN, seed and properties around. @@ -692,6 +718,7 @@ type PulumiResource struct { URN resource.URN Properties resource.PropertyMap Seed []byte + Autonaming *ComputeDefaultAutonamingOptions } // Overlay contains optional overlay information. Each info has a 1:1 correspondence with a module and diff --git a/pkg/tfbridge/names.go b/pkg/tfbridge/names.go index 66f4996df..7ebc4b155 100644 --- a/pkg/tfbridge/names.go +++ b/pkg/tfbridge/names.go @@ -309,6 +309,7 @@ func FromName(options AutoNameOptions) func(res *PulumiResource) (interface{}, e URN: res.URN, Properties: res.Properties, Seed: res.Seed, + Autonaming: res.Autonaming, }) } } diff --git a/pkg/tfbridge/names_test.go b/pkg/tfbridge/names_test.go index a38e4c542..37c65ae34 100644 --- a/pkg/tfbridge/names_test.go +++ b/pkg/tfbridge/names_test.go @@ -25,6 +25,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "github.com/pulumi/pulumi-terraform-bridge/v3/pkg/tfbridge/info" shimv1 "github.com/pulumi/pulumi-terraform-bridge/v3/pkg/tfshim/sdk-v1" shimv2 "github.com/pulumi/pulumi-terraform-bridge/v3/pkg/tfshim/sdk-v2" ) @@ -154,6 +155,32 @@ func TestFromName(t *testing.T) { assert.True(t, strings.HasSuffix(out1.(string), ".fifo")) } +func TestFromNameSeedAndAutonaming(t *testing.T) { + t.Parallel() + res := &PulumiResource{ + URN: "urn:pulumi:test::test::pkgA:index:t1::n1", + Properties: resource.PropertyMap{}, + Seed: []byte("test-seed"), + Autonaming: &info.ComputeDefaultAutonamingOptions{ + ProposedName: "proposed-name", + Mode: info.ComputeDefaultAutonamingModePropose, + }, + } + + f := FromName(AutoNameOptions{ + Separator: "-", + Maxlen: 80, + Randlen: 7, + }) + + out, err := f(res) + assert.NoError(t, err) + + // Verify the output is a string and has expected format + outStr := out.(string) + assert.Equal(t, "proposed-name", outStr) +} + func TestBijectiveNameConversion(t *testing.T) { t.Parallel() diff --git a/pkg/tfbridge/provider.go b/pkg/tfbridge/provider.go index 50593a885..42e8b33ac 100644 --- a/pkg/tfbridge/provider.go +++ b/pkg/tfbridge/provider.go @@ -926,7 +926,8 @@ func (p *Provider) Configure(ctx context.Context, } return &pulumirpc.ConfigureResponse{ - SupportsPreview: true, + SupportsPreview: true, + SupportsAutonamingConfiguration: true, }, nil } @@ -1015,9 +1016,17 @@ func (p *Provider) Check(ctx context.Context, req *pulumirpc.CheckRequest) (*pul } } + var autonaming *info.ComputeDefaultAutonamingOptions + if req.Autonaming != nil { + autonaming = &info.ComputeDefaultAutonamingOptions{ + ProposedName: req.Autonaming.ProposedName, + Mode: info.ComputeDefaultAutonamingOptionsMode(req.Autonaming.Mode), + } + } + tfname := res.TFName inputs, _, err := makeTerraformInputsWithOptions(ctx, - &PulumiResource{URN: urn, Properties: news, Seed: req.RandomSeed}, + &PulumiResource{URN: urn, Properties: news, Seed: req.RandomSeed, Autonaming: autonaming}, p.configValues, olds, news, schemaMap, res.Schema.Fields, makeTerraformInputsOptions{DisableTFDefaults: true, UnknownCollectionsSupported: p.tf.SupportsUnknownCollections()}) if err != nil { @@ -1036,7 +1045,7 @@ func (p *Provider) Check(ctx context.Context, req *pulumirpc.CheckRequest) (*pul // Now re-generate the inputs WITH the TF defaults inputs, assets, err := makeTerraformInputsWithOptions(ctx, - &PulumiResource{URN: urn, Properties: news, Seed: req.RandomSeed}, + &PulumiResource{URN: urn, Properties: news, Seed: req.RandomSeed, Autonaming: autonaming}, p.configValues, olds, news, schemaMap, res.Schema.Fields, makeTerraformInputsOptions{UnknownCollectionsSupported: p.tf.SupportsUnknownCollections()}) if err != nil { diff --git a/pkg/tfbridge/provider_test.go b/pkg/tfbridge/provider_test.go index d52ca61dc..38b903b87 100644 --- a/pkg/tfbridge/provider_test.go +++ b/pkg/tfbridge/provider_test.go @@ -799,6 +799,54 @@ func TestProviderPreviewV2(t *testing.T) { }).DeepEquals(outs["nestedResources"])) } +func TestProviderCheckWithAutonaming(t *testing.T) { + t.Parallel() + provider := &Provider{ + tf: shimv2.NewProvider(testTFProviderV2), + config: shimv2.NewSchemaMap(testTFProviderV2.Schema), + } + provider.resources = map[tokens.Type]Resource{ + "ExampleResource": { + TF: shimv1.NewResource(testTFProvider.ResourcesMap["example_resource"]), + TFName: "example_resource", + Schema: &ResourceInfo{ + Tok: "ExampleResource", + Fields: map[string]*SchemaInfo{ + "string_property_value": AutoNameWithCustomOptions("string_property_value", AutoNameOptions{ + Separator: "-", + Maxlen: 50, + Randlen: 8, + }), + }, + }, + }, + } + urn := resource.NewURN("stack", "project", "", "ExampleResource", "name") + + pulumiIns, err := plugin.MarshalProperties(resource.PropertyMap{ + "arrayPropertyValues": resource.NewArrayProperty([]resource.PropertyValue{resource.NewStringProperty("foo")}), + }, plugin.MarshalOptions{KeepUnknowns: true}) + assert.NoError(t, err) + checkResp, err := provider.Check(context.Background(), &pulumirpc.CheckRequest{ + Urn: string(urn), + News: pulumiIns, + Autonaming: &pulumirpc.CheckRequest_AutonamingOptions{ + ProposedName: "this-name-please", + Mode: pulumirpc.CheckRequest_AutonamingOptions_ENFORCE, + }, + }) + + require.NoError(t, err) + require.NotNil(t, checkResp) + require.Empty(t, checkResp.Failures) + ins, err := plugin.UnmarshalProperties(checkResp.GetInputs(), plugin.MarshalOptions{}) + require.NoError(t, err) + name := ins["string_property_value"] + require.True(t, name.IsString()) + require.Equal(t, "this-name-please", name.StringValue()) + _ = name +} + func testCheckFailures(t *testing.T, provider *Provider, typeName tokens.Type) []*pulumirpc.CheckFailure { urn := resource.NewURN("stack", "project", "", typeName, "name") unknown := resource.MakeComputed(resource.NewStringProperty("")) @@ -896,7 +944,8 @@ func TestCheckCallback(t *testing.T) { } }, "response": { - "supportsPreview": true + "supportsPreview": true, + "supportsAutonamingConfiguration": true } }, { @@ -1932,7 +1981,8 @@ func TestConfigure(t *testing.T) { "acceptResources": true }, "response": { - "supportsPreview": true + "supportsPreview": true, + "supportsAutonamingConfiguration": true } }`) }) @@ -3668,7 +3718,8 @@ func TestMaxItemsOneConflictsWith(t *testing.T) { "variables": {} }, "response": { - "supportsPreview": true + "supportsPreview": true, + "supportsAutonamingConfiguration": true } }, { @@ -3701,7 +3752,8 @@ func TestMaxItemsOneConflictsWith(t *testing.T) { "variables": {} }, "response": { - "supportsPreview": true + "supportsPreview": true, + "supportsAutonamingConfiguration": true } }, { @@ -3767,7 +3819,8 @@ func TestMinMaxItemsOneOptional(t *testing.T) { "variables": {} }, "response": { - "supportsPreview": true + "supportsPreview": true, + "supportsAutonamingConfiguration": true } }, { @@ -3798,7 +3851,8 @@ func TestMinMaxItemsOneOptional(t *testing.T) { "variables": {} }, "response": { - "supportsPreview": true + "supportsPreview": true, + "supportsAutonamingConfiguration": true } }, { @@ -3872,7 +3926,8 @@ func TestComputedMaxItemsOneNotSpecified(t *testing.T) { "variables": {} }, "response": { - "supportsPreview": true + "supportsPreview": true, + "supportsAutonamingConfiguration": true } }, { diff --git a/pkg/tfbridge/schema.go b/pkg/tfbridge/schema.go index 2ce0ce7ae..ec5cb38c0 100644 --- a/pkg/tfbridge/schema.go +++ b/pkg/tfbridge/schema.go @@ -313,6 +313,7 @@ func makeTerraformInputsWithOptions( PriorState: olds, Properties: instance.Properties, Seed: instance.Seed, + Autonaming: instance.Autonaming, URN: instance.URN, } } @@ -849,6 +850,7 @@ func (ctx *conversionContext) applyDefaults( URN: ctx.ComputeDefaultOptions.URN, Properties: ctx.ComputeDefaultOptions.Properties, Seed: ctx.ComputeDefaultOptions.Seed, + Autonaming: ctx.ComputeDefaultOptions.Autonaming, }) if err != nil { return err diff --git a/pkg/x/muxer/muxer.go b/pkg/x/muxer/muxer.go index bdf760952..d8db6efbe 100644 --- a/pkg/x/muxer/muxer.go +++ b/pkg/x/muxer/muxer.go @@ -300,10 +300,11 @@ func (m *muxer) Configure(ctx context.Context, req *pulumirpc.ConfigureRequest) } } response := &pulumirpc.ConfigureResponse{ - AcceptSecrets: true, - SupportsPreview: true, - AcceptResources: true, - AcceptOutputs: true, + AcceptSecrets: true, + SupportsPreview: true, + AcceptResources: true, + AcceptOutputs: true, + SupportsAutonamingConfiguration: true, } errs := new(multierror.Error) for _, r := range asyncJoin(subs) { @@ -315,6 +316,8 @@ func (m *muxer) Configure(ctx context.Context, req *pulumirpc.ConfigureRequest) response.AcceptResources = response.AcceptResources && r.A.GetAcceptResources() response.AcceptSecrets = response.AcceptSecrets && r.A.GetAcceptSecrets() response.SupportsPreview = response.SupportsPreview && r.A.GetSupportsPreview() + response.SupportsAutonamingConfiguration = response.SupportsAutonamingConfiguration && + r.A.GetSupportsAutonamingConfiguration() } return response, m.muxedErrors(errs) } diff --git a/pkg/x/muxer/tests/muxer_test.go b/pkg/x/muxer/tests/muxer_test.go index ba2edce7e..67de0eecf 100644 --- a/pkg/x/muxer/tests/muxer_test.go +++ b/pkg/x/muxer/tests/muxer_test.go @@ -136,7 +136,8 @@ func TestConfigure(t *testing.T) { "c": "3" } }`, `{ - "supportsPreview": true + "supportsPreview": true, + "supportsAutonamingConfiguration": true }`, nil, part(0, `{ "args": { @@ -146,7 +147,8 @@ func TestConfigure(t *testing.T) { } }`, `{ "acceptSecrets": true, - "supportsPreview": true + "supportsPreview": true, + "supportsAutonamingConfiguration": true }`, nil), part(1, `{ "args": { @@ -156,7 +158,8 @@ func TestConfigure(t *testing.T) { } }`, `{ "supportsPreview": true, - "acceptResources": true + "acceptResources": true, + "supportsAutonamingConfiguration": true }`, nil), )) } diff --git a/x/muxer/tests/muxer_test.go b/x/muxer/tests/muxer_test.go index 64f6de92c..47545e1c9 100644 --- a/x/muxer/tests/muxer_test.go +++ b/x/muxer/tests/muxer_test.go @@ -35,7 +35,7 @@ import ( ) func TestSimpleDispatch(t *testing.T) { - t.Parallel() + t.Parallel() var m muxer.DispatchTable m.Resources = map[string]int{ "test:mod:A": 0, @@ -70,7 +70,7 @@ func TestSimpleDispatch(t *testing.T) { } func TestCheckConfigErrorNotDuplicated(t *testing.T) { - t.Parallel() + t.Parallel() var m muxer.DispatchTable m.Resources = map[string]int{ "test:mod:A": 0, @@ -85,7 +85,7 @@ func TestCheckConfigErrorNotDuplicated(t *testing.T) { } func TestCheckConfigDifferentErrorsNotDropped(t *testing.T) { - t.Parallel() + t.Parallel() var m muxer.DispatchTable m.Resources = map[string]int{ "test:mod:A": 0, @@ -106,7 +106,7 @@ func TestCheckConfigDifferentErrorsNotDropped(t *testing.T) { } func TestCheckConfigOneErrorReturned(t *testing.T) { - t.Parallel() + t.Parallel() var m muxer.DispatchTable m.Resources = map[string]int{ "test:mod:A": 0, @@ -121,7 +121,7 @@ func TestCheckConfigOneErrorReturned(t *testing.T) { } func TestConfigure(t *testing.T) { - t.Parallel() + t.Parallel() var m muxer.DispatchTable m.Resources = map[string]int{ "test:mod:A": 0, @@ -136,7 +136,8 @@ func TestConfigure(t *testing.T) { "c": "3" } }`, `{ - "supportsPreview": true + "supportsPreview": true, + "supportsAutonamingConfiguration": true }`, nil, part(0, `{ "args": { @@ -146,7 +147,8 @@ func TestConfigure(t *testing.T) { } }`, `{ "acceptSecrets": true, - "supportsPreview": true + "supportsPreview": true, + "supportsAutonamingConfiguration": true }`, nil), part(1, `{ "args": { @@ -156,13 +158,14 @@ func TestConfigure(t *testing.T) { } }`, `{ "supportsPreview": true, - "acceptResources": true + "acceptResources": true, + "supportsAutonamingConfiguration": true }`, nil), )) } func TestDivergentCheckConfig(t *testing.T) { - t.Parallel() + t.Parallel() // Early versions of muxer failed hard on divergent responses from CheckConfig. This test ensures that it can // tolerate such responses (with logging or warning). The practical case is divergent handling of secret markers // where pf and v3 based providers respond with the same value but do not agree on the secret markers. @@ -203,7 +206,7 @@ func TestDivergentCheckConfig(t *testing.T) { } func TestGetMapping(t *testing.T) { - t.Parallel() + t.Parallel() t.Run("single-responding-server", func(t *testing.T) { var m muxer.DispatchTable m.Resources = map[string]int{