diff --git a/pkg/cloud/openstack/machine/actuator.go b/pkg/cloud/openstack/machine/actuator.go index db824f991a..80e801486c 100644 --- a/pkg/cloud/openstack/machine/actuator.go +++ b/pkg/cloud/openstack/machine/actuator.go @@ -27,6 +27,7 @@ import ( constants "sigs.k8s.io/cluster-api-provider-openstack/pkg/cloud/openstack/contants" "sigs.k8s.io/cluster-api-provider-openstack/pkg/cloud/openstack/services/compute" "sigs.k8s.io/cluster-api-provider-openstack/pkg/cloud/openstack/services/loadbalancer" + "sigs.k8s.io/cluster-api-provider-openstack/pkg/cloud/openstack/services/networking" "sigs.k8s.io/cluster-api-provider-openstack/pkg/cloud/openstack/services/provider" "sigs.k8s.io/cluster-api-provider-openstack/pkg/cloud/openstack/services/userdata" "sigs.k8s.io/cluster-api-provider-openstack/pkg/deployer" @@ -96,6 +97,11 @@ func (a *Actuator) Create(ctx context.Context, cluster *clusterv1.Cluster, machi return err } + networkingService, err := networking.NewService(osProviderClient, clientOpts) + if err != nil { + return err + } + clusterProviderSpec, clusterProviderStatus, err := providerv1.ClusterSpecAndStatusFromProviderSpec(cluster) if err != nil { return a.handleMachineError(machine, apierrors.CreateMachine( @@ -149,7 +155,13 @@ func (a *Actuator) Create(ctx context.Context, cluster *clusterv1.Cluster, machi } if machineProviderSpec.FloatingIP != "" { - err := computeService.AssociateFloatingIP(instance.ID, machineProviderSpec.FloatingIP) + err := networkingService.GetOrCreateFloatingIP(clusterProviderSpec, machineProviderSpec.FloatingIP) + if err != nil { + return a.handleMachineError(machine, apierrors.CreateMachine( + "Create floatingIP err: %v", err)) + } + + err = computeService.AssociateFloatingIP(instance.ID, machineProviderSpec.FloatingIP) if err != nil { return a.handleMachineError(machine, apierrors.CreateMachine( "Associate floatingIP err: %v", err)) diff --git a/pkg/cloud/openstack/services/loadbalancer/loadbalancer.go b/pkg/cloud/openstack/services/loadbalancer/loadbalancer.go index 07553c1d0f..d397fd774b 100644 --- a/pkg/cloud/openstack/services/loadbalancer/loadbalancer.go +++ b/pkg/cloud/openstack/services/loadbalancer/loadbalancer.go @@ -68,16 +68,25 @@ func (s *Service) ReconcileLoadBalancer(clusterName string, clusterProviderSpec fpCreateOpts := &floatingips.CreateOpts{ FloatingIP: clusterProviderSpec.APIServerLoadBalancerFloatingIP, FloatingNetworkID: clusterProviderSpec.ExternalNetworkID, - PortID: lb.VipPortID, } fp, err = floatingips.Create(s.networkingClient, fpCreateOpts).Extract() if err != nil { return fmt.Errorf("error allocating floating IP: %s", err) } - err = waitForFloatingIP(s.networkingClient, fp.ID, "ACTIVE") - if err != nil { - return err - } + } + + // associate floating ip + klog.Infof("Associating floating ip %s", clusterProviderSpec.APIServerLoadBalancerFloatingIP) + fpUpdateOpts := &floatingips.UpdateOpts{ + PortID: &lb.VipPortID, + } + fp, err = floatingips.Update(s.networkingClient, fp.ID, fpUpdateOpts).Extract() + if err != nil { + return fmt.Errorf("error allocating floating IP: %s", err) + } + err = waitForFloatingIP(s.networkingClient, fp.ID, "ACTIVE") + if err != nil { + return err } // lb listener diff --git a/pkg/cloud/openstack/services/networking/floatingip.go b/pkg/cloud/openstack/services/networking/floatingip.go new file mode 100644 index 0000000000..757d478346 --- /dev/null +++ b/pkg/cloud/openstack/services/networking/floatingip.go @@ -0,0 +1,63 @@ +package networking + +import ( + "fmt" + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/layer3/floatingips" + "k8s.io/apimachinery/pkg/util/wait" + "k8s.io/klog" + providerv1 "sigs.k8s.io/cluster-api-provider-openstack/pkg/apis/openstackproviderconfig/v1alpha1" + "time" +) + +func (s *Service) GetOrCreateFloatingIP(clusterProviderSpec *providerv1.OpenstackClusterProviderSpec, ip string) error { + fp, err := checkIfFloatingIPExists(s.client, ip) + if err != nil { + return err + } + if fp == nil { + klog.Infof("Creating floating ip %s", ip) + fpCreateOpts := &floatingips.CreateOpts{ + FloatingIP: ip, + FloatingNetworkID: clusterProviderSpec.ExternalNetworkID, + } + fp, err = floatingips.Create(s.client, fpCreateOpts).Extract() + if err != nil { + return fmt.Errorf("error allocating floating IP: %s", err) + } + } + return nil +} + +func checkIfFloatingIPExists(client *gophercloud.ServiceClient, ip string) (*floatingips.FloatingIP, error) { + allPages, err := floatingips.List(client, floatingips.ListOpts{FloatingIP: ip}).AllPages() + if err != nil { + return nil, err + } + fpList, err := floatingips.ExtractFloatingIPs(allPages) + if err != nil { + return nil, err + } + if len(fpList) == 0 { + return nil, nil + } + return &fpList[0], nil +} + +var backoff = wait.Backoff{ + Steps: 10, + Duration: 30 * time.Second, + Factor: 1.0, + Jitter: 0.1, +} + +func waitForFloatingIP(client *gophercloud.ServiceClient, id, target string) error { + klog.Infof("Waiting for floatingip %s to become %s.", id, target) + return wait.ExponentialBackoff(backoff, func() (bool, error) { + fp, err := floatingips.Get(client, id).Extract() + if err != nil { + return false, err + } + return fp.Status == target, nil + }) +}