Skip to content

Commit 926bbaa

Browse files
committed
Ensure E2E cleanup
Signed-off-by: Huy Mai <[email protected]>
1 parent acc680d commit 926bbaa

File tree

3 files changed

+162
-1
lines changed

3 files changed

+162
-1
lines changed

test/e2e/shared/openstack.go

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ import (
3939
"github.com/gophercloud/gophercloud/openstack/compute/v2/flavors"
4040
"github.com/gophercloud/gophercloud/openstack/compute/v2/servers"
4141
"github.com/gophercloud/gophercloud/openstack/imageservice/v2/images"
42+
"github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/loadbalancers"
4243
"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/layer3/routers"
4344
"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/security/groups"
4445
"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/security/rules"
@@ -865,3 +866,28 @@ func GetFlavorFromName(e2eCtx *E2EContext, name string) (*flavors.Flavor, error)
865866

866867
return flavors.Get(computeClient, flavorID).Extract()
867868
}
869+
870+
func DumpOpenStackLoadBalancers(e2eCtx *E2EContext, filter loadbalancers.ListOpts) ([]loadbalancers.LoadBalancer, error) {
871+
providerClient, clientOpts, _, err := GetTenantProviderClient(e2eCtx)
872+
if err != nil {
873+
_, _ = fmt.Fprintf(GinkgoWriter, "error creating provider client: %s\n", err)
874+
return nil, err
875+
}
876+
877+
loadBalancerClient, err := openstack.NewLoadBalancerV2(providerClient, gophercloud.EndpointOpts{
878+
Region: clientOpts.RegionName,
879+
})
880+
if err != nil {
881+
return nil, fmt.Errorf("error creating network client: %s", err)
882+
}
883+
884+
allPages, err := loadbalancers.List(loadBalancerClient, filter).AllPages()
885+
if err != nil {
886+
return nil, fmt.Errorf("error getting load balancers: %s", err)
887+
}
888+
loadBalancersList, err := loadbalancers.ExtractLoadBalancers(allPages)
889+
if err != nil {
890+
return nil, fmt.Errorf("error getting load balancers: %s", err)
891+
}
892+
return loadBalancersList, nil
893+
}

test/e2e/suites/e2e/e2e_suite_test.go

Lines changed: 61 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,15 +23,29 @@ import (
2323
"os"
2424
"testing"
2525

26+
"github.com/gophercloud/gophercloud/openstack/blockstorage/v3/volumes"
27+
"github.com/gophercloud/gophercloud/openstack/compute/v2/servers"
28+
"github.com/gophercloud/gophercloud/openstack/loadbalancer/v2/loadbalancers"
29+
"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/security/groups"
30+
"github.com/gophercloud/gophercloud/openstack/networking/v2/networks"
2631
. "github.com/onsi/ginkgo/v2"
2732
. "github.com/onsi/gomega"
2833
"k8s.io/klog/v2"
34+
"k8s.io/utils/pointer"
2935
ctrl "sigs.k8s.io/controller-runtime"
3036

3137
"sigs.k8s.io/cluster-api-provider-openstack/test/e2e/shared"
3238
)
3339

34-
var e2eCtx *shared.E2EContext
40+
var (
41+
e2eCtx *shared.E2EContext
42+
initialServers []servers.Server
43+
initialNetworks []networks.Network
44+
initialSecurityGroups []groups.SecGroup
45+
initialLoadBalancers []loadbalancers.LoadBalancer
46+
initialVolumes []volumes.Volume
47+
err error
48+
)
3549

