module Network.Wreq.Docker.Image where
import Control.Lens
import Control.Monad
import Control.Monad.Except
import Control.Monad.Reader
import Data.ByteString.Lazy.Char8 as C8L
import Data.Coerce
import Data.Either
import Data.HashSet as Set
import Data.Monoid
import Data.Text (Text)
import qualified Data.Text as Text
import NeatInterpolation
import qualified Network.Wreq as Wreq
import System.FilePath.Posix as File
import System.Terminal.Concurrent
import Data.Docker.Image.Types
import Hocker.Lib
import Network.Wreq.Docker.Image.Lib as Docker.Image
import Network.Wreq.Docker.Registry as Docker.Registry
import Hocker.Types
import Hocker.Types.Exceptions
import Hocker.Types.ImageName
fetchImage :: HockerMeta -> IO (Either HockerException Text)
fetchImage =
runHocker $ ask >>= \HockerMeta{..} -> do
imageOutDir <- Hocker.Lib.requirePath outDir
manifest <- fetchManifest >>= checkResponseIntegrity'
configDigest <- getConfigDigest $ manifest ^. Wreq.responseBody
let configFileHash = Hocker.Lib.stripHashId . Text.pack $ showSHA configDigest
imageConfig <- fetchImageConfig configDigest
imageConfigFile <- writeRespBody
(File.joinPath [imageOutDir, Text.unpack configFileHash] `addExtension` "json")
configFileHash
imageConfig
let refLayers = pluckRefLayersFrom $ imageConfig ^. Wreq.responseBody
refLayers' = fmap Hocker.Lib.stripHashId refLayers
refLayerSet = Set.fromList refLayers'
manifestLayers = pluckLayersFrom $ manifest ^. Wreq.responseBody
(_, strippedReg) = Text.breakOnEnd "//" . Text.pack . show $ dockerRegistry
repoTags = (Text.unpack strippedReg) </> (coerce imageName)
layers <- mapPool 3 Docker.Image.fetchLayer $ Prelude.zip refLayers' manifestLayers
let writtenLayerSet = Set.fromList . fmap (Text.pack . takeBaseName) $ rights layers
refLayerSetTxt = Text.pack (show refLayerSet)
wrtLayerSetTxt = Text.pack (show writtenLayerSet)
dffLayerSetTxt = Text.pack (show $ Set.difference refLayerSet writtenLayerSet)
when (writtenLayerSet /= refLayerSet) $
throwError . hockerException $ Text.unpack
([text|
Written layers do not match the reference layers!
Reference layers: ${refLayerSetTxt}
Written layers: ${wrtLayerSetTxt}
Difference: ${dffLayerSetTxt}
|])
createImageRepository repoTags refLayers'
createImageManifest repoTags imageConfigFile refLayers'
archivePath <- createImageTar
return (Text.pack archivePath)
fetchLayer :: HockerMeta -> IO (Either HockerException FilePath)
fetchLayer =
runHocker $ ask >>= \HockerMeta{..} -> do
layerOut <- Hocker.Lib.requirePath out
layerDigest <- Text.pack . show <$> maybe
(throwError $ hockerException
"a layer digest is expected!")
return
imageLayer
let shortRef = Text.take 7 layerDigest
writeC <- liftIO $ getConcurrentOutputter
liftIO . writeC . Text.unpack $ "Downloading layer: " <> shortRef
fetchedImageLayer <- Docker.Registry.fetchLayer ("sha256:" <> layerDigest)
layerPath <- writeRespBody layerOut layerDigest fetchedImageLayer
liftIO . writeC $ Text.unpack ("=> wrote " <> shortRef)
return layerPath
fetchConfig :: HockerMeta -> IO (Either HockerException C8L.ByteString)
fetchConfig =
runHocker $ ask >>= \HockerMeta{..} -> do
configDigest <-
fetchManifest
>>= getConfigDigest . view Wreq.responseBody
fetchImageConfig configDigest
>>= return . view Wreq.responseBody
fetchImageManifest :: HockerMeta -> IO (Either HockerException C8L.ByteString)
fetchImageManifest = runHocker (fetchManifest >>= return . view Wreq.responseBody)