package app import ( "fmt" "sort" "strings" abraFormatter "coopcloud.tech/abra/cli/formatter" "coopcloud.tech/abra/cli/internal" "coopcloud.tech/abra/pkg/catalogue" "coopcloud.tech/abra/pkg/config" "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, } 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)) 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 := make(map[string]map[string]string) tableCol := []string{"Server", "Type", "Domain"} if status { tableCol = append(tableCol, "Status", "Version", "Updates") statuses, err = config.GetAppStatuses(appFiles) if err != nil { logrus.Fatal(err) } } table := abraFormatter.CreateTable(tableCol) table.SetAutoMergeCellsByColumnIndex([]int{0}) var ( versionedAppsCount int unversionedAppsCount int onLatestCount int canUpgradeCount int ) catl, err := catalogue.ReadRecipeCatalogue() if err != nil { logrus.Fatal(err) } var appsCount int for _, app := range apps { var tableRow []string if app.Type == appType || appType == "" { appsCount++ // If type flag is set, check for it, if not, Type == "" tableRow = []string{app.Server, app.Type, app.Domain} 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"] } tableRow = append(tableRow, status, version) versionedAppsCount++ } else { tableRow = append(tableRow, status, version) unversionedAppsCount++ } var newUpdates []string if version != "unknown" { updates, err := catalogue.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" { tableRow = append(tableRow, "unknown") } else { tableRow = append(tableRow, "on latest") onLatestCount++ } } 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] } tableRow = append(tableRow, strings.Join(newUpdates, "\n")) canUpgradeCount++ } } } table.Append(tableRow) } var stats string if status { stats = fmt.Sprintf( "Total apps: %v | Versioned: %v | Unversioned: %v | On latest: %v | Can upgrade: %v", appsCount, versionedAppsCount, unversionedAppsCount, onLatestCount, canUpgradeCount, ) } else { stats = fmt.Sprintf("Total apps: %v", appsCount) } table.SetCaption(true, stats) table.Render() return nil }, }