Skip to content

Commit

Permalink
OpenStackFloatingIP: tag the resource in OpenStack
Browse files Browse the repository at this point in the history
  • Loading branch information
pierreprinetti committed Dec 19, 2023
1 parent 863678a commit 430aec5
Showing 1 changed file with 96 additions and 90 deletions.
186 changes: 96 additions & 90 deletions internal/controller/openstackfloatingip_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
"context"
"errors"
"fmt"
"strings"
"time"

corev1 "k8s.io/api/core/v1"
Expand Down Expand Up @@ -182,99 +183,102 @@ func (r *OpenStackFloatingIPReconciler) reconcile(ctx context.Context, networkCl
}
logger.Info("OpenStack resource found")
} else {
var networkID string
{
dependency := &openstackv1.OpenStackNetwork{}
dependencyKey := client.ObjectKey{Namespace: resource.GetNamespace(), Name: resource.Spec.Resource.FloatingNetwork}
err = r.Client.Get(ctx, dependencyKey, dependency)
if err != nil && !apierrors.IsNotFound(err) {
return ctrl.Result{}, err
}

// Dependency either doesn't exist, or is being deleted, or is not ready
if err != nil || !dependency.DeletionTimestamp.IsZero() || !conditions.IsReady(dependency) || dependency.Status.Resource.ID == "" {
logger.Info("waiting for network")

if updated, condition := conditions.SetNotReadyConditionWaiting(resource, statusPatchResource, []conditions.Dependency{
{ObjectKey: dependencyKey, Resource: "network"},
}); updated {
// Emit an event if we're setting the condition for the first time
conditions.EmitEventForCondition(r.Recorder, resource, corev1.EventTypeNormal, condition)
}
return ctrl.Result{}, nil
}
networkID = dependency.Status.Resource.ID
floatingIP, err = r.findAdoptee(log.IntoContext(ctx, logger), networkClient, resource)
if err != nil {
return ctrl.Result{}, fmt.Errorf("unable to find adoption candidates: %w", err)
}
if floatingIP != nil {
logger = logger.WithValues("OpenStackID", floatingIP.ID)
logger.Info("OpenStack resource adopted")
} else {
var networkID string
{
dependency := &openstackv1.OpenStackNetwork{}
dependencyKey := client.ObjectKey{Namespace: resource.GetNamespace(), Name: resource.Spec.Resource.FloatingNetwork}
err = r.Client.Get(ctx, dependencyKey, dependency)
if err != nil && !apierrors.IsNotFound(err) {
return ctrl.Result{}, err
}

var subnetID string
if subnetName := resource.Spec.Resource.Subnet; subnetName != "" {
dependency := &openstackv1.OpenStackSubnet{}
dependencyKey := client.ObjectKey{Namespace: resource.GetNamespace(), Name: subnetName}
err = r.Client.Get(ctx, dependencyKey, dependency)
if err != nil && !apierrors.IsNotFound(err) {
return ctrl.Result{}, err
// Dependency either doesn't exist, or is being deleted, or is not ready
if err != nil || !dependency.DeletionTimestamp.IsZero() || !conditions.IsReady(dependency) || dependency.Status.Resource.ID == "" {
logger.Info("waiting for network")

if updated, condition := conditions.SetNotReadyConditionWaiting(resource, statusPatchResource, []conditions.Dependency{
{ObjectKey: dependencyKey, Resource: "network"},
}); updated {
// Emit an event if we're setting the condition for the first time
conditions.EmitEventForCondition(r.Recorder, resource, corev1.EventTypeNormal, condition)
}
return ctrl.Result{}, nil
}
networkID = dependency.Status.Resource.ID
}

// Dependency either doesn't exist, or is being deleted, or is not ready
if err != nil || !dependency.DeletionTimestamp.IsZero() || !conditions.IsReady(dependency) || dependency.Status.Resource.ID == "" {
logger.Info("waiting for subnet")

if updated, condition := conditions.SetNotReadyConditionWaiting(resource, statusPatchResource, []conditions.Dependency{
{ObjectKey: dependencyKey, Resource: "subnet"},
}); updated {
// Emit an event if we're setting the condition for the first time
conditions.EmitEventForCondition(r.Recorder, resource, corev1.EventTypeNormal, condition)
var subnetID string
if subnetName := resource.Spec.Resource.Subnet; subnetName != "" {
dependency := &openstackv1.OpenStackSubnet{}
dependencyKey := client.ObjectKey{Namespace: resource.GetNamespace(), Name: subnetName}
err = r.Client.Get(ctx, dependencyKey, dependency)
if err != nil && !apierrors.IsNotFound(err) {
return ctrl.Result{}, err
}
return ctrl.Result{}, nil
}

subnetID = dependency.Status.Resource.ID
}
// Dependency either doesn't exist, or is being deleted, or is not ready
if err != nil || !dependency.DeletionTimestamp.IsZero() || !conditions.IsReady(dependency) || dependency.Status.Resource.ID == "" {
logger.Info("waiting for subnet")

if updated, condition := conditions.SetNotReadyConditionWaiting(resource, statusPatchResource, []conditions.Dependency{
{ObjectKey: dependencyKey, Resource: "subnet"},
}); updated {
// Emit an event if we're setting the condition for the first time
conditions.EmitEventForCondition(r.Recorder, resource, corev1.EventTypeNormal, condition)
}
return ctrl.Result{}, nil
}

var portID string
if portName := resource.Spec.Resource.Port; portName != "" {
dependency := &openstackv1.OpenStackPort{}
dependencyKey := client.ObjectKey{Namespace: resource.GetNamespace(), Name: portName}
err = r.Client.Get(ctx, dependencyKey, dependency)
if err != nil && !apierrors.IsNotFound(err) {
return ctrl.Result{}, err
subnetID = dependency.Status.Resource.ID
}

// Dependency either doesn't exist, or is being deleted, or is not ready
if err != nil || !dependency.DeletionTimestamp.IsZero() || !conditions.IsReady(dependency) || dependency.Status.Resource.ID == "" {
logger.Info("waiting for port")
var portID string
if portName := resource.Spec.Resource.Port; portName != "" {
dependency := &openstackv1.OpenStackPort{}
dependencyKey := client.ObjectKey{Namespace: resource.GetNamespace(), Name: portName}
err = r.Client.Get(ctx, dependencyKey, dependency)
if err != nil && !apierrors.IsNotFound(err) {
return ctrl.Result{}, err
}

if updated, condition := conditions.SetNotReadyConditionWaiting(resource, statusPatchResource, []conditions.Dependency{
{ObjectKey: dependencyKey, Resource: "port"},
}); updated {
// Emit an event if we're setting the condition for the first time
conditions.EmitEventForCondition(r.Recorder, resource, corev1.EventTypeNormal, condition)
// Dependency either doesn't exist, or is being deleted, or is not ready
if err != nil || !dependency.DeletionTimestamp.IsZero() || !conditions.IsReady(dependency) || dependency.Status.Resource.ID == "" {
logger.Info("waiting for port")

if updated, condition := conditions.SetNotReadyConditionWaiting(resource, statusPatchResource, []conditions.Dependency{
{ObjectKey: dependencyKey, Resource: "port"},
}); updated {
// Emit an event if we're setting the condition for the first time
conditions.EmitEventForCondition(r.Recorder, resource, corev1.EventTypeNormal, condition)
}
return ctrl.Result{}, nil
}
return ctrl.Result{}, nil
portID = dependency.Status.Resource.ID
}
portID = dependency.Status.Resource.ID
}

createOpts := floatingips.CreateOpts{
Description: resource.Spec.Resource.Description,
FloatingNetworkID: networkID,
FloatingIP: resource.Spec.Resource.FloatingIPAddress,
PortID: portID,
FixedIP: resource.Spec.Resource.FixedIPAddress,
SubnetID: subnetID,
TenantID: resource.Spec.Resource.TenantID,
ProjectID: resource.Spec.Resource.ProjectID,
}
description := orcTag(resource)
if resource.Spec.Resource.Description != "" {
description = resource.Spec.Resource.Description + " " + description
}

floatingIP, err = r.findAdoptee(log.IntoContext(ctx, logger), networkClient, resource, createOpts)
if err != nil {
return ctrl.Result{}, fmt.Errorf("unable to find adoption candidates: %w", err)
}
if floatingIP != nil {
logger = logger.WithValues("OpenStackID", floatingIP.ID)
logger.Info("OpenStack resource adopted")
} else {
floatingIP, err = floatingips.Create(networkClient, createOpts).Extract()
floatingIP, err = floatingips.Create(networkClient, floatingips.CreateOpts{
Description: description,
FloatingNetworkID: networkID,
FloatingIP: resource.Spec.Resource.FloatingIPAddress,
PortID: portID,
FixedIP: resource.Spec.Resource.FixedIPAddress,
SubnetID: subnetID,
TenantID: resource.Spec.Resource.TenantID,
ProjectID: resource.Spec.Resource.ProjectID,
}).Extract()
if err != nil {
return ctrl.Result{}, err
}
Expand Down Expand Up @@ -338,7 +342,7 @@ func (r *OpenStackFloatingIPReconciler) reconcileDelete(ctx context.Context, net
return ctrl.Result{}, nil
}

func (r *OpenStackFloatingIPReconciler) findAdoptee(ctx context.Context, networkClient *gophercloud.ServiceClient, resource client.Object, createOpts floatingips.CreateOpts) (*floatingips.FloatingIP, error) {
func (r *OpenStackFloatingIPReconciler) findAdoptee(ctx context.Context, networkClient *gophercloud.ServiceClient, resource client.Object) (*floatingips.FloatingIP, error) {
adoptedIDs := make(map[string]struct{})
{
list := &openstackv1.OpenStackFloatingIPList{}
Expand All @@ -354,22 +358,24 @@ func (r *OpenStackFloatingIPReconciler) findAdoptee(ctx context.Context, network
}
}

tag := orcTag(resource)
isTagged := func(f floatingips.FloatingIP) bool {
for _, s := range strings.Split(f.Description, " ") {
if strings.Contains(s, tag) {
return true
}
}
return false
}

var candidates []floatingips.FloatingIP
err := floatingips.List(networkClient, floatingips.ListOpts{
Description: createOpts.Description,
FloatingNetworkID: createOpts.FloatingNetworkID,
PortID: createOpts.PortID,
FixedIP: createOpts.FixedIP,
FloatingIP: createOpts.FloatingIP,
TenantID: createOpts.TenantID,
ProjectID: createOpts.ProjectID,
}).EachPage(func(page pagination.Page) (bool, error) {
err := floatingips.List(networkClient, nil).EachPage(func(page pagination.Page) (bool, error) {
items, err := floatingips.ExtractFloatingIPs(page)
if err != nil {
return false, fmt.Errorf("extracting resources: %w", err)
}
for i := range items {
if _, ok := adoptedIDs[items[i].ID]; !ok {
if _, ok := adoptedIDs[items[i].ID]; !ok && isTagged(items[i]) {
candidates = append(candidates, items[i])
}
}
Expand Down

0 comments on commit 430aec5

Please sign in to comment.