Merge pull request #331 from nan-yu/release-3.x
Export the error details to an error file
This commit is contained in:
commit
448f4b412c
|
|
@ -21,6 +21,7 @@ package main // import "k8s.io/git-sync/cmd/git-sync"
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
|
"encoding/json"
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
|
@ -37,6 +38,7 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/go-logr/glogr"
|
"github.com/go-logr/glogr"
|
||||||
|
"github.com/go-logr/logr"
|
||||||
"github.com/prometheus/client_golang/prometheus"
|
"github.com/prometheus/client_golang/prometheus"
|
||||||
"github.com/prometheus/client_golang/prometheus/promhttp"
|
"github.com/prometheus/client_golang/prometheus/promhttp"
|
||||||
"k8s.io/git-sync/pkg/pid1"
|
"k8s.io/git-sync/pkg/pid1"
|
||||||
|
|
@ -60,6 +62,8 @@ var flRoot = flag.String("root", envString("GIT_SYNC_ROOT", envString("HOME", ""
|
||||||
"the root directory for git-sync operations, under which --dest will be created")
|
"the root directory for git-sync operations, under which --dest will be created")
|
||||||
var flDest = flag.String("dest", envString("GIT_SYNC_DEST", ""),
|
var flDest = flag.String("dest", envString("GIT_SYNC_DEST", ""),
|
||||||
"the name of (a symlink to) a directory in which to check-out files under --root (defaults to the leaf dir of --repo)")
|
"the name of (a symlink to) a directory in which to check-out files under --root (defaults to the leaf dir of --repo)")
|
||||||
|
var flErrorFile = flag.String("error-file", envString("GIT_SYNC_ERROR_FILE", ""),
|
||||||
|
"the name of a file into which errors will be written under --root (defaults to \"\", disabling error reporting)")
|
||||||
var flWait = flag.Float64("wait", envFloat("GIT_SYNC_WAIT", 1),
|
var flWait = flag.Float64("wait", envFloat("GIT_SYNC_WAIT", 1),
|
||||||
"the number of seconds between syncs")
|
"the number of seconds between syncs")
|
||||||
var flSyncTimeout = flag.Int("timeout", envInt("GIT_SYNC_TIMEOUT", 120),
|
var flSyncTimeout = flag.Int("timeout", envInt("GIT_SYNC_TIMEOUT", 120),
|
||||||
|
|
@ -119,7 +123,7 @@ var flHTTPMetrics = flag.Bool("http-metrics", envBool("GIT_SYNC_HTTP_METRICS", t
|
||||||
var flHTTPprof = flag.Bool("http-pprof", envBool("GIT_SYNC_HTTP_PPROF", false),
|
var flHTTPprof = flag.Bool("http-pprof", envBool("GIT_SYNC_HTTP_PPROF", false),
|
||||||
"enable the pprof debug endpoints on git-sync's HTTP endpoint")
|
"enable the pprof debug endpoints on git-sync's HTTP endpoint")
|
||||||
|
|
||||||
var log = glogr.New()
|
var log *customLogger
|
||||||
|
|
||||||
// Total pull/error, summary on pull duration
|
// Total pull/error, summary on pull duration
|
||||||
var (
|
var (
|
||||||
|
|
@ -155,6 +159,90 @@ const (
|
||||||
submodulesOff = "off"
|
submodulesOff = "off"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type customLogger struct {
|
||||||
|
logr.Logger
|
||||||
|
errorFile string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l customLogger) Error(err error, msg string, kvList ...interface{}) {
|
||||||
|
l.Logger.Error(err, msg, kvList...)
|
||||||
|
if l.errorFile == "" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
payload := struct {
|
||||||
|
Msg string
|
||||||
|
Err string
|
||||||
|
Args map[string]interface{}
|
||||||
|
}{
|
||||||
|
Msg: msg,
|
||||||
|
Err: err.Error(),
|
||||||
|
Args: map[string]interface{}{},
|
||||||
|
}
|
||||||
|
if len(kvList)%2 != 0 {
|
||||||
|
kvList = append(kvList, "<no-value>")
|
||||||
|
}
|
||||||
|
for i := 0; i < len(kvList); i += 2 {
|
||||||
|
k, ok := kvList[i].(string)
|
||||||
|
if !ok {
|
||||||
|
k = fmt.Sprintf("%v", kvList[i])
|
||||||
|
}
|
||||||
|
payload.Args[k] = kvList[i+1]
|
||||||
|
}
|
||||||
|
jb, err := json.Marshal(payload)
|
||||||
|
if err != nil {
|
||||||
|
l.Logger.Error(err, "can't encode error payload")
|
||||||
|
content := fmt.Sprintf("%v", err)
|
||||||
|
l.writeContent([]byte(content))
|
||||||
|
} else {
|
||||||
|
l.writeContent(jb)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// exportError exports the error to the error file if --export-error is enabled.
|
||||||
|
func (l *customLogger) exportError(content string) {
|
||||||
|
if l.errorFile == "" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
l.writeContent([]byte(content))
|
||||||
|
}
|
||||||
|
|
||||||
|
// writeContent writes the error content to the error file.
|
||||||
|
func (l *customLogger) writeContent(content []byte) {
|
||||||
|
tmpFile, err := ioutil.TempFile(*flRoot, "tmp-err-")
|
||||||
|
if err != nil {
|
||||||
|
l.Logger.Error(err, "can't create temporary error-file", "directory", *flRoot, "prefix", "tmp-err-")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
if err := tmpFile.Close(); err != nil {
|
||||||
|
l.Logger.Error(err, "can't close temporary error-file", "filename", tmpFile.Name())
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
if _, err = tmpFile.Write(content); err != nil {
|
||||||
|
l.Logger.Error(err, "can't write to temporary error-file", "filename", tmpFile.Name())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := os.Rename(tmpFile.Name(), l.errorFile); err != nil {
|
||||||
|
l.Logger.Error(err, "can't rename to error-file", "temp-file", tmpFile.Name(), "error-file", l.errorFile)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// deleteErrorFile deletes the error file.
|
||||||
|
func (l *customLogger) deleteErrorFile() {
|
||||||
|
if l.errorFile == "" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err := os.Remove(l.errorFile); err != nil {
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
l.Logger.Error(err, "can't delete the error-file", "filename", l.errorFile)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
prometheus.MustRegister(syncDuration)
|
prometheus.MustRegister(syncDuration)
|
||||||
prometheus.MustRegister(syncCount)
|
prometheus.MustRegister(syncCount)
|
||||||
|
|
@ -184,7 +272,7 @@ func envInt(key string, def int) int {
|
||||||
if env := os.Getenv(key); env != "" {
|
if env := os.Getenv(key); env != "" {
|
||||||
val, err := strconv.ParseInt(env, 0, 0)
|
val, err := strconv.ParseInt(env, 0, 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error(err, "invalid env value, using default", "key", key, "val", os.Getenv(key), "default", def)
|
fmt.Fprintf(os.Stderr, "WARNING: invalid env value (%v): using default, key=%s, val=%q, default=%d\n", err, key, env, def)
|
||||||
return def
|
return def
|
||||||
}
|
}
|
||||||
return int(val)
|
return int(val)
|
||||||
|
|
@ -196,7 +284,7 @@ func envFloat(key string, def float64) float64 {
|
||||||
if env := os.Getenv(key); env != "" {
|
if env := os.Getenv(key); env != "" {
|
||||||
val, err := strconv.ParseFloat(env, 64)
|
val, err := strconv.ParseFloat(env, 64)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error(err, "invalid env value, using default", "key", key, "val", os.Getenv(key), "default", def)
|
fmt.Fprintf(os.Stderr, "WARNING: invalid env value (%v): using default, key=%s, val=%q, default=%f\n", err, key, env, def)
|
||||||
return def
|
return def
|
||||||
}
|
}
|
||||||
return val
|
return val
|
||||||
|
|
@ -208,7 +296,7 @@ func envDuration(key string, def time.Duration) time.Duration {
|
||||||
if env := os.Getenv(key); env != "" {
|
if env := os.Getenv(key); env != "" {
|
||||||
val, err := time.ParseDuration(env)
|
val, err := time.ParseDuration(env)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error(err, "invalid env value, using default", "key", key, "val", os.Getenv(key), "default", def)
|
fmt.Fprintf(os.Stderr, "WARNING: invalid env value (%v): using default, key=%s, val=%q, default=%d\n", err, key, env, def)
|
||||||
return def
|
return def
|
||||||
}
|
}
|
||||||
return val
|
return val
|
||||||
|
|
@ -220,8 +308,7 @@ func setFlagDefaults() {
|
||||||
// Force logging to stderr (from glog).
|
// Force logging to stderr (from glog).
|
||||||
stderrFlag := flag.Lookup("logtostderr")
|
stderrFlag := flag.Lookup("logtostderr")
|
||||||
if stderrFlag == nil {
|
if stderrFlag == nil {
|
||||||
fmt.Fprintf(os.Stderr, "ERROR: can't find flag 'logtostderr'\n")
|
handleError(false, "ERROR: can't find flag 'logtostderr'")
|
||||||
os.Exit(1)
|
|
||||||
}
|
}
|
||||||
stderrFlag.Value.Set("true")
|
stderrFlag.Value.Set("true")
|
||||||
}
|
}
|
||||||
|
|
@ -241,35 +328,33 @@ func main() {
|
||||||
setFlagDefaults()
|
setFlagDefaults()
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
||||||
|
var errorFile string
|
||||||
|
if *flErrorFile != "" {
|
||||||
|
errorFile = filepath.Join(*flRoot, *flErrorFile)
|
||||||
|
}
|
||||||
|
log = &customLogger{glogr.New(), errorFile}
|
||||||
|
|
||||||
if *flVer {
|
if *flVer {
|
||||||
fmt.Println(version.VERSION)
|
fmt.Println(version.VERSION)
|
||||||
os.Exit(0)
|
os.Exit(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
if *flRepo == "" {
|
if *flRepo == "" {
|
||||||
fmt.Fprintf(os.Stderr, "ERROR: --repo must be specified\n")
|
handleError(true, "ERROR: --repo must be specified")
|
||||||
flag.Usage()
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if *flDepth < 0 { // 0 means "no limit"
|
if *flDepth < 0 { // 0 means "no limit"
|
||||||
fmt.Fprintf(os.Stderr, "ERROR: --depth must be greater than or equal to 0\n")
|
handleError(true, "ERROR: --depth must be greater than or equal to 0")
|
||||||
flag.Usage()
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
switch *flSubmodules {
|
switch *flSubmodules {
|
||||||
case submodulesRecursive, submodulesShallow, submodulesOff:
|
case submodulesRecursive, submodulesShallow, submodulesOff:
|
||||||
default:
|
default:
|
||||||
fmt.Fprintf(os.Stderr, "ERROR: --submodules must be one of %q, %q, or %q", submodulesRecursive, submodulesShallow, submodulesOff)
|
handleError(true, "ERROR: --submodules must be one of %q, %q, or %q", submodulesRecursive, submodulesShallow, submodulesOff)
|
||||||
flag.Usage()
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if *flRoot == "" {
|
if *flRoot == "" {
|
||||||
fmt.Fprintf(os.Stderr, "ERROR: --root must be specified\n")
|
handleError(true, "ERROR: --root must be specified")
|
||||||
flag.Usage()
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if *flDest == "" {
|
if *flDest == "" {
|
||||||
|
|
@ -278,81 +363,59 @@ func main() {
|
||||||
}
|
}
|
||||||
|
|
||||||
if strings.Contains(*flDest, "/") {
|
if strings.Contains(*flDest, "/") {
|
||||||
fmt.Fprintf(os.Stderr, "ERROR: --dest must be a leaf name, not a path\n")
|
handleError(true, "ERROR: --dest must be a leaf name, not a path")
|
||||||
flag.Usage()
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if *flWait < 0 {
|
if *flWait < 0 {
|
||||||
fmt.Fprintf(os.Stderr, "ERROR: --wait must be greater than or equal to 0\n")
|
handleError(true, "ERROR: --wait must be greater than or equal to 0")
|
||||||
flag.Usage()
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if *flSyncTimeout < 0 {
|
if *flSyncTimeout < 0 {
|
||||||
fmt.Fprintf(os.Stderr, "ERROR: --timeout must be greater than 0\n")
|
handleError(true, "ERROR: --timeout must be greater than 0")
|
||||||
flag.Usage()
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if *flWebhookURL != "" {
|
if *flWebhookURL != "" {
|
||||||
if *flWebhookStatusSuccess < -1 {
|
if *flWebhookStatusSuccess < -1 {
|
||||||
fmt.Fprintf(os.Stderr, "ERROR: --webhook-success-status must be a valid HTTP code or -1\n")
|
handleError(true, "ERROR: --webhook-success-status must be a valid HTTP code or -1")
|
||||||
flag.Usage()
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
}
|
||||||
if *flWebhookTimeout < time.Second {
|
if *flWebhookTimeout < time.Second {
|
||||||
fmt.Fprintf(os.Stderr, "ERROR: --webhook-timeout must be at least 1s\n")
|
handleError(true, "ERROR: --webhook-timeout must be at least 1s")
|
||||||
flag.Usage()
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
}
|
||||||
if *flWebhookBackoff < time.Second {
|
if *flWebhookBackoff < time.Second {
|
||||||
fmt.Fprintf(os.Stderr, "ERROR: --webhook-backoff must be at least 1s\n")
|
handleError(true, "ERROR: --webhook-backoff must be at least 1s")
|
||||||
flag.Usage()
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := exec.LookPath(*flGitCmd); err != nil {
|
if _, err := exec.LookPath(*flGitCmd); err != nil {
|
||||||
fmt.Fprintf(os.Stderr, "ERROR: git executable %q not found: %v\n", *flGitCmd, err)
|
handleError(false, "ERROR: git executable %q not found: %v", *flGitCmd, err)
|
||||||
os.Exit(1)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if *flSSH {
|
if *flSSH {
|
||||||
if *flUsername != "" {
|
if *flUsername != "" {
|
||||||
fmt.Fprintf(os.Stderr, "ERROR: only one of --ssh and --username may be specified\n")
|
handleError(false, "ERROR: only one of --ssh and --username may be specified")
|
||||||
os.Exit(1)
|
|
||||||
}
|
}
|
||||||
if *flPassword != "" {
|
if *flPassword != "" {
|
||||||
fmt.Fprintf(os.Stderr, "ERROR: only one of --ssh and --password may be specified\n")
|
handleError(false, "ERROR: only one of --ssh and --password may be specified")
|
||||||
os.Exit(1)
|
|
||||||
}
|
}
|
||||||
if *flAskPassURL != "" {
|
if *flAskPassURL != "" {
|
||||||
fmt.Fprintf(os.Stderr, "ERROR: only one of --ssh and --askpass-url may be specified\n")
|
handleError(false, "ERROR: only one of --ssh and --askpass-url may be specified")
|
||||||
os.Exit(1)
|
|
||||||
}
|
}
|
||||||
if *flCookieFile {
|
if *flCookieFile {
|
||||||
fmt.Fprintf(os.Stderr, "ERROR: only one of --ssh and --cookie-file may be specified\n")
|
handleError(false, "ERROR: only one of --ssh and --cookie-file may be specified")
|
||||||
os.Exit(1)
|
|
||||||
}
|
}
|
||||||
if *flSSHKeyFile == "" {
|
if *flSSHKeyFile == "" {
|
||||||
fmt.Fprintf(os.Stderr, "ERROR: --ssh-key-file must be specified when --ssh is specified\n")
|
handleError(true, "ERROR: --ssh-key-file must be specified when --ssh is specified")
|
||||||
flag.Usage()
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
}
|
||||||
if *flSSHKnownHosts {
|
if *flSSHKnownHosts {
|
||||||
if *flSSHKnownHostsFile == "" {
|
if *flSSHKnownHostsFile == "" {
|
||||||
fmt.Fprintf(os.Stderr, "ERROR: --ssh-known-hosts-file must be specified when --ssh-known-hosts is specified\n")
|
handleError(true, "ERROR: --ssh-known-hosts-file must be specified when --ssh-known-hosts is specified")
|
||||||
flag.Usage()
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if *flAddUser {
|
if *flAddUser {
|
||||||
if err := addUser(); err != nil {
|
if err := addUser(); err != nil {
|
||||||
fmt.Fprintf(os.Stderr, "ERROR: can't write to /etc/passwd: %v\n", err)
|
handleError(false, "ERROR: can't write to /etc/passwd: %v", err)
|
||||||
os.Exit(1)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -362,30 +425,26 @@ func main() {
|
||||||
|
|
||||||
if *flUsername != "" && *flPassword != "" {
|
if *flUsername != "" && *flPassword != "" {
|
||||||
if err := setupGitAuth(ctx, *flUsername, *flPassword, *flRepo); err != nil {
|
if err := setupGitAuth(ctx, *flUsername, *flPassword, *flRepo); err != nil {
|
||||||
fmt.Fprintf(os.Stderr, "ERROR: can't create .netrc file: %v\n", err)
|
handleError(false, "ERROR: can't create .netrc file: %v", err)
|
||||||
os.Exit(1)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if *flSSH {
|
if *flSSH {
|
||||||
if err := setupGitSSH(*flSSHKnownHosts); err != nil {
|
if err := setupGitSSH(*flSSHKnownHosts); err != nil {
|
||||||
fmt.Fprintf(os.Stderr, "ERROR: can't configure SSH: %v\n", err)
|
handleError(false, "ERROR: can't configure SSH: %v", err)
|
||||||
os.Exit(1)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if *flCookieFile {
|
if *flCookieFile {
|
||||||
if err := setupGitCookieFile(ctx); err != nil {
|
if err := setupGitCookieFile(ctx); err != nil {
|
||||||
fmt.Fprintf(os.Stderr, "ERROR: can't set git cookie file: %v\n", err)
|
handleError(false, "ERROR: can't set git cookie file: %v", err)
|
||||||
os.Exit(1)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if *flAskPassURL != "" {
|
if *flAskPassURL != "" {
|
||||||
if err := callGitAskPassURL(ctx, *flAskPassURL); err != nil {
|
if err := callGitAskPassURL(ctx, *flAskPassURL); err != nil {
|
||||||
askpassCount.WithLabelValues(metricKeyError).Inc()
|
askpassCount.WithLabelValues(metricKeyError).Inc()
|
||||||
fmt.Fprintf(os.Stderr, "ERROR: failed to call ASKPASS callback URL: %v\n", err)
|
handleError(false, "ERROR: failed to call ASKPASS callback URL: %v", err)
|
||||||
os.Exit(1)
|
|
||||||
}
|
}
|
||||||
askpassCount.WithLabelValues(metricKeySuccess).Inc()
|
askpassCount.WithLabelValues(metricKeySuccess).Inc()
|
||||||
}
|
}
|
||||||
|
|
@ -404,8 +463,7 @@ func main() {
|
||||||
if *flHTTPBind != "" {
|
if *flHTTPBind != "" {
|
||||||
ln, err := net.Listen("tcp", *flHTTPBind)
|
ln, err := net.Listen("tcp", *flHTTPBind)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Fprintf(os.Stderr, "ERROR: unable to bind HTTP endpoint: %v\n", err)
|
handleError(false, "ERROR: unable to bind HTTP endpoint: %v", err)
|
||||||
os.Exit(1)
|
|
||||||
}
|
}
|
||||||
mux := http.NewServeMux()
|
mux := http.NewServeMux()
|
||||||
go func() {
|
go func() {
|
||||||
|
|
@ -480,6 +538,7 @@ func main() {
|
||||||
|
|
||||||
if initialSync {
|
if initialSync {
|
||||||
if *flOneTime {
|
if *flOneTime {
|
||||||
|
log.deleteErrorFile()
|
||||||
os.Exit(0)
|
os.Exit(0)
|
||||||
}
|
}
|
||||||
if isHash, err := revIsHash(ctx, *flRev, *flRoot); err != nil {
|
if isHash, err := revIsHash(ctx, *flRev, *flRoot); err != nil {
|
||||||
|
|
@ -487,12 +546,14 @@ func main() {
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
} else if isHash {
|
} else if isHash {
|
||||||
log.V(0).Info("rev appears to be a git hash, no further sync needed", "rev", *flRev)
|
log.V(0).Info("rev appears to be a git hash, no further sync needed", "rev", *flRev)
|
||||||
|
log.deleteErrorFile()
|
||||||
sleepForever()
|
sleepForever()
|
||||||
}
|
}
|
||||||
initialSync = false
|
initialSync = false
|
||||||
}
|
}
|
||||||
|
|
||||||
failCount = 0
|
failCount = 0
|
||||||
|
log.deleteErrorFile()
|
||||||
log.V(1).Info("next sync", "wait_time", waitTime(*flWait))
|
log.V(1).Info("next sync", "wait_time", waitTime(*flWait))
|
||||||
cancel()
|
cancel()
|
||||||
time.Sleep(waitTime(*flWait))
|
time.Sleep(waitTime(*flWait))
|
||||||
|
|
@ -517,6 +578,18 @@ func sleepForever() {
|
||||||
os.Exit(0)
|
os.Exit(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// handleError prints the error to the standard error, prints the usage if the `printUsage` flag is true,
|
||||||
|
// exports the error to the error file and exits the process with the exit code.
|
||||||
|
func handleError(printUsage bool, format string, a ...interface{}) {
|
||||||
|
s := fmt.Sprintf(format, a...)
|
||||||
|
fmt.Fprintln(os.Stderr, s)
|
||||||
|
if printUsage {
|
||||||
|
flag.Usage()
|
||||||
|
}
|
||||||
|
log.exportError(s)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
// Put the current UID/GID into /etc/passwd so SSH can look it up. This
|
// Put the current UID/GID into /etc/passwd so SSH can look it up. This
|
||||||
// assumes that we have the permissions to write to it.
|
// assumes that we have the permissions to write to it.
|
||||||
func addUser() error {
|
func addUser() error {
|
||||||
|
|
|
||||||
47
test_e2e.sh
47
test_e2e.sh
|
|
@ -59,6 +59,13 @@ function assert_file_eq() {
|
||||||
fail "file $1 does not contain '$2': $(cat $1)"
|
fail "file $1 does not contain '$2': $(cat $1)"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function assert_file_contains() {
|
||||||
|
if grep -q "$2" "$1"; then
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
fail "file $1 does not contain '$2': $(cat $1)"
|
||||||
|
}
|
||||||
|
|
||||||
# Helper: run a docker container.
|
# Helper: run a docker container.
|
||||||
function docker_run() {
|
function docker_run() {
|
||||||
docker run \
|
docker run \
|
||||||
|
|
@ -1226,3 +1233,43 @@ pass
|
||||||
echo
|
echo
|
||||||
echo "cleaning up $DIR"
|
echo "cleaning up $DIR"
|
||||||
rm -rf "$DIR"
|
rm -rf "$DIR"
|
||||||
|
|
||||||
|
##############################################
|
||||||
|
# Test export-error
|
||||||
|
##############################################
|
||||||
|
testcase "export-error"
|
||||||
|
echo "$TESTCASE" > "$REPO"/file
|
||||||
|
git -C "$REPO" commit -qam "$TESTCASE"
|
||||||
|
(
|
||||||
|
set +o errexit
|
||||||
|
GIT_SYNC \
|
||||||
|
--repo="file://$REPO" \
|
||||||
|
--branch=does-not-exit \
|
||||||
|
--root="$ROOT" \
|
||||||
|
--dest="link" \
|
||||||
|
--error-file="error.json" \
|
||||||
|
> "$DIR"/log."$TESTCASE" 2>&1
|
||||||
|
RET=$?
|
||||||
|
if [[ "$RET" != 1 ]]; then
|
||||||
|
fail "expected exit code 1, got $RET"
|
||||||
|
fi
|
||||||
|
assert_file_absent "$ROOT"/link
|
||||||
|
assert_file_absent "$ROOT"/link/file
|
||||||
|
assert_file_contains "$ROOT"/error.json "Remote branch does-not-exit not found in upstream origin"
|
||||||
|
)
|
||||||
|
|
||||||
|
# the error.json file should be removed if sync succeeds.
|
||||||
|
GIT_SYNC \
|
||||||
|
--one-time \
|
||||||
|
--repo="file://$REPO" \
|
||||||
|
--branch=e2e-branch \
|
||||||
|
--root="$ROOT" \
|
||||||
|
--dest="link" \
|
||||||
|
--error-file="error.json" \
|
||||||
|
> "$DIR"/log."$TESTCASE" 2>&1
|
||||||
|
assert_link_exists "$ROOT"/link
|
||||||
|
assert_file_exists "$ROOT"/link/file
|
||||||
|
assert_file_eq "$ROOT"/link/file "$TESTCASE"
|
||||||
|
assert_file_absent "$ROOT"/error.json
|
||||||
|
# Wrap up
|
||||||
|
pass
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue