Skip to content

Commit cf050ff

Browse files
author
Mitsutoshi Aoe
committed
Merge branch 'release/v0.6.0'
2 parents c09dae5 + 0d1c6ab commit cf050ff

File tree

3 files changed

+84
-54
lines changed

3 files changed

+84
-54
lines changed

CHANGELOG.md

+5
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
# Revision history for sensu-run
22

3+
## 0.6.0 -- 2018-07-09
4+
5+
* Lock a file to prevent multiple instances of the same name from running
6+
* Use --no-lock option to disable this behavior
7+
38
## 0.5.0.3 -- 2018-07-07
49

510
* Relax upper version bound for lens

sensu-run.cabal

+3-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
name: sensu-run
2-
version: 0.5.0.3
2+
version: 0.6.0
33
synopsis: A tool to send command execution results to Sensu
44
description:
55
@sensu-run@ is a command line tool to send command execution results to Sensu
@@ -29,6 +29,8 @@ executable sensu-run
2929
, async < 2.3
3030
, base >= 4.9 && < 4.12
3131
, bytestring >= 0.10 && < 0.11
32+
, directory < 1.4
33+
, filelock < 0.2
3234
, filepath >= 1.4.1 && < 1.5
3335
, http-client >= 0.5.6 && < 0.6
3436
, http-client-tls < 0.4

sensu-run.hs

+76-53
Original file line numberDiff line numberDiff line change
@@ -27,16 +27,18 @@ import qualified System.Timeout as Timeout
2727
import Prelude
2828

