summary refs log tree commit diff
path: root/scripts-dev/complement.sh
blob: 24b83cfeb6057d492147588530a0e9a872254035 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
#!/usr/bin/env bash
# This script is designed for developers who want to test their code
# against Complement.
#
# It makes a Synapse image which represents the current checkout,
# builds a synapse-complement image on top, then runs tests with it.
#
# By default the script will fetch the latest Complement main branch and
# run tests with that. This can be overridden to use a custom Complement
# checkout by setting the COMPLEMENT_DIR environment variable to the
# filepath of a local Complement checkout or by setting the COMPLEMENT_REF
# environment variable to pull a different branch or commit.
#
# To use the 'podman' command instead 'docker', set the PODMAN environment
# variable. Example:
#
# PODMAN=1 ./complement.sh
#
# By default Synapse is run in monolith mode. This can be overridden by
# setting the WORKERS environment variable.
#
# You can optionally give a "-f" argument (for "fast") before any to skip
# rebuilding the docker images, if you just want to rerun the tests.
#
# Remaining commandline arguments are passed through to `go test`. For example,
# you can supply a regular expression of test method names via the "-run"
# argument:
#
# ./complement.sh -run "TestOutboundFederation(Profile|Send)"
#
# Specifying TEST_ONLY_SKIP_DEP_HASH_VERIFICATION=1 will cause `poetry export`
# to not emit any hashes when building the Docker image. This then means that
# you can use 'unverifiable' sources such as git repositories as dependencies.

# Exit if a line returns a non-zero exit code
set -e

# Helper to emit annotations that collapse portions of the log in GitHub Actions
echo_if_github() {
  if [[ -n "$GITHUB_WORKFLOW" ]]; then
    echo $*
  fi
}

# Helper to print out the usage instructions
usage() {
    cat >&2 <<EOF
Usage: $0 [-f] <go test arguments>...
Run the complement test suite on Synapse.

  -f, --fast
        Skip rebuilding the docker images, and just use the most recent
        'complement-synapse:latest' image.
        Conflicts with --build-only.

  --build-only
        Only build the Docker images. Don't actually run Complement.
        Conflicts with -f/--fast.

  -e, --editable
        Use an editable build of Synapse, rebuilding the image if necessary.
        This is suitable for use in development where a fast turn-around time
        is important.
        Not suitable for use in CI in case the editable environment is impure.

  --rebuild-editable
        Force a rebuild of the editable build of Synapse.
        This is occasionally useful if the built-in rebuild detection with
        --editable fails, e.g. when changing configure_workers_and_start.py.

For help on arguments to 'go test', run 'go help testflag'.
EOF
}

# parse our arguments
skip_docker_build=""
skip_complement_run=""
while [ $# -ge 1 ]; do
    arg=$1
    case "$arg" in
        "-h")
            usage
            exit 1
            ;;
        "-f"|"--fast")
            skip_docker_build=1
            ;;
        "--build-only")
            skip_complement_run=1
            ;;
        "-e"|"--editable")
            use_editable_synapse=1
            ;;
        "--rebuild-editable")
            rebuild_editable_synapse=1
            ;;
        *)
            # unknown arg: presumably an argument to gotest. break the loop.
            break
    esac
    shift
done

# enable buildkit for the docker builds
export DOCKER_BUILDKIT=1

# Determine whether to use the docker or podman container runtime.
if [ -n "$PODMAN" ]; then
  export CONTAINER_RUNTIME=podman
  export DOCKER_HOST=unix://$XDG_RUNTIME_DIR/podman/podman.sock
  export BUILDAH_FORMAT=docker
  export COMPLEMENT_HOSTNAME_RUNNING_COMPLEMENT=host.containers.internal
else
  export CONTAINER_RUNTIME=docker
fi

# Change to the repository root
cd "$(dirname $0)/.."

# Check for a user-specified Complement checkout
if [[ -z "$COMPLEMENT_DIR" ]]; then
  COMPLEMENT_REF=${COMPLEMENT_REF:-main}
  echo "COMPLEMENT_DIR not set. Fetching Complement checkout from ${COMPLEMENT_REF}..."
  wget -Nq https://github.com/matrix-org/complement/archive/${COMPLEMENT_REF}.tar.gz
  tar -xzf ${COMPLEMENT_REF}.tar.gz
  COMPLEMENT_DIR=complement-${COMPLEMENT_REF}
  echo "Checkout available at 'complement-${COMPLEMENT_REF}'"
fi

if [ -n "$use_editable_synapse" ]; then
    if [[ -e synapse/synapse_rust.abi3.so ]]; then
        # In an editable install, back up the host's compiled Rust module to prevent
        # inconvenience; the container will overwrite the module with its own copy.
        mv -n synapse/synapse_rust.abi3.so synapse/synapse_rust.abi3.so~host
        # And restore it on exit:
        synapse_pkg=`realpath synapse`
        trap "mv -f '$synapse_pkg/synapse_rust.abi3.so~host' '$synapse_pkg/synapse_rust.abi3.so'" EXIT
    fi

    editable_mount="$(realpath .):/editable-src:z"
    if [ -n "$rebuild_editable_synapse" ]; then
        unset skip_docker_build
    elif $CONTAINER_RUNTIME inspect complement-synapse-editable &>/dev/null; then
        # complement-synapse-editable already exists: see if we can still use it:
        # - The Rust module must still be importable; it will fail to import if the Rust source has changed.
        # - The Poetry lock file must be the same (otherwise we assume dependencies have changed)

        # First set up the module in the right place for an editable installation.
        $CONTAINER_RUNTIME run --rm -v $editable_mount --entrypoint 'cp' complement-synapse-editable -- /synapse_rust.abi3.so.bak /editable-src/synapse/synapse_rust.abi3.so

        if ($CONTAINER_RUNTIME run --rm -v $editable_mount --entrypoint 'python' complement-synapse-editable -c 'import synapse.synapse_rust' \
            && $CONTAINER_RUNTIME run --rm -v $editable_mount --entrypoint 'diff' complement-synapse-editable --brief /editable-src/poetry.lock /poetry.lock.bak); then
            skip_docker_build=1
        else
            echo "Editable Synapse image is stale. Will rebuild."
            unset skip_docker_build
        fi
    fi
