diff --git a/.all-contributorsrc b/.all-contributorsrc index f0f4374..043cacc 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -309,7 +309,8 @@ "avatar_url": "https://avatars0.githubusercontent.com/u/21138205?v=4", "profile": "https://github.com/zoispag", "contributions": [ - "code" + "code", + "review" ] }, { @@ -375,6 +376,33 @@ "contributions": [ "code" ] + }, + { + "login": "8ear", + "name": "Max H.", + "avatar_url": "https://avatars0.githubusercontent.com/u/10329648?v=4", + "profile": "https://github.com/8ear", + "contributions": [ + "code" + ] + }, + { + "login": "pjknkda", + "name": "Jungkook Park", + "avatar_url": "https://avatars0.githubusercontent.com/u/4986524?v=4", + "profile": "https://pjknkda.github.io", + "contributions": [ + "doc" + ] + }, + { + "login": "jnidzwetzki", + "name": "Jan Kristof Nidzwetzki", + "avatar_url": "https://avatars1.githubusercontent.com/u/5753622?v=4", + "profile": "https://achfrag.net", + "contributions": [ + "doc" + ] } ], "contributorsPerLine": 7, @@ -382,5 +410,6 @@ "projectOwner": "containrrr", "repoType": "github", "repoHost": "https://github.com", - "commitConvention": "none" + "commitConvention": "none", + "skipCi": true } diff --git a/.github/config.yml b/.github/config.yml deleted file mode 100644 index 62993b5..0000000 --- a/.github/config.yml +++ /dev/null @@ -1,11 +0,0 @@ -newIssueWelcomeComment: > - Hi there! - - Thanks a bunch for opening your first issue! :pray: - As you're new to this repo, we'd like to suggest that you read our [code of conduct](https://github.com/containrrr/watchtower/blob/master/CODE_OF_CONDUCT.md) - -newPRWelcomeComment: > - Thanks for opening this pull request! Please check out our [contributing guidelines](https://github.com/containrrr/watchtower/blob/master/CONTRIBUTING.md) as well as our [code of conduct](https://github.com/containrrr/watchtower/blob/master/CODE_OF_CONDUCT.md). - -firstPRMergeComment: > - Congrats on merging your first pull request! We are all very proud of you! :sparkles: diff --git a/.github/workflows/greetings.yml b/.github/workflows/greetings.yml new file mode 100644 index 0000000..7034c9e --- /dev/null +++ b/.github/workflows/greetings.yml @@ -0,0 +1,18 @@ +name: Greetings + +on: [pull_request, issues] + +jobs: + greeting: + runs-on: ubuntu-latest + steps: + - uses: actions/first-interaction@v1 + with: + repo-token: ${{ secrets.GITHUB_TOKEN }} + issue-message: > + Hi there! πŸ‘‹πŸΌ + As you're new to this repo, we'd like to suggest that you read our [code of conduct](https://github.com/containrrr/watchtower/blob/master/CODE_OF_CONDUCT.md) + as well as our [contribution guidelines](https://github.com/containrrr/watchtower/blob/master/CONTRIBUTING.md). + Thanks a bunch for opening your first issue! πŸ™ + pr-message: > + Congratulations on opening your first pull request! We'll get back to you as soon as possible. In the meantime, please make sure you've updated the documentation to reflect your changes and have added test automation as needed. Thanks! πŸ™πŸΌ diff --git a/README.md b/README.md index 3fbe043..37a06a8 100644 --- a/README.md +++ b/README.md @@ -62,62 +62,70 @@ 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)): - + + - - - - - - - + + + + + + + - - - - - - - + + + + + + + - - - - - - - + + + + + + + - - - - - - - + + + + + + + - - - - - - - + + + + + + + - - - - - + + + + + + + + + +
James
James

⚠️ πŸ€”
Florian
Florian

πŸ‘€ πŸ“–
Brian DeHamer
Brian DeHamer

πŸ’» 🚧
Ross Cadogan
Ross Cadogan

πŸ’»
stffabi
stffabi

πŸ’» 🚧
Austin
Austin

πŸ“–
David Gardner
David Gardner

πŸ‘€ πŸ“–

James

⚠️ πŸ€”

Florian

πŸ‘€ πŸ“–

Brian DeHamer

πŸ’» 🚧

Ross Cadogan

πŸ’»

stffabi

πŸ’» 🚧

Austin

πŸ“–

David Gardner

πŸ‘€ πŸ“–
Tanguy ⧓ Herrmann
Tanguy ⧓ Herrmann

πŸ’»
Rodrigo Damazio Bovendorp
Rodrigo Damazio Bovendorp

πŸ’» πŸ“–
Ryan Kuba
Ryan Kuba

πŸš‡
cnrmck
cnrmck

πŸ“–
Harry Walter
Harry Walter

πŸ’»
Robotex
Robotex

πŸ“–
Gerald Pape
Gerald Pape

πŸ“–

Tanguy ⧓ Herrmann

πŸ’»

Rodrigo Damazio Bovendorp

πŸ’» πŸ“–

Ryan Kuba

πŸš‡

cnrmck

πŸ“–

Harry Walter

πŸ’»

Robotex

πŸ“–

Gerald Pape

πŸ“–
fomk
fomk

πŸ’»
Sven Gottwald
Sven Gottwald

πŸš‡
techknowlogick
techknowlogick

πŸ’»
waja
waja

πŸ“–
Scott Albertson
Scott Albertson

πŸ“–
Jason Huddleston
Jason Huddleston

πŸ“–
Napster
Napster

πŸ’»

fomk

πŸ’»

Sven Gottwald

πŸš‡

techknowlogick

πŸ’»

waja

πŸ“–

Scott Albertson

πŸ“–

Jason Huddleston

πŸ“–

Napster

πŸ’»
Maxim
Maxim

πŸ’» πŸ“–
Max Schmitt
Max Schmitt

πŸ“–
cron410
cron410

πŸ“–
Paulo Henrique
Paulo Henrique

πŸ“–
Kaleb Elwert
Kaleb Elwert

πŸ“–
Bill Butler
Bill Butler

πŸ“–
Mario Tacke
Mario Tacke

πŸ’»

Maxim

πŸ’» πŸ“–

Max Schmitt

πŸ“–

cron410

πŸ“–

Paulo Henrique

πŸ“–

Kaleb Elwert

πŸ“–

Bill Butler

πŸ“–

Mario Tacke

πŸ’»
Mark Woodbridge
Mark Woodbridge

πŸ’»
Simon Aronsson
Simon Aronsson

πŸ’» 🚧 πŸ‘€
Ansem93
Ansem93

πŸ“–
Luka Peschke
Luka Peschke

πŸ’» πŸ“–
Zois Pagoulatos
Zois Pagoulatos

πŸ’»
Alexandre Menif
Alexandre Menif

πŸ’»
Andrey
Andrey

πŸ“–

Mark Woodbridge

πŸ’»

Simon Aronsson

πŸ’» 🚧 πŸ‘€

Ansem93

πŸ“–

Luka Peschke

πŸ’» πŸ“–

Zois Pagoulatos

πŸ’» πŸ‘€

Alexandre Menif

πŸ’»

Andrey

πŸ“–
Armando LΓΌscher
Armando LΓΌscher

πŸ“–
Ryan Budke
Ryan Budke

πŸ“–
Kaloyan Raev
Kaloyan Raev

πŸ’» ⚠️
sixth
sixth

πŸ“–
Gina HÀußge
Gina HÀußge

πŸ’»

Armando LΓΌscher

πŸ“–

Ryan Budke

πŸ“–

Kaloyan Raev

πŸ’» ⚠️

sixth

πŸ“–

Gina HÀußge

πŸ’»

Max H.

πŸ’»

Jungkook Park

πŸ“–

Jan Kristof Nidzwetzki

πŸ“–
+ + This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome! diff --git a/cmd/root.go b/cmd/root.go index ea00786..453196a 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -93,11 +93,13 @@ func PreRun(cmd *cobra.Command, args []string) { noPull, _ := f.GetBool("no-pull") includeStopped, _ := f.GetBool("include-stopped") + reviveStopped, _ := f.GetBool("revive-stopped") removeVolumes, _ := f.GetBool("remove-volumes") client = container.NewClient( !noPull, includeStopped, + reviveStopped, removeVolumes, ) diff --git a/docs/arguments.md b/docs/arguments.md index db51c95..938dffa 100644 --- a/docs/arguments.md +++ b/docs/arguments.md @@ -62,7 +62,7 @@ Enable debug mode with verbose logging. ``` Argument: --debug -Environment Variable: N/A +Environment Variable: WATCHTOWER_DEBUG Type: Boolean Default: false ``` @@ -97,6 +97,16 @@ Environment Variable: WATCHTOWER_INCLUDE_STOPPED Default: false ``` +## Revive stopped +Start any stopped containers that have had their image updated. This argument is only usable with the `--include-stopped` argument. + +``` + Argument: --revive-stopped +Environment Variable: WATCHTOWER_REVIVE_STOPPED + Type: Boolean + Default: false +``` + ## Poll interval Poll interval (in seconds). This value controls how frequently watchtower will poll for new images. @@ -127,6 +137,16 @@ Environment Variable: WATCHTOWER_MONITOR_ONLY Default: false ``` +## Without restarting containers +Do not restart containers after updating. This option can be useful when the start of the containers +is managed by an external system such as systemd. +``` + Argument: --no-restart +Environment Variable: WATCHTOWER_NO_RESTART + Type: Boolean + Default: false +``` + ## Without pulling new images Do not pull new images. When this flag is specified, watchtower will not attempt to pull new images from the registry. Instead it will only monitor the local image cache for changes. diff --git a/docs/credential-helpers.md b/docs/credential-helpers.md index 1722906..c86ef7c 100644 --- a/docs/credential-helpers.md +++ b/docs/credential-helpers.md @@ -45,20 +45,22 @@ volumes: helper: {} ``` -and for `.docker/config.yml`: -```yaml +and for `/.docker/config.json`: +```json { "HttpHeaders" : { "User-Agent" : "Docker-Client/19.03.1 (XXXXXX)" }, - "credsStore" : "osxkeychain", // ...or your prefered helper + "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" // ...or your prefered helper + "index.docker.io": "osxkeychain" } } -``` \ No newline at end of file +``` + +*Note:* `osxkeychain` can be changed to your prefered credentials helper. diff --git a/docs/notifications.md b/docs/notifications.md index 5741566..b95e95e 100644 --- a/docs/notifications.md +++ b/docs/notifications.md @@ -2,13 +2,19 @@ # Notifications Watchtower can send notifications when containers are updated. Notifications are sent via hooks in the logging system, [logrus](http://github.com/sirupsen/logrus). -The types of notifications to send are passed via the comma-separated option `--notifications` (or corresponding environment variable `WATCHTOWER_NOTIFICATIONS`), which has the following valid values: +The types of notifications to send are set by passing a comma-separated list of values to the `--notifications` option (or corresponding environment variable `WATCHTOWER_NOTIFICATIONS`), which has the following valid values: - `email` to send notifications via e-mail - `slack` to send notifications through a Slack webhook - `msteams` to send notifications via MSTeams webhook - `gotify` to send notifications via Gotify +> There is currently a [bug](https://github.com/spf13/viper/issues/380) in Viper, which prevents comma-separated slices to be used when using the environment variable. A workaround is available where we instead put quotes around the environment variable value and replace the commas with spaces, as `WATCHTOWER_NOTIFICATIONS="slack msteams"` + +> If you're a `docker-compose` user, make sure to specify environment variables' values in your `.yml` file without double quotes (`"`). +> +> This prevents unexpected errors when watchtower starts. + ## Settings - `--notifications-level` (env. `WATCHTOWER_NOTIFICATIONS_LEVEL`): Controls the log level which is used for the notifications. If omitted, the default log level is `info`. Possible values are: `panic`, `fatal`, `error`, `warn`, `info` or `debug`. @@ -27,6 +33,7 @@ To receive notifications by email, the following command-line options, or their - `--notification-email-server-user` (env. `WATCHTOWER_NOTIFICATION_EMAIL_SERVER_USER`): The username to authenticate with the SMTP server with. - `--notification-email-server-password` (env. `WATCHTOWER_NOTIFICATION_EMAIL_SERVER_PASSWORD`): The password to authenticate with the SMTP server with. - `--notification-email-delay` (env. `WATCHTOWER_NOTIFICATION_EMAIL_DELAY`): Delay before sending notifications expressed in seconds. +- `--notification-email-subjecttag` (env. `WATCHTOWER_NOTIFICATION_EMAIL_SUBJECTTAG`): Prefix to include in the subject tag. Useful when running multiple watchtowers. Example: diff --git a/go.mod b/go.mod index 5b6e517..a45b228 100644 --- a/go.mod +++ b/go.mod @@ -56,5 +56,8 @@ require ( 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 + 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 gotest.tools v2.2.0+incompatible // indirect ) diff --git a/internal/flags/flags.go b/internal/flags/flags.go index 6e9ea55..7ab09bd 100644 --- a/internal/flags/flags.go +++ b/internal/flags/flags.go @@ -94,6 +94,12 @@ func RegisterSystemFlags(rootCmd *cobra.Command) { viper.GetBool("WATCHTOWER_INCLUDE_STOPPED"), "Will also include created and exited containers") + flags.BoolP( + "revive-stopped", + "", + viper.GetBool("WATCHTOWER_REVIVE_STOPPED"), + "Will also start stopped containers that were updated, if include-stopped is active") + flags.BoolP( "enable-lifecycle-hooks", "", @@ -128,7 +134,7 @@ func RegisterNotificationFlags(rootCmd *cobra.Command) { "", viper.GetString("WATCHTOWER_NOTIFICATION_EMAIL_TO"), "Address to send notification emails to") - + flags.IntP( "notification-email-delay", "", @@ -168,6 +174,12 @@ Should only be used for testing. viper.GetString("WATCHTOWER_NOTIFICATION_EMAIL_SERVER_PASSWORD"), "SMTP server password for sending notifications") + flags.StringP( + "notification-email-subjecttag", + "", + viper.GetString("WATCHTOWER_NOTIFICATION_EMAIL_SUBJECTTAG"), + "Subject prefix tag for notifications via mail") + flags.StringP( "notification-slack-hook-url", "", @@ -232,6 +244,7 @@ func SetDefaults() { viper.SetDefault("WATCHTOWER_NOTIFICATIONS", []string{}) viper.SetDefault("WATCHTOWER_NOTIFICATIONS_LEVEL", "info") viper.SetDefault("WATCHTOWER_NOTIFICATION_EMAIL_SERVER_PORT", 25) + viper.SetDefault("WATCHTOWER_NOTIFICATION_EMAIL_SUBJECTTAG", "") viper.SetDefault("WATCHTOWER_NOTIFICATION_SLACK_IDENTIFIER", "watchtower") } diff --git a/mkdocs.yml b/mkdocs.yml index 9656c6d..e5e7c34 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -19,5 +19,6 @@ nav: - 'Remote hosts': 'remote-hosts.md' - 'Secure connections': 'secure-connections.md' - 'Stop signals': 'stop-signals.md' + - 'Lifecycle hooks': 'lifecycle-hooks.md' plugins: - - search \ No newline at end of file + - search diff --git a/pkg/container/client.go b/pkg/container/client.go index 0dc22db..5877eb4 100644 --- a/pkg/container/client.go +++ b/pkg/container/client.go @@ -38,7 +38,7 @@ type Client interface { // * DOCKER_HOST the docker-engine host to send api requests to // * 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, removeVolumes bool) Client { +func NewClient(pullImages bool, includeStopped bool, reviveStopped bool, removeVolumes bool) Client { cli, err := dockerclient.NewClientWithOpts(dockerclient.FromEnv) if err != nil { @@ -50,6 +50,7 @@ func NewClient(pullImages bool, includeStopped bool, removeVolumes bool) Client pullImages: pullImages, removeVolumes: removeVolumes, includeStopped: includeStopped, + reviveStopped: reviveStopped, } } @@ -58,6 +59,7 @@ type dockerClient struct { pullImages bool removeVolumes bool includeStopped bool + reviveStopped bool } func (client dockerClient) ListContainers(fn t.Filter) ([]Container, error) { @@ -203,7 +205,7 @@ func (client dockerClient) StartContainer(c Container) (string, error) { } - if !c.IsRunning() { + if !c.IsRunning() && !client.reviveStopped { return createdContainer.ID, nil } diff --git a/pkg/notifications/email.go b/pkg/notifications/email.go index b5ef979..ca54499 100644 --- a/pkg/notifications/email.go +++ b/pkg/notifications/email.go @@ -23,13 +23,13 @@ const ( // - It would only send errors // We work around that by holding on to log entries until the update cycle is done. type emailTypeNotifier struct { - From, To string - Server, User, Password string - Port int - tlsSkipVerify bool - entries []*log.Entry - logLevels []log.Level - delay time.Duration + From, To string + Server, User, Password, SubjectTag string + Port int + tlsSkipVerify bool + entries []*log.Entry + logLevels []log.Level + delay time.Duration } func newEmailNotifier(c *cobra.Command, acceptedLogLevels []log.Level) t.Notifier { @@ -43,6 +43,7 @@ func newEmailNotifier(c *cobra.Command, acceptedLogLevels []log.Level) t.Notifie port, _ := flags.GetInt("notification-email-server-port") tlsSkipVerify, _ := flags.GetBool("notification-email-server-tls-skip-verify") delay, _ := flags.GetInt("notification-email-delay") + subjecttag, _ := flags.GetString("notification-email-subjecttag") n := &emailTypeNotifier{ From: from, @@ -54,6 +55,7 @@ func newEmailNotifier(c *cobra.Command, acceptedLogLevels []log.Level) t.Notifie tlsSkipVerify: tlsSkipVerify, logLevels: acceptedLogLevels, delay: time.Duration(delay) * time.Second, + SubjectTag: subjecttag, } log.AddHook(n) @@ -62,7 +64,13 @@ func newEmailNotifier(c *cobra.Command, acceptedLogLevels []log.Level) t.Notifie } func (e *emailTypeNotifier) buildMessage(entries []*log.Entry) []byte { - emailSubject := "Watchtower updates" + var emailSubject string + + if e.SubjectTag == "" { + emailSubject = "Watchtower updates" + } else { + emailSubject = e.SubjectTag + " Watchtower updates" + } if hostname, err := os.Hostname(); err == nil { emailSubject += " on " + hostname } @@ -128,7 +136,7 @@ func (e *emailTypeNotifier) SendNotification() { time.Sleep(e.delay) } - e.sendEntries(e.entries) + e.sendEntries(e.entries) e.entries = nil }