Skip to content

Commit e53d8ef

Browse files
dulekmandre
authored andcommitted
Optimize applyNodeSecurityGroupIDForLB() (kubernetes#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 6543e54 commit e53d8ef

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
@@ -41,13 +41,13 @@ import (
4141
secgroups "github.com/gophercloud/utils/openstack/networking/v2/extensions/security/groups"
4242
"gopkg.in/godo.v2/glob"
4343
corev1 "k8s.io/api/core/v1"
44-
"k8s.io/apimachinery/pkg/types"
4544
utilerrors "k8s.io/apimachinery/pkg/util/errors"
4645
"k8s.io/apimachinery/pkg/util/sets"
4746
"k8s.io/client-go/kubernetes"
4847
cloudprovider "k8s.io/cloud-provider"
4948
"k8s.io/klog/v2"
5049
netutils "k8s.io/utils/net"
50+
"k8s.io/utils/strings/slices"
5151

5252
"k8s.io/cloud-provider-openstack/pkg/metrics"
5353
cpoutil "k8s.io/cloud-provider-openstack/pkg/util"
@@ -717,48 +717,44 @@ func getSubnetIDForLB(network *gophercloud.ServiceClient, node corev1.Node, pref
717717
}
718718

719719
// applyNodeSecurityGroupIDForLB associates the security group with all the ports on the nodes.
720-
func applyNodeSecurityGroupIDForLB(compute *gophercloud.ServiceClient, network *gophercloud.ServiceClient, nodes []*corev1.Node, sg string) error {
720+
func applyNodeSecurityGroupIDForLB(network *gophercloud.ServiceClient, nodes []*corev1.Node, sg string) error {
721721
for _, node := range nodes {
722-
nodeName := types.NodeName(node.Name)
723-
srv, err := getServerByName(compute, nodeName)
722+
serverID, _, err := instanceIDFromProviderID(node.Spec.ProviderID)
724723
if err != nil {
725-
return err
724+
return fmt.Errorf("error getting server ID from the node: %w", err)
726725
}
727-
728-
listOpts := neutronports.ListOpts{DeviceID: srv.ID}
726+
listOpts := neutronports.ListOpts{DeviceID: serverID}
729727
allPorts, err := openstackutil.GetPorts(network, listOpts)
730728
if err != nil {
731729
return err
732730
}
733731

734732
for _, port := range allPorts {
735733
// If the Security Group is already present on the port, skip it.
736-
// As soon as this only supports Go 1.18, this can be replaces by
737-
// slices.Contains.
738-
if func() bool {
739-
for _, currentSG := range port.SecurityGroups {
740-
if currentSG == sg {
741-
return true
742-
}
743-
}
744-
return false
745-
}() {
734+
if slices.Contains(port.SecurityGroups, sg) {
746735
continue
747736
}
748737

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

@@ -2361,7 +2357,7 @@ func (lbaas *LbaasV2) ensureAndUpdateOctaviaSecurityGroup(clusterName string, ap
23612357
}
23622358
}
23632359

2364-
if err := applyNodeSecurityGroupIDForLB(lbaas.compute, lbaas.network, nodes, lbSecGroupID); err != nil {
2360+
if err := applyNodeSecurityGroupIDForLB(lbaas.network, nodes, lbSecGroupID); err != nil {
23652361
return err
23662362
}
23672363
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
@@ -337,12 +336,6 @@ func (os *OpenStack) LoadBalancer() (cloudprovider.LoadBalancer, bool) {
337336
return nil, false
338337
}
339338

340-
compute, err := client.NewComputeV2(os.provider, os.epOpts)
341-
if err != nil {
342-
klog.Errorf("Failed to create an OpenStack Compute client: %v", err)
343-
return nil, false
344-
}
345-
346339
lb, err := client.NewLoadBalancerV2(os.provider, os.epOpts)
347340
if err != nil {
348341
klog.Errorf("Failed to create an OpenStack LoadBalancer client: %v", err)
@@ -365,7 +358,7 @@ func (os *OpenStack) LoadBalancer() (cloudprovider.LoadBalancer, bool) {
365358

366359
klog.V(1).Info("Claiming to support LoadBalancer")
367360

368-
return &LbaasV2{LoadBalancer{secret, network, compute, lb, os.lbOpts, os.kclient}}, true
361+
return &LbaasV2{LoadBalancer{secret, network, lb, os.lbOpts, os.kclient}}, true
369362
}
370363

371364
// Zones indicates that we support zones

0 commit comments

Comments
 (0)