From 3473545d7138ee7589baeca17cc6af80a90d25e4 Mon Sep 17 00:00:00 2001 From: Francesco Torta <62566275+fra98@users.noreply.github.com> Date: Fri, 22 Nov 2024 17:54:07 +0100 Subject: [PATCH] feature: ipam support reserved networks --- apis/ipam/v1alpha1/network_types.go | 4 +- cmd/liqo-controller-manager/main.go | 2 +- .../modules/authentication.go | 3 +- .../modules/networking.go | 84 ++++++++++++++++++- .../liqo-crds/crds/ipam.liqo.io_networks.yaml | 3 +- .../liqo/templates/liqo-ipam-networks.yaml | 1 + pkg/ipam/ipam.go | 2 +- pkg/ipam/networks.go | 8 +- .../network-controller/network_controller.go | 3 +- .../networking/network-controller/utils.go | 7 +- 10 files changed, 102 insertions(+), 15 deletions(-) diff --git a/apis/ipam/v1alpha1/network_types.go b/apis/ipam/v1alpha1/network_types.go index 362ac6335a..b704173ce4 100644 --- a/apis/ipam/v1alpha1/network_types.go +++ b/apis/ipam/v1alpha1/network_types.go @@ -43,8 +43,8 @@ type NetworkSpec struct { // PreAllocated is the number of IPs to pre-allocate (reserve) in the CIDR, starting from the first IP. // +kubebuilder:validation:Optional // +kubebuilder:validation:Minimum=0 - // +kubebuilder:validation:XValidation:rule="self == oldSelf",message="Reserved field is immutable" - PreAllocated uint `json:"preAllocated"` + // +kubebuilder:validation:XValidation:rule="self == oldSelf",message="PreAllocated field is immutable" + PreAllocated uint32 `json:"preAllocated"` } // NetworkStatus defines the observed state of Network. diff --git a/cmd/liqo-controller-manager/main.go b/cmd/liqo-controller-manager/main.go index 8ea36a8a2c..3214430101 100644 --- a/cmd/liqo-controller-manager/main.go +++ b/cmd/liqo-controller-manager/main.go @@ -269,7 +269,7 @@ func main() { ipamClient = ipam.NewIPAMClient(conn) } - if err := modules.SetupNetworkingModule(mgr, &modules.NetworkingOption{ + if err := modules.SetupNetworkingModule(ctx, mgr, uncachedClient, &modules.NetworkingOption{ DynClient: dynClient, Factory: factory, diff --git a/cmd/liqo-controller-manager/modules/authentication.go b/cmd/liqo-controller-manager/modules/authentication.go index 5cae0f6046..47a1d80de6 100644 --- a/cmd/liqo-controller-manager/modules/authentication.go +++ b/cmd/liqo-controller-manager/modules/authentication.go @@ -61,6 +61,7 @@ func SetupAuthenticationModule(ctx context.Context, mgr manager.Manager, uncache } if err := enforceAuthenticationKeys(ctx, uncachedClient, opts.LiqoNamespace); err != nil { + klog.Errorf("Unable to enforce authentication keys: %v", err) return err } @@ -135,7 +136,7 @@ func SetupAuthenticationModule(ctx context.Context, mgr manager.Manager, uncache func enforceAuthenticationKeys(ctx context.Context, cl client.Client, liqoNamespace string) error { if err := authentication.InitClusterKeys(ctx, cl, liqoNamespace); err != nil { - klog.Errorf("Unable to initialize cluster authentication keys: %v", err) + return err } klog.Info("Enforced cluster authentication keys") diff --git a/cmd/liqo-controller-manager/modules/networking.go b/cmd/liqo-controller-manager/modules/networking.go index 6135df9ae1..10037a9675 100644 --- a/cmd/liqo-controller-manager/modules/networking.go +++ b/cmd/liqo-controller-manager/modules/networking.go @@ -15,10 +15,15 @@ package modules import ( + "context" + "fmt" + "k8s.io/client-go/dynamic" "k8s.io/klog/v2" + "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/manager" + ipamv1alpha1 "github.com/liqotech/liqo/apis/ipam/v1alpha1" "github.com/liqotech/liqo/pkg/ipam" clientoperator "github.com/liqotech/liqo/pkg/liqo-controller-manager/networking/external-network/client-operator" configuration "github.com/liqotech/liqo/pkg/liqo-controller-manager/networking/external-network/configuration" @@ -36,6 +41,7 @@ import ( ipctrl "github.com/liqotech/liqo/pkg/liqo-controller-manager/networking/ip-controller" networkctrl "github.com/liqotech/liqo/pkg/liqo-controller-manager/networking/network-controller" dynamicutils "github.com/liqotech/liqo/pkg/utils/dynamic" + ipamutils "github.com/liqotech/liqo/pkg/utils/ipam" ) // NetworkingOption defines the options to setup the Networking module. @@ -59,7 +65,13 @@ type NetworkingOption struct { } // SetupNetworkingModule setup the networking module and initializes its controllers . -func SetupNetworkingModule(mgr manager.Manager, opts *NetworkingOption) error { +func SetupNetworkingModule(ctx context.Context, mgr manager.Manager, uncachedClient client.Client, opts *NetworkingOption) error { + // Initialize reserved networks + if err := initializeReservedNetworks(ctx, uncachedClient, opts.IpamClient); err != nil { + klog.Errorf("Unable to initialize reserved networks: %v", err) + return err + } + networkReconciler := networkctrl.NewNetworkReconciler(mgr.GetClient(), mgr.GetScheme(), opts.IpamClient) if err := networkReconciler.SetupWithManager(mgr, opts.NetworkWorkers); err != nil { klog.Errorf("Unable to start the networkReconciler: %v", err) @@ -209,3 +221,73 @@ func SetupNetworkingModule(mgr manager.Manager, opts *NetworkingOption) error { return nil } + +func initializeReservedNetworks(ctx context.Context, cl client.Client, ipamClient ipam.IPAMClient) error { + var networksToReserve []ipamv1alpha1.Network + + // PodCIDR is a special case of reserved network + podCidr, err := ipamutils.GetPodCIDRNetwork(ctx, cl) + if err != nil { + return err + } + networksToReserve = append(networksToReserve, *podCidr) + + // ServiceCIDR is a special case of reserved network + serviceCidr, err := ipamutils.GetServiceCIDRNetwork(ctx, cl) + if err != nil { + return err + } + networksToReserve = append(networksToReserve, *serviceCidr) + + // Get the reserved networks + reservedNetworks, err := ipamutils.GetReservedSubnetNetworks(ctx, cl) + if err != nil { + return err + } + networksToReserve = append(networksToReserve, reservedNetworks...) + + // Reserve the networks and fill their status CIDR. + for i := range networksToReserve { + nw := &networksToReserve[i] + + // If the status CIDR is already set, we do not need to reserve the network + // as it will be reserved when the ipam server is initialized. + if nw.Status.CIDR != "" { + continue + } + + if ipamClient == nil { + nw.Status.CIDR = nw.Spec.CIDR + } else { + // First check if the network is already reserved + res, err := ipamClient.NetworkIsAvailable(ctx, &ipam.NetworkAvailableRequest{ + Cidr: nw.Spec.CIDR.String(), + }) + if err != nil { + return err + } + + if res.Available { + // Network is not reserved, reserve it + _, err := ipamClient.NetworkAcquire(ctx, &ipam.NetworkAcquireRequest{ + Cidr: nw.Spec.CIDR.String(), + Immutable: true, + PreAllocated: nw.Spec.PreAllocated, + }) + if err != nil { + return err + } + } + + // Since reserved network must not be remapped (immutable), we can set the status CIDR to the spec CIDR + nw.Status.CIDR = nw.Spec.CIDR + } + + if err := cl.Status().Update(ctx, nw); err != nil { + return fmt.Errorf("unable to update the reserved network %s: %w", nw.Name, err) + } + } + + klog.Info("Reserved networks initialized") + return nil +} diff --git a/deployments/liqo/charts/liqo-crds/crds/ipam.liqo.io_networks.yaml b/deployments/liqo/charts/liqo-crds/crds/ipam.liqo.io_networks.yaml index b7567b8954..15a858592e 100644 --- a/deployments/liqo/charts/liqo-crds/crds/ipam.liqo.io_networks.yaml +++ b/deployments/liqo/charts/liqo-crds/crds/ipam.liqo.io_networks.yaml @@ -63,10 +63,11 @@ spec: preAllocated: description: PreAllocated is the number of IPs to pre-allocate (reserve) in the CIDR, starting from the first IP. + format: int32 minimum: 0 type: integer x-kubernetes-validations: - - message: Reserved field is immutable + - message: PreAllocated field is immutable rule: self == oldSelf required: - cidr diff --git a/deployments/liqo/templates/liqo-ipam-networks.yaml b/deployments/liqo/templates/liqo-ipam-networks.yaml index a0b96b89dd..cae42f4504 100644 --- a/deployments/liqo/templates/liqo-ipam-networks.yaml +++ b/deployments/liqo/templates/liqo-ipam-networks.yaml @@ -54,4 +54,5 @@ metadata: ipam.liqo.io/network-not-remapped: "true" spec: cidr: {{ $value }} +--- {{- end }} diff --git a/pkg/ipam/ipam.go b/pkg/ipam/ipam.go index 6a928f1fce..5cfe349209 100644 --- a/pkg/ipam/ipam.go +++ b/pkg/ipam/ipam.go @@ -90,7 +90,7 @@ func (lipam *LiqoIPAM) IPRelease(_ context.Context, req *IPReleaseRequest) (*IPR // NetworkAcquire acquires a network. If it is already reserved, it allocates and reserves a new free one with the same prefix length. func (lipam *LiqoIPAM) NetworkAcquire(_ context.Context, req *NetworkAcquireRequest) (*NetworkAcquireResponse, error) { - remappedCidr, err := lipam.acquireNetwork(req.GetCidr(), uint(req.GetPreAllocated()), req.GetImmutable()) + remappedCidr, err := lipam.acquireNetwork(req.GetCidr(), req.GetPreAllocated(), req.GetImmutable()) if err != nil { return &NetworkAcquireResponse{}, err } diff --git a/pkg/ipam/networks.go b/pkg/ipam/networks.go index f81fd592fa..c34fa0d24b 100644 --- a/pkg/ipam/networks.go +++ b/pkg/ipam/networks.go @@ -31,7 +31,7 @@ type networkInfo struct { type network struct { cidr string - preAllocated uint + preAllocated uint32 } func (n network) String() string { @@ -57,7 +57,7 @@ func (lipam *LiqoIPAM) reserveNetwork(nw network) error { } // acquireNetwork acquires a network, eventually remapped if conflicts are found. -func (lipam *LiqoIPAM) acquireNetwork(cidr string, preAllocated uint, immutable bool) (string, error) { +func (lipam *LiqoIPAM) acquireNetwork(cidr string, preAllocated uint32, immutable bool) (string, error) { lipam.mutex.Lock() defer lipam.mutex.Unlock() @@ -98,9 +98,9 @@ func (lipam *LiqoIPAM) isNetworkAvailable(nw network) bool { if lipam.cacheNetworks == nil { return true } - _, ok := lipam.cacheNetworks[nw.String()] + _, exists := lipam.cacheNetworks[nw.String()] - return ok + return !exists } func listNetworksOnCluster(ctx context.Context, cl client.Client) ([]network, error) { diff --git a/pkg/liqo-controller-manager/networking/network-controller/network_controller.go b/pkg/liqo-controller-manager/networking/network-controller/network_controller.go index 45726b0497..1dec9857e7 100644 --- a/pkg/liqo-controller-manager/networking/network-controller/network_controller.go +++ b/pkg/liqo-controller-manager/networking/network-controller/network_controller.go @@ -125,7 +125,8 @@ func (r *NetworkReconciler) handleNetworkStatus(ctx context.Context, nw *ipamv1a desiredCIDR := nw.Spec.CIDR // if the Network must not be remapped, we acquire the network specifying to the IPAM that the cidr is immutable. immutable := ipamutils.NetworkNotRemapped(nw) - remappedCIDR, err := getRemappedCIDR(ctx, r.ipamClient, desiredCIDR, immutable) + preallocated := nw.Spec.PreAllocated + remappedCIDR, err := getRemappedCIDR(ctx, r.ipamClient, desiredCIDR, immutable, preallocated) if err != nil { return err } diff --git a/pkg/liqo-controller-manager/networking/network-controller/utils.go b/pkg/liqo-controller-manager/networking/network-controller/utils.go index 9d9fc2c57c..4706b22f06 100644 --- a/pkg/liqo-controller-manager/networking/network-controller/utils.go +++ b/pkg/liqo-controller-manager/networking/network-controller/utils.go @@ -25,7 +25,7 @@ import ( // getRemappedCIDR returns the remapped CIDR for the given CIDR. func getRemappedCIDR(ctx context.Context, ipamClient ipam.IPAMClient, - desiredCIDR networkingv1beta1.CIDR, immutable bool) (networkingv1beta1.CIDR, error) { + desiredCIDR networkingv1beta1.CIDR, immutable bool, preallocated uint32) (networkingv1beta1.CIDR, error) { switch ipamClient.(type) { case nil: // IPAM is not enabled, use original CIDR from spec @@ -33,8 +33,9 @@ func getRemappedCIDR(ctx context.Context, ipamClient ipam.IPAMClient, default: // interact with the IPAM to retrieve the correct mapping. response, err := ipamClient.NetworkAcquire(ctx, &ipam.NetworkAcquireRequest{ - Cidr: desiredCIDR.String(), - Immutable: immutable, + Cidr: desiredCIDR.String(), + Immutable: immutable, + PreAllocated: preallocated, }) if err != nil { klog.Errorf("IPAM: error while mapping network CIDR %s: %v", desiredCIDR, err)