Skip to content

Commit

Permalink
aspire more autogen input config (#3561)
Browse files Browse the repository at this point in the history
  • Loading branch information
vhvb1989 authored Mar 25, 2024
1 parent cac9e76 commit 8929197
Show file tree
Hide file tree
Showing 14 changed files with 310 additions and 80 deletions.
63 changes: 56 additions & 7 deletions cli/azd/pkg/apphost/generate.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,10 @@ import (
"strings"
"text/template"

"github.com/Azure/azure-sdk-for-go/sdk/azcore/to"
"github.com/azure/azure-dev/cli/azd/internal/scaffold"
"github.com/azure/azure-dev/cli/azd/pkg/azure"
"github.com/azure/azure-dev/cli/azd/pkg/convert"
"github.com/azure/azure-dev/cli/azd/pkg/osutil"
"github.com/azure/azure-dev/cli/azd/resources"
"github.com/psanford/memfs"
Expand Down Expand Up @@ -141,8 +144,8 @@ func BicepTemplate(manifest *Manifest) (*memfs.FS, error) {
// bicepContext merges the bicepContext with the inputs from the manifest to execute the main.bicep template
// this allows the template to access the auto-gen inputs from the generator
type autoGenInput struct {
Name string
Len int
Name string
Config string
}
type bicepContext struct {
genBicepTemplateContext
Expand All @@ -160,10 +163,16 @@ func BicepTemplate(manifest *Manifest) (*memfs.FS, error) {
resource, inputName := handleBicepNameQuotes(parts[0]), handleBicepNameQuotes(parts[1])

resourceGenList, exists := inputs[resource]

inputMetadata, err := inputMetadata(*input.Default)
if err != nil {
return nil, fmt.Errorf("generating input metadata for %s: %w", key, err)
}

if exists {
inputs[resource] = append(resourceGenList, autoGenInput{Name: inputName, Len: input.DefaultMinLength})
inputs[resource] = append(resourceGenList, autoGenInput{Name: inputName, Config: inputMetadata})
} else {
inputs[resource] = []autoGenInput{{Name: inputName, Len: input.DefaultMinLength}}
inputs[resource] = []autoGenInput{{Name: inputName, Config: inputMetadata}}
}
}
context := bicepContext{
Expand All @@ -186,6 +195,45 @@ func BicepTemplate(manifest *Manifest) (*memfs.FS, error) {
return fs, nil
}

func inputMetadata(config InputDefaultGenerate) (string, error) {
finalLength := convert.ToValueWithDefault(config.MinLength, 0)
clusterLength := convert.ToValueWithDefault(config.MinLower, 0) +
convert.ToValueWithDefault(config.MinUpper, 0) +
convert.ToValueWithDefault(config.MinNumeric, 0) +
convert.ToValueWithDefault(config.MinSpecial, 0)
if clusterLength > finalLength {
finalLength = clusterLength
}

adaptBool := func(b *bool) *bool {
if b == nil {
return b
}
return to.Ptr(!*b)
}

metadataModel := azure.AutoGenInput{
Length: finalLength,
MinLower: config.MinLower,
MinUpper: config.MinUpper,
MinNumeric: config.MinNumeric,
MinSpecial: config.MinSpecial,
NoLower: adaptBool(config.Lower),
NoUpper: adaptBool(config.Upper),
NoNumeric: adaptBool(config.Numeric),
NoSpecial: adaptBool(config.Special),
}

metadataBytes, err := json.Marshal(metadataModel)
if err != nil {
return "", fmt.Errorf("marshalling metadata: %w", err)
}

// key identifiers for objects on bicep don't need quotes, unless they have special characters, like `-` or `.`.
// jsonSimpleKeyRegex is used to remove the quotes from the key if no needed to avoid a bicep lint warning.
return jsonSimpleKeyRegex.ReplaceAllString(string(metadataBytes), "${1}:"), nil
}

func handleBicepNameQuotes(name string) string {
if strings.Contains(name, " ") || strings.Contains(name, "-") || strings.Contains(name, ".") {
return fmt.Sprintf("'%s'", name)
Expand Down Expand Up @@ -374,12 +422,12 @@ func (b *infraGenerator) extractOutputs(resource *Resource) error {
func (b *infraGenerator) LoadManifest(m *Manifest) error {
for name, comp := range m.Resources {
for k, v := range comp.Inputs {
if v.Default != nil && v.Default.Generate != nil && v.Default.Generate.MinLength != nil {
if v.Default != nil && v.Default.Generate != nil {
input := genInput{
Secret: v.Secret,
Secret: v.Secret,
Default: v.Default.Generate,
}

input.DefaultMinLength = *v.Default.Generate.MinLength
// only inputs with default.generate are tracked here
b.inputs[fmt.Sprintf("%s.%s", name, k)] = input
}
Expand Down Expand Up @@ -955,6 +1003,7 @@ func containsSecretInput(resourceName, envValue string, inputs map[string]Input)
// singleQuotedStringRegex is a regular expression pattern used to match single-quoted strings.
var singleQuotedStringRegex = regexp.MustCompile(`'[^']*'`)
var propertyNameRegex = regexp.MustCompile(`'([^']*)':`)
var jsonSimpleKeyRegex = regexp.MustCompile(`"([a-zA-Z0-9]*)":`)

// Compile compiles the loaded manifest into the internal representation used to generate the infrastructure files. Once
// called the context objects on the infraGenerator can be passed to the text templates to generate the required
Expand Down
4 changes: 2 additions & 2 deletions cli/azd/pkg/apphost/generate_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -96,8 +96,8 @@ type genDaprComponent struct {
}

type genInput struct {
Secret bool
DefaultMinLength int
Secret bool
Default *InputDefaultGenerate
}

type genSqlServer struct {
Expand Down
16 changes: 12 additions & 4 deletions cli/azd/pkg/apphost/manifest.go
Original file line number Diff line number Diff line change
Expand Up @@ -122,12 +122,20 @@ type Input struct {
Default *InputDefault `json:"default,omitempty"`
}

type InputDefault struct {
Generate *InputDefaultGenerate `json:"generate,omitempty"`
type InputDefaultGenerate struct {
MinLength *uint `json:"minLength,omitempty"`
Lower *bool `json:"lower,omitempty"`
Upper *bool `json:"upper,omitempty"`
Numeric *bool `json:"numeric,omitempty"`
Special *bool `json:"special,omitempty"`
MinLower *uint `json:"minLower,omitempty"`
MinUpper *uint `json:"minUpper,omitempty"`
MinNumeric *uint `json:"minNumeric,omitempty"`
MinSpecial *uint `json:"minSpecial,omitempty"`
}

type InputDefaultGenerate struct {
MinLength *int `json:"minLength,omitempty"`
type InputDefault struct {
Generate *InputDefaultGenerate `json:"generate,omitempty"`
}

// ManifestFromAppHost returns the Manifest from the given app host.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,16 @@ param location string
type: 'inputs'
autoGenerate: {
'my-sql-abstract': {
'pas-sw-ord': { len: 10 }
password: { len: 10 }
'pas-sw-ord': {length:10}
password: {length:10}
}
mysqlabstract: {
'pas-sw-ord': { len: 10 }
password: { len: 10 }
'pas-sw-ord': {length:15,noSpecial:true,minLower:10,minNumeric:5}
password: {length:10,noSpecial:true}
}
noVolume: {
'pas-sw-ord': { len: 10 }
password: { len: 10 }
'pas-sw-ord': {length:10}
password: {length:10}
}
}}
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ param location string
type: 'inputs'
autoGenerate: {
mysqlabstract: {
password: { len: 10 }
password: {length:20,noLower:true,minNumeric:5,minSpecial:5}
}
}}
})
Expand Down
7 changes: 5 additions & 2 deletions cli/azd/pkg/apphost/testdata/aspire-container.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@
"secret": true,
"default": {
"generate": {
"minLength": 10
"minLength": 10,
"special": false
}
}
},
Expand All @@ -38,7 +39,9 @@
"secret": true,
"default": {
"generate": {
"minLength": 10
"minLower": 10,
"special": false,
"minNumeric": 5
}
}
}
Expand Down
5 changes: 4 additions & 1 deletion cli/azd/pkg/apphost/testdata/aspire-docker.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,10 @@
"secret": true,
"default": {
"generate": {
"minLength": 10
"minLength": 20,
"lower": false,
"minNumeric": 5,
"minSpecial": 5
}
}
}
Expand Down
10 changes: 9 additions & 1 deletion cli/azd/pkg/azure/arm_template.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,15 @@ func (d *ArmTemplateParameterDefinition) Secure() bool {
}

type AutoGenInput struct {
Len int `json:"len"`
Length uint `json:"length,omitempty"`
NoLower *bool `json:"noLower,omitempty"`
NoUpper *bool `json:"noUpper,omitempty"`
NoNumeric *bool `json:"noNumeric,omitempty"`
NoSpecial *bool `json:"noSpecial,omitempty"`
MinLower *uint `json:"minLower,omitempty"`
MinUpper *uint `json:"minUpper,omitempty"`
MinNumeric *uint `json:"minNumeric,omitempty"`
MinSpecial *uint `json:"minSpecial,omitempty"`
}

type AzdMetadata struct {
Expand Down
4 changes: 3 additions & 1 deletion cli/azd/pkg/cmdsubst/secretOrRandomPassword.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"log"
"strings"

"github.com/Azure/azure-sdk-for-go/sdk/azcore/to"
"github.com/azure/azure-dev/cli/azd/pkg/keyvault"
"github.com/azure/azure-dev/cli/azd/pkg/password"
)
Expand Down Expand Up @@ -39,7 +40,8 @@ func (e *SecretOrRandomPasswordCommandExecutor) Run(
}

generatePassword := func() (bool, string, error) {
substitute, err := password.Generate(password.PasswordComposition{NumLowercase: 5, NumUppercase: 5, NumDigits: 5})
substitute, err := password.Generate(
password.GenerateConfig{MinLower: to.Ptr[uint](5), MinUpper: to.Ptr[uint](5), MinNumeric: to.Ptr[uint](5)})
return err == nil, substitute, err
}

Expand Down
13 changes: 12 additions & 1 deletion cli/azd/pkg/infra/provisioning/bicep/bicep_provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -1911,7 +1911,18 @@ func inputsParameter(
}
for inputName, inputInfo := range inputResourceInfo {
if _, has := existingRecordsForResource[inputName]; !has {
val, err := password.FromAlphabet(password.LettersAndDigits, inputInfo.Len)
val, err := password.Generate(password.GenerateConfig{
Length: inputInfo.Length,
NoLower: inputInfo.NoLower,
NoUpper: inputInfo.NoUpper,
NoNumeric: inputInfo.NoNumeric,
NoSpecial: inputInfo.NoSpecial,
MinLower: inputInfo.MinLower,
MinUpper: inputInfo.MinUpper,
MinNumeric: inputInfo.MinNumeric,
MinSpecial: inputInfo.MinSpecial,
},
)
if err != nil {
return inputsParameter, inputsUpdated, fmt.Errorf("generating value for input %s: %w", inputName, err)
}
Expand Down
12 changes: 6 additions & 6 deletions cli/azd/pkg/infra/provisioning/bicep/bicep_provider_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1247,20 +1247,20 @@ func TestInputsParameter(t *testing.T) {
autoGenParameters := map[string]map[string]azure.AutoGenInput{
"resource1": {
"input1": {
Len: 10,
Length: 10,
},
"input3": {
Len: 8,
Length: 8,
},
},
"resource2": {
"input2": {
Len: 12,
Length: 12,
},
},
"resource3": {
"input4": {
Len: 6,
Length: 6,
},
},
}
Expand Down Expand Up @@ -1290,13 +1290,13 @@ func TestInputsParameter(t *testing.T) {
t, expectedInputsParameter["resource1"]["input1"], result["resource1"]["input1"])
// generated - only check length
require.Equal(
t, autoGenParameters["resource1"]["input3"].Len, len(result["resource1"]["input3"].(string)))
t, autoGenParameters["resource1"]["input3"].Length, uint(len(result["resource1"]["input3"].(string))))

require.Equal(t, expectedInputsParameter["resource2"], result["resource2"])

// generated - only check length
require.Equal(
t, autoGenParameters["resource3"]["input4"].Len, len(result["resource3"]["input4"].(string)))
t, autoGenParameters["resource3"]["input4"].Length, uint(len(result["resource3"]["input4"].(string))))

require.Equal(t, expectedInputsUpdated, inputsUpdated)
}
Loading

0 comments on commit 8929197

Please sign in to comment.