2929
import Control.Concurrent.Async
30-
import Control.Lens hiding ((.=))
30+
import Control.Lens hiding ((.=), (<.>))
3131
import Data.Time
3232
import Data.Time.Clock.POSIX
3333
import Network.HTTP.Client (HttpException)
3434
import Network.HTTP.Client.TLS
3535
import Network.Socket
36-
import System.FilePath ((</>))
36+
import System.Directory (removeFile)
37+
import System.FileLock
38+
import System.FilePath ((</>), (<.>))
3739
import System.IO.Temp
38-
import System.Process
3940
import System.PosixCompat.User (getEffectiveUserName)
41+
import System.Process
4042
import qualified Data.ByteString as B
4143
import qualified Data.ByteString.Lazy as BL
4244
import qualified Data.ByteString.Lazy.Char8 as BL8
@@ -67,56 +69,72 @@ main = do
6769
ShowVersion -> do
6870
putStrLn $ "sensu-run " ++ V.showVersion Paths.version
6971
exitSuccess
70-
RunOptions {..} -> withSystemTempFile "sensu-run.XXX" $ \path hdl -> do
71-
executed <- getCurrentTime
72-
rawStatus <- try $ bracket
73-
(startProcess cmdspec)
74-
(\(_, _, ph) -> do
75-
terminateProcess ph
76-
killProcessTree ph
77-
waitForProcess ph)
78-
$ \(out, err, ph) -> do
79-
aout <- async $ redirectOutput out $ if redirect
80-
then [hdl, stdout] else [hdl]
81-
aerr <- async $ redirectOutput err $ if redirect
82-
then [hdl, stderr] else [hdl]
83-
mapM_ waitCatch [aout, aerr]
84-
withTimeout timeout $ waitForProcess ph
85-
hClose hdl
86-
exited <- getCurrentTime
87-
rawOutput <- BL.readFile path
88-
user <- T.pack <$> getEffectiveUserName
89-
let
90-
encoded = encode CheckResult
91-
{ command = cmdspec
92-
, output = TL.toLazyText $ mconcat
93-
[ TL.fromLazyText (TL.decodeUtf8With TE.lenientDecode rawOutput)
94-
, case rawStatus of
95-
Left (ioe :: IOException) -> "\n" <> TL.fromString (show ioe)
96-
Right Nothing -> "\n" <> "sensu-run: timed out"
97-
Right _ -> mempty
98-
]
99-
, status = case rawStatus of
100-
Right (Just ExitSuccess) -> OK
101-
Right (Just ExitFailure {}) -> CRITICAL
102-
_ -> UNKNOWN
103-
, duration = diffUTCTime exited executed
104-
, ..
105-
}
106-
if dryRun
107-
then BL8.putStrLn encoded
108-
else case endpoint of
109-
ClientSocketInput port -> sendToClientSocketInput port encoded
110-
SensuServer urls -> sendToSensuServer urls encoded
111-
case rawStatus of
112-
Left ioe -> do
113-
hPutStrLn stderr $ show ioe
114-
exitFailure
115-
Right (Just ExitSuccess) -> exitSuccess
116-
Right Nothing -> do
117-
hPutStrLn stderr $ showCmdSpec cmdspec ++ " timed out"
118-
exitFailure
119-
Right (Just ExitFailure {}) -> exitFailure
72+
RunOptions {..} -> exclusivelyIf lock name $
73+
withSystemTempFile "sensu-run.XXX" $ \path hdl -> do
74+
executed <- getCurrentTime
75+
rawStatus <- try $ bracket
76+
(startProcess cmdspec)
77+
(\(_, _, ph) -> do
78+
terminateProcess ph
79+
killProcessTree ph
80+
waitForProcess ph)
81+
$ \(out, err, ph) -> do
82+
aout <- async $ redirectOutput out $ if redirect
83+
then [hdl, stdout] else [hdl]
84+
aerr <- async $ redirectOutput err $ if redirect
85+
then [hdl, stderr] else [hdl]
86+
mapM_ waitCatch [aout, aerr]
87+
withTimeout timeout $ waitForProcess ph
88+
hClose hdl
89+
exited <- getCurrentTime
90+
rawOutput <- BL.readFile path
91+
user <- T.pack <$> getEffectiveUserName
92+
let
93+
encoded = encode CheckResult
94+
{ command = cmdspec
95+
, output = TL.toLazyText $ mconcat
96+
[ TL.fromLazyText (TL.decodeUtf8With TE.lenientDecode rawOutput)
97+
, case rawStatus of
98+
Left (ioe :: IOException) -> "\n" <> TL.fromString (show ioe)
99+
Right Nothing -> "\n" <> "sensu-run: timed out"
100+
Right _ -> mempty
101+
]
102+
, status = case rawStatus of
103+
Right (Just ExitSuccess) -> OK
104+
Right (Just ExitFailure {}) -> CRITICAL
105+
_ -> UNKNOWN
106+
, duration = diffUTCTime exited executed
107+
, ..
108+
}
109+
if dryRun
110+
then BL8.putStrLn encoded
111+
else case endpoint of
112+
ClientSocketInput port -> sendToClientSocketInput port encoded
113+
SensuServer urls -> sendToSensuServer urls encoded
114+
case rawStatus of
115+
Left ioe -> do
116+
hPutStrLn stderr $ show ioe
117+
exitFailure
118+
Right (Just ExitSuccess) -> exitSuccess
119+
Right Nothing -> do
120+
hPutStrLn stderr $ showCmdSpec cmdspec ++ " timed out"
121+
exitFailure
122+
Right (Just ExitFailure {}) -> exitFailure
123+
124+
exclusivelyIf :: Bool -> T.Text -> IO a -> IO a
125+
exclusivelyIf exclusive name io
126+
| exclusive = do
127+
tmpDir <- getCanonicalTemporaryDirectory
128+
let path = tmpDir </> "sensu-run" <.> T.unpack name <.> "lock"
129+
r <- withTryFileLock path Exclusive (const io)
130+
case r of
131+
Nothing -> do
132+
putStrLn $ path ++ " is aquired by other process"
133+
exitSuccess
134+
Just a -> do
135+
removeFile path
136+
return a
137+
| otherwise = io
120138

121139
sendToClientSocketInput
122140
:: PortNumber -- ^ Listening port of Sensu client socket
@@ -215,6 +233,7 @@ data Options
215233
, handlers :: [T.Text]
216234
, endpoint :: Endpoint
217235
, redirect :: Bool
236+
, lock :: Bool
218237
, dryRun :: Bool
219238
}
220239

@@ -271,6 +290,10 @@ options = asum
271290
[ O.long "redirect"
272291
, O.help "Redirect command output to sensu-run's output"
273292
]
293+
(not -> lock) <- O.switch $ mconcat
294+
[ O.long "no-lock"
295+
, O.help "Do not create a lock file to allow multiple instances to run"
296+
]
274297
dryRun <- O.switch $ mconcat
275298
[ O.long "dry-run"
276299
, O.long "dry"

0 commit comments

Comments
 (0)