Add cli option sync-on-signal to trigger git-sync with a signal. (#664)

Operation:
git-sync --repo https://github.com/kubernetes/kubernetes --sync-on-signal SIGHUP
git-sync --repo https://github.com/kubernetes/kubernetes --sync-on-signal HUP
git-sync --repo https://github.com/kubernetes/kubernetes --sync-on-signal 1

Signals can be sent to docker containers with cmd:
docker kill -signal SIGHUP <Container ID>
This commit is contained in:
Timothy Rule 2023-02-15 21:00:22 +01:00 committed by GitHub
parent f7ff396716
commit 2b3f1bcdd7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 156 additions and 1 deletions

View File

@ -35,11 +35,13 @@ import (
"strconv" "strconv"
"strings" "strings"
"sync" "sync"
"syscall"
"time" "time"
"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"
"github.com/spf13/pflag" "github.com/spf13/pflag"
"golang.org/x/sys/unix"
"k8s.io/git-sync/pkg/cmd" "k8s.io/git-sync/pkg/cmd"
"k8s.io/git-sync/pkg/hook" "k8s.io/git-sync/pkg/hook"
"k8s.io/git-sync/pkg/logging" "k8s.io/git-sync/pkg/logging"
@ -77,6 +79,8 @@ var flSyncTimeout = pflag.Duration("sync-timeout", envDuration("GIT_SYNC_SYNC_TI
"the total time allowed for one complete sync, must be >= 10ms; --timeout overrides this") "the total time allowed for one complete sync, must be >= 10ms; --timeout overrides this")
var flOneTime = pflag.Bool("one-time", envBool("GIT_SYNC_ONE_TIME", false), var flOneTime = pflag.Bool("one-time", envBool("GIT_SYNC_ONE_TIME", false),
"exit after the first sync") "exit after the first sync")
var flSyncOnSignal = pflag.String("sync-on-signal", envString("GIT_SYNC_SYNC_ON_SIGNAL", ""),
"sync on receipt of the specified signal (e.g. SIGHUP)")
var flMaxFailures = pflag.Int("max-failures", envInt("GIT_SYNC_MAX_FAILURES", 0), var flMaxFailures = pflag.Int("max-failures", envInt("GIT_SYNC_MAX_FAILURES", 0),
"the number of consecutive failures allowed before aborting (the first sync must succeed, -1 will retry forever") "the number of consecutive failures allowed before aborting (the first sync must succeed, -1 will retry forever")
var flChmod = pflag.Int("change-permissions", envInt("GIT_SYNC_PERMISSIONS", 0), var flChmod = pflag.Int("change-permissions", envInt("GIT_SYNC_PERMISSIONS", 0),
@ -397,6 +401,24 @@ func main() {
handleConfigError(log, true, "ERROR: --period must be at least 10ms") handleConfigError(log, true, "ERROR: --period must be at least 10ms")
} }
var syncSig syscall.Signal
if *flSyncOnSignal != "" {
if num, err := strconv.ParseInt(*flSyncOnSignal, 0, 0); err == nil {
// sync-on-signal value is a number
syncSig = syscall.Signal(num)
} else {
// sync-on-signal value is a name
syncSig = unix.SignalNum(*flSyncOnSignal)
if syncSig == 0 {
// last resort - maybe they said "HUP", meaning "SIGHUP"
syncSig = unix.SignalNum("SIG" + *flSyncOnSignal)
}
}
if syncSig == 0 {
handleConfigError(log, true, "ERROR: --sync-on-signal must be a valid signal name or number")
}
}
if *flTimeout != 0 { if *flTimeout != 0 {
// Back-compat // Back-compat
*flSyncTimeout = time.Duration(*flTimeout) * time.Second *flSyncTimeout = time.Duration(*flTimeout) * time.Second
@ -670,6 +692,13 @@ func main() {
go exechookRunner.Run(context.Background()) go exechookRunner.Run(context.Background())
} }
// Setup signal notify channel
sigChan := make(chan os.Signal, 1)
if syncSig != 0 {
log.V(2).Info("installing signal handler", "signal", unix.SignalName(syncSig))
signal.Notify(sigChan, syncSig)
}
// Craft a function that can be called to refresh credentials when needed. // Craft a function that can be called to refresh credentials when needed.
refreshCreds := func(ctx context.Context) error { refreshCreds := func(ctx context.Context) error {
// These should all be mutually-exclusive configs. // These should all be mutually-exclusive configs.
@ -772,7 +801,16 @@ func main() {
log.V(1).Info("next sync", "waitTime", flPeriod.String()) log.V(1).Info("next sync", "waitTime", flPeriod.String())
cancel() cancel()
time.Sleep(*flPeriod)
// Sleep until the next sync. If syncSig is set then the sleep may
// be interrupted by that signal.
t := time.NewTimer(*flPeriod)
select {
case <-t.C:
case <-sigChan:
log.V(2).Info("caught signal", "signal", unix.SignalName(syncSig))
t.Stop()
}
} }
} }
@ -2139,6 +2177,13 @@ OPTIONS
The git submodule behavior: one of "recursive", "shallow", or The git submodule behavior: one of "recursive", "shallow", or
"off". If not specified, this defaults to "recursive". "off". If not specified, this defaults to "recursive".
--sync-on-signal <string>, $GIT_SYNC_SYNC_ON_SIGNAL
Indicates that a sync attempt should occur upon receipt of the
specified signal name (e.g. SIGHUP) or number (e.g. 1). If a sync
is already in progress, another sync will be triggered as soon as
the current one completes. If not specified, signals will not
trigger syncs.
--sync-timeout <duration>, $GIT_SYNC_SYNC_TIMEOUT --sync-timeout <duration>, $GIT_SYNC_SYNC_TIMEOUT
The total time allowed for one complete sync. This must be at least The total time allowed for one complete sync. This must be at least
10ms. This flag obsoletes --timeout, but if --timeout is specified, 10ms. This flag obsoletes --timeout, but if --timeout is specified,

View File

@ -126,6 +126,14 @@ function docker_kill() {
docker kill "$1" >/dev/null docker kill "$1" >/dev/null
} }
function docker_signal() {
if [[ -z "$1" || -z "$2" ]]; then
echo "usage: $0 <id> <signal>"
return 1
fi
docker kill "--signal=$2" "$1" >/dev/null
}
# E2E_TAG is the tag used for docker builds. This is needed because docker # E2E_TAG is the tag used for docker builds. This is needed because docker
# tags are system-global, but one might have multiple repos checked out. # tags are system-global, but one might have multiple repos checked out.
E2E_TAG=$(git rev-parse --show-toplevel | sed 's|/|_|g') E2E_TAG=$(git rev-parse --show-toplevel | sed 's|/|_|g')
@ -1111,6 +1119,108 @@ function e2e::sync_slow_git_long_timeout() {
assert_metric_eq "${METRIC_GOOD_SYNC_COUNT}" 2 assert_metric_eq "${METRIC_GOOD_SYNC_COUNT}" 2
} }
##############################################
# Test sync-on-signal with SIGHUP
##############################################
function e2e::sync_on_signal_sighup() {
# First sync
echo "$FUNCNAME 1" > "$REPO"/file
git -C "$REPO" commit -qam "$FUNCNAME 1"
GIT_SYNC \
--period=100s \
--sync-on-signal="SIGHUP" \
--repo="file://$REPO" \
--branch="$MAIN_BRANCH" \
--rev=HEAD \
--root="$ROOT" \
--link="link" \
>> "$1" 2>&1 &
wait_for_sync 3
assert_link_exists "$ROOT"/link
assert_file_exists "$ROOT"/link/file
assert_file_eq "$ROOT"/link/file "$FUNCNAME 1"
# Move HEAD forward
echo "$FUNCNAME 2" > "$REPO"/file
git -C "$REPO" commit -qam "$FUNCNAME 2"
# Send signal (note --period is 100s, signal should trigger sync)
CTR=$(docker ps --filter label="git-sync-e2e=$RUNID" --format="{{.ID}}")
docker_signal "$CTR" SIGHUP
wait_for_sync 3
assert_link_exists "$ROOT"/link
assert_file_exists "$ROOT"/link/file
assert_file_eq "$ROOT"/link/file "$FUNCNAME 2"
}
##############################################
# Test sync-on-signal with HUP
##############################################
function e2e::sync_on_signal_hup() {
# First sync
echo "$FUNCNAME 1" > "$REPO"/file
git -C "$REPO" commit -qam "$FUNCNAME 1"
GIT_SYNC \
--period=100s \
--sync-on-signal="HUP" \
--repo="file://$REPO" \
--branch="$MAIN_BRANCH" \
--rev=HEAD \
--root="$ROOT" \
--link="link" \
>> "$1" 2>&1 &
wait_for_sync 3
assert_link_exists "$ROOT"/link
assert_file_exists "$ROOT"/link/file
assert_file_eq "$ROOT"/link/file "$FUNCNAME 1"
# Move HEAD forward
echo "$FUNCNAME 2" > "$REPO"/file
git -C "$REPO" commit -qam "$FUNCNAME 2"
# Send signal (note --period is 100s, signal should trigger sync)
CTR=$(docker ps --filter label="git-sync-e2e=$RUNID" --format="{{.ID}}")
docker_signal "$CTR" SIGHUP
wait_for_sync 3
assert_link_exists "$ROOT"/link
assert_file_exists "$ROOT"/link/file
assert_file_eq "$ROOT"/link/file "$FUNCNAME 2"
}
##############################################
# Test sync-on-signal with 1 (SIGHUP)
##############################################
function e2e::sync_on_signal_1() {
# First sync
echo "$FUNCNAME 1" > "$REPO"/file
git -C "$REPO" commit -qam "$FUNCNAME 1"
GIT_SYNC \
--period=100s \
--sync-on-signal=1 \
--repo="file://$REPO" \
--branch="$MAIN_BRANCH" \
--rev=HEAD \
--root="$ROOT" \
--link="link" \
>> "$1" 2>&1 &
wait_for_sync 3
assert_link_exists "$ROOT"/link
assert_file_exists "$ROOT"/link/file
assert_file_eq "$ROOT"/link/file "$FUNCNAME 1"
# Move HEAD forward
echo "$FUNCNAME 2" > "$REPO"/file
git -C "$REPO" commit -qam "$FUNCNAME 2"
# Send signal (note --period is 100s, signal should trigger sync)
CTR=$(docker ps --filter label="git-sync-e2e=$RUNID" --format="{{.ID}}")
docker_signal "$CTR" SIGHUP
wait_for_sync 3
assert_link_exists "$ROOT"/link
assert_file_exists "$ROOT"/link/file
assert_file_eq "$ROOT"/link/file "$FUNCNAME 2"
}
############################################## ##############################################
# Test depth syncing # Test depth syncing
############################################## ##############################################