Resolve "Add Delete permanently action for marked-for-deletion Terraform states"
What does this MR do and why?
Adds a TerraformStateDeletePermanently GraphQL mutation. It lets a user permanently delete a Terraform state that is already marked for deletion, without waiting for the grace period.
This is part of the Terraform state delayed deletion epic: gitlab-org#23396
Resolves #621470 (closed)
The flow has two steps, mirroring project deletion: Remove state, then Delete permanently. The mutation rejects a state that has not been marked for deletion, so there is no single-step permanent delete.
Changes
- Mutation:
Mutations::Terraform::State::DeletePermanentlyfinds the state withauthorized_find!, calls the new service, and returns{ errors: response.errors }. It is registered inMutationTypeas an experiment for milestone 19.6 and uses thedelete_terraform_stategranular token permission. - Service:
Terraform::States::PermanentDestroyServicereturns an error when the user lacksadmin_terraform_stateor whendeleted_atis nil. Otherwise it enqueuesTerraform::States::DestroyWorkerwithpermanentset totrue. - Worker and destroy service:
DestroyWorker#performaccepts apermanentargument (defaultfalse) and passes it toDestroyService. Withpermanent: true,DestroyServiceskips the grace period check. Regular deletion is unchanged. - Audit event (EE):
EE::Terraform::States::PermanentDestroyServicerecords a newterraform_state_permanently_deletedevent, separate from the regular deletion event. - Docs: updates the GraphQL reference, the fine-grained token docs and the audit event types page.
- Authorization: use granular
delete_terraform_stateTerraform::States::PermanentDestroyServicecheckedadmin_terraform_state, whichGitlab/Authz/PermissionCheckflags as a coarseadmin_*permission. It now checks the granulardelete_terraform_statepermission, which already has a definition inconfig/authz/permissions/terraform_state/delete.ymland is used by the GraphQL mutations for the granular token check.app/services/terraform/states/permanent_destroy_service.rb:can?(:delete_terraform_state, state.project)replacescan?(:admin_terraform_state, state.project).app/policies/project_policy.rb:prevent :delete_terraform_statein theinfrastructure_disabled | terraform_state_disabledblock, so the permission is denied when Terraform state is disabled.spec/policies/project_policy_spec.rb: added:delete_terraform_stateto the maintainer permission lists, for both the enabled and the disabled Terraform state contexts.
Notes
- Behavior is unchanged: only Maintainers and above can permanently delete a state.
TriggerDestroyServiceandMutations::Terraform::State::Basestill useadmin_terraform_state. They are already in.rubocop_todo/gitlab/authz/permission_check.yml, so they are out of scope here.
Notes for reviewers
- Permission differs from the issue. The issue says to reuse
admin_terraform_state. This MR checksdelete_terraform_stateinstead, becauseGitlab/Authz/PermissionCheckflags newadmin_*checks as coarse. No access change:ProjectPolicyenablesdelete_terraform_statefor anyone withadmin_terraform_state(Maintainers and above), and prevents it when Terraform state or infrastructure is disabled. The permission was already defined inconfig/authz/permissions/terraform_state/delete.ymland is used for the granular token check on this mutation. No separate permission for permanent deletion is added. The two-step flow (Remove state, thenDelete permanently) is the guard. DestroyWorkertakespermanentas a positional argument, so the service callsperform_async(state.id, true). The issue showspermanent: true, but Sidekiq job arguments don't support keyword arguments.PermanentDestroyServicedoes not check theterraform_state_delayed_deletionfeature flag. Permanent deletion only skips a grace period check, which only applies when that flag is on.
References
- Work item: #621470 (closed)
- Epic: gitlab-org#23396
- Depends on (grace period check, closed): #621467 (closed)
- UI follow-up that calls this mutation: #622167
How to set up and validate locally
Run the specs:
bundle exec rspec \
spec/workers/terraform/states/destroy_worker_spec.rb \
spec/services/terraform/states/destroy_service_spec.rb \
spec/services/terraform/states/permanent_destroy_service_spec.rb \
spec/requests/api/graphql/mutations/terraform/state/delete_permanently_spec.rbTo try it in GraphiQL, enable terraform_state_delayed_deletion for a project and mark a state for deletion. Then run:
mutation {
terraformStateDeletePermanently(input: { id: "gid://gitlab/Terraform::State/<id>" }) {
errors
}
}- Maintainer: returns no errors, and the state is destroyed.
- Developer: returns a permission error.
- State not marked for deletion: returns an error.
MR acceptance checklist
Evaluate this MR against the MR acceptance checklist. It helps you analyze changes to reduce risks in quality, performance, reliability, security, and maintainability.
Related to #621470 (closed)