Skip to content

Commit a5a62fb

Browse files
petrmvalaPetr Vála
andauthored
resource/gitlab_project_approval_rule: Add rule_type attribute (#913)
* feat: Add rule_type parameter to merge_request_approval_rule This is working in the API, and is necessary in order to enable Eligible Approvers. * feat: Fix rule_type validation Based on MR suggestions. * fix: Add forgotten imports * feat: Move validRuleTypeValues into RegisterResource Co-authored-by: Petr Vála <[email protected]>
1 parent 278f2b3 commit a5a62fb

File tree

6 files changed

+116
-7
lines changed

6 files changed

+116
-7
lines changed

docs/resources/project_approval_rule.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,14 @@ resource "gitlab_project_approval_rule" "example-three" {
5454
approvals_required = 3
5555
user_ids = [for user in data.gitlab_user.users : user.id]
5656
}
57+
58+
# Example using `approval_rule`
59+
resource "gitlab_branch_protection" "any-approver" {
60+
project = 5
61+
name = "Any name"
62+
rule_type = "any_approver"
63+
approvals_required = 1
64+
}
5765
```
5866

5967
<!-- schema generated by tfplugindocs -->
@@ -70,6 +78,7 @@ resource "gitlab_project_approval_rule" "example-three" {
7078
- **group_ids** (Set of Number) A list of group IDs whose members can approve of the merge request.
7179
- **id** (String) The ID of this resource.
7280
- **protected_branch_ids** (Set of Number) A list of protected branch IDs (not branch names) for which the rule applies.
81+
- **rule_type** (String) String, defaults to 'regular'. The type of rule. `any_approver` is a pre-configured default rule with `approvals_required` at `0`. Valid values are `regular`, `any_approver`.
7382
- **user_ids** (Set of Number) A list of specific User IDs to add to the list of approvers.
7483

7584
## Import

examples/resources/gitlab_project_approval_rule/resource.tf

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,3 +36,11 @@ resource "gitlab_project_approval_rule" "example-three" {
3636
approvals_required = 3
3737
user_ids = [for user in data.gitlab_user.users : user.id]
3838
}
39+
40+
# Example using `approval_rule`
41+
resource "gitlab_branch_protection" "any-approver" {
42+
project = 5
43+
name = "Any name"
44+
rule_type = "any_approver"
45+
approvals_required = 1
46+
}

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ require (
1111
github.com/hashicorp/terraform-plugin-sdk/v2 v2.10.1
1212
github.com/mitchellh/hashstructure v1.1.0
1313
github.com/onsi/gomega v1.18.1
14-
github.com/xanzy/go-gitlab v0.55.1
14+
github.com/xanzy/go-gitlab v0.56.0
1515
golang.org/x/crypto v0.0.0-20220214200702-86341886e292 // indirect
1616
google.golang.org/api v0.34.0 // indirect
1717
)

go.sum

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -344,8 +344,8 @@ github.com/vmihailenco/msgpack v4.0.4+incompatible h1:dSLoQfGFAo3F6OoNhwUmLwVgaU
344344
github.com/vmihailenco/msgpack v4.0.4+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk=
345345
github.com/vmihailenco/msgpack/v4 v4.3.12/go.mod h1:gborTTJjAo/GWTqqRjrLCn9pgNN+NXzzngzBKDPIqw4=
346346
github.com/vmihailenco/tagparser v0.1.1/go.mod h1:OeAg3pn3UbLjkWt+rN9oFYB6u/cQgqMEUPoW2WPyhdI=
347-
github.com/xanzy/go-gitlab v0.55.1 h1:IgX/DS9buV0AUz8fuJPQkdl0fQGfBiAsAHxpun8sNhg=
348-
github.com/xanzy/go-gitlab v0.55.1/go.mod h1:F0QEXwmqiBUxCgJm8fE9S+1veX4XC9Z4cfaAbqwk4YM=
347+
github.com/xanzy/go-gitlab v0.56.0 h1:/QHBvk3IKVNwvXB/UOWVb5J6VCN6r2bg9/WxjUbFY/0=
348+
github.com/xanzy/go-gitlab v0.56.0/go.mod h1:F0QEXwmqiBUxCgJm8fE9S+1veX4XC9Z4cfaAbqwk4YM=
349349
github.com/xanzy/ssh-agent v0.3.0 h1:wUMzuKtKilRgBAD1sUb8gOwwRr2FGoBVumcjoOACClI=
350350
github.com/xanzy/ssh-agent v0.3.0/go.mod h1:3s9xbODqPuuhK9JV1R321M/FlMZSBvE5aY6eAcqrDh0=
351351
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=

internal/provider/resource_gitlab_project_approval_rule.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,24 @@ package provider
33
import (
44
"context"
55
"errors"
6+
"fmt"
67
"log"
78
"strconv"
89

910
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
1011
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
12+
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
1113
gitlab "github.com/xanzy/go-gitlab"
1214
)
1315

1416
// https://docs.gitlab.com/ee/api/merge_request_approvals.html#create-project-level-rule
1517
var errApprovalRuleNotFound = errors.New("approval rule not found")
1618

1719
var _ = registerResource("gitlab_project_approval_rule", func() *schema.Resource {
20+
var validRuleTypeValues = []string{
21+
"regular",
22+
"any_approver",
23+
}
1824
return &schema.Resource{
1925
Description: "This resource allows you to create and manage multiple approval rules for your GitLab projects. For further information on approval rules, consult the [gitlab documentation](https://docs.gitlab.com/ee/api/merge_request_approvals.html#project-level-mr-approvals).\n\n" +
2026
"-> This feature requires GitLab Premium.",
@@ -43,6 +49,13 @@ var _ = registerResource("gitlab_project_approval_rule", func() *schema.Resource
4349
Type: schema.TypeInt,
4450
Required: true,
4551
},
52+
"rule_type": {
53+
Description: fmt.Sprintf("String, defaults to 'regular'. The type of rule. `any_approver` is a pre-configured default rule with `approvals_required` at `0`. Valid values are %s.", renderValueListForDocs(validRuleTypeValues)),
54+
Type: schema.TypeString,
55+
Optional: true,
56+
Default: "regular",
57+
ValidateDiagFunc: validation.ToDiagFunc(validation.StringInSlice(validRuleTypeValues, false)),
58+
},
4659
"user_ids": {
4760
Description: "A list of specific User IDs to add to the list of approvers.",
4861
Type: schema.TypeSet,
@@ -77,6 +90,10 @@ func resourceGitlabProjectApprovalRuleCreate(ctx context.Context, d *schema.Reso
7790
ProtectedBranchIDs: expandProtectedBranchIDs(d.Get("protected_branch_ids")),
7891
}
7992

93+
if v, ok := d.GetOk("rule_type"); ok {
94+
options.RuleType = gitlab.String(v.(string))
95+
}
96+
8097
project := d.Get("project").(string)
8198

8299
log.Printf("[DEBUG] Project %s create gitlab project-level rule %+v", project, options)
@@ -115,6 +132,7 @@ func resourceGitlabProjectApprovalRuleRead(ctx context.Context, d *schema.Resour
115132

116133
d.Set("name", rule.Name)
117134
d.Set("approvals_required", rule.ApprovalsRequired)
135+
d.Set("rule_type", rule.RuleType)
118136

119137
if err := d.Set("group_ids", flattenApprovalRuleGroupIDs(rule.Groups)); err != nil {
120138
return diag.FromErr(err)

internal/provider/resource_gitlab_project_approval_rule_test.go

Lines changed: 78 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -43,12 +43,13 @@ func TestAccGitLabProjectApprovalRule_basic(t *testing.T) {
4343
Steps: []resource.TestStep{
4444
// Create rule
4545
{
46-
Config: testAccGitlabProjectApprovalRuleConfig(project.ID, 3, projectUsers[0].ID, groups[0].ID, branches[0].ID),
46+
Config: testAccGitlabProjectApprovalRuleConfig(project.ID, 3, "regular", projectUsers[0].ID, groups[0].ID, branches[0].ID),
4747
Check: resource.ComposeTestCheckFunc(
4848
testAccCheckGitlabProjectApprovalRuleExists("gitlab_project_approval_rule.foo", &projectApprovalRule),
4949
testAccCheckGitlabProjectApprovalRuleAttributes(&projectApprovalRule, &testAccGitlabProjectApprovalRuleExpectedAttributes{
5050
Name: "foo",
5151
ApprovalsRequired: 3,
52+
RuleType: "regular",
5253
EligibleApproverIDs: []int{currentUser.ID, projectUsers[0].ID, group0Users[0].ID},
5354
GroupIDs: []int{groups[0].ID},
5455
ProtectedBranchIDs: []int{branches[0].ID},
@@ -57,12 +58,82 @@ func TestAccGitLabProjectApprovalRule_basic(t *testing.T) {
5758
},
5859
// Update rule
5960
{
60-
Config: testAccGitlabProjectApprovalRuleConfig(project.ID, 2, projectUsers[1].ID, groups[1].ID, branches[1].ID),
61+
Config: testAccGitlabProjectApprovalRuleConfig(project.ID, 2, "regular", projectUsers[1].ID, groups[1].ID, branches[1].ID),
6162
Check: resource.ComposeTestCheckFunc(
6263
testAccCheckGitlabProjectApprovalRuleExists("gitlab_project_approval_rule.foo", &projectApprovalRule),
6364
testAccCheckGitlabProjectApprovalRuleAttributes(&projectApprovalRule, &testAccGitlabProjectApprovalRuleExpectedAttributes{
6465
Name: "foo",
6566
ApprovalsRequired: 2,
67+
RuleType: "regular",
68+
EligibleApproverIDs: []int{currentUser.ID, projectUsers[1].ID, group1Users[0].ID},
69+
GroupIDs: []int{groups[1].ID},
70+
ProtectedBranchIDs: []int{branches[1].ID},
71+
}),
72+
),
73+
},
74+
// Verify import
75+
{
76+
ResourceName: "gitlab_project_approval_rule.foo",
77+
ImportState: true,
78+
ImportStateVerify: true,
79+
},
80+
},
81+
})
82+
}
83+
84+
func TestAccGitLabProjectApprovalRule_AnyApprover(t *testing.T) {
85+
// Set up project, groups, users, and branches to use in the test.
86+
87+
testAccCheck(t)
88+
testAccCheckEE(t)
89+
90+
// Need to get the current user (usually the admin) because they are automatically added as group members, and we
91+
// will need the user ID for our assertions later.
92+
currentUser := testAccCurrentUser(t)
93+
94+
project := testAccCreateProject(t)
95+
projectUsers := testAccCreateUsers(t, 2)
96+
branches := testAccCreateProtectedBranches(t, project, 2)
97+
groups := testAccCreateGroups(t, 2)
98+
group0Users := testAccCreateUsers(t, 1)
99+
group1Users := testAccCreateUsers(t, 1)
100+
101+
testAccAddProjectMembers(t, project.ID, projectUsers) // Users must belong to the project for rules to work.
102+
testAccAddGroupMembers(t, groups[0].ID, group0Users)
103+
testAccAddGroupMembers(t, groups[1].ID, group1Users)
104+
105+
// Terraform test starts here.
106+
107+
var projectApprovalRule gitlab.ProjectApprovalRule
108+
109+
resource.Test(t, resource.TestCase{
110+
ProviderFactories: providerFactories,
111+
CheckDestroy: testAccCheckGitlabProjectApprovalRuleDestroy(project.ID),
112+
Steps: []resource.TestStep{
113+
// Create rule
114+
{
115+
Config: testAccGitlabProjectApprovalRuleConfig(project.ID, 3, "any_approver", projectUsers[0].ID, groups[0].ID, branches[0].ID),
116+
Check: resource.ComposeTestCheckFunc(
117+
testAccCheckGitlabProjectApprovalRuleExists("gitlab_project_approval_rule.foo", &projectApprovalRule),
118+
testAccCheckGitlabProjectApprovalRuleAttributes(&projectApprovalRule, &testAccGitlabProjectApprovalRuleExpectedAttributes{
119+
Name: "foo",
120+
ApprovalsRequired: 3,
121+
RuleType: "any_approver",
122+
EligibleApproverIDs: []int{currentUser.ID, projectUsers[0].ID, group0Users[0].ID},
123+
GroupIDs: []int{groups[0].ID},
124+
ProtectedBranchIDs: []int{branches[0].ID},
125+
}),
126+
),
127+
},
128+
// Update rule
129+
{
130+
Config: testAccGitlabProjectApprovalRuleConfig(project.ID, 2, "any_approver", projectUsers[1].ID, groups[1].ID, branches[1].ID),
131+
Check: resource.ComposeTestCheckFunc(
132+
testAccCheckGitlabProjectApprovalRuleExists("gitlab_project_approval_rule.foo", &projectApprovalRule),
133+
testAccCheckGitlabProjectApprovalRuleAttributes(&projectApprovalRule, &testAccGitlabProjectApprovalRuleExpectedAttributes{
134+
Name: "foo",
135+
ApprovalsRequired: 2,
136+
RuleType: "any_approver",
66137
EligibleApproverIDs: []int{currentUser.ID, projectUsers[1].ID, group1Users[0].ID},
67138
GroupIDs: []int{groups[1].ID},
68139
ProtectedBranchIDs: []int{branches[1].ID},
@@ -82,6 +153,7 @@ func TestAccGitLabProjectApprovalRule_basic(t *testing.T) {
82153
type testAccGitlabProjectApprovalRuleExpectedAttributes struct {
83154
Name string
84155
ApprovalsRequired int
156+
RuleType string
85157
EligibleApproverIDs []int
86158
GroupIDs []int
87159
ProtectedBranchIDs []int
@@ -92,6 +164,7 @@ func testAccCheckGitlabProjectApprovalRuleAttributes(got *gitlab.ProjectApproval
92164
return InterceptGomegaFailure(func() {
93165
Expect(got.Name).To(Equal(want.Name), "name")
94166
Expect(got.ApprovalsRequired).To(Equal(want.ApprovalsRequired), "approvals_required")
167+
Expect(got.RuleType).To(Equal(want.RuleType), "rule_type")
95168

96169
var approverIDs []int
97170
for _, approver := range got.EligibleApprovers {
@@ -114,16 +187,17 @@ func testAccCheckGitlabProjectApprovalRuleAttributes(got *gitlab.ProjectApproval
114187
}
115188
}
116189

117-
func testAccGitlabProjectApprovalRuleConfig(project, approvals, userID, groupID, protectedBranchID int) string {
190+
func testAccGitlabProjectApprovalRuleConfig(project int, approvals int, rule_type string, userID int, groupID int, protectedBranchID int) string {
118191
return fmt.Sprintf(`
119192
resource "gitlab_project_approval_rule" "foo" {
120193
project = %d
121194
name = "foo"
122195
approvals_required = %d
196+
rule_type = %s
123197
user_ids = [%d]
124198
group_ids = [%d]
125199
protected_branch_ids = [%d]
126-
}`, project, approvals, userID, groupID, protectedBranchID)
200+
}`, project, approvals, rule_type, userID, groupID, protectedBranchID)
127201
}
128202

129203
func testAccCheckGitlabProjectApprovalRuleExists(n string, projectApprovalRule *gitlab.ProjectApprovalRule) resource.TestCheckFunc {

0 commit comments

Comments
 (0)