Skip to content

Commit 1a34179

Browse files
committed
Upgrading upgrade tests in-line with Openserach
1 parent 8c2cae6 commit 1a34179

2 files changed

Lines changed: 314 additions & 15 deletions

File tree

tests/integration/helpers.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -623,6 +623,13 @@ async def get_leader_id(ops_test: OpsTest, app_name: str = APP_NAME) -> str:
623623
return leader_name.split("/")[-1]
624624

625625

626+
async def get_leader_unit(ops_test: OpsTest, app_name: str = APP_NAME) -> Unit:
627+
"""Get the leader unit name."""
628+
for unit in ops_test.model.applications[app_name].units:
629+
if await unit.is_leader_from_status():
630+
return unit
631+
632+
626633
def get_unit(ops_test: OpsTest, unit_name: str) -> str:
627634
"""Get unit by name."""
628635
for unit in ops_test.model.units:

tests/integration/test_upgrade.py

Lines changed: 307 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,63 @@
44

55
import json
66
import logging
7+
import subprocess
78
from pathlib import Path
89

910
import pytest
1011
import yaml
1112
from pytest_operator.plugin import OpsTest
1213

13-
from .helpers import access_all_dashboards, get_app_relation_data
14+
from src.literals import MSG_INCOMPATIBLE_UPGRADE
15+
16+
from ..ha.continuous_writes import ContinuousWrites
17+
from .helpers import (
18+
access_all_dashboards,
19+
check_full_status,
20+
get_app_relation_data,
21+
get_leader_unit,
22+
get_relations,
23+
)
1424

1525
logger = logging.getLogger(__name__)
1626

27+
28+
OPENSEARCH_CHARM_NAME = "opensearch"
29+
OPENSEARCH_DASHBOARDS_CHARM_NAME = "opensearch-dashboards"
30+
CHANNEL = "2/edge"
31+
32+
STARTING_VERSION = "2.14.0"
33+
34+
35+
OPENSEARCH_VERSION_TO_REVISION = {
36+
STARTING_VERSION: 143,
37+
"2.15.0": 144,
38+
"2.16.0": 161,
39+
}
40+
41+
VERSION_TO_REVISION = {
42+
STARTING_VERSION: 18,
43+
"2.15.0": 19,
44+
"2.16.0": 20,
45+
}
46+
47+
48+
FROM_VERSION_PREFIX = "from_v{}_to_local"
49+
50+
UPGRADE_INITIAL_VERSION = [
51+
(
52+
pytest.param(
53+
version,
54+
id=FROM_VERSION_PREFIX.format(version),
55+
marks=pytest.mark.group(FROM_VERSION_PREFIX.format(version)),
56+
)
57+
)
58+
for version in VERSION_TO_REVISION.keys()
59+
]
60+
61+
charm = None
62+
63+
1764
METADATA = yaml.safe_load(Path("./metadata.yaml").read_text())
1865
APP_NAME = METADATA["name"]
1966

