Skip to content

audit(aws): surface OpenSearch RI tag-failure for ops monitoring (closes #250) - #831

Merged
cristim merged 5 commits into
mainfrom
fix/250-wave10
Jul 19, 2026
Merged

cristim merged 5 commits into
mainfrom
fix/250-wave10

Conversation

@cristim

@cristim cristim commented May 28, 2026 •

Copy link
Copy Markdown
Member

Summary

  • Replace the freeform WARNING: log line in PurchaseCommitment with a
    structured OPENSEARCH_TAG_FAILED commitment_id=<id> error=<msg> line so
    operator dashboards and CloudWatch Logs Insights queries can alert on
    untagged RIs
  • Add runbooks/opensearch-untagged-ri.md covering background (unsupported
    reserved-instance ARN type), manual re-tagging steps, and the fallback for
    when AWS still rejects the call
  • Update the PurchaseCommitment comment to reference the runbook

Test plan

  • TestPurchaseCommitment_TagFailure_StructuredLog (new): verifies
    OPENSEARCH_TAG_FAILED sentinel and commitment_id= field appear in the
    log, purchase result is still Success=true, and account ID is absent from
    the line
  • Existing TestClient_PurchaseCommitment_AddTagsFailureDoesNotFailPurchase
    remains green (non-fatal contract unchanged)
  • go vet ./providers/aws/services/opensearch/... clean

Summary by CodeRabbit

  • Bug Fixes

    • Added validation to prevent invalid or oversized OpenSearch reserved-instance counts from being submitted.
    • Improved handling and reporting when tagging fails after a reserved-instance purchase, while preserving the successful purchase result.
  • Documentation

    • Added an operational runbook with steps for identifying and manually tagging OpenSearch reserved instances when automatic tagging fails.

@cristim cristim added triaged Item has been triaged priority/p3 Polish / idea / may never ship severity/low Minor harm urgency/eventually No deadline impact/internal Team-internal only effort/xs Trivial / one-liner type/chore Maintenance / non-user-visible labels May 28, 2026
@cristim

cristim commented May 28, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented May 28, 2026 •

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

The OpenSearch client now uses a renamed API interface, validates reserved-instance counts, emits structured logs when tagging fails, and adds tests and an operational runbook for untagged reservations.

Changes

OpenSearch commitment flow

Layer / File(s) Summary
Client contract and purchase validation
providers/aws/services/opensearch/client.go
Renames the OpenSearch interface to API, updates client injection and metadata helpers, and validates InstanceCount against the AWS int32 limit before purchase requests.
Tagging failure observability and remediation
providers/aws/services/opensearch/client.go, providers/aws/services/opensearch/client_test.go, runbooks/opensearch-untagged-ri.md
Emits OPENSEARCH_TAG_FAILED with the commitment ID when tagging fails, verifies the behavior in tests, and documents manual remediation for untagged reservations.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 75.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: surfacing OpenSearch RI tag-failure events for operational monitoring.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/250-wave10

Comment @coderabbitai help to get the list of available commands.

@coderabbitai

coderabbitai Bot commented May 28, 2026

Copy link
Copy Markdown
Contributor
✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@cristim

cristim commented May 30, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai full review

@coderabbitai

coderabbitai Bot commented May 30, 2026

Copy link
Copy Markdown
Contributor
✅ Actions performed

Full review triggered.

cristim added a commit that referenced this pull request Jun 1, 2026
…lure (#831)

The prior run failed because GitHub's CDN returned a 404 for the
pinned actions/setup-go SHA at download time (transient infrastructure
issue). No code changes are needed; this empty commit re-triggers CI.
@cristim

cristim commented Jun 1, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jun 1, 2026

Copy link
Copy Markdown
Contributor
✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@cristim

cristim commented Jun 4, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jun 4, 2026

Copy link
Copy Markdown
Contributor

Rate Limit Exceeded

@cristim have exceeded the limit for the number of chat messages per hour. Please wait 39 minutes and 22 seconds before sending another message.

@cristim

cristim commented Jun 5, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jun 5, 2026 •

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@cristim

cristim commented Jun 8, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai full review

@coderabbitai

coderabbitai Bot commented Jun 8, 2026 •

Copy link
Copy Markdown
Contributor
✅ Action performed

Full review finished.

@cristim
cristim changed the base branch from feat/multicloud-web-frontend to main June 9, 2026 15:44
cristim added a commit that referenced this pull request Jun 19, 2026
…lure (#831)

The prior run failed because GitHub's CDN returned a 404 for the
pinned actions/setup-go SHA at download time (transient infrastructure
issue). No code changes are needed; this empty commit re-triggers CI.
@cristim

cristim commented Jun 19, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jun 19, 2026 •

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@cristim

cristim commented Jul 9, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 9, 2026 •

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

cristim added a commit that referenced this pull request Jul 10, 2026
…lure (#831)

The prior run failed because GitHub's CDN returned a 404 for the
pinned actions/setup-go SHA at download time (transient infrastructure
issue). No code changes are needed; this empty commit re-triggers CI.
@cristim

cristim commented Jul 10, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 10, 2026 •

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@providers/aws/services/opensearch/client_test.go`:
- Around line 954-1017: The test TestPurchaseCommitment_TagFailure_StructuredLog
must also verify that the structured log includes the tag failure error details,
not just the sentinel and commitment ID. Add an assertion against logOut for the
expected error field/value, such as the ValidationException message, while
retaining the existing PII exclusion assertion.

In `@providers/aws/services/opensearch/client.go`:
- Around line 191-199: Move the safeInt32Count validation in the reservation
purchase flow to immediately after result is initialized, before findOfferingID
and idempotencyGuard execute; preserve the existing error assignment and early
return so invalid counts cannot bypass validation. Use the surrounding purchase
method and safeInt32Count as anchors.

In `@runbooks/opensearch-untagged-ri.md`:
- Around line 64-66: Revise the fallback in the runbook so parent OpenSearch
domain tagging is explicitly described as an attribution workaround, not
reserved-instance remediation. Explain that RI-level queries must map the RI ID
to the tagged parent domain (or provide equivalent query changes) to preserve
cost and audit attribution; otherwise retain the spreadsheet record-only option.
- Around line 11-13: Update the runbook to state that a failed
opensearch:AddTags request may leave all six tags absent: Purpose, ResourceType,
Region, PurchaseDate, Tool, and the CUDly source tag. Revise the remediation
command to restore the complete tag set, or explicitly instruct operators to
verify and restore every tag before closing the incident.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 1e1249d6-614f-43d5-a33f-6a5a9306dd3b

📥 Commits

Reviewing files that changed from the base of the PR and between d484c9d and fe4b99a.

📒 Files selected for processing (3)
  • providers/aws/services/opensearch/client.go
  • providers/aws/services/opensearch/client_test.go
  • runbooks/opensearch-untagged-ri.md

Comment on lines +954 to +1017

// TestPurchaseCommitment_TagFailure_StructuredLog asserts that when AddTags
// returns an error after a successful purchase:
// - the purchase result is still Success=true (tag failure is non-fatal)
// - a line containing "OPENSEARCH_TAG_FAILED" is emitted to the log
// - the commitment ID is present in that log line (for operator lookup)
// - no AWS account ID or other PII appears in the log line
func TestPurchaseCommitment_TagFailure_StructuredLog(t *testing.T) {
mockOS := &MockOpenSearchClient{}
mockSTS := &MockOpenSearchSTSClient{}
t.Cleanup(func() { mockOS.AssertExpectations(t); mockSTS.AssertExpectations(t) })

client := &Client{client: mockOS, stsClient: mockSTS, region: "us-east-1"}

rec := common.Recommendation{
Service: common.ServiceSearch,
ResourceType: "m5.large.search",
Count: 1,
Term: "1yr",
Region: "us-east-1",
PaymentOption: "all-upfront",
}

mockOS.On("DescribeReservedInstanceOfferings", mock.Anything, mock.Anything).
Return(&opensearch.DescribeReservedInstanceOfferingsOutput{
ReservedInstanceOfferings: []types.ReservedInstanceOffering{{
ReservedInstanceOfferingId: aws.String("off-tag-fail"),
InstanceType: types.OpenSearchPartitionInstanceTypeM5LargeSearch,
Duration: 31536000,
PaymentOption: types.ReservedInstancePaymentOptionAllUpfront,
}},
}, nil)

const riID = "ri-tag-fail-abc123"
mockOS.On("PurchaseReservedInstanceOffering", mock.Anything, mock.Anything).
Return(&opensearch.PurchaseReservedInstanceOfferingOutput{
ReservedInstanceId: aws.String(riID),
}, nil)

mockSTS.On("GetCallerIdentity", mock.Anything, mock.Anything).
Return(&sts.GetCallerIdentityOutput{Account: aws.String("000000000000")}, nil)

mockOS.On("AddTags", mock.Anything, mock.Anything).
Return(nil, fmt.Errorf("ValidationException: invalid resource type")).Once()

// Redirect log output to capture the structured line.
var buf bytes.Buffer
origWriter := log.Writer()
log.SetOutput(&buf)
t.Cleanup(func() { log.SetOutput(origWriter) })

result, err := client.PurchaseCommitment(context.Background(), rec, common.PurchaseOptions{Source: common.PurchaseSourceCLI})

assert.NoError(t, err, "tag failure must not surface as a purchase error")
assert.True(t, result.Success, "purchase must remain successful when tagging fails")
assert.Equal(t, riID, result.CommitmentID)

logOut := buf.String()
assert.Contains(t, logOut, "OPENSEARCH_TAG_FAILED", "structured sentinel must appear in log")
assert.Contains(t, logOut, "commitment_id="+riID, "commitment ID must be present for operator lookup")
// The account ID (000000000000) must not be logged -- it is not PII but
// the structured line should stay minimal and scoped to the commitment.
assert.NotContains(t, logOut, "000000000000", "account ID must not appear in the tag-failure log line")
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Assert the error field as well as the sentinel.

Lines 1011-1016 do not verify the error value, so a log containing only OPENSEARCH_TAG_FAILED commitment_id=... would pass. Assert the expected field too:

 	assert.Contains(t, logOut, "OPENSEARCH_TAG_FAILED", "structured sentinel must appear in log")
 	assert.Contains(t, logOut, "commitment_id="+riID, "commitment ID must be present for operator lookup")
+	assert.Contains(t, logOut, "error=ValidationException: invalid resource type")
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// TestPurchaseCommitment_TagFailure_StructuredLog asserts that when AddTags
// returns an error after a successful purchase:
// - the purchase result is still Success=true (tag failure is non-fatal)
// - a line containing "OPENSEARCH_TAG_FAILED" is emitted to the log
// - the commitment ID is present in that log line (for operator lookup)
// - no AWS account ID or other PII appears in the log line
func TestPurchaseCommitment_TagFailure_StructuredLog(t *testing.T) {
mockOS := &MockOpenSearchClient{}
mockSTS := &MockOpenSearchSTSClient{}
t.Cleanup(func() { mockOS.AssertExpectations(t); mockSTS.AssertExpectations(t) })
client := &Client{client: mockOS, stsClient: mockSTS, region: "us-east-1"}
rec := common.Recommendation{
Service: common.ServiceSearch,
ResourceType: "m5.large.search",
Count: 1,
Term: "1yr",
Region: "us-east-1",
PaymentOption: "all-upfront",
}
mockOS.On("DescribeReservedInstanceOfferings", mock.Anything, mock.Anything).
Return(&opensearch.DescribeReservedInstanceOfferingsOutput{
ReservedInstanceOfferings: []types.ReservedInstanceOffering{{
ReservedInstanceOfferingId: aws.String("off-tag-fail"),
InstanceType: types.OpenSearchPartitionInstanceTypeM5LargeSearch,
Duration: 31536000,
PaymentOption: types.ReservedInstancePaymentOptionAllUpfront,
}},
}, nil)
const riID = "ri-tag-fail-abc123"
mockOS.On("PurchaseReservedInstanceOffering", mock.Anything, mock.Anything).
Return(&opensearch.PurchaseReservedInstanceOfferingOutput{
ReservedInstanceId: aws.String(riID),
}, nil)
mockSTS.On("GetCallerIdentity", mock.Anything, mock.Anything).
Return(&sts.GetCallerIdentityOutput{Account: aws.String("000000000000")}, nil)
mockOS.On("AddTags", mock.Anything, mock.Anything).
Return(nil, fmt.Errorf("ValidationException: invalid resource type")).Once()
// Redirect log output to capture the structured line.
var buf bytes.Buffer
origWriter := log.Writer()
log.SetOutput(&buf)
t.Cleanup(func() { log.SetOutput(origWriter) })
result, err := client.PurchaseCommitment(context.Background(), rec, common.PurchaseOptions{Source: common.PurchaseSourceCLI})
assert.NoError(t, err, "tag failure must not surface as a purchase error")
assert.True(t, result.Success, "purchase must remain successful when tagging fails")
assert.Equal(t, riID, result.CommitmentID)
logOut := buf.String()
assert.Contains(t, logOut, "OPENSEARCH_TAG_FAILED", "structured sentinel must appear in log")
assert.Contains(t, logOut, "commitment_id="+riID, "commitment ID must be present for operator lookup")
// The account ID (000000000000) must not be logged -- it is not PII but
// the structured line should stay minimal and scoped to the commitment.
assert.NotContains(t, logOut, "000000000000", "account ID must not appear in the tag-failure log line")
}
logOut := buf.String()
assert.Contains(t, logOut, "OPENSEARCH_TAG_FAILED", "structured sentinel must appear in log")
assert.Contains(t, logOut, "commitment_id="+riID, "commitment ID must be present for operator lookup")
assert.Contains(t, logOut, "error=ValidationException: invalid resource type")
// The account ID (000000000000) must not be logged -- it is not PII but
// the structured line should stay minimal and scoped to the commitment.
assert.NotContains(t, logOut, "000000000000", "account ID must not appear in the tag-failure log line")
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@providers/aws/services/opensearch/client_test.go` around lines 954 - 1017,
The test TestPurchaseCommitment_TagFailure_StructuredLog must also verify that
the structured log includes the tag failure error details, not just the sentinel
and commitment ID. Add an assertion against logOut for the expected error
field/value, such as the ValidationException message, while retaining the
existing PII exclusion assertion.

Comment on lines +191 to +199
instanceCount, countErr := safeInt32Count(rec.Count)
if countErr != nil {
result.Error = countErr
return result, result.Error
}
input := &opensearch.PurchaseReservedInstanceOfferingInput{
ReservedInstanceOfferingId: aws.String(offeringID),
ReservationName: aws.String(reservationName),
InstanceCount: aws.Int32(int32(rec.Count)),
InstanceCount: aws.Int32(instanceCount),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Validate the count before lookups and idempotency handling.

At Lines 191-195, validation happens after findOfferingID and idempotencyGuard. The guard at Lines 182-188 can therefore return success for an existing reservation before safeInt32Count runs, allowing an invalid count to bypass the new validation. Move this check immediately after result is initialized.

Suggested placement
 func (c *Client) PurchaseCommitment(ctx context.Context, rec common.Recommendation, opts common.PurchaseOptions) (common.PurchaseResult, error) {
 	result := common.PurchaseResult{
 		// ...
 	}
+
+	instanceCount, countErr := safeInt32Count(rec.Count)
+	if countErr != nil {
+		result.Error = countErr
+		return result, result.Error
+	}

 	offeringID, err := c.findOfferingID(ctx, rec, opts.ExecutionID)
 	// ...

-	instanceCount, countErr := safeInt32Count(rec.Count)
-	if countErr != nil {
-		result.Error = countErr
-		return result, result.Error
-	}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
instanceCount, countErr := safeInt32Count(rec.Count)
if countErr != nil {
result.Error = countErr
return result, result.Error
}
input := &opensearch.PurchaseReservedInstanceOfferingInput{
ReservedInstanceOfferingId: aws.String(offeringID),
ReservationName: aws.String(reservationName),
InstanceCount: aws.Int32(int32(rec.Count)),
InstanceCount: aws.Int32(instanceCount),
func (c *Client) PurchaseCommitment(ctx context.Context, rec common.Recommendation, opts common.PurchaseOptions) (common.PurchaseResult, error) {
result := common.PurchaseResult{
// ...
}
instanceCount, countErr := safeInt32Count(rec.Count)
if countErr != nil {
result.Error = countErr
return result, result.Error
}
offeringID, err := c.findOfferingID(ctx, rec, opts.ExecutionID)
// ...
input := &opensearch.PurchaseReservedInstanceOfferingInput{
ReservedInstanceOfferingId: aws.String(offeringID),
ReservationName: aws.String(reservationName),
InstanceCount: aws.Int32(instanceCount),
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@providers/aws/services/opensearch/client.go` around lines 191 - 199, Move the
safeInt32Count validation in the reservation purchase flow to immediately after
result is initialized, before findOfferingID and idempotencyGuard execute;
preserve the existing error assignment and early return so invalid counts cannot
bypass validation. Use the surrounding purchase method and safeInt32Count as
anchors.

Comment on lines +11 to +13
Emitted by `providers/aws/services/opensearch/client.go` when
`opensearch:AddTags` fails after a successful RI purchase. The RI is active;
only the CUDly source tag is absent.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

Describe all tags affected by the failed request.

Lines 11-13 claim only the CUDly source tag is absent, but client.go:342-349 sends six tags in one AddTags request. A failed request may leave Purpose, ResourceType, Region, PurchaseDate, Tool, and the source tag absent. The manual command at Lines 53-58 also restores only a subset; reword this and either restore or verify the complete tag set.

Also applies to: 45-59

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@runbooks/opensearch-untagged-ri.md` around lines 11 - 13, Update the runbook
to state that a failed opensearch:AddTags request may leave all six tags absent:
Purpose, ResourceType, Region, PurchaseDate, Tool, and the CUDly source tag.
Revise the remediation command to restore the complete tag set, or explicitly
instruct operators to verify and restore every tag before closing the incident.

Comment on lines +64 to +66
3. **Fallback (AWS still rejects reserved-instance ARN):** Tag the parent
OpenSearch domain instead, or record the RI ID in your cost-allocation
spreadsheet until AWS adds native support.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Do not present parent-domain tagging as RI remediation.

Tagging the parent domain does not attach cudly:purchase-source to the reserved instance, so the RI-level cost and audit queries described at Lines 33-35 can still miss it. Document this as a separate attribution workaround and provide the required mapping/query changes, or retain record-only remediation.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@runbooks/opensearch-untagged-ri.md` around lines 64 - 66, Revise the fallback
in the runbook so parent OpenSearch domain tagging is explicitly described as an
attribution workaround, not reserved-instance remediation. Explain that RI-level
queries must map the RI ID to the tagged parent domain (or provide equivalent
query changes) to preserve cost and audit attribution; otherwise retain the
spreadsheet record-only option.

cristim added 5 commits July 17, 2026 21:23
 #250)

Emit a structured OPENSEARCH_TAG_FAILED log line when the best-effort
AddTags call after RI purchase fails, so operator dashboards can alert on
untagged reservations. Add runbooks/opensearch-untagged-ri.md with manual
remediation steps and background on the unsupported ARN type. Update the
PurchaseCommitment comment to reference the runbook.
…lure (#831)

The prior run failed because GitHub's CDN returned a 404 for the
pinned actions/setup-go SHA at download time (transient infrastructure
issue). No code changes are needed; this empty commit re-triggers CI.
- Add periods to 14 comments missing godot terminator
- Rename OpenSearchAPI interface to API (revive: stutters as
  opensearch.OpenSearchAPI); update SetOpenSearchAPI and test comment
- Reorder Client struct fields for optimal GC pointer bitmap (govet
  fieldalignment: 80->72 pointer bytes)
- Reorder two test-case structs to remove padding (govet fieldalignment)
- Add rec.Count bounds check before int32 conversion; suppress gosec G115
  with nolint comment on the now-safe cast
- Add .Once() to AddTags mock to assert exactly one call (ErrPermanent
  guarantees no retry; strengthens the new tag-failure test)
Replace the opaque uint-subtraction trick with explicit < 1 || > MaxInt32
guards so gosec can recognize the pattern as a safe bounded conversion
and no longer flags G115 on int32(n).
The pre-commit markdownlint hook (MD040) failed on
runbooks/opensearch-untagged-ri.md because three fenced code blocks
lacked a language specifier. Tag the plain-text log-pattern and ARN
blocks as `text` so the pre-commit hook passes.
@cristim
cristim merged commit 673f1f0 into main Jul 19, 2026
19 checks passed
cristim added a commit that referenced this pull request Jul 19, 2026
…it (follow-up to #831) (#1460)

PurchaseCommitment ran safeInt32Count after the idempotency guard, so a re-drive
carrying an invalid instance count (negative / int32-overflow / zero) against an
already-existing reservation would short-circuit to Success and never surface the
bad input. Move the count validation to the boundary -- immediately after result
init, before findOfferingID and the idempotency guard -- so invalid counts fail
loud regardless of idempotency state (addresses the unresolved CodeRabbit Major
thread on #831).

Also harden the tag-failure structured-log test to assert the error= field
carries the underlying cause, not just the OPENSEARCH_TAG_FAILED sentinel and
commitment_id (unresolved CodeRabbit Minor thread on #831).

Tests:
- New TestClient_PurchaseCommitment_InvalidCount_FailsBeforeIdempotencyGuard:
  verified it FAILS on the pre-fix ordering (returns Success via short-circuit)
  and PASSES after the reorder; asserts the offering lookup and reservation
  describe are never reached.
- TestClient_PurchaseCommitment_OfferingNotFound now sets a valid Count=1 (it
  previously relied on Count=0 being tolerated up to the offering lookup).
@cristim
cristim deleted the fix/250-wave10 branch July 27, 2026 11:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

effort/xs Trivial / one-liner impact/internal Team-internal only priority/p3 Polish / idea / may never ship severity/low Minor harm triaged Item has been triaged type/chore Maintenance / non-user-visible urgency/eventually No deadline

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant