Compare commits

..

No commits in common. "2d0a62dc459274ced1cb24cbf142a5bd2b3b6946" and "0a501582394c63581d52dacb19777e3bf3aeaf89" have entirely different histories.

9 changed files with 52 additions and 124 deletions

View File

@ -27,13 +27,6 @@ gitlab:
# A list of the user ids to expose their personal projects in the filesystem. # A list of the user ids to expose their personal projects in the filesystem.
user_ids: [] user_ids: []
# Set how archived projects are handled.
# If set to "show", it will add them to the filesystem and treat them like any other repository
# If set to "hide", it will add them to the filesystem, but prefix the symlink with a "."
# If set to "ignore", it will make them absent from the filesystem
# Default to "hide"
archived_project_handling: hide
# If set to true, the user the api token belongs to will automatically be added to the list of users exposed by the filesystem. # If set to true, the user the api token belongs to will automatically be added to the list of users exposed by the filesystem.
include_current_user: true include_current_user: true

View File

@ -41,7 +41,6 @@ func LoadConfig(configPath string) (*Config, error) {
PullMethod: "http", PullMethod: "http",
GroupIDs: []int{9970}, GroupIDs: []int{9970},
UserIDs: []int{}, UserIDs: []int{},
ArchivedProjectHandling: "hide",
IncludeCurrentUser: true, IncludeCurrentUser: true,
}, },
Git: git.GitClientParam{ Git: git.GitClientParam{
@ -86,10 +85,5 @@ func MakeGitlabConfig(config *Config) (*gitlab.GitlabClientConfig, error) {
return nil, fmt.Errorf("pull_method must be either \"%v\" or \"%v\"", gitlab.PullMethodHTTP, gitlab.PullMethodSSH) return nil, fmt.Errorf("pull_method must be either \"%v\" or \"%v\"", gitlab.PullMethodHTTP, gitlab.PullMethodSSH)
} }
// parse archive_handing
if config.Gitlab.ArchivedProjectHandling != gitlab.ArchivedProjectShow && config.Gitlab.ArchivedProjectHandling != gitlab.ArchivedProjectHide && config.Gitlab.ArchivedProjectHandling != gitlab.ArchivedProjectIgnore {
return nil, fmt.Errorf("pull_method must be either \"%v\", \"%v\" or \"%v\"", gitlab.ArchivedProjectShow, gitlab.ArchivedProjectHide, gitlab.ArchivedProjectIgnore)
}
return &config.Gitlab, nil return &config.Gitlab, nil
} }

View File

