Skip to content

Commit 2bf02c5

Browse files
committed
fix(openstack): determine ports to delete based on tags
1 parent 732397b commit 2bf02c5

File tree

3 files changed

+162
-11
lines changed

3 files changed

+162
-11
lines changed

cloudmock/openstack/mocknetworking/ports.go

+22
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import (
2222
"net/http"
2323
"net/url"
2424
"regexp"
25+
"slices"
2526
"strings"
2627

2728
"k8s.io/kops/upup/pkg/fi"
@@ -83,6 +84,22 @@ func (m *MockClient) mockPorts() {
8384
m.Mux.HandleFunc("/ports", handler)
8485
}
8586

87+
func containsAll(list []string, subList []string) bool {
88+
for _, item := range subList {
89+
if !slices.Contains(list, item) {
90+
return false
91+
}
92+
}
93+
return true
94+
}
95+
96+
func parseTags(tags string) []string {
97+
if tags == "" {
98+
return []string{}
99+
}
100+
return strings.Split(tags, ",")
101+
}
102+
86103
func (m *MockClient) listPorts(w http.ResponseWriter, vals url.Values) {
87104
w.WriteHeader(http.StatusOK)
88105

@@ -91,6 +108,8 @@ func (m *MockClient) listPorts(w http.ResponseWriter, vals url.Values) {
91108
idFilter := vals.Get("id")
92109
networkFilter := vals.Get("network_id")
93110
deviceFilter := vals.Get("device_id")
111+
tags := parseTags(vals.Get("tags"))
112+
94113
for _, p := range m.ports {
95114
if nameFilter != "" && nameFilter != p.Name {
96115
continue
@@ -104,6 +123,9 @@ func (m *MockClient) listPorts(w http.ResponseWriter, vals url.Values) {
104123
if idFilter != "" && idFilter != p.ID {
105124
continue
106125
}
126+
if len(tags) > 0 && !containsAll(p.Tags, tags) {
127+
continue
128+
}
107129
ports = append(ports, p)
108130
}
109131

upup/pkg/fi/cloudup/openstack/cloud.go

+26-11
Original file line numberDiff line numberDiff line change
@@ -617,6 +617,29 @@ func InstanceInClusterAndIG(instance servers.Server, clusterName string, instanc
617617
return true
618618
}
619619

620+
func deletePorts(c OpenstackCloud, instanceGroupName string, clusterName string) error {
621+
tags := []string{
622+
fmt.Sprintf("%s=%s", TagClusterName, clusterName),
623+
fmt.Sprintf("%s=%s", TagKopsInstanceGroup, instanceGroupName),
624+
}
625+
626+
ports, err := c.ListPorts(ports.ListOpts{Tags: strings.Join(tags, ",")})
627+
if err != nil {
628+
return fmt.Errorf("could not list ports %v", err)
629+
}
630+
631+
for _, port := range ports {
632+
klog.V(2).Infof("Delete port '%s' (%s)", port.Name, port.ID)
633+
err := c.DeletePort(port.ID)
634+
635+
if err != nil {
636+
return fmt.Errorf("could not delete port %q: %v", port.ID, err)
637+
}
638+
}
639+
640+
return nil
641+
}
642+
620643
func deleteGroup(c OpenstackCloud, g *cloudinstances.CloudInstanceGroup) error {
621644
cluster := g.Raw.(*kops.Cluster)
622645
allInstances, err := c.ListInstances(servers.ListOpts{
@@ -639,18 +662,10 @@ func deleteGroup(c OpenstackCloud, g *cloudinstances.CloudInstanceGroup) error {
639662
return fmt.Errorf("could not delete instance %q: %v", instance.ID, err)
640663
}
641664
}
642-
ports, err := c.ListPorts(ports.ListOpts{})
643-
if err != nil {
644-
return fmt.Errorf("could not list ports %v", err)
645-
}
646665

647-
for _, port := range ports {
648-
if strings.HasPrefix(port.Name, fmt.Sprintf("port-%s", g.InstanceGroup.Name)) && fi.ArrayContains(port.Tags, fmt.Sprintf("%s=%s", TagClusterName, cluster.Name)) {
649-
err := c.DeletePort(port.ID)
650-
if err != nil {
651-
return fmt.Errorf("could not delete port %q: %v", port.ID, err)
652-
}
653-
}
666+
err = deletePorts(c, g.InstanceGroup.Name, cluster.Name)
667+
if err != nil {
668+
return err
654669
}
655670

656671
sgName := g.InstanceGroup.Name

upup/pkg/fi/cloudup/openstack/cloud_test.go

+114
Original file line numberDiff line numberDiff line change
@@ -22,14 +22,17 @@ import (
2222
"net/http"
2323
"net/http/httptest"
2424
"reflect"
25+
"slices"
2526
"sort"
2627
"testing"
2728

2829
"github.com/gophercloud/gophercloud/v2"
2930
"github.com/gophercloud/gophercloud/v2/openstack/compute/v2/servers"
3031
"github.com/gophercloud/gophercloud/v2/openstack/loadbalancer/v2/loadbalancers"
3132
l3floatingips "github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/layer3/floatingips"
33+
"github.com/gophercloud/gophercloud/v2/openstack/networking/v2/ports"
3234
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
35+
"k8s.io/kops/cloudmock/openstack/mocknetworking"
3336
"k8s.io/kops/pkg/apis/kops"
3437
"k8s.io/kops/upup/pkg/fi"
3538
"k8s.io/kops/util/pkg/vfs"
@@ -662,3 +665,114 @@ func Test_BuildClients(t *testing.T) {
662665
})
663666
}
664667
}
668+
669+
func setupMockCloudForDeletePortsTest(portDefinitions map[string]map[string]int) (*MockCloud, error) {
670+
cloud := InstallMockOpenstackCloud("mock-central-1")
671+
cloud.MockNeutronClient = mocknetworking.CreateClient()
672+
673+
for clusterName, instanceGroups := range portDefinitions {
674+
for instanceGroup, n := range instanceGroups {
675+
for i := 0; i < n; i++ {
676+
port, err := cloud.CreatePort(ports.CreateOpts{
677+
Name: fmt.Sprintf("port-%s-%d-%s", instanceGroup, i+1, clusterName),
678+
NetworkID: "mock-network-id",
679+
})
680+
if err != nil {
681+
return nil, fmt.Errorf("error creating port: %v", err)
682+
}
683+
684+
err = cloud.AppendTag(ResourceTypePort, port.ID, fmt.Sprintf("%s=%s", TagClusterName, clusterName))
685+
if err != nil {
686+
return nil, fmt.Errorf("error appending tag: %v", err)
687+
}
688+
err = cloud.AppendTag(ResourceTypePort, port.ID, fmt.Sprintf("%s=%s", TagKopsInstanceGroup, instanceGroup))
689+
if err != nil {
690+
return nil, fmt.Errorf("error appending tag: %v", err)
691+
}
692+
}
693+
}
694+
}
695+
696+
return cloud, nil
697+
}
698+
699+
func Test_deletePorts(t *testing.T) {
700+
testCases := []struct {
701+
description string
702+
clusterName string
703+
instanceGroup string
704+
expectedPorts []string
705+
}{
706+
{
707+
description: "Only delete ports of worker IG of my-cluster",
708+
clusterName: "my-cluster",
709+
instanceGroup: "worker",
710+
expectedPorts: []string{
711+
"port-control-plane-0-1-my-cluster",
712+
"port-worker-2-1-my-cluster",
713+
"port-worker-2-2-my-cluster",
714+
"port-control-plane-0-1-my-cluster-2",
715+
"port-worker-1-my-cluster-2",
716+
"port-worker-2-my-cluster-2",
717+
"port-worker-2-1-my-cluster-2",
718+
"port-worker-2-2-my-cluster-2",
719+
},
720+
},
721+
{
722+
description: "Only delete ports of worker-2 IG of my-cluster",
723+
clusterName: "my-cluster",
724+
instanceGroup: "worker-2",
725+
expectedPorts: []string{
726+
"port-control-plane-0-1-my-cluster",
727+
"port-worker-1-my-cluster",
728+
"port-worker-2-my-cluster",
729+
"port-control-plane-0-1-my-cluster-2",
730+
"port-worker-1-my-cluster-2",
731+
"port-worker-2-my-cluster-2",
732+
"port-worker-2-1-my-cluster-2",
733+
"port-worker-2-2-my-cluster-2",
734+
},
735+
},
736+
}
737+
738+
portDefinitions := map[string]map[string]int{
739+
"my-cluster": map[string]int{
740+
"control-plane-0": 1,
741+
"worker": 2,
742+
"worker-2": 2,
743+
},
744+
"my-cluster-2": map[string]int{
745+
"control-plane-0": 1,
746+
"worker": 2,
747+
"worker-2": 2,
748+
},
749+
}
750+
751+
for _, testCase := range testCases {
752+
t.Run(testCase.description, func(t *testing.T) {
753+
cloud, err := setupMockCloudForDeletePortsTest(portDefinitions)
754+
if err != nil {
755+
t.Errorf("error while setting up test: %v", err)
756+
}
757+
758+
deletePorts(cloud, testCase.instanceGroup, testCase.clusterName)
759+
760+
allPorts, err := cloud.ListPorts(ports.ListOpts{})
761+
if err != nil {
762+
t.Errorf("error while listing ports: %v", err)
763+
}
764+
765+
actualPorts := []string{}
766+
for _, port := range allPorts {
767+
actualPorts = append(actualPorts, port.Name)
768+
}
769+
770+
slices.Sort(actualPorts)
771+
slices.Sort(testCase.expectedPorts)
772+
773+
if !reflect.DeepEqual(actualPorts, testCase.expectedPorts) {
774+
t.Errorf("ports differ: expected\n%+#v\n\tgot:\n%+#v\n", testCase.expectedPorts, actualPorts)
775+
}
776+
})
777+
}
778+
}

0 commit comments

Comments
 (0)