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

Workflow Template Enhancements #324

Merged
merged 54 commits into from
Jun 28, 2024
Merged
Show file tree
Hide file tree
Changes from 49 commits
Commits
Show all changes
54 commits
Select commit Hold shift + click to select a range
107f0e8
adding logic for generating default app name
Vidya2606 Jun 7, 2024
b043d06
updated the sanitize function and added validator for appApp
Vidya2606 Jun 8, 2024
b9d68b9
addressing comments
Vidya2606 Jun 10, 2024
66521b0
added the validator for app name and unit tests
Vidya2606 Jun 12, 2024
94a1913
address comments
Vidya2606 Jun 12, 2024
cad4954
kitchen sink
meecethereese Jun 18, 2024
65f9520
updated the sanitize function
Vidya2606 Jun 18, 2024
c289515
refactored tests for new config
meecethereese Jun 19, 2024
7400d71
handling for Dockerfile envarg
meecethereese Jun 19, 2024
3e55769
rolled back changes for PR separation
meecethereese Jun 20, 2024
f75510f
rolled back dockerfile handling
meecethereese Jun 20, 2024
f53059e
rolled back resource type field
meecethereese Jun 20, 2024
61ae60d
merged vidya's defaultAppName branch
meecethereese Jun 20, 2024
b1aeffe
merge from main
meecethereese Jun 20, 2024
f82c374
grouped default fields into a substruct
meecethereese Jun 21, 2024
a0541d3
merged with draft-refactor
meecethereese Jun 21, 2024
76f92af
typo
meecethereese Jun 21, 2024
568f93b
typo
meecethereese Jun 21, 2024
e2c5a91
integrated DevHub's functionalities
meecethereese Jun 21, 2024
33d770e
changed data structure for BuilderVars from map back to a slice
meecethereese Jun 21, 2024
2c45d46
cleaned up templates
meecethereese Jun 21, 2024
5a51711
merged draft-config-refactor
meecethereese Jun 21, 2024
9076e2f
added GenerateWorkflowBytes() and VariableIdxMap()
meecethereese Jun 21, 2024
e38f13f
moving stuff into a future PR
meecethereese Jun 21, 2024
8c792fb
fixed ApplyDefaultVariables()
meecethereese Jun 21, 2024
d88b3da
wip
meecethereese Jun 21, 2024
bf1d716
wip
meecethereese Jun 21, 2024
7e6614e
updated tests
meecethereese Jun 21, 2024
a80a6d5
added back noDefaultIsEmptyString test
meecethereese Jun 23, 2024
148eba2
updating prompts_tests
meecethereese Jun 23, 2024
7b539e0
merged draft-config-refactor
meecethereese Jun 24, 2024
9fb8005
rolled back changes to prompts.go
meecethereese Jun 24, 2024
e370432
got rid of variableName
meecethereese Jun 24, 2024
d48f262
got rid of variableName
meecethereese Jun 24, 2024
5706cbb
cyclical recursion protection
meecethereese Jun 24, 2024
98d2e58
Merge remote-tracking branch 'origin/apply-default-variables-bug' int…
meecethereese Jun 24, 2024
f8652d2
wip
meecethereese Jun 24, 2024
157f5c0
Merge branch 'main' into workflow-template-enhancements
meecethereese Jun 24, 2024
bc488d6
added flags for new env args
meecethereese Jun 24, 2024
1ceef56
Merge branch 'workflow-template-enhancements' of https://github.com/m…
meecethereese Jun 24, 2024
4be85f9
added flags
meecethereese Jun 24, 2024
e9f4341
updating tests
meecethereese Jun 25, 2024
be850bd
fixing cli calls
meecethereese Jun 25, 2024
e186f26
typo
meecethereese Jun 25, 2024
b96f437
updated flag key
meecethereese Jun 25, 2024
f32b9d4
Updated integration tests
meecethereese Jun 25, 2024
bd4c774
got rid of test files
meecethereese Jun 25, 2024
4c19f47
got rid of second branch flag arg
meecethereese Jun 25, 2024
74e8783
updated gen_integration.sh
meecethereese Jun 25, 2024
11ffd24
added test for ApplyDefaultVariables
meecethereese Jun 25, 2024
23d3c2e
wip
meecethereese Jun 25, 2024
e201a5b
removed GenerateWorkflowBytes
meecethereese Jun 25, 2024
b9054e6
made tests more thorough
meecethereese Jun 26, 2024
6da6ebe
Merge remote-tracking branch 'upstream/main' into workflow-template-e…
meecethereese Jun 28, 2024
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
72 changes: 36 additions & 36 deletions .github/workflows/integration-linux.yml

