Skip to content

Commit a628ae8

Browse files
Merge pull request #511 from gitlabhq/group_share_group_resource
Add resource for group shared_with_groups
2 parents 2f1dbef + b60653b commit a628ae8

File tree

4 files changed

+342
-0
lines changed

4 files changed

+342
-0
lines changed

docs/resources/group_share_group.md

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
# gitlab\_group\_share\_group
2+
3+
This resource allows you to share a group with another group
4+
5+
## Example Usage
6+
7+
```hcl
8+
resource "gitlab_group_share_group" "test" {
9+
group_id = gitlab_group.foo.id
10+
share_group_id = gitlab_group.bar.id
11+
group_access = "guest"
12+
expires_at = "2099-01-01"
13+
}
14+
```
15+
16+
## Argument Reference
17+
18+
The following arguments are supported:
19+
20+
* `group_id` - (Required) The id of the main group.
21+
22+
* `share_group_id` - (Required) The id of an additional group which will be shared with the main group.
23+
24+
* `group_access` - (Required) One of five levels of access to the group.
25+
26+
* `expires_at` - (Optional) Share expiration date. Format: `YYYY-MM-DD`
27+
28+
## Import
29+
30+
GitLab group shares can be imported using an id made up of `mainGroupId:shareGroupId`, e.g.
31+
32+
```
33+
$ terraform import gitlab_group_share_group.test 12345:1337
34+
```

