Skip to content

Commit 333bd58

Browse files
Adding feature for allocating requested IP
Signed-off-by: khandelwalsahil <[email protected]>
1 parent 49d9a22 commit 333bd58

File tree

3 files changed

+156
-0
lines changed

3 files changed

+156
-0
lines changed

examples/ippool/ippool.yaml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ apiVersion: ipam.metal3.io/v1alpha1
3535
kind: IPClaim
3636
metadata:
3737
name: ${CLUSTER_NAME}-controlplane-template-0-provisioning-pool
38+
labels:
39+
ipAddress: <optional-label-for-specific-ip-request>
3840
spec:
3941
pool:
4042
name: pool1
@@ -44,6 +46,8 @@ apiVersion: ipam.cluster.x-k8s.io/v1beta1
4446
kind: IPAddressClaim
4547
metadata:
4648
name: ${CLUSTER_NAME}-controlplane-template-1-provisioning-pool
49+
labels:
50+
ipAddress: <optional-label-for-specific-ip-request>
4751
spec:
4852
poolRef:
4953
apiGroup: ipam.metal3.io

ipam/ippool_manager.go

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ var (
4141
const (
4242
IPAddressClaimFinalizer = "ipam.metal3.io/ipaddressclaim"
4343
IPAddressFinalizer = "ipam.metal3.io/ipaddress"
44+
IpAddressLabel = "ipAddress"
4445
)
4546

4647
// IPPoolManagerInterface is an interface for a IPPoolManager.
@@ -415,6 +416,9 @@ func (m *IPPoolManager) allocateAddress(addressClaim *ipamv1.IPClaim,
415416

416417
ipAllocated := false
417418

419+
requestedIp := ipamv1.IPAddressStr(addressClaim.ObjectMeta.Labels[IpAddressLabel])
420+
isRequestedIpAllocated := false
421+
418422
for _, pool := range m.IPPool.Spec.Pools {
419423
if ipAllocated {
420424
break
@@ -426,6 +430,13 @@ func (m *IPPoolManager) allocateAddress(addressClaim *ipamv1.IPClaim,
426430
break
427431
}
428432
index++
433+
// Check if requestedIP is present and matches the current address
434+
if requestedIp != "" && allocatedAddress != requestedIp {
435+
continue
436+
}
437+
if requestedIp != "" && allocatedAddress == requestedIp {
438+
isRequestedIpAllocated = true
439+
}
429440
// We have a pre-allocated ip, we just need to ensure that it matches the current address
430441
// if it does not, continue and try the next address
431442
if ipPreAllocated && allocatedAddress != preAllocatedAddress {
@@ -455,6 +466,11 @@ func (m *IPPoolManager) allocateAddress(addressClaim *ipamv1.IPClaim,
455466
}
456467
}
457468
}
469+
// We did not get requestedIp as it did not match with any available IP
470+
if requestedIp != "" && isRequestedIpAllocated && !ipAllocated {
471+
addressClaim.Status.ErrorMessage = ptr.To("Requested IP not available")
472+
return "", 0, nil, []ipamv1.IPAddressStr{}, errors.New("Requested IP not available")
473+
}
458474
// We have a preallocated IP but we did not find it in the pools! It means it is
459475
// misconfigured
460476
if !ipAllocated && ipPreAllocated {
@@ -485,6 +501,9 @@ func (m *IPPoolManager) capiAllocateAddress(addressClaim *capipamv1.IPAddressCla
485501

486502
ipAllocated := false
487503

504+
requestedIp := ipamv1.IPAddressStr(addressClaim.ObjectMeta.Labels[IpAddressLabel])
505+
isRequestedIpAllocated := false
506+
488507
for _, pool := range m.IPPool.Spec.Pools {
489508
if ipAllocated {
490509
break
@@ -496,6 +515,13 @@ func (m *IPPoolManager) capiAllocateAddress(addressClaim *capipamv1.IPAddressCla
496515
break
497516
}
498517
index++
518+
// Check if requestedIP is present and matches the current address
519+
if requestedIp != "" && allocatedAddress != requestedIp {
520+
continue
521+
}
522+
if requestedIp != "" && allocatedAddress == requestedIp {
523+
isRequestedIpAllocated = true
524+
}
499525
// We have a pre-allocated ip, we just need to ensure that it matches the current address
500526
// if it does not, continue and try the next address
501527
if ipPreAllocated && allocatedAddress != preAllocatedAddress {
@@ -522,6 +548,20 @@ func (m *IPPoolManager) capiAllocateAddress(addressClaim *capipamv1.IPAddressCla
522548
}
523549
}
524550
}
551+
// We did not get requestedIp as it did not match with any available IP
552+
if requestedIp != "" && isRequestedIpAllocated && !ipAllocated {
553+
conditions := clusterv1.Conditions{}
554+
conditions = append(conditions, clusterv1.Condition{
555+
Type: "ErrorMessage",
556+
Status: corev1.ConditionTrue,
557+
LastTransitionTime: metav1.Now(),
558+
Severity: "Error",
559+
Reason: "ErrorMessage",
560+
Message: "Requested IP not available",
561+
})
562+
addressClaim.SetConditions(conditions)
563+
return "", 0, nil, errors.New("Requested IP not available")
564+
}
525565
// We have a preallocated IP but we did not find it in the pools! It means it is
526566
// misconfigured
527567
if !ipAllocated && ipPreAllocated {

ipam/ippool_manager_test.go

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1916,6 +1916,62 @@ var _ = Describe("IPPool manager", func() {
19161916
expectedGateway: (*ipamv1.IPAddressStr)(ptr.To("192.168.0.1")),
19171917
expectedPrefix: 24,
19181918
}),
1919+
Entry("One pool, with start and existing address, ipAddress label present", testCaseAllocateAddress{
1920+
ipPool: &ipamv1.IPPool{
1921+
Spec: ipamv1.IPPoolSpec{
1922+
Pools: []ipamv1.Pool{
1923+
{
1924+
Start: (*ipamv1.IPAddressStr)(ptr.To("192.168.0.11")),
1925+
End: (*ipamv1.IPAddressStr)(ptr.To("192.168.0.20")),
1926+
},
1927+
},
1928+
Prefix: 24,
1929+
Gateway: (*ipamv1.IPAddressStr)(ptr.To("192.168.0.1")),
1930+
},
1931+
},
1932+
ipClaim: &ipamv1.IPClaim{
1933+
ObjectMeta: metav1.ObjectMeta{
1934+
Name: "TestRef",
1935+
Labels: map[string]string{
1936+
IpAddressLabel: "192.168.0.16",
1937+
},
1938+
},
1939+
},
1940+
addresses: map[ipamv1.IPAddressStr]string{
1941+
ipamv1.IPAddressStr("192.168.0.12"): "bcde",
1942+
ipamv1.IPAddressStr("192.168.0.11"): "abcd",
1943+
},
1944+
expectedAddress: ipamv1.IPAddressStr("192.168.0.16"),
1945+
expectedGateway: (*ipamv1.IPAddressStr)(ptr.To("192.168.0.1")),
1946+
expectedPrefix: 24,
1947+
}),
1948+
Entry("One pool, with start and existing address, ipAddress label present but alraedy acquired", testCaseAllocateAddress{
1949+
ipPool: &ipamv1.IPPool{
1950+
Spec: ipamv1.IPPoolSpec{
1951+
Pools: []ipamv1.Pool{
1952+
{
1953+
Start: (*ipamv1.IPAddressStr)(ptr.To("192.168.0.11")),
1954+
End: (*ipamv1.IPAddressStr)(ptr.To("192.168.0.20")),
1955+
},
1956+
},
1957+
Prefix: 24,
1958+
Gateway: (*ipamv1.IPAddressStr)(ptr.To("192.168.0.1")),
1959+
},
1960+
},
1961+
ipClaim: &ipamv1.IPClaim{
1962+
ObjectMeta: metav1.ObjectMeta{
1963+
Name: "TestRef",
1964+
Labels: map[string]string{
1965+
IpAddressLabel: "192.168.0.11",
1966+
},
1967+
},
1968+
},
1969+
addresses: map[ipamv1.IPAddressStr]string{
1970+
ipamv1.IPAddressStr("192.168.0.12"): "bcde",
1971+
ipamv1.IPAddressStr("192.168.0.11"): "abcd",
1972+
},
1973+
expectError: true,
1974+
}),
19191975
Entry("One pool, with subnet and override prefix", testCaseAllocateAddress{
19201976
ipPool: &ipamv1.IPPool{
19211977
Spec: ipamv1.IPPoolSpec{
@@ -2230,6 +2286,62 @@ var _ = Describe("IPPool manager", func() {
22302286
expectedGateway: (*ipamv1.IPAddressStr)(ptr.To("192.168.0.1")),
22312287
expectedPrefix: 24,
22322288
}),
2289+
Entry("One pool, with start and existing address, ipAddress label", testCapiCaseAllocateAddress{
2290+
ipPool: &ipamv1.IPPool{
2291+
Spec: ipamv1.IPPoolSpec{
2292+
Pools: []ipamv1.Pool{
2293+
{
2294+
Start: (*ipamv1.IPAddressStr)(ptr.To("192.168.0.11")),
2295+
End: (*ipamv1.IPAddressStr)(ptr.To("192.168.0.20")),
2296+
},
2297+
},
2298+
Prefix: 24,
2299+
Gateway: (*ipamv1.IPAddressStr)(ptr.To("192.168.0.1")),
2300+
},
2301+
},
2302+
ipAddressClaim: &capipamv1.IPAddressClaim{
2303+
ObjectMeta: metav1.ObjectMeta{
2304+
Name: "TestRef",
2305+
Labels: map[string]string{
2306+
IpAddressLabel: "192.168.0.16",
2307+
},
2308+
},
2309+
},
2310+
addresses: map[ipamv1.IPAddressStr]string{
2311+
ipamv1.IPAddressStr("192.168.0.12"): "bcde",
2312+
ipamv1.IPAddressStr("192.168.0.11"): "abcd",
2313+
},
2314+
expectedAddress: ipamv1.IPAddressStr("192.168.0.16"),
2315+
expectedGateway: (*ipamv1.IPAddressStr)(ptr.To("192.168.0.1")),
2316+
expectedPrefix: 24,
2317+
}),
2318+
Entry("One pool, with start and existing address, ipAddress label but alraedy acquired", testCapiCaseAllocateAddress{
2319+
ipPool: &ipamv1.IPPool{
2320+
Spec: ipamv1.IPPoolSpec{
2321+
Pools: []ipamv1.Pool{
2322+
{
2323+
Start: (*ipamv1.IPAddressStr)(ptr.To("192.168.0.11")),
2324+
End: (*ipamv1.IPAddressStr)(ptr.To("192.168.0.20")),
2325+
},
2326+
},
2327+
Prefix: 24,
2328+
Gateway: (*ipamv1.IPAddressStr)(ptr.To("192.168.0.1")),
2329+
},
2330+
},
2331+
ipAddressClaim: &capipamv1.IPAddressClaim{
2332+
ObjectMeta: metav1.ObjectMeta{
2333+
Name: "TestRef",
2334+
Labels: map[string]string{
2335+
IpAddressLabel: "192.168.0.12",
2336+
},
2337+
},
2338+
},
2339+
addresses: map[ipamv1.IPAddressStr]string{
2340+
ipamv1.IPAddressStr("192.168.0.12"): "bcde",
2341+
ipamv1.IPAddressStr("192.168.0.11"): "abcd",
2342+
},
2343+
expectError: true,
2344+
}),
22332345
Entry("One pool, with subnet and override prefix", testCapiCaseAllocateAddress{
22342346
ipPool: &ipamv1.IPPool{
22352347
Spec: ipamv1.IPPoolSpec{

0 commit comments

Comments
 (0)