@@ -1,35 +1,137 @@
1+{-# LANGUAGE RecordWildCards #-}
2+{-# LANGUAGE ScopedTypeVariables #-}
1−module Docker.Types where
4+module Docker.Types (
16+ , ContainerDetails(..)
17+ , DockerClientOpts(..)
22+ , ContainerPortInfo(..)
43+ , VolumePermission(..)
50+ , LogDriverOptions(..)
51+ , LogDriverConfig(..)
54+ , NetworkInterface(..)
56+ , NetworkSettings(..)
68+ , ContainerResources(..)
73+ , ContainerConfig(..)
3−import Data.Aeson (FromJSON, ToJSON, decode, encode,
76+import Data.Aeson (FromJSON, ToJSON, genericParseJSON,
4− genericParseJSON, genericToJSON, object,
5− parseJSON, toJSON, (.:), (.:?), (.=))
77+ genericToJSON, object, parseJSON, toJSON,
78+ (.!=), (.:), (.:?), (.=))
6−import qualified Data.Aeson as JSON
79+import qualified Data.Aeson as JSON
7−import Data.Aeson.Types (defaultOptions, fieldLabelModifier)
80+import Data.Aeson.Types (defaultOptions, fieldLabelModifier)
8−import Data.Char (toUpper)
81+import Data.Char (isAlphaNum, toUpper)
9−import qualified Data.Map as M
82+import qualified Data.HashMap.Strict as HM
83+import qualified Data.Map as M
10−import Data.Text (Text)
84+import Data.Monoid ((<>))
85+import Data.Text (Text)
11−import qualified Data.Text as T
86+import qualified Data.Text as T
87+import Data.Time.Clock (UTCTime)
12−import GHC.Generics (Generic)
88+import GHC.Generics (Generic)
13−import Prelude hiding (all, tail)
89+import Prelude hiding (all, tail)
90+import Text.Read (readMaybe)
15data Endpoint =
92data Endpoint =
16 VersionEndpoint
93 VersionEndpoint
17 | ListContainersEndpoint ListOpts
94 | ListContainersEndpoint ListOpts
18 | ListImagesEndpoint ListOpts
95 | ListImagesEndpoint ListOpts
19− | CreateContainerEndpoint
96+ | CreateContainerEndpoint CreateOpts
20 | StartContainerEndpoint StartOpts ContainerID
97 | StartContainerEndpoint StartOpts ContainerID
21 | StopContainerEndpoint Timeout ContainerID
98 | StopContainerEndpoint Timeout ContainerID
22 | KillContainerEndpoint Signal ContainerID
99 | KillContainerEndpoint Signal ContainerID
23 | RestartContainerEndpoint Timeout ContainerID
100 | RestartContainerEndpoint Timeout ContainerID
24 | PauseContainerEndpoint ContainerID
101 | PauseContainerEndpoint ContainerID
25 | UnpauseContainerEndpoint ContainerID
102 | UnpauseContainerEndpoint ContainerID
26− | ContainerLogsEndpoint LogOpts ContainerID
103+ | ContainerLogsEndpoint LogOpts Bool ContainerID -- Second argument (Bool) is whether to follow.
27 | DeleteContainerEndpoint DeleteOpts ContainerID
104 | DeleteContainerEndpoint DeleteOpts ContainerID
105+ | InspectContainerEndpoint ContainerID
29type URL = Text
108type URL = Text
30type ApiVersion = Text
109type ApiVersion = Text
110+newtype ContainerID = ContainerID Text
113+fromContainerID :: ContainerID -> Text
31−type ContainerID = Text
114+fromContainerID (ContainerID t) = t
116+toContainerID :: Text -> Maybe ContainerID
118+ if T.all (\c -> isAlphaNum c || c == ':') t then -- Note: Can we improve this whitelist?
119+ Just $ ContainerID t
123+newtype ImageID = ImageID Text
126+fromImageID :: ImageID -> Text
32−type ImageID = Text
127+fromImageID (ImageID t) = t
129+toImageID :: Text -> Maybe ImageID
131+ if T.all (\c -> isAlphaNum c || c == ':') t then -- Note: Can we improve this whitelist?
34data Timeout = Timeout Integer | DefaultTimeout deriving (Eq, Show)
136data Timeout = Timeout Integer | DefaultTimeout deriving (Eq, Show)
@@ -43,21 +145,121 @@ data Signal = SIGHUP
43 | SIG Integer
145 | SIG Integer
44 | SIGKILL deriving (Eq, Show)
146 | SIGKILL deriving (Eq, Show)
148+instance FromJSON Signal where
149+ parseJSON (JSON.String "SIGTERM") = return SIGTERM
150+ parseJSON (JSON.String "SIGHUP") = return SIGHUP -- Note: Guessing on the string values for these.
151+ parseJSON (JSON.String "SIGINT") = return SIGINT
152+ parseJSON (JSON.String "SIGQUIT") = return SIGQUIT
153+ parseJSON (JSON.String "SIGSTOP") = return SIGSTOP
154+ parseJSON (JSON.String "SIGUSR1") = return SIGUSR1
155+ parseJSON _ = fail "Unknown Signal"
157+instance ToJSON Signal where
158+ toJSON SIGHUP = "SIGHUP"
159+ toJSON SIGINT = "SIGINT"
160+ toJSON SIGQUIT = "SIGQUIT"
161+ toJSON SIGSTOP = "SIGSTOP"
162+ toJSON SIGTERM = "SIGTERM"
163+ toJSON SIGUSR1 = "SIGUSR1"
164+ toJSON (SIG i) = toJSON i
165+ toJSON SIGKILL = "SIGKILL"
167+data ContainerDetails = ContainerDetails {
168+ appArmorProfile :: Text
170+ , containerDetailsConfig :: ContainerConfig
171+ , created :: UTCTime
173+ -- , execIDs -- Not sure what this is in 1.24 spec.
174+ , containerDetailsHostConfig :: HostConfig
175+ , hostnamePath :: FilePath
176+ , hostsPath :: FilePath
177+ , logPath :: FilePath
178+ , containerDetailsId :: ContainerID
179+ , containerDetailsImage :: ImageID
180+ , mountLabel :: Text
182+ , networkSettings :: NetworkSettings
184+ , processLabel :: Text
185+ , resolveConfPath :: FilePath
186+ , restartCount :: Int
187+ , state :: ContainerState
190+ deriving (Eq, Show, Generic)
194+ , mountSource :: FilePath
195+ , mountDestination :: FilePath
196+ , mountDriver :: Text
197+ , mountMode :: Text -- Note: Improve this?
199+ , mountPropogation :: Text
201+ deriving (Eq, Show, Generic)
203+instance FromJSON Mount where
204+ parseJSON (JSON.Object o) = do
206+ src <- o .: "Source"
207+ dest <- o .: "Destination"
208+ driver <- o .: "Driver"
211+ prop <- o .: "Propagation"
212+ return $ Mount name src dest driver mode rw prop
213+ parseJSON _ = fail "Mount is not an object"
215+data ContainerState = ContainerState {
216+ containerError :: Text
218+ , finishedAt :: Maybe UTCTime -- Note: Is this a maybe?
223+ , restarting :: Bool
225+ , startedAt :: UTCTime
228+ deriving (Eq, Show, Generic)
230+instance FromJSON ContainerState where
231+ parseJSON (JSON.Object o) = do
233+ exit <- o .: "ExitCode"
234+ finished <- o .:? "FinishedAt"
235+ oomKilled <- o .: "OOMKilled"
237+ paused <- o .: "Paused"
239+ restarting <- o .: "Restarting"
240+ running <- o .: "Running"
241+ started <- o .: "StartedAt"
243+ return $ ContainerState err exit finished oomKilled dead paused pid restarting running started st
244+ parseJSON _ = fail "ContainerState is not an object"
46data DockerClientOpts = DockerClientOpts {
246data DockerClientOpts = DockerClientOpts {
47 apiVer :: ApiVersion
247 apiVer :: ApiVersion
48 , baseUrl :: URL
248 , baseUrl :: URL
51−data ListOpts = ListOpts { all :: Bool } deriving (Eq, Show)
250+ deriving (Eq, Show)
53−defaultListOpts = ListOpts { all=False }
55defaultClientOpts :: DockerClientOpts
252defaultClientOpts :: DockerClientOpts
56defaultClientOpts = DockerClientOpts {
253defaultClientOpts = DockerClientOpts {
57− apiVer = "v1.22"
254+ apiVer = "v1.23"
58 , baseUrl = "http://127.0.0.1:2375"
255 , baseUrl = "http://127.0.0.1:2375"
258+data ListOpts = ListOpts { all :: Bool } deriving (Eq, Show)
260+defaultListOpts :: ListOpts
261+defaultListOpts = ListOpts { all=False }
61data DockerVersion = DockerVersion {
263data DockerVersion = DockerVersion {
62 version :: Text
264 version :: Text
63 , apiVersion :: ApiVersion
265 , apiVersion :: ApiVersion
@@ -78,6 +280,57 @@ instance FromJSON DockerVersion where
78 parseJSON = genericParseJSON defaultOptions {
280 parseJSON = genericParseJSON defaultOptions {
79 fieldLabelModifier = (\(x:xs) -> toUpper x : xs)}
281 fieldLabelModifier = (\(x:xs) -> toUpper x : xs)}
283+-- instance ToJSON ContainerDetails where
284+-- toJSON = error "TODO"
286+instance FromJSON ContainerDetails where
287+ parseJSON (JSON.Object o) = do
288+ appArmor <- o .: "AppArmorProfile"
290+ config <- o .: "Config"
291+ created <- o .: "Created"
292+ driver <- o .: "Driver"
293+ hostConfig <- o .: "HostConfig"
294+ hostnamePath <- o .: "HostnamePath"
295+ hostsPath <- o .: "HostsPath"
296+ logPath <- o .: "LogPath"
298+ image <- o .: "Image"
299+ mountLabel <- o .: "MountLabel"
301+ networkSettings <- o .: "NetworkSettings"
303+ processLabel <- o .: "ProcessLabel"
304+ resolveConfPath <- o .: "ResolvConfPath"
305+ restartCount <- o .: "RestartCount"
306+ state <- o .: "State"
307+ mounts <- o .: "Mounts"
308+ return $ ContainerDetails appArmor args config created driver hostConfig hostnamePath hostsPath logPath id image mountLabel name networkSettings path processLabel resolveConfPath restartCount state mounts
309+ parseJSON _ = fail "ContainerDetails is not an object"
311+instance ToJSON ContainerID where
312+ toJSON (ContainerID cid) = object ["Id" .= cid]
314+instance FromJSON ContainerID where
315+ parseJSON (JSON.Object o) = do
317+ case toContainerID cid of
319+ fail "Invalid ContainerID"
322+ parseJSON _ = fail "ContainerID is not an object."
324+instance ToJSON ImageID where
325+ toJSON (ImageID iid) = JSON.String iid
327+instance FromJSON ImageID where
328+ parseJSON (JSON.String t) = case toImageID t of
330+ fail "Invalid ImageID"
333+ parseJSON _ = fail "ImageID is not an object."
82data ContainerPortInfo = ContainerPortInfo {
335data ContainerPortInfo = ContainerPortInfo {
83 ipAddressInfo :: Maybe Text
336 ipAddressInfo :: Maybe Text
@@ -92,50 +345,100 @@ instance FromJSON ContainerPortInfo where
92 <*> (v .: "PrivatePort")
345 <*> (v .: "PrivatePort")
93 <*> (v .:? "PublicPort")
346 <*> (v .:? "PublicPort")
94 <*> (v .:? "Type")
347 <*> (v .:? "Type")
95−
348+ parseJSON _ = fail "ContainerPortInfo: Not a JSON object."
350+-- For inspecting container details.
96−-- data NetworkOptions = NetworkOptions {
351+data NetworkOptions = NetworkOptions {
97−-- ipamConfig :: Maybe Text
352+-- ipamConfig :: Maybe Text -- Don't see in 1.24
98−-- , links :: Maybe Text
353+-- , links :: Maybe Text -- Don't see in 1.24
99−-- , aliases :: Maybe Text
354+-- , aliases :: Maybe Text -- Don't see in 1.24
100−-- , networkId :: Text
355+ networkOptionsId :: Text
101−-- , endpointId :: Text
356+ , networkOptionsEndpointId :: Text
102−-- , gateway :: Text
357+ , networkOptionsGateway :: Text
103−-- , ipAddress :: Text
358+ , networkOptionsIpAddress :: Text -- Note: Parse this?
104−-- , ipPrefixLen :: Text
359+ , networkOptionsIpPrefixLen :: Int
105−-- , ipV6Gateway :: Maybe Text
360+ , networkOptionsIpV6Gateway :: Maybe Text
106−-- , globalIPv6Address :: Maybe Text
361+ , networkOptionsGlobalIPv6Address :: Maybe Text
107−-- , globalIPv6PrefixLen :: Maybe Text
362+ , networkOptionsGlobalIPv6PrefixLen :: Maybe Int
108−-- , macAddress :: Text
363+ , networkOptionsMacAddress :: Text
109−-- } deriving (Eq, Show)
364+ } deriving (Eq, Show)
111−-- data Network = Network (M.Map NetworkMode NetworkOptions) deriving (Eq, Show)
366+instance FromJSON NetworkOptions where
112−
367+ parseJSON (JSON.Object o) = do
113−-- data NetworkSettings = NetworkSettings {
368+ networkId <- o .: "NetworkID"
114−-- bridge
369+ endpointId <- o .: "EndpointID"
115−-- , sandboxId
370+ gateway <- o .: "Gateway"
116−-- , hairpinMode
371+ ip <- o .: "IPAddress"
117−-- , linkLocalIPv6Address
372+ ipLen <- o .: "IPPrefixLen"
118−-- , linkLocalIPv6PrefixLen
373+ ip6Gateway <- o .:? "IPv6Gateway"
119−-- , ports
374+ globalIP6 <- o .:? "GlobalIPv6Address"
120−-- , sandboxKey
375+ globalIP6Len <- o .:? "GlobalIPv6PrefixLen"
121−-- , secondaryIPAddresses
376+ mac <- o .: "MacAddress"
122−-- , secondaryIPv6Addresses
377+ return $ NetworkOptions networkId endpointId gateway ip ipLen ip6Gateway globalIP6 globalIP6Len mac
123−-- , endpointID
378+ parseJSON _ = fail "NetworkOptions is not an object"
125−-- , globalIPv6Address
380+newtype Networks = Networks (M.Map NetworkMode NetworkOptions) -- Note: Is it ever possible that there will be duplicates of network modes?
126−-- , globalIPv6PrefixLen
381+ deriving (Eq, Show)
128−-- , ipPrefixLen
383+instance FromJSON Networks where
129−-- , ipv6Gateway
384+ parseJSON (JSON.Object o) = do
130−-- , macAddress
385+ Networks <$> HM.foldlWithKey' f (return M.empty) o
131−-- , networks :: [Network]
386+
133−
388+ f accM k' v' = do
390+ k <- parseJSON $ JSON.String k'
392+ return $ M.insert k v acc
394+ parseJSON _ = fail "Networks is not an object"
134−-- data NetworkSettings = NetworkSettings (M.Map Text Text) deriving (Eq, Show, Generic)
396+data NetworkSettings = NetworkSettings {
135−
397+ networkSettingsBridge :: Text
398+ , networkSettingsSandboxId :: Text
399+ , networkSettingsHairpinMode :: Bool
400+ , networkSettingsLinkLocalIPv6Address :: Text
401+ , networkSettingsLinkLocalIPv6PrefixLen :: Int
402+ , networkSettingsPorts :: [Int] -- TODO: 1.24 spec is unclear
403+ , networkSettingsSandboxKey :: Text
404+ , networkSettingsSecondaryIPAddresses :: Maybe [Text] -- TODO: 1.24 spec is unclear
405+ , networkSettingsSecondaryIPv6Addresses :: Maybe [Text] -- TODO: 1.24 spec is unclear
406+ , networkSettingsEndpointID :: Text
407+ , networkSettingsGateway :: Text
408+ , networkSettingsGlobalIPv6Address :: Text
409+ , networkSettingsGlobalIPv6PrefixLen :: Int
410+ , networkSettingsIpAddress :: Text
411+ , networkSettingsIpPrefixLen :: Int
412+ , networkSettingsIpv6Gateway
413+ , networkSettingsMacAddress
414+ , networkSettingsNetworks :: Networks
136−-- instance FromJSON NetworkSettings
418+instance FromJSON NetworkSettings where
137−
419+ parseJSON (JSON.Object o) = do
420+ bridge <- o .: "Bridge"
421+ sandbox <- o .: "SandboxID"
422+ hairpin <- o .: "HairpinMode"
423+ localIP6 <- o .: "LinkLocalIPv6Address"
424+ localIP6Len <- o .: "LinkLocalIPv6PrefixLen"
425+ ports <- o .:? "Ports" .!= []
426+ sandboxKey <- o .: "SandboxKey"
427+ secondaryIP <- o .: "SecondaryIPAddresses"
428+ secondayIP6 <- o .: "SecondaryIPv6Addresses"
429+ endpointID <- o .: "EndpointID"
430+ gateway <- o .: "Gateway"
431+ globalIP6 <- o .: "GlobalIPv6Address"
432+ globalIP6Len <- o .: "GlobalIPv6PrefixLen"
433+ ip <- o .: "IPAddress"
434+ ipLen <- o .: "IPPrefixLen"
435+ ip6Gateway <- o .: "IPv6Gateway"
436+ mac <- o .: "MacAddress"
437+ networks <- o .: "Networks"
438+ return $ NetworkSettings bridge sandbox hairpin localIP6 localIP6Len ports sandboxKey secondaryIP secondayIP6 endpointID gateway globalIP6 globalIP6Len ip ipLen ip6Gateway mac networks
439+ parseJSON _ = fail "NetworkSettings is not an Object"
138−-- TODO: Add NetworkSettings
441+-- TODO: Add NetworkSettings, Mounts
139data Container = Container
442data Container = Container
140 { containerId :: ContainerID
443 { containerId :: ContainerID
141 , containerNames :: [Text]
444 , containerNames :: [Text]
@@ -143,10 +446,11 @@ data Container = Container
143 , containerImageId :: ImageID
446 , containerImageId :: ImageID
144 , containerCommand :: Text
447 , containerCommand :: Text
145 , containerCreatedAt :: Int
448 , containerCreatedAt :: Int
146− , containerStatus :: Text
449+ , containerStatus :: Status
147 , containerPorts :: [ContainerPortInfo]
450 , containerPorts :: [ContainerPortInfo]
148 , containerLabels :: Labels
451 , containerLabels :: Labels
149− -- , containerNetworkSettings :: NetworkSettings
452+ , containerNetworks :: Networks
453+ , containerMounts :: [Mount]
150 } deriving (Show, Eq)
454 } deriving (Show, Eq)
152instance FromJSON Container where
456instance FromJSON Container where
@@ -160,14 +464,29 @@ instance FromJSON Container where
160 <*> (v .: "Status")
464 <*> (v .: "Status")
161 <*> (v .: "Ports")
465 <*> (v .: "Ports")
162 <*> (v .: "Labels")
466 <*> (v .: "Labels")
163− -- <*> (v .: "NetworkSettings")
467+ <*> (v .: "NetworkSettings" >>= parseNetworks)
164−
468+ <*> (v .: "Mounts")
470+ parseNetworks (JSON.Object v) =
471+ (v .: "Networks") >>= parseJSON
472+ parseNetworks _ = fail "Container NetworkSettings: Not a JSON object."
474+ parseJSON _ = fail "Container: Not a JSON object."
165−data Status = Created | Restarting | Running | Paused | Exited | Dead deriving (Eq, Show, Generic)
476+data Status = Created | Restarting | Running | Paused | Exited | Dead
166−
477+ deriving (Eq, Show, Generic)
167−instance FromJSON Status
479+instance FromJSON Status where
168−instance ToJSON Status
480+ parseJSON (JSON.String "running") = return Running
481+ parseJSON (JSON.String "created") = return Created -- Note: Guessing on the string values of these.
482+ parseJSON (JSON.String "restarting") = return Restarting
483+ parseJSON (JSON.String "paused") = return Paused
484+ parseJSON (JSON.String "exited") = return Exited
485+ parseJSON (JSON.String "dead") = return Dead
486+ parseJSON _ = fail "Unknown Status"
170type Digest = Text
488type Digest = Text
171newtype Labels = Labels (M.Map Name Value) deriving (Eq, Show)
490newtype Labels = Labels (M.Map Name Value) deriving (Eq, Show)
173instance FromJSON Labels where
492instance FromJSON Labels where
@@ -183,12 +502,13 @@ data Image = DockerImage {
183 , imageCreated :: Integer
502 , imageCreated :: Integer
184 , imageParentId :: Maybe ImageID
503 , imageParentId :: Maybe ImageID
185 , imageRepoTags :: [Tag]
504 , imageRepoTags :: [Tag]
186− , imageRepoDigests :: [Digest]
505+ , imageRepoDigests :: Maybe [Digest]
187 , imageSize :: Integer
506 , imageSize :: Integer
188 , imageVirtualSize :: Integer
507 , imageVirtualSize :: Integer
189− , imageLabels :: Labels
508+ , imageLabels :: Maybe Labels
190 } deriving (Show, Eq, Generic)
509 } deriving (Show, Eq, Generic)
511+dropImagePrefix :: [a] -> [a]
192dropImagePrefix = drop 5
512dropImagePrefix = drop 5
194instance FromJSON Image where
514instance FromJSON Image where
@@ -196,17 +516,102 @@ instance FromJSON Image where
196 fieldLabelModifier = dropImagePrefix}
516 fieldLabelModifier = dropImagePrefix}
198data CreateOpts = CreateOpts {
518data CreateOpts = CreateOpts {
199− containerConfig :: ContainerConfig
519+ containerConfig :: ContainerConfig
200− , hostConfig :: HostConfig
520+ , hostConfig :: HostConfig
201− , containerResources :: ContainerResources
202 } deriving (Eq, Show)
521 } deriving (Eq, Show)
523+instance ToJSON CreateOpts where
524+ toJSON (CreateOpts cc hc) = do
525+ let ccJSON = toJSON cc
526+ let hcJSON = toJSON hc
528+ JSON.Object (o :: HM.HashMap T.Text JSON.Value) -> do
529+ JSON.Object $ HM.insert "HostConfig" hcJSON o
530+ _ -> error "ContainerConfig is not an object." -- This should never happen.
532+cConfig :: Text -> ContainerConfig
533+cConfig imageName = ContainerConfig {
535+ , domainname=Nothing
538+ , attachStdout=False
540+ , attachStderr=False
541+ , exposedPorts=ExposedPorts M.empty
548+ , workingDir=Nothing
549+ , entrypoint=Nothing
550+ , networkDisabled=False
551+ , macAddress=Nothing
553+ , stopSignal=SIGTERM
556+hConfig :: HostConfig
557+hConfig = HostConfig {
559+ , containerIDFile=Nothing
560+ , logConfig=LogDriverConfig JsonFile Nothing
561+ , networkMode=Bridge
562+ , portBindings=PortBindings []
563+ , restartPolicy=RestartOff
564+ , volumeDriver=Nothing
574+ , oomScoreAdj=Nothing
576+ , publishAllPorts=False
577+ , readOnlyRootfs=False
580+ , resources=cResources
583+cResources :: ContainerResources
584+cResources = ContainerResources {
586+ , blkioWeight=Nothing
587+ , blkioWeightDevice=Nothing
588+ , blkioDeviceReadBps=Nothing
589+ , blkioDeviceWriteBps=Nothing
590+ , blkioDeviceReadIOps=Nothing
591+ , blkioDeviceWriteIOps=Nothing
593+ , cpusetCpus=Nothing
594+ , cpusetMems=Nothing
596+ , kernelMemory=Nothing
598+ , memoryReservation=Nothing
599+ , memorySwap=Nothing
600+ , oomKillDisable=False
605+defaultCreateOpts :: T.Text -> CreateOpts
606+defaultCreateOpts imageName = CreateOpts { containerConfig = cConfig imageName, hostConfig = hConfig }
204-- detachKeys – Override the key sequence for detaching a container.
608-- detachKeys – Override the key sequence for detaching a container.
205-- Format is a single character [a-Z] or ctrl-<value> where <value> is one of: a-z, @, ^, [, , or _.
609-- Format is a single character [a-Z] or ctrl-<value> where <value> is one of: a-z, @, ^, [, , or _.
206data DetachKeys = WithCtrl Char | WithoutCtrl Char | DefaultDetachKey deriving (Eq, Show)
610data DetachKeys = WithCtrl Char | WithoutCtrl Char | DefaultDetachKey deriving (Eq, Show)
208data StartOpts = StartOpts { detachKeys :: DetachKeys } deriving (Eq, Show)
612data StartOpts = StartOpts { detachKeys :: DetachKeys } deriving (Eq, Show)
614+defaultStartOpts :: StartOpts
210defaultStartOpts = StartOpts { detachKeys = DefaultDetachKey }
615defaultStartOpts = StartOpts { detachKeys = DefaultDetachKey }
212data DeleteOpts = DeleteOpts {
617data DeleteOpts = DeleteOpts {
@@ -218,16 +623,15 @@ type Timestamp = Integer
218data TailLogOpt = Tail Integer | All deriving (Eq, Show)
623data TailLogOpt = Tail Integer | All deriving (Eq, Show)
220data LogOpts = LogOpts {
625data LogOpts = LogOpts {
222− , stdout :: Bool
626+ stdout :: Bool
223 , stderr :: Bool
627 , stderr :: Bool
224 , since :: Maybe Timestamp
628 , since :: Maybe Timestamp
225 , timestamps :: Bool
629 , timestamps :: Bool
226 , tail :: TailLogOpt
630 , tail :: TailLogOpt
227 } deriving (Eq, Show)
631 } deriving (Eq, Show)
229−defaultLogOpts = LogOpts { follow = False
633+defaultLogOpts :: LogOpts
230− , stdout = True
634+defaultLogOpts = LogOpts { stdout = True
231 , stderr = True
635 , stderr = True
232 , since = Nothing
636 , since = Nothing
233 , timestamps = True
637 , timestamps = True
@@ -236,14 +640,36 @@ defaultLogOpts = LogOpts { follow = False
237data VolumePermission = Rw | Ro deriving (Eq, Show, Generic)
641data VolumePermission = Rw | Ro deriving (Eq, Show, Generic)
239−instance ToJSON VolumePermission
643+-- instance ToJSON VolumePermission where
644+-- toJSON = error "TODO"
240−instance FromJSON VolumePermission
645+-- instance FromJSON VolumePermission where
646+-- parseJSON = error "TODO"
648+-- Note: Note really sure what this should be (1.24).
649+-- This is not the same as [Volume] in HostConfig.
650+newtype Volumes = Volumes [Text] -- [Volume]
653+instance FromJSON Volumes where
654+ parseJSON (JSON.Object o) = return $ Volumes $ HM.keys o
655+ parseJSON _ = fail "Volumes is not an object"
657+instance ToJSON Volumes where
658+ toJSON (Volumes vs) = toJSON vs
242data Volume = Volume { hostSrc :: Text
660data Volume = Volume { hostSrc :: Text
243 , containerDest :: Text
661 , containerDest :: Text
244 , volumePermission :: Maybe VolumePermission
662 , volumePermission :: Maybe VolumePermission
245 } deriving (Eq, Show)
663 } deriving (Eq, Show)
665+instance FromJSON Volume where
666+ parseJSON (JSON.String t) = case T.split (== ':') t of
667+ [src, dest] -> return $ Volume src dest Nothing
668+ [src, dest, "rw"] -> return $ Volume src dest $ Just Rw
669+ [src, dest, "ro"] -> return $ Volume src dest $ Just Ro
670+ _ -> fail "Could not parse Volume"
671+ parseJSON _ = fail "Volume is not a string"
247data Device = Device {
673data Device = Device {
248 pathOnHost :: FilePath
674 pathOnHost :: FilePath
249 , pathInContainer :: FilePath
675 , pathInContainer :: FilePath
@@ -260,7 +686,20 @@ instance FromJSON Device where
261type ContainerName = Text
687type ContainerName = Text
263−data VolumeFrom = VolumeFrom ContainerName VolumePermission deriving (Eq, Show)
689+data VolumeFrom = VolumeFrom ContainerName (Maybe VolumePermission) deriving (Eq, Show)
691+instance FromJSON VolumeFrom where
692+ parseJSON (JSON.String t) = case T.split (== ':') t of
693+ [vol] -> return $ VolumeFrom vol Nothing
694+ [vol, "rw"] -> return $ VolumeFrom vol $ Just Rw
695+ [vol, "ro"] -> return $ VolumeFrom vol $ Just Ro
696+ _ -> fail "Could not parse VolumeFrom"
697+ parseJSON _ = fail "VolumeFrom is not a string"
699+instance ToJSON VolumeFrom where
700+ toJSON (VolumeFrom n p) = case p of
701+ Nothing -> toJSON $ n <> ":" <> "rw"
702+ Just per -> toJSON $ n <> ":" <> (T.pack $ show per)
265instance ToJSON Volume where
704instance ToJSON Volume where
266 toJSON (Volume src dest mode) = toJSON $ case mode of
705 toJSON (Volume src dest mode) = toJSON $ case mode of
@@ -269,47 +708,206 @@ instance ToJSON Volume where
270data Link = Link Text (Maybe Text) deriving (Eq, Show)
709data Link = Link Text (Maybe Text) deriving (Eq, Show)
711+instance FromJSON Link where
712+ parseJSON (JSON.String t) = case T.split (== ':') t of
713+ [f] -> return $ Link f Nothing
714+ [f,s] -> return $ Link f $ Just s
715+ _ -> fail "Could not parse Link"
716+ parseJSON _ = fail "Link is not a string"
272instance ToJSON Link where
718instance ToJSON Link where
273 toJSON (Link n1 n2) = toJSON $ case n2 of
719 toJSON (Link n1 n2) = toJSON $ case n2 of
274 Nothing -> T.concat[n1, ":", n1] -- use same name in container
720 Nothing -> T.concat[n1, ":", n1] -- use same name in container
275 Just n -> T.concat[n1, ":", n] -- used specified name in container
721 Just n -> T.concat[n1, ":", n] -- used specified name in container
277-- { "Type": "<driver_name>", "Config": {"key1": "val1"} }
723-- { "Type": "<driver_name>", "Config": {"key1": "val1"} }
278−data LogDriverType = JsonFile | Syslog | Journald | Gelf | AwsLogs | Splunk | LoggingDisabled deriving (Eq, Show)
724+data LogDriverType = JsonFile | Syslog | Journald | Gelf | Fluentd | AwsLogs | Splunk | Etwlogs | LoggingDisabled deriving (Eq, Show)
726+instance FromJSON LogDriverType where
727+ parseJSON (JSON.String "json-file") = return JsonFile
728+ parseJSON (JSON.String "syslog") = return Syslog
729+ parseJSON (JSON.String "journald") = return Journald
730+ parseJSON (JSON.String "gelf") = return Gelf
731+ parseJSON (JSON.String "fluentd") = return Fluentd
732+ parseJSON (JSON.String "awslogs") = return AwsLogs
733+ parseJSON (JSON.String "splunk") = return Splunk
734+ parseJSON (JSON.String "etwlogs") = return Etwlogs
735+ parseJSON (JSON.String "none") = return LoggingDisabled
736+ parseJSON _ = fail "Unknown LogDriverType"
738+instance ToJSON LogDriverType where
739+ toJSON JsonFile = JSON.String "json-file"
740+ toJSON Syslog = JSON.String "syslog"
741+ toJSON Journald = JSON.String "journald"
742+ toJSON Gelf = JSON.String "gelf"
743+ toJSON Fluentd = JSON.String "fluentd"
744+ toJSON AwsLogs = JSON.String "awslogs"
745+ toJSON Splunk = JSON.String "splunk"
746+ toJSON Etwlogs = JSON.String "etwlogs"
747+ toJSON LoggingDisabled = JSON.String "none"
280−data LogDriverOptions = Map Text Text deriving (Eq, Show)
749+newtype LogDriverOptions = LogDriverOptions (M.Map Text Text) deriving (Eq, Show)
751+instance FromJSON LogDriverOptions where
752+ parseJSON (JSON.Object o) = do
753+ LogDriverOptions <$> HM.foldlWithKey' f (return M.empty) o
756+ f accM k (JSON.String v) = do
758+ return $ M.insert k v acc
759+ f _ _ _ = fail "Value of LogDriverOptions is not a string"
761+ parseJSON JSON.Null = return $ LogDriverOptions M.empty
762+ parseJSON _ = fail "LogDriverOptions is not an object"
764+instance ToJSON LogDriverOptions where
765+ toJSON (LogDriverOptions mp) = toJSON mp
282data LogDriverConfig = LogDriverConfig LogDriverType (Maybe LogDriverOptions) deriving (Eq, Show)
767data LogDriverConfig = LogDriverConfig LogDriverType (Maybe LogDriverOptions) deriving (Eq, Show)
769+instance ToJSON LogDriverConfig where
770+ toJSON (LogDriverConfig driverType driverOptions) = object ["Type" .= driverType, "Config" .= driverOptions]
772+instance FromJSON LogDriverConfig where
773+ parseJSON (JSON.Object o) = do
775+ opts <- o .: "Config"
776+ return $ LogDriverConfig typ opts
777+ parseJSON _ = fail "LogDriverConfig is not an object"
284-- TODO: Add container:<name|id> mode
779-- TODO: Add container:<name|id> mode
285−data NetworkMode = Bridge | Host | NetworkDisabled deriving (Eq, Show)
780+data NetworkMode = Bridge | Host | NetworkDisabled
781+ deriving (Eq, Show, Ord)
783+instance FromJSON NetworkMode where
784+ parseJSON (JSON.String "bridge") = return Bridge
785+ parseJSON (JSON.String "host") = return Host -- Note: Guessing on these.
786+ parseJSON (JSON.String "none") = return NetworkDisabled
787+ parseJSON _ = fail "Unknown NetworkMode"
789+instance ToJSON NetworkMode where
790+ toJSON Bridge = JSON.String "bridge"
791+ toJSON Host = JSON.String "host"
792+ toJSON NetworkDisabled = JSON.String "none"
287−data PortType = TCP | UDP deriving (Eq, Show, Generic)
794+data PortType = TCP | UDP deriving (Eq, Show, Generic, Read, Ord)
289instance ToJSON PortType where
796instance ToJSON PortType where
290 toJSON TCP = "tcp"
797 toJSON TCP = "tcp"
291 toJSON UDP = "udp"
798 toJSON UDP = "udp"
293instance FromJSON PortType where
800instance FromJSON PortType where
294− parseJSON val = return $ case val of
801+ parseJSON val = case val of
295− "tcp" -> TCP
802+ "tcp" -> return TCP
296− "udp" -> UDP
803+ "udp" -> return UDP
804+ _ -> fail "PortType: Invalid port type."
298newtype NetworkInterface = NetworkInterface Text deriving (Eq, Show)
806newtype NetworkInterface = NetworkInterface Text deriving (Eq, Show)
808+instance FromJSON NetworkInterface where
809+ parseJSON (JSON.String v) = return $ NetworkInterface v
810+ parseJSON _ = fail "Network interface is not a string."
812+instance ToJSON NetworkInterface where
813+ toJSON (NetworkInterface i) = JSON.String i
815+-- | This datastructure models mapping a Port from the container onto the
816+-- host system s that the service running in the container can be accessed from
817+-- the outside world. We either map a port onto all interfaces (default) or onto a specific
818+-- interface like `127.0.0.1`.
819+newtype PortBindings = PortBindings [PortBinding]
300-- { <port>/<protocol>: [{ "HostPort": "<port>" }] }
822-- { <port>/<protocol>: [{ "HostPort": "<port>" }] }
301data PortBinding = PortBinding {
823data PortBinding = PortBinding {
302 containerPort :: Port
824 containerPort :: Port
303 , portType :: PortType
825 , portType :: PortType
304− , hostPort :: [HostPort]
826+ , hostPorts :: HostPorts
305 } deriving (Eq, Show)
827 } deriving (Eq, Show)
830+portAndType2Text :: Port -> PortType -> Text
831+portAndType2Text p t = (T.pack $ show p) <> "/" <> (T.pack $ show t)
833+instance ToJSON PortBinding where
834+ toJSON (PortBinding {..}) = object [portAndType2Text containerPort portType .= hostPorts]
836+instance FromJSON PortBindings where
837+ parseJSON (JSON.Object o) = do
838+ PortBindings <$> HM.foldlWithKey' f (return []) o
841+ f accM k v = case T.split (== '/') k of
842+ [port', portType'] -> do
843+ port <- parseIntegerText port'
844+ portType <- parseJSON $ JSON.String portType'
847+ return $ (PortBinding port portType hps):acc
849+ fail "Could not parse PortBindings"
850+ parseJSON _ = fail "PortBindings is not an object"
852+instance ToJSON PortBindings where
853+ toJSON (PortBindings []) = JSON.Object HM.empty
854+ toJSON (PortBindings (p:ps)) = toJSON (p:ps)
307−data HostPort = HostPort NetworkInterface Port deriving (Eq, Show)
856+data HostPort = HostPort NetworkInterface Port
859+instance ToJSON HostPort where
860+ toJSON (HostPort i p) = object ["HostPort" .= p, "HostIP" .= i]
862+instance FromJSON HostPort where
863+ parseJSON (JSON.Object o) = do
864+ p <- o .: "HostPort"
866+ return $ HostPort i p
867+ parseJSON _ = fail "HostPort is not an object."
869+newtype HostPorts = HostPorts [HostPort]
873+instance ToJSON HostPorts where
874+ toJSON (HostPorts hps) = toJSON hps
876+instance FromJSON HostPorts where
877+ parseJSON (JSON.Object o) = do
878+ HostPorts <$> HM.foldlWithKey' f (return []) o
884+ p <- parseIntegerText p'
885+ return $ (HostPort (NetworkInterface k) p):acc
886+ parseJSON _ = fail "HostPorts is not an object"
309-- { "Name": "on-failure" , "MaximumRetryCount": 2}
888-- { "Name": "on-failure" , "MaximumRetryCount": 2}
310type RetryCount = Integer
889type RetryCount = Integer
311data RestartPolicy = RestartAlways | RestartUnlessStopped | RestartOnFailure RetryCount | RestartOff deriving (Eq, Show)
890data RestartPolicy = RestartAlways | RestartUnlessStopped | RestartOnFailure RetryCount | RestartOff deriving (Eq, Show)
892+instance FromJSON RestartPolicy where
893+ parseJSON (JSON.Object o) = do
894+ (name :: Text) <- o .: "Name"
896+ "always" -> return RestartAlways
897+ "unless-stopped" -> return RestartUnlessStopped
899+ retry <- o .: "MaximumRetryCount"
900+ return $ RestartOnFailure retry
901+ "" -> return RestartOff -- Note: Is this correct? 1.24 spec is unclear.
902+ _ -> fail "Could not parse RestartPolicy"
903+ parseJSON _ = fail "RestartPolicy is not an object"
905+instance ToJSON RestartPolicy where
906+ toJSON RestartAlways = object ["Name" .= JSON.String "always"]
907+ toJSON RestartUnlessStopped = object ["Name" .= JSON.String "unless-stopped"]
908+ toJSON (RestartOnFailure c) = object ["Name" .= JSON.String "on-failure", "MaximumRetryCount" .= c]
909+ toJSON RestartOff = object ["Name" .= JSON.String "off"]
313data Isolation = Default | Process | Hyperv deriving (Eq, Show)
911data Isolation = Default | Process | Hyperv deriving (Eq, Show)
315newtype UTSMode = UTSMode Text deriving (Eq, Show)
913newtype UTSMode = UTSMode Text deriving (Eq, Show)
@@ -319,12 +917,12 @@ newtype UTSMode = UTSMode Text deriving (Eq, Show)
319-- TODO: Sysctls map[string]string `json:",omitempty"` // List of Namespaced sysctls used for the container
917-- TODO: Sysctls map[string]string `json:",omitempty"` // List of Namespaced sysctls used for the container
320data HostConfig = HostConfig
918data HostConfig = HostConfig
321 { binds :: [Volume]
919 { binds :: [Volume]
322− , containerIDFile :: FilePath
920+ , containerIDFile :: Maybe FilePath -- 1.24: Only in responses, not create
323 , logConfig :: LogDriverConfig
921 , logConfig :: LogDriverConfig
324 , networkMode :: NetworkMode
922 , networkMode :: NetworkMode
325− , portBindings :: [PortBinding]
923+ , portBindings :: PortBindings
326 , restartPolicy :: RestartPolicy
924 , restartPolicy :: RestartPolicy
327− , volumeDriver :: Text
925+ , volumeDriver :: Maybe Text
328 , volumesFrom :: [VolumeFrom]
926 , volumesFrom :: [VolumeFrom]
329 , capAdd :: [Text]
927 , capAdd :: [Text]
330 , capDrop :: [Text]
928 , capDrop :: [Text]
@@ -332,93 +930,178 @@ data HostConfig = HostConfig
332 , dnsOptions :: [Text]
930 , dnsOptions :: [Text]
333 , dnsSearch :: [Text]
931 , dnsSearch :: [Text]
334 , extraHosts :: [Text]
932 , extraHosts :: [Text]
335− , groupAdd :: [Integer]
933+ -- , groupAdd :: [Integer] -- 1.24: Missing from inspecting container details... Going to omit for now.
336− , ipcMode :: Text
934+ , ipcMode :: Maybe Text -- 1.24: Only in inspect, not create
337 , links :: [Link]
935 , links :: [Link]
338− , oomScoreAdj :: Integer
936+ , oomScoreAdj :: Maybe Integer
339− , pidMode :: Text
937+ -- , pidMode :: Text -- 1.24: Don't see pidMode, just pidsLimit
340 , privileged :: Bool
938 , privileged :: Bool
341 , publishAllPorts :: Bool
939 , publishAllPorts :: Bool
342 , readOnlyRootfs :: Bool
940 , readOnlyRootfs :: Bool
343 , securityOpt :: [Text]
941 , securityOpt :: [Text]
344− , storageOpt :: [Text]
345− , utsMode :: UTSMode
942+ -- , utsMode :: UTSMode -- 1.24: Don't see this
346− , shmSize :: Integer
943+ , shmSize :: Maybe Integer
347− , consoleSize :: Integer
944+ -- , consoleSize :: Integer -- 1.24: Don't see this
348− , isolation :: Isolation
945+ -- , isolation :: Isolation -- 1.24: Don't see this
349 , resources :: ContainerResources
946 , resources :: ContainerResources
350 } deriving (Eq, Show, Generic)
947 } deriving (Eq, Show, Generic)
949+instance FromJSON HostConfig where
950+ parseJSON = genericParseJSON defaultOptions {
951+ fieldLabelModifier = (\(x:xs) -> toUpper x : xs)}
953+instance ToJSON HostConfig where
954+ toJSON = genericToJSON defaultOptions {
955+ fieldLabelModifier = (\(x:xs) -> toUpper x : xs)}
352-- { "Name": <name>, "Soft": <soft limit>, "Hard": <hard limit> }
958-- { "Name": <name>, "Soft": <soft limit>, "Hard": <hard limit> }
353-- { "Name": "nofile", "Soft": 1024, "Hard": 2048 }
959-- { "Name": "nofile", "Soft": 1024, "Hard": 2048 }
354data Ulimit = Ulimit {
960data Ulimit = Ulimit {
355 ulimitName :: Text
961 ulimitName :: Text
356 , ulimitSoft :: Integer
962 , ulimitSoft :: Integer
357 , ulimitHard :: Integer
963 , ulimitHard :: Integer
358− } deriving (Eq, Show)
964+ } deriving (Eq, Show, Generic)
966+instance FromJSON Ulimit where
967+ parseJSON = genericParseJSON defaultOptions {
968+ fieldLabelModifier = drop 5}
970+instance ToJSON Ulimit where
971+ toJSON = genericToJSON defaultOptions {
972+ fieldLabelModifier = drop 5}
360data ContainerResources = ContainerResources {
974data ContainerResources = ContainerResources {
361− cpuShares :: Integer
975+ cpuShares :: Maybe Integer
362− , cgroupParent :: Text
976+ -- , cgroupParent :: Text -- 1.24: Missing from inspecting container details... Going to omit for now.
363− , blkioWeight :: Text
977+ , blkioWeight :: Maybe Text
364− , blkioWeightDevice :: Text
978+ , blkioWeightDevice :: Maybe Text
365− , blkioDeviceReadBps :: Text
979+ , blkioDeviceReadBps :: Maybe Text
366− , blkioDeviceWriteBps :: Text
980+ , blkioDeviceWriteBps :: Maybe Text
367− , blkioDeviceReadIOps :: Text
981+ , blkioDeviceReadIOps :: Maybe Text
368− , blkioDeviceWriteIOps :: Text
982+ , blkioDeviceWriteIOps :: Maybe Text
369− , cpuPeriod :: Integer
983+ , cpuPeriod :: Maybe Integer
370− , cpuQuota :: Integer
984+ -- , cpuQuota :: Integer -- 1.24: Missing from inspecting container details... Going to omit for now.
371− , cpusetCpus :: Integer
985+ , cpusetCpus :: Maybe Integer
372− , cpusetMems :: Text
986+ , cpusetMems :: Maybe Text
373 , devices :: [Device]
987 , devices :: [Device]
374− , diskQuota :: Integer
988+ -- , diskQuota :: Integer -- Don't see this ins 1.24.
375− , kernelMemory :: Integer
989+ , kernelMemory :: Maybe Integer
376− , memory :: Integer
990+ , memory :: Maybe Integer
377− , memoryReservation :: Integer
991+ , memoryReservation :: Maybe Integer
378− , memorySwap :: Integer
992+ , memorySwap :: Maybe Integer
379− , memorySwappiness :: Integer
993+ -- , memorySwappiness :: Integer -- 1.24: Missing from inspecting container details... Going to omit for now.
380 , oomKillDisable :: Bool
994 , oomKillDisable :: Bool
381− , pidsLimit :: Integer
995+ -- , pidsLimit :: Integer -- 1.24: Missing from inspecting container details... Going to omit for now.
382 , ulimits :: [Ulimit]
996 , ulimits :: [Ulimit]
997+ -- TODO: Missing from 1.24
998+ -- StorageOpt :: [(Text, Text)]
999+ -- VolumeDriver :: ??
1000+ -- EndpointsConfig :: ??
1001+ -- TODO: Only in inspect container in 1.24
1002+ -- CpuPercent :: Int +
1003+ -- MaximumIOps :: Int +
1004+ -- MaximumIOBps :: Int +
1005+ -- LxcConf :: [??] +
383− } deriving (Eq, Show)
1006+ } deriving (Eq, Show, Generic)
1008+instance ToJSON ContainerResources where
1009+ toJSON = genericToJSON defaultOptions {
1010+ fieldLabelModifier = (\(x:xs) -> toUpper x : xs)}
1012+instance FromJSON ContainerResources where
1013+ parseJSON = genericParseJSON defaultOptions {
1014+ fieldLabelModifier = (\(x:xs) -> toUpper x : xs)}
385type Port = Integer
1016type Port = Integer
387type Name = Text
1018type Name = Text
388type Value = Text
1019type Value = Text
390−data EnvVar = EnvVar Name Value deriving (Eq, Show)
1021+data EnvVar = EnvVar Name Value
1022+ deriving (Eq, Show)
1024+instance FromJSON EnvVar where
1025+ parseJSON (JSON.String env) =
1026+ let (n, v') = T.break (== '=') env in
1027+ let v = T.drop 1 v' in
1028+ return $ EnvVar n v
1029+ parseJSON _ = fail "EnvVar is not a string"
1031+instance ToJSON EnvVar where
1032+ toJSON (EnvVar n v) = object [n .= v]
1034+-- | ExposedPorts represent a enumeraton of all the ports (and their type)
1035+-- that a container should expose to other containers or the host system.
1036+-- `NOTE`: This does not automatically expose the ports onto the host
1037+-- system but rathet it just tags them. It's best to be used with with
1038+-- the PublishAllPorts flag. It is also useful for
1039+-- the deamon to know wich Environment variables to
1040+-- inject into a container linking to our container.
1041+-- Example linking a Postgres container named db would inject the following
1042+-- environment variables automatically if we set the corresponding
1044+-- DB_PORT_5432_TCP_PORT="5432"
1045+-- DB_PORT_5432_TCP_PROTO="tcp"
1046+-- DB_PORT_5432_TCP="tcp://172.17.0.1:5432"
1047+newtype ExposedPorts = ExposedPorts (M.Map PortType Port) deriving (Eq, Show)
1049+instance FromJSON ExposedPorts where
1050+ parseJSON (JSON.Object o) = do
1051+ ExposedPorts <$> HM.foldlWithKey' f (return M.empty) o
1054+ f accM k _ = case T.split (== '/') k of
1055+ [port', portType'] -> do
1056+ port <- parseIntegerText port'
1057+ portType <- parseJSON $ JSON.String portType'
1059+ return $ M.insert portType port acc
1061+ fail "Could not parse ExposedPorts"
1062+ parseJSON _ = fail "ExposedPorts is not an object."
1064+instance ToJSON ExposedPorts where
1065+ toJSON (ExposedPorts kvs) = object [((T.pack $ show p) <> "/" <> (T.pack $ show t)) .= JSON.Object HM.empty | (t,p) <- (M.toList kvs)]
392data ContainerConfig = ContainerConfig {
1068data ContainerConfig = ContainerConfig {
393− hostname :: Text
1069+ hostname :: Maybe Text
394− , domainname :: Text
1070+ , domainname :: Maybe Text
395− , user :: Text
1071+ , user :: Maybe Text
396 , attachStdin :: Bool
1072 , attachStdin :: Bool
397 , attachStdout :: Bool
1073 , attachStdout :: Bool
398 , attachStderr :: Bool
1074 , attachStderr :: Bool
399− -- ExposedPorts are used for the PublishAllPorts flag
400− -- to know which ports to publish. Also it's used for
401− -- the deamon to know wich Environment variables to
402− -- inject into a container linking to our container.
403− -- Example linking a Postgres container named db:
404− -- DB_PORT_5432_TCP_PORT="5432"
405− -- DB_PORT_5432_TCP_PROTO="tcp"
406− -- DB_PORT_5432_TCP="tcp://172.17.0.1:5432"
407− , exposedPorts :: [Port]
1075+ , exposedPorts :: ExposedPorts
408− , publishService :: Text
1076+ -- , publishService :: Text -- Don't see this in 1.24
409 , tty :: Bool
1077 , tty :: Bool
410 , openStdin :: Bool
1078 , openStdin :: Bool
411 , stdinOnce :: Bool
1079 , stdinOnce :: Bool
412 , env :: [EnvVar]
1080 , env :: [EnvVar]
413− , cmd :: Text
1081+ , cmd :: [Text]
414− , argsEscaped :: Bool
1082+ -- , argsEscaped :: Bool -- Don't see this in 1.24
415 , image :: Text
1083 , image :: Text
416− , volumes :: [Volume]
1084+ , volumes :: Maybe Volumes
417− , workingDir :: FilePath
1085+ , workingDir :: Maybe FilePath
418− , entrypoint :: Text
1086+ , entrypoint :: Maybe Text -- Can be null?
419 , networkDisabled :: Bool
1087 , networkDisabled :: Bool
420− , macAddress :: Text
1088+ , macAddress :: Maybe Text
421− , onBuild :: Text
1089+ -- , onBuild :: Text -- For 1.24, only see this in the inspect response.
422− , labels :: Labels
1090+ , labels :: Maybe Labels
423 , stopSignal :: Signal
1091 , stopSignal :: Signal
424− } deriving (Eq, Show)
1092+ } deriving (Eq, Show, Generic)
1094+instance ToJSON ContainerConfig where
1095+ toJSON = genericToJSON defaultOptions {
1096+ fieldLabelModifier = (\(x:xs) -> toUpper x : xs)}
1098+instance FromJSON ContainerConfig where
1099+ parseJSON = genericParseJSON defaultOptions {
1100+ fieldLabelModifier = (\(x:xs) -> toUpper x : xs)}
1102+parseIntegerText :: (Monad m) =>Text -> m Integer
1103+parseIntegerText t = case readMaybe $ T.unpack t of
1105+ fail "Could not parse Integer"