Skip to content

feat(terraform): add terraform provider code generator - #268

Open
bhoomikarsnewrelic wants to merge 7 commits into
mainfrom
feat/terraform-generator-v2
Open

feat(terraform): add terraform provider code generator#268
bhoomikarsnewrelic wants to merge 7 commits into
mainfrom
feat/terraform-generator-v2

Conversation

@bhoomikarsnewrelic

Copy link
Copy Markdown
Collaborator

Adds a new terraform generator to tutone that produces the Go files needed for each terraform-provider-newrelic resource from a NerdGraph GraphQL schema, reducing new resource work from 1–3 weeks to same-day.

What's generated per resource

  • resource_newrelic_<name>.go — Schema declaration + full CRUD wiring
  • structures_newrelic_<name>.go — expand/flatten scaffolds with TUTONE:MANUAL stubs
  • resource_newrelic_<name>_test.go — acceptance test scaffold (written once)
  • provider_registration.txt — single registration line for provider_newrelic.go

Key design decisions

  • Generator reads pkgConfig.Terraform config block; silently skips packages that have no terraform: block — existing typegen/nerdgraphclient runs are completely unaffected.
  • Input type names are resolved directly from schema.json (not string manipulation), guaranteeing 100% accuracy against the same schema that produced the go-client types.
  • ClientService defaults to TitleCase(last import path segment) with a WARN log when not explicit, so authors are prompted to verify.
  • SchemaFields sorted alphabetically for deterministic/idempotent output.
  • All four read patterns supported via config flag: direct | list_filter | list_search | entity_management

Config fields added to PackageConfig

  • TerraformConfig struct with 30+ fields covering all CRUD variants: BatchCreate, ReadAfterCreate, NoUpdateMutation, ImmutableFields, ConflictingFields, BatchDelete, RequiresOrgID, ReadRetry, etc.

