diff --git a/init.go b/init.go index ff06d6d..9969a23 100644 --- a/init.go +++ b/init.go @@ -5,6 +5,7 @@ import ( "fmt" "os" "path" + "sync" "github.com/docker/cli/cli/command" "github.com/docker/cli/cli/flags" @@ -55,17 +56,22 @@ func initRepos() (err error) { } } repos[repoName] = repo + repoLocks[repoName] = &sync.Mutex{} } return nil } func initDockerCli() (err error) { - dockerCli, err = command.NewDockerCli() + // suppress command outputs (errors are returned as objects) + nullFile, _ := os.Open("/dev/null") + defer nullFile.Close() + dockerCli, err = command.NewDockerCli(command.WithOutputStream(nullFile), command.WithErrorStream(nullFile)) if err != nil { return fmt.Errorf("could not create a docker cli object: %w", err) } err = dockerCli.Initialize(flags.NewClientOptions()) + if err != nil { return fmt.Errorf("could not initialize docker cli object: %w", err) } diff --git a/log.go b/log.go new file mode 100644 index 0000000..a1d1ff0 --- /dev/null +++ b/log.go @@ -0,0 +1,17 @@ +package main + +import ( + "log/slog" + "os" +) + +var logger *slog.Logger + +func init() { + logOptions := &slog.HandlerOptions{ + Level: slog.LevelInfo, + } + + logger = slog.New(slog.NewTextHandler(os.Stderr, logOptions)) +} + diff --git a/main.go b/main.go index c09d897..cadb388 100644 --- a/main.go +++ b/main.go @@ -2,8 +2,8 @@ package main import ( "fmt" - "log" "path" + "sync" "time" "github.com/docker/cli/cli/command/stack" @@ -12,19 +12,38 @@ import ( "github.com/go-git/go-git/v5/plumbing/transport/http" ) - +var repoLocks map[string]*sync.Mutex = make(map[string]*sync.Mutex) func main() { + logger.Info("starting SwarmCD") for { + var waitGroup sync.WaitGroup + logger.Info("updating stacks...") for stackName := range config.StackConfigs { - err := updateStack(stackName) - logIfError(err) + waitGroup.Add(1) + go updateStackThread(&waitGroup, stackName) } + waitGroup.Wait() + logger.Info("waiting for the update interval") time.Sleep(time.Duration(config.UpdateInterval) * time.Second) } } +func updateStackThread(waitGroup *sync.WaitGroup, stackName string) { + repoLock := repoLocks[config.StackConfigs[stackName].Repo] + repoLock.Lock() + defer repoLock.Unlock() + logger.Info(fmt.Sprintf("updating %s stack", stackName)) + err := updateStack(stackName) + if err != nil{ + logger.Error(err.Error()) + } else { + logger.Info(fmt.Sprintf("done updating %s stack", stackName)) + } + waitGroup.Done() +} + func updateStack(stackName string) (err error) { err = pullChanges(stackName) if err != nil && err != git.NoErrAlreadyUpToDate { @@ -33,7 +52,7 @@ func updateStack(stackName string) (err error) { stackConfig := config.StackConfigs[stackName] cmd := stack.NewStackCommand(dockerCli) cmd.SetArgs([]string{ - "deploy", "-c", + "deploy", "--detach", "-c", path.Join(config.ReposPath, stackConfig.Repo, stackConfig.ComposeFile), stackName, }) @@ -52,18 +71,20 @@ func pullChanges(stackName string) (err error) { stackConfig := config.StackConfigs[stackName] repoConfig := config.RepoConfigs[stackConfig.Repo] branch := stackConfig.Branch + // repos[stackConfig.Repo].//Branch(branch)//.Fetch(&git.FetchOptions{}) workTree, err := repos[stackConfig.Repo].Worktree() if err != nil { return fmt.Errorf("could not get %s repo worktree: %w", stackConfig.Repo, err) } err = workTree.Checkout(&git.CheckoutOptions{ - Branch: plumbing.NewBranchReferenceName(branch), + Branch: plumbing.ReferenceName("refs/remotes/origin/" + branch), }) if err != nil { return fmt.Errorf("could not checkout branch %s: %w", branch, err) } pullOptions := &git.PullOptions{ ReferenceName: plumbing.NewBranchReferenceName(branch), + RemoteName: "origin", } if repoConfig.Password != "" && repoConfig.Username != "" { pullOptions.Auth = &http.BasicAuth{ @@ -77,10 +98,3 @@ func pullChanges(stackName string) (err error) { } return } - - -func logIfError(err error) { - if err != nil { - log.Println(err) - } -} \ No newline at end of file