git.iko.soy/typeable/octopod
on master help sign in
Merge pull request #59 from Aviora/stagings-statuses

Periodic check stagings statuses

commit7571ae6b6e86e5a23c2f260b2d87df2321350ab9 authorandor0 <aaorlov1@gmail.com> dateMon 6 Jul 2020, 17:23 (6 years ago) parent40a311bdb411fa
12 files changed+142−25
dm-backend/src/DMC.hs+3−2 dm-backend/src/DMS.hs+74−10 dm-common/src/Common/API.hs+1−1 dm-common/src/Common/Types.hs+18−4 dm-frontend/src/Frontend/API.hs+1−1 dm-frontend/src/Page/Deployments.hs+4−4 dm-frontend/src/Page/Utils.hs+2−2 migrations/deploy/status_and_status_updated_at.sql+13 migrations/revert/status_and_status_updated_at.sql+11 migrations/sqitch.plan+1 migrations/verify/status_and_status_updated_at.sql+7 nix/default.nix+7−1
dm-backend/src/DMC.hs+3−2
@@ -28,6 +28,7 @@ import System.IO.Temp
2828import System.Process.Typed
2929
3030import Common.API
31+import Common.Utils (dfiName)
3132import DMC.Args
3233import TLS (makeClientManager)
3334import Types
@@ -92,7 +93,7 @@ handleList = do
9293 liftIO $ do
9394 resp <- runClientM listH clientEnv
9495 let
95− getName (DeploymentFullInfo (Deployment dName _ _) _ _ _ _) = dName
96+ getName dep = dep ^.dfiName
9697 names = T.unlines . coerce . (getName <$>) <$> resp
9798 handleResponse T.putStr names
9899
@@ -182,7 +183,7 @@ updateH :: DeploymentName -> DeploymentUpdate -> ClientM CommandResponse
182183
183184infoH :: DeploymentName -> ClientM [DeploymentInfo]
184185
185−_statusH :: DeploymentName -> ClientM DeploymentStatus
186+_statusH :: DeploymentName -> ClientM CurrentDeploymentStatus
186187
187188cleanupH :: DeploymentName -> ClientM CommandResponse
188189
dm-backend/src/DMS.hs+74−10
@@ -3,7 +3,7 @@ module DMS (runDMS) where
33
44import Chronos (Time, getTime, now)
55import Control.Applicative
6−import Control.Concurrent (forkIO)
6+import Control.Concurrent (forkIO, threadDelay)
77import Control.Concurrent.Async (race_)
88import Control.Concurrent.STM
99import Control.Exception (throwIO, try, Exception)
@@ -94,8 +94,8 @@ runDMS = do
9494 (unDBConnectionString $ dmsDB opts) (unDBPoolSize $ dmsDBPoolSize opts)
9595 channel <- liftIO . atomically $ newBroadcastTChan
9696 let
97− app' =
98− app $ AppState
97+ appSt =
98+ AppState
9999 pgPool
100100 logger'
101101 channel
@@ -108,6 +108,7 @@ runDMS = do
108108 deletionCmd
109109 checkingCmd
110110 cleanupCmd
111+ app' = app appSt
111112 wsApp' = wsApp channel
112113 serverPort = dmsPort opts
113114 uiServerPort = unServerPort $ dmsUIPort opts
@@ -120,6 +121,7 @@ runDMS = do
120121 (run uiServerPort app')
121122 `race_` (runTLS tlsOpts warpOpts app')
122123 `race_` (run wsServerPort wsApp')
124+ `race_` (runStatusUpdater appSt)
123125
124126initConnectionPool :: ByteString -> Int -> IO PgPool
125127initConnectionPool dbConnStr =
@@ -160,16 +162,17 @@ listH = do
160162 AppState {pool = p, logger = l, baseDomain = d} <- ask
161163
162164 retrieved <- liftIO $ withResource p $ \conn -> query_ conn q
163− deployments <- liftIO $ for retrieved $ \(n, t, e, a, ct, ut) -> do
165+ deployments <- liftIO $ for retrieved $ \(n, t, e, a, ct, ut, st) -> do
164166 es <- parseEnvs (lines e)
165− pure $ DeploymentFullInfo (Deployment n t es) a ct ut (depUrls d n)
167+ pure $
168+ DeploymentFullInfo (Deployment n t es) (read st) a ct ut (depUrls d n)
166169
167170 liftIO . logInfo l $ "get deployments: " <> (pack . show $ deployments)
168171 return deployments
169172 where
170173 q =
171174 "SELECT name, tag, envs, archived, extract(epoch from created_at)::int, \
172− \extract(epoch from updated_at)::int \
175+ \extract(epoch from updated_at)::int, status::text \
173176 \FROM deployments ORDER BY name"
174177 depUrls domain dName =
175178 [ ("app", appUrl domain dName)
@@ -446,19 +449,20 @@ pingH = do
446449projectNameH :: AppM ProjectName
447450projectNameH = projectName <$> ask
448451
449−statusH :: DeploymentName -> AppM DeploymentStatus
452+statusH :: DeploymentName -> AppM CurrentDeploymentStatus
450453statusH dName = do
451454 st <- ask
452455 let
453456 pgPool = pool st
457+ log = logInfo (logger st)
454458 cmd = checkingCommand st
455459 args =
456460 [ "--namespace", coerce $ namespace st
457461 , "--name", coerce $ dName ]
458462 void $ selectDeployment pgPool dName AllDeployments
459− (ec, out, err) <- liftIO $ runCommand (unpack $ coerce cmd) (unpack <$> args)
460− liftIO $ print out >> print err
461− pure . DeploymentStatus $
463+ liftIO $ log $ "call " <> unwords (coerce cmd : args)
464+ ec <- liftIO $ runCommandWithoutPipes (unpack $ coerce cmd) (unpack <$> args)
465+ pure . CurrentDeploymentStatus $
462466 case ec of
463467 ExitSuccess -> Ok
464468 _ -> Error
@@ -561,6 +565,12 @@ runCommand cmd args = do
561565 err <- hGetContents . getStderr $ p
562566 pure (ec, Stdout out, Stderr err)
563567
568+runCommandWithoutPipes :: FilePath -> [String] -> IO ExitCode
569+runCommandWithoutPipes cmd args = do
570+ withProcessWait (proc cmd args) $ \p -> do
571+ ec <- waitExitCode p
572+ pure ec
573+
564574handleExitCode :: ExitCode -> IO ()
565575handleExitCode ExitSuccess = return ()
566576handleExitCode (ExitFailure c) = throwIO $ DeploymentFailed c
@@ -623,6 +633,60 @@ elapsedTime :: Time -> Time -> Duration
623633elapsedTime t1 t2 =
624634 Duration . fromIntegral . (`div` 1000000) . abs $ getTime t2 - getTime t1
625635
636+runStatusUpdater :: AppState -> IO ()
637+runStatusUpdater state = do
638+ let
639+ pgPool = pool state
640+ checkingCmd = checkingCommand state
641+ ns = namespace state
642+ interval = 30 :: Int
643+ select_deps =
644+ "SELECT name, status::text, \
645+ \extract(epoch from now())::int - \
646+ \extract(epoch from status_updated_at)::int \
647+ \FROM deployments \
648+ \WHERE checked_at < now() - interval '?' second"
649+ update_status =
650+ "UPDATE deployments \
651+ \SET status = ?, status_updated_at = now(), checked_at = now() \
652+ \WHERE name = ?"
653+ update_checked_at =
654+ "UPDATE deployments SET checked_at = now() WHERE name = ?"
655+
656+ forever $ do
657+ rows :: [(DeploymentName, Text, Int)] <- liftIO $
658+ withResource pgPool $ \conn -> query conn select_deps (Only interval)
659+ let
660+ checkList :: [(DeploymentName, DeploymentStatus, Timestamp)] =
661+ (\(n, s, t) -> (n, read . unpack $ s, coerce t)) <$> rows
662+ checkResult <- for checkList $ \(dName, dStatus, ts) -> do
663+ let
664+ args = unpack <$> coerce ns : coerce dName : []
665+ cmd = unpack . coerce $ checkingCmd
666+ ec <- runCommandWithoutPipes cmd args
667+ pure (dName, newStatus ec dStatus ts, ts)
668+ void $
669+ for (zip checkList checkResult) $ \((dName, oldSt, _), (_, newSt, _)) ->
670+ withResource pgPool $ \conn ->
671+ if oldSt == newSt
672+ then execute conn update_checked_at (Only dName)
673+ else execute conn update_status (show newSt, dName)
674+ if checkList == checkResult
675+ then pure ()
676+ else sendReloadEvent state
677+ threadDelay 5000000
678+
679+updatePeriod :: Timestamp
680+updatePeriod = Timestamp 600
681+
682+newStatus :: ExitCode -> DeploymentStatus -> Timestamp -> DeploymentStatus
683+newStatus ExitSuccess _ _ = Running
684+newStatus (ExitFailure _) Running _ = Failure
685+newStatus (ExitFailure _) CreatePending ts | ts > updatePeriod = Failure
686+newStatus (ExitFailure _) UpdatePending ts | ts > updatePeriod = Failure
687+newStatus (ExitFailure _) DeletePending _ = Failure
688+newStatus (ExitFailure _) oldStatus _ = oldStatus
689+
626690logInfo :: TimedFastLogger -> Text -> IO ()
627691logInfo l = logWithSeverity l "INFO"
628692
dm-common/src/Common/API.hs+1−1
@@ -17,7 +17,7 @@ type DeleteEndpoint c = c :> Delete '[JSON] CommandResponse
1717type UpdateEndpoint c =
1818 c :> ReqBody '[JSON] DeploymentUpdate :> Put '[JSON] CommandResponse
1919type InfoEndpoint c = c :> "info" :> Get '[JSON] [DeploymentInfo]
20−type StatusEndpoint c = c :> "status" :> Get '[JSON] DeploymentStatus
20+type StatusEndpoint c = c :> "status" :> Get '[JSON] CurrentDeploymentStatus
2121type CleanupEndpoint c =
2222 c :> "cleanup" :> Delete '[JSON] CommandResponse
2323type RestoreEndpoint c =
dm-common/src/Common/Types.hs+18−4
@@ -30,9 +30,21 @@ newtype ArchivedFlag = ArchivedFlag { unArchivedFlag :: Bool }
3030newtype Duration = Duration { unDuration :: Int }
3131 deriving (Show, FromJSON, ToJSON)
3232
33+newtype Timestamp = Timestamp { unTimestamp :: Int }
34+ deriving (Show, Eq, Ord, FromJSON, ToJSON)
35+
3336newtype ProjectName = ProjectName { uProjectName :: Text }
3437 deriving (Show, FromJSON, ToJSON)
3538
39+data DeploymentStatus
40+ = Running
41+ | Failure
42+ | CreatePending
43+ | UpdatePending
44+ | DeletePending
45+ deriving (Generic, Read, Show, Eq)
46+ deriving (FromJSON, ToJSON) via Snake DeploymentStatus
47+
3648data Deployment = Deployment
3749 { name :: DeploymentName
3850 , tag :: DeploymentTag
@@ -62,6 +74,7 @@ data DeploymentInfo = DeploymentInfo
6274
6375data DeploymentFullInfo = DeploymentFullInfo
6476 { deployment :: Deployment
77+ , status :: DeploymentStatus
6578 , archived :: Bool
6679 , createdAt :: Int
6780 , updatedAt :: Int
@@ -77,15 +90,16 @@ data DeploymentUpdate = DeploymentUpdate
7790 deriving (Generic, Show)
7891 deriving (FromJSON, ToJSON) via Snake DeploymentUpdate
7992
80−data Status
93+data CurrentStatus
8194 = Ok
8295 | Error
8396 deriving (Generic, Show, Eq)
84− deriving (FromJSON, ToJSON) via Snake Status
97+ deriving (FromJSON, ToJSON) via Snake CurrentStatus
8598
86−newtype DeploymentStatus = DeploymentStatus { status :: Status }
99+newtype CurrentDeploymentStatus =
100+ CurrentDeploymentStatus { status :: CurrentStatus }
87101 deriving (Generic, Show, Eq)
88− deriving (FromJSON, ToJSON) via Snake DeploymentStatus
102+ deriving (FromJSON, ToJSON) via Snake CurrentDeploymentStatus
89103
90104data CommandResponse
91105 = Success
dm-frontend/src/Frontend/API.hs+1−1
@@ -79,7 +79,7 @@ statusEndpoint
7979 :: MonadWidget t m
8080 => Dynamic t (Either Text DeploymentName)
8181 -> Event t ()
82− -> m (Event t (ReqResult () DeploymentStatus))
82+ -> m (Event t (ReqResult () CurrentDeploymentStatus))
8383cleanupEndpoint
8484 :: MonadWidget t m
8585 => Dynamic t (Either Text DeploymentName)
dm-frontend/src/Page/Deployments.hs+4−4
@@ -146,7 +146,7 @@ activeDeploymentsWidget
146146 => Event t ClickedElement
147147 -> Dynamic t
148148 (Map DeploymentName
149− (DeploymentFullInfo, Dynamic t (Maybe DeploymentStatus)))
149+ (DeploymentFullInfo, Dynamic t (Maybe CurrentDeploymentStatus)))
150150 -> m ()
151151activeDeploymentsWidget clickedEv dsDyn =
152152 divClass "data__primary" $
@@ -164,7 +164,7 @@ activeDeploymentWidget
164164 , SetRoute t (R Routes) m )
165165 => Event t ClickedElement
166166 -> DeploymentName
167− -> Dynamic t (DeploymentFullInfo, Dynamic t (Maybe DeploymentStatus))
167+ -> Dynamic t (DeploymentFullInfo, Dynamic t (Maybe CurrentDeploymentStatus))
168168 -> m ()
169169activeDeploymentWidget clickedEv dname dDyn' = do
170170 dDyn <- holdUniqDyn $ fst <$> dDyn'
@@ -217,7 +217,7 @@ archivedDeploymentsWidget
217217 => Event t ClickedElement
218218 -> Dynamic t
219219 (Map DeploymentName
220− (DeploymentFullInfo, Dynamic t (Maybe DeploymentStatus)))
220+ (DeploymentFullInfo, Dynamic t (Maybe CurrentDeploymentStatus)))
221221 -> m ()
222222archivedDeploymentsWidget clickedEv dsDyn = do
223223 showDyn <- toggleButton
@@ -257,7 +257,7 @@ archivedDeploymentWidget
257257 , SetRoute t (R Routes) m )
258258 => Event t ClickedElement
259259 -> DeploymentName
260− -> Dynamic t (DeploymentFullInfo, Dynamic t (Maybe DeploymentStatus))
260+ -> Dynamic t (DeploymentFullInfo, Dynamic t (Maybe CurrentDeploymentStatus))
261261 -> m ()
262262archivedDeploymentWidget clickedEv dname dDyn' = do
263263 dDyn <- holdUniqDyn $ fst <$> dDyn'
dm-frontend/src/Page/Utils.hs+2−2
@@ -178,12 +178,12 @@ formatPosixToDateTime = pack
178178 . formatTime defaultTimeLocale (iso8601DateFormat (Just "%H:%M:%S"))
179179 . intToUTCTime
180180
181−statusWidget :: MonadWidget t m => Dynamic t (Maybe DeploymentStatus) -> m ()
181+statusWidget :: MonadWidget t m => Dynamic t (Maybe CurrentDeploymentStatus) -> m ()
182182statusWidget stDyn = do
183183 stDyn' <- holdUniqDyn stDyn
184184 dyn_ $ stDyn' <&> \case
185185 Nothing -> divClass "loading loading--status-alike" $ text "Loading"
186− Just st -> case status st of
186+ Just (CurrentDeploymentStatus st) -> case st of
187187 CT.Ok -> divClass "status status--success" $ text "Success"
188188 CT.Error -> divClass "status status--failure" $ text "Failure"
189189
migrations/deploy/status_and_status_updated_at.sqlnew file+13
@@ -0,0 +1,13 @@
1+-- Deploy dm:status_and_status_updated_at to pg
2+
3+BEGIN;
4+
5+CREATE TYPE statuses as ENUM ('Running', 'Failure', 'CreatePending', 'UpdatePending', 'DeletePending');
6+
7+ALTER TABLE deployments ADD status statuses NOT NULL DEFAULT 'Running';
8+ALTER TABLE deployments ADD status_updated_at TIMESTAMPTZ NOT NULL DEFAULT now();
9+ALTER TABLE deployments ADD checked_at TIMESTAMPTZ NOT NULL DEFAULT now();
10+
11+CREATE INDEX deployments_checked_at_idx ON deployments USING BTREE (checked_at);
12+
13+COMMIT;
migrations/revert/status_and_status_updated_at.sqlnew file+11
@@ -0,0 +1,11 @@
1+-- Revert dm:status_and_status_updated_at from pg
2+
3+BEGIN;
4+
5+ALTER TABLE deployments DROP status;
6+ALTER TABLE deployments DROP status_updated_at;
7+ALTER TABLE deployments DROP checked_at;
8+
9+DROP TYPE statuses;
10+
11+COMMIT;
migrations/sqitch.plan+1
@@ -6,3 +6,4 @@ deployments 2020-02-09T15:47:32Z root <root@f56c3faaf0bf> # Creates deployments
66deployment_logs 2020-02-28T21:35:30Z Andrei Orlov <aaorlov1@gmail.com> # Creates deployment_logs table.
77archived_flag 2020-06-09T20:30:45Z Andrei Orlov <aaorlov1@gmail.com> # Add archived flag to 'deployments' and 'deployment_logs' tables
88add_duration_and_stdout_and_stderr_to_deployment_logs 2020-06-26T09:30:35Z Andrei Orlov <aaorlov1@gmail.com> # Add 'duration', 'stdout' and 'stderr' fields to 'deployment_logs' table
9+status_and_status_updated_at 2020-07-06T08:08:10Z Andrei Orlov <aaorlov1@gmail.com> # Add status, status_updated_at, checked_at to 'deployments' table
migrations/verify/status_and_status_updated_at.sqlnew file+7
@@ -0,0 +1,7 @@
1+-- Verify dm:status_and_status_updated_at on pg
2+
3+BEGIN;
4+
5+-- XXX Add verifications here.
6+
7+ROLLBACK;
nix/default.nix+7−1
@@ -67,7 +67,8 @@ with {
6767
6868 mkdir -p /usr/local/bin
6969 cp ${kubernetes-helm2-bin}/helm /usr/local/bin/
70− chmod +x /usr/local/bin/helm
70+ cp -av ${kubernetes-kubedog-bin} /usr/local/bin/kubedog
71+ chmod +x /usr/local/bin/*
7172
7273 mkdir -p /www/static/{images,styles}
7374 cp -av ${dm-frontend}/bin/frontend.jsexe/* /www/
@@ -127,6 +128,11 @@ with {
127128 sha256 = "1r91i8gy3zsgwx1fr2n6syspjrqv822ngf54db8xycskv4p5mxxj";
128129 };
129130
131+ kubernetes-kubedog-bin = fetchurl {
132+ url = "https://dl.bintray.com/flant/kubedog/v0.3.4/kubedog-linux-amd64-v0.3.4";
133+ sha256 = "13fza85xvg0g5l5013ackfkzisfsmmh3xhawncl3xb94f71s536p";
134+ };
135+
130136 haskellPackages = pkgs.haskellPackages.override {
131137 overrides = hself: hsuper: {
132138 dm-backend = (hsuper.callPackage ../default.nix { }).ghc.dm-backend;