Skip to content

Commit c09fdf6

Browse files
committed
add load balancer controller to capv
Signed-off-by: Yassine TIJANI <[email protected]>
1 parent 6cd81f9 commit c09fdf6

File tree

8 files changed

+329
-52
lines changed

8 files changed

+329
-52
lines changed
Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
package controllers
2+
3+
import (
4+
goctx "context"
5+
6+
"github.com/go-logr/logr"
7+
"k8s.io/client-go/tools/record"
8+
infrav1 "sigs.k8s.io/cluster-api-provider-vsphere/api/v1alpha2"
9+
"sigs.k8s.io/cluster-api-provider-vsphere/api/v1alpha2/cloud"
10+
controllerutil "sigs.k8s.io/cluster-api-provider-vsphere/controllers/util"
11+
"sigs.k8s.io/cluster-api-provider-vsphere/pkg/cloud/vsphere/config"
12+
"sigs.k8s.io/cluster-api-provider-vsphere/pkg/cloud/vsphere/services"
13+
"sigs.k8s.io/cluster-api-provider-vsphere/pkg/cloud/vsphere/services/loadbalancer/aws"
14+
infrautilv1 "sigs.k8s.io/cluster-api-provider-vsphere/pkg/cloud/vsphere/util"
15+
16+
clusterutilv1 "sigs.k8s.io/cluster-api/util"
17+
ctrl "sigs.k8s.io/controller-runtime"
18+
"sigs.k8s.io/controller-runtime/pkg/client"
19+
"sigs.k8s.io/controller-runtime/pkg/reconcile"
20+
)
21+
22+
type providerFunc func(config *cloud.LoadBalancerConfig) services.LoadBalancerService
23+
24+
var providerFactory map[string]providerFunc
25+
26+
func init() {
27+
providerFactory = map[string]providerFunc{
28+
cloud.AwsProvider: aws.NewProvider,
29+
}
30+
}
31+
32+
// LoadBalancerReconciler reconciles load balancers
33+
type LoadBalancerReconciler struct {
34+
client.Client
35+
ProviderName string
36+
Recorder record.EventRecorder
37+
Log logr.Logger
38+
}
39+
40+
// +kubebuilder:rbac:groups=infrastructure.cluster.x-k8s.io,resources=vsphereclusters,verbs=get;list;watch;
41+
// +kubebuilder:rbac:groups=infrastructure.cluster.x-k8s.io,resources=vsphereclusters/status,verbs=get;update;patch
42+
43+
// Reconcile todo
44+
func (r *LoadBalancerReconciler) Reconcile(req ctrl.Request) (_ ctrl.Result, reterr error) {
45+
logger := r.Log.WithName("loadBalancer")
46+
47+
ctx, result, reterr := controllerutil.GetClusterContext(req, logger, r)
48+
if ctx == nil {
49+
return result, reterr
50+
}
51+
vsphereCluster := ctx.VSphereCluster
52+
// check if the cluster does not have Load balancing config
53+
if vsphereCluster.Spec.LoadBalancerConfiguration == nil {
54+
return ctrl.Result{}, reterr
55+
}
56+
// Always close the context when exiting this function so we can persist any VSphereCluster changes.
57+
defer func() {
58+
if err := ctx.Patch(); err != nil && reterr == nil {
59+
reterr = err
60+
}
61+
}()
62+
63+
// Handle deleted clusters
64+
if !vsphereCluster.DeletionTimestamp.IsZero() {
65+
return r.reconcileDelete(vsphereCluster)
66+
}
67+
68+
// Handle non-deleted clusters
69+
return r.reconcileNormal(ctx, vsphereCluster)
70+
}
71+
72+
func (r *LoadBalancerReconciler) reconcileDelete(vsphereCluster *infrav1.VSphereCluster) (_ ctrl.Result, reterr error) {
73+
newProvider := providerFactory[r.ProviderName]
74+
loadBalancerProvider := newProvider(vsphereCluster.Spec.LoadBalancerConfiguration)
75+
if reterr = loadBalancerProvider.Delete(vsphereCluster); reterr != nil {
76+
return reconcile.Result{RequeueAfter: config.DefaultRequeue}, reterr
77+
}
78+
vsphereCluster.Finalizers = clusterutilv1.Filter(vsphereCluster.Finalizers, infrav1.LoadBalancerFinalizer)
79+
return ctrl.Result{}, nil
80+
}
81+
82+
func (r *LoadBalancerReconciler) reconcileNormal(ctx goctx.Context, vsphereCluster *infrav1.VSphereCluster) (_ ctrl.Result, reterr error) {
83+
newProvider := providerFactory[r.ProviderName]
84+
loadBalancerProvider := newProvider(vsphereCluster.Spec.LoadBalancerConfiguration)
85+
86+
machines, reterr := infrautilv1.GetMachinesInCluster(ctx, r.Client, vsphereCluster.Namespace, vsphereCluster.Name)
87+
if reterr != nil {
88+
return ctrl.Result{}, reterr
89+
}
90+
machines = clusterutilv1.GetControlPlaneMachines(machines)
91+
vsphereMachines, reterr := infrautilv1.GetVSphereMachinesInCluster(ctx, r.Client, vsphereCluster.Namespace, vsphereCluster.Name)
92+
if reterr != nil {
93+
return ctrl.Result{}, reterr
94+
}
95+
96+
var controlPlaneIPs []string
97+
for _, controlPlane := range machines {
98+
vsphereMachine := vsphereMachines[controlPlane.Name]
99+
ip, reterr := infrautilv1.GetMachinePreferredIPAddress(vsphereMachine)
100+
if reterr != nil {
101+
return ctrl.Result{}, reterr
102+
}
103+
controlPlaneIPs = append(controlPlaneIPs, ip)
104+
}
105+
106+
apiEndpoint, reterr := loadBalancerProvider.Reconcile(vsphereCluster, controlPlaneIPs)
107+
if reterr != nil {
108+
return ctrl.Result{}, reterr
109+
}
110+
vsphereCluster.Finalizers = append(vsphereCluster.Finalizers, infrav1.LoadBalancerFinalizer)
111+
vsphereCluster.Status.APIEndpoints = append(vsphereCluster.Status.APIEndpoints, apiEndpoint)
112+
return ctrl.Result{}, nil
113+
}
114+
115+
// SetupWithManager adds this controller to the provided manager.
116+
func (r *LoadBalancerReconciler) SetupWithManager(mgr ctrl.Manager) error {
117+
return ctrl.NewControllerManagedBy(mgr).
118+
For(&infrav1.VSphereCluster{}).
119+
Complete(r)
120+
}

