Skip to content

Commit 3f6080c

Browse files
committed
'require-docker-version' config option
1 parent 75e118f commit 3f6080c

8 files changed

Lines changed: 66 additions & 21 deletions

File tree

ChangeLog.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ Other enhancements:
1414
* `stack setup` can now install GHCJS on windows. See [#1145](https://github.com/commercialhaskell/stack/issues/1145) and [#749](https://github.com/commercialhaskell/stack/issues/749)
1515
* `stack hpc report` command added, which generates reports for HPC tix files
1616
* `stack ghci` builds the project before launching GHCi. If the build fails, optimistically launch GHCi anyway. Use `stack ghci --no-build` option to disable [#1065](https://github.com/commercialhaskell/stack/issues/1065)
17+
* Added `require-docker-version` configuration option
1718

1819
Bug fixes:
1920

doc/docker_integration.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,10 @@ otherwise noted.
203203
# When the Docker Engine is remote (accessed by tcp), defaults to false.
204204
set-user: true
205205

206+
# Require the version of the Docker client to be within the specified
207+
# Cabal-style version range (e.g., ">= 1.6.0 && < 1.9.0")
208+
require-docker-version: "any"
209+
206210
Image Repositories
207211
-------------------------------------------------------------------------------
208212

src/Stack/Build/ConstructPlan.hs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ import qualified Data.Text as T
3434
import Data.Text.Encoding (decodeUtf8With)
3535
import Data.Text.Encoding.Error (lenientDecode)
3636
import Distribution.Package (Dependency (..))
37-
import Distribution.Version (anyVersion)
37+
import Distribution.Version (anyVersion)
3838
import Network.HTTP.Client.Conduit (HasHttpManager)
3939
import Prelude hiding (pi, writeFile)
4040
import Stack.Build.Cache

src/Stack/Config/Docker.hs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import Data.List (find)
1010
import Data.Maybe
1111
import qualified Data.Text as T
1212
import Data.Typeable (Typeable)
13+
import Distribution.Version (simplifyVersionRange)
1314
import Path
1415
import Stack.Types
1516

@@ -55,6 +56,7 @@ dockerOptsFromMonoid mproject stackRoot DockerOptsMonoid{..} = do
5556
dockerMount = dockerMonoidMount
5657
dockerEnv = dockerMonoidEnv
5758
dockerSetUser = dockerMonoidSetUser
59+
dockerRequireDockerVersion = simplifyVersionRange dockerMonoidRequireDockerVersion
5860
dockerDatabasePath <-
5961
case dockerMonoidDatabasePath of
6062
Nothing -> return $ stackRoot </> $(mkRelFile "docker.db")

src/Stack/Docker.hs

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -231,7 +231,7 @@ runContainerAndExit getCmdArgs
231231
do config <- asks getConfig
232232
let docker = configDocker config
233233
envOverride <- getEnvOverride (configPlatform config)
234-
checkDockerVersion envOverride
234+
checkDockerVersion envOverride docker
235235
(dockerHost,dockerCertPath,bamboo,jenkins) <-
236236
liftIO ((,,,) <$> lookupEnv "DOCKER_HOST"
237237
<*> lookupEnv "DOCKER_CERT_PATH"
@@ -366,8 +366,9 @@ cleanup :: M env m
366366
=> CleanupOpts -> m ()
367367
cleanup opts =
368368
do config <- asks getConfig
369+
let docker = configDocker config
369370
envOverride <- getEnvOverride (configPlatform config)
370-
checkDockerVersion envOverride
371+
checkDockerVersion envOverride docker
371372
let runDocker = readDockerProcess envOverride
372373
imagesOut <- runDocker ["images","--no-trunc","-f","dangling=false"]
373374
danglingImagesOut <- runDocker ["images","--no-trunc","-f","dangling=true"]
@@ -618,7 +619,7 @@ pull =
618619
do config <- asks getConfig
619620
let docker = configDocker config
620621
envOverride <- getEnvOverride (configPlatform config)
621-
checkDockerVersion envOverride
622+
checkDockerVersion envOverride docker
622623
pullImage envOverride docker (dockerImage docker)
623624

624625
-- | Pull Docker image from registry.
@@ -645,19 +646,21 @@ pullImage envOverride docker image =
645646
-- | Check docker version (throws exception if incorrect)
646647
checkDockerVersion
647648
:: (MonadIO m, MonadThrow m, MonadLogger m, MonadBaseControl IO m, MonadCatch m)
648-
=> EnvOverride -> m ()
649-
checkDockerVersion envOverride =
649+
=> EnvOverride -> DockerOpts -> m ()
650+
checkDockerVersion envOverride docker =
650651
do dockerExists <- doesExecutableExist envOverride "docker"
651652
unless dockerExists (throwM DockerNotInstalledException)
652653
dockerVersionOut <- readDockerProcess envOverride ["--version"]
653654
case words (decodeUtf8 dockerVersionOut) of
654-
(_:_:v:_) ->
655+
(_:_:v:_) -> do
655656
case parseVersionFromString (dropWhileEnd (not . isDigit) v) of
656657
Just v'
657658
| v' < minimumDockerVersion ->
658659
throwM (DockerTooOldException minimumDockerVersion v')
659660
| v' `elem` prohibitedDockerVersions ->
660661
throwM (DockerVersionProhibitedException prohibitedDockerVersions v')
662+
| not (v' `withinRange` dockerRequireDockerVersion docker) ->
663+
(throwM (BadDockerVersionException (dockerRequireDockerVersion docker) v'))
661664
| otherwise ->
662665
return ()
663666
_ -> throwM InvalidVersionOutputException
@@ -816,6 +819,8 @@ data StackDockerException
816819
-- ^ Installed version of @docker@ below minimum version.
817820
| DockerVersionProhibitedException [Version] Version
818821
-- ^ Installed version of @docker@ is prohibited.
822+
| BadDockerVersionException VersionRange Version
823+
-- ^ Installed version of @docker@ is out of range specified in config file.
819824
| InvalidVersionOutputException
820825
-- ^ Invalid output from @docker --version@.
821826
| HostStackTooOldException Version (Maybe Version)
@@ -864,15 +869,26 @@ instance Show StackDockerException where
864869
show (DockerTooOldException minVersion haveVersion) =
865870
concat ["Minimum docker version '"
866871
,versionString minVersion
867-
,"' is required (you have '"
872+
,"' is required by "
873+
,stackProgName
874+
," (you have '"
868875
,versionString haveVersion
869876
,"')."]
870877
show (DockerVersionProhibitedException prohibitedVersions haveVersion) =
871-
concat ["These Docker versions are prohibited (you have '"
878+
concat ["These Docker versions are incompatible with "
879+
,stackProgName
880+
," (you have '"
872881
,versionString haveVersion
873882
,"'): "
874883
,intercalate ", " (map versionString prohibitedVersions)
875884
,"."]
885+
show (BadDockerVersionException requiredRange haveVersion) =
886+
concat ["The version of 'docker' you are using ("
887+
,show haveVersion
888+
,") is outside the required\n"
889+
,"version range specified in stack.yaml ("
890+
,T.unpack (versionRangeText requiredRange)
891+
,")."]
876892
show InvalidVersionOutputException =
877893
"Cannot get Docker version (invalid 'docker --version' output)."
878894
show (HostStackTooOldException minVersion (Just hostVersion)) =

src/Stack/Options.hs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ import Data.Monoid
3535
import qualified Data.Set as Set
3636
import qualified Data.Text as T
3737
import Data.Text.Read (decimal)
38+
import Distribution.Version (anyVersion)
3839
import Options.Applicative
3940
import Options.Applicative.Args
4041
import Options.Applicative.Builder.Extra
@@ -370,6 +371,7 @@ dockerOptsParser showOptions =
370371
<*> maybeBoolFlags (dockerOptName dockerSetUserArgName)
371372
"setting user in container to match host"
372373
hide
374+
<*> pure anyVersion
373375
where
374376
dockerOptName optName = dockerCmdName ++ "-" ++ T.unpack optName
375377
maybeStrOption = optional . option str

src/Stack/Types/Config.hs

Lines changed: 2 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -898,16 +898,6 @@ configMonoidApplyGhcOptionsName = "apply-ghc-options"
898898
configMonoidAllowNewerName :: Text
899899
configMonoidAllowNewerName = "allow-newer"
900900

901-
-- | Newtype for non-orphan FromJSON instance.
902-
newtype VersionRangeJSON = VersionRangeJSON { unVersionRangeJSON :: VersionRange }
903-
904-
-- | Parse VersionRange.
905-
instance FromJSON VersionRangeJSON where
906-
parseJSON = withText "VersionRange"
907-
(\s -> maybe (fail ("Invalid cabal-style VersionRange: " ++ T.unpack s))
908-
(return . VersionRangeJSON)
909-
(Distribution.Text.simpleParse (T.unpack s)))
910-
911901
data ConfigException
912902
= ParseConfigFileException (Path Abs File) ParseException
913903
| ParseResolverException Text
@@ -951,9 +941,9 @@ instance Show ConfigException where
951941
[ "The version of stack you are using ("
952942
, show (fromCabalVersion Meta.version)
953943
, ") is outside the required\n"
954-
,"version range ("
944+
,"version range specified in stack.yaml ("
955945
, T.unpack (versionRangeText requiredRange)
956-
, ") specified in stack.yaml." ]
946+
, ")." ]
957947
show (NoMatchingSnapshot names) = concat
958948
[ "There was no snapshot found that matched the package "
959949
, "bounds in your .cabal files.\n"

src/Stack/Types/Docker.hs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,11 @@ import Control.Monad.Catch (MonadThrow)
1010
import Data.Aeson.Extended
1111
import Data.Monoid
1212
import Data.Text (Text)
13+
import qualified Data.Text as T
14+
import Distribution.Text (simpleParse)
15+
import Distribution.Version (anyVersion)
1316
import Path
17+
import Stack.Types.Version
1418

1519
-- | Docker configuration.
1620
data DockerOpts = DockerOpts
@@ -46,6 +50,8 @@ data DockerOpts = DockerOpts
4650
-- ^ Location of container-compatible stack executable
4751
,dockerSetUser :: !(Maybe Bool)
4852
-- ^ Set in-container user to match host's
53+
,dockerRequireDockerVersion :: !VersionRange
54+
-- ^ Require a version of Docker within this range.
4955
}
5056
deriving (Show)
5157

@@ -86,6 +92,8 @@ data DockerOptsMonoid = DockerOptsMonoid
8692
-- ^ Location of container-compatible stack executable
8793
,dockerMonoidSetUser :: !(Maybe Bool)
8894
-- ^ Set in-container user to match host's
95+
,dockerMonoidRequireDockerVersion :: !VersionRange
96+
-- ^ See: 'dockerRequireDockerVersion'
8997
}
9098
deriving (Show)
9199

@@ -110,6 +118,10 @@ instance FromJSON (DockerOptsMonoid, [JSONWarning]) where
110118
dockerMonoidDatabasePath <- o ..:? dockerDatabasePathArgName
111119
dockerMonoidStackExe <- o ..:? dockerStackExeArgName
112120
dockerMonoidSetUser <- o ..:? dockerSetUserArgName
121+
dockerMonoidRequireDockerVersion
122+
<- unVersionRangeJSON <$>
123+
o ..:? dockerRequireDockerVersionArgName
124+
..!= VersionRangeJSON anyVersion
113125
return DockerOptsMonoid{..})
114126

115127
-- | Left-biased combine Docker options
@@ -131,6 +143,7 @@ instance Monoid DockerOptsMonoid where
131143
,dockerMonoidDatabasePath = Nothing
132144
,dockerMonoidStackExe = Nothing
133145
,dockerMonoidSetUser = Nothing
146+
,dockerMonoidRequireDockerVersion = anyVersion
134147
}
135148
mappend l r = DockerOptsMonoid
136149
{dockerMonoidDefaultEnable = dockerMonoidDefaultEnable l || dockerMonoidDefaultEnable r
@@ -149,6 +162,9 @@ instance Monoid DockerOptsMonoid where
149162
,dockerMonoidDatabasePath = dockerMonoidDatabasePath l <|> dockerMonoidDatabasePath r
150163
,dockerMonoidStackExe = dockerMonoidStackExe l <|> dockerMonoidStackExe r
151164
,dockerMonoidSetUser = dockerMonoidSetUser l <|> dockerMonoidSetUser r
165+
,dockerMonoidRequireDockerVersion
166+
= intersectVersionRanges (dockerMonoidRequireDockerVersion l)
167+
(dockerMonoidRequireDockerVersion r)
152168
}
153169

154170
-- | Where to get the `stack` executable to run in Docker containers
@@ -194,6 +210,16 @@ data DockerMonoidRepoOrImage
194210
| DockerMonoidImage String
195211
deriving (Show)
196212

213+
-- | Newtype for non-orphan FromJSON instance.
214+
newtype VersionRangeJSON = VersionRangeJSON { unVersionRangeJSON :: VersionRange }
215+
216+
-- | Parse VersionRange.
217+
instance FromJSON VersionRangeJSON where
218+
parseJSON = withText "VersionRange"
219+
(\s -> maybe (fail ("Invalid cabal-style VersionRange: " ++ T.unpack s))
220+
(return . VersionRangeJSON)
221+
(Distribution.Text.simpleParse (T.unpack s)))
222+
197223
-- | Docker enable argument name.
198224
dockerEnableArgName :: Text
199225
dockerEnableArgName = "enable"
@@ -269,3 +295,7 @@ dockerStackExeImageVal = "image"
269295
-- | Docker @set-user@ argument name
270296
dockerSetUserArgName :: Text
271297
dockerSetUserArgName = "set-user"
298+
299+
-- | Docker @require-version@ argument name
300+
dockerRequireDockerVersionArgName :: Text
301+
dockerRequireDockerVersionArgName = "require-docker-version"

0 commit comments

Comments
 (0)