Skip to content

Commit

Permalink
Include UserFacingClusterConfig (#39)
Browse files Browse the repository at this point in the history
* Include UserFacingClusterConfig

* Update tox.ini

* Update tox.ini
  • Loading branch information
mateoflorido authored Mar 5, 2024
1 parent a32b33a commit 75eade7
Show file tree
Hide file tree
Showing 4 changed files with 161 additions and 36 deletions.
153 changes: 137 additions & 16 deletions charms/worker/k8s/lib/charms/k8s/v0/k8sd_api_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -162,20 +162,142 @@ class ClusterMember(BaseModel):

name: str
address: str
cluster_role: str = Field(..., alias="cluster-role")
datastore_role: str = Field(..., alias="datastore-role")
cluster_role: str = Field(None, alias="cluster-role")
datastore_role: str = Field(None, alias="datastore-role")


class ClusterComponent(BaseModel):
"""Represents a component in the k8sd cluster.
class DNSConfig(BaseModel):
"""Configuration for the DNS settings of the cluster.
Attributes:
name (str): Name of the component.
status (str): Current status of the component.
enabled: Optional flag which.
cluster_domain: The domain name of the cluster.
service_ip: The IP address of the DNS service within the cluster.
upstream_nameservers: List of upstream nameservers for DNS resolution.
"""

name: str
status: str
enabled: Optional[bool]
cluster_domain: Optional[str] = Field(None, alias="cluster-domain")
service_ip: Optional[str] = Field(None, alias="service-ip")
upstream_nameservers: Optional[List[str]] = Field(None, alias="upstream-nameservers")


class IngressConfig(BaseModel):
"""Configuration for the ingress settings of the cluster.
Attributes:
enabled: Optional flag which represents the status of Ingress.
default_tls_secret: The default TLS secret for ingress.
enable_proxy_protocol: Optional flag to enable or disable proxy protocol.
"""

enabled: Optional[bool]
default_tls_secret: Optional[str] = Field(None, alias="default-tls-secret")
enable_proxy_protocol: Optional[bool] = Field(None, alias="enable-proxy-protocol")


class LoadBalancerConfig(BaseModel):
"""Configuration for the load balancer settings of the cluster.
Attributes:
enabled: Optional flag which represents the status of LoadBalancer.
cidrs: List of CIDR blocks for the load balancer.
l2_enabled: Optional flag to enable or disable layer 2 functionality.
l2_interfaces: List of layer 2 interfaces for the load balancer.
bgp_enabled: Optional flag to enable or disable BGP.
bgp_local_asn: The local ASN for BGP configuration.
bgp_peer_address: The peer address for BGP configuration.
bgp_peer_asn: The peer ASN for BGP configuration.
bgp_peer_port: The port for BGP peering.
"""

enabled: Optional[bool]
cidrs: Optional[List[str]] = Field(None, alias="cidrs")
l2_enabled: Optional[bool] = Field(None, alias="l2-enabled")
l2_interfaces: Optional[List[str]] = Field(None, alias="l2-interfaces")
bgp_enabled: Optional[bool] = Field(None, alias="bgp-enabled")
bgp_local_asn: Optional[int] = Field(None, alias="bgp-local-asn")
bgp_peer_address: Optional[str] = Field(None, alias="bgp-peer-address")
bgp_peer_asn: Optional[int] = Field(None, alias="bgp-peer-asn")
bgp_peer_port: Optional[int] = Field(None, alias="bgp-peer-port")


class LocalStorageConfig(BaseModel):
"""Configuration for the local storage settings of the cluster.
Attributes:
enabled: Optional flag which represents the status of Storage.
local_path: The local path for storage.
reclaim_policy: The policy for reclaiming local storage.
set_default: Optional flag to set this as the default storage option.
"""

enabled: Optional[bool]
local_path: Optional[str] = Field(None, alias="local-path")
reclaim_policy: Optional[str] = Field(None, alias="reclaim-policy")
set_default: Optional[bool] = Field(None, alias="set-default")


class NetworkConfig(BaseModel):
"""Configuration for the network settings of the cluster.
Attributes:
enabled: Optional flag which represents the status of Network.
"""

enabled: Optional[bool]


class GatewayConfig(BaseModel):
"""Configuration for the gateway settings of the cluster.
Attributes:
enabled: Optional flag which represents the status of Gateway.
"""

enabled: Optional[bool]


class MetricsServerConfig(BaseModel):
"""Configuration for the metrics server settings of the cluster.
Attributes:
enabled: Optional flag which represents the status of MetricsServer.
"""

enabled: Optional[bool]


class UserFacingClusterConfig(BaseModel):
"""Aggregated configuration model for the user-facing aspects of a cluster.
Attributes:
network: Network configuration for the cluster.
dns: DNS configuration for the cluster.
ingress: Ingress configuration for the cluster.
load_balancer: Load balancer configuration for the cluster.
local_storage: Local storage configuration for the cluster.
gateway: Gateway configuration for the cluster.
metrics_server: Metrics server configuration for the cluster.
"""

network: Optional[NetworkConfig] = None
dns: Optional[DNSConfig] = None
ingress: Optional[IngressConfig] = None
load_balancer: Optional[LoadBalancerConfig] = Field(None, alias="load-balancer")
local_storage: Optional[LocalStorageConfig] = Field(None, alias="local-storage")
gateway: Optional[GatewayConfig]
metrics_server: Optional[MetricsServerConfig] = Field(None, alias="metrics-server")


class UpdateClusterConfigRequest(BaseModel):
"""Request model for updating Cluster config.
Attributes:
config (Optional[UserFacingClusterConfig]): The cluster configuration.
"""

config: UserFacingClusterConfig


class ClusterStatus(BaseModel):
Expand All @@ -184,12 +306,12 @@ class ClusterStatus(BaseModel):
Attributes:
ready (bool): Indicates if the cluster is ready.
members (List[ClusterMember]): List of members in the cluster.
components (List[ClusterComponent]): List of components in the cluster.
config (Optional[UserFacingClusterConfig]): information about the cluster configuration.
"""

ready: bool
ready: Optional[bool] = False
members: Optional[List[ClusterMember]]
components: Optional[List[ClusterComponent]]
config: Optional[UserFacingClusterConfig]


class ClusterMetadata(BaseModel):
Expand Down Expand Up @@ -418,15 +540,14 @@ def remove_node(self, name: str, force: bool = True):
body = {"name": name, "force": force}
self._send_request(endpoint, "POST", EmptyResponse, body)

def enable_component(self, name: str, enable: bool):
def update_cluster_config(self, config: UpdateClusterConfigRequest):
"""Enable or disable a k8s component.
Args:
name (str): Name of the component.
enable (bool): True to enable, False to disable the component.
config (UpdateClusterConfigRequest): The cluster configuration.
"""
endpoint = f"/1.0/k8sd/components/{name}"
body = {"status": "enabled" if enable else "disabled"}
endpoint = "/1.0/k8sd/cluster/config"
body = config.dict(exclude_none=True)
self._send_request(endpoint, "PUT", EmptyResponse, body)

def is_cluster_bootstrapped(self) -> bool:
Expand Down
19 changes: 13 additions & 6 deletions charms/worker/k8s/src/charm.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,14 @@
from charms.contextual_status import WaitingStatus, on_error
from charms.grafana_agent.v0.cos_agent import COSAgentProvider
from charms.k8s.v0.k8sd_api_manager import (
DNSConfig,
InvalidResponseError,
K8sdAPIManager,
K8sdConnectionError,
NetworkConfig,
UnixSocketConnectionFactory,
UpdateClusterConfigRequest,
UserFacingClusterConfig,
)
from charms.node_base import LabelMaker
from charms.operator_libs_linux.v2.snap import SnapError, SnapState
Expand Down Expand Up @@ -274,12 +278,15 @@ def _create_cos_tokens(self):
@on_error(
WaitingStatus("Waiting for enable components"), InvalidResponseError, K8sdConnectionError
)
def _enable_components(self):
def _enable_functionalities(self):
"""Enable necessary components for the Kubernetes cluster."""
status.add(ops.MaintenanceStatus("Enabling DNS"))
self.api_manager.enable_component("dns", True)
status.add(ops.MaintenanceStatus("Enabling Network"))
self.api_manager.enable_component("network", True)
status.add(ops.MaintenanceStatus("Enabling DNS and Network"))
dns_config = DNSConfig(enabled=True)
network_config = NetworkConfig(enabled=True)
user_cluster_config = UserFacingClusterConfig(dns=dns_config, network=network_config)
update_request = UpdateClusterConfigRequest(config=user_cluster_config)

self.api_manager.update_cluster_config(update_request)

def _get_scrape_jobs(self):
"""Retrieve the Prometheus Scrape Jobs.
Expand Down Expand Up @@ -367,7 +374,7 @@ def _reconcile(self, event):
self._check_k8sd_ready()
if self.lead_control_plane:
self._bootstrap_k8s_snap()
self._enable_components()
self._enable_functionalities()
self._create_cluster_tokens()
self._create_cos_tokens()
self._apply_cos_requirements()
Expand Down
2 changes: 1 addition & 1 deletion charms/worker/k8s/tests/unit/test_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ def mock_reconciler_handlers(harness):
if harness.charm.is_control_plane:
handler_names |= {
"_bootstrap_k8s_snap",
"_enable_components",
"_enable_functionalities",
"_create_cluster_tokens",
"_create_cos_tokens",
"_apply_cos_requirements",
Expand Down
23 changes: 10 additions & 13 deletions charms/worker/k8s/tests/unit/test_k8sd_api_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,15 @@
AuthTokenResponse,
BaseRequestModel,
CreateJoinTokenResponse,
DNSConfig,
EmptyResponse,
InvalidResponseError,
K8sdAPIManager,
K8sdConnectionError,
TokenMetadata,
UnixSocketHTTPConnection,
UpdateClusterConfigRequest,
UserFacingClusterConfig,
)


Expand Down Expand Up @@ -208,24 +211,18 @@ def test_remove_node(self, mock_send_request):
)

@patch("lib.charms.k8s.v0.k8sd_api_manager.K8sdAPIManager._send_request")
def test_enable_component__enable(self, mock_send_request):
def test_update_cluster_config(self, mock_send_request):
mock_send_request.return_value = EmptyResponse(status_code=200, type="test", error_code=0)

self.api_manager.enable_component("foo", True)
dns_config = DNSConfig(enabled=True)
user_config = UserFacingClusterConfig(dns=dns_config)
request = UpdateClusterConfigRequest(config=user_config)
self.api_manager.update_cluster_config(request)
mock_send_request.assert_called_once_with(
"/1.0/k8sd/components/foo",
"/1.0/k8sd/cluster/config",
"PUT",
EmptyResponse,
{"status": "enabled"},
)

@patch("lib.charms.k8s.v0.k8sd_api_manager.K8sdAPIManager._send_request")
def test_enable_component__disable(self, mock_send_request):
mock_send_request.return_value = EmptyResponse(status_code=200, type="test", error_code=0)

self.api_manager.enable_component("foo", False)
mock_send_request.assert_called_once_with(
"/1.0/k8sd/components/foo", "PUT", EmptyResponse, {"status": "disabled"}
{"config": {"dns": {"enabled": True}}},
)

@patch("lib.charms.k8s.v0.k8sd_api_manager.K8sdAPIManager._send_request")
Expand Down

0 comments on commit 75eade7

Please sign in to comment.