{-# LANGUAGE FlexibleContexts #-}
module Network.EtcdV3
(
etcdClientConfigSimple
, EtcdQuery
, KeyRange(..)
, range
, rangeResponsePairs
, grantLease
, GrantedLease
, fromLeaseGrantResponse
, keepAlive
, put
, delete
, AcquiredLock
, fromLockResponse
, lock
, unlock
, def
, module Control.Lens
) 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 qualified Proto.Etcd.Etcdserver.Etcdserverpb.Rpc as EtcdRPC
import qualified Proto.Etcd.Etcdserver.Etcdserverpb.Rpc_Fields as EtcdPB
import qualified Proto.Etcd.Etcdserver.Api.V3lock.V3lockpb.V3lock as LockRPC
import qualified Proto.Etcd.Etcdserver.Api.V3lock.V3lockpb.V3lock_Fields as LockPB
etcdClientConfigSimple :: HostName -> PortNumber -> UseTlsOrNot -> GrpcClientConfig
etcdClientConfigSimple host port tls =
(grpcClientConfigSimple host port tls) { _grpcClientConfigCompression = uncompressed }
type EtcdQuery a = IO (Maybe a)
data KeyRange
= SingleKey !ByteString
| FromKey !ByteString
| Prefixed !ByteString
deriving (Show, Eq, Ord)
range
:: GrpcClient
-> KeyRange
-> EtcdQuery EtcdRPC.RangeResponse
range grpc r = preview unaryOutput <$>
rawUnary (RPC :: RPC EtcdRPC.KV "range") grpc (def & EtcdPB.key .~ k0 & EtcdPB.rangeEnd .~ kend)
where
(k0, kend) = rangePairForRangeQuery r
rangeResponsePairs
:: Getting (Endo [(ByteString, ByteString)]) EtcdRPC.RangeResponse (ByteString, ByteString)
rangeResponsePairs = EtcdPB.kvs . traverse . to (\x -> (x ^. EtcdPB.key, x ^. EtcdPB.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 EtcdRPC.LeaseGrantResponse
grantLease grpc seconds =
preview unaryOutput <$> rawUnary (RPC :: RPC EtcdRPC.Lease "leaseGrant") grpc (def & EtcdPB.ttl .~ seconds)
newtype GrantedLease = GrantedLease { _getGrantedLeaseId :: Int64 }
instance Show GrantedLease where
show (GrantedLease n) = "(lease #" <> show n <> ")"
fromLeaseGrantResponse :: EtcdRPC.LeaseGrantResponse -> GrantedLease
fromLeaseGrantResponse r = GrantedLease $ r ^. EtcdPB.id
keepAlive
:: GrpcClient
-> GrantedLease
-> EtcdQuery EtcdRPC.LeaseKeepAliveResponse
keepAlive grpc (GrantedLease leaseID) =
preview unaryOutput <$> rawUnary (RPC :: RPC EtcdRPC.Lease "leaseKeepAlive") grpc (def & EtcdPB.id .~ leaseID)
put
:: GrpcClient
-> ByteString
-> ByteString
-> Maybe GrantedLease
-> EtcdQuery EtcdRPC.PutResponse
put grpc k v gl =
preview unaryOutput <$> rawUnary (RPC :: RPC EtcdRPC.KV "put") grpc (def & EtcdPB.key .~ k & EtcdPB.value .~ v & EtcdPB.lease .~ l)
where
l = maybe 0 _getGrantedLeaseId gl
delete
:: GrpcClient
-> KeyRange
-> EtcdQuery EtcdRPC.DeleteRangeResponse
delete grpc r = preview unaryOutput <$>
rawUnary (RPC :: RPC EtcdRPC.KV "deleteRange") grpc (def & EtcdPB.key .~ k0 & EtcdPB.rangeEnd .~ kend)
where
(k0, kend) = rangePairForRangeQuery r
newtype AcquiredLock = AcquiredLock { _getAcquiredLock :: ByteString }
instance Show AcquiredLock where
show (AcquiredLock n) = "(lock #" <> show n <> ")"
fromLockResponse :: LockRPC.LockResponse -> AcquiredLock
fromLockResponse l = AcquiredLock $ l ^. LockPB.key
lock
:: GrpcClient
-> ByteString
-> GrantedLease
-> EtcdQuery LockRPC.LockResponse
lock grpc n (GrantedLease leaseID) = preview unaryOutput <$>
rawUnary (RPC :: RPC LockRPC.Lock "lock") grpc (def & LockPB.name .~ n & LockPB.lease .~ leaseID)
unlock
:: GrpcClient
-> AcquiredLock
-> EtcdQuery LockRPC.UnlockResponse
unlock grpc (AcquiredLock k) = preview unaryOutput <$>
rawUnary (RPC :: RPC LockRPC.Lock "unlock") grpc (def & LockPB.key .~ k)
watch
:: GrpcClient
-> (a -> EtcdRPC.WatchResponse -> IO a)
-> a
-> EtcdQuery (a)
watch grpc foo = undefined