Large diffs are not rendered by default.

24 changes: 6 additions & 18 deletions cmd/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -292,7 +292,7 @@ func (cc *createCmd) generateDockerfile(langConfig *config.DraftConfig, lowerLan
return err
}
} else {
inputs, err = validateConfigInputsToPrompts(langConfig.Variables, cc.createConfig.LanguageVariables)
inputs, err = validateConfigInputsToPrompts(langConfig, cc.createConfig.LanguageVariables)
if err != nil {
return err
}
Expand Down Expand Up @@ -330,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)
customInputs, err = validateConfigInputsToPrompts(deployConfig, cc.createConfig.DeployVariables)
if err != nil {
return err
}
Expand Down Expand Up @@ -464,31 +464,19 @@ func init() {
rootCmd.AddCommand(newCreateCmd())
}

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

// set inputs to provided values
for _, variable := range provided {
customInputs[variable.Name] = variable.Value
}

// fill in missing vars using variable default references
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 _, 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
}
if err := draftConfig.ApplyDefaultVariables(customInputs); err != nil {
return nil, fmt.Errorf("validate config inputs to prompts: %w", err)
}

for _, variable := range required {
for _, variable := range draftConfig.Variables {
value, ok := customInputs[variable.Name]
if !ok {
return nil, fmt.Errorf("config missing required variable: %s with description: %s", variable.Name, variable.Description)
Expand Down
36 changes: 20 additions & 16 deletions cmd/create_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -142,40 +142,44 @@ func TestInitConfig(t *testing.T) {
}

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

vars, err := validateConfigInputsToPrompts(required, provided)
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",
required := config.DraftConfig{
Variables: []config.BuilderVar{
{
Name: "REQUIRED_PROVIDED",
},
{
Name: "REQUIRED_MISSING",
},
},
}
provided := []UserInputs{
{Name: "REQUIRED_PROVIDED"},
}

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

Expand Down
19 changes: 12 additions & 7 deletions cmd/generate-workflow.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,15 +52,20 @@ with draft on AKS. This command assumes the 'setup-gh' command has been run prop
}

f := cmd.Flags()
f.StringVarP(&gwCmd.workflowConfig.AksClusterName, "cluster-name", "c", emptyDefaultFlagValue, "specify the AKS cluster name")
f.StringVarP(&gwCmd.workflowConfig.AcrName, "registry-name", "r", emptyDefaultFlagValue, "specify the Azure container registry name")
f.StringVar(&gwCmd.workflowConfig.ContainerName, "container-name", emptyDefaultFlagValue, "specify the container image name")
f.StringVarP(&gwCmd.workflowConfig.ResourceGroupName, "resource-group", "g", emptyDefaultFlagValue, "specify the Azure resource group of your AKS cluster")
f.StringVarP(&gwCmd.dest, "destination", "d", currentDirDefaultFlagValue, "specify the path to the project directory")
f.StringVarP(&gwCmd.workflowConfig.BranchName, "branch", "b", emptyDefaultFlagValue, "specify the Github branch to automatically deploy from")
f.StringVar(&gwCmd.deployType, "deploy-type", emptyDefaultFlagValue, "specify the type of deployment")
f.StringArrayVarP(&gwCmd.flagVariables, "variable", "", []string{}, "pass additional variables")
f.StringVarP(&gwCmd.workflowConfig.WorkflowName, "workflow", "w", emptyDefaultFlagValue, "specify the Github workflow name")
f.StringVarP(&gwCmd.workflowConfig.BranchName, "branch", "b", emptyDefaultFlagValue, "specify the Github branch to automatically deploy from")
f.StringVar(&gwCmd.workflowConfig.AcrResourceGroup, "acr-resource-group", emptyDefaultFlagValue, "specify the Azure container registry resource group")
f.StringVarP(&gwCmd.workflowConfig.Acr, "registry-name", "r", emptyDefaultFlagValue, "specify the Azure container registry name")
f.StringVar(&gwCmd.workflowConfig.ContainerName, "container-name", emptyDefaultFlagValue, "specify the container image name")
f.StringVarP(&gwCmd.workflowConfig.ClusterResourceGroup, "cluster-resource-group", "g", emptyDefaultFlagValue, "specify the Azure resource group of your AKS cluster")
f.StringVarP(&gwCmd.workflowConfig.ClusterName, "cluster-name", "c", emptyDefaultFlagValue, "specify the AKS cluster name")
f.StringVar(&gwCmd.workflowConfig.Dockerfile, "dockerfile", emptyDefaultFlagValue, "specify the path to the Dockerfile")
f.StringVarP(&gwCmd.workflowConfig.BuildContextPath, "build-context-path", "x", emptyDefaultFlagValue, "specify the docker build context path")
f.StringVarP(&gwCmd.workflowConfig.Namespace, "namespace", "n", emptyDefaultFlagValue, "specify the Kubernetes namespace")
f.StringVar(&gwCmd.workflowConfig.PrivateCluster, "private-cluster", emptyDefaultFlagValue, "specify if the AKS cluster is private")
f.StringArrayVarP(&gwCmd.flagVariables, "variable", "", []string{}, "pass additional variables")
gwCmd.templateWriter = &writers.LocalFSWriter{}
return cmd
}
Expand Down Expand Up @@ -109,4 +114,4 @@ func (gwc *generateWorkflowCmd) generateWorkflows(dest string, deployType string
maps.Copy(customInputs, flagValuesMap)

return workflow.CreateWorkflowFiles(deployType, customInputs, templateWriter)
}
}
67 changes: 61 additions & 6 deletions pkg/config/draftconfig.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package config

import (
"errors"
"fmt"

log "github.com/sirupsen/logrus"
)
Expand All @@ -26,6 +27,7 @@ type BuilderVar struct {
Description string `yaml:"description"`
ExampleValues []string `yaml:"exampleValues"`
Type string `yaml:"type"`
Value string `yaml:"value"`
}

type BuilderVarDefault struct {
Expand Down Expand Up @@ -67,21 +69,49 @@ 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) error {
func (d *DraftConfig) ApplyDefaultVariables(customInputs map[string]string) error {
varIdxMap := VariableIdxMap(d.Variables)

for _, variable := range d.Variables {
// handle where variable is not set or is set to an empty string from cli handling
if val, ok := customConfig[variable.Name]; !ok || val == "" {
if variable.Default.Value == "" {
return errors.New("variable " + variable.Name + " has no default value")
if customInputs[variable.Name] == "" {
if variable.Default.ReferenceVar != "" {
defaultVal, err := recurseReferenceVars(d.Variables, variable, customInputs, varIdxMap, variable, true)
if err != nil {
return fmt.Errorf("apply default variables: %w", err)
}
log.Infof("Variable %s defaulting to value %s", variable.Name, customInputs[variable.Name])
customInputs[variable.Name] = defaultVal
}

if customInputs[variable.Name] == "" {
if variable.Default.Value != "" {
log.Infof("Variable %s defaulting to value %s", variable.Name, variable.Default.Value)
customInputs[variable.Name] = variable.Default.Value
} else {
return errors.New("variable " + variable.Name + " has no default value")
meecethereese marked this conversation as resolved.
Show resolved Hide resolved
}
}
log.Infof("Variable %s defaulting to value %s", variable.Name, variable.Default.Value)
customConfig[variable.Name] = variable.Default.Value
}
}

return nil
}

meecethereese marked this conversation as resolved.
Show resolved Hide resolved
func recurseReferenceVars(variables []BuilderVar, variable BuilderVar, customInputs map[string]string, varIdxMap map[string]int, variableCheck BuilderVar, isFirst bool) (string, error) {
meecethereese marked this conversation as resolved.
Show resolved Hide resolved
if !isFirst && variable.Name == variableCheck.Name {
return "", errors.New("circular reference detected")
}

if customInputs[variable.Default.ReferenceVar] != "" {
return customInputs[variable.Default.ReferenceVar], nil
} else if variable.Default.ReferenceVar != "" {
return recurseReferenceVars(variables, variables[varIdxMap[variable.Default.ReferenceVar]], customInputs, varIdxMap, variableCheck, false)
}

return variable.Default.Value, nil
}

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

Expand All @@ -96,3 +126,28 @@ func VariableIdxMap(variables []BuilderVar) map[string]int {
type TemplateVariableRecorder interface {
Record(key, value string)
}

func (d *DraftConfig) VariableMap() (map[string]string, error) {
envArgs := make(map[string]string)

for _, variable := range d.Variables {
envArgs[variable.Name] = variable.Value
}

err := d.ApplyDefaultVariables(envArgs)
if err != nil {
return nil, fmt.Errorf("creating variable map: %w", err)
}

return envArgs, nil
}

func (d *DraftConfig) VariableIdxMap() map[string]int {
varIdxMap := make(map[string]int)

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

return varIdxMap
}
8 changes: 4 additions & 4 deletions pkg/osutil/osutil.go
Original file line number Diff line number Diff line change
Expand Up @@ -107,12 +107,12 @@ func CopyDir(
return err
}
} else {
fileContent, err := replaceTemplateVariables(fileSys, srcPath, customInputs)
fileContent, err := ReplaceTemplateVariables(fileSys, srcPath, customInputs)
if err != nil {
return err
}

if err = checkAllVariablesSubstituted(string(fileContent)); err != nil {
if err = CheckAllVariablesSubstituted(string(fileContent)); err != nil {
return fmt.Errorf("error substituting file %s: %w", srcPath, err)
}

Expand All @@ -131,15 +131,15 @@ If any draft variables are found, an error is returned.
Draft variables are defined as a string of non-whitespace characters starting with a non-period character wrapped in double curly braces.
The non-period first character constraint is used to avoid matching helm template functions.
*/
func checkAllVariablesSubstituted(fileContent string) error {
func CheckAllVariablesSubstituted(fileContent string) error {
if unsubstitutedVars := draftVariableRegex.FindAllString(fileContent, -1); len(unsubstitutedVars) > 0 {
unsubstitutedVarsString := strings.Join(unsubstitutedVars, ", ")
return fmt.Errorf("unsubstituted variable: %s", unsubstitutedVarsString)
}
return nil
}

func replaceTemplateVariables(fileSys fs.FS, srcPath string, customInputs map[string]string) ([]byte, error) {
func ReplaceTemplateVariables(fileSys fs.FS, srcPath string, customInputs map[string]string) ([]byte, error) {
file, err := fs.ReadFile(fileSys, srcPath)
if err != nil {
return nil, err
Expand Down
2 changes: 1 addition & 1 deletion pkg/osutil/osutil_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ func TestAllVariablesSubstituted(t *testing.T) {

for _, test := range tests {
t.Run(test.String, func(t *testing.T) {
err := checkAllVariablesSubstituted(test.String)
err := CheckAllVariablesSubstituted(test.String)
didError := err != nil
assert.Equal(t, test.ExpectError, didError)
})
Expand Down
14 changes: 7 additions & 7 deletions pkg/prompts/prompts.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,26 +20,26 @@ const defaultAppName = "my-app"
// Function to get current directory name
var getCurrentDirNameFunc = getCurrentDirName

func RunPromptsFromConfig(config *config.DraftConfig) (map[string]string, error) {
return RunPromptsFromConfigWithSkips(config, []string{})
func RunPromptsFromConfig(draftConfig *config.DraftConfig) (map[string]string, error) {
return RunPromptsFromConfigWithSkips(draftConfig, []string{})
}

func RunPromptsFromConfigWithSkips(config *config.DraftConfig, varsToSkip []string) (map[string]string, error) {
return RunPromptsFromConfigWithSkipsIO(config, varsToSkip, nil, nil)
func RunPromptsFromConfigWithSkips(draftConfig *config.DraftConfig, varsToSkip []string) (map[string]string, error) {
return RunPromptsFromConfigWithSkipsIO(draftConfig, varsToSkip, nil, nil)
}

// RunPromptsFromConfigWithSkipsIO runs the prompts for the given config
// RunPromptsFromConfigWithSkipsIO runs the prompts for the given draftConfig
// skipping any variables in varsToSkip or where the BuilderVar.IsPromptDisabled is true.
// If Stdin or Stdout are nil, the default values will be used.
func RunPromptsFromConfigWithSkipsIO(config *config.DraftConfig, varsToSkip []string, Stdin io.ReadCloser, Stdout io.WriteCloser) (map[string]string, error) {
func RunPromptsFromConfigWithSkipsIO(draftConfig *config.DraftConfig, varsToSkip []string, Stdin io.ReadCloser, Stdout io.WriteCloser) (map[string]string, error) {
skipMap := make(map[string]interface{})
for _, v := range varsToSkip {
skipMap[v] = interface{}(nil)
}

inputs := make(map[string]string)

for _, variable := range config.Variables {
for _, variable := range draftConfig.Variables {
if val, ok := skipMap[variable.Name]; ok && val != "" {
log.Debugf("Skipping prompt for %s", variable.Name)
continue
Expand Down
Loading
Loading