forked from toolshed/abra
		
	
		
			
				
	
	
		
			497 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			497 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
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/v2"
 | 
						|
)
 | 
						|
 | 
						|
// 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 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 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().Slice() {
 | 
						|
		if !strings.HasPrefix(arg, "--") {
 | 
						|
			for _, flag := range c.Args().Slice()[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 DNSName == "" {
 | 
						|
		ShowSubcommandHelpAndError(c, errors.New("no record value provided"))
 | 
						|
	}
 | 
						|
 | 
						|
	return nil
 | 
						|
}
 | 
						|
 | 
						|
// EnsureZoneArgument ensures a zone argument is present.
 | 
						|
func EnsureZoneArgument(c *cli.Context) (string, error) {
 | 
						|
	var zone string
 | 
						|
	if c.Args().First() == "" && !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.NewStringSlice(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.NewStringSlice(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 len(HetznerCloudSSHKeys.Value()) == 0 {
 | 
						|
		ShowSubcommandHelpAndError(c, fmt.Errorf("missing hetzner cloud ssh keys?"))
 | 
						|
	}
 | 
						|
	if HetznerCloudLocation == "" {
 | 
						|
		ShowSubcommandHelpAndError(c, fmt.Errorf("missing hetzner cloud VPS location?"))
 | 
						|
	}
 | 
						|
	if HetznerCloudAPIToken == "" {
 | 
						|
		ShowSubcommandHelpAndError(c, fmt.Errorf("missing hetzner cloud API token?"))
 | 
						|
	}
 | 
						|
 | 
						|
	return nil
 | 
						|
}
 |