Skip to content

Commit bf8d30c

Browse files
ericfitzclaude
andcommitted
fix(terraform): resolve OCI production deployment issues for managed nodes
Fix multiple issues discovered during OKE managed node pool deployment: - Add init container to extract Oracle wallet zip and fix sqlnet.ora path - Add Redis --requirepass command (official image doesn't use REDIS_PASSWORD env) - Add NSG rules for NodePort range (30000-32767) and kube-proxy health check (10256) - Add security list rules for LB-to-worker traffic on managed nodes - Fix LB NSG annotation to use correct OKE CCM format - Configure free-tier ADB settings to match existing database state - Add ignore_changes for freeform_tags on free-tier ADB (rejects updates) - Mark sensitive outputs in k8s module - Add OCI CLI profile for Terraform provider authentication Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 95f3821 commit bf8d30c

6 files changed

Lines changed: 141 additions & 21 deletions

File tree

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,3 +118,5 @@ scripts/oci-env.sh
118118
**/terraform.tfvars
119119
**/tfplan
120120
**/.terraform/
121+
# Lock files in modules (generated by terraform init during validation)
122+
terraform/modules/**/.terraform.lock.hcl

terraform/environments/oci-production/main.tf

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -35,20 +35,22 @@ terraform {
3535
# OCI Provider configuration
3636
# Uses IMDS or ~/.oci/config for authentication
3737
provider "oci" {
38-
region = var.region
38+
region = var.region
39+
config_file_profile = "tmi"
3940
# auth = "InstancePrincipal" # Uncomment for IMDS authentication
4041
}
4142

4243
# Kubernetes Provider - configured after OKE cluster creation
4344
# Uses OCI CLI for token authentication
45+
# Note: Run with GODEBUG=x509negativeserial=1 if Go 1.24+ rejects OKE certs
4446
provider "kubernetes" {
4547
host = module.kubernetes.cluster_endpoint
4648
cluster_ca_certificate = module.kubernetes.cluster_ca_certificate
4749

4850
exec {
4951
api_version = "client.authentication.k8s.io/v1beta1"
5052
command = "oci"
51-
args = ["ce", "cluster", "generate-token", "--cluster-id", module.kubernetes.cluster_id, "--region", var.region]
53+
args = ["ce", "cluster", "generate-token", "--cluster-id", module.kubernetes.cluster_id, "--region", var.region, "--profile", "tmi"]
5254
}
5355
}
5456

@@ -122,12 +124,11 @@ module "database" {
122124
database_nsg_ids = [module.network.database_nsg_id]
123125
object_storage_namespace = data.oci_objectstorage_namespace.ns.namespace
124126

125-
# Paid tier settings - enables private endpoint for ADB
126-
is_free_tier = false
127-
cpu_core_count = 1
128-
compute_count = 2
129-
data_storage_size_in_tbs = 1
130-
is_auto_scaling_enabled = true
127+
# Free tier - existing ADB is free-tier with public endpoint
128+
# Note: Cannot convert free-tier ADB to paid with private endpoint in-place
129+
is_free_tier = true
130+
compute_count = 1
131+
is_auto_scaling_enabled = false
131132
is_auto_scaling_for_storage_enabled = false
132133
prevent_destroy = var.prevent_database_destroy
133134

terraform/modules/database/oci/main.tf

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,8 @@ resource "oci_database_autonomous_database" "tmi" {
6060
# Note: Terraform doesn't allow variables in lifecycle blocks
6161
lifecycle {
6262
prevent_destroy = true
63+
# Free-tier ADB rejects most update operations; ignore drift on tags
64+
ignore_changes = [freeform_tags]
6365
}
6466

6567
freeform_tags = var.tags

terraform/modules/kubernetes/oci/k8s_resources.tf

Lines changed: 32 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,27 @@ resource "kubernetes_deployment_v1" "tmi_api" {
122122
service_account_name = kubernetes_service_account_v1.tmi_api.metadata[0].name
123123
automount_service_account_token = true
124124

125+
# Init container to extract Oracle wallet zip into a shared emptyDir
126+
init_container {
127+
name = "wallet-extract"
128+
image = "busybox:1.36"
129+
command = [
130+
"sh", "-c",
131+
"cp /wallet-zip/wallet.zip /tmp/wallet.zip && cd /wallet-extracted && unzip -o /tmp/wallet.zip && sed -i 's|DIRECTORY=\"?/network/admin\"|DIRECTORY=\"/wallet\"|' /wallet-extracted/sqlnet.ora"
132+
]
133+
134+
volume_mount {
135+
name = "wallet-zip"
136+
mount_path = "/wallet-zip"
137+
read_only = true
138+
}
139+
140+
volume_mount {
141+
name = "wallet-extracted"
142+
mount_path = "/wallet-extracted"
143+
}
144+
}
145+
125146
container {
126147
name = "tmi-api"
127148
image = var.tmi_image_url
@@ -145,7 +166,7 @@ resource "kubernetes_deployment_v1" "tmi_api" {
145166
}
146167

147168
volume_mount {
148-
name = "wallet"
169+
name = "wallet-extracted"
149170
mount_path = "/wallet"
150171
read_only = true
151172
}
@@ -190,12 +211,17 @@ resource "kubernetes_deployment_v1" "tmi_api" {
190211
}
191212

192213
volume {
193-
name = "wallet"
214+
name = "wallet-zip"
194215
secret {
195216
secret_name = kubernetes_secret_v1.wallet.metadata[0].name
196217
}
197218
}
198219

220+
volume {
221+
name = "wallet-extracted"
222+
empty_dir {}
223+
}
224+
199225
volume {
200226
name = "tmp"
201227
empty_dir {}
@@ -241,21 +267,14 @@ resource "kubernetes_deployment_v1" "redis" {
241267
name = "redis"
242268
image = var.redis_image_url
243269

270+
# Official Redis image requires --requirepass flag for authentication
271+
command = ["redis-server", "--requirepass", var.redis_password, "--port", "6379"]
272+
244273
port {
245274
container_port = 6379
246275
protocol = "TCP"
247276
}
248277

249-
env {
250-
name = "REDIS_PASSWORD"
251-
value = var.redis_password
252-
}
253-
254-
env {
255-
name = "REDIS_PORT"
256-
value = "6379"
257-
}
258-
259278
liveness_probe {
260279
tcp_socket {
261280
port = 6379
@@ -338,7 +357,7 @@ resource "kubernetes_service_v1" "tmi_api" {
338357
"service.beta.kubernetes.io/oci-load-balancer-shape-flex-min" = tostring(var.lb_min_bandwidth_mbps)
339358
"service.beta.kubernetes.io/oci-load-balancer-shape-flex-max" = tostring(var.lb_max_bandwidth_mbps)
340359
"service.beta.kubernetes.io/oci-load-balancer-security-list-management-mode" = "None"
341-
"oci-network-security-groups" = join(",", var.lb_nsg_ids)
360+
"oci.oraclecloud.com/oci-network-security-groups" = join(",", var.lb_nsg_ids)
342361
},
343362
# SSL annotations when certificate is provided
344363
var.ssl_certificate_pem != null ? {

terraform/modules/kubernetes/oci/outputs.tf

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,11 +48,13 @@ output "http_url" {
4848

4949
output "https_url" {
5050
description = "HTTPS URL for the application (if SSL configured)"
51+
sensitive = true
5152
value = var.ssl_certificate_pem != null && length(kubernetes_service_v1.tmi_api.status[0].load_balancer[0].ingress) > 0 ? "https://${kubernetes_service_v1.tmi_api.status[0].load_balancer[0].ingress[0].ip}" : null
5253
}
5354

5455
output "service_endpoint" {
5556
description = "Service endpoint URL (standard interface)"
57+
sensitive = true
5658
value = length(kubernetes_service_v1.tmi_api.status[0].load_balancer[0].ingress) > 0 ? (
5759
var.ssl_certificate_pem != null ?
5860
"https://${kubernetes_service_v1.tmi_api.status[0].load_balancer[0].ingress[0].ip}" :

terraform/modules/network/oci/main.tf

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,28 @@ resource "oci_core_security_list" "private" {
164164
}
165165
}
166166

167+
# Allow LB to NodePort range (required for managed node pools)
168+
ingress_security_rules {
169+
protocol = "6" # TCP
170+
source = var.public_subnet_cidr
171+
source_type = "CIDR_BLOCK"
172+
tcp_options {
173+
min = 30000
174+
max = 32767
175+
}
176+
}
177+
178+
# Allow LB health check to kube-proxy (required for managed node pools)
179+
ingress_security_rules {
180+
protocol = "6" # TCP
181+
source = var.public_subnet_cidr
182+
source_type = "CIDR_BLOCK"
183+
tcp_options {
184+
min = 10256
185+
max = 10256
186+
}
187+
}
188+
167189
# Allow egress to database subnet (Oracle DB ports)
168190
egress_security_rules {
169191
protocol = "6" # TCP
@@ -904,6 +926,78 @@ resource "oci_core_network_security_group_security_rule" "lb_egress_oke_pods" {
904926
}
905927
}
906928

929+
# Allow load balancer to reach OKE NodePort range (required for managed node pools)
930+
resource "oci_core_network_security_group_security_rule" "lb_egress_oke_nodeport" {
931+
network_security_group_id = oci_core_network_security_group.lb.id
932+
direction = "EGRESS"
933+
protocol = "6" # TCP
934+
935+
description = "Allow traffic to OKE NodePort range (managed nodes)"
936+
destination = oci_core_network_security_group.oke_pod.id
937+
destination_type = "NETWORK_SECURITY_GROUP"
938+
939+
tcp_options {
940+
destination_port_range {
941+
min = 30000
942+
max = 32767
943+
}
944+
}
945+
}
946+
947+
# Allow load balancer health check to kube-proxy (required for managed node pools)
948+
resource "oci_core_network_security_group_security_rule" "lb_egress_oke_healthcheck" {
949+
network_security_group_id = oci_core_network_security_group.lb.id
950+
direction = "EGRESS"
951+
protocol = "6" # TCP
952+
953+
description = "Allow health check to kube-proxy (managed nodes)"
954+
destination = oci_core_network_security_group.oke_pod.id
955+
destination_type = "NETWORK_SECURITY_GROUP"
956+
957+
tcp_options {
958+
destination_port_range {
959+
min = 10256
960+
max = 10256
961+
}
962+
}
963+
}
964+
965+
# Allow OKE workers to accept NodePort traffic from load balancer (managed nodes)
966+
resource "oci_core_network_security_group_security_rule" "oke_pod_ingress_lb_nodeport" {
967+
network_security_group_id = oci_core_network_security_group.oke_pod.id
968+
direction = "INGRESS"
969+
protocol = "6" # TCP
970+
971+
description = "Allow LB to NodePort range (managed nodes)"
972+
source = oci_core_network_security_group.lb.id
973+
source_type = "NETWORK_SECURITY_GROUP"
974+
975+
tcp_options {
976+
destination_port_range {
977+
min = 30000
978+
max = 32767
979+
}
980+
}
981+
}
982+
983+
# Allow OKE workers to accept health check from load balancer (managed nodes)
984+
resource "oci_core_network_security_group_security_rule" "oke_pod_ingress_lb_healthcheck" {
985+
network_security_group_id = oci_core_network_security_group.oke_pod.id
986+
direction = "INGRESS"
987+
protocol = "6" # TCP
988+
989+
description = "Allow LB health check to kube-proxy (managed nodes)"
990+
source = oci_core_network_security_group.lb.id
991+
source_type = "NETWORK_SECURITY_GROUP"
992+
993+
tcp_options {
994+
destination_port_range {
995+
min = 10256
996+
max = 10256
997+
}
998+
}
999+
}
1000+
9071001
# Allow OKE pods to reach database NSG
9081002
resource "oci_core_network_security_group_security_rule" "db_ingress_oke_pods" {
9091003
network_security_group_id = oci_core_network_security_group.database.id

0 commit comments

Comments
 (0)