Implement deprovision secrets manager
Overview
Resolves #548419 (closed)
This MR implements the complete deprovision functionality for GitLab Project Secrets Manager, allowing full cleanup of project secrets infrastructure.
Architecture Overview
Design Decisions
Main Architectural Decision: Async Deprovision with Complete Resource Removal
The core decision is to implement deprovision as an asynchronous background process that completely removes all project-specific resources:
-
Deprovision flow:
active→deprovisioning→ (deleted) (via background job) -
Fresh setup flow: After deprovision, must use existing
provisioning→activeflow - Cleanup scope: Delete JWT roles, all policies, secrets engine, and database record
- Resource preservation: Keep auth mounts only (shared per namespace)
Key Design Principle: Complete Removal with State Tracking
We use the deprovisioning intermediate state to:
- User experience: Show users that operation is in progress
- Prevent duplicate operations: Users can't trigger multiple deprovision operations simultaneously
-
Consistent pattern: Matches existing
provisioningworkflow
Why Background Workers for Deprovision?
- Distributed system reliability: Multiple API calls with DB state tracking
- Auto-retry capability: All OpenBao methods are idempotent, enabling safe retries
- Performance: Non-blocking UI operations - user gets immediate feedback
- Scalability: Doesn't tie up web workers for external API calls
- Error handling: Proper logging and monitoring of failures
- Consistency: Same pattern as existing provision workflow
Service Separation
- ProvisionService: Full setup (engines, auth, policies, JWT roles)
- DeprovisionService: Complete removal (JWT roles, policies, secrets engine, DB record)
Wildcard Secret Handling
During deprovision, all secrets including wildcard secrets are completely removed along with their policies. No special handling is needed since everything is deleted.
GraphQL API
Deprovision Mutation
Request:
mutation {
projectSecretsManagerDeprovision(input: {
projectPath: "group/project"
}) {
projectSecretsManager {
status
project {
fullPath
}
}
errors
}
}
Response:
{
"data": {
"projectSecretsManagerDeprovision": {
"projectSecretsManager": {
"status": "DEPROVISIONING",
"project": {
"fullPath": "group/project"
}
},
"errors": []
}
}
}
Re-provision After Deprovision
Users must use the existing initialization mutation to set up a fresh secrets manager:
Request:
mutation {
projectSecretsManagerInitialize(input: {
projectPath: "group/project"
}) {
projectSecretsManager {
status
project {
fullPath
}
}
errors
}
}
Response (Fresh provisioning):
{
"data": {
"projectSecretsManagerInitialize": {
"projectSecretsManager": {
"status": "PROVISIONING",
"project": {
"fullPath": "group/project"
}
},
"errors": []
}
}
}