Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Using unique unit host-alias on /etc/hosts #441

Draft
wants to merge 12 commits into
base: main
Choose a base branch
from
52 changes: 36 additions & 16 deletions lib/charms/data_platform_libs/v0/data_interfaces.py
Original file line number Diff line number Diff line change
Expand Up @@ -331,7 +331,7 @@ def _on_topic_requested(self, event: TopicRequestedEvent):

# Increment this PATCH version before using `charmcraft publish-lib` or reset
# to 0 if you are raising the major API version
LIBPATCH = 34
LIBPATCH = 35

PYDEPS = ["ops>=2.0.0"]

Expand Down Expand Up @@ -2277,7 +2277,7 @@ def __init__(


################################################################################
# Cross-charm Relatoins Data Handling and Evenets
# Cross-charm Relations Data Handling and Events
################################################################################

# Generic events
Expand All @@ -2300,7 +2300,7 @@ class RelationEventWithSecret(RelationEvent):

@property
def _secrets(self) -> dict:
"""Caching secrets to avoid fetching them each time a field is referrd.
"""Caching secrets to avoid fetching them each time a field is referred.

DON'T USE the encapsulated helper variable outside of this function
"""
Expand All @@ -2309,7 +2309,7 @@ def _secrets(self) -> dict:
return self._cached_secrets

def _get_secret(self, group) -> Optional[Dict[str, str]]:
"""Retrieveing secrets."""
"""Retrieving secrets."""
if not self.app:
return
if not self._secrets.get(group):
Expand Down Expand Up @@ -2498,6 +2498,17 @@ def version(self) -> Optional[str]:

return self.relation.data[self.relation.app].get("version")

@property
def hostname_mapping(self) -> Optional[str]:
"""Returns the hostname mapping.

A list that maps the hostnames to IP address.
"""
if not self.relation.app:
return None

return self.relation.data[self.relation.app].get("hostname-mapping")


class DatabaseCreatedEvent(AuthenticationEvent, DatabaseRequiresEvent):
"""Event emitted when a new database is created for use on this relation."""
Expand Down Expand Up @@ -2602,6 +2613,15 @@ def set_version(self, relation_id: int, version: str) -> None:
"""
self.update_relation_data(relation_id, {"version": version})

def set_hostname_mapping(self, relation_id: int, hostname_mapping: list[dict]) -> None:
"""Set hostname mapping.

Args:
relation_id: the identifier for a particular relation.
hostname_mapping: list of hostname mapping.
"""
self.update_relation_data(relation_id, {"hostname-mapping": json.dumps(hostname_mapping)})


class DatabaseProviderEventHandlers(EventHandlers):
"""Provider-side of the database relation handlers."""
Expand Down Expand Up @@ -3016,7 +3036,7 @@ class KafkaRequiresEvents(CharmEvents):
# Kafka Provides and Requires


class KafkaProvidesData(ProviderData):
class KafkaProviderData(ProviderData):
"""Provider-side of the Kafka relation."""

def __init__(self, model: Model, relation_name: str) -> None:
Expand Down Expand Up @@ -3059,12 +3079,12 @@ def set_zookeeper_uris(self, relation_id: int, zookeeper_uris: str) -> None:
self.update_relation_data(relation_id, {"zookeeper-uris": zookeeper_uris})


class KafkaProvidesEventHandlers(EventHandlers):
class KafkaProviderEventHandlers(EventHandlers):
"""Provider-side of the Kafka relation."""

on = KafkaProvidesEvents() # pyright: ignore [reportAssignmentType]

def __init__(self, charm: CharmBase, relation_data: KafkaProvidesData) -> None:
def __init__(self, charm: CharmBase, relation_data: KafkaProviderData) -> None:
super().__init__(charm, relation_data)
# Just to keep lint quiet, can't resolve inheritance. The same happened in super().__init__() above
self.relation_data = relation_data
Expand All @@ -3086,15 +3106,15 @@ def _on_relation_changed_event(self, event: RelationChangedEvent) -> None:
)


class KafkaProvides(KafkaProvidesData, KafkaProvidesEventHandlers):
class KafkaProvides(KafkaProviderData, KafkaProviderEventHandlers):
"""Provider-side of the Kafka relation."""

def __init__(self, charm: CharmBase, relation_name: str) -> None:
KafkaProvidesData.__init__(self, charm.model, relation_name)
KafkaProvidesEventHandlers.__init__(self, charm, self)
KafkaProviderData.__init__(self, charm.model, relation_name)
KafkaProviderEventHandlers.__init__(self, charm, self)


class KafkaRequiresData(RequirerData):
class KafkaRequirerData(RequirerData):
"""Requirer-side of the Kafka relation."""

