Skip to content

Commit

Permalink
Merge pull request #145 from Azure/davidgamero/update-variable-flag
Browse files Browse the repository at this point in the history
add `--variable` flag to `draft update` command
  • Loading branch information
davidgamero authored Sep 28, 2022
2 parents c5f5f2c + 63fea25 commit bb19713
Show file tree
Hide file tree
Showing 15 changed files with 1,334 additions and 127 deletions.
539 changes: 506 additions & 33 deletions .github/workflows/integration-linux.yml

Large diffs are not rendered by default.

582 changes: 560 additions & 22 deletions .github/workflows/integration-windows.yml

Large diffs are not rendered by default.

1 change: 0 additions & 1 deletion cmd/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,6 @@ type createCmd struct {
createConfig *config.CreateConfig

supportedLangs *languages.Languages
fileMatches *filematches.FileMatches

templateWriter templatewriter.TemplateWriter
}
Expand Down
69 changes: 57 additions & 12 deletions cmd/update.go
Original file line number Diff line number Diff line change
@@ -1,42 +1,87 @@
package cmd

import (
"embed"
"fmt"
"strings"

log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"

"github.com/Azure/draft/pkg/addons"
"github.com/Azure/draft/pkg/templatewriter"
"github.com/Azure/draft/pkg/templatewriter/writers"
"github.com/Azure/draft/template"
)

type updateCmd struct {
dest string
provider string
addon string
flagVariables []string
userInputs map[string]string
templateWriter templatewriter.TemplateWriter
addonFS embed.FS
}

