module Network.Wreq.Docker.Image.Lib where
import qualified Codec.Archive.Tar as Tar
import qualified Codec.Compression.GZip as GZip
import qualified Control.Concurrent.PooledIO.Final as Pool
import Control.Lens
import Control.Monad.Except
import Control.Monad.Reader
import qualified Data.ByteString.Lazy.Char8 as C8L
import Data.Coerce
import qualified Data.HashMap.Strict as HashMap
import Data.Monoid
import qualified Data.Text as Text
import qualified Network.Wreq as Wreq
import qualified System.Directory as Directory
import System.FilePath.Posix as File
import System.Terminal.Concurrent
import Data.Docker.Image.Types
import Hocker.Lib
import Network.Wreq.Docker.Registry as Docker.Registry
import Hocker.Types
import Hocker.Types.Exceptions
import Hocker.Types.ImageTag
mapPool :: Traversable t
=> Int
-> ((String -> IO ()) -> a -> Hocker FilePath)
-> t a
-> Hocker (t (Either HockerException FilePath))
mapPool n f l = do
env <- ask
writeC <- liftIO getConcurrentOutputter
let f' v = (runHocker (f writeC v) env)
liftIO . Pool.runLimited n $ traverse (Pool.fork . f') l
forPool :: Traversable t
=> Int
-> t a
-> ((String -> IO ()) -> a -> Hocker FilePath)
-> Hocker (t (Either HockerException FilePath))
forPool n = flip $ mapPool n
fetchLayer :: (String -> IO ())
-> (RefLayer, Layer)
-> Hocker FilePath
fetchLayer writeC layer@(refl, (stripHashId -> layer')) = ask >>= \HockerMeta{..} -> do
liftIO . writeC . Text.unpack $ "Downloading layer: " <> (Text.take 7 layer')
fetchedImageLayer <- checkResponseIntegrity' =<< (Docker.Registry.fetchLayer $ snd layer)
let decompressed = fetchedImageLayer & Wreq.responseBody %~ GZip.decompress
shortRef = Text.take 7 refl
imageOutDir <- Hocker.Lib.requirePath outDir
liftIO $ writeC " => decompressed "
let layerOutPath = File.joinPath [imageOutDir, Text.unpack refl] `addExtension` "tar"
layerPath <- writeRespBody layerOutPath refl decompressed
liftIO . writeC $ Text.unpack ("=> wrote " <> shortRef)
return layerPath
createImageManifest :: RepoTag
-> FilePath
-> [RefLayer]
-> Hocker ()
createImageManifest repoTag imageConfigFile refls = ask >>= \HockerMeta{..} -> do
let imageManifest = [
ImageManifest
(takeBaseName imageConfigFile `addExtension` "json")
[Text.pack (repoTag ++ ":" ++ coerce imageTag)]
(fmap ((`addExtension` "tar") . Text.unpack) refls) ]
imageOutDir <- Hocker.Lib.requirePath outDir
liftIO $ C8L.writeFile
(imageOutDir </> "manifest" `addExtension` "json")
(Hocker.Lib.encodeCanonical imageManifest)
createImageRepository :: RepoTag
-> [RefLayer]
-> Hocker ()
createImageRepository repoTag refls = ask >>= \HockerMeta{..} -> do
let repositories =
ImageRepo
(Text.pack repoTag)
(HashMap.singleton
(Text.pack $ coerce imageTag)
((Prelude.last refls) <> ".tar"))
imageOutDir <- Hocker.Lib.requirePath outDir
liftIO $ C8L.writeFile
(imageOutDir </> "repositories")
(Hocker.Lib.encodeCanonical repositories)
createImageTar :: Hocker FilePath
createImageTar = ask >>= \HockerMeta{..} -> do
imageOutDir <- Hocker.Lib.requirePath outDir
archivePath <- Hocker.Lib.requirePath out
entries <- liftIO $ Directory.getDirectoryContents imageOutDir
let entriesToPack = [e | e <- entries, e /= ".", e /= ".."]
liftIO $ Tar.create archivePath imageOutDir entriesToPack
liftIO $ Directory.removeDirectoryRecursive imageOutDir
return $ archivePath