decentral1se
3381b8936d
All checks were successful
continuous-integration/drone/push Build is passing
519 lines
12 KiB
Go
519 lines
12 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"
|
|
)
|
|
|
|
// AppName is used for configuring app name programmatically
|
|
var AppName string
|
|
|
|
// ValidateRecipe ensures the recipe arg is valid.
|
|
func ValidateRecipe(c *cli.Context, ensureLatest bool) 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)
|
|
}
|
|
}
|
|
|
|
if ensureLatest {
|
|
if err := recipe.EnsureLatest(recipeName); err != nil {
|
|
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, ensureLatest bool) 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)
|
|
}
|
|
|
|
if ensureLatest {
|
|
if err := recipe.EnsureLatest(recipeName); 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.Recipe); 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 {
|
|
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 {
|
|
logrus.Fatal(err)
|
|
}
|
|
}
|
|
|
|
if domainName == "" {
|
|
ShowSubcommandHelpAndError(c, errors.New("no domain provided"))
|
|
}
|
|
|
|
logrus.Debugf("validated %s as domain argument", domainName)
|
|
|
|
return domainName
|
|
}
|
|
|
|
// 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 {
|
|
serverName := c.Args().First()
|
|
|
|
serverNames, err := config.ReadServerNames()
|
|
if err != nil {
|
|
logrus.Fatal(err)
|
|
}
|
|
|
|
if serverName == "" && !NoInput {
|
|
prompt := &survey.Select{
|
|
Message: "Specify a server name",
|
|
Options: serverNames,
|
|
}
|
|
if err := survey.AskOne(prompt, &serverName); err != nil {
|
|
logrus.Fatal(err)
|
|
}
|
|
}
|
|
|
|
matched := false
|
|
for _, name := range serverNames {
|
|
if name == serverName {
|
|
matched = true
|
|
}
|
|
}
|
|
|
|
if !matched {
|
|
ShowSubcommandHelpAndError(c, errors.New("server doesn't exist?"))
|
|
}
|
|
|
|
if serverName == "" {
|
|
ShowSubcommandHelpAndError(c, errors.New("no server provided"))
|
|
}
|
|
|
|
logrus.Debugf("validated %s as server argument", serverName)
|
|
|
|
return serverName
|
|
}
|
|
|
|
// 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
|
|
}
|