{-# LANGUAGE LambdaCase #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE RecordWildCards #-} {-# LANGUAGE TemplateHaskell #-} {-# LANGUAGE TupleSections #-} ----------------------------------------------------------------------------- -- | -- Module : Data.Docker.Image.Types -- Copyright : (C) 2016 Awake Networks -- License : Apache-2.0 -- Maintainer : Awake Networks -- Stability : stable ---------------------------------------------------------------------------- module Data.Docker.Image.Types where import qualified Crypto.Hash as Hash import Data.Aeson import Data.Aeson.TH import Data.Aeson.Types import qualified Data.ByteArray as BA import qualified Data.ByteArray.Encoding as BA import qualified Data.ByteString.Char8 as C8 import Data.ByteString.Lazy.Char8 as C8L import Data.HashMap.Strict as H import Data.Text (Text) import Data.Docker.Image.AesonHelpers import Hocker.Types import Hocker.Types.ImageTag -- | Metadata needed for constructing a docker image. data HockerImageMeta = HockerImageMeta { -- | Docker image repo, the first part of a repository+name -- separated by a "/"; e.g: library/debian. imageRepo :: RepoNamePart -- | Docker image name, the second part of a repository+name -- separated by a "/"; e.g: library/debian. , imageName :: ImageNamePart -- | Docker image tag , imageTag :: ImageTag -- | A docker image manifest JSON blob as usually fetched from a -- docker registry. , manifestJSON :: C8L.ByteString -- | The URI (even if the default public registry) of the docker -- registry. , dockerRegistry :: RegistryURI -- | An alternative name for the docker image in the generated nix -- build instructions. , altImageName :: Maybe Text } deriving (Show) -- | Parse a 'C8.ByteString' into a 'Hash.SHA256'. -- -- A digest value, as seen in the docker registry manifest, is the -- hexadecimal encoding of a hashing function's digest with the -- hashing function identifier prefixed onto the string. At this time -- the only prefix used is @sha256:@. toDigest :: C8.ByteString -> Maybe (Hash.Digest Hash.SHA256) toDigest = from . C8.break (== ':') where from ("sha256", r) = either (const Nothing) Hash.digestFromByteString . toBytes $ C8.tail r from (_, _) = Nothing toBytes :: C8.ByteString -> Either String BA.Bytes toBytes = BA.convertFromBase BA.Base16 -- | Show a hexadecimal encoded 'SHA256' hash digest and prefix -- @sha256:@ to it. showSHA :: Hash.Digest Hash.SHA256 -> String showSHA = ("sha256:" ++) . show -- Pretty-printed example of the `manifest.json` file. {- [ { "Config": "3e83c23dba6a16cd936a3dc044df71b26706c5a4c28181bc3ca4a4af9f5f38ee.json", "Layers": [ "10a267c67f423630f3afe5e04bbbc93d578861ddcc54283526222f3ad5e895b9.tar" ], "RepoTags": [ "library/debian:jessie" ] } ] -} -- Pretty-printed example of the `repositories` json file. {- { "library/debian": { "jessie": "10a267c67f423630f3afe5e04bbbc93d578861ddcc54283526222f3ad5e895b9" } } -} -- | A layer hash digest from a docker image's config JSON. This hash -- is different from those found in the image's manifest JSON. type RefLayer = Text -- | A 'String' representing the full repository tag, e.g: @library/debian@. type RepoTag = String -- | A v1.2 docker image manifest. data ImageManifest = ImageManifest { -- | 'FilePath' within the image archive of the image's config -- JSON config :: FilePath -- | List of image repository tags , repoTags :: [Text] -- | List of layers within the image archive named by their hash -- digest and with a @.tar@ extension , layers :: [FilePath] } deriving (Show, Eq) -- | A map of 'ImageRepo's. The repository names are the top-level -- keys and their value is a map who's keys are the tags of the -- repository with the hash-value of the layer that tag references. data ImageRepositories = ImageRepositories [ImageRepo] deriving (Show, Eq) data ImageRepo = ImageRepo { -- | Repository tag repo :: Text -- | 'HashMap' of tags to the top-most layer associated with that tag , tags :: H.HashMap Text Text } deriving (Show, Eq) $(deriveJSON stdOpts{ fieldLabelModifier = upperFirst } ''ImageManifest) instance ToJSON ImageRepositories where toJSON (ImageRepositories r) = Object . H.unions $ [i | o@(Object i) <- (fmap toJSON r), isObject o] where isObject (Object _) = True isObject _ = False instance ToJSON ImageRepo where toJSON (ImageRepo r t) = object [ r .= toJSON t ] instance FromJSON ImageRepositories where parseJSON (Object v) = ImageRepositories <$> (mapM buildRepo $ H.toList v) where buildRepo (k,v') = ImageRepo k <$> parseJSON v' parseJSON v = typeMismatch "ImageRepositories" v