Skip to content

Commit

Permalink
Print command output to logs (#9)
Browse files Browse the repository at this point in the history
* Update scheduler.go

Fix issue when containerID returns `/../..`, so that when it filters the returned string, it identifies containerID `..`, and then fails at the end, bc this is invalid container id

* print out stdout/stderr to container logs

* add changes suggested by @3timeslazy:

This code fixes the case when the exec command fails (if command not found, for instance), but scheduler thinks it finished successfully

* make logging opt in

* use strconv to parse bool value of logsLabel

* refactor execService:

- use old approach with ContainerExecStart if logging disabled
- use new approach with ContainerExecAttach if logging enabled

* format with gofmt
  • Loading branch information
lorado authored Jun 22, 2024
1 parent 3353d7d commit a2d38ad
Show file tree
Hide file tree
Showing 2 changed files with 48 additions and 1 deletion.
1 change: 1 addition & 0 deletions docker-compose.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ services:
labels:
- "net.reddec.scheduler.cron=* * * * *"
- "net.reddec.scheduler.exec=nginx -s reload"
- "net.reddec.scheduler.logs=true"
date:
image: busybox
restart: "no"
Expand Down
48 changes: 47 additions & 1 deletion scheduler.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@ package scheduler
import (
"context"
"fmt"
"io"
"log"
"os"
"path/filepath"
"regexp"
"strconv"
"strings"
"sync/atomic"
"time"
Expand All @@ -24,6 +26,7 @@ const (
composeServiceLabel = "com.docker.compose.service"
schedulerLabel = "net.reddec.scheduler.cron"
commandLabel = "net.reddec.scheduler.exec"
logsLabel = "net.reddec.scheduler.logs"
)

func Create(ctx context.Context, options ...Option) (*Scheduler, error) {
Expand Down Expand Up @@ -57,6 +60,7 @@ type Task struct {
Container string
Schedule string
Command []string
logging bool
}

type Scheduler struct {
Expand All @@ -81,7 +85,7 @@ func (sc *Scheduler) Run(ctx context.Context) error {
engine := cron.New()

for _, t := range tasks {
log.Println("task for service", t.Service, "at", t.Schedule)
log.Println("task for service", t.Service, "at", t.Schedule, "| logging:", t.logging)
running := new(int32)
t := t
_, err = engine.AddFunc(t.Schedule, func() {
Expand Down Expand Up @@ -145,6 +149,14 @@ func (sc *Scheduler) runTask(ctx context.Context, running *int32, task Task) err
}

func (sc *Scheduler) execService(ctx context.Context, task Task) error {
if task.logging {
return sc.execAttachService(ctx, task)
} else {
return sc.execStartService(ctx, task)
}
}

func (sc *Scheduler) execStartService(ctx context.Context, task Task) error {
execID, err := sc.client.ContainerExecCreate(ctx, task.Container, types.ExecConfig{
Cmd: task.Command,
})
Expand All @@ -159,6 +171,33 @@ func (sc *Scheduler) execService(ctx context.Context, task Task) error {
return nil
}

func (sc *Scheduler) execAttachService(ctx context.Context, task Task) error {
execID, err := sc.client.ContainerExecCreate(ctx, task.Container, types.ExecConfig{
Cmd: task.Command,
AttachStderr: true,
AttachStdout: true,
})
if err != nil {
return fmt.Errorf("create exec for %s: %w", task.Service, err)
}

attach, err := sc.client.ContainerExecAttach(ctx, execID.ID, types.ExecStartCheck{})
if err != nil {
return fmt.Errorf("exec for %s: %w", task.Service, err)
}
defer attach.Close()
io.Copy(log.Writer(), attach.Reader)

inspect, err := sc.client.ContainerExecInspect(ctx, execID.ID)
if err != nil {
return fmt.Errorf("inspect exec for %s: %w", task.Service, err)
}
if inspect.ExitCode != 0 {
return fmt.Errorf("command returned non-zero code %d", inspect.ExitCode)
}
return nil
}

func (sc *Scheduler) runService(ctx context.Context, task Task) error {
err := sc.client.ContainerStart(ctx, task.Container, types.ContainerStartOptions{})
if err != nil {
Expand Down Expand Up @@ -202,11 +241,18 @@ func (sc *Scheduler) listTasks(ctx context.Context) ([]Task, error) {
}
args = cmd
}

isLoggingEnabled, err := strconv.ParseBool(c.Labels[logsLabel])
if err != nil {
isLoggingEnabled = false
}

ans = append(ans, Task{
Container: c.ID,
Schedule: c.Labels[schedulerLabel],
Service: service,
Command: args,
logging: isLoggingEnabled,
})
}

Expand Down

0 comments on commit a2d38ad

Please sign in to comment.