diff --git a/controllers/elfmachine_controller.go b/controllers/elfmachine_controller.go index abf6029..3f442ab 100644 --- a/controllers/elfmachine_controller.go +++ b/controllers/elfmachine_controller.go @@ -336,6 +336,16 @@ func (r *ElfMachineReconciler) reconcileDeviceIPAddress(ctx *context.MachineCont return ctrl.Result{}, nil } +// getIPPool returns the specified IPPool. +// +// getIPPool selects IPPool according to the following priorities: +// (1) Without ip-pool-name +// 1. select IPPool with `is-default`` label from elfMachine.Namespace +// 2. select IPPool with `is-default` label from default namespace +// (2) With ip-pool-name(from AddressesFromPools or ElfMachineTemplate) +// 1. select IPPool using the specified ip-pool-name and ip-pool-namespace +// 2. select the IPPool named ip-pool-name in the default namespace + func (r *ElfMachineReconciler) getIPPool(ctx *context.MachineContext, device capev1.NetworkDeviceSpec) (ipam.IPPool, error) { poolMatchLabels := make(map[string]string) // Prefer IPPool of device. Only Metal3 IPPool is supported now. diff --git a/controllers/elfmachine_controller_test.go b/controllers/elfmachine_controller_test.go index e36ee01..3b87814 100644 --- a/controllers/elfmachine_controller_test.go +++ b/controllers/elfmachine_controller_test.go @@ -226,9 +226,11 @@ var _ = Describe("ElfMachineReconciler", func() { It("should wait for IP when IPClaim without IP", func() { ctrlutil.AddFinalizer(elfMachine, MachineStaticIPFinalizer) + metal3IPPool.Namespace = ipam.DefaultIPPoolNamespace metal3IPPool.Labels = map[string]string{ ipam.ClusterIPPoolGroupKey: "ip-pool-group", ipam.ClusterNetworkNameKey: "ip-pool-vm-network", + ipam.DefaultIPPoolKey: "true", } elfMachineTemplate.Labels = metal3IPPool.Labels metal3IPClaim, metal3IPAddress = fake.NewMetal3IPObjects(metal3IPPool, ipamutil.GetFormattedClaimName(elfMachine.Namespace, elfMachine.Name, 0)) diff --git a/pkg/ipam/ipam.go b/pkg/ipam/ipam.go index 4c66e9c..c8009f5 100644 --- a/pkg/ipam/ipam.go +++ b/pkg/ipam/ipam.go @@ -29,6 +29,6 @@ const ( // Default IPPool. const ( - DefaultIPPoolNamespace = "cape-system" + DefaultIPPoolNamespace = "default" DefaultIPPoolKey = "ippool.cluster.x-k8s.io/is-default" ) diff --git a/pkg/ipam/metal3io/ipam.go b/pkg/ipam/metal3io/ipam.go index c33d268..a13614a 100644 --- a/pkg/ipam/metal3io/ipam.go +++ b/pkg/ipam/metal3io/ipam.go @@ -130,26 +130,28 @@ func (m *Metal3IPAM) ReleaseIPs(ctx goctx.Context, owner metav1.Object, pool ipa func (m *Metal3IPAM) GetAvailableIPPool(ctx goctx.Context, poolMatchLabels map[string]string, clusterMeta metav1.ObjectMeta) (ipam.IPPool, error) { poolNamespace := getIPPoolNamespace(poolMatchLabels, clusterMeta) + poolName := poolMatchLabels[ipam.ClusterIPPoolNameKey] - // if the specific ip-pool name is provided use that to get the ip-pool - var ipPool ipamv1.IPPool - if poolName, ok := poolMatchLabels[ipam.ClusterIPPoolNameKey]; ok && poolName != "" { - if err := m.Get(ctx, apitypes.NamespacedName{ - Namespace: poolNamespace, - Name: poolName, - }, &ipPool); err != nil { - if apierrors.IsNotFound(err) { - return nil, nil - } - - return nil, errors.Wrapf(err, "failed to get IPPool %s/%s", poolNamespace, poolName) - } - - return toIPPool(ipPool), nil + // 1. If the specific ip-pool name is provided use that to get the ip-pool. + if poolName != "" { + return m.getIPPoolByName(ctx, poolNamespace, poolName) } - matchLabels := map[string]string{} + // 2. Otherwise use default ip-pool + + ipPoolList := &ipamv1.IPPoolList{} + if err := m.List( + ctx, + ipPoolList, + client.InNamespace(clusterMeta.Namespace), + client.MatchingLabels(map[string]string{ipam.DefaultIPPoolKey: "true"})); err != nil { + return nil, err + } + if len(ipPoolList.Items) > 0 { + return toIPPool(ipPoolList.Items[0]), nil + } + matchLabels := map[string]string{ipam.DefaultIPPoolKey: "true"} // use labels 'ip-pool-group' & 'network-name' to select the ip-pool if label, ok := poolMatchLabels[ipam.ClusterIPPoolGroupKey]; ok && label != "" { matchLabels[ipam.ClusterIPPoolGroupKey] = label @@ -158,30 +160,50 @@ func (m *Metal3IPAM) GetAvailableIPPool(ctx goctx.Context, poolMatchLabels map[s matchLabels[ipam.ClusterNetworkNameKey] = label } - // use default ip-pool - if len(matchLabels) == 0 { - poolNamespace = ipam.DefaultIPPoolNamespace - matchLabels[ipam.DefaultIPPoolKey] = "true" - } - - ipPoolList := &ipamv1.IPPoolList{} + ipPoolList = &ipamv1.IPPoolList{} if err := m.List( ctx, ipPoolList, - client.InNamespace(poolNamespace), + client.InNamespace(ipam.DefaultIPPoolNamespace), client.MatchingLabels(matchLabels)); err != nil { return nil, err } if len(ipPoolList.Items) == 0 { - m.logger.Info("failed to get a matching IPPool") return nil, nil } - ipPool = ipPoolList.Items[0] - m.logger.Info(fmt.Sprintf("IPPool %s is available", ipPool.Name)) + return toIPPool(ipPoolList.Items[0]), nil +} + +// getIPPoolByName returns the IPPool with the specified name. +// +// If IPPool is not found in the specified namespace, will try to find from the +// default namespace. +func (m *Metal3IPAM) getIPPoolByName(ctx goctx.Context, poolNamespace, poolName string) (ipam.IPPool, error) { + var ipPool ipamv1.IPPool + err := m.Get(ctx, apitypes.NamespacedName{ + Namespace: poolNamespace, + Name: poolName, + }, &ipPool) + if err == nil { + return toIPPool(ipPool), nil + } else if !apierrors.IsNotFound(err) { + return nil, errors.Wrapf(err, "failed to get IPPool %s/%s", poolNamespace, poolName) + } + + // Try the ip-pool default namespace. + err = m.Get(ctx, apitypes.NamespacedName{ + Namespace: ipam.DefaultIPPoolNamespace, + Name: poolName, + }, &ipPool) + if err == nil { + return toIPPool(ipPool), nil + } else if apierrors.IsNotFound(err) { + return nil, nil + } - return toIPPool(ipPool), nil + return nil, errors.Wrapf(err, "failed to get IPPool %s/%s", poolNamespace, poolName) } func (m *Metal3IPAM) getIPClaim(ctx goctx.Context, pool ipam.IPPool, claimName string) (*ipamv1.IPClaim, error) {