Catalogue package had to be merged into the recipe package due to too many circular import errors. Also, use https url for cloning, assume folks don't have ssh setup by default (the whole reason for the refactor).
250 lines
5.9 KiB
Go
250 lines
5.9 KiB
Go
package app
|
|
|
|
import (
|
|
"fmt"
|
|
"sort"
|
|
"strings"
|
|
|
|
abraFormatter "coopcloud.tech/abra/cli/formatter"
|
|
"coopcloud.tech/abra/cli/internal"
|
|
"coopcloud.tech/abra/pkg/config"
|
|
"coopcloud.tech/abra/pkg/recipe"
|
|
"coopcloud.tech/abra/pkg/ssh"
|
|
"coopcloud.tech/tagcmp"
|
|
"github.com/sirupsen/logrus"
|
|
"github.com/urfave/cli/v2"
|
|
)
|
|
|
|
var status bool
|
|
var statusFlag = &cli.BoolFlag{
|
|
Name: "status",
|
|
Aliases: []string{"S"},
|
|
Value: false,
|
|
Usage: "Show app deployment status",
|
|
Destination: &status,
|
|
}
|
|
|
|
var appType string
|
|
var typeFlag = &cli.StringFlag{
|
|
Name: "type",
|
|
Aliases: []string{"t"},
|
|
Value: "",
|
|
Usage: "Show apps of a specific type",
|
|
Destination: &appType,
|
|
}
|
|
|
|
var listAppServer string
|
|
var listAppServerFlag = &cli.StringFlag{
|
|
Name: "server",
|
|
Aliases: []string{"s"},
|
|
Value: "",
|
|
Usage: "Show apps of a specific server",
|
|
Destination: &listAppServer,
|
|
}
|
|
|
|
type appStatus struct {
|
|
server string
|
|
recipe string
|
|
appName string
|
|
domain string
|
|
status string
|
|
version string
|
|
upgrade string
|
|
}
|
|
|
|
type serverStatus struct {
|
|
apps []appStatus
|
|
appCount int
|
|
versionCount int
|
|
unversionedCount int
|
|
latestCount int
|
|
upgradeCount int
|
|
}
|
|
|
|
var appListCommand = &cli.Command{
|
|
Name: "list",
|
|
Usage: "List all managed apps",
|
|
Description: `
|
|
This command looks at your local file system listing of apps and servers (e.g.
|
|
in ~/.abra/) to generate a report of all your apps.
|
|
|
|
By passing the "--status/-S" flag, you can query all your servers for the
|
|
actual live deployment status. Depending on how many servers you manage, this
|
|
can take some time.
|
|
`,
|
|
Aliases: []string{"ls"},
|
|
Flags: []cli.Flag{
|
|
statusFlag,
|
|
listAppServerFlag,
|
|
typeFlag,
|
|
},
|
|
Action: func(c *cli.Context) error {
|
|
appFiles, err := config.LoadAppFiles(listAppServer)
|
|
if err != nil {
|
|
logrus.Fatal(err)
|
|
}
|
|
|
|
apps, err := config.GetApps(appFiles)
|
|
if err != nil {
|
|
logrus.Fatal(err)
|
|
}
|
|
|
|
sort.Sort(config.ByServerAndType(apps))
|
|
|
|
statuses := make(map[string]map[string]string)
|
|
var catl recipe.RecipeCatalogue
|
|
if status {
|
|
alreadySeen := make(map[string]bool)
|
|
for _, app := range apps {
|
|
if _, ok := alreadySeen[app.Server]; !ok {
|
|
if err := ssh.EnsureHostKey(app.Server); err != nil {
|
|
logrus.Fatal(fmt.Sprintf(internal.SSHFailMsg, app.Server))
|
|
}
|
|
alreadySeen[app.Server] = true
|
|
}
|
|
}
|
|
|
|
statuses, err = config.GetAppStatuses(appFiles)
|
|
if err != nil {
|
|
logrus.Fatal(err)
|
|
}
|
|
|
|
var err error
|
|
catl, err = recipe.ReadRecipeCatalogue()
|
|
if err != nil {
|
|
logrus.Fatal(err)
|
|
}
|
|
}
|
|
|
|
var totalServersCount int
|
|
var totalAppsCount int
|
|
allStats := make(map[string]serverStatus)
|
|
for _, app := range apps {
|
|
var stats serverStatus
|
|
var ok bool
|
|
if stats, ok = allStats[app.Server]; !ok {
|
|
stats = serverStatus{}
|
|
totalServersCount++
|
|
}
|
|
|
|
if app.Type == appType || appType == "" {
|
|
appStats := appStatus{}
|
|
stats.appCount++
|
|
totalAppsCount++
|
|
|
|
if status {
|
|
stackName := app.StackName()
|
|
status := "unknown"
|
|
version := "unknown"
|
|
if statusMeta, ok := statuses[stackName]; ok {
|
|
if currentVersion, exists := statusMeta["version"]; exists {
|
|
version = currentVersion
|
|
}
|
|
if statusMeta["status"] != "" {
|
|
status = statusMeta["status"]
|
|
}
|
|
stats.versionCount++
|
|
} else {
|
|
stats.unversionedCount++
|
|
}
|
|
|
|
appStats.status = status
|
|
appStats.version = version
|
|
|
|
var newUpdates []string
|
|
if version != "unknown" {
|
|
updates, err := recipe.GetRecipeCatalogueVersions(app.Type, catl)
|
|
if err != nil {
|
|
logrus.Fatal(err)
|
|
}
|
|
|
|
parsedVersion, err := tagcmp.Parse(version)
|
|
if err != nil {
|
|
logrus.Fatal(err)
|
|
}
|
|
|
|
for _, update := range updates {
|
|
parsedUpdate, err := tagcmp.Parse(update)
|
|
if err != nil {
|
|
logrus.Fatal(err)
|
|
}
|
|
|
|
if update != version && parsedUpdate.IsGreaterThan(parsedVersion) {
|
|
newUpdates = append(newUpdates, update)
|
|
}
|
|
}
|
|
}
|
|
|
|
if len(newUpdates) == 0 {
|
|
if version == "unknown" {
|
|
appStats.upgrade = "unknown"
|
|
} else {
|
|
appStats.upgrade = "latest"
|
|
stats.latestCount++
|
|
}
|
|
} else {
|
|
// FIXME: jeezus golang why do you not have a list reverse function
|
|
for i, j := 0, len(newUpdates)-1; i < j; i, j = i+1, j-1 {
|
|
newUpdates[i], newUpdates[j] = newUpdates[j], newUpdates[i]
|
|
}
|
|
appStats.upgrade = strings.Join(newUpdates, "\n")
|
|
stats.upgradeCount++
|
|
}
|
|
}
|
|
|
|
appStats.server = app.Server
|
|
appStats.recipe = app.Type
|
|
appStats.appName = app.StackName()
|
|
appStats.domain = app.Domain
|
|
|
|
stats.apps = append(stats.apps, appStats)
|
|
}
|
|
|
|
allStats[app.Server] = stats
|
|
}
|
|
|
|
for serverName, serverStat := range allStats {
|
|
tableCol := []string{"recipe", "app name", "domain"}
|
|
if status {
|
|
tableCol = append(tableCol, []string{"status", "version", "upgrade"}...)
|
|
}
|
|
|
|
table := abraFormatter.CreateTable(tableCol)
|
|
|
|
for _, appStat := range serverStat.apps {
|
|
tableRow := []string{appStat.recipe, appStat.appName, appStat.domain}
|
|
if status {
|
|
tableRow = append(tableRow, []string{appStat.status, appStat.version, appStat.upgrade}...)
|
|
}
|
|
table.Append(tableRow)
|
|
}
|
|
|
|
table.Render()
|
|
|
|
if status {
|
|
fmt.Println(fmt.Sprintf(
|
|
"server: %s | total apps: %v | versioned: %v | unversioned: %v | latest: %v | upgrade: %v",
|
|
serverName,
|
|
serverStat.appCount,
|
|
serverStat.versionCount,
|
|
serverStat.unversionedCount,
|
|
serverStat.latestCount,
|
|
serverStat.upgradeCount,
|
|
))
|
|
} else {
|
|
fmt.Println(fmt.Sprintf("server: %s | total apps: %v", serverName, serverStat.appCount))
|
|
}
|
|
|
|
if len(allStats) > 1 {
|
|
fmt.Println() // newline separator for multiple servers
|
|
}
|
|
}
|
|
|
|
if len(allStats) > 1 {
|
|
fmt.Println(fmt.Sprintf("total servers: %v | total apps: %v ", totalServersCount, totalAppsCount))
|
|
}
|
|
|
|
return nil
|
|
},
|
|
}
|