refactor: vertical deploy overview

This commit is contained in:
decentral1se 2024-07-10 20:48:08 +02:00
parent d3ede0f0f6
commit f28cffe6d8
Signed by: decentral1se
GPG Key ID: 03789458B3D0C410
5 changed files with 153 additions and 85 deletions

View File

@ -2,6 +2,7 @@ package app
import ( import (
"context" "context"
"fmt"
"coopcloud.tech/abra/cli/internal" "coopcloud.tech/abra/cli/internal"
"coopcloud.tech/abra/pkg/autocomplete" "coopcloud.tech/abra/pkg/autocomplete"
@ -47,6 +48,8 @@ EXAMPLE:
abra app deploy foo.example.com 1e83340e`, abra app deploy foo.example.com 1e83340e`,
BashComplete: autocomplete.AppNameComplete, BashComplete: autocomplete.AppNameComplete,
Action: func(c *cli.Context) error { Action: func(c *cli.Context) error {
var warnMessages []string
app := internal.ValidateApp(c) app := internal.ValidateApp(c)
stackName := app.StackName() stackName := app.StackName()
@ -115,7 +118,7 @@ EXAMPLE:
if deployMeta.IsDeployed { if deployMeta.IsDeployed {
if internal.Force || internal.Chaos { if internal.Force || internal.Chaos {
log.Warnf("%s is already deployed but continuing (--force/--chaos)", app.Name) warnMessages = append(warnMessages, fmt.Sprintf("%s is already deployed", app.Name))
} else { } else {
log.Fatalf("%s is already deployed", app.Name) log.Fatalf("%s is already deployed", app.Name)
} }
@ -139,13 +142,13 @@ EXAMPLE:
log.Fatal(err) log.Fatal(err)
} }
version = formatter.SmallSHA(head.String()) version = formatter.SmallSHA(head.String())
log.Warn("no versions detected, using latest commit") warnMessages = append(warnMessages, fmt.Sprintf("no versions detected, using latest commit"))
} }
} }
chaosVersion := "false" chaosVersion := "false"
if internal.Chaos { if internal.Chaos {
log.Warnf("chaos mode engaged") warnMessages = append(warnMessages, "chaos mode engaged")
if isChaosCommit { if isChaosCommit {
chaosVersion = specificVersion chaosVersion = specificVersion
@ -201,14 +204,12 @@ EXAMPLE:
for _, envVar := range envVars { for _, envVar := range envVars {
if !envVar.Present { if !envVar.Present {
log.Warnf("env var %s missing from %s.env, present in recipe .env.sample", envVar.Name, app.Domain) warnMessages = append(warnMessages,
fmt.Sprintf("env var %s missing from %s.env, present in recipe .env.sample", envVar.Name, app.Domain),
)
} }
} }
if err := internal.DeployOverview(app, version, chaosVersion, "continue with deployment?"); err != nil {
log.Fatal(err)
}
if !internal.NoDomainChecks { if !internal.NoDomainChecks {
domainName, ok := app.Env["DOMAIN"] domainName, ok := app.Env["DOMAIN"]
if ok { if ok {
@ -216,10 +217,14 @@ EXAMPLE:
log.Fatal(err) log.Fatal(err)
} }
} else { } else {
log.Warn("skipping domain checks as no DOMAIN=... configured for app") warnMessages = append(warnMessages, "skipping domain checks as no DOMAIN=... configured for app")
} }
} else { } else {
log.Warn("skipping domain checks as requested") warnMessages = append(warnMessages, "skipping domain checks as requested")
}
if err := internal.DeployOverview(app, warnMessages, version, chaosVersion); err != nil {
log.Fatal(err)
} }
stack.WaitTimeout, err = appPkg.GetTimeoutFromLabel(compose, stackName) stack.WaitTimeout, err = appPkg.GetTimeoutFromLabel(compose, stackName)

View File

@ -47,6 +47,8 @@ EXAMPLE:
abra app rollback foo.example.com 1.2.3+3.2.1`, abra app rollback foo.example.com 1.2.3+3.2.1`,
BashComplete: autocomplete.AppNameComplete, BashComplete: autocomplete.AppNameComplete,
Action: func(c *cli.Context) error { Action: func(c *cli.Context) error {
var warnMessages []string
app := internal.ValidateApp(c) app := internal.ValidateApp(c)
stackName := app.StackName() stackName := app.StackName()
@ -82,7 +84,7 @@ EXAMPLE:
var availableDowngrades []string var availableDowngrades []string
if deployMeta.Version == "unknown" { if deployMeta.Version == "unknown" {
availableDowngrades = versions availableDowngrades = versions
log.Warnf("failed to determine deployed version of %s", app.Name) warnMessages = append(warnMessages, fmt.Sprintf("failed to determine deployed version of %s", app.Name))
} }
specificVersion := c.Args().Get(1) specificVersion := c.Args().Get(1)
@ -113,7 +115,7 @@ EXAMPLE:
if deployMeta.Version != "unknown" && specificVersion == "" { if deployMeta.Version != "unknown" && specificVersion == "" {
if deployMeta.IsChaos { if deployMeta.IsChaos {
log.Warn("attempting to rollback a chaos deployment") warnMessages = append(warnMessages, fmt.Sprintf("attempting to rollback a chaos deployment"))
} }
for _, version := range versions { for _, version := range versions {
@ -203,7 +205,14 @@ EXAMPLE:
} }
// NOTE(d1): no release notes implemeneted for rolling back // NOTE(d1): no release notes implemeneted for rolling back
if err := internal.NewVersionOverview(app, deployMeta.Version, chaosVersion, chosenDowngrade, ""); err != nil { if err := internal.NewVersionOverview(
app,
warnMessages,
"rollback",
deployMeta.Version,
chaosVersion,
chosenDowngrade,
""); err != nil {
log.Fatal(err) log.Fatal(err)
} }

View File

@ -107,7 +107,7 @@ Passing "-p/--prune" does not remove those volumes.`,
chaosVersion = deployMeta.ChaosVersion chaosVersion = deployMeta.ChaosVersion
} }
if err := internal.DeployOverview(app, deployMeta.Version, chaosVersion, "continue with undeploy?"); err != nil { if err := internal.DeployOverview(app, []string{}, deployMeta.Version, chaosVersion); err != nil {
log.Fatal(err) log.Fatal(err)
} }

View File

@ -47,6 +47,8 @@ EXAMPLE:
abra app upgrade foo.example.com 1.2.3+3.2.1`, abra app upgrade foo.example.com 1.2.3+3.2.1`,
BashComplete: autocomplete.AppNameComplete, BashComplete: autocomplete.AppNameComplete,
Action: func(c *cli.Context) error { Action: func(c *cli.Context) error {
var warnMessages []string
app := internal.ValidateApp(c) app := internal.ValidateApp(c)
stackName := app.StackName() stackName := app.StackName()
@ -82,7 +84,7 @@ EXAMPLE:
var availableUpgrades []string var availableUpgrades []string
if deployMeta.Version == "unknown" { if deployMeta.Version == "unknown" {
availableUpgrades = versions availableUpgrades = versions
log.Warnf("failed to determine deployed version of %s", app.Name) warnMessages = append(warnMessages, fmt.Sprintf("failed to determine deployed version of %s", app.Name))
} }
specificVersion := c.Args().Get(1) specificVersion := c.Args().Get(1)
@ -114,7 +116,7 @@ EXAMPLE:
if deployMeta.Version != "unknown" && specificVersion == "" { if deployMeta.Version != "unknown" && specificVersion == "" {
if deployMeta.IsChaos { if deployMeta.IsChaos {
log.Warn("attempting to upgrade a chaos deployment") warnMessages = append(warnMessages, fmt.Sprintf("attempting to upgrade a chaos deployment"))
} }
for _, version := range versions { for _, version := range versions {
@ -156,7 +158,7 @@ EXAMPLE:
} }
if internal.Force && chosenUpgrade == "" { if internal.Force && chosenUpgrade == "" {
log.Warnf("%s is already upgraded to latest but continuing (--force)", app.Name) warnMessages = append(warnMessages, fmt.Sprintf("%s is already upgraded to latest", app.Name))
chosenUpgrade = deployMeta.Version chosenUpgrade = deployMeta.Version
} }
@ -230,7 +232,9 @@ EXAMPLE:
for _, envVar := range envVars { for _, envVar := range envVars {
if !envVar.Present { if !envVar.Present {
log.Warnf("env var %s missing from %s.env, present in recipe .env.sample", envVar.Name, app.Domain) warnMessages = append(warnMessages,
fmt.Sprintf("env var %s missing from %s.env, present in recipe .env.sample", envVar.Name, app.Domain),
)
} }
} }
@ -245,7 +249,14 @@ EXAMPLE:
chaosVersion = deployMeta.ChaosVersion chaosVersion = deployMeta.ChaosVersion
} }
if err := internal.NewVersionOverview(app, deployMeta.Version, chaosVersion, chosenUpgrade, releaseNotes); err != nil { if err := internal.NewVersionOverview(
app,
warnMessages,
"upgrade",
deployMeta.Version,
chaosVersion,
chosenUpgrade,
releaseNotes); err != nil {
log.Fatal(err) log.Fatal(err)
} }

View File

@ -6,17 +6,41 @@ import (
"strings" "strings"
appPkg "coopcloud.tech/abra/pkg/app" appPkg "coopcloud.tech/abra/pkg/app"
"coopcloud.tech/abra/pkg/formatter"
"coopcloud.tech/abra/pkg/log" "coopcloud.tech/abra/pkg/log"
"github.com/AlecAivazis/survey/v2" "github.com/AlecAivazis/survey/v2"
"github.com/charmbracelet/lipgloss"
dockerClient "github.com/docker/docker/client" dockerClient "github.com/docker/docker/client"
) )
// NewVersionOverview shows an upgrade or downgrade overview var borderStyle = lipgloss.NewStyle().
func NewVersionOverview(app appPkg.App, currentVersion, chaosVersion, newVersion, releaseNotes string) error { BorderStyle(lipgloss.ThickBorder()).
tableCol := []string{"server", "recipe", "config", "domain", "version", "chaos", "to deploy"} Padding(0, 1, 0, 1).
table := formatter.CreateTable(tableCol) MaxWidth(79).
BorderForeground(lipgloss.Color("63"))
var headerStyle = lipgloss.NewStyle().
Underline(true).
Bold(true)
var leftStyle = lipgloss.NewStyle().
Bold(true)
var rightStyle = lipgloss.NewStyle()
// horizontal is a JoinHorizontal helper function.
func horizontal(left, mid, right string) string {
return lipgloss.JoinHorizontal(lipgloss.Left, left, mid, right)
}
// NewVersionOverview shows an upgrade or downgrade overview
func NewVersionOverview(
app appPkg.App,
warnMessages []string,
kind,
currentVersion,
chaosVersion,
newVersion,
releaseNotes string) error {
deployConfig := "compose.yml" deployConfig := "compose.yml"
if composeFiles, ok := app.Env["COMPOSE_FILE"]; ok { if composeFiles, ok := app.Env["COMPOSE_FILE"]; ok {
deployConfig = strings.Join(strings.Split(composeFiles, ":"), "\n") deployConfig = strings.Join(strings.Split(composeFiles, ":"), "\n")
@ -27,22 +51,36 @@ func NewVersionOverview(app appPkg.App, currentVersion, chaosVersion, newVersion
server = "local" server = "local"
} }
table.Append([]string{ body := strings.Builder{}
server, body.WriteString(
app.Recipe.Name, borderStyle.Render(
deployConfig, lipgloss.JoinVertical(
app.Domain, lipgloss.Center,
currentVersion, headerStyle.Render(fmt.Sprintf("%s OVERVIEW", strings.ToUpper(kind))),
chaosVersion, lipgloss.JoinVertical(
newVersion, lipgloss.Left,
}) horizontal(leftStyle.Render("SERVER"), " ", rightStyle.Render(server)),
table.Render() horizontal(leftStyle.Render("DOMAIN"), " ", rightStyle.Render(app.Domain)),
horizontal(leftStyle.Render("RECIPE"), " ", rightStyle.Render(app.Recipe.Name)),
horizontal(leftStyle.Render("CONFIG"), " ", rightStyle.Render(deployConfig)),
horizontal(leftStyle.Render("VERSION"), " ", rightStyle.Render(currentVersion)),
horizontal(leftStyle.Render("CHAOS"), " ", rightStyle.Render(chaosVersion)),
horizontal(leftStyle.Render("DEPLOY"), " ", rightStyle.Padding(0).Render(newVersion)),
),
),
),
)
fmt.Println(body.String())
if releaseNotes != "" && newVersion != "" { if releaseNotes != "" && newVersion != "" {
fmt.Println() fmt.Println()
fmt.Print(releaseNotes) fmt.Print(releaseNotes)
} else { } else {
log.Warnf("no release notes available for %s", newVersion) warnMessages = append(warnMessages, fmt.Sprintf("no release notes available for %s", newVersion))
}
for _, msg := range warnMessages {
log.Warn(msg)
} }
if NoInput { if NoInput {
@ -50,16 +88,66 @@ func NewVersionOverview(app appPkg.App, currentVersion, chaosVersion, newVersion
} }
response := false response := false
prompt := &survey.Confirm{ prompt := &survey.Confirm{Message: "proceed?"}
Message: "continue with deployment?",
}
if err := survey.AskOne(prompt, &response); err != nil { if err := survey.AskOne(prompt, &response); err != nil {
return err return err
} }
if !response { if !response {
log.Fatal("exiting as requested") log.Fatal("deployment cancelled")
}
return nil
}
// DeployOverview shows a deployment overview
func DeployOverview(app appPkg.App, warnMessages []string, version, chaosVersion string) error {
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"
}
body := strings.Builder{}
body.WriteString(
borderStyle.Render(
lipgloss.JoinVertical(
lipgloss.Center,
headerStyle.Render("DEPLOY OVERVIEW"),
lipgloss.JoinVertical(
lipgloss.Left,
horizontal(leftStyle.Render("SERVER"), " ", rightStyle.Render(server)),
horizontal(leftStyle.Render("DOMAIN"), " ", rightStyle.Render(app.Domain)),
horizontal(leftStyle.Render("RECIPE"), " ", rightStyle.Render(app.Recipe.Name)),
horizontal(leftStyle.Render("CONFIG"), " ", rightStyle.Render(deployConfig)),
horizontal(leftStyle.Render("VERSION"), " ", rightStyle.Render(version)),
horizontal(leftStyle.Render("CHAOS"), " ", rightStyle.Padding(0).Render(chaosVersion)),
),
),
),
)
fmt.Println(body.String())
for _, msg := range warnMessages {
log.Warn(msg)
}
if NoInput {
return nil
}
response := false
prompt := &survey.Confirm{Message: "proceed?"}
if err := survey.AskOne(prompt, &response); err != nil {
return err
}
if !response {
log.Fatal("deployment cancelled")
} }
return nil return nil
@ -118,48 +206,3 @@ func PostCmds(cl *dockerClient.Client, app appPkg.App, commands string) error {
} }
return nil return nil
} }
// DeployOverview shows a deployment overview
func DeployOverview(app appPkg.App, version, chaosVersion, message string) error {
tableCol := []string{"server", "recipe", "config", "domain", "version", "chaos"}
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,
app.Recipe.Name,
deployConfig,
app.Domain,
version,
chaosVersion,
})
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 {
log.Fatal("exiting as requested")
}
return nil
}