Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
122 changes: 121 additions & 1 deletion internal/controller/metalstackcluster_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,16 @@ import (
"net/http"

ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/builder"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
"sigs.k8s.io/controller-runtime/pkg/handler"
ctrllog "sigs.k8s.io/controller-runtime/pkg/log"

apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/types"
"k8s.io/utils/ptr"

clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
Expand Down Expand Up @@ -142,10 +146,126 @@ func (r *MetalStackClusterReconciler) SetupWithManager(mgr ctrl.Manager) error {
For(&v1alpha1.MetalStackCluster{}).
Named("metalstackcluster").
WithEventFilter(predicates.ResourceIsNotExternallyManaged(mgr.GetScheme(), mgr.GetLogger())).
// TODO: implement resource paused from cluster-api's predicates?
WithEventFilter(predicates.ResourceNotPaused(mgr.GetScheme(), mgr.GetLogger())).
Watches(
&clusterv1.Cluster{},
handler.EnqueueRequestsFromMapFunc(r.clusterToMetalStackCluster(mgr.GetLogger())),
builder.WithPredicates(predicates.ClusterUnpaused(mgr.GetScheme(), mgr.GetLogger())),
).
Watches(&v1alpha1.MetalStackMachine{},
handler.EnqueueRequestsFromMapFunc(r.metalStackMachineToMetalStackCluster(mgr.GetLogger())),
builder.WithPredicates(predicates.ResourceNotPaused(mgr.GetScheme(), mgr.GetLogger())),
).
Complete(r)
}

func (r *MetalStackClusterReconciler) clusterToMetalStackCluster(log logr.Logger) handler.MapFunc {
return func(ctx context.Context, o client.Object) []ctrl.Request {
cluster, ok := o.(*clusterv1.Cluster)
if !ok {
log.Error(fmt.Errorf("expected a cluster, got %T", o), "failed to get cluster", "object", o)
return nil
}

log := log.WithValues("cluster", cluster.Name, "namespace", cluster.Namespace)

if cluster.Spec.InfrastructureRef == nil {
return nil
}
if cluster.Spec.InfrastructureRef.GroupVersionKind().Kind != "MetalStackCluster" {
return nil
}

infraCluster := &v1alpha1.MetalStackCluster{}
infraName := types.NamespacedName{
Namespace: cluster.Spec.InfrastructureRef.Namespace,
Name: cluster.Spec.InfrastructureRef.Name,
}

if err := r.Client.Get(ctx, infraName, infraCluster); err != nil {
log.Error(err, "failed to get infra cluster")
return nil
}
if annotations.IsExternallyManaged(infraCluster) {
return nil
}

log.Info("cluster changed, reconcile", "infraCluster", infraCluster.Name)
return []ctrl.Request{
{
NamespacedName: infraName,
},
}
}
}

func (r *MetalStackClusterReconciler) metalStackMachineToMetalStackCluster(log logr.Logger) handler.MapFunc {
return func(ctx context.Context, o client.Object) []ctrl.Request {
infraMachine, ok := o.(*v1alpha1.MetalStackMachine)
if !ok {
log.Error(fmt.Errorf("expected an infra cluster, got %T", o), "failed to get infra machine", "object", o)
return nil
}

log := log.WithValues("namespace", infraMachine.Namespace, "infraMachine", infraMachine.Name)

machine, err := util.GetOwnerMachine(ctx, r.Client, infraMachine.ObjectMeta)
if err != nil {
log.Error(err, "failed to get owner machine")
}
if machine == nil {
return nil
}

log = log.WithValues("machine", machine.Name)

cluster, err := util.GetClusterFromMetadata(ctx, r.Client, machine.ObjectMeta)
if err != nil {
log.Error(err, "failed to get owner cluster")
return nil
}
if cluster == nil {
log.Info("machine resource has no cluster yet")
return nil
}

log = log.WithValues("cluster", cluster.Name)

infraCluster := &v1alpha1.MetalStackCluster{
ObjectMeta: metav1.ObjectMeta{
Namespace: cluster.Spec.InfrastructureRef.Namespace,
Name: cluster.Spec.InfrastructureRef.Name,
},
}
err = r.Client.Get(ctx, client.ObjectKeyFromObject(infraCluster), infraCluster)
if apierrors.IsNotFound(err) {
log.Info("infrastructure cluster no longer exists")
return nil
}
if err != nil {
log.Error(err, "failed to get infra cluster")
return nil
}

if cluster.Spec.InfrastructureRef.GroupVersionKind().Kind != "MetalStackCluster" {
log.Info("different infra cluster", "kind", cluster.Spec.InfrastructureRef.GroupVersionKind().Kind)
return nil
}

if annotations.IsExternallyManaged(infraCluster) {
log.Info("infra cluster is externally managed")
return nil
}

log.Info("metalstackmachine changed, reconcile", "infraCluster", infraCluster.Name)
return []ctrl.Request{
{
NamespacedName: client.ObjectKeyFromObject(infraCluster),
},
}
}
}

func (r *clusterReconciler) reconcile() error {
if r.infraCluster.Spec.ControlPlaneEndpoint.Host == "" {
ip, err := r.ensureControlPlaneIP()
Expand Down
140 changes: 140 additions & 0 deletions internal/controller/metalstackmachine_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,16 +27,20 @@ import (
corev1 "k8s.io/api/core/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/utils/ptr"
clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
capierrors "sigs.k8s.io/cluster-api/errors" //nolint:staticcheck
"sigs.k8s.io/cluster-api/util"
"sigs.k8s.io/cluster-api/util/annotations"
"sigs.k8s.io/cluster-api/util/conditions"
"sigs.k8s.io/cluster-api/util/patch"
"sigs.k8s.io/cluster-api/util/predicates"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/builder"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
"sigs.k8s.io/controller-runtime/pkg/handler"
ctrllog "sigs.k8s.io/controller-runtime/pkg/log"

"github.com/go-logr/logr"
Expand Down Expand Up @@ -178,9 +182,145 @@ func (r *MetalStackMachineReconciler) SetupWithManager(mgr ctrl.Manager) error {
return ctrl.NewControllerManagedBy(mgr).
For(&v1alpha1.MetalStackMachine{}).
Named("metalstackmachine").
WithEventFilter(predicates.ResourceIsNotExternallyManaged(mgr.GetScheme(), mgr.GetLogger())).
WithEventFilter(predicates.ResourceNotPaused(mgr.GetScheme(), mgr.GetLogger())).
Watches(
&clusterv1.Cluster{},
handler.EnqueueRequestsFromMapFunc(r.clusterToMetalStackMachine(mgr.GetLogger())),
builder.WithPredicates(predicates.ClusterUnpaused(mgr.GetScheme(), mgr.GetLogger())),
).
Watches(
&v1alpha1.MetalStackCluster{},
handler.EnqueueRequestsFromMapFunc(r.metalStackClusterToMetalStackMachine(mgr.GetLogger())),
builder.WithPredicates(predicates.ResourceNotPaused(mgr.GetScheme(), mgr.GetLogger())),
).
Watches(
&clusterv1.Machine{},
handler.EnqueueRequestsFromMapFunc(r.machineToMetalStackMachine(mgr.GetLogger())),
builder.WithPredicates(predicates.ResourceNotPaused(mgr.GetScheme(), mgr.GetLogger())),
).
Complete(r)
}

func (r *MetalStackMachineReconciler) clusterToMetalStackMachine(log logr.Logger) handler.MapFunc {
return func(ctx context.Context, o client.Object) []ctrl.Request {
cluster, ok := o.(*clusterv1.Cluster)
if !ok {
log.Error(fmt.Errorf("expected a cluster, got %T", o), "failed to get cluster", "object", o)
return nil
}

log := log.WithValues("cluster", cluster.Name, "namespace", cluster.Namespace)

infraMachineList := &v1alpha1.MetalStackMachineList{}
err := r.Client.List(ctx, infraMachineList, &client.ListOptions{
Namespace: cluster.Namespace,
LabelSelector: labels.SelectorFromSet(labels.Set{
clusterv1.ClusterNameLabel: cluster.Name,
}),
})
if err != nil {
log.Error(err, "failed to get infra machines")
return nil
}

var reqs []ctrl.Request
for _, infraMachine := range infraMachineList.Items {
log.Info("cluster changed, reconcile", "infraMachine", infraMachine.Name)
reqs = append(reqs, ctrl.Request{
NamespacedName: client.ObjectKeyFromObject(&infraMachine),
})
}
return reqs
}
}

func (r *MetalStackMachineReconciler) metalStackClusterToMetalStackMachine(log logr.Logger) handler.MapFunc {
return func(ctx context.Context, o client.Object) []ctrl.Request {
infraCluster, ok := o.(*v1alpha1.MetalStackCluster)
if !ok {
log.Error(fmt.Errorf("expected an infra cluster, got %T", o), "failed to get cluster", "object", o)
return nil
}

log := log.WithValues("infraCluster", infraCluster.Name, "namespace", infraCluster.Namespace)

clusterName, ok := infraCluster.Labels[clusterv1.ClusterNameLabel]
if !ok {
return nil
}

infraMachineList := &v1alpha1.MetalStackMachineList{}
err := r.Client.List(ctx, infraMachineList, &client.ListOptions{
Namespace: infraCluster.Namespace,
LabelSelector: labels.SelectorFromSet(labels.Set{
clusterv1.ClusterNameLabel: clusterName,
}),
})
if err != nil {
log.Error(err, "failed to get infra machines")
return nil
}

var reqs []ctrl.Request
for _, infraMachine := range infraMachineList.Items {
log.Info("metalstackcluster changed, reconcile", "infraMachine", infraMachine.Name)
reqs = append(reqs, ctrl.Request{
NamespacedName: client.ObjectKeyFromObject(&infraMachine),
})
}
return reqs
}
}

func (r *MetalStackMachineReconciler) machineToMetalStackMachine(log logr.Logger) handler.MapFunc {
return func(ctx context.Context, o client.Object) []ctrl.Request {
machine, ok := o.(*clusterv1.Machine)
if !ok {
log.Error(fmt.Errorf("expected a machine, got %T", o), "failed to get machine", "object", o)
return nil
}

log := log.WithValues("machine", machine.Name, "namespace", machine.Namespace)

clusterName, ok := machine.Labels[clusterv1.ClusterNameLabel]
if !ok {
return nil
}
deploymentName, ok := machine.Labels[clusterv1.MachineDeploymentNameLabel]
if !ok {
return nil
}
machineSetName, ok := machine.Labels[clusterv1.MachineSetNameLabel]
if !ok {
return nil
}

infraMachineList := &v1alpha1.MetalStackMachineList{}
err := r.Client.List(ctx, infraMachineList, &client.ListOptions{
Namespace: machine.Namespace,
LabelSelector: labels.SelectorFromSet(labels.Set{
clusterv1.ClusterNameLabel: clusterName,
clusterv1.MachineDeploymentNameLabel: deploymentName,
clusterv1.MachineSetNameLabel: machineSetName,
}),
})
if err != nil {
log.Error(err, "failed to get infra machines")
return nil
}

var reqs []ctrl.Request
for _, infraMachine := range infraMachineList.Items {
log.Info("machine changed, reconcile", "infraMachine", infraMachine.Name)
reqs = append(reqs, ctrl.Request{
NamespacedName: client.ObjectKeyFromObject(&infraMachine),
})
}
return reqs
}
}

func (r *machineReconciler) reconcile() (ctrl.Result, error) {
if r.infraCluster.Spec.ControlPlaneEndpoint.Host == "" {
return ctrl.Result{}, errors.New("waiting until control plane ip was set to infrastructure cluster spec")
Expand Down