diff --git a/docs/runtime-contracts.md b/docs/runtime-contracts.md index 4a14d94..a94d779 100644 --- a/docs/runtime-contracts.md +++ b/docs/runtime-contracts.md @@ -250,6 +250,12 @@ a controlled reboot after the apply so cloud-init reruns filesystem preparation and `resize2fs`, then verify the mounted ext4 capacity. Never reduce the input after the disk exists. +The reviewed `n2d-standard-2` profile is available for workloads that need a +non-E2 capacity pool. N2D deployments must select `pd-standard` or `pd-ssd`; +the module rejects `hyperdisk-balanced` because N2D supports Persistent Disk +and Hyperdisk Throughput, not Hyperdisk Balanced. See Google's +[N2D disk compatibility](https://cloud.google.com/compute/docs/general-purpose-machines#n2d_series). + Every Compose `project_dir` must be a normalized descendant of the fixed `/mnt/disks/data` ownership boundary. Terraform rejects paths such as `/`, `/etc`, repeated separators, and dot-segment traversal during planning. The diff --git a/modules/gcp/main.tf b/modules/gcp/main.tf index 85467ef..db483be 100644 --- a/modules/gcp/main.tf +++ b/modules/gcp/main.tf @@ -986,11 +986,11 @@ resource "google_compute_instance" "cloud-compose" { } precondition { condition = ( - startswith(var.machine_type, "e2") ? + startswith(var.machine_type, "e2") || startswith(var.machine_type, "n2d") ? contains(["pd-ssd", "pd-standard"], var.disk_type) : true ) - error_message = "When using an 'e2' machine type, 'disk_type' must be 'pd-ssd' or 'pd-standard'." + error_message = "E2 and N2D machine types require disk_type pd-ssd or pd-standard." } precondition { condition = !var.rollout_enabled || trimspace(var.rollout_release_url) != "" diff --git a/modules/gcp/runtime_contracts.tftest.hcl b/modules/gcp/runtime_contracts.tftest.hcl index c3fcc97..38166be 100644 --- a/modules/gcp/runtime_contracts.tftest.hcl +++ b/modules/gcp/runtime_contracts.tftest.hcl @@ -127,6 +127,42 @@ run "sizes_application_data_independently" { } } +run "accepts_n2d_with_standard_persistent_disk" { + command = plan + + variables { + name = "gcp-contract" + project_id = "test-project" + docker_compose_repo = "https://github.com/libops/wp.git" + machine_type = "n2d-standard-2" + disk_type = "pd-standard" + } + + assert { + condition = ( + google_compute_instance.cloud-compose.machine_type == "n2d-standard-2" && + google_compute_disk.boot.type == "pd-standard" && + google_compute_disk.data.type == "pd-standard" && + google_compute_disk.docker-volumes.type == "pd-standard" + ) + error_message = "The GCP module must apply the reviewed N2D and Persistent Disk profile to the VM and all disks." + } +} + +run "rejects_n2d_with_hyperdisk_balanced" { + command = plan + + variables { + name = "gcp-contract" + project_id = "test-project" + docker_compose_repo = "https://github.com/libops/wp.git" + machine_type = "n2d-standard-2" + disk_type = "hyperdisk-balanced" + } + + expect_failures = [google_compute_instance.cloud-compose] +} + run "rejects_fractional_application_data_size" { command = plan diff --git a/modules/gcp/variables.tf b/modules/gcp/variables.tf index fd7522e..9e1b969 100644 --- a/modules/gcp/variables.tf +++ b/modules/gcp/variables.tf @@ -114,13 +114,14 @@ variable "disk_type" { variable "machine_type" { type = string default = "n4-standard-2" - description = "VM machine type (General-purpose series that support Hyperdisk Balanced" + description = "Reviewed general-purpose VM machine type." validation { condition = contains([ "e2-micro", "e2-small", "e2-medium", + "n2d-standard-2", "n4-standard-2", "n4-standard-4", "n4-standard-8", @@ -137,7 +138,7 @@ variable "machine_type" { "c4-standard-48", "c4-standard-96", ], var.machine_type) - error_message = "The 'machine_type' must be from a General-Purpose family that supports Hyperdisk Balanced (C4, or N4 series)" + error_message = "machine_type must be an explicitly reviewed general-purpose profile." } } diff --git a/providers/gcp/template_versions.tftest.hcl b/providers/gcp/template_versions.tftest.hcl index 87e93be..958be67 100644 --- a/providers/gcp/template_versions.tftest.hcl +++ b/providers/gcp/template_versions.tftest.hcl @@ -97,6 +97,32 @@ run "gcp_entrypoint_forwards_application_data_size" { } } +run "gcp_entrypoint_accepts_n2d_with_standard_persistent_disk" { + command = plan + + variables { + name = "template-versions" + template = "wp" + gcp = { + project_id = "test-project" + instance = { + machine_type = "n2d-standard-2" + } + disks = { + type = "pd-standard" + } + } + } + + assert { + condition = ( + local.gcp_instance.machine_type == "n2d-standard-2" && + local.gcp_disks.type == "pd-standard" + ) + error_message = "The GCP provider entrypoint must accept the reviewed N2D and Persistent Disk profile." + } +} + run "accepts_direct_cloud_run_proxy_depth" { command = plan diff --git a/providers/gcp/variables.tf b/providers/gcp/variables.tf index d120fe3..1bfadee 100644 --- a/providers/gcp/variables.tf +++ b/providers/gcp/variables.tf @@ -121,6 +121,7 @@ variable "gcp" { "e2-micro", "e2-small", "e2-medium", + "n2d-standard-2", "n4-standard-2", "n4-standard-4", "n4-standard-8", diff --git a/runtime_contracts.tftest.hcl b/runtime_contracts.tftest.hcl index 1cf30b2..1dfbf9d 100644 --- a/runtime_contracts.tftest.hcl +++ b/runtime_contracts.tftest.hcl @@ -99,6 +99,33 @@ run "public_entrypoint_forwards_application_data_size" { } } +run "public_entrypoint_accepts_n2d_with_standard_persistent_disk" { + command = plan + + variables { + name = "root-contract" + cloud_provider = "gcp" + template = "wp" + gcp = { + project_id = "test-project" + instance = { + machine_type = "n2d-standard-2" + } + disks = { + type = "pd-standard" + } + } + } + + assert { + condition = ( + local.gcp_instance.machine_type == "n2d-standard-2" && + local.gcp_disks.type == "pd-standard" + ) + error_message = "The compatibility GCP entrypoint must accept the reviewed N2D and Persistent Disk profile." + } +} + run "public_entrypoint_rejects_reserved_extra_environment" { command = plan diff --git a/tests/smoke/gcp/variables.tf b/tests/smoke/gcp/variables.tf index 0bea916..b046202 100644 --- a/tests/smoke/gcp/variables.tf +++ b/tests/smoke/gcp/variables.tf @@ -29,7 +29,7 @@ variable "gcp_zone" { variable "gcp_machine_type" { type = string - default = "e2-medium" + default = "n2d-standard-2" description = "Google Compute Engine machine type." } diff --git a/variables.tf b/variables.tf index 199b556..9e1c074 100644 --- a/variables.tf +++ b/variables.tf @@ -132,6 +132,7 @@ variable "gcp" { "e2-micro", "e2-small", "e2-medium", + "n2d-standard-2", "n4-standard-2", "n4-standard-4", "n4-standard-8",