@@ -19,9 +19,12 @@ package compute
19
19
import (
20
20
"encoding/json"
21
21
"fmt"
22
+ "sort"
22
23
24
+ "github.com/go-logr/logr"
23
25
"github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/availabilityzones"
24
26
"github.com/gophercloud/gophercloud/openstack/compute/v2/servers"
27
+ corev1 "k8s.io/api/core/v1"
25
28
26
29
infrav1 "sigs.k8s.io/cluster-api-provider-openstack/api/v1alpha4"
27
30
)
@@ -63,10 +66,11 @@ type InstanceIdentifier struct {
63
66
// InstanceStatus represents instance data which has been returned by OpenStack.
64
67
type InstanceStatus struct {
65
68
server * ServerExt
69
+ logger logr.Logger
66
70
}
67
71
68
- func NewInstanceStatusFromServer (server * ServerExt ) * InstanceStatus {
69
- return & InstanceStatus {server }
72
+ func NewInstanceStatusFromServer (server * ServerExt , logger logr. Logger ) * InstanceStatus {
73
+ return & InstanceStatus {server , logger }
70
74
}
71
75
72
76
type networkInterface struct {
@@ -79,7 +83,7 @@ type networkInterface struct {
79
83
// as used by CAPO. Therefore it may use more context than just data which was
80
84
// returned by OpenStack.
81
85
type InstanceNetworkStatus struct {
82
- addresses map [string ][]networkInterface
86
+ addresses map [string ][]corev1. NodeAddress
83
87
}
84
88
85
89
func (is * InstanceStatus ) ID () string {
@@ -103,7 +107,7 @@ func (is *InstanceStatus) AvailabilityZone() string {
103
107
}
104
108
105
109
// 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 ) {
107
111
i := infrav1.Instance {
108
112
ID : is .ID (),
109
113
Name : is .Name (),
@@ -116,8 +120,9 @@ func (is *InstanceStatus) APIInstance() (*infrav1.Instance, error) {
116
120
return nil , err
117
121
}
118
122
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 )
121
126
122
127
return & i , nil
123
128
}
@@ -132,51 +137,102 @@ func (is *InstanceStatus) InstanceIdentifier() *InstanceIdentifier {
132
137
133
138
// NetworkStatus returns an InstanceNetworkStatus object for an InstanceStatus.
134
139
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 )
137
149
for networkName , b := range is .server .Addresses {
138
150
list , err := json .Marshal (b )
139
151
if err != nil {
140
152
return nil , fmt .Errorf ("error marshalling addresses for instance %s: %w" , is .ID (), err )
141
153
}
142
- var networkList []networkInterface
143
- err = json .Unmarshal (list , & networkList )
154
+ var interfaceList []networkInterface
155
+ err = json .Unmarshal (list , & interfaceList )
144
156
if err != nil {
145
157
return nil , fmt .Errorf ("error unmarshalling addresses for instance %s: %w" , is .ID (), err )
146
158
}
147
159
148
- addresses [networkName ] = networkList
149
- }
160
+ var addresses []corev1.NodeAddress
161
+ for i := range interfaceList {
162
+ address := & interfaceList [i ]
150
163
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
+ }
153
169
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
163
179
}
180
+
181
+ addresses = append (addresses , corev1.NodeAddress {
182
+ Type : addressType ,
183
+ Address : address .Address ,
184
+ })
164
185
}
186
+
187
+ addressesByNetwork [networkName ] = addresses
165
188
}
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
167
215
}
168
216
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
178
223
}
179
224
}
180
225
}
181
226
return ""
182
227
}
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