From dfe71085a6c15aae3d8b3f63c46330c11f05de53 Mon Sep 17 00:00:00 2001 From: b-lnimmala Date: Wed, 29 Apr 2026 06:59:14 -0400 Subject: [PATCH 01/16] ARO-3837: Add EncryptionAtHost feature flag validation before cluster provisioning --- pkg/frontend/encryptionathost_validation.go | 57 +++++++++++++++++++++ pkg/frontend/sku_validation.go | 10 +++- 2 files changed, 66 insertions(+), 1 deletion(-) create mode 100644 pkg/frontend/encryptionathost_validation.go diff --git a/pkg/frontend/encryptionathost_validation.go b/pkg/frontend/encryptionathost_validation.go new file mode 100644 index 00000000000..e8a0b9ac8ea --- /dev/null +++ b/pkg/frontend/encryptionathost_validation.go @@ -0,0 +1,57 @@ +package frontend + +// Copyright (c) Microsoft Corporation. +// Licensed under the Apache License 2.0. + +import ( + "context" + "fmt" + "net/http" + + "github.com/Azure/ARO-RP/pkg/api" + "github.com/Azure/ARO-RP/pkg/env" + "github.com/Azure/ARO-RP/pkg/util/azureclient" + "github.com/Azure/ARO-RP/pkg/util/azureclient/mgmt/features" +) + +func validateEncryptionAtHostFeature( + ctx context.Context, + azEnv *azureclient.AROEnvironment, + environment env.Interface, + subscriptionID, tenantID string) error { + + fpAuthorizer, err := environment.FPAuthorizer( + tenantID, nil, + environment.Environment().ResourceManagerScope) + if err != nil { + return err + } + + providersClient := features.NewProvidersClient( + azEnv, subscriptionID, fpAuthorizer) + + providers, err := providersClient.List(ctx, nil, "") + if err != nil { + return err + } + + for _, provider := range providers { + if *provider.Namespace == "Microsoft.Compute" { + for _, resourceType := range *provider.ResourceTypes { + if *resourceType.ResourceType == "encryptionAtHost" { + return nil + } + } + return api.NewCloudError( + http.StatusBadRequest, + api.CloudErrorCodeInvalidParameter, + "", + fmt.Sprintf( + "Microsoft.Compute/EncryptionAtHost"+ + " is not registered for"+ + " subscription %s.", + subscriptionID)) + } + } + return nil +} diff --git a/pkg/frontend/sku_validation.go b/pkg/frontend/sku_validation.go index 47bf319e2b0..4df20f6dbd8 100644 --- a/pkg/frontend/sku_validation.go +++ b/pkg/frontend/sku_validation.go @@ -33,7 +33,15 @@ func (s skuValidator) ValidateVMSku(ctx context.Context, azEnv *azureclient.AROE if err != nil { return err } - + if oc.Properties.MasterProfile.EncryptionAtHost == + api.EncryptionAtHostEnabled { + err = validateEncryptionAtHostFeature( + ctx, azEnv, environment, + subscriptionID, tenantID) + if err != nil { + return err + } + } return validateVMSku(ctx, oc, armResourceSKUsClient) } From 4bbbe6086efd7d84f95f87c85bc8fe3b9033bc76 Mon Sep 17 00:00:00 2001 From: b-lnimmala Date: Wed, 29 Apr 2026 10:37:33 -0400 Subject: [PATCH 02/16] ARO-3837: Add EncryptionAtHost feature flag validation before cluster provisioning --- pkg/frontend/encryptionathost_validation.go | 65 ++++++++++++++------- 1 file changed, 43 insertions(+), 22 deletions(-) diff --git a/pkg/frontend/encryptionathost_validation.go b/pkg/frontend/encryptionathost_validation.go index e8a0b9ac8ea..aaf87df722f 100644 --- a/pkg/frontend/encryptionathost_validation.go +++ b/pkg/frontend/encryptionathost_validation.go @@ -18,8 +18,8 @@ func validateEncryptionAtHostFeature( ctx context.Context, azEnv *azureclient.AROEnvironment, environment env.Interface, - subscriptionID, tenantID string) error { - + subscriptionID, tenantID string, +) error { fpAuthorizer, err := environment.FPAuthorizer( tenantID, nil, environment.Environment().ResourceManagerScope) @@ -27,31 +27,52 @@ func validateEncryptionAtHostFeature( return err } - providersClient := features.NewProvidersClient( + resourcesClient := features.NewResourcesClient( azEnv, subscriptionID, fpAuthorizer) - providers, err := providersClient.List(ctx, nil, "") + resourceID := fmt.Sprintf( + "/subscriptions/%s/providers/Microsoft.Features"+ + "/features/EncryptionAtHost", + subscriptionID) + + feature, err := resourcesClient.GetByID( + ctx, resourceID, "2021-07-01") if err != nil { - return err + return api.NewCloudError( + http.StatusBadRequest, + api.CloudErrorCodeInvalidParameter, + "", + fmt.Sprintf( + "Microsoft.Compute/EncryptionAtHost"+ + " is not registered for"+ + " subscription %s.", + subscriptionID)) } - for _, provider := range providers { - if *provider.Namespace == "Microsoft.Compute" { - for _, resourceType := range *provider.ResourceTypes { - if *resourceType.ResourceType == "encryptionAtHost" { - return nil - } - } - return api.NewCloudError( - http.StatusBadRequest, - api.CloudErrorCodeInvalidParameter, - "", - fmt.Sprintf( - "Microsoft.Compute/EncryptionAtHost"+ - " is not registered for"+ - " subscription %s.", - subscriptionID)) - } + if feature.Properties == nil { + return api.NewCloudError( + http.StatusBadRequest, + api.CloudErrorCodeInvalidParameter, + "", + fmt.Sprintf( + "Microsoft.Compute/EncryptionAtHost"+ + " is not registered for"+ + " subscription %s.", + subscriptionID)) } + + properties, ok := feature.Properties.(map[string]interface{}) + if !ok || properties["state"] != "Registered" { + return api.NewCloudError( + http.StatusBadRequest, + api.CloudErrorCodeInvalidParameter, + "", + fmt.Sprintf( + "Microsoft.Compute/EncryptionAtHost"+ + " is not registered for"+ + " subscription %s.", + subscriptionID)) + } + return nil } From 5ea89d6023c528bbd3bca1e4ec099e286d70e96e Mon Sep 17 00:00:00 2001 From: b-lnimmala Date: Wed, 29 Apr 2026 19:22:49 -0400 Subject: [PATCH 03/16] ARO-3837: add EncryptionAtHost feature flag validation --- pkg/frontend/encryptionathost_validation.go | 80 ++++++++++----------- pkg/frontend/sku_validation.go | 9 ++- 2 files changed, 41 insertions(+), 48 deletions(-) diff --git a/pkg/frontend/encryptionathost_validation.go b/pkg/frontend/encryptionathost_validation.go index aaf87df722f..4cbcc0048cb 100644 --- a/pkg/frontend/encryptionathost_validation.go +++ b/pkg/frontend/encryptionathost_validation.go @@ -8,6 +8,8 @@ import ( "fmt" "net/http" + "github.com/Azure/go-autorest/autorest" + "github.com/Azure/ARO-RP/pkg/api" "github.com/Azure/ARO-RP/pkg/env" "github.com/Azure/ARO-RP/pkg/util/azureclient" @@ -18,8 +20,8 @@ func validateEncryptionAtHostFeature( ctx context.Context, azEnv *azureclient.AROEnvironment, environment env.Interface, - subscriptionID, tenantID string, -) error { + subscriptionID, tenantID string) error { + fpAuthorizer, err := environment.FPAuthorizer( tenantID, nil, environment.Environment().ResourceManagerScope) @@ -27,52 +29,44 @@ func validateEncryptionAtHostFeature( return err } - resourcesClient := features.NewResourcesClient( + providersClient := features.NewProvidersClient( azEnv, subscriptionID, fpAuthorizer) - resourceID := fmt.Sprintf( - "/subscriptions/%s/providers/Microsoft.Features"+ - "/features/EncryptionAtHost", - subscriptionID) - - feature, err := resourcesClient.GetByID( - ctx, resourceID, "2021-07-01") + top := int32(0) + providers, err := providersClient.List(ctx, &top, "") if err != nil { - return api.NewCloudError( - http.StatusBadRequest, - api.CloudErrorCodeInvalidParameter, - "", - fmt.Sprintf( - "Microsoft.Compute/EncryptionAtHost"+ - " is not registered for"+ - " subscription %s.", - subscriptionID)) - } - - if feature.Properties == nil { - return api.NewCloudError( - http.StatusBadRequest, - api.CloudErrorCodeInvalidParameter, - "", - fmt.Sprintf( - "Microsoft.Compute/EncryptionAtHost"+ - " is not registered for"+ - " subscription %s.", - subscriptionID)) + if detailed, ok := err.(autorest.DetailedError); ok { + if detailed.StatusCode == http.StatusNotFound { + return api.NewCloudError( + http.StatusBadRequest, + api.CloudErrorCodeInvalidParameter, + "encryptionAtHost", + fmt.Sprintf( + "Microsoft.Compute/EncryptionAtHost"+ + " is not registered for"+ + " subscription %s.", + subscriptionID)) + } + } + return err } - properties, ok := feature.Properties.(map[string]interface{}) - if !ok || properties["state"] != "Registered" { - return api.NewCloudError( - http.StatusBadRequest, - api.CloudErrorCodeInvalidParameter, - "", - fmt.Sprintf( - "Microsoft.Compute/EncryptionAtHost"+ - " is not registered for"+ - " subscription %s.", - subscriptionID)) + for _, provider := range providers { + if provider.Namespace != nil && + *provider.Namespace == "Microsoft.Compute" && + provider.RegistrationState != nil && + *provider.RegistrationState == "Registered" { + return nil + } } - return nil + return api.NewCloudError( + http.StatusBadRequest, + api.CloudErrorCodeInvalidParameter, + "encryptionAtHost", + fmt.Sprintf( + "Microsoft.Compute/EncryptionAtHost"+ + " is not registered for"+ + " subscription %s.", + subscriptionID)) } diff --git a/pkg/frontend/sku_validation.go b/pkg/frontend/sku_validation.go index 4df20f6dbd8..6955d46b340 100644 --- a/pkg/frontend/sku_validation.go +++ b/pkg/frontend/sku_validation.go @@ -33,15 +33,14 @@ func (s skuValidator) ValidateVMSku(ctx context.Context, azEnv *azureclient.AROE if err != nil { return err } - if oc.Properties.MasterProfile.EncryptionAtHost == - api.EncryptionAtHostEnabled { - err = validateEncryptionAtHostFeature( - ctx, azEnv, environment, - subscriptionID, tenantID) + + if oc.Properties.MasterProfile.EncryptionAtHost == api.EncryptionAtHostEnabled { + err = validateEncryptionAtHostFeature(ctx, azEnv, environment, subscriptionID, tenantID) if err != nil { return err } } + return validateVMSku(ctx, oc, armResourceSKUsClient) } From 593b668043451ca1a7323bb0cfb9ed1a9fc0354b Mon Sep 17 00:00:00 2001 From: b-lnimmala Date: Wed, 29 Apr 2026 19:39:00 -0400 Subject: [PATCH 04/16] ARO-3837: fix imports and formatting --- pkg/frontend/encryptionathost_validation.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/frontend/encryptionathost_validation.go b/pkg/frontend/encryptionathost_validation.go index 4cbcc0048cb..1e56e98731a 100644 --- a/pkg/frontend/encryptionathost_validation.go +++ b/pkg/frontend/encryptionathost_validation.go @@ -20,8 +20,8 @@ func validateEncryptionAtHostFeature( ctx context.Context, azEnv *azureclient.AROEnvironment, environment env.Interface, - subscriptionID, tenantID string) error { - + subscriptionID, tenantID string, +) error { fpAuthorizer, err := environment.FPAuthorizer( tenantID, nil, environment.Environment().ResourceManagerScope) From b938cfd269e47bfa1c554eee8054b9d7dc6553bf Mon Sep 17 00:00:00 2001 From: Lakshmi Sravya Nimmala Date: Thu, 30 Apr 2026 20:30:51 -0400 Subject: [PATCH 05/16] Update pkg/frontend/encryptionathost_validation.go Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- pkg/frontend/encryptionathost_validation.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pkg/frontend/encryptionathost_validation.go b/pkg/frontend/encryptionathost_validation.go index 1e56e98731a..3a49c24fcea 100644 --- a/pkg/frontend/encryptionathost_validation.go +++ b/pkg/frontend/encryptionathost_validation.go @@ -32,8 +32,7 @@ func validateEncryptionAtHostFeature( providersClient := features.NewProvidersClient( azEnv, subscriptionID, fpAuthorizer) - top := int32(0) - providers, err := providersClient.List(ctx, &top, "") + providers, err := providersClient.List(ctx, nil, "") if err != nil { if detailed, ok := err.(autorest.DetailedError); ok { if detailed.StatusCode == http.StatusNotFound { From 8c9918a989415ce7d949ddeef1b954e85cd07a88 Mon Sep 17 00:00:00 2001 From: b-lnimmala Date: Thu, 30 Apr 2026 21:16:05 -0400 Subject: [PATCH 06/16] ARO-3837: validate EncryptionAtHost feature registration before cluster provisioning --- pkg/frontend/encryptionathost_validation.go | 82 ++++++++++++--------- 1 file changed, 49 insertions(+), 33 deletions(-) diff --git a/pkg/frontend/encryptionathost_validation.go b/pkg/frontend/encryptionathost_validation.go index 3a49c24fcea..6d6634c8ce8 100644 --- a/pkg/frontend/encryptionathost_validation.go +++ b/pkg/frontend/encryptionathost_validation.go @@ -9,13 +9,21 @@ import ( "net/http" "github.com/Azure/go-autorest/autorest" + "github.com/Azure/go-autorest/autorest/azure" "github.com/Azure/ARO-RP/pkg/api" "github.com/Azure/ARO-RP/pkg/env" "github.com/Azure/ARO-RP/pkg/util/azureclient" - "github.com/Azure/ARO-RP/pkg/util/azureclient/mgmt/features" ) +type featureResult struct { + Properties *featureResultProperties `json:"properties"` +} + +type featureResultProperties struct { + State string `json:"state"` +} + func validateEncryptionAtHostFeature( ctx context.Context, azEnv *azureclient.AROEnvironment, @@ -29,43 +37,51 @@ func validateEncryptionAtHostFeature( return err } - providersClient := features.NewProvidersClient( - azEnv, subscriptionID, fpAuthorizer) + client := autorest.NewClientWithUserAgent("") + client.Authorizer = fpAuthorizer + + url := fmt.Sprintf( + "%ssubscriptions/%s/providers/Microsoft.Features/providers/Microsoft.Compute/features/EncryptionAtHost", + azEnv.ResourceManagerEndpoint, + subscriptionID) + + req, err := autorest.CreatePreparer( + autorest.AsGet(), + autorest.WithBaseURL(url), + autorest.WithQueryParameters(map[string]interface{}{ + "api-version": "2021-07-01", + }), + ).Prepare((&http.Request{}).WithContext(ctx)) + if err != nil { + return err + } + + resp, err := autorest.SendWithSender(client, req) + if err != nil { + return err + } - providers, err := providersClient.List(ctx, nil, "") + var result featureResult + err = autorest.Respond(resp, + azure.WithErrorUnlessStatusCode(http.StatusOK), + autorest.ByUnmarshallingJSON(&result), + autorest.ByClosing()) if err != nil { - if detailed, ok := err.(autorest.DetailedError); ok { - if detailed.StatusCode == http.StatusNotFound { - return api.NewCloudError( - http.StatusBadRequest, - api.CloudErrorCodeInvalidParameter, - "encryptionAtHost", - fmt.Sprintf( - "Microsoft.Compute/EncryptionAtHost"+ - " is not registered for"+ - " subscription %s.", - subscriptionID)) - } - } return err } - for _, provider := range providers { - if provider.Namespace != nil && - *provider.Namespace == "Microsoft.Compute" && - provider.RegistrationState != nil && - *provider.RegistrationState == "Registered" { - return nil - } + if result.Properties == nil || + result.Properties.State != "Registered" { + return api.NewCloudError( + http.StatusBadRequest, + api.CloudErrorCodeInvalidParameter, + "encryptionAtHost", + fmt.Sprintf( + "Microsoft.Compute/EncryptionAtHost"+ + " is not registered for"+ + " subscription %s.", + subscriptionID)) } - return api.NewCloudError( - http.StatusBadRequest, - api.CloudErrorCodeInvalidParameter, - "encryptionAtHost", - fmt.Sprintf( - "Microsoft.Compute/EncryptionAtHost"+ - " is not registered for"+ - " subscription %s.", - subscriptionID)) + return nil } From 2ce156a10030309e8cf78cc62d5274dea8977b6d Mon Sep 17 00:00:00 2001 From: b-lnimmala Date: Wed, 6 May 2026 11:12:06 -0400 Subject: [PATCH 07/16] ARO-3837: Add EncryptionAtHost feature validation POC --- hack/encryptionathost-poc/main.go | 109 ++++++++++++++++++++++++++++++ 1 file changed, 109 insertions(+) create mode 100644 hack/encryptionathost-poc/main.go diff --git a/hack/encryptionathost-poc/main.go b/hack/encryptionathost-poc/main.go new file mode 100644 index 00000000000..44365b992e9 --- /dev/null +++ b/hack/encryptionathost-poc/main.go @@ -0,0 +1,109 @@ +package main + +// Copyright (c) Microsoft Corporation. +// Licensed under the Apache License 2.0. + +import ( + "context" + "fmt" + "net/http" + "os" + + "github.com/Azure/go-autorest/autorest" + "github.com/Azure/go-autorest/autorest/adal" + "github.com/Azure/go-autorest/autorest/azure" +) + +type featureResult struct { + Properties *featureResultProperties `json:"properties"` +} + +type featureResultProperties struct { + State string `json:"state"` +} + +func validateEncryptionAtHostFeature(subscriptionID string, authorizer autorest.Authorizer) error { + client := autorest.NewClientWithUserAgent("") + client.Authorizer = authorizer + + url := fmt.Sprintf( + "https://management.azure.com/subscriptions/%s/providers/Microsoft.Features/providers/Microsoft.Compute/features/EncryptionAtHost", + subscriptionID) + + req, err := autorest.CreatePreparer( + autorest.AsGet(), + autorest.WithBaseURL(url), + autorest.WithQueryParameters(map[string]interface{}{ + "api-version": "2021-07-01", + }), + ).Prepare((&http.Request{}).WithContext(context.Background())) + if err != nil { + return fmt.Errorf("failed to prepare request: %v", err) + } + + resp, err := autorest.SendWithSender(client, req) + if err != nil { + return fmt.Errorf("failed to send request: %v", err) + } + + var result featureResult + err = autorest.Respond(resp, + azure.WithErrorUnlessStatusCode(http.StatusOK), + autorest.ByUnmarshallingJSON(&result), + autorest.ByClosing()) + if err != nil { + return fmt.Errorf("failed to read response: %v", err) + } + + if result.Properties == nil || + result.Properties.State != "Registered" { + return fmt.Errorf("Microsoft.Compute/EncryptionAtHost is not registered for subscription %s", subscriptionID) + } + + return nil +} + +func main() { + tenantID := os.Getenv("TENANT_ID") + clientID := os.Getenv("CLIENT_ID") + clientSecret := os.Getenv("CLIENT_SECRET") + registeredSubscriptionID := os.Getenv("SUBSCRIPTION_ID") + notRegisteredSubscriptionID := os.Getenv("SUBSCRIPTION_ID_NOT_REGISTERED") + + oauthConfig, err := adal.NewOAuthConfig( + azure.PublicCloud.ActiveDirectoryEndpoint, tenantID) + if err != nil { + panic(err) + } + + token, err := adal.NewServicePrincipalToken( + *oauthConfig, + clientID, + clientSecret, + azure.PublicCloud.ResourceManagerEndpoint) + if err != nil { + panic(err) + } + + authorizer := autorest.NewBearerAuthorizer(token) + + // SUCCESS SCENARIO - feature IS registered + fmt.Println("=== SCENARIO 1: Feature IS registered ===") + err = validateEncryptionAtHostFeature(registeredSubscriptionID, authorizer) + if err != nil { + fmt.Printf("FAIL - %v\n", err) + } else { + fmt.Println("SUCCESS - Feature is registered, cluster provisioning would proceed") + } + + fmt.Println("") + + // FAILURE SCENARIO - feature is NOT registered + fmt.Println("=== SCENARIO 2: Feature is NOT registered ===") + err = validateEncryptionAtHostFeature(notRegisteredSubscriptionID, authorizer) + if err != nil { + fmt.Printf("SUCCESS - Correctly caught unregistered feature: %v\n", err) + } else { + fmt.Println("FAIL - Should have returned error but did not") + } +} From f127f355cf0066cc848218371eb0fcf16715fc56 Mon Sep 17 00:00:00 2001 From: b-lnimmala Date: Wed, 6 May 2026 12:21:03 -0400 Subject: [PATCH 08/16] ARO-3837: upadte the poc to use track-2 sdk --- hack/encryptionathost-poc/main.go | 85 ++++++++++--------------------- 1 file changed, 27 insertions(+), 58 deletions(-) diff --git a/hack/encryptionathost-poc/main.go b/hack/encryptionathost-poc/main.go index 44365b992e9..c11020150bf 100644 --- a/hack/encryptionathost-poc/main.go +++ b/hack/encryptionathost-poc/main.go @@ -6,58 +6,26 @@ package main import ( "context" "fmt" - "net/http" "os" - "github.com/Azure/go-autorest/autorest" - "github.com/Azure/go-autorest/autorest/adal" - "github.com/Azure/go-autorest/autorest/azure" + "github.com/Azure/azure-sdk-for-go/sdk/azidentity" + "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armfeatures" ) -type featureResult struct { - Properties *featureResultProperties `json:"properties"` -} - -type featureResultProperties struct { - State string `json:"state"` -} - -func validateEncryptionAtHostFeature(subscriptionID string, authorizer autorest.Authorizer) error { - client := autorest.NewClientWithUserAgent("") - client.Authorizer = authorizer - - url := fmt.Sprintf( - "https://management.azure.com/subscriptions/%s/providers/Microsoft.Features/providers/Microsoft.Compute/features/EncryptionAtHost", - subscriptionID) - - req, err := autorest.CreatePreparer( - autorest.AsGet(), - autorest.WithBaseURL(url), - autorest.WithQueryParameters(map[string]interface{}{ - "api-version": "2021-07-01", - }), - ).Prepare((&http.Request{}).WithContext(context.Background())) +func validateEncryptionAtHostFeature(ctx context.Context, subscriptionID string, featuresClient *armfeatures.Client) error { + // Get the feature registration status + resp, err := featuresClient.Get(ctx, "Microsoft.Compute", "EncryptionAtHost", nil) if err != nil { - return fmt.Errorf("failed to prepare request: %v", err) + return fmt.Errorf("failed to get feature: %w", err) } - resp, err := autorest.SendWithSender(client, req) - if err != nil { - return fmt.Errorf("failed to send request: %v", err) - } - - var result featureResult - err = autorest.Respond(resp, - azure.WithErrorUnlessStatusCode(http.StatusOK), - autorest.ByUnmarshallingJSON(&result), - autorest.ByClosing()) - if err != nil { - return fmt.Errorf("failed to read response: %v", err) + if resp.Properties == nil || resp.Properties.State == nil { + return fmt.Errorf("Microsoft.Compute/EncryptionAtHost feature has no state for subscription %s", subscriptionID) } - if result.Properties == nil || - result.Properties.State != "Registered" { - return fmt.Errorf("Microsoft.Compute/EncryptionAtHost is not registered for subscription %s", subscriptionID) + if *resp.Properties.State != "Registered" { + return fmt.Errorf("Microsoft.Compute/EncryptionAtHost is not registered for subscription %s (current state: %s)", + subscriptionID, *resp.Properties.State) } return nil @@ -70,26 +38,22 @@ func main() { registeredSubscriptionID := os.Getenv("SUBSCRIPTION_ID") notRegisteredSubscriptionID := os.Getenv("SUBSCRIPTION_ID_NOT_REGISTERED") - oauthConfig, err := adal.NewOAuthConfig( - azure.PublicCloud.ActiveDirectoryEndpoint, tenantID) + // Create credential using track-2 SDK + cred, err := azidentity.NewClientSecretCredential(tenantID, clientID, clientSecret, nil) if err != nil { - panic(err) + panic(fmt.Sprintf("failed to create credential: %v", err)) } - token, err := adal.NewServicePrincipalToken( - *oauthConfig, - clientID, - clientSecret, - azure.PublicCloud.ResourceManagerEndpoint) - if err != nil { - panic(err) - } - - authorizer := autorest.NewBearerAuthorizer(token) + ctx := context.Background() // SUCCESS SCENARIO - feature IS registered fmt.Println("=== SCENARIO 1: Feature IS registered ===") - err = validateEncryptionAtHostFeature(registeredSubscriptionID, authorizer) + featuresClient1, err := armfeatures.NewClient(registeredSubscriptionID, cred, nil) + if err != nil { + panic(fmt.Sprintf("failed to create features client: %v", err)) + } + + err = validateEncryptionAtHostFeature(ctx, registeredSubscriptionID, featuresClient1) if err != nil { fmt.Printf("FAIL - %v\n", err) } else { @@ -100,7 +64,12 @@ func main() { // FAILURE SCENARIO - feature is NOT registered fmt.Println("=== SCENARIO 2: Feature is NOT registered ===") - err = validateEncryptionAtHostFeature(notRegisteredSubscriptionID, authorizer) + featuresClient2, err := armfeatures.NewClient(notRegisteredSubscriptionID, cred, nil) + if err != nil { + panic(fmt.Sprintf("failed to create features client: %v", err)) + } + + err = validateEncryptionAtHostFeature(ctx, notRegisteredSubscriptionID, featuresClient2) if err != nil { fmt.Printf("SUCCESS - Correctly caught unregistered feature: %v\n", err) } else { From b9f349c6f4c3a03d0a76574fe8fd59e5b9ceceb5 Mon Sep 17 00:00:00 2001 From: b-lnimmala Date: Wed, 13 May 2026 14:49:02 -0400 Subject: [PATCH 09/16] update encryptionathost validation to track -2 SDK --- go.mod | 1 + go.sum | 2 + pkg/frontend/encryptionathost_validation.go | 58 ++++++--------------- 3 files changed, 20 insertions(+), 41 deletions(-) diff --git a/go.mod b/go.mod index 4c0830bd870..c0e5712285f 100644 --- a/go.mod +++ b/go.mod @@ -18,6 +18,7 @@ require ( github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/monitor/armmonitor v0.11.0 github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/msi/armmsi v1.3.0 github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork/v6 v6.2.0 + github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armfeatures v1.2.0 github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/storage/armstorage v1.8.1 github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azcertificates v1.4.0 github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azsecrets v1.4.0 diff --git a/go.sum b/go.sum index 25f2d450078..9aa9beb397d 100644 --- a/go.sum +++ b/go.sum @@ -38,6 +38,8 @@ github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/msi/armmsi v1.3.0 h1:L7G3d github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/msi/armmsi v1.3.0/go.mod h1:Ms6gYEy0+A2knfKrwdatsggTXYA2+ICKug8w7STorFw= github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork/v6 v6.2.0 h1:HYGD75g0bQ3VO/Omedm54v4LrD3B1cGImuRF3AJ5wLo= github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork/v6 v6.2.0/go.mod h1:ulHyBFJOI0ONiRL4vcJTmS7rx18jQQlEPmAgo80cRdM= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armfeatures v1.2.0 h1:wIDqH4WA5uJ6irRqjzodeSw6Pmp0tu3oIbwzBZEdMfQ= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armfeatures v1.2.0/go.mod h1:g8mnARUMaYRsg80mxm3PxjF7+oUotB/lneDbwYbGNxg= github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources v1.2.0 h1:Dd+RhdJn0OTtVGaeDLZpcumkIVCtA/3/Fo42+eoYvVM= github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources v1.2.0/go.mod h1:5kakwfW5CjC9KK+Q4wjXAg+ShuIm2mBMua0ZFj2C8PE= github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/storage/armstorage v1.8.1 h1:/Zt+cDPnpC3OVDm/JKLOs7M2DKmLRIIp3XIx9pHHiig= diff --git a/pkg/frontend/encryptionathost_validation.go b/pkg/frontend/encryptionathost_validation.go index 6d6634c8ce8..9f9995d75be 100644 --- a/pkg/frontend/encryptionathost_validation.go +++ b/pkg/frontend/encryptionathost_validation.go @@ -8,80 +8,56 @@ import ( "fmt" "net/http" - "github.com/Azure/go-autorest/autorest" - "github.com/Azure/go-autorest/autorest/azure" + "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armfeatures" "github.com/Azure/ARO-RP/pkg/api" "github.com/Azure/ARO-RP/pkg/env" "github.com/Azure/ARO-RP/pkg/util/azureclient" ) -type featureResult struct { - Properties *featureResultProperties `json:"properties"` -} - -type featureResultProperties struct { - State string `json:"state"` -} - func validateEncryptionAtHostFeature( ctx context.Context, azEnv *azureclient.AROEnvironment, environment env.Interface, subscriptionID, tenantID string, ) error { - fpAuthorizer, err := environment.FPAuthorizer( - tenantID, nil, - environment.Environment().ResourceManagerScope) - if err != nil { - return err - } - - client := autorest.NewClientWithUserAgent("") - client.Authorizer = fpAuthorizer - - url := fmt.Sprintf( - "%ssubscriptions/%s/providers/Microsoft.Features/providers/Microsoft.Compute/features/EncryptionAtHost", - azEnv.ResourceManagerEndpoint, - subscriptionID) - - req, err := autorest.CreatePreparer( - autorest.AsGet(), - autorest.WithBaseURL(url), - autorest.WithQueryParameters(map[string]interface{}{ - "api-version": "2021-07-01", - }), - ).Prepare((&http.Request{}).WithContext(ctx)) + fpCred, err := environment.FPNewClientCertificateCredential(tenantID, nil) if err != nil { return err } - resp, err := autorest.SendWithSender(client, req) + featuresClient, err := armfeatures.NewClient(subscriptionID, fpCred, azEnv.ArmClientOptions()) if err != nil { return err } - var result featureResult - err = autorest.Respond(resp, - azure.WithErrorUnlessStatusCode(http.StatusOK), - autorest.ByUnmarshallingJSON(&result), - autorest.ByClosing()) + resp, err := featuresClient.Get(ctx, "Microsoft.Compute", "EncryptionAtHost", nil) if err != nil { return err } - if result.Properties == nil || - result.Properties.State != "Registered" { + if resp.Properties == nil || resp.Properties.State == nil { return api.NewCloudError( http.StatusBadRequest, api.CloudErrorCodeInvalidParameter, "encryptionAtHost", fmt.Sprintf( "Microsoft.Compute/EncryptionAtHost"+ - " is not registered for"+ + " feature has no state for"+ " subscription %s.", subscriptionID)) } + if *resp.Properties.State != "Registered" { + return api.NewCloudError( + http.StatusBadRequest, + api.CloudErrorCodeInvalidParameter, + "encryptionAtHost", + fmt.Sprintf( + "Microsoft.Compute/EncryptionAtHost feature is not registered on subscription %s. "+ + "Please register the feature on your subscription before creating the cluster.", + subscriptionID)) + } + return nil } From 4ac9213b7fa774569008b89465b69327f619608c Mon Sep 17 00:00:00 2001 From: b-lnimmala Date: Tue, 19 May 2026 09:24:34 -0400 Subject: [PATCH 10/16] remove POC file --- hack/encryptionathost-poc/main.go | 78 ------------------------------- 1 file changed, 78 deletions(-) delete mode 100644 hack/encryptionathost-poc/main.go diff --git a/hack/encryptionathost-poc/main.go b/hack/encryptionathost-poc/main.go deleted file mode 100644 index c11020150bf..00000000000 --- a/hack/encryptionathost-poc/main.go +++ /dev/null @@ -1,78 +0,0 @@ -package main - -// Copyright (c) Microsoft Corporation. -// Licensed under the Apache License 2.0. - -import ( - "context" - "fmt" - "os" - - "github.com/Azure/azure-sdk-for-go/sdk/azidentity" - "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armfeatures" -) - -func validateEncryptionAtHostFeature(ctx context.Context, subscriptionID string, featuresClient *armfeatures.Client) error { - // Get the feature registration status - resp, err := featuresClient.Get(ctx, "Microsoft.Compute", "EncryptionAtHost", nil) - if err != nil { - return fmt.Errorf("failed to get feature: %w", err) - } - - if resp.Properties == nil || resp.Properties.State == nil { - return fmt.Errorf("Microsoft.Compute/EncryptionAtHost feature has no state for subscription %s", subscriptionID) - } - - if *resp.Properties.State != "Registered" { - return fmt.Errorf("Microsoft.Compute/EncryptionAtHost is not registered for subscription %s (current state: %s)", - subscriptionID, *resp.Properties.State) - } - - return nil -} - -func main() { - tenantID := os.Getenv("TENANT_ID") - clientID := os.Getenv("CLIENT_ID") - clientSecret := os.Getenv("CLIENT_SECRET") - registeredSubscriptionID := os.Getenv("SUBSCRIPTION_ID") - notRegisteredSubscriptionID := os.Getenv("SUBSCRIPTION_ID_NOT_REGISTERED") - - // Create credential using track-2 SDK - cred, err := azidentity.NewClientSecretCredential(tenantID, clientID, clientSecret, nil) - if err != nil { - panic(fmt.Sprintf("failed to create credential: %v", err)) - } - - ctx := context.Background() - - // SUCCESS SCENARIO - feature IS registered - fmt.Println("=== SCENARIO 1: Feature IS registered ===") - featuresClient1, err := armfeatures.NewClient(registeredSubscriptionID, cred, nil) - if err != nil { - panic(fmt.Sprintf("failed to create features client: %v", err)) - } - - err = validateEncryptionAtHostFeature(ctx, registeredSubscriptionID, featuresClient1) - if err != nil { - fmt.Printf("FAIL - %v\n", err) - } else { - fmt.Println("SUCCESS - Feature is registered, cluster provisioning would proceed") - } - - fmt.Println("") - - // FAILURE SCENARIO - feature is NOT registered - fmt.Println("=== SCENARIO 2: Feature is NOT registered ===") - featuresClient2, err := armfeatures.NewClient(notRegisteredSubscriptionID, cred, nil) - if err != nil { - panic(fmt.Sprintf("failed to create features client: %v", err)) - } - - err = validateEncryptionAtHostFeature(ctx, notRegisteredSubscriptionID, featuresClient2) - if err != nil { - fmt.Printf("SUCCESS - Correctly caught unregistered feature: %v\n", err) - } else { - fmt.Println("FAIL - Should have returned error but did not") - } -} From 5079948145415693fc56e7f0f1ebf9b2f3a91866 Mon Sep 17 00:00:00 2001 From: b-lnimmala Date: Tue, 19 May 2026 11:55:13 -0400 Subject: [PATCH 11/16] PR Review fixes --- pkg/frontend/encryptionathost_validation.go | 55 ++++++--- .../encryptionathost_validation_test.go | 104 ++++++++++++++++++ pkg/frontend/frontend.go | 2 + pkg/frontend/generate.go | 1 + pkg/frontend/openshiftcluster_putorpatch.go | 5 + pkg/frontend/sku_validation.go | 7 -- .../frontend/encryptionathost_validation.go | 100 +++++++++++++++++ 7 files changed, 251 insertions(+), 23 deletions(-) create mode 100644 pkg/frontend/encryptionathost_validation_test.go create mode 100644 pkg/util/mocks/frontend/encryptionathost_validation.go diff --git a/pkg/frontend/encryptionathost_validation.go b/pkg/frontend/encryptionathost_validation.go index 9f9995d75be..d3936bf4a35 100644 --- a/pkg/frontend/encryptionathost_validation.go +++ b/pkg/frontend/encryptionathost_validation.go @@ -15,22 +15,45 @@ import ( "github.com/Azure/ARO-RP/pkg/util/azureclient" ) -func validateEncryptionAtHostFeature( - ctx context.Context, - azEnv *azureclient.AROEnvironment, - environment env.Interface, - subscriptionID, tenantID string, -) error { - fpCred, err := environment.FPNewClientCertificateCredential(tenantID, nil) - if err != nil { - return err +type FeaturesClient interface { + Get(ctx context.Context, resourceProviderNamespace string, featureName string, options *armfeatures.ClientGetOptions) (armfeatures.ClientGetResponse, error) +} + +type FeaturesValidator interface { + ValidateSubscriptionFeatures(ctx context.Context, azEnv *azureclient.AROEnvironment, environment env.Interface, subscriptionID, tenantID string, oc *api.OpenShiftCluster) error +} + +type featuresValidator struct{} + +func (f featuresValidator) ValidateSubscriptionFeatures(ctx context.Context, azEnv *azureclient.AROEnvironment, environment env.Interface, subscriptionID, tenantID string, oc *api.OpenShiftCluster) error { + var fieldPath string + if oc.Properties.MasterProfile.EncryptionAtHost == api.EncryptionAtHostEnabled { + fieldPath = "properties.masterProfile.encryptionAtHost" + } else if oc.Properties.WorkerProfiles[0].EncryptionAtHost == api.EncryptionAtHostEnabled { + fieldPath = "properties.workerProfiles[0].encryptionAtHost" } - featuresClient, err := armfeatures.NewClient(subscriptionID, fpCred, azEnv.ArmClientOptions()) - if err != nil { - return err + if fieldPath != "" { + fpCred, err := environment.FPNewClientCertificateCredential(tenantID, nil) + if err != nil { + return err + } + + featuresClient, err := armfeatures.NewClient(subscriptionID, fpCred, azEnv.ArmClientOptions()) + if err != nil { + return err + } + + return validateEncryptionAtHostFeature(ctx, featuresClient, subscriptionID, fieldPath) } + return nil +} +func validateEncryptionAtHostFeature( + ctx context.Context, + featuresClient FeaturesClient, + subscriptionID, fieldPath string, +) error { resp, err := featuresClient.Get(ctx, "Microsoft.Compute", "EncryptionAtHost", nil) if err != nil { return err @@ -38,9 +61,9 @@ func validateEncryptionAtHostFeature( if resp.Properties == nil || resp.Properties.State == nil { return api.NewCloudError( - http.StatusBadRequest, - api.CloudErrorCodeInvalidParameter, - "encryptionAtHost", + http.StatusInternalServerError, + api.CloudErrorCodeInternalServerError, + "", fmt.Sprintf( "Microsoft.Compute/EncryptionAtHost"+ " feature has no state for"+ @@ -52,7 +75,7 @@ func validateEncryptionAtHostFeature( return api.NewCloudError( http.StatusBadRequest, api.CloudErrorCodeInvalidParameter, - "encryptionAtHost", + fieldPath, fmt.Sprintf( "Microsoft.Compute/EncryptionAtHost feature is not registered on subscription %s. "+ "Please register the feature on your subscription before creating the cluster.", diff --git a/pkg/frontend/encryptionathost_validation_test.go b/pkg/frontend/encryptionathost_validation_test.go new file mode 100644 index 00000000000..35d322fbe90 --- /dev/null +++ b/pkg/frontend/encryptionathost_validation_test.go @@ -0,0 +1,104 @@ +package frontend + +// Copyright (c) Microsoft Corporation. +// Licensed under the Apache License 2.0. + +import ( + "context" + "errors" + "testing" + + "go.uber.org/mock/gomock" + + "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armfeatures" + + mock_frontend "github.com/Azure/ARO-RP/pkg/util/mocks/frontend" + "github.com/Azure/ARO-RP/pkg/util/pointerutils" +) + +func TestValidateEncryptionAtHostFeature(t *testing.T) { + ctx := context.Background() + + for _, tt := range []struct { + name string + fieldPath string + mockFeatureState *string + mockPropertiesNil bool + mockStateNil bool + mockGetErr error + wantErr string + }{ + { + name: "feature is registered - should return nil", + fieldPath: "properties.masterProfile.encryptionAtHost", + mockFeatureState: pointerutils.ToPtr("Registered"), + wantErr: "", + }, + { + name: "feature is not registered - should return 400 BadRequest", + fieldPath: "properties.masterProfile.encryptionAtHost", + mockFeatureState: pointerutils.ToPtr("NotRegistered"), + wantErr: "400: InvalidParameter: properties.masterProfile.encryptionAtHost: Microsoft.Compute/EncryptionAtHost feature is not registered on subscription test-subscription. Please register the feature on your subscription before creating the cluster.", + }, + { + name: "resp.Properties is nil - should return internal server error", + fieldPath: "properties.workerProfiles[0].encryptionAtHost", + mockPropertiesNil: true, + wantErr: "500: InternalServerError: : Microsoft.Compute/EncryptionAtHost feature has no state for subscription test-subscription.", + }, + { + name: "resp.Properties.State is nil - should return internal server error", + fieldPath: "properties.masterProfile.encryptionAtHost", + mockStateNil: true, + wantErr: "500: InternalServerError: : Microsoft.Compute/EncryptionAtHost feature has no state for subscription test-subscription.", + }, + { + name: "Get returns error", + fieldPath: "properties.masterProfile.encryptionAtHost", + mockGetErr: errors.New("random error"), + wantErr: "random error", + }, + } { + t.Run(tt.name, func(t *testing.T) { + controller := gomock.NewController(t) + defer controller.Finish() + + featuresClient := mock_frontend.NewMockFeaturesClient(controller) + + var mockResponse armfeatures.ClientGetResponse + if tt.mockPropertiesNil { + mockResponse = armfeatures.ClientGetResponse{ + FeatureResult: armfeatures.FeatureResult{ + Properties: nil, + }, + } + } else if tt.mockStateNil { + mockResponse = armfeatures.ClientGetResponse{ + FeatureResult: armfeatures.FeatureResult{ + Properties: &armfeatures.FeatureProperties{ + State: nil, + }, + }, + } + } else if tt.mockFeatureState != nil { + mockResponse = armfeatures.ClientGetResponse{ + FeatureResult: armfeatures.FeatureResult{ + Properties: &armfeatures.FeatureProperties{ + State: tt.mockFeatureState, + }, + }, + } + } + + featuresClient.EXPECT(). + Get(ctx, "Microsoft.Compute", "EncryptionAtHost", nil). + Return(mockResponse, tt.mockGetErr) + + err := validateEncryptionAtHostFeature(ctx, featuresClient, "test-subscription", tt.fieldPath) + if err != nil && err.Error() != tt.wantErr || + err == nil && tt.wantErr != "" { + t.Errorf("expected error %q, got %q", tt.wantErr, err) + } + }) + } +} diff --git a/pkg/frontend/frontend.go b/pkg/frontend/frontend.go index 254be2197a5..a16566d23e2 100644 --- a/pkg/frontend/frontend.go +++ b/pkg/frontend/frontend.go @@ -104,6 +104,7 @@ type frontend struct { skuValidator SkuValidator quotaValidator QuotaValidator providersValidator ProvidersValidator + featuresValidator FeaturesValidator clusterEnricher clusterdata.BestEffortEnricher @@ -188,6 +189,7 @@ func NewFrontend(ctx context.Context, quotaValidator: quotaValidator{}, skuValidator: skuValidator{}, providersValidator: providersValidator{}, + featuresValidator: featuresValidator{}, clusterEnricher: enricher, diff --git a/pkg/frontend/generate.go b/pkg/frontend/generate.go index 8f3de552f88..47a47598253 100644 --- a/pkg/frontend/generate.go +++ b/pkg/frontend/generate.go @@ -7,4 +7,5 @@ package frontend //go:generate mockgen -source quota_validation.go -destination=../util/mocks/$GOPACKAGE/quota_validation.go github.com/Azure/ARO-RP/pkg/frontend QuotaValidator //go:generate mockgen -source providers_validation.go -destination=../util/mocks/$GOPACKAGE/providers_validation.go github.com/Azure/ARO-RP/pkg/frontend ProvidersValidator //go:generate mockgen -source sku_validation.go -destination=../util/mocks/$GOPACKAGE/sku_validation.go github.com/Azure/ARO-RP/pkg/frontend SkuValidator +//go:generate mockgen -source encryptionathost_validation.go -destination=../util/mocks/$GOPACKAGE/encryptionathost_validation.go github.com/Azure/ARO-RP/pkg/frontend FeaturesClient,FeaturesValidator //go:generate mockgen -source adminreplies.go -destination=../util/mocks/$GOPACKAGE/adminreplies.go github.com/Azure/ARO-RP/pkg/frontend StreamResponder diff --git a/pkg/frontend/openshiftcluster_putorpatch.go b/pkg/frontend/openshiftcluster_putorpatch.go index 61e91a7fe17..d0301e52c17 100644 --- a/pkg/frontend/openshiftcluster_putorpatch.go +++ b/pkg/frontend/openshiftcluster_putorpatch.go @@ -455,6 +455,11 @@ func (f *frontend) ValidateNewCluster(ctx context.Context, subscription *api.Sub return err } + err = f.featuresValidator.ValidateSubscriptionFeatures(ctx, f.env.Environment(), f.env, subscription.ID, subscription.Subscription.Properties.TenantID, cluster) + if err != nil { + return err + } + err = f.quotaValidator.ValidateQuota(ctx, f.env.Environment(), f.env, subscription.ID, subscription.Subscription.Properties.TenantID, cluster) if err != nil { return err diff --git a/pkg/frontend/sku_validation.go b/pkg/frontend/sku_validation.go index 6955d46b340..47bf319e2b0 100644 --- a/pkg/frontend/sku_validation.go +++ b/pkg/frontend/sku_validation.go @@ -34,13 +34,6 @@ func (s skuValidator) ValidateVMSku(ctx context.Context, azEnv *azureclient.AROE return err } - if oc.Properties.MasterProfile.EncryptionAtHost == api.EncryptionAtHostEnabled { - err = validateEncryptionAtHostFeature(ctx, azEnv, environment, subscriptionID, tenantID) - if err != nil { - return err - } - } - return validateVMSku(ctx, oc, armResourceSKUsClient) } diff --git a/pkg/util/mocks/frontend/encryptionathost_validation.go b/pkg/util/mocks/frontend/encryptionathost_validation.go new file mode 100644 index 00000000000..ceee1668abc --- /dev/null +++ b/pkg/util/mocks/frontend/encryptionathost_validation.go @@ -0,0 +1,100 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: encryptionathost_validation.go +// +// Generated by this command: +// +// mockgen -source encryptionathost_validation.go -destination=../util/mocks/frontend/encryptionathost_validation.go github.com/Azure/ARO-RP/pkg/frontend FeaturesClient,FeaturesValidator +// + +// Package mock_frontend is a generated GoMock package. +package mock_frontend + +import ( + context "context" + reflect "reflect" + + gomock "go.uber.org/mock/gomock" + + armfeatures "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armfeatures" + + api "github.com/Azure/ARO-RP/pkg/api" + env "github.com/Azure/ARO-RP/pkg/env" + azureclient "github.com/Azure/ARO-RP/pkg/util/azureclient" +) + +// MockFeaturesClient is a mock of FeaturesClient interface. +type MockFeaturesClient struct { + ctrl *gomock.Controller + recorder *MockFeaturesClientMockRecorder + isgomock struct{} +} + +// MockFeaturesClientMockRecorder is the mock recorder for MockFeaturesClient. +type MockFeaturesClientMockRecorder struct { + mock *MockFeaturesClient +} + +// NewMockFeaturesClient creates a new mock instance. +func NewMockFeaturesClient(ctrl *gomock.Controller) *MockFeaturesClient { + mock := &MockFeaturesClient{ctrl: ctrl} + mock.recorder = &MockFeaturesClientMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockFeaturesClient) EXPECT() *MockFeaturesClientMockRecorder { + return m.recorder +} + +// Get mocks base method. +func (m *MockFeaturesClient) Get(ctx context.Context, resourceProviderNamespace, featureName string, options *armfeatures.ClientGetOptions) (armfeatures.ClientGetResponse, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Get", ctx, resourceProviderNamespace, featureName, options) + ret0, _ := ret[0].(armfeatures.ClientGetResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Get indicates an expected call of Get. +func (mr *MockFeaturesClientMockRecorder) Get(ctx, resourceProviderNamespace, featureName, options any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Get", reflect.TypeOf((*MockFeaturesClient)(nil).Get), ctx, resourceProviderNamespace, featureName, options) +} + +// MockFeaturesValidator is a mock of FeaturesValidator interface. +type MockFeaturesValidator struct { + ctrl *gomock.Controller + recorder *MockFeaturesValidatorMockRecorder + isgomock struct{} +} + +// MockFeaturesValidatorMockRecorder is the mock recorder for MockFeaturesValidator. +type MockFeaturesValidatorMockRecorder struct { + mock *MockFeaturesValidator +} + +// NewMockFeaturesValidator creates a new mock instance. +func NewMockFeaturesValidator(ctrl *gomock.Controller) *MockFeaturesValidator { + mock := &MockFeaturesValidator{ctrl: ctrl} + mock.recorder = &MockFeaturesValidatorMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockFeaturesValidator) EXPECT() *MockFeaturesValidatorMockRecorder { + return m.recorder +} + +// ValidateSubscriptionFeatures mocks base method. +func (m *MockFeaturesValidator) ValidateSubscriptionFeatures(ctx context.Context, azEnv *azureclient.AROEnvironment, environment env.Interface, subscriptionID, tenantID string, oc *api.OpenShiftCluster) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ValidateSubscriptionFeatures", ctx, azEnv, environment, subscriptionID, tenantID, oc) + ret0, _ := ret[0].(error) + return ret0 +} + +// ValidateSubscriptionFeatures indicates an expected call of ValidateSubscriptionFeatures. +func (mr *MockFeaturesValidatorMockRecorder) ValidateSubscriptionFeatures(ctx, azEnv, environment, subscriptionID, tenantID, oc any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ValidateSubscriptionFeatures", reflect.TypeOf((*MockFeaturesValidator)(nil).ValidateSubscriptionFeatures), ctx, azEnv, environment, subscriptionID, tenantID, oc) +} From 05a637fb033866dfa5ae4b7b1cef6c927d998770 Mon Sep 17 00:00:00 2001 From: b-lnimmala Date: Tue, 19 May 2026 16:14:23 -0400 Subject: [PATCH 12/16] Move FeatureClient to azureclient wrapper and rename files --- ...t_validation.go => features_validation.go} | 11 +--- ...on_test.go => features_validation_test.go} | 4 +- pkg/frontend/generate.go | 2 +- .../azuresdk/armfeatures/client.go | 47 +++++++++++++++ .../azuresdk/armfeatures/generate.go | 7 +++ .../azuresdk/armfeatures/armfeatures.go | 57 +++++++++++++++++++ ...t_validation.go => features_validation.go} | 48 +--------------- .../mocks/frontend/providers_validation.go | 3 +- pkg/util/mocks/frontend/quota_validation.go | 3 +- pkg/util/mocks/frontend/sku_validation.go | 3 +- 10 files changed, 123 insertions(+), 62 deletions(-) rename pkg/frontend/{encryptionathost_validation.go => features_validation.go} (84%) rename pkg/frontend/{encryptionathost_validation_test.go => features_validation_test.go} (95%) create mode 100644 pkg/util/azureclient/azuresdk/armfeatures/client.go create mode 100644 pkg/util/azureclient/azuresdk/armfeatures/generate.go create mode 100644 pkg/util/mocks/azureclient/azuresdk/armfeatures/armfeatures.go rename pkg/util/mocks/frontend/{encryptionathost_validation.go => features_validation.go} (53%) diff --git a/pkg/frontend/encryptionathost_validation.go b/pkg/frontend/features_validation.go similarity index 84% rename from pkg/frontend/encryptionathost_validation.go rename to pkg/frontend/features_validation.go index d3936bf4a35..a7fe8156215 100644 --- a/pkg/frontend/encryptionathost_validation.go +++ b/pkg/frontend/features_validation.go @@ -8,17 +8,12 @@ import ( "fmt" "net/http" - "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armfeatures" - "github.com/Azure/ARO-RP/pkg/api" "github.com/Azure/ARO-RP/pkg/env" "github.com/Azure/ARO-RP/pkg/util/azureclient" + "github.com/Azure/ARO-RP/pkg/util/azureclient/azuresdk/armfeatures" ) -type FeaturesClient interface { - Get(ctx context.Context, resourceProviderNamespace string, featureName string, options *armfeatures.ClientGetOptions) (armfeatures.ClientGetResponse, error) -} - type FeaturesValidator interface { ValidateSubscriptionFeatures(ctx context.Context, azEnv *azureclient.AROEnvironment, environment env.Interface, subscriptionID, tenantID string, oc *api.OpenShiftCluster) error } @@ -39,7 +34,7 @@ func (f featuresValidator) ValidateSubscriptionFeatures(ctx context.Context, azE return err } - featuresClient, err := armfeatures.NewClient(subscriptionID, fpCred, azEnv.ArmClientOptions()) + featuresClient, err := armfeatures.NewFeaturesClient(subscriptionID, fpCred, azEnv.ArmClientOptions()) if err != nil { return err } @@ -51,7 +46,7 @@ func (f featuresValidator) ValidateSubscriptionFeatures(ctx context.Context, azE func validateEncryptionAtHostFeature( ctx context.Context, - featuresClient FeaturesClient, + featuresClient armfeatures.FeaturesClient, subscriptionID, fieldPath string, ) error { resp, err := featuresClient.Get(ctx, "Microsoft.Compute", "EncryptionAtHost", nil) diff --git a/pkg/frontend/encryptionathost_validation_test.go b/pkg/frontend/features_validation_test.go similarity index 95% rename from pkg/frontend/encryptionathost_validation_test.go rename to pkg/frontend/features_validation_test.go index 35d322fbe90..697f2065a9c 100644 --- a/pkg/frontend/encryptionathost_validation_test.go +++ b/pkg/frontend/features_validation_test.go @@ -12,7 +12,7 @@ import ( "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armfeatures" - mock_frontend "github.com/Azure/ARO-RP/pkg/util/mocks/frontend" + mock_armfeatures "github.com/Azure/ARO-RP/pkg/util/mocks/azureclient/azuresdk/armfeatures" "github.com/Azure/ARO-RP/pkg/util/pointerutils" ) @@ -63,7 +63,7 @@ func TestValidateEncryptionAtHostFeature(t *testing.T) { controller := gomock.NewController(t) defer controller.Finish() - featuresClient := mock_frontend.NewMockFeaturesClient(controller) + featuresClient := mock_armfeatures.NewMockFeaturesClient(controller) var mockResponse armfeatures.ClientGetResponse if tt.mockPropertiesNil { diff --git a/pkg/frontend/generate.go b/pkg/frontend/generate.go index 47a47598253..cee57c5c5d6 100644 --- a/pkg/frontend/generate.go +++ b/pkg/frontend/generate.go @@ -7,5 +7,5 @@ package frontend //go:generate mockgen -source quota_validation.go -destination=../util/mocks/$GOPACKAGE/quota_validation.go github.com/Azure/ARO-RP/pkg/frontend QuotaValidator //go:generate mockgen -source providers_validation.go -destination=../util/mocks/$GOPACKAGE/providers_validation.go github.com/Azure/ARO-RP/pkg/frontend ProvidersValidator //go:generate mockgen -source sku_validation.go -destination=../util/mocks/$GOPACKAGE/sku_validation.go github.com/Azure/ARO-RP/pkg/frontend SkuValidator -//go:generate mockgen -source encryptionathost_validation.go -destination=../util/mocks/$GOPACKAGE/encryptionathost_validation.go github.com/Azure/ARO-RP/pkg/frontend FeaturesClient,FeaturesValidator +//go:generate mockgen -source features_validation.go -destination=../util/mocks/$GOPACKAGE/features_validation.go github.com/Azure/ARO-RP/pkg/frontend FeaturesValidator //go:generate mockgen -source adminreplies.go -destination=../util/mocks/$GOPACKAGE/adminreplies.go github.com/Azure/ARO-RP/pkg/frontend StreamResponder diff --git a/pkg/util/azureclient/azuresdk/armfeatures/client.go b/pkg/util/azureclient/azuresdk/armfeatures/client.go new file mode 100644 index 00000000000..09d61bdb31c --- /dev/null +++ b/pkg/util/azureclient/azuresdk/armfeatures/client.go @@ -0,0 +1,47 @@ +package armfeatures + +// Copyright (c) Microsoft Corporation. +// Licensed under the Apache License 2.0. + +import ( + "context" + + "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/resources/armfeatures" + + "github.com/Azure/ARO-RP/pkg/util/azureclient" +) + +type FeaturesClient interface { + Get(ctx context.Context, resourceProviderNamespace string, featureName string, options *armfeatures.ClientGetOptions) (armfeatures.ClientGetResponse, error) +} + +type featuresClient struct { + *armfeatures.Client +} + +var _ FeaturesClient = &featuresClient{} + +// NewDefaultFeaturesClient creates a new FeaturesClient with default options +func NewDefaultFeaturesClient(environment *azureclient.AROEnvironment, subscriptionID string, credential azcore.TokenCredential) (FeaturesClient, error) { + options := &arm.ClientOptions{ + ClientOptions: azcore.ClientOptions{ + Cloud: environment.Cloud, + }, + } + + return NewFeaturesClient(subscriptionID, credential, options) +} + +// NewFeaturesClient creates a new FeaturesClient +func NewFeaturesClient(subscriptionID string, credential azcore.TokenCredential, options *arm.ClientOptions) (FeaturesClient, error) { + client, err := armfeatures.NewClient(subscriptionID, credential, options) + if err != nil { + return nil, err + } + + return &featuresClient{ + Client: client, + }, nil +} diff --git a/pkg/util/azureclient/azuresdk/armfeatures/generate.go b/pkg/util/azureclient/azuresdk/armfeatures/generate.go new file mode 100644 index 00000000000..7df46930cb7 --- /dev/null +++ b/pkg/util/azureclient/azuresdk/armfeatures/generate.go @@ -0,0 +1,7 @@ +package armfeatures + +// Copyright (c) Microsoft Corporation. +// Licensed under the Apache License 2.0. + +//go:generate rm -rf ../../../../util/mocks/$GOPACKAGE +//go:generate mockgen -destination=../../../../util/mocks/azureclient/azuresdk/$GOPACKAGE/$GOPACKAGE.go github.com/Azure/ARO-RP/pkg/util/azureclient/azuresdk/$GOPACKAGE FeaturesClient diff --git a/pkg/util/mocks/azureclient/azuresdk/armfeatures/armfeatures.go b/pkg/util/mocks/azureclient/azuresdk/armfeatures/armfeatures.go new file mode 100644 index 00000000000..2950385a56c --- /dev/null +++ b/pkg/util/mocks/azureclient/azuresdk/armfeatures/armfeatures.go @@ -0,0 +1,57 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: github.com/Azure/ARO-RP/pkg/util/azureclient/azuresdk/armfeatures (interfaces: FeaturesClient) +// +// Generated by this command: +// +// mockgen -destination=../../../../util/mocks/azureclient/azuresdk/armfeatures/armfeatures.go github.com/Azure/ARO-RP/pkg/util/azureclient/azuresdk/armfeatures FeaturesClient +// + +// Package mock_armfeatures is a generated GoMock package. +package mock_armfeatures + +import ( + context "context" + reflect "reflect" + + armfeatures "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armfeatures" + gomock "go.uber.org/mock/gomock" +) + +// MockFeaturesClient is a mock of FeaturesClient interface. +type MockFeaturesClient struct { + ctrl *gomock.Controller + recorder *MockFeaturesClientMockRecorder + isgomock struct{} +} + +// MockFeaturesClientMockRecorder is the mock recorder for MockFeaturesClient. +type MockFeaturesClientMockRecorder struct { + mock *MockFeaturesClient +} + +// NewMockFeaturesClient creates a new mock instance. +func NewMockFeaturesClient(ctrl *gomock.Controller) *MockFeaturesClient { + mock := &MockFeaturesClient{ctrl: ctrl} + mock.recorder = &MockFeaturesClientMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockFeaturesClient) EXPECT() *MockFeaturesClientMockRecorder { + return m.recorder +} + +// Get mocks base method. +func (m *MockFeaturesClient) Get(ctx context.Context, resourceProviderNamespace, featureName string, options *armfeatures.ClientGetOptions) (armfeatures.ClientGetResponse, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Get", ctx, resourceProviderNamespace, featureName, options) + ret0, _ := ret[0].(armfeatures.ClientGetResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Get indicates an expected call of Get. +func (mr *MockFeaturesClientMockRecorder) Get(ctx, resourceProviderNamespace, featureName, options any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Get", reflect.TypeOf((*MockFeaturesClient)(nil).Get), ctx, resourceProviderNamespace, featureName, options) +} diff --git a/pkg/util/mocks/frontend/encryptionathost_validation.go b/pkg/util/mocks/frontend/features_validation.go similarity index 53% rename from pkg/util/mocks/frontend/encryptionathost_validation.go rename to pkg/util/mocks/frontend/features_validation.go index ceee1668abc..ed4989b4a4a 100644 --- a/pkg/util/mocks/frontend/encryptionathost_validation.go +++ b/pkg/util/mocks/frontend/features_validation.go @@ -1,9 +1,9 @@ // Code generated by MockGen. DO NOT EDIT. -// Source: encryptionathost_validation.go +// Source: features_validation.go // // Generated by this command: // -// mockgen -source encryptionathost_validation.go -destination=../util/mocks/frontend/encryptionathost_validation.go github.com/Azure/ARO-RP/pkg/frontend FeaturesClient,FeaturesValidator +// mockgen -source features_validation.go -destination=../util/mocks/frontend/features_validation.go github.com/Azure/ARO-RP/pkg/frontend FeaturesValidator // // Package mock_frontend is a generated GoMock package. @@ -13,54 +13,12 @@ import ( context "context" reflect "reflect" - gomock "go.uber.org/mock/gomock" - - armfeatures "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armfeatures" - api "github.com/Azure/ARO-RP/pkg/api" env "github.com/Azure/ARO-RP/pkg/env" azureclient "github.com/Azure/ARO-RP/pkg/util/azureclient" + gomock "go.uber.org/mock/gomock" ) -// MockFeaturesClient is a mock of FeaturesClient interface. -type MockFeaturesClient struct { - ctrl *gomock.Controller - recorder *MockFeaturesClientMockRecorder - isgomock struct{} -} - -// MockFeaturesClientMockRecorder is the mock recorder for MockFeaturesClient. -type MockFeaturesClientMockRecorder struct { - mock *MockFeaturesClient -} - -// NewMockFeaturesClient creates a new mock instance. -func NewMockFeaturesClient(ctrl *gomock.Controller) *MockFeaturesClient { - mock := &MockFeaturesClient{ctrl: ctrl} - mock.recorder = &MockFeaturesClientMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockFeaturesClient) EXPECT() *MockFeaturesClientMockRecorder { - return m.recorder -} - -// Get mocks base method. -func (m *MockFeaturesClient) Get(ctx context.Context, resourceProviderNamespace, featureName string, options *armfeatures.ClientGetOptions) (armfeatures.ClientGetResponse, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Get", ctx, resourceProviderNamespace, featureName, options) - ret0, _ := ret[0].(armfeatures.ClientGetResponse) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// Get indicates an expected call of Get. -func (mr *MockFeaturesClientMockRecorder) Get(ctx, resourceProviderNamespace, featureName, options any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Get", reflect.TypeOf((*MockFeaturesClient)(nil).Get), ctx, resourceProviderNamespace, featureName, options) -} - // MockFeaturesValidator is a mock of FeaturesValidator interface. type MockFeaturesValidator struct { ctrl *gomock.Controller diff --git a/pkg/util/mocks/frontend/providers_validation.go b/pkg/util/mocks/frontend/providers_validation.go index 6e928406fcb..1dc9e84eed0 100644 --- a/pkg/util/mocks/frontend/providers_validation.go +++ b/pkg/util/mocks/frontend/providers_validation.go @@ -13,10 +13,9 @@ import ( context "context" reflect "reflect" - gomock "go.uber.org/mock/gomock" - env "github.com/Azure/ARO-RP/pkg/env" azureclient "github.com/Azure/ARO-RP/pkg/util/azureclient" + gomock "go.uber.org/mock/gomock" ) // MockProvidersValidator is a mock of ProvidersValidator interface. diff --git a/pkg/util/mocks/frontend/quota_validation.go b/pkg/util/mocks/frontend/quota_validation.go index 6e199d53b69..71398bd5060 100644 --- a/pkg/util/mocks/frontend/quota_validation.go +++ b/pkg/util/mocks/frontend/quota_validation.go @@ -13,11 +13,10 @@ import ( context "context" reflect "reflect" - gomock "go.uber.org/mock/gomock" - api "github.com/Azure/ARO-RP/pkg/api" env "github.com/Azure/ARO-RP/pkg/env" azureclient "github.com/Azure/ARO-RP/pkg/util/azureclient" + gomock "go.uber.org/mock/gomock" ) // MockQuotaValidator is a mock of QuotaValidator interface. diff --git a/pkg/util/mocks/frontend/sku_validation.go b/pkg/util/mocks/frontend/sku_validation.go index 32dc598fab4..12ee7501788 100644 --- a/pkg/util/mocks/frontend/sku_validation.go +++ b/pkg/util/mocks/frontend/sku_validation.go @@ -13,11 +13,10 @@ import ( context "context" reflect "reflect" - gomock "go.uber.org/mock/gomock" - api "github.com/Azure/ARO-RP/pkg/api" env "github.com/Azure/ARO-RP/pkg/env" azureclient "github.com/Azure/ARO-RP/pkg/util/azureclient" + gomock "go.uber.org/mock/gomock" ) // MockSkuValidator is a mock of SkuValidator interface. From b127b6e782b76c062f00ad3f0d99f82545167c89 Mon Sep 17 00:00:00 2001 From: b-lnimmala Date: Tue, 19 May 2026 16:41:01 -0400 Subject: [PATCH 13/16] regenerate mocks --- pkg/util/mocks/azureclient/azuresdk/armfeatures/armfeatures.go | 3 ++- pkg/util/mocks/frontend/features_validation.go | 3 ++- pkg/util/mocks/frontend/providers_validation.go | 3 ++- pkg/util/mocks/frontend/quota_validation.go | 3 ++- pkg/util/mocks/frontend/sku_validation.go | 3 ++- 5 files changed, 10 insertions(+), 5 deletions(-) diff --git a/pkg/util/mocks/azureclient/azuresdk/armfeatures/armfeatures.go b/pkg/util/mocks/azureclient/azuresdk/armfeatures/armfeatures.go index 2950385a56c..b9147053b4f 100644 --- a/pkg/util/mocks/azureclient/azuresdk/armfeatures/armfeatures.go +++ b/pkg/util/mocks/azureclient/azuresdk/armfeatures/armfeatures.go @@ -13,8 +13,9 @@ import ( context "context" reflect "reflect" - armfeatures "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armfeatures" gomock "go.uber.org/mock/gomock" + + armfeatures "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armfeatures" ) // MockFeaturesClient is a mock of FeaturesClient interface. diff --git a/pkg/util/mocks/frontend/features_validation.go b/pkg/util/mocks/frontend/features_validation.go index ed4989b4a4a..375b5d9fc7b 100644 --- a/pkg/util/mocks/frontend/features_validation.go +++ b/pkg/util/mocks/frontend/features_validation.go @@ -13,10 +13,11 @@ import ( context "context" reflect "reflect" + gomock "go.uber.org/mock/gomock" + api "github.com/Azure/ARO-RP/pkg/api" env "github.com/Azure/ARO-RP/pkg/env" azureclient "github.com/Azure/ARO-RP/pkg/util/azureclient" - gomock "go.uber.org/mock/gomock" ) // MockFeaturesValidator is a mock of FeaturesValidator interface. diff --git a/pkg/util/mocks/frontend/providers_validation.go b/pkg/util/mocks/frontend/providers_validation.go index 1dc9e84eed0..6e928406fcb 100644 --- a/pkg/util/mocks/frontend/providers_validation.go +++ b/pkg/util/mocks/frontend/providers_validation.go @@ -13,9 +13,10 @@ import ( context "context" reflect "reflect" + gomock "go.uber.org/mock/gomock" + env "github.com/Azure/ARO-RP/pkg/env" azureclient "github.com/Azure/ARO-RP/pkg/util/azureclient" - gomock "go.uber.org/mock/gomock" ) // MockProvidersValidator is a mock of ProvidersValidator interface. diff --git a/pkg/util/mocks/frontend/quota_validation.go b/pkg/util/mocks/frontend/quota_validation.go index 71398bd5060..6e199d53b69 100644 --- a/pkg/util/mocks/frontend/quota_validation.go +++ b/pkg/util/mocks/frontend/quota_validation.go @@ -13,10 +13,11 @@ import ( context "context" reflect "reflect" + gomock "go.uber.org/mock/gomock" + api "github.com/Azure/ARO-RP/pkg/api" env "github.com/Azure/ARO-RP/pkg/env" azureclient "github.com/Azure/ARO-RP/pkg/util/azureclient" - gomock "go.uber.org/mock/gomock" ) // MockQuotaValidator is a mock of QuotaValidator interface. diff --git a/pkg/util/mocks/frontend/sku_validation.go b/pkg/util/mocks/frontend/sku_validation.go index 12ee7501788..32dc598fab4 100644 --- a/pkg/util/mocks/frontend/sku_validation.go +++ b/pkg/util/mocks/frontend/sku_validation.go @@ -13,10 +13,11 @@ import ( context "context" reflect "reflect" + gomock "go.uber.org/mock/gomock" + api "github.com/Azure/ARO-RP/pkg/api" env "github.com/Azure/ARO-RP/pkg/env" azureclient "github.com/Azure/ARO-RP/pkg/util/azureclient" - gomock "go.uber.org/mock/gomock" ) // MockSkuValidator is a mock of SkuValidator interface. From 8437053114178be313ed8a0fbe8d842725ff073a Mon Sep 17 00:00:00 2001 From: b-lnimmala Date: Tue, 26 May 2026 16:37:15 -0400 Subject: [PATCH 14/16] move FP credential creation out of validators and into caller --- pkg/frontend/features_validation.go | 14 +++++--------- pkg/frontend/openshiftcluster_putorpatch.go | 11 ++++++++--- .../openshiftcluster_putorpatch_test.go | 6 +++--- pkg/frontend/quota_validation.go | 19 ++++++++----------- pkg/frontend/sku_validation.go | 13 ++++--------- .../mocks/frontend/features_validation.go | 13 ++++++------- .../mocks/frontend/providers_validation.go | 3 +-- pkg/util/mocks/frontend/quota_validation.go | 12 ++++++------ pkg/util/mocks/frontend/sku_validation.go | 13 ++++++------- 9 files changed, 47 insertions(+), 57 deletions(-) diff --git a/pkg/frontend/features_validation.go b/pkg/frontend/features_validation.go index a7fe8156215..c61442655b7 100644 --- a/pkg/frontend/features_validation.go +++ b/pkg/frontend/features_validation.go @@ -8,19 +8,20 @@ import ( "fmt" "net/http" + "github.com/Azure/azure-sdk-for-go/sdk/azcore" + "github.com/Azure/ARO-RP/pkg/api" "github.com/Azure/ARO-RP/pkg/env" - "github.com/Azure/ARO-RP/pkg/util/azureclient" "github.com/Azure/ARO-RP/pkg/util/azureclient/azuresdk/armfeatures" ) type FeaturesValidator interface { - ValidateSubscriptionFeatures(ctx context.Context, azEnv *azureclient.AROEnvironment, environment env.Interface, subscriptionID, tenantID string, oc *api.OpenShiftCluster) error + ValidateSubscriptionFeatures(ctx context.Context, environment env.Interface, subscriptionID string, fpCred azcore.TokenCredential, oc *api.OpenShiftCluster) error } type featuresValidator struct{} -func (f featuresValidator) ValidateSubscriptionFeatures(ctx context.Context, azEnv *azureclient.AROEnvironment, environment env.Interface, subscriptionID, tenantID string, oc *api.OpenShiftCluster) error { +func (f featuresValidator) ValidateSubscriptionFeatures(ctx context.Context, environment env.Interface, subscriptionID string, fpCred azcore.TokenCredential, oc *api.OpenShiftCluster) error { var fieldPath string if oc.Properties.MasterProfile.EncryptionAtHost == api.EncryptionAtHostEnabled { fieldPath = "properties.masterProfile.encryptionAtHost" @@ -29,12 +30,7 @@ func (f featuresValidator) ValidateSubscriptionFeatures(ctx context.Context, azE } if fieldPath != "" { - fpCred, err := environment.FPNewClientCertificateCredential(tenantID, nil) - if err != nil { - return err - } - - featuresClient, err := armfeatures.NewFeaturesClient(subscriptionID, fpCred, azEnv.ArmClientOptions()) + featuresClient, err := armfeatures.NewFeaturesClient(subscriptionID, fpCred, environment.Environment().ArmClientOptions()) if err != nil { return err } diff --git a/pkg/frontend/openshiftcluster_putorpatch.go b/pkg/frontend/openshiftcluster_putorpatch.go index d0301e52c17..11fced3f67f 100644 --- a/pkg/frontend/openshiftcluster_putorpatch.go +++ b/pkg/frontend/openshiftcluster_putorpatch.go @@ -450,17 +450,22 @@ func (f *frontend) ValidateNewCluster(ctx context.Context, subscription *api.Sub return err } - err = f.skuValidator.ValidateVMSku(ctx, f.env.Environment(), f.env, subscription.ID, subscription.Subscription.Properties.TenantID, cluster) + fpCred, err := f.env.FPNewClientCertificateCredential(subscription.Subscription.Properties.TenantID, nil) if err != nil { return err } - err = f.featuresValidator.ValidateSubscriptionFeatures(ctx, f.env.Environment(), f.env, subscription.ID, subscription.Subscription.Properties.TenantID, cluster) + err = f.skuValidator.ValidateVMSku(ctx, f.env, subscription.ID, fpCred, cluster) if err != nil { return err } - err = f.quotaValidator.ValidateQuota(ctx, f.env.Environment(), f.env, subscription.ID, subscription.Subscription.Properties.TenantID, cluster) + err = f.featuresValidator.ValidateSubscriptionFeatures(ctx, f.env, subscription.ID, fpCred, cluster) + if err != nil { + return err + } + + err = f.quotaValidator.ValidateQuota(ctx, f.env.Environment(), f.env, subscription.ID, fpCred, cluster) if err != nil { return err } diff --git a/pkg/frontend/openshiftcluster_putorpatch_test.go b/pkg/frontend/openshiftcluster_putorpatch_test.go index 2335969c4c5..b28fc44f2e6 100644 --- a/pkg/frontend/openshiftcluster_putorpatch_test.go +++ b/pkg/frontend/openshiftcluster_putorpatch_test.go @@ -741,7 +741,7 @@ func TestPutorPatchOpenShiftClusterCreate(t *testing.T) { mockQuotaValidator.EXPECT().ValidateQuota(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(tt.quotaValidatorError).AnyTimes() mockSkuValidator := mock_frontend.NewMockSkuValidator(controller) - mockSkuValidator.EXPECT().ValidateVMSku(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(tt.skuValidatorError).AnyTimes() + mockSkuValidator.EXPECT().ValidateVMSku(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(tt.skuValidatorError).AnyTimes() mockProvidersValidator := mock_frontend.NewMockProvidersValidator(controller) mockProvidersValidator.EXPECT().ValidateProviders(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(tt.providersValidatorError).AnyTimes() @@ -1145,7 +1145,7 @@ func TestPutorPatchOpenShiftClusterUpdatePut(t *testing.T) { mockQuotaValidator.EXPECT().ValidateQuota(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(tt.quotaValidatorError).AnyTimes() mockSkuValidator := mock_frontend.NewMockSkuValidator(controller) - mockSkuValidator.EXPECT().ValidateVMSku(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(tt.skuValidatorError).AnyTimes() + mockSkuValidator.EXPECT().ValidateVMSku(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(tt.skuValidatorError).AnyTimes() mockProvidersValidator := mock_frontend.NewMockProvidersValidator(controller) mockProvidersValidator.EXPECT().ValidateProviders(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(tt.providersValidatorError).AnyTimes() @@ -1644,7 +1644,7 @@ func TestPutorPatchOpenShiftClusterUpdatePatch(t *testing.T) { mockQuotaValidator.EXPECT().ValidateQuota(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(tt.quotaValidatorError).AnyTimes() mockSkuValidator := mock_frontend.NewMockSkuValidator(controller) - mockSkuValidator.EXPECT().ValidateVMSku(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(tt.skuValidatorError).AnyTimes() + mockSkuValidator.EXPECT().ValidateVMSku(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(tt.skuValidatorError).AnyTimes() mockProvidersValidator := mock_frontend.NewMockProvidersValidator(controller) mockProvidersValidator.EXPECT().ValidateProviders(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(tt.providersValidatorError).AnyTimes() diff --git a/pkg/frontend/quota_validation.go b/pkg/frontend/quota_validation.go index aa9fb787680..131c93ae686 100644 --- a/pkg/frontend/quota_validation.go +++ b/pkg/frontend/quota_validation.go @@ -8,6 +8,10 @@ import ( "fmt" "net/http" + "github.com/jongio/azidext/go/azidext" + + "github.com/Azure/azure-sdk-for-go/sdk/azcore" + "github.com/Azure/ARO-RP/pkg/api" "github.com/Azure/ARO-RP/pkg/api/validate" "github.com/Azure/ARO-RP/pkg/env" @@ -17,7 +21,7 @@ import ( ) type QuotaValidator interface { - ValidateQuota(ctx context.Context, azEnv *azureclient.AROEnvironment, environment env.Interface, subscriptionID, tenantID string, oc *api.OpenShiftCluster) error + ValidateQuota(ctx context.Context, azEnv *azureclient.AROEnvironment, environment env.Interface, subscriptionID string, fpCred azcore.TokenCredential, oc *api.OpenShiftCluster) error } type quotaValidator struct{} @@ -39,20 +43,13 @@ func addRequiredResources(requiredResources map[string]int, vmSize api.VMSize, c // ValidateQuota checks usage quotas vs. resources required by cluster before cluster // creation // It is a method on struct so we can make use of interfaces. -func (q quotaValidator) ValidateQuota(ctx context.Context, azEnv *azureclient.AROEnvironment, environment env.Interface, subscriptionID, tenantID string, oc *api.OpenShiftCluster) error { - fpAuthorizer, err := environment.FPAuthorizer(tenantID, nil, environment.Environment().ResourceManagerScope) - if err != nil { - return err - } +func (q quotaValidator) ValidateQuota(ctx context.Context, azEnv *azureclient.AROEnvironment, environment env.Interface, subscriptionID string, fpCred azcore.TokenCredential, oc *api.OpenShiftCluster) error { + fpAuthorizer := azidext.NewTokenCredentialAdapter(fpCred, []string{environment.Environment().ResourceManagerScope}) - credential, err := environment.FPNewClientCertificateCredential(tenantID, []string{}) - if err != nil { - return err - } options := environment.Environment().ArmClientOptions() spComputeUsage := compute.NewUsageClient(azEnv, subscriptionID, fpAuthorizer) - spNetworkUsage, err := armnetwork.NewUsagesClient(subscriptionID, credential, options) + spNetworkUsage, err := armnetwork.NewUsagesClient(subscriptionID, fpCred, options) if err != nil { return err } diff --git a/pkg/frontend/sku_validation.go b/pkg/frontend/sku_validation.go index 47bf319e2b0..300efc9ff50 100644 --- a/pkg/frontend/sku_validation.go +++ b/pkg/frontend/sku_validation.go @@ -8,28 +8,23 @@ import ( "fmt" "net/http" + "github.com/Azure/azure-sdk-for-go/sdk/azcore" sdkcompute "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute/v7" "github.com/Azure/ARO-RP/pkg/api" "github.com/Azure/ARO-RP/pkg/env" - "github.com/Azure/ARO-RP/pkg/util/azureclient" "github.com/Azure/ARO-RP/pkg/util/azureclient/azuresdk/armcompute" "github.com/Azure/ARO-RP/pkg/util/computeskus" ) type SkuValidator interface { - ValidateVMSku(ctx context.Context, azEnv *azureclient.AROEnvironment, environment env.Interface, subscriptionID, tenantID string, oc *api.OpenShiftCluster) error + ValidateVMSku(ctx context.Context, environment env.Interface, subscriptionID string, fpCred azcore.TokenCredential, oc *api.OpenShiftCluster) error } type skuValidator struct{} -func (s skuValidator) ValidateVMSku(ctx context.Context, azEnv *azureclient.AROEnvironment, environment env.Interface, subscriptionID, tenantID string, oc *api.OpenShiftCluster) error { - fpCredClusterTenant, err := environment.FPNewClientCertificateCredential(tenantID, nil) - if err != nil { - return err - } - - armResourceSKUsClient, err := armcompute.NewResourceSKUsClient(subscriptionID, fpCredClusterTenant, environment.Environment().ArmClientOptions()) +func (s skuValidator) ValidateVMSku(ctx context.Context, environment env.Interface, subscriptionID string, fpCred azcore.TokenCredential, oc *api.OpenShiftCluster) error { + armResourceSKUsClient, err := armcompute.NewResourceSKUsClient(subscriptionID, fpCred, environment.Environment().ArmClientOptions()) if err != nil { return err } diff --git a/pkg/util/mocks/frontend/features_validation.go b/pkg/util/mocks/frontend/features_validation.go index 375b5d9fc7b..c177bc8cd8d 100644 --- a/pkg/util/mocks/frontend/features_validation.go +++ b/pkg/util/mocks/frontend/features_validation.go @@ -13,11 +13,10 @@ import ( context "context" reflect "reflect" - gomock "go.uber.org/mock/gomock" - api "github.com/Azure/ARO-RP/pkg/api" env "github.com/Azure/ARO-RP/pkg/env" - azureclient "github.com/Azure/ARO-RP/pkg/util/azureclient" + azcore "github.com/Azure/azure-sdk-for-go/sdk/azcore" + gomock "go.uber.org/mock/gomock" ) // MockFeaturesValidator is a mock of FeaturesValidator interface. @@ -45,15 +44,15 @@ func (m *MockFeaturesValidator) EXPECT() *MockFeaturesValidatorMockRecorder { } // ValidateSubscriptionFeatures mocks base method. -func (m *MockFeaturesValidator) ValidateSubscriptionFeatures(ctx context.Context, azEnv *azureclient.AROEnvironment, environment env.Interface, subscriptionID, tenantID string, oc *api.OpenShiftCluster) error { +func (m *MockFeaturesValidator) ValidateSubscriptionFeatures(ctx context.Context, environment env.Interface, subscriptionID string, fpCred azcore.TokenCredential, oc *api.OpenShiftCluster) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ValidateSubscriptionFeatures", ctx, azEnv, environment, subscriptionID, tenantID, oc) + ret := m.ctrl.Call(m, "ValidateSubscriptionFeatures", ctx, environment, subscriptionID, fpCred, oc) ret0, _ := ret[0].(error) return ret0 } // ValidateSubscriptionFeatures indicates an expected call of ValidateSubscriptionFeatures. -func (mr *MockFeaturesValidatorMockRecorder) ValidateSubscriptionFeatures(ctx, azEnv, environment, subscriptionID, tenantID, oc any) *gomock.Call { +func (mr *MockFeaturesValidatorMockRecorder) ValidateSubscriptionFeatures(ctx, environment, subscriptionID, fpCred, oc any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ValidateSubscriptionFeatures", reflect.TypeOf((*MockFeaturesValidator)(nil).ValidateSubscriptionFeatures), ctx, azEnv, environment, subscriptionID, tenantID, oc) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ValidateSubscriptionFeatures", reflect.TypeOf((*MockFeaturesValidator)(nil).ValidateSubscriptionFeatures), ctx, environment, subscriptionID, fpCred, oc) } diff --git a/pkg/util/mocks/frontend/providers_validation.go b/pkg/util/mocks/frontend/providers_validation.go index 6e928406fcb..1dc9e84eed0 100644 --- a/pkg/util/mocks/frontend/providers_validation.go +++ b/pkg/util/mocks/frontend/providers_validation.go @@ -13,10 +13,9 @@ import ( context "context" reflect "reflect" - gomock "go.uber.org/mock/gomock" - env "github.com/Azure/ARO-RP/pkg/env" azureclient "github.com/Azure/ARO-RP/pkg/util/azureclient" + gomock "go.uber.org/mock/gomock" ) // MockProvidersValidator is a mock of ProvidersValidator interface. diff --git a/pkg/util/mocks/frontend/quota_validation.go b/pkg/util/mocks/frontend/quota_validation.go index 6e199d53b69..5aabbe470f1 100644 --- a/pkg/util/mocks/frontend/quota_validation.go +++ b/pkg/util/mocks/frontend/quota_validation.go @@ -13,11 +13,11 @@ import ( context "context" reflect "reflect" - gomock "go.uber.org/mock/gomock" - api "github.com/Azure/ARO-RP/pkg/api" env "github.com/Azure/ARO-RP/pkg/env" azureclient "github.com/Azure/ARO-RP/pkg/util/azureclient" + azcore "github.com/Azure/azure-sdk-for-go/sdk/azcore" + gomock "go.uber.org/mock/gomock" ) // MockQuotaValidator is a mock of QuotaValidator interface. @@ -45,15 +45,15 @@ func (m *MockQuotaValidator) EXPECT() *MockQuotaValidatorMockRecorder { } // ValidateQuota mocks base method. -func (m *MockQuotaValidator) ValidateQuota(ctx context.Context, azEnv *azureclient.AROEnvironment, environment env.Interface, subscriptionID, tenantID string, oc *api.OpenShiftCluster) error { +func (m *MockQuotaValidator) ValidateQuota(ctx context.Context, azEnv *azureclient.AROEnvironment, environment env.Interface, subscriptionID string, fpCred azcore.TokenCredential, oc *api.OpenShiftCluster) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ValidateQuota", ctx, azEnv, environment, subscriptionID, tenantID, oc) + ret := m.ctrl.Call(m, "ValidateQuota", ctx, azEnv, environment, subscriptionID, fpCred, oc) ret0, _ := ret[0].(error) return ret0 } // ValidateQuota indicates an expected call of ValidateQuota. -func (mr *MockQuotaValidatorMockRecorder) ValidateQuota(ctx, azEnv, environment, subscriptionID, tenantID, oc any) *gomock.Call { +func (mr *MockQuotaValidatorMockRecorder) ValidateQuota(ctx, azEnv, environment, subscriptionID, fpCred, oc any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ValidateQuota", reflect.TypeOf((*MockQuotaValidator)(nil).ValidateQuota), ctx, azEnv, environment, subscriptionID, tenantID, oc) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ValidateQuota", reflect.TypeOf((*MockQuotaValidator)(nil).ValidateQuota), ctx, azEnv, environment, subscriptionID, fpCred, oc) } diff --git a/pkg/util/mocks/frontend/sku_validation.go b/pkg/util/mocks/frontend/sku_validation.go index 32dc598fab4..b0b7ab2d54a 100644 --- a/pkg/util/mocks/frontend/sku_validation.go +++ b/pkg/util/mocks/frontend/sku_validation.go @@ -13,11 +13,10 @@ import ( context "context" reflect "reflect" - gomock "go.uber.org/mock/gomock" - api "github.com/Azure/ARO-RP/pkg/api" env "github.com/Azure/ARO-RP/pkg/env" - azureclient "github.com/Azure/ARO-RP/pkg/util/azureclient" + azcore "github.com/Azure/azure-sdk-for-go/sdk/azcore" + gomock "go.uber.org/mock/gomock" ) // MockSkuValidator is a mock of SkuValidator interface. @@ -45,15 +44,15 @@ func (m *MockSkuValidator) EXPECT() *MockSkuValidatorMockRecorder { } // ValidateVMSku mocks base method. -func (m *MockSkuValidator) ValidateVMSku(ctx context.Context, azEnv *azureclient.AROEnvironment, environment env.Interface, subscriptionID, tenantID string, oc *api.OpenShiftCluster) error { +func (m *MockSkuValidator) ValidateVMSku(ctx context.Context, environment env.Interface, subscriptionID string, fpCred azcore.TokenCredential, oc *api.OpenShiftCluster) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ValidateVMSku", ctx, azEnv, environment, subscriptionID, tenantID, oc) + ret := m.ctrl.Call(m, "ValidateVMSku", ctx, environment, subscriptionID, fpCred, oc) ret0, _ := ret[0].(error) return ret0 } // ValidateVMSku indicates an expected call of ValidateVMSku. -func (mr *MockSkuValidatorMockRecorder) ValidateVMSku(ctx, azEnv, environment, subscriptionID, tenantID, oc any) *gomock.Call { +func (mr *MockSkuValidatorMockRecorder) ValidateVMSku(ctx, environment, subscriptionID, fpCred, oc any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ValidateVMSku", reflect.TypeOf((*MockSkuValidator)(nil).ValidateVMSku), ctx, azEnv, environment, subscriptionID, tenantID, oc) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ValidateVMSku", reflect.TypeOf((*MockSkuValidator)(nil).ValidateVMSku), ctx, environment, subscriptionID, fpCred, oc) } From 30147dd457320f1e8f69ad3e09b832e6f193f0fe Mon Sep 17 00:00:00 2001 From: b-lnimmala Date: Wed, 27 May 2026 14:40:55 -0400 Subject: [PATCH 15/16] regenerate mocks --- pkg/util/mocks/frontend/features_validation.go | 6 ++++-- pkg/util/mocks/frontend/providers_validation.go | 3 ++- pkg/util/mocks/frontend/quota_validation.go | 6 ++++-- pkg/util/mocks/frontend/sku_validation.go | 6 ++++-- 4 files changed, 14 insertions(+), 7 deletions(-) diff --git a/pkg/util/mocks/frontend/features_validation.go b/pkg/util/mocks/frontend/features_validation.go index c177bc8cd8d..3620a4b74a0 100644 --- a/pkg/util/mocks/frontend/features_validation.go +++ b/pkg/util/mocks/frontend/features_validation.go @@ -13,10 +13,12 @@ import ( context "context" reflect "reflect" + gomock "go.uber.org/mock/gomock" + + azcore "github.com/Azure/azure-sdk-for-go/sdk/azcore" + api "github.com/Azure/ARO-RP/pkg/api" env "github.com/Azure/ARO-RP/pkg/env" - azcore "github.com/Azure/azure-sdk-for-go/sdk/azcore" - gomock "go.uber.org/mock/gomock" ) // MockFeaturesValidator is a mock of FeaturesValidator interface. diff --git a/pkg/util/mocks/frontend/providers_validation.go b/pkg/util/mocks/frontend/providers_validation.go index 1dc9e84eed0..6e928406fcb 100644 --- a/pkg/util/mocks/frontend/providers_validation.go +++ b/pkg/util/mocks/frontend/providers_validation.go @@ -13,9 +13,10 @@ import ( context "context" reflect "reflect" + gomock "go.uber.org/mock/gomock" + env "github.com/Azure/ARO-RP/pkg/env" azureclient "github.com/Azure/ARO-RP/pkg/util/azureclient" - gomock "go.uber.org/mock/gomock" ) // MockProvidersValidator is a mock of ProvidersValidator interface. diff --git a/pkg/util/mocks/frontend/quota_validation.go b/pkg/util/mocks/frontend/quota_validation.go index 5aabbe470f1..f86a0e55843 100644 --- a/pkg/util/mocks/frontend/quota_validation.go +++ b/pkg/util/mocks/frontend/quota_validation.go @@ -13,11 +13,13 @@ import ( context "context" reflect "reflect" + gomock "go.uber.org/mock/gomock" + + azcore "github.com/Azure/azure-sdk-for-go/sdk/azcore" + api "github.com/Azure/ARO-RP/pkg/api" env "github.com/Azure/ARO-RP/pkg/env" azureclient "github.com/Azure/ARO-RP/pkg/util/azureclient" - azcore "github.com/Azure/azure-sdk-for-go/sdk/azcore" - gomock "go.uber.org/mock/gomock" ) // MockQuotaValidator is a mock of QuotaValidator interface. diff --git a/pkg/util/mocks/frontend/sku_validation.go b/pkg/util/mocks/frontend/sku_validation.go index b0b7ab2d54a..a4e303f0f6c 100644 --- a/pkg/util/mocks/frontend/sku_validation.go +++ b/pkg/util/mocks/frontend/sku_validation.go @@ -13,10 +13,12 @@ import ( context "context" reflect "reflect" + gomock "go.uber.org/mock/gomock" + + azcore "github.com/Azure/azure-sdk-for-go/sdk/azcore" + api "github.com/Azure/ARO-RP/pkg/api" env "github.com/Azure/ARO-RP/pkg/env" - azcore "github.com/Azure/azure-sdk-for-go/sdk/azcore" - gomock "go.uber.org/mock/gomock" ) // MockSkuValidator is a mock of SkuValidator interface. From 7b7b566afcefdf087f30b67fcb2929fc98c789ba Mon Sep 17 00:00:00 2001 From: b-lnimmala Date: Wed, 27 May 2026 15:11:21 -0400 Subject: [PATCH 16/16] fix unit tests by adding missing FPNewclientcertificatecredential mock expectation --- pkg/frontend/shared_test.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pkg/frontend/shared_test.go b/pkg/frontend/shared_test.go index a20c305c27d..18f8a791d5c 100644 --- a/pkg/frontend/shared_test.go +++ b/pkg/frontend/shared_test.go @@ -21,6 +21,7 @@ import ( "github.com/sirupsen/logrus" "go.uber.org/mock/gomock" + "github.com/Azure/azure-sdk-for-go/sdk/azidentity" "github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azsecrets" "github.com/Azure/ARO-RP/pkg/api" @@ -132,6 +133,7 @@ func newTestInfraWithFeatures(t *testing.T, features map[env.Feature]bool) *test _env.EXPECT().Domain().AnyTimes().Return("aro.example") _env.EXPECT().Listen().AnyTimes().Return(l, nil) _env.EXPECT().NewMSITokenCredential().AnyTimes().Return(nil, fmt.Errorf("MSI not available in test")) + _env.EXPECT().FPNewClientCertificateCredential(gomock.Any(), gomock.Any()).AnyTimes().Return(&azidentity.ClientCertificateCredential{}, nil) for f, val := range features { _env.EXPECT().FeatureIsSet(f).AnyTimes().Return(val) }