diff --git a/pkg/corerp/frontend/controller/applications/graph_util.go b/pkg/corerp/frontend/controller/applications/graph_util.go index 6c5d1bae835..3cb78bdd976 100644 --- a/pkg/corerp/frontend/controller/applications/graph_util.go +++ b/pkg/corerp/frontend/controller/applications/graph_util.go @@ -295,7 +295,7 @@ func computeGraph(applicationName string, applicationResources []generated.Gener entry := applicationGraphResourcesByID[id] for _, connection := range entry.Connections { - otherID := *connection.ID + otherID := to.String(connection.ID) direction := connection.Direction // For each connection let's make sure the destination is also part of the application graph. This handles @@ -328,11 +328,10 @@ func computeGraph(applicationName string, applicationResources []generated.Gener //id is the source from which the connections in connectionsBySource go out if *direction == corerpv20231001preview.DirectionOutbound { // we are dealing with a relation formed by "connection" connectionsBySource[id] = append(connectionsBySource[id], *connection) - dir := corerpv20231001preview.DirectionInbound //otherID is the destination to the connections in connectionsByDestination connectionInbound := corerpv20231001preview.ApplicationGraphConnection{ - ID: &id, - Direction: &dir, //Direction is set with respect to Resource defining this connection + ID: to.Ptr(id), + Direction: to.Ptr(corerpv20231001preview.DirectionInbound), //Direction is set with respect to Resource defining this connection } connectionsByDestination[otherID] = append(connectionsByDestination[otherID], connectionInbound) } else { @@ -493,7 +492,7 @@ func connectionsFromAPIData(resource generated.GenericResource, allResources []g // If we encounter an error processing this data, just skip "invalid" connection entry. entries := []*corerpv20231001preview.ApplicationGraphConnection{} for _, connection := range connections { - dir := corerpv20231001preview.DirectionInbound + dir := corerpv20231001preview.DirectionOutbound data := corerpv20231001preview.ConnectionProperties{} err := toStronglyTypedData(connection, &data) if err == nil { @@ -582,7 +581,7 @@ func providesFromAPIData(resource generated.GenericResource) []*corerpv20231001p // If we encounter an error processing this data, just skip "invalid" connection entry. entries := []*corerpv20231001preview.ApplicationGraphConnection{} for _, connection := range connections { - dir := corerpv20231001preview.DirectionOutbound + dir := corerpv20231001preview.DirectionInbound data := corerpv20231001preview.ContainerPortProperties{} err := toStronglyTypedData(connection, &data) if err == nil { diff --git a/pkg/corerp/frontend/controller/applications/graph_util_test.go b/pkg/corerp/frontend/controller/applications/graph_util_test.go index de94c21dabc..f45564c7492 100644 --- a/pkg/corerp/frontend/controller/applications/graph_util_test.go +++ b/pkg/corerp/frontend/controller/applications/graph_util_test.go @@ -140,6 +140,13 @@ func Test_computeGraph(t *testing.T) { envResourceDataFile: "", expectedDataFile: "graph-app-httproute-out.json", }, + { + name: "using httproute 2", + applicationName: "myapp", + appResourceDataFile: "graph-app-httproute2-in.json", + envResourceDataFile: "", + expectedDataFile: "graph-app-httproute2-out.json", + }, { name: "direct route", applicationName: "myapp", diff --git a/pkg/corerp/frontend/controller/applications/testdata/graph-app-directroute-out.json b/pkg/corerp/frontend/controller/applications/testdata/graph-app-directroute-out.json index ed65c15b5c5..4875d003afa 100644 --- a/pkg/corerp/frontend/controller/applications/testdata/graph-app-directroute-out.json +++ b/pkg/corerp/frontend/controller/applications/testdata/graph-app-directroute-out.json @@ -2,7 +2,7 @@ { "connections": [ { - "direction": "Inbound", + "direction": "Outbound", "id": "/planes/radius/local/resourcegroups/default/providers/Applications.Core/containers/backendapp" } ], @@ -13,7 +13,12 @@ "type": "Applications.Core/containers" }, { - "connections": [], + "connections": [ + { + "direction": "Inbound", + "id": "/planes/radius/local/resourcegroups/default/providers/Applications.Core/containers/frontend" + } + ], "id": "/planes/radius/local/resourcegroups/default/providers/Applications.Core/containers/backendapp", "name": "backendapp", "outputResources": [], diff --git a/pkg/corerp/frontend/controller/applications/testdata/graph-app-httproute-out.json b/pkg/corerp/frontend/controller/applications/testdata/graph-app-httproute-out.json index e34c4ec30e8..fda2bc267cc 100644 --- a/pkg/corerp/frontend/controller/applications/testdata/graph-app-httproute-out.json +++ b/pkg/corerp/frontend/controller/applications/testdata/graph-app-httproute-out.json @@ -2,7 +2,7 @@ { "connections": [ { - "direction": "Outbound", + "direction": "Inbound", "id": "/planes/radius/local/resourcegroups/default/providers/Applications.Core/httpRoutes/sql-rte" } ], @@ -13,18 +13,19 @@ "type": "Applications.Core/containers" }, { + "connections": [ + { + "direction": "Inbound", + "id": "/planes/radius/local/resourcegroups/default/providers/Applications.Core/containers/sql-app-ctnr" + } + ], "id": "/planes/radius/local/resourcegroups/default/providers/Applications.Datastores/sqlDatabases/sql-db", "name": "sql-db", "provisioningState": "Succeeded", "type": "Applications.Datastores/sqlDatabases" }, { - "connections": [ - { - "direction": "Inbound", - "id": "/planes/radius/local/resourcegroups/default/providers/Applications.Core/containers/sql-ctnr" - } - ], + "connections": [], "id": "/planes/radius/local/resourcegroups/default/providers/Applications.Core/httpRoutes/sql-rte", "name": "sql-rte", "outputResources": [], @@ -34,7 +35,7 @@ { "connections": [ { - "direction": "Inbound", + "direction": "Outbound", "id": "/planes/radius/local/resourcegroups/default/providers/Applications.Datastores/sqlDatabases/sql-db" } ], @@ -44,4 +45,4 @@ "provisioningState": "Succeeded", "type": "Applications.Core/containers" } -] +] \ No newline at end of file diff --git a/pkg/corerp/frontend/controller/applications/testdata/graph-app-httproute2-in.json b/pkg/corerp/frontend/controller/applications/testdata/graph-app-httproute2-in.json new file mode 100644 index 00000000000..aa98ab190bd --- /dev/null +++ b/pkg/corerp/frontend/controller/applications/testdata/graph-app-httproute2-in.json @@ -0,0 +1,72 @@ +[ + { + "id": "/planes/radius/local/resourcegroups/default/providers/Applications.Core/httpRoutes/http-back-rte-simple1", + "name": "http-back-rte-simple1", + "properties": { + "application": "/planes/radius/local/resourcegroups/default/providers/Applications.Core/Applications/myapp", + "provisioningState": "Succeeded" + }, + "type": "Applications.Core/httpRoutes" + }, + { + "id": "/planes/radius/local/resourcegroups/default/providers/Applications.Core/containers/http-front-ctnr-simple1", + "name": "http-front-ctnr-simple1", + "properties": { + "application": "/planes/radius/local/resourcegroups/default/providers/Applications.Core/Applications/myapp", + "containers": { + "image": "magpie:latest", + "ports": { + "web": { + "port": 8080 + } + }, + "readinessProbe": { + "kind": "httpGet", + "path": "/healthz", + "containerPort": 8080 + } + }, + "connections": { + "backend": { + "source": "/planes/radius/local/resourcegroups/default/providers/Applications.Core/httpRoutes/http-back-rte-simple1" + } + }, + "provisioningState": "Succeeded", + "status": { + "outputResources": { + "id": "/some/thing/else", + "localId": "something" + } + } + }, + "type": "Applications.Core/containers" + }, + { + "id": "/planes/radius/local/resourcegroups/default/providers/Applications.Core/containers/http-back-ctnr-simple1", + "name": "http-back-ctnr-simple1", + "properties": { + "application": "/planes/radius/local/resourcegroups/default/providers/Applications.Core/Applications/myapp", + "container": { + "ports": { + "web": { + "port": 8080, + "provides": "/planes/radius/local/resourcegroups/default/providers/Applications.Core/httpRoutes/http-back-rte-simple1" + } + }, + "readinessProbe": { + "kind": "httpGet", + "path": "/healthz", + "containerPort": 8080 + } + }, + "provisioningState": "Succeeded", + "status": { + "outputResources": { + "id": "/some/thing/else", + "localId": "something" + } + } + }, + "type": "Applications.Core/containers" + } +] diff --git a/pkg/corerp/frontend/controller/applications/testdata/graph-app-httproute2-out.json b/pkg/corerp/frontend/controller/applications/testdata/graph-app-httproute2-out.json new file mode 100644 index 00000000000..930c2d9350e --- /dev/null +++ b/pkg/corerp/frontend/controller/applications/testdata/graph-app-httproute2-out.json @@ -0,0 +1,41 @@ +[ + { + "connections": [ + { + "direction": "Inbound", + "id": "/planes/radius/local/resourcegroups/default/providers/Applications.Core/containers/http-front-ctnr-simple1" + } + ], + "id": "/planes/radius/local/resourcegroups/default/providers/Applications.Core/httpRoutes/http-back-rte-simple1", + "name": "http-back-rte-simple1", + "outputResources": [], + "provisioningState": "Succeeded", + "type": "Applications.Core/httpRoutes" + }, + { + "connections": [ + { + "direction": "Outbound", + "id": "/planes/radius/local/resourcegroups/default/providers/Applications.Core/httpRoutes/http-back-rte-simple1" + } + ], + "id": "/planes/radius/local/resourcegroups/default/providers/Applications.Core/containers/http-front-ctnr-simple1", + "name": "http-front-ctnr-simple1", + "outputResources": [], + "provisioningState": "Succeeded", + "type": "Applications.Core/containers" + }, + { + "connections": [ + { + "direction": "Inbound", + "id": "/planes/radius/local/resourcegroups/default/providers/Applications.Core/httpRoutes/http-back-rte-simple1" + } + ], + "id": "/planes/radius/local/resourcegroups/default/providers/Applications.Core/containers/http-back-ctnr-simple1", + "name": "http-back-ctnr-simple1", + "outputResources": [], + "provisioningState": "Succeeded", + "type": "Applications.Core/containers" + } +] \ No newline at end of file diff --git a/test/functional/shared/resources/application_test.go b/test/functional/shared/resources/application_test.go index 11373793d09..3f7b18bd002 100644 --- a/test/functional/shared/resources/application_test.go +++ b/test/functional/shared/resources/application_test.go @@ -18,12 +18,12 @@ package resource_test import ( "context" - "sort" "testing" "github.com/radius-project/radius/test/functional" "github.com/radius-project/radius/test/functional/shared" "github.com/radius-project/radius/test/step" + "github.com/radius-project/radius/test/testutil" "github.com/radius-project/radius/test/validation" aztoken "github.com/radius-project/radius/pkg/azure/tokencredentials" @@ -66,147 +66,6 @@ func Test_ApplicationGraph(t *testing.T) { name := "corerp-application-simple1" appNamespace := "default-corerp-application-simple1" - frontCntrID := "/planes/radius/local/resourcegroups/kind-radius/providers/Applications.Core/containers/http-front-ctnr-simple1" - frontCntrName := "http-front-ctnr-simple1" - frontCntrType := "Applications.Core/containers" - - backCntrID := "/planes/radius/local/resourcegroups/kind-radius/providers/Applications.Core/containers/http-back-ctnr-simple1" - backCntrName := "http-back-ctnr-simple1" - backCntrType := "Applications.Core/containers" - - backRteID := "/planes/radius/local/resourcegroups/kind-radius/providers/Applications.Core/httpRoutes/http-back-rte-simple1" - backRteName := "http-back-rte-simple1" - backRteType := "Applications.Core/httpRoutes" - - backOutputResourceID := "/planes/kubernetes/local/namespaces/default-corerp-application-simple1/providers/apps/Deployment/http-back-ctnr-simple1" - backOutputResourceServiceAccountID := "/planes/kubernetes/local/namespaces/default-corerp-application-simple1/providers/core/ServiceAccount/http-back-ctnr-simple1" - backOutputResourceRoleID := "/planes/kubernetes/local/namespaces/default-corerp-application-simple1/providers/rbac.authorization.k8s.io/Role/http-back-ctnr-simple1" - backOutputResourceRoleBindingID := "/planes/kubernetes/local/namespaces/default-corerp-application-simple1/providers/rbac.authorization.k8s.io/RoleBinding/http-back-ctnr-simple1" - - rteOutputResourceID := "/planes/kubernetes/local/namespaces/default-corerp-application-simple1/providers/core/Service/http-back-rte-simple1" - - frontOutputResourceID := "/planes/kubernetes/local/namespaces/default-corerp-application-simple1/providers/apps/Deployment/http-front-ctnr-simple1" - frontOutputResourceSecretID := "/planes/kubernetes/local/namespaces/default-corerp-application-simple1/providers/core/Secret/http-front-ctnr-simple1" - frontOutputResourceServiceID := "/planes/kubernetes/local/namespaces/default-corerp-application-simple1/providers/core/Service/http-front-ctnr-simple1" - frontOutputResourcesServiceAccountID := "/planes/kubernetes/local/namespaces/default-corerp-application-simple1/providers/core/ServiceAccount/http-front-ctnr-simple1" - frontOutputResourceRoleID := "/planes/kubernetes/local/namespaces/default-corerp-application-simple1/providers/rbac.authorization.k8s.io/Role/http-front-ctnr-simple1" - frontOutputResourceRoleBindingID := "/planes/kubernetes/local/namespaces/default-corerp-application-simple1/providers/rbac.authorization.k8s.io/RoleBinding/http-front-ctnr-simple1" - - provisioningStateSuccess := "Succeeded" - directionInbound := v20231001preview.DirectionInbound - directionOutbound := v20231001preview.DirectionOutbound - - deploymentType := "apps/Deployment" - serviceAccountType := "core/ServiceAccount" - roleType := "rbac.authorization.k8s.io/Role" - serviceType := "core/Service" - secretType := "core/Secret" - roleBindingType := "rbac.authorization.k8s.io/RoleBinding" - - expectedGraphResp := v20231001preview.ApplicationsClientGetGraphResponse{ - ApplicationGraphResponse: v20231001preview.ApplicationGraphResponse{ - Resources: []*v20231001preview.ApplicationGraphResource{ - { - ID: &frontCntrID, - Name: &frontCntrName, - Type: &frontCntrType, - ProvisioningState: &provisioningStateSuccess, - OutputResources: []*v20231001preview.ApplicationGraphOutputResource{ - { - ID: &frontOutputResourceID, - Name: &frontCntrName, - Type: &deploymentType, - }, - { - ID: &frontOutputResourceSecretID, - Name: &frontCntrName, - Type: &secretType, - }, - { - ID: &frontOutputResourceServiceID, - Name: &frontCntrName, - Type: &serviceType, - }, - { - ID: &frontOutputResourcesServiceAccountID, - Name: &frontCntrName, - Type: &serviceAccountType, - }, - { - ID: &frontOutputResourceRoleID, - Name: &frontCntrName, - Type: &roleType, - }, - { - ID: &frontOutputResourceRoleBindingID, - Name: &frontCntrName, - Type: &roleBindingType, - }, - }, - Connections: []*v20231001preview.ApplicationGraphConnection{ - { - Direction: &directionInbound, - ID: &backRteID, - }, - }, - }, - { - ID: &backCntrID, - Name: &backCntrName, - Type: &backCntrType, - ProvisioningState: &provisioningStateSuccess, - OutputResources: []*v20231001preview.ApplicationGraphOutputResource{ - { - ID: &backOutputResourceID, - Name: &backCntrName, - Type: &deploymentType, - }, - { - ID: &backOutputResourceServiceAccountID, - Name: &backCntrName, - Type: &serviceAccountType, - }, - { - ID: &backOutputResourceRoleID, - Name: &backCntrName, - Type: &roleType, - }, - { - ID: &backOutputResourceRoleBindingID, - Name: &backCntrName, - Type: &roleBindingType, - }, - }, - Connections: []*v20231001preview.ApplicationGraphConnection{ - { - Direction: &directionOutbound, - ID: &backRteID, - }, - }, - }, - { - ID: &backRteID, - Name: &backRteName, - Type: &backRteType, - ProvisioningState: &provisioningStateSuccess, - Connections: []*v20231001preview.ApplicationGraphConnection{ - { - Direction: &directionInbound, - ID: &backCntrID, - }, - }, - OutputResources: []*v20231001preview.ApplicationGraphOutputResource{ - { - ID: &rteOutputResourceID, - Name: &backRteName, - Type: &serviceType, - }, - }, - }, - }, - }, - } - test := shared.NewRPTest(t, name, []shared.TestStep{ { Executor: step.NewDeployExecutor(template, functional.GetMagpieImage()), @@ -247,29 +106,18 @@ func Test_ApplicationGraph(t *testing.T) { options := shared.NewRPTestOptions(t) client := options.ManagementClient require.IsType(t, client, &clients.UCPApplicationsManagementClient{}) + appManagementClient := client.(*clients.UCPApplicationsManagementClient) appGraphClient, err := v20231001preview.NewApplicationsClient(appManagementClient.RootScope, &aztoken.AnonymousCredential{}, appManagementClient.ClientOptions) require.NoError(t, err) + res, err := appGraphClient.GetGraph(ctx, "corerp-application-simple1", map[string]any{}, nil) require.NoError(t, err) - sort.Slice(expectedGraphResp.ApplicationGraphResponse.Resources, func(i, j int) bool { - return *expectedGraphResp.ApplicationGraphResponse.Resources[i].ID < *expectedGraphResp.ApplicationGraphResponse.Resources[j].ID - }) - - sort.Slice(res.ApplicationGraphResponse.Resources, func(i, j int) bool { - return *res.ApplicationGraphResponse.Resources[i].ID < *res.ApplicationGraphResponse.Resources[j].ID - }) - - sort.Slice(expectedGraphResp.ApplicationGraphResponse.Resources, func(i, j int) bool { - return *expectedGraphResp.ApplicationGraphResponse.Resources[i].ID < *expectedGraphResp.ApplicationGraphResponse.Resources[j].ID - }) - - sort.Slice(res.ApplicationGraphResponse.Resources, func(i, j int) bool { - return *res.ApplicationGraphResponse.Resources[i].ID < *res.ApplicationGraphResponse.Resources[j].ID - }) - - require.Equal(t, expectedGraphResp, res) + // assert that the graph is as expected + expected := []*v20231001preview.ApplicationGraphResource{} + testutil.MustUnmarshalFromFile("corerp-resources-application-graph-out.json", &expected) + require.ElementsMatch(t, expected, res.Resources) }, }, }) diff --git a/test/functional/shared/resources/testdata/corerp-resources-application-graph-out.json b/test/functional/shared/resources/testdata/corerp-resources-application-graph-out.json new file mode 100644 index 00000000000..e0dc5e74eab --- /dev/null +++ b/test/functional/shared/resources/testdata/corerp-resources-application-graph-out.json @@ -0,0 +1,99 @@ +[ + { + "connections": [ + { + "direction": "Inbound", + "id": "/planes/radius/local/resourcegroups/kind-radius/providers/Applications.Core/containers/http-front-ctnr-simple1" + } + ], + "id": "/planes/radius/local/resourcegroups/kind-radius/providers/Applications.Core/httpRoutes/http-back-rte-simple1", + "name": "http-back-rte-simple1", + "outputResources": [ + { + "id": "/planes/kubernetes/local/namespaces/default-corerp-application-simple1/providers/core/Service/http-back-rte-simple1", + "name": "http-back-rte-simple1", + "type": "core/Service" + } + ], + "provisioningState": "Succeeded", + "type": "Applications.Core/httpRoutes" + }, + { + "connections": [ + { + "direction": "Inbound", + "id": "/planes/radius/local/resourcegroups/kind-radius/providers/Applications.Core/httpRoutes/http-back-rte-simple1" + } + ], + "id": "/planes/radius/local/resourcegroups/kind-radius/providers/Applications.Core/containers/http-back-ctnr-simple1", + "name": "http-back-ctnr-simple1", + "outputResources": [ + { + "id": "/planes/kubernetes/local/namespaces/default-corerp-application-simple1/providers/apps/Deployment/http-back-ctnr-simple1", + "name": "http-back-ctnr-simple1", + "type": "apps/Deployment" + }, + { + "id": "/planes/kubernetes/local/namespaces/default-corerp-application-simple1/providers/core/ServiceAccount/http-back-ctnr-simple1", + "name": "http-back-ctnr-simple1", + "type": "core/ServiceAccount" + }, + { + "id": "/planes/kubernetes/local/namespaces/default-corerp-application-simple1/providers/rbac.authorization.k8s.io/Role/http-back-ctnr-simple1", + "name": "http-back-ctnr-simple1", + "type": "rbac.authorization.k8s.io/Role" + }, + { + "id": "/planes/kubernetes/local/namespaces/default-corerp-application-simple1/providers/rbac.authorization.k8s.io/RoleBinding/http-back-ctnr-simple1", + "name": "http-back-ctnr-simple1", + "type": "rbac.authorization.k8s.io/RoleBinding" + } + ], + "provisioningState": "Succeeded", + "type": "Applications.Core/containers" + }, + { + "connections": [ + { + "direction": "Outbound", + "id": "/planes/radius/local/resourcegroups/kind-radius/providers/Applications.Core/httpRoutes/http-back-rte-simple1" + } + ], + "id": "/planes/radius/local/resourcegroups/kind-radius/providers/Applications.Core/containers/http-front-ctnr-simple1", + "name": "http-front-ctnr-simple1", + "outputResources": [ + { + "id": "/planes/kubernetes/local/namespaces/default-corerp-application-simple1/providers/apps/Deployment/http-front-ctnr-simple1", + "name": "http-front-ctnr-simple1", + "type": "apps/Deployment" + }, + { + "id": "/planes/kubernetes/local/namespaces/default-corerp-application-simple1/providers/core/Secret/http-front-ctnr-simple1", + "name": "http-front-ctnr-simple1", + "type": "core/Secret" + }, + { + "id": "/planes/kubernetes/local/namespaces/default-corerp-application-simple1/providers/core/Service/http-front-ctnr-simple1", + "name": "http-front-ctnr-simple1", + "type": "core/Service" + }, + { + "id": "/planes/kubernetes/local/namespaces/default-corerp-application-simple1/providers/core/ServiceAccount/http-front-ctnr-simple1", + "name": "http-front-ctnr-simple1", + "type": "core/ServiceAccount" + }, + { + "id": "/planes/kubernetes/local/namespaces/default-corerp-application-simple1/providers/rbac.authorization.k8s.io/Role/http-front-ctnr-simple1", + "name": "http-front-ctnr-simple1", + "type": "rbac.authorization.k8s.io/Role" + }, + { + "id": "/planes/kubernetes/local/namespaces/default-corerp-application-simple1/providers/rbac.authorization.k8s.io/RoleBinding/http-front-ctnr-simple1", + "name": "http-front-ctnr-simple1", + "type": "rbac.authorization.k8s.io/RoleBinding" + } + ], + "provisioningState": "Succeeded", + "type": "Applications.Core/containers" + } +] \ No newline at end of file