Skip to content

Commit 549c3c6

Browse files
christianangAidan Obley
and
Aidan Obley
committed
Add out of range ip count to pool status
Co-authored-by: Aidan Obley <[email protected]>
1 parent b43c9f0 commit 549c3c6

7 files changed

+103
-4
lines changed

api/v1alpha1/inclusterippool_types.go

+5
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,11 @@ type InClusterIPPoolStatusIPAddresses struct {
5959
// Used is the count of allocated IPs in the pool.
6060
// Counts greater than int can contain will report as math.MaxInt.
6161
Used int `json:"used"`
62+
63+
// Out of Range is the count of allocated IPs in the pool that is not
64+
// contained within spec.Addresses.
65+
// Counts greater than int can contain will report as math.MaxInt.
66+
OutOfRange int `json:"outOfRange"`
6267
}
6368

6469
// +kubebuilder:object:root=true

config/crd/bases/ipam.cluster.x-k8s.io_globalinclusterippools.yaml

+6
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,11 @@ spec:
103103
description: Free is the count of unallocated IPs in the pool.
104104
Counts greater than int can contain will report as math.MaxInt.
105105
type: integer
106+
outOfRange:
107+
description: Out of Range is the count of allocated IPs in the
108+
pool that is not contained within spec.Addresses. Counts greater
109+
than int can contain will report as math.MaxInt.
110+
type: integer
106111
total:
107112
description: Total is the total number of IPs configured for the
108113
pool. Counts greater than int can contain will report as math.MaxInt.
@@ -113,6 +118,7 @@ spec:
113118
type: integer
114119
required:
115120
- free
121+
- outOfRange
116122
- total
117123
- used
118124
type: object

config/crd/bases/ipam.cluster.x-k8s.io_inclusterippools.yaml

+6
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,11 @@ spec:
105105
description: Free is the count of unallocated IPs in the pool.
106106
Counts greater than int can contain will report as math.MaxInt.
107107
type: integer
108+
outOfRange:
109+
description: Out of Range is the count of allocated IPs in the
110+
pool that is not contained within spec.Addresses. Counts greater
111+
than int can contain will report as math.MaxInt.
112+
type: integer
108113
total:
109114
description: Total is the total number of IPs configured for the
110115
pool. Counts greater than int can contain will report as math.MaxInt.
@@ -115,6 +120,7 @@ spec:
115120
type: integer
116121
required:
117122
- free
123+
- outOfRange
118124
- total
119125
- used
120126
type: object

internal/controllers/inclusterippool.go

+8-3
Original file line numberDiff line numberDiff line change
@@ -199,11 +199,16 @@ func genericReconcile(ctx context.Context, c client.Client, pool pooltypes.Gener
199199
}
200200

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

203207
pool.PoolStatus().Addresses = &v1alpha1.InClusterIPPoolStatusIPAddresses{
204-
Total: poolCount,
205-
Used: inUseCount,
206-
Free: free,
208+
Total: poolCount,
209+
Used: inUseCount,
210+
Free: free,
211+
OutOfRange: poolutil.IPSetCount(outOfRangeIPSet),
207212
}
208213

209214
log.Info("Updating pool with usage info", "statusAddresses", pool.PoolStatus().Addresses)

internal/controllers/inclusterippool_test.go

