From 8651e22441448dbb38d3ccbc385f22b64100e47d Mon Sep 17 00:00:00 2001 From: decentral1se Date: Sun, 29 Aug 2021 14:13:35 +0200 Subject: [PATCH] feat: implement app logs command --- TODO.md | 2 +- cli/app/logs.go | 113 +++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 113 insertions(+), 2 deletions(-) diff --git a/TODO.md b/TODO.md index b063927b..4754214e 100644 --- a/TODO.md +++ b/TODO.md @@ -20,7 +20,7 @@ - [x] `version` - [x] `config` - [x] `cp` - - [ ] `logs` + - [x] `logs` - [x] `ps` - [ ] `restore` - [x] `rm` diff --git a/cli/app/logs.go b/cli/app/logs.go index 8ca0909c..7b862840 100644 --- a/cli/app/logs.go +++ b/cli/app/logs.go @@ -1,8 +1,119 @@ package app -import "github.com/urfave/cli/v2" +import ( + "context" + "errors" + "fmt" + "io" + "os" + "sync" + + "coopcloud.tech/abra/cli/internal" + "coopcloud.tech/abra/client" + "coopcloud.tech/abra/config" + "github.com/docker/docker/api/types" + "github.com/docker/docker/api/types/filters" + dockerClient "github.com/docker/docker/client" + "github.com/sirupsen/logrus" + "github.com/urfave/cli/v2" +) + +// stackLogs lists logs for all stack services +func stackLogs(stackName string, client *dockerClient.Client) { + ctx := context.Background() + filters := filters.NewArgs() + filters.Add("name", stackName) + serviceOpts := types.ServiceListOptions{Filters: filters} + services, err := client.ServiceList(ctx, serviceOpts) + if err != nil { + logrus.Fatal(err) + } + + var wg sync.WaitGroup + for _, service := range services { + wg.Add(1) + go func(s string) { + logOpts := types.ContainerLogsOptions{ + Details: true, + Follow: true, + ShowStderr: true, + ShowStdout: true, + Tail: "20", + Timestamps: true, + } + logs, err := client.ServiceLogs(ctx, s, logOpts) + defer logs.Close() + + _, err = io.Copy(os.Stdout, logs) + if err != nil && err != io.EOF { + logrus.Fatal(err) + } + }(service.ID) + } + wg.Wait() + os.Exit(0) +} var appLogsCommand = &cli.Command{ Name: "logs", ArgsUsage: "[]", + Usage: "tail app logs", + Action: func(c *cli.Context) error { + appName := c.Args().First() + if appName == "" { + internal.ShowSubcommandHelpAndError(c, errors.New("no app name provided")) + } + + appFiles, err := config.LoadAppFiles("") + if err != nil { + logrus.Fatal(err) + } + + appEnv, err := config.GetApp(appFiles, appName) + if err != nil { + logrus.Fatal(err) + } + + ctx := context.Background() + host := appFiles[appName].Server + cl, err := client.NewClientWithContext(host) + if err != nil { + logrus.Fatal(err) + } + + serviceName := c.Args().Get(1) + if serviceName == "" { + stackLogs(appEnv.StackName(), cl) + } + + service := fmt.Sprintf("%s_%s", appEnv.StackName(), serviceName) + filters := filters.NewArgs() + filters.Add("name", service) + serviceOpts := types.ServiceListOptions{Filters: filters} + services, err := cl.ServiceList(ctx, serviceOpts) + if err != nil { + logrus.Fatal(err) + } + if len(services) != 1 { + logrus.Fatalf("expected 1 service but got %v", len(services)) + } + + logOpts := types.ContainerLogsOptions{ + Details: true, + Follow: true, + ShowStderr: true, + ShowStdout: true, + Tail: "20", + Timestamps: true, + } + logs, err := cl.ServiceLogs(ctx, services[0].ID, logOpts) + defer logs.Close() + + _, err = io.Copy(os.Stdout, logs) + if err != nil && err != io.EOF { + logrus.Fatal(err) + } + + return nil + }, }