Skip to content

Implement API interface for Deployment Approval Rules

What does this MR do and why?

This MR adds the API interface for Multi Access Levels in Deployment Approval.

  • Users can protect an environment with rules.
    • The rules attributes are sanitized as well as deploy access levels to exclude inappropriate users and groups.
  • Users can see the current rules in Get a single protected environment API.
  • Users can see the current approval summary in Get a specific deployment API.
  • Add a limit to protected_environment_approval_rules.required_approvals for preventing users from setting excessive number.
  • Fix ProcessBuildService not to set ci_builds.when, when the deployment job is converted to manual job.

A few notes:

Manual QA

  • Result: PASSED
  • Date: Mon 11 Apr 2022 03:02:32 AM UTC

Example organization

XYZ org
  - qa-group
    - qa-user
  - security-group
    - security-user-1
    - security-user-2
  - operator-group
    - operator-user
  - awesome-project
    - project-maintainer

Configure approval rules

  1. Group administorator creates a new group-level protected environment to require approval process in deployments in subsequent projects:
XYZ org
  - production environment (group-level)
    - It allows `operator-group` to execute the deployment job.
    - It requires 2 approvals from security-group before the execution.
    - It requires 1 approval from qa-group before the execution.
API response
shinya@shinya-B550-VISION-D:~/workspace/thin-gdk/services/rails/src$ curl --header 'Content-Type: application/json' \
>      --request POST \
>      --data '{"name": "production", "deploy_access_levels": [{"group_id": 138}], "approval_rules": [{"group_id": 134}, {"group_id": 135, "required_approvals": 2}]}' --header "PRIVATE-TOKEN: LuRFCC6cvFbwf5vozEEm" \
>      "http://local.gitlab.test:8181/api/v4/groups/128/protected_environments" | jq
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100   569  100   419  100   150   1587    568 --:--:-- --:--:-- --:--:--  2155
{
  "name": "production",
  "deploy_access_levels": [
    {
      "access_level": 40,
      "access_level_description": "operator-group",
      "user_id": null,
      "group_id": 138
    }
  ],
  "required_approval_count": 0,
  "approval_rules": [
    {
      "user_id": null,
      "group_id": 134,
      "access_level": null,
      "access_level_description": "qa-group",
      "required_approvals": 1
    },
    {
      "user_id": null,
      "group_id": 135,
      "access_level": null,
      "access_level_description": "security-group",
      "required_approvals": 2
    }
  ]
}

Control deployments with approval rules

  1. project-maintainer creates a new pipeline that contains a deployment job to production environment. The deployment job should be blocked and waiting for approvals to be proceeded.

2022-04-11_12-24

  1. operator-user attempts to run a job, but fails because the deployment has not been approved yet. Checking the current approval status.
API response
shinya@shinya-B550-VISION-D:~/workspace/thin-gdk/services/rails/src$ curl --header "PRIVATE-TOKEN: LuRFCC6cvFbwf5vozEEm" "http://local.gitlab.test:8181/api/v4/projects/23/deployments/154" | jq '{ id: .id, iid: .iid, status: .status, approval_summary: .approval_summary }'
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100  2826    0  2826    0     0  13718      0 --:--:-- --:--:-- --:--:-- 13718
{
  "id": 154,
  "iid": 2,
  "status": "blocked",
  "approval_summary": {
    "rules": [
      {
        "user_id": null,
        "group_id": 134,
        "access_level": null,
        "access_level_description": "qa-group",
        "required_approvals": 1,
        "deployment_approvals": []
      },
      {
        "user_id": null,
        "group_id": 135,
        "access_level": null,
        "access_level_description": "security-group",
        "required_approvals": 2,
        "deployment_approvals": []
      }
    ]<details>
<summary>API response</summary>

shinya@shinya-B550-VISION-D:~/workspace/thin-gdk/services/rails/src$ curl --header "PRIVATE-TOKEN: LuRFCC6cvFbwf5vozEEm" "http://local.gitlab.test:8181/api/v4/projects/23/deployments/154" | jq '{ id: .id, iid: .iid, status: .status, approval_summary: .approval_summary }'
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100  5038    0  5038    0     0  16464      0 --:--:-- --:--:-- --:--:-- 16464
{
  "id": 154,
  "iid": 2,
  "status": "success",
  "approval_summary": {
    "rules": [
      {
        "user_id": null,
        "group_id": 134,
        "access_level": null,
        "access_level_description": "qa-group",
        "required_approvals": 1,
        "deployment_approvals": [
          {
            "user": {
              "id": 99,
              "username": "qa-user",
              "name": "qa user",
              "state": "active",
              "avatar_url": "https://www.gravatar.com/avatar/f9a6153de4da8b954839be477c02bf36?s=80&d=identicon",
              "web_url": "http://local.gitlab.test:8181/qa-user"
            },
            "status": "approved",
            "created_at": "2022-04-11T03:43:46.591Z",
            "comment": null
          }
        ]
      },
      {
        "user_id": null,
        "group_id": 135,
        "access_level": null,
        "access_level_description": "security-group",
        "required_approvals": 2,
        "deployment_approvals": [
          {
            "user": {
              "id": 100,
              "username": "security-user-1",
              "name": "security user-1",
              "state": "active",
              "avatar_url": "https://www.gravatar.com/avatar/e130fcd3a1681f41a3de69d10841afa9?s=80&d=identicon",
              "web_url": "http://local.gitlab.test:8181/security-user-1"
            },
            "status": "approved",
            "created_at": "2022-04-11T03:37:03.058Z",
            "comment": null
          },
          {
            "user": {
              "id": 101,
              "username": "security-user-2",
              "name": "security user-2",
              "state": "active",
              "avatar_url": "https://www.gravatar.com/avatar/c5d6b7c79bea7b0ac44d602bfd35acfa?s=80&d=identicon",
              "web_url": "http://local.gitlab.test:8181/security-user-2"
            },
            "status": "approved",
            "created_at": "2022-04-11T03:40:06.122Z",
            "comment": null
          }
        ]
      }
    ]
  }
}

</details>
  }
}
  1. security-user-1 approves the deployment. Checking the current approval status.
