forked from toolshed/abra
		
	
		
			
				
	
	
		
			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/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,
 | 
						|
}
 | 
						|
 | 
						|
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 catalogue.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 = catalogue.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 := 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" {
 | 
						|
							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
 | 
						|
	},
 | 
						|
}
 |