2021-12-21 23:48:00 +00:00
|
|
|
package app
|
|
|
|
|
|
|
|
import (
|
2022-01-18 13:13:20 +00:00
|
|
|
"context"
|
2021-12-24 00:40:39 +00:00
|
|
|
"strconv"
|
2021-12-23 20:45:59 +00:00
|
|
|
"strings"
|
2021-12-24 01:23:46 +00:00
|
|
|
"time"
|
2021-12-23 20:45:59 +00:00
|
|
|
|
|
|
|
"coopcloud.tech/abra/cli/internal"
|
2021-12-21 23:48:00 +00:00
|
|
|
"coopcloud.tech/abra/pkg/autocomplete"
|
2021-12-23 20:45:59 +00:00
|
|
|
"coopcloud.tech/abra/pkg/client"
|
2021-12-24 01:23:46 +00:00
|
|
|
"coopcloud.tech/abra/pkg/config"
|
|
|
|
"coopcloud.tech/abra/pkg/recipe"
|
2021-12-24 00:40:39 +00:00
|
|
|
stack "coopcloud.tech/abra/pkg/upstream/stack"
|
2021-12-23 20:45:59 +00:00
|
|
|
"github.com/docker/docker/api/types"
|
|
|
|
"github.com/docker/docker/api/types/filters"
|
2021-12-24 01:23:46 +00:00
|
|
|
dockerClient "github.com/docker/docker/client"
|
2021-12-23 20:45:59 +00:00
|
|
|
"github.com/sirupsen/logrus"
|
2022-01-18 13:13:20 +00:00
|
|
|
"github.com/urfave/cli"
|
2021-12-21 23:48:00 +00:00
|
|
|
)
|
|
|
|
|
2022-01-18 13:13:20 +00:00
|
|
|
var appErrorsCommand = cli.Command{
|
2021-12-21 23:48:00 +00:00
|
|
|
Name: "errors",
|
|
|
|
Usage: "List errors for a deployed app",
|
|
|
|
Description: `
|
2021-12-31 11:10:11 +00:00
|
|
|
This command lists errors for a deployed app.
|
|
|
|
|
|
|
|
This is a best-effort implementation and an attempt to gather a number of tips
|
|
|
|
& tricks for finding errors together into one convenient command. When an app
|
|
|
|
is failing to deploy or having issues, it could be a lot of things.
|
|
|
|
|
|
|
|
This command currently takes into account:
|
|
|
|
|
2022-01-03 17:41:02 +00:00
|
|
|
Is the service deployed?
|
2021-12-31 11:10:11 +00:00
|
|
|
Is the service killed by an OOM error?
|
2022-01-03 17:41:02 +00:00
|
|
|
Is the service reporting an error (like in "ps --no-trunc" output)
|
|
|
|
Is the service healthcheck failing? what are the healthcheck logs?
|
2021-12-31 11:10:11 +00:00
|
|
|
|
|
|
|
Got any more ideas? Please let us know:
|
|
|
|
|
|
|
|
https://git.coopcloud.tech/coop-cloud/organising/issues/new/choose
|
|
|
|
|
|
|
|
This command is best accompanied by "abra app logs <app>" which may reveal
|
|
|
|
further information which can help you debug the cause of an app failure via
|
|
|
|
the logs.
|
|
|
|
|
2021-12-21 23:48:00 +00:00
|
|
|
`,
|
2022-01-18 13:13:20 +00:00
|
|
|
Aliases: []string{"e"},
|
|
|
|
Flags: []cli.Flag{
|
|
|
|
internal.DebugFlag,
|
|
|
|
internal.NoInputFlag,
|
|
|
|
internal.WatchFlag,
|
|
|
|
},
|
|
|
|
Before: internal.SubCommandBefore,
|
2021-12-21 23:48:00 +00:00
|
|
|
BashComplete: autocomplete.AppNameComplete,
|
|
|
|
Action: func(c *cli.Context) error {
|
2021-12-23 20:45:59 +00:00
|
|
|
app := internal.ValidateApp(c)
|
|
|
|
|
|
|
|
cl, err := client.New(app.Server)
|
|
|
|
if err != nil {
|
|
|
|
logrus.Fatal(err)
|
|
|
|
}
|
|
|
|
|
2022-01-18 13:13:20 +00:00
|
|
|
isDeployed, _, err := stack.IsDeployed(context.Background(), cl, app.StackName())
|
2021-12-24 00:40:39 +00:00
|
|
|
if err != nil {
|
|
|
|
logrus.Fatal(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if !isDeployed {
|
|
|
|
logrus.Fatalf("%s is not deployed?", app.Name)
|
|
|
|
}
|
|
|
|
|
2021-12-24 01:23:46 +00:00
|
|
|
if !internal.Watch {
|
|
|
|
if err := checkErrors(c, cl, app); err != nil {
|
|
|
|
logrus.Fatal(err)
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
for {
|
|
|
|
if err := checkErrors(c, cl, app); err != nil {
|
|
|
|
logrus.Fatal(err)
|
|
|
|
}
|
|
|
|
time.Sleep(2 * time.Second)
|
|
|
|
}
|
2021-12-23 20:45:59 +00:00
|
|
|
|
2021-12-24 01:23:46 +00:00
|
|
|
return nil
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
func checkErrors(c *cli.Context, cl *dockerClient.Client, app config.App) error {
|
|
|
|
recipe, err := recipe.Get(app.Type)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, service := range recipe.Config.Services {
|
|
|
|
filters := filters.NewArgs()
|
|
|
|
filters.Add("name", service.Name)
|
2022-01-18 13:13:20 +00:00
|
|
|
containers, err := cl.ContainerList(context.Background(), types.ContainerListOptions{Filters: filters})
|
2021-12-23 20:45:59 +00:00
|
|
|
if err != nil {
|
2021-12-24 01:23:46 +00:00
|
|
|
return err
|
2021-12-23 20:45:59 +00:00
|
|
|
}
|
|
|
|
|
2021-12-24 01:23:46 +00:00
|
|
|
if len(containers) == 0 {
|
|
|
|
logrus.Warnf("%s is not up, something seems wrong", service.Name)
|
|
|
|
continue
|
|
|
|
}
|
2021-12-23 20:45:59 +00:00
|
|
|
|
2021-12-24 01:23:46 +00:00
|
|
|
container := containers[0]
|
2022-01-18 13:13:20 +00:00
|
|
|
containerState, err := cl.ContainerInspect(context.Background(), container.ID)
|
2021-12-24 01:23:46 +00:00
|
|
|
if err != nil {
|
|
|
|
logrus.Fatal(err)
|
|
|
|
}
|
2021-12-23 20:45:59 +00:00
|
|
|
|
2021-12-24 01:23:46 +00:00
|
|
|
if containerState.State.OOMKilled {
|
|
|
|
logrus.Warnf("%s has been killed due to an out of memory error", service.Name)
|
|
|
|
}
|
2021-12-23 20:45:59 +00:00
|
|
|
|
2021-12-24 01:23:46 +00:00
|
|
|
if containerState.State.Error != "" {
|
2021-12-31 11:05:40 +00:00
|
|
|
logrus.Warnf("%s reports this error: %s", service.Name, containerState.State.Error)
|
2021-12-24 01:23:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if containerState.State.Health != nil {
|
|
|
|
if containerState.State.Health.Status != "healthy" {
|
|
|
|
logrus.Warnf("%s healthcheck status is %s", service.Name, containerState.State.Health.Status)
|
|
|
|
logrus.Warnf("%s healthcheck has failed %s times", service.Name, strconv.Itoa(containerState.State.Health.FailingStreak))
|
2021-12-23 20:45:59 +00:00
|
|
|
for _, log := range containerState.State.Health.Log {
|
2021-12-24 01:23:46 +00:00
|
|
|
logrus.Warnf("%s healthcheck logs: %s", service.Name, strings.TrimSpace(log.Output))
|
2021-12-23 20:45:59 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2021-12-24 01:23:46 +00:00
|
|
|
}
|
2021-12-23 20:45:59 +00:00
|
|
|
|
2021-12-24 01:23:46 +00:00
|
|
|
return nil
|
2021-12-21 23:48:00 +00:00
|
|
|
}
|
2021-12-24 00:40:39 +00:00
|
|
|
|
|
|
|
func getServiceName(names []string) string {
|
|
|
|
containerName := strings.Join(names, " ")
|
|
|
|
trimmed := strings.TrimPrefix(containerName, "/")
|
|
|
|
return strings.Split(trimmed, ".")[0]
|
|
|
|
}
|