@@ -117,15 +117,33 @@ def get_migrations_map(self, node=None):
117
117
migrations = self .admin .list_data_migrations (node ).json ()
118
118
return {migration ["id" ]: migration for migration in migrations }
119
119
120
+ def get_migration (self , id , node = None ):
121
+ try :
122
+ return self .admin .get_data_migration (id , node ).json ()
123
+ except requests .exceptions .HTTPError as e :
124
+ if e .response .status_code == 404 :
125
+ return None
126
+ else :
127
+ raise
128
+
129
+ def assure_not_deletable (self , id , node = None ):
130
+ try :
131
+ self .admin .delete_data_migration (id , node )
132
+ assert False
133
+ except :
134
+ pass
135
+
120
136
def on_all_live_nodes (self , migration_id , predicate ):
121
137
success_cnt = 0
122
138
exception_cnt = 0
123
139
for n in self .redpanda .nodes :
124
140
try :
125
141
map = self .get_migrations_map (n )
126
142
self .logger .debug (f"migrations on node { n .name } : { map } " )
127
- if predicate (map [migration_id ] if migration_id in
128
- map else None ):
143
+ list_item = map [migration_id ] if migration_id in map else None
144
+ individual = self .get_migration (migration_id , n )
145
+
146
+ if predicate (list_item ) and predicate (individual ):
129
147
success_cnt += 1
130
148
else :
131
149
return False
@@ -146,6 +164,9 @@ def migration_in_one_of_states():
146
164
err_msg =
147
165
f"Failed waiting for migration { id } to reach one of { states } states"
148
166
)
167
+ if all (state not in ('planned' , 'finished' , 'cancelled' )
168
+ for state in states ):
169
+ self .assure_not_deletable (id )
149
170
150
171
def wait_migration_appear (self , migration_id ):
151
172
def migration_is_present (id : int ):
@@ -170,8 +191,11 @@ def migration_is_absent(id: int):
170
191
def wait_partitions_appear (self , topics : list [TopicSpec ]):
171
192
# we may be unlucky to query a slow node
172
193
def topic_has_all_partitions (t : TopicSpec ):
173
- return t .partition_count == \
174
- len (self .client ().describe_topic (t .name ).partitions )
194
+ exp_part_cnt = len (self .client ().describe_topic (t .name ).partitions )
195
+ self .logger .debug (
196
+ f"topic { t .name } has { t .partition_count } partitions out of { exp_part_cnt } expected"
197
+ )
198
+ return t .partition_count == exp_part_cnt
175
199
176
200
wait_until (lambda : all (topic_has_all_partitions (t ) for t in topics ),
177
201
timeout_sec = 90 ,
@@ -211,6 +235,62 @@ def migration_id_if_exists():
211
235
self .wait_migration_appear (migration_id )
212
236
return migration_id
213
237
238
+ def assure_not_migratable (self , topic : TopicSpec ):
239
+ out_migration = OutboundDataMigration (
240
+ [make_namespaced_topic (topic .name )], consumer_groups = [])
241
+ try :
242
+ self .create_and_wait (out_migration )
243
+ assert False
244
+ except requests .exceptions .HTTPError as e :
245
+ pass
246
+
247
+ @cluster (num_nodes = 3 , log_allow_list = MIGRATION_LOG_ALLOW_LIST )
248
+ def test_listing_inexistent_migration (self ):
249
+ try :
250
+ self .get_migration (42 )
251
+ except Exception as e :
252
+ # check 404
253
+ self .logger .info ("f{e}" )
254
+ raise
255
+
256
+ @cluster (num_nodes = 3 , log_allow_list = MIGRATION_LOG_ALLOW_LIST )
257
+ def test_creating_with_topic_no_remote_writes (self ):
258
+ self .redpanda .set_cluster_config (
259
+ {"cloud_storage_enable_remote_write" : False }, expect_restart = True )
260
+ topic = TopicSpec (partition_count = 3 )
261
+ self .client ().create_topic (topic )
262
+ self .wait_partitions_appear ([topic ])
263
+ self .redpanda .set_cluster_config (
264
+ {"cloud_storage_enable_remote_write" : True }, expect_restart = True )
265
+ self .assure_not_migratable (topic )
266
+
267
+ @cluster (
268
+ num_nodes = 3 ,
269
+ log_allow_list = MIGRATION_LOG_ALLOW_LIST + [
270
+ r'/v1/migrations.*Requested feature is disabled' , # cloud storage disabled
271
+ ])
272
+ def test_creating_when_cluster_misconfigured1 (self ):
273
+ self .creating_when_cluster_misconfigured ("cloud_storage_enabled" )
274
+
275
+ @cluster (
276
+ num_nodes = 3 ,
277
+ log_allow_list = MIGRATION_LOG_ALLOW_LIST + [
278
+ r'/v1/migrations.*Requested feature is disabled' , # cloud storage disabled
279
+ 'archival' # a variety of archival errors is observed
280
+ ])
281
+ def test_creating_when_cluster_misconfigured2 (self ):
282
+ self .creating_when_cluster_misconfigured ("cloud_storage_enabled" )
283
+
284
+ def creating_when_cluster_misconfigured (self , param_to_disable ):
285
+ self .redpanda .set_cluster_config ({param_to_disable : False },
286
+ expect_restart = True )
287
+ topic = TopicSpec (partition_count = 3 )
288
+ self .client ().create_topic (topic )
289
+ self .assure_not_migratable (topic )
290
+ # for scrubbing to complete
291
+ self .redpanda .set_cluster_config ({param_to_disable : True },
292
+ expect_restart = True )
293
+
214
294
@cluster (num_nodes = 3 , log_allow_list = MIGRATION_LOG_ALLOW_LIST )
215
295
def test_creating_and_listing_migrations (self ):
216
296
topics = [TopicSpec (partition_count = 3 ) for i in range (5 )]
@@ -310,7 +390,7 @@ def test_creating_and_listing_migrations(self):
310
390
self .redpanda .si_settings .set_expected_damage (
311
391
{"ntr_no_topic_manifest" , "missing_segments" })
312
392
313
- @cluster (num_nodes = 3 )
393
+ @cluster (num_nodes = 3 , log_allow_list = MIGRATION_LOG_ALLOW_LIST )
314
394
def test_higher_level_migration_api (self ):
315
395
topics = [TopicSpec (partition_count = 3 ) for i in range (5 )]
316
396
0 commit comments