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
 | |
| 	},
 | |
| }
 |