diff --git a/.github/workflows/integration_test.yaml b/.github/workflows/integration_test.yaml index 7cc6d6a..51bdbab 100644 --- a/.github/workflows/integration_test.yaml +++ b/.github/workflows/integration_test.yaml @@ -15,6 +15,7 @@ jobs: - integration-scaling - integration-upgrades - integration-auth + - integration-server-upgrade runs-on: ubuntu-latest steps: - name: Checkout diff --git a/documentation/how-to/upgrade-server.md b/documentation/how-to/upgrade-server.md index 58e7fd8..434c4da 100644 --- a/documentation/how-to/upgrade-server.md +++ b/documentation/how-to/upgrade-server.md @@ -33,8 +33,8 @@ The Temporal K8s charms facilitate server upgrades in the following way: This will ensure that your database schema is updated if any updates are available. -2. The Temporal K8s charm should be updated to the next charm revision - that you currently have deployed as follows: +2. The Temporal K8s charm should be updated to the next charm revision that you + currently have deployed as follows: ```bash juju refresh temporal-k8s --revision= @@ -44,3 +44,14 @@ _Warning: It is essential that upgrades are done one consecutive revision at a time. Charmed Temporal K8s can only guarantee backward compatibility between two consecutive revisions in line with the upgrade system adopted by the Temporal Server._ + +## Appendix + +The table below shows a mapping between the Temporal K8s charms and the Temporal +server versions. It can be used as a reference for upgrading your charm +revisions in line with the Temporal server version so as to avoid any breaking +changes. + +| Temporal Server Charm Revision | Temporal Admin Charm Revision | Temporal Server Version | +| :----------------------------: | :---------------------------: | :---------------------: | +| 20-21 | 8-9 | v1.21.5 | diff --git a/templates/config.jinja b/templates/config.jinja index bdb765b..c838fb5 100644 --- a/templates/config.jinja +++ b/templates/config.jinja @@ -291,5 +291,5 @@ additionalEnvs: {%- endif %} dynamicConfigClient: - filepath: "{{ DYNAMIC_CONFIG_FILE_PATH | default("/etc/temporal/config/dynamicconfig/development.yaml") }}" + filepath: "{{ DYNAMIC_CONFIG_FILE_PATH | default("/etc/temporal/config/dynamicconfig/docker.yaml") }}" pollInterval: "60s" diff --git a/tests/integration/test_server_upgrade.py b/tests/integration/test_server_upgrade.py new file mode 100644 index 0000000..dd1e304 --- /dev/null +++ b/tests/integration/test_server_upgrade.py @@ -0,0 +1,103 @@ +# Copyright 2024 Canonical Ltd. +# See LICENSE file for licensing details. + +"""Temporal charm upgrades integration tests.""" + +import asyncio +import logging +import time + +import pytest +import pytest_asyncio +from helpers import ( + APP_NAME, + APP_NAME_ADMIN, + APP_NAME_UI, + create_default_namespace, + perform_temporal_integrations, + run_sample_workflow, +) +from pytest_operator.plugin import OpsTest + +logger = logging.getLogger(__name__) + + +@pytest.mark.skip_if_deployed +@pytest_asyncio.fixture(name="deploy", scope="module") +async def deploy(ops_test: OpsTest): + """The app is up and running.""" + await ops_test.model.set_config({"update-status-hook-interval": "1m"}) + charm = await ops_test.build_charm(".") + + # Deploy Temporal server, Temporal admin, Temporal UI and postgresql charms. + asyncio.gather( + ops_test.model.deploy( + charm, + application_name=APP_NAME, + resources={"temporal-server-image": "temporalio/server:1.20.0"}, + config={"num-history-shards": "1"}, + ), + ops_test.model.deploy( + APP_NAME_ADMIN, channel="edge", resources={"temporal-admin-image": "temporalio/admin-tools:1.20.0"} + ), + ops_test.model.deploy(APP_NAME_UI, channel="edge"), + ops_test.model.deploy("postgresql-k8s", channel="14/stable", trust=True), + ) + + async with ops_test.fast_forward(): + await ops_test.model.wait_for_idle( + apps=[APP_NAME, APP_NAME_ADMIN, APP_NAME_UI], status="blocked", raise_on_blocked=False, timeout=600 + ) + await ops_test.model.wait_for_idle( + apps=["postgresql-k8s"], status="active", raise_on_blocked=False, timeout=600 + ) + + await perform_temporal_integrations(ops_test) + + await create_default_namespace(ops_test) + + await ops_test.model.wait_for_idle(apps=[APP_NAME], status="active", raise_on_blocked=False, timeout=300) + assert ops_test.model.applications[APP_NAME].units[0].workload_status == "active" + assert ops_test.model.applications[APP_NAME_UI].units[0].workload_status == "active" + await run_sample_workflow(ops_test) + + +@pytest.mark.abort_on_fail +@pytest.mark.usefixtures("deploy") +class TestServerUpgrade: + """Integration test for Temporal server upgrade requiring schema update. + + This test ensures that upgrading from v1.20.0 to v1.21.2 (which requires a schema update) runs + successfully on the newly built charm. + """ + + async def test_server_upgrade(self, ops_test: OpsTest): + """Refresh the charm with a new resource which requires a schema update.""" + # Update admin charm to v1.21.2 first + await ops_test.model.applications[APP_NAME_ADMIN].refresh( + resources={"temporal-admin-image": "temporalio/admin-tools:1.21.2"}, + ) + await ops_test.model.wait_for_idle( + apps=[APP_NAME_ADMIN], raise_on_error=False, status="active", raise_on_blocked=False, timeout=600 + ) + + # Needed for a local charm refresh + charm = await ops_test.build_charm(".") + + # Update server charm to v1.21.2 + await ops_test.model.applications[APP_NAME].refresh( + resources={"temporal-server-image": "temporalio/server:1.21.2"}, + path=str(charm), + ) + + # This is to accmmodate for a self-resolving error which sometimes appears when Temporal + # services attempt to connect to the cluster before the application is ready. + await ops_test.model.wait_for_idle( + apps=[APP_NAME], raise_on_error=False, status="active", raise_on_blocked=False, timeout=600 + ) + time.sleep(10) + + assert ops_test.model.applications[APP_NAME].units[0].workload_status == "active" + assert ops_test.model.applications[APP_NAME_ADMIN].units[0].workload_status == "active" + + await run_sample_workflow(ops_test) diff --git a/tests/integration/test_upgrades.py b/tests/integration/test_upgrades.py index 46daf19..0d74f96 100644 --- a/tests/integration/test_upgrades.py +++ b/tests/integration/test_upgrades.py @@ -29,10 +29,10 @@ async def deploy(ops_test: OpsTest): """The app is up and running.""" # Deploy temporal server, temporal admin and postgresql charms. - await ops_test.model.deploy(APP_NAME, channel="edge") + await ops_test.model.deploy(APP_NAME, channel="edge", config={"num-history-shards": 1}) await ops_test.model.deploy(APP_NAME_ADMIN, channel="edge") await ops_test.model.deploy(APP_NAME_UI, channel="edge") - await ops_test.model.deploy("postgresql-k8s", channel="14", trust=True) + await ops_test.model.deploy("postgresql-k8s", channel="14/stable", trust=True) async with ops_test.fast_forward(): await ops_test.model.wait_for_idle( @@ -66,9 +66,6 @@ async def test_upgrade(self, ops_test: OpsTest): # This is to accmmodate for a self-resolving error which sometimes appears when Temporal # services attempt to connect to the cluster before the application is ready. await ops_test.model.applications[APP_NAME].refresh(path=str(charm), resources=resources) - await ops_test.model.applications[APP_NAME].set_config( - {"num-history-shards": "1"}, - ) await ops_test.model.wait_for_idle( apps=[APP_NAME], raise_on_error=False, status="active", raise_on_blocked=False, timeout=600 diff --git a/tox.ini b/tox.ini index 334fb80..7e1ea1e 100644 --- a/tox.ini +++ b/tox.ini @@ -154,3 +154,16 @@ deps = -r{toxinidir}/requirements.txt commands = pytest {[vars]tst_path}integration/test_auth.py -v --tb native --ignore={[vars]tst_path}unit --log-cli-level=INFO -s {posargs} --destructive-mode + +[testenv:integration-server-upgrade] +description = Run integration server upgrade testes +deps = + ipdb==0.13.9 + juju==3.3.0.0 + pytest==7.1.3 + pytest-operator==0.31.1 + temporalio==1.1.0 + pytest-asyncio==0.21 + -r{toxinidir}/requirements.txt +commands = + pytest {[vars]tst_path}integration/test_server_upgrade.py -v --tb native --ignore={[vars]tst_path}unit --log-cli-level=INFO -s {posargs} --destructive-mode