controllers/util/cluster.go

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
package util
2+
3+
import (
4+
goctx "context"
5+
"fmt"
6+
7+
"github.com/go-logr/logr"
8+
"github.com/pkg/errors"
9+
apierrors "k8s.io/apimachinery/pkg/api/errors"
10+
clusterutilv1 "sigs.k8s.io/cluster-api/util"
11+
ctrl "sigs.k8s.io/controller-runtime"
12+
"sigs.k8s.io/controller-runtime/pkg/client"
13+
"sigs.k8s.io/controller-runtime/pkg/reconcile"
14+
15+
infrav1 "sigs.k8s.io/cluster-api-provider-vsphere/api/v1alpha2"
16+
"sigs.k8s.io/cluster-api-provider-vsphere/pkg/cloud/vsphere/config"
17+
"sigs.k8s.io/cluster-api-provider-vsphere/pkg/cloud/vsphere/context"
18+
)
19+
20+
// GetClusterContext creates clusterContext wrapped with logs
21+
func GetClusterContext(req ctrl.Request, logger logr.Logger, client client.Client) (ctx *context.ClusterContext, _ ctrl.Result, reterr error) {
22+
parentContext := goctx.Background()
23+
24+
logger = logger.WithName(fmt.Sprintf("namespace=%s", req.Namespace)).
25+
WithName(fmt.Sprintf("vsphereCluster=%s", req.Name))
26+
27+
// Fetch the VSphereCluster instance
28+
vsphereCluster := &infrav1.VSphereCluster{}
29+
reterr = client.Get(parentContext, req.NamespacedName, vsphereCluster)
30+
if reterr != nil {
31+
if apierrors.IsNotFound(reterr) {
32+
return nil, reconcile.Result{}, nil
33+
}
34+
return nil, reconcile.Result{}, reterr
35+
}
36+
37+
logger = logger.WithName(vsphereCluster.APIVersion)
38+
39+
// Fetch the Cluster.
40+
cluster, reterr := clusterutilv1.GetOwnerCluster(parentContext, client, vsphereCluster.ObjectMeta)
41+
if reterr != nil {
42+
return nil, reconcile.Result{}, reterr
43+
}
44+
if cluster == nil {
45+
logger.Info("Waiting for Cluster Controller to set OwnerRef on VSphereCluster")
46+
return nil, reconcile.Result{RequeueAfter: config.DefaultRequeue}, nil
47+
}
48+
49+
logger = logger.WithName(fmt.Sprintf("cluster=%s", cluster.Name))
50+
51+
// Create the context.
52+
ctx, reterr = context.NewClusterContext(&context.ClusterContextParams{
53+
Context: parentContext,
54+
Cluster: cluster,
55+
VSphereCluster: vsphereCluster,
56+
Client: client,
57+
Logger: logger,
58+
})
59+
if reterr != nil {
60+
return nil, reconcile.Result{}, errors.Errorf("failed to create cluster context: %+v", reterr)
61+
}
62+
return ctx, reconcile.Result{}, nil
63+
}

