Merge pull request #103 from hercules-ci/streamlayer

Add dockerTools.streamLayeredImage support
This commit is contained in:
Robert Hensing 2020-10-02 13:00:10 +02:00 committed by GitHub
commit 427a3b0e3c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 48 additions and 11 deletions

View file

@ -12,6 +12,7 @@
"8000:8000" # host:container "8000:8000" # host:container
]; ];
service.environment.WEB_ROOT = "${pkgs.nix.doc}/share/doc/nix/manual"; service.environment.WEB_ROOT = "${pkgs.nix.doc}/share/doc/nix/manual";
service.stop_signal = "SIGINT";
}; };
}; };
} }

View file

@ -17,7 +17,8 @@ import Control.Lens
import Data.Aeson.Lens import Data.Aeson.Lens
data Image = Image data Image = Image
{ image :: Text -- ^ file path { image :: Maybe Text -- ^ image tar.gz file path
, imageExe :: Maybe Text -- ^ path to exe producing image tar
, imageName :: Text , imageName :: Text
, imageTag :: Text , imageTag :: Text
} deriving (Generic, Aeson.ToJSON, Aeson.FromJSON, Show) } deriving (Generic, Aeson.ToJSON, Aeson.FromJSON, Show)
@ -30,7 +31,7 @@ loadImages fp = do
v <- decodeFile fp v <- decodeFile fp
loaded <- dockerImages loaded <- getDockerImages
let let
images :: [Image] images :: [Image]
@ -38,10 +39,11 @@ loadImages fp = do
isNew i = (imageName i <> ":" <> imageTag i) `notElem` loaded isNew i = (imageName i <> ":" <> imageTag i) `notElem` loaded
traverse_ loadImage . map (toS . image) . filter isNew $ images traverse_ loadImage . filter isNew $ images
loadImage :: FilePath -> IO () loadImage :: Image -> IO ()
loadImage imgPath = withFile (imgPath) ReadMode $ \fileHandle -> do loadImage (Image { image = Just imgPath, imageName = name }) =
withFile (toS imgPath) ReadMode $ \fileHandle -> do
let procSpec = (Process.proc "docker" [ "load" ]) { let procSpec = (Process.proc "docker" [ "load" ]) {
Process.std_in = Process.UseHandle fileHandle Process.std_in = Process.UseHandle fileHandle
} }
@ -49,10 +51,32 @@ loadImage imgPath = withFile (imgPath) ReadMode $ \fileHandle -> do
e <- Process.waitForProcess procHandle e <- Process.waitForProcess procHandle
case e of case e of
ExitSuccess -> pass ExitSuccess -> pass
ExitFailure code -> panic $ "docker load (" <> show code <> ") failed for " <> toS imgPath ExitFailure code ->
panic $ "docker load failed with exit code " <> show code <> " for image " <> name <> " from path " <> imgPath
loadImage (Image { imageExe = Just imgExe, imageName = name }) = do
let loadSpec = (Process.proc "docker" [ "load" ]) { Process.std_in = Process.CreatePipe }
Process.withCreateProcess loadSpec $ \(Just inHandle) _out _err loadProcHandle -> do
let streamSpec = Process.proc (toS imgExe) []
Process.withCreateProcess streamSpec { Process.std_out = Process.UseHandle inHandle } $ \_ _ _ streamProcHandle ->
withAsync (Process.waitForProcess loadProcHandle) $ \loadExitAsync ->
withAsync (Process.waitForProcess streamProcHandle) $ \streamExitAsync -> do
r <- waitEither loadExitAsync streamExitAsync
case r of
Right (ExitFailure code) -> panic $ "image producer for image " <> name <> " failed with exit code " <> show code <> " from executable " <> imgExe
Right ExitSuccess -> pass
Left _ -> pass
loadExit <- wait loadExitAsync
case loadExit of
ExitFailure code -> panic $ "docker load failed with exit code " <> show code <> " for image " <> name <> " produced by executable " <> imgExe
_ -> pass
pass
loadImage (Image { imageName = name }) = do
panic $ "image " <> name <> " doesn't specify an image file or imageExe executable"
dockerImages :: IO [TaggedImage] getDockerImages :: IO [TaggedImage]
dockerImages = do getDockerImages = do
let procSpec = Process.proc "docker" [ "images", "--filter", "dangling=false", "--format", "{{.Repository}}:{{.Tag}}" ] let procSpec = Process.proc "docker" [ "images", "--filter", "dangling=false", "--format", "{{.Repository}}:{{.Tag}}" ]
(map toS . T.lines . toS) <$> Process.readCreateProcess procSpec "" (map toS . T.lines . toS) <$> Process.readCreateProcess procSpec ""

View file

@ -16,13 +16,20 @@ let
(let (let
inherit (service) build; inherit (service) build;
in { in {
image = build.image.outPath;
imageName = build.imageName or service.image.name; imageName = build.imageName or service.image.name;
imageTag = imageTag =
if build.image.imageTag != "" if build.image.imageTag != ""
then build.image.imageTag then build.image.imageTag
else lib.head (lib.strings.splitString "-" (baseNameOf build.image.outPath)); else lib.head (lib.strings.splitString "-" (baseNameOf build.image.outPath));
}); } // (if build.image.isExe or false
then {
imageExe = build.image.outPath;
}
else {
image = build.image.outPath;
}
)
);
in in
{ {
options = { options = {

View file

@ -9,7 +9,12 @@ let
(pkgs.writeText "dummy-config.json" (builtins.toJSON config.image.rawConfig)) (pkgs.writeText "dummy-config.json" (builtins.toJSON config.image.rawConfig))
]; ];
builtImage = pkgs.dockerTools.buildLayeredImage { buildOrStreamLayeredImage = args:
if pkgs.dockerTools?streamLayeredImage
then pkgs.dockerTools.streamLayeredImage args // { isExe = true; }
else pkgs.dockerTools.buildLayeredImage args;
builtImage = buildOrStreamLayeredImage {
inherit (config.image) inherit (config.image)
name name
contents contents