3650
func init() {
3751
e2eCtx = shared.NewE2EContext()
@@ -55,10 +69,56 @@ var _ = SynchronizedBeforeSuite(func() []byte {
5569
return data
5670
}, func(data []byte) {
5771
shared.AllNodesBeforeSuite(e2eCtx, data)
72+
initialServers, err = shared.DumpOpenStackServers(e2eCtx, servers.ListOpts{})
73+
Expect(err).NotTo(HaveOccurred())
74+
initialNetworks, err = shared.DumpOpenStackNetworks(e2eCtx, networks.ListOpts{})
75+
Expect(err).NotTo(HaveOccurred())
76+
initialSecurityGroups, err = shared.DumpOpenStackSecurityGroups(e2eCtx, groups.ListOpts{})
77+
Expect(err).NotTo(HaveOccurred())
78+
initialLoadBalancers, err = shared.DumpOpenStackLoadBalancers(e2eCtx, loadbalancers.ListOpts{})
79+
Expect(err).NotTo(HaveOccurred())
80+
initialVolumes, err = shared.DumpOpenStackVolumes(e2eCtx, volumes.ListOpts{})
81+
Expect(err).NotTo(HaveOccurred())
5882
})
5983

84+
func CheckResourceCleanup[T any, L any](f func(*shared.E2EContext, L) ([]T, error), l L, initialResources []T) *string {
85+
endResources, err := f(e2eCtx, l)
86+
if err != nil {
87+
return pointer.String(err.Error())
88+
}
89+
90+
matcher := ConsistOfIDs(initialResources)
91+
success, err := matcher.Match(endResources)
92+
if err != nil {
93+
return pointer.String(err.Error())
94+
}
95+
if !success {
96+
return pointer.String(matcher.FailureMessage(endResources))
97+
}
98+
99+
return nil
100+
}
101+
60102
var _ = SynchronizedAfterSuite(func() {
61103
shared.AllNodesAfterSuite(e2eCtx)
62104
}, func() {
105+
failed := false
106+
for _, error := range []*string{
107+
CheckResourceCleanup(shared.DumpOpenStackServers, servers.ListOpts{}, initialServers),
108+
CheckResourceCleanup(shared.DumpOpenStackNetworks, networks.ListOpts{}, initialNetworks),
109+
CheckResourceCleanup(shared.DumpOpenStackSecurityGroups, groups.ListOpts{}, initialSecurityGroups),
110+
CheckResourceCleanup(shared.DumpOpenStackLoadBalancers, loadbalancers.ListOpts{}, initialLoadBalancers),
111+
CheckResourceCleanup(shared.DumpOpenStackVolumes, volumes.ListOpts{}, initialVolumes),
112+
} {
113+
if error != nil {
114+
GinkgoWriter.Println(*error)
115+
failed = true
116+
}
117+
}
118+
63119
shared.Node1AfterSuite(e2eCtx)
120+
121+
if failed {
122+
Fail("Not all resources were cleaned up")
123+
}
64124
})

test/e2e/suites/e2e/idmatcher_test.go

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
/*
2+
Copyright 2021 The Kubernetes Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package e2e
18+
19+
import (
20+
"fmt"
21+
"reflect"
22+
23+
"github.com/onsi/gomega"
24+
"github.com/onsi/gomega/format"
25+
"github.com/onsi/gomega/gstruct"
26+
"github.com/onsi/gomega/types"
27+
)
28+
29+
type idMatcher struct {
30+
expected interface{}
31+
}
32+
33+
var (
34+
_ types.GomegaMatcher = &idMatcher{}
35+
_ format.GomegaStringer = &idMatcher{}
36+
)
37+
38+
func (m *idMatcher) Match(actual interface{}) (bool, error) {
39+
v := reflect.ValueOf(m.expected)
40+
id := v.FieldByName("ID").String()
41+
42+
matcher := &gstruct.FieldsMatcher{
43+
Fields: gstruct.Fields{
44+
"ID": gomega.Equal(id),
45+
},
46+
IgnoreExtras: true,
47+
}
48+
49+
return matcher.Match(actual)
50+
}
51+
52+
func (m *idMatcher) FailureMessage(actual interface{}) string {
53+
return fmt.Sprintf("Expected:\n%s\nto have the same ID as:\n%s", format.Object(actual, 1), format.Object(m.expected, 1))
54+
}
55+
56+
func (m *idMatcher) NegatedFailureMessage(actual interface{}) string {
57+
return fmt.Sprintf("Expected:\n%s\nnot to have the same ID as:\n%s", format.Object(actual, 1), format.Object(m.expected, 1))
58+
}
59+
60+
func (m *idMatcher) GomegaString() string {
61+
return fmt.Sprintf("ID match for:\n%s", format.Object(m.expected, 1))
62+
}
63+
64+
func IDOf(expected interface{}) types.GomegaMatcher {
65+
return &idMatcher{expected: expected}
66+
}
67+
68+
func ConsistOfIDs[T any](expected []T) types.GomegaMatcher {
69+
matchers := make([]types.GomegaMatcher, len(expected))
70+
for i := range expected {
71+
matchers[i] = IDOf(expected[i])
72+
}
73+
74+
return gomega.ConsistOf(matchers)
75+
}

0 commit comments

Comments
 (0)