Skip to content

Commit b23bbe2

Browse files
authored
feat: reconcile associated resources (#76)
1 parent 351e031 commit b23bbe2

File tree

2 files changed

+261
-1
lines changed

2 files changed

+261
-1
lines changed

internal/controller/metalstackcluster_controller.go

+121-1
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,16 @@ import (
2323
"net/http"
2424

2525
ctrl "sigs.k8s.io/controller-runtime"
26+
"sigs.k8s.io/controller-runtime/pkg/builder"
2627
"sigs.k8s.io/controller-runtime/pkg/client"
2728
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
29+
"sigs.k8s.io/controller-runtime/pkg/handler"
2830
ctrllog "sigs.k8s.io/controller-runtime/pkg/log"
2931

3032
apierrors "k8s.io/apimachinery/pkg/api/errors"
33+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
3134
"k8s.io/apimachinery/pkg/labels"
35+
"k8s.io/apimachinery/pkg/types"
3236
"k8s.io/utils/ptr"
3337

3438
clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
@@ -142,10 +146,126 @@ func (r *MetalStackClusterReconciler) SetupWithManager(mgr ctrl.Manager) error {
142146
For(&v1alpha1.MetalStackCluster{}).
143147
Named("metalstackcluster").
144148
WithEventFilter(predicates.ResourceIsNotExternallyManaged(mgr.GetScheme(), mgr.GetLogger())).
145-
// TODO: implement resource paused from cluster-api's predicates?
149+
WithEventFilter(predicates.ResourceNotPaused(mgr.GetScheme(), mgr.GetLogger())).
150+
Watches(
151+
&clusterv1.Cluster{},
152+
handler.EnqueueRequestsFromMapFunc(r.clusterToMetalStackCluster(mgr.GetLogger())),
153+
builder.WithPredicates(predicates.ClusterUnpaused(mgr.GetScheme(), mgr.GetLogger())),
154+
).
155+
Watches(&v1alpha1.MetalStackMachine{},
156+
handler.EnqueueRequestsFromMapFunc(r.metalStackMachineToMetalStackCluster(mgr.GetLogger())),
157+
builder.WithPredicates(predicates.ResourceNotPaused(mgr.GetScheme(), mgr.GetLogger())),
158+
).
146159
Complete(r)
147160
}
148161

162+
func (r *MetalStackClusterReconciler) clusterToMetalStackCluster(log logr.Logger) handler.MapFunc {
163+
return func(ctx context.Context, o client.Object) []ctrl.Request {
164+
cluster, ok := o.(*clusterv1.Cluster)
165+
if !ok {
166+
log.Error(fmt.Errorf("expected a cluster, got %T", o), "failed to get cluster", "object", o)
167+
return nil
168+
}
169+
170+
log := log.WithValues("cluster", cluster.Name, "namespace", cluster.Namespace)
171+
172+
if cluster.Spec.InfrastructureRef == nil {
173+
return nil
174+
}
175+
if cluster.Spec.InfrastructureRef.GroupVersionKind().Kind != "MetalStackCluster" {
176+
return nil
177+
}
178+
179+
infraCluster := &v1alpha1.MetalStackCluster{}
180+
infraName := types.NamespacedName{
181+
Namespace: cluster.Spec.InfrastructureRef.Namespace,
182+
Name: cluster.Spec.InfrastructureRef.Name,
183+
}
184+
185+
if err := r.Client.Get(ctx, infraName, infraCluster); err != nil {
186+
log.Error(err, "failed to get infra cluster")
187+
return nil
188+
}
189+
if annotations.IsExternallyManaged(infraCluster) {
190+
return nil
191+
}
192+
193+
log.Info("cluster changed, reconcile", "infraCluster", infraCluster.Name)
194+
return []ctrl.Request{
195+
{
196+
NamespacedName: infraName,
197+
},
198+
}
199+
}
200+
}
201+
202+
func (r *MetalStackClusterReconciler) metalStackMachineToMetalStackCluster(log logr.Logger) handler.MapFunc {
203+
return func(ctx context.Context, o client.Object) []ctrl.Request {
204+
infraMachine, ok := o.(*v1alpha1.MetalStackMachine)
205+
if !ok {
206+
log.Error(fmt.Errorf("expected an infra cluster, got %T", o), "failed to get infra machine", "object", o)
207+
return nil
208+
}
209+
210+
log := log.WithValues("namespace", infraMachine.Namespace, "infraMachine", infraMachine.Name)
211+
212+
machine, err := util.GetOwnerMachine(ctx, r.Client, infraMachine.ObjectMeta)
213+
if err != nil {
214+
log.Error(err, "failed to get owner machine")
215+
}
216+
if machine == nil {
217+
return nil
218+
}
219+
220+
log = log.WithValues("machine", machine.Name)
221+
222+
cluster, err := util.GetClusterFromMetadata(ctx, r.Client, machine.ObjectMeta)
223+
if err != nil {
224+
log.Error(err, "failed to get owner cluster")
225+
return nil
226+
}
227+
if cluster == nil {
228+
log.Info("machine resource has no cluster yet")
229+
return nil
230+
}
231+
232+
log = log.WithValues("cluster", cluster.Name)
233+
234+
infraCluster := &v1alpha1.MetalStackCluster{
235+
ObjectMeta: metav1.ObjectMeta{
236+
Namespace: cluster.Spec.InfrastructureRef.Namespace,
237+
Name: cluster.Spec.InfrastructureRef.Name,
238+
},
239+
}
240+
err = r.Client.Get(ctx, client.ObjectKeyFromObject(infraCluster), infraCluster)
241+
if apierrors.IsNotFound(err) {
242+
log.Info("infrastructure cluster no longer exists")
243+
return nil
244+
}
245+
if err != nil {
246+
log.Error(err, "failed to get infra cluster")
247+
return nil
248+
}
249+
250+
if cluster.Spec.InfrastructureRef.GroupVersionKind().Kind != "MetalStackCluster" {
251+
log.Info("different infra cluster", "kind", cluster.Spec.InfrastructureRef.GroupVersionKind().Kind)
252+
return nil
253+
}
254+
255+
if annotations.IsExternallyManaged(infraCluster) {
256+
log.Info("infra cluster is externally managed")
257+
return nil
258+
}
259+
260+
log.Info("metalstackmachine changed, reconcile", "infraCluster", infraCluster.Name)
261+
return []ctrl.Request{
262+
{
263+
NamespacedName: client.ObjectKeyFromObject(infraCluster),
264+
},
265+
}
266+
}
267+
}
268+
149269
func (r *clusterReconciler) reconcile() error {
150270
if r.infraCluster.Spec.ControlPlaneEndpoint.Host == "" {
151271
ip, err := r.ensureControlPlaneIP()

internal/controller/metalstackmachine_controller.go

+140
Original file line numberDiff line numberDiff line change
@@ -27,16 +27,20 @@ import (
2727
corev1 "k8s.io/api/core/v1"
2828
apierrors "k8s.io/apimachinery/pkg/api/errors"
2929
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
30+
"k8s.io/apimachinery/pkg/labels"
3031
"k8s.io/utils/ptr"
3132
clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
3233
capierrors "sigs.k8s.io/cluster-api/errors" //nolint:staticcheck
3334
"sigs.k8s.io/cluster-api/util"
3435
"sigs.k8s.io/cluster-api/util/annotations"
3536
"sigs.k8s.io/cluster-api/util/conditions"
3637
"sigs.k8s.io/cluster-api/util/patch"
38+
"sigs.k8s.io/cluster-api/util/predicates"
3739
ctrl "sigs.k8s.io/controller-runtime"
40+
"sigs.k8s.io/controller-runtime/pkg/builder"
3841
"sigs.k8s.io/controller-runtime/pkg/client"
3942
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
43+
"sigs.k8s.io/controller-runtime/pkg/handler"
4044
ctrllog "sigs.k8s.io/controller-runtime/pkg/log"
4145

4246
"github.com/go-logr/logr"
@@ -178,9 +182,145 @@ func (r *MetalStackMachineReconciler) SetupWithManager(mgr ctrl.Manager) error {
178182
return ctrl.NewControllerManagedBy(mgr).
179183
For(&v1alpha1.MetalStackMachine{}).
180184
Named("metalstackmachine").
185+
WithEventFilter(predicates.ResourceIsNotExternallyManaged(mgr.GetScheme(), mgr.GetLogger())).
186+
WithEventFilter(predicates.ResourceNotPaused(mgr.GetScheme(), mgr.GetLogger())).
187+
Watches(
188+
&clusterv1.Cluster{},
189+
handler.EnqueueRequestsFromMapFunc(r.clusterToMetalStackMachine(mgr.GetLogger())),
190+
builder.WithPredicates(predicates.ClusterUnpaused(mgr.GetScheme(), mgr.GetLogger())),
191+
).
192+
Watches(
193+
&v1alpha1.MetalStackCluster{},
194+
handler.EnqueueRequestsFromMapFunc(r.metalStackClusterToMetalStackMachine(mgr.GetLogger())),
195+
builder.WithPredicates(predicates.ResourceNotPaused(mgr.GetScheme(), mgr.GetLogger())),
196+
).
197+
Watches(
198+
&clusterv1.Machine{},
199+
handler.EnqueueRequestsFromMapFunc(r.machineToMetalStackMachine(mgr.GetLogger())),
200+
builder.WithPredicates(predicates.ResourceNotPaused(mgr.GetScheme(), mgr.GetLogger())),
201+
).
181202
Complete(r)
182203
}
183204

205+
func (r *MetalStackMachineReconciler) clusterToMetalStackMachine(log logr.Logger) handler.MapFunc {
206+
return func(ctx context.Context, o client.Object) []ctrl.Request {
207+
cluster, ok := o.(*clusterv1.Cluster)
208+
if !ok {
209+
log.Error(fmt.Errorf("expected a cluster, got %T", o), "failed to get cluster", "object", o)
210+
return nil
211+
}
212+
213+
log := log.WithValues("cluster", cluster.Name, "namespace", cluster.Namespace)
214+
215+
infraMachineList := &v1alpha1.MetalStackMachineList{}
216+
err := r.Client.List(ctx, infraMachineList, &client.ListOptions{
217+
Namespace: cluster.Namespace,
218+
LabelSelector: labels.SelectorFromSet(labels.Set{
219+
clusterv1.ClusterNameLabel: cluster.Name,
220+
}),
221+
})
222+
if err != nil {
223+
log.Error(err, "failed to get infra machines")
224+
return nil
225+
}
226+
227+
var reqs []ctrl.Request
228+
for _, infraMachine := range infraMachineList.Items {
229+
log.Info("cluster changed, reconcile", "infraMachine", infraMachine.Name)
230+
reqs = append(reqs, ctrl.Request{
231+
NamespacedName: client.ObjectKeyFromObject(&infraMachine),
232+
})
233+
}
234+
return reqs
235+
}
236+
}
237+
238+
func (r *MetalStackMachineReconciler) metalStackClusterToMetalStackMachine(log logr.Logger) handler.MapFunc {
239+
return func(ctx context.Context, o client.Object) []ctrl.Request {
240+
infraCluster, ok := o.(*v1alpha1.MetalStackCluster)
241+
if !ok {
242+
log.Error(fmt.Errorf("expected an infra cluster, got %T", o), "failed to get cluster", "object", o)
243+
return nil
244+
}
245+
246+
log := log.WithValues("infraCluster", infraCluster.Name, "namespace", infraCluster.Namespace)
247+
248+
clusterName, ok := infraCluster.Labels[clusterv1.ClusterNameLabel]
249+
if !ok {
250+
return nil
251+
}
252+
253+
infraMachineList := &v1alpha1.MetalStackMachineList{}
254+
err := r.Client.List(ctx, infraMachineList, &client.ListOptions{
255+
Namespace: infraCluster.Namespace,
256+
LabelSelector: labels.SelectorFromSet(labels.Set{
257+
clusterv1.ClusterNameLabel: clusterName,
258+
}),
259+
})
260+
if err != nil {
261+
log.Error(err, "failed to get infra machines")
262+
return nil
263+
}
264+
265+
var reqs []ctrl.Request
266+
for _, infraMachine := range infraMachineList.Items {
267+
log.Info("metalstackcluster changed, reconcile", "infraMachine", infraMachine.Name)
268+
reqs = append(reqs, ctrl.Request{
269+
NamespacedName: client.ObjectKeyFromObject(&infraMachine),
270+
})
271+
}
272+
return reqs
273+
}
274+
}
275+
276+
func (r *MetalStackMachineReconciler) machineToMetalStackMachine(log logr.Logger) handler.MapFunc {
277+
return func(ctx context.Context, o client.Object) []ctrl.Request {
278+
machine, ok := o.(*clusterv1.Machine)
279+
if !ok {
280+
log.Error(fmt.Errorf("expected a machine, got %T", o), "failed to get machine", "object", o)
281+
return nil
282+
}
283+
284+
log := log.WithValues("machine", machine.Name, "namespace", machine.Namespace)
285+
286+
clusterName, ok := machine.Labels[clusterv1.ClusterNameLabel]
287+
if !ok {
288+
return nil
289+
}
290+
deploymentName, ok := machine.Labels[clusterv1.MachineDeploymentNameLabel]
291+
if !ok {
292+
return nil
293+
}
294+
machineSetName, ok := machine.Labels[clusterv1.MachineSetNameLabel]
295+
if !ok {
296+
return nil
297+
}
298+
299+
infraMachineList := &v1alpha1.MetalStackMachineList{}
300+
err := r.Client.List(ctx, infraMachineList, &client.ListOptions{
301+
Namespace: machine.Namespace,
302+
LabelSelector: labels.SelectorFromSet(labels.Set{
303+
clusterv1.ClusterNameLabel: clusterName,
304+
clusterv1.MachineDeploymentNameLabel: deploymentName,
305+
clusterv1.MachineSetNameLabel: machineSetName,
306+
}),
307+
})
308+
if err != nil {
309+
log.Error(err, "failed to get infra machines")
310+
return nil
311+
}
312+
313+
var reqs []ctrl.Request
314+
for _, infraMachine := range infraMachineList.Items {
315+
log.Info("machine changed, reconcile", "infraMachine", infraMachine.Name)
316+
reqs = append(reqs, ctrl.Request{
317+
NamespacedName: client.ObjectKeyFromObject(&infraMachine),
318+
})
319+
}
320+
return reqs
321+
}
322+
}
323+
184324
func (r *machineReconciler) reconcile() (ctrl.Result, error) {
185325
if r.infraCluster.Spec.ControlPlaneEndpoint.Host == "" {
186326
return ctrl.Result{}, errors.New("waiting until control plane ip was set to infrastructure cluster spec")

0 commit comments

Comments
 (0)