From 35f88303fa947804ef2a1404c65dfb3931e32a65 Mon Sep 17 00:00:00 2001 From: Christopher Vagnetoft Date: Sat, 19 Mar 2022 23:22:08 +0100 Subject: [PATCH] Improved hooks and scripts * Invoke hooks before and after updating * Scripts can now be run --before and --after --- README.md | 31 ++++++++++++++++++++++++++++++- src/Refresher.php | 34 +++++++++++++++++++++++----------- 2 files changed, 53 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 704143b..e3755a7 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,18 @@ # Fresh: Keeping your docker stacks up to date Fresh was written to scratch an itch. It works by querying the respective repositories -returning +for the various manifests in order to determine if the images have been changed. If so +it can do a combination of things: + +- Set the exitcode to indicate the freshness, `0` means up-to-date and `1` outdated. +- Pull the updated images with docker-compose, and optionally recreate the containers + with `docker-compose up`. +- Run a script before and after updating, f.ex. to enable maintenance mode or update + permission on volumes. +- Notify webhooks when updating. Currently only Slack and Mattermost are supported. + +Fresh is designed to be invoked using cron or systemd timers, and as such provides +a light-weight easy-to-use alternative to more complex toolkits. ## Building @@ -13,6 +24,8 @@ Build using NoccyLabs Pharlite: Download the latest version (or build it yourself) and move it into `/usr/bin`. +Go grab it at [https://dev.noccylabs.info/noccy/fresh/releases](https://dev.noccylabs.info/noccy/fresh/releases) + ## Usage To check for updates, pull updated images and recreate any containers defined in the @@ -20,6 +33,15 @@ To check for updates, pull updated images and recreate any containers defined in $ fresh.phar +Specify a directory to chdir into; very useful with cron: + + $ fresh.phar --dir /srv/docker/mystack + +To invoke scripts or webhooks: + + $ fresh.phar --before scripts/sitedown.sh --after scripts/siteup.sh \ + --slack https://my.slack.or/mattermost/webhook + For all available options, use the `--help` flag. ## Known Issues @@ -37,6 +59,8 @@ For all available options, use the `--help` flag. - **What are these hashes?** Fresh grabs the manifest for the image from the registry and proceeds to hash a concatenation of all the various build layer hashes. This should mean if the image is new but the layers are the same nothing will be updated. +- **How do I notify a Mattermost webook?** Mattermost webhooks are compatible with + Slack webhooks, so simply use the `--slack` flag. ## Changes @@ -54,3 +78,8 @@ For all available options, use the `--help` flag. - The `--image` option finally works. - Added a `--write-state`/`-w` option to write updated hashes to the state file. - Implemented `--config` and `--config-type`options. + +**0.1.3** + +- Added a `--before` script hook, to complement the `--after` hook. +- Hooks now invoked both before and after deploy. diff --git a/src/Refresher.php b/src/Refresher.php index 04f6fa3..2ae3b12 100644 --- a/src/Refresher.php +++ b/src/Refresher.php @@ -30,7 +30,8 @@ class Refresher ], 'Hooks' => [ 'slack' => [ null, 'slack:', "Notify a slack webhook when updating", "FRESH_SLACK" ], - 'script' => [ null, 'after:', "Invoke script after updating", "FRESH_AFTER" ], + 'script-after' => [ null, 'after:', "Invoke script after updating", "FRESH_AFTER" ], + 'script-before' => [ null, 'before:', "Invoke script before updating", "FRESH_BEFORE" ], ], 'Config' => [ 'config' => [ 'c:', 'config:', "Use custom configuration file", "FRESH_CONFIG" ], @@ -175,9 +176,11 @@ class Refresher } if ($updated !== null) { - $this->callHooks($updated); - $this->doUpdate($updated); - $this->callScript(); + $this->callHooks($updated, 'before'); + $this->callScript('before'); + $this->doUpdate(); + $this->callHooks($updated, 'after'); + $this->callScript('after'); } exit(($updated === null) ? 0 : 1); @@ -362,13 +365,22 @@ class Refresher } } - private function callHooks(array $updated) + private function callHooks(array $updated, string $event) { - $images = []; - foreach ($updated as $u) { - $images[] = sprintf("%s/%s:%s", $u->ref->getRegistry(), $u->ref->getImage(), $u->ref->getTag()); + switch ($event) { + case 'before': + $images = []; + foreach ($updated as $u) { + $images[] = sprintf("%s/%s:%s", $u->ref->getRegistry(), $u->ref->getImage(), $u->ref->getTag()); + } + $msg = "Deploying updated containers:\n* ".join("\n* ", $images); + break; + case 'after': + $msg = "Deploy complete"; + break; + default: + return; } - $msg = "Deploying updated containers:\n* ".join("\n* ", $images); foreach ($this->hooks as $hook) { $hook->sendMessage($msg, []); @@ -376,9 +388,9 @@ class Refresher } - private function callScript() + private function callScript(string $event) { - $script = $this->options['script']; + $script = $this->options["script-{$event}"] ?? null; if ($script) { $this->exec($script); }