@@ -21,6 +21,7 @@ import (
2121	"fmt" 
2222	"net/url" 
2323	"strconv" 
24+ 	"strings" 
2425
2526	"github.com/pkg/errors" 
2627
@@ -29,6 +30,7 @@ import (
2930	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 
3031	corev1 "k8s.io/client-go/kubernetes/typed/core/v1" 
3132	"k8s.io/klog/klogr" 
33+ 	elb "sigs.k8s.io/cluster-api-provider-vsphere/pkg/cloud/vsphere/services/aws" 
3234	clusterv1 "sigs.k8s.io/cluster-api/pkg/apis/cluster/v1alpha1" 
3335	clientv1 "sigs.k8s.io/cluster-api/pkg/client/clientset_generated/clientset/typed/cluster/v1alpha1" 
3436	clusterErr "sigs.k8s.io/cluster-api/pkg/controller/error" 
@@ -41,6 +43,7 @@ import (
4143	"sigs.k8s.io/cluster-api-provider-vsphere/pkg/cloud/vsphere/context" 
4244	"sigs.k8s.io/cluster-api-provider-vsphere/pkg/cloud/vsphere/services/certificates" 
4345	"sigs.k8s.io/cluster-api-provider-vsphere/pkg/cloud/vsphere/services/kubeclient" 
46+ 	"sigs.k8s.io/cluster-api-provider-vsphere/pkg/cloud/vsphere/services/kubeconfig" 
4447)
4548
4649//+kubebuilder:rbac:groups=vsphere.cluster.k8s.io,resources=vsphereclusterproviderspecs;vsphereclusterproviderstatuses,verbs=get;list;watch;create;update;patch;delete 
@@ -91,11 +94,22 @@ func (a *Actuator) Reconcile(cluster *clusterv1.Cluster) (opErr error) {
9194		return  err 
9295	}
9396
94- 	if  err  :=  a .reconcileCloudConfigSecret (ctx ); err  !=  nil  {
97+ 	isVMwareCloud  :=  ctx .ClusterConfig .VmwareCloud  !=  nil 
98+ 	if  isVMwareCloud  {
99+ 		if  err  =  a .reconcileLoadBalancers (ctx ); err  !=  nil  {
100+ 			return  err 
101+ 		}
102+ 	}
103+ 
104+ 	if  err  :=  a .reconcileKubeConfig (ctx ); err  !=  nil  {
95105		return  err 
96106	}
97107
98- 	if  err  :=  a .reconcileReadyState (ctx ); err  !=  nil  {
108+ 	if  err  :=  a .reconcileReadyState (ctx , isVMwareCloud ); err  !=  nil  {
109+ 		return  err 
110+ 	}
111+ 
112+ 	if  err  :=  a .reconcileCloudConfigSecret (ctx ); err  !=  nil  {
99113		return  err 
100114	}
101115
@@ -130,6 +144,13 @@ func (a *Actuator) Delete(cluster *clusterv1.Cluster) (opErr error) {
130144		return  err 
131145	}
132146
147+ 	// Delete the Load balancer if we are using VMC 
148+ 	if  ctx .ClusterConfig .VmwareCloud  !=  nil  {
149+ 		if  err  :=  a .deleteLoadBalancer (ctx ); err  !=  nil  {
150+ 			return  err 
151+ 		}
152+ 	}
153+ 
133154	return  nil 
134155}
135156
@@ -140,7 +161,7 @@ func (a *Actuator) reconcilePKI(ctx *context.ClusterContext) error {
140161	return  nil 
141162}
142163
143- func  (a  * Actuator ) reconcileReadyState (ctx  * context.ClusterContext ) error  {
164+ func  (a  * Actuator ) reconcileReadyState (ctx  * context.ClusterContext ,  isVMwareCloud   bool ) error  {
144165
145166	// Always recalculate the API Endpoints. 
146167	ctx .Cluster .Status .APIEndpoints  =  []clusterv1.APIEndpoint {}
@@ -163,45 +184,45 @@ func (a *Actuator) reconcileReadyState(ctx *context.ClusterContext) error {
163184		ctx .Logger .Error (err , "unable to list nodes for target cluster" )
164185		return  & clusterErr.RequeueAfterError {RequeueAfter : config .DefaultRequeue }
165186	}
187+ 	if  isVMwareCloud  {
188+ 		// Get the RESTConfig in order to parse its Host to use as the control plane 
189+ 		// endpoint to add to the Cluster's API endpoints. 
190+ 		restConfig  :=  client .RESTConfig ()
191+ 		if  restConfig  ==  nil  {
192+ 			ctx .Logger .Error (errors .New ("restConfig == nil" ), "error getting RESTConfig for kube client" )
193+ 			return  & clusterErr.RequeueAfterError {RequeueAfter : config .DefaultRequeue }
194+ 		}
166195
167- 	// Get the RESTConfig in order to parse its Host to use as the control plane 
168- 	// endpoint to add to the Cluster's API endpoints. 
169- 	restConfig  :=  client .RESTConfig ()
170- 	if  restConfig  ==  nil  {
171- 		ctx .Logger .Error (errors .New ("restConfig == nil" ), "error getting RESTConfig for kube client" )
172- 		return  & clusterErr.RequeueAfterError {RequeueAfter : config .DefaultRequeue }
173- 	}
174- 
175- 	// Calculate the API endpoint for the cluster. 
176- 	controlPlaneEndpointURL , err  :=  url .Parse (restConfig .Host )
177- 	if  err  !=  nil  {
178- 		return  errors .Wrapf (err , "unable to parse cluster's restConifg host value: %v" , restConfig .Host )
179- 	}
196+ 		// Calculate the API endpoint for the cluster. 
197+ 		controlPlaneEndpointURL , err  :=  url .Parse (restConfig .Host )
198+ 		if  err  !=  nil  {
199+ 			return  errors .Wrapf (err , "unable to parse cluster's restConifg host value: %v" , restConfig .Host )
200+ 		}
180201
181- 	// The API endpoint may just have a host. 
182- 	apiEndpoint  :=  clusterv1.APIEndpoint {
183- 		Host : controlPlaneEndpointURL .Hostname (),
184- 	}
202+ 		 // The API endpoint may just have a host. 
203+ 		 apiEndpoint  :=  clusterv1.APIEndpoint {
204+ 			 Host : controlPlaneEndpointURL .Hostname (),
205+ 		 }
185206
186- 	// Check to see if there is also a port. 
187- 	if  szPort  :=  controlPlaneEndpointURL .Port (); szPort  !=  ""  {
188- 		port , err  :=  strconv .Atoi (szPort )
189- 		if  err  !=  nil  {
190- 			return  errors .Wrapf (err , "unable to get parse host and port for control plane endpoint %q for %q" , controlPlaneEndpointURL .Host , ctx )
207+ 		// Check to see if there is also a port. 
208+ 		if  szPort  :=  controlPlaneEndpointURL .Port (); szPort  !=  ""  {
209+ 			port , err  :=  strconv .Atoi (szPort )
210+ 			if  err  !=  nil  {
211+ 				return  errors .Wrapf (err , "unable to get parse host and port for control plane endpoint %q for %q" , controlPlaneEndpointURL .Host , ctx )
212+ 			}
213+ 			apiEndpoint .Port  =  port 
191214		}
192- 		apiEndpoint .Port  =  port 
193- 	}
194215
195- 	// Update the API endpoints. 
196- 	ctx .Cluster .Status .APIEndpoints  =  []clusterv1.APIEndpoint {apiEndpoint }
197- 	ctx .Logger .V (6 ).Info ("calculated API endpoint for target cluster" , "api-endpoint-host" , apiEndpoint .Host , "api-endpoint-port" , apiEndpoint .Port )
216+ 		 // Update the API endpoints. 
217+ 		 ctx .Cluster .Status .APIEndpoints  =  []clusterv1.APIEndpoint {apiEndpoint }
218+ 		 ctx .Logger .V (6 ).Info ("calculated API endpoint for target cluster" , "api-endpoint-host" , apiEndpoint .Host , "api-endpoint-port" , apiEndpoint .Port )
198219
199- 	// Update the kubeadm control plane endpoint with the one from the kubeconfig. 
200- 	if  ctx .ClusterConfig .ClusterConfiguration .ControlPlaneEndpoint  !=  controlPlaneEndpointURL .Host  {
201- 		ctx .ClusterConfig .ClusterConfiguration .ControlPlaneEndpoint  =  controlPlaneEndpointURL .Host 
202- 		ctx .Logger .V (6 ).Info ("stored control plane endpoint in kubeadm cluster config" , "control-plane-endpoint" , controlPlaneEndpointURL .Host )
220+ 		// Update the kubeadm control plane endpoint with the one from the kubeconfig. 
221+ 		if  ctx .ClusterConfig .ClusterConfiguration .ControlPlaneEndpoint  !=  controlPlaneEndpointURL .Host  {
222+ 			ctx .ClusterConfig .ClusterConfiguration .ControlPlaneEndpoint  =  controlPlaneEndpointURL .Host 
223+ 			ctx .Logger .V (6 ).Info ("stored control plane endpoint in kubeadm cluster config" , "control-plane-endpoint" , controlPlaneEndpointURL .Host )
224+ 		}
203225	}
204- 
205226	// Update the ready status. 
206227	ctx .ClusterStatus .Ready  =  true 
207228
@@ -262,3 +283,128 @@ func (a *Actuator) deleteControlPlaneConfigMap(ctx *context.ClusterContext) erro
262283	}
263284	return  nil 
264285}
286+ 
287+ func  (a  * Actuator ) deleteLoadBalancer (ctx  * context.ClusterContext ) error  {
288+ 	elbSvc  :=  elb .New (ctx .ClusterConfig .VmwareCloud .AwsProvider .Region )
289+ 	clusterName  :=  ctx .ClusterName ()
290+ 
291+ 	if  err  :=  elbSvc .Delete (clusterName ); err  !=  nil  {
292+ 		ctx .Logger .Error (err , "cannot delete load balancers" )
293+ 		return  & clusterErr.RequeueAfterError {RequeueAfter : config .DefaultRequeue }
294+ 	}
295+ 	return  nil 
296+ }
297+ 
298+ func  (a  * Actuator ) reconcileLoadBalancers (ctx  * context.ClusterContext ) error  {
299+ 
300+ 	ctx .Logger .V (2 ).Info ("Reconciling load balancers" )
301+ 
302+ 	clusterName  :=  ctx .ClusterName ()
303+ 	controlPlaneMachines , err  :=  ctx .GetControlPlaneMachines ()
304+ 	if  err  !=  nil  {
305+ 		ctx .Logger .Error (err , "error getting control plane machines" )
306+ 		return  & clusterErr.RequeueAfterError {RequeueAfter : config .DefaultRequeue }
307+ 	}
308+ 	controlPlaneIPs  :=  []string {}
309+ 	for  _ , controlPlaneMachine  :=  range  controlPlaneMachines  {
310+ 		for  _ , nodeAddress  :=  range  controlPlaneMachine .Status .Addresses  {
311+ 			if  nodeAddress .Type  ==  apiv1 .NodeExternalIP  ||  nodeAddress .Type  ==  apiv1 .NodeInternalIP  {
312+ 				controlPlaneIPs  =  append (controlPlaneIPs , nodeAddress .Address )
313+ 				break 
314+ 			}
315+ 		}
316+ 	}
317+ 
318+ 	awsProviderInfo  :=  ctx .ClusterConfig .VmwareCloud .AwsProvider 
319+ 	elbSvc  :=  elb .New (awsProviderInfo .Region )
320+ 	vpcID  :=  awsProviderInfo .VpcID 
321+ 	subnets  :=  awsProviderInfo .Subnets 
322+ 	loadBalancerDNS , loadBalancerPort , err  :=  elbSvc .Reconcile (vpcID , controlPlaneIPs , clusterName , subnets )
323+ 	if  err  !=  nil  {
324+ 		ctx .Logger .Error (err , "cannot reconcile load balancer" )
325+ 		return  & clusterErr.RequeueAfterError {RequeueAfter : config .DefaultRequeue }
326+ 	}
327+ 
328+ 	apiEndpoint  :=  clusterv1.APIEndpoint {
329+ 		Host : loadBalancerDNS ,
330+ 		Port : loadBalancerPort ,
331+ 	}
332+ 
333+ 	// Update the API endpoints. 
334+ 	ctx .Cluster .Status .APIEndpoints  =  []clusterv1.APIEndpoint {apiEndpoint }
335+ 	ctx .Logger .V (6 ).Info ("calculated API endpoint for target cluster" , "api-endpoint-host" , apiEndpoint .Host , "api-endpoint-port" , apiEndpoint .Port )
336+ 	controlPlaneEndpointURL  :=  strings .Join ([]string {apiEndpoint .Host , strconv .Itoa (apiEndpoint .Port )}, ":" )
337+ 	// Update the kubeadm control plane endpoint with the one from the kubeconfig. 
338+ 	if  ctx .ClusterConfig .ClusterConfiguration .ControlPlaneEndpoint  !=  controlPlaneEndpointURL  {
339+ 		ctx .ClusterConfig .ClusterConfiguration .ControlPlaneEndpoint  =  controlPlaneEndpointURL 
340+ 		ctx .Logger .V (6 ).Info ("stored control plane endpoint in kubeadm cluster config" , "control-plane-endpoint" , controlPlaneEndpointURL )
341+ 	}
342+ 
343+ 	return  nil 
344+ 
345+ }
346+ 
347+ // reconcileKubeConfig creates a secret on the management cluster with 
348+ // the kubeconfig for target cluster. 
349+ func  (a  * Actuator ) reconcileKubeConfig (ctx  * context.ClusterContext ) error  {
350+ 
351+ 	// Get the control plane endpoint. 
352+ 	controlPlaneEndpoint , err  :=  ctx .ControlPlaneEndpoint ()
353+ 	if  err  !=  nil  {
354+ 		ctx .Logger .Error (err , "requeueing until control plane endpoint is available" )
355+ 		return  & clusterErr.RequeueAfterError {RequeueAfter : config .DefaultRequeue }
356+ 	}
357+ 
358+ 	// Create a new kubeconfig for the target cluster. 
359+ 	ctx .Logger .V (6 ).Info ("generating kubeconfig secret" , "controlPlaneEndpoint" , controlPlaneEndpoint )
360+ 	kubeConfig , err  :=  kubeconfig .New (ctx .Cluster .Name , controlPlaneEndpoint , ctx .ClusterConfig .CAKeyPair )
361+ 	if  err  !=  nil  {
362+ 		return  errors .Wrapf (err , "error generating kubeconfig for %q" , ctx )
363+ 	}
364+ 
365+ 	// Define the kubeconfig secret for the target cluster. 
366+ 	secret  :=  & apiv1.Secret {
367+ 		ObjectMeta : metav1.ObjectMeta {
368+ 			Namespace : ctx .Cluster .Namespace ,
369+ 			Name :      remotev1 .KubeConfigSecretName (ctx .Cluster .Name ),
370+ 			OwnerReferences : []metav1.OwnerReference {
371+ 				{
372+ 					APIVersion : ctx .Cluster .APIVersion ,
373+ 					Kind :       ctx .Cluster .Kind ,
374+ 					Name :       ctx .Cluster .Name ,
375+ 					UID :        ctx .Cluster .UID ,
376+ 				},
377+ 			},
378+ 		},
379+ 		StringData : map [string ]string {
380+ 			"value" : kubeConfig ,
381+ 		},
382+ 	}
383+ 	ctx .Logger .V (6 ).Info ("computed kubeconfig" , "kubeconfig" , kubeConfig )
384+ 	if  exstingSecret , err  :=  a .coreClient .Secrets (ctx .Cluster .Namespace ).Get (secret .Name , metav1.GetOptions {}); err  !=  nil  {
385+ 		if  apierrors .IsNotFound (err ) {
386+ 			if  _ , err  :=  a .coreClient .Secrets (ctx .Cluster .Namespace ).Create (secret ); err  !=  nil  {
387+ 				if  ! apierrors .IsAlreadyExists (err ) {
388+ 					ctx .Logger .Error (err , "error creating kubeconfig secret" )
389+ 					return  & clusterErr.RequeueAfterError {RequeueAfter : config .DefaultRequeue }
390+ 				}
391+ 				ctx .Logger .V (6 ).Info ("kubeconfig secret already exists" )
392+ 			} else  {
393+ 				ctx .Logger .V (4 ).Info ("created kubeconfig secret" )
394+ 			}
395+ 		} else  {
396+ 			ctx .Logger .Error (err , "cannot get the kubeconfig secret" )
397+ 			return  & clusterErr.RequeueAfterError {RequeueAfter : config .DefaultRequeue }
398+ 		}
399+ 	} else  {
400+ 		secret .ResourceVersion  =  exstingSecret .ResourceVersion 
401+ 		updatedKubeConfig , err  :=  a .coreClient .Secrets (ctx .Cluster .Namespace ).Update (secret )
402+ 		if  err  !=  nil  {
403+ 			ctx .Logger .Error (err , "cannot update the existing kubeconfig" )
404+ 			return  & clusterErr.RequeueAfterError {RequeueAfter : config .DefaultRequeue }
405+ 		}
406+ 		ctx .Logger .V (6 ).Info ("updated kubeconfig" , "kubeconfig" , updatedKubeConfig )
407+ 	}
408+ 	// Create the kubeconfig secret. 
409+ 	return  nil 
410+ }
0 commit comments