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
This commit is contained in:
decentral1se 2021-07-27 12:52:09 +02:00
parent 429c7e4e50
commit ef1591d596
No known key found for this signature in database
GPG Key ID: 5E2EF5A63E3718CC
2 changed files with 66 additions and 13 deletions

View File

@ -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)
}

View File

@ -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) {