Skip to content

Commit c684b7e

Browse files
committed
add e2e tests
Signed-off-by: Zhiying Lin <[email protected]>
1 parent 89b1a10 commit c684b7e

File tree

5 files changed

+178
-3
lines changed

5 files changed

+178
-3
lines changed

.github/workflows/ci.yml

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,16 @@ jobs:
8686
HUB_SERVER_URL: 'https://172.19.0.2:6443'
8787

8888
e2e-tests:
89+
strategy:
90+
fail-fast: false
91+
matrix:
92+
customized-settings: [default, custom]
93+
include:
94+
- customized-settings: default
95+
# to shorten the test duration, set the resource snapshot creation interval to 0
96+
resource-snapshot-creation-interval: 0m
97+
- customized-settings: custom
98+
resource-snapshot-creation-interval: 1m
8999
runs-on: ubuntu-latest
90100
needs: [
91101
detect-noop,
@@ -119,7 +129,11 @@ jobs:
119129
120130
- name: Run e2e tests
121131
run: |
122-
make e2e-tests
132+
if [ "${{ matrix.customized-settings }}" = "default" ]; then
133+
make e2e-tests
134+
else
135+
make e2e-tests-custom
136+
fi
123137
env:
124138
KUBECONFIG: '/home/runner/.kube/config'
125139
HUB_SERVER_URL: 'https://172.19.0.2:6443'
@@ -129,4 +143,5 @@ jobs:
129143
# TO-DO (chenyu1): to ensure a vendor-neutral experience, switch to a dummy
130144
# property provider once the AKS one is split out.
131145
PROPERTY_PROVIDER: 'azure'
146+
RESOURCE_SNAPSHOT_CREATION_INTERVAL: ${{ matrix.resource-snapshot-creation-interval }}
132147

Makefile

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -213,7 +213,10 @@ e2e-tests-v1alpha1: create-kind-cluster run-e2e-v1alpha1
213213

214214
.PHONY: e2e-tests
215215
e2e-tests: setup-clusters
216-
cd ./test/e2e && ginkgo -v -p .
216+
cd ./test/e2e && ginkgo -v -p . --label-filter="!custom"
217+
218+
e2e-tests-custom: setup-clusters
219+
cd ./test/e2e && ginkgo -v -p . --label-filter="custom"
217220

218221
.PHONY: setup-clusters
219222
setup-clusters:
Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
/*
2+
Copyright 2025 The KubeFleet 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+
package e2e
17+
18+
import (
19+
"fmt"
20+
"math"
21+
"time"
22+
23+
. "github.com/onsi/ginkgo/v2"
24+
. "github.com/onsi/gomega"
25+
corev1 "k8s.io/api/core/v1"
26+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
27+
"k8s.io/apimachinery/pkg/types"
28+
"k8s.io/utils/ptr"
29+
"sigs.k8s.io/controller-runtime/pkg/client"
30+
31+
placementv1beta1 "github.com/kubefleet-dev/kubefleet/apis/placement/v1beta1"
32+
)
33+
34+
var _ = Describe("validating CRP when using customized resourceSnapshotCreationInterval", Label("custom"), Ordered, func() {
35+
// skip entire suite if interval is zero
36+
BeforeAll(func() {
37+
if resourceSnapshotCreationInterval == 0 {
38+
Skip("Skipping customized-config placement test when RESOURCE_SNAPSHOT_CREATION_INTERVAL=0m")
39+
}
40+
})
41+
42+
crpName := fmt.Sprintf(crpNameTemplate, GinkgoParallelProcess())
43+
44+
BeforeAll(func() {
45+
By("creating work resources")
46+
createWorkResources()
47+
48+
// Create the CRP.
49+
crp := &placementv1beta1.ClusterResourcePlacement{
50+
ObjectMeta: metav1.ObjectMeta{
51+
Name: crpName,
52+
// Add a custom finalizer; this would allow us to better observe
53+
// the behavior of the controllers.
54+
Finalizers: []string{customDeletionBlockerFinalizer},
55+
},
56+
Spec: placementv1beta1.PlacementSpec{
57+
ResourceSelectors: []placementv1beta1.ClusterResourceSelector{
58+
{
59+
Group: "",
60+
Kind: "Namespace",
61+
Version: "v1",
62+
LabelSelector: &metav1.LabelSelector{
63+
MatchLabels: map[string]string{
64+
workNamespaceLabelName: fmt.Sprintf("test-%d", GinkgoParallelProcess()),
65+
},
66+
},
67+
},
68+
},
69+
Strategy: placementv1beta1.RolloutStrategy{
70+
RollingUpdate: &placementv1beta1.RollingUpdateConfig{
71+
UnavailablePeriodSeconds: ptr.To(5),
72+
},
73+
},
74+
},
75+
}
76+
By(fmt.Sprintf("creating placement %s", crpName))
77+
Expect(hubClient.Create(ctx, crp)).To(Succeed(), "Failed to create CRP %s", crpName)
78+
})
79+
80+
AfterAll(func() {
81+
By(fmt.Sprintf("garbage all things related to placement %s", crpName))
82+
ensureCRPAndRelatedResourcesDeleted(crpName, allMemberClusters)
83+
})
84+
85+
It("should update CRP status as expected", func() {
86+
crpStatusUpdatedActual := crpStatusUpdatedActual([]placementv1beta1.ResourceIdentifier{}, allMemberClusterNames, nil, "0")
87+
Eventually(crpStatusUpdatedActual, eventuallyDuration, eventuallyInterval).Should(Succeed(), "Failed to update CRP %s status as expected", crpName)
88+
})
89+
90+
It("should not place work resources on member clusters", checkIfRemovedWorkResourcesFromAllMemberClusters)
91+
92+
It("updating the resources on the hub and the namespace becomes selected", func() {
93+
workNamespaceName := fmt.Sprintf(workNamespaceNameTemplate, GinkgoParallelProcess())
94+
ns := &corev1.Namespace{}
95+
Expect(hubClient.Get(ctx, types.NamespacedName{Name: workNamespaceName}, ns)).Should(Succeed(), "Failed to get the namespace %s", workNamespaceName)
96+
ns.Labels = map[string]string{
97+
workNamespaceLabelName: fmt.Sprintf("test-%d", GinkgoParallelProcess()),
98+
}
99+
Expect(hubClient.Update(ctx, ns)).Should(Succeed(), "Failed to update namespace %s", workNamespaceName)
100+
})
101+
102+
It("should not update CRP status immeidately", func() {
103+
crpStatusUpdatedActual := crpStatusUpdatedActual([]placementv1beta1.ResourceIdentifier{}, allMemberClusterNames, nil, "0")
104+
Consistently(crpStatusUpdatedActual, resourceSnapshotCreationInterval-3*time.Second, consistentlyInterval).Should(Succeed(), "CRP %s status should be unchanged", crpName)
105+
})
106+
107+
It("should update CRP status as expected", func() {
108+
crpStatusUpdatedActual := crpStatusUpdatedActual(workResourceIdentifiers(), allMemberClusterNames, nil, "1")
109+
Eventually(crpStatusUpdatedActual, eventuallyDuration, eventuallyInterval).Should(Succeed(), "Failed to update CRP %s status as expected", crpName)
110+
})
111+
112+
It("should place the selected resources on member clusters", checkIfPlacedWorkResourcesOnAllMemberClusters)
113+
114+
It("validating the clusterResourceSnapshots are created", func() {
115+
var resourceSnapshotList placementv1beta1.ClusterResourceSnapshotList
116+
masterResourceSnapshotLabels := client.MatchingLabels{
117+
placementv1beta1.CRPTrackingLabel: crpName,
118+
}
119+
Expect(hubClient.List(ctx, &resourceSnapshotList, masterResourceSnapshotLabels)).Should(Succeed(), "Failed to list ClusterResourceSnapshots for CRP %s", crpName)
120+
Expect(len(resourceSnapshotList.Items)).Should(Equal(2), "Expected 2 ClusterResourceSnapshots for CRP %s, got %d", crpName, len(resourceSnapshotList.Items))
121+
// Use math.Abs to get the absolute value of the time difference in seconds.
122+
snapshotDiffInSeconds := resourceSnapshotList.Items[0].CreationTimestamp.Time.Sub(resourceSnapshotList.Items[1].CreationTimestamp.Time).Seconds()
123+
diff := math.Abs(snapshotDiffInSeconds)
124+
Expect(time.Duration(diff)*time.Second >= resourceSnapshotCreationInterval).To(BeTrue(), "The time difference between ClusterResourceSnapshots should be more than resourceSnapshotCreationInterval")
125+
})
126+
127+
It("can delete the CRP", func() {
128+
// Delete the CRP.
129+
crp := &placementv1beta1.ClusterResourcePlacement{
130+
ObjectMeta: metav1.ObjectMeta{
131+
Name: crpName,
132+
},
133+
}
134+
Expect(hubClient.Delete(ctx, crp)).To(Succeed(), "Failed to delete CRP %s", crpName)
135+
})
136+
137+
It("should remove placed resources from all member clusters", checkIfRemovedWorkResourcesFromAllMemberClusters)
138+
139+
It("should remove controller finalizers from CRP", func() {
140+
finalizerRemovedActual := allFinalizersExceptForCustomDeletionBlockerRemovedFromCRPActual(crpName)
141+
Eventually(finalizerRemovedActual, eventuallyDuration, eventuallyInterval).Should(Succeed(), "Failed to remove controller finalizers from CRP %s", crpName)
142+
})
143+
})

test/e2e/setup.sh

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ export MEMBER_AGENT_IMAGE="${MEMBER_AGENT_IMAGE:-member-agent}"
2626
export REFRESH_TOKEN_IMAGE="${REFRESH_TOKEN_IMAGE:-refresh-token}"
2727
export PROPERTY_PROVIDER="${PROPERTY_PROVIDER:-azure}"
2828
export USE_PREDEFINED_REGIONS="${USE_PREDEFINED_REGIONS:-false}"
29+
export RESOURCE_SNAPSHOT_CREATION_INTERVAL="${RESOURCE_SNAPSHOT_CREATION_INTERVAL:-0m}"
30+
2931
# The pre-defined regions; if the AKS property provider is used.
3032
#
3133
# Note that for a specific cluster, if a predefined region is not set, the node region must
@@ -124,7 +126,7 @@ helm install hub-agent ../../charts/hub-agent/ \
124126
--set forceDeleteWaitTime="1m0s" \
125127
--set clusterUnhealthyThreshold="3m0s" \
126128
--set logFileMaxSize=1000000 \
127-
--set resourceSnapshotCreationInterval="0s"
129+
--set resourceSnapshotCreationInterval=$RESOURCE_SNAPSHOT_CREATION_INTERVAL
128130

129131
# Download CRDs from Fleet networking repo
130132
export ENDPOINT_SLICE_EXPORT_CRD_URL=https://raw.githubusercontent.com/Azure/fleet-networking/v0.2.7/config/crd/bases/networking.fleet.azure.com_endpointsliceexports.yaml

test/e2e/setup_test.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,8 @@ var (
112112

113113
allMemberClusters []*framework.Cluster
114114
allMemberClusterNames = []string{}
115+
116+
resourceSnapshotCreationInterval time.Duration
115117
)
116118

117119
var (
@@ -301,6 +303,16 @@ func beforeSuiteForAllProcesses() {
301303
// Check if the required environment variable, which specifies the path to kubeconfig file, has been set.
302304
Expect(os.Getenv(kubeConfigPathEnvVarName)).NotTo(BeEmpty(), "Required environment variable KUBECONFIG is not set")
303305

306+
resourceSnapshotCreationIntervalEnv := os.Getenv("RESOURCE_SNAPSHOT_CREATION_INTERVAL")
307+
if resourceSnapshotCreationIntervalEnv == "" {
308+
// If the environment variable is not set, use a default value.
309+
resourceSnapshotCreationInterval = 0
310+
} else {
311+
var err error
312+
resourceSnapshotCreationInterval, err = time.ParseDuration(resourceSnapshotCreationIntervalEnv)
313+
Expect(err).Should(Succeed(), "failed to parse RESOURCE_SNAPSHOT_CREATION_INTERVAL")
314+
}
315+
304316
// Initialize the cluster objects and their clients.
305317
hubCluster = framework.NewCluster(hubClusterName, "", scheme, nil)
306318
Expect(hubCluster).NotTo(BeNil(), "Failed to initialize cluster object")

0 commit comments

Comments
 (0)