@@ -76,6 +76,27 @@ module Docker.Client.Types (
7676 , HostConfig(..)
7777 , defaultHostConfig
7878 , NetworkingConfig(..)
79+ , NetworkScope(..)
80+ , CIDR(..)
81+ , IPAMDriver(..)
82+ , IPAMConfig(..)
83+ , IPAM(..)
84+ , NetworkContainer(..)
85+ , NetworkDetails(..)
86+ , NetworkType(..)
87+ , NetworkName
88+ , NetworkFilter(..)
89+ , ConnectConfig(..)
90+ , defaultConnectConfig
91+ , DisconnectConfig(..)
92+ , defaultDisconnectConfig
93+ , PruneFilter(..)
94+ , defaultPruneFilter
95+ , NetworksDeleted(..)
96+ , EndpointSettings(..)
97+ , defaultEndpointSettings
98+ , IPAMSettings(..)
99+ , defaultIPAMSettings
79100 , EndpointConfig(..)
80101 , Ulimit(..)
81102 , ContainerResources(..)
@@ -100,6 +121,7 @@ module Docker.Client.Types (
100121 , MemoryConstraintSize(..)
101122 ) where
102123
124+import Control.Monad (join)
103125import Data.Aeson (FromJSON, ToJSON, genericParseJSON,
104126 genericToJSON, object, parseJSON, toJSON,
105127 (.!=), (.:), (.:?), (.=))
@@ -107,6 +129,7 @@ import qualified Data.Aeson as JSON
107129import Data.Aeson.Types (defaultOptions, fieldLabelModifier)
108130import Data.Char (isAlphaNum, toUpper)
109131import qualified Data.HashMap.Strict as HM
132+import Data.Maybe (maybeToList, catMaybes)
110133import Data.Monoid ((<>))
111134import Data.Scientific (floatingOrInteger)
112135import Data.Text (Text)
@@ -114,6 +137,7 @@ import qualified Data.Text as T
114137import Data.Time.Clock (UTCTime)
115138import qualified Data.Vector as V
116139import GHC.Generics (Generic)
140+import Numeric (readDec)
117141import Prelude hiding (all, tail)
118142import Text.Read (readMaybe)
119143
@@ -139,6 +163,11 @@ data Endpoint =
139163 | DeleteImageEndpoint ImageDeleteOpts ImageID
140164 | CreateNetworkEndpoint CreateNetworkOpts
141165 | RemoveNetworkEndpoint NetworkID
166+ | ListNetworksEndpoint [NetworkFilter]
167+ | InspectNetworkEndpoint NetworkID
168+ | ConnectNetworkEndpoint NetworkID ConnectConfig
169+ | DisconnectNetworkEndpoint NetworkID DisconnectConfig
170+ | PruneNetworksEndpoint PruneFilter
142171 deriving (Eq, Show)
143172
144173-- | We should newtype this
@@ -858,6 +887,242 @@ instance ToJSON CreateNetworkOpts where
858887 , "EnableIPv6" .= createNetworkEnableIPv6 opts
859888 ]
860889
890+data NetworkScope = LocalScope | GlobalScope | SwarmScope deriving (Eq, Show)
891+
892+fromNetworkScope :: NetworkScope -> Text
893+fromNetworkScope LocalScope = "local"
894+fromNetworkScope GlobalScope = "global"
895+fromNetworkScope SwarmScope = "swarm"
896+
897+instance ToJSON NetworkScope where
898+ toJSON = JSON.String . fromNetworkScope
899+
900+instance FromJSON NetworkScope where
901+ parseJSON "local" = return LocalScope
902+ parseJSON "global" = return GlobalScope
903+ parseJSON "swarm" = return SwarmScope
904+ parseJSON _ = fail "Failed to parse NetworkScope"
905+
906+data CIDR = CIDR Text Int deriving (Eq, Show)
907+
908+instance FromJSON CIDR where
909+ parseJSON (JSON.String t) = case T.splitOn "/" t of
910+ [a,p] -> CIDR a <$> parsePrefixLen p
911+ _ -> fail "Failed to parse CIDR"
912+ where
913+ parsePrefixLen p = case readDec (T.unpack p) of
914+ (n,""):_ -> return n
915+ _ -> fail "Failed to parse CIDR prefix length"
916+ parseJSON _ = fail "Failed to parse CIDR"
917+
918+data IPAMDriver = DefaultIPAMDriver | NamedIPAMDriver Text deriving (Eq, Show)
919+
920+instance FromJSON IPAMDriver where
921+ parseJSON (JSON.String "default") = return DefaultIPAMDriver
922+ parseJSON (JSON.String t) = return $ NamedIPAMDriver t
923+ parseJSON _ = fail "IPAMDriver is not a string"
924+
925+data IPAMConfig = IPAMConfig
926+ { ipamConfigSubnet :: CIDR
927+ , ipamConfigIPRange :: Maybe CIDR
928+ , ipamSubnetGateway :: Maybe Text
929+ , ipamConfigAuxAddress :: Maybe Text
930+ } deriving (Eq, Show)
931+
932+instance FromJSON IPAMConfig where
933+ parseJSON (JSON.Object o) = IPAMConfig
934+ <$> o .: "Subnet"
935+ <*> o .:? "IPRange"
936+ <*> o .:? "Gateway"
937+ <*> o .:? "AuxAddress"
938+ parseJSON _ = fail "IPAMConfig is not an object"
939+
940+data IPAM = IPAM
941+ { ipamDriver :: IPAMDriver
942+ , ipamConfig :: [IPAMConfig]
943+ , ipamOptions :: HM.HashMap Text Text
944+ } deriving (Eq, Show)
945+
946+instance FromJSON IPAM where
947+ parseJSON (JSON.Object o) = IPAM
948+ <$> o .:? "Driver" .!= DefaultIPAMDriver
949+ <*> o .:? "Config" .!= []
950+ <*> o .:? "Options" .!= HM.empty
951+ parseJSON _ = fail "IPAM is not an object"
952+
953+data NetworkContainer = NetworkContainer
954+ { networkContainerName :: ContainerName
955+ , networkContainerEndpointID :: Text
956+ , networkContainerMacAddress :: Text
957+ , networkContainerIPv4Address :: Text
958+ , networkContainerIPv6Address :: Text
959+ } deriving (Eq, Show)
960+
961+instance FromJSON NetworkContainer where
962+ parseJSON (JSON.Object o) = NetworkContainer
963+ <$> o .: "Name"
964+ <*> o .: "EndpointID"
965+ <*> o .: "MacAddress"
966+ <*> o .: "IPv4Address"
967+ <*> o .: "IPv6Address"
968+ parseJSON _ = fail "NetworkContainer is not an object"
969+
970+data NetworkDetails = NetworkDetails
971+ { networkDetailsName :: Text
972+ , networkDetailsID :: NetworkID
973+ , networkDetailsCreated :: UTCTime
974+ , networkDetailsScope :: NetworkScope
975+ , networkDetailsDriver :: NetworkMode
976+ , networkDetailsEnableIPv6 :: Bool
977+ , networkDetailsInternal :: Bool
978+ , networkDetailsAttachable :: Bool
979+ , networkDetailsIngress :: Bool
980+ , networkDetailsIPAM :: IPAM
981+ , networkDetailsOptions :: HM.HashMap Text Text
982+ , networkDetailsLabels :: HM.HashMap Text Text
983+ , networkDetailsContainers :: HM.HashMap Text NetworkContainer
984+ } deriving (Eq, Show)
985+
986+instance FromJSON NetworkDetails where
987+ parseJSON v@(JSON.Object o) = NetworkDetails
988+ <$> o .: "Name"
989+ <*> parseJSON v
990+ <*> o .: "Created"
991+ <*> o .: "Scope"
992+ <*> o .: "Driver"
993+ <*> o .: "EnableIPv6"
994+ <*> o .: "Internal"
995+ <*> o .: "Attachable"
996+ <*> o .: "Ingress"
997+ <*> o .: "IPAM"
998+ <*> o .:? "Options" .!= HM.empty
999+ <*> o .:? "Labels" .!= HM.empty
1000+ <*> o .:? "Containers" .!= HM.empty
1001+ parseJSON _ = fail "NetworkDetails is not an object"
1002+
1003+data NetworkType = BuiltinNetwork | CustomNetwork deriving (Eq, Show)
1004+
1005+fromNetworkType :: NetworkType -> Text
1006+fromNetworkType BuiltinNetwork = "builtin"
1007+fromNetworkType CustomNetwork = "custom"
1008+
1009+type NetworkName = Text
1010+
1011+instance ToJSON NetworkType where
1012+ toJSON = JSON.String . fromNetworkType
1013+
1014+instance FromJSON NetworkType where
1015+ parseJSON (JSON.String "builtin") = return BuiltinNetwork
1016+ parseJSON (JSON.String "custom") = return CustomNetwork
1017+ parseJSON _ = fail "Failed to parse NetworkType"
1018+
1019+data NetworkFilter
1020+ = NetworkFilterName Text
1021+ | NetworkFilterID NetworkID
1022+ | NetworkFilterLabel Text
1023+ | NetworkFilterDriver NetworkMode
1024+ | NetworkFilterScope NetworkScope
1025+ | NetworkFilterType NetworkType
1026+ deriving (Eq, Show)
1027+
1028+instance {-# OVERLAPPING #-} ToJSON [NetworkFilter] where
1029+ toJSON = object . fmap toKV
1030+ where
1031+ toKV (NetworkFilterName n) = "name" .= [n]
1032+ toKV (NetworkFilterID i) = "id" .= [fromNetworkID i]
1033+ toKV (NetworkFilterLabel l) = "label" .= [l]
1034+ toKV (NetworkFilterDriver d) = "driver" .= [fromNetworkMode d]
1035+ toKV (NetworkFilterScope s) = "scope" .= [fromNetworkScope s]
1036+ toKV (NetworkFilterType t) = "type" .= [fromNetworkType t]
1037+
1038+data ConnectConfig = ConnectConfig
1039+ { connectContainer :: Text
1040+ , connectEndpointConfig :: EndpointSettings
1041+ } deriving (Eq, Show, Generic)
1042+
1043+instance ToJSON ConnectConfig where
1044+ toJSON = genericToJSON defaultOptions { fieldLabelModifier = drop 7 }
1045+
1046+defaultConnectConfig :: Text -> ConnectConfig
1047+defaultConnectConfig = flip ConnectConfig defaultEndpointSettings
1048+
1049+data DisconnectConfig = DisconnectConfig
1050+ { disconnectContainer :: Text
1051+ , disconnectForce :: Bool
1052+ } deriving (Eq, Show, Generic)
1053+
1054+instance ToJSON DisconnectConfig where
1055+ toJSON = genericToJSON defaultOptions { fieldLabelModifier = drop 10 }
1056+
1057+defaultDisconnectConfig :: Text -> DisconnectConfig
1058+defaultDisconnectConfig = flip DisconnectConfig False
1059+
1060+data PruneFilter = PruneFilter
1061+ { pruneFilterUntil :: Maybe Text
1062+ -- ^ Can be a Unix timestamp, a date-formatted timestamp, or a duration,
1063+ -- such as @10m@ or @1h30m@.
1064+ , pruneFilterIncludeLabels :: [(Text, (Maybe Text))]
1065+ , pruneFilterExcludeLabels :: [(Text, (Maybe Text))]
1066+ } deriving (Eq, Show)
1067+
1068+instance ToJSON PruneFilter where
1069+ toJSON pf = object . catMaybes $
1070+ [ "until" .=? maybeToList (pruneFilterUntil pf)
1071+ , "label" .=? (constructLabel <$> pruneFilterIncludeLabels pf)
1072+ , "label!" .=? (constructLabel <$> pruneFilterExcludeLabels pf)
1073+ ]
1074+ where
1075+ _ .=? [] = Nothing
1076+ k .=? l = Just $ k .= l
1077+
1078+constructLabel :: (Text, Maybe Text) -> Text
1079+constructLabel (k, Just v) = T.concat [k, "=", v]
1080+constructLabel (k, Nothing) = k
1081+
1082+defaultPruneFilter :: PruneFilter
1083+defaultPruneFilter = PruneFilter Nothing [] []
1084+
1085+newtype NetworksDeleted = NetworksDeleted [Text]
1086+ deriving (Eq, Show)
1087+
1088+instance FromJSON NetworksDeleted where
1089+ parseJSON (JSON.Object o) =
1090+ NetworksDeleted . join . maybeToList <$> o .: "NetworksDeleted"
1091+ parseJSON _ = fail $ "NetworksDeleted is not an object"
1092+
1093+data EndpointSettings = EndpointSettings
1094+ { endpointIPAMConfig :: IPAMSettings
1095+ , endpointLinks :: [Text]
1096+ , endpointAliases :: [Text]
1097+ , endpointNetworkID :: Maybe Text
1098+ , endpointEndpointID :: Maybe Text
1099+ , endpointGateway :: Maybe Text
1100+ , endpointIPAddress :: Maybe Text
1101+ , endpointIPPrefixLen :: Maybe Int
1102+ , endpointIPv6Gateway :: Maybe Text
1103+ , endpointGlobalIPv6Address :: Maybe Text
1104+ , endpointGlobalIPv6PrefixLen :: Maybe Int
1105+ , endpointMacAddress :: Maybe Text
1106+ } deriving (Eq, Show, Generic)
1107+
1108+instance ToJSON EndpointSettings where
1109+ toJSON = genericToJSON defaultOptions { fieldLabelModifier = drop 8 }
1110+
1111+defaultEndpointSettings :: EndpointSettings
1112+defaultEndpointSettings = EndpointSettings defaultIPAMSettings [] [] Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing
1113+
1114+data IPAMSettings = IPAMSettings
1115+ { ipamIPv4Address :: Maybe Text
1116+ , ipamIPv6Address :: Maybe Text
1117+ , ipamLinkLocalIPs :: [Text]
1118+ } deriving (Eq, Show, Generic)
1119+
1120+instance ToJSON IPAMSettings where
1121+ toJSON = genericToJSON defaultOptions { fieldLabelModifier = drop 4 }
1122+
1123+defaultIPAMSettings :: IPAMSettings
1124+defaultIPAMSettings = IPAMSettings Nothing Nothing []
1125+
8611126-- TOOD: Add support for SELinux Volume labels (eg. "ro,z" or "ro/Z")
8621127-- | Set permissions on volumes that you mount in the container.
8631128data VolumePermission = ReadWrite | ReadOnly deriving (Eq, Show, Generic)
@@ -1027,11 +1292,14 @@ instance FromJSON NetworkMode where
10271292 parseJSON (JSON.String n) = return $ NetworkNamed n
10281293 parseJSON _ = fail "Unknown NetworkMode"
10291294
1295+fromNetworkMode :: NetworkMode -> Text
1296+fromNetworkMode NetworkBridge = "bridge"
1297+fromNetworkMode NetworkHost = "host"
1298+fromNetworkMode NetworkDisabled = "none"
1299+fromNetworkMode (NetworkNamed n) = n
1300+
10301301instance ToJSON NetworkMode where
1031− toJSON NetworkBridge = JSON.String "bridge"
1032− toJSON NetworkHost = JSON.String "host"
1033− toJSON NetworkDisabled = JSON.String "none"
1034− toJSON (NetworkNamed n) = JSON.String n
1302+ toJSON = JSON.String . fromNetworkMode
10351303
10361304newtype NetworkID = NetworkID Text
10371305 deriving (Eq, Show)