@@ -123,7 +123,61 @@ func (s *Service) GetPortForExternalNetwork(instanceID string, externalNetworkID
123
123
return nil , nil
124
124
}
125
125
126
- func (s * Service ) CreatePort (eventObject runtime.Object , portSpec * infrav1.ResolvedPortSpec ) (* ports.Port , error ) {
126
+ // ensurePortTagsAndTrunk ensures that the provided port has the tags and trunk defined in portSpec.
127
+ func (s * Service ) ensurePortTagsAndTrunk (port * ports.Port , eventObject runtime.Object , portSpec * infrav1.ResolvedPortSpec ) error {
128
+ wantedTags := uniqueSortedTags (portSpec .Tags )
129
+ actualTags := uniqueSortedTags (port .Tags )
130
+ // Only replace tags if there is a difference
131
+ if ! slices .Equal (wantedTags , actualTags ) && len (wantedTags ) > 0 {
132
+ if err := s .replaceAllAttributesTags (eventObject , portResource , port .ID , wantedTags ); err != nil {
133
+ record .Warnf (eventObject , "FailedReplaceTags" , "Failed to replace port tags %s: %v" , port .Name , err )
134
+ return err
135
+ }
136
+ }
137
+ if ptr .Deref (portSpec .Trunk , false ) {
138
+ trunk , err := s .getOrCreateTrunkForPort (eventObject , port )
139
+ if err != nil {
140
+ record .Warnf (eventObject , "FailedCreateTrunk" , "Failed to create trunk for port %s: %v" , port .Name , err )
141
+ return err
142
+ }
143
+
144
+ if ! slices .Equal (wantedTags , trunk .Tags ) {
145
+ if err = s .replaceAllAttributesTags (eventObject , trunkResource , trunk .ID , wantedTags ); err != nil {
146
+ record .Warnf (eventObject , "FailedReplaceTags" , "Failed to replace trunk tags %s: %v" , port .Name , err )
147
+ return err
148
+ }
149
+ }
150
+ }
151
+ return nil
152
+ }
153
+
154
+ // EnsurePort ensure that a port defined with portSpec Name and NetworkID exists,
155
+ // and that the port has suitable tags and trunk. If the PortStatus is already known,
156
+ // use the ID when filtering for existing ports.
157
+ func (s * Service ) EnsurePort (eventObject runtime.Object , portSpec * infrav1.ResolvedPortSpec , portStatus infrav1.PortStatus ) (* ports.Port , error ) {
158
+ opts := ports.ListOpts {
159
+ Name : portSpec .Name ,
160
+ NetworkID : portSpec .NetworkID ,
161
+ }
162
+ if portStatus .ID != "" {
163
+ opts .ID = portStatus .ID
164
+ }
165
+
166
+ existingPorts , err := s .client .ListPort (opts )
167
+ if err != nil {
168
+ return nil , fmt .Errorf ("searching for existing port for server: %v" , err )
169
+ }
170
+ if len (existingPorts ) > 1 {
171
+ return nil , fmt .Errorf ("multiple ports found with name \" %s\" " , portSpec .Name )
172
+ }
173
+
174
+ if len (existingPorts ) == 1 {
175
+ port := & existingPorts [0 ]
176
+ if err = s .ensurePortTagsAndTrunk (port , eventObject , portSpec ); err != nil {
177
+ return nil , err
178
+ }
179
+ return port , nil
180
+ }
127
181
var addressPairs []ports.AddressPair
128
182
if ! ptr .Deref (portSpec .DisablePortSecurity , false ) {
129
183
for _ , ap := range portSpec .AllowedAddressPairs {
@@ -196,24 +250,10 @@ func (s *Service) CreatePort(eventObject runtime.Object, portSpec *infrav1.Resol
196
250
return nil , err
197
251
}
198
252
199
- if len (portSpec .Tags ) > 0 {
200
- if err = s .replaceAllAttributesTags (eventObject , portResource , port .ID , portSpec .Tags ); err != nil {
201
- record .Warnf (eventObject , "FailedReplaceTags" , "Failed to replace port tags %s: %v" , portSpec .Name , err )
202
- return nil , err
203
- }
253
+ if err = s .ensurePortTagsAndTrunk (port , eventObject , portSpec ); err != nil {
254
+ return nil , err
204
255
}
205
256
record .Eventf (eventObject , "SuccessfulCreatePort" , "Created port %s with id %s" , port .Name , port .ID )
206
- if ptr .Deref (portSpec .Trunk , false ) {
207
- trunk , err := s .getOrCreateTrunkForPort (eventObject , port )
208
- if err != nil {
209
- record .Warnf (eventObject , "FailedCreateTrunk" , "Failed to create trunk for port %s: %v" , port .Name , err )
210
- return nil , err
211
- }
212
- if err = s .replaceAllAttributesTags (eventObject , trunkResource , trunk .ID , portSpec .Tags ); err != nil {
213
- record .Warnf (eventObject , "FailedReplaceTags" , "Failed to replace trunk tags %s: %v" , port .Name , err )
214
- return nil , err
215
- }
216
- }
217
257
218
258
return port , nil
219
259
}
@@ -324,23 +364,30 @@ func getPortName(baseName string, portSpec *infrav1.PortOpts, netIndex int) stri
324
364
return fmt .Sprintf ("%s-%d" , baseName , netIndex )
325
365
}
326
366
327
- func (s * Service ) CreatePorts (eventObject runtime.Object , desiredPorts []infrav1.ResolvedPortSpec , resources * infrav1.MachineResources ) error {
367
+ // EnsurePorts ensures that every one of desiredPorts is created and has
368
+ // expected trunk and tags.
369
+ func (s * Service ) EnsurePorts (eventObject runtime.Object , desiredPorts []infrav1.ResolvedPortSpec , resources * infrav1.MachineResources ) error {
328
370
for i := range desiredPorts {
329
- // Skip creation of ports which already exist
371
+ // If we already created the port, make use of the status
372
+ portStatus := infrav1.PortStatus {}
330
373
if i < len (resources .Ports ) {
331
- continue
374
+ portStatus = resources . Ports [ i ]
332
375
}
333
-
334
- portSpec := & desiredPorts [i ]
335
- // Events are recorded in CreatePort
336
- port , err := s .CreatePort (eventObject , portSpec )
376
+ // Events are recorded in EnsurePort
377
+ port , err := s .EnsurePort (eventObject , & desiredPorts [i ], portStatus )
337
378
if err != nil {
338
379
return err
339
380
}
340
381
341
- resources .Ports = append (resources .Ports , infrav1.PortStatus {
342
- ID : port .ID ,
343
- })
382
+ // If we already have the status, replace it,
383
+ // otherwise append it.
384
+ if i < len (resources .Ports ) {
385
+ resources .Ports [i ] = portStatus
386
+ } else {
387
+ resources .Ports = append (resources .Ports , infrav1.PortStatus {
388
+ ID : port .ID ,
389
+ })
390
+ }
344
391
}
345
392
346
393
return nil
@@ -609,3 +656,19 @@ func (s *Service) AdoptPorts(scope *scope.WithLogger, desiredPorts []infrav1.Res
609
656
610
657
return nil
611
658
}
659
+
660
+ // uniqueSortedTags returns a new, sorted slice where any duplicates have been removed.
661
+ func uniqueSortedTags (tags []string ) []string {
662
+ // remove duplicate values from tags
663
+ tagsMap := make (map [string ]string )
664
+ for _ , t := range tags {
665
+ tagsMap [t ] = t
666
+ }
667
+
668
+ uniqueTags := []string {}
669
+ for k := range tagsMap {
670
+ uniqueTags = append (uniqueTags , k )
671
+ }
672
+ slices .Sort (uniqueTags )
673
+ return uniqueTags
674
+ }
0 commit comments