locals {
  cluster_name      = "${var.prefix}-${var.name}"
  cluster_addresses = module.cluster-hcloud.ips != null ? [for ip in module.cluster-hcloud.ips : { ip = ip }] : []
}

data "vault_kv_secret_v2" "minio" {
  mount = local.cluster_name
  name  = "minio"
}

resource "tls_private_key" "cluster" {
  algorithm = "RSA"
  rsa_bits  = 2048
}

resource "rancher2_cluster_v2" "cluster" {
  depends_on = [var.wait_on]
  provider = rancher2.admin

  name               = local.cluster_name
  kubernetes_version = var.kubernetes_version
}

resource "hcloud_ssh_key" "cluster" {
  name       = "${local.cluster_name}-ssh-key"
  public_key = tls_private_key.cluster.public_key_openssh

  lifecycle {
    ignore_changes = [public_key]
  }
}

// then do management module
module "cluster-management" {
  source = "./management"

  cluster          = local.cluster_name
  k8s_config_yaml  = rancher2_cluster_v2.cluster.kube_config
  minio_access_key = data.vault_kv_secret_v2.minio.data["access_key"]
  minio_secret_key = data.vault_kv_secret_v2.minio.data["secret_key"]
  minio_server     = var.minio_server
  ssh_private_key  = tls_private_key.cluster.private_key_pem
  ssh_public_key   = tls_private_key.cluster.public_key_openssh
  vault_token      = var.vault_token
  vault_server     = var.vault_server
}

module "devops" {
  source = "./devops"

  minio_access_key = data.vault_kv_secret_v2.minio.data["access_key"]
  minio_secret_key = data.vault_kv_secret_v2.minio.data["secret_key"]
  minio_server     = var.minio_server

  cluster         = local.cluster_name
  k8s_config_yaml = var.k8s_config_yaml
}

module "cluster-hcloud" {
  source        = "./hcloud"
  node_count    = var.node_count
  instance_type = var.node_instance_type

  cluster_registration_command = rancher2_cluster_v2.cluster.cluster_registration_token.0.insecure_node_command
  hcloud_network_id            = var.hcloud_network_id
  hcloud_token                 = var.hcloud_token
  ssh_key_id                   = hcloud_ssh_key.cluster.id
  prefix                       = var.prefix
  name                         = var.name
}

resource "kubernetes_namespace" "cluster" {
  metadata {
    name = local.cluster_name
  }

  lifecycle {
    ignore_changes = [metadata]
  }
}

module "cluster-tls" {
  source = "./tls"

  name            = local.cluster_name
  namespace       = kubernetes_namespace.cluster.metadata[0].name
  hosts           = var.hosts
  k8s_config_yaml = var.k8s_config_yaml
}

resource "kubernetes_manifest" "cluster-endpoints" {
  manifest = {
    apiVersion = "v1"
    kind       = "Endpoints"
    metadata = {
      name      = local.cluster_name
      namespace = kubernetes_namespace.cluster.metadata[0].name
    }

    subsets = [
      {
        addresses = local.cluster_addresses,
        ports = [
          {
            port = 80
          }
        ]
      }
    ]
  }
}

resource "kubernetes_manifest" "cluster-service" {
  depends_on = [kubernetes_manifest.cluster-endpoints]
  manifest = {
    apiVersion = "v1"
    kind       = "Service"
    metadata = {
      name      = local.cluster_name
      namespace = kubernetes_namespace.cluster.metadata[0].name
    }

    spec = {
      ports = [
        {
          port       = 80
          protocol   = "TCP"
          targetPort = 80
        }
      ]
      type = "ClusterIP"
    }
  }
}

resource "kubernetes_manifest" "application_ingress" {
  depends_on = [
    kubernetes_manifest.cluster-endpoints,
    kubernetes_manifest.cluster-service,
    module.cluster-tls.installed
  ]

  manifest = {
    apiVersion = "networking.k8s.io/v1"
    kind       = "Ingress"
    metadata = {
      name      = local.cluster_name
      namespace = kubernetes_namespace.cluster.metadata[0].name
      annotations = {
        "kubernetes.io/ingress.class"                                                     = "traefik"
        "cert-manager.io/cluster-issuer"                                                  = "letsencrypt"
        "traefik.ingress.kubernetes.io/router.entrypoints"                                = "web,websecure"
        "traefik.ingress.kubernetes.io/router.middlewares"                                = "default-redirect-to-https@kubernetescrd,default-preserve-host-headers@kubernetescrd"
        "traefik.ingress.kubernetes.io/service.backend.loadbalancer.server.scheme"        = "http"
        "traefik.ingress.kubernetes.io/service.backend.loadbalancer.healthcheck.path"     = "/healthz"
        "traefik.ingress.kubernetes.io/service.backend.loadbalancer.healthcheck.interval" = "10s"
        "traefik.ingress.kubernetes.io/service.backend.loadbalancer.healthcheck.timeout"  = "3s"
        #"traefik.ingress.kubernetes.io/service.backend.loadbalancer.sticky.cookie"        = "true"
        #"traefik.ingress.kubernetes.io/service.backend.loadbalancer.sticky.cookie.name"   = "platform_sticky"
      }
    }

    spec = {
      ingressClassName = "traefik"
      rules            = var.hosts != null ? [
        for host in var.hosts : {
          host = host
          http = {
            paths = [
              {
                path     = "/"
                pathType = "Prefix"
                backend = {
                  service = {
                    name = kubernetes_manifest.cluster-service.manifest.metadata.name
                    port = {
                      number = 80
                    }
                  }
                }
              }
            ]
          }
        }
      ] : [],
      tls = [
        {
          hosts      = var.hosts
          secretName = "${local.cluster_name}-tls"
        }
        // TODO: Optional extra TLS from external secret
      ]
    }
  }
}
