Skip to content

Commit

Permalink
initialize ipam
Browse files Browse the repository at this point in the history
  • Loading branch information
aleoli committed Oct 25, 2024
1 parent 91a1d76 commit e16c9ee
Show file tree
Hide file tree
Showing 5 changed files with 194 additions and 5 deletions.
5 changes: 5 additions & 0 deletions apis/ipam/v1alpha1/ip_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
12 changes: 11 additions & 1 deletion cmd/ipam/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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{
Expand All @@ -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 {
Expand Down
13 changes: 13 additions & 0 deletions deployments/liqo/charts/liqo-crds/crds/ipam.liqo.io_ips.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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
Expand Down
148 changes: 148 additions & 0 deletions pkg/ipam/initialize.go
Original file line number Diff line number Diff line change
@@ -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
}
21 changes: 17 additions & 4 deletions pkg/ipam/ipam.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
}

0 comments on commit e16c9ee

Please sign in to comment.