Merge branch 'master' into fix/retain-cmd

pull/418/head
Simon Aronsson 5 years ago committed by GitHub
commit e1239b2cdb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -277,11 +277,12 @@
"login": "simskij",
"name": "Simon Aronsson",
"avatar_url": "https://avatars0.githubusercontent.com/u/1596025?v=4",
"profile": "http://www.arcticbit.se",
"profile": "http://simme.dev",
"contributions": [
"code",
"maintenance",
"review"
"review",
"doc"
]
},
{
@ -309,7 +310,8 @@
"avatar_url": "https://avatars0.githubusercontent.com/u/21138205?v=4",
"profile": "https://github.com/zoispag",
"contributions": [
"code"
"code",
"review"
]
},
{
@ -393,6 +395,42 @@
"contributions": [
"doc"
]
},
{
"login": "jnidzwetzki",
"name": "Jan Kristof Nidzwetzki",
"avatar_url": "https://avatars1.githubusercontent.com/u/5753622?v=4",
"profile": "https://achfrag.net",
"contributions": [
"doc"
]
},
{
"login": "mindrunner",
"name": "lukas",
"avatar_url": "https://avatars0.githubusercontent.com/u/1413542?v=4",
"profile": "https://www.lukaselsner.de",
"contributions": [
"code"
]
},
{
"login": "codingCoffee",
"name": "Ameya Shenoy",
"avatar_url": "https://avatars3.githubusercontent.com/u/13611153?v=4",
"profile": "https://codingcoffee.dev",
"contributions": [
"code"
]
},
{
"login": "raymondelooff",
"name": "Raymon de Looff",
"avatar_url": "https://avatars0.githubusercontent.com/u/9716806?v=4",
"profile": "https://github.com/raymondelooff",
"contributions": [
"code"
]
}
],
"contributorsPerLine": 7,
@ -400,5 +438,6 @@
"projectOwner": "containrrr",
"repoType": "github",
"repoHost": "https://github.com",
"commitConvention": "none"
"commitConvention": "none",
"skipCi": true
}

@ -0,0 +1,23 @@
name: Docker image (latest-dev)
on:
push:
branches:
- master
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: jerray/publish-docker-action@master
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_PASSWORD }}
file: dockerfiles/Dockerfile.self-contained
repository: containrrr/watchtower
tags: latest-dev

