From 82ef7ff43b26e598aa48bb0162c50950495728c7 Mon Sep 17 00:00:00 2001 From: Timo Furrer Date: Wed, 10 Aug 2022 15:41:20 +0200 Subject: [PATCH] resource/gitlab_group_membership: Support removal options This change will support the two options when removing a user from a group using `gitlab_group_membership`. These are `skip_subresources_on_destroy` and `unassign_issuables_on_destroy`. Both default to `false` and the change is backwards compatible. Closes: #1194 --- docs/resources/group_membership.md | 2 + internal/provider/helper_test.go | 24 +++++++++++ .../resource_gitlab_group_membership.go | 21 +++++++++- .../resource_gitlab_group_membership_test.go | 41 +++++++++++++++++++ 4 files changed, 86 insertions(+), 2 deletions(-) diff --git a/docs/resources/group_membership.md b/docs/resources/group_membership.md index 04aebeeb4..537c4359d 100644 --- a/docs/resources/group_membership.md +++ b/docs/resources/group_membership.md @@ -39,6 +39,8 @@ resource "gitlab_group_membership" "test" { ### Optional - `expires_at` (String) Expiration date for the group membership. Format: `YYYY-MM-DD` +- `skip_subresources_on_destroy` (Boolean) Whether the deletion of direct memberships of the removed member in subgroups and projects should be skipped. Only used during a destroy. +- `unassign_issuables_on_destroy` (Boolean) Whether the removed member should be unassigned from any issues or merge requests inside a given group or project. Only used during a destroy. ### Read-Only diff --git a/internal/provider/helper_test.go b/internal/provider/helper_test.go index 46cc1052d..5d524c987 100644 --- a/internal/provider/helper_test.go +++ b/internal/provider/helper_test.go @@ -225,6 +225,30 @@ func testAccCreateGroups(t *testing.T, n int) []*gitlab.Group { return groups } +// testAccCreateSubGroups is a test helper for creating a specified number of subgroups. +func testAccCreateSubGroups(t *testing.T, parentGroup *gitlab.Group, n int) []*gitlab.Group { + t.Helper() + + groups := make([]*gitlab.Group, n) + + for i := range groups { + var err error + name := acctest.RandomWithPrefix("acctest-group") + groups[i], _, err = testGitlabClient.Groups.CreateGroup(&gitlab.CreateGroupOptions{ + Name: gitlab.String(name), + Path: gitlab.String(name), + // So that acceptance tests can be run in a gitlab organization with no billing. + Visibility: gitlab.Visibility(gitlab.PublicVisibility), + ParentID: gitlab.Int(parentGroup.ID), + }) + if err != nil { + t.Fatalf("could not create test subgroup: %v", err) + } + } + + return groups +} + // testAccCreateBranches is a test helper for creating a specified number of branches. // It assumes the project will be destroyed at the end of the test and will not cleanup created branches. func testAccCreateBranches(t *testing.T, project *gitlab.Project, n int) []*gitlab.Branch { diff --git a/internal/provider/resource_gitlab_group_membership.go b/internal/provider/resource_gitlab_group_membership.go index 54e3db24c..ce4b400af 100644 --- a/internal/provider/resource_gitlab_group_membership.go +++ b/internal/provider/resource_gitlab_group_membership.go @@ -54,6 +54,18 @@ var _ = registerResource("gitlab_group_membership", func() *schema.Resource { ValidateFunc: validateDateFunc, Optional: true, }, + "skip_subresources_on_destroy": { + Description: "Whether the deletion of direct memberships of the removed member in subgroups and projects should be skipped. Only used during a destroy.", + Type: schema.TypeBool, + Optional: true, + Default: false, + }, + "unassign_issuables_on_destroy": { + Description: "Whether the removed member should be unassigned from any issues or merge requests inside a given group or project. Only used during a destroy.", + Type: schema.TypeBool, + Optional: true, + Default: false, + }, }, } }) @@ -149,9 +161,14 @@ func resourceGitlabGroupMembershipDelete(ctx context.Context, d *schema.Resource return diag.FromErr(err) } - log.Printf("[DEBUG] Delete gitlab group membership %v for %s", userId, groupId) + options := gitlab.RemoveGroupMemberOptions{ + SkipSubresources: gitlab.Bool(d.Get("skip_subresources_on_destroy").(bool)), + UnassignIssuables: gitlab.Bool(d.Get("unassign_issuables_on_destroy").(bool)), + } + + log.Printf("[DEBUG] Delete gitlab group membership %v for %s with options: %+v", userId, groupId, options) - _, err = client.GroupMembers.RemoveGroupMember(groupId, userId, &gitlab.RemoveGroupMemberOptions{}, gitlab.WithContext(ctx)) + _, err = client.GroupMembers.RemoveGroupMember(groupId, userId, &options, gitlab.WithContext(ctx)) if err != nil { return diag.FromErr(err) } diff --git a/internal/provider/resource_gitlab_group_membership_test.go b/internal/provider/resource_gitlab_group_membership_test.go index 76f28a1ea..a76e41e16 100644 --- a/internal/provider/resource_gitlab_group_membership_test.go +++ b/internal/provider/resource_gitlab_group_membership_test.go @@ -51,6 +51,47 @@ func TestAccGitlabGroupMembership_basic(t *testing.T) { }) } +func TestAccGitlabGroupMembership_skipRemoveFromSubgroup(t *testing.T) { + testUser := testAccCreateUsers(t, 1)[0] + testGroup := testAccCreateGroups(t, 1)[0] + testSubgroup := testAccCreateSubGroups(t, testGroup, 1)[0] + + resource.ParallelTest(t, resource.TestCase{ + ProviderFactories: providerFactories, + CheckDestroy: testAccCheckGitlabGroupMembershipDestroy, + Steps: []resource.TestStep{ + // Add user to main and subgroup individually + { + Config: fmt.Sprintf(` + resource "gitlab_group_membership" "main_group" { + group_id = "%d" + user_id = %d + access_level = "developer" + skip_subresources_on_destroy = true + } + + resource "gitlab_group_membership" "sub_group" { + group_id = "%d" + user_id = %d + access_level = "maintainer" + } + `, testGroup.ID, testUser.ID, testSubgroup.ID, testUser.ID), + }, + // Remove user from main group without removing from subgroup + { + Config: fmt.Sprintf(` + resource "gitlab_group_membership" "sub_group" { + group_id = "%d" + user_id = %d + access_level = "maintainer" + } + `, testSubgroup.ID, testUser.ID), + Check: testAccCheckGitlabGroupMembershipExists("gitlab_group_membership.sub_group", &gitlab.GroupMember{}), + }, + }, + }) +} + func testAccCheckGitlabGroupMembershipExists(n string, membership *gitlab.GroupMember) resource.TestCheckFunc { return func(s *terraform.State) error { rs, ok := s.RootModule().Resources[n]