API response
shinya@shinya-B550-VISION-D:~/workspace/thin-gdk/services/rails/src$ curl --data "status=approved" --header "PRIVATE-TOKEN: WdY5Jy4o_zhC8Zd9EGBB" "http://local.gitlab.test:8181/api/v4/projects/23/deployments/154/approval"
{
  "user": {
    "id": 100,
    "username": "security-user-1",
    "name": "security user-1",
    "state": "active",
    "avatar_url": "https://www.gravatar.com/avatar/e130fcd3a1681f41a3de69d10841afa9?s=80&d=identicon",
    "web_url": "http://local.gitlab.test:8181/security-user-1"
  },
  "status": "approved",
  "created_at": "2022-04-11T03:37:03.058Z",
  "comment": null
}
shinya@shinya-B550-VISION-D:~/workspace/thin-gdk/services/rails/src$ curl --header "PRIVATE-TOKEN: LuRFCC6cvFbwf5vozEEm" "http://local.gitlab.test:8181/api/v4/projects/23/deployments/154" | jq '{ id: .id, iid: .iid, status: .status, approval_summary: .approval_summary }'
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100  3477    0  3477    0     0  18015      0 --:--:-- --:--:-- --:--:-- 18015
{
  "id": 154,
  "iid": 2,
  "status": "blocked",
  "approval_summary": {
    "rules": [
      {
        "user_id": null,
        "group_id": 134,
        "access_level": null,
        "access_level_description": "qa-group",
        "required_approvals": 1,
        "deployment_approvals": []
      },
      {
        "user_id": null,
        "group_id": 135,
        "access_level": null,
        "access_level_description": "security-group",
        "required_approvals": 2,
        "deployment_approvals": [
          {
            "user": {
              "id": 100,
              "username": "security-user-1",
              "name": "security user-1",
              "state": "active",
              "avatar_url": "https://www.gravatar.com/avatar/e130fcd3a1681f41a3de69d10841afa9?s=80&d=identicon",
              "web_url": "http://local.gitlab.test:8181/security-user-1"
            },
            "status": "approved",
            "created_at": "2022-04-11T03:37:03.058Z",
            "comment": null
          }
        ]
      }
    ]
  }
}
  1. security-user-2 approves the deployment. Checking the current approval status.
