Skip to content

Commit

Permalink
test: add atm e2e tests (#231)
Browse files Browse the repository at this point in the history
  • Loading branch information
zhiying-lin authored Dec 13, 2024
1 parent 7a16d0c commit 8fd9033
Show file tree
Hide file tree
Showing 9 changed files with 234 additions and 13 deletions.
1 change: 1 addition & 0 deletions .github/workflows/e2e-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ jobs:
AZURE_SUBSCRIPTION_ID: ${{ secrets.E2E_AZURE_SUBSCRIPTION_ID }}
AZURE_NETWORK_SETTING: ${{ matrix.network-setting }}
AZURE_RESOURCE_GROUP: ${{ env.AZURE_RESOURCE_GROUP }}
ENABLE_TRAFFIC_MANAGER: ${{ matrix.enable-traffic-manager }}
- name: Cleanup e2e
if: always()
run: |
Expand Down
1 change: 1 addition & 0 deletions test/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ export AZURE_SUBSCRIPTION_ID=<YOUR-SUBSCRIPTION-ID>
# Available values for AZURE_NETWORK_SETTING are shared-vnet, dynamic-ip-allocation and peered-vnet,
# and detailed explanations for each network setting are provided in the scripts under folder "test/scripts".
export AZURE_NETWORK_SETTING=shared-vnet
export ENABLE_TRAFFIC_MANAGER=true
```

Run Makefile Target to setup e2e environment:
Expand Down
72 changes: 72 additions & 0 deletions test/common/trafficmanager/validator/backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
"github.com/google/go-cmp/cmp/cmpopts"
"github.com/onsi/gomega"
"k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
Expand All @@ -28,6 +29,12 @@ var (
cmpopts.SortSlices(func(s1, s2 fleetnetv1alpha1.TrafficManagerEndpointStatus) bool {
return s1.Cluster.Cluster < s2.Cluster.Cluster
}),
cmpConditionOptions,
}

cmpTrafficManagerStatusByIgnoringEndpointName = cmp.Options{
cmpConditionOptions,
cmpopts.IgnoreFields(fleetnetv1alpha1.TrafficManagerEndpointStatus{}, "Name"), // ignore the generated endpoint name
}
)

Expand All @@ -45,6 +52,71 @@ func IsTrafficManagerBackendFinalizerAdded(ctx context.Context, k8sClient client
}, timeout, interval).Should(gomega.Succeed(), "Failed to add finalizer to trafficManagerBackend %s", name)
}

// ValidateTrafficManagerBackendIfAcceptedAndIgnoringEndpointName validates the trafficManagerBackend object if it is accepted
// while ignoring the generated endpoint name.
func ValidateTrafficManagerBackendIfAcceptedAndIgnoringEndpointName(ctx context.Context, k8sClient client.Client, backendName types.NamespacedName, wantEndpoints []fleetnetv1alpha1.TrafficManagerEndpointStatus) fleetnetv1alpha1.TrafficManagerBackendStatus {
var wantStatus fleetnetv1alpha1.TrafficManagerBackendStatus
if len(wantEndpoints) == 0 {
wantStatus = fleetnetv1alpha1.TrafficManagerBackendStatus{
Conditions: []metav1.Condition{
{
Status: metav1.ConditionFalse,
Type: string(fleetnetv1alpha1.TrafficManagerBackendConditionAccepted),
Reason: string(fleetnetv1alpha1.TrafficManagerBackendReasonInvalid),
},
},
}
} else {
wantStatus = fleetnetv1alpha1.TrafficManagerBackendStatus{
Conditions: []metav1.Condition{
{
Status: metav1.ConditionTrue,
Type: string(fleetnetv1alpha1.TrafficManagerBackendConditionAccepted),
Reason: string(fleetnetv1alpha1.TrafficManagerBackendReasonAccepted),
},
},
Endpoints: wantEndpoints,
}
}

gomega.Eventually(func() error {
backend := &fleetnetv1alpha1.TrafficManagerBackend{}
if err := k8sClient.Get(ctx, backendName, backend); err != nil {
return err
}

if diff := cmp.Diff(
backend.Status,
wantStatus,
cmpTrafficManagerStatusByIgnoringEndpointName,
); diff != "" {
return fmt.Errorf("trafficManagerBackend status diff (-got, +want): %s", diff)
}
return nil
}, timeout, interval).Should(gomega.Succeed(), "Get() trafficManagerBackend status mismatch")
return wantStatus
}

// ValidateTrafficManagerBackendStatusAndIgnoringEndpointNameConsistently validates the trafficManagerBackend status consistently
// while ignoring the generated endpoint name.
func ValidateTrafficManagerBackendStatusAndIgnoringEndpointNameConsistently(ctx context.Context, k8sClient client.Client, backendName types.NamespacedName, want fleetnetv1alpha1.TrafficManagerBackendStatus) {
key := types.NamespacedName{Name: backendName.Name, Namespace: backendName.Namespace}
backend := &fleetnetv1alpha1.TrafficManagerBackend{}
gomega.Consistently(func() error {
if err := k8sClient.Get(ctx, key, backend); err != nil {
return err
}
if diff := cmp.Diff(
backend.Status,
want,
cmpTrafficManagerStatusByIgnoringEndpointName,
); diff != "" {
return fmt.Errorf("trafficManagerBackend status diff (-got, +want): %s", diff)
}
return nil
}, duration, interval).Should(gomega.Succeed(), "Get() trafficManagerBackend status mismatch")
}

// ValidateTrafficManagerBackend validates the trafficManagerBackend object.
func ValidateTrafficManagerBackend(ctx context.Context, k8sClient client.Client, want *fleetnetv1alpha1.TrafficManagerBackend) {
key := types.NamespacedName{Name: want.Name, Namespace: want.Namespace}
Expand Down
38 changes: 37 additions & 1 deletion test/common/trafficmanager/validator/profile.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import (
"k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
"k8s.io/utils/ptr"
"sigs.k8s.io/controller-runtime/pkg/client"

fleetnetv1alpha1 "go.goms.io/fleet-networking/api/v1alpha1"
Expand All @@ -32,13 +33,16 @@ var (
commonCmpOptions = cmp.Options{
cmpopts.IgnoreFields(metav1.ObjectMeta{}, "ResourceVersion", "UID", "CreationTimestamp", "ManagedFields", "Generation"),
cmpopts.IgnoreFields(metav1.OwnerReference{}, "UID"),
cmpopts.IgnoreFields(metav1.Condition{}, "Message", "LastTransitionTime", "ObservedGeneration"),
cmpopts.SortSlices(func(c1, c2 metav1.Condition) bool {
return c1.Type < c2.Type
}),
}
cmpConditionOptions = cmp.Options{
cmpopts.IgnoreFields(metav1.Condition{}, "Message", "LastTransitionTime", "ObservedGeneration"),
}
cmpTrafficManagerProfileOptions = cmp.Options{
commonCmpOptions,
cmpConditionOptions,
cmpopts.IgnoreFields(fleetnetv1alpha1.TrafficManagerProfile{}, "TypeMeta"),
}
)
Expand All @@ -58,6 +62,38 @@ func ValidateTrafficManagerProfile(ctx context.Context, k8sClient client.Client,
}, timeout, interval).Should(gomega.Succeed(), "Get() trafficManagerProfile mismatch")
}

// ValidateIfTrafficManagerProfileIsProgrammed validates the trafficManagerProfile is programmed and returns the DNSName.
func ValidateIfTrafficManagerProfileIsProgrammed(ctx context.Context, k8sClient client.Client, profileName types.NamespacedName) string {
wantDNSName := fmt.Sprintf("%s-%s.trafficmanager.net", profileName.Namespace, profileName.Name)
wantStatus := fleetnetv1alpha1.TrafficManagerProfileStatus{
DNSName: ptr.To(wantDNSName),
Conditions: []metav1.Condition{
{
Status: metav1.ConditionTrue,
Type: string(fleetnetv1alpha1.TrafficManagerProfileConditionProgrammed),
Reason: string(fleetnetv1alpha1.TrafficManagerProfileReasonProgrammed),
},
},
}

gomega.Eventually(func() error {
profile := &fleetnetv1alpha1.TrafficManagerProfile{}
if err := k8sClient.Get(ctx, profileName, profile); err != nil {
return err
}

if diff := cmp.Diff(
profile.Status,
wantStatus,
cmpConditionOptions,
); diff != "" {
return fmt.Errorf("trafficManagerProfile status diff (-got, +want): %s", diff)
}
return nil
}, timeout, interval).Should(gomega.Succeed(), "Get() trafficManagerProfile status mismatch")
return wantDNSName
}

// IsTrafficManagerProfileDeleted validates whether the profile is deleted or not.
func IsTrafficManagerProfileDeleted(ctx context.Context, k8sClient client.Client, name types.NamespacedName) {
gomega.Eventually(func() error {
Expand Down
2 changes: 1 addition & 1 deletion test/e2e/e2e_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ var (
fleet *framework.Fleet

scheme = runtime.NewScheme()
ctx = context.Background()
)

func init() {
Expand Down Expand Up @@ -92,7 +93,6 @@ var _ = AfterSuite(func() {
Name: testNamespace,
},
}
ctx := context.Background()
Expect(hubCluster.Client().Delete(ctx, &ns)).Should(Succeed(), "Failed to delete namespace %s cluster %s", testNamespace, hubClusterName)
for _, m := range memberClusters {
Expect(m.Client().Delete(ctx, &ns)).Should(Succeed(), "Failed to delete namespace %s cluster %s", testNamespace, m.Name())
Expand Down
5 changes: 0 additions & 5 deletions test/e2e/export_service_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ Licensed under the MIT license.
package e2e

import (
"context"
"fmt"
"io"
"net/http"
Expand Down Expand Up @@ -35,14 +34,10 @@ const (

var _ = Describe("Test exporting service", func() {
var (
ctx context.Context

wm *framework.WorkloadManager
)

BeforeEach(func() {
ctx = context.Background()

wm = framework.NewWorkloadManager(fleet)

By("Deploying workload")
Expand Down
39 changes: 37 additions & 2 deletions test/e2e/framework/workload_manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (
"k8s.io/apimachinery/pkg/util/intstr"
"k8s.io/apimachinery/pkg/util/wait"
"k8s.io/client-go/util/retry"
"k8s.io/utils/ptr"

fleetnetv1alpha1 "go.goms.io/fleet-networking/api/v1alpha1"
"go.goms.io/fleet-networking/pkg/common/uniquename"
Expand Down Expand Up @@ -113,7 +114,7 @@ func (wm *WorkloadManager) ServiceExport() fleetnetv1alpha1.ServiceExport {
}
}

// ServiceExport returns the MultiClusterService definition from pre-defined service name and namespace.
// MultiClusterService returns the MultiClusterService definition from pre-defined service name and namespace.
func (wm *WorkloadManager) MultiClusterService() fleetnetv1alpha1.MultiClusterService {
return fleetnetv1alpha1.MultiClusterService{
ObjectMeta: metav1.ObjectMeta{
Expand All @@ -128,6 +129,40 @@ func (wm *WorkloadManager) MultiClusterService() fleetnetv1alpha1.MultiClusterSe
}
}

// TrafficManagerProfile returns the TrafficManagerProfile definition from pre-defined service name and namespace.
func (wm *WorkloadManager) TrafficManagerProfile() fleetnetv1alpha1.TrafficManagerProfile {
return fleetnetv1alpha1.TrafficManagerProfile{
ObjectMeta: metav1.ObjectMeta{
Namespace: wm.namespace,
Name: wm.service.Name, // use the service name as the profile name
},
Spec: fleetnetv1alpha1.TrafficManagerProfileSpec{
MonitorConfig: &fleetnetv1alpha1.MonitorConfig{
Port: ptr.To(int64(80)),
},
},
}
}

// TrafficManagerBackend returns the TrafficManagerBackend definition from pre-defined service name and namespace.
func (wm *WorkloadManager) TrafficManagerBackend() fleetnetv1alpha1.TrafficManagerBackend {
return fleetnetv1alpha1.TrafficManagerBackend{
ObjectMeta: metav1.ObjectMeta{
Namespace: wm.namespace,
Name: wm.service.Name, // use the service name as the endpoint name
},
Spec: fleetnetv1alpha1.TrafficManagerBackendSpec{
Profile: fleetnetv1alpha1.TrafficManagerProfileRef{
Name: wm.service.Name,
},
Backend: fleetnetv1alpha1.TrafficManagerBackendRef{
Name: wm.service.Name,
},
Weight: ptr.To(int64(100)),
},
}
}

// Deployment returns an deployment definition base on the cluster name.
func (wm *WorkloadManager) Deployment(clusterName string) *appsv1.Deployment {
deployment := wm.deploymentTemplate
Expand Down Expand Up @@ -161,7 +196,7 @@ func (wm *WorkloadManager) DeployWorkload(ctx context.Context) error {
return nil
}

// DeployWorkload deletes workload(deployment and its service) from member clusters.
// RemoveWorkload deletes workload(deployment and its service) from member clusters.
func (wm *WorkloadManager) RemoveWorkload(ctx context.Context) error {
for _, m := range wm.Fleet.MemberClusters() {
deploymentDef := wm.Deployment(m.Name())
Expand Down
4 changes: 0 additions & 4 deletions test/e2e/join_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,6 @@ Licensed under the MIT license.
package e2e

import (
"context"

"github.com/google/go-cmp/cmp"
"github.com/google/go-cmp/cmp/cmpopts"
. "github.com/onsi/ginkgo/v2"
Expand All @@ -27,7 +25,6 @@ import (
// When member cluster leaves, it will delete all the networking resources and cause the flaky behaviors.
var _ = Describe("Test Join/Leave workflow", Serial, Ordered, func() {
var (
ctx context.Context
memberClusterName = memberClusterNames[0]
memberClusterNamespace = "fleet-member-" + memberClusterName
imcKey = types.NamespacedName{Namespace: memberClusterNamespace, Name: memberClusterName}
Expand All @@ -46,7 +43,6 @@ var _ = Describe("Test Join/Leave workflow", Serial, Ordered, func() {
Context("Member cluster agents should join/leave fleet", func() {
BeforeEach(func() {
By("Creating internalMemberCluster")
ctx = context.Background()
imc = fleetv1beta1.InternalMemberCluster{
ObjectMeta: metav1.ObjectMeta{
Name: memberClusterName,
Expand Down
85 changes: 85 additions & 0 deletions test/e2e/traffic_manager_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
/*
Copyright (c) Microsoft Corporation.
Licensed under the MIT license.
*/
package e2e

import (
"os"

. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
"k8s.io/apimachinery/pkg/types"
"sigs.k8s.io/controller-runtime/pkg/client"

fleetnetv1alpha1 "go.goms.io/fleet-networking/api/v1alpha1"
"go.goms.io/fleet-networking/test/common/trafficmanager/validator"
"go.goms.io/fleet-networking/test/e2e/framework"
)

var (
enabled = os.Getenv("ENABLE_TRAFFIC_MANAGER") == "true"
)

var _ = Describe("Test exporting service via Azure traffic manager", func() {
var wm *framework.WorkloadManager
var profile fleetnetv1alpha1.TrafficManagerProfile
var hubClient client.Client
//var dnsName string

BeforeEach(func() {
if !enabled {
Skip("Skipping setting up when traffic manager is not enabled")
}

wm = framework.NewWorkloadManager(fleet)
hubClient = wm.Fleet.HubCluster().Client()

By("Deploying workload")
Expect(wm.DeployWorkload(ctx)).Should(Succeed(), "Failed to deploy workloads")

By("Creating trafficManagerProfile")
profile = wm.TrafficManagerProfile()
Expect(hubClient.Create(ctx, &profile)).Should(Succeed(), "Failed to creat the trafficManagerProfile")

By("Validating the trafficManagerProfile status")
validator.ValidateIfTrafficManagerProfileIsProgrammed(ctx, hubClient, types.NamespacedName{Namespace: profile.Namespace, Name: profile.Name})
})

AfterEach(func() {
if !enabled {
Skip("Skipping deleting when traffic manager is not enabled")
}

By("Removing workload")
Expect(wm.RemoveWorkload(ctx)).Should(Succeed())

By("Deleting trafficManagerProfile")
Expect(hubClient.Delete(ctx, &profile)).Should(Succeed(), "Failed to delete the trafficManagerProfile")

By("Validating trafficManagerProfile is deleted")
validator.IsTrafficManagerProfileDeleted(ctx, hubClient, types.NamespacedName{Namespace: profile.Namespace, Name: profile.Name})
})

Context("Test invalid trafficManagerBackend (invalid serviceImport)", Ordered, func() {
var backend fleetnetv1alpha1.TrafficManagerBackend
var name types.NamespacedName
BeforeAll(func() {
By("Creating trafficManagerBackend")
backend = wm.TrafficManagerBackend()
name = types.NamespacedName{Namespace: backend.Namespace, Name: backend.Name}
Expect(hubClient.Create(ctx, &backend)).Should(Succeed(), "Failed to create the trafficManagerBackend")
})

AfterAll(func() {
By("Deleting trafficManagerBackend")
Expect(hubClient.Delete(ctx, &backend)).Should(Succeed(), "Failed to delete the trafficManagerBackend")
validator.IsTrafficManagerBackendDeleted(ctx, hubClient, name)
})

It("Validating the trafficManagerBackend status", func() {
status := validator.ValidateTrafficManagerBackendIfAcceptedAndIgnoringEndpointName(ctx, hubClient, name, nil)
validator.ValidateTrafficManagerBackendStatusAndIgnoringEndpointNameConsistently(ctx, hubClient, name, status)
})
})
})

0 comments on commit 8fd9033

Please sign in to comment.