Skip to content

Commit 1d77213

Browse files
committed
feat: Apply MetalLB configuration to remote cluster
1 parent 85318d1 commit 1d77213

File tree

4 files changed

+146
-0
lines changed

4 files changed

+146
-0
lines changed

pkg/handlers/generic/lifecycle/serviceloadbalancer/handler.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import (
2222
type ServiceLoadBalancerProvider interface {
2323
Apply(
2424
ctx context.Context,
25+
slb v1alpha1.ServiceLoadBalancer,
2526
cluster *clusterv1.Cluster,
2627
log logr.Logger,
2728
) error
@@ -131,6 +132,7 @@ func (s *ServiceLoadBalancerHandler) apply(
131132
log.Info(fmt.Sprintf("Deploying ServiceLoadBalancer provider %s", slb.Provider))
132133
err = handler.Apply(
133134
ctx,
135+
slb,
134136
cluster,
135137
log,
136138
)

pkg/handlers/generic/lifecycle/serviceloadbalancer/handler_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ type fakeServiceLoadBalancerProvider struct {
2525

2626
func (p *fakeServiceLoadBalancerProvider) Apply(
2727
ctx context.Context,
28+
slb v1alpha1.ServiceLoadBalancer,
2829
cluster *clusterv1.Cluster,
2930
log logr.Logger,
3031
) error {
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
package metallb
2+
3+
import (
4+
"fmt"
5+
6+
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
7+
"k8s.io/apimachinery/pkg/runtime/schema"
8+
9+
"github.com/nutanix-cloud-native/cluster-api-runtime-extensions-nutanix/api/v1alpha1"
10+
)
11+
12+
func groupVersionKind(kind string) schema.GroupVersionKind {
13+
return schema.GroupVersionKind{
14+
Group: "metallb.io",
15+
Version: "v1beta1",
16+
Kind: kind,
17+
}
18+
}
19+
20+
type configurationInput struct {
21+
name string
22+
namespace string
23+
addressRanges []v1alpha1.AddressRange
24+
}
25+
26+
func configurationObjects(input *configurationInput) ([]unstructured.Unstructured, error) {
27+
ipAddressPool := unstructured.Unstructured{}
28+
ipAddressPool.SetGroupVersionKind(groupVersionKind("IPAddressPool"))
29+
ipAddressPool.SetName(input.name)
30+
ipAddressPool.SetNamespace(input.namespace)
31+
32+
addresses := []string{}
33+
for _, ar := range input.addressRanges {
34+
addresses = append(addresses, fmt.Sprintf("%s-%s", ar.Start, ar.End))
35+
}
36+
if err := unstructured.SetNestedStringSlice(
37+
ipAddressPool.Object,
38+
addresses,
39+
"spec",
40+
"addresses",
41+
); err != nil {
42+
return nil, fmt.Errorf("failed to set IPAddressPool .spec.addresses: %w", err)
43+
}
44+
45+
l2Advertisement := unstructured.Unstructured{}
46+
l2Advertisement.SetGroupVersionKind(groupVersionKind("L2Advertisement"))
47+
l2Advertisement.SetName(input.name)
48+
l2Advertisement.SetNamespace(input.namespace)
49+
50+
if err := unstructured.SetNestedStringSlice(
51+
l2Advertisement.Object,
52+
[]string{
53+
ipAddressPool.GetName(),
54+
},
55+
"spec",
56+
"ipAddressPools",
57+
); err != nil {
58+
return nil, fmt.Errorf("failed to set L2Advertisement .spec.ipAddressPools: %w", err)
59+
}
60+
61+
return []unstructured.Unstructured{
62+
ipAddressPool,
63+
l2Advertisement,
64+
}, nil
65+
}

pkg/handlers/generic/lifecycle/serviceloadbalancer/metallb/handler.go

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,20 +6,25 @@ package metallb
66
import (
77
"context"
88
"fmt"
9+
"time"
910

1011
"github.com/go-logr/logr"
1112
"github.com/spf13/pflag"
13+
corev1 "k8s.io/api/core/v1"
1214
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
15+
kwait "k8s.io/apimachinery/pkg/util/wait"
1316
clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
1417
"sigs.k8s.io/cluster-api/controllers/remote"
1518
ctrlclient "sigs.k8s.io/controller-runtime/pkg/client"
1619
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
1720

1821
caaphv1 "github.com/nutanix-cloud-native/cluster-api-runtime-extensions-nutanix/api/external/sigs.k8s.io/cluster-api-addon-provider-helm/api/v1alpha1"
22+
"github.com/nutanix-cloud-native/cluster-api-runtime-extensions-nutanix/api/v1alpha1"
1923
"github.com/nutanix-cloud-native/cluster-api-runtime-extensions-nutanix/common/pkg/k8s/client"
2024
"github.com/nutanix-cloud-native/cluster-api-runtime-extensions-nutanix/pkg/handlers/generic/lifecycle/config"
2125
"github.com/nutanix-cloud-native/cluster-api-runtime-extensions-nutanix/pkg/handlers/options"
2226
handlersutils "github.com/nutanix-cloud-native/cluster-api-runtime-extensions-nutanix/pkg/handlers/utils"
27+
"github.com/nutanix-cloud-native/cluster-api-runtime-extensions-nutanix/pkg/wait"
2328
)
2429

2530
const (
@@ -70,6 +75,7 @@ func New(
7075

7176
func (n *MetalLB) Apply(
7277
ctx context.Context,
78+
slb v1alpha1.ServiceLoadBalancer,
7379
cluster *clusterv1.Cluster,
7480
log logr.Logger,
7581
) error {
@@ -152,5 +158,77 @@ func (n *MetalLB) Apply(
152158
return fmt.Errorf("failed to apply MetalLB installation HelmChartProxy: %w", err)
153159
}
154160

161+
if err := wait.ForObject(
162+
ctx,
163+
wait.ForObjectInput[*caaphv1.HelmChartProxy]{
164+
Reader: n.client,
165+
Target: hcp.DeepCopy(),
166+
Check: func(_ context.Context, obj *caaphv1.HelmChartProxy) (bool, error) {
167+
for _, c := range obj.GetConditions() {
168+
if c.Type == caaphv1.HelmReleaseProxiesReadyCondition && c.Status == corev1.ConditionTrue {
169+
return true, nil
170+
}
171+
}
172+
return false, nil
173+
},
174+
Interval: 5 * time.Second,
175+
Timeout: 10 * time.Second,
176+
},
177+
); err != nil {
178+
return fmt.Errorf("failed to wait for MetalLB to deploy: %w", err)
179+
}
180+
181+
log.Info(
182+
fmt.Sprintf("Applying MetalLB configuration to cluster %s",
183+
ctrlclient.ObjectKeyFromObject(cluster),
184+
),
185+
)
186+
187+
cos, err := configurationObjects(&configurationInput{
188+
name: defaultHelmReleaseName,
189+
namespace: defaultHelmReleaseNamespace,
190+
addressRanges: slb.Configuration.AddressRanges,
191+
})
192+
if err != nil {
193+
return fmt.Errorf("failed to generate MetalLB configuration: %w", err)
194+
}
195+
196+
var applyErr error
197+
if waitErr := kwait.PollUntilContextTimeout(
198+
ctx,
199+
5*time.Second,
200+
10*time.Second,
201+
true,
202+
func(ctx context.Context) (done bool, err error) {
203+
for i := range cos {
204+
o := &cos[i]
205+
if err = client.ServerSideApply(
206+
ctx,
207+
remoteClient,
208+
o,
209+
&ctrlclient.PatchOptions{
210+
Raw: &metav1.PatchOptions{
211+
FieldValidation: metav1.FieldValidationStrict,
212+
},
213+
},
214+
); err != nil {
215+
applyErr = fmt.Errorf(
216+
"failed to apply MetalLB configuration %s %s: %w",
217+
o.GetKind(),
218+
ctrlclient.ObjectKeyFromObject(o),
219+
err,
220+
)
221+
return false, nil
222+
}
223+
}
224+
return true, nil
225+
},
226+
); waitErr != nil {
227+
if applyErr != nil {
228+
return fmt.Errorf("%w: last apply error: %w", waitErr, applyErr)
229+
}
230+
return fmt.Errorf("%w: failed to apply MetalLB configuration", waitErr)
231+
}
232+
155233
return nil
156234
}

0 commit comments

Comments
 (0)