Skip to content

Commit

Permalink
Add configuration for controlling the autonaming behavior (#1831)
Browse files Browse the repository at this point in the history
This PR adds some new functionality to control the auto naming behavior.
The new behavior lives behind a provider config variable and must be
explicitly enabled by the user. The existing behavior will remain the
default behavior of the provider.

**What's new**

- `autoTrim`: When this is set to true the provider will automatically
  trim the generated name to fit within the `maxLength` requirement.
- `randomSuffixMinLength`: Set this to control the minimum length of the
  random suffix that is generated. This is especially useful in
  combination with `autoTrim` to ensure that you still end up with
  unique names (e.g. a random suffix of 1 character is not very unique)

closes #1816, re
pulumi/pulumi-cdk#62
  • Loading branch information
corymhall authored Nov 18, 2024
1 parent 6d3b70c commit 6f526ba
Show file tree
Hide file tree
Showing 26 changed files with 888 additions and 12 deletions.
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 @@ -282,6 +282,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 @@ -28740,6 +28744,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 @@ -62272,6 +62301,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 @@ -169588,6 +169642,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 @@ -169677,6 +169735,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."+
" 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.
// 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

0 comments on commit 6f526ba

Please sign in to comment.