diff --git a/cluster/services/consul/default.nix b/cluster/services/consul/default.nix index 24bb20c..99f6aca 100644 --- a/cluster/services/consul/default.nix +++ b/cluster/services/consul/default.nix @@ -15,6 +15,7 @@ in nixos.agent = [ ./agent.nix ./remote-api.nix + ./ready.nix ]; }; diff --git a/cluster/services/consul/ready.nix b/cluster/services/consul/ready.nix new file mode 100644 index 0000000..c3ced6e --- /dev/null +++ b/cluster/services/consul/ready.nix @@ -0,0 +1,54 @@ +{ config, lib, pkgs, ... }: + +let + consulReady = pkgs.writers.writeHaskellBin "consul-ready" { + libraries = with pkgs.haskellPackages; [ aeson http-conduit watchdog ]; + } '' + {-# LANGUAGE OverloadedStrings #-} + import Control.Watchdog + import Control.Exception + import System.IO + import Network.HTTP.Simple + import Data.Aeson + + flushLogger :: WatchdogLogger String + flushLogger taskErr delay = do + defaultLogger taskErr delay + hFlush stdout + + data ConsulHealth = ConsulHealth { + healthy :: Bool + } + + instance FromJSON ConsulHealth where + parseJSON (Object v) = ConsulHealth <$> (v .: "Healthy") + + handleException ex = case ex of + (SomeException _) -> return $ Left "Consul is not active" + + main :: IO () + main = watchdog $ do + setInitialDelay 300_000 + setMaximumDelay 30_000_000 + setLoggingAction flushLogger + watch $ handle handleException $ do + res <- httpJSON "${config.links.consulAgent.url}/v1/operator/autopilot/health" + case getResponseBody res of + ConsulHealth True -> return $ Right () + ConsulHealth False -> return $ Left "Consul is unhealthy" + ''; +in + +{ + systemd.services.consul-ready = { + description = "Wait for Consul"; + requires = [ "consul.service" ]; + after = [ "consul.service" ]; + serviceConfig = { + ExecStart = lib.getExe consulReady; + DynamicUser = true; + TimeoutStartSec = "5m"; + Type = "oneshot"; + }; + }; +}