Subchapter 27.2
references/clustering/terraform/clustering-algorithm.mdMarkdown8 KBView on GitHub
Groups resources into named clusters using priority-ordered rules.
All resources with fields:
address, type, classification (PRIMARY/SECONDARY)secondary_role (if SECONDARY)typed_edges[], depth, serves[]IF google_compute_network resource exists:
google_compute_network + ALL network_path secondaries (subnetworks, firewalls, routers)networking_vpc_{gcp_region}_001 (e.g., networking_vpc_us-central1_001)Output: 1 cluster (or 0 if no networks found)
Mark these resources as clustered; remove from unassigned pool.
CRITICAL: Create ONE cluster per resource type, NOT one cluster per resource.
Process:
Identify all resource types with 2+ PRIMARY resources
google_pubsub_topic, 3× google_storage_bucket, 2× google_sql_database_instanceFor EACH resource type with 2+ primaries: Create ONE cluster containing ALL of them
Cluster ID format: {service_category}_{service_type}_{gcp_region}_{sequence:001}
messaging_pubsubtopic_us-central1_001 (contains ALL 4 pubsub topics)storage_bucket_us-central1_001 (contains ALL 3 storage buckets)database_sql_us-central1_001 (contains ALL 2 SQL instances)Primary resources in cluster: List ALL matching resources
messaging_pubsubtopic_us-central1_001:
google_pubsub_topic.order_eventsgoogle_pubsub_topic.inventory_eventsgoogle_pubsub_topic.user_eventsgoogle_pubsub_topic.dead_letterSecondary resources: Collect ALL secondaries that serve ANY of the grouped primaries
Correct Examples (ONE cluster per type):
google_pubsub_topic → 1 cluster: messaging_pubsubtopic_us-central1_001google_storage_bucket → 1 cluster: storage_bucket_us-central1_001google_sql_database_instance → 1 cluster: database_sql_us-central1_001google_container_cluster → 1 cluster: compute_gke_us-central1_001 (NOT k8s_001, k8s_002, k8s_003)INCORRECT Examples (DO NOT DO THIS):
google_pubsub_topic → 4 clusters (compute_pubsubtopic_001, compute_pubsubtopic_002, etc.)google_storage_bucket → 3 clusters (compute_storagebucket_001, compute_storagebucket_002, etc.)google_container_cluster → 3 clusters (k8s_001, k8s_002, k8s_003)Output: ONE cluster per resource type (not per resource)
Reasoning: Identical workloads of the same GCP service type migrate together, share operational characteristics, and are managed as a unit.
Mark all resources of this type as clustered; remove from unassigned pool.
FOR EACH remaining PRIMARY resource (unassigned):
serves[] array{service_type}_{gcp_region}_{sequence} (e.g., cloudrun_us-central1_001)Output: N clusters (one per remaining PRIMARY)
Mark all included resources as clustered.
IF two clusters have bidirectional data_dependency edges between their PRIMARY resources (A→B AND B→A):
Action: Combine into one cluster; update ID to reflect both (e.g., web-api_us-central1_001)
Reasoning: Bidirectional data dependencies indicate a tightly coupled deployment unit that must migrate together.
Do NOT merge when edges are unidirectional (A→B only). Unidirectional dependencies are captured in dependencies[] instead.
IF resource is google_project_service:
google_project_service.cloud_run attaches to Cloud Run cluster)Reasoning: API enablement is prerequisite, not a deployable unit.
Apply consistent cluster naming:
{service_category}_{service_type}_{gcp_region}_{sequence}compute, database, storage, networking, messaging, monitoring, analytics, securitycloudrun, sql, bucket, vpc)us-central1)001, 002)Examples:
compute_cloudrun_us-central1_001database_sql_us-west1_001storage_bucket_multi-region_001networking_vpc_us-central1_001 (rule 1 network cluster)Reasoning: Names reflect deployment intent; deterministic for reproducibility.
After all clusters are formed, populate these fields for each cluster:
Identify which VPC/network the cluster’s resources belong to. Trace network_path edges from resources in this cluster to find the google_compute_network they reference. Store the network cluster ID (e.g., networking_vpc_us-central1_001). Set to null if resources have no network association.
Default: true for all clusters. Set to false only if the cluster contains resources that can be independently migrated without breaking dependencies (rare — most clusters are atomic).
Derive from Primary→Primary edges that cross cluster boundaries. If cluster A contains a resource with a data_dependency edge to a resource in cluster B, then cluster A depends on cluster B. Store as array of cluster IDs.
Build a global ordering of clusters by depth level:
"creation_order": [
{ "depth": 0, "clusters": ["networking_vpc_us-central1_001"] },
{ "depth": 1, "clusters": ["security_iam_us-central1_001"] },
{ "depth": 2, "clusters": ["database_sql_us-central1_001", "storage_gcs_us-central1_001"] },
{ "depth": 3, "clusters": ["compute_cloudrun_us-central1_001"] }
]Cluster depth = minimum depth across all primary resources in the cluster. Clusters at the same depth can be migrated in parallel.
Each cluster includes:
{
"cluster_id": "compute_cloudrun_us-central1_001",
"gcp_region": "us-central1",
"primary_resources": ["google_cloud_run_service.app"],
"secondary_resources": ["google_service_account.app_runner"],
"network": "networking_vpc_us-central1_001",
"creation_order_depth": 2,
"must_migrate_together": true,
"dependencies": ["database_sql_us-central1_001"],
"edges": [
{
"from": "google_cloud_run_service.app",
"to": "google_sql_database_instance.db",
"relationship_type": "data_dependency",
"evidence": {
"field_path": "template.spec.containers[0].env[].value",
"reference": "DATABASE_URL"
}
}
]
}Given the same classified resource inputs, the clustering algorithm produces the same cluster structure every run:
Note: Resource classification (see classification-rules.md) may use LLM inference as a fallback for resource types not in the hardcoded tables. If LLM-classified resources enter the pipeline, overall reproducibility depends on the LLM producing consistent classifications.