Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 68 additions & 0 deletions .terraform.lock.hcl

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

59 changes: 58 additions & 1 deletion acm.tf
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,16 @@
# steps are required; tofu apply blocks until the cert reaches ISSUED status.
# ─────────────────────────────────────────────────────────────────────────────

locals {
# Determine if any service needs a certificate (for Route 53 zone data source)
needs_route53_zone = (
var.argocd_hostname != null ||
var.skypilot_api_server_hostname != null
)
}

data "aws_route53_zone" "main" {
count = var.argocd_hostname != null ? 1 : 0
count = local.needs_route53_zone ? 1 : 0
name = var.route53_zone_name
private_zone = false
}
Expand Down Expand Up @@ -56,3 +64,52 @@ resource "aws_route53_record" "argocd" {
ttl = 60
records = [module.eks_addons.argocd_lb_hostname]
}

# ─────────────────────────────────────────────────────────────────────────────
# SkyPilot API Server Certificate — auto-created when hostname is configured
# ─────────────────────────────────────────────────────────────────────────────

resource "aws_acm_certificate" "skypilot" {
count = var.skypilot_api_server_hostname != null ? 1 : 0
domain_name = var.skypilot_api_server_hostname
validation_method = "DNS"

lifecycle {
create_before_destroy = true
}

tags = var.tags
}

resource "aws_route53_record" "skypilot_cert_validation" {
for_each = var.skypilot_api_server_hostname != null ? {
for dvo in aws_acm_certificate.skypilot[0].domain_validation_options : dvo.domain_name => {
name = dvo.resource_record_name
record = dvo.resource_record_value
type = dvo.resource_record_type
}
} : {}

allow_overwrite = true
name = each.value.name
records = [each.value.record]
ttl = 60
type = each.value.type
zone_id = data.aws_route53_zone.main[0].zone_id
}

resource "aws_acm_certificate_validation" "skypilot" {
count = var.skypilot_api_server_hostname != null ? 1 : 0
certificate_arn = aws_acm_certificate.skypilot[0].arn
validation_record_fqdns = [for r in aws_route53_record.skypilot_cert_validation : r.fqdn]
}

# Route 53 record — points the SkyPilot hostname at the ALB created by AWS LBC.
resource "aws_route53_record" "skypilot" {
count = var.skypilot_api_server_hostname != null ? 1 : 0
zone_id = data.aws_route53_zone.main[0].zone_id
name = var.skypilot_api_server_hostname
type = "CNAME"
ttl = 60
records = [module.eks_addons.skypilot_api_server_lb_hostname]
}
16 changes: 16 additions & 0 deletions main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,14 @@ terraform {
source = "hashicorp/kubernetes"
version = "~> 2.0"
}
htpasswd = {
source = "loafoe/htpasswd"
version = "~> 1.2"
}
random = {
source = "hashicorp/random"
version = "~> 3.0"
}
}
}

Expand Down Expand Up @@ -207,6 +215,14 @@ module "eks_addons" {
tailscale_oauth_client_secret = var.tailscale_oauth_client_secret
tailscale_chart_version = var.tailscale_chart_version

# SkyPilot API Server
skypilot_api_server_enabled = var.skypilot_api_server_enabled
skypilot_api_server_web_password = var.skypilot_api_server_web_password
skypilot_api_server_hostname = var.skypilot_api_server_hostname
skypilot_api_server_certificate_arn = var.skypilot_api_server_hostname != null ? aws_acm_certificate_validation.skypilot[0].certificate_arn : null
skypilot_api_server_chart_version = var.skypilot_api_server_chart_version
skypilot_api_server_resources = var.skypilot_api_server_resources

tags = var.tags

depends_on = [module.eks]
Expand Down
54 changes: 42 additions & 12 deletions modules/aws/eks-addons/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -111,19 +111,49 @@ resource "aws_eks_addon" "ebs_csi" {
tags = var.tags
}

# Set gp3 (EBS CSI) as default StorageClass and enable volume expansion.
resource "terraform_data" "gp3_default_storageclass" {
# gp3 StorageClass using EBS CSI driver (replaces in-tree gp2)
resource "kubectl_manifest" "gp3_storageclass" {
yaml_body = yamlencode({
apiVersion = "storage.k8s.io/v1"
kind = "StorageClass"
metadata = {
name = "gp3"
annotations = {
"storageclass.kubernetes.io/is-default-class" = "true"
}
}
provisioner = "ebs.csi.aws.com"
parameters = {
type = "gp3"
fsType = "ext4"
}
reclaimPolicy = "Delete"
volumeBindingMode = "WaitForFirstConsumer"
allowVolumeExpansion = true
})

depends_on = [aws_eks_addon.ebs_csi]
}

provisioner "local-exec" {
command = <<-EOT
kubectl patch storageclass gp3 -p '{"metadata":{"annotations":{"storageclass.kubernetes.io/is-default-class":"true"}}}' --type=merge
kubectl patch storageclass gp3 -p '{"allowVolumeExpansion":true}' --type=merge
kubectl patch storageclass gp3 -p '{"reclaimPolicy":"Delete"}' --type=merge
kubectl patch storageclass gp2 -p '{"metadata":{"annotations":{"storageclass.kubernetes.io/is-default-class":null}}}' --type=merge 2>/dev/null || true
EOT
}
# Remove default annotation from the legacy in-tree gp2 storage class
resource "kubectl_manifest" "gp2_remove_default" {
yaml_body = yamlencode({
apiVersion = "storage.k8s.io/v1"
kind = "StorageClass"
metadata = {
name = "gp2"
annotations = {
"storageclass.kubernetes.io/is-default-class" = "false"
}
}
provisioner = "kubernetes.io/aws-ebs"
reclaimPolicy = "Delete"
volumeBindingMode = "WaitForFirstConsumer"
})

depends_on = [kubectl_manifest.gp3_storageclass]

# Re-run when inputs change (triggers replace)
input = "gp3-default"
lifecycle {
ignore_changes = [yaml_body]
}
}
15 changes: 15 additions & 0 deletions modules/aws/eks-addons/outputs.tf
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,18 @@ output "argocd_lb_hostname" {
description = "ALB hostname assigned to the ArgoCD ingress by AWS LBC. Null when ArgoCD is not exposed."
value = local.expose_argocd ? data.kubernetes_ingress_v1.argocd[0].status[0].load_balancer[0].ingress[0].hostname : null
}

output "skypilot_api_server_installed" {
description = "Whether SkyPilot API server was installed."
value = local.install_skypilot_api_server
}

output "skypilot_api_server_namespace" {
description = "Kubernetes namespace where SkyPilot API server is deployed."
value = local.install_skypilot_api_server ? var.skypilot_api_server_namespace : null
}

output "skypilot_api_server_lb_hostname" {
description = "ALB hostname assigned to the SkyPilot API server ingress by AWS LBC. Null when SkyPilot is not exposed."
value = local.expose_skypilot_api_server ? data.kubernetes_ingress_v1.skypilot_api_server[0].status[0].load_balancer[0].ingress[0].hostname : null
}
Loading
Loading