diff --git a/.github/workflows/functional-test.yaml b/.github/workflows/functional-test.yaml index e4904855c7..b97f0a480b 100644 --- a/.github/workflows/functional-test.yaml +++ b/.github/workflows/functional-test.yaml @@ -70,6 +70,8 @@ env: ACTION_LINK: '${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}' # Server where terraform test modules are deployed TF_RECIPE_MODULE_SERVER_URL: "http://tf-module-server.radius-test-tf-module-server.svc.cluster.local" + # Private Git repository where terraform module is stored. + TF_RECIPE_PRIVATE_GIT_SOURCE: "git::https://github.com/radius-project/terraform-private-modules//kubernetes-redis" jobs: build: @@ -557,6 +559,7 @@ jobs: INTEGRATION_TEST_RESOURCE_GROUP_NAME: ${{ env.AZURE_TEST_RESOURCE_GROUP }} BICEP_RECIPE_REGISTRY: ${{ env.BICEP_RECIPE_REGISTRY }} BICEP_RECIPE_TAG_VERSION: ${{ env.BICEP_RECIPE_TAG_VERSION }} + GH_ACCOUNT: "test-git-pat" - uses: azure/setup-kubectl@v3 if: always() with: diff --git a/test/functional/shared/resources/recipe_terraform_test.go b/test/functional/shared/resources/recipe_terraform_test.go index ec02155b16..6cd50f1cc0 100644 --- a/test/functional/shared/resources/recipe_terraform_test.go +++ b/test/functional/shared/resources/recipe_terraform_test.go @@ -425,3 +425,89 @@ func getSecretSuffix(resourceID, envName, appName string) (string, error) { return kubernetes["secret_suffix"].(string), nil } + +// Test_TerraformRecipe_Redis covers the following terraform recipe scenario: +// +// - Create an extender resource using a Terraform recipe that deploys Redis on Kubernetes. +// - The recipe deployment creates a Kubernetes deployment and a Kubernetes service. +func Test_TerraformPrivateGitModule_KubernetesRedis(t *testing.T) { + template := "testdata/corerp-resources-terraform-private-git-repo-redis.bicep" + name := "corerp-resources-terraform-private-redis" + appName := "corerp-resources-terraform-private-app" + envName := "corerp-resources-terraform-private-env" + redisCacheName := "tf-redis-cache-private" + + secretSuffix, err := getSecretSuffix("/planes/radius/local/resourcegroups/kind-radius/providers/Applications.Core/extenders/"+name, envName, appName) + require.NoError(t, err) + t.Logf(fmt.Sprintf("modulesource terraform git:%s", testutil.GetTerraformPrivateModuleSource())) + t.Logf(fmt.Sprintf("modulesource git token :%s", testutil.GetGitPAT())) + test := shared.NewRPTest(t, name, []shared.TestStep{ + { + Executor: step.NewDeployExecutor(template, testutil.GetTerraformPrivateModuleSource(), "appName="+appName, "redisCacheName="+redisCacheName, testutil.GetGitPAT()), + RPResources: &validation.RPResourceSet{ + Resources: []validation.RPResource{ + { + Name: envName, + Type: validation.EnvironmentsResource, + }, + { + Name: appName, + Type: validation.ApplicationsResource, + }, + { + Name: name, + Type: validation.ExtendersResource, + App: appName, + OutputResources: []validation.OutputResourceResponse{ + {ID: "/planes/kubernetes/local/namespaces/corerp-resources-terraform-redis-app/providers/apps/Deployment/tf-redis-cache-private"}, + {ID: "/planes/kubernetes/local/namespaces/corerp-resources-terraform-redis-app/providers/core/Service/tf-redis-cache-private"}, + }, + }, + }, + }, + K8sObjects: &validation.K8sObjectSet{ + Namespaces: map[string][]validation.K8sObject{ + appName: { + validation.NewK8sServiceForResource(appName, redisCacheName). + ValidateLabels(false), + }, + secretNamespace: { + validation.NewK8sSecretForResourceWithResourceName(secretPrefix + secretSuffix). + ValidateLabels(false), + }, + }, + }, + PostStepVerify: func(ctx context.Context, t *testing.T, test shared.RPTest) { + secret, err := test.Options.K8sClient.CoreV1().Secrets(secretNamespace). + Get(ctx, secretPrefix+secretSuffix, metav1.GetOptions{}) + require.NoError(t, err) + require.Equal(t, secretNamespace, secret.Namespace) + require.Equal(t, secretPrefix+secretSuffix, secret.Name) + + redis, err := test.Options.ManagementClient.ShowResource(ctx, "Applications.Core/extenders", name) + require.NoError(t, err) + require.NotNil(t, redis) + status := redis.Properties["status"].(map[string]any) + recipe := status["recipe"].(map[string]interface{}) + require.Equal(t, "terraform", recipe["templateKind"].(string)) + expectedTemplatePath := strings.Replace(testutil.GetTerraformPrivateModuleSource(), "moduleServer=", "", 1) + require.Equal(t, expectedTemplatePath, recipe["templatePath"].(string)) + // At present, it is not possible to verify the template version in functional tests + // This is verified by UTs though + + // Manually delete Kubernetes the secret that stores the Terraform state file now. The next step in the test will be the deletion + // of the portable resource that uses this secret for Terraform recipe. This is to verify that the test and portable resource + // deletion will not fail even though the secret is already deleted. + err = test.Options.K8sClient.CoreV1().Secrets(secretNamespace).Delete(ctx, secretPrefix+secretSuffix, metav1.DeleteOptions{}) + require.NoError(t, err) + }, + }, + }) + + test.PostDeleteVerify = func(ctx context.Context, t *testing.T, test shared.RPTest) { + resourceID := "/planes/radius/local/resourcegroups/kind-radius/providers/Applications.Core/extenders/" + name + testSecretDeletion(t, ctx, test, appName, envName, resourceID) + } + + test.Test(t) +} diff --git a/test/functional/shared/resources/testdata/corerp-resources-terraform-private-git-repo-redis.bicep b/test/functional/shared/resources/testdata/corerp-resources-terraform-private-git-repo-redis.bicep new file mode 100644 index 0000000000..08648c1e28 --- /dev/null +++ b/test/functional/shared/resources/testdata/corerp-resources-terraform-private-git-repo-redis.bicep @@ -0,0 +1,85 @@ +import radius as radius + +@description('Name of the Redis Cache resource.') +param redisCacheName string + +@description('Name of the Radius Application.') +param appName string + +@secure() +param pat string + +@description('Private Git module source in generic git format.') +param privateGitModule string + +resource env 'Applications.Core/environments@2023-10-01-preview' = { + name: 'corerp-resources-terraform-private-env' + properties: { + compute: { + kind: 'kubernetes' + resourceId: 'self' + namespace: 'corerp-resources-terraform-private-env' + } + recipeConfig: { + terraform: { + authentication: { + git: { + pat: { + 'github.com':{ + secret: moduleSecrets.id + } + } + } + } + } + } + recipes: { + 'Applications.Core/extenders': { + default: { + templateKind: 'terraform' + templatePath: privateGitModule + } + } + } + } +} + +resource app 'Applications.Core/applications@2023-10-01-preview' = { + name: appName + properties: { + environment: env.id + extensions: [ + { + kind: 'kubernetesNamespace' + namespace: appName + } + ] + } +} + +resource webapp 'Applications.Core/extenders@2023-10-01-preview' = { + name: 'corerp-resources-terraform-private-redis' + properties: { + application: app.id + environment: env.id + recipe: { + name: 'default' + parameters: { + redis_cache_name: redisCacheName + } + } + } +} + +resource moduleSecrets 'Applications.Core/secretStores@2023-10-01-preview' = { + name: 'module-secrets' + properties: { + resource: 'test-namespace/github' + type: 'generic' + data: { + pat: { + value: pat + } + } + } +} diff --git a/test/testutil/testutil.go b/test/testutil/testutil.go index e76450326d..6cb9397686 100644 --- a/test/testutil/testutil.go +++ b/test/testutil/testutil.go @@ -126,6 +126,19 @@ func GetTerraformRecipeModuleServerURL() string { return "moduleServer=" + u } +func GetTerraformPrivateModuleSource() string { + u := os.Getenv("TF_RECIPE_PRIVATE_GIT_SOURCE") + if u == "" { + return "privateGitModule=git::https://github.com/radius-project/terraform-private-modules//kubernetes-redis" + } + return "privateGitModule=" + u +} + +func GetGitPAT() string { + u := os.Getenv("GH_ACCOUNT") + return "pat=" + u +} + // GetAWSAccountId retrieves the AWS Account ID from the environment and returns it as a string. func GetAWSAccountId() string { awsAccountId := os.Getenv("AWS_ACCOUNT_ID")