content: {-# LANGUAGE GADTs #-}
  {-# LANGUAGE DataKinds #-}
  {-# LANGUAGE ConstraintKinds #-}
  {-# LANGUAGE TemplateHaskell #-}
  {-# LANGUAGE TypeApplications #-}
  {-# LANGUAGE TypeOperators #-}
  
  module Test.Sandwich.Contexts.Kubernetes.KindCluster.Config where
  
  import Data.Aeson as A
  import Data.Aeson.TH as A
  import qualified Data.List as L
  import Data.String.Interpolate
  import qualified Data.Vector as V
  import Relude
  
  
  data ExtraPortMapping = ExtraPortMapping {
    containerPort :: Int16
    , hostPort :: Int16
  
    -- | Set the bind address on the host
    -- 0.0.0.0 is the current default
    , listenAddress :: Maybe String
  
    -- | Set the protocol to one of TCP, UDP, SCTP.
    --  TCP is the default
    , protocol :: Maybe String
    }
  deriveToJSON A.defaultOptions ''ExtraPortMapping
  
  data ExtraMount = ExtraMount {
    hostPath :: String
    , containerPath :: String
    -- | If set, the mount is read-only.
    -- default false
    , readOnly :: Maybe Bool
    -- | If set, the mount needs SELinux relabeling.
    -- default false
    , selinuxRelabel :: Maybe Bool
  
    -- | Set propagation mode (None, HostToContainer or Bidirectional).
    -- See https://kubernetes.io/docs/concepts/storage/volumes/#mount-propagation.
    --
    -- WARNING: You very likely do not need this field.
    --
    -- This field controls propagation of *additional* mounts created
    -- at runtime underneath this mount.
    --
    -- On MacOS with Docker Desktop, if the mount is from macOS and not the
    -- docker desktop VM, you cannot use this field. You can use it for
    -- mounts to the linux VM.
    , propagation :: Maybe String
    }
  deriveToJSON A.defaultOptions ''ExtraMount
  
  kindConfig :: Int -> Map Text Text -> [ExtraPortMapping] -> [ExtraMount] -> A.Value
  kindConfig numNodes _containerLabels extraPortMappings extraMounts = A.object [
    ("kind", A.String "Cluster")
    , ("apiVersion", A.String "kind.x-k8s.io/v1alpha4")
    , ("nodes", A.Array (V.fromList nodes))
    ]
    where
      nodes = mkNode "control-plane" : (L.replicate (numNodes - 1) (mkNode "worker"))
  
      mkNode :: Text -> A.Value
      mkNode role = A.object ([
        ("role", A.String role)
        ]
        <> if role == "control-plane" then [("kubeadmConfigPatches", A.Array (V.fromList [A.String extraPatches]))] else []
        <> if L.null extraPortMappings then [] else [("extraPortMappings", A.Array (V.fromList (fmap A.toJSON extraPortMappings)))]
        <> if L.null extraMounts then [] else [("extraMounts", A.Array (V.fromList (fmap A.toJSON extraMounts)))]
        )
  
      extraPatches = [__i|kind: InitConfiguration
                          nodeRegistration:
                            kubeletExtraArgs:
                              node-labels: "ingress-ready=true"
                              authorization-mode: "AlwaysAllow"
                              streaming-connection-idle-timeout: "0"
                         |]
  
  
  -- Note: here's how to provide an extra container registry:
  -- containerdConfigPatches:
  -- - |-
  --   [plugins."io.containerd.grpc.v1.cri".registry.mirrors."#{registryHostname}:5000"]
  --     endpoint = ["http://#{registryHostname}:5000"]
  