Charmed Zookeeper
- Canonical
- Databases
Channel | Revision | Published | Runs on |
---|---|---|---|
latest/stable | 53 | 29 Nov 2021 | |
latest/candidate | 56 | 29 Nov 2021 | |
latest/beta | 56 | 29 Nov 2021 | |
latest/edge | 98 | 20 Apr 2023 | |
latest/edge | 85 | 21 Oct 2022 | |
latest/edge | 65 | 09 Feb 2022 | |
latest/edge | 52 | 29 Nov 2021 | |
3/stable | 149 | 23 Oct 2024 | |
3/candidate | 149 | 21 Oct 2024 | |
3/beta | 149 | 21 Oct 2024 | |
3/edge | 152 | 17 Dec 2024 |
juju deploy zookeeper --channel 3/stable
Deploy universal operators easily with Juju, the Universal Operator Lifecycle Manager.
Platform:
charms.zookeeper.v0.cluster
-
- Last updated 08 Jul 2022
- Revision Library version 0.1
ZooKeeperCluster class and methods.
ZooKeeperCluster
is a general-purpose handler for managing the ZooKeeper peer-relation data,
and scale-up/down orchestration.
Exposed attributes include a reference to the cluster
peer relation for ZooKeeper, as well as wrappers
for grabbing units that have started the ZooKeeper service, found from the peer unit data.
Exposed methods include the setting of passwords for the cluster, ensuring units start up in increasing order
with the correct servers config, and handling the adding/removing of members from the ZooKeeper quorum.
Instances of ZooKeeperCluster
are to be created during the __init__
for the target Charm
.
or from another library. It does not set any relation data itself, and as such is reliant on the calling Charm
to set required relation data where necessary during life-cycle events.
It maintains a status
attribute for passing success/failure of called methods back to the calling Charm
.
Example minimal usage for ZooKeeperCluster
:
class ZooKeeperCharm(CharmBase):
def __init__(self, *args):
super().__init__(*args)
self.cluster = ZooKeeperCluster(self)
self.framework.observe(getattr(self.on, "install"), self._on_install)
self.framework.observe(getattr(self.on, "start"), self._on_start)
self.framework.observe(
getattr(self.on, "leader_elected"), self._on_cluster_relation_updated
)
self.framework.observe(
getattr(self.on, "cluster_relation_changed"), self._on_cluster_relation_updated
)
self.framework.observe(
getattr(self.on, "cluster_relation_joined"), self._on_cluster_relation_updated
)
self.framework.observe(
getattr(self.on, "cluster_relation_departed"), self._on_cluster_relation_updated
)
def _on_install(self, event):
unit_myid = self.cluster.get_unit_id(self.unit) + 1
# write_unit_id(id=unit_myid)
def _on_start(self, event):
# setting cluster passwords during first deployment
if self.unit.is_leader():
self.cluster.relation.data[self.app].update({"super_password": self.cluster.generate_password()})
self.cluster.relation.data[self.app].update({"sync_password": self.cluster.generate_password()})
# checking passwords have been set by leader on other units, in case they started first
if not self.cluster.passwords_set():
event.defer()
return
# units must be started in increasing unit.id order
try:
servers, unit_config = self.cluster.ready_to_start(self.unit)
except (NotUnitTurnError, UnitNotFoundError, NoPasswordError) as e:
self.unit.status = self.cluster.status
event.defer()
return
# start_zookeeper_service()
# set useful metadata for each unit
self.cluster.relation.data[self.unit].update(unit_config)
# NECESSARY - set data so that subsequent `self.cluster` commands pick up this unit as a 'started' one
self.cluster.relation.data[self.unit].update({"state": "started"})
def _on_cluster_relation_updated(self, event):
if not self.unit.is_leader():
return
# units need to exist in the app data to be iterated through for next_turn
for unit in self.cluster.started_units:
unit_id = self.cluster.get_unit_id(unit)
current_value = self.cluster.relation.data[self.app].get(str(unit_id), None)
# sets to "added" for init quorum leader, if not already exists
# may already exist if during the case of a failover of unit 0
if unit_id == 0:
self.cluster.relation.data[self.app].update(
{str(unit_id): current_value or "added"}
)
if not self.cluster.passwords_set:
event.defer()
return
# adds + removes members for all self-confirmed started units
updated_servers = self.cluster.update_cluster()
# either Active if successful, else Maintenance
self.unit.status = self.cluster.status
if self.cluster.status == ActiveStatus():
self.cluster.relation.data[self.app].update(updated_servers)
else:
# in the event some unit wasn't started/ready
event.defer()
return
Index
class UnitNotFoundError
Description
A desired unit isn't yet found in the relation data. None
class NotUnitTurnError
Description
A desired unit isn't next in line to start safely. None
class NoPasswordError
Description
Required passwords not yet set in the app data. None
class ZooKeeperCluster
Handler for managing the ZK peer-relation.
Description
Mainly for managing scale-up/down orchestration
Methods
ZooKeeperCluster. __init__( self , charm: CharmBase , client_port: int , server_port: int , election_port: int )
ZooKeeperCluster. relation( self )
Relation property to be used by both the instance and charm.
Returns
The peer relation instance
ZooKeeperCluster. peer_units( self )
Grabs all units in the current peer relation, including the running unit.
Returns
Set of units in the current peer relation, including the running unit
ZooKeeperCluster. started_units( self )
Checks peer relation units for whether they've started the ZK service.
Returns
Set of units with unit data "state" == "started". Shows only those units currently found related to the current unit.
Description
Such units are ready to join the ZK quorum if they haven't already.
ZooKeeperCluster. active_hosts( self )
Grabs all the hosts of the started units.
Returns
List of hosts for started units
ZooKeeperCluster. active_servers( self )
Grabs all the server strings of the started units.
Returns
List of ZK server strings for started units
ZooKeeperCluster. get_unit_id( unit: Unit )
Grabs the unit's ID as defined by Juju.
Arguments
The target Unit
Returns
The Juju unit ID for the unit.
e.g zookeeper/0
-> 0
ZooKeeperCluster. get_unit_from_id( self , unit_id: int )
Grabs the corresponding Unit for a given Juju unit ID
Arguments
The target unit id
Returns
The target Unit
ZooKeeperCluster. unit_config( self , unit , state: str , role: str )
Builds a collection of data useful for ZK for a given unit.
Arguments
The target Unit
, either explicitly or from it's Juju unit ID
The desired output state. "ready" or "started"
The ZK role for the unit. Default = "participant"
Returns
The generated config for the given unit. e.g for unit zookeeper/1:
{ "host": 10.121.23.23, "server_string": "server.1=host:server_port:election_port:role;localhost:clientport", "server_id": "2", "unit_id": "1", "unit_name": "zookeeper/1", "state": "ready", }
ZooKeeperCluster. update_cluster( self )
Adds and removes members from the current ZK quorum.
Returns
A mapping of Juju unit IDs and updated state for changed units To be used in updating the app data e.g {"0": "added", "1": "removed"}
Description
To be ran by the Juju leader.
After grabbing all the "started" units that the leader can see in the peer relation unit data.
Removes members not in the quorum anymore (i.e relation_departed
/leader_elected
event)
Adds new members to the quorum (i.e relation_joined
event).
ZooKeeperCluster. ready_to_start( self , unit )
Decides whether a unit should start the ZK service, and with what configuration.
Arguments
the Unit
or Juju unit ID to evaluate startability
Returns
a new-line delimited string of servers to add to a config file
unit_config
: a mapping of configuration for the given unit to be added to unit data
ZooKeeperCluster. generate_password( )
Creates randomized string for use as app passwords.
Returns
String of 32 randomized letter+digit characters
ZooKeeperCluster. passwords( self )
Gets the current super+sync passwords from the app relation data.
Returns
Tuple of super_password, sync_password
ZooKeeperCluster. passwords_set( self )
Checks that the two desired passwords are in the app relation data.
Returns
True if both passwords have been set. Otherwise False