# HG changeset patch # User Alain Mazy # Date 1705596414 -3600 # Node ID f3475c3e42e5f0ac5fce83b72bce08a45c4ae781 # Parent b9ae7c59fee9d8a54c09f74d097a77549ceb11b2 run integ tests after a PG downgrade diff -r b9ae7c59fee9 -r f3475c3e42e5 NewTests/PostgresUpgrades/docker-compose.yml --- a/NewTests/PostgresUpgrades/docker-compose.yml Wed Jan 10 15:19:46 2024 +0100 +++ b/NewTests/PostgresUpgrades/docker-compose.yml Thu Jan 18 17:46:54 2024 +0100 @@ -15,7 +15,53 @@ PG_LOCK: "false" PG_INDEX_ENABLED: "true" AC_AUTHENTICATION_ENABLED: "false" - + + # Orthanc previous version + orthanc-pg-15-61: + image: osimis/orthanc:24.1.2 + container_name: orthanc-pg-15-61 + depends_on: [pg-15] + restart: unless-stopped + ports: ["8052:8042"] + volumes: ["storage-orthanc-pg-15:/var/lib/orthanc/db"] + environment: + VERBOSE_ENABLED: "true" + PG_HOST: "pg-15" + PG_LOCK: "false" + PG_INDEX_ENABLED: "true" + AC_AUTHENTICATION_ENABLED: "false" + + # Orthanc previous version to run the integration tests + orthanc-pg-15-61-for-integ-tests: + image: osimis/orthanc:24.1.2 + container_name: orthanc-pg-15-61-for-integ-tests + depends_on: [pg-15] + restart: unless-stopped + ports: ["8053:8042"] + volumes: + - "storage-orthanc-pg-15:/var/lib/orthanc/db" + - "./orthanc-for-integ-tests.json:/etc/orthanc/orthanc.json" + - ./docker-entrypoint-integ-tests.sh:/docker-entrypoint-integ-tests.sh + entrypoint: /docker-entrypoint-integ-tests.sh + + environment: + VERBOSE_ENABLED: "true" + PG_HOST: "pg-15" + PG_LOCK: "false" + PG_INDEX_ENABLED: "true" + AC_AUTHENTICATION_ENABLED: "false" + + orthanc-tests: + image: jodogne/orthanc-tests + container_name: orthanc-tests + depends_on: + - orthanc-pg-15-61-for-integ-tests + volumes: + - ../../:/tests/orthanc-tests + - ./wait-for-it.sh:/scripts/wait-for-it.sh + - ./run-integ-tests-from-docker.sh:/scripts/run-integ-tests-from-docker.sh + entrypoint: /scripts/run-integ-tests-from-docker.sh + # Orthanc latest version orthanc-pg-15-under-tests: image: ${ORTHANC_IMAGE_UNDER_TESTS:-osimis/orthanc:latest} @@ -39,7 +85,10 @@ container_name: pg-15 restart: unless-stopped ports: ["5439:5432"] - volumes: ["storage-pg-15:/var/lib/postgresql/data"] + volumes: + - "storage-pg-15:/var/lib/postgresql/data" + - "./downgrade.sh:/scripts/downgrade.sh" + - "./downgrade.sql:/scripts/downgrade.sql" environment: POSTGRES_HOST_AUTH_METHOD: "trust" healthcheck: diff -r b9ae7c59fee9 -r f3475c3e42e5 NewTests/PostgresUpgrades/docker-entrypoint-integ-tests.sh --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/NewTests/PostgresUpgrades/docker-entrypoint-integ-tests.sh Thu Jan 18 17:46:54 2024 +0100 @@ -0,0 +1,7 @@ +#!/bin/bash + +set -ex + +apt-get -y update +apt-get install -y libgdcm-tools +/docker-entrypoint.sh /tmp/orthanc.json diff -r b9ae7c59fee9 -r f3475c3e42e5 NewTests/PostgresUpgrades/downgrade.sh --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/NewTests/PostgresUpgrades/downgrade.sh Thu Jan 18 17:46:54 2024 +0100 @@ -0,0 +1,11 @@ +#!/bin/bash + +pushd /scripts + +# TODO: change pg-transactions by the plugin version number ! +apt-get update && apt-get install -y wget && wget https://orthanc.uclouvain.be/hg/orthanc-databases/raw-file/pg-transactions/PostgreSQL/Plugins/SQL/Downgrades/V6.2ToV6.1.sql +psql -U postgres -f V6.2ToV6.1.sql + +# if you want to test a downgrade procedure, you may use this code ... +# psql -U postgres -f downgrade.sql +popd \ No newline at end of file diff -r b9ae7c59fee9 -r f3475c3e42e5 NewTests/PostgresUpgrades/downgrade.sql --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/NewTests/PostgresUpgrades/downgrade.sql Thu Jan 18 17:46:54 2024 +0100 @@ -0,0 +1,1 @@ +-- you may paste here a test downgrade procedure to avoid rebuilding the docker image ... check downgrade.sh \ No newline at end of file diff -r b9ae7c59fee9 -r f3475c3e42e5 NewTests/PostgresUpgrades/orthanc-for-integ-tests.json --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/NewTests/PostgresUpgrades/orthanc-for-integ-tests.json Thu Jan 18 17:46:54 2024 +0100 @@ -0,0 +1,194 @@ +// how to obtain this file: +// in orthanc-tests: generate the configuration file with python2 ./GenerateConfigurationForTests.py +// then, paste it here and: +// - remove the Plugins section +// - remove the PostgreSQL/MySQL/Odbc sections +// - update the IP of OrthancPeers and DicomModalities: replace localhost by orthanc-tests for the first item +// - update the Worklists.Database to "/worklists" +// - update the StorageDirectory to "/var/lib/orthand/db/" +{ + "AcceptedTransferSyntaxes": [ + "1.2.840.10008.1.*" + ], + "AllowFindSopClassesInStudy": false, + "AuthenticationEnabled": true, + "BuiltinDecoderTranscoderOrder": "After", + "CaseSensitivePN": false, + "CheckRevisions": true, + "ConcurrentJobs": 2, + "DefaultEncoding": "Utf8", + "DefaultPrivateCreator": "Lunit", + "DeidentifyLogs": true, + "DicomAet": "ORTHANC", + "DicomAlwaysAllowEcho": true, + "DicomAlwaysAllowFind": false, + "DicomAlwaysAllowFindWorklist": false, + "DicomAlwaysAllowGet": false, + "DicomAlwaysAllowMove": false, + "DicomAlwaysAllowStore": true, + "DicomAssociationCloseDelay": 0, + "DicomCheckCalledAet": false, + "DicomCheckModalityHost": false, + "DicomEchoChecksFind": false, + "DicomLossyTranscodingQuality": 90, + "DicomModalities": { + "orthanctest": [ + "ORTHANCTEST", + "orthanc-tests", + 5001 + ], + "self": [ + "ORTHANC", + "127.0.0.1", + 4242 + ] + }, + "DicomModalitiesInDatabase": false, + "DicomPort": 4242, + "DicomScpTimeout": 30, + "DicomScuPreferredTransferSyntax": "1.2.840.10008.1.2.1", + "DicomScuTimeout": 10, + "DicomServerEnabled": true, + "DicomThreadsCount": 4, + "DicomTlsEnabled": false, + "DicomTlsRemoteCertificateRequired": true, + "DicomWeb": { + "Servers": { + "sample": [ + "http://localhost:8042/dicom-web/", + "alice", + "orthanctest" + ] + } + }, + "Dictionary": { + "0009,0010": [ + "LO", + "Private data element", + 1, + 1, + "Lunit" + ], + "0009,1001": [ + "DS", + "Abnormality score", + 1, + 1, + "Lunit" + ], + "00e1,10c2": [ + "UI", + "PET-CT Multi Modality Name", + 1, + 1, + "ELSCINT1" + ], + "4321,1012": [ + "LO", + "RadioButton3", + 1, + 1, + "RadioLogic" + ], + "7053,1003": [ + "ST", + "Original Image Filename", + 1, + 1, + "Philips PET Private Group" + ] + }, + "ExecuteLuaEnabled": true, + "HttpCompressionEnabled": false, + "HttpDescribeErrors": true, + "HttpPort": 8042, + "HttpProxy": "", + "HttpRequestTimeout": 30, + "HttpServerEnabled": true, + "HttpThreadsCount": 50, + "HttpTimeout": 2, + "HttpVerbose": false, + "HttpsCACertificates": "/etc/ssl/certs/ca-certificates.crt", + "HttpsVerifyPeers": true, + "IndexDirectory": "/var/lib/orthanc/db/", + "IngestTranscodingOfCompressed": true, + "IngestTranscodingOfUncompressed": true, + "JobsHistorySize": 1000, + "LimitFindInstances": 0, + "LimitFindResults": 0, + "LoadPrivateDictionary": true, + "LogExportedResources": true, + "LuaScripts": [], + "MallocArenaMax": 5, + "MaximumPatientCount": 0, + "MaximumPduLength": 16384, + "MaximumStorageCacheSize": 128, + "MaximumStorageSize": 0, + "MediaArchiveSize": 1, + "MetricsEnabled": true, + "Name": "MyOrthanc", + "OrthancExplorerEnabled": true, + "OrthancPeers": { + "peer": [ + "http://orthanc-tests:5000/", + "alice", + "orthanctest" + ], + "self": { + "Password": "orthanctest", + "Url": "http://127.0.0.1:8042/", + "Username": "alice" + }, + "transfers-bidirectional": { + "Password": "orthanctest", + "RemoteSelf": "transfers-bidirectional", + "Url": "http://localhost:8042/", + "Username": "alice" + }, + "transfers-simple": { + "Password": "orthanctest", + "Url": "http://localhost:8042/", + "Username": "alice" + } + }, + "OrthancPeersInDatabase": false, + "OverwriteInstances": true, + "QueryRetrieveSize": 100, + "RegisteredUsers": { + "alice": "orthanctest" + }, + "RemoteAccessAllowed": true, + "SaveJobs": false, + "SslCertificate": "certificate.pem", + "SslEnabled": false, + "SslMinimumProtocolVersion": 4, + "SslTrustedClientCertificates": "trustedClientCertificates.pem", + "SslVerifyPeers": false, + "StableAge": 1, + "StorageAccessOnFind": "Always", + "StorageCommitmentReportsSize": 100, + "StorageCompression": false, + "StorageDirectory": "/var/lib/orthanc/db/", + "StoreDicom": true, + "StoreMD5ForAttachments": true, + "StrictAetComparison": false, + "SyncStorageArea": false, + "SynchronousCMove": false, + "SynchronousZipStream": true, + "TcpNoDelay": true, + "TranscodeDicomProtocol": true, + "UnknownSopClassAccepted": false, + "UserContentType": {}, + "UserMetadata": { + "my-metadata": 1098 + }, + "WebDavDeleteAllowed": true, + "WebDavEnabled": true, + "WebDavUploadAllowed": true, + "Worklists": { + "Database": "/worklists", + "Enable": true + }, + "ZipLoaderThreads": 0 + } + \ No newline at end of file diff -r b9ae7c59fee9 -r f3475c3e42e5 NewTests/PostgresUpgrades/run-integ-tests-from-docker.sh --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/NewTests/PostgresUpgrades/run-integ-tests-from-docker.sh Thu Jan 18 17:46:54 2024 +0100 @@ -0,0 +1,7 @@ +#!/bin/bash + +set -ex + +/scripts/wait-for-it.sh orthanc-pg-15-61-for-integ-tests:8042 -t 60 +# python /tests/orthanc-tests/Tests/Run.py --server=orthanc-pg-15-61-for-integ-tests --force --docker -- -v Orthanc.test_lua_deadlock +python /tests/orthanc-tests/Tests/Run.py --server=orthanc-pg-15-61-for-integ-tests --force --docker -- -v diff -r b9ae7c59fee9 -r f3475c3e42e5 NewTests/PostgresUpgrades/test_pg_upgrades.py --- a/NewTests/PostgresUpgrades/test_pg_upgrades.py Wed Jan 10 15:19:46 2024 +0100 +++ b/NewTests/PostgresUpgrades/test_pg_upgrades.py Thu Jan 18 17:46:54 2024 +0100 @@ -36,11 +36,22 @@ class TestPgUpgrades(unittest.TestCase): @classmethod - def setUpClass(cls): + def cleanup(cls): os.chdir(here) print("Cleaning old compose") subprocess.run(["docker", "compose", "down", "-v", "--remove-orphans"], check=True) + + @classmethod + def setUpClass(cls): + cls.cleanup() + + @classmethod + def tearDownClass(cls): + pass + # cls.cleanup() + + def test_upgrades_downgrades_with_pg_15(self): print("Launching PG-15 server") @@ -60,7 +71,11 @@ time.sleep(2) print("Launching newest Orthanc") - subprocess.run(["docker", "compose", "up", "orthanc-pg-15-under-tests", "-d"], check=True) + subprocess.run(["docker", "compose", "up", "orthanc-pg-15-under-tests", "-d"], + env= { + "ORTHANC_IMAGE_UNDER_TESTS": Helpers.orthanc_under_tests_docker_image + }, + check=True) o = OrthancApiClient("http://localhost:8050") o.wait_started() @@ -68,14 +83,47 @@ # make sure we can 'play' with Orthanc o.instances.get_tags(orthanc_id=instances[0]) o.instances.delete_all() + self.assertEqual(0, int(o.get_json('/statistics')['TotalDiskSize'])) instances = o.upload_folder(here / "../../Database/Knee") + size_before_downgrade = int(o.get_json('/statistics')['TotalDiskSize']) - print("Stopping new Orthanc ") + print("Stopping newest Orthanc ") subprocess.run(["docker", "compose", "stop", "orthanc-pg-15-under-tests"], check=True) time.sleep(2) - print("TODO Downgrading Orthanc DB and restart an Orthanc 23.12.1") - # ... + print("Downgrading Orthanc DB to v6.1") + subprocess.run(["docker", "exec", "pg-15", "./scripts/downgrade.sh"], check=True) + time.sleep(2) + + print("Downgrading Orthanc DB to v6.1") + print("Launching previous Orthanc (DB v6.1)") + subprocess.run(["docker", "compose", "up", "orthanc-pg-15-61", "-d"], check=True) + + o = OrthancApiClient("http://localhost:8052") + o.wait_started() + + # make sure we can 'play' with Orthanc + o.instances.get_tags(orthanc_id=instances[0]) + self.assertEqual(size_before_downgrade, int(o.get_json('/statistics')['TotalDiskSize'])) + o.instances.delete_all() + self.assertEqual(0, int(o.get_json('/statistics')['TotalDiskSize'])) + instances = o.upload_folder(here / "../../Database/Knee") + o.instances.delete_all() + self.assertEqual(0, int(o.get_json('/statistics')['TotalDiskSize'])) + + print("run the integration tests after a downgrade") + # first create the containers (orthanc-tests + orthanc-pg-15-61-for-integ-tests) so they know each other + # subprocess.run(["docker", "compose", "create", "orthanc-tests"], check=True) + + # subprocess.run(["docker", "compose", "up", "orthanc-pg-15-61-for-integ-tests", "-d"], check=True) + + # o = OrthancApiClient("http://localhost:8053", user="alice", pwd="orthanctest") + # o.wait_started() + + # time.sleep(10000) + subprocess.run(["docker", "compose", "up", "orthanc-tests"], check=True) + + def test_latest_orthanc_with_pg_9(self): print("Launching PG-9 server") diff -r b9ae7c59fee9 -r f3475c3e42e5 NewTests/PostgresUpgrades/wait-for-it.sh --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/NewTests/PostgresUpgrades/wait-for-it.sh Thu Jan 18 17:46:54 2024 +0100 @@ -0,0 +1,177 @@ +#!/usr/bin/env bash +# Use this script to test if a given TCP host/port are available + +cmdname=$(basename $0) + +echoerr() { if [[ $QUIET -ne 1 ]]; then echo "$@" 1>&2; fi } + +usage() +{ + cat << USAGE >&2 +Usage: + $cmdname host:port [-s] [-t timeout] [-- command args] + -h HOST | --host=HOST Host or IP under test + -p PORT | --port=PORT TCP port under test + Alternatively, you specify the host and port as host:port + -s | --strict Only execute subcommand if the test succeeds + -q | --quiet Don't output any status messages + -t TIMEOUT | --timeout=TIMEOUT + Timeout in seconds, zero for no timeout + -- COMMAND ARGS Execute command with args after the test finishes +USAGE + exit 1 +} + +wait_for() +{ + if [[ $TIMEOUT -gt 0 ]]; then + echoerr "$cmdname: waiting $TIMEOUT seconds for $HOST:$PORT" + else + echoerr "$cmdname: waiting for $HOST:$PORT without a timeout" + fi + start_ts=$(date +%s) + while : + do + if [[ $ISBUSY -eq 1 ]]; then + nc -z $HOST $PORT + result=$? + else + (echo > /dev/tcp/$HOST/$PORT) >/dev/null 2>&1 + result=$? + fi + if [[ $result -eq 0 ]]; then + end_ts=$(date +%s) + echoerr "$cmdname: $HOST:$PORT is available after $((end_ts - start_ts)) seconds" + break + fi + sleep 1 + done + return $result +} + +wait_for_wrapper() +{ + # In order to support SIGINT during timeout: http://unix.stackexchange.com/a/57692 + if [[ $QUIET -eq 1 ]]; then + timeout $BUSYTIMEFLAG $TIMEOUT $0 --quiet --child --host=$HOST --port=$PORT --timeout=$TIMEOUT & + else + timeout $BUSYTIMEFLAG $TIMEOUT $0 --child --host=$HOST --port=$PORT --timeout=$TIMEOUT & + fi + PID=$! + trap "kill -INT -$PID" INT + wait $PID + RESULT=$? + if [[ $RESULT -ne 0 ]]; then + echoerr "$cmdname: timeout occurred after waiting $TIMEOUT seconds for $HOST:$PORT" + fi + return $RESULT +} + +# process arguments +while [[ $# -gt 0 ]] +do + case "$1" in + *:* ) + hostport=(${1//:/ }) + HOST=${hostport[0]} + PORT=${hostport[1]} + shift 1 + ;; + --child) + CHILD=1 + shift 1 + ;; + -q | --quiet) + QUIET=1 + shift 1 + ;; + -s | --strict) + STRICT=1 + shift 1 + ;; + -h) + HOST="$2" + if [[ $HOST == "" ]]; then break; fi + shift 2 + ;; + --host=*) + HOST="${1#*=}" + shift 1 + ;; + -p) + PORT="$2" + if [[ $PORT == "" ]]; then break; fi + shift 2 + ;; + --port=*) + PORT="${1#*=}" + shift 1 + ;; + -t) + TIMEOUT="$2" + if [[ $TIMEOUT == "" ]]; then break; fi + shift 2 + ;; + --timeout=*) + TIMEOUT="${1#*=}" + shift 1 + ;; + --) + shift + CLI=("$@") + break + ;; + --help) + usage + ;; + *) + echoerr "Unknown argument: $1" + usage + ;; + esac +done + +if [[ "$HOST" == "" || "$PORT" == "" ]]; then + echoerr "Error: you need to provide a host and port to test." + usage +fi + +TIMEOUT=${TIMEOUT:-15} +STRICT=${STRICT:-0} +CHILD=${CHILD:-0} +QUIET=${QUIET:-0} + +# check to see if timeout is from busybox? +# check to see if timeout is from busybox? +TIMEOUT_PATH=$(realpath $(which timeout)) +if [[ $TIMEOUT_PATH =~ "busybox" ]]; then + ISBUSY=1 + BUSYTIMEFLAG="-t" +else + ISBUSY=0 + BUSYTIMEFLAG="" +fi + +if [[ $CHILD -gt 0 ]]; then + wait_for + RESULT=$? + exit $RESULT +else + if [[ $TIMEOUT -gt 0 ]]; then + wait_for_wrapper + RESULT=$? + else + wait_for + RESULT=$? + fi +fi + +if [[ $CLI != "" ]]; then + if [[ $RESULT -ne 0 && $STRICT -eq 1 ]]; then + echoerr "$cmdname: strict mode, refusing to execute subprocess" + exit $RESULT + fi + exec "${CLI[@]}" +else + exit $RESULT +fi \ No newline at end of file