From 0a2a1852fab3d0a2d33f49128e7fb6bef4fbe087 Mon Sep 17 00:00:00 2001 From: Zhiying Lin Date: Wed, 18 Sep 2024 16:13:54 +0800 Subject: [PATCH] rebase --- go.mod | 2 +- pkg/common/azureerrors/azureerrors_test.go | 322 ++++----- pkg/common/objectmeta/objectmeta.go | 94 +-- .../hub/trafficmanagerprofile/controller.go | 542 +++++++------- .../controller_integration_test.go | 660 +++++++++--------- .../trafficmanagerprofile/controller_test.go | 226 +++--- .../hub/trafficmanagerprofile/suite_test.go | 280 ++++---- .../trafficmanager/fakeprovider/profile.go | 300 ++++---- 8 files changed, 1213 insertions(+), 1213 deletions(-) diff --git a/go.mod b/go.mod index a4ff616b..90b7ccdc 100644 --- a/go.mod +++ b/go.mod @@ -11,12 +11,12 @@ require ( github.com/prometheus/client_golang v1.19.1 github.com/prometheus/common v0.55.0 github.com/stretchr/testify v1.9.0 + golang.org/x/sync v0.7.0 k8s.io/api v0.30.2 k8s.io/apimachinery v0.30.2 k8s.io/client-go v0.30.2 k8s.io/klog/v2 v2.130.1 k8s.io/utils v0.0.0-20240502163921-fe8a2dddb1d0 - golang.org/x/sync v0.7.0 sigs.k8s.io/controller-runtime v0.18.4 ) diff --git a/pkg/common/azureerrors/azureerrors_test.go b/pkg/common/azureerrors/azureerrors_test.go index c3231ed0..3a503a47 100644 --- a/pkg/common/azureerrors/azureerrors_test.go +++ b/pkg/common/azureerrors/azureerrors_test.go @@ -1,161 +1,161 @@ -/* -Copyright (c) Microsoft Corporation. -Licensed under the MIT license. -*/ - -package azureerrors - -import ( - "errors" - "testing" - - "github.com/Azure/azure-sdk-for-go/sdk/azcore" -) - -func TestIsNotFound(t *testing.T) { - tests := []struct { - name string - err error - want bool - }{ - { - name: "nil error", - err: nil, - want: false, - }, - { - name: "not azure error", - err: errors.New("not azure error"), - want: false, - }, - { - name: "bad request error", - err: &azcore.ResponseError{StatusCode: 400}, - want: false, - }, - { - name: "not found error", - err: &azcore.ResponseError{StatusCode: 404}, - want: true, - }, - } - for _, tc := range tests { - t.Run(tc.name, func(t *testing.T) { - got := IsNotFound(tc.err) - if got != tc.want { - t.Errorf("IsNotFound() = %v, want %v", got, tc.want) - } - }) - } -} - -func TestIsClientError(t *testing.T) { - tests := []struct { - name string - err error - want bool - }{ - { - name: "nil error", - err: nil, - want: false, - }, - { - name: "not azure error", - err: errors.New("not azure error"), - want: false, - }, - { - name: "bad request error", - err: &azcore.ResponseError{StatusCode: 400}, - want: true, - }, - { - name: "not found error", - err: &azcore.ResponseError{StatusCode: 404}, - want: true, - }, - } - for _, tc := range tests { - t.Run(tc.name, func(t *testing.T) { - got := IsClientError(tc.err) - if got != tc.want { - t.Errorf("IsClientError() = %v, want %v", got, tc.want) - } - }) - } -} - -func TestIsConflict(t *testing.T) { - tests := []struct { - name string - err error - want bool - }{ - { - name: "nil error", - err: nil, - want: false, - }, - { - name: "not azure error", - err: errors.New("not azure error"), - want: false, - }, - { - name: "bad request error", - err: &azcore.ResponseError{StatusCode: 400}, - want: false, - }, - { - name: "conflict error", - err: &azcore.ResponseError{StatusCode: 409}, - want: true, - }, - } - for _, tc := range tests { - t.Run(tc.name, func(t *testing.T) { - got := IsConflict(tc.err) - if got != tc.want { - t.Errorf("IsConflict() = %v, want %v", got, tc.want) - } - }) - } -} - -func TestIsThrottled(t *testing.T) { - tests := []struct { - name string - err error - want bool - }{ - { - name: "nil error", - err: nil, - want: false, - }, - { - name: "not azure error", - err: errors.New("not azure error"), - want: false, - }, - { - name: "bad request error", - err: &azcore.ResponseError{StatusCode: 400}, - want: false, - }, - { - name: "throttled error", - err: &azcore.ResponseError{StatusCode: 429}, - want: true, - }, - } - for _, tc := range tests { - t.Run(tc.name, func(t *testing.T) { - got := IsThrottled(tc.err) - if got != tc.want { - t.Errorf("IsConflict() = %v, want %v", got, tc.want) - } - }) - } -} +/* +Copyright (c) Microsoft Corporation. +Licensed under the MIT license. +*/ + +package azureerrors + +import ( + "errors" + "testing" + + "github.com/Azure/azure-sdk-for-go/sdk/azcore" +) + +func TestIsNotFound(t *testing.T) { + tests := []struct { + name string + err error + want bool + }{ + { + name: "nil error", + err: nil, + want: false, + }, + { + name: "not azure error", + err: errors.New("not azure error"), + want: false, + }, + { + name: "bad request error", + err: &azcore.ResponseError{StatusCode: 400}, + want: false, + }, + { + name: "not found error", + err: &azcore.ResponseError{StatusCode: 404}, + want: true, + }, + } + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + got := IsNotFound(tc.err) + if got != tc.want { + t.Errorf("IsNotFound() = %v, want %v", got, tc.want) + } + }) + } +} + +func TestIsClientError(t *testing.T) { + tests := []struct { + name string + err error + want bool + }{ + { + name: "nil error", + err: nil, + want: false, + }, + { + name: "not azure error", + err: errors.New("not azure error"), + want: false, + }, + { + name: "bad request error", + err: &azcore.ResponseError{StatusCode: 400}, + want: true, + }, + { + name: "not found error", + err: &azcore.ResponseError{StatusCode: 404}, + want: true, + }, + } + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + got := IsClientError(tc.err) + if got != tc.want { + t.Errorf("IsClientError() = %v, want %v", got, tc.want) + } + }) + } +} + +func TestIsConflict(t *testing.T) { + tests := []struct { + name string + err error + want bool + }{ + { + name: "nil error", + err: nil, + want: false, + }, + { + name: "not azure error", + err: errors.New("not azure error"), + want: false, + }, + { + name: "bad request error", + err: &azcore.ResponseError{StatusCode: 400}, + want: false, + }, + { + name: "conflict error", + err: &azcore.ResponseError{StatusCode: 409}, + want: true, + }, + } + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + got := IsConflict(tc.err) + if got != tc.want { + t.Errorf("IsConflict() = %v, want %v", got, tc.want) + } + }) + } +} + +func TestIsThrottled(t *testing.T) { + tests := []struct { + name string + err error + want bool + }{ + { + name: "nil error", + err: nil, + want: false, + }, + { + name: "not azure error", + err: errors.New("not azure error"), + want: false, + }, + { + name: "bad request error", + err: &azcore.ResponseError{StatusCode: 400}, + want: false, + }, + { + name: "throttled error", + err: &azcore.ResponseError{StatusCode: 429}, + want: true, + }, + } + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + got := IsThrottled(tc.err) + if got != tc.want { + t.Errorf("IsConflict() = %v, want %v", got, tc.want) + } + }) + } +} diff --git a/pkg/common/objectmeta/objectmeta.go b/pkg/common/objectmeta/objectmeta.go index c40d59e8..a46211f4 100644 --- a/pkg/common/objectmeta/objectmeta.go +++ b/pkg/common/objectmeta/objectmeta.go @@ -1,47 +1,47 @@ -/* -Copyright (c) Microsoft Corporation. -Licensed under the MIT license. -*/ - -// Package objectmeta defines shared meta const used by the networking objects. -package objectmeta - -const ( - fleetNetworkingPrefix = "networking.fleet.azure.com/" -) - -// Finalizers -const ( - // InternalServiceExportFinalizer is the finalizer InternalServiceExport controllers adds to mark that a - // InternalServiceExport can only be deleted after both ServiceImport label and ServiceExport conflict resolution - // result have been updated. - InternalServiceExportFinalizer = fleetNetworkingPrefix + "internal-svc-export-cleanup" - - // TrafficManagerProfileFinalizer a finalizer added by the TrafficManagerProfile controller to all trafficManagerProfiles, - // to make sure that the controller can react to profile deletions if necessary. - TrafficManagerProfileFinalizer = fleetNetworkingPrefix + "traffic-manager-profile-cleanup" -) - -// Labels -const ( - // MultiClusterServiceLabelDerivedService is the label added by the MCS controller, which marks the - // derived Service behind a MCS. - MultiClusterServiceLabelDerivedService = fleetNetworkingPrefix + "derived-service" -) - -// Annotations -const ( - // ServiceImportAnnotationServiceInUseBy is the key of the ServiceInUseBy annotation, which marks the list - // of member clusters importing an exported Service. - ServiceImportAnnotationServiceInUseBy = fleetNetworkingPrefix + "service-in-use-by" - - // ExportedObjectAnnotationUniqueName is an annotation that marks the fleet-scoped unique name assigned to - // an exported object. - ExportedObjectAnnotationUniqueName = fleetNetworkingPrefix + "fleet-unique-name" -) - -// Azure Resource Tags -const ( - // AzureTrafficManagerProfileTagKey is the key of the Azure Traffic Manager profile tag when the controller creates it. - AzureTrafficManagerProfileTagKey = fleetNetworkingPrefix + "trafficManagerProfile" -) +/* +Copyright (c) Microsoft Corporation. +Licensed under the MIT license. +*/ + +// Package objectmeta defines shared meta const used by the networking objects. +package objectmeta + +const ( + fleetNetworkingPrefix = "networking.fleet.azure.com/" +) + +// Finalizers +const ( + // InternalServiceExportFinalizer is the finalizer InternalServiceExport controllers adds to mark that a + // InternalServiceExport can only be deleted after both ServiceImport label and ServiceExport conflict resolution + // result have been updated. + InternalServiceExportFinalizer = fleetNetworkingPrefix + "internal-svc-export-cleanup" + + // TrafficManagerProfileFinalizer a finalizer added by the TrafficManagerProfile controller to all trafficManagerProfiles, + // to make sure that the controller can react to profile deletions if necessary. + TrafficManagerProfileFinalizer = fleetNetworkingPrefix + "traffic-manager-profile-cleanup" +) + +// Labels +const ( + // MultiClusterServiceLabelDerivedService is the label added by the MCS controller, which marks the + // derived Service behind a MCS. + MultiClusterServiceLabelDerivedService = fleetNetworkingPrefix + "derived-service" +) + +// Annotations +const ( + // ServiceImportAnnotationServiceInUseBy is the key of the ServiceInUseBy annotation, which marks the list + // of member clusters importing an exported Service. + ServiceImportAnnotationServiceInUseBy = fleetNetworkingPrefix + "service-in-use-by" + + // ExportedObjectAnnotationUniqueName is an annotation that marks the fleet-scoped unique name assigned to + // an exported object. + ExportedObjectAnnotationUniqueName = fleetNetworkingPrefix + "fleet-unique-name" +) + +// Azure Resource Tags +const ( + // AzureTrafficManagerProfileTagKey is the key of the Azure Traffic Manager profile tag when the controller creates it. + AzureTrafficManagerProfileTagKey = fleetNetworkingPrefix + "trafficManagerProfile" +) diff --git a/pkg/controllers/hub/trafficmanagerprofile/controller.go b/pkg/controllers/hub/trafficmanagerprofile/controller.go index da64b53a..5984c055 100644 --- a/pkg/controllers/hub/trafficmanagerprofile/controller.go +++ b/pkg/controllers/hub/trafficmanagerprofile/controller.go @@ -1,271 +1,271 @@ -/* -Copyright (c) Microsoft Corporation. -Licensed under the MIT license. -*/ - -// Package trafficmanagerprofile features the TrafficManagerProfile controller to reconcile TrafficManagerProfile CRs. -package trafficmanagerprofile - -import ( - "context" - "errors" - "fmt" - "time" - - "github.com/Azure/azure-sdk-for-go/sdk/azcore" - "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/trafficmanager/armtrafficmanager" - "k8s.io/apimachinery/pkg/api/equality" - apierrors "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/api/meta" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/types" - "k8s.io/klog/v2" - "k8s.io/utils/ptr" - ctrl "sigs.k8s.io/controller-runtime" - "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" - - "go.goms.io/fleet/pkg/utils/controller" - - fleetnetv1alpha1 "go.goms.io/fleet-networking/api/v1alpha1" - "go.goms.io/fleet-networking/pkg/common/azureerrors" - "go.goms.io/fleet-networking/pkg/common/defaulter" - "go.goms.io/fleet-networking/pkg/common/objectmeta" -) - -const ( - // DNSRelativeNameFormat consists of "Profile-Namespace" and "Profile-Name". - DNSRelativeNameFormat = "%s-%s" - // AzureResourceProfileNameFormat is the name format of the Azure Traffic Manager Profile created by the fleet controller. - AzureResourceProfileNameFormat = "fleet-%s" -) - -var ( - // create the func as a variable so that the integration test can use a customized function. - generateAzureTrafficManagerProfileNameFunc = func(profile *fleetnetv1alpha1.TrafficManagerProfile) string { - return fmt.Sprintf(AzureResourceProfileNameFormat, profile.UID) - } -) - -// Reconciler reconciles a TrafficManagerProfile object. -type Reconciler struct { - client.Client - - ProfilesClient *armtrafficmanager.ProfilesClient - ResourceGroupName string // default resource group name to create azure traffic manager profiles -} - -//+kubebuilder:rbac:groups=networking.fleet.azure.com,resources=trafficmanagerprofiles,verbs=get;list;watch;create;update;patch;delete -//+kubebuilder:rbac:groups=networking.fleet.azure.com,resources=trafficmanagerprofiles/status,verbs=get;update;patch -//+kubebuilder:rbac:groups=networking.fleet.azure.com,resources=trafficmanagerprofiles/finalizers,verbs=get;update -//+kubebuilder:rbac:groups="",resources=events,verbs=create;patch - -// Reconcile triggers a single reconcile round. -func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { - name := req.NamespacedName - profileKRef := klog.KRef(name.Namespace, name.Name) - - startTime := time.Now() - klog.V(2).InfoS("Reconciliation starts", "trafficManagerProfile", profileKRef) - defer func() { - latency := time.Since(startTime).Milliseconds() - klog.V(2).InfoS("Reconciliation ends", "trafficManagerProfile", profileKRef, "latency", latency) - }() - - profile := &fleetnetv1alpha1.TrafficManagerProfile{} - if err := r.Client.Get(ctx, name, profile); err != nil { - if apierrors.IsNotFound(err) { - klog.V(4).InfoS("Ignoring NotFound trafficManagerProfile", "trafficManagerProfile", profileKRef) - return ctrl.Result{}, nil - } - klog.ErrorS(err, "Failed to get trafficManagerProfile", "trafficManagerProfile", profileKRef) - return ctrl.Result{}, controller.NewAPIServerError(true, err) - } - - if !profile.ObjectMeta.DeletionTimestamp.IsZero() { - // TODO: handle the deletion when backends are still attached to the profile - return r.handleDelete(ctx, profile) - } - - // register finalizer - if !controllerutil.ContainsFinalizer(profile, objectmeta.TrafficManagerProfileFinalizer) { - controllerutil.AddFinalizer(profile, objectmeta.TrafficManagerProfileFinalizer) - if err := r.Update(ctx, profile); err != nil { - klog.ErrorS(err, "Failed to add finalizer to trafficManagerProfile", "trafficManagerProfile", profileKRef) - return ctrl.Result{}, controller.NewUpdateIgnoreConflictError(err) - } - } - - // TODO: replace the following with defaulter wehbook - defaulter.SetDefaultsTrafficManagerProfile(profile) - return r.handleUpdate(ctx, profile) -} - -func (r *Reconciler) handleDelete(ctx context.Context, profile *fleetnetv1alpha1.TrafficManagerProfile) (ctrl.Result, error) { - profileKObj := klog.KObj(profile) - // The profile is being deleted - if !controllerutil.ContainsFinalizer(profile, objectmeta.TrafficManagerProfileFinalizer) { - klog.V(4).InfoS("TrafficManagerProfile is being deleted", "trafficManagerProfile", profileKObj) - return ctrl.Result{}, nil - } - - azureProfileName := generateAzureTrafficManagerProfileNameFunc(profile) - klog.V(2).InfoS("Deleting Azure Traffic Manager profile", "trafficManagerProfile", profileKObj, "azureProfileName", azureProfileName) - if _, err := r.ProfilesClient.Delete(ctx, r.ResourceGroupName, azureProfileName, nil); err != nil { - if !azureerrors.IsNotFound(err) { - klog.ErrorS(err, "Failed to delete Azure Traffic Manager profile", "trafficManagerProfile", profileKObj, "azureProfileName", azureProfileName) - return ctrl.Result{}, err - } - } - - controllerutil.RemoveFinalizer(profile, objectmeta.TrafficManagerProfileFinalizer) - if err := r.Client.Update(ctx, profile); err != nil { - klog.ErrorS(err, "Failed to remove trafficManagerProfile finalizer", "trafficManagerProfile", profileKObj) - return ctrl.Result{}, err - } - klog.V(2).InfoS("Removed trafficManagerProfile finalizer", "trafficManagerProfile", profileKObj) - return ctrl.Result{}, nil -} - -func (r *Reconciler) handleUpdate(ctx context.Context, profile *fleetnetv1alpha1.TrafficManagerProfile) (ctrl.Result, error) { - profileKObj := klog.KObj(profile) - azureProfileName := generateAzureTrafficManagerProfileNameFunc(profile) - var responseError *azcore.ResponseError - getRes, getErr := r.ProfilesClient.Get(ctx, r.ResourceGroupName, azureProfileName, nil) - if getErr != nil { - if !azureerrors.IsNotFound(getErr) { - klog.ErrorS(getErr, "Failed to get the profile", "trafficManagerProfile", profileKObj, "azureProfileName", azureProfileName) - return ctrl.Result{}, getErr - } - klog.V(2).InfoS("Azure Traffic Manager profile does not exist", "trafficManagerProfile", profileKObj, "azureProfileName", azureProfileName) - } else { - existingSpec := convertToTrafficManagerProfileSpec(&getRes.Profile) - if equality.Semantic.DeepEqual(existingSpec, profile.Spec) { - // skip creating or updating the profile - klog.V(2).InfoS("No profile update needed", "trafficManagerProfile", profileKObj, "azureProfileName", azureProfileName) - return r.updateProfileStatus(ctx, profile, getRes.Profile, nil) - } - } - - res, updateErr := r.ProfilesClient.CreateOrUpdate(ctx, r.ResourceGroupName, azureProfileName, generateAzureTrafficManagerProfile(profile), nil) - if updateErr != nil { - if !errors.As(updateErr, &responseError) { - klog.ErrorS(updateErr, "Failed to send the createOrUpdate request", "trafficManagerProfile", profileKObj, "azureProfileName", azureProfileName) - return ctrl.Result{}, updateErr - } - klog.ErrorS(updateErr, "Failed to create or update a profile", "trafficManagerProfile", profileKObj, - "azureProfileName", azureProfileName, - "errorCode", responseError.ErrorCode, "statusCode", responseError.StatusCode) - } - return r.updateProfileStatus(ctx, profile, res.Profile, updateErr) -} - -func convertToTrafficManagerProfileSpec(profile *armtrafficmanager.Profile) fleetnetv1alpha1.TrafficManagerProfileSpec { - if profile.Properties != nil && profile.Properties.MonitorConfig != nil { - var protocol fleetnetv1alpha1.TrafficManagerMonitorProtocol - if profile.Properties.MonitorConfig.Protocol != nil { - protocol = fleetnetv1alpha1.TrafficManagerMonitorProtocol(*profile.Properties.MonitorConfig.Protocol) - } - return fleetnetv1alpha1.TrafficManagerProfileSpec{ - MonitorConfig: &fleetnetv1alpha1.MonitorConfig{ - IntervalInSeconds: profile.Properties.MonitorConfig.IntervalInSeconds, - Path: profile.Properties.MonitorConfig.Path, - Port: profile.Properties.MonitorConfig.Port, - Protocol: &protocol, - TimeoutInSeconds: profile.Properties.MonitorConfig.TimeoutInSeconds, - ToleratedNumberOfFailures: profile.Properties.MonitorConfig.ToleratedNumberOfFailures, - }, - } - } - return fleetnetv1alpha1.TrafficManagerProfileSpec{} -} - -func (r *Reconciler) updateProfileStatus(ctx context.Context, profile *fleetnetv1alpha1.TrafficManagerProfile, azureProfile armtrafficmanager.Profile, updateErr error) (ctrl.Result, error) { - profileKObj := klog.KObj(profile) - if updateErr == nil { - // azureProfile.Properties.DNSConfig.Fqdn should not be nil - if azureProfile.Properties != nil && azureProfile.Properties.DNSConfig != nil { - profile.Status.DNSName = azureProfile.Properties.DNSConfig.Fqdn - } else { - err := fmt.Errorf("got nil DNSConfig for Azure Traffic Manager profile") - klog.ErrorS(controller.NewUnexpectedBehaviorError(err), "Unexpected value returned by the Azure Traffic Manager", "trafficManagerProfile", profileKObj, "azureProfileName", azureProfile.Name) - profile.Status.DNSName = nil // reset the DNS name - } - } else { - profile.Status.DNSName = nil // reset the DNS name - } - - cond := metav1.Condition{ - Type: string(fleetnetv1alpha1.TrafficManagerProfileConditionProgrammed), - Status: metav1.ConditionTrue, - ObservedGeneration: profile.Generation, - Reason: string(fleetnetv1alpha1.TrafficManagerProfileReasonProgrammed), - Message: "Successfully configured the Azure Traffic Manager profile", - } - if azureerrors.IsConflict(updateErr) { - cond = metav1.Condition{ - Type: string(fleetnetv1alpha1.TrafficManagerProfileConditionProgrammed), - Status: metav1.ConditionFalse, - ObservedGeneration: profile.Generation, - Reason: string(fleetnetv1alpha1.TrafficManagerProfileReasonDNSNameNotAvailable), - Message: "Domain name is not available. Please choose a different profile name or namespace", - } - } else if azureerrors.IsClientError(updateErr) && !azureerrors.IsThrottled(updateErr) { - cond = metav1.Condition{ - Type: string(fleetnetv1alpha1.TrafficManagerProfileConditionProgrammed), - Status: metav1.ConditionFalse, - ObservedGeneration: profile.Generation, - Reason: string(fleetnetv1alpha1.TrafficManagerProfileReasonInvalid), - Message: fmt.Sprintf("Invalid profile: %v", updateErr), - } - } else if updateErr != nil { - cond = metav1.Condition{ - Type: string(fleetnetv1alpha1.TrafficManagerProfileConditionProgrammed), - Status: metav1.ConditionFalse, - ObservedGeneration: profile.Generation, - Reason: string(fleetnetv1alpha1.TrafficManagerProfileReasonPending), - Message: fmt.Sprintf("Failed to configure profile and retyring: %v", updateErr), - } - } - meta.SetStatusCondition(&profile.Status.Conditions, cond) - if err := r.Client.Status().Update(ctx, profile); err != nil { - klog.ErrorS(err, "Failed to update trafficManagerProfile status", "trafficManagerProfile", profileKObj) - return ctrl.Result{}, controller.NewUpdateIgnoreConflictError(err) - } - klog.V(2).InfoS("Updated the trafficProfile status", "trafficManagerProfile", profileKObj, "status", profile.Status) - return ctrl.Result{}, updateErr -} - -func generateAzureTrafficManagerProfile(profile *fleetnetv1alpha1.TrafficManagerProfile) armtrafficmanager.Profile { - mc := profile.Spec.MonitorConfig - namespacedName := types.NamespacedName{Name: profile.Name, Namespace: profile.Namespace} - return armtrafficmanager.Profile{ - Location: ptr.To("global"), - Properties: &armtrafficmanager.ProfileProperties{ - DNSConfig: &armtrafficmanager.DNSConfig{ - RelativeName: ptr.To(fmt.Sprintf(DNSRelativeNameFormat, profile.Namespace, profile.Name)), - }, - MonitorConfig: &armtrafficmanager.MonitorConfig{ - IntervalInSeconds: mc.IntervalInSeconds, - Path: mc.Path, - Port: mc.Port, - Protocol: ptr.To(armtrafficmanager.MonitorProtocol(*mc.Protocol)), - TimeoutInSeconds: mc.TimeoutInSeconds, - ToleratedNumberOfFailures: mc.ToleratedNumberOfFailures, - }, - ProfileStatus: ptr.To(armtrafficmanager.ProfileStatusEnabled), - // By default, the routing method is set to Weighted. - TrafficRoutingMethod: ptr.To(armtrafficmanager.TrafficRoutingMethodWeighted), - }, - Tags: map[string]*string{ - objectmeta.AzureTrafficManagerProfileTagKey: ptr.To(namespacedName.String()), - }, - } -} - -// SetupWithManager sets up the controller with the Manager. -func (r *Reconciler) SetupWithManager(mgr ctrl.Manager) error { - return ctrl.NewControllerManagedBy(mgr). - For(&fleetnetv1alpha1.TrafficManagerProfile{}). - Complete(r) -} +/* +Copyright (c) Microsoft Corporation. +Licensed under the MIT license. +*/ + +// Package trafficmanagerprofile features the TrafficManagerProfile controller to reconcile TrafficManagerProfile CRs. +package trafficmanagerprofile + +import ( + "context" + "errors" + "fmt" + "time" + + "github.com/Azure/azure-sdk-for-go/sdk/azcore" + "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/trafficmanager/armtrafficmanager" + "k8s.io/apimachinery/pkg/api/equality" + apierrors "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/api/meta" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" + "k8s.io/klog/v2" + "k8s.io/utils/ptr" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" + + "go.goms.io/fleet/pkg/utils/controller" + + fleetnetv1alpha1 "go.goms.io/fleet-networking/api/v1alpha1" + "go.goms.io/fleet-networking/pkg/common/azureerrors" + "go.goms.io/fleet-networking/pkg/common/defaulter" + "go.goms.io/fleet-networking/pkg/common/objectmeta" +) + +const ( + // DNSRelativeNameFormat consists of "Profile-Namespace" and "Profile-Name". + DNSRelativeNameFormat = "%s-%s" + // AzureResourceProfileNameFormat is the name format of the Azure Traffic Manager Profile created by the fleet controller. + AzureResourceProfileNameFormat = "fleet-%s" +) + +var ( + // create the func as a variable so that the integration test can use a customized function. + generateAzureTrafficManagerProfileNameFunc = func(profile *fleetnetv1alpha1.TrafficManagerProfile) string { + return fmt.Sprintf(AzureResourceProfileNameFormat, profile.UID) + } +) + +// Reconciler reconciles a TrafficManagerProfile object. +type Reconciler struct { + client.Client + + ProfilesClient *armtrafficmanager.ProfilesClient + ResourceGroupName string // default resource group name to create azure traffic manager profiles +} + +//+kubebuilder:rbac:groups=networking.fleet.azure.com,resources=trafficmanagerprofiles,verbs=get;list;watch;create;update;patch;delete +//+kubebuilder:rbac:groups=networking.fleet.azure.com,resources=trafficmanagerprofiles/status,verbs=get;update;patch +//+kubebuilder:rbac:groups=networking.fleet.azure.com,resources=trafficmanagerprofiles/finalizers,verbs=get;update +//+kubebuilder:rbac:groups="",resources=events,verbs=create;patch + +// Reconcile triggers a single reconcile round. +func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { + name := req.NamespacedName + profileKRef := klog.KRef(name.Namespace, name.Name) + + startTime := time.Now() + klog.V(2).InfoS("Reconciliation starts", "trafficManagerProfile", profileKRef) + defer func() { + latency := time.Since(startTime).Milliseconds() + klog.V(2).InfoS("Reconciliation ends", "trafficManagerProfile", profileKRef, "latency", latency) + }() + + profile := &fleetnetv1alpha1.TrafficManagerProfile{} + if err := r.Client.Get(ctx, name, profile); err != nil { + if apierrors.IsNotFound(err) { + klog.V(4).InfoS("Ignoring NotFound trafficManagerProfile", "trafficManagerProfile", profileKRef) + return ctrl.Result{}, nil + } + klog.ErrorS(err, "Failed to get trafficManagerProfile", "trafficManagerProfile", profileKRef) + return ctrl.Result{}, controller.NewAPIServerError(true, err) + } + + if !profile.ObjectMeta.DeletionTimestamp.IsZero() { + // TODO: handle the deletion when backends are still attached to the profile + return r.handleDelete(ctx, profile) + } + + // register finalizer + if !controllerutil.ContainsFinalizer(profile, objectmeta.TrafficManagerProfileFinalizer) { + controllerutil.AddFinalizer(profile, objectmeta.TrafficManagerProfileFinalizer) + if err := r.Update(ctx, profile); err != nil { + klog.ErrorS(err, "Failed to add finalizer to trafficManagerProfile", "trafficManagerProfile", profileKRef) + return ctrl.Result{}, controller.NewUpdateIgnoreConflictError(err) + } + } + + // TODO: replace the following with defaulter wehbook + defaulter.SetDefaultsTrafficManagerProfile(profile) + return r.handleUpdate(ctx, profile) +} + +func (r *Reconciler) handleDelete(ctx context.Context, profile *fleetnetv1alpha1.TrafficManagerProfile) (ctrl.Result, error) { + profileKObj := klog.KObj(profile) + // The profile is being deleted + if !controllerutil.ContainsFinalizer(profile, objectmeta.TrafficManagerProfileFinalizer) { + klog.V(4).InfoS("TrafficManagerProfile is being deleted", "trafficManagerProfile", profileKObj) + return ctrl.Result{}, nil + } + + azureProfileName := generateAzureTrafficManagerProfileNameFunc(profile) + klog.V(2).InfoS("Deleting Azure Traffic Manager profile", "trafficManagerProfile", profileKObj, "azureProfileName", azureProfileName) + if _, err := r.ProfilesClient.Delete(ctx, r.ResourceGroupName, azureProfileName, nil); err != nil { + if !azureerrors.IsNotFound(err) { + klog.ErrorS(err, "Failed to delete Azure Traffic Manager profile", "trafficManagerProfile", profileKObj, "azureProfileName", azureProfileName) + return ctrl.Result{}, err + } + } + + controllerutil.RemoveFinalizer(profile, objectmeta.TrafficManagerProfileFinalizer) + if err := r.Client.Update(ctx, profile); err != nil { + klog.ErrorS(err, "Failed to remove trafficManagerProfile finalizer", "trafficManagerProfile", profileKObj) + return ctrl.Result{}, err + } + klog.V(2).InfoS("Removed trafficManagerProfile finalizer", "trafficManagerProfile", profileKObj) + return ctrl.Result{}, nil +} + +func (r *Reconciler) handleUpdate(ctx context.Context, profile *fleetnetv1alpha1.TrafficManagerProfile) (ctrl.Result, error) { + profileKObj := klog.KObj(profile) + azureProfileName := generateAzureTrafficManagerProfileNameFunc(profile) + var responseError *azcore.ResponseError + getRes, getErr := r.ProfilesClient.Get(ctx, r.ResourceGroupName, azureProfileName, nil) + if getErr != nil { + if !azureerrors.IsNotFound(getErr) { + klog.ErrorS(getErr, "Failed to get the profile", "trafficManagerProfile", profileKObj, "azureProfileName", azureProfileName) + return ctrl.Result{}, getErr + } + klog.V(2).InfoS("Azure Traffic Manager profile does not exist", "trafficManagerProfile", profileKObj, "azureProfileName", azureProfileName) + } else { + existingSpec := convertToTrafficManagerProfileSpec(&getRes.Profile) + if equality.Semantic.DeepEqual(existingSpec, profile.Spec) { + // skip creating or updating the profile + klog.V(2).InfoS("No profile update needed", "trafficManagerProfile", profileKObj, "azureProfileName", azureProfileName) + return r.updateProfileStatus(ctx, profile, getRes.Profile, nil) + } + } + + res, updateErr := r.ProfilesClient.CreateOrUpdate(ctx, r.ResourceGroupName, azureProfileName, generateAzureTrafficManagerProfile(profile), nil) + if updateErr != nil { + if !errors.As(updateErr, &responseError) { + klog.ErrorS(updateErr, "Failed to send the createOrUpdate request", "trafficManagerProfile", profileKObj, "azureProfileName", azureProfileName) + return ctrl.Result{}, updateErr + } + klog.ErrorS(updateErr, "Failed to create or update a profile", "trafficManagerProfile", profileKObj, + "azureProfileName", azureProfileName, + "errorCode", responseError.ErrorCode, "statusCode", responseError.StatusCode) + } + return r.updateProfileStatus(ctx, profile, res.Profile, updateErr) +} + +func convertToTrafficManagerProfileSpec(profile *armtrafficmanager.Profile) fleetnetv1alpha1.TrafficManagerProfileSpec { + if profile.Properties != nil && profile.Properties.MonitorConfig != nil { + var protocol fleetnetv1alpha1.TrafficManagerMonitorProtocol + if profile.Properties.MonitorConfig.Protocol != nil { + protocol = fleetnetv1alpha1.TrafficManagerMonitorProtocol(*profile.Properties.MonitorConfig.Protocol) + } + return fleetnetv1alpha1.TrafficManagerProfileSpec{ + MonitorConfig: &fleetnetv1alpha1.MonitorConfig{ + IntervalInSeconds: profile.Properties.MonitorConfig.IntervalInSeconds, + Path: profile.Properties.MonitorConfig.Path, + Port: profile.Properties.MonitorConfig.Port, + Protocol: &protocol, + TimeoutInSeconds: profile.Properties.MonitorConfig.TimeoutInSeconds, + ToleratedNumberOfFailures: profile.Properties.MonitorConfig.ToleratedNumberOfFailures, + }, + } + } + return fleetnetv1alpha1.TrafficManagerProfileSpec{} +} + +func (r *Reconciler) updateProfileStatus(ctx context.Context, profile *fleetnetv1alpha1.TrafficManagerProfile, azureProfile armtrafficmanager.Profile, updateErr error) (ctrl.Result, error) { + profileKObj := klog.KObj(profile) + if updateErr == nil { + // azureProfile.Properties.DNSConfig.Fqdn should not be nil + if azureProfile.Properties != nil && azureProfile.Properties.DNSConfig != nil { + profile.Status.DNSName = azureProfile.Properties.DNSConfig.Fqdn + } else { + err := fmt.Errorf("got nil DNSConfig for Azure Traffic Manager profile") + klog.ErrorS(controller.NewUnexpectedBehaviorError(err), "Unexpected value returned by the Azure Traffic Manager", "trafficManagerProfile", profileKObj, "azureProfileName", azureProfile.Name) + profile.Status.DNSName = nil // reset the DNS name + } + } else { + profile.Status.DNSName = nil // reset the DNS name + } + + cond := metav1.Condition{ + Type: string(fleetnetv1alpha1.TrafficManagerProfileConditionProgrammed), + Status: metav1.ConditionTrue, + ObservedGeneration: profile.Generation, + Reason: string(fleetnetv1alpha1.TrafficManagerProfileReasonProgrammed), + Message: "Successfully configured the Azure Traffic Manager profile", + } + if azureerrors.IsConflict(updateErr) { + cond = metav1.Condition{ + Type: string(fleetnetv1alpha1.TrafficManagerProfileConditionProgrammed), + Status: metav1.ConditionFalse, + ObservedGeneration: profile.Generation, + Reason: string(fleetnetv1alpha1.TrafficManagerProfileReasonDNSNameNotAvailable), + Message: "Domain name is not available. Please choose a different profile name or namespace", + } + } else if azureerrors.IsClientError(updateErr) && !azureerrors.IsThrottled(updateErr) { + cond = metav1.Condition{ + Type: string(fleetnetv1alpha1.TrafficManagerProfileConditionProgrammed), + Status: metav1.ConditionFalse, + ObservedGeneration: profile.Generation, + Reason: string(fleetnetv1alpha1.TrafficManagerProfileReasonInvalid), + Message: fmt.Sprintf("Invalid profile: %v", updateErr), + } + } else if updateErr != nil { + cond = metav1.Condition{ + Type: string(fleetnetv1alpha1.TrafficManagerProfileConditionProgrammed), + Status: metav1.ConditionFalse, + ObservedGeneration: profile.Generation, + Reason: string(fleetnetv1alpha1.TrafficManagerProfileReasonPending), + Message: fmt.Sprintf("Failed to configure profile and retyring: %v", updateErr), + } + } + meta.SetStatusCondition(&profile.Status.Conditions, cond) + if err := r.Client.Status().Update(ctx, profile); err != nil { + klog.ErrorS(err, "Failed to update trafficManagerProfile status", "trafficManagerProfile", profileKObj) + return ctrl.Result{}, controller.NewUpdateIgnoreConflictError(err) + } + klog.V(2).InfoS("Updated the trafficProfile status", "trafficManagerProfile", profileKObj, "status", profile.Status) + return ctrl.Result{}, updateErr +} + +func generateAzureTrafficManagerProfile(profile *fleetnetv1alpha1.TrafficManagerProfile) armtrafficmanager.Profile { + mc := profile.Spec.MonitorConfig + namespacedName := types.NamespacedName{Name: profile.Name, Namespace: profile.Namespace} + return armtrafficmanager.Profile{ + Location: ptr.To("global"), + Properties: &armtrafficmanager.ProfileProperties{ + DNSConfig: &armtrafficmanager.DNSConfig{ + RelativeName: ptr.To(fmt.Sprintf(DNSRelativeNameFormat, profile.Namespace, profile.Name)), + }, + MonitorConfig: &armtrafficmanager.MonitorConfig{ + IntervalInSeconds: mc.IntervalInSeconds, + Path: mc.Path, + Port: mc.Port, + Protocol: ptr.To(armtrafficmanager.MonitorProtocol(*mc.Protocol)), + TimeoutInSeconds: mc.TimeoutInSeconds, + ToleratedNumberOfFailures: mc.ToleratedNumberOfFailures, + }, + ProfileStatus: ptr.To(armtrafficmanager.ProfileStatusEnabled), + // By default, the routing method is set to Weighted. + TrafficRoutingMethod: ptr.To(armtrafficmanager.TrafficRoutingMethodWeighted), + }, + Tags: map[string]*string{ + objectmeta.AzureTrafficManagerProfileTagKey: ptr.To(namespacedName.String()), + }, + } +} + +// SetupWithManager sets up the controller with the Manager. +func (r *Reconciler) SetupWithManager(mgr ctrl.Manager) error { + return ctrl.NewControllerManagedBy(mgr). + For(&fleetnetv1alpha1.TrafficManagerProfile{}). + Complete(r) +} diff --git a/pkg/controllers/hub/trafficmanagerprofile/controller_integration_test.go b/pkg/controllers/hub/trafficmanagerprofile/controller_integration_test.go index eb1280ae..cb559c63 100644 --- a/pkg/controllers/hub/trafficmanagerprofile/controller_integration_test.go +++ b/pkg/controllers/hub/trafficmanagerprofile/controller_integration_test.go @@ -1,330 +1,330 @@ -/* -Copyright (c) Microsoft Corporation. -Licensed under the MIT license. -*/ - -package trafficmanagerprofile - -import ( - "fmt" - - . "github.com/onsi/ginkgo/v2" - . "github.com/onsi/gomega" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/types" - "k8s.io/utils/ptr" - - fleetnetv1alpha1 "go.goms.io/fleet-networking/api/v1alpha1" - "go.goms.io/fleet-networking/pkg/common/objectmeta" - "go.goms.io/fleet-networking/test/common/trafficmanager/fakeprovider" - "go.goms.io/fleet-networking/test/common/trafficmanager/validator" -) - -func trafficManagerProfileForTest(name string) *fleetnetv1alpha1.TrafficManagerProfile { - return &fleetnetv1alpha1.TrafficManagerProfile{ - ObjectMeta: metav1.ObjectMeta{ - Name: name, - Namespace: testNamespace, - }, - Spec: fleetnetv1alpha1.TrafficManagerProfileSpec{ - MonitorConfig: &fleetnetv1alpha1.MonitorConfig{ - IntervalInSeconds: ptr.To[int64](30), - Path: ptr.To("/healthz"), - Port: ptr.To[int64](8080), - Protocol: ptr.To(fleetnetv1alpha1.TrafficManagerMonitorProtocolHTTPS), - TimeoutInSeconds: ptr.To[int64](10), - ToleratedNumberOfFailures: ptr.To[int64](5), - }, - }, - } -} - -var _ = Describe("Test TrafficManagerProfile Controller", func() { - Context("When updating existing valid trafficManagerProfile", Ordered, func() { - name := fakeprovider.ValidProfileName - var profile *fleetnetv1alpha1.TrafficManagerProfile - relativeDNSName := fmt.Sprintf(DNSRelativeNameFormat, testNamespace, name) - fqdn := fmt.Sprintf(fakeprovider.ProfileDNSNameFormat, relativeDNSName) - - It("AzureTrafficManager should be configured", func() { - By("By creating a new TrafficManagerProfile") - profile = trafficManagerProfileForTest(name) - Expect(k8sClient.Create(ctx, profile)).Should(Succeed()) - - By("By checking profile") - want := fleetnetv1alpha1.TrafficManagerProfile{ - ObjectMeta: metav1.ObjectMeta{ - Name: name, - Namespace: testNamespace, - Finalizers: []string{objectmeta.TrafficManagerProfileFinalizer}, - }, - Spec: profile.Spec, - Status: fleetnetv1alpha1.TrafficManagerProfileStatus{ - DNSName: ptr.To(fqdn), - Conditions: []metav1.Condition{ - { - Status: metav1.ConditionTrue, - Type: string(fleetnetv1alpha1.TrafficManagerProfileConditionProgrammed), - Reason: string(fleetnetv1alpha1.TrafficManagerProfileReasonProgrammed), - }, - }, - }, - } - validator.ValidateTrafficManagerProfile(ctx, k8sClient, &want) - }) - - It("Update the trafficManagerProfile spec", func() { - Expect(k8sClient.Get(ctx, types.NamespacedName{Namespace: testNamespace, Name: name}, profile)).Should(Succeed(), "failed to get the trafficManagerProfile") - profile.Spec.MonitorConfig.IntervalInSeconds = ptr.To[int64](10) - profile.Spec.MonitorConfig.TimeoutInSeconds = ptr.To[int64](10) - Expect(k8sClient.Update(ctx, profile)).Should(Succeed(), "failed to update the trafficManagerProfile") - }) - - It("Validating trafficManagerProfile status and update should fail", func() { - want := fleetnetv1alpha1.TrafficManagerProfile{ - ObjectMeta: metav1.ObjectMeta{ - Name: name, - Namespace: testNamespace, - Finalizers: []string{objectmeta.TrafficManagerProfileFinalizer}, - }, - Spec: profile.Spec, - Status: fleetnetv1alpha1.TrafficManagerProfileStatus{ - Conditions: []metav1.Condition{ - { - Status: metav1.ConditionFalse, - Type: string(fleetnetv1alpha1.TrafficManagerProfileConditionProgrammed), - Reason: string(fleetnetv1alpha1.TrafficManagerProfileReasonInvalid), - }, - }, - }, - } - validator.ValidateTrafficManagerProfile(ctx, k8sClient, &want) - }) - - It("Deleting trafficManagerProfile", func() { - err := k8sClient.Delete(ctx, profile) - Expect(err).Should(Succeed(), "failed to delete trafficManagerProfile") - }) - - It("Validating trafficManagerProfile is deleted", func() { - validator.IsTrafficManagerProfileDeleted(ctx, k8sClient, types.NamespacedName{Namespace: testNamespace, Name: name}) - }) - }) - - Context("When updating existing valid trafficManagerProfile with no changes", Ordered, func() { - name := fakeprovider.ValidProfileName - var profile *fleetnetv1alpha1.TrafficManagerProfile - - It("AzureTrafficManager should be configured", func() { - By("By creating a new TrafficManagerProfile") - profile = &fleetnetv1alpha1.TrafficManagerProfile{ - ObjectMeta: metav1.ObjectMeta{ - Name: name, - Namespace: testNamespace, - }, - Spec: fleetnetv1alpha1.TrafficManagerProfileSpec{ - MonitorConfig: &fleetnetv1alpha1.MonitorConfig{ - IntervalInSeconds: ptr.To[int64](10), - Path: ptr.To("/healthz"), - Port: ptr.To[int64](8080), - Protocol: ptr.To(fleetnetv1alpha1.TrafficManagerMonitorProtocolHTTP), - TimeoutInSeconds: ptr.To[int64](9), - ToleratedNumberOfFailures: ptr.To[int64](4), - }, - }, - } - Expect(k8sClient.Create(ctx, profile)).Should(Succeed()) - - By("By checking profile") - want := fleetnetv1alpha1.TrafficManagerProfile{ - ObjectMeta: metav1.ObjectMeta{ - Name: name, - Namespace: testNamespace, - Finalizers: []string{objectmeta.TrafficManagerProfileFinalizer}, - }, - Spec: profile.Spec, - Status: fleetnetv1alpha1.TrafficManagerProfileStatus{ - // The DNS name is returned by the fake Azure GET call. - DNSName: ptr.To(fmt.Sprintf(fakeprovider.ProfileDNSNameFormat, name)), - Conditions: []metav1.Condition{ - { - Status: metav1.ConditionTrue, - Type: string(fleetnetv1alpha1.TrafficManagerProfileConditionProgrammed), - Reason: string(fleetnetv1alpha1.TrafficManagerProfileReasonProgrammed), - }, - }, - }, - } - validator.ValidateTrafficManagerProfile(ctx, k8sClient, &want) - }) - - It("Deleting trafficManagerProfile", func() { - err := k8sClient.Delete(ctx, profile) - Expect(err).Should(Succeed(), "failed to delete trafficManagerProfile") - }) - - It("Validating trafficManagerProfile is deleted", func() { - validator.IsTrafficManagerProfileDeleted(ctx, k8sClient, types.NamespacedName{Namespace: testNamespace, Name: name}) - }) - }) - - Context("When creating trafficManagerProfile and DNS name is not available", Ordered, func() { - name := fakeprovider.ConflictErrProfileName - var profile *fleetnetv1alpha1.TrafficManagerProfile - - It("AzureTrafficManager should not be configured", func() { - By("By creating a new TrafficManagerProfile") - profile = trafficManagerProfileForTest(name) - Expect(k8sClient.Create(ctx, profile)).Should(Succeed()) - - By("By checking profile") - want := fleetnetv1alpha1.TrafficManagerProfile{ - ObjectMeta: metav1.ObjectMeta{ - Name: name, - Namespace: testNamespace, - Finalizers: []string{objectmeta.TrafficManagerProfileFinalizer}, - }, - Spec: profile.Spec, - Status: fleetnetv1alpha1.TrafficManagerProfileStatus{ - Conditions: []metav1.Condition{ - { - Status: metav1.ConditionFalse, - Type: string(fleetnetv1alpha1.TrafficManagerProfileConditionProgrammed), - Reason: string(fleetnetv1alpha1.TrafficManagerProfileReasonDNSNameNotAvailable), - }, - }, - }, - } - validator.ValidateTrafficManagerProfile(ctx, k8sClient, &want) - }) - - It("Deleting trafficManagerProfile", func() { - err := k8sClient.Delete(ctx, profile) - Expect(err).Should(Succeed(), "failed to delete trafficManagerProfile") - }) - - It("Validating trafficManagerProfile is deleted", func() { - validator.IsTrafficManagerProfileDeleted(ctx, k8sClient, types.NamespacedName{Namespace: testNamespace, Name: name}) - }) - }) - - Context("When creating trafficManagerProfile and azure request failed because of too many requests", Ordered, func() { - name := fakeprovider.ThrottledErrProfileName - var profile *fleetnetv1alpha1.TrafficManagerProfile - - It("AzureTrafficManager should not be configured", func() { - By("By creating a new TrafficManagerProfile") - profile = trafficManagerProfileForTest(name) - Expect(k8sClient.Create(ctx, profile)).Should(Succeed()) - - By("By checking profile") - want := fleetnetv1alpha1.TrafficManagerProfile{ - ObjectMeta: metav1.ObjectMeta{ - Name: name, - Namespace: testNamespace, - Finalizers: []string{objectmeta.TrafficManagerProfileFinalizer}, - }, - Spec: profile.Spec, - Status: fleetnetv1alpha1.TrafficManagerProfileStatus{ - Conditions: []metav1.Condition{ - { - Status: metav1.ConditionFalse, - Type: string(fleetnetv1alpha1.TrafficManagerProfileConditionProgrammed), - Reason: string(fleetnetv1alpha1.TrafficManagerProfileReasonPending), - }, - }, - }, - } - validator.ValidateTrafficManagerProfile(ctx, k8sClient, &want) - }) - - It("Deleting trafficManagerProfile", func() { - err := k8sClient.Delete(ctx, profile) - Expect(err).Should(Succeed(), "failed to delete trafficManagerProfile") - }) - - It("Validating trafficManagerProfile is deleted", func() { - validator.IsTrafficManagerProfileDeleted(ctx, k8sClient, types.NamespacedName{Namespace: testNamespace, Name: name}) - }) - }) - - Context("When creating trafficManagerProfile and azure request failed because of client side error", Ordered, func() { - name := "bad-request" - var profile *fleetnetv1alpha1.TrafficManagerProfile - - It("AzureTrafficManager should not be configured", func() { - By("By creating a new TrafficManagerProfile") - profile = trafficManagerProfileForTest(name) - Expect(k8sClient.Create(ctx, profile)).Should(Succeed()) - - By("By checking profile") - want := fleetnetv1alpha1.TrafficManagerProfile{ - ObjectMeta: metav1.ObjectMeta{ - Name: name, - Namespace: testNamespace, - Finalizers: []string{objectmeta.TrafficManagerProfileFinalizer}, - }, - Spec: profile.Spec, - Status: fleetnetv1alpha1.TrafficManagerProfileStatus{ - Conditions: []metav1.Condition{ - { - Status: metav1.ConditionFalse, - Type: string(fleetnetv1alpha1.TrafficManagerProfileConditionProgrammed), - Reason: string(fleetnetv1alpha1.TrafficManagerProfileReasonInvalid), - }, - }, - }, - } - validator.ValidateTrafficManagerProfile(ctx, k8sClient, &want) - }) - - It("Deleting trafficManagerProfile", func() { - err := k8sClient.Delete(ctx, profile) - Expect(err).Should(Succeed(), "failed to delete trafficManagerProfile") - }) - - It("Validating trafficManagerProfile is deleted", func() { - validator.IsTrafficManagerProfileDeleted(ctx, k8sClient, types.NamespacedName{Namespace: testNamespace, Name: name}) - }) - }) - - Context("When creating trafficManagerProfile and azure request failed because of internal server error", Ordered, func() { - name := fakeprovider.InternalServerErrProfileName - var profile *fleetnetv1alpha1.TrafficManagerProfile - - It("AzureTrafficManager should not be configured", func() { - By("By creating a new TrafficManagerProfile") - profile = trafficManagerProfileForTest(name) - Expect(k8sClient.Create(ctx, profile)).Should(Succeed()) - - By("By checking profile") - want := fleetnetv1alpha1.TrafficManagerProfile{ - ObjectMeta: metav1.ObjectMeta{ - Name: name, - Namespace: testNamespace, - Finalizers: []string{objectmeta.TrafficManagerProfileFinalizer}, - }, - Spec: profile.Spec, - Status: fleetnetv1alpha1.TrafficManagerProfileStatus{ - Conditions: []metav1.Condition{ - { - Status: metav1.ConditionFalse, - Type: string(fleetnetv1alpha1.TrafficManagerProfileConditionProgrammed), - Reason: string(fleetnetv1alpha1.TrafficManagerProfileReasonPending), - }, - }, - }, - } - validator.ValidateTrafficManagerProfile(ctx, k8sClient, &want) - }) - - It("Deleting trafficManagerProfile", func() { - err := k8sClient.Delete(ctx, profile) - Expect(err).Should(Succeed(), "failed to delete trafficManagerProfile") - }) - - It("Validating trafficManagerProfile is deleted", func() { - validator.IsTrafficManagerProfileDeleted(ctx, k8sClient, types.NamespacedName{Namespace: testNamespace, Name: name}) - }) - }) -}) +/* +Copyright (c) Microsoft Corporation. +Licensed under the MIT license. +*/ + +package trafficmanagerprofile + +import ( + "fmt" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" + "k8s.io/utils/ptr" + + fleetnetv1alpha1 "go.goms.io/fleet-networking/api/v1alpha1" + "go.goms.io/fleet-networking/pkg/common/objectmeta" + "go.goms.io/fleet-networking/test/common/trafficmanager/fakeprovider" + "go.goms.io/fleet-networking/test/common/trafficmanager/validator" +) + +func trafficManagerProfileForTest(name string) *fleetnetv1alpha1.TrafficManagerProfile { + return &fleetnetv1alpha1.TrafficManagerProfile{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: testNamespace, + }, + Spec: fleetnetv1alpha1.TrafficManagerProfileSpec{ + MonitorConfig: &fleetnetv1alpha1.MonitorConfig{ + IntervalInSeconds: ptr.To[int64](30), + Path: ptr.To("/healthz"), + Port: ptr.To[int64](8080), + Protocol: ptr.To(fleetnetv1alpha1.TrafficManagerMonitorProtocolHTTPS), + TimeoutInSeconds: ptr.To[int64](10), + ToleratedNumberOfFailures: ptr.To[int64](5), + }, + }, + } +} + +var _ = Describe("Test TrafficManagerProfile Controller", func() { + Context("When updating existing valid trafficManagerProfile", Ordered, func() { + name := fakeprovider.ValidProfileName + var profile *fleetnetv1alpha1.TrafficManagerProfile + relativeDNSName := fmt.Sprintf(DNSRelativeNameFormat, testNamespace, name) + fqdn := fmt.Sprintf(fakeprovider.ProfileDNSNameFormat, relativeDNSName) + + It("AzureTrafficManager should be configured", func() { + By("By creating a new TrafficManagerProfile") + profile = trafficManagerProfileForTest(name) + Expect(k8sClient.Create(ctx, profile)).Should(Succeed()) + + By("By checking profile") + want := fleetnetv1alpha1.TrafficManagerProfile{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: testNamespace, + Finalizers: []string{objectmeta.TrafficManagerProfileFinalizer}, + }, + Spec: profile.Spec, + Status: fleetnetv1alpha1.TrafficManagerProfileStatus{ + DNSName: ptr.To(fqdn), + Conditions: []metav1.Condition{ + { + Status: metav1.ConditionTrue, + Type: string(fleetnetv1alpha1.TrafficManagerProfileConditionProgrammed), + Reason: string(fleetnetv1alpha1.TrafficManagerProfileReasonProgrammed), + }, + }, + }, + } + validator.ValidateTrafficManagerProfile(ctx, k8sClient, &want) + }) + + It("Update the trafficManagerProfile spec", func() { + Expect(k8sClient.Get(ctx, types.NamespacedName{Namespace: testNamespace, Name: name}, profile)).Should(Succeed(), "failed to get the trafficManagerProfile") + profile.Spec.MonitorConfig.IntervalInSeconds = ptr.To[int64](10) + profile.Spec.MonitorConfig.TimeoutInSeconds = ptr.To[int64](10) + Expect(k8sClient.Update(ctx, profile)).Should(Succeed(), "failed to update the trafficManagerProfile") + }) + + It("Validating trafficManagerProfile status and update should fail", func() { + want := fleetnetv1alpha1.TrafficManagerProfile{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: testNamespace, + Finalizers: []string{objectmeta.TrafficManagerProfileFinalizer}, + }, + Spec: profile.Spec, + Status: fleetnetv1alpha1.TrafficManagerProfileStatus{ + Conditions: []metav1.Condition{ + { + Status: metav1.ConditionFalse, + Type: string(fleetnetv1alpha1.TrafficManagerProfileConditionProgrammed), + Reason: string(fleetnetv1alpha1.TrafficManagerProfileReasonInvalid), + }, + }, + }, + } + validator.ValidateTrafficManagerProfile(ctx, k8sClient, &want) + }) + + It("Deleting trafficManagerProfile", func() { + err := k8sClient.Delete(ctx, profile) + Expect(err).Should(Succeed(), "failed to delete trafficManagerProfile") + }) + + It("Validating trafficManagerProfile is deleted", func() { + validator.IsTrafficManagerProfileDeleted(ctx, k8sClient, types.NamespacedName{Namespace: testNamespace, Name: name}) + }) + }) + + Context("When updating existing valid trafficManagerProfile with no changes", Ordered, func() { + name := fakeprovider.ValidProfileName + var profile *fleetnetv1alpha1.TrafficManagerProfile + + It("AzureTrafficManager should be configured", func() { + By("By creating a new TrafficManagerProfile") + profile = &fleetnetv1alpha1.TrafficManagerProfile{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: testNamespace, + }, + Spec: fleetnetv1alpha1.TrafficManagerProfileSpec{ + MonitorConfig: &fleetnetv1alpha1.MonitorConfig{ + IntervalInSeconds: ptr.To[int64](10), + Path: ptr.To("/healthz"), + Port: ptr.To[int64](8080), + Protocol: ptr.To(fleetnetv1alpha1.TrafficManagerMonitorProtocolHTTP), + TimeoutInSeconds: ptr.To[int64](9), + ToleratedNumberOfFailures: ptr.To[int64](4), + }, + }, + } + Expect(k8sClient.Create(ctx, profile)).Should(Succeed()) + + By("By checking profile") + want := fleetnetv1alpha1.TrafficManagerProfile{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: testNamespace, + Finalizers: []string{objectmeta.TrafficManagerProfileFinalizer}, + }, + Spec: profile.Spec, + Status: fleetnetv1alpha1.TrafficManagerProfileStatus{ + // The DNS name is returned by the fake Azure GET call. + DNSName: ptr.To(fmt.Sprintf(fakeprovider.ProfileDNSNameFormat, name)), + Conditions: []metav1.Condition{ + { + Status: metav1.ConditionTrue, + Type: string(fleetnetv1alpha1.TrafficManagerProfileConditionProgrammed), + Reason: string(fleetnetv1alpha1.TrafficManagerProfileReasonProgrammed), + }, + }, + }, + } + validator.ValidateTrafficManagerProfile(ctx, k8sClient, &want) + }) + + It("Deleting trafficManagerProfile", func() { + err := k8sClient.Delete(ctx, profile) + Expect(err).Should(Succeed(), "failed to delete trafficManagerProfile") + }) + + It("Validating trafficManagerProfile is deleted", func() { + validator.IsTrafficManagerProfileDeleted(ctx, k8sClient, types.NamespacedName{Namespace: testNamespace, Name: name}) + }) + }) + + Context("When creating trafficManagerProfile and DNS name is not available", Ordered, func() { + name := fakeprovider.ConflictErrProfileName + var profile *fleetnetv1alpha1.TrafficManagerProfile + + It("AzureTrafficManager should not be configured", func() { + By("By creating a new TrafficManagerProfile") + profile = trafficManagerProfileForTest(name) + Expect(k8sClient.Create(ctx, profile)).Should(Succeed()) + + By("By checking profile") + want := fleetnetv1alpha1.TrafficManagerProfile{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: testNamespace, + Finalizers: []string{objectmeta.TrafficManagerProfileFinalizer}, + }, + Spec: profile.Spec, + Status: fleetnetv1alpha1.TrafficManagerProfileStatus{ + Conditions: []metav1.Condition{ + { + Status: metav1.ConditionFalse, + Type: string(fleetnetv1alpha1.TrafficManagerProfileConditionProgrammed), + Reason: string(fleetnetv1alpha1.TrafficManagerProfileReasonDNSNameNotAvailable), + }, + }, + }, + } + validator.ValidateTrafficManagerProfile(ctx, k8sClient, &want) + }) + + It("Deleting trafficManagerProfile", func() { + err := k8sClient.Delete(ctx, profile) + Expect(err).Should(Succeed(), "failed to delete trafficManagerProfile") + }) + + It("Validating trafficManagerProfile is deleted", func() { + validator.IsTrafficManagerProfileDeleted(ctx, k8sClient, types.NamespacedName{Namespace: testNamespace, Name: name}) + }) + }) + + Context("When creating trafficManagerProfile and azure request failed because of too many requests", Ordered, func() { + name := fakeprovider.ThrottledErrProfileName + var profile *fleetnetv1alpha1.TrafficManagerProfile + + It("AzureTrafficManager should not be configured", func() { + By("By creating a new TrafficManagerProfile") + profile = trafficManagerProfileForTest(name) + Expect(k8sClient.Create(ctx, profile)).Should(Succeed()) + + By("By checking profile") + want := fleetnetv1alpha1.TrafficManagerProfile{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: testNamespace, + Finalizers: []string{objectmeta.TrafficManagerProfileFinalizer}, + }, + Spec: profile.Spec, + Status: fleetnetv1alpha1.TrafficManagerProfileStatus{ + Conditions: []metav1.Condition{ + { + Status: metav1.ConditionFalse, + Type: string(fleetnetv1alpha1.TrafficManagerProfileConditionProgrammed), + Reason: string(fleetnetv1alpha1.TrafficManagerProfileReasonPending), + }, + }, + }, + } + validator.ValidateTrafficManagerProfile(ctx, k8sClient, &want) + }) + + It("Deleting trafficManagerProfile", func() { + err := k8sClient.Delete(ctx, profile) + Expect(err).Should(Succeed(), "failed to delete trafficManagerProfile") + }) + + It("Validating trafficManagerProfile is deleted", func() { + validator.IsTrafficManagerProfileDeleted(ctx, k8sClient, types.NamespacedName{Namespace: testNamespace, Name: name}) + }) + }) + + Context("When creating trafficManagerProfile and azure request failed because of client side error", Ordered, func() { + name := "bad-request" + var profile *fleetnetv1alpha1.TrafficManagerProfile + + It("AzureTrafficManager should not be configured", func() { + By("By creating a new TrafficManagerProfile") + profile = trafficManagerProfileForTest(name) + Expect(k8sClient.Create(ctx, profile)).Should(Succeed()) + + By("By checking profile") + want := fleetnetv1alpha1.TrafficManagerProfile{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: testNamespace, + Finalizers: []string{objectmeta.TrafficManagerProfileFinalizer}, + }, + Spec: profile.Spec, + Status: fleetnetv1alpha1.TrafficManagerProfileStatus{ + Conditions: []metav1.Condition{ + { + Status: metav1.ConditionFalse, + Type: string(fleetnetv1alpha1.TrafficManagerProfileConditionProgrammed), + Reason: string(fleetnetv1alpha1.TrafficManagerProfileReasonInvalid), + }, + }, + }, + } + validator.ValidateTrafficManagerProfile(ctx, k8sClient, &want) + }) + + It("Deleting trafficManagerProfile", func() { + err := k8sClient.Delete(ctx, profile) + Expect(err).Should(Succeed(), "failed to delete trafficManagerProfile") + }) + + It("Validating trafficManagerProfile is deleted", func() { + validator.IsTrafficManagerProfileDeleted(ctx, k8sClient, types.NamespacedName{Namespace: testNamespace, Name: name}) + }) + }) + + Context("When creating trafficManagerProfile and azure request failed because of internal server error", Ordered, func() { + name := fakeprovider.InternalServerErrProfileName + var profile *fleetnetv1alpha1.TrafficManagerProfile + + It("AzureTrafficManager should not be configured", func() { + By("By creating a new TrafficManagerProfile") + profile = trafficManagerProfileForTest(name) + Expect(k8sClient.Create(ctx, profile)).Should(Succeed()) + + By("By checking profile") + want := fleetnetv1alpha1.TrafficManagerProfile{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: testNamespace, + Finalizers: []string{objectmeta.TrafficManagerProfileFinalizer}, + }, + Spec: profile.Spec, + Status: fleetnetv1alpha1.TrafficManagerProfileStatus{ + Conditions: []metav1.Condition{ + { + Status: metav1.ConditionFalse, + Type: string(fleetnetv1alpha1.TrafficManagerProfileConditionProgrammed), + Reason: string(fleetnetv1alpha1.TrafficManagerProfileReasonPending), + }, + }, + }, + } + validator.ValidateTrafficManagerProfile(ctx, k8sClient, &want) + }) + + It("Deleting trafficManagerProfile", func() { + err := k8sClient.Delete(ctx, profile) + Expect(err).Should(Succeed(), "failed to delete trafficManagerProfile") + }) + + It("Validating trafficManagerProfile is deleted", func() { + validator.IsTrafficManagerProfileDeleted(ctx, k8sClient, types.NamespacedName{Namespace: testNamespace, Name: name}) + }) + }) +}) diff --git a/pkg/controllers/hub/trafficmanagerprofile/controller_test.go b/pkg/controllers/hub/trafficmanagerprofile/controller_test.go index bec9ebcb..cbb0e78e 100644 --- a/pkg/controllers/hub/trafficmanagerprofile/controller_test.go +++ b/pkg/controllers/hub/trafficmanagerprofile/controller_test.go @@ -1,113 +1,113 @@ -/* -Copyright (c) Microsoft Corporation. -Licensed under the MIT license. -*/ - -package trafficmanagerprofile - -import ( - "testing" - - "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/trafficmanager/armtrafficmanager" - "github.com/google/go-cmp/cmp" - "k8s.io/utils/ptr" - - fleetnetv1alpha1 "go.goms.io/fleet-networking/api/v1alpha1" -) - -func TestConvertToTrafficManagerProfileSpec(t *testing.T) { - tests := []struct { - name string - profile *armtrafficmanager.Profile - want fleetnetv1alpha1.TrafficManagerProfileSpec - }{ - { - name: "nil properties", // not possible in production - profile: &armtrafficmanager.Profile{}, - want: fleetnetv1alpha1.TrafficManagerProfileSpec{}, - }, - { - name: "nil monitor protocol", // not possible in production - profile: &armtrafficmanager.Profile{ - Properties: &armtrafficmanager.ProfileProperties{ - MonitorConfig: &armtrafficmanager.MonitorConfig{ - IntervalInSeconds: ptr.To(int64(10)), - Path: ptr.To("/healthz"), - Port: ptr.To(int64(8080)), - TimeoutInSeconds: ptr.To(int64(9)), - ToleratedNumberOfFailures: ptr.To(int64(4)), - }, - }, - }, - want: fleetnetv1alpha1.TrafficManagerProfileSpec{ - MonitorConfig: &fleetnetv1alpha1.MonitorConfig{ - IntervalInSeconds: ptr.To(int64(10)), - Path: ptr.To("/healthz"), - Port: ptr.To(int64(8080)), - Protocol: (*fleetnetv1alpha1.TrafficManagerMonitorProtocol)(ptr.To("")), - TimeoutInSeconds: ptr.To(int64(9)), - ToleratedNumberOfFailures: ptr.To(int64(4)), - }, - }, - }, - { - name: "invalid monitor protocol", // not possible in production - profile: &armtrafficmanager.Profile{ - Properties: &armtrafficmanager.ProfileProperties{ - MonitorConfig: &armtrafficmanager.MonitorConfig{ - IntervalInSeconds: ptr.To(int64(10)), - Path: ptr.To("/healthz"), - Port: ptr.To(int64(8080)), - Protocol: (*armtrafficmanager.MonitorProtocol)(ptr.To("UDP")), - TimeoutInSeconds: ptr.To(int64(9)), - ToleratedNumberOfFailures: ptr.To(int64(4)), - }, - }, - }, - want: fleetnetv1alpha1.TrafficManagerProfileSpec{ - MonitorConfig: &fleetnetv1alpha1.MonitorConfig{ - IntervalInSeconds: ptr.To(int64(10)), - Path: ptr.To("/healthz"), - Port: ptr.To(int64(8080)), - Protocol: (*fleetnetv1alpha1.TrafficManagerMonitorProtocol)(ptr.To("UDP")), - TimeoutInSeconds: ptr.To(int64(9)), - ToleratedNumberOfFailures: ptr.To(int64(4)), - }, - }, - }, - { - name: "valid profile", // not possible in production - profile: &armtrafficmanager.Profile{ - Properties: &armtrafficmanager.ProfileProperties{ - MonitorConfig: &armtrafficmanager.MonitorConfig{ - IntervalInSeconds: ptr.To(int64(10)), - Path: ptr.To("/healthz"), - Port: ptr.To(int64(8080)), - Protocol: ptr.To(armtrafficmanager.MonitorProtocolHTTP), - TimeoutInSeconds: ptr.To(int64(9)), - ToleratedNumberOfFailures: ptr.To(int64(4)), - }, - }, - }, - want: fleetnetv1alpha1.TrafficManagerProfileSpec{ - MonitorConfig: &fleetnetv1alpha1.MonitorConfig{ - IntervalInSeconds: ptr.To(int64(10)), - Path: ptr.To("/healthz"), - Port: ptr.To(int64(8080)), - Protocol: ptr.To(fleetnetv1alpha1.TrafficManagerMonitorProtocolHTTP), - TimeoutInSeconds: ptr.To(int64(9)), - ToleratedNumberOfFailures: ptr.To(int64(4)), - }, - }, - }, - } - - for _, tc := range tests { - t.Run(tc.name, func(t *testing.T) { - got := convertToTrafficManagerProfileSpec(tc.profile) - if diff := cmp.Diff(tc.want, got); diff != "" { - t.Errorf("convertToTrafficManagerProfileSpec() mismatch (-want +got):\n%s", diff) - } - }) - } -} +/* +Copyright (c) Microsoft Corporation. +Licensed under the MIT license. +*/ + +package trafficmanagerprofile + +import ( + "testing" + + "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/trafficmanager/armtrafficmanager" + "github.com/google/go-cmp/cmp" + "k8s.io/utils/ptr" + + fleetnetv1alpha1 "go.goms.io/fleet-networking/api/v1alpha1" +) + +func TestConvertToTrafficManagerProfileSpec(t *testing.T) { + tests := []struct { + name string + profile *armtrafficmanager.Profile + want fleetnetv1alpha1.TrafficManagerProfileSpec + }{ + { + name: "nil properties", // not possible in production + profile: &armtrafficmanager.Profile{}, + want: fleetnetv1alpha1.TrafficManagerProfileSpec{}, + }, + { + name: "nil monitor protocol", // not possible in production + profile: &armtrafficmanager.Profile{ + Properties: &armtrafficmanager.ProfileProperties{ + MonitorConfig: &armtrafficmanager.MonitorConfig{ + IntervalInSeconds: ptr.To(int64(10)), + Path: ptr.To("/healthz"), + Port: ptr.To(int64(8080)), + TimeoutInSeconds: ptr.To(int64(9)), + ToleratedNumberOfFailures: ptr.To(int64(4)), + }, + }, + }, + want: fleetnetv1alpha1.TrafficManagerProfileSpec{ + MonitorConfig: &fleetnetv1alpha1.MonitorConfig{ + IntervalInSeconds: ptr.To(int64(10)), + Path: ptr.To("/healthz"), + Port: ptr.To(int64(8080)), + Protocol: (*fleetnetv1alpha1.TrafficManagerMonitorProtocol)(ptr.To("")), + TimeoutInSeconds: ptr.To(int64(9)), + ToleratedNumberOfFailures: ptr.To(int64(4)), + }, + }, + }, + { + name: "invalid monitor protocol", // not possible in production + profile: &armtrafficmanager.Profile{ + Properties: &armtrafficmanager.ProfileProperties{ + MonitorConfig: &armtrafficmanager.MonitorConfig{ + IntervalInSeconds: ptr.To(int64(10)), + Path: ptr.To("/healthz"), + Port: ptr.To(int64(8080)), + Protocol: (*armtrafficmanager.MonitorProtocol)(ptr.To("UDP")), + TimeoutInSeconds: ptr.To(int64(9)), + ToleratedNumberOfFailures: ptr.To(int64(4)), + }, + }, + }, + want: fleetnetv1alpha1.TrafficManagerProfileSpec{ + MonitorConfig: &fleetnetv1alpha1.MonitorConfig{ + IntervalInSeconds: ptr.To(int64(10)), + Path: ptr.To("/healthz"), + Port: ptr.To(int64(8080)), + Protocol: (*fleetnetv1alpha1.TrafficManagerMonitorProtocol)(ptr.To("UDP")), + TimeoutInSeconds: ptr.To(int64(9)), + ToleratedNumberOfFailures: ptr.To(int64(4)), + }, + }, + }, + { + name: "valid profile", // not possible in production + profile: &armtrafficmanager.Profile{ + Properties: &armtrafficmanager.ProfileProperties{ + MonitorConfig: &armtrafficmanager.MonitorConfig{ + IntervalInSeconds: ptr.To(int64(10)), + Path: ptr.To("/healthz"), + Port: ptr.To(int64(8080)), + Protocol: ptr.To(armtrafficmanager.MonitorProtocolHTTP), + TimeoutInSeconds: ptr.To(int64(9)), + ToleratedNumberOfFailures: ptr.To(int64(4)), + }, + }, + }, + want: fleetnetv1alpha1.TrafficManagerProfileSpec{ + MonitorConfig: &fleetnetv1alpha1.MonitorConfig{ + IntervalInSeconds: ptr.To(int64(10)), + Path: ptr.To("/healthz"), + Port: ptr.To(int64(8080)), + Protocol: ptr.To(fleetnetv1alpha1.TrafficManagerMonitorProtocolHTTP), + TimeoutInSeconds: ptr.To(int64(9)), + ToleratedNumberOfFailures: ptr.To(int64(4)), + }, + }, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + got := convertToTrafficManagerProfileSpec(tc.profile) + if diff := cmp.Diff(tc.want, got); diff != "" { + t.Errorf("convertToTrafficManagerProfileSpec() mismatch (-want +got):\n%s", diff) + } + }) + } +} diff --git a/pkg/controllers/hub/trafficmanagerprofile/suite_test.go b/pkg/controllers/hub/trafficmanagerprofile/suite_test.go index d9a9d68a..3e468db5 100644 --- a/pkg/controllers/hub/trafficmanagerprofile/suite_test.go +++ b/pkg/controllers/hub/trafficmanagerprofile/suite_test.go @@ -1,140 +1,140 @@ -/* -Copyright (c) Microsoft Corporation. -Licensed under the MIT license. -*/ - -package trafficmanagerprofile - -import ( - "context" - "flag" - "path/filepath" - "testing" - - . "github.com/onsi/ginkgo/v2" - . "github.com/onsi/gomega" - corev1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/client-go/kubernetes/scheme" - "k8s.io/client-go/rest" - "k8s.io/klog/v2" - ctrl "sigs.k8s.io/controller-runtime" - "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/envtest" - "sigs.k8s.io/controller-runtime/pkg/log/zap" - "sigs.k8s.io/controller-runtime/pkg/manager" - metricsserver "sigs.k8s.io/controller-runtime/pkg/metrics/server" - - fleetnetv1alpha1 "go.goms.io/fleet-networking/api/v1alpha1" - "go.goms.io/fleet-networking/test/common/trafficmanager/fakeprovider" -) - -var ( - cfg *rest.Config - mgr manager.Manager - k8sClient client.Client - testEnv *envtest.Environment - ctx context.Context - cancel context.CancelFunc -) - -const ( - testNamespace = "profile-ns" -) - -var ( - originalGenerateAzureTrafficManagerProfileNameFunc = generateAzureTrafficManagerProfileNameFunc -) - -func TestAPIs(t *testing.T) { - RegisterFailHandler(Fail) - - RunSpecs(t, "TrafficManagerProfile Controller Suite") -} - -var _ = BeforeSuite(func() { - klog.SetLogger(zap.New(zap.WriteTo(GinkgoWriter), zap.UseDevMode(true))) - - ctx, cancel = context.WithCancel(context.TODO()) - - By("bootstrapping test environment") - testEnv = &envtest.Environment{ - CRDDirectoryPaths: []string{filepath.Join("../../../../", "config", "crd", "bases")}, - ErrorIfCRDPathMissing: true, - } - - var err error - cfg, err = testEnv.Start() - Expect(err).NotTo(HaveOccurred()) - Expect(cfg).NotTo(BeNil()) - - err = fleetnetv1alpha1.AddToScheme(scheme.Scheme) - Expect(err).NotTo(HaveOccurred()) - - //+kubebuilder:scaffold:scheme - By("construct the k8s client") - k8sClient, err = client.New(cfg, client.Options{Scheme: scheme.Scheme}) - Expect(err).NotTo(HaveOccurred()) - Expect(k8sClient).NotTo(BeNil()) - - By("starting the controller manager") - klog.InitFlags(flag.CommandLine) - flag.Parse() - - mgr, err = ctrl.NewManager(cfg, ctrl.Options{ - Scheme: scheme.Scheme, - Metrics: metricsserver.Options{ - BindAddress: "0", - }, - }) - Expect(err).NotTo(HaveOccurred()) - - profileClient, err := fakeprovider.NewProfileClient(ctx, "default-sub") - Expect(err).Should(Succeed(), "failed to create the fake profile client") - - generateAzureTrafficManagerProfileNameFunc = func(profile *fleetnetv1alpha1.TrafficManagerProfile) string { - return profile.Name - } - - err = (&Reconciler{ - Client: mgr.GetClient(), - ProfilesClient: profileClient, - ResourceGroupName: fakeprovider.DefaultResourceGroupName, - }).SetupWithManager(mgr) - Expect(err).ToNot(HaveOccurred()) - - ctx, cancel = context.WithCancel(context.TODO()) - - By("Create profile namespace") - ns := corev1.Namespace{ - ObjectMeta: metav1.ObjectMeta{ - Name: testNamespace, - }, - } - Expect(k8sClient.Create(ctx, &ns)).Should(Succeed()) - - go func() { - defer GinkgoRecover() - err = mgr.Start(ctx) - Expect(err).ToNot(HaveOccurred(), "failed to run manager") - }() -}) - -var _ = AfterSuite(func() { - defer klog.Flush() - - By("delete profile namespace") - ns := corev1.Namespace{ - ObjectMeta: metav1.ObjectMeta{ - Name: testNamespace, - }, - } - Expect(k8sClient.Delete(ctx, &ns)).Should(Succeed()) - - cancel() - By("tearing down the test environment") - err := testEnv.Stop() - Expect(err).NotTo(HaveOccurred()) - - generateAzureTrafficManagerProfileNameFunc = originalGenerateAzureTrafficManagerProfileNameFunc -}) +/* +Copyright (c) Microsoft Corporation. +Licensed under the MIT license. +*/ + +package trafficmanagerprofile + +import ( + "context" + "flag" + "path/filepath" + "testing" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/kubernetes/scheme" + "k8s.io/client-go/rest" + "k8s.io/klog/v2" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/envtest" + "sigs.k8s.io/controller-runtime/pkg/log/zap" + "sigs.k8s.io/controller-runtime/pkg/manager" + metricsserver "sigs.k8s.io/controller-runtime/pkg/metrics/server" + + fleetnetv1alpha1 "go.goms.io/fleet-networking/api/v1alpha1" + "go.goms.io/fleet-networking/test/common/trafficmanager/fakeprovider" +) + +var ( + cfg *rest.Config + mgr manager.Manager + k8sClient client.Client + testEnv *envtest.Environment + ctx context.Context + cancel context.CancelFunc +) + +const ( + testNamespace = "profile-ns" +) + +var ( + originalGenerateAzureTrafficManagerProfileNameFunc = generateAzureTrafficManagerProfileNameFunc +) + +func TestAPIs(t *testing.T) { + RegisterFailHandler(Fail) + + RunSpecs(t, "TrafficManagerProfile Controller Suite") +} + +var _ = BeforeSuite(func() { + klog.SetLogger(zap.New(zap.WriteTo(GinkgoWriter), zap.UseDevMode(true))) + + ctx, cancel = context.WithCancel(context.TODO()) + + By("bootstrapping test environment") + testEnv = &envtest.Environment{ + CRDDirectoryPaths: []string{filepath.Join("../../../../", "config", "crd", "bases")}, + ErrorIfCRDPathMissing: true, + } + + var err error + cfg, err = testEnv.Start() + Expect(err).NotTo(HaveOccurred()) + Expect(cfg).NotTo(BeNil()) + + err = fleetnetv1alpha1.AddToScheme(scheme.Scheme) + Expect(err).NotTo(HaveOccurred()) + + //+kubebuilder:scaffold:scheme + By("construct the k8s client") + k8sClient, err = client.New(cfg, client.Options{Scheme: scheme.Scheme}) + Expect(err).NotTo(HaveOccurred()) + Expect(k8sClient).NotTo(BeNil()) + + By("starting the controller manager") + klog.InitFlags(flag.CommandLine) + flag.Parse() + + mgr, err = ctrl.NewManager(cfg, ctrl.Options{ + Scheme: scheme.Scheme, + Metrics: metricsserver.Options{ + BindAddress: "0", + }, + }) + Expect(err).NotTo(HaveOccurred()) + + profileClient, err := fakeprovider.NewProfileClient(ctx, "default-sub") + Expect(err).Should(Succeed(), "failed to create the fake profile client") + + generateAzureTrafficManagerProfileNameFunc = func(profile *fleetnetv1alpha1.TrafficManagerProfile) string { + return profile.Name + } + + err = (&Reconciler{ + Client: mgr.GetClient(), + ProfilesClient: profileClient, + ResourceGroupName: fakeprovider.DefaultResourceGroupName, + }).SetupWithManager(mgr) + Expect(err).ToNot(HaveOccurred()) + + ctx, cancel = context.WithCancel(context.TODO()) + + By("Create profile namespace") + ns := corev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: testNamespace, + }, + } + Expect(k8sClient.Create(ctx, &ns)).Should(Succeed()) + + go func() { + defer GinkgoRecover() + err = mgr.Start(ctx) + Expect(err).ToNot(HaveOccurred(), "failed to run manager") + }() +}) + +var _ = AfterSuite(func() { + defer klog.Flush() + + By("delete profile namespace") + ns := corev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: testNamespace, + }, + } + Expect(k8sClient.Delete(ctx, &ns)).Should(Succeed()) + + cancel() + By("tearing down the test environment") + err := testEnv.Stop() + Expect(err).NotTo(HaveOccurred()) + + generateAzureTrafficManagerProfileNameFunc = originalGenerateAzureTrafficManagerProfileNameFunc +}) diff --git a/test/common/trafficmanager/fakeprovider/profile.go b/test/common/trafficmanager/fakeprovider/profile.go index 4a2a8b2b..53150478 100644 --- a/test/common/trafficmanager/fakeprovider/profile.go +++ b/test/common/trafficmanager/fakeprovider/profile.go @@ -1,150 +1,150 @@ -/* -Copyright (c) Microsoft Corporation. -Licensed under the MIT license. -*/ - -// Package fakeprovider provides a fake azure implementation of traffic manager resources. -package fakeprovider - -import ( - "context" - "fmt" - "net/http" - - "github.com/Azure/azure-sdk-for-go/sdk/azcore" - "github.com/Azure/azure-sdk-for-go/sdk/azcore/arm" - "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/trafficmanager/armtrafficmanager/fake" - "k8s.io/utils/ptr" - - azcorefake "github.com/Azure/azure-sdk-for-go/sdk/azcore/fake" - "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/trafficmanager/armtrafficmanager" -) - -const ( - DefaultResourceGroupName = "default-resource-group-name" - - ValidProfileName = "valid-profile" - ConflictErrProfileName = "conflict-err-profile" - InternalServerErrProfileName = "internal-server-err-profile" - ThrottledErrProfileName = "throttled-err-profile" - - ProfileDNSNameFormat = "%s.trafficmanager.net" -) - -// NewProfileClient creates a client which talks to a fake profile server. -func NewProfileClient(_ context.Context, subscriptionID string) (*armtrafficmanager.ProfilesClient, error) { - fakeServer := fake.ProfilesServer{ - CreateOrUpdate: CreateOrUpdate, - Delete: Delete, - Get: Get, - } - clientFactory, err := armtrafficmanager.NewClientFactory(subscriptionID, &azcorefake.TokenCredential{}, - &arm.ClientOptions{ - ClientOptions: azcore.ClientOptions{ - Transport: fake.NewProfilesServerTransport(&fakeServer), - }, - }) - if err != nil { - return nil, err - } - return clientFactory.NewProfilesClient(), nil -} - -// Get returns the http status code based on the profileName. -func Get(_ context.Context, resourceGroupName string, profileName string, _ *armtrafficmanager.ProfilesClientGetOptions) (resp azcorefake.Responder[armtrafficmanager.ProfilesClientGetResponse], errResp azcorefake.ErrorResponder) { - if resourceGroupName != DefaultResourceGroupName { - errResp.SetResponseError(http.StatusNotFound, "ResourceGroupNotFound") - return resp, errResp - } - switch profileName { - case ValidProfileName: - profileResp := armtrafficmanager.ProfilesClientGetResponse{ - Profile: armtrafficmanager.Profile{ - Name: ptr.To(profileName), - Location: ptr.To("global"), - Properties: &armtrafficmanager.ProfileProperties{ - DNSConfig: &armtrafficmanager.DNSConfig{ - Fqdn: ptr.To(fmt.Sprintf(ProfileDNSNameFormat, profileName)), - RelativeName: ptr.To(profileName), - TTL: ptr.To[int64](30), - }, - Endpoints: []*armtrafficmanager.Endpoint{}, - MonitorConfig: &armtrafficmanager.MonitorConfig{ - IntervalInSeconds: ptr.To(int64(10)), - Path: ptr.To("/healthz"), - Port: ptr.To(int64(8080)), - Protocol: ptr.To(armtrafficmanager.MonitorProtocolHTTP), - TimeoutInSeconds: ptr.To(int64(9)), - ToleratedNumberOfFailures: ptr.To(int64(4)), - }, - ProfileStatus: ptr.To(armtrafficmanager.ProfileStatusEnabled), - TrafficRoutingMethod: ptr.To(armtrafficmanager.TrafficRoutingMethodWeighted), - TrafficViewEnrollmentStatus: ptr.To(armtrafficmanager.TrafficViewEnrollmentStatusDisabled), - }, - }} - resp.SetResponse(http.StatusOK, profileResp, nil) - default: - errResp.SetResponseError(http.StatusNotFound, "NotFoundError") - } - return resp, errResp -} - -// CreateOrUpdate returns the http status code based on the profileName. -func CreateOrUpdate(_ context.Context, resourceGroupName string, profileName string, parameters armtrafficmanager.Profile, _ *armtrafficmanager.ProfilesClientCreateOrUpdateOptions) (resp azcorefake.Responder[armtrafficmanager.ProfilesClientCreateOrUpdateResponse], errResp azcorefake.ErrorResponder) { - if resourceGroupName != DefaultResourceGroupName { - errResp.SetResponseError(http.StatusNotFound, "ResourceGroupNotFound") - return resp, errResp - } - switch profileName { - case ConflictErrProfileName: - errResp.SetResponseError(http.StatusConflict, "Conflict") - case InternalServerErrProfileName: - errResp.SetResponseError(http.StatusInternalServerError, "InternalServerError") - case ThrottledErrProfileName: - errResp.SetResponseError(http.StatusTooManyRequests, "ThrottledError") - case ValidProfileName: - if parameters.Properties.MonitorConfig.IntervalInSeconds != nil && *parameters.Properties.MonitorConfig.IntervalInSeconds == 10 { - if parameters.Properties.MonitorConfig.TimeoutInSeconds != nil && *parameters.Properties.MonitorConfig.TimeoutInSeconds > 9 { - errResp.SetResponseError(http.StatusBadRequest, "BadRequestError") - return - } - } - profileResp := armtrafficmanager.ProfilesClientCreateOrUpdateResponse{ - Profile: armtrafficmanager.Profile{ - Name: ptr.To(profileName), - Location: ptr.To("global"), - Properties: &armtrafficmanager.ProfileProperties{ - DNSConfig: &armtrafficmanager.DNSConfig{ - Fqdn: ptr.To(fmt.Sprintf(ProfileDNSNameFormat, *parameters.Properties.DNSConfig.RelativeName)), - RelativeName: parameters.Properties.DNSConfig.RelativeName, - TTL: ptr.To[int64](30), - }, - Endpoints: []*armtrafficmanager.Endpoint{}, - MonitorConfig: parameters.Properties.MonitorConfig, - ProfileStatus: ptr.To(armtrafficmanager.ProfileStatusEnabled), - TrafficRoutingMethod: ptr.To(armtrafficmanager.TrafficRoutingMethodWeighted), - TrafficViewEnrollmentStatus: ptr.To(armtrafficmanager.TrafficViewEnrollmentStatusDisabled), - }, - }} - resp.SetResponse(http.StatusOK, profileResp, nil) - default: - errResp.SetResponseError(http.StatusBadRequest, "BadRequestError") - } - return resp, errResp -} - -// Delete returns the http status code based on the profileName. -func Delete(_ context.Context, resourceGroupName string, profileName string, _ *armtrafficmanager.ProfilesClientDeleteOptions) (resp azcorefake.Responder[armtrafficmanager.ProfilesClientDeleteResponse], errResp azcorefake.ErrorResponder) { - if resourceGroupName != DefaultResourceGroupName { - errResp.SetResponseError(http.StatusNotFound, "ResourceGroupNotFound") - return resp, errResp - } - switch profileName { - case ValidProfileName: - profileResp := armtrafficmanager.ProfilesClientDeleteResponse{} - resp.SetResponse(http.StatusOK, profileResp, nil) - default: - errResp.SetResponseError(http.StatusNotFound, "NotFound") - } - return resp, errResp -} +/* +Copyright (c) Microsoft Corporation. +Licensed under the MIT license. +*/ + +// Package fakeprovider provides a fake azure implementation of traffic manager resources. +package fakeprovider + +import ( + "context" + "fmt" + "net/http" + + "github.com/Azure/azure-sdk-for-go/sdk/azcore" + "github.com/Azure/azure-sdk-for-go/sdk/azcore/arm" + "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/trafficmanager/armtrafficmanager/fake" + "k8s.io/utils/ptr" + + azcorefake "github.com/Azure/azure-sdk-for-go/sdk/azcore/fake" + "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/trafficmanager/armtrafficmanager" +) + +const ( + DefaultResourceGroupName = "default-resource-group-name" + + ValidProfileName = "valid-profile" + ConflictErrProfileName = "conflict-err-profile" + InternalServerErrProfileName = "internal-server-err-profile" + ThrottledErrProfileName = "throttled-err-profile" + + ProfileDNSNameFormat = "%s.trafficmanager.net" +) + +// NewProfileClient creates a client which talks to a fake profile server. +func NewProfileClient(_ context.Context, subscriptionID string) (*armtrafficmanager.ProfilesClient, error) { + fakeServer := fake.ProfilesServer{ + CreateOrUpdate: CreateOrUpdate, + Delete: Delete, + Get: Get, + } + clientFactory, err := armtrafficmanager.NewClientFactory(subscriptionID, &azcorefake.TokenCredential{}, + &arm.ClientOptions{ + ClientOptions: azcore.ClientOptions{ + Transport: fake.NewProfilesServerTransport(&fakeServer), + }, + }) + if err != nil { + return nil, err + } + return clientFactory.NewProfilesClient(), nil +} + +// Get returns the http status code based on the profileName. +func Get(_ context.Context, resourceGroupName string, profileName string, _ *armtrafficmanager.ProfilesClientGetOptions) (resp azcorefake.Responder[armtrafficmanager.ProfilesClientGetResponse], errResp azcorefake.ErrorResponder) { + if resourceGroupName != DefaultResourceGroupName { + errResp.SetResponseError(http.StatusNotFound, "ResourceGroupNotFound") + return resp, errResp + } + switch profileName { + case ValidProfileName: + profileResp := armtrafficmanager.ProfilesClientGetResponse{ + Profile: armtrafficmanager.Profile{ + Name: ptr.To(profileName), + Location: ptr.To("global"), + Properties: &armtrafficmanager.ProfileProperties{ + DNSConfig: &armtrafficmanager.DNSConfig{ + Fqdn: ptr.To(fmt.Sprintf(ProfileDNSNameFormat, profileName)), + RelativeName: ptr.To(profileName), + TTL: ptr.To[int64](30), + }, + Endpoints: []*armtrafficmanager.Endpoint{}, + MonitorConfig: &armtrafficmanager.MonitorConfig{ + IntervalInSeconds: ptr.To(int64(10)), + Path: ptr.To("/healthz"), + Port: ptr.To(int64(8080)), + Protocol: ptr.To(armtrafficmanager.MonitorProtocolHTTP), + TimeoutInSeconds: ptr.To(int64(9)), + ToleratedNumberOfFailures: ptr.To(int64(4)), + }, + ProfileStatus: ptr.To(armtrafficmanager.ProfileStatusEnabled), + TrafficRoutingMethod: ptr.To(armtrafficmanager.TrafficRoutingMethodWeighted), + TrafficViewEnrollmentStatus: ptr.To(armtrafficmanager.TrafficViewEnrollmentStatusDisabled), + }, + }} + resp.SetResponse(http.StatusOK, profileResp, nil) + default: + errResp.SetResponseError(http.StatusNotFound, "NotFoundError") + } + return resp, errResp +} + +// CreateOrUpdate returns the http status code based on the profileName. +func CreateOrUpdate(_ context.Context, resourceGroupName string, profileName string, parameters armtrafficmanager.Profile, _ *armtrafficmanager.ProfilesClientCreateOrUpdateOptions) (resp azcorefake.Responder[armtrafficmanager.ProfilesClientCreateOrUpdateResponse], errResp azcorefake.ErrorResponder) { + if resourceGroupName != DefaultResourceGroupName { + errResp.SetResponseError(http.StatusNotFound, "ResourceGroupNotFound") + return resp, errResp + } + switch profileName { + case ConflictErrProfileName: + errResp.SetResponseError(http.StatusConflict, "Conflict") + case InternalServerErrProfileName: + errResp.SetResponseError(http.StatusInternalServerError, "InternalServerError") + case ThrottledErrProfileName: + errResp.SetResponseError(http.StatusTooManyRequests, "ThrottledError") + case ValidProfileName: + if parameters.Properties.MonitorConfig.IntervalInSeconds != nil && *parameters.Properties.MonitorConfig.IntervalInSeconds == 10 { + if parameters.Properties.MonitorConfig.TimeoutInSeconds != nil && *parameters.Properties.MonitorConfig.TimeoutInSeconds > 9 { + errResp.SetResponseError(http.StatusBadRequest, "BadRequestError") + return + } + } + profileResp := armtrafficmanager.ProfilesClientCreateOrUpdateResponse{ + Profile: armtrafficmanager.Profile{ + Name: ptr.To(profileName), + Location: ptr.To("global"), + Properties: &armtrafficmanager.ProfileProperties{ + DNSConfig: &armtrafficmanager.DNSConfig{ + Fqdn: ptr.To(fmt.Sprintf(ProfileDNSNameFormat, *parameters.Properties.DNSConfig.RelativeName)), + RelativeName: parameters.Properties.DNSConfig.RelativeName, + TTL: ptr.To[int64](30), + }, + Endpoints: []*armtrafficmanager.Endpoint{}, + MonitorConfig: parameters.Properties.MonitorConfig, + ProfileStatus: ptr.To(armtrafficmanager.ProfileStatusEnabled), + TrafficRoutingMethod: ptr.To(armtrafficmanager.TrafficRoutingMethodWeighted), + TrafficViewEnrollmentStatus: ptr.To(armtrafficmanager.TrafficViewEnrollmentStatusDisabled), + }, + }} + resp.SetResponse(http.StatusOK, profileResp, nil) + default: + errResp.SetResponseError(http.StatusBadRequest, "BadRequestError") + } + return resp, errResp +} + +// Delete returns the http status code based on the profileName. +func Delete(_ context.Context, resourceGroupName string, profileName string, _ *armtrafficmanager.ProfilesClientDeleteOptions) (resp azcorefake.Responder[armtrafficmanager.ProfilesClientDeleteResponse], errResp azcorefake.ErrorResponder) { + if resourceGroupName != DefaultResourceGroupName { + errResp.SetResponseError(http.StatusNotFound, "ResourceGroupNotFound") + return resp, errResp + } + switch profileName { + case ValidProfileName: + profileResp := armtrafficmanager.ProfilesClientDeleteResponse{} + resp.SetResponse(http.StatusOK, profileResp, nil) + default: + errResp.SetResponseError(http.StatusNotFound, "NotFound") + } + return resp, errResp +}