1515 CHARM_SERIES ,
1616 get_primary ,
1717)
18+ from ..juju_ import juju_major_version
1819from .helpers import (
20+ add_unit_with_storage ,
1921 check_db ,
2022 check_password_auth ,
2123 create_db ,
2224 get_any_deatached_storage ,
2325 is_postgresql_ready ,
2426 is_storage_exists ,
27+ remove_unit_force ,
2528 storage_id ,
2629)
2730
28- TEST_GARBADGE_STORAGE_NAME = "test_pgdata"
31+ TEST_garbage_STORAGE_NAME = "test_pgdata"
2932TEST_DATABASE_RELATION_NAME = "test_database"
3033DUP_APPLICATION_NAME = "postgres-test-dup"
3134
3235logger = logging .getLogger (__name__ )
3336
3437
35- @pytest .mark .group (1 )
36- @pytest .mark .abort_on_fail
37- async def test_app_removal (ops_test : OpsTest , charm : str ):
38- """Test all recoureces is removed after application removal."""
39- # Deploy the charm.
40- async with ops_test .fast_forward ():
41- await ops_test .model .deploy (
42- charm ,
43- application_name = APPLICATION_NAME ,
44- num_units = 1 ,
45- series = CHARM_SERIES ,
46- config = {"profile" : "testing" },
47- )
48-
49- # Reducing the update status frequency to speed up the triggering of deferred events.
50- await ops_test .model .set_config ({"update-status-hook-interval" : "10s" })
51-
52- await ops_test .model .wait_for_idle (apps = [APPLICATION_NAME ], status = "active" , timeout = 1500 )
53- assert ops_test .model .applications [APPLICATION_NAME ].units [0 ].workload_status == "active"
54-
55- primary_name = await get_primary (
56- ops_test , ops_test .model .applications [APPLICATION_NAME ].units [0 ].name
57- )
58- assert await is_postgresql_ready (ops_test , primary_name )
59-
60- storage_id_str = storage_id (ops_test , primary_name )
61-
62- # Check if storage exists after application deployed
63- assert await is_storage_exists (ops_test , storage_id_str )
64-
65- await ops_test .model .remove_application (
66- APPLICATION_NAME , block_until_done = True , destroy_storage = True
67- )
68-
69- # Check if storage removed after application removal
70- assert not await is_storage_exists (ops_test , storage_id_str )
71-
72-
7338@pytest .mark .group (1 )
7439@pytest .mark .abort_on_fail
7540async def test_app_force_removal (ops_test : OpsTest , charm : str ):
7641 """Remove unit with force while storage is alive."""
7742 async with ops_test .fast_forward ():
7843 # Deploy the charm.
44+ logger .info ("deploying charm" )
7945 await ops_test .model .deploy (
8046 charm ,
8147 application_name = APPLICATION_NAME ,
@@ -88,101 +54,181 @@ async def test_app_force_removal(ops_test: OpsTest, charm: str):
8854 # Reducing the update status frequency to speed up the triggering of deferred events.
8955 await ops_test .model .set_config ({"update-status-hook-interval" : "10s" })
9056
57+ logger .info ("waiting for idle" )
9158 await ops_test .model .wait_for_idle (apps = [APPLICATION_NAME ], status = "active" , timeout = 1500 )
9259 assert ops_test .model .applications [APPLICATION_NAME ].units [0 ].workload_status == "active"
9360
61+ logger .info ("getting primary" )
9462 primary_name = await get_primary (
9563 ops_test , ops_test .model .applications [APPLICATION_NAME ].units [0 ].name
9664 )
97- assert await is_postgresql_ready (ops_test , primary_name )
9865
66+ logger .info ("waiting for postgresql" )
67+ for attempt in Retrying (stop = stop_after_delay (15 * 3 ), wait = wait_fixed (3 )):
68+ with attempt :
69+ assert await is_postgresql_ready (ops_test , primary_name )
70+
71+ logger .info ("getting storage id" )
9972 storage_id_str = storage_id (ops_test , primary_name )
10073
10174 # Check if storage exists after application deployed
102- assert await is_storage_exists (ops_test , storage_id_str )
75+ logger .info ("werifing is storage exists" )
76+ for attempt in Retrying (stop = stop_after_delay (15 * 3 ), wait = wait_fixed (3 )):
77+ with attempt :
78+ assert await is_storage_exists (ops_test , storage_id_str )
10379
10480 # Create test database to check there is no resources conflicts
81+ logger .info ("creating db" )
10582 await create_db (ops_test , APPLICATION_NAME , TEST_DATABASE_RELATION_NAME )
10683
10784 # Destroy charm
108- await ops_test .model .destroy_unit (
109- primary_name , force = True , destroy_storage = False , max_wait = 1500
110- )
85+ logger .info ("force removing charm" )
86+ if juju_major_version == 2 :
87+ await remove_unit_force (ops_test , primary_name )
88+ else :
89+ await ops_test .model .destroy_unit (
90+ primary_name , force = True , destroy_storage = False , max_wait = 1500
91+ )
11192
11293 # Storage should remain
113- assert await is_storage_exists (ops_test , storage_id_str )
94+ logger .info ("werifing is storage exists" )
95+ for attempt in Retrying (stop = stop_after_delay (15 * 3 ), wait = wait_fixed (3 )):
96+ with attempt :
97+ assert await is_storage_exists (ops_test , storage_id_str )
11498
11599
116100@pytest .mark .group (1 )
117101@pytest .mark .abort_on_fail
118102async def test_charm_garbage_ignorance (ops_test : OpsTest , charm : str ):
119103 """Test charm deploy in dirty environment with garbage storage."""
120104 async with ops_test .fast_forward ():
121- garbadge_storage = None
105+ logger .info ("checking garbage storage" )
106+ garbage_storage = None
122107 for attempt in Retrying (stop = stop_after_delay (30 * 3 ), wait = wait_fixed (3 )):
123108 with attempt :
124- garbadge_storage = await get_any_deatached_storage (ops_test )
125- assert garbadge_storage is not None
126-
127- assert garbadge_storage is not None
128-
129- await ops_test .model .applications [APPLICATION_NAME ].add_unit (
130- 1 , attach_storage = [tag .storage (garbadge_storage )]
131- )
109+ garbage_storage = await get_any_deatached_storage (ops_test )
132110
133111 # Reducing the update status frequency to speed up the triggering of deferred events.
134112 await ops_test .model .set_config ({"update-status-hook-interval" : "10s" })
135113
136- await ops_test . model . wait_for_idle ( apps = [ APPLICATION_NAME ], status = "active" , timeout = 1500 )
137- assert ops_test . model . applications [ APPLICATION_NAME ]. units [ 0 ]. workload_status == "active"
114+ logger . info ( "add unit with attached storage" )
115+ await add_unit_with_storage ( ops_test , APPLICATION_NAME , garbage_storage )
138116
117+ logger .info ("getting primary" )
139118 primary_name = await get_primary (
140119 ops_test , ops_test .model .applications [APPLICATION_NAME ].units [0 ].name
141120 )
142- assert await is_postgresql_ready (ops_test , primary_name )
143121
122+ logger .info ("waiting for postgresql" )
123+ for attempt in Retrying (stop = stop_after_delay (15 * 3 ), wait = wait_fixed (3 )):
124+ with attempt :
125+ assert await is_postgresql_ready (ops_test , primary_name )
126+
127+ logger .info ("getting storage id" )
144128 storage_id_str = storage_id (ops_test , primary_name )
145129
130+ assert storage_id_str == garbage_storage
131+
146132 # Check if storage exists after application deployed
147- assert await is_storage_exists (ops_test , storage_id_str )
133+ logger .info ("werifing is storage exists" )
134+ for attempt in Retrying (stop = stop_after_delay (15 * 3 ), wait = wait_fixed (3 )):
135+ with attempt :
136+ assert await is_storage_exists (ops_test , storage_id_str )
148137
149138 # Check that test database is not exists for new unit
139+ logger .info ("checking db" )
150140 assert not await check_db (ops_test , APPLICATION_NAME , TEST_DATABASE_RELATION_NAME )
151141
152- await ops_test .model .destroy_unit (primary_name , destroy_storage = False , max_wait = 1500 )
142+ logger .info ("removing charm" )
143+ await ops_test .model .destroy_unit (primary_name )
153144
154145
155146@pytest .mark .group (1 )
156147@pytest .mark .abort_on_fail
157- async def test_app_recoures_conflicts (ops_test : OpsTest , charm : str ):
148+ @pytest .mark .skipif (juju_major_version < 3 , reason = "Requires juju 3 or higher" )
149+ async def test_app_resources_conflicts_v3 (ops_test : OpsTest , charm : str ):
158150 """Test application deploy in dirty environment with garbage storage from another application."""
159151 async with ops_test .fast_forward ():
160- garbadge_storage = None
152+ logger .info ("checking garbage storage" )
153+ garbage_storage = None
161154 for attempt in Retrying (stop = stop_after_delay (30 * 3 ), wait = wait_fixed (3 )):
162155 with attempt :
163- garbadge_storage = await get_any_deatached_storage (ops_test )
164- assert garbadge_storage is not None
156+ garbage_storage = await get_any_deatached_storage (ops_test )
157+
158+ logger .info ("deploying duplicate application with attached storage" )
159+ await ops_test .model .deploy (
160+ charm ,
161+ application_name = DUP_APPLICATION_NAME ,
162+ num_units = 1 ,
163+ series = CHARM_SERIES ,
164+ attach_storage = [tag .storage (garbage_storage )],
165+ config = {"profile" : "testing" },
166+ )
167+
168+ # Reducing the update status frequency to speed up the triggering of deferred events.
169+ await ops_test .model .set_config ({"update-status-hook-interval" : "10s" })
170+
171+ logger .info ("waiting for duplicate application to be blocked" )
172+ try :
173+ await ops_test .model .wait_for_idle (
174+ apps = [DUP_APPLICATION_NAME ], timeout = 1000 , status = "blocked"
175+ )
176+ except TimeoutError :
177+ logger .info ("Application is not in blocked state. Checking logs..." )
178+
179+ # Since application have postgresql db in storage from external application it should not be able to connect due to new password
180+ logger .info ("checking operator password auth" )
181+ assert not await check_password_auth (
182+ ops_test , ops_test .model .applications [DUP_APPLICATION_NAME ].units [0 ].name
183+ )
184+
165185
166- assert garbadge_storage is not None
186+ @pytest .mark .group (1 )
187+ @pytest .mark .abort_on_fail
188+ @pytest .mark .skipif (juju_major_version != 2 , reason = "Requires juju 2" )
189+ async def test_app_resources_conflicts_v2 (ops_test : OpsTest , charm : str ):
190+ """Test application deploy in dirty environment with garbage storage from another application."""
191+ async with ops_test .fast_forward ():
192+ logger .info ("checking garbage storage" )
193+ garbage_storage = None
194+ for attempt in Retrying (stop = stop_after_delay (30 * 3 ), wait = wait_fixed (3 )):
195+ with attempt :
196+ garbage_storage = await get_any_deatached_storage (ops_test )
167197
168198 # Deploy duplicaate charm
199+ logger .info ("deploying duplicate application" )
169200 await ops_test .model .deploy (
170201 charm ,
171202 application_name = DUP_APPLICATION_NAME ,
172203 num_units = 1 ,
173204 series = CHARM_SERIES ,
174205 config = {"profile" : "testing" },
175- attach_storage = [tag .storage (garbadge_storage )],
176206 )
177207
208+ # Reducing the update status frequency to speed up the triggering of deferred events.
209+ await ops_test .model .set_config ({"update-status-hook-interval" : "10s" })
210+
211+ logger .info ("force removing charm" )
212+ await remove_unit_force (
213+ ops_test , ops_test .model .applications [DUP_APPLICATION_NAME ].units [0 ].name
214+ )
215+
216+ # Add unit with garbage storage
217+ logger .info ("adding charm with attached storage" )
218+ add_unit_cmd = f"add-unit { DUP_APPLICATION_NAME } --model={ ops_test .model .info .name } --attach-storage={ garbage_storage } " .split ()
219+ return_code , _ , _ = await ops_test .juju (* add_unit_cmd )
220+ assert return_code == 0 , "Failed to add unit with storage"
221+
222+ logger .info ("waiting for duplicate application to be blocked" )
178223 try :
179224 await ops_test .model .wait_for_idle (
180- apps = [DUP_APPLICATION_NAME ], timeout = 500 , status = "blocked"
225+ apps = [DUP_APPLICATION_NAME ], timeout = 1000 , status = "blocked"
181226 )
182227 except TimeoutError :
183228 logger .info ("Application is not in blocked state. Checking logs..." )
184229
185230 # Since application have postgresql db in storage from external application it should not be able to connect due to new password
231+ logger .info ("checking operator password auth" )
186232 assert not await check_password_auth (
187233 ops_test , ops_test .model .applications [DUP_APPLICATION_NAME ].units [0 ].name
188234 )
0 commit comments