204 lines
5.0 KiB
Go
204 lines
5.0 KiB
Go
package sync
|
|
|
|
import (
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"time"
|
|
|
|
"git.csclub.uwaterloo.ca/public/merlin/config"
|
|
)
|
|
|
|
func cscTraceArchLinux(repo *config.Repo, newTime string) []string {
|
|
args := []string{
|
|
"curl",
|
|
"--interface", config.Conf.IPv4Address,
|
|
"-s", repo.TraceHost,
|
|
"-o", newTime,
|
|
}
|
|
|
|
return args
|
|
}
|
|
|
|
// return false iff both files exist, are non-empty, and have the same contents
|
|
func cscTraceArchLinuxDiff(repo *config.Repo, newTime string) bool {
|
|
readFile := func(file string) string {
|
|
f, err := os.ReadFile(file)
|
|
if err != nil {
|
|
repo.Logger.Debug(err.Error())
|
|
return ""
|
|
}
|
|
return strings.TrimSpace(string(f))
|
|
}
|
|
|
|
file1 := readFile(newTime)
|
|
file2 := readFile(filepath.Join(buildDownloadDir(repo), "lastupdate"))
|
|
|
|
if file1 == "" || file2 == "" {
|
|
return true
|
|
}
|
|
return file1 != file2
|
|
}
|
|
|
|
// update the lastsync file
|
|
func cscTraceArchLinuxUpdate(repo *config.Repo) (args []string) {
|
|
rsyncDir := repo.RsyncDir
|
|
localDir := repo.LocalDir
|
|
|
|
repo.RsyncDir = repo.RsyncDir + "/lastsync"
|
|
repo.LocalDir = repo.LocalDir + "/lastsync"
|
|
|
|
args = cscSyncArchLinux(repo)
|
|
|
|
repo.RsyncDir = rsyncDir
|
|
repo.LocalDir = localDir
|
|
|
|
return args
|
|
}
|
|
|
|
func cscTraceDebian(repo *config.Repo, newTime string) []string {
|
|
args := []string{
|
|
"nice", "rsync", "-tv", "-4",
|
|
"--address=" + config.Conf.IPv4Address,
|
|
"--quiet",
|
|
repo.RsyncHost + "::" + filepath.Join(repo.RsyncDir, "project/trace", repo.TraceHost),
|
|
newTime,
|
|
}
|
|
|
|
return args
|
|
}
|
|
|
|
// return false iff both files exist and were modified at the same time
|
|
func cscTraceDebianDiff(repo *config.Repo, newTime string) bool {
|
|
statFile := func(file string) int64 {
|
|
f, err := os.Stat(file)
|
|
if err != nil {
|
|
repo.Logger.Debug("Error while trying to stat file: " + file)
|
|
return 0
|
|
}
|
|
|
|
return f.ModTime().Unix()
|
|
}
|
|
|
|
file1 := statFile(newTime)
|
|
file2 := statFile(filepath.Join(buildDownloadDir(repo), "project/trace", repo.TraceHost))
|
|
|
|
if file1 == 0 || file2 == 0 {
|
|
return true
|
|
}
|
|
|
|
return file1 != file2
|
|
}
|
|
|
|
// update our trace file's modification date by writing the current time
|
|
func cscTraceDebianUpdate(repo *config.Repo) {
|
|
target := filepath.Join(buildDownloadDir(repo), "project/trace", config.Conf.Hostname)
|
|
|
|
os.MkdirAll(filepath.Join(buildDownloadDir(repo), "project/trace"), 0755)
|
|
|
|
f, err := os.OpenFile(target, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644)
|
|
if err != nil {
|
|
repo.Logger.Error("Unable to open trace file: " + target)
|
|
return
|
|
}
|
|
if _, err = f.WriteString(time.Now().UTC().Format(time.RFC1123)); err != nil {
|
|
repo.Logger.Error("Unable to write to trace file: " + target)
|
|
return
|
|
}
|
|
}
|
|
|
|
// some repos want us to check a last modification time file before performing a full sync
|
|
// returns a status to update the original sync's status along with if the sync should continue
|
|
func checkIfSyncNeeded(repo *config.Repo) (status int, continueSync bool) {
|
|
// default return values are the default status and to continue the sync
|
|
status = config.FAILURE
|
|
continueSync = true
|
|
if repo.DryRun {
|
|
return
|
|
}
|
|
runCommand := func(args []string) int {
|
|
ch := spawnProcess(repo, args)
|
|
if ch == nil {
|
|
// spawnSyncProcess will have already logged error
|
|
return 1
|
|
}
|
|
cmd := <-ch
|
|
return cmd.ProcessState.ExitCode()
|
|
}
|
|
|
|
// create a temp file
|
|
temp, err := os.CreateTemp("/tmp", "merlin-"+repo.Name+"-trace-*")
|
|
if err != nil {
|
|
repo.Logger.Error(err.Error())
|
|
return
|
|
}
|
|
temp.Close()
|
|
defer os.Remove(temp.Name())
|
|
|
|
// get the trace command to copy a last updated file
|
|
var args []string
|
|
switch repo.SyncType {
|
|
case "csc-sync-archlinux":
|
|
args = cscTraceArchLinux(repo, temp.Name())
|
|
case "csc-sync-debian":
|
|
args = cscTraceDebian(repo, temp.Name())
|
|
default:
|
|
repo.Logger.Error("Trace files are not implemented for sync type '" + repo.SyncType + "'")
|
|
return
|
|
}
|
|
|
|
exit := runCommand(args)
|
|
if exit != 0 {
|
|
// if the process was terminated then don't continue to sync
|
|
if exit == -1 {
|
|
return config.TERMINATED, false
|
|
}
|
|
return
|
|
}
|
|
|
|
filesDiffer := true
|
|
switch repo.SyncType {
|
|
case "csc-sync-archlinux":
|
|
filesDiffer = cscTraceArchLinuxDiff(repo, temp.Name())
|
|
case "csc-sync-debian":
|
|
filesDiffer = cscTraceDebianDiff(repo, temp.Name())
|
|
}
|
|
// debian still syncs even if the trace file is the same
|
|
if !filesDiffer && repo.SyncType == "csc-sync-debian" {
|
|
repo.Logger.Error("trace file for " + repo.RsyncHost + " unchanged")
|
|
return
|
|
}
|
|
if filesDiffer {
|
|
// continue sync if files differ
|
|
return
|
|
}
|
|
|
|
repo.Logger.Debug("No changes found; full sync will not be made")
|
|
|
|
// archlinux wants to sync a "lastsync" file if lastupdate prevents sync
|
|
switch repo.SyncType {
|
|
case "csc-sync-archlinux":
|
|
args = cscTraceArchLinuxUpdate(repo)
|
|
default:
|
|
return config.SUCCESS, false
|
|
}
|
|
|
|
exit = runCommand(args)
|
|
if exit != 0 {
|
|
// if the process was terminated then don't continue to sync
|
|
if exit == -1 {
|
|
return config.TERMINATED, false
|
|
}
|
|
return
|
|
}
|
|
|
|
return config.SUCCESS, false
|
|
}
|
|
|
|
// csc-sync-debian wants to update a trace file after the repo is done syncing
|
|
func postSyncTraceUpdate(repo *config.Repo) {
|
|
if repo.SyncType == "csc-sync-debian" {
|
|
cscTraceDebianUpdate(repo)
|
|
}
|
|
}
|