Skip to content

Commit 2a91b39

Browse files
authored
Merge pull request #573 from hidekazuna/discover_external
✨Add external network discovery
2 parents 50680e7 + 9f029c5 commit 2a91b39

File tree

10 files changed

+241
-130
lines changed

10 files changed

+241
-130
lines changed

api/v1alpha3/openstackcluster_types.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ type OpenStackClusterSpec struct {
5959
ExternalRouterIPs []ExternalRouterIPParam `json:"externalRouterIPs,omitempty"`
6060
// ExternalNetworkID is the ID of an external OpenStack Network. This is necessary
6161
// to get public internet to the VMs.
62+
// +optional
6263
ExternalNetworkID string `json:"externalNetworkId,omitempty"`
6364

6465
// UseOctavia is weather LoadBalancer Service is Octavia or not
@@ -129,6 +130,9 @@ type OpenStackClusterStatus struct {
129130
// It includes Subnets and Router.
130131
Network *Network `json:"network,omitempty"`
131132

133+
// External Network contains information about the created OpenStack external network.
134+
ExternalNetwork *Network `json:"externalNetwork,omitempty"`
135+
132136
// FailureDomains represent OpenStack availability zones
133137
FailureDomains clusterv1.FailureDomains `json:"failureDomains,omitempty"`
134138

api/v1alpha3/zz_generated.deepcopy.go

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

config/crd/bases/infrastructure.cluster.x-k8s.io_openstackclusters.yaml

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -412,6 +412,63 @@ spec:
412412
- name
413413
- rules
414414
type: object
415+
externalNetwork:
416+
description: External Network contains information about the created
417+
OpenStack external network.
418+
properties:
419+
apiServerLoadBalancer:
420+
description: Be careful when using APIServerLoadBalancer, because
421+
this field is optional and therefore not set in all cases
422+
properties:
423+
id:
424+
type: string
425+
internalIP:
426+
type: string
427+
ip:
428+
type: string
429+
name:
430+
type: string
431+
required:
432+
- id
433+
- internalIP
434+
- ip
435+
- name
436+
type: object
437+
id:
438+
type: string
439+
name:
440+
type: string
441+
router:
442+
description: Router represents basic information about the associated
443+
OpenStack Neutron Router
444+
properties:
445+
id:
446+
type: string
447+
name:
448+
type: string
449+
required:
450+
- id
451+
- name
452+
type: object
453+
subnet:
454+
description: Subnet represents basic information about the associated
455+
OpenStack Neutron Subnet
456+
properties:
457+
cidr:
458+
type: string
459+
id:
460+
type: string
461+
name:
462+
type: string
463+
required:
464+
- cidr
465+
- id
466+
- name
467+
type: object
468+
required:
469+
- id
470+
- name
471+
type: object
415472
failureDomains:
416473
additionalProperties:
417474
description: FailureDomainSpec is the Schema for Cluster API failure

controllers/openstackcluster_controller.go

Lines changed: 35 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -214,35 +214,6 @@ func (r *OpenStackClusterReconciler) reconcileNormal(ctx context.Context, log lo
214214
return reconcile.Result{}, err
215215
}
216216

217-
// Set APIEndpoints so the Cluster API Cluster Controller can pull them
218-
if openStackCluster.Spec.ManagedAPIServerLoadBalancer {
219-
openStackCluster.Spec.ControlPlaneEndpoint = clusterv1.APIEndpoint{
220-
Host: openStackCluster.Spec.APIServerLoadBalancerFloatingIP,
221-
Port: int32(openStackCluster.Spec.APIServerLoadBalancerPort),
222-
}
223-
} else {
224-
controlPlaneMachine, err := getControlPlaneMachine(r.Client)
225-
if err != nil {
226-
return ctrl.Result{}, errors.Errorf("failed to get control plane machine: %v", err)
227-
}
228-
if controlPlaneMachine != nil {
229-
var apiPort int
230-
if cluster.Spec.ClusterNetwork.APIServerPort == nil {
231-
log.Info("No API endpoint given, default to 6443")
232-
apiPort = 6443
233-
} else {
234-
apiPort = int(*cluster.Spec.ClusterNetwork.APIServerPort)
235-
}
236-
237-
openStackCluster.Spec.ControlPlaneEndpoint = clusterv1.APIEndpoint{
238-
Host: controlPlaneMachine.Spec.FloatingIP,
239-
Port: int32(apiPort),
240-
}
241-
} else {
242-
log.Info("No control plane node found yet, could not write OpenStackCluster.Spec.ControlPlaneEndpoint")
243-
}
244-
}
245-
246217
availabilityZones, err := computeService.GetAvailabilityZones()
247218
if err != nil {
248219
return ctrl.Result{}, err
@@ -292,6 +263,12 @@ func (r *OpenStackClusterReconciler) reconcileNetworkComponents(log logr.Logger,
292263
}
293264

294265
log.Info("Reconciling network components")
266+
267+
err = networkingService.ReconcileExternalNetwork(openStackCluster)
268+
if err != nil {
269+
return errors.Errorf("failed to reconcile external network: %v", err)
270+
}
271+
295272
if openStackCluster.Spec.NodeCIDR == "" {
296273
log.V(4).Info("No need to reconcile network, searching network and subnet instead")
297274

@@ -337,6 +314,35 @@ func (r *OpenStackClusterReconciler) reconcileNetworkComponents(log logr.Logger,
337314
}
338315
}
339316

317+
if openStackCluster.Spec.ControlPlaneEndpoint.IsZero() {
318+
var controlPlaneEndpointHost string
319+
var port int32
320+
if openStackCluster.Spec.ManagedAPIServerLoadBalancer {
321+
controlPlaneEndpointHost = openStackCluster.Spec.APIServerLoadBalancerFloatingIP
322+
if openStackCluster.Spec.APIServerLoadBalancerPort == 0 {
323+
port = 6443
324+
} else {
325+
port = int32(openStackCluster.Spec.APIServerLoadBalancerPort)
326+
}
327+
} else {
328+
controlPlaneEndpointHost = openStackCluster.Spec.ControlPlaneEndpoint.Host
329+
if openStackCluster.Spec.ControlPlaneEndpoint.Port == 0 {
330+
port = 6443
331+
} else {
332+
port = openStackCluster.Spec.ControlPlaneEndpoint.Port
333+
}
334+
}
335+
fp, err := networkingService.GetOrCreateFloatingIP(openStackCluster, controlPlaneEndpointHost)
336+
if err != nil {
337+
return errors.Errorf("Floating IP cannot be got or created: %v", err)
338+
}
339+
// Set APIEndpoints so the Cluster API Cluster Controller can pull them
340+
openStackCluster.Spec.ControlPlaneEndpoint = clusterv1.APIEndpoint{
341+
Host: fp.FloatingIP,
342+
Port: port,
343+
}
344+
}
345+
340346
if openStackCluster.Spec.ManagedAPIServerLoadBalancer {
341347
err = loadBalancerService.ReconcileLoadBalancer(clusterName, openStackCluster)
342348
if err != nil {
@@ -359,33 +365,3 @@ func (r *OpenStackClusterReconciler) SetupWithManager(mgr ctrl.Manager, options
359365
WithEventFilter(pausePredicates).
360366
Complete(r)
361367
}
362-
363-
func getControlPlaneMachine(client client.Client) (*infrav1.OpenStackMachine, error) {
364-
machines := &clusterv1.MachineList{}
365-
if err := client.List(context.TODO(), machines); err != nil {
366-
return nil, err
367-
}
368-
openStackMachines := &infrav1.OpenStackMachineList{}
369-
if err := client.List(context.TODO(), openStackMachines); err != nil {
370-
return nil, err
371-
}
372-
373-
var controlPlaneMachine *clusterv1.Machine
374-
for _, machine := range machines.Items {
375-
m := machine
376-
if util.IsControlPlaneMachine(&m) {
377-
controlPlaneMachine = &m
378-
break
379-
}
380-
}
381-
if controlPlaneMachine == nil {
382-
return nil, nil
383-
}
384-
385-
for _, openStackMachine := range openStackMachines.Items {
386-
if openStackMachine.Name == controlPlaneMachine.Name {
387-
return &openStackMachine, nil
388-
}
389-
}
390-
return nil, nil
391-
}

controllers/openstackmachine_controller.go

Lines changed: 11 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -335,20 +335,23 @@ func (r *OpenStackMachineReconciler) reconcileNormal(ctx context.Context, logger
335335
return ctrl.Result{}, nil
336336
}
337337

338-
if openStackMachine.Spec.FloatingIP != "" {
339-
err = r.reconcileFloatingIP(computeService, networkingService, instance, openStackMachine, openStackCluster)
340-
if err != nil {
341-
handleUpdateMachineError(logger, openStackMachine, errors.Errorf("FloatingIP cannot be reconciled: %v", err))
342-
return ctrl.Result{}, nil
343-
}
344-
}
345-
346338
if openStackCluster.Spec.ManagedAPIServerLoadBalancer {
347339
err = r.reconcileLoadBalancerMember(logger, osProviderClient, clientOpts, instance, clusterName, machine, openStackMachine, openStackCluster)
348340
if err != nil {
349341
handleUpdateMachineError(logger, openStackMachine, errors.Errorf("LoadBalancerMember cannot be reconciled: %v", err))
350342
return ctrl.Result{}, nil
351343
}
344+
} else if util.IsControlPlaneMachine(machine) {
345+
fp, err := networkingService.GetOrCreateFloatingIP(openStackCluster, openStackCluster.Spec.ControlPlaneEndpoint.Host)
346+
if err != nil {
347+
handleUpdateMachineError(logger, openStackMachine, errors.Errorf("Floating IP cannot be got or created: %v", err))
348+
return ctrl.Result{}, nil
349+
}
350+
err = computeService.AssociateFloatingIP(instance.ID, fp.FloatingIP)
351+
if err != nil {
352+
handleUpdateMachineError(logger, openStackMachine, errors.Errorf("Floating IP cannot be associated: %v", err))
353+
return ctrl.Result{}, nil
354+
}
352355
}
353356

354357
logger.Info("Reconciled Machine create successfully")
@@ -404,19 +407,6 @@ func getTimeout(name string, timeout int) time.Duration {
404407
return time.Duration(timeout)
405408
}
406409

407-
func (r *OpenStackMachineReconciler) reconcileFloatingIP(computeService *compute.Service, networkingService *networking.Service, instance *compute.Instance, openStackMachine *infrav1.OpenStackMachine, openStackCluster *infrav1.OpenStackCluster) error {
408-
err := networkingService.CreateFloatingIPIfNecessary(openStackCluster, openStackMachine.Spec.FloatingIP)
409-
if err != nil {
410-
return fmt.Errorf("error creating floatingIP: %v", err)
411-
}
412-
413-
err = computeService.AssociateFloatingIP(instance.ID, openStackMachine.Spec.FloatingIP)
414-
if err != nil {
415-
return fmt.Errorf("error associating floatingIP: %v", err)
416-
}
417-
return nil
418-
}
419-
420410
func (r *OpenStackMachineReconciler) reconcileLoadBalancerMember(logger logr.Logger, osProviderClient *gophercloud.ProviderClient, clientOpts *clientconfig.ClientOpts, instance *compute.Instance, clusterName string, machine *clusterv1.Machine, openStackMachine *infrav1.OpenStackMachine, openStackCluster *infrav1.OpenStackCluster) error {
421411
ip, err := getIPFromInstance(instance)
422412
if err != nil {

pkg/cloud/services/loadbalancer/loadbalancer.go

Lines changed: 34 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -30,25 +30,13 @@ import (
3030
"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/layer3/floatingips"
3131
"k8s.io/apimachinery/pkg/util/wait"
3232
infrav1 "sigs.k8s.io/cluster-api-provider-openstack/api/v1alpha3"
33+
"sigs.k8s.io/cluster-api-provider-openstack/pkg/record"
3334
clusterv1 "sigs.k8s.io/cluster-api/api/v1alpha3"
3435
"sigs.k8s.io/cluster-api/util"
3536
)
3637

3738
func (s *Service) ReconcileLoadBalancer(clusterName string, openStackCluster *infrav1.OpenStackCluster) error {
3839

39-
if openStackCluster.Spec.ExternalNetworkID == "" {
40-
s.logger.V(3).Info("No need to create loadbalancer, due to missing ExternalNetworkID")
41-
return nil
42-
}
43-
if openStackCluster.Spec.APIServerLoadBalancerFloatingIP == "" {
44-
s.logger.V(3).Info("No need to create loadbalancer, due to missing APIServerLoadBalancerFloatingIP")
45-
return nil
46-
}
47-
if openStackCluster.Spec.APIServerLoadBalancerPort == 0 {
48-
s.logger.V(3).Info("No need to create loadbalancer, due to missing APIServerLoadBalancerPort")
49-
return nil
50-
}
51-
5240
loadBalancerName := fmt.Sprintf("%s-cluster-%s-%s", networkPrefix, clusterName, kubeapiLBSuffix)
5341
s.logger.Info("Reconciling loadbalancer", "name", loadBalancerName)
5442

@@ -73,39 +61,26 @@ func (s *Service) ReconcileLoadBalancer(clusterName string, openStackCluster *in
7361
return err
7462
}
7563

76-
// floating ip
77-
fp, err := checkIfFloatingIPExists(s.networkingClient, openStackCluster.Spec.APIServerLoadBalancerFloatingIP)
64+
fp, err := getOrCreateFloatingIP(s.networkingClient, openStackCluster, openStackCluster.Spec.ControlPlaneEndpoint.Host)
7865
if err != nil {
7966
return err
8067
}
81-
if fp == nil {
82-
s.logger.Info("Creating floating ip", "ip", openStackCluster.Spec.APIServerLoadBalancerFloatingIP)
83-
fpCreateOpts := &floatingips.CreateOpts{
84-
FloatingIP: openStackCluster.Spec.APIServerLoadBalancerFloatingIP,
85-
FloatingNetworkID: openStackCluster.Spec.ExternalNetworkID,
86-
}
87-
fp, err = floatingips.Create(s.networkingClient, fpCreateOpts).Extract()
88-
if err != nil {
89-
return fmt.Errorf("error allocating floating IP: %s", err)
90-
}
91-
}
92-
93-
// associate floating ip
94-
s.logger.Info("Associating floating ip", "ip", openStackCluster.Spec.APIServerLoadBalancerFloatingIP)
68+
s.logger.Info("Associating floating ip", "ip", fp.FloatingIP)
9569
fpUpdateOpts := &floatingips.UpdateOpts{
9670
PortID: &lb.VipPortID,
9771
}
9872
fp, err = floatingips.Update(s.networkingClient, fp.ID, fpUpdateOpts).Extract()
9973
if err != nil {
100-
return fmt.Errorf("error allocating floating IP: %s", err)
74+
return fmt.Errorf("error associating floating IP: %s", err)
10175
}
10276
err = waitForFloatingIP(s.logger, s.networkingClient, fp.ID, "ACTIVE")
10377
if err != nil {
10478
return err
10579
}
80+
record.Eventf(openStackCluster, "SuccessfulAssociateFloatingIP", "Associate floating IP %s with port %s", fp.FloatingIP, &lb.VipPortID)
10681

10782
// lb listener
108-
portList := []int{openStackCluster.Spec.APIServerLoadBalancerPort}
83+
portList := []int{int(openStackCluster.Spec.ControlPlaneEndpoint.Port)}
10984
portList = append(portList, openStackCluster.Spec.APIServerLoadBalancerAdditionalPorts...)
11085
for _, port := range portList {
11186
lbPortObjectsName := fmt.Sprintf("%s-%d", loadBalancerName, port)
@@ -212,7 +187,7 @@ func (s *Service) ReconcileLoadBalancerMember(clusterName string, machine *clust
212187
lbID := openStackCluster.Status.Network.APIServerLoadBalancer.ID
213188
subnetID := openStackCluster.Status.Network.Subnet.ID
214189

215-
portList := []int{openStackCluster.Spec.APIServerLoadBalancerPort}
190+
portList := []int{int(openStackCluster.Spec.ControlPlaneEndpoint.Port)}
216191
portList = append(portList, openStackCluster.Spec.APIServerLoadBalancerAdditionalPorts...)
217192
for _, port := range portList {
218193
lbPortObjectsName := fmt.Sprintf("%s-%d", loadBalancerName, port)
@@ -415,7 +390,7 @@ func (s *Service) DeleteLoadBalancerMember(clusterName string, machine *clusterv
415390

416391
lbID := openStackCluster.Status.Network.APIServerLoadBalancer.ID
417392

418-
portList := []int{openStackCluster.Spec.APIServerLoadBalancerPort}
393+
portList := []int{int(openStackCluster.Spec.ControlPlaneEndpoint.Port)}
419394
portList = append(portList, openStackCluster.Spec.APIServerLoadBalancerAdditionalPorts...)
420395
for _, port := range portList {
421396
lbPortObjectsName := fmt.Sprintf("%s-%d", loadBalancerName, port)
@@ -470,6 +445,32 @@ func checkIfLbExists(client *gophercloud.ServiceClient, name string) (*loadbalan
470445
return &lbList[0], nil
471446
}
472447

448+
func getOrCreateFloatingIP(client *gophercloud.ServiceClient, openStackCluster *infrav1.OpenStackCluster, ip string) (*floatingips.FloatingIP, error) {
449+
var fp *floatingips.FloatingIP
450+
var err error
451+
if ip != "" {
452+
fp, err = checkIfFloatingIPExists(client, ip)
453+
if err != nil {
454+
return nil, err
455+
}
456+
}
457+
if fp == nil {
458+
fpCreateOpts := &floatingips.CreateOpts{
459+
FloatingNetworkID: openStackCluster.Status.ExternalNetwork.ID,
460+
}
461+
if ip != "" {
462+
fpCreateOpts.FloatingIP = ip
463+
}
464+
fp, err = floatingips.Create(client, fpCreateOpts).Extract()
465+
if err != nil {
466+
return nil, fmt.Errorf("error creating floating IP: %s", err)
467+
}
468+
record.Eventf(openStackCluster, "SuccessfulCreateFloatingIP", "Created floating IP %s with id %s", fp.FloatingIP, fp.ID)
469+
470+
}
471+
return fp, nil
472+
}
473+
473474
func checkIfFloatingIPExists(client *gophercloud.ServiceClient, ip string) (*floatingips.FloatingIP, error) {
474475
allPages, err := floatingips.List(client, floatingips.ListOpts{FloatingIP: ip}).AllPages()
475476
if err != nil {

0 commit comments

Comments
 (0)