From 95d385c420dc89aa0a3ede0e948824e32bf453bc Mon Sep 17 00:00:00 2001 From: cellarspoon Date: Fri, 31 Dec 2021 12:49:31 +0100 Subject: [PATCH] fix: GetService & handling missing services --- cli/app/logs.go | 16 ++++----- pkg/container/container.go | 4 +-- pkg/service/service.go | 69 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 77 insertions(+), 12 deletions(-) create mode 100644 pkg/service/service.go diff --git a/cli/app/logs.go b/cli/app/logs.go index 0c962a2e..b8f5fed7 100644 --- a/cli/app/logs.go +++ b/cli/app/logs.go @@ -10,6 +10,7 @@ import ( "coopcloud.tech/abra/pkg/autocomplete" "coopcloud.tech/abra/pkg/client" "coopcloud.tech/abra/pkg/config" + "coopcloud.tech/abra/pkg/service" "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/filters" dockerClient "github.com/docker/docker/client" @@ -48,7 +49,6 @@ func stackLogs(c *cli.Context, stackName string, client *dockerClient.Client) { if err != nil { logrus.Fatal(err) } - // defer after err check as any err returns a nil io.ReadCloser defer logs.Close() _, err = io.Copy(os.Stdout, logs) @@ -57,7 +57,9 @@ func stackLogs(c *cli.Context, stackName string, client *dockerClient.Client) { } }(service.ID) } + wg.Wait() + os.Exit(0) } @@ -94,27 +96,21 @@ var appLogsCommand = &cli.Command{ } func tailServiceLogs(c *cli.Context, cl *dockerClient.Client, app config.App, serviceName string) error { - service := fmt.Sprintf("%s_%s", app.StackName(), serviceName) filters := filters.NewArgs() - filters.Add("name", service) - serviceOpts := types.ServiceListOptions{Filters: filters} - services, err := cl.ServiceList(c.Context, serviceOpts) + filters.Add("name", fmt.Sprintf("%s_%s", app.StackName(), serviceName)) + chosenService, err := service.GetService(c.Context, cl, filters, internal.NoInput) if err != nil { logrus.Fatal(err) } - if len(services) != 1 { - logrus.Fatalf("expected 1 service but got %v", len(services)) - } if internal.StdErrOnly { logOpts.ShowStdout = false } - logs, err := cl.ServiceLogs(c.Context, services[0].ID, logOpts) + logs, err := cl.ServiceLogs(c.Context, chosenService.ID, logOpts) if err != nil { logrus.Fatal(err) } - // defer after err check as any err returns a nil io.ReadCloser defer logs.Close() _, err = io.Copy(os.Stdout, logs) diff --git a/pkg/container/container.go b/pkg/container/container.go index 37e25938..0f8cb738 100644 --- a/pkg/container/container.go +++ b/pkg/container/container.go @@ -14,8 +14,8 @@ import ( ) // GetContainer retrieves a container. If prompt is true and the retrievd count -// of containers does not match expectedN, then a prompt is presented to let -// the user choose. +// of containers does not match 1, then a prompt is presented to let the user +// choose. A count of 0 is handled gracefully. func GetContainer(c context.Context, cl *client.Client, filters filters.Args, prompt bool) (types.Container, error) { containerOpts := types.ContainerListOptions{Filters: filters} containers, err := cl.ContainerList(c, containerOpts) diff --git a/pkg/service/service.go b/pkg/service/service.go new file mode 100644 index 00000000..b957e772 --- /dev/null +++ b/pkg/service/service.go @@ -0,0 +1,69 @@ +package service + +import ( + "context" + "fmt" + "strings" + + "coopcloud.tech/abra/pkg/formatter" + "github.com/AlecAivazis/survey/v2" + "github.com/docker/docker/api/types" + "github.com/docker/docker/api/types/filters" + "github.com/docker/docker/api/types/swarm" + "github.com/docker/docker/client" + "github.com/sirupsen/logrus" +) + +// GetService retrieves a service container. If prompt is true and the retrievd +// count of service containers does not match 1, then a prompt is presented to +// let the user choose. A count of 0 is handled gracefully. +func GetService(c context.Context, cl *client.Client, filters filters.Args, prompt bool) (swarm.Service, error) { + serviceOpts := types.ServiceListOptions{Filters: filters} + services, err := cl.ServiceList(c, serviceOpts) + if err != nil { + return swarm.Service{}, err + } + + if len(services) == 0 { + filter := filters.Get("name")[0] + return swarm.Service{}, fmt.Errorf("no services matching the %v filter found?", filter) + } + + if len(services) != 1 { + var servicesRaw []string + for _, service := range services { + serviceName := service.Spec.Name + created := formatter.HumanDuration(service.CreatedAt.Unix()) + servicesRaw = append(servicesRaw, fmt.Sprintf("%s (created %v)", serviceName, created)) + } + + if !prompt { + err := fmt.Errorf("expected 1 service but found %v: %s", len(services), strings.Join(servicesRaw, " ")) + return swarm.Service{}, err + } + + logrus.Warnf("ambiguous service list received, prompting for input") + + var response string + prompt := &survey.Select{ + Message: "which service are you looking for?", + Options: servicesRaw, + } + + if err := survey.AskOne(prompt, &response); err != nil { + return swarm.Service{}, err + } + + chosenService := strings.TrimSpace(strings.Split(response, " ")[0]) + for _, service := range services { + serviceName := strings.ToLower(service.Spec.Name) + if serviceName == chosenService { + return service, nil + } + } + + logrus.Panic("failed to match chosen service") + } + + return services[0], nil +}