3
3
4
4
import sys
5
5
6
- import boto3
7
-
6
+ import boto3 , time
7
+ from botocore .exceptions import ClientError , WaiterError
8
+
9
+ class ThingDetachedWaiter :
10
+ """
11
+ Wait until principal (cert or Cognito identity) is detached from a thing.
12
+ Raise WaiterError after timeout seconds.
13
+ """
14
+
15
+ def __init__ (self , client , delay = 2.0 , max_delay = 10.0 , timeout = 60.0 ):
16
+ self ._client = client
17
+ self ._delay = delay
18
+ self ._max_delay = max_delay
19
+ self ._timeout = timeout
20
+
21
+ def wait (self , thing_name ):
22
+ start = time .monotonic ()
23
+ sleep = self ._delay
24
+
25
+ while True :
26
+ try :
27
+ resp = self ._client .list_thing_principals (thingName = thing_name )
28
+ except ClientError as e :
29
+ if e .response ["Error" ]["Code" ] == "ResourceNotFoundException" :
30
+ return
31
+ raise
32
+
33
+ if not resp .get ("principals" ):
34
+ # No principals, we can move on.
35
+ return
36
+
37
+ if time .monotonic () - start > self ._timeout :
38
+ raise WaiterError (
39
+ name = "ThingDetached" ,
40
+ reason = "timeout" ,
41
+ last_response = resp ,
42
+ )
43
+
44
+ time .sleep (sleep )
45
+ sleep = min (sleep * 1.6 , self ._max_delay ) # exponential backoff on retrys
8
46
9
47
def create_iot_thing (thing_name , region , policy_name , certificate_path , key_path , thing_group = None ):
10
48
""" Create IoT thing along with policy and credentials. """
@@ -57,6 +95,8 @@ def delete_iot_thing(thing_name, region):
57
95
print (f"ERROR: Could not make Boto3 client. Credentials likely could not be sourced" , file = sys .stderr )
58
96
raise
59
97
98
+ cert_ids = []
99
+
60
100
# Detach and delete thing's principals.
61
101
try :
62
102
thing_principals = iot_client .list_thing_principals (thingName = thing_name )
@@ -65,12 +105,24 @@ def delete_iot_thing(thing_name, region):
65
105
certificate_id = principal .split ("/" )[1 ]
66
106
iot_client .detach_thing_principal (thingName = thing_name , principal = principal )
67
107
iot_client .update_certificate (certificateId = certificate_id , newStatus = 'INACTIVE' )
68
- iot_client . delete_certificate ( certificateId = certificate_id , forceDelete = True )
108
+ cert_ids . append ( certificate_id )
69
109
except Exception :
70
- print (f"ERROR: Could not delete certificate for IoT thing { thing_name } , probably thing does not exist" ,
110
+ print (f"ERROR: Could not detatch principal or set its certificate to INACTIVE for { thing_name } , probably thing does not exist" ,
71
111
file = sys .stderr )
72
112
raise
73
113
114
+ # Wait for thing to be free of principals
115
+ ThingDetachedWaiter (iot_client , timeout = 10 ).wait (thing_name )
116
+
117
+ # Delete all the certificates
118
+ for cert in cert_ids :
119
+ try :
120
+ iot_client .delete_certificate (certificateId = cert , forceDelete = True )
121
+ except Exception :
122
+ print (f"ERROR: Could not delete certificate for IoT thing { thing_name } ." ,
123
+ file = sys .stderr )
124
+ raise
125
+
74
126
# Delete thing.
75
127
try :
76
128
iot_client .delete_thing (thingName = thing_name )
0 commit comments