Skip to content

feat!: refactor modules - #732

Draft
amandakarina wants to merge 46 commits into
GoogleCloudPlatform:mainfrom
amandakarina:feat/refactor-modules
Draft

feat!: refactor modules#732
amandakarina wants to merge 46 commits into
GoogleCloudPlatform:mainfrom
amandakarina:feat/refactor-modules

Conversation

@amandakarina

Copy link
Copy Markdown
Collaborator

Summary

This PR refactors the repository layout by moving all Terraform modules into a top-level modules/ directory and removing legacy step folders (0-bootstrap, 1-multitenant, 2-multitenant, 3-fleetscope, 4-appfactory, 5-appinfra). It also introduces new reusable infrastructure modules (nat, private_workerpool, standalone-harness), renames existing modules for clarity, and updates the integration test framework.

Key Changes

1. Repository Restructuring & Module Consolidation

  • Removed Step Folders: Removed legacy top-level step directories (0-bootstrap, 1-multitenant, 2-multitenant, 3-fleetscope, 4-appfactory, 5-appinfra).
  • Renamed Modules for Clarity:
    • app-group-baselinesecure-cicd-pipeline
    • cicd-pipelinedeployment-pipeline

2. New Terraform Modules

  • modules/nat: Added a modular Cloud NAT configuration for VPC networks.
  • modules/private_workerpool: Created a module to handle Cloud Build Private Worker Pools, including network peering, subnet creation, and NAT configuration.
  • modules/standalone-harness: Added a standalone harness module for testing environments.

3. Module & Dependency Enhancements

  • Explicit Dependency Management: Introduced a module_dependencies variable across modules to manage execution dependencies cleanly without triggering Terraform provider depends_on limitations.
  • VPC-SC & Service Perimeter Adjustments: Refactored service perimeters and IAM permissions within secure-cicd-pipeline.

4. Integration Testing & Test Setup

  • Multi-Cluster Discovery Integration Test: Added multi_cluster_discovery_test.go under test/integration/multi_cluster_discovery/.
  • GitLab Test Harness Refactoring: Updated test/setup/harness/gitlab/ to include dedicated VM setup, NAT, and network modules.
  • Test Infrastructure: Added hub_network module under test/setup/modules/.

Dependecies

  • Flattened Module Tree: Moved all Terraform modules previously nested within step directories (e.g., 5-appinfra/modules/..., 3-fleetscope/modules/...) into the root modules/ folder for better modularity and reusability. Dependes on feat!: moves modules to root #705

Breaking Changes

  • Module Paths Updated: Module sources have shifted from <step>/modules/<module> or <step>/<module> to modules/<module>. Upstream callers will need to update their source
    parameters to reference the new paths under modules/.

Testing Strategy

  • Added and ran integration tests for multi_cluster_discovery.
  • Verified GitLab test harness provisioning and VM creation.
  • Validated Terraform syntax and formatting across all refactored modules.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request reorganizes the repository by removing legacy files, introducing a new standalone multi-cluster discovery example, and adding a reusable cluster network module. It also updates helper deployment utilities and refactors the binary authorization build image module. The review feedback highlights several key improvement opportunities, including adding a configurable Cloud NAT option to the cluster network module to support multi-region internet egress, ensuring the standalone example explicitly enables Cloud NAT, and refining file operations in the deployer helper to guarantee idempotency and correct symbolic link handling.

Comment thread modules/cluster_network/variables.tf Outdated
Comment on lines +31 to +35