func newUpdateCmd() *cobra.Command {
dest := ""
provider := ""
addon := ""
userInputs := make(map[string]string)
templateWriter := &writers.LocalFSWriter{}
uc := &updateCmd{}
// updateCmd represents the update command
var cmd = &cobra.Command{
Use: "update",
Short: "Updates your application to be internet accessible",
Long: `This command automatically updates your yaml files as necessary so that your application
will be able to receive external requests.`,
will be able to receive external requests.`,
RunE: func(cmd *cobra.Command, args []string) error {
if err := addons.GenerateAddon(template.Addons, provider, addon, dest, userInputs, templateWriter); err != nil {
if err := uc.run(); err != nil {
return err
}

log.Info("Draft has successfully updated your yaml files so that your application will be able to receive external requests 😃")

return nil
},
}
f := cmd.Flags()
f.StringVarP(&dest, "destination", "d", ".", "specify the path to the project directory")
f.StringVarP(&provider, "provider", "p", "azure", "cloud provider")
f.StringVarP(&addon, "addon", "a", "", "addon name")
f.StringVarP(&uc.dest, "destination", "d", ".", "specify the path to the project directory")
f.StringVarP(&uc.provider, "provider", "p", "azure", "cloud provider")
f.StringVarP(&uc.addon, "addon", "a", "", "addon name")
f.StringArrayVarP(&uc.flagVariables, "variable", "", []string{}, "pass a variable non-interactively (ex: --variable foo=bar)")

uc.templateWriter = &writers.LocalFSWriter{}

return cmd
}

func (uc *updateCmd) run() error {
flagVariablesMap := make(map[string]string)
for _, flagVar := range uc.flagVariables {
flagVarName, flagVarValue, ok := strings.Cut(flagVar, "=")
if !ok {
return fmt.Errorf("invalid variable format: %s", flagVar)
}
flagVariablesMap[flagVarName] = flagVarValue
log.Debugf("flag variable %s=%s", flagVarName, flagVarValue)
}

if uc.addon == "" {
addon, err := addons.PromptAddon(template.Addons, uc.provider)
if err != nil {
return err
}
uc.addon = addon
}

addonConfig, err := addons.GetAddonConfig(template.Addons, uc.provider, uc.addon)
if err != nil {
return err
}

uc.userInputs, err = addons.PromptAddonValues(uc.dest, flagVariablesMap, addonConfig)
if err != nil {
return err
}
log.Debugf("addonInputs is: %s", uc.userInputs)

return addons.GenerateAddon(template.Addons, uc.provider, uc.addon, uc.dest, uc.userInputs, uc.templateWriter)
}

func init() {
Expand Down
102 changes: 62 additions & 40 deletions pkg/addons/addons.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import (
"errors"
"fmt"
"io/fs"
"path/filepath"
"path"
"strings"

"github.com/manifoldco/promptui"
Expand All @@ -25,75 +25,97 @@ var (
)

func GenerateAddon(addons embed.FS, provider, addon, dest string, userInputs map[string]string, templateWriter templatewriter.TemplateWriter) error {
providerPath := filepath.Join(parentDirName, strings.ToLower(provider))
addonMap, err := embedutils.EmbedFStoMap(addons, providerPath)
addOnConfig, err := GetAddonConfig(addons, provider, addon)
if err != nil {
return err
}
if addon == "" {
addonNames := maps.Keys(addonMap)
prompt := promptui.Select{
Label: fmt.Sprintf("Select %s addon", provider),
Items: addonNames,
}
_, addon, err = prompt.Run()
if err != nil {
return err
}
log.Debugf("addOnConfig is: %s", addOnConfig)

selectedAddonPath, err := GetAddonPath(addons, provider, addon)
if err != nil {
return err
}

addonDestPath, err := addOnConfig.GetAddonDestPath(dest)
if err != nil {
return err
}

if err = osutil.CopyDir(addons, selectedAddonPath, addonDestPath, &addOnConfig.DraftConfig, userInputs, templateWriter); err != nil {
return err
}

return err
}

func GetAddonPath(addons embed.FS, provider, addon string) (string, error) {
providerPath := path.Join(parentDirName, strings.ToLower(provider))
addonMap, err := embedutils.EmbedFStoMap(addons, providerPath)
if err != nil {
return "", err
}
var selectedAddon fs.DirEntry
var ok bool
if selectedAddon, ok = addonMap[addon]; !ok {
return errors.New("addon not found")
return "", errors.New("addon not found")
}

selectedAddonPath := filepath.Join(providerPath, selectedAddon.Name())
selectedAddonPath := path.Join(providerPath, selectedAddon.Name())

return selectedAddonPath, nil
}

configBytes, err := fs.ReadFile(addons, filepath.Join(selectedAddonPath, "draft.yaml"))
func GetAddonConfig(addons embed.FS, provider, addon string) (config.AddonConfig, error) {
selectedAddonPath, err := GetAddonPath(addons, provider, addon)
if err != nil {
return err
return config.AddonConfig{}, err
}

configBytes, err := fs.ReadFile(addons, path.Join(selectedAddonPath, "draft.yaml"))
if err != nil {
return config.AddonConfig{}, err
}
var addOnConfig config.AddonConfig
if err = yaml.Unmarshal(configBytes, &addOnConfig); err != nil {
return err
return config.AddonConfig{}, err
}

log.Debugf("addOnConfig is: %s", addOnConfig)
return addOnConfig, nil
}

addonVals, err := getAddonValues(dest, userInputs, addOnConfig)
func PromptAddon(addons embed.FS, provider string) (string, error) {
providerPath := path.Join(parentDirName, strings.ToLower(provider))
addonMap, err := embedutils.EmbedFStoMap(addons, providerPath)
if err != nil {
return err
return "", err
}

log.Debugf("addonValues are: %s", addonVals)

addonDestPath, err := addOnConfig.GetAddonDestPath(dest)
if err != nil {
return err
addonNames := maps.Keys(addonMap)
prompt := promptui.Select{
Label: fmt.Sprintf("Select %s addon", provider),
Items: addonNames,
}

if err = osutil.CopyDir(addons, selectedAddonPath, addonDestPath, &addOnConfig.DraftConfig, addonVals, templateWriter); err != nil {
return err
_, addon, err := prompt.Run()
if err != nil {
return "", err
}

return err
return addon, nil
}

func getAddonValues(dest string, userInputs map[string]string, addOnConfig config.AddonConfig) (map[string]string, error) {
func PromptAddonValues(dest string, userInputs map[string]string, addOnConfig config.AddonConfig) (map[string]string, error) {
log.Debugf("getAddonValues: %s", userInputs)
var err error
if userInputs == nil {
userInputs = make(map[string]string)
}

if len(userInputs) == 0 {
userInputs, err = prompts.RunPromptsFromConfig(&addOnConfig.DraftConfig)
if err != nil {
return nil, err
}
log.Debug("got user inputs")
inputsToSkip := maps.Keys(userInputs)
log.Debugf("inputsToSkip: %s", inputsToSkip)
promptInputs, err := prompts.RunPromptsFromConfigWithSkips(&addOnConfig.DraftConfig, inputsToSkip)
if err != nil {
return nil, err
}
log.Debug("got user inputs")
for k, v := range promptInputs {
userInputs[k] = v
}

referenceMap, err := addOnConfig.GetReferenceValueMap(dest)
Expand Down
4 changes: 3 additions & 1 deletion pkg/config/addon_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,9 @@ func (ac *AddonConfig) getDeployType(dest string) (string, error) {
if ac.deployType != "" {
return ac.deployType, nil
}
return filematches.FindDraftDeploymentFiles(dest)
deploymentType, err := filematches.FindDraftDeploymentFiles(dest)
log.Debugf("found deployment type: %s", deploymentType)
return deploymentType, err
}

func (ac *AddonConfig) GetAddonDestPath(dest string) (string, error) {
Expand Down
2 changes: 1 addition & 1 deletion pkg/filematches/filematches.go
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ func FindDraftDeploymentFiles(dest string) (deploymentType string, err error) {
if _, err := os.Stat(dest + "/charts"); !os.IsNotExist(err) {
return "helm", nil
}
if _, err := os.Stat(dest + "/base"); !os.IsNotExist(err) {
if _, err := os.Stat(dest + "/overlays"); !os.IsNotExist(err) {
return "kustomize", nil
}
if _, err := os.Stat(dest + "/manifests"); !os.IsNotExist(err) {
Expand Down
18 changes: 18 additions & 0 deletions pkg/prompts/prompts.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,31 @@ type TemplateSelect struct {
}

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

func RunPromptsFromConfigWithSkips(config *config.DraftConfig, varsToSkip []string) (map[string]string, error) {
templatePrompts := make([]*TemplatePrompt, 0)
templateSelects := make([]*TemplateSelect, 0)

skipMap := make(map[string]interface{})
for _, v := range varsToSkip {
skipMap[v] = interface{}(nil)
}

for _, customPrompt := range config.Variables {
if _, ok := skipMap[customPrompt.Name]; ok {
log.Debugf("Skipping prompt for %s", customPrompt.Name)
continue
}

log.Debugf("constructing prompt for: %s", customPrompt)
if customPrompt.VarType == "bool" {
prompt := &promptui.Select{
Label: "Please select " + customPrompt.Description,
Items: []bool{true, false},
}

templateSelects = append(templateSelects, &TemplateSelect{
Select: prompt,
OverrideString: customPrompt.Name,
Expand All @@ -39,6 +55,7 @@ func RunPromptsFromConfig(config *config.DraftConfig) (map[string]string, error)
defaultString = " (default: " + variableDefault.Value + ")"
}
}

prompt := &promptui.Prompt{
Label: "Please enter " + customPrompt.Description + defaultString,
Validate: func(s string) error {
Expand All @@ -52,6 +69,7 @@ func RunPromptsFromConfig(config *config.DraftConfig) (map[string]string, error)
return nil
},
}

templatePrompts = append(templatePrompts, &TemplatePrompt{
Prompt: prompt,
OverrideString: customPrompt.Name,
Expand Down
4 changes: 4 additions & 0 deletions test/check_windows_addon_helm.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
$filesExist=$true
$filesExist=$filesExist -and (Test-Path -Path ./charts/templates/ingress.yaml -PathType Leaf)
echo "$file exists: $filesExist"
if (-not $filesExist) {Exit 1}
4 changes: 4 additions & 0 deletions test/check_windows_addon_kustomize.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
$filesExist=$true
$filesExist=$filesExist -and (Test-Path -Path ./overlays/production/ingress.yaml -PathType Leaf)
echo "$file exists: $filesExist"
if (-not $filesExist) {Exit 1}
Loading

0 comments on commit bb19713

Please sign in to comment.