Files changed

  • internal/config/config.go — TerraformConfig struct + embed in PackageConfig
  • pkg/lang/terraform.go — TerraformGenerator, TerraformField, type-mapping
  • pkg/lang/terraform_test.go — 14 unit tests (scalar/enum/nested/list mapping,
    DeriveClientService, DeriveMethodName, etc.)
  • generators/terraform/generator.go — full codegen.Generator implementation
  • generators/terraform/generator_test.go — 10 unit tests covering all read_type paths,
    field overrides, Execute file creation
  • generators/terraform/testdata/schema.json — fixture schema for unit tests
  • templates/terraform/*.tmpl — 4 templates (resource, structures, test, registration)
  • pkg/generate/generate.go — registers "terraform" generator
  • .gitignore — allow generators/terraform/testdata/schema.json

bhoomikars and others added 7 commits August 13, 2026 11:27
Adds a new `terraform` generator to tutone that produces the Go files
needed for each terraform-provider-newrelic resource from a NerdGraph
GraphQL schema, reducing new resource work from 1–3 weeks to same-day.

## What's generated per resource
- `resource_newrelic_<name>.go`       — Schema declaration + full CRUD wiring
- `structures_newrelic_<name>.go`     — expand/flatten scaffolds with TUTONE:MANUAL stubs
- `resource_newrelic_<name>_test.go`  — acceptance test scaffold (written once)
- `provider_registration.txt`         — single registration line for provider_newrelic.go

## Key design decisions
- Generator reads `pkgConfig.Terraform` config block; silently skips packages
  that have no `terraform:` block — existing typegen/nerdgraphclient runs
  are completely unaffected.
- Input type names are resolved directly from schema.json (not string
  manipulation), guaranteeing 100% accuracy against the same schema that
  produced the go-client types.
- `ClientService` defaults to TitleCase(last import path segment) with a
  WARN log when not explicit, so authors are prompted to verify.
- SchemaFields sorted alphabetically for deterministic/idempotent output.
- All four read patterns supported via config flag:
    direct | list_filter | list_search | entity_management

## Config fields added to PackageConfig
- `TerraformConfig` struct with 30+ fields covering all CRUD variants:
  BatchCreate, ReadAfterCreate, NoUpdateMutation, ImmutableFields,
  ConflictingFields, BatchDelete, RequiresOrgID, ReadRetry, etc.

## Files changed
- `internal/config/config.go`          — TerraformConfig struct + embed in PackageConfig
- `pkg/lang/terraform.go`              — TerraformGenerator, TerraformField, type-mapping
- `pkg/lang/terraform_test.go`         — 14 unit tests (scalar/enum/nested/list mapping,
                                          DeriveClientService, DeriveMethodName, etc.)
- `generators/terraform/generator.go`  — full codegen.Generator implementation
- `generators/terraform/generator_test.go` — 10 unit tests covering all read_type paths,
                                          field overrides, Execute file creation
- `generators/terraform/testdata/schema.json` — fixture schema for unit tests
- `templates/terraform/*.tmpl`         — 4 templates (resource, structures, test, registration)
- `pkg/generate/generate.go`           — registers "terraform" generator
- `.gitignore`                         — allow generators/terraform/testdata/schema.json

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
Two bugs fixed:

1. Templates not found when tutone runs from a consuming repo
   (e.g. terraform-provider-newrelic, newrelic-client-go) that has no
   local templates/ directory. The binary now embeds all templates via
   go:embed so it is fully self-contained. Local files still take
   precedence, enabling per-repo overrides during development.

2. os.Create() was called before template parsing. A parse failure
   (missing template, syntax error) left a 0-byte file on disk that
   subsequent git add/commit would pick up silently. Reordered so the
   destination file is only created after the template parses
   successfully.

Root cause of the 0-byte resource_newrelic_notification_destination.go
in terraform-provider-newrelic PR #3146 was bug #2 triggered by bug #1.

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
…e path

embed.FS in templates/tmpl.go is rooted at the templates/ directory, so
embedded paths are terraform/resource.go.tmpl — not
templates/terraform/resource.go.tmpl. ParseFS was failing with
'pattern matches no files' because the full path including the
templates/ prefix was passed unchanged.

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
…nerated code

1. resource.go.tmpl: import the go-client namespace package so generated
   code compiles. Qualify ReadFilterType and ReadEntityType with the
   package name (e.g. notifications.AiNotificationsDestinationFilter).

2. structures.go.tmpl: replace interface{} stubs with concrete go-client
   types so the resource.go function calls type-check at compile time.
   expand funcs return PackageName.InputType; flatten accepts
   *PackageName.ReturnType.

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
Two imports of the same path pkg/errors caused 'imported and not used'
compile errors. The bare errors alias was only used in entity_management
read type; all other read types used nrErrors. Unified to nrErrors only.

Also removed the {{ .ImportPath }} import added in the previous commit —
goimports strips it as unused since resource.go never references
packagename.TypeName directly (types flow from structures.go in the
same package).

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
…fix test signature

1. TerraformField.GoTypeName: new field populated for KindInputObject
   fields with the actual NerdGraph type name (e.g.
   MaintenanceWindowScopedReferenceInput). Previously the template used
   GoName (the field name, e.g. Scope) which does not exist in the
   go-client package.

2. structures.go.tmpl: nested expand functions now return
   PackageName.GoTypeName instead of PackageName.GoName.

3. resource_test.go.tmpl: testAccCheckDestroy now accepts
   *terraform.State (not interface{}) and imports the terraform package.
   Added //go:build integration build tag to exclude from unit builds.

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
…pass it

The template was always passing accountID to go-client methods when
requires_account_id=true, but some mutations (e.g. maintenanceWindowCreate)
have no accountId argument in NerdGraph — so the go-client method doesn't
accept it. This caused 'too many arguments' compile errors.

Added MutationHasAccountIDArg helper that checks the mutation's argument
list. Generator now sets CreateHasAccountIDArg / UpdateHasAccountIDArg /
DeleteHasAccountIDArg from the live schema. Template uses these flags
instead of RequiresAccountID for method call argument passing.

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
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.

2 participants