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 setci_builds.when
, when the deployment job is converted to manual job.
A few notes:
- Previous MRs are !83495 (merged), !82800 (merged) and !84248 (merged).
- This feature is behind the feature flag
deployment_approval_rules
, which is disabled by default. - You can find more feature concept of Protected Environment in the documentation.
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
- 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
-
project-maintainer
creates a new pipeline that contains a deployment job toproduction
environment. The deployment job should be blocked and waiting for approvals to be proceeded.
-
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>
}
}
-
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
}
]
}
]
}
}
-
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
}
]
}
]
}
}
-
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."
}
-
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
.
-
operator-user
attempts to run a job and it succeeds. Checking the deployment 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 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.
-
I have evaluated the MR acceptance checklist for this MR.
Edited by Shinya Maeda