gitlab/provider.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ func Provider() terraform.ResourceProvider {
9595
"gitlab_project_level_mr_approvals": resourceGitlabProjectLevelMRApprovals(),
9696
"gitlab_project_approval_rule": resourceGitlabProjectApprovalRule(),
9797
"gitlab_instance_variable": resourceGitlabInstanceVariable(),
98+
"gitlab_group_share_group": resourceGitlabGroupShareGroup(),
9899
},
99100
}
100101

Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
package gitlab
2+
3+
import (
4+
"fmt"
5+
"log"
6+
"net/http"
7+
"strconv"
8+
9+
"github.com/hashicorp/terraform-plugin-sdk/helper/schema"
10+
gitlab "github.com/xanzy/go-gitlab"
11+
)
12+
13+
// https://docs.gitlab.com/ee/api/groups.html#share-groups-with-groups
14+
15+
func resourceGitlabGroupShareGroup() *schema.Resource {
16+
acceptedAccessLevels := make([]string, 0, len(accessLevelID))
17+
for k := range accessLevelID {
18+
acceptedAccessLevels = append(acceptedAccessLevels, k)
19+
}
20+
21+
return &schema.Resource{
22+
Create: resourceGitlabGroupShareGroupCreate,
23+
Read: resourceGitlabGroupShareGroupRead,
24+
Delete: resourceGitlabGroupShareGroupDelete,
25+
Importer: &schema.ResourceImporter{
26+
State: schema.ImportStatePassthrough,
27+
},
28+
Schema: map[string]*schema.Schema{
29+
"group_id": {
30+
Type: schema.TypeString,
31+
ForceNew: true,
32+
Required: true,
33+
},
34+
"share_group_id": {
35+
Type: schema.TypeInt,
36+
ForceNew: true,
37+
Required: true,
38+
},
39+
"group_access": {
40+
Type: schema.TypeString,
41+
ValidateFunc: validateValueFunc(acceptedAccessLevels),
42+
ForceNew: true,
43+
Required: true,
44+
},
45+
"expires_at": {
46+
Type: schema.TypeString, // Format YYYY-MM-DD
47+
ValidateFunc: validateDateFunc,
48+
ForceNew: true,
49+
Optional: true,
50+
},
51+
},
52+
}
53+
}
54+
55+
func resourceGitlabGroupShareGroupCreate(d *schema.ResourceData, meta interface{}) error {
56+
groupId := d.Get("group_id").(string)
57+
shareGroupId := d.Get("share_group_id").(int)
58+
groupAccess := accessLevelID[d.Get("group_access").(string)]
59+
options := &gitlab.ShareWithGroupOptions{
60+
GroupID: &shareGroupId,
61+
GroupAccess: &groupAccess,
62+
ExpiresAt: gitlab.String(d.Get("expires_at").(string)),
63+
}
64+
65+
client := meta.(*gitlab.Client)
66+
log.Printf("[DEBUG] create gitlab group share for %d in %s", shareGroupId, groupId)
67+
68+
_, _, err := client.GroupMembers.ShareWithGroup(groupId, options)
69+
if err != nil {
70+
return err
71+
}
72+
73+
shareGroupIdString := strconv.Itoa(shareGroupId)
74+
d.SetId(buildTwoPartID(&groupId, &shareGroupIdString))
75+
76+
return resourceGitlabGroupShareGroupRead(d, meta)
77+
}
78+
79+
func resourceGitlabGroupShareGroupRead(d *schema.ResourceData, meta interface{}) error {
80+
client := meta.(*gitlab.Client)
81+
id := d.Id()
82+
log.Printf("[DEBUG] read gitlab shared groups %s", id)
83+
84+
groupId, sharedGroupId, err := groupIdsFromId(id)
85+
if err != nil {
86+
return err
87+
}
88+
89+
// Query main group
90+
group, resp, err := client.Groups.GetGroup(groupId)
91+
if err != nil {
92+
if resp != nil && resp.StatusCode == http.StatusNotFound {
93+
log.Printf("[DEBUG] gitlab group %s not found so removing from state", groupId)
94+
d.SetId("")
95+
return nil
96+
}
97+
return err
98+
}
99+
100+
// Find shared group data from queried group
101+
for _, sharedGroup := range group.SharedWithGroups {
102+
if sharedGroupId == sharedGroup.GroupID {
103+
convertedAccessLevel := gitlab.AccessLevelValue(sharedGroup.GroupAccessLevel)
104+
105+
d.Set("group_id", groupId)
106+
d.Set("share_group_id", sharedGroup.GroupID)
107+
d.Set("group_access", accessLevel[convertedAccessLevel])
108+
d.Set("expires_at", sharedGroup.ExpiresAt.String())
109+
110+
return nil
111+
}
112+
}
113+
114+
log.Printf("[DEBUG] gitlab shared group %s not found so removing from state", id)
115+
d.SetId("")
116+
return nil
117+
}
118+
119+
func resourceGitlabGroupShareGroupDelete(d *schema.ResourceData, meta interface{}) error {
120+
client := meta.(*gitlab.Client)
121+
id := d.Id()
122+
123+
groupId, sharedGroupId, err := groupIdsFromId(id)
124+
if err != nil {
125+
return err
126+
}
127+
128+
log.Printf("[DEBUG] Delete gitlab share group %d for %s", sharedGroupId, groupId)
129+
130+
_, err = client.GroupMembers.DeleteShareWithGroup(groupId, sharedGroupId)
131+
return err
132+
}
133+
134+
func groupIdsFromId(id string) (string, int, error) {
135+
groupId, sharedGroupIdString, err := parseTwoPartID(id)
136+
if err != nil {
137+
return "", 0, fmt.Errorf("Error parsing ID: %s", id)
138+
}
139+
140+
sharedGroupId, err := strconv.Atoi(sharedGroupIdString)
141+
if err != nil {
142+
return "", 0, fmt.Errorf("Can not determine shared group id: %s", sharedGroupIdString)
143+
}
144+
145+
return groupId, sharedGroupId, nil
146+
}
Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
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+
"github.com/xanzy/go-gitlab"
11+
)
12+
13+
func TestAccGitlabGroupShareGroup_basic(t *testing.T) {
14+
randName := acctest.RandomWithPrefix("acctest")
15+
16+
resource.Test(t, resource.TestCase{
17+
PreCheck: func() { testAccPreCheck(t) },
18+
Providers: testAccProviders,
19+
Steps: []resource.TestStep{
20+
// Share a new group with another group
21+
{
22+
Config: testAccGitlabGroupShareGroupConfig(randName, "guest", "2099-01-01"),
23+
Check: testAccCheckGitlabGroupSharedWithGroup(randName, "2099-01-01", gitlab.GuestPermissions),
24+
},
25+
// Update the share group
26+
{
27+
Config: testAccGitlabGroupShareGroupConfig(randName, "reporter", "2099-02-02"),
28+
Check: testAccCheckGitlabGroupSharedWithGroup(randName, "2099-02-02", gitlab.ReporterPermissions),
29+
},
30+
// Delete the gitlab_group_share_group resource
31+
{
32+
Config: testAccGitlabGroupShareGroupConfigDelete(randName),
33+
Check: testAccCheckGitlabGroupIsNotShared(randName),
34+
},
35+
},
36+
})
37+
}
38+
39+
func TestAccGitlabGroupShareGroup_import(t *testing.T) {
40+
randName := acctest.RandomWithPrefix("acctest")
41+
42+
resource.Test(t, resource.TestCase{
43+
Providers: testAccProviders,
44+
PreCheck: func() { testAccPreCheck(t) },
45+
CheckDestroy: testAccCheckGitlabGroupDestroy,
46+
Steps: []resource.TestStep{
47+
{
48+
// create shared groups
49+
Config: testAccGitlabGroupShareGroupConfig(randName, "guest", "2099-03-03"),
50+
Check: testAccCheckGitlabGroupSharedWithGroup(randName, "2099-03-03", gitlab.GuestPermissions),
51+
},
52+
{
53+
// Verify Import
54+
ResourceName: "gitlab_group_share_group.test",
55+
ImportState: true,
56+
ImportStateVerify: true,
57+
},
58+
},
59+
})
60+
}
61+
62+
func testAccCheckGitlabGroupSharedWithGroup(
63+
groupName string,
64+
expireTime string,
65+
accessLevel gitlab.AccessLevelValue,
66+
) resource.TestCheckFunc {
67+
return func(_ *terraform.State) error {
68+
client := testAccProvider.Meta().(*gitlab.Client)
69+
70+
mainGroup, _, err := client.Groups.GetGroup(fmt.Sprintf("%s_main", groupName))
71+
if err != nil {
72+
return err
73+
}
74+
75+
sharedGroupsCount := len(mainGroup.SharedWithGroups)
76+
if sharedGroupsCount != 1 {
77+
return fmt.Errorf("Number of shared groups was %d (wanted %d)", sharedGroupsCount, 1)
78+
}
79+
80+
sharedGroup := mainGroup.SharedWithGroups[0]
81+
82+
if sharedGroup.GroupName != fmt.Sprintf("%s_share", groupName) {
83+
return fmt.Errorf("group name was %s (wanted %s)", sharedGroup.GroupName, fmt.Sprintf("%s_share", groupName))
84+
}
85+
86+
if gitlab.AccessLevelValue(sharedGroup.GroupAccessLevel) != accessLevel {
87+
return fmt.Errorf("groupAccessLevel was %d (wanted %d)", sharedGroup.GroupAccessLevel, accessLevel)
88+
}
89+
90+
if sharedGroup.ExpiresAt.String() != expireTime {
91+
return fmt.Errorf("expired time was %s (wanted %s)", sharedGroup.ExpiresAt.String(), expireTime)
92+
}
93+
94+
return nil
95+
}
96+
}
97+
98+
func testAccCheckGitlabGroupIsNotShared(groupName string) resource.TestCheckFunc {
99+
return func(_ *terraform.State) error {
100+
client := testAccProvider.Meta().(*gitlab.Client)
101+
102+
mainGroup, _, err := client.Groups.GetGroup(fmt.Sprintf("%s_main", groupName))
103+
if err != nil {
104+
return err
105+
}
106+
107+
sharedGroupsCount := len(mainGroup.SharedWithGroups)
108+
if sharedGroupsCount != 0 {
109+
return fmt.Errorf("Number of shared groups was %d (wanted %d)", sharedGroupsCount, 0)
110+
}
111+
112+
return nil
113+
}
114+
}
115+
116+
func testAccGitlabGroupShareGroupConfig(
117+
randName string,
118+
accessLevel string,
119+
expireTime string,
120+
) string {
121+
return fmt.Sprintf(
122+
`
123+
resource "gitlab_group" "test_main" {
124+
name = "%[1]s_main"
125+
path = "%[1]s_main"
126+
}
127+
128+
resource "gitlab_group" "test_share" {
129+
name = "%[1]s_share"
130+
path = "%[1]s_share"
131+
}
132+
133+
resource "gitlab_group_share_group" "test" {
134+
group_id = gitlab_group.test_main.id
135+
share_group_id = gitlab_group.test_share.id
136+
group_access = "%[2]s"
137+
expires_at = "%[3]s"
138+
}
139+
`,
140+
randName,
141+
accessLevel,
142+
expireTime,
143+
)
144+
}
145+
146+
func testAccGitlabGroupShareGroupConfigDelete(randName string) string {
147+
return fmt.Sprintf(
148+
`
149+
resource "gitlab_group" "test_main" {
150+
name = "%[1]s_main"
151+
path = "%[1]s_main"
152+
}
153+
154+
resource "gitlab_group" "test_share" {
155+
name = "%[1]s_share"
156+
path = "%[1]s_share"
157+
}
158+
`,
159+
randName,
160+
)
161+
}

0 commit comments

Comments
 (0)