Skip to content

Commit 3c383c8

Browse files
committed
GardenerNodeLifecycle: Use SSA for declaring Resources
This makes the source of the values clearer, as the show-managed-fields will show the controller sa field-owner.
1 parent 06d108d commit 3c383c8

File tree

2 files changed

+87
-112
lines changed

2 files changed

+87
-112
lines changed

internal/controller/gardener_node_lifecycle_controller.go

Lines changed: 76 additions & 112 deletions
Original file line numberDiff line numberDiff line change
@@ -26,35 +26,33 @@ import (
2626
corev1 "k8s.io/api/core/v1"
2727
policyv1 "k8s.io/api/policy/v1"
2828
"k8s.io/apimachinery/pkg/api/meta"
29-
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
3029
"k8s.io/apimachinery/pkg/runtime"
3130
"k8s.io/apimachinery/pkg/util/intstr"
31+
apps1ac "k8s.io/client-go/applyconfigurations/apps/v1"
32+
corev1ac "k8s.io/client-go/applyconfigurations/core/v1"
33+
v1 "k8s.io/client-go/applyconfigurations/meta/v1"
34+
policyv1ac "k8s.io/client-go/applyconfigurations/policy/v1"
3235
"k8s.io/client-go/util/retry"
3336
ctrl "sigs.k8s.io/controller-runtime"
3437
k8sclient "sigs.k8s.io/controller-runtime/pkg/client"
35-
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
38+
"sigs.k8s.io/controller-runtime/pkg/client/apiutil"
3639
logger "sigs.k8s.io/controller-runtime/pkg/log"
3740

38-
"github.com/gophercloud/gophercloud/v2"
39-
4041
kvmv1 "github.com/cobaltcore-dev/openstack-hypervisor-operator/api/v1"
41-
"github.com/cobaltcore-dev/openstack-hypervisor-operator/internal/openstack"
4242
)
4343

4444
type GardenerNodeLifecycleController struct {
4545
k8sclient.Client
46-
Scheme *runtime.Scheme
47-
serviceClient *gophercloud.ServiceClient
48-
namespace string
46+
Scheme *runtime.Scheme
47+
namespace string
4948
}
5049

5150
const (
52-
labelDeployment = "cobaltcore-maintenance-controller"
53-
maintenancePodsNamespace = "kube-system"
54-
labelCriticalComponent = "node.gardener.cloud/critical-component"
55-
labelCriticalComponentsNotReady = "node.gardener.cloud/critical-components-not-ready"
56-
valueReasonTerminating = "terminating"
57-
MaintenanceControllerName = "maintenance"
51+
labelDeployment = "cobaltcore-maintenance-controller"
52+
maintenancePodsNamespace = "kube-system"
53+
labelCriticalComponent = "node.gardener.cloud/critical-component"
54+
valueReasonTerminating = "terminating"
55+
MaintenanceControllerName = "maintenance"
5856
)
5957

