Skip to content

Commit e743861

Browse files
Run integration tests on arm64 (#473)
1 parent 6821b27 commit e743861

File tree

9 files changed

+79
-21
lines changed

9 files changed

+79
-21
lines changed

.github/workflows/ci.yaml

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -67,21 +67,29 @@ jobs:
6767
juju:
6868
- agent: 2.9.49 # renovate: juju-agent-pin-minor
6969
libjuju: ^2
70-
allure: false
70+
allure_on_amd64: false
7171
- agent: 3.4.3 # renovate: juju-agent-pin-minor
72-
allure: true
73-
name: Integration test charm | ${{ matrix.juju.agent }}
72+
allure_on_amd64: true
73+
architecture:
74+
- amd64
75+
include:
76+
- juju:
77+
agent: 3.4.3 # renovate: juju-agent-pin-minor
78+
allure_on_amd64: true
79+
architecture: arm64
80+
name: Integration test charm | ${{ matrix.juju.agent }} | ${{ matrix.architecture }}
7481
needs:
7582
- lint
7683
- unit-test
7784
- build
7885
uses: canonical/data-platform-workflows/.github/workflows/[email protected]
7986
with:
8087
artifact-prefix: ${{ needs.build.outputs.artifact-prefix }}
88+
architecture: ${{ matrix.architecture }}
8189
cloud: lxd
8290
juju-agent-version: ${{ matrix.juju.agent }}
8391
libjuju-version-constraint: ${{ matrix.juju.libjuju }}
84-
_beta_allure_report: ${{ matrix.juju.allure }}
92+
_beta_allure_report: ${{ matrix.juju.allure_on_amd64 && matrix.architecture == 'amd64' }}
8593
secrets:
8694
# GitHub appears to redact each line of a multi-line secret
8795
# Avoid putting `{` or `}` on a line by itself so that it doesn't get redacted in logs

tests/integration/architecture.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# Copyright 2024 Canonical Ltd.
2+
# See LICENSE file for licensing details.
3+
import subprocess
4+
5+
architecture = subprocess.run(
6+
["dpkg", "--print-architecture"], capture_output=True, check=True, encoding="utf-8"
7+
).stdout.strip()

tests/integration/helpers.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -267,11 +267,14 @@ def is_relation_broken(ops_test: OpsTest, endpoint_one: str, endpoint_two: str)
267267

268268

269269
@retry(stop=stop_after_attempt(8), wait=wait_fixed(15), reraise=True)
270-
def is_connection_possible(credentials: Dict, **extra_opts) -> bool:
270+
def is_connection_possible(
271+
credentials: Dict, *, retry_if_not_possible=False, **extra_opts
272+
) -> bool:
271273
"""Test a connection to a MySQL server.
272274
273275
Args:
274276
credentials: A dictionary with the credentials to test
277+
retry_if_not_possible: Retry if connection not possible
275278
extra_opts: extra options for mysql connection
276279
"""
277280
config = {
@@ -289,6 +292,9 @@ def is_connection_possible(credentials: Dict, **extra_opts) -> bool:
289292
return cursor.fetchone()[0] == 1
290293
except (DatabaseError, InterfaceError, OperationalError, ProgrammingError):
291294
# Errors raised when the connection is not possible
295+
if retry_if_not_possible:
296+
# Retry
297+
raise
292298
return False
293299

294300

tests/integration/high_availability/test_async_replication.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55

66
import logging
7+
import subprocess
78
from asyncio import gather
89
from pathlib import Path
910
from time import sleep
@@ -14,7 +15,7 @@
1415
from juju.model import Model
1516
from pytest_operator.plugin import OpsTest
1617

17-
from .. import juju_
18+
from .. import architecture, juju_
1819
from ..helpers import execute_queries_on_unit, get_cluster_status, get_leader_unit
1920
from ..markers import juju3
2021
from .high_availability_helpers import DATABASE_NAME, TABLE_NAME
@@ -47,6 +48,11 @@ async def second_model(
4748
second_model_name = f"{first_model.info.name}-other"
4849
logger.info(f"Creating second model {second_model_name}")
4950
await ops_test._controller.add_model(second_model_name)
51+
subprocess.run(["juju", "switch", second_model_name], check=True)
52+
subprocess.run(
53+
["juju", "set-model-constraints", f"arch={architecture.architecture}"], check=True
54+
)
55+
subprocess.run(["juju", "switch", first_model.info.name], check=True)
5056
second_model = Model()
5157
await second_model.connect(model_name=second_model_name)
5258
yield second_model # pyright: ignore [reportReturnType]
@@ -180,7 +186,7 @@ async def test_deploy_router_and_app(first_model: Model) -> None:
180186
APPLICATION_APP_NAME,
181187
application_name=APPLICATION_APP_NAME,
182188
series="jammy",
183-
channel="latest/stable",
189+
channel="latest/edge",
184190
num_units=1,
185191
)
186192

tests/integration/high_availability/test_self_healing.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -272,7 +272,9 @@ async def test_replicate_data_on_restart(ops_test: OpsTest, continuous_writes):
272272
await ops_test.model.set_config({"update-status-hook-interval": "5m"})
273273

274274
# verify/wait availability
275-
assert is_connection_possible(config), "❌ Connection not possible after restart"
275+
assert is_connection_possible(
276+
config, retry_if_not_possible=True
277+
), "❌ Connection not possible after restart"
276278

277279
# read and verify data
278280
select_data_sql = [

tests/integration/high_availability/test_upgrade_from_stable.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
import pytest
88
from pytest_operator.plugin import OpsTest
99

10-
from .. import juju_
10+
from .. import juju_, markers
1111
from ..helpers import get_leader_unit, get_primary_unit_wrapper, retrieve_database_variable_value
1212
from .high_availability_helpers import (
1313
ensure_all_units_continuous_writes_incrementing,
@@ -23,6 +23,7 @@
2323

2424

2525
@pytest.mark.group(1)
26+
@markers.amd64_only # TODO: remove after arm64 stable release
2627
@pytest.mark.abort_on_fail
2728
async def test_deploy_stable(ops_test: OpsTest) -> None:
2829
"""Simple test to ensure that the mysql and application charms get deployed."""
@@ -53,6 +54,7 @@ async def test_deploy_stable(ops_test: OpsTest) -> None:
5354

5455

5556
@pytest.mark.group(1)
57+
@markers.amd64_only # TODO: remove after arm64 stable release
5658
@pytest.mark.abort_on_fail
5759
async def test_pre_upgrade_check(ops_test: OpsTest) -> None:
5860
"""Test that the pre-upgrade-check action runs successfully."""
@@ -77,6 +79,7 @@ async def test_pre_upgrade_check(ops_test: OpsTest) -> None:
7779

7880

7981
@pytest.mark.group(1)
82+
@markers.amd64_only # TODO: remove after arm64 stable release
8083
async def test_upgrade_from_stable(ops_test: OpsTest):
8184
"""Test updating from stable channel."""
8285
application = ops_test.model.applications[MYSQL_APP_NAME]

tests/integration/high_availability/test_upgrade_rollback_incompat.py

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
import pytest
1515
from pytest_operator.plugin import OpsTest
1616

17-
from .. import juju_
17+
from .. import juju_, markers
1818
from ..helpers import get_leader_unit, get_relation_data, get_unit_by_index
1919
from .high_availability_helpers import (
2020
ensure_all_units_continuous_writes_incrementing,
@@ -29,14 +29,17 @@
2929

3030

3131
@pytest.mark.group(1)
32+
# TODO: remove after next incompatible MySQL server version released in our snap
33+
# (details: https://github.com/canonical/mysql-operator/pull/472#discussion_r1659300069)
34+
@markers.amd64_only
3235
@pytest.mark.abort_on_fail
3336
async def test_build_and_deploy(ops_test: OpsTest) -> None:
3437
"""Simple test to ensure that the mysql and application charms get deployed."""
3538
snap_revisions = pathlib.Path("snap_revisions.json")
3639
with snap_revisions.open("r") as file:
3740
old_revisions: dict = json.load(file)
3841
new_revisions = old_revisions.copy()
39-
# TODO: mark as amd64 only or support arm64
42+
# TODO: support arm64
4043
new_revisions["x86_64"] = "69"
4144
with snap_revisions.open("w") as file:
4245
json.dump(new_revisions, file)
@@ -71,6 +74,9 @@ async def test_build_and_deploy(ops_test: OpsTest) -> None:
7174

7275

7376
@pytest.mark.group(1)
77+
# TODO: remove after next incompatible MySQL server version released in our snap
78+
# (details: https://github.com/canonical/mysql-operator/pull/472#discussion_r1659300069)
79+
@markers.amd64_only
7480
@pytest.mark.abort_on_fail
7581
async def test_pre_upgrade_check(ops_test: OpsTest) -> None:
7682
"""Test that the pre-upgrade-check action runs successfully."""
@@ -83,6 +89,9 @@ async def test_pre_upgrade_check(ops_test: OpsTest) -> None:
8389

8490

8591
@pytest.mark.group(1)
92+
# TODO: remove after next incompatible MySQL server version released in our snap
93+
# (details: https://github.com/canonical/mysql-operator/pull/472#discussion_r1659300069)
94+
@markers.amd64_only
8695
@pytest.mark.abort_on_fail
8796
async def test_upgrade_to_failling(
8897
ops_test: OpsTest,
@@ -127,6 +136,9 @@ async def test_upgrade_to_failling(
127136

128137

129138
@pytest.mark.group(1)
139+
# TODO: remove after next incompatible MySQL server version released in our snap
140+
# (details: https://github.com/canonical/mysql-operator/pull/472#discussion_r1659300069)
141+
@markers.amd64_only
130142
@pytest.mark.abort_on_fail
131143
@pytest.mark.unstable
132144
async def test_rollback(ops_test, continuous_writes) -> None:

tests/integration/markers.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
import pytest
55

6-
from . import juju_
6+
from . import architecture, juju_
77

88
juju3 = pytest.mark.skipif(juju_.juju_major_version < 3, reason="Requires juju 3+")
99
only_with_juju_secrets = pytest.mark.skipif(
@@ -12,3 +12,9 @@
1212
only_without_juju_secrets = pytest.mark.skipif(
1313
juju_.has_secrets, reason="Requires juju version w/o secrets"
1414
)
15+
amd64_only = pytest.mark.skipif(
16+
architecture.architecture != "amd64", reason="Requires amd64 architecture"
17+
)
18+
arm64_only = pytest.mark.skipif(
19+
architecture.architecture != "arm64", reason="Requires arm64 architecture"
20+
)

tests/integration/test_tls.py

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111

1212
from constants import CLUSTER_ADMIN_USERNAME, TLS_SSL_CERT_FILE
1313

14-
from . import juju_
14+
from . import architecture, juju_
1515
from .helpers import (
1616
app_name,
1717
get_system_user_password,
@@ -29,11 +29,19 @@
2929
APP_NAME = METADATA["name"]
3030

3131
if juju_.has_secrets:
32-
TLS_APP_NAME = "self-signed-certificates"
33-
TLS_CONFIG = {"ca-common-name": "Test CA"}
32+
tls_app_name = "self-signed-certificates"
33+
if architecture.architecture == "arm64":
34+
tls_channel = "latest/edge"
35+
else:
36+
tls_channel = "latest/stable"
37+
tls_config = {"ca-common-name": "Test CA"}
3438
else:
35-
TLS_APP_NAME = "tls-certificates-operator"
36-
TLS_CONFIG = {"generate-self-signed-certificates": "true", "ca-common-name": "Test CA"}
39+
tls_app_name = "tls-certificates-operator"
40+
if architecture.architecture == "arm64":
41+
tls_channel = "legacy/edge"
42+
else:
43+
tls_channel = "legacy/stable"
44+
tls_config = {"generate-self-signed-certificates": "true", "ca-common-name": "Test CA"}
3745

3846

3947
@pytest.mark.group(1)
@@ -109,12 +117,12 @@ async def test_enable_tls(ops_test: OpsTest) -> None:
109117
# Deploy TLS Certificates operator.
110118
logger.info("Deploy TLS operator")
111119
async with ops_test.fast_forward("60s"):
112-
await ops_test.model.deploy(TLS_APP_NAME, channel="latest/stable", config=TLS_CONFIG)
113-
await ops_test.model.wait_for_idle(apps=[TLS_APP_NAME], status="active", timeout=15 * 60)
120+
await ops_test.model.deploy(tls_app_name, channel=tls_channel, config=tls_config)
121+
await ops_test.model.wait_for_idle(apps=[tls_app_name], status="active", timeout=15 * 60)
114122

115123
# Relate with TLS charm
116124
logger.info("Relate to TLS operator")
117-
await ops_test.model.relate(app, TLS_APP_NAME)
125+
await ops_test.model.relate(app, tls_app_name)
118126

119127
# Wait for hooks start reconfiguring app
120128
# add as a wait since app state does not change
@@ -207,7 +215,7 @@ async def test_disable_tls(ops_test: OpsTest) -> None:
207215

208216
logger.info("Removing relation")
209217
await ops_test.model.applications[app].remove_relation(
210-
f"{app}:certificates", f"{TLS_APP_NAME}:certificates"
218+
f"{app}:certificates", f"{tls_app_name}:certificates"
211219
)
212220

213221
# Allow time for reconfigure

0 commit comments

Comments
 (0)