From e16c9eedbec3a27d0adcd58d8d856cc34d6e474a Mon Sep 17 00:00:00 2001 From: Alessandro Olivero Date: Fri, 25 Oct 2024 14:30:23 +0200 Subject: [PATCH] initialize ipam --- apis/ipam/v1alpha1/ip_types.go | 5 + cmd/ipam/main.go | 12 +- .../liqo-crds/crds/ipam.liqo.io_ips.yaml | 13 ++ pkg/ipam/initialize.go | 148 ++++++++++++++++++ pkg/ipam/ipam.go | 21 ++- 5 files changed, 194 insertions(+), 5 deletions(-) create mode 100644 pkg/ipam/initialize.go diff --git a/apis/ipam/v1alpha1/ip_types.go b/apis/ipam/v1alpha1/ip_types.go index 68d7104e93..654d3ebf92 100644 --- a/apis/ipam/v1alpha1/ip_types.go +++ b/apis/ipam/v1alpha1/ip_types.go @@ -63,6 +63,11 @@ type IPSpec struct { 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"` + // CIDR is the network CIDR where the IP is allocated. + CIDR networkingv1beta1.CIDR `json:"cidr,omitempty"` } // +kubebuilder:object:root=true diff --git a/cmd/ipam/main.go b/cmd/ipam/main.go index 7129c5a00b..b265894b4d 100644 --- a/cmd/ipam/main.go +++ b/cmd/ipam/main.go @@ -31,6 +31,7 @@ import ( "k8s.io/client-go/tools/record" "k8s.io/klog/v2" ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/log" ipamv1alpha1 "github.com/liqotech/liqo/apis/ipam/v1alpha1" @@ -100,6 +101,12 @@ func run(cmd *cobra.Command, _ []string) error { // Get the rest config. cfg := restcfg.SetRateLimiter(ctrl.GetConfigOrDie()) + options.Config = cfg + cl, err := client.New(cfg, client.Options{}) + if err != nil { + return err + } + options.Client = cl if options.EnableLeaderElection { if leader, err := leaderelection.Blocking(ctx, cfg, record.NewBroadcaster(), &leaderelection.Opts{ @@ -120,7 +127,10 @@ func run(cmd *cobra.Command, _ []string) error { } hs := health.NewServer() - liqoIPAM := ipam.New(&options) + liqoIPAM, err := ipam.New(ctx, &options) + if err != nil { + return err + } lis, err := net.Listen("tcp", fmt.Sprintf("0.0.0.0:%d", options.Port)) if err != nil { 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 4c704246d2..29c699f961 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 @@ -439,6 +439,17 @@ spec: status: description: IPStatus defines remapped IPs. properties: + cidr: + description: CIDR is the network CIDR where the IP is allocated. + format: cidr + type: string + ip: + description: IP is the remapped IP. + format: ipv4 + type: string + x-kubernetes-validations: + - message: IP field is immutable + rule: self == oldSelf ipMappings: additionalProperties: description: IP defines a syntax validated IP. @@ -447,6 +458,8 @@ spec: description: IPMappings contains the mapping of the local IP for each remote cluster. type: object + required: + - ip type: object required: - spec diff --git a/pkg/ipam/initialize.go b/pkg/ipam/initialize.go new file mode 100644 index 0000000000..268fd684f0 --- /dev/null +++ b/pkg/ipam/initialize.go @@ -0,0 +1,148 @@ +// Copyright 2019-2024 The Liqo Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package ipam + +import ( + "context" + + klog "k8s.io/klog/v2" + "sigs.k8s.io/controller-runtime/pkg/client" + + ipamv1alpha1 "github.com/liqotech/liqo/apis/ipam/v1alpha1" + "github.com/liqotech/liqo/pkg/consts" +) + +// +kubebuilder:rbac:groups=ipam.liqo.io,resources=ips,verbs=get;list;watch +// +kubebuilder:rbac:groups=ipam.liqo.io,resources=networks,verbs=get;list;watch + +type ipCidr struct { + ip string + cidr string +} + +func (lipam *LiqoIPAM) initialize(ctx context.Context) error { + if err := lipam.initializeNetworks(ctx); err != nil { + return err + } + + if err := lipam.initializeIPs(ctx); err != nil { + return err + } + + klog.Info("IPAM initialized") + return nil +} + +func (lipam *LiqoIPAM) initializeNetworks(ctx context.Context) error { + // Initialize the networks. + nets, err := lipam.getReservedNetworks(ctx) + if err != nil { + return err + } + + for _, net := range nets { + if err := lipam.reserveNetwork(net); err != nil { + klog.Errorf("Failed to reserve network %s: %v", net, err) + return err + } + } + + return nil +} + +func (lipam *LiqoIPAM) initializeIPs(ctx context.Context) error { + // Initialize the IPs. + ips, err := lipam.getReservedIPs(ctx) + if err != nil { + return err + } + + for _, ip := range ips { + if err := lipam.reserveIP(ip.ip, ip.cidr); err != nil { + klog.Errorf("Failed to reserve IP %s in network %s: %v", ip.ip, ip.cidr, err) + return err + } + } + + return nil +} + +func (lipam *LiqoIPAM) getReservedNetworks(ctx context.Context) ([]string, error) { + var nets []string + var networks ipamv1alpha1.NetworkList + if err := lipam.Options.Client.List(ctx, &networks); err != nil { + return nil, err + } + + for i := range networks.Items { + net := &networks.Items[i] + + var cidr string + switch { + case net.Labels != nil && net.Labels[consts.NetworkNotRemappedLabelKey] == consts.NetworkNotRemappedLabelValue: + cidr = net.Spec.CIDR.String() + default: + cidr = net.Status.CIDR.String() + } + if cidr == "" { + klog.Warningf("Network %s has no CIDR", net.Name) + continue + } + + nets = append(nets, cidr) + } + + return nets, nil +} + +func (lipam *LiqoIPAM) getReservedIPs(ctx context.Context) ([]ipCidr, error) { + var ips []ipCidr + var ipList ipamv1alpha1.IPList + if err := lipam.Options.Client.List(ctx, &ipList); err != nil { + return nil, err + } + + for i := range ipList.Items { + ip := &ipList.Items[i] + + address := ip.Status.IP.String() + if address == "" { + klog.Warningf("IP %s has no address", ip.Name) + continue + } + + cidr := ip.Status.CIDR.String() + if cidr == "" { + klog.Warningf("IP %s has no CIDR", ip.Name) + continue + } + + ips = append(ips, ipCidr{ip: address, cidr: cidr}) + } + + return ips, nil +} + +func (lipam *LiqoIPAM) reserveNetwork(cidr string) error { + // TODO: Reserve the network. + klog.Infof("Reserved network %s", cidr) + return nil +} + +func (lipam *LiqoIPAM) reserveIP(ip string, cidr string) error { + // TODO: Reserve the IP. + klog.Infof("Reserved IP %s in network %s", ip, cidr) + return nil +} diff --git a/pkg/ipam/ipam.go b/pkg/ipam/ipam.go index 413fe0d8b7..d91aaf700d 100644 --- a/pkg/ipam/ipam.go +++ b/pkg/ipam/ipam.go @@ -15,20 +15,27 @@ package ipam import ( + "context" "time" "google.golang.org/grpc/health" "google.golang.org/grpc/health/grpc_health_v1" + "k8s.io/client-go/rest" + "sigs.k8s.io/controller-runtime/pkg/client" ) // LiqoIPAM is the struct implementing the IPAM interface. type LiqoIPAM struct { UnimplementedIPAMServer + + Options *Options } // Options contains the options to configure the IPAM. type Options struct { - Port int + Port int + Config *rest.Config + Client client.Client EnableLeaderElection bool LeaderElectionNamespace string @@ -42,11 +49,17 @@ type Options struct { } // New creates a new instance of the LiqoIPAM. -func New(opts *Options) *LiqoIPAM { +func New(ctx context.Context, opts *Options) (*LiqoIPAM, error) { opts.HealthServer.SetServingStatus(IPAM_ServiceDesc.ServiceName, grpc_health_v1.HealthCheckResponse_NOT_SERVING) - // TODO: add here the initialization logic + lipam := &LiqoIPAM{ + Options: opts, + } + + if err := lipam.initialize(ctx); err != nil { + return nil, err + } opts.HealthServer.SetServingStatus(IPAM_ServiceDesc.ServiceName, grpc_health_v1.HealthCheckResponse_SERVING) - return &LiqoIPAM{} + return lipam, nil }