Compare commits

...

2 Commits

5 changed files with 221 additions and 50 deletions

View File

@ -4,74 +4,54 @@ import (
"fmt"
"io"
"os"
"sync"
"coopcloud.tech/abra/cli/internal"
"coopcloud.tech/abra/pkg/client"
"coopcloud.tech/abra/pkg/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(c *cli.Context, stackName string, client *dockerClient.Client) {
filters := filters.NewArgs()
filters.Add("name", stackName)
serviceOpts := types.ServiceListOptions{Filters: filters}
services, err := client.ServiceList(c.Context, 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: false,
Follow: true,
ShowStderr: true,
ShowStdout: true,
Tail: "20",
Timestamps: true,
}
logs, err := client.ServiceLogs(c.Context, s, 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)
if err != nil && err != io.EOF {
logrus.Fatal(err)
}
}(service.ID)
}
wg.Wait()
os.Exit(0)
}
var appLogsCommand = &cli.Command{
Name: "logs",
Aliases: []string{"l"},
ArgsUsage: "[<service>]",
Usage: "Tail app logs",
Flags: []cli.Flag{
internal.StderrFlag,
internal.StdoutFlag,
internal.HealthcheckFlag,
},
Action: func(c *cli.Context) error {
app := internal.ValidateApp(c)
if !internal.Stderr && !internal.Stdout && !internal.Healthcheck {
internal.Stderr = true
internal.Stdout = true
internal.Healthcheck = true
}
logrus.Debugf("flags parsed. --stderr: %t, --stdout: %t, --healthcheck: %t", internal.Stderr, internal.Stdout, internal.Healthcheck)
cl, err := client.New(app.Server)
if err != nil {
logrus.Fatal(err)
}
logOpts := types.ContainerLogsOptions{
Details: false,
Follow: true,
ShowStderr: internal.Stderr,
ShowStdout: internal.Stdout,
Tail: "20",
Timestamps: true,
}
serviceName := c.Args().Get(1)
if serviceName == "" {
logrus.Debug("tailing logs for all app services")
stackLogs(c, app.StackName(), cl)
internal.StackLogs(c, app.StackName(), logOpts, cl)
}
logrus.Debugf("tailing logs for '%s'", serviceName)
@ -87,14 +67,6 @@ var appLogsCommand = &cli.Command{
logrus.Fatalf("expected 1 service but got %v", len(services))
}
logOpts := types.ContainerLogsOptions{
Details: false,
Follow: true,
ShowStderr: true,
ShowStdout: true,
Tail: "20",
Timestamps: true,
}
logs, err := cl.ServiceLogs(c.Context, services[0].ID, logOpts)
if err != nil {
logrus.Fatal(err)

68
cli/internal/logs.go Normal file
View File

@ -0,0 +1,68 @@
package internal
import (
"io"
"os"
"sync"
"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"
)
var Stderr bool
var StderrFlag = &cli.BoolFlag{
Name: "stderr",
Aliases: []string{"e"},
Value: false,
Destination: &Stderr,
}
var Stdout bool
var StdoutFlag = &cli.BoolFlag{
Name: "stdout",
Aliases: []string{"o"},
Value: false,
Destination: &Stdout,
}
var Healthcheck bool
var HealthcheckFlag = &cli.BoolFlag{
Name: "healthcheck",
Aliases: []string{"c"},
Value: false,
Destination: &Healthcheck,
}
// StackLogs lists logs for all stack services
func StackLogs(c *cli.Context, stackName string, logOpts types.ContainerLogsOptions, client *dockerClient.Client) {
filters := filters.NewArgs()
filters.Add("name", stackName)
serviceOpts := types.ServiceListOptions{Filters: filters}
services, err := client.ServiceList(c.Context, serviceOpts)
if err != nil {
logrus.Fatal(err)
}
var wg sync.WaitGroup
for _, service := range services {
wg.Add(1)
go func(s string) {
logs, err := client.ServiceLogs(c.Context, s, 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)
if err != nil && err != io.EOF {
logrus.Fatal(err)
}
}(service.ID)
}
wg.Wait()
os.Exit(0)
}

View File

@ -112,6 +112,35 @@ func ValidateApp(c *cli.Context) config.App {
return app
}
// ValidateAppByName ensures the app is valid and takes an app name as an argument, not context.
func ValidateAppByName(c *cli.Context, appName string) config.App {
if AppName != "" {
appName = AppName
logrus.Debugf("programmatically setting app name to %s", appName)
}
if appName == "" {
ShowSubcommandHelpAndError(c, errors.New("no app provided"))
}
app, err := app.Get(appName)
if err != nil {
logrus.Fatal(err)
}
if err := recipe.EnsureExists(app.Type); err != nil {
logrus.Fatal(err)
}
if err := ssh.EnsureHostKey(app.Server); err != nil {
logrus.Fatal(err)
}
logrus.Debugf("validated '%s' as app argument", appName)
return app
}
// ValidateDomain ensures the domain name arg is valid.
func ValidateDomain(c *cli.Context) (string, error) {
domainName := c.Args().First()

101
cli/server/logs.go Normal file
View File

@ -0,0 +1,101 @@
package server
import (
"fmt"
"io"
"os"
"coopcloud.tech/abra/cli/internal"
"coopcloud.tech/abra/pkg/client"
"coopcloud.tech/abra/pkg/config"
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/filters"
"github.com/sirupsen/logrus"
"github.com/urfave/cli/v2"
)
var Taillen string
var TaillenFlag = &cli.StringFlag{
Name: "tail",
Aliases: []string{"t"},
Value: "5",
Destination: &Taillen,
Usage: "change how many lines are shown",
}
var serverLogsCommand = &cli.Command{
Name: "logs",
Aliases: []string{"l"},
ArgsUsage: "<server>",
Usage: "show logs from all apps from server",
Flags: []cli.Flag{
TaillenFlag,
internal.StderrFlag,
internal.StdoutFlag,
internal.HealthcheckFlag,
},
Action: func(c *cli.Context) error {
serverName, err := internal.ValidateServer(c)
serviceName := ""
if !internal.Stderr && !internal.Stdout && !internal.Healthcheck {
internal.Stderr = true
internal.Stdout = true
internal.Healthcheck = true
}
logrus.Debugf("flags parsed. --stderr: %t, --stdout: %t, --healthcheck: %t", internal.Stderr, internal.Stdout, internal.Healthcheck)
if err != nil {
logrus.Fatal(err)
}
appMap, err := config.LoadAppFiles(serverName)
if err != nil {
logrus.Fatal(err)
}
logOpts := types.ContainerLogsOptions{
Details: false,
Follow: false,
ShowStderr: internal.Stderr,
ShowStdout: internal.Stdout,
Tail: Taillen,
Timestamps: true,
}
var appFiles []config.App
for appname, _ := range appMap {
app := internal.ValidateAppByName(c, appname)
appFiles = append(appFiles, app)
}
for _, app := range appFiles {
fmt.Println(app)
logrus.Debugf("checking logs for: %s", app.Name)
cl, err := client.New(app.Server)
if err != nil {
logrus.Fatal(err)
}
logrus.Debugf("tailing logs for all services")
filters := filters.NewArgs()
filters.Add("name", service)
serviceOpts := types.ServiceListOptions{Filters: filters}
services, err := cl.ServiceList(c.Context, serviceOpts)
if err != nil {
logrus.Fatal(err)
}
logs, err := cl.ServiceLogs(c.Context, services[0].ID, logOpts)
if err != nil {
logrus.Fatal(err)
}
logrus.Info(app.StackName())
for {
_, err = io.Copy(os.Stdout, logs)
if err == io.EOF {
break
} else if err != nil {
logrus.Fatal(err)
}
}
logs.Close()
}
return nil
},
}

View File

@ -23,5 +23,6 @@ apps, see available flags on "server add" for more.
serverAddCommand,
serverListCommand,
serverRemoveCommand,
serverLogsCommand,
},
}