Skip to content

Commit 2514136

Browse files
committed
This commit makes it possible to:
- Deploy IPAM with clusterctl - Reconsile CAPI's ipaddressclaims with this managers ippools Signed-off-by: peppi-lotta <[email protected]>
1 parent 2707785 commit 2514136

18 files changed

+2475
-422
lines changed

Dockerfile

+1-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ ARG BUILD_IMAGE=docker.io/golang:1.23.4@sha256:574185e5c6b9d09873f455a7c205ea051
1717
ARG BASE_IMAGE=gcr.io/distroless/static:nonroot@sha256:9ecc53c269509f63c69a266168e4a687c7eb8c0cfd753bd8bfcaa4f58a90876f
1818

1919
# Build the manager binary on golang image
20-
FROM $BUILD_IMAGE as builder
20+
FROM $BUILD_IMAGE AS builder
2121
WORKDIR /workspace
2222

2323
# Run this with docker build --build_arg $(go env GOPROXY) to override the goproxy

Makefile

+1
Original file line numberDiff line numberDiff line change
@@ -302,6 +302,7 @@ $(RELEASE_NOTES_DIR):
302302
.PHONY: release-manifests
303303
release-manifests: $(KUSTOMIZE) $(RELEASE_DIR) ## Builds the manifests to publish with a release
304304
$(KUSTOMIZE) build config/default > $(RELEASE_DIR)/ipam-components.yaml
305+
cp metadata.yaml $(RELEASE_DIR)/metadata.yaml
305306

306307
.PHONY: release-notes-tool
307308
release-notes-tool:

README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ Deploys IPAM CRDs and deploys IPAM controllers
5454
Runs IPAM controller locally
5555

5656
```sh
57-
kubectl scale -n capm3-system \
57+
kubectl scale -n metal3-ipam-system \
5858
deployment.v1.apps/metal3-ipam-controller-manager --replicas 0
5959
make run
6060
```

config/default/kustomization.yaml

+3-4
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,15 @@
11
apiVersion: kustomize.config.k8s.io/v1beta1
22
kind: Kustomization
33

4-
# Adds namespace to all resources. Keep it in capm3-system, as it is a
5-
# dependency for CAPM3
6-
namespace: capm3-system
4+
# Adds namespace to all resources.
5+
namespace: metal3-ipam-system
76

87
namePrefix: ipam-
98

109
labels:
1110
- includeSelectors: true
1211
pairs:
13-
cluster.x-k8s.io/provider: infrastructure-metal3
12+
cluster.x-k8s.io/provider: ipam-metal3
1413

1514
resources:
1615
- ../rbac

config/manager/manager.yaml

+7
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,10 @@
1+
apiVersion: v1
2+
kind: Namespace
3+
metadata:
4+
labels:
5+
control-plane: controller-manager
6+
name: system
7+
---
18
apiVersion: apps/v1
29
kind: Deployment
310
metadata:

config/rbac/role.yaml

+40
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,46 @@ rules:
5353
- clusters/status
5454
verbs:
5555
- get
56+
- apiGroups:
57+
- ipam.cluster.x-k8s.io
58+
resources:
59+
- ipaddressclaims
60+
verbs:
61+
- create
62+
- delete
63+
- get
64+
- list
65+
- patch
66+
- update
67+
- watch
68+
- apiGroups:
69+
- ipam.cluster.x-k8s.io
70+
resources:
71+
- ipaddressclaims/status
72+
verbs:
73+
- get
74+
- patch
75+
- update
76+
- apiGroups:
77+
- ipam.cluster.x-k8s.io
78+
resources:
79+
- ipaddresses
80+
verbs:
81+
- create
82+
- delete
83+
- get
84+
- list
85+
- patch
86+
- update
87+
- watch
88+
- apiGroups:
89+
- ipam.cluster.x-k8s.io
90+
resources:
91+
- ipaddresses/status
92+
verbs:
93+
- get
94+
- patch
95+
- update
5696
- apiGroups:
5797
- ipam.metal3.io
5898
resources:

controllers/ippool_controller.go

