Skip to content

Commit 32e9fe2

Browse files
authored
Merge pull request #934 from beekeep/feat_branch_protection_allow_unprotect
Add `allowed_to_unprotect` and `unprotect_access_level` fields to branch protection
2 parents 47d8c3f + de33ca9 commit 32e9fe2

File tree

5 files changed

+388
-107
lines changed

5 files changed

+388
-107
lines changed

docs/resources/branch_protection.md

Lines changed: 32 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,15 @@ page_title: "gitlab_branch_protection Resource - terraform-provider-gitlab"
44
subcategory: ""
55
description: |-
66
The gitlab_branch_protection resource allows to manage the lifecycle of a protected branch of a repository.
7-
~> The allowedtopush, allowedtomerge and codeownerapproval_required attributes require a GitLab Enterprise instance.
7+
~> The allowed_to_push, allowed_to_merge, allowed_to_unprotect, unprotect_access_level and code_owner_approval_required attributes require a GitLab Enterprise instance.
88
Upstream API: GitLab REST API docs https://docs.gitlab.com/ee/api/protected_branches.html
99
---
1010

1111
# gitlab_branch_protection (Resource)
1212

1313
The `gitlab_branch_protection` resource allows to manage the lifecycle of a protected branch of a repository.
1414

15-
~> The allowed_to_push, allowed_to_merge and code_owner_approval_required attributes require a GitLab Enterprise instance.
15+
~> The `allowed_to_push`, `allowed_to_merge`, `allowed_to_unprotect`, `unprotect_access_level` and `code_owner_approval_required` attributes require a GitLab Enterprise instance.
1616

