sensu-run 0.4.0.5 → 0.5.0
raw patch · 4 files changed
+61/−21 lines, 4 filesdep +asyncdep +http-client-tlsdep ~temporarydep ~unix
Dependencies added: async, http-client-tls
Dependency ranges changed: temporary, unix
Files
- CHANGELOG.md +8/−0
- README.md +7/−3
- sensu-run.cabal +5/−3
- sensu-run.hs +41/−15
CHANGELOG.md view
@@ -1,5 +1,13 @@ # Revision history for sensu-run +## 0.5.0 -- 2018-04-20++* Add support for HTTPS (#18)+* Do not append new line to the output when the command was successful+* Add --redirect option (#12)+* Relax upper version bound for temporary+* Tighten upper version bound for unix+ ## 0.4.0.5 -- 2018-04-09 * Relax upper version bounds for base and aeson
README.md view
@@ -10,11 +10,13 @@ ## Installation +NOTE: Currently sensu-run doesn't work on Windows. See [#16](https://github.com/maoe/sensu-run/issues/16).+ Binary releases are available at [GitHub Releases](https://github.com/maoe/sensu-run/releases). Currently supported platforms for the binary releases are: * Ubuntu (64bit) * macOS-* Windows (x64)+* Windows (x64, x86) You can also build it yourself using [stack](https://docs.haskellstack.org/en/stable/README/): ```sh@@ -28,8 +30,8 @@ % 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])+ [--server URL]) [--redirect] [--dry|--dry-run] [-s|--shell]+ [COMMAND] | [-v|--version]) Available options: -h,--help Show this help text@@ -44,6 +46,8 @@ --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+ --redirect Redirect command output to sensu-run's output+ --dry,--dry-run Dump the JSON object which is supposed to be sent -s,--shell Execute the command using the shell ```
sensu-run.cabal view
@@ -1,5 +1,5 @@ name: sensu-run-version: 0.4.0.5+version: 0.5.0 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@@ -26,16 +26,18 @@ main-is: sensu-run.hs build-depends: aeson >= 0.11 && < 1.4+ , async < 2.3 , base >= 4.9 && < 4.12 , bytestring >= 0.10 && < 0.11 , filepath >= 1.4.1 && < 1.5 , http-client >= 0.5.6 && < 0.6+ , http-client-tls < 0.4 , http-types >= 0.9.1 && < 0.13 , lens >= 4.15 && < 4.17 , network >= 2.2.3 && < 2.7 , optparse-applicative >= 0.12 && < 0.15 , process >= 1.4 && < 1.7- , temporary >= 1.1 && < 1.3+ , temporary >= 1.1 && < 1.4 , text >= 1.2.2 && < 1.3 , time >= 1.5.0.1 && < 1.10 , unix-compat < 0.6@@ -47,6 +49,6 @@ if os(windows) cpp-options: -DWINDOWS else- build-depends: unix+ build-depends: unix < 2.8 ghc-options: -Wall -threaded default-language: Haskell2010
sensu-run.hs view
@@ -13,7 +13,9 @@ {-# LANGUAGE ViewPatterns #-} module Main where import Control.Exception+import Control.Monad import Data.Foldable+import Data.Function import Data.List.NonEmpty (NonEmpty) import Data.Maybe import Data.Monoid@@ -24,17 +26,21 @@ import qualified System.Timeout as Timeout import Prelude +import Control.Concurrent.Async import Control.Lens hiding ((.=)) import Data.Time import Data.Time.Clock.POSIX import Network.HTTP.Client (HttpException)+import Network.HTTP.Client.TLS import Network.Socket import System.FilePath ((</>)) import System.IO.Temp import System.Process import System.PosixCompat.User (getEffectiveUserName)+import qualified Data.ByteString as B import qualified Data.ByteString.Lazy as BL import qualified Data.ByteString.Lazy.Char8 as BL8+import qualified Data.ByteString.Lazy.Internal as BLI import qualified Data.Text as T import qualified Data.Text.Encoding.Error as TE import qualified Data.Text.Lazy as TL@@ -64,12 +70,18 @@ RunOptions {..} -> withSystemTempFile "sensu-run.XXX" $ \path hdl -> do executed <- getCurrentTime rawStatus <- try $ bracket- (startProcess cmdspec hdl)- (\ph -> do+ (startProcess cmdspec)+ (\(_, _, ph) -> do terminateProcess ph killProcessTree ph waitForProcess ph)- (withTimeout timeout . waitForProcess)+ $ \(out, err, ph) -> do+ aout <- async $ redirectOutput out $ if redirect+ then [hdl, stdout] else [hdl]+ aerr <- async $ redirectOutput err $ if redirect+ then [hdl, stderr] else [hdl]+ mapM_ waitCatch [aout, aerr]+ withTimeout timeout $ waitForProcess ph hClose hdl exited <- getCurrentTime rawOutput <- BL.readFile path@@ -79,10 +91,9 @@ { command = cmdspec , output = TL.toLazyText $ mconcat [ TL.fromLazyText (TL.decodeUtf8With TE.lenientDecode rawOutput)- , "\n"- , TL.fromString $ case rawStatus of- Left ioe -> show (ioe :: IOException)- Right Nothing -> "sensu-run: timed out"+ , case rawStatus of+ Left (ioe :: IOException) -> "\n" <> TL.fromString (show ioe)+ Right Nothing -> "\n" <> "sensu-run: timed out" Right _ -> mempty ] , status = case rawStatus of@@ -140,22 +151,23 @@ | HT.statusIsSuccessful status -> return () | otherwise -> fail $ "sendToSensuServer: unexpected status " ++ show status- params = W.defaults &- W.header "Content-Type" .~ ["application/json"]+ params = W.defaults+ & W.header "Content-Type" .~ ["application/json"]+ & W.manager .~ Left tlsManagerSettings handleError reason = do hPutStrLn stderr $ "Failed to POST results to Sensu server (" ++ reason ++ ")" exitFailure -startProcess :: CmdSpec -> Handle -> IO ProcessHandle-startProcess cmdspec hdl = do- (_, _, _, ph) <- createProcess CreateProcess+startProcess :: CmdSpec -> IO (Handle, Handle, ProcessHandle)+startProcess cmdspec = do+ (_, Just out, Just err, ph) <- createProcess CreateProcess { cmdspec , cwd = Nothing , env = Nothing , std_in = Inherit- , std_out = UseHandle hdl- , std_err = UseHandle hdl+ , std_out = CreatePipe+ , std_err = CreatePipe , close_fds = False , create_group = True -- necessary to not kill sensu-run itself , delegate_ctlc = False@@ -168,8 +180,16 @@ , use_process_jobs = True #endif }- return ph+ return (out, err, ph) +redirectOutput :: Handle -> [Handle] -> IO ()+redirectOutput source sinks = fix $ \loop -> do+ eof <- hIsEOF source+ unless eof $ do+ chunk <- B.hGetSome source BLI.defaultChunkSize+ mapM_ (flip B.hPut chunk) sinks+ loop+ withTimeout :: Maybe NominalDiffTime -> IO a -> IO (Maybe a) withTimeout time io = case time of Just n -> Timeout.timeout (seconds n) io@@ -187,6 +207,7 @@ , timeout :: Maybe NominalDiffTime , handlers :: [T.Text] , endpoint :: Endpoint+ , redirect :: Bool , dryRun :: Bool } @@ -239,9 +260,14 @@ [ ClientSocketInput <$> portOption , SensuServer . NE.fromList <$> O.some serverOption ]+ redirect <- O.switch $ mconcat+ [ O.long "redirect"+ , O.help "Redirect command output to sensu-run's output"+ ] dryRun <- O.switch $ mconcat [ O.long "dry-run" , O.long "dry"+ , O.help "Dump the JSON object which is supposed to be sent" ] cmdspec <- cmdSpecOption return RunOptions {..}