+61
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,67 @@ var _ = Describe("IP Pool Reconciler", func() {
9999
Entry("When there is 1 claim with gateway outside of range - GlobalInClusterIPPool",
100100
"GlobalInClusterIPPool", []string{"10.0.0.10-10.0.0.20"}, "10.0.0.1", 11, 1, 10),
101101
)
102+
103+
DescribeTable("it shows the out of range ips if any",
104+
func(poolType string, addresses []string, gateway string, updatedAddresses []string, numClaims, expectedOutOfRange int) {
105+
poolSpec := v1alpha1.InClusterIPPoolSpec{
106+
Prefix: 24,
107+
Gateway: gateway,
108+
Addresses: addresses,
109+
}
110+
111+
switch poolType {
112+
case "InClusterIPPool":
113+
genericPool = &v1alpha1.InClusterIPPool{
114+
ObjectMeta: metav1.ObjectMeta{GenerateName: testPool, Namespace: namespace},
115+
Spec: poolSpec,
116+
}
117+
case "GlobalInClusterIPPool":
118+
genericPool = &v1alpha1.GlobalInClusterIPPool{
119+
ObjectMeta: metav1.ObjectMeta{GenerateName: testPool, Namespace: namespace},
120+
Spec: poolSpec,
121+
}
122+
default:
123+
Fail("Unknown pool type")
124+
}
125+
126+
Expect(k8sClient.Create(context.Background(), genericPool)).To(Succeed())
127+
128+
for i := 0; i < numClaims; i++ {
129+
claim := ipamv1.IPAddressClaim{
130+
ObjectMeta: metav1.ObjectMeta{
131+
Name: fmt.Sprintf("test%d", i),
132+
Namespace: namespace,
133+
},
134+
Spec: ipamv1.IPAddressClaimSpec{
135+
PoolRef: corev1.TypedLocalObjectReference{
136+
APIGroup: pointer.String("ipam.cluster.x-k8s.io"),
137+
Kind: poolType,
138+
Name: genericPool.GetName(),
139+
},
140+
},
141+
}
142+
Expect(k8sClient.Create(context.Background(), &claim)).To(Succeed())
143+
createdClaimNames = append(createdClaimNames, claim.Name)
144+
}
145+
146+
Eventually(Object(genericPool)).
147+
WithTimeout(5 * time.Second).WithPolling(100 * time.Millisecond).Should(
148+
HaveField("Status.Addresses.Used", Equal(numClaims)))
149+
150+
genericPool.PoolSpec().Addresses = updatedAddresses
151+
Expect(k8sClient.Update(context.Background(), genericPool)).To(Succeed())
152+
153+
Eventually(Object(genericPool)).
154+
WithTimeout(5 * time.Second).WithPolling(100 * time.Millisecond).Should(
155+
HaveField("Status.Addresses.OutOfRange", Equal(expectedOutOfRange)))
156+
},
157+
158+
Entry("InClusterIPPool",
159+
"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),
160+
Entry("GlobalInClusterIPPool",
161+
"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),
162+
)
102163
})
103164

104165
Context("when the pool has IPAddresses", func() {

internal/poolutil/pool.go

+16
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,22 @@ import (
1919
"github.com/telekom/cluster-api-ipam-provider-in-cluster/internal/index"
2020
)
2121

22+
// AddressesOutOfRangeIPSet returns an IPSet of the inUseAddresses IPs that are
23+
// not in the poolIPSet.
24+
func AddressesOutOfRangeIPSet(inUseAddresses []ipamv1.IPAddress, poolIPSet *netipx.IPSet) (*netipx.IPSet, error) {
25+
outOfRangeBuilder := &netipx.IPSetBuilder{}
26+
for _, address := range inUseAddresses {
27+
ip, err := netip.ParseAddr(address.Spec.Address)
28+
if err != nil {
29+
// if an address we fetch for the pool is unparsable then it isn't in the pool ranges
30+
continue
31+
}
32+
outOfRangeBuilder.Add(ip)
33+
}
34+
outOfRangeBuilder.RemoveSet(poolIPSet)
35+
return outOfRangeBuilder.IPSet()
36+
}
37+
2238
// ListAddressesInUse fetches all IPAddresses belonging to the specified pool.
2339
// Note: requires `index.ipAddressByCombinedPoolRef` to be set up.
2440
func ListAddressesInUse(ctx context.Context, c client.Reader, namespace string, poolRef corev1.TypedLocalObjectReference) ([]ipamv1.IPAddress, error) {

internal/webhooks/inclusterippool.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,7 @@ func (webhook *InClusterIPPool) ValidateUpdate(ctx context.Context, oldObj, newO
154154
inUseBuilder.RemoveSet(newPoolIPSet)
155155
outOfRangeIPSet, err := inUseBuilder.IPSet()
156156
if err != nil {
157-
panic("oh no")
157+
return apierrors.NewInternalError(err)
158158
}
159159

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

0 commit comments

Comments
 (0)