Skip to content

Commit

Permalink
Merge pull request #27 from telekom/feature/central-reconciler
Browse files Browse the repository at this point in the history
Introduce Central Reconciler
  • Loading branch information
chdxD1 authored Jun 29, 2023
2 parents a1f166d + b78cdb4 commit a4485df
Show file tree
Hide file tree
Showing 9 changed files with 519 additions and 403 deletions.
6 changes: 6 additions & 0 deletions api/v1alpha1/vrfrouteconfiguration_webhook.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,12 @@ func (r *VRFRouteConfiguration) validateItems() error {
if err != nil {
return err
}
for _, item := range r.Spec.Aggregate {
_, _, err := net.ParseCIDR(item)
if err != nil {
return err
}
}
return nil
}

Expand Down
167 changes: 3 additions & 164 deletions controllers/layer2networkconfiguration_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,11 @@ package controllers

import (
"context"
"fmt"
"net"
"os"
"time"

"github.com/google/go-cmp/cmp"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/types"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/builder"
"sigs.k8s.io/controller-runtime/pkg/client"
Expand All @@ -38,22 +34,17 @@ import (
"sigs.k8s.io/controller-runtime/pkg/source"

networkv1alpha1 "github.com/telekom/das-schiff-network-operator/api/v1alpha1"
"github.com/telekom/das-schiff-network-operator/pkg/anycast"
"github.com/telekom/das-schiff-network-operator/pkg/debounce"
"github.com/telekom/das-schiff-network-operator/pkg/nl"
"github.com/telekom/das-schiff-network-operator/pkg/reconciler"

corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

// Layer2NetworkConfigurationReconciler reconciles a Layer2NetworkConfiguration object
type Layer2NetworkConfigurationReconciler struct {
client.Client
Scheme *runtime.Scheme

AnycastTracker *anycast.AnycastTracker
Debouncer *debounce.Debouncer
NLManager *nl.NetlinkManager
Reconciler *reconciler.Reconciler
}

//+kubebuilder:rbac:groups=core,resources=nodes,verbs=get;list;watch
Expand All @@ -63,27 +54,19 @@ type Layer2NetworkConfigurationReconciler struct {

// Reconcile is part of the main kubernetes reconciliation loop which aims to
// move the current state of the cluster closer to the desired state.
// TODO(user): Modify the Reconcile function to compare the state specified by
// the Layer2NetworkConfiguration object against the actual cluster state, and then
// perform operations to make the cluster state reflect the state specified by
// the user.
//
// For more details, check Reconcile and its Result here:
// - https://pkg.go.dev/sigs.k8s.io/[email protected]/pkg/reconcile
func (r *Layer2NetworkConfigurationReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
_ = log.FromContext(ctx)

// Run ReconcileDebounced through debouncer
r.Debouncer.Debounce(ctx)
r.Reconciler.Reconcile(ctx)

return ctrl.Result{RequeueAfter: 10 * time.Minute}, nil
}

// SetupWithManager sets up the controller with the Manager.
func (r *Layer2NetworkConfigurationReconciler) SetupWithManager(mgr ctrl.Manager) error {
r.Debouncer = debounce.NewDebouncer(r.ReconcileDebounced, 5*time.Second)
r.NLManager = &nl.NetlinkManager{}

// Create empty request for changes to node
nodesMapFn := handler.EnqueueRequestsFromMapFunc(func(o client.Object) []reconcile.Request { return []reconcile.Request{{}} })
nodePredicates := predicate.Funcs{
Expand All @@ -100,147 +83,3 @@ func (r *Layer2NetworkConfigurationReconciler) SetupWithManager(mgr ctrl.Manager
Watches(&source.Kind{Type: &corev1.Node{}}, nodesMapFn, builder.WithPredicates(nodePredicates)).
Complete(r)
}

func (r *Layer2NetworkConfigurationReconciler) ReconcileDebounced(ctx context.Context) error {
logger := log.FromContext(ctx)

layer2s := &networkv1alpha1.Layer2NetworkConfigurationList{}
err := r.Client.List(ctx, layer2s)
if err != nil {
logger.Error(err, "error getting list of Layer2s from Kubernetes")
return err
}

nodeName := os.Getenv("HOSTNAME")
node := &corev1.Node{}
err = r.Client.Get(ctx, types.NamespacedName{Name: nodeName}, node)
if err != nil {
logger.Error(err, "error getting local node name")
return err
}

layer2Info := []nl.Layer2Information{}

for _, layer2 := range layer2s.Items {
spec := layer2.Spec

if spec.NodeSelector != nil {
selector, err := metav1.LabelSelectorAsSelector(spec.NodeSelector)
if err != nil {
logger.Error(err, "error converting nodeSelector of layer2 to selector", "layer2", layer2.ObjectMeta.Name)
return err
}
if !selector.Matches(labels.Set(node.ObjectMeta.Labels)) {
logger.Info("local node does not match nodeSelector of layer2", "layer2", layer2.ObjectMeta.Name, "node", nodeName)
continue
}
}

var anycastMAC *net.HardwareAddr
if mac, err := net.ParseMAC(spec.AnycastMac); err == nil {
anycastMAC = &mac
}

anycastGateways, err := r.NLManager.ParseIPAddresses(spec.AnycastGateways)
if err != nil {
logger.Error(err, "error parsing anycast gateways", "layer", layer2.ObjectMeta.Name, "gw", spec.AnycastGateways)
return err
}

layer2Info = append(layer2Info, nl.Layer2Information{
VlanID: spec.ID,
MTU: spec.MTU,
VNI: spec.VNI,
VRF: spec.VRF,
AnycastMAC: anycastMAC,
AnycastGateways: anycastGateways,
AdvertiseNeighbors: spec.AdvertiseNeighbors,
CreateMACVLANInterface: spec.CreateMACVLANInterface,
})
}

if err := r.reconcileLayer2(ctx, layer2Info); err != nil {
logger.Error(err, "error reconciling Layer2s")
return err
}

return nil
}

func (r *Layer2NetworkConfigurationReconciler) reconcileLayer2(ctx context.Context, layer2Info []nl.Layer2Information) error {
logger := log.FromContext(ctx)

existing, err := r.NLManager.ListL2()
if err != nil {
return err
}

delete := []nl.Layer2Information{}
for _, cfg := range existing {
stillExists := false
for _, info := range layer2Info {
if info.VlanID == cfg.VlanID {
// Maybe reconcile to match MTU, gateways?
stillExists = true
}
}
if !stillExists {
delete = append(delete, cfg)
}
}

create := []nl.Layer2Information{}
anycastTrackerInterfaces := []int{}
for _, info := range layer2Info {
alreadyExists := false
var currentConfig nl.Layer2Information
for _, cfg := range existing {
if info.VlanID == cfg.VlanID {
alreadyExists = true
currentConfig = cfg
}
}
if !alreadyExists {
create = append(create, info)
} else {
err := r.NLManager.ReconcileL2(currentConfig, info)
if err != nil {
return fmt.Errorf("error reconciling layer2 vlan %d vni %d: %v", info.VlanID, info.VNI, err)
}
if info.AdvertiseNeighbors {
bridgeId, err := r.NLManager.GetBridgeId(info)
if err != nil {
return fmt.Errorf("error getting bridge id for vlanId %d: %v", info.VlanID, err)
}
anycastTrackerInterfaces = append(anycastTrackerInterfaces, bridgeId)
}
}
}

for _, info := range delete {
logger.Info("Deleting Layer2 because it is no longer configured", "vlan", info.VlanID, "vni", info.VNI)
errs := r.NLManager.CleanupL2(info)
for _, err := range errs {
logger.Error(err, "Error deleting Layer2", "vlan", info.VlanID, "vni", info.VNI)
}
}

for _, info := range create {
logger.Info("Creating Layer2", "vlan", info.VlanID, "vni", info.VNI)
err := r.NLManager.CreateL2(info)
if err != nil {
return fmt.Errorf("error creating layer2 vlan %d vni %d: %v", info.VlanID, info.VNI, err)
}
if info.AdvertiseNeighbors {
bridgeId, err := r.NLManager.GetBridgeId(info)
if err != nil {
return fmt.Errorf("error getting bridge id for vlanId %d: %v", info.VlanID, err)
}
anycastTrackerInterfaces = append(anycastTrackerInterfaces, bridgeId)
}
}

r.AnycastTracker.TrackedBridges = anycastTrackerInterfaces

return nil
}
Loading

0 comments on commit a4485df

Please sign in to comment.