Skip to content

Commit b229f84

Browse files
authored
Optimize applyNodeSecurityGroupIDForLB() (#2293)
Matt noticed several optimizations that can be done in `applyNodeSecurityGroupIDForLB()` function. I added a few more refactorings. In particular: * We don't need to lookup servers by name as we have `.spec.providerID` in the nodes passed to the function. * We don't need to lookup servers at all when we have the ID, we can fetch ports by `deviceID`. * This means we don't need a Nova client in the LB instance anymore. * `slices.Contains()` from K8s utils can be used to check if SG is in the list. * It's better to tag the port with SG before we actually apply it. This will protect us from situations when the SG is applied, but tagging the port failed and now when deleting the SG CPO would be unable to find the port and therefore delete the SG.
1 parent 64b382f commit b229f84

File tree

2 files changed

+22
-33
lines changed

2 files changed

+22
-33
lines changed

pkg/openstack/loadbalancer.go

Lines changed: 21 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -40,13 +40,13 @@ import (
4040
secgroups "github.com/gophercloud/utils/openstack/networking/v2/extensions/security/groups"
4141
"gopkg.in/godo.v2/glob"
4242
corev1 "k8s.io/api/core/v1"
43-
"k8s.io/apimachinery/pkg/types"
4443
utilerrors "k8s.io/apimachinery/pkg/util/errors"
4544
"k8s.io/apimachinery/pkg/util/sets"
4645
"k8s.io/client-go/kubernetes"
4746
cloudprovider "k8s.io/cloud-provider"
4847
"k8s.io/klog/v2"
4948
netutils "k8s.io/utils/net"
49+
"k8s.io/utils/strings/slices"
5050

5151
"k8s.io/cloud-provider-openstack/pkg/metrics"
5252
cpoutil "k8s.io/cloud-provider-openstack/pkg/util"
@@ -721,48 +721,44 @@ func getSubnetIDForLB(network *gophercloud.ServiceClient, node corev1.Node, pref
721721
}
722722

723723
// applyNodeSecurityGroupIDForLB associates the security group with all the ports on the nodes.
724-
func applyNodeSecurityGroupIDForLB(compute *gophercloud.ServiceClient, network *gophercloud.ServiceClient, nodes []*corev1.Node, sg string) error {
724+
func applyNodeSecurityGroupIDForLB(network *gophercloud.ServiceClient, nodes []*corev1.Node, sg string) error {
725725
for _, node := range nodes {
726-
nodeName := types.NodeName(node.Name)
727-
srv, err := getServerByName(compute, nodeName)
726+
serverID, _, err := instanceIDFromProviderID(node.Spec.ProviderID)
728727
if err != nil {
729-
return err
728+
return fmt.Errorf("error getting server ID from the node: %w", err)
730729
}
731-
732-
listOpts := neutronports.ListOpts{DeviceID: srv.ID}
730+
listOpts := neutronports.ListOpts{DeviceID: serverID}
733731
allPorts, err := openstackutil.GetPorts(network, listOpts)
734732
if err != nil {
735733
return err
736734
}
737735

738736
for _, port := range allPorts {
739737
// If the Security Group is already present on the port, skip it.
740-
// As soon as this only supports Go 1.18, this can be replaces by
741-
// slices.Contains.
742-
if func() bool {
743-
for _, currentSG := range port.SecurityGroups {
744-
if currentSG == sg {
745-
return true
746-
}
747-
}
748-
return false
749-
}() {
738+
if slices.Contains(port.SecurityGroups, sg) {
750739
continue
751740
}
752741

742+
// Add the security group ID as a tag to the port in order to find all these ports when removing the security group.
743+
// We're doing that before actually applying the SG as if tagging would fail we wouldn't be able to find the port
744+
// when deleting the SG and operation would be stuck forever. It's better to find more ports than not all of them.
745+
mc := metrics.NewMetricContext("port_tag", "add")
746+
err := neutrontags.Add(network, "ports", port.ID, sg).ExtractErr()
747+
if mc.ObserveRequest(err) != nil {
748+
return fmt.Errorf("failed to add tag %s to port %s: %v", sg, port.ID, err)
749+
}
750+
751+
// Add the SG to the port
752+
// TODO(dulek): This isn't an atomic operation. In order to protect from lost update issues we should use
753+
// `revision_number` handling to make sure our update to `security_groups` field wasn't preceded
754+
// by a different one. Same applies to a removal of the SG.
753755
newSGs := append(port.SecurityGroups, sg)
754756
updateOpts := neutronports.UpdateOpts{SecurityGroups: &newSGs}
755-
mc := metrics.NewMetricContext("port", "update")
757+
mc = metrics.NewMetricContext("port", "update")
756758
res := neutronports.Update(network, port.ID, updateOpts)
757759
if mc.ObserveRequest(res.Err) != nil {
758760
return fmt.Errorf("failed to update security group for port %s: %v", port.ID, res.Err)
759761
}
760-
// Add the security group ID as a tag to the port in order to find all these ports when removing the security group.
761-
mc = metrics.NewMetricContext("port_tag", "add")
762-
err := neutrontags.Add(network, "ports", port.ID, sg).ExtractErr()
763-
if mc.ObserveRequest(err) != nil {
764-
return fmt.Errorf("failed to add tag %s to port %s: %v", sg, port.ID, err)
765-
}
766762
}
767763
}
768764

@@ -2325,7 +2321,7 @@ func (lbaas *LbaasV2) ensureAndUpdateOctaviaSecurityGroup(clusterName string, ap
23252321
}
23262322
}
23272323

2328-
if err := applyNodeSecurityGroupIDForLB(lbaas.compute, lbaas.network, nodes, lbSecGroupID); err != nil {
2324+
if err := applyNodeSecurityGroupIDForLB(lbaas.network, nodes, lbSecGroupID); err != nil {
23292325
return err
23302326
}
23312327
return nil

pkg/openstack/openstack.go

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,6 @@ func AddExtraFlags(fs *pflag.FlagSet) {
7171
type LoadBalancer struct {
7272
secret *gophercloud.ServiceClient
7373
network *gophercloud.ServiceClient
74-
compute *gophercloud.ServiceClient
7574
lb *gophercloud.ServiceClient
7675
opts LoadBalancerOpts
7776
kclient kubernetes.Interface
@@ -335,12 +334,6 @@ func (os *OpenStack) LoadBalancer() (cloudprovider.LoadBalancer, bool) {
335334
return nil, false
336335
}
337336

338-
compute, err := client.NewComputeV2(os.provider, os.epOpts)
339-
if err != nil {
340-
klog.Errorf("Failed to create an OpenStack Compute client: %v", err)
341-
return nil, false
342-
}
343-
344337
lb, err := client.NewLoadBalancerV2(os.provider, os.epOpts)
345338
if err != nil {
346339
klog.Errorf("Failed to create an OpenStack LoadBalancer client: %v", err)
@@ -363,7 +356,7 @@ func (os *OpenStack) LoadBalancer() (cloudprovider.LoadBalancer, bool) {
363356

364357
klog.V(1).Info("Claiming to support LoadBalancer")
365358

366-
return &LbaasV2{LoadBalancer{secret, network, compute, lb, os.lbOpts, os.kclient}}, true
359+
return &LbaasV2{LoadBalancer{secret, network, lb, os.lbOpts, os.kclient}}, true
367360
}
368361

369362
// Zones indicates that we support zones

0 commit comments

Comments
 (0)