package internal import ( "errors" "fmt" "os" "strings" "coopcloud.tech/abra/pkg/app" "coopcloud.tech/abra/pkg/config" "coopcloud.tech/abra/pkg/recipe" "coopcloud.tech/abra/pkg/ssh" "github.com/AlecAivazis/survey/v2" "github.com/sirupsen/logrus" "github.com/urfave/cli" ) // AppName is used for configuring app name programmatically var AppName string // ValidateRecipe ensures the recipe arg is valid. func ValidateRecipe(c *cli.Context) recipe.Recipe { recipeName := c.Args().First() if recipeName == "" { ShowSubcommandHelpAndError(c, errors.New("no recipe name provided")) } chosenRecipe, err := recipe.Get(recipeName) if err != nil { if c.Command.Name == "generate" { if strings.Contains(err.Error(), "missing a compose") { logrus.Fatal(err) } logrus.Warn(err) } else { logrus.Fatal(err) } } logrus.Debugf("validated %s as recipe argument", recipeName) return chosenRecipe } // ValidateRecipeWithPrompt ensures a recipe argument is present before // validating, asking for input if required. func ValidateRecipeWithPrompt(c *cli.Context) recipe.Recipe { recipeName := c.Args().First() if recipeName == "" && !NoInput { var recipes []string catl, err := recipe.ReadRecipeCatalogue() if err != nil { logrus.Fatal(err) } knownRecipes := make(map[string]bool) for name := range catl { knownRecipes[name] = true } localRecipes, err := recipe.GetRecipesLocal() if err != nil { logrus.Fatal(err) } for _, recipeLocal := range localRecipes { if _, ok := knownRecipes[recipeLocal]; !ok { knownRecipes[recipeLocal] = true } } for recipeName := range knownRecipes { recipes = append(recipes, recipeName) } prompt := &survey.Select{ Message: "Select recipe", Options: recipes, } if err := survey.AskOne(prompt, &recipeName); err != nil { logrus.Fatal(err) } } if RecipeName != "" { recipeName = RecipeName logrus.Debugf("programmatically setting recipe name to %s", recipeName) } if recipeName == "" { ShowSubcommandHelpAndError(c, errors.New("no recipe name provided")) } chosenRecipe, err := recipe.Get(recipeName) if err != nil { logrus.Fatal(err) } logrus.Debugf("validated %s as recipe argument", recipeName) return chosenRecipe } // ValidateApp ensures the app name arg is valid. func ValidateApp(c *cli.Context) config.App { appName := c.Args().First() if AppName != "" { appName = AppName logrus.Debugf("programmatically setting app name to %s", appName) } if appName == "" { ShowSubcommandHelpAndError(c, errors.New("no app provided")) } app, err := app.Get(appName) if err != nil { logrus.Fatal(err) } if err := recipe.EnsureExists(app.Type); err != nil { logrus.Fatal(err) } if err := ssh.EnsureHostKey(app.Server); err != nil { logrus.Fatal(err) } logrus.Debugf("validated %s as app argument", appName) return app } // ValidateDomain ensures the domain name arg is valid. func ValidateDomain(c *cli.Context) (string, error) { domainName := c.Args().First() if domainName == "" && !NoInput { prompt := &survey.Input{ Message: "Specify a domain name", Default: "example.com", } if err := survey.AskOne(prompt, &domainName); err != nil { return domainName, err } } if domainName == "" { ShowSubcommandHelpAndError(c, errors.New("no domain provided")) } logrus.Debugf("validated %s as domain argument", domainName) return domainName, nil } // ValidateSubCmdFlags ensures flag order conforms to correct order func ValidateSubCmdFlags(c *cli.Context) bool { for argIdx, arg := range c.Args() { if !strings.HasPrefix(arg, "--") { for _, flag := range c.Args()[argIdx:] { if strings.HasPrefix(flag, "--") { return false } } } } return true } // ValidateServer ensures the server name arg is valid. func ValidateServer(c *cli.Context) (string, error) { serverName := c.Args().First() serverNames, err := config.ReadServerNames() if err != nil { return serverName, err } if serverName == "" && !NoInput { prompt := &survey.Select{ Message: "Specify a server name", Options: serverNames, } if err := survey.AskOne(prompt, &serverName); err != nil { return serverName, err } } if serverName == "" { ShowSubcommandHelpAndError(c, errors.New("no server provided")) } logrus.Debugf("validated %s as server argument", serverName) return serverName, nil } // EnsureDNSProvider ensures a DNS provider is chosen. func EnsureDNSProvider() error { if DNSProvider == "" && !NoInput { prompt := &survey.Select{ Message: "Select DNS provider", Options: []string{"gandi"}, } if err := survey.AskOne(prompt, &DNSProvider); err != nil { return err } } if DNSProvider == "" { return fmt.Errorf("missing DNS provider?") } return nil } // EnsureDNSTypeFlag ensures a DNS type flag is present. func EnsureDNSTypeFlag(c *cli.Context) error { if DNSType == "" && !NoInput { prompt := &survey.Input{ Message: "Specify DNS record type", Default: "A", } if err := survey.AskOne(prompt, &DNSType); err != nil { return err } } if DNSType == "" { ShowSubcommandHelpAndError(c, errors.New("no record type provided")) } return nil } // EnsureDNSNameFlag ensures a DNS name flag is present. func EnsureDNSNameFlag(c *cli.Context) error { if DNSName == "" && !NoInput { prompt := &survey.Input{ Message: "Specify DNS record name", Default: "mysubdomain", } if err := survey.AskOne(prompt, &DNSName); err != nil { return err } } if DNSName == "" { ShowSubcommandHelpAndError(c, errors.New("no record name provided")) } return nil } // EnsureDNSValueFlag ensures a DNS value flag is present. func EnsureDNSValueFlag(c *cli.Context) error { if DNSValue == "" && !NoInput { prompt := &survey.Input{ Message: "Specify DNS record value", Default: "192.168.1.2", } if err := survey.AskOne(prompt, &DNSValue); err != nil { return err } } if DNSValue == "" { ShowSubcommandHelpAndError(c, errors.New("no record value provided")) } return nil } // EnsureZoneArgument ensures a zone argument is present. func EnsureZoneArgument(c *cli.Context) (string, error) { zone := c.Args().First() if zone == "" && !NoInput { prompt := &survey.Input{ Message: "Specify a domain name zone", Default: "example.com", } if err := survey.AskOne(prompt, &zone); err != nil { return zone, err } } if zone == "" { ShowSubcommandHelpAndError(c, errors.New("no zone value provided")) } return zone, nil } // EnsureServerProvider ensures a 3rd party server provider is chosen. func EnsureServerProvider() error { if ServerProvider == "" && !NoInput { prompt := &survey.Select{ Message: "Select server provider", Options: []string{"capsul", "hetzner-cloud"}, } if err := survey.AskOne(prompt, &ServerProvider); err != nil { return err } } if ServerProvider == "" { return fmt.Errorf("missing server provider?") } return nil } // EnsureNewCapsulVPSFlags ensure all flags are present. func EnsureNewCapsulVPSFlags(c *cli.Context) error { if CapsulName == "" && !NoInput { prompt := &survey.Input{ Message: "specify capsul name", } if err := survey.AskOne(prompt, &CapsulName); err != nil { return err } } if !NoInput { prompt := &survey.Input{ Message: "specify capsul instance URL", Default: CapsulInstanceURL, } if err := survey.AskOne(prompt, &CapsulInstanceURL); err != nil { return err } } if !NoInput { prompt := &survey.Input{ Message: "specify capsul type", Default: CapsulType, } if err := survey.AskOne(prompt, &CapsulType); err != nil { return err } } if !NoInput { prompt := &survey.Input{ Message: "specify capsul image", Default: CapsulImage, } if err := survey.AskOne(prompt, &CapsulImage); err != nil { return err } } if len(CapsulSSHKeys.Value()) == 0 && !NoInput { var sshKeys string prompt := &survey.Input{ Message: "specify capsul SSH keys (e.g. me@foo.com)", Default: "", } if err := survey.AskOne(prompt, &CapsulSSHKeys); err != nil { return err } CapsulSSHKeys = cli.StringSlice(strings.Split(sshKeys, ",")) } if CapsulAPIToken == "" && !NoInput { token, ok := os.LookupEnv("CAPSUL_TOKEN") if !ok { prompt := &survey.Input{ Message: "specify capsul API token", } if err := survey.AskOne(prompt, &CapsulAPIToken); err != nil { return err } } else { CapsulAPIToken = token } } if CapsulName == "" { ShowSubcommandHelpAndError(c, fmt.Errorf("missing capsul name?")) } if CapsulInstanceURL == "" { ShowSubcommandHelpAndError(c, fmt.Errorf("missing capsul instance url?")) } if CapsulType == "" { ShowSubcommandHelpAndError(c, fmt.Errorf("missing capsul type?")) } if CapsulImage == "" { ShowSubcommandHelpAndError(c, fmt.Errorf("missing capsul image?")) } if len(CapsulSSHKeys.Value()) == 0 { ShowSubcommandHelpAndError(c, fmt.Errorf("missing capsul ssh keys?")) } if CapsulAPIToken == "" { ShowSubcommandHelpAndError(c, fmt.Errorf("missing capsul API token?")) } return nil } // EnsureNewHetznerCloudVPSFlags ensure all flags are present. func EnsureNewHetznerCloudVPSFlags(c *cli.Context) error { if HetznerCloudName == "" && !NoInput { prompt := &survey.Input{ Message: "specify hetzner cloud VPS name", } if err := survey.AskOne(prompt, &HetznerCloudName); err != nil { return err } } if !NoInput { prompt := &survey.Input{ Message: "specify hetzner cloud VPS type", Default: HetznerCloudType, } if err := survey.AskOne(prompt, &HetznerCloudType); err != nil { return err } } if !NoInput { prompt := &survey.Input{ Message: "specify hetzner cloud VPS image", Default: HetznerCloudImage, } if err := survey.AskOne(prompt, &HetznerCloudImage); err != nil { return err } } if len(HetznerCloudSSHKeys.Value()) == 0 && !NoInput { var sshKeys string prompt := &survey.Input{ Message: "specify hetzner cloud SSH keys (e.g. me@foo.com)", Default: "", } if err := survey.AskOne(prompt, &sshKeys); err != nil { return err } HetznerCloudSSHKeys = cli.StringSlice(strings.Split(sshKeys, ",")) } if !NoInput { prompt := &survey.Input{ Message: "specify hetzner cloud VPS location", Default: HetznerCloudLocation, } if err := survey.AskOne(prompt, &HetznerCloudLocation); err != nil { return err } } if HetznerCloudAPIToken == "" && !NoInput { token, ok := os.LookupEnv("HCLOUD_TOKEN") if !ok { prompt := &survey.Input{ Message: "specify hetzner cloud API token", } if err := survey.AskOne(prompt, &HetznerCloudAPIToken); err != nil { return err } } else { HetznerCloudAPIToken = token } } if HetznerCloudName == "" { ShowSubcommandHelpAndError(c, fmt.Errorf("missing hetzner cloud VPS name?")) } if HetznerCloudType == "" { ShowSubcommandHelpAndError(c, fmt.Errorf("missing hetzner cloud VPS type?")) } if HetznerCloudImage == "" { ShowSubcommandHelpAndError(c, fmt.Errorf("missing hetzner cloud image?")) } if HetznerCloudLocation == "" { ShowSubcommandHelpAndError(c, fmt.Errorf("missing hetzner cloud VPS location?")) } if HetznerCloudAPIToken == "" { ShowSubcommandHelpAndError(c, fmt.Errorf("missing hetzner cloud API token?")) } return nil }