@@ -30,6 +77,7 @@
3077
- [ 'sysctl', '-w', 'net.ipv4.tcp_retries2=5' ]
3178
""",
3279
}
80+
OPENSEARCH_RELATION_NAME = "opensearch-client"
3381
TLS_CERTIFICATES_APP_NAME = "self-signed-certificates"
3482

3583
NUM_UNITS_APP = 3
@@ -45,9 +93,11 @@ async def test_build_and_deploy(ops_test: OpsTest):
4593
"""Deploying all charms required for the tests, and wait for their complete setup to be done."""
4694

4795
pytest.charm = await ops_test.build_charm(".")
48-
await ops_test.model.deploy(pytest.charm, application_name=APP_NAME, num_units=NUM_UNITS_APP)
4996
await ops_test.model.set_config(OPENSEARCH_CONFIG)
50-
await ops_test.model.deploy(OPENSEARCH_APP_NAME, channel="2/edge", num_units=NUM_UNITS_DB)
97+
await ops_test.modeGl.deploy(
98+
OPENSEARCH_APP_NAME, channel="2/edge", num_units=NUM_UNITS_DB, revision=161
99+
)
100+
await ops_test.model.deploy(pytest.charm, application_name=APP_NAME, num_units=NUM_UNITS_APP)
51101

52102
config = {"ca-common-name": "CN_CA"}
53103
await ops_test.model.deploy(TLS_CERTIFICATES_APP_NAME, channel="stable", config=config)
@@ -80,12 +130,7 @@ async def test_build_and_deploy(ops_test: OpsTest):
80130
@pytest.mark.group(1)
81131
@pytest.mark.abort_on_fail
82132
async def test_in_place_upgrade_http(ops_test: OpsTest):
83-
leader_unit = None
84-
for unit in ops_test.model.applications[APP_NAME].units:
85-
if await unit.is_leader_from_status():
86-
leader_unit = unit
87-
assert leader_unit
88-
133+
leader_unit = get_leader_unit(ops_test)
89134
action = await leader_unit.run_action("pre-upgrade-check")
90135
await action.wait()
91136

@@ -124,12 +169,7 @@ async def test_switch_tls_on(ops_test: OpsTest):
124169
@pytest.mark.group(1)
125170
@pytest.mark.abort_on_fail
126171
async def test_in_place_upgrade_https(ops_test: OpsTest):
127-
leader_unit = None
128-
for unit in ops_test.model.applications[APP_NAME].units:
129-
if await unit.is_leader_from_status():
130-
leader_unit = unit
131-
assert leader_unit
132-
172+
leader_unit = get_leader_unit(ops_test)
133173
action = await leader_unit.run_action("pre-upgrade-check")
134174
await action.wait()
135175

@@ -149,3 +189,255 @@ async def test_in_place_upgrade_https(ops_test: OpsTest):
149189
)
150190

151191
assert await access_all_dashboards(ops_test, https=True)
192+
193+
194+
#######################################################################
195+
#
196+
# Auxiliary functions
197+
#
198+
#######################################################################
199+
200+
201+
@pytest.mark.runner(["self-hosted", "linux", "X64", "jammy", "large"])
202+
async def _build_env(ops_test: OpsTest, version: str) -> None:
203+
"""Deploy OpenSearch cluster from a given revision."""
204+
await ops_test.model.set_config(OPENSEARCH_CONFIG)
205+
206+
await ops_test.model.deploy(
207+
OPENSEARCH_CHARM_NAME,
208+
application_name=APP_NAME,
209+
num_units=3,
210+
channel=CHANNEL,
211+
revision=OPENSEARCH_VERSION_TO_REVISION[version],
212+
)
213+
214+
# Deploy TLS Certificates operator.
215+
config = {"ca-common-name": "CN_CA"}
216+
await ops_test.model.deploy(TLS_CERTIFICATES_APP_NAME, channel="stable", config=config)
217+
218+
# Relate it to OpenSearch to set up TLS.
219+
await ops_test.model.integrate(APP_NAME, TLS_CERTIFICATES_APP_NAME)
220+
await ops_test.model.wait_for_idle(
221+
apps=[TLS_CERTIFICATES_APP_NAME, APP_NAME],
222+
status="active",
223+
timeout=1400,
224+
idle_period=50,
225+
)
226+
assert len(ops_test.model.applications[APP_NAME].units) == 3
227+
228+
await ops_test.model.deploy(
229+
OPENSEARCH_DASHBOARDS_CHARM_NAME,
230+
application_name=APP_NAME,
231+
num_units=3,
232+
channel=CHANNEL,
233+
revision=VERSION_TO_REVISION[version],
234+
)
235+
236+
237+
#######################################################################
238+
#
239+
# Tests
240+
#
241+
#######################################################################
242+
243+
244+
@pytest.mark.runner(["self-hosted", "linux", "X64", "jammy", "large"])
245+
@pytest.mark.group("happy_path_upgrade")
246+
@pytest.mark.abort_on_fail
247+
@pytest.mark.skip_if_deployed
248+
async def test_deploy_latest_from_channel(ops_test: OpsTest) -> None:
249+
"""Deploy OpenSearch."""
250+
await _build_env(ops_test, STARTING_VERSION)
251+
252+
253+
@pytest.mark.group("happy_path_upgrade")
254+
@pytest.mark.abort_on_fail
255+
async def test_upgrade_between_versions(
256+
ops_test: OpsTest, c_writes: ContinuousWrites, c_writes_runner
257+
) -> None:
258+
"""Test upgrade from upstream to currently locally built version."""
259+
for version, rev in VERSION_TO_REVISION.items():
260+
if version == STARTING_VERSION:
261+
# We're starting in this version
262+
continue
263+
264+
logger.info(f"Upgrading to version {version}")
265+
266+
leader_unit = get_leader_unit(ops_test)
267+
action = await leader_unit.run_action("pre-upgrade-check")
268+
await action.wait()
269+
assert action.status == "completed"
270+
271+
async with ops_test.fast_forward():
272+
logger.info("Refresh the charm")
273+
# due to: https://github.com/juju/python-libjuju/issues/1057
274+
# application = ops_test.model.applications[APP_NAME]
275+
# await application.refresh(
276+
# revision=rev,
277+
# )
278+
subprocess.check_output(
279+
f"juju refresh {OPENSEARCH_CHARM_NAME} --revision={rev}".split()
280+
)
281+
282+
logger.info("Refresh is over, waiting for the charm to settle")
283+
284+
await ops_test.model.wait_for_idle(
285+
apps=[OPENSEARCH_CHARM_NAME], status="active", timeout=1000, idle_period=120
286+
)
287+
logger.info("Opensearch upgrade finished")
288+
289+
await ops_test.model.wait_for_idle(
290+
apps=[APP_NAME], status="blocked", timeout=1000, idle_period=120
291+
)
292+
293+
assert await check_full_status(
294+
ops_test, status="blocked", status_msg=MSG_INCOMPATIBLE_UPGRADE
295+
)
296+
297+
subprocess.check_output(
298+
f"juju refresh {OPENSEARCH_DASHBOARDS_CHARM_NAME} --revision={rev}".split()
299+
)
300+
301+
logger.info("Refresh is over, waiting for the charm to settle")
302+
await ops_test.model.wait_for_idle(
303+
apps=[APP_NAME], status="blocked", timeout=1000, idle_period=120
304+
)
305+
logger.info("Opensearch Dashboards upgrade finished")
306+
307+
opensearch_relation = get_relations(ops_test, OPENSEARCH_RELATION_NAME)[0]
308+
assert await access_all_dashboards(ops_test, opensearch_relation.id)
309+
310+
311+
# @pytest.mark.runner(["self-hosted", "linux", "X64", "jammy", "large"])
312+
# @pytest.mark.group("happy_path_upgrade")
313+
# @pytest.mark.abort_on_fail
314+
# async def test_upgrade_to_local(
315+
# ops_test: OpsTest, c_writes: ContinuousWrites, c_writes_runner
316+
# ) -> None:
317+
# """Test upgrade from usptream to currently locally built version."""
318+
# logger.info("Build charm locally")
319+
# charm = await ops_test.build_charm(".")
320+
# await assert_upgrade_to_local(ops_test, c_writes, charm)
321+
#
322+
#
323+
# ##################################################################################
324+
# #
325+
# # test scenarios from each version:
326+
# # Start with each version, moving to local and then rolling back mid-upgrade
327+
# # Once this test passes, the 2nd test will rerun the upgrade, this time to
328+
# # its end.
329+
# #
330+
# ##################################################################################
331+
#
332+
#
333+
# @pytest.mark.runner(["self-hosted", "linux", "X64", "jammy", "large"])
334+
# @pytest.mark.parametrize("version", UPGRADE_INITIAL_VERSION)
335+
# @pytest.mark.abort_on_fail
336+
# @pytest.mark.skip_if_deployed
337+
# async def test_deploy_from_version(ops_test: OpsTest, version) -> None:
338+
# """Deploy OpenSearch."""
339+
# await _build_env(ops_test, version)
340+
#
341+
#
342+
# @pytest.mark.runner(["self-hosted", "linux", "X64", "jammy", "large"])
343+
# @pytest.mark.parametrize("version", UPGRADE_INITIAL_VERSION)
344+
# @pytest.mark.abort_on_fail
345+
# async def test_upgrade_rollback_from_local(
346+
# ops_test: OpsTest, c_writes: ContinuousWrites, c_writes_runner, version
347+
# ) -> None:
348+
# """Test upgrade and rollback to each version available."""
349+
# app = (await app_name(ops_test)) or APP_NAME
350+
# units = await get_application_units(ops_test, app)
351+
# leader_id = [u.id for u in units if u.is_leader][0]
352+
#
353+
# action = await run_action(
354+
# ops_test,
355+
# leader_id,
356+
# "pre-upgrade-check",
357+
# app=app,
358+
# )
359+
# assert action.status == "completed"
360+
#
361+
# logger.info("Build charm locally")
362+
# global charm
363+
# if not charm:
364+
# charm = await ops_test.build_charm(".")
365+
#
366+
# async with ops_test.fast_forward():
367+
# logger.info("Refresh the charm")
368+
# # due to: https://github.com/juju/python-libjuju/issues/1057
369+
# # application = ops_test.model.applications[APP_NAME]
370+
# # await application.refresh(
371+
# # revision=new_rev,
372+
# # )
373+
# subprocess.check_output(f"juju refresh opensearch --path={charm}".split())
374+
#
375+
# await wait_until(
376+
# ops_test,
377+
# apps=[app],
378+
# apps_statuses=["blocked"],
379+
# units_statuses=["active"],
380+
# wait_for_exact_units={
381+
# APP_NAME: 3,
382+
# },
383+
# timeout=1400,
384+
# idle_period=IDLE_PERIOD,
385+
# )
386+
#
387+
# logger.info(f"Rolling back to {version}")
388+
# # due to: https://github.com/juju/python-libjuju/issues/1057
389+
# # await application.refresh(
390+
# # revision=rev,
391+
# # )
392+
#
393+
# # Rollback operation
394+
# # We must first switch back to the upstream charm, then rollback to the original
395+
# # revision we were using.
396+
# subprocess.check_output(
397+
# f"""juju refresh opensearch
398+
# --switch={OPENSEARCH_DASHBOARDS_CHARM_NAME}
399+
# --channel={CHANNEL}""".split()
400+
# )
401+
#
402+
# # Wait until we are set in an idle state and can rollback the revision.
403+
# await wait_until(
404+
# ops_test,
405+
# apps=[app],
406+
# apps_statuses=["blocked"],
407+
# units_statuses=["active"],
408+
# wait_for_exact_units={
409+
# APP_NAME: 3,
410+
# },
411+
# timeout=1400,
412+
# idle_period=IDLE_PERIOD,
413+
# )
414+
#
415+
# subprocess.check_output(
416+
# f"juju refresh opensearch --revision={VERSION_TO_REVISION[version]}".split()
417+
# )
418+
#
419+
# await wait_until(
420+
# ops_test,
421+
# apps=[app],
422+
# apps_statuses=["active"],
423+
# units_statuses=["active"],
424+
# wait_for_exact_units={
425+
# APP_NAME: 3,
426+
# },
427+
# timeout=1400,
428+
# idle_period=IDLE_PERIOD,
429+
# )
430+
#
431+
#
432+
# @pytest.mark.runner(["self-hosted", "linux", "X64", "jammy", "large"])
433+
# @pytest.mark.parametrize("version", UPGRADE_INITIAL_VERSION)
434+
# @pytest.mark.abort_on_fail
435+
# async def test_upgrade_from_version_to_local(
436+
# ops_test: OpsTest, c_writes: ContinuousWrites, c_writes_runner, version
437+
# ) -> None:
438+
# """Test upgrade from usptream to currently locally built version."""
439+
# logger.info("Build charm locally")
440+
# global charm
441+
# if not charm:
442+
# charm = await ops_test.build_charm(".")
443+
# await assert_upgrade_to_local(ops_test, c_writes, charm)

0 commit comments

Comments
 (0)