Skip to content

Latest commit

 

History

History
245 lines (231 loc) · 27.3 KB

File metadata and controls

245 lines (231 loc) · 27.3 KB
service codedeploy
sdk_module aws-sdk-go-v2/service/codedeploy@v1.38.4
last_audit_commit 59ab8f6a
last_audit_date 2026-08-10
overall A
ops
CreateApplication GetApplication ListApplications DeleteApplication UpdateApplication BatchGetApplications CreateDeploymentGroup GetDeploymentGroup ListDeploymentGroups DeleteDeploymentGroup UpdateDeploymentGroup BatchGetDeploymentGroups CreateDeployment GetDeployment ListDeployments StopDeployment ContinueDeployment SkipWaitTimeForInstanceTermination BatchGetDeployments BatchGetDeploymentInstances BatchGetDeploymentTargets GetDeploymentInstance GetDeploymentTarget ListDeploymentInstances ListDeploymentTargets PutLifecycleEventHookExecutionStatus CreateDeploymentConfig GetDeploymentConfig ListDeploymentConfigs DeleteDeploymentConfig RegisterApplicationRevision GetApplicationRevision ListApplicationRevisions BatchGetApplicationRevisions DeleteGitHubAccountToken ListGitHubAccountTokenNames RegisterOnPremisesInstance DeregisterOnPremisesInstance GetOnPremisesInstance ListOnPremisesInstances BatchGetOnPremisesInstances AddTagsToOnPremisesInstances RemoveTagsFromOnPremisesInstances TagResource UntagResource ListTagsForResource DeleteResourcesByExternalId
wire errors state persist
ok
ok
ok
ok
wire errors state persist note
ok
ok
ok
ok
createTime was UnixMilli int64, fixed to awstime.Epoch float64
wire errors state persist
ok
ok
ok
ok
wire errors state persist
ok
ok
ok
ok
wire errors state persist
ok
ok
ok
ok
wire errors state persist note
ok
n/a
ok
ok
same createTime fix as GetApplication
wire errors state persist
ok
ok
ok
ok
wire errors state persist
ok
ok
ok
ok
wire errors state persist
ok
ok
ok
ok
wire errors state persist
ok
ok
ok
ok
wire errors state persist
ok
ok
ok
ok
wire errors state persist
ok
ok
ok
ok
wire errors state persist note
ok
ok
ok
ok
FIXED this pass: fileExistsBehavior was accepted and stored unvalidated (any garbage string round-tripped); now validated against the real DISALLOW|OVERWRITE|RETAIN enum, InvalidFileExistsBehaviorException (confirmed in CreateDeployment's own error set, deserializers.go) for anything else
wire errors state persist note
ok
ok
ok
ok
createTime/completeTime were UnixMilli int64, fixed to awstime.Epoch float64
wire errors state persist note
ok
n/a
ok
ok
createTimeRange.start/end request fields were parsed as epoch-millis (time.UnixMilli), fixed to epoch-seconds float64 matching smithytime.FormatEpochSeconds
wire errors state persist note
ok
ok
ok
ok
output status was returning the deployment's own status literal (Stopped), fixed to the real StopStatus enum (Succeeded); deployment status itself still correctly becomes Stopped
wire errors state note
ok
ok
ok
FIXED this pass: READY_WAIT/TERMINATION_WAIT are not DeploymentStatus values at all (they're ContinueDeploymentInput.DeploymentWaitType, an input enum) -- the prior 'blue/green wait-state' framing conflated the two. The real gap was narrower: ContinueDeployment accepted a deployment in ANY status and deploymentWaitType was read off the wire and never validated or used. Added the real precondition (status must be Ready, else DeploymentIsNotInReadyStateException/DeploymentAlreadyCompletedException per types/errors.go:221,556-557) and deploymentWaitType enum validation (InvalidDeploymentWaitTypeException). Since CreateDeployment completes synchronously and no op ever sets status=Ready, ContinueDeployment now always errors in practice -- which is the honest behavior for a backend with no genuine blue/green wait state, not a regression
wire errors state persist note
ok
ok
ok
n/a
was missing the deploymentId existence check every sibling deployment-scoped op has; fixed
wire errors state persist note
ok
n/a
ok
ok
same createTime/completeTime fix as GetDeployment
wire errors state persist note
ok
ok
ok
n/a
FIXED this pass: resolves real on-premises instances matched against the deployment group's tag filters (matchesOnPremisesTargeting), status derived from the deployment's own Status via targetStatusForDeployment instead of hardcoded Succeeded; unmatched/never-registered IDs silently omitted matching the codebase's Batch* convention
wire errors state persist note
ok
ok
ok
n/a
FIXED this pass: instanceTarget/ecsTarget/lambdaTarget union resolved from real deployment group config (on-prem tag matching / ECSServices list / single Lambda target); deploymentTargetType + nested member wire shape verified against types.DeploymentTarget deserializer
wire errors state persist note
ok
ok
ok
n/a
FIXED this pass: real lookup against computed targets, InstanceDoesNotExistException for a real-but-non-participating or unknown instance instead of fabricating a match for any ID
wire errors state persist note
ok
ok
ok
n/a
FIXED this pass: real lookup against computed targets, new DeploymentTargetDoesNotExistException (404) sentinel for an unknown target ID instead of fabricating a match
wire errors state persist note
ok
ok
ok
n/a
FIXED this pass: returns the real matched instanceTarget IDs for Server/Lambda platforms; empty for ECS (no per-instance concept) -- previously always empty regardless of real targets
wire errors state persist note
ok
ok
ok
n/a
FIXED this pass: returns the real computed target IDs (sorted) -- previously always empty regardless of real targets
wire errors state persist note
ok
ok
ok
n/a
was missing the deploymentId existence check every sibling deployment-scoped op has; fixed
wire errors state persist
ok
ok
ok
ok
wire errors state persist note
ok
ok
ok
ok
createTime was UnixMilli int64, fixed to awstime.Epoch float64
wire errors state persist
ok
n/a
ok
ok
wire errors state persist
ok
ok
ok
ok
wire errors state persist note
ok
ok
ok
ok
FIXED this pass: persists to a real applicationRevisions store.Table keyed by (appName, canonical revision JSON); re-registering an already-known revision refreshes description, preserves original registerTime
wire errors state persist note
ok
ok
ok
ok
FIXED this pass: reads the persisted revision, populates revisionInfo (GenericRevisionInfo: description/registerTime/firstUsedTime/lastUsedTime/deploymentGroups, field names+epoch-seconds verified against deserializers.go), new RevisionDoesNotExistException (404) for an unregistered revision instead of echoing the request back
wire errors state persist note
ok
ok
ok
ok
FIXED this pass: returns real registered revisions for the application with deployed/s3Bucket/s3KeyPrefix/sortBy/sortOrder filtering; previously always empty since nothing was ever persisted
wire errors state persist note
ok
ok
ok
ok
FIXED this pass: genericRevisionInfo populated for revisions that are actually registered, omitted for ones that are not, instead of echoing the input unconditionally with no real lookup
wire errors state persist
ok
ok
ok
ok
wire errors state persist
ok
n/a
ok
ok
wire errors state persist
ok
ok
ok
ok
wire errors state persist note
ok
ok
ok
ok
ErrOnPremisesInstanceNotFound had the wrong error code (InstanceNameRequiredException) and no errorMappings entry at all, so it fell through to 500 ServiceException; fixed to InstanceDoesNotExistException + 404
wire errors state persist note
ok
ok
ok
ok
same registerTime/deregisterTime epoch fix + errorMappings fix as DeregisterOnPremisesInstance
wire errors state persist
ok
n/a
ok
ok
wire errors state persist note
ok
n/a
ok
ok
same registerTime/deregisterTime epoch fix
wire errors state persist
ok
ok
ok
ok
wire errors state persist
ok
ok
ok
ok
wire errors state persist
ok
ok
ok
ok
wire errors state persist
ok
ok
ok
ok
wire errors state persist
ok
ok
ok
ok
wire errors state persist note
ok
n/a
ok
n/a
no resource-by-external-id tracking exists anywhere in this backend (or any other gopherstack service); an idempotent no-op matches real AWS's own best-effort cleanup semantics
families
Application DeploymentGroup Deployment DeploymentConfig Tags OnPremisesInstance ApplicationRevision DeploymentTarget cross-service
status note
ok
verified wire shapes (applicationId/applicationName/computePlatform/createTime), error codes, and persistence against aws-sdk-go-v2/service/codedeploy@v1.37.0 deserializers.go
status note
ok
full field-by-field mapping (blue/green, alarms, triggers, ECS, tag sets, load balancer info) verified against dgToOutput/dgInputFromWire
status note
ok
lifecycle is synchronous: CreateDeployment immediately sets status=Succeeded with completeTime = now+5s, which is a deliberate simplification (documented in Notes) rather than a bug
status note
ok
9 built-in AWS default configs correctly seeded and protected from deletion (DeploymentConfigInUseException)
status note
ok
ARN-based dispatch to application/deploymentgroup tag stores verified; on-premises instance tagging is a separate, also-correct path
status note
ok
registerTime/deregisterTime epoch fix + error-code fix (earlier pass); this pass decomposed matchesTagFilters (banned gocognit nolint) and added matchesTagSetGroups/matchesOnPremisesTargeting, reused by the new deployment-target computation
status note
ok
FIXED this pass: real applicationRevisions store.Table (composite key appName+canonical-revision-JSON, byApplication index), wired into backendSnapshot as a 'clean' table (no live tags.Tags field). RegisterApplicationRevision persists; CreateDeployment auto-registers an unseen revision and stamps FirstUsedTime/LastUsedTime/DeploymentGroups (touchApplicationRevisionForDeployment); DeleteApplication cascades deletes (deleteApplicationRevisions), UpdateApplication rename moves revisions to the new app name (renameApplicationRevisions) -- no ghost rows in either case
status note
ok
FIXED this pass: GetDeploymentTarget/ListDeploymentTargets/BatchGetDeploymentTargets/GetDeploymentInstance/ListDeploymentInstances/BatchGetDeploymentInstances all resolve from deploymentTargets(), a real (not fabricated) computation over the deployment's owning deployment group: matched on-premises instances (Server), one target per configured ECS service (ECS), or the single Lambda target (Lambda) real AWS always has exactly one of for that platform. Target Status is mapped from the deployment's own current Status via targetStatusForDeployment instead of a hardcoded literal. FIXED THIS PASS (previously the known limitation): Ec2TagFilters/Ec2TagSet now resolve against real services/ec2 instances via cross_service.go's lazy SetAppConfig wiring (same pattern as services/mgn), matching non-terminated EC2 instances whose TagsForResource tags satisfy the deployment group's targeting config (matchesEc2Targeting, mirroring matchesOnPremisesTargeting's Ec2TagSet-precedes-Ec2TagFilters rule). Falls back to zero EC2-side targets when the EC2 backend isn't wired (e.g. unit tests constructing InMemoryBackend directly) -- documented, not fabricated.
status note
wired
codedeploy now resolves the services/ec2 backend on demand via cross_service.go's siblingServices interface (GetEC2Handler), matched structurally against *CLI -- the same lazy SetAppConfig pattern services/mgn, services/grafana, and services/resiliencehub already use. *CLI already exposed GetEC2Handler() (cli.go:1134) for those services, so this needed zero cli.go changes: only provider.go gained one line (backend.SetAppConfig(ctx.Config)) and a new services/codedeploy/cross_service.go file.
gaps
deferred
StopDeployment accepts a deployment in any status (including already-terminal Succeeded/Failed/Stopped) and unconditionally overwrites it to Stopped. Real AWS's StopDeployment error set includes DeploymentAlreadyCompletedException (confirmed in StopDeployment's own deserializer switch, deserializers.go:5294) for exactly this case. NOT fixed this pass: unlike ContinueDeployment (which had no legitimate success path in this backend either way, since blue/green Ready state is never reached), StopDeployment's current behavior is relied on by existing tests and the general 'stop a just-created deployment' UX this mock supports; enforcing the precondition would make StopDeployment permanently non-functional given CreateDeployment's synchronous-completion design, which is the same underlying lifecycle gap noted below, not a narrow validation fix. (bd: unfiled)
CreateDeployment completing synchronously (status=Succeeded immediately, no Created/Queued/InProgress/Baking/Ready window) is a deliberate simplification, not a bug (see Notes) -- but it does mean no deployment in this backend can ever reach the real blue/green Ready wait-state, so ContinueDeployment's now-correct precondition check (see ops table) will always reject it. Modeling a genuine in-progress/waiting lifecycle so blue/green deployments can actually reach Ready is a larger rearchitecture, deliberately out of scope for this pass.
leaks
status note
clean
no goroutines/janitors in this service; Reset/Snapshot/Restore all close tags.Tags handles correctly on the three dirty tables (applications, deploymentGroups, onPremisesInstances). The new applicationRevisions table carries no live handles (no tags.Tags field), so it needs no Close() calls -- registered as a 'clean' store.Table on b.registry like deployments/deploymentConfigs, reset via registry.ResetAll().

Notes

  • Protocol: awsjson1.1, single POST endpoint, X-Amz-Target: CodeDeploy_20141006.<Op> dispatch via RouteMatcher/ExtractOperation in handler.go. Verified every op in GetSupportedOperations() has a dispatchTable() entry and is reachable — no stub registrations, no ops silently dropped.

  • Epoch-seconds timestamp bug (the big one this pass): every Timestamp shape in CodeDeploy's model (createTime, completeTime, registerTime, deregisterTime, and the createTimeRange.start/end request filter) is serialized by the real SDK as an epoch-seconds JSON number (smithytime.FormatEpochSeconds / parsed with smithytime.ParseEpochSeconds — confirmed by reading aws-sdk-go-v2/service/codedeploy@v1.37.0/deserializers.go and serializers.go directly). The handler was using time.Time.UnixMilli() (epoch milliseconds, int64) for every response timestamp and time.UnixMilli() to parse the request-side range filter — a 1000x wire-format mismatch in both directions. Fixed by switching every timestamp field to float64 and using pkgs/awstime.Epoch() for output / a small epochSecondsToTime helper (mirrors the secretsmanager package's time.Unix(0, int64(sec*float64(time.Second))) pattern) for input. Proven with a real-SDK-client round-trip test (handler_sdk_roundtrip_test.go) rather than just unit-level JSON assertions, since a scale-wrong-but-well-typed number silently decodes to a garbage time.Time instead of erroring — exactly the kind of bug unit tests miss (see parity-principles.md rule 3).

  • StopDeploymentOutput.status is a different enum than Deployment.status: real AWS's StopStatus enum is only ever "Pending" or "Succeeded" — it describes the outcome of the stop request itself (this backend performs it synchronously, so always "Succeeded"). The Deployment's own resulting lifecycle status ("Stopped") is a completely separate field returned by GetDeployment. The handler was previously reusing statusStopped for both, which is not a valid StopStatus value and would fail real SDK unmarshaling into the types.StopStatus type in strict validation paths. This is a "looks-right-but-is-wrong" trap: don't re-flag GetDeployment returning "Stopped" after a stop — that part was and remains correct.

  • ErrOnPremisesInstanceNotFound sentinel had the wrong error code baked in ("InstanceNameRequiredException", which is actually a different real AWS exception for a missing/empty instance name) and no errorMappings entry at all, so any not-found lookup on an on-premises instance (GetOnPremisesInstance, DeregisterOnPremisesInstance) fell through to the generic 500 ServiceException branch regardless of the sentinel's own code — the exact "missing errCodeLookup entry" bug class called out in parity-principles.md rule 2. Fixed the sentinel's code to InstanceDoesNotExistException (confirmed against types.InstanceDoesNotExistException in the real SDK) and added the errorMappings row.

  • SkipWaitTimeForInstanceTermination and PutLifecycleEventHookExecutionStatus skipped the deployment-existence check every sibling deployment-scoped op (GetDeploymentInstance, GetDeploymentTarget, ListDeploymentInstances, ListDeploymentTargets, ContinueDeployment, StopDeployment) performs via h.Backend.GetDeployment(...). Both previously returned 200 OK for a nonexistent deploymentId. Fixed to match the sibling pattern; real AWS returns DeploymentDoesNotExistException in both cases.

  • Deliberate simplification, not a bug: CreateDeployment sets Status: "Succeeded" and CompleteTime: now + 5s immediately at creation time rather than modeling a genuine Created → Queued → InProgress → Succeeded progression over time. ListDeployments' status filter and StopDeployment's transition to "Stopped" both work correctly against this synchronous model. Don't re-flag this as a "stuck deployment" bug — it's the opposite problem (instant-complete, not stuck), and every consumer-visible field derived from it (deploymentOverview, status filters) is internally consistent.

  • DeleteResourcesByExternalId empty-envelope return is correct, not a stub: this backend has no external-id-linked-resource tracking anywhere (nothing populates it), and the real AWS operation itself is a best-effort async cleanup with no required side effect visible to the caller synchronously. This is the "void-result op" pattern from parity-principles.md rule 4, not a disguised no-op.

  • ApplicationRevision family de-stubbed (this pass): RegisterApplicationRevision, GetApplicationRevision, ListApplicationRevisions, and BatchGetApplicationRevisions previously had no backing store at all — RegisterApplicationRevision validated the application existed and then discarded the revision, so GetApplicationRevision echoed the request straight back, ListApplicationRevisions always returned an empty list, and BatchGetApplicationRevisions echoed each requested revision with no real lookup. Added an applicationRevisions *store.Table[ApplicationRevision] keyed by appName + "\x00" + canonical-JSON(RevisionLocation) (applicationRevisionKey in store_setup.go — two RevisionLocation values with identical fields always produce the same key, matching how real CodeDeploy deduplicates revision registrations) with a byApplication index. Registered directly on b.registry as a "clean" table (no live *tags.Tags field, no DTO wrapper needed — see persistence.go's dirty-table doc comment). CreateDeployment now calls touchApplicationRevisionForDeployment, which auto-registers an unseen revision (real CodeDeploy auto-registers revisions supplied directly to CreateDeployment) and stamps FirstUsedTime/LastUsedTime/DeploymentGroups — the last of which also removes the deployment group from every other revision of the same application, since a deployment group targets exactly one revision at a time. DeleteApplication/UpdateApplication gained deleteApplicationRevisions/renameApplicationRevisions cascades so no ghost revision rows survive an app delete or outlive an app rename under the old name. Wire shapes (genericRevisionInfo/revisionInfo/revisionLocation field names, GenericRevisionInfo's registerTime/firstUsedTime/lastUsedTime as epoch-seconds float64) verified against aws-sdk-go-v2/service/codedeploy@v1.37.0/deserializers.go's awsAwsjson11_deserializeDocumentGenericRevisionInfo/...RevisionInfo and proven with a real SDK-client round trip (Test_SDKRoundTrip_ApplicationRevision_EpochSeconds). New sentinel ErrRevisionNotFoundRevisionDoesNotExistException, 404 (confirmed against types.RevisionDoesNotExistException).

  • Deployment-instance/target family de-stubbed (this pass): GetDeploymentInstance, GetDeploymentTarget, ListDeploymentInstances, ListDeploymentTargets, BatchGetDeploymentInstances, and BatchGetDeploymentTargets previously fabricated a Succeeded record for literally any requested ID (Get*/Batch*) or always returned an empty list (List*), regardless of whether that ID (or any target at all) actually existed. Added (b *InMemoryBackend) deploymentTargets(d *Deployment) []DeploymentTargetRecord (deployment_instances.go), computed on read from the deployment's owning deployment group's real configuration rather than persisted as a separate table (so it always reflects current on-premises instance/tag state, and needs no snapshot-version bump): for ComputePlatform == "Server", one instanceTarget per registered, non-deregistered on-premises instance whose tags satisfy the deployment group's OnPremisesInstanceTagFilters/OnPremisesTagSet (new matchesOnPremisesTargeting/matchesTagSetGroups helpers in on_premises_instances.go); for "ECS", one ecsTarget per ECSServices entry (clusterName/serviceName as the deterministic TargetID); for "Lambda", exactly one lambdaTarget keyed by the deployment ID itself (real CodeDeploy Lambda/ECS deployments always have exactly one and, respectively, len(ECSServices) targets — confirmed against types.DeploymentTargetListSizeExceededException's doc comment). Every target's Status is derived from the deployment's own current Status via targetStatusForDeployment (Succeeded/Failed/Skipped/InProgress) instead of a hardcoded literal that ignored what actually happened to the deployment (e.g. a stopped deployment's targets now correctly report Skipped, proven by TestDeploymentTargets_StatusTracksDeploymentStatus). Get*/ GetDeploymentTarget on an unresolvable ID now returns the new ErrDeploymentTargetNotFoundDeploymentTargetDoesNotExistException (404) sentinel instead of fabricating a match; Batch* silently omits unresolvable IDs, matching this codebase's established Batch* convention (BatchGetApplications etc.). DeploymentTarget union wire shape (deploymentTargetType PascalCase enum values InstanceTarget/ECSTarget/ LambdaTarget, nested instanceTarget/ecsTarget/lambdaTarget member field names, lastUpdatedAt as epoch-seconds float64) verified against awsAwsjson11_deserializeDocumentDeploymentTarget/...InstanceTarget/...ECSTarget/ ...LambdaTarget and proven with a real SDK-client round trip (Test_SDKRoundTrip_DeploymentTarget_EpochSeconds). KNOWN, DOCUMENTED LIMITATION: this backend has no live EC2 instance registry, so Ec2TagFilters/Ec2TagSet resolve zero targets — only the on-premises side of "Server" targeting is modeled (see gaps).

  • ContinueDeployment's real gap was narrower than "blue/green lifecycle" (this pass): READY_WAIT/TERMINATION_WAIT are types.DeploymentWaitType values (an input parameter on ContinueDeploymentInput), not types.DeploymentStatus values — a deployment's status is never "READY_WAIT"; the closest status is the separate "Ready" value. The actual fixable gap: ContinueDeployment accepted a deployment in any status and never read deploymentWaitType off the wire at all. Added the real precondition (types/errors.go:556-557 DeploymentIsNotInReadyStateException, :221 DeploymentAlreadyCompletedException) and wait-type enum validation (InvalidDeploymentWaitTypeException). CreateDeployment's synchronous completion means no deployment here ever reaches "Ready", so ContinueDeployment now always errors — correct given this backend's lifecycle, not a regression from a previously-working feature.

  • Ec2TagFilters/Ec2TagSet were stored but never evaluated, not dropped on input (this pass): CreateDeploymentGroup/UpdateDeploymentGroup always persisted them correctly (dg.Ec2TagFilters = input.Ec2TagFilters et al.) and GetDeploymentGroup echoed them back — the gap was entirely in deploymentTargets(), which had no EC2 instance data to match against. Wired services/codedeploy to services/ec2 via the same lazy SetAppConfig/siblingServices pattern services/mgn uses (cross_service.go); *CLI already exposed GetEC2Handler() for that pattern, so no cli.go changes were needed.

  • CreateDeployment's fileExistsBehavior was accepted and persisted with no enum check (this pass): any string round-tripped through GetDeployment/BatchGetDeployments. Real AWS defines InvalidFileExistsBehaviorException specifically for CreateDeployment (confirmed in its deserializer's error switch); added validation against DISALLOW/OVERWRITE/RETAIN.

  • Two banned //nolint:gocognit,cyclop,funlen removed via decomposition (this pass): dgToOutput/dgInputFromWire in handler_deployment_groups.go were single ~130-line functions doing wire-format conversion for every optional deployment-group sub-structure (load balancer info, blue/green config, alarms, auto-rollback, EC2/on-premises tag sets) inline. Split each into one dgXToOutput/dgXFromWire helper per sub-structure (7 helpers each direction); the top-level functions now just assemble the struct literal and loop over the flat slice fields, with zero behavior change (proven by the existing deployment_groups_test.go suite passing unmodified). One more banned //nolint:gocognit removed from matchesTagFilters in on_premises_instances.go by extracting the per-filter KEY_ONLY/VALUE_ONLY/default switch into matchesOneTagFilter (also fixed its comment, which incorrectly called the default case "EQUALS" — the real TagFilterType enum value is KEY_AND_VALUE, confirmed against types.TagFilterType in enums.go; the code was already correct, only the comment was wrong). All three were the full set of banned nolints flagged for this service — grep -rnE 'nolint:[a-z,]*(cyclop|gocyclo|gocognit|funlen)' services/codedeploy/ now returns empty.