#!/bin/bash # Copyright (c) Facebook, Inc. and its affiliates. # # This software may be used and distributed according to the terms of the # GNU General Public License found in the LICENSE file in the root # directory of this source tree. # Library routines and initial setup for Mononoke-related tests. if [[ -n "$DB_SHARD_NAME" ]]; then function db_config() { echo "db.db_address=\"$DB_SHARD_NAME\"" } MONONOKE_DEFAULT_START_TIMEOUT=60 else function db_config() { local reponame reponame="$1" echo "db.local_db_path=\"$TESTTMP/monsql\"" } MONONOKE_DEFAULT_START_TIMEOUT=15 fi REPOID=0 REPONAME=repo CACHING_ARGS=(--skip-caching) TEST_CERTDIR="${HGTEST_CERTDIR:-"$TEST_CERTS"}" function get_free_socket { # From https://unix.stackexchange.com/questions/55913/whats-the-easiest-way-to-find-an-unused-local-port cat > "$TESTTMP/get_free_socket.py" <> "$TESTTMP/mononoke.out" 2>&1 & export MONONOKE_PID=$! echo "$MONONOKE_PID" >> "$DAEMON_PIDS" } function mononoke_hg_sync { HG_REPO="$1" shift START_ID="$1" shift GLOG_minloglevel=5 "$MONONOKE_HG_SYNC" \ "${CACHING_ARGS[@]}" \ --retry-num 1 \ --repo-id $REPOID \ --mononoke-config-path mononoke-config \ --verify-server-bookmark-on-failure \ ssh://user@dummy/"$HG_REPO" "$@" sync-once --start-id "$START_ID" } function megarepo_tool { GLOG_minloglevel=5 "$MEGAREPO_TOOL" \ "${CACHING_ARGS[@]}" \ --repo-id $REPOID \ --mononoke-config-path mononoke-config \ "$@" } function mononoke_x_repo_sync_once() { source_repo_id=$1 target_repo_id=$2 target_bookmark=$3 shift shift shift GLOG_minloglevel=5 "$MONONOKE_X_REPO_SYNC" \ "${CACHING_ARGS[@]}" \ --source-tier-config "$TESTTMP/mononoke-config" \ --target-tier-config "$TESTTMP/mononoke-config" \ --source-repo-id "$source_repo_id" \ --target-repo-id "$target_repo_id" \ --target-bookmark "$target_bookmark" \ "$@" } function mononoke_rechunker { GLOG_minloglevel=5 "$MONONOKE_RECHUNKER" \ "${CACHING_ARGS[@]}" \ --repo-id $REPOID \ --mononoke-config-path mononoke-config \ "$@" } function mononoke_hg_sync_with_retry { GLOG_minloglevel=5 "$MONONOKE_HG_SYNC" \ "${CACHING_ARGS[@]}" \ --base-retry-delay-ms 1 \ --repo-id $REPOID \ --mononoke-config-path mononoke-config \ --verify-server-bookmark-on-failure \ ssh://user@dummy/"$1" sync-once --start-id "$2" } function mononoke_hg_sync_with_failure_handler { sql_name="${TESTTMP}/hgrepos/repo_lock" GLOG_minloglevel=5 "$MONONOKE_HG_SYNC" \ "${CACHING_ARGS[@]}" \ --retry-num 1 \ --repo-id $REPOID \ --mononoke-config-path mononoke-config \ --verify-server-bookmark-on-failure \ --lock-on-failure \ --repo-lock-sqlite \ --repo-lock-db-address "$sql_name" \ ssh://user@dummy/"$1" sync-once --start-id "$2" } function create_repo_lock_sqlite3_db { cat >> "$TESTTMP"/repo_lock.sql <> "$TESTTMP"/mutable_counters.sql <> "$TESTTMP"/bookmarks.sql <&1 | grep -q 'Empty reply' && break sleep 0.1 done if ! $SSLCURL 2>&1 | grep -q 'Empty reply'; then echo "Mononoke did not start" >&2 cat "$TESTTMP/mononoke.out" exit 1 fi } # Wait until cache warmup finishes function wait_for_mononoke_cache_warmup { local attempts=150 for _ in $(seq 1 $attempts); do grep -q "finished initial warmup" "$TESTTMP/mononoke.out" && break sleep 0.1 done if ! grep -q "finished initial warmup" "$TESTTMP/mononoke.out"; then echo "Mononoke warmup did not finished" >&2 cat "$TESTTMP/mononoke.out" exit 1 fi } function setup_common_hg_configs { cat >> "$HGRCPATH" <> "$TESTTMP"/pushrebaserecording.sql <> "$TESTTMP/mononoke_hgcli" < common/common.toml < "repos/$reponame/server.toml" <> "repos/$reponame/server.toml" <> "repos/$reponame/server.toml" <> "repos/$reponame/server.toml" <> "repos/$reponame/server.toml" <> "repos/$reponame/server.toml" <> "repos/$reponame/server.toml" <> "repos/$reponame/server.toml" <> "repos/$reponame/server.toml" <> "repos/$reponame/server.toml" <> "repos/$reponame/server.toml" <> "repos/$reponame/server.toml" <> "repos/$reponame/server.toml" <> "repos/$reponame/server.toml" <> "repos/$reponame/server.toml" <> "repos/$reponame/server.toml" <> "repos/$reponame/server.toml" <> "repos/$reponame/server.toml" <> "repos/$reponame/server.toml" <> "repos/$reponame/server.toml" <> "repos/$REPONAME/server.toml" } function blobimport { input="$1" output="$2" shift 2 mkdir -p "$output" $MONONOKE_BLOBIMPORT --repo-id $REPOID \ --mononoke-config-path "$TESTTMP/mononoke-config" \ "$input" "${CACHING_ARGS[@]}" "$@" >> "$TESTTMP/blobimport.out" 2>&1 BLOBIMPORT_RC="$?" if [[ $BLOBIMPORT_RC -ne 0 ]]; then cat "$TESTTMP/blobimport.out" # set exit code, otherwise previous cat sets it to 0 return "$BLOBIMPORT_RC" fi } function bonsai_verify { GLOG_minloglevel=5 "$MONONOKE_BONSAI_VERIFY" --repo-id "$REPOID" \ --mononoke-config-path "$TESTTMP/mononoke-config" "${CACHING_ARGS[@]}" "$@" } function lfs_import { GLOG_minloglevel=5 "$MONONOKE_LFS_IMPORT" --repo-id "$REPOID" \ --mononoke-config-path "$TESTTMP/mononoke-config" "${CACHING_ARGS[@]}" "$@" } function setup_no_ssl_apiserver { APISERVER_PORT=$(get_free_socket) no_ssl_apiserver --http-host "127.0.0.1" --http-port "$APISERVER_PORT" wait_for_apiserver --no-ssl } function apiserver { GLOG_minloglevel=5 "$MONONOKE_APISERVER" "$@" \ --mononoke-config-path "$TESTTMP/mononoke-config" \ --without-skiplist \ --ssl-ca "$TEST_CERTDIR/root-ca.crt" \ --ssl-private-key "$TEST_CERTDIR/localhost.key" \ --ssl-certificate "$TEST_CERTDIR/localhost.crt" \ --ssl-ticket-seeds "$TEST_CERTDIR/server.pem.seeds" \ "${CACHING_ARGS[@]}" >> "$TESTTMP/apiserver.out" 2>&1 & export APISERVER_PID=$! echo "$APISERVER_PID" >> "$DAEMON_PIDS" } function no_ssl_apiserver { GLOG_minloglevel=5 "$MONONOKE_APISERVER" "$@" \ --without-skiplist \ --mononoke-config-path "$TESTTMP/mononoke-config" \ "${CACHING_ARGS[@]}" >> "$TESTTMP/apiserver.out" 2>&1 & echo $! >> "$DAEMON_PIDS" } function wait_for_apiserver { for _ in $(seq 1 200); do if [[ -a "$TESTTMP/apiserver.out" ]]; then PORT=$(grep "Listening to" < "$TESTTMP/apiserver.out" | grep -Pzo "(\\d+)\$") && break fi sleep 0.1 done if [[ -z "$PORT" ]]; then echo "error: Mononoke API Server is not started" cat "$TESTTMP/apiserver.out" exit 1 fi export APIHOST="localhost:$PORT" export APISERVER APISERVER="https://localhost:$PORT" if [[ ($# -eq 1 && "$1" == "--no-ssl") ]]; then APISERVER="http://localhost:$PORT" fi } function lfs_server { local port uri log opts args proto poll lfs_server_pid port="$(get_free_socket)" log="${TESTTMP}/lfs_server.${port}" proto="http" poll="curl" opts=( "${CACHING_ARGS[@]}" --mononoke-config-path "$TESTTMP/mononoke-config" --listen-host 127.0.0.1 --listen-port "$port" ) args=() while [[ "$#" -gt 0 ]]; do if [[ "$1" = "--upstream" ]]; then shift args=("${args[@]}" "$1") shift elif [[ "$1" = "--live-config" ]]; then opts=("${opts[@]}" "$1" "$2") shift shift elif [[ "$1" = "--tls" ]]; then shift proto="https" poll="sslcurl" opts=( "${opts[@]}" --tls-ca "$TEST_CERTDIR/root-ca.crt" --tls-private-key "$TEST_CERTDIR/localhost.key" --tls-certificate "$TEST_CERTDIR/localhost.crt" --tls-ticket-seeds "$TEST_CERTDIR/server.pem.seeds" ) shift elif [[ "$1" = "--always-wait-for-upstream" ]]; then opts=("${opts[@]}" "$1") shift elif [[ "$1" = "--scuba-log-file" ]]; then opts=("${opts[@]}" "$1" "$2") shift shift elif [[ "$1" = "--log" ]]; then shift log="$1" shift else echo "invalid argument: $1" >&2 return 1 fi done uri="${proto}://localhost:${port}" echo "$uri" GLOG_minloglevel=5 "$LFS_SERVER" \ "${opts[@]}" "$uri" "${args[@]}" >> "$log" 2>&1 & lfs_server_pid="$!" echo "$lfs_server_pid" >> "$DAEMON_PIDS" for _ in $(seq 1 200); do if "$poll" "${uri}/health_check" >/dev/null 2>&1; then truncate -s 0 "$log" return 0 fi sleep 0.1 done echo "lfs_server did not start:" >&2 cat "$log" >&2 return 1 } function extract_json_error { input=$(< /dev/stdin) echo "$input" | head -1 | jq -r '.message' echo "$input" | tail -n +2 } # Run an hg binary configured with the settings required to talk to Mononoke. function hgmn { hg --config ui.ssh="$DUMMYSSH" --config paths.default=ssh://user@dummy/$REPONAME --config ui.remotecmd="$MONONOKE_HGCLI" "$@" } function hgmn_show { echo "LOG $*" hgmn log --template 'node:\t{node}\np1node:\t{p1node}\np2node:\t{p2node}\nauthor:\t{author}\ndate:\t{date}\ndesc:\t{desc}\n\n{diff()}' -r "$@" hgmn update "$@" echo echo "CONTENT $*" find . -type f -not -path "./.hg/*" -print -exec cat {} \; } function hginit_treemanifest() { hg init "$@" cat >> "$1"/.hg/hgrc <> "$2"/.hg/hgrc <> "$1"/.hg/hgrc <> "$2"/.hg/hgrc <> .hg/hgrc <> .hg/hgrc <> .hg/hgrc <> "repos/$REPONAME/server.toml" <> "$HGRCPATH" <> .hg/hgrc <> .hg/hgrc < "$1" hg add "$1" hg ci -m "$1" } function pushrebase_replay() { DB_INDICES=$1 REPLAY_CA_PEM="$TEST_CERTDIR/root-ca.crt" \ THRIFT_TLS_CL_CERT_PATH="$TEST_CERTDIR/localhost.crt" \ THRIFT_TLS_CL_KEY_PATH="$TEST_CERTDIR/localhost.key" \ GLOG_minloglevel=5 "$PUSHREBASE_REPLAY" \ --mononoke-config-path "$TESTTMP/mononoke-config" \ --reponame repo \ --hgcli "$MONONOKE_HGCLI" \ --mononoke-admin "$MONONOKE_ADMIN" \ --mononoke-address "[::1]:$MONONOKE_SOCKET" \ --mononoke-server-common-name localhost \ --db-indices "$DB_INDICES" \ --repo-id $REPOID \ --bundle-provider filesystem \ --filesystem-bundles-storage-path "$TESTTMP" \ --sqlite3-path "$TESTTMP/pushrebaserecording" \ "${CACHING_ARGS[@]}" } function enable_replay_verification_hook { cat >> "$TESTTMP"/replayverification.py < hg sync job we need a way to # quickly disable the replay verification to let unsynced bundles # through. # Disable this hook by placing a file in the .hg directory. if repo.localvfs.exists('REPLAY_BYPASS'): ui.note("[ReplayVerification] Bypassing check as override file is present\n") return 0 if expected_book is None and expected_head is None: # We are allowing non-unbundle-replay pushes to go through return 0 if allowed_replay_books and actual_book not in allowed_replay_books: ui.warn("[ReplayVerification] only allowed to unbundlereplay on %r\n" % (allowed_replay_books, )) return 1 expected_head = expected_head or None actual_head = actual_head or None if expected_book == actual_book and expected_head == actual_head: ui.note("[ReplayVerification] Everything seems in order\n") return 0 ui.warn("[ReplayVerification] Expected: (%s, %s). Actual: (%s, %s)\n" % (expected_book, expected_head, actual_book, actual_head)) return 1 EOF cat >> "$TESTTMP"/repo_lock.py << EOF def run(*args, **kwargs): """Repo is locked for everything except replays In-process style hook.""" if kwargs.get("EXPECTED_ONTOBOOK"): return 0 print "[RepoLock] Repo locked for non-unbundlereplay pushes" return 1 EOF [[ -f .hg/hgrc ]] || echo ".hg/hgrc does not exists!" cat >>.hg/hgrc </dev/null </dev/null </dev/null)" if [[ -n "$ret" ]]; then echo "$ret" return 0 fi sleep 0.1 done return 1 }