forked from toolshed/abra
		
	
		
			
				
	
	
		
			338 lines
		
	
	
		
			8.1 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			338 lines
		
	
	
		
			8.1 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
package app
 | 
						|
 | 
						|
import (
 | 
						|
	"encoding/json"
 | 
						|
	"fmt"
 | 
						|
	"sort"
 | 
						|
	"strconv"
 | 
						|
	"strings"
 | 
						|
 | 
						|
	"coopcloud.tech/abra/cli/internal"
 | 
						|
	appPkg "coopcloud.tech/abra/pkg/app"
 | 
						|
	"coopcloud.tech/abra/pkg/autocomplete"
 | 
						|
	"coopcloud.tech/abra/pkg/formatter"
 | 
						|
	"coopcloud.tech/abra/pkg/i18n"
 | 
						|
	"coopcloud.tech/abra/pkg/log"
 | 
						|
	"coopcloud.tech/tagcmp"
 | 
						|
	"github.com/spf13/cobra"
 | 
						|
)
 | 
						|
 | 
						|
type appStatus struct {
 | 
						|
	Server       string `json:"server"`
 | 
						|
	Recipe       string `json:"recipe"`
 | 
						|
	AppName      string `json:"appName"`
 | 
						|
	Domain       string `json:"domain"`
 | 
						|
	Status       string `json:"status"`
 | 
						|
	Chaos        string `json:"chaos"`
 | 
						|
	ChaosVersion string `json:"chaosVersion"`
 | 
						|
	AutoUpdate   string `json:"autoUpdate"`
 | 
						|
	Version      string `json:"version"`
 | 
						|
	Upgrade      string `json:"upgrade"`
 | 
						|
}
 | 
						|
 | 
						|
type serverStatus struct {
 | 
						|
	Apps             []appStatus `json:"apps"`
 | 
						|
	AppCount         int         `json:"appCount"`
 | 
						|
	VersionCount     int         `json:"versionCount"`
 | 
						|
	UnversionedCount int         `json:"unversionedCount"`
 | 
						|
	LatestCount      int         `json:"latestCount"`
 | 
						|
	UpgradeCount     int         `json:"upgradeCount"`
 | 
						|
}
 | 
						|
 | 
						|
// translators: `abra app list` aliases. use a comma separated list of aliases with
 | 
						|
// no spaces in between
 | 
						|
var appListAliases = i18n.G("ls")
 | 
						|
 | 
						|