controllers/vspherecluster_controller.go

Lines changed: 13 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ limitations under the License.
1717
package controllers
1818

1919
import (
20-
goctx "context"
2120
"fmt"
2221

2322
"github.com/go-logr/logr"
@@ -26,13 +25,13 @@ import (
2625
apierrors "k8s.io/apimachinery/pkg/api/errors"
2726
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2827
"k8s.io/client-go/tools/record"
28+
controllerutil "sigs.k8s.io/cluster-api-provider-vsphere/controllers/util"
2929
clusterutilv1 "sigs.k8s.io/cluster-api/util"
3030
ctrl "sigs.k8s.io/controller-runtime"
3131
"sigs.k8s.io/controller-runtime/pkg/client"
3232
"sigs.k8s.io/controller-runtime/pkg/reconcile"
3333

3434
infrav1 "sigs.k8s.io/cluster-api-provider-vsphere/api/v1alpha2"
35-
"sigs.k8s.io/cluster-api-provider-vsphere/pkg/cloud/vsphere/config"
3635
"sigs.k8s.io/cluster-api-provider-vsphere/pkg/cloud/vsphere/context"
3736
infrautilv1 "sigs.k8s.io/cluster-api-provider-vsphere/pkg/cloud/vsphere/util"
3837
)
@@ -54,48 +53,13 @@ type VSphereClusterReconciler struct {
5453

5554
// Reconcile ensures the back-end state reflects the Kubernetes resource state intent.
5655
func (r *VSphereClusterReconciler) Reconcile(req ctrl.Request) (_ ctrl.Result, reterr error) {
57-
parentContext := goctx.Background()
56+
logger := r.Log.WithName(controllerName)
5857

59-
logger := r.Log.WithName(controllerName).
60-
WithName(fmt.Sprintf("namespace=%s", req.Namespace)).
61-
WithName(fmt.Sprintf("vsphereCluster=%s", req.Name))
62-
63-
// Fetch the VSphereCluster instance
64-
vsphereCluster := &infrav1.VSphereCluster{}
65-
err := r.Get(parentContext, req.NamespacedName, vsphereCluster)
66-
if err != nil {
67-
if apierrors.IsNotFound(err) {
68-
return reconcile.Result{}, nil
69-
}
70-
return reconcile.Result{}, err
71-
}
72-
73-
logger = logger.WithName(vsphereCluster.APIVersion)
74-
75-
// Fetch the Cluster.
76-
cluster, err := clusterutilv1.GetOwnerCluster(parentContext, r.Client, vsphereCluster.ObjectMeta)
77-
if err != nil {
78-
return reconcile.Result{}, err
79-
}
80-
if cluster == nil {
81-
logger.Info("Waiting for Cluster Controller to set OwnerRef on VSphereCluster")
82-
return reconcile.Result{RequeueAfter: config.DefaultRequeue}, nil
83-
}
84-
85-
logger = logger.WithName(fmt.Sprintf("cluster=%s", cluster.Name))
86-
87-
// Create the context.
88-
ctx, err := context.NewClusterContext(&context.ClusterContextParams{
89-
Context: parentContext,
90-
Cluster: cluster,
91-
VSphereCluster: vsphereCluster,
92-
Client: r.Client,
93-
Logger: logger,
94-
})
95-
if err != nil {
96-
return reconcile.Result{}, errors.Errorf("failed to create cluster context: %+v", err)
58+
ctx, result, reterr := controllerutil.GetClusterContext(req, logger, r)
59+
if ctx == nil {
60+
return result, reterr
9761
}
98-
62+
vsphereCluster := ctx.VSphereCluster
9963
// Always close the context when exiting this function so we can persist any VSphereCluster changes.
10064
defer func() {
10165
if err := ctx.Patch(); err != nil && reterr == nil {
@@ -132,14 +96,14 @@ func (r *VSphereClusterReconciler) reconcileNormal(ctx *context.ClusterContext)
13296
"cluster-namespace", ctx.VSphereCluster.Namespace,
13397
"cluster-name", ctx.VSphereCluster.Name)
13498
}
135-
136-
// Update the VSphereCluster resource with its API enpoints.
137-
if err := r.reconcileAPIEndpoints(ctx); err != nil {
138-
return reconcile.Result{}, errors.Wrapf(err,
139-
"failed to reconcile API endpoints for VSphereCluster %s/%s",
140-
ctx.VSphereCluster.Namespace, ctx.VSphereCluster.Name)
99+
if ctx.VSphereCluster.Spec.LoadBalancerConfiguration == nil {
100+
// Update the VSphereCluster resource with its API enpoints.
101+
if err := r.reconcileAPIEndpoints(ctx); err != nil {
102+
return reconcile.Result{}, errors.Wrapf(err,
103+
"failed to reconcile API endpoints for VSphereCluster %s/%s",
104+
ctx.VSphereCluster.Namespace, ctx.VSphereCluster.Name)
105+
}
141106
}
142-
143107
// If the VSphereCluster resource has no API endpoints set then requeue
144108
// until an API endpoint for the cluster resource can be found.
145109
if len(ctx.VSphereCluster.Status.APIEndpoints) == 0 {

main.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,9 @@ func main() {
8383
"Bind address to expose the pprof profiler")
8484
syncPeriod := flag.Duration("sync-period", defaultSyncPeriod,
8585
"The interval at which cluster-api objects are synchronized")
86+
loadBalancingProvider := flag.String(
87+
"loadbalancer-provider", "",
88+
"the name of the load balancer provider")
8689
flag.DurationVar(&config.DefaultRequeue, "requeue-period", defaultRequeuePeriod,
8790
"The default amount of time to wait before an operation is requeued.")
8891
flag.Parse()
@@ -129,6 +132,16 @@ func main() {
129132
setupLog.Error(err, "unable to create controller", "controller", "VSphereCluster")
130133
os.Exit(1)
131134
}
135+
if loadBalancingProvider != nil {
136+
if err = (&controllers.LoadBalancerReconciler{
137+
Client: mgr.GetClient(),
138+
Log: ctrl.Log.WithName("controllers").WithName("loadBalancer"),
139+
Recorder: mgr.GetEventRecorderFor("loadBalancer-controller"),
140+
}).SetupWithManager(mgr); err != nil {
141+
setupLog.Error(err, "unable to create controller", "controller", "loadBalancer")
142+
os.Exit(1)
143+
}
144+
}
132145
// +kubebuilder:scaffold:builder
133146

134147
setupLog.Info("starting manager")

pkg/cloud/vsphere/util/machines.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ func GetMachinesInCluster(
6464
func GetVSphereMachinesInCluster(
6565
ctx context.Context,
6666
controllerClient client.Client,
67-
namespace, clusterName string) ([]*infrav1.VSphereMachine, error) {
67+
namespace, clusterName string) (map[string]*infrav1.VSphereMachine, error) {
6868

6969
labels := map[string]string{clusterv1.MachineClusterLabelName: clusterName}
7070
machineList := &infrav1.VSphereMachineList{}
@@ -76,9 +76,10 @@ func GetVSphereMachinesInCluster(
7676
return nil, err
7777
}
7878

79-
machines := make([]*infrav1.VSphereMachine, len(machineList.Items))
79+
machines := make(map[string]*infrav1.VSphereMachine, len(machineList.Items))
8080
for i := range machineList.Items {
81-
machines[i] = &machineList.Items[i]
81+
machineName := machineList.Items[i].Name
82+
machines[machineName] = &machineList.Items[i]
8283
}
8384

8485
return machines, nil

vendor/github.com/mattn/go-isatty/go.mod

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

vendor/github.com/mattn/go-isatty/isatty_tcgets.go

Lines changed: 19 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)