@ -25,4 +25,14 @@ go build # compiles and packages an executable bin
go test ./... -v # runs tests with verbose output
./watchtower # runs the application (outside of a container)
```
If you dont have it enabled, you'll either have to prefix each command with `GO111MODULE=on` or run `export GO111MODULE=on` before running the commands. [You can read more about modules here.](https://github.com/golang/go/wiki/Modules)
To build a Watchtower image of your own, use the self-contained Dockerfiles. As the main Dockerfile, they can be found in `dockerfiles/`:
- `dockerfiles/Dockerfile.dev-self-contained` will build an image based on your current local Watchtower files.
- `dockerfiles/Dockerfile.self-contained` will build an image based on current Watchtower's repository on GitHub.
e.g.:
```bash
sudo docker build . -f dockerfiles/Dockerfile.dev-self-contained -t containrrr/watchtower # to build an image from local files
```

@ -62,64 +62,73 @@ The full documentation is available at https://containrrr.github.io/watchtower.
Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)):
<!-- ALL-CONTRIBUTORS-LIST:START - Do not remove or modify this section -->
<!-- prettier-ignore -->
<!-- prettier-ignore-start -->
<!-- markdownlint-disable -->
<table>
<tr>
<td align="center"><a href="http://codelica.com"><img src="https://avatars3.githubusercontent.com/u/386101?v=4" width="100px;" alt="James"/><br /><sub><b>James</b></sub></a><br /><a href="https://github.com/containrrr/watchtower/commits?author=Codelica" title="Tests">⚠️</a> <a href="#ideas-Codelica" title="Ideas, Planning, & Feedback">🤔</a></td>
<td align="center"><a href="https://kopfkrieg.org"><img src="https://avatars2.githubusercontent.com/u/5047813?v=4" width="100px;" alt="Florian"/><br /><sub><b>Florian</b></sub></a><br /><a href="#review-KopfKrieg" title="Reviewed Pull Requests">👀</a> <a href="https://github.com/containrrr/watchtower/commits?author=KopfKrieg" title="Documentation">📖</a></td>
<td align="center"><a href="https://github.com/bdehamer"><img src="https://avatars1.githubusercontent.com/u/398027?v=4" width="100px;" alt="Brian DeHamer"/><br /><sub><b>Brian DeHamer</b></sub></a><br /><a href="https://github.com/containrrr/watchtower/commits?author=bdehamer" title="Code">💻</a> <a href="#maintenance-bdehamer" title="Maintenance">🚧</a></td>
<td align="center"><a href="https://github.com/rosscado"><img src="https://avatars1.githubusercontent.com/u/16578183?v=4" width="100px;" alt="Ross Cadogan"/><br /><sub><b>Ross Cadogan</b></sub></a><br /><a href="https://github.com/containrrr/watchtower/commits?author=rosscado" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/stffabi"><img src="https://avatars0.githubusercontent.com/u/9464631?v=4" width="100px;" alt="stffabi"/><br /><sub><b>stffabi</b></sub></a><br /><a href="https://github.com/containrrr/watchtower/commits?author=stffabi" title="Code">💻</a> <a href="#maintenance-stffabi" title="Maintenance">🚧</a></td>
<td align="center"><a href="https://github.com/ATCUSA"><img src="https://avatars3.githubusercontent.com/u/3581228?v=4" width="100px;" alt="Austin"/><br /><sub><b>Austin</b></sub></a><br /><a href="https://github.com/containrrr/watchtower/commits?author=ATCUSA" title="Documentation">📖</a></td>
<td align="center"><a href="https://labs.ctl.io"><img src="https://avatars2.githubusercontent.com/u/6181487?v=4" width="100px;" alt="David Gardner"/><br /><sub><b>David Gardner</b></sub></a><br /><a href="#review-davidgardner11" title="Reviewed Pull Requests">👀</a> <a href="https://github.com/containrrr/watchtower/commits?author=davidgardner11" title="Documentation">📖</a></td>
<td align="center"><a href="http://codelica.com"><img src="https://avatars3.githubusercontent.com/u/386101?v=4" width="100px;" alt=""/><br /><sub><b>James</b></sub></a><br /><a href="https://github.com/containrrr/watchtower/commits?author=Codelica" title="Tests">⚠️</a> <a href="#ideas-Codelica" title="Ideas, Planning, & Feedback">🤔</a></td>
<td align="center"><a href="https://kopfkrieg.org"><img src="https://avatars2.githubusercontent.com/u/5047813?v=4" width="100px;" alt=""/><br /><sub><b>Florian</b></sub></a><br /><a href="https://github.com/containrrr/watchtower/pulls?q=is%3Apr+reviewed-by%3AKopfKrieg" title="Reviewed Pull Requests">👀</a> <a href="https://github.com/containrrr/watchtower/commits?author=KopfKrieg" title="Documentation">📖</a></td>
<td align="center"><a href="https://github.com/bdehamer"><img src="https://avatars1.githubusercontent.com/u/398027?v=4" width="100px;" alt=""/><br /><sub><b>Brian DeHamer</b></sub></a><br /><a href="https://github.com/containrrr/watchtower/commits?author=bdehamer" title="Code">💻</a> <a href="#maintenance-bdehamer" title="Maintenance">🚧</a></td>
<td align="center"><a href="https://github.com/rosscado"><img src="https://avatars1.githubusercontent.com/u/16578183?v=4" width="100px;" alt=""/><br /><sub><b>Ross Cadogan</b></sub></a><br /><a href="https://github.com/containrrr/watchtower/commits?author=rosscado" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/stffabi"><img src="https://avatars0.githubusercontent.com/u/9464631?v=4" width="100px;" alt=""/><br /><sub><b>stffabi</b></sub></a><br /><a href="https://github.com/containrrr/watchtower/commits?author=stffabi" title="Code">💻</a> <a href="#maintenance-stffabi" title="Maintenance">🚧</a></td>
<td align="center"><a href="https://github.com/ATCUSA"><img src="https://avatars3.githubusercontent.com/u/3581228?v=4" width="100px;" alt=""/><br /><sub><b>Austin</b></sub></a><br /><a href="https://github.com/containrrr/watchtower/commits?author=ATCUSA" title="Documentation">📖</a></td>
<td align="center"><a href="https://labs.ctl.io"><img src="https://avatars2.githubusercontent.com/u/6181487?v=4" width="100px;" alt=""/><br /><sub><b>David Gardner</b></sub></a><br /><a href="https://github.com/containrrr/watchtower/pulls?q=is%3Apr+reviewed-by%3Adavidgardner11" title="Reviewed Pull Requests">👀</a> <a href="https://github.com/containrrr/watchtower/commits?author=davidgardner11" title="Documentation">📖</a></td>
</tr>
<tr>
<td align="center"><a href="https://github.com/dolanor"><img src="https://avatars3.githubusercontent.com/u/928722?v=4" width="100px;" alt="Tanguy ⧓ Herrmann"/><br /><sub><b>Tanguy ⧓ Herrmann</b></sub></a><br /><a href="https://github.com/containrrr/watchtower/commits?author=dolanor" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/rdamazio"><img src="https://avatars3.githubusercontent.com/u/997641?v=4" width="100px;" alt="Rodrigo Damazio Bovendorp"/><br /><sub><b>Rodrigo Damazio Bovendorp</b></sub></a><br /><a href="https://github.com/containrrr/watchtower/commits?author=rdamazio" title="Code">💻</a> <a href="https://github.com/containrrr/watchtower/commits?author=rdamazio" title="Documentation">📖</a></td>
<td align="center"><a href="https://www.taisun.io/"><img src="https://avatars3.githubusercontent.com/u/1852688?v=4" width="100px;" alt="Ryan Kuba"/><br /><sub><b>Ryan Kuba</b></sub></a><br /><a href="#infra-thelamer" title="Infrastructure (Hosting, Build-Tools, etc)">🚇</a></td>
<td align="center"><a href="https://github.com/cnrmck"><img src="https://avatars2.githubusercontent.com/u/22061955?v=4" width="100px;" alt="cnrmck"/><br /><sub><b>cnrmck</b></sub></a><br /><a href="https://github.com/containrrr/watchtower/commits?author=cnrmck" title="Documentation">📖</a></td>
<td align="center"><a href="http://harrywalter.co.uk"><img src="https://avatars3.githubusercontent.com/u/338588?v=4" width="100px;" alt="Harry Walter"/><br /><sub><b>Harry Walter</b></sub></a><br /><a href="https://github.com/containrrr/watchtower/commits?author=haswalt" title="Code">💻</a></td>
<td align="center"><a href="http://projectsperanza.com"><img src="https://avatars3.githubusercontent.com/u/74515?v=4" width="100px;" alt="Robotex"/><br /><sub><b>Robotex</b></sub></a><br /><a href="https://github.com/containrrr/watchtower/commits?author=Robotex" title="Documentation">📖</a></td>
<td align="center"><a href="http://geraldpape.io"><img src="https://avatars0.githubusercontent.com/u/1494211?v=4" width="100px;" alt="Gerald Pape"/><br /><sub><b>Gerald Pape</b></sub></a><br /><a href="https://github.com/containrrr/watchtower/commits?author=ubergesundheit" title="Documentation">📖</a></td>
<td align="center"><a href="https://github.com/dolanor"><img src="https://avatars3.githubusercontent.com/u/928722?v=4" width="100px;" alt=""/><br /><sub><b>Tanguy ⧓ Herrmann</b></sub></a><br /><a href="https://github.com/containrrr/watchtower/commits?author=dolanor" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/rdamazio"><img src="https://avatars3.githubusercontent.com/u/997641?v=4" width="100px;" alt=""/><br /><sub><b>Rodrigo Damazio Bovendorp</b></sub></a><br /><a href="https://github.com/containrrr/watchtower/commits?author=rdamazio" title="Code">💻</a> <a href="https://github.com/containrrr/watchtower/commits?author=rdamazio" title="Documentation">📖</a></td>
<td align="center"><a href="https://www.taisun.io/"><img src="https://avatars3.githubusercontent.com/u/1852688?v=4" width="100px;" alt=""/><br /><sub><b>Ryan Kuba</b></sub></a><br /><a href="#infra-thelamer" title="Infrastructure (Hosting, Build-Tools, etc)">🚇</a></td>
<td align="center"><a href="https://github.com/cnrmck"><img src="https://avatars2.githubusercontent.com/u/22061955?v=4" width="100px;" alt=""/><br /><sub><b>cnrmck</b></sub></a><br /><a href="https://github.com/containrrr/watchtower/commits?author=cnrmck" title="Documentation">📖</a></td>
<td align="center"><a href="http://harrywalter.co.uk"><img src="https://avatars3.githubusercontent.com/u/338588?v=4" width="100px;" alt=""/><br /><sub><b>Harry Walter</b></sub></a><br /><a href="https://github.com/containrrr/watchtower/commits?author=haswalt" title="Code">💻</a></td>
<td align="center"><a href="http://projectsperanza.com"><img src="https://avatars3.githubusercontent.com/u/74515?v=4" width="100px;" alt=""/><br /><sub><b>Robotex</b></sub></a><br /><a href="https://github.com/containrrr/watchtower/commits?author=Robotex" title="Documentation">📖</a></td>
<td align="center"><a href="http://geraldpape.io"><img src="https://avatars0.githubusercontent.com/u/1494211?v=4" width="100px;" alt=""/><br /><sub><b>Gerald Pape</b></sub></a><br /><a href="https://github.com/containrrr/watchtower/commits?author=ubergesundheit" title="Documentation">📖</a></td>
</tr>
<tr>
<td align="center"><a href="https://github.com/fomk"><img src="https://avatars0.githubusercontent.com/u/17636183?v=4" width="100px;" alt="fomk"/><br /><sub><b>fomk</b></sub></a><br /><a href="https://github.com/containrrr/watchtower/commits?author=fomk" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/svengo"><img src="https://avatars3.githubusercontent.com/u/2502366?v=4" width="100px;" alt="Sven Gottwald"/><br /><sub><b>Sven Gottwald</b></sub></a><br /><a href="#infra-svengo" title="Infrastructure (Hosting, Build-Tools, etc)">🚇</a></td>
<td align="center"><a href="https://liberapay.com/techknowlogick/"><img src="https://avatars1.githubusercontent.com/u/164197?v=4" width="100px;" alt="techknowlogick"/><br /><sub><b>techknowlogick</b></sub></a><br /><a href="https://github.com/containrrr/watchtower/commits?author=techknowlogick" title="Code">💻</a></td>
<td align="center"><a href="http://log.c5t.org/about/"><img src="https://avatars1.githubusercontent.com/u/1449568?v=4" width="100px;" alt="waja"/><br /><sub><b>waja</b></sub></a><br /><a href="https://github.com/containrrr/watchtower/commits?author=waja" title="Documentation">📖</a></td>
<td align="center"><a href="http://scottalbertson.com"><img src="https://avatars2.githubusercontent.com/u/154463?v=4" width="100px;" alt="Scott Albertson"/><br /><sub><b>Scott Albertson</b></sub></a><br /><a href="https://github.com/containrrr/watchtower/commits?author=salbertson" title="Documentation">📖</a></td>
<td align="center"><a href="https://github.com/huddlesj"><img src="https://avatars1.githubusercontent.com/u/11966535?v=4" width="100px;" alt="Jason Huddleston"/><br /><sub><b>Jason Huddleston</b></sub></a><br /><a href="https://github.com/containrrr/watchtower/commits?author=huddlesj" title="Documentation">📖</a></td>
<td align="center"><a href="https://npstr.space/"><img src="https://avatars3.githubusercontent.com/u/6048348?v=4" width="100px;" alt="Napster"/><br /><sub><b>Napster</b></sub></a><br /><a href="https://github.com/containrrr/watchtower/commits?author=napstr" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/fomk"><img src="https://avatars0.githubusercontent.com/u/17636183?v=4" width="100px;" alt=""/><br /><sub><b>fomk</b></sub></a><br /><a href="https://github.com/containrrr/watchtower/commits?author=fomk" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/svengo"><img src="https://avatars3.githubusercontent.com/u/2502366?v=4" width="100px;" alt=""/><br /><sub><b>Sven Gottwald</b></sub></a><br /><a href="#infra-svengo" title="Infrastructure (Hosting, Build-Tools, etc)">🚇</a></td>
<td align="center"><a href="https://liberapay.com/techknowlogick/"><img src="https://avatars1.githubusercontent.com/u/164197?v=4" width="100px;" alt=""/><br /><sub><b>techknowlogick</b></sub></a><br /><a href="https://github.com/containrrr/watchtower/commits?author=techknowlogick" title="Code">💻</a></td>
<td align="center"><a href="http://log.c5t.org/about/"><img src="https://avatars1.githubusercontent.com/u/1449568?v=4" width="100px;" alt=""/><br /><sub><b>waja</b></sub></a><br /><a href="https://github.com/containrrr/watchtower/commits?author=waja" title="Documentation">📖</a></td>
<td align="center"><a href="http://scottalbertson.com"><img src="https://avatars2.githubusercontent.com/u/154463?v=4" width="100px;" alt=""/><br /><sub><b>Scott Albertson</b></sub></a><br /><a href="https://github.com/containrrr/watchtower/commits?author=salbertson" title="Documentation">📖</a></td>
<td align="center"><a href="https://github.com/huddlesj"><img src="https://avatars1.githubusercontent.com/u/11966535?v=4" width="100px;" alt=""/><br /><sub><b>Jason Huddleston</b></sub></a><br /><a href="https://github.com/containrrr/watchtower/commits?author=huddlesj" title="Documentation">📖</a></td>
<td align="center"><a href="https://npstr.space/"><img src="https://avatars3.githubusercontent.com/u/6048348?v=4" width="100px;" alt=""/><br /><sub><b>Napster</b></sub></a><br /><a href="https://github.com/containrrr/watchtower/commits?author=napstr" title="Code">💻</a></td>
</tr>
<tr>
<td align="center"><a href="https://github.com/darknode"><img src="https://avatars1.githubusercontent.com/u/809429?v=4" width="100px;" alt="Maxim"/><br /><sub><b>Maxim</b></sub></a><br /><a href="https://github.com/containrrr/watchtower/commits?author=darknode" title="Code">💻</a> <a href="https://github.com/containrrr/watchtower/commits?author=darknode" title="Documentation">📖</a></td>
<td align="center"><a href="https://schmitt.cat"><img src="https://avatars0.githubusercontent.com/u/17984549?v=4" width="100px;" alt="Max Schmitt"/><br /><sub><b>Max Schmitt</b></sub></a><br /><a href="https://github.com/containrrr/watchtower/commits?author=mxschmitt" title="Documentation">📖</a></td>
<td align="center"><a href="https://github.com/cron410"><img src="https://avatars1.githubusercontent.com/u/3082899?v=4" width="100px;" alt="cron410"/><br /><sub><b>cron410</b></sub></a><br /><a href="https://github.com/containrrr/watchtower/commits?author=cron410" title="Documentation">📖</a></td>
<td align="center"><a href="https://github.com/Cardoso222"><img src="https://avatars3.githubusercontent.com/u/7026517?v=4" width="100px;" alt="Paulo Henrique"/><br /><sub><b>Paulo Henrique</b></sub></a><br /><a href="https://github.com/containrrr/watchtower/commits?author=Cardoso222" title="Documentation">📖</a></td>
<td align="center"><a href="https://coded.io"><img src="https://avatars0.githubusercontent.com/u/107097?v=4" width="100px;" alt="Kaleb Elwert"/><br /><sub><b>Kaleb Elwert</b></sub></a><br /><a href="https://github.com/containrrr/watchtower/commits?author=belak" title="Documentation">📖</a></td>
<td align="center"><a href="https://github.com/wmbutler"><img src="https://avatars1.githubusercontent.com/u/1254810?v=4" width="100px;" alt="Bill Butler"/><br /><sub><b>Bill Butler</b></sub></a><br /><a href="https://github.com/containrrr/watchtower/commits?author=wmbutler" title="Documentation">📖</a></td>
<td align="center"><a href="https://www.mariotacke.io"><img src="https://avatars2.githubusercontent.com/u/4942019?v=4" width="100px;" alt="Mario Tacke"/><br /><sub><b>Mario Tacke</b></sub></a><br /><a href="https://github.com/containrrr/watchtower/commits?author=mariotacke" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/darknode"><img src="https://avatars1.githubusercontent.com/u/809429?v=4" width="100px;" alt=""/><br /><sub><b>Maxim</b></sub></a><br /><a href="https://github.com/containrrr/watchtower/commits?author=darknode" title="Code">💻</a> <a href="https://github.com/containrrr/watchtower/commits?author=darknode" title="Documentation">📖</a></td>
<td align="center"><a href="https://schmitt.cat"><img src="https://avatars0.githubusercontent.com/u/17984549?v=4" width="100px;" alt=""/><br /><sub><b>Max Schmitt</b></sub></a><br /><a href="https://github.com/containrrr/watchtower/commits?author=mxschmitt" title="Documentation">📖</a></td>
<td align="center"><a href="https://github.com/cron410"><img src="https://avatars1.githubusercontent.com/u/3082899?v=4" width="100px;" alt=""/><br /><sub><b>cron410</b></sub></a><br /><a href="https://github.com/containrrr/watchtower/commits?author=cron410" title="Documentation">📖</a></td>
<td align="center"><a href="https://github.com/Cardoso222"><img src="https://avatars3.githubusercontent.com/u/7026517?v=4" width="100px;" alt=""/><br /><sub><b>Paulo Henrique</b></sub></a><br /><a href="https://github.com/containrrr/watchtower/commits?author=Cardoso222" title="Documentation">📖</a></td>
<td align="center"><a href="https://coded.io"><img src="https://avatars0.githubusercontent.com/u/107097?v=4" width="100px;" alt=""/><br /><sub><b>Kaleb Elwert</b></sub></a><br /><a href="https://github.com/containrrr/watchtower/commits?author=belak" title="Documentation">📖</a></td>
<td align="center"><a href="https://github.com/wmbutler"><img src="https://avatars1.githubusercontent.com/u/1254810?v=4" width="100px;" alt=""/><br /><sub><b>Bill Butler</b></sub></a><br /><a href="https://github.com/containrrr/watchtower/commits?author=wmbutler" title="Documentation">📖</a></td>
<td align="center"><a href="https://www.mariotacke.io"><img src="https://avatars2.githubusercontent.com/u/4942019?v=4" width="100px;" alt=""/><br /><sub><b>Mario Tacke</b></sub></a><br /><a href="https://github.com/containrrr/watchtower/commits?author=mariotacke" title="Code">💻</a></td>
</tr>
<tr>
<td align="center"><a href="https://markwoodbridge.com"><img src="https://avatars2.githubusercontent.com/u/1101318?v=4" width="100px;" alt="Mark Woodbridge"/><br /><sub><b>Mark Woodbridge</b></sub></a><br /><a href="https://github.com/containrrr/watchtower/commits?author=mrw34" title="Code">💻</a></td>
<td align="center"><a href="http://www.arcticbit.se"><img src="https://avatars0.githubusercontent.com/u/1596025?v=4" width="100px;" alt="Simon Aronsson"/><br /><sub><b>Simon Aronsson</b></sub></a><br /><a href="https://github.com/containrrr/watchtower/commits?author=simskij" title="Code">💻</a> <a href="#maintenance-simskij" title="Maintenance">🚧</a> <a href="#review-simskij" title="Reviewed Pull Requests">👀</a></td>
<td align="center"><a href="https://github.com/Ansem93"><img src="https://avatars3.githubusercontent.com/u/6626218?v=4" width="100px;" alt="Ansem93"/><br /><sub><b>Ansem93</b></sub></a><br /><a href="https://github.com/containrrr/watchtower/commits?author=Ansem93" title="Documentation">📖</a></td>
<td align="center"><a href="https://github.com/lukapeschke"><img src="https://avatars1.githubusercontent.com/u/17085536?v=4" width="100px;" alt="Luka Peschke"/><br /><sub><b>Luka Peschke</b></sub></a><br /><a href="https://github.com/containrrr/watchtower/commits?author=lukapeschke" title="Code">💻</a> <a href="https://github.com/containrrr/watchtower/commits?author=lukapeschke" title="Documentation">📖</a></td>
<td align="center"><a href="https://github.com/zoispag"><img src="https://avatars0.githubusercontent.com/u/21138205?v=4" width="100px;" alt="Zois Pagoulatos"/><br /><sub><b>Zois Pagoulatos</b></sub></a><br /><a href="https://github.com/containrrr/watchtower/commits?author=zoispag" title="Code">💻</a></td>
<td align="center"><a href="https://alexandre.menif.name"><img src="https://avatars0.githubusercontent.com/u/16152103?v=4" width="100px;" alt="Alexandre Menif"/><br /><sub><b>Alexandre Menif</b></sub></a><br /><a href="https://github.com/containrrr/watchtower/commits?author=alexandremenif" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/chugunov"><img src="https://avatars1.githubusercontent.com/u/4140479?v=4" width="100px;" alt="Andrey"/><br /><sub><b>Andrey</b></sub></a><br /><a href="https://github.com/containrrr/watchtower/commits?author=chugunov" title="Documentation">📖</a></td>
<td align="center"><a href="https://markwoodbridge.com"><img src="https://avatars2.githubusercontent.com/u/1101318?v=4" width="100px;" alt=""/><br /><sub><b>Mark Woodbridge</b></sub></a><br /><a href="https://github.com/containrrr/watchtower/commits?author=mrw34" title="Code">💻</a></td>
<td align="center"><a href="http://simme.dev"><img src="https://avatars0.githubusercontent.com/u/1596025?v=4" width="100px;" alt=""/><br /><sub><b>Simon Aronsson</b></sub></a><br /><a href="https://github.com/containrrr/watchtower/commits?author=simskij" title="Code">💻</a> <a href="#maintenance-simskij" title="Maintenance">🚧</a> <a href="https://github.com/containrrr/watchtower/pulls?q=is%3Apr+reviewed-by%3Asimskij" title="Reviewed Pull Requests">👀</a> <a href="https://github.com/containrrr/watchtower/commits?author=simskij" title="Documentation">📖</a></td>
<td align="center"><a href="https://github.com/Ansem93"><img src="https://avatars3.githubusercontent.com/u/6626218?v=4" width="100px;" alt=""/><br /><sub><b>Ansem93</b></sub></a><br /><a href="https://github.com/containrrr/watchtower/commits?author=Ansem93" title="Documentation">📖</a></td>
<td align="center"><a href="https://github.com/lukapeschke"><img src="https://avatars1.githubusercontent.com/u/17085536?v=4" width="100px;" alt=""/><br /><sub><b>Luka Peschke</b></sub></a><br /><a href="https://github.com/containrrr/watchtower/commits?author=lukapeschke" title="Code">💻</a> <a href="https://github.com/containrrr/watchtower/commits?author=lukapeschke" title="Documentation">📖</a></td>
<td align="center"><a href="https://github.com/zoispag"><img src="https://avatars0.githubusercontent.com/u/21138205?v=4" width="100px;" alt=""/><br /><sub><b>Zois Pagoulatos</b></sub></a><br /><a href="https://github.com/containrrr/watchtower/commits?author=zoispag" title="Code">💻</a> <a href="https://github.com/containrrr/watchtower/pulls?q=is%3Apr+reviewed-by%3Azoispag" title="Reviewed Pull Requests">👀</a></td>
<td align="center"><a href="https://alexandre.menif.name"><img src="https://avatars0.githubusercontent.com/u/16152103?v=4" width="100px;" alt=""/><br /><sub><b>Alexandre Menif</b></sub></a><br /><a href="https://github.com/containrrr/watchtower/commits?author=alexandremenif" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/chugunov"><img src="https://avatars1.githubusercontent.com/u/4140479?v=4" width="100px;" alt=""/><br /><sub><b>Andrey</b></sub></a><br /><a href="https://github.com/containrrr/watchtower/commits?author=chugunov" title="Documentation">📖</a></td>
</tr>
<tr>
<td align="center"><a href="https://noplanman.ch"><img src="https://avatars3.githubusercontent.com/u/9423417?v=4" width="100px;" alt="Armando Lüscher"/><br /><sub><b>Armando Lüscher</b></sub></a><br /><a href="https://github.com/containrrr/watchtower/commits?author=noplanman" title="Documentation">📖</a></td>
<td align="center"><a href="https://github.com/rjbudke"><img src="https://avatars2.githubusercontent.com/u/273485?v=4" width="100px;" alt="Ryan Budke"/><br /><sub><b>Ryan Budke</b></sub></a><br /><a href="https://github.com/containrrr/watchtower/commits?author=rjbudke" title="Documentation">📖</a></td>
<td align="center"><a href="http://kaloyan.raev.name"><img src="https://avatars2.githubusercontent.com/u/468091?v=4" width="100px;" alt="Kaloyan Raev"/><br /><sub><b>Kaloyan Raev</b></sub></a><br /><a href="https://github.com/containrrr/watchtower/commits?author=kaloyan-raev" title="Code">💻</a> <a href="https://github.com/containrrr/watchtower/commits?author=kaloyan-raev" title="Tests">⚠️</a></td>
<td align="center"><a href="https://github.com/sixth"><img src="https://avatars3.githubusercontent.com/u/11591445?v=4" width="100px;" alt="sixth"/><br /><sub><b>sixth</b></sub></a><br /><a href="https://github.com/containrrr/watchtower/commits?author=sixth" title="Documentation">📖</a></td>
<td align="center"><a href="https://foosel.net"><img src="https://avatars0.githubusercontent.com/u/83657?v=4" width="100px;" alt="Gina Häußge"/><br /><sub><b>Gina Häußge</b></sub></a><br /><a href="https://github.com/containrrr/watchtower/commits?author=foosel" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/8ear"><img src="https://avatars0.githubusercontent.com/u/10329648?v=4" width="100px;" alt="Max H."/><br /><sub><b>Max H.</b></sub></a><br /><a href="https://github.com/containrrr/watchtower/commits?author=8ear" title="Code">💻</a></td>
<td align="center"><a href="https://pjknkda.github.io"><img src="https://avatars0.githubusercontent.com/u/4986524?v=4" width="100px;" alt="Jungkook Park"/><br /><sub><b>Jungkook Park</b></sub></a><br /><a href="https://github.com/containrrr/watchtower/commits?author=pjknkda" title="Documentation">📖</a></td>
<td align="center"><a href="https://noplanman.ch"><img src="https://avatars3.githubusercontent.com/u/9423417?v=4" width="100px;" alt=""/><br /><sub><b>Armando Lüscher</b></sub></a><br /><a href="https://github.com/containrrr/watchtower/commits?author=noplanman" title="Documentation">📖</a></td>
<td align="center"><a href="https://github.com/rjbudke"><img src="https://avatars2.githubusercontent.com/u/273485?v=4" width="100px;" alt=""/><br /><sub><b>Ryan Budke</b></sub></a><br /><a href="https://github.com/containrrr/watchtower/commits?author=rjbudke" title="Documentation">📖</a></td>
<td align="center"><a href="http://kaloyan.raev.name"><img src="https://avatars2.githubusercontent.com/u/468091?v=4" width="100px;" alt=""/><br /><sub><b>Kaloyan Raev</b></sub></a><br /><a href="https://github.com/containrrr/watchtower/commits?author=kaloyan-raev" title="Code">💻</a> <a href="https://github.com/containrrr/watchtower/commits?author=kaloyan-raev" title="Tests">⚠️</a></td>
<td align="center"><a href="https://github.com/sixth"><img src="https://avatars3.githubusercontent.com/u/11591445?v=4" width="100px;" alt=""/><br /><sub><b>sixth</b></sub></a><br /><a href="https://github.com/containrrr/watchtower/commits?author=sixth" title="Documentation">📖</a></td>
<td align="center"><a href="https://foosel.net"><img src="https://avatars0.githubusercontent.com/u/83657?v=4" width="100px;" alt=""/><br /><sub><b>Gina Häußge</b></sub></a><br /><a href="https://github.com/containrrr/watchtower/commits?author=foosel" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/8ear"><img src="https://avatars0.githubusercontent.com/u/10329648?v=4" width="100px;" alt=""/><br /><sub><b>Max H.</b></sub></a><br /><a href="https://github.com/containrrr/watchtower/commits?author=8ear" title="Code">💻</a></td>
<td align="center"><a href="https://pjknkda.github.io"><img src="https://avatars0.githubusercontent.com/u/4986524?v=4" width="100px;" alt=""/><br /><sub><b>Jungkook Park</b></sub></a><br /><a href="https://github.com/containrrr/watchtower/commits?author=pjknkda" title="Documentation">📖</a></td>
</tr>
<tr>
<td align="center"><a href="https://achfrag.net"><img src="https://avatars1.githubusercontent.com/u/5753622?v=4" width="100px;" alt=""/><br /><sub><b>Jan Kristof Nidzwetzki</b></sub></a><br /><a href="https://github.com/containrrr/watchtower/commits?author=jnidzwetzki" title="Documentation">📖</a></td>
<td align="center"><a href="https://www.lukaselsner.de"><img src="https://avatars0.githubusercontent.com/u/1413542?v=4" width="100px;" alt=""/><br /><sub><b>lukas</b></sub></a><br /><a href="https://github.com/containrrr/watchtower/commits?author=mindrunner" title="Code">💻</a></td>
<td align="center"><a href="https://codingcoffee.dev"><img src="https://avatars3.githubusercontent.com/u/13611153?v=4" width="100px;" alt=""/><br /><sub><b>Ameya Shenoy</b></sub></a><br /><a href="https://github.com/containrrr/watchtower/commits?author=codingCoffee" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/raymondelooff"><img src="https://avatars0.githubusercontent.com/u/9716806?v=4" width="100px;" alt=""/><br /><sub><b>Raymon de Looff</b></sub></a><br /><a href="https://github.com/containrrr/watchtower/commits?author=raymondelooff" title="Code">💻</a></td>
</tr>
</table>
<!-- markdownlint-enable -->
<!-- prettier-ignore-end -->
<!-- ALL-CONTRIBUTORS-LIST:END -->
This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome!

@ -1,6 +1,7 @@
package cmd
import (
"github.com/containrrr/watchtower/pkg/filters"
"os"
"os/signal"
"strconv"
@ -108,7 +109,7 @@ func PreRun(cmd *cobra.Command, args []string) {
// Run is the main execution flow of the command
func Run(c *cobra.Command, names []string) {
filter := container.BuildFilter(names, enableLabel)
filter := filters.BuildFilter(names, enableLabel)
runOnce, _ := c.PersistentFlags().GetBool("run-once")
if runOnce {
@ -155,7 +156,7 @@ func runUpgradesOnSchedule(filter t.Filter) error {
return err
}
log.Debug("Starting Watchtower and scheduling first run: " + cron.Entries()[0].Schedule.Next(time.Now()).String())
log.Info("Starting Watchtower and scheduling first run: " + cron.Entries()[0].Schedule.Next(time.Now()).String())
cron.Start()
// Graceful shut-down on SIGINT/SIGTERM
@ -172,7 +173,7 @@ func runUpgradesOnSchedule(filter t.Filter) error {
func runUpdatesWithNotifications(filter t.Filter) {
notifier.StartNotification()
updateParams := actions.UpdateParams{
updateParams := t.UpdateParams{
Filter: filter,
Cleanup: cleanup,
NoRestart: noRestart,

@ -1,4 +1,4 @@
FROM alpine:3.9 as alpine
FROM alpine:3.11 as alpine
RUN apk add --no-cache \
ca-certificates \
@ -15,4 +15,4 @@ COPY --from=alpine \
/usr/share/zoneinfo
COPY watchtower /
ENTRYPOINT ["/watchtower"]
ENTRYPOINT ["/watchtower"]

@ -0,0 +1,38 @@
#
# Builder
#
FROM golang:alpine as builder
# use version (for example "v0.3.3") or "master"
ARG WATCHTOWER_VERSION=master
RUN apk add --no-cache \
alpine-sdk \
ca-certificates \
git \
tzdata
COPY . /watchtower
RUN \
cd /watchtower && \
\
GO111MODULE=on CGO_ENABLED=0 GOOS=linux go build -a -ldflags '-extldflags "-static"' . && \
GO111MODULE=on go test ./... -v
#
# watchtower
#
FROM scratch
LABEL "com.centurylinklabs.watchtower"="true"
# copy files from other container
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt
COPY --from=builder /usr/share/zoneinfo /usr/share/zoneinfo
COPY --from=builder /watchtower/watchtower /watchtower
ENTRYPOINT ["/watchtower"]

@ -1,64 +0,0 @@
Some private docker registries (the most prominent probably being AWS ECR) use non-standard ways of authentication.
To be able to use this together with watchtower, we need to use a credential helper.
To keep the image size small we've decided to not include any helpers in the watchtower image, instead we'll put the
helper in a separate container and mount it using volumes.
### Example
Example implementation for use with [amazon-ecr-credential-helper](https://github.com/awslabs/amazon-ecr-credential-helper):
```Dockerfile
FROM golang:latest
ENV CGO_ENABLED 0
ENV REPO github.com/awslabs/amazon-ecr-credential-helper/ecr-login/cli/docker-credential-ecr-login
RUN go get -u $REPO
RUN rm /go/bin/docker-credential-ecr-login
RUN go build \
-o /go/bin/docker-credential-ecr-login \
/go/src/$REPO
WORKDIR /go/bin/
```
and the docker-compose definition:
```yaml
version: "3"
services:
watchtower:
image: index.docker.io/containrrr/watchtower:latest
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- <PATH_TO_HOME_DIR>/.docker/config.json:/config.json
- helper:/go/bin
environment:
- HOME=/
- PATH=$PATH:/go/bin
- AWS_REGION=<AWS_REGION>
- AWS_ACCESS_KEY_ID=<AWS_ACCESS_KEY>
- AWS_SECRET_ACCESS_KEY=<AWS_SECRET_ACCESS_KEY>
volumes:
helper: {}
```
and for `.docker/config.yml`:
```yaml
{
"HttpHeaders" : {
"User-Agent" : "Docker-Client/19.03.1 (XXXXXX)"
},
"credsStore" : "osxkeychain", // ...or your prefered helper
"auths" : {
"xyzxyzxyz.dkr.ecr.eu-north-1.amazonaws.com" : {},
"https://index.docker.io/v1/": {}
},
"credHelpers": {
"xyzxyzxyz.dkr.ecr.eu-north-1.amazonaws.com" : "ecr-login",
"index.docker.io": "osxkeychain" // ...or your prefered helper
}
}
```

@ -0,0 +1,118 @@
Watchtower supports private Docker image registries. In many cases, accessing a private registry requires a valid username and password (i.e., _credentials_). In order to operate in such an environment, watchtower needs to know the credentials to access the registry.
The credentials can be provided to watchtower in a configuration file called `config.json`. There are two ways to generate this configuration file:
* The configuration file can be created manually.
* Call `docker login <REGISTRY_NAME>` and share the resulting configuration file.
### Create the configuration file manually
Create a new configuration file with the following syntax and a base64 encoded username and password `auth` string:
```json
{
"auths": {
"<REGISTRY_NAME>": {
"auth": "XXXXXXX"
}
}
}
```
`<REGISTRY_NAME>` needs to be replaced by the name of your private registry (e.g., `my-private-registry.example.org`)
The required `auth` string can be generated as follows:
```bash
echo -n 'username:password' | base64
```
When the watchtower Docker container is started, the created configuration file (`<PATH>/config.json` in this example) needs to be passed to the container:
```bash
docker run [...] -v <PATH>/config.json:/config.json containrrr/watchtower
```
### Share the Docker configuration file
To pull an image from a private registry, `docker login` needs to be called first, to get access to the registry. The provided credentials are stored in a configuration file called `<PATH_TO_HOME_DIR>/.docker/config.json`. This configuration file can be directly used by watchtower. In this case, the creation of an additional configuration file is not necessary.
When the Docker container is started, pass the configuration file to watchtower:
```bash
docker run [...] -v <PATH_TO_HOME_DIR>/.docker/config.json:/config.json containrrr/watchtower
```
When creating the watchtower container via docker-compose, use the following lines:
```yaml
version: "3"
[...]
watchtower:
image: index.docker.io/containrrr/watchtower:latest
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- <PATH_TO_HOME_DIR>/.docker/config.json:/config.json
[...]
```
## Credential helpers
Some private Docker registries (the most prominent probably being AWS ECR) use non-standard ways of authentication.
To be able to use this together with watchtower, we need to use a credential helper.
To keep the image size small we've decided to not include any helpers in the watchtower image, instead we'll put the
helper in a separate container and mount it using volumes.
### Example
Example implementation for use with [amazon-ecr-credential-helper](https://github.com/awslabs/amazon-ecr-credential-helper):
```Dockerfile
FROM golang:latest
ENV CGO_ENABLED 0
ENV REPO github.com/awslabs/amazon-ecr-credential-helper/ecr-login/cli/docker-credential-ecr-login
RUN go get -u $REPO
RUN rm /go/bin/docker-credential-ecr-login
RUN go build \
-o /go/bin/docker-credential-ecr-login \
/go/src/$REPO
WORKDIR /go/bin/
```
and the docker-compose definition:
```yaml
version: "3"
services:
watchtower:
image: index.docker.io/containrrr/watchtower:latest
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- <PATH_TO_HOME_DIR>/.docker/config.json:/config.json
- helper:/go/bin
environment:
- HOME=/
- PATH=$PATH:/go/bin
- AWS_REGION=<AWS_REGION>
- AWS_ACCESS_KEY_ID=<AWS_ACCESS_KEY>
- AWS_SECRET_ACCESS_KEY=<AWS_SECRET_ACCESS_KEY>
volumes:
helper: {}
```
and for `<PATH_TO_HOME_DIR>/.docker/config.json`:
```json
{
"HttpHeaders" : {
"User-Agent" : "Docker-Client/19.03.1 (XXXXXX)"
},
"credsStore" : "osxkeychain",
"auths" : {
"xyzxyzxyz.dkr.ecr.eu-north-1.amazonaws.com" : {},
"https://index.docker.io/v1/": {}
},
"credHelpers": {
"xyzxyzxyz.dkr.ecr.eu-north-1.amazonaws.com" : "ecr-login",
"index.docker.io": "osxkeychain"
}
}
```
*Note:* `osxkeychain` can be changed to your prefered credentials helper.

@ -3,63 +3,57 @@ module github.com/containrrr/watchtower
go 1.12
require (
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78
github.com/Microsoft/go-winio v0.4.12
github.com/agl/ed25519 v0.0.0-20170116200512-5312a6153412
github.com/beorn7/perks v1.0.0
github.com/brysgo/gomock_ginkgo v0.0.0-20180512161304-be2c1b0e4111
github.com/containerd/containerd v1.2.6 // indirect
github.com/containerd/continuity v0.0.0-20181203112020-004b46473808
github.com/davecgh/go-spew v1.1.1
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 // indirect
github.com/Microsoft/go-winio v0.4.12 // indirect
github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d // indirect
github.com/agl/ed25519 v0.0.0-20170116200512-5312a6153412 // indirect
github.com/bitly/go-hostpool v0.0.0-20171023180738-a3a6125de932 // indirect
github.com/bitly/go-simplejson v0.5.0 // indirect
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 // indirect
github.com/bugsnag/bugsnag-go v1.5.3 // indirect
github.com/bugsnag/panicwrap v1.2.0 // indirect
github.com/cenkalti/backoff v2.2.1+incompatible // indirect
github.com/cloudflare/cfssl v0.0.0-20190911221928-1a911ca1b1d6 // indirect
github.com/containerd/continuity v0.0.0-20181203112020-004b46473808 // indirect
github.com/docker/cli v0.0.0-20190327152802-57b27434ea29
github.com/docker/distribution v2.7.1+incompatible
github.com/docker/docker v0.0.0-20190404075923-dbe4a30928d4
github.com/docker/docker-credential-helpers v0.6.1
github.com/docker/go v1.5.1-1
github.com/docker/go-connections v0.4.0
github.com/docker/go-metrics v0.0.0-20181218153428-b84716841b82
github.com/docker/go-units v0.3.3
github.com/docker/swarmkit v1.12.0 // indirect
github.com/gogo/protobuf v1.2.1
github.com/golang/mock v1.1.1
github.com/golang/protobuf v1.3.1
github.com/gorilla/mux v1.7.0
github.com/hashicorp/go-memdb v1.0.0 // indirect
github.com/hashicorp/go-version v1.1.0
github.com/inconshreveable/mousetrap v1.0.0
github.com/johntdyer/slack-go v0.0.0-20180213144715-95fac1160b22
github.com/docker/docker-credential-helpers v0.6.1 // indirect
github.com/docker/go v1.5.1-1 // indirect
github.com/docker/go-connections v0.4.0 // indirect
github.com/docker/go-metrics v0.0.0-20181218153428-b84716841b82 // indirect
github.com/docker/go-units v0.3.3 // indirect
github.com/docker/libtrust v0.0.0-20160708172513-aabc10ec26b7 // indirect
github.com/gofrs/uuid v3.2.0+incompatible // indirect
github.com/google/certificate-transparency-go v1.0.21 // indirect
github.com/gorilla/mux v1.7.0 // indirect
github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed // indirect
github.com/hashicorp/go-version v1.1.0 // indirect
github.com/inconshreveable/mousetrap v1.0.0 // indirect
github.com/jinzhu/gorm v1.9.11 // indirect
github.com/johntdyer/slack-go v0.0.0-20180213144715-95fac1160b22 // indirect
github.com/johntdyer/slackrus v0.0.0-20180518184837-f7aae3243a07
github.com/konsorten/go-windows-terminal-sequences v1.0.2
github.com/mattn/go-shellwords v1.0.5 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.1
github.com/miekg/pkcs11 v0.0.0-20190401114359-553cfdd26aaa
github.com/morikuni/aec v0.0.0-20170113033406-39771216ff4c
github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 // indirect
github.com/konsorten/go-windows-terminal-sequences v1.0.2 // indirect
github.com/lib/pq v1.2.0 // indirect
github.com/miekg/pkcs11 v0.0.0-20190401114359-553cfdd26aaa // indirect
github.com/morikuni/aec v0.0.0-20170113033406-39771216ff4c // indirect
github.com/onsi/ginkgo v1.8.0
github.com/onsi/gomega v1.5.0
github.com/opencontainers/go-digest v1.0.0-rc1
github.com/opencontainers/image-spec v1.0.1
github.com/opencontainers/go-digest v1.0.0-rc1 // indirect
github.com/opencontainers/image-spec v1.0.1 // indirect
github.com/opencontainers/runc v0.1.1
github.com/opencontainers/runtime-spec v1.0.1 // indirect
github.com/opencontainers/selinux v1.2.1 // indirect
github.com/pkg/errors v0.8.1
github.com/pmezard/go-difflib v1.0.0
github.com/prometheus/client_golang v0.9.3
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90
github.com/prometheus/common v0.4.0
github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084
github.com/pkg/errors v0.8.1 // indirect
github.com/robfig/cron v0.0.0-20180505203441-b41be1df6967
github.com/sirupsen/logrus v1.4.1
github.com/spf13/cobra v0.0.3
github.com/spf13/pflag v1.0.3
github.com/spf13/viper v1.4.0
github.com/stretchr/objx v0.1.1
github.com/stretchr/testify v1.3.0
github.com/theupdateframework/notary v0.6.1
github.com/vbatts/tar-split v0.11.1 // indirect
golang.org/x/crypto v0.0.0-20190403202508-8e1b8d32e692
github.com/theupdateframework/notary v0.6.1 // indirect
github.com/zmap/zlint v1.0.2 // indirect
golang.org/x/net v0.0.0-20190522155817-f3200d17e092
golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e
google.golang.org/genproto v0.0.0-20190401181712-f467c93bbac2
google.golang.org/grpc v1.21.0
gopkg.in/dancannon/gorethink.v3 v3.0.5 // indirect
gopkg.in/fatih/pool.v2 v2.0.0 // indirect
gopkg.in/gorethink/gorethink.v3 v3.0.5 // indirect
gotest.tools v2.2.0+incompatible // indirect
)

152
go.sum

@ -1,25 +1,46 @@
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.37.4 h1:glPeL3BQJsbF6aIIYfZizMwc5LTYz250bDMjttbBGAU=
cloud.google.com/go v0.37.4/go.mod h1:NHPJ89PdicEuT9hdPXMROBD91xc5uRDxsMtSB16k7hw=
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/Microsoft/go-winio v0.4.12 h1:xAfWHN1IrQ0NJ9TBC0KBZoqLjzDTr1ML+4MywiUOryc=
github.com/Microsoft/go-winio v0.4.12/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA=
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d h1:UrqY+r/OJnIp5u0s1SbQ8dVfLCZJsnvazdBP5hS4iRs=
github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d/go.mod h1:HI8ITrYtUY+O+ZhtlqUnD8+KwNPOyugEhfP9fdUIaEQ=
github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo=
github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI=
github.com/agl/ed25519 v0.0.0-20170116200512-5312a6153412 h1:w1UutsfOrms1J05zt7ISrnJIXKzwaspym5BTKGx93EI=
github.com/agl/ed25519 v0.0.0-20170116200512-5312a6153412/go.mod h1:WPjqKcmVOxf0XSf3YxCJs6N6AOSrOx3obionmG7T0y0=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc h1:cAKDfWh5VpdgMhJosfJnn5/FoN2SRZ4p7fJNX58YPaU=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf h1:qet1QNfXsQxTZqLG4oE62mJzwPIB8+Tee4RNCL9ulrY=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973 h1:xJ4a3vCFaGF/jqvzLMYoU8P317H5OQ+Via4RmuPwCS0=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/beorn7/perks v1.0.0 h1:HWo1m869IqiPhD389kmkxeTalrjNbbJTC8LXupb+sl0=
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
github.com/brysgo/gomock_ginkgo v0.0.0-20180512161304-be2c1b0e4111 h1:gRfsoKtF1tba+hVsNgo7OKG7a35hBK30ouOTHPgqFf8=
github.com/brysgo/gomock_ginkgo v0.0.0-20180512161304-be2c1b0e4111/go.mod h1:H1ipqq0hhUWJgVeQ5dbUe/C8YptJrE/VGDQp9bI+qTo=
github.com/bitly/go-hostpool v0.0.0-20171023180738-a3a6125de932 h1:mXoPYz/Ul5HYEDvkta6I8/rnYM5gSdSV2tJ6XbZuEtY=
github.com/bitly/go-hostpool v0.0.0-20171023180738-a3a6125de932/go.mod h1:NOuUCSz6Q9T7+igc/hlvDOUdtWKryOrtFyIVABv/p7k=
github.com/bitly/go-simplejson v0.5.0 h1:6IH+V8/tVMab511d5bn4M7EwGXZf9Hj6i2xSwkNEM+Y=
github.com/bitly/go-simplejson v0.5.0/go.mod h1:cXHtHw4XUPsvGaxgjIAn8PhEWG9NfngEKAMDJEczWVA=
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 h1:DDGfHa7BWjL4YnC6+E63dPcxHo2sUxDIu8g3QgEJdRY=
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4=
github.com/bugsnag/bugsnag-go v1.5.3 h1:yeRUT3mUE13jL1tGwvoQsKdVbAsQx9AJ+fqahKveP04=
github.com/bugsnag/bugsnag-go v1.5.3/go.mod h1:2oa8nejYd4cQ/b0hMIopN0lCRxU0bueqREvZLWFrtK8=
github.com/bugsnag/panicwrap v1.2.0 h1:OzrKrRvXis8qEvOkfcxNcYbOd2O7xXS2nnKMEMABFQA=
github.com/bugsnag/panicwrap v1.2.0/go.mod h1:D/8v3kj0zr8ZAKg1AQ6crr+5VwKN5eIywRkfhyM/+dE=
github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4=
github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/containerd/containerd v1.2.6 h1:K38ZSAA9oKSrX3iFNY+4SddZ8hH1TCMCerc8NHfcKBQ=
github.com/containerd/containerd v1.2.6/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA=
github.com/cloudflare/cfssl v0.0.0-20190911221928-1a911ca1b1d6 h1:A7RURps5t4yDU0zktlgrE3Bdmjfv35nVs+xJdoWgIgY=
github.com/cloudflare/cfssl v0.0.0-20190911221928-1a911ca1b1d6/go.mod h1:yMWuSON2oQp+43nFtAV/uvKQIFpSPerB57DCt9t8sSA=
github.com/containerd/continuity v0.0.0-20181203112020-004b46473808 h1:4BX8f882bXEDKfWIf0wa8HRvpnBoPszJJXL+TVbBw4M=
github.com/containerd/continuity v0.0.0-20181203112020-004b46473808/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y=
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
@ -30,6 +51,8 @@ github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfc
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/denisenkom/go-mssqldb v0.0.0-20190515213511-eb9f6a1743f3 h1:tkum0XDgfR0jcVVXuTsYv/erY2NnEDqwRojbxR1rBYA=
github.com/denisenkom/go-mssqldb v0.0.0-20190515213511-eb9f6a1743f3/go.mod h1:zAg7JM8CkOJ43xKXIj7eRO9kmWm/TW578qo+oDO6tuM=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
github.com/docker/cli v0.0.0-20190327152802-57b27434ea29 h1:ciaXDHaWQda0nvevWqcjtXX/buQY3e0lga1vq8Batq0=
@ -48,38 +71,56 @@ github.com/docker/go-metrics v0.0.0-20181218153428-b84716841b82 h1:X0fj836zx99zF
github.com/docker/go-metrics v0.0.0-20181218153428-b84716841b82/go.mod h1:/u0gXw0Gay3ceNrsHubL3BtdOL2fHf93USgMTe0W5dI=
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/swarmkit v1.12.0 h1:vcbNXevt9xOod0miQxkp9WZ70IsOCe8geXkmFnXP2e0=
github.com/docker/swarmkit v1.12.0/go.mod h1:n3Z4lIEl7g261ptkGDBcYi/3qBMDl9csaAhwi2MPejs=
github.com/docker/libtrust v0.0.0-20160708172513-aabc10ec26b7 h1:UhxFibDNY/bfvqU5CAUmr9zpesgbU6SWc8/B4mflAE4=
github.com/docker/libtrust v0.0.0-20160708172513-aabc10ec26b7/go.mod h1:cyGadeNEkKy96OOhEzfZl+yxihPEzKnqJwvfuSUqbZE=
github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs=
github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU=
github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I=
github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5 h1:Yzb9+7DPaBjB8zlTR87/ElzFsnQfuHnVUVqpZZIcV5Y=
github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5/go.mod h1:a2zkGnVExMxdzMo3M0Hi/3sEU+cWnZpSni0O6/Yb/P0=
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
github.com/go-sql-driver/mysql v1.4.1 h1:g24URVg0OFbNUTx9qqY1IRZ9D9z3iPyi5zKhQZpNwpA=
github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/gofrs/uuid v3.2.0+incompatible h1:y12jRkkFxsd7GpqdSZ+/KCs/fJbqpEXSGd4+jfEaewE=
github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.2.1 h1:/s5zKNz0uPFCZ5hddgPdo2TK2TVrUNMn0OOX8/aZMTE=
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/mock v1.1.1 h1:G5FRp8JnTd7RQH5kemVNlMeyXQAztQ3mOWV95KxsXH8=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.2.0/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 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/certificate-transparency-go v1.0.21 h1:Yf1aXowfZ2nuboBsg7iYGLmwsOARdV86pfH3g95wXmE=
github.com/google/certificate-transparency-go v1.0.21/go.mod h1:QeJfpSbVSfYc7RgB3gJFj9cbuQMMchQxrWXz8Ruopmg=
github.com/google/go-cmp v0.2.0 h1:+dTQ8DZQJz0Mb/HjFlkptS1FeQ4cWSnN941F8aEG4SQ=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
github.com/gorilla/mux v1.7.0 h1:tOSd0UKHQd6urX6ApfOn4XdBMY6Sh1MfxV3kmaazO+U=
github.com/gorilla/mux v1.7.0/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
github.com/hashicorp/go-memdb v1.0.0 h1:K1O4N2VPndZiTrdH3lmmf5bemr9Xw81KjVwhReIUjTQ=
github.com/hashicorp/go-memdb v1.0.0/go.mod h1:I6dKdmYhZqU0RJSheVEWgTNWdVQH5QvTgIUQ0t/t32M=
github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed h1:5upAirOpQc1Q53c0bnx2ufif5kANL7bfZWcc6VJWJd8=
github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed/go.mod h1:tMWxXQ9wFIaZeTI9F+hmhFiGpFmhOHzyShyFUhRm0H4=
github.com/hashicorp/go-version v1.1.0 h1:bPIoEKD27tNdebFGGxxYwcL4nepeY4j1QP23PFRGzg0=
github.com/hashicorp/go-version v1.1.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
@ -89,79 +130,97 @@ github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/jinzhu/gorm v1.9.11 h1:gaHGvE+UnWGlbWG4Y3FUwY1EcZ5n6S9WtqBA/uySMLE=
github.com/jinzhu/gorm v1.9.11/go.mod h1:bu/pK8szGZ2puuErfU0RwyeNdsf3e6nCX/noXaVxkfw=
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
github.com/jinzhu/now v1.0.1 h1:HjfetcXq097iXP0uoPCdnM4Efp5/9MsM0/M+XOTeR3M=
github.com/jinzhu/now v1.0.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
github.com/johntdyer/slack-go v0.0.0-20180213144715-95fac1160b22 h1:jKUP9TQ0c7X3w6+IPyMit07RE42MtTWNd77sN2cHngQ=
github.com/johntdyer/slack-go v0.0.0-20180213144715-95fac1160b22/go.mod h1:u0Jo4f2dNlTJeeOywkM6bLwxq6gC3pZ9rEFHn3AhTdk=
github.com/johntdyer/slackrus v0.0.0-20180518184837-f7aae3243a07 h1:+kBG/8rjCa6vxJZbUjAiE4MQmBEBYc8nLEb51frnvBY=
github.com/johntdyer/slackrus v0.0.0-20180518184837-f7aae3243a07/go.mod h1:j1kV/8f3jowErEq4XyeypkCdvg5EeHkf0YCKCcq5Ybo=
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 h1:iQTw/8FWTuc7uiaSepXwyf3o52HaUYcV+Tu66S3F5GA=
github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0/go.mod h1:1NbS8ALrpOvjt0rHPNLyCIeMtbizbir8U//inJ+zuB8=
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
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/konsorten/go-windows-terminal-sequences v1.0.2 h1:DB17ag19krx9CFsz4o3enTrPXyIXCl+2iCXH/aMAp9s=
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
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/lib/pq v1.1.1/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lib/pq v1.2.0 h1:LXpIM/LZ5xGFhOpXAQUIMM1HdyqzVYM13zNdjCEEcA0=
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/magiconair/properties v1.8.0 h1:LLgXmsheXeRoUOBOjtwPQCWIYqM/LU1ayDtDePerRcY=
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/mattn/go-shellwords v1.0.5/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o=
github.com/mattn/go-sqlite3 v1.11.0 h1:LDdKkqtYlom37fkvqs8rMPFKAMe8+SgjbwZ6ex1/A/Q=
github.com/mattn/go-sqlite3 v1.11.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/miekg/pkcs11 v0.0.0-20190401114359-553cfdd26aaa h1:gOXc1BXmFuxWYmTfoK51YJR7srco3CwbsVHgr+8Y4r0=
github.com/miekg/pkcs11 v0.0.0-20190401114359-553cfdd26aaa/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs=
github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/morikuni/aec v0.0.0-20170113033406-39771216ff4c h1:nXxl5PrvVm2L/wCy8dQu6DMTwH4oIuGN8GJDAlqDdVE=
github.com/morikuni/aec v0.0.0-20170113033406-39771216ff4c/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
github.com/mreiferson/go-httpclient v0.0.0-20160630210159-31f0106b4474/go.mod h1:OQA4XLvDbMgS8P0CevmM4m9Q3Jq4phKUzcocxuGJ5m8=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.8.0 h1:VkHVNpR4iVnU8XQR6DBm8BqYjN7CRzw+xKUbVVbbW9w=
github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/onsi/gomega v1.5.0 h1:izbySO9zDPmjJ8rDjLvkA2zJHIo+HkYXHnf7eN7SSyo=
github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk=
github.com/opencontainers/go-digest v1.0.0-rc1 h1:WzifXhOVOEOuFYOJAW6aQqW0TooG2iki3E3Ii+WN7gQ=
github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
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.1.1 h1:GlxAyO6x8rfZYN9Tt0Kti5a/cP41iuiO2yYT0IJGY8Y=
github.com/opencontainers/runc v0.1.1/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U=
github.com/opencontainers/runtime-spec v1.0.1/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
github.com/opencontainers/selinux v1.2.1 h1:Svlc+L67YcjN4K2bqD8Wlw9jtMlmZ+1FEGn6zsm8am0=
github.com/opencontainers/selinux v1.2.1/go.mod h1:+BLncwf63G4dgOzykXAxcmnFlUaOlkDdmw/CqsW6pjs=
github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw=
github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc=
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
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/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_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_golang v0.9.2 h1:awm861/B8OKDd2I/6o1dy3ra4BamzKhYOiGItCeZ740=
github.com/prometheus/client_golang v0.9.2/go.mod h1:OsXs2jCmiKlQ1lTBmv21f2mNfw4xf/QclQDMrYNZzcM=
github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs=
github.com/prometheus/client_golang v0.9.3 h1:9iH4JKXLzFbOAdtqv/a+j8aewx2Y8lAjAydhbaScPF8=
github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90 h1:S/YWwWx/RA8rT8tKFRuGUZhuA90OyIBpPCXkcbwU8DE=
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
github.com/prometheus/common v0.0.0-20181126121408-4724e9255275/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
github.com/prometheus/common v0.2.0 h1:kUZDBDTdBVBYBj5Tmh2NZLlF60mfjA27rM34b+cVwNU=
github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/common v0.4.0 h1:7etb9YClo3a6HjLzfl6rIQaU+FDfi0VSX39io3aQ+DM=
github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.0-20190403104016-ea9eea638872 h1:0aNv3xC7DmQoy1/x1sMh18g+fihWW68LL13i8ao9kl4=
github.com/prometheus/procfs v0.0.0-20190403104016-ea9eea638872/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084 h1:sofwID9zm4tzrgykg80hfFph1mryUeLRsUfoocVVmRY=
github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
github.com/robfig/cron v0.0.0-20180505203441-b41be1df6967 h1:x7xEyJDP7Hv3LVgvWhzioQqbC/KtuUhTigKlH/8ehhE=
github.com/robfig/cron v0.0.0-20180505203441-b41be1df6967/go.mod h1:JGuDeoQd7Z6yL4zQhZ3OPEVHB7fL6Ka6skscFHfmt2k=
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.3.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.4.1 h1:GL2rEmy6nsikmW0r8opw9JIRScdMF5hA8cOYLH7In1k=
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
@ -188,34 +247,45 @@ github.com/theupdateframework/notary v0.6.1 h1:7wshjstgS9x9F5LuB1L5mBI2xNMObWqjz
github.com/theupdateframework/notary v0.6.1/go.mod h1:MOfgIfmox8s7/7fduvB2xyPPMJCrjRLRizA8OFwpnKY=
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
github.com/vbatts/tar-split v0.11.1/go.mod h1:LEuURwDEiWjRjwu46yU3KVGuUdVv/dcnpcEPSzR8z6g=
github.com/weppos/publicsuffix-go v0.4.0 h1:YSnfg3V65LcCFKtIGKGoBhkyKolEd0hlipcXaOjdnQw=
github.com/weppos/publicsuffix-go v0.4.0/go.mod h1:z3LCPQ38eedDQSwmsSRW4Y7t2L8Ln16JPQ02lHAdn5k=
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
github.com/zmap/rc2 v0.0.0-20131011165748-24b9757f5521/go.mod h1:3YZ9o3WnatTIZhuOtot4IcUfzoKVjUHqu6WALIyI0nE=
github.com/zmap/zcertificate v0.0.0-20180516150559-0e3d58b1bac4/go.mod h1:5iU54tB79AMBcySS0R2XIyZBAVmeHranShAFELYx7is=
github.com/zmap/zcrypto v0.0.0-20190729165852-9051775e6a2e h1:mvOa4+/DXStR4ZXOks/UsjeFdn5O5JpLUtzqk9U8xXw=
github.com/zmap/zcrypto v0.0.0-20190729165852-9051775e6a2e/go.mod h1:w7kd3qXHh8FNaczNjslXqvFQiv5mMWRXlL9klTUAHc8=
github.com/zmap/zlint v1.0.2 h1:07+WuC/prlXVlWa1CJx2lCpuCd8biIeBAVnwTN2CPaA=
github.com/zmap/zlint v1.0.2/go.mod h1:29UiAJNsiVdvTBFCJW8e3q6dcDbOoPkhMgttOSCIMMY=
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190403202508-8e1b8d32e692 h1:GRhHqDOgeDr6QDTtq9gn2O4iKvm5dsbfqD/TXb0KLX0=
golang.org/x/crypto v0.0.0-20190403202508-8e1b8d32e692/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=
golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4 h1:HuIa8hRrWRSrqYzx1qI49NNxhdi2PrY7gxVSq1JjLDc=
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
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-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
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-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/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-20190403144856-b630fd6fe46b h1:/zjbcJPEGAyu6Is/VBOALsgdi4z9+kz/Vtdm6S+beD0=
golang.org/x/net v0.0.0-20190403144856-b630fd6fe46b/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190522155817-f3200d17e092 h1:4QSRKanuywn15aTZvI/mIDEgPQpswuFndXpOj3rKEco=
golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
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-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@ -226,32 +296,48 @@ golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5h
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/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-20190403152447-81d4e9dc473e h1:nFYrTHrdrAOpShe27kaFHjsqYSEQ0KWqdWLu3xuZJts=
golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190412213103-97732733099d h1:+R4KGOnez64A81RvjARKc4UT5/tI9ujCIVX+P5KiHuI=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2 h1:z99zHgr7hKfrUcX/KsoJk5FJfjTceCKIp96+biqP4To=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 h1:SvFZT6jyqRaOeXpc5h/JSfZenJ2O330aBsf7JfSUXmQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/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-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0 h1:/wp5JvzpHIxhs/dumFmF7BXTf3Z+dd4uXta4kVyO508=
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-20190401181712-f467c93bbac2 h1:8FyEBtGg6Px24p+H2AkuVWqhj4+R9fo+fZD17mg+lzk=
google.golang.org/genproto v0.0.0-20190401181712-f467c93bbac2/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190404172233-64821d5d2107 h1:xtNn7qFlagY2mQNFHMSRPjT2RkOV4OXM7P5TVy9xATo=
google.golang.org/genproto v0.0.0-20190404172233-64821d5d2107/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.19.1 h1:TrBcJ1yqAl1G++wO39nD/qtgpsW9/1+QGrluyMGEYgM=
google.golang.org/grpc v1.19.1/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.21.0 h1:G+97AoqBnmZIT91cLG/EkCoK9NSelj64P8bOHHNmGn0=
google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
gopkg.in/alecthomas/kingpin.v2 v2.2.6 h1:jMFz6MfLP0/4fUyZle81rXUoxOBFi19VUFKVDOQfozc=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
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/dancannon/gorethink.v3 v3.0.5 h1:/g7PWP7zUS6vSNmHSDbjCHQh1Rqn8Jy6zSMQxAsBSMQ=
gopkg.in/dancannon/gorethink.v3 v3.0.5/go.mod h1:GXsi1e3N2OcKhcP6nsYABTiUejbWMFO4GY5a4pEaeEc=
gopkg.in/fatih/pool.v2 v2.0.0 h1:xIFeWtxifuQJGk/IEPKsTduEKcKvPmhoiVDGpC40nKg=
gopkg.in/fatih/pool.v2 v2.0.0/go.mod h1:8xVGeu1/2jr2wm5V9SPuMht2H5AEmf5aFMGSQixtjTY=
gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/gorethink/gorethink.v3 v3.0.5 h1:e2Uc/Xe+hpcVQFsj6MuHlYog3r0JYpnTzwDj/y2O4MU=
gopkg.in/gorethink/gorethink.v3 v3.0.5/go.mod h1:+3yIIHJUGMBK+wyPH+iN5TP+88ikFDfZdqTlK3Y9q8I=
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
@ -261,4 +347,6 @@ gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
gopkg.in/yaml.v2 v2.2.2/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=
honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=

@ -29,7 +29,7 @@ dockers:
goos: linux
goarch: amd64
goarm: ''
dockerfile: Dockerfile
dockerfile: dockerfiles/Dockerfile
image_templates:
- containrrr/watchtower:amd64-{{ .Version }}
- containrrr/watchtower:amd64-latest
@ -39,7 +39,7 @@ dockers:
goos: linux
goarch: 386
goarm: ''
dockerfile: Dockerfile
dockerfile: dockerfiles/Dockerfile
image_templates:
- containrrr/watchtower:i386-{{ .Version }}
- containrrr/watchtower:i386-latest
@ -49,7 +49,7 @@ dockers:
goos: linux
goarch: arm
goarm: 6
dockerfile: Dockerfile
dockerfile: dockerfiles/Dockerfile
image_templates:
- containrrr/watchtower:armhf-{{ .Version }}
- containrrr/watchtower:armhf-latest
@ -59,7 +59,7 @@ dockers:
goos: linux
goarch: arm64
goarm: ''
dockerfile: Dockerfile
dockerfile: dockerfiles/Dockerfile
image_templates:
- containrrr/watchtower:arm64v8-{{ .Version }}
- containrrr/watchtower:arm64v8-latest

@ -1,18 +1,16 @@
package actions_test
import (
"errors"
"github.com/containrrr/watchtower/internal/actions"
"testing"
"time"
"github.com/containrrr/watchtower/pkg/container"
"github.com/containrrr/watchtower/pkg/container/mocks"
"github.com/docker/docker/api/types"
t "github.com/containrrr/watchtower/pkg/types"
cli "github.com/docker/docker/client"
. "github.com/containrrr/watchtower/internal/actions/mocks"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)
@ -24,7 +22,7 @@ func TestActions(t *testing.T) {
var _ = Describe("the actions package", func() {
var dockerClient cli.CommonAPIClient
var client mockClient
var client MockClient
BeforeSuite(func() {
server := mocks.NewMockAPIServer()
dockerClient, _ = cli.NewClientWithOpts(
@ -32,12 +30,15 @@ var _ = Describe("the actions package", func() {
cli.WithHTTPClient(server.Client()))
})
BeforeEach(func() {
client = mockClient{
api: dockerClient,
pullImages: false,
removeVolumes: false,
TestData: &TestData{},
}
pullImages := false
removeVolumes := false
client = CreateMockClient(
&TestData {},
dockerClient,
pullImages,
removeVolumes,
)
})
Describe("the check prerequisites method", func() {
@ -51,7 +52,7 @@ var _ = Describe("the actions package", func() {
When("given an array of one", func() {
It("should not do anything", func() {
client.TestData.Containers = []container.Container{
createMockContainer(
CreateMockContainer(
"test-container",
"test-container",
"watchtower",
@ -63,27 +64,30 @@ var _ = Describe("the actions package", func() {
})
When("given multiple containers", func() {
BeforeEach(func() {
client = mockClient{
api: dockerClient,
pullImages: false,
removeVolumes: false,
TestData: &TestData{
pullImages := false
removeVolumes := false
client = CreateMockClient(
&TestData{
NameOfContainerToKeep: "test-container-02",
Containers: []container.Container{
createMockContainer(
CreateMockContainer(
"test-container-01",
"test-container-01",
"watchtower",
time.Now().AddDate(0, 0, -1)),
createMockContainer(
CreateMockContainer(
"test-container-02",
"test-container-02",
"watchtower",
time.Now()),
},
},
}
dockerClient,
pullImages,
removeVolumes,
)
})
It("should stop all but the latest one", func() {
err := actions.CheckForMultipleWatchtowerInstances(client, false)
Expect(err).NotTo(HaveOccurred())
@ -91,96 +95,40 @@ var _ = Describe("the actions package", func() {
})
When("deciding whether to cleanup images", func() {
BeforeEach(func() {
client = mockClient{
api: dockerClient,
pullImages: false,
removeVolumes: false,
TestData: &TestData{
pullImages := false
removeVolumes := false
client = CreateMockClient(
&TestData{
Containers: []container.Container{
createMockContainer(
CreateMockContainer(
"test-container-01",
"test-container-01",
"watchtower",
time.Now().AddDate(0, 0, -1)),
createMockContainer(
CreateMockContainer(
"test-container-02",
"test-container-02",
"watchtower",
time.Now()),
},
},
}
dockerClient,
pullImages,
removeVolumes,
)
})
It("should try to delete the image if the cleanup flag is true", func() {
err := actions.CheckForMultipleWatchtowerInstances(client, true)
Expect(err).NotTo(HaveOccurred())
Expect(client.TestData.TriedToRemoveImage).To(BeTrue())
Expect(client.TestData.TriedToRemoveImage()).To(BeTrue())
})
It("should not try to delete the image if the cleanup flag is false", func() {
err := actions.CheckForMultipleWatchtowerInstances(client, false)
Expect(err).NotTo(HaveOccurred())
Expect(client.TestData.TriedToRemoveImage).To(BeFalse())
Expect(client.TestData.TriedToRemoveImage()).To(BeFalse())
})
})
})
})
func createMockContainer(id string, name string, image string, created time.Time) container.Container {
content := types.ContainerJSON{
ContainerJSONBase: &types.ContainerJSONBase{
ID: id,
Image: image,
Name: name,
Created: created.String(),
},
}
return *container.NewContainer(&content, nil)
}
type mockClient struct {
TestData *TestData
api cli.CommonAPIClient
pullImages bool
removeVolumes bool
}
type TestData struct {
TriedToRemoveImage bool
NameOfContainerToKeep string
Containers []container.Container
}
func (client mockClient) ListContainers(f t.Filter) ([]container.Container, error) {
return client.TestData.Containers, nil
}
func (client mockClient) StopContainer(c container.Container, d time.Duration) error {
if c.Name() == client.TestData.NameOfContainerToKeep {
return errors.New("tried to stop the instance we want to keep")
}
return nil
}
func (client mockClient) StartContainer(c container.Container) (string, error) {
panic("Not implemented")
}
func (client mockClient) RenameContainer(c container.Container, s string) error {
panic("Not implemented")
}
func (client mockClient) RemoveImage(c container.Container) error {
client.TestData.TriedToRemoveImage = true
return nil
}
func (client mockClient) GetContainer(containerID string) (container.Container, error) {
return container.Container{}, nil
}
func (client mockClient) ExecuteCommand(containerID string, command string) error {
return nil
}
func (client mockClient) IsContainerStale(c container.Container) (bool, error) {
panic("Not implemented")
}

@ -3,6 +3,8 @@ package actions
import (
"errors"
"fmt"
"github.com/containrrr/watchtower/pkg/filters"
"github.com/containrrr/watchtower/pkg/sorter"
"sort"
"strings"
"time"
@ -19,7 +21,7 @@ import (
// will stop and remove all but the most recently started container.
func CheckForMultipleWatchtowerInstances(client container.Client, cleanup bool) error {
awaitDockerClient()
containers, err := client.ListContainers(container.WatchtowerContainersFilter)
containers, err := client.ListContainers(filters.WatchtowerContainersFilter)
if err != nil {
log.Fatal(err)
@ -39,7 +41,7 @@ func cleanupExcessWatchtowers(containers []container.Container, client container
var cleanupErrors int
var stopErrors int
sort.Sort(container.ByCreated(containers))
sort.Sort(sorter.ByCreated(containers))
allContainersExceptLast := containers[0 : len(containers)-1]
for _, c := range allContainersExceptLast {
@ -51,7 +53,7 @@ func cleanupExcessWatchtowers(containers []container.Container, client container
}
if cleanup {
if err := client.RemoveImage(c); err != nil {
if err := client.RemoveImageByID(c.ImageID()); err != nil {
// logging the original here as we're just returning a count
logrus.Error(err)
cleanupErrors++

@ -0,0 +1,84 @@
package mocks
import (
"errors"
"github.com/containrrr/watchtower/pkg/container"
"time"
t "github.com/containrrr/watchtower/pkg/types"
cli "github.com/docker/docker/client"
)
// MockClient is a mock that passes as a watchtower Client
type MockClient struct {
TestData *TestData
api cli.CommonAPIClient
pullImages bool
removeVolumes bool
}
// TestData is the data used to perform the test
type TestData struct {
TriedToRemoveImageCount int
NameOfContainerToKeep string
Containers []container.Container
}
// TriedToRemoveImage is a test helper function to check whether RemoveImageByID has been called
func (testdata *TestData) TriedToRemoveImage() bool {
return testdata.TriedToRemoveImageCount > 0
}
// CreateMockClient creates a mock watchtower Client for usage in tests
func CreateMockClient(data *TestData, api cli.CommonAPIClient, pullImages bool, removeVolumes bool) MockClient {
return MockClient {
data,
api,
pullImages,
removeVolumes,
}
}
// ListContainers is a mock method returning the provided container testdata
func (client MockClient) ListContainers(f t.Filter) ([]container.Container, error) {
return client.TestData.Containers, nil
}
// StopContainer is a mock method
func (client MockClient) StopContainer(c container.Container, d time.Duration) error {
if c.Name() == client.TestData.NameOfContainerToKeep {
return errors.New("tried to stop the instance we want to keep")
}
return nil
}
// StartContainer is a mock method
func (client MockClient) StartContainer(c container.Container) (string, error) {
return "", nil
}
// RenameContainer is a mock method
func (client MockClient) RenameContainer(c container.Container, s string) error {
return nil
}
// RemoveImageByID increments the TriedToRemoveImageCount on being called
func (client MockClient) RemoveImageByID(id string) error {
client.TestData.TriedToRemoveImageCount++
return nil
}
// GetContainer is a mock method
func (client MockClient) GetContainer(containerID string) (container.Container, error) {
return container.Container{}, nil
}
// ExecuteCommand is a mock method
func (client MockClient) ExecuteCommand(containerID string, command string) error {
return nil
}
// IsContainerStale is always true for the mock client
func (client MockClient) IsContainerStale(c container.Container) (bool, error) {
return true, nil
}

@ -0,0 +1,29 @@
package mocks
import (
"github.com/containrrr/watchtower/pkg/container"
"github.com/docker/docker/api/types"
container2 "github.com/docker/docker/api/types/container"
"time"
)
// CreateMockContainer creates a container substitute valid for testing
func CreateMockContainer(id string, name string, image string, created time.Time) container.Container {
content := types.ContainerJSON{
ContainerJSONBase: &types.ContainerJSONBase{
ID: id,
Image: image,
Name: name,
Created: created.String(),
},
Config: &container2.Config{
Labels: make(map[string]string),
},
}
return *container.NewContainer(
&content,
&types.ImageInspect{
ID: image,
},
)
}

@ -3,6 +3,9 @@ package actions
import (
"github.com/containrrr/watchtower/internal/util"
"github.com/containrrr/watchtower/pkg/container"
"github.com/containrrr/watchtower/pkg/lifecycle"
"github.com/containrrr/watchtower/pkg/sorter"
"github.com/containrrr/watchtower/pkg/types"
log "github.com/sirupsen/logrus"
)
@ -10,10 +13,12 @@ import (
// used to start those containers have been updated. If a change is detected in
// any of the images, the associated containers are stopped and restarted with
// the new image.
func Update(client container.Client, params UpdateParams) error {
func Update(client container.Client, params types.UpdateParams) error {
log.Debug("Checking containers for updated images")
executePreCheck(client, params)
if params.LifecycleHooks {
lifecycle.ExecutePreChecks(client, params)
}
containers, err := client.ListContainers(params.Filter)
if err != nil {
@ -30,7 +35,7 @@ func Update(client container.Client, params UpdateParams) error {
containers[i].Stale = stale
}
containers, err = container.SortByDependencies(containers)
containers, err = sorter.SortByDependencies(containers)
if err != nil {
return err
}
@ -38,24 +43,28 @@ func Update(client container.Client, params UpdateParams) error {
checkDependencies(containers)
if params.MonitorOnly {
executePostCheck(client, params)
if params.LifecycleHooks {
lifecycle.ExecutePostChecks(client, params)
}
return nil
}
stopContainersInReversedOrder(containers, client, params)
restartContainersInSortedOrder(containers, client, params)
executePostCheck(client, params)
if params.LifecycleHooks {
lifecycle.ExecutePostChecks(client, params)
}
return nil
}
func stopContainersInReversedOrder(containers []container.Container, client container.Client, params UpdateParams) {
func stopContainersInReversedOrder(containers []container.Container, client container.Client, params types.UpdateParams) {
for i := len(containers) - 1; i >= 0; i-- {
stopStaleContainer(containers[i], client, params)
}
}
func stopStaleContainer(container container.Container, client container.Client, params UpdateParams) {
func stopStaleContainer(container container.Container, client container.Client, params types.UpdateParams) {
if container.IsWatchtower() {
log.Debugf("This is the watchtower container %s", container.Name())
return
@ -64,24 +73,36 @@ func stopStaleContainer(container container.Container, client container.Client,
if !container.Stale {
return
}
if params.LifecycleHooks {
lifecycle.ExecutePreUpdateCommand(client, container)
executePreUpdateCommand(client, container)
}
if err := client.StopContainer(container, params.Timeout); err != nil {
log.Error(err)
}
}
func restartContainersInSortedOrder(containers []container.Container, client container.Client, params UpdateParams) {
func restartContainersInSortedOrder(containers []container.Container, client container.Client, params types.UpdateParams) {
imageIDs := make(map[string]bool)
for _, container := range containers {
if !container.Stale {
continue
}
restartStaleContainer(container, client, params)
imageIDs[container.ImageID()] = true
}
if params.Cleanup {
for imageID := range imageIDs {
if err := client.RemoveImageByID(imageID); err != nil {
log.Error(err)
}
}
}
}
func restartStaleContainer(container container.Container, client container.Client, params UpdateParams) {
func restartStaleContainer(container container.Container, client container.Client, params types.UpdateParams) {
// Since we can't shutdown a watchtower container immediately, we need to
// start the new one while the old one is still running. This prevents us
// from re-using the same container name so we first rename the current
@ -97,13 +118,7 @@ func restartStaleContainer(container container.Container, client container.Clien
if newContainerID, err := client.StartContainer(container); err != nil {
log.Error(err)
} else if container.Stale && params.LifecycleHooks {
executePostUpdateCommand(client, newContainerID)
}
}
if params.Cleanup {
if err := client.RemoveImage(container); err != nil {
log.Error(err)
lifecycle.ExecutePostUpdateCommand(client, newContainerID)
}
}
}
@ -126,82 +141,3 @@ func checkDependencies(containers []container.Container) {
}
}
}
func executePreCheck(client container.Client, params UpdateParams) {
containers, err := client.ListContainers(params.Filter)
if err != nil {
return
}
for _, container := range containers {
executePreCheckCommand(client, container)
}
}
func executePostCheck(client container.Client, params UpdateParams) {
containers, err := client.ListContainers(params.Filter)
if err != nil {
return
}
for _, container := range containers {
executePostCheckCommand(client, container)
}
}
func executePreCheckCommand(client container.Client, container container.Container) {
command := container.GetLifecyclePreCheckCommand()
if len(command) == 0 {
log.Debug("No pre-check command supplied. Skipping")
return
}
log.Info("Executing pre-check command.")
if err := client.ExecuteCommand(container.ID(), command); err != nil {
log.Error(err)
}
}
func executePostCheckCommand(client container.Client, container container.Container) {
command := container.GetLifecyclePostCheckCommand()
if len(command) == 0 {
log.Debug("No post-check command supplied. Skipping")
return
}
log.Info("Executing post-check command.")
if err := client.ExecuteCommand(container.ID(), command); err != nil {
log.Error(err)
}
}
func executePreUpdateCommand(client container.Client, container container.Container) {
command := container.GetLifecyclePreUpdateCommand()
if len(command) == 0 {
log.Debug("No pre-update command supplied. Skipping")
return
}
log.Info("Executing pre-update command.")
if err := client.ExecuteCommand(container.ID(), command); err != nil {
log.Error(err)
}
}
func executePostUpdateCommand(client container.Client, newContainerID string) {
newContainer, err := client.GetContainer(newContainerID)
if err != nil {
log.Error(err)
return
}
command := newContainer.GetLifecyclePostUpdateCommand()
if len(command) == 0 {
log.Debug("No post-update command supplied. Skipping")
return
}
log.Info("Executing post-update command.")
if err := client.ExecuteCommand(newContainerID, command); err != nil {
log.Error(err)
}
}

@ -0,0 +1,85 @@
package actions_test
import (
"github.com/containrrr/watchtower/internal/actions"
"github.com/containrrr/watchtower/pkg/container"
"github.com/containrrr/watchtower/pkg/container/mocks"
"github.com/containrrr/watchtower/pkg/types"
cli "github.com/docker/docker/client"
"time"
. "github.com/containrrr/watchtower/internal/actions/mocks"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)
var _ = Describe("the update action", func() {
var dockerClient cli.CommonAPIClient
var client MockClient
BeforeEach(func() {
server := mocks.NewMockAPIServer()
dockerClient, _ = cli.NewClientWithOpts(
cli.WithHost(server.URL),
cli.WithHTTPClient(server.Client()))
})
When("watchtower has been instructed to clean up", func() {
BeforeEach(func() {
pullImages := false
removeVolumes := false
client = CreateMockClient(
&TestData{
NameOfContainerToKeep: "test-container-02",
Containers: []container.Container{
CreateMockContainer(
"test-container-01",
"test-container-01",
"fake-image:latest",
time.Now().AddDate(0, 0, -1)),
CreateMockContainer(
"test-container-02",
"test-container-02",
"fake-image:latest",
time.Now()),
CreateMockContainer(
"test-container-02",
"test-container-02",
"fake-image:latest",
time.Now()),
},
},
dockerClient,
pullImages,
removeVolumes,
)
})
When("there are multiple containers using the same image", func() {
It("should only try to remove the image once", func() {
err := actions.Update(client, types.UpdateParams{ Cleanup: true })
Expect(err).NotTo(HaveOccurred())
Expect(client.TestData.TriedToRemoveImageCount).To(Equal(1))
})
})
When("there are multiple containers using different images", func() {
It("should try to remove each of them", func() {
client.TestData.Containers = append(
client.TestData.Containers,
CreateMockContainer(
"unique-test-container",
"unique-test-container",
"unique-fake-image:latest",
time.Now(),
),
)
err := actions.Update(client, types.UpdateParams{ Cleanup: true })
Expect(err).NotTo(HaveOccurred())
Expect(client.TestData.TriedToRemoveImageCount).To(Equal(2))
})
})
})
})

@ -14,7 +14,7 @@ nav:
- 'Arguments': 'arguments.md'
- 'Notifications': 'notifications.md'
- 'Container selection': 'container-selection.md'
- 'Credential helpers': 'credential-helpers.md'
- 'Private registries': 'private-registries.md'
- 'Linked containers': 'linked-containers.md'
- 'Remote hosts': 'remote-hosts.md'
- 'Secure connections': 'secure-connections.md'

@ -3,6 +3,7 @@ package container
import (
"bytes"
"fmt"
"github.com/containrrr/watchtower/pkg/registry"
"io/ioutil"
"strings"
"time"
@ -12,7 +13,7 @@ import (
"github.com/docker/docker/api/types/container"
"github.com/docker/docker/api/types/filters"
"github.com/docker/docker/api/types/network"
dockerclient "github.com/docker/docker/client"
sdkClient "github.com/docker/docker/client"
log "github.com/sirupsen/logrus"
"golang.org/x/net/context"
)
@ -29,7 +30,8 @@ type Client interface {
RenameContainer(Container, string) error
IsContainerStale(Container) (bool, error)
ExecuteCommand(containerID string, command string) error
RemoveImage(Container) error
RemoveImageByID(string) error
}
// NewClient returns a new Client instance which can be used to interact with
@ -39,7 +41,7 @@ type Client interface {
// * DOCKER_TLS_VERIFY whether to verify tls certificates
// * DOCKER_API_VERSION the minimum docker api version to work with
func NewClient(pullImages bool, includeStopped bool, reviveStopped bool, removeVolumes bool) Client {
cli, err := dockerclient.NewClientWithOpts(dockerclient.FromEnv)
cli, err := sdkClient.NewClientWithOpts(sdkClient.FromEnv)
if err != nil {
log.Fatalf("Error instantiating Docker client: %s", err)
@ -55,7 +57,7 @@ func NewClient(pullImages bool, includeStopped bool, reviveStopped bool, removeV
}
type dockerClient struct {
api dockerclient.CommonAPIClient
api sdkClient.CommonAPIClient
pullImages bool
removeVolumes bool
includeStopped bool
@ -156,7 +158,7 @@ func (client dockerClient) StopContainer(c Container, timeout time.Duration) err
// Wait for container to be removed. In this case an error is a good thing
if err := client.waitForStopOrTimeout(c, timeout); err == nil {
return fmt.Errorf("Container %s (%s) could not be removed", c.Name(), c.ID())
return fmt.Errorf("container %s (%s) could not be removed", c.Name(), c.ID())
}
return nil
@ -230,59 +232,72 @@ func (client dockerClient) RenameContainer(c Container, newName string) error {
return client.api.ContainerRename(bg, c.ID(), newName)
}
func (client dockerClient) IsContainerStale(c Container) (bool, error) {
bg := context.Background()
oldImageInfo := c.imageInfo
imageName := c.ImageName()
func (client dockerClient) IsContainerStale(container Container) (bool, error) {
ctx := context.Background()
if client.pullImages {
log.Debugf("Pulling %s for %s", imageName, c.Name())
if !client.pullImages {
log.Debugf("Skipping image pull.")
} else if err := client.PullImage(ctx, container); err != nil {
return false, err
}
var opts types.ImagePullOptions // ImagePullOptions can take a RegistryAuth arg to authenticate against a private registry
auth, err := EncodedAuth(imageName)
log.Debugf("Got auth value: %s", auth)
log.Debugf("Got image name: %s", imageName)
if err != nil {
log.Debugf("Error loading authentication credentials %s", err)
return false, err
} else if auth == "" {
log.Debugf("No authentication credentials found for %s", imageName)
opts = types.ImagePullOptions{} // empty/no auth credentials
} else {
opts = types.ImagePullOptions{RegistryAuth: auth, PrivilegeFunc: DefaultAuthHandler}
}
return client.HasNewImage(ctx, container)
}
response, err := client.api.ImagePull(bg, imageName, opts)
if err != nil {
log.Debugf("Error pulling image %s, %s", imageName, err)
return false, err
}
defer response.Close()
func (client dockerClient) HasNewImage(ctx context.Context, container Container) (bool, error) {
oldImageID := container.imageInfo.ID
imageName := container.ImageName()
// the pull request will be aborted prematurely unless the response is read
if _, err = ioutil.ReadAll(response); err != nil {
log.Error(err)
}
newImageInfo, _, err := client.api.ImageInspectWithRaw(ctx, imageName)
if err != nil {
return false, err
}
newImageInfo, _, err := client.api.ImageInspectWithRaw(bg, imageName)
if newImageInfo.ID == oldImageID {
log.Debugf("No new images found for %s", container.Name())
return false, nil
}
log.Infof("Found new %s image (%s)", imageName, newImageInfo.ID)
return true, nil
}
func (client dockerClient) PullImage(ctx context.Context, container Container) error {
containerName := container.Name()
imageName := container.ImageName()
log.Debugf("Pulling %s for %s", imageName, containerName)
opts, err := registry.GetPullOptions(imageName)
if err != nil {
return false, err
log.Debugf("Error loading authentication credentials %s", err)
return err
}
if newImageInfo.ID != oldImageInfo.ID {
log.Infof("Found new %s image (%s)", imageName, newImageInfo.ID)
return true, nil
response, err := client.api.ImagePull(ctx, imageName, opts)
if err != nil {
log.Debugf("Error pulling image %s, %s", imageName, err)
return err
}
log.Debugf("No new images found for %s", c.Name())
return false, nil
defer response.Close()
// the pull request will be aborted prematurely unless the response is read
if _, err = ioutil.ReadAll(response); err != nil {
log.Error(err)
return err
}
return nil
}
func (client dockerClient) RemoveImage(c Container) error {
imageID := c.ImageID()
log.Infof("Removing image %s", imageID)
_, err := client.api.ImageRemove(context.Background(), imageID, types.ImageRemoveOptions{Force: true})
func (client dockerClient) RemoveImageByID(id string) error {
log.Infof("Removing image %s", id)
_, err := client.api.ImageRemove(
context.Background(),
id,
types.ImageRemoveOptions{
Force: true,
})
return err
}

@ -28,6 +28,11 @@ type Container struct {
imageInfo *types.ImageInspect
}
// ContainerInfo fetches JSON info for the container
func (c Container) ContainerInfo() *types.ContainerJSON {
return c.containerInfo
}
// ID returns the Docker container ID.
func (c Container) ID() string {
return c.containerInfo.ID
@ -133,6 +138,7 @@ func (c Container) StopSignal() string {
// the options overridden at runtime.
func (c Container) runtimeConfig() *dockercontainer.Config {
config := c.containerInfo.Config
hostConfig := c.containerInfo.HostConfig
imageConfig := c.imageInfo.Config
if config.WorkingDir == imageConfig.WorkingDir {
@ -143,6 +149,10 @@ func (c Container) runtimeConfig() *dockercontainer.Config {
config.User = ""
}
if hostConfig.NetworkMode.IsContainer() {
config.Hostname = ""
}
if util.SliceEqual(config.Entrypoint, imageConfig.Entrypoint) {
config.Entrypoint = nil
if util.SliceEqual(config.Cmd, imageConfig.Cmd) {

@ -2,6 +2,7 @@ package container
import (
"github.com/containrrr/watchtower/pkg/container/mocks"
"github.com/containrrr/watchtower/pkg/filters"
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/container"
cli "github.com/docker/docker/client"
@ -34,14 +35,14 @@ var _ = Describe("the container", func() {
})
When("listing containers without any filter", func() {
It("should return all available containers", func() {
containers, err := client.ListContainers(noFilter)
containers, err := client.ListContainers(filters.NoFilter)
Expect(err).NotTo(HaveOccurred())
Expect(len(containers) == 2).To(BeTrue())
})
})
When("listing containers with a filter matching nothing", func() {
It("should return an empty array", func() {
filter := filterByNames([]string{"lollercoaster"}, noFilter)
filter := filters.FilterByNames([]string{"lollercoaster"}, filters.NoFilter)
containers, err := client.ListContainers(filter)
Expect(err).NotTo(HaveOccurred())
Expect(len(containers) == 0).To(BeTrue())
@ -49,7 +50,7 @@ var _ = Describe("the container", func() {
})
When("listing containers with a watchtower filter", func() {
It("should return only the watchtower container", func() {
containers, err := client.ListContainers(WatchtowerContainersFilter)
containers, err := client.ListContainers(filters.WatchtowerContainersFilter)
Expect(err).NotTo(HaveOccurred())
Expect(len(containers) == 1).To(BeTrue())
Expect(containers[0].ImageName()).To(Equal("containrrr/watchtower:latest"))
@ -62,7 +63,7 @@ var _ = Describe("the container", func() {
pullImages: false,
includeStopped: true,
}
containers, err := client.ListContainers(noFilter)
containers, err := client.ListContainers(filters.NoFilter)
Expect(err).NotTo(HaveOccurred())
Expect(len(containers) > 0).To(BeTrue())
})

@ -1,15 +1,15 @@
package container
package filters
import t "github.com/containrrr/watchtower/pkg/types"
// WatchtowerContainersFilter filters only watchtower containers
func WatchtowerContainersFilter(c t.FilterableContainer) bool { return c.IsWatchtower() }
// Filter no containers and returns all
func noFilter(t.FilterableContainer) bool { return true }
// NoFilter will not filter out any containers
func NoFilter(t.FilterableContainer) bool { return true }
// Filters containers which don't have a specified name
func filterByNames(names []string, baseFilter t.Filter) t.Filter {
// FilterByNames returns all containers that match the specified name
func FilterByNames(names []string, baseFilter t.Filter) t.Filter {
if len(names) == 0 {
return baseFilter
}
@ -24,8 +24,8 @@ func filterByNames(names []string, baseFilter t.Filter) t.Filter {
}
}
// Filters out containers that don't have the 'enableLabel'
func filterByEnableLabel(baseFilter t.Filter) t.Filter {
// FilterByEnableLabel returns all containers that have the enabled label set
func FilterByEnableLabel(baseFilter t.Filter) t.Filter {
return func(c t.FilterableContainer) bool {
// If label filtering is enabled, containers should only be considered
// if the label is specifically set.
@ -38,8 +38,8 @@ func filterByEnableLabel(baseFilter t.Filter) t.Filter {
}
}
// Filters out containers that have a 'enableLabel' and is set to disable.
func filterByDisabledLabel(baseFilter t.Filter) t.Filter {
// FilterByDisabledLabel returns all containers that have the enabled label set to disable
func FilterByDisabledLabel(baseFilter t.Filter) t.Filter {
return func(c t.FilterableContainer) bool {
enabledLabel, ok := c.Enabled()
if ok && !enabledLabel {
@ -53,13 +53,13 @@ func filterByDisabledLabel(baseFilter t.Filter) t.Filter {
// BuildFilter creates the needed filter of containers
func BuildFilter(names []string, enableLabel bool) t.Filter {
filter := noFilter
filter = filterByNames(names, filter)
filter := NoFilter
filter = FilterByNames(names, filter)
if enableLabel {
// If label filtering is enabled, containers should only be considered
// if the label is specifically set.
filter = filterByEnableLabel(filter)
filter = FilterByEnableLabel(filter)
}
filter = filterByDisabledLabel(filter)
filter = FilterByDisabledLabel(filter)
return filter
}

@ -1,4 +1,4 @@
package container
package filters
import (
"testing"
@ -20,7 +20,7 @@ func TestWatchtowerContainersFilter(t *testing.T) {
func TestNoFilter(t *testing.T) {
container := new(mocks.FilterableContainer)
assert.True(t, noFilter(container))
assert.True(t, NoFilter(container))
container.AssertExpectations(t)
}
@ -28,12 +28,12 @@ func TestNoFilter(t *testing.T) {
func TestFilterByNames(t *testing.T) {
var names []string
filter := filterByNames(names, nil)
filter := FilterByNames(names, nil)
assert.Nil(t, filter)
names = append(names, "test")
filter = filterByNames(names, noFilter)
filter = FilterByNames(names, NoFilter)
assert.NotNil(t, filter)
container := new(mocks.FilterableContainer)
@ -48,7 +48,7 @@ func TestFilterByNames(t *testing.T) {
}
func TestFilterByEnableLabel(t *testing.T) {
filter := filterByEnableLabel(noFilter)
filter := FilterByEnableLabel(NoFilter)
assert.NotNil(t, filter)
container := new(mocks.FilterableContainer)
@ -68,7 +68,7 @@ func TestFilterByEnableLabel(t *testing.T) {
}
func TestFilterByDisabledLabel(t *testing.T) {
filter := filterByDisabledLabel(noFilter)
filter := FilterByDisabledLabel(NoFilter)
assert.NotNil(t, filter)
container := new(mocks.FilterableContainer)

@ -0,0 +1,93 @@
package lifecycle
import (
"github.com/containrrr/watchtower/pkg/container"
"github.com/containrrr/watchtower/pkg/types"
log "github.com/sirupsen/logrus"
)
// ExecutePreChecks tries to run the pre-check lifecycle hook for all containers included by the current filter.
func ExecutePreChecks(client container.Client, params types.UpdateParams) {
containers, err := client.ListContainers(params.Filter)
if err != nil {
return
}
for _, container := range containers {
ExecutePreCheckCommand(client, container)
}
}
// ExecutePostChecks tries to run the post-check lifecycle hook for all containers included by the current filter.
func ExecutePostChecks(client container.Client, params types.UpdateParams) {
containers, err := client.ListContainers(params.Filter)
if err != nil {
return
}
for _, container := range containers {
ExecutePostCheckCommand(client, container)
}
}
// ExecutePreCheckCommand tries to run the pre-check lifecycle hook for a single container.
func ExecutePreCheckCommand(client container.Client, container container.Container) {
command := container.GetLifecyclePreCheckCommand()
if len(command) == 0 {
log.Debug("No pre-check command supplied. Skipping")
return
}
log.Info("Executing pre-check command.")
if err := client.ExecuteCommand(container.ID(), command); err != nil {
log.Error(err)
}
}
// ExecutePostCheckCommand tries to run the post-check lifecycle hook for a single container.
func ExecutePostCheckCommand(client container.Client, container container.Container) {
command := container.GetLifecyclePostCheckCommand()
if len(command) == 0 {
log.Debug("No post-check command supplied. Skipping")
return
}
log.Info("Executing post-check command.")
if err := client.ExecuteCommand(container.ID(), command); err != nil {
log.Error(err)
}
}
// ExecutePreUpdateCommand tries to run the pre-update lifecycle hook for a single container.
func ExecutePreUpdateCommand(client container.Client, container container.Container) {
command := container.GetLifecyclePreUpdateCommand()
if len(command) == 0 {
log.Debug("No pre-update command supplied. Skipping")
return
}
log.Info("Executing pre-update command.")
if err := client.ExecuteCommand(container.ID(), command); err != nil {
log.Error(err)
}
}
// ExecutePostUpdateCommand tries to run the post-update lifecycle hook for a single container.
func ExecutePostUpdateCommand(client container.Client, newContainerID string) {
newContainer, err := client.GetContainer(newContainerID)
if err != nil {
log.Error(err)
return
}
command := newContainer.GetLifecyclePostUpdateCommand()
if len(command) == 0 {
log.Debug("No post-update command supplied. Skipping")
return
}
log.Info("Executing post-update command.")
if err := client.ExecuteCommand(newContainerID, command); err != nil {
log.Error(err)
}
}

@ -6,6 +6,7 @@ import (
"github.com/spf13/cobra"
"net/smtp"
"os"
"strings"
"time"
t "github.com/containrrr/watchtower/pkg/types"
@ -113,7 +114,7 @@ func (e *emailTypeNotifier) sendEntries(entries []*log.Entry) {
if e.User != "" {
auth = smtp.PlainAuth("", e.User, e.Password, e.Server)
}
err := SendMail(e.Server+":"+strconv.Itoa(e.Port), e.tlsSkipVerify, auth, e.From, []string{e.To}, msg)
err := SendMail(e.Server+":"+strconv.Itoa(e.Port), e.tlsSkipVerify, auth, e.From, strings.Split(e.To, ","), msg)
if err != nil {
// Use fmt so it doesn't trigger another email.
fmt.Println("Failed to send notification email: ", err)

@ -0,0 +1,33 @@
package registry
import (
"github.com/docker/docker/api/types"
log "github.com/sirupsen/logrus"
)
// GetPullOptions creates a struct with all options needed for pulling images from a registry
func GetPullOptions(imageName string) (types.ImagePullOptions, error) {
auth, err := EncodedAuth(imageName)
log.Debugf("Got image name: %s", imageName)
if err != nil {
return types.ImagePullOptions{}, err
}
log.Debugf("Got auth value: %s", auth)
if auth == "" {
return types.ImagePullOptions{}, nil
}
return types.ImagePullOptions{
RegistryAuth: auth,
PrivilegeFunc: DefaultAuthHandler,
}, nil
}
// DefaultAuthHandler will be invoked if an AuthConfig is rejected
// It could be used to return a new value for the "X-Registry-Auth" authentication header,
// but there's no point trying again with the same value as used in AuthConfig
func DefaultAuthHandler() (string, error) {
log.Debug("Authentication request was rejected. Trying again without authentication")
return "", nil
}

@ -1,4 +1,4 @@
package container
package registry
import (
"errors"
@ -97,11 +97,3 @@ func CredentialsStore(configFile configfile.ConfigFile) credentials.Store {
func EncodeAuth(auth types.AuthConfig) (string, error) {
return command.EncodeAuthToBase64(auth)
}
// DefaultAuthHandler will be invoked if an AuthConfig is rejected
// It could be used to return a new value for the "X-Registry-Auth" authentication header,
// but there's no point trying again with the same value as used in AuthConfig
func DefaultAuthHandler() (string, error) {
log.Debug("Authentication request was rejected. Trying again without authentication")
return "", nil
}

@ -1,4 +1,4 @@
package container
package registry
import (
"github.com/stretchr/testify/assert"

@ -1,13 +1,14 @@
package container
package sorter
import (
"fmt"
"github.com/containrrr/watchtower/pkg/container"
"time"
)
// ByCreated allows a list of Container structs to be sorted by the container's
// created date.
type ByCreated []Container
type ByCreated []container.Container
func (c ByCreated) Len() int { return len(c) }
func (c ByCreated) Swap(i, j int) { c[i], c[j] = c[j], c[i] }
@ -15,12 +16,12 @@ func (c ByCreated) Swap(i, j int) { c[i], c[j] = c[j], c[i] }
// Less will compare two elements (identified by index) in the Container
// list by created-date.
func (c ByCreated) Less(i, j int) bool {
t1, err := time.Parse(time.RFC3339Nano, c[i].containerInfo.Created)
t1, err := time.Parse(time.RFC3339Nano, c[i].ContainerInfo().Created)
if err != nil {
t1 = time.Now()
}
t2, _ := time.Parse(time.RFC3339Nano, c[j].containerInfo.Created)
t2, _ := time.Parse(time.RFC3339Nano, c[j].ContainerInfo().Created)
if err != nil {
t1 = time.Now()
}
@ -33,18 +34,18 @@ func (c ByCreated) Less(i, j int) bool {
// the front of the list while containers with links will be sorted after all
// of their dependencies. This sort order ensures that linked containers can
// be started in the correct order.
func SortByDependencies(containers []Container) ([]Container, error) {
func SortByDependencies(containers []container.Container) ([]container.Container, error) {
sorter := dependencySorter{}
return sorter.Sort(containers)
}
type dependencySorter struct {
unvisited []Container
unvisited []container.Container
marked map[string]bool
sorted []Container
sorted []container.Container
}
func (ds *dependencySorter) Sort(containers []Container) ([]Container, error) {
func (ds *dependencySorter) Sort(containers []container.Container) ([]container.Container, error) {
ds.unvisited = containers
ds.marked = map[string]bool{}
@ -57,10 +58,10 @@ func (ds *dependencySorter) Sort(containers []Container) ([]Container, error) {
return ds.sorted, nil
}
func (ds *dependencySorter) visit(c Container) error {
func (ds *dependencySorter) visit(c container.Container) error {
if _, ok := ds.marked[c.Name()]; ok {
return fmt.Errorf("Circular reference to %s", c.Name())
return fmt.Errorf("circular reference to %s", c.Name())
}
// Mark any visited node so that circular references can be detected
@ -83,7 +84,7 @@ func (ds *dependencySorter) visit(c Container) error {
return nil
}
func (ds *dependencySorter) findUnvisited(name string) *Container {
func (ds *dependencySorter) findUnvisited(name string) *container.Container {
for _, c := range ds.unvisited {
if c.Name() == name {
return &c
@ -93,7 +94,7 @@ func (ds *dependencySorter) findUnvisited(name string) *Container {
return nil
}
func (ds *dependencySorter) removeUnvisited(c Container) {
func (ds *dependencySorter) removeUnvisited(c container.Container) {
var idx int
for i := range ds.unvisited {
if ds.unvisited[i].Name() == c.Name() {

@ -1,13 +1,12 @@
package actions
package types
import (
t "github.com/containrrr/watchtower/pkg/types"
"time"
)
// UpdateParams contains all different options available to alter the behavior of the Update func
type UpdateParams struct {
Filter t.Filter
Filter Filter
Cleanup bool
NoRestart bool
Timeout time.Duration
Loading…
Cancel
Save