def __init__(
Expand Down Expand Up @@ -3124,12 +3144,12 @@ def topic(self, value):
self._topic = value


class KafkaRequiresEventHandlers(RequirerEventHandlers):
class KafkaRequirerEventHandlers(RequirerEventHandlers):
"""Requires-side of the Kafka relation."""

on = KafkaRequiresEvents() # pyright: ignore [reportAssignmentType]

def __init__(self, charm: CharmBase, relation_data: KafkaRequiresData) -> None:
def __init__(self, charm: CharmBase, relation_data: KafkaRequirerData) -> None:
super().__init__(charm, relation_data)
# Just to keep lint quiet, can't resolve inheritance. The same happened in super().__init__() above
self.relation_data = relation_data
Expand Down Expand Up @@ -3188,7 +3208,7 @@ def _on_relation_changed_event(self, event: RelationChangedEvent) -> None:
return


class KafkaRequires(KafkaRequiresData, KafkaRequiresEventHandlers):
class KafkaRequires(KafkaRequirerData, KafkaRequirerEventHandlers):
"""Provider-side of the Kafka relation."""

def __init__(
Expand All @@ -3200,7 +3220,7 @@ def __init__(
consumer_group_prefix: Optional[str] = None,
additional_secret_fields: Optional[List[str]] = [],
) -> None:
KafkaRequiresData.__init__(
KafkaRequirerData.__init__(
self,
charm.model,
relation_name,
Expand All @@ -3209,7 +3229,7 @@ def __init__(
consumer_group_prefix,
additional_secret_fields,
)
KafkaRequiresEventHandlers.__init__(self, charm, self)
KafkaRequirerEventHandlers.__init__(self, charm, self)


# Opensearch related events
Expand Down
27 changes: 16 additions & 11 deletions lib/charms/mysql/v0/async_replication.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import typing
import uuid
from functools import cached_property
from time import sleep

from charms.mysql.v0.mysql import (
MySQLFencingWritesError,
Expand Down Expand Up @@ -221,17 +222,21 @@ def on_async_relation_broken(self, event): # noqa: C901
# reset flag to allow instances rejoining the cluster
self._charm.unit_peer_data["member-state"] = "waiting"
del self._charm.unit_peer_data["unit-initialized"]
if self._charm.unit.is_leader():
self._charm.app.status = BlockedStatus("Recreate or rejoin cluster.")
logger.info(
"\n\tThis is a replica cluster and will be dissolved.\n"
"\tThe cluster can be recreated with the `recreate-cluster` action.\n"
"\tAlternatively the cluster can be rejoined to the cluster set."
)
# reset the cluster node count flag
del self._charm.app_peer_data["units-added-to-cluster"]
# set flag to persist removed from cluster-set state
self._charm.app_peer_data["removed-from-cluster-set"] = "true"
if not self._charm.unit.is_leader():
# delay non leader to avoid `update_status` running before
# leader updates app peer data
sleep(10)
return
self._charm.app.status = BlockedStatus("Recreate or rejoin cluster.")
logger.info(
"\n\tThis is a replica cluster and will be dissolved.\n"
"\tThe cluster can be recreated with the `recreate-cluster` action.\n"
"\tAlternatively the cluster can be rejoined to the cluster set."
)
# reset the cluster node count flag
del self._charm.app_peer_data["units-added-to-cluster"]
# set flag to persist removed from cluster-set state
self._charm.app_peer_data["removed-from-cluster-set"] = "true"

elif self.role.cluster_role == "primary":
if self._charm.unit.is_leader():
Expand Down
4 changes: 2 additions & 2 deletions lib/charms/mysql/v0/mysql.py
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ def wait_until_mysql_connection(self) -> None:

# Increment this PATCH version before using `charmcraft publish-lib` or reset
# to 0 if you are raising the major API version
LIBPATCH = 58
LIBPATCH = 59

UNIT_TEARDOWN_LOCKNAME = "unit-teardown"
UNIT_ADD_LOCKNAME = "unit-add"
Expand Down Expand Up @@ -1361,7 +1361,7 @@ def create_replica_cluster(
"communicationStack": "MySQL",
}

if donor:
if donor and method == "clone":
options["cloneDonor"] = donor

commands = (
Expand Down
34 changes: 12 additions & 22 deletions poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ boto3 = "^1.28.23"
pyopenssl = "^24.0.0"
typing_extensions = "^4.7.1"
jinja2 = "^3.1.2"
python-hosts = "^1.0.6"

[tool.poetry.group.charm-libs.dependencies]
# data_platform_libs/v0/data_interfaces.py
Expand Down
13 changes: 12 additions & 1 deletion src/charm.py
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,9 @@ def _on_start(self, event: StartEvent) -> None:

self.unit.status = MaintenanceStatus("Setting up cluster node")

if not self.hostname_resolution.is_unit_in_hosts:
self.hostname_resolution.update_etc_hosts(None)

try:
self.workload_initialise()
except MySQLConfigureMySQLUsersError:
Expand Down Expand Up @@ -526,7 +529,7 @@ def _on_cos_agent_relation_broken(self, event: RelationBrokenEvent) -> None:
def _mysql(self):
"""Returns an instance of the MySQL object."""
return MySQL(
self.unit_fqdn,
self.unit_host_alias,
self.app_peer_data["cluster-name"],
self.app_peer_data["cluster-set-domain-name"],
self.get_secret("app", ROOT_PASSWORD_KEY), # pyright: ignore [reportArgumentType]
Expand Down Expand Up @@ -562,6 +565,14 @@ def unit_fqdn(self) -> str:
"""Returns the unit's FQDN."""
return socket.getfqdn()

@property
def unit_host_alias(self) -> str:
"""Returns the unit's host alias.

The format is <unit_label>.<model_uuid>
"""
return self.unit_label + "." + self.model.uuid

@property
def restart_peers(self) -> Optional[ops.model.Relation]:
"""Retrieve the peer relation."""
Expand Down
Loading
Loading