Skip to content

Commit 15bbf07

Browse files
feat: make EKS Auto Mode optional (#32)
* feat: optionally disable automode * feat: added test case for auto mode disabled
1 parent 5991e3f commit 15bbf07

6 files changed

Lines changed: 356 additions & 8 deletions

File tree

.spacelift/config.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,7 @@ tests:
1010
project_root: examples/basic-example
1111
environment:
1212
TF_VAR_aws_region: "eu-west-2"
13+
- name: Without auto mode
14+
project_root: examples/without-auto-mode
15+
environment:
16+
TF_VAR_aws_region: "eu-west-2"

eks.tf

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,31 @@ module "eks" {
1818

1919
# Enable EKS Auto mode using a general purpose node pool
2020
compute_config = {
21-
enabled = true
22-
node_pools = ["general-purpose"]
21+
enabled = var.eks_auto_mode_enabled
22+
node_pools = var.eks_auto_mode_enabled ? ["general-purpose"] : []
2323
}
2424

25+
# When auto mode is disabled, the essential networking addons that auto mode
26+
# normally manages must be installed BEFORE node groups via before_compute.
27+
# Without this ordering, nodes deadlock: they need VPC CNI to pass health
28+
# checks, but the default addon resource waits for node groups to complete.
29+
addons = var.eks_auto_mode_enabled ? {} : {
30+
vpc-cni = {
31+
most_recent = true
32+
before_compute = true
33+
}
34+
kube-proxy = {
35+
most_recent = true
36+
before_compute = true
37+
}
38+
coredns = {
39+
most_recent = true
40+
before_compute = true
41+
}
42+
}
43+
44+
eks_managed_node_groups = var.eks_auto_mode_enabled ? null : var.eks_managed_node_groups
45+
2546
tags = {
2647
Name = "Spacelift cluster ${module.spacelift.unique_suffix}"
2748
}

examples/without-auto-mode/main.tf

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
data "aws_rds_engine_version" "postgres" {
2+
engine = "aurora-postgresql"
3+
latest = true
4+
}
5+
6+
module "spacelift_eks_selfhosted" {
7+
source = "../../"
8+
9+
aws_region = var.aws_region
10+
server_domain = "test.spacelift.example.com"
11+
rds_engine_version = data.aws_rds_engine_version.postgres.version_actual
12+
13+
eks_cluster_version = "1.32"
14+
15+
eks_upgrade_policy = {
16+
support_type = "STANDARD"
17+
}
18+
19+
# Disable EKS Auto Mode and use managed node groups instead
20+
eks_auto_mode_enabled = false
21+
22+
eks_managed_node_groups = {
23+
spacelift = {
24+
ami_type = "AL2023_x86_64_STANDARD"
25+
instance_types = ["m5.large"]
26+
min_size = 2
27+
max_size = 4
28+
desired_size = 2
29+
}
30+
}
31+
32+
# For easier test cleanup:
33+
rds_delete_protection_enabled = false
34+
s3_retain_on_destroy = false
35+
ecr_force_delete = true
36+
rds_instance_configuration = {}
37+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
provider "aws" {
2+
region = var.aws_region
3+
4+
default_tags {
5+
tags = {
6+
repo = "github.com/spacelift-io/terraform-aws-eks-spacelift-selfhosted"
7+
testcase = "without-auto-mode"
8+
}
9+
}
10+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
variable "aws_region" {
2+
type = string
3+
}

variables.tf

Lines changed: 279 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,6 @@ variable "server_domain" {
1616
description = "The domain that Spacelift is being hosted on, for example spacelift.example.com."
1717
}
1818

19-
variable "server_port" {
20-
type = number
21-
description = "The port that server pods listen on for HTTP. Used to setup security group rules between the load balancer and server pods."
22-
default = 1983
23-
}
24-
2519
variable "license_token" {
2620
type = string
2721
description = "The JWT token for using Spacelift. Only required for generating the kubernetes_secrets output. It can be ignored if you are not using that output."
@@ -330,6 +324,285 @@ variable "eks_cluster_name" {
330324
default = null
331325
}
332326

327+
variable "eks_auto_mode_enabled" {
328+
type = bool
329+
description = "Whether to enable EKS Auto Mode."
330+
default = true
331+
}
332+
333+
variable "eks_managed_node_groups" {
334+
description = "Map of EKS managed node group definitions to create"
335+
type = map(object({
336+
create = optional(bool)
337+
kubernetes_version = optional(string)
338+
339+
# EKS Managed Node Group
340+
name = optional(string) # Will fall back to map key
341+
use_name_prefix = optional(bool)
342+
subnet_ids = optional(list(string))
343+
min_size = optional(number)
344+
max_size = optional(number)
345+
desired_size = optional(number)
346+
ami_id = optional(string)
347+
ami_type = optional(string)
348+
ami_release_version = optional(string)
349+
use_latest_ami_release_version = optional(bool)
350+
capacity_type = optional(string)
351+
disk_size = optional(number)
352+
force_update_version = optional(bool)
353+
instance_types = optional(list(string))
354+
labels = optional(map(string))
355+
node_repair_config = optional(object({
356+
enabled = optional(bool)
357+
max_parallel_nodes_repaired_count = optional(number)
358+
max_parallel_nodes_repaired_percentage = optional(number)
359+
max_unhealthy_node_threshold_count = optional(number)
360+
max_unhealthy_node_threshold_percentage = optional(number)
361+
node_repair_config_overrides = optional(list(object({
362+
min_repair_wait_time_mins = number
363+
node_monitoring_condition = string
364+
node_unhealthy_reason = string
365+
repair_action = string
366+
})))
367+
}))
368+
remote_access = optional(object({
369+
ec2_ssh_key = optional(string)
370+
source_security_group_ids = optional(list(string))
371+
}))
372+
taints = optional(map(object({
373+
key = string
374+
value = optional(string)
375+
effect = string
376+
})))
377+
update_config = optional(object({
378+
max_unavailable = optional(number)
379+
max_unavailable_percentage = optional(number)
380+
update_strategy = optional(string)
381+
}))
382+
timeouts = optional(object({
383+
create = optional(string)
384+
update = optional(string)
385+
delete = optional(string)
386+
}))
387+
# User data
388+
enable_bootstrap_user_data = optional(bool)
389+
pre_bootstrap_user_data = optional(string)
390+
post_bootstrap_user_data = optional(string)
391+
bootstrap_extra_args = optional(string)
392+
user_data_template_path = optional(string)
393+
cloudinit_pre_nodeadm = optional(list(object({
394+
content = string
395+
content_type = optional(string)
396+
filename = optional(string)
397+
merge_type = optional(string)
398+
})))
399+
cloudinit_post_nodeadm = optional(list(object({
400+
content = string
401+
content_type = optional(string)
402+
filename = optional(string)
403+
merge_type = optional(string)
404+
})))
405+
# Launch Template
406+
create_launch_template = optional(bool)
407+
use_custom_launch_template = optional(bool)
408+
launch_template_id = optional(string)
409+
launch_template_name = optional(string) # Will fall back to map key
410+
launch_template_use_name_prefix = optional(bool)
411+
launch_template_version = optional(string)
412+
launch_template_default_version = optional(string)
413+
update_launch_template_default_version = optional(bool)
414+
launch_template_description = optional(string)
415+
launch_template_tags = optional(map(string))
416+
tag_specifications = optional(list(string))
417+
ebs_optimized = optional(bool)
418+
key_name = optional(string)
419+
disable_api_termination = optional(bool)
420+
kernel_id = optional(string)
421+
ram_disk_id = optional(string)
422+
block_device_mappings = optional(map(object({
423+
device_name = optional(string)
424+
ebs = optional(object({
425+
delete_on_termination = optional(bool)
426+
encrypted = optional(bool)
427+
iops = optional(number)
428+
kms_key_id = optional(string)
429+
snapshot_id = optional(string)
430+
throughput = optional(number)
431+
volume_initialization_rate = optional(number)
432+
volume_size = optional(number)
433+
volume_type = optional(string)
434+
}))
435+
no_device = optional(string)
436+
virtual_name = optional(string)
437+
})))
438+
capacity_reservation_specification = optional(object({
439+
capacity_reservation_preference = optional(string)
440+
capacity_reservation_target = optional(object({
441+
capacity_reservation_id = optional(string)
442+
capacity_reservation_resource_group_arn = optional(string)
443+
}))
444+
}))
445+
cpu_options = optional(object({
446+
amd_sev_snp = optional(string)
447+
core_count = optional(number)
448+
threads_per_core = optional(number)
449+
}))
450+
credit_specification = optional(object({
451+
cpu_credits = optional(string)
452+
}))
453+
enclave_options = optional(object({
454+
enabled = optional(bool)
455+
}))
456+
instance_market_options = optional(object({
457+
market_type = optional(string)
458+
spot_options = optional(object({
459+
block_duration_minutes = optional(number)
460+
instance_interruption_behavior = optional(string)
461+
max_price = optional(string)
462+
spot_instance_type = optional(string)
463+
valid_until = optional(string)
464+
}))
465+
}))
466+
license_specifications = optional(list(object({
467+
license_configuration_arn = string
468+
})))
469+
metadata_options = optional(object({
470+
http_endpoint = optional(string)
471+
http_protocol_ipv6 = optional(string)
472+
http_put_response_hop_limit = optional(number)
473+
http_tokens = optional(string)
474+
instance_metadata_tags = optional(string)
475+
}))
476+
enable_monitoring = optional(bool)
477+
enable_efa_support = optional(bool)
478+
enable_efa_only = optional(bool)
479+
efa_indices = optional(list(string))
480+
create_placement_group = optional(bool)
481+
placement = optional(object({
482+
affinity = optional(string)
483+
availability_zone = optional(string)
484+
group_name = optional(string)
485+
host_id = optional(string)
486+
host_resource_group_arn = optional(string)
487+
partition_number = optional(number)
488+
spread_domain = optional(string)
489+
tenancy = optional(string)
490+
}))
491+
network_interfaces = optional(list(object({
492+
associate_carrier_ip_address = optional(bool)
493+
associate_public_ip_address = optional(bool)
494+
connection_tracking_specification = optional(object({
495+
tcp_established_timeout = optional(number)
496+
udp_stream_timeout = optional(number)
497+
udp_timeout = optional(number)
498+
}))
499+
delete_on_termination = optional(bool)
500+
description = optional(string)
501+
device_index = optional(number)
502+
ena_srd_specification = optional(object({
503+
ena_srd_enabled = optional(bool)
504+
ena_srd_udp_specification = optional(object({
505+
ena_srd_udp_enabled = optional(bool)
506+
}))
507+
}))
508+
interface_type = optional(string)
509+
ipv4_address_count = optional(number)
510+
ipv4_addresses = optional(list(string))
511+
ipv4_prefix_count = optional(number)
512+
ipv4_prefixes = optional(list(string))
513+
ipv6_address_count = optional(number)
514+
ipv6_addresses = optional(list(string))
515+
ipv6_prefix_count = optional(number)
516+
ipv6_prefixes = optional(list(string))
517+
network_card_index = optional(number)
518+
network_interface_id = optional(string)
519+
primary_ipv6 = optional(bool)
520+
private_ip_address = optional(string)
521+
security_groups = optional(list(string), [])
522+
subnet_id = optional(string)
523+
})))
524+
maintenance_options = optional(object({
525+
auto_recovery = optional(string)
526+
}))
527+
private_dns_name_options = optional(object({
528+
enable_resource_name_dns_aaaa_record = optional(bool)
529+
enable_resource_name_dns_a_record = optional(bool)
530+
hostname_type = optional(string)
531+
}))
532+
# IAM role
533+
create_iam_role = optional(bool)
534+
iam_role_arn = optional(string)
535+
iam_role_name = optional(string)
536+
iam_role_use_name_prefix = optional(bool)
537+
iam_role_path = optional(string)
538+
iam_role_description = optional(string)
539+
iam_role_permissions_boundary = optional(string)
540+
iam_role_tags = optional(map(string))
541+
iam_role_attach_cni_policy = optional(bool)
542+
iam_role_additional_policies = optional(map(string))
543+
create_iam_role_policy = optional(bool)
544+
iam_role_policy_statements = optional(list(object({
545+
sid = optional(string)
546+
actions = optional(list(string))
547+
not_actions = optional(list(string))
548+
effect = optional(string)
549+
resources = optional(list(string))
550+
not_resources = optional(list(string))
551+
principals = optional(list(object({
552+
type = string
553+
identifiers = list(string)
554+
})))
555+
not_principals = optional(list(object({
556+
type = string
557+
identifiers = list(string)
558+
})))
559+
condition = optional(list(object({
560+
test = string
561+
values = list(string)
562+
variable = string
563+
})))
564+
})))
565+
# Security group
566+
vpc_security_group_ids = optional(list(string), [])
567+
attach_cluster_primary_security_group = optional(bool, false)
568+
cluster_primary_security_group_id = optional(string)
569+
create_security_group = optional(bool)
570+
security_group_name = optional(string)
571+
security_group_use_name_prefix = optional(bool)
572+
security_group_description = optional(string)
573+
security_group_ingress_rules = optional(map(object({
574+
name = optional(string)
575+
cidr_ipv4 = optional(string)
576+
cidr_ipv6 = optional(string)
577+
description = optional(string)
578+
from_port = optional(string)
579+
ip_protocol = optional(string)
580+
prefix_list_id = optional(string)
581+
referenced_security_group_id = optional(string)
582+
self = optional(bool)
583+
tags = optional(map(string))
584+
to_port = optional(string)
585+
})))
586+
security_group_egress_rules = optional(map(object({
587+
name = optional(string)
588+
cidr_ipv4 = optional(string)
589+
cidr_ipv6 = optional(string)
590+
description = optional(string)
591+
from_port = optional(string)
592+
ip_protocol = optional(string)
593+
prefix_list_id = optional(string)
594+
referenced_security_group_id = optional(string)
595+
self = optional(bool)
596+
tags = optional(map(string))
597+
to_port = optional(string)
598+
})), {})
599+
security_group_tags = optional(map(string))
600+
601+
tags = optional(map(string))
602+
}))
603+
default = null
604+
}
605+
333606
variable "eks_cluster_version" {
334607
type = string
335608
description = "The Kubernetes version to run on the cluster. If not specified, the latest available version at resource creation is used and no upgrades will occur except those automatically triggered by EKS."

0 commit comments

Comments
 (0)