diff --git a/TODO.md b/TODO.md index 6885dbbf..2b113fba 100644 --- a/TODO.md +++ b/TODO.md @@ -12,7 +12,7 @@ Disclaimer!: List is WIP - [ ] `init` - [ ] `apps` - [ ] `abra app` - - [ ] `ls` (XXX: in progress (decentral1se)) + - [x] `ls` - [ ] `new` - [ ] `backup` - [ ] `deploy` diff --git a/cli/app.go b/cli/app.go index bae8e45b..116bec79 100644 --- a/cli/app.go +++ b/cli/app.go @@ -81,8 +81,7 @@ var appListCommand = &cli.Command{ // If type flag is set, check for it, if not, Type == "" tableRow = []string{app.File.Server, app.Type, app.Domain} if Status { - stackName := strings.ReplaceAll(app.Name, ".", "_") - if status, ok := statuses[stackName]; ok { + if status, ok := statuses[app.StackName()]; ok { tableRow = append(tableRow, status) } else { tableRow = append(tableRow, "unknown") diff --git a/config/env.go b/config/env.go index 37f1f85c..e2cf2bfe 100644 --- a/config/env.go +++ b/config/env.go @@ -19,6 +19,8 @@ import ( "github.com/sirupsen/logrus" ) +const dockerStackNamespace = "com.docker.stack.namespace" + var ABRA_DIR = os.ExpandEnv("$HOME/.abra") var ABRA_SERVER_FOLDER = path.Join(ABRA_DIR, "servers") var APPS_JSON = path.Join(ABRA_DIR, "apps.json") @@ -37,6 +39,10 @@ type App struct { File AppFile } +func (a App) StackName() string { + return strings.ReplaceAll(a.Name, ".", "_") +} + type ByServer []App func (a ByServer) Len() int { return len(a) } @@ -78,7 +84,19 @@ type AppFile struct { Server string } -type AppFiles = map[AppName]AppFile +type AppFiles map[AppName]AppFile + +func (a AppFiles) GetServers() []string { + var unique []string + servers := make(map[string]struct{}) + for _, appFile := range a { + if _, ok := servers[appFile.Server]; !ok { + servers[appFile.Server] = struct{}{} + unique = append(unique, appFile.Server) + } + } + return unique +} func LoadAppFiles(servers ...string) (AppFiles, error) { appFiles := make(AppFiles) @@ -137,40 +155,42 @@ func GetApps(appFiles AppFiles) ([]App, error) { } 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 } + servers := appFiles.GetServers() ch := make(chan status, len(servers)) - for server, _ := range 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} + if strings.Contains(err.Error(), "does not exist") { + // No local context found, bail out gracefully + ch <- status{services: []swarm.Service{}, err: nil} + return + } + ch <- status{services: []swarm.Service{}, err: err} return } + ctx := context.Background() filter := filters.NewArgs() - filter.Add("label", "com.docker.stack.namespace") - services, _ := cl.ServiceList(ctx, types.ServiceListOptions{Filters: filter}) + filter.Add("label", dockerStackNamespace) + services, err := cl.ServiceList(ctx, types.ServiceListOptions{Filters: filter}) + if err != nil { + ch <- status{services: []swarm.Service{}, err: err} + return + } ch <- status{services: services, err: nil} }(server) } + statuses := map[string]string{} for range servers { status := <-ch for _, service := range status.services { - name := service.Spec.Labels["com.docker.stack.namespace"] + name := service.Spec.Labels[dockerStackNamespace] if _, ok := statuses[name]; !ok { statuses[name] = "deployed" }