diff --git a/Makefile b/Makefile index 3b4e557..cb344bf 100644 --- a/Makefile +++ b/Makefile @@ -181,6 +181,7 @@ test: $(BUILD_DIRS) test-tools: @docker build -t $(REGISTRY)/test/test-sshd _test_tools/sshd + @docker build -t $(REGISTRY)/test/test-ncsvr _test_tools/ncsvr # Help set up multi-arch build tools. This assumes you have the tools # installed. If you already have a buildx builder available, you don't need diff --git a/_test_tools/ncsvr/Dockerfile b/_test_tools/ncsvr/Dockerfile new file mode 100644 index 0000000..3a7abd1 --- /dev/null +++ b/_test_tools/ncsvr/Dockerfile @@ -0,0 +1,25 @@ +# Stolen from https://github.com/linuxkit/linuxkit/tree/master/pkg/sshd/ + +FROM alpine AS base + +RUN mkdir -p /out/etc/apk && cp -r /etc/apk/* /out/etc/apk/ +RUN apk add --no-cache --initdb -p /out \ + alpine-baselayout \ + apk-tools \ + busybox \ + util-linux \ + netcat-openbsd \ + && true + +############### + +FROM scratch + +ENTRYPOINT [] +WORKDIR / + +COPY --from=base /out/ / + +COPY ncsvr.sh / + +ENTRYPOINT ["/ncsvr.sh"] diff --git a/_test_tools/ncsvr/README.md b/_test_tools/ncsvr/README.md new file mode 100644 index 0000000..76253d6 --- /dev/null +++ b/_test_tools/ncsvr/README.md @@ -0,0 +1,38 @@ +# A simple server for tests + +DO NOT USE THIS FOR ANYTHING BUT TESTING!!! + +## How to use it + +Build yourself a test image. We use example.com so you can't accidentally push +it. + +``` +$ docker build -t example.com/test/test-ncvsr . +...lots of output... +Successfully tagged example.com/test/test-ncsvr:latest +``` + +Run it. + +``` +$ docker run -d example.com/test/test-ncsvr 9376 "echo hello" +6d05b4111b03c66907031e3cd7587763f0e4fab6c50fac33c4a8284732b448ae +``` + +Find your IP. + +``` +$ docker inspect 6d05b4111b03c66907031e3cd7587763f0e4fab6c50fac33c4a8284732b448ae | jq -r .[0].NetworkSettings.IPAddress +192.168.1.2 +``` + +Connect to it. + +``` +$ echo "" | nc 192.168.9.2 9376 +hello +``` + +If you want to know how many times it was accessed, mount a file on +/var/log/hits. This will log one line per hit. diff --git a/_test_tools/ncsvr/ncsvr.sh b/_test_tools/ncsvr/ncsvr.sh new file mode 100755 index 0000000..3ec4f81 --- /dev/null +++ b/_test_tools/ncsvr/ncsvr.sh @@ -0,0 +1,11 @@ +#!/bin/sh + +if [ -z "$1" -o -z "$2" ]; then + echo "usage: $0 " + exit 1 +fi + +while true; do + sh -c "$2" | nc -l -p "$1" -N -w0 >/dev/null + date >> /var/log/hits +done diff --git a/test_e2e.sh b/test_e2e.sh index f394cd8..135f2e2 100755 --- a/test_e2e.sh +++ b/test_e2e.sh @@ -52,12 +52,31 @@ function assert_file_eq() { fail "file $1 does not contain '$2': $(cat $1)" } -NCPORT=8888 -function freencport() { - while :; do - NCPORT=$((RANDOM+2000)) - ss -lpn | grep -q ":$NCPORT " || break - done +# Helper: run a docker container. +function docker_run() { + docker run \ + -d \ + --rm \ + --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 " + return 1 + fi + docker inspect "$1" | jq -r .[0].NetworkSettings.IPAddress +} + +function docker_kill() { + if [ -z "$1" ]; then + echo "usage: $0 " + return 1 + fi + docker kill "$1" >/dev/null } # ##################### @@ -106,7 +125,8 @@ cat "$DOT_SSH/id_test.pub" > "$DOT_SSH/authorized_keys" function finish() { if [ $? -ne 0 ]; then - echo "The directory $DIR was not removed as it contains"\ + echo + echo "the directory $DIR was not removed as it contains"\ "log files useful for debugging" fi remove_containers @@ -592,19 +612,15 @@ pass ############################################## testcase "askpass_url" echo "$TESTCASE 1" > "$REPO"/file -freencport git -C "$REPO" commit -qam "$TESTCASE 1" # run the askpass_url service with wrong password -{ ( - for i in 1 2; do - echo -e 'HTTP/1.1 200 OK\r\n\r\nusername=my-username\npassword=wrong' \ - | nc -N -l $NCPORT > /dev/null; - done - ) & -} +CTR=$(docker_run \ + e2e/test/test-ncsvr \ + 80 'echo -e "HTTP/1.1 200 OK\r\n\r\nusername=my-username\npassword=wrong"') +IP=$(docker_ip "$CTR") GIT_SYNC \ --git="$ASKPASS_GIT" \ - --askpass-url="http://localhost:$NCPORT/git_askpass" \ + --askpass-url="http://$IP/git_askpass" \ --one-time \ --repo="file://$REPO" \ --branch=e2e-branch \ @@ -612,26 +628,26 @@ GIT_SYNC \ --root="$ROOT" \ --dest="link" \ > "$DIR"/log."$TESTCASE" 2>&1 || true +docker_kill "$CTR" # check for failure assert_file_absent "$ROOT"/link/file + # run with askpass_url service with correct password -{ ( - for i in 1 2; do - echo -e 'HTTP/1.1 200 OK\r\n\r\nusername=my-username\npassword=my-password' \ - | nc -N -l $NCPORT > /dev/null; - done - ) & -} +CTR=$(docker_run \ + e2e/test/test-ncsvr \ + 80 'echo -e "HTTP/1.1 200 OK\r\n\r\nusername=my-username\npassword=my-password"') +IP=$(docker_ip "$CTR") GIT_SYNC \ --git="$ASKPASS_GIT" \ - --askpass-url="http://localhost:$NCPORT/git_askpass" \ + --askpass-url="http://$IP/git_askpass" \ --one-time \ --repo="file://$REPO" \ --branch=e2e-branch \ --rev=HEAD \ --root="$ROOT" \ --dest="link" \ - > "$DIR"/log."$TESTCASE" 2>&1 + >> "$DIR"/log."$TESTCASE" 2>&1 +docker_kill "$CTR" assert_link_exists "$ROOT"/link assert_file_exists "$ROOT"/link/file assert_file_eq "$ROOT"/link/file "$TESTCASE 1" @@ -675,42 +691,88 @@ pass # Test webhook success ############################################## testcase "webhook-success" -freencport +HITLOG="$DIR/hitlog.$TESTCASE" # First sync +cat /dev/null > "$HITLOG" +CTR=$(docker_run \ + -v "$HITLOG":/var/log/hits \ + e2e/test/test-ncsvr \ + 80 'echo -e "HTTP/1.1 200 OK\r\n"') +IP=$(docker_ip "$CTR") echo "$TESTCASE 1" > "$REPO"/file git -C "$REPO" commit -qam "$TESTCASE 1" GIT_SYNC \ + --wait=0.1 \ --repo="file://$REPO" \ --branch=e2e-branch \ --root="$ROOT" \ - --webhook-url="http://127.0.0.1:$NCPORT" \ + --webhook-url="http://$IP" \ --webhook-success-status=200 \ --dest="link" \ > "$DIR"/log."$TESTCASE" 2>&1 & # check that basic call works -{ (echo -e "HTTP/1.1 200 OK\r\n" | nc -q1 -l $NCPORT > /dev/null) &} -NCPID=$! -sleep 3 -if kill -0 $NCPID > /dev/null 2>&1; then - fail "webhook 1 not called, server still running" +sleep 2 +HITS=$(cat "$HITLOG" | wc -l) +if [ "$HITS" -lt 1 ]; then + fail "webhook 1 called $HITS times" fi # Move forward +cat /dev/null > "$HITLOG" echo "$TESTCASE 2" > "$REPO"/file git -C "$REPO" commit -qam "$TESTCASE 2" -# return a failure to ensure that we try again -{ (echo -e "HTTP/1.1 500 Internal Server Error\r\n" | nc -q1 -l $NCPORT > /dev/null) &} -NCPID=$! -sleep 3 -if kill -0 $NCPID > /dev/null 2>&1; then - fail "webhook 2 not called, server still running" +# check that another call works +sleep 2 +HITS=$(cat "$HITLOG" | wc -l) +if [ "$HITS" -lt 1 ]; then + fail "webhook 2 called $HITS times" fi +docker_kill "$CTR" +# Wrap up +pass + +############################################## +# Test webhook fail-retry +############################################## +testcase "webhook-fail-retry" +HITLOG="$DIR/hitlog.$TESTCASE" +# First sync - return a failure to ensure that we try again +cat /dev/null > "$HITLOG" +CTR=$(docker_run \ + -v "$HITLOG":/var/log/hits \ + e2e/test/test-ncsvr \ + 80 'echo -e "HTTP/1.1 500 Internal Server Error\r\n"') +IP=$(docker_ip "$CTR") +echo "$TESTCASE 1" > "$REPO"/file +git -C "$REPO" commit -qam "$TESTCASE 1" +GIT_SYNC \ + --wait=0.1 \ + --repo="file://$REPO" \ + --branch=e2e-branch \ + --root="$ROOT" \ + --webhook-url="http://$IP" \ + --webhook-success-status=200 \ + --dest="link" \ + > "$DIR"/log."$TESTCASE" 2>&1 & +# Check that webhook was called +sleep 2 +HITS=$(cat "$HITLOG" | wc -l) +if [ "$HITS" -lt 1 ]; then + fail "webhook 1 called $HITS times" +fi +docker_kill "$CTR" # Now return 200, ensure that it gets called -{ (echo -e "HTTP/1.1 200 OK\r\n" | nc -q1 -l $NCPORT > /dev/null) &} -NCPID=$! -sleep 3 -if kill -0 $NCPID > /dev/null 2>&1; then - fail "webhook 3 not called, server still running" +cat /dev/null > "$HITLOG" +CTR=$(docker_run \ + --ip="$IP" \ + -v "$HITLOG":/var/log/hits \ + e2e/test/test-ncsvr \ + 80 'echo -e "HTTP/1.1 200 OK\r\n"') +sleep 2 +HITS=$(cat "$HITLOG" | wc -l) +if [ "$HITS" -lt 1 ]; then + fail "webhook 2 called $HITS times" fi +docker_kill "$CTR" # Wrap up pass @@ -718,25 +780,33 @@ pass # Test webhook fire-and-forget ############################################## testcase "webhook-fire-and-forget" -freencport +HITLOG="$DIR/hitlog.$TESTCASE" +# First sync +cat /dev/null > "$HITLOG" +CTR=$(docker_run \ + -v "$HITLOG":/var/log/hits \ + e2e/test/test-ncsvr \ + 80 'echo -e "HTTP/1.1 404 Not Found\r\n"') +IP=$(docker_ip "$CTR") # First sync echo "$TESTCASE 1" > "$REPO"/file git -C "$REPO" commit -qam "$TESTCASE 1" GIT_SYNC \ + --wait=0.1 \ --repo="file://$REPO" \ --branch=e2e-branch \ --root="$ROOT" \ - --webhook-url="http://127.0.0.1:$NCPORT" \ + --webhook-url="http://$IP" \ --webhook-success-status=-1 \ --dest="link" \ > "$DIR"/log."$TESTCASE" 2>&1 & # check that basic call works -{ (echo -e "HTTP/1.1 404 Not Found\r\n" | nc -q1 -l $NCPORT > /dev/null) &} -NCPID=$! -sleep 3 -if kill -0 $NCPID > /dev/null 2>&1; then - fail "webhook 1 not called, server still running" +sleep 2 +HITS=$(cat "$HITLOG" | wc -l) +if [ "$HITS" -lt 1 ]; then + fail "webhook called $HITS times" fi +docker_kill "$CTR" # Wrap up pass @@ -1040,15 +1110,11 @@ pass testcase "ssh" echo "$TESTCASE" > "$REPO"/file # Run a git-over-SSH server -CTR=$(docker run \ - -d \ - --rm \ - --label git-sync-e2e="$RUNID" \ +CTR=$(docker_run \ -v "$DOT_SSH":/dot_ssh:ro \ -v "$REPO":/src:ro \ e2e/test/test-sshd) -sleep 3 # wait for sshd to come up -IP=$(docker inspect "$CTR" | jq -r .[0].NetworkSettings.IPAddress) +IP=$(docker_ip "$CTR") git -C "$REPO" commit -qam "$TESTCASE" GIT_SYNC \ --one-time \ @@ -1066,5 +1132,6 @@ assert_file_eq "$ROOT"/link/file "$TESTCASE" # Wrap up pass +echo echo "cleaning up $DIR" rm -rf "$DIR"