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
14 changes: 14 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -361,6 +361,20 @@ This unified approach:
- ✅ Simplifies maintenance
- ✅ Enables easy configuration

### Platform Ownership

NodeInfra is the source of truth for platform behavior such as ingress, TLS termination, DNS integration, and provider compatibility.

External validator repositories should treat NodeInfra as the reusable platform layer and keep only validator-specific configuration locally, such as:

- `DNS_ZONE_NAME`
- `CHAIN_NAME`
- `INGRESS_DOMAIN`
- secrets and environment values
- deployment selection and operational wrappers

Platform logic should not be re-implemented in downstream Terraform roots. When ingress or TLS behavior changes, the fix should land in NodeInfra once and downstream consumers should inherit it by updating their NodeInfra integration rather than copying the logic.

## Deployment Tools

The `tools/` package provides reusable Python modules for infrastructure automation:
Expand Down
11 changes: 7 additions & 4 deletions examples/public-fullnode/deploy.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,16 @@ def build_terraform_vars(env_vars: dict) -> dict:
if "VPC_CIDR" in env_vars:
variables["vpc_cidr"] = env_vars["VPC_CIDR"]

enable_ingress = env_vars.get("INGRESS_ENABLED", "false").lower() in ("true", "1", "yes")
ingress_base_domain = env_vars.get("INGRESS_DOMAIN", "scratchpad.movementnetwork.xyz")

# DNS configuration
enable_dns = env_vars.get("ENABLE_DNS", "false").lower() in ("true", "1", "yes")
variables["enable_dns"] = enable_dns
if enable_dns:
if "DNS_ZONE_NAME" in env_vars:
variables["dns_zone_name"] = env_vars["DNS_ZONE_NAME"]
variables["dns_zone_name"] = env_vars.get("DNS_ZONE_NAME") or (
ingress_base_domain if enable_ingress else ""
)
if "FULLNODE_DNS_NAME" in env_vars:
variables["fullnode_dns_name"] = env_vars["FULLNODE_DNS_NAME"]
else:
Expand All @@ -60,11 +64,10 @@ def build_terraform_vars(env_vars: dict) -> dict:
]

# Ingress configuration
enable_ingress = env_vars.get("INGRESS_ENABLED", "false").lower() in ("true", "1", "yes")
variables["enable_ingress"] = enable_ingress
if enable_ingress:
variables["chain_name"] = env_vars.get("CHAIN_NAME", "testnet")
variables["ingress_domain"] = env_vars.get("INGRESS_DOMAIN", "scratchpad.movementnetwork.xyz")
variables["ingress_domain"] = ingress_base_domain

return variables

Expand Down
46 changes: 46 additions & 0 deletions examples/validator-vfn/.terraform.lock.hcl

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

5 changes: 4 additions & 1 deletion examples/validator-vfn/deploy.py
Original file line number Diff line number Diff line change
Expand Up @@ -437,10 +437,13 @@ def deploy(env_vars: dict, force_create: bool, validate: bool, terraform_dir: Pa
namespace = env_vars.get("NAMESPACE", "movement-l1")
validator_keys_secret = env_vars.get("VALIDATOR_KEYS_SECRET", "validator-identity")
vfn_keys_secret = env_vars.get("VFN_KEYS_SECRET", "vfn-identity")
validator_public = env_vars.get("VALIDATOR_PUBLIC", "false").lower() in ("true", "1", "yes")
validator_service_type = "LoadBalancer" if validator_public else "ClusterIP"

# Display deployment plan
info("Deployment Topology:")
info(f" Validator: {validator_name} (ClusterIP - private)")
validator_access = "LoadBalancer - public" if validator_public else "ClusterIP - private"
info(f" Validator: {validator_name} ({validator_access})")
if ingress_enabled:
info(f" Ingress: ENABLED (TLS via *.{ingress_domain})")
if deploy_vfn and deploy_fullnode:
Expand Down
24 changes: 16 additions & 8 deletions terraform-modules/movement-ingress/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -160,17 +160,25 @@ resource "kubectl_manifest" "wildcard_certificate" {
data "aws_region" "current" {}

# NOTE: NLB provisioning is asynchronous. After helm_release.nginx_ingress completes,
# AWS needs 2-5 minutes to provision the actual NLB. If the first terraform apply fails
# with "no matching ELB found", wait a few minutes and rerun. This is expected behavior.
#
# The data source lookup will fail fast if NLB doesn't exist yet, which is preferable
# to blocking with arbitrary sleep times that may still be insufficient.
# AWS needs 2-5 minutes to provision the actual NLB. We discover the load balancer
# via the ingress controller Service and the AWS service tag rather than assuming
# the requested semantic name becomes the final AWS LB name.
data "kubernetes_service_v1" "nginx_ingress" {
metadata {
name = "ingress-nginx-controller"
namespace = var.ingress_namespace
}

depends_on = [helm_release.nginx_ingress]
}

# Get the NLB by name (set via service annotation)
# Get the NLB from the stable service tag applied by the Kubernetes service controller.
data "aws_lb" "nginx_ingress" {
name = local.nlb_name
tags = {
"kubernetes.io/service-name" = "${var.ingress_namespace}/ingress-nginx-controller"
}

depends_on = [helm_release.nginx_ingress]
depends_on = [data.kubernetes_service_v1.nginx_ingress]
}

# Create DNS record for the wildcard domain pointing to the NLB
Expand Down
2 changes: 1 addition & 1 deletion terraform-modules/movement-ingress/outputs.tf
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ output "ingress_class_name" {

output "load_balancer_hostname" {
description = "NLB hostname for the NGINX Ingress Controller"
value = data.aws_lb.nginx_ingress.dns_name
value = data.kubernetes_service_v1.nginx_ingress.status[0].load_balancer[0].ingress[0].hostname
}

output "wildcard_dns_record" {
Expand Down
8 changes: 6 additions & 2 deletions terraform-modules/movement-ingress/versions.tf
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,17 @@ terraform {
source = "hashicorp/aws"
version = ">= 5.0"
}
kubernetes = {
source = "hashicorp/kubernetes"
version = "~> 2.35"
}
helm = {
source = "hashicorp/helm"
version = ">= 2.0"
version = "~> 2.17"
}
kubectl = {
source = "gavinbunney/kubectl"
version = ">= 1.14"
version = "~> 1.14"
}
}
}
Loading