diff --git a/.circleci/config.yml b/.circleci/config.yml new file mode 100644 index 00000000..59571826 --- /dev/null +++ b/.circleci/config.yml @@ -0,0 +1,63 @@ +# Golang CircleCI 2.0 configuration file +# +# Check https://circleci.com/docs/2.0/language-go/ for more details +version: 2 +jobs: + build: + docker: + # specify the version + - image: circleci/golang:1.16 + + # Specify service dependencies here if necessary + # CircleCI maintains a library of pre-built images + # documented at https://circleci.com/docs/2.0/circleci-images/ + # - image: circleci/postgres:9.4 + + #### TEMPLATE_NOTE: go expects specific checkout path representing url + #### expecting it in the form of + #### /go/src/github.com/circleci/go-tool + #### /go/src/bitbucket.org/circleci/go-tool + working_directory: /go/src/github.com/bugficks/docker-gen + steps: + - checkout + - run: make test check-gofmt all + - run: make release + - run: echo "export VERSION=$(git describe --tags)" >> VERSION + - persist_to_workspace: + root: . + paths: + - VERSION + - release/* + + publish-github-release: + docker: + - image: cibuilds/github:0.10 + steps: + - attach_workspace: + at: . + - run: + name: "Publish Release on GitHub" + command: | + source VERSION + ghr -t ${GITHUB_TOKEN} -u ${CIRCLE_PROJECT_USERNAME} -r ${CIRCLE_PROJECT_REPONAME} -c ${CIRCLE_SHA1} -delete ${VERSION} ./release/ + +# https://circleci.com/docs/2.0/workflows/#executing-workflows-for-a-git-tag +workflows: + version: 2 + untagged-build: + jobs: + - build + tagged-build: + jobs: + - build: + filters: + tags: + only: /^\d+\.\d+\.\d+$/ + - publish-github-release: + requires: + - build + filters: + branches: + ignore: /.*/ + tags: + only: /^\d+\.\d+\.\d+$/ diff --git a/.dockerignore b/.dockerignore index a9c198a7..0b495b41 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,4 +1,5 @@ -.git +#.git docker-gen dist +release *.gz diff --git a/.travis.yml b/.travis.yml index 545f4147..07b4b87d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,9 @@ language: go go: -- 1.9 +- "1.13" +- "1.14" +- "1.15" +- "1.16" install: - make get-deps script: diff --git a/Dockerfile b/Dockerfile index 2277a2fb..02e447e7 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,12 +1,58 @@ +######################################################################################################################## +# +ARG BUILD_ENV=github +ARG SOURCE_REPO=https://github.com/bugficks/docker-gen + +######################################################################################################################## +# +FROM golang:alpine as builder +LABEL stage=docker-gen-intermediate + +ARG BUILD_ENV +ARG SOURCE_REPO + +ENV BUILD_ENV=${BUILD_ENV:-github}\ + SOURCE_REPO=${SOURCE_REPO} + +RUN apk add --no-cache \ + git \ + make \ + gcc \ + libc-dev \ + curl + +######################################################################################################################## +# +FROM builder as builder_github +ONBUILD RUN \ + export TAG=${TAG:-$(curl -fsSLI -o /dev/null -w %{url_effective} ${SOURCE_REPO}/releases/latest | awk -F / '{print $NF}')} \ + && git clone ${SOURCE_REPO} --single-branch --branch ${TAG} --depth 1 /build + +######################################################################################################################## +# +FROM builder as builder_local +ONBUILD COPY . /build + +######################################################################################################################## +# +FROM builder_${BUILD_ENV} as final + +# Tests are disabled here because docker build servers are too slow for ms dependent tests +WORKDIR /build + +# Tests are disabled here because docker build servers are too slow for ms dependent tests +RUN make check-gofmt all + +######################################################################################################################## +# FROM alpine:latest -LABEL maintainer="Jason Wilder " -RUN apk -U add openssl +LABEL maintainer="github.com/bugficks/docker-gen" + +RUN apk --no-cache add openssl -ENV VERSION 0.7.3 -ENV DOWNLOAD_URL https://github.com/jwilder/docker-gen/releases/download/$VERSION/docker-gen-alpine-linux-amd64-$VERSION.tar.gz ENV DOCKER_HOST unix:///tmp/docker.sock -RUN wget -qO- $DOWNLOAD_URL | tar xvz -C /usr/local/bin +COPY --from=final /build/docker-gen /usr/local/bin/ ENTRYPOINT ["/usr/local/bin/docker-gen"] diff --git a/GLOCKFILE b/GLOCKFILE deleted file mode 100644 index a9a6d4a7..00000000 --- a/GLOCKFILE +++ /dev/null @@ -1,6 +0,0 @@ -github.com/BurntSushi/toml 056c9bc7be7190eaa7715723883caffa5f8fa3e4 -github.com/docker/docker f2afa26235941fd79f40eb1e572e19e4ac2b9bbe -github.com/docker/go-units 0dadbb0345b35ec7ef35e228dabb8de89a65bf52 -github.com/fsouza/go-dockerclient d2a6d0596004cc01062a2a068540b817f911e6dc -github.com/gorilla/mux d391bea3118c9fc17a88d62c9189bb791255e0ef -golang.org/x/net a04bdaca5b32abe1c069418fb7088ae607de5bd0 diff --git a/Makefile b/Makefile index e7b8c225..238080a8 100644 --- a/Makefile +++ b/Makefile @@ -1,46 +1,48 @@ .SILENT : -.PHONY : docker-gen clean fmt +.PHONY : docker-gen dist dist-clean release check-gofmt test -TAG:=`git describe --tags` +TAG:=$(shell git describe --tags) LDFLAGS:=-X main.buildVersion=$(TAG) +# https://stackoverflow.com/a/58185179 +LDFLAGS_EXTRA=-linkmode external -w -extldflags "-static" all: docker-gen docker-gen: echo "Building docker-gen" - go build -ldflags "$(LDFLAGS)" ./cmd/docker-gen + go build -ldflags "$(LDFLAGS) $(LDFLAGS_EXTRA)" ./cmd/docker-gen dist-clean: - rm -rf dist - rm -f docker-gen-alpine-linux-*.tar.gz - rm -f docker-gen-linux-*.tar.gz - rm -f docker-gen-darwin-*.tar.gz + go mod tidy + rm -rf dist release/ + rm -f docker-gen dist: dist-clean - mkdir -p dist/alpine-linux/amd64 && GOOS=linux GOARCH=amd64 go build -ldflags "$(LDFLAGS)" -a -tags netgo -installsuffix netgo -o dist/alpine-linux/amd64/docker-gen ./cmd/docker-gen + mkdir -p dist/alpine-linux/amd64 && GOOS=linux GOARCH=amd64 go build -ldflags "$(LDFLAGS) ${LDFLAGS_EXTRA}" -a -tags netgo -installsuffix netgo -o dist/alpine-linux/amd64/docker-gen ./cmd/docker-gen + mkdir -p dist/alpine-linux/arm64 && GOOS=linux GOARCH=arm64 go build -ldflags "$(LDFLAGS)" -a -tags netgo -installsuffix netgo -o dist/alpine-linux/arm64/docker-gen ./cmd/docker-gen mkdir -p dist/alpine-linux/armhf && GOOS=linux GOARCH=arm GOARM=6 go build -ldflags "$(LDFLAGS)" -a -tags netgo -installsuffix netgo -o dist/alpine-linux/armhf/docker-gen ./cmd/docker-gen - mkdir -p dist/linux/amd64 && GOOS=linux GOARCH=amd64 go build -ldflags "$(LDFLAGS)" -o dist/linux/amd64/docker-gen ./cmd/docker-gen + mkdir -p dist/linux/amd64 && GOOS=linux GOARCH=amd64 go build -ldflags "$(LDFLAGS) ${LDFLAGS_EXTRA}" -o dist/linux/amd64/docker-gen ./cmd/docker-gen + mkdir -p dist/linux/arm64 && GOOS=linux GOARCH=arm64 go build -ldflags "$(LDFLAGS)" -o dist/linux/arm64/docker-gen ./cmd/docker-gen mkdir -p dist/linux/i386 && GOOS=linux GOARCH=386 go build -ldflags "$(LDFLAGS)" -o dist/linux/i386/docker-gen ./cmd/docker-gen mkdir -p dist/linux/armel && GOOS=linux GOARCH=arm GOARM=5 go build -ldflags "$(LDFLAGS)" -o dist/linux/armel/docker-gen ./cmd/docker-gen mkdir -p dist/linux/armhf && GOOS=linux GOARCH=arm GOARM=6 go build -ldflags "$(LDFLAGS)" -o dist/linux/armhf/docker-gen ./cmd/docker-gen mkdir -p dist/darwin/amd64 && GOOS=darwin GOARCH=amd64 go build -ldflags "$(LDFLAGS)" -o dist/darwin/amd64/docker-gen ./cmd/docker-gen - mkdir -p dist/darwin/i386 && GOOS=darwin GOARCH=386 go build -ldflags "$(LDFLAGS)" -o dist/darwin/i386/docker-gen ./cmd/docker-gen - +# mkdir -p dist/darwin/i386 && GOOS=darwin GOARCH=386 go build -ldflags "$(LDFLAGS)" -o dist/darwin/i386/docker-gen ./cmd/docker-gen release: dist - glock sync -n < GLOCKFILE - tar -cvzf docker-gen-alpine-linux-amd64-$(TAG).tar.gz -C dist/alpine-linux/amd64 docker-gen - tar -cvzf docker-gen-alpine-linux-armhf-$(TAG).tar.gz -C dist/alpine-linux/armhf docker-gen - tar -cvzf docker-gen-linux-amd64-$(TAG).tar.gz -C dist/linux/amd64 docker-gen - tar -cvzf docker-gen-linux-i386-$(TAG).tar.gz -C dist/linux/i386 docker-gen - tar -cvzf docker-gen-linux-armel-$(TAG).tar.gz -C dist/linux/armel docker-gen - tar -cvzf docker-gen-linux-armhf-$(TAG).tar.gz -C dist/linux/armhf docker-gen - tar -cvzf docker-gen-darwin-amd64-$(TAG).tar.gz -C dist/darwin/amd64 docker-gen - tar -cvzf docker-gen-darwin-i386-$(TAG).tar.gz -C dist/darwin/i386 docker-gen + mkdir -p release + tar -cvzf release/docker-gen-alpine-linux-amd64-$(TAG).tar.gz -C dist/alpine-linux/amd64 docker-gen + tar -cvzf release/docker-gen-alpine-linux-arm64-$(TAG).tar.gz -C dist/alpine-linux/arm64 docker-gen + tar -cvzf release/docker-gen-alpine-linux-armhf-$(TAG).tar.gz -C dist/alpine-linux/armhf docker-gen + tar -cvzf release/docker-gen-linux-amd64-$(TAG).tar.gz -C dist/linux/amd64 docker-gen + tar -cvzf release/docker-gen-linux-arm64-$(TAG).tar.gz -C dist/linux/arm64 docker-gen + tar -cvzf release/docker-gen-linux-i386-$(TAG).tar.gz -C dist/linux/i386 docker-gen + tar -cvzf release/docker-gen-linux-armel-$(TAG).tar.gz -C dist/linux/armel docker-gen + tar -cvzf release/docker-gen-linux-armhf-$(TAG).tar.gz -C dist/linux/armhf docker-gen + tar -cvzf release/docker-gen-darwin-amd64-$(TAG).tar.gz -C dist/darwin/amd64 docker-gen +# tar -cvzf release/docker-gen-darwin-i386-$(TAG).tar.gz -C dist/darwin/i386 docker-gen get-deps: - go get github.com/robfig/glock - glock sync -n < GLOCKFILE check-gofmt: if [ -n "$(shell gofmt -l .)" ]; then \ @@ -50,4 +52,11 @@ check-gofmt: fi test: - go test + go test ./... + +# build docker image from either latest release on github or local directory +# BUILD_ENV: (github | local) default=github +.PHONY: docker-image +docker-image: + docker build -t docker-gen . --build-arg BUILD_ENV=$${BUILD_ENV:-github} + docker tag docker-gen docker-gen:$(shell docker run --rm -it docker-gen:latest -version) diff --git a/README.md b/README.md index 3f74d2cd..f71c498d 100644 --- a/README.md +++ b/README.md @@ -1,17 +1,19 @@ docker-gen ===== -![latest 0.7.3](https://img.shields.io/badge/latest-0.7.3-green.svg?style=flat) -[![Build Status](https://travis-ci.org/jwilder/docker-gen.svg?branch=master)](https://travis-ci.org/jwilder/docker-gen) +![latest 0.8.5](https://img.shields.io/badge/latest-0.8.5-green.svg?style=flat) +[![Travis CI](https://travis-ci.org/bugficks/docker-gen.svg?branch=master)](https://travis-ci.org/github/bugficks/docker-gen) +[![CircleCI](https://circleci.com/gh/bugficks/docker-gen.svg?style=svg)](https://circleci.com/gh/bugficks/docker-gen) ![License MIT](https://img.shields.io/badge/license-MIT-blue.svg?style=flat) +![Go Version](https://img.shields.io/github/go-mod/go-version/bugficks/docker-gen) `docker-gen` is a file generator that renders templates using docker container meta-data. It can be used to generate various kinds of files for: - * **Centralized logging** - [fluentd](https://github.com/jwilder/docker-gen/blob/master/templates/fluentd.conf.tmpl), logstash or other centralized logging tools that tail the containers JSON log file or files within the container. - * **Log Rotation** - [logrotate](https://github.com/jwilder/docker-gen/blob/master/templates/logrotate.tmpl) files to rotate container JSON log files - * **Reverse Proxy Configs** - [nginx](https://github.com/jwilder/docker-gen/blob/master/templates/nginx.tmpl), [haproxy](https://github.com/jwilder/docker-discover), etc. reverse proxy configs to route requests from the host to containers + * **Centralized logging** - [fluentd](https://github.com/bugficks/docker-gen/blob/master/templates/fluentd.conf.tmpl), logstash or other centralized logging tools that tail the containers JSON log file or files within the container. + * **Log Rotation** - [logrotate](https://github.com/bugficks/docker-gen/blob/master/templates/logrotate.tmpl) files to rotate container JSON log files + * **Reverse Proxy Configs** - [nginx](https://github.com/bugficks/docker-gen/blob/master/templates/nginx.tmpl), [haproxy](https://github.com/jwilder/docker-discover), etc. reverse proxy configs to route requests from the host to containers * **Service Discovery** - Scripts (python, bash, etc..) to register containers within [etcd](https://github.com/jwilder/docker-register), hipache, etc.. === @@ -25,17 +27,13 @@ There are three common ways to run docker-gen: #### Host Install -Linux/OSX binaries for release [0.7.3](https://github.com/jwilder/docker-gen/releases) - -* [amd64](https://github.com/jwilder/docker-gen/releases/download/0.7.3/docker-gen-linux-amd64-0.7.3.tar.gz) -* [i386](https://github.com/jwilder/docker-gen/releases/download/0.7.3/docker-gen-linux-i386-0.7.3.tar.gz) -* [alpine-linux](https://github.com/jwilder/docker-gen/releases/download/0.7.3/docker-gen-alpine-linux-amd64-0.7.3.tar.gz) +Linux/OSX binaries for [release](https://github.com/bugficks/docker-gen/releases) Download the version you need, untar, and install to your PATH. ``` -$ wget https://github.com/jwilder/docker-gen/releases/download/0.7.3/docker-gen-linux-amd64-0.7.3.tar.gz -$ tar xvzf docker-gen-linux-amd64-0.7.3.tar.gz +$ wget https://github.com/bugficks/docker-gen/releases/download/VERSION/docker-gen-linux-amd64-VERSION.tar.gz +$ tar xvzf docker-gen-linux-amd64-VERSION.tar.gz $ ./docker-gen ``` @@ -50,7 +48,7 @@ docker-gen within a container to do service registration with etcd. #### Separate Container Install -It can also be run as two separate containers using the [jwilder/docker-gen](https://index.docker.io/u/jwilder/docker-gen/) +It can also be run as two separate containers using the [joellinn/docker-gen](https://index.docker.io/u/joellinn/docker-gen/) image, together with virtually any other image. This is how you could run the official [nginx](https://registry.hub.docker.com/_/nginx/) image and @@ -66,11 +64,11 @@ $ docker run -d -p 80:80 --name nginx -v /tmp/nginx:/etc/nginx/conf.d -t nginx Fetch the template and start the docker-gen container with the shared volume: ``` $ mkdir -p /tmp/templates && cd /tmp/templates -$ curl -o nginx.tmpl https://raw.githubusercontent.com/jwilder/docker-gen/master/templates/nginx.tmpl +$ curl -o nginx.tmpl https://raw.githubusercontent.com/joellinn/docker-gen/master/templates/nginx.tmpl $ docker run -d --name nginx-gen --volumes-from nginx \ -v /var/run/docker.sock:/tmp/docker.sock:ro \ -v /tmp/templates:/etc/docker-gen/templates \ - -t jwilder/docker-gen -notify-sighup nginx -watch -only-exposed /etc/docker-gen/templates/nginx.tmpl /etc/nginx/conf.d/default.conf + -t joellinn/docker-gen -notify-sighup nginx -watch -only-exposed /etc/docker-gen/templates/nginx.tmpl /etc/nginx/conf.d/default.conf ``` === @@ -95,8 +93,13 @@ Options: run command after template is regenerated (e.g restart xyz) -notify-output log the output(stdout/stderr) of notify command + -notify-container container-ID + container to send a signal to + -notify-signal signal + signal to send to the -notify-container. -1 to call docker restart. Defaults to 1 aka. HUP. + All available signals available on the [dockerclient](https://github.com/fsouza/go-dockerclient/blob/01804dec8a84d0a77e63611f2b62d33e9bb2b64a/signal.go) -notify-sighup container-ID - send HUP signal to container. Equivalent to 'docker kill -s HUP container-ID' + send HUP signal to container. Equivalent to 'docker kill -s HUP container-ID' or `-notify-container container-ID -notify-signal 1` -only-exposed only include containers with exposed ports -only-published @@ -363,16 +366,20 @@ For example, this is a JSON version of an emitted RuntimeContainer struct: * *`json $value`*: Returns the JSON representation of `$value` as a `string`. * *`keys $map`*: Returns the keys from `$map`. If `$map` is `nil`, a `nil` is returned. If `$map` is not a `map`, an error will be thrown. * *`last $array`*: Returns the last value of an array. -* *`parseBool $string`*: parseBool returns the boolean value represented by the string. It accepts 1, t, T, TRUE, true, True, 0, f, F, FALSE, false, False. Any other value returns an error. Alias for [`strconv.ParseBool`](http://golang.org/pkg/strconv/#ParseBool) +* *`parseBool $string`*: parseBool returns the boolean value represented by the string. It accepts 1, t, T, TRUE, true, True, 0, f, F, FALSE, false, False. Any other value returns an error. Alias for [`strconv.ParseBool`](http://golang.org/pkg/strconv/#ParseBool) * *`replace $string $old $new $count`*: Replaces up to `$count` occurences of `$old` with `$new` in `$string`. Alias for [`strings.Replace`](http://golang.org/pkg/strings/#Replace) * *`sha1 $string`*: Returns the hexadecimal representation of the SHA1 hash of `$string`. +* *`setHTPasswd $file $user $password`*: Creates or adds `$user` with `$password` to htpasswd `$file`. * *`split $string $sep`*: Splits `$string` into a slice of substrings delimited by `$sep`. Alias for [`strings.Split`](http://golang.org/pkg/strings/#Split) * *`splitN $string $sep $count`*: Splits `$string` into a slice of substrings delimited by `$sep`, with number of substrings returned determined by `$count`. Alias for [`strings.SplitN`](https://golang.org/pkg/strings/#SplitN) * *`trimPrefix $prefix $string`*: If `$prefix` is a prefix of `$string`, return `$string` with `$prefix` trimmed from the beginning. Otherwise, return `$string` unchanged. * *`trimSuffix $suffix $string`*: If `$suffix` is a suffix of `$string`, return `$string` with `$suffix` trimmed from the end. Otherwise, return `$string` unchanged. * *`trim $string`*: Removes whitespace from both sides of `$string`. +* *`toLower $string`*: Replace capital letters in `$string` to lowercase. +* *`toUpper $string`*: Replace lowercase letters in `$string` to uppercase. * *`when $condition $trueValue $falseValue`*: Returns the `$trueValue` when the `$condition` is `true` and the `$falseValue` otherwise * *`where $items $fieldPath $value`*: Filters an array or slice based on the values of a field path expression `$fieldPath`. A field path expression is a dot-delimited list of map keys or struct member names specifying the path from container to a nested value. Returns an array of items having that value. +* *`whereNot $items $fieldPath $value`*: Filters an array or slice based on the values of a field path expression `$fieldPath`. A field path expression is a dot-delimited list of map keys or struct member names specifying the path from container to a nested value. Returns an array of items **not** having that value. * *`whereExist $items $fieldPath`*: Like `where`, but returns only items where `$fieldPath` exists (is not nil). * *`whereNotExist $items $fieldPath`*: Like `where`, but returns only items where `$fieldPath` does not exist (is nil). * *`whereAny $items $fieldPath $sep $values`*: Like `where`, but the string value specified by `$fieldPath` is first split by `$sep` into a list of strings. The comparison value is a string slice with possible matches. Returns items which OR intersect these values. @@ -433,13 +440,8 @@ $ docker-gen -notify "/bin/bash /tmp/etcd.sh" -interval 10 templates/etcd.tmpl / ### Development -This project uses [glock](https://github.com/robfig/glock) for managing 3rd party dependencies. -You'll need to install glock into your workspace before hacking on docker-gen. - ``` $ git clone -$ cd -$ make get-deps $ make ``` diff --git a/cmd/docker-gen/main.go b/cmd/docker-gen/main.go index f84546a9..a75f9d44 100644 --- a/cmd/docker-gen/main.go +++ b/cmd/docker-gen/main.go @@ -5,38 +5,43 @@ import ( "fmt" "log" "os" + "os/signal" "path/filepath" "sync" + "syscall" "github.com/BurntSushi/toml" + //dockergen "github.com/JoelLinn/docker-gen" + //dockergen "../.." + dockergen "docker-gen" docker "github.com/fsouza/go-dockerclient" - "github.com/jwilder/docker-gen" ) type stringslice []string var ( - buildVersion string - version bool - watch bool - wait string - notifyCmd string - notifyOutput bool - notifySigHUPContainerID string - onlyExposed bool - onlyPublished bool - includeStopped bool - configFiles stringslice - configs dockergen.ConfigFile - interval int - keepBlankLines bool - endpoint string - tlsCert string - tlsKey string - tlsCaCert string - tlsVerify bool - tlsCertPath string - wg sync.WaitGroup + buildVersion string + version bool + watch bool + wait string + notifyCmd string + notifyOutput bool + notifyContainerID string + notifyContainerSignal int + onlyExposed bool + onlyPublished bool + includeStopped bool + configFiles stringslice + configs dockergen.ConfigFile + interval int + keepBlankLines bool + endpoint string + tlsCert string + tlsKey string + tlsCaCert string + tlsVerify bool + tlsCertPath string + wg sync.WaitGroup ) func (strings *stringslice) String() string { @@ -95,8 +100,9 @@ func initFlags() { flag.BoolVar(&includeStopped, "include-stopped", false, "include stopped containers") flag.BoolVar(¬ifyOutput, "notify-output", false, "log the output(stdout/stderr) of notify command") flag.StringVar(¬ifyCmd, "notify", "", "run command after template is regenerated (e.g `restart xyz`)") - flag.StringVar(¬ifySigHUPContainerID, "notify-sighup", "", - "send HUP signal to container. Equivalent to docker kill -s HUP `container-ID`") + flag.StringVar(¬ifyContainerID, "notify-sighup", "", "send HUP signal to container. Equivalent to docker kill -s HUP `container-ID`") + flag.StringVar(¬ifyContainerID, "notify-container", "", "container to send a signal to") + flag.IntVar(¬ifyContainerSignal, "notify-signal", int(docker.SIGHUP), "signal to send to the notify-container. Defaults to SIGHUP") flag.Var(&configFiles, "config", "config files with template directives. Config files will be merged if this option is specified multiple times.") flag.IntVar(&interval, "interval", 0, "notify command interval (secs)") flag.BoolVar(&keepBlankLines, "keep-blank-lines", false, "keep blank lines in the output file") @@ -111,6 +117,10 @@ func initFlags() { } func main() { + // SIGHUP is used to trigger generation but go programs call os.Exit(2) at default. + // Ignore the signal until the handler is registered: + signal.Ignore(syscall.SIGHUP) + initFlags() if version { @@ -149,8 +159,8 @@ func main() { Interval: interval, KeepBlankLines: keepBlankLines, } - if notifySigHUPContainerID != "" { - config.NotifyContainers[notifySigHUPContainerID] = docker.SIGHUP + if notifyContainerID != "" { + config.NotifyContainers[notifyContainerID] = docker.Signal(notifyContainerSignal) } configs = dockergen.ConfigFile{ Config: []dockergen.Config{config}} diff --git a/context.go b/context.go index f810f452..e46dc069 100644 --- a/context.go +++ b/context.go @@ -6,7 +6,7 @@ import ( "regexp" "sync" - "github.com/fsouza/go-dockerclient" + docker "github.com/fsouza/go-dockerclient" ) var ( @@ -88,6 +88,7 @@ type RuntimeContainer struct { Gateway string Name string Hostname string + NetworkMode string Image DockerImage Env map[string]string Volumes map[string]Volume @@ -135,6 +136,7 @@ type SwarmNode struct { ID string Name string Address Address + Labels map[string]string } type Mount struct { @@ -159,29 +161,56 @@ type Docker struct { } func GetCurrentContainerID() string { - file, err := os.Open("/proc/self/cgroup") + filepaths := []string{"/proc/self/cgroup", "/proc/self/mountinfo"} - if err != nil { - return "" + for _, filepath := range filepaths { + file, err := os.Open(filepath) + if err != nil { + continue + } + + reader := bufio.NewReader(file) + scanner := bufio.NewScanner(reader) + scanner.Split(bufio.ScanLines) + + for scanner.Scan() { + _, lines, err := bufio.ScanLines([]byte(scanner.Text()), true) + if err == nil { + strLines := string(lines) + if id := matchDockerCurrentContainerID(strLines); id != "" { + return id + } else if id := matchECSCurrentContainerID(strLines); id != "" { + return id + } + } + } } - reader := bufio.NewReader(file) - scanner := bufio.NewScanner(reader) - scanner.Split(bufio.ScanLines) + return "" +} - regex := "/docker[/-]([[:alnum:]]{64})(\\.scope)?$" +func matchDockerCurrentContainerID(lines string) string { + regex := "/docker(/containers)?[/-]([[:alnum:]]{64})(/|$)" re := regexp.MustCompilePOSIX(regex) - for scanner.Scan() { - _, lines, err := bufio.ScanLines([]byte(scanner.Text()), true) - if err == nil { - if re.MatchString(string(lines)) { - submatches := re.FindStringSubmatch(string(lines)) - containerID := submatches[1] + if re.MatchString(lines) { + submatches := re.FindStringSubmatch(string(lines)) + containerID := submatches[len(submatches)-2] - return containerID - } - } + return containerID + } + return "" +} + +func matchECSCurrentContainerID(lines string) string { + regex := "/ecs\\/[^\\/]+\\/(.+)$" + re := regexp.MustCompilePOSIX(regex) + + if re.MatchString(string(lines)) { + submatches := re.FindStringSubmatch(string(lines)) + containerID := submatches[1] + + return containerID } return "" diff --git a/context_test.go b/context_test.go index b4cf87b2..2d667741 100644 --- a/context_test.go +++ b/context_test.go @@ -11,3 +11,74 @@ func TestGetCurrentContainerID(t *testing.T) { t.Fail() } } + +func TestGetCurrentContainerID_ECS(t *testing.T) { + cgroup := + `9:perf_event:/ecs/628967a1-46b4-4a8a-84ff-605128f4679e/3c94e08259a6235781bb65f3dec91150c92e9d414ecc410d6245687392d3900f +8:memory:/ecs/628967a1-46b4-4a8a-84ff-605128f4679e/3c94e08259a6235781bb65f3dec91150c92e9d414ecc410d6245687392d3900f +7:hugetlb:/ecs/628967a1-46b4-4a8a-84ff-605128f4679e/3c94e08259a6235781bb65f3dec91150c92e9d414ecc410d6245687392d3900f +6:freezer:/ecs/628967a1-46b4-4a8a-84ff-605128f4679e/3c94e08259a6235781bb65f3dec91150c92e9d414ecc410d6245687392d3900f +5:devices:/ecs/628967a1-46b4-4a8a-84ff-605128f4679e/3c94e08259a6235781bb65f3dec91150c92e9d414ecc410d6245687392d3900f +4:cpuset:/ecs/628967a1-46b4-4a8a-84ff-605128f4679e/3c94e08259a6235781bb65f3dec91150c92e9d414ecc410d6245687392d3900f +3:cpuacct:/ecs/628967a1-46b4-4a8a-84ff-605128f4679e/3c94e08259a6235781bb65f3dec91150c92e9d414ecc410d6245687392d3900f +2:cpu:/ecs/628967a1-46b4-4a8a-84ff-605128f4679e/3c94e08259a6235781bb65f3dec91150c92e9d414ecc410d6245687392d3900f +1:blkio:/ecs/628967a1-46b4-4a8a-84ff-605128f4679e/3c94e08259a6235781bb65f3dec91150c92e9d414ecc410d6245687392d3900f` + + if got, exp := matchECSCurrentContainerID(cgroup), "3c94e08259a6235781bb65f3dec91150c92e9d414ecc410d6245687392d3900f"; got != exp { + t.Fatalf("id mismatch: got %v, exp %v", got, exp) + } +} + +func TestGetCurrentContainerID_DockerCE(t *testing.T) { + cgroup := + `13:name=systemd:/docker-ce/docker/18862cabc2e0d24142cf93c46ccb6e070c2ea7b996c81c0311ec0309abcbcdfb +12:pids:/docker-ce/docker/18862cabc2e0d24142cf93c46ccb6e070c2ea7b996c81c0311ec0309abcbcdfb +11:hugetlb:/docker-ce/docker/18862cabc2e0d24142cf93c46ccb6e070c2ea7b996c81c0311ec0309abcbcdfb +10:net_prio:/docker-ce/docker/18862cabc2e0d24142cf93c46ccb6e070c2ea7b996c81c0311ec0309abcbcdfb +9:perf_event:/docker-ce/docker/18862cabc2e0d24142cf93c46ccb6e070c2ea7b996c81c0311ec0309abcbcdfb +8:net_cls:/docker-ce/docker/18862cabc2e0d24142cf93c46ccb6e070c2ea7b996c81c0311ec0309abcbcdfb +7:freezer:/docker-ce/docker/18862cabc2e0d24142cf93c46ccb6e070c2ea7b996c81c0311ec0309abcbcdfb +6:devices:/docker-ce/docker/18862cabc2e0d24142cf93c46ccb6e070c2ea7b996c81c0311ec0309abcbcdfb +5:memory:/docker-ce/docker/18862cabc2e0d24142cf93c46ccb6e070c2ea7b996c81c0311ec0309abcbcdfb +4:blkio:/docker-ce/docker/18862cabc2e0d24142cf93c46ccb6e070c2ea7b996c81c0311ec0309abcbcdfb +3:cpuacct:/docker-ce/docker/18862cabc2e0d24142cf93c46ccb6e070c2ea7b996c81c0311ec0309abcbcdfb +2:cpu:/docker-ce/docker/18862cabc2e0d24142cf93c46ccb6e070c2ea7b996c81c0311ec0309abcbcdfb +1:cpuset:/docker-ce/docker/18862cabc2e0d24142cf93c46ccb6e070c2ea7b996c81c0311ec0309abcbcdfb` + + if got, exp := matchDockerCurrentContainerID(cgroup), "18862cabc2e0d24142cf93c46ccb6e070c2ea7b996c81c0311ec0309abcbcdfb"; got != exp { + t.Fatalf("id mismatch: got %v, exp %v", got, exp) + } +} + +func TestGetCurrentContainerID_DockerCE_mountinfo(t *testing.T) { + mountinfo := + `550 209 0:38 / / rw,relatime master:97 - overlay overlay rw,seclabel,lowerdir=/var/lib/docker/overlay2/l/CMLIRGTLVPWATOT2BBGMMWTKPL:/var/lib/docker/overlay2/l/JBJ54L4BQPWTRHZVLNTFAIKIMU:/var/lib/docker/overlay2/l/GKWSGW3V5DTMUG5OV2QF2NDXZM:/var/lib/docker/overlay2/l/KKB7IBA5SXQ6GOHKPC3X2TVPDL,upperdir=/var/lib/docker/overlay2/d3c3d349e227bba0d9f66787a03a5ba8928e6fb8198e787d4d038425afc89fd0/diff,workdir=/var/lib/docker/overlay2/d3c3d349e227bba0d9f66787a03a5ba8928e6fb8198e787d4d038425afc89fd0/work +551 550 0:47 / /proc rw,nosuid,nodev,noexec,relatime - proc proc rw +552 550 0:48 / /dev rw,nosuid - tmpfs tmpfs rw,seclabel,size=65536k,mode=755,inode64 +553 552 0:49 / /dev/pts rw,nosuid,noexec,relatime - devpts devpts rw,seclabel,gid=5,mode=620,ptmxmode=666 +554 550 0:50 / /sys ro,nosuid,nodev,noexec,relatime - sysfs sysfs ro,seclabel +555 554 0:27 / /sys/fs/cgroup ro,nosuid,nodev,noexec,relatime - cgroup2 cgroup rw,seclabel,nsdelegate +556 552 0:46 / /dev/mqueue rw,nosuid,nodev,noexec,relatime - mqueue mqueue rw,seclabel +557 552 0:51 / /dev/shm rw,nosuid,nodev,noexec,relatime - tmpfs shm rw,seclabel,size=65536k,inode64 +558 550 253:1 /var/lib/docker/containers/bc4ed21e220deb8e759970f0853854b868f0157d1b45caab844c5641435553a4/resolv.conf /etc/resolv.conf rw,relatime - xfs /dev/mapper/fedora_tpdachsserver-root rw,seclabel,attr2,inode64,logbufs=8,logbsize=32k,noquota +559 550 253:1 /var/lib/docker/containers/bc4ed21e220deb8e759970f0853854b868f0157d1b45caab844c5641435553a4/hostname /etc/hostname rw,relatime - xfs /dev/mapper/fedora_tpdachsserver-root rw,seclabel,attr2,inode64,logbufs=8,logbsize=32k,noquota +560 550 253:1 /var/lib/docker/containers/bc4ed21e220deb8e759970f0853854b868f0157d1b45caab844c5641435553a4/hosts /etc/hosts rw,relatime - xfs /dev/mapper/fedora_tpdachsserver-root rw,seclabel,attr2,inode64,logbufs=8,logbsize=32k,noquota +210 551 0:47 /bus /proc/bus ro,relatime - proc proc rw +211 551 0:47 /fs /proc/fs ro,relatime - proc proc rw +212 551 0:47 /irq /proc/irq ro,relatime - proc proc rw +264 551 0:47 /sys /proc/sys ro,relatime - proc proc rw +265 551 0:47 /sysrq-trigger /proc/sysrq-trigger ro,relatime - proc proc rw +523 551 0:58 / /proc/asound ro,relatime - tmpfs tmpfs ro,seclabel,inode64 +524 551 0:59 / /proc/acpi ro,relatime - tmpfs tmpfs ro,seclabel,inode64 +529 551 0:48 /null /proc/kcore rw,nosuid - tmpfs tmpfs rw,seclabel,size=65536k,mode=755,inode64 +530 551 0:48 /null /proc/keys rw,nosuid - tmpfs tmpfs rw,seclabel,size=65536k,mode=755,inode64 +531 551 0:48 /null /proc/latency_stats rw,nosuid - tmpfs tmpfs rw,seclabel,size=65536k,mode=755,inode64 +532 551 0:48 /null /proc/timer_list rw,nosuid - tmpfs tmpfs rw,seclabel,size=65536k,mode=755,inode64 +533 551 0:48 /null /proc/sched_debug rw,nosuid - tmpfs tmpfs rw,seclabel,size=65536k,mode=755,inode64 +534 551 0:60 / /proc/scsi ro,relatime - tmpfs tmpfs ro,seclabel,inode64 +535 554 0:61 / /sys/firmware ro,relatime - tmpfs tmpfs ro,seclabel,inode64` + + if got, exp := matchDockerCurrentContainerID(mountinfo), "bc4ed21e220deb8e759970f0853854b868f0157d1b45caab844c5641435553a4"; got != exp { + t.Fatalf("id mismatch: got %v, exp %v", got, exp) + } +} diff --git a/examples/docker-gen.service b/examples/docker-gen.service index 4bd43b33..d4133265 100644 --- a/examples/docker-gen.service +++ b/examples/docker-gen.service @@ -1,6 +1,6 @@ [Unit] Description=A file generator that renders templates using Docker Container meta-data. -Documentation=https://github.com/jwilder/docker-gen +Documentation=https://github.com/joellinn/docker-gen After=network.target docker.socket Requires=docker.socket diff --git a/generator.go b/generator.go index bf68be66..a330f0f9 100644 --- a/generator.go +++ b/generator.go @@ -331,6 +331,14 @@ func (g *generator) sendSignalToContainer(config Config) { for container, signal := range config.NotifyContainers { log.Printf("Sending container '%s' signal '%v'", container, signal) + + if signal == -1 { + if err := g.Client.RestartContainer(container, 10); err != nil { + log.Printf("Error sending restarting container: %s", err) + } + return + } + killOpts := docker.KillContainerOptions{ ID: container, Signal: signal, @@ -379,6 +387,7 @@ func (g *generator) getContainers() ([]*RuntimeContainer, error) { Name: strings.TrimLeft(container.Name, "/"), Hostname: container.Config.Hostname, Gateway: container.NetworkSettings.Gateway, + NetworkMode: container.HostConfig.NetworkMode, Addresses: []Address{}, Networks: []Network{}, Env: make(map[string]string), @@ -434,6 +443,7 @@ func (g *generator) getContainers() ([]*RuntimeContainer, error) { runtimeContainer.Node.Address = Address{ IP: container.Node.IP, } + runtimeContainer.Node.Labels = container.Node.Labels } for _, v := range container.Mounts { diff --git a/generator_test.go b/generator_test.go index 87b8ce71..c1918e74 100644 --- a/generator_test.go +++ b/generator_test.go @@ -79,6 +79,9 @@ func TestGenerateFromEvents(t *testing.T) { Cmd: []string{"/bin/sh"}, Image: "base:latest", }, + HostConfig: &docker.HostConfig{ + NetworkMode: "container:d246e2c9e3d465d96359c942e91de493f6d51a01ba33900d865180d64c34ee91", + }, State: docker.State{ Running: true, Pid: 400, diff --git a/go.mod b/go.mod new file mode 100644 index 00000000..49063141 --- /dev/null +++ b/go.mod @@ -0,0 +1,9 @@ +module docker-gen + +go 1.16 + +require ( + github.com/BurntSushi/toml v0.3.1 + github.com/foomo/htpasswd v0.0.0-20200116085101-e3a90e78da9c + github.com/fsouza/go-dockerclient v1.7.1 +) diff --git a/go.sum b/go.sum new file mode 100644 index 00000000..9c9365bc --- /dev/null +++ b/go.sum @@ -0,0 +1,220 @@ +bazil.org/fuse v0.0.0-20160811212531-371fbbdaa898/go.mod h1:Xbm+BRKSBEpa4q4hTSxohYNQpsxXPbPry4JJWOB3LB8= +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 h1:w+iIsaOQNcT7OZ575w+acHgRric5iCyQh+xv+KJ4HB8= +github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= +github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/GehirnInc/crypt v0.0.0-20190301055215-6c0105aabd46 h1:rs0kDBt2zF4/CM9rO5/iH+U22jnTygPlqWgX55Ufcxg= +github.com/GehirnInc/crypt v0.0.0-20190301055215-6c0105aabd46/go.mod h1:kC29dT1vFpj7py2OvG1khBdQpo3kInWP+6QipLbdngo= +github.com/Microsoft/go-winio v0.4.16-0.20201130162521-d1ffc52c7331/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0= +github.com/Microsoft/go-winio v0.4.16 h1:FtSW/jqD+l4ba5iPBj9CODVtgfYAD8w2wS923g/cFDk= +github.com/Microsoft/go-winio v0.4.16/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0= +github.com/Microsoft/hcsshim v0.8.14 h1:lbPVK25c1cu5xTLITwpUcxoA9vKrKErASPYygvouJns= +github.com/Microsoft/hcsshim v0.8.14/go.mod h1:NtVKoYxQuTLx6gEq0L96c9Ju4JbRJ4nY2ow3VK6a9Lg= +github.com/cilium/ebpf v0.0.0-20200110133405-4032b1d8aae3/go.mod h1:MA5e5Lr8slmEg9bt0VpxxWqJlO4iwu3FBdHUzV7wQVg= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/containerd/cgroups v0.0.0-20200531161412-0dbf7f05ba59 h1:qWj4qVYZ95vLWwqyNJCQg7rDsG5wPdze0UaPolH7DUk= +github.com/containerd/cgroups v0.0.0-20200531161412-0dbf7f05ba59/go.mod h1:pA0z1pT8KYB3TCXK/ocprsh7MAkoW8bZVzPdih9snmM= +github.com/containerd/console v0.0.0-20180822173158-c12b1e7919c1/go.mod h1:Tj/on1eG8kiEhd0+fhSDzsPAFESxzBBvdyEgyryXffw= +github.com/containerd/containerd v1.3.2/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= +github.com/containerd/containerd v1.4.3 h1:ijQT13JedHSHrQGWFcGEwzcNKrAGIiZ+jSD5QQG07SY= +github.com/containerd/containerd v1.4.3/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= +github.com/containerd/continuity v0.0.0-20190426062206-aaeac12a7ffc/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= +github.com/containerd/continuity v0.0.0-20210208174643-50096c924a4e h1:6JKvHHt396/qabvMhnhUZvWaHZzfVfldxE60TK8YLhg= +github.com/containerd/continuity v0.0.0-20210208174643-50096c924a4e/go.mod h1:EXlVlkqNba9rJe3j7w3Xa924itAMLgZH4UD/Q4PExuQ= +github.com/containerd/fifo v0.0.0-20190226154929-a9fb20d87448/go.mod h1:ODA38xgv3Kuk8dQz2ZQXpnv/UZZUHUCL7pnLehbXgQI= +github.com/containerd/go-runc v0.0.0-20180907222934-5a6d9f37cfa3/go.mod h1:IV7qH3hrUgRmyYrtgEeGWJfWbgcHL9CSRruz2Vqcph0= +github.com/containerd/ttrpc v0.0.0-20190828154514-0e0f228740de/go.mod h1:PvCDdDGpgqzQIzDW1TphrGLssLDZp2GuS+X5DkEJB8o= +github.com/containerd/typeurl v0.0.0-20180627222232-a93fcdb778cd/go.mod h1:Cm3kwCdlkCfMSHURc+r6fwoGH6/F1hH3S4sg0rLFWPc= +github.com/coreos/go-systemd/v22 v22.0.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+1atmu1JpKERPPk= +github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= +github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= +github.com/creack/pty v1.1.11 h1:07n33Z8lZxZ2qwegKbObQohDhXDQxiMMz1NOUGYlesw= +github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/docker/docker v20.10.3-0.20210216175712-646072ed6524+incompatible h1:Yu2uGErhwEoOT/OxAFe+/SiJCqRLs+pgcS5XKrDXnG4= +github.com/docker/docker v20.10.3-0.20210216175712-646072ed6524+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.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw= +github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= +github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/foomo/htpasswd v0.0.0-20200116085101-e3a90e78da9c h1:DBGU7zCwrrPPDsD6+gqKG8UfMxenWg9BOJE/Nmfph+4= +github.com/foomo/htpasswd v0.0.0-20200116085101-e3a90e78da9c/go.mod h1:SHawtolbB0ZOFoRWgDwakX5WpwuIWAK88bUXVZqK0Ss= +github.com/fsouza/go-dockerclient v1.7.1 h1:OHRaYvQslCqBStrel+I3OcXZBmpoTnRCGsmH2tAk7Hk= +github.com/fsouza/go-dockerclient v1.7.1/go.mod h1:PHUSk8IVIp+nkIVGrQa2GK69jPOeW/43OUKQgQcOH+M= +github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/gogo/protobuf v1.3.1 h1:DqDEcV5aeaTmdFBePNpYsp3FlcVH/2ISVVM9Qf8PSls= +github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58= +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.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +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.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.4 h1:L8R9j+yAqZuZjsqh/z+F1NCffTKKLShY6zXTItVIZ8M= +github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= +github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= +github.com/hashicorp/golang-lru v0.5.1 h1:0hERBMJE1eitiLkihrMvRVBYAkpHzc/J3QdDN+dAcgU= +github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/moby/sys/mount v0.2.0 h1:WhCW5B355jtxndN5ovugJlMFJawbUODuW8fSnEH6SSM= +github.com/moby/sys/mount v0.2.0/go.mod h1:aAivFE2LB3W4bACsUXChRHQ0qKWsetY4Y9V7sxOougM= +github.com/moby/sys/mountinfo v0.4.0 h1:1KInV3Huv18akCu58V7lzNlt+jFmqlu1EaErnEHE/VM= +github.com/moby/sys/mountinfo v0.4.0/go.mod h1:rEr8tzG/lsIZHBtN/JjGG+LMYx9eXgW2JI+6q0qou+A= +github.com/moby/term v0.0.0-20201216013528-df9cb8a40635 h1:rzf0wL0CHVc8CEsgyygG0Mn9CNCCPZqOPaz8RiiHYQk= +github.com/moby/term v0.0.0-20201216013528-df9cb8a40635/go.mod h1:FBS0z0QWA44HXygs7VXDUOGoN/1TV3RuWkLO04am3wc= +github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= +github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= +github.com/opencontainers/go-digest v0.0.0-20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= +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.0.1 h1:JMemWkRwHx4Zj+fVxWoMCFm/8sYGGrUVojFA6h/TRcI= +github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= +github.com/opencontainers/runc v0.0.0-20190115041553-12f6a991201f/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= +github.com/opencontainers/runc v0.1.1 h1:GlxAyO6x8rfZYN9Tt0Kti5a/cP41iuiO2yYT0IJGY8Y= +github.com/opencontainers/runc v0.1.1/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= +github.com/opencontainers/runtime-spec v1.0.2/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= +github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= +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/procfs v0.0.0-20180125133057-cb4147076ac7/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= +github.com/sirupsen/logrus v1.4.1 h1:GL2rEmy6nsikmW0r8opw9JIRScdMF5hA8cOYLH7In1k= +github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= +github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/sirupsen/logrus v1.7.0 h1:ShrD1U9pZB12TX0cVy0DtePoCH97K8EtX+mg7ZARUtM= +github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= +github.com/spf13/cobra v0.0.2-0.20171109065643-2da4a54c5cee/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= +github.com/spf13/pflag v1.0.1-0.20171106142849-4c012f6dcd95/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +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.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/urfave/cli v1.22.2/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +go.opencensus.io v0.22.0 h1:C9hSCOW830chIVkdja34wa6Ky+IzWllkUinR+BtRZd4= +go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= +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-20200115085410-6d4e4cb37c7d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +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/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +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-20180906233101-161cd47e91fd/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-20190501004415-9ce7a6920f09 h1:KaQtG+aDELoNmXYas3TVkGNYRuq8JQ1aa7LJt8EXVyo= +golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/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-20191004110552-13f9640d40b9/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/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-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58 h1:8gQV6CLnAEikrhgkHFbMAEhagSSnXWGV915qUMm9mrU= +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 h1:SQFwaSi55rU7vdNs9Yr0Z324VNlrF+0wMqRXT4St8ck= +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-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3 h1:7TYNF4UdlohbFwpNH04CoPMp1cHUZgO1Ebq5r2hIjfo= +golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191022100944-742c48ecaeb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200120151820-655fe14d7479/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200831180312-196b9ba8737a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200909081042-eff7692f9009/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200922070232-aee5d888a860/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210216224549-f992740a1bac h1:9glrpwtNjBYgRpb67AZJKHfzj1stG/8BL5H7In2oTC4= +golang.org/x/sys v0.0.0-20210216224549-f992740a1bac/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/term v0.0.0-20201113234701-d7a72108b828 h1:htWEtQEuEVJ4tU/Ngx7Cd/4Q7e3A5Up1owgyBtVsTwk= +golang.org/x/term v0.0.0-20201113234701-d7a72108b828/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +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/tools v0.0.0-20190624222133-a101b041ded4/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +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/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +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-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= +google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo= +gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= +gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk= +gotest.tools/v3 v3.0.3 h1:4AuOwCGf4lLR9u3YOe2awrHygurzhO/HeQ6laiA6Sx0= +gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8= +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= diff --git a/template.go b/template.go index 1eeffaf5..32831b75 100644 --- a/template.go +++ b/template.go @@ -18,6 +18,8 @@ import ( "strings" "syscall" "text/template" + + "github.com/foomo/htpasswd" ) func exists(path string) (bool, error) { @@ -156,6 +158,13 @@ func where(entries interface{}, key string, cmp interface{}) (interface{}, error }) } +// select entries where a key is not equal to a value +func whereNot(entries interface{}, key string, cmp interface{}) (interface{}, error) { + return generalizedWhere("whereNot", entries, key, func(value interface{}) bool { + return !reflect.DeepEqual(value, cmp) + }) +} + // selects entries where a key exists func whereExist(entries interface{}, key string) (interface{}, error) { return generalizedWhere("whereExist", entries, key, func(value interface{}) bool { @@ -312,6 +321,21 @@ func hashSha1(input string) string { return fmt.Sprintf("%x", h.Sum(nil)) } +// no bcrypt on debian. default to sha instead +// https://github.com/nginxinc/docker-nginx/issues/29#issuecomment-194817391 +func setHTPasswd(file, name, password, algo string) (bool, error) { + if algo == "" { + algo = htpasswd.HashSHA + } else { + strings.ToLower(algo) + } + err := htpasswd.SetPassword(file, name, password, htpasswd.HashAlgorithm(algo)) + if err != nil { + return false, err + } + return true, nil +} + func marshalJson(input interface{}) (string, error) { var buf bytes.Buffer enc := json.NewEncoder(&buf) @@ -402,6 +426,16 @@ func trim(s string) string { return strings.TrimSpace(s) } +// toLower return the string in lower case +func toLower(s string) string { + return strings.ToLower(s) +} + +// toUpper return the string in upper case +func toUpper(s string) string { + return strings.ToUpper(s) +} + // when returns the trueValue when the condition is true and the falseValue otherwise func when(condition bool, trueValue, falseValue interface{}) interface{} { if condition { @@ -435,13 +469,17 @@ func newTemplate(name string) *template.Template { "parseJson": unmarshalJson, "queryEscape": url.QueryEscape, "sha1": hashSha1, + "setHTPasswd": setHTPasswd, "split": strings.Split, "splitN": strings.SplitN, "trimPrefix": trimPrefix, "trimSuffix": trimSuffix, "trim": trim, + "toLower": toLower, + "toUpper": toUpper, "when": when, "where": where, + "whereNot": whereNot, "whereExist": whereExist, "whereNotExist": whereNotExist, "whereAny": whereAny, diff --git a/template_test.go b/template_test.go index a64d3a67..f8ac3c88 100644 --- a/template_test.go +++ b/template_test.go @@ -71,7 +71,7 @@ func TestKeysEmpty(t *testing.T) { } if len(input) != vk.Len() { - t.Fatalf("Incorrect key count; expected %s, got %s", len(input), vk.Len()) + t.Fatalf("Incorrect key count; expected %d, got %d", len(input), vk.Len()) } } @@ -269,11 +269,11 @@ func TestGroupByMulti(t *testing.T) { } if len(groups["demo1.localhost"]) != 2 { - t.Fatalf("expected 2 got %s", len(groups["demo1.localhost"])) + t.Fatalf("expected 2 got %d", len(groups["demo1.localhost"])) } if len(groups["demo2.localhost"]) != 1 { - t.Fatalf("expected 1 got %s", len(groups["demo2.localhost"])) + t.Fatalf("expected 1 got %d", len(groups["demo2.localhost"])) } if groups["demo2.localhost"][0].(RuntimeContainer).ID != "3" { t.Fatalf("expected 2 got %s", groups["demo2.localhost"][0].(RuntimeContainer).ID) @@ -351,6 +351,71 @@ func TestWhere(t *testing.T) { tests.run(t, "where") } +func TestWhereNot(t *testing.T) { + containers := []*RuntimeContainer{ + &RuntimeContainer{ + Env: map[string]string{ + "VIRTUAL_HOST": "demo1.localhost", + }, + ID: "1", + Addresses: []Address{ + Address{ + IP: "172.16.42.1", + Port: "80", + Proto: "tcp", + }, + }, + }, + &RuntimeContainer{ + Env: map[string]string{ + "VIRTUAL_HOST": "demo2.localhost", + }, + ID: "2", + Addresses: []Address{ + Address{ + IP: "172.16.42.1", + Port: "9999", + Proto: "tcp", + }, + }, + }, + &RuntimeContainer{ + Env: map[string]string{ + "VIRTUAL_HOST": "demo3.localhost", + }, + ID: "3", + }, + &RuntimeContainer{ + Env: map[string]string{ + "VIRTUAL_HOST": "demo2.localhost", + }, + ID: "4", + }, + } + + tests := templateTestList{ + {`{{whereNot . "Env.VIRTUAL_HOST" "demo1.localhost" | len}}`, containers, `3`}, + {`{{whereNot . "Env.VIRTUAL_HOST" "demo2.localhost" | len}}`, containers, `2`}, + {`{{whereNot . "Env.VIRTUAL_HOST" "demo3.localhost" | len}}`, containers, `3`}, + {`{{whereNot . "Env.NOEXIST" "demo3.localhost" | len}}`, containers, `4`}, + {`{{whereNot .Addresses "Port" "80" | len}}`, containers[0], `0`}, + {`{{whereNot .Addresses "Port" "80" | len}}`, containers[1], `1`}, + { + `{{whereNot . "Value" 5 | len}}`, + []struct { + Value int + }{ + {Value: 5}, + {Value: 3}, + {Value: 5}, + }, + `1`, + }, + } + + tests.run(t, "whereNot") +} + func TestWhereExist(t *testing.T) { containers := []*RuntimeContainer{ &RuntimeContainer{ @@ -644,6 +709,24 @@ func TestTrim(t *testing.T) { } } +func TestToLower(t *testing.T) { + const str = ".RaNd0m StrinG_" + const lowered = ".rand0m string_" + got := toLower(str) + if got != lowered { + t.Fatalf("Expected toLower(%s) to be '%s', got '%s'", str, lowered, got) + } +} + +func TestToUpper(t *testing.T) { + const str = ".RaNd0m StrinG_" + const uppered = ".RAND0M STRING_" + got := toUpper(str) + if got != uppered { + t.Fatalf("Expected toUpper(%s) to be '%s', got '%s'", str, uppered, got) + } +} + func TestDict(t *testing.T) { containers := []*RuntimeContainer{ &RuntimeContainer{ @@ -720,7 +803,7 @@ func TestJson(t *testing.T) { t.Fatal(err) } if len(decoded) != len(containers) { - t.Fatal("Incorrect unmarshaled container count. Expected %d, got %d.", len(containers), len(decoded)) + t.Fatalf("Incorrect unmarshaled container count. Expected %d, got %d.", len(containers), len(decoded)) } } diff --git a/utils_test.go b/utils_test.go index 45186ac3..c5a8c17b 100644 --- a/utils_test.go +++ b/utils_test.go @@ -30,7 +30,7 @@ func TestDockerHostEndpoint(t *testing.T) { endpoint, err := GetEndpoint("") if err != nil { - t.Fatal("%s", err) + t.Fatalf("%s", err) } if endpoint != "tcp://127.0.0.1:4243" { @@ -48,7 +48,7 @@ func TestDockerFlagEndpoint(t *testing.T) { // flag value should override DOCKER_HOST and default value endpoint, err := GetEndpoint("tcp://127.0.0.1:5555") if err != nil { - t.Fatal("%s", err) + t.Fatalf("%s", err) } if endpoint != "tcp://127.0.0.1:5555" { t.Fatalf("Expected tcp://127.0.0.1:5555, got %s", endpoint)