name: test-terraform-fields description: Guides through validating, testing, generating golden files (HTTP logs and KRM objects), running tests against mockgcp or real GCP, and aligning mock behavior.
KCC Test Terraform Fields (Agentic-Friendly Guide)
This skill guides an automated agent or developer through testing, validating, and generating golden test assets (HTTP logs and KRM object configurations) when adding or modifying fields on Config Connector resources. It covers running tests against real GCP, recording the baseline behavior, running against MockGCP, and resolving mock discrepancies.
1. Structure of Resource Fixture Tests
KCC uses a golden file testing strategy for end-to-end (E2E) validation. The tests are defined under pkg/test/resourcefixture/testdata/basic/<service>/<version>/<kind>/<testname>/.
A complete test fixture directory contains:
create.yaml: The primary KRM resource definition (what gets created first). Ensure the resource has unique labels/names.dependencies.yaml(optional): Supporting resources (e.g. IAM policies, networks, service accounts) that the primary resource depends on.update.yaml(strongly recommended): The KRM resource definition with updates applied after initial creation. While structurally optional, it is highly recommended (and mandatory for new mutable fields) to include anupdate.yamlto ensure the controller can successfully reconcile in-place updates._http.log: Golden HTTP/gRPC request/response traffic log generated during E2E reconciliation._generated_object_[testname].golden.yaml: Golden file representing the final KRM object status/spec in the Kube API server._exported.yaml(optional): Golden exported KRM representation.
2. Setting Up Test Cases
Create Directory: Create the directory
pkg/test/resourcefixture/testdata/basic/<service>/<version>/<kind>/<testname>/.Define KRM files: Add
create.yaml, andupdate.yaml(highly recommended to verify update reconciliation), and optionallydependencies.yaml.Verify Yaml Validation: Ensure YAML files are valid Kubernetes manifests. Avoid any hardcoded project IDs or dynamic IDs (use placeholders if necessary, but KCC test runner resolves them).
3. Recording Ground Truth against Real GCP
To establish a baseline or update golden logs, you must run the tests against a real GCP project. This records actual HTTP/gRPC API interactions into the _http.log file.
A. Authentication and Credentials
- Application Default Credentials (ADC):
Ensure your local environment is authenticated with Google Cloud:
gcloud auth application-default login - IAM Privileges:
The active credentials must have sufficient privileges in the target GCP project. Depending on the resource type, permissions like
Project Owner,Storage Admin,DNS Administrator, or custom resource-specific roles are required. - Resource Quotas: Verify that your target project has sufficient quota remaining for the resources you intend to create (e.g., compute CPUs, IP addresses, database instances).
B. Environment Variables Configuration
Configure the following environment variables to control project routing, billing, and resource scopes:
| Environment Variable | Description | Default / Fallback |
|---|---|---|
GCP_PROJECT_ID |
The target GCP project to provision resources in. | Output of gcloud config get-value project |
TEST_BILLING_ACCOUNT_ID |
The billing account to associate with billing resources or projects created during E2E. | Dynamically queried from GCP_PROJECT_ID billing config |
TEST_FOLDER_ID |
The folder ID under which to run folder-level resource tests. | Parent folder ID of GCP_PROJECT_ID |
TEST_ORG_ID |
The organization ID under which organization-level tests run. | Ancestor organization of GCP_PROJECT_ID |
[!NOTE] KCC tests dynamically substitute namespace project references with the actual target GCP project specified by these variables during execution.
C. Running E2E Fixture Test Recordings
For standard E2E fixture tests under pkg/test/resourcefixture/testdata/basic/, use the hack/record-gcp script:
- Run the script passing either the test name suffix or the package path:
- Using test name suffix:
hack/record-gcp <test_name> - Using full test package path:
hack/record-gcp pkg/test/resourcefixture/testdata/basic/dns/v1beta1/<test_name>- Example: Using test name suffix:
hack/record-gcp fixtures/dnsrecordsetbasic - Example: Using full test package path:
hack/record-gcp pkg/test/resourcefixture/testdata/basic/dns/v1beta1/dnsrecordset
- Using test name suffix:
- The script executes the tests with
E2E_GCP_TARGET=real,WRITE_GOLDEN_OUTPUT=1, and records the traffic to_http.log. - If the script fails (e.g. due to permissions or an invalid default project ID like
foobar), DO NOT skip this step. Ask the user for a valid GCP project ID to test against, and then run:GCP_PROJECT_ID=<project-id> hack/record-gcp <test_name>
D. Running MockGCP Script Test Recordings
For MockGCP-specific script tests (located under mockgcp/mockgcptests/), use the python recording task:
- Run the task specifying the mock test path:
mockgcp/dev/tasks/record-gcp mocksql/testdata/instance/dr-replica - This runs the mock tests targeting real GCP to refresh golden script logs.
E. Committing E2E Baseline (MANDATORY BEFORE MOCK RUN)
To prevent _http.log and golden files from introducing undue/mixed changes between real GCP and MockGCP runs, you MUST commit the real GCP baseline after the real GCP run and before running against MockGCP:
- Stage and commit the generated
_http.logand_generated_object_*.golden.yamlfiles:git add pkg/test/resourcefixture/testdata/basic/<service>/<version>/<kind>/<testname>/ git commit -m "Establish clean GCP golden logs for <testname>"
4. Running against MockGCP and Checking Diffs
To verify the mock implementation matches real GCP behavior:
[!IMPORTANT] Prerequisite: You must have committed the real GCP baseline (from Section 3.E) before starting this section. Running against MockGCP without committing the real GCP baseline first can mix up different modifications in the
_http.logfiles and make diff analysis impossible.
- Run Mock Test:
- Run
hack/compare-mock <testname>.- Example:
hack/compare-mock tests-e2e-sql
- Example:
- The test will execute using the MockGCP control plane.
- Run
- Check for Differences:
- Check if the command fails or if
git statusshows modifications to the golden files. - Run
git diff pkg/test/resourcefixture/testdata/basic/<service>/<version>/<kind>/<testname>/.
- Check if the command fails or if
- Commit the Baseline Updates:
- If there are any differences (such as
selfLinkremoval or minor formatting alignment), stage and commit these updates:git add pkg/test/resourcefixture/testdata/basic/<service>/<version>/<kind>/<testname>/ git commit -m "Update golden logs for <testname> after mock comparison"
- If there are any differences (such as
5. Aligning MockGCP with Real GCP (Troubleshooting & Alignment)
If the mock test fails or produces a diff against the baseline golden logs, apply the following strategies:
A. Enum Mismatch (e.g., Short vs. Proto Enum Names)
- Problem: GCP REST API returns short enum values (e.g.,
"OS_2022"), but MockGCP protobuf definition expects full enum values (e.g.,"OS_VERSION_LTSC2022"). - Solution: Intercept the request and response bodies in the mock HTTP Mux (
mockgcp/mock<service>/service.go) to translate these names. - Example in
mockgcp/mockcontainer/service.go:// Intercept Request Body: OS_2022 -> OS_VERSION_LTSC2022 if r.Body != nil { bodyBytes, err := io.ReadAll(r.Body) if err == nil { bodyBytes = bytes.ReplaceAll(bodyBytes, []byte(`"OS_2022"`), []byte(`"OS_VERSION_LTSC2022"`)) r.Body = io.NopCloser(bytes.NewReader(bodyBytes)) r.ContentLength = int64(len(bodyBytes)) } }
B. Output-Only or Server-Generated Fields
- Problem: Fields generated by real GCP (e.g. project numbers, server-assigned IDs, URLs) differ in the mock response.
- Solution: Implement default value generation in the mock service CRUD handlers (
mockgcp/mock<service>/<resource>.go). Ensure values match patterns expected by KCC (e.g.,projects/<project-id>/locations/<location>/...).
C. Normalizing Volatile Fields (timestamps, UUIDs, IPs)
- Problem: Dynamic values change on every execution.
- Solution: Add normalization rules in
mockgcp/mock<service>/normalize.go. - CRITICAL: The
Previsitnormalizer runs globally. To prevent corrupting golden files of unrelated services, always scope the normalization rule to the specific service domain:func (s *MockService) Previsit(event mockgcpregistry.Event, replacements mockgcpregistry.NormalizingVisitor) { if !strings.Contains(event.URL(), "myservice.googleapis.com") { return } // Apply replacements here... // e.g. replacements.Replace(uuidRegex, "uuid-placeholder") }
D. Duplicate Normalization Conflict (Zeros Placeholder Conflict)
- Problem: When running legacy E2E tests, the test runner applies a global normalizer that replaces server-generated identifiers in JSON response bodies with a string of zeros (e.g.,
"000000000000000000000"). If the per-service normalizer (Previsit) tries to match or replace this path, it can cause conflict errors or map multiple distinct resources to the same zeros placeholder. - Solution:
- In the mock service's
normalize.go, guard yourReplaceStringValuecalls to ignore the placeholder:if val == "000000000000000000000" { return } - Extract resource IDs from the request URL path (e.g. matching
/changes/<id>) instead of response bodies, as URL paths are not mutated by the legacy normalizer.
- In the mock service's
6. Pre-Submit Checks
Before finishing the task or proposing a PR, the agent must run formatting, generation, static analysis, and full CI validation checks locally to ensure zero CI/CD failures:
- Prepare PR and Regenerate Code:
- Run
make ready-prandmake resource-docsto ensure all manifests, Go client types, documentation, and code formatting are up to date:make ready-pr make resource-docs
- Run
- Mandatory CI/CD Presubmit Verification (CRITICAL):
- To guarantee generated PRs pass GitHub Actions CI/CD checks cleanly, execute the resource docs generation and the primary validation scripts locally:
make resource-docs dev/ci/presubmits/validate-generated-files scripts/validate-prereqs.sh - (Note:
validate-generated-filesruns GitHub Actions workflow codegen, static config generation, CRDs, mappers, and resource docs.validate-prereqs.shvalidates formatting and generation).
- To guarantee generated PRs pass GitHub Actions CI/CD checks cleanly, execute the resource docs generation and the primary validation scripts locally:
- Go Vet:
go vet ./... - Verify Local Control Plane Webhooks:
- If envtest webhook startup fails with validation errors under new Kubernetes control plane versions, ensure
admissionReviewVersionsinpkg/webhook/manifests.goincludes both"v1"and"v1beta1".
- If envtest webhook startup fails with validation errors under new Kubernetes control plane versions, ensure
- Verify CRD Field Coverage Checks:
- Run the API checks tests to ensure all new fields are either tested in the fixture tests. WARNING: New fields, in the vast majority of cases, should not be added to the the exceptions list; this is verified by KCC testing as well. If a field needs to be added to the exceptions list you must provide a clear explanation as to why this is the case:
- For Beta resources:
WRITE_GOLDEN_OUTPUT=1 go test ./tests/apichecks/... -run TestCRDFieldPresenceInTests - For Alpha resources:
WRITE_GOLDEN_OUTPUT=1 go test ./tests/apichecks/... -run TestCRDFieldPresenceInTestsForAlpha
- For Beta resources:
- Run the API checks tests to ensure all new fields are either tested in the fixture tests. WARNING: New fields, in the vast majority of cases, should not be added to the the exceptions list; this is verified by KCC testing as well. If a field needs to be added to the exceptions list you must provide a clear explanation as to why this is the case:
- Verify Acronym Casing Compliance:
- KCC enforces strict naming rules for acronyms in field names (e.g. using
IPinstead ofIp,VPCinstead ofVpc). If the generated fields use non-standard casing (e.g. they were generated directly from Terraform's snake_case schema which mapsallow_cross_org_vpcs->allowCrossOrgVpcs), the API checks testTestCRDsAcronymswill fail. - Run this test locally before submitting:
go test ./tests/apichecks/... -run TestCRDsAcronyms - If the test fails, you MUST add the reported non-standard casing exceptions in alphabetical order to the exceptions file:
tests/apichecks/testdata/exceptions/acronyms.txt
- KCC enforces strict naming rules for acronyms in field names (e.g. using
- Run CI/CD Group Presubmit Tests Locally:
- Locate and run the presubmit script under
dev/ci/presubmits/tests-e2e-fixtures-<service_name>matching the resource's service name (e.g.,dev/ci/presubmits/tests-e2e-fixtures-container) to ensure everything reconciles cleanly:dev/ci/presubmits/tests-e2e-fixtures-<service_name>
- Locate and run the presubmit script under
- Commit All Updated Artifacts and Generated Changes:
- Verify if any generated files (such as
mapper.generated.go, GitHub Actions YAMLs, CRDs, Go clients, or exceptions) are modified usinggit statusorgit diff. Stage and commit them:git add -A git commit -m "chore: ensure pristine generated state and formatting to pass CI/CD presubmits"
- Verify if any generated files (such as
- CI/CD & Golden File Traps (Gotchas):
- Accidental Binary Profile Artifacts (
heap.prof): When running recorder or memory profile footprint tests (e.g.,TestProfileRecorderFootprint), binary profile outputs likeheap.profmay be written to the test directory (cmd/recorder/pprof/.../heap.prof). Always ensure these binary files are deleted and never committed to git. - Selective Presubmit Harness &
WRITE_GOLDEN_OUTPUT=1Trap: Running specialized presubmit subsets (e.g.,test-pause) withWRITE_GOLDEN_OUTPUT=1can inadvertently delete golden files belonging to skipped phases (e.g.,_exported.yaml). Ensure you only regenerate golden files using the appropriate comprehensive test scope, or inspect git diffs to revert unintended deletions. - Accidental Staging of Unrelated Logs: When E2E fixture tests run locally (via
hack/record-gcp,hack/compare-mock, or directgo test), other fixtures in the same compute/service package may produce local differences in their_http_old_controller.logor_http.difflogs. Never commit changes or.difffiles for unrelated fixtures. Inspectgit diffbefore staging and revert them. - Missing
_identities.yaml: Newly created E2E fixtures dynamically generate a_identities.yamlidentity registry file when run. Ensure this file is staged and committed along with your E2E fixture, or the CI presubmits will fail.
- Accidental Binary Profile Artifacts (