+37-2
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import (
2727
apierrors "k8s.io/apimachinery/pkg/api/errors"
2828
"k8s.io/apimachinery/pkg/types"
2929
clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
30+
capipamv1 "sigs.k8s.io/cluster-api/exp/ipam/api/v1beta1"
3031
"sigs.k8s.io/cluster-api/util/annotations"
3132
"sigs.k8s.io/cluster-api/util/patch"
3233
"sigs.k8s.io/cluster-api/util/predicates"
@@ -56,6 +57,10 @@ type IPPoolReconciler struct {
5657
// +kubebuilder:rbac:groups=ipam.metal3.io,resources=ipclaims/status,verbs=get;update;patch
5758
// +kubebuilder:rbac:groups=ipam.metal3.io,resources=ipaddresses,verbs=get;list;watch;create;update;patch;delete
5859
// +kubebuilder:rbac:groups=ipam.metal3.io,resources=ipaddresses/status,verbs=get;update;patch
60+
// +kubebuilder:rbac:groups=ipam.cluster.x-k8s.io,resources=ipaddressclaims,verbs=get;list;watch;create;update;patch;delete
61+
// +kubebuilder:rbac:groups=ipam.cluster.x-k8s.io,resources=ipaddressclaims/status,verbs=get;update;patch
62+
// +kubebuilder:rbac:groups=ipam.cluster.x-k8s.io,resources=ipaddresses,verbs=get;list;watch;create;update;patch;delete
63+
// +kubebuilder:rbac:groups=ipam.cluster.x-k8s.io,resources=ipaddresses/status,verbs=get;update;patch
5964
// +kubebuilder:rbac:groups=cluster.x-k8s.io,resources=clusters,verbs=get;list;watch
6065
// +kubebuilder:rbac:groups=cluster.x-k8s.io,resources=clusters/status,verbs=get
6166
// +kubebuilder:rbac:groups="",resources=events,verbs=get;list;watch;create;update;patch
@@ -98,7 +103,7 @@ func (r *IPPoolReconciler) Reconcile(ctx context.Context, req ctrl.Request) (_ c
98103
ipamv1IPPool.ObjectMeta.Labels = make(map[string]string)
99104
}
100105
ipamv1IPPool.ObjectMeta.Labels[clusterv1.ClusterNameLabel] = *ipamv1IPPool.Spec.ClusterName
101-
ipamv1IPPool.ObjectMeta.Labels[clusterv1.ProviderNameLabel] = "infrastructure-metal3"
106+
ipamv1IPPool.ObjectMeta.Labels[clusterv1.ProviderNameLabel] = "ipam-metal3"
102107

103108
// Fetch the Cluster. Ignore an error if the deletion timestamp is set
104109
err = r.Client.Get(ctx, key, cluster)
@@ -172,7 +177,7 @@ func (r *IPPoolReconciler) reconcileDelete(ctx context.Context,
172177
}
173178

174179
// SetupWithManager will add watches for this controller.
175-
func (r *IPPoolReconciler) SetupWithManager(ctx context.Context, mgr ctrl.Manager, options controller.Options) error {
180+
func (r *IPPoolReconciler) SetupWithManagerForIPClaim(ctx context.Context, mgr ctrl.Manager, options controller.Options) error {
176181
return ctrl.NewControllerManagedBy(mgr).
177182
For(&ipamv1.IPPool{}).
178183
WithOptions(options).
@@ -184,6 +189,19 @@ func (r *IPPoolReconciler) SetupWithManager(ctx context.Context, mgr ctrl.Manage
184189
Complete(r)
185190
}
186191

192+
// SetupWithManager will add watches for this controller.
193+
func (r *IPPoolReconciler) SetupWithManagerForIPAddressClaim(ctx context.Context, mgr ctrl.Manager, options controller.Options) error {
194+
return ctrl.NewControllerManagedBy(mgr).
195+
For(&ipamv1.IPPool{}).
196+
WithOptions(options).
197+
Watches(
198+
&capipamv1.IPAddressClaim{},
199+
handler.EnqueueRequestsFromMapFunc(r.IPAddressClaimToIPPool),
200+
).
201+
WithEventFilter(predicates.ResourceNotPausedAndHasFilterLabel(ctrl.LoggerFrom(ctx), r.WatchFilterValue)).
202+
Complete(r)
203+
}
204+
187205
// IPClaimToIPPool will return a reconcile request for a
188206
// Metal3DataTemplate if the event is for a
189207
// IPClaim and that IPClaim references a Metal3DataTemplate.
@@ -207,6 +225,23 @@ func (r *IPPoolReconciler) IPClaimToIPPool(_ context.Context, obj client.Object)
207225
return []ctrl.Request{}
208226
}
209227

228+
func (r *IPPoolReconciler) IPAddressClaimToIPPool(_ context.Context, obj client.Object) []ctrl.Request {
229+
if ipac, ok := obj.(*capipamv1.IPAddressClaim); ok {
230+
if ipac.Spec.PoolRef.Name != "" {
231+
namespace := ipac.Namespace
232+
return []ctrl.Request{
233+
{
234+
NamespacedName: types.NamespacedName{
235+
Name: ipac.Spec.PoolRef.Name,
236+
Namespace: namespace,
237+
},
238+
},
239+
}
240+
}
241+
}
242+
return []ctrl.Request{}
243+
}
244+
210245
// checkReconcileError checks if the error is a transient or terminal error.
211246
// If it is transient, it returns a Result with Requeue set to true.
212247
// Non-reconcile errors are returned as-is.

controllers/ippool_controller_test.go

+60
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ import (
3333
"k8s.io/apimachinery/pkg/types"
3434
"k8s.io/utils/ptr"
3535
clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
36+
capipamv1 "sigs.k8s.io/cluster-api/exp/ipam/api/v1beta1"
3637
"sigs.k8s.io/controller-runtime/pkg/client"
3738
"sigs.k8s.io/controller-runtime/pkg/client/fake"
3839
"sigs.k8s.io/controller-runtime/pkg/reconcile"
@@ -407,4 +408,63 @@ var _ = Describe("IPPool controller", func() {
407408
},
408409
),
409410
)
411+
412+
type TestCaseK8SIPACToM3IPP struct {
413+
IPAddressClaim *capipamv1.IPAddressClaim
414+
ExpectRequest bool
415+
}
416+
417+
DescribeTable("IPAddressClaim To IPPool tests",
418+
func(tc TestCaseK8SIPACToM3IPP) {
419+
r := IPPoolReconciler{}
420+
obj := client.Object(tc.IPAddressClaim)
421+
reqs := r.IPAddressClaimToIPPool(context.Background(), obj)
422+
423+
if tc.ExpectRequest {
424+
Expect(len(reqs)).To(Equal(1), "Expected 1 request, found %d", len(reqs))
425+
426+
req := reqs[0]
427+
Expect(req.NamespacedName.Name).To(Equal(tc.IPAddressClaim.Spec.PoolRef.Name),
428+
"Expected name %s, found %s", tc.IPAddressClaim.Spec.PoolRef.Name, req.NamespacedName.Name)
429+
} else {
430+
Expect(len(reqs)).To(Equal(0), "Expected 0 request, found %d", len(reqs))
431+
432+
}
433+
},
434+
Entry("No IPPool in Spec",
435+
TestCaseK8SIPACToM3IPP{
436+
IPAddressClaim: &capipamv1.IPAddressClaim{
437+
ObjectMeta: testObjectMeta,
438+
Spec: capipamv1.IPAddressClaimSpec{},
439+
},
440+
ExpectRequest: false,
441+
},
442+
),
443+
Entry("IPPool in Spec, with namespace",
444+
TestCaseK8SIPACToM3IPP{
445+
IPAddressClaim: &capipamv1.IPAddressClaim{
446+
ObjectMeta: testObjectMeta,
447+
Spec: capipamv1.IPAddressClaimSpec{
448+
PoolRef: corev1.TypedLocalObjectReference{
449+
Name: "abc",
450+
},
451+
},
452+
},
453+
ExpectRequest: true,
454+
},
455+
),
456+
Entry("IPPool in Spec, no namespace",
457+
TestCaseK8SIPACToM3IPP{
458+
IPAddressClaim: &capipamv1.IPAddressClaim{
459+
ObjectMeta: testObjectMeta,
460+
Spec: capipamv1.IPAddressClaimSpec{
461+
PoolRef: corev1.TypedLocalObjectReference{
462+
Name: "abc",
463+
},
464+
},
465+
},
466+
ExpectRequest: true,
467+
},
468+
),
469+
)
410470
})

controllers/suite_test.go

+9
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ import (
3232
ipamv1 "github.com/metal3-io/ip-address-manager/api/v1alpha1"
3333
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
3434
clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
35+
capipamv1 "sigs.k8s.io/cluster-api/exp/ipam/api/v1beta1"
3536
ctrl "sigs.k8s.io/controller-runtime"
3637
"sigs.k8s.io/controller-runtime/pkg/client"
3738
"sigs.k8s.io/controller-runtime/pkg/envtest"
@@ -57,6 +58,7 @@ func init() {
5758
_ = apiextensionsv1.AddToScheme(scheme.Scheme)
5859
_ = clusterv1.AddToScheme(scheme.Scheme)
5960
_ = ipamv1.AddToScheme(scheme.Scheme)
61+
_ = capipamv1.AddToScheme(scheme.Scheme)
6062
}
6163

6264
func setupScheme() *runtime.Scheme {
@@ -70,6 +72,10 @@ func setupScheme() *runtime.Scheme {
7072
panic(err)
7173
}
7274

75+
if err := capipamv1.AddToScheme(s); err != nil {
76+
panic(err)
77+
}
78+
7379
return s
7480
}
7581
func TestAPIs(t *testing.T) {
@@ -94,6 +100,9 @@ var _ = BeforeSuite(func() {
94100
err = ipamv1.AddToScheme(scheme.Scheme)
95101
Expect(err).NotTo(HaveOccurred())
96102

103+
err = capipamv1.AddToScheme(scheme.Scheme)
104+
Expect(err).NotTo(HaveOccurred())
105+
97106
err = apiextensionsv1.AddToScheme(scheme.Scheme)
98107
Expect(err).NotTo(HaveOccurred())
99108

docs/api.md

+54-3
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,9 @@ The *spec* field contains the following :
4040
* **pools**: this is a list of IP address pools
4141
* **prefix**: This is a default prefix for this IPPool
4242
* **gateway**: This is a default gateway for this IPPool
43-
* **preAllocations**: This is a default preallocated IP address for this IPPool
43+
* **preAllocations**: This is a default preallocated IP address for this IPPool.
44+
Preallocations asossiate a claim's name to an IP address. It doesn't matter if
45+
the claim type is (metal3)IPClaim or (capi)IPAddressClaim.
4446
4547
The *prefix* and *gateway* can be overridden per pool. The pool definition is
4648
as follows :
@@ -51,12 +53,13 @@ as follows :
5153
It is used to verify that the allocated address belongs to this subnet.
5254
* **prefix**: override of the default prefix for this pool
5355
* **gateway**: override of the default gateway for this pool
56+
* **DNSServers**: override of the default dns servers for this pool
5457
5558
## IPClaim
5659
5760
An IPClaim is an object representing a request for an IP address allocation.
5861
59-
Example pool:
62+
Example IPClaim:
6063
6164
```yaml
6265
apiVersion: ipam.metal3.io/v1alpha1
@@ -78,7 +81,7 @@ The *spec* field contains the following :
7881
7982
An IPAddress is an object representing an IP address allocation.
8083
81-
Example pool:
84+
Example IPAddress:
8285
8386
```yaml
8487
apiVersion: ipam.metal3.io/v1alpha1
@@ -105,8 +108,56 @@ The *spec* field contains the following :
105108
* **address**: the allocated IP address
106109
* **prefix**: the prefix for this address
107110
* **gateway**: the gateway for this address
111+
* **DNSServers**: a list of dns servers
108112
109113
## Metal3 dev env examples
110114
111115
You can find CR examples in the
112116
[Metal3-io dev env project](https://github.com/metal3-io/metal3-dev-env)
117+
118+
## Handling CAPI CRs
119+
120+
This IPAM can be deployed and used as an
121+
[IPAM provider](https://github.com/kubernetes-sigs/cluster-api/blob/main/docs/book/src/reference/glossary.md#ipam-provider)for
122+
[CAPI](https://github.com/kubernetes-sigs/cluster-api).
123+
124+
IPPool reconsiles (metal3)ipclaims into (metal3)ipaddresses
125+
and (capi)ipaddressclaims into (capi)ipaddresses.
126+
127+
### IPAddressClaim
128+
129+
Check out more on [IPAddressClaim docs](https://docs.openshift.com/container-platform/4.16/rest_api/network_apis/ipaddressclaim-ipam-cluster-x-k8s-io-v1beta1.html).
130+
131+
### IpAddress
132+
133+
Check out more on [IPAddress docs](https://docs.openshift.com/container-platform/4.16/rest_api/network_apis/ipaddress-ipam-cluster-x-k8s-io-v1beta1.html).
134+
135+
### Set up via clusterctl
136+
137+
Since it's not added to the built-in list of providers yet,
138+
you'll need to add the following to your
139+
```$XDG_CONFIG_HOME/cluster-api/clusterctl.yaml```
140+
if you want to install it using ```clusterctl init --ipam metal3```:
141+
142+
```yaml
143+
providers:
144+
- name: metal3
145+
url: https://github.com/metal3-io/ip-address-manager/releases/latest/ipam-components.yaml
146+
type: IPAMProvider
147+
```
148+
149+
If you are also specifying infrastructure provider
150+
metal3 liko so:
151+
```clusterctl init --infrastructure metal3 --ipam metal3```.
152+
It might cause a problem to have the same name
153+
for both providers if you are creating local
154+
[overrides layers](https://cluster-api.sigs.k8s.io/clusterctl/configuration#overrides-layer).
155+
Solution is to change the ipam providers name:
156+
```clusterctl init --infrastructure metal3 --ipam m3ipam``
157+
158+
```yaml
159+
providers:
160+
- name: m3ipam
161+
url: https://github.com/metal3-io/ip-address-manager/releases/latest/ipam-components.yaml
162+
type: IPAMProvider
163+
```

examples/generate.sh

+1-1
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ OUTPUT_DIR=${OUTPUT_DIR:-${SOURCE_DIR}/_out}
2323

2424
# Cluster.
2525
export CLUSTER_NAME="${CLUSTER_NAME:-test1}"
26-
export NAMESPACE="${NAMESPACE:-capm3-system}"
26+
export NAMESPACE="${NAMESPACE:-metal3-ipam-system}"
2727

2828
# Outputs.
2929
COMPONENTS_CERT_MANAGER_GENERATED_FILE=${OUTPUT_DIR}/cert-manager.yaml

0 commit comments

Comments
 (0)