Skip to content

Commit c82e2dc

Browse files
committed
Transfer labels from node to hypervisor object
also interprets the lifecycle label now again for persistence, added a test for hypervisor-controller
1 parent 1e207c4 commit c82e2dc

3 files changed

Lines changed: 115 additions & 9 deletions

File tree

internal/controller/eviction_controller.go

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -135,8 +135,6 @@ func (r *EvictionReconciler) handlePreflight(ctx context.Context, eviction *kvmv
135135
// Does the hypervisor even exist? Is it enabled/disabled?
136136
hypervisor, err := openstack.GetHypervisorByName(ctx, r.computeClient, hypervisorName, false)
137137
if err != nil {
138-
// TODO: Maybe this check can be removed, adds unnecessary complexity?
139-
// if the hypervisor is gone, it's gone
140138
expectHypervisor := true
141139
hv := &kvmv1.Hypervisor{}
142140
if err := r.Get(ctx, client.ObjectKey{Name: eviction.Spec.Hypervisor}, hv); client.IgnoreNotFound(err) != nil {
@@ -165,7 +163,7 @@ func (r *EvictionReconciler) handlePreflight(ctx context.Context, eviction *kvmv
165163
Type: kvmv1.ConditionTypeEviction,
166164
Status: metav1.ConditionFalse,
167165
Message: msg,
168-
Reason: kvmv1.ConditionReasonFailed,
166+
Reason: kvmv1.ConditionReasonSuceeded,
169167
})
170168
eviction.Status.OutstandingRamMb = 0
171169
logger.FromContext(ctx).Info(msg)

internal/controller/hypervisor_controller.go

Lines changed: 32 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import (
2424
corev1 "k8s.io/api/core/v1"
2525
k8serrors "k8s.io/apimachinery/pkg/api/errors"
2626
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
27+
"k8s.io/apimachinery/pkg/labels"
2728
"k8s.io/apimachinery/pkg/runtime"
2829
ctrl "sigs.k8s.io/controller-runtime"
2930
k8sclient "sigs.k8s.io/controller-runtime/pkg/client"
@@ -34,6 +35,19 @@ import (
3435
kvmv1 "github.com/cobaltcore-dev/openstack-hypervisor-operator/api/v1"
3536
)
3637

38+
const (
39+
labelLifecycleMode = "cobaltcore.cloud.sap/node-hypervisor-lifecycle"
40+
)
41+
42+
var transferLabels = []string{
43+
"kubernetes.metal.cloud.sap/name",
44+
"kubernetes.metal.cloud.sap/cluster",
45+
"kubernetes.metal.cloud.sap/bb",
46+
corev1.LabelTopologyZone,
47+
corev1.LabelTopologyRegion,
48+
corev1.LabelHostname,
49+
}
50+
3751
type HypervisorController struct {
3852
k8sclient.Client
3953
Scheme *runtime.Scheme
@@ -44,6 +58,7 @@ type HypervisorController struct {
4458
// +kubebuilder:rbac:groups=kvm.cloud.sap,resources=hypervisors,verbs=get;list;watch;create;delete
4559

4660
func (hv *HypervisorController) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
61+
var lifecycleEnabled, skipTest bool
4762
log := logger.FromContext(ctx).WithName(req.Name)
4863

4964
node := &corev1.Node{}
@@ -52,21 +67,32 @@ func (hv *HypervisorController) Reconcile(ctx context.Context, req ctrl.Request)
5267
return ctrl.Result{}, k8sclient.IgnoreNotFound(err)
5368
}
5469

70+
nodeLabels := labels.Set(node.Labels)
71+
if nodeLabels.Has(labelLifecycleMode) {
72+
lifecycleEnabled = true
73+
skipTest = nodeLabels.Get(labelLifecycleMode) == "skip-test"
74+
}
75+
5576
hypervisor := &kvmv1.Hypervisor{
5677
ObjectMeta: metav1.ObjectMeta{
57-
Name: node.Name,
58-
Labels: map[string]string{
59-
corev1.LabelHostname: node.Name,
60-
},
78+
Name: node.Name,
79+
Labels: map[string]string{},
6180
},
6281
Spec: kvmv1.HypervisorSpec{
63-
LifecycleEnabled: true,
64-
SkipTests: true,
82+
LifecycleEnabled: lifecycleEnabled,
83+
SkipTests: skipTest,
6584
HighAvailability: true,
6685
InstallCertificate: true,
6786
},
6887
}
6988

89+
// Transfer Labels
90+
for _, label := range transferLabels {
91+
if nodeLabels.Has(label) {
92+
hypervisor.Labels[label] = nodeLabels.Get(label)
93+
}
94+
}
95+
7096
if node.DeletionTimestamp != nil {
7197
return ctrl.Result{}, nil
7298
}
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
/*
2+
SPDX-FileCopyrightText: Copyright 2024 SAP SE or an SAP affiliate company and cobaltcore-dev contributors
3+
SPDX-License-Identifier: Apache-2.0
4+
5+
Licensed under the Apache License, Version 2.0 (the "License");
6+
you may not use this file except in compliance with the License.
7+
You may obtain a copy of the License at
8+
9+
http://www.apache.org/licenses/LICENSE-2.0
10+
11+
Unless required by applicable law or agreed to in writing, software
12+
distributed under the License is distributed on an "AS IS" BASIS,
13+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
See the License for the specific language governing permissions and
15+
limitations under the License.
16+
*/
17+
18+
package controller
19+
20+
import (
21+
. "github.com/onsi/ginkgo/v2"
22+
. "github.com/onsi/gomega"
23+
corev1 "k8s.io/api/core/v1"
24+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
25+
"k8s.io/apimachinery/pkg/types"
26+
ctrl "sigs.k8s.io/controller-runtime"
27+
"sigs.k8s.io/controller-runtime/pkg/client"
28+
29+
kvmv1 "github.com/cobaltcore-dev/openstack-hypervisor-operator/api/v1"
30+
)
31+
32+
var _ = Describe("Hypervisor Controller", func() {
33+
var hypervisorController *HypervisorController
34+
35+
Context("When reconciling a node", func() {
36+
const nodeName = "node-test"
37+
38+
BeforeEach(func(ctx SpecContext) {
39+
hypervisorController = &HypervisorController{
40+
Client: k8sClient,
41+
Scheme: k8sClient.Scheme(),
42+
}
43+
44+
By("creating the namespace for the reconciler")
45+
ns := &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "monsoon3"}}
46+
Expect(client.IgnoreAlreadyExists(k8sClient.Create(ctx, ns))).To(Succeed())
47+
48+
By("creating the core resource for the Kind Node")
49+
resource := &corev1.Node{
50+
ObjectMeta: metav1.ObjectMeta{
51+
Name: nodeName,
52+
Labels: map[string]string{corev1.LabelTopologyZone: "test-zone"}, //nolint:goconst
53+
},
54+
}
55+
Expect(k8sClient.Create(ctx, resource)).To(Succeed())
56+
})
57+
58+
AfterEach(func(ctx SpecContext) {
59+
node := &corev1.Node{ObjectMeta: metav1.ObjectMeta{Name: nodeName}}
60+
By("Cleanup the specific node")
61+
Expect(client.IgnoreNotFound(k8sClient.Delete(ctx, node))).To(Succeed())
62+
})
63+
64+
It("should successfully reconcile the node", func(ctx SpecContext) {
65+
By("Reconciling the created resource")
66+
_, err := hypervisorController.Reconcile(ctx, ctrl.Request{
67+
NamespacedName: types.NamespacedName{Name: nodeName},
68+
})
69+
Expect(err).NotTo(HaveOccurred())
70+
})
71+
72+
It("should have created the Hypervisor resource", func(ctx SpecContext) {
73+
// Get the Hypervisor resource
74+
hypervisor := &kvmv1.Hypervisor{}
75+
hypervisorName := types.NamespacedName{Name: nodeName}
76+
Expect(hypervisorController.Get(ctx, hypervisorName, hypervisor)).To(Succeed())
77+
Expect(hypervisor.Name).To(Equal(nodeName))
78+
Expect(hypervisor.Labels).ToNot(BeNil())
79+
Expect(hypervisor.Labels[corev1.LabelTopologyZone]).To(Equal("test-zone"))
80+
})
81+
})
82+
})

0 commit comments

Comments
 (0)