Compare commits
26 Commits
master
...
add-exec-s
Author | SHA1 | Date | |
---|---|---|---|
301563c302 | |||
4cdd1eab9e | |||
eac49e36b0 | |||
3de07349ed | |||
c742a324ed | |||
a5406be70b | |||
bb0db6a9d6 | |||
0e09d2ca2c | |||
c9d19f9c01 | |||
7e9f2e38ca | |||
996d31b9eb | |||
e3431971a0 | |||
4bf79f769e | |||
088bf2ab5e | |||
4ef3f0b431 | |||
f4c7801d84 | |||
0b8454bb80 | |||
61171cc9eb | |||
a1ba2060bb | |||
a572bd6635 | |||
1fc9672971 | |||
63532b39f8 | |||
73b7a12eb7 | |||
32d681703c | |||
4cb0b027cd | |||
3ee32b632a |
13
.drone.yml
13
.drone.yml
@ -4,14 +4,17 @@ name: test
|
||||
|
||||
steps:
|
||||
- name: test
|
||||
image: golang:1.22
|
||||
image: golang:1.15
|
||||
commands:
|
||||
- make test
|
||||
|
||||
- name: check
|
||||
image: iamthefij/drone-pre-commit:personal
|
||||
environment:
|
||||
SKIP: docker-compose-check
|
||||
image: python:3
|
||||
commands:
|
||||
- pip install docker-compose pre-commit
|
||||
- wget -L -O /usr/bin/hadolint https://github.com/hadolint/hadolint/releases/download/v1.18.0/hadolint-Linux-x86_64
|
||||
- chmod +x /usr/bin/hadolint
|
||||
- make check
|
||||
|
||||
# - name: itest
|
||||
# image: docker/compose:alpine-1.26.2
|
||||
@ -38,7 +41,7 @@ trigger:
|
||||
|
||||
steps:
|
||||
- name: build
|
||||
image: golang:1.22
|
||||
image: golang:1.15
|
||||
environment:
|
||||
VERSION: ${DRONE_TAG:-${DRONE_COMMIT}}
|
||||
commands:
|
||||
|
@ -1,36 +0,0 @@
|
||||
---
|
||||
linters:
|
||||
enable:
|
||||
- errname
|
||||
- errorlint
|
||||
- exhaustive
|
||||
- gofumpt
|
||||
- goimports
|
||||
- goprintffuncname
|
||||
- misspell
|
||||
- gomnd
|
||||
- tagliatelle
|
||||
- tenv
|
||||
- testpackage
|
||||
- thelper
|
||||
- tparallel
|
||||
- unconvert
|
||||
- wrapcheck
|
||||
- wsl
|
||||
disable:
|
||||
- gochecknoglobals
|
||||
|
||||
linters-settings:
|
||||
gosec:
|
||||
excludes:
|
||||
- G204
|
||||
tagliatelle:
|
||||
case:
|
||||
rules:
|
||||
yaml: snake
|
||||
|
||||
issues:
|
||||
exclude-rules:
|
||||
- path: _test\.go
|
||||
linters:
|
||||
- gosec
|
@ -1,22 +1,21 @@
|
||||
---
|
||||
repos:
|
||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||
rev: v4.6.0
|
||||
rev: v2.4.0
|
||||
hooks:
|
||||
- id: check-added-large-files
|
||||
- id: trailing-whitespace
|
||||
- id: end-of-file-fixer
|
||||
- id: check-merge-conflict
|
||||
- repo: https://github.com/golangci/golangci-lint
|
||||
rev: v1.52.2
|
||||
- repo: git://github.com/dnephin/pre-commit-golang
|
||||
rev: v0.3.5
|
||||
hooks:
|
||||
- id: golangci-lint
|
||||
args: ["--timeout=5m"]
|
||||
- id: go-fmt
|
||||
- id: go-imports
|
||||
# - id: gometalinter
|
||||
# - id: golangci-lint
|
||||
- repo: https://github.com/IamTheFij/docker-pre-commit
|
||||
rev: v3.0.1
|
||||
rev: v2.0.0
|
||||
hooks:
|
||||
- id: docker-compose-check
|
||||
- repo: https://github.com/hadolint/hadolint
|
||||
rev: v2.12.0
|
||||
hooks:
|
||||
- id: hadolint
|
||||
- id: hadolint-system
|
||||
|
@ -1,15 +1,17 @@
|
||||
ARG REPO=library
|
||||
FROM golang:1.22-alpine AS builder
|
||||
FROM golang:1.15-alpine AS builder
|
||||
|
||||
# hadolint ignore=DL3018
|
||||
RUN apk add --no-cache git && \
|
||||
mkdir /app
|
||||
RUN apk add --no-cache git
|
||||
|
||||
RUN mkdir /app
|
||||
WORKDIR /app
|
||||
|
||||
COPY ./go.mod ./go.sum /app/
|
||||
RUN go mod download
|
||||
|
||||
COPY ./main.go /app/
|
||||
COPY ./slog /app/slog
|
||||
|
||||
ARG ARCH=amd64
|
||||
ARG VERSION=dev
|
||||
|
71
Makefile
71
Makefile
@ -1,8 +1,7 @@
|
||||
OUTPUT ?= dockron
|
||||
DOCKER_TAG ?= $(OUTPUT)-dev-$(USER)
|
||||
DOCKER_TAG ?= dockron-dev-${USER}
|
||||
GIT_TAG_NAME := $(shell git tag -l --contains HEAD)
|
||||
GIT_SHA := $(shell git rev-parse HEAD)
|
||||
VERSION ?= $(if $(GIT_TAG_NAME),$(GIT_TAG_NAME),$(GIT_SHA))
|
||||
VERSION := $(if $(GIT_TAG_NAME),$(GIT_TAG_NAME),$(GIT_SHA))
|
||||
|
||||
GOFILES = *.go go.mod go.sum
|
||||
|
||||
@ -43,45 +42,45 @@ check:
|
||||
pre-commit run --all-files
|
||||
|
||||
# Output target
|
||||
$(OUTPUT): $(GOFILES)
|
||||
dockron: $(GOFILES)
|
||||
@echo Version: $(VERSION)
|
||||
go build -ldflags '-X "main.version=$(VERSION)"' -o $(OUTPUT)
|
||||
go build -ldflags '-X "main.version=${VERSION}"' -o dockron
|
||||
|
||||
# Alias for building
|
||||
.PHONY: build
|
||||
build: $(OUTPUT)
|
||||
build: dockron
|
||||
|
||||
$(OUTPUT)-darwin-amd64: $(GOFILES)
|
||||
dockron-darwin-amd64: $(GOFILES)
|
||||
GOOS=darwin GOARCH=amd64 CGO_ENABLED=0 \
|
||||
go build -ldflags '-X "main.version=$(VERSION)"' -a -installsuffix nocgo \
|
||||
-o $(OUTPUT)-darwin-amd64
|
||||
go build -ldflags '-X "main.version=${VERSION}"' -a -installsuffix nocgo \
|
||||
-o dockron-darwin-amd64
|
||||
|
||||
$(OUTPUT)-linux-amd64: $(GOFILES)
|
||||
dockron-linux-amd64: $(GOFILES)
|
||||
GOOS=linux GOARCH=amd64 CGO_ENABLED=0 \
|
||||
go build -ldflags '-X "main.version=$(VERSION)"' -a -installsuffix nocgo \
|
||||
-o $(OUTPUT)-linux-amd64
|
||||
go build -ldflags '-X "main.version=${VERSION}"' -a -installsuffix nocgo \
|
||||
-o dockron-linux-amd64
|
||||
|
||||
$(OUTPUT)-linux-arm: $(GOFILES)
|
||||
dockron-linux-arm: $(GOFILES)
|
||||
GOOS=linux GOARCH=arm CGO_ENABLED=0 \
|
||||
go build -ldflags '-X "main.version=$(VERSION)"' -a -installsuffix nocgo \
|
||||
-o $(OUTPUT)-linux-arm
|
||||
go build -ldflags '-X "main.version=${VERSION}"' -a -installsuffix nocgo \
|
||||
-o dockron-linux-arm
|
||||
|
||||
$(OUTPUT)-linux-arm64: $(GOFILES)
|
||||
dockron-linux-arm64: $(GOFILES)
|
||||
GOOS=linux GOARCH=arm64 CGO_ENABLED=0 \
|
||||
go build -ldflags '-X "main.version=$(VERSION)"' -a -installsuffix nocgo \
|
||||
-o $(OUTPUT)-linux-arm64
|
||||
go build -ldflags '-X "main.version=${VERSION}"' -a -installsuffix nocgo \
|
||||
-o dockron-linux-arm64
|
||||
|
||||
.PHONY: build-linux-static
|
||||
build-linux-static: $(OUTPUT)-linux-amd64 $(OUTPUT)-linux-arm $(OUTPUT)-linux-arm64
|
||||
build-linux-static: dockron-linux-amd64 dockron-linux-arm dockron-linux-arm64
|
||||
|
||||
.PHONY: build-all-static
|
||||
build-all-static: $(OUTPUT)-darwin-amd64 build-linux-static
|
||||
build-all-static: dockron-darwin-amd64 build-linux-static
|
||||
|
||||
# Cleans all build artifacts
|
||||
.PHONY: clean
|
||||
clean:
|
||||
rm -f $(OUTPUT)
|
||||
rm -f $(OUTPUT)-linux-*
|
||||
rm -f dockron
|
||||
rm -f dockron-linux-*
|
||||
|
||||
# Cleans vendor directory
|
||||
.PHONY: clean-vendor
|
||||
@ -89,17 +88,17 @@ clean-vendor:
|
||||
rm -fr ./vendor
|
||||
|
||||
.PHONY: docker-build
|
||||
docker-build: $(OUTPUT)-linux-amd64
|
||||
docker build . -t $(DOCKER_TAG)-linux-amd64
|
||||
docker-build: dockron-linux-amd64
|
||||
docker build . -t ${DOCKER_TAG}-linux-amd64
|
||||
|
||||
# Cross build for arm architechtures
|
||||
.PHONY: docker-build-arm
|
||||
docker-build-arm: $(OUTPUT)-linux-arm
|
||||
docker build --build-arg REPO=arm32v7 --build-arg ARCH=arm . -t $(DOCKER_TAG)-linux-arm
|
||||
docker-build-arm: dockron-linux-arm
|
||||
docker build --build-arg REPO=arm32v7 --build-arg ARCH=arm . -t ${DOCKER_TAG}-linux-arm
|
||||
|
||||
.PHONY: docker-build-arm
|
||||
docker-build-arm64: $(OUTPUT)-linux-arm64
|
||||
docker build --build-arg REPO=arm64v8 --build-arg ARCH=arm64 . -t $(DOCKER_TAG)-linux-arm64
|
||||
docker-build-arm64: dockron-linux-arm64
|
||||
docker build --build-arg REPO=arm64v8 --build-arg ARCH=arm64 . -t ${DOCKER_TAG}-linux-arm64
|
||||
|
||||
.PHONY: docker-run
|
||||
docker-run: docker-build
|
||||
@ -108,30 +107,30 @@ docker-run: docker-build
|
||||
# Cross run on host architechture
|
||||
.PHONY: docker-run-arm
|
||||
docker-run-arm: docker-build-arm
|
||||
docker run --rm -v /var/run/docker.sock:/var/run/docker.sock --name $(DOCKER_TAG)-run $(DOCKER_TAG)-linux-arm
|
||||
docker run --rm -v /var/run/docker.sock:/var/run/docker.sock --name $(DOCKER_TAG)-run ${DOCKER_TAG}-linux-arm
|
||||
|
||||
.PHONY: docker-run-arm64
|
||||
docker-run-arm64: docker-build-arm64
|
||||
docker run --rm -v /var/run/docker.sock:/var/run/docker.sock --name $(DOCKER_TAG)-run $(DOCKER_TAG)-linux-arm64
|
||||
docker run --rm -v /var/run/docker.sock:/var/run/docker.sock --name $(DOCKER_TAG)-run ${DOCKER_TAG}-linux-arm64
|
||||
|
||||
# Multi stage builds
|
||||
.PHONY: docker-staged-build
|
||||
docker-staged-build:
|
||||
docker build --build-arg VERSION=$(VERSION) \
|
||||
-t $(DOCKER_TAG)-linux-amd64 \
|
||||
docker build --build-arg VERSION=${VERSION} \
|
||||
-t ${DOCKER_TAG}-linux-amd64 \
|
||||
-f Dockerfile.multi-stage .
|
||||
|
||||
# Cross build for arm architechtures
|
||||
.PHONY: docker-staged-build-arm
|
||||
docker-staged-build-arm:
|
||||
docker build --build-arg VERSION=$(VERSION) \
|
||||
--build-arg REPO=arm32v7 --build-arg ARCH=arm -t $(DOCKER_TAG)-linux-arm \
|
||||
docker build --build-arg VERSION=${VERSION} \
|
||||
--build-arg REPO=arm32v7 --build-arg ARCH=arm -t ${DOCKER_TAG}-linux-arm \
|
||||
-f Dockerfile.multi-stage .
|
||||
|
||||
.PHONY: docker-staged-build-arm
|
||||
docker-staged-build-arm64:
|
||||
docker build --build-arg VERSION=$(VERSION) \
|
||||
--build-arg REPO=arm64v8 --build-arg ARCH=arm64 -t $(DOCKER_TAG)-linux-arm64 \
|
||||
docker build --build-arg VERSION=${VERSION} \
|
||||
--build-arg REPO=arm64v8 --build-arg ARCH=arm64 -t ${DOCKER_TAG}-linux-arm64 \
|
||||
-f Dockerfile.multi-stage .
|
||||
|
||||
.PHONY: docker-example
|
||||
|
24
README.md
24
README.md
@ -24,16 +24,6 @@ From either an `amd64`, `arm`, or `arm64` machine, you can run Dockron using:
|
||||
|
||||
docker run -v /var/run/docker.sock:/var/run/docker.sock:ro iamthefij/dockron -watch
|
||||
|
||||
### Getting a Docker API Version error?
|
||||
|
||||
You might see something like the following error when Dockron connects to the Docker API
|
||||
|
||||
```
|
||||
Error response from daemon: client version 1.47 is too new. Maximum supported API version is 1.45
|
||||
```
|
||||
|
||||
This is because the API client library is newer than the version of the Docker API on your host. You can tell the Dockron API Client to use a compatible version by specifying `DOCKER_API_VERSION=1.45`, where the version you specify matches the API version shown when you run `docker version`. If you are running Dockron in Docker, make sure you add this to your compose environment or otherwise pass it to the container.
|
||||
|
||||
### Scheduling a container
|
||||
|
||||
First, be sure your container is something that is not long running and will actually exit when complete. This is for batch runs and not keeping a service running. Docker should be able to do that on it's own with a restart policy.
|
||||
@ -42,16 +32,6 @@ Create your container and add a label in the form `'dockron.schedule=* * * * *'`
|
||||
|
||||
Dockron will now start that container peridically on the schedule.
|
||||
|
||||
If you have a long running container that you'd like to schedule an exec command inside of, you can do so with labels as well. Add your job in the form `dockron.<job>.schedule=* * * * *` and `dockeron.<job>.command=echo hello`. Both labels are required to create an exec job.
|
||||
|
||||
Eg.
|
||||
|
||||
labels:
|
||||
- "dockron.dates.schedule=* * * * *"
|
||||
- "dockron.dates.command=date"
|
||||
|
||||
_Note: Exec jobs will log their output to Dockron. There is also currently no way to health check these._
|
||||
|
||||
### Cron Expression Formatting
|
||||
|
||||
For more information on the cron expression parsing, see the docs for [robfig/cron](https://godoc.org/github.com/robfig/cron).
|
||||
@ -75,7 +55,3 @@ Either use a separate tool in conjunction with Dockron, or use a more robust sch
|
||||
If you have go on your machine, you can simply use `make build` or `make run` to build and test Dockron. If you don't have go but you do have Docker, you can still build docker images using the provide multi-stage Dockerfile! You can kick that off with `make docker-staged-build`
|
||||
|
||||
There is also an example `docker-compose.yml` that will use the multi-stage build to ensure an easy sample. This can be run with `make docker-example`.
|
||||
|
||||
## Tests
|
||||
|
||||
There are now some basic tests as well as linting and integration tests. You can run all of these by executing `make all`.
|
||||
|
49
go.mod
49
go.mod
@ -1,39 +1,26 @@
|
||||
module github.com/iamthefij/dockron
|
||||
|
||||
go 1.22
|
||||
|
||||
toolchain go1.22.7
|
||||
|
||||
require (
|
||||
git.iamthefij.com/iamthefij/slog v1.3.0
|
||||
github.com/docker/docker v27.3.1+incompatible
|
||||
github.com/robfig/cron/v3 v3.0.1
|
||||
golang.org/x/net v0.29.0
|
||||
)
|
||||
go 1.12
|
||||
|
||||
require (
|
||||
github.com/Microsoft/go-winio v0.4.14 // indirect
|
||||
github.com/containerd/log v0.1.0 // indirect
|
||||
github.com/distribution/reference v0.6.0 // indirect
|
||||
github.com/docker/go-connections v0.5.0 // indirect
|
||||
github.com/docker/go-units v0.5.0 // indirect
|
||||
github.com/felixge/httpsnoop v1.0.4 // indirect
|
||||
github.com/go-logr/logr v1.4.2 // indirect
|
||||
github.com/go-logr/stdr v1.2.2 // indirect
|
||||
github.com/gogo/protobuf v1.3.2 // indirect
|
||||
github.com/moby/docker-image-spec v1.3.1 // indirect
|
||||
github.com/moby/term v0.5.0 // indirect
|
||||
github.com/morikuni/aec v1.0.0 // indirect
|
||||
github.com/containerd/containerd v1.3.7 // indirect
|
||||
github.com/docker/distribution v2.7.1+incompatible // indirect
|
||||
github.com/docker/docker v17.12.0-ce-rc1.0.20200618181300-9dc6525e6118+incompatible
|
||||
github.com/docker/go-connections v0.4.0 // indirect
|
||||
github.com/docker/go-units v0.4.0 // indirect
|
||||
github.com/gogo/protobuf v1.3.1 // indirect
|
||||
github.com/golang/protobuf v1.4.2 // indirect
|
||||
github.com/opencontainers/go-digest v1.0.0 // indirect
|
||||
github.com/opencontainers/image-spec v1.1.0 // indirect
|
||||
github.com/opencontainers/image-spec v1.0.1 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.55.0 // indirect
|
||||
go.opentelemetry.io/otel v1.30.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.30.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.30.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk v1.30.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.30.0 // indirect
|
||||
golang.org/x/sys v0.25.0 // indirect
|
||||
golang.org/x/time v0.6.0 // indirect
|
||||
gotest.tools/v3 v3.5.1 // indirect
|
||||
github.com/robfig/cron/v3 v3.0.1
|
||||
github.com/sirupsen/logrus v1.6.0 // indirect
|
||||
github.com/stevvooe/resumable v0.0.0-20180830230917-22b14a53ba50 // indirect
|
||||
github.com/stretchr/testify v1.5.1 // indirect
|
||||
golang.org/x/net v0.0.0-20200707034311-ab3426394381
|
||||
golang.org/x/sys v0.0.0-20200806125547-5acd03effb82 // indirect
|
||||
google.golang.org/genproto v0.0.0-20200806141610-86f49bd18e98 // indirect
|
||||
google.golang.org/grpc v1.31.0 // indirect
|
||||
google.golang.org/protobuf v1.25.0 // indirect
|
||||
)
|
||||
|
217
go.sum
217
go.sum
@ -1,127 +1,142 @@
|
||||
git.iamthefij.com/iamthefij/slog v1.3.0 h1:4Hu5PQvDrW5e3FrTS3q2iIXW0iPvhNY/9qJsqDR3K3I=
|
||||
git.iamthefij.com/iamthefij/slog v1.3.0/go.mod h1:1RUj4hcCompZkAxXCRfUX786tb3cM/Zpkn97dGfUfbg=
|
||||
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8=
|
||||
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
|
||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/Microsoft/go-winio v0.4.9 h1:3RbgqgGVqmcpbOiwrjbVtDHLlJBGF6aE+yHmNtBNsFQ=
|
||||
github.com/Microsoft/go-winio v0.4.9/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA=
|
||||
github.com/Microsoft/go-winio v0.4.14 h1:+hMXMk01us9KgxGb7ftKQt2Xpf5hH/yky+TDA+qxleU=
|
||||
github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA=
|
||||
github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
|
||||
github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
|
||||
github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I=
|
||||
github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||
github.com/containerd/containerd v1.3.7 h1:eFSOChY8TTcxvkzp8g+Ov1RL0MYww7XEeK0y+zqGpVc=
|
||||
github.com/containerd/containerd v1.3.7/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
|
||||
github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
|
||||
github.com/docker/docker v27.3.1+incompatible h1:KttF0XoteNTicmUtBO0L2tP+J7FGRFTjaEF4k6WdhfI=
|
||||
github.com/docker/docker v27.3.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||
github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c=
|
||||
github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc=
|
||||
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
|
||||
github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
|
||||
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
|
||||
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
|
||||
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
|
||||
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
||||
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
||||
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
|
||||
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0 h1:asbCHRVmodnJTuQ3qamDwqVOIjwqUPTYmYuemVOx+Ys=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0/go.mod h1:ggCgvZ2r7uOoQjOyu2Y1NhHmEPPzzuhWgcza5M1Ji1I=
|
||||
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
|
||||
github.com/docker/distribution v2.6.2+incompatible h1:4FI6af79dfCS/CYb+RRtkSHw3q1L/bnDjG1PcPZtQhM=
|
||||
github.com/docker/distribution v2.6.2+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
|
||||
github.com/docker/distribution v2.7.1+incompatible h1:a5mlkVzth6W5A4fOsS3D2EO5BUmsJpcB+cRlLU7cSug=
|
||||
github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
|
||||
github.com/docker/docker v1.13.1 h1:IkZjBSIc8hBjLpqeAbeE5mca5mNgeatLHBy3GO78BWo=
|
||||
github.com/docker/docker v1.13.1/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||
github.com/docker/docker v17.12.0-ce-rc1.0.20200618181300-9dc6525e6118+incompatible h1:iWPIG7pWIsCwT6ZtHnTUpoVMnete7O/pzd9HFE3+tn8=
|
||||
github.com/docker/docker v17.12.0-ce-rc1.0.20200618181300-9dc6525e6118+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||
github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ=
|
||||
github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
|
||||
github.com/docker/go-units v0.3.3 h1:Xk8S3Xj5sLGlG5g67hJmYMmUgXv5N4PhkjJHHqrwnTk=
|
||||
github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
|
||||
github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw=
|
||||
github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
|
||||
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/gogo/protobuf v1.3.1 h1:DqDEcV5aeaTmdFBePNpYsp3FlcVH/2ISVVM9Qf8PSls=
|
||||
github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.3 h1:gyjaxf+svBWX08ZjK86iN9geUJF0H6gp2IRKX6Nf6/I=
|
||||
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
||||
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
||||
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
|
||||
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
|
||||
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
|
||||
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
||||
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
|
||||
github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0=
|
||||
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0=
|
||||
github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo=
|
||||
github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0=
|
||||
github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y=
|
||||
github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
|
||||
github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.3 h1:CE8S1cTafDpPvMhIxNJKvHsGVBgn1xWYf1NbHQhywc8=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
|
||||
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
|
||||
github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug=
|
||||
github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM=
|
||||
github.com/opencontainers/image-spec v1.0.1 h1:JMemWkRwHx4Zj+fVxWoMCFm/8sYGGrUVojFA6h/TRcI=
|
||||
github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
|
||||
github.com/pkg/errors v0.8.0 h1:WdK/asTD0HN+q6hsWO3/vpuAkAr+tw6aNJNDFFf0+qw=
|
||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
|
||||
github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
|
||||
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
|
||||
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
|
||||
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||
github.com/sirupsen/logrus v1.6.0 h1:UBcNElsrwanuuMsnGSlYmtmgbb23qDR5dG+6X6Oo89I=
|
||||
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
|
||||
github.com/stevvooe/resumable v0.0.0-20180830230917-22b14a53ba50/go.mod h1:1pdIZTAHUz+HDKDVZ++5xg/duPlhKAIzw9qy42CWYp4=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.55.0 h1:ZIg3ZT/aQ7AfKqdwp7ECpOK6vHqquXXuyTjIO8ZdmPs=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.55.0/go.mod h1:DQAwmETtZV00skUwgD6+0U89g80NKsJE3DCKeLLPQMI=
|
||||
go.opentelemetry.io/otel v1.30.0 h1:F2t8sK4qf1fAmY9ua4ohFS/K+FUuOPemHUIXHtktrts=
|
||||
go.opentelemetry.io/otel v1.30.0/go.mod h1:tFw4Br9b7fOS+uEao81PJjVMjW/5fvNCbpsDIXqP0pc=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.30.0 h1:lsInsfvhVIfOI6qHVyysXMNDnjO9Npvl7tlDPJFBVd4=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.30.0/go.mod h1:KQsVNh4OjgjTG0G6EiNi1jVpnaeeKsKMRwbLN+f1+8M=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.30.0 h1:umZgi92IyxfXd/l4kaDhnKgY8rnN/cZcF1LKc6I8OQ8=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.30.0/go.mod h1:4lVs6obhSVRb1EW5FhOuBTyiQhtRtAnnva9vD3yRfq8=
|
||||
go.opentelemetry.io/otel/metric v1.30.0 h1:4xNulvn9gjzo4hjg+wzIKG7iNFEaBMX00Qd4QIZs7+w=
|
||||
go.opentelemetry.io/otel/metric v1.30.0/go.mod h1:aXTfST94tswhWEb+5QjlSqG+cZlmyXy/u8jFpor3WqQ=
|
||||
go.opentelemetry.io/otel/sdk v1.30.0 h1:cHdik6irO49R5IysVhdn8oaiR9m8XluDaJAs4DfOrYE=
|
||||
go.opentelemetry.io/otel/sdk v1.30.0/go.mod h1:p14X4Ok8S+sygzblytT1nqG98QG2KYKv++HE0LY/mhg=
|
||||
go.opentelemetry.io/otel/trace v1.30.0 h1:7UBkkYzeg3C7kQX8VAidWh2biiQbtAKjyIML8dQ9wmc=
|
||||
go.opentelemetry.io/otel/trace v1.30.0/go.mod h1:5EyKqTzzmyqB9bwtCCq6pDLktPK6fmGf/Dph+8VI02o=
|
||||
go.opentelemetry.io/proto/otlp v1.3.1 h1:TrMUixzpM0yuc/znrFTP9MMRh8trP93mkCiDVeXrui0=
|
||||
go.opentelemetry.io/proto/otlp v1.3.1/go.mod h1:0X1WI4de4ZsLrrJNLAQbFeLCm3T7yBkR0XqQ7niQU+8=
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.29.0 h1:5ORfpBpCs4HzDYoodCDBbwHzdR5UrLBZ3sOnUJmFoHo=
|
||||
golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0=
|
||||
golang.org/x/net v0.0.0-20200707034311-ab3426394381 h1:VXak5I6aEWmAXeQjA+QSZzlgNrpq9mjcfDemuexIKsU=
|
||||
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34=
|
||||
golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd h1:xhmwyvizuTgC2qz7ZlMluP20uW+C3Rm0FD/WLDX8884=
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200806125547-5acd03effb82 h1:6cBnXxYO+CiRVrChvCosSv7magqTPbyAgz1M8iOv5wM=
|
||||
golang.org/x/sys v0.0.0-20200806125547-5acd03effb82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224=
|
||||
golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
|
||||
golang.org/x/time v0.6.0 h1:eTDhh4ZXt5Qf0augr54TN6suAUudPcawVZeIAPU7D4U=
|
||||
golang.org/x/time v0.6.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20240903143218-8af14fe29dc1 h1:hjSy6tcFQZ171igDaN5QHOw2n6vx40juYbC/x67CEhc=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20240903143218-8af14fe29dc1/go.mod h1:qpvKtACPCQhAdu3PyQgV4l3LMXZEtft7y8QcarRsp9I=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1 h1:pPJltXNxVzT4pK9yD8vR9X75DaWYYmLGMsEvBfFQZzQ=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU=
|
||||
google.golang.org/grpc v1.66.1 h1:hO5qAXR19+/Z44hmvIM4dQFMSYX9XcWsByfoxutBpAM=
|
||||
google.golang.org/grpc v1.66.1/go.mod h1:s3/l6xSSCURdVfAnL+TqCNMyTDAGN6+lZeVxnZR128Y=
|
||||
google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
|
||||
google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gotest.tools/v3 v3.5.1 h1:EENdUnS3pdur5nybKYIh2Vfgc8IUNBjxDPSjtiJcOzU=
|
||||
gotest.tools/v3 v3.5.1/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55 h1:gSJIx1SDwno+2ElGhA4+qG2zF97qiUzTM+rQ0klBOcE=
|
||||
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
|
||||
google.golang.org/genproto v0.0.0-20200806141610-86f49bd18e98 h1:LCO0fg4kb6WwkXQXRQQgUYsFeFb5taTX5WAx5O/Vt28=
|
||||
google.golang.org/genproto v0.0.0-20200806141610-86f49bd18e98/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
|
||||
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.31.0 h1:T7P4R73V3SSDPhH7WW7ATbfViLtmamH0DKrP3f9AuDI=
|
||||
google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
|
||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
|
||||
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
|
||||
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.24.0 h1:UhZDfRO8JRQru4/+LlLE0BRKGF8L+PICnvYZmx/fEGA=
|
||||
google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
|
||||
google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c=
|
||||
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
|
@ -9,8 +9,6 @@ services:
|
||||
command: ["-watch", "10s", "-debug"]
|
||||
volumes:
|
||||
- /var/run/docker.sock:/var/run/docker.sock:ro
|
||||
environment:
|
||||
DOCKER_API_VERSION: 1.45
|
||||
|
||||
start_echoer:
|
||||
image: busybox:latest
|
||||
@ -29,4 +27,4 @@ services:
|
||||
labels:
|
||||
# Execute every minute
|
||||
- 'dockron.test.schedule=* * * * *'
|
||||
- 'dockron.test.command=echo ok | tee /result.txt && echo "Yay!"'
|
||||
- 'dockron.test.command=echo ok >> /result.txt'
|
||||
|
@ -17,31 +17,20 @@ function main() {
|
||||
echo "start" > ./exec_result.txt
|
||||
|
||||
# Clean old containers
|
||||
docker compose down || true
|
||||
docker-compose down || true
|
||||
# Start containers
|
||||
echo "Starting containers"
|
||||
docker compose up -d --build
|
||||
docker-compose up -d --build
|
||||
echo "Containers started. Sleeping for 70s to let schedules run"
|
||||
# Schedules run on the shortest interval of a minute. This should allow time
|
||||
# for the containers to start and execute once
|
||||
local seconds=$((65 - $(date +"%S")))
|
||||
echo "Containers started. Sleeping for ${seconds}s to let schedules run"
|
||||
sleep $seconds
|
||||
|
||||
sleep 70
|
||||
echo "Stopping containers"
|
||||
docker compose stop
|
||||
|
||||
# Print logs
|
||||
docker compose logs
|
||||
docker-compose stop
|
||||
|
||||
# Validate result shows minimum amount of executions
|
||||
check_results ./start_result.txt 2
|
||||
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
|
||||
|
131
main.go
131
main.go
@ -1,7 +1,6 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
@ -9,10 +8,9 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"git.iamthefij.com/iamthefij/slog"
|
||||
dockerTypes "github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/container"
|
||||
dockerClient "github.com/docker/docker/client"
|
||||
"github.com/iamthefij/dockron/slog"
|
||||
"github.com/robfig/cron/v3"
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
@ -30,15 +28,14 @@ var (
|
||||
version = "dev"
|
||||
)
|
||||
|
||||
// ContainerClient provides an interface for interracting with Docker. Makes it possible to mock in tests
|
||||
// ContainerClient provides an interface for interracting with Docker
|
||||
type ContainerClient interface {
|
||||
ContainerExecCreate(ctx context.Context, container string, config container.ExecOptions) (dockerTypes.IDResponse, error)
|
||||
ContainerExecInspect(ctx context.Context, execID string) (container.ExecInspect, error)
|
||||
ContainerExecStart(ctx context.Context, execID string, config container.ExecStartOptions) error
|
||||
ContainerExecAttach(ctx context.Context, execID string, options container.ExecAttachOptions) (dockerTypes.HijackedResponse, error)
|
||||
ContainerExecCreate(ctx context.Context, container string, config dockerTypes.ExecConfig) (dockerTypes.IDResponse, error)
|
||||
ContainerExecInspect(ctx context.Context, execID string) (dockerTypes.ContainerExecInspect, error)
|
||||
ContainerExecStart(ctx context.Context, execID string, config dockerTypes.ExecStartCheck) error
|
||||
ContainerInspect(ctx context.Context, containerID string) (dockerTypes.ContainerJSON, error)
|
||||
ContainerList(context context.Context, options container.ListOptions) ([]dockerTypes.Container, error)
|
||||
ContainerStart(context context.Context, containerID string, options container.StartOptions) error
|
||||
ContainerList(context context.Context, options dockerTypes.ContainerListOptions) ([]dockerTypes.Container, error)
|
||||
ContainerStart(context context.Context, containerID string, options dockerTypes.ContainerStartOptions) error
|
||||
}
|
||||
|
||||
// ContainerCronJob is an interface of a job to run on containers
|
||||
@ -63,18 +60,17 @@ type ContainerStartJob struct {
|
||||
// Run is executed based on the ContainerStartJob Schedule and starts the
|
||||
// container
|
||||
func (job ContainerStartJob) Run() {
|
||||
slog.Infof("Starting: %s", job.name)
|
||||
slog.Info("Starting: %s", job.name)
|
||||
|
||||
// Check if container is already running
|
||||
containerJSON, err := job.client.ContainerInspect(
|
||||
job.context,
|
||||
job.containerID,
|
||||
)
|
||||
slog.OnErrPanicf(err, "Could not get container details for job %s", job.name)
|
||||
slog.PanicOnErr(err, "Could not get container details for job %s", job.name)
|
||||
|
||||
if containerJSON.State.Running {
|
||||
slog.Warningf("%s: Container is already running. Skipping start.", job.name)
|
||||
|
||||
slog.Warning("Container is already running. Skipping %s", job.name)
|
||||
return
|
||||
}
|
||||
|
||||
@ -82,32 +78,32 @@ func (job ContainerStartJob) Run() {
|
||||
err = job.client.ContainerStart(
|
||||
job.context,
|
||||
job.containerID,
|
||||
container.StartOptions{},
|
||||
dockerTypes.ContainerStartOptions{},
|
||||
)
|
||||
slog.OnErrPanicf(err, "Could not start container for job %s", job.name)
|
||||
slog.PanicOnErr(err, "Could not start container for job %s", job.name)
|
||||
|
||||
// Check results of job
|
||||
for check := true; check; check = containerJSON.State.Running {
|
||||
slog.Debugf("%s: Still running", job.name)
|
||||
slog.Debug("Still running %s", job.name)
|
||||
|
||||
containerJSON, err = job.client.ContainerInspect(
|
||||
job.context,
|
||||
job.containerID,
|
||||
)
|
||||
slog.OnErrPanicf(err, "Could not get container details for job %s", job.name)
|
||||
slog.PanicOnErr(err, "Could not get container details for job %s", job.name)
|
||||
|
||||
time.Sleep(1 * time.Second)
|
||||
}
|
||||
slog.Debugf("%s: Done running. %+v", job.name, containerJSON.State)
|
||||
|
||||
slog.Debug("Done execing %s. %+v", job.name, containerJSON.State)
|
||||
// Log exit code if failed
|
||||
if containerJSON.State.ExitCode != 0 {
|
||||
slog.Errorf(
|
||||
"%s: Exec job exited with code %d",
|
||||
slog.Error(
|
||||
"Exec job %s existed with code %d",
|
||||
job.name,
|
||||
containerJSON.State.ExitCode,
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Name returns the name of the job
|
||||
@ -136,104 +132,72 @@ type ContainerExecJob struct {
|
||||
// Run is executed based on the ContainerStartJob Schedule and starts the
|
||||
// container
|
||||
func (job ContainerExecJob) Run() {
|
||||
slog.Infof("Execing: %s", job.name)
|
||||
slog.Info("Execing: %s", job.name)
|
||||
containerJSON, err := job.client.ContainerInspect(
|
||||
job.context,
|
||||
job.containerID,
|
||||
)
|
||||
slog.OnErrPanicf(err, "Could not get container details for job %s", job.name)
|
||||
slog.PanicOnErr(err, "Could not get container details for job %s", job.name)
|
||||
|
||||
if !containerJSON.State.Running {
|
||||
slog.Warningf("%s: Container not running. Skipping exec.", job.name)
|
||||
|
||||
slog.Warning("Container not running. Skipping %s", job.name)
|
||||
return
|
||||
}
|
||||
|
||||
execID, err := job.client.ContainerExecCreate(
|
||||
job.context,
|
||||
job.containerID,
|
||||
container.ExecOptions{
|
||||
AttachStdout: true,
|
||||
AttachStderr: true,
|
||||
dockerTypes.ExecConfig{
|
||||
Cmd: []string{"sh", "-c", strings.TrimSpace(job.shellCommand)},
|
||||
},
|
||||
)
|
||||
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)
|
||||
slog.PanicOnErr(err, "Could not create container exec job for %s", job.name)
|
||||
|
||||
err = job.client.ContainerExecStart(
|
||||
job.context,
|
||||
execID.ID,
|
||||
container.ExecStartOptions{},
|
||||
dockerTypes.ExecStartCheck{},
|
||||
)
|
||||
slog.OnErrPanicf(err, "Could not start container exec job for %s", job.name)
|
||||
slog.PanicOnErr(err, "Could not start container exec job for %s", job.name)
|
||||
|
||||
// Wait for job results
|
||||
execInfo := container.ExecInspect{Running: true}
|
||||
execInfo := dockerTypes.ContainerExecInspect{Running: true}
|
||||
for execInfo.Running {
|
||||
time.Sleep(1 * time.Second)
|
||||
|
||||
slog.Debugf("Still execing %s", job.name)
|
||||
slog.Debug("Still execing %s", job.name)
|
||||
execInfo, err = job.client.ContainerExecInspect(
|
||||
job.context,
|
||||
execID.ID,
|
||||
)
|
||||
|
||||
// Maybe print output
|
||||
if hj.Reader != nil {
|
||||
for scanner.Scan() {
|
||||
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)
|
||||
|
||||
slog.Debug("Exec info: %+v", execInfo)
|
||||
if err != nil {
|
||||
// Nothing we can do if we got an error here, so let's go
|
||||
slog.OnErrWarnf(err, "%s: Could not get status for exec job", job.name)
|
||||
|
||||
slog.WarnOnErr(err, "Could not get status for exec job %s", job.name)
|
||||
return
|
||||
}
|
||||
time.Sleep(1 * time.Second)
|
||||
}
|
||||
slog.Debugf("%s: Done execing. %+v", job.name, execInfo)
|
||||
slog.Debug("Done execing %s. %+v", job.name, execInfo)
|
||||
// Log exit code if failed
|
||||
if execInfo.ExitCode != 0 {
|
||||
slog.Errorf("%s: Exec job existed with code %d", job.name, execInfo.ExitCode)
|
||||
slog.Error("Exec job %s existed with code %d", job.name, execInfo.ExitCode)
|
||||
}
|
||||
}
|
||||
|
||||
// QueryScheduledJobs queries Docker for all containers with a schedule and
|
||||
// returns a list of ContainerCronJob records to be scheduled
|
||||
func QueryScheduledJobs(client ContainerClient) (jobs []ContainerCronJob) {
|
||||
slog.Debugf("Scanning containers for new schedules...")
|
||||
slog.Debug("Scanning containers for new schedules...")
|
||||
|
||||
containers, err := client.ContainerList(
|
||||
context.Background(),
|
||||
container.ListOptions{All: true},
|
||||
dockerTypes.ContainerListOptions{All: true},
|
||||
)
|
||||
slog.OnErrPanicf(err, "Failure querying docker containers")
|
||||
slog.PanicOnErr(err, "Failure querying docker containers")
|
||||
|
||||
for _, container := range containers {
|
||||
// Add start job
|
||||
if val, ok := container.Labels[schedLabel]; ok {
|
||||
jobName := strings.Join(container.Names, "/")
|
||||
|
||||
jobs = append(jobs, ContainerStartJob{
|
||||
client: client,
|
||||
containerID: container.ID,
|
||||
@ -245,12 +209,9 @@ func QueryScheduledJobs(client ContainerClient) (jobs []ContainerCronJob) {
|
||||
|
||||
// Add exec jobs
|
||||
execJobs := map[string]map[string]string{}
|
||||
|
||||
for label, value := range container.Labels {
|
||||
results := execLabelRegexp.FindStringSubmatch(label)
|
||||
expectedLabelParts := 3
|
||||
|
||||
if len(results) == expectedLabelParts {
|
||||
if len(results) == 3 {
|
||||
// We've got part of a new job
|
||||
jobName, jobField := results[1], results[2]
|
||||
if partJob, ok := execJobs[jobName]; ok {
|
||||
@ -264,18 +225,15 @@ func QueryScheduledJobs(client ContainerClient) (jobs []ContainerCronJob) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for jobName, jobConfig := range execJobs {
|
||||
schedule, ok := jobConfig["schedule"]
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
shellCommand, ok := jobConfig["command"]
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
jobs = append(jobs, ContainerExecJob{
|
||||
ContainerStartJob: ContainerStartJob{
|
||||
client: client,
|
||||
@ -289,7 +247,7 @@ func QueryScheduledJobs(client ContainerClient) (jobs []ContainerCronJob) {
|
||||
}
|
||||
}
|
||||
|
||||
return jobs
|
||||
return
|
||||
}
|
||||
|
||||
// ScheduleJobs accepts a Cron instance and a list of jobs to schedule.
|
||||
@ -306,16 +264,15 @@ func ScheduleJobs(c *cron.Cron, jobs []ContainerCronJob) {
|
||||
if _, ok := existingJobs[job.UniqueName()]; ok {
|
||||
// Job already exists, remove it from existing jobs so we don't
|
||||
// unschedule it later
|
||||
slog.Debugf("Job %s is already scheduled. Skipping", job.Name())
|
||||
slog.Debug("Job %s is already scheduled. Skipping", job.Name())
|
||||
delete(existingJobs, job.UniqueName())
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
// Job doesn't exist yet, schedule it
|
||||
_, err := c.AddJob(job.Schedule(), job)
|
||||
if err == nil {
|
||||
slog.Infof(
|
||||
slog.Info(
|
||||
"Scheduled %s (%s) with schedule '%s'\n",
|
||||
job.Name(),
|
||||
job.UniqueName(),
|
||||
@ -323,7 +280,7 @@ func ScheduleJobs(c *cron.Cron, jobs []ContainerCronJob) {
|
||||
)
|
||||
} else {
|
||||
// TODO: Track something for a healthcheck here
|
||||
slog.Errorf(
|
||||
slog.Error(
|
||||
"Could not schedule %s (%s) with schedule '%s'. %v\n",
|
||||
job.Name(),
|
||||
job.UniqueName(),
|
||||
@ -342,14 +299,14 @@ func ScheduleJobs(c *cron.Cron, jobs []ContainerCronJob) {
|
||||
func main() {
|
||||
// Get a Docker Client
|
||||
client, err := dockerClient.NewClientWithOpts(dockerClient.FromEnv)
|
||||
slog.OnErrPanicf(err, "Could not create Docker client")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// Read interval for polling Docker
|
||||
var watchInterval time.Duration
|
||||
|
||||
showVersion := flag.Bool("version", false, "Display the version of dockron and exit")
|
||||
|
||||
flag.DurationVar(&watchInterval, "watch", defaultWatchInterval, "Interval used to poll Docker for changes")
|
||||
var showVersion = flag.Bool("version", false, "Display the version of dockron and exit")
|
||||
flag.BoolVar(&slog.DebugLevel, "debug", false, "Show debug logs")
|
||||
flag.Parse()
|
||||
|
||||
|
281
main_test.go
281
main_test.go
@ -1,17 +1,13 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"reflect"
|
||||
"sort"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
dockerTypes "github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"github.com/robfig/cron/v3"
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
@ -33,8 +29,6 @@ var (
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
errGeneric = errors.New("error")
|
||||
)
|
||||
|
||||
// FakeCall represents a faked method call
|
||||
@ -51,8 +45,6 @@ type FakeDockerClient struct {
|
||||
|
||||
// AssertFakeCalls checks expected against actual calls to fake methods
|
||||
func (fakeClient FakeDockerClient) AssertFakeCalls(t *testing.T, expectedCalls map[string][]FakeCall, message string) {
|
||||
t.Helper()
|
||||
|
||||
if !reflect.DeepEqual(fakeClient.FakeCalls, expectedCalls) {
|
||||
t.Errorf(
|
||||
"%s: Expected and actual calls do not match. Expected %+v Actual %+v",
|
||||
@ -78,60 +70,52 @@ func (fakeClient *FakeDockerClient) called(method string, v ...interface{}) Fake
|
||||
return results
|
||||
}
|
||||
|
||||
func (fakeClient *FakeDockerClient) ContainerStart(context context.Context, containerID string, options container.StartOptions) (e error) {
|
||||
func (fakeClient *FakeDockerClient) ContainerStart(context context.Context, containerID string, options dockerTypes.ContainerStartOptions) (e error) {
|
||||
results := fakeClient.called("ContainerStart", context, containerID, options)
|
||||
if results[0] != nil {
|
||||
e = results[0].(error)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (fakeClient *FakeDockerClient) ContainerList(context context.Context, options container.ListOptions) (c []dockerTypes.Container, e error) {
|
||||
func (fakeClient *FakeDockerClient) ContainerList(context context.Context, options dockerTypes.ContainerListOptions) (c []dockerTypes.Container, e error) {
|
||||
results := fakeClient.called("ContainerList", context, options)
|
||||
if results[0] != nil {
|
||||
c = results[0].([]dockerTypes.Container)
|
||||
}
|
||||
|
||||
if results[1] != nil {
|
||||
e = results[1].(error)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (fakeClient *FakeDockerClient) ContainerExecCreate(ctx context.Context, container string, config container.ExecOptions) (r dockerTypes.IDResponse, e error) {
|
||||
func (fakeClient *FakeDockerClient) ContainerExecCreate(ctx context.Context, container string, config dockerTypes.ExecConfig) (r dockerTypes.IDResponse, e error) {
|
||||
results := fakeClient.called("ContainerExecCreate", ctx, container, config)
|
||||
if results[0] != nil {
|
||||
r = results[0].(dockerTypes.IDResponse)
|
||||
}
|
||||
|
||||
if results[1] != nil {
|
||||
e = results[1].(error)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (fakeClient *FakeDockerClient) ContainerExecStart(ctx context.Context, execID string, config container.ExecStartOptions) (e error) {
|
||||
func (fakeClient *FakeDockerClient) ContainerExecStart(ctx context.Context, execID string, config dockerTypes.ExecStartCheck) (e error) {
|
||||
results := fakeClient.called("ContainerExecStart", ctx, execID, config)
|
||||
if results[0] != nil {
|
||||
e = results[0].(error)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (fakeClient *FakeDockerClient) ContainerExecInspect(ctx context.Context, execID string) (r container.ExecInspect, e error) {
|
||||
func (fakeClient *FakeDockerClient) ContainerExecInspect(ctx context.Context, execID string) (r dockerTypes.ContainerExecInspect, e error) {
|
||||
results := fakeClient.called("ContainerExecInspect", ctx, execID)
|
||||
if results[0] != nil {
|
||||
r = results[0].(container.ExecInspect)
|
||||
r = results[0].(dockerTypes.ContainerExecInspect)
|
||||
}
|
||||
|
||||
if results[1] != nil {
|
||||
e = results[1].(error)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
@ -140,20 +124,12 @@ func (fakeClient *FakeDockerClient) ContainerInspect(ctx context.Context, contai
|
||||
if results[0] != nil {
|
||||
r = results[0].(dockerTypes.ContainerJSON)
|
||||
}
|
||||
|
||||
if results[1] != nil {
|
||||
e = results[1].(error)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (fakeClient *FakeDockerClient) ContainerExecAttach(ctx context.Context, execID string, options container.ExecAttachOptions) (dockerTypes.HijackedResponse, error) {
|
||||
return dockerTypes.HijackedResponse{
|
||||
Reader: bufio.NewReader(strings.NewReader("Some output from our command")),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// NewFakeDockerClient creates an empty client
|
||||
func NewFakeDockerClient() *FakeDockerClient {
|
||||
return &FakeDockerClient{
|
||||
@ -163,8 +139,6 @@ func NewFakeDockerClient() *FakeDockerClient {
|
||||
|
||||
// ErrorUnequal checks that two values are equal and fails the test if not
|
||||
func ErrorUnequal(t *testing.T, expected interface{}, actual interface{}, message string) {
|
||||
t.Helper()
|
||||
|
||||
if expected != actual {
|
||||
t.Errorf("%s Expected: %+v Actual: %+v", message, expected, actual)
|
||||
}
|
||||
@ -188,7 +162,7 @@ func TestQueryScheduledJobs(t *testing.T) {
|
||||
{
|
||||
name: "One container without schedule",
|
||||
fakeContainers: []dockerTypes.Container{
|
||||
{
|
||||
dockerTypes.Container{
|
||||
Names: []string{"no_schedule_1"},
|
||||
ID: "no_schedule_1",
|
||||
},
|
||||
@ -198,7 +172,7 @@ func TestQueryScheduledJobs(t *testing.T) {
|
||||
{
|
||||
name: "One container with schedule",
|
||||
fakeContainers: []dockerTypes.Container{
|
||||
{
|
||||
dockerTypes.Container{
|
||||
Names: []string{"has_schedule_1"},
|
||||
ID: "has_schedule_1",
|
||||
Labels: map[string]string{
|
||||
@ -219,11 +193,11 @@ func TestQueryScheduledJobs(t *testing.T) {
|
||||
{
|
||||
name: "One container with and one without schedule",
|
||||
fakeContainers: []dockerTypes.Container{
|
||||
{
|
||||
dockerTypes.Container{
|
||||
Names: []string{"no_schedule_1"},
|
||||
ID: "no_schedule_1",
|
||||
},
|
||||
{
|
||||
dockerTypes.Container{
|
||||
Names: []string{"has_schedule_1"},
|
||||
ID: "has_schedule_1",
|
||||
Labels: map[string]string{
|
||||
@ -244,7 +218,7 @@ func TestQueryScheduledJobs(t *testing.T) {
|
||||
{
|
||||
name: "Incomplete exec job, schedule only",
|
||||
fakeContainers: []dockerTypes.Container{
|
||||
{
|
||||
dockerTypes.Container{
|
||||
Names: []string{"exec_job_1"},
|
||||
ID: "exec_job_1",
|
||||
Labels: map[string]string{
|
||||
@ -257,7 +231,7 @@ func TestQueryScheduledJobs(t *testing.T) {
|
||||
{
|
||||
name: "Incomplete exec job, command only",
|
||||
fakeContainers: []dockerTypes.Container{
|
||||
{
|
||||
dockerTypes.Container{
|
||||
Names: []string{"exec_job_1"},
|
||||
ID: "exec_job_1",
|
||||
Labels: map[string]string{
|
||||
@ -270,7 +244,7 @@ func TestQueryScheduledJobs(t *testing.T) {
|
||||
{
|
||||
name: "Complete exec job",
|
||||
fakeContainers: []dockerTypes.Container{
|
||||
{
|
||||
dockerTypes.Container{
|
||||
Names: []string{"exec_job_1"},
|
||||
ID: "exec_job_1",
|
||||
Labels: map[string]string{
|
||||
@ -295,7 +269,7 @@ func TestQueryScheduledJobs(t *testing.T) {
|
||||
{
|
||||
name: "Dual exec jobs on single container",
|
||||
fakeContainers: []dockerTypes.Container{
|
||||
{
|
||||
dockerTypes.Container{
|
||||
Names: []string{"exec_job_1"},
|
||||
ID: "exec_job_1",
|
||||
Labels: map[string]string{
|
||||
@ -338,7 +312,7 @@ func TestQueryScheduledJobs(t *testing.T) {
|
||||
// Load fake containers
|
||||
t.Logf("Fake containers: %+v", c.fakeContainers)
|
||||
client.FakeResults["ContainerList"] = []FakeResult{
|
||||
{c.fakeContainers, nil},
|
||||
FakeResult{c.fakeContainers, nil},
|
||||
}
|
||||
|
||||
jobs := QueryScheduledJobs(client)
|
||||
@ -349,7 +323,6 @@ func TestQueryScheduledJobs(t *testing.T) {
|
||||
|
||||
t.Logf("Expected jobs: %+v, Actual jobs: %+v", c.expectedJobs, jobs)
|
||||
ErrorUnequal(t, len(c.expectedJobs), len(jobs), "Job lengths don't match")
|
||||
|
||||
for i, job := range jobs {
|
||||
ErrorUnequal(t, c.expectedJobs[i], job, "Job value does not match")
|
||||
}
|
||||
@ -458,7 +431,6 @@ func TestScheduleJobs(t *testing.T) {
|
||||
t.Logf("Cron entries: %+v", scheduledEntries)
|
||||
|
||||
ErrorUnequal(t, len(c.expectedJobs), len(scheduledEntries), "Job and entry lengths don't match")
|
||||
|
||||
for i, entry := range scheduledEntries {
|
||||
ErrorUnequal(t, c.expectedJobs[i], entry.Job, "Job value does not match entry")
|
||||
}
|
||||
@ -487,7 +459,7 @@ func TestDoLoop(t *testing.T) {
|
||||
{
|
||||
name: "One container without schedule",
|
||||
fakeContainers: []dockerTypes.Container{
|
||||
{
|
||||
dockerTypes.Container{
|
||||
Names: []string{"no_schedule_1"},
|
||||
ID: "no_schedule_1",
|
||||
},
|
||||
@ -497,7 +469,7 @@ func TestDoLoop(t *testing.T) {
|
||||
{
|
||||
name: "One container with schedule",
|
||||
fakeContainers: []dockerTypes.Container{
|
||||
{
|
||||
dockerTypes.Container{
|
||||
Names: []string{"has_schedule_1"},
|
||||
ID: "has_schedule_1",
|
||||
Labels: map[string]string{
|
||||
@ -518,11 +490,11 @@ func TestDoLoop(t *testing.T) {
|
||||
{
|
||||
name: "One container with and one without schedule",
|
||||
fakeContainers: []dockerTypes.Container{
|
||||
{
|
||||
dockerTypes.Container{
|
||||
Names: []string{"no_schedule_1"},
|
||||
ID: "no_schedule_1",
|
||||
},
|
||||
{
|
||||
dockerTypes.Container{
|
||||
Names: []string{"has_schedule_1"},
|
||||
ID: "has_schedule_1",
|
||||
Labels: map[string]string{
|
||||
@ -543,14 +515,14 @@ func TestDoLoop(t *testing.T) {
|
||||
{
|
||||
name: "Add a second container with a schedule",
|
||||
fakeContainers: []dockerTypes.Container{
|
||||
{
|
||||
dockerTypes.Container{
|
||||
Names: []string{"has_schedule_1"},
|
||||
ID: "has_schedule_1",
|
||||
Labels: map[string]string{
|
||||
"dockron.schedule": "* * * * *",
|
||||
},
|
||||
},
|
||||
{
|
||||
dockerTypes.Container{
|
||||
Names: []string{"has_schedule_2"},
|
||||
ID: "has_schedule_2",
|
||||
Labels: map[string]string{
|
||||
@ -578,14 +550,14 @@ func TestDoLoop(t *testing.T) {
|
||||
{
|
||||
name: "Modify the first container",
|
||||
fakeContainers: []dockerTypes.Container{
|
||||
{
|
||||
dockerTypes.Container{
|
||||
Names: []string{"has_schedule_1"},
|
||||
ID: "has_schedule_1_prime",
|
||||
Labels: map[string]string{
|
||||
"dockron.schedule": "* * * * *",
|
||||
},
|
||||
},
|
||||
{
|
||||
dockerTypes.Container{
|
||||
Names: []string{"has_schedule_2"},
|
||||
ID: "has_schedule_2",
|
||||
Labels: map[string]string{
|
||||
@ -613,7 +585,7 @@ func TestDoLoop(t *testing.T) {
|
||||
{
|
||||
name: "Remove second container and add exec to first",
|
||||
fakeContainers: []dockerTypes.Container{
|
||||
{
|
||||
dockerTypes.Container{
|
||||
Names: []string{"has_schedule_1"},
|
||||
ID: "has_schedule_1_prime",
|
||||
Labels: map[string]string{
|
||||
@ -652,7 +624,7 @@ func TestDoLoop(t *testing.T) {
|
||||
// Load fake containers
|
||||
t.Logf("Fake containers: %+v", c.fakeContainers)
|
||||
client.FakeResults["ContainerList"] = []FakeResult{
|
||||
{c.fakeContainers, nil},
|
||||
FakeResult{c.fakeContainers, nil},
|
||||
}
|
||||
|
||||
// Execute loop iteration loop
|
||||
@ -665,7 +637,6 @@ func TestDoLoop(t *testing.T) {
|
||||
t.Logf("Cron entries: %+v", scheduledEntries)
|
||||
|
||||
ErrorUnequal(t, len(c.expectedJobs), len(scheduledEntries), "Job and entry lengths don't match")
|
||||
|
||||
for i, entry := range scheduledEntries {
|
||||
ErrorUnequal(t, c.expectedJobs[i], entry.Job, "Job value does not match entry")
|
||||
}
|
||||
@ -682,8 +653,8 @@ func TestDoLoop(t *testing.T) {
|
||||
// to a subpackage that offers a single function for interfacing with the
|
||||
// Docker client to start or exec a container so that Dockron needn't care.
|
||||
func TestRunExecJobs(t *testing.T) {
|
||||
var jobContext context.Context
|
||||
|
||||
var jobContext context.Context
|
||||
jobContainerID := "container_id"
|
||||
jobCommand := "true"
|
||||
|
||||
@ -697,13 +668,13 @@ func TestRunExecJobs(t *testing.T) {
|
||||
name: "Initial inspect call raises error",
|
||||
client: &FakeDockerClient{
|
||||
FakeResults: map[string][]FakeResult{
|
||||
"ContainerInspect": {
|
||||
{nil, errGeneric},
|
||||
"ContainerInspect": []FakeResult{
|
||||
FakeResult{nil, fmt.Errorf("error")},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedCalls: map[string][]FakeCall{
|
||||
"ContainerInspect": {
|
||||
"ContainerInspect": []FakeCall{
|
||||
FakeCall{jobContext, jobContainerID},
|
||||
},
|
||||
},
|
||||
@ -713,14 +684,14 @@ func TestRunExecJobs(t *testing.T) {
|
||||
name: "Handle container not running",
|
||||
client: &FakeDockerClient{
|
||||
FakeResults: map[string][]FakeResult{
|
||||
"ContainerInspect": {
|
||||
{stoppedContainerInfo, nil},
|
||||
"ContainerInspect": []FakeResult{
|
||||
FakeResult{stoppedContainerInfo, nil},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedCalls: map[string][]FakeCall{
|
||||
"ContainerInspect": {
|
||||
{jobContext, jobContainerID},
|
||||
"ContainerInspect": []FakeCall{
|
||||
FakeCall{jobContext, jobContainerID},
|
||||
},
|
||||
},
|
||||
},
|
||||
@ -728,25 +699,23 @@ func TestRunExecJobs(t *testing.T) {
|
||||
name: "Handle error creating exec",
|
||||
client: &FakeDockerClient{
|
||||
FakeResults: map[string][]FakeResult{
|
||||
"ContainerInspect": {
|
||||
{runningContainerInfo, nil},
|
||||
"ContainerInspect": []FakeResult{
|
||||
FakeResult{runningContainerInfo, nil},
|
||||
},
|
||||
"ContainerExecCreate": {
|
||||
{nil, errGeneric},
|
||||
"ContainerExecCreate": []FakeResult{
|
||||
FakeResult{nil, fmt.Errorf("fail")},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedCalls: map[string][]FakeCall{
|
||||
"ContainerInspect": {
|
||||
{jobContext, jobContainerID},
|
||||
"ContainerInspect": []FakeCall{
|
||||
FakeCall{jobContext, jobContainerID},
|
||||
},
|
||||
"ContainerExecCreate": {
|
||||
{
|
||||
"ContainerExecCreate": []FakeCall{
|
||||
FakeCall{
|
||||
jobContext,
|
||||
jobContainerID,
|
||||
container.ExecOptions{
|
||||
AttachStdout: true,
|
||||
AttachStderr: true,
|
||||
dockerTypes.ExecConfig{
|
||||
Cmd: []string{"sh", "-c", jobCommand},
|
||||
},
|
||||
},
|
||||
@ -758,34 +727,32 @@ func TestRunExecJobs(t *testing.T) {
|
||||
name: "Fail starting exec container",
|
||||
client: &FakeDockerClient{
|
||||
FakeResults: map[string][]FakeResult{
|
||||
"ContainerInspect": {
|
||||
{runningContainerInfo, nil},
|
||||
"ContainerInspect": []FakeResult{
|
||||
FakeResult{runningContainerInfo, nil},
|
||||
},
|
||||
"ContainerExecCreate": {
|
||||
{dockerTypes.IDResponse{ID: "id"}, nil},
|
||||
"ContainerExecCreate": []FakeResult{
|
||||
FakeResult{dockerTypes.IDResponse{ID: "id"}, nil},
|
||||
},
|
||||
"ContainerExecStart": {
|
||||
{errGeneric},
|
||||
"ContainerExecStart": []FakeResult{
|
||||
FakeResult{fmt.Errorf("fail")},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedCalls: map[string][]FakeCall{
|
||||
"ContainerInspect": {
|
||||
{jobContext, jobContainerID},
|
||||
"ContainerInspect": []FakeCall{
|
||||
FakeCall{jobContext, jobContainerID},
|
||||
},
|
||||
"ContainerExecCreate": {
|
||||
{
|
||||
"ContainerExecCreate": []FakeCall{
|
||||
FakeCall{
|
||||
jobContext,
|
||||
jobContainerID,
|
||||
container.ExecOptions{
|
||||
AttachStdout: true,
|
||||
AttachStderr: true,
|
||||
dockerTypes.ExecConfig{
|
||||
Cmd: []string{"sh", "-c", jobCommand},
|
||||
},
|
||||
},
|
||||
},
|
||||
"ContainerExecStart": {
|
||||
{jobContext, "id", container.ExecStartOptions{}},
|
||||
"ContainerExecStart": []FakeCall{
|
||||
FakeCall{jobContext, "id", dockerTypes.ExecStartCheck{}},
|
||||
},
|
||||
},
|
||||
expectPanic: true,
|
||||
@ -794,40 +761,38 @@ func TestRunExecJobs(t *testing.T) {
|
||||
name: "Successfully start an exec job fail on status",
|
||||
client: &FakeDockerClient{
|
||||
FakeResults: map[string][]FakeResult{
|
||||
"ContainerInspect": {
|
||||
{runningContainerInfo, nil},
|
||||
"ContainerInspect": []FakeResult{
|
||||
FakeResult{runningContainerInfo, nil},
|
||||
},
|
||||
"ContainerExecCreate": {
|
||||
{dockerTypes.IDResponse{ID: "id"}, nil},
|
||||
"ContainerExecCreate": []FakeResult{
|
||||
FakeResult{dockerTypes.IDResponse{ID: "id"}, nil},
|
||||
},
|
||||
"ContainerExecStart": {
|
||||
{nil},
|
||||
"ContainerExecStart": []FakeResult{
|
||||
FakeResult{nil},
|
||||
},
|
||||
"ContainerExecInspect": {
|
||||
{nil, errGeneric},
|
||||
"ContainerExecInspect": []FakeResult{
|
||||
FakeResult{nil, fmt.Errorf("fail")},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedCalls: map[string][]FakeCall{
|
||||
"ContainerInspect": {
|
||||
{jobContext, jobContainerID},
|
||||
"ContainerInspect": []FakeCall{
|
||||
FakeCall{jobContext, jobContainerID},
|
||||
},
|
||||
"ContainerExecCreate": {
|
||||
{
|
||||
"ContainerExecCreate": []FakeCall{
|
||||
FakeCall{
|
||||
jobContext,
|
||||
jobContainerID,
|
||||
container.ExecOptions{
|
||||
AttachStdout: true,
|
||||
AttachStderr: true,
|
||||
dockerTypes.ExecConfig{
|
||||
Cmd: []string{"sh", "-c", jobCommand},
|
||||
},
|
||||
},
|
||||
},
|
||||
"ContainerExecStart": {
|
||||
{jobContext, "id", container.ExecStartOptions{}},
|
||||
"ContainerExecStart": []FakeCall{
|
||||
FakeCall{jobContext, "id", dockerTypes.ExecStartCheck{}},
|
||||
},
|
||||
"ContainerExecInspect": {
|
||||
{jobContext, "id"},
|
||||
"ContainerExecInspect": []FakeCall{
|
||||
FakeCall{jobContext, "id"},
|
||||
},
|
||||
},
|
||||
},
|
||||
@ -835,42 +800,40 @@ func TestRunExecJobs(t *testing.T) {
|
||||
name: "Successfully start an exec job and run to completion",
|
||||
client: &FakeDockerClient{
|
||||
FakeResults: map[string][]FakeResult{
|
||||
"ContainerInspect": {
|
||||
{runningContainerInfo, nil},
|
||||
"ContainerInspect": []FakeResult{
|
||||
FakeResult{runningContainerInfo, nil},
|
||||
},
|
||||
"ContainerExecCreate": {
|
||||
{dockerTypes.IDResponse{ID: "id"}, nil},
|
||||
"ContainerExecCreate": []FakeResult{
|
||||
FakeResult{dockerTypes.IDResponse{ID: "id"}, nil},
|
||||
},
|
||||
"ContainerExecStart": {
|
||||
{nil},
|
||||
"ContainerExecStart": []FakeResult{
|
||||
FakeResult{nil},
|
||||
},
|
||||
"ContainerExecInspect": {
|
||||
{container.ExecInspect{Running: true}, nil},
|
||||
{container.ExecInspect{Running: false}, nil},
|
||||
"ContainerExecInspect": []FakeResult{
|
||||
FakeResult{dockerTypes.ContainerExecInspect{Running: true}, nil},
|
||||
FakeResult{dockerTypes.ContainerExecInspect{Running: false}, nil},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedCalls: map[string][]FakeCall{
|
||||
"ContainerInspect": {
|
||||
{jobContext, jobContainerID},
|
||||
"ContainerInspect": []FakeCall{
|
||||
FakeCall{jobContext, jobContainerID},
|
||||
},
|
||||
"ContainerExecCreate": {
|
||||
{
|
||||
"ContainerExecCreate": []FakeCall{
|
||||
FakeCall{
|
||||
jobContext,
|
||||
jobContainerID,
|
||||
container.ExecOptions{
|
||||
AttachStdout: true,
|
||||
AttachStderr: true,
|
||||
dockerTypes.ExecConfig{
|
||||
Cmd: []string{"sh", "-c", jobCommand},
|
||||
},
|
||||
},
|
||||
},
|
||||
"ContainerExecStart": {
|
||||
{jobContext, "id", container.ExecStartOptions{}},
|
||||
"ContainerExecStart": []FakeCall{
|
||||
FakeCall{jobContext, "id", dockerTypes.ExecStartCheck{}},
|
||||
},
|
||||
"ContainerExecInspect": {
|
||||
{jobContext, "id"},
|
||||
{jobContext, "id"},
|
||||
"ContainerExecInspect": []FakeCall{
|
||||
FakeCall{jobContext, "id"},
|
||||
FakeCall{jobContext, "id"},
|
||||
},
|
||||
},
|
||||
},
|
||||
@ -897,11 +860,9 @@ func TestRunExecJobs(t *testing.T) {
|
||||
t.Log("Recovered from panic")
|
||||
t.Log(err)
|
||||
}
|
||||
|
||||
c.client.AssertFakeCalls(t, c.expectedCalls, "Failed")
|
||||
}()
|
||||
job.Run()
|
||||
|
||||
if c.expectPanic {
|
||||
t.Errorf("Expected panic but got none")
|
||||
}
|
||||
@ -916,7 +877,6 @@ func TestRunExecJobs(t *testing.T) {
|
||||
// Docker client to start or exec a container so that Dockron needn't care.
|
||||
func TestRunStartJobs(t *testing.T) {
|
||||
var jobContext context.Context
|
||||
|
||||
jobContainerID := "container_id"
|
||||
|
||||
cases := []struct {
|
||||
@ -929,14 +889,14 @@ func TestRunStartJobs(t *testing.T) {
|
||||
name: "Initial inspect call raises error",
|
||||
client: &FakeDockerClient{
|
||||
FakeResults: map[string][]FakeResult{
|
||||
"ContainerInspect": {
|
||||
{nil, errGeneric},
|
||||
"ContainerInspect": []FakeResult{
|
||||
FakeResult{nil, fmt.Errorf("error")},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedCalls: map[string][]FakeCall{
|
||||
"ContainerInspect": {
|
||||
{jobContext, jobContainerID},
|
||||
"ContainerInspect": []FakeCall{
|
||||
FakeCall{jobContext, jobContainerID},
|
||||
},
|
||||
},
|
||||
expectPanic: true,
|
||||
@ -945,14 +905,14 @@ func TestRunStartJobs(t *testing.T) {
|
||||
name: "Handle container already running",
|
||||
client: &FakeDockerClient{
|
||||
FakeResults: map[string][]FakeResult{
|
||||
"ContainerInspect": {
|
||||
{runningContainerInfo, nil},
|
||||
"ContainerInspect": []FakeResult{
|
||||
FakeResult{runningContainerInfo, nil},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedCalls: map[string][]FakeCall{
|
||||
"ContainerInspect": {
|
||||
{jobContext, jobContainerID},
|
||||
"ContainerInspect": []FakeCall{
|
||||
FakeCall{jobContext, jobContainerID},
|
||||
},
|
||||
},
|
||||
},
|
||||
@ -960,41 +920,41 @@ func TestRunStartJobs(t *testing.T) {
|
||||
name: "Handle error starting container",
|
||||
client: &FakeDockerClient{
|
||||
FakeResults: map[string][]FakeResult{
|
||||
"ContainerInspect": {
|
||||
{stoppedContainerInfo, nil},
|
||||
"ContainerInspect": []FakeResult{
|
||||
FakeResult{stoppedContainerInfo, nil},
|
||||
},
|
||||
"ContainerStart": {{errGeneric}},
|
||||
"ContainerStart": []FakeResult{FakeResult{fmt.Errorf("fail")}},
|
||||
},
|
||||
},
|
||||
expectedCalls: map[string][]FakeCall{
|
||||
"ContainerInspect": {
|
||||
{jobContext, jobContainerID},
|
||||
"ContainerInspect": []FakeCall{
|
||||
FakeCall{jobContext, jobContainerID},
|
||||
},
|
||||
"ContainerStart": {
|
||||
{jobContext, jobContainerID, container.StartOptions{}},
|
||||
"ContainerStart": []FakeCall{
|
||||
FakeCall{jobContext, jobContainerID, dockerTypes.ContainerStartOptions{}},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Successfully start a container",
|
||||
name: "Succesfully start a container",
|
||||
client: &FakeDockerClient{
|
||||
FakeResults: map[string][]FakeResult{
|
||||
"ContainerInspect": {
|
||||
{stoppedContainerInfo, nil},
|
||||
{runningContainerInfo, nil},
|
||||
{stoppedContainerInfo, nil},
|
||||
"ContainerInspect": []FakeResult{
|
||||
FakeResult{stoppedContainerInfo, nil},
|
||||
FakeResult{runningContainerInfo, nil},
|
||||
FakeResult{stoppedContainerInfo, nil},
|
||||
},
|
||||
"ContainerStart": {{nil}},
|
||||
"ContainerStart": []FakeResult{FakeResult{nil}},
|
||||
},
|
||||
},
|
||||
expectedCalls: map[string][]FakeCall{
|
||||
"ContainerInspect": {
|
||||
{jobContext, jobContainerID},
|
||||
{jobContext, jobContainerID},
|
||||
{jobContext, jobContainerID},
|
||||
"ContainerInspect": []FakeCall{
|
||||
FakeCall{jobContext, jobContainerID},
|
||||
FakeCall{jobContext, jobContainerID},
|
||||
FakeCall{jobContext, jobContainerID},
|
||||
},
|
||||
"ContainerStart": {
|
||||
{jobContext, jobContainerID, container.StartOptions{}},
|
||||
"ContainerStart": []FakeCall{
|
||||
FakeCall{jobContext, jobContainerID, dockerTypes.ContainerStartOptions{}},
|
||||
},
|
||||
},
|
||||
},
|
||||
@ -1014,15 +974,14 @@ func TestRunStartJobs(t *testing.T) {
|
||||
|
||||
defer func() {
|
||||
// Recover from panics, if there were any
|
||||
_ = recover()
|
||||
|
||||
recover()
|
||||
c.client.AssertFakeCalls(t, c.expectedCalls, "Failed")
|
||||
}()
|
||||
job.Run()
|
||||
|
||||
if c.expectPanic {
|
||||
t.Errorf("Expected panic but got none")
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
}
|
||||
|
54
slog/Readme.md
Normal file
54
slog/Readme.md
Normal file
@ -0,0 +1,54 @@
|
||||
# slog
|
||||
|
||||
A super simple go logger
|
||||
|
||||
I know there are many go loggers out there that offer various logging features such as file rotation, granular verbosity settings, colored and JSON output, etc.
|
||||
|
||||
_Slog is not one of them._
|
||||
|
||||
Slog lets you hide or show debug logs as well as provides a simpler way to log messages with Warning and Error prefixes for consistency.
|
||||
|
||||
Also provided are a few simple methods for handling returned `error` variables, logging them out and optionally panicing or fatally exiting.
|
||||
|
||||
## Documentation
|
||||
package slog // import "github.com/iamthefij/dockron/slog"
|
||||
|
||||
Package slog is a super simple logger that allows a few convenience methods
|
||||
for handling debug vs warning/error logs. It also adds a few conveniences
|
||||
for handling errors.
|
||||
|
||||
VARIABLES
|
||||
|
||||
var (
|
||||
// DebugLevel indicates if we should log at the debug level
|
||||
DebugLevel = true
|
||||
)
|
||||
|
||||
FUNCTIONS
|
||||
|
||||
func Debug(format string, v ...interface{})
|
||||
Debug will log with a DEBUG prefix if DebugLevel is se
|
||||
|
||||
func Error(format string, v ...interface{})
|
||||
Error will log with a ERROR prefix
|
||||
|
||||
func FatalOnErr(err error, format string, v ...interface{})
|
||||
FatalOnErr if error provided, will log out details of an error and exi
|
||||
|
||||
func Info(format string, v ...interface{})
|
||||
Info formats logs with an INFO prefix
|
||||
|
||||
func Log(format string, v ...interface{})
|
||||
Log formats logs directly to the main logger
|
||||
|
||||
func PanicOnErr(err error, format string, v ...interface{})
|
||||
PanicOnErr if error provided, will log out details of an error and exi
|
||||
|
||||
func SetFlags(flag int)
|
||||
SetFlags allows changing the logger flags using flags found in `log`
|
||||
|
||||
func WarnOnErr(err error, format string, v ...interface{})
|
||||
WarnOnErr if error provided, will provide a warning if an error is provided
|
||||
|
||||
func Warning(format string, v ...interface{})
|
||||
Warning will log with a WARNING prefix
|
8
slog/add-docs-to-readme.sh
Executable file
8
slog/add-docs-to-readme.sh
Executable file
@ -0,0 +1,8 @@
|
||||
#! /bin/bash
|
||||
set -e
|
||||
|
||||
slogdir=$(dirname "$0")
|
||||
readme="$slogdir/Readme.md"
|
||||
|
||||
awk '/## Documentation/ {print ; exit} {print}' "$readme" > "$readme.tmp" && go doc -all slog | sed "s/^/ /;s/[ \t]*$//" >> "$readme.tmp"
|
||||
mv "$readme.tmp" "$readme"
|
90
slog/slog.go
Normal file
90
slog/slog.go
Normal file
@ -0,0 +1,90 @@
|
||||
// Package slog is a super simple logger that allows a few convenience methods
|
||||
// for handling debug vs warning/error logs. It also adds a few conveniences for
|
||||
// handling errors.
|
||||
package slog
|
||||
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
)
|
||||
|
||||
var (
|
||||
// DebugLevel indicates if we should log at the debug level
|
||||
DebugLevel = true
|
||||
|
||||
// Default set of flags to use
|
||||
defaultFlags = log.LstdFlags | log.Lmsgprefix
|
||||
|
||||
// Loggers for various levels. Prefixes are padded to align logged content
|
||||
loggerInfo = log.New(os.Stderr, "INFO ", defaultFlags)
|
||||
loggerWarning = log.New(os.Stderr, "WARNING ", defaultFlags)
|
||||
loggerError = log.New(os.Stderr, "ERROR ", defaultFlags)
|
||||
loggerDebug = log.New(os.Stderr, "DEBUG ", defaultFlags)
|
||||
|
||||
// Convenience for calling functions for all loggers in one method
|
||||
allLoggers = []*log.Logger{
|
||||
loggerInfo,
|
||||
loggerWarning,
|
||||
loggerError,
|
||||
loggerDebug,
|
||||
}
|
||||
)
|
||||
|
||||
// SetFlags allows changing the logger flags using flags found in `log`
|
||||
func SetFlags(flag int) {
|
||||
for _, logger := range allLoggers {
|
||||
logger.SetFlags(flag)
|
||||
}
|
||||
}
|
||||
|
||||
// Log formats logs directly to the main logger
|
||||
func Log(format string, v ...interface{}) {
|
||||
log.Printf(format, v...)
|
||||
}
|
||||
|
||||
// Info formats logs with an INFO prefix
|
||||
func Info(format string, v ...interface{}) {
|
||||
loggerInfo.Printf(format, v...)
|
||||
}
|
||||
|
||||
// Warning will log with a WARNING prefix
|
||||
func Warning(format string, v ...interface{}) {
|
||||
loggerWarning.Printf(format, v...)
|
||||
}
|
||||
|
||||
// Error will log with a ERROR prefix
|
||||
func Error(format string, v ...interface{}) {
|
||||
loggerError.Printf(format, v...)
|
||||
}
|
||||
|
||||
// Debug will log with a DEBUG prefix if DebugLevel is set
|
||||
func Debug(format string, v ...interface{}) {
|
||||
if !DebugLevel {
|
||||
return
|
||||
}
|
||||
loggerDebug.Printf(format, v...)
|
||||
}
|
||||
|
||||
// WarnOnErr if error provided, will provide a warning if an error is provided
|
||||
func WarnOnErr(err error, format string, v ...interface{}) {
|
||||
if err != nil {
|
||||
loggerWarning.Printf(format, v...)
|
||||
loggerError.Print(err)
|
||||
}
|
||||
}
|
||||
|
||||
// FatalOnErr if error provided, will log out details of an error and exit
|
||||
func FatalOnErr(err error, format string, v ...interface{}) {
|
||||
if err != nil {
|
||||
loggerError.Printf(format, v...)
|
||||
loggerError.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
// PanicOnErr if error provided, will log out details of an error and exit
|
||||
func PanicOnErr(err error, format string, v ...interface{}) {
|
||||
if err != nil {
|
||||
loggerError.Printf(format, v...)
|
||||
loggerError.Panic(err)
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user