-- Hoogle documentation, generated by Haddock -- See Hoogle, http://www.haskell.org/hoogle/ -- | Interact with the docker registry and generate nix build instructions -- -- hocker is a suite of command line utilities and a library -- for: -- -- -- -- The motivation for this tool came from a need to fetch docker image -- artifacts from a docker registry without the stock docker tooling that -- is designed to only work with the docker daemon. -- -- These tools only work with version 2 of the docker registry and -- docker version (>=) 1.10. -- -- For a complete set of usage examples please see the project's -- README.md. @package hocker @version 1.0.5 module Hocker.Types.URI -- | Parse a URI value. uriReader :: ReadM (URIRef Absolute) instance Options.Generic.ParseField (URI.ByteString.Types.URIRef URI.ByteString.Types.Absolute) instance Options.Generic.ParseFields (URI.ByteString.Types.URIRef URI.ByteString.Types.Absolute) instance Options.Generic.ParseRecord (URI.ByteString.Types.URIRef URI.ByteString.Types.Absolute) module Hocker.Types.ImageTag newtype ImageTag ImageTag :: String -> ImageTag [unImageTag] :: ImageTag -> String instance GHC.Show.Show Hocker.Types.ImageTag.ImageTag instance GHC.Generics.Generic Hocker.Types.ImageTag.ImageTag instance Options.Generic.ParseField Hocker.Types.ImageTag.ImageTag instance Options.Generic.ParseFields Hocker.Types.ImageTag.ImageTag instance Options.Generic.ParseRecord Hocker.Types.ImageTag.ImageTag instance Control.DeepSeq.NFData Hocker.Types.ImageTag.ImageTag module Hocker.Types.ImageName newtype ImageName ImageName :: String -> ImageName [unImageName] :: ImageName -> String instance GHC.Show.Show Hocker.Types.ImageName.ImageName instance GHC.Generics.Generic Hocker.Types.ImageName.ImageName instance Options.Generic.ParseField Hocker.Types.ImageName.ImageName instance Options.Generic.ParseFields Hocker.Types.ImageName.ImageName instance Options.Generic.ParseRecord Hocker.Types.ImageName.ImageName instance Control.DeepSeq.NFData Hocker.Types.ImageName.ImageName module Hocker.Types.Hash toBytes :: ByteString -> Either String Bytes readSHA256 :: ByteString -> Maybe (Digest SHA256) instance Options.Generic.ParseField (Crypto.Hash.Types.Digest Crypto.Hash.SHA256.SHA256) instance Options.Generic.ParseFields (Crypto.Hash.Types.Digest Crypto.Hash.SHA256.SHA256) instance Options.Generic.ParseRecord (Crypto.Hash.Types.Digest Crypto.Hash.SHA256.SHA256) module Hocker.Types.Exceptions data HockerException HockerException :: String -> Maybe String -> Maybe String -> HockerException [baseMsg] :: HockerException -> String [expected] :: HockerException -> Maybe String [received] :: HockerException -> Maybe String hockerException :: String -> HockerException instance Control.DeepSeq.NFData Hocker.Types.Exceptions.HockerException instance GHC.Generics.Generic Hocker.Types.Exceptions.HockerException instance GHC.Read.Read Hocker.Types.Exceptions.HockerException instance GHC.Exception.Exception Hocker.Types.Exceptions.HockerException instance GHC.Show.Show Hocker.Types.Exceptions.HockerException module Network.Wreq.ErrorHandling interceptHttpExc :: ExceptT HockerException IO a -> ExceptT HockerException IO a prettify :: HttpException -> HockerException module Hocker.Types -- | Docker registry URI. type RegistryURI = URIRef Absolute -- | Docker registry username. type Username = Text -- | Docker registry user password. type Password = Text -- | Docker image layer sha256 hash digest. type Layer = Text -- | SHA256 hash digest with the hash algorithm identifier prefix, stripped type StrippedDigest = Text -- | Docker image manifest JSON. type Manifest = ByteString -- | Docker image config JSON. type ImageConfigJSON = ByteString -- | Wreq response type parameterized by the lazy bytestring type. type RspBS = Response ByteString -- | A file extension. type Extension = String -- | RepoName is the part before the forward slash in a docker image name, -- e.g: library in library/debian type RepoNamePart = Text -- | ImageName is the part after the forward slash in a docker image name, -- e.g: library in library/debian type ImageNamePart = Text -- | Docker image config JSON file's sha256 hash digest in Nix's base32 -- encoding. -- -- NB: it's very important to realize there's a significant difference -- between Nix's base32 encoding and the standard base32 encoding! (i.e, -- they're not compatible). type ConfigDigest = Base32Digest -- | Generic top-level optparse-generic CLI args data type and -- specification. -- -- NOTE: `hocker-layer` does not use this data type because it requires -- an additional layer sha256 hash digest argument. data Options w Options :: w ::: (Maybe RegistryURI "URI of registry, defaults to the Docker Hub registry") -> Maybe Credentials -> w ::: (Maybe FilePath "Write content to location") -> ImageName -> ImageTag -> Options w -- | URI for the registry, optional [$sel:registry:Options] :: Options w -> w ::: (Maybe RegistryURI "URI of registry, defaults to the Docker Hub registry") [$sel:credentials:Options] :: Options w -> Maybe Credentials -- | Filesystem path to write output to [$sel:out:Options] :: Options w -> w ::: (Maybe FilePath "Write content to location") -- | Docker image name (includes the reponame, e.g: library/debian) [$sel:imageName:Options] :: Options w -> ImageName -- | Docker image tag [$sel:imageTag:Options] :: Options w -> ImageTag -- | Hocker ExceptT and ReaderT transformer stack -- threading a HockerMeta data type. newtype Hocker a Hocker :: ReaderT HockerMeta (ExceptT HockerException IO) a -> Hocker a [$sel:unHocker:Hocker] :: Hocker a -> ReaderT HockerMeta (ExceptT HockerException IO) a runHocker :: Hocker a -> HockerMeta -> IO (Either HockerException a) -- | Red wagon record carrying around the environment as we fetch, -- transform, and assemble docker image artifacts. data HockerMeta HockerMeta :: RegistryURI -> Maybe Auth -> ImageName -> ImageTag -> Maybe FilePath -> Maybe FilePath -> Maybe (Digest SHA256) -> HockerMeta [$sel:dockerRegistry:HockerMeta] :: HockerMeta -> RegistryURI [$sel:auth:HockerMeta] :: HockerMeta -> Maybe Auth [$sel:imageName:HockerMeta] :: HockerMeta -> ImageName [$sel:imageTag:HockerMeta] :: HockerMeta -> ImageTag [$sel:out:HockerMeta] :: HockerMeta -> Maybe FilePath [$sel:outDir:HockerMeta] :: HockerMeta -> Maybe FilePath [$sel:imageLayer:HockerMeta] :: HockerMeta -> Maybe (Digest SHA256) -- | Newtype base32 encoding of a hash digest. -- -- Please note, this base32 encoding is unique to Nix and not compatible -- with other base32 encodings. newtype Base32Digest Base32Digest :: Text -> Base32Digest -- | Newtype base16 encoding of a hash digest. -- -- This encoding has no known idiosyncracies specific to Nix, it should -- be compatible with other tools and library's expectations. newtype Base16Digest Base16Digest :: Text -> Base16Digest data Credentials Basic :: Username -> Password -> Credentials BearerToken :: Text -> Credentials -- | upperFirst uppercases the first letter of the string. upperFirst :: String -> String instance GHC.Generics.Generic (Hocker.Types.Options w) instance GHC.Show.Show Hocker.Types.Credentials instance GHC.Classes.Eq Hocker.Types.Base16Digest instance GHC.Read.Read Hocker.Types.Base16Digest instance GHC.Show.Show Hocker.Types.Base16Digest instance GHC.Classes.Eq Hocker.Types.Base32Digest instance GHC.Read.Read Hocker.Types.Base32Digest instance GHC.Show.Show Hocker.Types.Base32Digest instance Control.Monad.Error.Class.MonadError Hocker.Types.Exceptions.HockerException Hocker.Types.Hocker instance Control.Monad.Reader.Class.MonadReader Hocker.Types.HockerMeta Hocker.Types.Hocker instance Control.Monad.IO.Class.MonadIO Hocker.Types.Hocker instance GHC.Base.Monad Hocker.Types.Hocker instance GHC.Base.Applicative Hocker.Types.Hocker instance GHC.Base.Functor Hocker.Types.Hocker instance GHC.Show.Show Hocker.Types.HockerMeta instance GHC.Show.Show (Hocker.Types.Options Options.Generic.Unwrapped) instance Options.Generic.ParseRecord (Hocker.Types.Options Options.Generic.Wrapped) instance Options.Generic.ParseField Hocker.Types.Credentials instance Options.Generic.ParseFields Hocker.Types.Credentials instance Options.Generic.ParseRecord Hocker.Types.Credentials module Data.Docker.Nix.Lib -- | Convert a Base16Digest to a Base32Digest using the -- nix-hash utility. -- -- NB: Nix implements its own custom base32 encoding function for hashes -- that is not compatible with other more standard and native -- implementations in Haskell. I opted to call out to nix-hash -- instead of re-implementing their algorithm because it's non-standard -- and may change, creating a maintenance headache and surprise behavior. toBase32Nix :: (MonadIO m, MonadError HockerException m) => Base16Digest -> m Base32Digest module Data.Docker.Image.AesonHelpers -- | Produce a default option record with omitNothingFields set to -- True by default. stdOpts :: Options module Data.Docker.Image.Types -- | Metadata needed for constructing a docker image. data HockerImageMeta HockerImageMeta :: RepoNamePart -> ImageNamePart -> ImageTag -> ByteString -> RegistryURI -> Maybe Text -> HockerImageMeta -- | Docker image repo, the first part of a repository+name separated by a -- ""; e.g: librarydebian. [imageRepo] :: HockerImageMeta -> RepoNamePart -- | Docker image name, the second part of a repository+name separated by a -- ""; e.g: librarydebian. [imageName] :: HockerImageMeta -> ImageNamePart -- | Docker image tag [imageTag] :: HockerImageMeta -> ImageTag -- | A docker image manifest JSON blob as usually fetched from a docker -- registry. [manifestJSON] :: HockerImageMeta -> ByteString -- | The URI (even if the default public registry) of the docker registry. [dockerRegistry] :: HockerImageMeta -> RegistryURI -- | An alternative name for the docker image in the generated nix build -- instructions. [altImageName] :: HockerImageMeta -> Maybe Text -- | Parse a ByteString into a 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 :: ByteString -> Maybe (Digest SHA256) -- | Show a hexadecimal encoded SHA256 hash digest and prefix -- sha256: to it. showSHA :: Digest SHA256 -> String -- | 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 -> [Text] -> [FilePath] -> ImageManifest -- | FilePath within the image archive of the image's config JSON [config] :: ImageManifest -> FilePath -- | List of image repository tags [repoTags] :: ImageManifest -> [Text] -- | List of layers within the image archive named by their hash digest and -- with a .tar extension [layers] :: ImageManifest -> [FilePath] -- | A map of ImageRepos. 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] -> ImageRepositories data ImageRepo ImageRepo :: Text -> HashMap Text Text -> ImageRepo -- | Repository tag [repo] :: ImageRepo -> Text -- | HashMap of tags to the top-most layer associated with that tag [tags] :: ImageRepo -> HashMap Text Text instance Data.Aeson.Types.ToJSON.ToJSON Data.Docker.Image.Types.ImageManifest instance Data.Aeson.Types.FromJSON.FromJSON Data.Docker.Image.Types.ImageManifest instance Data.Aeson.Types.ToJSON.ToJSON Data.Docker.Image.Types.ImageRepositories instance Data.Aeson.Types.ToJSON.ToJSON Data.Docker.Image.Types.ImageRepo instance Data.Aeson.Types.FromJSON.FromJSON Data.Docker.Image.Types.ImageRepositories instance GHC.Classes.Eq Data.Docker.Image.Types.ImageRepositories instance GHC.Show.Show Data.Docker.Image.Types.ImageRepositories instance GHC.Classes.Eq Data.Docker.Image.Types.ImageRepo instance GHC.Show.Show Data.Docker.Image.Types.ImageRepo instance GHC.Classes.Eq Data.Docker.Image.Types.ImageManifest instance GHC.Show.Show Data.Docker.Image.Types.ImageManifest instance GHC.Show.Show Data.Docker.Image.Types.HockerImageMeta module Hocker.Lib -- | Throw a userError, exiting the program with the supplied -- message. die :: MonadIO io => Text -> io a -- | Print an error message to stderr and return a non-zero exit code, the -- message is prefixed with the name of the program. exitProgFail :: String -> IO a -- | Print the bytestring to stdout if the first argument is -- Nothing, otherwise write the bytestring to the provided -- filesystem path and print the path to stdout. writeOrPrint :: Maybe FilePath -> ByteString -> IO () -- | Combine an image name and a base path producing an output path. mkOutImage :: ImageName -> FilePath -> FilePath -- | Combine an image name, an image tag, and a base path producing an -- output path with a -config.json suffix. mkOutConfig :: ImageName -> ImageTag -> FilePath -> FilePath -- | Combine an image name, an image tag, and a base path producing an -- output path with a -manifest.json suffix. mkOutManifest :: ImageName -> ImageTag -> FilePath -> FilePath -- | Join a list of strings and the path part of a RegistryURI to -- produce a new RegistryURI with a path root of /v2. joinURIPath :: [String] -> RegistryURI -> RegistryURI -- | Given a Auth produce a Options. opts :: Maybe Auth -> Options -- | Hash a Char8 using the SHA256 algorithm. sha256 :: ByteString -> Digest SHA256 -- | Strip the sha256: identifier prefix from a hash digest. stripHashId :: Text -> Text -- | Encode, following Docker's canonical JSON rules, any ToJSON -- data type. -- -- The canonicalization rules enable consistent hashing of encoded JSON, -- a process relied upon heavily by docker for content addressability and -- unique identification of resources within a docker registry. Notably, -- an image's config JSON file and layers. -- -- NB: Docker's canonical JSON spec intentionally *does not* -- follow the OLPC's Canonical JSON format even though it was -- inspired by it. encodeCanonical :: ToJSON a => a -> ByteString -- | Throw an error if the first argument is Nothing, otherwise -- return the FilePath unwrapped. requirePath :: (MonadError HockerException m) => Maybe FilePath -> m (FilePath) -- | Pluck out the digest value for the config JSON given a docker registry -- image manifest. Attempting to parse and return the digest value as a -- SHA256, otherwise throw an error. getConfigDigest :: (MonadError HockerException m) => ByteString -> m (Digest SHA256) -- | Split a docker image's name on the forward slash separator so we get -- the distinct repo name and image name. splitRepository :: ImageName -> (RepoNamePart, ImageNamePart) -- | Given a nix expression AST, produce a pretty printer document. renderNixExpr :: NExpr -> SimpleDoc -- | Print a nix expression AST using the renderNixExpr pretty -- printing renderer. pprintNixExpr :: NExpr -> IO () -- | Given an executable's name, try to find it in the PATH. findExec :: (MonadIO m, MonadError HockerException m) => String -> m FilePath -- | Convenience functions for interacting with an instance of Docker -- Distribution (Docker Registry V2). I've kept the module naming -- consistent with the docker registry terms since that appears to be -- what everyone uses colloquially even though the formal name for the -- software is "docker distribution". module Network.Wreq.Docker.Registry -- | Default docker hub registry -- (https://registry-1.docker.io/v2/). defaultRegistry :: URIRef Absolute -- | Given Credentials, produce a Auth. -- -- If Credentials is either BearerToken or Basic -- then produce a Auth value for that type of credential. -- -- If Nothing is provided _and_ the provided RegistryURI -- matches the default registry, make a request to -- https://auth.docker.io/token for a temporary pull-only -- bearer token, assuming the request we want to make is to the public -- docker hub and without any other credentials. -- -- Otherwise, return Nothing so that an unauthenticated request -- can be made. mkAuth :: RegistryURI -> ImageName -> Maybe Credentials -> IO (Maybe Auth) -- | Retrieve a list of layer hash digests from a docker registry image -- manifest JSON. -- -- TODO: pluck out the layer's size and digest into a tuple. pluckLayersFrom :: Manifest -> [Layer] -- | Retrieve a list of layer hash digests from an image's configuration -- JSON. -- -- This is subtly different from pluckLayersFrom because both list -- hash digests for the image's layers but the manifest's layer hash -- digests are keys into the registry's blob storage referencing -- _compressed_ layer archives. The configuration JSON's layer hash -- digests reference the uncompressed layer tar archives within the -- image. pluckRefLayersFrom :: ImageConfigJSON -> [Layer] -- | Request a V2 registry manifest for the specified docker image. fetchManifest :: Hocker RspBS -- | Retrieve the configuratino JSON of an image by its hash digest (found -- in the V2 manifest for an image given by a name and a tag). fetchImageConfig :: (Digest SHA256) -> Hocker RspBS -- | Retrieve a compressed layer blob by its hash digest. -- -- TODO: take advantage of registry's support for the Range header so we -- can stream downloads. fetchLayer :: Layer -> Hocker RspBS -- | Write a responseBody to the specified FilePath, checking -- the integrity of the file with its sha256 hash digest. -- -- The second argument, the StrippedDigest, must be a hash digest -- stripped of the sha256: algorithm identifier prefix. writeRespBody :: FilePath -> StrippedDigest -> RspBS -> Hocker FilePath -- | Write a response to the filesystem without a request hash digest. -- Attempt to fetch the value of the ETag header to verify the -- integrity of the content received. -- -- The Docker docs do _not_ recommended this method for verification -- because the ETag and Docker-Content-Digest headers -- may change between the time you issue a request with a digest and when -- you receive a response back! -- -- We do it anyway and leave this warning. writeRespBody' :: FilePath -> RspBS -> Hocker FilePath -- | Compute a sha256 hash digest of the response body and compare it -- against the supplied hash digest. checkResponseIntegrity :: (MonadError HockerException m) => RspBS -> StrippedDigest -> m RspBS -- | Compute a sha256 hash digest of the response body and compare it -- against the Docker-Content-Digest header from the response. -- -- The Docker docs do *not* recommended this method for verification -- because the Docker-Content-Digest header may change between the time -- you issue a request with a digest and when you receive a response -- back! -- -- NB: some registries do not send a Docker-Content-Digest -- header, I'm not sure yet what the cause for this is but this -- function's behavior lacking that information is to ignore the hash -- check. checkResponseIntegrity' :: (MonadError HockerException m) => RspBS -> m RspBS -- | Compute a sha256 hash digest for a file and compare that hash to the -- supplied hash digest. checkFileIntegrity :: FilePath -> StrippedDigest -> IO (Either String FilePath) module Data.Docker.Nix.FetchDocker -- | fetchdocker function name. constFetchdocker :: Text -- | fetchDockerConfig function name. constFetchDockerConfig :: Text -- | fetchDockerLayer function name. constFetchDockerLayer :: Text -- | Generate a Nix expression AST from a HockerImageMeta record. -- -- This function checks that the supplied manifest JSON contains a key in -- the top-level object describing what version of the manifest we have. generate :: HockerImageMeta -> IO (Either HockerException NExpr) -- | Generate a top-level Nix Expression AST from a HockerImageMeta -- record, a config digest, and a list of layer digests. -- -- The generated AST, pretty printed, may look similar to the following: -- --
--   { fetchdocker, fetchDockerConfig, fetchDockerLayer }:
--   fetchdocker rec {
--     name = "debian";
--     registry = "https://registry-1.docker.io/v2/";
--     repository = "library";
--     imageName = "debian";
--     tag = "latest";
--     imageConfig = fetchDockerConfig {
--       inherit registry repository imageName tag;
--       sha256 = "1viqbygsz9547jy830f2lk2hcrxjf7gl9h1xda9ws5kap8yw50ry";
--     };
--     imageLayers = let
--       layer0 = fetchDockerLayer {
--         inherit registry repository imageName;
--         layerDigest = "10a267c67f423630f3afe5e04bbbc93d578861ddcc54283526222f3ad5e895b9";
--         sha256 = "1fcmx3aklbr24qsjhm6cvmhqhmrxr6xlpq75mzrk0dj2gz36g8hh";
--       };
--       in [ layer0 ];
--   }
--   
generateFetchDockerExpr :: HockerImageMeta -> ConfigDigest -> [(Base16Digest, Base32Digest)] -> Either HockerException NExpr -- | Generate a fetchdocker { ... } function call and argument -- attribute set. Please see generateFetchDockerExpr documentation -- for an example of full output. mkFetchDocker :: HockerImageMeta -> NExpr -> NExpr -> Either HockerException NExpr -- | Generate a fetchDockerConfig { ... } function call and -- argument attrset. -- -- This function takes an argument for a list of static keys to inherit -- from the parent attribute set; it helps reduce the noise in the output -- expression. mkFetchDockerConfig :: Binding NExpr -> Base32Digest -> NExpr -- | Generate a list of Nix expression ASTs representing -- fetchDockerLayer { ... } function calls. -- -- This function takes an argument for a list of static keys to inherit -- from the parent attribute set; it helps reduce the noise in the output -- expression. -- -- NB: the hash digest tuple in the second argument is the base16 encoded -- hash digest plucked from the image's manifest JSON and a -- nix-hash base32 encoded copy. -- -- This is necessary because fixed output derivations require a -- pre-computed hash (which we have, thanks to the manifest) and the hash -- must be base32 encoded using nix-hash's own base32 encoding. -- The base16 encoded hash digest is needed intact in order for the -- fetchDockerLayer builder script (which calls the -- hocker-layer utility) to download the layer from a docker -- registry. mkFetchDockerLayers :: Binding NExpr -> [(Base16Digest, Base32Digest)] -> [Binding NExpr] -- | This module only re-exports Nix modules providing Docker-specific -- functionality as it pertains to generation of Nix expression. module Data.Docker.Nix module Network.Wreq.Docker.Image.Lib -- | Like mapM but concurrently apply a function to the elements -- of the Traversable, limiting the maximum number of worker -- threads by _n_. mapPool :: Traversable t => Int -> ((String -> IO ()) -> a -> Hocker FilePath) -> t a -> Hocker (t (Either HockerException FilePath)) -- | Like mapPool but with the arguments flipped. forPool :: Traversable t => Int -> t a -> ((String -> IO ()) -> a -> Hocker FilePath) -> Hocker (t (Either HockerException FilePath)) -- | Download, verify, decompress, and write a docker container image layer -- to the filesystem. fetchLayer :: (String -> IO ()) -> (RefLayer, Layer) -> Hocker FilePath -- | Generate a manifest.json file. createImageManifest :: RepoTag -> FilePath -> [RefLayer] -> Hocker () -- | Generate a repositories json file. -- -- NB: it is JSON but Docker doesn't want it a .json extension -- unlike its sibling the manifest.json file. createImageRepository :: RepoTag -> [RefLayer] -> Hocker () -- | Tar and gzip the output dir into the final docker image archive and -- remove the output dir. createImageTar :: Hocker FilePath module Network.Wreq.Docker.Image -- | Fetch an image from the docker registery, assembling the artifacts -- into a Docker V1.2 Image. fetchImage :: HockerMeta -> IO (Either HockerException Text) -- | Fetch a layer using its digest key from the docker registery. fetchLayer :: HockerMeta -> IO (Either HockerException FilePath) -- | Fetch the configuration JSON file of the specified image from the -- docker registry. fetchConfig :: HockerMeta -> IO (Either HockerException ByteString) -- | Fetch the docker registry manifest JSON file for the specified image -- from the docker registry.. fetchImageManifest :: HockerMeta -> IO (Either HockerException ByteString)