terraform {
  required_providers {
    alicloud = {
      source  = "aliyun/alicloud"
      version = ">= 1.268"
    }
  }
}

resource "alicloud_vpc" "this" {
  vpc_name   = "${var.cluster_name}-vpc"
  cidr_block = var.vpc_cidr
  tags       = var.tags
}

resource "alicloud_vswitch" "node_switch" {
  count        = length(var.zone_ids)
  vpc_id       = alicloud_vpc.this.id
  zone_id      = var.zone_ids[count.index]
  cidr_block   = cidrsubnet(var.vpc_cidr, 8, count.index)
  vswitch_name = "${var.cluster_name}-node-${substr(var.zone_ids[count.index], -1, -1)}"
  tags         = var.tags
}

resource "alicloud_vswitch" "pod_switch" {
  count        = length(var.zone_ids)
  vpc_id       = alicloud_vpc.this.id
  zone_id      = var.zone_ids[count.index]
  cidr_block   = cidrsubnet(var.vpc_cidr, 8, count.index + length(var.zone_ids))
  vswitch_name = "${var.cluster_name}-pod-${substr(var.zone_ids[count.index], -1, -1)}"
  tags         = var.tags
}

resource "alicloud_security_group" "this" {
  security_group_name = "${var.cluster_name}-sg"
  vpc_id              = alicloud_vpc.this.id
  description         = "ACK cluster SG"
  tags                = var.tags
}

resource "alicloud_cs_managed_kubernetes" "this" {
  name                = var.cluster_name
  vswitch_ids         = alicloud_vswitch.node_switch.*.id
  pod_vswitch_ids     = alicloud_vswitch.pod_switch.*.id
  cluster_spec        = "ack.pro.small"
  service_cidr        = var.service_cidr
  enable_rrsa         = true
  security_group_id   = alicloud_security_group.this.id
  deletion_protection = false
  proxy_mode          = "ipvs"
  tags                = var.tags

  addons {
    name = "terway-eniip"
  }
  addons {
    name = "ack-pod-identity-webhook"
  }
  addons {
    name = "ack-ram-authenticator"
    config = jsonencode({
      EnableNonBootstrapMapping = true
    })
  }
  addons {
    name    = "managed-aliyun-acr-credential-helper"
    version = "v24.01.29.1-5318af4-aliyun"
    config = jsonencode({
      AcrInstanceInfo = [{
        instanceId = var.acr_instance_id
        regionId   = var.acr_region
      }]
      watchNamespace    = "all"
      enableRRSA        = false
      serviceAccount    = "*"
      expiringThreshold = "15m"
    })
  }
}

resource "alicloud_cs_kubernetes_node_pool" "this" {
  count                = length(var.node_groups)
  cluster_id           = alicloud_cs_managed_kubernetes.this.id
  node_pool_name       = var.node_groups[count.index].name
  vswitch_ids          = alicloud_vswitch.node_switch.*.id
  instance_types       = var.node_groups[count.index].instance_types
  system_disk_category = var.node_groups[count.index].system_disk_category
  system_disk_size     = var.node_groups[count.index].system_disk_size
  multi_az_policy      = "BALANCE"
  security_group_ids   = [alicloud_security_group.this.id]
  desired_size         = var.node_groups[count.index].desired_size
  data_disks {
    category = var.node_groups[count.index].data_disk_category
    size     = var.node_groups[count.index].data_disk_size
  }
  tags = var.tags

  scaling_config {
    enable      = var.node_groups[count.index].auto_scaling.enable
    min_size    = var.node_groups[count.index].auto_scaling.min
    max_size    = var.node_groups[count.index].auto_scaling.max
    is_bond_eip = false
  }

  management {
    enable      = true
    auto_repair = true
  }
}

resource "alicloud_cs_kubernetes_permissions" "this" {
  count = length(var.admin_ram_ids)
  uid   = var.admin_ram_ids[count.index]
  permissions {
    cluster     = alicloud_cs_managed_kubernetes.this.id
    role_type   = "cluster"
    role_name   = "admin"
    namespace   = ""
    is_custom   = false
    is_ram_role = true
  }
}

resource "alicloud_slb_acl" "acl" {
  name       = "${alicloud_cs_managed_kubernetes.this.name}-slb-acl"
  ip_version = "ipv4"
}

resource "alicloud_slb_acl_entry_attachment" "attachment" {
  # https://pingcap.feishu.cn/wiki/AmgUw1qzpi00U6kdb6ncecOZnOd
  for_each = toset(concat(["100.104.0.0/16", var.vpc_cidr], var.public_access_cidrs))
  acl_id   = alicloud_slb_acl.acl.id
  entry    = each.key
}

