2020-12-30 00:49:11 +00:00
|
|
|
package gitlab
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
2020-12-31 20:00:10 +00:00
|
|
|
"sync"
|
2020-12-30 00:49:11 +00:00
|
|
|
|
2024-05-05 20:23:07 +00:00
|
|
|
"github.com/badjware/gitlabfs/fstree"
|
2020-12-30 00:49:11 +00:00
|
|
|
"github.com/xanzy/go-gitlab"
|
|
|
|
)
|
|
|
|
|
|
|
|
type Group struct {
|
|
|
|
ID int
|
|
|
|
Name string
|
|
|
|
|
2024-06-07 02:01:22 +00:00
|
|
|
gitlabClient *gitlabClient
|
|
|
|
|
2024-05-05 20:09:03 +00:00
|
|
|
mux sync.Mutex
|
|
|
|
|
2024-07-18 03:35:18 +00:00
|
|
|
// hold group content
|
2024-06-07 02:01:22 +00:00
|
|
|
childGroups map[string]fstree.GroupSource
|
|
|
|
childProjects map[string]fstree.RepositorySource
|
2020-12-30 00:49:11 +00:00
|
|
|
}
|
|
|
|
|
2024-05-05 20:09:03 +00:00
|
|
|
func (g *Group) GetGroupID() uint64 {
|
|
|
|
return uint64(g.ID)
|
2020-12-30 00:49:11 +00:00
|
|
|
}
|
|
|
|
|
2024-07-18 03:35:18 +00:00
|
|
|
func (g *Group) InvalidateContentCache() {
|
2020-12-31 20:00:10 +00:00
|
|
|
g.mux.Lock()
|
|
|
|
defer g.mux.Unlock()
|
|
|
|
|
2024-06-07 02:01:22 +00:00
|
|
|
// clear child group from cache
|
2024-07-18 03:35:18 +00:00
|
|
|
g.gitlabClient.groupCacheMux.Lock()
|
2024-06-07 02:01:22 +00:00
|
|
|
for _, childGroup := range g.childGroups {
|
|
|
|
gid := int(childGroup.GetGroupID())
|
|
|
|
delete(g.gitlabClient.groupCache, gid)
|
|
|
|
}
|
2024-07-18 03:35:18 +00:00
|
|
|
g.gitlabClient.groupCacheMux.Unlock()
|
2024-06-07 02:01:22 +00:00
|
|
|
g.childGroups = nil
|
|
|
|
|
|
|
|
// clear child repositories from cache
|
2024-06-07 01:46:29 +00:00
|
|
|
g.childGroups = nil
|
2020-12-31 20:00:10 +00:00
|
|
|
}
|
|
|
|
|
2024-05-05 20:09:03 +00:00
|
|
|
func (c *gitlabClient) fetchGroup(gid int) (*Group, error) {
|
|
|
|
// start by searching the cache
|
|
|
|
// TODO: cache invalidation?
|
2024-07-18 03:35:18 +00:00
|
|
|
c.groupCacheMux.RLock()
|
2024-05-05 20:09:03 +00:00
|
|
|
group, found := c.groupCache[gid]
|
2024-07-18 03:35:18 +00:00
|
|
|
c.groupCacheMux.RUnlock()
|
2024-05-05 20:09:03 +00:00
|
|
|
if found {
|
2024-06-07 01:46:29 +00:00
|
|
|
c.logger.Debug("Group cache hit", "gid", gid)
|
2024-05-05 20:09:03 +00:00
|
|
|
return group, nil
|
2024-06-07 01:46:29 +00:00
|
|
|
} else {
|
|
|
|
c.logger.Debug("Group cache miss; fetching group", "gid", gid)
|
2024-05-05 20:09:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// If not in cache, fetch group infos from API
|
2024-08-09 21:12:48 +00:00
|
|
|
gitlabGroup, _, err := c.client.Groups.GetGroup(gid, &gitlab.GetGroupOptions{})
|
2020-12-30 00:49:11 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("failed to fetch group with id %v: %v", gid, err)
|
|
|
|
}
|
2024-06-07 01:46:29 +00:00
|
|
|
c.logger.Debug("Fetched group", "gid", gid)
|
2024-05-05 20:09:03 +00:00
|
|
|
newGroup := Group{
|
|
|
|
ID: gitlabGroup.ID,
|
|
|
|
Name: gitlabGroup.Path,
|
|
|
|
|
2024-06-07 02:01:22 +00:00
|
|
|
gitlabClient: c,
|
|
|
|
|
|
|
|
childGroups: nil,
|
|
|
|
childProjects: nil,
|
2024-06-07 01:46:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// save in cache
|
2024-07-18 03:35:18 +00:00
|
|
|
c.groupCacheMux.Lock()
|
2024-06-07 01:46:29 +00:00
|
|
|
c.groupCache[gid] = &newGroup
|
2024-07-18 03:35:18 +00:00
|
|
|
c.groupCacheMux.Unlock()
|
2024-06-07 01:46:29 +00:00
|
|
|
|
|
|
|
return &newGroup, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *gitlabClient) newGroupFromGitlabGroup(gitlabGroup *gitlab.Group) (*Group, error) {
|
|
|
|
gid := gitlabGroup.ID
|
|
|
|
|
|
|
|
// start by searching the cache
|
2024-07-18 03:35:18 +00:00
|
|
|
c.groupCacheMux.RLock()
|
2024-06-07 01:46:29 +00:00
|
|
|
group, found := c.groupCache[gid]
|
2024-07-18 03:35:18 +00:00
|
|
|
c.groupCacheMux.RUnlock()
|
2024-06-07 01:46:29 +00:00
|
|
|
if found {
|
2024-07-18 03:35:18 +00:00
|
|
|
// if found in cache, return the cached reference
|
2024-06-07 01:46:29 +00:00
|
|
|
c.logger.Debug("Group cache hit", "gid", gid)
|
|
|
|
return group, nil
|
|
|
|
} else {
|
|
|
|
c.logger.Debug("Group cache miss; registering group", "gid", gid)
|
|
|
|
}
|
|
|
|
|
2024-07-18 03:35:18 +00:00
|
|
|
// if not found in cache, convert and save to cache now
|
2024-06-07 01:46:29 +00:00
|
|
|
newGroup := Group{
|
|
|
|
ID: gitlabGroup.ID,
|
|
|
|
Name: gitlabGroup.Path,
|
|
|
|
|
2024-06-07 02:01:22 +00:00
|
|
|
gitlabClient: c,
|
|
|
|
|
|
|
|
childGroups: nil,
|
|
|
|
childProjects: nil,
|
2024-05-05 20:09:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// save in cache
|
2024-07-18 03:35:18 +00:00
|
|
|
c.groupCacheMux.Lock()
|
2024-05-05 20:09:03 +00:00
|
|
|
c.groupCache[gid] = &newGroup
|
2024-07-18 03:35:18 +00:00
|
|
|
c.groupCacheMux.Unlock()
|
2024-05-05 20:09:03 +00:00
|
|
|
|
|
|
|
return &newGroup, nil
|
2020-12-30 00:49:11 +00:00
|
|
|
}
|
|
|
|
|
2024-05-05 20:23:07 +00:00
|
|
|
func (c *gitlabClient) fetchGroupContent(group *Group) (map[string]fstree.GroupSource, map[string]fstree.RepositorySource, error) {
|
2024-07-18 03:35:18 +00:00
|
|
|
// Only a single routine can fetch the group content at the time.
|
|
|
|
// We lock for the whole duration of the function to avoid fetching the same data from the API
|
|
|
|
// multiple times if concurrent calls where to occur.
|
2020-12-31 20:00:10 +00:00
|
|
|
group.mux.Lock()
|
|
|
|
defer group.mux.Unlock()
|
|
|
|
|
|
|
|
// Get cached data if available
|
2024-05-05 20:09:03 +00:00
|
|
|
// TODO: cache cache invalidation?
|
2024-06-07 02:01:22 +00:00
|
|
|
if group.childGroups == nil || group.childProjects == nil {
|
|
|
|
childGroups := make(map[string]fstree.GroupSource)
|
|
|
|
childProjects := make(map[string]fstree.RepositorySource)
|
2024-05-05 20:09:03 +00:00
|
|
|
|
|
|
|
// List subgroups in path
|
2024-08-09 21:12:48 +00:00
|
|
|
listGroupsOpt := &gitlab.ListSubGroupsOptions{
|
2024-05-05 20:09:03 +00:00
|
|
|
ListOptions: gitlab.ListOptions{
|
|
|
|
Page: 1,
|
|
|
|
PerPage: 100,
|
|
|
|
},
|
2024-08-09 21:12:48 +00:00
|
|
|
AllAvailable: gitlab.Ptr(true),
|
2020-12-30 00:49:11 +00:00
|
|
|
}
|
2024-05-05 20:09:03 +00:00
|
|
|
for {
|
2024-08-09 21:12:48 +00:00
|
|
|
gitlabGroups, response, err := c.client.Groups.ListSubGroups(group.ID, listGroupsOpt)
|
2024-05-05 20:09:03 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, nil, fmt.Errorf("failed to fetch groups in gitlab: %v", err)
|
|
|
|
}
|
|
|
|
for _, gitlabGroup := range gitlabGroups {
|
2024-06-07 01:46:29 +00:00
|
|
|
group, _ := c.newGroupFromGitlabGroup(gitlabGroup)
|
2024-06-07 02:01:22 +00:00
|
|
|
childGroups[group.Name] = group
|
2024-05-05 20:09:03 +00:00
|
|
|
}
|
|
|
|
if response.CurrentPage >= response.TotalPages {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
// Get the next page
|
2024-08-09 21:12:48 +00:00
|
|
|
listGroupsOpt.Page = response.NextPage
|
2020-12-30 00:49:11 +00:00
|
|
|
}
|
|
|
|
|
2024-05-05 20:09:03 +00:00
|
|
|
// List projects in path
|
|
|
|
listProjectOpt := &gitlab.ListGroupProjectsOptions{
|
|
|
|
ListOptions: gitlab.ListOptions{
|
|
|
|
Page: 1,
|
|
|
|
PerPage: 100,
|
|
|
|
}}
|
|
|
|
for {
|
|
|
|
gitlabProjects, response, err := c.client.Groups.ListGroupProjects(group.ID, listProjectOpt)
|
|
|
|
if err != nil {
|
|
|
|
return nil, nil, fmt.Errorf("failed to fetch projects in gitlab: %v", err)
|
|
|
|
}
|
|
|
|
for _, gitlabProject := range gitlabProjects {
|
|
|
|
project := c.newProjectFromGitlabProject(gitlabProject)
|
2024-07-18 04:25:13 +00:00
|
|
|
if project != nil {
|
|
|
|
childProjects[project.Path] = project
|
|
|
|
}
|
2024-05-05 20:09:03 +00:00
|
|
|
}
|
|
|
|
if response.CurrentPage >= response.TotalPages {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
// Get the next page
|
|
|
|
listProjectOpt.Page = response.NextPage
|
2020-12-30 00:49:11 +00:00
|
|
|
}
|
|
|
|
|
2024-06-07 02:01:22 +00:00
|
|
|
group.childGroups = childGroups
|
|
|
|
group.childProjects = childProjects
|
2024-05-05 20:09:03 +00:00
|
|
|
}
|
2024-06-07 02:01:22 +00:00
|
|
|
return group.childGroups, group.childProjects, nil
|
2020-12-30 00:49:11 +00:00
|
|
|
}
|