feat(metal): module for setting project custom data#1009
feat(metal): module for setting project custom data#1009brosenqvist wants to merge 1 commit intoequinix:mainfrom
Conversation
There was a problem hiding this comment.
Pull request overview
Adds a new Terraform (Framework) Metal resource for managing an Equinix Metal project’s customdata, along with docs, an example, and acceptance coverage, and wires it into the provider’s Metal service registration.
Changes:
- Introduces
equinix_metal_project_custom_dataresource implementation (CRUD where Delete clears project custom data). - Adds acceptance test coverage plus example configuration.
- Adds template + generated docs for the new resource and registers it in
MetalResources().
Reviewed changes
Copilot reviewed 8 out of 8 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
internal/resources/metal/project_custom_data/resource.go |
Implements create/read/update/delete (delete clears custom data with retries). |
internal/resources/metal/project_custom_data/resource_schema.go |
Defines schema for project_id and custom_data. |
internal/resources/metal/project_custom_data/models.go |
Maps API project customdata into Terraform state. |
internal/resources/metal/project_custom_data/resource_test.go |
Acceptance test for create/update/remove + destroy verification. |
internal/provider/services/metal.go |
Registers the new Metal resource with the framework provider. |
templates/resources/metal_project_custom_data.md.tmpl |
Documentation template for the resource. |
docs/resources/metal_project_custom_data.md |
Generated docs page for the resource. |
examples/resources/equinix_metal_project_custom_data/example_1.tf |
Example usage showing custom_data = jsonencode({...}). |
| "custom_data": schema.StringAttribute{ | ||
| Description: "Project custom data as a JSON object string", | ||
| Required: true, | ||
| }, |
There was a problem hiding this comment.
custom_data is a required string, but the resource persists a canonicalized JSON string from the API into state (see ResourceModel.parse). If a user supplies equivalent JSON with different whitespace/key ordering, Terraform will see perpetual diffs (config string != state string). Consider normalizing the planned value (e.g., via a custom string plan modifier that parses+re-marshals JSON) or modeling custom_data as a map/object type instead of a raw string to avoid endless updates.
| customData := map[string]interface{}{} | ||
| if err := json.Unmarshal([]byte(raw), &customData); err != nil { | ||
| return nil, fmt.Errorf("custom_data must be valid JSON object: %w", err) | ||
| } |
There was a problem hiding this comment.
parseCustomData unmarshals into a map, which accepts JSON null without error and yields a nil map. Passing a nil map through to the API will serialize as null, which contradicts the “JSON object” expectation and may lead to unexpected behavior. After unmarshalling, explicitly reject null (nil map) and/or verify the decoded value is a JSON object.
| } | |
| } | |
| if customData == nil { | |
| return nil, fmt.Errorf("custom_data must be a JSON object, not null") | |
| } |
| // Persist compact canonical JSON to keep state stable. | ||
| b, err := json.Marshal(project.GetCustomdata()) | ||
| if err != nil { | ||
| return diag.Diagnostics{diag.NewErrorDiagnostic("Failed to marshal project custom data", err.Error())} | ||
| } | ||
| m.CustomData = types.StringValue(string(b)) |
There was a problem hiding this comment.
ResourceModel.parse marshals project.GetCustomdata() directly; if the API returns null for custom data, this will persist the literal string "null" into state even though the schema/docs describe a JSON object. Consider coercing a nil/absent customdata value to {} before marshaling so state remains an object string and avoids surprising diffs.
| project, _, err := client.ProjectsApi.FindProjectById(context.Background(), rs.Primary.Attributes["project_id"]).Execute() | ||
| if err != nil { | ||
| continue | ||
| } |
There was a problem hiding this comment.
testAccMetalProjectCustomDataCheckDestroyed ignores all API errors (if err != nil { continue }), which can produce false positives (e.g., transient 5xx/rate-limit errors) where the test passes without verifying custom data was cleared. Prefer only ignoring expected "not found" cases (e.g., 404 after project destroy) and returning other errors so the acceptance test reliably validates behavior.
| Steps: []resource.TestStep{ | ||
| { | ||
| Config: testAccMetalProjectCustomDataConfig(rInt, `{"owner":"platform","env":"test"}`), | ||
| Check: resource.ComposeTestCheckFunc( | ||
| resource.TestCheckResourceAttr(resourceName, "custom_data", `{"env":"test","owner":"platform"}`), |
There was a problem hiding this comment.
The acceptance test covers create/update/delete, but it doesn’t assert there is no post-apply drift once custom_data is canonicalized (JSON ordering/whitespace). Consider adding a ConfigPlanChecks step that re-applies the same config and expects an empty plan, to catch perpetual-diff regressions.
module: equinix_metal_project_custom_data
description: allows the creation, modifications, deletion of a projects custom data
Creation
Modification
Deletion