Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Add configuration for controlling the autonaming behavior #1831

Merged
merged 5 commits into from
Nov 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion examples/autonaming-overlay/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import * as pulumi from '@pulumi/pulumi';
import * as aws from "@pulumi/aws-native";

new aws.iam.Role("myRole".repeat(11), {
const config = new pulumi.Config();
const name = config.require('roleName');
const role = new aws.iam.Role(name, {
assumeRolePolicyDocument: {
Version: "2012-10-17",
Statement: [
Expand All @@ -14,3 +17,5 @@ new aws.iam.Role("myRole".repeat(11), {
],
},
});

export const roleName = role.roleName;
21 changes: 21 additions & 0 deletions examples/examples_nodejs_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,9 @@ func TestAutoNamingOverlay(t *testing.T) {
With(integration.ProgramTestOptions{
Dir: filepath.Join(getCwd(t), "autonaming-overlay"),
Stderr: &buf,
Config: map[string]string{
"roleName": "myReallyLongRoleNameThatIsLongerThan64CharactersOneTwoThreeFour",
},
ExpectFailure: true,
ExtraRuntimeValidation: func(t *testing.T, stackInfo integration.RuntimeValidationStackInfo) {
assert.Contains(t, buf.String(), "is too large to fix max length constraint of 64")
Expand All @@ -157,6 +160,24 @@ func TestAutoNamingOverlay(t *testing.T) {
integration.ProgramTest(t, &test)
}

func TestAutoNamingOverlayWithConfig(t *testing.T) {
test := getJSBaseOptions(t).
With(integration.ProgramTestOptions{
Dir: filepath.Join(getCwd(t), "autonaming-overlay"),
Config: map[string]string{
"aws-native:autoNaming": `{"autoTrim": true, "randomSuffixMinLength": 7}`,
"roleName": "myReallyLongRoleNameThatIsLongerThan64CharactersOneTwoThreeFour",
},
ExtraRuntimeValidation: func(t *testing.T, stackInfo integration.RuntimeValidationStackInfo) {
roleName := stackInfo.Outputs["roleName"].(string)
assert.Equal(t, 64, len(roleName))
assert.Regexp(t, "myReallyLongRoleNameThatIsLon64CharactersOneTwoThreeFour-[a-z0-9]{7}", roleName)
},
})

integration.ProgramTest(t, &test)
}

func TestParallelTs(t *testing.T) {
test := getJSBaseOptions(t).
With(integration.ProgramTestOptions{
Expand Down
62 changes: 62 additions & 0 deletions provider/cmd/pulumi-resource-aws-native/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,10 @@
"$ref": "#/types/aws-native:config:AssumeRole",
"description": "Configuration for retrieving temporary credentials from the STS service."
},
"autoNaming": {
"$ref": "#/types/aws-native:config:AutoNaming",
"description": "The configuration for automatically naming resources."
},
"defaultTags": {
"$ref": "#/types/aws-native:config:DefaultTags",
"description": "Configuration block with resource tag settings to apply across all resources handled by this provider. This is designed to replace redundant per-resource `tags` configurations. Provider tags can be overridden with new values, but not excluded from specific resources. To override provider tag values, use the `tags` argument within a resource to configure new tag values for matching keys."
Expand Down Expand Up @@ -28410,6 +28414,31 @@
},
"type": "object"
},
"aws-native:config:AutoNaming": {
"description": "The configuration for automatically naming resources.",
"properties": {
"autoTrim": {
"type": "boolean",
"description": "Automatically trim the auto-generated name to meet the maximum length constraint.",
"language": {
"python": {
"mapCase": false
}
}
},
"randomSuffixMinLength": {
"type": "integer",
"description": "The minimum length of the random suffix to append to the auto-generated name.",
"default": 1,
"language": {
"python": {
"mapCase": false
}
}
}
},
"type": "object"
},
"aws-native:config:DefaultTags": {
"description": "The configuration with resource tag settings to apply across all resources handled by this provider. This is designed to replace redundant per-resource `tags` configurations. Provider tags can be overridden with new values, but not excluded from specific resources. To override provider tag values, use the `tags` argument within a resource to configure new tag values for matching keys.",
"properties": {
Expand Down Expand Up @@ -61409,6 +61438,31 @@
},
"type": "object"
},
"aws-native:index:ProviderAutoNaming": {
"description": "The configuration for automatically naming resources.",
"properties": {
"autoTrim": {
"type": "boolean",
"description": "Automatically trim the auto-generated name to meet the maximum length constraint.",
"language": {
"python": {
"mapCase": false
}
}
},
"randomSuffixMinLength": {
"type": "integer",
"description": "The minimum length of the random suffix to append to the auto-generated name.",
"default": 1,
"language": {
"python": {
"mapCase": false
}
}
}
},
"type": "object"
},
"aws-native:index:ProviderDefaultTags": {
"description": "The configuration with resource tag settings to apply across all resources handled by this provider. This is designed to replace redundant per-resource `tags` configurations. Provider tags can be overridden with new values, but not excluded from specific resources. To override provider tag values, use the `tags` argument within a resource to configure new tag values for matching keys.",
"properties": {
Expand Down Expand Up @@ -168697,6 +168751,10 @@
"$ref": "#/types/aws-native:index:ProviderAssumeRole",
"description": "Configuration for retrieving temporary credentials from the STS service."
},
"autoNaming": {
"$ref": "#/types/aws-native:index:ProviderAutoNaming",
"description": "The configuration for automatically naming resources."
},
"defaultTags": {
"$ref": "#/types/aws-native:index:ProviderDefaultTags",
"description": "Configuration block with resource tag settings to apply across all resources handled by this provider. This is designed to replace redundant per-resource `tags` configurations. Provider tags can be overridden with new values, but not excluded from specific resources. To override provider tag values, use the `tags` argument within a resource to configure new tag values for matching keys."
Expand Down Expand Up @@ -168786,6 +168844,10 @@
"$ref": "#/types/aws-native:index:ProviderAssumeRole",
"description": "Configuration for retrieving temporary credentials from the STS service."
},
"autoNaming": {
"$ref": "#/types/aws-native:index:ProviderAutoNaming",
"description": "The configuration for automatically naming resources."
},
"defaultTags": {
"$ref": "#/types/aws-native:index:ProviderDefaultTags",
"description": "Configuration block with resource tag settings to apply across all resources handled by this provider. This is designed to replace redundant per-resource `tags` configurations. Provider tags can be overridden with new values, but not excluded from specific resources. To override provider tag values, use the `tags` argument within a resource to configure new tag values for matching keys."
Expand Down
60 changes: 58 additions & 2 deletions provider/pkg/autonaming/application.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,23 +4,30 @@ package autonaming

import (
"fmt"
"math"

"github.com/pulumi/pulumi-aws-native/provider/pkg/metadata"
"github.com/pulumi/pulumi/sdk/v3/go/common/resource"
)

type AutoNamingConfig struct {
AutoTrim bool `json:"autoTrim"`
RandomSuffixMinLength int `json:"randomSuffixMinLength"`
}

func ApplyAutoNaming(
spec *metadata.AutoNamingSpec,
urn resource.URN,
randomSeed []byte,
olds,
news resource.PropertyMap,
config *AutoNamingConfig,
) error {
if spec == nil {
return nil
}
// Auto-name fields if not already specified
val, err := getDefaultName(randomSeed, urn, spec, olds, news)
val, err := getDefaultName(randomSeed, urn, spec, olds, news, config)
if err != nil {
return err
}
Expand All @@ -39,6 +46,7 @@ func getDefaultName(
autoNamingSpec *metadata.AutoNamingSpec,
olds,
news resource.PropertyMap,
config *AutoNamingConfig,
) (resource.PropertyValue, error) {
sdkName := autoNamingSpec.SdkName

Expand All @@ -58,10 +66,23 @@ func getDefaultName(
return resource.PropertyValue{}, err
}

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
}
}

// Generate random name that fits the length constraints.
name := urn.Name()
prefix := name + "-"
randLength := 7
randLength := 7 // Default value
if randomSuffixMinLength > randLength {
randLength = randomSuffixMinLength
}
if len(prefix)+namingTrivia.Length()+randLength < autoNamingSpec.MinLength {
randLength = autoNamingSpec.MinLength - len(prefix) - namingTrivia.Length()
}
Expand All @@ -70,6 +91,19 @@ func getDefaultName(
if autoNamingSpec.MaxLength > 0 {
left := autoNamingSpec.MaxLength - len(prefix) - namingTrivia.Length()

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."+
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So surprised to see Fortran-style 1-based indices here but I think this checks out. This is right.

" 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)
}
prefix = trimName(prefix, autoTrimMaxLength)
randLength = randomSuffixMinLength
left = randomSuffixMinLength
}

if left <= 0 {
if namingTrivia.Length() > 0 {
return resource.PropertyValue{}, fmt.Errorf("failed to auto-generate value for %[1]q."+
Expand Down Expand Up @@ -101,3 +135,25 @@ func getDefaultName(

return resource.NewStringProperty(random), nil
}

// trimName will trim the prefix to fit within the max length constraint.
// It will cut out part of the middle, keeping the beginning and the end of the string.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is the right thing to do given the current constraints. But it could also be rather surprising to users.

For example, I often split my resource names with hyphens. This could generate some rather odd names.

But given that pulumi/pulumi#17592 is not ready yet, I think this is a good choice.
If we see the need for it, we could also expose more configuration options for autonaming in the future.

// This is so that really long generated names can still be unique. For example, if the
// user creates a resource name by appending the parent onto the child, you could end up
// with names like:
// - "topParent-middleParent-bottonParent-child1"
// - "topParent-middleParent-bottonParent-child2"
//
// If the max length is 30, the trimmed generated name for both would be something like
// "topParent-middleParent-bottonP" and you would not be able to tell them apart.
//
// By trimming from the middle you would end up with something like this, preserving
// the uniqueness of the generated names:
// - "topParent-middlonParent-child1"
// - "topParent-middlonParent-child2"
func trimName(name string, maxLength int) string {
floorHalf := math.Floor(float64(maxLength) / 2)
half := int(floorHalf)
left := maxLength - half
return name[0:half] + name[len(name)-left:]
}
Loading
Loading