Skip to content

Commit

Permalink
Add out of range ip count to pool status
Browse files Browse the repository at this point in the history
Co-authored-by: Aidan Obley <[email protected]>
  • Loading branch information
christianang and Aidan Obley committed May 10, 2023
1 parent b43c9f0 commit 549c3c6
Show file tree
Hide file tree
Showing 7 changed files with 103 additions and 4 deletions.
5 changes: 5 additions & 0 deletions api/v1alpha1/inclusterippool_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,11 @@ type InClusterIPPoolStatusIPAddresses struct {
// Used is the count of allocated IPs in the pool.
// Counts greater than int can contain will report as math.MaxInt.
Used int `json:"used"`

// Out of Range is the count of allocated IPs in the pool that is not
// contained within spec.Addresses.
// Counts greater than int can contain will report as math.MaxInt.
OutOfRange int `json:"outOfRange"`
}

// +kubebuilder:object:root=true
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,11 @@ spec:
description: Free is the count of unallocated IPs in the pool.
Counts greater than int can contain will report as math.MaxInt.
type: integer
outOfRange:
description: Out of Range is the count of allocated IPs in the
pool that is not contained within spec.Addresses. Counts greater
than int can contain will report as math.MaxInt.
type: integer
total:
description: Total is the total number of IPs configured for the
pool. Counts greater than int can contain will report as math.MaxInt.
Expand All @@ -113,6 +118,7 @@ spec:
type: integer
required:
- free
- outOfRange
- total
- used
type: object
Expand Down
6 changes: 6 additions & 0 deletions config/crd/bases/ipam.cluster.x-k8s.io_inclusterippools.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,11 @@ spec:
description: Free is the count of unallocated IPs in the pool.
Counts greater than int can contain will report as math.MaxInt.
type: integer
outOfRange:
description: Out of Range is the count of allocated IPs in the
pool that is not contained within spec.Addresses. Counts greater
than int can contain will report as math.MaxInt.
type: integer
total:
description: Total is the total number of IPs configured for the
pool. Counts greater than int can contain will report as math.MaxInt.
Expand All @@ -115,6 +120,7 @@ spec:
type: integer
required:
- free
- outOfRange
- total
- used
type: object
Expand Down
11 changes: 8 additions & 3 deletions internal/controllers/inclusterippool.go
Original file line number Diff line number Diff line change
Expand Up @@ -199,11 +199,16 @@ func genericReconcile(ctx context.Context, c client.Client, pool pooltypes.Gener
}

free := poolCount - inUseCount
outOfRangeIPSet, err := poolutil.AddressesOutOfRangeIPSet(addressesInUse, poolIPSet)
if err != nil {
return ctrl.Result{}, errors.Wrap(err, "failed to build out of range ip set")
}

pool.PoolStatus().Addresses = &v1alpha1.InClusterIPPoolStatusIPAddresses{
Total: poolCount,
Used: inUseCount,
Free: free,
Total: poolCount,
Used: inUseCount,
Free: free,
OutOfRange: poolutil.IPSetCount(outOfRangeIPSet),
}

