diff --git a/test/functional/shared/resources/aws_s3_bucket_test.go b/test/functional/shared/resources/aws_s3_bucket_test.go index 2c475da5ef..8ebe601556 100644 --- a/test/functional/shared/resources/aws_s3_bucket_test.go +++ b/test/functional/shared/resources/aws_s3_bucket_test.go @@ -129,5 +129,6 @@ func Test_AWS_S3Bucket_Existing(t *testing.T) { }, }) + test.RequiredFeatures = []shared.RequiredFeature{shared.FeatureAWS} test.Test(t) } diff --git a/test/functional/shared/resources/azure_connections_test.go b/test/functional/shared/resources/azure_connections_test.go index e341c7b640..d004222e58 100644 --- a/test/functional/shared/resources/azure_connections_test.go +++ b/test/functional/shared/resources/azure_connections_test.go @@ -63,5 +63,6 @@ func Test_AzureConnections(t *testing.T) { }, }) + test.RequiredFeatures = []shared.RequiredFeature{shared.FeatureAzure} test.Test(t) } diff --git a/test/functional/shared/rptest.go b/test/functional/shared/rptest.go index 05e8fbd4ba..251aa72fa4 100644 --- a/test/functional/shared/rptest.go +++ b/test/functional/shared/rptest.go @@ -53,6 +53,8 @@ const ( daprFeatureMessage = "This test requires Dapr installed in your Kubernetes cluster. Please install Dapr by following the instructions at https://docs.dapr.io/operations/hosting/kubernetes/kubernetes-deploy/." secretProviderClassesCRD = "secretproviderclasses.secrets-store.csi.x-k8s.io" csiDriverMessage = "This test requires secret store CSI driver. Please install it by following https://secrets-store-csi-driver.sigs.k8s.io/." + awsMessage = "This test requires AWS. Please configure the test environment to include an AWS provider." + azureMessage = "This test requires Azure. Please configure the test environment to include an Azure provider." ) // RequiredFeature is used to specify an optional feature that is required @@ -67,6 +69,23 @@ const ( // FeatureCSIDriver should be used with required features to indicate a test dependency // on the CSI driver. FeatureCSIDriver RequiredFeature = "CSIDriver" + + // FeatureAWS should be used with required features to indicate a test dependency on AWS cloud provider. + FeatureAWS RequiredFeature = "AWS" + + // FeatureAzure should be used with required features to indicate a test dependency on Azure cloud provider. + FeatureAzure RequiredFeature = "Azure" +) + +// RequiredFeatureValidatorType is used to specify the type of validator to use +type RequiredFeatureValidatorType string + +const ( + // Use CRD to check for required features + RequiredFeatureValidatorTypeCRD RequiredFeatureValidatorType = "ValidatorCRD" + + // Use cloud provider API to check for required features + RequiredFeatureValidatorTypeCloud RequiredFeatureValidatorType = "ValidatorCloud" ) type TestStep struct { @@ -185,23 +204,44 @@ func (ct RPTest) CleanUpExtensionResources(resources []unstructured.Unstructured // returns an error if there is an issue. func (ct RPTest) CheckRequiredFeatures(ctx context.Context, t *testing.T) { for _, feature := range ct.RequiredFeatures { - var crd, message string + var crd, message, credential string + var validatorType RequiredFeatureValidatorType switch feature { case FeatureDapr: crd = daprComponentCRD message = daprFeatureMessage + validatorType = RequiredFeatureValidatorTypeCRD case FeatureCSIDriver: crd = secretProviderClassesCRD message = csiDriverMessage + validatorType = RequiredFeatureValidatorTypeCRD + case FeatureAWS: + message = awsMessage + credential = "aws" + validatorType = RequiredFeatureValidatorTypeCloud + case FeatureAzure: + message = azureMessage + credential = "azure" + validatorType = RequiredFeatureValidatorTypeCloud default: panic(fmt.Sprintf("unsupported feature: %s", feature)) } - err := ct.Options.Client.Get(ctx, client.ObjectKey{Name: crd}, &apiextv1.CustomResourceDefinition{}) - if apierrors.IsNotFound(err) { - t.Skip(message) - } else if err != nil { - require.NoError(t, err, "failed to check for required features") + switch validatorType { + case RequiredFeatureValidatorTypeCRD: + err := ct.Options.Client.Get(ctx, client.ObjectKey{Name: crd}, &apiextv1.CustomResourceDefinition{}) + if apierrors.IsNotFound(err) { + t.Skip(message) + } else if err != nil { + require.NoError(t, err, "failed to check for required features") + } + case RequiredFeatureValidatorTypeCloud: + exists := validation.DoesCredentialExist(t, credential) + if !exists { + t.Skip(message) + } + default: + panic(fmt.Sprintf("unsupported required features validator type: %s", validatorType)) } } } diff --git a/test/functional/ucp/aws_credential_test.go b/test/functional/ucp/aws_credential_test.go index 35b3788beb..249789fc5a 100644 --- a/test/functional/ucp/aws_credential_test.go +++ b/test/functional/ucp/aws_credential_test.go @@ -37,6 +37,7 @@ func Test_AWS_Credential_Operations(t *testing.T) { runAWSCredentialTests(t, resourceURL, collectionURL, roundTripper, getAWSTestCredentialObject(), getExpectedAWSTestCredentialObject()) }) + test.RequiredFeatures = []RequiredFeature{"AWS"} test.Test(t) } diff --git a/test/functional/ucp/aws_test.go b/test/functional/ucp/aws_test.go index 7dd6e31064..52fedff077 100644 --- a/test/functional/ucp/aws_test.go +++ b/test/functional/ucp/aws_test.go @@ -46,10 +46,9 @@ var ( func Test_AWS_DeleteResource(t *testing.T) { ctx := context.Background() - bucketName := generateS3BucketName() - setupTestAWSResource(t, ctx, bucketName) - test := NewUCPTest(t, "Test_AWS_DeleteResource", func(t *testing.T, url string, roundTripper http.RoundTripper) { + bucketName := generateS3BucketName() + setupTestAWSResource(t, ctx, bucketName) resourceID, err := validation.GetResourceIdentifier(ctx, s3BucketResourceType, bucketName) require.NoError(t, err) @@ -101,16 +100,16 @@ func Test_AWS_DeleteResource(t *testing.T) { require.True(t, deleteSucceeded) }) + test.RequiredFeatures = []RequiredFeature{FeatureAWS} test.Test(t) } func Test_AWS_ListResources(t *testing.T) { ctx := context.Background() - var bucketName = generateS3BucketName() - setupTestAWSResource(t, ctx, bucketName) - test := NewUCPTest(t, "Test_AWS_ListResources", func(t *testing.T, url string, roundTripper http.RoundTripper) { + var bucketName = generateS3BucketName() + setupTestAWSResource(t, ctx, bucketName) resourceID, err := validation.GetResourceIdentifier(ctx, s3BucketResourceType, bucketName) require.NoError(t, err) @@ -140,6 +139,7 @@ func Test_AWS_ListResources(t *testing.T) { require.GreaterOrEqual(t, len(body["value"]), 1) }) + test.RequiredFeatures = []RequiredFeature{FeatureAWS} test.Test(t) } diff --git a/test/functional/ucp/azure_credential_test.go b/test/functional/ucp/azure_credential_test.go index 45e127001b..fbc72e2501 100644 --- a/test/functional/ucp/azure_credential_test.go +++ b/test/functional/ucp/azure_credential_test.go @@ -37,6 +37,7 @@ func Test_Azure_Credential_Operations(t *testing.T) { runAzureCredentialTests(t, resourceURL, collectionURL, roundTripper, getAzureTestCredentialObject(), getExpectedAzureTestCredentialObject()) }) + test.RequiredFeatures = []RequiredFeature{"Azure"} test.Test(t) } diff --git a/test/functional/ucp/ucptest.go b/test/functional/ucp/ucptest.go index 85bea2f774..e25e7868cf 100644 --- a/test/functional/ucp/ucptest.go +++ b/test/functional/ucp/ucptest.go @@ -17,6 +17,8 @@ limitations under the License. package ucp import ( + "context" + "fmt" "io" "net/http" "os" @@ -31,17 +33,36 @@ import ( "github.com/stretchr/testify/require" ) -const ContainerLogPathEnvVar = "RADIUS_CONTAINER_LOG_PATH" +const ( + ContainerLogPathEnvVar = "RADIUS_CONTAINER_LOG_PATH" + awsMessage = "This test requires AWS. Please configure the test environment to include an AWS provider." + azureMessage = "This test requires Azure. Please configure the test environment to include an Azure provider." +) var radiusControllerLogSync sync.Once type TestRunMethod func(t *testing.T, url string, roundtripper http.RoundTripper) +// RequiredFeature is used to specify an optional feature that is required +// for the test to run. +type RequiredFeature string + +const ( + // FeatureAWS should be used with required features to indicate a test dependency on AWS cloud provider. + FeatureAWS RequiredFeature = "AWS" + + // FeatureAzure should be used with required features to indicate a test dependency on Azure cloud provider. + FeatureAzure RequiredFeature = "Azure" +) + type UCPTest struct { Options test.TestOptions Name string Description string RunMethod TestRunMethod + + // RequiredFeatures is a list of features that are required for the test to run. + RequiredFeatures []RequiredFeature } type TestStep struct { @@ -59,6 +80,9 @@ func NewUCPTest(t *testing.T, name string, runMethod TestRunMethod) UCPTest { func (ucptest UCPTest) Test(t *testing.T) { ctx, cancel := testcontext.NewWithCancel(t) + + ucptest.CheckRequiredFeatures(ctx, t) + t.Cleanup(cancel) t.Parallel() @@ -104,3 +128,26 @@ func NewUCPRequest(method string, url string, body io.Reader) (*http.Request, er return req, nil } + +// CheckRequiredFeatures checks the test environment for the features that the test requires and skips the test if not, otherwise +// returns an error if there is an issue. +func (ct UCPTest) CheckRequiredFeatures(ctx context.Context, t *testing.T) { + for _, feature := range ct.RequiredFeatures { + var credential, message string + switch feature { + case FeatureAWS: + message = awsMessage + credential = "aws" + case FeatureAzure: + message = azureMessage + credential = "azure" + default: + panic(fmt.Sprintf("unsupported feature: %s", feature)) + } + + exists := validation.DoesCredentialExist(t, credential) + if !exists { + t.Skip(message) + } + } +} diff --git a/test/validation/shared.go b/test/validation/shared.go index d1694b548a..e47476d3af 100644 --- a/test/validation/shared.go +++ b/test/validation/shared.go @@ -24,11 +24,14 @@ import ( "testing" "github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime" + "github.com/radius-project/radius/pkg/cli" "github.com/radius-project/radius/pkg/cli/clients" + "github.com/radius-project/radius/pkg/cli/connections" "github.com/radius-project/radius/pkg/cli/output" "github.com/stretchr/testify/require" "github.com/radius-project/radius/test/radcli" + "github.com/radius-project/radius/test/testcontext" ) const ( @@ -169,3 +172,24 @@ func ValidateRPResources(ctx context.Context, t *testing.T, expected *RPResource } } } + +// DoesCredentialExist checks if the credential is registered in the workspace and returns a boolean value. +func DoesCredentialExist(t *testing.T, credential string) bool { + ctx := testcontext.New(t) + + config, err := cli.LoadConfig("") + require.NoError(t, err, "failed to read radius config") + + workspace, err := cli.GetWorkspace(config, "") + require.NoError(t, err, "failed to read default workspace") + require.NotNil(t, workspace, "default workspace is not set") + + t.Logf("Loaded workspace: %s (%s)", workspace.Name, workspace.FmtConnection()) + + credentialsClient, err := connections.DefaultFactory.CreateCredentialManagementClient(ctx, *workspace) + require.NoError(t, err, "failed to create credentials client") + cred, err := credentialsClient.Get(ctx, credential) + require.NoError(t, err, "failed to get credentials") + + return cred.CloudProviderStatus.Enabled +}