Skip to content

Commit df1053d

Browse files
committed
feat(tests): add warm up tests
Signed-off-by: l1b0k <[email protected]>
1 parent 47557e0 commit df1053d

File tree

9 files changed

+829
-98
lines changed

9 files changed

+829
-98
lines changed

docs/warm-up.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ The warm-up size can be configured through the `eni-config` ConfigMap:
2626
**Important**: The `ip_warm_up_size` is independent of `min_pool_size` and `max_pool_size`. It can be set to a value larger or smaller than the pool size limits.
2727

2828
However, setting `ip_warm_up_size` larger than `max_pool_size` is **not recommended** because:
29+
2930
- The warm-up process will allocate IPs up to `ip_warm_up_size`
3031
- After warm-up completes, the pool management and idle IP reclaim policy will release excess IPs to maintain the pool within `min_pool_size` and `max_pool_size` boundaries
3132
- This results in unnecessary IP allocation and deallocation, wasting resources and API calls
@@ -42,6 +43,7 @@ However, setting `ip_warm_up_size` larger than `max_pool_size` is **not recommen
4243
```
4344

4445
In this example:
46+
4547
- On node startup, 15 IPs will be pre-allocated (warm-up)
4648
- The pool will maintain between 5-20 IPs during normal operation
4749
- Since `ip_warm_up_size` (15) is within the pool size range (5-20), all pre-allocated IPs will be retained
@@ -58,6 +60,7 @@ In this example:
5860
```
5961

6062
In this example:
63+
6164
- On node startup, 20 IPs will be pre-allocated (warm-up)
6265
- After warm-up completes, 10 IPs will be released because `max_pool_size` is only 10
6366
- This causes unnecessary IP churn and API calls
@@ -94,4 +97,3 @@ status:
9497
- Warm-up only runs once per node lifecycle (when the node first joins the cluster)
9598
- If a node already has warm-up status initialized, changing `ip_warm_up_size` will not affect the ongoing warm-up
9699
- Warm-up progress is tracked independently of actual IP usage, ensuring consistent behavior even if IPs are allocated/deallocated during warm-up
97-

tests/config_test.go

Lines changed: 6 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -34,22 +34,13 @@ func TestIPPool(t *testing.T) {
3434
}
3535

