forked from toolshed/abra
		
	
		
			
				
	
	
		
			265 lines
		
	
	
		
			6.0 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			265 lines
		
	
	
		
			6.0 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
package app
 | 
						|
 | 
						|
import (
 | 
						|
	"fmt"
 | 
						|
	"sort"
 | 
						|
	"strings"
 | 
						|
 | 
						|
	"coopcloud.tech/abra/cli/internal"
 | 
						|
	"coopcloud.tech/abra/pkg/config"
 | 
						|
	"coopcloud.tech/abra/pkg/formatter"
 | 
						|
	"coopcloud.tech/abra/pkg/recipe"
 | 
						|
	"coopcloud.tech/abra/pkg/ssh"
 | 
						|
	"coopcloud.tech/tagcmp"
 | 
						|
	"github.com/sirupsen/logrus"
 | 
						|
	"github.com/urfave/cli"
 | 
						|
)
 | 
						|
 | 
						|
var status bool
 | 
						|
var statusFlag = &cli.BoolFlag{
 | 
						|
	Name:        "status, S",
 | 
						|
	Usage:       "Show app deployment status",
 | 
						|
	Destination: &status,
 | 
						|
}
 | 
						|
 | 
						|
var appRecipe string
 | 
						|
var recipeFlag = &cli.StringFlag{
 | 
						|
	Name:        "recipe, r",
 | 
						|
	Value:       "",
 | 
						|
	Usage:       "Show apps of a specific recipe",
 | 
						|
	Destination: &appRecipe,
 | 
						|
}
 | 
						|
 | 
						|
var listAppServer string
 | 
						|
var listAppServerFlag = &cli.StringFlag{
 | 
						|
	Name:        "server, 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",
 | 
						|
	Aliases: []string{"ls"},
 | 
						|
	Usage:   "List all managed apps",
 | 
						|
	Description: `
 | 
						|
Read the local file system listing of apps and servers (e.g. ~/.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.
 | 
						|
`,
 | 
						|
	Flags: []cli.Flag{
 | 
						|
		internal.DebugFlag,
 | 
						|
		statusFlag,
 | 
						|
		listAppServerFlag,
 | 
						|
		recipeFlag,
 | 
						|
	},
 | 
						|
	Before: internal.SubCommandBefore,
 | 
						|
	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.ByServerAndRecipe(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{}
 | 
						|
				if appRecipe == "" {
 | 
						|
					// count server, no filtering
 | 
						|
					totalServersCount++
 | 
						|
				}
 | 
						|
			}
 | 
						|
 | 
						|
			if app.Recipe == appRecipe || appRecipe == "" {
 | 
						|
				if appRecipe != "" {
 | 
						|
					// only count server if matches filter
 | 
						|
					totalServersCount++
 | 
						|
				}
 | 
						|
 | 
						|
				appStats := appStatus{}
 | 
						|
				stats.appCount++
 | 
						|
				totalAppsCount++
 | 
						|
 | 
						|
				if status {
 | 
						|
					status := "unknown"
 | 
						|
					version := "unknown"
 | 
						|
					if statusMeta, ok := statuses[app.StackName()]; ok {
 | 
						|
						if currentVersion, exists := statusMeta["version"]; exists {
 | 
						|
							if currentVersion != "" {
 | 
						|
								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.Recipe, 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 {
 | 
						|
						newUpdates = internal.ReverseStringList(newUpdates)
 | 
						|
						appStats.upgrade = strings.Join(newUpdates, "\n")
 | 
						|
						stats.upgradeCount++
 | 
						|
					}
 | 
						|
				}
 | 
						|
 | 
						|
				appStats.server = app.Server
 | 
						|
				appStats.recipe = app.Recipe
 | 
						|
				appStats.appName = app.Name
 | 
						|
				appStats.domain = app.Domain
 | 
						|
 | 
						|
				stats.apps = append(stats.apps, appStats)
 | 
						|
			}
 | 
						|
 | 
						|
			allStats[app.Server] = stats
 | 
						|
		}
 | 
						|
 | 
						|
		alreadySeen := make(map[string]bool)
 | 
						|
		for _, app := range apps {
 | 
						|
			if _, ok := alreadySeen[app.Server]; ok {
 | 
						|
				continue
 | 
						|
			}
 | 
						|
 | 
						|
			serverStat := allStats[app.Server]
 | 
						|
 | 
						|
			tableCol := []string{"recipe", "domain"}
 | 
						|
			if status {
 | 
						|
				tableCol = append(tableCol, []string{"status", "version", "upgrade"}...)
 | 
						|
			}
 | 
						|
 | 
						|
			table := formatter.CreateTable(tableCol)
 | 
						|
 | 
						|
			for _, appStat := range serverStat.apps {
 | 
						|
				tableRow := []string{appStat.recipe, appStat.domain}
 | 
						|
				if status {
 | 
						|
					tableRow = append(tableRow, []string{appStat.status, appStat.version, appStat.upgrade}...)
 | 
						|
				}
 | 
						|
				table.Append(tableRow)
 | 
						|
			}
 | 
						|
 | 
						|
			if table.NumLines() > 0 {
 | 
						|
				table.Render()
 | 
						|
 | 
						|
				if status {
 | 
						|
					fmt.Println(fmt.Sprintf(
 | 
						|
						"server: %s | total apps: %v | versioned: %v | unversioned: %v | latest: %v | upgrade: %v",
 | 
						|
						app.Server,
 | 
						|
						serverStat.appCount,
 | 
						|
						serverStat.versionCount,
 | 
						|
						serverStat.unversionedCount,
 | 
						|
						serverStat.latestCount,
 | 
						|
						serverStat.upgradeCount,
 | 
						|
					))
 | 
						|
				} else {
 | 
						|
					fmt.Println(fmt.Sprintf("server: %s | total apps: %v", app.Server, serverStat.appCount))
 | 
						|
				}
 | 
						|
			}
 | 
						|
 | 
						|
			if len(allStats) > 1 && table.NumLines() > 0 {
 | 
						|
				fmt.Println() // newline separator for multiple servers
 | 
						|
			}
 | 
						|
 | 
						|
			alreadySeen[app.Server] = true
 | 
						|
		}
 | 
						|
 | 
						|
		if len(allStats) > 1 {
 | 
						|
			fmt.Println(fmt.Sprintf("total servers: %v | total apps: %v ", totalServersCount, totalAppsCount))
 | 
						|
		}
 | 
						|
 | 
						|
		return nil
 | 
						|
	},
 | 
						|
}
 |