k3s/contrib/git-sync/main.go

146 lines
3.9 KiB
Go
Raw Normal View History

2014-12-22 21:13:47 +00:00
package main // import "github.com/GoogleCloudPlatform/kubernetes/git-sync"
import (
"flag"
2014-12-23 21:54:29 +00:00
"fmt"
2014-12-22 21:13:47 +00:00
"log"
"net/http"
"os"
"os/exec"
"path"
"strings"
"time"
)
var interval = flag.String("interval", env("GIT_SYNC_INTERVAL", "60s"), "git pull interval")
var repo = flag.String("repo", env("GIT_SYNC_REPO", ""), "git repo url")
var branch = flag.String("branch", env("GIT_SYNC_BRANCH", "master"), "git branch")
var handler = flag.String("handler", env("GIT_SYNC_HANDLER", "/"), "web hook handler")
var dest = flag.String("dest", env("GIT_SYNC_DEST", ""), "destination path")
2014-12-22 21:13:47 +00:00
func env(key, def string) string {
if env := os.Getenv(key); env != "" {
return env
}
return def
}
const usage = "usage: GIT_SYNC_REPO= GIT_SYNC_DEST= [GIT_SYNC_INTERVAL= GIT_SYNC_BRANCH= GIT_SYNC_HANDLER=] git-sync -repo GIT_REPO_URL -dest PATH [-interval -branch -handler]"
2014-12-22 21:13:47 +00:00
func main() {
flag.Parse()
if *repo == "" || *dest == "" {
flag.Usage()
log.Fatal(usage)
}
pullInterval, err := time.ParseDuration(*interval)
if err != nil {
log.Fatalf("error parsing time duration %q: %v", *interval, err)
}
if _, err := exec.LookPath("git"); err != nil {
log.Fatalf("required git executable not found: %v", err)
}
2014-12-23 21:54:29 +00:00
repo, err := NewRepo()
if err != nil {
log.Fatalf("error creating repo: %v", err)
}
syncc := make(chan struct{})
tick := time.Tick(pullInterval)
2014-12-22 21:13:47 +00:00
go func() {
2014-12-23 21:54:29 +00:00
for {
repo.Sync()
select {
case <-tick:
case <-syncc:
}
2014-12-22 21:13:47 +00:00
}
}()
http.HandleFunc(*handler, func(w http.ResponseWriter, r *http.Request) {
2014-12-23 21:54:29 +00:00
syncc <- struct{}{}
2014-12-22 21:13:47 +00:00
})
log.Fatal(http.ListenAndServe(":8080", nil))
}
2014-12-23 21:54:29 +00:00
type Repo struct {
basePath string
mirrorPath string
lastRev string
}
func NewRepo() (*Repo, error) {
mirrorRepoPath := path.Join(*dest, ".git")
_, err := os.Stat(mirrorRepoPath)
if err == nil {
log.Printf("found existing mirror repo %q", mirrorRepoPath)
return &Repo{
basePath: *dest,
mirrorPath: mirrorRepoPath,
}, nil
}
if !os.IsNotExist(err) {
return nil, fmt.Errorf("error checking repo %q: %v", mirrorRepoPath, err)
2014-12-22 21:13:47 +00:00
}
2014-12-23 21:54:29 +00:00
cmd := exec.Command("git", "clone", "--mirror", "-b", *branch, *repo, mirrorRepoPath)
2014-12-22 21:13:47 +00:00
output, err := cmd.CombinedOutput()
if err != nil {
2014-12-23 21:54:29 +00:00
return nil, fmt.Errorf("error cloning repo %q: %v:", strings.Join(cmd.Args, " "), err)
}
log.Printf("clone %q: %s", *repo, string(output))
return &Repo{
basePath: *dest,
mirrorPath: mirrorRepoPath,
}, nil
}
func (r *Repo) Sync() {
cmd := exec.Command("git", "fetch", "origin", *branch)
cmd.Dir = r.mirrorPath
output, err := cmd.CombinedOutput()
if err != nil {
log.Printf("error running command %q: %v", strings.Join(cmd.Args, " "), err)
return
}
log.Printf("fetch: %s", string(output))
cmd = exec.Command("git", "rev-parse", "HEAD")
cmd.Dir = r.mirrorPath
output, err = cmd.CombinedOutput()
if err != nil {
log.Printf("error running command %q: %v", strings.Join(cmd.Args, " "), err)
return
}
rev := strings.TrimSpace(string(output))
if rev == r.lastRev {
log.Printf("no new rev since last check %q", rev)
return
}
r.lastRev = rev
log.Printf("HEAD is: %q", rev)
repoPath := path.Join(r.basePath, rev)
_, err = os.Stat(repoPath)
if err == nil {
log.Printf("found existing repo: %q", repoPath)
return
}
if !os.IsNotExist(err) {
log.Printf("error stating repo %q: %v", repoPath, err)
return
}
cmd = exec.Command("git", "clone", r.mirrorPath, repoPath)
output, err = cmd.CombinedOutput()
if err != nil {
log.Printf("error running command %q : %v", strings.Join(cmd.Args, " "), err)
return
}
log.Printf("clone %q: %v", repoPath, string(output))
tempPath := path.Join(r.basePath, "HEAD."+rev)
if err := os.Symlink(rev, tempPath); err != nil {
log.Printf("error creating temporary symlink %q: %v", tempPath, err)
return
}
linkPath := path.Join(r.basePath, "HEAD")
if err := os.Rename(tempPath, linkPath); err != nil {
log.Printf("error moving symlink %q: %v", linkPath, err)
2014-12-22 21:13:47 +00:00
return
}
}