@@ -124,7 +124,61 @@ func (s *Service) GetPortForExternalNetwork(instanceID string, externalNetworkID
124124 return nil , nil
125125}
126126
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+ }
128182 var addressPairs []ports.AddressPair
129183 if ! ptr .Deref (portSpec .DisablePortSecurity , false ) {
130184 for _ , ap := range portSpec .AllowedAddressPairs {
@@ -200,24 +254,10 @@ func (s *Service) CreatePort(eventObject runtime.Object, portSpec *infrav1.Resol
200254 return nil , err
201255 }
202256
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
208259 }
209260 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- }
221261
222262 return port , nil
223263}
@@ -328,23 +368,30 @@ func getPortName(baseName string, portSpec *infrav1.PortOpts, netIndex int) stri
328368 return fmt .Sprintf ("%s-%d" , baseName , netIndex )
329369}
330370
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 {
332374 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 {}
334377 if i < len (resources .Ports ) {
335- continue
378+ portStatus = resources . Ports [ i ]
336379 }
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 )
341382 if err != nil {
342383 return err
343384 }
344385
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+ }
348395 }
349396
350397 return nil
@@ -604,3 +651,19 @@ func (s *Service) AdoptPortsServer(scope *scope.WithLogger, desiredPorts []infra
604651
605652 return nil
606653}
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