From af4fd2691c00941dde2220999638de07fd067bd5 Mon Sep 17 00:00:00 2001 From: Francesco Torta <62566275+fra98@users.noreply.github.com> Date: Thu, 14 Nov 2024 12:55:48 +0100 Subject: [PATCH] refactor: updated IP controller for new ipam --- apis/ipam/v1alpha1/ip_types.go | 12 +- apis/ipam/v1alpha1/zz_generated.deepcopy.go | 17 +- cmd/liqo-controller-manager/main.go | 2 +- .../modules/networking.go | 16 +- .../liqo-crds/crds/ipam.liqo.io_ips.yaml | 74 +++-- docs/advanced/external-ip-remapping.md | 4 +- pkg/consts/ipam.go | 5 + pkg/ipam/ips.go | 7 +- pkg/ipam/sync_test.go | 4 +- pkg/ipamold/ipam.go | 3 +- pkg/ipamold/utils/utils.go | 13 - .../ipmapping/configuration_controller.go | 4 +- .../external-network/remapping/finalizer.go | 8 +- .../remapping/ip_controller.go | 6 +- .../configuration-controller/firewall.go | 4 +- .../route/internalnode_controller.go | 3 +- .../networking/ip-controller/exposition.go | 31 +- .../networking/ip-controller/ip_controller.go | 311 ++++++++++-------- pkg/liqoctl/authenticate/cluster.go | 9 +- pkg/liqoctl/test/network/setup/ip.go | 2 +- pkg/utils/getters/k8sGetters.go | 8 +- pkg/utils/ipam/ips.go | 19 +- pkg/utils/ipam/networks.go | 76 +++-- pkg/virtualKubelet/provider/provider.go | 9 +- .../reflection/exposition/endpointslice.go | 6 +- .../exposition/endpointslice_test.go | 4 +- 26 files changed, 387 insertions(+), 270 deletions(-) diff --git a/apis/ipam/v1alpha1/ip_types.go b/apis/ipam/v1alpha1/ip_types.go index 4dad966d9c..27149ac847 100644 --- a/apis/ipam/v1alpha1/ip_types.go +++ b/apis/ipam/v1alpha1/ip_types.go @@ -49,10 +49,10 @@ type IPSpec struct { // IP is the local IP. // +kubebuilder:validation:XValidation:rule="self == oldSelf",message="IP field is immutable" IP networkingv1beta1.IP `json:"ip"` - // CIDR is the network CIDR where the desired IP should be allocated from. + // NetworkRef is the reference to the Network CR containing the CIDR where the desired IP should be allocated from. // It is optional, if left empty the IP will be allocated in a default network CIDR (e.g., external CIDR). // +kubebuilder:validation:Optional - CIDR *networkingv1beta1.CIDR `json:"cidr,omitempty"` + NetworkRef *v1.ObjectReference `json:"networkRef,omitempty"` // ServiceTemplate contains the template to create the associated service (and endpointslice) for the IP endopoint. // If empty the creation of the service is disabled (default). // +kubebuilder:validation:Optional @@ -65,12 +65,11 @@ type IPSpec struct { // IPStatus defines remapped IPs. type IPStatus struct { - // IPMappings contains the mapping of the local IP for each remote cluster. - IPMappings map[string]networkingv1beta1.IP `json:"ipMappings,omitempty"` // IP is the remapped IP. // +kubebuilder:validation:XValidation:rule="self == oldSelf",message="IP field is immutable" - IP networkingv1beta1.IP `json:"ip"` + IP networkingv1beta1.IP `json:"ip,omitempty"` // CIDR is the network CIDR where the IP is allocated. + // +kubebuilder:validation:XValidation:rule="self == oldSelf",message="CIDR field is immutable" CIDR networkingv1beta1.CIDR `json:"cidr,omitempty"` } @@ -78,8 +77,9 @@ type IPStatus struct { // +kubebuilder:resource:categories=liqo // +kubebuilder:subresource:status // +kubebuilder:printcolumn:name="Local IP",type=string,JSONPath=`.spec.ip` +// +kubebuilder:printcolumn:name="Remapped IP",type=string,JSONPath=`.status.ip` +// +kubebuilder:printcolumn:name="Remapped IP CIDR",type=string,JSONPath=`.status.cidr` // +kubebuilder:printcolumn:name="Age",type=date,JSONPath=`.metadata.creationTimestamp` -// +kubebuilder:printcolumn:name="Remapped IPs",type=string,JSONPath=`.status.ipMappings`,priority=1 // +genclient // IP is the Schema for the IP API. diff --git a/apis/ipam/v1alpha1/zz_generated.deepcopy.go b/apis/ipam/v1alpha1/zz_generated.deepcopy.go index 36b4697c16..7affb37385 100644 --- a/apis/ipam/v1alpha1/zz_generated.deepcopy.go +++ b/apis/ipam/v1alpha1/zz_generated.deepcopy.go @@ -19,7 +19,7 @@ package v1alpha1 import ( - "github.com/liqotech/liqo/apis/networking/v1beta1" + "k8s.io/api/core/v1" runtime "k8s.io/apimachinery/pkg/runtime" ) @@ -66,7 +66,7 @@ func (in *IP) DeepCopyInto(out *IP) { out.TypeMeta = in.TypeMeta in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) in.Spec.DeepCopyInto(&out.Spec) - in.Status.DeepCopyInto(&out.Status) + out.Status = in.Status } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new IP. @@ -122,9 +122,9 @@ func (in *IPList) DeepCopyObject() runtime.Object { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *IPSpec) DeepCopyInto(out *IPSpec) { *out = *in - if in.CIDR != nil { - in, out := &in.CIDR, &out.CIDR - *out = new(v1beta1.CIDR) + if in.NetworkRef != nil { + in, out := &in.NetworkRef, &out.NetworkRef + *out = new(v1.ObjectReference) **out = **in } if in.ServiceTemplate != nil { @@ -152,13 +152,6 @@ func (in *IPSpec) DeepCopy() *IPSpec { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *IPStatus) DeepCopyInto(out *IPStatus) { *out = *in - if in.IPMappings != nil { - in, out := &in.IPMappings, &out.IPMappings - *out = make(map[string]v1beta1.IP, len(*in)) - for key, val := range *in { - (*out)[key] = val - } - } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new IPStatus. diff --git a/cmd/liqo-controller-manager/main.go b/cmd/liqo-controller-manager/main.go index 48add3e4df..000290e989 100644 --- a/cmd/liqo-controller-manager/main.go +++ b/cmd/liqo-controller-manager/main.go @@ -260,7 +260,7 @@ func main() { ipamClient = ipam.NewIPAMClient(conn) } - if err := modules.SetupNetworkingModule(ctx, mgr, &modules.NetworkingOption{ + if err := modules.SetupNetworkingModule(mgr, &modules.NetworkingOption{ DynClient: dynClient, Factory: factory, diff --git a/cmd/liqo-controller-manager/modules/networking.go b/cmd/liqo-controller-manager/modules/networking.go index 3305a4c5db..6135df9ae1 100644 --- a/cmd/liqo-controller-manager/modules/networking.go +++ b/cmd/liqo-controller-manager/modules/networking.go @@ -15,8 +15,6 @@ package modules import ( - "context" - "k8s.io/client-go/dynamic" "k8s.io/klog/v2" "sigs.k8s.io/controller-runtime/pkg/manager" @@ -35,6 +33,7 @@ import ( nodecontroller "github.com/liqotech/liqo/pkg/liqo-controller-manager/networking/internal-network/node-controller" "github.com/liqotech/liqo/pkg/liqo-controller-manager/networking/internal-network/route" internalservercontroller "github.com/liqotech/liqo/pkg/liqo-controller-manager/networking/internal-network/server-controller" + 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" ) @@ -60,19 +59,18 @@ type NetworkingOption struct { } // SetupNetworkingModule setup the networking module and initializes its controllers . -func SetupNetworkingModule(_ context.Context, mgr manager.Manager, opts *NetworkingOption) error { +func SetupNetworkingModule(mgr manager.Manager, opts *NetworkingOption) error { 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) return err } - // TODO: refactor IP reconciler with the new IPAM client. - // ipReconciler := ipctrl.NewIPReconciler(mgr.GetClient(), mgr.GetScheme(), opts.IpamClient) - // if err := ipReconciler.SetupWithManager(ctx, mgr, opts.IPWorkers); err != nil { - // klog.Errorf("Unable to start the ipReconciler: %v", err) - // return err - // } + ipReconciler := ipctrl.NewIPReconciler(mgr.GetClient(), mgr.GetScheme(), opts.IpamClient) + if err := ipReconciler.SetupWithManager(mgr, opts.IPWorkers); err != nil { + klog.Errorf("Unable to start the ipReconciler: %v", err) + return err + } cfgReconciler := configuration.NewConfigurationReconciler(mgr.GetClient(), mgr.GetScheme(), mgr.GetEventRecorderFor("configuration-controller")) diff --git a/deployments/liqo/charts/liqo-crds/crds/ipam.liqo.io_ips.yaml b/deployments/liqo/charts/liqo-crds/crds/ipam.liqo.io_ips.yaml index c2f8e96c6c..d4f158d676 100644 --- a/deployments/liqo/charts/liqo-crds/crds/ipam.liqo.io_ips.yaml +++ b/deployments/liqo/charts/liqo-crds/crds/ipam.liqo.io_ips.yaml @@ -20,13 +20,15 @@ spec: - jsonPath: .spec.ip name: Local IP type: string + - jsonPath: .status.ip + name: Remapped IP + type: string + - jsonPath: .status.cidr + name: Remapped IP CIDR + type: string - jsonPath: .metadata.creationTimestamp name: Age type: date - - jsonPath: .status.ipMappings - name: Remapped IPs - priority: 1 - type: string name: v1alpha1 schema: openAPIV3Schema: @@ -52,12 +54,6 @@ spec: spec: description: IPSpec defines a local IP. properties: - cidr: - description: |- - CIDR is the network CIDR where the desired IP should be allocated from. - It is optional, if left empty the IP will be allocated in a default network CIDR (e.g., external CIDR). - format: cidr - type: string ip: description: IP is the local IP. format: ipv4 @@ -70,6 +66,51 @@ spec: Masquerade is a flag to enable masquerade for the local IP on nodes. If empty the masquerade is disabled. type: boolean + networkRef: + description: |- + NetworkRef is the reference to the Network CR containing the CIDR where the desired IP should be allocated from. + It is optional, if left empty the IP will be allocated in a default network CIDR (e.g., external CIDR). + properties: + apiVersion: + description: API version of the referent. + type: string + fieldPath: + description: |- + If referring to a piece of an object instead of an entire object, this string + should contain a valid JSON/Go field access statement, such as desiredState.manifest.containers[2]. + For example, if the object reference is to a container within a pod, this would take on a value like: + "spec.containers{name}" (where "name" refers to the name of the container that triggered + the event) or if no container name is specified "spec.containers[2]" (container with + index 2 in this pod). This syntax is chosen only to have some well-defined way of + referencing a part of an object. + type: string + kind: + description: |- + Kind of the referent. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + name: + description: |- + Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + namespace: + description: |- + Namespace of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/ + type: string + resourceVersion: + description: |- + Specific resourceVersion to which this reference is made, if any. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency + type: string + uid: + description: |- + UID of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#uids + type: string + type: object + x-kubernetes-map-type: atomic serviceTemplate: description: |- ServiceTemplate contains the template to create the associated service (and endpointslice) for the IP endopoint. @@ -449,6 +490,9 @@ spec: description: CIDR is the network CIDR where the IP is allocated. format: cidr type: string + x-kubernetes-validations: + - message: CIDR field is immutable + rule: self == oldSelf ip: description: IP is the remapped IP. format: ipv4 @@ -456,16 +500,6 @@ spec: x-kubernetes-validations: - message: IP field is immutable rule: self == oldSelf - ipMappings: - additionalProperties: - description: IP defines a syntax validated IP. - format: ipv4 - type: string - description: IPMappings contains the mapping of the local IP for each - remote cluster. - type: object - required: - - ip type: object required: - spec diff --git a/docs/advanced/external-ip-remapping.md b/docs/advanced/external-ip-remapping.md index 89fac7ae9b..2f7e6f8775 100644 --- a/docs/advanced/external-ip-remapping.md +++ b/docs/advanced/external-ip-remapping.md @@ -59,8 +59,8 @@ apiVersion: ipam.liqo.io/v1alpha1 kind: IP ... status: - ipMappings: - cluster1: + ip: + cidr: ``` diff --git a/pkg/consts/ipam.go b/pkg/consts/ipam.go index 35b45ff2f9..8ed921f501 100644 --- a/pkg/consts/ipam.go +++ b/pkg/consts/ipam.go @@ -50,6 +50,11 @@ const ( // IPTypeAPIServerProxy is the constant representing an IP of type APIServerProxy. IPTypeAPIServerProxy = "api-server-proxy" + // NetworkNamespaceLabelKey is the label key used to indicate the namespace of a Network. + NetworkNamespaceLabelKey = "ipam.liqo.io/network-namespace" + // NetworkNameLabelKey is the label key used to indicate the name of a Network. + NetworkNameLabelKey = "ipam.liqo.io/network-name" + // DefaultCIDRValue is the default value for a string that contains a CIDR. DefaultCIDRValue = "None" ) diff --git a/pkg/ipam/ips.go b/pkg/ipam/ips.go index 0a9117985c..5784cb7ef9 100644 --- a/pkg/ipam/ips.go +++ b/pkg/ipam/ips.go @@ -18,6 +18,7 @@ import ( "context" "time" + "github.com/google/nftables" klog "k8s.io/klog/v2" "sigs.k8s.io/controller-runtime/pkg/client" @@ -64,8 +65,12 @@ func (lipam *LiqoIPAM) acquireIP(cidr string) (string, error) { if lipam.cacheIPs == nil { lipam.cacheIPs = make(map[string]ipInfo) } + firstIP, _, err := nftables.NetFirstAndLastIP(cidr) + if err != nil { + return "", err + } ip := ipCidr{ - ip: "", + ip: firstIP.String(), cidr: cidr, } lipam.cacheIPs[ip.String()] = ipInfo{ diff --git a/pkg/ipam/sync_test.go b/pkg/ipam/sync_test.go index 8d9c54e794..4ff86ec37e 100644 --- a/pkg/ipam/sync_test.go +++ b/pkg/ipam/sync_test.go @@ -22,7 +22,6 @@ import ( . "github.com/onsi/gomega" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/kubernetes/scheme" - "k8s.io/utils/ptr" "sigs.k8s.io/controller-runtime/pkg/client/fake" ipamv1alpha1 "github.com/liqotech/liqo/apis/ipam/v1alpha1" @@ -81,8 +80,7 @@ var _ = Describe("Sync routine tests", func() { Namespace: testNamespace, }, Spec: ipamv1alpha1.IPSpec{ - IP: networkingv1beta1.IP(ip), - CIDR: ptr.To(networkingv1beta1.CIDR(cidr)), + IP: networkingv1beta1.IP(ip), }, Status: ipamv1alpha1.IPStatus{ IP: networkingv1beta1.IP(ip), diff --git a/pkg/ipamold/ipam.go b/pkg/ipamold/ipam.go index ad32a7e91e..46ad253dcd 100644 --- a/pkg/ipamold/ipam.go +++ b/pkg/ipamold/ipam.go @@ -36,6 +36,7 @@ import ( "github.com/liqotech/liqo/pkg/consts" ipamerrors "github.com/liqotech/liqo/pkg/ipamold/errors" ipamutils "github.com/liqotech/liqo/pkg/ipamold/utils" + utils "github.com/liqotech/liqo/pkg/utils/ipam" ) // Ipam Interface. @@ -415,7 +416,7 @@ func (liqoIPAM *IPAM) GetOrSetExternalCIDR(ctx context.Context, getOrSetExtCIDRR // Acquire the UnknownSourceIP // It must be the first ip in the externalCIDR - unknownSourceIP, err := ipamutils.GetUnknownSourceIP(externalCIDR) + unknownSourceIP, err := utils.GetUnknownSourceIP(externalCIDR) if err != nil { return &GetOrSetExtCIDRResponse{}, fmt.Errorf("cannot get the UnknownSourceIP: %w", err) } diff --git a/pkg/ipamold/utils/utils.go b/pkg/ipamold/utils/utils.go index 580f3914ce..68b1bb8ef1 100644 --- a/pkg/ipamold/utils/utils.go +++ b/pkg/ipamold/utils/utils.go @@ -19,7 +19,6 @@ import ( "net" "net/netip" - "github.com/google/nftables" "go4.org/netipx" utilruntime "k8s.io/apimachinery/pkg/util/runtime" @@ -117,15 +116,3 @@ func SplitNetwork(network string) []string { return halves } - -// GetUnknownSourceIP returns the IP address used to map unknown sources. -func GetUnknownSourceIP(extCIDR string) (string, error) { - if extCIDR == "" { - return "", fmt.Errorf("ExternalCIDR not set") - } - firstExtCIDRip, _, err := nftables.NetFirstAndLastIP(extCIDR) - if err != nil { - return "", fmt.Errorf("cannot get first IP of ExternalCIDR") - } - return firstExtCIDRip.String(), nil -} diff --git a/pkg/liqo-controller-manager/ipmapping/configuration_controller.go b/pkg/liqo-controller-manager/ipmapping/configuration_controller.go index c6d6c7a7bd..153f568ee1 100644 --- a/pkg/liqo-controller-manager/ipmapping/configuration_controller.go +++ b/pkg/liqo-controller-manager/ipmapping/configuration_controller.go @@ -32,8 +32,8 @@ import ( ipamv1alpha1 "github.com/liqotech/liqo/apis/ipam/v1alpha1" networkingv1beta1 "github.com/liqotech/liqo/apis/networking/v1beta1" "github.com/liqotech/liqo/pkg/consts" - "github.com/liqotech/liqo/pkg/ipamold/utils" configuration "github.com/liqotech/liqo/pkg/liqo-controller-manager/networking/external-network/configuration" + ipamutils "github.com/liqotech/liqo/pkg/utils/ipam" ) // cluster-role @@ -74,7 +74,7 @@ func (r *ConfigurationReconciler) Reconcile(ctx context.Context, req ctrl.Reques klog.V(4).Infof("Reconciling configuration %q", req.NamespacedName) extCIDR := cfg.Status.Remote.CIDR.External - remoteUnknownSourceIP, err := utils.GetUnknownSourceIP(string(extCIDR)) + remoteUnknownSourceIP, err := ipamutils.GetUnknownSourceIP(string(extCIDR)) if err != nil { return ctrl.Result{}, fmt.Errorf("unable to get the unknown source IP: %w", err) } diff --git a/pkg/liqo-controller-manager/networking/external-network/remapping/finalizer.go b/pkg/liqo-controller-manager/networking/external-network/remapping/finalizer.go index 6be80ca193..881651225b 100644 --- a/pkg/liqo-controller-manager/networking/external-network/remapping/finalizer.go +++ b/pkg/liqo-controller-manager/networking/external-network/remapping/finalizer.go @@ -23,18 +23,18 @@ import ( ) const ( - // ipMappingsControllerFinalizer is the finalizer added to IP resources (related to mapping) to allow the controller to clean up. - ipMappingsControllerFinalizer = "ipmapping-nat-controller.liqo.io/finalizer" + // ipMappingControllerFinalizer is the finalizer added to IP resources (related to mapping) to allow the controller to clean up. + ipMappingControllerFinalizer = "ipmapping-nat-controller.liqo.io/finalizer" ) func (r *IPReconciler) ensureIPMappingFinalizerPresence( ctx context.Context, ip *ipamv1alpha1.IP) error { - controllerutil.AddFinalizer(ip, ipMappingsControllerFinalizer) + controllerutil.AddFinalizer(ip, ipMappingControllerFinalizer) return r.Client.Update(ctx, ip) } func (r *IPReconciler) ensureIPMappingFinalizerAbsence( ctx context.Context, ip *ipamv1alpha1.IP) error { - controllerutil.RemoveFinalizer(ip, ipMappingsControllerFinalizer) + controllerutil.RemoveFinalizer(ip, ipMappingControllerFinalizer) return r.Client.Update(ctx, ip) } diff --git a/pkg/liqo-controller-manager/networking/external-network/remapping/ip_controller.go b/pkg/liqo-controller-manager/networking/external-network/remapping/ip_controller.go index 4116545297..b776634e54 100644 --- a/pkg/liqo-controller-manager/networking/external-network/remapping/ip_controller.go +++ b/pkg/liqo-controller-manager/networking/external-network/remapping/ip_controller.go @@ -65,7 +65,7 @@ func (r *IPReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Re klog.V(4).Infof("Reconciling IP %s", req.String()) deleting := !ip.DeletionTimestamp.IsZero() - containsFinalizer := controllerutil.ContainsFinalizer(ip, ipMappingsControllerFinalizer) + containsFinalizer := controllerutil.ContainsFinalizer(ip, ipMappingControllerFinalizer) if !deleting { if !containsFinalizer { if err := r.ensureIPMappingFinalizerPresence(ctx, ip); err != nil { @@ -86,8 +86,8 @@ func (r *IPReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Re return ctrl.Result{}, nil } - if len(ip.Status.IPMappings) == 0 { - klog.Warningf("IP %s has no IP mappings yet", req.String()) + if ip.Status.IP == "" { + klog.Warningf("IP %q has no IP assigned yet", req.String()) return ctrl.Result{}, nil } diff --git a/pkg/liqo-controller-manager/networking/internal-network/configuration-controller/firewall.go b/pkg/liqo-controller-manager/networking/internal-network/configuration-controller/firewall.go index edcb2f64a6..004e27269a 100644 --- a/pkg/liqo-controller-manager/networking/internal-network/configuration-controller/firewall.go +++ b/pkg/liqo-controller-manager/networking/internal-network/configuration-controller/firewall.go @@ -27,7 +27,7 @@ import ( networkingv1beta1 "github.com/liqotech/liqo/apis/networking/v1beta1" firewallapi "github.com/liqotech/liqo/apis/networking/v1beta1/firewall" "github.com/liqotech/liqo/pkg/fabric" - "github.com/liqotech/liqo/pkg/ipamold/utils" + ipamutils "github.com/liqotech/liqo/pkg/utils/ipam" ) func (r *ConfigurationReconciler) ensureFirewallConfiguration(ctx context.Context, @@ -93,7 +93,7 @@ func forgeFirewallChain() *firewallapi.Chain { } func forgeFirewallNatRule(cfg *networkingv1beta1.Configuration, opts *Options) (natrules []firewallapi.NatRule, err error) { - unknownSourceIP, err := utils.GetUnknownSourceIP(cfg.Spec.Local.CIDR.External.String()) + unknownSourceIP, err := ipamutils.GetUnknownSourceIP(cfg.Spec.Local.CIDR.External.String()) if err != nil { return nil, fmt.Errorf("unable to get first IP from CIDR: %w", err) } diff --git a/pkg/liqo-controller-manager/networking/internal-network/route/internalnode_controller.go b/pkg/liqo-controller-manager/networking/internal-network/route/internalnode_controller.go index ab8d69525c..b8426505c1 100644 --- a/pkg/liqo-controller-manager/networking/internal-network/route/internalnode_controller.go +++ b/pkg/liqo-controller-manager/networking/internal-network/route/internalnode_controller.go @@ -32,7 +32,6 @@ import ( ipamv1alpha1 "github.com/liqotech/liqo/apis/ipam/v1alpha1" networkingv1beta1 "github.com/liqotech/liqo/apis/networking/v1beta1" "github.com/liqotech/liqo/pkg/consts" - "github.com/liqotech/liqo/pkg/ipamold/utils" configuration "github.com/liqotech/liqo/pkg/liqo-controller-manager/networking/external-network/configuration" "github.com/liqotech/liqo/pkg/utils/getters" "github.com/liqotech/liqo/pkg/utils/ipam" @@ -117,7 +116,7 @@ func (r *InternalNodeReconciler) Reconcile(ctx context.Context, req ctrl.Request return ctrl.Result{}, err } - unknownSourceIP, err := utils.GetUnknownSourceIP(extCIDR) + unknownSourceIP, err := ipam.GetUnknownSourceIP(extCIDR) if err != nil { return ctrl.Result{}, err } diff --git a/pkg/liqo-controller-manager/networking/ip-controller/exposition.go b/pkg/liqo-controller-manager/networking/ip-controller/exposition.go index d593af97e0..6212a94e77 100644 --- a/pkg/liqo-controller-manager/networking/ip-controller/exposition.go +++ b/pkg/liqo-controller-manager/networking/ip-controller/exposition.go @@ -96,10 +96,31 @@ func (r *IPReconciler) handleAssociatedService(ctx context.Context, ip *ipamv1al return nil } +// ensureAssociatedServiceAbsence ensures that the service associated to the IP (and its endpointslices) does not exist. +func (r *IPReconciler) ensureAssociatedServiceAbsence(ctx context.Context, ip *ipamv1alpha1.IP) error { + svc := v1.Service{ObjectMeta: metav1.ObjectMeta{ + Name: ip.Name, + Namespace: ip.Namespace, + }} + if err := ensureResourceAbsence(ctx, r.Client, &svc, "service"); err != nil { + return err + } + + eps := discoveryv1.EndpointSlice{ObjectMeta: metav1.ObjectMeta{ + Name: ip.Name, + Namespace: ip.Namespace, + }} + if err := ensureResourceAbsence(ctx, r.Client, &eps, "endpointslice"); err != nil { + return err + } + + return nil +} + // enforceResource ensures that the given resource exists. // It either creates or update the resource. -func enforceResource(ctx context.Context, r client.Client, obj client.Object, mutateFn controllerutil.MutateFn, resourceKind string) error { - op, err := controllerutil.CreateOrUpdate(ctx, r, obj, mutateFn) +func enforceResource(ctx context.Context, cl client.Client, obj client.Object, mutateFn controllerutil.MutateFn, resourceKind string) error { + op, err := controllerutil.CreateOrUpdate(ctx, cl, obj, mutateFn) if err != nil { klog.Errorf("error while creating/updating %s %q (operation: %s): %v", resourceKind, obj.GetName(), op, err) return err @@ -110,8 +131,8 @@ func enforceResource(ctx context.Context, r client.Client, obj client.Object, mu // ensureResourceAbsence ensures that the given resource does not exist. // If the resource does not exist, it does nothing. -func ensureResourceAbsence(ctx context.Context, r client.Client, obj client.Object, resourceKind string) error { - err := r.Get(ctx, types.NamespacedName{Name: obj.GetName(), Namespace: obj.GetNamespace()}, obj) +func ensureResourceAbsence(ctx context.Context, cl client.Client, obj client.Object, resourceKind string) error { + err := cl.Get(ctx, types.NamespacedName{Name: obj.GetName(), Namespace: obj.GetNamespace()}, obj) switch { case err != nil && !apierrors.IsNotFound(err): klog.Errorf("error while getting %s %q: %v", resourceKind, obj.GetName(), err) @@ -120,7 +141,7 @@ func ensureResourceAbsence(ctx context.Context, r client.Client, obj client.Obje // The resource does not exist, do nothing. klog.V(6).Infof("%s %q does not exist. Nothing to do", resourceKind, obj.GetName()) default: - if err := r.Delete(ctx, obj); err != nil { + if err := client.IgnoreNotFound(cl.Delete(ctx, obj)); err != nil { klog.Errorf("error while deleting %s %q: %v", resourceKind, obj.GetName(), err) return err } diff --git a/pkg/liqo-controller-manager/networking/ip-controller/ip_controller.go b/pkg/liqo-controller-manager/networking/ip-controller/ip_controller.go index b7bb641b9c..082df79a2b 100644 --- a/pkg/liqo-controller-manager/networking/ip-controller/ip_controller.go +++ b/pkg/liqo-controller-manager/networking/ip-controller/ip_controller.go @@ -16,14 +16,12 @@ package ipctrl import ( "context" - "slices" + "fmt" v1 "k8s.io/api/core/v1" discoveryv1 "k8s.io/api/discovery/v1" apierrors "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/types" "k8s.io/klog/v2" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" @@ -35,10 +33,8 @@ import ( ipamv1alpha1 "github.com/liqotech/liqo/apis/ipam/v1alpha1" networkingv1beta1 "github.com/liqotech/liqo/apis/networking/v1beta1" "github.com/liqotech/liqo/pkg/consts" - ipam "github.com/liqotech/liqo/pkg/ipamold" - "github.com/liqotech/liqo/pkg/utils/getters" + "github.com/liqotech/liqo/pkg/ipam" ipamutils "github.com/liqotech/liqo/pkg/utils/ipam" - "github.com/liqotech/liqo/pkg/utils/slice" ) const ( @@ -50,34 +46,34 @@ type IPReconciler struct { client.Client Scheme *runtime.Scheme - ipamClient ipam.IpamClient - externalCIDRSet bool + ipamClient ipam.IPAMClient + + externalCidrRef v1.ObjectReference + externalCidr networkingv1beta1.CIDR } // NewIPReconciler returns a new IPReconciler. -func NewIPReconciler(cl client.Client, s *runtime.Scheme, ipamClient ipam.IpamClient) *IPReconciler { +func NewIPReconciler(cl client.Client, s *runtime.Scheme, ipamClient ipam.IPAMClient) *IPReconciler { return &IPReconciler{ Client: cl, Scheme: s, - ipamClient: ipamClient, - externalCIDRSet: false, + ipamClient: ipamClient, } } // +kubebuilder:rbac:groups=ipam.liqo.io,resources=ips,verbs=get;list;watch;create;update;patch;delete // +kubebuilder:rbac:groups=ipam.liqo.io,resources=ips/status,verbs=get;list;watch;create;update;patch;delete // +kubebuilder:rbac:groups=ipam.liqo.io,resources=ips/finalizers,verbs=get;list;watch;create;update;patch;delete -// +kubebuilder:rbac:groups=networking.liqo.io,resources=configurations,verbs=get;list;watch +// +kubebuilder:rbac:groups=ipam.liqo.io,resources=networks,verbs=get;list;watch +// +kubebuilder:rbac:groups=ipam.liqo.io,resources=networks/status,verbs=get;list;watch // +kubebuilder:rbac:groups=core,resources=services,verbs=get;list;watch;create;update;patch;delete // +kubebuilder:rbac:groups=discovery.k8s.io,resources=endpointslices,verbs=get;list;watch;create;update;patch;delete // Reconcile Ip objects. func (r *IPReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { - var ip ipamv1alpha1.IP - var desiredIP networkingv1beta1.IP - // Fetch the IP instance + var ip ipamv1alpha1.IP if err := r.Get(ctx, req.NamespacedName, &ip); err != nil { if apierrors.IsNotFound(err) { klog.V(4).Infof(" %q not found", req.NamespacedName) @@ -87,204 +83,243 @@ func (r *IPReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Re return ctrl.Result{}, err } - if !r.externalCIDRSet { - // Retrieve the externalCIDR of the local cluster - _, err := ipamutils.GetExternalCIDR(ctx, r.Client) - if apierrors.IsNotFound(err) { - klog.Errorf("ExternalCIDR is not set yet. Configure it to correctly handle IP mappings") - return ctrl.Result{}, err - } else if err != nil { - klog.Errorf("error while retrieving externalCIDR: %v", err) - return ctrl.Result{}, err - } - // The external CIDR is set, we do not need to check it again in successive reconciliations. - r.externalCIDRSet = true - } - - desiredIP = ip.Spec.IP - - // Get the clusterIDs of all remote clusters - configurations, err := getters.ListConfigurationsByLabel(ctx, r.Client, labels.Everything()) - if err != nil { - klog.Errorf("error while listing virtual nodes: %v", err) + // Get the CIDR of the Network referenced by the IP. + // If it is not set, it is defaulted to the external CIDR of the local cluster. + networkRef, cidr, err := r.handleNetworkRef(ctx, &ip) + if client.IgnoreNotFound(err) != nil { + klog.Errorf("error while handling network reference for IP %q: %v", req.NamespacedName, err) return ctrl.Result{}, err } - clusterIDs := getters.RetrieveClusterIDsFromObjectsLabels(slice.ToPointerSlice(configurations.Items)) - - if ip.GetDeletionTimestamp().IsZero() { - if !controllerutil.ContainsFinalizer(&ip, ipamIPFinalizer) { - // Add finalizer to prevent deletion without unmapping the IP. - controllerutil.AddFinalizer(&ip, ipamIPFinalizer) + networkExists := !apierrors.IsNotFound(err) + deleting := !ip.GetDeletionTimestamp().IsZero() - // Update the IP object - if err := r.Update(ctx, &ip); err != nil { - klog.Errorf("error while adding finalizer to IP %q: %v", req.NamespacedName, err) - return ctrl.Result{}, err - } - klog.Infof("finalizer %q correctly added to IP %q", ipamIPFinalizer, req.NamespacedName) + // Print a warning if the IP is not being deleted and it is referencing a non-existing network. + if !deleting && !networkExists { + klog.Warningf("network referenced by IP %q does not exist", req.NamespacedName) + } - // We return immediately and wait for the next reconcile to eventually update the status. - return ctrl.Result{}, nil + // The resource is being deleted or the referenced network does not exist: + // - call the IPAM to release the IP if it set, and empty the status. + // - remove eventual finalizers from the resource. + if deleting || !networkExists { + if err := r.handleIPStatusDeletion(ctx, &ip); err != nil { + klog.Errorf("error while handling IP status deletion %q: %v", req.NamespacedName, err) + return ctrl.Result{}, err } - needUpdate, err := r.forgeIPMappings(ctx, clusterIDs, desiredIP, &ip) - if err != nil { + if err := r.ensureAssociatedServiceAbsence(ctx, &ip); err != nil { + klog.Errorf("error while ensuring absence of the associated service of IP %q: %v", req.NamespacedName, err) return ctrl.Result{}, err } - // Update resource status if needed - if needUpdate { - if err := r.Client.Status().Update(ctx, &ip); err != nil { - klog.Errorf("error while updating IP %q status: %v", req.NamespacedName, err) + // Remove finalizer from the IP resource if present. + if controllerutil.ContainsFinalizer(&ip, ipamIPFinalizer) { + controllerutil.RemoveFinalizer(&ip, ipamIPFinalizer) + if err := r.Update(ctx, &ip); err != nil { + klog.Errorf("error while removing finalizer from IP %q: %v", req.NamespacedName, err) return ctrl.Result{}, err } - klog.Infof("updated IP %q status", req.NamespacedName) + klog.Infof("finalizer %q correctly removed from IP %q", ipamIPFinalizer, req.NamespacedName) } - // Create service and associated endpointslice if the template is defined - if err := r.handleAssociatedService(ctx, &ip); err != nil { + return ctrl.Result{}, nil + } + + // Add finalizer to prevent deletion without releasing the IP. + if !controllerutil.ContainsFinalizer(&ip, ipamIPFinalizer) { + controllerutil.AddFinalizer(&ip, ipamIPFinalizer) + if err := r.Update(ctx, &ip); err != nil { + klog.Errorf("error while adding finalizer to IP %q: %v", req.NamespacedName, err) return ctrl.Result{}, err } - } else if controllerutil.ContainsFinalizer(&ip, ipamIPFinalizer) { - // the resource is being deleted, but the finalizer is present: - // - unmap the remapped IPs - // - remove finalizer from the resource. - if err := r.handleDelete(ctx, clusterIDs, desiredIP, &ip); err != nil { + klog.Infof("finalizer %q correctly added to IP %q", ipamIPFinalizer, req.NamespacedName) + + // We return immediately and wait for the next reconcile to eventually update the status. + return ctrl.Result{}, nil + } + + // Add network reference to the IP in the labels. This is used to trigger the reconciliation + // of the IP by watching deletion events of the father Network. + if ip.Labels == nil { + ip.Labels = make(map[string]string) + } + ip.Labels[consts.NetworkNamespaceLabelKey] = networkRef.Namespace + ip.Labels[consts.NetworkNameLabelKey] = networkRef.Name + + if err := r.Update(ctx, &ip); err != nil { + klog.Errorf("error while updating IP %q: %v", req.NamespacedName, err) + return ctrl.Result{}, err + } + + // Forge the IP status if it is not set yet. + if ip.Status.IP == "" { + if err := r.forgeIPStatus(ctx, &ip, cidr); err != nil { + klog.Errorf("error while forging IP status for IP %q: %v", req.NamespacedName, err) return ctrl.Result{}, err } - // Update the IP object - if err := r.Update(ctx, &ip); err != nil { - klog.Errorf("error while removing finalizer from IP %q: %v", req.NamespacedName, err) + if err := r.Client.Status().Update(ctx, &ip); err != nil { + klog.Errorf("error while updating IP %q: %v", req.NamespacedName, err) return ctrl.Result{}, err } - klog.Infof("finalizer %q correctly removed from IP %q", ipamIPFinalizer, req.NamespacedName) + klog.Infof("updated IP %q", req.NamespacedName) + } - // We do not have to delete possible service and endpointslice associated, as already deleted by - // the Kubernetes garbage collector (since they are owned by the IP resource). + // Create service and associated endpointslice if the template is defined + if err := r.handleAssociatedService(ctx, &ip); err != nil { + klog.Errorf("error while handling associated service for IP %q: %v", req.NamespacedName, err) + return ctrl.Result{}, err } return ctrl.Result{}, nil } // SetupWithManager monitors IP resources. -func (r *IPReconciler) SetupWithManager(ctx context.Context, mgr ctrl.Manager, workers int) error { - // List all IP resources and enqueue them. - enqueuer := func(_ context.Context, _ client.Object) []reconcile.Request { - var ipList ipamv1alpha1.IPList - if err := r.List(ctx, &ipList); err != nil { - klog.Errorf("error while listing IPs: %v", err) - return nil - } - var requests []reconcile.Request - for i := range ipList.Items { - requests = append(requests, reconcile.Request{NamespacedName: types.NamespacedName{ - Namespace: ipList.Items[i].Namespace, Name: ipList.Items[i].Name}}) - } - return requests - } - +func (r *IPReconciler) SetupWithManager(mgr ctrl.Manager, workers int) error { return ctrl.NewControllerManagedBy(mgr).Named(consts.CtrlIP). For(&ipamv1alpha1.IP{}). Owns(&v1.Service{}). Owns(&discoveryv1.EndpointSlice{}). - Watches(&networkingv1beta1.Configuration{}, handler.EnqueueRequestsFromMapFunc(enqueuer)). + Watches(&ipamv1alpha1.Network{}, handler.EnqueueRequestsFromMapFunc(r.ipEnqueuerFromNetwork)). WithOptions(controller.Options{MaxConcurrentReconciles: workers}). Complete(r) } -// forgeIPMappings forge the IP mappings for each remote cluster. Return true if the status must be updated, false otherwise. -func (r *IPReconciler) forgeIPMappings(ctx context.Context, clusterIDs []string, desiredIP networkingv1beta1.IP, ip *ipamv1alpha1.IP) (bool, error) { - // Update IP status for each remote cluster - needUpdate := false +func (r *IPReconciler) ipEnqueuerFromNetwork(ctx context.Context, obj client.Object) []ctrl.Request { + var requests []reconcile.Request - if ip.Status.IPMappings == nil { - ip.Status.IPMappings = make(map[string]networkingv1beta1.IP) + // Get the IPs associated with the Network. + var ipList ipamv1alpha1.IPList + if err := r.List(ctx, &ipList, client.MatchingLabels{ + consts.NetworkNamespaceLabelKey: obj.GetNamespace(), + consts.NetworkNameLabelKey: obj.GetName(), + }); err != nil { + klog.Errorf("error while listing IPs associated with Network %q: %v", client.ObjectKeyFromObject(obj), err) + return nil + } + + // Enqueue reconcile requests for each IP associated with the Network. + for i := range ipList.Items { + requests = append(requests, reconcile.Request{ + NamespacedName: client.ObjectKeyFromObject(&ipList.Items[i]), + }) } - for i := range clusterIDs { - remoteClusterID := &clusterIDs[i] - // Update IP status if it is not set yet - // The IPAM function that maps IPs is not idempotent, so we avoid to call it - // multiple times by checking if the IP for that remote cluster is already set. - _, found := ip.Status.IPMappings[*remoteClusterID] - if !found { - remappedIP, err := getRemappedIP(ctx, r.ipamClient, *remoteClusterID, desiredIP) + return requests +} + +// handleNetworkRef get the CIDR of the Network referenced by the IP, or default to the +// external CIDR of the local cluster if the IP has no NetworkRef set. +func (r *IPReconciler) handleNetworkRef(ctx context.Context, ip *ipamv1alpha1.IP) (*v1.ObjectReference, networkingv1beta1.CIDR, error) { + // If the IP has not set a reference to a Network CIDR, we remap it on the external CIDR of the local cluster. + if ip.Spec.NetworkRef == nil { + if r.externalCidr == "" { + network, err := ipamutils.GetExternalCIDRNetwork(ctx, r.Client) if err != nil { - return false, err + return nil, "", err + } + // The externalCIDR Network has no CIDR set yet, we return an error. + if network.Status.CIDR == "" { + return nil, "", fmt.Errorf("externalCIDR is not set yet. Configure it to correctly handle IP mapping") } - ip.Status.IPMappings[*remoteClusterID] = remappedIP - needUpdate = true + + r.externalCidrRef = v1.ObjectReference{ + Namespace: network.Namespace, + Name: network.Name, + } + r.externalCidr = network.Status.CIDR } + return &r.externalCidrRef, r.externalCidr, nil } - // Check if the IPMappings has entries associated to clusters that have been deleted (i.e., the virtualNode is missing) - for entry := range ip.Status.IPMappings { - if !slices.Contains(clusterIDs, entry) { - // We ignore eventual errors from the IPAM because the entries in the IpamStorage for that cluster - // may have been already removed. - _ = deleteRemappedIP(ctx, r.ipamClient, entry, desiredIP) - delete(ip.Status.IPMappings, entry) - needUpdate = true + // Retrieve the Network object referenced by the IP. + var network ipamv1alpha1.Network + if err := r.Get(ctx, client.ObjectKey{Namespace: ip.Spec.NetworkRef.Namespace, Name: ip.Spec.NetworkRef.Name}, &network); err != nil { + return nil, "", err + } + if network.Status.CIDR == "" { + return nil, "", fmt.Errorf("network %s/%s has no CIDR set yet", network.Namespace, network.Name) + } + return ip.Spec.NetworkRef, network.Status.CIDR, nil +} + +// forgeIPStatus forge the IP status. +func (r *IPReconciler) forgeIPStatus(ctx context.Context, ip *ipamv1alpha1.IP, cidr networkingv1beta1.CIDR) error { + // Update IP status if it is not set yet. + // The IPAM function that maps IPs is not idempotent, so we avoid to call it + // multiple times by checking if the IP is already set. + if ip.Status.IP == "" { + acquiredIP, err := acquireIP(ctx, r.ipamClient, cidr) + if err != nil { + return err } + ip.Status.IP = acquiredIP + ip.Status.CIDR = cidr } - return needUpdate, nil + return nil } -// handleDelete handles the deletion of the IP resource. It call the IPAM to unmap the IPs of each remote cluster. -func (r *IPReconciler) handleDelete(ctx context.Context, clusterIDs []string, desiredIP networkingv1beta1.IP, ip *ipamv1alpha1.IP) error { - for i := range clusterIDs { - remoteClusterID := &clusterIDs[i] - if err := deleteRemappedIP(ctx, r.ipamClient, *remoteClusterID, desiredIP); err != nil { +// handleIPStatusDeletion handles the deletion of the IP status. +// It calls the IPAM to release the IP and empties the status. +func (r *IPReconciler) handleIPStatusDeletion(ctx context.Context, ip *ipamv1alpha1.IP) error { + if ip.Status.IP != "" && ip.Status.CIDR != "" { + if err := releaseIP(ctx, r.ipamClient, ip.Status.IP, ip.Status.CIDR); err != nil { return err } - delete(ip.Status.IPMappings, *remoteClusterID) - } - // Remove status and finalizer, and update the object. - ip.Status.IPMappings = nil - controllerutil.RemoveFinalizer(ip, ipamIPFinalizer) + // Remove status and finalizer, and update the object. + ip.Status = ipamv1alpha1.IPStatus{} + + // Update the IP status + if err := r.Client.Status().Update(ctx, ip); err != nil { + return err + } + + klog.Infof("IP %q status correctly cleaned", client.ObjectKeyFromObject(ip)) + } return nil } -// getRemappedIP returns the remapped IP for the given IP and remote clusterID. -func getRemappedIP(ctx context.Context, ipamClient ipam.IpamClient, remoteClusterID string, - desiredIP networkingv1beta1.IP) (networkingv1beta1.IP, error) { +// acquireIP acquire a free IP of a given CIDR from the IPAM. +func acquireIP(ctx context.Context, ipamClient ipam.IPAMClient, cidr networkingv1beta1.CIDR) (networkingv1beta1.IP, error) { switch ipamClient.(type) { case nil: - // IPAM is not enabled, use original IP from spec - return desiredIP, nil + // IPAM is not enabled, return an error. + return "", fmt.Errorf("IPAM is not enabled") default: // interact with the IPAM to retrieve the correct mapping. - response, err := ipamClient.MapEndpointIP(ctx, &ipam.MapRequest{ - ClusterID: remoteClusterID, Ip: desiredIP.String()}) + response, err := ipamClient.IPAcquire(ctx, &ipam.IPAcquireRequest{ + Cidr: string(cidr), + }) if err != nil { - klog.Errorf("IPAM: error while mapping IP %s for remote cluster %q: %v", desiredIP, remoteClusterID, err) + klog.Errorf("IPAM: error while acquiring IP from CIDR %q: %v", cidr, err) return "", err } - klog.Infof("IPAM: mapped IP %s to %s for remote cluster %q", desiredIP, response.Ip, remoteClusterID) + klog.Infof("IPAM: acquired IP %q from CIDR %q", response.Ip, cidr) return networkingv1beta1.IP(response.Ip), nil } } -// deleteRemappedIP unmaps the IP for the given remote clusterID. -func deleteRemappedIP(ctx context.Context, ipamClient ipam.IpamClient, remoteClusterID string, desiredIP networkingv1beta1.IP) error { +// releaseIP release a IP of a given CIDR from the IPAM. +func releaseIP(ctx context.Context, ipamClient ipam.IPAMClient, ip networkingv1beta1.IP, cidr networkingv1beta1.CIDR) error { switch ipamClient.(type) { case nil: - // If the IPAM is not enabled we do not need to release the translation. + // If the IPAM is not enabled we do not need to release any IP. return nil default: // Interact with the IPAM to release the translation. - _, err := ipamClient.UnmapEndpointIP(ctx, &ipam.UnmapRequest{ - ClusterID: remoteClusterID, Ip: desiredIP.String()}) + _, err := ipamClient.IPRelease(ctx, &ipam.IPReleaseRequest{ + Ip: string(ip), + Cidr: string(cidr), + }) if err != nil { - klog.Errorf("IPAM: error while unmapping IP %s for remote cluster %q: %v", desiredIP, remoteClusterID, err) + klog.Errorf("IPAM: error while de allocating IP %q from CIDR: %q: %v", ip, cidr, err) return err } - klog.Infof("IPAM: unmapped IP %s for remote cluster %q", desiredIP, remoteClusterID) + klog.Infof("IPAM: de-allocated IP %q from CIDR %q", ip, cidr) return nil } } diff --git a/pkg/liqoctl/authenticate/cluster.go b/pkg/liqoctl/authenticate/cluster.go index 0d1015e2b5..b02dd1f4ff 100644 --- a/pkg/liqoctl/authenticate/cluster.go +++ b/pkg/liqoctl/authenticate/cluster.go @@ -25,6 +25,7 @@ import ( authv1beta1 "github.com/liqotech/liqo/apis/authentication/v1beta1" liqov1beta1 "github.com/liqotech/liqo/apis/core/v1beta1" ipamv1alpha1 "github.com/liqotech/liqo/apis/ipam/v1alpha1" + "github.com/liqotech/liqo/pkg/consts" authutils "github.com/liqotech/liqo/pkg/liqo-controller-manager/authentication/utils" "github.com/liqotech/liqo/pkg/liqoctl/factory" "github.com/liqotech/liqo/pkg/liqoctl/output" @@ -220,17 +221,17 @@ func (c *Cluster) GetAPIServerProxyRemappedIP(ctx context.Context) (string, erro var ip ipamv1alpha1.IP err := c.local.CRClient.Get(ctx, types.NamespacedName{ Namespace: c.local.LiqoNamespace, - Name: "api-server-proxy", + Name: consts.IPTypeAPIServerProxy, }, &ip) if err != nil { return "", err } - for _, ipam := range ip.Status.IPMappings { - return ipam.String(), nil + if ip.Status.IP == "" { + return "", fmt.Errorf("no IP found, make sure the Liqo Networking module is enabled and working") } - return "", fmt.Errorf("no IP found, make sure the Liqo Networking module is enabled and working") + return string(ip.Status.IP), nil } // RemapIPExternalCIDR remaps the given IP address to the external CIDR of the remote cluster. diff --git a/pkg/liqoctl/test/network/setup/ip.go b/pkg/liqoctl/test/network/setup/ip.go index 9fd42e29ab..e0bbefa34e 100644 --- a/pkg/liqoctl/test/network/setup/ip.go +++ b/pkg/liqoctl/test/network/setup/ip.go @@ -75,7 +75,7 @@ func WaitIPRemapped(ctx context.Context, cl ctrlclient.Client) error { if err := cl.Get(ctx, ctrlclient.ObjectKey{Name: IPName, Namespace: NamespaceName}, &ip); err != nil { return false, err } - if len(ip.Status.IPMappings) == 0 { + if ip.Status.IP == "" { return false, nil } return true, nil diff --git a/pkg/utils/getters/k8sGetters.go b/pkg/utils/getters/k8sGetters.go index 298c502bea..c7287625f1 100644 --- a/pkg/utils/getters/k8sGetters.go +++ b/pkg/utils/getters/k8sGetters.go @@ -948,21 +948,21 @@ func GetUniqueNetworkByLabel(ctx context.Context, cl client.Client, lSelector la return nil, err } - switch len(networks.Items) { + switch len(networks) { case 0: return nil, kerrors.NewNotFound(ipamv1alpha1.NetworkGroupResource, ipamv1alpha1.NetworkResource) case 1: - return &networks.Items[0], nil + return &networks[0], nil default: return nil, fmt.Errorf("multiple Network resources found for label selector %q", lSelector) } } // GetNetworksByLabel retrieves the Network resources with the given labelSelector. -func GetNetworksByLabel(ctx context.Context, cl client.Client, lSelector labels.Selector) (*ipamv1alpha1.NetworkList, error) { +func GetNetworksByLabel(ctx context.Context, cl client.Client, lSelector labels.Selector) ([]ipamv1alpha1.Network, error) { var networks ipamv1alpha1.NetworkList if err := cl.List(ctx, &networks, &client.ListOptions{LabelSelector: lSelector}); err != nil { return nil, err } - return &networks, nil + return networks.Items, nil } diff --git a/pkg/utils/ipam/ips.go b/pkg/utils/ipam/ips.go index 0b5f2abea3..cdebfb931d 100644 --- a/pkg/utils/ipam/ips.go +++ b/pkg/utils/ipam/ips.go @@ -15,6 +15,10 @@ package ipam import ( + "fmt" + + "github.com/google/nftables" + ipamv1alpha1 "github.com/liqotech/liqo/apis/ipam/v1alpha1" networkingv1beta1 "github.com/liqotech/liqo/apis/networking/v1beta1" "github.com/liqotech/liqo/pkg/consts" @@ -34,8 +38,17 @@ func IsAPIServerProxyIP(ip *ipamv1alpha1.IP) bool { // GetRemappedIP returns the remapped IP of the given IP resource. func GetRemappedIP(ip *ipamv1alpha1.IP) networkingv1beta1.IP { - for _, ipremapped := range ip.Status.IPMappings { - return ipremapped + return ip.Status.IP +} + +// GetUnknownSourceIP returns the IP address used to map unknown sources. +func GetUnknownSourceIP(extCIDR string) (string, error) { + if extCIDR == "" { + return "", fmt.Errorf("ExternalCIDR not set") + } + firstExtCIDRip, _, err := nftables.NetFirstAndLastIP(extCIDR) + if err != nil { + return "", fmt.Errorf("cannot get first IP of ExternalCIDR") } - return "" + return firstExtCIDRip.String(), nil } diff --git a/pkg/utils/ipam/networks.go b/pkg/utils/ipam/networks.go index 7e1118ef18..577d6d2c58 100644 --- a/pkg/utils/ipam/networks.go +++ b/pkg/utils/ipam/networks.go @@ -30,35 +30,58 @@ import ( liqogetters "github.com/liqotech/liqo/pkg/utils/getters" ) -// GetPodCIDR retrieves the podCIDR of the local cluster. -func GetPodCIDR(ctx context.Context, cl client.Client) (string, error) { - nw, err := liqogetters.GetUniqueNetworkByLabel(ctx, cl, labels.SelectorFromSet(map[string]string{ +// GetPodCIDRNetwork retrieves the Network resource of type PodCIDR. +func GetPodCIDRNetwork(ctx context.Context, cl client.Client) (*ipamv1alpha1.Network, error) { + return liqogetters.GetUniqueNetworkByLabel(ctx, cl, labels.SelectorFromSet(map[string]string{ consts.NetworkTypeLabelKey: string(consts.NetworkTypePodCIDR), })) +} + +// GetPodCIDR retrieves the podCIDR of the local cluster. +func GetPodCIDR(ctx context.Context, cl client.Client) (string, error) { + nw, err := GetPodCIDRNetwork(ctx, cl) if err != nil { return "", err } - return nw.Spec.CIDR.String(), nil + if nw.Status.CIDR == "" { + return "", fmt.Errorf("the pod CIDR is not yet configured: missing status on the Network resource") + } + + return nw.Status.CIDR.String(), nil } -// GetServiceCIDR retrieves the serviceCIDR of the local cluster. -func GetServiceCIDR(ctx context.Context, cl client.Client) (string, error) { - nw, err := liqogetters.GetUniqueNetworkByLabel(ctx, cl, labels.SelectorFromSet(map[string]string{ +// GetServiceCIDRNetwork retrieves the Network resource of type ServiceCIDR. +func GetServiceCIDRNetwork(ctx context.Context, cl client.Client) (*ipamv1alpha1.Network, error) { + return liqogetters.GetUniqueNetworkByLabel(ctx, cl, labels.SelectorFromSet(map[string]string{ consts.NetworkTypeLabelKey: string(consts.NetworkTypeServiceCIDR), })) +} + +// GetServiceCIDR retrieves the serviceCIDR of the local cluster. +func GetServiceCIDR(ctx context.Context, cl client.Client) (string, error) { + nw, err := GetServiceCIDRNetwork(ctx, cl) if err != nil { return "", err } - return nw.Spec.CIDR.String(), nil + if nw.Status.CIDR == "" { + return "", fmt.Errorf("the service CIDR is not yet configured: missing status on the Network resource") + } + + return nw.Status.CIDR.String(), nil } -// GetExternalCIDR retrieves the externalCIDR of the local cluster. -func GetExternalCIDR(ctx context.Context, cl client.Client) (string, error) { - nw, err := liqogetters.GetUniqueNetworkByLabel(ctx, cl, labels.SelectorFromSet(map[string]string{ +// GetExternalCIDRNetwork retrieves the Network resource of type ExternalCIDR. +func GetExternalCIDRNetwork(ctx context.Context, cl client.Client) (*ipamv1alpha1.Network, error) { + return liqogetters.GetUniqueNetworkByLabel(ctx, cl, labels.SelectorFromSet(map[string]string{ consts.NetworkTypeLabelKey: string(consts.NetworkTypeExternalCIDR), })) +} + +// GetExternalCIDR retrieves the externalCIDR of the local cluster. +func GetExternalCIDR(ctx context.Context, cl client.Client) (string, error) { + nw, err := GetExternalCIDRNetwork(ctx, cl) if err != nil { return "", err } @@ -70,11 +93,16 @@ func GetExternalCIDR(ctx context.Context, cl client.Client) (string, error) { return nw.Status.CIDR.String(), nil } -// GetInternalCIDR retrieves the internalCIDR of the local cluster. -func GetInternalCIDR(ctx context.Context, cl client.Client) (string, error) { - nw, err := liqogetters.GetUniqueNetworkByLabel(ctx, cl, labels.SelectorFromSet(map[string]string{ +// GetInternalCIDRNetwork retrieves the Network resource of type InternalCIDR. +func GetInternalCIDRNetwork(ctx context.Context, cl client.Client) (*ipamv1alpha1.Network, error) { + return liqogetters.GetUniqueNetworkByLabel(ctx, cl, labels.SelectorFromSet(map[string]string{ consts.NetworkTypeLabelKey: string(consts.NetworkTypeInternalCIDR), })) +} + +// GetInternalCIDR retrieves the internalCIDR of the local cluster. +func GetInternalCIDR(ctx context.Context, cl client.Client) (string, error) { + nw, err := GetInternalCIDRNetwork(ctx, cl) if err != nil { return "", err } @@ -86,19 +114,25 @@ func GetInternalCIDR(ctx context.Context, cl client.Client) (string, error) { return nw.Status.CIDR.String(), nil } -// GetReservedSubnets retrieves the reserved subnets of the local cluster. -func GetReservedSubnets(ctx context.Context, cl client.Client) ([]string, error) { - var reservedSubnets []string - - networks, err := liqogetters.GetNetworksByLabel(ctx, cl, labels.SelectorFromSet(map[string]string{ +// GetReservedSubnetNetworks retrieves the Network resources of type Reserved. +func GetReservedSubnetNetworks(ctx context.Context, cl client.Client) ([]ipamv1alpha1.Network, error) { + return liqogetters.GetNetworksByLabel(ctx, cl, labels.SelectorFromSet(map[string]string{ consts.NetworkTypeLabelKey: string(consts.NetworkTypeReserved), })) +} + +// GetReservedSubnets retrieves the reserved subnets of the local cluster. +func GetReservedSubnets(ctx context.Context, cl client.Client) ([]string, error) { + networks, err := GetReservedSubnetNetworks(ctx, cl) if err != nil { return nil, err } - for i := range networks.Items { - reservedSubnets = append(reservedSubnets, networks.Items[i].Spec.CIDR.String()) + var reservedSubnets []string + for i := range networks { + if networks[i].Status.CIDR != "" { + reservedSubnets = append(reservedSubnets, networks[i].Status.CIDR.String()) + } } return reservedSubnets, nil diff --git a/pkg/virtualKubelet/provider/provider.go b/pkg/virtualKubelet/provider/provider.go index 4bcfa12d2b..5271f6a491 100644 --- a/pkg/virtualKubelet/provider/provider.go +++ b/pkg/virtualKubelet/provider/provider.go @@ -126,16 +126,11 @@ func NewLiqoProvider(ctx context.Context, cfg *InitConfig, eb record.EventBroadc return "", err } - if ip.Status.IPMappings == nil { - return "", errors.New("no IP mappings found for the API server") - } - - v, ok := ip.Status.IPMappings[string(cfg.RemoteCluster)] - if !ok { + if ip.Status.IP == "" { return "", errors.New("no IP mapping found for the remote cluster API server") } - return string(v), nil + return string(ip.Status.IP), nil }, NetConfiguration: cfg.NetConfiguration, } diff --git a/pkg/virtualKubelet/reflection/exposition/endpointslice.go b/pkg/virtualKubelet/reflection/exposition/endpointslice.go index e0d7745c8c..6eab45aa6a 100644 --- a/pkg/virtualKubelet/reflection/exposition/endpointslice.go +++ b/pkg/virtualKubelet/reflection/exposition/endpointslice.go @@ -267,10 +267,10 @@ func (ner *NamespacedEndpointSliceReflector) MapEndpointIPFromIPResource(origina for i := range ips { if ips[i].Spec.IP.String() == original { remappedIP := ipamutils.GetRemappedIP(ips[i]) - if len(ips[i].Status.IPMappings) > 0 { - return remappedIP.String(), nil + if remappedIP == "" { + return "", fmt.Errorf("resource IP %q (%q) has not been mapped yet", ips[i].Name, ips[i].Spec.IP) } - return "", fmt.Errorf("resource IP %s has not been mapped yet", ips[i].Name) + return remappedIP.String(), nil } } return original, fmt.Errorf("resource IP %s not found", original) diff --git a/pkg/virtualKubelet/reflection/exposition/endpointslice_test.go b/pkg/virtualKubelet/reflection/exposition/endpointslice_test.go index 18b5da1622..6544273a46 100644 --- a/pkg/virtualKubelet/reflection/exposition/endpointslice_test.go +++ b/pkg/virtualKubelet/reflection/exposition/endpointslice_test.go @@ -106,9 +106,7 @@ var _ = Describe("EndpointSlice Reflection Tests", func() { ipamIP, err = liqoClient.IpamV1alpha1().IPs(namespace).Create(ctx, ipamIP, metav1.CreateOptions{}) Expect(err).ToNot(HaveOccurred()) ipamIP.Status = ipamv1alpha1.IPStatus{ - IPMappings: map[string]networkingv1beta1.IP{ - RemoteClusterID: networkingv1beta1.IP(remappedIP), - }, + IP: networkingv1beta1.IP(remappedIP), } ipamIP, err = liqoClient.IpamV1alpha1().IPs(namespace).UpdateStatus(ctx, ipamIP, metav1.UpdateOptions{}) Expect(err).ToNot(HaveOccurred())