@ -27,7 +27,6 @@ func TestLoadConfig(t *testing.T) {
PullMethod: "ssh", PullMethod: "ssh",
GroupIDs: []int{123}, GroupIDs: []int{123},
UserIDs: []int{456}, UserIDs: []int{456},
ArchivedProjectHandling: "hide",
IncludeCurrentUser: true, IncludeCurrentUser: true,
}, },
Git: git.GitClientParam{ Git: git.GitClientParam{
@ -120,48 +119,31 @@ func TestMakeGitlabConfig(t *testing.T) {
input: &config.Config{ input: &config.Config{
Gitlab: gitlab.GitlabClientConfig{ Gitlab: gitlab.GitlabClientConfig{
URL: "https://gitlab.com", URL: "https://gitlab.com",
PullMethod: "http",
Token: "", Token: "",
GroupIDs: []int{9970}, GroupIDs: []int{9970},
UserIDs: []int{}, UserIDs: []int{},
ArchivedProjectHandling: "hide",
IncludeCurrentUser: true, IncludeCurrentUser: true,
PullMethod: "http",
}, },
}, },
expected: &gitlab.GitlabClientConfig{ expected: &gitlab.GitlabClientConfig{
URL: "https://gitlab.com", URL: "https://gitlab.com",
PullMethod: "http",
Token: "", Token: "",
GroupIDs: []int{9970}, GroupIDs: []int{9970},
UserIDs: []int{}, UserIDs: []int{},
ArchivedProjectHandling: "hide",
IncludeCurrentUser: true, IncludeCurrentUser: true,
PullMethod: "http",
}, },
}, },
"InvalidPullMethod": { "InvalidPullMethod": {
input: &config.Config{ input: &config.Config{
Gitlab: gitlab.GitlabClientConfig{ Gitlab: gitlab.GitlabClientConfig{
URL: "https://gitlab.com", URL: "https://gitlab.com",
Token: "",
GroupIDs: []int{9970},
UserIDs: []int{},
IncludeCurrentUser: true,
PullMethod: "invalid", PullMethod: "invalid",
Token: "",
GroupIDs: []int{9970},
UserIDs: []int{},
ArchivedProjectHandling: "hide",
IncludeCurrentUser: true,
},
},
expected: nil,
},
"InvalidArchiveHandling": {
input: &config.Config{
Gitlab: gitlab.GitlabClientConfig{
URL: "https://gitlab.com",
PullMethod: "http",
Token: "",
GroupIDs: []int{9970},
UserIDs: []int{},
IncludeCurrentUser: true,
ArchivedProjectHandling: "invalid",
}, },
}, },
expected: nil, expected: nil,

View File

@ -18,7 +18,7 @@ type groupNode struct {
type GroupSource interface { type GroupSource interface {
GetGroupID() uint64 GetGroupID() uint64
InvalidateContentCache() InvalidateCache()
} }
// Ensure we are implementing the NodeReaddirer interface // Ensure we are implementing the NodeReaddirer interface

View File

@ -41,6 +41,6 @@ func (n *refreshNode) Setattr(ctx context.Context, fh fs.FileHandle, in *fuse.Se
} }
func (n *refreshNode) Open(ctx context.Context, flags uint32) (fh fs.FileHandle, fuseFlags uint32, errno syscall.Errno) { func (n *refreshNode) Open(ctx context.Context, flags uint32) (fh fs.FileHandle, fuseFlags uint32, errno syscall.Errno) {
n.source.InvalidateContentCache() n.source.InvalidateCache()
return nil, 0, 0 return nil, 0, 0
} }

View File

@ -4,7 +4,6 @@ import (
"fmt" "fmt"
"log/slog" "log/slog"
"slices" "slices"
"sync"
"github.com/badjware/gitlabfs/fstree" "github.com/badjware/gitlabfs/fstree"
"github.com/xanzy/go-gitlab" "github.com/xanzy/go-gitlab"
@ -13,10 +12,6 @@ import (
const ( const (
PullMethodHTTP = "http" PullMethodHTTP = "http"
PullMethodSSH = "ssh" PullMethodSSH = "ssh"
ArchivedProjectShow = "show"
ArchivedProjectHide = "hide"
ArchivedProjectIgnore = "ignore"
) )
type GitlabClientConfig struct { type GitlabClientConfig struct {
@ -24,7 +19,6 @@ type GitlabClientConfig struct {
Token string `yaml:"token,omitempty"` Token string `yaml:"token,omitempty"`
GroupIDs []int `yaml:"group_ids,omitempty"` GroupIDs []int `yaml:"group_ids,omitempty"`
UserIDs []int `yaml:"user_ids,omitempty"` UserIDs []int `yaml:"user_ids,omitempty"`
ArchivedProjectHandling string `yaml:"archived_project_handling,omitempty"`
IncludeCurrentUser bool `yaml:"include_current_user,omitempty"` IncludeCurrentUser bool `yaml:"include_current_user,omitempty"`
PullMethod string `yaml:"pull_method,omitempty"` PullMethod string `yaml:"pull_method,omitempty"`
} }
@ -40,9 +34,7 @@ type gitlabClient struct {
currentUserCache *User currentUserCache *User
// API response cache // API response cache
groupCacheMux sync.RWMutex
groupCache map[int]*Group groupCache map[int]*Group
userCacheMux sync.RWMutex
userCache map[int]*User userCache map[int]*User
} }

View File

@ -16,7 +16,7 @@ type Group struct {
mux sync.Mutex mux sync.Mutex
// hold group content // group content
childGroups map[string]fstree.GroupSource childGroups map[string]fstree.GroupSource
childProjects map[string]fstree.RepositorySource childProjects map[string]fstree.RepositorySource
} }
@ -25,17 +25,15 @@ func (g *Group) GetGroupID() uint64 {
return uint64(g.ID) return uint64(g.ID)
} }
func (g *Group) InvalidateContentCache() { func (g *Group) InvalidateCache() {
g.mux.Lock() g.mux.Lock()
defer g.mux.Unlock() defer g.mux.Unlock()
// clear child group from cache // clear child group from cache
g.gitlabClient.groupCacheMux.Lock()
for _, childGroup := range g.childGroups { for _, childGroup := range g.childGroups {
gid := int(childGroup.GetGroupID()) gid := int(childGroup.GetGroupID())
delete(g.gitlabClient.groupCache, gid) delete(g.gitlabClient.groupCache, gid)
} }
g.gitlabClient.groupCacheMux.Unlock()
g.childGroups = nil g.childGroups = nil
// clear child repositories from cache // clear child repositories from cache
@ -45,9 +43,7 @@ func (g *Group) InvalidateContentCache() {
func (c *gitlabClient) fetchGroup(gid int) (*Group, error) { func (c *gitlabClient) fetchGroup(gid int) (*Group, error) {
// start by searching the cache // start by searching the cache
// TODO: cache invalidation? // TODO: cache invalidation?
c.groupCacheMux.RLock()
group, found := c.groupCache[gid] group, found := c.groupCache[gid]
c.groupCacheMux.RUnlock()
if found { if found {
c.logger.Debug("Group cache hit", "gid", gid) c.logger.Debug("Group cache hit", "gid", gid)
return group, nil return group, nil
@ -72,9 +68,7 @@ func (c *gitlabClient) fetchGroup(gid int) (*Group, error) {
} }
// save in cache // save in cache
c.groupCacheMux.Lock()
c.groupCache[gid] = &newGroup c.groupCache[gid] = &newGroup
c.groupCacheMux.Unlock()
return &newGroup, nil return &newGroup, nil
} }
@ -83,18 +77,15 @@ func (c *gitlabClient) newGroupFromGitlabGroup(gitlabGroup *gitlab.Group) (*Grou
gid := gitlabGroup.ID gid := gitlabGroup.ID
// start by searching the cache // start by searching the cache
c.groupCacheMux.RLock()
group, found := c.groupCache[gid] group, found := c.groupCache[gid]
c.groupCacheMux.RUnlock()
if found { if found {
// if found in cache, return the cached reference
c.logger.Debug("Group cache hit", "gid", gid) c.logger.Debug("Group cache hit", "gid", gid)
return group, nil return group, nil
} else { } else {
c.logger.Debug("Group cache miss; registering group", "gid", gid) c.logger.Debug("Group cache miss; registering group", "gid", gid)
} }
// if not found in cache, convert and save to cache now // if not in cache, convert and save to cache now
newGroup := Group{ newGroup := Group{
ID: gitlabGroup.ID, ID: gitlabGroup.ID,
Name: gitlabGroup.Path, Name: gitlabGroup.Path,
@ -106,17 +97,12 @@ func (c *gitlabClient) newGroupFromGitlabGroup(gitlabGroup *gitlab.Group) (*Grou
} }
// save in cache // save in cache
c.groupCacheMux.Lock()
c.groupCache[gid] = &newGroup c.groupCache[gid] = &newGroup
c.groupCacheMux.Unlock()
return &newGroup, nil return &newGroup, nil
} }
func (c *gitlabClient) fetchGroupContent(group *Group) (map[string]fstree.GroupSource, map[string]fstree.RepositorySource, error) { func (c *gitlabClient) fetchGroupContent(group *Group) (map[string]fstree.GroupSource, map[string]fstree.RepositorySource, error) {
// 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.
group.mux.Lock() group.mux.Lock()
defer group.mux.Unlock() defer group.mux.Unlock()
@ -163,9 +149,7 @@ func (c *gitlabClient) fetchGroupContent(group *Group) (map[string]fstree.GroupS
} }
for _, gitlabProject := range gitlabProjects { for _, gitlabProject := range gitlabProjects {
project := c.newProjectFromGitlabProject(gitlabProject) project := c.newProjectFromGitlabProject(gitlabProject)
if project != nil { childProjects[project.Name] = &project
childProjects[project.Path] = project
}
} }
if response.CurrentPage >= response.TotalPages { if response.CurrentPage >= response.TotalPages {
break break

View File

@ -1,14 +1,12 @@
package gitlab package gitlab
import ( import (
"path"
"github.com/xanzy/go-gitlab" "github.com/xanzy/go-gitlab"
) )
type Project struct { type Project struct {
ID int ID int
Path string Name string
CloneURL string CloneURL string
DefaultBranch string DefaultBranch string
} }
@ -25,14 +23,11 @@ func (p *Project) GetDefaultBranch() string {
return p.DefaultBranch return p.DefaultBranch
} }
func (c *gitlabClient) newProjectFromGitlabProject(project *gitlab.Project) *Project { func (c *gitlabClient) newProjectFromGitlabProject(project *gitlab.Project) Project {
// https://godoc.org/github.com/xanzy/go-gitlab#Project // https://godoc.org/github.com/xanzy/go-gitlab#Project
if c.ArchivedProjectHandling == ArchivedProjectIgnore && project.Archived {
return nil
}
p := Project{ p := Project{
ID: project.ID, ID: project.ID,
Path: project.Path, Name: project.Path,
DefaultBranch: project.DefaultBranch, DefaultBranch: project.DefaultBranch,
} }
if p.DefaultBranch == "" { if p.DefaultBranch == "" {
@ -43,8 +38,5 @@ func (c *gitlabClient) newProjectFromGitlabProject(project *gitlab.Project) *Pro
} else { } else {
p.CloneURL = project.HTTPURLToRepo p.CloneURL = project.HTTPURLToRepo
} }
if c.ArchivedProjectHandling == ArchivedProjectHide && project.Archived { return p
p.Path = path.Join(path.Dir(p.Path), "."+path.Base(p.Path))
}
return &p
} }

View File

@ -14,7 +14,7 @@ type User struct {
mux sync.Mutex mux sync.Mutex
// hold user content // user content
childProjects map[string]fstree.RepositorySource childProjects map[string]fstree.RepositorySource
} }
@ -22,7 +22,7 @@ func (u *User) GetGroupID() uint64 {
return uint64(u.ID) return uint64(u.ID)
} }
func (u *User) InvalidateContentCache() { func (u *User) InvalidateCache() {
u.mux.Lock() u.mux.Lock()
defer u.mux.Unlock() defer u.mux.Unlock()
@ -33,18 +33,15 @@ func (u *User) InvalidateContentCache() {
func (c *gitlabClient) fetchUser(uid int) (*User, error) { func (c *gitlabClient) fetchUser(uid int) (*User, error) {
// start by searching the cache // start by searching the cache
// TODO: cache invalidation? // TODO: cache invalidation?
c.userCacheMux.RLock()
user, found := c.userCache[uid] user, found := c.userCache[uid]
c.userCacheMux.RUnlock()
if found { if found {
// if found in cache, return the cached reference
c.logger.Debug("User cache hit", "uid", uid) c.logger.Debug("User cache hit", "uid", uid)
return user, nil return user, nil
} else { } else {
c.logger.Debug("User cache miss", "uid", uid) c.logger.Debug("User cache miss", "uid", uid)
} }
// If not found in cache, fetch group infos from API // If not in cache, fetch group infos from API
gitlabUser, _, err := c.client.Users.GetUser(uid) gitlabUser, _, err := c.client.Users.GetUser(uid)
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to fetch user with id %v: %v", uid, err) return nil, fmt.Errorf("failed to fetch user with id %v: %v", uid, err)
@ -58,7 +55,6 @@ func (c *gitlabClient) fetchUser(uid int) (*User, error) {
// save in cache // save in cache
c.userCache[uid] = &newUser c.userCache[uid] = &newUser
c.userCacheMux.Unlock()
return &newUser, nil return &newUser, nil
} }
@ -81,9 +77,6 @@ func (c *gitlabClient) fetchCurrentUser() (*User, error) {
} }
func (c *gitlabClient) fetchUserContent(user *User) (map[string]fstree.GroupSource, map[string]fstree.RepositorySource, error) { func (c *gitlabClient) fetchUserContent(user *User) (map[string]fstree.GroupSource, map[string]fstree.RepositorySource, error) {
// Only a single routine can fetch the user 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.
user.mux.Lock() user.mux.Lock()
defer user.mux.Unlock() defer user.mux.Unlock()
@ -105,9 +98,7 @@ func (c *gitlabClient) fetchUserContent(user *User) (map[string]fstree.GroupSour
} }
for _, gitlabProject := range gitlabProjects { for _, gitlabProject := range gitlabProjects {
project := c.newProjectFromGitlabProject(gitlabProject) project := c.newProjectFromGitlabProject(gitlabProject)
if project != nil { childProjects[project.Name] = &project
childProjects[project.Path] = project
}
} }
if response.CurrentPage >= response.TotalPages { if response.CurrentPage >= response.TotalPages {
break break