refactor to decouple fstree package from git package

This commit is contained in:
Massaki Archambault 2024-05-05 19:52:57 -04:00
parent 0c647a692f
commit 735a803cdb
7 changed files with 55 additions and 77 deletions

View File

@ -42,7 +42,7 @@ func newGroupNodeFromSource(source GroupSource, param *FSParam) (*groupNode, err
func (n *groupNode) Readdir(ctx context.Context) (fs.DirStream, syscall.Errno) { func (n *groupNode) Readdir(ctx context.Context) (fs.DirStream, syscall.Errno) {
groups, repositories, err := n.param.GitPlatform.FetchGroupContent(n.source.GetGroupID()) groups, repositories, err := n.param.GitPlatform.FetchGroupContent(n.source.GetGroupID())
if err != nil { if err != nil {
fmt.Errorf("%v", err) fmt.Println(err)
} }
entries := make([]fuse.DirEntry, 0, len(groups)+len(repositories)+len(n.staticNodes)) entries := make([]fuse.DirEntry, 0, len(groups)+len(repositories)+len(n.staticNodes))

View File

@ -2,6 +2,7 @@ package fstree
import ( import (
"context" "context"
"fmt"
"syscall" "syscall"
"github.com/hanwen/go-fuse/v2/fs" "github.com/hanwen/go-fuse/v2/fs"
@ -37,7 +38,9 @@ func newRepositoryNodeFromSource(source RepositorySource, param *FSParam) (*repo
func (n *repositoryNode) Readlink(ctx context.Context) ([]byte, syscall.Errno) { func (n *repositoryNode) Readlink(ctx context.Context) ([]byte, syscall.Errno) {
// Create the local copy of the repo // Create the local copy of the repo
// TODO: cleanup // TODO: cleanup
localRepositoryPath, _ := n.param.GitImplementation.CloneOrPull(n.source.GetCloneURL(), int(n.source.GetRepositoryID()), n.source.GetDefaultBranch()) localRepositoryPath, err := n.param.GitClient.FetchLocalRepositoryPath(n.source)
if err != nil {
fmt.Println(err)
}
return []byte(localRepositoryPath), 0 return []byte(localRepositoryPath), 0
} }

View File

@ -7,7 +7,6 @@ import (
"os/signal" "os/signal"
"syscall" "syscall"
"github.com/badjware/gitlabfs/git"
"github.com/hanwen/go-fuse/v2/fs" "github.com/hanwen/go-fuse/v2/fs"
"github.com/hanwen/go-fuse/v2/fuse" "github.com/hanwen/go-fuse/v2/fuse"
) )
@ -22,14 +21,18 @@ type staticNode interface {
Mode() uint32 Mode() uint32
} }
type GitClient interface {
FetchLocalRepositoryPath(source RepositorySource) (string, error)
}
type GitPlatform interface { type GitPlatform interface {
FetchRootGroupContent() (map[string]GroupSource, error) FetchRootGroupContent() (map[string]GroupSource, error)
FetchGroupContent(gid uint64) (map[string]GroupSource, map[string]RepositorySource, error) FetchGroupContent(gid uint64) (map[string]GroupSource, map[string]RepositorySource, error)
} }
type FSParam struct { type FSParam struct {
GitImplementation git.GitClonerPuller GitClient GitClient
GitPlatform GitPlatform GitPlatform GitPlatform
staticInoChan chan uint64 staticInoChan chan uint64
} }

View File

@ -2,39 +2,33 @@ package git
import ( import (
"context" "context"
"net/url" "fmt"
"os" "os"
"path/filepath" "path/filepath"
"regexp"
"strconv" "strconv"
"time" "time"
"github.com/badjware/gitlabfs/fstree"
"github.com/vmihailenco/taskq/v3" "github.com/vmihailenco/taskq/v3"
"github.com/vmihailenco/taskq/v3/memqueue" "github.com/vmihailenco/taskq/v3/memqueue"
) )
const (
CloneInit = iota
CloneClone = iota
)
type GitClonerPuller interface {
CloneOrPull(url string, pid int, defaultBranch string) (localRepoLoc string, err error)
}
type GitClientParam struct { type GitClientParam struct {
CloneLocation string CloneLocation string `yaml:"clone_location,omitempty"`
RemoteName string Remote string `yaml:"remote,omitempty"`
RemoteURL *url.URL OnClone string `yaml:"on_clone,omitempty"`
CloneMethod int AutoPull bool `yaml:"auto_pull,omitempty"`
PullDepth int Depth int `yaml:"depth,omitempty"`
AutoPull bool QueueSize int `yaml:"queue_size,omitempty"`
QueueWorkerCount int `yaml:"worker_count,omitempty"`
QueueSize int
QueueWorkerCount int
} }
type gitClient struct { type gitClient struct {
GitClientParam GitClientParam
hostnameProg *regexp.Regexp
queue taskq.Queue queue taskq.Queue
cloneTask *taskq.Task cloneTask *taskq.Task
pullTask *taskq.Task pullTask *taskq.Task
@ -46,6 +40,8 @@ func NewClient(p GitClientParam) (*gitClient, error) {
c := &gitClient{ c := &gitClient{
GitClientParam: p, GitClientParam: p,
hostnameProg: regexp.MustCompile(`([a-z0-1]+\.)+[a-z0-1]+`),
queue: queueFactory.RegisterQueue(&taskq.QueueOptions{ queue: queueFactory.RegisterQueue(&taskq.QueueOptions{
Name: "git-queue", Name: "git-queue",
MaxNumWorker: int32(p.QueueWorkerCount), MaxNumWorker: int32(p.QueueWorkerCount),
@ -68,21 +64,27 @@ func NewClient(p GitClientParam) (*gitClient, error) {
return c, nil return c, nil
} }
func (c *gitClient) getLocalRepoLoc(pid int) string { func (c *gitClient) FetchLocalRepositoryPath(source fstree.RepositorySource) (localRepoLoc string, err error) {
return filepath.Join(c.CloneLocation, c.RemoteURL.Hostname(), strconv.Itoa(pid)) rid := source.GetRepositoryID()
} cloneUrl := source.GetCloneURL()
defaultBranch := source.GetDefaultBranch()
func (c *gitClient) CloneOrPull(url string, pid int, defaultBranch string) (localRepoLoc string, err error) { // Parse the url
localRepoLoc = c.getLocalRepoLoc(pid) hostname := c.hostnameProg.FindString(cloneUrl)
if hostname == "" {
return "", fmt.Errorf("failed to match a valid hostname from \"%v\"", cloneUrl)
}
localRepoLoc = filepath.Join(c.CloneLocation, hostname, strconv.Itoa(int(rid)))
if _, err := os.Stat(localRepoLoc); os.IsNotExist(err) { if _, err := os.Stat(localRepoLoc); os.IsNotExist(err) {
// Dispatch clone msg // Dispatch clone msg
msg := c.cloneTask.WithArgs(context.Background(), url, defaultBranch, localRepoLoc) msg := c.cloneTask.WithArgs(context.Background(), cloneUrl, defaultBranch, localRepoLoc)
msg.OnceInPeriod(time.Second, pid) msg.OnceInPeriod(time.Second, rid)
c.queue.Add(msg) c.queue.Add(msg)
} else if c.AutoPull { } else if c.AutoPull {
// Dispatch pull msg // Dispatch pull msg
msg := c.pullTask.WithArgs(context.Background(), localRepoLoc, defaultBranch) msg := c.pullTask.WithArgs(context.Background(), localRepoLoc, defaultBranch)
msg.OnceInPeriod(time.Second, pid) msg.OnceInPeriod(time.Second, rid)
c.queue.Add(msg) c.queue.Add(msg)
} }
return localRepoLoc, nil return localRepoLoc, nil

View File

@ -8,7 +8,7 @@ import (
) )
func (c *gitClient) clone(url string, defaultBranch string, dst string) error { func (c *gitClient) clone(url string, defaultBranch string, dst string) error {
if c.CloneMethod == CloneInit { if c.GitClientParam.OnClone == "init" {
// "Fake" cloning the repo by never actually talking to the git server // "Fake" cloning the repo by never actually talking to the git server
// This skip a fetch operation that we would do if we where to do a proper clone // This skip a fetch operation that we would do if we where to do a proper clone
// We can save a lot of time and network i/o doing it this way, at the cost of // We can save a lot of time and network i/o doing it this way, at the cost of
@ -32,8 +32,8 @@ func (c *gitClient) clone(url string, defaultBranch string, dst string) error {
"git", "remote", "add", "git", "remote", "add",
"-m", defaultBranch, "-m", defaultBranch,
"--", "--",
c.RemoteName, // name c.GitClientParam.Remote, // name
url, // url url, // url
) )
if err != nil { if err != nil {
return fmt.Errorf("failed to setup remote %v in git repo %v: %v", url, dst, err) return fmt.Errorf("failed to setup remote %v in git repo %v: %v", url, dst, err)
@ -45,7 +45,7 @@ func (c *gitClient) clone(url string, defaultBranch string, dst string) error {
"git", "config", "--local", "git", "config", "--local",
"--", "--",
fmt.Sprintf("branch.%s.remote", defaultBranch), // key fmt.Sprintf("branch.%s.remote", defaultBranch), // key
c.RemoteName, // value c.GitClientParam.Remote, // value
) )
if err != nil { if err != nil {
@ -66,8 +66,8 @@ func (c *gitClient) clone(url string, defaultBranch string, dst string) error {
// Clone the repo // Clone the repo
_, err := utils.ExecProcess( _, err := utils.ExecProcess(
"git", "clone", "git", "clone",
"--origin", c.RemoteName, "--origin", c.GitClientParam.Remote,
"--depth", strconv.Itoa(c.PullDepth), "--depth", strconv.Itoa(c.GitClientParam.Depth),
"--", "--",
url, // repository url, // repository
dst, // directory dst, // directory

View File

@ -23,10 +23,10 @@ func (c *gitClient) pull(repoPath string, defaultBranch string) error {
_, err = utils.ExecProcessInDir( _, err = utils.ExecProcessInDir(
repoPath, // workdir repoPath, // workdir
"git", "pull", "git", "pull",
"--depth", strconv.Itoa(c.PullDepth), "--depth", strconv.Itoa(c.GitClientParam.Depth),
"--", "--",
c.RemoteName, // repository c.GitClientParam.Remote, // repository
defaultBranch, // refspec defaultBranch, // refspec
) )
if err != nil { if err != nil {
return fmt.Errorf("failed to pull git repo %v: %v", repoPath, err) return fmt.Errorf("failed to pull git repo %v: %v", repoPath, err)

40
main.go
View File

@ -3,7 +3,6 @@ package main
import ( import (
"flag" "flag"
"fmt" "fmt"
"net/url"
"os" "os"
"path/filepath" "path/filepath"
"strings" "strings"
@ -18,21 +17,12 @@ type (
Config struct { Config struct {
FS FSConfig `yaml:"fs,omitempty"` FS FSConfig `yaml:"fs,omitempty"`
Gitlab gitlab.GitlabClientConfig `yaml:"gitlab,omitempty"` Gitlab gitlab.GitlabClientConfig `yaml:"gitlab,omitempty"`
Git GitConfig `yaml:"git,omitempty"` Git git.GitClientParam `yaml:"git,omitempty"`
} }
FSConfig struct { FSConfig struct {
Mountpoint string `yaml:"mountpoint,omitempty"` Mountpoint string `yaml:"mountpoint,omitempty"`
MountOptions string `yaml:"mountoptions,omitempty"` MountOptions string `yaml:"mountoptions,omitempty"`
} }
GitConfig struct {
CloneLocation string `yaml:"clone_location,omitempty"`
Remote string `yaml:"remote,omitempty"`
OnClone string `yaml:"on_clone,omitempty"`
AutoPull bool `yaml:"auto_pull,omitempty"`
Depth int `yaml:"depth,omitempty"`
QueueSize int `yaml:"queue_size,omitempty"`
QueueWorkerCount int `yaml:"worker_count,omitempty"`
}
) )
func loadConfig(configPath string) (*Config, error) { func loadConfig(configPath string) (*Config, error) {
@ -56,7 +46,7 @@ func loadConfig(configPath string) (*Config, error) {
IncludeCurrentUser: true, IncludeCurrentUser: true,
PullMethod: "http", PullMethod: "http",
}, },
Git: GitConfig{ Git: git.GitClientParam{
CloneLocation: defaultCloneLocation, CloneLocation: defaultCloneLocation,
Remote: "origin", Remote: "origin",
OnClone: "init", OnClone: "init",
@ -93,32 +83,12 @@ func makeGitlabConfig(config *Config) (*gitlab.GitlabClientConfig, error) {
} }
func makeGitConfig(config *Config) (*git.GitClientParam, error) { func makeGitConfig(config *Config) (*git.GitClientParam, error) {
// Parse the gilab url
parsedGitlabURL, err := url.Parse(config.Gitlab.URL)
if err != nil {
return nil, err
}
// parse on_clone // parse on_clone
cloneMethod := 0 if config.Git.OnClone != "init" && config.Git.OnClone != "clone" {
if config.Git.OnClone == "init" {
cloneMethod = git.CloneInit
} else if config.Git.OnClone == "clone" {
cloneMethod = git.CloneClone
} else {
return nil, fmt.Errorf("on_clone must be either \"init\" or \"clone\"") return nil, fmt.Errorf("on_clone must be either \"init\" or \"clone\"")
} }
return &git.GitClientParam{ return &config.Git, nil
CloneLocation: config.Git.CloneLocation,
RemoteName: config.Git.Remote,
RemoteURL: parsedGitlabURL,
CloneMethod: cloneMethod,
AutoPull: config.Git.AutoPull,
PullDepth: config.Git.Depth,
QueueSize: config.Git.QueueSize,
QueueWorkerCount: config.Git.QueueWorkerCount,
}, nil
} }
func main() { func main() {
@ -181,7 +151,7 @@ func main() {
err = fstree.Start( err = fstree.Start(
mountpoint, mountpoint,
parsedMountoptions, parsedMountoptions,
&fstree.FSParam{GitImplementation: gitClient, GitPlatform: gitlabClient}, &fstree.FSParam{GitClient: gitClient, GitPlatform: gitlabClient},
*debug, *debug,
) )
if err != nil { if err != nil {