6058
// The counter-side in gardener is here:
@@ -116,26 +114,20 @@ func (r *GardenerNodeLifecycleController) Reconcile(ctx context.Context, req ctr
116114

117115
func (r *GardenerNodeLifecycleController) ensureBlockingPodDisruptionBudget(ctx context.Context, node *corev1.Node, minAvailable int32) error {
118116
name := nameForNode(node)
119-
podDisruptionBudget := &policyv1.PodDisruptionBudget{
120-
ObjectMeta: metav1.ObjectMeta{
121-
Name: name,
122-
Namespace: maintenancePodsNamespace,
123-
},
117+
nodeLabels := labelsForNode(node)
118+
gvk, err := apiutil.GVKForObject(node, r.Scheme)
119+
if err != nil {
120+
return err
124121
}
125122

126-
_, err := controllerutil.CreateOrUpdate(ctx, r.Client, podDisruptionBudget, func() error {
127-
minAvail := intstr.FromInt32(minAvailable)
128-
nodeLabels := labelsForNode(node)
129-
podDisruptionBudget.Labels = nodeLabels
130-
podDisruptionBudget.Spec = policyv1.PodDisruptionBudgetSpec{
131-
MinAvailable: &minAvail,
132-
Selector: &metav1.LabelSelector{
133-
MatchLabels: nodeLabels,
134-
},
135-
}
136-
return controllerutil.SetControllerReference(node, podDisruptionBudget, r.Scheme)
137-
})
138-
return err
123+
podDisruptionBudget := policyv1ac.PodDisruptionBudget(name, maintenancePodsNamespace).
124+
WithLabels(nodeLabels).
125+
WithOwnerReferences(OwnerReference(node, &gvk)).
126+
WithSpec(policyv1ac.PodDisruptionBudgetSpec().
127+
WithMinAvailable(intstr.FromInt32(minAvailable)).
128+
WithSelector(v1.LabelSelector().WithMatchLabels(nodeLabels)))
129+
130+
return r.Apply(ctx, podDisruptionBudget, k8sclient.FieldOwner(MaintenanceControllerName))
139131
}
140132

141133
func isTerminating(node *corev1.Node) bool {
@@ -166,86 +158,63 @@ func labelsForNode(node *corev1.Node) map[string]string {
166158

167159
func (r *GardenerNodeLifecycleController) ensureSignallingDeployment(ctx context.Context, node *corev1.Node, scale int32, ready bool) error {
168160
name := nameForNode(node)
169-
deployment := &appsv1.Deployment{
170-
ObjectMeta: metav1.ObjectMeta{
171-
Name: name,
172-
Namespace: maintenancePodsNamespace,
173-
},
174-
}
161+
labels := labelsForNode(node)
175162

176-
_, err := controllerutil.CreateOrUpdate(ctx, r.Client, deployment, func() error {
177-
labels := labelsForNode(node)
178-
deployment.Labels = labels
163+
podLabels := maps.Clone(labels)
164+
podLabels[labelCriticalComponent] = "true"
179165

180-
podLabels := maps.Clone(labels)
181-
podLabels[labelCriticalComponent] = "true"
166+
var command string
167+
if ready {
168+
command = "/bin/true"
169+
} else {
170+
command = "/bin/false"
171+
}
182172

183-
var command []string
184-
if ready {
185-
command = []string{"/bin/true"}
186-
} else {
187-
command = []string{"/bin/false"}
188-
}
173+
gvk, err := apiutil.GVKForObject(node, r.Scheme)
174+
if err != nil {
175+
return err
176+
}
189177

190-
var one int64 = 1
191-
zeroStr := intstr.FromInt(0)
192-
oneStr := intstr.FromInt(1)
193-
194-
deployment.Spec = appsv1.DeploymentSpec{
195-
Replicas: &scale,
196-
Selector: &metav1.LabelSelector{
197-
MatchLabels: labels,
198-
},
199-
Strategy: appsv1.DeploymentStrategy{
200-
Type: appsv1.RollingUpdateDeploymentStrategyType,
201-
RollingUpdate: &appsv1.RollingUpdateDeployment{
202-
MaxUnavailable: &zeroStr,
203-
MaxSurge: &oneStr,
204-
},
205-
},
206-
Template: corev1.PodTemplateSpec{
207-
ObjectMeta: metav1.ObjectMeta{
208-
Labels: podLabels,
209-
},
210-
Spec: corev1.PodSpec{
211-
HostNetwork: true, // to make it run as early as possible
212-
NodeSelector: map[string]string{
178+
deployment := apps1ac.Deployment(name, maintenancePodsNamespace).
179+
WithOwnerReferences(OwnerReference(node, &gvk)).
180+
WithLabels(labels).
181+
WithSpec(apps1ac.DeploymentSpec().
182+
WithReplicas(scale).
183+
WithSelector(v1.LabelSelector().
184+
WithMatchLabels(labels)).
185+
WithStrategy(apps1ac.DeploymentStrategy().
186+
WithRollingUpdate(apps1ac.RollingUpdateDeployment().
187+
WithMaxUnavailable(intstr.FromInt(0)).
188+
WithMaxSurge(intstr.FromInt(1)))).
189+
WithTemplate(corev1ac.PodTemplateSpec().
190+
WithLabels(podLabels).
191+
WithSpec(corev1ac.PodSpec().
192+
WithHostNetwork(true).
193+
WithNodeSelector(map[string]string{
213194
corev1.LabelHostname: node.Labels[corev1.LabelHostname],
214-
},
215-
TerminationGracePeriodSeconds: &one, // busybox sleep doesn't handle TERM so well as pid 1
216-
Tolerations: []corev1.Toleration{
217-
{
218-
Effect: corev1.TaintEffectNoExecute,
219-
Operator: corev1.TolerationOpExists,
220-
},
221-
{
222-
Effect: corev1.TaintEffectNoSchedule,
223-
Operator: corev1.TolerationOpExists,
224-
},
225-
},
226-
Containers: []corev1.Container{
227-
{
228-
Name: "sleep",
229-
Image: "keppel.global.cloud.sap/ccloud-dockerhub-mirror/library/busybox:latest",
230-
Command: []string{"sleep", "inf"},
231-
// We need apparently retry to get the timing of the container being up correct
232-
// The startup probe makes sure that we only need to poll this for the (hopefully)
233-
// non-standard case of the host not being integrated yet
234-
StartupProbe: &corev1.Probe{
235-
ProbeHandler: corev1.ProbeHandler{Exec: &corev1.ExecAction{Command: command}},
236-
InitialDelaySeconds: 0,
237-
PeriodSeconds: 1,
238-
SuccessThreshold: 1,
239-
FailureThreshold: 1,
240-
},
241-
},
242-
},
243-
},
244-
},
245-
}
246-
return controllerutil.SetControllerReference(node, deployment, r.Scheme)
247-
})
248-
return err
195+
}).
196+
WithTerminationGracePeriodSeconds(1).
197+
WithTolerations(
198+
corev1ac.Toleration().
199+
WithEffect(corev1.TaintEffectNoExecute).
200+
WithOperator(corev1.TolerationOpExists),
201+
corev1ac.Toleration().
202+
WithEffect(corev1.TaintEffectNoSchedule).
203+
WithOperator(corev1.TolerationOpExists),
204+
).
205+
WithContainers(
206+
corev1ac.Container().
207+
WithName("sleep").
208+
WithImage("keppel.global.cloud.sap/ccloud-dockerhub-mirror/library/busybox:latest").
209+
WithCommand("sleep", "inf").
210+
WithStartupProbe(corev1ac.Probe().
211+
WithExec(corev1ac.ExecAction().WithCommand(command)).
212+
WithInitialDelaySeconds(0).
213+
WithPeriodSeconds(0).
214+
WithFailureThreshold(1).
215+
WithSuccessThreshold(1))))))
216+
217+
return r.Apply(ctx, deployment, k8sclient.FieldOwner(MaintenanceControllerName))
249218
}
250219

251220
// SetupWithManager sets up the controller with the Manager.
@@ -254,11 +223,6 @@ func (r *GardenerNodeLifecycleController) SetupWithManager(mgr ctrl.Manager, nam
254223
_ = logger.FromContext(ctx)
255224
r.namespace = namespace
256225

257-
var err error
258-
if r.serviceClient, err = openstack.GetServiceClient(ctx, "compute", nil); err != nil {
259-
return err
260-
}
261-
262226
return ctrl.NewControllerManagedBy(mgr).
263227
Named(MaintenanceControllerName).
264228
For(&corev1.Node{}).

internal/controller/utils.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ import (
3030

3131
corev1 "k8s.io/api/core/v1"
3232
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
33+
"k8s.io/apimachinery/pkg/runtime/schema"
34+
v1ac "k8s.io/client-go/applyconfigurations/meta/v1"
3335
"sigs.k8s.io/controller-runtime/pkg/client"
3436

3537
kvmv1 "github.com/cobaltcore-dev/openstack-hypervisor-operator/api/v1"
@@ -137,4 +139,13 @@ func Difference[S ~[]E, E comparable](s1, s2 S) S {
137139
return diff
138140
}
139141

142+
// returns a OwnerReference for the given object and groupversionkind info as returned by apiutil.GVKForObject
143+
func OwnerReference(obj metav1.Object, gvk *schema.GroupVersionKind) *v1ac.OwnerReferenceApplyConfiguration {
144+
return v1ac.OwnerReference().
145+
WithAPIVersion(gvk.Group + "/" + gvk.Version).
146+
WithKind(gvk.Kind).
147+
WithName(obj.GetName()).
148+
WithUID(obj.GetUID())
149+
}
150+
140151
var ErrRetry = errors.New("ErrRetry")

0 commit comments

Comments
 (0)