chore: Adds PoC sa org rotation DO NOT MERGE - #4487
EspenAlbert wants to merge 11 commits into
Conversation
There was a problem hiding this comment.
Pull request overview
Adds experimental proof-of-concept Terraform Plugin Framework resources to demonstrate ModifyPlan-driven service account client secret rotation, including a mock-backed version (mongodbatlas_organization2) and a real-Atlas version (mongodbatlas_organization3). It also updates the provider’s internal service-account OAuth handling to support multiple credential sets within a single provider process (needed for rotation flows).
Changes:
- Introduces new PoC resources
mongodbatlas_organization2(mock store) andmongodbatlas_organization3(Atlas API-backed) with rotation scheduling/forcing behavior. - Registers the PoC resources in the framework provider and adds unit + acceptance tests for rotation behavior.
- Reworks service account OAuth token caching to be keyed by credential tuple (client ID/secret/base URL/terraform version), allowing rotation to change secrets without breaking subsequent reads.
Reviewed changes
Copilot reviewed 20 out of 20 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
POC_README.md |
Documents both PoCs, usage, limitations, and test commands. |
internal/testutil/acc/pre_check.go |
Adds acceptance-test skip helper for org-owner-dependent PoC tests. |
internal/service/organization3/resource.go |
Implements mongodbatlas_organization3 CRUD + ModifyPlan rotation workflow. |
internal/service/organization3/resource_test.go |
Real-Atlas acceptance test covering rotation lifecycle. |
internal/service/organization3/resource_schema.go |
Defines schema for org3 rotation block + secret metadata objects. |
internal/service/organization3/objects.go |
Object conversions and plan-shaping helpers for rotation. |
internal/service/organization3/model.go |
Rotation policy/decision logic and helpers. |
internal/service/organization3/model_test.go |
Unit tests for rotation decisions and ModifyPlan behavior. |
internal/service/organization3/auth.go |
Builds Atlas clients using SA creds from state when available. |
internal/service/organization3/api.go |
Atlas API calls for org + SA secret create/delete. |
internal/service/organization2/store.go |
Adds a persisted local JSON store for the mock PoC backend. |
internal/service/organization2/resource.go |
Implements mongodbatlas_organization2 CRUD + ModifyPlan rotation behavior. |
internal/service/organization2/resource_test.go |
Mock-backed acceptance tests for org2 rotation scenarios. |
internal/service/organization2/resource_schema.go |
Defines schema for org2 rotation block and state fields. |
internal/service/organization2/model.go |
Rotation timing and interval parsing helpers for org2. |
internal/service/organization2/model_test.go |
Unit tests for org2 rotation due logic + ModifyPlan shaping. |
internal/provider/provider.go |
Registers organization2 and organization3 resources in the provider. |
internal/config/service_account.go |
Replaces single global SA token source with a credential-keyed token cache. |
internal/config/service_account_test.go |
Adds tests validating new SA token cache semantics. |
internal/config/export_test.go |
Exposes internal SA token-cache helpers for tests. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| req := admin.NewCreateOrganizationRequest(name) | ||
| req.OrgOwnerId = new(orgOwnerID) | ||
| skipDefaultAlerts := true | ||
| req.SkipDefaultAlertsSettings = &skipDefaultAlerts |
| func (r *organization2RS) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { | ||
| var state TFModel | ||
| resp.Diagnostics.Append(req.State.Get(ctx, &state)...) | ||
| if resp.Diagnostics.HasError() { | ||
| return | ||
| } | ||
|
|
||
| if err := deleteStoreEntry(state.Name.ValueString()); err != nil { | ||
| resp.Diagnostics.AddError(errorUpdate, fmt.Sprintf("failed to persist mock store: %s", err)) | ||
| } |
| if plan.ClientSecretRotation.IsNull() { | ||
| state.Name = plan.Name | ||
| state.ClientSecretRotation = types.ObjectNull(rotationObjectType.AttrTypes) | ||
| resp.Diagnostics.Append(resp.State.Set(ctx, state)...) | ||
| return | ||
| } | ||
|
|
||
| planRotation, diags := RotationFromObject(ctx, plan.ClientSecretRotation) | ||
| resp.Diagnostics.Append(diags...) | ||
| stateRotation, stateDiags := RotationFromObject(ctx, state.ClientSecretRotation) | ||
| resp.Diagnostics.Append(stateDiags...) | ||
| if resp.Diagnostics.HasError() { | ||
| return | ||
| } |
|
This PR has gone 7 days without any activity and meets the project’s definition of "stale". This will be auto-closed if there is no new activity over the next 7 days. If the issue is still relevant and active, you can simply comment with a "bump" to keep it open, or add the label "not_stale". Thanks for keeping our repository healthy! |
Description
Link to any related issue(s): CLOUDP-381539
Type of change:
Required Checklist:
Further comments