var AppListCommand = &cobra.Command{
 | 
						|
	// translators: `app list` command
 | 
						|
	Use:     i18n.G("list [flags]"),
 | 
						|
	Aliases: strings.Split(appListAliases, ","),
 | 
						|
	// translators: Short description for `app list` command
 | 
						|
	Short: i18n.G("List all managed apps"),
 | 
						|
	Long: i18n.G(`Generate a report of all managed apps.
 | 
						|
 | 
						|
Use "--status/-S" flag to query all servers for the live deployment status.`),
 | 
						|
	Example: i18n.G(`  # list apps of all servers without live status
 | 
						|
  abra app ls
 | 
						|
 | 
						|
  # list apps of a specific server with live status
 | 
						|
  abra app ls -s 1312.net -S
 | 
						|
 | 
						|
  # list apps of all servers which match a specific recipe
 | 
						|
  abra app ls -r gitea`),
 | 
						|
	Args: cobra.NoArgs,
 | 
						|
	Run: func(cmd *cobra.Command, args []string) {
 | 
						|
		appFiles, err := appPkg.LoadAppFiles(listAppServer)
 | 
						|
		if err != nil {
 | 
						|
			log.Fatal(err)
 | 
						|
		}
 | 
						|
 | 
						|
		apps, err := appPkg.GetApps(appFiles, recipeFilter)
 | 
						|
		if err != nil {
 | 
						|
			log.Fatal(err)
 | 
						|
		}
 | 
						|
 | 
						|
		sort.Sort(appPkg.ByServerAndRecipe(apps))
 | 
						|
 | 
						|
		statuses := make(map[string]map[string]string)
 | 
						|
		if status {
 | 
						|
			alreadySeen := make(map[string]bool)
 | 
						|
			for _, app := range apps {
 | 
						|
				if _, ok := alreadySeen[app.Server]; !ok {
 | 
						|
					alreadySeen[app.Server] = true
 | 
						|
				}
 | 
						|
			}
 | 
						|
 | 
						|
			statuses, err = appPkg.GetAppStatuses(apps, internal.MachineReadable)
 | 
						|
			if err != nil {
 | 
						|
				log.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 recipeFilter == "" {
 | 
						|
					// count server, no filtering
 | 
						|
					totalServersCount++
 | 
						|
				}
 | 
						|
			}
 | 
						|
 | 
						|
			if app.Recipe.Name == recipeFilter || recipeFilter == "" {
 | 
						|
				if recipeFilter != "" {
 | 
						|
					// only count server if matches filter
 | 
						|
					totalServersCount++
 | 
						|
				}
 | 
						|
 | 
						|
				appStats := appStatus{}
 | 
						|
				stats.AppCount++
 | 
						|
				totalAppsCount++
 | 
						|
 | 
						|
				if status {
 | 
						|
					status := i18n.G("unknown")
 | 
						|
					version := i18n.G("unknown")
 | 
						|
					chaos := i18n.G("unknown")
 | 
						|
					chaosVersion := i18n.G("unknown")
 | 
						|
					autoUpdate := i18n.G("unknown")
 | 
						|
					if statusMeta, ok := statuses[app.StackName()]; ok {
 | 
						|
						if currentVersion, exists := statusMeta["version"]; exists {
 | 
						|
							if currentVersion != "" {
 | 
						|
								version = currentVersion
 | 
						|
							}
 | 
						|
						}
 | 
						|
						if chaosDeploy, exists := statusMeta["chaos"]; exists {
 | 
						|
							chaos = chaosDeploy
 | 
						|
						}
 | 
						|
						if chaosDeployVersion, exists := statusMeta["chaosVersion"]; exists {
 | 
						|
							chaosVersion = chaosDeployVersion
 | 
						|
						}
 | 
						|
						if autoUpdateState, exists := statusMeta["autoUpdate"]; exists {
 | 
						|
							autoUpdate = autoUpdateState
 | 
						|
						}
 | 
						|
						if statusMeta["status"] != "" {
 | 
						|
							status = statusMeta["status"]
 | 
						|
						}
 | 
						|
						stats.VersionCount++
 | 
						|
					} else {
 | 
						|
						stats.UnversionedCount++
 | 
						|
					}
 | 
						|
 | 
						|
					appStats.Status = status
 | 
						|
					appStats.Chaos = chaos
 | 
						|
					appStats.ChaosVersion = chaosVersion
 | 
						|
					appStats.Version = version
 | 
						|
					appStats.AutoUpdate = autoUpdate
 | 
						|
 | 
						|
					var newUpdates []string
 | 
						|
					if version != "unknown" && chaos == "false" {
 | 
						|
						if err := app.Recipe.EnsureExists(); err != nil {
 | 
						|
							log.Fatal(i18n.G("unable to clone %s: %s", app.Name, err))
 | 
						|
						}
 | 
						|
 | 
						|
						updates, err := app.Recipe.Tags()
 | 
						|
						if err != nil {
 | 
						|
							log.Fatal(i18n.G("unable to retrieve tags for %s: %s", app.Name, err))
 | 
						|
						}
 | 
						|
 | 
						|
						parsedVersion, err := tagcmp.Parse(version)
 | 
						|
						if err != nil {
 | 
						|
							log.Fatal(err)
 | 
						|
						}
 | 
						|
 | 
						|
						for _, update := range updates {
 | 
						|
							parsedUpdate, err := tagcmp.Parse(update)
 | 
						|
							if err != nil {
 | 
						|
								log.Fatal(err)
 | 
						|
							}
 | 
						|
 | 
						|
							if update != version && parsedUpdate.IsGreaterThan(parsedVersion) {
 | 
						|
								newUpdates = append(newUpdates, update)
 | 
						|
							}
 | 
						|
						}
 | 
						|
					}
 | 
						|
 | 
						|
					if len(newUpdates) == 0 {
 | 
						|
						if version == "unknown" {
 | 
						|
							appStats.Upgrade = i18n.G("unknown")
 | 
						|
						} else {
 | 
						|
							appStats.Upgrade = i18n.G("latest")
 | 
						|
							stats.LatestCount++
 | 
						|
						}
 | 
						|
					} else {
 | 
						|
						newUpdates = internal.SortVersionsDesc(newUpdates)
 | 
						|
						appStats.Upgrade = strings.Join(newUpdates, "\n")
 | 
						|
						stats.UpgradeCount++
 | 
						|
					}
 | 
						|
				}
 | 
						|
 | 
						|
				appStats.Server = app.Server
 | 
						|
				appStats.Recipe = app.Recipe.Name
 | 
						|
				appStats.AppName = app.Name
 | 
						|
				appStats.Domain = app.Domain
 | 
						|
 | 
						|
				stats.Apps = append(stats.Apps, appStats)
 | 
						|
			}
 | 
						|
			allStats[app.Server] = stats
 | 
						|
		}
 | 
						|
 | 
						|
		if internal.MachineReadable {
 | 
						|
			jsonstring, err := json.Marshal(allStats)
 | 
						|
			if err != nil {
 | 
						|
				log.Fatal(err)
 | 
						|
			} else {
 | 
						|
				fmt.Println(string(jsonstring))
 | 
						|
			}
 | 
						|
 | 
						|
			return
 | 
						|
		}
 | 
						|
 | 
						|
		alreadySeen := make(map[string]bool)
 | 
						|
		for _, app := range apps {
 | 
						|
			if _, ok := alreadySeen[app.Server]; ok {
 | 
						|
				continue
 | 
						|
			}
 | 
						|
 | 
						|
			serverStat := allStats[app.Server]
 | 
						|
 | 
						|
			headers := []string{i18n.G("RECIPE"), i18n.G("DOMAIN"), i18n.G("SERVER")}
 | 
						|
			if status {
 | 
						|
				headers = append(headers, []string{
 | 
						|
					i18n.G("STATUS"),
 | 
						|
					i18n.G("CHAOS"),
 | 
						|
					i18n.G("VERSION"),
 | 
						|
					i18n.G("UPGRADE"),
 | 
						|
					i18n.G("AUTOUPDATE"),
 | 
						|
				}...,
 | 
						|
				)
 | 
						|
			}
 | 
						|
 | 
						|
			table, err := formatter.CreateTable()
 | 
						|
			if err != nil {
 | 
						|
				log.Fatal(err)
 | 
						|
			}
 | 
						|
 | 
						|
			table.Headers(headers...)
 | 
						|
 | 
						|
			var rows [][]string
 | 
						|
			for _, appStat := range serverStat.Apps {
 | 
						|
				row := []string{appStat.Recipe, appStat.Domain, appStat.Server}
 | 
						|
				if status {
 | 
						|
					chaosStatus := appStat.Chaos
 | 
						|
					if chaosStatus != "unknown" {
 | 
						|
						chaosEnabled, err := strconv.ParseBool(chaosStatus)
 | 
						|
						if err != nil {
 | 
						|
							log.Fatal(err)
 | 
						|
						}
 | 
						|
						if chaosEnabled && appStat.ChaosVersion != "unknown" {
 | 
						|
							chaosStatus = appStat.ChaosVersion
 | 
						|
						}
 | 
						|
					}
 | 
						|
 | 
						|
					row = append(row, []string{
 | 
						|
						appStat.Status,
 | 
						|
						chaosStatus,
 | 
						|
						appStat.Version,
 | 
						|
						appStat.Upgrade,
 | 
						|
						appStat.AutoUpdate}...,
 | 
						|
					)
 | 
						|
				}
 | 
						|
 | 
						|
				rows = append(rows, row)
 | 
						|
			}
 | 
						|
 | 
						|
			table.Rows(rows...)
 | 
						|
 | 
						|
			if len(rows) > 0 {
 | 
						|
				if err := formatter.PrintTable(table); err != nil {
 | 
						|
					log.Fatal(err)
 | 
						|
				}
 | 
						|
 | 
						|
				if len(allStats) > 1 && len(rows) > 0 {
 | 
						|
					fmt.Println() // newline separator for multiple servers
 | 
						|
				}
 | 
						|
			}
 | 
						|
 | 
						|
			alreadySeen[app.Server] = true
 | 
						|
		}
 | 
						|
	},
 | 
						|
}
 | 
						|
 | 
						|
var (
 | 
						|
	status        bool
 | 
						|
	recipeFilter  string
 | 
						|
	listAppServer string
 | 
						|
)
 | 
						|
 | 
						|
func init() {
 | 
						|
	AppListCommand.Flags().BoolVarP(
 | 
						|
		&status,
 | 
						|
		i18n.G("status"),
 | 
						|
		i18n.G("S"),
 | 
						|
		false,
 | 
						|
		i18n.G("show app deployment status"),
 | 
						|
	)
 | 
						|
 | 
						|
	AppListCommand.Flags().StringVarP(
 | 
						|
		&recipeFilter,
 | 
						|
		i18n.G("recipe"),
 | 
						|
		i18n.G("r"),
 | 
						|
		"",
 | 
						|
		i18n.G("show apps of a specific recipe"),
 | 
						|
	)
 | 
						|
 | 
						|
	AppListCommand.RegisterFlagCompletionFunc(
 | 
						|
		i18n.G("recipe"),
 | 
						|
		func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
 | 
						|
			return autocomplete.RecipeNameComplete()
 | 
						|
		},
 | 
						|
	)
 | 
						|
 | 
						|
	AppListCommand.Flags().BoolVarP(
 | 
						|
		&internal.MachineReadable,
 | 
						|
		i18n.G("machine"),
 | 
						|
		i18n.G("m"),
 | 
						|
		false,
 | 
						|
		i18n.G("print machine-readable output"),
 | 
						|
	)
 | 
						|
 | 
						|
	AppListCommand.Flags().StringVarP(
 | 
						|
		&listAppServer,
 | 
						|
		i18n.G("server"),
 | 
						|
		i18n.G("s"),
 | 
						|
		"",
 | 
						|
		i18n.G("show apps of a specific server"),
 | 
						|
	)
 | 
						|
 | 
						|
	AppListCommand.RegisterFlagCompletionFunc(
 | 
						|
		i18n.G("server"),
 | 
						|
		func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
 | 
						|
			return autocomplete.ServerNameComplete()
 | 
						|
		},
 | 
						|
	)
 | 
						|
}
 |