sensu-run 0.1.0 → 0.1.1
raw patch · 4 files changed
+180/−82 lines, 4 files
Files
- CHANGELOG.md +4/−0
- README.md +78/−0
- sensu-run.cabal +4/−2
- sensu-run.hs +94/−80
CHANGELOG.md view
@@ -1,5 +1,9 @@ # Revision history for sensu-run +## 0.1.1 -- 2017-06-09++* Add --version option+ ## 0.1.0 -- 2017-06-07 * Fix a bug that ignores --handle option by accident
+ README.md view
@@ -0,0 +1,78 @@+# sensu-run+[](http://hackage.haskell.org/package/sensu-run)+[](http://packdeps.haskellers.com/feed?needle=sensu-run)+[](http://stackage.org/nightly/package/sensu-run)+[](https://travis-ci.org/maoe/sensu-run)+[](https://ci.appveyor.com/project/maoe/sensu-run/branch/master)++`sensu-run` runs a command and send its result to Sensu server using the [client socket input](https://sensuapp.org/docs/latest/reference/clients.html#client-socket-input) or via the Sensu API. It is useful to monitor cron jobs for example.++## Installation++Install [stack](https://docs.haskellstack.org/en/stable/README/).++```sh+stack install --resolver=nightly sensu-run+```+will install the `sensu-run` command in `~/.local/bin`.++## Usage++```console+% sensu-run --help+Usage: sensu-run ([-n|--name NAME] [--source SOURCE] [--ttl SECONDS]+ [--timeout SECONDS] [--handler HANDLER] ([--port PORT] |+ [--server URL]) [--dry|--dry-run] [-s|--shell] [COMMAND] |+ [-v|--version])++Available options:+ -h,--help Show this help text+ -n,--name NAME The name of the check+ --source SOURCE The check source, used to create a JIT Sensu client+ for an external resource+ --ttl SECONDS The time to live in seconds until check results are+ considered stale+ --timeout SECONDS The check executaion duration timeout in seconds+ --handler HANDLER Sensu event handler(s) to use for events created by+ the check+ --port PORT Send results to the local sensu-client listening on+ the specified port (default: 3030)+ --server URL Send results to the specified Sensu server+ -s,--shell Execute the command using the shell+```++`--dry-run` option is useful to check the JSON output:++```console+% sensu-run --name check-home-src-size --handler foo --dry-run -- du -s $HOME/src | jq .+{+ "name": "check-home-src-size",+ "command": "du -s /home/maoe/src",+ "issued": 1496966954,+ "executed": 1496966954,+ "duration": 1.235584,+ "status": 0,+ "output": "44567740\t/home/maoe/src\n",+ "handlers": [+ "foo"+ ]+}+```++Without the `--dry-run` option, `sensu-run` sends the output to localhost:PORT, which is expected to be listened by `sensu-client`.++`sensu-run` sets the status field depending on the command exit code and timeout:++| command exit code | `status` field |+|-------------------|----------------|+| 0 | 0 (OK) |+| non-zero | 2 (CRITICAL) |+| command timed out | 3 (UNKNOWN) |++### Sensu API++`sensu-run` supports posting check results via Sensu API as well. Use `--server` option to specify Sensu server addresses. If multiple servers are specified, `sensu-run` tries them one by one until it succeeds.++```sh+sensu-run --name check-true --handler foo --server sensu1.example.com --server sensu2.example.com --dry-run -- du -s $HOME/src+```
sensu-run.cabal view
@@ -1,5 +1,5 @@ name: sensu-run-version: 0.1.0+version: 0.1.1 synopsis: A tool to send command execution results to Sensu description: @sensu-run@ is a command line tool to send command execution results to Sensu@@ -13,7 +13,9 @@ bug-reports: https://github.com/maoe/sensu-run/issues category: System build-type: Simple-extra-source-files: CHANGELOG.md+extra-source-files:+ CHANGELOG.md+ README.md cabal-version: >= 1.10 tested-with: GHC == 8.0.2
sensu-run.hs view
@@ -15,9 +15,11 @@ import Data.Foldable import Data.List.NonEmpty (NonEmpty) import Data.Maybe+import Data.Monoid import System.Exit import System.IO import qualified Data.List.NonEmpty as NE+import qualified Data.Version as V import qualified System.Timeout as Timeout import Control.Lens hiding ((.=))@@ -40,40 +42,46 @@ import qualified Network.Wreq as W import qualified Options.Applicative as O +import qualified Paths_sensu_run as Paths+ main :: IO () main = do issued <- getCurrentTime- Options {..} <- O.execParser $ O.info (O.helper <*> options) O.fullDesc- withSystemTempFile "sensu-run.XXX" $ \path hdl -> do- executed <- getCurrentTime- rawStatus <- bracket- (startProcess cmdspec hdl)- terminateProcess- (withTimeout timeout . waitForProcess)- exited <- getCurrentTime- rawOutput <- BL.readFile path- let- encoded = encode CheckResult- { command = cmdspec- , output = TL.decodeUtf8With TE.lenientDecode rawOutput- , status = case rawStatus of- Nothing -> UNKNOWN- Just ExitSuccess -> OK- Just ExitFailure {} -> CRITICAL- , duration = diffUTCTime exited executed- , ..- }- if dryRun- then BL8.putStrLn encoded- else case endpoint of- ClientSocketInput port -> sendToClientSocketInput port encoded- SensuServer urls -> sendToSensuServer urls encoded- case rawStatus of- Just ExitSuccess -> exitSuccess- Nothing -> do- hPutStrLn stderr $ showCmdSpec cmdspec ++ " timed out"- exitFailure- Just ExitFailure {} -> exitFailure+ opts <- O.execParser $ O.info (O.helper <*> options) O.fullDesc+ case opts of+ ShowVersion -> do+ putStrLn $ "sensu-run " ++ V.showVersion Paths.version+ exitSuccess+ RunOptions {..} -> withSystemTempFile "sensu-run.XXX" $ \path hdl -> do+ executed <- getCurrentTime+ rawStatus <- bracket+ (startProcess cmdspec hdl)+ terminateProcess+ (withTimeout timeout . waitForProcess)+ exited <- getCurrentTime+ rawOutput <- BL.readFile path+ let+ encoded = encode CheckResult+ { command = cmdspec+ , output = TL.decodeUtf8With TE.lenientDecode rawOutput+ , status = case rawStatus of+ Nothing -> UNKNOWN+ Just ExitSuccess -> OK+ Just ExitFailure {} -> CRITICAL+ , duration = diffUTCTime exited executed+ , ..+ }+ if dryRun+ then BL8.putStrLn encoded+ else case endpoint of+ ClientSocketInput port -> sendToClientSocketInput port encoded+ SensuServer urls -> sendToSensuServer urls encoded+ case rawStatus of+ Just ExitSuccess -> exitSuccess+ Nothing -> do+ hPutStrLn stderr $ showCmdSpec cmdspec ++ " timed out"+ exitFailure+ Just ExitFailure {} -> exitFailure sendToClientSocketInput :: PortNumber -- ^ Listening port of Sensu client socket@@ -140,16 +148,18 @@ Just n -> Timeout.timeout (round $ n * 10^(6 :: Int)) io Nothing -> Just <$> io -data Options = Options- { name :: T.Text- , cmdspec :: CmdSpec- , source :: Maybe T.Text- , ttl :: Maybe NominalDiffTime- , timeout :: Maybe NominalDiffTime- , handlers :: [T.Text]- , endpoint :: Endpoint- , dryRun :: Bool- }+data Options+ = ShowVersion+ | RunOptions+ { name :: T.Text+ , cmdspec :: CmdSpec+ , source :: Maybe T.Text+ , ttl :: Maybe NominalDiffTime+ , timeout :: Maybe NominalDiffTime+ , handlers :: [T.Text]+ , endpoint :: Endpoint+ , dryRun :: Bool+ } data Endpoint = ClientSocketInput PortNumber@@ -162,46 +172,50 @@ -- client HTTP sockets listen on 3031. options :: O.Parser Options-options = do- name <- textOption $ mconcat- [ O.short 'n'- , O.long "name"- , O.metavar "NAME"- , O.help "The name of the check"- ]- source <- O.optional $ textOption $ mconcat- [ O.long "source"- , O.metavar "SOURCE"- , O.help $ unlines- [ "The check source, used to create a JIT Sensu client for an"- , "external resource" ]- ]- ttl <- durationOption $ mconcat- [ O.long "ttl"- , O.metavar "SECONDS"- , O.help "The time to live in seconds until check results are considered stale"- ]- timeout <- durationOption $ mconcat- [ O.long "timeout"- , O.metavar "SECONDS"- , O.help "The check executaion duration timeout in seconds"- ]- handlers <- O.some $ textOption $ mconcat- [ O.long "handler"- , O.metavar "HANDLER"- , O.help "Sensu event handler(s) to use for events created by the check"- ]- endpoint <- asum- [ ClientSocketInput <$> portOption- , SensuServer . NE.fromList <$> O.some serverOption- ]- dryRun <- O.switch $ mconcat- [ O.long "dry-run"- , O.long "dry"- ]- cmdspec <- cmdSpecOption- return Options {..}+options = asum+ [ runOptions+ , ShowVersion <$ O.switch (O.long "version" <> O.short 'v')+ ] where+ runOptions = do+ name <- textOption $ mconcat+ [ O.short 'n'+ , O.long "name"+ , O.metavar "NAME"+ , O.help "The name of the check"+ ]+ source <- O.optional $ textOption $ mconcat+ [ O.long "source"+ , O.metavar "SOURCE"+ , O.help $ unlines+ [ "The check source, used to create a JIT Sensu client for an"+ , "external resource" ]+ ]+ ttl <- durationOption $ mconcat+ [ O.long "ttl"+ , O.metavar "SECONDS"+ , O.help "The time to live in seconds until check results are considered stale"+ ]+ timeout <- durationOption $ mconcat+ [ O.long "timeout"+ , O.metavar "SECONDS"+ , O.help "The check executaion duration timeout in seconds"+ ]+ handlers <- O.some $ textOption $ mconcat+ [ O.long "handler"+ , O.metavar "HANDLER"+ , O.help "Sensu event handler(s) to use for events created by the check"+ ]+ endpoint <- asum+ [ ClientSocketInput <$> portOption+ , SensuServer . NE.fromList <$> O.some serverOption+ ]+ dryRun <- O.switch $ mconcat+ [ O.long "dry-run"+ , O.long "dry"+ ]+ cmdspec <- cmdSpecOption+ return RunOptions {..} textOption m = T.pack <$> O.strOption m durationOption m = fmap (realToFrac @Double) <$> O.optional (O.option O.auto m)