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

Draft Config Refactor #320

Merged
merged 23 commits into from
Jun 24, 2024
Merged
Show file tree
Hide file tree
Changes from 21 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
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
41 changes: 23 additions & 18 deletions cmd/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -268,17 +268,19 @@ func (cc *createCmd) generateDockerfile(langConfig *config.DraftConfig, lowerLan
// Check for existing duplicate defualts
meecethereese marked this conversation as resolved.
Show resolved Hide resolved
for k, v := range extractedValues {
variableExists := false
for i, varD := range langConfig.VariableDefaults {
if k == varD.Name {
for i, variable := range langConfig.Variables {
if k == variable.Name {
variableExists = true
langConfig.VariableDefaults[i].Value = v
langConfig.Variables[i].Default.Value = v
break
}
}
if !variableExists {
langConfig.VariableDefaults = append(langConfig.VariableDefaults, config.BuilderVarDefault{
Name: k,
Value: v,
langConfig.Variables = append(langConfig.Variables, config.BuilderVar{
Name: k,
Default: config.BuilderVarDefault{
Value: v,
},
})
}
}
Expand All @@ -290,7 +292,7 @@ func (cc *createCmd) generateDockerfile(langConfig *config.DraftConfig, lowerLan
return err
}
} else {
inputs, err = validateConfigInputsToPrompts(langConfig.Variables, cc.createConfig.LanguageVariables, langConfig.VariableDefaults)
inputs, err = validateConfigInputsToPrompts(langConfig.Variables, cc.createConfig.LanguageVariables)
if err != nil {
return err
}
Expand Down Expand Up @@ -328,7 +330,7 @@ func (cc *createCmd) createDeployment() error {
if deployConfig == nil {
return errors.New("invalid deployment type")
}
customInputs, err = validateConfigInputsToPrompts(deployConfig.Variables, cc.createConfig.DeployVariables, deployConfig.VariableDefaults)
customInputs, err = validateConfigInputsToPrompts(deployConfig.Variables, cc.createConfig.DeployVariables)
if err != nil {
return err
}
Expand Down Expand Up @@ -462,7 +464,7 @@ func init() {
rootCmd.AddCommand(newCreateCmd())
}

func validateConfigInputsToPrompts(required []config.BuilderVar, provided []UserInputs, defaults []config.BuilderVarDefault) (map[string]string, error) {
func validateConfigInputsToPrompts(required []config.BuilderVar, provided []UserInputs) (map[string]string, error) {
customInputs := make(map[string]string)

// set inputs to provided values
Expand All @@ -471,24 +473,27 @@ func validateConfigInputsToPrompts(required []config.BuilderVar, provided []User
}

// fill in missing vars using variable default references
for _, variableDefault := range defaults {
if customInputs[variableDefault.Name] == "" && variableDefault.ReferenceVar != "" {
log.Debugf("variable %s is empty, using default referenceVar value from %s", variableDefault.Name, variableDefault.ReferenceVar)
customInputs[variableDefault.Name] = customInputs[variableDefault.ReferenceVar]
for _, variable := range required {
if customInputs[variable.Name] == "" && variable.Default.ReferenceVar != "" {
log.Debugf("variable %s is empty, using default referenceVar value from %s", variable.Name, variable.Default.ReferenceVar)
customInputs[variable.Name] = customInputs[variable.Default.ReferenceVar]
}
}

// fill in missing vars using variable default values
for _, variableDefault := range defaults {
if customInputs[variableDefault.Name] == "" && variableDefault.Value != "" {
log.Debugf("setting default value for %s to %s", variableDefault.Name, variableDefault.Value)
customInputs[variableDefault.Name] = variableDefault.Value
for _, variable := range required {
if customInputs[variable.Name] == "" && variable.Default.Value != "" {
log.Debugf("variable %s is empty, using default value %s", variable.Name, variable.Default.Value)
customInputs[variable.Name] = variable.Default.Value
}
}

for _, variable := range required {
if _, ok := customInputs[variable.Name]; !ok {
value, ok := customInputs[variable.Name]
if !ok {
return nil, fmt.Errorf("config missing required variable: %s with description: %s", variable.Name, variable.Description)
} else if value == "" {
return nil, fmt.Errorf("value for variable %s is empty", variable.Name)
}
}

Expand Down
27 changes: 17 additions & 10 deletions cmd/create_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -143,32 +143,39 @@ func TestInitConfig(t *testing.T) {

func TestValidateConfigInputsToPromptsPass(t *testing.T) {
required := []config.BuilderVar{
{Name: "REQUIRED_PROVIDED"},
{Name: "REQUIRED_DEFAULTED"},
{
Name: "REQUIRED_PROVIDED",
},
{
Name: "REQUIRED_DEFAULTED",
Default: config.BuilderVarDefault{
Value: "DEFAULT_VALUE",
},
},
}
provided := []UserInputs{
{Name: "REQUIRED_PROVIDED", Value: "PROVIDED_VALUE"},
}
defaults := []config.BuilderVarDefault{
{Name: "REQUIRED_DEFAULTED", Value: "DEFAULT_VALUE"},
}

vars, err := validateConfigInputsToPrompts(required, provided, defaults)
vars, err := validateConfigInputsToPrompts(required, provided)
assert.True(t, err == nil)
assert.Equal(t, vars["REQUIRED_DEFAULTED"], "DEFAULT_VALUE")
}

func TestValidateConfigInputsToPromptsMissing(t *testing.T) {
required := []config.BuilderVar{
{Name: "REQUIRED_PROVIDED"},
{Name: "REQUIRED_MISSING"},
{
Name: "REQUIRED_PROVIDED",
},
{
Name: "REQUIRED_MISSING",
},
}
provided := []UserInputs{
{Name: "REQUIRED_PROVIDED"},
}
defaults := []config.BuilderVarDefault{}

_, err := validateConfigInputsToPrompts(required, provided, defaults)
_, err := validateConfigInputsToPrompts(required, provided)
assert.NotNil(t, err)
}

Expand Down
4 changes: 2 additions & 2 deletions cmd/setup-gh.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ func fillSetUpConfig(sc *providers.SetUpCmd) error {
return fmt.Errorf("getting subscription labels: %w", err)
}

sc.SubscriptionID, err = GetAzSubscriptionId(subLabels, currentSub)
sc.SubscriptionID, err = getAzSubscriptionId(subLabels, currentSub)
if err != nil {
return fmt.Errorf("getting subscription ID: %w", err)
}
Expand Down Expand Up @@ -223,7 +223,7 @@ func getCloudProvider() string {
return selectResponse
}

func GetAzSubscriptionId(subLabels []providers.SubLabel, currentSub providers.SubLabel) (string, error) {
func getAzSubscriptionId(subLabels []providers.SubLabel, currentSub providers.SubLabel) (string, error) {
subLabel, err := prompts.Select("Please choose the subscription ID you would like to use", subLabels, &prompts.SelectOpt[providers.SubLabel]{
Field: func(subLabel providers.SubLabel) string {
return subLabel.Name + " (" + subLabel.ID + ")"
Expand Down
49 changes: 33 additions & 16 deletions pkg/config/draftconfig.go
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
package config

import (
"errors"

log "github.com/sirupsen/logrus"
)

// TODO: remove Name Overrides since we don't need them anymore
type DraftConfig struct {
DisplayName string `yaml:"displayName"`
NameOverrides []FileNameOverride `yaml:"nameOverrides"`
Variables []BuilderVar `yaml:"variables"`
VariableDefaults []BuilderVarDefault `yaml:"variableDefaults"`
DisplayName string `yaml:"displayName"`
NameOverrides []FileNameOverride `yaml:"nameOverrides"`
Variables []BuilderVar `yaml:"variables"`

nameOverrideMap map[string]string
}
Expand All @@ -20,17 +21,17 @@ type FileNameOverride struct {
}

type BuilderVar struct {
Name string `yaml:"name"`
Description string `yaml:"description"`
VarType string `yaml:"type"`
ExampleValues []string `yaml:"exampleValues"`
Name string `yaml:"name"`
Default BuilderVarDefault `yaml:"default"`
Description string `yaml:"description"`
ExampleValues []string `yaml:"exampleValues"`
Type string `yaml:"type"`
}

type BuilderVarDefault struct {
Name string `yaml:"name"`
Value string `yaml:"value"`
ReferenceVar string `yaml:"referenceVar"`
IsPromptDisabled bool `yaml:"disablePrompt"`
ReferenceVar string `yaml:"referenceVar"`
Value string `yaml:"value"`
}

func (d *DraftConfig) GetVariableExampleValues() map[string][]string {
Expand All @@ -40,6 +41,7 @@ func (d *DraftConfig) GetVariableExampleValues() map[string][]string {
variableExampleValues[variable.Name] = variable.ExampleValues
}
}

return variableExampleValues
}

Expand All @@ -65,14 +67,29 @@ func (d *DraftConfig) GetNameOverride(path string) string {
}

// ApplyDefaultVariables will apply the defaults to variables that are not already set
func (d *DraftConfig) ApplyDefaultVariables(customConfig map[string]string) {
for _, variable := range d.VariableDefaults {
func (d *DraftConfig) ApplyDefaultVariables(customConfig map[string]string) error {
for _, variable := range d.Variables {
// handle where variable is not set or is set to an empty string from cli handling
if defaultVal, ok := customConfig[variable.Name]; !ok || defaultVal == "" {
log.Infof("Variable %s defaulting to value %s", variable.Name, variable.Value)
customConfig[variable.Name] = variable.Value
if val, ok := customConfig[variable.Name]; !ok || val == "" {
if variable.Default.Value == "" {
return errors.New("variable " + variable.Name + " has no default value")
}
log.Infof("Variable %s defaulting to value %s", variable.Name, variable.Default.Value)
customConfig[variable.Name] = variable.Default.Value
}
}

return nil
}

func VariableIdxMap(variables []BuilderVar) map[string]int {
varIdxMap := make(map[string]int)

for i, variable := range variables {
varIdxMap[variable.Name] = i
}

return varIdxMap
}

// TemplateVariableRecorder is an interface for recording variables that are used read using draft configs
Expand Down
73 changes: 27 additions & 46 deletions pkg/prompts/prompts.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,56 +39,49 @@ func RunPromptsFromConfigWithSkipsIO(config *config.DraftConfig, varsToSkip []st

inputs := make(map[string]string)

for _, customPrompt := range config.Variables {
promptVariableName := customPrompt.Name
if _, ok := skipMap[promptVariableName]; ok {
log.Debugf("Skipping prompt for %s", promptVariableName)
for _, variable := range config.Variables {
if val, ok := skipMap[variable.Name]; ok && val != "" {
log.Debugf("Skipping prompt for %s", variable.Name)
continue
}
if GetIsPromptDisabled(customPrompt.Name, config.VariableDefaults) {
log.Debugf("Skipping prompt for %s as it has IsPromptDisabled=true", promptVariableName)
noPromptDefaultValue := GetVariableDefaultValue(promptVariableName, config.VariableDefaults, inputs)

if variable.Default.IsPromptDisabled {
log.Debugf("Skipping prompt for %s as it has IsPromptDisabled=true", variable.Name)
noPromptDefaultValue := GetVariableDefaultValue(variable, inputs)
if noPromptDefaultValue == "" {
return nil, fmt.Errorf("IsPromptDisabled is true for %s but no default value was found", promptVariableName)
return nil, fmt.Errorf("IsPromptDisabled is true for %s but no default value was found", variable.Name)
}
log.Debugf("Using default value %s for %s", noPromptDefaultValue, promptVariableName)
inputs[promptVariableName] = noPromptDefaultValue
log.Debugf("Using default value %s for %s", noPromptDefaultValue, variable.Name)
inputs[variable.Name] = noPromptDefaultValue
continue
}

log.Debugf("constructing prompt for: %s", promptVariableName)
if customPrompt.VarType == "bool" {
input, err := RunBoolPrompt(customPrompt, Stdin, Stdout)
log.Debugf("constructing prompt for: %s", variable.Name)
if variable.Type == "bool" {
input, err := RunBoolPrompt(variable, Stdin, Stdout)
if err != nil {
return nil, err
}
inputs[promptVariableName] = input
inputs[variable.Name] = input
} else {
defaultValue := GetVariableDefaultValue(promptVariableName, config.VariableDefaults, inputs)
defaultValue := GetVariableDefaultValue(variable, inputs)

stringInput, err := RunDefaultableStringPrompt(customPrompt, defaultValue, nil, Stdin, Stdout)
stringInput, err := RunDefaultableStringPrompt(defaultValue, variable, nil, Stdin, Stdout)
if err != nil {
return nil, err
}
inputs[promptVariableName] = stringInput
}
}

// Substitute the default value for variables where the user didn't enter anything
for _, variableDefault := range config.VariableDefaults {
if inputs[variableDefault.Name] == "" {
inputs[variableDefault.Name] = variableDefault.Value
inputs[variable.Name] = stringInput
}
}

return inputs, nil
}

// GetVariableDefaultValue returns the default value for a variable, if one is set in variableDefaults from a ReferenceVar or literal VariableDefault.Value in that order.
func GetVariableDefaultValue(variableName string, variableDefaults []config.BuilderVarDefault, inputs map[string]string) string {
// GetVariableDefaultValue returns the default value for a variable, if one is set in variableDefaults from a ReferenceVar or literal Variable.DefaultValue in that order.
func GetVariableDefaultValue(variable config.BuilderVar, inputs map[string]string) string {
defaultValue := ""

if variableName == "APPNAME" {
if variable.Name == "APPNAME" {
dirName, err := getCurrentDirNameFunc()
if err != nil {
log.Errorf("Error retrieving current directory name: %s", err)
Expand All @@ -98,26 +91,14 @@ func GetVariableDefaultValue(variableName string, variableDefaults []config.Buil
return defaultValue
}

for _, variableDefault := range variableDefaults {
if variableDefault.Name == variableName {
defaultValue = variableDefault.Value
log.Debugf("setting default value for %s to %s from variable default rule", variableName, defaultValue)
if variableDefault.ReferenceVar != "" && inputs[variableDefault.ReferenceVar] != "" {
defaultValue = inputs[variableDefault.ReferenceVar]
log.Debugf("setting default value for %s to %s from referenceVar %s", variableName, defaultValue, variableDefault.ReferenceVar)
}
}
defaultValue = variable.Default.Value
log.Debugf("setting default value for %s to %s from variable default rule", variable.Name, defaultValue)
if variable.Default.ReferenceVar != "" && inputs[variable.Default.ReferenceVar] != "" {
defaultValue = inputs[variable.Default.ReferenceVar]
log.Debugf("setting default value for %s to %s from referenceVar %s", variable.Name, defaultValue, variable.Default.ReferenceVar)
}
return defaultValue
}

func GetIsPromptDisabled(variableName string, variableDefaults []config.BuilderVarDefault) bool {
for _, variableDefault := range variableDefaults {
if variableDefault.Name == variableName {
return variableDefault.IsPromptDisabled
}
}
return false
return defaultValue
}

func RunBoolPrompt(customPrompt config.BuilderVar, Stdin io.ReadCloser, Stdout io.WriteCloser) (string, error) {
Expand Down Expand Up @@ -176,7 +157,7 @@ func appNameValidator(name string) error {
}

// RunDefaultableStringPrompt runs a prompt for a string variable, returning the user string input for the prompt
func RunDefaultableStringPrompt(customPrompt config.BuilderVar, defaultValue string, validate func(string) error, Stdin io.ReadCloser, Stdout io.WriteCloser) (string, error) {
func RunDefaultableStringPrompt(defaultValue string, customPrompt config.BuilderVar, validate func(string) error, Stdin io.ReadCloser, Stdout io.WriteCloser) (string, error) {
if validate == nil {
validate = NoBlankStringValidator
}
Expand Down
Loading
Loading