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"
"strings"
"sync"
"syscall"
"time"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promhttp"
"github.com/spf13/pflag"
"golang.org/x/sys/unix"
"k8s.io/git-sync/pkg/cmd"
"k8s.io/git-sync/pkg/hook"
"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")
var flOneTime = pflag.Bool("one-time", envBool("GIT_SYNC_ONE_TIME", false),
"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),
"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),
@ -397,6 +401,24 @@ func main() {
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 {
// Back-compat
*flSyncTimeout = time.Duration(*flTimeout) * time.Second
@ -670,6 +692,13 @@ func main() {
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.
refreshCreds := func(ctx context.Context) error {
// These should all be mutually-exclusive configs.
@ -772,7 +801,16 @@ func main() {
log.V(1).Info("next sync", "waitTime", flPeriod.String())
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
"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
The total time allowed for one complete sync. This must be at least
10ms. This flag obsoletes --timeout, but if --timeout is specified,

View File

@ -126,6 +126,14 @@ function docker_kill() {
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
# tags are system-global, but one might have multiple repos checked out.
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
}
##############################################
# 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
##############################################