From ef1591d596ee60aee36dba63814d017e4edc650a Mon Sep 17 00:00:00 2001 From: decentral1se Date: Tue, 27 Jul 2021 12:52:09 +0200 Subject: [PATCH] WIP: app status listing using concurrency This being my first time using goroutines, it is pretty messy but the idea has been shown to be workable! We can concurrently look up multiple contexts for a much faster response time especially when using multiple servers. Remaining TODOs are: - [ ] Get proper status reporting (deployed/inactive/unknown) - [ ] Error handling (especially when missing contexts) - [ ] Refactor and tidy --- cli/app.go | 26 ++++++++++++++++--------- config/env.go | 53 +++++++++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 66 insertions(+), 13 deletions(-) diff --git a/cli/app.go b/cli/app.go index c1c9fc7b5..bae8e45b3 100644 --- a/cli/app.go +++ b/cli/app.go @@ -59,27 +59,35 @@ var appListCommand = &cli.Command{ logrus.Fatal(err) } + apps, err := config.GetApps(appFiles) + sort.Sort(config.ByServerAndType(apps)) + + statuses := map[string]string{} tableCol := []string{"Server", "Type", "Domain"} if Status { - tableCol = []string{"Server", "Type", "Domain", "Status"} + tableCol = append(tableCol, "Status") + statuses, err = config.GetAppStatuses(appFiles) + if err != nil { + logrus.Fatal(err) + } } + table := createTable(tableCol) table.SetAutoMergeCellsByColumnIndex([]int{0}) - apps, err := config.GetApps(appFiles) - sort.Sort(config.ByServerAndType(apps)) for _, app := range apps { var tableRow []string if app.Type == Type || Type == "" { // If type flag is set, check for it, if not, Type == "" tableRow = []string{app.File.Server, app.Type, app.Domain} - } - if Status { - status, err := app.GetStatus() - if err != nil { - logrus.Fatal(err) + if Status { + stackName := strings.ReplaceAll(app.Name, ".", "_") + if status, ok := statuses[stackName]; ok { + tableRow = append(tableRow, status) + } else { + tableRow = append(tableRow, "unknown") + } } - tableRow = []string{app.File.Server, app.Type, app.Domain, status} } table.Append(tableRow) } diff --git a/config/env.go b/config/env.go index 4e3dd47c0..da3fd0272 100644 --- a/config/env.go +++ b/config/env.go @@ -1,6 +1,7 @@ package config import ( + "context" "errors" "fmt" "io/fs" @@ -10,6 +11,10 @@ import ( "path/filepath" "strings" + "coopcloud.tech/abra/client" + "github.com/docker/docker/api/types" + "github.com/docker/docker/api/types/filters" + "github.com/docker/docker/api/types/swarm" "github.com/joho/godotenv" "github.com/sirupsen/logrus" ) @@ -24,10 +29,6 @@ var REPOS_BASE_URL = "https://git.coopcloud.tech/coop-cloud" type AppEnv = map[string]string type AppName = string -type AppStatus struct { - Deployed bool -} - type App struct { Name AppName Type string @@ -139,6 +140,50 @@ func GetApps(appFiles AppFiles) ([]App, error) { return apps, nil } +func GetAppStatuses(appFiles AppFiles) (map[string]string, error) { + statuses := map[string]string{} + + servers := make(map[string]struct{}) + for _, appFile := range appFiles { + if _, ok := servers[appFile.Server]; !ok { + servers[appFile.Server] = struct{}{} + } + } + + type status struct { + services []swarm.Service + err error + } + + ch := make(chan status, len(servers)) + for server, _ := range servers { + go func(s string) { + ctx := context.Background() + cl, err := client.NewClientWithContext(s) + if err != nil { + ch <- status{services: []swarm.Service{}, err: nil} + return + } + filter := filters.NewArgs() + filter.Add("label", "com.docker.stack.namespace") + services, _ := cl.ServiceList(ctx, types.ServiceListOptions{Filters: filter}) + ch <- status{services: services, err: nil} + }(server) + } + + for range servers { + status := <-ch + for _, service := range status.services { + name := service.Spec.Labels["com.docker.stack.namespace"] + if _, ok := statuses[name]; !ok { + statuses[name] = "deployed" + } + } + } + + return statuses, nil +} + // TODO: maybe better names than read and get func readAppFile(appFile AppFile, name AppName) (App, error) {