1717
**Upstream API**: [GitLab REST API docs](https://docs.gitlab.com/ee/api/protected_branches.html)
1818

@@ -24,6 +24,7 @@ resource "gitlab_branch_protection" "BranchProtect" {
2424
branch = "BranchProtected"
2525
push_access_level = "developer"
2626
merge_access_level = "developer"
27+
unprotect_access_level = "developer"
2728
allow_force_push = true
2829
code_owner_approval_required = true
2930
allowed_to_push {
@@ -38,14 +39,21 @@ resource "gitlab_branch_protection" "BranchProtect" {
3839
allowed_to_merge {
3940
user_id = 37
4041
}
42+
allowed_to_unprotect {
43+
user_id = 15
44+
}
45+
allowed_to_unprotect {
46+
group_id = 42
47+
}
4148
}
4249
4350
# Example using dynamic block
4451
resource "gitlab_branch_protection" "main" {
45-
project = "12345"
46-
branch = "main"
47-
push_access_level = "maintainer"
48-
merge_access_level = "maintainer"
52+
project = "12345"
53+
branch = "main"
54+
push_access_level = "maintainer"
55+
merge_access_level = "maintainer"
56+
unprotect_access_level = "maintainer"
4957
5058
dynamic "allowed_to_push" {
5159
for_each = [50, 55, 60]
@@ -62,17 +70,19 @@ resource "gitlab_branch_protection" "main" {
6270
### Required
6371

6472
- **branch** (String) Name of the branch.
65-
- **merge_access_level** (String) Access levels allowed to merge. Valid values are: `no one`, `developer`, `maintainer`.
6673
- **project** (String) The id of the project.
67-
- **push_access_level** (String) Access levels allowed to push. Valid values are: `no one`, `developer`, `maintainer`.
6874

6975
### Optional
7076

7177
- **allow_force_push** (Boolean) Can be set to true to allow users with push access to force push.
7278
- **allowed_to_merge** (Block Set) Defines permissions for action. (see [below for nested schema](#nestedblock--allowed_to_merge))
7379
- **allowed_to_push** (Block Set) Defines permissions for action. (see [below for nested schema](#nestedblock--allowed_to_push))
80+
- **allowed_to_unprotect** (Block Set) Defines permissions for action. (see [below for nested schema](#nestedblock--allowed_to_unprotect))
7481
- **code_owner_approval_required** (Boolean) Can be set to true to require code owner approval before merging.
7582
- **id** (String) The ID of this resource.
83+
- **merge_access_level** (String) Access levels allowed to merge. Valid values are: `no one`, `developer`, `maintainer`.
84+
- **push_access_level** (String) Access levels allowed to push. Valid values are: `no one`, `developer`, `maintainer`.
85+
- **unprotect_access_level** (String) Access levels allowed to unprotect. Valid values are: `developer`, `maintainer`.
7686

7787
### Read-Only
7888

@@ -105,6 +115,20 @@ Read-Only:
105115
- **access_level** (String) Level of access.
106116
- **access_level_description** (String) Readable description of level of access.
107117

118+
119+
<a id="nestedblock--allowed_to_unprotect"></a>
120+
### Nested Schema for `allowed_to_unprotect`
121+
122+
Optional:
123+
124+
- **group_id** (Number) The ID of a GitLab group allowed to perform the relevant action. Mutually exclusive with `user_id`.
125+
- **user_id** (Number) The ID of a GitLab user allowed to perform the relevant action. Mutually exclusive with `group_id`.
126+
127+
Read-Only:
128+
129+
- **access_level** (String) Level of access.
130+
- **access_level_description** (String) Readable description of level of access.
131+
108132
## Import
109133

110134
Import is supported using the following syntax:

examples/resources/gitlab_branch_protection/resource.tf

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ resource "gitlab_branch_protection" "BranchProtect" {
33
branch = "BranchProtected"
44
push_access_level = "developer"
55
merge_access_level = "developer"
6+
unprotect_access_level = "developer"
67
allow_force_push = true
78
code_owner_approval_required = true
89
allowed_to_push {
@@ -17,14 +18,21 @@ resource "gitlab_branch_protection" "BranchProtect" {
1718
allowed_to_merge {
1819
user_id = 37
1920
}
21+
allowed_to_unprotect {
22+
user_id = 15
23+
}
24+
allowed_to_unprotect {
25+
group_id = 42
26+
}
2027
}
2128

2229
# Example using dynamic block
2330
resource "gitlab_branch_protection" "main" {
24-
project = "12345"
25-
branch = "main"
26-
push_access_level = "maintainer"
27-
merge_access_level = "maintainer"
31+
project = "12345"
32+
branch = "main"
33+
push_access_level = "maintainer"
34+
merge_access_level = "maintainer"
35+
unprotect_access_level = "maintainer"
2836

2937
dynamic "allowed_to_push" {
3038
for_each = [50, 55, 60]

internal/provider/access_level_helpers.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,12 @@ var validProtectedBranchTagAccessLevelNames = []string{
4949
"no one", "developer", "maintainer",
5050
}
5151

52+
// The only access levels allowed to be configured to unprotect a protected branch
53+
// The API states the others are either forbidden (via 403) or invalid
54+
var validProtectedBranchUnprotectAccessLevelNames = []string{
55+
"developer", "maintainer",
56+
}
57+
5258
var accessLevelNameToValue = map[string]gitlab.AccessLevelValue{
5359
"no one": gitlab.NoPermissions,
5460
"minimal": gitlab.MinimalAccessPermissions,

internal/provider/resource_gitlab_branch_protection.go

Lines changed: 31 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ var _ = registerResource("gitlab_branch_protection", func() *schema.Resource {
4343
return &schema.Resource{
4444
Description: `The ` + "`gitlab_branch_protection`" + ` resource allows to manage the lifecycle of a protected branch of a repository.
4545
46-
~> The allowed_to_push, allowed_to_merge and code_owner_approval_required attributes require a GitLab Enterprise instance.
46+
~> The ` + "`allowed_to_push`" + `, ` + "`allowed_to_merge`" + `, ` + "`allowed_to_unprotect`" + `, ` + "`unprotect_access_level`" + ` and ` + "`code_owner_approval_required`" + ` attributes require a GitLab Enterprise instance.
4747
4848
**Upstream API**: [GitLab REST API docs](https://docs.gitlab.com/ee/api/protected_branches.html)`,
4949

@@ -71,14 +71,24 @@ var _ = registerResource("gitlab_branch_protection", func() *schema.Resource {
7171
Description: fmt.Sprintf("Access levels allowed to merge. Valid values are: %s.", renderValueListForDocs(validProtectedBranchTagAccessLevelNames)),
7272
Type: schema.TypeString,
7373
ValidateDiagFunc: validation.ToDiagFunc(validation.StringInSlice(validProtectedBranchTagAccessLevelNames, false)),
74-
Required: true,
74+
Optional: true,
75+
Default: accessLevelValueToName[gitlab.MaintainerPermissions],
7576
ForceNew: true,
7677
},
7778
"push_access_level": {
7879
Description: fmt.Sprintf("Access levels allowed to push. Valid values are: %s.", renderValueListForDocs(validProtectedBranchTagAccessLevelNames)),
7980
Type: schema.TypeString,
8081
ValidateDiagFunc: validation.ToDiagFunc(validation.StringInSlice(validProtectedBranchTagAccessLevelNames, false)),
81-
Required: true,
82+
Optional: true,
83+
Default: accessLevelValueToName[gitlab.MaintainerPermissions],
84+
ForceNew: true,
85+
},
86+
"unprotect_access_level": {
87+
Description: fmt.Sprintf("Access levels allowed to unprotect. Valid values are: %s.", renderValueListForDocs(validProtectedBranchUnprotectAccessLevelNames)),
88+
Type: schema.TypeString,
89+
ValidateDiagFunc: validation.ToDiagFunc(validation.StringInSlice(validProtectedBranchUnprotectAccessLevelNames, false)),
90+
Optional: true,
91+
Default: accessLevelValueToName[gitlab.MaintainerPermissions],
8292
ForceNew: true,
8393
},
8494
"allow_force_push": {
@@ -88,8 +98,9 @@ var _ = registerResource("gitlab_branch_protection", func() *schema.Resource {
8898
Default: false,
8999
ForceNew: true,
90100
},
91-
"allowed_to_push": schemaAllowedTo(),
92-
"allowed_to_merge": schemaAllowedTo(),
101+
"allowed_to_push": schemaAllowedTo(),
102+
"allowed_to_merge": schemaAllowedTo(),
103+
"allowed_to_unprotect": schemaAllowedTo(),
93104
"code_owner_approval_required": {
94105
Description: "Can be set to true to require code owner approval before merging.",
95106
Type: schema.TypeBool,
@@ -124,19 +135,24 @@ func resourceGitlabBranchProtectionCreate(ctx context.Context, d *schema.Resourc
124135

125136
mergeAccessLevel := accessLevelNameToValue[d.Get("merge_access_level").(string)]
126137
pushAccessLevel := accessLevelNameToValue[d.Get("push_access_level").(string)]
138+
unprotectAccessLevel := accessLevelNameToValue[d.Get("unprotect_access_level").(string)]
139+
127140
allowForcePush := d.Get("allow_force_push").(bool)
128141
codeOwnerApprovalRequired := d.Get("code_owner_approval_required").(bool)
129142

130143
allowedToPush := expandBranchPermissionOptions(d.Get("allowed_to_push").(*schema.Set).List())
131144
allowedToMerge := expandBranchPermissionOptions(d.Get("allowed_to_merge").(*schema.Set).List())
145+
allowedToUnprotect := expandBranchPermissionOptions(d.Get("allowed_to_unprotect").(*schema.Set).List())
132146

133147
pb, _, err := client.ProtectedBranches.ProtectRepositoryBranches(project, &gitlab.ProtectRepositoryBranchesOptions{
134148
Name: &branch,
135149
PushAccessLevel: &pushAccessLevel,
136150
MergeAccessLevel: &mergeAccessLevel,
151+
UnprotectAccessLevel: &unprotectAccessLevel,
137152
AllowForcePush: &allowForcePush,
138153
AllowedToPush: &allowedToPush,
139154
AllowedToMerge: &allowedToMerge,
155+
AllowedToUnprotect: &allowedToUnprotect,
140156
CodeOwnerApprovalRequired: &codeOwnerApprovalRequired,
141157
}, gitlab.WithContext(ctx))
142158
if err != nil {
@@ -184,6 +200,12 @@ func resourceGitlabBranchProtectionRead(ctx context.Context, d *schema.ResourceD
184200
}
185201
}
186202

203+
if unprotectAccessLevels, err := firstValidAccessLevel(pb.UnprotectAccessLevels); err == nil {
204+
if err := d.Set("unprotect_access_level", accessLevelValueToName[*unprotectAccessLevels]); err != nil {
205+
return diag.Errorf("error setting unprotect_access_level: %v", err)
206+
}
207+
}
208+
187209
if err := d.Set("allow_force_push", pb.AllowForcePush); err != nil {
188210
return diag.Errorf("error setting allow_force_push: %v", err)
189211
}
@@ -195,6 +217,10 @@ func resourceGitlabBranchProtectionRead(ctx context.Context, d *schema.ResourceD
195217
return diag.Errorf("error setting allowed_to_merge: %v", err)
196218
}
197219

220+
if err := d.Set("allowed_to_unprotect", flattenNonZeroBranchAccessDescriptions(pb.UnprotectAccessLevels)); err != nil {
221+
return diag.Errorf("error setting allowed_to_unprotect: %v", err)
222+
}
223+
198224
if err := d.Set("code_owner_approval_required", pb.CodeOwnerApprovalRequired); err != nil {
199225
return diag.Errorf("error setting code_owner_approval_required: %v", err)
200226
}

0 commit comments

Comments
 (0)