module Hocker.Lib where
import Control.Exception (throwIO)
import Control.Lens
import qualified Control.Monad.Except as Except
import Control.Monad.IO.Class (MonadIO (..))
import qualified Crypto.Hash as Hash
import qualified Data.Aeson
import qualified Data.Aeson.Encode.Pretty as AP
import Data.Aeson.Lens
import qualified Data.ByteString.Char8 as C8
import Data.ByteString.Lazy.Char8 as C8L
import Data.Coerce
import Data.Monoid
import Data.Text (Text)
import qualified Data.Text as Text
import Data.Text.Encoding (encodeUtf8)
import qualified Network.Wreq as Wreq
import Nix.Expr (NExpr)
import Nix.Pretty
import System.Directory (findExecutable)
import System.Environment (getProgName)
import System.Exit as Exit
import System.FilePath.Posix as File
import Text.PrettyPrint.ANSI.Leijen as Text.PrettyPrint (SimpleDoc,
displayS,
renderPretty)
import URI.ByteString
import Data.Docker.Image.Types
import Hocker.Types
import Hocker.Types.Exceptions
import Hocker.Types.ImageName
import Hocker.Types.ImageTag
die :: MonadIO io => Text -> io a
die = liftIO . throwIO . userError . Text.unpack
exitProgFail :: String -> IO a
exitProgFail msg = do
name <- getProgName
Exit.die $ name ++ ": " ++ msg
writeOrPrint :: Maybe FilePath -> C8L.ByteString -> IO ()
writeOrPrint filepath content = maybe (C8L.putStrLn content) writeContent filepath
where
writeContent p = C8L.writeFile p content >> Prelude.putStrLn p
mkOutImage :: ImageName
-> FilePath
-> FilePath
mkOutImage n o = o </> (takeBaseName $ coerce n)
mkOutConfig :: ImageName
-> ImageTag
-> FilePath
-> FilePath
mkOutConfig n t o = o </> Prelude.concat
[ (takeBaseName $ coerce n)
, "_", coerce t
, "-config.json"
]
mkOutManifest :: ImageName
-> ImageTag
-> FilePath
-> FilePath
mkOutManifest n t o = o </> Prelude.concat
[ (takeBaseName $ coerce n)
, "_", coerce t
, "-manifest.json"
]
joinURIPath :: [String]
-> RegistryURI
-> RegistryURI
joinURIPath pts uri@URI{..} = uri { uriPath = joinedParts }
where
joinedParts = C8.pack $ File.joinPath ("/":"v2":(C8.unpack uriPath):pts)
opts :: Maybe Wreq.Auth -> Wreq.Options
opts bAuth = Wreq.defaults & Wreq.auth .~ bAuth
sha256 :: C8L.ByteString -> Hash.Digest Hash.SHA256
sha256 = Hash.hashlazy
stripHashId :: Text -> Text
stripHashId = snd . Text.breakOnEnd ":"
encodeCanonical :: Data.Aeson.ToJSON a => a -> C8L.ByteString
encodeCanonical = AP.encodePretty' conf
where
conf = AP.defConfig { AP.confIndent = AP.Spaces 0, AP.confCompare = compare }
requirePath :: (Except.MonadError HockerException m)
=> Maybe FilePath
-> m (FilePath)
requirePath = maybe pathError pure
where
pathError =
Except.throwError
(hockerException "To fetch and assemble a docker image, '--out=<path>' must be supplied")
getConfigDigest :: (Except.MonadError HockerException m)
=> C8L.ByteString
-> m (Hash.Digest Hash.SHA256)
getConfigDigest (view (key "config" . key "digest" . _String) -> digest) =
maybe badDigest return parsedDigest
where
parsedDigest = toDigest $ encodeUtf8 digest
badDigest = Except.throwError $ hockerException "Failed parsing the config hash digest"
splitRepository :: ImageName -> (RepoNamePart, ImageNamePart)
splitRepository (ImageName (Text.pack -> n)) = over _2 Text.tail $ Text.break (=='/') n
renderNixExpr :: NExpr -> Text.PrettyPrint.SimpleDoc
renderNixExpr = renderPretty 0.4 120 . prettyNix
pprintNixExpr :: NExpr -> IO ()
pprintNixExpr expr = Prelude.putStrLn (displayS (renderNixExpr expr) "")
findExec :: (MonadIO m, Except.MonadError HockerException m)
=> String
-> m Prelude.FilePath
findExec execname = (liftIO $ findExecutable execname) >>= \case
Just v -> return v
Nothing -> Except.throwError $
HockerException
("cannot find executable `" <> execname <> "'")
Nothing
Nothing