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