{-# LANGUAGE FlexibleContexts #-}
module Network.EtcdV3
(
etcdClientConfigSimple
, EtcdQuery
, KeyRange(..)
, range
, rangeResponsePairs
, grantLease
, fromLeaseGrantResponse
, keepAlive
, put
, def
, module Control.Lens
, module EtcdPB
, module EtcdRPC
) where
import Control.Lens
import Data.Default.Class (def)
import Data.ByteString (ByteString)
import qualified Data.ByteString.Char8 as C8
import Data.Semigroup (Endo)
import GHC.Int (Int64)
import Network.GRPC.Client
import Network.GRPC.Client.Helpers
import Network.Socket (HostName, PortNumber)
import Proto.Etcd.Etcdserver.Etcdserverpb.Rpc as EtcdRPC
import Proto.Etcd.Etcdserver.Etcdserverpb.Rpc_Fields as EtcdPB
etcdClientConfigSimple :: HostName -> PortNumber -> UseTlsOrNot -> GrpcClientConfig
etcdClientConfigSimple host port tls =
(grpcClientConfigSimple host port tls) { _grpcClientConfigCompression = uncompressed }
unaryOutput
:: (Applicative f, Field3 a1 b1 (Either c1 a2) (Either c1 b2)) =>
(a2 -> f b2)
-> Either c2 (Either c3 a1) -> f (Either c2 (Either c3 b1))
unaryOutput = _Right . _Right . _3 . _Right
type EtcdQuery a = IO (Maybe a)
data KeyRange
= SingleKey !ByteString
| FromKey !ByteString
| Prefixed !ByteString
deriving (Show, Eq, Ord)
range
:: GrpcClient
-> KeyRange
-> EtcdQuery RangeResponse
range grpc r = preview unaryOutput <$>
rawUnary (RPC :: RPC KV "range") grpc (def & key .~ k0 & rangeEnd .~ kend)
where
(k0, kend) = rangePairForRangeQuery r
rangeResponsePairs
:: Getting (Endo [(ByteString, ByteString)]) RangeResponse (ByteString, ByteString)
rangeResponsePairs = kvs . traverse . to (\x -> (x ^. key, x ^. value))
rangePairForRangeQuery :: KeyRange -> (ByteString, ByteString)
rangePairForRangeQuery (SingleKey k) = (k, "")
rangePairForRangeQuery (FromKey k) = (k, "\NUL")
rangePairForRangeQuery (Prefixed k) = (k, kPlus1)
where
rest = C8.dropWhile (== '\xff') $ C8.reverse k
kPlus1 = if C8.null rest then "\NUL" else C8.reverse $ C8.cons (succ (C8.head rest)) (C8.drop 1 rest)
grantLease
:: GrpcClient
-> Int64
-> EtcdQuery LeaseGrantResponse
grantLease grpc seconds =
preview unaryOutput <$> rawUnary (RPC :: RPC Lease "leaseGrant") grpc (def & ttl .~ seconds)
newtype GrantedLease = GrantedLease { _getGrantedLeaseId :: Int64 }
instance Show GrantedLease where
show (GrantedLease n) = "(lease #" <> show n <> ")"
fromLeaseGrantResponse :: LeaseGrantResponse -> GrantedLease
fromLeaseGrantResponse r = GrantedLease $ r ^. EtcdPB.id
keepAlive
:: GrpcClient
-> GrantedLease
-> EtcdQuery LeaseKeepAliveResponse
keepAlive grpc (GrantedLease leaseID) =
preview unaryOutput <$> rawUnary (RPC :: RPC Lease "leaseKeepAlive") grpc (def & EtcdPB.id .~ leaseID)
put
:: GrpcClient
-> ByteString
-> ByteString
-> Maybe GrantedLease
-> EtcdQuery PutResponse
put grpc k v gl =
preview unaryOutput <$> rawUnary (RPC :: RPC KV "put") grpc (def & key .~ k & value .~ v & lease .~ l)
where
l = maybe 0 _getGrantedLeaseId gl