variable "shared_vpc_host" {
description = "Makes this project a Shared VPC host if 'true' (default 'false')"
type = bool
default = false

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

To support internet egress for private GKE clusters in non-shared VPC environments (such as the standalone cluster-multicluster-discovery example), we should introduce a create_cloud_nat variable to allow explicit control over Cloud NAT creation, rather than strictly tying it to shared_vpc_host.

variable "shared_vpc_host" {
  description = "Makes this project a Shared VPC host if 'true' (default 'false')"
  type        = bool
  default     = false
}

variable "create_cloud_nat" {
  description = "Create a Cloud NAT gateway for internet egress if 'true' (default 'false')"
  type        = bool
  default     = false
}

Comment on lines +43 to +64
resource "google_compute_router" "nat_router" {
for_each = var.shared_vpc_host ? { "create" : true } : {}
name = "nat-router-${var.region}"
region = var.region
network = module.cluster_vpc.network_self_link
project = module.cluster_vpc.project_id
}

resource "google_compute_router_nat" "cloud_nat" {
for_each = google_compute_router.nat_router
name = "cloud-nat"
router = each.value.name
region = each.value.region
project = module.cluster_vpc.project_id
nat_ip_allocate_option = "AUTO_ONLY"
source_subnetwork_ip_ranges_to_nat = "ALL_SUBNETWORKS_ALL_IP_RANGES"

log_config {
enable = true
filter = "ERRORS_ONLY"
}
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The current implementation only creates a single Cloud NAT gateway in the region specified by var.region. However, the module supports creating subnets across multiple regions. In a multi-region deployment (such as the cluster-multicluster-discovery example), subnets in other regions will not have internet egress, causing GKE private nodes in those regions to fail to pull external images or connect to external APIs (like Gitlab).

We can dynamically create a Cloud NAT gateway in each unique region where a subnet is defined by extracting the regions from var.subnets and using for_each over them.

locals {
  nat_regions = (var.shared_vpc_host || var.create_cloud_nat) ? distinct([for s in var.subnets : s.subnet_region]) : []
}

resource "google_compute_router" "nat_router" {
  for_each = toset(local.nat_regions)
  name     = "nat-router-${each.value}"
  region   = each.value
  network  = module.cluster_vpc.network_self_link
  project  = module.cluster_vpc.project_id
}

resource "google_compute_router_nat" "cloud_nat" {
  for_each                           = google_compute_router.nat_router
  name                               = "cloud-nat-${each.value}"
  router                             = each.value.name
  region                             = each.value.region
  project                            = module.cluster_vpc.project_id
  nat_ip_allocate_option             = "AUTO_ONLY"
  source_subnetwork_ip_ranges_to_nat = "ALL_SUBNETWORKS_ALL_IP_RANGES"

  log_config {
    enable = true
    filter = "ERRORS_ONLY"
  }
}

Comment on lines +17 to +23
module "cluster_network" {
source = "../../modules/cluster_network"

vpc_name = "vpc-eab-cluster"
project_id = var.project_id
region = var.regions[0]
shared_vpc_host = false

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

Since shared_vpc_host is set to false for this standalone example, we must explicitly set create_cloud_nat = true to ensure that Cloud NAT gateways are created in all cluster regions. This is required so that the private GKE Autopilot nodes can access external resources (such as pulling Kueue images from registry.k8s.io and syncing configurations from gitlab.com).

module "cluster_network" {
  source = "../../modules/cluster_network"

  vpc_name         = "vpc-eab-cluster"
  project_id       = var.project_id
  region           = var.regions[0]
  shared_vpc_host  = false
  create_cloud_nat = true

Comment on lines +42 to +50
// DeleteFile deletes a single file from the src path to the dest path
func DeleteFile(src string) error {
_, err := os.Stat(src)
if err != nil {
return err
}
err = os.RemoveAll(src)
return err
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The DeleteFile function is not idempotent because it calls os.Stat(src) first, which will return an error if the file or directory does not exist. Additionally, the comment contains a copy-paste typo mentioning a dest path that does not exist in the function signature. Since os.RemoveAll already handles non-existent paths gracefully and returns nil, we can simplify the function to make it idempotent and correct the comment.

// DeleteFile deletes a file or directory at the given path.
func DeleteFile(src string) error {
	return os.RemoveAll(src)
}

return err
}
} else {
} else if !isSymlinkToDir(filepath.Join(src, f.Name())) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The current implementation of CopyDirectory only skips symbolic links that point to directories, while symbolic links pointing to files are still copied (and dereferenced into regular files). This violates the stated behavior in the comment ('It will skip symbolic links'). We can simplify this check and correctly skip all symbolic links by checking the file type directly using f.Type() & os.ModeSymlink == 0.

Suggested change
} else if !isSymlinkToDir(filepath.Join(src, f.Name())) {
} else if f.Type()&os.ModeSymlink == 0 {

@amandakarina
amandakarina force-pushed the feat/refactor-modules branch 2 times, most recently from bfe8c99 to 287d33a Compare August 7, 2026 19:31
@amandakarina
amandakarina force-pushed the feat/refactor-modules branch 4 times, most recently from 7d9262a to b967b1e Compare August 10, 2026 13:19
@amandakarina
amandakarina force-pushed the feat/refactor-modules branch from b967b1e to e641120 Compare August 10, 2026 14:06
@amandakarina
amandakarina force-pushed the feat/refactor-modules branch from 1dcbf0f to c3d98ac Compare August 10, 2026 17:39
@amandakarina
amandakarina force-pushed the feat/refactor-modules branch from 6dbc369 to 0109d8c Compare August 13, 2026 19:23
@amandakarina
amandakarina force-pushed the feat/refactor-modules branch 2 times, most recently from 035d4ea to 69809aa Compare August 14, 2026 12:53
@amandakarina
amandakarina force-pushed the feat/refactor-modules branch from 69809aa to 156cafa Compare August 14, 2026 13:00
Comment thread build/int.cloudbuild.yaml Outdated
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant