@@ -11,11 +11,7 @@ module Docker.Client.Types (
1111 , ImageID
1212 , fromImageID
1313 , toImageID
14− , NetworkID
15− , fromNetworkID
16− , toNetworkID
1714 , Timeout(..)
18− , StatusCode(..)
1915 , Signal(..)
2016 , ContainerDetails(..)
2117 , DockerClientOpts(..)
@@ -26,30 +22,23 @@ module Docker.Client.Types (
2622 , ContainerPortInfo(..)
2723 , Container(..)
2824 , ContainerState(..)
29− , State(..)
25+ , Status(..)
3026 , Digest
3127 , Label(..)
3228 , Tag
3329 , Image(..)
34− , Entrypoint(..)
3530 , dropImagePrefix
3631 , CreateOpts(..)
37− , BuildOpts(..)
38− , defaultBuildOpts
3932 , defaultCreateOpts
4033 , DetachKeys(..)
4134 , StartOpts(..)
4235 , defaultStartOpts
43− , ContainerDeleteOpts(..)
44− , defaultContainerDeleteOpts
45− , ImageDeleteOpts(..)
46− , defaultImageDeleteOpts
36+ , DeleteOpts(..)
37+ , defaultDeleteOpts
4738 , Timestamp
4839 , TailLogOpt(..)
4940 , LogOpts(..)
5041 , defaultLogOpts
51− , CreateNetworkOpts(..)
52− , defaultCreateNetworkOpts
5342 , VolumePermission(..)
5443 , Bind(..)
5544 , Volume(..)
@@ -58,12 +47,12 @@ module Docker.Client.Types (
5847 , VolumeFrom(..)
5948 , Link(..)
6049 , LogDriverType(..)
61− , LogDriverOption(..)
50+ , LogDriverOptions(..)
6251 , LogDriverConfig(..)
6352 , NetworkMode(..)
6453 , PortType(..)
6554-- , NetworkInterface(..)
66− , Network(..)
55+ , Networks(..)
6756 , NetworkSettings(..)
6857 , NetworkOptions(..)
6958 , Mount(..)
@@ -75,8 +64,6 @@ module Docker.Client.Types (
7564 , UTSMode(..)
7665 , HostConfig(..)
7766 , defaultHostConfig
78− , NetworkingConfig(..)
79− , EndpointConfig(..)
8067 , Ulimit(..)
8168 , ContainerResources(..)
8269 , defaultContainerResources
@@ -96,8 +83,6 @@ module Docker.Client.Types (
9683 , addLink
9784 , addVolume
9885 , addVolumeFrom
99− , MemoryConstraint(..)
100− , MemoryConstraintSize(..)
10186 ) where
10287
10388import Data.Aeson (FromJSON, ToJSON, genericParseJSON,
@@ -107,12 +92,11 @@ import qualified Data.Aeson as JSON
10792import Data.Aeson.Types (defaultOptions, fieldLabelModifier)
10893import Data.Char (isAlphaNum, toUpper)
10994import qualified Data.HashMap.Strict as HM
95+import qualified Data.Map as M
11096import Data.Monoid ((<>))
111−import Data.Scientific (floatingOrInteger)
11297import Data.Text (Text)
11398import qualified Data.Text as T
11499import Data.Time.Clock (UTCTime)
115−import qualified Data.Vector as V
116100import GHC.Generics (Generic)
117101import Prelude hiding (all, tail)
118102import Text.Read (readMaybe)
@@ -125,20 +109,14 @@ data Endpoint =
125109 | CreateContainerEndpoint CreateOpts (Maybe ContainerName)
126110 | StartContainerEndpoint StartOpts ContainerID
127111 | StopContainerEndpoint Timeout ContainerID
128− | WaitContainerEndpoint ContainerID
129112 | KillContainerEndpoint Signal ContainerID
130113 | RestartContainerEndpoint Timeout ContainerID
131114 | PauseContainerEndpoint ContainerID
132115 | UnpauseContainerEndpoint ContainerID
133116 | ContainerLogsEndpoint LogOpts Bool ContainerID -- ^ Second argument (Bool) is whether to follow which is currently hardcoded to False.
134117 -- See note in 'Docker.Client.Api.getContainerLogs' for explanation why.
135− | DeleteContainerEndpoint ContainerDeleteOpts ContainerID
118+ | DeleteContainerEndpoint DeleteOpts ContainerID
136119 | InspectContainerEndpoint ContainerID
137− | BuildImageEndpoint BuildOpts FilePath
138− | CreateImageEndpoint T.Text Tag (Maybe T.Text) -- ^ Either pull an image from docker hub or imports an image from a tarball (or URL)
139− | DeleteImageEndpoint ImageDeleteOpts ImageID
140− | CreateNetworkEndpoint CreateNetworkOpts
141− | RemoveNetworkEndpoint NetworkID
142120 deriving (Eq, Show)
143121
144122-- | We should newtype this
@@ -185,20 +163,6 @@ toImageID t =
185163-- | Timeout used for stopping a container. DefaultTimeout is 10 seconds.
186164data Timeout = Timeout Integer | DefaultTimeout deriving (Eq, Show)
187165
188−data StatusCode = StatusCode Int
189−
190−instance ToJSON StatusCode where
191− toJSON (StatusCode c) = object ["StatusCode" .= c]
192−
193−instance FromJSON StatusCode where
194− parseJSON (JSON.Object o) = do
195− c <- o .: "StatusCode"
196− if c >= 0 && c <= 255 then
197− return (StatusCode c)
198− else
199− fail "Unknown exit code"
200− parseJSON _ = fail "Unknown exit code"
201−
202166-- TODO: Add more Signals or use an existing lib
203167-- | Signal used for sending to the process running in the container.
204168-- The default signal is SIGTERM.
@@ -213,16 +177,16 @@ data Signal = SIGHUP
213177
214178instance FromJSON Signal where
215179 parseJSON (JSON.String "SIGTERM") = return SIGTERM
216− parseJSON (JSON.String "SIGHUP") = return SIGHUP -- Note: Guessing on the string values for these.
217− parseJSON (JSON.String "SIGINT") = return SIGINT
180+ parseJSON (JSON.String "SIGHUP") = return SIGHUP -- Note: Guessing on the string values for these.
181+ parseJSON (JSON.String "SIGINT") = return SIGINT
218182 parseJSON (JSON.String "SIGQUIT") = return SIGQUIT
219183 parseJSON (JSON.String "SIGSTOP") = return SIGSTOP
220184 parseJSON (JSON.String "SIGUSR1") = return SIGUSR1
221− parseJSON _ = fail "Unknown Signal"
185+ parseJSON _ = fail "Unknown Signal"
222186
223187instance ToJSON Signal where
224− toJSON SIGHUP = "SIGHUP"
225− toJSON SIGINT = "SIGINT"
188+ toJSON SIGHUP = "SIGHUP"
189+ toJSON SIGINT = "SIGINT"
226190 toJSON SIGQUIT = "SIGQUIT"
227191 toJSON SIGSTOP = "SIGSTOP"
228192 toJSON SIGTERM = "SIGTERM"
@@ -250,7 +214,7 @@ data ContainerDetails = ContainerDetails {
250214 , processLabel :: Text
251215 , resolveConfPath :: FilePath
252216 , restartCount :: Int
253− , containerDetailsState :: ContainerState
217+ , state :: ContainerState
254218 , mounts :: [Mount]
255219 }
256220 deriving (Eq, Show, Generic)
@@ -258,11 +222,11 @@ data ContainerDetails = ContainerDetails {
258222-- | Data type used for parsing the mount information from a container
259223-- list.
260224data Mount = Mount {
261− mountName :: Maybe Text -- this is optional
225+ mountName :: Text
262226 , mountSource :: FilePath
263227 , mountDestination :: FilePath
264− , mountDriver :: Maybe Text
265− -- , mountMode :: Maybe VolumePermission -- apparently this can be null
228+ , mountDriver :: Text
229+ , mountMode :: Maybe VolumePermission -- apparently this can be null
266230 , mountRW :: Bool
267231 , mountPropogation :: Text
268232 }
@@ -270,14 +234,14 @@ data Mount = Mount {
270234
271235instance FromJSON Mount where
272236 parseJSON (JSON.Object o) = do
273− name <- o .:? "Name"
237+ name <- o .: "Name"
274238 src <- o .: "Source"
275239 dest <- o .: "Destination"
276− driver <- o .:? "Driver"
277− -- mode <- o .: "Mode"
240+ driver <- o .: "Driver"
241+ mode <- o .: "Mode"
278242 rw <- o .: "RW"
279243 prop <- o .: "Propagation"
280− return $ Mount name src dest driver rw prop
244+ return $ Mount name src dest driver mode rw prop
281245 parseJSON _ = fail "Mount is not an object"
282246
283247-- | Data type used for parsing the container state from a list of
@@ -293,7 +257,7 @@ data ContainerState = ContainerState {
293257 , restarting :: Bool
294258 , running :: Bool
295259 , startedAt :: UTCTime
296− , state :: State
260+ , status :: Status
297261 }
298262 deriving (Eq, Show, Generic)
299263
@@ -456,17 +420,20 @@ instance FromJSON NetworkOptions where
456420 parseJSON _ = fail "NetworkOptions is not an object"
457421
458422-- TODO: Not sure what this is used for anymore.
459−data Network = Network NetworkMode NetworkOptions
423+newtype Networks = Networks (M.Map NetworkMode NetworkOptions) -- Note: Is it ever possible that there will be duplicates of network modes?
460424 deriving (Eq, Show)
461425
462−instance {-# OVERLAPPING #-} FromJSON [Network] where
463− parseJSON (JSON.Object o) = HM.foldlWithKey' f (return []) o
426+instance FromJSON Networks where
427+ parseJSON (JSON.Object o) = do
428+ Networks <$> HM.foldlWithKey' f (return M.empty) o
429+
464430 where
465431 f accM k' v' = do
466432 acc <- accM
467433 k <- parseJSON $ JSON.String k'
468434 v <- parseJSON v'
469− return $ (Network k v):acc
435+ return $ M.insert k v acc
436+
470437 parseJSON _ = fail "Networks is not an object"
471438
472439-- | Data type reprsenting the various network settings a container can have.
@@ -488,7 +455,7 @@ data NetworkSettings = NetworkSettings {
488455 , networkSettingsIpPrefixLen :: Int
489456 , networkSettingsIpv6Gateway :: Text
490457 , networkSettingsMacAddress :: Text
491− , networkSettingsNetworks :: [Network]
458+ , networkSettingsNetworks :: Networks
492459 }
493460 deriving (Eq, Show)
494461
@@ -523,23 +490,21 @@ data Container = Container
523490 , containerImageId :: ImageID
524491 , containerCommand :: Text
525492 , containerCreatedAt :: Int
526− , containerState :: State
527− , containerStatus :: Maybe Text
493+ , containerStatus :: Status
528494 , containerPorts :: [ContainerPortInfo]
529495 , containerLabels :: [Label]
530− , containerNetworks :: [Network]
496+ , containerNetworks :: Networks
531497 , containerMounts :: [Mount]
532498 } deriving (Show, Eq)
533499
534500instance FromJSON Container where
535− parseJSON o@(JSON.Object v) =
536− Container <$> parseJSON o
501+ parseJSON (JSON.Object v) =
502+ Container <$> (v .: "Id")
537503 <*> (v .: "Names")
538504 <*> (v .: "Image")
539− <*> (v .: "ImageID") -- Doesn't exist anymore
540− <*> (v .: "Command") -- Doesn't exist anymore
505+ <*> (v .: "ImageID")
506+ <*> (v .: "Command")
541507 <*> (v .: "Created")
542− <*> (v .: "State")
543508 <*> (v .: "Status")
544509 <*> (v .: "Ports")
545510 <*> (v .: "Labels")
@@ -549,20 +514,21 @@ instance FromJSON Container where
549514 parseNetworks (JSON.Object v) =
550515 (v .: "Networks") >>= parseJSON
551516 parseNetworks _ = fail "Container NetworkSettings: Not a JSON object."
517+
552518 parseJSON _ = fail "Container: Not a JSON object."
553519
554−-- | Represents the state of the container life cycle.
555−data State = Created | Restarting | Running | Paused | Exited | Dead
520+-- | Represents the status of the container life cycle.
521+data Status = Created | Restarting | Running | Paused | Exited | Dead
556522 deriving (Eq, Show, Generic)
557523
558−instance FromJSON State where
559− parseJSON (JSON.String "running") = return Running
560− parseJSON (JSON.String "created") = return Created -- Note: Guessing on the string values of these.
524+instance FromJSON Status where
525+ parseJSON (JSON.String "running") = return Running
526+ parseJSON (JSON.String "created") = return Created -- Note: Guessing on the string values of these.
561527 parseJSON (JSON.String "restarting") = return Restarting
562− parseJSON (JSON.String "paused") = return Paused
563− parseJSON (JSON.String "exited") = return Exited
564− parseJSON (JSON.String "dead") = return Dead
565− parseJSON s = fail $ "Unknown Status: " ++ show s
528+ parseJSON (JSON.String "paused") = return Paused
529+ parseJSON (JSON.String "exited") = return Exited
530+ parseJSON (JSON.String "dead") = return Dead
531+ parseJSON _ = fail "Unknown Status"
566532
567533-- | Alias for representing a RepoDigest. We could newtype this and add
568534-- some validation.
@@ -574,13 +540,13 @@ data Label = Label Name Value deriving (Eq, Show)
574540
575541-- If there are multiple lables with the same Name in the list
576542-- then the last one wins.
577−instance {-# OVERLAPPING #-} ToJSON [Label] where
543+instance ToJSON [Label] where
578544 toJSON [] = emptyJsonObject
579545 toJSON (l:ls) = toJsonKeyVal (l:ls) key val
580546 where key (Label k _) = T.unpack k
581547 val (Label _ v) = v
582548
583−instance {-# OVERLAPPING #-} FromJSON [Label] where
549+instance FromJSON [Label] where
584550 parseJSON (JSON.Object o) = HM.foldlWithKey' f (return []) o
585551 where f accM k v = do
586552 acc <- accM
@@ -597,8 +563,8 @@ data Image = DockerImage {
597563 imageId :: ImageID
598564 , imageCreated :: Integer
599565 , imageParentId :: Maybe ImageID
600− , imageRepoTags :: [Tag]
601− , imageRepoDigests :: [Digest]
566+ , imageRepoTags :: Maybe [Tag]
567+ , imageRepoDigests :: Maybe [Digest]
602568 , imageSize :: Integer
603569 , imageVirtualSize :: Integer
604570 , imageLabels :: [Label]
@@ -615,54 +581,30 @@ instance FromJSON Image where
615581 imageId <- o .: "Id"
616582 imageCreated <- o .: "Created"
617583 imageParentId <- o .:? "ParentId"
618− imageRepoTags <- o .:? "RepoTags" .!= []
619− imageRepoDigests <- o .:? "RepoDigests" .!= []
584+ imageRepoTags <- o .:? "RepoTags"
585+ imageRepoDigests <- o .:? "RepoDigests"
620586 imageSize <- o .: "Size"
621587 imageVirtualSize <- o .: "VirtualSize"
622− imageLabels <- o .:? "Labels" .!= []
588+ imageLabels <- case HM.lookup "Labels" o of
589+ Just ls -> parseJSON ls
590+ Nothing -> return []
623591 return $ DockerImage imageId imageCreated imageParentId imageRepoTags imageRepoDigests imageSize imageVirtualSize imageLabels
624592 parseJSON _ = fail "Failed to parse DockerImage."
625593
626−-- | Alias for Aliases.
627−type Alias = Text
628−
629−-- | EndpointsConfig is container configuration for a specific network
630−newtype EndpointConfig = EndpointConfig [Alias] deriving (Eq, Show)
631−
632−instance ToJSON EndpointConfig where
633− toJSON (EndpointConfig aliases) = JSON.object
634− [ "Aliases" .= aliases
635− ]
636−
637−-- | Alias for endpoint name
638−type EndpointName = Text
639−
640−-- | Data type for the NetworkingConfig section of the container settings
641−newtype NetworkingConfig = NetworkingConfig
642− { endpointsConfig :: HM.HashMap EndpointName EndpointConfig
643− } deriving (Eq, Show)
644−
645−instance ToJSON NetworkingConfig where
646− toJSON (NetworkingConfig endpointsConfig) = JSON.object
647− [ "EndpointsConfig" .= endpointsConfig
648− ]
649594
650595-- | Options used for creating a Container.
651596data CreateOpts = CreateOpts {
652− containerConfig :: ContainerConfig
653− , hostConfig :: HostConfig
654− , networkingConfig :: NetworkingConfig
597+ containerConfig :: ContainerConfig
598+ , hostConfig :: HostConfig
655599 } deriving (Eq, Show)
656600
657601instance ToJSON CreateOpts where
658− toJSON (CreateOpts cc hc nc) = do
602+ toJSON (CreateOpts cc hc) = do
659603 let ccJSON = toJSON cc
660604 let hcJSON = toJSON hc
661605 case ccJSON of
662606 JSON.Object (o :: HM.HashMap T.Text JSON.Value) -> do
663− let o1 = HM.insert "HostConfig" hcJSON o
664− let o2 = HM.insert "NetworkingConfig" (toJSON nc) o1
665− JSON.Object o2
607+ JSON.Object $ HM.insert "HostConfig" hcJSON o
666608 _ -> error "ContainerConfig is not an object." -- This should never happen.
667609
668610-- | Container configuration used for creating a container with sensible
@@ -684,7 +626,7 @@ defaultContainerConfig imageName = ContainerConfig {
684626 , cmd=[]
685627 , volumes=[]
686628 , workingDir=Nothing
687− , entrypoint=Entrypoint []
629+ , entrypoint=Nothing
688630 , networkDisabled=Nothing
689631 , macAddress=Nothing
690632 , labels=[]
@@ -696,8 +638,8 @@ defaultHostConfig :: HostConfig
696638defaultHostConfig = HostConfig {
697639 binds=[]
698640 , containerIDFile=Nothing
699− , logConfig=LogDriverConfig JsonFile []
700− , networkMode=NetworkBridge
641+ , logConfig=LogDriverConfig JsonFile Nothing
642+ , networkMode=Bridge
701643 , portBindings=[]
702644 , restartPolicy=RestartOff
703645 , volumeDriver=Nothing
@@ -737,18 +679,14 @@ defaultContainerResources = ContainerResources {
737679 , memory=Nothing
738680 , memoryReservation=Nothing
739681 , memorySwap=Nothing
740− , oomKillDisable=Just False
682+ , oomKillDisable=False
741683 , ulimits=[]
742684 }
743685
744686-- | Default create options when creating a container. You only need to
745687-- specify an image name and the rest is all sensible defaults.
746688defaultCreateOpts :: T.Text -> CreateOpts
747−defaultCreateOpts imageName = CreateOpts
748− { containerConfig = defaultContainerConfig imageName
749− , hostConfig = defaultHostConfig
750− , networkingConfig = NetworkingConfig HM.empty
751− }
689+defaultCreateOpts imageName = CreateOpts { containerConfig = defaultContainerConfig imageName, hostConfig = defaultHostConfig }
752690
753691-- | Override the key sequence for detaching a container.
754692-- Format is a single character [a-Z] or ctrl-<value> where <value> is one of: a-z, @, ^, [, , or _.
@@ -762,45 +700,16 @@ defaultStartOpts :: StartOpts
762700defaultStartOpts = StartOpts { detachKeys = DefaultDetachKey }
763701
764702-- | Options for deleting a container.
765−data ContainerDeleteOpts = ContainerDeleteOpts {
703+data DeleteOpts = DeleteOpts {
766704 deleteVolumes :: Bool -- ^ Automatically cleanup volumes that the container created as well.
767705 , force :: Bool -- ^ If the container is still running force deletion anyway.
768706 } deriving (Eq, Show)
769707
770−-- TODO: Add support for container build constraints
771−-- | Options for when building images from a Dockerfile
772−data BuildOpts = BuildOpts {
773− buildImageName :: Text -- ^ Image name in the form of name:tag; ie. myimage:latest.:w
774− , buildDockerfileName :: Text -- ^ Name of dockerfile (default: Dockerfile)
775− , buildQuiet :: Bool
776− , buildNoCache :: Bool -- ^ Do not use cache when building the image.
777− , buildRemoveItermediate :: Bool -- ^ Remove intermediate containers after a successful build (default true).
778− , buildForceRemoveIntermediate :: Bool -- ^ Always remove intermediate containers.
779− , buildPullParent :: Bool -- ^ Always attempt to pull a newer version of the *parent* image (ie. FROM debian:jessie).
780− } deriving (Eq, Show)
781−
782−defaultBuildOpts :: Text -> BuildOpts
783−defaultBuildOpts nameTag = BuildOpts { buildImageName = nameTag
784− , buildDockerfileName = "Dockerfile"
785− , buildQuiet = False
786− , buildNoCache = False
787− , buildRemoveItermediate = True
788− , buildForceRemoveIntermediate = False
789− , buildPullParent = False
790− }
791−
792708-- | Default options for deleting a container. Most of the time we DON'T
793709-- want to delete the container's volumes or force delete it if it's
794710-- running.
795−defaultContainerDeleteOpts :: ContainerDeleteOpts
796−defaultContainerDeleteOpts = ContainerDeleteOpts { deleteVolumes = False, force = False }
797−
798−-- | Image delete opts
799−data ImageDeleteOpts = ImageDeleteOpts deriving (Eq, Show)
800−
801−-- | Sane image deletion defaults
802−defaultImageDeleteOpts :: ImageDeleteOpts
803−defaultImageDeleteOpts = ImageDeleteOpts
711+defaultDeleteOpts :: DeleteOpts
712+defaultDeleteOpts = DeleteOpts { deleteVolumes = False, force = False }
804713
805714-- | Timestamp alias.
806715type Timestamp = Integer
@@ -828,50 +737,18 @@ defaultLogOpts = LogOpts { stdout = True
828737 , tail = All
829738 }
830739
831−-- | Options for creating a network
832−data CreateNetworkOpts = CreateNetworkOpts
833− { createNetworkName :: Text -- ^ The network's name
834− , createNetworkCheckDuplicate :: Bool -- ^ Check for networks with duplicate names.
835− , createNetworkDriver :: Text -- ^ Name of the network driver plugin to use.
836− , createNetworkInternal :: Bool -- ^ Restrict external access to the network.
837− , createNetworkEnableIPv6 :: Bool -- ^ Enable IPv6 on the network.
838− } deriving (Eq, Show)
839−
840−-- | Sensible defalut for create network options
841−defaultCreateNetworkOpts :: Text -> CreateNetworkOpts
842−defaultCreateNetworkOpts name =
843− CreateNetworkOpts
844− { createNetworkName = name
845− , createNetworkCheckDuplicate = False
846− , createNetworkDriver = "bridge"
847− , createNetworkInternal = True
848− , createNetworkEnableIPv6 = False
849− }
850−
851−instance ToJSON CreateNetworkOpts where
852− toJSON opts =
853− object
854− [ "Name" .= createNetworkName opts
855− , "CheckDuplicate" .= createNetworkCheckDuplicate opts
856− , "Driver" .= createNetworkDriver opts
857− , "Internal" .= createNetworkInternal opts
858− , "EnableIPv6" .= createNetworkEnableIPv6 opts
859− ]
860−
861740-- TOOD: Add support for SELinux Volume labels (eg. "ro,z" or "ro/Z")
862741-- | Set permissions on volumes that you mount in the container.
863742data VolumePermission = ReadWrite | ReadOnly deriving (Eq, Show, Generic)
864743
865744instance ToJSON VolumePermission where
866745 toJSON ReadWrite = "rw"
867− toJSON ReadOnly = "ro"
746+ toJSON ReadOnly = "ro"
868747
869748instance FromJSON VolumePermission where
870749 parseJSON "rw" = return ReadWrite
871750 parseJSON "ro" = return ReadOnly
872− parseJSON "RW" = return ReadWrite
873− parseJSON "RO" = return ReadOnly
874− parseJSON _ = fail "Failed to parse VolumePermission"
751+ parseJSON _ = fail "Failed to parse VolumePermission"
875752
876753-- | Used for marking a directory in the container as "exposed" hence
877754-- taking it outside of the COW filesystem and making it mountable
@@ -886,15 +763,15 @@ instance FromJSON VolumePermission where
886763-- @
887764newtype Volume = Volume FilePath deriving (Eq, Show)
888765
889−instance {-# OVERLAPPING #-} ToJSON [Volume] where
766+instance ToJSON [Volume] where
890767 toJSON [] = emptyJsonObject
891768 toJSON (v:vs) = toJsonKey (v:vs) getKey
892769 where getKey (Volume v) = v
893770
894−instance {-# OVERLAPPING #-} FromJSON [Volume] where
771+instance FromJSON [Volume] where
895772 parseJSON (JSON.Object o) = return $ map (Volume . T.unpack) $ HM.keys o
896− parseJSON (JSON.Null) = return []
897− parseJSON _ = fail "Volume is not an object"
773+ parseJSON (JSON.Null) = return []
774+ parseJSON _ = fail "Volume is not an object"
898775
899776
900777data Bind = Bind { hostSrc :: Text
@@ -904,10 +781,10 @@ data Bind = Bind { hostSrc :: Text
904781
905782instance FromJSON Bind where
906783 parseJSON (JSON.String t) = case T.split (== ':') t of
907− [src, dest] -> return $ Bind src dest Nothing
784+ [src, dest] -> return $ Bind src dest Nothing
908785 [src, dest, "rw"] -> return $ Bind src dest $ Just ReadWrite
909786 [src, dest, "ro"] -> return $ Bind src dest $ Just ReadOnly
910− _ -> fail "Could not parse Bind"
787+ _ -> fail "Could not parse Bind"
911788 parseJSON _ = fail "Bind is not a string"
912789
913790data Device = Device {
@@ -930,15 +807,15 @@ data VolumeFrom = VolumeFrom ContainerName (Maybe VolumePermission) deriving (Eq
930807
931808instance FromJSON VolumeFrom where
932809 parseJSON (JSON.String t) = case T.split (== ':') t of
933− [vol] -> return $ VolumeFrom vol Nothing
810+ [vol] -> return $ VolumeFrom vol Nothing
934811 [vol, "rw"] -> return $ VolumeFrom vol $ Just ReadWrite
935812 [vol, "ro"] -> return $ VolumeFrom vol $ Just ReadOnly
936− _ -> fail "Could not parse VolumeFrom"
813+ _ -> fail "Could not parse VolumeFrom"
937814 parseJSON _ = fail "VolumeFrom is not a string"
938815
939816instance ToJSON VolumeFrom where
940817 toJSON (VolumeFrom n p) = case p of
941− Nothing -> toJSON $ n <> ":" <> "rw"
818+ Nothing -> toJSON $ n <> ":" <> "rw"
942819 Just per -> toJSON $ n <> ":" <> (T.pack $ show per)
943820
944821instance ToJSON Bind where
@@ -950,63 +827,63 @@ data Link = Link Text (Maybe Text) deriving (Eq, Show)
950827
951828instance FromJSON Link where
952829 parseJSON (JSON.String t) = case T.split (== ':') t of
953− [f] -> return $ Link f Nothing
830+ [f] -> return $ Link f Nothing
954831 [f,s] -> return $ Link f $ Just s
955− _ -> fail "Could not parse Link"
832+ _ -> fail "Could not parse Link"
956833 parseJSON _ = fail "Link is not a string"
957834
958835instance ToJSON Link where
959836 toJSON (Link n1 n2) = toJSON $ case n2 of
960837 Nothing -> T.concat[n1, ":", n1] -- use same name in container
961− Just n -> T.concat[n1, ":", n] -- used specified name in container
838+ Just n -> T.concat[n1, ":", n] -- used specified name in container
962839
963840-- { "Type": "<driver_name>", "Config": {"key1": "val1"} }
964841data LogDriverType = JsonFile | Syslog | Journald | Gelf | Fluentd | AwsLogs | Splunk | Etwlogs | LoggingDisabled deriving (Eq, Show)
965842
966843instance FromJSON LogDriverType where
967844 parseJSON (JSON.String "json-file") = return JsonFile
968− parseJSON (JSON.String "syslog") = return Syslog
969− parseJSON (JSON.String "journald") = return Journald
970− parseJSON (JSON.String "gelf") = return Gelf
971− parseJSON (JSON.String "fluentd") = return Fluentd
972− parseJSON (JSON.String "awslogs") = return AwsLogs
973− parseJSON (JSON.String "splunk") = return Splunk
974− parseJSON (JSON.String "etwlogs") = return Etwlogs
975− parseJSON (JSON.String "none") = return LoggingDisabled
976− parseJSON _ = fail "Unknown LogDriverType"
845+ parseJSON (JSON.String "syslog") = return Syslog
846+ parseJSON (JSON.String "journald") = return Journald
847+ parseJSON (JSON.String "gelf") = return Gelf
848+ parseJSON (JSON.String "fluentd") = return Fluentd
849+ parseJSON (JSON.String "awslogs") = return AwsLogs
850+ parseJSON (JSON.String "splunk") = return Splunk
851+ parseJSON (JSON.String "etwlogs") = return Etwlogs
852+ parseJSON (JSON.String "none") = return LoggingDisabled
853+ parseJSON _ = fail "Unknown LogDriverType"
977854
978855instance ToJSON LogDriverType where
979− toJSON JsonFile = JSON.String "json-file"
980− toJSON Syslog = JSON.String "syslog"
981− toJSON Journald = JSON.String "journald"
982− toJSON Gelf = JSON.String "gelf"
983− toJSON Fluentd = JSON.String "fluentd"
984− toJSON AwsLogs = JSON.String "awslogs"
985− toJSON Splunk = JSON.String "splunk"
986− toJSON Etwlogs = JSON.String "etwlogs"
856+ toJSON JsonFile = JSON.String "json-file"
857+ toJSON Syslog = JSON.String "syslog"
858+ toJSON Journald = JSON.String "journald"
859+ toJSON Gelf = JSON.String "gelf"
860+ toJSON Fluentd = JSON.String "fluentd"
861+ toJSON AwsLogs = JSON.String "awslogs"
862+ toJSON Splunk = JSON.String "splunk"
863+ toJSON Etwlogs = JSON.String "etwlogs"
987864 toJSON LoggingDisabled = JSON.String "none"
988865
989−data LogDriverOption = LogDriverOption Name Value deriving (Eq, Show)
866+newtype LogDriverOptions = LogDriverOptions (M.Map Text Text) deriving (Eq, Show)
990867
991−instance {-# OVERLAPPING #-} ToJSON [LogDriverOption] where
992− toJSON [] = emptyJsonObject
993− toJSON (o:os) = toJsonKeyVal (o:os) key val
994− where key (LogDriverOption n _) = T.unpack n
995− val (LogDriverOption _ v) = v
868+instance FromJSON LogDriverOptions where
869+ parseJSON (JSON.Object o) = do
870+ LogDriverOptions <$> HM.foldlWithKey' f (return M.empty) o
996871
997−instance {-# OVERLAPPING #-} FromJSON [LogDriverOption] where
998− parseJSON (JSON.Object o) = HM.foldlWithKey' f (return []) o
999− where f accM k v = do
872+ where
873+ f accM k (JSON.String v) = do
1000874 acc <- accM
1001− value <- parseJSON v
1002− return $ (LogDriverOption k value):acc
1003− parseJSON JSON.Null = return []
1004− parseJSON _ = fail "Failed to parse LogDriverOptions"
875+ return $ M.insert k v acc
876+ f _ _ _ = fail "Value of LogDriverOptions is not a string"
877+
878+ parseJSON JSON.Null = return $ LogDriverOptions M.empty
879+ parseJSON _ = fail "LogDriverOptions is not an object"
1005880
1006−data LogDriverConfig = LogDriverConfig LogDriverType [LogDriverOption] deriving (Eq, Show)
881+instance ToJSON LogDriverOptions where
882+ toJSON (LogDriverOptions mp) = toJSON mp
883+
884+data LogDriverConfig = LogDriverConfig LogDriverType (Maybe LogDriverOptions) deriving (Eq, Show)
1007885
1008886instance ToJSON LogDriverConfig where
1009− toJSON (LogDriverConfig driverType []) = object ["Type" .= driverType]
1010887 toJSON (LogDriverConfig driverType driverOptions) = object ["Type" .= driverType, "Config" .= driverOptions]
1011888
1012889instance FromJSON LogDriverConfig where
@@ -1017,41 +894,19 @@ instance FromJSON LogDriverConfig where
1017894 parseJSON _ = fail "LogDriverConfig is not an object"
1018895
1019896-- TODO: Add container:<name|id> mode
1020−data NetworkMode = NetworkBridge | NetworkHost | NetworkDisabled | NetworkNamed Text
897+data NetworkMode = Bridge | Host | NetworkDisabled
1021898 deriving (Eq, Show, Ord)
1022899
1023900instance FromJSON NetworkMode where
1024− parseJSON (JSON.String "bridge") = return NetworkBridge
1025− parseJSON (JSON.String "host") = return NetworkHost -- Note: Guessing on these.
1026− parseJSON (JSON.String "none") = return NetworkDisabled
1027− parseJSON (JSON.String n) = return $ NetworkNamed n
1028− parseJSON _ = fail "Unknown NetworkMode"
901+ parseJSON (JSON.String "bridge") = return Bridge
902+ parseJSON (JSON.String "host") = return Host -- Note: Guessing on these.
903+ parseJSON (JSON.String "none") = return NetworkDisabled
904+ parseJSON _ = fail "Unknown NetworkMode"
1029905
1030906instance ToJSON NetworkMode where
1031− toJSON NetworkBridge = JSON.String "bridge"
1032− toJSON NetworkHost = JSON.String "host"
907+ toJSON Bridge = JSON.String "bridge"
908+ toJSON Host = JSON.String "host"
1033909 toJSON NetworkDisabled = JSON.String "none"
1034− toJSON (NetworkNamed n) = JSON.String n
1035−
1036−newtype NetworkID = NetworkID Text
1037− deriving (Eq, Show)
1038−
1039−-- | Used for extracting the id of the container from the newtype
1040−fromNetworkID :: NetworkID -> Text
1041−fromNetworkID (NetworkID t) = t
1042−
1043−-- | Used for parsing a Text value into a NetworkID.
1044−toNetworkID :: Text -> Maybe NetworkID
1045−toNetworkID t = Just $ NetworkID t
1046−
1047−instance FromJSON NetworkID where
1048− parseJSON (JSON.Object o) = do
1049− nid <- o .: "Id"
1050− return $ NetworkID nid
1051− parseJSON _ = fail "NetworkID is not an object."
1052−
1053−instance ToJSON NetworkID where
1054− toJSON (NetworkID nid) = object ["Id" .= nid]
1055910
1056911data PortType = TCP | UDP deriving (Eq, Generic, Read, Ord)
1057912
@@ -1067,7 +922,7 @@ instance FromJSON PortType where
1067922 parseJSON val = case val of
1068923 "tcp" -> return TCP
1069924 "udp" -> return UDP
1070− _ -> fail "PortType: Invalid port type."
925+ _ -> fail "PortType: Invalid port type."
1071926
1072927-- newtype NetworkInterface = NetworkInterface Text deriving (Eq, Show)
1073928--
@@ -1148,7 +1003,7 @@ addExposedPort ep c = c{containerConfig=cc{exposedPorts=oldeps <> [ep]}}
11481003 oldeps = exposedPorts cc
11491004
11501005
1151−instance {-# OVERLAPPING #-} FromJSON [PortBinding] where
1006+instance FromJSON [PortBinding] where
11521007 parseJSON (JSON.Object o) = HM.foldlWithKey' f (return []) o
11531008 where
11541009 f accM k v = case T.split (== '/') k of
@@ -1162,7 +1017,7 @@ instance {-# OVERLAPPING #-} FromJSON [PortBinding] where
11621017 parseJSON (JSON.Null) = return []
11631018 parseJSON _ = fail "PortBindings is not an object"
11641019
1165−instance {-# OVERLAPPING #-} ToJSON [PortBinding] where
1020+instance ToJSON [PortBinding] where
11661021 toJSON [] = emptyJsonObject
11671022 toJSON (p:ps) = toJsonKeyVal (p:ps) key val
11681023 where key p = T.unpack $ portAndType2Text (containerPort p) (portType p)
@@ -1197,7 +1052,7 @@ instance FromJSON RestartPolicy where
11971052 "on-failure" -> do
11981053 retry <- o .: "MaximumRetryCount"
11991054 return $ RestartOnFailure retry
1200− "no" -> return RestartOff
1055+ "off" -> return RestartOff
12011056 _ -> fail "Could not parse RestartPolicy"
12021057 parseJSON _ = fail "RestartPolicy is not an object"
12031058
@@ -1205,7 +1060,7 @@ instance ToJSON RestartPolicy where
12051060 toJSON RestartAlways = object ["Name" .= JSON.String "always"]
12061061 toJSON RestartUnlessStopped = object ["Name" .= JSON.String "unless-stopped"]
12071062 toJSON (RestartOnFailure c) = object ["Name" .= JSON.String "on-failure", "MaximumRetryCount" .= c]
1208− toJSON RestartOff = object ["Name" .= JSON.String "no"]
1063+ toJSON RestartOff = object ["Name" .= JSON.String "off"]
12091064
12101065data Isolation = Default | Process | Hyperv deriving (Eq, Show)
12111066
@@ -1378,22 +1233,6 @@ instance ToJSON DeviceRate where
13781233 , "Rate" .= r
13791234 ]
13801235
1381−data MemoryConstraintSize = B | MB | GB deriving (Eq, Show)
1382−
1383−data MemoryConstraint = MemoryConstraint Integer MemoryConstraintSize deriving (Eq, Show)
1384−
1385−instance ToJSON MemoryConstraint where
1386− toJSON (MemoryConstraint x B) = toJSON x
1387− toJSON (MemoryConstraint x MB) = toJSON $ x * 1024 * 1024
1388− toJSON (MemoryConstraint x GB) = toJSON $ x * 1024 * 1024 * 1024
1389−
1390−instance FromJSON MemoryConstraint where
1391− parseJSON (JSON.Number x) = case (floatingOrInteger x) of
1392− Left (_ :: Double) -> fail "Failed to parse MemoryConstraint"
1393− Right i -> return $ MemoryConstraint i B
1394− -- The docker daemon will always return the number as bytes (integer), regardless of how we set them (using MB or GB)
1395− parseJSON _ = fail "Failed to parse MemoryConstraint"
1396−
13971236data ContainerResources = ContainerResources {
13981237 cpuShares :: Maybe Integer
13991238 -- , cgroupParent :: Text -- 1.24: Missing from inspecting container details... Going to omit for now.
@@ -1409,12 +1248,12 @@ data ContainerResources = ContainerResources {
14091248 , cpusetMems :: Maybe Text
14101249 , devices :: [Device]
14111250 -- , diskQuota :: Integer -- Don't see this ins 1.24.
1412− , kernelMemory :: Maybe MemoryConstraint
1413− , memory :: Maybe MemoryConstraint
1414− , memoryReservation :: Maybe MemoryConstraint
1415− , memorySwap :: Maybe MemoryConstraint
1251+ , kernelMemory :: Maybe Integer
1252+ , memory :: Maybe Integer
1253+ , memoryReservation :: Maybe Integer
1254+ , memorySwap :: Maybe Integer
14161255 -- , memorySwappiness :: Integer -- 1.24: Missing from inspecting container details... Going to omit for now.
1417− , oomKillDisable :: Maybe Bool
1256+ , oomKillDisable :: Bool
14181257 -- , pidsLimit :: Integer -- 1.24: Missing from inspecting container details... Going to omit for now.
14191258 , ulimits :: [Ulimit]
14201259 -- TODO: Missing from 1.24
@@ -1452,7 +1291,7 @@ instance FromJSON EnvVar where
14521291 parseJSON _ = fail "EnvVar is not a string"
14531292
14541293instance ToJSON EnvVar where
1455− toJSON (EnvVar n v) = JSON.String $ n <> T.pack "=" <> v
1294+ toJSON (EnvVar n v) = object [n .= v]
14561295
14571296-- | ExposedPort represents a port (and it's type)
14581297-- that a container should expose to other containers or the host system.
@@ -1473,7 +1312,7 @@ instance ToJSON EnvVar where
14731312-- @
14741313data ExposedPort = ExposedPort Port PortType deriving (Eq, Show)
14751314
1476−instance {-# OVERLAPPING #-} FromJSON [ExposedPort] where
1315+instance FromJSON [ExposedPort] where
14771316 parseJSON (JSON.Object o) = HM.foldlWithKey' f (return []) o
14781317 where
14791318 f accM k _ = case T.split (== '/') k of
@@ -1486,27 +1325,12 @@ instance {-# OVERLAPPING #-} FromJSON [ExposedPort] where
14861325 parseJSON (JSON.Null) = return []
14871326 parseJSON _ = fail "ExposedPorts is not an object"
14881327
1489−instance {-# OVERLAPPING #-} ToJSON [ExposedPort] where
1328+instance ToJSON [ExposedPort] where
14901329 toJSON [] = emptyJsonObject
14911330 toJSON (p:ps) = toJsonKey (p:ps) key
14921331 where key (ExposedPort p t) = show p <> slash <> show t
14931332 slash = T.unpack "/"
14941333
1495−data Entrypoint = Entrypoint [T.Text] deriving (Eq, Show, Generic)
1496−
1497−instance ToJSON Entrypoint where
1498− toJSON (Entrypoint (e:es)) = toJSON (e:es)
1499− toJSON (Entrypoint []) = JSON.Null
1500−
1501−instance FromJSON Entrypoint where
1502− parseJSON (JSON.String e) = return $ Entrypoint [e]
1503− parseJSON (JSON.Array ar) = do
1504− arr <- mapM parseJSON (V.toList ar)
1505− return $ Entrypoint arr
1506− parseJSON JSON.Null = return $ Entrypoint []
1507− parseJSON _ = fail "Failed to parse Entrypoint"
1508−
1509−
15101334data ContainerConfig = ContainerConfig {
15111335 hostname :: Maybe Text
15121336 , domainname :: Maybe Text
@@ -1525,7 +1349,7 @@ data ContainerConfig = ContainerConfig {
15251349 , image :: Text
15261350 , volumes :: [Volume]
15271351 , workingDir :: Maybe FilePath
1528− , entrypoint :: Entrypoint
1352+ , entrypoint :: Maybe Text -- Can be null?
15291353 , networkDisabled :: Maybe Bool -- Note: Should we expand the JSON instance and take away the Maybe? Null is False?
15301354 , macAddress :: Maybe Text
15311355 -- , onBuild :: Maybe Text -- For 1.24, only see this in the inspect response.
@@ -1533,7 +1357,6 @@ data ContainerConfig = ContainerConfig {
15331357 , stopSignal :: Signal
15341358 } deriving (Eq, Show, Generic)
15351359
1536−
15371360instance ToJSON ContainerConfig where
15381361 toJSON = genericToJSON defaultOptions {
15391362 fieldLabelModifier = (\(x:xs) -> toUpper x : xs)}
@@ -1546,7 +1369,9 @@ instance FromJSON ContainerConfig where
15461369 attachStdin <- o .: "AttachStdin"
15471370 attachStdout <- o .: "AttachStdout"
15481371 attachStderr <- o .: "AttachStderr"
1549− exposedPorts <- o .:? "ExposedPorts" .!= []
1372+ exposedPorts <- case HM.lookup "ExposedPorts" o of
1373+ Just eps -> parseJSON eps
1374+ Nothing -> return []
15501375 tty <- o .: "Tty"
15511376 openStdin <- o .: "OpenStdin"
15521377 stdinOnce <- o .: "StdinOnce"
@@ -1555,20 +1380,18 @@ instance FromJSON ContainerConfig where
15551380 image <- o .: "Image"
15561381 volumes <- o .: "Volumes"
15571382 workingDir <- o .:? "WorkingDir"
1558− entrypoint <- o .: "Entrypoint"
1383+ entrypoint <- o .:? "Entrypoint"
15591384 networkDisabled <- o .:? "networkDisabled"
15601385 macAddress <- o .:? "MacAddress"
1561− labels <- o .:? "Labels" .!= []
1386+ labels <- case HM.lookup "Labels" o of
1387+ Just ls -> parseJSON ls
1388+ Nothing -> return []
15621389 stopSignal <- o .: "StopSignal"
15631390 return $ ContainerConfig hostname domainname user attachStdin attachStdout attachStderr exposedPorts tty openStdin stdinOnce env cmd image volumes workingDir entrypoint networkDisabled
15641391 macAddress labels stopSignal
15651392 parseJSON _ = fail "NetworkSettings is not an object."
15661393
1567−#if MIN_VERSION_base(4,13,0)
1568−parseIntegerText :: (MonadFail m) => Text -> m Integer
1569−#else
15701394parseIntegerText :: (Monad m) => Text -> m Integer
1571−#endif
15721395parseIntegerText t = case readMaybe $ T.unpack t of
15731396 Nothing ->
15741397 fail "Could not parse Integer"
@@ -1590,3 +1413,4 @@ toJsonKeyVal vs getKey getVal = JSON.Object $ foldl f HM.empty vs
15901413-- | Helper function that return an empty dictionary "{}"
15911414emptyJsonObject :: JSON.Value
15921415emptyJsonObject = JSON.Object HM.empty
1416+