diff --git a/pkg/cli/cmd/run/run.go b/pkg/cli/cmd/run/run.go index 95627c209f..34a9aa3ec5 100644 --- a/pkg/cli/cmd/run/run.go +++ b/pkg/cli/cmd/run/run.go @@ -11,6 +11,7 @@ import ( "fmt" "os" + "github.com/Azure/go-autorest/autorest/to" "github.com/fatih/color" "github.com/project-radius/radius/pkg/cli" "github.com/project-radius/radius/pkg/cli/cmd/commonflags" @@ -114,17 +115,21 @@ func (r *Runner) Run(ctx context.Context) error { return nil } - // We don't expect an error here because we already deployed to the environment - environment, err := client.GetEnvDetails(ctx, r.EnvironmentName) + app, err := client.ShowApplication(ctx, r.ApplicationName) if err != nil { return err } namespace := "" - switch compute := environment.Properties.Compute.(type) { - case *v20220315privatepreview.KubernetesCompute: - namespace = *compute.Namespace - default: + appStatus := app.Properties.Status + if appStatus != nil && appStatus.Compute != nil { + kube, ok := appStatus.Compute.(*v20220315privatepreview.KubernetesCompute) + if ok && kube.Namespace != nil { + namespace = to.String(kube.Namespace) + } + } + + if namespace == "" { return &cli.FriendlyError{Message: "Only kubernetes runtimes are supported."} } diff --git a/pkg/cli/cmd/run/run_test.go b/pkg/cli/cmd/run/run_test.go index bfe7e5623f..ad39ccdef0 100644 --- a/pkg/cli/cmd/run/run_test.go +++ b/pkg/cli/cmd/run/run_test.go @@ -175,24 +175,26 @@ func Test_Run(t *testing.T) { }). Times(1) - environment := v20220315privatepreview.EnvironmentResource{ - Properties: &v20220315privatepreview.EnvironmentProperties{ - Compute: &v20220315privatepreview.KubernetesCompute{ - Kind: to.Ptr(v20220315privatepreview.EnvironmentComputeKindKubernetes), - Namespace: to.Ptr("test-namespace"), + app := v20220315privatepreview.ApplicationResource{ + Properties: &v20220315privatepreview.ApplicationProperties{ + Status: &v20220315privatepreview.ResourceStatus{ + Compute: &v20220315privatepreview.KubernetesCompute{ + Kind: to.Ptr("kubernetes"), + Namespace: to.Ptr("test-namespace-app"), + }, }, }, } clientMock := clients.NewMockApplicationsManagementClient(ctrl) - clientMock.EXPECT(). - GetEnvDetails(gomock.Any(), radcli.TestEnvironmentName). - Return(environment, nil). - Times(1) clientMock.EXPECT(). CreateApplicationIfNotFound(gomock.Any(), "test-application", gomock.Any()). Return(nil). Times(1) + clientMock.EXPECT(). + ShowApplication(gomock.Any(), "test-application"). + Return(app, nil). + Times(1) workspace := &workspaces.Workspace{ Connection: map[string]interface{}{ @@ -242,13 +244,13 @@ func Test_Run(t *testing.T) { // Logstream is scoped to application and namespace require.Equal(t, runner.ApplicationName, logStreamOptions.ApplicationName) require.Equal(t, "kind-kind", logStreamOptions.KubeContext) - require.Equal(t, "test-namespace", logStreamOptions.Namespace) + require.Equal(t, "test-namespace-app", logStreamOptions.Namespace) portforwardOptions := <-portforwardOptionsChan // Port-forward is scoped to application and namespace require.Equal(t, runner.ApplicationName, portforwardOptions.ApplicationName) require.Equal(t, "kind-kind", portforwardOptions.KubeContext) - require.Equal(t, "test-namespace", portforwardOptions.Namespace) + require.Equal(t, "test-namespace-app", portforwardOptions.Namespace) // Shut down the log stream and verify result cancel() diff --git a/pkg/cli/cmd/workspace/create/create.go b/pkg/cli/cmd/workspace/create/create.go index dbd68522fe..4656727240 100644 --- a/pkg/cli/cmd/workspace/create/create.go +++ b/pkg/cli/cmd/workspace/create/create.go @@ -38,7 +38,7 @@ Workspaces allow you to manage multiple Radius platforms and environments using You can easily define and switch between workspaces to deploy and manage applications across local, test, and production environments.`, Args: ValidateArgs(), Example: ` -# Create a workspace with name 'myworkspace' and kuberentes context 'aks' +# Create a workspace with name 'myworkspace' and kubernetes context 'aks' rad workspace create kubernetes myworkspace --context aks # Create a workspace with name of current kubernetes context in current kubernetes context rad workspace create kubernetes`, diff --git a/pkg/cli/deployment/diagnostics.go b/pkg/cli/deployment/diagnostics.go index 12fcf4bb94..ed1f2e2735 100644 --- a/pkg/cli/deployment/diagnostics.go +++ b/pkg/cli/deployment/diagnostics.go @@ -158,47 +158,37 @@ func (dc *ARMDiagnosticsClient) findNamespaceOfContainer(ctx context.Context, re return "", fmt.Errorf("could not namespace for container %q:%w", resourceName, err) } - obj, ok = applicationResponse.Properties["environment"] + obj, ok = applicationResponse.Properties["status"] if !ok { return "", fmt.Errorf("could not find namespace for container %q", resourceName) } - environment, ok := obj.(string) + status, ok := obj.(map[string]any) if !ok { return "", fmt.Errorf("could not find namespace for container %q", resourceName) } - id, err = resources.ParseResource(environment) - if err != nil { - return "", fmt.Errorf("could not namespace for container %q:%w", resourceName, err) - } - - environmentResponse, err := dc.EnvironmentClient.Get(ctx, id.Name(), nil) - if err != nil { - return "", fmt.Errorf("could not namespace for container %q:%w", resourceName, err) - } - - obj, ok = environmentResponse.Properties["compute"] + obj, ok = status["compute"] if !ok { return "", fmt.Errorf("could not find namespace for container %q", resourceName) } - compute, ok := obj.(map[string]interface{}) + compute, ok := obj.(map[string]any) if !ok { return "", fmt.Errorf("could not find namespace for container %q", resourceName) } - obj, ok = compute["namespace"] - if !ok { + kind, ok := compute["kind"].(string) + if !ok || !strings.EqualFold(kind, "kubernetes") { return "", fmt.Errorf("could not find namespace for container %q", resourceName) } - namespace, ok := obj.(string) - if !ok { - return "", fmt.Errorf("could not find namespace for container %q", resourceName) + namespace, ok := compute["namespace"].(string) + if ok { + return namespace, nil } - return namespace, nil + return "", fmt.Errorf("could not find namespace for container %q", resourceName) } // Note: If an error is returned, any streams that were created before the error will also be returned. diff --git a/pkg/corerp/api/v20220315privatepreview/application_conversion.go b/pkg/corerp/api/v20220315privatepreview/application_conversion.go index b233532cb1..bedd6f592f 100644 --- a/pkg/corerp/api/v20220315privatepreview/application_conversion.go +++ b/pkg/corerp/api/v20220315privatepreview/application_conversion.go @@ -91,7 +91,7 @@ func (dst *ApplicationResource) ConvertFrom(src conv.DataModelInterface) error { func fromAppExtensionClassificationDataModel(e datamodel.Extension) ApplicationExtensionClassification { switch e.Kind { case datamodel.KubernetesMetadata: - var ann, lbl = getFromExtensionClassificationFields(e) + var ann, lbl = fromExtensionClassificationFields(e) return &ApplicationKubernetesMetadataExtension{ Kind: to.StringPtr(string(e.Kind)), Annotations: *to.StringMapPtr(ann), diff --git a/pkg/corerp/api/v20220315privatepreview/container_conversion.go b/pkg/corerp/api/v20220315privatepreview/container_conversion.go index 89a2f8eb92..7ca5a95142 100644 --- a/pkg/corerp/api/v20220315privatepreview/container_conversion.go +++ b/pkg/corerp/api/v20220315privatepreview/container_conversion.go @@ -506,7 +506,7 @@ func fromExtensionClassificationDataModel(e datamodel.Extension) ContainerExtens Provides: to.StringPtr(e.DaprSidecar.Provides), } case datamodel.KubernetesMetadata: - var ann, lbl = getFromExtensionClassificationFields(e) + var ann, lbl = fromExtensionClassificationFields(e) return &ContainerKubernetesMetadataExtension{ Kind: to.StringPtr(string(e.Kind)), Annotations: *to.StringMapPtr(ann), @@ -532,7 +532,7 @@ func toVolumeBaseDataModel(v Volume) datamodel.VolumeBase { } } -func getFromExtensionClassificationFields(e datamodel.Extension) (map[string]string, map[string]string) { +func fromExtensionClassificationFields(e datamodel.Extension) (map[string]string, map[string]string) { var ann map[string]string var lbl map[string]string diff --git a/pkg/corerp/api/v20220315privatepreview/environment_conversion.go b/pkg/corerp/api/v20220315privatepreview/environment_conversion.go index 55c7299215..43ad4d0096 100644 --- a/pkg/corerp/api/v20220315privatepreview/environment_conversion.go +++ b/pkg/corerp/api/v20220315privatepreview/environment_conversion.go @@ -223,10 +223,9 @@ func fromEnvironmentComputeKind(kind rp.EnvironmentComputeKind) *string { // fromExtensionClassificationEnvDataModel: Converts from base datamodel to versioned datamodel func fromEnvExtensionClassificationDataModel(e datamodel.Extension) EnvironmentExtensionClassification { - switch e.Kind { case datamodel.KubernetesMetadata: - var ann, lbl = getFromExtensionClassificationFields(e) + var ann, lbl = fromExtensionClassificationFields(e) return &EnvironmentKubernetesMetadataExtension{ Kind: to.StringPtr(string(e.Kind)), Annotations: *to.StringMapPtr(ann), diff --git a/pkg/corerp/backend/deployment/deployment_test.go b/pkg/corerp/backend/deployment/deployment_test.go index 4e0816d00c..8761c20367 100644 --- a/pkg/corerp/backend/deployment/deployment_test.go +++ b/pkg/corerp/backend/deployment/deployment_test.go @@ -717,7 +717,7 @@ func Test_Render(t *testing.T) { _, err := dp.Render(ctx, resourceID, &testResource) require.Error(t, err) require.Equal(t, v1.CodeInvalid, err.(*conv.ErrClientRP).Code) - require.Equal(t, "linked application ID \"/subscriptions/test-subscription/resourceGroups/test-resource-group/providers/Applications.Core/app/test-application\" for resource \"/subscriptions/test-subscription/resourceGroups/test-resource-group/providers/Applications.Core/containers/test-resource\" has invalid application resource type.", err.(*conv.ErrClientRP).Message) + require.Equal(t, "linked \"/subscriptions/test-subscription/resourceGroups/test-resource-group/providers/Applications.Core/app/test-application\" has invalid Applications.Core/applications resource type.", err.(*conv.ErrClientRP).Message) }) t.Run("Missing output resource provider", func(t *testing.T) { diff --git a/pkg/corerp/backend/deployment/deploymentprocessor.go b/pkg/corerp/backend/deployment/deploymentprocessor.go index a7764280cb..c74386f5a3 100644 --- a/pkg/corerp/backend/deployment/deploymentprocessor.go +++ b/pkg/corerp/backend/deployment/deploymentprocessor.go @@ -39,6 +39,7 @@ import ( "github.com/project-radius/radius/pkg/resourcemodel" "github.com/project-radius/radius/pkg/rp" "github.com/project-radius/radius/pkg/rp/outputresource" + rp_util "github.com/project-radius/radius/pkg/rp/util" "github.com/project-radius/radius/pkg/ucp/dataprovider" "github.com/project-radius/radius/pkg/ucp/resources" "github.com/project-radius/radius/pkg/ucp/store" @@ -79,7 +80,7 @@ type ResourceData struct { OutputResources []outputresource.OutputResource ComputedValues map[string]interface{} SecretValues map[string]rp.SecretValueReference - AppID resources.ID // Application ID for which the resource is created + AppID *resources.ID // Application ID for which the resource is created RecipeData link_dm.RecipeData // Relevant only for links created with recipes to find relevant connections created by that recipe } @@ -99,12 +100,14 @@ func (dp *deploymentProcessor) Render(ctx context.Context, resourceID resources. return renderers.RendererOutput{}, err } // 2. fetch the application properties from the DB - appProperties, err := dp.getApplicationProperties(ctx, res.AppID, resourceID.String()) + app := &corerp_dm.Application{} + err = rp_util.FetchScopeResource(ctx, dp.sp, res.AppID.String(), app) if err != nil { return renderers.RendererOutput{}, err } // 3. fetch the environment resource from the db to get the Namespace - env, err := dp.fetchEnvironment(ctx, appProperties.BasicResourceProperties.Environment, resourceID) + env := &corerp_dm.Environment{} + err = rp_util.FetchScopeResource(ctx, dp.sp, app.Properties.Environment, env) if err != nil { return renderers.RendererOutput{}, err } @@ -125,7 +128,13 @@ func (dp *deploymentProcessor) Render(ctx context.Context, resourceID resources. return renderers.RendererOutput{}, err } - appOptions, err := dp.getAppOptions(ctx, appProperties) + c := app.Properties.Status.Compute + // Override environment-scope namespace with application-scope kubernetes namespace. + if c != nil && c.Kind == rp.KubernetesComputeKind { + envOptions.Namespace = c.KubernetesCompute.Namespace + } + + appOptions, err := dp.getAppOptions(ctx, &app.Properties) if err != nil { return renderers.RendererOutput{}, err } @@ -573,16 +582,16 @@ func (dp *deploymentProcessor) getResourceDataByID(ctx context.Context, resource } func (dp *deploymentProcessor) buildResourceDependency(resourceID resources.ID, applicationID string, resource conv.DataModelInterface, outputResources []outputresource.OutputResource, computedValues map[string]interface{}, secretValues map[string]rp.SecretValueReference, recipeData link_dm.RecipeData) (ResourceData, error) { - var appID resources.ID + var appID *resources.ID if applicationID != "" { parsedID, err := resources.ParseResource(applicationID) if err != nil { return ResourceData{}, conv.NewClientErrInvalidRequest(fmt.Sprintf("application ID %q for the resource %q is not a valid id. Error: %s", applicationID, resourceID.String(), err.Error())) } - appID = parsedID + appID = &parsedID } else if strings.EqualFold(resourceID.ProviderNamespace(), resources.LinkRPNamespace) { // Application id is optional for link resource types - appID = resources.ID{} + appID = nil } else { return ResourceData{}, fmt.Errorf("missing required application id for the resource %q", resourceID.String()) } @@ -632,69 +641,3 @@ func (dp *deploymentProcessor) getRendererDependency(ctx context.Context, depend return rendererDependency, nil } - -// getApplicationProperties returns application properties linked to the application fetched from the db -func (dp *deploymentProcessor) getApplicationProperties(ctx context.Context, appID resources.ID, resourceID string) (*corerp_dm.ApplicationProperties, error) { - errMsg := "failed to fetch the application %q for the resource %q. Err: %w" - - appIDType := appID.Type() - app := &corerp_dm.Application{} - if !strings.EqualFold(appIDType, app.ResourceTypeName()) { - return nil, conv.NewClientErrInvalidRequest(fmt.Sprintf("linked application ID %q for resource %q has invalid application resource type.", appID.String(), resourceID)) - } - - sc, err := dp.sp.GetStorageClient(ctx, appIDType) - if err != nil { - return nil, fmt.Errorf(errMsg, appID.String(), resourceID, err) - } - - res, err := sc.Get(ctx, appID.String()) - if err != nil { - if errors.Is(&store.ErrNotFound{}, err) { - return nil, conv.NewClientErrInvalidRequest(fmt.Sprintf("linked application %q for resource %q does not exist", appID.String(), resourceID)) - } - return nil, fmt.Errorf(errMsg, appID.String(), resourceID, err) - } - err = res.As(app) - if err != nil { - return nil, fmt.Errorf(errMsg, appID.String(), resourceID, err) - } - - return &app.Properties, nil -} - -// fetchEnvironment fetches the environment resource from the db for getting the namespace to deploy the resources -func (dp *deploymentProcessor) fetchEnvironment(ctx context.Context, environmentID string, resourceID resources.ID) (*corerp_dm.Environment, error) { - envId, err := resources.ParseResource(environmentID) - if err != nil { - return nil, err - } - - env := &corerp_dm.Environment{} - - if !strings.EqualFold(envId.Type(), env.ResourceTypeName()) { - return nil, conv.NewClientErrInvalidRequest(fmt.Sprintf("environment id %q linked to the application for resource %s is not a valid environment type. Error: %s", envId.Type(), resourceID, err.Error())) - } - - sc, err := dp.sp.GetStorageClient(ctx, envId.Type()) - if err != nil { - return nil, err - } - - const errMsg = "failed to fetch the environment %q for the resource %q. Error: %w" - - res, err := sc.Get(ctx, envId.String()) - if err != nil { - if errors.Is(&store.ErrNotFound{}, err) { - return nil, conv.NewClientErrInvalidRequest(fmt.Sprintf("linked environment %q for resource %s does not exist", environmentID, resourceID)) - } - return nil, fmt.Errorf(errMsg, environmentID, resourceID, err) - } - - err = res.As(env) - if err != nil { - return nil, fmt.Errorf(errMsg, environmentID, resourceID, err) - } - - return env, nil -} diff --git a/pkg/corerp/frontend/controller/applications/createorupdateapplication.go b/pkg/corerp/frontend/controller/applications/createorupdateapplication.go index 9280d32cd3..f8ca4fedd4 100644 --- a/pkg/corerp/frontend/controller/applications/createorupdateapplication.go +++ b/pkg/corerp/frontend/controller/applications/createorupdateapplication.go @@ -21,6 +21,11 @@ import ( rp_frontend "github.com/project-radius/radius/pkg/rp/frontend" rp_kube "github.com/project-radius/radius/pkg/rp/kube" "github.com/project-radius/radius/pkg/ucp/resources" + + "github.com/go-logr/logr" + corev1 "k8s.io/api/core/v1" + apierrors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) var _ ctrl.Controller = (*CreateOrUpdateApplication)(nil) @@ -57,6 +62,8 @@ func NewCreateOrUpdateApplication(opts ctrl.Options) (ctrl.Controller, error) { // +-----------------+--------------------+-------------------------------+-------------------------------+ func (a *CreateOrUpdateApplication) populateKubernetesNamespace(ctx context.Context, old, newResource *datamodel.Application) (rest.Response, error) { + logger := logr.FromContextOrDiscard(ctx) + serviceCtx := v1.ARMRequestContextFromContext(ctx) kubeNamespace := "" @@ -105,10 +112,9 @@ func (a *CreateOrUpdateApplication) populateKubernetesNamespace(ctx context.Cont } } - // TODO: Enable this in https://github.com/project-radius/radius/pull/4783 - // if !kubernetes.IsValidObjectName(kubeNamespace) { - // return rest.NewBadRequestResponse(fmt.Sprintf("'%s' is the invalid namespace. This must be at most 63 alphanumeric characters or '-'. Please specify the valid namespace in properties.extensions[*].kubernetesNamespaceOverride.", kubeNamespace)), nil - // } + if !kubernetes.IsValidObjectName(kubeNamespace) { + return rest.NewBadRequestResponse(fmt.Sprintf("'%s' is the invalid namespace. This must be at most 63 alphanumeric characters or '-'. Please specify a valid namespace using 'kubernetesNamespace' extension in '$.properties.extensions[*]'.", kubeNamespace)), nil + } // Populate kubernetes namespace to internal metadata property for query indexing. newResource.Properties.Status.Compute = &rp.EnvironmentCompute{ @@ -116,6 +122,16 @@ func (a *CreateOrUpdateApplication) populateKubernetesNamespace(ctx context.Cont KubernetesCompute: rp.KubernetesComputeProperties{Namespace: kubeNamespace}, } + // TODO: Move it to backend controller - https://github.com/project-radius/radius/issues/4742 + err = a.KubeClient().Create(ctx, &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: kubeNamespace}}) + if apierrors.IsAlreadyExists(err) { + logger.Info("Using existing namespace", "namespace", kubeNamespace) + } else if err != nil { + return nil, err + } else { + logger.Info("Created the namespace", "namespace", kubeNamespace) + } + return nil, nil } diff --git a/pkg/corerp/frontend/controller/applications/createorupdateapplication_test.go b/pkg/corerp/frontend/controller/applications/createorupdateapplication_test.go index 4d28c40fee..1306c05aab 100644 --- a/pkg/corerp/frontend/controller/applications/createorupdateapplication_test.go +++ b/pkg/corerp/frontend/controller/applications/createorupdateapplication_test.go @@ -552,7 +552,6 @@ func TestPopulateKubernetesNamespace_invalid_property(t *testing.T) { appCtrl := ctl.(*CreateOrUpdateApplication) t.Run("invalid namespace", func(t *testing.T) { - t.SkipNow() tCtx.MockSC.EXPECT(). Query(gomock.Any(), gomock.Any()). DoAndReturn(func(ctx context.Context, query store.Query, options ...store.QueryOptions) (*store.ObjectQueryResult, error) { @@ -583,7 +582,7 @@ func TestPopulateKubernetesNamespace_invalid_property(t *testing.T) { resp, err := appCtrl.populateKubernetesNamespace(ctx, nil, newResource) require.NoError(t, err) res := resp.(*rest.BadRequestResponse) - require.Equal(t, res.Body.Error.Message, "'invalid-nameinvalid-nameinvalid-nameinvalid-nameinvalid-nameinvalid-name' is the invalid namespace. This must be at most 63 alphanumeric characters or '-'. Please specify the valid namespace in properties.extensions[*].kubernetesNamespaceOverride.") + require.Equal(t, res.Body.Error.Message, "'invalid-nameinvalid-nameinvalid-nameinvalid-nameinvalid-nameinvalid-name' is the invalid namespace. This must be at most 63 alphanumeric characters or '-'. Please specify a valid namespace using 'kubernetesNamespace' extension in '$.properties.extensions[*]'.") }) t.Run("conflicted namespace in environment resource", func(t *testing.T) { diff --git a/pkg/linkrp/frontend/deployment/deploymentprocessor.go b/pkg/linkrp/frontend/deployment/deploymentprocessor.go index cf6c0e0e65..b2d4a02149 100644 --- a/pkg/linkrp/frontend/deployment/deploymentprocessor.go +++ b/pkg/linkrp/frontend/deployment/deploymentprocessor.go @@ -30,9 +30,9 @@ import ( "github.com/project-radius/radius/pkg/resourcemodel" "github.com/project-radius/radius/pkg/rp" "github.com/project-radius/radius/pkg/rp/outputresource" + rp_util "github.com/project-radius/radius/pkg/rp/util" "github.com/project-radius/radius/pkg/ucp/dataprovider" "github.com/project-radius/radius/pkg/ucp/resources" - "github.com/project-radius/radius/pkg/ucp/store" "sigs.k8s.io/controller-runtime/pkg/client" ) @@ -91,19 +91,32 @@ func (dp *deploymentProcessor) Render(ctx context.Context, id resources.ID, reso } // fetch the environment ID and recipe name from the resource - env, recipe, err := dp.getMetadataFromResource(ctx, id, resource) + basicResource, recipe, err := dp.getMetadataFromResource(ctx, id, resource) if err != nil { return renderers.RendererOutput{}, err } // Fetch the environment namespace, recipe link type and recipe template path by doing a db lookup - envMetadata, err := dp.getEnvironmentMetadata(ctx, env, recipe.Name) + envMetadata, err := dp.getEnvironmentMetadata(ctx, basicResource.Environment, recipe.Name) if err != nil { return renderers.RendererOutput{}, err } + kubeNamespace := envMetadata.Namespace + // Override environment-scope namespace with application-scope kubernetes namespace. + if basicResource.Application != "" { + app := &coreDatamodel.Application{} + if err := rp_util.FetchScopeResource(ctx, dp.sp, basicResource.Application, app); err != nil { + return renderers.RendererOutput{}, err + } + c := app.Properties.Status.Compute + if c != nil && c.Kind == rp.KubernetesComputeKind { + kubeNamespace = c.KubernetesCompute.Namespace + } + } + rendererOutput, err := renderer.Render(ctx, resource, renderers.RenderOptions{ - Namespace: envMetadata.Namespace, + Namespace: kubeNamespace, RecipeProperties: datamodel.RecipeProperties{ LinkRecipe: recipe, LinkType: envMetadata.RecipeLinkType, @@ -345,105 +358,85 @@ func (dp *deploymentProcessor) fetchSecret(ctx context.Context, outputResources } // getMetadataFromResource returns the environment id and the recipe name to look up environment metadata -func (dp *deploymentProcessor) getMetadataFromResource(ctx context.Context, resourceID resources.ID, resource conv.DataModelInterface) (envId string, recipe datamodel.LinkRecipe, err error) { +func (dp *deploymentProcessor) getMetadataFromResource(ctx context.Context, resourceID resources.ID, resource conv.DataModelInterface) (basicResource *rp.BasicResourceProperties, recipe datamodel.LinkRecipe, err error) { resourceType := strings.ToLower(resourceID.Type()) switch resourceType { case strings.ToLower(mongodatabases.ResourceType): obj := resource.(*datamodel.MongoDatabase) - envId = obj.Properties.Environment + basicResource = &obj.Properties.BasicResourceProperties if obj.Properties.Mode == datamodel.LinkModeRecipe { recipe.Name = obj.Properties.Recipe.Name recipe.Parameters = obj.Properties.Recipe.Parameters } case strings.ToLower(sqldatabases.ResourceType): obj := resource.(*datamodel.SqlDatabase) - envId = obj.Properties.Environment + basicResource = &obj.Properties.BasicResourceProperties if obj.Properties.Mode == datamodel.LinkModeRecipe { recipe.Name = obj.Properties.Recipe.Name recipe.Parameters = obj.Properties.Recipe.Parameters } case strings.ToLower(rediscaches.ResourceType): obj := resource.(*datamodel.RedisCache) - envId = obj.Properties.Environment + basicResource = &obj.Properties.BasicResourceProperties if obj.Properties.Mode == datamodel.LinkModeRecipe { recipe.Name = obj.Properties.Recipe.Name recipe.Parameters = obj.Properties.Recipe.Parameters } case strings.ToLower(rabbitmqmessagequeues.ResourceType): obj := resource.(*datamodel.RabbitMQMessageQueue) - envId = obj.Properties.Environment + basicResource = &obj.Properties.BasicResourceProperties if obj.Properties.Mode == datamodel.LinkModeRecipe { recipe.Name = obj.Properties.Recipe.Name recipe.Parameters = obj.Properties.Recipe.Parameters } case strings.ToLower(extenders.ResourceType): obj := resource.(*datamodel.Extender) - envId = obj.Properties.Environment + basicResource = &obj.Properties.BasicResourceProperties case strings.ToLower(daprstatestores.ResourceType): obj := resource.(*datamodel.DaprStateStore) - envId = obj.Properties.Environment + basicResource = &obj.Properties.BasicResourceProperties if obj.Properties.Mode == datamodel.LinkModeRecipe { recipe.Name = obj.Properties.Recipe.Name recipe.Parameters = obj.Properties.Recipe.Parameters } case strings.ToLower(daprsecretstores.ResourceType): obj := resource.(*datamodel.DaprSecretStore) - envId = obj.Properties.Environment + basicResource = &obj.Properties.BasicResourceProperties if obj.Properties.Mode == datamodel.LinkModeRecipe { recipe.Name = obj.Properties.Recipe.Name recipe.Parameters = obj.Properties.Recipe.Parameters } case strings.ToLower(daprpubsubbrokers.ResourceType): obj := resource.(*datamodel.DaprPubSubBroker) - envId = obj.Properties.Environment + basicResource = &obj.Properties.BasicResourceProperties if obj.Properties.Mode == datamodel.LinkModeRecipe { recipe.Name = obj.Properties.Recipe.Name recipe.Parameters = obj.Properties.Recipe.Parameters } case strings.ToLower(daprinvokehttproutes.ResourceType): obj := resource.(*datamodel.DaprInvokeHttpRoute) - envId = obj.Properties.Environment + basicResource = &obj.Properties.BasicResourceProperties if obj.Properties.Recipe.Name != "" { recipe.Name = obj.Properties.Recipe.Name recipe.Parameters = obj.Properties.Recipe.Parameters } default: // Internal error: this shouldn't happen unless a new supported resource type wasn't added here - return "", recipe, fmt.Errorf("unsupported resource type: %q for resource ID: %q", resourceType, resourceID.String()) + err = fmt.Errorf("unsupported resource type: %q for resource ID: %q", resourceType, resourceID.String()) + basicResource = nil + return } - - return envId, recipe, nil + return } // getEnvironmentMetadata fetches the environment resource from the db to retrieve namespace and recipe metadata required to deploy the link and linked resources ``` func (dp *deploymentProcessor) getEnvironmentMetadata(ctx context.Context, environmentID string, recipeName string) (envMetadata EnvironmentMetadata, err error) { - envId, err := resources.ParseResource(environmentID) - envMetadata = EnvironmentMetadata{} - if err != nil { - return envMetadata, conv.NewClientErrInvalidRequest(fmt.Sprintf("provided environment id %q is not a valid id.", environmentID)) - } - env := &coreDatamodel.Environment{} - if !strings.EqualFold(envId.Type(), env.ResourceTypeName()) { - return envMetadata, conv.NewClientErrInvalidRequest(fmt.Sprintf("provided environment id type %q is not a valid type.", envId.Type())) - } - - sc, err := dp.sp.GetStorageClient(ctx, envId.Type()) - if err != nil { - return - } - res, err := sc.Get(ctx, environmentID) - if err != nil { - if errors.Is(&store.ErrNotFound{}, err) { - return envMetadata, conv.NewClientErrInvalidRequest(fmt.Sprintf("environment %q does not exist", environmentID)) - } - return - } - err = res.As(env) - if err != nil { + if err = rp_util.FetchScopeResource(ctx, dp.sp, environmentID, env); err != nil { return } + envMetadata = EnvironmentMetadata{} if env.Properties.Compute != (rp.EnvironmentCompute{}) && env.Properties.Compute.KubernetesCompute != (rp.KubernetesComputeProperties{}) { envMetadata.Namespace = env.Properties.Compute.KubernetesCompute.Namespace } else { diff --git a/pkg/linkrp/frontend/deployment/deploymentprocessor_test.go b/pkg/linkrp/frontend/deployment/deploymentprocessor_test.go index 0b8ddd83ce..99ab88fe88 100644 --- a/pkg/linkrp/frontend/deployment/deploymentprocessor_test.go +++ b/pkg/linkrp/frontend/deployment/deploymentprocessor_test.go @@ -245,7 +245,40 @@ func buildOutputResourcesDapr(mode string) []outputresource.OutputResource { } } -func buildEnvironmentResource(recipeName string, providers *corerpDatamodel.Providers) store.Object { +func buildApplicationResource(namespace string) *store.Object { + if namespace == "" { + namespace = "radius-test" + } + + app := corerpDatamodel.Application{ + BaseResource: v1.BaseResource{ + TrackedResource: v1.TrackedResource{ + ID: applicationID, + }, + }, + Properties: corerpDatamodel.ApplicationProperties{ + BasicResourceProperties: rp.BasicResourceProperties{ + Status: rp.ResourceStatus{ + Compute: &rp.EnvironmentCompute{ + Kind: rp.KubernetesComputeKind, + KubernetesCompute: rp.KubernetesComputeProperties{ + Namespace: namespace, + }, + }, + }, + }, + }, + } + + return &store.Object{ + Metadata: store.Metadata{ + ID: app.ID, + }, + Data: app, + } +} + +func buildEnvironmentResource(recipeName string, providers *corerpDatamodel.Providers) *store.Object { environment := corerpDatamodel.Environment{ BaseResource: v1.BaseResource{ TrackedResource: v1.TrackedResource{ @@ -271,13 +304,13 @@ func buildEnvironmentResource(recipeName string, providers *corerpDatamodel.Prov if providers != nil { environment.Properties.Providers = *providers } - er := store.Object{ + + return &store.Object{ Metadata: store.Metadata{ ID: environment.ID, }, Data: environment, } - return er } type SharedMocks struct { @@ -372,14 +405,15 @@ func Test_Render(t *testing.T) { ctrl := gomock.NewController(t) mockRecipeHandler := handlers.NewMockRecipeHandler(ctrl) dp := deploymentProcessor{mocks.model, mocks.dbProvider, mocks.secretsValueClient, nil} + t.Run("verify render success", func(t *testing.T) { testResource := buildInputResourceMongo(modeResource) testRendererOutput := buildRendererOutputMongo(modeResource) mocks.renderer.EXPECT().Render(gomock.Any(), gomock.Any(), gomock.Any()).Times(1).Return(testRendererOutput, nil) - mocks.dbProvider.EXPECT().GetStorageClient(gomock.Any(), gomock.Any()).Times(1).Return(mocks.db, nil) - er := buildEnvironmentResource("", nil) - mocks.db.EXPECT().Get(gomock.Any(), gomock.Any()).Times(1).Return(&er, nil) + mocks.dbProvider.EXPECT().GetStorageClient(gomock.Any(), gomock.Any()).Times(2).Return(mocks.db, nil) + mocks.db.EXPECT().Get(gomock.Any(), gomock.Any()).Times(1).Return(buildEnvironmentResource("", nil), nil) + mocks.db.EXPECT().Get(gomock.Any(), gomock.Any()).Times(1).Return(buildApplicationResource(""), nil) rendererOutput, err := dp.Render(ctx, mongoLinkResourceID, &testResource) require.NoError(t, err) @@ -392,9 +426,10 @@ func Test_Render(t *testing.T) { testResource := buildInputResourceMongo(modeRecipe) testRendererOutput := buildRendererOutputMongo(modeRecipe) mocks.renderer.EXPECT().Render(gomock.Any(), gomock.Any(), gomock.Any()).Times(1).Return(testRendererOutput, nil) - mocks.dbProvider.EXPECT().GetStorageClient(gomock.Any(), gomock.Any()).Times(1).Return(mocks.db, nil) + mocks.dbProvider.EXPECT().GetStorageClient(gomock.Any(), gomock.Any()).Times(2).Return(mocks.db, nil) er := buildEnvironmentResource(recipeName, &corerpDatamodel.Providers{Azure: corerpDatamodel.ProvidersAzure{Scope: "/subscriptions/testSub/resourceGroups/testGroup"}}) - mocks.db.EXPECT().Get(gomock.Any(), gomock.Any()).Times(1).Return(&er, nil) + mocks.db.EXPECT().Get(gomock.Any(), gomock.Any()).Times(1).Return(er, nil) + mocks.db.EXPECT().Get(gomock.Any(), gomock.Any()).Times(1).Return(buildApplicationResource(""), nil) rendererOutput, err := dp.Render(ctx, mongoLinkResourceID, &testResource) require.NoError(t, err) @@ -412,9 +447,10 @@ func Test_Render(t *testing.T) { testRendererOutput := buildRendererOutputMongo(modeResource) mocks.renderer.EXPECT().Render(gomock.Any(), gomock.Any(), gomock.Any()).Times(1).Return(testRendererOutput, nil) - mocks.dbProvider.EXPECT().GetStorageClient(gomock.Any(), gomock.Any()).Times(1).Return(mocks.db, nil) + mocks.dbProvider.EXPECT().GetStorageClient(gomock.Any(), gomock.Any()).Times(2).Return(mocks.db, nil) er := buildEnvironmentResource("", nil) - mocks.db.EXPECT().Get(gomock.Any(), gomock.Any()).Times(1).Return(&er, nil) + mocks.db.EXPECT().Get(gomock.Any(), gomock.Any()).Times(1).Return(er, nil) + mocks.db.EXPECT().Get(gomock.Any(), gomock.Any()).Times(1).Return(buildApplicationResource(""), nil) rendererOutput, err := dp.Render(ctx, mongoLinkResourceID, &testResource) require.NoError(t, err) @@ -440,14 +476,15 @@ func Test_Render(t *testing.T) { _, err := dp.Render(ctx, mongoLinkResourceID, &resource) require.Error(t, err) require.Equal(t, v1.CodeInvalid, err.(*conv.ErrClientRP).Code) - require.Equal(t, "provided environment id \"invalid-id\" is not a valid id.", err.(*conv.ErrClientRP).Message) + require.Equal(t, "invalid-id is not a valid resource id for Applications.Core/environments.", err.(*conv.ErrClientRP).Message) }) t.Run("verify render error", func(t *testing.T) { mocks.renderer.EXPECT().Render(gomock.Any(), gomock.Any(), gomock.Any()).Times(1).Return(renderers.RendererOutput{}, errors.New("failed to render the resource")) - mocks.dbProvider.EXPECT().GetStorageClient(gomock.Any(), gomock.Any()).Times(1).Return(mocks.db, nil) + mocks.dbProvider.EXPECT().GetStorageClient(gomock.Any(), gomock.Any()).Times(2).Return(mocks.db, nil) er := buildEnvironmentResource("", nil) - mocks.db.EXPECT().Get(gomock.Any(), gomock.Any()).Times(1).Return(&er, nil) + mocks.db.EXPECT().Get(gomock.Any(), gomock.Any()).Times(1).Return(er, nil) + mocks.db.EXPECT().Get(gomock.Any(), gomock.Any()).Times(1).Return(buildApplicationResource(""), nil) testResource := buildInputResourceMongo(modeResource) @@ -502,7 +539,7 @@ func Test_Render(t *testing.T) { _, err := dp.Render(ctx, mongoLinkResourceID, &resource) require.Error(t, err) require.Equal(t, v1.CodeInvalid, err.(*conv.ErrClientRP).Code) - require.Equal(t, "provided environment id type \"Applications.Core/env\" is not a valid type.", err.(*conv.ErrClientRP).Message) + require.Equal(t, "linked \"/subscriptions/test-sub/resourceGroups/test-group/providers/Applications.Core/env/test-env\" has invalid Applications.Core/environments resource type.", err.(*conv.ErrClientRP).Message) }) @@ -515,7 +552,7 @@ func Test_Render(t *testing.T) { _, err := dp.Render(ctx, mongoLinkResourceID, &testResource) require.Error(t, err) require.Equal(t, v1.CodeInvalid, err.(*conv.ErrClientRP).Code) - require.Equal(t, "environment \"/subscriptions/test-sub/resourceGroups/test-group/providers/Applications.Core/environments/env0\" does not exist", err.(*conv.ErrClientRP).Message) + require.Equal(t, "linked resource /subscriptions/test-sub/resourceGroups/test-group/providers/Applications.Core/environments/env0 does not exist", err.(*conv.ErrClientRP).Message) }) t.Run("Missing output resource provider", func(t *testing.T) { @@ -524,9 +561,10 @@ func Test_Render(t *testing.T) { testRendererOutput.Resources[0].ResourceType.Provider = "" mocks.renderer.EXPECT().Render(gomock.Any(), gomock.Any(), gomock.Any()).Times(1).Return(testRendererOutput, nil) - mocks.dbProvider.EXPECT().GetStorageClient(gomock.Any(), gomock.Any()).Times(1).Return(mocks.db, nil) + mocks.dbProvider.EXPECT().GetStorageClient(gomock.Any(), gomock.Any()).Times(2).Return(mocks.db, nil) er := buildEnvironmentResource("", nil) - mocks.db.EXPECT().Get(gomock.Any(), gomock.Any()).Times(1).Return(&er, nil) + mocks.db.EXPECT().Get(gomock.Any(), gomock.Any()).Times(1).Return(er, nil) + mocks.db.EXPECT().Get(gomock.Any(), gomock.Any()).Times(1).Return(buildApplicationResource(""), nil) _, err := dp.Render(ctx, mongoLinkResourceID, &testResource) require.Error(t, err) @@ -548,9 +586,10 @@ func Test_Render(t *testing.T) { } mocks.renderer.EXPECT().Render(gomock.Any(), gomock.Any(), gomock.Any()).Times(1).Return(rendererOutput, nil) - mocks.dbProvider.EXPECT().GetStorageClient(gomock.Any(), gomock.Any()).Times(1).Return(mocks.db, nil) + mocks.dbProvider.EXPECT().GetStorageClient(gomock.Any(), gomock.Any()).Times(2).Return(mocks.db, nil) er := buildEnvironmentResource("", nil) - mocks.db.EXPECT().Get(gomock.Any(), gomock.Any()).Times(1).Return(&er, nil) + mocks.db.EXPECT().Get(gomock.Any(), gomock.Any()).Times(1).Return(er, nil) + mocks.db.EXPECT().Get(gomock.Any(), gomock.Any()).Times(1).Return(buildApplicationResource(""), nil) _, err := dp.Render(ctx, mongoLinkResourceID, &testResource) require.Error(t, err) @@ -581,9 +620,10 @@ func Test_Render(t *testing.T) { testRendererOutput := buildRendererOutputMongo(modeResource) mocks.renderer.EXPECT().Render(gomock.Any(), gomock.Any(), gomock.Any()).Times(1).Return(testRendererOutput, nil) - mocks.dbProvider.EXPECT().GetStorageClient(gomock.Any(), gomock.Any()).Times(1).Return(mocks.db, nil) + mocks.dbProvider.EXPECT().GetStorageClient(gomock.Any(), gomock.Any()).Times(2).Return(mocks.db, nil) er := buildEnvironmentResource("", nil) - mocks.db.EXPECT().Get(gomock.Any(), gomock.Any()).Times(1).Return(&er, nil) + mocks.db.EXPECT().Get(gomock.Any(), gomock.Any()).Times(1).Return(er, nil) + mocks.db.EXPECT().Get(gomock.Any(), gomock.Any()).Times(1).Return(buildApplicationResource(""), nil) _, err := mockdp.Render(ctx, mongoLinkResourceID, &testResource) require.Error(t, err) @@ -1030,7 +1070,7 @@ func Test_GetEnvironmentMetadata(t *testing.T) { mocks.dbProvider.EXPECT().GetStorageClient(gomock.Any(), gomock.Any()).Times(1).Return(mocks.db, nil) er := buildEnvironmentResource(recipeName, &corerpDatamodel.Providers{Azure: corerpDatamodel.ProvidersAzure{Scope: "/subscriptions/testSub/resourceGroups/testGroup"}}) env := er.Metadata.ID - mocks.db.EXPECT().Get(gomock.Any(), gomock.Any()).Times(1).Return(&er, nil) + mocks.db.EXPECT().Get(gomock.Any(), gomock.Any()).Times(1).Return(er, nil) envMetadata, err := dp.getEnvironmentMetadata(ctx, env, recipeName) require.NoError(t, err) @@ -1043,7 +1083,7 @@ func Test_GetEnvironmentMetadata(t *testing.T) { mocks.dbProvider.EXPECT().GetStorageClient(gomock.Any(), gomock.Any()).Times(1).Return(mocks.db, nil) er := buildEnvironmentResource("cosmos-test", &corerpDatamodel.Providers{Azure: corerpDatamodel.ProvidersAzure{Scope: "/subscriptions/testSub/resourceGroups/testGroup"}}) env := er.Metadata.ID - mocks.db.EXPECT().Get(gomock.Any(), gomock.Any()).Times(1).Return(&er, nil) + mocks.db.EXPECT().Get(gomock.Any(), gomock.Any()).Times(1).Return(er, nil) _, err := dp.getEnvironmentMetadata(ctx, env, recipeName) require.Error(t, err) diff --git a/pkg/rp/util/datastore.go b/pkg/rp/util/datastore.go new file mode 100644 index 0000000000..144ab4b168 --- /dev/null +++ b/pkg/rp/util/datastore.go @@ -0,0 +1,49 @@ +// ------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +// ------------------------------------------------------------ + +package util + +import ( + "context" + "errors" + "fmt" + "strings" + + "github.com/project-radius/radius/pkg/armrpc/api/conv" + "github.com/project-radius/radius/pkg/ucp/dataprovider" + resources "github.com/project-radius/radius/pkg/ucp/resources" + "github.com/project-radius/radius/pkg/ucp/store" +) + +// FetchScopeResource fetches environment or application resource linked to resource. +func FetchScopeResource(ctx context.Context, sp dataprovider.DataStorageProvider, scopeID string, resource conv.DataModelInterface) error { + id, err := resources.ParseResource(scopeID) + if err != nil { + return conv.NewClientErrInvalidRequest(fmt.Sprintf("%s is not a valid resource id for %s.", scopeID, resource.ResourceTypeName())) + } + + if !strings.EqualFold(id.Type(), resource.ResourceTypeName()) { + return conv.NewClientErrInvalidRequest(fmt.Sprintf("linked %q has invalid %s resource type.", scopeID, resource.ResourceTypeName())) + } + sc, err := sp.GetStorageClient(ctx, id.Type()) + if err != nil { + return err + } + + res, err := sc.Get(ctx, id.String()) + if errors.Is(&store.ErrNotFound{}, err) { + return conv.NewClientErrInvalidRequest(fmt.Sprintf("linked resource %s does not exist", scopeID)) + } + if err != nil { + return fmt.Errorf("failed to fetch %s. Error: %w", scopeID, err) + } + + err = res.As(resource) + if err != nil { + return err + } + + return nil +} diff --git a/test/functional/corerp/cli/cli_test.go b/test/functional/corerp/cli/cli_test.go index 461a08eb9e..d9930a8236 100644 --- a/test/functional/corerp/cli/cli_test.go +++ b/test/functional/corerp/cli/cli_test.go @@ -65,6 +65,7 @@ func verifyRecipeCLI(ctx context.Context, t *testing.T, test corerp.CoreRPTest) }) } + func verifyCLIBasics(ctx context.Context, t *testing.T, test corerp.CoreRPTest) { options := corerp.NewCoreRPTestOptions(t) cli := radcli.NewCLI(t, options.ConfigFilePath) @@ -366,7 +367,7 @@ func Test_CLI(t *testing.T) { }, K8sObjects: &validation.K8sObjectSet{ Namespaces: map[string][]validation.K8sObject{ - "default": { + "default-kubernetes-cli": { validation.NewK8sPodForResource(name, "containera"), validation.NewK8sPodForResource(name, "containerb"), }, @@ -382,7 +383,6 @@ func Test_CLI(t *testing.T) { func Test_CLI_JSON(t *testing.T) { template := "testdata/corerp-kubernetes-cli.json" name := "kubernetes-cli-json" - requiredSecrets := map[string]map[string]string{} test := corerp.NewCoreRPTest(t, name, []corerp.TestStep{ @@ -408,7 +408,7 @@ func Test_CLI_JSON(t *testing.T) { }, K8sObjects: &validation.K8sObjectSet{ Namespaces: map[string][]validation.K8sObject{ - "default": { + "default-kubernetes-cli-json": { validation.NewK8sPodForResource(name, "containera-json"), validation.NewK8sPodForResource(name, "containerb-json"), }, @@ -448,7 +448,7 @@ func Test_CLI_Delete(t *testing.T) { validation.ValidateObjectsRunning(ctx, t, options.K8sClient, options.DynamicClient, validation.K8sObjectSet{ Namespaces: map[string][]validation.K8sObject{ - "default": { + "default-kubernetes-cli-with-resources": { validation.NewK8sPodForResource(appName, "containera-app-with-resources"), validation.NewK8sPodForResource(appName, "containerb-app-with-resources"), }, @@ -482,7 +482,7 @@ func Test_CLI_Delete(t *testing.T) { validation.ValidateObjectsRunning(ctx, t, options.K8sClient, options.DynamicClient, validation.K8sObjectSet{ Namespaces: map[string][]validation.K8sObject{ - "default": { + "default-kubernetes-cli-with-resources": { validation.NewK8sPodForResource(appName, "containera-app-with-resources"), validation.NewK8sPodForResource(appName, "containerb-app-with-resources"), }, @@ -542,7 +542,7 @@ func Test_CLI_DeploymentParameters(t *testing.T) { }, K8sObjects: &validation.K8sObjectSet{ Namespaces: map[string][]validation.K8sObject{ - "default": { + "default-kubernetes-cli-params": { validation.NewK8sPodForResource(name, "containerc"), validation.NewK8sPodForResource(name, "containerd"), }, diff --git a/test/functional/corerp/corerptest.go b/test/functional/corerp/corerptest.go index 393e52f1ee..14f48fe065 100644 --- a/test/functional/corerp/corerptest.go +++ b/test/functional/corerp/corerptest.go @@ -14,10 +14,9 @@ import ( "github.com/stretchr/testify/require" "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/api/meta" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/watch" "k8s.io/client-go/discovery" - "k8s.io/client-go/restmapper" "github.com/project-radius/radius/pkg/cli/kubernetes" "github.com/project-radius/radius/test" @@ -26,7 +25,6 @@ import ( "github.com/project-radius/radius/test/validation" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - memory "k8s.io/client-go/discovery/cached" corev1 "k8s.io/api/core/v1" ) @@ -86,27 +84,13 @@ func NewCoreRPTest(t *testing.T, name string, steps []TestStep, secrets map[stri } func (ct CoreRPTest) CreateInitialResources(ctx context.Context) error { - err := kubernetes.EnsureNamespace(ctx, ct.Options.K8sClient, ct.Name) - if err != nil { + if err := kubernetes.EnsureNamespace(ctx, ct.Options.K8sClient, ct.Name); err != nil { return fmt.Errorf("failed to create namespace %s: %w", ct.Name, err) } - restMapper := restmapper.NewDeferredDiscoveryRESTMapper(memory.NewMemCacheClient(ct.Options.K8sClient.Discovery())) for _, r := range ct.InitialResources { - mapping, err := restMapper.RESTMapping(r.GroupVersionKind().GroupKind(), r.GroupVersionKind().Version) - if err != nil { - return fmt.Errorf("unknown kind %q: %w", r.GroupVersionKind().String(), err) - } - if mapping.Scope == meta.RESTScopeNamespace { - _, err = ct.Options.DynamicClient.Resource(mapping.Resource). - Namespace(ct.Name). - Create(ctx, &r, v1.CreateOptions{}) - } else { - _, err = ct.Options.DynamicClient.Resource(mapping.Resource). - Create(ctx, &r, v1.CreateOptions{}) - } - if err != nil { - return fmt.Errorf("failed to create %q resource %#v: %w", mapping.Resource.String(), r, err) + if err := ct.Options.Client.Create(ctx, &r); err != nil { + return fmt.Errorf("failed to create resource %#v: %w", r, err) } } @@ -164,17 +148,8 @@ func (ct CoreRPTest) DeleteSecrets(ctx context.Context) error { } func (ct CoreRPTest) CleanUpExtensionResources(resources []unstructured.Unstructured) { - restMapper := restmapper.NewDeferredDiscoveryRESTMapper(memory.NewMemCacheClient(ct.Options.K8sClient.Discovery())) - for _, r := range resources { - mapping, _ := restMapper.RESTMapping(r.GroupVersionKind().GroupKind(), r.GroupVersionKind().Version) - if mapping.Scope == meta.RESTScopeNamespace { - _ = ct.Options.DynamicClient.Resource(mapping.Resource). - Namespace(r.GetNamespace()). - Delete(context.TODO(), r.GetName(), v1.DeleteOptions{}) - } else { - _ = ct.Options.DynamicClient.Resource(mapping.Resource). - Delete(context.TODO(), r.GetName(), v1.DeleteOptions{}) - } + for i := len(resources) - 1; i >= 0; i-- { + _ = ct.Options.Client.Delete(context.TODO(), &resources[i]) } } @@ -198,17 +173,30 @@ func (ct CoreRPTest) Test(t *testing.T) { // Only start capturing controller logs once. radiusControllerLogSync.Do(func() { - err := validation.SaveLogsForController(ctx, ct.Options.K8sClient, "radius-system", logPrefix) + _, err := validation.SaveContainerLogs(ctx, ct.Options.K8sClient, "radius-system", logPrefix) if err != nil { t.Errorf("failed to capture logs from radius controller: %v", err) } + }) - // Getting logs from all pods in the default namespace as well, which is where all app pods run for calls to rad deploy - err = validation.SaveLogsForController(ctx, ct.Options.K8sClient, "default", logPrefix) - if err != nil { - t.Errorf("failed to capture logs from radius controller: %v", err) + // Start pod watchers for this test. + watchers := map[string]watch.Interface{} + for _, step := range ct.Steps { + if step.K8sObjects == nil { + continue } - }) + for ns := range step.K8sObjects.Namespaces { + if _, ok := watchers[ns]; ok { + continue + } + + var err error + watchers[ns], err = validation.SaveContainerLogs(ctx, ct.Options.K8sClient, ns, logPrefix) + if err != nil { + t.Errorf("failed to capture logs from radius controller: %v", err) + } + } + } t.Logf("Creating secrets if provided") err := ct.CreateSecrets(ctx) @@ -337,5 +325,10 @@ func (ct CoreRPTest) Test(t *testing.T) { t.Logf("finished post-delete verification for %s", ct.Description) } + // Stop all watchers for the tests. + for _, watcher := range watchers { + watcher.Stop() + } + t.Logf("finished cleanup phase of %s", ct.Description) } diff --git a/test/functional/corerp/mechanics/mechanics_test.go b/test/functional/corerp/mechanics/mechanics_test.go index c5c5798abb..d2722c1866 100644 --- a/test/functional/corerp/mechanics/mechanics_test.go +++ b/test/functional/corerp/mechanics/mechanics_test.go @@ -52,6 +52,7 @@ func Test_NestedModules(t *testing.T) { func Test_RedeployWithAnotherResource(t *testing.T) { name := "corerp-mechanics-redeploy-with-another-resource" + appNamespace := "default-corerp-mechanics-redeploy-with-another-resource" templateFmt := "testdata/corerp-mechanics-redeploy-withanotherresource.step%d.bicep" requiredSecrets := map[string]map[string]string{} @@ -74,7 +75,7 @@ func Test_RedeployWithAnotherResource(t *testing.T) { }, K8sObjects: &validation.K8sObjectSet{ Namespaces: map[string][]validation.K8sObject{ - "default": { + appNamespace: { validation.NewK8sPodForResource(name, "mechanicsa"), }, }, @@ -102,7 +103,7 @@ func Test_RedeployWithAnotherResource(t *testing.T) { }, K8sObjects: &validation.K8sObjectSet{ Namespaces: map[string][]validation.K8sObject{ - "default": { + appNamespace: { validation.NewK8sPodForResource(name, "mechanicsb"), validation.NewK8sPodForResource(name, "mechanicsc"), }, @@ -116,6 +117,7 @@ func Test_RedeployWithAnotherResource(t *testing.T) { func Test_RedeployWithUpdatedResourceUpdatesResource(t *testing.T) { name := "corerp-mechanics-redeploy-withupdatedresource" + appNamespace := "default-corerp-mechanics-redeploy-withupdatedresource" templateFmt := "testdata/corerp-mechanics-redeploy-withupdatedresource.step%d.bicep" requiredSecrets := map[string]map[string]string{} @@ -138,7 +140,7 @@ func Test_RedeployWithUpdatedResourceUpdatesResource(t *testing.T) { }, K8sObjects: &validation.K8sObjectSet{ Namespaces: map[string][]validation.K8sObject{ - "default": { + appNamespace: { validation.NewK8sPodForResource(name, "mechanicsd"), }, }, @@ -161,7 +163,7 @@ func Test_RedeployWithUpdatedResourceUpdatesResource(t *testing.T) { }, K8sObjects: &validation.K8sObjectSet{ Namespaces: map[string][]validation.K8sObject{ - "default": { + appNamespace: { validation.NewK8sPodForResource(name, "mechanicsd"), }, }, @@ -169,7 +171,7 @@ func Test_RedeployWithUpdatedResourceUpdatesResource(t *testing.T) { PostStepVerify: func(ctx context.Context, t *testing.T, test corerp.CoreRPTest) { labelset := kubernetes.MakeSelectorLabels(name, "mechanicsd") - deployments, err := test.Options.K8sClient.AppsV1().Deployments("default").List(context.Background(), metav1.ListOptions{ + deployments, err := test.Options.K8sClient.AppsV1().Deployments(appNamespace).List(context.Background(), metav1.ListOptions{ LabelSelector: labels.SelectorFromSet(labelset).String(), }) @@ -187,6 +189,7 @@ func Test_RedeployWithUpdatedResourceUpdatesResource(t *testing.T) { func Test_RedeployWithTwoSeparateResourcesKeepsResource(t *testing.T) { name := "corerp-mechanics-redeploy-withtwoseparateresource" + appNamespace := "default-corerp-mechanics-redeploy-withtwoseparateresource" templateFmt := "testdata/corerp-mechanics-redeploy-withtwoseparateresource.step%d.bicep" requiredSecrets := map[string]map[string]string{} @@ -209,7 +212,7 @@ func Test_RedeployWithTwoSeparateResourcesKeepsResource(t *testing.T) { }, K8sObjects: &validation.K8sObjectSet{ Namespaces: map[string][]validation.K8sObject{ - "default": { + appNamespace: { validation.NewK8sPodForResource(name, "mechanicse"), }, }, @@ -237,7 +240,7 @@ func Test_RedeployWithTwoSeparateResourcesKeepsResource(t *testing.T) { }, K8sObjects: &validation.K8sObjectSet{ Namespaces: map[string][]validation.K8sObject{ - "default": { + appNamespace: { validation.NewK8sPodForResource(name, "mechanicse"), validation.NewK8sPodForResource(name, "mechanicsf"), }, @@ -251,6 +254,7 @@ func Test_RedeployWithTwoSeparateResourcesKeepsResource(t *testing.T) { func Test_CommunicationCycle(t *testing.T) { name := "corerp-mechanics-communication-cycle" + appNamespace := "default-corerp-mechanics-communication-cycle" template := "testdata/corerp-mechanics-communication-cycle.bicep" requiredSecrets := map[string]map[string]string{} @@ -288,7 +292,7 @@ func Test_CommunicationCycle(t *testing.T) { }, K8sObjects: &validation.K8sObjectSet{ Namespaces: map[string][]validation.K8sObject{ - "default": { + appNamespace: { validation.NewK8sPodForResource(name, "mechanicsg"), validation.NewK8sPodForResource(name, "cyclea"), }, diff --git a/test/functional/corerp/resources/application_environment_test.go b/test/functional/corerp/resources/application_environment_test.go index 2d7da4fc19..5cda823cc8 100644 --- a/test/functional/corerp/resources/application_environment_test.go +++ b/test/functional/corerp/resources/application_environment_test.go @@ -6,11 +6,14 @@ package resource_test import ( + "context" "testing" "github.com/project-radius/radius/test/functional/corerp" "github.com/project-radius/radius/test/step" "github.com/project-radius/radius/test/validation" + "github.com/stretchr/testify/require" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) func Test_ApplicationAndEnvironment(t *testing.T) { @@ -36,6 +39,16 @@ func Test_ApplicationAndEnvironment(t *testing.T) { }, // Application and Environment should not render any K8s Objects directly K8sObjects: &validation.K8sObjectSet{}, + PostStepVerify: func(ctx context.Context, t *testing.T, test corerp.CoreRPTest) { + expectedNS := []string{ + "corerp-resources-app-env", + "corerp-resources-app-env-env-corerp-resources-app-env-app", + } + for _, ns := range expectedNS { + _, err := test.Options.K8sClient.CoreV1().Namespaces().Get(ctx, ns, metav1.GetOptions{}) + require.NoErrorf(t, err, "%s must be created", ns) + } + }, }, }, requiredSecrets) diff --git a/test/functional/corerp/resources/application_test.go b/test/functional/corerp/resources/application_test.go index 614ca72464..1771add029 100644 --- a/test/functional/corerp/resources/application_test.go +++ b/test/functional/corerp/resources/application_test.go @@ -6,16 +6,21 @@ package resource_test import ( + "context" "testing" "github.com/project-radius/radius/test/functional/corerp" "github.com/project-radius/radius/test/step" "github.com/project-radius/radius/test/validation" + + "github.com/stretchr/testify/require" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) func Test_Application(t *testing.T) { template := "testdata/corerp-resources-application.bicep" name := "corerp-resources-application" + appNamespace := "corerp-resources-application-app" requiredSecrets := map[string]map[string]string{} @@ -32,6 +37,10 @@ func Test_Application(t *testing.T) { }, // Application should not render any K8s Objects directly K8sObjects: &validation.K8sObjectSet{}, + PostStepVerify: func(ctx context.Context, t *testing.T, test corerp.CoreRPTest) { + _, err := test.Options.K8sClient.CoreV1().Namespaces().Get(ctx, appNamespace, metav1.GetOptions{}) + require.NoErrorf(t, err, "%s must be created", appNamespace) + }, }, }, requiredSecrets) test.Test(t) diff --git a/test/functional/corerp/resources/aws_kinesis_stream_test.go b/test/functional/corerp/resources/aws_kinesis_stream_test.go index a77c96a908..634ff43562 100644 --- a/test/functional/corerp/resources/aws_kinesis_stream_test.go +++ b/test/functional/corerp/resources/aws_kinesis_stream_test.go @@ -50,6 +50,7 @@ func Test_KinesisStreamExisting(t *testing.T) { template := "testdata/aws-kinesis.bicep" templateExisting := "testdata/aws-kinesis-existing.bicep" name := "ms" + uuid.New().String() + appNamespace := "default-aws-kinesis-existing-app" requiredSecrets := map[string]map[string]string{} @@ -85,7 +86,7 @@ func Test_KinesisStreamExisting(t *testing.T) { }, K8sObjects: &validation.K8sObjectSet{ Namespaces: map[string][]validation.K8sObject{ - "default": { + appNamespace: { validation.NewK8sPodForResource("aws-kinesis-existing-app", "aws-ctnr"), }, }, @@ -93,7 +94,7 @@ func Test_KinesisStreamExisting(t *testing.T) { PostStepVerify: func(ctx context.Context, t *testing.T, ct corerp.CoreRPTest) { labelset := kubernetes.MakeSelectorLabels("aws-kinesis-existing-app", "aws-ctnr") - deployments, err := ct.Options.K8sClient.AppsV1().Deployments("default").List(context.Background(), metav1.ListOptions{ + deployments, err := ct.Options.K8sClient.AppsV1().Deployments(appNamespace).List(context.Background(), metav1.ListOptions{ LabelSelector: labels.SelectorFromSet(labelset).String(), }) require.NoError(t, err, "failed to list deployments") diff --git a/test/functional/corerp/resources/azure_connections_test.go b/test/functional/corerp/resources/azure_connections_test.go index 21e78e3483..e6eb13de68 100644 --- a/test/functional/corerp/resources/azure_connections_test.go +++ b/test/functional/corerp/resources/azure_connections_test.go @@ -18,6 +18,7 @@ func Test_AzureConnections(t *testing.T) { name := "corerp-azure-connection-database-service" containerResourceName := "db-service" template := "testdata/corerp-azure-connection-database-service.bicep" + appNamespace := "default-corerp-azure-connection-database-service" requiredSecrets := map[string]map[string]string{} @@ -39,7 +40,7 @@ func Test_AzureConnections(t *testing.T) { }, K8sObjects: &validation.K8sObjectSet{ Namespaces: map[string][]validation.K8sObject{ - "default": { + appNamespace: { validation.NewK8sPodForResource(name, containerResourceName), }, }, diff --git a/test/functional/corerp/resources/container_test.go b/test/functional/corerp/resources/container_test.go index 8aa4653e94..26378256ff 100644 --- a/test/functional/corerp/resources/container_test.go +++ b/test/functional/corerp/resources/container_test.go @@ -21,6 +21,7 @@ import ( func Test_Container(t *testing.T) { template := "testdata/corerp-resources-container.bicep" name := "corerp-resources-container" + appNamespace := "corerp-resources-container-app" requiredSecrets := map[string]map[string]string{} @@ -42,7 +43,7 @@ func Test_Container(t *testing.T) { }, K8sObjects: &validation.K8sObjectSet{ Namespaces: map[string][]validation.K8sObject{ - "default": { + appNamespace: { validation.NewK8sPodForResource(name, "ctnr-ctnr"), }, }, @@ -56,6 +57,7 @@ func Test_Container(t *testing.T) { func Test_ContainerHttpRoute(t *testing.T) { template := "testdata/corerp-resources-container-httproute.bicep" name := "corerp-resources-container-httproute" + appNamespace := "corerp-resources-container-httproute-app" requiredSecrets := map[string]map[string]string{} @@ -82,7 +84,7 @@ func Test_ContainerHttpRoute(t *testing.T) { }, K8sObjects: &validation.K8sObjectSet{ Namespaces: map[string][]validation.K8sObject{ - "default": { + appNamespace: { validation.NewK8sPodForResource(name, "ctnr-rte-ctnr"), validation.NewK8sServiceForResource(name, "ctnr-rte-rte"), }, @@ -97,6 +99,7 @@ func Test_ContainerHttpRoute(t *testing.T) { func Test_ContainerReadinessLiveness(t *testing.T) { template := "testdata/corerp-resources-container-liveness-readiness.bicep" name := "corerp-resources-container-live-ready" + appNamespace := "corerp-resources-container-live-ready-app" requiredSecrets := map[string]map[string]string{} @@ -118,7 +121,7 @@ func Test_ContainerReadinessLiveness(t *testing.T) { }, K8sObjects: &validation.K8sObjectSet{ Namespaces: map[string][]validation.K8sObject{ - "default": { + appNamespace: { validation.NewK8sPodForResource(name, "ctnr-live-ready"), }, }, @@ -132,6 +135,7 @@ func Test_ContainerReadinessLiveness(t *testing.T) { func Test_ContainerManualScale(t *testing.T) { template := "testdata/corerp-azure-container-manualscale.bicep" name := "corerp-resources-container-manualscale" + appNamespace := "corerp-resources-container-manualscale-app" requiredSecrets := map[string]map[string]string{} @@ -153,7 +157,7 @@ func Test_ContainerManualScale(t *testing.T) { }, K8sObjects: &validation.K8sObjectSet{ Namespaces: map[string][]validation.K8sObject{ - "default": { + appNamespace: { validation.NewK8sPodForResource(name, "ctnr-manualscale"), }, }, @@ -167,6 +171,7 @@ func Test_ContainerManualScale(t *testing.T) { func Test_ContainerWithCommandAndArgs(t *testing.T) { container := "testdata/corerp-resources-container-cmd-args.bicep" name := "corerp-resources-container-cmd-args" + appNamespace := "corerp-resources-container-cmd-args-app" requiredSecrets := map[string]map[string]string{} @@ -188,14 +193,14 @@ func Test_ContainerWithCommandAndArgs(t *testing.T) { }, K8sObjects: &validation.K8sObjectSet{ Namespaces: map[string][]validation.K8sObject{ - "default": { + appNamespace: { validation.NewK8sPodForResource(name, "ctnr-cmd-args"), }, }, }, PostStepVerify: func(ctx context.Context, t *testing.T, test corerp.CoreRPTest) { label := fmt.Sprintf("radius.dev/application=%s", name) - pods, err := test.Options.K8sClient.CoreV1().Pods("default").List(ctx, metav1.ListOptions{ + pods, err := test.Options.K8sClient.CoreV1().Pods(appNamespace).List(ctx, metav1.ListOptions{ LabelSelector: label, }) require.NoError(t, err) diff --git a/test/functional/corerp/resources/container_versioning_test.go b/test/functional/corerp/resources/container_versioning_test.go index 857c7fbb15..8d0d8ed78b 100644 --- a/test/functional/corerp/resources/container_versioning_test.go +++ b/test/functional/corerp/resources/container_versioning_test.go @@ -24,6 +24,7 @@ func Test_ContainerVersioning(t *testing.T) { containerV2 := "testdata/containers/corerp-resources-friendly-container-version-2.bicep" name := "corerp-resources-container-versioning" + appNamespace := "default-corerp-resources-container-versioning" requiredSecrets := map[string]map[string]string{} @@ -45,7 +46,7 @@ func Test_ContainerVersioning(t *testing.T) { }, K8sObjects: &validation.K8sObjectSet{ Namespaces: map[string][]validation.K8sObject{ - "default": { + appNamespace: { validation.NewK8sPodForResource(name, "friendly-ctnr"), }, }, @@ -53,7 +54,7 @@ func Test_ContainerVersioning(t *testing.T) { SkipResourceDeletion: true, PostStepVerify: func(ctx context.Context, t *testing.T, test corerp.CoreRPTest) { label := fmt.Sprintf("radius.dev/application=%s", name) - secrets, err := test.Options.K8sClient.CoreV1().Secrets("default").List(ctx, metav1.ListOptions{ + secrets, err := test.Options.K8sClient.CoreV1().Secrets(appNamespace).List(ctx, metav1.ListOptions{ LabelSelector: label, }) require.NoError(t, err) @@ -77,14 +78,14 @@ func Test_ContainerVersioning(t *testing.T) { }, K8sObjects: &validation.K8sObjectSet{ Namespaces: map[string][]validation.K8sObject{ - "default": { + appNamespace: { validation.NewK8sPodForResource(name, "friendly-ctnr"), }, }, }, PostStepVerify: func(ctx context.Context, t *testing.T, test corerp.CoreRPTest) { label := fmt.Sprintf("radius.dev/application=%s", name) - secrets, err := test.Options.K8sClient.CoreV1().Secrets("default").List(ctx, metav1.ListOptions{ + secrets, err := test.Options.K8sClient.CoreV1().Secrets(appNamespace).List(ctx, metav1.ListOptions{ LabelSelector: label, }) require.NoError(t, err) diff --git a/test/functional/corerp/resources/dapr_invokehttproute_test.go b/test/functional/corerp/resources/dapr_invokehttproute_test.go index d5ac968385..f96d753768 100644 --- a/test/functional/corerp/resources/dapr_invokehttproute_test.go +++ b/test/functional/corerp/resources/dapr_invokehttproute_test.go @@ -17,6 +17,7 @@ import ( func Test_DaprInvokeHttpRoute(t *testing.T) { template := "testdata/corerp-resources-dapr-httproute.bicep" name := "dapr-invokehttproute" + appNamespace := "default-dapr-invokehttproute" requiredSecrets := map[string]map[string]string{} @@ -48,7 +49,7 @@ func Test_DaprInvokeHttpRoute(t *testing.T) { }, K8sObjects: &validation.K8sObjectSet{ Namespaces: map[string][]validation.K8sObject{ - "default": { + appNamespace: { validation.NewK8sPodForResource(name, "dapr-frontend"), validation.NewK8sPodForResource(name, "dapr-backend"), }, diff --git a/test/functional/corerp/resources/dapr_pubsub_test.go b/test/functional/corerp/resources/dapr_pubsub_test.go index 8cac4d608b..5719d6db32 100644 --- a/test/functional/corerp/resources/dapr_pubsub_test.go +++ b/test/functional/corerp/resources/dapr_pubsub_test.go @@ -18,6 +18,7 @@ import ( func Test_DaprPubSubGeneric(t *testing.T) { template := "testdata/corerp-resources-dapr-pubsub-generic.bicep" name := "corerp-resources-dapr-pubsub-generic" + appNamespace := "default-corerp-resources-dapr-pubsub-generic" requiredSecrets := map[string]map[string]string{} @@ -44,7 +45,7 @@ func Test_DaprPubSubGeneric(t *testing.T) { }, K8sObjects: &validation.K8sObjectSet{ Namespaces: map[string][]validation.K8sObject{ - "default": { + appNamespace: { validation.NewK8sPodForResource(name, "gnrc-publisher"), }, }, @@ -58,6 +59,7 @@ func Test_DaprPubSubGeneric(t *testing.T) { func Test_DaprPubSubServiceBus(t *testing.T) { template := "testdata/corerp-resources-dapr-pubsub-servicebus.bicep" name := "corerp-resources-dapr-pubsub-servicebus" + appNamespace := "default-corerp-resources-dapr-pubsub-servicebus" requiredSecrets := map[string]map[string]string{} @@ -84,7 +86,7 @@ func Test_DaprPubSubServiceBus(t *testing.T) { }, K8sObjects: &validation.K8sObjectSet{ Namespaces: map[string][]validation.K8sObject{ - "default": { + appNamespace: { validation.NewK8sPodForResource(name, "sb-publisher"), }, }, diff --git a/test/functional/corerp/resources/dapr_secretstore_test.go b/test/functional/corerp/resources/dapr_secretstore_test.go index 4236915a6b..cce9929e18 100644 --- a/test/functional/corerp/resources/dapr_secretstore_test.go +++ b/test/functional/corerp/resources/dapr_secretstore_test.go @@ -12,25 +12,40 @@ import ( "github.com/project-radius/radius/test/functional/corerp" "github.com/project-radius/radius/test/step" "github.com/project-radius/radius/test/validation" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" ) func Test_DaprSecretStoreGeneric(t *testing.T) { template := "testdata/corerp-resources-dapr-secretstore-generic.bicep" name := "corerp-resources-dapr-secretstore-generic" + appNamespace := "default-corerp-resources-dapr-secretstore-generic" - requiredSecrets := map[string]map[string]string{ - "mysecret": { - "mysecret": "mysecret", + requiredSecrets := map[string]map[string]string{} + + // TODO: remove requiredSecrets, but instead use the below initialResource approach. + resources := []unstructured.Unstructured{ + { + Object: map[string]any{ + "apiVersion": "v1", + "kind": "Secret", + "metadata": map[string]any{ + "name": "mysecret", + "namespace": appNamespace, + }, + "data": map[string]any{ + "mysecret": []byte("mysecret"), + }, + }, }, } - test := corerp.NewCoreRPTest(t, name, []corerp.TestStep{ + test := corerp.NewCoreRPTest(t, appNamespace, []corerp.TestStep{ { Executor: step.NewDeployExecutor(template, functional.GetMagpieImage()), CoreRPResources: &validation.CoreRPResourceSet{ Resources: []validation.CoreRPResource{ { - Name: "corerp-resources-dapr-secretstore-generic", + Name: name, Type: validation.ApplicationsResource, }, { @@ -47,13 +62,13 @@ func Test_DaprSecretStoreGeneric(t *testing.T) { }, K8sObjects: &validation.K8sObjectSet{ Namespaces: map[string][]validation.K8sObject{ - "default": { + appNamespace: { validation.NewK8sPodForResource(name, "gnrc-scs-ctnr"), }, }, }, }, - }, requiredSecrets) + }, requiredSecrets, resources...) test.Test(t) } diff --git a/test/functional/corerp/resources/dapr_statestore_test.go b/test/functional/corerp/resources/dapr_statestore_test.go index 65e7f6319a..288933dfd8 100644 --- a/test/functional/corerp/resources/dapr_statestore_test.go +++ b/test/functional/corerp/resources/dapr_statestore_test.go @@ -17,6 +17,7 @@ import ( func Test_DaprStateStoreGeneric(t *testing.T) { template := "testdata/corerp-resources-dapr-statestore-generic.bicep" name := "corerp-resources-dapr-statestore-generic" + appNamespace := "default-corerp-resources-dapr-statestore-generic" requiredSecrets := map[string]map[string]string{} @@ -43,7 +44,7 @@ func Test_DaprStateStoreGeneric(t *testing.T) { }, K8sObjects: &validation.K8sObjectSet{ Namespaces: map[string][]validation.K8sObject{ - "default": { + appNamespace: { validation.NewK8sPodForResource(name, "gnrc-sts-ctnr"), }, }, @@ -57,6 +58,7 @@ func Test_DaprStateStoreGeneric(t *testing.T) { func Test_DaprStateStoreTableStorage(t *testing.T) { template := "testdata/corerp-resources-dapr-statestore-tablestorage.bicep" name := "corerp-resources-dapr-statestore-tablestorage" + appNamespace := "default-corerp-resources-dapr-statestore-tablestorage" requiredSecrets := map[string]map[string]string{} @@ -83,7 +85,7 @@ func Test_DaprStateStoreTableStorage(t *testing.T) { }, K8sObjects: &validation.K8sObjectSet{ Namespaces: map[string][]validation.K8sObject{ - "default": { + appNamespace: { validation.NewK8sPodForResource(name, "ts-sts-ctnr"), }, }, diff --git a/test/functional/corerp/resources/extender_test.go b/test/functional/corerp/resources/extender_test.go index 1cfca36edb..867901b807 100644 --- a/test/functional/corerp/resources/extender_test.go +++ b/test/functional/corerp/resources/extender_test.go @@ -17,6 +17,7 @@ import ( func Test_Extender(t *testing.T) { template := "testdata/corerp-resources-extender.bicep" name := "corerp-resources-extender" + appNamespace := "default-corerp-resources-extender" requiredSecrets := map[string]map[string]string{} @@ -42,7 +43,7 @@ func Test_Extender(t *testing.T) { }, K8sObjects: &validation.K8sObjectSet{ Namespaces: map[string][]validation.K8sObject{ - "default": { + appNamespace: { validation.NewK8sPodForResource(name, "extr-ctnr"), }, }, diff --git a/test/functional/corerp/resources/gateway_test.go b/test/functional/corerp/resources/gateway_test.go index 5bffb34f1c..bfcf9c6ab7 100644 --- a/test/functional/corerp/resources/gateway_test.go +++ b/test/functional/corerp/resources/gateway_test.go @@ -32,6 +32,7 @@ const ( func Test_Gateway(t *testing.T) { template := "testdata/corerp-resources-gateway.bicep" name := "corerp-resources-gateway" + appNamespace := "default-corerp-resources-gateway" requiredSecrets := map[string]map[string]string{} @@ -73,7 +74,7 @@ func Test_Gateway(t *testing.T) { }, K8sObjects: &validation.K8sObjectSet{ Namespaces: map[string][]validation.K8sObject{ - "default": { + appNamespace: { validation.NewK8sPodForResource(name, "http-gtwy-front-ctnr"), validation.NewK8sPodForResource(name, "http-gtwy-back-ctnr"), validation.NewK8sHTTPProxyForResource(name, "http-gtwy-gtwy"), @@ -86,7 +87,7 @@ func Test_Gateway(t *testing.T) { }, PostStepVerify: func(ctx context.Context, t *testing.T, ct corerp.CoreRPTest) { // Get hostname from root HTTPProxy in 'default' namespace - hostname, err := functional.GetHostnameForHTTPProxy(ctx, ct.Options.Client, "default", name) + hostname, err := functional.GetHostnameForHTTPProxy(ctx, ct.Options.Client, appNamespace, name) require.NoError(t, err) t.Logf("found root proxy with hostname: {%s}", hostname) @@ -163,6 +164,7 @@ func testGatewayWithPortForward(t *testing.T, ctx context.Context, at corerp.Cor func Test_HTTPSGateway(t *testing.T) { template := "testdata/corerp-resources-secure-gateway.bicep" name := "corerp-resources-gateways" + appNamespace := "default-corerp-resources-gateways" requiredSecrets := map[string]map[string]string{} @@ -194,7 +196,7 @@ func Test_HTTPSGateway(t *testing.T) { }, K8sObjects: &validation.K8sObjectSet{ Namespaces: map[string][]validation.K8sObject{ - "default": { + appNamespace: { validation.NewK8sPodForResource(name, "gtwy-front-ctnr"), validation.NewK8sHTTPProxyForResource(name, "gtwy-gtwy"), validation.NewK8sHTTPProxyForResource(name, "gtwy-front-rte"), @@ -204,7 +206,7 @@ func Test_HTTPSGateway(t *testing.T) { }, PostStepVerify: func(ctx context.Context, t *testing.T, ct corerp.CoreRPTest) { // Get hostname from root HTTPProxy in 'default' namespace - hostname, err := functional.GetHostnameForHTTPProxy(ctx, ct.Options.Client, "default", name) + hostname, err := functional.GetHostnameForHTTPProxy(ctx, ct.Options.Client, appNamespace, name) require.NoError(t, err) t.Logf("found root proxy with hostname: {%s}", hostname) diff --git a/test/functional/corerp/resources/kubemetadata_container_test.go b/test/functional/corerp/resources/kubemetadata_container_test.go index 373c417962..c2a9f45552 100644 --- a/test/functional/corerp/resources/kubemetadata_container_test.go +++ b/test/functional/corerp/resources/kubemetadata_container_test.go @@ -21,7 +21,7 @@ import ( func Test_KubeMetadataContainer(t *testing.T) { template := "testdata/corerp-resources-kubemetadata-container.bicep" name := "corerp-kmd-app" - ns := "corerp-kmd-ns" + appNamespace := "corerp-kmd-ns-corerp-kmd-app" requiredSecrets := map[string]map[string]string{} @@ -57,7 +57,7 @@ func Test_KubeMetadataContainer(t *testing.T) { }, K8sObjects: &validation.K8sObjectSet{ Namespaces: map[string][]validation.K8sObject{ - ns: { + appNamespace: { validation.NewK8sPodForResource(name, "corerp-kmd-ctnr"), }, }, @@ -65,7 +65,7 @@ func Test_KubeMetadataContainer(t *testing.T) { PostStepVerify: func(ctx context.Context, t *testing.T, test corerp.CoreRPTest) { // Verify pod labels and annotations label := fmt.Sprintf("radius.dev/application=%s", name) - pods, err := test.Options.K8sClient.CoreV1().Pods(ns).List(ctx, metav1.ListOptions{ + pods, err := test.Options.K8sClient.CoreV1().Pods(appNamespace).List(ctx, metav1.ListOptions{ LabelSelector: label, }) require.NoError(t, err) @@ -76,7 +76,7 @@ func Test_KubeMetadataContainer(t *testing.T) { require.True(t, isMapSubSet(expectedLabels, pod.Labels)) // Verify deployment labels and annotations - deployments, err := test.Options.K8sClient.AppsV1().Deployments(ns).List(context.Background(), metav1.ListOptions{ + deployments, err := test.Options.K8sClient.AppsV1().Deployments(appNamespace).List(context.Background(), metav1.ListOptions{ LabelSelector: label, }) require.NoError(t, err) diff --git a/test/functional/corerp/resources/kubmetadata_cascade_test.go b/test/functional/corerp/resources/kubmetadata_cascade_test.go index 734463b9f5..ebbdb4113a 100644 --- a/test/functional/corerp/resources/kubmetadata_cascade_test.go +++ b/test/functional/corerp/resources/kubmetadata_cascade_test.go @@ -22,7 +22,7 @@ import ( func Test_KubeMetadataCascade(t *testing.T) { template := "testdata/corerp-resources-kubemetadata-cascade.bicep" name := "corerp-kmd-cascade-app" - ns := "corerp-kmd-cascade-ns" + appNamespace := "corerp-kmd-cascade-ns-corerp-kmd-cascade-app" requiredSecrets := map[string]map[string]string{} @@ -82,7 +82,7 @@ func Test_KubeMetadataCascade(t *testing.T) { }, K8sObjects: &validation.K8sObjectSet{ Namespaces: map[string][]validation.K8sObject{ - ns: { + appNamespace: { validation.NewK8sPodForResource(name, "corerp-kmd-cascade-ctnr"), }, }, @@ -90,7 +90,7 @@ func Test_KubeMetadataCascade(t *testing.T) { PostStepVerify: func(ctx context.Context, t *testing.T, test corerp.CoreRPTest) { // Verify pod labels and annotations label := fmt.Sprintf("radius.dev/application=%s", name) - pods, err := test.Options.K8sClient.CoreV1().Pods(ns).List(ctx, metav1.ListOptions{ + pods, err := test.Options.K8sClient.CoreV1().Pods(appNamespace).List(ctx, metav1.ListOptions{ LabelSelector: label, }) require.NoError(t, err) @@ -103,7 +103,7 @@ func Test_KubeMetadataCascade(t *testing.T) { require.True(t, isMapNonIntersecting(notExpectedLabels, pod.Labels)) // Verify deployment labels and annotations - deployments, err := test.Options.K8sClient.AppsV1().Deployments(ns).List(context.Background(), metav1.ListOptions{ + deployments, err := test.Options.K8sClient.AppsV1().Deployments(appNamespace).List(context.Background(), metav1.ListOptions{ LabelSelector: label, }) require.NoError(t, err) diff --git a/test/functional/corerp/resources/microsoftsql_test.go b/test/functional/corerp/resources/microsoftsql_test.go index fa6dbd72d7..a66ef6b490 100644 --- a/test/functional/corerp/resources/microsoftsql_test.go +++ b/test/functional/corerp/resources/microsoftsql_test.go @@ -17,6 +17,7 @@ import ( func Test_MicrosoftSQL(t *testing.T) { template := "testdata/corerp-resources-microsoft-sql.bicep" name := "corerp-resources-microsoft-sql" + appNamespace := "default-corerp-resources-microsoft-sql" requiredSecrets := map[string]map[string]string{} @@ -26,7 +27,7 @@ func Test_MicrosoftSQL(t *testing.T) { CoreRPResources: &validation.CoreRPResourceSet{ Resources: []validation.CoreRPResource{ { - Name: "corerp-resources-microsoft-sql", + Name: name, Type: validation.ApplicationsResource, }, { @@ -38,7 +39,7 @@ func Test_MicrosoftSQL(t *testing.T) { }, K8sObjects: &validation.K8sObjectSet{ Namespaces: map[string][]validation.K8sObject{ - "default": { + appNamespace: { validation.NewK8sPodForResource(name, "mssql-app-ctnr"), }, }, diff --git a/test/functional/corerp/resources/mongodb_test.go b/test/functional/corerp/resources/mongodb_test.go index 0dd7e2565c..ac2dabb3f3 100644 --- a/test/functional/corerp/resources/mongodb_test.go +++ b/test/functional/corerp/resources/mongodb_test.go @@ -18,6 +18,7 @@ func Test_MongoDB(t *testing.T) { t.Skip() template := "testdata/corerp-resources-mongodb.bicep" name := "corerp-resources-mongodb" + appNamespace := "default-corerp-resources-mongodb" requiredSecrets := map[string]map[string]string{} @@ -44,7 +45,7 @@ func Test_MongoDB(t *testing.T) { }, K8sObjects: &validation.K8sObjectSet{ Namespaces: map[string][]validation.K8sObject{ - "default": { + appNamespace: { validation.NewK8sPodForResource(name, "mdb-app-ctnr"), }, }, @@ -58,6 +59,7 @@ func Test_MongoDB(t *testing.T) { func Test_MongoDBUserSecrets(t *testing.T) { template := "testdata/corerp-resources-mongodb-user-secrets.bicep" name := "corerp-resources-mongodb-user-secrets" + appNamespace := "default-corerp-resources-mongodb-user-secrets" requiredSecrets := map[string]map[string]string{} @@ -94,7 +96,7 @@ func Test_MongoDBUserSecrets(t *testing.T) { }, K8sObjects: &validation.K8sObjectSet{ Namespaces: map[string][]validation.K8sObject{ - "default": { + appNamespace: { validation.NewK8sPodForResource(name, "mdb-us-app-ctnr"), validation.NewK8sPodForResource(name, "mdb-us-ctnr"), validation.NewK8sServiceForResource(name, "mdb-us-rte"), @@ -111,9 +113,9 @@ func Test_MongoDBUserSecrets(t *testing.T) { // the creation of a mongoDB from recipe // container using the mongoDB link to connect to the mongoDB resource func Test_MongoDB_Recipe(t *testing.T) { - template := "testdata/corerp-resources-mongodb-recipe.bicep" name := "corerp-resources-mongodb-recipe" + appNamespace := "corerp-resources-mongodb-recipe-app" requiredSecrets := map[string]map[string]string{} @@ -145,7 +147,7 @@ func Test_MongoDB_Recipe(t *testing.T) { }, K8sObjects: &validation.K8sObjectSet{ Namespaces: map[string][]validation.K8sObject{ - "corerp-resources-environment-recipes-env": { + appNamespace: { validation.NewK8sPodForResource(name, "mongodb-recipe-app-ctnr"), }, }, @@ -163,6 +165,7 @@ func Test_MongoDB_DevRecipe(t *testing.T) { template := "testdata/corerp-resources-mongodb-devrecipe.bicep" name := "corerp-resources-mongodb-devrecipe" + appNamespace := "corerp-resources-mongodb-devrecipe-app" requiredSecrets := map[string]map[string]string{} @@ -194,7 +197,7 @@ func Test_MongoDB_DevRecipe(t *testing.T) { }, K8sObjects: &validation.K8sObjectSet{ Namespaces: map[string][]validation.K8sObject{ - "corerp-resources-environment-devrecipe-env": { + appNamespace: { validation.NewK8sPodForResource(name, "mongodb-devrecipe-app-ctnr"), }, }, diff --git a/test/functional/corerp/resources/persistent_volume_test.go b/test/functional/corerp/resources/persistent_volume_test.go index 8660793c81..84941c66b8 100644 --- a/test/functional/corerp/resources/persistent_volume_test.go +++ b/test/functional/corerp/resources/persistent_volume_test.go @@ -17,6 +17,7 @@ import ( func Test_PersistentVolume(t *testing.T) { template := "testdata/corerp-resources-volume-azure-keyvault.bicep" name := "corerp-resources-volume-azure-keyvault" + appNamespace := "corerp-resources-volume-azure-keyvault-app" requiredSecrets := map[string]map[string]string{} @@ -47,7 +48,7 @@ func Test_PersistentVolume(t *testing.T) { }, K8sObjects: &validation.K8sObjectSet{ Namespaces: map[string][]validation.K8sObject{ - "corerp-azure-workload-env": { + appNamespace: { validation.NewK8sPodForResource(name, "volume-azkv-ctnr"), }, }, diff --git a/test/functional/corerp/resources/rabbitmq_test.go b/test/functional/corerp/resources/rabbitmq_test.go index 295f35b9df..7f882de370 100644 --- a/test/functional/corerp/resources/rabbitmq_test.go +++ b/test/functional/corerp/resources/rabbitmq_test.go @@ -17,6 +17,7 @@ import ( func Test_RabbitMQ(t *testing.T) { template := "testdata/corerp-resources-rabbitmq.bicep" name := "corerp-resources-rabbitmq" + appNamespace := "default-corerp-resources-rabbitmq" requiredSecrets := map[string]map[string]string{} @@ -53,7 +54,7 @@ func Test_RabbitMQ(t *testing.T) { }, K8sObjects: &validation.K8sObjectSet{ Namespaces: map[string][]validation.K8sObject{ - "default": { + appNamespace: { validation.NewK8sPodForResource(name, "rmq-app-ctnr"), validation.NewK8sPodForResource(name, "rmq-ctnr"), validation.NewK8sServiceForResource(name, "rmq-rte"), diff --git a/test/functional/corerp/resources/redis_test.go b/test/functional/corerp/resources/redis_test.go index edfbdc4692..ceea3f4a95 100644 --- a/test/functional/corerp/resources/redis_test.go +++ b/test/functional/corerp/resources/redis_test.go @@ -17,6 +17,7 @@ import ( func Test_Redis(t *testing.T) { template := "testdata/corerp-resources-redis-user-secrets.bicep" name := "corerp-resources-redis-user-secrets" + appNamespace := "default-corerp-resources-redis-user-secrets" requiredSecrets := map[string]map[string]string{} @@ -53,7 +54,7 @@ func Test_Redis(t *testing.T) { }, K8sObjects: &validation.K8sObjectSet{ Namespaces: map[string][]validation.K8sObject{ - "default": { + appNamespace: { validation.NewK8sPodForResource(name, "rds-app-ctnr"), validation.NewK8sPodForResource(name, "rds-ctnr"), validation.NewK8sServiceForResource(name, "rds-rte"), diff --git a/test/functional/corerp/resources/sql_test.go b/test/functional/corerp/resources/sql_test.go index f49e4ad72a..f5bcec1b36 100644 --- a/test/functional/corerp/resources/sql_test.go +++ b/test/functional/corerp/resources/sql_test.go @@ -22,6 +22,7 @@ func Test_SQL(t *testing.T) { } template := "testdata/corerp-resources-sql.bicep" name := "corerp-resources-sql" + appNamespace := "default-corerp-resources-sql" requiredSecrets := map[string]map[string]string{} @@ -58,7 +59,7 @@ func Test_SQL(t *testing.T) { }, K8sObjects: &validation.K8sObjectSet{ Namespaces: map[string][]validation.K8sObject{ - "default": { + appNamespace: { validation.NewK8sPodForResource(name, "sql-app-ctnr"), validation.NewK8sPodForResource(name, "sql-ctnr"), validation.NewK8sServiceForResource(name, "sql-rte"), diff --git a/test/functional/corerp/resources/storage_test.go b/test/functional/corerp/resources/storage_test.go index 9f9d53d7cb..b65d629001 100644 --- a/test/functional/corerp/resources/storage_test.go +++ b/test/functional/corerp/resources/storage_test.go @@ -16,8 +16,9 @@ import ( // Test_Storage tests if a container can be created and then deleted by the magpiego with the workload identity. func Test_Storage(t *testing.T) { - template := "testdata/corerp-resources-container-wi.bicep" - name := "corerp-resources-container-wi" + template := "testdata/corerp-resources-container-workload.bicep" + name := "corerp-resources-container-workload" + appNamespace := "azstorage-workload-app" requiredSecrets := map[string]map[string]string{} @@ -31,7 +32,7 @@ func Test_Storage(t *testing.T) { Type: validation.ApplicationsResource, }, { - Name: "test-container-wi", + Name: "azstorage-ctnr", Type: validation.ContainersResource, App: name, }, @@ -39,8 +40,8 @@ func Test_Storage(t *testing.T) { }, K8sObjects: &validation.K8sObjectSet{ Namespaces: map[string][]validation.K8sObject{ - "test-namespace": { - validation.NewK8sPodForResource(name, "test-container-wi"), + appNamespace: { + validation.NewK8sPodForResource(name, "azstorage-ctnr"), }, }, }, diff --git a/test/functional/corerp/resources/testdata/corerp-azure-container-manualscale.bicep b/test/functional/corerp/resources/testdata/corerp-azure-container-manualscale.bicep index f719b5359f..a4985c8e46 100644 --- a/test/functional/corerp/resources/testdata/corerp-azure-container-manualscale.bicep +++ b/test/functional/corerp/resources/testdata/corerp-azure-container-manualscale.bicep @@ -18,6 +18,12 @@ resource app 'Applications.Core/applications@2022-03-15-privatepreview' = { location: location properties: { environment: environment + extensions: [ + { + kind: 'kubernetesNamespace' + namespace: 'corerp-resources-container-manualscale-app' + } + ] } } diff --git a/test/functional/corerp/resources/testdata/corerp-resources-application.bicep b/test/functional/corerp/resources/testdata/corerp-resources-application.bicep index fd90af95c4..7330fe5e2f 100644 --- a/test/functional/corerp/resources/testdata/corerp-resources-application.bicep +++ b/test/functional/corerp/resources/testdata/corerp-resources-application.bicep @@ -11,5 +11,11 @@ resource app 'Applications.Core/applications@2022-03-15-privatepreview' = { location: location properties: { environment: environment + extensions: [ + { + kind: 'kubernetesNamespace' + namespace: 'corerp-resources-application-app' + } + ] } } diff --git a/test/functional/corerp/resources/testdata/corerp-resources-container-cmd-args.bicep b/test/functional/corerp/resources/testdata/corerp-resources-container-cmd-args.bicep index a754896a96..e459a25959 100644 --- a/test/functional/corerp/resources/testdata/corerp-resources-container-cmd-args.bicep +++ b/test/functional/corerp/resources/testdata/corerp-resources-container-cmd-args.bicep @@ -11,6 +11,12 @@ resource app 'Applications.Core/applications@2022-03-15-privatepreview' = { location: location properties: { environment: environment + extensions: [ + { + kind: 'kubernetesNamespace' + namespace: 'corerp-resources-container-cmd-args-app' + } + ] } } diff --git a/test/functional/corerp/resources/testdata/corerp-resources-container-httproute.bicep b/test/functional/corerp/resources/testdata/corerp-resources-container-httproute.bicep index 981e83f2fb..f555578ca3 100644 --- a/test/functional/corerp/resources/testdata/corerp-resources-container-httproute.bicep +++ b/test/functional/corerp/resources/testdata/corerp-resources-container-httproute.bicep @@ -17,6 +17,12 @@ resource app 'Applications.Core/applications@2022-03-15-privatepreview' = { location: location properties: { environment: environment + extensions: [ + { + kind: 'kubernetesNamespace' + namespace: 'corerp-resources-container-httproute-app' + } + ] } } diff --git a/test/functional/corerp/resources/testdata/corerp-resources-container-liveness-readiness.bicep b/test/functional/corerp/resources/testdata/corerp-resources-container-liveness-readiness.bicep index dedf6526ae..7422c404f3 100644 --- a/test/functional/corerp/resources/testdata/corerp-resources-container-liveness-readiness.bicep +++ b/test/functional/corerp/resources/testdata/corerp-resources-container-liveness-readiness.bicep @@ -18,6 +18,12 @@ resource app 'Applications.Core/applications@2022-03-15-privatepreview' = { location: location properties: { environment: environment + extensions: [ + { + kind: 'kubernetesNamespace' + namespace: 'corerp-resources-container-live-ready-app' + } + ] } } diff --git a/test/functional/corerp/resources/testdata/corerp-resources-container-wi.bicep b/test/functional/corerp/resources/testdata/corerp-resources-container-workload.bicep similarity index 86% rename from test/functional/corerp/resources/testdata/corerp-resources-container-wi.bicep rename to test/functional/corerp/resources/testdata/corerp-resources-container-workload.bicep index 3863734c08..9b28c66ce4 100644 --- a/test/functional/corerp/resources/testdata/corerp-resources-container-wi.bicep +++ b/test/functional/corerp/resources/testdata/corerp-resources-container-workload.bicep @@ -11,13 +11,13 @@ param magpieimage string param oidcIssuer string = 'https://radiusoidc.blob.core.windows.net/kubeoidc/' resource env 'Applications.Core/environments@2022-03-15-privatepreview' = { - name: 'test-env' + name: 'corerp-azure-workload-env' location: location properties: { compute: { kind: 'kubernetes' resourceId: 'self' - namespace: 'test-namespace' + namespace: 'corerp-azure-workload-env' identity: { kind: 'azure.com.workload' oidcIssuer: oidcIssuer @@ -32,15 +32,21 @@ resource env 'Applications.Core/environments@2022-03-15-privatepreview' = { } resource app 'Applications.Core/applications@2022-03-15-privatepreview' = { - name: 'corerp-resources-container-wi' + name: 'corerp-resources-container-workload' location: location properties: { environment: env.id + extensions: [ + { + kind: 'kubernetesNamespace' + namespace: 'azstorage-workload-app' + } + ] } } resource container 'Applications.Core/containers@2022-03-15-privatepreview' = { - name: 'test-container-wi' + name: 'azstorage-ctnr' location: location properties: { application: app.id diff --git a/test/functional/corerp/resources/testdata/corerp-resources-container.bicep b/test/functional/corerp/resources/testdata/corerp-resources-container.bicep index 428d888dda..68b5f33ea0 100644 --- a/test/functional/corerp/resources/testdata/corerp-resources-container.bicep +++ b/test/functional/corerp/resources/testdata/corerp-resources-container.bicep @@ -17,6 +17,12 @@ resource app 'Applications.Core/applications@2022-03-15-privatepreview' = { location: location properties: { environment: environment + extensions: [ + { + kind: 'kubernetesNamespace' + namespace: 'corerp-resources-container-app' + } + ] } } diff --git a/test/functional/corerp/resources/testdata/corerp-resources-mongodb-devrecipe.bicep b/test/functional/corerp/resources/testdata/corerp-resources-mongodb-devrecipe.bicep index f8d6e20032..852b0d37bc 100644 --- a/test/functional/corerp/resources/testdata/corerp-resources-mongodb-devrecipe.bicep +++ b/test/functional/corerp/resources/testdata/corerp-resources-mongodb-devrecipe.bicep @@ -29,6 +29,12 @@ resource app 'Applications.Core/applications@2022-03-15-privatepreview' = { location: 'global' properties: { environment: env.id + extensions: [ + { + kind: 'kubernetesNamespace' + namespace: 'corerp-resources-mongodb-devrecipe-app' + } + ] } } diff --git a/test/functional/corerp/resources/testdata/corerp-resources-mongodb-recipe.bicep b/test/functional/corerp/resources/testdata/corerp-resources-mongodb-recipe.bicep index b50b6bf555..9a5c63088b 100644 --- a/test/functional/corerp/resources/testdata/corerp-resources-mongodb-recipe.bicep +++ b/test/functional/corerp/resources/testdata/corerp-resources-mongodb-recipe.bicep @@ -34,6 +34,12 @@ resource app 'Applications.Core/applications@2022-03-15-privatepreview' = { location: 'global' properties: { environment: env.id + extensions: [ + { + kind: 'kubernetesNamespace' + namespace: 'corerp-resources-mongodb-recipe-app' + } + ] } } diff --git a/test/functional/corerp/resources/testdata/corerp-resources-volume-azure-keyvault.bicep b/test/functional/corerp/resources/testdata/corerp-resources-volume-azure-keyvault.bicep index 7c0ba71f55..278d3adb1f 100644 --- a/test/functional/corerp/resources/testdata/corerp-resources-volume-azure-keyvault.bicep +++ b/test/functional/corerp/resources/testdata/corerp-resources-volume-azure-keyvault.bicep @@ -50,6 +50,12 @@ resource app 'Applications.Core/applications@2022-03-15-privatepreview' = { location: location properties: { environment: env.id + extensions: [ + { + kind: 'kubernetesNamespace' + namespace: 'corerp-resources-volume-azure-keyvault-app' + } + ] } } diff --git a/test/functional/samples/tutorial_test.go b/test/functional/samples/tutorial_test.go index 10fbb25275..6e5d282351 100644 --- a/test/functional/samples/tutorial_test.go +++ b/test/functional/samples/tutorial_test.go @@ -45,6 +45,7 @@ func Test_TutorialSampleMongoContainer(t *testing.T) { relPathSamplesRepo, _ := filepath.Rel(cwd, samplesRepoAbsPath) template := filepath.Join(relPathSamplesRepo, "tutorial/app.bicep") appName := "webapp" + appNamespace := "default-webapp" requiredSecrets := map[string]map[string]string{} @@ -77,7 +78,7 @@ func Test_TutorialSampleMongoContainer(t *testing.T) { }, PostStepVerify: func(ctx context.Context, t *testing.T, ct corerp.CoreRPTest) { // Get hostname from root HTTPProxy in 'default' namespace - hostname, err := functional.GetHostnameForHTTPProxy(ctx, ct.Options.Client, "default", appName) + hostname, err := functional.GetHostnameForHTTPProxy(ctx, ct.Options.Client, appNamespace, appName) require.NoError(t, err) t.Logf("found root proxy with hostname: {%s}", hostname) @@ -101,7 +102,7 @@ func Test_TutorialSampleMongoContainer(t *testing.T) { K8sOutputResources: []unstructured.Unstructured{}, K8sObjects: &validation.K8sObjectSet{ Namespaces: map[string][]validation.K8sObject{ - "default": { + appNamespace: { validation.NewK8sPodForResource(appName, "frontend"), validation.NewK8sHTTPProxyForResource(appName, "public"), validation.NewK8sHTTPProxyForResource(appName, "http-route"), diff --git a/test/functional/ucp/ucptest.go b/test/functional/ucp/ucptest.go index be2de0404e..2a1444020e 100644 --- a/test/functional/ucp/ucptest.go +++ b/test/functional/ucp/ucptest.go @@ -55,7 +55,7 @@ func (ucptest UCPTest) Test(t *testing.T) { // Only start capturing controller logs once. radiusControllerLogSync.Do(func() { - err := validation.SaveLogsForController(ctx, ucptest.Options.K8sClient, "radius-system", logPrefix) + _, err := validation.SaveContainerLogs(ctx, ucptest.Options.K8sClient, "radius-system", logPrefix) if err != nil { t.Errorf("failed to capture logs from radius controller: %v", err) } diff --git a/test/magpiego/bindings/redis.go b/test/magpiego/bindings/redis.go index 89fd746c16..f19a3a237c 100644 --- a/test/magpiego/bindings/redis.go +++ b/test/magpiego/bindings/redis.go @@ -2,7 +2,7 @@ package bindings import ( "context" - "fmt" + "crypto/tls" "log" "time" @@ -18,13 +18,23 @@ func RedisBinding(envParams map[string]string) BindingStatus { return BindingStatus{false, "Redis HOST and PORT are required"} } redisPassword := envParams["PASSWORD"] - op := &redis.Options{Addr: redisHost, Password: redisPassword, WriteTimeout: 5 * time.Second} + op := &redis.Options{ + Addr: redisHost, + Password: redisPassword, + WriteTimeout: 5 * time.Second, + } + + // Enable TLS if the port is 6380. + if envParams["PORT"] == "6380" { + op.TLSConfig = &tls.Config{} + } + client := redis.NewClient(op) ctx := context.Background() err = client.Ping(ctx).Err() if err != nil { - log.Println(fmt.Sprintf("failed to connect with redis instance at %s - %v", redisHost, err.Error())) + log.Printf("failed to connect with redis instance at %s - %v\n", redisHost, err.Error()) return BindingStatus{false, "not connected"} } return BindingStatus{true, "connected"} diff --git a/test/validation/k8s.go b/test/validation/k8s.go index 21b9a30a4f..1ba4763f51 100644 --- a/test/validation/k8s.go +++ b/test/validation/k8s.go @@ -136,19 +136,19 @@ func ValidateDeploymentsRunning(ctx context.Context, t *testing.T, k8s *kubernet } // SaveContainerLogs get container logs for all containers in a namespace and saves them to disk. -func SaveLogsForController(ctx context.Context, k8s *kubernetes.Clientset, namespace string, logPrefix string) error { +func SaveContainerLogs(ctx context.Context, k8s *kubernetes.Clientset, namespace string, logPrefix string) (watchk8s.Interface, error) { return watchForPods(ctx, k8s, namespace, logPrefix, "") } // SaveAndWatchContainerLogsForApp watches for all containers in a namespace and saves them to disk. -func SaveLogsForApplication(ctx context.Context, k8s *kubernetes.Clientset, namespace string, logPrefix string, appName string) error { +func SaveLogsForApplication(ctx context.Context, k8s *kubernetes.Clientset, namespace string, logPrefix string, appName string) (watchk8s.Interface, error) { return watchForPods(ctx, k8s, namespace, logPrefix, fmt.Sprintf("%s=%s", kuberneteskeys.LabelRadiusApplication, appName)) } -func watchForPods(ctx context.Context, k8s *kubernetes.Clientset, namespace string, logPrefix string, labelSelector string) error { +func watchForPods(ctx context.Context, k8s *kubernetes.Clientset, namespace string, logPrefix string, labelSelector string) (watchk8s.Interface, error) { if err := os.MkdirAll(logPrefix, os.ModePerm); err != nil { log.Printf("Failed to create output log directory '%s' Error was: '%q'. Container logs will be discarded", logPrefix, err) - return nil + return nil, nil } podClient := k8s.CoreV1().Pods(namespace) @@ -159,7 +159,7 @@ func watchForPods(ctx context.Context, k8s *kubernetes.Clientset, namespace stri LabelSelector: labelSelector, }) if err != nil { - return err + return nil, err } go func() { @@ -177,6 +177,13 @@ func watchForPods(ctx context.Context, k8s *kubernetes.Clientset, namespace stri continue } + // Skip streaming log when Pod is in pending state. + if pod.Status.Phase == corev1.PodPending { + continue + } + + log.Printf("Start streaming Kubernetes logs - Pod %s is in state: %s", pod.Name, pod.Status.Phase) + // Only start one log capture per pod _, ok = pods[pod.Name] if ok { @@ -190,7 +197,7 @@ func watchForPods(ctx context.Context, k8s *kubernetes.Clientset, namespace stri } }() - return nil + return podList, nil } // See https://github.com/dapr/dapr/blob/22bb68bc89a86fc64c2c27dfd219ba68a38fb2ad/tests/platforms/kubernetes/appmanager.go#L706 for reference.