Skip to content

Commit 1358f6e

Browse files
authored
Merge pull request #1004 from shiftstack/alladdresses
✨Return all NodeAddresses in OpenStackMachine.Status.Addresses
2 parents 1be73ec + aea62dd commit 1358f6e

File tree

10 files changed

+949
-95
lines changed

10 files changed

+949
-95
lines changed

controllers/openstackcluster_controller.go

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import (
2727
"github.com/gophercloud/gophercloud/openstack/networking/v2/subnets"
2828
"github.com/gophercloud/utils/openstack/clientconfig"
2929
"github.com/pkg/errors"
30+
corev1 "k8s.io/api/core/v1"
3031
apierrors "k8s.io/apimachinery/pkg/api/errors"
3132
"k8s.io/client-go/tools/record"
3233
"k8s.io/utils/pointer"
@@ -206,16 +207,14 @@ func deleteBastion(log logr.Logger, osProviderClient *gophercloud.ProviderClient
206207
if err != nil {
207208
return err
208209
}
210+
addresses := instanceNS.Addresses()
209211

210-
floatingIP := instanceNS.FloatingIP()
211-
if floatingIP != "" {
212-
if err = networkingService.DisassociateFloatingIP(openStackCluster, floatingIP); err != nil {
213-
handleUpdateOSCError(openStackCluster, errors.Errorf("failed to disassociate floating IP: %v", err))
214-
return errors.Errorf("failed to disassociate floating IP: %v", err)
215-
}
216-
if err = networkingService.DeleteFloatingIP(openStackCluster, floatingIP); err != nil {
217-
handleUpdateOSCError(openStackCluster, errors.Errorf("failed to delete floating IP: %v", err))
218-
return errors.Errorf("failed to delete floating IP: %v", err)
212+
for _, address := range addresses {
213+
if address.Type == corev1.NodeExternalIP {
214+
if err = networkingService.DeleteFloatingIP(openStackCluster, address.Address); err != nil {
215+
handleUpdateOSCError(openStackCluster, errors.Errorf("failed to delete floating IP: %v", err))
216+
return errors.Errorf("failed to delete floating IP: %v", err)
217+
}
219218
}
220219
}
221220

@@ -319,7 +318,7 @@ func reconcileBastion(log logr.Logger, osProviderClient *gophercloud.ProviderCli
319318
return err
320319
}
321320
if instanceStatus != nil {
322-
bastion, err := instanceStatus.APIInstance()
321+
bastion, err := instanceStatus.APIInstance(openStackCluster)
323322
if err != nil {
324323
return err
325324
}
@@ -343,7 +342,7 @@ func reconcileBastion(log logr.Logger, osProviderClient *gophercloud.ProviderCli
343342
handleUpdateOSCError(openStackCluster, errors.Errorf("failed to get or create floating IP for bastion: %v", err))
344343
return errors.Errorf("failed to get or create floating IP for bastion: %v", err)
345344
}
346-
port, err := computeService.GetManagementPort(instanceStatus)
345+
port, err := computeService.GetManagementPort(openStackCluster, instanceStatus)
347346
if err != nil {
348347
err = errors.Errorf("getting management port for bastion: %v", err)
349348
handleUpdateOSCError(openStackCluster, err)
@@ -355,7 +354,7 @@ func reconcileBastion(log logr.Logger, osProviderClient *gophercloud.ProviderCli
355354
return errors.Errorf("failed to associate floating IP with bastion: %v", err)
356355
}
357356

358-
bastion, err := instanceStatus.APIInstance()
357+
bastion, err := instanceStatus.APIInstance(openStackCluster)
359358
if err != nil {
360359
return err
361360
}

controllers/openstackmachine_controller.go

Lines changed: 22 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -224,28 +224,27 @@ func (r *OpenStackMachineReconciler) reconcileDelete(ctx context.Context, logger
224224

225225
if instanceStatus == nil {
226226
logger.Info("Skipped deleting machine that is already deleted")
227-
controllerutil.RemoveFinalizer(openStackMachine, infrav1.MachineFinalizer)
228-
if err := patchHelper.Patch(ctx, openStackMachine); err != nil {
229-
return ctrl.Result{}, err
230-
}
231-
return ctrl.Result{}, nil
232-
}
233-
234-
instanceNS, err := instanceStatus.NetworkStatus()
235-
if err != nil {
236-
handleUpdateMachineError(logger, openStackMachine, errors.Errorf("error getting network status for OpenStack instance %s with ID %s: %v", instanceStatus.Name(), instanceStatus.ID(), err))
237-
return ctrl.Result{}, nil
238-
}
227+
} else {
228+
if !openStackCluster.Spec.ManagedAPIServerLoadBalancer && util.IsControlPlaneMachine(machine) && openStackCluster.Spec.APIServerFloatingIP == "" {
229+
instanceNS, err := instanceStatus.NetworkStatus()
230+
if err != nil {
231+
handleUpdateMachineError(logger, openStackMachine, errors.Errorf("error getting network status for OpenStack instance %s with ID %s: %v", instanceStatus.Name(), instanceStatus.ID(), err))
232+
return ctrl.Result{}, nil
233+
}
239234

240-
if err = computeService.DeleteInstance(openStackMachine, instanceStatus); err != nil {
241-
handleUpdateMachineError(logger, openStackMachine, errors.Errorf("error deleting OpenStack instance %s with ID %s: %v", instanceStatus.Name(), instanceStatus.ID(), err))
242-
return ctrl.Result{}, nil
243-
}
235+
addresses := instanceNS.Addresses()
236+
for _, address := range addresses {
237+
if address.Type == corev1.NodeExternalIP {
238+
if err = networkingService.DeleteFloatingIP(openStackCluster, address.Address); err != nil {
239+
handleUpdateMachineError(logger, openStackMachine, errors.Errorf("error deleting Openstack floating IP: %v", err))
240+
return ctrl.Result{}, nil
241+
}
242+
}
243+
}
244+
}
244245

245-
floatingIP := instanceNS.FloatingIP()
246-
if !openStackCluster.Spec.ManagedAPIServerLoadBalancer && util.IsControlPlaneMachine(machine) && openStackCluster.Spec.APIServerFloatingIP == "" && floatingIP != "" {
247-
if err = networkingService.DeleteFloatingIP(openStackCluster, floatingIP); err != nil {
248-
handleUpdateMachineError(logger, openStackMachine, errors.Errorf("error deleting Openstack floating IP: %v", err))
246+
if err := computeService.DeleteInstance(openStackMachine, instanceStatus); err != nil {
247+
handleUpdateMachineError(logger, openStackMachine, errors.Errorf("error deleting OpenStack instance %s with ID %s: %v", instanceStatus.Name(), instanceStatus.ID(), err))
249248
return ctrl.Result{}, nil
250249
}
251250
}
@@ -331,10 +330,7 @@ func (r *OpenStackMachineReconciler) reconcileNormal(ctx context.Context, logger
331330
return ctrl.Result{}, nil
332331
}
333332

334-
addresses := []corev1.NodeAddress{{Type: corev1.NodeInternalIP, Address: instanceNS.IP()}}
335-
if instanceNS.FloatingIP() != "" {
336-
addresses = append(addresses, []corev1.NodeAddress{{Type: corev1.NodeExternalIP, Address: instanceNS.FloatingIP()}}...)
337-
}
333+
addresses := instanceNS.Addresses()
338334
openStackMachine.Status.Addresses = addresses
339335

340336
// TODO(sbueringer) From CAPA: TODO(vincepri): Remove this annotation when clusterctl is no longer relevant.
@@ -370,7 +366,7 @@ func (r *OpenStackMachineReconciler) reconcileNormal(ctx context.Context, logger
370366
handleUpdateMachineError(logger, openStackMachine, errors.Errorf("Floating IP cannot be got or created: %v", err))
371367
return ctrl.Result{}, nil
372368
}
373-
port, err := computeService.GetManagementPort(instanceStatus)
369+
port, err := computeService.GetManagementPort(openStackCluster, instanceStatus)
374370
if err != nil {
375371
err = errors.Errorf("getting management port for control plane machine %s: %v", machine.Name, err)
376372
handleUpdateMachineError(logger, openStackMachine, err)
@@ -413,7 +409,7 @@ func handleUpdateMachineError(logger logr.Logger, openstackMachine *infrav1.Open
413409
}
414410

415411
func (r *OpenStackMachineReconciler) reconcileLoadBalancerMember(logger logr.Logger, osProviderClient *gophercloud.ProviderClient, clientOpts *clientconfig.ClientOpts, openStackCluster *infrav1.OpenStackCluster, machine *clusterv1.Machine, openStackMachine *infrav1.OpenStackMachine, instanceNS *compute.InstanceNetworkStatus, clusterName string) error {
416-
ip := instanceNS.IP()
412+
ip := instanceNS.IP(openStackCluster.Status.Network.Name)
417413
loadbalancerService, err := loadbalancer.NewService(osProviderClient, clientOpts, logger)
418414
if err != nil {
419415
return err

pkg/cloud/services/compute/instance.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -417,12 +417,12 @@ func (s *Service) getImageID(imageName string) (string, error) {
417417

418418
// GetManagementPort returns the port which is used for management and external
419419
// traffic. Cluster floating IPs must be associated with this port.
420-
func (s *Service) GetManagementPort(instanceStatus *InstanceStatus) (*ports.Port, error) {
420+
func (s *Service) GetManagementPort(openStackCluster *infrav1.OpenStackCluster, instanceStatus *InstanceStatus) (*ports.Port, error) {
421421
ns, err := instanceStatus.NetworkStatus()
422422
if err != nil {
423423
return nil, err
424424
}
425-
allPorts, err := s.networkingService.GetPortFromInstanceIP(instanceStatus.ID(), ns.IP())
425+
allPorts, err := s.networkingService.GetPortFromInstanceIP(instanceStatus.ID(), ns.IP(openStackCluster.Status.Network.Name))
426426
if err != nil {
427427
return nil, fmt.Errorf("lookup management port for server %s: %w", instanceStatus.ID(), err)
428428
}
@@ -551,7 +551,7 @@ func (s *Service) GetInstanceStatus(resourceID string) (instance *InstanceStatus
551551
return nil, fmt.Errorf("get server %q detail failed: %v", resourceID, err)
552552
}
553553

554-
return &InstanceStatus{server: &server}, nil
554+
return &InstanceStatus{&server, s.logger}, nil
555555
}
556556

557557
func (s *Service) GetInstanceStatusByName(eventObject runtime.Object, name string) (instance *InstanceStatus, err error) {
@@ -584,7 +584,7 @@ func (s *Service) GetInstanceStatusByName(eventObject runtime.Object, name strin
584584

585585
// Return the first returned server, if any
586586
for i := range serverList {
587-
return &InstanceStatus{server: &serverList[i]}, nil
587+
return &InstanceStatus{&serverList[i], s.logger}, nil
588588
}
589589
return nil, nil
590590
}

pkg/cloud/services/compute/instance_types.go

Lines changed: 89 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,12 @@ package compute
1919
import (
2020
"encoding/json"
2121
"fmt"
22+
"sort"
2223

24+
"github.com/go-logr/logr"
2325
"github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/availabilityzones"
2426
"github.com/gophercloud/gophercloud/openstack/compute/v2/servers"
27+
corev1 "k8s.io/api/core/v1"
2528

2629
infrav1 "sigs.k8s.io/cluster-api-provider-openstack/api/v1alpha4"
2730
)
@@ -63,10 +66,11 @@ type InstanceIdentifier struct {
6366
// InstanceStatus represents instance data which has been returned by OpenStack.
6467
type InstanceStatus struct {
6568
server *ServerExt
69+
logger logr.Logger
6670
}
6771

68-
func NewInstanceStatusFromServer(server *ServerExt) *InstanceStatus {
69-
return &InstanceStatus{server}
72+
func NewInstanceStatusFromServer(server *ServerExt, logger logr.Logger) *InstanceStatus {
73+
return &InstanceStatus{server, logger}
7074
}
7175

7276
type networkInterface struct {
@@ -79,7 +83,7 @@ type networkInterface struct {
7983
// as used by CAPO. Therefore it may use more context than just data which was
8084
// returned by OpenStack.
8185
type InstanceNetworkStatus struct {
82-
addresses map[string][]networkInterface
86+
addresses map[string][]corev1.NodeAddress
8387
}
8488

8589
func (is *InstanceStatus) ID() string {
@@ -103,7 +107,7 @@ func (is *InstanceStatus) AvailabilityZone() string {
103107
}
104108

105109
// APIInstance returns an infrav1.Instance object for use by the API.
106-
func (is *InstanceStatus) APIInstance() (*infrav1.Instance, error) {
110+
func (is *InstanceStatus) APIInstance(openStackCluster *infrav1.OpenStackCluster) (*infrav1.Instance, error) {
107111
i := infrav1.Instance{
108112
ID: is.ID(),
109113
Name: is.Name(),
@@ -116,8 +120,9 @@ func (is *InstanceStatus) APIInstance() (*infrav1.Instance, error) {
116120
return nil, err
117121
}
118122

119-
i.IP = ns.IP()
120-
i.FloatingIP = ns.FloatingIP()
123+
clusterNetwork := openStackCluster.Status.Network.Name
124+
i.IP = ns.IP(clusterNetwork)
125+
i.FloatingIP = ns.FloatingIP(clusterNetwork)
121126

122127
return &i, nil
123128
}
@@ -132,51 +137,102 @@ func (is *InstanceStatus) InstanceIdentifier() *InstanceIdentifier {
132137

133138
// NetworkStatus returns an InstanceNetworkStatus object for an InstanceStatus.
134139
func (is *InstanceStatus) NetworkStatus() (*InstanceNetworkStatus, error) {
135-
addresses := make(map[string][]networkInterface)
136-
140+
// Gophercloud doesn't give us a struct for server addresses: we get a
141+
// map of networkname -> interface{}. That interface{} is a list of
142+
// addresses as in the example output here:
143+
// https://docs.openstack.org/api-ref/compute/?expanded=show-server-details-detail#show-server-details
144+
//
145+
// Here we convert the interface{} into something more usable by
146+
// marshalling it to json, then unmarshalling it back into our own
147+
// struct.
148+
addressesByNetwork := make(map[string][]corev1.NodeAddress)
137149
for networkName, b := range is.server.Addresses {
138150
list, err := json.Marshal(b)
139151
if err != nil {
140152
return nil, fmt.Errorf("error marshalling addresses for instance %s: %w", is.ID(), err)
141153
}
142-
var networkList []networkInterface
143-
err = json.Unmarshal(list, &networkList)
154+
var interfaceList []networkInterface
155+
err = json.Unmarshal(list, &interfaceList)
144156
if err != nil {
145157
return nil, fmt.Errorf("error unmarshalling addresses for instance %s: %w", is.ID(), err)
146158
}
147159

148-
addresses[networkName] = networkList
149-
}
160+
var addresses []corev1.NodeAddress
161+
for i := range interfaceList {
162+
address := &interfaceList[i]
150163

151-
return &InstanceNetworkStatus{addresses}, nil
152-
}
164+
// Only consider IPv4
165+
if address.Version != 4 {
166+
is.logger.V(6).Info("Ignoring IPv%d address %s: only IPv4 is supported", address.Version, address.Address)
167+
continue
168+
}
153169

154-
func (ns *InstanceNetworkStatus) IP() string {
155-
// Return the last listed non-floating IPv4 from the last listed network
156-
// This behaviour is wrong, but consistent with the previous behaviour
157-
// https://github.com/kubernetes-sigs/cluster-api-provider-openstack/blob/4debc1fc4742e483302b0c36b16c076977bd165d/pkg/cloud/services/compute/instance.go#L973-L998
158-
// XXX: Fix this
159-
for _, vifs := range ns.addresses {
160-
for _, vif := range vifs {
161-
if vif.Version == 4.0 && vif.Type != "floating" {
162-
return vif.Address
170+
var addressType corev1.NodeAddressType
171+
switch address.Type {
172+
case "floating":
173+
addressType = corev1.NodeExternalIP
174+
case "fixed":
175+
addressType = corev1.NodeInternalIP
176+
default:
177+
is.logger.V(6).Info("Ignoring address %s with unknown type '%s'", address.Address, address.Type)
178+
continue
163179
}
180+
181+
addresses = append(addresses, corev1.NodeAddress{
182+
Type: addressType,
183+
Address: address.Address,
184+
})
164185
}
186+
187+
addressesByNetwork[networkName] = addresses
165188
}
166-
return ""
189+
190+
return &InstanceNetworkStatus{addressesByNetwork}, nil
191+
}
192+
193+
// Addresses returns a list of NodeAddresses containing all addresses which will
194+
// be reported on the OpenStackMachine object.
195+
func (ns *InstanceNetworkStatus) Addresses() []corev1.NodeAddress {
196+
// We want the returned order of addresses to be deterministic to make
197+
// it easy to detect changes and avoid unnecessary updates. Iteration
198+
// over maps is non-deterministic, so we explicitly iterate over the
199+
// address map in lexical order of network names. This order is
200+
// arbitrary.
201+
// Pull out addresses map keys (network names) and sort them lexically
202+
networks := make([]string, 0, len(ns.addresses))
203+
for network := range ns.addresses {
204+
networks = append(networks, network)
205+
}
206+
sort.Strings(networks)
207+
208+
var addresses []corev1.NodeAddress
209+
for _, network := range networks {
210+
addressList := ns.addresses[network]
211+
addresses = append(addresses, addressList...)
212+
}
213+
214+
return addresses
167215
}
168216

169-
func (ns *InstanceNetworkStatus) FloatingIP() string {
170-
// Return the last listed floating IPv4 from the last listed network
171-
// This behaviour is wrong, but consistent with the previous behaviour
172-
// https://github.com/kubernetes-sigs/cluster-api-provider-openstack/blob/4debc1fc4742e483302b0c36b16c076977bd165d/pkg/cloud/services/compute/instance.go#L973-L998
173-
// XXX: Fix this
174-
for _, vifs := range ns.addresses {
175-
for _, vif := range vifs {
176-
if vif.Version == 4.0 && vif.Type == "floating" {
177-
return vif.Address
217+
func (ns *InstanceNetworkStatus) firstAddressByNetworkAndType(networkName string, addressType corev1.NodeAddressType) string {
218+
if addressList, ok := ns.addresses[networkName]; ok {
219+
for i := range addressList {
220+
address := &addressList[i]
221+
if address.Type == addressType {
222+
return address.Address
178223
}
179224
}
180225
}
181226
return ""
182227
}
228+
229+
// IP returns the first listed ip of an instance for the given network name.
230+
func (ns *InstanceNetworkStatus) IP(networkName string) string {
231+
return ns.firstAddressByNetworkAndType(networkName, corev1.NodeInternalIP)
232+
}
233+
234+
// FloatingIP returns the first listed floating ip of an instance for the given
235+
// network name.
236+
func (ns *InstanceNetworkStatus) FloatingIP(networkName string) string {
237+
return ns.firstAddressByNetworkAndType(networkName, corev1.NodeExternalIP)
238+
}

0 commit comments

Comments
 (0)