API response
shinya@shinya-B550-VISION-D:~/workspace/thin-gdk/services/rails/src$ curl --data "status=approved" --header "PRIVATE-TOKEN: VH4KA-vYU91YbYAyxPMV" "http://local.gitlab.test:8181/api/v4/projects/23/deployments/154/approval" | jq
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100   340  100   325  100    15   2056     94 --:--:-- --:--:-- --:--:--  2138
{
  "user": {
    "id": 101,
    "username": "security-user-2",
    "name": "security user-2",
    "state": "active",
    "avatar_url": "https://www.gravatar.com/avatar/c5d6b7c79bea7b0ac44d602bfd35acfa?s=80&d=identicon",
    "web_url": "http://local.gitlab.test:8181/security-user-2"
  },
  "status": "approved",
  "created_at": "2022-04-11T03:40:06.122Z",
  "comment": null
}
shinya@shinya-B550-VISION-D:~/workspace/thin-gdk/services/rails/src$ curl --header "PRIVATE-TOKEN: LuRFCC6cvFbwf5vozEEm" "http://local.gitlab.test:8181/api/v4/projects/23/deployments/154" | jq '{ id: .id, iid: .iid, status: .status, approval_summary: .approval_summary }'
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100  4129    0  4129    0     0  18940      0 --:--:-- --:--:-- --:--:-- 18940
{
  "id": 154,
  "iid": 2,
  "status": "blocked",
  "approval_summary": {
    "rules": [
      {
        "user_id": null,
        "group_id": 134,
        "access_level": null,
        "access_level_description": "qa-group",
        "required_approvals": 1,
        "deployment_approvals": []
      },
      {
        "user_id": null,
        "group_id": 135,
        "access_level": null,
        "access_level_description": "security-group",
        "required_approvals": 2,
        "deployment_approvals": [
          {
            "user": {
              "id": 100,
              "username": "security-user-1",
              "name": "security user-1",
              "state": "active",
              "avatar_url": "https://www.gravatar.com/avatar/e130fcd3a1681f41a3de69d10841afa9?s=80&d=identicon",
              "web_url": "http://local.gitlab.test:8181/security-user-1"
            },
            "status": "approved",
            "created_at": "2022-04-11T03:37:03.058Z",
            "comment": null
          },
          {
            "user": {
              "id": 101,
              "username": "security-user-2",
              "name": "security user-2",
              "state": "active",
              "avatar_url": "https://www.gravatar.com/avatar/c5d6b7c79bea7b0ac44d602bfd35acfa?s=80&d=identicon",
              "web_url": "http://local.gitlab.test:8181/security-user-2"
            },
            "status": "approved",
            "created_at": "2022-04-11T03:40:06.122Z",
            "comment": null
          }
        ]
      }
    ]
  }
}
  1. operator-user attempts to approves the deployment, but fails because the person is not allowed to approve.
API response
shinya@shinya-B550-VISION-D:~/workspace/thin-gdk/services/rails/src$ curl --data "status=approved" --header "PRIVATE-TOKEN: 7Pq7rF47rrMHwbuYZ_oc" "http://local.gitlab.test:8181/api/v4/projects/23/deployments/154/approval" | jq
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100   126  100   111  100    15    711     96 --:--:-- --:--:-- --:--:--   807
{
  "message": "You don't have permission to review this deployment. Contact the project or group owner for help."
}
  1. qa-user approves the deployment. Checking the current approval status.