fi

if [ -z "$skip_docker_build" ]; then
    if [ -n "$use_editable_synapse" ]; then

        # Build a special image designed for use in development with editable
        # installs.
        $CONTAINER_RUNTIME build -t synapse-editable \
            -f "docker/editable.Dockerfile" .

        $CONTAINER_RUNTIME build -t synapse-workers-editable \
            --build-arg FROM=synapse-editable \
            -f "docker/Dockerfile-workers" .

        $CONTAINER_RUNTIME build -t complement-synapse-editable \
            --build-arg FROM=synapse-workers-editable \
            -f "docker/complement/Dockerfile" "docker/complement"

        # Prepare the Rust module
        $CONTAINER_RUNTIME run --rm -v $editable_mount --entrypoint 'cp' complement-synapse-editable -- /synapse_rust.abi3.so.bak /editable-src/synapse/synapse_rust.abi3.so

    else

        # Build the base Synapse image from the local checkout
        echo_if_github "::group::Build Docker image: matrixdotorg/synapse"
        $CONTAINER_RUNTIME build -t matrixdotorg/synapse \
        --build-arg TEST_ONLY_SKIP_DEP_HASH_VERIFICATION \
        --build-arg TEST_ONLY_IGNORE_POETRY_LOCKFILE \
        -f "docker/Dockerfile" .
        echo_if_github "::endgroup::"

        # Build the workers docker image (from the base Synapse image we just built).
        echo_if_github "::group::Build Docker image: matrixdotorg/synapse-workers"
        $CONTAINER_RUNTIME build -t matrixdotorg/synapse-workers -f "docker/Dockerfile-workers" .
        echo_if_github "::endgroup::"

        # Build the unified Complement image (from the worker Synapse image we just built).
        echo_if_github "::group::Build Docker image: complement/Dockerfile"
        $CONTAINER_RUNTIME build -t complement-synapse \
            -f "docker/complement/Dockerfile" "docker/complement"
        echo_if_github "::endgroup::"

    fi
fi

if [ -n "$skip_complement_run" ]; then
    echo "Skipping Complement run as requested."
    exit
fi

export COMPLEMENT_BASE_IMAGE=complement-synapse
if [ -n "$use_editable_synapse" ]; then
    export COMPLEMENT_BASE_IMAGE=complement-synapse-editable
    export COMPLEMENT_HOST_MOUNTS="$editable_mount"
fi

extra_test_args=()

test_tags="synapse_blacklist,msc3787,msc3874,msc3890,msc3391,msc3930,faster_joins"

# All environment variables starting with PASS_ will be shared.
# (The prefix is stripped off before reaching the container.)
export COMPLEMENT_SHARE_ENV_PREFIX=PASS_

# It takes longer than 10m to run the whole suite.
extra_test_args+=("-timeout=60m")

if [[ -n "$WORKERS" ]]; then
  # Use workers.
  export PASS_SYNAPSE_COMPLEMENT_USE_WORKERS=true

  # Pass through the workers defined. If none, it will be an empty string
  export PASS_SYNAPSE_WORKER_TYPES="$WORKER_TYPES"

  # Workers can only use Postgres as a database.
  export PASS_SYNAPSE_COMPLEMENT_DATABASE=postgres

  # And provide some more configuration to complement.

  # It can take quite a while to spin up a worker-mode Synapse for the first
  # time (the main problem is that we start 14 python processes for each test,
  # and complement likes to do two of them in parallel).
  export COMPLEMENT_SPAWN_HS_TIMEOUT_SECS=120
else
  export PASS_SYNAPSE_COMPLEMENT_USE_WORKERS=
  if [[ -n "$POSTGRES" ]]; then
    export PASS_SYNAPSE_COMPLEMENT_DATABASE=postgres
  else
    export PASS_SYNAPSE_COMPLEMENT_DATABASE=sqlite
  fi
fi

if [[ -n "$ASYNCIO_REACTOR" ]]; then
  # Enable the Twisted asyncio reactor
  export PASS_SYNAPSE_COMPLEMENT_USE_ASYNCIO_REACTOR=true
fi


if [[ -n "$SYNAPSE_TEST_LOG_LEVEL" ]]; then
  # Set the log level to what is desired
  export PASS_SYNAPSE_LOG_LEVEL="$SYNAPSE_TEST_LOG_LEVEL"

  # Allow logging sensitive things (currently SQL queries & parameters).
  # (This won't have any effect if we're not logging at DEBUG level overall.)
  # Since this is just a test suite, this is fine and won't reveal anyone's
  # personal information
  export PASS_SYNAPSE_LOG_SENSITIVE=1
fi

# Log a few more useful things for a developer attempting to debug something
# particularly tricky.
export PASS_SYNAPSE_LOG_TESTING=1

# Run the tests!
echo "Images built; running complement"
cd "$COMPLEMENT_DIR"

go test -v -tags $test_tags -count=1 "${extra_test_args[@]}" "$@" ./tests/...