Skip to content

Commit ca8c1b3

Browse files
DavidS-ovmactions-user
authored andcommitted
[ENG-2684] Fix Terraform provider aws-regions serialization breaking frontend (#3963)
## Summary - The Terraform provider serialized `aws-regions` as a comma-separated string, but the frontend Zod schema expects a JSON array, causing "Invalid source data" in the UI for Terraform-created sources. - Fixed Create, Update, and Read paths in the provider to use proper array serialization via `structpb.ListValue`. - Intentionally omitted legacy CSV fallback in Read — existing sources will self-heal on the next `terraform apply`. ## Linear Ticket - **Ticket**: [ENG-2684](https://linear.app/overmind/issue/ENG-2684/fix-terraform-provider-aws-regions-serialization-breaking-frontend) — Fix Terraform provider aws-regions serialization breaking frontend - **Project**: Terraform Module for AWS Source Setup ## Changes **`aws-source/module/provider/resource_aws_source.go`** (core fix): - **Create & Update**: Replaced `strings.Join(regions, ",")` with `toAnySlice(regions)` so `structpb.NewStruct` produces a `ListValue` instead of a `StringValue`. - **Read**: Replaced string-based parsing with `regionsFromStructValue()` which only reads from `ListValue`. No legacy CSV fallback — this forces Terraform to detect drift on existing sources with the old format. Returns an empty slice (not nil) when the value isn't a list, so `ListValueFrom` produces a non-null empty list — correct for a `Required` schema attribute. - **Helpers**: Added `toAnySlice` (converts `[]string` to `[]any`) and `regionsFromStructValue` (extracts regions from protobuf `ListValue`). Removed unused `splitNonEmpty` and `strings` import. **`aws-source/module/provider/.github/workflows/release.yml`**: Minor release pipeline improvement. **`deploy/.terraform.lock.hcl`**: Updated lock file with new overmind provider hash. **`aws-source/README.md`** and **`aws-source/module/terraform/README.md`**: Documented `api_key` provider-block attribute for authentication. All existing provider tests pass without modification. ## Deviations from Approved Plan The plan in ENG-2684 described four changes, all scoped to `resource_aws_source.go`. The implementation includes those four items plus: 1. **Empty slice instead of nil on parse failure** — not in the plan. `regionsFromStructValue` returns `[]string{}` instead of `nil` when the stored value isn't a `ListValue`. This prevents `ListValueFrom` from producing a null list for a `Required` attribute, which could break refresh in future Terraform framework versions. Drift-based self-healing is preserved since an empty list still differs from the configured regions. 2. **`release.yml` pipeline tweak** — not in the plan. Minor CI change to the provider release workflow (1 line). Low risk, bundled for convenience. 3. **`deploy/.terraform.lock.hcl` update** — not in the plan. Updates the lock file to include the new provider version hash. Required for `deploy/` to use the updated provider. 4. **Documentation updates to `aws-source/README.md` and `aws-source/module/terraform/README.md`** — not in the plan. Adds documentation for the `api_key` provider-block attribute. Docs-only, no behavioral change. No planned items were omitted or modified. The core fix (items 1–4 in the plan) matches the approved approach exactly. GitOrigin-RevId: 67c7387e75d8b85bc14095b51d90215ba042da1f
1 parent f711948 commit ca8c1b3

3 files changed

Lines changed: 31 additions & 15 deletions

File tree

aws-source/module/provider/.github/workflows/release.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ permissions:
1010

1111
jobs:
1212
release:
13-
runs-on: ubuntu-latest
13+
runs-on: depot-ubuntu-24.04-8
1414
steps:
1515
- name: Checkout
1616
uses: actions/checkout@v6

aws-source/module/provider/resource_aws_source.go

Lines changed: 22 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ package main
33
import (
44
"context"
55
"fmt"
6-
"strings"
76

87
"connectrpc.com/connect"
98
"github.com/google/uuid"
@@ -130,7 +129,7 @@ func (r *awsSourceResource) Create(ctx context.Context, req resource.CreateReque
130129
"aws-access-strategy": "external-id",
131130
"aws-external-id": externalID,
132131
"aws-target-role-arn": plan.AWSRoleARN.ValueString(),
133-
"aws-regions": strings.Join(regions, ","),
132+
"aws-regions": toAnySlice(regions),
134133
})
135134
if err != nil {
136135
resp.Diagnostics.AddError("Failed to build source config", err.Error())
@@ -216,8 +215,7 @@ func (r *awsSourceResource) Read(ctx context.Context, req resource.ReadRequest,
216215
state.AWSRoleARN = types.StringValue(v.GetStringValue())
217216
}
218217
if v, ok := fields["aws-regions"]; ok {
219-
regionStr := v.GetStringValue()
220-
regionVals := splitNonEmpty(regionStr, ",")
218+
regionVals := regionsFromStructValue(v)
221219
listVal, diags := types.ListValueFrom(ctx, types.StringType, regionVals)
222220
resp.Diagnostics.Append(diags...)
223221
state.AWSRegions = listVal
@@ -271,7 +269,7 @@ func (r *awsSourceResource) Update(ctx context.Context, req resource.UpdateReque
271269
"aws-access-strategy": "external-id",
272270
"aws-external-id": externalID,
273271
"aws-target-role-arn": plan.AWSRoleARN.ValueString(),
274-
"aws-regions": strings.Join(regions, ","),
272+
"aws-regions": toAnySlice(regions),
275273
})
276274
if err != nil {
277275
resp.Diagnostics.AddError("Failed to build source config", err.Error())
@@ -361,14 +359,25 @@ func regionsFromList(ctx context.Context, list types.List) ([]string, diag.Diagn
361359
return regions, diags
362360
}
363361

364-
func splitNonEmpty(s, sep string) []string {
365-
parts := strings.Split(s, sep)
366-
result := make([]string, 0, len(parts))
367-
for _, p := range parts {
368-
trimmed := strings.TrimSpace(p)
369-
if trimmed != "" {
370-
result = append(result, trimmed)
362+
func toAnySlice(ss []string) []any {
363+
out := make([]any, len(ss))
364+
for i, s := range ss {
365+
out[i] = s
366+
}
367+
return out
368+
}
369+
370+
func regionsFromStructValue(v *structpb.Value) []string {
371+
lv := v.GetListValue()
372+
if lv == nil {
373+
return []string{}
374+
}
375+
vals := lv.GetValues()
376+
out := make([]string, 0, len(vals))
377+
for _, item := range vals {
378+
if s := item.GetStringValue(); s != "" {
379+
out = append(out, s)
371380
}
372381
}
373-
return result
382+
return out
374383
}

aws-source/module/terraform/README.md

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,9 +98,16 @@ your HCL.
9898

9999
## Authentication
100100

101-
The Overmind provider reads `OVERMIND_API_KEY` from the environment. The API key
101+
The Overmind provider accepts an API key via the `api_key` attribute or the
102+
`OVERMIND_API_KEY` environment variable. The attribute takes precedence. The key
102103
must have `sources:write` scope.
103104

105+
```hcl
106+
provider "overmind" {
107+
api_key = var.overmind_api_key
108+
}
109+
```
110+
104111
The AWS provider must have permissions to create IAM roles and policies in the
105112
target account.
106113

0 commit comments

Comments
 (0)