Skip to content

Commit

Permalink
add machineset resource implementation
Browse files Browse the repository at this point in the history
This patch adds the abilty to create the 'machineset' resource.
  • Loading branch information
Guy Afik committed Aug 16, 2021
1 parent 8c1f60b commit 4e8d29b
Showing 1 changed file with 225 additions and 0 deletions.
225 changes: 225 additions & 0 deletions ocp_resources/machineset.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,225 @@
import logging

from ocp_resources.resource import NamespacedResource, ResourceEditor
from ocp_resources.utils import TimeoutExpiredError, TimeoutSampler


LOGGER = logging.getLogger(__name__)


TIMEOUT = 300


class MachineSetNotReadyError(Exception):
pass


class MachineSet(NamespacedResource):
"""
Machineset object.
Args:
cluster_name (str): OpenShift cluster name.
machine_role (str): machine role. e.g.: 'worker'.
machine_type (str): machine role. e.g.: 'worker'.
replicas (int): amount of replicas the machine-set will have.
provider_spec (dict): provider spec information.
wait_ready_state (bool): True if to wait for machine-set to be 'ready' state, False otherwise.
provider spec example:
{
"value": {
"apiVersion": "ovirtproviderconfig.machine.openshift.io/v1beta1",
"auto_pinning_policy": "none",
"cluster_id": 5612af70-f4f5-455d-b7df-fbad66accc38,
"cpu": {
"cores": 8,
"sockets": 1,
"threads": 1
},
"credentialsSecret": {
"name": "ovirt-credentials"
},
"kind": "OvirtMachineProviderSpec",
"memory_mb": 16000,
"os_disk": {
"size_gb": 31
},
"template_name": "ge2n1-gcwmg-rhcos",
"type": "server",
"userDataSecret": {
"name": "worker-user-data"
}
}
}
"""

api_group = NamespacedResource.ApiGroup.MACHINE_OPENSHIFT_IO

def __init__(
self,
cluster_name=None,
name=None,
namespace=None,
teardown=True,
client=None,
machine_role="worker",
machine_type="worker",
replicas=1,
provider_spec=None,
yaml_file=None,
wait_ready_state=True,
):
super().__init__(
name=name,
namespace=namespace,
client=client,
teardown=teardown,
yaml_file=yaml_file,
)
self.replicas = replicas
self.cluster_name = cluster_name
self.machine_role = machine_role
self.machine_type = machine_type
self.provider_spec = provider_spec or {}
self.wait_ready_state = wait_ready_state

def __enter__(self):
super().__enter__()
if self.wait_ready_state and not self.wait(timeout=self.replicas * TIMEOUT):
raise MachineSetNotReadyError(
f"Machine-set {self.name} has not reached 'ready' state"
)
return self

@property
def available_replicas(self):
return self.instance.status.availableReplicas

@property
def ready_replicas(self):
return self.instance.status.readyReplicas

@property
def desired_replicas(self):
return self.instance.status.replicas

@property
def provider_spec_value(self):
return self.instance.spec.template.spec.providerSpec.value

def wait(self, timeout=TIMEOUT, sleep=1):
"""
Wait for machine-set to reach 'ready' state.
Args:
timeout (int): maximum time to wait for the 'ready' state.
sleep (int): sleep time between each sample.
Returns:
bool: True if machine-set reached 'ready' state, False otherwise.
"""
try:
for ready_replicas in TimeoutSampler(
wait_timeout=timeout, sleep=sleep, func=lambda: self.ready_replicas
):
if ready_replicas and ready_replicas == self.desired_replicas:
return True
except TimeoutExpiredError:
LOGGER.error(
f"Ready replicas: {self.ready_replicas}, desired replicas: {self.desired_replicas}"
)
return False

def scale(self, replicas, wait_timeout=300, sleep=1):
"""
Scale down/up a machine-set.
Args:
replicas (int): num of replicas to scale to.
wait_timeout (int): maximum time to wait for scaling the machine-set.
sleep (int): sleep time between each sample of the machine-set state.
Returns:
bool: True if scaling the machine set was successful, False otherwise.
"""
LOGGER.info(
f"Scale machine-set from {self.desired_replicas} replicas to {replicas} replicas"
)

return self.update(
patches={"spec": {"replicas": replicas}},
wait_timeout=wait_timeout,
sleep=sleep,
)

def update(self, patches, wait_timeout=TIMEOUT, sleep=1):
"""
Update machine-set.
Args:
patches (dict): information to update the machine-set.
wait_timeout (int): maximum time to wait for machine-set to be ready after updating.
sleep (int): sleep time between each sample of the machine-set state.
Returns:
bool: True if updating the machine-set was successful, False otherwise.
"""
LOGGER.info(f"Update machine-set {self.name} to {patches}")
ResourceEditor(patches={self: patches}).update()
return self.wait(timeout=wait_timeout, sleep=sleep)

def to_dict(self):
res = super().to_dict()
if self.yaml_file:
return res

_spec, _metadata, _labels, _template = (
"spec",
"metadata",
"labels",
"template",
)
(
_cluster_api_cluster,
_cluster_api_machine_role,
_cluster_api_machine_type,
_cluster_api_machineset,
) = (
"cluster-api-cluster",
"cluster-api-machine-role",
"cluster-api-machine-type",
"cluster-api-machineset",
)

res[_metadata].setdefault(_labels, {}).update(
{
f"{self.api_group}/{_cluster_api_cluster}": self.cluster_name,
f"{self.api_group}/{_cluster_api_machine_role}": self.machine_role,
f"{self.api_group}/{_cluster_api_machine_type}": self.machine_type,
}
)

res.setdefault(_spec, {})
res[_spec].setdefault("replicas", self.replicas)
res[_spec].setdefault("selector", {}).setdefault("matchLabels", {}).update(
{
f"{self.api_group}/{_cluster_api_cluster}": self.cluster_name,
f"{self.api_group}/{_cluster_api_machineset}": f"{self.cluster_name}-{self.machine_role}",
}
)
res[_spec].setdefault(_template, {}).setdefault(_metadata, {}).setdefault(
_labels, {}
).update(
{
f"{self.api_group}/{_cluster_api_cluster}": self.cluster_name,
f"{self.api_group}/{_cluster_api_machine_role}": self.machine_role,
f"{self.api_group}/{_cluster_api_machine_type}": self.machine_type,
f"{self.api_group}/{_cluster_api_machineset}": f"{self.cluster_name}-{self.machine_role}",
}
)
res[_spec][_template].setdefault(_spec, {}).update(
{"providerSpec": self.provider_spec}
)
return res

0 comments on commit 4e8d29b

Please sign in to comment.