Skip to content

Commit

Permalink
Remove SGs from non-existent nodes ports on Update
Browse files Browse the repository at this point in the history
  • Loading branch information
EmilienM committed Oct 22, 2024
1 parent 04bd8e1 commit ed7c4a8
Showing 1 changed file with 54 additions and 24 deletions.
78 changes: 54 additions & 24 deletions pkg/openstack/loadbalancer_sg.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ func applyNodeSecurityGroupIDForLB(network *gophercloud.ServiceClient, svcConf *
continue
}

listOpts := neutronports.ListOpts{DeviceID: serverID}
listOpts := neutronports.ListOpts{}
allPorts, err := openstackutil.GetPorts[PortWithPortSecurity](network, listOpts)
if err != nil {
return err
Expand All @@ -85,6 +85,25 @@ func applyNodeSecurityGroupIDForLB(network *gophercloud.ServiceClient, svcConf *
continue
}

// If the port is not attached to the server, we'll check whether it has the SG and remove it.
// This can happen when a Node is removed from the LB and the port wasn't updated.
if port.DeviceID != serverID {
sgFound := false
for _, portSG := range port.SecurityGroups {
if portSG == sg {
sgFound = true
break
}
}
if sgFound {
err := removeSecurityGroupFromPort(network, port.Port, sg)
if err != nil {
return err
}
continue
}
}

// Add the SG to the port
// TODO(dulek): This isn't an atomic operation. In order to protect from lost update issues we should use
// `revision_number` handling to make sure our update to `security_groups` field wasn't preceded
Expand All @@ -97,6 +116,7 @@ func applyNodeSecurityGroupIDForLB(network *gophercloud.ServiceClient, svcConf *
return fmt.Errorf("failed to update security group for port %s: %v", port.ID, res.Err)
}
}

}

return nil
Expand All @@ -113,31 +133,41 @@ func disassociateSecurityGroupForLB(network *gophercloud.ServiceClient, sg strin

// Disassocate security group and remove the tag.
for _, port := range allPorts {
existingSGs := sets.NewString()
for _, sgID := range port.SecurityGroups {
existingSGs.Insert(sgID)
}
existingSGs.Delete(sg)

// Update port security groups
newSGs := existingSGs.List()
// TODO(dulek): This should be done using Neutron's revision_number to make sure
// we don't trigger a lost update issue.
updateOpts := neutronports.UpdateOpts{SecurityGroups: &newSGs}
mc := metrics.NewMetricContext("port", "update")
res := neutronports.Update(context.TODO(), network, port.ID, updateOpts)
if mc.ObserveRequest(res.Err) != nil {
return fmt.Errorf("failed to update security group for port %s: %v", port.ID, res.Err)
err := removeSecurityGroupFromPort(network, port, sg)
if err != nil {
return err
}
}

// Remove the security group ID tag from the port. Please note we don't tag ports with SG IDs anymore,
// so this stays for backward compatibility. It's reasonable to delete it in the future. 404s are ignored.
if slices.Contains(port.Tags, sg) {
mc = metrics.NewMetricContext("port_tag", "delete")
err := neutrontags.Delete(context.TODO(), network, "ports", port.ID, sg).ExtractErr()
if mc.ObserveRequest(err) != nil {
return fmt.Errorf("failed to remove tag %s to port %s: %v", sg, port.ID, res.Err)
}
return nil
}

// removeSecurityGroupFromPort removes the given security group from the port
func removeSecurityGroupFromPort(network *gophercloud.ServiceClient, port neutronports.Port, sg string) error {
existingSGs := sets.NewString()
for _, sgID := range port.SecurityGroups {
existingSGs.Insert(sgID)
}
existingSGs.Delete(sg)

// Update port security groups
newSGs := existingSGs.List()
// TODO(dulek): This should be done using Neutron's revision_number to make sure
// we don't trigger a lost update issue.
updateOpts := neutronports.UpdateOpts{SecurityGroups: &newSGs}
mc := metrics.NewMetricContext("port", "update")
res := neutronports.Update(context.TODO(), network, port.ID, updateOpts)
if mc.ObserveRequest(res.Err) != nil {
return fmt.Errorf("failed to update security group for port %s: %v", port.ID, res.Err)
}

// Remove the security group ID tag from the port. Please note we don't tag ports with SG IDs anymore,
// so this stays for backward compatibility. It's reasonable to delete it in the future. 404s are ignored.
if slices.Contains(port.Tags, sg) {
mc = metrics.NewMetricContext("port_tag", "delete")
err := neutrontags.Delete(context.TODO(), network, "ports", port.ID, sg).ExtractErr()
if mc.ObserveRequest(err) != nil {
return fmt.Errorf("failed to remove tag %s to port %s: %v", sg, port.ID, res.Err)
}
}

Expand Down

0 comments on commit ed7c4a8

Please sign in to comment.