API response
shinya@shinya-B550-VISION-D:~/workspace/thin-gdk/services/rails/src$ curl --data "status=approved" --header "PRIVATE-TOKEN: YZk57DBskhvNd5qm_QxC" "http://local.gitlab.test:8181/api/v4/projects/23/deployments/154/approval" | jq
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100   315  100   300  100    15   1630     81 --:--:-- --:--:-- --:--:--  1711
{
  "user": {
    "id": 99,
    "username": "qa-user",
    "name": "qa user",
    "state": "active",
    "avatar_url": "https://www.gravatar.com/avatar/f9a6153de4da8b954839be477c02bf36?s=80&d=identicon",
    "web_url": "http://local.gitlab.test:8181/qa-user"
  },
  "status": "approved",
  "created_at": "2022-04-11T03:43:46.591Z",
  "comment": null
}
shinya@shinya-B550-VISION-D:~/workspace/thin-gdk/services/rails/src$ curl --header "PRIVATE-TOKEN: LuRFCC6cvFbwf5vozEEm" "http://local.gitlab.test:8181/api/v4/projects/23/deployments/154" | jq '{ id: .id, iid: .iid, status: .status, approval_summary: .approval_summary }'
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100  4729    0  4729    0     0  20038      0 --:--:-- --:--:-- --:--:-- 20038
{
  "id": 154,
  "iid": 2,
  "status": "created",
  "approval_summary": {
    "rules": [
      {
        "user_id": null,
        "group_id": 134,
        "access_level": null,
        "access_level_description": "qa-group",
        "required_approvals": 1,
        "deployment_approvals": [
          {
            "user": {
              "id": 99,
              "username": "qa-user",
              "name": "qa user",
              "state": "active",
              "avatar_url": "https://www.gravatar.com/avatar/f9a6153de4da8b954839be477c02bf36?s=80&d=identicon",
              "web_url": "http://local.gitlab.test:8181/qa-user"
            },
            "status": "approved",
            "created_at": "2022-04-11T03:43:46.591Z",
            "comment": null
          }
        ]
      },
      {
        "user_id": null,
        "group_id": 135,
        "access_level": null,
        "access_level_description": "security-group",
        "required_approvals": 2,
        "deployment_approvals": [
          {
            "user": {
              "id": 100,
              "username": "security-user-1",
              "name": "security user-1",
              "state": "active",
              "avatar_url": "https://www.gravatar.com/avatar/e130fcd3a1681f41a3de69d10841afa9?s=80&d=identicon",
              "web_url": "http://local.gitlab.test:8181/security-user-1"
            },
            "status": "approved",
            "created_at": "2022-04-11T03:37:03.058Z",
            "comment": null
          },
          {
            "user": {
              "id": 101,
              "username": "security-user-2",
              "name": "security user-2",
              "state": "active",
              "avatar_url": "https://www.gravatar.com/avatar/c5d6b7c79bea7b0ac44d602bfd35acfa?s=80&d=identicon",
              "web_url": "http://local.gitlab.test:8181/security-user-2"
            },
            "status": "approved",
            "created_at": "2022-04-11T03:40:06.122Z",
            "comment": null
          }
        ]
      }
    ]
  }
}

NOTE: The status has transitioned to created from blocked.

  1. operator-user attempts to run a job and it succeeds. Checking the deployment status.

2022-04-11_12-56_1 2022-04-11_12-56

API response
shinya@shinya-B550-VISION-D:~/workspace/thin-gdk/services/rails/src$ curl --header "PRIVATE-TOKEN: LuRFCC6cvFbwf5vozEEm" "http://local.gitlab.test:8181/api/v4/projects/23/deployments/154" | jq '{ id: .id, iid: .iid, status: .status, approval_summary: .approval_summary }'
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100  5038    0  5038    0     0  16464      0 --:--:-- --:--:-- --:--:-- 16464
{
  "id": 154,
  "iid": 2,
  "status": "success",
  "approval_summary": {
    "rules": [
      {
        "user_id": null,
        "group_id": 134,
        "access_level": null,
        "access_level_description": "qa-group",
        "required_approvals": 1,
        "deployment_approvals": [
          {
            "user": {
              "id": 99,
              "username": "qa-user",
              "name": "qa user",
              "state": "active",
              "avatar_url": "https://www.gravatar.com/avatar/f9a6153de4da8b954839be477c02bf36?s=80&d=identicon",
              "web_url": "http://local.gitlab.test:8181/qa-user"
            },
            "status": "approved",
            "created_at": "2022-04-11T03:43:46.591Z",
            "comment": null
          }
        ]
      },
      {
        "user_id": null,
        "group_id": 135,
        "access_level": null,
        "access_level_description": "security-group",
        "required_approvals": 2,
        "deployment_approvals": [
          {
            "user": {
              "id": 100,
              "username": "security-user-1",
              "name": "security user-1",
              "state": "active",
              "avatar_url": "https://www.gravatar.com/avatar/e130fcd3a1681f41a3de69d10841afa9?s=80&d=identicon",
              "web_url": "http://local.gitlab.test:8181/security-user-1"
            },
            "status": "approved",
            "created_at": "2022-04-11T03:37:03.058Z",
            "comment": null
          },
          {
            "user": {
              "id": 101,
              "username": "security-user-2",
              "name": "security user-2",
              "state": "active",
              "avatar_url": "https://www.gravatar.com/avatar/c5d6b7c79bea7b0ac44d602bfd35acfa?s=80&d=identicon",
              "web_url": "http://local.gitlab.test:8181/security-user-2"
            },
            "status": "approved",
            "created_at": "2022-04-11T03:40:06.122Z",
            "comment": null
          }
        ]
      }
    ]
  }
}

How to set up and validate locally

  • Enable deployment_approval_rules feature flag.
  • See the Manual QA section above for the end-to-end setup process.

MR acceptance checklist

This checklist encourages us to confirm any changes have been analyzed to reduce risks in quality, performance, reliability, security, and maintainability.

Edited by Shinya Maeda

Merge request reports

Loading