forked from toolshed/abra
.gitea
cli
app
catalogue
internal
command.go
deploy.go
errors.go
flags.go
list.go
new.go
recipe.go
validate.go
recipe
record
server
cli.go
cmd
pkg
scripts
tests
.drone.yml
.e2e.env.sample
.envrc.sample
.gitignore
.goreleaser.yml
Makefile
README.md
go.mod
go.sum
renovate.json
265 lines
6.1 KiB
Go
265 lines
6.1 KiB
Go
package internal
|
|
|
|
import (
|
|
"fmt"
|
|
"io/ioutil"
|
|
"os"
|
|
"path"
|
|
"strings"
|
|
|
|
"coopcloud.tech/abra/pkg/client"
|
|
"coopcloud.tech/abra/pkg/config"
|
|
"coopcloud.tech/abra/pkg/dns"
|
|
"coopcloud.tech/abra/pkg/formatter"
|
|
"coopcloud.tech/abra/pkg/git"
|
|
"coopcloud.tech/abra/pkg/lint"
|
|
"coopcloud.tech/abra/pkg/recipe"
|
|
"coopcloud.tech/abra/pkg/upstream/stack"
|
|
"github.com/AlecAivazis/survey/v2"
|
|
"github.com/sirupsen/logrus"
|
|
"github.com/urfave/cli/v2"
|
|
)
|
|
|
|
// DeployAction is the main command-line action for this package
|
|
func DeployAction(c *cli.Context) error {
|
|
app := ValidateApp(c)
|
|
|
|
if err := recipe.EnsureUpToDate(app.Type); err != nil {
|
|
logrus.Fatal(err)
|
|
}
|
|
|
|
r, err := recipe.Get(app.Type)
|
|
if err != nil {
|
|
logrus.Fatal(err)
|
|
}
|
|
|
|
if err := lint.LintForErrors(r); err != nil {
|
|
logrus.Fatal(err)
|
|
}
|
|
|
|
cl, err := client.New(app.Server)
|
|
if err != nil {
|
|
logrus.Fatal(err)
|
|
}
|
|
|
|
logrus.Debugf("checking whether %s is already deployed", app.StackName())
|
|
|
|
isDeployed, deployedVersion, err := stack.IsDeployed(c.Context, cl, app.StackName())
|
|
if err != nil {
|
|
logrus.Fatal(err)
|
|
}
|
|
|
|
if isDeployed {
|
|
if Force || Chaos {
|
|
logrus.Warnf("%s is already deployed but continuing (--force/--chaos)", app.Name)
|
|
} else {
|
|
logrus.Fatalf("%s is already deployed", app.Name)
|
|
}
|
|
}
|
|
|
|
version := deployedVersion
|
|
if version == "" && !Chaos {
|
|
catl, err := recipe.ReadRecipeCatalogue()
|
|
if err != nil {
|
|
logrus.Fatal(err)
|
|
}
|
|
versions, err := recipe.GetRecipeCatalogueVersions(app.Type, catl)
|
|
if err != nil {
|
|
logrus.Fatal(err)
|
|
}
|
|
if len(versions) > 0 {
|
|
version = versions[len(versions)-1]
|
|
logrus.Debugf("choosing %s as version to deploy", version)
|
|
if err := recipe.EnsureVersion(app.Type, version); err != nil {
|
|
logrus.Fatal(err)
|
|
}
|
|
} else {
|
|
head, err := git.GetRecipeHead(app.Type)
|
|
if err != nil {
|
|
logrus.Fatal(err)
|
|
}
|
|
version = formatter.SmallSHA(head.String())
|
|
logrus.Warn("no versions detected, using latest commit")
|
|
if err := recipe.EnsureLatest(app.Type); err != nil {
|
|
logrus.Fatal(err)
|
|
}
|
|
}
|
|
}
|
|
|
|
if version == "" && !Chaos {
|
|
logrus.Debugf("choosing %s as version to deploy", version)
|
|
if err := recipe.EnsureVersion(app.Type, version); err != nil {
|
|
logrus.Fatal(err)
|
|
}
|
|
}
|
|
|
|
if version != "" && !Chaos {
|
|
if err := recipe.EnsureVersion(app.Type, version); err != nil {
|
|
logrus.Fatal(err)
|
|
}
|
|
}
|
|
|
|
if Chaos {
|
|
logrus.Warnf("chaos mode engaged")
|
|
var err error
|
|
version, err = recipe.ChaosVersion(app.Type)
|
|
if err != nil {
|
|
logrus.Fatal(err)
|
|
}
|
|
}
|
|
|
|
abraShPath := fmt.Sprintf("%s/%s/%s", config.RECIPES_DIR, app.Type, "abra.sh")
|
|
abraShEnv, err := config.ReadAbraShEnvVars(abraShPath)
|
|
if err != nil {
|
|
logrus.Fatal(err)
|
|
}
|
|
for k, v := range abraShEnv {
|
|
app.Env[k] = v
|
|
}
|
|
|
|
composeFiles, err := config.GetAppComposeFiles(app.Type, app.Env)
|
|
if err != nil {
|
|
logrus.Fatal(err)
|
|
}
|
|
deployOpts := stack.Deploy{
|
|
Composefiles: composeFiles,
|
|
Namespace: app.StackName(),
|
|
Prune: false,
|
|
ResolveImage: stack.ResolveImageAlways,
|
|
}
|
|
compose, err := config.GetAppComposeConfig(app.Name, deployOpts, app.Env)
|
|
if err != nil {
|
|
logrus.Fatal(err)
|
|
}
|
|
|
|
if err := DeployOverview(app, version, "continue with deployment?"); err != nil {
|
|
logrus.Fatal(err)
|
|
}
|
|
|
|
if !NoDomainChecks {
|
|
domainName := app.Env["DOMAIN"]
|
|
ipv4, err := dns.EnsureIPv4(domainName)
|
|
if err != nil || ipv4 == "" {
|
|
logrus.Fatalf("could not find an IP address assigned to %s?", domainName)
|
|
}
|
|
|
|
if _, err = dns.EnsureDomainsResolveSameIPv4(domainName, app.Server); err != nil {
|
|
logrus.Fatal(err)
|
|
}
|
|
} else {
|
|
logrus.Warn("skipping domain checks as requested")
|
|
}
|
|
|
|
if err := stack.RunDeploy(cl, deployOpts, compose, app.Name, DontWaitConverge); err != nil {
|
|
logrus.Fatal(err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// DeployOverview shows a deployment overview
|
|
func DeployOverview(app config.App, version, message string) error {
|
|
tableCol := []string{"server", "compose", "domain", "app name", "version"}
|
|
table := formatter.CreateTable(tableCol)
|
|
|
|
deployConfig := "compose.yml"
|
|
if composeFiles, ok := app.Env["COMPOSE_FILE"]; ok {
|
|
deployConfig = strings.Join(strings.Split(composeFiles, ":"), "\n")
|
|
}
|
|
|
|
server := app.Server
|
|
if app.Server == "default" {
|
|
server = "local"
|
|
}
|
|
|
|
table.Append([]string{server, deployConfig, app.Domain, app.Name, version})
|
|
table.Render()
|
|
|
|
if NoInput {
|
|
return nil
|
|
}
|
|
|
|
response := false
|
|
prompt := &survey.Confirm{
|
|
Message: message,
|
|
}
|
|
|
|
if err := survey.AskOne(prompt, &response); err != nil {
|
|
return err
|
|
}
|
|
|
|
if !response {
|
|
logrus.Fatal("exiting as requested")
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// NewVersionOverview shows an upgrade or downgrade overview
|
|
func NewVersionOverview(app config.App, currentVersion, newVersion, releaseNotes string) error {
|
|
tableCol := []string{"server", "compose", "domain", "app name", "current version", "to be deployed"}
|
|
table := formatter.CreateTable(tableCol)
|
|
|
|
deployConfig := "compose.yml"
|
|
if composeFiles, ok := app.Env["COMPOSE_FILE"]; ok {
|
|
deployConfig = strings.Join(strings.Split(composeFiles, ":"), "\n")
|
|
}
|
|
|
|
server := app.Server
|
|
if app.Server == "default" {
|
|
server = "local"
|
|
}
|
|
|
|
table.Append([]string{server, deployConfig, app.Domain, app.Name, currentVersion, newVersion})
|
|
table.Render()
|
|
|
|
if releaseNotes == "" {
|
|
var err error
|
|
releaseNotes, err = GetReleaseNotes(app.Type, newVersion)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
if releaseNotes != "" {
|
|
fmt.Println()
|
|
fmt.Println(fmt.Sprintf("%s release notes:\n\n%s", newVersion, releaseNotes))
|
|
} else {
|
|
logrus.Warnf("no release notes available for %s", newVersion)
|
|
}
|
|
|
|
if NoInput {
|
|
return nil
|
|
}
|
|
|
|
response := false
|
|
prompt := &survey.Confirm{
|
|
Message: "continue with deployment?",
|
|
}
|
|
|
|
if err := survey.AskOne(prompt, &response); err != nil {
|
|
return err
|
|
}
|
|
|
|
if !response {
|
|
logrus.Fatal("exiting as requested")
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// GetReleaseNotes prints release notes for a recipe version
|
|
func GetReleaseNotes(recipeName, version string) (string, error) {
|
|
fpath := path.Join(config.RECIPES_DIR, recipeName, "release", version)
|
|
|
|
if _, err := os.Stat(fpath); !os.IsNotExist(err) {
|
|
releaseNotes, err := ioutil.ReadFile(fpath)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
return string(releaseNotes), nil
|
|
}
|
|
|
|
return "", nil
|
|
}
|