language-docker 10.0.2 → 10.1.0
raw patch · 7 files changed
+571/−339 lines, 7 files
Files
- language-docker.cabal +4/−2
- src/Language/Docker/Parser/Arguments.hs +10/−2
- src/Language/Docker/Parser/Copy.hs +20/−7
- src/Language/Docker/Parser/Prelude.hs +65/−4
- test/Language/Docker/ParseCopySpec.hs +138/−0
- test/Language/Docker/ParseRunSpec.hs +334/−0
- test/Language/Docker/ParserSpec.hs +0/−324
language-docker.cabal view
@@ -4,10 +4,10 @@ -- -- see: https://github.com/sol/hpack ----- hash: 0acabc7fcd899e8d19189dd6a3e3541b451fb5e35c3ed9751f8f0261404d8197+-- hash: 3b38c6eec0fc0543e696e51fa5a654074e9718ae41bd50f4114824964938c535 name: language-docker-version: 10.0.2+version: 10.1.0 synopsis: Dockerfile parser, pretty-printer and embedded DSL description: All functions for parsing and pretty-printing Dockerfiles are exported through @Language.Docker@. For more fine-grained operations look for specific modules that implement a certain functionality. See the <https://github.com/hadolint/language-docker GitHub project> for the source-code and examples.@@ -81,8 +81,10 @@ main-is: Spec.hs other-modules: Language.Docker.IntegrationSpec+ Language.Docker.ParseCopySpec Language.Docker.ParsePragmaSpec Language.Docker.ParserSpec+ Language.Docker.ParseRunSpec Language.Docker.PrettyPrintSpec TestHelper Paths_language_docker
src/Language/Docker/Parser/Arguments.hs view
@@ -15,9 +15,17 @@ -- Parse arguments of a command in the shell form argumentsShell :: (?esc :: Char) => Parser (Arguments Text)-argumentsShell = ArgumentsText <$> toEnd+argumentsShell =+ try (ArgumentsText <$> untilHeredoc)+ <|> (ArgumentsText <$> toEnd) where toEnd = untilEol "the shell arguments" +-- Parse arguments of a command in the heredoc format+argumentsHeredoc :: Parser (Arguments Text)+argumentsHeredoc = ArgumentsText <$> heredoc+ arguments :: (?esc :: Char) => Parser (Arguments Text)-arguments = try argumentsExec <|> try argumentsShell+arguments = try argumentsHeredoc+ <|> try argumentsExec+ <|> try argumentsShell
src/Language/Docker/Parser/Copy.hs view
@@ -42,12 +42,13 @@ case sourceFlags of [] -> NoSource f : _ -> f- fileList "COPY" (\src dest -> Copy (CopyArgs src dest cho chm fr))+ try (heredocList (\src dest -> Copy (CopyArgs src dest cho chm fr)))+ <|> fileList "COPY" (\src dest -> Copy (CopyArgs src dest cho chm fr)) parseAdd :: (?esc :: Char) => Parser (Instruction Text) parseAdd = do reserved "ADD"- flags <- copyFlag `sepEndBy` requiredWhitespace+ flags <- addFlag `sepEndBy` requiredWhitespace let chownFlags = [c | FlagChown c <- flags] let chmodFlags = [c | FlagChmod c <- flags] let invalidFlags = [i | FlagInvalid i <- flags]@@ -66,6 +67,17 @@ c : _ -> c fileList "ADD" (\src dest -> Add (AddArgs src dest cho chm)) +heredocList :: (?esc :: Char) =>+ (NonEmpty SourcePath -> TargetPath -> Instruction Text) ->+ Parser (Instruction Text)+heredocList constr = do+ markers <- try (spaceSep1 heredocMarker) <?> "a list of heredoc markers"+ target <- untilEol "target path"+ case reverse markers of+ [] -> customError $ FileListError "empty list of heredoc markers"+ m:_ -> void $ heredocContent m+ return $ constr (SourcePath <$> fromList markers) (TargetPath target)+ fileList :: (?esc :: Char) => Text -> (NonEmpty SourcePath -> TargetPath -> Instruction Text) -> Parser (Instruction Text)@@ -86,11 +98,12 @@ unexpectedFlag name _ = customFailure $ InvalidFlagError (T.unpack name) copyFlag :: (?esc :: Char) => Parser CopyFlag-copyFlag =- (FlagChown <$> try chown <?> "only one --chown")- <|> (FlagChmod <$> try chmod <?> "only one --chmod")- <|> (FlagSource <$> try copySource <?> "only one --from")- <|> (FlagInvalid <$> try anyFlag <?> "no other flags")+copyFlag = (FlagSource <$> try copySource <?> "only one --from") <|> addFlag++addFlag :: (?esc :: Char) => Parser CopyFlag+addFlag = (FlagChown <$> try chown <?> "--chown")+ <|> (FlagChmod <$> try chmod <?> "--chmod")+ <|> (FlagInvalid <$> try anyFlag <?> "other flag") chown :: (?esc :: Char) => Parser Chown chown = do
src/Language/Docker/Parser/Prelude.hs view
@@ -7,11 +7,16 @@ reserved, natural, commaSep,+ spaceSep1, stringLiteral, brackets,+ heredoc,+ heredocMarker,+ heredocContent, whitespace, requiredWhitespace, untilEol,+ untilHeredoc, symbol, onlySpaces, onlyWhitespaces,@@ -126,14 +131,70 @@ commaSep :: (?esc :: Char) => Parser a -> Parser [a] commaSep p = sepBy (p <* whitespace) (symbol ",") +spaceSep1 :: Parser a -> Parser [a]+spaceSep1 p = sepEndBy1 p onlySpaces++-- | Note this is just an alias for compatibility stringLiteral :: Parser Text-stringLiteral = do- void (char '"')- lit <- manyTill L.charLiteral (char '"')- return (T.pack lit)+stringLiteral = doubleQuotedString +singleQuotedString :: Parser Text+singleQuotedString = quotedString '\''++doubleQuotedString :: Parser Text+doubleQuotedString = quotedString '\"'++quotedString :: Char -> Parser Text+quotedString c = do+ void $ char c+ lit <- manyTill L.charLiteral (char c)+ return $ T.pack lit+ brackets :: (?esc :: Char) => Parser a -> Parser a brackets = between (symbol "[" *> whitespace) (whitespace *> symbol "]")++justWhitespace :: Parser Text+justWhitespace = do+ c <- choice+ [ char ' ',+ char '\t',+ char '\n'+ ]+ return (T.pack [c])++untilWS :: Parser Text+untilWS = do+ s <- manyTill L.charLiteral justWhitespace+ return $ T.pack s++heredocMarker :: Parser Text+heredocMarker = do+ void $ string "<<"+ void $ takeWhileP (Just "dash") (== '-')+ m <- try doubleQuotedString <|> try singleQuotedString <|> untilWS+ optional heredocRedirect+ pure m++heredocRedirect :: Parser Text+heredocRedirect = do+ void $ char '>'+ takeWhileP (Just "heredoc path") (/= '\n')++heredocContent :: Text -> Parser Text+heredocContent marker = do+ doc <- manyTill L.charLiteral (string marker)+ return $ T.strip $ T.pack doc++heredoc :: Parser Text+heredoc = do+ m <- heredocMarker+ heredocContent m++-- | Parses text until a heredoc is found. Will also consume the heredoc.+untilHeredoc :: Parser Text+untilHeredoc = do+ txt <- manyTill L.charLiteral heredoc+ return $ T.strip $ T.pack txt onlySpaces :: Parser Text onlySpaces = takeWhileP (Just "spaces") (\c -> c == ' ' || c == '\t')
+ test/Language/Docker/ParseCopySpec.hs view
@@ -0,0 +1,138 @@+module Language.Docker.ParseCopySpec where++import qualified Data.Text as Text+import Language.Docker.Parser+import Language.Docker.Syntax+import TestHelper+import Test.HUnit hiding (Label)+import Test.Hspec+++spec :: Spec+spec = do+ describe "regular Copy" $ do+ it "simple COPY" $+ let file = Text.unlines ["COPY . /app", "COPY baz /some/long/path"]+ in assertAst+ file+ [ Copy $ CopyArgs [SourcePath "."] (TargetPath "/app") NoChown NoChmod NoSource,+ Copy $ CopyArgs [SourcePath "baz"] (TargetPath "/some/long/path") NoChown NoChmod NoSource+ ]+ it "multifiles COPY" $+ let file = Text.unlines ["COPY foo bar baz /app"]+ in assertAst+ file+ [ Copy $ CopyArgs (fmap SourcePath ["foo", "bar", "baz"]) (TargetPath "/app") NoChown NoChmod NoSource+ ]+ it "list of quoted files" $+ let file = Text.unlines ["COPY [\"foo\", \"bar\", \"baz\", \"/app\"]"]+ in assertAst+ file+ [ Copy $ CopyArgs (fmap SourcePath ["foo", "bar", "baz"]) (TargetPath "/app") NoChown NoChmod NoSource+ ]+ it "supports windows paths" $+ let file = Text.unlines ["COPY C:\\\\go C:\\\\go"]+ in assertAst+ file+ [ Copy $ CopyArgs (fmap SourcePath ["C:\\\\go"]) (TargetPath "C:\\\\go") NoChown NoChmod NoSource+ ]+ it "does not get confused with trailing whitespace" $+ let file = Text.unlines ["COPY a b "]+ in assertAst+ file+ [ Copy $ CopyArgs [SourcePath "a"] (TargetPath "b") NoChown NoChmod NoSource+ ]++ describe "Copy with flags" $ do+ it "with chown flag" $+ let file = Text.unlines ["COPY --chown=user:group foo bar"]+ in assertAst+ file+ [ Copy $ CopyArgs (fmap SourcePath ["foo"]) (TargetPath "bar") (Chown "user:group") NoChmod NoSource+ ]+ it "with chmod flag" $+ let file = Text.unlines ["COPY --chmod=777 foo bar"]+ in assertAst+ file+ [ Copy $ CopyArgs (fmap SourcePath ["foo"]) (TargetPath "bar") NoChown (Chmod "777") NoSource+ ]+ it "with from flag" $+ let file = Text.unlines ["COPY --from=node foo bar"]+ in assertAst+ file+ [ Copy $ CopyArgs (fmap SourcePath ["foo"]) (TargetPath "bar") NoChown NoChmod (CopySource "node")+ ]+ it "with all three flags" $+ let file = Text.unlines ["COPY --from=node --chmod=751 --chown=user:group foo bar"]+ in assertAst+ file+ [ Copy $ CopyArgs (fmap SourcePath ["foo"]) (TargetPath "bar") (Chown "user:group") (Chmod "751") (CopySource "node")+ ]+ it "with all three flags in different order" $+ let file = Text.unlines ["COPY --chown=user:group --from=node --chmod=644 foo bar"]+ in assertAst+ file+ [ Copy $ CopyArgs (fmap SourcePath ["foo"]) (TargetPath "bar") (Chown "user:group") (Chmod "644") (CopySource "node")+ ]++ describe "Copy with Heredocs" $ do+ it "empty heredoc" $+ let file = Text.unlines ["COPY <<EOF /target", "EOF"]+ in assertAst+ file+ [ Copy $ CopyArgs [SourcePath "EOF"] (TargetPath "/target") NoChown NoChmod NoSource+ ]+ it "foo heredoc" $+ let file = Text.unlines ["COPY <<FOO /target", "foo", "FOO"]+ in assertAst+ file+ [ Copy $ CopyArgs [SourcePath "FOO"] (TargetPath "/target") NoChown NoChmod NoSource+ ]+ it "foo heredoc lowercase +extensions" $+ let file = Text.unlines ["COPY <<foo.txt /target", "foo content", "line 2", "foo.txt"]+ in assertAst+ file+ [ Copy $ CopyArgs [SourcePath "foo.txt"] (TargetPath "/target") NoChown NoChmod NoSource+ ]+ it "foo heredoc single quoted marker" $+ let file = Text.unlines ["COPY <<\'FOO\' /target", "foo", "FOO"]+ in assertAst+ file+ [ Copy $ CopyArgs [SourcePath "FOO"] (TargetPath "/target") NoChown NoChmod NoSource+ ]+ it "foo heredoc double quoted marker" $+ let file = Text.unlines ["COPY <<\"FOO\" /target", "foo", "FOO"]+ in assertAst+ file+ [ Copy $ CopyArgs [SourcePath "FOO"] (TargetPath "/target") NoChown NoChmod NoSource+ ]+ it "foo heredoc +dash" $+ let file = Text.unlines ["COPY <<-FOO /target", "foo", "FOO"]+ in assertAst+ file+ [ Copy $ CopyArgs [SourcePath "FOO"] (TargetPath "/target") NoChown NoChmod NoSource+ ]+ it "foo heredoc single quoted marker +dash" $+ let file = Text.unlines ["COPY <<-\'FOO\' /target", "foo", "FOO"]+ in assertAst+ file+ [ Copy $ CopyArgs [SourcePath "FOO"] (TargetPath "/target") NoChown NoChmod NoSource+ ]+ it "foo heredoc double quoted marker +dash" $+ let file = Text.unlines ["COPY <<-\"FOO\" /target", "foo", "FOO"]+ in assertAst+ file+ [ Copy $ CopyArgs [SourcePath "FOO"] (TargetPath "/target") NoChown NoChmod NoSource+ ]+ it "multiple heredocs" $+ let file = Text.unlines ["COPY <<FOO <<BAR /target", "foo", "FOO", "bar", "BAR"]+ in assertAst+ file+ [ Copy $ CopyArgs [SourcePath "FOO", SourcePath "BAR"] (TargetPath "/target") NoChown NoChmod NoSource+ ]+ it "multiple heredocs with single/double quotes and dash mixed" $+ let file = Text.unlines ["COPY <<FOO <<-\"BAR\" <<\'FIZZ\' <<-BUZZ /target", "foo", "FOO", "bar", "BAR", "fizz", "FIZZ", "buss", "BUZZ"]+ in assertAst+ file+ [ Copy $ CopyArgs [SourcePath "FOO", SourcePath "BAR", SourcePath "FIZZ", SourcePath "BUZZ"] (TargetPath "/target") NoChown NoChmod NoSource+ ]
+ test/Language/Docker/ParseRunSpec.hs view
@@ -0,0 +1,334 @@+module Language.Docker.ParseRunSpec where++import Data.Default.Class (def)+import qualified Data.Text as Text+import Language.Docker.Parser+import Language.Docker.Syntax+import TestHelper+import Test.HUnit hiding (Label)+import Test.Hspec+++spec :: Spec+spec = do+ describe "parse RUN instructions" $ do+ it "escaped with space before" $+ let dockerfile = Text.unlines ["RUN yum install -y \\", "imagemagick \\", "mysql"]+ in assertAst dockerfile [Run "yum install -y imagemagick mysql"]+ it "does not choke on unmatched brackets" $+ let dockerfile = Text.unlines ["RUN [foo"]+ in assertAst dockerfile [Run "[foo"]+ it "Distinguishes between text and a list" $+ let dockerfile =+ Text.unlines+ [ "RUN echo foo",+ "RUN [\"echo\", \"foo\"]"+ ]+ in assertAst dockerfile [Run $ RunArgs (ArgumentsText "echo foo") def, Run $ RunArgs (ArgumentsList "echo foo") def]+ it "Accepts spaces inside the brackets" $+ let dockerfile =+ Text.unlines+ [ "RUN [ \"echo\", \"foo\" ]"+ ]+ in assertAst dockerfile [Run $ RunArgs (ArgumentsList "echo foo") def]++ describe "RUN with experimental flags" $ do+ it "--mount=type=bind and target" $+ let file = Text.unlines ["RUN --mount=type=bind,target=/foo echo foo"]+ flags = def {mount = Just $ BindMount (def {bTarget = "/foo"})}+ in assertAst+ file+ [ Run $ RunArgs (ArgumentsText "echo foo") flags+ ]+ it "--mount default to bind" $+ let file = Text.unlines ["RUN --mount=target=/foo echo foo"]+ flags = def {mount = Just $ BindMount (def {bTarget = "/foo"})}+ in assertAst+ file+ [ Run $ RunArgs (ArgumentsText "echo foo") flags+ ]+ it "--mount=type=bind all modifiers" $+ let file = Text.unlines ["RUN --mount=type=bind,target=/foo,source=/bar,from=ubuntu,ro echo foo"]+ flags = def {mount = Just $ BindMount (BindOpts {bTarget = "/foo", bSource = Just "/bar", bFromImage = Just "ubuntu", bReadOnly = Just True})}+ in assertAst+ file+ [ Run $ RunArgs (ArgumentsText "echo foo") flags+ ]+ it "--mount=type=cache with target" $+ let file =+ Text.unlines+ [ "RUN --mount=type=cache,target=/foo echo foo",+ "RUN --mount=type=cache,target=/bar echo foo",+ "RUN --mount=type=cache,target=/baz echo foo"+ ]+ flags1 = def {mount = Just $ CacheMount (def {cTarget = "/foo"})}+ flags2 = def {mount = Just $ CacheMount (def {cTarget = "/bar"})}+ flags3 = def {mount = Just $ CacheMount (def {cTarget = "/baz"})}+ in assertAst+ file+ [ Run $ RunArgs (ArgumentsText "echo foo") flags1,+ Run $ RunArgs (ArgumentsText "echo foo") flags2,+ Run $ RunArgs (ArgumentsText "echo foo") flags3+ ]+ it "--mount=type=cache with all modifiers" $+ let file =+ Text.unlines+ [ "RUN --mount=type=cache,target=/foo,sharing=private,id=a,ro,from=ubuntu,source=/bar,mode=0700,uid=0,gid=0 echo foo"+ ]+ flags =+ def+ { mount =+ Just $+ CacheMount+ ( def+ { cTarget = "/foo",+ cSharing = Just Private,+ cCacheId = Just "a",+ cReadOnly = Just True,+ cFromImage = Just "ubuntu",+ cSource = Just "/bar",+ cMode = Just "0700",+ cUid = Just 0,+ cGid = Just 0+ }+ )+ }+ in assertAst+ file+ [ Run $ RunArgs (ArgumentsText "echo foo") flags+ ]+ it "--mount=type=tmpfs" $+ let file = Text.unlines ["RUN --mount=type=tmpfs,target=/foo echo foo"]+ flags = def {mount = Just $ TmpfsMount (def {tTarget = "/foo"})}+ in assertAst+ file+ [ Run $ RunArgs (ArgumentsText "echo foo") flags+ ]+ it "--mount=type=ssh" $+ let file = Text.unlines ["RUN --mount=type=ssh echo foo"]+ flags = def {mount = Just $ SshMount def}+ in assertAst+ file+ [ Run $ RunArgs (ArgumentsText "echo foo") flags+ ]+ it "--mount=type=ssh,required=false" $+ let file = Text.unlines ["RUN --mount=type=ssh,required=false echo foo"]+ flags = def {mount = Just $ SshMount def {sIsRequired = Just False}}+ in assertAst+ file+ [ Run $ RunArgs (ArgumentsText "echo foo") flags+ ]+ it "--mount=type=ssh,required=False" $+ let file = Text.unlines ["RUN --mount=type=ssh,required=False echo foo"]+ flags = def {mount = Just $ SshMount def {sIsRequired = Just False}}+ in assertAst+ file+ [ Run $ RunArgs (ArgumentsText "echo foo") flags+ ]+ it "--mount=type=secret,required=true" $+ let file = Text.unlines ["RUN --mount=type=secret,required=true echo foo"]+ flags = def {mount = Just $ SecretMount def {sIsRequired = Just True}}+ in assertAst+ file+ [ Run $ RunArgs (ArgumentsText "echo foo") flags+ ]+ it "--mount=type=secret,required=True" $+ let file = Text.unlines ["RUN --mount=type=secret,required=True echo foo"]+ flags = def {mount = Just $ SecretMount def {sIsRequired = Just True}}+ in assertAst+ file+ [ Run $ RunArgs (ArgumentsText "echo foo") flags+ ]+ it "--mount=type=ssh all modifiers" $+ let file = Text.unlines ["RUN --mount=type=ssh,target=/foo,id=a,required,source=/bar,mode=0700,uid=0,gid=0 echo foo"]+ flags =+ def+ { mount =+ Just $+ SshMount+ ( def+ { sTarget = Just "/foo",+ sCacheId = Just "a",+ sIsRequired = Just True,+ sSource = Just "/bar",+ sMode = Just "0700",+ sUid = Just 0,+ sGid = Just 0+ }+ )+ }+ in assertAst+ file+ [ Run $ RunArgs (ArgumentsText "echo foo") flags+ ]+ it "--mount=type=ssh all modifiers, required explicit" $+ let file = Text.unlines ["RUN --mount=type=ssh,target=/foo,id=a,required=true,source=/bar,mode=0700,uid=0,gid=0 echo foo"]+ flags =+ def+ { mount =+ Just $+ SshMount+ ( def+ { sTarget = Just "/foo",+ sCacheId = Just "a",+ sIsRequired = Just True,+ sSource = Just "/bar",+ sMode = Just "0700",+ sUid = Just 0,+ sGid = Just 0+ }+ )+ }+ in assertAst+ file+ [ Run $ RunArgs (ArgumentsText "echo foo") flags+ ]+ it "--mount=type=secret all modifiers" $+ let file = Text.unlines ["RUN --mount=type=secret,target=/foo,id=a,required,source=/bar,mode=0700,uid=0,gid=0 echo foo"]+ flags =+ def+ { mount =+ Just $+ SecretMount+ ( def+ { sTarget = Just "/foo",+ sCacheId = Just "a",+ sIsRequired = Just True,+ sSource = Just "/bar",+ sMode = Just "0700",+ sUid = Just 0,+ sGid = Just 0+ }+ )+ }+ in assertAst+ file+ [ Run $ RunArgs (ArgumentsText "echo foo") flags+ ]+ it "--mount=type=secret all modifiers, required explicit" $+ let file = Text.unlines ["RUN --mount=type=secret,target=/foo,id=a,required=true,source=/bar,mode=0700,uid=0,gid=0 echo foo"]+ flags =+ def+ { mount =+ Just $+ SecretMount+ ( def+ { sTarget = Just "/foo",+ sCacheId = Just "a",+ sIsRequired = Just True,+ sSource = Just "/bar",+ sMode = Just "0700",+ sUid = Just 0,+ sGid = Just 0+ }+ )+ }+ in assertAst+ file+ [ Run $ RunArgs (ArgumentsText "echo foo") flags+ ]+ it "--network=none" $+ let file = Text.unlines ["RUN --network=none echo foo"]+ flags = def {network = Just NetworkNone}+ in assertAst+ file+ [ Run $ RunArgs (ArgumentsText "echo foo") flags+ ]+ it "--network=host" $+ let file = Text.unlines ["RUN --network=host echo foo"]+ flags = def {network = Just NetworkHost}+ in assertAst+ file+ [ Run $ RunArgs (ArgumentsText "echo foo") flags+ ]+ it "--network=default" $+ let file = Text.unlines ["RUN --network=default echo foo"]+ flags = def {network = Just NetworkDefault}+ in assertAst+ file+ [ Run $ RunArgs (ArgumentsText "echo foo") flags+ ]+ it "--security=insecure" $+ let file = Text.unlines ["RUN --security=insecure echo foo"]+ flags = def {security = Just Insecure}+ in assertAst+ file+ [ Run $ RunArgs (ArgumentsText "echo foo") flags+ ]+ it "--security=sandbox" $+ let file = Text.unlines ["RUN --security=sandbox echo foo"]+ flags = def {security = Just Sandbox}+ in assertAst+ file+ [ Run $ RunArgs (ArgumentsText "echo foo") flags+ ]+ it "allows all flags" $+ let file = Text.unlines ["RUN --mount=target=/foo --network=none --security=sandbox echo foo"]+ flags =+ def+ { security = Just Sandbox,+ network = Just NetworkNone,+ mount = Just $ BindMount $ def {bTarget = "/foo"}+ }+ in assertAst+ file+ [ Run $ RunArgs (ArgumentsText "echo foo") flags+ ]++ describe "parse RUN in heredocs format" $ do+ it "foo heredoc" $+ let file = Text.unlines [ "RUN <<EOF", "foo", "EOF"]+ flags = def { security = Nothing }+ in assertAst file [ Run $ RunArgs (ArgumentsText "foo") flags ]+ it "foo heredoc marker" $+ let file = Text.unlines [ "RUN <<FOO", "foo", "FOO"]+ flags = def { security = Nothing }+ in assertAst file [ Run $ RunArgs (ArgumentsText "foo") flags ]+ it "foo heredoc quoted marker" $+ let file = Text.unlines [ "RUN <<\"FOO\"", "foo", "FOO"]+ flags = def { security = Nothing }+ in assertAst file [ Run $ RunArgs (ArgumentsText "foo") flags ]+ it "foo heredoc single quoted marker" $+ let file = Text.unlines [ "RUN <<\'FOO\'", "foo", "FOO"]+ flags = def { security = Nothing }+ in assertAst file [ Run $ RunArgs (ArgumentsText "foo") flags ]+ it "foo heredoc +dash" $+ let file = Text.unlines [ "RUN <<-FOO", "foo", "FOO"]+ flags = def { security = Nothing }+ in assertAst file [ Run $ RunArgs (ArgumentsText "foo") flags ]+ it "foo heredoc quoted +dash" $+ let file = Text.unlines [ "RUN <<-\"FOO\"", "foo", "FOO"]+ flags = def { security = Nothing }+ in assertAst file [ Run $ RunArgs (ArgumentsText "foo") flags ]+ it "foo heredoc single quoted +dash" $+ let file = Text.unlines [ "RUN <<-\'FOO\'", "foo", "FOO"]+ flags = def { security = Nothing }+ in assertAst file [ Run $ RunArgs (ArgumentsText "foo") flags ]+ it "multiline foo heredoc" $+ let file = Text.unlines [ "RUN <<EOF", "foo", "bar", "", "dodo", "EOF"]+ flags = def { security = Nothing }+ in assertAst file [ Run $ RunArgs (ArgumentsText "foo\nbar\n\ndodo") flags ]+ it "heredoc with shebang/commend" $+ let file = Text.unlines [ "RUN <<EOF", "#/usr/bin/env python", "", "#print foo", "print(\"foo\")", "EOF" ]+ flags = def { security = Nothing }+ in assertAst file [ Run $ RunArgs (ArgumentsText "#/usr/bin/env python\n\n#print foo\nprint(\"foo\")") flags ]+ it "empty heredoc" $+ let file = Text.unlines [ "RUN <<EOF", "EOF"]+ flags = def { security = Nothing }+ in assertAst file [ Run $ RunArgs (ArgumentsText "") flags ]+ it "evil heredoc" $+ let file = Text.unlines [ "RUN <<EOF foo", "bar EOF"]+ flags = def { security = Nothing }+ in assertAst file [ Run $ RunArgs (ArgumentsText "foo\nbar") flags ]+ it "heredoc with redirection to file" $+ let file = Text.unlines [ "RUN <<EOF > /file", "foo", "EOF" ]+ flags = def {security = Nothing }+ in assertAst file [ Run $ RunArgs (ArgumentsText "foo") flags ]+ it "heredoc to program stdin" $+ let file = Text.unlines [ "RUN python <<EOF", "print(\"foo\")", "EOF" ]+ flags = def {security = Nothing }+ in assertAst file [ Run $ RunArgs (ArgumentsText "python") flags ]+ it "heredoc to program stdin with redirect to file" $+ let file = Text.unlines [ "RUN python <<EOF > /file", "print(\"foo\")", "EOF" ]+ flags = def {security = Nothing }+ in assertAst file [ Run $ RunArgs (ArgumentsText "python") flags ]
test/Language/Docker/ParserSpec.hs view
@@ -174,26 +174,6 @@ [ Env [("PHP_FPM_ACCESS_FORMAT", "%R - %u %t \"%m %r\" %s")] ] in assertAst dockerfile ast- describe "parse RUN" $ do- it "escaped with space before" $- let dockerfile = Text.unlines ["RUN yum install -y \\", "imagemagick \\", "mysql"]- in assertAst dockerfile [Run "yum install -y imagemagick mysql"]- it "does not choke on unmatched brackets" $- let dockerfile = Text.unlines ["RUN [foo"]- in assertAst dockerfile [Run "[foo"]- it "Distinguishes between text and a list" $- let dockerfile =- Text.unlines- [ "RUN echo foo",- "RUN [\"echo\", \"foo\"]"- ]- in assertAst dockerfile [Run $ RunArgs (ArgumentsText "echo foo") def, Run $ RunArgs (ArgumentsList "echo foo") def]- it "Accepts spaces inside the brackets" $- let dockerfile =- Text.unlines- [ "RUN [ \"echo\", \"foo\" ]"- ]- in assertAst dockerfile [Run $ RunArgs (ArgumentsList "echo foo") def] describe "parse CMD" $ do it "one line cmd" $ assertAst "CMD true" [Cmd "true"] it "cmd over several lines" $@@ -413,308 +393,4 @@ in assertAst file [ Add $ AddArgs (fmap SourcePath ["foo", "bar", "baz"]) (TargetPath "/app") (Chown "user:group") NoChmod- ]- describe "COPY" $ do- it "simple COPY" $- let file = Text.unlines ["COPY . /app", "COPY baz /some/long/path"]- in assertAst- file- [ Copy $ CopyArgs [SourcePath "."] (TargetPath "/app") NoChown NoChmod NoSource,- Copy $ CopyArgs [SourcePath "baz"] (TargetPath "/some/long/path") NoChown NoChmod NoSource- ]- it "multifiles COPY" $- let file = Text.unlines ["COPY foo bar baz /app"]- in assertAst- file- [ Copy $ CopyArgs (fmap SourcePath ["foo", "bar", "baz"]) (TargetPath "/app") NoChown NoChmod NoSource- ]- it "list of quoted files" $- let file = Text.unlines ["COPY [\"foo\", \"bar\", \"baz\", \"/app\"]"]- in assertAst- file- [ Copy $ CopyArgs (fmap SourcePath ["foo", "bar", "baz"]) (TargetPath "/app") NoChown NoChmod NoSource- ]- it "with chown flag" $- let file = Text.unlines ["COPY --chown=user:group foo bar"]- in assertAst- file- [ Copy $ CopyArgs (fmap SourcePath ["foo"]) (TargetPath "bar") (Chown "user:group") NoChmod NoSource- ]- it "with chmod flag" $- let file = Text.unlines ["COPY --chmod=777 foo bar"]- in assertAst- file- [ Copy $ CopyArgs (fmap SourcePath ["foo"]) (TargetPath "bar") NoChown (Chmod "777") NoSource- ]- it "with from flag" $- let file = Text.unlines ["COPY --from=node foo bar"]- in assertAst- file- [ Copy $ CopyArgs (fmap SourcePath ["foo"]) (TargetPath "bar") NoChown NoChmod (CopySource "node")- ]- it "with all three flags" $- let file = Text.unlines ["COPY --from=node --chmod=751 --chown=user:group foo bar"]- in assertAst- file- [ Copy $ CopyArgs (fmap SourcePath ["foo"]) (TargetPath "bar") (Chown "user:group") (Chmod "751") (CopySource "node")- ]- it "with all three flags in different order" $- let file = Text.unlines ["COPY --chown=user:group --from=node --chmod=644 foo bar"]- in assertAst- file- [ Copy $ CopyArgs (fmap SourcePath ["foo"]) (TargetPath "bar") (Chown "user:group") (Chmod "644") (CopySource "node")- ]- it "supports windows paths" $- let file = Text.unlines ["COPY C:\\\\go C:\\\\go"]- in assertAst- file- [ Copy $ CopyArgs (fmap SourcePath ["C:\\\\go"]) (TargetPath "C:\\\\go") NoChown NoChmod NoSource- ]- it "does not get confused with trailing whitespace" $- let file = Text.unlines ["COPY a b "]- in assertAst- file- [ Copy $ CopyArgs [SourcePath "a"] (TargetPath "b") NoChown NoChmod NoSource- ]- describe "RUN with experimental flags" $ do- it "--mount=type=bind and target" $- let file = Text.unlines ["RUN --mount=type=bind,target=/foo echo foo"]- flags = def {mount = Just $ BindMount (def {bTarget = "/foo"})}- in assertAst- file- [ Run $ RunArgs (ArgumentsText "echo foo") flags- ]- it "--mount default to bind" $- let file = Text.unlines ["RUN --mount=target=/foo echo foo"]- flags = def {mount = Just $ BindMount (def {bTarget = "/foo"})}- in assertAst- file- [ Run $ RunArgs (ArgumentsText "echo foo") flags- ]- it "--mount=type=bind all modifiers" $- let file = Text.unlines ["RUN --mount=type=bind,target=/foo,source=/bar,from=ubuntu,ro echo foo"]- flags = def {mount = Just $ BindMount (BindOpts {bTarget = "/foo", bSource = Just "/bar", bFromImage = Just "ubuntu", bReadOnly = Just True})}- in assertAst- file- [ Run $ RunArgs (ArgumentsText "echo foo") flags- ]- it "--mount=type=cache with target" $- let file =- Text.unlines- [ "RUN --mount=type=cache,target=/foo echo foo",- "RUN --mount=type=cache,target=/bar echo foo",- "RUN --mount=type=cache,target=/baz echo foo"- ]- flags1 = def {mount = Just $ CacheMount (def {cTarget = "/foo"})}- flags2 = def {mount = Just $ CacheMount (def {cTarget = "/bar"})}- flags3 = def {mount = Just $ CacheMount (def {cTarget = "/baz"})}- in assertAst- file- [ Run $ RunArgs (ArgumentsText "echo foo") flags1,- Run $ RunArgs (ArgumentsText "echo foo") flags2,- Run $ RunArgs (ArgumentsText "echo foo") flags3- ]- it "--mount=type=cache with all modifiers" $- let file =- Text.unlines- [ "RUN --mount=type=cache,target=/foo,sharing=private,id=a,ro,from=ubuntu,source=/bar,mode=0700,uid=0,gid=0 echo foo"- ]- flags =- def- { mount =- Just $- CacheMount- ( def- { cTarget = "/foo",- cSharing = Just Private,- cCacheId = Just "a",- cReadOnly = Just True,- cFromImage = Just "ubuntu",- cSource = Just "/bar",- cMode = Just "0700",- cUid = Just 0,- cGid = Just 0- }- )- }- in assertAst- file- [ Run $ RunArgs (ArgumentsText "echo foo") flags- ]- it "--mount=type=tmpfs" $- let file = Text.unlines ["RUN --mount=type=tmpfs,target=/foo echo foo"]- flags = def {mount = Just $ TmpfsMount (def {tTarget = "/foo"})}- in assertAst- file- [ Run $ RunArgs (ArgumentsText "echo foo") flags- ]- it "--mount=type=ssh" $- let file = Text.unlines ["RUN --mount=type=ssh echo foo"]- flags = def {mount = Just $ SshMount def}- in assertAst- file- [ Run $ RunArgs (ArgumentsText "echo foo") flags- ]- it "--mount=type=ssh,required=false" $- let file = Text.unlines ["RUN --mount=type=ssh,required=false echo foo"]- flags = def {mount = Just $ SshMount def {sIsRequired = Just False}}- in assertAst- file- [ Run $ RunArgs (ArgumentsText "echo foo") flags- ]- it "--mount=type=ssh,required=False" $- let file = Text.unlines ["RUN --mount=type=ssh,required=False echo foo"]- flags = def {mount = Just $ SshMount def {sIsRequired = Just False}}- in assertAst- file- [ Run $ RunArgs (ArgumentsText "echo foo") flags- ]- it "--mount=type=secret,required=true" $- let file = Text.unlines ["RUN --mount=type=secret,required=true echo foo"]- flags = def {mount = Just $ SecretMount def {sIsRequired = Just True}}- in assertAst- file- [ Run $ RunArgs (ArgumentsText "echo foo") flags- ]- it "--mount=type=secret,required=True" $- let file = Text.unlines ["RUN --mount=type=secret,required=True echo foo"]- flags = def {mount = Just $ SecretMount def {sIsRequired = Just True}}- in assertAst- file- [ Run $ RunArgs (ArgumentsText "echo foo") flags- ]- it "--mount=type=ssh all modifiers" $- let file = Text.unlines ["RUN --mount=type=ssh,target=/foo,id=a,required,source=/bar,mode=0700,uid=0,gid=0 echo foo"]- flags =- def- { mount =- Just $- SshMount- ( def- { sTarget = Just "/foo",- sCacheId = Just "a",- sIsRequired = Just True,- sSource = Just "/bar",- sMode = Just "0700",- sUid = Just 0,- sGid = Just 0- }- )- }- in assertAst- file- [ Run $ RunArgs (ArgumentsText "echo foo") flags- ]- it "--mount=type=ssh all modifiers, required explicit" $- let file = Text.unlines ["RUN --mount=type=ssh,target=/foo,id=a,required=true,source=/bar,mode=0700,uid=0,gid=0 echo foo"]- flags =- def- { mount =- Just $- SshMount- ( def- { sTarget = Just "/foo",- sCacheId = Just "a",- sIsRequired = Just True,- sSource = Just "/bar",- sMode = Just "0700",- sUid = Just 0,- sGid = Just 0- }- )- }- in assertAst- file- [ Run $ RunArgs (ArgumentsText "echo foo") flags- ]- it "--mount=type=secret all modifiers" $- let file = Text.unlines ["RUN --mount=type=secret,target=/foo,id=a,required,source=/bar,mode=0700,uid=0,gid=0 echo foo"]- flags =- def- { mount =- Just $- SecretMount- ( def- { sTarget = Just "/foo",- sCacheId = Just "a",- sIsRequired = Just True,- sSource = Just "/bar",- sMode = Just "0700",- sUid = Just 0,- sGid = Just 0- }- )- }- in assertAst- file- [ Run $ RunArgs (ArgumentsText "echo foo") flags- ]- it "--mount=type=secret all modifiers, required explicit" $- let file = Text.unlines ["RUN --mount=type=secret,target=/foo,id=a,required=true,source=/bar,mode=0700,uid=0,gid=0 echo foo"]- flags =- def- { mount =- Just $- SecretMount- ( def- { sTarget = Just "/foo",- sCacheId = Just "a",- sIsRequired = Just True,- sSource = Just "/bar",- sMode = Just "0700",- sUid = Just 0,- sGid = Just 0- }- )- }- in assertAst- file- [ Run $ RunArgs (ArgumentsText "echo foo") flags- ]- it "--network=none" $- let file = Text.unlines ["RUN --network=none echo foo"]- flags = def {network = Just NetworkNone}- in assertAst- file- [ Run $ RunArgs (ArgumentsText "echo foo") flags- ]- it "--network=host" $- let file = Text.unlines ["RUN --network=host echo foo"]- flags = def {network = Just NetworkHost}- in assertAst- file- [ Run $ RunArgs (ArgumentsText "echo foo") flags- ]- it "--network=default" $- let file = Text.unlines ["RUN --network=default echo foo"]- flags = def {network = Just NetworkDefault}- in assertAst- file- [ Run $ RunArgs (ArgumentsText "echo foo") flags- ]- it "--security=insecure" $- let file = Text.unlines ["RUN --security=insecure echo foo"]- flags = def {security = Just Insecure}- in assertAst- file- [ Run $ RunArgs (ArgumentsText "echo foo") flags- ]- it "--security=sandbox" $- let file = Text.unlines ["RUN --security=sandbox echo foo"]- flags = def {security = Just Sandbox}- in assertAst- file- [ Run $ RunArgs (ArgumentsText "echo foo") flags- ]- it "allows all flags" $- let file = Text.unlines ["RUN --mount=target=/foo --network=none --security=sandbox echo foo"]- flags =- def- { security = Just Sandbox,- network = Just NetworkNone,- mount = Just $ BindMount $ def {bTarget = "/foo"}- }- in assertAst- file- [ Run $ RunArgs (ArgumentsText "echo foo") flags ]