Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 8 additions & 4 deletions cmd/metalnetlet/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ func main() {
var configOptions config.GetConfigOptions
var metalnetKubeconfig string
var metalnetNamespace string
var disableNetworkPeering bool

flag.StringVar(&name, "name", "", "The name of the partition the metalnetlet represents (required).")
flag.StringToStringVar(&nodeLabels, "node-label", nodeLabels, "Additional labels to add to the nodes.")
Expand All @@ -69,6 +70,8 @@ func main() {
configOptions.BindFlags(flag.CommandLine)
flag.StringVar(&metalnetKubeconfig, "metalnet-kubeconfig", "", "Metalnet kubeconfig to use.")
flag.StringVar(&metalnetNamespace, "metalnet-namespace", corev1.NamespaceDefault, "Metalnet namespace to use.")
flag.BoolVar(&disableNetworkPeering, "disable-network-peering", false,
"Disable the metalnet based network peering. If set to true the network peering is handled externally.")

opts := zap.Options{
Development: true,
Expand Down Expand Up @@ -153,10 +156,11 @@ func main() {
}

if err := (&controllers.NetworkReconciler{
Client: mgr.GetClient(),
MetalnetClient: metalnetCluster.GetClient(),
PartitionName: name,
MetalnetNamespace: metalnetNamespace,
Client: mgr.GetClient(),
MetalnetClient: metalnetCluster.GetClient(),
PartitionName: name,
MetalnetNamespace: metalnetNamespace,
NetworkPeeringDisabled: disableNetworkPeering,
}).SetupWithManager(mgr, metalnetCluster.GetCache()); err != nil {
setupLog.Error(err, "unable to create controller", "controller", "Network")
os.Exit(1)
Expand Down
49 changes: 49 additions & 0 deletions metalnetlet/controllers/controllers_suite_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,55 @@ func SetupTest(metalnetNs *corev1.Namespace) {
})
}

func SetupTestWithNetworkPeeringDisabled(metalnetNs *corev1.Namespace) {
BeforeEach(func(ctx SpecContext) {
k8sManager, err := ctrl.NewManager(cfg, ctrl.Options{
Scheme: scheme.Scheme,
Metrics: metricsserver.Options{
BindAddress: "0",
},
})
Expect(err).ToNot(HaveOccurred())

// register reconciler here
Expect((&NetworkReconciler{
Client: k8sManager.GetClient(),
MetalnetClient: k8sManager.GetClient(),
PartitionName: partitionName,
MetalnetNamespace: metalnetNs.Name,
NetworkPeeringDisabled: true,
}).SetupWithManager(k8sManager, k8sManager.GetCache())).To(Succeed())

Expect((&MetalnetNodeReconciler{
Client: k8sManager.GetClient(),
MetalnetClient: k8sManager.GetClient(),
PartitionName: partitionName,
NodeLabels: nodeLabels,
}).SetupWithManager(k8sManager, k8sManager.GetCache())).To(Succeed())

Expect((&NetworkInterfaceReconciler{
Client: k8sManager.GetClient(),
MetalnetClient: k8sManager.GetClient(),
PartitionName: partitionName,
MetalnetNamespace: metalnetNs.Name,
}).SetupWithManager(k8sManager, k8sManager.GetCache())).To(Succeed())

Expect((&InstanceReconciler{
Client: k8sManager.GetClient(),
MetalnetClient: k8sManager.GetClient(),
PartitionName: partitionName,
MetalnetNamespace: metalnetNs.Name,
}).SetupWithManager(k8sManager, k8sManager.GetCache())).To(Succeed())

mgrCtx, cancel := context.WithCancel(context.Background())
DeferCleanup(cancel)
go func() {
defer GinkgoRecover()
Expect(k8sManager.Start(mgrCtx)).To(Succeed(), "failed to start manager")
}()
})
}

func SetupMetalnetNode() *corev1.Node {
return SetupObjectStruct[*corev1.Node](&k8sClient, func(node *corev1.Node) {
*node = corev1.Node{
Expand Down
54 changes: 30 additions & 24 deletions metalnetlet/controllers/network_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ type NetworkReconciler struct {
PartitionName string

MetalnetNamespace string

NetworkPeeringDisabled bool
}

//+kubebuilder:rbac:groups="",resources=events,verbs=create;patch
Expand Down Expand Up @@ -114,18 +116,20 @@ func (r *NetworkReconciler) delete(ctx context.Context, log logr.Logger, network
}

func (r *NetworkReconciler) updateApinetNetworkStatus(ctx context.Context, log logr.Logger, network *apinetv1alpha1.Network, metalnetNetwork *metalnetv1alpha1.Network) error {
apinetStatusPeerings := metalnetNetworkPeeringsStatusToNetworkPeeringsStatus(metalnetNetwork.Status.Peerings)
if !equality.Semantic.DeepEqual(network.Status.Peerings[r.PartitionName], apinetStatusPeerings) {
log.V(1).Info("Patching apinet network status", "status", apinetStatusPeerings)
networkBase := network.DeepCopy()
if network.Status.Peerings == nil {
network.Status.Peerings = make(map[string][]apinetv1alpha1.NetworkPeeringStatus)
}
network.Status.Peerings[r.PartitionName] = apinetStatusPeerings
if err := r.Status().Patch(ctx, network, client.MergeFrom(networkBase)); err != nil {
return fmt.Errorf("unable to patch network: %w", err)
if !r.NetworkPeeringDisabled {
apinetStatusPeerings := metalnetNetworkPeeringsStatusToNetworkPeeringsStatus(metalnetNetwork.Status.Peerings)
if !equality.Semantic.DeepEqual(network.Status.Peerings[r.PartitionName], apinetStatusPeerings) {
log.V(1).Info("Patching apinet network status", "status", apinetStatusPeerings)
networkBase := network.DeepCopy()
if network.Status.Peerings == nil {
network.Status.Peerings = make(map[string][]apinetv1alpha1.NetworkPeeringStatus)
}
network.Status.Peerings[r.PartitionName] = apinetStatusPeerings
if err := r.Status().Patch(ctx, network, client.MergeFrom(networkBase)); err != nil {
return fmt.Errorf("unable to patch network: %w", err)
}
log.V(1).Info("Patched apinet network status")
}
log.V(1).Info("Patched apinet network status")
}
return nil
}
Expand Down Expand Up @@ -178,22 +182,24 @@ func (r *NetworkReconciler) reconcile(ctx context.Context, log logr.Logger, netw
}
var peeredIDs []int32
var peeredPrefixes []metalnetv1alpha1.PeeredPrefix
for _, peering := range network.Spec.Peerings {
id, err := networkid.ParseVNI(peering.ID)
if err != nil {
return ctrl.Result{}, fmt.Errorf("failed to parse peered network ID: %w", err)
}
if !r.NetworkPeeringDisabled {
for _, peering := range network.Spec.Peerings {
id, err := networkid.ParseVNI(peering.ID)
if err != nil {
return ctrl.Result{}, fmt.Errorf("failed to parse peered network ID: %w", err)
}

// metalnetNetwork.Spec.PeeredIDs = append(metalnetNetwork.Spec.PeeredIDs, id)
peeredIDs = append(peeredIDs, id)
// metalnetNetwork.Spec.PeeredIDs = append(metalnetNetwork.Spec.PeeredIDs, id)
peeredIDs = append(peeredIDs, id)

if len(peering.Prefixes) > 0 {
ipPrefixes := getIPPrefixes(peering.Prefixes)
peeredPrefix := metalnetv1alpha1.PeeredPrefix{
ID: id,
Prefixes: ipPrefixesToMetalnetPrefixes(ipPrefixes),
if len(peering.Prefixes) > 0 {
ipPrefixes := getIPPrefixes(peering.Prefixes)
peeredPrefix := metalnetv1alpha1.PeeredPrefix{
ID: id,
Prefixes: ipPrefixesToMetalnetPrefixes(ipPrefixes),
}
peeredPrefixes = append(peeredPrefixes, peeredPrefix)
}
peeredPrefixes = append(peeredPrefixes, peeredPrefix)
}
}
// metalnetNetwork.Spec.PeeredPrefixes = peeredPrefixes
Expand Down
128 changes: 128 additions & 0 deletions metalnetlet/controllers/network_controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -140,4 +140,132 @@ var _ = Describe("NetworkController", func() {
Expect(k8sClient.Get(ctx, client.ObjectKeyFromObject(metalnetNetwork2), metalnetNetwork2)).To(Satisfy(apierrors.IsNotFound))

})

var _ = Describe("NetworkPeeringController", func() {
ns := SetupNamespace(&k8sClient)
metalnetNs := SetupNamespace(&k8sClient)
SetupTestWithNetworkPeeringDisabled(metalnetNs)

It("should create metalnet networks for apinet networks without peerings information if NetworkPeeringDisabled is set to true", func(ctx SpecContext) {
By("creating a apinet network-1")
network1 := &apinetv1alpha1.Network{
ObjectMeta: metav1.ObjectMeta{
Namespace: ns.Name,
Name: "network-1",
},
}
Expect(k8sClient.Create(ctx, network1)).To(Succeed())

By("creating a apinet network-2")
network2 := &apinetv1alpha1.Network{
ObjectMeta: metav1.ObjectMeta{
Namespace: ns.Name,
Name: "network-2",
},
}
Expect(k8sClient.Create(ctx, network2)).To(Succeed())

By("updating apinet networks spec with peerings")
baseNetwork1 := network1.DeepCopy()
network1.Spec.Peerings = []apinetv1alpha1.NetworkPeering{{
Name: "peering-1",
Prefixes: []apinetv1alpha1.PeeringPrefix{{
Name: "my-prefix",
Prefix: net.MustParseNewIPPrefix("10.0.0.0/24")}},
ID: network2.Spec.ID}}
Expect(k8sClient.Patch(ctx, network1, client.MergeFrom(baseNetwork1))).To(Succeed())

baseNetwork2 := network2.DeepCopy()
network2.Spec.Peerings = []apinetv1alpha1.NetworkPeering{{
Name: "peering-1",
ID: network1.Spec.ID}}
Expect(k8sClient.Patch(ctx, network2, client.MergeFrom(baseNetwork2))).To(Succeed())

By("parsing the VNI of network-1")
network1Vni, err := networkid.ParseVNI(network1.Spec.ID)
Expect(err).NotTo(HaveOccurred())

By("parsing the VNI of network-2")
network2Vni, err := networkid.ParseVNI(network2.Spec.ID)
Expect(err).NotTo(HaveOccurred())

By("waiting for the metalnet networks to be created")
metalnetNetwork1 := &metalnetv1alpha1.Network{
ObjectMeta: metav1.ObjectMeta{
Namespace: metalnetNs.Name,
Name: string(network1.UID),
},
}
Eventually(Object(metalnetNetwork1)).Should(SatisfyAll(
HaveField("Spec", metalnetv1alpha1.NetworkSpec{
ID: network1Vni,
}),
))

By("validating metalnet network spec is not updated with peering information")
Consistently(Object(metalnetNetwork1)).Should(SatisfyAll(
HaveField("Spec", metalnetv1alpha1.NetworkSpec{
ID: network1Vni,
PeeredIDs: nil,
PeeredPrefixes: nil,
}),
))

metalnetNetwork2 := &metalnetv1alpha1.Network{
ObjectMeta: metav1.ObjectMeta{
Namespace: metalnetNs.Name,
Name: string(network2.UID),
},
}
Eventually(Object(metalnetNetwork2)).Should(SatisfyAll(
HaveField("Spec", metalnetv1alpha1.NetworkSpec{
ID: network2Vni,
}),
))

Consistently(Object(metalnetNetwork2)).Should(SatisfyAll(
HaveField("Spec", metalnetv1alpha1.NetworkSpec{
ID: network2Vni,
PeeredIDs: nil,
PeeredPrefixes: nil,
}),
))

By("updating status of metalnet network peerings")
Eventually(UpdateStatus(metalnetNetwork1, func() {
metalnetNetwork1.Status.Peerings = []metalnetv1alpha1.NetworkPeeringStatus{{
ID: network2Vni,
State: metalnetv1alpha1.NetworkPeeringStateReady,
}}
})).Should(Succeed())

Eventually(UpdateStatus(metalnetNetwork2, func() {
metalnetNetwork2.Status.Peerings = []metalnetv1alpha1.NetworkPeeringStatus{{
ID: network1Vni,
State: metalnetv1alpha1.NetworkPeeringStateReady,
}}
})).Should(Succeed())

By("ensuring apinet network status peerings are not updated")
Consistently(Object(network1)).Should(SatisfyAll(
HaveField("Status.Peerings", BeEmpty()),
))

Consistently(Object(network2)).Should(SatisfyAll(
HaveField("Status.Peerings", BeEmpty()),
))

By("deleting the networks")
Expect(k8sClient.Delete(ctx, network1)).To(Succeed())
Expect(k8sClient.Delete(ctx, network2)).To(Succeed())

By("waiting for networks to be gone")
Eventually(Get(network1)).Should(Satisfy(apierrors.IsNotFound))
Eventually(Get(network2)).Should(Satisfy(apierrors.IsNotFound))

By("asserting the corresponding apinet network is gone as well")
Expect(k8sClient.Get(ctx, client.ObjectKeyFromObject(metalnetNetwork1), metalnetNetwork1)).To(Satisfy(apierrors.IsNotFound))
Expect(k8sClient.Get(ctx, client.ObjectKeyFromObject(metalnetNetwork2), metalnetNetwork2)).To(Satisfy(apierrors.IsNotFound))
})
})
})
Loading