@@ -123,35 +123,25 @@ def __init__(self, api_key):
123
123
# In situations where there are a lot of resources, we want to
124
124
# slow down the rate of requests just to avoid any concerns about
125
125
# rate limits
126
- self .sleep_interval = 10
126
+ self .sleep_interval = 5
127
+ self .undeleteable_resources = []
127
128
128
129
def pluralize (self , resource_name ):
129
130
if resource_name .lower () == "index" :
130
131
return resource_name + "es"
131
132
else :
132
133
return resource_name + "s"
133
134
134
- def _delete_all_of_resource (
135
- self ,
136
- resource_name ,
137
- list_func ,
138
- describe_func ,
139
- delete_func ,
140
- get_state_func = None ,
141
- configure_func = None ,
142
- ):
135
+ def _delete_all_of_resource (self , resource_name , list_func , delete_func , get_state_func ):
143
136
resources_to_delete = deque (list_func ())
144
137
if len (resources_to_delete ) == 0 :
145
138
logger .info (f"No { self .pluralize (resource_name )} to delete" )
146
139
return
147
140
148
- max_retries = 3
149
- max_terminating_status_checks = 10
150
- state_check_counts = RetryCounter (max_retries )
151
- failed_delete_counts = RetryCounter (max_retries )
152
- status_check_counts = RetryCounter (max_retries )
153
- deletion_protection_change_counts = RetryCounter (max_retries )
154
- terminating_status_counts = RetryCounter (max_terminating_status_checks )
141
+ state_check_retries = RetryCounter (3 )
142
+ failed_delete_retries = RetryCounter (3 )
143
+ is_deletable_retries = RetryCounter (3 )
144
+ is_terminating_retries = RetryCounter (10 )
155
145
156
146
undeletable_resources = []
157
147
@@ -164,21 +154,21 @@ def _delete_all_of_resource(
164
154
resource = resources_to_delete .popleft ()
165
155
logger .info (f"Processing { resource_name } { resource .name } " )
166
156
167
- # Get the latest description of the index
157
+ # Get the latest description of the resource
168
158
try :
169
- state_check_counts .increment (resource .name )
159
+ state_check_retries .increment (resource .name )
170
160
state = get_state_func (name = resource .name )
171
161
logger .info (f"{ resource_name } { resource .name } has state { state } " )
172
162
except NotFoundException :
173
163
logger .info (f"{ resource_name } { resource .name } has already been deleted, continuing" )
174
164
continue
175
165
except Exception as e :
176
- state_check_counts .increment (resource .name )
177
- if state_check_counts .is_maxed_out (resource .name ):
166
+ if state_check_retries .is_maxed_out (resource .name ):
178
167
logger .error (f"Error describing { resource_name } { resource .name } : { e } " )
179
168
undeletable_resources .append (
180
169
{
181
170
"resource" : resource ,
171
+ "type" : resource_name ,
182
172
"reason" : f"Error describing { resource_name } { resource .name } : { e } " ,
183
173
}
184
174
)
@@ -190,15 +180,16 @@ def _delete_all_of_resource(
190
180
resources_to_delete .append (resource )
191
181
continue
192
182
193
- if state == "Terminating" :
194
- terminating_status_counts .increment (resource .name )
195
- if terminating_status_counts .is_maxed_out (resource .name ):
183
+ if state == "Terminating" or state == "Terminated" :
184
+ is_terminating_retries .increment (resource .name )
185
+ if is_terminating_retries .is_maxed_out (resource .name ):
196
186
logger .error (
197
187
f"{ resource_name } { resource .name } has been in the terminating state for too long, skipping"
198
188
)
199
189
undeletable_resources .append (
200
190
{
201
191
"resource" : resource ,
192
+ "type" : resource_name ,
202
193
"reason" : f"{ resource_name } has been in the terminating state for too long" ,
203
194
}
204
195
)
@@ -213,15 +204,17 @@ def _delete_all_of_resource(
213
204
# If the index is not in a deleteable state, add it to the back of the delete queue
214
205
deleteable_states = ["Ready" , "InitializationFailed" ]
215
206
if state not in deleteable_states :
216
- status_check_counts .increment (resource .name )
217
- if status_check_counts .is_maxed_out (resource .name ):
207
+ is_deletable_retries .increment (resource .name )
208
+ if is_deletable_retries .is_maxed_out (resource .name ):
209
+ attempts = is_deletable_retries .get_count (resource .name )
218
210
logger .error (
219
- f"{ resource_name } { resource .name } did not enter a deleteable state after { max_retries } attempts, skipping"
211
+ f"{ resource_name } { resource .name } did not enter a deleteable state after { attempts } attempts, skipping"
220
212
)
221
213
undeletable_resources .append (
222
214
{
223
215
"resource" : resource ,
224
- "reason" : f"Not in a deleteable state after { max_retries } attempts" ,
216
+ "type" : resource_name ,
217
+ "reason" : f"Not in a deleteable state after { attempts } attempts" ,
225
218
}
226
219
)
227
220
continue
@@ -232,61 +225,23 @@ def _delete_all_of_resource(
232
225
resources_to_delete .append (resource )
233
226
continue
234
227
235
- if configure_func is not None :
236
- description = describe_func (name = resource .name )
237
- logger .info (
238
- f"{ resource_name } { resource .name } has deletion protection { description .deletion_protection } "
239
- )
240
- if description .deletion_protection == "enabled" :
241
- try :
242
- logger .info (
243
- f"Disabling deletion protection for { resource_name } { resource .name } "
244
- )
245
- configure_func (name = resource .name , deletion_protection = "disabled" )
246
- resources_to_delete .append (resource )
247
- logger .info (
248
- f"{ resource_name } { resource .name } has been returned to the back of the delete queue"
249
- )
250
- continue
251
- except Exception as e :
252
- logger .error (
253
- f"Error disabling deletion protection for { resource_name } { resource .name } : { e } "
254
- )
255
-
256
- deletion_protection_change_counts .increment (resource .name )
257
- if deletion_protection_change_counts .is_maxed_out (resource .name ):
258
- logger .error (
259
- f"Failed to change deletion protection for { resource_name } { resource .name } after { max_retries } attempts, skipping"
260
- )
261
- undeletable_resources .append (
262
- {
263
- "resource" : resource ,
264
- "reason" : f"Failed to change deletion protection after { max_retries } attempts" ,
265
- }
266
- )
267
- continue
268
- else :
269
- logger .info (
270
- f"{ resource_name } { resource .name } has been returned to the back of the delete queue"
271
- )
272
- resources_to_delete .append (resource )
273
- continue
274
-
275
228
try :
276
229
logger .info (f"Attempting deleting of { resource_name } { resource .name } " )
277
230
delete_func (name = resource .name )
278
231
logger .info (f"Successfully deleted { resource_name } { resource .name } " )
279
232
except Exception as e :
280
233
logger .error (f"Error deleting { resource_name } { resource .name } : { e } " )
281
- failed_delete_counts .increment (resource .name )
282
- if failed_delete_counts .is_maxed_out (resource .name ):
234
+ failed_delete_retries .increment (resource .name )
235
+ if failed_delete_retries .is_maxed_out (resource .name ):
236
+ attempts = failed_delete_retries .get_count (resource .name )
283
237
logger .error (
284
- f"Failed to delete { resource_name } { resource .name } after { max_retries } attempts, skipping"
238
+ f"Failed to delete { resource_name } { resource .name } after { attempts } attempts, skipping"
285
239
)
286
240
undeletable_resources .append (
287
241
{
288
242
"resource" : resource ,
289
- "reason" : f"Failed to delete after { max_retries } attempts" ,
243
+ "type" : resource_name ,
244
+ "reason" : f"Failed to delete after { attempts } attempts" ,
290
245
}
291
246
)
292
247
continue
@@ -301,28 +256,47 @@ def _delete_all_of_resource(
301
256
logger .error (
302
257
f"There were { len (undeletable_resources )} { self .pluralize (resource_name )} that were not deleted"
303
258
)
304
- for resource in undeletable_resources :
259
+ for item in undeletable_resources :
305
260
logger .error (
306
- f"{ resource_name } { resource ['resource' ].name } was not deleted because { resource ['reason' ]} "
261
+ f"{ resource_name } { item ['resource' ].name } was not deleted because { item ['reason' ]} "
307
262
)
308
- raise Exception (
309
- f"There were { len (undeletable_resources )} { self .pluralize (resource_name )} that were not deleted"
310
- )
263
+ self .undeleteable_resources .append (item )
311
264
else :
312
265
logger .info (f"All { self .pluralize (resource_name )} were deleted successfully" )
313
266
314
267
def delete_all_indexes (self ):
268
+ index_list = self .pc .db .index .list ()
269
+ if len (index_list ) == 0 :
270
+ logger .info ("No indexes to delete" )
271
+ return
272
+
273
+ index_with_deletion_protection = [
274
+ index for index in index_list if index .deletion_protection == "enabled"
275
+ ]
276
+ for index in index_with_deletion_protection :
277
+ logger .info (f"Disabling deletion protection for Index { index .name } " )
278
+ time .sleep (self .sleep_interval )
279
+ try :
280
+ self .pc .db .index .configure (name = index .name , deletion_protection = "disabled" )
281
+ except Exception as e :
282
+ logger .error (f"Error disabling deletion protection for Index { index .name } : { e } " )
283
+ self .undeleteable_resources .append (
284
+ {
285
+ "resource" : index ,
286
+ "type" : "index" ,
287
+ "reason" : f"Failed to disable deletion protection: { e } " ,
288
+ }
289
+ )
290
+
315
291
def get_state_func (name ):
316
292
desc = self .pc .db .index .describe (name = name )
317
293
return desc .status .state
318
294
319
295
return self ._delete_all_of_resource (
320
296
resource_name = "index" ,
321
297
list_func = self .pc .db .index .list ,
322
- describe_func = self .pc .db .index .describe ,
323
298
delete_func = self .pc .db .index .delete ,
324
299
get_state_func = get_state_func ,
325
- configure_func = self .pc .db .index .configure ,
326
300
)
327
301
328
302
def delete_all_collections (self ):
@@ -333,7 +307,6 @@ def get_state_func(name):
333
307
return self ._delete_all_of_resource (
334
308
resource_name = "collection" ,
335
309
list_func = self .pc .db .collection .list ,
336
- describe_func = self .pc .db .collection .describe ,
337
310
delete_func = self .pc .db .collection .delete ,
338
311
get_state_func = get_state_func ,
339
312
)
@@ -345,10 +318,6 @@ def _get_backup_by_name(name):
345
318
return backup
346
319
raise Exception (f"Backup { name } not found" )
347
320
348
- def describe_func (name ):
349
- backup = _get_backup_by_name (name )
350
- return self .pc .db .backup .describe (backup_id = backup .backup_id )
351
-
352
321
def delete_func (name ):
353
322
backup = _get_backup_by_name (name )
354
323
return self .pc .db .backup .delete (backup_id = backup .backup_id )
@@ -360,15 +329,54 @@ def get_state_func(name):
360
329
return self ._delete_all_of_resource (
361
330
resource_name = "backup" ,
362
331
list_func = self .pc .db .backup .list ,
363
- describe_func = describe_func ,
364
332
delete_func = delete_func ,
365
333
get_state_func = get_state_func ,
366
334
)
367
335
368
- def cleanup_all (self ):
336
+ def _cleanup_all (self ):
337
+ self .undeleteable_resources = []
369
338
self .delete_all_backups ()
370
- self .delete_all_indexes ()
371
339
self .delete_all_collections ()
340
+ self .delete_all_indexes ()
341
+
342
+ def cleanup_all (self ):
343
+ self ._cleanup_all ()
344
+
345
+ if len (self .undeleteable_resources ) > 0 :
346
+ logger .info (
347
+ f"There were { len (self .undeleteable_resources )} undeleteable resources, retrying in 60 seconds"
348
+ )
349
+ time .sleep (60 )
350
+ self ._cleanup_all ()
351
+
352
+ if len (self .undeleteable_resources ) > 0 :
353
+ logger .info (
354
+ f"There were { len (self .undeleteable_resources )} undeleteable resources, retrying in 120 seconds"
355
+ )
356
+ time .sleep (120 )
357
+ self ._cleanup_all ()
358
+
359
+ if len (self .undeleteable_resources ) > 0 :
360
+ logger .info (
361
+ f"There were { len (self .undeleteable_resources )} undeleteable resources, retrying in 240 seconds"
362
+ )
363
+ time .sleep (240 )
364
+ self ._cleanup_all ()
365
+
366
+ if len (self .undeleteable_resources ) > 0 :
367
+ logger .info (
368
+ f"There were { len (self .undeleteable_resources )} undeleteable resources, retrying in 240 seconds"
369
+ )
370
+ time .sleep (240 )
371
+ self ._cleanup_all ()
372
+
373
+ if len (self .undeleteable_resources ) > 0 :
374
+ logger .error (
375
+ f"There were { len (self .undeleteable_resources )} undeleteable resources, giving up"
376
+ )
377
+ raise Exception (
378
+ f"There were { len (self .undeleteable_resources )} undeleteable resources, giving up"
379
+ )
372
380
373
381
374
382
if __name__ == "__main__" :
0 commit comments