packages feed

sandwich-contexts-kubernetes-0.1.0.0: lib/Test/Sandwich/Contexts/Kubernetes/MinioS3Server/Parsing.hs


module Test.Sandwich.Contexts.Kubernetes.MinioS3Server.Parsing (
  parseMinioUserAndPassword
  , transformKustomizeChunks
  ) where

import Control.Lens
import Data.Aeson (FromJSON)
import qualified Data.Aeson as A
import Data.Aeson.Lens
import qualified Data.Map as M
import Data.String.Interpolate
import Data.Text as T
import qualified Data.Yaml as Yaml
import Kubernetes.OpenAPI.Model as Kubernetes
import Relude
import Safe (headMay)
import Test.Sandwich.Contexts.Kubernetes.Util.Aeson
import Text.Regex.TDFA


parseMinioUserAndPassword :: Text -> Maybe (Text, Text)
parseMinioUserAndPassword txt = case (userValues, passwordValues) of
  (Just (_before, _fullMatch, _after, [user]), Just (_, _, _, [password])) -> Just (user, toText password)
  _ -> Nothing
  where
    userValues :: Maybe (Text, Text, Text, [Text]) = txt =~~ ([i|MINIO_ROOT_USER="([^"]*)"|] :: Text)
    passwordValues :: Maybe (Text, Text, Text, [Text]) = txt =~~ ([i|MINIO_ROOT_PASSWORD="([^"]*)"|] :: Text)

-- testInput :: Text
-- testInput = [__i|export MINIO_ROOT_USER="WXSTFUWIRS04LMGIMJGV"
--                  export MINIO_ROOT_PASSWORD="NCDCfTaiXcGHq8QRfSaXMAWOXgdrhpGwPSkoYMWf"|]

transformKustomizeChunks :: String -> String -> [Text] -> Either String ((Text, Text), Text)
transformKustomizeChunks namespace deploymentName initialChunks = do
  userAndPassword <- getUserAndPassword initialChunks

  return (userAndPassword, finalYaml)

  where
    finalYaml = initialChunks
              -- Don't include a kind: Namespace
              & Relude.filter (not . isNamespace)

              -- Set metadata.namespace on all values
              & fmap (setMetaNamespace namespace)

              -- Disable TLS
              & fmap disableTLS

              -- Set deployment name
              & fmap (setDeploymentNameAndPoolSize deploymentName)

              -- Combine everything into multi-document Yaml
              & T.intercalate "---\n"

getUserAndPassword :: [Text] -> Either String (Text, Text)
getUserAndPassword chunks = case headMay (mapMaybe getUserAndPassword' chunks) of
  Nothing -> Left "Couldn't find user/password YAML."
  Just x -> Right x
  where
    getUserAndPassword' :: Text -> Maybe (Text, Text)
    getUserAndPassword' (decode -> Right (V1Secret {v1SecretMetadata=(Just (V1ObjectMeta {v1ObjectMetaName=(Just "storage-configuration")}))
                                                   , v1SecretStringData=(Just (M.lookup "config.env" -> Just t))
                                                   }))
      = parseMinioUserAndPassword t
    getUserAndPassword' _ = Nothing

isNamespace :: Text -> Bool
isNamespace (decode -> Right (A.Object (aesonLookup "kind" -> Just (A.String "Namespace")))) = True
isNamespace _ = False

setMetaNamespace :: String -> Text -> Text
setMetaNamespace namespace (decode -> Right (A.Object obj1@(aesonLookup "metadata" -> Just (A.Object obj2@(aesonLookup "namespace" -> Just (A.String _)))))) =
  decodeUtf8 (Yaml.encode obj1')
  where
    obj1' :: A.Value
    obj1' = A.Object (aesonInsert "metadata" obj2' obj1)

    obj2' :: A.Value
    obj2' = A.Object (aesonInsert "namespace" (A.String (toText namespace)) obj2)
setMetaNamespace _ t = t

-- Do the steps to disable TLS in the tenant CRD.
-- See https://min.io/docs/minio/kubernetes/upstream/reference/operator-crd.html#tenantspec
disableTLS :: Text -> Text
disableTLS (decode -> Right x@(A.Object (aesonLookup "kind" -> Just (A.String "Tenant")))) = decodeUtf8 (Yaml.encode x')
  where
    x' = x
       & set (_Object . ix "spec" . _Object . ix "requestAutoCert") (A.Bool False)
       & set (_Object . ix "spec" . _Object . at "externalCertSecret") Nothing
disableTLS t = t

setDeploymentNameAndPoolSize :: String -> Text -> Text
setDeploymentNameAndPoolSize deploymentName (decode -> Right x@(A.Object (aesonLookup "kind" -> Just (A.String "Tenant")))) = decodeUtf8 (Yaml.encode x')
  where
    x' = x
       & set (_Object . ix "metadata" . _Object . ix "name") (A.String (toText deploymentName))
       & set (_Object . ix "spec" . _Object . ix "pools" . _Array . ix 0 . _Object . ix "servers") (A.Number 1)
setDeploymentNameAndPoolSize _ t = t

decode :: FromJSON a => Text -> Either Yaml.ParseException a
decode = Yaml.decodeEither' . encodeUtf8