Skip to content

Commit 0ff40d5

Browse files
authored
Merge pull request #356 from husunal/feature/gitlab_project_approval_configuration
Add Project-level MR approvals
2 parents a2c27f5 + a8ad4ad commit 0ff40d5

File tree

5 files changed

+389
-0
lines changed

5 files changed

+389
-0
lines changed

gitlab/provider.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@ func Provider() terraform.ResourceProvider {
9191
"gitlab_group_cluster": resourceGitlabGroupCluster(),
9292
"gitlab_group_ldap_link": resourceGitlabGroupLdapLink(),
9393
"gitlab_project_mirror": resourceGitlabProjectMirror(),
94+
"gitlab_project_level_mr_approvals": resourceGitlabProjectLevelMRApprovals(),
9495
},
9596

9697
ConfigureFunc: providerConfigure,
Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
package gitlab
2+
3+
import (
4+
"fmt"
5+
"log"
6+
7+
"github.com/hashicorp/terraform-plugin-sdk/helper/schema"
8+
gitlab "github.com/xanzy/go-gitlab"
9+
)
10+
11+
func resourceGitlabProjectLevelMRApprovals() *schema.Resource {
12+
return &schema.Resource{
13+
Create: resourceGitlabProjectLevelMRApprovalsCreate,
14+
Read: resourceGitlabProjectLevelMRApprovalsRead,
15+
Update: resourceGitlabProjectLevelMRApprovalsUpdate,
16+
Delete: resourceGitlabProjectLevelMRApprovalsDelete,
17+
Importer: &schema.ResourceImporter{
18+
State: schema.ImportStatePassthrough,
19+
},
20+
Schema: map[string]*schema.Schema{
21+
"project_id": {
22+
Type: schema.TypeInt,
23+
ForceNew: true,
24+
Required: true,
25+
},
26+
"reset_approvals_on_push": {
27+
Type: schema.TypeBool,
28+
Optional: true,
29+
},
30+
"disable_overriding_approvers_per_merge_request": {
31+
Type: schema.TypeBool,
32+
Optional: true,
33+
},
34+
"merge_requests_author_approval": {
35+
Type: schema.TypeBool,
36+
Optional: true,
37+
},
38+
"merge_requests_disable_committers_approval": {
39+
Type: schema.TypeBool,
40+
Optional: true,
41+
},
42+
},
43+
}
44+
}
45+
46+
func resourceGitlabProjectLevelMRApprovalsCreate(d *schema.ResourceData, meta interface{}) error {
47+
client := meta.(*gitlab.Client)
48+
49+
projectId := d.Get("project_id").(string)
50+
51+
options := &gitlab.ChangeApprovalConfigurationOptions{
52+
ResetApprovalsOnPush: gitlab.Bool(d.Get("reset_approvals_on_push").(bool)),
53+
DisableOverridingApproversPerMergeRequest: gitlab.Bool(d.Get("disable_overriding_approvers_per_merge_request").(bool)),
54+
MergeRequestsAuthorApproval: gitlab.Bool(d.Get("merge_requests_author_approval").(bool)),
55+
MergeRequestsDisableCommittersApproval: gitlab.Bool(d.Get("merge_requests_disable_committers_approval").(bool)),
56+
}
57+
58+
log.Printf("[DEBUG] Creating new MR approval configuration for project %s:", projectId)
59+
60+
if _, _, err := client.Projects.ChangeApprovalConfiguration(projectId, options); err != nil {
61+
return fmt.Errorf("couldn't create approval configuration: %w", err)
62+
}
63+
64+
d.SetId(projectId)
65+
return resourceGitlabProjectLevelMRApprovalsRead(d, meta)
66+
}
67+
68+
func resourceGitlabProjectLevelMRApprovalsRead(d *schema.ResourceData, meta interface{}) error {
69+
client := meta.(*gitlab.Client)
70+
71+
projectId := d.Id()
72+
log.Printf("[DEBUG] Reading gitlab approval configuration for project %s", projectId)
73+
74+
approvalConfig, _, err := client.Projects.GetApprovalConfiguration(projectId)
75+
if err != nil {
76+
return fmt.Errorf("couldn't read approval configuration: %w", err)
77+
}
78+
79+
d.Set("project_id", projectId)
80+
d.Set("reset_approvals_on_push", approvalConfig.ResetApprovalsOnPush)
81+
d.Set("disable_overriding_approvers_per_merge_request", approvalConfig.DisableOverridingApproversPerMergeRequest)
82+
d.Set("merge_requests_author_approval", approvalConfig.MergeRequestsAuthorApproval)
83+
d.Set("merge_requests_disable_committers_approval", approvalConfig.MergeRequestsDisableCommittersApproval)
84+
85+
return nil
86+
}
87+
88+
func resourceGitlabProjectLevelMRApprovalsUpdate(d *schema.ResourceData, meta interface{}) error {
89+
client := meta.(*gitlab.Client)
90+
options := &gitlab.ChangeApprovalConfigurationOptions{}
91+
92+
projectId := d.Id()
93+
log.Printf("[DEBUG] Updating approval configuration for project %s:", projectId)
94+
95+
if d.HasChange("reset_approvals_on_push") {
96+
options.ResetApprovalsOnPush = gitlab.Bool(d.Get("reset_approvals_on_push").(bool))
97+
}
98+
if d.HasChange("disable_overriding_approvers_per_merge_request") {
99+
options.DisableOverridingApproversPerMergeRequest = gitlab.Bool(d.Get("disable_overriding_approvers_per_merge_request").(bool))
100+
}
101+
if d.HasChange("merge_requests_author_approval") {
102+
options.MergeRequestsAuthorApproval = gitlab.Bool(d.Get("merge_requests_author_approval").(bool))
103+
}
104+
if d.HasChange("merge_requests_disable_committers_approval") {
105+
options.MergeRequestsDisableCommittersApproval = gitlab.Bool(d.Get("merge_requests_disable_committers_approval").(bool))
106+
}
107+
108+
if _, _, err := client.Projects.ChangeApprovalConfiguration(d.Id(), options); err != nil {
109+
return fmt.Errorf("couldn't update approval configuration: %w", err)
110+
}
111+
112+
return resourceGitlabProjectLevelMRApprovalsRead(d, meta)
113+
}
114+
115+
func resourceGitlabProjectLevelMRApprovalsDelete(d *schema.ResourceData, meta interface{}) error {
116+
client := meta.(*gitlab.Client)
117+
projectId := d.Id()
118+
119+
options := &gitlab.ChangeApprovalConfigurationOptions{
120+
ResetApprovalsOnPush: gitlab.Bool(true),
121+
DisableOverridingApproversPerMergeRequest: gitlab.Bool(false),
122+
MergeRequestsAuthorApproval: gitlab.Bool(false),
123+
MergeRequestsDisableCommittersApproval: gitlab.Bool(false),
124+
}
125+
126+
log.Printf("[DEBUG] Resetting approval configuration for project %s:", projectId)
127+
128+
if _, _, err := client.Projects.ChangeApprovalConfiguration(projectId, options); err != nil {
129+
return fmt.Errorf("couldn't reset approval configuration: %w", err)
130+
}
131+
132+
return nil
133+
}
Lines changed: 195 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,195 @@
1+
package gitlab
2+
3+
import (
4+
"fmt"
5+
"testing"
6+
7+
"github.com/hashicorp/terraform-plugin-sdk/helper/acctest"
8+
"github.com/hashicorp/terraform-plugin-sdk/helper/resource"
9+
"github.com/hashicorp/terraform-plugin-sdk/terraform"
10+
gitlab "github.com/xanzy/go-gitlab"
11+
)
12+
13+
func TestAccGitlabProjectLevelMRApprovals_basic(t *testing.T) {
14+
15+
var projectApprovals gitlab.ProjectApprovals
16+
rInt := acctest.RandInt()
17+
18+
resource.Test(t, resource.TestCase{
19+
PreCheck: func() { testAccPreCheck(t) },
20+
Providers: testAccProviders,
21+
CheckDestroy: testAccCheckGitlabProjectLevelMRApprovalsDestroy,
22+
Steps: []resource.TestStep{
23+
{
24+
SkipFunc: isRunningInCE,
25+
Config: testAccGitlabProjectLevelMRApprovalsConfig(rInt),
26+
Check: resource.ComposeTestCheckFunc(
27+
testAccCheckGitlabProjectLevelMRApprovalsExists("gitlab_project_level_mr_approvals.foo", &projectApprovals),
28+
testAccCheckGitlabProjectLevelMRApprovalsAttributes(&projectApprovals, &testAccGitlabProjectLevelMRApprovalsExpectedAttributes{
29+
resetApprovalsOnPush: true,
30+
disableOverridingApproversPerMergeRequest: true,
31+
mergeRequestsAuthorApproval: true,
32+
mergeRequestsDisableCommittersApproval: true,
33+
}),
34+
),
35+
},
36+
{
37+
SkipFunc: isRunningInCE,
38+
Config: testAccGitlabProjectLevelMRApprovalsUpdateConfig(rInt),
39+
Check: resource.ComposeTestCheckFunc(
40+
testAccCheckGitlabProjectLevelMRApprovalsExists("gitlab_project_level_mr_approvals.foo", &projectApprovals),
41+
testAccCheckGitlabProjectLevelMRApprovalsAttributes(&projectApprovals, &testAccGitlabProjectLevelMRApprovalsExpectedAttributes{
42+
resetApprovalsOnPush: false,
43+
disableOverridingApproversPerMergeRequest: false,
44+
mergeRequestsAuthorApproval: false,
45+
mergeRequestsDisableCommittersApproval: false,
46+
}),
47+
),
48+
},
49+
{
50+
SkipFunc: isRunningInCE,
51+
Config: testAccGitlabProjectLevelMRApprovalsConfig(rInt),
52+
Check: resource.ComposeTestCheckFunc(
53+
testAccCheckGitlabProjectLevelMRApprovalsExists("gitlab_project_level_mr_approvals.foo", &projectApprovals),
54+
testAccCheckGitlabProjectLevelMRApprovalsAttributes(&projectApprovals, &testAccGitlabProjectLevelMRApprovalsExpectedAttributes{
55+
resetApprovalsOnPush: true,
56+
disableOverridingApproversPerMergeRequest: true,
57+
mergeRequestsAuthorApproval: true,
58+
mergeRequestsDisableCommittersApproval: true,
59+
}),
60+
),
61+
},
62+
},
63+
})
64+
}
65+
66+
func TestAccGitlabProjectLevelMRApprovals_import(t *testing.T) {
67+
resourceName := "gitlab_project_level_mr_approvals.foo"
68+
rInt := acctest.RandInt()
69+
70+
resource.Test(t, resource.TestCase{
71+
PreCheck: func() { testAccPreCheck(t) },
72+
Providers: testAccProviders,
73+
CheckDestroy: testAccCheckGitlabProjectLevelMRApprovalsDestroy,
74+
Steps: []resource.TestStep{
75+
{
76+
SkipFunc: isRunningInCE,
77+
Config: testAccGitlabProjectLevelMRApprovalsConfig(rInt),
78+
},
79+
{
80+
SkipFunc: isRunningInCE,
81+
ResourceName: resourceName,
82+
ImportState: true,
83+
ImportStateVerify: true,
84+
},
85+
},
86+
})
87+
}
88+
89+
type testAccGitlabProjectLevelMRApprovalsExpectedAttributes struct {
90+
resetApprovalsOnPush bool
91+
disableOverridingApproversPerMergeRequest bool
92+
mergeRequestsAuthorApproval bool
93+
mergeRequestsDisableCommittersApproval bool
94+
}
95+
96+
func testAccCheckGitlabProjectLevelMRApprovalsAttributes(projectApprovals *gitlab.ProjectApprovals, want *testAccGitlabProjectLevelMRApprovalsExpectedAttributes) resource.TestCheckFunc {
97+
return func(s *terraform.State) error {
98+
if projectApprovals.ResetApprovalsOnPush != want.resetApprovalsOnPush {
99+
return fmt.Errorf("got reset_approvals_on_push %t; want %t", projectApprovals.ResetApprovalsOnPush, want.resetApprovalsOnPush)
100+
}
101+
if projectApprovals.DisableOverridingApproversPerMergeRequest != want.disableOverridingApproversPerMergeRequest {
102+
return fmt.Errorf("got disable_overriding_approvers_per_merge_request %t; want %t", projectApprovals.DisableOverridingApproversPerMergeRequest, want.disableOverridingApproversPerMergeRequest)
103+
}
104+
if projectApprovals.MergeRequestsAuthorApproval != want.mergeRequestsAuthorApproval {
105+
return fmt.Errorf("got merge_requests_author_approval %t; want %t", projectApprovals.MergeRequestsAuthorApproval, want.mergeRequestsAuthorApproval)
106+
}
107+
if projectApprovals.MergeRequestsDisableCommittersApproval != want.mergeRequestsDisableCommittersApproval {
108+
return fmt.Errorf("got merge_requests_disable_committers_approval %t; want %t", projectApprovals.MergeRequestsDisableCommittersApproval, want.mergeRequestsDisableCommittersApproval)
109+
}
110+
return nil
111+
}
112+
}
113+
114+
func testAccCheckGitlabProjectLevelMRApprovalsDestroy(s *terraform.State) error {
115+
conn := testAccProvider.Meta().(*gitlab.Client)
116+
117+
for _, rs := range s.RootModule().Resources {
118+
if rs.Type != "gitlab_project" {
119+
continue
120+
}
121+
122+
gotRepo, resp, err := conn.Projects.GetProject(rs.Primary.ID, nil)
123+
if err == nil {
124+
if gotRepo != nil && fmt.Sprintf("%d", gotRepo.ID) == rs.Primary.ID {
125+
if gotRepo.MarkedForDeletionAt == nil {
126+
return fmt.Errorf("Repository still exists.")
127+
}
128+
}
129+
}
130+
if resp.StatusCode != 404 {
131+
return err
132+
}
133+
return nil
134+
}
135+
return nil
136+
}
137+
138+
func testAccCheckGitlabProjectLevelMRApprovalsExists(n string, projectApprovals *gitlab.ProjectApprovals) resource.TestCheckFunc {
139+
return func(s *terraform.State) error {
140+
rs, ok := s.RootModule().Resources[n]
141+
if !ok {
142+
return fmt.Errorf("Not Found: %s", n)
143+
}
144+
145+
projectId := rs.Primary.ID
146+
if projectId == "" {
147+
return fmt.Errorf("No project ID is set")
148+
}
149+
conn := testAccProvider.Meta().(*gitlab.Client)
150+
151+
gotApprovalConfig, _, err := conn.Projects.GetApprovalConfiguration(projectId)
152+
if err != nil {
153+
return err
154+
}
155+
156+
*projectApprovals = *gotApprovalConfig
157+
return nil
158+
}
159+
}
160+
161+
func testAccGitlabProjectLevelMRApprovalsConfig(rInt int) string {
162+
return fmt.Sprintf(`
163+
resource "gitlab_project" "foo" {
164+
name = "foo-%d"
165+
description = "Terraform acceptance tests"
166+
visibility_level = "public"
167+
}
168+
169+
resource "gitlab_project_level_mr_approvals" "foo" {
170+
project_id = gitlab_project.foo.id
171+
reset_approvals_on_push = true
172+
disable_overriding_approvers_per_merge_request = true
173+
merge_requests_author_approval = true
174+
merge_requests_disable_committers_approval = true
175+
}
176+
`, rInt)
177+
}
178+
179+
func testAccGitlabProjectLevelMRApprovalsUpdateConfig(rInt int) string {
180+
return fmt.Sprintf(`
181+
resource "gitlab_project" "foo" {
182+
name = "foo-%d"
183+
description = "Terraform acceptance tests"
184+
visibility_level = "public"
185+
}
186+
187+
resource "gitlab_project_level_mr_approvals" "foo" {
188+
project_id = gitlab_project.foo.id
189+
reset_approvals_on_push = false
190+
disable_overriding_approvers_per_merge_request = false
191+
merge_requests_author_approval = false
192+
merge_requests_disable_committers_approval = false
193+
}
194+
`, rInt)
195+
}
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
---
2+
layout: "gitlab"
3+
page_title: "GitLab: gitlab_project_level_mr_approvals"
4+
sidebar_current: "docs-gitlab-resource-project-level-mr-approvals"
5+
description: |-
6+
Configures project-level MR approvals.
7+
---
8+
9+
# gitlab\_project\_level\_mr\_approvals
10+
11+
This resource allows you to configure project-level MR approvals. for your GitLab projects.
12+
For further information on merge request approvals, consult the [GitLab API
13+
documentation](https://docs.gitlab.com/ee/api/merge_request_approvals.html#project-level-mr-approvals).
14+
15+
16+
## Example Usage
17+
18+
```hcl
19+
resource "gitlab_project" "foo" {
20+
name = "Example"
21+
description = "My example project"
22+
}
23+
24+
resource "gitlab_project_level_mr_approvals" "foo" {
25+
project_id = gitlab_project.foo.id
26+
reset_approvals_on_push = true
27+
disable_overriding_approvers_per_merge_request = false
28+
merge_requests_author_approval = false
29+
merge_requests_disable_committers_approval = true
30+
}
31+
```
32+
33+
## Argument Reference
34+
35+
The following arguments are supported:
36+
37+
* `project_id` - (Required) The ID of the project to change MR approval configuration.
38+
39+
* `reset_approvals_on_push` - (Optional) Set to `true` if you want to remove all approvals in a merge request when new commits are pushed to its source branch. Default is `true`.
40+
41+
* `disable_overriding_approvers_per_merge_request` - (Optional) By default, users are able to edit the approval rules in merge requests. If set to true,
42+
the approval rules for all new merge requests will be determined by the default approval rules. Default is `false`.
43+
44+
* `merge_requests_author_approval` - (Optional) Set to `true` if you want to allow merge request authors to self-approve merge requests. Authors
45+
also need to be included in the approvers list in order to be able to approve their merge request. Default is `false`.
46+
47+
* `merge_requests_disable_committers_approval` - (Optional) Set to `true` if you want to prevent approval of merge requests by merge request committers. Default is `false`.
48+
49+
## Importing approval configuration
50+
51+
You can import an approval configuration state using `terraform import <resource> <project_id>`.
52+
53+
For example:
54+
55+
```bash
56+
$ terraform import gitlab_project_level_mr_approvals.foo 53
57+
```

0 commit comments

Comments
 (0)