Skip to content

Commit 557ea26

Browse files
tobiasbptechknowlogick
authored andcommitted
New resources for managing team membership (#36)
This PR adds two new resources, _gitea_team_membership_ & _gitea_team_members_, in an attempt to decouple _gitea_team_ resources from team memberships. This facilitates the removal of members from teams without altering/recreating an existing _team_ resource. This PR adresses this issue: https://gitea.com/gitea/terraform-provider-gitea/issues/30 The ability to set members in the _gitea_team_ resource has been removed. The resources proposed here are inspired by similar resources in the _GitHub_ provider: * [team_members](https://registry.terraform.io/providers/integrations/github/latest/docs/resources/team_members) * [team_membership](https://registry.terraform.io/providers/integrations/github/latest/docs/resources/team_membership) # gitea_team_members A single resource manages all members of a team. - This resource must be recreated when membership changes. This means, that other team members will temporarily loose their membership until the recreation of the resource is complete. - If the recreation of the resource fails, other users will have lost their membership until the resource can be recreated. # gitea_team_membership A single resource holds the relationship between a single user and a single team. - Memberships can be deleted without affecting other users. Reviewed-on: https://gitea.com/gitea/terraform-provider-gitea/pulls/36 Reviewed-by: techknowlogick <[email protected]> Co-authored-by: Tobias Balle-Petersen <[email protected]> Co-committed-by: Tobias Balle-Petersen <[email protected]>
1 parent 0c0ab51 commit 557ea26

File tree

9 files changed

+414
-35
lines changed

9 files changed

+414
-35
lines changed

docs/resources/team.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,6 @@ resource "gitea_team" "test_team_restricted" {
6969
- `can_create_repos` (Boolean) Flag if the Teams members should be able to create Rpositories in the Organisation
7070
- `description` (String) Description of the Team
7171
- `include_all_repositories` (Boolean) Flag if the Teams members should have access to all Repositories in the Organisation
72-
- `members` (List of String) List of Users that should be part of this team
7372
- `permission` (String) Permissions associated with this Team
7473
Can be `none`, `read`, `write`, `admin` or `owner`
7574
- `repositories` (List of String) List of Repositories that should be part of this team

docs/resources/team_members.md

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
---
2+
# generated by https://github.com/hashicorp/terraform-plugin-docs
3+
page_title: "gitea_team_members Resource - terraform-provider-gitea"
4+
subcategory: ""
5+
description: |-
6+
gitea_team_members manages all members of a single team. This resource will be recreated on member changes.
7+
---
8+
9+
# gitea_team_members (Resource)
10+
11+
`gitea_team_members` manages all members of a single team. This resource will be recreated on member changes.
12+
13+
## Example Usage
14+
15+
```terraform
16+
resource "gitea_org" "example_org" {
17+
name = "m_example_org"
18+
}
19+
20+
resource "gitea_user" "example_users" {
21+
count = 5
22+
username = "m_example_user_${count.index}"
23+
login_name = "m_example_user_${count.index}"
24+
password = "Geheim1!"
25+
email = "m_example_user_${count.index}@user.dev"
26+
}
27+
28+
resource "gitea_team" "example_team" {
29+
name = "m_example_team"
30+
organisation = gitea_org.example_org.name
31+
description = "An example of team membership"
32+
permission = "read"
33+
}
34+
35+
resource "gitea_team_members" "example_members" {
36+
team_id = gitea_team.example_team.id
37+
members = [for user in gitea_user.example_users : user.username]
38+
}
39+
```
40+
41+
<!-- schema generated by tfplugindocs -->
42+
## Schema
43+
44+
### Required
45+
46+
- `members` (List of String) The user names of the members of the team.
47+
- `team_id` (Number) The ID of the team.
48+
49+
### Read-Only
50+
51+
- `id` (String) The ID of this resource.

docs/resources/team_membership.md

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
---
2+
# generated by https://github.com/hashicorp/terraform-plugin-docs
3+
page_title: "gitea_team_membership Resource - terraform-provider-gitea"
4+
subcategory: ""
5+
description: |-
6+
gitea_team_membership manages a single user's membership of a single team.
7+
---
8+
9+
# gitea_team_membership (Resource)
10+
11+
`gitea_team_membership` manages a single user's membership of a single team.
12+
13+
## Example Usage
14+
15+
```terraform
16+
resource "gitea_org" "example_org" {
17+
name = "m_example_org"
18+
}
19+
20+
resource "gitea_user" "example_users" {
21+
count = 5
22+
username = "m_example_user_${count.index}"
23+
login_name = "m_example_user_${count.index}"
24+
password = "Geheim1!"
25+
email = "m_example_user_${count.index}@user.dev"
26+
}
27+
28+
resource "gitea_team" "example_team" {
29+
name = "m_example_team"
30+
organisation = gitea_org.example_org.name
31+
description = "An example team for membership testing"
32+
permission = "read"
33+
}
34+
35+
resource "gitea_team_membership" "example_team_memberships" {
36+
for_each = { for user in gitea_user.example_users : user.username => user }
37+
team_id = gitea_team.example_team.id
38+
username = each.value["username"]
39+
}
40+
```
41+
42+
<!-- schema generated by tfplugindocs -->
43+
## Schema
44+
45+
### Required
46+
47+
- `team_id` (Number) The ID of the team.
48+
- `username` (String) The username of the team member.
49+
50+
### Read-Only
51+
52+
- `id` (String) The ID of this resource.
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
resource "gitea_org" "example_org" {
2+
name = "m_example_org"
3+
}
4+
5+
resource "gitea_user" "example_users" {
6+
count = 5
7+
username = "m_example_user_${count.index}"
8+
login_name = "m_example_user_${count.index}"
9+
password = "Geheim1!"
10+
email = "m_example_user_${count.index}@user.dev"
11+
}
12+
13+
resource "gitea_team" "example_team" {
14+
name = "m_example_team"
15+
organisation = gitea_org.example_org.name
16+
description = "An example of team membership"
17+
permission = "read"
18+
}
19+
20+
resource "gitea_team_members" "example_members" {
21+
team_id = gitea_team.example_team.id
22+
members = [for user in gitea_user.example_users : user.username]
23+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
resource "gitea_org" "example_org" {
2+
name = "m_example_org"
3+
}
4+
5+
resource "gitea_user" "example_users" {
6+
count = 5
7+
username = "m_example_user_${count.index}"
8+
login_name = "m_example_user_${count.index}"
9+
password = "Geheim1!"
10+
email = "m_example_user_${count.index}@user.dev"
11+
}
12+
13+
resource "gitea_team" "example_team" {
14+
name = "m_example_team"
15+
organisation = gitea_org.example_org.name
16+
description = "An example team for membership testing"
17+
permission = "read"
18+
}
19+
20+
resource "gitea_team_membership" "example_team_memberships" {
21+
for_each = { for user in gitea_user.example_users : user.username => user }
22+
team_id = gitea_team.example_team.id
23+
username = each.value["username"]
24+
}

gitea/provider.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,8 @@ func Provider() *schema.Provider {
8181
"gitea_fork": resourceGiteaFork(),
8282
"gitea_public_key": resourceGiteaPublicKey(),
8383
"gitea_team": resourceGiteaTeam(),
84+
"gitea_team_membership": resourceGiteaTeamMembership(),
85+
"gitea_team_members": resourceGiteaTeamMembers(),
8486
"gitea_git_hook": resourceGiteaGitHook(),
8587
"gitea_token": resourceGiteaToken(),
8688
"gitea_repository_key": resourceGiteaRepositoryKey(),

gitea/resource_gitea_team.go

Lines changed: 1 addition & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ const (
1818
TeamCreateRepoFlag string = "can_create_repos"
1919
TeamIncludeAllReposFlag string = "include_all_repositories"
2020
TeamUnits string = "units"
21-
TeamMembers string = "members"
2221
TeamRepositories string = "repositories"
2322
)
2423

@@ -94,17 +93,6 @@ func resourceTeamCreate(d *schema.ResourceData, meta interface{}) (err error) {
9493
return
9594
}
9695

97-
users := d.Get(TeamMembers).([]interface{})
98-
99-
for _, user := range users {
100-
if user != "" {
101-
_, err = client.AddTeamMember(team.ID, user.(string))
102-
if err != nil {
103-
return err
104-
}
105-
}
106-
}
107-
10896
if !includeAllRepos {
10997
err = setTeamRepositories(team, d, meta, false)
11098
if err != nil {
@@ -181,17 +169,6 @@ func resourceTeamUpdate(d *schema.ResourceData, meta interface{}) (err error) {
181169
return err
182170
}
183171

184-
users := d.Get(TeamMembers).([]interface{})
185-
186-
for _, user := range users {
187-
if user != "" {
188-
_, err = client.AddTeamMember(team.ID, user.(string))
189-
if err != nil {
190-
return err
191-
}
192-
}
193-
}
194-
195172
if !includeAllRepos {
196173
err = setTeamRepositories(team, d, meta, true)
197174
if err != nil {
@@ -240,8 +217,8 @@ func setTeamResourceData(team *gitea.Team, d *schema.ResourceData, meta interfac
240217
d.Set(TeamPermissions, string(team.Permission))
241218
d.Set(TeamIncludeAllReposFlag, team.IncludesAllRepositories)
242219
d.Set(TeamUnits, d.Get(TeamUnits).(string))
243-
d.Set(TeamMembers, d.Get(TeamMembers))
244220
d.Set(TeamRepositories, d.Get(TeamRepositories))
221+
245222
return
246223
}
247224

@@ -304,16 +281,6 @@ func resourceGiteaTeam() *schema.Resource {
304281
Description: "List of types of Repositories that should be allowed to be created from Team members.\n" +
305282
"Can be `repo.code`, `repo.issues`, `repo.ext_issues`, `repo.wiki`, `repo.pulls`, `repo.releases`, `repo.projects` and/or `repo.ext_wiki`",
306283
},
307-
"members": {
308-
Type: schema.TypeList,
309-
Elem: &schema.Schema{
310-
Type: schema.TypeString,
311-
},
312-
Optional: true,
313-
Required: false,
314-
Computed: true,
315-
Description: "List of Users that should be part of this team",
316-
},
317284
"repositories": {
318285
Type: schema.TypeList,
319286
Elem: &schema.Schema{

gitea/resource_gitea_team_members.go

Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
package gitea
2+
3+
import (
4+
"fmt"
5+
6+
"code.gitea.io/sdk/gitea"
7+
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
8+
)
9+
10+
const (
11+
membersTeamID string = "team_id"
12+
membersTeamMembers string = "members"
13+
)
14+
15+
func getTeamMembers(team_id int, meta interface{}) (membersNames []string, err error) {
16+
client := meta.(*gitea.Client)
17+
18+
var memberNames []string
19+
var members []*gitea.User
20+
21+
// Get all pages of users
22+
page := 1
23+
for {
24+
// Set options for current page
25+
opts := gitea.ListTeamMembersOptions{
26+
ListOptions: gitea.ListOptions{Page: page, PageSize: 50},
27+
}
28+
29+
// Get page of team members
30+
members, _, err = client.ListTeamMembers(int64(team_id), opts)
31+
if err != nil {
32+
return nil, err
33+
}
34+
35+
// If no members were returned, we are done
36+
if len(members) == 0 {
37+
break
38+
}
39+
40+
// Update list of usernames with data from current page
41+
for _, m := range members {
42+
memberNames = append(memberNames, m.UserName)
43+
}
44+
45+
// Next page
46+
page += 1
47+
}
48+
49+
return memberNames, nil
50+
}
51+
52+
func resourceTeamMembersCreate(d *schema.ResourceData, meta interface{}) (err error) {
53+
client := meta.(*gitea.Client)
54+
team_id := d.Get(membersTeamID).(int)
55+
56+
var memberNames []string
57+
58+
// What if team already has member?
59+
// What if user is already in the team?
60+
// What if user does not exist?
61+
62+
// Add members to the team
63+
for _, name := range d.Get(membersTeamMembers).(*schema.Set).List() {
64+
_ , err = client.AddTeamMember(int64(team_id), name.(string))
65+
if err != nil {
66+
return err
67+
}
68+
// Update list of usernames of the team members
69+
memberNames = append(memberNames, name.(string))
70+
}
71+
72+
err = setTeamMembersData(team_id, memberNames, d)
73+
74+
return
75+
}
76+
77+
func resourceTeamMembersRead(d *schema.ResourceData, meta interface{}) (err error) {
78+
team_id := d.Get(membersTeamID).(int)
79+
80+
memberNames, err := getTeamMembers(team_id, meta)
81+
if err != nil {
82+
return err
83+
}
84+
85+
err = setTeamMembersData(team_id, memberNames, d)
86+
87+
return
88+
}
89+
90+
func resourceTeamMembersDelete(d *schema.ResourceData, meta interface{}) (err error) {
91+
client := meta.(*gitea.Client)
92+
team_id := d.Get(membersTeamID).(int)
93+
94+
var memberNames []string
95+
96+
memberNames , err = getTeamMembers(team_id, meta)
97+
if err != nil {
98+
return err
99+
}
100+
101+
// Delete all memberships
102+
for _, username := range memberNames {
103+
_, err = client.RemoveTeamMember(int64(team_id), username)
104+
if err != nil {
105+
return err
106+
}
107+
}
108+
109+
return
110+
}
111+
112+
func setTeamMembersData(team_id int, memberNames []string, d *schema.ResourceData) (err error) {
113+
d.SetId(fmt.Sprintf("%d", team_id))
114+
d.Set(membersTeamID, team_id)
115+
d.Set(membersTeamMembers, memberNames)
116+
117+
return
118+
}
119+
120+
func resourceGiteaTeamMembers() *schema.Resource {
121+
return &schema.Resource{
122+
Read: resourceTeamMembersRead,
123+
Create: resourceTeamMembersCreate,
124+
Delete: resourceTeamMembersDelete,
125+
Importer: &schema.ResourceImporter{
126+
StateContext: schema.ImportStatePassthroughContext,
127+
},
128+
Schema: map[string]*schema.Schema{
129+
"team_id": {
130+
Type: schema.TypeInt,
131+
Required: true,
132+
ForceNew: true,
133+
Description: "The ID of the team.",
134+
},
135+
"members": {
136+
// TypeSet is better than TypeList because
137+
// reordering the members will not trigger recreation
138+
Type: schema.TypeSet,
139+
Elem: &schema.Schema{
140+
Type: schema.TypeString,
141+
},
142+
Required: true,
143+
ForceNew: true,
144+
Description: "The user names of the members of the team.",
145+
},
146+
147+
},
148+
Description: "`gitea_team_members` manages all members of a single team. This resource will be recreated on member changes.",
149+
}
150+
}

0 commit comments

Comments
 (0)