Skip to content

Commit 13ba133

Browse files
committed
add hypervisor CR creation on detected node
1 parent cd06269 commit 13ba133

5 files changed

Lines changed: 152 additions & 1 deletion

File tree

.editorconfig

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
# SPDX-FileCopyrightText: SAP SE or an SAP affiliate company
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
root = true
5+
6+
[*]
7+
insert_final_newline = true
8+
charset = utf-8
9+
trim_trailing_whitespace = true
10+
indent_style = space
11+
indent_size = 2
12+
13+
[{Makefile,go.mod,go.sum,*.go}]
14+
indent_style = tab
15+
indent_size = unset
16+
17+
[*.md]
18+
trim_trailing_whitespace = false
19+
20+
[{LICENSE,LICENSES/*,vendor/**}]
21+
charset = unset
22+
end_of_line = unset
23+
indent_size = unset
24+
indent_style = unset
25+
insert_final_newline = unset
26+
trim_trailing_whitespace = unset

cmd/main.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import (
2222
"flag"
2323
"os"
2424

25+
kvmv1alpha1 "github.com/cobaltcore-dev/kvm-node-agent/api/v1alpha1"
2526
// Import all Kubernetes client auth plugins (e.g. Azure, GCP, OIDC, etc.)
2627
// to ensure that exec-entrypoint and run can make use of them.
2728
_ "k8s.io/client-go/plugin/pkg/client/auth"
@@ -56,6 +57,8 @@ func init() {
5657
// +kubebuilder:scaffold:scheme
5758

5859
utilruntime.Must(cmapi.AddToScheme(scheme))
60+
61+
utilruntime.Must(kvmv1alpha1.AddToScheme(scheme))
5962
}
6063

6164
func main() {
@@ -160,6 +163,14 @@ func main() {
160163
os.Exit(1)
161164
}
162165

166+
if err = (&controller.HypervisorController{
167+
Client: mgr.GetClient(),
168+
Scheme: mgr.GetScheme(),
169+
}).SetupWithManager(mgr); err != nil {
170+
setupLog.Error(err, "unable to create controller", "controller", "Hypervisor")
171+
os.Exit(1)
172+
}
173+
163174
if err = (&controller.EvictionReconciler{
164175
Client: mgr.GetClient(),
165176
Scheme: mgr.GetScheme(),

go.mod

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,10 @@ require (
1717
sigs.k8s.io/controller-runtime v0.21.0
1818
)
1919

20-
require sigs.k8s.io/gateway-api v1.1.0 // indirect
20+
require (
21+
github.com/cobaltcore-dev/kvm-node-agent v0.0.0-20250821153446-8085302c1d22 // indirect
22+
sigs.k8s.io/gateway-api v1.1.0 // indirect
23+
)
2124

2225
require (
2326
cel.dev/expr v0.19.1 // indirect

go.sum

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ github.com/cert-manager/cert-manager v1.18.2 h1:H2P75ycGcTMauV3gvpkDqLdS3RSXonWF
1212
github.com/cert-manager/cert-manager v1.18.2/go.mod h1:icDJx4kG9BCNpGjBvrmsFd99d+lXUvWdkkcrSSQdIiw=
1313
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
1414
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
15+
github.com/cobaltcore-dev/kvm-node-agent v0.0.0-20250821153446-8085302c1d22 h1:VZsnEJH+X5pSQ3lrXri5niDL3iNEeb1FOuu6toBd/Io=
16+
github.com/cobaltcore-dev/kvm-node-agent v0.0.0-20250821153446-8085302c1d22/go.mod h1:sn1SVcUVxzuQPhOo3hKdK8LbF8jSopRDP4ZXIe5EfYc=
1517
github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
1618
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
1719
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
/*
2+
SPDX-FileCopyrightText: Copyright 2025 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+
"context"
22+
"fmt"
23+
24+
corev1 "k8s.io/api/core/v1"
25+
k8serrors "k8s.io/apimachinery/pkg/api/errors"
26+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
27+
"k8s.io/apimachinery/pkg/runtime"
28+
ctrl "sigs.k8s.io/controller-runtime"
29+
k8sclient "sigs.k8s.io/controller-runtime/pkg/client"
30+
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
31+
logger "sigs.k8s.io/controller-runtime/pkg/log"
32+
"sigs.k8s.io/controller-runtime/pkg/predicate"
33+
34+
kvmv1alpha1 "github.com/cobaltcore-dev/kvm-node-agent/api/v1alpha1"
35+
)
36+
37+
type HypervisorController struct {
38+
k8sclient.Client
39+
Scheme *runtime.Scheme
40+
}
41+
42+
// +kubebuilder:rbac:groups="",resources=nodes,verbs=get;list;watch
43+
// +kubebuilder:rbac:groups="",resources=nodes/status,verbs=get
44+
// +kubebuilder:rbac:groups=kvm.cloud.sap,resources=hypervisors,verbs=get;list;watch;create;delete
45+
func (hv *HypervisorController) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
46+
log := logger.FromContext(ctx).WithName(req.Name)
47+
48+
node := &corev1.Node{}
49+
if err := hv.Get(ctx, req.NamespacedName, node); err != nil {
50+
// Ignore not found errors, could be deleted
51+
return ctrl.Result{}, k8sclient.IgnoreNotFound(err)
52+
}
53+
54+
hypervisor := &kvmv1alpha1.Hypervisor{
55+
ObjectMeta: metav1.ObjectMeta{
56+
Name: node.Name,
57+
Labels: map[string]string{
58+
corev1.LabelHostname: node.Name,
59+
},
60+
},
61+
}
62+
63+
// Ensure corresponding hypervisor exists
64+
log.Info("Reconcile", "name", req.Name, "namespace", req.Namespace)
65+
if err := hv.Get(ctx, k8sclient.ObjectKeyFromObject(hypervisor), hypervisor); err != nil {
66+
if k8serrors.IsNotFound(err) {
67+
// attach ownerReference for cascading deletion
68+
if err = controllerutil.SetControllerReference(node, hypervisor, hv.Scheme); err != nil {
69+
return ctrl.Result{}, fmt.Errorf("failed setting controller reference: %w", err)
70+
}
71+
72+
log.Info("Setup hypervisor", "name", node.Name)
73+
if err = hv.Create(ctx, hypervisor); err != nil {
74+
return ctrl.Result{}, err
75+
}
76+
77+
// Requeue to update status
78+
return ctrl.Result{}, nil
79+
}
80+
81+
return ctrl.Result{}, err
82+
}
83+
84+
if node.ObjectMeta.DeletionTimestamp != nil {
85+
// node is being deleted, cleanup hypervisor
86+
if err := hv.Delete(ctx, hypervisor); k8sclient.IgnoreNotFound(err) != nil {
87+
return ctrl.Result{}, fmt.Errorf("failed cleaning up hypervisor: %w", err)
88+
}
89+
}
90+
91+
return ctrl.Result{}, nil
92+
}
93+
94+
func (hv *HypervisorController) SetupWithManager(mgr ctrl.Manager) error {
95+
novaVirtLabeledPredicate, err := predicate.LabelSelectorPredicate(metav1.LabelSelector{
96+
MatchLabels: map[string]string{
97+
labelNovaVirtDriver: "true",
98+
},
99+
})
100+
if err != nil {
101+
return fmt.Errorf("failed to create label selector predicate: %w", err)
102+
}
103+
104+
return ctrl.NewControllerManagedBy(mgr).
105+
For(&corev1.Node{}).
106+
Owns(&kvmv1alpha1.Hypervisor{}).
107+
WithEventFilter(novaVirtLabeledPredicate).
108+
Complete(hv)
109+
}

0 commit comments

Comments
 (0)