Skip to content

test(api): RI-exchange carve-out test reaches a live AWS STS/IMDS call under mutation #175

Open

Description

@cristim

Summary

TestExecuteExchange_PlainAdminIsRefused (internal/api/ri_exchange_carveout_test.go, added in PR LeanerCloud/cloud-commitments-cli#1758) is fast and offline in the state we ship. But under mutation — with {ActionExecute, ResourceRIExchange} removed from adminCarvedOuts, which is exactly how the test is verified — execution proceeds past the permission gate and attempts a live AWS call:

failed to resolve cloud account scope: resolve source aws account for reshape scope:
sts get-caller-identity: operation error STS: GetCallerIdentity, exceeded maximum number
of attempts, 3, get identity: get credentials: failed to refresh cached credentials,
no EC2 IMDS role found, operation error ec2imds: GetMetadata, exceeded maximum number of
attempts, 3, request send failed, Get "http://169.254.169.254/latest/meta-data/iam/
security-credentials/": dial tcp 169.254.169.254:80: connect: host is down

Unmutated the carve-out refuses at the gate in ~1.3s and no call is made. The exposure exists only while someone re-runs the mutation matrix — but that is a routine verification step for this test, not an exotic one.

Why it is worth fixing

  1. Correctness, not just slowness. The 9.7s figure is a network timeout on a machine with no credentials. On a machine that has AWS credentials in the environment, that path resolves a live account identity. Nobody re-running a mutation matrix should have to think about which credentials are loaded.
  2. It is the mirror of a positive we recorded recently. PR fix(cli): apply --max-instances once run-wide, not per service and region cloud-commitments-cli#1725's guard hoist was called out in review specifically because it removed a live DescribeReservedDBInstances from the unit suite. This is the same class, introduced rather than removed.
  3. It makes the mutation matrix environment-dependent, which is the property that makes a verification step untrustworthy.

Mechanism NOT traced — deliberately

I established where it is not and stopped there rather than guessing:

  • The failure is in cloud-account scope resolution, which happens upstream of the executeExchangeFn seam at internal/api/handler.go:126-130 ("the RI exchange execution function injected by tests"). Injecting that seam alone would not prevent the call.
  • It is not reached through h.config, so stubbing mockStore cannot block it either — which is the obvious first guess and is wrong.

I have not read what constructs the STS client, so I am not proposing a mechanism. That is the expensive half and can be done fresh.

Suggested direction (unvalidated)

Either an injectable scope resolver on Handler, or a fail-fast when no credential source is configured under test, so the path errors immediately and locally instead of attempting network I/O. Whoever picks this up should trace the constructor first; the above is a starting point, not a design.

Reproduction

  1. Remove {ActionExecute, ResourceRIExchange} from adminCarvedOuts in internal/auth/types.go.
  2. go test -count=1 -run '^TestExecuteExchange_PlainAdminIsRefused$' ./internal/api/
  3. Observe the STS/IMDS error above and the ~9.7s runtime (vs ~1.3s unmutated).

Found while mutation-verifying PR LeanerCloud/cloud-commitments-cli#1758 (issue LeanerCloud/cloud-commitments-cli#1644).

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions