Deployments stuck in unapprovable state if `Prevent outdated deployment jobs` is enabled and unified approval rules are used

Given that this problem will go away with https://docs.gitlab.com/ee/ci/environments/deployment_approvals.html#multiple-approval-rules, we probably won't fix

From the internal investigation.

Users can't approve the deployment and see this screen:

header
Screenshot_2023-03-10_at_12.35.03

To reproduce:

  1. enable deployment approvals feature
  2. create a few manual deployments(by updating code!)
  3. deploy the latest deployment
  4. try to approve/reject old deployment

Workaround

There are 2 workarounds:

  1. use multiple approval rules instead https://docs.gitlab.com/ee/ci/environments/deployment_approvals.html#multiple-approval-rules (they don't have this bug)
  2. temporarily disable https://docs.gitlab.com/ee/ci/environments/deployment_safety.html#prevent-outdated-deployment-jobs, reject these old jobs and enable it back

Debugging

DeploymentPolicy.new(user, deployment).debug(:approve_deployment)
- [0] prevent when all?(anonymous, ~public_project) ((@gudekb : Project/38987106))
- [14] prevent when all?(ip_enforcement_prevents_access, ~admin, ~auditor, strict_ip_enforcement)
- [28] prevent when all?(~public_project, ~internal_access, ~project_allowed_for_job_token)
- [267] enable when all?(needs_approval, ~has_approval_rules, can?(:update_deployment))
- [0] enable when all?(needs_approval, has_approval_rules, can?(:read_deployment), approval_rule_for_user)

DeploymentPolicy.new(user, deployment).debug(:update_deployment)
- [0] prevent when archived 
- [0] prevent when all?(anonymous, ~public_project) 
- [7] prevent when read_only 
- [14] prevent when ~can_update_deployment 
- [0] prevent when environments_disabled 
+ [0] enable when can?(:developer_access) 
- [0] prevent when builds_disabled 
- [0] prevent when repository_disabled 
- [0] prevent when all?(~public_project, ~internal_access, ~project_allowed_for_job_token)
- [0] prevent when all?(ip_enforcement_prevents_access, ~admin, ~auditor, strict_ip_enforcement)
+ [28] prevent when all?(has_deployable, ~can_retry_deployable)

Ci::BuildPolicy.new(user, deployment.deployable).debug(:update_build)
- [0] prevent when archived 
- [0] prevent when all?(anonymous, ~public_project) 
- [7] prevent when read_only 
+ [14] prevent when outdated_deployment 
  [14] prevent when all?(ip_enforcement_prevents_access, ~admin, ~auditor, strict_ip_enforcement) 
  [16] enable when reporter_has_access_to_protected_environment 
  [28] prevent when builds_disabled 
  [28] prevent when repository_disabled 
  [28] prevent when all?(~public_project, ~internal_access, ~project_allowed_for_job_token) 
  [85] prevent when all?(~can?(:jailbreak), any?(archived, protected_ref, protected_environment)) 
  [323] enable when can?(:developer_access) 
  [337] enable when all?(can?(:public_access), branch_allows_collaboration)
  [765] prevent when ~can?(:update_commit_status)

Problem

  1. Prevent execution of outdated manual deployment... (!97171 - merged) we introduced a mechanism to prevent outdated manual jobs from being run.
  2. we also made deployment approvals to be dependent on the update_deployment permission

So now when deployment requires approvals, but is outdated it's in this weird state:

  1. you can't approve or reject it, but it's shown as requesting approvals

Potential solution

  1. Instead of preventing old manual jobs from running, just cancel them after the newer deployment succeeds.
  2. Or simply deprecate unified approval rules(we already done this)

Doing the first may still make sense even without context of this bug 🤷