Skip to content

Commit

Permalink
refactor: updated IP controller for new ipam
Browse files Browse the repository at this point in the history
  • Loading branch information
fra98 committed Nov 14, 2024
1 parent 1d7ef60 commit f5bfa30
Show file tree
Hide file tree
Showing 25 changed files with 435 additions and 227 deletions.
6 changes: 2 additions & 4 deletions apis/ipam/v1alpha1/ip_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -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:"cidr,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
Expand All @@ -65,8 +65,6 @@ 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"`
Expand Down
17 changes: 5 additions & 12 deletions apis/ipam/v1alpha1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion cmd/liqo-controller-manager/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -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,

Expand Down
16 changes: 7 additions & 9 deletions cmd/liqo-controller-manager/modules/networking.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,6 @@
package modules

import (
"context"

"k8s.io/client-go/dynamic"
"k8s.io/klog/v2"
"sigs.k8s.io/controller-runtime/pkg/manager"
Expand All @@ -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"
)
Expand All @@ -60,19 +59,18 @@ type NetworkingOption struct {
}

// SetupNetworkingModule setup the networking module and initializes its controllers .
func SetupNetworkingModule(ctx 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"))
Expand Down
53 changes: 42 additions & 11 deletions deployments/liqo/charts/liqo-crds/crds/ipam.liqo.io_ips.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -54,10 +54,49 @@ spec:
properties:
cidr:
description: |-
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).
format: cidr
type: string
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
ip:
description: IP is the local IP.
format: ipv4
Expand Down Expand Up @@ -456,14 +495,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
Expand Down
4 changes: 1 addition & 3 deletions pkg/ipam/sync_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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),
Expand Down
16 changes: 16 additions & 0 deletions pkg/ipam/utils/doc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// 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 utils contain utility functions for the IPAM package.
package utils
109 changes: 109 additions & 0 deletions pkg/ipam/utils/utils.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
// 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 utils

import (
"fmt"
"net"
"net/netip"

"go4.org/netipx"
utilruntime "k8s.io/apimachinery/pkg/util/runtime"

"github.com/liqotech/liqo/pkg/consts"
)

// MapIPToNetwork creates a new IP address obtained by means of the old IP address and the new network.
func MapIPToNetwork(newNetwork, oldIP string) (newIP string, err error) {
if newNetwork == consts.DefaultCIDRValue {
return oldIP, nil
}
// Parse newNetwork
ip, network, err := net.ParseCIDR(newNetwork)
if err != nil {
return "", err
}
// Get mask
mask := network.Mask
// Get slice of bytes for newNetwork
// Type net.IP has underlying type []byte
parsedNewIP := ip.To4()
// Get oldIP as slice of bytes
parsedOldIP := net.ParseIP(oldIP)
if parsedOldIP == nil {
return "", fmt.Errorf("cannot parse oldIP")
}
parsedOldIP = parsedOldIP.To4()
// Substitute the last 32-mask bits of newNetwork with bits taken by the old ip
for i := 0; i < len(mask); i++ {
// Step 1: NOT(mask[i]) = mask[i] ^ 0xff. They are the 'host' bits
// Step 2: BITWISE AND between the host bits and parsedOldIP[i] zeroes the network bits in parsedOldIP[i]
// Step 3: BITWISE OR copies the result of step 2 in newIP[i]
parsedNewIP[i] |= (mask[i] ^ 0xff) & parsedOldIP[i]
}
newIP = parsedNewIP.String()
return
}

// GetMask retrieves the mask from a CIDR.
func GetMask(network string) uint8 {
_, subnet, err := net.ParseCIDR(network)
utilruntime.Must(err)
ones, _ := subnet.Mask.Size()
return uint8(ones)
}

// SetMask forges a new cidr from a network cidr and a mask.
func SetMask(network string, mask uint8) string {
_, n, err := net.ParseCIDR(network)
utilruntime.Must(err)
newMask := net.CIDRMask(int(mask), 32)
n.Mask = newMask
return n.String()
}

// Next used to get the second half of a given network.
func Next(network string) string {
prefix, err := netip.ParsePrefix(network)
utilruntime.Must(err)
// Step 1: Get last IP address of network
// Step 2: Get next IP address
firstIP := netipx.RangeOfPrefix(prefix).To().Next()
prefix = netip.PrefixFrom(firstIP, prefix.Bits())
return prefix.String()
}

// IsValidCIDR returns an error if the received CIDR is invalid.
func IsValidCIDR(cidr string) error {
_, _, err := net.ParseCIDR(cidr)
return err
}

// SplitNetwork returns the two halves that make up a given network.
func SplitNetwork(network string) []string {
halves := make([]string, 2)

// Get halves mask length.
mask := GetMask(network)
mask++

// Get first half CIDR.
halves[0] = SetMask(network, mask)

// Get second half CIDR.
halves[1] = Next(halves[0])

return halves
}
27 changes: 27 additions & 0 deletions pkg/ipam/utils/utils_suite_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// 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 utils_test

import (
"testing"

. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)

func TestUtils(t *testing.T) {
RegisterFailHandler(Fail)
RunSpecs(t, "Utils Suite")
}
Loading

0 comments on commit f5bfa30

Please sign in to comment.