|
| 1 | +{-# LANGUAGE FlexibleContexts #-} |
| 2 | +{-# LANGUAGE OverloadedStrings #-} |
| 3 | +module Kubernetes.Watch.Client |
| 4 | + ( WatchEvent |
| 5 | + , eventType |
| 6 | + , eventObject |
| 7 | + , dispatchWatch |
| 8 | + ) where |
| 9 | + |
| 10 | +import Control.Monad |
| 11 | +import Control.Monad.Trans (lift) |
| 12 | +import Data.Aeson |
| 13 | +import qualified Data.ByteString as B |
| 14 | +import qualified Data.ByteString.Streaming.Char8 as Q |
| 15 | +import qualified Data.Text as T |
| 16 | +import Kubernetes.Core |
| 17 | +import Kubernetes.Client |
| 18 | +import Kubernetes.MimeTypes |
| 19 | +import Kubernetes.Model (Watch(..)) |
| 20 | +import Network.HTTP.Client |
| 21 | + |
| 22 | +data WatchEvent a = WatchEvent |
| 23 | + { _eventType :: T.Text |
| 24 | + , _eventObject :: a |
| 25 | + } deriving (Eq, Show) |
| 26 | + |
| 27 | +instance FromJSON a => FromJSON (WatchEvent a) where |
| 28 | + parseJSON (Object x) = WatchEvent <$> x .: "type" <*> x .: "object" |
| 29 | + parseJSON _ = fail "Expected an object" |
| 30 | + |
| 31 | +instance ToJSON a => ToJSON (WatchEvent a) where |
| 32 | + toJSON x = object |
| 33 | + [ "type" .= _eventType x |
| 34 | + , "object" .= _eventObject x |
| 35 | + ] |
| 36 | + |
| 37 | +-- | Type of the 'WatchEvent'. |
| 38 | +eventType :: WatchEvent a -> T.Text |
| 39 | +eventType = _eventType |
| 40 | + |
| 41 | +-- | Object within the 'WatchEvent'. |
| 42 | +eventObject :: WatchEvent a -> a |
| 43 | +eventObject = _eventObject |
| 44 | + |
| 45 | +{-| Dispatch a request setting watch to true. Takes a consumer function |
| 46 | +which consumes the 'Q.ByteString' stream. Following is a simple example which |
| 47 | +just streams to stdout. First some setup - this assumes kubernetes is accessible |
| 48 | +at http://localhost:8001, e.g. after running /kubectl proxy/: |
| 49 | +
|
| 50 | +@ |
| 51 | +import qualified Data.ByteString.Streaming.Char8 as Q -- from <https://hackage.haskell.org/package/streaming-bytestring-0.1.5/docs/Data-ByteString-Streaming-Char8.html streaming-bytestring> |
| 52 | +
|
| 53 | +manager <- newManager defaultManagerSettings |
| 54 | +defaultConfig <- newConfig |
| 55 | +config = defaultConfig { configHost = "http://localhost:8001", configValidateAuthMethods = False } |
| 56 | +request = listEndpointsForAllNamespaces (Accept MimeJSON) |
| 57 | +@ |
| 58 | +
|
| 59 | +Launching 'dispatchWatch' with the above we get a stream of endpoints data: |
| 60 | +
|
| 61 | +@ |
| 62 | + > dispatchWatch manager config request Q.stdout |
| 63 | + {"type":\"ADDED\","object":{"kind":\"Endpoints\","apiVersion":"v1","metadata":{"name":"heapster" .... |
| 64 | +@ |
| 65 | +-} |
| 66 | +dispatchWatch :: |
| 67 | + (HasOptionalParam req Watch, MimeType accept, MimeType contentType) => |
| 68 | + Manager |
| 69 | + -> KubernetesConfig |
| 70 | + -> KubernetesRequest req contentType resp accept |
| 71 | + -> (Q.ByteString IO () -> IO a) |
| 72 | + -> IO a |
| 73 | +dispatchWatch manager config request apply = do |
| 74 | + let watchRequest = applyOptionalParam request (Watch True) |
| 75 | + (InitRequest req) <- _toInitRequest config watchRequest |
| 76 | + withHTTP req manager $ \resp -> apply $ responseBody resp |
| 77 | + |
| 78 | +withHTTP :: |
| 79 | + Request |
| 80 | + -> Manager |
| 81 | + -> (Response (Q.ByteString IO ()) -> IO a) |
| 82 | + -> IO a |
| 83 | +withHTTP response manager f = withResponse response manager f' |
| 84 | + where |
| 85 | + f' resp = do |
| 86 | + let p = (from . brRead . responseBody) resp |
| 87 | + f (resp {responseBody = p}) |
| 88 | + from :: IO B.ByteString -> Q.ByteString IO () |
| 89 | + from io = go |
| 90 | + where |
| 91 | + go = do |
| 92 | + bs <- lift io |
| 93 | + unless (B.null bs) $ do |
| 94 | + Q.chunk bs |
| 95 | + go |
0 commit comments