resource "null_resource" "acl_attachment" {
  count = length(var.public_access_cidrs) > 0 ? 1 : 0

  triggers = {
    slb_id  = alicloud_cs_managed_kubernetes.this.slb_id
    acl_id  = alicloud_slb_acl.acl.id
    profile = var.profile
    region  = var.region
  }

  provisioner "local-exec" {
    when    = create
    command = <<-EOT
      aliyun slb SetLoadBalancerTCPListenerAttribute \
        --profile ${self.triggers.profile} \
        --region ${self.triggers.region} \
        --LoadBalancerId ${self.triggers.slb_id} \
        --ListenerPort 6443 \
        --AclStatus 'on' \
        --AclType white \
        --AclId ${self.triggers.acl_id}
    EOT
  }

  provisioner "local-exec" {
    when    = destroy
    command = <<-EOT
      aliyun slb SetLoadBalancerTCPListenerAttribute \
        --profile ${self.triggers.profile} \
        --region ${self.triggers.region} \
        --LoadBalancerId ${self.triggers.slb_id} \
        --ListenerPort 6443 \
        --AclStatus 'off' \
    EOT
  }
}

data "alicloud_nat_gateways" "this" {
  depends_on = [alicloud_cs_kubernetes_permissions.this]
  vpc_id     = alicloud_vpc.this.id
}

data "external" "config" {
  program = ["bash", "-c", <<-EOT
    kube_config=$(aliyun cs DescribeClusterUserKubeconfig \
      --profile ${var.profile} \
      --region ${var.region} \
      --ClusterId ${alicloud_cs_managed_kubernetes.this.id} \
      --PrivateIpAddress false | jq -r '.config')
    server=$(echo "$kube_config" | yq -r '.clusters[0].cluster.server')
    ca=$(echo "$kube_config" | yq -r '.clusters[0].cluster.certificate-authority-data')
    key=$(echo "$kube_config" | yq -r '.users[0].user.client-key-data')
    cert=$(echo "$kube_config" | yq -r '.users[0].user.client-certificate-data')
    kubeconfig_b64=$(echo -n "$kube_config" | base64 -w 0)
    cat <<EOF
    {
      "server": "$${server}",
      "ca": "$${ca}",
      "key": "$${key}",
      "cert": "$${cert}",
      "kubeconfig_b64": "$${kubeconfig_b64}"
    }
    EOF
    EOT
  ]

  lifecycle {
    postcondition {
      condition     = self.result.server != ""
      error_message = "server is empty"
    }

    postcondition {
      condition     = self.result.ca != ""
      error_message = "ca is empty"
    }

    postcondition {
      condition     = self.result.key != ""
      error_message = "key is empty"
    }

    postcondition {
      condition     = self.result.cert != ""
      error_message = "cert is empty"
    }

    postcondition {
      condition     = self.result.kubeconfig_b64 != ""
      error_message = "kubeconfig is empty"
    }
  }
}

output "ca" {
  value     = data.external.config.result.ca
  sensitive = true
}

output "endpoint" {
  value = data.external.config.result.server
}

output "argocd_config" {
  value = jsonencode({
    tlsClientConfig = {
      insecure = false
      certData = data.external.config.result.cert
      keyData  = data.external.config.result.key
      caData   = data.external.config.result.ca
    }
  })
  sensitive = true
}

output "kubeconfig" {
  value     = base64decode(data.external.config.result.kubeconfig_b64)
  sensitive = true
}

output "public_ips" {
  value = flatten(data.alicloud_nat_gateways.this.gateways.*.ip_lists)
}

output "oidc_issuer" {
  value = alicloud_cs_managed_kubernetes.this.rrsa_metadata.0.rrsa_oidc_issuer_url
}

output "oidc_arn" {
  value = alicloud_cs_managed_kubernetes.this.rrsa_metadata.0.ram_oidc_provider_arn
}

output "cluster_id" {
  value = alicloud_cs_managed_kubernetes.this.id
}

output "security_group_id" {
  value = alicloud_security_group.this.id
}

output "pod_switch_ids" {
  value = alicloud_vswitch.pod_switch.*.id
}

output "pod_switch_zone_ids" {
  value = alicloud_vswitch.pod_switch.*.zone_id
}

output "vpc_id" {
  value = alicloud_vpc.this.id
}