3636
// Check terway daemonset name is terway-eniip
37-
isTerwayENIIP, err := CheckTerwayDaemonSetName(ctx, config, "terway-eniip")
38-
if err != nil {
39-
t.Fatalf("failed to check terway daemonset name: %v", err)
40-
}
41-
if !isTerwayENIIP {
42-
t.Skipf("TestIPPool requires terway-eniip daemonset")
37+
if GetCachedTerwayDaemonSetName() != "terway-eniip" {
38+
t.Skipf("TestIPPool requires terway-eniip daemonset, current: %s", GetCachedTerwayDaemonSetName())
4339
}
4440

4541
// Check terway version >= v1.16.1
46-
versionOK, err := CheckTerwayVersion(ctx, config, "v1.16.1")
47-
if err != nil {
48-
t.Fatalf("failed to check terway version: %v", err)
49-
}
50-
if !versionOK {
51-
terwayVersion, _ := GetTerwayVersion(ctx, config)
52-
t.Skipf("TestIPPool requires terway version >= v1.16.1, current version: %s", terwayVersion)
42+
if !RequireTerwayVersion("v1.16.1") {
43+
t.Skipf("TestIPPool requires terway version >= v1.16.1, current version: %s", GetCachedTerwayVersion())
5344
}
5445

5546
return ctx
@@ -269,6 +260,8 @@ func TestIPPool(t *testing.T) {
269260
}
270261
if idle > 0 {
271262
t.Logf("node %v has %v idle ip", node.Name, idle)
263+
_ = triggerNode(ctx, config, t, &node)
264+
272265
return false, nil
273266
}
274267
}

tests/connective_test.go

Lines changed: 9 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ import (
1010
"testing"
1111
"time"
1212

13-
"golang.org/x/mod/semver"
1413
corev1 "k8s.io/api/core/v1"
1514
networkingv1 "k8s.io/api/networking/v1"
1615
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -578,25 +577,13 @@ func TestNormal_HostPort(t *testing.T) {
578577
hostPortNodeIP := features.New(fmt.Sprintf("HostPort/NodeIP-%s", name)).
579578
Setup(func(ctx context.Context, t *testing.T, config *envconf.Config) context.Context {
580579
// Check terway version >= v1.15.0
581-
versionOK, err := CheckTerwayVersion(ctx, config, "v1.15.0")
582-
if err != nil {
583-
t.Fatalf("failed to check terway version: %v", err)
584-
}
585-
if !versionOK {
586-
terwayVersion, _ := GetTerwayVersion(ctx, config)
587-
t.Skipf("TestNormal_HostPort requires terway version >= v1.15.0, current version: %s", terwayVersion)
580+
if !RequireTerwayVersion("v1.15.0") {
581+
t.Skipf("TestNormal_HostPort requires terway version >= v1.15.0, current version: %s", GetCachedTerwayVersion())
588582
}
589583

590584
// Check k8s version >= v1.34.0
591-
k8sVersion, err := GetK8sVersion(ctx, config)
592-
if err != nil {
593-
t.Fatalf("failed to get k8s version: %v", err)
594-
}
595-
if !semver.IsValid(k8sVersion) {
596-
t.Fatalf("invalid k8s version: %s", k8sVersion)
597-
}
598-
if semver.Compare(k8sVersion, "v1.34.0") < 0 {
599-
t.Skipf("TestNormal_HostPort requires k8s version >= v1.34.0, current version: %s", k8sVersion)
585+
if !RequireK8sVersion("v1.34.0") {
586+
t.Skipf("TestNormal_HostPort requires k8s version >= v1.34.0, current version: %s", GetCachedK8sVersion())
600587
}
601588

602589
// Check if nodes have no-kube-proxy label (kube-proxy replacement / Datapath V2)
@@ -726,30 +713,18 @@ func TestNormal_HostPort(t *testing.T) {
726713
hostPortExternalIP := features.New(fmt.Sprintf("HostPort/ExternalIP-%s", name)).
727714
Setup(func(ctx context.Context, t *testing.T, config *envconf.Config) context.Context {
728715
// Check terway version >= v1.15.0
729-
versionOK, err := CheckTerwayVersion(ctx, config, "v1.15.0")
730-
if err != nil {
731-
t.Fatalf("failed to check terway version: %v", err)
732-
}
733-
if !versionOK {
734-
terwayVersion, _ := GetTerwayVersion(ctx, config)
735-
t.Skipf("TestNormal_HostPort requires terway version >= v1.15.0, current version: %s", terwayVersion)
716+
if !RequireTerwayVersion("v1.15.0") {
717+
t.Skipf("TestNormal_HostPort requires terway version >= v1.15.0, current version: %s", GetCachedTerwayVersion())
736718
}
737719

738720
// Check k8s version >= v1.34.0
739-
k8sVersion, err := GetK8sVersion(ctx, config)
740-
if err != nil {
741-
t.Fatalf("failed to get k8s version: %v", err)
742-
}
743-
if !semver.IsValid(k8sVersion) {
744-
t.Fatalf("invalid k8s version: %s", k8sVersion)
745-
}
746-
if semver.Compare(k8sVersion, "v1.34.0") < 0 {
747-
t.Skipf("TestNormal_HostPort requires k8s version >= v1.34.0, current version: %s", k8sVersion)
721+
if !RequireK8sVersion("v1.34.0") {
722+
t.Skipf("TestNormal_HostPort requires k8s version >= v1.34.0, current version: %s", GetCachedK8sVersion())
748723
}
749724

750725
// Check if any node has external IP before running the test
751726
nodes := &corev1.NodeList{}
752-
err = config.Client().Resources().List(ctx, nodes)
727+
err := config.Client().Resources().List(ctx, nodes)
753728
if err != nil {
754729
t.Fatalf("failed to list nodes: %v", err)
755730
}

tests/idle_ip_reclaim_test.go

Lines changed: 22 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -74,34 +74,32 @@ func cleanupPoolConfig(ctx context.Context, t *testing.T, config *envconf.Config
7474
// TestIdleIPReclaimPolicy tests the idle IP reclaim policy feature
7575
// This test only covers centralized IPAM mode (ipam_type == "crd")
7676
func TestIdleIPReclaimPolicy(t *testing.T) {
77-
feature := features.New("IdleIPReclaimPolicy").
78-
Setup(func(ctx context.Context, t *testing.T, config *envconf.Config) context.Context {
79-
// Only test centralized IPAM mode
80-
if eniConfig.IPAMType != "crd" {
81-
t.Skipf("skip: ipam type is not crd, current type: %s", eniConfig.IPAMType)
82-
}
77+
// Pre-check: only test centralized IPAM mode
78+
if eniConfig == nil || eniConfig.IPAMType != "crd" {
79+
ipamType := ""
80+
if eniConfig != nil {
81+
ipamType = eniConfig.IPAMType
82+
}
83+
t.Skipf("skip: ipam type is not crd, current type: %s", ipamType)
84+
return
85+
}
8386

84-
// Check terway daemonset name is terway-eniip
85-
isTerwayENIIP, err := CheckTerwayDaemonSetName(ctx, config, "terway-eniip")
86-
if err != nil {
87-
t.Fatalf("failed to check terway daemonset name: %v", err)
88-
}
89-
if !isTerwayENIIP {
90-
t.Skipf("TestIdleIPReclaimPolicy requires terway-eniip daemonset")
91-
}
87+
// Pre-check: terway daemonset name must be terway-eniip
88+
if terway != "terway-eniip" {
89+
t.Skipf("TestIdleIPReclaimPolicy requires terway-eniip daemonset, current: %s", terway)
90+
return
91+
}
9292

93-
// Check terway version >= v1.16.1
94-
versionOK, err := CheckTerwayVersion(ctx, config, "v1.16.1")
95-
if err != nil {
96-
t.Fatalf("failed to check terway version: %v", err)
97-
}
98-
if !versionOK {
99-
terwayVersion, _ := GetTerwayVersion(ctx, config)
100-
t.Skipf("TestIdleIPReclaimPolicy requires terway version >= v1.16.1, current version: %s", terwayVersion)
101-
}
93+
// Pre-check: terway version must be >= v1.16.1
94+
if !RequireTerwayVersion("v1.16.1") {
95+
t.Skipf("TestIdleIPReclaimPolicy requires terway version >= v1.16.1, current version: %s", GetCachedTerwayVersion())
96+
return
97+
}
10298

99+
feature := features.New("IdleIPReclaimPolicy").
100+
Setup(func(ctx context.Context, t *testing.T, config *envconf.Config) context.Context {
103101
// Cleanup pool configuration before tests
104-
err = cleanupPoolConfig(ctx, t, config)
102+
err := cleanupPoolConfig(ctx, t, config)
105103
if err != nil {
106104
t.Fatalf("failed to cleanup pool configuration: %v", err)
107105
}

tests/main_test.go

Lines changed: 39 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ var (
5151

5252
terway string
5353
terwayVersion string
54+
k8sVersion string
5455

5556
vSwitchIDs string
5657
securityGroupIDs string
@@ -107,10 +108,16 @@ func TestMain(m *testing.M) {
107108
checkENIConfig,
108109
)
109110
testenv.AfterEachFeature(func(ctx context.Context, config *envconf.Config, t *testing.T, feature features.Feature) (context.Context, error) {
111+
// If test was skipped, don't do anything
112+
if t.Skipped() {
113+
t.Log("Test was skipped, cleaning up")
114+
return ctx, nil
115+
}
116+
110117
pod := &corev1.PodList{}
111118
err = config.Client().Resources(envCfg.Namespace()).List(ctx, pod)
112119
t.Log("---------list pods---------")
113-
// 遍历 Pod 列表,筛选出非 Running 状态的 Pod
120+
// Filter pods that are not in Running state
114121
isTestFailed := false
115122
for _, printPod := range pod.Items {
116123
if printPod.Status.Phase != corev1.PodRunning {
@@ -120,7 +127,7 @@ func TestMain(m *testing.M) {
120127
}
121128
if isTestFailed {
122129
t.Log("---------list events---------")
123-
// 遍历 Event 列表
130+
// List events
124131
event := &corev1.EventList{}
125132
err = config.Client().Resources(envCfg.Namespace()).List(ctx, event)
126133
for _, printEvent := range event.Items {
@@ -158,13 +165,41 @@ func checkENIConfig(ctx context.Context, config *envconf.Config) (context.Contex
158165
case "terway", "terway-eni", "terway-eniip":
159166
terway = d.Name
160167

161-
tag := strings.Split(d.Spec.Template.Spec.Containers[0].Image, ":")[1]
162-
terwayVersion = tag
168+
// Get version from the first init container's image tag (more reliable)
169+
if len(d.Spec.Template.Spec.InitContainers) > 0 {
170+
image := d.Spec.Template.Spec.InitContainers[0].Image
171+
parts := strings.Split(image, ":")
172+
if len(parts) >= 2 {
173+
terwayVersion = parts[len(parts)-1]
174+
}
175+
} else if len(d.Spec.Template.Spec.Containers) > 0 {
176+
// Fallback to container image tag
177+
image := d.Spec.Template.Spec.Containers[0].Image
178+
parts := strings.Split(image, ":")
179+
if len(parts) >= 2 {
180+
terwayVersion = parts[len(parts)-1]
181+
}
182+
}
163183

164184
break
165185
}
166186
}
167187

188+
// Get k8s version from node
189+
nodes := &corev1.NodeList{}
190+
err = config.Client().Resources().List(ctx, nodes)
191+
if err != nil {
192+
return ctx, err
193+
}
194+
if len(nodes.Items) > 0 {
195+
k8sVersion = nodes.Items[0].Status.NodeInfo.KubeletVersion
196+
if !strings.HasPrefix(k8sVersion, "v") {
197+
k8sVersion = "v" + k8sVersion
198+
}
199+
}
200+
201+
fmt.Printf("Detected terway daemonset: %s, version: %s, k8s version: %s\n", terway, terwayVersion, k8sVersion)
202+
168203
// we can determine cluster config by terway eni-conifg
169204
cm := &corev1.ConfigMap{}
170205
err = config.Client().Resources().Get(ctx, "eni-config", "kube-system", cm)

tests/multi_network_test.go

Lines changed: 22 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111
"time"
1212

1313
"github.com/stretchr/testify/assert"
14+
"golang.org/x/mod/semver"
1415
corev1 "k8s.io/api/core/v1"
1516
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
1617
"sigs.k8s.io/e2e-framework/klient/wait"
@@ -66,12 +67,24 @@ func (c *MultiNetworkConfig) HasCustomConfig() bool {
6667

6768
// TestMultiNetwork tests multi-network functionality across different node types
6869
func TestMultiNetwork(t *testing.T) {
69-
// Check terway daemonset name is terway-eniip
70+
// Pre-check: terway daemonset name must be terway-eniip
7071
if terway != "terway-eniip" {
71-
t.Logf("TestMultiNetwork requires terway-eniip daemonset, current: %s, skipping", terway)
72+
t.Skipf("TestMultiNetwork requires terway-eniip daemonset, current: %s", terway)
7273
return
7374
}
7475

76+
// Pre-check: terway version must be >= v1.16.1
77+
if terwayVersion != "" {
78+
version := terwayVersion
79+
if !strings.HasPrefix(version, "v") {
80+
version = "v" + version
81+
}
82+
if semver.IsValid(version) && semver.Compare(version, "v1.16.1") < 0 {
83+
t.Skipf("TestMultiNetwork requires terway version >= v1.16.1, current: %s", terwayVersion)
84+
return
85+
}
86+
}
87+
7588
config := NewMultiNetworkConfig()
7689

7790
// Test scenarios: default mode (if enabled) + custom mode (if config available)
@@ -111,6 +124,13 @@ func TestMultiNetwork(t *testing.T) {
111124

112125
// testMultiNetworkForNodeType tests multi-network functionality for a specific node type and configuration mode
113126
func testMultiNetworkForNodeType(t *testing.T, nodeType NodeType, mode string, config *MultiNetworkConfig) {
127+
// Pre-check: skip Lingjun nodes as they are not supported yet
128+
switch nodeType {
129+
case NodeTypeLingjunSharedENI, NodeTypeLingjunExclusiveENI:
130+
t.Skipf("Lingjun nodes are not supported yet")
131+
return
132+
}
133+
114134
testName := fmt.Sprintf("MultiNetwork/%s/%s", mode, nodeType)
115135

116136
feature := createMultiNetworkTestFeature(testName, nodeType, mode, config)
@@ -126,16 +146,6 @@ func createMultiNetworkTestFeature(testName string, nodeType NodeType, mode stri
126146
return features.New(testName).
127147
WithLabel("env", "multi-network").
128148
Setup(func(ctx context.Context, t *testing.T, config *envconf.Config) context.Context {
129-
// Check terway version >= v1.16.1
130-
versionOK, err := CheckTerwayVersion(ctx, config, "v1.16.1")
131-
if err != nil {
132-
t.Fatalf("failed to check terway version: %v", err)
133-
}
134-
if !versionOK {
135-
terwayVersion, _ := GetTerwayVersion(ctx, config)
136-
t.Skipf("TestMultiNetwork requires terway version >= v1.16.1, current version: %s", terwayVersion)
137-
}
138-
139149
// Check if required node type is available
140150
nodeInfo, err := DiscoverNodeTypes(context.Background(), config.Client())
141151
if err != nil {
@@ -147,11 +157,6 @@ func createMultiNetworkTestFeature(testName string, nodeType NodeType, mode stri
147157
t.Skipf("No nodes of type %s available", nodeType)
148158
}
149159

150-
switch nodeType {
151-
case NodeTypeLingjunSharedENI, NodeTypeLingjunExclusiveENI:
152-
t.Skipf("Lingjun nodes are not supported yet")
153-
}
154-
155160
// Create primary PodNetworking (always use default config)
156161
pn1 := NewPodNetworking(pnPrimary)
157162
if nodeType == NodeTypeECSExclusiveENI || nodeType == NodeTypeLingjunExclusiveENI {

tests/pod_networking_test.go

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -218,20 +218,26 @@ func TestPodNetworking_NamespaceSelector(t *testing.T) {
218218
// Create namespace WITH matching label
219219
matchingNsObj := &corev1.Namespace{
220220
ObjectMeta: metav1.ObjectMeta{
221-
Name: matchingNs,
222-
Labels: map[string]string{"network-type": "custom"},
221+
Name: matchingNs,
222+
Labels: map[string]string{
223+
"network-type": "custom",
224+
"k8s.aliyun.com/terway-e2e": "true",
225+
},
223226
},
224227
}
225228
err = config.Client().Resources().Create(ctx, matchingNsObj)
226229
if err != nil {
227230
t.Fatalf("create matching namespace failed: %v", err)
228231
}
229232

230-
// Create namespace WITHOUT matching label
233+
// Create namespace WITHOUT matching label (for PodNetworking selector)
231234
nonMatchingNsObj := &corev1.Namespace{
232235
ObjectMeta: metav1.ObjectMeta{
233-
Name: nonMatchingNs,
234-
Labels: map[string]string{"network-type": "default"},
236+
Name: nonMatchingNs,
237+
Labels: map[string]string{
238+
"network-type": "default",
239+
"k8s.aliyun.com/terway-e2e": "true",
240+
},
235241
},
236242
}
237243
err = config.Client().Resources().Create(ctx, nonMatchingNsObj)

0 commit comments

Comments
 (0)