log.Info("Updating pool with usage info", "statusAddresses", pool.PoolStatus().Addresses)
Expand Down
61 changes: 61 additions & 0 deletions internal/controllers/inclusterippool_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,67 @@ var _ = Describe("IP Pool Reconciler", func() {
Entry("When there is 1 claim with gateway outside of range - GlobalInClusterIPPool",
"GlobalInClusterIPPool", []string{"10.0.0.10-10.0.0.20"}, "10.0.0.1", 11, 1, 10),
)

DescribeTable("it shows the out of range ips if any",
func(poolType string, addresses []string, gateway string, updatedAddresses []string, numClaims, expectedOutOfRange int) {
poolSpec := v1alpha1.InClusterIPPoolSpec{
Prefix: 24,
Gateway: gateway,
Addresses: addresses,
}

switch poolType {
case "InClusterIPPool":
genericPool = &v1alpha1.InClusterIPPool{
ObjectMeta: metav1.ObjectMeta{GenerateName: testPool, Namespace: namespace},
Spec: poolSpec,
}
case "GlobalInClusterIPPool":
genericPool = &v1alpha1.GlobalInClusterIPPool{
ObjectMeta: metav1.ObjectMeta{GenerateName: testPool, Namespace: namespace},
Spec: poolSpec,
}
default:
Fail("Unknown pool type")
}

Expect(k8sClient.Create(context.Background(), genericPool)).To(Succeed())

for i := 0; i < numClaims; i++ {
claim := ipamv1.IPAddressClaim{
ObjectMeta: metav1.ObjectMeta{
Name: fmt.Sprintf("test%d", i),
Namespace: namespace,
},
Spec: ipamv1.IPAddressClaimSpec{
PoolRef: corev1.TypedLocalObjectReference{
APIGroup: pointer.String("ipam.cluster.x-k8s.io"),
Kind: poolType,
Name: genericPool.GetName(),
},
},
}
Expect(k8sClient.Create(context.Background(), &claim)).To(Succeed())
createdClaimNames = append(createdClaimNames, claim.Name)
}

Eventually(Object(genericPool)).
WithTimeout(5 * time.Second).WithPolling(100 * time.Millisecond).Should(
HaveField("Status.Addresses.Used", Equal(numClaims)))

genericPool.PoolSpec().Addresses = updatedAddresses
Expect(k8sClient.Update(context.Background(), genericPool)).To(Succeed())

Eventually(Object(genericPool)).
WithTimeout(5 * time.Second).WithPolling(100 * time.Millisecond).Should(
HaveField("Status.Addresses.OutOfRange", Equal(expectedOutOfRange)))
},

Entry("InClusterIPPool",
"InClusterIPPool", []string{"10.0.0.10-10.0.0.20"}, "10.0.0.1", []string{"10.0.0.13-10.0.0.20"}, 5, 3),
Entry("GlobalInClusterIPPool",
"GlobalInClusterIPPool", []string{"10.0.0.10-10.0.0.20"}, "10.0.0.1", []string{"10.0.0.13-10.0.0.20"}, 5, 3),
)
})

Context("when the pool has IPAddresses", func() {
Expand Down
16 changes: 16 additions & 0 deletions internal/poolutil/pool.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,22 @@ import (
"github.com/telekom/cluster-api-ipam-provider-in-cluster/internal/index"
)

// AddressesOutOfRangeIPSet returns an IPSet of the inUseAddresses IPs that are
// not in the poolIPSet.
func AddressesOutOfRangeIPSet(inUseAddresses []ipamv1.IPAddress, poolIPSet *netipx.IPSet) (*netipx.IPSet, error) {
outOfRangeBuilder := &netipx.IPSetBuilder{}
for _, address := range inUseAddresses {
ip, err := netip.ParseAddr(address.Spec.Address)
if err != nil {
// if an address we fetch for the pool is unparsable then it isn't in the pool ranges
continue
}
outOfRangeBuilder.Add(ip)
}
outOfRangeBuilder.RemoveSet(poolIPSet)
return outOfRangeBuilder.IPSet()
}

// ListAddressesInUse fetches all IPAddresses belonging to the specified pool.
// Note: requires `index.ipAddressByCombinedPoolRef` to be set up.
func ListAddressesInUse(ctx context.Context, c client.Reader, namespace string, poolRef corev1.TypedLocalObjectReference) ([]ipamv1.IPAddress, error) {
Expand Down
2 changes: 1 addition & 1 deletion internal/webhooks/inclusterippool.go
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ func (webhook *InClusterIPPool) ValidateUpdate(ctx context.Context, oldObj, newO
inUseBuilder.RemoveSet(newPoolIPSet)
outOfRangeIPSet, err := inUseBuilder.IPSet()
if err != nil {
panic("oh no")
return apierrors.NewInternalError(err)
}

if outOfRange := outOfRangeIPSet.Ranges(); len(outOfRange) > 0 {
Expand Down

0 comments on commit 549c3c6

Please sign in to comment.