Summary
The console-plugin's Developer Portal integration requires two enhancements to the APIProduct CRD and controller logic:
- Extend lifecycle states to support full API retirement workflow (Draft → Published → Deprecated → Retired)
- Add finalizer for clean cascade deletion of APIProducts with dependent APIKeys
These are blocking console-plugin issues #320 and #321.
1. Extend publishStatus Enum for Lifecycle States
Current State
The PublishStatus field only supports two states:
// api/v1alpha1/apiproduct_types.go:102-104
// +kubebuilder:validation:Enum=Draft;Published
// +kubebuilder:default=Draft
PublishStatus string `json:"publishStatus"`
Required Change
Extend to support four lifecycle states:
// +kubebuilder:validation:Enum=Draft;Published;Deprecated;Retired
// +kubebuilder:default=Draft
PublishStatus string `json:"publishStatus"`
State Definitions
- Draft: Not visible in API catalog, cannot request keys
- Published: Visible in catalog, accepting new requests
- Deprecated: Visible with warning badge, new APIKey requests blocked, existing approved keys continue working (grace period)
- Retired: Not visible in catalog, all existing APIKeys transitioned to Rejected phase, Secrets deleted
Controller Logic Required
When APIProduct transitions to publishStatus: Retired:
- Query all APIKey resources where
spec.apiProductRef.name matches the APIProduct
- For each APIKey:
- If
status.phase: Approved → update to Rejected, delete the Secret
- If
status.phase: Pending → update to Rejected
- Set
status.rejectionReason: "Parent API Product was retired on <date>"
- Update APIProduct status conditions to reflect retirement
Validation: Block new APIKey creation if publishStatus: Retired (validating webhook)
2. Finalizer for Clean Cascade Deletion
Current State
- Controller has RBAC for finalizers:
apiproducts/finalizers,verbs=update
- Controller sets
OwnerReference on APIKeys (apikey_controller.go:130-138)
- Missing: Finalizer add/remove logic in reconcile loop
Required Implementation
Add finalizer devportal.kuadrant.io/apiproduct-finalizer to ensure clean deletion order:
Deletion Flow:
1. User deletes APIProduct → resource gets deletionTimestamp
2. Controller detects deletionTimestamp, starts cleanup:
- Delete all child APIKeys (Pending, Approved, Rejected)
- Wait for Kubernetes garbage collection to delete Secrets
3. Once all APIKeys are gone → Controller removes finalizer
4. APIProduct is finally deleted
Why this matters:
- Prevents orphaned APIKeys and Secrets
- Ensures all API access is revoked before APIProduct removal
- Provides predictable deletion behavior for console-plugin UI
Console-Plugin UX Dependency
The console-plugin delete modal (issue #321) needs to query dependent APIKeys before deletion:
// Query dependent APIKeys
const dependentKeys = apiKeys.filter(
key => key.spec.apiProductRef.name === apiProduct.metadata.name
);
// Show counts in delete confirmation modal
- 47 approved API keys will be revoked immediately
- 12 pending requests will be cancelled
- 8 rejected requests will be deleted
The finalizer ensures this cascade deletion completes cleanly.
Acceptance Criteria
References
Notes
Un-retirement Behavior
If an APIProduct is un-retired (transitioned from Retired back to Published):
- Previously revoked APIKeys remain in
Rejected state (Secrets were deleted)
- Users must create new APIKey requests to regain access
- This is by design to prevent accidental re-activation of old credentials
Testing Recommendations
- Create APIProduct with 10+ dependent APIKeys (mix of approved/pending/rejected)
- Transition to
Retired → verify all approved keys become rejected, Secrets deleted
- Delete APIProduct → verify finalizer prevents deletion until all APIKeys cleaned up
- Attempt to create new APIKey for retired product → verify validation blocks it
Summary
The console-plugin's Developer Portal integration requires two enhancements to the APIProduct CRD and controller logic:
These are blocking console-plugin issues #320 and #321.
1. Extend
publishStatusEnum for Lifecycle StatesCurrent State
The
PublishStatusfield only supports two states:Required Change
Extend to support four lifecycle states:
State Definitions
Controller Logic Required
When APIProduct transitions to
publishStatus: Retired:spec.apiProductRef.namematches the APIProductstatus.phase: Approved→ update toRejected, delete the Secretstatus.phase: Pending→ update toRejectedstatus.rejectionReason: "Parent API Product was retired on <date>"Validation: Block new APIKey creation if
publishStatus: Retired(validating webhook)2. Finalizer for Clean Cascade Deletion
Current State
apiproducts/finalizers,verbs=updateOwnerReferenceon APIKeys (apikey_controller.go:130-138)Required Implementation
Add finalizer
devportal.kuadrant.io/apiproduct-finalizerto ensure clean deletion order:Deletion Flow:
Why this matters:
Console-Plugin UX Dependency
The console-plugin delete modal (issue #321) needs to query dependent APIKeys before deletion:
The finalizer ensures this cascade deletion completes cleanly.
Acceptance Criteria
PublishStatusenum extended to includeDeprecatedandRetiredRetiredstate by transitioning dependent APIKeys to RejectedReferences
Notes
Un-retirement Behavior
If an APIProduct is un-retired (transitioned from
Retiredback toPublished):Rejectedstate (Secrets were deleted)Testing Recommendations
Retired→ verify all approved keys become rejected, Secrets deleted