Attach to exec jobs and print their output
Some checks failed
continuous-integration/drone/push Build is failing
Some checks failed
continuous-integration/drone/push Build is failing
Still no logging or means of detecting failed attempts
This commit is contained in:
parent
50045e71c8
commit
81211d1340
@ -27,4 +27,4 @@ services:
|
|||||||
labels:
|
labels:
|
||||||
# Execute every minute
|
# Execute every minute
|
||||||
- 'dockron.test.schedule=* * * * *'
|
- 'dockron.test.schedule=* * * * *'
|
||||||
- 'dockron.test.command=echo ok >> /result.txt'
|
- 'dockron.test.command=echo ok | tee /result.txt && echo "Yay!"'
|
||||||
|
@ -30,9 +30,18 @@ function main() {
|
|||||||
echo "Stopping containers"
|
echo "Stopping containers"
|
||||||
docker compose stop
|
docker compose stop
|
||||||
|
|
||||||
|
# Print logs
|
||||||
|
docker compose logs
|
||||||
|
|
||||||
# Validate result shows minimum amount of executions
|
# Validate result shows minimum amount of executions
|
||||||
check_results ./start_result.txt 2
|
check_results ./start_result.txt 2
|
||||||
check_results ./exec_result.txt 1
|
check_results ./exec_result.txt 1
|
||||||
|
|
||||||
|
# Check for exec output
|
||||||
|
if ! (docker compose logs | grep -q "Yay!"); then
|
||||||
|
echo "Exec output 'Yay!' not found"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
main
|
main
|
||||||
|
56
main.go
56
main.go
@ -1,6 +1,7 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bufio"
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
@ -29,11 +30,12 @@ var (
|
|||||||
version = "dev"
|
version = "dev"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ContainerClient provides an interface for interracting with Docker
|
// ContainerClient provides an interface for interracting with Docker. Makes it possible to mock in tests
|
||||||
type ContainerClient interface {
|
type ContainerClient interface {
|
||||||
ContainerExecCreate(ctx context.Context, container string, config container.ExecOptions) (dockerTypes.IDResponse, error)
|
ContainerExecCreate(ctx context.Context, container string, config container.ExecOptions) (dockerTypes.IDResponse, error)
|
||||||
ContainerExecInspect(ctx context.Context, execID string) (container.ExecInspect, error)
|
ContainerExecInspect(ctx context.Context, execID string) (container.ExecInspect, error)
|
||||||
ContainerExecStart(ctx context.Context, execID string, config container.ExecStartOptions) error
|
ContainerExecStart(ctx context.Context, execID string, config container.ExecStartOptions) error
|
||||||
|
ContainerExecAttach(ctx context.Context, execID string, options container.ExecAttachOptions) (dockerTypes.HijackedResponse, error)
|
||||||
ContainerInspect(ctx context.Context, containerID string) (dockerTypes.ContainerJSON, error)
|
ContainerInspect(ctx context.Context, containerID string) (dockerTypes.ContainerJSON, error)
|
||||||
ContainerList(context context.Context, options container.ListOptions) ([]dockerTypes.Container, error)
|
ContainerList(context context.Context, options container.ListOptions) ([]dockerTypes.Container, error)
|
||||||
ContainerStart(context context.Context, containerID string, options container.StartOptions) error
|
ContainerStart(context context.Context, containerID string, options container.StartOptions) error
|
||||||
@ -71,7 +73,7 @@ func (job ContainerStartJob) Run() {
|
|||||||
slog.OnErrPanicf(err, "Could not get container details for job %s", job.name)
|
slog.OnErrPanicf(err, "Could not get container details for job %s", job.name)
|
||||||
|
|
||||||
if containerJSON.State.Running {
|
if containerJSON.State.Running {
|
||||||
slog.Warningf("Container is already running. Skipping %s", job.name)
|
slog.Warningf("%s: Container is already running. Skipping start.", job.name)
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -86,7 +88,7 @@ func (job ContainerStartJob) Run() {
|
|||||||
|
|
||||||
// Check results of job
|
// Check results of job
|
||||||
for check := true; check; check = containerJSON.State.Running {
|
for check := true; check; check = containerJSON.State.Running {
|
||||||
slog.Debugf("Still running %s", job.name)
|
slog.Debugf("%s: Still running", job.name)
|
||||||
|
|
||||||
containerJSON, err = job.client.ContainerInspect(
|
containerJSON, err = job.client.ContainerInspect(
|
||||||
job.context,
|
job.context,
|
||||||
@ -96,12 +98,12 @@ func (job ContainerStartJob) Run() {
|
|||||||
|
|
||||||
time.Sleep(1 * time.Second)
|
time.Sleep(1 * time.Second)
|
||||||
}
|
}
|
||||||
slog.Debugf("Done execing %s. %+v", job.name, containerJSON.State)
|
slog.Debugf("%s: Done running. %+v", job.name, containerJSON.State)
|
||||||
|
|
||||||
// Log exit code if failed
|
// Log exit code if failed
|
||||||
if containerJSON.State.ExitCode != 0 {
|
if containerJSON.State.ExitCode != 0 {
|
||||||
slog.Errorf(
|
slog.Errorf(
|
||||||
"Exec job %s exited with code %d",
|
"%s: Exec job exited with code %d",
|
||||||
job.name,
|
job.name,
|
||||||
containerJSON.State.ExitCode,
|
containerJSON.State.ExitCode,
|
||||||
)
|
)
|
||||||
@ -142,7 +144,7 @@ func (job ContainerExecJob) Run() {
|
|||||||
slog.OnErrPanicf(err, "Could not get container details for job %s", job.name)
|
slog.OnErrPanicf(err, "Could not get container details for job %s", job.name)
|
||||||
|
|
||||||
if !containerJSON.State.Running {
|
if !containerJSON.State.Running {
|
||||||
slog.Warningf("Container not running. Skipping %s", job.name)
|
slog.Warningf("%s: Container not running. Skipping exec.", job.name)
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -151,11 +153,19 @@ func (job ContainerExecJob) Run() {
|
|||||||
job.context,
|
job.context,
|
||||||
job.containerID,
|
job.containerID,
|
||||||
container.ExecOptions{
|
container.ExecOptions{
|
||||||
|
AttachStdout: true,
|
||||||
|
AttachStderr: true,
|
||||||
Cmd: []string{"sh", "-c", strings.TrimSpace(job.shellCommand)},
|
Cmd: []string{"sh", "-c", strings.TrimSpace(job.shellCommand)},
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
slog.OnErrPanicf(err, "Could not create container exec job for %s", job.name)
|
slog.OnErrPanicf(err, "Could not create container exec job for %s", job.name)
|
||||||
|
|
||||||
|
hj, err := job.client.ContainerExecAttach(job.context, execID.ID, container.ExecAttachOptions{})
|
||||||
|
slog.OnErrWarnf(err, "%s: Error attaching to exec: %s", job.name, err)
|
||||||
|
defer hj.Close()
|
||||||
|
|
||||||
|
scanner := bufio.NewScanner(hj.Reader)
|
||||||
|
|
||||||
err = job.client.ContainerExecStart(
|
err = job.client.ContainerExecStart(
|
||||||
job.context,
|
job.context,
|
||||||
execID.ID,
|
execID.ID,
|
||||||
@ -166,26 +176,48 @@ func (job ContainerExecJob) Run() {
|
|||||||
// Wait for job results
|
// Wait for job results
|
||||||
execInfo := container.ExecInspect{Running: true}
|
execInfo := container.ExecInspect{Running: true}
|
||||||
for execInfo.Running {
|
for execInfo.Running {
|
||||||
|
time.Sleep(1 * time.Second)
|
||||||
|
|
||||||
slog.Debugf("Still execing %s", job.name)
|
slog.Debugf("Still execing %s", job.name)
|
||||||
execInfo, err = job.client.ContainerExecInspect(
|
execInfo, err = job.client.ContainerExecInspect(
|
||||||
job.context,
|
job.context,
|
||||||
execID.ID,
|
execID.ID,
|
||||||
)
|
)
|
||||||
slog.Debugf("Exec info: %+v", execInfo)
|
|
||||||
|
// Maybe print output
|
||||||
|
if hj.Reader != nil {
|
||||||
|
slog.Debugf("%s: Getting exec reader", job.name)
|
||||||
|
for scanner.Scan() {
|
||||||
|
slog.Debugf("%s: Getting exec line", job.name)
|
||||||
|
|
||||||
|
line := scanner.Text()
|
||||||
|
if len(line) > 0 {
|
||||||
|
slog.Infof("%s: Exec output: %s", job.name, line)
|
||||||
|
} else {
|
||||||
|
slog.Debugf("%s: Empty exec output", job.name)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := scanner.Err(); err != nil {
|
||||||
|
slog.OnErrWarnf(err, "%s: Error reading from exec", job.name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
slog.Debugf("%s: No exec reader", job.name)
|
||||||
|
}
|
||||||
|
|
||||||
|
slog.Debugf("%s: Exec info: %+v", job.name, execInfo)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// Nothing we can do if we got an error here, so let's go
|
// Nothing we can do if we got an error here, so let's go
|
||||||
slog.OnErrWarnf(err, "Could not get status for exec job %s", job.name)
|
slog.OnErrWarnf(err, "%s: Could not get status for exec job", job.name)
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
time.Sleep(1 * time.Second)
|
|
||||||
}
|
}
|
||||||
slog.Debugf("Done execing %s. %+v", job.name, execInfo)
|
slog.Debugf("%s: Done execing. %+v", job.name, execInfo)
|
||||||
// Log exit code if failed
|
// Log exit code if failed
|
||||||
if execInfo.ExitCode != 0 {
|
if execInfo.ExitCode != 0 {
|
||||||
slog.Errorf("Exec job %s existed with code %d", job.name, execInfo.ExitCode)
|
slog.Errorf("%s: Exec job existed with code %d", job.name, execInfo.ExitCode)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
12
main_test.go
12
main_test.go
@ -144,6 +144,10 @@ func (fakeClient *FakeDockerClient) ContainerInspect(ctx context.Context, contai
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (fakeClient *FakeDockerClient) ContainerExecAttach(ctx context.Context, execID string, options container.ExecAttachOptions) (dockerTypes.HijackedResponse, error) {
|
||||||
|
return dockerTypes.HijackedResponse{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
// NewFakeDockerClient creates an empty client
|
// NewFakeDockerClient creates an empty client
|
||||||
func NewFakeDockerClient() *FakeDockerClient {
|
func NewFakeDockerClient() *FakeDockerClient {
|
||||||
return &FakeDockerClient{
|
return &FakeDockerClient{
|
||||||
@ -730,6 +734,8 @@ func TestRunExecJobs(t *testing.T) {
|
|||||||
jobContext,
|
jobContext,
|
||||||
jobContainerID,
|
jobContainerID,
|
||||||
container.ExecOptions{
|
container.ExecOptions{
|
||||||
|
AttachStdout: true,
|
||||||
|
AttachStderr: true,
|
||||||
Cmd: []string{"sh", "-c", jobCommand},
|
Cmd: []string{"sh", "-c", jobCommand},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -761,6 +767,8 @@ func TestRunExecJobs(t *testing.T) {
|
|||||||
jobContext,
|
jobContext,
|
||||||
jobContainerID,
|
jobContainerID,
|
||||||
container.ExecOptions{
|
container.ExecOptions{
|
||||||
|
AttachStdout: true,
|
||||||
|
AttachStderr: true,
|
||||||
Cmd: []string{"sh", "-c", jobCommand},
|
Cmd: []string{"sh", "-c", jobCommand},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -798,6 +806,8 @@ func TestRunExecJobs(t *testing.T) {
|
|||||||
jobContext,
|
jobContext,
|
||||||
jobContainerID,
|
jobContainerID,
|
||||||
container.ExecOptions{
|
container.ExecOptions{
|
||||||
|
AttachStdout: true,
|
||||||
|
AttachStderr: true,
|
||||||
Cmd: []string{"sh", "-c", jobCommand},
|
Cmd: []string{"sh", "-c", jobCommand},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -838,6 +848,8 @@ func TestRunExecJobs(t *testing.T) {
|
|||||||
jobContext,
|
jobContext,
|
||||||
jobContainerID,
|
jobContainerID,
|
||||||
container.ExecOptions{
|
container.ExecOptions{
|
||||||
|
AttachStdout: true,
|
||||||
|
AttachStderr: true,
|
||||||
Cmd: []string{"sh", "-c", jobCommand},
|
Cmd: []string{"sh", "-c", jobCommand},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
Loading…
Reference in New Issue
Block a user