feat: add !aws.organization_id YAML function#2117
Conversation
Add a new `!aws.organization_id` YAML function that retrieves the AWS Organization ID by calling the AWS Organizations DescribeOrganization API. This complements the existing AWS context functions and is equivalent to Terragrunt's `get_aws_org_id()`. - Create `pkg/aws/organization/` package with Getter interface, caching, and mock support (parallel to `pkg/aws/identity/`) - Register tag constants in both modern (`pkg/function/tags.go`) and legacy (`pkg/utils/yaml_utils.go`) layers - Implement modern layer function in `pkg/function/aws.go` - Implement legacy layer handler in `internal/exec/yaml_func_aws.go` - Add routing in `internal/exec/yaml_func_utils.go` - Add comprehensive unit tests across all three layers - Add documentation, blog post, PRD, and roadmap update - Update Go version references from 1.24/1.25 to 1.26 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Document how the function integrates with Atmos Authentication, including auth context flow, credential resolution, stack-level configuration, and per-auth-context caching. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
🚀 Go Version Change Detected This PR changes the Go version:
Tip Upgrade Checklist
This is an automated comment from the Go Version Check action. |
|
Warning This PR exceeds the recommended limit of 1,000 lines.Large PRs are difficult to review and may be rejected due to their size. Please verify that this PR does not address multiple issues. |
|
Important Cloud Posse Engineering Team Review RequiredThis pull request modifies files that require Cloud Posse's review. Please be patient, and a core maintainer will review your changes. To expedite this process, reach out to us on Slack in the |
Dependency ReviewThe following issues were found:
License Issuesgo.mod
Scanned Files
|
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughIntroduces a new Changes
Sequence Diagram(s)sequenceDiagram
participant Client as YAML Parser
participant Handler as yaml_func_aws
participant Cache as Organization Cache
participant AWSSDk as AWS SDK
Client->>Handler: processTagAwsOrganizationID(input)
Handler->>Handler: Validate input tag
Handler->>Handler: Extract auth context from stackInfo
Handler->>Cache: GetOrganizationCached(ctx, config, authContext)
alt Cache Hit
Cache-->>Handler: Return cached OrganizationInfo
else Cache Miss
Cache->>AWSSDk: DescribeOrganization()
AWSSDk-->>Cache: Organization details
Cache->>Cache: Store in cache with auth context key
Cache-->>Handler: Return OrganizationInfo
end
Handler->>Handler: Extract organization ID
Handler-->>Client: Return organization ID string
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Suggested reviewers
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
…d tags Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 6
🧹 Nitpick comments (2)
internal/exec/yaml_func_aws.go (1)
184-192: Add a defensive guard before dereferencing organization info.A nil/empty response from the getter path will currently panic or return a silent empty value.
Suggested hardening
orgInfo, err := getAWSOrganizationCached(ctx, atmosConfig, authContext) if err != nil { log.Error("Failed to get AWS organization info", "error", err) errUtils.CheckErrorPrintAndExit(err, "", "") return nil } + + if orgInfo == nil || orgInfo.ID == "" { + log.Error("Failed to get AWS organization info", "error", errUtils.ErrAwsDescribeOrganization) + errUtils.CheckErrorPrintAndExit(errUtils.ErrAwsDescribeOrganization, "", "") + return nil + } log.Debug("Resolved !aws.organization_id", "organization_id", orgInfo.ID) return orgInfo.ID🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@internal/exec/yaml_func_aws.go` around lines 184 - 192, The code calls getAWSOrganizationCached and immediately dereferences orgInfo.ID; add a defensive guard to check orgInfo for nil and orgInfo.ID for empty before using it. Specifically, after the call to getAWSOrganizationCached (in yaml_func_aws.go) check if orgInfo == nil || orgInfo.ID == "" and if so log an error via log.Error (include context like "empty or nil orgInfo") and call errUtils.CheckErrorPrintAndExit (or return an appropriate error) instead of proceeding to log.Debug and return orgInfo.ID, so you never dereference a nil orgInfo or return an empty ID.internal/exec/yaml_func_aws_test.go (1)
766-804: Add an explicit failure-path case forprocessTagAwsOrganizationID.The table includes
mockErr, but all rows are success cases. Add one non-nil error case so the new function’s error behavior is asserted, not inferred.As per coding guidelines "Every new feature must include comprehensive unit tests targeting >80% code coverage for all packages."
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@internal/exec/yaml_func_aws_test.go` around lines 766 - 804, Add a failing-case row to TestProcessTagAwsOrganizationID that sets mockErr to a non-nil error (e.g. errors.New("fetch error")) and asserts the function's error-path output (typically an empty string) by adding a test case named like "error fetching org info" to the tests slice; use the same helpers (runAWSOrgYamlFuncTest, processTagAwsOrganizationID, AWSOrganizationInfo) so the test verifies the function returns the expectedResult when mockErr is returned.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In @.devcontainer/Dockerfile:
- Line 1: The final image currently runs as root; update the Dockerfile's final
build stage by adding a non-root runtime user assignment (e.g., set USER vscode)
so containers start with non-root privileges; locate the final stage in the
Dockerfile (the stage labeled or ending with the runtime artifacts, after AS
confetty) and append the USER vscode directive (and ensure the vscode user
exists or is created earlier in the Dockerfile) to enforce the non-root runtime
user.
In `@NOTICE`:
- Around line 160-163: The NOTICE file is out of sync and must be regenerated
rather than edited manually; run the provided script
./scripts/generate-notice.sh to regenerate NOTICE (ensuring it captures the
github.com/aws/aws-sdk-go-v2/service/organizations entry and any other changes),
review the updated NOTICE for correctness, and commit the regenerated NOTICE
output to the branch before merging.
In `@pkg/aws/organization/organization_test.go`:
- Around line 16-42: Replace the manual mocks mockOrgGetter and
countingOrgGetter with the generated MockGetter from mock_organization.go:
remove types mockOrgGetter and countingOrgGetter, import/use MockGetter, and set
expectations in tests with MockGetter.EXPECT().GetOrganization(ctx, atmosConfig,
authContext).Return(<desired>, <err>).Times(n) to both supply return values and
verify call counts; update any test setup referencing mockOrgGetter or
countingOrgGetter to construct a *MockGetter and configure
EXPECT().GetOrganization(...) accordingly.
In `@pkg/aws/organization/organization.go`:
- Around line 55-58: The call to awsIdentity.LoadConfigWithAuth in
organization.go returns a raw error instead of being wrapped with the project
standard; replace the direct return of err with a wrapped error using
ErrAwsDescribeOrganization and ErrWrapFormat (the same pattern used later on
lines returning ErrAwsDescribeOrganization) so all organization lookup failures
are classified uniformly—wrap the error from awsIdentity.LoadConfigWithAuth with
fmt.Sprintf(ErrWrapFormat, ErrAwsDescribeOrganization, err) (or the project's
equivalent) before returning.
In `@pkg/function/aws_test.go`:
- Around line 315-326: Add a go:generate directive to the organization.Getter
interface declaration so mockgen can produce a mock for it (e.g., add
"//go:generate mockgen -destination=mock_organization.go -package=organization
<module>/pkg/aws/organization Organization" next to the interface in
organization.go), run mockgen to generate the mock, then remove the hand-written
mock type mockAWSOrgGetter and update the test to use the generated mock
(construct the generated mock struct, set its EXPECT() for GetOrganization(ctx,
atmosConfig, authContext) to return the desired (*awsOrg.OrganizationInfo,
error) values). Ensure references to the hand-rolled mockAWSOrgGetter and its
GetOrganization method are replaced with the generated mock's methods and
import.
In `@website/src/data/roadmap.js`:
- Line 378: The roadmap entry object with label '`!aws.organization_id` YAML
function' in website/src/data/roadmap.js is missing the required PR metadata;
add a pr: 2117 property to that object (the same object that has status:
'shipped', changelog: 'aws-organization-id-yaml-function') so shipped milestones
tied to minor/major PRs include the PR number, and after adding it, update the
related initiative's progress percentage to reflect this shipped milestone.
---
Nitpick comments:
In `@internal/exec/yaml_func_aws_test.go`:
- Around line 766-804: Add a failing-case row to TestProcessTagAwsOrganizationID
that sets mockErr to a non-nil error (e.g. errors.New("fetch error")) and
asserts the function's error-path output (typically an empty string) by adding a
test case named like "error fetching org info" to the tests slice; use the same
helpers (runAWSOrgYamlFuncTest, processTagAwsOrganizationID,
AWSOrganizationInfo) so the test verifies the function returns the
expectedResult when mockErr is returned.
In `@internal/exec/yaml_func_aws.go`:
- Around line 184-192: The code calls getAWSOrganizationCached and immediately
dereferences orgInfo.ID; add a defensive guard to check orgInfo for nil and
orgInfo.ID for empty before using it. Specifically, after the call to
getAWSOrganizationCached (in yaml_func_aws.go) check if orgInfo == nil ||
orgInfo.ID == "" and if so log an error via log.Error (include context like
"empty or nil orgInfo") and call errUtils.CheckErrorPrintAndExit (or return an
appropriate error) instead of proceeding to log.Debug and return orgInfo.ID, so
you never dereference a nil orgInfo or return an empty ID.
ℹ️ Review info
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Disabled knowledge base sources:
- Linear integration is disabled
You can enable these sources in your CodeRabbit configuration.
⛔ Files ignored due to path filters (1)
go.sumis excluded by!**/*.sum
📒 Files selected for processing (34)
.devcontainer/Dockerfile.github/actions/go-version-check/README.md.golangci.yml.pre-commit-config.yamlCLAUDE.mdNOTICEdemo/screengrabs/Makefiledocs/development.mddocs/prd/aws-organization-id-yaml-function.mderrors/errors.goexamples/quick-start-advanced/Dockerfilego.modinternal/exec/aws_getter.gointernal/exec/yaml_func_aws.gointernal/exec/yaml_func_aws_test.gointernal/exec/yaml_func_utils.gopkg/aws/organization/organization.gopkg/aws/organization/organization_test.gopkg/devcontainer/lifecycle_rebuild_test.gopkg/function/aws.gopkg/function/aws_test.gopkg/function/defaults.gopkg/function/tags.gopkg/function/tags_test.gopkg/utils/yaml_utils.gopkg/utils/yaml_utils_test.gotools/gomodcheck/go.modwebsite/blog/2026-02-26-aws-organization-id-yaml-function.mdxwebsite/docs/functions/yaml/aws.account-id.mdxwebsite/docs/functions/yaml/aws.caller-identity-arn.mdxwebsite/docs/functions/yaml/aws.caller-identity-user-id.mdxwebsite/docs/functions/yaml/aws.organization-id.mdxwebsite/docs/functions/yaml/aws.region.mdxwebsite/src/data/roadmap.js
…number - Replace manual mocks with generated MockGetter (go.uber.org/mock) in organization_test.go and aws_test.go per repo conventions - Add defensive nil/empty guard before dereferencing orgInfo in processTagAwsOrganizationID (legacy layer) - Add subprocess-based error path tests for processTagAwsOrganizationID (getter error, nil info, empty ID all cause os.Exit) - Wrap config-loading error with ErrAwsDescribeOrganization using standard ErrWrapFormat pattern - Add pr: 2117 to roadmap milestone Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 4
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@internal/exec/yaml_func_aws_test.go`:
- Around line 841-842: The -test.run argument passed to exec.Command uses an
unanchored regex which can match unintended tests; update the argument for the
subprocess invocation (the exec.Command call that uses os.Args[0] and
"-test.run=TestProcessTagAwsOrganizationID_ErrorHelper") to use an anchored
regex by changing the value to
"-test.run=^TestProcessTagAwsOrganizationID_ErrorHelper$" so only that exact
helper test is executed.
- Around line 731-743: Replace the handwritten mock type
mockAWSOrganizationGetter and its GetOrganization method with the generated
gomock MockGetter from pkg/aws/organization: remove the
mockAWSOrganizationGetter struct and its method, import the generated mock
package, create a mock via organization.NewMockGetter(ctrl) (using
gomock.NewController in the test setup), and replace uses of
mockAWSOrganizationGetter with the MockGetter and its
EXPECT().GetOrganization(...).Return(...) setup so tests follow the existing
gomock pattern.
In `@pkg/aws/organization/organization.go`:
- Around line 108-116: SetGetter currently allows setting a nil Getter which
will cause a panic later when getter.GetOrganization is called; update SetGetter
to guard against nil by checking if g == nil and either (a) reject it
immediately with a clear panic or error (e.g., panic("organization.SetGetter:
nil Getter provided")) or (b) substitute a safe no-op/default implementation so
future calls to getter.GetOrganization won't panic; implement the chosen
approach inside SetGetter and ensure the returned restore func still restores
the original getter. Refer to SetGetter, Getter, getter and GetOrganization when
making the change.
- Around line 76-78: The current nil-check for org returns (info, nil) which
hides an anomalous AWS response; instead when org == nil return a wrapped
ErrAwsDescribeOrganization error (including context such as the AWS account or
request ID if available) rather than nil error. Update the nil branch in the
function that inspects org (the org variable in
pkg/aws/organization/organization.go) to return the zero info and
fmt.Errorf("...: %w", ErrAwsDescribeOrganization) or errors.Wrap-style error so
callers get a clear, typed error instead of an empty ID.
ℹ️ Review info
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Disabled knowledge base sources:
- Linear integration is disabled
You can enable these sources in your CodeRabbit configuration.
⛔ Files ignored due to path filters (1)
go.sumis excluded by!**/*.sum
📒 Files selected for processing (13)
NOTICEgo.modinternal/exec/yaml_func_aws.gointernal/exec/yaml_func_aws_test.gopkg/aws/organization/mock_organization.gopkg/aws/organization/organization.gopkg/aws/organization/organization_test.gopkg/function/aws_test.gotests/snapshots/TestCLICommands_atmos_auth_validate_--verbose.stderr.goldentests/snapshots/TestCLICommands_atmos_describe_config_imports.stderr.goldentests/snapshots/TestCLICommands_atmos_describe_configuration.stderr.goldentests/snapshots/TestCLICommands_echo_info_runs_with_verbose_flag.stderr.goldenwebsite/src/data/roadmap.js
🚧 Files skipped from review as they are similar to previous changes (3)
- NOTICE
- pkg/function/aws_test.go
- internal/exec/yaml_func_aws.go
- Replace manual mockAWSOrganizationGetter and countingAWSOrgGetter with generated awsOrg.NewMockGetter(ctrl) in internal/exec tests - Anchor -test.run regex with ^...$ in subprocess error tests - Return wrapped error instead of empty info when Organization payload is nil - Guard SetGetter against nil injection (falls back to defaultGetter) - Revert golden snapshots to main (remove env-specific gh auth debug line) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@internal/exec/yaml_func_aws_test.go`:
- Around line 747-752: The test uses gomock.AnyTimes() on the
awsOrg.NewMockGetter().GetOrganization expectation which can hide missed or
extra calls; replace AnyTimes() with a strict Times(n) (e.g., Times(1) if the
getter should be called once) so the test asserts the exact invocation count for
GetOrganization. Do the same change for the other occurrence of the same
expectation (the block around lines shown for the second test), keeping the same
mock (awsOrg.NewMockGetter / mock) and expectation setup but swapping AnyTimes()
for the appropriate Times(...) value.
- Around line 839-842: The test currently only checks for a non-nil error from
cmd.Run() when tt.wantErr is true; change this to assert that the error is an
*exec.ExitError and that its ExitCode() == 1. After calling cmd.Run() in the
test (the same spot where tt.wantErr is checked), use errors.As (or a type
assertion) to extract an *exec.ExitError from err and assert the extraction
succeeded, then assert exitErr.ExitCode() == 1; keep behavior unchanged when
tt.wantErr is false.
ℹ️ Review info
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Disabled knowledge base sources:
- Linear integration is disabled
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (2)
internal/exec/yaml_func_aws_test.gopkg/aws/organization/organization.go
🚧 Files skipped from review as they are similar to previous changes (1)
- pkg/aws/organization/organization.go
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #2117 +/- ##
==========================================
+ Coverage 76.49% 76.52% +0.03%
==========================================
Files 831 832 +1
Lines 79254 79391 +137
==========================================
+ Hits 60624 60753 +129
- Misses 14837 14842 +5
- Partials 3793 3796 +3
Flags with carried forward coverage won't be shown. Click here to find out more.
🚀 New features to boost your workflow:
|
- Replace manual mocks with gomock-generated mocks across all test files - Tighten mock expectations from AnyTimes() to Times(1) - Add specific exit code assertion in subprocess error tests - Add organizationsAPI interface + injectable factory variables for testable defaultGetter.GetOrganization (was 0%, now 100%) - Add TestSetGetter_NilFallback for nil guard branch coverage - Add 7 defaultGetter unit tests: success, nil fields, config error, describe error, not-in-org error, nil payload, auth context passthrough - Regenerate mock_organization.go with both Getter and organizationsAPI - Overall organization package coverage: 47.88% → 95.3% Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@pkg/aws/organization/organization.go`:
- Around line 162-169: getCacheKey currently builds a colon-joined key which can
collide when fields contain ":"; update getCacheKey to produce a collision-safe
key by canonicalizing the three fields (authContext.Profile,
authContext.CredentialsFile, authContext.ConfigFile) and hashing them (e.g.,
SHA-256) or JSON-marshaling the struct and using the result as the cache key;
keep the nil check and return a deterministic string (e.g., hex hash or
marshaled string) instead of fmt.Sprintf("%s:%s:%s", ...).
- Around line 83-99: The code currently dereferences output.Organization without
ensuring output is non-nil after calling orgClient.DescribeOrganization; update
the DescribeOrganization handling (the call to orgClient.DescribeOrganization
and subsequent uses of output) to first check if output == nil and return a
wrapped errUtils.ErrAwsDescribeOrganization with a clear message if so, then
proceed to check output.Organization for nil as before (referencing output,
output.Organization and the org variable) to avoid a potential panic on
anomalous SDK responses.
ℹ️ Review info
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Disabled knowledge base sources:
- Linear integration is disabled
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (8)
internal/exec/yaml_func_aws_test.gopkg/aws/organization/mock_organization.gopkg/aws/organization/organization.gopkg/aws/organization/organization_test.gotests/snapshots/TestCLICommands_atmos_auth_validate_--verbose.stderr.goldentests/snapshots/TestCLICommands_atmos_describe_config_imports.stderr.goldentests/snapshots/TestCLICommands_atmos_describe_configuration.stderr.goldentests/snapshots/TestCLICommands_echo_info_runs_with_verbose_flag.stderr.golden
- Guard nil DescribeOrganization output before dereferencing Organization - Add TestDefaultGetter_GetOrganization_NilOutput for the new guard - Make cache keys collision-safe using %q with | delimiter - Revert golden snapshots to remove env-specific gh auth debug line Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
🧹 Nitpick comments (1)
pkg/aws/organization/organization_test.go (1)
185-192: Minor defensive improvement for goroutines.If assertions fail inside goroutines,
assertwon't stop execution - subsequentinfo.IDaccess could panic on nil. While the mock guarantees success here, a guard would be more resilient to future refactoring.💡 Optional defensive pattern
go func() { defer wg.Done() info, err := GetOrganizationCached(ctx, atmosConfig, nil) - assert.NoError(t, err) - assert.Equal(t, "o-concurrent", info.ID) + if assert.NoError(t, err) && info != nil { + assert.Equal(t, "o-concurrent", info.ID) + } }()🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@pkg/aws/organization/organization_test.go` around lines 185 - 192, In the goroutine calling GetOrganizationCached, avoid potential nil dereference on info.ID by guarding after the assert: check the returned err and/or info before accessing info.ID (e.g., after assert.NoError(t, err) add if err != nil || info == nil { return } ), or use an early return when err != nil; target the anonymous goroutine containing GetOrganizationCached, the assert.NoError call, and the subsequent info.ID access to add this defensive guard.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@pkg/aws/organization/organization_test.go`:
- Around line 185-192: In the goroutine calling GetOrganizationCached, avoid
potential nil dereference on info.ID by guarding after the assert: check the
returned err and/or info before accessing info.ID (e.g., after assert.NoError(t,
err) add if err != nil || info == nil { return } ), or use an early return when
err != nil; target the anonymous goroutine containing GetOrganizationCached, the
assert.NoError call, and the subsequent info.ID access to add this defensive
guard.
ℹ️ Review info
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Disabled knowledge base sources:
- Linear integration is disabled
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (2)
pkg/aws/organization/organization.gopkg/aws/organization/organization_test.go
🚧 Files skipped from review as they are similar to previous changes (1)
- pkg/aws/organization/organization.go
|
Warning Release Documentation RequiredThis PR is labeled
|
|
These changes were released in v1.208.0. |
what
!aws.organization_idYAML function that retrieves the AWS Organization ID by calling the AWS OrganizationsDescribeOrganizationAPIpkg/aws/organization/package withGetterinterface, per-auth-context caching with double-checked locking, and mock supportAWSOrganizationsNotInUseExceptionwith a clear error message when the account is not in an organizationErrAwsDescribeOrganizationsentinel errorwhy
get_aws_org_id()functionreferences
get_aws_org_id()Summary by CodeRabbit
Release Notes
New Features
!aws.organization_idYAML function to retrieve AWS Organization ID from stack configurations with automatic per-invocation caching.Chores
Documentation