magnum/magnum/tests/unit/drivers/cluster_api/test_driver.py

1501 lines
55 KiB
Python

# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from unittest import mock
from magnum.common import exception
from magnum.common.x509 import operations as x509
from magnum.conductor.handlers.common import cert_manager
from magnum import conf
from magnum.drivers.cluster_api import app_creds
from magnum.drivers.cluster_api import driver
from magnum.drivers.cluster_api import helm
from magnum.drivers.cluster_api import kubernetes
from magnum.drivers.common import k8s_monitor
from magnum.objects import fields
from magnum.tests.unit.db import base
from magnum.tests.unit.objects import utils as obj_utils
CONF = conf.CONF
class ClusterAPIDriverTest(base.DbTestCase):
def setUp(self):
super(ClusterAPIDriverTest, self).setUp()
self.driver = driver.Driver()
self.cluster_obj = obj_utils.create_test_cluster(
self.context,
name="cluster_example_$A",
master_flavor_id="flavor_small",
flavor_id="flavor_medium",
stack_id="cluster-example-a-111111111111",
)
def test_provides(self):
self.assertEqual(
[{"server_type": "vm", "os": "ubuntu", "coe": "kubernetes"}],
self.driver.provides,
)
@mock.patch.object(driver.Driver, "_update_status_deleting")
@mock.patch.object(driver.Driver, "_update_status_updating")
@mock.patch.object(driver.Driver, "_update_all_nodegroups_status")
@mock.patch.object(driver.Driver, "_get_capi_cluster")
def test_update_cluster_status_creating(
self, mock_capi, mock_ng, mock_update, mock_delete
):
mock_ng.return_value = True
mock_capi.return_value = {"spec": {}}
self.cluster_obj.status = fields.ClusterStatus.CREATE_IN_PROGRESS
self.driver.update_cluster_status(self.context, self.cluster_obj)
mock_ng.assert_called_once_with(self.cluster_obj)
mock_update.assert_not_called()
mock_delete.assert_not_called()
@mock.patch.object(driver.Driver, "_update_status_deleting")
@mock.patch.object(driver.Driver, "_update_status_updating")
@mock.patch.object(driver.Driver, "_update_all_nodegroups_status")
@mock.patch.object(driver.Driver, "_get_capi_cluster")
def test_update_cluster_status_creating_not_found(
self, mock_capi, mock_ng, mock_update, mock_delete
):
mock_ng.return_value = True
mock_capi.return_value = None
self.cluster_obj.status = fields.ClusterStatus.CREATE_IN_PROGRESS
self.driver.update_cluster_status(self.context, self.cluster_obj)
mock_ng.assert_called_once_with(self.cluster_obj)
mock_update.assert_not_called()
mock_delete.assert_not_called()
@mock.patch.object(driver.Driver, "_update_status_deleting")
@mock.patch.object(driver.Driver, "_update_status_updating")
@mock.patch.object(driver.Driver, "_update_all_nodegroups_status")
@mock.patch.object(driver.Driver, "_get_capi_cluster")
def test_update_cluster_status_created(
self, mock_capi, mock_ng, mock_update, mock_delete
):
mock_ng.return_value = False
mock_capi.return_value = {"spec": {}}
self.cluster_obj.status = fields.ClusterStatus.CREATE_IN_PROGRESS
self.driver.update_cluster_status(self.context, self.cluster_obj)
mock_ng.assert_called_once_with(self.cluster_obj)
mock_update.assert_called_once_with(self.cluster_obj, {"spec": {}})
mock_delete.assert_not_called()
@mock.patch.object(driver.Driver, "_update_status_deleting")
@mock.patch.object(driver.Driver, "_update_status_updating")
@mock.patch.object(driver.Driver, "_update_all_nodegroups_status")
@mock.patch.object(driver.Driver, "_get_capi_cluster")
def test_update_cluster_status_deleted(
self, mock_capi, mock_ng, mock_update, mock_delete
):
mock_capi.return_value = None
self.cluster_obj.status = fields.ClusterStatus.DELETE_IN_PROGRESS
self.driver.update_cluster_status(self.context, self.cluster_obj)
mock_ng.assert_called_once_with(self.cluster_obj)
mock_update.assert_not_called()
mock_delete.assert_called_once_with(self.context, self.cluster_obj)
@mock.patch.object(driver.Driver, "_update_status_deleting")
@mock.patch.object(driver.Driver, "_update_status_updating")
@mock.patch.object(driver.Driver, "_update_all_nodegroups_status")
@mock.patch.object(driver.Driver, "_get_capi_cluster")
def test_update_cluster_status_deleting(
self, mock_capi, mock_ng, mock_update, mock_delete
):
mock_capi.return_value = {"spec": {}}
self.cluster_obj.status = fields.ClusterStatus.DELETE_IN_PROGRESS
self.driver.update_cluster_status(self.context, self.cluster_obj)
mock_ng.assert_called_once_with(self.cluster_obj)
mock_update.assert_not_called()
mock_delete.assert_not_called()
@mock.patch.object(driver.Driver, "_update_status_deleting")
@mock.patch.object(driver.Driver, "_update_status_updating")
@mock.patch.object(driver.Driver, "_update_all_nodegroups_status")
@mock.patch.object(driver.Driver, "_get_capi_cluster")
def test_update_cluster_status_create_complete(
self, mock_capi, mock_ng, mock_update, mock_delete
):
mock_capi.return_value = {"spec": {}}
self.cluster_obj.status = fields.ClusterStatus.CREATE_COMPLETE
self.driver.update_cluster_status(self.context, self.cluster_obj)
mock_ng.assert_called_once_with(self.cluster_obj)
mock_update.assert_not_called()
mock_delete.assert_not_called()
@mock.patch.object(driver.Driver, "_update_worker_nodegroup_status")
@mock.patch.object(driver.Driver, "_update_control_plane_nodegroup_status")
def test_update_all_nodegroups_status_not_in_progress(
self, mock_cp, mock_w
):
control_plane = [
ng
for ng in self.cluster_obj.nodegroups
if ng.role == driver.NODE_GROUP_ROLE_CONTROLLER
][0]
control_plane.status = fields.ClusterStatus.CREATE_COMPLETE
mock_cp.return_value = control_plane
mock_w.return_value = None
result = self.driver._update_all_nodegroups_status(self.cluster_obj)
self.assertFalse(result)
control_plane = [
ng
for ng in self.cluster_obj.nodegroups
if ng.role == driver.NODE_GROUP_ROLE_CONTROLLER
][0]
mock_cp.assert_called_once_with(self.cluster_obj, mock.ANY)
self.assertEqual(
control_plane.obj_to_primitive(),
mock_cp.call_args_list[0][0][1].obj_to_primitive(),
)
mock_w.assert_called_once_with(self.cluster_obj, mock.ANY)
worker = [
ng
for ng in self.cluster_obj.nodegroups
if ng.role != driver.NODE_GROUP_ROLE_CONTROLLER
][0]
self.assertEqual(
worker.obj_to_primitive(),
mock_w.call_args_list[0][0][1].obj_to_primitive(),
)
@mock.patch.object(driver.Driver, "_update_worker_nodegroup_status")
@mock.patch.object(driver.Driver, "_update_control_plane_nodegroup_status")
def test_update_all_nodegroups_status_in_progress(self, mock_cp, mock_w):
control_plane = [
ng
for ng in self.cluster_obj.nodegroups
if ng.role == driver.NODE_GROUP_ROLE_CONTROLLER
][0]
control_plane.status = fields.ClusterStatus.CREATE_IN_PROGRESS
mock_cp.return_value = control_plane
mock_w.return_value = None
result = self.driver._update_all_nodegroups_status(self.cluster_obj)
self.assertTrue(result)
mock_cp.assert_called_once_with(self.cluster_obj, mock.ANY)
mock_w.assert_called_once_with(self.cluster_obj, mock.ANY)
@mock.patch.object(driver.Driver, "_update_nodegroup_status")
@mock.patch.object(kubernetes.Client, "load")
def test_update_worker_nodegroup_status_empty(
self, mock_load, mock_update
):
mock_client = mock.MagicMock(spec=kubernetes.Client)
mock_load.return_value = mock_client
nodegroup = mock.MagicMock()
nodegroup.name = "workers"
nodegroup.status = fields.ClusterStatus.CREATE_IN_PROGRESS
md = {"status": {}}
mock_client.get_machine_deployment.return_value = md
self.driver._update_worker_nodegroup_status(
self.cluster_obj, nodegroup
)
mock_client.get_machine_deployment.assert_called_once_with(
"cluster-example-a-111111111111-workers", "magnum-fakeproject"
)
mock_update.assert_called_once_with(
self.cluster_obj, nodegroup, driver.NodeGroupState.PENDING
)
@mock.patch.object(driver.Driver, "_update_nodegroup_status")
@mock.patch.object(kubernetes.Client, "load")
def test_update_worker_nodegroup_status_scaling_up(
self, mock_load, mock_update
):
mock_client = mock.MagicMock(spec=kubernetes.Client)
mock_load.return_value = mock_client
nodegroup = mock.MagicMock()
nodegroup.name = "workers"
md = {"status": {"phase": "ScalingUp"}}
mock_client.get_machine_deployment.return_value = md
self.driver._update_worker_nodegroup_status(
self.cluster_obj, nodegroup
)
mock_client.get_machine_deployment.assert_called_once_with(
"cluster-example-a-111111111111-workers", "magnum-fakeproject"
)
mock_update.assert_called_once_with(
self.cluster_obj, mock.ANY, driver.NodeGroupState.PENDING
)
@mock.patch.object(driver.Driver, "_update_nodegroup_status")
@mock.patch.object(kubernetes.Client, "load")
def test_update_worker_nodegroup_status_failed(
self, mock_load, mock_update
):
mock_client = mock.MagicMock(spec=kubernetes.Client)
mock_load.return_value = mock_client
nodegroup = mock.MagicMock()
nodegroup.name = "workers"
nodegroup.status = fields.ClusterStatus.CREATE_IN_PROGRESS
md = {"status": {"phase": "Failed"}}
mock_client.get_machine_deployment.return_value = md
self.driver._update_worker_nodegroup_status(
self.cluster_obj, nodegroup
)
mock_client.get_machine_deployment.assert_called_once_with(
"cluster-example-a-111111111111-workers", "magnum-fakeproject"
)
mock_update.assert_called_once_with(
self.cluster_obj, nodegroup, driver.NodeGroupState.FAILED
)
@mock.patch.object(driver.Driver, "_update_nodegroup_status")
@mock.patch.object(kubernetes.Client, "load")
def test_update_worker_nodegroup_status_not_present_creating(
self, mock_load, mock_update
):
mock_client = mock.MagicMock(spec=kubernetes.Client)
mock_load.return_value = mock_client
nodegroup = mock.MagicMock()
nodegroup.name = "workers"
nodegroup.status = fields.ClusterStatus.CREATE_IN_PROGRESS
mock_client.get_machine_deployment.return_value = None
mock_client.get_all_machines_by_label.return_value = None
self.driver._update_worker_nodegroup_status(
self.cluster_obj, nodegroup
)
mock_client.get_machine_deployment.assert_called_once_with(
"cluster-example-a-111111111111-workers", "magnum-fakeproject"
)
mock_update.assert_called_once_with(
self.cluster_obj, nodegroup, driver.NodeGroupState.NOT_PRESENT
)
mock_client.get_all_machines_by_label.assert_not_called()
nodegroup.destroy.assert_not_called()
nodegroup.save.assert_not_called()
@mock.patch.object(driver.Driver, "_update_nodegroup_status")
@mock.patch.object(kubernetes.Client, "load")
def test_update_worker_nodegroup_status_not_present_deleting(
self, mock_load, mock_update
):
mock_client = mock.MagicMock(spec=kubernetes.Client)
mock_load.return_value = mock_client
nodegroup = mock.MagicMock()
nodegroup.name = "workers"
nodegroup.status = fields.ClusterStatus.DELETE_IN_PROGRESS
machine = {"status": {}}
mock_client.get_machine_deployment.return_value = None
mock_client.get_all_machines_by_label.return_value = machine
self.driver._update_worker_nodegroup_status(
self.cluster_obj, nodegroup
)
mock_client.get_machine_deployment.assert_called_once_with(
"cluster-example-a-111111111111-workers", "magnum-fakeproject"
)
mock_client.get_all_machines_by_label.assert_called_once_with(
{
"capi.stackhpc.com/cluster": "cluster-example-a-111111111111",
"capi.stackhpc.com/component": "worker",
"capi.stackhpc.com/node-group": "workers",
},
"magnum-fakeproject",
)
mock_update.assert_called_once_with(
self.cluster_obj, nodegroup, driver.NodeGroupState.PENDING
)
@mock.patch.object(kubernetes.Client, "load")
def test_update_worker_nodegroup_status_machines_missing_non_default(
self, mock_load
):
mock_client = mock.MagicMock(spec=kubernetes.Client)
mock_load.return_value = mock_client
nodegroup = mock.MagicMock()
nodegroup.name = "workers"
nodegroup.status = fields.ClusterStatus.DELETE_IN_PROGRESS
nodegroup.is_default = False
mock_client.get_machine_deployment.return_value = None
mock_client.get_all_machines_by_label.return_value = None
self.driver._update_worker_nodegroup_status(
self.cluster_obj, nodegroup
)
mock_client.get_machine_deployment.assert_called_once_with(
"cluster-example-a-111111111111-workers", "magnum-fakeproject"
)
mock_client.get_all_machines_by_label.assert_called_once_with(
{
"capi.stackhpc.com/cluster": "cluster-example-a-111111111111",
"capi.stackhpc.com/component": "worker",
"capi.stackhpc.com/node-group": "workers",
},
"magnum-fakeproject",
)
nodegroup.destroy.assert_called_once_with()
nodegroup.save.assert_not_called()
@mock.patch.object(driver.Driver, "_update_nodegroup_status")
@mock.patch.object(kubernetes.Client, "load")
def test_update_worker_nodegroup_status_running(
self, mock_load, mock_update
):
mock_client = mock.MagicMock(spec=kubernetes.Client)
mock_load.return_value = mock_client
nodegroup = mock.MagicMock()
nodegroup.name = "workers"
md = {"status": {"phase": "Running"}}
mock_client.get_machine_deployment.return_value = md
self.driver._update_worker_nodegroup_status(
self.cluster_obj, nodegroup
)
mock_client.get_machine_deployment.assert_called_once_with(
"cluster-example-a-111111111111-workers", "magnum-fakeproject"
)
mock_update.assert_called_once_with(
self.cluster_obj, mock.ANY, driver.NodeGroupState.READY
)
@mock.patch.object(driver.Driver, "_update_nodegroup_status")
@mock.patch.object(kubernetes.Client, "load")
def test_update_control_plane_nodegroup_status_empty(
self, mock_load, mock_update
):
mock_client = mock.MagicMock(spec=kubernetes.Client)
mock_load.return_value = mock_client
nodegroup = mock.MagicMock()
nodegroup.name = "masters"
mock_client.get_kubeadm_control_plane.return_value = None
self.driver._update_control_plane_nodegroup_status(
self.cluster_obj, nodegroup
)
mock_client.get_kubeadm_control_plane.assert_called_once_with(
"cluster-example-a-111111111111-control-plane",
"magnum-fakeproject",
)
mock_update.assert_called_once_with(
self.cluster_obj, mock.ANY, driver.NodeGroupState.NOT_PRESENT
)
@mock.patch.object(driver.Driver, "_update_nodegroup_status")
@mock.patch.object(kubernetes.Client, "load")
def test_update_control_plane_nodegroup_status_condition_false(
self, mock_load, mock_update
):
mock_client = mock.MagicMock(spec=kubernetes.Client)
mock_load.return_value = mock_client
nodegroup = mock.MagicMock()
nodegroup.name = "masters"
kcp = {
"spec": {
"replicas": 3,
},
"status": {
"conditions": [
{"type": "MachinesReady", "status": "True"},
{"type": "Ready", "status": "True"},
{"type": "EtcdClusterHealthy", "status": "True"},
{
"type": "ControlPlaneComponentsHealthy",
"status": "False",
},
],
"replicas": 3,
"updatedReplicas": 3,
"readyReplicas": 3,
}
}
mock_client.get_kubeadm_control_plane.return_value = kcp
self.driver._update_control_plane_nodegroup_status(
self.cluster_obj, nodegroup
)
mock_client.get_kubeadm_control_plane.assert_called_once_with(
"cluster-example-a-111111111111-control-plane",
"magnum-fakeproject",
)
mock_update.assert_called_once_with(
self.cluster_obj, mock.ANY, driver.NodeGroupState.PENDING
)
@mock.patch.object(driver.Driver, "_update_nodegroup_status")
@mock.patch.object(kubernetes.Client, "load")
def test_update_control_plane_nodegroup_status_mismatched_replicas(
self, mock_load, mock_update
):
mock_client = mock.MagicMock(spec=kubernetes.Client)
mock_load.return_value = mock_client
nodegroup = mock.MagicMock()
nodegroup.name = "masters"
kcp = {
"spec": {
"replicas": 3,
},
"status": {
"conditions": [
{"type": "MachinesReady", "status": "True"},
{"type": "Ready", "status": "True"},
{"type": "EtcdClusterHealthy", "status": "True"},
{
"type": "ControlPlaneComponentsHealthy",
"status": "True",
},
],
"replicas": 3,
"updatedReplicas": 2,
"readyReplicas": 2,
}
}
mock_client.get_kubeadm_control_plane.return_value = kcp
self.driver._update_control_plane_nodegroup_status(
self.cluster_obj, nodegroup
)
mock_client.get_kubeadm_control_plane.assert_called_once_with(
"cluster-example-a-111111111111-control-plane",
"magnum-fakeproject",
)
mock_update.assert_called_once_with(
self.cluster_obj, mock.ANY, driver.NodeGroupState.PENDING
)
@mock.patch.object(driver.Driver, "_update_nodegroup_status")
@mock.patch.object(kubernetes.Client, "load")
def test_update_control_plane_nodegroup_status_ready(
self, mock_load, mock_update
):
mock_client = mock.MagicMock(spec=kubernetes.Client)
mock_load.return_value = mock_client
nodegroup = mock.MagicMock()
nodegroup.name = "masters"
kcp = {
"spec": {
"replicas": 3,
},
"status": {
"conditions": [
{"type": "MachinesReady", "status": "True"},
{"type": "Ready", "status": "True"},
{"type": "EtcdClusterHealthy", "status": "True"},
{
"type": "ControlPlaneComponentsHealthy",
"status": "True",
},
],
"replicas": 3,
"updatedReplicas": 3,
"readyReplicas": 3,
}
}
mock_client.get_kubeadm_control_plane.return_value = kcp
self.driver._update_control_plane_nodegroup_status(
self.cluster_obj, nodegroup
)
mock_client.get_kubeadm_control_plane.assert_called_once_with(
"cluster-example-a-111111111111-control-plane",
"magnum-fakeproject",
)
mock_update.assert_called_once_with(
self.cluster_obj, mock.ANY, driver.NodeGroupState.READY
)
@mock.patch.object(kubernetes.Client, "load")
def test_nodegroup_machines_exist(self, mock_load):
mock_client = mock.MagicMock(spec=kubernetes.Client)
mock_load.return_value = mock_client
mock_client.get_all_machines_by_label.return_value = ["item1"]
nodegroup = obj_utils.create_test_nodegroup(self.context)
result = self.driver._nodegroup_machines_exist(
self.cluster_obj, nodegroup
)
self.assertTrue(result)
mock_client.get_all_machines_by_label.assert_called_once_with(
{
"capi.stackhpc.com/cluster": "cluster-example-a-111111111111",
"capi.stackhpc.com/component": "worker",
"capi.stackhpc.com/node-group": "nodegroup1",
},
"magnum-fakeproject",
)
@mock.patch.object(k8s_monitor, "K8sMonitor")
def test_get_monitor(self, mock_mon):
self.driver.get_monitor(self.context, self.cluster_obj)
mock_mon.assert_called_once_with(self.context, self.cluster_obj)
@mock.patch.object(kubernetes.Client, "load")
def test_get_capi_cluster(self, mock_load):
mock_client = mock.MagicMock(spec=kubernetes.Client)
mock_load.return_value = mock_client
self.driver._get_capi_cluster(self.cluster_obj)
mock_client.get_capi_cluster.assert_called_once_with(
"cluster-example-a-111111111111", "magnum-fakeproject"
)
@mock.patch.object(app_creds, "delete_app_cred")
@mock.patch.object(kubernetes.Client, "load")
def test_update_status_deleting(self, mock_load, mock_delete):
mock_client = mock.MagicMock(spec=kubernetes.Client)
mock_load.return_value = mock_client
self.driver._update_status_deleting(self.context, self.cluster_obj)
self.assertEqual("DELETE_COMPLETE", self.cluster_obj.status)
mock_delete.assert_called_once_with(self.context, self.cluster_obj)
mock_client.delete_all_secrets_by_label.assert_called_once_with(
"magnum.openstack.org/cluster-uuid",
self.cluster_obj.uuid,
"magnum-fakeproject",
)
def test_update_status_updating_not_ready(self):
self.cluster_obj.status = fields.ClusterStatus.CREATE_IN_PROGRESS
capi_cluster = {}
self.driver._update_status_updating(self.cluster_obj, capi_cluster)
self.assertEqual(
fields.ClusterStatus.CREATE_IN_PROGRESS, self.cluster_obj.status
)
@mock.patch.object(kubernetes.Client, "load")
def test_update_status_updating_condition_false(self, mock_load):
mock_client = mock.MagicMock(spec=kubernetes.Client)
mock_client.get_addons_by_label.return_value = []
mock_load.return_value = mock_client
self.cluster_obj.status = fields.ClusterStatus.CREATE_IN_PROGRESS
capi_cluster = {
"status": {
"conditions": [
dict(type="InfrastructureReady", status="True"),
dict(type="ControlPlaneReady", status="True"),
dict(type="Ready", status="False"),
]
}
}
self.driver._update_status_updating(self.cluster_obj, capi_cluster)
self.assertEqual(
fields.ClusterStatus.CREATE_IN_PROGRESS, self.cluster_obj.status
)
@mock.patch.object(kubernetes.Client, "load")
def test_update_status_updating_ready_created(self, mock_load):
mock_client = mock.MagicMock(spec=kubernetes.Client)
mock_client.get_addons_by_label.return_value = []
mock_load.return_value = mock_client
self.cluster_obj.status = fields.ClusterStatus.CREATE_IN_PROGRESS
capi_cluster = {
"status": {
"conditions": [
dict(type="InfrastructureReady", status="True"),
dict(type="ControlPlaneReady", status="True"),
dict(type="Ready", status="True"),
]
}
}
self.driver._update_status_updating(self.cluster_obj, capi_cluster)
self.assertEqual(
fields.ClusterStatus.CREATE_COMPLETE, self.cluster_obj.status
)
@mock.patch.object(kubernetes.Client, "load")
def test_update_status_updating_addons_unknown(self, mock_load):
mock_client = mock.MagicMock(spec=kubernetes.Client)
mock_client.get_addons_by_label.return_value = [
{
"metadata": {"name": "cni"},
"status": {},
},
{
"metadata": {"name": "monitoring"},
"status": {},
},
]
mock_load.return_value = mock_client
self.cluster_obj.status = fields.ClusterStatus.CREATE_IN_PROGRESS
capi_cluster = {
"status": {
"conditions": [
dict(type="InfrastructureReady", status="True"),
dict(type="ControlPlaneReady", status="True"),
dict(type="Ready", status="True"),
]
}
}
self.driver._update_status_updating(self.cluster_obj, capi_cluster)
self.assertEqual(
fields.ClusterStatus.CREATE_IN_PROGRESS, self.cluster_obj.status
)
@mock.patch.object(kubernetes.Client, "load")
def test_update_status_updating_addons_installing(self, mock_load):
mock_client = mock.MagicMock(spec=kubernetes.Client)
mock_client.get_addons_by_label.return_value = [
{
"metadata": {"name": "cni"},
"status": {"phase": "Deployed"},
},
{
"metadata": {"name": "monitoring"},
"status": {"phase": "Installing"},
},
]
mock_load.return_value = mock_client
self.cluster_obj.status = fields.ClusterStatus.CREATE_IN_PROGRESS
capi_cluster = {
"status": {
"conditions": [
dict(type="InfrastructureReady", status="True"),
dict(type="ControlPlaneReady", status="True"),
dict(type="Ready", status="True"),
]
}
}
self.driver._update_status_updating(self.cluster_obj, capi_cluster)
self.assertEqual(
fields.ClusterStatus.CREATE_IN_PROGRESS, self.cluster_obj.status
)
@mock.patch.object(kubernetes.Client, "load")
def test_update_status_updating_addons_deployed(self, mock_load):
mock_client = mock.MagicMock(spec=kubernetes.Client)
mock_client.get_addons_by_label.return_value = [
{
"metadata": {"name": "cni"},
"status": {"phase": "Deployed"},
},
{
"metadata": {"name": "monitoring"},
"status": {"phase": "Deployed"},
},
]
mock_load.return_value = mock_client
self.cluster_obj.status = fields.ClusterStatus.CREATE_IN_PROGRESS
capi_cluster = {
"status": {
"conditions": [
dict(type="InfrastructureReady", status="True"),
dict(type="ControlPlaneReady", status="True"),
dict(type="Ready", status="True"),
]
}
}
self.driver._update_status_updating(self.cluster_obj, capi_cluster)
self.assertEqual(
fields.ClusterStatus.CREATE_COMPLETE, self.cluster_obj.status
)
@mock.patch.object(kubernetes.Client, "load")
def test_update_status_updating_addons_failed(self, mock_load):
mock_client = mock.MagicMock(spec=kubernetes.Client)
mock_client.get_addons_by_label.return_value = [
{
"metadata": {"name": "cni"},
"status": {"phase": "Deployed"},
},
{
"metadata": {"name": "monitoring"},
"status": {"phase": "Failed"},
},
]
mock_load.return_value = mock_client
self.cluster_obj.status = fields.ClusterStatus.CREATE_IN_PROGRESS
capi_cluster = {
"status": {
"conditions": [
dict(type="InfrastructureReady", status="True"),
dict(type="ControlPlaneReady", status="True"),
dict(type="Ready", status="True"),
]
}
}
self.driver._update_status_updating(self.cluster_obj, capi_cluster)
self.assertEqual(
fields.ClusterStatus.CREATE_FAILED, self.cluster_obj.status
)
@mock.patch.object(kubernetes.Client, "load")
def test_update_status_updating_ready_updated(self, mock_load):
mock_client = mock.MagicMock(spec=kubernetes.Client)
mock_client.get_addons_by_label.return_value = []
mock_load.return_value = mock_client
self.cluster_obj.status = fields.ClusterStatus.UPDATE_IN_PROGRESS
capi_cluster = {
"status": {
"conditions": [
dict(type="InfrastructureReady", status="True"),
dict(type="ControlPlaneReady", status="True"),
dict(type="Ready", status="True"),
]
}
}
self.driver._update_status_updating(self.cluster_obj, capi_cluster)
self.assertEqual(
fields.ClusterStatus.UPDATE_COMPLETE, self.cluster_obj.status
)
def test_update_cluster_api_address(self):
capi_cluster = {
"spec": {"controlPlaneEndpoint": {"host": "foo", "port": 6443}}
}
self.driver._update_cluster_api_address(self.cluster_obj, capi_cluster)
self.assertEqual("https://foo:6443", self.cluster_obj.api_address)
def test_update_cluster_api_address_skip(self):
self.cluster_obj.api_address = "asdf"
capi_cluster = {"spec": {"foo": "bar"}}
self.driver._update_cluster_api_address(self.cluster_obj, capi_cluster)
self.assertEqual("asdf", self.cluster_obj.api_address)
def test_update_cluster_api_address_skip_on_delete(self):
self.cluster_obj.status = fields.ClusterStatus.DELETE_IN_PROGRESS
self.cluster_obj.api_address = "asdf"
capi_cluster = {
"spec": {"controlPlaneEndpoint": {"host": "foo", "port": 6443}}
}
self.driver._update_cluster_api_address(self.cluster_obj, capi_cluster)
self.assertEqual("asdf", self.cluster_obj.api_address)
def test_update_nodegroup_status_create_complete(self):
nodegroup = obj_utils.create_test_nodegroup(self.context)
nodegroup.status = fields.ClusterStatus.CREATE_IN_PROGRESS
updated = self.driver._update_nodegroup_status(
self.cluster_obj, nodegroup, driver.NodeGroupState.READY
)
self.assertEqual(fields.ClusterStatus.CREATE_COMPLETE, updated.status)
def test_update_nodegroup_status_update_complete(self):
nodegroup = obj_utils.create_test_nodegroup(self.context)
nodegroup.status = fields.ClusterStatus.UPDATE_IN_PROGRESS
updated = self.driver._update_nodegroup_status(
self.cluster_obj, nodegroup, driver.NodeGroupState.READY
)
self.assertEqual(fields.ClusterStatus.UPDATE_COMPLETE, updated.status)
def test_update_nodegroup_status_create_failed(self):
nodegroup = obj_utils.create_test_nodegroup(self.context)
nodegroup.status = fields.ClusterStatus.CREATE_IN_PROGRESS
updated = self.driver._update_nodegroup_status(
self.cluster_obj, nodegroup, driver.NodeGroupState.FAILED
)
self.assertEqual(fields.ClusterStatus.CREATE_FAILED, updated.status)
def test_update_nodegroup_status_update_failed(self):
nodegroup = obj_utils.create_test_nodegroup(self.context)
nodegroup.status = fields.ClusterStatus.UPDATE_IN_PROGRESS
updated = self.driver._update_nodegroup_status(
self.cluster_obj, nodegroup, driver.NodeGroupState.FAILED
)
self.assertEqual(fields.ClusterStatus.UPDATE_FAILED, updated.status)
def test_update_nodegroup_status_create_in_progress(self):
nodegroup = obj_utils.create_test_nodegroup(self.context)
nodegroup.status = fields.ClusterStatus.CREATE_IN_PROGRESS
updated = self.driver._update_nodegroup_status(
self.cluster_obj, nodegroup, driver.NodeGroupState.PENDING
)
self.assertEqual(
fields.ClusterStatus.CREATE_IN_PROGRESS, updated.status
)
def test_update_nodegroup_status_delete_in_progress(self):
nodegroup = obj_utils.create_test_nodegroup(self.context)
nodegroup.status = fields.ClusterStatus.DELETE_IN_PROGRESS
updated = self.driver._update_nodegroup_status(
self.cluster_obj, nodegroup, driver.NodeGroupState.PENDING
)
self.assertEqual(
fields.ClusterStatus.DELETE_IN_PROGRESS, updated.status
)
self.assertEqual(nodegroup.as_dict(), updated.as_dict())
def test_update_nodegroup_creating_but_not_found(self):
nodegroup = obj_utils.create_test_nodegroup(self.context)
nodegroup.status = fields.ClusterStatus.CREATE_IN_PROGRESS
updated = self.driver._update_nodegroup_status(
self.cluster_obj, nodegroup, driver.NodeGroupState.NOT_PRESENT
)
self.assertEqual(
fields.ClusterStatus.CREATE_IN_PROGRESS, updated.status
)
def test_update_nodegroup_status_delete_return_none(self):
nodegroup = obj_utils.create_test_nodegroup(self.context)
nodegroup.status = fields.ClusterStatus.DELETE_IN_PROGRESS
result = self.driver._update_nodegroup_status(
self.cluster_obj, nodegroup, driver.NodeGroupState.NOT_PRESENT
)
self.assertIsNone(result)
def test_update_nodegroup_status_delete_non_default_destroy(self):
nodegroup = mock.MagicMock()
nodegroup.status = fields.ClusterStatus.DELETE_IN_PROGRESS
nodegroup.is_default = False
result = self.driver._update_nodegroup_status(
self.cluster_obj, nodegroup, driver.NodeGroupState.NOT_PRESENT
)
self.assertIsNone(result)
nodegroup.destroy.assert_called_once_with()
def test_update_nodegroup_status_delete_unexpected_state(self):
nodegroup = obj_utils.create_test_nodegroup(self.context)
nodegroup.status = fields.ClusterStatus.ROLLBACK_IN_PROGRESS
updated = self.driver._update_nodegroup_status(
self.cluster_obj, nodegroup, driver.NodeGroupState.NOT_PRESENT
)
self.assertEqual(
fields.ClusterStatus.ROLLBACK_IN_PROGRESS, updated.status
)
self.assertEqual(nodegroup.as_dict(), updated.as_dict())
def test_namespace(self):
self.cluster_obj.project_id = "123-456F"
namespace = self.driver._namespace(self.cluster_obj)
self.assertEqual("magnum-123456f", namespace)
def test_label_return_default(self):
self.cluster_obj.labels = dict()
self.cluster_obj.cluster_template.labels = dict()
result = self.driver._label(self.cluster_obj, "foo", "bar")
self.assertEqual("bar", result)
def test_label_return_template(self):
self.cluster_obj.cluster_template.labels = dict(foo=42)
result = self.driver._label(self.cluster_obj, "foo", "bar")
self.assertEqual("42", result)
def test_label_return_cluster(self):
self.cluster_obj.labels = dict(foo=41)
self.cluster_obj.cluster_template.labels = dict(foo=42)
result = self.driver._label(self.cluster_obj, "foo", "bar")
self.assertEqual("41", result)
def test_sanitised_name_no_suffix(self):
self.assertEqual(
"123-456fab", self.driver._sanitised_name("123-456Fab")
)
def test_sanitised_name_with_suffix(self):
self.assertEqual(
"123-456-fab-1-asdf",
self.driver._sanitised_name("123-456_Fab!!_1!!", "asdf"),
)
self.assertEqual(
"123-456-fab-1-asdf",
self.driver._sanitised_name("123-456_Fab-1", "asdf"),
)
def test_get_kube_version_raises(self):
mock_image = mock.Mock()
mock_image.get.return_value = None
mock_image.id = "myid"
e = self.assertRaises(
exception.KubeVersionPropertyNotFound,
self.driver._get_kube_version,
mock_image,
)
self.assertEqual(
"Image myid does not have a kube_version property.", str(e)
)
mock_image.get.assert_called_once_with("kube_version")
def test_get_kube_version_works(self):
mock_image = mock.Mock()
mock_image.get.return_value = "v1.27.9"
result = self.driver._get_kube_version(mock_image)
self.assertEqual("1.27.9", result)
mock_image.get.assert_called_once_with("kube_version")
@mock.patch("magnum.common.clients.OpenStackClients")
@mock.patch("magnum.api.utils.get_openstack_resource")
def test_get_image_details(self, mock_get, mock_osc):
mock_image = mock.Mock()
mock_image.get.return_value = "v1.27.9"
mock_image.id = "myid"
mock_get.return_value = mock_image
id, version = self.driver._get_image_details(
self.context, "myimagename"
)
self.assertEqual("1.27.9", version)
self.assertEqual("myid", id)
mock_image.get.assert_called_once_with("kube_version")
mock_get.assert_called_once_with(mock.ANY, "myimagename", "images")
def test_get_chart_release_name_lenght(self):
self.cluster_obj.stack_id = "foo"
result = self.driver._get_chart_release_name(self.cluster_obj)
self.assertEqual("foo", result)
def test_generate_release_name_skip(self):
self.cluster_obj.stack_id = "foo"
self.driver._generate_release_name(self.cluster_obj)
self.assertEqual("foo", self.cluster_obj.stack_id)
def test_generate_release_name_generates(self):
self.cluster_obj.stack_id = None
self.cluster_obj.name = "a" * 77
self.driver._generate_release_name(self.cluster_obj)
first = self.cluster_obj.stack_id
self.assertEqual(43, len(first))
self.assertTrue(self.cluster_obj.name[:30] in first)
self.cluster_obj.stack_id = None
self.driver._generate_release_name(self.cluster_obj)
second = self.cluster_obj.stack_id
self.assertNotEqual(first, second)
self.assertEqual(43, len(second))
self.assertTrue(self.cluster_obj.name[:30] in second)
def test_get_monitoring_enabled_from_template(self):
self.cluster_obj.cluster_template.labels["monitoring_enabled"] = "true"
result = self.driver._get_monitoring_enabled(self.cluster_obj)
self.assertTrue(result)
def test_get_kube_dash_enabled_from_template(self):
self.cluster_obj.cluster_template.labels[
"kube_dashboard_enabled"
] = "false"
result = self.driver._get_kube_dash_enabled(self.cluster_obj)
self.assertFalse(result)
def test_get_chart_version_from_config(self):
version = self.driver._get_chart_version(self.cluster_obj)
self.assertEqual(CONF.capi_driver.helm_chart_version, version)
def test_get_chart_version_from_template(self):
self.cluster_obj.cluster_template.labels[
"capi_helm_chart_version"
] = "1.42.0"
version = self.driver._get_chart_version(self.cluster_obj)
self.assertEqual("1.42.0", version)
@mock.patch.object(driver.Driver, "_ensure_certificate_secrets")
@mock.patch.object(driver.Driver, "_create_appcred_secret")
@mock.patch.object(kubernetes.Client, "load")
@mock.patch.object(driver.Driver, "_get_image_details")
@mock.patch.object(helm.Client, "install_or_upgrade")
def test_create_cluster(
self,
mock_install,
mock_image,
mock_load,
mock_appcred,
mock_certs,
):
mock_image.return_value = ("imageid1", "1.27.4")
mock_client = mock.MagicMock(spec=kubernetes.Client)
mock_load.return_value = mock_client
self.cluster_obj.keypair = "kp1"
self.driver.create_cluster(self.context, self.cluster_obj, 10)
app_cred_name = "cluster-example-a-111111111111-cloud-credentials"
mock_install.assert_called_once_with(
"cluster-example-a-111111111111",
"openstack-cluster",
{
"kubernetesVersion": "1.27.4",
"machineImageId": "imageid1",
"cloudCredentialsSecretName": app_cred_name,
"clusterNetworking": {
"internalNetwork": {"nodeCidr": "10.0.0.0/24"},
"dnsNameservers": ["8.8.1.1"],
},
"apiServer": {
"enableLoadBalancer": True,
"loadBalancerProvider": "amphora",
},
"controlPlane": {
"machineFlavor": "flavor_small",
"machineCount": 3,
},
"addons": {
"monitoring": {"enabled": False},
"kubernetesDashboard": {"enabled": True},
"ingress": {"enabled": False},
},
"nodeGroups": [
{
"name": "test-worker",
"machineFlavor": "flavor_medium",
"machineCount": 3,
}
],
"machineSSHKeyName": "kp1",
},
repo=CONF.capi_driver.helm_chart_repo,
version=CONF.capi_driver.helm_chart_version,
namespace="magnum-fakeproject",
)
mock_client.ensure_namespace.assert_called_once_with(
"magnum-fakeproject"
)
mock_appcred.assert_called_once_with(self.context, self.cluster_obj)
mock_certs.assert_called_once_with(self.context, self.cluster_obj)
@mock.patch.object(driver.Driver, "_ensure_certificate_secrets")
@mock.patch.object(driver.Driver, "_create_appcred_secret")
@mock.patch.object(kubernetes.Client, "load")
@mock.patch.object(driver.Driver, "_get_image_details")
@mock.patch.object(helm.Client, "install_or_upgrade")
def test_create_cluster_no_dns(
self,
mock_install,
mock_image,
mock_load,
mock_appcred,
mock_certs,
):
mock_image.return_value = ("imageid1", "1.27.4")
mock_client = mock.MagicMock(spec=kubernetes.Client)
mock_load.return_value = mock_client
self.cluster_obj.cluster_template.dns_nameserver = ""
self.cluster_obj.keypair = "kp1"
self.driver.create_cluster(self.context, self.cluster_obj, 10)
app_cred_name = "cluster-example-a-111111111111-cloud-credentials"
mock_install.assert_called_once_with(
"cluster-example-a-111111111111",
"openstack-cluster",
{
"kubernetesVersion": "1.27.4",
"machineImageId": "imageid1",
"cloudCredentialsSecretName": app_cred_name,
"clusterNetworking": {
"internalNetwork": {"nodeCidr": "10.0.0.0/24"},
},
"apiServer": {
"enableLoadBalancer": True,
"loadBalancerProvider": "amphora",
},
"controlPlane": {
"machineFlavor": "flavor_small",
"machineCount": 3,
},
"addons": {
"monitoring": {"enabled": False},
"kubernetesDashboard": {"enabled": True},
"ingress": {"enabled": False},
},
"nodeGroups": [
{
"name": "test-worker",
"machineFlavor": "flavor_medium",
"machineCount": 3,
}
],
"machineSSHKeyName": "kp1",
},
repo=CONF.capi_driver.helm_chart_repo,
version=CONF.capi_driver.helm_chart_version,
namespace="magnum-fakeproject",
)
mock_client.ensure_namespace.assert_called_once_with(
"magnum-fakeproject"
)
mock_appcred.assert_called_once_with(self.context, self.cluster_obj)
mock_certs.assert_called_once_with(self.context, self.cluster_obj)
@mock.patch.object(driver.Driver, "_ensure_certificate_secrets")
@mock.patch.object(driver.Driver, "_create_appcred_secret")
@mock.patch.object(kubernetes.Client, "load")
@mock.patch.object(driver.Driver, "_get_image_details")
@mock.patch.object(helm.Client, "install_or_upgrade")
def test_create_cluster_no_keypair(
self,
mock_install,
mock_image,
mock_load,
mock_appcred,
mock_certs,
):
mock_image.return_value = ("imageid1", "1.27.4")
mock_client = mock.MagicMock(spec=kubernetes.Client)
mock_load.return_value = mock_client
self.cluster_obj.keypair = ""
self.driver.create_cluster(self.context, self.cluster_obj, 10)
app_cred_name = "cluster-example-a-111111111111-cloud-credentials"
mock_install.assert_called_once_with(
"cluster-example-a-111111111111",
"openstack-cluster",
{
"kubernetesVersion": "1.27.4",
"machineImageId": "imageid1",
"cloudCredentialsSecretName": app_cred_name,
"clusterNetworking": {
"internalNetwork": {"nodeCidr": "10.0.0.0/24"},
"dnsNameservers": ["8.8.1.1"],
},
"apiServer": {
"enableLoadBalancer": True,
"loadBalancerProvider": "amphora",
},
"controlPlane": {
"machineFlavor": "flavor_small",
"machineCount": 3,
},
"addons": {
"monitoring": {"enabled": False},
"kubernetesDashboard": {"enabled": True},
"ingress": {"enabled": False},
},
"nodeGroups": [
{
"name": "test-worker",
"machineFlavor": "flavor_medium",
"machineCount": 3,
}
],
},
repo=CONF.capi_driver.helm_chart_repo,
version=CONF.capi_driver.helm_chart_version,
namespace="magnum-fakeproject",
)
mock_client.ensure_namespace.assert_called_once_with(
"magnum-fakeproject"
)
mock_appcred.assert_called_once_with(self.context, self.cluster_obj)
mock_certs.assert_called_once_with(self.context, self.cluster_obj)
@mock.patch.object(app_creds, "get_app_cred_yaml")
@mock.patch.object(app_creds, "get_openstack_ca_certificate")
@mock.patch.object(kubernetes.Client, "load")
def test_create_appcred_secret(self, mock_load, mock_cert, mock_yaml):
mock_client = mock.MagicMock(spec=kubernetes.Client)
mock_load.return_value = mock_client
mock_cert.return_value = "ca"
mock_yaml.return_value = "appcred"
self.driver._create_appcred_secret(self.context, self.cluster_obj)
uuid = self.cluster_obj.uuid
mock_client.apply_secret.assert_called_once_with(
"cluster-example-a-111111111111-cloud-credentials",
{
"metadata": {
"labels": {
"magnum.openstack.org/project-id": "fake_project",
"magnum.openstack.org/user-id": "fake_user",
"magnum.openstack.org/cluster-uuid": uuid,
}
},
"stringData": {"cacert": "ca", "clouds.yaml": "appcred"},
},
"magnum-fakeproject",
)
@mock.patch.object(cert_manager, "get_cluster_magnum_cert")
@mock.patch.object(cert_manager, "get_cluster_ca_certificate")
@mock.patch.object(driver.Driver, "_decode_key")
@mock.patch.object(driver.Driver, "_decode_cert")
@mock.patch.object(driver.Driver, "_k8s_resource_labels")
@mock.patch.object(kubernetes.Client, "load")
def test_ensure_certificate_secrets(
self, mock_load, mock_labels, mock_cert, mock_key, mock_ca, mock_mag
):
mock_client = mock.MagicMock(spec=kubernetes.Client)
mock_load.return_value = mock_client
mock_labels.return_value = dict(foo="bar")
# TODO(johngarbutt): use side effects here?
mock_cert.return_value = "cert1"
mock_key.return_value = "key1"
mock_ca.return_value = "cert_mgr_ca"
mock_mag.return_value = "cert_mag"
self.driver._ensure_certificate_secrets(self.context, self.cluster_obj)
mock_client.apply_secret.assert_has_calls(
[
mock.call(
"cluster-example-a-111111111111-ca",
{
"metadata": {"labels": {"foo": "bar"}},
"type": "cluster.x-k8s.io/secret",
"stringData": {"tls.crt": "cert1", "tls.key": "key1"},
},
"magnum-fakeproject",
),
mock.call(
"cluster-example-a-111111111111-etcd",
{
"metadata": {"labels": {"foo": "bar"}},
"type": "cluster.x-k8s.io/secret",
"stringData": {"tls.crt": "cert1", "tls.key": "key1"},
},
"magnum-fakeproject",
),
mock.call(
"cluster-example-a-111111111111-proxy",
{
"metadata": {"labels": {"foo": "bar"}},
"type": "cluster.x-k8s.io/secret",
"stringData": {"tls.crt": "cert1", "tls.key": "key1"},
},
"magnum-fakeproject",
),
mock.call(
"cluster-example-a-111111111111-sa",
{
"metadata": {"labels": {"foo": "bar"}},
"type": "cluster.x-k8s.io/secret",
"stringData": {"tls.crt": "cert1", "tls.key": "key1"},
},
"magnum-fakeproject",
),
]
)
# TODO(johngarbutt): assert more calls for the other mocks here
def test_decode_cert(self):
mock_cert = mock.MagicMock()
mock_cert.get_certificate.return_value = "cert"
result = self.driver._decode_cert(mock_cert)
self.assertEqual("cert", result)
@mock.patch.object(x509, "decrypt_key")
def test_decode_key(self, mock_decrypt):
mock_cert = mock.MagicMock()
mock_cert.get_private_key.return_value = "private"
mock_cert.get_private_key_passphrase.return_value = "pass"
mock_decrypt.return_value = "foo"
result = self.driver._decode_key(mock_cert)
self.assertEqual("foo", result)
mock_decrypt.assert_called_once_with("private", "pass")
@mock.patch.object(helm.Client, "uninstall_release")
def test_delete_cluster(self, mock_uninstall):
self.driver.delete_cluster(self.context, self.cluster_obj)
mock_uninstall.assert_called_once_with(
"cluster-example-a-111111111111", namespace="magnum-fakeproject"
)
def test_update_cluster(self):
self.assertRaises(
NotImplementedError,
self.driver.update_cluster,
self.context,
self.cluster_obj,
)
@mock.patch.object(driver.Driver, "_update_helm_release")
def test_resize_cluster(self, mock_update):
self.driver.resize_cluster(
self.context,
self.cluster_obj,
None,
None,
None,
)
mock_update.assert_called_once_with(self.context, self.cluster_obj)
@mock.patch.object(driver.Driver, "_update_helm_release")
def test_resize_cluster_ignore_nodes_to_remove(self, mock_update):
self.driver.resize_cluster(
self.context,
self.cluster_obj,
None,
["node1"],
None,
)
mock_update.assert_called_once_with(self.context, self.cluster_obj)
def test_upgrade_cluster(self):
self.assertRaises(
NotImplementedError,
self.driver.upgrade_cluster,
self.context,
self.cluster_obj,
self.cluster_obj.cluster_template,
1,
None,
)
@mock.patch.object(driver.Driver, "_update_helm_release")
def test_create_nodegroup(self, mock_update):
node_group = mock.MagicMock()
self.driver.create_nodegroup(
self.context, self.cluster_obj, node_group
)
mock_update.assert_called_once_with(self.context, self.cluster_obj)
node_group.save.assert_called_once_with()
self.assertEqual("CREATE_IN_PROGRESS", node_group.status)
@mock.patch.object(driver.Driver, "_update_helm_release")
def test_update_nodegroup(self, mock_update):
node_group = mock.MagicMock()
self.driver.update_nodegroup(
self.context,
self.cluster_obj,
node_group,
)
mock_update.assert_called_once_with(self.context, self.cluster_obj)
node_group.save.assert_called_once_with()
self.assertEqual("UPDATE_IN_PROGRESS", node_group.status)
@mock.patch.object(driver.Driver, "_update_helm_release")
def test_delete_nodegroup(self, mock_update):
self.driver.delete_nodegroup(
self.context,
self.cluster_obj,
self.cluster_obj.nodegroups[1],
)
mock_update.assert_called_once_with(
self.context,
self.cluster_obj,
mock.ANY,
)
# because nodegroups equality is broken
self.assertEqual(
self.cluster_obj.nodegroups[0].as_dict(),
mock_update.call_args.args[2][0].as_dict(),
)
def test_create_federation(self):
self.assertRaises(
NotImplementedError,
self.driver.create_federation,
self.context,
None,
)
def test_update_federation(self):
self.assertRaises(
NotImplementedError,
self.driver.update_federation,
self.context,
None,
)
def test_delete_federation(self):
self.assertRaises(
NotImplementedError,
self.driver.delete_federation,
self.context,
None,
)