@@ -74,7 +74,7 @@ def update_cluster(new_members: List[str], event: EventBase) -> None:
74
74
75
75
# Increment this PATCH version before using `charmcraft publish-lib` or reset
76
76
# to 0 if you are raising the major API version
77
- LIBPATCH = 6
77
+ LIBPATCH = 8
78
78
79
79
80
80
logger = logging .getLogger (__name__ )
@@ -101,6 +101,12 @@ class QuorumLeaderNotFoundError(Exception):
101
101
pass
102
102
103
103
104
+ class NoUnitFoundError (Exception ):
105
+ """Generic exception for when there are no running zk unit in the app."""
106
+
107
+ pass
108
+
109
+
104
110
class ZooKeeperManager :
105
111
"""Handler for performing ZK commands."""
106
112
@@ -114,6 +120,7 @@ def __init__(
114
120
keyfile_path : Optional [str ] = "" ,
115
121
keyfile_password : Optional [str ] = "" ,
116
122
certfile_path : Optional [str ] = "" ,
123
+ read_only : bool = True ,
117
124
):
118
125
self .hosts = hosts
119
126
self .username = username
@@ -123,12 +130,21 @@ def __init__(
123
130
self .keyfile_path = keyfile_path
124
131
self .keyfile_password = keyfile_password
125
132
self .certfile_path = certfile_path
126
- self .leader = ""
133
+ self .zk_host = ""
134
+ self .read_only = read_only
127
135
128
- try :
129
- self .leader = self .get_leader ()
130
- except RetryError :
131
- raise QuorumLeaderNotFoundError ("quorum leader not found" )
136
+ if not read_only :
137
+ try :
138
+ self .zk_host = self .get_leader ()
139
+ except RetryError :
140
+ raise QuorumLeaderNotFoundError ("quorum leader not found" )
141
+
142
+ else :
143
+ try :
144
+ self .zk_host = self .get_any_unit ()
145
+
146
+ except RetryError :
147
+ raise NoUnitFoundError
132
148
133
149
@retry (
134
150
wait = wait_fixed (3 ),
@@ -170,6 +186,35 @@ def get_leader(self) -> str:
170
186
171
187
return leader or ""
172
188
189
+ @retry (
190
+ wait = wait_fixed (3 ),
191
+ stop = stop_after_attempt (2 ),
192
+ retry = retry_if_not_result (lambda result : True if result else False ),
193
+ )
194
+ def get_any_unit (self ) -> str :
195
+ any_host = None
196
+ for host in self .hosts :
197
+ try :
198
+ with ZooKeeperClient (
199
+ host = host ,
200
+ client_port = self .client_port ,
201
+ username = self .username ,
202
+ password = self .password ,
203
+ use_ssl = self .use_ssl ,
204
+ keyfile_path = self .keyfile_path ,
205
+ keyfile_password = self .keyfile_password ,
206
+ certfile_path = self .certfile_path ,
207
+ ) as zk :
208
+ response = zk .srvr
209
+ if response :
210
+ any_host = host
211
+ break
212
+ except KazooTimeoutError : # in the case of having a dead unit in relation data
213
+ logger .debug (f"TIMEOUT - { host } " )
214
+ continue
215
+
216
+ return any_host or ""
217
+
173
218
@property
174
219
def server_members (self ) -> Set [str ]:
175
220
"""The current members within the ZooKeeper quorum.
@@ -179,7 +224,7 @@ def server_members(self) -> Set[str]:
179
224
e.g {"server.1=10.141.78.207:2888:3888:participant;0.0.0.0:2181"}
180
225
"""
181
226
with ZooKeeperClient (
182
- host = self .leader ,
227
+ host = self .zk_host ,
183
228
client_port = self .client_port ,
184
229
username = self .username ,
185
230
password = self .password ,
@@ -200,7 +245,7 @@ def config_version(self) -> int:
200
245
The zookeeper config version decoded from base16
201
246
"""
202
247
with ZooKeeperClient (
203
- host = self .leader ,
248
+ host = self .zk_host ,
204
249
client_port = self .client_port ,
205
250
username = self .username ,
206
251
password = self .password ,
@@ -221,7 +266,7 @@ def members_syncing(self) -> bool:
221
266
True if any members are syncing. Otherwise False.
222
267
"""
223
268
with ZooKeeperClient (
224
- host = self .leader ,
269
+ host = self .zk_host ,
225
270
client_port = self .client_port ,
226
271
username = self .username ,
227
272
password = self .password ,
@@ -305,7 +350,7 @@ def add_members(self, members: Iterable[str]) -> None:
305
350
306
351
# specific connection to leader
307
352
with ZooKeeperClient (
308
- host = self .leader ,
353
+ host = self .zk_host ,
309
354
client_port = self .client_port ,
310
355
username = self .username ,
311
356
password = self .password ,
@@ -330,7 +375,7 @@ def remove_members(self, members: Iterable[str]) -> None:
330
375
for member in members :
331
376
member_id = re .findall (r"server.([0-9]+)" , member )[0 ]
332
377
with ZooKeeperClient (
333
- host = self .leader ,
378
+ host = self .zk_host ,
334
379
client_port = self .client_port ,
335
380
username = self .username ,
336
381
password = self .password ,
@@ -356,7 +401,7 @@ def leader_znodes(self, path: str) -> Set[str]:
356
401
Set of all nested child zNodes
357
402
"""
358
403
with ZooKeeperClient (
359
- host = self .leader ,
404
+ host = self .zk_host ,
360
405
client_port = self .client_port ,
361
406
username = self .username ,
362
407
password = self .password ,
@@ -369,15 +414,15 @@ def leader_znodes(self, path: str) -> Set[str]:
369
414
370
415
return all_znode_children
371
416
372
- def create_znode_leader (self , path : str , acls : List [ACL ]) -> None :
417
+ def create_znode_leader (self , path : str , acls : List [ACL ] | None = None ) -> None :
373
418
"""Creates a new zNode on the current quorum leader with given ACLs.
374
419
375
420
Args:
376
421
path: the zNode path to set
377
422
acls: the ACLs to be set on that path
378
423
"""
379
424
with ZooKeeperClient (
380
- host = self .leader ,
425
+ host = self .zk_host ,
381
426
client_port = self .client_port ,
382
427
username = self .username ,
383
428
password = self .password ,
@@ -388,15 +433,15 @@ def create_znode_leader(self, path: str, acls: List[ACL]) -> None:
388
433
) as zk :
389
434
zk .create_znode (path = path , acls = acls )
390
435
391
- def set_acls_znode_leader (self , path : str , acls : List [ACL ]) -> None :
436
+ def set_acls_znode_leader (self , path : str , acls : List [ACL ] | None = None ) -> None :
392
437
"""Updates ACLs for an existing zNode on the current quorum leader.
393
438
394
439
Args:
395
440
path: the zNode path to update
396
441
acls: the new ACLs to be set on that path
397
442
"""
398
443
with ZooKeeperClient (
399
- host = self .leader ,
444
+ host = self .zk_host ,
400
445
client_port = self .client_port ,
401
446
username = self .username ,
402
447
password = self .password ,
@@ -414,7 +459,7 @@ def delete_znode_leader(self, path: str) -> None:
414
459
path: the zNode path to delete
415
460
"""
416
461
with ZooKeeperClient (
417
- host = self .leader ,
462
+ host = self .zk_host ,
418
463
client_port = self .client_port ,
419
464
username = self .username ,
420
465
password = self .password ,
@@ -432,7 +477,7 @@ def get_version(self) -> str:
432
477
String of ZooKeeper service version
433
478
"""
434
479
with ZooKeeperClient (
435
- host = self .leader ,
480
+ host = self .zk_host ,
436
481
client_port = self .client_port ,
437
482
username = self .username ,
438
483
password = self .password ,
@@ -577,7 +622,7 @@ def delete_znode(self, path: str) -> None:
577
622
return
578
623
self .client .delete (path , recursive = True )
579
624
580
- def create_znode (self , path : str , acls : List [ACL ]) -> None :
625
+ def create_znode (self , path : str , acls : List [ACL ] | None = None ) -> None :
581
626
"""Create new znode.
582
627
583
628
Args:
@@ -599,7 +644,7 @@ def get_acls(self, path: str) -> List[ACL]:
599
644
600
645
return acl_list if acl_list else []
601
646
602
- def set_acls (self , path : str , acls : List [ACL ]) -> None :
647
+ def set_acls (self , path : str , acls : List [ACL ] | None = None ) -> None :
603
648
"""Sets acls for a desired znode path.
604
649
605
650
Args:
0 commit comments