Skip to content

Commit 0b0295b

Browse files
feat: set disableAPIServerFloatingIP as optional boolean (#486)
1 parent 7044ba8 commit 0b0295b

File tree

8 files changed

+196
-24
lines changed

8 files changed

+196
-24
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,4 @@ site
66
*.orig
77
*.rej
88
.tox
9+
.stestr

hack/stack.sh

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,18 +20,26 @@ sudo chown -R ${USER}. /opt/stack
2020

2121
# Clone repository if not present, otherwise update
2222
if [ ! -f /opt/stack/stack.sh ]; then
23-
git clone https://git.openstack.org/openstack-dev/devstack /opt/stack
23+
git clone https://github.com/openstack/devstack /opt/stack
2424
else
2525
pushd /opt/stack
2626
git pull
2727
popd
2828
fi
2929

30+
# Backport Magnum trusts fix
31+
pushd /opt/stack
32+
git clone https://github.com/openstack/magnum
33+
cd magnum
34+
git fetch https://review.opendev.org/openstack/magnum refs/changes/15/940815/1 && git checkout FETCH_HEAD
35+
popd
36+
3037
# Create DevStack configuration file
3138
cat <<EOF > /opt/stack/local.conf
3239
[[local|localrc]]
3340
# General
3441
GIT_BASE=https://github.com
42+
RECLONE=no
3543
3644
# Secrets
3745
DATABASE_PASSWORD=root

magnum_cluster_api/resources.py

Lines changed: 49 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -140,8 +140,9 @@ def delete(self):
140140

141141

142142
class Base:
143-
def __init__(self, api: pykube.HTTPClient):
143+
def __init__(self, api: pykube.HTTPClient, namespace="magnum-system"):
144144
self.api = api
145+
self.namespace = namespace
145146

146147
def apply(self) -> None:
147148
resource = self.get_object()
@@ -167,14 +168,18 @@ def delete(self) -> None:
167168

168169

169170
class Namespace(Base):
171+
def __init__(self, api: pykube.HTTPClient, name="magnum-system"):
172+
super().__init__(api, name)
173+
self.name = name
174+
170175
def get_object(self) -> pykube.Namespace:
171176
return pykube.Namespace(
172177
self.api,
173178
{
174179
"apiVersion": pykube.Namespace.version,
175180
"kind": pykube.Namespace.kind,
176181
"metadata": {
177-
"name": "magnum-system",
182+
"name": self.namespace,
178183
},
179184
},
180185
)
@@ -681,7 +686,7 @@ def get_object(self) -> objects.KubeadmControlPlaneTemplate:
681686
"kind": objects.KubeadmControlPlaneTemplate.kind,
682687
"metadata": {
683688
"name": CLUSTER_CLASS_NAME,
684-
"namespace": "magnum-system",
689+
"namespace": self.namespace,
685690
},
686691
"spec": {
687692
"template": {
@@ -796,7 +801,7 @@ def get_object(self) -> objects.KubeadmConfigTemplate:
796801
"kind": objects.KubeadmConfigTemplate.kind,
797802
"metadata": {
798803
"name": CLUSTER_CLASS_NAME,
799-
"namespace": "magnum-system",
804+
"namespace": self.namespace,
800805
},
801806
"spec": {
802807
"template": {
@@ -833,7 +838,7 @@ def get_object(self) -> objects.OpenStackMachineTemplate:
833838
"kind": objects.OpenStackMachineTemplate.kind,
834839
"metadata": {
835840
"name": CLUSTER_CLASS_NAME,
836-
"namespace": "magnum-system",
841+
"namespace": self.namespace,
837842
},
838843
"spec": {
839844
"template": {
@@ -862,7 +867,7 @@ def get_object(self) -> objects.OpenStackClusterTemplate:
862867
"kind": objects.OpenStackClusterTemplate.kind,
863868
"metadata": {
864869
"name": CLUSTER_CLASS_NAME,
865-
"namespace": "magnum-system",
870+
"namespace": self.namespace,
866871
},
867872
"spec": {
868873
"template": {
@@ -890,7 +895,7 @@ def get_object(self) -> objects.ClusterClass:
890895
"kind": objects.ClusterClass.kind,
891896
"metadata": {
892897
"name": CLUSTER_CLASS_NAME,
893-
"namespace": "magnum-system",
898+
"namespace": self.namespace,
894899
},
895900
"spec": {
896901
"controlPlane": {
@@ -1881,13 +1886,6 @@ def get_object(self) -> objects.ClusterClass:
18811886
"variable": "clusterIdentityRefName"
18821887
},
18831888
},
1884-
{
1885-
"op": "add",
1886-
"path": "/spec/template/spec/disableAPIServerFloatingIP",
1887-
"valueFrom": {
1888-
"variable": "disableAPIServerFloatingIP"
1889-
},
1890-
},
18911889
{
18921890
"op": "add",
18931891
"path": "/spec/template/spec/externalNetwork",
@@ -1903,6 +1901,30 @@ def get_object(self) -> objects.ClusterClass:
19031901
},
19041902
],
19051903
},
1904+
{
1905+
"name": "disableAPIServerFloatingIP",
1906+
"enabledIf": "{{ if .disableAPIServerFloatingIP }}true{{end}}",
1907+
"definitions": [
1908+
{
1909+
"selector": {
1910+
"apiVersion": objects.OpenStackClusterTemplate.version,
1911+
"kind": objects.OpenStackClusterTemplate.kind,
1912+
"matchResources": {
1913+
"infrastructureCluster": True,
1914+
},
1915+
},
1916+
"jsonPatches": [
1917+
{
1918+
"op": "add",
1919+
"path": "/spec/template/spec/disableAPIServerFloatingIP",
1920+
"valueFrom": {
1921+
"variable": "disableAPIServerFloatingIP"
1922+
},
1923+
},
1924+
],
1925+
},
1926+
],
1927+
},
19061928
{
19071929
"name": "controlPlaneAvailabilityZones",
19081930
"enabledIf": '{{ if ne (index .controlPlaneAvailabilityZones 0) "" }}true{{end}}',
@@ -2393,17 +2415,18 @@ def get_object(self) -> objects.ClusterClass:
23932415

23942416
def create_cluster_class(
23952417
api: pykube.HTTPClient,
2418+
namespace: str = "magnum-system",
23962419
) -> ClusterClass:
23972420
"""
23982421
Create a ClusterClass and all of it's supporting resources from a Magnum
23992422
cluster template using server-side apply.
24002423
"""
24012424

2402-
KubeadmControlPlaneTemplate(api).apply()
2403-
KubeadmConfigTemplate(api).apply()
2404-
OpenStackMachineTemplate(api).apply()
2405-
OpenStackClusterTemplate(api).apply()
2406-
ClusterClass(api).apply()
2425+
KubeadmControlPlaneTemplate(api, namespace).apply()
2426+
KubeadmConfigTemplate(api, namespace).apply()
2427+
OpenStackMachineTemplate(api, namespace).apply()
2428+
OpenStackClusterTemplate(api, namespace).apply()
2429+
ClusterClass(api, namespace).apply()
24072430

24082431

24092432
def mutate_machine_deployment(
@@ -2545,10 +2568,12 @@ def __init__(
25452568
context: context.RequestContext,
25462569
api: pykube.HTTPClient,
25472570
cluster: magnum_objects.Cluster,
2571+
namespace: str = "magnum-system",
25482572
):
25492573
self.context = context
25502574
self.api = api
25512575
self.cluster = cluster
2576+
self.namespace = namespace
25522577

25532578
@property
25542579
def labels(self) -> dict:
@@ -2567,7 +2592,7 @@ def labels(self) -> dict:
25672592
return {**super().labels, **labels}
25682593

25692594
def get_or_none(self) -> objects.Cluster:
2570-
return objects.Cluster.objects(self.api, namespace="magnum-system").get_or_none(
2595+
return objects.Cluster.objects(self.api, namespace=self.namespace).get_or_none(
25712596
name=self.cluster.stack_id
25722597
)
25732598

@@ -2593,7 +2618,7 @@ def get_object(self) -> objects.Cluster:
25932618
"kind": objects.Cluster.kind,
25942619
"metadata": {
25952620
"name": self.cluster.stack_id,
2596-
"namespace": "magnum-system",
2621+
"namespace": self.namespace,
25972622
"labels": self.labels,
25982623
},
25992624
"spec": {
@@ -2720,7 +2745,9 @@ def get_object(self) -> objects.Cluster:
27202745
"name": "cloudControllerManagerConfig",
27212746
"value": base64.encode_as_text(
27222747
utils.generate_cloud_controller_manager_config(
2723-
self.context, self.api, self.cluster
2748+
self.context,
2749+
self.api,
2750+
self.cluster,
27242751
)
27252752
),
27262753
},

magnum_cluster_api/tests/functional/__init__.py

Whitespace-only changes.
Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
# Copyright (c) 2025 VEXXHOST, Inc.
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
import string
5+
from unittest import mock
6+
7+
import shortuuid
8+
from magnum import objects as magnum_objects # type: ignore
9+
from magnum.common import context as magnum_context # type: ignore
10+
from magnum.tests.unit.db import utils # type: ignore
11+
from oslo_utils import uuidutils # type: ignore
12+
from oslotest import base # type: ignore
13+
from tenacity import retry, retry_if_exception_type, stop_after_delay, wait_fixed
14+
15+
from magnum_cluster_api import clients, exceptions, resources
16+
17+
18+
class TestClusterClass(base.BaseTestCase):
19+
def setUp(self):
20+
super(TestClusterClass, self).setUp()
21+
22+
self.api = clients.get_pykube_api()
23+
24+
alphabet = string.ascii_lowercase + string.digits
25+
su = shortuuid.ShortUUID(alphabet=alphabet)
26+
name = "test-%s" % (su.random(length=5))
27+
28+
self.namespace = resources.Namespace(self.api, name)
29+
self.namespace.apply()
30+
self.addCleanup(self.namespace.delete)
31+
32+
resources.create_cluster_class(self.api, namespace=self.namespace.name)
33+
34+
@mock.patch(
35+
"magnum_cluster_api.clients.get_openstack_api",
36+
)
37+
@mock.patch("magnum.objects.nodegroup.NodeGroup.list")
38+
@mock.patch(
39+
"magnum_cluster_api.utils.generate_cloud_controller_manager_config",
40+
return_value="fake-config",
41+
)
42+
@mock.patch(
43+
"magnum_cluster_api.utils.get_image_uuid",
44+
return_value=uuidutils.generate_uuid(),
45+
)
46+
@mock.patch(
47+
"magnum_cluster_api.utils.ensure_controlplane_server_group",
48+
return_value=uuidutils.generate_uuid(),
49+
)
50+
def _test_disable_api_server_floating_ip(
51+
self,
52+
mock_ensure_controlplane_server_group,
53+
mock_get_image_uuid,
54+
mock_generate_config,
55+
mock_list,
56+
mock_get_openstack_api,
57+
**kwargs,
58+
):
59+
master_lb_floating_ip_enabled = kwargs.get("master_lb_floating_ip_enabled")
60+
expected = kwargs.get("expected")
61+
62+
mock_get_openstack_api.return_value.cinder.return_value.volume_types.default.return_value.name = (
63+
"fake-boot-volume-type"
64+
)
65+
66+
context = magnum_context.RequestContext(is_admin=False)
67+
cluster = magnum_objects.Cluster(
68+
context,
69+
**utils.get_test_cluster(
70+
master_count=1,
71+
master_flavor_id="m1.medium",
72+
flavor_id="m1.large",
73+
keypair="fake-keypair",
74+
labels={},
75+
),
76+
)
77+
cluster.cluster_template = magnum_objects.ClusterTemplate(
78+
context,
79+
**utils.get_test_cluster_template(),
80+
)
81+
82+
if master_lb_floating_ip_enabled is not None:
83+
cluster.labels["master_lb_floating_ip_enabled"] = str(
84+
master_lb_floating_ip_enabled
85+
)
86+
87+
capi_cluster = resources.Cluster(
88+
context, self.api, cluster, namespace=self.namespace.name
89+
)
90+
91+
capi_cluster_obj = capi_cluster.get_object()
92+
for variable in capi_cluster_obj.obj["spec"]["topology"]["variables"]:
93+
if variable["name"] == "master_lb_floating_ip_enabled":
94+
self.assertEqual(expected, variable["value"])
95+
96+
capi_cluster.apply()
97+
self.addCleanup(capi_cluster.delete)
98+
99+
@retry(
100+
stop=stop_after_delay(10),
101+
wait=wait_fixed(1),
102+
retry=retry_if_exception_type(exceptions.OpenStackClusterNotCreated),
103+
)
104+
def get_capi_oc():
105+
return capi_cluster_obj.openstack_cluster
106+
107+
capi_oc = get_capi_oc()
108+
self.assertEqual(
109+
expected, capi_oc.obj["spec"].get("disableAPIServerFloatingIP", False)
110+
)
111+
112+
def test_disable_api_server_floating_ip_unset(self):
113+
self._test_disable_api_server_floating_ip(
114+
master_lb_floating_ip_enabled=None, expected=False
115+
)
116+
117+
def test_disable_api_server_floating_ip_true(self):
118+
self._test_disable_api_server_floating_ip(
119+
master_lb_floating_ip_enabled=True, expected=False
120+
)
121+
122+
def test_disable_api_server_floating_ip_false(self):
123+
self._test_disable_api_server_floating_ip(
124+
master_lb_floating_ip_enabled=False, expected=True
125+
)

magnum_cluster_api/tests/unit/test_resources.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,11 @@ def test_generate_machine_deployments_for_cluster_with_deleting_node_group(
6262
mock_get_image_uuid = mocker.patch("magnum_cluster_api.utils.get_image_uuid")
6363
mock_get_image_uuid.return_value = "foo"
6464

65+
mock_ensure_worker_server_group = mocker.patch(
66+
"magnum_cluster_api.utils.ensure_worker_server_group"
67+
)
68+
mock_ensure_worker_server_group.return_value = "foo"
69+
6570
mds = resources.generate_machine_deployments_for_cluster(
6671
context,
6772
cluster,

tox.ini

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,18 @@ deps =
99
pytest
1010
pytest-mock
1111
responses
12+
stestr
1213

1314
[testenv:{unit,py3,py38,py39,py310}]
1415
commands =
1516
pytest magnum_cluster_api/tests/unit/
1617

1718
[testenv:functional]
19+
passenv =
20+
KUBECONFIG
1821
commands =
19-
pytest magnum_cluster_api/tests/functional/
22+
stestr --test-path=./magnum_cluster_api/tests/functional run {posargs}
23+
stestr slowest
2024

2125
[testenv:linters]
2226
skipsdist = True

zuul.d/jobs.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@
2121
pre-run: zuul.d/playbooks/functional/pre.yml
2222
vars:
2323
tox_envlist: functional
24+
tox_environment:
25+
KUBECONFIG: "{{ ansible_env.HOME }}/.kube/config"
2426

2527
- job:
2628
name: magnum-cluster-api-image-build

0 commit comments

Comments
 (0)