feat: abra app logs shows task errors
continuous-integration/drone/pr Build is passing
Details
continuous-integration/drone/pr Build is passing
Details
This commit is contained in:
parent
af8cd1f67a
commit
e40d99bfe2
115
cli/app/logs.go
115
cli/app/logs.go
|
@ -2,7 +2,6 @@ package app
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"sync"
|
"sync"
|
||||||
|
@ -12,10 +11,10 @@ import (
|
||||||
"coopcloud.tech/abra/pkg/client"
|
"coopcloud.tech/abra/pkg/client"
|
||||||
"coopcloud.tech/abra/pkg/config"
|
"coopcloud.tech/abra/pkg/config"
|
||||||
"coopcloud.tech/abra/pkg/recipe"
|
"coopcloud.tech/abra/pkg/recipe"
|
||||||
"coopcloud.tech/abra/pkg/service"
|
|
||||||
"coopcloud.tech/abra/pkg/upstream/stack"
|
"coopcloud.tech/abra/pkg/upstream/stack"
|
||||||
"github.com/docker/docker/api/types"
|
"github.com/docker/docker/api/types"
|
||||||
"github.com/docker/docker/api/types/filters"
|
"github.com/docker/docker/api/types/filters"
|
||||||
|
"github.com/docker/docker/api/types/swarm"
|
||||||
dockerClient "github.com/docker/docker/client"
|
dockerClient "github.com/docker/docker/client"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
"github.com/urfave/cli"
|
"github.com/urfave/cli"
|
||||||
|
@ -32,45 +31,6 @@ var logOpts = types.ContainerLogsOptions{
|
||||||
Details: false,
|
Details: false,
|
||||||
}
|
}
|
||||||
|
|
||||||
// stackLogs lists logs for all stack services
|
|
||||||
func stackLogs(c *cli.Context, app config.App, client *dockerClient.Client) {
|
|
||||||
filters, err := app.Filters(true, false)
|
|
||||||
if err != nil {
|
|
||||||
logrus.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
serviceOpts := types.ServiceListOptions{Filters: filters}
|
|
||||||
services, err := client.ServiceList(context.Background(), serviceOpts)
|
|
||||||
if err != nil {
|
|
||||||
logrus.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
var wg sync.WaitGroup
|
|
||||||
for _, service := range services {
|
|
||||||
wg.Add(1)
|
|
||||||
go func(s string) {
|
|
||||||
if internal.StdErrOnly {
|
|
||||||
logOpts.ShowStdout = false
|
|
||||||
}
|
|
||||||
|
|
||||||
logs, err := client.ServiceLogs(context.Background(), s, logOpts)
|
|
||||||
if err != nil {
|
|
||||||
logrus.Fatal(err)
|
|
||||||
}
|
|
||||||
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{
|
var appLogsCommand = cli.Command{
|
||||||
Name: "logs",
|
Name: "logs",
|
||||||
Aliases: []string{"l"},
|
Aliases: []string{"l"},
|
||||||
|
@ -108,43 +68,68 @@ var appLogsCommand = cli.Command{
|
||||||
logOpts.Since = internal.SinceLogs
|
logOpts.Since = internal.SinceLogs
|
||||||
|
|
||||||
serviceName := c.Args().Get(1)
|
serviceName := c.Args().Get(1)
|
||||||
if serviceName == "" {
|
serviceNames := []string{}
|
||||||
logrus.Debugf("tailing logs for all %s services", app.Recipe)
|
if serviceName != "" {
|
||||||
stackLogs(c, app, cl)
|
serviceNames = []string{serviceName}
|
||||||
} else {
|
}
|
||||||
logrus.Debugf("tailing logs for %s", serviceName)
|
err = tailLogs(cl, app, serviceNames)
|
||||||
if err := tailServiceLogs(c, cl, app, serviceName); err != nil {
|
if err != nil {
|
||||||
logrus.Fatal(err)
|
logrus.Fatal(err)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
func tailServiceLogs(c *cli.Context, cl *dockerClient.Client, app config.App, serviceName string) error {
|
// tailLogs lists logs for the given app with optional service names to be filtered on
|
||||||
filters := filters.NewArgs()
|
func tailLogs(cl *dockerClient.Client, app config.App, serviceNames []string) error {
|
||||||
filters.Add("name", fmt.Sprintf("%s_%s", app.StackName(), serviceName))
|
f, err := app.Filters(true, false, serviceNames...)
|
||||||
|
|
||||||
chosenService, err := service.GetService(context.Background(), cl, filters, internal.NoInput)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.Fatal(err)
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if internal.StdErrOnly {
|
services, err := cl.ServiceList(context.Background(), types.ServiceListOptions{Filters: f})
|
||||||
logOpts.ShowStdout = false
|
|
||||||
}
|
|
||||||
|
|
||||||
logs, err := cl.ServiceLogs(context.Background(), chosenService.ID, logOpts)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.Fatal(err)
|
return err
|
||||||
}
|
}
|
||||||
defer logs.Close()
|
|
||||||
|
|
||||||
_, err = io.Copy(os.Stdout, logs)
|
var wg sync.WaitGroup
|
||||||
if err != nil && err != io.EOF {
|
for _, service := range services {
|
||||||
logrus.Fatal(err)
|
wg.Add(1)
|
||||||
|
go func(serviceID, serviceName string) {
|
||||||
|
filters := filters.NewArgs()
|
||||||
|
filters.Add("name", serviceName)
|
||||||
|
tasks, err := cl.TaskList(context.Background(), types.TaskListOptions{Filters: f})
|
||||||
|
if err != nil {
|
||||||
|
logrus.Fatal(err)
|
||||||
|
}
|
||||||
|
if len(tasks) > 0 {
|
||||||
|
lastTask := tasks[len(tasks)-1].Status
|
||||||
|
if lastTask.State != swarm.TaskStateRunning {
|
||||||
|
for _, task := range tasks {
|
||||||
|
logrus.Errorf("Service %s: State %s: %s", serviceName, task.Status.State, task.Status.Err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if internal.StdErrOnly {
|
||||||
|
logOpts.ShowStdout = false
|
||||||
|
}
|
||||||
|
|
||||||
|
logs, err := cl.ServiceLogs(context.Background(), serviceID, logOpts)
|
||||||
|
if err != nil {
|
||||||
|
logrus.Fatal(err)
|
||||||
|
}
|
||||||
|
defer logs.Close()
|
||||||
|
|
||||||
|
_, err = io.Copy(os.Stdout, logs)
|
||||||
|
if err != nil && err != io.EOF {
|
||||||
|
logrus.Fatal(err)
|
||||||
|
}
|
||||||
|
}(service.ID, service.Spec.Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
wg.Wait()
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -83,8 +83,14 @@ func StackName(appName string) string {
|
||||||
// to implement their own validation that the right secrets are matched. In
|
// to implement their own validation that the right secrets are matched. In
|
||||||
// order to handle these cases, we provide the `appendServiceNames` /
|
// order to handle these cases, we provide the `appendServiceNames` /
|
||||||
// `exactMatch` modifiers.
|
// `exactMatch` modifiers.
|
||||||
func (a App) Filters(appendServiceNames, exactMatch bool) (filters.Args, error) {
|
func (a App) Filters(appendServiceNames, exactMatch bool, services ...string) (filters.Args, error) {
|
||||||
filters := filters.NewArgs()
|
filters := filters.NewArgs()
|
||||||
|
if len(services) > 0 {
|
||||||
|
for _, serviceName := range services {
|
||||||
|
filters.Add("name", fmtFilter(appendServiceNames, exactMatch, a.StackName(), serviceName))
|
||||||
|
}
|
||||||
|
return filters, nil
|
||||||
|
}
|
||||||
|
|
||||||
composeFiles, err := GetComposeFiles(a.Recipe, a.Env)
|
composeFiles, err := GetComposeFiles(a.Recipe, a.Env)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -98,28 +104,25 @@ func (a App) Filters(appendServiceNames, exactMatch bool) (filters.Args, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, service := range compose.Services {
|
for _, service := range compose.Services {
|
||||||
var filter string
|
filters.Add("name", fmtFilter(appendServiceNames, exactMatch, a.StackName(), service.Name))
|
||||||
|
|
||||||
if appendServiceNames {
|
|
||||||
if exactMatch {
|
|
||||||
filter = fmt.Sprintf("^%s_%s", a.StackName(), service.Name)
|
|
||||||
} else {
|
|
||||||
filter = fmt.Sprintf("%s_%s", a.StackName(), service.Name)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if exactMatch {
|
|
||||||
filter = fmt.Sprintf("^%s", a.StackName())
|
|
||||||
} else {
|
|
||||||
filter = fmt.Sprintf("%s", a.StackName())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
filters.Add("name", filter)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return filters, nil
|
return filters, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func fmtFilter(appendMode bool, exact bool, stack string, service string) string {
|
||||||
|
if appendMode {
|
||||||
|
if exact {
|
||||||
|
return fmt.Sprintf("^%s_%s", stack, service)
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("%s_%s", stack, service)
|
||||||
|
}
|
||||||
|
if exact {
|
||||||
|
return fmt.Sprintf("^%s", stack)
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("%s", stack)
|
||||||
|
}
|
||||||
|
|
||||||
// ByServer sort a slice of Apps
|
// ByServer sort a slice of Apps
|
||||||
type ByServer []App
|
type ByServer []App
|
||||||
|
|
||||||
|
@ -340,7 +343,7 @@ func TemplateAppEnvSample(recipeName, appName, server, domain string) error {
|
||||||
return fmt.Errorf("%s already exists?", appEnvPath)
|
return fmt.Errorf("%s already exists?", appEnvPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = ioutil.WriteFile(appEnvPath, envSample, 0664)
|
err = ioutil.WriteFile(appEnvPath, envSample, 0o664)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -602,7 +605,7 @@ func GetLabel(compose *composetypes.Config, stackName string, label string) stri
|
||||||
|
|
||||||
// GetTimeoutFromLabel reads the timeout value from docker label "coop-cloud.${STACK_NAME}.TIMEOUT" and returns 50 as default value
|
// GetTimeoutFromLabel reads the timeout value from docker label "coop-cloud.${STACK_NAME}.TIMEOUT" and returns 50 as default value
|
||||||
func GetTimeoutFromLabel(compose *composetypes.Config, stackName string) (int, error) {
|
func GetTimeoutFromLabel(compose *composetypes.Config, stackName string) (int, error) {
|
||||||
var timeout = 50 // Default Timeout
|
timeout := 50 // Default Timeout
|
||||||
var err error = nil
|
var err error = nil
|
||||||
if timeoutLabel := GetLabel(compose, stackName, "timeout"); timeoutLabel != "" {
|
if timeoutLabel := GetLabel(compose, stackName, "timeout"); timeoutLabel != "" {
|
||||||
logrus.Debugf("timeout label: %s", timeoutLabel)
|
logrus.Debugf("timeout label: %s", timeoutLabel)
|
||||||
|
|
Loading…
Reference in New Issue