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

Port over charm lib changes from k8s charm relating to pod reschdule during cluster setup #518

Merged
merged 6 commits into from
Sep 13, 2024
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions lib/charms/mysql/v0/backups.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,9 +60,9 @@ def is_unit_blocked(self) -> bool:
MySQLDeleteTempRestoreDirectoryError,
MySQLEmptyDataDirectoryError,
MySQLExecuteBackupCommandsError,
MySQLGetMemberStateError,
MySQLInitializeJujuOperationsTableError,
MySQLKillSessionError,
MySQLNoMemberStateError,
MySQLOfflineModeAndHiddenInstanceExistsError,
MySQLPrepareBackupForRestoreError,
MySQLRescanClusterError,
Expand All @@ -73,6 +73,7 @@ def is_unit_blocked(self) -> bool:
MySQLSetInstanceOptionError,
MySQLStartMySQLDError,
MySQLStopMySQLDError,
MySQLUnableToGetMemberStateError,
)
from charms.mysql.v0.s3_helpers import (
fetch_and_check_existence_of_s3_path,
Expand All @@ -99,7 +100,7 @@ def is_unit_blocked(self) -> bool:

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


if typing.TYPE_CHECKING:
Expand Down Expand Up @@ -339,7 +340,7 @@ def _can_unit_perform_backup(self) -> Tuple[bool, Optional[str]]:

try:
state, role = self.charm._mysql.get_member_state()
except MySQLGetMemberStateError:
except (MySQLNoMemberStateError, MySQLUnableToGetMemberStateError):
return False, "Error obtaining member state"

if role == "primary" and self.charm.app.planned_units() > 1:
Expand Down
50 changes: 43 additions & 7 deletions lib/charms/mysql/v0/mysql.py
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ def wait_until_mysql_connection(self) -> None:
# Increment this major API version when introducing breaking changes
LIBAPI = 0

LIBPATCH = 72
LIBPATCH = 73

UNIT_TEARDOWN_LOCKNAME = "unit-teardown"
UNIT_ADD_LOCKNAME = "unit-add"
Expand Down Expand Up @@ -276,8 +276,12 @@ class MySQLGrantPrivilegesToUserError(Error):
"""Exception raised when there is an issue granting privileges to user."""


class MySQLGetMemberStateError(Error):
"""Exception raised when there is an issue getting member state."""
class MySQLNoMemberStateError(Error):
"""Exception raised when there is no member state."""


class MySQLUnableToGetMemberStateError(Error):
"""Exception raised when unable to get member state."""


class MySQLGetClusterEndpointsError(Error):
Expand Down Expand Up @@ -620,6 +624,26 @@ def cluster_initialized(self) -> bool:

return False

@property
def only_one_cluster_node_thats_uninitialized(self) -> Optional[bool]:
"""Check if only a single cluster node exists across all units."""
if not self.app_peer_data.get("cluster-name"):
return None

total_cluster_nodes = 0
for unit in self.app_units:
total_cluster_nodes += self._mysql.get_cluster_node_count(
from_instance=self.get_unit_address(unit)
)

total_online_cluster_nodes = 0
for unit in self.app_units:
total_online_cluster_nodes += self._mysql.get_cluster_node_count(
from_instance=self.get_unit_address(unit), node_status=MySQLMemberState["ONLINE"]
)

return total_cluster_nodes == 1 and total_online_cluster_nodes == 0

@property
def cluster_fully_initialized(self) -> bool:
"""Returns True if the cluster is fully initialized.
Expand Down Expand Up @@ -1728,6 +1752,18 @@ def is_instance_configured_for_innodb(
)
return False

def drop_group_replication_metadata_schema(self) -> None:
"""Drop the group replication metadata schema from current unit."""
commands = (
f"shell.connect('{self.instance_def(self.server_config_user)}')",
"dba.drop_metadata_schema()",
)

try:
self._run_mysqlsh_script("\n".join(commands))
except MySQLClientError:
logger.exception("Failed to drop group replication metadata schema")

def are_locks_acquired(self, from_instance: Optional[str] = None) -> bool:
"""Report if any topology change is being executed."""
commands = (
Expand Down Expand Up @@ -2356,13 +2392,13 @@ def get_member_state(self) -> Tuple[str, str]:
logger.error(
"Failed to get member state: mysqld daemon is down",
)
raise MySQLGetMemberStateError(e.message)
raise MySQLUnableToGetMemberStateError(e.message)

# output is like:
# 'MEMBER_STATE\tMEMBER_ROLE\tMEMBER_ID\t@@server_uuid\nONLINE\tPRIMARY\t<uuid>\t<uuid>\n'
lines = output.strip().lower().split("\n")
if len(lines) < 2:
raise MySQLGetMemberStateError("No member state retrieved")
raise MySQLNoMemberStateError("No member state retrieved")

if len(lines) == 2:
# Instance just know it own state
Expand All @@ -2378,7 +2414,7 @@ def get_member_state(self) -> Tuple[str, str]:
# filter server uuid
return results[0], results[1] or "unknown"

raise MySQLGetMemberStateError("No member state retrieved")
raise MySQLNoMemberStateError("No member state retrieved")

def is_cluster_replica(self, from_instance: Optional[str] = None) -> Optional[bool]:
"""Check if this cluster is a replica in a cluster set."""
Expand Down Expand Up @@ -2435,7 +2471,7 @@ def hold_if_recovering(self) -> None:
while True:
try:
member_state, _ = self.get_member_state()
except MySQLGetMemberStateError:
except (MySQLNoMemberStateError, MySQLUnableToGetMemberStateError):
break
if member_state == MySQLMemberState.RECOVERING:
logger.debug("Unit is recovering")
Expand Down
8 changes: 4 additions & 4 deletions lib/charms/tempo_k8s/v2/tracing.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ def __init__(self, *args):
)
from ops.framework import EventSource, Object
from ops.model import ModelError, Relation
from pydantic import BaseModel, ConfigDict, Field
from pydantic import BaseModel, Field

# The unique Charmhub library identifier, never change it
LIBID = "12977e9aa0b34367903d8afeb8c3d85d"
Expand All @@ -107,7 +107,7 @@ def __init__(self, *args):

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

PYDEPS = ["pydantic"]

Expand Down Expand Up @@ -338,7 +338,7 @@ class Config:
class ProtocolType(BaseModel):
"""Protocol Type."""

model_config = ConfigDict(
model_config = ConfigDict( # type: ignore
# Allow serializing enum values.
use_enum_values=True
)
Expand Down Expand Up @@ -925,7 +925,7 @@ def get_endpoint(
def charm_tracing_config(
endpoint_requirer: TracingEndpointRequirer, cert_path: Optional[Union[Path, str]]
) -> Tuple[Optional[str], Optional[str]]:
"""Utility function to determine the charm_tracing config you will likely want.
"""Return the charm_tracing config you likely want.

If no endpoint is provided:
disable charm tracing.
Expand Down
1 change: 0 additions & 1 deletion poetry.lock

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

5 changes: 3 additions & 2 deletions src/charm.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,14 @@
MySQLCreateClusterError,
MySQLCreateClusterSetError,
MySQLGetClusterPrimaryAddressError,
MySQLGetMemberStateError,
MySQLGetMySQLVersionError,
MySQLInitializeJujuOperationsTableError,
MySQLLockAcquisitionError,
MySQLNoMemberStateError,
MySQLPluginInstallError,
MySQLRebootFromCompleteOutageError,
MySQLSetClusterPrimaryError,
MySQLUnableToGetMemberStateError,
)
from charms.mysql.v0.tls import MySQLTLS
from charms.rolling_ops.v0.rollingops import RollingOpsManager
Expand Down Expand Up @@ -511,7 +512,7 @@ def _on_update_status(self, _) -> None: # noqa: C901
state, role = self._mysql.get_member_state()
self.unit_peer_data["member-role"] = role
self.unit_peer_data["member-state"] = state
except MySQLGetMemberStateError:
except (MySQLNoMemberStateError, MySQLUnableToGetMemberStateError):
role = self.unit_peer_data["member-role"] = "unknown"
state = self.unit_peer_data["member-state"] = "unreachable"
logger.info(f"Unit workload member-state is {state} with member-role {role}")
Expand Down
4 changes: 2 additions & 2 deletions tests/unit/test_backups.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@
MySQLDeleteTempRestoreDirectoryError,
MySQLEmptyDataDirectoryError,
MySQLExecuteBackupCommandsError,
MySQLGetMemberStateError,
MySQLInitializeJujuOperationsTableError,
MySQLNoMemberStateError,
MySQLOfflineModeAndHiddenInstanceExistsError,
MySQLPrepareBackupForRestoreError,
MySQLRescanClusterError,
Expand Down Expand Up @@ -356,7 +356,7 @@ def test_can_unit_perform_backup_failure(
self.harness.remove_relation_unit(self.peer_relation_id, "mysql/1")

# test error getting member state
_get_member_state.side_effect = MySQLGetMemberStateError
_get_member_state.side_effect = MySQLNoMemberStateError

success, error_message = self.mysql_backups._can_unit_perform_backup()
self.assertFalse(success)
Expand Down
19 changes: 17 additions & 2 deletions tests/unit/test_mysql.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,12 @@
MySQLExecuteBackupCommandsError,
MySQLGetAutoTuningParametersError,
MySQLGetClusterPrimaryAddressError,
MySQLGetMemberStateError,
MySQLGetMySQLVersionError,
MySQLGetRouterUsersError,
MySQLGetVariableError,
MySQLInitializeJujuOperationsTableError,
MySQLMemberState,
MySQLNoMemberStateError,
MySQLOfflineModeAndHiddenInstanceExistsError,
MySQLPluginInstallError,
MySQLPrepareBackupForRestoreError,
Expand Down Expand Up @@ -463,6 +463,21 @@ def test_is_instance_configured_for_innodb(self, _run_mysqlsh_script):
)
self.assertFalse(is_instance_configured)

@patch("charms.mysql.v0.mysql.MySQLBase._run_mysqlsh_script")
def test_drop_group_replicatoin_metadata_schema(self, _run_mysqlsh_script):
shayancanonical marked this conversation as resolved.
Show resolved Hide resolved
"""Test with no exceptions while calling the drop_group_replication_metadata_schema method."""
# test successfully configured instance
drop_group_replication_metadata_schema_commands = (
"shell.connect('serverconfig:[email protected]:33062')",
"dba.drop_metadata_schema()",
)

self.mysql.drop_group_replication_metadata_schema()

_run_mysqlsh_script.assert_called_once_with(
"\n".join(drop_group_replication_metadata_schema_commands)
)

@patch("charms.mysql.v0.mysql.MySQLBase._run_mysqlsh_script")
def test_is_instance_configured_for_innodb_exceptions(self, _run_mysqlsh_script):
"""Test an exception while calling the is_instance_configured_for_innodb method."""
Expand Down Expand Up @@ -835,7 +850,7 @@ def test_get_member_state(self, _run_mysqlcli_script):

_run_mysqlcli_script.return_value = "MEMBER_STATE\tMEMBER_ROLE\tMEMBER_ID\t@@server_uuid\n"

with self.assertRaises(MySQLGetMemberStateError):
with self.assertRaises(MySQLNoMemberStateError):
self.mysql.get_member_state()

@patch("charms.mysql.v0.mysql.MySQLBase._run_mysqlsh_script")
Expand Down
Loading