3816 lines
118 KiB
Bash
Executable File
3816 lines
118 KiB
Bash
Executable File
#!/bin/bash
|
|
#
|
|
# Copyright 2016 The Kubernetes Authors.
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
# you may not use this file except in compliance with the License.
|
|
# You may obtain a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
# See the License for the specific language governing permissions and
|
|
# limitations under the License.
|
|
|
|
set -o errexit
|
|
set -o nounset
|
|
set -o pipefail
|
|
|
|
# shellcheck disable=SC2120
|
|
function caller() {
|
|
local stack_skip=${1:-0}
|
|
stack_skip=$((stack_skip + 1))
|
|
if [[ ${#FUNCNAME[@]} -gt ${stack_skip} ]]; then
|
|
local i
|
|
for ((i=1 ; i <= ${#FUNCNAME[@]} - stack_skip ; i++)); do
|
|
local frame_no=$((i - 1 + stack_skip))
|
|
local source_lineno=${BASH_LINENO[$((frame_no - 1))]}
|
|
local funcname=${FUNCNAME[${frame_no}]}
|
|
if [[ "$funcname" =~ 'e2e::' ]]; then
|
|
echo "${source_lineno}"
|
|
fi
|
|
done
|
|
fi
|
|
}
|
|
|
|
function fail() {
|
|
echo "FAIL: line $(caller):" "$@" >&3
|
|
return 42
|
|
}
|
|
|
|
function skip() {
|
|
echo "SKIP" >&3
|
|
return 43
|
|
}
|
|
|
|
function pass() {
|
|
echo "PASS"
|
|
}
|
|
|
|
# $1: a file/dir name
|
|
# $2: max seconds to wait
|
|
function wait_for_file_exists() {
|
|
local file=$1
|
|
local ticks=$(($2*10)) # 100ms per tick
|
|
|
|
while (( ticks > 0 )); do
|
|
if [[ -f "$file" ]]; then
|
|
break
|
|
fi
|
|
sleep 0.1
|
|
ticks=$((ticks-1))
|
|
done
|
|
}
|
|
|
|
function assert_link_exists() {
|
|
if ! [[ -e "$1" ]]; then
|
|
fail "$1 does not exist"
|
|
fi
|
|
if ! [[ -L "$1" ]]; then
|
|
fail "$1 is not a symlink"
|
|
fi
|
|
}
|
|
|
|
function assert_link_basename_eq() {
|
|
if [[ $(basename "$(readlink "$1")") == "$2" ]]; then
|
|
return
|
|
fi
|
|
fail "$1 does not point to $2: $(readlink "$1")"
|
|
}
|
|
|
|
function assert_file_exists() {
|
|
if ! [[ -f "$1" ]]; then
|
|
fail "$1 does not exist"
|
|
fi
|
|
}
|
|
|
|
function assert_file_absent() {
|
|
if [[ -f "$1" ]]; then
|
|
fail "$1 exists but should not"
|
|
fi
|
|
}
|
|
|
|
function assert_file_eq() {
|
|
if [[ $(cat "$1") == "$2" ]]; then
|
|
return
|
|
fi
|
|
fail "$1 does not contain '$2': $(cat "$1")"
|
|
}
|
|
|
|
function assert_file_contains() {
|
|
if grep -q "$2" "$1"; then
|
|
return
|
|
fi
|
|
fail "$1 does not contain '$2': $(cat "$1")"
|
|
}
|
|
|
|
function assert_file_lines_eq() {
|
|
local n
|
|
n=$(wc -l < "$1")
|
|
if (( "$n" != "$2" )); then
|
|
fail "$1 is not $2 lines: $n"
|
|
fi
|
|
}
|
|
|
|
function assert_file_lines_ge() {
|
|
local n
|
|
n=$(wc -l < "$1")
|
|
if (( "$n" < "$2" )); then
|
|
fail "$1 is not at least $2 lines: $n"
|
|
fi
|
|
}
|
|
|
|
function assert_metric_eq() {
|
|
local val
|
|
val="$(curl --silent "http://localhost:$HTTP_PORT/metrics" \
|
|
| grep "^$1 " \
|
|
| awk '{print $NF}')"
|
|
if [[ "${val}" == "$2" ]]; then
|
|
return
|
|
fi
|
|
fail "metric $1 was expected to be '$2': ${val}"
|
|
}
|
|
|
|
function assert_fail() {
|
|
(
|
|
set +o errexit
|
|
"$@"
|
|
local ret=$?
|
|
if [[ "$ret" != 0 ]]; then
|
|
return
|
|
fi
|
|
fail "expected non-zero exit code, got $ret"
|
|
)
|
|
}
|
|
|
|
# Helper: run a docker container.
|
|
function docker_run() {
|
|
local rm="--rm"
|
|
if [[ "${CLEANUP:-}" == 0 ]]; then
|
|
rm=""
|
|
fi
|
|
docker run \
|
|
-d \
|
|
${rm} `# not quoted on purpose` \
|
|
--label git-sync-e2e="$RUNID" \
|
|
"$@"
|
|
sleep 2 # wait for it to come up
|
|
}
|
|
|
|
# Helper: get the IP of a docker container.
|
|
function docker_ip() {
|
|
if [[ -z "$1" ]]; then
|
|
echo "usage: $0 <id>"
|
|
return 1
|
|
fi
|
|
docker inspect "$1" | jq -r .[0].NetworkSettings.IPAddress
|
|
}
|
|
|
|
function docker_kill() {
|
|
if [[ -z "$1" ]]; then
|
|
echo "usage: $0 <id>"
|
|
return 1
|
|
fi
|
|
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')
|
|
|
|
# Setting GIT_SYNC_E2E_IMAGE forces the test to use a specific image instead of the
|
|
# current tree.
|
|
build_container=false
|
|
if [[ "${GIT_SYNC_E2E_IMAGE:-unset}" == "unset" ]]; then
|
|
GIT_SYNC_E2E_IMAGE="e2e/git-sync:${E2E_TAG}__$(go env GOOS)_$(go env GOARCH)"
|
|
build_container=true
|
|
fi
|
|
|
|
# DIR is the directory in which all this test's state lives.
|
|
RUNID="${RANDOM}${RANDOM}"
|
|
DIR="/tmp/git-sync-e2e.$RUNID"
|
|
mkdir "$DIR"
|
|
function final_cleanup() {
|
|
if [[ "${CLEANUP:-}" == 0 ]]; then
|
|
echo "leaving logs in $DIR"
|
|
else
|
|
rm -rf "$DIR"
|
|
fi
|
|
}
|
|
# Set the trap to call the final_cleanup function on exit.
|
|
trap final_cleanup EXIT
|
|
|
|
skip_github_app_test="${SKIP_GITHUB_APP_TEST:-true}"
|
|
required_env_vars=()
|
|
LOCAL_GITHUB_APP_PRIVATE_KEY_FILE="github_app_private_key.pem"
|
|
GITHUB_APP_PRIVATE_KEY_MOUNT=()
|
|
if [[ "${skip_github_app_test}" != "true" ]]; then
|
|
required_env_vars=(
|
|
"TEST_GITHUB_APP_AUTH_TEST_REPO"
|
|
"TEST_GITHUB_APP_APPLICATION_ID"
|
|
"TEST_GITHUB_APP_INSTALLATION_ID"
|
|
"TEST_GITHUB_APP_CLIENT_ID"
|
|
)
|
|
|
|
if [[ -n "${TEST_GITHUB_APP_PRIVATE_KEY_FILE:-}" && -n "${TEST_GITHUB_APP_PRIVATE_KEY:-}" ]]; then
|
|
echo "ERROR: Both TEST_GITHUB_APP_PRIVATE_KEY_FILE and TEST_GITHUB_APP_PRIVATE_KEY were specified."
|
|
exit 1
|
|
fi
|
|
if [[ -n "${TEST_GITHUB_APP_PRIVATE_KEY_FILE:-}" ]]; then
|
|
cp "${TEST_GITHUB_APP_PRIVATE_KEY_FILE}" "${DIR}/${LOCAL_GITHUB_APP_PRIVATE_KEY_FILE}"
|
|
elif [[ -n "${TEST_GITHUB_APP_PRIVATE_KEY:-}" ]]; then
|
|
echo "${TEST_GITHUB_APP_PRIVATE_KEY}" > "${DIR}/${LOCAL_GITHUB_APP_PRIVATE_KEY_FILE}"
|
|
else
|
|
echo "ERROR: Neither TEST_GITHUB_APP_PRIVATE_KEY_FILE nor TEST_GITHUB_APP_PRIVATE_KEY was specified."
|
|
echo " Either provide a value or skip this test (SKIP_GITHUB_APP_TEST=true)."
|
|
exit 1
|
|
fi
|
|
|
|
# Validate all required environment variables for the github-app-auth tests are provided.
|
|
for var in "${required_env_vars[@]}"; do
|
|
if [[ ! -v "${var}" ]]; then
|
|
echo "ERROR: Required environment variable '${var}' is not set."
|
|
echo " Either provide a value or skip this test (SKIP_GITHUB_APP_TEST=true)."
|
|
exit 1
|
|
fi
|
|
done
|
|
|
|
# Mount the GitHub App private key file to the git-sync container
|
|
GITHUB_APP_PRIVATE_KEY_MOUNT=(-v "${DIR}/${LOCAL_GITHUB_APP_PRIVATE_KEY_FILE}":"/${LOCAL_GITHUB_APP_PRIVATE_KEY_FILE}":ro)
|
|
fi
|
|
|
|
# WORK is temp space and in reset for each testcase.
|
|
WORK="$DIR/work"
|
|
function clean_work() {
|
|
rm -rf "$WORK"
|
|
mkdir -p "$WORK"
|
|
}
|
|
|
|
# REPO and REPO2 are the source repos under test.
|
|
REPO="$DIR/repo"
|
|
REPO2="${REPO}2"
|
|
MAIN_BRANCH="e2e-branch"
|
|
function init_repo() {
|
|
local arg="${1}"
|
|
|
|
rm -rf "$REPO"
|
|
mkdir -p "$REPO"
|
|
git -C "$REPO" init -q -b "$MAIN_BRANCH"
|
|
echo "$arg" > "$REPO/file"
|
|
git -C "$REPO" add file
|
|
git -C "$REPO" commit -aqm "init file"
|
|
|
|
rm -rf "$REPO2"
|
|
cp -r "$REPO" "$REPO2"
|
|
}
|
|
|
|
# ROOT is the volume (usually) used as --root.
|
|
ROOT="$DIR/root"
|
|
function clean_root() {
|
|
rm -rf "$ROOT"
|
|
mkdir -p "$ROOT"
|
|
chmod g+rwx "$ROOT"
|
|
}
|
|
|
|
# How long we wait for sync operations to happen between test steps, in seconds
|
|
MAXWAIT="${MAXWAIT:-3}"
|
|
|
|
# INTERLOCK is a file, under $ROOT, used to coordinate tests and syncs.
|
|
INTERLOCK="_sync_lock"
|
|
function wait_for_sync() {
|
|
if [[ -z "$1" ]]; then
|
|
echo "usage: $0 <max-wait-seconds>"
|
|
return 1
|
|
fi
|
|
local path="$ROOT/$INTERLOCK"
|
|
wait_for_file_exists "$path" "$1"
|
|
rm -f "$path"
|
|
}
|
|
|
|
# Init SSH for test cases.
|
|
DOT_SSH="$DIR/dot_ssh"
|
|
for i in $(seq 1 3); do
|
|
mkdir -p "$DOT_SSH/$i"
|
|
ssh-keygen -f "$DOT_SSH/$i/id_test" -P "" >/dev/null
|
|
cp -a "$DOT_SSH/$i/id_test" "$DOT_SSH/$i/id_local" # for outside-of-container use
|
|
mkdir -p "$DOT_SSH/server/$i"
|
|
cat "$DOT_SSH/$i/id_test.pub" > "$DOT_SSH/server/$i/authorized_keys"
|
|
done
|
|
# Allow files to be read inside containers running as a different UID.
|
|
# Note: this does not include the *.local forms.
|
|
chmod g+r "$DOT_SSH"/*/id_test* "$DOT_SSH"/server/*
|
|
|
|
TEST_TOOLS="_test_tools"
|
|
SLOW_GIT_FETCH="$TEST_TOOLS/git_slow_fetch.sh"
|
|
ASKPASS_GIT="$TEST_TOOLS/git_askpass.sh"
|
|
EXECHOOK_COMMAND="$TEST_TOOLS/exechook_command.sh"
|
|
EXECHOOK_COMMAND_FAIL="$TEST_TOOLS/exechook_command_fail.sh"
|
|
EXECHOOK_COMMAND_SLEEPY="$TEST_TOOLS/exechook_command_with_sleep.sh"
|
|
EXECHOOK_COMMAND_FAIL_SLEEPY="$TEST_TOOLS/exechook_command_fail_with_sleep.sh"
|
|
EXECHOOK_ENVKEY=ENVKEY
|
|
EXECHOOK_ENVVAL=envval
|
|
RUNLOG="$DIR/runlog"
|
|
rm -f "$RUNLOG"
|
|
touch "$RUNLOG"
|
|
chmod g+rw "$RUNLOG"
|
|
HTTP_PORT=9376
|
|
METRIC_GOOD_SYNC_COUNT='git_sync_count_total{status="success"}'
|
|
METRIC_FETCH_COUNT='git_fetch_count_total'
|
|
|
|
function GIT_SYNC() {
|
|
#./bin/linux_amd64/git-sync "$@"
|
|
local rm="--rm"
|
|
if [[ "${CLEANUP:-}" == 0 ]]; then
|
|
rm=""
|
|
fi
|
|
docker run \
|
|
-i \
|
|
${rm} `# not quoted on purpose` \
|
|
--label git-sync-e2e="$RUNID" \
|
|
--network="host" \
|
|
-u git-sync:"$(id -g)" `# rely on GID, triggering "dubious ownership"` \
|
|
-v "$ROOT":"$ROOT":rw \
|
|
-v "$REPO":"$REPO":ro \
|
|
-v "$REPO2":"$REPO2":ro \
|
|
-v "$WORK":"$WORK":ro \
|
|
-v "$(pwd)/$TEST_TOOLS":"/$TEST_TOOLS":ro \
|
|
--env "$EXECHOOK_ENVKEY=$EXECHOOK_ENVVAL" \
|
|
-v "$RUNLOG":/var/log/runs \
|
|
-v "$DOT_SSH/1/id_test":"/ssh/secret.1":ro \
|
|
-v "$DOT_SSH/2/id_test":"/ssh/secret.2":ro \
|
|
-v "$DOT_SSH/3/id_test":"/ssh/secret.3":ro \
|
|
"${GITHUB_APP_PRIVATE_KEY_MOUNT[@]}" \
|
|
"${GIT_SYNC_E2E_IMAGE}" \
|
|
-v=6 \
|
|
--add-user \
|
|
--group-write \
|
|
--touch-file="$INTERLOCK" \
|
|
--git-config='protocol.file.allow:always' \
|
|
--http-bind=":$HTTP_PORT" \
|
|
--http-metrics \
|
|
--http-pprof \
|
|
"$@"
|
|
}
|
|
|
|
function remove_containers() {
|
|
sleep 2 # Let docker finish saving container metadata
|
|
docker ps --filter label="git-sync-e2e=$RUNID" --format="{{.ID}}" \
|
|
| while read -r ctr; do
|
|
docker kill "$ctr" >/dev/null
|
|
done
|
|
}
|
|
|
|
#
|
|
# After all the test functions are defined, we can iterate over them and run
|
|
# them all automatically. See the end of this file.
|
|
#
|
|
|
|
##############################################
|
|
# Test init when root doesn't exist
|
|
##############################################
|
|
function e2e::init_root_doesnt_exist() {
|
|
GIT_SYNC \
|
|
--one-time \
|
|
--repo="file://$REPO" \
|
|
--root="$ROOT/subdir" \
|
|
--link="link"
|
|
assert_link_exists "$ROOT/subdir/link"
|
|
assert_file_exists "$ROOT/subdir/link/file"
|
|
assert_file_eq "$ROOT/subdir/link/file" "${FUNCNAME[0]}"
|
|
}
|
|
|
|
##############################################
|
|
# Test init when root exists and is empty
|
|
##############################################
|
|
function e2e::init_root_exists_empty() {
|
|
GIT_SYNC \
|
|
--one-time \
|
|
--repo="file://$REPO" \
|
|
--root="$ROOT" \
|
|
--link="link"
|
|
assert_link_exists "$ROOT/link"
|
|
assert_file_exists "$ROOT/link/file"
|
|
assert_file_eq "$ROOT/link/file" "${FUNCNAME[0]}"
|
|
}
|
|
|
|
##############################################
|
|
# Test init with a weird --root flag
|
|
##############################################
|
|
function e2e::init_root_flag_is_weird() {
|
|
GIT_SYNC \
|
|
--one-time \
|
|
--repo="file://$REPO" \
|
|
--root="../../../../../$ROOT/../../../../../../$ROOT" \
|
|
--link="link"
|
|
assert_link_exists "$ROOT/link"
|
|
assert_file_exists "$ROOT/link/file"
|
|
assert_file_eq "$ROOT/link/file" "${FUNCNAME[0]}"
|
|
}
|
|
|
|
##############################################
|
|
# Test init with a symlink in --root
|
|
##############################################
|
|
function e2e::init_root_flag_has_symlink() {
|
|
mkdir -p "$ROOT/subdir"
|
|
ln -s "$ROOT/subdir" "$ROOT/rootlink" # symlink to test
|
|
|
|
GIT_SYNC \
|
|
--one-time \
|
|
--repo="file://$REPO" \
|
|
--root="$ROOT/rootlink" \
|
|
--link="link"
|
|
assert_link_exists "$ROOT/subdir/link"
|
|
assert_file_exists "$ROOT/subdir/link/file"
|
|
assert_file_eq "$ROOT/subdir/link/file" "${FUNCNAME[0]}"
|
|
}
|
|
|
|
##############################################
|
|
# Test init when root is under a git repo
|
|
##############################################
|
|
function e2e::init_root_is_under_another_repo() {
|
|
# Make a parent dir that is a git repo.
|
|
mkdir -p "$ROOT/subdir/root"
|
|
date > "$ROOT/subdir/root/file" # so it is not empty
|
|
git -C "$ROOT/subdir" init -q
|
|
|
|
GIT_SYNC \
|
|
--one-time \
|
|
--repo="file://$REPO" \
|
|
--root="$ROOT/subdir/root" \
|
|
--link="link"
|
|
assert_link_exists "$ROOT/subdir/root/link"
|
|
assert_file_exists "$ROOT/subdir/root/link/file"
|
|
assert_file_eq "$ROOT/subdir/root/link/file" "${FUNCNAME[0]}"
|
|
}
|
|
|
|
##############################################
|
|
# Test init when root fails sanity
|
|
##############################################
|
|
function e2e::init_root_fails_sanity() {
|
|
# Make an invalid git repo.
|
|
git -C "$ROOT" init -q
|
|
echo "ref: refs/heads/nonexist" > "$ROOT/.git/HEAD"
|
|
|
|
GIT_SYNC \
|
|
--one-time \
|
|
--repo="file://$REPO" \
|
|
--root="$ROOT" \
|
|
--link="link"
|
|
assert_link_exists "$ROOT/link"
|
|
assert_file_exists "$ROOT/link/file"
|
|
assert_file_eq "$ROOT/link/file" "${FUNCNAME[0]}"
|
|
}
|
|
|
|
##############################################
|
|
# Test non-zero exit with a bad ref
|
|
##############################################
|
|
function e2e::bad_ref_non_zero_exit() {
|
|
assert_fail \
|
|
GIT_SYNC \
|
|
--one-time \
|
|
--repo="file://$REPO" \
|
|
--ref=does-not-exist \
|
|
--root="$ROOT" \
|
|
--link="link"
|
|
assert_file_absent "$ROOT/link"
|
|
}
|
|
|
|
##############################################
|
|
# Test default ref syncing
|
|
##############################################
|
|
function e2e::sync_default_ref() {
|
|
# First sync
|
|
echo "${FUNCNAME[0]} 1" > "$REPO/file"
|
|
git -C "$REPO" commit -qam "${FUNCNAME[0]} 1"
|
|
git -C "$REPO" checkout -q -b weird-name
|
|
|
|
GIT_SYNC \
|
|
--period=100ms \
|
|
--repo="file://$REPO" \
|
|
--root="$ROOT" \
|
|
--link="link" \
|
|
&
|
|
wait_for_sync "${MAXWAIT}"
|
|
assert_link_exists "$ROOT/link"
|
|
assert_file_exists "$ROOT/link/file"
|
|
assert_file_eq "$ROOT/link/file" "${FUNCNAME[0]} 1"
|
|
assert_metric_eq "${METRIC_GOOD_SYNC_COUNT}" 1
|
|
assert_metric_eq "${METRIC_FETCH_COUNT}" 1
|
|
|
|
# Move forward
|
|
echo "${FUNCNAME[0]} 2" > "$REPO/file"
|
|
git -C "$REPO" commit -qam "${FUNCNAME[0]} 2"
|
|
wait_for_sync "${MAXWAIT}"
|
|
assert_link_exists "$ROOT/link"
|
|
assert_file_exists "$ROOT/link/file"
|
|
assert_file_eq "$ROOT/link/file" "${FUNCNAME[0]} 2"
|
|
assert_metric_eq "${METRIC_GOOD_SYNC_COUNT}" 2
|
|
assert_metric_eq "${METRIC_FETCH_COUNT}" 2
|
|
|
|
# Move backward
|
|
git -C "$REPO" reset -q --hard HEAD^
|
|
wait_for_sync "${MAXWAIT}"
|
|
assert_link_exists "$ROOT/link"
|
|
assert_file_exists "$ROOT/link/file"
|
|
assert_file_eq "$ROOT/link/file" "${FUNCNAME[0]} 1"
|
|
assert_metric_eq "${METRIC_GOOD_SYNC_COUNT}" 3
|
|
assert_metric_eq "${METRIC_FETCH_COUNT}" 3
|
|
}
|
|
|
|
##############################################
|
|
# Test HEAD syncing
|
|
##############################################
|
|
function e2e::sync_head() {
|
|
# First sync
|
|
echo "${FUNCNAME[0]} 1" > "$REPO/file"
|
|
git -C "$REPO" commit -qam "${FUNCNAME[0]} 1"
|
|
|
|
GIT_SYNC \
|
|
--period=100ms \
|
|
--repo="file://$REPO" \
|
|
--ref=HEAD \
|
|
--root="$ROOT" \
|
|
--link="link" \
|
|
&
|
|
wait_for_sync "${MAXWAIT}"
|
|
assert_link_exists "$ROOT/link"
|
|
assert_file_exists "$ROOT/link/file"
|
|
assert_file_eq "$ROOT/link/file" "${FUNCNAME[0]} 1"
|
|
assert_metric_eq "${METRIC_GOOD_SYNC_COUNT}" 1
|
|
assert_metric_eq "${METRIC_FETCH_COUNT}" 1
|
|
|
|
# Move HEAD forward
|
|
echo "${FUNCNAME[0]} 2" > "$REPO/file"
|
|
git -C "$REPO" commit -qam "${FUNCNAME[0]} 2"
|
|
wait_for_sync "${MAXWAIT}"
|
|
assert_link_exists "$ROOT/link"
|
|
assert_file_exists "$ROOT/link/file"
|
|
assert_file_eq "$ROOT/link/file" "${FUNCNAME[0]} 2"
|
|
assert_metric_eq "${METRIC_GOOD_SYNC_COUNT}" 2
|
|
assert_metric_eq "${METRIC_FETCH_COUNT}" 2
|
|
|
|
# Move HEAD backward
|
|
git -C "$REPO" reset -q --hard HEAD^
|
|
wait_for_sync "${MAXWAIT}"
|
|
assert_link_exists "$ROOT/link"
|
|
assert_file_exists "$ROOT/link/file"
|
|
assert_file_eq "$ROOT/link/file" "${FUNCNAME[0]} 1"
|
|
assert_metric_eq "${METRIC_GOOD_SYNC_COUNT}" 3
|
|
assert_metric_eq "${METRIC_FETCH_COUNT}" 3
|
|
}
|
|
|
|
##############################################
|
|
# Test sync with an absolute-path link
|
|
##############################################
|
|
function e2e::sync_head_absolute_link() {
|
|
# First sync
|
|
echo "${FUNCNAME[0]} 1" > "$REPO/file"
|
|
git -C "$REPO" commit -qam "${FUNCNAME[0]} 1"
|
|
|
|
GIT_SYNC \
|
|
--period=100ms \
|
|
--repo="file://$REPO" \
|
|
--ref=HEAD \
|
|
--root="$ROOT/root" \
|
|
--link="$ROOT/other/dir/link" \
|
|
&
|
|
wait_for_sync "${MAXWAIT}"
|
|
assert_file_absent "$ROOT/root/link"
|
|
assert_link_exists "$ROOT/other/dir/link"
|
|
assert_file_exists "$ROOT/other/dir/link/file"
|
|
assert_file_eq "$ROOT/other/dir/link/file" "${FUNCNAME[0]} 1"
|
|
assert_metric_eq "${METRIC_GOOD_SYNC_COUNT}" 1
|
|
assert_metric_eq "${METRIC_FETCH_COUNT}" 1
|
|
|
|
# Move HEAD forward
|
|
echo "${FUNCNAME[0]} 2" > "$REPO/file"
|
|
git -C "$REPO" commit -qam "${FUNCNAME[0]} 2"
|
|
wait_for_sync "${MAXWAIT}"
|
|
assert_file_absent "$ROOT/root/link"
|
|
assert_link_exists "$ROOT/other/dir/link"
|
|
assert_file_exists "$ROOT/other/dir/link/file"
|
|
assert_file_eq "$ROOT/other/dir/link/file" "${FUNCNAME[0]} 2"
|
|
assert_metric_eq "${METRIC_GOOD_SYNC_COUNT}" 2
|
|
assert_metric_eq "${METRIC_FETCH_COUNT}" 2
|
|
|
|
# Move HEAD backward
|
|
git -C "$REPO" reset -q --hard HEAD^
|
|
wait_for_sync "${MAXWAIT}"
|
|
assert_file_absent "$ROOT/root/link"
|
|
assert_link_exists "$ROOT/other/dir/link"
|
|
assert_file_exists "$ROOT/other/dir/link/file"
|
|
assert_file_eq "$ROOT/other/dir/link/file" "${FUNCNAME[0]} 1"
|
|
assert_metric_eq "${METRIC_GOOD_SYNC_COUNT}" 3
|
|
assert_metric_eq "${METRIC_FETCH_COUNT}" 3
|
|
}
|
|
|
|
##############################################
|
|
# Test sync with a subdir-path link
|
|
##############################################
|
|
function e2e::sync_head_subdir_link() {
|
|
# First sync
|
|
echo "${FUNCNAME[0]} 1" > "$REPO/file"
|
|
git -C "$REPO" commit -qam "${FUNCNAME[0]} 1"
|
|
|
|
GIT_SYNC \
|
|
--period=100ms \
|
|
--repo="file://$REPO" \
|
|
--ref=HEAD \
|
|
--root="$ROOT" \
|
|
--link="other/dir/link" \
|
|
&
|
|
wait_for_sync "${MAXWAIT}"
|
|
assert_file_absent "$ROOT/link"
|
|
assert_link_exists "$ROOT/other/dir/link"
|
|
assert_file_exists "$ROOT/other/dir/link/file"
|
|
assert_file_eq "$ROOT/other/dir/link/file" "${FUNCNAME[0]} 1"
|
|
assert_metric_eq "${METRIC_GOOD_SYNC_COUNT}" 1
|
|
assert_metric_eq "${METRIC_FETCH_COUNT}" 1
|
|
|
|
# Move HEAD forward
|
|
echo "${FUNCNAME[0]} 2" > "$REPO/file"
|
|
git -C "$REPO" commit -qam "${FUNCNAME[0]} 2"
|
|
wait_for_sync "${MAXWAIT}"
|
|
assert_file_absent "$ROOT/link"
|
|
assert_link_exists "$ROOT/other/dir/link"
|
|
assert_file_exists "$ROOT/other/dir/link/file"
|
|
assert_file_eq "$ROOT/other/dir/link/file" "${FUNCNAME[0]} 2"
|
|
assert_metric_eq "${METRIC_GOOD_SYNC_COUNT}" 2
|
|
assert_metric_eq "${METRIC_FETCH_COUNT}" 2
|
|
|
|
# Move HEAD backward
|
|
git -C "$REPO" reset -q --hard HEAD^
|
|
wait_for_sync "${MAXWAIT}"
|
|
assert_file_absent "$ROOT/link"
|
|
assert_link_exists "$ROOT/other/dir/link"
|
|
assert_file_exists "$ROOT/other/dir/link/file"
|
|
assert_file_eq "$ROOT/other/dir/link/file" "${FUNCNAME[0]} 1"
|
|
assert_metric_eq "${METRIC_GOOD_SYNC_COUNT}" 3
|
|
assert_metric_eq "${METRIC_FETCH_COUNT}" 3
|
|
}
|
|
|
|
##############################################
|
|
# Test worktree-cleanup
|
|
##############################################
|
|
function e2e::worktree_cleanup() {
|
|
GIT_SYNC \
|
|
--period=100ms \
|
|
--repo="file://$REPO" \
|
|
--root="$ROOT" \
|
|
--link="link" \
|
|
&
|
|
|
|
# wait for first sync
|
|
wait_for_sync "${MAXWAIT}"
|
|
assert_link_exists "$ROOT/link"
|
|
assert_file_exists "$ROOT/link/file"
|
|
assert_file_eq "$ROOT/link/file" "${FUNCNAME[0]}"
|
|
assert_metric_eq "${METRIC_GOOD_SYNC_COUNT}" 1
|
|
assert_metric_eq "${METRIC_FETCH_COUNT}" 1
|
|
|
|
# suspend time so we can fake corruption
|
|
docker ps --filter label="git-sync-e2e=$RUNID" --format="{{.ID}}" \
|
|
| while read -r ctr; do
|
|
docker pause "$ctr" >/dev/null
|
|
done
|
|
|
|
# make a second commit
|
|
echo "${FUNCNAME[0]}-ok" > "$REPO/file2"
|
|
git -C "$REPO" add file2
|
|
git -C "$REPO" commit -qam "${FUNCNAME[0]} new file"
|
|
|
|
# make a worktree to collide with git-sync
|
|
local sha
|
|
sha=$(git -C "$REPO" rev-list -n1 HEAD)
|
|
git -C "$REPO" worktree add -q "$ROOT/.worktrees/$sha" -b e2e --no-checkout
|
|
chmod g+w "$ROOT/.worktrees/$sha"
|
|
|
|
# add some garbage
|
|
mkdir -p "$ROOT/.worktrees/not_a_hash/subdir"
|
|
touch "$ROOT/.worktrees/not_a_directory"
|
|
|
|
# resume time
|
|
docker ps --filter label="git-sync-e2e=$RUNID" --format="{{.ID}}" \
|
|
| while read -r ctr; do
|
|
docker unpause "$ctr" >/dev/null
|
|
done
|
|
|
|
wait_for_sync "${MAXWAIT}"
|
|
assert_file_exists "$ROOT/link/file2"
|
|
assert_file_eq "$ROOT/link/file2" "${FUNCNAME[0]}-ok"
|
|
assert_metric_eq "${METRIC_GOOD_SYNC_COUNT}" 2
|
|
assert_metric_eq "${METRIC_FETCH_COUNT}" 2
|
|
assert_file_absent "$ROOT/.worktrees/$sha"
|
|
assert_file_absent "$ROOT/.worktrees/not_a_hash"
|
|
assert_file_absent "$ROOT/.worktrees/not_a_directory"
|
|
}
|
|
|
|
##############################################
|
|
# Test worktree unexpected removal
|
|
##############################################
|
|
function e2e::worktree_unexpected_removal() {
|
|
GIT_SYNC \
|
|
--period=100ms \
|
|
--repo="file://$REPO" \
|
|
--root="$ROOT" \
|
|
--link="link" \
|
|
&
|
|
|
|
# wait for first sync
|
|
wait_for_sync "${MAXWAIT}"
|
|
assert_link_exists "$ROOT/link"
|
|
assert_file_exists "$ROOT/link/file"
|
|
assert_file_eq "$ROOT/link/file" "${FUNCNAME[0]}"
|
|
assert_metric_eq "${METRIC_GOOD_SYNC_COUNT}" 1
|
|
assert_metric_eq "${METRIC_FETCH_COUNT}" 1
|
|
|
|
# suspend time so we can fake corruption
|
|
docker ps --filter label="git-sync-e2e=$RUNID" --format="{{.ID}}" \
|
|
| while read -r ctr; do
|
|
docker pause "$ctr" >/dev/null
|
|
done
|
|
|
|
# make a unexpected removal
|
|
local wt
|
|
wt=$(git -C "$REPO" rev-list -n1 HEAD)
|
|
rm -r "$ROOT/.worktrees/$wt"
|
|
|
|
# resume time
|
|
docker ps --filter label="git-sync-e2e=$RUNID" --format="{{.ID}}" \
|
|
| while read -r ctr; do
|
|
docker unpause "$ctr" >/dev/null
|
|
done
|
|
|
|
wait_for_sync "${MAXWAIT}"
|
|
assert_link_exists "$ROOT/link"
|
|
assert_file_exists "$ROOT/link/file"
|
|
assert_file_eq "$ROOT/link/file" "${FUNCNAME[0]}"
|
|
assert_metric_eq "${METRIC_GOOD_SYNC_COUNT}" 2
|
|
assert_metric_eq "${METRIC_FETCH_COUNT}" 2
|
|
}
|
|
|
|
##############################################
|
|
# Test syncing when the worktree is wrong hash
|
|
##############################################
|
|
function e2e::sync_recover_wrong_worktree_hash() {
|
|
GIT_SYNC \
|
|
--period=100ms \
|
|
--repo="file://$REPO" \
|
|
--root="$ROOT" \
|
|
--link="link" \
|
|
&
|
|
|
|
# wait for first sync
|
|
wait_for_sync "${MAXWAIT}"
|
|
assert_link_exists "$ROOT/link"
|
|
assert_file_exists "$ROOT/link/file"
|
|
assert_file_eq "$ROOT/link/file" "${FUNCNAME[0]}"
|
|
assert_metric_eq "${METRIC_GOOD_SYNC_COUNT}" 1
|
|
assert_metric_eq "${METRIC_FETCH_COUNT}" 1
|
|
|
|
# suspend time so we can fake corruption
|
|
docker ps --filter label="git-sync-e2e=$RUNID" --format="{{.ID}}" \
|
|
| while read -r ctr; do
|
|
docker pause "$ctr" >/dev/null
|
|
done
|
|
|
|
# Corrupt it
|
|
echo "unexpected" > "$ROOT/link/file"
|
|
git -C "$ROOT/link" commit -qam "corrupt it"
|
|
|
|
# resume time
|
|
docker ps --filter label="git-sync-e2e=$RUNID" --format="{{.ID}}" \
|
|
| while read -r ctr; do
|
|
docker unpause "$ctr" >/dev/null
|
|
done
|
|
|
|
wait_for_sync "${MAXWAIT}"
|
|
assert_link_exists "$ROOT/link"
|
|
assert_file_exists "$ROOT/link/file"
|
|
assert_file_eq "$ROOT/link/file" "${FUNCNAME[0]}"
|
|
assert_metric_eq "${METRIC_GOOD_SYNC_COUNT}" 2
|
|
assert_metric_eq "${METRIC_FETCH_COUNT}" 2
|
|
}
|
|
|
|
##############################################
|
|
# Test stale-worktree-timeout
|
|
##############################################
|
|
function e2e::stale_worktree_timeout() {
|
|
echo "${FUNCNAME[0]} 1" > "$REPO"/file
|
|
git -C "$REPO" commit -qam "${FUNCNAME[0]}"
|
|
local wt1
|
|
wt1=$(git -C "$REPO" rev-list -n1 HEAD)
|
|
GIT_SYNC \
|
|
--period=100ms \
|
|
--repo="file://$REPO" \
|
|
--root="$ROOT" \
|
|
--link="link" \
|
|
--stale-worktree-timeout="5s" \
|
|
&
|
|
|
|
# wait for first sync
|
|
wait_for_sync "${MAXWAIT}"
|
|
assert_link_exists "$ROOT/link"
|
|
assert_file_exists "$ROOT/link/file"
|
|
assert_file_eq "$ROOT/link/file" "${FUNCNAME[0]} 1"
|
|
|
|
# wait 2 seconds and make another commit
|
|
sleep 2
|
|
echo "${FUNCNAME[0]} 2" > "$REPO"/file2
|
|
git -C "$REPO" add file2
|
|
git -C "$REPO" commit -qam "${FUNCNAME[0]} new file"
|
|
local wt2
|
|
wt2=$(git -C "$REPO" rev-list -n1 HEAD)
|
|
|
|
# wait for second sync
|
|
wait_for_sync "${MAXWAIT}"
|
|
# at this point both wt1 and wt2 should exist, with
|
|
# link pointing to the new wt2
|
|
assert_link_exists "$ROOT/link"
|
|
assert_file_exists "$ROOT/link/file"
|
|
assert_file_exists "$ROOT/link/file2"
|
|
assert_file_exists "$ROOT/.worktrees/$wt1/file"
|
|
assert_file_absent "$ROOT/.worktrees/$wt1/file2"
|
|
|
|
# wait 2 seconds and make a third commit
|
|
sleep 2
|
|
echo "${FUNCNAME[0]} 3" > "$REPO"/file3
|
|
git -C "$REPO" add file3
|
|
git -C "$REPO" commit -qam "${FUNCNAME[0]} new file"
|
|
local wt3
|
|
wt3=$(git -C "$REPO" rev-list -n1 HEAD)
|
|
|
|
wait_for_sync "${MAXWAIT}"
|
|
|
|
# at this point wt1, wt2, wt3 should exist, with
|
|
# link pointing to wt3
|
|
assert_link_exists "$ROOT/link"
|
|
assert_file_exists "$ROOT/link/file"
|
|
assert_file_exists "$ROOT/link/file2"
|
|
assert_file_exists "$ROOT/link/file3"
|
|
assert_file_exists "$ROOT/.worktrees/$wt1/file"
|
|
assert_file_absent "$ROOT/.worktrees/$wt1/file2"
|
|
assert_file_absent "$ROOT/.worktrees/$wt1/file3"
|
|
assert_file_exists "$ROOT/.worktrees/$wt2/file"
|
|
assert_file_exists "$ROOT/.worktrees/$wt2/file2"
|
|
assert_file_absent "$ROOT/.worktrees/$wt2/file3"
|
|
assert_file_exists "$ROOT/.worktrees/$wt3/file"
|
|
assert_file_exists "$ROOT/.worktrees/$wt3/file2"
|
|
assert_file_exists "$ROOT/.worktrees/$wt3/file3"
|
|
|
|
# wait for wt1 to go stale
|
|
sleep 4
|
|
|
|
# now wt1 should be stale and deleted,
|
|
# wt2 and wt3 should still exist
|
|
assert_link_exists "$ROOT/link"
|
|
assert_file_exists "$ROOT/link/file"
|
|
assert_file_exists "$ROOT/link/file2"
|
|
assert_file_exists "$ROOT/link/file3"
|
|
assert_file_absent "$ROOT/.worktrees/$wt1/file"
|
|
assert_file_absent "$ROOT/.worktrees/$wt1/file2"
|
|
assert_file_absent "$ROOT/.worktrees/$wt1/file3"
|
|
assert_file_exists "$ROOT/.worktrees/$wt2/file"
|
|
assert_file_exists "$ROOT/.worktrees/$wt2/file2"
|
|
assert_file_absent "$ROOT/.worktrees/$wt2/file3"
|
|
assert_file_exists "$ROOT/.worktrees/$wt3/file"
|
|
assert_file_exists "$ROOT/.worktrees/$wt3/file2"
|
|
assert_file_exists "$ROOT/.worktrees/$wt3/file3"
|
|
|
|
# wait for wt2 to go stale
|
|
sleep 2
|
|
|
|
# now both wt1 and wt2 are stale, wt3 should be the only
|
|
# worktree left
|
|
assert_link_exists "$ROOT/link"
|
|
assert_file_exists "$ROOT/link/file"
|
|
assert_file_exists "$ROOT/link/file2"
|
|
assert_file_exists "$ROOT/link/file3"
|
|
assert_file_absent "$ROOT/.worktrees/$wt1/file"
|
|
assert_file_absent "$ROOT/.worktrees/$wt1/file2"
|
|
assert_file_absent "$ROOT/.worktrees/$wt1/file3"
|
|
assert_file_absent "$ROOT/.worktrees/$wt2/file"
|
|
assert_file_absent "$ROOT/.worktrees/$wt2/file2"
|
|
assert_file_absent "$ROOT/.worktrees/$wt2/file3"
|
|
assert_file_exists "$ROOT/.worktrees/$wt3/file"
|
|
assert_file_exists "$ROOT/.worktrees/$wt3/file2"
|
|
assert_file_exists "$ROOT/.worktrees/$wt3/file3"
|
|
}
|
|
|
|
##############################################
|
|
# Test stale-worktree-timeout with restarts
|
|
##############################################
|
|
function e2e::stale_worktree_timeout_restart() {
|
|
echo "${FUNCNAME[0]} 1" > "$REPO"/file
|
|
git -C "$REPO" commit -qam "${FUNCNAME[0]}"
|
|
local wt1
|
|
wt1=$(git -C "$REPO" rev-list -n1 HEAD)
|
|
GIT_SYNC \
|
|
--repo="file://$REPO" \
|
|
--root="$ROOT" \
|
|
--link="link" \
|
|
--one-time
|
|
|
|
assert_link_exists "$ROOT/link"
|
|
assert_file_exists "$ROOT/link/file"
|
|
assert_file_eq "$ROOT/link/file" "${FUNCNAME[0]} 1"
|
|
|
|
# wait 2 seconds and make another commit
|
|
sleep 2
|
|
echo "${FUNCNAME[0]} 2" > "$REPO"/file2
|
|
git -C "$REPO" add file2
|
|
git -C "$REPO" commit -qam "${FUNCNAME[0]} new file"
|
|
local wt2
|
|
wt2=$(git -C "$REPO" rev-list -n1 HEAD)
|
|
|
|
# restart git-sync
|
|
GIT_SYNC \
|
|
--repo="file://$REPO" \
|
|
--root="$ROOT" \
|
|
--link="link" \
|
|
--stale-worktree-timeout="10s" \
|
|
--one-time
|
|
|
|
# at this point both wt1 and wt2 should exist, with
|
|
# link pointing to the new wt2
|
|
assert_link_exists "$ROOT/link"
|
|
assert_file_exists "$ROOT/link/file"
|
|
assert_file_exists "$ROOT/link/file2"
|
|
assert_file_exists "$ROOT/.worktrees/$wt1/file"
|
|
assert_file_absent "$ROOT/.worktrees/$wt1/file2"
|
|
|
|
# wait 2 seconds and make a third commit
|
|
sleep 4
|
|
echo "${FUNCNAME[0]} 3" > "$REPO"/file3
|
|
git -C "$REPO" add file3
|
|
git -C "$REPO" commit -qam "${FUNCNAME[0]} new file"
|
|
local wt3
|
|
wt3=$(git -C "$REPO" rev-list -n1 HEAD)
|
|
|
|
# restart git-sync
|
|
GIT_SYNC \
|
|
--repo="file://$REPO" \
|
|
--root="$ROOT" \
|
|
--link="link" \
|
|
--stale-worktree-timeout="10s" \
|
|
--one-time
|
|
|
|
# at this point wt1, wt2, wt3 should exist, with
|
|
# link pointing to wt3
|
|
assert_link_exists "$ROOT/link"
|
|
assert_file_exists "$ROOT/link/file"
|
|
assert_file_exists "$ROOT/link/file2"
|
|
assert_file_exists "$ROOT/link/file3"
|
|
assert_file_exists "$ROOT/.worktrees/$wt1/file"
|
|
assert_file_absent "$ROOT/.worktrees/$wt1/file2"
|
|
assert_file_absent "$ROOT/.worktrees/$wt1/file3"
|
|
assert_file_exists "$ROOT/.worktrees/$wt2/file"
|
|
assert_file_exists "$ROOT/.worktrees/$wt2/file2"
|
|
assert_file_absent "$ROOT/.worktrees/$wt2/file3"
|
|
assert_file_exists "$ROOT/.worktrees/$wt3/file"
|
|
assert_file_exists "$ROOT/.worktrees/$wt3/file2"
|
|
assert_file_exists "$ROOT/.worktrees/$wt3/file3"
|
|
|
|
# wait for wt1 to go stale and restart git-sync
|
|
sleep 8
|
|
GIT_SYNC \
|
|
--repo="file://$REPO" \
|
|
--root="$ROOT" \
|
|
--link="link" \
|
|
--stale-worktree-timeout="10s" \
|
|
--one-time
|
|
|
|
# now wt1 should be stale and deleted,
|
|
# wt2 and wt3 should still exist
|
|
assert_link_exists "$ROOT/link"
|
|
assert_file_exists "$ROOT/link/file"
|
|
assert_file_exists "$ROOT/link/file2"
|
|
assert_file_exists "$ROOT/link/file3"
|
|
assert_file_absent "$ROOT/.worktrees/$wt1/file"
|
|
assert_file_absent "$ROOT/.worktrees/$wt1/file2"
|
|
assert_file_absent "$ROOT/.worktrees/$wt1/file3"
|
|
assert_file_exists "$ROOT/.worktrees/$wt2/file"
|
|
assert_file_exists "$ROOT/.worktrees/$wt2/file2"
|
|
assert_file_absent "$ROOT/.worktrees/$wt2/file3"
|
|
assert_file_exists "$ROOT/.worktrees/$wt3/file"
|
|
assert_file_exists "$ROOT/.worktrees/$wt3/file2"
|
|
assert_file_exists "$ROOT/.worktrees/$wt3/file3"
|
|
|
|
# wait for wt2 to go stale and restart git-sync
|
|
sleep 4
|
|
GIT_SYNC \
|
|
--repo="file://$REPO" \
|
|
--root="$ROOT" \
|
|
--link="link" \
|
|
--stale-worktree-timeout="10s" \
|
|
--one-time
|
|
|
|
# now both wt1 and wt2 are stale, wt3 should be the only
|
|
# worktree left
|
|
assert_link_exists "$ROOT/link"
|
|
assert_file_exists "$ROOT/link/file"
|
|
assert_file_exists "$ROOT/link/file2"
|
|
assert_file_exists "$ROOT/link/file3"
|
|
assert_file_absent "$ROOT/.worktrees/$wt1/file"
|
|
assert_file_absent "$ROOT/.worktrees/$wt1/file2"
|
|
assert_file_absent "$ROOT/.worktrees/$wt1/file3"
|
|
assert_file_absent "$ROOT/.worktrees/$wt2/file"
|
|
assert_file_absent "$ROOT/.worktrees/$wt2/file2"
|
|
assert_file_absent "$ROOT/.worktrees/$wt2/file3"
|
|
assert_file_exists "$ROOT/.worktrees/$wt3/file"
|
|
assert_file_exists "$ROOT/.worktrees/$wt3/file2"
|
|
assert_file_exists "$ROOT/.worktrees/$wt3/file3"
|
|
}
|
|
|
|
##############################################
|
|
# Test v3->v4 upgrade
|
|
##############################################
|
|
function e2e::v3_v4_upgrade_in_place() {
|
|
echo "${FUNCNAME[0]} 1" > "$REPO/file"
|
|
git -C "$REPO" commit -qam "${FUNCNAME[0]}"
|
|
|
|
# sync once
|
|
GIT_SYNC \
|
|
--one-time \
|
|
--repo="file://$REPO" \
|
|
--root="$ROOT" \
|
|
--link="link"
|
|
|
|
assert_link_exists "$ROOT/link"
|
|
assert_file_exists "$ROOT/link/file"
|
|
assert_file_eq "$ROOT/link/file" "${FUNCNAME[0]} 1"
|
|
|
|
# simulate v3's worktrees
|
|
local wt
|
|
wt="$(readlink "$ROOT/link")"
|
|
local sha
|
|
sha="$(basename "$wt")"
|
|
mv -f "$ROOT/$wt" "$ROOT/$sha"
|
|
ln -sf "$sha" "$ROOT/link"
|
|
|
|
# make a second commit
|
|
echo "${FUNCNAME[0]} 2" > "$REPO/file2"
|
|
git -C "$REPO" add file2
|
|
git -C "$REPO" commit -qam "${FUNCNAME[0]} new file"
|
|
|
|
# sync again
|
|
GIT_SYNC \
|
|
--one-time \
|
|
--repo="file://$REPO" \
|
|
--root="$ROOT" \
|
|
--link="link"
|
|
|
|
assert_link_exists "$ROOT/link"
|
|
assert_file_exists "$ROOT/link/file"
|
|
assert_file_eq "$ROOT/link/file" "${FUNCNAME[0]} 1"
|
|
assert_file_exists "$ROOT/link/file2"
|
|
assert_file_eq "$ROOT/link/file2" "${FUNCNAME[0]} 2"
|
|
assert_file_absent "$ROOT/$sha"
|
|
}
|
|
|
|
##############################################
|
|
# Test readlink
|
|
##############################################
|
|
function e2e::readlink() {
|
|
# First sync
|
|
echo "${FUNCNAME[0]} 1" > "$REPO/file"
|
|
git -C "$REPO" commit -qam "${FUNCNAME[0]} 1"
|
|
|
|
GIT_SYNC \
|
|
--period=100ms \
|
|
--repo="file://$REPO" \
|
|
--root="$ROOT" \
|
|
--link="link" \
|
|
&
|
|
wait_for_sync "${MAXWAIT}"
|
|
assert_link_exists "$ROOT/link"
|
|
assert_link_basename_eq "$ROOT/link" "$(git -C "$REPO" rev-parse HEAD)"
|
|
|
|
# Move HEAD forward
|
|
echo "${FUNCNAME[0]} 2" > "$REPO/file"
|
|
git -C "$REPO" commit -qam "${FUNCNAME[0]} 2"
|
|
wait_for_sync "${MAXWAIT}"
|
|
assert_link_exists "$ROOT/link"
|
|
assert_link_basename_eq "$ROOT/link" "$(git -C "$REPO" rev-parse HEAD)"
|
|
|
|
# Move HEAD backward
|
|
git -C "$REPO" reset -q --hard HEAD^
|
|
wait_for_sync "${MAXWAIT}"
|
|
assert_link_exists "$ROOT/link"
|
|
assert_link_basename_eq "$ROOT/link" "$(git -C "$REPO" rev-parse HEAD)"
|
|
}
|
|
|
|
##############################################
|
|
# Test branch syncing
|
|
##############################################
|
|
function e2e::sync_branch() {
|
|
local other_branch="other-branch"
|
|
|
|
# First sync
|
|
git -C "$REPO" checkout -q -b "$other_branch"
|
|
echo "${FUNCNAME[0]} 1" > "$REPO/file"
|
|
git -C "$REPO" commit -qam "${FUNCNAME[0]} 1"
|
|
git -C "$REPO" checkout -q "$MAIN_BRANCH"
|
|
|
|
GIT_SYNC \
|
|
--period=100ms \
|
|
--repo="file://$REPO" \
|
|
--ref="$other_branch" \
|
|
--root="$ROOT" \
|
|
--link="link" \
|
|
&
|
|
wait_for_sync "${MAXWAIT}"
|
|
assert_link_exists "$ROOT/link"
|
|
assert_file_exists "$ROOT/link/file"
|
|
assert_file_eq "$ROOT/link/file" "${FUNCNAME[0]} 1"
|
|
assert_metric_eq "${METRIC_GOOD_SYNC_COUNT}" 1
|
|
assert_metric_eq "${METRIC_FETCH_COUNT}" 1
|
|
|
|
# Add to the branch.
|
|
git -C "$REPO" checkout -q "$other_branch"
|
|
echo "${FUNCNAME[0]} 2" > "$REPO/file"
|
|
git -C "$REPO" commit -qam "${FUNCNAME[0]} 2"
|
|
git -C "$REPO" checkout -q "$MAIN_BRANCH"
|
|
wait_for_sync "${MAXWAIT}"
|
|
assert_link_exists "$ROOT/link"
|
|
assert_file_exists "$ROOT/link/file"
|
|
assert_file_eq "$ROOT/link/file" "${FUNCNAME[0]} 2"
|
|
assert_metric_eq "${METRIC_GOOD_SYNC_COUNT}" 2
|
|
assert_metric_eq "${METRIC_FETCH_COUNT}" 2
|
|
|
|
# Move the branch backward
|
|
git -C "$REPO" checkout -q "$other_branch"
|
|
git -C "$REPO" reset -q --hard HEAD^
|
|
git -C "$REPO" checkout -q "$MAIN_BRANCH"
|
|
wait_for_sync "${MAXWAIT}"
|
|
assert_link_exists "$ROOT/link"
|
|
assert_file_exists "$ROOT/link/file"
|
|
assert_file_eq "$ROOT/link/file" "${FUNCNAME[0]} 1"
|
|
assert_metric_eq "${METRIC_GOOD_SYNC_COUNT}" 3
|
|
assert_metric_eq "${METRIC_FETCH_COUNT}" 3
|
|
}
|
|
|
|
##############################################
|
|
# Test switching branch after depth=1 checkout
|
|
##############################################
|
|
function e2e::sync_branch_switch() {
|
|
# First sync
|
|
echo "${FUNCNAME[0]} 1" > "$REPO/file"
|
|
git -C "$REPO" commit -qam "${FUNCNAME[0]} 1"
|
|
|
|
GIT_SYNC \
|
|
--one-time \
|
|
--repo="file://$REPO" \
|
|
--ref="$MAIN_BRANCH" \
|
|
--depth=1 \
|
|
--root="$ROOT" \
|
|
--link="link"
|
|
assert_link_exists "$ROOT/link"
|
|
assert_file_exists "$ROOT/link/file"
|
|
assert_file_eq "$ROOT/link/file" "${FUNCNAME[0]} 1"
|
|
|
|
local other_branch="${MAIN_BRANCH}2"
|
|
git -C "$REPO" checkout -q -b $other_branch
|
|
echo "${FUNCNAME[0]} 2" > "$REPO/file"
|
|
git -C "$REPO" commit -qam "${FUNCNAME[0]} 2"
|
|
|
|
GIT_SYNC \
|
|
--one-time \
|
|
--repo="file://$REPO" \
|
|
--ref="$other_branch" \
|
|
--root="$ROOT" \
|
|
--link="link"
|
|
assert_link_exists "$ROOT/link"
|
|
assert_file_exists "$ROOT/link/file"
|
|
assert_file_eq "$ROOT/link/file" "${FUNCNAME[0]} 2"
|
|
}
|
|
|
|
##############################################
|
|
# Test tag syncing
|
|
##############################################
|
|
function e2e::sync_tag() {
|
|
local tag="e2e-tag"
|
|
|
|
# First sync
|
|
echo "${FUNCNAME[0]} 1" > "$REPO/file"
|
|
git -C "$REPO" commit -qam "${FUNCNAME[0]} 1"
|
|
git -C "$REPO" tag -f "$tag" >/dev/null
|
|
|
|
GIT_SYNC \
|
|
--period=100ms \
|
|
--repo="file://$REPO" \
|
|
--ref="$tag" \
|
|
--root="$ROOT" \
|
|
--link="link" \
|
|
&
|
|
wait_for_sync "${MAXWAIT}"
|
|
assert_link_exists "$ROOT/link"
|
|
assert_file_exists "$ROOT/link/file"
|
|
assert_file_eq "$ROOT/link/file" "${FUNCNAME[0]} 1"
|
|
assert_metric_eq "${METRIC_GOOD_SYNC_COUNT}" 1
|
|
assert_metric_eq "${METRIC_FETCH_COUNT}" 1
|
|
|
|
# Add something and move the tag forward
|
|
echo "${FUNCNAME[0]} 2" > "$REPO/file"
|
|
git -C "$REPO" commit -qam "${FUNCNAME[0]} 2"
|
|
git -C "$REPO" tag -f "$tag" >/dev/null
|
|
wait_for_sync "${MAXWAIT}"
|
|
assert_link_exists "$ROOT/link"
|
|
assert_file_exists "$ROOT/link/file"
|
|
assert_file_eq "$ROOT/link/file" "${FUNCNAME[0]} 2"
|
|
assert_metric_eq "${METRIC_GOOD_SYNC_COUNT}" 2
|
|
assert_metric_eq "${METRIC_FETCH_COUNT}" 2
|
|
|
|
# Move the tag backward
|
|
git -C "$REPO" reset -q --hard HEAD^
|
|
git -C "$REPO" tag -f "$tag" >/dev/null
|
|
wait_for_sync "${MAXWAIT}"
|
|
assert_link_exists "$ROOT/link"
|
|
assert_file_exists "$ROOT/link/file"
|
|
assert_file_eq "$ROOT/link/file" "${FUNCNAME[0]} 1"
|
|
assert_metric_eq "${METRIC_GOOD_SYNC_COUNT}" 3
|
|
assert_metric_eq "${METRIC_FETCH_COUNT}" 3
|
|
|
|
# Add something after the tag
|
|
echo "${FUNCNAME[0]} 3" > "$REPO/file"
|
|
git -C "$REPO" commit -qam "${FUNCNAME[0]} 3"
|
|
sleep 1 # touch-file will not be touched
|
|
assert_link_exists "$ROOT/link"
|
|
assert_file_exists "$ROOT/link/file"
|
|
assert_file_eq "$ROOT/link/file" "${FUNCNAME[0]} 1"
|
|
assert_metric_eq "${METRIC_GOOD_SYNC_COUNT}" 3
|
|
assert_metric_eq "${METRIC_FETCH_COUNT}" 3
|
|
}
|
|
|
|
##############################################
|
|
# Test tag syncing with annotated tags
|
|
##############################################
|
|
function e2e::sync_annotated_tag() {
|
|
local tag="e2e-tag"
|
|
|
|
# First sync
|
|
echo "${FUNCNAME[0]} 1" > "$REPO/file"
|
|
git -C "$REPO" commit -qam "${FUNCNAME[0]} 1"
|
|
git -C "$REPO" tag -af "$tag" -m "${FUNCNAME[0]} 1" >/dev/null
|
|
|
|
GIT_SYNC \
|
|
--period=100ms \
|
|
--repo="file://$REPO" \
|
|
--ref="$tag" \
|
|
--root="$ROOT" \
|
|
--link="link" \
|
|
&
|
|
wait_for_sync "${MAXWAIT}"
|
|
assert_link_exists "$ROOT/link"
|
|
assert_file_exists "$ROOT/link/file"
|
|
assert_file_eq "$ROOT/link/file" "${FUNCNAME[0]} 1"
|
|
assert_metric_eq "${METRIC_GOOD_SYNC_COUNT}" 1
|
|
assert_metric_eq "${METRIC_FETCH_COUNT}" 1
|
|
|
|
# Add something and move the tag forward
|
|
echo "${FUNCNAME[0]} 2" > "$REPO/file"
|
|
git -C "$REPO" commit -qam "${FUNCNAME[0]} 2"
|
|
git -C "$REPO" tag -af "$tag" -m "${FUNCNAME[0]} 2" >/dev/null
|
|
wait_for_sync "${MAXWAIT}"
|
|
assert_link_exists "$ROOT/link"
|
|
assert_file_exists "$ROOT/link/file"
|
|
assert_file_eq "$ROOT/link/file" "${FUNCNAME[0]} 2"
|
|
assert_metric_eq "${METRIC_GOOD_SYNC_COUNT}" 2
|
|
assert_metric_eq "${METRIC_FETCH_COUNT}" 2
|
|
|
|
# Move the tag backward
|
|
git -C "$REPO" reset -q --hard HEAD^
|
|
git -C "$REPO" tag -af "$tag" -m "${FUNCNAME[0]} 3" >/dev/null
|
|
wait_for_sync "${MAXWAIT}"
|
|
assert_link_exists "$ROOT/link"
|
|
assert_file_exists "$ROOT/link/file"
|
|
assert_file_eq "$ROOT/link/file" "${FUNCNAME[0]} 1"
|
|
assert_metric_eq "${METRIC_GOOD_SYNC_COUNT}" 3
|
|
assert_metric_eq "${METRIC_FETCH_COUNT}" 3
|
|
|
|
# Add something after the tag
|
|
echo "${FUNCNAME[0]} 3" > "$REPO/file"
|
|
git -C "$REPO" commit -qam "${FUNCNAME[0]} 3"
|
|
sleep 1 # touch-file will not be touched
|
|
assert_link_exists "$ROOT/link"
|
|
assert_file_exists "$ROOT/link/file"
|
|
assert_file_eq "$ROOT/link/file" "${FUNCNAME[0]} 1"
|
|
assert_metric_eq "${METRIC_GOOD_SYNC_COUNT}" 3
|
|
assert_metric_eq "${METRIC_FETCH_COUNT}" 3
|
|
}
|
|
|
|
##############################################
|
|
# Test SHA syncing
|
|
##############################################
|
|
function e2e::sync_sha() {
|
|
# First sync
|
|
echo "${FUNCNAME[0]} 1" > "$REPO/file"
|
|
git -C "$REPO" commit -qam "${FUNCNAME[0]} 1"
|
|
local sha
|
|
sha=$(git -C "$REPO" rev-list -n1 HEAD)
|
|
|
|
GIT_SYNC \
|
|
--period=100ms \
|
|
--repo="file://$REPO" \
|
|
--ref="$sha" \
|
|
--root="$ROOT" \
|
|
--link="link" \
|
|
&
|
|
wait_for_sync "${MAXWAIT}"
|
|
assert_link_exists "$ROOT/link"
|
|
assert_file_exists "$ROOT/link/file"
|
|
assert_file_eq "$ROOT/link/file" "${FUNCNAME[0]} 1"
|
|
assert_metric_eq "${METRIC_GOOD_SYNC_COUNT}" 1
|
|
assert_metric_eq "${METRIC_FETCH_COUNT}" 1
|
|
|
|
# Commit something new
|
|
echo "${FUNCNAME[0]} 2" > "$REPO/file"
|
|
git -C "$REPO" commit -qam "${FUNCNAME[0]} 2"
|
|
sleep 1 # touch-file will not be touched
|
|
assert_link_exists "$ROOT/link"
|
|
assert_file_exists "$ROOT/link/file"
|
|
assert_file_eq "$ROOT/link/file" "${FUNCNAME[0]} 1"
|
|
assert_metric_eq "${METRIC_GOOD_SYNC_COUNT}" 1
|
|
assert_metric_eq "${METRIC_FETCH_COUNT}" 1
|
|
|
|
# Revert the last change
|
|
git -C "$REPO" reset -q --hard HEAD^
|
|
sleep 1 # touch-file will not be touched
|
|
assert_link_exists "$ROOT/link"
|
|
assert_file_exists "$ROOT/link/file"
|
|
assert_file_eq "$ROOT/link/file" "${FUNCNAME[0]} 1"
|
|
assert_metric_eq "${METRIC_GOOD_SYNC_COUNT}" 1
|
|
assert_metric_eq "${METRIC_FETCH_COUNT}" 1
|
|
}
|
|
|
|
##############################################
|
|
# Test SHA-sync one-time
|
|
##############################################
|
|
function e2e::sync_sha_once() {
|
|
local sha
|
|
sha=$(git -C "$REPO" rev-list -n1 HEAD)
|
|
|
|
GIT_SYNC \
|
|
--one-time \
|
|
--repo="file://$REPO" \
|
|
--ref="$sha" \
|
|
--root="$ROOT" \
|
|
--link="link"
|
|
assert_link_exists "$ROOT/link"
|
|
assert_file_exists "$ROOT/link/file"
|
|
assert_file_eq "$ROOT/link/file" "${FUNCNAME[0]}"
|
|
}
|
|
|
|
##############################################
|
|
# Test SHA-sync on a different SHA we already have
|
|
##############################################
|
|
function e2e::sync_sha_once_sync_different_sha_known() {
|
|
# All revs will be known because we check out the branch
|
|
echo "${FUNCNAME[0]} 1" > "$REPO/file"
|
|
git -C "$REPO" commit -qam "${FUNCNAME[0]} 1"
|
|
local sha1
|
|
sha1=$(git -C "$REPO" rev-list -n1 HEAD)
|
|
echo "${FUNCNAME[0]} 2" > "$REPO/file"
|
|
git -C "$REPO" commit -qam "${FUNCNAME[0]} 2"
|
|
local sha2
|
|
sha2=$(git -C "$REPO" rev-list -n1 HEAD)
|
|
echo "${FUNCNAME[0]} 3" > "$REPO/file"
|
|
git -C "$REPO" commit -qam "${FUNCNAME[0]} 3"
|
|
|
|
# Sync sha1
|
|
GIT_SYNC \
|
|
--one-time \
|
|
--repo="file://$REPO" \
|
|
--ref="$sha1" \
|
|
--root="$ROOT" \
|
|
--link="link"
|
|
assert_link_exists "$ROOT/link"
|
|
assert_file_exists "$ROOT/link/file"
|
|
assert_file_eq "$ROOT/link/file" "${FUNCNAME[0]} 1"
|
|
|
|
# Sync sha2
|
|
GIT_SYNC \
|
|
--one-time \
|
|
--repo="file://$REPO" \
|
|
--ref="$sha2" \
|
|
--root="$ROOT" \
|
|
--link="link"
|
|
assert_link_exists "$ROOT/link"
|
|
assert_file_exists "$ROOT/link/file"
|
|
assert_file_eq "$ROOT/link/file" "${FUNCNAME[0]} 2"
|
|
}
|
|
|
|
##############################################
|
|
# Test SHA-sync on a different SHA we do not have
|
|
##############################################
|
|
function e2e::sync_sha_once_sync_different_sha_unknown() {
|
|
echo "${FUNCNAME[0]} 1" > "$REPO/file"
|
|
git -C "$REPO" commit -qam "${FUNCNAME[0]} 1"
|
|
local sha1
|
|
sha1=$(git -C "$REPO" rev-list -n1 HEAD)
|
|
|
|
# Sync sha1
|
|
GIT_SYNC \
|
|
--one-time \
|
|
--repo="file://$REPO" \
|
|
--ref="$sha1" \
|
|
--root="$ROOT" \
|
|
--link="link"
|
|
assert_link_exists "$ROOT/link"
|
|
assert_file_exists "$ROOT/link/file"
|
|
assert_file_eq "$ROOT/link/file" "${FUNCNAME[0]} 1"
|
|
|
|
# The locally synced repo does not know this new SHA.
|
|
echo "${FUNCNAME[0]} 2" > "$REPO/file"
|
|
git -C "$REPO" commit -qam "${FUNCNAME[0]} 2"
|
|
local sha2
|
|
sha2=$(git -C "$REPO" rev-list -n1 HEAD)
|
|
# Make sure the SHA is not at HEAD, to prevent things that only work in
|
|
# that case.
|
|
echo "${FUNCNAME[0]} 3" > "$REPO/file"
|
|
git -C "$REPO" commit -qam "${FUNCNAME[0]} 3"
|
|
|
|
# Sync sha2
|
|
GIT_SYNC \
|
|
--one-time \
|
|
--repo="file://$REPO" \
|
|
--ref="$sha2" \
|
|
--root="$ROOT" \
|
|
--link="link"
|
|
assert_link_exists "$ROOT/link"
|
|
assert_file_exists "$ROOT/link/file"
|
|
assert_file_eq "$ROOT/link/file" "${FUNCNAME[0]} 2"
|
|
}
|
|
|
|
##############################################
|
|
# Test syncing after a crash
|
|
##############################################
|
|
function e2e::sync_crash_no_link_cleanup_retry() {
|
|
# First sync
|
|
GIT_SYNC \
|
|
--one-time \
|
|
--repo="file://$REPO" \
|
|
--root="$ROOT" \
|
|
--link="link"
|
|
assert_link_exists "$ROOT/link"
|
|
assert_file_exists "$ROOT/link/file"
|
|
assert_file_eq "$ROOT/link/file" "${FUNCNAME[0]}"
|
|
|
|
# Corrupt it
|
|
rm -f "$ROOT/link"
|
|
|
|
# Try again
|
|
GIT_SYNC \
|
|
--one-time \
|
|
--repo="file://$REPO" \
|
|
--root="$ROOT" \
|
|
--link="link"
|
|
assert_link_exists "$ROOT/link"
|
|
assert_file_exists "$ROOT/link/file"
|
|
assert_file_eq "$ROOT/link/file" "${FUNCNAME[0]}"
|
|
}
|
|
|
|
##############################################
|
|
# Test syncing after a crash
|
|
##############################################
|
|
function e2e::sync_crash_no_worktree_cleanup_retry() {
|
|
# First sync
|
|
GIT_SYNC \
|
|
--one-time \
|
|
--repo="file://$REPO" \
|
|
--root="$ROOT" \
|
|
--link="link"
|
|
assert_link_exists "$ROOT/link"
|
|
assert_file_exists "$ROOT/link/file"
|
|
assert_file_eq "$ROOT/link/file" "${FUNCNAME[0]}"
|
|
|
|
# Corrupt it
|
|
rm -rf "$ROOT/.worktrees/"
|
|
|
|
# Try again
|
|
GIT_SYNC \
|
|
--one-time \
|
|
--repo="file://$REPO" \
|
|
--root="$ROOT" \
|
|
--link="link"
|
|
assert_link_exists "$ROOT/link"
|
|
assert_file_exists "$ROOT/link/file"
|
|
assert_file_eq "$ROOT/link/file" "${FUNCNAME[0]}"
|
|
}
|
|
|
|
##############################################
|
|
# Test syncing if a file named for the SHA exists
|
|
##############################################
|
|
function e2e::sync_sha_shafile_exists() {
|
|
echo "${FUNCNAME[0]} 1" > "$REPO/file"
|
|
git -C "$REPO" commit -qam "${FUNCNAME[0]} 1"
|
|
local sha1
|
|
sha1=$(git -C "$REPO" rev-list -n1 HEAD)
|
|
echo "${FUNCNAME[0]} 2" > "$REPO/file"
|
|
git -C "$REPO" commit -qam "${FUNCNAME[0]} 2"
|
|
local sha2
|
|
sha2=$(git -C "$REPO" rev-list -n1 HEAD)
|
|
|
|
GIT_SYNC \
|
|
--one-time \
|
|
--repo="file://$REPO" \
|
|
--ref="$sha1" \
|
|
--root="$ROOT" \
|
|
--link="link"
|
|
assert_link_exists "$ROOT/link"
|
|
assert_file_exists "$ROOT/link/file"
|
|
assert_file_eq "$ROOT/link/file" "${FUNCNAME[0]} 1"
|
|
|
|
touch "$ROOT/$sha2"
|
|
|
|
GIT_SYNC \
|
|
--one-time \
|
|
--repo="file://$REPO" \
|
|
--ref="$sha2" \
|
|
--root="$ROOT" \
|
|
--link="link"
|
|
assert_link_exists "$ROOT/link"
|
|
assert_file_exists "$ROOT/link/file"
|
|
assert_file_eq "$ROOT/link/file" "${FUNCNAME[0]} 2"
|
|
}
|
|
|
|
##############################################
|
|
# Test changing repos with storage intact
|
|
##############################################
|
|
function e2e::sync_repo_switch() {
|
|
# Prepare first repo
|
|
echo "${FUNCNAME[0]} 1" > "$REPO/file"
|
|
git -C "$REPO" commit -qam "${FUNCNAME[0]} 1"
|
|
|
|
# First sync
|
|
GIT_SYNC \
|
|
--repo="file://$REPO" \
|
|
--root="$ROOT" \
|
|
--link="link" \
|
|
--one-time
|
|
assert_link_exists "$ROOT/link"
|
|
assert_file_exists "$ROOT/link/file"
|
|
assert_file_eq "$ROOT/link/file" "${FUNCNAME[0]} 1"
|
|
|
|
# Prepare other repo
|
|
echo "${FUNCNAME[0]} 2" > "$REPO2/file"
|
|
git -C "$REPO2" commit -qam "${FUNCNAME[0]} 2"
|
|
|
|
# Now sync the other repo
|
|
GIT_SYNC \
|
|
--repo="file://$REPO2" \
|
|
--root="$ROOT" \
|
|
--link="link" \
|
|
--one-time
|
|
assert_link_exists "$ROOT/link"
|
|
assert_file_exists "$ROOT/link/file"
|
|
assert_file_eq "$ROOT/link/file" "${FUNCNAME[0]} 2"
|
|
}
|
|
|
|
##############################################
|
|
# Test with slow git, short timeout
|
|
##############################################
|
|
function e2e::error_slow_git_short_timeout() {
|
|
assert_fail \
|
|
GIT_SYNC \
|
|
--git="/$SLOW_GIT_FETCH" \
|
|
--one-time \
|
|
--sync-timeout=1s \
|
|
--repo="file://$REPO" \
|
|
--root="$ROOT" \
|
|
--link="link"
|
|
assert_file_absent "$ROOT/link/file"
|
|
}
|
|
|
|
##############################################
|
|
# Test with slow git, long timeout
|
|
##############################################
|
|
function e2e::sync_slow_git_long_timeout() {
|
|
# First sync
|
|
echo "${FUNCNAME[0]} 1" > "$REPO/file"
|
|
git -C "$REPO" commit -qam "${FUNCNAME[0]} 1"
|
|
|
|
# run with slow_git_clone but without timing out
|
|
GIT_SYNC \
|
|
--git="/$SLOW_GIT_FETCH" \
|
|
--period=100ms \
|
|
--sync-timeout=16s \
|
|
--repo="file://$REPO" \
|
|
--root="$ROOT" \
|
|
--link="link" \
|
|
&
|
|
wait_for_sync "$((MAXWAIT * 3))"
|
|
assert_link_exists "$ROOT/link"
|
|
assert_file_exists "$ROOT/link/file"
|
|
assert_file_eq "$ROOT/link/file" "${FUNCNAME[0]} 1"
|
|
assert_metric_eq "${METRIC_GOOD_SYNC_COUNT}" 1
|
|
assert_metric_eq "${METRIC_FETCH_COUNT}" 1
|
|
|
|
# Move forward
|
|
echo "${FUNCNAME[0]} 2" > "$REPO/file"
|
|
git -C "$REPO" commit -qam "${FUNCNAME[0]} 2"
|
|
wait_for_sync "$((MAXWAIT * 3))"
|
|
assert_link_exists "$ROOT/link"
|
|
assert_file_exists "$ROOT/link/file"
|
|
assert_file_eq "$ROOT/link/file" "${FUNCNAME[0]} 2"
|
|
assert_metric_eq "${METRIC_GOOD_SYNC_COUNT}" 2
|
|
assert_metric_eq "${METRIC_FETCH_COUNT}" 2
|
|
}
|
|
|
|
##############################################
|
|
# Test sync-on-signal with SIGHUP
|
|
##############################################
|
|
function e2e::sync_on_signal_sighup() {
|
|
# First sync
|
|
echo "${FUNCNAME[0]} 1" > "$REPO/file"
|
|
git -C "$REPO" commit -qam "${FUNCNAME[0]} 1"
|
|
|
|
GIT_SYNC \
|
|
--period=100s \
|
|
--sync-on-signal="SIGHUP" \
|
|
--repo="file://$REPO" \
|
|
--root="$ROOT" \
|
|
--link="link" \
|
|
&
|
|
wait_for_sync 3
|
|
assert_link_exists "$ROOT/link"
|
|
assert_file_exists "$ROOT/link/file"
|
|
assert_file_eq "$ROOT/link/file" "${FUNCNAME[0]} 1"
|
|
|
|
# Move HEAD forward
|
|
echo "${FUNCNAME[0]} 2" > "$REPO/file"
|
|
git -C "$REPO" commit -qam "${FUNCNAME[0]} 2"
|
|
# Send signal (note --period is 100s, signal should trigger sync)
|
|
local ctr
|
|
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[0]} 2"
|
|
}
|
|
|
|
##############################################
|
|
# Test sync-on-signal with HUP
|
|
##############################################
|
|
function e2e::sync_on_signal_hup() {
|
|
# First sync
|
|
echo "${FUNCNAME[0]} 1" > "$REPO/file"
|
|
git -C "$REPO" commit -qam "${FUNCNAME[0]} 1"
|
|
|
|
GIT_SYNC \
|
|
--period=100s \
|
|
--sync-on-signal="HUP" \
|
|
--repo="file://$REPO" \
|
|
--root="$ROOT" \
|
|
--link="link" \
|
|
&
|
|
wait_for_sync 3
|
|
assert_link_exists "$ROOT/link"
|
|
assert_file_exists "$ROOT/link/file"
|
|
assert_file_eq "$ROOT/link/file" "${FUNCNAME[0]} 1"
|
|
|
|
# Move HEAD forward
|
|
echo "${FUNCNAME[0]} 2" > "$REPO/file"
|
|
git -C "$REPO" commit -qam "${FUNCNAME[0]} 2"
|
|
# Send signal (note --period is 100s, signal should trigger sync)
|
|
local ctr
|
|
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[0]} 2"
|
|
}
|
|
|
|
##############################################
|
|
# Test sync-on-signal with 1 (SIGHUP)
|
|
##############################################
|
|
function e2e::sync_on_signal_1() {
|
|
# First sync
|
|
echo "${FUNCNAME[0]} 1" > "$REPO/file"
|
|
git -C "$REPO" commit -qam "${FUNCNAME[0]} 1"
|
|
|
|
GIT_SYNC \
|
|
--period=100s \
|
|
--sync-on-signal=1 \
|
|
--repo="file://$REPO" \
|
|
--root="$ROOT" \
|
|
--link="link" \
|
|
&
|
|
wait_for_sync 3
|
|
assert_link_exists "$ROOT/link"
|
|
assert_file_exists "$ROOT/link/file"
|
|
assert_file_eq "$ROOT/link/file" "${FUNCNAME[0]} 1"
|
|
|
|
# Move HEAD forward
|
|
echo "${FUNCNAME[0]} 2" > "$REPO/file"
|
|
git -C "$REPO" commit -qam "${FUNCNAME[0]} 2"
|
|
# Send signal (note --period is 100s, signal should trigger sync)
|
|
local ctr
|
|
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[0]} 2"
|
|
}
|
|
|
|
##############################################
|
|
# Test depth default is shallow
|
|
##############################################
|
|
function e2e::sync_depth_default_shallow() {
|
|
echo "${FUNCNAME[0]} 1" > "$REPO/file"
|
|
git -C "$REPO" commit -qam "${FUNCNAME[0]} 1"
|
|
echo "${FUNCNAME[0]} 2" > "$REPO/file"
|
|
git -C "$REPO" commit -qam "${FUNCNAME[0]} 2"
|
|
echo "${FUNCNAME[0]} 3" > "$REPO/file"
|
|
git -C "$REPO" commit -qam "${FUNCNAME[0]} 3"
|
|
|
|
GIT_SYNC \
|
|
--one-time \
|
|
--repo="file://$REPO" \
|
|
--root="$ROOT" \
|
|
--link="link"
|
|
assert_link_exists "$ROOT/link"
|
|
assert_file_exists "$ROOT/link/file"
|
|
local depth
|
|
depth=$(git -C "$ROOT/link" rev-list HEAD | wc -l)
|
|
if [[ $depth != 1 ]]; then
|
|
fail "expected depth 1, got $depth"
|
|
fi
|
|
}
|
|
|
|
##############################################
|
|
# Test depth syncing across updates
|
|
##############################################
|
|
function e2e::sync_depth_across_updates() {
|
|
local expected_depth=1
|
|
local depth
|
|
|
|
# First sync
|
|
echo "${FUNCNAME[0]} 1" > "$REPO/file"
|
|
git -C "$REPO" commit -qam "${FUNCNAME[0]} 1"
|
|
|
|
GIT_SYNC \
|
|
--period=100ms \
|
|
--repo="file://$REPO" \
|
|
--depth="$expected_depth" \
|
|
--root="$ROOT" \
|
|
--link="link" \
|
|
&
|
|
wait_for_sync "${MAXWAIT}"
|
|
assert_link_exists "$ROOT/link"
|
|
assert_file_exists "$ROOT/link/file"
|
|
assert_file_eq "$ROOT/link/file" "${FUNCNAME[0]} 1"
|
|
assert_metric_eq "${METRIC_GOOD_SYNC_COUNT}" 1
|
|
assert_metric_eq "${METRIC_FETCH_COUNT}" 1
|
|
depth=$(git -C "$ROOT/link" rev-list HEAD | wc -l)
|
|
if [[ "$expected_depth" != "$depth" ]]; then
|
|
fail "initial: expected depth $expected_depth, got $depth"
|
|
fi
|
|
|
|
# Move forward
|
|
echo "${FUNCNAME[0]} 2" > "$REPO/file"
|
|
git -C "$REPO" commit -qam "${FUNCNAME[0]} 2"
|
|
wait_for_sync "${MAXWAIT}"
|
|
assert_link_exists "$ROOT/link"
|
|
assert_file_exists "$ROOT/link/file"
|
|
assert_file_eq "$ROOT/link/file" "${FUNCNAME[0]} 2"
|
|
assert_metric_eq "${METRIC_GOOD_SYNC_COUNT}" 2
|
|
assert_metric_eq "${METRIC_FETCH_COUNT}" 2
|
|
depth=$(git -C "$ROOT/link" rev-list HEAD | wc -l)
|
|
if [[ "$expected_depth" != "$depth" ]]; then
|
|
fail "forward: expected depth $expected_depth, got $depth"
|
|
fi
|
|
|
|
# Move backward
|
|
git -C "$REPO" reset -q --hard HEAD^
|
|
wait_for_sync "${MAXWAIT}"
|
|
assert_link_exists "$ROOT/link"
|
|
assert_file_exists "$ROOT/link/file"
|
|
assert_file_eq "$ROOT/link/file" "${FUNCNAME[0]} 1"
|
|
assert_metric_eq "${METRIC_GOOD_SYNC_COUNT}" 3
|
|
assert_metric_eq "${METRIC_FETCH_COUNT}" 3
|
|
depth=$(git -C "$ROOT/link" rev-list HEAD | wc -l)
|
|
if [[ "$expected_depth" != "$depth" ]]; then
|
|
fail "backward: expected depth $expected_depth, got $depth"
|
|
fi
|
|
}
|
|
|
|
##############################################
|
|
# Test depth switching on back-to-back runs
|
|
##############################################
|
|
function e2e::sync_depth_change_on_restart() {
|
|
echo "${FUNCNAME[0]} 1" > "$REPO/file"
|
|
git -C "$REPO" commit -qam "${FUNCNAME[0]} 1"
|
|
echo "${FUNCNAME[0]} 2" > "$REPO/file"
|
|
git -C "$REPO" commit -qam "${FUNCNAME[0]} 2"
|
|
echo "${FUNCNAME[0]} 3" > "$REPO/file"
|
|
git -C "$REPO" commit -qam "${FUNCNAME[0]} 3"
|
|
|
|
local depth
|
|
|
|
# Sync depth=1
|
|
GIT_SYNC \
|
|
--one-time \
|
|
--repo="file://$REPO" \
|
|
--depth=1 \
|
|
--root="$ROOT" \
|
|
--link="link"
|
|
wait_for_sync "${MAXWAIT}"
|
|
assert_link_exists "$ROOT/link"
|
|
assert_file_exists "$ROOT/link/file"
|
|
depth=$(git -C "$ROOT/link" rev-list HEAD | wc -l)
|
|
if [[ $depth != 1 ]]; then
|
|
fail "expected depth 1, got $depth"
|
|
fi
|
|
|
|
# Sync depth=2
|
|
GIT_SYNC \
|
|
--one-time \
|
|
--repo="file://$REPO" \
|
|
--depth=2 \
|
|
--root="$ROOT" \
|
|
--link="link"
|
|
wait_for_sync "${MAXWAIT}"
|
|
assert_link_exists "$ROOT/link"
|
|
assert_file_exists "$ROOT/link/file"
|
|
depth=$(git -C "$ROOT/link" rev-list HEAD | wc -l)
|
|
if [[ $depth != 2 ]]; then
|
|
fail "expected depth 2, got $depth"
|
|
fi
|
|
|
|
# Sync depth=1
|
|
GIT_SYNC \
|
|
--one-time \
|
|
--repo="file://$REPO" \
|
|
--depth=1 \
|
|
--root="$ROOT" \
|
|
--link="link"
|
|
wait_for_sync "${MAXWAIT}"
|
|
assert_link_exists "$ROOT/link"
|
|
assert_file_exists "$ROOT/link/file"
|
|
depth=$(git -C "$ROOT/link" rev-list HEAD | wc -l)
|
|
if [[ $depth != 1 ]]; then
|
|
fail "expected depth 1, got $depth"
|
|
fi
|
|
|
|
# Sync depth=0 (full)
|
|
GIT_SYNC \
|
|
--one-time \
|
|
--repo="file://$REPO" \
|
|
--depth=0 \
|
|
--root="$ROOT" \
|
|
--link="link"
|
|
wait_for_sync "${MAXWAIT}"
|
|
assert_link_exists "$ROOT/link"
|
|
assert_file_exists "$ROOT/link/file"
|
|
depth=$(git -C "$ROOT/link" rev-list HEAD | wc -l)
|
|
if [[ $depth != 4 ]]; then
|
|
fail "expected depth 4, got $depth"
|
|
fi
|
|
}
|
|
|
|
##############################################
|
|
# Test HTTP basicauth with a password
|
|
##############################################
|
|
function e2e::auth_http_password() {
|
|
# Run a git-over-HTTP server.
|
|
local ctr
|
|
ctr=$(docker_run \
|
|
-v "$REPO":/git/repo:ro \
|
|
e2e/test/httpd)
|
|
local ip
|
|
ip=$(docker_ip "$ctr")
|
|
|
|
# Try with wrong username
|
|
assert_fail \
|
|
GIT_SYNC \
|
|
--one-time \
|
|
--repo="http://$ip/repo" \
|
|
--root="$ROOT" \
|
|
--link="link" \
|
|
--username="wrong" \
|
|
--__env__GITSYNC_PASSWORD="testpass"
|
|
assert_file_absent "$ROOT/link/file"
|
|
|
|
# Try with wrong password
|
|
assert_fail \
|
|
GIT_SYNC \
|
|
--one-time \
|
|
--repo="http://$ip/repo" \
|
|
--root="$ROOT" \
|
|
--link="link" \
|
|
--username="testuser" \
|
|
--__env__GITSYNC_PASSWORD="wrong"
|
|
assert_file_absent "$ROOT/link/file"
|
|
|
|
# Try with the right password
|
|
GIT_SYNC \
|
|
--one-time \
|
|
--repo="http://$ip/repo" \
|
|
--root="$ROOT" \
|
|
--link="link" \
|
|
--username="testuser" \
|
|
--__env__GITSYNC_PASSWORD="testpass" \
|
|
|
|
assert_link_exists "$ROOT/link"
|
|
assert_file_exists "$ROOT/link/file"
|
|
assert_file_eq "$ROOT/link/file" "${FUNCNAME[0]}"
|
|
}
|
|
|
|
##############################################
|
|
# Test HTTP basicauth with a password in the URL
|
|
##############################################
|
|
function e2e::auth_http_password_in_url() {
|
|
# Run a git-over-HTTP server.
|
|
local ctr
|
|
ctr=$(docker_run \
|
|
-v "$REPO":/git/repo:ro \
|
|
e2e/test/httpd)
|
|
local ip
|
|
ip=$(docker_ip "$ctr")
|
|
|
|
# Try with wrong username
|
|
assert_fail \
|
|
GIT_SYNC \
|
|
--one-time \
|
|
--repo="http://wrong:testpass@$ip/repo" \
|
|
--root="$ROOT" \
|
|
--link="link"
|
|
assert_file_absent "$ROOT/link/file"
|
|
|
|
# Try with wrong password
|
|
assert_fail \
|
|
GIT_SYNC \
|
|
--one-time \
|
|
--repo="http://testuser:wrong@$ip/repo" \
|
|
--root="$ROOT" \
|
|
--link="link"
|
|
assert_file_absent "$ROOT/link/file"
|
|
|
|
# Try with the right password
|
|
GIT_SYNC \
|
|
--one-time \
|
|
--repo="http://testuser:testpass@$ip/repo" \
|
|
--root="$ROOT" \
|
|
--link="link"
|
|
|
|
assert_link_exists "$ROOT/link"
|
|
assert_file_exists "$ROOT/link/file"
|
|
assert_file_eq "$ROOT/link/file" "${FUNCNAME[0]}"
|
|
}
|
|
|
|
##############################################
|
|
# Test HTTP basicauth with a password-file
|
|
##############################################
|
|
function e2e::auth_http_password_file() {
|
|
# Run a git-over-HTTP server.
|
|
local ctr
|
|
ctr=$(docker_run \
|
|
-v "$REPO":/git/repo:ro \
|
|
e2e/test/httpd)
|
|
local ip
|
|
ip=$(docker_ip "$ctr")
|
|
|
|
# Make a password file with a bad password.
|
|
echo -n "wrong" > "$WORK/password-file"
|
|
|
|
assert_fail \
|
|
GIT_SYNC \
|
|
--one-time \
|
|
--repo="http://$ip/repo" \
|
|
--root="$ROOT" \
|
|
--link="link" \
|
|
--username="testuser" \
|
|
--password-file="$WORK/password-file"
|
|
assert_file_absent "$ROOT/link/file"
|
|
|
|
# Make a password file the right password.
|
|
echo -n "testpass" > "$WORK/password-file"
|
|
|
|
GIT_SYNC \
|
|
--one-time \
|
|
--repo="http://$ip/repo" \
|
|
--root="$ROOT" \
|
|
--link="link" \
|
|
--username="testuser" \
|
|
--password-file="$WORK/password-file"
|
|
|
|
assert_link_exists "$ROOT/link"
|
|
assert_file_exists "$ROOT/link/file"
|
|
assert_file_eq "$ROOT/link/file" "${FUNCNAME[0]}"
|
|
}
|
|
|
|
##############################################
|
|
# Test SSH (user@host:path syntax)
|
|
##############################################
|
|
function e2e::auth_ssh() {
|
|
# Run a git-over-SSH server. Use key #3 to exercise the multi-key logic.
|
|
local ctr
|
|
ctr=$(docker_run \
|
|
-v "$DOT_SSH/server/3":/dot_ssh:ro \
|
|
-v "$REPO":/git/repo:ro \
|
|
e2e/test/sshd)
|
|
local ip
|
|
ip=$(docker_ip "$ctr")
|
|
|
|
# Try to sync with key #1.
|
|
assert_fail \
|
|
GIT_SYNC \
|
|
--one-time \
|
|
--repo="test@$ip:/git/repo" \
|
|
--root="$ROOT" \
|
|
--link="link" \
|
|
--ssh-known-hosts=false \
|
|
--ssh-key-file="/ssh/secret.2"
|
|
assert_file_absent "$ROOT/link/file"
|
|
|
|
# Try to sync with multiple keys
|
|
GIT_SYNC \
|
|
--one-time \
|
|
--repo="test@$ip:/git/repo" \
|
|
--root="$ROOT" \
|
|
--link="link" \
|
|
--ssh-known-hosts=false \
|
|
--ssh-key-file="/ssh/secret.1" \
|
|
--ssh-key-file="/ssh/secret.2" \
|
|
--ssh-key-file="/ssh/secret.3"
|
|
|
|
assert_link_exists "$ROOT/link"
|
|
assert_file_exists "$ROOT/link/file"
|
|
assert_file_eq "$ROOT/link/file" "${FUNCNAME[0]}"
|
|
}
|
|
|
|
##############################################
|
|
# Test SSH (ssh://user@host/path syntax)
|
|
##############################################
|
|
function e2e::auth_ssh_url() {
|
|
# Run a git-over-SSH server. Use key #3 to exercise the multi-key logic.
|
|
local ctr
|
|
ctr=$(docker_run \
|
|
-v "$DOT_SSH/server/3":/dot_ssh:ro \
|
|
-v "$REPO":/git/repo:ro \
|
|
e2e/test/sshd)
|
|
local ip
|
|
ip=$(docker_ip "$ctr")
|
|
|
|
# Try to sync with key #1.
|
|
assert_fail \
|
|
GIT_SYNC \
|
|
--one-time \
|
|
--repo="ssh://test@$ip/git/repo" \
|
|
--root="$ROOT" \
|
|
--link="link" \
|
|
--ssh-known-hosts=false \
|
|
--ssh-key-file="/ssh/secret.2"
|
|
assert_file_absent "$ROOT/link/file"
|
|
|
|
# Try to sync with multiple keys
|
|
GIT_SYNC \
|
|
--one-time \
|
|
--repo="ssh://test@$ip/git/repo" \
|
|
--root="$ROOT" \
|
|
--link="link" \
|
|
--ssh-known-hosts=false \
|
|
--ssh-key-file="/ssh/secret.1" \
|
|
--ssh-key-file="/ssh/secret.2" \
|
|
--ssh-key-file="/ssh/secret.3"
|
|
|
|
assert_link_exists "$ROOT/link"
|
|
assert_file_exists "$ROOT/link/file"
|
|
assert_file_eq "$ROOT/link/file" "${FUNCNAME[0]}"
|
|
}
|
|
|
|
##############################################
|
|
# Test askpass-url with bad password
|
|
##############################################
|
|
function e2e::auth_askpass_url_wrong_password() {
|
|
# run the askpass_url service with wrong password
|
|
local hitlog="$WORK/hitlog"
|
|
cat /dev/null > "$hitlog"
|
|
local ctr
|
|
ctr=$(docker_run \
|
|
-v "$hitlog":/var/log/hits \
|
|
e2e/test/ncsvr \
|
|
80 'read X
|
|
echo "HTTP/1.1 200 OK"
|
|
echo
|
|
echo "username=my-username"
|
|
echo "password=wrong"
|
|
')
|
|
local ip
|
|
ip=$(docker_ip "$ctr")
|
|
|
|
assert_fail \
|
|
GIT_SYNC \
|
|
--one-time \
|
|
--repo="file://$REPO" \
|
|
--root="$ROOT" \
|
|
--link="link" \
|
|
--git="/$ASKPASS_GIT" \
|
|
--askpass-url="http://$ip/git_askpass"
|
|
assert_file_absent "$ROOT/link/file"
|
|
}
|
|
|
|
##############################################
|
|
# Test askpass-url
|
|
##############################################
|
|
function e2e::auth_askpass_url_correct_password() {
|
|
# run with askpass_url service with correct password
|
|
local hitlog="$WORK/hitlog"
|
|
cat /dev/null > "$hitlog"
|
|
local ctr
|
|
ctr=$(docker_run \
|
|
-v "$hitlog":/var/log/hits \
|
|
e2e/test/ncsvr \
|
|
80 'read X
|
|
echo "HTTP/1.1 200 OK"
|
|
echo
|
|
echo "username=my-username"
|
|
echo "password=my-password"
|
|
')
|
|
local ip
|
|
ip=$(docker_ip "$ctr")
|
|
|
|
GIT_SYNC \
|
|
--one-time \
|
|
--repo="file://$REPO" \
|
|
--root="$ROOT" \
|
|
--link="link" \
|
|
--git="/$ASKPASS_GIT" \
|
|
--askpass-url="http://$ip/git_askpass"
|
|
|
|
assert_link_exists "$ROOT/link"
|
|
assert_file_exists "$ROOT/link/file"
|
|
assert_file_eq "$ROOT/link/file" "${FUNCNAME[0]}"
|
|
}
|
|
|
|
##############################################
|
|
# Test askpass-url where the URL is sometimes wrong
|
|
##############################################
|
|
function e2e::auth_askpass_url_sometimes_wrong() {
|
|
# run with askpass_url service which alternates good/bad replies.
|
|
local hitlog="$WORK/hitlog"
|
|
cat /dev/null > "$hitlog"
|
|
local ctr
|
|
ctr=$(docker_run \
|
|
-v "$hitlog":/var/log/hits \
|
|
e2e/test/ncsvr \
|
|
80 'read X
|
|
echo "HTTP/1.1 200 OK"
|
|
echo
|
|
if [ -f /tmp/flag ]; then
|
|
echo "username=my-username"
|
|
echo "password=my-password"
|
|
rm /tmp/flag
|
|
else
|
|
echo "username=my-username"
|
|
echo "password=wrong"
|
|
touch /tmp/flag
|
|
fi
|
|
')
|
|
local ip
|
|
ip=$(docker_ip "$ctr")
|
|
|
|
# First sync
|
|
echo "${FUNCNAME[0]} 1" > "$REPO/file"
|
|
git -C "$REPO" commit -qam "${FUNCNAME[0]} 1"
|
|
|
|
GIT_SYNC \
|
|
--period=100ms \
|
|
--repo="file://$REPO" \
|
|
--root="$ROOT" \
|
|
--link="link" \
|
|
--git="/$ASKPASS_GIT" \
|
|
--askpass-url="http://$ip/git_askpass" \
|
|
--max-failures=2 \
|
|
&
|
|
wait_for_sync "${MAXWAIT}"
|
|
assert_link_exists "$ROOT/link"
|
|
assert_file_exists "$ROOT/link/file"
|
|
assert_file_eq "$ROOT/link/file" "${FUNCNAME[0]} 1"
|
|
|
|
# Move HEAD forward
|
|
echo "${FUNCNAME[0]} 2" > "$REPO/file"
|
|
git -C "$REPO" commit -qam "${FUNCNAME[0]} 2"
|
|
wait_for_sync "${MAXWAIT}"
|
|
assert_link_exists "$ROOT/link"
|
|
assert_file_exists "$ROOT/link/file"
|
|
assert_file_eq "$ROOT/link/file" "${FUNCNAME[0]} 2"
|
|
|
|
# Move HEAD backward
|
|
git -C "$REPO" reset -q --hard HEAD^
|
|
wait_for_sync "${MAXWAIT}"
|
|
assert_link_exists "$ROOT/link"
|
|
assert_file_exists "$ROOT/link/file"
|
|
assert_file_eq "$ROOT/link/file" "${FUNCNAME[0]} 1"
|
|
}
|
|
|
|
##############################################
|
|
# Test askpass-url where the URL is flaky
|
|
##############################################
|
|
function e2e::auth_askpass_url_flaky() {
|
|
# run with askpass_url service which alternates good/bad replies.
|
|
local hitlog="$WORK/hitlog"
|
|
cat /dev/null > "$hitlog"
|
|
local ctr
|
|
ctr=$(docker_run \
|
|
-v "$hitlog":/var/log/hits \
|
|
e2e/test/ncsvr \
|
|
80 'read X
|
|
if [ -f /tmp/flag ]; then
|
|
echo "HTTP/1.1 200 OK"
|
|
echo
|
|
echo "username=my-username"
|
|
echo "password=my-password"
|
|
rm /tmp/flag
|
|
else
|
|
echo "HTTP/1.1 503 Service Unavailable"
|
|
echo
|
|
touch /tmp/flag
|
|
fi
|
|
')
|
|
local ip
|
|
ip=$(docker_ip "$ctr")
|
|
|
|
# First sync
|
|
echo "${FUNCNAME[0]} 1" > "$REPO/file"
|
|
git -C "$REPO" commit -qam "${FUNCNAME[0]} 1"
|
|
|
|
GIT_SYNC \
|
|
--period=100ms \
|
|
--repo="file://$REPO" \
|
|
--root="$ROOT" \
|
|
--link="link" \
|
|
--git="/$ASKPASS_GIT" \
|
|
--askpass-url="http://$ip/git_askpass" \
|
|
--max-failures=2 \
|
|
&
|
|
wait_for_sync "${MAXWAIT}"
|
|
assert_link_exists "$ROOT/link"
|
|
assert_file_exists "$ROOT/link/file"
|
|
assert_file_eq "$ROOT/link/file" "${FUNCNAME[0]} 1"
|
|
|
|
# Move HEAD forward
|
|
echo "${FUNCNAME[0]} 2" > "$REPO/file"
|
|
git -C "$REPO" commit -qam "${FUNCNAME[0]} 2"
|
|
wait_for_sync "${MAXWAIT}"
|
|
assert_link_exists "$ROOT/link"
|
|
assert_file_exists "$ROOT/link/file"
|
|
assert_file_eq "$ROOT/link/file" "${FUNCNAME[0]} 2"
|
|
|
|
# Move HEAD backward
|
|
git -C "$REPO" reset -q --hard HEAD^
|
|
wait_for_sync "${MAXWAIT}"
|
|
assert_link_exists "$ROOT/link"
|
|
assert_file_exists "$ROOT/link/file"
|
|
assert_file_eq "$ROOT/link/file" "${FUNCNAME[0]} 1"
|
|
}
|
|
|
|
##############################################
|
|
# Test askpass-url where the URL fails at startup
|
|
##############################################
|
|
function e2e::auth_askpass_url_slow_start() {
|
|
# run with askpass_url service which takes a while to come up
|
|
local hitlog="$WORK/hitlog"
|
|
cat /dev/null > "$hitlog"
|
|
local ctr
|
|
ctr=$(docker_run \
|
|
-v "$hitlog":/var/log/hits \
|
|
--entrypoint sh \
|
|
e2e/test/ncsvr \
|
|
-c "sleep 4;
|
|
/ncsvr.sh 80 'read X
|
|
echo \"HTTP/1.1 200 OK\"
|
|
echo
|
|
echo \"username=my-username\"
|
|
echo \"password=my-password\"
|
|
'")
|
|
local ip
|
|
ip=$(docker_ip "$ctr")
|
|
|
|
GIT_SYNC \
|
|
--period=1s \
|
|
--repo="file://$REPO" \
|
|
--root="$ROOT" \
|
|
--link="link" \
|
|
--git="/$ASKPASS_GIT" \
|
|
--askpass-url="http://$ip/git_askpass" \
|
|
--max-failures=5 \
|
|
&
|
|
sleep 1
|
|
assert_file_absent "$ROOT/link"
|
|
|
|
wait_for_sync 5
|
|
assert_link_exists "$ROOT/link"
|
|
assert_file_exists "$ROOT/link/file"
|
|
assert_file_eq "$ROOT/link/file" "${FUNCNAME[0]}"
|
|
}
|
|
|
|
##############################################
|
|
# Test github app auth
|
|
##############################################
|
|
function e2e::auth_github_app_application_id() {
|
|
if [[ "${skip_github_app_test}" == "true" ]]; then
|
|
skip
|
|
fi
|
|
GIT_SYNC \
|
|
--one-time \
|
|
--repo="${TEST_GITHUB_APP_AUTH_TEST_REPO}" \
|
|
--github-app-application-id "${TEST_GITHUB_APP_APPLICATION_ID}" \
|
|
--github-app-installation-id "${TEST_GITHUB_APP_INSTALLATION_ID}" \
|
|
--github-app-private-key-file "/${LOCAL_GITHUB_APP_PRIVATE_KEY_FILE}" \
|
|
--root="${ROOT}" \
|
|
--link="link"
|
|
assert_file_exists "${ROOT}/link/LICENSE"
|
|
}
|
|
|
|
function e2e::auth_github_app_client_id() {
|
|
if [[ "${skip_github_app_test}" == "true" ]]; then
|
|
skip
|
|
fi
|
|
GIT_SYNC \
|
|
--one-time \
|
|
--repo="${TEST_GITHUB_APP_AUTH_TEST_REPO}" \
|
|
--github-app-client-id "${TEST_GITHUB_APP_CLIENT_ID}" \
|
|
--github-app-installation-id "${TEST_GITHUB_APP_INSTALLATION_ID}" \
|
|
--github-app-private-key-file "/${LOCAL_GITHUB_APP_PRIVATE_KEY_FILE}" \
|
|
--root="${ROOT}" \
|
|
--link="link"
|
|
assert_file_exists "${ROOT}/link/LICENSE"
|
|
}
|
|
|
|
##############################################
|
|
# Test exechook-success
|
|
##############################################
|
|
function e2e::exechook_success() {
|
|
cat /dev/null > "$RUNLOG"
|
|
|
|
# First sync
|
|
echo "${FUNCNAME[0]} 1" > "$REPO/file"
|
|
git -C "$REPO" commit -qam "${FUNCNAME[0]} 1"
|
|
|
|
GIT_SYNC \
|
|
--period=100ms \
|
|
--repo="file://$REPO" \
|
|
--root="$ROOT" \
|
|
--link="link" \
|
|
--exechook-command="/$EXECHOOK_COMMAND" \
|
|
&
|
|
wait_for_sync "${MAXWAIT}"
|
|
assert_link_exists "$ROOT/link"
|
|
assert_file_exists "$ROOT/link/file"
|
|
assert_file_exists "$ROOT/link/exechook"
|
|
assert_file_eq "$ROOT/link/file" "${FUNCNAME[0]} 1"
|
|
assert_file_eq "$ROOT/link/exechook" "${FUNCNAME[0]} 1"
|
|
assert_file_eq "$ROOT/link/exechook-env" "$EXECHOOK_ENVKEY=$EXECHOOK_ENVVAL"
|
|
assert_file_lines_eq "$RUNLOG" 1
|
|
|
|
# Move forward
|
|
echo "${FUNCNAME[0]} 2" > "$REPO/file"
|
|
git -C "$REPO" commit -qam "${FUNCNAME[0]} 2"
|
|
wait_for_sync "${MAXWAIT}"
|
|
assert_link_exists "$ROOT/link"
|
|
assert_file_exists "$ROOT/link/file"
|
|
assert_file_exists "$ROOT/link/exechook"
|
|
assert_file_eq "$ROOT/link/file" "${FUNCNAME[0]} 2"
|
|
assert_file_eq "$ROOT/link/exechook" "${FUNCNAME[0]} 2"
|
|
assert_file_eq "$ROOT/link/exechook-env" "$EXECHOOK_ENVKEY=$EXECHOOK_ENVVAL"
|
|
assert_file_lines_eq "$RUNLOG" 2
|
|
}
|
|
|
|
##############################################
|
|
# Test exechook-fail-retry
|
|
##############################################
|
|
function e2e::exechook_fail_retry() {
|
|
cat /dev/null > "$RUNLOG"
|
|
|
|
# First sync - return a failure to ensure that we try again
|
|
GIT_SYNC \
|
|
--period=100ms \
|
|
--repo="file://$REPO" \
|
|
--root="$ROOT" \
|
|
--link="link" \
|
|
--exechook-command="/$EXECHOOK_COMMAND_FAIL" \
|
|
--exechook-backoff=1s \
|
|
&
|
|
sleep 3 # give it time to retry
|
|
|
|
# Check that exechook was called
|
|
assert_file_lines_ge "$RUNLOG" 2
|
|
}
|
|
|
|
##############################################
|
|
# Test exechook-success with --one-time
|
|
##############################################
|
|
function e2e::exechook_success_once() {
|
|
GIT_SYNC \
|
|
--one-time \
|
|
--repo="file://$REPO" \
|
|
--root="$ROOT" \
|
|
--link="link" \
|
|
--exechook-command="/$EXECHOOK_COMMAND_SLEEPY"
|
|
|
|
wait_for_sync "${MAXWAIT}"
|
|
assert_link_exists "$ROOT/link"
|
|
assert_file_exists "$ROOT/link/file"
|
|
assert_file_exists "$ROOT/link/exechook"
|
|
assert_file_eq "$ROOT/link/file" "${FUNCNAME[0]}"
|
|
assert_file_eq "$ROOT/link/exechook" "${FUNCNAME[0]}"
|
|
assert_file_eq "$ROOT/link/exechook-env" "$EXECHOOK_ENVKEY=$EXECHOOK_ENVVAL"
|
|
}
|
|
|
|
##############################################
|
|
# Test exechook-fail with --one-time
|
|
##############################################
|
|
function e2e::exechook_fail_once() {
|
|
cat /dev/null > "$RUNLOG"
|
|
|
|
assert_fail \
|
|
GIT_SYNC \
|
|
--one-time \
|
|
--repo="file://$REPO" \
|
|
--root="$ROOT" \
|
|
--link="link" \
|
|
--exechook-command="/$EXECHOOK_COMMAND_FAIL_SLEEPY" \
|
|
--exechook-backoff=1s
|
|
|
|
assert_link_exists "$ROOT/link"
|
|
assert_file_exists "$ROOT/link/file"
|
|
assert_file_eq "$ROOT/link/file" "${FUNCNAME[0]}"
|
|
assert_file_lines_eq "$RUNLOG" 1
|
|
}
|
|
|
|
##############################################
|
|
# Test exechook at startup with correct SHA
|
|
##############################################
|
|
function e2e::exechook_startup_after_crash() {
|
|
GIT_SYNC \
|
|
--one-time \
|
|
--repo="file://$REPO" \
|
|
--ref="$MAIN_BRANCH" \
|
|
--root="$ROOT" \
|
|
--link="link"
|
|
assert_link_exists "$ROOT/link"
|
|
assert_file_exists "$ROOT/link/file"
|
|
assert_file_eq "$ROOT/link/file" "${FUNCNAME[0]}"
|
|
|
|
# No changes to repo
|
|
|
|
cat /dev/null > "$RUNLOG"
|
|
GIT_SYNC \
|
|
--one-time \
|
|
--repo="file://$REPO" \
|
|
--ref="$MAIN_BRANCH" \
|
|
--root="$ROOT" \
|
|
--link="link" \
|
|
--exechook-command="/$EXECHOOK_COMMAND"
|
|
assert_link_exists "$ROOT/link"
|
|
assert_file_exists "$ROOT/link/file"
|
|
assert_file_exists "$ROOT/link/exechook"
|
|
assert_file_eq "$ROOT/link/file" "${FUNCNAME[0]}"
|
|
assert_file_eq "$ROOT/link/exechook" "${FUNCNAME[0]}"
|
|
assert_file_eq "$ROOT/link/exechook-env" "$EXECHOOK_ENVKEY=$EXECHOOK_ENVVAL"
|
|
assert_file_lines_eq "$RUNLOG" 1
|
|
}
|
|
|
|
##############################################
|
|
# Test webhook success
|
|
##############################################
|
|
function e2e::webhook_success() {
|
|
local hitlog="$WORK/hitlog"
|
|
|
|
# First sync
|
|
cat /dev/null > "$hitlog"
|
|
local ctr
|
|
ctr=$(docker_run \
|
|
-v "$hitlog":/var/log/hits \
|
|
e2e/test/ncsvr \
|
|
80 'read X
|
|
echo "HTTP/1.1 200 OK"
|
|
echo
|
|
')
|
|
local ip
|
|
ip=$(docker_ip "$ctr")
|
|
echo "${FUNCNAME[0]} 1" > "$REPO/file"
|
|
git -C "$REPO" commit -qam "${FUNCNAME[0]} 1"
|
|
|
|
GIT_SYNC \
|
|
--period=100ms \
|
|
--repo="file://$REPO" \
|
|
--root="$ROOT" \
|
|
--webhook-url="http://$ip" \
|
|
--webhook-success-status=200 \
|
|
--link="link" \
|
|
&
|
|
|
|
# check that basic call works
|
|
wait_for_sync "${MAXWAIT}"
|
|
sleep 1 # webhooks are async
|
|
assert_file_lines_eq "$hitlog" 1
|
|
|
|
# Move forward
|
|
echo "${FUNCNAME[0]} 2" > "$REPO/file"
|
|
git -C "$REPO" commit -qam "${FUNCNAME[0]} 2"
|
|
|
|
# check that another call works
|
|
wait_for_sync "${MAXWAIT}"
|
|
sleep 1 # webhooks are async
|
|
assert_file_lines_eq "$hitlog" 2
|
|
}
|
|
|
|
##############################################
|
|
# Test webhook fail-retry
|
|
##############################################
|
|
function e2e::webhook_fail_retry() {
|
|
local hitlog="$WORK/hitlog"
|
|
local script="$WORK/http_resp.sh"
|
|
touch "$script"
|
|
chmod 755 "$script"
|
|
|
|
# First sync - return a failure to ensure that we try again
|
|
cat /dev/null > "$hitlog"
|
|
cat > "$script" << __EOF__
|
|
#!/bin/sh
|
|
read X
|
|
echo "HTTP/1.1 500 Internal Server Error"
|
|
echo
|
|
__EOF__
|
|
local ctr
|
|
ctr=$(docker_run \
|
|
-v "$hitlog":/var/log/hits \
|
|
-v "$script":/http_resp.sh \
|
|
e2e/test/ncsvr \
|
|
80 '/http_resp.sh')
|
|
local ip
|
|
ip=$(docker_ip "$ctr")
|
|
|
|
GIT_SYNC \
|
|
--period=100ms \
|
|
--repo="file://$REPO" \
|
|
--root="$ROOT" \
|
|
--webhook-url="http://$ip" \
|
|
--webhook-success-status=200 \
|
|
--link="link" \
|
|
&
|
|
|
|
# Check that webhook was called
|
|
wait_for_sync "${MAXWAIT}"
|
|
sleep 1 # webhooks are async
|
|
assert_file_lines_ge "$hitlog" 1
|
|
|
|
# Now return 200, ensure that it gets called
|
|
cat /dev/null > "$hitlog"
|
|
cat > "$script" << __EOF__
|
|
#!/bin/sh
|
|
read X
|
|
echo "HTTP/1.1 200 OK"
|
|
echo
|
|
__EOF__
|
|
sleep 2 # webhooks are async
|
|
assert_file_lines_eq "$hitlog" 1
|
|
}
|
|
|
|
##############################################
|
|
# Test webhook success with --one-time
|
|
##############################################
|
|
function e2e::webhook_success_once() {
|
|
local hitlog="$WORK/hitlog"
|
|
|
|
# First sync
|
|
cat /dev/null > "$hitlog"
|
|
local ctr
|
|
ctr=$(docker_run \
|
|
-v "$hitlog":/var/log/hits \
|
|
e2e/test/ncsvr \
|
|
80 'read X
|
|
echo "HTTP/1.1 200 OK"
|
|
echo
|
|
')
|
|
local ip
|
|
ip=$(docker_ip "$ctr")
|
|
|
|
GIT_SYNC \
|
|
--period=100ms \
|
|
--one-time \
|
|
--repo="file://$REPO" \
|
|
--root="$ROOT" \
|
|
--webhook-url="http://$ip" \
|
|
--webhook-success-status=200 \
|
|
--link="link"
|
|
|
|
# check that basic call works
|
|
assert_file_lines_eq "$hitlog" 1
|
|
}
|
|
|
|
##############################################
|
|
# Test webhook fail with --one-time
|
|
##############################################
|
|
function e2e::webhook_fail_retry_once() {
|
|
local hitlog="$WORK/hitlog"
|
|
|
|
# First sync - return a failure to ensure that we try again
|
|
cat /dev/null > "$hitlog"
|
|
local ctr
|
|
ctr=$(docker_run \
|
|
-v "$hitlog":/var/log/hits \
|
|
e2e/test/ncsvr \
|
|
80 'read X
|
|
echo "HTTP/1.1 500 Internal Server Error"
|
|
echo
|
|
')
|
|
local ip
|
|
ip=$(docker_ip "$ctr")
|
|
|
|
assert_fail \
|
|
GIT_SYNC \
|
|
--period=100ms \
|
|
--one-time \
|
|
--repo="file://$REPO" \
|
|
--root="$ROOT" \
|
|
--webhook-url="http://$ip" \
|
|
--webhook-success-status=200 \
|
|
--link="link"
|
|
|
|
assert_link_exists "$ROOT/link"
|
|
assert_file_exists "$ROOT/link/file"
|
|
assert_file_eq "$ROOT/link/file" "${FUNCNAME[0]}"
|
|
assert_file_lines_eq "$hitlog" 1
|
|
}
|
|
|
|
##############################################
|
|
# Test webhook fire-and-forget
|
|
##############################################
|
|
function e2e::webhook_fire_and_forget() {
|
|
local hitlog="$WORK/hitlog"
|
|
|
|
cat /dev/null > "$hitlog"
|
|
local ctr
|
|
ctr=$(docker_run \
|
|
-v "$hitlog":/var/log/hits \
|
|
e2e/test/ncsvr \
|
|
80 'read X
|
|
echo "HTTP/1.1 404 Not Found"
|
|
echo
|
|
')
|
|
local ip
|
|
ip=$(docker_ip "$ctr")
|
|
|
|
GIT_SYNC \
|
|
--period=100ms \
|
|
--repo="file://$REPO" \
|
|
--root="$ROOT" \
|
|
--webhook-url="http://$ip" \
|
|
--webhook-success-status=0 \
|
|
--link="link" \
|
|
&
|
|
|
|
# check that basic call works
|
|
wait_for_sync "${MAXWAIT}"
|
|
sleep 1 # webhooks are async
|
|
assert_file_lines_eq "$hitlog" 1
|
|
}
|
|
|
|
##############################################
|
|
# Test http handler
|
|
##############################################
|
|
function e2e::expose_http() {
|
|
GIT_SYNC \
|
|
--git="/$SLOW_GIT_FETCH" \
|
|
--period=100ms \
|
|
--repo="file://$REPO" \
|
|
--root="$ROOT" \
|
|
--link="link" \
|
|
&
|
|
|
|
# do nothing, just wait for the HTTP to come up
|
|
for i in $(seq 1 5); do
|
|
sleep 1
|
|
if curl --silent --output /dev/null http://localhost:$HTTP_PORT; then
|
|
break
|
|
fi
|
|
if [[ "$i" == 5 ]]; then
|
|
fail "HTTP server failed to start"
|
|
fi
|
|
done
|
|
|
|
# check that health endpoint fails
|
|
if [[ $(curl --write-out '%{http_code}' --silent --output /dev/null http://localhost:$HTTP_PORT) -ne 503 ]] ; then
|
|
fail "health endpoint should have failed: $(curl --write-out '%{http_code}' --silent --output /dev/null http://localhost:$HTTP_PORT)"
|
|
fi
|
|
wait_for_sync "${MAXWAIT}"
|
|
|
|
# check that health endpoint is alive
|
|
if [[ $(curl --write-out '%{http_code}' --silent --output /dev/null http://localhost:$HTTP_PORT) -ne 200 ]] ; then
|
|
fail "health endpoint failed"
|
|
fi
|
|
|
|
# check that the metrics endpoint exists
|
|
if [[ $(curl --write-out '%{http_code}' --silent --output /dev/null http://localhost:$HTTP_PORT/metrics) -ne 200 ]] ; then
|
|
fail "metrics endpoint failed"
|
|
fi
|
|
|
|
# check that the pprof endpoint exists
|
|
if [[ $(curl --write-out '%{http_code}' --silent --output /dev/null http://localhost:$HTTP_PORT/debug/pprof/) -ne 200 ]] ; then
|
|
fail "pprof endpoint failed"
|
|
fi
|
|
}
|
|
|
|
##############################################
|
|
# Test http handler after restart
|
|
##############################################
|
|
function e2e::expose_http_after_restart() {
|
|
# Sync once to set up the repo
|
|
GIT_SYNC \
|
|
--one-time \
|
|
--repo="file://$REPO" \
|
|
--root="$ROOT" \
|
|
--link="link"
|
|
assert_link_exists "$ROOT/link"
|
|
assert_file_exists "$ROOT/link/file"
|
|
assert_file_eq "$ROOT/link/file" "${FUNCNAME[0]}"
|
|
|
|
# Sync again and prove readiness.
|
|
GIT_SYNC \
|
|
--repo="file://$REPO" \
|
|
--root="$ROOT" \
|
|
--link="link" \
|
|
&
|
|
# do nothing, just wait for the HTTP to come up
|
|
for i in $(seq 1 5); do
|
|
sleep 1
|
|
if curl --silent --output /dev/null http://localhost:$HTTP_PORT; then
|
|
break
|
|
fi
|
|
if [[ "$i" == 5 ]]; then
|
|
fail "HTTP server failed to start"
|
|
fi
|
|
done
|
|
|
|
sleep 2 # wait for first loop to confirm synced
|
|
|
|
# check that health endpoint is alive
|
|
if [[ $(curl --write-out '%{http_code}' --silent --output /dev/null http://localhost:$HTTP_PORT) -ne 200 ]] ; then
|
|
fail "health endpoint failed"
|
|
fi
|
|
assert_link_exists "$ROOT/link"
|
|
assert_file_exists "$ROOT/link/file"
|
|
assert_file_eq "$ROOT/link/file" "${FUNCNAME[0]}"
|
|
}
|
|
|
|
##############################################
|
|
# Test submodule sync
|
|
##############################################
|
|
function e2e::submodule_sync_default() {
|
|
# Init submodule repo
|
|
local submodule_repo_name="sub"
|
|
local submodule="$WORK/$submodule_repo_name"
|
|
mkdir "$submodule"
|
|
|
|
git -C "$submodule" init -q -b "$MAIN_BRANCH"
|
|
echo "submodule" > "$submodule/submodule.file"
|
|
git -C "$submodule" add submodule.file
|
|
git -C "$submodule" commit -aqm "init submodule.file"
|
|
|
|
# Init nested submodule repo
|
|
local nested_submodule_repo_name="nested-sub"
|
|
local nested_submodule="$WORK/$nested_submodule_repo_name"
|
|
mkdir "$nested_submodule"
|
|
|
|
git -C "$nested_submodule" init -q -b "$MAIN_BRANCH"
|
|
echo "nested-submodule" > "$nested_submodule/nested-submodule.file"
|
|
git -C "$nested_submodule" add nested-submodule.file
|
|
git -C "$nested_submodule" commit -aqm "init nested-submodule.file"
|
|
|
|
# Add submodule
|
|
git -C "$REPO" -c protocol.file.allow=always submodule add -q file://$submodule "$submodule_repo_name"
|
|
git -C "$REPO" commit -aqm "add submodule"
|
|
|
|
GIT_SYNC \
|
|
--period=100ms \
|
|
--repo="file://$REPO" \
|
|
--root="$ROOT" \
|
|
--link="link" \
|
|
&
|
|
wait_for_sync "${MAXWAIT}"
|
|
assert_link_exists "$ROOT/link"
|
|
assert_file_exists "$ROOT/link/file"
|
|
assert_file_exists "$ROOT/link/$submodule_repo_name/submodule.file"
|
|
assert_file_eq "$ROOT/link/$submodule_repo_name/submodule.file" "submodule"
|
|
assert_metric_eq "${METRIC_GOOD_SYNC_COUNT}" 1
|
|
|
|
# Make change in submodule repo
|
|
echo "${FUNCNAME[0]} 2" > "$submodule/submodule.file"
|
|
git -C "$submodule" commit -qam "${FUNCNAME[0]} 2"
|
|
git -C "$REPO" -c protocol.file.allow=always submodule update --recursive --remote > /dev/null 2>&1
|
|
git -C "$REPO" commit -qam "${FUNCNAME[0]} 2"
|
|
wait_for_sync "${MAXWAIT}"
|
|
assert_link_exists "$ROOT/link"
|
|
assert_file_exists "$ROOT/link/file"
|
|
assert_file_exists "$ROOT/link/$submodule_repo_name/submodule.file"
|
|
assert_file_eq "$ROOT/link/$submodule_repo_name/submodule.file" "${FUNCNAME[0]} 2"
|
|
assert_metric_eq "${METRIC_GOOD_SYNC_COUNT}" 2
|
|
|
|
# Move backward in submodule repo
|
|
git -C "$submodule" reset -q --hard HEAD^
|
|
git -C "$REPO" -c protocol.file.allow=always submodule update --recursive --remote > /dev/null 2>&1
|
|
git -C "$REPO" commit -qam "${FUNCNAME[0]} 3"
|
|
wait_for_sync "${MAXWAIT}"
|
|
assert_link_exists "$ROOT/link"
|
|
assert_file_exists "$ROOT/link/file"
|
|
assert_file_exists "$ROOT/link/$submodule_repo_name/submodule.file"
|
|
assert_file_eq "$ROOT/link/$submodule_repo_name/submodule.file" "submodule"
|
|
assert_metric_eq "${METRIC_GOOD_SYNC_COUNT}" 3
|
|
|
|
# Add nested submodule to submodule repo
|
|
git -C "$submodule" -c protocol.file.allow=always submodule add -q file://$nested_submodule "$nested_submodule_repo_name"
|
|
git -C "$submodule" commit -aqm "add nested submodule"
|
|
git -C "$REPO" -c protocol.file.allow=always submodule update --recursive --remote > /dev/null 2>&1
|
|
git -C "$REPO" commit -qam "${FUNCNAME[0]} 4"
|
|
wait_for_sync "${MAXWAIT}"
|
|
assert_link_exists "$ROOT/link"
|
|
assert_file_exists "$ROOT/link/file"
|
|
assert_file_exists "$ROOT/link/$submodule_repo_name/submodule.file"
|
|
assert_file_exists "$ROOT/link/$submodule_repo_name/$nested_submodule_repo_name/nested-submodule.file"
|
|
assert_file_eq "$ROOT/link/$submodule_repo_name/$nested_submodule_repo_name/nested-submodule.file" "nested-submodule"
|
|
assert_metric_eq "${METRIC_GOOD_SYNC_COUNT}" 4
|
|
|
|
# Remove nested submodule
|
|
git -C "$submodule" submodule deinit -q $nested_submodule_repo_name
|
|
rm -rf "$submodule/.git/modules/$nested_submodule_repo_name"
|
|
git -C "$submodule" rm -qf $nested_submodule_repo_name
|
|
git -C "$submodule" commit -aqm "delete nested submodule"
|
|
git -C "$REPO" -c protocol.file.allow=always submodule update --recursive --remote > /dev/null 2>&1
|
|
git -C "$REPO" commit -qam "${FUNCNAME[0]} 5"
|
|
wait_for_sync "${MAXWAIT}"
|
|
assert_link_exists "$ROOT/link"
|
|
assert_file_exists "$ROOT/link/file"
|
|
assert_file_exists "$ROOT/link/$submodule_repo_name/submodule.file"
|
|
assert_file_absent "$ROOT/link/$submodule_repo_name/$nested_submodule_repo_name/nested-submodule.file"
|
|
assert_metric_eq "${METRIC_GOOD_SYNC_COUNT}" 5
|
|
|
|
# Remove submodule
|
|
git -C "$REPO" submodule deinit -q $submodule_repo_name
|
|
rm -rf "$REPO/.git/modules/$submodule_repo_name"
|
|
git -C "$REPO" rm -qf $submodule_repo_name
|
|
git -C "$REPO" commit -aqm "delete submodule"
|
|
wait_for_sync "${MAXWAIT}"
|
|
assert_link_exists "$ROOT/link"
|
|
assert_file_exists "$ROOT/link/file"
|
|
assert_file_absent "$ROOT/link/$submodule_repo_name/submodule.file"
|
|
assert_metric_eq "${METRIC_GOOD_SYNC_COUNT}" 6
|
|
|
|
rm -rf $submodule
|
|
rm -rf $nested_submodule
|
|
}
|
|
|
|
##############################################
|
|
# Test submodules depth syncing
|
|
##############################################
|
|
function e2e::submodule_sync_depth() {
|
|
# Init submodule repo
|
|
local submodule_repo_name="sub"
|
|
local submodule="$WORK/$submodule_repo_name"
|
|
mkdir "$submodule"
|
|
|
|
git -C "$submodule" init -q -b "$MAIN_BRANCH"
|
|
|
|
local expected_depth="1"
|
|
local depth
|
|
local submodule_depth
|
|
|
|
# First sync
|
|
echo "${FUNCNAME[0]} 1" > "$submodule/submodule.file"
|
|
git -C "$submodule" add submodule.file
|
|
git -C "$submodule" commit -aqm "submodule ${FUNCNAME[0]} 1"
|
|
git -C "$REPO" -c protocol.file.allow=always submodule add -q file://$submodule "$submodule_repo_name"
|
|
git -C "$REPO" config -f "$REPO/.gitmodules" "submodule.$submodule_repo_name.shallow" true
|
|
git -C "$REPO" commit -qam "${FUNCNAME[0]} 1"
|
|
|
|
GIT_SYNC \
|
|
--period=100ms \
|
|
--repo="file://$REPO" \
|
|
--depth="$expected_depth" \
|
|
--root="$ROOT" \
|
|
--link="link" \
|
|
&
|
|
wait_for_sync "${MAXWAIT}"
|
|
assert_link_exists "$ROOT/link"
|
|
assert_file_exists "$ROOT/link/$submodule_repo_name/submodule.file"
|
|
assert_file_eq "$ROOT/link/$submodule_repo_name/submodule.file" "${FUNCNAME[0]} 1"
|
|
assert_metric_eq "${METRIC_GOOD_SYNC_COUNT}" 1
|
|
depth=$(git -C "$ROOT/link" rev-list HEAD | wc -l)
|
|
if [[ "$expected_depth" != "$depth" ]]; then
|
|
fail "initial depth mismatch expected=$expected_depth actual=$depth"
|
|
fi
|
|
submodule_depth=$(git -C "$ROOT/link/$submodule_repo_name" rev-list HEAD | wc -l)
|
|
if [[ "$expected_depth" != "$submodule_depth" ]]; then
|
|
fail "initial submodule depth mismatch expected=$expected_depth actual=$submodule_depth"
|
|
fi
|
|
|
|
# Move forward
|
|
echo "${FUNCNAME[0]} 2" > "$submodule/submodule.file"
|
|
git -C "$submodule" commit -aqm "submodule ${FUNCNAME[0]} 2"
|
|
git -C "$REPO" -c protocol.file.allow=always submodule update --recursive --remote > /dev/null 2>&1
|
|
git -C "$REPO" commit -qam "${FUNCNAME[0]} 2"
|
|
wait_for_sync "${MAXWAIT}"
|
|
assert_link_exists "$ROOT/link"
|
|
assert_file_exists "$ROOT/link/$submodule_repo_name/submodule.file"
|
|
assert_file_eq "$ROOT/link/$submodule_repo_name/submodule.file" "${FUNCNAME[0]} 2"
|
|
assert_metric_eq "${METRIC_GOOD_SYNC_COUNT}" 2
|
|
depth=$(git -C "$ROOT/link" rev-list HEAD | wc -l)
|
|
if [[ "$expected_depth" != "$depth" ]]; then
|
|
fail "forward depth mismatch expected=$expected_depth actual=$depth"
|
|
fi
|
|
submodule_depth=$(git -C "$ROOT/link/$submodule_repo_name" rev-list HEAD | wc -l)
|
|
if [[ "$expected_depth" != "$submodule_depth" ]]; then
|
|
fail "forward submodule depth mismatch expected=$expected_depth actual=$submodule_depth"
|
|
fi
|
|
|
|
# Move backward
|
|
git -C "$submodule" reset -q --hard HEAD^
|
|
git -C "$REPO" -c protocol.file.allow=always submodule update --recursive --remote > /dev/null 2>&1
|
|
git -C "$REPO" commit -qam "${FUNCNAME[0]} 3"
|
|
wait_for_sync "${MAXWAIT}"
|
|
assert_link_exists "$ROOT/link"
|
|
assert_file_exists "$ROOT/link/$submodule_repo_name/submodule.file"
|
|
assert_file_eq "$ROOT/link/$submodule_repo_name/submodule.file" "${FUNCNAME[0]} 1"
|
|
assert_metric_eq "${METRIC_GOOD_SYNC_COUNT}" 3
|
|
depth=$(git -C "$ROOT/link" rev-list HEAD | wc -l)
|
|
if [[ "$expected_depth" != "$depth" ]]; then
|
|
fail "initial depth mismatch expected=$expected_depth actual=$depth"
|
|
fi
|
|
submodule_depth=$(git -C "$ROOT/link/$submodule_repo_name" rev-list HEAD | wc -l)
|
|
if [[ "$expected_depth" != "$submodule_depth" ]]; then
|
|
fail "initial submodule depth mismatch expected=$expected_depth actual=$submodule_depth"
|
|
fi
|
|
rm -rf $submodule
|
|
}
|
|
|
|
##############################################
|
|
# Test submodules off
|
|
##############################################
|
|
function e2e::submodule_sync_off() {
|
|
# Init submodule repo
|
|
local submodule_repo_name="sub"
|
|
local submodule="$WORK/$submodule_repo_name"
|
|
mkdir "$submodule"
|
|
|
|
git -C "$submodule" init -q -b "$MAIN_BRANCH"
|
|
echo "submodule" > "$submodule/submodule.file"
|
|
git -C "$submodule" add submodule.file
|
|
git -C "$submodule" commit -aqm "init submodule file"
|
|
|
|
# Add submodule
|
|
git -C "$REPO" -c protocol.file.allow=always submodule add -q file://$submodule
|
|
git -C "$REPO" commit -aqm "add submodule"
|
|
|
|
GIT_SYNC \
|
|
--period=100ms \
|
|
--repo="file://$REPO" \
|
|
--root="$ROOT" \
|
|
--link="link" \
|
|
--submodules=off \
|
|
&
|
|
wait_for_sync "${MAXWAIT}"
|
|
assert_file_absent "$ROOT/link/$submodule_repo_name/submodule.file"
|
|
rm -rf $submodule
|
|
}
|
|
|
|
##############################################
|
|
# Test submodules shallow
|
|
##############################################
|
|
function e2e::submodule_sync_shallow() {
|
|
# Init submodule repo
|
|
local submodule_repo_name="sub"
|
|
local submodule="$WORK/$submodule_repo_name"
|
|
mkdir "$submodule"
|
|
|
|
git -C "$submodule" init -q -b "$MAIN_BRANCH"
|
|
echo "submodule" > "$submodule/submodule.file"
|
|
git -C "$submodule" add submodule.file
|
|
git -C "$submodule" commit -aqm "init submodule file"
|
|
|
|
# Init nested submodule repo
|
|
local nested_submodule_repo_name="nested-sub"
|
|
local nested_submodule="$WORK/$nested_submodule_repo_name"
|
|
mkdir "$nested_submodule"
|
|
|
|
git -C "$nested_submodule" init -q -b "$MAIN_BRANCH"
|
|
echo "nested-submodule" > "$nested_submodule/nested-submodule.file"
|
|
git -C "$nested_submodule" add nested-submodule.file
|
|
git -C "$nested_submodule" commit -aqm "init nested-submodule file"
|
|
git -C "$submodule" -c protocol.file.allow=always submodule add -q file://$nested_submodule "$nested_submodule_repo_name"
|
|
git -C "$submodule" commit -aqm "add nested submodule"
|
|
|
|
# Add submodule
|
|
git -C "$REPO" -c protocol.file.allow=always submodule add -q file://$submodule "$submodule_repo_name"
|
|
git -C "$REPO" commit -aqm "add submodule"
|
|
|
|
GIT_SYNC \
|
|
--period=100ms \
|
|
--repo="file://$REPO" \
|
|
--root="$ROOT" \
|
|
--link="link" \
|
|
--submodules=shallow \
|
|
&
|
|
wait_for_sync "${MAXWAIT}"
|
|
assert_link_exists "$ROOT/link"
|
|
assert_file_exists "$ROOT/link/file"
|
|
assert_file_exists "$ROOT/link/$submodule_repo_name/submodule.file"
|
|
assert_file_absent "$ROOT/link/$submodule_repo_name/$nested_submodule_repo_name/nested-submodule.file"
|
|
rm -rf $submodule
|
|
rm -rf $nested_submodule
|
|
}
|
|
|
|
##############################################
|
|
# Test submodule sync with a relative path
|
|
##############################################
|
|
function e2e::submodule_sync_relative() {
|
|
# Init submodule repo
|
|
local submodule_repo_name="sub"
|
|
local submodule="$WORK/$submodule_repo_name"
|
|
mkdir "$submodule"
|
|
|
|
git -C "$submodule" init -q -b "$MAIN_BRANCH"
|
|
echo "submodule" > "$submodule/submodule.file"
|
|
git -C "$submodule" add submodule.file
|
|
git -C "$submodule" commit -aqm "init submodule file"
|
|
|
|
# Add submodule
|
|
local rel
|
|
rel="$(realpath --relative-to "$REPO" "$WORK/$submodule_repo_name")"
|
|
git -C "$REPO" -c protocol.file.allow=always submodule add -q "$rel" "$submodule_repo_name"
|
|
git -C "$REPO" commit -aqm "add submodule"
|
|
|
|
GIT_SYNC \
|
|
--period=100ms \
|
|
--repo="file://$REPO" \
|
|
--root="$ROOT" \
|
|
--link="link" \
|
|
&
|
|
wait_for_sync "${MAXWAIT}"
|
|
assert_link_exists "$ROOT/link"
|
|
assert_file_exists "$ROOT/link/file"
|
|
assert_file_exists "$ROOT/link/$submodule_repo_name/submodule.file"
|
|
assert_file_eq "$ROOT/link/$submodule_repo_name/submodule.file" "submodule"
|
|
assert_metric_eq "${METRIC_GOOD_SYNC_COUNT}" 1
|
|
|
|
rm -rf $submodule
|
|
}
|
|
|
|
##############################################
|
|
# Test submodules over SSH with different keys
|
|
##############################################
|
|
function e2e::submodule_sync_over_ssh_different_keys() {
|
|
# Init nested submodule repo
|
|
local nested_submodule_repo_name="nested-sub"
|
|
local nested_submodule="$WORK/$nested_submodule_repo_name"
|
|
mkdir "$nested_submodule"
|
|
|
|
git -C "$nested_submodule" init -q -b "$MAIN_BRANCH"
|
|
echo "nested-submodule" > "$nested_submodule/nested-submodule.file"
|
|
git -C "$nested_submodule" add nested-submodule.file
|
|
git -C "$nested_submodule" commit -aqm "init nested-submodule.file"
|
|
|
|
# Run a git-over-SSH server. Use key #1.
|
|
local ctr_subsub
|
|
ctr_subsub=$(docker_run \
|
|
-v "$DOT_SSH/server/1":/dot_ssh:ro \
|
|
-v "$nested_submodule":/git/repo:ro \
|
|
e2e/test/sshd)
|
|
local ip_subsub
|
|
ip_subsub=$(docker_ip "$ctr_subsub")
|
|
|
|
# Tell local git not to do host checking and to use the test keys.
|
|
export GIT_SSH_COMMAND="ssh -F none -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i $DOT_SSH/1/id_local -i $DOT_SSH/2/id_local"
|
|
|
|
# Init submodule repo
|
|
local submodule_repo_name="sub"
|
|
local submodule="$WORK/$submodule_repo_name"
|
|
mkdir "$submodule"
|
|
|
|
git -C "$submodule" init -q -b "$MAIN_BRANCH"
|
|
echo "submodule" > "$submodule/submodule.file"
|
|
git -C "$submodule" add submodule.file
|
|
git -C "$submodule" commit -aqm "init submodule.file"
|
|
|
|
# Add nested submodule to submodule repo
|
|
git -C "$submodule" submodule add -q "test@$ip_subsub:/git/repo" "$nested_submodule_repo_name"
|
|
git -C "$submodule" commit -aqm "add nested submodule"
|
|
|
|
# Run a git-over-SSH server. Use key #2.
|
|
local ctr_sub
|
|
ctr_sub=$(docker_run \
|
|
-v "$DOT_SSH/server/2":/dot_ssh:ro \
|
|
-v "$submodule":/git/repo:ro \
|
|
e2e/test/sshd)
|
|
local ip_sub
|
|
ip_sub=$(docker_ip "$ctr_sub")
|
|
|
|
# Add the submodule to the main repo
|
|
git -C "$REPO" submodule add -q "test@$ip_sub:/git/repo" "$submodule_repo_name"
|
|
git -C "$REPO" commit -aqm "add submodule"
|
|
git -C "$REPO" submodule update --recursive --remote > /dev/null 2>&1
|
|
|
|
# Run a git-over-SSH server. Use key #3.
|
|
local ctr
|
|
ctr=$(docker_run \
|
|
-v "$DOT_SSH/server/3":/dot_ssh:ro \
|
|
-v "$REPO":/git/repo:ro \
|
|
e2e/test/sshd)
|
|
local ip
|
|
ip=$(docker_ip "$ctr")
|
|
|
|
GIT_SYNC \
|
|
--period=100ms \
|
|
--repo="test@$ip:/git/repo" \
|
|
--root="$ROOT" \
|
|
--link="link" \
|
|
--ssh-key-file="/ssh/secret.1" \
|
|
--ssh-key-file="/ssh/secret.2" \
|
|
--ssh-key-file="/ssh/secret.3" \
|
|
--ssh-known-hosts=false \
|
|
&
|
|
wait_for_sync "${MAXWAIT}"
|
|
assert_link_exists "$ROOT/link"
|
|
assert_file_exists "$ROOT/link/file"
|
|
assert_file_exists "$ROOT/link/$submodule_repo_name/submodule.file"
|
|
assert_file_exists "$ROOT/link/$submodule_repo_name/$nested_submodule_repo_name/nested-submodule.file"
|
|
assert_metric_eq "${METRIC_GOOD_SYNC_COUNT}" 1
|
|
|
|
rm -rf $submodule
|
|
rm -rf $nested_submodule
|
|
}
|
|
|
|
##############################################
|
|
# Test submodules over HTTP with different passwords
|
|
##############################################
|
|
function e2e::submodule_sync_over_http_different_passwords() {
|
|
# Init nested submodule repo
|
|
local nested_submodule_repo_name="nested-sub"
|
|
local nested_submodule="$WORK/$nested_submodule_repo_name"
|
|
mkdir "$nested_submodule"
|
|
|
|
git -C "$nested_submodule" init -q -b "$MAIN_BRANCH"
|
|
echo "nested-submodule" > "$nested_submodule/nested-submodule.file"
|
|
git -C "$nested_submodule" add nested-submodule.file
|
|
git -C "$nested_submodule" commit -aqm "init nested-submodule.file"
|
|
|
|
# Run a git-over-SSH server. Use password "test1".
|
|
# shellcheck disable=SC2016
|
|
echo 'test:$apr1$cXiFWR90$Pmoz7T8kEmlpC9Bpj4MX3.' > "$WORK/htpasswd.1"
|
|
local ctr_subsub
|
|
ctr_subsub=$(docker_run \
|
|
-v "$nested_submodule":/git/repo:ro \
|
|
-v "$WORK/htpasswd.1":/etc/htpasswd:ro \
|
|
e2e/test/httpd)
|
|
local ip_subsub
|
|
ip_subsub=$(docker_ip "$ctr_subsub")
|
|
|
|
# Init submodule repo
|
|
local submodule_repo_name="sub"
|
|
local submodule="$WORK/$submodule_repo_name"
|
|
mkdir "$submodule"
|
|
|
|
git -C "$submodule" init -q -b "$MAIN_BRANCH"
|
|
echo "submodule" > "$submodule/submodule.file"
|
|
git -C "$submodule" add submodule.file
|
|
git -C "$submodule" commit -aqm "init submodule.file"
|
|
|
|
# Add nested submodule to submodule repo
|
|
echo -ne "url=http://$ip_subsub/repo\nusername=test\npassword=test1\n" | git credential approve
|
|
git -C "$submodule" submodule add -q "http://$ip_subsub/repo" "$nested_submodule_repo_name"
|
|
git -C "$submodule" commit -aqm "add nested submodule"
|
|
|
|
# Run a git-over-SSH server. Use password "test2".
|
|
# shellcheck disable=SC2016
|
|
echo 'test:$apr1$vWBoWUBS$2H.WFxF8T7rH/gZF99Edl/' > "$WORK/htpasswd.2"
|
|
local ctr_sub
|
|
ctr_sub=$(docker_run \
|
|
-v "$submodule":/git/repo:ro \
|
|
-v "$WORK/htpasswd.2":/etc/htpasswd:ro \
|
|
e2e/test/httpd)
|
|
local ip_sub
|
|
ip_sub=$(docker_ip "$ctr_sub")
|
|
|
|
# Add the submodule to the main repo
|
|
echo -ne "url=http://$ip_sub/repo\nusername=test\npassword=test2\n" | git credential approve
|
|
git -C "$REPO" submodule add -q "http://$ip_sub/repo" "$submodule_repo_name"
|
|
git -C "$REPO" commit -aqm "add submodule"
|
|
git -C "$REPO" submodule update --recursive --remote > /dev/null 2>&1
|
|
|
|
# Run a git-over-SSH server. Use password "test3".
|
|
# shellcheck disable=SC2016
|
|
echo 'test:$apr1$oKP2oGwp$ESJ4FESEP/8Sisy02B/vM/' > "$WORK/htpasswd.3"
|
|
local ctr
|
|
ctr=$(docker_run \
|
|
-v "$REPO":/git/repo:ro \
|
|
-v "$WORK/htpasswd.3":/etc/htpasswd:ro \
|
|
e2e/test/httpd)
|
|
local ip
|
|
ip=$(docker_ip "$ctr")
|
|
|
|
GIT_SYNC \
|
|
--period=100ms \
|
|
--repo="http://$ip/repo" \
|
|
--root="$ROOT" \
|
|
--link="link" \
|
|
--credential="{ \"url\": \"http://$ip_subsub/repo\", \"username\": \"test\", \"password\": \"test1\" }" \
|
|
--credential="{ \"url\": \"http://$ip_sub/repo\", \"username\": \"test\", \"password\": \"test2\" }" \
|
|
--credential="{ \"url\": \"http://$ip/repo\", \"username\": \"test\", \"password\": \"test3\" }" \
|
|
&
|
|
wait_for_sync "${MAXWAIT}"
|
|
assert_link_exists "$ROOT/link"
|
|
assert_file_exists "$ROOT/link/file"
|
|
assert_file_exists "$ROOT/link/$submodule_repo_name/submodule.file"
|
|
assert_file_exists "$ROOT/link/$submodule_repo_name/$nested_submodule_repo_name/nested-submodule.file"
|
|
assert_metric_eq "${METRIC_GOOD_SYNC_COUNT}" 1
|
|
|
|
rm -rf $submodule
|
|
rm -rf $nested_submodule
|
|
}
|
|
|
|
##############################################
|
|
# Test sparse-checkout files
|
|
##############################################
|
|
function e2e::sparse_checkout() {
|
|
echo "!/*" > "$WORK/sparseconfig"
|
|
echo "!/*/" >> "$WORK/sparseconfig"
|
|
echo "file2" >> "$WORK/sparseconfig"
|
|
echo "${FUNCNAME[0]}" > "$REPO/file"
|
|
echo "${FUNCNAME[0]}" > "$REPO/file2"
|
|
mkdir "$REPO/dir"
|
|
echo "${FUNCNAME[0]}" > "$REPO/dir/file3"
|
|
git -C "$REPO" add file2
|
|
git -C "$REPO" add dir
|
|
git -C "$REPO" commit -qam "${FUNCNAME[0]}"
|
|
|
|
GIT_SYNC \
|
|
--one-time \
|
|
--repo="file://$REPO" \
|
|
--root="$ROOT" \
|
|
--link="link" \
|
|
--sparse-checkout-file="$WORK/sparseconfig"
|
|
assert_link_exists "$ROOT/link"
|
|
assert_file_exists "$ROOT/link/file2"
|
|
assert_file_absent "$ROOT/link/file"
|
|
assert_file_absent "$ROOT/link/dir/file3"
|
|
assert_file_absent "$ROOT/link/dir"
|
|
assert_file_eq "$ROOT/link/file2" "${FUNCNAME[0]}"
|
|
}
|
|
|
|
##############################################
|
|
# Test additional git configs
|
|
##############################################
|
|
function e2e::additional_git_configs() {
|
|
GIT_SYNC \
|
|
--one-time \
|
|
--repo="file://$REPO" \
|
|
--root="$ROOT" \
|
|
--link="link" \
|
|
--git-config='http.postBuffer:10485760,sect.k1:"a val",sect.k2:another val'
|
|
assert_link_exists "$ROOT/link"
|
|
assert_file_exists "$ROOT/link/file"
|
|
assert_file_eq "$ROOT/link/file" "${FUNCNAME[0]}"
|
|
}
|
|
|
|
##############################################
|
|
# Test export-error
|
|
##############################################
|
|
function e2e::export_error() {
|
|
assert_fail \
|
|
GIT_SYNC \
|
|
--repo="file://$REPO" \
|
|
--ref=does-not-exit \
|
|
--root="$ROOT" \
|
|
--link="link" \
|
|
--error-file="error.json"
|
|
assert_file_absent "$ROOT/link"
|
|
assert_file_absent "$ROOT/link/file"
|
|
assert_file_contains "$ROOT/error.json" "couldn't find remote ref"
|
|
|
|
# the error.json file should be removed if sync succeeds.
|
|
GIT_SYNC \
|
|
--one-time \
|
|
--repo="file://$REPO" \
|
|
--root="$ROOT" \
|
|
--link="link" \
|
|
--error-file="error.json"
|
|
assert_link_exists "$ROOT/link"
|
|
assert_file_exists "$ROOT/link/file"
|
|
assert_file_eq "$ROOT/link/file" "${FUNCNAME[0]}"
|
|
assert_file_absent "$ROOT/error.json"
|
|
}
|
|
|
|
##############################################
|
|
# Test export-error with an absolute path
|
|
##############################################
|
|
function e2e::export_error_abs_path() {
|
|
assert_fail \
|
|
GIT_SYNC \
|
|
--repo="file://$REPO" \
|
|
--ref=does-not-exit \
|
|
--root="$ROOT" \
|
|
--link="link" \
|
|
--error-file="$ROOT/dir/error.json"
|
|
assert_file_absent "$ROOT/link"
|
|
assert_file_absent "$ROOT/link/file"
|
|
assert_file_contains "$ROOT/dir/error.json" "couldn't find remote ref"
|
|
}
|
|
|
|
##############################################
|
|
# Test touch-file
|
|
##############################################
|
|
function e2e::touch_file() {
|
|
# First sync
|
|
echo "${FUNCNAME[0]} 1" > "$REPO/file"
|
|
git -C "$REPO" commit -qam "${FUNCNAME[0]} 1"
|
|
|
|
GIT_SYNC \
|
|
--period=100ms \
|
|
--repo="file://$REPO" \
|
|
--root="$ROOT" \
|
|
--link="link" \
|
|
--touch-file="touch.file" \
|
|
&
|
|
wait_for_file_exists "$ROOT/touch.file" 3
|
|
assert_file_exists "$ROOT/touch.file"
|
|
rm -f "$ROOT/touch.file"
|
|
assert_link_exists "$ROOT/link"
|
|
assert_file_exists "$ROOT/link/file"
|
|
assert_file_eq "$ROOT/link/file" "${FUNCNAME[0]} 1"
|
|
|
|
# It should not come back until we commit again.
|
|
sleep 1
|
|
assert_file_absent "$ROOT/touch.file"
|
|
|
|
# Move HEAD forward
|
|
echo "${FUNCNAME[0]} 2" > "$REPO/file"
|
|
git -C "$REPO" commit -qam "${FUNCNAME[0]} 2"
|
|
wait_for_file_exists "$ROOT/touch.file" 3
|
|
assert_file_exists "$ROOT/touch.file"
|
|
rm -f "$ROOT/touch.file"
|
|
assert_link_exists "$ROOT/link"
|
|
assert_file_exists "$ROOT/link/file"
|
|
assert_file_eq "$ROOT/link/file" "${FUNCNAME[0]} 2"
|
|
|
|
# It should not come back until we commit again.
|
|
sleep 1
|
|
assert_file_absent "$ROOT/touch.file"
|
|
|
|
# Move HEAD backward
|
|
git -C "$REPO" reset -q --hard HEAD^
|
|
wait_for_file_exists "$ROOT/touch.file" 3
|
|
assert_file_exists "$ROOT/touch.file"
|
|
rm -f "$ROOT/touch.file"
|
|
assert_link_exists "$ROOT/link"
|
|
assert_file_exists "$ROOT/link/file"
|
|
assert_file_eq "$ROOT/link/file" "${FUNCNAME[0]} 1"
|
|
|
|
# It should not come back until we commit again.
|
|
sleep 1
|
|
assert_file_absent "$ROOT/touch.file"
|
|
}
|
|
|
|
##############################################
|
|
# Test touch-file with an absolute path
|
|
##############################################
|
|
function e2e::touch_file_abs_path() {
|
|
# First sync
|
|
echo "${FUNCNAME[0]} 1" > "$REPO/file"
|
|
git -C "$REPO" commit -qam "${FUNCNAME[0]} 1"
|
|
|
|
GIT_SYNC \
|
|
--period=100ms \
|
|
--repo="file://$REPO" \
|
|
--root="$ROOT" \
|
|
--link="link" \
|
|
--touch-file="$ROOT/dir/touch.file" \
|
|
&
|
|
wait_for_file_exists "$ROOT/dir/touch.file" 3
|
|
assert_file_exists "$ROOT/dir/touch.file"
|
|
rm -f "$ROOT/dir/touch.file"
|
|
assert_link_exists "$ROOT/link"
|
|
assert_file_exists "$ROOT/link/file"
|
|
assert_file_eq "$ROOT/link/file" "${FUNCNAME[0]} 1"
|
|
|
|
# It should not come back until we commit again.
|
|
sleep 1
|
|
assert_file_absent "$ROOT/dir/touch.file"
|
|
|
|
# Move HEAD forward
|
|
echo "${FUNCNAME[0]} 2" > "$REPO/file"
|
|
git -C "$REPO" commit -qam "${FUNCNAME[0]} 2"
|
|
wait_for_file_exists "$ROOT/dir/touch.file" 3
|
|
assert_file_exists "$ROOT/dir/touch.file"
|
|
rm -f "$ROOT/dir/touch.file"
|
|
assert_link_exists "$ROOT/link"
|
|
assert_file_exists "$ROOT/link/file"
|
|
assert_file_eq "$ROOT/link/file" "${FUNCNAME[0]} 2"
|
|
|
|
# It should not come back until we commit again.
|
|
sleep 1
|
|
assert_file_absent "$ROOT/dir/touch.file"
|
|
|
|
# Move HEAD backward
|
|
git -C "$REPO" reset -q --hard HEAD^
|
|
wait_for_file_exists "$ROOT/dir/touch.file" 3
|
|
assert_file_exists "$ROOT/dir/touch.file"
|
|
rm -f "$ROOT/dir/touch.file"
|
|
assert_link_exists "$ROOT/link"
|
|
assert_file_exists "$ROOT/link/file"
|
|
assert_file_eq "$ROOT/link/file" "${FUNCNAME[0]} 1"
|
|
|
|
# It should not come back until we commit again.
|
|
sleep 1
|
|
assert_file_absent "$ROOT/dir/touch.file"
|
|
}
|
|
|
|
##############################################
|
|
# Test github HTTPS
|
|
##############################################
|
|
function e2e::github_https() {
|
|
GIT_SYNC \
|
|
--one-time \
|
|
--repo="https://github.com/kubernetes/git-sync" \
|
|
--root="$ROOT" \
|
|
--link="link"
|
|
assert_file_exists "$ROOT/link/LICENSE"
|
|
}
|
|
|
|
##############################################
|
|
# Test git-gc default
|
|
##############################################
|
|
function e2e::gc_default() {
|
|
local sha1
|
|
sha1=$(git -C "$REPO" rev-parse HEAD)
|
|
dd if=/dev/urandom of="$REPO/big1" bs=1024 count=4096 >/dev/null
|
|
git -C "$REPO" add .
|
|
git -C "$REPO" commit -qam "${FUNCNAME[0]} 1"
|
|
local sha2
|
|
sha2=$(git -C "$REPO" rev-parse HEAD)
|
|
dd if=/dev/urandom of="$REPO/big2" bs=1024 count=4096 >/dev/null
|
|
git -C "$REPO" add .
|
|
git -C "$REPO" commit -qam "${FUNCNAME[0]} 2"
|
|
local sha3
|
|
sha3=$(git -C "$REPO" rev-parse HEAD)
|
|
|
|
GIT_SYNC \
|
|
--one-time \
|
|
--repo="file://$REPO" \
|
|
--root="$ROOT" \
|
|
--link="link" \
|
|
--ref="$sha3" \
|
|
--depth=0
|
|
assert_link_exists "$ROOT/link"
|
|
assert_file_exists "$ROOT/link/big1"
|
|
assert_file_exists "$ROOT/link/big2"
|
|
local size
|
|
size=$(du -s "$ROOT" | cut -f1)
|
|
if [ "$size" -lt 14000 ]; then
|
|
fail "repo is impossibly small: $size"
|
|
fi
|
|
if [ "$size" -gt 18000 ]; then
|
|
fail "repo is too big: $size"
|
|
fi
|
|
|
|
GIT_SYNC \
|
|
--one-time \
|
|
--repo="file://$REPO" \
|
|
--root="$ROOT" \
|
|
--link="link" \
|
|
--ref="$sha3" \
|
|
--depth=1
|
|
assert_link_exists "$ROOT/link"
|
|
assert_file_exists "$ROOT/link/big1"
|
|
assert_file_exists "$ROOT/link/big2"
|
|
size=$(du -s "$ROOT" | cut -f1)
|
|
if [ "$size" -lt 14000 ]; then
|
|
fail "repo is impossibly small: $size"
|
|
fi
|
|
if [ "$size" -gt 18000 ]; then
|
|
fail "repo is too big: $size"
|
|
fi
|
|
|
|
GIT_SYNC \
|
|
--one-time \
|
|
--repo="file://$REPO" \
|
|
--root="$ROOT" \
|
|
--link="link" \
|
|
--ref="$sha2" \
|
|
--depth=1
|
|
assert_link_exists "$ROOT/link"
|
|
assert_file_exists "$ROOT/link/big1"
|
|
assert_file_absent "$ROOT/link/big2"
|
|
size=$(du -s "$ROOT" | cut -f1)
|
|
if [ "$size" -lt 7000 ]; then
|
|
fail "repo is impossibly small: $size"
|
|
fi
|
|
if [ "$size" -gt 9000 ]; then
|
|
fail "repo is too big: $size"
|
|
fi
|
|
|
|
GIT_SYNC \
|
|
--one-time \
|
|
--repo="file://$REPO" \
|
|
--root="$ROOT" \
|
|
--link="link" \
|
|
--ref="$sha1" \
|
|
--depth=1
|
|
assert_link_exists "$ROOT/link"
|
|
assert_file_absent "$ROOT/link/big1"
|
|
assert_file_absent "$ROOT/link/big2"
|
|
size=$(du -s "$ROOT" | cut -f1)
|
|
if [ "$size" -lt 100 ]; then
|
|
fail "repo is impossibly small: $size"
|
|
fi
|
|
if [ "$size" -gt 1000 ]; then
|
|
fail "repo is too big: $size"
|
|
fi
|
|
}
|
|
|
|
##############################################
|
|
# Test git-gc=auto
|
|
##############################################
|
|
function e2e::gc_auto() {
|
|
GIT_SYNC \
|
|
--one-time \
|
|
--repo="file://$REPO" \
|
|
--root="$ROOT" \
|
|
--link="link" \
|
|
--git-gc="auto"
|
|
assert_link_exists "$ROOT/link"
|
|
assert_file_exists "$ROOT/link/file"
|
|
assert_file_eq "$ROOT/link/file" "${FUNCNAME[0]}"
|
|
}
|
|
|
|
##############################################
|
|
# Test git-gc=always
|
|
##############################################
|
|
function e2e::gc_always() {
|
|
GIT_SYNC \
|
|
--one-time \
|
|
--repo="file://$REPO" \
|
|
--root="$ROOT" \
|
|
--link="link" \
|
|
--git-gc="always"
|
|
assert_link_exists "$ROOT/link"
|
|
assert_file_exists "$ROOT/link/file"
|
|
assert_file_eq "$ROOT/link/file" "${FUNCNAME[0]}"
|
|
}
|
|
|
|
##############################################
|
|
# Test git-gc=aggressive
|
|
##############################################
|
|
function e2e::gc_aggressive() {
|
|
GIT_SYNC \
|
|
--one-time \
|
|
--repo="file://$REPO" \
|
|
--root="$ROOT" \
|
|
--link="link" \
|
|
--git-gc="aggressive"
|
|
assert_link_exists "$ROOT/link"
|
|
assert_file_exists "$ROOT/link/file"
|
|
assert_file_eq "$ROOT/link/file" "${FUNCNAME[0]}"
|
|
}
|
|
|
|
##############################################
|
|
# Test git-gc=off
|
|
##############################################
|
|
function e2e::gc_off() {
|
|
GIT_SYNC \
|
|
--one-time \
|
|
--repo="file://$REPO" \
|
|
--root="$ROOT" \
|
|
--link="link" \
|
|
--git-gc="off"
|
|
assert_link_exists "$ROOT/link"
|
|
assert_file_exists "$ROOT/link/file"
|
|
assert_file_eq "$ROOT/link/file" "${FUNCNAME[0]}"
|
|
}
|
|
|
|
#
|
|
# main
|
|
#
|
|
|
|
function list_tests() {
|
|
(
|
|
shopt -s extdebug
|
|
declare -F \
|
|
| cut -f3 -d' ' \
|
|
| grep "^e2e::" \
|
|
| while read -r X; do declare -F "$X"; done \
|
|
| sort -n -k2 \
|
|
| cut -f1 -d' ' \
|
|
| sed 's/^e2e:://'
|
|
)
|
|
}
|
|
|
|
# Figure out which, if any, tests to run.
|
|
mapfile -t all_tests < <(list_tests)
|
|
tests_to_run=()
|
|
|
|
function print_tests() {
|
|
echo "available tests:"
|
|
for t in "${all_tests[@]}"; do
|
|
echo " $t"
|
|
done
|
|
}
|
|
|
|
# Validate and accumulate tests to run if args are specified.
|
|
for arg; do
|
|
# Use -? to list known tests.
|
|
if [[ "${arg}" == "-?" ]]; then
|
|
print_tests
|
|
exit 0
|
|
fi
|
|
if [[ "${arg}" =~ ^- ]]; then
|
|
echo "ERROR: unknown flag '${arg}'"
|
|
exit 1
|
|
fi
|
|
# Make sure each non-flag arg matches at least one test.
|
|
nmatches=0
|
|
for t in "${all_tests[@]}"; do
|
|
if [[ "${t}" =~ ${arg} ]]; then
|
|
nmatches=$((nmatches+1))
|
|
# Don't run tests twice, just keep the first match.
|
|
if [[ " ${tests_to_run[*]} " == *" ${t} "* ]]; then
|
|
continue
|
|
fi
|
|
tests_to_run+=("${t}")
|
|
continue
|
|
fi
|
|
done
|
|
if [[ ${nmatches} == 0 ]]; then
|
|
echo "ERROR: no tests match pattern '${arg}'"
|
|
echo
|
|
print_tests
|
|
exit 1
|
|
fi
|
|
tests_to_run+=("${matches[@]}")
|
|
done
|
|
set -- "${tests_to_run[@]}"
|
|
|
|
# If no tests were specified, run them all.
|
|
if [[ "$#" == 0 ]]; then
|
|
set -- "${all_tests[@]}"
|
|
fi
|
|
|
|
# Build it
|
|
$build_container && make container REGISTRY=e2e VERSION="${E2E_TAG}" ALLOW_STALE_APT=1
|
|
make test-tools REGISTRY=e2e
|
|
|
|
function finish() {
|
|
local ret=$?
|
|
trap "" INT EXIT ERR
|
|
if [[ $ret != 0 ]]; then
|
|
echo
|
|
echo "the directory $DIR was not removed as it contains"\
|
|
"log files useful for debugging"
|
|
fi
|
|
exit $ret
|
|
}
|
|
trap finish INT EXIT ERR
|
|
|
|
# Run a test function and return its error code. This is needed because POSIX
|
|
# dictates that `errexit` does not apply inside a function called in an `if`
|
|
# context. But if we don't call it with `if`, then it terminates the whole
|
|
# test run as soon as one test fails. So this jumps through hoops to let the
|
|
# individual test functions run outside of `if` and return a code in a
|
|
# variable.
|
|
#
|
|
# Args:
|
|
# $1: the name of a variable to populate with the return code
|
|
# $2+: the test function to run and optional args
|
|
function run_test() {
|
|
local retvar=$1
|
|
shift
|
|
|
|
declare -g "$retvar"
|
|
local restore_opts
|
|
restore_opts=$(set +o)
|
|
set +o errexit
|
|
set +o nounset
|
|
set +o pipefail
|
|
(
|
|
set -o errexit
|
|
set -o nounset
|
|
set -o pipefail
|
|
"$@"
|
|
)
|
|
eval "$retvar=$?"
|
|
eval "$restore_opts"
|
|
}
|
|
|
|
# Override local configs for predictability in this test.
|
|
export GIT_CONFIG_GLOBAL="$DIR/gitconfig"
|
|
export GIT_CONFIG_SYSTEM=/dev/null
|
|
git config --global user.email "git-sync-test@example.com"
|
|
git config --global user.name "git-sync-test"
|
|
|
|
# Make sure files we create can be group writable.
|
|
umask 0002
|
|
|
|
# Mark all repos as safe, to avoid "dubious ownership".
|
|
git config --global --add safe.directory '*'
|
|
|
|
# Store credentials for the test.
|
|
git config --global credential.helper "store --file $DIR/gitcreds"
|
|
|
|
# Log some info
|
|
if [[ -n "${VERBOSE:-}" ]]; then
|
|
git version
|
|
echo
|
|
docker version
|
|
echo
|
|
fi
|
|
|
|
failures=()
|
|
final_ret=0
|
|
RUNS="${RUNS:-1}"
|
|
|
|
echo
|
|
echo "test root is $DIR"
|
|
if (( "${RUNS}" > 1 )); then
|
|
echo " RUNS=$RUNS"
|
|
fi
|
|
if [[ "${CLEANUP:-}" == 0 ]]; then
|
|
echo " CLEANUP disabled"
|
|
fi
|
|
if [[ -n "${VERBOSE:-}" ]]; then
|
|
echo " VERBOSE enabled"
|
|
fi
|
|
echo
|
|
|
|
# Iterate over the chosen tests and run them.
|
|
for t; do
|
|
test_fn="e2e::${t}"
|
|
test_ret=0
|
|
run=0
|
|
while (( "${run}" < "${RUNS}" )); do
|
|
clean_root
|
|
clean_work
|
|
init_repo "${test_fn}"
|
|
|
|
sfx=""
|
|
if (( "${RUNS}" > 1 )); then
|
|
sfx=" ($((run+1))/${RUNS})"
|
|
fi
|
|
echo -n "testcase ${t}${sfx}: "
|
|
|
|
# Set &3 for our own output, let testcases use &2 and &1.
|
|
exec 3>&1
|
|
|
|
# See comments on run_test for details.
|
|
run_ret=0
|
|
log="${DIR}/log.$t"
|
|
run_test run_ret "${test_fn}" >"${log}.${run}" 2>&1
|
|
if [[ "$run_ret" == 0 ]]; then
|
|
pass
|
|
elif [[ "$run_ret" == 43 ]]; then
|
|
true # do nothing
|
|
else
|
|
test_ret=1
|
|
if [[ "$run_ret" != 42 ]]; then
|
|
echo "FAIL: unknown error"
|
|
fi
|
|
if [[ -n "${VERBOSE:-}" ]]; then
|
|
echo -ne "\n\n"
|
|
echo "log ----------------------"
|
|
cat "${log}.${run}"
|
|
echo "--------------------------"
|
|
echo -ne "\n\n"
|
|
fi
|
|
fi
|
|
remove_containers || true
|
|
run=$((run+1))
|
|
done
|
|
if [[ "$test_ret" != 0 ]]; then
|
|
final_ret=1
|
|
failures+=("$t (log: ${log}.*)")
|
|
fi
|
|
done
|
|
if [[ "$final_ret" != 0 ]]; then
|
|
echo
|
|
echo "the following tests failed:"
|
|
for f in "${failures[@]}"; do
|
|
echo " $f"
|
|
done
|
|
exit 1
|
|
fi
|
|
|
|
|