From ea5bb66f64c99cf90be4b0eab65af7246318b51b Mon Sep 17 00:00:00 2001 From: sgayangi Date: Fri, 23 Aug 2024 15:24:50 +0530 Subject: [PATCH 01/17] Add adapter changes for gRPC APIs --- .../internal/oasparser/constants/constants.go | 1 + .../oasparser/envoyconf/internal_dtos.go | 4 +- .../envoyconf/routes_with_clusters.go | 97 +++++- .../envoyconf/routes_with_clusters_test.go | 36 +- .../oasparser/model/adapter_internal_api.go | 251 ++++++++++---- adapter/internal/oasparser/model/common.go | 2 +- .../internal/oasparser/model/http_route.go | 3 +- .../config/crd/bases/dp.wso2.com_apis.yaml | 195 ++++++++++- .../internal/operator/constants/constants.go | 5 + .../operator/controllers/dp/api_controller.go | 315 ++++++++++++++---- adapter/internal/operator/operator.go | 6 +- .../operator/synchronizer/api_state.go | 16 +- .../operator/synchronizer/data_store.go | 107 ++++++ .../operator/synchronizer/grpc_api.go | 185 ++++++++++ .../operator/synchronizer/synchronizer.go | 41 +++ .../synchronizer/zz_generated.deepcopy.go | 71 +++- adapter/internal/operator/utils/utils.go | 21 +- adapter/pkg/logging/logging_constant.go | 1 + 18 files changed, 1179 insertions(+), 178 deletions(-) create mode 100644 adapter/internal/operator/synchronizer/grpc_api.go diff --git a/adapter/internal/oasparser/constants/constants.go b/adapter/internal/oasparser/constants/constants.go index 38b0c23bc1..ef8ecce263 100644 --- a/adapter/internal/oasparser/constants/constants.go +++ b/adapter/internal/oasparser/constants/constants.go @@ -105,6 +105,7 @@ const ( SOAP string = "SOAP" WS string = "WS" GRAPHQL string = "GraphQL" + GRPC string = "gRPC" WEBHOOK string = "WEBHOOK" SSE string = "SSE" Prototyped string = "prototyped" diff --git a/adapter/internal/oasparser/envoyconf/internal_dtos.go b/adapter/internal/oasparser/envoyconf/internal_dtos.go index 0cb5f6b2b2..7e27725679 100644 --- a/adapter/internal/oasparser/envoyconf/internal_dtos.go +++ b/adapter/internal/oasparser/envoyconf/internal_dtos.go @@ -19,7 +19,7 @@ package envoyconf import ( "github.com/wso2/apk/adapter/internal/oasparser/model" - dpv1alpha2 "github.com/wso2/apk/common-go-libs/apis/dp/v1alpha2" + "github.com/wso2/apk/common-go-libs/apis/dp/v1beta1" ) // routeCreateParams is the DTO used to provide information to the envoy route create function @@ -41,7 +41,7 @@ type routeCreateParams struct { isDefaultVersion bool createDefaultPath bool apiLevelRateLimitPolicy *model.RateLimitPolicy - apiProperties []dpv1alpha2.Property + apiProperties []v1beta1.Property environment string envType string mirrorClusterNames map[string][]string diff --git a/adapter/internal/oasparser/envoyconf/routes_with_clusters.go b/adapter/internal/oasparser/envoyconf/routes_with_clusters.go index 157b51a770..59e8c3f10c 100644 --- a/adapter/internal/oasparser/envoyconf/routes_with_clusters.go +++ b/adapter/internal/oasparser/envoyconf/routes_with_clusters.go @@ -40,8 +40,8 @@ import ( cors_filter_v3 "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/cors/v3" extAuthService "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/ext_authz/v3" extProcessorv3 "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/ext_proc/v3" - ratelimitv3 "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/ratelimit/v3" lua "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/lua/v3" + ratelimitv3 "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/ratelimit/v3" tlsv3 "github.com/envoyproxy/go-control-plane/envoy/extensions/transport_sockets/tls/v3" upstreams "github.com/envoyproxy/go-control-plane/envoy/extensions/upstreams/http/v3" envoy_type_matcherv3 "github.com/envoyproxy/go-control-plane/envoy/type/matcher/v3" @@ -54,7 +54,7 @@ import ( logging "github.com/wso2/apk/adapter/internal/logging" "github.com/wso2/apk/adapter/internal/oasparser/constants" "github.com/wso2/apk/adapter/internal/oasparser/model" - dpv1alpha2 "github.com/wso2/apk/common-go-libs/apis/dp/v1alpha2" + "github.com/wso2/apk/common-go-libs/apis/dp/v1beta1" "google.golang.org/protobuf/proto" gwapiv1 "sigs.k8s.io/gateway-api/apis/v1" ) @@ -92,6 +92,7 @@ const ( // to the api level clusters. func CreateRoutesWithClusters(adapterInternalAPI *model.AdapterInternalAPI, interceptorCerts map[string][]byte, vHost string, organizationID string) (routesP []*routev3.Route, clustersP []*clusterv3.Cluster, addressesP []*corev3.Address, err error) { + var ( routes []*routev3.Route clusters []*clusterv3.Cluster @@ -199,6 +200,77 @@ func CreateRoutesWithClusters(adapterInternalAPI *model.AdapterInternalAPI, inte } return routes, clusters, endpoints, nil } + + if adapterInternalAPI.GetAPIType() == constants.GRPC { + basePath := strings.TrimSuffix(adapterInternalAPI.Endpoints.Endpoints[0].Basepath, "/") + + clusterName := getClusterName(adapterInternalAPI.Endpoints.EndpointPrefix, organizationID, vHost, + adapterInternalAPI.GetTitle(), apiVersion, "") + adapterInternalAPI.Endpoints.HTTP2BackendEnabled = true + cluster, address, err := processEndpoints(clusterName, adapterInternalAPI.Endpoints, timeout, basePath) + if err != nil { + logger.LoggerOasparser.ErrorC(logging.PrintError(logging.Error2239, logging.MAJOR, + "Error while adding grpc endpoints for %s:%v. %v", apiTitle, apiVersion, err.Error())) + return nil, nil, nil, fmt.Errorf("error while adding grpc endpoints for %s:%v. %v", apiTitle, apiVersion, + err.Error()) + } + clusters = append(clusters, cluster) + endpoints = append(endpoints, address...) + + for _, resource := range adapterInternalAPI.GetResources() { + var clusterName string + resourcePath := resource.GetPath() + endpoint := resource.GetEndpoints() + endpoint.HTTP2BackendEnabled = true + basePath := strings.TrimSuffix(endpoint.Endpoints[0].Basepath, "/") + existingClusterName := getExistingClusterName(*endpoint, processedEndpoints) + + if existingClusterName == "" { + clusterName = getClusterName(endpoint.EndpointPrefix, organizationID, vHost, adapterInternalAPI.GetTitle(), apiVersion, resource.GetID()) + cluster, address, err := processEndpoints(clusterName, endpoint, timeout, basePath) + if err != nil { + logger.LoggerOasparser.ErrorC(logging.PrintError(logging.Error2239, logging.MAJOR, "Error while adding resource level endpoints for %s:%v-%v. %v", apiTitle, apiVersion, resourcePath, err.Error())) + } else { + clusters = append(clusters, cluster) + endpoints = append(endpoints, address...) + processedEndpoints[clusterName] = *endpoint + } + } else { + clusterName = existingClusterName + } + + // Create resource level interceptor clusters if required + clustersI, endpointsI, operationalReqInterceptors, operationalRespInterceptorVal := createInterceptorResourceClusters(adapterInternalAPI, + interceptorCerts, vHost, organizationID, apiRequestInterceptor, apiResponseInterceptor, resource) + clusters = append(clusters, clustersI...) + endpoints = append(endpoints, endpointsI...) + routeParams := genRouteCreateParams(adapterInternalAPI, resource, vHost, basePath, clusterName, *operationalReqInterceptors, *operationalRespInterceptorVal, organizationID, + false, false, nil) + + routeP, err := createRoutes(routeParams) + if err != nil { + logger.LoggerXds.ErrorC(logging.PrintError(logging.Error2231, logging.MAJOR, + "Error while creating routes for GRPC API %s %s for path: %s Error: %s", adapterInternalAPI.GetTitle(), + adapterInternalAPI.GetVersion(), resource.GetPath(), err.Error())) + return nil, nil, nil, fmt.Errorf("error while creating routes. %v", err) + } + routes = append(routes, routeP...) + if adapterInternalAPI.IsDefaultVersion { + defaultRoutes, errDefaultPath := createRoutes(genRouteCreateParams(adapterInternalAPI, resource, vHost, basePath, clusterName, + *operationalReqInterceptors, *operationalRespInterceptorVal, organizationID, + false, true, nil)) + if errDefaultPath != nil { + logger.LoggerXds.ErrorC(logging.PrintError(logging.Error2231, logging.MAJOR, "Error while creating routes for GRPC API %s %s for path: %s Error: %s", + adapterInternalAPI.GetTitle(), adapterInternalAPI.GetVersion(), removeFirstOccurrence(resource.GetPath(), adapterInternalAPI.GetVersion()), errDefaultPath.Error())) + return nil, nil, nil, fmt.Errorf("error while creating routes. %v", errDefaultPath) + } + routes = append(routes, defaultRoutes...) + } + } + + return routes, clusters, endpoints, nil + } + for _, resource := range adapterInternalAPI.GetResources() { var clusterName string mirrorClusterNames := map[string][]string{} @@ -884,9 +956,9 @@ func createRoutes(params *routeCreateParams) (routes []*routev3.Route, err error Override: &extProcessorv3.ExtProcPerRoute_Overrides{ Overrides: &extProcessorv3.ExtProcOverrides{ ProcessingMode: &extProcessorv3.ProcessingMode{ - RequestHeaderMode: extProcessorv3.ProcessingMode_SKIP, + RequestHeaderMode: extProcessorv3.ProcessingMode_SKIP, ResponseHeaderMode: extProcessorv3.ProcessingMode_SEND, - ResponseBodyMode: extProcessorv3.ProcessingMode_NONE, + ResponseBodyMode: extProcessorv3.ProcessingMode_NONE, }, }, }, @@ -1099,6 +1171,11 @@ func createRoutes(params *routeCreateParams) (routes []*routev3.Route, err error } } + var mirrorClusterNameList []string + if mirrorClusterNames != nil && mirrorClusterNames[operation.GetID()] != nil { + mirrorClusterNameList = mirrorClusterNames[operation.GetID()] + } + // TODO: (suksw) preserve header key case? if hasMethodRewritePolicy { logger.LoggerOasparser.Debugf("Creating two routes to support method rewrite for %s %s. New method: %s", @@ -1116,10 +1193,10 @@ func createRoutes(params *routeCreateParams) (routes []*routev3.Route, err error metadataValue := operation.GetMethod() + "_to_" + newMethod match2.DynamicMetadata = generateMetadataMatcherForInternalRoutes(metadataValue) - action1 := generateRouteAction(apiType, routeConfig, rateLimitPolicyCriteria, mirrorClusterNames[operation.GetID()], resource.GetEnableBackendBasedAIRatelimit() && params.isAiAPI, resource.GetBackendBasedAIRatelimitDescriptorValue()) - action2 := generateRouteAction(apiType, routeConfig, rateLimitPolicyCriteria, mirrorClusterNames[operation.GetID()], resource.GetEnableBackendBasedAIRatelimit() && params.isAiAPI, resource.GetBackendBasedAIRatelimitDescriptorValue()) + action1 := generateRouteAction(apiType, routeConfig, rateLimitPolicyCriteria, mirrorClusterNameList, resource.GetEnableBackendBasedAIRatelimit() && params.isAiAPI, resource.GetBackendBasedAIRatelimitDescriptorValue()) + action2 := generateRouteAction(apiType, routeConfig, rateLimitPolicyCriteria, mirrorClusterNameList, resource.GetEnableBackendBasedAIRatelimit() && params.isAiAPI, resource.GetBackendBasedAIRatelimitDescriptorValue()) - requestHeadersToRemove := make([]string,0) + requestHeadersToRemove := make([]string, 0) // Create route1 for current method. // Do not add policies to route config. Send via enforcer route1 := generateRouteConfig(xWso2Basepath+operation.GetMethod(), match1, action1, requestRedirectAction, metaData, decorator, perRouteFilterConfigs, @@ -1141,7 +1218,7 @@ func createRoutes(params *routeCreateParams) (routes []*routev3.Route, err error } else { var action *routev3.Route_Route if requestRedirectAction == nil { - action = generateRouteAction(apiType, routeConfig, rateLimitPolicyCriteria, mirrorClusterNames[operation.GetID()], resource.GetEnableBackendBasedAIRatelimit() && params.isAiAPI, resource.GetBackendBasedAIRatelimitDescriptorValue()) + action = generateRouteAction(apiType, routeConfig, rateLimitPolicyCriteria, mirrorClusterNameList, resource.GetEnableBackendBasedAIRatelimit() && params.isAiAPI, resource.GetBackendBasedAIRatelimitDescriptorValue()) } logger.LoggerOasparser.Debug("Creating routes for resource with policies", resourcePath, operation.GetMethod()) // create route for current method. Add policies to route config. Send via enforcer @@ -1170,7 +1247,7 @@ func createRoutes(params *routeCreateParams) (routes []*routev3.Route, err error action := generateRouteAction(apiType, routeConfig, rateLimitPolicyCriteria, nil, resource.GetEnableBackendBasedAIRatelimit() && params.isAiAPI, resource.GetBackendBasedAIRatelimitDescriptorValue()) rewritePath := generateRoutePathForReWrite(basePath, resourcePath, pathMatchType) action.Route.RegexRewrite = generateRegexMatchAndSubstitute(rewritePath, resourcePath, pathMatchType) - requestHeadersToRemove := make([]string,0) + requestHeadersToRemove := make([]string, 0) route := generateRouteConfig(xWso2Basepath, match, action, nil, metaData, decorator, perRouteFilterConfigs, nil, requestHeadersToRemove, nil, nil) // general headers to add and remove are included in this methods routes = append(routes, route) @@ -1632,7 +1709,7 @@ func getUpgradeConfig(apiType string) []*routev3.RouteAction_UpgradeConfig { return upgradeConfig } -func getAPIProperties(apiPropertiesConfig []dpv1alpha2.Property) string { +func getAPIProperties(apiPropertiesConfig []v1beta1.Property) string { var apiProperties = make(map[string]string) for _, val := range apiPropertiesConfig { apiProperties[val.Name] = val.Value diff --git a/adapter/internal/oasparser/envoyconf/routes_with_clusters_test.go b/adapter/internal/oasparser/envoyconf/routes_with_clusters_test.go index ab26811a40..4ef7526a2d 100644 --- a/adapter/internal/oasparser/envoyconf/routes_with_clusters_test.go +++ b/adapter/internal/oasparser/envoyconf/routes_with_clusters_test.go @@ -39,16 +39,16 @@ import ( func TestCreateRoutesWithClustersWithExactAndRegularExpressionRules(t *testing.T) { apiState := synchronizer.APIState{} - apiDefinition := v1alpha2.API{ + apiDefinition := v1beta1.API{ ObjectMeta: metav1.ObjectMeta{ Namespace: "default", Name: "test-api-2", }, - Spec: v1alpha2.APISpec{ + Spec: v1beta1.APISpec{ APIName: "test-api-2", APIVersion: "2.0.0", BasePath: "/test-api/2.0.0", - Production: []v1alpha2.EnvConfig{ + Production: []v1beta1.EnvConfig{ { RouteRefs: []string{ "test-api-2-prod-http-route", @@ -217,16 +217,16 @@ func TestExtractAPIDetailsFromHTTPRouteForSpecificEnvironment(t *testing.T) { func generateSampleAPI(apiName string, apiVersion string, basePath string) synchronizer.APIState { apiState := synchronizer.APIState{} - apiDefinition := v1alpha2.API{ + apiDefinition := v1beta1.API{ ObjectMeta: metav1.ObjectMeta{ Namespace: "default", Name: apiName, }, - Spec: v1alpha2.APISpec{ + Spec: v1beta1.APISpec{ APIName: apiName, APIVersion: apiVersion, BasePath: basePath, - Production: []v1alpha2.EnvConfig{ + Production: []v1beta1.EnvConfig{ { RouteRefs: []string{ apiName + "-prod-http-route", @@ -283,16 +283,16 @@ func generateSampleAPI(apiName string, apiVersion string, basePath string) synch // TODO: Fix this test case func TestCreateRoutesWithClustersWithMultiplePathPrefixRules(t *testing.T) { apiState := synchronizer.APIState{} - apiDefinition := v1alpha2.API{ + apiDefinition := v1beta1.API{ ObjectMeta: metav1.ObjectMeta{ Namespace: "default", Name: "test-api-1", }, - Spec: v1alpha2.APISpec{ + Spec: v1beta1.APISpec{ APIName: "test-api", APIVersion: "1.0.0", BasePath: "/test-api/1.0.0", - Production: []v1alpha2.EnvConfig{ + Production: []v1beta1.EnvConfig{ { RouteRefs: []string{ "test-api-1-prod-http-route", @@ -434,16 +434,16 @@ func TestCreateRoutesWithClustersWithMultiplePathPrefixRules(t *testing.T) { func TestCreateRoutesWithClustersWithBackendTLSConfigs(t *testing.T) { apiState := synchronizer.APIState{} - apiDefinition := v1alpha2.API{ + apiDefinition := v1beta1.API{ ObjectMeta: metav1.ObjectMeta{ Namespace: "default", Name: "test-api-3", }, - Spec: v1alpha2.APISpec{ + Spec: v1beta1.APISpec{ APIName: "test-api-3", APIVersion: "1.0.0", BasePath: "/test-api-3/1.0.0", - Production: []v1alpha2.EnvConfig{ + Production: []v1beta1.EnvConfig{ { RouteRefs: []string{ "test-api-3-prod-http-route", @@ -559,16 +559,16 @@ func TestCreateHealthEndpoint(t *testing.T) { func TestCreateRoutesWithClustersDifferentBackendRefs(t *testing.T) { apiState := synchronizer.APIState{} - apiDefinition := v1alpha2.API{ + apiDefinition := v1beta1.API{ ObjectMeta: metav1.ObjectMeta{ Namespace: "default", Name: "test-api-different-backendrefs", }, - Spec: v1alpha2.APISpec{ + Spec: v1beta1.APISpec{ APIName: "test-api-different-backendrefs", APIVersion: "1.0.0", BasePath: "/test-api-different-backendrefs/1.0.0", - Production: []v1alpha2.EnvConfig{ + Production: []v1beta1.EnvConfig{ { RouteRefs: []string{ "test-api-different-backendrefs-prod-http-route", @@ -653,16 +653,16 @@ func TestCreateRoutesWithClustersDifferentBackendRefs(t *testing.T) { func TestCreateRoutesWithClustersSameBackendRefs(t *testing.T) { apiState := synchronizer.APIState{} - apiDefinition := v1alpha2.API{ + apiDefinition := v1beta1.API{ ObjectMeta: metav1.ObjectMeta{ Namespace: "default", Name: "test-api-same-backendrefs", }, - Spec: v1alpha2.APISpec{ + Spec: v1beta1.APISpec{ APIName: "test-api-same-backendrefs", APIVersion: "1.0.0", BasePath: "/test-api-same-backendrefs/1.0.0", - Production: []v1alpha2.EnvConfig{ + Production: []v1beta1.EnvConfig{ { RouteRefs: []string{ "test-api-same-backendrefs-prod-http-route", diff --git a/adapter/internal/oasparser/model/adapter_internal_api.go b/adapter/internal/oasparser/model/adapter_internal_api.go index 0a50bbff3f..80747b8151 100644 --- a/adapter/internal/oasparser/model/adapter_internal_api.go +++ b/adapter/internal/oasparser/model/adapter_internal_api.go @@ -34,9 +34,11 @@ import ( dpv1alpha1 "github.com/wso2/apk/common-go-libs/apis/dp/v1alpha1" dpv1alpha2 "github.com/wso2/apk/common-go-libs/apis/dp/v1alpha2" dpv1alpha3 "github.com/wso2/apk/common-go-libs/apis/dp/v1alpha3" + "github.com/wso2/apk/common-go-libs/apis/dp/v1beta1" "golang.org/x/exp/maps" "k8s.io/apimachinery/pkg/types" gwapiv1 "sigs.k8s.io/gateway-api/apis/v1" + gwapiv1a2 "sigs.k8s.io/gateway-api/apis/v1alpha2" ) // AdapterInternalAPI represents the object structure holding the information related to the @@ -72,7 +74,7 @@ type AdapterInternalAPI struct { apiDefinitionFile []byte apiDefinitionEndpoint string subscriptionValidation bool - APIProperties []dpv1alpha2.Property + APIProperties []v1beta1.Property // GraphQLSchema string // GraphQLComplexities GraphQLComplexityYaml IsSystemAPI bool @@ -1038,65 +1040,10 @@ func (adapterInternalAPI *AdapterInternalAPI) SetInfoGQLRouteCR(gqlRoute *dpv1al } resolvedBackend, ok := resourceParams.BackendMapping[backendName.String()] if ok { - endpointConfig := &EndpointConfig{} - if resolvedBackend.CircuitBreaker != nil { - endpointConfig.CircuitBreakers = &CircuitBreakers{ - MaxConnections: int32(resolvedBackend.CircuitBreaker.MaxConnections), - MaxRequests: int32(resolvedBackend.CircuitBreaker.MaxRequests), - MaxPendingRequests: int32(resolvedBackend.CircuitBreaker.MaxPendingRequests), - MaxRetries: int32(resolvedBackend.CircuitBreaker.MaxRetries), - MaxConnectionPools: int32(resolvedBackend.CircuitBreaker.MaxConnectionPools), - } - } - if resolvedBackend.Timeout != nil { - endpointConfig.TimeoutInMillis = resolvedBackend.Timeout.UpstreamResponseTimeout * 1000 - endpointConfig.IdleTimeoutInSeconds = resolvedBackend.Timeout.DownstreamRequestIdleTimeout - } - if resolvedBackend.Retry != nil { - statusCodes := config.Envoy.Upstream.Retry.StatusCodes - if len(resolvedBackend.Retry.StatusCodes) > 0 { - statusCodes = resolvedBackend.Retry.StatusCodes - } - endpointConfig.RetryConfig = &RetryConfig{ - Count: int32(resolvedBackend.Retry.Count), - StatusCodes: statusCodes, - BaseIntervalInMillis: int32(resolvedBackend.Retry.BaseIntervalMillis), - } - } - adapterInternalAPI.Endpoints = &EndpointCluster{ - Endpoints: GetEndpoints(backendName, resourceParams.BackendMapping), - Config: endpointConfig, - } - if resolvedBackend.HealthCheck != nil { - adapterInternalAPI.Endpoints.HealthCheck = &HealthCheck{ - Interval: resolvedBackend.HealthCheck.Interval, - Timeout: resolvedBackend.HealthCheck.Timeout, - UnhealthyThreshold: resolvedBackend.HealthCheck.UnhealthyThreshold, - HealthyThreshold: resolvedBackend.HealthCheck.HealthyThreshold, - } - } - - var securityConfig []EndpointSecurity - switch resolvedBackend.Security.Type { - case "Basic": - securityConfig = append(securityConfig, EndpointSecurity{ - Password: string(resolvedBackend.Security.Basic.Password), - Username: string(resolvedBackend.Security.Basic.Username), - Type: string(resolvedBackend.Security.Type), - Enabled: true, - }) - case "APIKey": - securityConfig = append(securityConfig, EndpointSecurity{ - Type: string(resolvedBackend.Security.Type), - Enabled: true, - CustomParameters: map[string]string{ - "in": string(resolvedBackend.Security.APIKey.In), - "key": string(resolvedBackend.Security.APIKey.Name), - "value": string(resolvedBackend.Security.APIKey.Value), - }, - }) - } + securityConfig, healthCheck, endpointCluster := setEndpointConfigs(resolvedBackend, config.Envoy.Upstream.Retry.StatusCodes, backendName, resourceParams) adapterInternalAPI.EndpointSecurity = utils.GetPtrSlice(securityConfig) + adapterInternalAPI.Endpoints.HealthCheck = healthCheck + adapterInternalAPI.Endpoints = endpointCluster } else { return fmt.Errorf("backend: %s has not been resolved", backendName) } @@ -1172,6 +1119,192 @@ func (adapterInternalAPI *AdapterInternalAPI) SetInfoGQLRouteCR(gqlRoute *dpv1al return nil } +// SetInfoGRPCRouteCR populates resources and endpoints of adapterInternalAPI. httpRoute.Spec.Rules.Matches +// are used to create resources and httpRoute.Spec.Rules.BackendRefs are used to create EndpointClusters. +func (adapterInternalAPI *AdapterInternalAPI) SetInfoGRPCRouteCR(grpcRoute *gwapiv1a2.GRPCRoute, resourceParams ResourceParams) error { + var resources []*Resource + outputAuthScheme := utils.TieBreaker(utils.GetPtrSlice(maps.Values(resourceParams.AuthSchemes))) + outputAPIPolicy := utils.TieBreaker(utils.GetPtrSlice(maps.Values(resourceParams.APIPolicies))) + outputRatelimitPolicy := utils.TieBreaker(utils.GetPtrSlice(maps.Values(resourceParams.RateLimitPolicies))) + + disableScopes := true + config := config.ReadConfigs() + + var authScheme *dpv1alpha2.Authentication + if outputAuthScheme != nil { + authScheme = *outputAuthScheme + } + var apiPolicy *dpv1alpha2.APIPolicy + if outputAPIPolicy != nil { + apiPolicy = *outputAPIPolicy + } + var ratelimitPolicy *dpv1alpha1.RateLimitPolicy + if outputRatelimitPolicy != nil { + ratelimitPolicy = *outputRatelimitPolicy + } + + backend := grpcRoute.Spec.Rules[0].BackendRefs[0] + backendName := types.NamespacedName{ + Name: string(backend.Name), + Namespace: utils.GetNamespace(backend.Namespace, grpcRoute.Namespace), + } + resolvedBackend, ok := resourceParams.BackendMapping[backendName.String()] + if ok { + securityConfig, healthCheck, endpointCluster := setEndpointConfigs(resolvedBackend, config.Envoy.Upstream.Retry.StatusCodes, backendName, resourceParams) + adapterInternalAPI.EndpointSecurity = utils.GetPtrSlice(securityConfig) + if healthCheck != nil { + adapterInternalAPI.Endpoints.HealthCheck = healthCheck + } + if endpointCluster != nil { + adapterInternalAPI.Endpoints = endpointCluster + } + } else { + return fmt.Errorf("backend: %s has not been resolved", backendName) + } + + for _, rule := range grpcRoute.Spec.Rules { + var policies = OperationPolicies{} + var endPoints []Endpoint + resourceAuthScheme := authScheme + resourceRatelimitPolicy := ratelimitPolicy + var scopes []string + for _, filter := range rule.Filters { + if filter.ExtensionRef != nil && filter.ExtensionRef.Kind == constants.KindAuthentication { + if ref, found := resourceParams.ResourceAuthSchemes[types.NamespacedName{ + Name: string(filter.ExtensionRef.Name), + Namespace: grpcRoute.Namespace, + }.String()]; found { + resourceAuthScheme = concatAuthSchemes(authScheme, &ref) + } else { + return fmt.Errorf(`auth scheme: %s has not been resolved, spec.targetRef.kind should be + 'Resource' in resource level Authentications`, filter.ExtensionRef.Name) + } + } + if filter.ExtensionRef != nil && filter.ExtensionRef.Kind == constants.KindScope { + if ref, found := resourceParams.ResourceScopes[types.NamespacedName{ + Name: string(filter.ExtensionRef.Name), + Namespace: grpcRoute.Namespace, + }.String()]; found { + scopes = ref.Spec.Names + disableScopes = false + } else { + return fmt.Errorf("scope: %s has not been resolved in namespace %s", filter.ExtensionRef.Name, grpcRoute.Namespace) + } + } + if filter.ExtensionRef != nil && filter.ExtensionRef.Kind == constants.KindRateLimitPolicy { + if ref, found := resourceParams.ResourceRateLimitPolicies[types.NamespacedName{ + Name: string(filter.ExtensionRef.Name), + Namespace: grpcRoute.Namespace, + }.String()]; found { + resourceRatelimitPolicy = concatRateLimitPolicies(ratelimitPolicy, &ref) + } else { + return fmt.Errorf(`ratelimitpolicy: %s has not been resolved, spec.targetRef.kind should be + 'Resource' in resource level RateLimitPolicies`, filter.ExtensionRef.Name) + } + } + } + resourceAuthScheme = concatAuthSchemes(resourceAuthScheme, nil) + resourceRatelimitPolicy = concatRateLimitPolicies(resourceRatelimitPolicy, nil) + + loggers.LoggerOasparser.Debugf("Calculating auths for API ..., API_UUID = %v", adapterInternalAPI.UUID) + apiAuth := getSecurity(resourceAuthScheme) + + for _, match := range rule.Matches { + resourcePath := adapterInternalAPI.GetXWso2Basepath() + "." + *match.Method.Service + "/" + *match.Method.Method + endPoints = append(endPoints, GetEndpoints(backendName, resourceParams.BackendMapping)...) + resource := &Resource{path: resourcePath, pathMatchType: "Exact", + methods: []*Operation{{iD: uuid.New().String(), method: "post", policies: policies, + auth: apiAuth, rateLimitPolicy: parseRateLimitPolicyToInternal(resourceRatelimitPolicy), scopes: scopes}}, + iD: uuid.New().String(), + } + endpoints := GetEndpoints(backendName, resourceParams.BackendMapping) + resource.endpoints = &EndpointCluster{ + Endpoints: endpoints, + } + resources = append(resources, resource) + } + } + + ratelimitPolicy = concatRateLimitPolicies(ratelimitPolicy, nil) + apiPolicy = concatAPIPolicies(apiPolicy, nil) + authScheme = concatAuthSchemes(authScheme, nil) + + adapterInternalAPI.RateLimitPolicy = parseRateLimitPolicyToInternal(ratelimitPolicy) + adapterInternalAPI.resources = resources + adapterInternalAPI.xWso2Cors = getCorsConfigFromAPIPolicy(apiPolicy) + if authScheme.Spec.Override != nil && authScheme.Spec.Override.Disabled != nil { + adapterInternalAPI.disableAuthentications = *authScheme.Spec.Override.Disabled + } + authSpec := utils.SelectPolicy(&authScheme.Spec.Override, &authScheme.Spec.Default, nil, nil) + if authSpec != nil && authSpec.AuthTypes != nil && authSpec.AuthTypes.Oauth2.Required != "" { + adapterInternalAPI.SetXWSO2ApplicationSecurity(authSpec.AuthTypes.Oauth2.Required == "mandatory") + } else { + adapterInternalAPI.SetXWSO2ApplicationSecurity(true) + } + adapterInternalAPI.disableScopes = disableScopes + // Check whether the API has a backend JWT token + if apiPolicy != nil && apiPolicy.Spec.Override != nil && apiPolicy.Spec.Override.BackendJWTPolicy != nil { + backendJWTPolicy := resourceParams.BackendJWTMapping[types.NamespacedName{ + Name: apiPolicy.Spec.Override.BackendJWTPolicy.Name, + Namespace: grpcRoute.Namespace, + }.String()].Spec + adapterInternalAPI.backendJWTTokenInfo = parseBackendJWTTokenToInternal(backendJWTPolicy) + } + return nil +} + +func setEndpointConfigs(resolvedBackend *dpv1alpha1.ResolvedBackend, statusCodes []uint32, backendName types.NamespacedName, resourceParams ResourceParams) ([]EndpointSecurity, *HealthCheck, *EndpointCluster) { + endpointConfig := &EndpointConfig{} + if resolvedBackend.CircuitBreaker != nil { + endpointConfig.CircuitBreakers = &CircuitBreakers{ + MaxConnections: int32(resolvedBackend.CircuitBreaker.MaxConnections), + MaxRequests: int32(resolvedBackend.CircuitBreaker.MaxRequests), + MaxPendingRequests: int32(resolvedBackend.CircuitBreaker.MaxPendingRequests), + MaxRetries: int32(resolvedBackend.CircuitBreaker.MaxRetries), + MaxConnectionPools: int32(resolvedBackend.CircuitBreaker.MaxConnectionPools), + } + } + if resolvedBackend.Timeout != nil { + endpointConfig.TimeoutInMillis = resolvedBackend.Timeout.UpstreamResponseTimeout * 1000 + endpointConfig.IdleTimeoutInSeconds = resolvedBackend.Timeout.DownstreamRequestIdleTimeout + } + if resolvedBackend.Retry != nil { + if len(resolvedBackend.Retry.StatusCodes) > 0 { + statusCodes = resolvedBackend.Retry.StatusCodes + } + endpointConfig.RetryConfig = &RetryConfig{ + Count: int32(resolvedBackend.Retry.Count), + StatusCodes: statusCodes, + BaseIntervalInMillis: int32(resolvedBackend.Retry.BaseIntervalMillis), + } + } + endpointCluster := &EndpointCluster{ + Endpoints: GetEndpoints(backendName, resourceParams.BackendMapping), + Config: endpointConfig, + } + var healthcheck *HealthCheck + if resolvedBackend.HealthCheck != nil { + healthcheck = &HealthCheck{ + Interval: resolvedBackend.HealthCheck.Interval, + Timeout: resolvedBackend.HealthCheck.Timeout, + UnhealthyThreshold: resolvedBackend.HealthCheck.UnhealthyThreshold, + HealthyThreshold: resolvedBackend.HealthCheck.HealthyThreshold, + } + } + + var securityConfig []EndpointSecurity + switch resolvedBackend.Security.Type { + case "Basic": + securityConfig = append(securityConfig, EndpointSecurity{ + Password: string(resolvedBackend.Security.Basic.Password), + Username: string(resolvedBackend.Security.Basic.Username), + Type: string(resolvedBackend.Security.Type), + Enabled: true, + }) + } + return securityConfig, healthcheck, endpointCluster +} + func (endpoint *Endpoint) validateEndpoint() error { if endpoint.Port == 0 || endpoint.Port > 65535 { return errors.New("endpoint port value should be between 0 and 65535") diff --git a/adapter/internal/oasparser/model/common.go b/adapter/internal/oasparser/model/common.go index d1d3ba6646..8c5c2f54cc 100644 --- a/adapter/internal/oasparser/model/common.go +++ b/adapter/internal/oasparser/model/common.go @@ -108,7 +108,7 @@ func getHostandBasepathandPort(apiType string, rawURL string) (*Endpoint, error) rawURL = strings.Trim(rawURL, " ") if !strings.Contains(rawURL, "://") { - if apiType == constants.REST || apiType == constants.GRAPHQL { + if apiType == constants.REST || apiType == constants.GRAPHQL || apiType == constants.GRPC { rawURL = "http://" + rawURL } else if apiType == constants.WS { rawURL = "ws://" + rawURL diff --git a/adapter/internal/oasparser/model/http_route.go b/adapter/internal/oasparser/model/http_route.go index feccb11d47..081ed2a00f 100644 --- a/adapter/internal/oasparser/model/http_route.go +++ b/adapter/internal/oasparser/model/http_route.go @@ -25,6 +25,7 @@ import ( dpv1alpha1 "github.com/wso2/apk/common-go-libs/apis/dp/v1alpha1" dpv1alpha2 "github.com/wso2/apk/common-go-libs/apis/dp/v1alpha2" dpv1alpha3 "github.com/wso2/apk/common-go-libs/apis/dp/v1alpha3" + "github.com/wso2/apk/common-go-libs/apis/dp/v1beta1" "k8s.io/apimachinery/pkg/types" gwapiv1 "sigs.k8s.io/gateway-api/apis/v1" ) @@ -310,7 +311,7 @@ func getAllowedOperations(httpMethod *gwapiv1.HTTPMethod, policies OperationPoli } // SetInfoAPICR populates ID, ApiType, Version and XWso2BasePath of adapterInternalAPI. -func (swagger *AdapterInternalAPI) SetInfoAPICR(api dpv1alpha2.API) { +func (swagger *AdapterInternalAPI) SetInfoAPICR(api v1beta1.API) { swagger.UUID = string(api.ObjectMeta.UID) swagger.title = api.Spec.APIName swagger.apiType = api.Spec.APIType diff --git a/adapter/internal/operator/config/crd/bases/dp.wso2.com_apis.yaml b/adapter/internal/operator/config/crd/bases/dp.wso2.com_apis.yaml index fc4e0a2a9f..d24c5cafa0 100644 --- a/adapter/internal/operator/config/crd/bases/dp.wso2.com_apis.yaml +++ b/adapter/internal/operator/config/crd/bases/dp.wso2.com_apis.yaml @@ -328,7 +328,200 @@ spec: description: APIStatus defines the observed state of API properties: deploymentStatus: - description: DeploymentStatus denotes the deployment status of the + description: + DeploymentStatus denotes the deployment status of the + API + properties: + accepted: + description: + Accepted represents whether the API is accepted or + not. + type: boolean + events: + description: Events contains a list of events related to the API. + items: + type: string + type: array + message: + description: + Message represents a user friendly message that explains + the current state of the API. + type: string + status: + description: + Status denotes the state of the API in its lifecycle. + Possible values could be Accepted, Invalid, Deploy etc. + type: string + transitionTime: + description: + TransitionTime represents the last known transition + timestamp. + format: date-time + type: string + required: + - accepted + - status + - transitionTime + type: object + type: object + type: object + served: true + storage: false + subresources: + status: {} + - additionalPrinterColumns: + - jsonPath: .spec.apiName + name: API Name + type: string + - jsonPath: .spec.apiVersion + name: Version + type: string + - jsonPath: .spec.basePath + name: BasePath + type: string + - jsonPath: .spec.organization + name: Organization + type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1beta1 + schema: + openAPIV3Schema: + description: API is the Schema for the apis API + properties: + apiVersion: + description: + "APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources" + type: string + kind: + description: + "Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds" + type: string + metadata: + type: object + spec: + description: APISpec defines the desired state of API + properties: + apiName: + description: + APIName is the unique name of the API can be used to + uniquely identify an API. + maxLength: 60 + minLength: 1 + pattern: ^[^~!@#;:%^*()+={}|\<>"'',&$\[\]\/]*$ + type: string + apiProperties: + description: APIProperties denotes the custom properties of the API. + items: + description: Property holds key value pair of APIProperties + properties: + name: + type: string + value: + type: string + type: object + nullable: true + type: array + apiType: + description: + APIType denotes the type of the API. Possible values + could be REST, GraphQL, gRPC + enum: + - REST + - GraphQL + - gRPC + type: string + apiVersion: + description: APIVersion is the version number of the API. + maxLength: 30 + minLength: 1 + pattern: ^[^~!@#;:%^*()+={}|\<>"'',&/$\[\]\s+\/]+$ + type: string + basePath: + description: "BasePath denotes the basepath of the API. e.g: /pet-store-api/1.0.6" + pattern: ^[/][a-zA-Z0-9~/_.-]*$ + type: string + definitionFileRef: + description: + DefinitionFileRef contains the definition of the API + in a ConfigMap. + type: string + definitionPath: + default: /api-definition + description: DefinitionPath contains the path to expose the API definition. + minLength: 1 + type: string + environment: + description: Environment denotes the environment of the API. + nullable: true + type: string + isDefaultVersion: + description: + IsDefaultVersion indicates whether this API version should + be used as a default API + type: boolean + organization: + description: + Organization denotes the organization. related to the + API + type: string + production: + description: + "Production contains a list of references to HttpRoutes + of type HttpRoute. xref: https://github.com/kubernetes-sigs/gateway-api/blob/main/apis/v1beta1/httproute_types.go" + items: + description: EnvConfig contains the environment specific configuration + properties: + routeRefs: + description: RouteRefs denotes the environment of the API. + items: + type: string + type: array + required: + - routeRefs + type: object + maxItems: 1 + nullable: true + type: array + sandbox: + description: + "Sandbox contains a list of references to HttpRoutes + of type HttpRoute. xref: https://github.com/kubernetes-sigs/gateway-api/blob/main/apis/v1beta1/httproute_types.go" + items: + description: EnvConfig contains the environment specific configuration + properties: + routeRefs: + description: RouteRefs denotes the environment of the API. + items: + type: string + type: array + required: + - routeRefs + type: object + maxItems: 1 + nullable: true + type: array + systemAPI: + description: SystemAPI denotes if it is an internal system API. + type: boolean + required: + - apiName + - apiType + - apiVersion + - basePath + - definitionPath + type: object + status: + description: APIStatus defines the observed state of API + properties: + deploymentStatus: + description: + DeploymentStatus denotes the deployment status of the API properties: accepted: diff --git a/adapter/internal/operator/constants/constants.go b/adapter/internal/operator/constants/constants.go index cbafee9184..b642648de3 100644 --- a/adapter/internal/operator/constants/constants.go +++ b/adapter/internal/operator/constants/constants.go @@ -68,3 +68,8 @@ const ( GlobalRequestInterceptorClusterName = "request_interceptor_global_cluster" GlobalResponseInterceptorClusterName = "response_interceptor_global_cluster" ) + +// API Types +const ( + GRPC = "gRPC" +) diff --git a/adapter/internal/operator/controllers/dp/api_controller.go b/adapter/internal/operator/controllers/dp/api_controller.go index 11f40d58e2..595990d594 100644 --- a/adapter/internal/operator/controllers/dp/api_controller.go +++ b/adapter/internal/operator/controllers/dp/api_controller.go @@ -33,6 +33,7 @@ import ( "strings" "sync" + "github.com/sirupsen/logrus" "github.com/wso2/apk/adapter/config" "github.com/wso2/apk/adapter/internal/controlplane" "github.com/wso2/apk/adapter/internal/discovery/xds" @@ -47,6 +48,8 @@ import ( k8error "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/fields" "k8s.io/apimachinery/pkg/types" + ctrl "sigs.k8s.io/controller-runtime" + k8client "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/controller" "sigs.k8s.io/controller-runtime/pkg/handler" "sigs.k8s.io/controller-runtime/pkg/manager" @@ -54,13 +57,12 @@ import ( "sigs.k8s.io/controller-runtime/pkg/reconcile" "sigs.k8s.io/controller-runtime/pkg/source" gwapiv1 "sigs.k8s.io/gateway-api/apis/v1" - - ctrl "sigs.k8s.io/controller-runtime" - k8client "sigs.k8s.io/controller-runtime/pkg/client" + gwapiv1a2 "sigs.k8s.io/gateway-api/apis/v1alpha2" dpv1alpha1 "github.com/wso2/apk/common-go-libs/apis/dp/v1alpha1" dpv1alpha2 "github.com/wso2/apk/common-go-libs/apis/dp/v1alpha2" dpv1alpha3 "github.com/wso2/apk/common-go-libs/apis/dp/v1alpha3" + dpv1beta1 "github.com/wso2/apk/common-go-libs/apis/dp/v1beta1" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) @@ -68,6 +70,7 @@ import ( const ( httpRouteAPIIndex = "httpRouteAPIIndex" gqlRouteAPIIndex = "gqlRouteAPIIndex" + grpcRouteAPIIndex = "grpcRouteAPIIndex" // apiAuthenticationIndex Index for API level authentications apiAuthenticationIndex = "apiAuthenticationIndex" // apiAuthenticationResourceIndex Index for resource level authentications @@ -78,26 +81,30 @@ const ( apiRateLimitResourceIndex = "apiRateLimitResourceIndex" // gatewayHTTPRouteIndex Index for gateway httproutes gatewayHTTPRouteIndex = "gatewayHTTPRouteIndex" + // gatewayGRPCRouteIndex Index for gateway grpcroutes + gatewayGRPCRouteIndex = "gatewayGRPCRouteIndex" // apiAPIPolicyIndex Index for API level apipolicies apiAPIPolicyIndex = "apiAPIPolicyIndex" // apiAPIPolicyResourceIndex Index for resource level apipolicies - apiAPIPolicyResourceIndex = "apiAPIPolicyResourceIndex" - serviceHTTPRouteIndex = "serviceHTTPRouteIndex" - httprouteScopeIndex = "httprouteScopeIndex" - gqlRouteScopeIndex = "gqlRouteScopeIndex" - configMapBackend = "configMapBackend" - configMapAPIDefinition = "configMapAPIDefinition" - secretBackend = "secretBackend" - configMapAuthentication = "configMapAuthentication" - secretAuthentication = "secretAuthentication" - backendHTTPRouteIndex = "backendHTTPRouteIndex" - backendGQLRouteIndex = "backendGQLRouteIndex" - interceptorServiceAPIPolicyIndex = "interceptorServiceAPIPolicyIndex" - backendInterceptorServiceIndex = "backendInterceptorServiceIndex" - backendJWTAPIPolicyIndex = "backendJWTAPIPolicyIndex" - aiRatelimitPolicyToBackendIndex = "aiRatelimitPolicyToBackendIndex" - subscriptionToAPIIndex = "subscriptionToAPIIndex" - apiToSubscriptionIndex = "apiToSubscriptionIndex" + apiAPIPolicyResourceIndex = "apiAPIPolicyResourceIndex" + serviceHTTPRouteIndex = "serviceHTTPRouteIndex" + httprouteScopeIndex = "httprouteScopeIndex" + grpcRouteScopeIndex = "grpcRouteScopeIndex" + gqlRouteScopeIndex = "gqlRouteScopeIndex" + configMapBackend = "configMapBackend" + configMapAPIDefinition = "configMapAPIDefinition" + secretBackend = "secretBackend" + configMapAuthentication = "configMapAuthentication" + secretAuthentication = "secretAuthentication" + backendHTTPRouteIndex = "backendHTTPRouteIndex" + backendGQLRouteIndex = "backendGQLRouteIndex" + backendGRPCRouteIndex = "backendGRPCRouteIndex" + interceptorServiceAPIPolicyIndex = "interceptorServiceAPIPolicyIndex" + backendInterceptorServiceIndex = "backendInterceptorServiceIndex" + backendJWTAPIPolicyIndex = "backendJWTAPIPolicyIndex" + aiRatelimitPolicyToBackendIndex = "aiRatelimitPolicyToBackendIndex" + subscriptionToAPIIndex = "subscriptionToAPIIndex" + apiToSubscriptionIndex = "apiToSubscriptionIndex" aiProviderAPIPolicyIndex = "aiProviderAPIPolicyIndex" ) @@ -116,7 +123,7 @@ type APIReconciler struct { apiPropagationEnabled bool } -// NewAPIController creates a new API controller instance. API Controllers watches for dpv1alpha2.API and gwapiv1.HTTPRoute. +// NewAPIController creates a new API controller instance. API Controllers watches for dpv1beta1.API and gwapiv1.HTTPRoute. func NewAPIController(mgr manager.Manager, operatorDataStore *synchronizer.OperatorDataStore, statusUpdater *status.UpdateHandler, ch *chan *synchronizer.APIEvent, successChannel *chan synchronizer.SuccessEvent) error { apiReconciler := &APIReconciler{ @@ -138,7 +145,7 @@ func NewAPIController(mgr manager.Manager, operatorDataStore *synchronizer.Opera apiReconciler.apiPropagationEnabled = conf.Adapter.ControlPlane.EnableAPIPropagation predicates := []predicate.Predicate{predicate.NewPredicateFuncs(utils.FilterByNamespaces(conf.Adapter.Operator.Namespaces))} - if err := c.Watch(source.Kind(mgr.GetCache(), &dpv1alpha2.API{}), &handler.EnqueueRequestForObject{}, + if err := c.Watch(source.Kind(mgr.GetCache(), &dpv1beta1.API{}), &handler.EnqueueRequestForObject{}, predicates...); err != nil { loggers.LoggerAPKOperator.ErrorC(logging.PrintError(logging.Error2611, logging.BLOCKER, "Error watching API resources: %v", err)) return err @@ -160,6 +167,12 @@ func NewAPIController(mgr manager.Manager, operatorDataStore *synchronizer.Opera return err } + if err := c.Watch(source.Kind(mgr.GetCache(), &gwapiv1a2.GRPCRoute{}), handler.EnqueueRequestsFromMapFunc(apiReconciler.populateAPIReconcileRequestsForGRPCRoute), + predicates...); err != nil { + loggers.LoggerAPKOperator.ErrorC(logging.PrintError(logging.Error2668, logging.BLOCKER, "Error watching GRPCRoute resources: %v", err)) + return err + } + if err := c.Watch(source.Kind(mgr.GetCache(), &gwapiv1.Gateway{}), handler.EnqueueRequestsFromMapFunc(apiReconciler.getAPIsForGateway), predicates...); err != nil { loggers.LoggerAPKOperator.ErrorC(logging.PrintError(logging.Error2611, logging.BLOCKER, "Error watching API resources: %v", err)) @@ -249,6 +262,9 @@ func NewAPIController(mgr manager.Manager, operatorDataStore *synchronizer.Opera // +kubebuilder:rbac:groups=dp.wso2.com,resources=authentications,verbs=get;list;watch;create;update;patch;delete // +kubebuilder:rbac:groups=dp.wso2.com,resources=authentications/status,verbs=get;update;patch // +kubebuilder:rbac:groups=dp.wso2.com,resources=authentications/finalizers,verbs=update +// +kubebuilder:rbac:groups=dp.wso2.com,resources=grpcroutes,verbs=get;list;watch;create;update;patch;delete +// +kubebuilder:rbac:groups=dp.wso2.com,resources=grpcroutes/status,verbs=get;update;patch +// +kubebuilder:rbac:groups=dp.wso2.com,resources=grpcroutes/finalizers,verbs=update // +kubebuilder:rbac:groups=dp.wso2.com,resources=apipolicies,verbs=get;list;watch;create;update;patch;delete // +kubebuilder:rbac:groups=dp.wso2.com,resources=apipolicies/status,verbs=get;update;patch // +kubebuilder:rbac:groups=dp.wso2.com,resources=apipolicies/finalizers,verbs=update @@ -271,7 +287,7 @@ func (apiReconciler *APIReconciler) Reconcile(ctx context.Context, req ctrl.Requ applyAllAPIsOnce.Do(apiReconciler.applyStartupAPIs) loggers.LoggerAPKOperator.Infof("Reconciling for API %s", req.NamespacedName.String()) // Check whether the API CR exist, if not consider as a DELETE event. - var apiCR dpv1alpha2.API + var apiCR dpv1beta1.API if err := apiReconciler.client.Get(ctx, req.NamespacedName, &apiCR); err != nil { apiState, found := apiReconciler.ods.GetCachedAPI(req.NamespacedName) if found && k8error.IsNotFound(err) { @@ -338,7 +354,7 @@ func (apiReconciler *APIReconciler) applyStartupAPIs() { // resolveAPIRefs validates following references related to the API // - HTTPRoutes -func (apiReconciler *APIReconciler) resolveAPIRefs(ctx context.Context, api dpv1alpha2.API) (*synchronizer.APIEvent, error) { +func (apiReconciler *APIReconciler) resolveAPIRefs(ctx context.Context, api dpv1beta1.API) (*synchronizer.APIEvent, error) { var prodRouteRefs, sandRouteRefs []string if len(api.Spec.Production) > 0 { prodRouteRefs = api.Spec.Production[0].RouteRefs @@ -450,6 +466,22 @@ func (apiReconciler *APIReconciler) resolveAPIRefs(ctx context.Context, api dpv1 } } + // handle gRPC APIs + if len(prodRouteRefs) > 0 && apiState.APIDefinition.Spec.APIType == "gRPC" { + if apiState.ProdGRPCRoute, err = apiReconciler.resolveGRPCRouteRefs(ctx, prodRouteRefs, + namespace, api); err != nil { + return nil, fmt.Errorf("error while resolving production grpcRouteref %s in namespace :%s has not found. %s", + prodRouteRefs, namespace, err.Error()) + } + } + if len(sandRouteRefs) > 0 && apiState.APIDefinition.Spec.APIType == "gRPC" { + if apiState.SandGRPCRoute, err = apiReconciler.resolveGRPCRouteRefs(ctx, sandRouteRefs, + namespace, api); err != nil { + return nil, fmt.Errorf("error while resolving sandbox grpcRouteref %s in namespace :%s has not found. %s", + sandRouteRefs, namespace, err.Error()) + } + } + // Validate resource level extension refs resolved extRefValErr := apiReconciler.validateRouteExtRefs(apiState) if extRefValErr != nil { @@ -509,6 +541,11 @@ func (apiReconciler *APIReconciler) resolveAPIRefs(ctx context.Context, api dpv1 func isAPIPropagatable(apiState *synchronizer.APIState) bool { validOrgs := []string{"carbon.super"} + + if apiState.APIDefinition.Spec.APIType == constants.GRPC { + return false + } + // System APIs should not be propagated to CP if apiState.APIDefinition.Spec.SystemAPI { return false @@ -521,7 +558,7 @@ func isAPIPropagatable(apiState *synchronizer.APIState) bool { } func (apiReconciler *APIReconciler) resolveGQLRouteRefs(ctx context.Context, gqlRouteRefs []string, - namespace string, api dpv1alpha2.API) (*synchronizer.GQLRouteState, error) { + namespace string, api dpv1beta1.API) (*synchronizer.GQLRouteState, error) { gqlRouteState, err := apiReconciler.concatGQLRoutes(ctx, gqlRouteRefs, namespace, api) if err != nil { return nil, err @@ -534,7 +571,7 @@ func (apiReconciler *APIReconciler) resolveGQLRouteRefs(ctx context.Context, gql // - Authentications func (apiReconciler *APIReconciler) resolveHTTPRouteRefs(ctx context.Context, httpRouteState *synchronizer.HTTPRouteState, httpRouteRefs []string, namespace string, interceptorServiceMapping map[string]dpv1alpha1.InterceptorService, - api dpv1alpha2.API) (*synchronizer.HTTPRouteState, error) { + api dpv1beta1.API) (*synchronizer.HTTPRouteState, error) { var err error httpRouteState.HTTPRouteCombined, httpRouteState.HTTPRoutePartitions, err = apiReconciler.concatHTTPRoutes(ctx, httpRouteRefs, namespace, api) if err != nil { @@ -549,8 +586,53 @@ func (apiReconciler *APIReconciler) resolveHTTPRouteRefs(ctx context.Context, ht return httpRouteState, err } +func (apiReconciler *APIReconciler) resolveGRPCRouteRefs(ctx context.Context, grpcRouteRefs []string, + namespace string, api dpv1beta1.API) (*synchronizer.GRPCRouteState, error) { + grpcRouteState, err := apiReconciler.concatGRPCRoutes(ctx, grpcRouteRefs, namespace, api) + if err != nil { + return nil, err + } + grpcRouteState.Scopes, err = apiReconciler.getScopesForGRPCRoute(ctx, grpcRouteState.GRPCRouteCombined, api) + return &grpcRouteState, err +} + +func (apiReconciler *APIReconciler) concatGRPCRoutes(ctx context.Context, grpcRouteRefs []string, + namespace string, api dpv1beta1.API) (synchronizer.GRPCRouteState, error) { + grpcRouteState := synchronizer.GRPCRouteState{} + grpcRoutePartitions := make(map[string]*gwapiv1a2.GRPCRoute) + for _, grpcRouteRef := range grpcRouteRefs { + var grpcRoute gwapiv1a2.GRPCRoute + namespacedName := types.NamespacedName{Namespace: namespace, Name: grpcRouteRef} + if err := utils.ResolveRef(ctx, apiReconciler.client, &api, namespacedName, true, &grpcRoute); err != nil { + return grpcRouteState, fmt.Errorf("error while getting grpcroute %s in namespace :%s, %s", grpcRouteRef, + namespace, err.Error()) + } + grpcRoutePartitions[namespacedName.String()] = &grpcRoute + if grpcRouteState.GRPCRouteCombined == nil { + grpcRouteState.GRPCRouteCombined = &grpcRoute + } else { + grpcRouteState.GRPCRouteCombined.Spec.Rules = append(grpcRouteState.GRPCRouteCombined.Spec.Rules, + grpcRoute.Spec.Rules...) + } + } + grpcRouteState.GRPCRoutePartitions = grpcRoutePartitions + backendNamespacedName := types.NamespacedName{ + //TODO check if this is correct + Name: string(grpcRouteState.GRPCRouteCombined.Spec.Rules[0].BackendRefs[0].BackendRef.Name), + Namespace: namespace, + } + resolvedBackend := utils.GetResolvedBackend(ctx, apiReconciler.client, backendNamespacedName, &api) + if resolvedBackend != nil { + grpcRouteState.BackendMapping = map[string]*dpv1alpha1.ResolvedBackend{ + backendNamespacedName.String(): resolvedBackend, + } + return grpcRouteState, nil + } + return grpcRouteState, errors.New("error while resolving backend for grpcroute") +} + func (apiReconciler *APIReconciler) concatGQLRoutes(ctx context.Context, gqlRouteRefs []string, - namespace string, api dpv1alpha2.API) (synchronizer.GQLRouteState, error) { + namespace string, api dpv1beta1.API) (synchronizer.GQLRouteState, error) { gqlRouteState := synchronizer.GQLRouteState{} gqlRoutePartitions := make(map[string]*dpv1alpha2.GQLRoute) for _, gqlRouteRef := range gqlRouteRefs { @@ -583,8 +665,28 @@ func (apiReconciler *APIReconciler) concatGQLRoutes(ctx context.Context, gqlRout return gqlRouteState, errors.New("error while resolving backend for gqlroute") } +func (apiReconciler *APIReconciler) getScopesForGRPCRoute(ctx context.Context, + grpcRoute *gwapiv1a2.GRPCRoute, api dpv1beta1.API) (map[string]dpv1alpha1.Scope, error) { + scopes := make(map[string]dpv1alpha1.Scope) + for _, rule := range grpcRoute.Spec.Rules { + for _, filter := range rule.Filters { + if filter.ExtensionRef != nil && filter.ExtensionRef.Kind == constants.KindScope { + scope := &dpv1alpha1.Scope{} + if err := utils.ResolveRef(ctx, apiReconciler.client, &api, + types.NamespacedName{Namespace: grpcRoute.Namespace, Name: string(filter.ExtensionRef.Name)}, false, + scope); err != nil { + return nil, fmt.Errorf("error while getting scope %s in namespace :%s, %s", filter.ExtensionRef.Name, + grpcRoute.Namespace, err.Error()) + } + scopes[utils.NamespacedName(scope).String()] = *scope + } + } + } + return scopes, nil +} + func (apiReconciler *APIReconciler) concatHTTPRoutes(ctx context.Context, httpRouteRefs []string, - namespace string, api dpv1alpha2.API) (*gwapiv1.HTTPRoute, map[string]*gwapiv1.HTTPRoute, error) { + namespace string, api dpv1beta1.API) (*gwapiv1.HTTPRoute, map[string]*gwapiv1.HTTPRoute, error) { var combinedHTTPRoute *gwapiv1.HTTPRoute httpRoutePartitions := make(map[string]*gwapiv1.HTTPRoute) for _, httpRouteRef := range httpRouteRefs { @@ -605,7 +707,7 @@ func (apiReconciler *APIReconciler) concatHTTPRoutes(ctx context.Context, httpRo } func (apiReconciler *APIReconciler) getAuthenticationsForAPI(ctx context.Context, - api dpv1alpha2.API) (map[string]dpv1alpha2.Authentication, error) { + api dpv1beta1.API) (map[string]dpv1alpha2.Authentication, error) { nameSpacedName := utils.NamespacedName(&api).String() authentications := make(map[string]dpv1alpha2.Authentication) authenticationList := &dpv1alpha2.AuthenticationList{} @@ -622,7 +724,7 @@ func (apiReconciler *APIReconciler) getAuthenticationsForAPI(ctx context.Context } func (apiReconciler *APIReconciler) getRatelimitPoliciesForAPI(ctx context.Context, - api dpv1alpha2.API) (map[string]dpv1alpha3.RateLimitPolicy, error) { + api dpv1beta1.API) (map[string]dpv1alpha3.RateLimitPolicy, error) { nameSpacedName := utils.NamespacedName(&api).String() ratelimitPolicies := make(map[string]dpv1alpha3.RateLimitPolicy) ratelimitPolicyList := &dpv1alpha3.RateLimitPolicyList{} @@ -639,7 +741,7 @@ func (apiReconciler *APIReconciler) getRatelimitPoliciesForAPI(ctx context.Conte } func (apiReconciler *APIReconciler) getScopesForGQLRoute(ctx context.Context, - gqlRoute *dpv1alpha2.GQLRoute, api dpv1alpha2.API) (map[string]dpv1alpha1.Scope, error) { + gqlRoute *dpv1alpha2.GQLRoute, api dpv1beta1.API) (map[string]dpv1alpha1.Scope, error) { scopes := make(map[string]dpv1alpha1.Scope) for _, rule := range gqlRoute.Spec.Rules { for _, filter := range rule.Filters { @@ -659,7 +761,7 @@ func (apiReconciler *APIReconciler) getScopesForGQLRoute(ctx context.Context, } func (apiReconciler *APIReconciler) getScopesForHTTPRoute(ctx context.Context, - httpRoute *gwapiv1.HTTPRoute, api dpv1alpha2.API) (map[string]dpv1alpha1.Scope, error) { + httpRoute *gwapiv1.HTTPRoute, api dpv1beta1.API) (map[string]dpv1alpha1.Scope, error) { scopes := make(map[string]dpv1alpha1.Scope) for _, rule := range httpRoute.Spec.Rules { for _, filter := range rule.Filters { @@ -681,7 +783,7 @@ func (apiReconciler *APIReconciler) getScopesForHTTPRoute(ctx context.Context, } func (apiReconciler *APIReconciler) getAuthenticationsForResources(ctx context.Context, - api dpv1alpha2.API) (map[string]dpv1alpha2.Authentication, error) { + api dpv1beta1.API) (map[string]dpv1alpha2.Authentication, error) { nameSpacedName := utils.NamespacedName(&api).String() authentications := make(map[string]dpv1alpha2.Authentication) authenticationList := &dpv1alpha2.AuthenticationList{} @@ -698,7 +800,7 @@ func (apiReconciler *APIReconciler) getAuthenticationsForResources(ctx context.C } func (apiReconciler *APIReconciler) getRatelimitPoliciesForResources(ctx context.Context, - api dpv1alpha2.API) (map[string]dpv1alpha3.RateLimitPolicy, error) { + api dpv1beta1.API) (map[string]dpv1alpha3.RateLimitPolicy, error) { nameSpacedName := utils.NamespacedName(&api).String() ratelimitpolicies := make(map[string]dpv1alpha3.RateLimitPolicy) ratelimitPolicyList := &dpv1alpha3.RateLimitPolicyList{} @@ -714,7 +816,7 @@ func (apiReconciler *APIReconciler) getRatelimitPoliciesForResources(ctx context return ratelimitpolicies, nil } -func (apiReconciler *APIReconciler) getAPIPoliciesForAPI(ctx context.Context, api dpv1alpha2.API) (map[string]dpv1alpha3.APIPolicy, error) { +func (apiReconciler *APIReconciler) getAPIPoliciesForAPI(ctx context.Context, api dpv1beta1.API) (map[string]dpv1alpha3.APIPolicy, error) { nameSpacedName := utils.NamespacedName(&api).String() apiPolicies := make(map[string]dpv1alpha3.APIPolicy) apiPolicyList := &dpv1alpha3.APIPolicyList{} @@ -732,7 +834,7 @@ func (apiReconciler *APIReconciler) getAPIPoliciesForAPI(ctx context.Context, ap } func (apiReconciler *APIReconciler) getAPIDefinitionForAPI(ctx context.Context, - apiDefinitionFile, namespace string, api dpv1alpha2.API) ([]byte, error) { + apiDefinitionFile, namespace string, api dpv1beta1.API) ([]byte, error) { configMap := &corev1.ConfigMap{} if err := utils.ResolveRef(ctx, apiReconciler.client, &api, types.NamespacedName{Namespace: namespace, Name: apiDefinitionFile}, true, configMap); err != nil { @@ -749,7 +851,7 @@ func (apiReconciler *APIReconciler) getAPIDefinitionForAPI(ctx context.Context, } func (apiReconciler *APIReconciler) getAPIPoliciesForResources(ctx context.Context, - api dpv1alpha2.API) (map[string]dpv1alpha3.APIPolicy, error) { + api dpv1beta1.API) (map[string]dpv1alpha3.APIPolicy, error) { nameSpacedName := utils.NamespacedName(&api).String() apiPolicies := make(map[string]dpv1alpha3.APIPolicy) apiPolicyList := &dpv1alpha3.APIPolicyList{} @@ -771,7 +873,7 @@ func (apiReconciler *APIReconciler) getAPIPoliciesForResources(ctx context.Conte // - subscription validation func (apiReconciler *APIReconciler) getAPIPolicyChildrenRefs(ctx context.Context, apiPolicies, resourceAPIPolicies map[string]dpv1alpha3.APIPolicy, - api dpv1alpha2.API) (map[string]dpv1alpha1.InterceptorService, map[string]dpv1alpha1.BackendJWT, bool, *dpv1alpha3.AIProvider, error) { + api dpv1beta1.API) (map[string]dpv1alpha1.InterceptorService, map[string]dpv1alpha1.BackendJWT, bool, *dpv1alpha3.AIProvider, error) { allAPIPolicies := append(maps.Values(apiPolicies), maps.Values(resourceAPIPolicies)...) interceptorServices := make(map[string]dpv1alpha1.InterceptorService) backendJWTs := make(map[string]dpv1alpha1.BackendJWT) @@ -860,7 +962,7 @@ func (apiReconciler *APIReconciler) resolveAuthentications(ctx context.Context, func (apiReconciler *APIReconciler) getResolvedBackendsMapping(ctx context.Context, httpRouteState *synchronizer.HTTPRouteState, interceptorServiceMapping map[string]dpv1alpha1.InterceptorService, - api dpv1alpha2.API) (map[string]*dpv1alpha2.ResolvedBackend, error) { + api dpv1beta1.API) (map[string]*dpv1alpha2.ResolvedBackend, error) { backendMapping := make(map[string]*dpv1alpha2.ResolvedBackend) // Resolve backends in HTTPRoute @@ -956,6 +1058,12 @@ func (apiReconciler *APIReconciler) populateAPIReconcileRequestsForHTTPRoute(ctx return requests } +func (apiReconciler *APIReconciler) populateAPIReconcileRequestsForGRPCRoute(ctx context.Context, obj k8client.Object) []reconcile.Request { + requests := apiReconciler.getAPIForGRPCRoute(ctx, obj) + apiReconciler.handleOwnerReference(ctx, obj, &requests) + return requests +} + func (apiReconciler *APIReconciler) populateAPIReconcileRequestsForConfigMap(ctx context.Context, obj k8client.Object) []reconcile.Request { requests := apiReconciler.getAPIsForConfigMap(ctx, obj) if len(requests) > 0 { @@ -1048,80 +1156,92 @@ func (apiReconciler *APIReconciler) traverseAPIStateAndUpdateOwnerReferences(ctx // travserse through all the children of this API and trigger update owner reference if apiState.ProdHTTPRoute != nil { for _, httpRoute := range apiState.ProdHTTPRoute.HTTPRoutePartitions { - apiReconciler.retriveParentAPIsAndUpdateOwnerReferene(ctx, httpRoute) + apiReconciler.retrieveParentAPIsAndUpdateOwnerReferene(ctx, httpRoute) } } if apiState.SandHTTPRoute != nil { for _, httpRoute := range apiState.SandHTTPRoute.HTTPRoutePartitions { - apiReconciler.retriveParentAPIsAndUpdateOwnerReferene(ctx, httpRoute) + apiReconciler.retrieveParentAPIsAndUpdateOwnerReferene(ctx, httpRoute) } } if apiState.ProdGQLRoute != nil { for _, gqlRoute := range apiState.ProdGQLRoute.GQLRoutePartitions { - apiReconciler.retriveParentAPIsAndUpdateOwnerReferene(ctx, gqlRoute) + apiReconciler.retrieveParentAPIsAndUpdateOwnerReferene(ctx, gqlRoute) } } if apiState.SandGQLRoute != nil { for _, gqlRoute := range apiState.SandGQLRoute.GQLRoutePartitions { - apiReconciler.retriveParentAPIsAndUpdateOwnerReferene(ctx, gqlRoute) + apiReconciler.retrieveParentAPIsAndUpdateOwnerReferene(ctx, gqlRoute) } } + + if apiState.ProdGRPCRoute != nil { + for _, grpcRoute := range apiState.ProdGRPCRoute.GRPCRoutePartitions { + apiReconciler.retrieveParentAPIsAndUpdateOwnerReferene(ctx, grpcRoute) + } + } + if apiState.SandGRPCRoute != nil { + for _, grpcRoute := range apiState.SandGRPCRoute.GRPCRoutePartitions { + apiReconciler.retrieveParentAPIsAndUpdateOwnerReferene(ctx, grpcRoute) + } + } + for _, auth := range apiState.Authentications { - apiReconciler.retriveParentAPIsAndUpdateOwnerReferene(ctx, &auth) + apiReconciler.retrieveParentAPIsAndUpdateOwnerReferene(ctx, &auth) } for _, auth := range apiState.ResourceAuthentications { - apiReconciler.retriveParentAPIsAndUpdateOwnerReferene(ctx, &auth) + apiReconciler.retrieveParentAPIsAndUpdateOwnerReferene(ctx, &auth) } for _, ratelimit := range apiState.RateLimitPolicies { - apiReconciler.retriveParentAPIsAndUpdateOwnerReferene(ctx, &ratelimit) + apiReconciler.retrieveParentAPIsAndUpdateOwnerReferene(ctx, &ratelimit) } for _, ratelimit := range apiState.ResourceRateLimitPolicies { - apiReconciler.retriveParentAPIsAndUpdateOwnerReferene(ctx, &ratelimit) + apiReconciler.retrieveParentAPIsAndUpdateOwnerReferene(ctx, &ratelimit) } for _, apiPolicy := range apiState.APIPolicies { - apiReconciler.retriveParentAPIsAndUpdateOwnerReferene(ctx, &apiPolicy) + apiReconciler.retrieveParentAPIsAndUpdateOwnerReferene(ctx, &apiPolicy) } for _, apiPolicy := range apiState.ResourceAPIPolicies { - apiReconciler.retriveParentAPIsAndUpdateOwnerReferene(ctx, &apiPolicy) + apiReconciler.retrieveParentAPIsAndUpdateOwnerReferene(ctx, &apiPolicy) } for _, interceptorService := range apiState.InterceptorServiceMapping { - apiReconciler.retriveParentAPIsAndUpdateOwnerReferene(ctx, &interceptorService) + apiReconciler.retrieveParentAPIsAndUpdateOwnerReferene(ctx, &interceptorService) } if apiState.ProdHTTPRoute != nil { for _, backend := range apiState.ProdHTTPRoute.BackendMapping { if backend != nil { - apiReconciler.retriveParentAPIsAndUpdateOwnerReferene(ctx, &backend.Backend) + apiReconciler.retrieveParentAPIsAndUpdateOwnerReferene(ctx, &backend.Backend) } } } if apiState.SandHTTPRoute != nil { for _, backend := range apiState.SandHTTPRoute.BackendMapping { if backend != nil { - apiReconciler.retriveParentAPIsAndUpdateOwnerReferene(ctx, &backend.Backend) + apiReconciler.retrieveParentAPIsAndUpdateOwnerReferene(ctx, &backend.Backend) } } } if apiState.ProdGQLRoute != nil { for _, backend := range apiState.ProdGQLRoute.BackendMapping { if backend != nil { - apiReconciler.retriveParentAPIsAndUpdateOwnerReferene(ctx, &backend.Backend) + apiReconciler.retrieveParentAPIsAndUpdateOwnerReferene(ctx, &backend.Backend) } } } if apiState.SandGQLRoute != nil { for _, backend := range apiState.SandGQLRoute.BackendMapping { if backend != nil { - apiReconciler.retriveParentAPIsAndUpdateOwnerReferene(ctx, &backend.Backend) + apiReconciler.retrieveParentAPIsAndUpdateOwnerReferene(ctx, &backend.Backend) } } } for _, backendJwt := range apiState.BackendJWTMapping { - apiReconciler.retriveParentAPIsAndUpdateOwnerReferene(ctx, &backendJwt) + apiReconciler.retrieveParentAPIsAndUpdateOwnerReferene(ctx, &backendJwt) } } -func (apiReconciler *APIReconciler) retriveParentAPIsAndUpdateOwnerReferene(ctx context.Context, obj k8client.Object) { +func (apiReconciler *APIReconciler) retrieveParentAPIsAndUpdateOwnerReferene(ctx context.Context, obj k8client.Object) { var requests []reconcile.Request switch obj.(type) { case *dpv1alpha2.Backend: @@ -1268,6 +1388,16 @@ func (apiReconciler *APIReconciler) retriveParentAPIsAndUpdateOwnerReferene(ctx } requests = apiReconciler.getAPIsForAIProvider(ctx, &aiProvider) apiReconciler.handleOwnerReference(ctx, &aiProvider, &requests) + case *gwapiv1a2.GRPCRoute: + var grpcRoute gwapiv1a2.GRPCRoute + namespaceName := types.NamespacedName{ + Name: string(obj.GetName()), + Namespace: string(obj.GetNamespace()), + } + if err := apiReconciler.client.Get(ctx, namespaceName, &grpcRoute); err != nil { + loggers.LoggerAPKOperator.Errorf("Unexpected error occured while loading the cr object from cluster %+v", err) + return + } default: loggers.LoggerAPKOperator.Errorf("Unexpected type found while processing owner reference %+v", obj) } @@ -1283,7 +1413,7 @@ func (apiReconciler *APIReconciler) getAPIForGQLRoute(ctx context.Context, obj k loggers.LoggerAPKOperator.ErrorC(logging.PrintError(logging.Error2665, logging.TRIVIAL, "Unexpected object type, bypassing reconciliation: %v", gqlRoute)) return []reconcile.Request{} } - apiList := &dpv1alpha2.APIList{} + apiList := &dpv1beta1.APIList{} if err := apiReconciler.client.List(ctx, apiList, &k8client.ListOptions{ FieldSelector: fields.OneTermEqualSelector(gqlRouteAPIIndex, utils.NamespacedName(gqlRoute).String()), }); err != nil { @@ -1318,7 +1448,7 @@ func (apiReconciler *APIReconciler) getAPIForHTTPRoute(ctx context.Context, obj return []reconcile.Request{} } - apiList := &dpv1alpha2.APIList{} + apiList := &dpv1beta1.APIList{} if err := apiReconciler.client.List(ctx, apiList, &k8client.ListOptions{ FieldSelector: fields.OneTermEqualSelector(httpRouteAPIIndex, utils.NamespacedName(httpRoute).String()), }); err != nil { @@ -1345,6 +1475,47 @@ func (apiReconciler *APIReconciler) getAPIForHTTPRoute(ctx context.Context, obj return requests } +// getAPIForGRPCRoute triggers the API controller reconcile method based on the changes detected +// from GRPCRoute objects. If the changes are done for an API stored in the Operator Data store, +// a new reconcile event will be created and added to the reconcile event queue. +func (apiReconciler *APIReconciler) getAPIForGRPCRoute(ctx context.Context, obj k8client.Object) []reconcile.Request { + grpcRoute, ok := obj.(*gwapiv1a2.GRPCRoute) + if !ok { + loggers.LoggerAPKOperator.ErrorC(logging.PrintError(logging.Error2622, logging.TRIVIAL, + "Unexpected object type, bypassing reconciliation: %v", grpcRoute)) + return []reconcile.Request{} + } + + apiList := &dpv1beta1.APIList{} + logrus.Info("=============GETTING API FOR GRPC ROUTE================") + + if err := apiReconciler.client.List(ctx, apiList, &k8client.ListOptions{ + FieldSelector: fields.OneTermEqualSelector(grpcRouteAPIIndex, utils.NamespacedName(grpcRoute).String()), + }); err != nil { + loggers.LoggerAPKOperator.ErrorC(logging.PrintError(logging.Error2623, logging.CRITICAL, + "Unable to find associated APIs: %s", utils.NamespacedName(grpcRoute).String())) + return []reconcile.Request{} + } + + if len(apiList.Items) == 0 { + loggers.LoggerAPKOperator.Debugf("APIs for GRPCRoute not found: %s", utils.NamespacedName(grpcRoute).String()) + return []reconcile.Request{} + } + + requests := []reconcile.Request{} + for _, api := range apiList.Items { + req := reconcile.Request{ + NamespacedName: types.NamespacedName{ + Name: api.Name, + Namespace: api.Namespace}, + } + requests = append(requests, req) + loggers.LoggerAPKOperator.Infof("Adding reconcile request for API: %s/%s with API UUID: %v", api.Namespace, api.Name, + string(api.ObjectMeta.UID)) + } + return requests +} + // getAPIsForConfigMap triggers the API controller reconcile method based on the changes detected // in configMap resources. func (apiReconciler *APIReconciler) getAPIsForConfigMap(ctx context.Context, obj k8client.Object) []reconcile.Request { @@ -1368,7 +1539,7 @@ func (apiReconciler *APIReconciler) getAPIsForConfigMap(ctx context.Context, obj return requests } - apiList := &dpv1alpha2.APIList{} + apiList := &dpv1beta1.APIList{} err = apiReconciler.client.List(ctx, apiList, &k8client.ListOptions{ FieldSelector: fields.OneTermEqualSelector(configMapAPIDefinition, utils.NamespacedName(configMap).String()), }) @@ -1772,9 +1943,9 @@ func (apiReconciler *APIReconciler) getAPIsForGateway(ctx context.Context, obj k // apiPolicy schemes related to httproutes // This helps to find apiPolicy schemes binded to HTTPRoute. func addIndexes(ctx context.Context, mgr manager.Manager) error { - if err := mgr.GetFieldIndexer().IndexField(ctx, &dpv1alpha2.API{}, httpRouteAPIIndex, + if err := mgr.GetFieldIndexer().IndexField(ctx, &dpv1beta1.API{}, httpRouteAPIIndex, func(rawObj k8client.Object) []string { - api := rawObj.(*dpv1alpha2.API) + api := rawObj.(*dpv1beta1.API) var httpRoutes []string if len(api.Spec.Production) > 0 { for _, ref := range api.Spec.Production[0].RouteRefs { @@ -1803,9 +1974,9 @@ func addIndexes(ctx context.Context, mgr manager.Manager) error { return err } - if err := mgr.GetFieldIndexer().IndexField(ctx, &dpv1alpha2.API{}, gqlRouteAPIIndex, + if err := mgr.GetFieldIndexer().IndexField(ctx, &dpv1beta1.API{}, gqlRouteAPIIndex, func(rawObj k8client.Object) []string { - api := rawObj.(*dpv1alpha2.API) + api := rawObj.(*dpv1beta1.API) var gqlRoutes []string if len(api.Spec.Production) > 0 { for _, ref := range api.Spec.Production[0].RouteRefs { @@ -1834,9 +2005,9 @@ func addIndexes(ctx context.Context, mgr manager.Manager) error { return err } - if err := mgr.GetFieldIndexer().IndexField(ctx, &dpv1alpha2.API{}, configMapAPIDefinition, + if err := mgr.GetFieldIndexer().IndexField(ctx, &dpv1beta1.API{}, configMapAPIDefinition, func(rawObj k8client.Object) []string { - api := rawObj.(*dpv1alpha2.API) + api := rawObj.(*dpv1beta1.API) var configMaps []string if api.Spec.DefinitionFileRef != "" { configMaps = append(configMaps, @@ -2379,9 +2550,9 @@ func (apiReconciler *APIReconciler) handleStatus() { for _, apiName := range successEvent.APINamespacedName { // handle startup multiple apis apiReconciler.statusUpdater.Send(status.Update{ NamespacedName: apiName, - Resource: new(dpv1alpha2.API), + Resource: new(dpv1beta1.API), UpdateStatus: func(obj k8client.Object) k8client.Object { - h, ok := obj.(*dpv1alpha2.API) + h, ok := obj.(*dpv1beta1.API) if !ok { loggers.LoggerAPKOperator.ErrorC(logging.PrintError(logging.Error2626, logging.BLOCKER, "Unsupported object type %T", obj)) } @@ -2418,7 +2589,7 @@ func (apiReconciler *APIReconciler) handleLabels(ctx context.Context) { }) } payloadBytes, _ := json.Marshal(patchOps) - apiCR := dpv1alpha2.API{ + apiCR := dpv1beta1.API{ ObjectMeta: metav1.ObjectMeta{ Namespace: labelUpdate.Namespace, Name: labelUpdate.Name, @@ -2429,7 +2600,7 @@ func (apiReconciler *APIReconciler) handleLabels(ctx context.Context) { if err != nil { loggers.LoggerAPKOperator.Errorf("Failed to patch api %s/%s with patch: %+v, error: %+v", labelUpdate.Name, labelUpdate.Namespace, patchOps, err) // Patch did not work it could be due to labels field does not exists. Lets try to update the CR with labels field. - var apiCR dpv1alpha2.API + var apiCR dpv1beta1.API if err := apiReconciler.client.Get(ctx, types.NamespacedName{Namespace: labelUpdate.Namespace, Name: labelUpdate.Name}, &apiCR); err == nil { if apiCR.ObjectMeta.Labels == nil { apiCR.ObjectMeta.Labels = map[string]string{} @@ -2449,9 +2620,9 @@ func (apiReconciler *APIReconciler) handleLabels(ctx context.Context) { } func (apiReconciler *APIReconciler) handleOwnerReference(ctx context.Context, obj k8client.Object, apiRequests *[]reconcile.Request) { - apis := []dpv1alpha2.API{} + apis := []dpv1beta1.API{} for _, req := range *apiRequests { - var apiCR dpv1alpha2.API + var apiCR dpv1beta1.API if err := apiReconciler.client.Get(ctx, req.NamespacedName, &apiCR); err == nil { apis = append(apis, apiCR) } else { @@ -2481,7 +2652,7 @@ func (apiReconciler *APIReconciler) handleOwnerReference(ctx context.Context, ob } } -func prepareOwnerReference(apiItems []dpv1alpha2.API) []metav1.OwnerReference { +func prepareOwnerReference(apiItems []dpv1beta1.API) []metav1.OwnerReference { ownerReferences := []metav1.OwnerReference{} uidMap := make(map[string]bool) for _, ref := range apiItems { diff --git a/adapter/internal/operator/operator.go b/adapter/internal/operator/operator.go index ece07386a8..7777932205 100644 --- a/adapter/internal/operator/operator.go +++ b/adapter/internal/operator/operator.go @@ -47,11 +47,12 @@ import ( "sigs.k8s.io/controller-runtime/pkg/log" "sigs.k8s.io/controller-runtime/pkg/log/zap" + cpv1alpha2 "github.com/wso2/apk/common-go-libs/apis/cp/v1alpha2" + cpv1alpha3 "github.com/wso2/apk/common-go-libs/apis/cp/v1alpha3" dpv1alpha1 "github.com/wso2/apk/common-go-libs/apis/dp/v1alpha1" dpv1alpha2 "github.com/wso2/apk/common-go-libs/apis/dp/v1alpha2" dpv1alpha3 "github.com/wso2/apk/common-go-libs/apis/dp/v1alpha3" - cpv1alpha2 "github.com/wso2/apk/common-go-libs/apis/cp/v1alpha2" - cpv1alpha3 "github.com/wso2/apk/common-go-libs/apis/cp/v1alpha3" + dpv1beta1 "github.com/wso2/apk/common-go-libs/apis/dp/v1beta1" //+kubebuilder:scaffold:imports ) @@ -74,6 +75,7 @@ func init() { utilruntime.Must(cpv1alpha2.AddToScheme(scheme)) utilruntime.Must(cpv1alpha3.AddToScheme(scheme)) + utilruntime.Must(dpv1beta1.AddToScheme(scheme)) //+kubebuilder:scaffold:scheme } diff --git a/adapter/internal/operator/synchronizer/api_state.go b/adapter/internal/operator/synchronizer/api_state.go index cacf066181..71fc19b489 100644 --- a/adapter/internal/operator/synchronizer/api_state.go +++ b/adapter/internal/operator/synchronizer/api_state.go @@ -21,18 +21,22 @@ import ( "github.com/wso2/apk/common-go-libs/apis/dp/v1alpha1" "github.com/wso2/apk/common-go-libs/apis/dp/v1alpha2" "github.com/wso2/apk/common-go-libs/apis/dp/v1alpha3" + "github.com/wso2/apk/common-go-libs/apis/dp/v1beta1" gwapiv1 "sigs.k8s.io/gateway-api/apis/v1" + gwapiv1a2 "sigs.k8s.io/gateway-api/apis/v1alpha2" ) // APIState holds the state of the deployed APIs. This state is compared with // the state of the Kubernetes controller cache to detect updates. // +k8s:deepcopy-gen=true type APIState struct { - APIDefinition *v1alpha2.API + APIDefinition *v1beta1.API ProdHTTPRoute *HTTPRouteState SandHTTPRoute *HTTPRouteState ProdGQLRoute *GQLRouteState SandGQLRoute *GQLRouteState + ProdGRPCRoute *GRPCRouteState + SandGRPCRoute *GRPCRouteState Authentications map[string]v1alpha2.Authentication RateLimitPolicies map[string]v1alpha3.RateLimitPolicy ResourceAuthentications map[string]v1alpha2.Authentication @@ -67,3 +71,13 @@ type GQLRouteState struct { BackendMapping map[string]*v1alpha2.ResolvedBackend Scopes map[string]v1alpha1.Scope } + +// GRPCRouteState holds the state of the deployed grpcRoutes. This state is compared with +// the state of the Kubernetes controller cache to detect updates. +// +k8s:deepcopy-gen=true +type GRPCRouteState struct { + GRPCRouteCombined *gwapiv1a2.GRPCRoute + GRPCRoutePartitions map[string]*gwapiv1a2.GRPCRoute + BackendMapping map[string]*v1alpha1.ResolvedBackend + Scopes map[string]v1alpha1.Scope +} diff --git a/adapter/internal/operator/synchronizer/data_store.go b/adapter/internal/operator/synchronizer/data_store.go index dd42b2ef75..b64d3b1e07 100644 --- a/adapter/internal/operator/synchronizer/data_store.go +++ b/adapter/internal/operator/synchronizer/data_store.go @@ -26,6 +26,7 @@ import ( dpv1alpha3 "github.com/wso2/apk/common-go-libs/apis/dp/v1alpha3" "k8s.io/apimachinery/pkg/types" gwapiv1 "sigs.k8s.io/gateway-api/apis/v1" + gwapiv1a2 "sigs.k8s.io/gateway-api/apis/v1alpha2" ) // OperatorDataStore holds the APIStore and API, HttpRoute mappings @@ -148,6 +149,25 @@ func (ods *OperatorDataStore) processAPIState(apiNamespacedName types.Namespaced } cachedAPI.ProdGQLRoute = nil } + + if apiState.ProdGRPCRoute != nil { + if cachedAPI.ProdGRPCRoute == nil { + cachedAPI.ProdGRPCRoute = apiState.ProdGRPCRoute + updated = true + events = append(events, "Production") + } else if routeEvents, routesUpdated := updateGRPCRoute(apiState.ProdGRPCRoute, cachedAPI.ProdGRPCRoute, + "Production"); routesUpdated { + updated = true + events = append(events, routeEvents...) + } + } else { + if cachedAPI.ProdGRPCRoute != nil { + updated = true + events = append(events, "Production") + } + cachedAPI.ProdGRPCRoute = nil + } + if apiState.SandHTTPRoute != nil { if cachedAPI.SandHTTPRoute == nil { cachedAPI.SandHTTPRoute = apiState.SandHTTPRoute @@ -197,6 +217,24 @@ func (ods *OperatorDataStore) processAPIState(apiNamespacedName types.Namespaced } cachedAPI.SandGQLRoute = nil } + + if apiState.SandGRPCRoute != nil { + if cachedAPI.SandGRPCRoute == nil { + cachedAPI.SandGRPCRoute = apiState.SandGRPCRoute + updated = true + events = append(events, "Sandbox") + } else if routeEvents, routesUpdated := updateGRPCRoute(apiState.SandGRPCRoute, cachedAPI.SandGRPCRoute, "Sandbox"); routesUpdated { + updated = true + events = append(events, routeEvents...) + } + } else { + if cachedAPI.SandGRPCRoute != nil { + updated = true + events = append(events, "Sandbox") + } + cachedAPI.SandGRPCRoute = nil + } + if len(apiState.Authentications) != len(cachedAPI.Authentications) { cachedAPI.Authentications = apiState.Authentications cachedAPI.MutualSSL = apiState.MutualSSL @@ -493,6 +531,62 @@ func updateGQLRoute(gqlRoute *GQLRouteState, cachedGQLRoute *GQLRouteState, endp return events, updated } +func updateGRPCRoute(grpcRoute *GRPCRouteState, cachedGRPCRoute *GRPCRouteState, endpointType string) ([]string, bool) { + var updated bool + events := []string{} + if cachedGRPCRoute.GRPCRouteCombined == nil || !isEqualGRPCRoutes(cachedGRPCRoute.GRPCRoutePartitions, grpcRoute.GRPCRoutePartitions) { + cachedGRPCRoute.GRPCRouteCombined = grpcRoute.GRPCRouteCombined + cachedGRPCRoute.GRPCRoutePartitions = grpcRoute.GRPCRoutePartitions + updated = true + events = append(events, endpointType+" Endpoint") + } + + if len(grpcRoute.Scopes) != len(cachedGRPCRoute.Scopes) { + cachedGRPCRoute.Scopes = grpcRoute.Scopes + updated = true + events = append(events, "Resource Scopes") + } else { + for key, scope := range grpcRoute.Scopes { + if existingScope, found := cachedGRPCRoute.Scopes[key]; found { + if scope.UID != existingScope.UID || scope.Generation > existingScope.Generation { + cachedGRPCRoute.Scopes = grpcRoute.Scopes + updated = true + events = append(events, "Resource Scopes") + break + } + } else { + cachedGRPCRoute.Scopes = grpcRoute.Scopes + updated = true + events = append(events, "Resource Scopes") + break + } + } + } + + if len(grpcRoute.BackendMapping) != len(cachedGRPCRoute.BackendMapping) { + cachedGRPCRoute.BackendMapping = grpcRoute.BackendMapping + updated = true + events = append(events, endpointType+" Backend Properties") + } else { + for key, backend := range grpcRoute.BackendMapping { + if existingBackend, found := cachedGRPCRoute.BackendMapping[key]; found { + if backend.Backend.UID != existingBackend.Backend.UID || backend.Backend.Generation > existingBackend.Backend.Generation { + cachedGRPCRoute.BackendMapping = grpcRoute.BackendMapping + updated = true + events = append(events, endpointType+" Backend Properties") + break + } + } else { + cachedGRPCRoute.BackendMapping = grpcRoute.BackendMapping + updated = true + events = append(events, endpointType+" Backend Properties") + break + } + } + } + return events, updated +} + func isEqualHTTPRoutes(cachedHTTPRoutes, newHTTPRoutes map[string]*gwapiv1.HTTPRoute) bool { for key, cachedHTTPRoute := range cachedHTTPRoutes { if newHTTPRoutes[key] == nil { @@ -608,6 +702,19 @@ func (ods *OperatorDataStore) GetCachedGateway(gatewayName types.NamespacedName) return GatewayState{}, false } +func isEqualGRPCRoutes(cachedGRPCRoutes, newGRPCRoutes map[string]*gwapiv1a2.GRPCRoute) bool { + for key, cachedGRPCRoute := range cachedGRPCRoutes { + if newGRPCRoutes[key] == nil { + return false + } + if newGRPCRoutes[key].UID == cachedGRPCRoute.UID && + newGRPCRoutes[key].Generation > cachedGRPCRoute.Generation { + return false + } + } + return true +} + // IsGatewayAvailable get cached gatewaystate func (ods *OperatorDataStore) IsGatewayAvailable(gatewayName types.NamespacedName) bool { _, found := ods.gatewayStore[gatewayName] diff --git a/adapter/internal/operator/synchronizer/grpc_api.go b/adapter/internal/operator/synchronizer/grpc_api.go new file mode 100644 index 0000000000..1c27c37dbc --- /dev/null +++ b/adapter/internal/operator/synchronizer/grpc_api.go @@ -0,0 +1,185 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +package synchronizer + +import ( + "errors" + + gwapiv1a2 "sigs.k8s.io/gateway-api/apis/v1alpha2" + + "github.com/wso2/apk/adapter/config" + "github.com/wso2/apk/adapter/internal/dataholder" + "github.com/wso2/apk/adapter/internal/discovery/xds" + "github.com/wso2/apk/adapter/internal/discovery/xds/common" + "github.com/wso2/apk/adapter/internal/loggers" + "github.com/wso2/apk/adapter/internal/oasparser/model" + "github.com/wso2/apk/adapter/pkg/logging" + "k8s.io/apimachinery/pkg/types" + gwapiv1b1 "sigs.k8s.io/gateway-api/apis/v1beta1" +) + +// extract APIDetails from the GQLRoute +func updateInternalMapsFromGRPCRoute(apiState APIState, grpcRoute *GRPCRouteState, envType string) (*model.AdapterInternalAPI, map[string]struct{}, error) { + adapterInternalAPI, err := generateGRPCAdapterInternalAPI(apiState, grpcRoute, envType) + if err != nil { + loggers.LoggerAPKOperator.ErrorC(logging.PrintError(logging.Error2632, logging.MAJOR, "Error generating AdapterInternalAPI for GRPCRoute: %v. %v", grpcRoute.GRPCRouteCombined.Name, err)) + return nil, nil, err + } + + vHosts := getVhostsForGRPCAPI(grpcRoute.GRPCRouteCombined) + labels := getLabelsForGRPCAPI(grpcRoute.GRPCRouteCombined) + listeners, relativeSectionNames := getListenersForGRPCAPI(grpcRoute.GRPCRouteCombined, adapterInternalAPI.UUID) + // We dont have a use case where a perticular API's two different grpc routes refer to two different gateway. Hence get the first listener name for the list for processing. + if len(listeners) == 0 || len(relativeSectionNames) == 0 { + loggers.LoggerAPKOperator.ErrorC(logging.PrintError(logging.Error2633, logging.MINOR, "Failed to find a matching listener for grpc route: %v. ", + grpcRoute.GRPCRouteCombined.Name)) + return nil, nil, errors.New("failed to find matching listener name for the provided grpc route") + } + listenerName := listeners[0] + sectionName := relativeSectionNames[0] + + if len(listeners) > 0 { + if err := xds.PopulateInternalMaps(adapterInternalAPI, labels, vHosts, sectionName, listenerName); err != nil { + return nil, nil, err + } + } + return adapterInternalAPI, labels, nil +} + +// generateGRPCAdapterInternalAPI this will populate a AdapterInternalAPI representation for an GRPCRoute +func generateGRPCAdapterInternalAPI(apiState APIState, grpcRoute *GRPCRouteState, envType string) (*model.AdapterInternalAPI, error) { + var adapterInternalAPI model.AdapterInternalAPI + adapterInternalAPI.SetIsDefaultVersion(apiState.APIDefinition.Spec.IsDefaultVersion) + adapterInternalAPI.SetInfoAPICR(*apiState.APIDefinition) + adapterInternalAPI.SetAPIDefinitionFile(apiState.APIDefinitionFile) + adapterInternalAPI.SetAPIDefinitionEndpoint(apiState.APIDefinition.Spec.DefinitionPath) + adapterInternalAPI.SetSubscriptionValidation(apiState.SubscriptionValidation) + adapterInternalAPI.EnvType = envType + + environment := apiState.APIDefinition.Spec.Environment + if environment == "" { + conf := config.ReadConfigs() + environment = conf.Adapter.Environment + } + adapterInternalAPI.SetEnvironment(environment) + adapterInternalAPI.SetXWso2RequestBodyPass(true) + + resourceParams := model.ResourceParams{ + AuthSchemes: apiState.Authentications, + ResourceAuthSchemes: apiState.ResourceAuthentications, + BackendMapping: grpcRoute.BackendMapping, + APIPolicies: apiState.APIPolicies, + ResourceAPIPolicies: apiState.ResourceAPIPolicies, + ResourceScopes: grpcRoute.Scopes, + InterceptorServiceMapping: apiState.InterceptorServiceMapping, + BackendJWTMapping: apiState.BackendJWTMapping, + RateLimitPolicies: apiState.RateLimitPolicies, + ResourceRateLimitPolicies: apiState.ResourceRateLimitPolicies, + } + if err := adapterInternalAPI.SetInfoGRPCRouteCR(grpcRoute.GRPCRouteCombined, resourceParams); err != nil { + loggers.LoggerAPKOperator.ErrorC(logging.PrintError(logging.Error2631, logging.MAJOR, "Error setting GRPCRoute CR info to adapterInternalAPI. %v", err)) + return nil, err + } + if apiState.MutualSSL != nil && apiState.MutualSSL.Required != "" && !adapterInternalAPI.GetDisableAuthentications() { + adapterInternalAPI.SetDisableMtls(apiState.MutualSSL.Disabled) + adapterInternalAPI.SetMutualSSL(apiState.MutualSSL.Required) + adapterInternalAPI.SetClientCerts(apiState.APIDefinition.Name, apiState.MutualSSL.ClientCertificates) + } else { + adapterInternalAPI.SetDisableMtls(true) + } + + return &adapterInternalAPI, nil + +} + +// getVhostForAPI returns the vHosts related to an API. +func getVhostsForGRPCAPI(grpcRoute *gwapiv1a2.GRPCRoute) map[string]struct{} { + vHosts := make(map[string]struct{}) + for _, hostName := range grpcRoute.Spec.Hostnames { + vHosts[string(hostName)] = struct{}{} + } + return vHosts +} + +// getLabelsForAPI returns the labels related to an API. +func getLabelsForGRPCAPI(grpcRoute *gwapiv1a2.GRPCRoute) map[string]struct{} { + labels := make(map[string]struct{}) + for _, parentRef := range grpcRoute.Spec.ParentRefs { + err := xds.SanitizeGateway(string(parentRef.Name), false) + if err != nil { + loggers.LoggerAPKOperator.ErrorC(logging.PrintError(logging.Error2653, logging.CRITICAL, "Gateway Label is invalid: %s", string(parentRef.Name))) + } else { + labels[string(parentRef.Name)] = struct{}{} + } + } + return labels +} + +// getListenersForGRPCAPI returns the listeners related to an API. +func getListenersForGRPCAPI(grpcRoute *gwapiv1a2.GRPCRoute, apiUUID string) ([]string, []string) { + var listeners []string + var sectionNames []string + for _, parentRef := range grpcRoute.Spec.ParentRefs { + namespace := grpcRoute.GetNamespace() + if parentRef.Namespace != nil && *parentRef.Namespace != "" { + namespace = string(*parentRef.Namespace) + } + gateway, found := dataholder.GetGatewayMap()[types.NamespacedName{ + Namespace: namespace, + Name: string(parentRef.Name), + }.String()] + if found { + // find the matching listener + matchedListener, listenerFound := common.FindElement(gateway.Spec.Listeners, func(listener gwapiv1b1.Listener) bool { + if string(listener.Name) == string(*parentRef.SectionName) { + return true + } + return false + }) + if listenerFound { + sectionNames = append(sectionNames, string(matchedListener.Name)) + listeners = append(listeners, common.GetEnvoyListenerName(string(matchedListener.Protocol), uint32(matchedListener.Port))) + continue + } + } + loggers.LoggerAPKOperator.Errorf("Failed to find matching listeners for the grpcroute: %+v", grpcRoute.Name) + } + return listeners, sectionNames +} + +func deleteGRPCAPIFromEnv(grpcRoute *gwapiv1a2.GRPCRoute, apiState APIState) error { + labels := getLabelsForGRPCAPI(grpcRoute) + uuid := string(apiState.APIDefinition.ObjectMeta.UID) + return xds.DeleteAPI(uuid, labels) +} + +// undeployGRPCAPIInGateway undeploys the related API in CREATE and UPDATE events. +func undeployGRPCAPIInGateway(apiState APIState) error { + var err error + if apiState.ProdGRPCRoute != nil { + err = deleteGRPCAPIFromEnv(apiState.ProdGRPCRoute.GRPCRouteCombined, apiState) + } + if err != nil { + loggers.LoggerXds.ErrorC(logging.PrintError(logging.Error2630, logging.MAJOR, "Error undeploying prod grpcRoute of API : %v in Organization %v from environments."+ + " Hence not checking on deleting the sand grpcRoute of the API", string(apiState.APIDefinition.ObjectMeta.UID), apiState.APIDefinition.Spec.Organization)) + return err + } + if apiState.SandGRPCRoute != nil { + err = deleteGRPCAPIFromEnv(apiState.SandGRPCRoute.GRPCRouteCombined, apiState) + } + return err +} diff --git a/adapter/internal/operator/synchronizer/synchronizer.go b/adapter/internal/operator/synchronizer/synchronizer.go index a61aa181ca..7f67c3c5d4 100644 --- a/adapter/internal/operator/synchronizer/synchronizer.go +++ b/adapter/internal/operator/synchronizer/synchronizer.go @@ -117,6 +117,10 @@ func undeployAPIInGateway(apiEvent *APIEvent) error { if apiState.APIDefinition.Spec.APIType == "GraphQL" { err = undeployGQLAPIInGateway(apiState) } + + if apiState.APIDefinition.Spec.APIType == constants.GRPC { + return undeployGRPCAPIInGateway(apiState) + } if err != nil { loggers.LoggerAPKOperator.ErrorC(logging.PrintError(logging.Error2629, logging.CRITICAL, "API deployment failed for %s event : %v, %v", apiEvent.EventType, apiState.APIDefinition.Name, err)) @@ -199,6 +203,36 @@ func deployMultipleAPIsInGateway(event *APIEvent, successChannel *chan SuccessEv } } } + + if apiState.APIDefinition.Spec.APIType == "gRPC" { + if apiState.ProdGRPCRoute != nil { + _, updatedLabels, err := updateInternalMapsFromGRPCRoute(apiState, apiState.ProdGRPCRoute, constants.Production) + if err != nil { + loggers.LoggerAPKOperator.ErrorC(logging.PrintError(logging.Error2665, logging.CRITICAL, + "Error deploying prod grpcRoute of API : %v in Organization %v from environments %v. Error: %v", + string(apiState.APIDefinition.Spec.APIName), apiState.APIDefinition.Spec.Organization, + getLabelsForGRPCAPI(apiState.ProdGRPCRoute.GRPCRouteCombined), err)) + continue + } + for label := range updatedLabels { + updatedLabelsMap[label] = struct{}{} + } + } + + if apiState.SandGRPCRoute != nil { + _, updatedLabels, err := updateInternalMapsFromGRPCRoute(apiState, apiState.SandGRPCRoute, constants.Sandbox) + if err != nil { + loggers.LoggerAPKOperator.ErrorC(logging.PrintError(logging.Error2665, logging.CRITICAL, + "Error deploying sand grpcRoute of API : %v in Organization %v from environments %v. Error: %v", + string(apiState.APIDefinition.Spec.APIName), apiState.APIDefinition.Spec.Organization, + getLabelsForGRPCAPI(apiState.SandGRPCRoute.GRPCRouteCombined), err)) + continue + } + for label := range updatedLabels { + updatedLabelsMap[label] = struct{}{} + } + } + } updatedAPIs = append(updatedAPIs, utils.NamespacedName(apiState.APIDefinition)) } @@ -254,6 +288,13 @@ func SendEventToPartitionServer() { for _, hostName := range httpRoute.HTTPRouteCombined.Spec.Hostnames { hostNames = append(hostNames, string(hostName)) } + grpcRoute := api.ProdGRPCRoute + if grpcRoute == nil { + grpcRoute = api.SandGRPCRoute + } + for _, hostName := range grpcRoute.GRPCRouteCombined.Spec.Hostnames { + hostNames = append(hostNames, string(hostName)) + } data := PartitionEvent{ EventType: eventType, BasePath: basePath, diff --git a/adapter/internal/operator/synchronizer/zz_generated.deepcopy.go b/adapter/internal/operator/synchronizer/zz_generated.deepcopy.go index 597fe54391..e6d5500922 100644 --- a/adapter/internal/operator/synchronizer/zz_generated.deepcopy.go +++ b/adapter/internal/operator/synchronizer/zz_generated.deepcopy.go @@ -26,7 +26,9 @@ import ( "github.com/wso2/apk/common-go-libs/apis/dp/v1alpha1" "github.com/wso2/apk/common-go-libs/apis/dp/v1alpha2" "github.com/wso2/apk/common-go-libs/apis/dp/v1alpha3" + "github.com/wso2/apk/common-go-libs/apis/dp/v1beta1" "sigs.k8s.io/gateway-api/apis/v1" + apisv1alpha2 "sigs.k8s.io/gateway-api/apis/v1alpha2" ) // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. @@ -34,7 +36,7 @@ func (in *APIState) DeepCopyInto(out *APIState) { *out = *in if in.APIDefinition != nil { in, out := &in.APIDefinition, &out.APIDefinition - *out = new(v1alpha2.API) + *out = new(v1beta1.API) (*in).DeepCopyInto(*out) } if in.ProdHTTPRoute != nil { @@ -57,6 +59,16 @@ func (in *APIState) DeepCopyInto(out *APIState) { *out = new(GQLRouteState) (*in).DeepCopyInto(*out) } + if in.ProdGRPCRoute != nil { + in, out := &in.ProdGRPCRoute, &out.ProdGRPCRoute + *out = new(GRPCRouteState) + (*in).DeepCopyInto(*out) + } + if in.SandGRPCRoute != nil { + in, out := &in.SandGRPCRoute, &out.SandGRPCRoute + *out = new(GRPCRouteState) + (*in).DeepCopyInto(*out) + } if in.Authentications != nil { in, out := &in.Authentications, &out.Authentications *out = make(map[string]v1alpha2.Authentication, len(*in)) @@ -178,6 +190,63 @@ func (in *GQLRouteState) DeepCopy() *GQLRouteState { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *GRPCRouteState) DeepCopyInto(out *GRPCRouteState) { + *out = *in + if in.GRPCRouteCombined != nil { + in, out := &in.GRPCRouteCombined, &out.GRPCRouteCombined + *out = new(apisv1alpha2.GRPCRoute) + (*in).DeepCopyInto(*out) + } + if in.GRPCRoutePartitions != nil { + in, out := &in.GRPCRoutePartitions, &out.GRPCRoutePartitions + *out = make(map[string]*apisv1alpha2.GRPCRoute, len(*in)) + for key, val := range *in { + var outVal *apisv1alpha2.GRPCRoute + if val == nil { + (*out)[key] = nil + } else { + in, out := &val, &outVal + *out = new(apisv1alpha2.GRPCRoute) + (*in).DeepCopyInto(*out) + } + (*out)[key] = outVal + } + } + if in.BackendMapping != nil { + in, out := &in.BackendMapping, &out.BackendMapping + *out = make(map[string]*v1alpha1.ResolvedBackend, len(*in)) + for key, val := range *in { + var outVal *v1alpha1.ResolvedBackend + if val == nil { + (*out)[key] = nil + } else { + in, out := &val, &outVal + *out = new(v1alpha1.ResolvedBackend) + (*in).DeepCopyInto(*out) + } + (*out)[key] = outVal + } + } + if in.Scopes != nil { + in, out := &in.Scopes, &out.Scopes + *out = make(map[string]v1alpha1.Scope, len(*in)) + for key, val := range *in { + (*out)[key] = *val.DeepCopy() + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GRPCRouteState. +func (in *GRPCRouteState) DeepCopy() *GRPCRouteState { + if in == nil { + return nil + } + out := new(GRPCRouteState) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *GatewayState) DeepCopyInto(out *GatewayState) { *out = *in diff --git a/adapter/internal/operator/utils/utils.go b/adapter/internal/operator/utils/utils.go index b3ecd1275c..a8a316f517 100644 --- a/adapter/internal/operator/utils/utils.go +++ b/adapter/internal/operator/utils/utils.go @@ -36,6 +36,7 @@ import ( dpv1alpha1 "github.com/wso2/apk/common-go-libs/apis/dp/v1alpha1" dpv1alpha2 "github.com/wso2/apk/common-go-libs/apis/dp/v1alpha2" dpv1alpha3 "github.com/wso2/apk/common-go-libs/apis/dp/v1alpha3" + dpv1beta1 "github.com/wso2/apk/common-go-libs/apis/dp/v1beta1" corev1 "k8s.io/api/core/v1" apierrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -349,7 +350,7 @@ func GetResolvedBackendFromService(k8sService *corev1.Service, svcPort int) (*dp // ResolveAndAddBackendToMapping resolves backend from reference and adds it to the backendMapping. func ResolveAndAddBackendToMapping(ctx context.Context, client k8client.Client, backendMapping map[string]*dpv1alpha2.ResolvedBackend, - backendRef dpv1alpha1.BackendReference, interceptorServiceNamespace string, api *dpv1alpha2.API) { + backendRef dpv1alpha1.BackendReference, interceptorServiceNamespace string, api *dpv1beta1.API) { backendName := types.NamespacedName{ Name: backendRef.Name, Namespace: interceptorServiceNamespace, @@ -361,7 +362,7 @@ func ResolveAndAddBackendToMapping(ctx context.Context, client k8client.Client, } // ResolveRef this function will return k8client object and update owner -func ResolveRef(ctx context.Context, client k8client.Client, api *dpv1alpha2.API, +func ResolveRef(ctx context.Context, client k8client.Client, api *dpv1beta1.API, namespacedName types.NamespacedName, isReplace bool, obj k8client.Object, opts ...k8client.GetOption) error { err := client.Get(ctx, namespacedName, obj, opts...) return err @@ -369,7 +370,7 @@ func ResolveRef(ctx context.Context, client k8client.Client, api *dpv1alpha2.API // GetResolvedBackend resolves backend TLS configurations. func GetResolvedBackend(ctx context.Context, client k8client.Client, - backendNamespacedName types.NamespacedName, api *dpv1alpha2.API) *dpv1alpha2.ResolvedBackend { + backendNamespacedName types.NamespacedName, api *dpv1beta1.API) *dpv1alpha2.ResolvedBackend { resolvedBackend := dpv1alpha2.ResolvedBackend{} resolvedTLSConfig := dpv1alpha2.ResolvedTLSConfig{} var backend dpv1alpha2.Backend @@ -595,7 +596,7 @@ func RetrieveNamespaceListOptions(namespaces []string) k8client.ListOptions { // GetInterceptorService reads InterceptorService when interceptorReference is given func GetInterceptorService(ctx context.Context, client k8client.Client, namespace string, - interceptorReference *dpv1alpha3.InterceptorReference, api *dpv1alpha2.API) *dpv1alpha1.InterceptorService { + interceptorReference *dpv1alpha3.InterceptorReference, api *dpv1beta1.API) *dpv1alpha1.InterceptorService { interceptorService := &dpv1alpha1.InterceptorService{} interceptorRef := types.NamespacedName{ Namespace: namespace, @@ -611,7 +612,7 @@ func GetInterceptorService(ctx context.Context, client k8client.Client, namespac // GetBackendJWT reads BackendJWT when backendJWTReference is given func GetBackendJWT(ctx context.Context, client k8client.Client, namespace, - backendJWTReference string, api *dpv1alpha2.API) *dpv1alpha1.BackendJWT { + backendJWTReference string, api *dpv1beta1.API) *dpv1alpha1.BackendJWT { backendJWT := &dpv1alpha1.BackendJWT{} backendJWTRef := types.NamespacedName{ Namespace: namespace, @@ -644,21 +645,21 @@ func GetAIProvider(ctx context.Context, client k8client.Client, namespace string } // RetrieveAPIList retrieves API list from the given kubernetes client -func RetrieveAPIList(k8sclient k8client.Client) ([]dpv1alpha2.API, error) { +func RetrieveAPIList(k8sclient k8client.Client) ([]dpv1beta1.API, error) { ctx := context.Background() conf := config.ReadConfigs() namespaces := conf.Adapter.Operator.Namespaces - var apis []dpv1alpha2.API + var apis []dpv1beta1.API if namespaces == nil { - apiList := &dpv1alpha2.APIList{} + apiList := &dpv1beta1.APIList{} if err := k8sclient.List(ctx, apiList, &k8client.ListOptions{}); err != nil { return nil, err } - apis = make([]dpv1alpha2.API, len(apiList.Items)) + apis = make([]dpv1beta1.API, len(apiList.Items)) copy(apis[:], apiList.Items[:]) } else { for _, namespace := range namespaces { - apiList := &dpv1alpha2.APIList{} + apiList := &dpv1beta1.APIList{} if err := k8sclient.List(ctx, apiList, &k8client.ListOptions{Namespace: namespace}); err != nil { return nil, err } diff --git a/adapter/pkg/logging/logging_constant.go b/adapter/pkg/logging/logging_constant.go index 93dd429c65..6ff9bb9b4b 100644 --- a/adapter/pkg/logging/logging_constant.go +++ b/adapter/pkg/logging/logging_constant.go @@ -164,6 +164,7 @@ const ( Error2665 = 2665 Error2666 = 2666 Error2667 = 2667 + Error2668 = 2668 ) // Error Log Pkg auth(3001-3099) Config Constants From f0367eba0f2ee28038d64a4664683e90e885f03f Mon Sep 17 00:00:00 2001 From: sgayangi Date: Fri, 23 Aug 2024 15:25:08 +0530 Subject: [PATCH 02/17] Add enforcer changes to gRPC APIs --- .../org/wso2/apk/enforcer/api/APIFactory.java | 19 +- .../org/wso2/apk/enforcer/api/GRPCAPI.java | 203 ++++++++++++++++++ .../apk/enforcer/constants/APIConstants.java | 1 + .../server/swagger/SwaggerServerHandler.java | 39 +++- 4 files changed, 254 insertions(+), 8 deletions(-) create mode 100644 gateway/enforcer/org.wso2.apk.enforcer/src/main/java/org/wso2/apk/enforcer/api/GRPCAPI.java diff --git a/gateway/enforcer/org.wso2.apk.enforcer/src/main/java/org/wso2/apk/enforcer/api/APIFactory.java b/gateway/enforcer/org.wso2.apk.enforcer/src/main/java/org/wso2/apk/enforcer/api/APIFactory.java index 58c9c3dd28..aa6e98eab2 100644 --- a/gateway/enforcer/org.wso2.apk.enforcer/src/main/java/org/wso2/apk/enforcer/api/APIFactory.java +++ b/gateway/enforcer/org.wso2.apk.enforcer/src/main/java/org/wso2/apk/enforcer/api/APIFactory.java @@ -73,11 +73,17 @@ public void addApis(List apis) { // String apiKey = getApiKey(webSocketAPI); // newApis.put(apiKey, webSocketAPI); // } else + System.out.println(api.getApiType()); if (APIConstants.ApiType.GRAPHQL.equals(api.getApiType())) { GraphQLAPI graphQLAPI = new GraphQLAPI(); graphQLAPI.init(api); String apiKey = getApiKey(graphQLAPI); newApis.put(apiKey, graphQLAPI); + } else if (APIConstants.ApiType.GRPC.equals(api.getApiType())) { + GRPCAPI grpcAPI = new GRPCAPI(); + grpcAPI.init(api); + String apiKey = getApiKey(grpcAPI); + newApis.put(apiKey, grpcAPI); } else { RestAPI enforcerApi = new RestAPI(); enforcerApi.init(api); @@ -133,10 +139,17 @@ public byte[] getAPIDefinition(final String basePath, final String version, fina public ResourceConfig getMatchedResource(API api, String matchedResourcePath, String method) { List resourceConfigList = api.getAPIConfig().getResources(); + System.out.println(APIConstants.ApiType.GRPC); + if (APIConstants.ApiType.GRPC.equals(api.getAPIConfig().getApiType())) { + return resourceConfigList.stream() + .filter(resourceConfig -> resourceConfig.getPath().equals(matchedResourcePath)) + .findFirst().orElse(null); + } return resourceConfigList.stream() - .filter(resourceConfig -> resourceConfig.getPath().equals(matchedResourcePath)). - filter(resourceConfig -> (method == null) || resourceConfig.getMethod() - .equals(ResourceConfig.HttpMethods.valueOf(method))).findFirst().orElse(null); + .filter(resourceConfig -> resourceConfig.getPath().equals(matchedResourcePath)) + .filter(resourceConfig -> (method == null) || resourceConfig.getMethod() + .equals(ResourceConfig.HttpMethods.valueOf(method))) + .findFirst().orElse(null); } // // For WebSocket APIs since there are no resources in WebSocket APIs. diff --git a/gateway/enforcer/org.wso2.apk.enforcer/src/main/java/org/wso2/apk/enforcer/api/GRPCAPI.java b/gateway/enforcer/org.wso2.apk.enforcer/src/main/java/org/wso2/apk/enforcer/api/GRPCAPI.java new file mode 100644 index 0000000000..970db479f1 --- /dev/null +++ b/gateway/enforcer/org.wso2.apk.enforcer/src/main/java/org/wso2/apk/enforcer/api/GRPCAPI.java @@ -0,0 +1,203 @@ +package org.wso2.apk.enforcer.api; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.wso2.apk.enforcer.analytics.AnalyticsFilter; +import org.wso2.apk.enforcer.commons.Filter; +import org.wso2.apk.enforcer.commons.dto.ClaimValueDTO; +import org.wso2.apk.enforcer.commons.dto.JWTConfigurationDto; +import org.wso2.apk.enforcer.commons.model.APIConfig; +import org.wso2.apk.enforcer.commons.model.ResourceConfig; +import org.wso2.apk.enforcer.commons.model.EndpointSecurity; +import org.wso2.apk.enforcer.commons.model.RequestContext; +import org.wso2.apk.enforcer.commons.model.EndpointCluster; +import org.wso2.apk.enforcer.config.ConfigHolder; +import org.wso2.apk.enforcer.config.EnforcerConfig; +import org.wso2.apk.enforcer.constants.APIConstants; +import org.wso2.apk.enforcer.constants.HttpConstants; +import org.wso2.apk.enforcer.cors.CorsFilter; +import org.wso2.apk.enforcer.discovery.api.Api; +import org.wso2.apk.enforcer.discovery.api.Resource; +import org.wso2.apk.enforcer.discovery.api.Operation; +import org.wso2.apk.enforcer.discovery.api.BackendJWTTokenInfo; +import org.wso2.apk.enforcer.discovery.api.Claim; +import org.wso2.apk.enforcer.security.AuthFilter; +import org.wso2.apk.enforcer.security.mtls.MtlsUtils; +import org.wso2.apk.enforcer.util.EndpointUtils; +import org.wso2.apk.enforcer.util.FilterUtils; + +import java.security.KeyStore; +import java.security.KeyStoreException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class GRPCAPI implements API { + + private static final Logger logger = LogManager.getLogger(GRPCAPI.class); + private final List filters = new ArrayList<>(); + private APIConfig apiConfig; + + @Override + public List getFilters() { + return filters; + } + + @Override + public String init(Api api) { + String vhost = api.getVhost(); + String basePath = api.getBasePath(); + String name = api.getTitle(); + String version = api.getVersion(); + String apiType = api.getApiType(); + List resources = new ArrayList<>(); + + String mutualSSL = api.getMutualSSL(); + boolean applicationSecurity = api.getApplicationSecurity(); + + EndpointCluster endpoints = Utils.processEndpoints(api.getEndpoints()); + EndpointSecurity[] endpointSecurity = APIProcessUtils.convertProtoEndpointSecurity( + api.getEndpointSecurityList()); + + for (Resource res : api.getResourcesList()) { + for (Operation operation : res.getMethodsList()) { + ResourceConfig resConfig = Utils.buildResource(operation, res.getPath(), endpointSecurity); + resConfig.setEndpoints(endpoints); + resConfig.setPolicyConfig(Utils.genPolicyConfig(operation.getPolicies())); + resources.add(resConfig); + } + } + + KeyStore trustStore; + try { + trustStore = MtlsUtils.createTrustStore(api.getClientCertificatesList()); + } catch (KeyStoreException e) { + throw new SecurityException(e); + } + + BackendJWTTokenInfo backendJWTTokenInfo = api.getBackendJWTTokenInfo(); + JWTConfigurationDto jwtConfigurationDto = new JWTConfigurationDto(); + + // If backendJWTTokeInfo is available + if (api.hasBackendJWTTokenInfo()) { + Map claims = backendJWTTokenInfo.getCustomClaimsMap(); + Map claimsMap = new HashMap<>(); + for (Map.Entry claimEntry : claims.entrySet()) { + Claim claim = claimEntry.getValue(); + ClaimValueDTO claimVal = new ClaimValueDTO(claim.getValue(), claim.getType()); + claimsMap.put(claimEntry.getKey(), claimVal); + } + EnforcerConfig enforcerConfig = ConfigHolder.getInstance().getConfig(); + jwtConfigurationDto.populateConfigValues(backendJWTTokenInfo.getEnabled(), + backendJWTTokenInfo.getHeader(), backendJWTTokenInfo.getSigningAlgorithm(), + backendJWTTokenInfo.getEncoding(), enforcerConfig.getJwtConfigurationDto().getPublicCert(), + enforcerConfig.getJwtConfigurationDto().getPrivateKey(), backendJWTTokenInfo.getTokenTTL(), + claimsMap, enforcerConfig.getJwtConfigurationDto().useKid(), + enforcerConfig.getJwtConfigurationDto().getKidValue()); + } + + byte[] apiDefinition = api.getApiDefinitionFile().toByteArray(); + String apiLifeCycleState = api.getApiLifeCycleState(); + this.apiConfig = new APIConfig.Builder(name).uuid(api.getId()).vhost(vhost).basePath(basePath).version(version) + .resources(resources).apiType(apiType).apiLifeCycleState(apiLifeCycleState).tier(api.getTier()) + .envType(api.getEnvType()).disableAuthentication(api.getDisableAuthentications()) + .disableScopes(api.getDisableScopes()).trustStore(trustStore).organizationId(api.getOrganizationId()) + .mutualSSL(mutualSSL) + .applicationSecurity(applicationSecurity).jwtConfigurationDto(jwtConfigurationDto) + .apiDefinition(apiDefinition).environment(api.getEnvironment()) + .environment(api.getEnvironment()) + .subscriptionValidation(api.getSubscriptionValidation()).build(); + initFilters(); + logger.info("APIConfig: " + this.apiConfig); + return basePath; + } + + @Override + public ResponseObject process(RequestContext requestContext) { + + ResponseObject responseObject = new ResponseObject(requestContext.getRequestID()); + responseObject.setRequestPath(requestContext.getRequestPath()); + boolean analyticsEnabled = ConfigHolder.getInstance().getConfig().getAnalyticsConfig().isEnabled(); + + Utils.handleCommonHeaders(requestContext); + boolean isExistsMatchedOperations = requestContext.getMatchedResourcePaths() != null && + requestContext.getMatchedResourcePaths().size() > 0; + // This flag is used to apply CORS filter + boolean isOptionCall = requestContext.getRequestMethod().contains(HttpConstants.OPTIONS); + + // handle other not allowed && non option request && not yet handled error + // scenarios. + if ((!isOptionCall && !isExistsMatchedOperations) && !requestContext.getProperties() + .containsKey(APIConstants.MessageFormat.ERROR_CODE)) { + requestContext.getProperties() + .put(APIConstants.MessageFormat.STATUS_CODE, APIConstants.StatusCodes.NOTFOUND.getCode()); + requestContext.getProperties().put(APIConstants.MessageFormat.ERROR_CODE, + APIConstants.StatusCodes.NOTFOUND.getValue()); + requestContext.getProperties().put(APIConstants.MessageFormat.ERROR_MESSAGE, + APIConstants.NOT_FOUND_MESSAGE); + requestContext.getProperties().put(APIConstants.MessageFormat.ERROR_DESCRIPTION, + APIConstants.NOT_FOUND_DESCRIPTION); + } + + if ((isExistsMatchedOperations || isOptionCall) && executeFilterChain(requestContext)) { + EndpointUtils.updateClusterHeaderAndCheckEnv(requestContext); + responseObject.setOrganizationId(requestContext.getMatchedAPI().getOrganizationId()); + responseObject.setRemoveHeaderMap(requestContext.getRemoveHeaders()); + responseObject.setQueryParamsToRemove(requestContext.getQueryParamsToRemove()); + responseObject.setRemoveAllQueryParams(requestContext.isRemoveAllQueryParams()); + responseObject.setQueryParamsToAdd(requestContext.getQueryParamsToAdd()); + responseObject.setQueryParamMap(requestContext.getQueryParameters()); + responseObject.setStatusCode(APIConstants.StatusCodes.OK.getCode()); + if (requestContext.getAddHeaders() != null && requestContext.getAddHeaders().size() > 0) { + responseObject.setHeaderMap(requestContext.getAddHeaders()); + } + if (analyticsEnabled) { + AnalyticsFilter.getInstance().handleSuccessRequest(requestContext); + } + // set metadata for interceptors + responseObject.setMetaDataMap(requestContext.getMetadataMap()); + } else { + // If enforcer stops with a false, it will be passed directly to the client. + responseObject.setDirectResponse(true); + responseObject.setStatusCode(Integer.parseInt( + requestContext.getProperties().get(APIConstants.MessageFormat.STATUS_CODE).toString())); + if (requestContext.getProperties().containsKey(APIConstants.MessageFormat.ERROR_CODE)) { + responseObject.setErrorCode( + requestContext.getProperties().get(APIConstants.MessageFormat.ERROR_CODE).toString()); + } + if (requestContext.getProperties().get(APIConstants.MessageFormat.ERROR_MESSAGE) != null) { + responseObject.setErrorMessage(requestContext.getProperties() + .get(APIConstants.MessageFormat.ERROR_MESSAGE).toString()); + } + if (requestContext.getProperties().get(APIConstants.MessageFormat.ERROR_DESCRIPTION) != null) { + responseObject.setErrorDescription(requestContext.getProperties() + .get(APIConstants.MessageFormat.ERROR_DESCRIPTION).toString()); + } + if (requestContext.getAddHeaders() != null && requestContext.getAddHeaders().size() > 0) { + responseObject.setHeaderMap(requestContext.getAddHeaders()); + } + if (analyticsEnabled && !FilterUtils.isSkippedAnalyticsFaultEvent(responseObject.getErrorCode())) { + AnalyticsFilter.getInstance().handleFailureRequest(requestContext); + responseObject.setMetaDataMap(new HashMap<>(0)); + } + } + + return responseObject; + } + + @Override + public APIConfig getAPIConfig() { + return this.apiConfig; + } + + private void initFilters() { + AuthFilter authFilter = new AuthFilter(); + authFilter.init(apiConfig, null); + this.filters.add(authFilter); + + // CORS filter is added as the first filter, and it is not customizable. + CorsFilter corsFilter = new CorsFilter(); + this.filters.add(0, corsFilter); + } +} \ No newline at end of file diff --git a/gateway/enforcer/org.wso2.apk.enforcer/src/main/java/org/wso2/apk/enforcer/constants/APIConstants.java b/gateway/enforcer/org.wso2.apk.enforcer/src/main/java/org/wso2/apk/enforcer/constants/APIConstants.java index 1f658f3438..917944c7b9 100644 --- a/gateway/enforcer/org.wso2.apk.enforcer/src/main/java/org/wso2/apk/enforcer/constants/APIConstants.java +++ b/gateway/enforcer/org.wso2.apk.enforcer/src/main/java/org/wso2/apk/enforcer/constants/APIConstants.java @@ -270,6 +270,7 @@ public static class ApiType { public static final String WEB_SOCKET = "WS"; public static final String GRAPHQL = "GraphQL"; + public static final String GRPC = "gRPC"; } /** diff --git a/gateway/enforcer/org.wso2.apk.enforcer/src/main/java/org/wso2/apk/enforcer/server/swagger/SwaggerServerHandler.java b/gateway/enforcer/org.wso2.apk.enforcer/src/main/java/org/wso2/apk/enforcer/server/swagger/SwaggerServerHandler.java index 16538f4d0f..9bdc520a53 100644 --- a/gateway/enforcer/org.wso2.apk.enforcer/src/main/java/org/wso2/apk/enforcer/server/swagger/SwaggerServerHandler.java +++ b/gateway/enforcer/org.wso2.apk.enforcer/src/main/java/org/wso2/apk/enforcer/server/swagger/SwaggerServerHandler.java @@ -18,6 +18,8 @@ import org.wso2.apk.enforcer.models.ResponsePayload; import java.util.HashMap; import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; public class SwaggerServerHandler extends SimpleChannelInboundHandler { @@ -43,17 +45,33 @@ public void channelRead0(ChannelHandlerContext ctx, HttpObject msg) throws Excep boolean isSwagger = false; - String [] params = request.uri().split("/"); - final String basePath = "/" + params[1] + "/" + params[2]; - final String vHost = params[3].split("\\?")[0]; - final String queryParam = params[3].split("\\?")[1]; + //check if it's GRPC request using the header + String header = request.headers().get("ApiType"); + String[] params = request.uri().split("/"); + final String basePath; + final String vHost; + final String queryParam; + final String version; + //if len params is 3, then it's a GRPC request else other + final String type = params.length == 3 ? "GRPC" : "REST"; + if (type.equals("GRPC")) { + basePath = "/" + params[1]; + vHost = params[2].split("\\?")[0]; + queryParam = params[2].split("\\?")[1]; + version = extractVersionFromGrpcBasePath(params[1]); + } else { + basePath = "/" + params[1] + "/" + params[2]; + vHost = params[3].split("\\?")[0]; + queryParam = params[3].split("\\?")[1]; + version = params[2]; + } if (APIDefinitionConstants.SWAGGER_DEFINITION.equalsIgnoreCase(queryParam)) { isSwagger = true; } if(isSwagger){ // load the corresponding swagger definition from the API name - byte[] apiDefinition = apiFactory.getAPIDefinition(basePath, params[2], vHost); + byte[] apiDefinition = apiFactory.getAPIDefinition(basePath, version, vHost); if (apiDefinition == null || apiDefinition.length == 0) { String error = AdminConstants.ErrorMessages.NOT_FOUND; responsePayload = new ResponsePayload(); @@ -91,4 +109,15 @@ private void buildAndSendResponse(ChannelHandlerContext ctx, ResponsePayload res httpResponse.headers().set(HTTP.CONTENT_LEN, httpResponse.content().readableBytes()); ctx.writeAndFlush(httpResponse); } + + private static String extractVersionFromGrpcBasePath(String input) { + // Pattern to match '.v' followed by digits and optional periods followed by more digits + Pattern pattern = Pattern.compile("\\.v\\d+(\\.\\d+)*"); + Matcher matcher = pattern.matcher(input); + + if (matcher.find()) { + return matcher.group().substring(1); // Returns the matched version + } + return ""; + } } From 2837d6487a4c7ad68c6b7880f9bf040c67c32315 Mon Sep 17 00:00:00 2001 From: sgayangi Date: Fri, 23 Aug 2024 15:25:30 +0530 Subject: [PATCH 03/17] Add webhooks for gRPC APIs --- common-go-libs/PROJECT | 12 + .../apis/dp/v1alpha1/api_conversion.go | 28 +- .../apis/dp/v1alpha2/api_conversion.go | 103 +++++- common-go-libs/apis/dp/v1alpha2/api_types.go | 1 - .../apis/dp/v1alpha2/api_webhook.go | 20 ++ .../apis/dp/v1beta1/api_conversion.go | 21 ++ common-go-libs/apis/dp/v1beta1/api_types.go | 202 +++++++++++ common-go-libs/apis/dp/v1beta1/api_webhook.go | 316 ++++++++++++++++++ .../apis/dp/v1beta1/groupversion_info.go | 37 ++ .../apis/dp/v1beta1/webhook_suite_test.go | 129 +++++++ .../apis/dp/v1beta1/zz_generated.deepcopy.go | 195 +++++++++++ .../config/certmanager/certificate.yaml | 39 +++ .../config/certmanager/kustomization.yaml | 5 + .../config/certmanager/kustomizeconfig.yaml | 16 + .../config/crd/bases/dp.wso2.com_apis.yaml | 178 ++++++++++ common-go-libs/config/crd/kustomization.yaml | 11 +- .../crd/patches/cainjection_in_dp_apis.yaml | 7 + .../crd/patches/webhook_in_dp_apis.yaml | 16 + .../config/default/manager_webhook_patch.yaml | 23 ++ .../default/webhookcainjection_patch.yaml | 29 ++ .../config/rbac/dp_api_editor_role.yaml | 31 ++ .../config/rbac/dp_api_viewer_role.yaml | 27 ++ .../config/samples/dp_v1beta1_api.yaml | 12 + .../config/webhook/kustomization.yaml | 6 + .../config/webhook/kustomizeconfig.yaml | 25 ++ common-go-libs/config/webhook/manifests.yaml | 40 +++ common-go-libs/config/webhook/service.yaml | 20 ++ common-go-libs/revive.toml | 6 +- 28 files changed, 1531 insertions(+), 24 deletions(-) create mode 100644 common-go-libs/apis/dp/v1beta1/api_conversion.go create mode 100644 common-go-libs/apis/dp/v1beta1/api_types.go create mode 100644 common-go-libs/apis/dp/v1beta1/api_webhook.go create mode 100644 common-go-libs/apis/dp/v1beta1/groupversion_info.go create mode 100644 common-go-libs/apis/dp/v1beta1/webhook_suite_test.go create mode 100644 common-go-libs/apis/dp/v1beta1/zz_generated.deepcopy.go create mode 100644 common-go-libs/config/certmanager/certificate.yaml create mode 100644 common-go-libs/config/certmanager/kustomization.yaml create mode 100644 common-go-libs/config/certmanager/kustomizeconfig.yaml create mode 100644 common-go-libs/config/crd/patches/cainjection_in_dp_apis.yaml create mode 100644 common-go-libs/config/crd/patches/webhook_in_dp_apis.yaml create mode 100644 common-go-libs/config/default/manager_webhook_patch.yaml create mode 100644 common-go-libs/config/default/webhookcainjection_patch.yaml create mode 100644 common-go-libs/config/rbac/dp_api_editor_role.yaml create mode 100644 common-go-libs/config/rbac/dp_api_viewer_role.yaml create mode 100644 common-go-libs/config/samples/dp_v1beta1_api.yaml create mode 100644 common-go-libs/config/webhook/kustomization.yaml create mode 100644 common-go-libs/config/webhook/kustomizeconfig.yaml create mode 100644 common-go-libs/config/webhook/service.yaml diff --git a/common-go-libs/PROJECT b/common-go-libs/PROJECT index 5aee973d41..d0c674b1df 100644 --- a/common-go-libs/PROJECT +++ b/common-go-libs/PROJECT @@ -189,4 +189,16 @@ resources: kind: AIRateLimitPolicy path: github.com/wso2/apk/common-go-libs/apis/dp/v1alpha3 version: v1alpha3 +- api: + crdVersion: v1 + namespaced: true + domain: wso2.com + group: dp + kind: API + path: github.com/wso2/apk/common-go-libs/apis/dp/v1beta1 + version: v1beta1 + webhooks: + defaulting: true + validation: true + webhookVersion: v1 version: "3" diff --git a/common-go-libs/apis/dp/v1alpha1/api_conversion.go b/common-go-libs/apis/dp/v1alpha1/api_conversion.go index 2aaf06c912..e6ffe0296d 100644 --- a/common-go-libs/apis/dp/v1alpha1/api_conversion.go +++ b/common-go-libs/apis/dp/v1alpha1/api_conversion.go @@ -18,15 +18,15 @@ package v1alpha1 import ( - "github.com/wso2/apk/common-go-libs/apis/dp/v1alpha2" + "github.com/wso2/apk/common-go-libs/apis/dp/v1beta1" "sigs.k8s.io/controller-runtime/pkg/conversion" ) -// ConvertTo converts this API CR to the Hub version (v1alpha2). -// src is v1alpha1.API and dst is v1alpha2.API. +// ConvertTo converts this API CR to the Hub version (v1beta1). +// src is v1alpha1.API and dst is v1beta1.API. func (src *API) ConvertTo(dstRaw conversion.Hub) error { - dst := dstRaw.(*v1alpha2.API) + dst := dstRaw.(*v1beta1.API) dst.ObjectMeta = src.ObjectMeta // Spec @@ -41,40 +41,40 @@ func (src *API) ConvertTo(dstRaw conversion.Hub) error { dst.Spec.SystemAPI = src.Spec.SystemAPI if src.Spec.Production != nil { - dst.Spec.Production = []v1alpha2.EnvConfig{} + dst.Spec.Production = []v1beta1.EnvConfig{} for _, productionRef := range src.Spec.Production { - dst.Spec.Production = append(dst.Spec.Production, v1alpha2.EnvConfig{ + dst.Spec.Production = append(dst.Spec.Production, v1beta1.EnvConfig{ RouteRefs: productionRef.HTTPRouteRefs, }) } } if src.Spec.Sandbox != nil { - dst.Spec.Sandbox = []v1alpha2.EnvConfig{} + dst.Spec.Sandbox = []v1beta1.EnvConfig{} for _, sandboxRef := range src.Spec.Sandbox { - dst.Spec.Sandbox = append(dst.Spec.Sandbox, v1alpha2.EnvConfig{ + dst.Spec.Sandbox = append(dst.Spec.Sandbox, v1beta1.EnvConfig{ RouteRefs: sandboxRef.HTTPRouteRefs, }) } } // Convert []Property to []v1alpha2.Property - var properties []v1alpha2.Property + var properties []v1beta1.Property for _, p := range src.Spec.APIProperties { - properties = append(properties, v1alpha2.Property(p)) + properties = append(properties, v1beta1.Property(p)) } dst.Spec.APIProperties = properties // Status - dst.Status.DeploymentStatus = v1alpha2.DeploymentStatus(src.Status.DeploymentStatus) + dst.Status.DeploymentStatus = v1beta1.DeploymentStatus(src.Status.DeploymentStatus) return nil } -// ConvertFrom converts from the Hub version (v1alpha2) to this version. -// src is v1alpha1.API and dst is v1alpha2.API. +// ConvertFrom converts from the Hub version (v1beta1) to this version. +// src is v1alpha1.API and dst is v1beta1.API. func (src *API) ConvertFrom(srcRaw conversion.Hub) error { - dst := srcRaw.(*v1alpha2.API) + dst := srcRaw.(*v1beta1.API) src.ObjectMeta = dst.ObjectMeta // Spec diff --git a/common-go-libs/apis/dp/v1alpha2/api_conversion.go b/common-go-libs/apis/dp/v1alpha2/api_conversion.go index 5a1953e138..5fd93d09b4 100644 --- a/common-go-libs/apis/dp/v1alpha2/api_conversion.go +++ b/common-go-libs/apis/dp/v1alpha2/api_conversion.go @@ -17,5 +17,104 @@ package v1alpha2 -// Hub marks this type as a conversion hub. -func (*API) Hub() {} +import ( + "github.com/wso2/apk/common-go-libs/apis/dp/v1beta1" + "sigs.k8s.io/controller-runtime/pkg/conversion" +) + +// ConvertTo converts this API CR to the Hub version (v1beta1). +// src is v1alpha2.API and dst is v1beta1.API. +func (src *API) ConvertTo(dstRaw conversion.Hub) error { + + dst := dstRaw.(*v1beta1.API) + dst.ObjectMeta = src.ObjectMeta + + // Spec + dst.Spec.APIName = src.Spec.APIName + dst.Spec.APIVersion = src.Spec.APIVersion + dst.Spec.IsDefaultVersion = src.Spec.IsDefaultVersion + dst.Spec.DefinitionFileRef = src.Spec.DefinitionFileRef + dst.Spec.DefinitionPath = src.Spec.DefinitionPath + dst.Spec.APIType = src.Spec.APIType + dst.Spec.BasePath = src.Spec.BasePath + dst.Spec.Organization = src.Spec.Organization + dst.Spec.SystemAPI = src.Spec.SystemAPI + + if src.Spec.Production != nil { + dst.Spec.Production = []v1beta1.EnvConfig{} + for _, productionRef := range src.Spec.Production { + dst.Spec.Production = append(dst.Spec.Production, v1beta1.EnvConfig{ + RouteRefs: productionRef.RouteRefs, + }) + } + } + if src.Spec.Sandbox != nil { + dst.Spec.Sandbox = []v1beta1.EnvConfig{} + for _, sandboxRef := range src.Spec.Sandbox { + dst.Spec.Sandbox = append(dst.Spec.Sandbox, v1beta1.EnvConfig{ + RouteRefs: sandboxRef.RouteRefs, + }) + } + } + + // Convert []Property to []v1alpha2.Property + var properties []v1beta1.Property + for _, p := range src.Spec.APIProperties { + properties = append(properties, v1beta1.Property(p)) + } + dst.Spec.APIProperties = properties + + // Status + dst.Status.DeploymentStatus = v1beta1.DeploymentStatus(src.Status.DeploymentStatus) + + return nil +} + +// ConvertFrom converts from the Hub version (v1beta1) to this version. +// src is v1alpha2.API and dst is v1beta1.API. +func (src *API) ConvertFrom(srcRaw conversion.Hub) error { + + dst := srcRaw.(*v1beta1.API) + src.ObjectMeta = dst.ObjectMeta + + // Spec + src.Spec.APIName = dst.Spec.APIName + src.Spec.APIVersion = dst.Spec.APIVersion + src.Spec.IsDefaultVersion = dst.Spec.IsDefaultVersion + src.Spec.DefinitionFileRef = dst.Spec.DefinitionFileRef + src.Spec.DefinitionPath = dst.Spec.DefinitionPath + src.Spec.APIType = dst.Spec.APIType + src.Spec.BasePath = dst.Spec.BasePath + src.Spec.Organization = dst.Spec.Organization + src.Spec.SystemAPI = dst.Spec.SystemAPI + + if dst.Spec.Production != nil { + src.Spec.Production = []EnvConfig{} + for _, productionRef := range dst.Spec.Production { + src.Spec.Production = append(src.Spec.Production, EnvConfig{ + RouteRefs: productionRef.RouteRefs, + }) + } + } + + if dst.Spec.Sandbox != nil { + src.Spec.Sandbox = []EnvConfig{} + for _, sandboxRef := range dst.Spec.Sandbox { + src.Spec.Sandbox = append(src.Spec.Sandbox, EnvConfig{ + RouteRefs: sandboxRef.RouteRefs, + }) + } + } + + // Convert []Property to []v1alpha1.Property + var properties []Property + for _, p := range dst.Spec.APIProperties { + properties = append(properties, Property(p)) + } + src.Spec.APIProperties = properties + + // Status + src.Status.DeploymentStatus = DeploymentStatus(dst.Status.DeploymentStatus) + + return nil +} diff --git a/common-go-libs/apis/dp/v1alpha2/api_types.go b/common-go-libs/apis/dp/v1alpha2/api_types.go index 4579e5e0b9..f5b08467bb 100644 --- a/common-go-libs/apis/dp/v1alpha2/api_types.go +++ b/common-go-libs/apis/dp/v1alpha2/api_types.go @@ -172,7 +172,6 @@ type DeploymentStatus struct { // +genclient //+kubebuilder:object:root=true //+kubebuilder:subresource:status -//+kubebuilder:storageversion //+kubebuilder:printcolumn:name="API Name",type="string",JSONPath=".spec.apiName" //+kubebuilder:printcolumn:name="Version",type="string",JSONPath=".spec.apiVersion" //+kubebuilder:printcolumn:name="BasePath",type="string",JSONPath=".spec.basePath" diff --git a/common-go-libs/apis/dp/v1alpha2/api_webhook.go b/common-go-libs/apis/dp/v1alpha2/api_webhook.go index 2e3348be35..7f62170633 100644 --- a/common-go-libs/apis/dp/v1alpha2/api_webhook.go +++ b/common-go-libs/apis/dp/v1alpha2/api_webhook.go @@ -24,6 +24,7 @@ import ( "errors" "fmt" "io" + "regexp" "strings" gqlparser "github.com/vektah/gqlparser" @@ -103,6 +104,8 @@ func (r *API) validateAPI() error { if r.Spec.BasePath == "" { allErrs = append(allErrs, field.Required(field.NewPath("spec").Child("basePath"), "API basePath is required")) + } else if errMsg := validateAPIBasePathRegex(r.Spec.BasePath, r.Spec.APIType); errMsg != "" { + allErrs = append(allErrs, field.Invalid(field.NewPath("spec").Child("basePath"), r.Spec.BasePath, errMsg)) } else if errMsg := validateAPIBasePathFormat(r.Spec.BasePath, r.Spec.APIVersion); errMsg != "" { allErrs = append(allErrs, field.Invalid(field.NewPath("spec").Child("basePath"), r.Spec.BasePath, errMsg)) } else if err := r.validateAPIBasePathExistsAndDefaultVersion(); err != nil { @@ -159,6 +162,23 @@ func (r *API) validateAPI() error { return nil } +func validateAPIBasePathRegex(basePath, apiType string) string { + var pattern string + if apiType == "GRPC" { + pattern = `^[/][a-zA-Z][a-zA-Z0-9_.]*$` + } else { + pattern = `^[/][a-zA-Z0-9~/_.-]*$` + } + re, err := regexp.Compile(pattern) + if err != nil { + return "Failed to compile basePath regex pattern" + } + if !re.MatchString(basePath) { + return "API basePath is not in a valid format for the specified API type" + } + return "" +} + func isEmptyStringsInArray(strings []string) bool { for _, str := range strings { if str == "" { diff --git a/common-go-libs/apis/dp/v1beta1/api_conversion.go b/common-go-libs/apis/dp/v1beta1/api_conversion.go new file mode 100644 index 0000000000..5564f83e08 --- /dev/null +++ b/common-go-libs/apis/dp/v1beta1/api_conversion.go @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package v1beta1 + +// Hub marks this type as a conversion hub. +func (*API) Hub() {} diff --git a/common-go-libs/apis/dp/v1beta1/api_types.go b/common-go-libs/apis/dp/v1beta1/api_types.go new file mode 100644 index 0000000000..96e929bfe8 --- /dev/null +++ b/common-go-libs/apis/dp/v1beta1/api_types.go @@ -0,0 +1,202 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package v1beta1 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN! +// NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized. + +// APISpec defines the desired state of API +type APISpec struct { + + // APIName is the unique name of the API + //can be used to uniquely identify an API. + // + // +kubebuilder:validation:MinLength=1 + // +kubebuilder:validation:MaxLength=60 + // +kubebuilder:validation:Pattern="^[^~!@#;:%^*()+={}|\\<>\"'',&$\\[\\]\\/]*$" + APIName string `json:"apiName"` + + // APIVersion is the version number of the API. + // + // +kubebuilder:validation:MinLength=1 + // +kubebuilder:validation:MaxLength=30 + // +kubebuilder:validation:Pattern="^[^~!@#;:%^*()+={}|\\<>\"'',&/$\\[\\]\\s+\\/]+$" + APIVersion string `json:"apiVersion"` + + // IsDefaultVersion indicates whether this API version should be used as a default API + // + // +optional + IsDefaultVersion bool `json:"isDefaultVersion"` + + // DefinitionFileRef contains the + // definition of the API in a ConfigMap. + // + // +optional + DefinitionFileRef string `json:"definitionFileRef"` + + // DefinitionPath contains the path to expose the API definition. + // + // +kubebuilder:default:=/api-definition + // +kubebuilder:validation:MinLength=1 + DefinitionPath string `json:"definitionPath"` + + // Production contains a list of references to HttpRoutes + // of type HttpRoute. + // xref: https://github.com/kubernetes-sigs/gateway-api/blob/main/apis/v1beta1/httproute_types.go + // + // + // +optional + // +nullable + // +kubebuilder:validation:MaxItems=1 + Production []EnvConfig `json:"production"` + + // Sandbox contains a list of references to HttpRoutes + // of type HttpRoute. + // xref: https://github.com/kubernetes-sigs/gateway-api/blob/main/apis/v1beta1/httproute_types.go + // + // + // +optional + // +nullable + // +kubebuilder:validation:MaxItems=1 + Sandbox []EnvConfig `json:"sandbox"` + + // APIType denotes the type of the API. + // Possible values could be REST, GraphQL, gRPC + // + // +kubebuilder:validation:Enum=REST;GraphQL;gRPC + APIType string `json:"apiType"` + + // BasePath denotes the basepath of the API. + // e.g: /pet-store-api/1.0.6 + // + // +kubectl:validation:MaxLength=232 + // +kubebuilder:validation:Pattern=^[/][a-zA-Z0-9~/_.-]*$ + BasePath string `json:"basePath"` + + // Organization denotes the organization. + // related to the API + // + // +optional + Organization string `json:"organization"` + + // SystemAPI denotes if it is an internal system API. + // + // +optional + SystemAPI bool `json:"systemAPI"` + + // APIProperties denotes the custom properties of the API. + // + // +optional + // +nullable + APIProperties []Property `json:"apiProperties,omitempty"` + + // Environment denotes the environment of the API. + // + // +optional + // +nullable + Environment string `json:"environment,omitempty"` +} + +// Property holds key value pair of APIProperties +type Property struct { + Name string `json:"name,omitempty"` + Value string `json:"value,omitempty"` +} + +// EnvConfig contains the environment specific configuration +type EnvConfig struct { + // RouteRefs denotes the environment of the API. + RouteRefs []string `json:"routeRefs"` +} + +// APIStatus defines the observed state of API +type APIStatus struct { + // DeploymentStatus denotes the deployment status of the API + // + // +optional + DeploymentStatus DeploymentStatus `json:"deploymentStatus"` +} + +// DeploymentStatus contains the status of the API deployment +type DeploymentStatus struct { + + // Status denotes the state of the API in its lifecycle. + // Possible values could be Accepted, Invalid, Deploy etc. + // + // + Status string `json:"status"` + + // Message represents a user friendly message that explains the + // current state of the API. + // + // + // +optional + Message string `json:"message"` + + // Accepted represents whether the API is accepted or not. + // + // + Accepted bool `json:"accepted"` + + // TransitionTime represents the last known transition timestamp. + // + // + TransitionTime *metav1.Time `json:"transitionTime"` + + // Events contains a list of events related to the API. + // + // + // +optional + Events []string `json:"events,omitempty"` +} + +// +genclient +// +kubebuilder:object:root=true +// +kubebuilder:subresource:status +// +kubebuilder:storageversion +// +kubebuilder:printcolumn:name="API Name",type="string",JSONPath=".spec.apiName" +// +kubebuilder:printcolumn:name="Version",type="string",JSONPath=".spec.apiVersion" +// +kubebuilder:printcolumn:name="BasePath",type="string",JSONPath=".spec.basePath" +// +kubebuilder:printcolumn:name="Organization",type="string",JSONPath=".spec.organization" +// +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp" + +// API is the Schema for the apis API +type API struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec APISpec `json:"spec,omitempty"` + Status APIStatus `json:"status,omitempty"` +} + +//+kubebuilder:object:root=true + +// APIList contains a list of API +type APIList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []API `json:"items"` +} + +func init() { + SchemeBuilder.Register(&API{}, &APIList{}) +} diff --git a/common-go-libs/apis/dp/v1beta1/api_webhook.go b/common-go-libs/apis/dp/v1beta1/api_webhook.go new file mode 100644 index 0000000000..0303745046 --- /dev/null +++ b/common-go-libs/apis/dp/v1beta1/api_webhook.go @@ -0,0 +1,316 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package v1beta1 + +import ( + "bytes" + "compress/gzip" + "context" + "errors" + "fmt" + "io" + "strings" + + "github.com/sirupsen/logrus" + "github.com/vektah/gqlparser" + "github.com/vektah/gqlparser/ast" + "github.com/wso2/apk/adapter/pkg/logging" + config "github.com/wso2/apk/common-go-libs/configs" + "github.com/wso2/apk/common-go-libs/loggers" + "github.com/wso2/apk/common-go-libs/utils" + "golang.org/x/exp/slices" + + corev1 "k8s.io/api/core/v1" + apierrors "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/types" + "k8s.io/apimachinery/pkg/util/validation/field" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + logf "sigs.k8s.io/controller-runtime/pkg/log" + "sigs.k8s.io/controller-runtime/pkg/webhook" + "sigs.k8s.io/controller-runtime/pkg/webhook/admission" +) + +// log is for logging in this package. +var apilog = logf.Log.WithName("api-resource") +var c client.Client + +// SetupWebhookWithManager creates a new webhook builder for API +func (r *API) SetupWebhookWithManager(mgr ctrl.Manager) error { + c = mgr.GetClient() + return ctrl.NewWebhookManagedBy(mgr). + For(r). + Complete() +} + +// TODO(user): EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN! + +//+kubebuilder:webhook:path=/mutate-dp-wso2-com-v1beta1-api,mutating=true,failurePolicy=fail,sideEffects=None,groups=dp.wso2.com,resources=apis,verbs=create;update,versions=v1beta1,name=mapi.kb.io,admissionReviewVersions=v1 + +var _ webhook.Defaulter = &API{} + +// Default implements webhook.Defaulter so a webhook will be registered for the type +func (r *API) Default() { + apilog.Info("default", "name", r.Name) + + // TODO(user): fill in your defaulting logic. +} + +// TODO(user): change verbs to "verbs=create;update;delete" if you want to enable deletion validation. +//+kubebuilder:webhook:path=/validate-dp-wso2-com-v1beta1-api,mutating=false,failurePolicy=fail,sideEffects=None,groups=dp.wso2.com,resources=apis,verbs=create;update,versions=v1beta1,name=vapi.kb.io,admissionReviewVersions=v1 + +var _ webhook.Validator = &API{} + +// ValidateCreate implements webhook.Validator so a webhook will be registered for the type +func (r *API) ValidateCreate() (admission.Warnings, error) { + return nil, r.validateAPI() +} + +// ValidateUpdate implements webhook.Validator so a webhook will be registered for the type +func (r *API) ValidateUpdate(old runtime.Object) (admission.Warnings, error) { + return nil, r.validateAPI() +} + +// ValidateDelete implements webhook.Validator so a webhook will be registered for the type +func (r *API) ValidateDelete() (admission.Warnings, error) { + + // TODO(user): fill in your validation logic upon object deletion. + return nil, nil +} + +// validateAPI validate api crd fields +func (r *API) validateAPI() error { + var allErrs field.ErrorList + conf := config.ReadConfigs() + namespaces := conf.CommonController.Operator.Namespaces + if len(namespaces) > 0 { + if !slices.Contains(namespaces, r.Namespace) { + loggers.LoggerAPK.Debugf("API validation Skipped for namespace: %v", r.Namespace) + return nil + } + } + + if r.Spec.BasePath == "" { + allErrs = append(allErrs, field.Required(field.NewPath("spec").Child("basePath"), "API basePath is required")) + } else if errMsg := validateAPIBasePathFormat(r.Spec.BasePath, r.Spec.APIVersion); errMsg != "" { + allErrs = append(allErrs, field.Invalid(field.NewPath("spec").Child("basePath"), r.Spec.BasePath, errMsg)) + } else if err := r.validateAPIBasePathExistsAndDefaultVersion(); err != nil { + allErrs = append(allErrs, err) + } + + if r.Spec.DefinitionFileRef != "" { + if schemaString, errMsg := validateGzip(r.Spec.DefinitionFileRef, r.Namespace); errMsg != "" { + allErrs = append(allErrs, field.Invalid(field.NewPath("spec").Child("definitionFileRef"), r.Spec.DefinitionFileRef, errMsg)) + } else if schemaString != "" && r.Spec.APIType == "GraphQL" { + if errMsg := validateSDL(schemaString); errMsg != "" { + allErrs = append(allErrs, field.Invalid(field.NewPath("spec").Child("definitionFileRef"), r.Spec.DefinitionFileRef, errMsg)) + } + } + } else if r.Spec.APIType == "GraphQL" { + allErrs = append(allErrs, field.Required(field.NewPath("spec").Child("definitionFileRef"), "GraphQL API definitionFileRef is required")) + } + + // Organization value should not be empty as it required when applying ratelimit policy + if r.Spec.Organization == "" { + allErrs = append(allErrs, field.Required(field.NewPath("spec").Child("organization"), "Organization can not be empty")) + } + + if !(len(r.Spec.Production) > 0 && r.Spec.Production[0].RouteRefs != nil && len(r.Spec.Production[0].RouteRefs) > 0) && + !(len(r.Spec.Sandbox) > 0 && r.Spec.Sandbox[0].RouteRefs != nil && len(r.Spec.Sandbox[0].RouteRefs) > 0) { + allErrs = append(allErrs, field.Required(field.NewPath("spec"), + "both API production and sandbox endpoint references cannot be empty")) + } + + var prodHTTPRoute, sandHTTPRoute []string + if len(r.Spec.Production) > 0 { + prodHTTPRoute = r.Spec.Production[0].RouteRefs + } + if len(r.Spec.Sandbox) > 0 { + sandHTTPRoute = r.Spec.Sandbox[0].RouteRefs + } + + if isEmptyStringsInArray(prodHTTPRoute) { + allErrs = append(allErrs, field.Required(field.NewPath("spec").Child("production").Child("httpRouteRefs"), + "API production endpoint reference cannot be empty")) + } + + if isEmptyStringsInArray(sandHTTPRoute) { + allErrs = append(allErrs, field.Required(field.NewPath("spec").Child("sandbox").Child("httpRouteRefs"), + "API sandbox endpoint reference cannot be empty")) + } + + if len(allErrs) > 0 { + return apierrors.NewInvalid( + schema.GroupKind{Group: "dp.wso2.com", Kind: "API"}, + r.Name, allErrs) + } + + return nil +} + +func isEmptyStringsInArray(strings []string) bool { + for _, str := range strings { + if str == "" { + return true + } + } + return false +} + +func (r *API) validateAPIBasePathExistsAndDefaultVersion() *field.Error { + + apiList, err := retrieveAPIList() + if err != nil { + return field.InternalError(field.NewPath("spec").Child("basePath"), + errors.New("unable to list APIs for API basePath validation")) + + } + currentAPIBasePathWithoutVersion := getBasePathWithoutVersion(r.Spec.BasePath) + incomingAPIEnvironment := utils.GetEnvironment(r.Spec.Environment) + for _, api := range apiList { + if (types.NamespacedName{Namespace: r.Namespace, Name: r.Name} != + types.NamespacedName{Namespace: api.Namespace, Name: api.Name}) { + + existingAPIEnvironment := utils.GetEnvironment(api.Spec.Environment) + if api.Spec.Organization == r.Spec.Organization && api.Spec.BasePath == r.Spec.BasePath && + incomingAPIEnvironment == existingAPIEnvironment { + return &field.Error{ + Type: field.ErrorTypeDuplicate, + Field: field.NewPath("spec").Child("basePath").String(), + BadValue: r.Spec.BasePath, + Detail: "an API has been already created for the basePath"} + } + if r.Spec.IsDefaultVersion { + targetAPIBasePathWithoutVersion := getBasePathWithoutVersion(api.Spec.BasePath) + targetAPIBasePathWithVersion := api.Spec.BasePath + if api.Spec.IsDefaultVersion { + if targetAPIBasePathWithoutVersion == currentAPIBasePathWithoutVersion { + return &field.Error{ + Type: field.ErrorTypeForbidden, + Field: field.NewPath("spec").Child("isDefaultVersion").String(), + BadValue: r.Spec.BasePath, + Detail: "this API already has a default version"} + } + + } + if targetAPIBasePathWithVersion == currentAPIBasePathWithoutVersion { + return &field.Error{ + Type: field.ErrorTypeForbidden, + Field: field.NewPath("spec").Child("isDefaultVersion").String(), + BadValue: r.Spec.BasePath, + Detail: fmt.Sprintf("api: %s's basePath path is colliding with default path", r.Name)} + } + } + } + } + return nil +} + +func retrieveAPIList() ([]API, error) { + ctx := context.Background() + conf := config.ReadConfigs() + namespaces := conf.CommonController.Operator.Namespaces + var apis []API + if namespaces == nil { + apiList := &APIList{} + if err := c.List(ctx, apiList, &client.ListOptions{}); err != nil { + loggers.LoggerAPKOperator.ErrorC(logging.PrintError(logging.Error2605, logging.CRITICAL, "Unable to list APIs: %v", err.Error())) + return nil, err + } + apis = make([]API, len(apiList.Items)) + copy(apis[:], apiList.Items[:]) + } else { + for _, namespace := range namespaces { + apiList := &APIList{} + if err := c.List(ctx, apiList, &client.ListOptions{Namespace: namespace}); err != nil { + loggers.LoggerAPKOperator.ErrorC(logging.PrintError(logging.Error2605, logging.CRITICAL, "Unable to list APIs: %v", err.Error())) + return nil, err + } + apis = append(apis, apiList.Items...) + } + } + return apis, nil +} + +func validateAPIBasePathFormat(basePath string, apiVersion string) string { + if !strings.HasSuffix("/"+basePath, apiVersion) { + return "API basePath value should contain the /{APIVersion} at end." + } + return "" +} + +// getBasePathWithoutVersion returns the basePath without version +func getBasePathWithoutVersion(basePath string) string { + lastIndex := strings.LastIndex(basePath, "/") + if lastIndex != -1 { + return basePath[:lastIndex] + } + return basePath +} + +func validateGzip(name, namespace string) (string, string) { + configMap := &corev1.ConfigMap{} + var err error + + if err = c.Get(context.Background(), types.NamespacedName{Name: string(name), Namespace: namespace}, configMap); err == nil { + var apiDef []byte + for _, val := range configMap.BinaryData { + // config map data key is "swagger.yaml" + apiDef = []byte(val) + } + // unzip gzip bytes + var schemaString string + if schemaString, err = unzip(apiDef); err != nil { + loggers.LoggerAPKOperator.ErrorC(logging.PrintError(logging.Error2600, logging.MINOR, "Error while unzipping gzip bytes: %v", err)) + return "", "invalid gzipped content" + } + return schemaString, "" + } + + loggers.LoggerAPKOperator.ErrorC(logging.PrintError(logging.Error2600, logging.MINOR, "ConfigMap for sdl not found: %v", err)) + + return "", "" +} + +// unzip gzip bytes +func unzip(compressedData []byte) (string, error) { + reader, err := gzip.NewReader(bytes.NewBuffer(compressedData)) + if err != nil { + logrus.Info(err) + return "", fmt.Errorf("error creating gzip reader: %v", err) + } + defer reader.Close() + + // Read the decompressed data + schemaString, err := io.ReadAll(reader) + if err != nil { + return "", fmt.Errorf("error reading decompressed data of the apiDefinition: %v", err) + } + return string(schemaString), nil +} + +func validateSDL(sdl string) string { + _, err := gqlparser.LoadSchema(&ast.Source{Input: sdl}) + if err != nil { + loggers.LoggerAPKOperator.ErrorC(logging.PrintError(logging.Error2600, logging.MINOR, "Error while parsing the GraphQL SDL: %v", err)) + return "error while parsing the GraphQL SDL" + } + return "" +} diff --git a/common-go-libs/apis/dp/v1beta1/groupversion_info.go b/common-go-libs/apis/dp/v1beta1/groupversion_info.go new file mode 100644 index 0000000000..9f7ad3d004 --- /dev/null +++ b/common-go-libs/apis/dp/v1beta1/groupversion_info.go @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +// Package v1beta1 contains API Schema definitions for the dp v1beta1 API group +// +kubebuilder:object:generate=true +// +groupName=dp.wso2.com +package v1beta1 + +import ( + "k8s.io/apimachinery/pkg/runtime/schema" + "sigs.k8s.io/controller-runtime/pkg/scheme" +) + +var ( + // GroupVersion is group version used to register these objects + GroupVersion = schema.GroupVersion{Group: "dp.wso2.com", Version: "v1beta1"} + + // SchemeBuilder is used to add go types to the GroupVersionKind scheme + SchemeBuilder = &scheme.Builder{GroupVersion: GroupVersion} + + // AddToScheme adds the types in this group-version to the given scheme. + AddToScheme = SchemeBuilder.AddToScheme +) diff --git a/common-go-libs/apis/dp/v1beta1/webhook_suite_test.go b/common-go-libs/apis/dp/v1beta1/webhook_suite_test.go new file mode 100644 index 0000000000..4c43950443 --- /dev/null +++ b/common-go-libs/apis/dp/v1beta1/webhook_suite_test.go @@ -0,0 +1,129 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package v1beta1 + +import ( + "context" + "crypto/tls" + "fmt" + "net" + "path/filepath" + "testing" + "time" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + + admissionv1beta1 "k8s.io/api/admission/v1beta1" + //+kubebuilder:scaffold:imports + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/client-go/rest" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/envtest" + logf "sigs.k8s.io/controller-runtime/pkg/log" + "sigs.k8s.io/controller-runtime/pkg/log/zap" +) + +// These tests use Ginkgo (BDD-style Go testing framework). Refer to +// http://onsi.github.io/ginkgo/ to learn more about Ginkgo. + +var cfg *rest.Config +var k8sClient client.Client +var testEnv *envtest.Environment +var ctx context.Context +var cancel context.CancelFunc + +func TestAPIs(t *testing.T) { + RegisterFailHandler(Fail) + + RunSpecs(t, "Webhook Suite") +} + +var _ = BeforeSuite(func() { + logf.SetLogger(zap.New(zap.WriteTo(GinkgoWriter), zap.UseDevMode(true))) + + ctx, cancel = context.WithCancel(context.TODO()) + + By("bootstrapping test environment") + testEnv = &envtest.Environment{ + CRDDirectoryPaths: []string{filepath.Join("..", "..", "..", "config", "crd", "bases")}, + ErrorIfCRDPathMissing: false, + WebhookInstallOptions: envtest.WebhookInstallOptions{ + Paths: []string{filepath.Join("..", "..", "..", "config", "webhook")}, + }, + } + + var err error + // cfg is defined in this file globally. + cfg, err = testEnv.Start() + Expect(err).NotTo(HaveOccurred()) + Expect(cfg).NotTo(BeNil()) + + scheme := runtime.NewScheme() + err = AddToScheme(scheme) + Expect(err).NotTo(HaveOccurred()) + + err = admissionv1beta1.AddToScheme(scheme) + Expect(err).NotTo(HaveOccurred()) + + //+kubebuilder:scaffold:scheme + + k8sClient, err = client.New(cfg, client.Options{Scheme: scheme}) + Expect(err).NotTo(HaveOccurred()) + Expect(k8sClient).NotTo(BeNil()) + + // start webhook server using Manager + webhookInstallOptions := &testEnv.WebhookInstallOptions + mgr, err := ctrl.NewManager(cfg, ctrl.Options{ + Scheme: scheme, + LeaderElection: false, + }) + Expect(err).NotTo(HaveOccurred()) + + err = (&API{}).SetupWebhookWithManager(mgr) + Expect(err).NotTo(HaveOccurred()) + + //+kubebuilder:scaffold:webhook + + go func() { + defer GinkgoRecover() + err = mgr.Start(ctx) + Expect(err).NotTo(HaveOccurred()) + }() + + // wait for the webhook server to get ready + dialer := &net.Dialer{Timeout: time.Second} + addrPort := fmt.Sprintf("%s:%d", webhookInstallOptions.LocalServingHost, webhookInstallOptions.LocalServingPort) + Eventually(func() error { + conn, err := tls.DialWithDialer(dialer, "tcp", addrPort, &tls.Config{InsecureSkipVerify: true}) + if err != nil { + return err + } + conn.Close() + return nil + }).Should(Succeed()) + +}) + +var _ = AfterSuite(func() { + cancel() + By("tearing down the test environment") + err := testEnv.Stop() + Expect(err).NotTo(HaveOccurred()) +}) diff --git a/common-go-libs/apis/dp/v1beta1/zz_generated.deepcopy.go b/common-go-libs/apis/dp/v1beta1/zz_generated.deepcopy.go new file mode 100644 index 0000000000..79cffd6dbb --- /dev/null +++ b/common-go-libs/apis/dp/v1beta1/zz_generated.deepcopy.go @@ -0,0 +1,195 @@ +//go:build !ignore_autogenerated +// +build !ignore_autogenerated + +/* + * Copyright (c) 2023, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +// Code generated by controller-gen. DO NOT EDIT. + +package v1beta1 + +import ( + "k8s.io/apimachinery/pkg/runtime" +) + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *API) DeepCopyInto(out *API) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + in.Status.DeepCopyInto(&out.Status) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new API. +func (in *API) DeepCopy() *API { + if in == nil { + return nil + } + out := new(API) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *API) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *APIList) DeepCopyInto(out *APIList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]API, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new APIList. +func (in *APIList) DeepCopy() *APIList { + if in == nil { + return nil + } + out := new(APIList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *APIList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *APISpec) DeepCopyInto(out *APISpec) { + *out = *in + if in.Production != nil { + in, out := &in.Production, &out.Production + *out = make([]EnvConfig, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.Sandbox != nil { + in, out := &in.Sandbox, &out.Sandbox + *out = make([]EnvConfig, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.APIProperties != nil { + in, out := &in.APIProperties, &out.APIProperties + *out = make([]Property, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new APISpec. +func (in *APISpec) DeepCopy() *APISpec { + if in == nil { + return nil + } + out := new(APISpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *APIStatus) DeepCopyInto(out *APIStatus) { + *out = *in + in.DeploymentStatus.DeepCopyInto(&out.DeploymentStatus) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new APIStatus. +func (in *APIStatus) DeepCopy() *APIStatus { + if in == nil { + return nil + } + out := new(APIStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DeploymentStatus) DeepCopyInto(out *DeploymentStatus) { + *out = *in + if in.TransitionTime != nil { + in, out := &in.TransitionTime, &out.TransitionTime + *out = (*in).DeepCopy() + } + if in.Events != nil { + in, out := &in.Events, &out.Events + *out = make([]string, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DeploymentStatus. +func (in *DeploymentStatus) DeepCopy() *DeploymentStatus { + if in == nil { + return nil + } + out := new(DeploymentStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *EnvConfig) DeepCopyInto(out *EnvConfig) { + *out = *in + if in.RouteRefs != nil { + in, out := &in.RouteRefs, &out.RouteRefs + *out = make([]string, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EnvConfig. +func (in *EnvConfig) DeepCopy() *EnvConfig { + if in == nil { + return nil + } + out := new(EnvConfig) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Property) DeepCopyInto(out *Property) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Property. +func (in *Property) DeepCopy() *Property { + if in == nil { + return nil + } + out := new(Property) + in.DeepCopyInto(out) + return out +} diff --git a/common-go-libs/config/certmanager/certificate.yaml b/common-go-libs/config/certmanager/certificate.yaml new file mode 100644 index 0000000000..4321a02d48 --- /dev/null +++ b/common-go-libs/config/certmanager/certificate.yaml @@ -0,0 +1,39 @@ +# The following manifests contain a self-signed issuer CR and a certificate CR. +# More document can be found at https://docs.cert-manager.io +# WARNING: Targets CertManager v1.0. Check https://cert-manager.io/docs/installation/upgrading/ for breaking changes. +apiVersion: cert-manager.io/v1 +kind: Issuer +metadata: + labels: + app.kubernetes.io/name: issuer + app.kubernetes.io/instance: selfsigned-issuer + app.kubernetes.io/component: certificate + app.kubernetes.io/created-by: operator + app.kubernetes.io/part-of: operator + app.kubernetes.io/managed-by: kustomize + name: selfsigned-issuer + namespace: system +spec: + selfSigned: {} +--- +apiVersion: cert-manager.io/v1 +kind: Certificate +metadata: + labels: + app.kubernetes.io/name: certificate + app.kubernetes.io/instance: serving-cert + app.kubernetes.io/component: certificate + app.kubernetes.io/created-by: operator + app.kubernetes.io/part-of: operator + app.kubernetes.io/managed-by: kustomize + name: serving-cert # this name should match the one appeared in kustomizeconfig.yaml + namespace: system +spec: + # $(SERVICE_NAME) and $(SERVICE_NAMESPACE) will be substituted by kustomize + dnsNames: + - $(SERVICE_NAME).$(SERVICE_NAMESPACE).svc + - $(SERVICE_NAME).$(SERVICE_NAMESPACE).svc.cluster.local + issuerRef: + kind: Issuer + name: selfsigned-issuer + secretName: webhook-server-cert # this secret will not be prefixed, since it's not managed by kustomize diff --git a/common-go-libs/config/certmanager/kustomization.yaml b/common-go-libs/config/certmanager/kustomization.yaml new file mode 100644 index 0000000000..bebea5a595 --- /dev/null +++ b/common-go-libs/config/certmanager/kustomization.yaml @@ -0,0 +1,5 @@ +resources: +- certificate.yaml + +configurations: +- kustomizeconfig.yaml diff --git a/common-go-libs/config/certmanager/kustomizeconfig.yaml b/common-go-libs/config/certmanager/kustomizeconfig.yaml new file mode 100644 index 0000000000..e631f77736 --- /dev/null +++ b/common-go-libs/config/certmanager/kustomizeconfig.yaml @@ -0,0 +1,16 @@ +# This configuration is for teaching kustomize how to update name ref and var substitution +nameReference: +- kind: Issuer + group: cert-manager.io + fieldSpecs: + - kind: Certificate + group: cert-manager.io + path: spec/issuerRef/name + +varReference: +- kind: Certificate + group: cert-manager.io + path: spec/commonName +- kind: Certificate + group: cert-manager.io + path: spec/dnsNames diff --git a/common-go-libs/config/crd/bases/dp.wso2.com_apis.yaml b/common-go-libs/config/crd/bases/dp.wso2.com_apis.yaml index 7846de3caa..b3dee8a220 100644 --- a/common-go-libs/config/crd/bases/dp.wso2.com_apis.yaml +++ b/common-go-libs/config/crd/bases/dp.wso2.com_apis.yaml @@ -360,6 +360,184 @@ spec: type: object type: object served: true + storage: false + subresources: + status: {} + - additionalPrinterColumns: + - jsonPath: .spec.apiName + name: API Name + type: string + - jsonPath: .spec.apiVersion + name: Version + type: string + - jsonPath: .spec.basePath + name: BasePath + type: string + - jsonPath: .spec.organization + name: Organization + type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1beta1 + schema: + openAPIV3Schema: + description: API is the Schema for the apis API + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: APISpec defines the desired state of API + properties: + apiName: + description: APIName is the unique name of the API can be used to + uniquely identify an API. + maxLength: 60 + minLength: 1 + pattern: ^[^~!@#;:%^*()+={}|\<>"'',&$\[\]\/]*$ + type: string + apiProperties: + description: APIProperties denotes the custom properties of the API. + items: + description: Property holds key value pair of APIProperties + properties: + name: + type: string + value: + type: string + type: object + nullable: true + type: array + apiType: + description: APIType denotes the type of the API. Possible values + could be REST, GraphQL, gRPC + enum: + - REST + - GraphQL + - gRPC + type: string + apiVersion: + description: APIVersion is the version number of the API. + maxLength: 30 + minLength: 1 + pattern: ^[^~!@#;:%^*()+={}|\<>"'',&/$\[\]\s+\/]+$ + type: string + basePath: + description: 'BasePath denotes the basepath of the API. e.g: /pet-store-api/1.0.6' + pattern: ^[/][a-zA-Z0-9~/_.-]*$ + type: string + definitionFileRef: + description: DefinitionFileRef contains the definition of the API + in a ConfigMap. + type: string + definitionPath: + default: /api-definition + description: DefinitionPath contains the path to expose the API definition. + minLength: 1 + type: string + environment: + description: Environment denotes the environment of the API. + nullable: true + type: string + isDefaultVersion: + description: IsDefaultVersion indicates whether this API version should + be used as a default API + type: boolean + organization: + description: Organization denotes the organization. related to the + API + type: string + production: + description: 'Production contains a list of references to HttpRoutes + of type HttpRoute. xref: https://github.com/kubernetes-sigs/gateway-api/blob/main/apis/v1beta1/httproute_types.go' + items: + description: EnvConfig contains the environment specific configuration + properties: + routeRefs: + description: RouteRefs denotes the environment of the API. + items: + type: string + type: array + required: + - routeRefs + type: object + maxItems: 1 + nullable: true + type: array + sandbox: + description: 'Sandbox contains a list of references to HttpRoutes + of type HttpRoute. xref: https://github.com/kubernetes-sigs/gateway-api/blob/main/apis/v1beta1/httproute_types.go' + items: + description: EnvConfig contains the environment specific configuration + properties: + routeRefs: + description: RouteRefs denotes the environment of the API. + items: + type: string + type: array + required: + - routeRefs + type: object + maxItems: 1 + nullable: true + type: array + systemAPI: + description: SystemAPI denotes if it is an internal system API. + type: boolean + required: + - apiName + - apiType + - apiVersion + - basePath + - definitionPath + type: object + status: + description: APIStatus defines the observed state of API + properties: + deploymentStatus: + description: DeploymentStatus denotes the deployment status of the + API + properties: + accepted: + description: Accepted represents whether the API is accepted or + not. + type: boolean + events: + description: Events contains a list of events related to the API. + items: + type: string + type: array + message: + description: Message represents a user friendly message that explains + the current state of the API. + type: string + status: + description: Status denotes the state of the API in its lifecycle. + Possible values could be Accepted, Invalid, Deploy etc. + type: string + transitionTime: + description: TransitionTime represents the last known transition + timestamp. + format: date-time + type: string + required: + - accepted + - status + - transitionTime + type: object + type: object + type: object + served: true storage: true subresources: status: {} diff --git a/common-go-libs/config/crd/kustomization.yaml b/common-go-libs/config/crd/kustomization.yaml index 5c3c767fc1..8de9eda332 100644 --- a/common-go-libs/config/crd/kustomization.yaml +++ b/common-go-libs/config/crd/kustomization.yaml @@ -2,9 +2,10 @@ # since it depends on service name and namespace that are out of this kustomize package. # It should be run by config/default resources: -- bases/dp.wso2.com_aiproviders.yaml -- bases/cp.wso2.com_subscriptions.yaml -- bases/dp.wso2.com_airatelimitpolicies.yaml + - bases/dp.wso2.com_aiproviders.yaml + - bases/cp.wso2.com_subscriptions.yaml + - bases/dp.wso2.com_airatelimitpolicies.yaml + - bases/dp.wso2.com_apis.yaml #+kubebuilder:scaffold:crdkustomizeresource patchesStrategicMerge: @@ -13,6 +14,7 @@ patchesStrategicMerge: #- patches/webhook_in_aiproviders.yaml #- patches/webhook_in_subscriptions.yaml #- patches/webhook_in_airatelimitpolicies.yaml +#- patches/webhook_in_apis.yaml #+kubebuilder:scaffold:crdkustomizewebhookpatch # [CERTMANAGER] To enable cert-manager, uncomment all the sections with [CERTMANAGER] prefix. @@ -20,8 +22,9 @@ patchesStrategicMerge: #- patches/cainjection_in_aiproviders.yaml #- patches/cainjection_in_subscriptions.yaml #- patches/cainjection_in_airatelimitpolicies.yaml +#- patches/cainjection_in_apis.yaml #+kubebuilder:scaffold:crdkustomizecainjectionpatch # the following config is for teaching kustomize how to do kustomization for CRDs. configurations: -- kustomizeconfig.yaml + - kustomizeconfig.yaml diff --git a/common-go-libs/config/crd/patches/cainjection_in_dp_apis.yaml b/common-go-libs/config/crd/patches/cainjection_in_dp_apis.yaml new file mode 100644 index 0000000000..7f949667b8 --- /dev/null +++ b/common-go-libs/config/crd/patches/cainjection_in_dp_apis.yaml @@ -0,0 +1,7 @@ +# The following patch adds a directive for certmanager to inject CA into the CRD +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME) + name: apis.dp.wso2.com diff --git a/common-go-libs/config/crd/patches/webhook_in_dp_apis.yaml b/common-go-libs/config/crd/patches/webhook_in_dp_apis.yaml new file mode 100644 index 0000000000..ad0c609d56 --- /dev/null +++ b/common-go-libs/config/crd/patches/webhook_in_dp_apis.yaml @@ -0,0 +1,16 @@ +# The following patch enables a conversion webhook for the CRD +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + name: apis.dp.wso2.com +spec: + conversion: + strategy: Webhook + webhook: + clientConfig: + service: + namespace: system + name: webhook-service + path: /convert + conversionReviewVersions: + - v1 diff --git a/common-go-libs/config/default/manager_webhook_patch.yaml b/common-go-libs/config/default/manager_webhook_patch.yaml new file mode 100644 index 0000000000..738de350b7 --- /dev/null +++ b/common-go-libs/config/default/manager_webhook_patch.yaml @@ -0,0 +1,23 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: controller-manager + namespace: system +spec: + template: + spec: + containers: + - name: manager + ports: + - containerPort: 9443 + name: webhook-server + protocol: TCP + volumeMounts: + - mountPath: /tmp/k8s-webhook-server/serving-certs + name: cert + readOnly: true + volumes: + - name: cert + secret: + defaultMode: 420 + secretName: webhook-server-cert diff --git a/common-go-libs/config/default/webhookcainjection_patch.yaml b/common-go-libs/config/default/webhookcainjection_patch.yaml new file mode 100644 index 0000000000..76add1fe00 --- /dev/null +++ b/common-go-libs/config/default/webhookcainjection_patch.yaml @@ -0,0 +1,29 @@ +# This patch add annotation to admission webhook config and +# the variables $(CERTIFICATE_NAMESPACE) and $(CERTIFICATE_NAME) will be substituted by kustomize. +apiVersion: admissionregistration.k8s.io/v1 +kind: MutatingWebhookConfiguration +metadata: + labels: + app.kubernetes.io/name: mutatingwebhookconfiguration + app.kubernetes.io/instance: mutating-webhook-configuration + app.kubernetes.io/component: webhook + app.kubernetes.io/created-by: operator + app.kubernetes.io/part-of: operator + app.kubernetes.io/managed-by: kustomize + name: mutating-webhook-configuration + annotations: + cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME) +--- +apiVersion: admissionregistration.k8s.io/v1 +kind: ValidatingWebhookConfiguration +metadata: + labels: + app.kubernetes.io/name: validatingwebhookconfiguration + app.kubernetes.io/instance: validating-webhook-configuration + app.kubernetes.io/component: webhook + app.kubernetes.io/created-by: operator + app.kubernetes.io/part-of: operator + app.kubernetes.io/managed-by: kustomize + name: validating-webhook-configuration + annotations: + cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME) diff --git a/common-go-libs/config/rbac/dp_api_editor_role.yaml b/common-go-libs/config/rbac/dp_api_editor_role.yaml new file mode 100644 index 0000000000..2de3070c5b --- /dev/null +++ b/common-go-libs/config/rbac/dp_api_editor_role.yaml @@ -0,0 +1,31 @@ +# permissions for end users to edit apis. +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: + app.kubernetes.io/name: clusterrole + app.kubernetes.io/instance: api-editor-role + app.kubernetes.io/component: rbac + app.kubernetes.io/created-by: operator + app.kubernetes.io/part-of: operator + app.kubernetes.io/managed-by: kustomize + name: api-editor-role +rules: +- apiGroups: + - dp.wso2.com + resources: + - apis + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - dp.wso2.com + resources: + - apis/status + verbs: + - get diff --git a/common-go-libs/config/rbac/dp_api_viewer_role.yaml b/common-go-libs/config/rbac/dp_api_viewer_role.yaml new file mode 100644 index 0000000000..75e4c488a0 --- /dev/null +++ b/common-go-libs/config/rbac/dp_api_viewer_role.yaml @@ -0,0 +1,27 @@ +# permissions for end users to view apis. +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: + app.kubernetes.io/name: clusterrole + app.kubernetes.io/instance: api-viewer-role + app.kubernetes.io/component: rbac + app.kubernetes.io/created-by: operator + app.kubernetes.io/part-of: operator + app.kubernetes.io/managed-by: kustomize + name: api-viewer-role +rules: +- apiGroups: + - dp.wso2.com + resources: + - apis + verbs: + - get + - list + - watch +- apiGroups: + - dp.wso2.com + resources: + - apis/status + verbs: + - get diff --git a/common-go-libs/config/samples/dp_v1beta1_api.yaml b/common-go-libs/config/samples/dp_v1beta1_api.yaml new file mode 100644 index 0000000000..c84dd75cdb --- /dev/null +++ b/common-go-libs/config/samples/dp_v1beta1_api.yaml @@ -0,0 +1,12 @@ +apiVersion: dp.wso2.com/v1beta1 +kind: API +metadata: + labels: + app.kubernetes.io/name: api + app.kubernetes.io/instance: api-sample + app.kubernetes.io/part-of: operator + app.kubernetes.io/managed-by: kustomize + app.kubernetes.io/created-by: operator + name: api-sample +spec: + # TODO(user): Add fields here diff --git a/common-go-libs/config/webhook/kustomization.yaml b/common-go-libs/config/webhook/kustomization.yaml new file mode 100644 index 0000000000..9cf26134e4 --- /dev/null +++ b/common-go-libs/config/webhook/kustomization.yaml @@ -0,0 +1,6 @@ +resources: +- manifests.yaml +- service.yaml + +configurations: +- kustomizeconfig.yaml diff --git a/common-go-libs/config/webhook/kustomizeconfig.yaml b/common-go-libs/config/webhook/kustomizeconfig.yaml new file mode 100644 index 0000000000..25e21e3c96 --- /dev/null +++ b/common-go-libs/config/webhook/kustomizeconfig.yaml @@ -0,0 +1,25 @@ +# the following config is for teaching kustomize where to look at when substituting vars. +# It requires kustomize v2.1.0 or newer to work properly. +nameReference: +- kind: Service + version: v1 + fieldSpecs: + - kind: MutatingWebhookConfiguration + group: admissionregistration.k8s.io + path: webhooks/clientConfig/service/name + - kind: ValidatingWebhookConfiguration + group: admissionregistration.k8s.io + path: webhooks/clientConfig/service/name + +namespace: +- kind: MutatingWebhookConfiguration + group: admissionregistration.k8s.io + path: webhooks/clientConfig/service/namespace + create: true +- kind: ValidatingWebhookConfiguration + group: admissionregistration.k8s.io + path: webhooks/clientConfig/service/namespace + create: true + +varReference: +- path: metadata/annotations diff --git a/common-go-libs/config/webhook/manifests.yaml b/common-go-libs/config/webhook/manifests.yaml index f91b7f0244..487ae0aae6 100644 --- a/common-go-libs/config/webhook/manifests.yaml +++ b/common-go-libs/config/webhook/manifests.yaml @@ -24,6 +24,26 @@ webhooks: resources: - apipolicies sideEffects: None +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: webhook-service + namespace: system + path: /mutate-dp-wso2-com-v1beta1-api + failurePolicy: Fail + name: mapi.kb.io + rules: + - apiGroups: + - dp.wso2.com + apiVersions: + - v1beta1 + operations: + - CREATE + - UPDATE + resources: + - apis + sideEffects: None - admissionReviewVersions: - v1 clientConfig: @@ -210,6 +230,26 @@ webhooks: resources: - apipolicies sideEffects: None +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: webhook-service + namespace: system + path: /validate-dp-wso2-com-v1beta1-api + failurePolicy: Fail + name: vapi.kb.io + rules: + - apiGroups: + - dp.wso2.com + apiVersions: + - v1beta1 + operations: + - CREATE + - UPDATE + resources: + - apis + sideEffects: None - admissionReviewVersions: - v1 clientConfig: diff --git a/common-go-libs/config/webhook/service.yaml b/common-go-libs/config/webhook/service.yaml new file mode 100644 index 0000000000..3d52bb199a --- /dev/null +++ b/common-go-libs/config/webhook/service.yaml @@ -0,0 +1,20 @@ + +apiVersion: v1 +kind: Service +metadata: + labels: + app.kubernetes.io/name: service + app.kubernetes.io/instance: webhook-service + app.kubernetes.io/component: webhook + app.kubernetes.io/created-by: operator + app.kubernetes.io/part-of: operator + app.kubernetes.io/managed-by: kustomize + name: webhook-service + namespace: system +spec: + ports: + - port: 443 + protocol: TCP + targetPort: 9443 + selector: + control-plane: controller-manager diff --git a/common-go-libs/revive.toml b/common-go-libs/revive.toml index 46dfa7325e..2a89307bda 100644 --- a/common-go-libs/revive.toml +++ b/common-go-libs/revive.toml @@ -1,14 +1,14 @@ -ignoreGeneratedHeader = false -severity = "warning" confidence = 0.8 errorCode = 0 +ignoreGeneratedHeader = false +severity = "warning" warningCode = 0 [rule.blank-imports] [rule.context-as-argument] [rule.context-keys-type] [rule.dot-imports] -exclude = ["apis/dp/v1alpha1/webhook_suite_test.go", "apis/dp/v1alpha2/webhook_suite_test.go"] +exclude = ["apis/dp/v1alpha1/webhook_suite_test.go", "apis/dp/v1alpha2/webhook_suite_test.go", "apis/dp/v1beta1/webhook_suite_test.go"] [rule.error-return] [rule.error-strings] [rule.error-naming] From 31217add2b697a9f4cf4124ad029de0815ba00bc Mon Sep 17 00:00:00 2001 From: sgayangi Date: Fri, 23 Aug 2024 15:26:00 +0530 Subject: [PATCH 04/17] Update API version in cc --- .../controllers/dp/ratelimitpolicy_controller.go | 16 ++++++++-------- common-controller/internal/operator/operator.go | 7 +++++++ 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/common-controller/internal/operator/controllers/dp/ratelimitpolicy_controller.go b/common-controller/internal/operator/controllers/dp/ratelimitpolicy_controller.go index 7db849b0ff..369defe31a 100644 --- a/common-controller/internal/operator/controllers/dp/ratelimitpolicy_controller.go +++ b/common-controller/internal/operator/controllers/dp/ratelimitpolicy_controller.go @@ -46,8 +46,8 @@ import ( "github.com/wso2/apk/common-controller/internal/utils" xds "github.com/wso2/apk/common-controller/internal/xds" dpv1alpha1 "github.com/wso2/apk/common-go-libs/apis/dp/v1alpha1" - dpv1alpha2 "github.com/wso2/apk/common-go-libs/apis/dp/v1alpha2" dpv1alpha3 "github.com/wso2/apk/common-go-libs/apis/dp/v1alpha3" + dpv1beta1 "github.com/wso2/apk/common-go-libs/apis/dp/v1beta1" "github.com/wso2/apk/common-go-libs/constants" ) @@ -88,7 +88,7 @@ func NewratelimitController(mgr manager.Manager, ratelimitStore *cache.Ratelimit conf := config.ReadConfigs() predicates := []predicate.Predicate{predicate.NewPredicateFuncs(utils.FilterByNamespaces(conf.CommonController.Operator.Namespaces))} - if err := c.Watch(source.Kind(mgr.GetCache(), &dpv1alpha2.API{}), + if err := c.Watch(source.Kind(mgr.GetCache(), &dpv1beta1.API{}), handler.EnqueueRequestsFromMapFunc(ratelimitReconsiler.getRatelimitForAPI), predicates...); err != nil { loggers.LoggerAPKOperator.ErrorC(logging.PrintError(logging.Error2611, logging.BLOCKER, "Error watching API resources: %v", err)) @@ -190,7 +190,7 @@ func (ratelimitReconsiler *RateLimitPolicyReconciler) Reconcile(ctx context.Cont } func (ratelimitReconsiler *RateLimitPolicyReconciler) getRatelimitForAPI(ctx context.Context, obj k8client.Object) []reconcile.Request { - api, ok := obj.(*dpv1alpha2.API) + api, ok := obj.(*dpv1beta1.API) if !ok { loggers.LoggerAPKOperator.ErrorC(logging.PrintError(logging.Error2622, logging.TRIVIAL, "Unexpected object type, bypassing reconciliation: %v", api)) @@ -271,13 +271,13 @@ func (ratelimitReconsiler *RateLimitPolicyReconciler) marshelSubscriptionRateLim return resolveSubscriptionRatelimit } -func (ratelimitReconsiler *RateLimitPolicyReconciler) marshelRateLimit(ctx context.Context, ratelimitKey types.NamespacedName, +func (ratelimitReconciler *RateLimitPolicyReconciler) marshelRateLimit(ctx context.Context, ratelimitKey types.NamespacedName, ratelimitPolicy dpv1alpha3.RateLimitPolicy) ([]dpv1alpha1.ResolveRateLimitAPIPolicy, error) { policyList := []dpv1alpha1.ResolveRateLimitAPIPolicy{} - var api dpv1alpha2.API + var api dpv1beta1.API - if err := ratelimitReconsiler.client.Get(ctx, types.NamespacedName{ + if err := ratelimitReconciler.client.Get(ctx, types.NamespacedName{ Namespace: ratelimitKey.Namespace, Name: string(ratelimitPolicy.Spec.TargetRef.Name)}, &api); err != nil { @@ -317,7 +317,7 @@ func (ratelimitReconsiler *RateLimitPolicyReconciler) marshelRateLimit(ctx conte resolveRatelimit.Environment = environment if len(api.Spec.Production) > 0 && api.Spec.APIType == "REST" { - resolveResourceList, err := ratelimitReconsiler.getHTTPRouteResourceList(ctx, ratelimitKey, ratelimitPolicy, + resolveResourceList, err := ratelimitReconciler.getHTTPRouteResourceList(ctx, ratelimitKey, ratelimitPolicy, api.Spec.Production[0].RouteRefs) if err != nil { return nil, err @@ -329,7 +329,7 @@ func (ratelimitReconsiler *RateLimitPolicyReconciler) marshelRateLimit(ctx conte } if len(api.Spec.Sandbox) > 0 && api.Spec.APIType == "REST" { - resolveResourceList, err := ratelimitReconsiler.getHTTPRouteResourceList(ctx, ratelimitKey, ratelimitPolicy, + resolveResourceList, err := ratelimitReconciler.getHTTPRouteResourceList(ctx, ratelimitKey, ratelimitPolicy, api.Spec.Sandbox[0].RouteRefs) if err != nil { return nil, err diff --git a/common-controller/internal/operator/operator.go b/common-controller/internal/operator/operator.go index 9a2acd91ae..b94111c6e5 100644 --- a/common-controller/internal/operator/operator.go +++ b/common-controller/internal/operator/operator.go @@ -41,6 +41,7 @@ import ( dpv1alpha1 "github.com/wso2/apk/common-go-libs/apis/dp/v1alpha1" dpv1alpha2 "github.com/wso2/apk/common-go-libs/apis/dp/v1alpha2" dpv1alpha3 "github.com/wso2/apk/common-go-libs/apis/dp/v1alpha3" + dpv1beta1 "github.com/wso2/apk/common-go-libs/apis/dp/v1beta1" "k8s.io/apimachinery/pkg/runtime" utilruntime "k8s.io/apimachinery/pkg/util/runtime" clientgoscheme "k8s.io/client-go/kubernetes/scheme" @@ -68,6 +69,7 @@ func init() { utilruntime.Must(cpv1alpha2.AddToScheme(scheme)) utilruntime.Must(cpv1alpha3.AddToScheme(scheme)) utilruntime.Must(dpv1alpha3.AddToScheme(scheme)) + utilruntime.Must(dpv1beta1.AddToScheme(scheme)) //+kubebuilder:scaffold:scheme } @@ -137,6 +139,11 @@ func InitOperator(metricsConfig config.Metrics) { "Unable to create webhook API, error: %v", err)) } + if err = (&dpv1beta1.API{}).SetupWebhookWithManager(mgr); err != nil { + loggers.LoggerAPKOperator.ErrorC(logging.PrintError(logging.Error2601, logging.MAJOR, + "Unable to create webhook API, error: %v", err)) + } + if err = (&dpv1alpha1.RateLimitPolicy{}).SetupWebhookWithManager(mgr); err != nil { loggers.LoggerAPKOperator.ErrorC(logging.PrintError(logging.Error2637, logging.MAJOR, "Unable to create webhook for Ratelimit, error: %v", err)) From c52204b3dc68d676fae36f933e9b67253c6c2399 Mon Sep 17 00:00:00 2001 From: sgayangi Date: Fri, 23 Aug 2024 15:26:14 +0530 Subject: [PATCH 05/17] Add new API version and webhooks --- helm-charts/.helmignore | 5 + .../templates/crds/dp.wso2.com_apis.yaml | 178 ++++++++++++++++++ .../wso2-apk-config-deployer-api.yaml | 2 +- .../wso2-apk-config-generator-api.yaml | 2 +- .../api/notification-api.yaml | 2 +- .../adapter-mutating-webhook-config.yaml | 4 +- .../adapter-validation-webhook-config.yaml | 4 +- .../gateway-runtime/jwks-domain-api.yaml | 2 +- .../authenticationEndpoint-domain-api.yaml | 2 +- .../templates/idp/commonoauth-domain-api.yaml | 2 +- helm-charts/templates/idp/dcr-domain-api.yaml | 2 +- .../templates/idp/oauth-domain-api.yaml | 2 +- .../serviceAccount/apk-cluster-role.yaml | 11 +- helm-charts/values.yaml | 29 +-- 14 files changed, 220 insertions(+), 27 deletions(-) diff --git a/helm-charts/.helmignore b/helm-charts/.helmignore index eb3fdbc575..59d832423f 100644 --- a/helm-charts/.helmignore +++ b/helm-charts/.helmignore @@ -22,3 +22,8 @@ .idea/ *.tmproj .vscode/ +instructions.md +README.md +apk-crds.md +Version-Upgrade.md +samples/ diff --git a/helm-charts/templates/crds/dp.wso2.com_apis.yaml b/helm-charts/templates/crds/dp.wso2.com_apis.yaml index 399c0c01fc..9db6f95a04 100644 --- a/helm-charts/templates/crds/dp.wso2.com_apis.yaml +++ b/helm-charts/templates/crds/dp.wso2.com_apis.yaml @@ -373,6 +373,184 @@ spec: type: object type: object served: true + storage: false + subresources: + status: {} + - additionalPrinterColumns: + - jsonPath: .spec.apiName + name: API Name + type: string + - jsonPath: .spec.apiVersion + name: Version + type: string + - jsonPath: .spec.basePath + name: BasePath + type: string + - jsonPath: .spec.organization + name: Organization + type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1beta1 + schema: + openAPIV3Schema: + description: API is the Schema for the apis API + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: APISpec defines the desired state of API + properties: + apiName: + description: APIName is the unique name of the API can be used to + uniquely identify an API. + maxLength: 60 + minLength: 1 + pattern: ^[^~!@#;:%^*()+={}|\<>"'',&$\[\]\/]*$ + type: string + apiProperties: + description: APIProperties denotes the custom properties of the API. + items: + description: Property holds key value pair of APIProperties + properties: + name: + type: string + value: + type: string + type: object + nullable: true + type: array + apiType: + description: APIType denotes the type of the API. Possible values + could be REST, GraphQL, gRPC + enum: + - REST + - GraphQL + - gRPC + type: string + apiVersion: + description: APIVersion is the version number of the API. + maxLength: 30 + minLength: 1 + pattern: ^[^~!@#;:%^*()+={}|\<>"'',&/$\[\]\s+\/]+$ + type: string + basePath: + description: 'BasePath denotes the basepath of the API. e.g: /pet-store-api/1.0.6' + pattern: ^[/][a-zA-Z0-9~/_.-]*$ + type: string + definitionFileRef: + description: DefinitionFileRef contains the definition of the API + in a ConfigMap. + type: string + definitionPath: + default: /api-definition + description: DefinitionPath contains the path to expose the API definition. + minLength: 1 + type: string + environment: + description: Environment denotes the environment of the API. + nullable: true + type: string + isDefaultVersion: + description: IsDefaultVersion indicates whether this API version should + be used as a default API + type: boolean + organization: + description: Organization denotes the organization. related to the + API + type: string + production: + description: 'Production contains a list of references to HttpRoutes + of type HttpRoute. xref: https://github.com/kubernetes-sigs/gateway-api/blob/main/apis/v1beta1/httproute_types.go' + items: + description: EnvConfig contains the environment specific configuration + properties: + routeRefs: + description: RouteRefs denotes the environment of the API. + items: + type: string + type: array + required: + - routeRefs + type: object + maxItems: 1 + nullable: true + type: array + sandbox: + description: 'Sandbox contains a list of references to HttpRoutes + of type HttpRoute. xref: https://github.com/kubernetes-sigs/gateway-api/blob/main/apis/v1beta1/httproute_types.go' + items: + description: EnvConfig contains the environment specific configuration + properties: + routeRefs: + description: RouteRefs denotes the environment of the API. + items: + type: string + type: array + required: + - routeRefs + type: object + maxItems: 1 + nullable: true + type: array + systemAPI: + description: SystemAPI denotes if it is an internal system API. + type: boolean + required: + - apiName + - apiType + - apiVersion + - basePath + - definitionPath + type: object + status: + description: APIStatus defines the observed state of API + properties: + deploymentStatus: + description: DeploymentStatus denotes the deployment status of the + API + properties: + accepted: + description: Accepted represents whether the API is accepted or + not. + type: boolean + events: + description: Events contains a list of events related to the API. + items: + type: string + type: array + message: + description: Message represents a user friendly message that explains + the current state of the API. + type: string + status: + description: Status denotes the state of the API in its lifecycle. + Possible values could be Accepted, Invalid, Deploy etc. + type: string + transitionTime: + description: TransitionTime represents the last known transition + timestamp. + format: date-time + type: string + required: + - accepted + - status + - transitionTime + type: object + type: object + type: object + served: true storage: true subresources: status: {} diff --git a/helm-charts/templates/data-plane/config-deployer/wso2-apk-config-deployer-api.yaml b/helm-charts/templates/data-plane/config-deployer/wso2-apk-config-deployer-api.yaml index 63185b0146..f81c8731d0 100644 --- a/helm-charts/templates/data-plane/config-deployer/wso2-apk-config-deployer-api.yaml +++ b/helm-charts/templates/data-plane/config-deployer/wso2-apk-config-deployer-api.yaml @@ -15,7 +15,7 @@ # under the License. {{- if and .Values.wso2.apk.dp.enabled .Values.wso2.apk.dp.configdeployer.enabled }} kind: "API" -apiVersion: "dp.wso2.com/v1alpha2" +apiVersion: "dp.wso2.com/v1beta1" metadata: name: "{{ template "apk-helm.resource.prefix" . }}-wso2-apk-config-deployer-api" namespace: {{ .Release.Namespace }} diff --git a/helm-charts/templates/data-plane/config-deployer/wso2-apk-config-generator-api.yaml b/helm-charts/templates/data-plane/config-deployer/wso2-apk-config-generator-api.yaml index 708e3f87fc..3835db7756 100644 --- a/helm-charts/templates/data-plane/config-deployer/wso2-apk-config-generator-api.yaml +++ b/helm-charts/templates/data-plane/config-deployer/wso2-apk-config-generator-api.yaml @@ -15,7 +15,7 @@ # under the License. {{- if and .Values.wso2.apk.dp.enabled .Values.wso2.apk.dp.configdeployer.enabled }} kind: "API" -apiVersion: "dp.wso2.com/v1alpha2" +apiVersion: "dp.wso2.com/v1beta1" metadata: name: "{{ template "apk-helm.resource.prefix" . }}-wso2-apk-config-generator-api" namespace: {{ .Release.Namespace }} diff --git a/helm-charts/templates/data-plane/gateway-components/common-controller/api/notification-api.yaml b/helm-charts/templates/data-plane/gateway-components/common-controller/api/notification-api.yaml index 04fd5e4a19..f6c677cc0c 100644 --- a/helm-charts/templates/data-plane/gateway-components/common-controller/api/notification-api.yaml +++ b/helm-charts/templates/data-plane/gateway-components/common-controller/api/notification-api.yaml @@ -16,7 +16,7 @@ {{- if and .Values.wso2.apk.dp.enabled .Values.wso2.apk.dp.commonController }} kind: "API" -apiVersion: "dp.wso2.com/v1alpha2" +apiVersion: "dp.wso2.com/v1beta1" metadata: name: "{{ template "apk-helm.resource.prefix" . }}-wso2-apk-notification-api" namespace: {{ .Release.Namespace }} diff --git a/helm-charts/templates/data-plane/gateway-components/common-controller/webhook/adapter-mutating-webhook-config.yaml b/helm-charts/templates/data-plane/gateway-components/common-controller/webhook/adapter-mutating-webhook-config.yaml index ebcd004fa3..92a0934c8f 100644 --- a/helm-charts/templates/data-plane/gateway-components/common-controller/webhook/adapter-mutating-webhook-config.yaml +++ b/helm-charts/templates/data-plane/gateway-components/common-controller/webhook/adapter-mutating-webhook-config.yaml @@ -31,14 +31,14 @@ webhooks: service: name: {{ template "apk-helm.resource.prefix" . }}-common-controller-service namespace: {{ .Release.Namespace }} - path: /mutate-dp-wso2-com-v1alpha2-api + path: /mutate-dp-wso2-com-v1beta1-api failurePolicy: Fail name: mapi.kb.io rules: - apiGroups: - dp.wso2.com apiVersions: - - v1alpha2 + - v1beta1 operations: - CREATE - UPDATE diff --git a/helm-charts/templates/data-plane/gateway-components/common-controller/webhook/adapter-validation-webhook-config.yaml b/helm-charts/templates/data-plane/gateway-components/common-controller/webhook/adapter-validation-webhook-config.yaml index f8474e594d..6b0aa2146e 100644 --- a/helm-charts/templates/data-plane/gateway-components/common-controller/webhook/adapter-validation-webhook-config.yaml +++ b/helm-charts/templates/data-plane/gateway-components/common-controller/webhook/adapter-validation-webhook-config.yaml @@ -51,14 +51,14 @@ webhooks: service: name: {{ template "apk-helm.resource.prefix" . }}-common-controller-service namespace: {{ .Release.Namespace }} - path: /validate-dp-wso2-com-v1alpha2-api + path: /validate-dp-wso2-com-v1beta1-api failurePolicy: Fail name: vapi.kb.io rules: - apiGroups: - dp.wso2.com apiVersions: - - v1alpha2 + - v1beta1 operations: - CREATE - UPDATE diff --git a/helm-charts/templates/data-plane/gateway-components/gateway-runtime/jwks-domain-api.yaml b/helm-charts/templates/data-plane/gateway-components/gateway-runtime/jwks-domain-api.yaml index 5da3d82955..4e97cb9248 100644 --- a/helm-charts/templates/data-plane/gateway-components/gateway-runtime/jwks-domain-api.yaml +++ b/helm-charts/templates/data-plane/gateway-components/gateway-runtime/jwks-domain-api.yaml @@ -15,7 +15,7 @@ # under the License. {{- if .Values.idp.enabled }} kind: "API" -apiVersion: "dp.wso2.com/v1alpha2" +apiVersion: "dp.wso2.com/v1beta1" metadata: name: {{ template "apk-helm.resource.prefix" . }}-jwks-endpoint-ds-api namespace: {{ .Release.Namespace }} diff --git a/helm-charts/templates/idp/authenticationEndpoint-domain-api.yaml b/helm-charts/templates/idp/authenticationEndpoint-domain-api.yaml index 0929f16582..4d4325330e 100644 --- a/helm-charts/templates/idp/authenticationEndpoint-domain-api.yaml +++ b/helm-charts/templates/idp/authenticationEndpoint-domain-api.yaml @@ -15,7 +15,7 @@ # under the License. {{- if .Values.idp.enabled }} kind: "API" -apiVersion: "dp.wso2.com/v1alpha2" +apiVersion: "dp.wso2.com/v1beta1" metadata: name: {{ template "apk-helm.resource.prefix" . }}-authentication-endpoint-ds-api namespace: {{ .Release.Namespace }} diff --git a/helm-charts/templates/idp/commonoauth-domain-api.yaml b/helm-charts/templates/idp/commonoauth-domain-api.yaml index a538daa857..cdd7a2a6d9 100644 --- a/helm-charts/templates/idp/commonoauth-domain-api.yaml +++ b/helm-charts/templates/idp/commonoauth-domain-api.yaml @@ -15,7 +15,7 @@ # under the License. {{- if .Values.idp.enabled }} kind: "API" -apiVersion: "dp.wso2.com/v1alpha2" +apiVersion: "dp.wso2.com/v1beta1" metadata: name: {{ template "apk-helm.resource.prefix" . }}-commonoauth-api namespace: {{ .Release.Namespace }} diff --git a/helm-charts/templates/idp/dcr-domain-api.yaml b/helm-charts/templates/idp/dcr-domain-api.yaml index fba48631a8..aa6d70eb31 100644 --- a/helm-charts/templates/idp/dcr-domain-api.yaml +++ b/helm-charts/templates/idp/dcr-domain-api.yaml @@ -15,7 +15,7 @@ # under the License. {{- if .Values.idp.enabled }} kind: "API" -apiVersion: "dp.wso2.com/v1alpha2" +apiVersion: "dp.wso2.com/v1beta1" metadata: name: {{ template "apk-helm.resource.prefix" . }}-dcr-api namespace: {{ .Release.Namespace }} diff --git a/helm-charts/templates/idp/oauth-domain-api.yaml b/helm-charts/templates/idp/oauth-domain-api.yaml index a4c4214e17..f67fecbb48 100644 --- a/helm-charts/templates/idp/oauth-domain-api.yaml +++ b/helm-charts/templates/idp/oauth-domain-api.yaml @@ -15,7 +15,7 @@ # under the License. {{- if .Values.idp.enabled }} kind: "API" -apiVersion: "dp.wso2.com/v1alpha2" +apiVersion: "dp.wso2.com/v1beta1" metadata: name: {{ template "apk-helm.resource.prefix" . }}-oauth-api namespace: {{ .Release.Namespace }} diff --git a/helm-charts/templates/serviceAccount/apk-cluster-role.yaml b/helm-charts/templates/serviceAccount/apk-cluster-role.yaml index 2581e93039..3022a5b82d 100644 --- a/helm-charts/templates/serviceAccount/apk-cluster-role.yaml +++ b/helm-charts/templates/serviceAccount/apk-cluster-role.yaml @@ -24,7 +24,7 @@ rules: resources: ["services","configmaps","secrets"] verbs: ["get","list","watch","update","delete","create"] - apiGroups: ["gateway.networking.k8s.io"] - resources: ["httproutes","gateways","gatewayclasses"] + resources: ["httproutes","gateways","gatewayclasses","grpcroutes"] verbs: ["get","list","watch","update","delete","create"] - apiGroups: [ "gateway.networking.k8s.io" ] resources: [ "gateways/status","gatewayclasses/status","httproutes/status" ] @@ -62,6 +62,15 @@ rules: - apiGroups: ["dp.wso2.com"] resources: ["apipolicies/finalizers"] verbs: ["update"] + - apiGroups: ["dp.wso2.com"] + resources: ["grpcroutes"] + verbs: ["get","list","watch","update","delete","create"] + - apiGroups: ["dp.wso2.com"] + resources: ["grpcroutes/finalizers"] + verbs: ["update"] + - apiGroups: ["dp.wso2.com"] + resources: ["grpcroutes/status"] + verbs: ["get","patch","update"] - apiGroups: ["dp.wso2.com"] resources: ["apipolicies/status"] verbs: ["get","patch","update"] diff --git a/helm-charts/values.yaml b/helm-charts/values.yaml index 2c3672cb16..799b18f26c 100644 --- a/helm-charts/values.yaml +++ b/helm-charts/values.yaml @@ -48,9 +48,9 @@ wso2: # secretName: "wso2apk-idp-signing" # fileName: "idp.crt" # cp: - # enabledSubscription: true - # host: "apim-apk-agent-service.apk.svc.cluster.local" - # skipSSLVerification: true + # enabledSubscription: true + # host: "apim-apk-agent-service.apk.svc.cluster.local" + # skipSSLVerification: true # skipSSLVerification: false # persistence: # type: "K8s" @@ -59,7 +59,8 @@ wso2: gateway: listener: hostname: "gw.wso2.com" - service: {} + service: + {} # secretName: "idp-tls" # partitionServer: # enabled: false @@ -245,16 +246,16 @@ wso2: image: wso2/apk-enforcer:1.2.0-m1 security: sslHostname: "enforcer" -# logging: -# level: DEBUG - configs: -# tls: -# secretName: "router-cert" -# certKeyFilename: "" -# certFilename: "" - # JWKSClient: - # skipSSLVerification: false - # hostnameVerifier: "AllowAll" + # logging: + # level: DEBUG + # configs: + # tls: + # secretName: "router-cert" + # certKeyFilename: "" + # certFilename: "" + # JWKSClient: + # skipSSLVerification: false + # hostnameVerifier: "AllowAll" metrics: enabled: false From ab486de835a51f49cbb43196a9699429837c2cb2 Mon Sep 17 00:00:00 2001 From: sgayangi Date: Fri, 23 Aug 2024 15:26:37 +0530 Subject: [PATCH 06/17] Add config deployer changes for gRPC APIs --- .../ballerina/APIClient.bal | 168 +- .../ballerina/Ballerina.toml | 10 + .../ballerina/Ballerina.toml.template | 6 + .../ballerina/ConfigGenreatorClient.bal | 20 +- .../ballerina/DeployerClient.bal | 94 +- .../ballerina/K8sClient.bal | 44 +- .../ballerina/constants.bal | 4 +- .../ballerina/modules/model/API.bal | 4 +- .../ballerina/modules/model/APIArtifact.bal | 2 + .../ballerina/modules/model/GRPCRoute.bal | 70 + .../RuntimeAPICommonUtil.bal | 7 +- .../config-deployer-service/java/build.gradle | 1 + .../org/wso2/apk/config/APIConstants.java | 5 +- .../apk/config/DefinitionParserFactory.java | 6 + .../wso2/apk/config/RuntimeAPICommonUtil.java | 57 +- .../wso2/apk/config/api/ExceptionCodes.java | 1769 +++++++++-------- .../apk/config/definitions/OASParserUtil.java | 118 +- .../apk/config/definitions/ProtoParser.java | 336 ++++ .../config/definitions/ProtoParserUtil.java | 70 + .../java/org/wso2/apk/config/model/API.java | 9 + 20 files changed, 1906 insertions(+), 894 deletions(-) create mode 100644 runtime/config-deployer-service/ballerina/modules/model/GRPCRoute.bal create mode 100644 runtime/config-deployer-service/java/src/main/java/org/wso2/apk/config/definitions/ProtoParser.java create mode 100644 runtime/config-deployer-service/java/src/main/java/org/wso2/apk/config/definitions/ProtoParserUtil.java diff --git a/runtime/config-deployer-service/ballerina/APIClient.bal b/runtime/config-deployer-service/ballerina/APIClient.bal index 1469775e03..a5bda731ef 100644 --- a/runtime/config-deployer-service/ballerina/APIClient.bal +++ b/runtime/config-deployer-service/ballerina/APIClient.bal @@ -46,7 +46,7 @@ public class APIClient { encodedString = encodedString.substring(0, encodedString.length() - 1); } APKConf apkConf = { - name: api.getName(), + name: self.getAPIName(api.getName(), api.getType()), basePath: api.getBasePath().length() > 0 ? api.getBasePath() : encodedString, version: api.getVersion(), 'type: api.getType() == "" ? API_TYPE_REST : api.getType().toUpperAscii() @@ -138,6 +138,11 @@ public class APIClient { _ = check self.setRoute(apiArtifact, apkConf, createdEndpoints.hasKey(PRODUCTION_TYPE) ? createdEndpoints.get(PRODUCTION_TYPE) : (), uniqueId, PRODUCTION_TYPE, organization); _ = check self.setRoute(apiArtifact, apkConf, createdEndpoints.hasKey(SANDBOX_TYPE) ? createdEndpoints.get(SANDBOX_TYPE) : (), uniqueId, SANDBOX_TYPE, organization); string|json generatedSwagger = check self.retrieveGeneratedSwaggerDefinition(apkConf, definition); + if generatedSwagger is string { + log:printInfo(generatedSwagger); + } else { + log:printInfo(generatedSwagger.toJsonString()); + } check self.retrieveGeneratedConfigmapForDefinition(apiArtifact, apkConf, generatedSwagger, uniqueId, organization); self.generateAndSetAPICRArtifact(apiArtifact, apkConf, organization); _ = check self.generateAndSetPolicyCRArtifact(apiArtifact, apkConf, organization); @@ -288,6 +293,14 @@ public class APIClient { return backendSpec.protocol + "://" + backendSpec.services[0].host + backendSpec.services[0].port.toString(); } + isolated function returnFullGRPCBasePath(string basePath, string 'version) returns string { + string fullBasePath = basePath; + if (!string:endsWith(basePath, 'version)) { + fullBasePath = string:'join(".", basePath, 'version); + } + return fullBasePath; + } + private isolated function retrieveGeneratedConfigmapForDefinition(model:APIArtifact apiArtifact, APKConf apkConf, string|json generatedSwaggerDefinition, string uniqueId, commons:Organization organization) returns error? { byte[]|javaio:IOException compressedContent = []; if apkConf.'type == API_TYPE_REST { @@ -425,7 +438,7 @@ public class APIClient { }, spec: { apiName: apkConf.name, - apiType: apkConf.'type == "GRAPHQL" ? "GraphQL" : apkConf.'type, + apiType: self.getAPIType(apkConf.'type), apiVersion: apkConf.'version, basePath: self.returnFullBasePath(apkConf.basePath, apkConf.'version), isDefaultVersion: apkConf.defaultVersion, @@ -463,6 +476,18 @@ public class APIClient { sandboxRoutes.push(httpRoute.metadata.name); } } + } else if apkConf.'type == API_TYPE_GRPC { + k8sAPI.spec.basePath = self.returnFullGRPCBasePath(apkConf.basePath, apkConf.'version); + foreach model:GRPCRoute grpcRoute in apiArtifact.productionGrpcRoutes { + if grpcRoute.spec.rules.length() > 0 { + productionRoutes.push(grpcRoute.metadata.name); + } + } + foreach model:GRPCRoute grpcRoute in apiArtifact.sandboxGrpcRoutes { + if grpcRoute.spec.rules.length() > 0 { + sandboxRoutes.push(grpcRoute.metadata.name); + } + } } if productionRoutes.length() > 0 { @@ -488,6 +513,15 @@ public class APIClient { return uniqueId + "-definition"; } + private isolated function getAPIType(string apiType) returns string { + if apiType.toUpperAscii() == "GRAPHQL" { + return "GraphQL"; + } else if apiType.toUpperAscii() == "GRPC" { + return "gRPC"; + } + return apiType; + } + private isolated function retrieveDisableAuthenticationRefName(APKConf apkConf, string 'type, commons:Organization organization) returns string { return self.getUniqueIdForAPI(apkConf.name, apkConf.'version, organization) + "-" + 'type + "-no-authentication"; } @@ -562,6 +596,26 @@ public class APIClient { apiArtifact.sandboxHttpRoutes.push(httpRoute); } } + } else if apkConf.'type == API_TYPE_GRPC { + model:GRPCRoute grpcRoute = { + metadata: + { + name: uniqueId + "-" + endpointType + "-grpcroute-" + count.toString(), + labels: self.getLabels(apkConf, organization) + }, + spec: { + parentRefs: self.generateAndRetrieveParentRefs(apkConf, uniqueId), + rules: check self.generateGRPCRouteRules(apiArtifact, apkConf, endpoint, endpointType, organization), + hostnames: self.getHostNames(apkConf, uniqueId, endpointType, organization) + } + }; + if grpcRoute.spec.rules.length() > 0 { + if endpointType == PRODUCTION_TYPE { + apiArtifact.productionGrpcRoutes.push(grpcRoute); + } else { + apiArtifact.sandboxGrpcRoutes.push(grpcRoute); + } + } } else { return e909018("Invalid API Type specified"); } @@ -583,7 +637,7 @@ public class APIClient { APKOperations[]? operations = apkConf.operations; if operations is APKOperations[] { foreach APKOperations operation in operations { - model:HTTPRouteRule|model:GQLRouteRule|() routeRule = check self.generateRouteRule(apiArtifact, apkConf, endpoint, operation, endpointType, organization); + model:HTTPRouteRule|model:GQLRouteRule|model:GRPCRouteRule|() routeRule = check self.generateRouteRule(apiArtifact, apkConf, endpoint, operation, endpointType, organization); if routeRule is model:HTTPRouteRule { model:HTTPRouteFilter[]? filters = routeRule.filters; if filters is () { @@ -642,7 +696,7 @@ public class APIClient { APKOperations[]? operations = apkConf.operations; if operations is APKOperations[] { foreach APKOperations operation in operations { - model:HTTPRouteRule|model:GQLRouteRule|() routeRule = check self.generateRouteRule(apiArtifact, apkConf, endpoint, operation, endpointType, organization); + model:HTTPRouteRule|model:GQLRouteRule|model:GRPCRouteRule|() routeRule = check self.generateRouteRule(apiArtifact, apkConf, endpoint, operation, endpointType, organization); if routeRule is model:GQLRouteRule { model:GQLRouteFilter[]? filters = routeRule.filters; if filters is () { @@ -696,6 +750,66 @@ public class APIClient { return gqlRouteRules; } + private isolated function generateGRPCRouteRules(model:APIArtifact apiArtifact, APKConf apkConf, model:Endpoint? endpoint, string endpointType, commons:Organization organization) returns model:GRPCRouteRule[]|commons:APKError|error { + model:GRPCRouteRule[] grpcRouteRules = []; + APKOperations[]? operations = apkConf.operations; + if operations is APKOperations[] { + foreach APKOperations operation in operations { + model:HTTPRouteRule|model:GQLRouteRule|model:GRPCRouteRule|() routeRule = check self.generateRouteRule(apiArtifact, apkConf, endpoint, operation, endpointType, organization); + if routeRule is model:GRPCRouteRule { + model:GRPCRouteFilter[]? filters = routeRule.filters; + if filters is () { + filters = []; + routeRule.filters = filters; + } + string disableAuthenticationRefName = self.retrieveDisableAuthenticationRefName(apkConf, endpointType, organization); + if !(operation.secured ?: true) { + if !apiArtifact.authenticationMap.hasKey(disableAuthenticationRefName) { + model:Authentication generateDisableAuthenticationCR = self.generateDisableAuthenticationCR(apiArtifact, apkConf, endpointType, organization); + apiArtifact.authenticationMap[disableAuthenticationRefName] = generateDisableAuthenticationCR; + } + model:GRPCRouteFilter disableAuthenticationFilter = {'type: "ExtensionRef", extensionRef: {group: "dp.wso2.com", kind: "Authentication", name: disableAuthenticationRefName}}; + (filters).push(disableAuthenticationFilter); + } + string[]? scopes = operation.scopes; + if scopes is string[] { + int count = 1; + foreach string scope in scopes { + model:Scope scopeCr; + if apiArtifact.scopes.hasKey(scope) { + scopeCr = apiArtifact.scopes.get(scope); + } else { + scopeCr = self.generateScopeCR(operation, apiArtifact, apkConf, organization, scope, count); + count = count + 1; + } + model:GRPCRouteFilter scopeFilter = {'type: "ExtensionRef", extensionRef: {group: "dp.wso2.com", kind: scopeCr.kind, name: scopeCr.metadata.name}}; + (filters).push(scopeFilter); + } + } + if operation.rateLimit != () { + model:RateLimitPolicy? rateLimitPolicyCR = self.generateRateLimitPolicyCR(apkConf, operation.rateLimit, apiArtifact.uniqueId, operation, organization); + if rateLimitPolicyCR != () { + apiArtifact.rateLimitPolicies[rateLimitPolicyCR.metadata.name] = rateLimitPolicyCR; + model:GRPCRouteFilter rateLimitPolicyFilter = {'type: "ExtensionRef", extensionRef: {group: "dp.wso2.com", kind: "RateLimitPolicy", name: rateLimitPolicyCR.metadata.name}}; + (filters).push(rateLimitPolicyFilter); + } + } + if operation.operationPolicies != () { + model:APIPolicy? apiPolicyCR = check self.generateAPIPolicyAndBackendCR(apiArtifact, apkConf, operation, operation.operationPolicies, organization, apiArtifact.uniqueId); + + if apiPolicyCR != () { + apiArtifact.apiPolicies[apiPolicyCR.metadata.name] = apiPolicyCR; + model:GRPCRouteFilter apiPolicyFilter = {'type: "ExtensionRef", extensionRef: {group: "dp.wso2.com", kind: "APIPolicy", name: apiPolicyCR.metadata.name}}; + (filters).push(apiPolicyFilter); + } + } + grpcRouteRules.push(routeRule); + } + } + } + return grpcRouteRules; + } + private isolated function generateAPIPolicyAndBackendCR(model:APIArtifact apiArtifact, APKConf apkConf, APKOperations? operations, APIOperationPolicies? policies, commons:Organization organization, string targetRefName) returns model:APIPolicy?|error { model:APIPolicyData defaultSpecData = {}; APKRequestOperationPolicy[]? request = policies?.request; @@ -781,13 +895,16 @@ public class APIClient { return authentication; } - private isolated function generateRouteRule(model:APIArtifact apiArtifact, APKConf apkConf, model:Endpoint? endpoint, APKOperations operation, string endpointType, commons:Organization organization) returns model:HTTPRouteRule|model:GQLRouteRule|()|commons:APKError { + private isolated function generateRouteRule(model:APIArtifact apiArtifact, APKConf apkConf, model:Endpoint? endpoint, APKOperations operation, string endpointType, commons:Organization organization) + returns model:HTTPRouteRule|model:GQLRouteRule|model:GRPCRouteRule|()|commons:APKError { + do { EndpointConfigurations? endpointConfig = operation.endpointConfigurations; model:Endpoint? endpointToUse = (); if endpointConfig is EndpointConfigurations { // endpointConfig presence at Operation Level. - map operationalLevelBackend = check self.createAndAddBackendServices(apiArtifact, apkConf, endpointConfig, operation, endpointType, organization); + map operationalLevelBackend = check self.createAndAddBackendServices(apiArtifact, apkConf, + endpointConfig, operation, endpointType, organization); if operationalLevelBackend.hasKey(endpointType) { endpointToUse = operationalLevelBackend.get(endpointType); } @@ -817,6 +934,14 @@ public class APIClient { httpRouteRule.backendRefs = self.retrieveGeneratedBackend(apkConf, endpointToUse, endpointType); } return httpRouteRule; + } else if apkConf.'type == API_TYPE_GRPC { + model:GRPCRouteMatch[]|error routeMatches = self.retrieveGRPCMatches(apkConf, operation, organization); + if routeMatches is model:GRPCRouteMatch[] && routeMatches.length() > 0 { + model:GRPCRouteRule grpcRouteRule = {matches: routeMatches, backendRefs: self.retrieveGeneratedBackend(apkConf, endpointToUse, endpointType)}; + return grpcRouteRule; + } else { + return e909022("Provided Type currently not supported for GRPC APIs.", error("Provided Type currently not supported for GRPC APIs.")); + } } else { return e909018("Invalid API Type specified"); } @@ -1116,7 +1241,13 @@ public class APIClient { gqlRouteMatch.push(gqlRoute); } return gqlRouteMatch; + } + private isolated function retrieveGRPCMatches(APKConf apkConf, APKOperations apiOperation, commons:Organization organization) returns model:GRPCRouteMatch[] { + model:GRPCRouteMatch[] grpcRouteMatch = []; + model:GRPCRouteMatch grpcRoute = self.retrieveGRPCRouteMatch(apiOperation); + grpcRouteMatch.push(grpcRoute); + return grpcRouteMatch; } private isolated function retrieveHttpRouteMatch(APKConf apkConf, APKOperations apiOperation, commons:Organization organization) returns model:HTTPRouteMatch { @@ -1133,6 +1264,17 @@ public class APIClient { } } + private isolated function retrieveGRPCRouteMatch(APKOperations apiOperation) returns model:GRPCRouteMatch { + model:GRPCRouteMatch grpcRouteMatch = { + method: { + 'type: "Exact", + 'service: apiOperation.target, + method: apiOperation.verb + } + }; + return grpcRouteMatch; + } + isolated function retrieveGeneratedSwaggerDefinition(APKConf apkConf, string? definition) returns string|json|commons:APKError|error { runtimeModels:API api1 = runtimeModels:newAPI1(); api1.setName(apkConf.name); @@ -1864,7 +2006,7 @@ public class APIClient { } else if definitionFile.fileName.endsWith(".json") { apiDefinition = definitionFileContent; } - } else if apiType == API_TYPE_GRAPHQL { + } else if apiType == API_TYPE_GRAPHQL || apiType == API_TYPE_GRPC { apiDefinition = definitionFileContent; } if apkConf is () { @@ -1881,4 +2023,16 @@ public class APIClient { } } + + private isolated function getAPIName(string apiName, string apiType) returns string { + if apiType.toUpperAscii() == API_TYPE_GRPC { + return self.getUniqueNameForGrpcApi(apiName); + } + return apiName; + } + + public isolated function getUniqueNameForGrpcApi(string concatanatedServices) returns string { + byte[] hashedValue = crypto:hashSha1(concatanatedServices.toBytes()); + return hashedValue.toBase16(); + } } diff --git a/runtime/config-deployer-service/ballerina/Ballerina.toml b/runtime/config-deployer-service/ballerina/Ballerina.toml index 68828664db..5aca2f1efa 100644 --- a/runtime/config-deployer-service/ballerina/Ballerina.toml +++ b/runtime/config-deployer-service/ballerina/Ballerina.toml @@ -373,6 +373,11 @@ groupId = "joda-time" artifactId = "joda-time" version = "2.10.2" +# transitive dependency of com.google.protobuf:protobuf-java:3.21.7 +[[platform.java11.dependency]] +groupId = "com.google.protobuf" +artifactId = "protobuf-java" +version = "3.21.7" # transitive dependency of org.wso2.apk:org.wso2.apk.config:1.2.0-SNAPSHOT [[platform.java11.dependency]] @@ -385,3 +390,8 @@ version = "20231013" groupId = "commons-lang" artifactId = "commons-lang" version = "2.4" + +[[platform.java11.dependency]] +groupId = "org.wso2.apk" +artifactId = "org.wso2.apk.config" +version = "1.1.0-SNAPSHOT" diff --git a/runtime/config-deployer-service/ballerina/Ballerina.toml.template b/runtime/config-deployer-service/ballerina/Ballerina.toml.template index 0a4e99c589..92a1405cdd 100644 --- a/runtime/config-deployer-service/ballerina/Ballerina.toml.template +++ b/runtime/config-deployer-service/ballerina/Ballerina.toml.template @@ -319,6 +319,12 @@ groupId = "org.reactivestreams" artifactId = "reactive-streams" version = "1.0.3" +# transitive dependency of com.google.protobuf:protobuf-java:3.21.7 +[[platform.java11.dependency]] +groupId = "com.google.protobuf" +artifactId = "protobuf-java" +version = "3.21.7" + # transitive dependency of org.wso2.apk:org.wso2.apk.config:1.0.0-SNAPSHOT [[platform.java11.dependency]] groupId = "com.fasterxml.jackson.core" diff --git a/runtime/config-deployer-service/ballerina/ConfigGenreatorClient.bal b/runtime/config-deployer-service/ballerina/ConfigGenreatorClient.bal index e410cdb51d..07f4f649ba 100644 --- a/runtime/config-deployer-service/ballerina/ConfigGenreatorClient.bal +++ b/runtime/config-deployer-service/ballerina/ConfigGenreatorClient.bal @@ -1,17 +1,19 @@ -import wso2/apk_common_lib as commons; -import ballerina/http; -import config_deployer_service.org.wso2.apk.config.api as runtimeapi; -import config_deployer_service.org.wso2.apk.config.model as runtimeModels; import config_deployer_service.java.util as utilapis; -import config_deployer_service.org.wso2.apk.config as runtimeUtil; -import ballerina/mime; -import ballerina/jballerina.java; import config_deployer_service.model; -import ballerina/io; +import config_deployer_service.org.wso2.apk.config as runtimeUtil; +import config_deployer_service.org.wso2.apk.config.api as runtimeapi; +import config_deployer_service.org.wso2.apk.config.model as runtimeModels; + import ballerina/file; +import ballerina/http; +import ballerina/io; +import ballerina/jballerina.java; import ballerina/log; +import ballerina/mime; import ballerina/uuid; +import wso2/apk_common_lib as commons; + public class ConfigGeneratorClient { public isolated function getGeneratedAPKConf(http:Request request) returns OkAnydata|commons:APKError|BadRequestError { @@ -93,7 +95,7 @@ public class ConfigGeneratorClient { private isolated function validateAndRetrieveDefinition(string 'type, string? url, byte[]? content, string? fileName) returns runtimeapi:APIDefinitionValidationResponse|runtimeapi:APIManagementException|error|commons:APKError { runtimeapi:APIDefinitionValidationResponse|runtimeapi:APIManagementException|error validationResponse; boolean typeAvailable = 'type.length() > 0; - string[] ALLOWED_API_DEFINITION_TYPES = [API_TYPE_REST, API_TYPE_GRAPHQL, "ASYNC"]; + string[] ALLOWED_API_DEFINITION_TYPES = [API_TYPE_REST, API_TYPE_GRAPHQL, API_TYPE_GRPC, API_TYPE_ASYNC]; if !typeAvailable { return e909005("type"); } diff --git a/runtime/config-deployer-service/ballerina/DeployerClient.bal b/runtime/config-deployer-service/ballerina/DeployerClient.bal index e5beb5d2df..943a525b47 100644 --- a/runtime/config-deployer-service/ballerina/DeployerClient.bal +++ b/runtime/config-deployer-service/ballerina/DeployerClient.bal @@ -1,9 +1,11 @@ -import ballerina/mime; import config_deployer_service.model; + import ballerina/http; -import wso2/apk_common_lib as commons; -import ballerina/log; import ballerina/lang.value; +import ballerina/log; +import ballerina/mime; + +import wso2/apk_common_lib as commons; public class DeployerClient { public isolated function handleAPIDeployment(http:Request request, commons:Organization organization) returns commons:APKError|http:Response { @@ -82,6 +84,7 @@ public class DeployerClient { private isolated function deployAPIToK8s(model:APIArtifact apiArtifact) returns commons:APKError|model:API { do { + log:printInfo("DEPLOYING API TO K8s"); model:Partition apiPartition; model:API? existingAPI; model:Partition|() availablePartitionForAPI = check partitionResolver.getAvailablePartitionForAPI(apiArtifact.uniqueId, apiArtifact.organization); @@ -95,6 +98,7 @@ public class DeployerClient { apiArtifact.namespace = apiPartition.namespace; if existingAPI is model:API { check self.deleteHttpRoutes(existingAPI, apiArtifact?.organization); + check self.deleteGrpcRoutes(existingAPI, apiArtifact?.organization); check self.deleteAuthenticationCRs(existingAPI, apiArtifact?.organization); _ = check self.deleteScopeCrsForAPI(existingAPI, apiArtifact?.organization); check self.deleteBackends(existingAPI, apiArtifact?.organization); @@ -121,9 +125,9 @@ public class DeployerClient { check self.deployInterceptorServiceCRs(apiArtifact, ownerReference); check self.deployBackendJWTConfigs(apiArtifact, ownerReference); check self.deployAPIPolicyCRs(apiArtifact, ownerReference); - - check self.deployRoutes(apiArtifact.productionHttpRoutes, apiArtifact.productionGqlRoutes, apiArtifact?.namespace, ownerReference); - check self.deployRoutes(apiArtifact.sandboxHttpRoutes, apiArtifact.sandboxGqlRoutes, apiArtifact?.namespace, ownerReference); + log:printInfo("REACHED THE POINT OF DEPLOYING ROUTES"); + check self.deployRoutes(apiArtifact.productionHttpRoutes, apiArtifact.productionGqlRoutes, apiArtifact.productionGrpcRoutes, apiArtifact?.namespace, ownerReference); + check self.deployRoutes(apiArtifact.sandboxHttpRoutes, apiArtifact.sandboxGqlRoutes, apiArtifact.sandboxGrpcRoutes, apiArtifact?.namespace, ownerReference); return deployK8sAPICrResult; } on fail var e { @@ -138,12 +142,15 @@ public class DeployerClient { return e909028(); } } else { + log:printInfo("NOT AN API??"); return e909028(); } } on fail var e { if e is commons:APKError { return e; } + log:printInfo("DEPLOYMENT FAAAAILED"); + log:printError("Internal Error occured while deploying API", e); return e909028(); } @@ -197,6 +204,30 @@ public class DeployerClient { } } + private isolated function deleteGrpcRoutes(model:API api, string organization) returns commons:APKError? { + do { + model:GRPCRouteList|http:ClientError grpcRouteListResponse = check getGrpcRoutesForAPIs(api.spec.apiName, api.spec.apiVersion, api.metadata?.namespace, organization); + if grpcRouteListResponse is model:GRPCRouteList { + foreach model:GRPCRoute item in grpcRouteListResponse.items { + http:Response|http:ClientError grpcRouteDeletionResponse = deleteGrpcRoute(item.metadata.name, api.metadata?.namespace); + if grpcRouteDeletionResponse is http:Response { + if grpcRouteDeletionResponse.statusCode != http:STATUS_OK { + json responsePayLoad = check grpcRouteDeletionResponse.getJsonPayload(); + model:Status statusResponse = check responsePayLoad.cloneWithType(model:Status); + check self.handleK8sTimeout(statusResponse); + } + } else { + log:printError("Error occured while deleting GrpcRoute", grpcRouteDeletionResponse); + } + } + return; + } + } on fail var e { + log:printError("Error occured deleting grpcRoutes", e); + return e909022("Error occured deleting grpcRoutes", e); + } + } + private isolated function deleteBackends(model:API api, string organization) returns commons:APKError? { do { model:BackendList|http:ClientError backendPolicyListResponse = check getBackendPolicyCRsForAPI(api.spec.apiName, api.spec.apiVersion, api.metadata?.namespace, organization); @@ -300,6 +331,7 @@ public class DeployerClient { if k8sAPIByNameAndNamespace is model:API { k8sAPI.metadata.resourceVersion = k8sAPIByNameAndNamespace.metadata.resourceVersion; http:Response deployAPICRResult = check updateAPICR(k8sAPI, apiArtifact?.namespace); + log:printInfo(deployAPICRResult.statusCode.toString()); if deployAPICRResult.statusCode == http:STATUS_OK { json responsePayLoad = check deployAPICRResult.getJsonPayload(); log:printDebug("Updated K8sAPI Successfully" + responsePayLoad.toJsonString()); @@ -312,8 +344,10 @@ public class DeployerClient { model:StatusCause[] 'causes = details.'causes; foreach model:StatusCause 'cause in 'causes { if 'cause.'field == "spec.basePath" { + log:printError("Error occured while updating K8sAPI due to base path ", e909015(k8sAPI.spec.basePath)); return e909015(k8sAPI.spec.basePath); } else if 'cause.'field == "spec.apiName" { + log:printError("Error occured while updating K8sAPI due to base path ", e909015(k8sAPI.spec.basePath)); return e909016(k8sAPI.spec.apiName); } } @@ -349,7 +383,8 @@ public class DeployerClient { return e909022("Internal error occured", e = error("Internal error occured")); } } - private isolated function deployRoutes(model:HTTPRoute[]? httproutes, model:GQLRoute[]? gqlroutes, string namespace, model:OwnerReference ownerReference) returns error? { + private isolated function deployRoutes(model:HTTPRoute[]? httproutes, model:GQLRoute[]? gqlroutes, model:GRPCRoute[]? grpcroutes, + string namespace, model:OwnerReference ownerReference) returns error? { if httproutes is model:HTTPRoute[] && httproutes.length() > 0 { model:HTTPRoute[] deployReadyHttproutes = httproutes; model:HTTPRoute[]|commons:APKError orderedHttproutes = self.createHttpRoutesOrder(httproutes); @@ -408,6 +443,51 @@ public class DeployerClient { } } } + } else if grpcroutes is model:GRPCRoute[] && grpcroutes.length() > 0 { + model:GRPCRoute[] deployReadyGrpcRoutes = grpcroutes; + model:GRPCRoute[]|commons:APKError orderedGrpcRoutes = self.createGrpcRoutesOrder(grpcroutes); + if orderedGrpcRoutes is model:GRPCRoute[] { + deployReadyGrpcRoutes = orderedGrpcRoutes; + } + foreach model:GRPCRoute grpcRoute in deployReadyGrpcRoutes { + grpcRoute.metadata.ownerReferences = [ownerReference]; + if grpcRoute.spec.rules.length() > 0 { + http:Response deployGrpcRouteResult = check deployGrpcRoute(grpcRoute, namespace); + if deployGrpcRouteResult.statusCode == http:STATUS_CREATED { + log:printDebug("Deployed GrpcRoute Successfully" + grpcRoute.toString()); + } else if deployGrpcRouteResult.statusCode == http:STATUS_CONFLICT { + log:printDebug("GrpcRoute already exists" + grpcRoute.toString()); + model:GRPCRoute grpcRouteFromK8s = check getGrpcRoute(grpcRoute.metadata.name, namespace); + grpcRoute.metadata.resourceVersion = grpcRouteFromK8s.metadata.resourceVersion; + http:Response grpcRouteCR = check updateGrpcRoute(grpcRoute, namespace); + if grpcRouteCR.statusCode != http:STATUS_OK { + json responsePayLoad = check grpcRouteCR.getJsonPayload(); + model:Status statusResponse = check responsePayLoad.cloneWithType(model:Status); + check self.handleK8sTimeout(statusResponse); + } + } else { + json responsePayLoad = check deployGrpcRouteResult.getJsonPayload(); + model:Status statusResponse = check responsePayLoad.cloneWithType(model:Status); + check self.handleK8sTimeout(statusResponse); + } + } + } + } + } + + public isolated function createGrpcRoutesOrder(model:GRPCRoute[] grpcRoutes) returns model:GRPCRoute[]|commons:APKError { + do { + foreach model:GRPCRoute route in grpcRoutes { + model:GRPCRouteRule[] routeRules = route.spec.rules; + model:GRPCRouteRule[] sortedRouteRules = from var routeRule in routeRules + order by (routeRule.matches[0].method.'service) descending + select routeRule; + route.spec.rules = sortedRouteRules; + } + return grpcRoutes; + } on fail var e { + log:printError("Error occured while sorting grpcRoutes", e); + return e909022("Error occured while sorting grpcRoutes", e); } } diff --git a/runtime/config-deployer-service/ballerina/K8sClient.bal b/runtime/config-deployer-service/ballerina/K8sClient.bal index 448e8f0ebe..cf71e4b607 100644 --- a/runtime/config-deployer-service/ballerina/K8sClient.bal +++ b/runtime/config-deployer-service/ballerina/K8sClient.bal @@ -1,3 +1,7 @@ +import config_deployer_service.model as model; + +import ballerina/crypto; +import ballerina/http; // // Copyright (c) 2022, WSO2 LLC. (http://www.wso2.com). // @@ -15,13 +19,11 @@ // specific language governing permissions and limitations // under the License. // - import ballerina/io; +import ballerina/log; import ballerina/url; -import ballerina/http; + import wso2/apk_common_lib as commons; -import ballerina/crypto; -import config_deployer_service.model as model; const string K8S_API_ENDPOINT = "/api/v1"; final string token = check io:fileReadString(k8sConfiguration.serviceAccountPath + "/token"); @@ -56,7 +58,7 @@ isolated function getConfigMapValueFromNameAndNamespace(string name, string name } isolated function deleteAPICR(string name, string namespace) returns http:Response|http:ClientError { - string endpoint = "/apis/dp.wso2.com/v1alpha2/namespaces/" + namespace + "/apis/" + name; + string endpoint = "/apis/dp.wso2.com/v1beta1/namespaces/" + namespace + "/apis/" + name; return k8sApiServerEp->delete(endpoint, targetType = http:Response); } @@ -111,12 +113,13 @@ isolated function deleteConfigMap(string name, string namespace) returns http:Re } isolated function deployAPICR(model:API api, string namespace) returns http:Response|http:ClientError { - string endpoint = "/apis/dp.wso2.com/v1alpha2/namespaces/" + namespace + "/apis"; + string endpoint = "/apis/dp.wso2.com/v1beta1/namespaces/" + namespace + "/apis"; return k8sApiServerEp->post(endpoint, api, targetType = http:Response); } isolated function updateAPICR(model:API api, string namespace) returns http:Response|http:ClientError { - string endpoint = "/apis/dp.wso2.com/v1alpha2/namespaces/" + namespace + "/apis/" + api.metadata.name; + string endpoint = "/apis/dp.wso2.com/v1beta1/namespaces/" + namespace + "/apis/" + api.metadata.name; + log:printInfo("UPDATING API CR"); return k8sApiServerEp->put(endpoint, api, targetType = http:Response); } @@ -151,7 +154,7 @@ isolated function updateGqlRoute(model:GQLRoute gqlroute, string namespace) retu } public isolated function getK8sAPIByNameAndNamespace(string name, string namespace) returns model:API?|commons:APKError { - string endpoint = "/apis/dp.wso2.com/v1alpha2/namespaces/" + namespace + "/apis/" + name; + string endpoint = "/apis/dp.wso2.com/v1beta1/namespaces/" + namespace + "/apis/" + name; do { http:Response response = check k8sApiServerEp->get(endpoint); if response.statusCode == 200 { @@ -344,3 +347,28 @@ isolated function getBackendJWTCrsForAPI(string apiName, string apiVersion, stri string endpoint = "/apis/dp.wso2.com/v1alpha1/namespaces/" + namespace + "/backendjwts?labelSelector=" + check generateUrlEncodedLabelSelector(apiName, apiVersion, organization); return k8sApiServerEp->get(endpoint, targetType = model:BackendJWTList); } + +isolated function getGrpcRoute(string name, string namespace) returns model:GRPCRoute|http:ClientError { + string endpoint = "/apis/gateway.networking.k8s.io/v1alpha2/namespaces/" + namespace + "/grpcroutes/" + name; + return k8sApiServerEp->get(endpoint, targetType = model:GRPCRoute); +} + +isolated function deleteGrpcRoute(string name, string namespace) returns http:Response|http:ClientError { + string endpoint = "/apis/gateway.networking.k8s.io/v1alpha2/namespaces/" + namespace + "/grpcroutes/" + name; + return k8sApiServerEp->delete(endpoint, targetType = http:Response); +} + +isolated function deployGrpcRoute(model:GRPCRoute grpcRoute, string namespace) returns http:Response|http:ClientError { + string endpoint = "/apis/gateway.networking.k8s.io/v1alpha2/namespaces/" + namespace + "/grpcroutes"; + return k8sApiServerEp->post(endpoint, grpcRoute, targetType = http:Response); +} + +isolated function updateGrpcRoute(model:GRPCRoute grpcRoute, string namespace) returns http:Response|http:ClientError { + string endpoint = "/apis/gateway.networking.k8s.io/v1alpha2/namespaces/" + namespace + "/grpcroutes/" + grpcRoute.metadata.name; + return k8sApiServerEp->put(endpoint, grpcRoute, targetType = http:Response); +} + +public isolated function getGrpcRoutesForAPIs(string apiName, string apiVersion, string namespace, string organization) returns model:GRPCRouteList|http:ClientError|error { + string endpoint = "/apis/gateway.networking.k8s.io/v1alpha2/namespaces/" + namespace + "/grpcroutes/?labelSelector=" + check generateUrlEncodedLabelSelector(apiName, apiVersion, organization); + return k8sApiServerEp->get(endpoint, targetType = model:GRPCRouteList); +} diff --git a/runtime/config-deployer-service/ballerina/constants.bal b/runtime/config-deployer-service/ballerina/constants.bal index 66f523456b..85246b3a19 100644 --- a/runtime/config-deployer-service/ballerina/constants.bal +++ b/runtime/config-deployer-service/ballerina/constants.bal @@ -11,6 +11,8 @@ public final string[] WS_SUPPORTED_METHODS = ["subscribe", "publish"]; const string API_TYPE_REST = "REST"; const string API_TYPE_GRAPHQL = "GRAPHQL"; +const string API_TYPE_GRPC = "GRPC"; +const string API_TYPE_ASYNC = "ASYNC"; const string API_TYPE_SOAP = "SOAP"; const string API_TYPE_SSE = "SSE"; const string API_TYPE_WS = "WS"; @@ -52,7 +54,7 @@ const string ENDPOINT_SECURITY_PASSWORD = "password"; const string ZIP_FILE_EXTENSTION = ".zip"; const string PROTOCOL_HTTP = "http"; const string PROTOCOL_HTTPS = "https"; -final string[] & readonly ALLOWED_API_TYPES = [API_TYPE_REST, API_TYPE_GRAPHQL]; +final string[] & readonly ALLOWED_API_TYPES = [API_TYPE_REST, API_TYPE_GRAPHQL, API_TYPE_GRPC]; const string MEDIATION_POLICY_TYPE_REQUEST_HEADER_MODIFIER = "RequestHeaderModifier"; const string MEDIATION_POLICY_TYPE_RESPONSE_HEADER_MODIFIER = "ResponseHeaderModifier"; diff --git a/runtime/config-deployer-service/ballerina/modules/model/API.bal b/runtime/config-deployer-service/ballerina/modules/model/API.bal index 25d7bd40c0..d7f6008b82 100644 --- a/runtime/config-deployer-service/ballerina/modules/model/API.bal +++ b/runtime/config-deployer-service/ballerina/modules/model/API.bal @@ -18,7 +18,7 @@ public type API record { string kind = "API"; - string apiVersion = "dp.wso2.com/v1alpha2"; + string apiVersion = "dp.wso2.com/v1beta1"; Metadata metadata; APISpec spec; APIStatus? status = (); @@ -62,7 +62,7 @@ public type EnvConfig record { }; public type APIList record { - string apiVersion = "dp.wso2.com/v1alpha2"; + string apiVersion = "dp.wso2.com/v1beta1"; string kind = "APIList"; API[] items; ListMeta metadata; diff --git a/runtime/config-deployer-service/ballerina/modules/model/APIArtifact.bal b/runtime/config-deployer-service/ballerina/modules/model/APIArtifact.bal index 7f272176aa..b40f954487 100644 --- a/runtime/config-deployer-service/ballerina/modules/model/APIArtifact.bal +++ b/runtime/config-deployer-service/ballerina/modules/model/APIArtifact.bal @@ -6,6 +6,8 @@ public type APIArtifact record {| HTTPRoute[] sandboxHttpRoutes = []; GQLRoute[] productionGqlRoutes = []; GQLRoute[] sandboxGqlRoutes = []; + GRPCRoute[] productionGrpcRoutes = []; + GRPCRoute[] sandboxGrpcRoutes = []; ConfigMap definition?; map endpointCertificates = {}; map certificateMap = {}; diff --git a/runtime/config-deployer-service/ballerina/modules/model/GRPCRoute.bal b/runtime/config-deployer-service/ballerina/modules/model/GRPCRoute.bal new file mode 100644 index 0000000000..3c28ef72b5 --- /dev/null +++ b/runtime/config-deployer-service/ballerina/modules/model/GRPCRoute.bal @@ -0,0 +1,70 @@ +// +// Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). +// +// WSO2 LLC. licenses this file to you under the Apache License, +// Version 2.0 (the "License"); you may not use this file except +// in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// + +public type GRPCRouteSpec record { + *CommonRouteSpec; + string[] hostnames?; + GRPCRouteRule[] rules = []; +}; + +public type GRPCRouteRule record { + GRPCRouteMatch[] matches; + GRPCRouteFilter[] filters?; + GRPCBackendRef[] backendRefs?; +}; + +public type GRPCRouteMatch record { + GRPCMethodMatch method; + GRPCHeaderMatch[] headers?; +}; + +public type GRPCHeaderMatch record { + string 'type; + string name; + string value; +}; + +public type GRPCMethodMatch record { + string 'type; + string 'service; + string method; +}; + +public type GRPCRouteFilter record { + string 'type; + LocalObjectReference extensionRef?; +}; + +public type GRPCBackendRef record { + *BackendRef; + GRPCRouteFilter[] filters?; +}; + +public type GRPCRoute record {| + string apiVersion = "gateway.networking.k8s.io/v1alpha2"; + string kind = "GRPCRoute"; + Metadata metadata; + GRPCRouteSpec spec; +|}; + +public type GRPCRouteList record {| + string apiVersion = "gateway.networking.k8s.io/v1alpha2"; + string kind = "GRPCRouteList"; + ListMeta metadata; + GRPCRoute[] items; +|}; diff --git a/runtime/config-deployer-service/ballerina/modules/org.wso2.apk.config/RuntimeAPICommonUtil.bal b/runtime/config-deployer-service/ballerina/modules/org.wso2.apk.config/RuntimeAPICommonUtil.bal index 6bf917f909..1e5011dc5a 100644 --- a/runtime/config-deployer-service/ballerina/modules/org.wso2.apk.config/RuntimeAPICommonUtil.bal +++ b/runtime/config-deployer-service/ballerina/modules/org.wso2.apk.config/RuntimeAPICommonUtil.bal @@ -1,9 +1,10 @@ -import ballerina/jballerina.java; -import ballerina/jballerina.java.arrays as jarrays; import config_deployer_service.java.lang as javalang; import config_deployer_service.java.util as javautil; -import config_deployer_service.org.wso2.apk.config.model as orgwso2apkconfigmodel; import config_deployer_service.org.wso2.apk.config.api as orgwso2apkconfigapi; +import config_deployer_service.org.wso2.apk.config.model as orgwso2apkconfigmodel; + +import ballerina/jballerina.java; +import ballerina/jballerina.java.arrays as jarrays; # Ballerina class mapping for the Java `org.wso2.apk.config.RuntimeAPICommonUtil` class. @java:Binding {'class: "org.wso2.apk.config.RuntimeAPICommonUtil"} diff --git a/runtime/config-deployer-service/java/build.gradle b/runtime/config-deployer-service/java/build.gradle index a7f8687853..c5d212cd95 100644 --- a/runtime/config-deployer-service/java/build.gradle +++ b/runtime/config-deployer-service/java/build.gradle @@ -33,6 +33,7 @@ dependencies { implementation libs.json implementation libs.commons.lang implementation libs.prometheus + implementation libs.protobuf.java } tasks.register('copy_dependencies', Copy) { diff --git a/runtime/config-deployer-service/java/src/main/java/org/wso2/apk/config/APIConstants.java b/runtime/config-deployer-service/java/src/main/java/org/wso2/apk/config/APIConstants.java index ed4a91e2a5..dac89a0089 100644 --- a/runtime/config-deployer-service/java/src/main/java/org/wso2/apk/config/APIConstants.java +++ b/runtime/config-deployer-service/java/src/main/java/org/wso2/apk/config/APIConstants.java @@ -81,16 +81,17 @@ public final class APIConstants { public static final String SQS_TRANSPORT_PROTOCOL_NAME = "sqs"; public static final String STOMP_TRANSPORT_PROTOCOL_NAME = "stomp"; public static final String REDIS_TRANSPORT_PROTOCOL_NAME = "redis"; - + // GraphQL related constants public static final Set GRAPHQL_SUPPORTED_METHOD_LIST = Collections.unmodifiableSet(new HashSet( Arrays.asList(new String[] { "QUERY", "MUTATION", "SUBSCRIPTION", "head", "options" }))); public static final String GRAPHQL_MUTATION = "MUTATION"; public static final String GRAPHQL_SUBSCRIPTION = "SUBSCRIPTION"; public static final String GRAPHQL_QUERY = "QUERY"; + public static final String GRPC_API = "GRPC"; public enum ParserType { - REST, ASYNC, GRAPHQL + REST, ASYNC, GRAPHQL, GRPC } public static class OperationParameter { diff --git a/runtime/config-deployer-service/java/src/main/java/org/wso2/apk/config/DefinitionParserFactory.java b/runtime/config-deployer-service/java/src/main/java/org/wso2/apk/config/DefinitionParserFactory.java index 8cfb710849..3041eaa22e 100644 --- a/runtime/config-deployer-service/java/src/main/java/org/wso2/apk/config/DefinitionParserFactory.java +++ b/runtime/config-deployer-service/java/src/main/java/org/wso2/apk/config/DefinitionParserFactory.java @@ -3,6 +3,7 @@ import org.wso2.apk.config.api.APIDefinition; import org.wso2.apk.config.definitions.AsyncApiParser; import org.wso2.apk.config.definitions.OAS3Parser; +import org.wso2.apk.config.definitions.ProtoParser; import org.wso2.apk.config.model.API; import java.util.ArrayList; @@ -17,12 +18,15 @@ private DefinitionParserFactory() { static { parsers.add(new AsyncApiParser()); parsers.add(new OAS3Parser()); + parsers.add(new ProtoParser()); } public static APIDefinition getParser(API api) { if (APIConstants.ParserType.REST.name().equals(api.getType()) || APIConstants.ParserType.GRAPHQL.name().equals(api.getType())) { return new OAS3Parser(); + } else if (APIConstants.ParserType.GRPC.name().equals(api.getType())) { + return new ProtoParser(); } else if (APIConstants.ParserType.ASYNC.name().equals(api.getType())) { return new AsyncApiParser(); } @@ -33,6 +37,8 @@ public static APIDefinition getParser(String apiType) { if (APIConstants.ParserType.REST.name().equals(apiType) || APIConstants.ParserType.GRAPHQL.name().equals(apiType)) { return new OAS3Parser(); + } else if (APIConstants.ParserType.GRPC.name().equals(apiType)) { + return new ProtoParser(); } else if ("ASYNC".equals(apiType)) { return new AsyncApiParser(); } diff --git a/runtime/config-deployer-service/java/src/main/java/org/wso2/apk/config/RuntimeAPICommonUtil.java b/runtime/config-deployer-service/java/src/main/java/org/wso2/apk/config/RuntimeAPICommonUtil.java index 03f0433593..68c8b2014d 100644 --- a/runtime/config-deployer-service/java/src/main/java/org/wso2/apk/config/RuntimeAPICommonUtil.java +++ b/runtime/config-deployer-service/java/src/main/java/org/wso2/apk/config/RuntimeAPICommonUtil.java @@ -6,6 +6,8 @@ import org.wso2.apk.config.api.ExceptionCodes; import org.wso2.apk.config.definitions.GraphQLSchemaDefinition; import org.wso2.apk.config.definitions.OASParserUtil; +import org.wso2.apk.config.definitions.ProtoParser; +import org.wso2.apk.config.definitions.ProtoParserUtil; import org.wso2.apk.config.model.API; import org.wso2.apk.config.model.URITemplate; @@ -20,31 +22,28 @@ public class RuntimeAPICommonUtil { public static String generateDefinition(API api) throws APIManagementException { - APIDefinition parser = DefinitionParserFactory.getParser(api); return parser.generateAPIDefinition(api); } /** - * @param inputByteArray OpenAPI definition file - * @param apiDefinition OpenAPI definition + * @param inputByteArray API definition file + * @param apiDefinition API definition * @param fileName Filename of the definition file * @param returnContent Whether to return json or not * @return APIDefinitionValidationResponse * @throws APIManagementException when file parsing fails */ public static APIDefinitionValidationResponse validateOpenAPIDefinition(String type, byte[] inputByteArray, - String apiDefinition, String fileName, - boolean returnContent) - throws APIManagementException { + String apiDefinition, String fileName, boolean returnContent) throws APIManagementException { APIDefinitionValidationResponse validationResponse = new APIDefinitionValidationResponse(); if (APIConstants.ParserType.REST.name().equals(type)) { if (inputByteArray != null && inputByteArray.length > 0) { if (fileName != null) { if (fileName.endsWith(".zip")) { - validationResponse = - OASParserUtil.extractAndValidateOpenAPIArchive(inputByteArray, returnContent); + validationResponse = OASParserUtil.extractAndValidateOpenAPIArchive(inputByteArray, + returnContent); } else { String openAPIContent = new String(inputByteArray, StandardCharsets.UTF_8); validationResponse = OASParserUtil.validateAPIDefinition(openAPIContent, returnContent); @@ -66,10 +65,50 @@ public static APIDefinitionValidationResponse validateOpenAPIDefinition(String t OASParserUtil.addErrorToValidationResponse(validationResponse, "Invalid definition file type provided."); } + } else if (APIConstants.ParserType.GRPC.name().equals(type.toUpperCase())) { + if (fileName.endsWith(".proto")) { + validationResponse = ProtoParserUtil.validateGRPCAPIDefinition( + new String(inputByteArray, StandardCharsets.UTF_8), + returnContent); + } else { + ProtoParserUtil.addErrorToValidationResponse(validationResponse, + "Invalid definition file type provided."); + } } return validationResponse; } + public static API getGRPCAPIFromProtoDefinition(String definition) { + System.out.println("GETTING API FROM PROTO"); + ProtoParser protoParser = new ProtoParser(); + protoParser.setContent(definition); + List uriTemplates = new ArrayList<>(); + API api = new API(); + api.setBasePath("/" + protoParser.protoFile.basePath); + api.setVersion(protoParser.protoFile.version); + StringBuilder apiName = new StringBuilder(); + List sortedServices = new ArrayList<>(); + + for (ProtoParser.Service service : protoParser.getServices()) { + sortedServices.add(service.name); + for (String method : service.methods) { + URITemplate uriTemplate = new URITemplate(); + uriTemplate.setUriTemplate(protoParser.protoFile.packageName + "." + service.name); + uriTemplate.setVerb(method); + uriTemplates.add(uriTemplate); + } + } + sortedServices.sort(String::compareTo); + for (String service : sortedServices) { + apiName.append(service).append("-"); + } + apiName.deleteCharAt(apiName.length() - 1); + api.setName(apiName.toString()); + api.setUriTemplates(uriTemplates.toArray(new URITemplate[uriTemplates.size()])); + + return api; + } + public static Set generateUriTemplatesFromAPIDefinition(String apiType, String content) throws APIManagementException { @@ -99,6 +138,8 @@ public static API getAPIFromDefinition(String definition, String apiType) throws if (apiType.toUpperCase().equals(APIConstants.GRAPHQL_API)) { return getGQLAPIFromDefinition(definition); + } else if (apiType.toUpperCase().equals(APIConstants.GRPC_API)) { + return getGRPCAPIFromProtoDefinition(definition); } else { APIDefinition parser = DefinitionParserFactory.getParser(apiType); if (parser != null) { diff --git a/runtime/config-deployer-service/java/src/main/java/org/wso2/apk/config/api/ExceptionCodes.java b/runtime/config-deployer-service/java/src/main/java/org/wso2/apk/config/api/ExceptionCodes.java index f24a3a2fe4..f097316cd2 100644 --- a/runtime/config-deployer-service/java/src/main/java/org/wso2/apk/config/api/ExceptionCodes.java +++ b/runtime/config-deployer-service/java/src/main/java/org/wso2/apk/config/api/ExceptionCodes.java @@ -21,820 +21,967 @@ import java.util.Arrays; /** - * This enum class holds error codes that we need to pass to upper level. For example, to the UI. + * This enum class holds error codes that we need to pass to upper level. For + * example, to the UI. * You have to define your custom error codes here. */ public enum ExceptionCodes implements ErrorHandler { - // API, Application related codes - API_NAME_ALREADY_EXISTS(900250, "The API name already exists.", 409, "An API with name '%s' already exists"), - API_CONTEXT_ALREADY_EXISTS(900251, "The API context already exists.", 409, "An API with context '%s' already exists"), - API_VERSION_ALREADY_EXISTS(900252, "The API version already exists.", 409, "An API with version '%s' already exists for API '%s'"), - API_EMPTY_PASSWORD_FOR_SECURED_ENDPOINT(900253, "Empty password is given for endpointSecurity when creating API: %s", - 400, "An empty password is given for endpointSecurity when creating API: %s"), - - API_PRODUCT_CONTEXT_ALREADY_EXISTS(900275, "The API Product context already exists.", 409, "An API Product with context '%s' already exists"), - - API_ALREADY_EXISTS(900300, "The API already exists.", 409, "The API already exists"), - APPLICATION_ALREADY_EXISTS(900301, "The application already exists.", 409, "The application already exists"), - APIMGT_DAO_EXCEPTION(900302, "Internal server error.", 500, "Error occurred while persisting/retrieving data"), - APIMGT_LIFECYCLE_EXCEPTION(900303, "Lifecycle exception occurred", 500, "Error occurred while changing " + - "lifecycle state"), - TIER_CANNOT_BE_NULL(900304, "The tier cannot be null.", 400, "The tier cannot be null"), - TIER_NAME_INVALID(900305, "The tier name is invalid.", 400, "The tier name is invalid"), - SEARCH_API_EXCEPTION(900306, "Internal server error.", 500, "Error occurred while searching APIs"), - APPLICATION_NOT_FOUND(900307, "Application not found", 404, "Application not found"), - API_NOT_FOUND(900308, "API Not Found", 404, "Requested API with id '%s' not found"), - APPLICATION_DELETE_FAILED(900309, "Error has occurred. Could not delete the application", 500, "Error has occurred. Could not delete the application '%s'"), - SUBSCRIPTION_NOT_FOUND(900310, "Subscription not found", 404, "Couldn't retrieve Subscriptions for API"), - UPDATE_STATE_CHANGE(900311, "API fields have state changes", 400, "Couldn't Update as API have changes can't be done"), - DOCUMENT_ALREADY_EXISTS(900312, "Document already exists", 409, "Document already exists"), - COULD_NOT_UPDATE_API(900313, "Error has occurred. Could not update the API", 500, "Error has occurred. Could not " - + "update the API"), - DOCUMENT_CONTENT_NOT_FOUND(900314, "Document content not found", 404, "Document content not found"), - DOCUMENT_NOT_FOUND(900315, "Document not found", 404, "Document not found"), - DOCUMENT_INVALID_SOURCE_TYPE(900319, "Invalid document source type", 500, "Source type of the document '%s' is invalid"), - UNSUPPORTED_DOC_EXTENSION(900367, "Document file type not supported", 400, "Unsupported extension type of document file"), - - API_EXPORT_ERROR(900316, "API export Error", 500, "Error while exporting the given APIs"), - API_IMPORT_ERROR(900317, "API import Error", 500, "Error while importing the given APIs"), - API_PRODUCT_IMPORT_ERROR(900318, "API product import Error", 500, - "Error while importing the given API Products"), - SUBSCRIPTION_STATE_INVALID(900320, "Invalid state change for subscription", 400, "Invalid state change for " + - "subscription"), - API_RETRIEVE_EXCEPTION(900319, "Internal server error.", 500, "Error occurred while retrieving %s"), - APPLICATION_RETRIEVE_EXCEPTION(900320, "Internal server error.", 500, "Error occurred while retrieving %s"), - SUBSCRIPTION_RETRIEVE_EXCEPTION(900321, "Internal server error.", 500, "Error occurred while retrieving %s"), - - GATEWAY_LABELS_CANNOT_BE_NULL(900322, "Gateway labels cannot be null.", 400, "Gateway labels cannot be null"), - ERROR_RETRIEVING_RATING(900323, "Cannot retrieve rating", 500, "Error while retrieving ratings for API '%s'"), - RATING_NOT_FOUND(900324, "Rating not found", 404, "Couldn't retrieve rating for API '%s'"), - RATING_VALUE_INVALID(900325, "Rating value invalid", 400, "Provided rating value does not fall in between min max " - + "values"), - DOCUMENT_INVALID_VISIBILITY(900326, "Invalid document visibility type", 500, "Visibility type of the document '%s' is invalid"), - API_TYPE_INVALID(900327, "API Type specified is invalid.", 400, "API Type specified is invalid"), - COMPOSITE_API_ALREADY_EXISTS(900328, "A Composite API already exists.", 409, - "A Composite API already exists for this application"), - API_DEFINITION_NOT_FOUND(900330, "API definition not found", 404, "API definition not found"), - APPLICATION_KEY_MAPPING_NOT_FOUND(900331, "Application Key mapping not found", 404, "Application Key mapping not " + - "found"), - NO_UPDATE_PERMISSIONS(900332, "No permissions to update API.", 403, "No permissions to update API."), - NO_DELETE_PERMISSIONS(900333, "No permissions to delete API.", 403, "No permissions to delete API."), - API_ATTRIBUTE_NOT_FOUND(900335, "API attribute not found", 404, "API attribute not found"), - SUBSCRIPTION_ALREADY_EXISTS(900336, "Subscription already exists", 409, "Subscription already exists"), - SDK_NOT_GENERATED(900337, "Error while generating SDK", 500, "Error while generating SDK"), - APPLICATION_EXPORT_ERROR(900338, "Application Export Error", 500, "Error while exporting the given Application"), - APPLICATION_IMPORT_ERROR(900339, "Application Import Error", 500, "Error while importing the given Application"), - NO_READ_PERMISSIONS(900340, "No permissions to read API.", 403, "No permissions to read API."), - API_PRODUCT_DUPLICATE_RESOURCE(900341, "Cannot create API Product with duplicate resource", - 400, "Cannot create API Product with duplicate resource: %s , verb: %s combination"), - API_PRODUCT_RESOURCE_ENDPOINT_UNDEFINED(900342, - "Cannot create API Product, due to resources with undefined endpoints in their parent APIs", - 409, "Cannot create API Product %s, due to resources with undefined endpoints in their parent APIs %s"), - API_PRODUCT_WITH_UNSUPPORTED_LIFECYCLE_API(900343, - "Cannot create API Product, due to resources parent API being in an unsupported Life Cycle state", - 409, "Cannot create API Product, due to resources parent API being in an unsupported Life Cycle state: %s"), - API_PRODUCT_USED_RESOURCES(900344, - "Cannot remove the resource paths because they are used by one or more API Products", - 409, "Cannot update API: %s:%s, due to the resources to remove are used by one or more API Products"), - API_DELETE_API_PRODUCT_USED_RESOURCES(900344, API_PRODUCT_USED_RESOURCES.getErrorMessage(), 409, - "Cannot delete API since the resources to remove are used by one or more API Products"), - API_CATEGORY_INVALID( - 900345, "The API category is invalid.", 400, " The API category is invalid for API: %s:%s"), - INVALID_ADDITIONAL_PROPERTIES(900346, "Invalid additional properties", 400, - "Invalid additional properties for API: %s:%s"), - INVALID_CONTEXT(900346, "Invalid context provided", 400, "Invalid context provided for API: %s:%s"), - INVALID_ENDPOINT_URL(900346, "Endpoint URL(s) is(are) not valid", 400, "Endpoint URL(s) is(are) not valid"), - USER_ROLES_CANNOT_BE_NULL(900610, "User roles cannot be found", 400, "User roles cannot be found"), - - ERROR_RETRIEVING_REVISION_FOR_UUID(900410, "Failed to get revision details for revision UUID: %s", - 500, "Failed to get revision details"), - - ERROR_RETRIEVING_REVISION_DEPLOYMENT_MAPPING(900411, - "Failed to get API Revision deployment mapping details for %s: %s", 500, - "Failed to get API Revision deployment mapping details"), - - ERROR_UPDATING_REVISION_DEPLOYMENT_MAPPING(900412, - "Failed to update Deployment Mapping entry for API UUID: ", 500, - "Failed to update Deployment Mapping entry"), - - METHOD_NOT_ALLOWED(900420, "Not allowed", 405, "Method not allowed"), - API_REVISION_NOT_FOUND(900347, "API Revision Not Found", 404, "Requested API Revision with id %s not found"), - EXISTING_API_REVISION_DEPLOYMENT_FOUND(900348, "Can not delete API Revision ", 400, "Couldn't delete API revision since API revision is currently deployed to a gateway. " + - "You need to undeploy the API Revision from the gateway before attempting deleting API Revision: %s "), - EXISTING_API_REVISION_FOUND(900349, "Can not create API Revision ", 400, "API revision already exists with id: %s "), - API_REVISION_UUID_NOT_FOUND(900350, "Can not create API Revision ", 400, "Failed to retrieve revision uuid from revision registry artifact"), - MAXIMUM_REVISIONS_REACHED(900351, "Can not create API Revision ", 400, "Maximum number of revisions per API has reached." + - "Need to remove any revision to create a new Revision for API with API UUID: %s"), - ERROR_CREATING_API_REVISION(900352, "Can not create API Revision ", 400, "Failed to create API revision registry artifacts: %s "), - ERROR_DELETING_API_REVISION(900353, "Can not delete API Revision ", 400, "Failed to delete API revision registry artifacts: %s "), - ERROR_RESTORING_API_REVISION(900354, "Can not restore API Revision ", 400, "Failed to restore API revision registry artifacts: %s "), - DEPLOYMENT_ID_NOT_FOUND(900355, "Deployment Id Not Found", 400, "Deployment Id Not Found"), - EXISTING_DEPLOYMENT_NOT_FOUND(900356, "Existing Deployment Not Found", 404, "Deployment with %s not found"), - ORGANIZATION_NOT_FOUND(900357, "Organization Not Found", 400, "Organization is not found in the request"), - INVALID_ENV_API_PROP_CONFIG(900358, "Invalid environment specific api property config", 400, - "Environment specific api property config is not valid. %s", false), - API_OR_API_PRODUCT_NOT_FOUND(900359, "API or API Product Not Found", 404, "Requested API or API Product with id '%s' not found"), - API_PRODUCT_NOT_FOUND(900360, "API Product Not Found", 404, "Requested API Product with id '%s' not found"), - SUB_ORGANIZATION_NOT_IDENTIFIED(900361, "User's Organization Not Identified", 403, "User's Organization is not identified"), - ERROR_RETRIEVING_CATEGORY(900362, "Cannot retrieve categories", 500, "Error while retrieving categories for organization '%s'"), - PERSISTENCE_ERROR(900363, "Error occurred in registry transaction", 500, "'%s'"), - NO_VIEW_UPDATE_PERMISSIONS(900365, "Insufficient permission to view or update the API", 403, "Insufficient permission to view or update the API"), - API_DELETE_FAILED_SUBSCRIPTIONS(900366, "Failed to delete the API", 409, "Cannot remove the API as active subscriptions exist"), - CATEGORY_NAME_CONTAINS_SPECIAL_CHARS(900368, "Name field contains special characters", 400, "API Category name contains special characters"), - CATEGORY_NAME_TOO_LONG(900369, "API Category name is too long", 400, "API Category name exceeds 255 characters"), - CATEGORY_ALREADY_EXISTS(900370, "API Category name already exists", 409, "Category with name '%s' already exists"), - CATEGORY_NOT_FOUND(900371, "Category not found", 404, "No API Category with the given category ID '%s' exists"), - CATEGORY_USED(900372, "Category has usages", 409, "Category is attached to one or more APIs"), - ERROR_CHANGING_APP_OWNER(900373, "Failed to change the application owner", 500, "Error while changing the application owner"), - - USER_SUBSCRIPTION_EXISTS_CHECK_FAILED(900374, "Failed to check if user subscribed to API", 500, "Failed to check if user '%s' with an application '%s' is subscribed to API %s"), - - USER_ACCESSIBLE_APPLICATION_CHECK_FAILED(900375, "Failed to check if user subscribed to API", 500, "Error occurred while checking whether the application '%s' is accessible to user '%s'" ), - API_TAGS_NOT_FOUND(900376, "API Tags Not Found", 404, "API Tags not found for organization '%s'"), - - //Lifecycle related codes - API_UPDATE_FORBIDDEN_PER_LC(900380, "Insufficient permission to update the API", 403, - "Updating the API is restricted as as it is %s."), - UNSUPPORTED_LIFECYCLE_ACTION(900381, "Unsupported state change action", 400, "Lifecycle state change action %s is not allowed"), - LIFECYCLE_STATE_INFORMATION_NOT_FOUND(900382, "Lifecycle state information not found", 500,"Lifecycle state change information for %s with %s cannot be found"), - - // Generic codes - JSON_PARSE_ERROR(900400, "Json parse error", 500, "JSON parse error"), - RESOURCE_NOT_FOUND(900401, "Resource not found", 404, "Requested resource not found"), - RESOURCE_RETRIEVAL_FAILED(900402, "Resource retrieval failed", 400, "Resource retrieval failed"), - USER_MAPPING_RETRIEVAL_FAILED(900404, "User mapping retrieval failed", 404, "User mapping retrieval failed"), - MALFORMED_URL(900403, "Malformed URL", 400, "Invalid or Malformed URL"), - URI_PARSE_ERROR(900405, "Error constructing the URI", 500, "'%s'"), - INVALID_OPERATION_TYPE(900406, "Unsupported '%s' operation", 400, "The '%s' operation type '%s' is invalid"), - VERB_NOT_FOUND(900407, "Missing '%s' type", 400, "Missing '%s type in URI templates"), - YAML_PARSE_ERROR(900408, "Yaml parse error", 500, "Yaml parse error"), - RESOURCE_NOT_FOUND_WITH_TYPE_AND_ID(900409, "Requested %s with Id %s not found", 404, - "Requested %s with Id %s not found"), - AUTHORIZATION_ERROR(900409, "Forbidden", 403, "You don't have permission to access the '%s' with Id '%s'"), - FORBIDDEN_ERROR(900409, "Forbidden", 403, "You don't have permission to access this resource"), - RESOURCE_NOT_FOUND_WITH_DESC(900401, "Resource not found", 404, "Requested '%s' with Id '%s' not found"), - UNAUTHORIZED(900410, "Unauthorized", 401, "User is unauthorized"), - - // Endpoint related codes - ENDPOINT_NOT_FOUND(900450, "Endpoint Not Found", 404, "Endpoint Not Found"), - ENDPOINT_ALREADY_EXISTS(900451, "Endpoint already exists", 409, "Endpoint already exists"), - ENDPOINT_ADD_FAILED(900452, "Endpoint adding failed", 400, "Endpoint adding failed"), - ENDPOINT_DELETE_FAILED(900453, "Endpoint Delete Failed", 400, "Endpoint Delete Failed"), - ENDPOINT_CRYPTO_ERROR(900454, "Error while encrypting/decrypting endpoint secrets", 500, "'%s'"), - ENDPOINT_CONFIG_PARSE_FAILED(900455, "Endpoint config parsing failed", 500, "Error occurred while parsing endpoint config json"), - - - // Service Endpoint Discovery related codes - ERROR_LOADING_SERVICE_DISCOVERY_IMPL_CLASS(900460, "Error loading service discovery impl class", 500, - "Error while trying to load a service discovery impl class"), - ERROR_INITIALIZING_SERVICE_DISCOVERY(900461, "Error initializing service discovery", 500, - "Error while connecting to the system with services"), - ERROR_WHILE_TRYING_TO_DISCOVER_SERVICES(900462, "Error while discovering services", 500, - "Error while trying to discover service endpoints"), - - // Gateway related codes - API_DEFINITION_MALFORMED(900500, "ApiDefinition not found", 400, "Failed to retrieve API Definition"), - TEMPLATE_EXCEPTION(900501, "Service configuration Error", 500, "Error generate service config"), - GATEWAY_EXCEPTION(900502, "Gateway publishing Error", 500, "Error occurred while publishing to Gateway"), - BROKER_EXCEPTION(900503, "Broker Connection Error", 500, "Error occurred while obtaining broker connection"), - INVALID_GATEWAY_ENVIRONMENT(900504, "Invalid Gateway Environment", 400, "Gateway Environment with name '%s' not found"), - NO_GATEWAY_ENVIRONMENTS_ADDED(900505, "No Gateway Environments Available", 400, "No gateway environments " + - "available for the API : %s."), - GATEWAY_ENVIRONMENT_NOT_FOUND(900506, "Gateway Environment not found", 404, - "Gateway Environment with %s not found"), - EXISTING_GATEWAY_ENVIRONMENT_FOUND(900507, "Gateway Environment already exists", 400, - "A Gateway Environment with %s already exists"), - READONLY_GATEWAY_ENVIRONMENT(900508, "Gateway Environment is read only", 400, - "A Gateway Environment with %s is read only"), - GATEWAY_ENVIRONMENT_DUPLICATE_VHOST_FOUND(900509, "Gateway Environment with duplicate virtual hosts", - 400, "A Gateway Environment cannot exists with duplicate virtual hosts"), - READONLY_GATEWAY_ENVIRONMENT_NAME(900510, "Names of Gateway Environment cannot be changed", - 400, "Name of the gateway is read only"), - GATEWAY_ENVIRONMENT_VHOST_NOT_PROVIDED(900511, "Gateway Environment virtual hosts name not provided", - 400, "Gateway Environment VHOST name not provided"), - - FAILED_GET_ENVIRONMENT_LIST_OF_VHOST(900512, "Failed to get gateway environments list of VHost", - 500, "Failed to get gateway environments list of VHost"), - FAILED_GET_ENVIRONMENT_LIST_OF_TENANT_DOMAIN(900513, "Failed to get Environments in tenant domain", - 500, "Failed to get Environments in tenant domain: %s"), - FAILED_GET_ENVIRONMENT_FOR_TENANT_DOMAIN(900514, "Failed to get Environment in tenant domain: %s", - 500, "Failed to get Environment in tenant domain: %s"), - TENANT_NOT_FOUND(900515, "Tenant does not exist", 404, - "Tenant does not exist"), - INVALID_TENANT_STATE(900516, "Invalid tenant state", 400, - "Invalid tenant state provided"), - FAILED_GET_ENVIRONMENT_SPECIFIC_PROPERTIES(900517, - "Error occurred when getting environment specific api properties", 500, - "Error occurred when getting environment specific api properties"), - VHOST_FOR_ENVIRONMENT_NOT_FOUND(900518, "VHosts not found for the environment: %s", 404, - "VHosts not found for the environment: %s"), - - // Workflow related codes - WORKFLOW_EXCEPTION(900550, "Workflow error", 500, - "Error occurred while executing workflow task"), - WORKFLOW_NOT_FOUND(900551, "Workflow error", 404, - "Workflow entry cannot be found for the given reference id"), - WORKFLOW_ALREADY_COMPLETED(900552, "Workflow error", 400, - "Workflow is already completed"), - WORKFLOW_PENDING(900553, "Workflow exception", 409, - "Pending workflow task exists for the seleted API"), - WORKFLOW_INVALID_WFTYPE(900554, "Workflow error", 500, "Invalid workflow type specified"), - WORKFLOW_INV_STORE_WFTYPE(900555, "Workflow error", 500, "Invalid workflow type for store workflows"), - WORKFLOW_STATE_MISSING(900556, "Workflow error", 400, "Workflow status is not defined"), - WORKFLOW_NO_PENDING_TASK(900557, "Workflow error", 412, - "Requested resource does not have a pending workflow task"), - WORKFLOW_REJCECTED(900558, "Workflow error", 403, "Requested action is rejected"), - INCOMPATIBLE_WORKFLOW_REQUEST_FOR_PUBLISHER(900559, "Incompatible workflow request", 400, "Incompatible workflow " + - "request received by publisher"), - INCOMPATIBLE_WORKFLOW_REQUEST_FOR_STORE(900560, "Incompatible workflow request", 400, "Incompatible workflow " + - "request received by store"), - WORKFLOW_RETRIEVE_EXCEPTION(900561, "Workflow retrieval error", 400, "Provided parameter is not valid"), - - // Auth related codes - ROLES_CANNOT_BE_EMPTY(900600, "Role list cannot be empty", 400, "Role list cannot be empty"), - ROLES_CANNOT_BE_NULL(900601, "Role list cannot be null", 400, "Role list cannot be null"), - UNSUPPORTED_ROLE(900602, "Non existing roles cannot be added to an API", 400, - "Non existing roles cannot be added to an API"), - USER_DOES_NOT_EXIST(900603, "User does not exist in the system", 404, "User does not exist in the system"), - USER_CREATION_FAILED(900604, "User creation failed", 500, "User creation failed"), - IDP_INITIALIZATION_FAILED(900605, "Identity Provider initialization failed", 500, - "Identity provider initialization failed"), - KEY_MANAGER_INITIALIZATION_FAILED(900606, "Key Manager initialization failed", 500, - "Key Manager initialization failed",true), - ROLE_DOES_NOT_EXIST(900607, "Role does not exist in the system", 404, "Role does not exist in the system"), - MULTIPLE_ROLES_EXIST(900608, "Multiple roles with the same display name exist in the system", 500, "Multiple " + - "roles with the same display name exist in the system"), - MULTIPLE_USERS_EXIST(900609, "Multiple users with the same username exist in the system", 500, "Multiple " + - "users with the same username exist in the system"), - INVALID_USER_ROLES(900610, "Invalid user roles found", 400, "Invalid user roles found"), - IDP_ADDING_FAILED(900611, "Unable to add the identity provider", 400, "Error while adding the identity provider"), - IDP_RETRIEVAL_FAILED(900612, "Unable to retrieve the identity provider", 400, "Error while retrieving the " - + "identity provider details"), - IDP_DELETION_FAILED(900613, "Unable to delete the identity provider", 400, "Error while deleting the " - + "identity provider"), - INVALID_IDP_TYPE(900614, "Unsupported identity provider type", 400, "Invalid identity provider type. %s"), - USERSTORE_INITIALIZATION_FAILED(900615, "Unable to get the user store manager", 500, - "Error while getting the user store manager from the realm"), - ANON_USER_ACTION(900616, "Operation not permitted", 401, "Attempt to execute privileged operation as the anonymous user"), - ROLE_ID_EMPTY(900617, "Role Id cannot be empty", 400, - "Role Id cannot be empty"), - - // Labels related codes - LABEL_INFORMATION_CANNOT_BE_NULL(900650, "Label information cannot be null", 400, "Label information cannot be " + - "null"), - LABEL_EXCEPTION(900651, "Label Error", 500, "Error occurred while retrieving label information"), - LABEL_NOT_FOUND(900652, "Label Not Found", 404, "Label with specified name cannot be found."), - LABEL_NOT_FOUND_IN_API(900653, "Label Not Found In API", 404, "Label with specified name" - + " cannot be found in the API."), - LABEL_ADDING_FAILED(900654, "Label Error", 500, "Error occurred while trying to add label"), - LABEL_UPDATE_FAILED(900655, "Label Error", 500, "Error occurred while trying to update label"), - LABEL_DELETION_FAILED(900656, "Label Error", 500, "Error occurred while trying to delete label"), - - - //WSDL related codes - INVALID_WSDL_URL_EXCEPTION(900675, "Invalid WSDL", 400, "Invalid WSDL URL"), - CANNOT_PROCESS_WSDL_CONTENT(900676, "Invalid WSDL", 400, "Provided WSDL content cannot be processed"), - INTERNAL_WSDL_EXCEPTION(900677, "Internal WSDL error", 500, "Internal error while processing WSDL"), - UNSUPPORTED_WSDL_EXTENSIBILITY_ELEMENT(900678, "Invalid WSDL", 400, "WSDL extensibility element not supported"), - ERROR_WHILE_INITIALIZING_WSDL_FACTORY(900679, "Internal WSDL error", 500, "Error while initializing WSDL factory"), - ERROR_WHILE_CREATING_WSDL_ARCHIVE(900680, "Internal WSDL error", 500, "Error while creating WSDL archive"), - NO_WSDL_FOUND_IN_WSDL_ARCHIVE(900681, "Invalid WSDL Archive", 400, "No valid WSDLs found in the provided WSDL archive"), - CONTENT_NOT_RECOGNIZED_AS_WSDL(900682, "Invalid WSDL Content", 400, "Provided content is not recognized as a WSDL"), - URL_NOT_RECOGNIZED_AS_WSDL(900683, "Invalid WSDL URL", 400, "Provided URL is not recognized as a WSDL"), - NO_WSDL_AVAILABLE_FOR_API(900684, "WSDL Not Found", 404, "No WSDL Available for the API %s:%s"), - CORRUPTED_STORED_WSDL(900685, "Corrupted Stored WSDL", 500, "The WSDL of the API %s is corrupted."), - UNSUPPORTED_WSDL_FILE_EXTENSION(900686, "Unsupported WSDL File Extension", 400, "Unsupported extension. Only supported extensions are .wsdl and .zip"), - API_NOT_SOAPTOREST(900687, "Provided API is not a SOAP to REST converted API", 400, "Provided API is not a SOAP to REST converted API"), - ERROR_ADDING_WSDL_TO_API(900687, "Error while adding WSDL to API: %s", 500, - "Error while saving WSDL"), - - - //OpenAPI/Swagger related codes [900750 900???) - MALFORMED_OPENAPI_DEFINITON(900758, "Malformed OpenAPI Definition", 400, "The provided OpenAPI definition is not parsable as a valid JSON or YAML."), - UNRECOGNIZED_OPENAPI_DEFINITON(900759, "Unrecognized OpenAPI Definition", 400, "The definition is parsable but cannot be identified as an OpenAPI definition."), - INVALID_SWAGGER_VERSION(900760, "Invalid Swagger Definition", 400, "Unsupported swagger version provided. Please add with swagger version 2.0."), - INVALID_SWAGGER_PARAMS(900751, "Invalid Swagger Definition", 400, "Swagger contains invalid parameters. Please add valid swagger definition."), - INVALID_OPENAPI_VERSION(900752, "Invalid OpenAPI Definition", 400, "Unsupported OpenAPI version provided. Please add with OpenAPI version 3.0.0."), - INVALID_OPENAPI_NO_INFO_PATH(900753, "Invalid OpenAPI Definition", 400, "Required property 'info' or 'paths' are not provided."), - OPENAPI_PARSE_EXCEPTION(900754, "Error while parsing OpenAPI definition", 400, "Error while parsing OpenAPI definition"), - OPENAPI_PARSE_EXCEPTION_WITH_CUSTOM_MESSAGE(OPENAPI_PARSE_EXCEPTION.getErrorCode(), OPENAPI_PARSE_EXCEPTION.getErrorMessage(), OPENAPI_PARSE_EXCEPTION.getHttpStatusCode(), "'%s'"), - OPENAPI_NOT_FOUND(900755, "OpenAPI definition not found", 404, "OpenAPI definition not found"), - OPENAPI_URL_MALFORMED(900756, "OpenAPI definition retrieval from URL failed", 400, "Exception occurred while retrieving the OpenAPI definition from URL"), - OPENAPI_URL_NO_200(900757, "OpenAPI definition retrieval from URL failed", 400, "Response didn't return a 200 OK status"), - INVALID_OAS2_FOUND(900761, "Invalid OpenAPI V2 definition found", 400, "Invalid OpenAPI V2 definition found"), - INVALID_OAS3_FOUND(900762, "Invalid OpenAPI V3 definition found", 400, "Invalid OpenAPI V3 definition found"), - NO_RESOURCES_FOUND(900763, "No resources found", 404, "API must have at least one resource defined"), - ERROR_REMOVING_EXAMPLES(900764, "Internal Error While Processing Swagger Definition", 500, "Couldn't remove one or more examples from the swagger definition"), - MOCK_HTTP_METHOD_MISSING(900765, "Could not find HTTP methods", 400, "Cannot find the HTTP method for the API Resource Mediation Policy"), - SWAGGER_ARCHIVE_MISSING(900766, "Could not find an archive in the given ZIP file", 500, "Could not find an archive in the given ZIP file"), - - //AsyncApi related error codes - ASYNCAPI_URL_MALFORMED(900756, "AsyncAPI specification retrieval from URL failed", 400, "Exception occurred while retrieving the AsyncAPI Specification from URL"), - ASYNCAPI_URL_NO_200(900757, "AsyncAPI specification retrieval from URL failed", 400, "Response didn't return a 200 OK status"), - - ERROR_READING_ASYNCAPI_SPECIFICATION(900765, "AsyncAPI specification read error", 500, "Exception occurred while reading the AsyncAPI Specification file"), - - // REST API related codes - PARAMETER_NOT_PROVIDED(900700, "Parameter value missing", 400, - "Some of the mandatory parameter values were missing"), - LOCATION_HEADER_INCORRECT(900701, "Error while obtaining URI for Location header", 500, - "Error occurred while obtaining URI for Location header"), - LAST_UPDATED_TIME_RETRIEVAL_ERROR(900702, "Error while retrieving last access time for the resource", 500, - "Error while retrieving last access time for the resource"), - INVALID_DATE_TIME_STAMP(900703, "Invalid timestamp value", 400, "Timestamp should be in ISO8601 format"), - LENGTH_EXCEEDS(900704, "Character length exceeds the allowable limit", 400, - "One of the provided input character length exceeds the allowable limit."), - BLANK_PROPERTY_VALUE(900705, "Blank value for required property", 400, - "%s property value of payload cannot be blank"), - CONTAIN_SPECIAL_CHARACTERS(900706, "contain invalid characters", 400, - "%s property value of payload cannot contain invalid characters"), - INVALID_SORT_CRITERIA(900707, "Invalid sort criteria", 400, "Sort criteria contain a non-allowable value"), - INVALID_PARAMETERS_PROVIDED(900708, "Invalid parameter(s) provided", 400, "Bad Request. Mandatory parameters are invalid/missing"), - - INVALID_PARAMETERS_PROVIDED_WITH_MESSAGE(900708, "'%s'", 400, "Bad Request. Parameters are invalid/missing"), - - //GraphQL API related codes - API_NOT_GRAPHQL(900800, "This API is not a GraphQL API", 400, "This API is not a GraphQL API"), - GRAPHQL_SCHEMA_CANNOT_BE_NULL(900801, "GraphQL Schema cannot be empty or nul", 400, - "GraphQL Schema cannot be empty or null"), - UNSUPPORTED_GRAPHQL_FILE_EXTENSION(900802, "Unsupported GraphQL Schema File Extension", 400, - "Unsupported extension. Only supported extensions are .graphql, .txt and .sdl"), - - // Oauth related codes - AUTH_GENERAL_ERROR(900900, "Authorization Error", 403, " Error in authorization"), - INVALID_CREDENTIALS(900901, "Invalid Credentials", 401, " Invalid username or password"), - MISSING_CREDENTIALS(900902, "Missing Credentials", 401, " Please provide an active access token to proceed"), - ACCESS_TOKEN_EXPIRED(900903, "Invalid Credentials", 401, " Access token is expired."), - ACCESS_TOKEN_INACTIVE(900904, "Access Token Error", 401, " Access token is inactive."), - USER_NOT_AUTHENTICATED(900905, "User is not Authenticated", 401, " User is not authenticated."), - ACCESS_TOKEN_INVALID(900906, "Invalid Credentials", 401, " Access token is invalid."), - - INVALID_SCOPE(900910, "Invalid Scope", 403, " You are not authorized to access the resource."), - INVALID_AUTHORIZATION_HEADER(900911, "Invalid Authorization header", 401, - " Please provide the Authorization : Bearer <> token to proceed."), - MALFORMED_AUTHORIZATION_HEADER_OAUTH(900912, "Malformed Authorization Header", 400, - "Please provide the Authorization : Bearer <> token to proceed."), - MALFORMED_AUTHORIZATION_HEADER_BASIC(900913, "Malformed Authorization Header", 400, - "Please provide the Authorization : Basic <> token to proceed."), - INVALID_PERMISSION(900915, "Invalid Permission", 403, " You are not authorized to access the '%s'."), - OPENID_CONFIG(900916, "Missing OpenID configurations", 500, "Error in fetching Open ID configuration"), - OAUTH2_APP_CREATION_FAILED(900950, "Key Management Error", 500, "Error while creating the consumer application."), - OAUTH2_APP_ALREADY_EXISTS(900951, "Key Management Error", 409, "OAuth2 application already created."), - OAUTH2_APP_DELETION_FAILED(900952, "Key Management Error", 500, "Error while deleting the consumer application."), - OAUTH2_APP_UPDATE_FAILED(900953, "Key Management Error", 500, "Error while updating the consumer application."), - OAUTH2_APP_RETRIEVAL_FAILED(900954, "Key Management Error", 500, "Error while retrieving the consumer application." - ), - APPLICATION_TOKEN_GENERATION_FAILED(900957, "Keymanagement Error", 500, " Error while generating the application" + - "access token."), - APPLICATION_CONSUMER_KEY_NOT_FOUND(900958, "Keymanagement Error", 403, "Requested consumer key with application '%s' not found"), - UNSUPPORTED_THROTTLE_LIMIT_TYPE(900960, "Throttle Policy Error", 400, "Throttle Limit type is not supported"), - POLICY_NOT_FOUND(900961, "Policy Not found", 404, "Failed to retrieve Policy Definition"), - OAUTH2_APP_MAP_FAILED(900962, "Key Management Error", 500, "Error while mapping an existing consumer application."), - TOKEN_INTROSPECTION_FAILED(900963, "Key Management Error", 500, "Error while introspecting the access token."), - ACCESS_TOKEN_GENERATION_FAILED(900964, "Key Management Error", 500, "Error while generating a new access token."), - INVALID_TOKEN_REQUEST(900965, "Key Management Error", 400, "Invalid access token request."), - ACCESS_TOKEN_REVOKE_FAILED(900966, "Key Management Error", 500, "Error while revoking the '%s'."), - - INTERNAL_ERROR(900967, "General Error", 500, "Server Error Occurred"), - INTERNAL_ERROR_WITH_SPECIFIC_MESSAGE(903006, "%s", 500, "Server Error Occurred"), - INTERNAL_ERROR_WITH_SPECIFIC_DESC(903007, "Internal Server Error", 500, "'%s'"), - - POLICY_LEVEL_NOT_SUPPORTED(900968, "Throttle Policy level invalid", 400, "Specified Throttle policy level is not " - + "valid"), - POLICY_LEVEL_EMPTY(900973, "Policy Level can not be empty", 400, - "Throttle policy level can not be empty"), - POLICY_LEVEL_NOT_FOUND(900974, "Policy Level %s not found", 404, - "Throttle policy level %s not found"), - UNSUPPORTED_POLICY_TYPE(901001, "Policy type error", 400, "Unsupported policy type"), - UNSUPPORTED_TIER_TYPE(901002, "Policy tier error", 400, "Unsupported policy tier"), - INVALID_THROTTLE_TIER(901003, "Invalid throttle tier", 400, "Invalid x-throttling tier"), - - THROTTLE_TIER_NOT_FOUND(901004, "Throttle tier", 400, "throttling tier cannot be found"), - - THROTTLING_POLICY_NOT_FOUND(903005, "Throttling Policy Not Found", 404, - "Requested throttling policy with name '%s' and type '%s' not found"), - INVALID_APPLICATION_ADDITIONAL_PROPERTIES(900970, "Invalid application additional properties", 400, - "Invalid additional properties. %s"), - ERROR_RETRIEVE_APPLICATION_DETAILS(900972, "Error while obtaining details of the Application : %s", - 500, "Error while obtaining details of the Application : %s"), - JWT_PARSING_FAILED(900986, "Key Management Error", 500, "Error while parsing JWT. Invalid Jwt."), - TOKEN_SCOPES_NOT_SET( - 900987, "The token information has not been correctly set internally", 400, - "The token information has not been correctly set internally"), - MUTUAL_SSL_NOT_SUPPORTED( - 900988, "Mutual SSL based authentication is not supported in this server", 400, - "Cannot add client certificates to this server"), - THROTTLING_POLICY_CANNOT_BE_NULL(900989, - "Throttling Policy cannot be empty or null", 400, "Throttling Policy cannot be empty or null"), - ALREADY_ASSIGNED_ADVANCED_POLICY_DELETE_ERROR(900971, "Cannot delete the advanced throttling policy", 403, - "Cannot delete the advanced policy with the name %s because it is already assigned to an API/Resource"), - - //Throttle related codes - ADVANCED_POLICY_EXISTS(902900, "Advanced policy already exists", 409, "Advanced Policy with name '%s' already exists"), - APPLICATION_POLICY_EXISTS(902901, "Application policy already exists", 409, "Application Policy with name '%s' already exists"), - SUBSCRIPTION_POLICY_EXISTS(902902, "Subscription policy already exists", 409, "Subscription Policy with name '%s' already exists"), - GLOBAL_POLICY_EXISTS(902903, "Policy already exists", 409, "Policy already exists"), - ADVANCED_POLICY_ADD_FAILED(902904, "Error while adding an Advanced level policy: '%s'", 500, "'%s'"), - ADVANCED_POLICY_GET_FAILED(902905, "Error while retrieving Advanced level policy : '%s'", 500, "'%s'"), - ADVANCED_POLICY_UPDATE_FAILED(902906, "Error while updating Advanced level policy : '%s'", 500, "'%s'"), - SUBSCRIPTION_POLICY_GET_ALL_FAILED(902907, "Error while retrieving Subscription level policies", 500, "Server Error Occurred"), - SUBSCRIPTION_POLICY_ADD_FAILED(902908, "Error while adding Subscription level policies", 500, "Server Error Occurred"), - SUBSCRIPTION_POLICY_GET_FAILED(902909, "Error while retrieving Subscription level policy : '%s'", 500, "Server Error Occurred"), - BAD_POLICY_OBJECT(902010, "Policy object doesn't contain mandatory parameters", 500, "Policy object doesn't contain mandatory parameters."), - SUBSCRIPTION_POLICY_UPDATE_FAILED(902911, "Error while updating Subscription level policy : '%s'", 500, "Server Error Occurred"), - CUSTOM_RULE_EXISTS(902914, "Custom rule already exists", 409, "Custom rule with name %s already exists"), - INVALID_IP_ADDRESS_FORMAT(902915, "Invalid IP address format", 400, "Invalid IP address format"), - BLOCK_CONDITION_ALREADY_EXISTS(902916, "Block condition already exists", 409, "A block condition with type: %s, value: %s already exists"), - ALREADY_ASSIGNED_APP_POLICY_DELETE_ERROR(902912, "Cannot delete the application throttling policy", 409, "Policy %s is already attached to an Application"), - ALREADY_ASSIGNED_SUB_POLICY_DELETE_ERROR(902913, "Cannot delete the subscription throttling policy", 409, "Policy %s already has subscriptions"), - - THROTTLE_TEMPLATE_EXCEPTION(900969, "Policy Generating Error", 500, " Error while generate policy configuration"), - ENDPOINT_CONFIG_NOT_FOUND(90070, "Endpoint Config Not found", 404, "Error while retrieving Endpoint " + - "Configuration"), - UNSUPPORTED_THROTTLE_CONDITION_TYPE(900975, "Throttle Condition Error", 400, "Throttle Condition type is not " - + "supported"), - INVALID_DOCUMENT_CONTENT_DATA(900976, "Invalid document content data provided", 400, "Mismatch between provided " + - "document content data and Document Source Type given"), - BLOCK_CONDITION_UNSUPPORTED_API_CONTEXT(900977, "Block Condition Error", 400, "API Context does not exist"), - BLOCK_CONDITION_UNSUPPORTED_APP_ID_NAME(900978, "Block Condition Error", 400, "Application ID or Name does not " + - "exist"), - SYSTEM_APP_NOT_FOUND(900980, "System Application not found", 409, "System Application not found"), - - SHARED_SCOPE_NOT_FOUND(900981, "Shared Scope not found", 404, - "Requested Shared Scope ID %s could not be found"), - SHARED_SCOPE_ID_NOT_SPECIFIED(900982, "Shared Scope ID not specified", 400, - "Shared Scope ID not specified"), - SHARED_SCOPE_NAME_NOT_SPECIFIED(900983, "Shared Scope name not specified", 400, - "Shared Scope name not specified"), - SCOPE_ALREADY_REGISTERED(900984, "Scope already exists", 409, "Scope %s already exists"), - SHARED_SCOPE_ALREADY_ATTACHED(900985, "Shared Scope already attached", 409, - "Shared Scope %s is already used by one or more APIs"), - SCOPE_VALIDATION_FAILED(900986, "Scope validation failed", 412, "Scope validation failed"), - SHARED_SCOPE_DISPLAY_NAME_NOT_SPECIFIED(900987, "Shared Scope display name not specified", 400, - "Shared Scope display name not specified"), - SCOPE_ALREADY_ASSIGNED(900988, "Scope already assigned locally by another API", 400, - "Scope already assigned locally by another API"), - TOKEN_VALIDATION_FAILED(900989, "Validation failed for the given token", 500, "Validation failed for the given token"), - ERROR_CHECKING_SCOPE_NAME(901000, "Error while checking scope name", 500, - "Error occurred while checking scope name %s"), - ERROR_CREATING_URI_FOR_SHARED_SCOPE(901004, "Error while creating shared scope URI", 500, - "Error while creating URI for shared scope: %s"), - FAILED_RETRIEVE_SHARED_SCOPE(901005, "Error while retrieving shared scope", 500, - "Error while retrieving shared scope"), - FAILED_CHECKING_SCOPE_KEY_AVAILABILITY(901006, "Failed to check scope key availability for: %s" , - 500, "Error while checking scope key availability"), - - //Dedicated container based gateway related Codes - NO_RESOURCE_LOADED_FROM_DEFINITION(900990, "Container based resource Not Found", 404, "No resource loaded from " + - "definition provided"), - LOADED_RESOURCE_DEFINITION_IS_NOT_VALID(900991, "Loaded resource is not valid", 400, "The loaded resource " + - "definition is not a valid"), - TEMPLATE_LOAD_EXCEPTION(900992, "Error in loading the template file by client as an InputStream", 500, " Error " + - "in loading the FileInputStream by client"), - CONTAINER_GATEWAY_REMOVAL_FAILED(900993, "Cannot complete removing dedicated container based Gateway", 404, - "Error in deleting the dedicated container based Gateway"), - ERROR_INITIALIZING_DEDICATED_CONTAINER_BASED_GATEWAY(900994, "Error initializing dedicated container based" + - " gateway", 500, "Error initializing dedicated container based gateway"), - DEDICATED_CONTAINER_GATEWAY_CREATION_FAILED(900995, "Error while creating dedicated container based gateway", 500, - "Error while creating dedicated container based gateway"), - ERROR_WHILE_UPDATING_DEDICATED_CONTAINER_BASED_GATEWAY(900996, "Error while updating dedicated container based" + - " gateway", 500, "Error while updating dedicated container based gateway"), - ERROR_WHILE_RETRIEVING_DEDICATED_CONTAINER_BASED_GATEWAY(900997, "Error while retrieving dedicated container " + - "based gateway", 500, "Error while retrieving dedicated container based gateway"), - INVALID_DEDICATED_CONTAINER_BASED_GATEWAY_LABEL(900998, "Invalid gateway label is provided", 400, - "Invalid gateway label is provided"), - DEDICATED_GATEWAY_DETAILS_NOT_FOUND(900999, "Dedicated gateway details not found for the API", 404, "Dedicated " + - "gateway details not found for the API"), - - //Comments related Codes - NEED_COMMENT_MODERATOR_PERMISSION(901100, "Comment moderator permission needed", 403, - "This user is not a comment moderator"), - COULD_NOT_UPDATE_COMMENT(901101, "Error has occurred. Could not update the Comment", 500, - "Error has occurred. Could not update the Comment"), - COMMENT_NOT_FOUND(901102, "Comment not found", 404, "Failed to retrieve comment"), - COMMENT_LENGTH_EXCEEDED(901103, "Comment length exceeds max limit", 400, "Comment length exceeds allowed maximum " - + "number of characters"), - COMMENT_NO_PERMISSION(901104, "Insufficient permission", 403, "User '%s' doesn't have permission to access the comment with id '%s'"), - COMMENT_CANNOT_RETRIEVE(901105, "Failed to get '%s'", 500, "Failed to get '%s"), - - COMMENT_CANNOT_DELETE(901106, "Failed to delete the Comment", 500, "Failed to delete the Comment of '%s'"), - - NEED_ADMIN_PERMISSION(901100, "Admin permission needed", 403, - "This user is not an admin"), - - //External Stores related codes - EXTERNAL_STORE_ID_NOT_FOUND(901200,"External Store Not Found", 404, "Error while publishing to external stores. " + - "External Store Not Found"), - EXTERNAL_STORE_CLASS_NOT_FOUND(901201, - ExceptionConstants.EXTERNAL_STORE_ERROR_MSG, 404, - "One or more classes defined in APIConstants.EXTERNAL_API_STORE_CLASS_NAME cannot be found"), - EXTERNAL_STORE_CLASS_NOT_LOADED(901202, - ExceptionConstants.EXTERNAL_STORE_ERROR_MSG, 500, - "One or more classes defined in APIConstants.EXTERNAL_API_STORE_CLASS_NAME cannot be loaded"), - EXTERNAL_STORE_CLASS_NOT_ACCESSIBLE(901203, - ExceptionConstants.EXTERNAL_STORE_ERROR_MSG, 500, - "One or more classes defined in APIConstants.EXTERNAL_API_STORE_CLASS_NAME cannot be accessed"), - ERROR_RETRIEVE_EXTERNAL_STORE_CONFIG(901204, "External Store Config Retrieve Error", 500, - "Error while retrieving External Stores Configuration from registry"), - MALFORMED_XML_IN_EXTERNAL_STORE_CONFIG(901205, "Malformed XML in External Stores Configuration", - 500, "Malformed XML found in the External Stores Configuration resource"), - - // Tenant related - INVALID_TENANT(901300,"Tenant Not Found", 404, "Tenant Not Found"), - CONFIG_NOT_FOUND(901301, "Config not found", 404, "Config not found in tenant-config"), - ERROR_GETTING_CUSTOM_URLS(901302, "Failed to get custom url info", 500, "Error while retrieving custom url info for tenant : '%s'"), - // Key Manager Related - INVALID_KEY_MANAGERS_VALUE(901350, "Key Managers value need to be an array", 400, - "Invalid Key Managers value"), - INVALID_KEY_MANAGER_TYPE(901400, "Key Manager Type not configured", 400, "Key Manager Type not configured"), - REQUIRED_KEY_MANAGER_CONFIGURATION_MISSING(901401,"Required Key Manager configuration missing",400,"Missing " + - "required configuration"), - KEY_MANAGER_ALREADY_EXIST(901402, "Key Manager Already Exists", 409, "Key Manager Already Exists"), - KEY_MANAGER_NOT_REGISTERED(901403, "Key Manager not Registered", 400, "Key Manager not Registered"), - KEY_MANAGER_NOT_FOUND(901411, "Key Manager not Found", 404, "Key Manager not found"), - KEY_MANAGER_NAME_EMPTY(901404, - "Key Manager name cannot be empty", 400,"Key Manager name cannot be empty"), - KEY_MANAGER_NOT_SUPPORT_OAUTH_APP_CREATION(901405, "Key Manager doesn't support generating OAuth applications", 400, - "Key Manager doesn't support generating OAuth applications"), - KEY_MANAGER_NOT_SUPPORTED_TOKEN_GENERATION(901405, "Key Manager doesn't support token generation", 400, - "Key Manager doesn't support token generation"), - KEY_MANAGER_NOT_ENABLED(901406, "Key Manager is not enabled in the system", 400, - "Key Manager is not enabled in the system"), - KEY_MANAGER_MISSING_REQUIRED_PROPERTIES_IN_APPLICATION(901407, "Required application properties are missing", 400, - "Required application properties are missing"), - APPLICATION_ALREADY_REGISTERED(901408, "Application already Registered", 409, "Application already Registered"), - KEY_MAPPING_ALREADY_EXIST(901409, "Key Mappings already exists", 409, "Key Mappings already exists"), - TENANT_MISMATCH(901410,"Tenant mismatch", 400, "Tenant mismatch"), - INVALID_APPLICATION_PROPERTIES(901411, "Invalid additional properties", 400, - "Invalid additional properties given for application"), - DECRYPT_CONFIG_ERROR(901412, "Error while decrypting key manager configuration", 500, "Unable to decrypt the value"), - - //Scope related - SCOPE_NOT_FOUND_FOR_USER(901500, "Scope does not belong to this user", 404, "Scope not found"), - SCOPE_NOT_FOUND(901501, "Scope Not Found", 404, "Scope does not exist"), - USER_NOT_FOUND(901502, "User Not Found", 404, "User does not exist"), - DEFINITION_EXCEPTION(901503, "Internal server error.", 500, " Error occurred while retrieving swagger definition"), - - //Analytics related codes - ANALYTICS_NOT_ENABLED(901600, "%s not accessible", 404, - "Analytics should be enabled to access %s"), - UNSUPPORTED_ALERT_TYPE(901601, "Unsupported alert type", 400, "Unsupported alert type: '%s' is provided"), - MALFORMED_SP_URL(901602, "Malformed URL", 500, "Error while parsing the stream processor url"), - ERROR_INVOKING_SP_REST_API(901603, "Error while invoking steam processor REST API", 500, "'%s'"), - ALREADY_SUBSCRIBED_FOR_BOT_ALERTS(901604, "Subscription already exists", 409, "Email: '%s' has already been subscribed for bot detection alerts"), - BOT_DETECTION_SUBSCRIPTION_NOT_FOUND(901605, "Subscription does not exist", 404, "Bot detection alert subscription with uuid: '%s' uuid does not exist"), - - // Password change related - PASSWORD_CHANGE_DISABLED(901450, "Password change disabled", 400, "Password change operation is disabled in the system"), - - CURRENT_PASSWORD_INCORRECT(901451, "Current password incorrect", 400, "The current password entered is incorrect"), - - PASSWORD_PATTERN_INVALID(901452, "Password pattern invalid", 400, "Password entered is invalid since it doesn't comply with the pattern/policy configured"), - - //Tenant theme related codes - TENANT_THEME_IMPORT_FAILED(901700, "Failed to import tenant theme of tenant %s", 500, - "%s"), - TENANT_THEME_EXPORT_FAILED(901701, "Failed to export tenant theme of tenant %s", 500, - "%s"), - TENANT_THEME_IMPORT_NOT_ALLOWED(901702, "Super Tenant not allowed to import tenant theme", 400, - "Super Tenant %s is not allowed to import a tenant theme"), - TENANT_THEME_NOT_FOUND(901703, "Tenant theme does not exist", 404, "Tenant theme for tenant: '%s' does not exist"), - - INVALID_API_IDENTIFIER(900851, "Provided API identifier (%s) is invalid", 400, - "Provided API identifier (%s) is invalid"), - API_NAME_OR_VERSION_NOT_NULL(900852, "name or version couldn't be null", 400, "name or version couldn't be null"), - INVALID_CONFIGURATION_ID(900853,"The configuration id validation failed. Should be " + - "{apiName}#{apiVersion}#{tenantDomain}",400,"The configuration id validation failed. Should be " + - "{apiName}#{apiVersion}#{tenantDomain}"), - INVALID_API_NAME(900854, "Invalid API Name",400 ,"Invalid API Name"), - ALIAS_CANNOT_BE_EMPTY(900855, "The alias cannot be empty", 400, "The alias cannot be empty"), - - // API import/export related codes - ERROR_READING_META_DATA(900907, "Error while reading meta information from the definition", 400, - "Error while reading meta information from the definition"), - ERROR_READING_PARAMS_FILE(900908, "Error while reading meta information from the params file", 400, - "%s"), - ERROR_FETCHING_DEFINITION_FILE(900909, "Cannot find the definition file of the project", 400, - "Cannot find the yaml/json file with the project definition."), - NO_API_ARTIFACT_FOUND(900910, "No Api artifacts found for given criteria", 404, - "No Api artifacts found for given criteria"), - ERROR_UPLOADING_THUMBNAIL(900914, - "Error while updating thumbnail of API/API Product", 500, - "Error while updating thumbnail of API/API Product: %s-%s"), - APICTL_OPENAPI_PARSE_EXCEPTION( - OPENAPI_PARSE_EXCEPTION.getErrorCode(), OPENAPI_PARSE_EXCEPTION.getErrorMessage(), - OPENAPI_PARSE_EXCEPTION.getHttpStatusCode(), "%s"), - GATEWAY_TYPE_NOT_FOUND(900903, "Gateway type not found", 404, - "Gateway type not found available Gateway types : " + "%s"), - - SERVICE_IMPORT_FAILED_WITHOUT_OVERWRITE(900910, "Service import is failed" , 412, "Cannot update existing services " + - "when overwrite is false"), - MISSING_PROTOCOL_IN_ASYNC_API_DEFINITION(900911, "Missing protocol in Async API Definition", 400, - "Missing protocol in Async API Definition"), - UNSUPPORTED_PROTOCOL_SPECIFIED_IN_ASYNC_API_DEFINITION(900912, "Unsupported protocol specified in Async API " + - "Definition", 400, "Unsupported protocol specified in Async API Definition"), - API_CREATION_NOT_SUPPORTED_FOR_ASYNC_TYPE_APIS(900915, "API Creation is supported only for WebSocket, WebSub and SSE APIs", 400, - "API Creation is supported only for WebSocket, WebSub and SSE APIs"), - LOGGING_API_NOT_FOUND(901400, "Requested Resource Not Found", 404, "Request API Not Found for context: %s"), - LOGGING_API_INCORRECT_LOG_LEVEL(901401, "Bad Request", 400, "Log level should be either OFF, BASIC, STANDARD or FULL"), - LOGGING_API_MISSING_DATA(901402, "Missing data", 400, "API context or log level is missing"), - - //Service Catalog related error codes - SERVICE_VERSION_NOT_FOUND(901900, "Cannot find the service version", 404, "Cannot find a service that matches the given version"), - ERROR_RETRIEVE_SERVICE_INFORMATION(901901, "Error while getting service information", - 500, "Error while executing SQL for getting service information"), - - INVALID_ENDPOINT_CREDENTIALS(902000, "Invalid Endpoint Security credentials", 400, - "Invalid Endpoint Security credentials. %s", false), - INVALID_TENANT_CONFIG(902001, "Invalid tenant-config found", 400, "Invalid tenant-config found with error %s", false), - - //Operation Policies related error codes - INVALID_OPERATION_POLICY(902005, "%s. Cannot find the selected operation policy", 400, - "Selected operation policy is not found"), - INVALID_OPERATION_POLICY_SPECIFICATION(902006, "Invalid operation policy specification found", 400, - "Invalid operation policy specification. %s", false), - - INVALID_OPERATION_POLICY_PARAMETERS(902007, "Missing required parameters for operation policy specification", 400, - "Required parameter(s) %s for operation policy specification %s are either missing or empty"), - OPERATION_POLICY_NOT_ALLOWED_IN_THE_APPLIED_FLOW(902008, "Operation policy is not allowed in the applied flow", 400, - "%s policy is not allowed in response flow"), - MISSING_MANDATORY_POLICY_ATTRIBUTES(902009, "Missing mandatory operation policy attribute", 400, - "Required attributes(s) %s for operation policy specification %s are either missing or empty"), - OPERATION_POLICY_NOT_FOUND(902010, "Operation Policy Not Found", 404, - "Requested operation policy with id '%s' not found"), - OPERATION_POLICY_SPEC_MISMATCH(902011, "Applied policy does not match specification", 400, "Applied policy for URI template does not match specification"), - - OPERATION_POLICY_ALREADY_EXISTS(903001, "The Operation Policy already exists.", 409, "An Operation Policy with name '%s' and version '%s' already exists"), - - OPERATION_POLICY_NOT_FOUND_WITH_NAME_AND_VERSION(903004, "Operation Policy Not Found with given name and version", 404, - "Requested operation policy with name '%s' and version '%s' not found"), - - OPERATION_POLICY_GATEWAY_ERROR(903008, - "Either Synapse or Choreo Gateway Definition files or both should be present", 400, - "Operation Policy cannot be imported due to the missing Gateway files."), - OPERATION_POLICY_USAGE_EXISTS(903009, "Operation policy usages exist", 500, "Policy usages exist for policy ID '%s'"), - - SUBSCRIPTION_TIER_NOT_ALLOWED(902002, "Subscription Tier is not allowed for user", 403, "Subscription Tier %s is" + - " not allowed for user %s ", false), - INVALID_KEY_MANAGER_REQUEST(902003, "Invalid Request sent to Key Manager.", 400, "Invalid Request sent to Key Manager.Error from Backend : %s", false), - INTERNAL_SERVER_ERROR_FROM_KEY_MANAGER(902004, "Internal Server Error from Key Manager", 500, "Internal Server Error from Key Manager.Error from Backend : %s", true), - REVISION_ALREADY_DEPLOYED(902005, "Revision deployment state conflicted", 409, - "Revision deployment request conflicted with the current deployment state of the revision %s. Please try again later", false), - INVALID_API_ID(902006, "Invalid API ID", 404, - "The provided API ID is not found %s", false), - GATEWAY_DOMAIN_MAPPING_RETRIEVE_ERROR(902100, "Error retrieving gateway domain mappings from registry", - 500, "Error while retrieving gateway domain mappings from registry"), - INVALID_GATEWAY_DOMAIN_MAPPING_JSON(902101, "Invalid JSON in gateway tenant domain mappings", - 500, "Invalid JSON found in the gateway tenant domain mappings"), - MALFORMED_GATEWAY_DOMAIN_MAPPING_JSON(902102, "Malformed JSON in gateway tenant domain mappings", - 500, "Malformed JSON found in the gateway tenant domain mappings"), - ERROR_PARSING_TENANT_CONFIG_JSON(902103, "Error occurred while converting to json", - 500, "Error occurred while converting tenantConfig to json"), - FAILED_RETRIEVE_CONFIGURATION_FOR_ORGANIZATION(902150, "Failed to retrieve configuration", - 500, "Failed to retrieve %s Configuration for org: %s"), - INVALID_QUERY(902200, "Failed to retrieve configuration", - 500, "Failed to retrieve %s Configuration for org: %s"), - ERROR_RETRIEVING_CLAIM_VALUES(902300, "Error while retrieving claim values from user store", - 500, "Error while retrieving claim values from user store"), - FAILED_FIND_API_USAGE(902350, "Failed to find API Usage for : %s", 500, - "Failed to find API Usage for : %s"), - BAD_REQUEST_SUBSCRIPTION_ID(902351, "Invalid Subscription ID", 400, - "Invalid Subscription ID"), - FAILED_GET_SUBSCRIPTION_POLICY(902352, "Failed to get subscription policy: %s", 500, - "Failed to retrieve subscription policy: %s data"), - FAILED_GET_API_POLICY(902353, "Failed to get API policy: %s", 500, - "Failed to retrieve API policy: %s data"), - FAILED_GET_APPLICATION_POLICY(902354, "Failed to get application policy: %s", 500, - "Failed to retrieve application policy: %s data"), - - READ_ONLY_ENVIRONMENT_NOT_FOUND(902400, "Configured read only environment not found: %s", - 404, "Configured read only environment not found: %s"), - - // monetization related codes - - INVALID_API_STATE_MONETIZATION(904300, "Invalid API state", 400, "Invalid API state to configure monetization"), - MONETIZATION_STATE_CHANGE_FAILED(904301, "Could not change the monetization state", 500, "Monetization state change to '%s' failed"), - - MONETIZATION_IMPLEMENTATION_LOADING_FAILED(904302, "Could not load the monetization implementation", 500, "Failed to load the monetization implementation"), - - // audit related codes - - AUDIT_SEND_FAILED(904200, "Error sending audit data", 500, "Sending audit data failed. Response code: '%s'"), - AUDIT_RETRIEVE_FAILED(904201, "Error retrieving audit data", 500, "Error retrieving audit data"), - - // transport related codes - UNSUPPORTED_TRANSPORT_TYPE(904100, "Unsupported transport type", 400, "Transport type '%s' is not supported"), - - // certificate related error codes - - CERT_NOT_FOUND(904001, "Could not find the certificate", 404, "'Cannot find the certificate with alias '%s' in the truststore'"), - CERT_BAD_REQUEST(904002, "Bad Request", 400, "'%s"), - GET_CERT_CONTENT(904003, "Error getting the certificate content", 500, "'%s'"), - RETRIEVE_CERT(904004, "Could not retrieve the certificate", 500, "'%s"), - DELETE_CERT(904005, "Could not delete the certificate", 500, "Error while deleting the certificate for alias '%s'"), - GET_CERT_INFO(904006, "Could not get the certificate information", 500, "'%s"), - UPDATE_CERT(904007, "Could not update the certificate", 500, "'%s'"), - ENCODE_CERT(904008, "Error occurred while encoding the certificate", 500, "'%s"), - INTERNAL_SERVER_CERT(904009, "Internal server error", 500, "'%s'"), - EXPIRED_CERT(904010, "Certificate expired", 400, "'%s'"), - CERT_ALREADY_EXIST(904011, "Certificate alias already exists", 409, "The alias '%s' already exists in the truststore"), - DECODE_CERT(904012, "Error occurred while decoding the certificate", 500, "'%s'"), - - INVALID_KEY_TYPE(904013, "Bad Request", 400, "Invalid keyType. KeyType should be either PRODUCTION or SANDBOX"), - - ERROR_DELETING_APPLICATION_REGISTRATION(904014, "Can not delete application registration", 400, "Failed to delete Application registration of : '%s'"), - - ERROR_DELETING_APPLICATION_KEY_MAPPING(904015, "Can not delete application key mapping", 500, "Failed to delete Application key mapping of : '%s'"), - - ERROR_RETRIEVE_APPLICATION_KEYS(904016, "Failed to retrieve application keys", 500, "Failed to retrieve application keys for '%s' "); - - private final long errorCode; - private final String errorMessage; - private final int httpStatusCode; - private final String errorDescription; - private boolean stackTrace = false; - - /** - * @param errorCode This is unique error code that pass to upper level. - * @param msg The error message that you need to pass along with the error code. - * @param httpErrorCode This HTTP status code which should return from REST API layer. If you don't want to pass - * a http status code keep it blank. - * @param errorDescription The error description. - */ - ExceptionCodes(long errorCode, String msg, int httpErrorCode, String errorDescription, boolean stackTrace) { - this.errorCode = errorCode; - this.errorMessage = msg; - this.httpStatusCode = httpErrorCode; - this.errorDescription = errorDescription; - this.stackTrace = stackTrace; - } - - /** - * @param errorCode This is unique error code that pass to upper level. - * @param msg The error message that you need to pass along with the error code. - * @param httpErrorCode This HTTP status code which should return from REST API layer. If you don't want to pass - * a http status code keep it blank. - * @param errorDescription The error description. - */ - ExceptionCodes(long errorCode, String msg, int httpErrorCode, String errorDescription) { - this.errorCode = errorCode; - this.errorMessage = msg; - this.httpStatusCode = httpErrorCode; - this.errorDescription = errorDescription; - } - @Override - public long getErrorCode() { - return this.errorCode; - } - - @Override - public String getErrorMessage() { - return this.errorMessage; - } - - @Override - public int getHttpStatusCode() { - return this.httpStatusCode; - } - - @Override - public String getErrorDescription() { - return this.errorDescription; - } - - public boolean printStackTrace() { - - return stackTrace; - } - - /** - * Create an ErrorHandler instance with the provided ExceptionCode filled with some dynamic input values - * - * @param errorHandler ErrorHandler or ExceptionCode object - * @param params dynamic values to be filled - * @return ErrorHandler instance with the provided ExceptionCode filled with some dynamic input values - */ - public static ErrorHandler from(ErrorHandler errorHandler, String... params) { - String message = errorHandler.getErrorMessage(); - String description = errorHandler.getErrorDescription(); - - if (params != null && params.length > 0) { - int placesToFormatInMessage = message.length() - message.replace("%", "").length(); - int placesToFormatInDescription = description.length() - description.replace("%", "").length(); - - String[] part1 = Arrays.copyOfRange(params, 0, placesToFormatInMessage); - String[] part2 = Arrays.copyOfRange(params, placesToFormatInMessage, - placesToFormatInMessage + placesToFormatInDescription); - - if (placesToFormatInMessage > 0) { - message = String.format(message, part1); - } - if (placesToFormatInDescription > 0) { - description = String.format(description, part2); - } + // API, Application related codes + API_NAME_ALREADY_EXISTS(900250, "The API name already exists.", 409, "An API with name '%s' already exists"), + API_CONTEXT_ALREADY_EXISTS(900251, "The API context already exists.", 409, + "An API with context '%s' already exists"), + API_VERSION_ALREADY_EXISTS(900252, "The API version already exists.", 409, + "An API with version '%s' already exists for API '%s'"), + API_EMPTY_PASSWORD_FOR_SECURED_ENDPOINT(900253, + "Empty password is given for endpointSecurity when creating API: %s", + 400, "An empty password is given for endpointSecurity when creating API: %s"), + + API_PRODUCT_CONTEXT_ALREADY_EXISTS(900275, "The API Product context already exists.", 409, + "An API Product with context '%s' already exists"), + + API_ALREADY_EXISTS(900300, "The API already exists.", 409, "The API already exists"), + APPLICATION_ALREADY_EXISTS(900301, "The application already exists.", 409, "The application already exists"), + APIMGT_DAO_EXCEPTION(900302, "Internal server error.", 500, "Error occurred while persisting/retrieving data"), + APIMGT_LIFECYCLE_EXCEPTION(900303, "Lifecycle exception occurred", 500, "Error occurred while changing " + + "lifecycle state"), + TIER_CANNOT_BE_NULL(900304, "The tier cannot be null.", 400, "The tier cannot be null"), + TIER_NAME_INVALID(900305, "The tier name is invalid.", 400, "The tier name is invalid"), + SEARCH_API_EXCEPTION(900306, "Internal server error.", 500, "Error occurred while searching APIs"), + APPLICATION_NOT_FOUND(900307, "Application not found", 404, "Application not found"), + API_NOT_FOUND(900308, "API Not Found", 404, "Requested API with id '%s' not found"), + APPLICATION_DELETE_FAILED(900309, "Error has occurred. Could not delete the application", 500, + "Error has occurred. Could not delete the application '%s'"), + SUBSCRIPTION_NOT_FOUND(900310, "Subscription not found", 404, "Couldn't retrieve Subscriptions for API"), + UPDATE_STATE_CHANGE(900311, "API fields have state changes", 400, + "Couldn't Update as API have changes can't be done"), + DOCUMENT_ALREADY_EXISTS(900312, "Document already exists", 409, "Document already exists"), + COULD_NOT_UPDATE_API(900313, "Error has occurred. Could not update the API", 500, + "Error has occurred. Could not " + + "update the API"), + DOCUMENT_CONTENT_NOT_FOUND(900314, "Document content not found", 404, "Document content not found"), + DOCUMENT_NOT_FOUND(900315, "Document not found", 404, "Document not found"), + DOCUMENT_INVALID_SOURCE_TYPE(900319, "Invalid document source type", 500, + "Source type of the document '%s' is invalid"), + UNSUPPORTED_DOC_EXTENSION(900367, "Document file type not supported", 400, + "Unsupported extension type of document file"), + + API_EXPORT_ERROR(900316, "API export Error", 500, "Error while exporting the given APIs"), + API_IMPORT_ERROR(900317, "API import Error", 500, "Error while importing the given APIs"), + API_PRODUCT_IMPORT_ERROR(900318, "API product import Error", 500, + "Error while importing the given API Products"), + SUBSCRIPTION_STATE_INVALID(900320, "Invalid state change for subscription", 400, "Invalid state change for " + + "subscription"), + API_RETRIEVE_EXCEPTION(900319, "Internal server error.", 500, "Error occurred while retrieving %s"), + APPLICATION_RETRIEVE_EXCEPTION(900320, "Internal server error.", 500, "Error occurred while retrieving %s"), + SUBSCRIPTION_RETRIEVE_EXCEPTION(900321, "Internal server error.", 500, "Error occurred while retrieving %s"), + + GATEWAY_LABELS_CANNOT_BE_NULL(900322, "Gateway labels cannot be null.", 400, "Gateway labels cannot be null"), + ERROR_RETRIEVING_RATING(900323, "Cannot retrieve rating", 500, "Error while retrieving ratings for API '%s'"), + RATING_NOT_FOUND(900324, "Rating not found", 404, "Couldn't retrieve rating for API '%s'"), + RATING_VALUE_INVALID(900325, "Rating value invalid", 400, + "Provided rating value does not fall in between min max " + + "values"), + DOCUMENT_INVALID_VISIBILITY(900326, "Invalid document visibility type", 500, + "Visibility type of the document '%s' is invalid"), + API_TYPE_INVALID(900327, "API Type specified is invalid.", 400, "API Type specified is invalid"), + COMPOSITE_API_ALREADY_EXISTS(900328, "A Composite API already exists.", 409, + "A Composite API already exists for this application"), + API_DEFINITION_NOT_FOUND(900330, "API definition not found", 404, "API definition not found"), + APPLICATION_KEY_MAPPING_NOT_FOUND(900331, "Application Key mapping not found", 404, + "Application Key mapping not " + + "found"), + NO_UPDATE_PERMISSIONS(900332, "No permissions to update API.", 403, "No permissions to update API."), + NO_DELETE_PERMISSIONS(900333, "No permissions to delete API.", 403, "No permissions to delete API."), + API_ATTRIBUTE_NOT_FOUND(900335, "API attribute not found", 404, "API attribute not found"), + SUBSCRIPTION_ALREADY_EXISTS(900336, "Subscription already exists", 409, "Subscription already exists"), + SDK_NOT_GENERATED(900337, "Error while generating SDK", 500, "Error while generating SDK"), + APPLICATION_EXPORT_ERROR(900338, "Application Export Error", 500, + "Error while exporting the given Application"), + APPLICATION_IMPORT_ERROR(900339, "Application Import Error", 500, + "Error while importing the given Application"), + NO_READ_PERMISSIONS(900340, "No permissions to read API.", 403, "No permissions to read API."), + API_PRODUCT_DUPLICATE_RESOURCE(900341, "Cannot create API Product with duplicate resource", + 400, "Cannot create API Product with duplicate resource: %s , verb: %s combination"), + API_PRODUCT_RESOURCE_ENDPOINT_UNDEFINED(900342, + "Cannot create API Product, due to resources with undefined endpoints in their parent APIs", + 409, + "Cannot create API Product %s, due to resources with undefined endpoints in their parent APIs %s"), + API_PRODUCT_WITH_UNSUPPORTED_LIFECYCLE_API(900343, + "Cannot create API Product, due to resources parent API being in an unsupported Life Cycle state", + 409, + "Cannot create API Product, due to resources parent API being in an unsupported Life Cycle state: %s"), + API_PRODUCT_USED_RESOURCES(900344, + "Cannot remove the resource paths because they are used by one or more API Products", + 409, + "Cannot update API: %s:%s, due to the resources to remove are used by one or more API Products"), + API_DELETE_API_PRODUCT_USED_RESOURCES(900344, API_PRODUCT_USED_RESOURCES.getErrorMessage(), 409, + "Cannot delete API since the resources to remove are used by one or more API Products"), + API_CATEGORY_INVALID( + 900345, "The API category is invalid.", 400, " The API category is invalid for API: %s:%s"), + INVALID_ADDITIONAL_PROPERTIES(900346, "Invalid additional properties", 400, + "Invalid additional properties for API: %s:%s"), + INVALID_CONTEXT(900346, "Invalid context provided", 400, "Invalid context provided for API: %s:%s"), + INVALID_ENDPOINT_URL(900346, "Endpoint URL(s) is(are) not valid", 400, "Endpoint URL(s) is(are) not valid"), + USER_ROLES_CANNOT_BE_NULL(900610, "User roles cannot be found", 400, "User roles cannot be found"), + + ERROR_RETRIEVING_REVISION_FOR_UUID(900410, "Failed to get revision details for revision UUID: %s", + 500, "Failed to get revision details"), + + ERROR_RETRIEVING_REVISION_DEPLOYMENT_MAPPING(900411, + "Failed to get API Revision deployment mapping details for %s: %s", 500, + "Failed to get API Revision deployment mapping details"), + + ERROR_UPDATING_REVISION_DEPLOYMENT_MAPPING(900412, + "Failed to update Deployment Mapping entry for API UUID: ", 500, + "Failed to update Deployment Mapping entry"), + + METHOD_NOT_ALLOWED(900420, "Not allowed", 405, "Method not allowed"), + API_REVISION_NOT_FOUND(900347, "API Revision Not Found", 404, "Requested API Revision with id %s not found"), + EXISTING_API_REVISION_DEPLOYMENT_FOUND(900348, "Can not delete API Revision ", 400, + "Couldn't delete API revision since API revision is currently deployed to a gateway. " + + "You need to undeploy the API Revision from the gateway before attempting deleting API Revision: %s "), + EXISTING_API_REVISION_FOUND(900349, "Can not create API Revision ", 400, + "API revision already exists with id: %s "), + API_REVISION_UUID_NOT_FOUND(900350, "Can not create API Revision ", 400, + "Failed to retrieve revision uuid from revision registry artifact"), + MAXIMUM_REVISIONS_REACHED(900351, "Can not create API Revision ", 400, + "Maximum number of revisions per API has reached." + + "Need to remove any revision to create a new Revision for API with API UUID: %s"), + ERROR_CREATING_API_REVISION(900352, "Can not create API Revision ", 400, + "Failed to create API revision registry artifacts: %s "), + ERROR_DELETING_API_REVISION(900353, "Can not delete API Revision ", 400, + "Failed to delete API revision registry artifacts: %s "), + ERROR_RESTORING_API_REVISION(900354, "Can not restore API Revision ", 400, + "Failed to restore API revision registry artifacts: %s "), + DEPLOYMENT_ID_NOT_FOUND(900355, "Deployment Id Not Found", 400, "Deployment Id Not Found"), + EXISTING_DEPLOYMENT_NOT_FOUND(900356, "Existing Deployment Not Found", 404, "Deployment with %s not found"), + ORGANIZATION_NOT_FOUND(900357, "Organization Not Found", 400, "Organization is not found in the request"), + INVALID_ENV_API_PROP_CONFIG(900358, "Invalid environment specific api property config", 400, + "Environment specific api property config is not valid. %s", false), + API_OR_API_PRODUCT_NOT_FOUND(900359, "API or API Product Not Found", 404, + "Requested API or API Product with id '%s' not found"), + API_PRODUCT_NOT_FOUND(900360, "API Product Not Found", 404, "Requested API Product with id '%s' not found"), + SUB_ORGANIZATION_NOT_IDENTIFIED(900361, "User's Organization Not Identified", 403, + "User's Organization is not identified"), + ERROR_RETRIEVING_CATEGORY(900362, "Cannot retrieve categories", 500, + "Error while retrieving categories for organization '%s'"), + PERSISTENCE_ERROR(900363, "Error occurred in registry transaction", 500, "'%s'"), + NO_VIEW_UPDATE_PERMISSIONS(900365, "Insufficient permission to view or update the API", 403, + "Insufficient permission to view or update the API"), + API_DELETE_FAILED_SUBSCRIPTIONS(900366, "Failed to delete the API", 409, + "Cannot remove the API as active subscriptions exist"), + CATEGORY_NAME_CONTAINS_SPECIAL_CHARS(900368, "Name field contains special characters", 400, + "API Category name contains special characters"), + CATEGORY_NAME_TOO_LONG(900369, "API Category name is too long", 400, + "API Category name exceeds 255 characters"), + CATEGORY_ALREADY_EXISTS(900370, "API Category name already exists", 409, + "Category with name '%s' already exists"), + CATEGORY_NOT_FOUND(900371, "Category not found", 404, "No API Category with the given category ID '%s' exists"), + CATEGORY_USED(900372, "Category has usages", 409, "Category is attached to one or more APIs"), + ERROR_CHANGING_APP_OWNER(900373, "Failed to change the application owner", 500, + "Error while changing the application owner"), + + USER_SUBSCRIPTION_EXISTS_CHECK_FAILED(900374, "Failed to check if user subscribed to API", 500, + "Failed to check if user '%s' with an application '%s' is subscribed to API %s"), + + USER_ACCESSIBLE_APPLICATION_CHECK_FAILED(900375, "Failed to check if user subscribed to API", 500, + "Error occurred while checking whether the application '%s' is accessible to user '%s'"), + API_TAGS_NOT_FOUND(900376, "API Tags Not Found", 404, "API Tags not found for organization '%s'"), + + // Lifecycle related codes + API_UPDATE_FORBIDDEN_PER_LC(900380, "Insufficient permission to update the API", 403, + "Updating the API is restricted as as it is %s."), + UNSUPPORTED_LIFECYCLE_ACTION(900381, "Unsupported state change action", 400, + "Lifecycle state change action %s is not allowed"), + LIFECYCLE_STATE_INFORMATION_NOT_FOUND(900382, "Lifecycle state information not found", 500, + "Lifecycle state change information for %s with %s cannot be found"), + + // Generic codes + JSON_PARSE_ERROR(900400, "Json parse error", 500, "JSON parse error"), + RESOURCE_NOT_FOUND(900401, "Resource not found", 404, "Requested resource not found"), + RESOURCE_RETRIEVAL_FAILED(900402, "Resource retrieval failed", 400, "Resource retrieval failed"), + USER_MAPPING_RETRIEVAL_FAILED(900404, "User mapping retrieval failed", 404, "User mapping retrieval failed"), + MALFORMED_URL(900403, "Malformed URL", 400, "Invalid or Malformed URL"), + URI_PARSE_ERROR(900405, "Error constructing the URI", 500, "'%s'"), + INVALID_OPERATION_TYPE(900406, "Unsupported '%s' operation", 400, "The '%s' operation type '%s' is invalid"), + VERB_NOT_FOUND(900407, "Missing '%s' type", 400, "Missing '%s type in URI templates"), + YAML_PARSE_ERROR(900408, "Yaml parse error", 500, "Yaml parse error"), + RESOURCE_NOT_FOUND_WITH_TYPE_AND_ID(900409, "Requested %s with Id %s not found", 404, + "Requested %s with Id %s not found"), + AUTHORIZATION_ERROR(900409, "Forbidden", 403, "You don't have permission to access the '%s' with Id '%s'"), + FORBIDDEN_ERROR(900409, "Forbidden", 403, "You don't have permission to access this resource"), + RESOURCE_NOT_FOUND_WITH_DESC(900401, "Resource not found", 404, "Requested '%s' with Id '%s' not found"), + UNAUTHORIZED(900410, "Unauthorized", 401, "User is unauthorized"), + + // Endpoint related codes + ENDPOINT_NOT_FOUND(900450, "Endpoint Not Found", 404, "Endpoint Not Found"), + ENDPOINT_ALREADY_EXISTS(900451, "Endpoint already exists", 409, "Endpoint already exists"), + ENDPOINT_ADD_FAILED(900452, "Endpoint adding failed", 400, "Endpoint adding failed"), + ENDPOINT_DELETE_FAILED(900453, "Endpoint Delete Failed", 400, "Endpoint Delete Failed"), + ENDPOINT_CRYPTO_ERROR(900454, "Error while encrypting/decrypting endpoint secrets", 500, "'%s'"), + ENDPOINT_CONFIG_PARSE_FAILED(900455, "Endpoint config parsing failed", 500, + "Error occurred while parsing endpoint config json"), + + // Service Endpoint Discovery related codes + ERROR_LOADING_SERVICE_DISCOVERY_IMPL_CLASS(900460, "Error loading service discovery impl class", 500, + "Error while trying to load a service discovery impl class"), + ERROR_INITIALIZING_SERVICE_DISCOVERY(900461, "Error initializing service discovery", 500, + "Error while connecting to the system with services"), + ERROR_WHILE_TRYING_TO_DISCOVER_SERVICES(900462, "Error while discovering services", 500, + "Error while trying to discover service endpoints"), + + // Gateway related codes + API_DEFINITION_MALFORMED(900500, "ApiDefinition not found", 400, "Failed to retrieve API Definition"), + TEMPLATE_EXCEPTION(900501, "Service configuration Error", 500, "Error generate service config"), + GATEWAY_EXCEPTION(900502, "Gateway publishing Error", 500, "Error occurred while publishing to Gateway"), + BROKER_EXCEPTION(900503, "Broker Connection Error", 500, "Error occurred while obtaining broker connection"), + INVALID_GATEWAY_ENVIRONMENT(900504, "Invalid Gateway Environment", 400, + "Gateway Environment with name '%s' not found"), + NO_GATEWAY_ENVIRONMENTS_ADDED(900505, "No Gateway Environments Available", 400, "No gateway environments " + + "available for the API : %s."), + GATEWAY_ENVIRONMENT_NOT_FOUND(900506, "Gateway Environment not found", 404, + "Gateway Environment with %s not found"), + EXISTING_GATEWAY_ENVIRONMENT_FOUND(900507, "Gateway Environment already exists", 400, + "A Gateway Environment with %s already exists"), + READONLY_GATEWAY_ENVIRONMENT(900508, "Gateway Environment is read only", 400, + "A Gateway Environment with %s is read only"), + GATEWAY_ENVIRONMENT_DUPLICATE_VHOST_FOUND(900509, "Gateway Environment with duplicate virtual hosts", + 400, "A Gateway Environment cannot exists with duplicate virtual hosts"), + READONLY_GATEWAY_ENVIRONMENT_NAME(900510, "Names of Gateway Environment cannot be changed", + 400, "Name of the gateway is read only"), + GATEWAY_ENVIRONMENT_VHOST_NOT_PROVIDED(900511, "Gateway Environment virtual hosts name not provided", + 400, "Gateway Environment VHOST name not provided"), + + FAILED_GET_ENVIRONMENT_LIST_OF_VHOST(900512, "Failed to get gateway environments list of VHost", + 500, "Failed to get gateway environments list of VHost"), + FAILED_GET_ENVIRONMENT_LIST_OF_TENANT_DOMAIN(900513, "Failed to get Environments in tenant domain", + 500, "Failed to get Environments in tenant domain: %s"), + FAILED_GET_ENVIRONMENT_FOR_TENANT_DOMAIN(900514, "Failed to get Environment in tenant domain: %s", + 500, "Failed to get Environment in tenant domain: %s"), + TENANT_NOT_FOUND(900515, "Tenant does not exist", 404, + "Tenant does not exist"), + INVALID_TENANT_STATE(900516, "Invalid tenant state", 400, + "Invalid tenant state provided"), + FAILED_GET_ENVIRONMENT_SPECIFIC_PROPERTIES(900517, + "Error occurred when getting environment specific api properties", 500, + "Error occurred when getting environment specific api properties"), + VHOST_FOR_ENVIRONMENT_NOT_FOUND(900518, "VHosts not found for the environment: %s", 404, + "VHosts not found for the environment: %s"), + + // Workflow related codes + WORKFLOW_EXCEPTION(900550, "Workflow error", 500, + "Error occurred while executing workflow task"), + WORKFLOW_NOT_FOUND(900551, "Workflow error", 404, + "Workflow entry cannot be found for the given reference id"), + WORKFLOW_ALREADY_COMPLETED(900552, "Workflow error", 400, + "Workflow is already completed"), + WORKFLOW_PENDING(900553, "Workflow exception", 409, + "Pending workflow task exists for the seleted API"), + WORKFLOW_INVALID_WFTYPE(900554, "Workflow error", 500, "Invalid workflow type specified"), + WORKFLOW_INV_STORE_WFTYPE(900555, "Workflow error", 500, "Invalid workflow type for store workflows"), + WORKFLOW_STATE_MISSING(900556, "Workflow error", 400, "Workflow status is not defined"), + WORKFLOW_NO_PENDING_TASK(900557, "Workflow error", 412, + "Requested resource does not have a pending workflow task"), + WORKFLOW_REJCECTED(900558, "Workflow error", 403, "Requested action is rejected"), + INCOMPATIBLE_WORKFLOW_REQUEST_FOR_PUBLISHER(900559, "Incompatible workflow request", 400, + "Incompatible workflow " + + "request received by publisher"), + INCOMPATIBLE_WORKFLOW_REQUEST_FOR_STORE(900560, "Incompatible workflow request", 400, "Incompatible workflow " + + "request received by store"), + WORKFLOW_RETRIEVE_EXCEPTION(900561, "Workflow retrieval error", 400, "Provided parameter is not valid"), + + // Auth related codes + ROLES_CANNOT_BE_EMPTY(900600, "Role list cannot be empty", 400, "Role list cannot be empty"), + ROLES_CANNOT_BE_NULL(900601, "Role list cannot be null", 400, "Role list cannot be null"), + UNSUPPORTED_ROLE(900602, "Non existing roles cannot be added to an API", 400, + "Non existing roles cannot be added to an API"), + USER_DOES_NOT_EXIST(900603, "User does not exist in the system", 404, "User does not exist in the system"), + USER_CREATION_FAILED(900604, "User creation failed", 500, "User creation failed"), + IDP_INITIALIZATION_FAILED(900605, "Identity Provider initialization failed", 500, + "Identity provider initialization failed"), + KEY_MANAGER_INITIALIZATION_FAILED(900606, "Key Manager initialization failed", 500, + "Key Manager initialization failed", true), + ROLE_DOES_NOT_EXIST(900607, "Role does not exist in the system", 404, "Role does not exist in the system"), + MULTIPLE_ROLES_EXIST(900608, "Multiple roles with the same display name exist in the system", 500, "Multiple " + + "roles with the same display name exist in the system"), + MULTIPLE_USERS_EXIST(900609, "Multiple users with the same username exist in the system", 500, "Multiple " + + "users with the same username exist in the system"), + INVALID_USER_ROLES(900610, "Invalid user roles found", 400, "Invalid user roles found"), + IDP_ADDING_FAILED(900611, "Unable to add the identity provider", 400, + "Error while adding the identity provider"), + IDP_RETRIEVAL_FAILED(900612, "Unable to retrieve the identity provider", 400, "Error while retrieving the " + + "identity provider details"), + IDP_DELETION_FAILED(900613, "Unable to delete the identity provider", 400, "Error while deleting the " + + "identity provider"), + INVALID_IDP_TYPE(900614, "Unsupported identity provider type", 400, "Invalid identity provider type. %s"), + USERSTORE_INITIALIZATION_FAILED(900615, "Unable to get the user store manager", 500, + "Error while getting the user store manager from the realm"), + ANON_USER_ACTION(900616, "Operation not permitted", 401, + "Attempt to execute privileged operation as the anonymous user"), + ROLE_ID_EMPTY(900617, "Role Id cannot be empty", 400, + "Role Id cannot be empty"), + + // Labels related codes + LABEL_INFORMATION_CANNOT_BE_NULL(900650, "Label information cannot be null", 400, + "Label information cannot be " + + "null"), + LABEL_EXCEPTION(900651, "Label Error", 500, "Error occurred while retrieving label information"), + LABEL_NOT_FOUND(900652, "Label Not Found", 404, "Label with specified name cannot be found."), + LABEL_NOT_FOUND_IN_API(900653, "Label Not Found In API", 404, "Label with specified name" + + " cannot be found in the API."), + LABEL_ADDING_FAILED(900654, "Label Error", 500, "Error occurred while trying to add label"), + LABEL_UPDATE_FAILED(900655, "Label Error", 500, "Error occurred while trying to update label"), + LABEL_DELETION_FAILED(900656, "Label Error", 500, "Error occurred while trying to delete label"), + + // WSDL related codes + INVALID_WSDL_URL_EXCEPTION(900675, "Invalid WSDL", 400, "Invalid WSDL URL"), + CANNOT_PROCESS_WSDL_CONTENT(900676, "Invalid WSDL", 400, "Provided WSDL content cannot be processed"), + INTERNAL_WSDL_EXCEPTION(900677, "Internal WSDL error", 500, "Internal error while processing WSDL"), + UNSUPPORTED_WSDL_EXTENSIBILITY_ELEMENT(900678, "Invalid WSDL", 400, "WSDL extensibility element not supported"), + ERROR_WHILE_INITIALIZING_WSDL_FACTORY(900679, "Internal WSDL error", 500, + "Error while initializing WSDL factory"), + ERROR_WHILE_CREATING_WSDL_ARCHIVE(900680, "Internal WSDL error", 500, "Error while creating WSDL archive"), + NO_WSDL_FOUND_IN_WSDL_ARCHIVE(900681, "Invalid WSDL Archive", 400, + "No valid WSDLs found in the provided WSDL archive"), + CONTENT_NOT_RECOGNIZED_AS_WSDL(900682, "Invalid WSDL Content", 400, + "Provided content is not recognized as a WSDL"), + URL_NOT_RECOGNIZED_AS_WSDL(900683, "Invalid WSDL URL", 400, "Provided URL is not recognized as a WSDL"), + NO_WSDL_AVAILABLE_FOR_API(900684, "WSDL Not Found", 404, "No WSDL Available for the API %s:%s"), + CORRUPTED_STORED_WSDL(900685, "Corrupted Stored WSDL", 500, "The WSDL of the API %s is corrupted."), + UNSUPPORTED_WSDL_FILE_EXTENSION(900686, "Unsupported WSDL File Extension", 400, + "Unsupported extension. Only supported extensions are .wsdl and .zip"), + API_NOT_SOAPTOREST(900687, "Provided API is not a SOAP to REST converted API", 400, + "Provided API is not a SOAP to REST converted API"), + ERROR_ADDING_WSDL_TO_API(900687, "Error while adding WSDL to API: %s", 500, + "Error while saving WSDL"), + + // OpenAPI/Swagger related codes [900750 900???) + MALFORMED_OPENAPI_DEFINITON(900758, "Malformed OpenAPI Definition", 400, + "The provided OpenAPI definition is not parsable as a valid JSON or YAML."), + UNRECOGNIZED_OPENAPI_DEFINITON(900759, "Unrecognized OpenAPI Definition", 400, + "The definition is parsable but cannot be identified as an OpenAPI definition."), + INVALID_SWAGGER_VERSION(900760, "Invalid Swagger Definition", 400, + "Unsupported swagger version provided. Please add with swagger version 2.0."), + INVALID_SWAGGER_PARAMS(900751, "Invalid Swagger Definition", 400, + "Swagger contains invalid parameters. Please add valid swagger definition."), + INVALID_OPENAPI_VERSION(900752, "Invalid OpenAPI Definition", 400, + "Unsupported OpenAPI version provided. Please add with OpenAPI version 3.0.0."), + INVALID_OPENAPI_NO_INFO_PATH(900753, "Invalid OpenAPI Definition", 400, + "Required property 'info' or 'paths' are not provided."), + OPENAPI_PARSE_EXCEPTION(900754, "Error while parsing OpenAPI definition", 400, + "Error while parsing OpenAPI definition"), + OPENAPI_PARSE_EXCEPTION_WITH_CUSTOM_MESSAGE(OPENAPI_PARSE_EXCEPTION.getErrorCode(), + OPENAPI_PARSE_EXCEPTION.getErrorMessage(), OPENAPI_PARSE_EXCEPTION.getHttpStatusCode(), "'%s'"), + OPENAPI_NOT_FOUND(900755, "OpenAPI definition not found", 404, "OpenAPI definition not found"), + OPENAPI_URL_MALFORMED(900756, "OpenAPI definition retrieval from URL failed", 400, + "Exception occurred while retrieving the OpenAPI definition from URL"), + OPENAPI_URL_NO_200(900757, "OpenAPI definition retrieval from URL failed", 400, + "Response didn't return a 200 OK status"), + INVALID_OAS2_FOUND(900761, "Invalid OpenAPI V2 definition found", 400, "Invalid OpenAPI V2 definition found"), + INVALID_OAS3_FOUND(900762, "Invalid OpenAPI V3 definition found", 400, "Invalid OpenAPI V3 definition found"), + NO_RESOURCES_FOUND(900763, "No resources found", 404, "API must have at least one resource defined"), + ERROR_REMOVING_EXAMPLES(900764, "Internal Error While Processing Swagger Definition", 500, + "Couldn't remove one or more examples from the swagger definition"), + MOCK_HTTP_METHOD_MISSING(900765, "Could not find HTTP methods", 400, + "Cannot find the HTTP method for the API Resource Mediation Policy"), + SWAGGER_ARCHIVE_MISSING(900766, "Could not find an archive in the given ZIP file", 500, + "Could not find an archive in the given ZIP file"), + + // AsyncApi related error codes + ASYNCAPI_URL_MALFORMED(900756, "AsyncAPI specification retrieval from URL failed", 400, + "Exception occurred while retrieving the AsyncAPI Specification from URL"), + ASYNCAPI_URL_NO_200(900757, "AsyncAPI specification retrieval from URL failed", 400, + "Response didn't return a 200 OK status"), + + ERROR_READING_ASYNCAPI_SPECIFICATION(900765, "AsyncAPI specification read error", 500, + "Exception occurred while reading the AsyncAPI Specification file"), + + // REST API related codes + PARAMETER_NOT_PROVIDED(900700, "Parameter value missing", 400, + "Some of the mandatory parameter values were missing"), + LOCATION_HEADER_INCORRECT(900701, "Error while obtaining URI for Location header", 500, + "Error occurred while obtaining URI for Location header"), + LAST_UPDATED_TIME_RETRIEVAL_ERROR(900702, "Error while retrieving last access time for the resource", 500, + "Error while retrieving last access time for the resource"), + INVALID_DATE_TIME_STAMP(900703, "Invalid timestamp value", 400, "Timestamp should be in ISO8601 format"), + LENGTH_EXCEEDS(900704, "Character length exceeds the allowable limit", 400, + "One of the provided input character length exceeds the allowable limit."), + BLANK_PROPERTY_VALUE(900705, "Blank value for required property", 400, + "%s property value of payload cannot be blank"), + CONTAIN_SPECIAL_CHARACTERS(900706, "contain invalid characters", 400, + "%s property value of payload cannot contain invalid characters"), + INVALID_SORT_CRITERIA(900707, "Invalid sort criteria", 400, "Sort criteria contain a non-allowable value"), + INVALID_PARAMETERS_PROVIDED(900708, "Invalid parameter(s) provided", 400, + "Bad Request. Mandatory parameters are invalid/missing"), + + INVALID_PARAMETERS_PROVIDED_WITH_MESSAGE(900708, "'%s'", 400, "Bad Request. Parameters are invalid/missing"), + + // GraphQL API related codes + API_NOT_GRAPHQL(900800, "This API is not a GraphQL API", 400, "This API is not a GraphQL API"), + GRAPHQL_SCHEMA_CANNOT_BE_NULL(900801, "GraphQL Schema cannot be empty or nul", 400, + "GraphQL Schema cannot be empty or null"), + UNSUPPORTED_GRAPHQL_FILE_EXTENSION(900802, "Unsupported GraphQL Schema File Extension", 400, + "Unsupported extension. Only supported extensions are .graphql, .txt and .sdl"), + + // GRPC API related codes + GRPC_PROTO_DEFINTION_CANNOT_BE_NULL(900803, "gRPC Proto Definition cannot be empty or null", 400, + "gRPC Proto Definition cannot be empty or null"), + API_NOT_GRPC(900800, "This API is not a GRPC API", 400, "This API is not a GRPC API"), + PROTO_DEFINITION_PARSE_EXCEPTION(900754, "Error while parsing the proto definition", 400, + "Error while parsing the proto definition"), + + // Oauth related codes + AUTH_GENERAL_ERROR(900900, "Authorization Error", 403, " Error in authorization"), + INVALID_CREDENTIALS(900901, "Invalid Credentials", 401, " Invalid username or password"), + MISSING_CREDENTIALS(900902, "Missing Credentials", 401, " Please provide an active access token to proceed"), + ACCESS_TOKEN_EXPIRED(900903, "Invalid Credentials", 401, " Access token is expired."), + ACCESS_TOKEN_INACTIVE(900904, "Access Token Error", 401, " Access token is inactive."), + USER_NOT_AUTHENTICATED(900905, "User is not Authenticated", 401, " User is not authenticated."), + ACCESS_TOKEN_INVALID(900906, "Invalid Credentials", 401, " Access token is invalid."), + + INVALID_SCOPE(900910, "Invalid Scope", 403, " You are not authorized to access the resource."), + INVALID_AUTHORIZATION_HEADER(900911, "Invalid Authorization header", 401, + " Please provide the Authorization : Bearer <> token to proceed."), + MALFORMED_AUTHORIZATION_HEADER_OAUTH(900912, "Malformed Authorization Header", 400, + "Please provide the Authorization : Bearer <> token to proceed."), + MALFORMED_AUTHORIZATION_HEADER_BASIC(900913, "Malformed Authorization Header", 400, + "Please provide the Authorization : Basic <> token to proceed."), + INVALID_PERMISSION(900915, "Invalid Permission", 403, " You are not authorized to access the '%s'."), + OPENID_CONFIG(900916, "Missing OpenID configurations", 500, "Error in fetching Open ID configuration"), + OAUTH2_APP_CREATION_FAILED(900950, "Key Management Error", 500, + "Error while creating the consumer application."), + OAUTH2_APP_ALREADY_EXISTS(900951, "Key Management Error", 409, "OAuth2 application already created."), + OAUTH2_APP_DELETION_FAILED(900952, "Key Management Error", 500, + "Error while deleting the consumer application."), + OAUTH2_APP_UPDATE_FAILED(900953, "Key Management Error", 500, "Error while updating the consumer application."), + OAUTH2_APP_RETRIEVAL_FAILED(900954, "Key Management Error", 500, + "Error while retrieving the consumer application."), + APPLICATION_TOKEN_GENERATION_FAILED(900957, "Keymanagement Error", 500, + " Error while generating the application" + + "access token."), + APPLICATION_CONSUMER_KEY_NOT_FOUND(900958, "Keymanagement Error", 403, + "Requested consumer key with application '%s' not found"), + UNSUPPORTED_THROTTLE_LIMIT_TYPE(900960, "Throttle Policy Error", 400, "Throttle Limit type is not supported"), + POLICY_NOT_FOUND(900961, "Policy Not found", 404, "Failed to retrieve Policy Definition"), + OAUTH2_APP_MAP_FAILED(900962, "Key Management Error", 500, + "Error while mapping an existing consumer application."), + TOKEN_INTROSPECTION_FAILED(900963, "Key Management Error", 500, "Error while introspecting the access token."), + ACCESS_TOKEN_GENERATION_FAILED(900964, "Key Management Error", 500, + "Error while generating a new access token."), + INVALID_TOKEN_REQUEST(900965, "Key Management Error", 400, "Invalid access token request."), + ACCESS_TOKEN_REVOKE_FAILED(900966, "Key Management Error", 500, "Error while revoking the '%s'."), + + INTERNAL_ERROR(900967, "General Error", 500, "Server Error Occurred"), + INTERNAL_ERROR_WITH_SPECIFIC_MESSAGE(903006, "%s", 500, "Server Error Occurred"), + INTERNAL_ERROR_WITH_SPECIFIC_DESC(903007, "Internal Server Error", 500, "'%s'"), + + POLICY_LEVEL_NOT_SUPPORTED(900968, "Throttle Policy level invalid", 400, + "Specified Throttle policy level is not " + + "valid"), + POLICY_LEVEL_EMPTY(900973, "Policy Level can not be empty", 400, + "Throttle policy level can not be empty"), + POLICY_LEVEL_NOT_FOUND(900974, "Policy Level %s not found", 404, + "Throttle policy level %s not found"), + UNSUPPORTED_POLICY_TYPE(901001, "Policy type error", 400, "Unsupported policy type"), + UNSUPPORTED_TIER_TYPE(901002, "Policy tier error", 400, "Unsupported policy tier"), + INVALID_THROTTLE_TIER(901003, "Invalid throttle tier", 400, "Invalid x-throttling tier"), + + THROTTLE_TIER_NOT_FOUND(901004, "Throttle tier", 400, "throttling tier cannot be found"), + + THROTTLING_POLICY_NOT_FOUND(903005, "Throttling Policy Not Found", 404, + "Requested throttling policy with name '%s' and type '%s' not found"), + INVALID_APPLICATION_ADDITIONAL_PROPERTIES(900970, "Invalid application additional properties", 400, + "Invalid additional properties. %s"), + ERROR_RETRIEVE_APPLICATION_DETAILS(900972, "Error while obtaining details of the Application : %s", + 500, "Error while obtaining details of the Application : %s"), + JWT_PARSING_FAILED(900986, "Key Management Error", 500, "Error while parsing JWT. Invalid Jwt."), + TOKEN_SCOPES_NOT_SET( + 900987, "The token information has not been correctly set internally", 400, + "The token information has not been correctly set internally"), + MUTUAL_SSL_NOT_SUPPORTED( + 900988, "Mutual SSL based authentication is not supported in this server", 400, + "Cannot add client certificates to this server"), + THROTTLING_POLICY_CANNOT_BE_NULL(900989, + "Throttling Policy cannot be empty or null", 400, "Throttling Policy cannot be empty or null"), + ALREADY_ASSIGNED_ADVANCED_POLICY_DELETE_ERROR(900971, "Cannot delete the advanced throttling policy", 403, + "Cannot delete the advanced policy with the name %s because it is already assigned to an API/Resource"), + + // Throttle related codes + ADVANCED_POLICY_EXISTS(902900, "Advanced policy already exists", 409, + "Advanced Policy with name '%s' already exists"), + APPLICATION_POLICY_EXISTS(902901, "Application policy already exists", 409, + "Application Policy with name '%s' already exists"), + SUBSCRIPTION_POLICY_EXISTS(902902, "Subscription policy already exists", 409, + "Subscription Policy with name '%s' already exists"), + GLOBAL_POLICY_EXISTS(902903, "Policy already exists", 409, "Policy already exists"), + ADVANCED_POLICY_ADD_FAILED(902904, "Error while adding an Advanced level policy: '%s'", 500, "'%s'"), + ADVANCED_POLICY_GET_FAILED(902905, "Error while retrieving Advanced level policy : '%s'", 500, "'%s'"), + ADVANCED_POLICY_UPDATE_FAILED(902906, "Error while updating Advanced level policy : '%s'", 500, "'%s'"), + SUBSCRIPTION_POLICY_GET_ALL_FAILED(902907, "Error while retrieving Subscription level policies", 500, + "Server Error Occurred"), + SUBSCRIPTION_POLICY_ADD_FAILED(902908, "Error while adding Subscription level policies", 500, + "Server Error Occurred"), + SUBSCRIPTION_POLICY_GET_FAILED(902909, "Error while retrieving Subscription level policy : '%s'", 500, + "Server Error Occurred"), + BAD_POLICY_OBJECT(902010, "Policy object doesn't contain mandatory parameters", 500, + "Policy object doesn't contain mandatory parameters."), + SUBSCRIPTION_POLICY_UPDATE_FAILED(902911, "Error while updating Subscription level policy : '%s'", 500, + "Server Error Occurred"), + CUSTOM_RULE_EXISTS(902914, "Custom rule already exists", 409, "Custom rule with name %s already exists"), + INVALID_IP_ADDRESS_FORMAT(902915, "Invalid IP address format", 400, "Invalid IP address format"), + BLOCK_CONDITION_ALREADY_EXISTS(902916, "Block condition already exists", 409, + "A block condition with type: %s, value: %s already exists"), + ALREADY_ASSIGNED_APP_POLICY_DELETE_ERROR(902912, "Cannot delete the application throttling policy", 409, + "Policy %s is already attached to an Application"), + ALREADY_ASSIGNED_SUB_POLICY_DELETE_ERROR(902913, "Cannot delete the subscription throttling policy", 409, + "Policy %s already has subscriptions"), + + THROTTLE_TEMPLATE_EXCEPTION(900969, "Policy Generating Error", 500, + " Error while generate policy configuration"), + ENDPOINT_CONFIG_NOT_FOUND(90070, "Endpoint Config Not found", 404, "Error while retrieving Endpoint " + + "Configuration"), + UNSUPPORTED_THROTTLE_CONDITION_TYPE(900975, "Throttle Condition Error", 400, "Throttle Condition type is not " + + "supported"), + INVALID_DOCUMENT_CONTENT_DATA(900976, "Invalid document content data provided", 400, + "Mismatch between provided " + + "document content data and Document Source Type given"), + BLOCK_CONDITION_UNSUPPORTED_API_CONTEXT(900977, "Block Condition Error", 400, "API Context does not exist"), + BLOCK_CONDITION_UNSUPPORTED_APP_ID_NAME(900978, "Block Condition Error", 400, + "Application ID or Name does not " + + "exist"), + SYSTEM_APP_NOT_FOUND(900980, "System Application not found", 409, "System Application not found"), + + SHARED_SCOPE_NOT_FOUND(900981, "Shared Scope not found", 404, + "Requested Shared Scope ID %s could not be found"), + SHARED_SCOPE_ID_NOT_SPECIFIED(900982, "Shared Scope ID not specified", 400, + "Shared Scope ID not specified"), + SHARED_SCOPE_NAME_NOT_SPECIFIED(900983, "Shared Scope name not specified", 400, + "Shared Scope name not specified"), + SCOPE_ALREADY_REGISTERED(900984, "Scope already exists", 409, "Scope %s already exists"), + SHARED_SCOPE_ALREADY_ATTACHED(900985, "Shared Scope already attached", 409, + "Shared Scope %s is already used by one or more APIs"), + SCOPE_VALIDATION_FAILED(900986, "Scope validation failed", 412, "Scope validation failed"), + SHARED_SCOPE_DISPLAY_NAME_NOT_SPECIFIED(900987, "Shared Scope display name not specified", 400, + "Shared Scope display name not specified"), + SCOPE_ALREADY_ASSIGNED(900988, "Scope already assigned locally by another API", 400, + "Scope already assigned locally by another API"), + TOKEN_VALIDATION_FAILED(900989, "Validation failed for the given token", 500, + "Validation failed for the given token"), + ERROR_CHECKING_SCOPE_NAME(901000, "Error while checking scope name", 500, + "Error occurred while checking scope name %s"), + ERROR_CREATING_URI_FOR_SHARED_SCOPE(901004, "Error while creating shared scope URI", 500, + "Error while creating URI for shared scope: %s"), + FAILED_RETRIEVE_SHARED_SCOPE(901005, "Error while retrieving shared scope", 500, + "Error while retrieving shared scope"), + FAILED_CHECKING_SCOPE_KEY_AVAILABILITY(901006, "Failed to check scope key availability for: %s", + 500, "Error while checking scope key availability"), + + // Dedicated container based gateway related Codes + NO_RESOURCE_LOADED_FROM_DEFINITION(900990, "Container based resource Not Found", 404, + "No resource loaded from " + + "definition provided"), + LOADED_RESOURCE_DEFINITION_IS_NOT_VALID(900991, "Loaded resource is not valid", 400, "The loaded resource " + + "definition is not a valid"), + TEMPLATE_LOAD_EXCEPTION(900992, "Error in loading the template file by client as an InputStream", 500, + " Error " + + "in loading the FileInputStream by client"), + CONTAINER_GATEWAY_REMOVAL_FAILED(900993, "Cannot complete removing dedicated container based Gateway", 404, + "Error in deleting the dedicated container based Gateway"), + ERROR_INITIALIZING_DEDICATED_CONTAINER_BASED_GATEWAY(900994, "Error initializing dedicated container based" + + " gateway", 500, "Error initializing dedicated container based gateway"), + DEDICATED_CONTAINER_GATEWAY_CREATION_FAILED(900995, "Error while creating dedicated container based gateway", + 500, + "Error while creating dedicated container based gateway"), + ERROR_WHILE_UPDATING_DEDICATED_CONTAINER_BASED_GATEWAY(900996, + "Error while updating dedicated container based" + + " gateway", + 500, "Error while updating dedicated container based gateway"), + ERROR_WHILE_RETRIEVING_DEDICATED_CONTAINER_BASED_GATEWAY(900997, "Error while retrieving dedicated container " + + "based gateway", 500, "Error while retrieving dedicated container based gateway"), + INVALID_DEDICATED_CONTAINER_BASED_GATEWAY_LABEL(900998, "Invalid gateway label is provided", 400, + "Invalid gateway label is provided"), + DEDICATED_GATEWAY_DETAILS_NOT_FOUND(900999, "Dedicated gateway details not found for the API", 404, + "Dedicated " + + "gateway details not found for the API"), + + // Comments related Codes + NEED_COMMENT_MODERATOR_PERMISSION(901100, "Comment moderator permission needed", 403, + "This user is not a comment moderator"), + COULD_NOT_UPDATE_COMMENT(901101, "Error has occurred. Could not update the Comment", 500, + "Error has occurred. Could not update the Comment"), + COMMENT_NOT_FOUND(901102, "Comment not found", 404, "Failed to retrieve comment"), + COMMENT_LENGTH_EXCEEDED(901103, "Comment length exceeds max limit", 400, + "Comment length exceeds allowed maximum " + + "number of characters"), + COMMENT_NO_PERMISSION(901104, "Insufficient permission", 403, + "User '%s' doesn't have permission to access the comment with id '%s'"), + COMMENT_CANNOT_RETRIEVE(901105, "Failed to get '%s'", 500, "Failed to get '%s"), + + COMMENT_CANNOT_DELETE(901106, "Failed to delete the Comment", 500, "Failed to delete the Comment of '%s'"), + + NEED_ADMIN_PERMISSION(901100, "Admin permission needed", 403, + "This user is not an admin"), + + // External Stores related codes + EXTERNAL_STORE_ID_NOT_FOUND(901200, "External Store Not Found", 404, + "Error while publishing to external stores. " + + "External Store Not Found"), + EXTERNAL_STORE_CLASS_NOT_FOUND(901201, + ExceptionConstants.EXTERNAL_STORE_ERROR_MSG, 404, + "One or more classes defined in APIConstants.EXTERNAL_API_STORE_CLASS_NAME cannot be found"), + EXTERNAL_STORE_CLASS_NOT_LOADED(901202, + ExceptionConstants.EXTERNAL_STORE_ERROR_MSG, 500, + "One or more classes defined in APIConstants.EXTERNAL_API_STORE_CLASS_NAME cannot be loaded"), + EXTERNAL_STORE_CLASS_NOT_ACCESSIBLE(901203, + ExceptionConstants.EXTERNAL_STORE_ERROR_MSG, 500, + "One or more classes defined in APIConstants.EXTERNAL_API_STORE_CLASS_NAME cannot be accessed"), + ERROR_RETRIEVE_EXTERNAL_STORE_CONFIG(901204, "External Store Config Retrieve Error", 500, + "Error while retrieving External Stores Configuration from registry"), + MALFORMED_XML_IN_EXTERNAL_STORE_CONFIG(901205, "Malformed XML in External Stores Configuration", + 500, "Malformed XML found in the External Stores Configuration resource"), + + // Tenant related + INVALID_TENANT(901300, "Tenant Not Found", 404, "Tenant Not Found"), + CONFIG_NOT_FOUND(901301, "Config not found", 404, "Config not found in tenant-config"), + ERROR_GETTING_CUSTOM_URLS(901302, "Failed to get custom url info", 500, + "Error while retrieving custom url info for tenant : '%s'"), + // Key Manager Related + INVALID_KEY_MANAGERS_VALUE(901350, "Key Managers value need to be an array", 400, + "Invalid Key Managers value"), + INVALID_KEY_MANAGER_TYPE(901400, "Key Manager Type not configured", 400, "Key Manager Type not configured"), + REQUIRED_KEY_MANAGER_CONFIGURATION_MISSING(901401, "Required Key Manager configuration missing", 400, + "Missing " + + "required configuration"), + KEY_MANAGER_ALREADY_EXIST(901402, "Key Manager Already Exists", 409, "Key Manager Already Exists"), + KEY_MANAGER_NOT_REGISTERED(901403, "Key Manager not Registered", 400, "Key Manager not Registered"), + KEY_MANAGER_NOT_FOUND(901411, "Key Manager not Found", 404, "Key Manager not found"), + KEY_MANAGER_NAME_EMPTY(901404, + "Key Manager name cannot be empty", 400, "Key Manager name cannot be empty"), + KEY_MANAGER_NOT_SUPPORT_OAUTH_APP_CREATION(901405, "Key Manager doesn't support generating OAuth applications", + 400, + "Key Manager doesn't support generating OAuth applications"), + KEY_MANAGER_NOT_SUPPORTED_TOKEN_GENERATION(901405, "Key Manager doesn't support token generation", 400, + "Key Manager doesn't support token generation"), + KEY_MANAGER_NOT_ENABLED(901406, "Key Manager is not enabled in the system", 400, + "Key Manager is not enabled in the system"), + KEY_MANAGER_MISSING_REQUIRED_PROPERTIES_IN_APPLICATION(901407, "Required application properties are missing", + 400, + "Required application properties are missing"), + APPLICATION_ALREADY_REGISTERED(901408, "Application already Registered", 409, "Application already Registered"), + KEY_MAPPING_ALREADY_EXIST(901409, "Key Mappings already exists", 409, "Key Mappings already exists"), + TENANT_MISMATCH(901410, "Tenant mismatch", 400, "Tenant mismatch"), + INVALID_APPLICATION_PROPERTIES(901411, "Invalid additional properties", 400, + "Invalid additional properties given for application"), + DECRYPT_CONFIG_ERROR(901412, "Error while decrypting key manager configuration", 500, + "Unable to decrypt the value"), + + // Scope related + SCOPE_NOT_FOUND_FOR_USER(901500, "Scope does not belong to this user", 404, "Scope not found"), + SCOPE_NOT_FOUND(901501, "Scope Not Found", 404, "Scope does not exist"), + USER_NOT_FOUND(901502, "User Not Found", 404, "User does not exist"), + DEFINITION_EXCEPTION(901503, "Internal server error.", 500, + " Error occurred while retrieving swagger definition"), + + // Analytics related codes + ANALYTICS_NOT_ENABLED(901600, "%s not accessible", 404, + "Analytics should be enabled to access %s"), + UNSUPPORTED_ALERT_TYPE(901601, "Unsupported alert type", 400, "Unsupported alert type: '%s' is provided"), + MALFORMED_SP_URL(901602, "Malformed URL", 500, "Error while parsing the stream processor url"), + ERROR_INVOKING_SP_REST_API(901603, "Error while invoking steam processor REST API", 500, "'%s'"), + ALREADY_SUBSCRIBED_FOR_BOT_ALERTS(901604, "Subscription already exists", 409, + "Email: '%s' has already been subscribed for bot detection alerts"), + BOT_DETECTION_SUBSCRIPTION_NOT_FOUND(901605, "Subscription does not exist", 404, + "Bot detection alert subscription with uuid: '%s' uuid does not exist"), + + // Password change related + PASSWORD_CHANGE_DISABLED(901450, "Password change disabled", 400, + "Password change operation is disabled in the system"), + + CURRENT_PASSWORD_INCORRECT(901451, "Current password incorrect", 400, + "The current password entered is incorrect"), + + PASSWORD_PATTERN_INVALID(901452, "Password pattern invalid", 400, + "Password entered is invalid since it doesn't comply with the pattern/policy configured"), + + // Tenant theme related codes + TENANT_THEME_IMPORT_FAILED(901700, "Failed to import tenant theme of tenant %s", 500, + "%s"), + TENANT_THEME_EXPORT_FAILED(901701, "Failed to export tenant theme of tenant %s", 500, + "%s"), + TENANT_THEME_IMPORT_NOT_ALLOWED(901702, "Super Tenant not allowed to import tenant theme", 400, + "Super Tenant %s is not allowed to import a tenant theme"), + TENANT_THEME_NOT_FOUND(901703, "Tenant theme does not exist", 404, + "Tenant theme for tenant: '%s' does not exist"), + + INVALID_API_IDENTIFIER(900851, "Provided API identifier (%s) is invalid", 400, + "Provided API identifier (%s) is invalid"), + API_NAME_OR_VERSION_NOT_NULL(900852, "name or version couldn't be null", 400, + "name or version couldn't be null"), + INVALID_CONFIGURATION_ID(900853, "The configuration id validation failed. Should be " + + "{apiName}#{apiVersion}#{tenantDomain}", 400, + "The configuration id validation failed. Should be " + + "{apiName}#{apiVersion}#{tenantDomain}"), + INVALID_API_NAME(900854, "Invalid API Name", 400, "Invalid API Name"), + ALIAS_CANNOT_BE_EMPTY(900855, "The alias cannot be empty", 400, "The alias cannot be empty"), + + // API import/export related codes + ERROR_READING_META_DATA(900907, "Error while reading meta information from the definition", 400, + "Error while reading meta information from the definition"), + ERROR_READING_PARAMS_FILE(900908, "Error while reading meta information from the params file", 400, + "%s"), + ERROR_FETCHING_DEFINITION_FILE(900909, "Cannot find the definition file of the project", 400, + "Cannot find the yaml/json file with the project definition."), + NO_API_ARTIFACT_FOUND(900910, "No Api artifacts found for given criteria", 404, + "No Api artifacts found for given criteria"), + ERROR_UPLOADING_THUMBNAIL(900914, + "Error while updating thumbnail of API/API Product", 500, + "Error while updating thumbnail of API/API Product: %s-%s"), + APICTL_OPENAPI_PARSE_EXCEPTION( + OPENAPI_PARSE_EXCEPTION.getErrorCode(), OPENAPI_PARSE_EXCEPTION.getErrorMessage(), + OPENAPI_PARSE_EXCEPTION.getHttpStatusCode(), "%s"), + GATEWAY_TYPE_NOT_FOUND(900903, "Gateway type not found", 404, + "Gateway type not found available Gateway types : " + "%s"), + + SERVICE_IMPORT_FAILED_WITHOUT_OVERWRITE(900910, "Service import is failed", 412, + "Cannot update existing services " + + "when overwrite is false"), + MISSING_PROTOCOL_IN_ASYNC_API_DEFINITION(900911, "Missing protocol in Async API Definition", 400, + "Missing protocol in Async API Definition"), + UNSUPPORTED_PROTOCOL_SPECIFIED_IN_ASYNC_API_DEFINITION(900912, "Unsupported protocol specified in Async API " + + "Definition", 400, "Unsupported protocol specified in Async API Definition"), + API_CREATION_NOT_SUPPORTED_FOR_ASYNC_TYPE_APIS(900915, + "API Creation is supported only for WebSocket, WebSub and SSE APIs", 400, + "API Creation is supported only for WebSocket, WebSub and SSE APIs"), + LOGGING_API_NOT_FOUND(901400, "Requested Resource Not Found", 404, "Request API Not Found for context: %s"), + LOGGING_API_INCORRECT_LOG_LEVEL(901401, "Bad Request", 400, + "Log level should be either OFF, BASIC, STANDARD or FULL"), + LOGGING_API_MISSING_DATA(901402, "Missing data", 400, "API context or log level is missing"), + + // Service Catalog related error codes + SERVICE_VERSION_NOT_FOUND(901900, "Cannot find the service version", 404, + "Cannot find a service that matches the given version"), + ERROR_RETRIEVE_SERVICE_INFORMATION(901901, "Error while getting service information", + 500, "Error while executing SQL for getting service information"), + + INVALID_ENDPOINT_CREDENTIALS(902000, "Invalid Endpoint Security credentials", 400, + "Invalid Endpoint Security credentials. %s", false), + INVALID_TENANT_CONFIG(902001, "Invalid tenant-config found", 400, "Invalid tenant-config found with error %s", + false), + + // Operation Policies related error codes + INVALID_OPERATION_POLICY(902005, "%s. Cannot find the selected operation policy", 400, + "Selected operation policy is not found"), + INVALID_OPERATION_POLICY_SPECIFICATION(902006, "Invalid operation policy specification found", 400, + "Invalid operation policy specification. %s", false), + + INVALID_OPERATION_POLICY_PARAMETERS(902007, "Missing required parameters for operation policy specification", + 400, + "Required parameter(s) %s for operation policy specification %s are either missing or empty"), + OPERATION_POLICY_NOT_ALLOWED_IN_THE_APPLIED_FLOW(902008, "Operation policy is not allowed in the applied flow", + 400, + "%s policy is not allowed in response flow"), + MISSING_MANDATORY_POLICY_ATTRIBUTES(902009, "Missing mandatory operation policy attribute", 400, + "Required attributes(s) %s for operation policy specification %s are either missing or empty"), + OPERATION_POLICY_NOT_FOUND(902010, "Operation Policy Not Found", 404, + "Requested operation policy with id '%s' not found"), + OPERATION_POLICY_SPEC_MISMATCH(902011, "Applied policy does not match specification", 400, + "Applied policy for URI template does not match specification"), + + OPERATION_POLICY_ALREADY_EXISTS(903001, "The Operation Policy already exists.", 409, + "An Operation Policy with name '%s' and version '%s' already exists"), + + OPERATION_POLICY_NOT_FOUND_WITH_NAME_AND_VERSION(903004, + "Operation Policy Not Found with given name and version", 404, + "Requested operation policy with name '%s' and version '%s' not found"), + + OPERATION_POLICY_GATEWAY_ERROR(903008, + "Either Synapse or Choreo Gateway Definition files or both should be present", 400, + "Operation Policy cannot be imported due to the missing Gateway files."), + OPERATION_POLICY_USAGE_EXISTS(903009, "Operation policy usages exist", 500, + "Policy usages exist for policy ID '%s'"), + + SUBSCRIPTION_TIER_NOT_ALLOWED(902002, "Subscription Tier is not allowed for user", 403, + "Subscription Tier %s is" + + " not allowed for user %s ", + false), + INVALID_KEY_MANAGER_REQUEST(902003, "Invalid Request sent to Key Manager.", 400, + "Invalid Request sent to Key Manager.Error from Backend : %s", false), + INTERNAL_SERVER_ERROR_FROM_KEY_MANAGER(902004, "Internal Server Error from Key Manager", 500, + "Internal Server Error from Key Manager.Error from Backend : %s", true), + REVISION_ALREADY_DEPLOYED(902005, "Revision deployment state conflicted", 409, + "Revision deployment request conflicted with the current deployment state of the revision %s. Please try again later", + false), + INVALID_API_ID(902006, "Invalid API ID", 404, + "The provided API ID is not found %s", false), + GATEWAY_DOMAIN_MAPPING_RETRIEVE_ERROR(902100, "Error retrieving gateway domain mappings from registry", + 500, "Error while retrieving gateway domain mappings from registry"), + INVALID_GATEWAY_DOMAIN_MAPPING_JSON(902101, "Invalid JSON in gateway tenant domain mappings", + 500, "Invalid JSON found in the gateway tenant domain mappings"), + MALFORMED_GATEWAY_DOMAIN_MAPPING_JSON(902102, "Malformed JSON in gateway tenant domain mappings", + 500, "Malformed JSON found in the gateway tenant domain mappings"), + ERROR_PARSING_TENANT_CONFIG_JSON(902103, "Error occurred while converting to json", + 500, "Error occurred while converting tenantConfig to json"), + FAILED_RETRIEVE_CONFIGURATION_FOR_ORGANIZATION(902150, "Failed to retrieve configuration", + 500, "Failed to retrieve %s Configuration for org: %s"), + INVALID_QUERY(902200, "Failed to retrieve configuration", + 500, "Failed to retrieve %s Configuration for org: %s"), + ERROR_RETRIEVING_CLAIM_VALUES(902300, "Error while retrieving claim values from user store", + 500, "Error while retrieving claim values from user store"), + FAILED_FIND_API_USAGE(902350, "Failed to find API Usage for : %s", 500, + "Failed to find API Usage for : %s"), + BAD_REQUEST_SUBSCRIPTION_ID(902351, "Invalid Subscription ID", 400, + "Invalid Subscription ID"), + FAILED_GET_SUBSCRIPTION_POLICY(902352, "Failed to get subscription policy: %s", 500, + "Failed to retrieve subscription policy: %s data"), + FAILED_GET_API_POLICY(902353, "Failed to get API policy: %s", 500, + "Failed to retrieve API policy: %s data"), + FAILED_GET_APPLICATION_POLICY(902354, "Failed to get application policy: %s", 500, + "Failed to retrieve application policy: %s data"), + + READ_ONLY_ENVIRONMENT_NOT_FOUND(902400, "Configured read only environment not found: %s", + 404, "Configured read only environment not found: %s"), + + // monetization related codes + + INVALID_API_STATE_MONETIZATION(904300, "Invalid API state", 400, "Invalid API state to configure monetization"), + MONETIZATION_STATE_CHANGE_FAILED(904301, "Could not change the monetization state", 500, + "Monetization state change to '%s' failed"), + + MONETIZATION_IMPLEMENTATION_LOADING_FAILED(904302, "Could not load the monetization implementation", 500, + "Failed to load the monetization implementation"), + + // audit related codes + + AUDIT_SEND_FAILED(904200, "Error sending audit data", 500, "Sending audit data failed. Response code: '%s'"), + AUDIT_RETRIEVE_FAILED(904201, "Error retrieving audit data", 500, "Error retrieving audit data"), + + // transport related codes + UNSUPPORTED_TRANSPORT_TYPE(904100, "Unsupported transport type", 400, "Transport type '%s' is not supported"), + + // certificate related error codes + + CERT_NOT_FOUND(904001, "Could not find the certificate", 404, + "'Cannot find the certificate with alias '%s' in the truststore'"), + CERT_BAD_REQUEST(904002, "Bad Request", 400, "'%s"), + GET_CERT_CONTENT(904003, "Error getting the certificate content", 500, "'%s'"), + RETRIEVE_CERT(904004, "Could not retrieve the certificate", 500, "'%s"), + DELETE_CERT(904005, "Could not delete the certificate", 500, + "Error while deleting the certificate for alias '%s'"), + GET_CERT_INFO(904006, "Could not get the certificate information", 500, "'%s"), + UPDATE_CERT(904007, "Could not update the certificate", 500, "'%s'"), + ENCODE_CERT(904008, "Error occurred while encoding the certificate", 500, "'%s"), + INTERNAL_SERVER_CERT(904009, "Internal server error", 500, "'%s'"), + EXPIRED_CERT(904010, "Certificate expired", 400, "'%s'"), + CERT_ALREADY_EXIST(904011, "Certificate alias already exists", 409, + "The alias '%s' already exists in the truststore"), + DECODE_CERT(904012, "Error occurred while decoding the certificate", 500, "'%s'"), + + INVALID_KEY_TYPE(904013, "Bad Request", 400, "Invalid keyType. KeyType should be either PRODUCTION or SANDBOX"), + + ERROR_DELETING_APPLICATION_REGISTRATION(904014, "Can not delete application registration", 400, + "Failed to delete Application registration of : '%s'"), + + ERROR_DELETING_APPLICATION_KEY_MAPPING(904015, "Can not delete application key mapping", 500, + "Failed to delete Application key mapping of : '%s'"), + + ERROR_RETRIEVE_APPLICATION_KEYS(904016, "Failed to retrieve application keys", 500, + "Failed to retrieve application keys for '%s' "); + + private final long errorCode; + private final String errorMessage; + private final int httpStatusCode; + private final String errorDescription; + private boolean stackTrace = false; + + /** + * @param errorCode This is unique error code that pass to upper level. + * @param msg The error message that you need to pass along with + * the error code. + * @param httpErrorCode This HTTP status code which should return from REST + * API layer. If you don't want to pass + * a http status code keep it blank. + * @param errorDescription The error description. + */ + ExceptionCodes(long errorCode, String msg, int httpErrorCode, String errorDescription, boolean stackTrace) { + this.errorCode = errorCode; + this.errorMessage = msg; + this.httpStatusCode = httpErrorCode; + this.errorDescription = errorDescription; + this.stackTrace = stackTrace; + } + + /** + * @param errorCode This is unique error code that pass to upper level. + * @param msg The error message that you need to pass along with + * the error code. + * @param httpErrorCode This HTTP status code which should return from REST + * API layer. If you don't want to pass + * a http status code keep it blank. + * @param errorDescription The error description. + */ + ExceptionCodes(long errorCode, String msg, int httpErrorCode, String errorDescription) { + this.errorCode = errorCode; + this.errorMessage = msg; + this.httpStatusCode = httpErrorCode; + this.errorDescription = errorDescription; + } + + @Override + public long getErrorCode() { + return this.errorCode; + } + + @Override + public String getErrorMessage() { + return this.errorMessage; + } + + @Override + public int getHttpStatusCode() { + return this.httpStatusCode; + } + + @Override + public String getErrorDescription() { + return this.errorDescription; + } + + public boolean printStackTrace() { + + return stackTrace; + } + + /** + * Create an ErrorHandler instance with the provided ExceptionCode filled with + * some dynamic input values + * + * @param errorHandler ErrorHandler or ExceptionCode object + * @param params dynamic values to be filled + * @return ErrorHandler instance with the provided ExceptionCode filled with + * some dynamic input values + */ + public static ErrorHandler from(ErrorHandler errorHandler, String... params) { + String message = errorHandler.getErrorMessage(); + String description = errorHandler.getErrorDescription(); + + if (params != null && params.length > 0) { + int placesToFormatInMessage = message.length() - message.replace("%", "").length(); + int placesToFormatInDescription = description.length() - description.replace("%", "").length(); + + String[] part1 = Arrays.copyOfRange(params, 0, placesToFormatInMessage); + String[] part2 = Arrays.copyOfRange(params, placesToFormatInMessage, + placesToFormatInMessage + placesToFormatInDescription); + + if (placesToFormatInMessage > 0) { + message = String.format(message, part1); + } + if (placesToFormatInDescription > 0) { + description = String.format(description, part2); + } + } + return new ErrorItem(message, description, errorHandler.getErrorCode(), + errorHandler.getHttpStatusCode(), + errorHandler.printStackTrace()); } - return new ErrorItem(message, description, errorHandler.getErrorCode(), errorHandler.getHttpStatusCode(), - errorHandler.printStackTrace()); - } } diff --git a/runtime/config-deployer-service/java/src/main/java/org/wso2/apk/config/definitions/OASParserUtil.java b/runtime/config-deployer-service/java/src/main/java/org/wso2/apk/config/definitions/OASParserUtil.java index 66eae511c8..77e38e5c33 100644 --- a/runtime/config-deployer-service/java/src/main/java/org/wso2/apk/config/definitions/OASParserUtil.java +++ b/runtime/config-deployer-service/java/src/main/java/org/wso2/apk/config/definitions/OASParserUtil.java @@ -94,7 +94,7 @@ public class OASParserUtil { private static final Log log = LogFactory.getLog(OASParserUtil.class); private static final String OPENAPI_RESOURCE_KEY = "paths"; - private static final String[] UNSUPPORTED_RESOURCE_BLOCKS = new String[]{"servers"}; + private static final String[] UNSUPPORTED_RESOURCE_BLOCKS = new String[] { "servers" }; private static final APIDefinition oas3Parser = new OAS3Parser(); public enum SwaggerVersion { @@ -123,7 +123,6 @@ static class SwaggerUpdateContext { referenceObjectMap.put(HEADERS, new HashSet<>()); } - Paths getPaths() { return paths; } @@ -236,9 +235,11 @@ public static File checkMasterSwagger(File archiveDirectory) throws APIManagemen } /** - * Update the APIDefinitionValidationResponse object with success state using the values given + * Update the APIDefinitionValidationResponse object with success state using + * the values given * - * @param validationResponse APIDefinitionValidationResponse object to be updated + * @param validationResponse APIDefinitionValidationResponse object to be + * updated * @param originalAPIDefinition original API Definition * @param openAPIVersion version of OpenAPI Spec (2.0 or 3.0.0) * @param title title of the OpenAPI Definition @@ -262,7 +263,8 @@ public static void updateValidationResponseAsSuccess(APIDefinitionValidationResp } /** - * Add error item with the provided message to the provided validation response object + * Add error item with the provided message to the provided validation response + * object * * @param validationResponse APIDefinitionValidationResponse object * @param errMessage error message @@ -290,14 +292,14 @@ public static String getSwaggerJsonString(Swagger swaggerObj) throws APIManageme mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); mapper.enable(SerializationFeature.INDENT_OUTPUT); - //this is to ignore "originalRef" in schema objects + // this is to ignore "originalRef" in schema objects mapper.addMixIn(RefModel.class, IgnoreOriginalRefMixin.class); mapper.addMixIn(RefProperty.class, IgnoreOriginalRefMixin.class); mapper.addMixIn(RefPath.class, IgnoreOriginalRefMixin.class); mapper.addMixIn(RefParameter.class, IgnoreOriginalRefMixin.class); mapper.addMixIn(RefResponse.class, IgnoreOriginalRefMixin.class); - //this is to ignore "responseSchema" in response schema objects + // this is to ignore "responseSchema" in response schema objects mapper.addMixIn(Response.class, ResponseSchemaMixin.class); try { return new String(mapper.writeValueAsBytes(swaggerObj)); @@ -309,9 +311,9 @@ public static String getSwaggerJsonString(Swagger swaggerObj) throws APIManageme /** * Sets the scopes to the URL template object using the given list of scopes * - * @param template URL template - * @param resourceScopes list of scopes of the resource - * @param apiScopes set of scopes defined for the API + * @param template URL template + * @param resourceScopes list of scopes of the resource + * @param apiScopes set of scopes defined for the API * @return URL template after setting the scopes */ public static URITemplate setScopesToTemplate(URITemplate template, List resourceScopes, @@ -379,35 +381,39 @@ public static void copyOperationVendorExtensions(Map existingExt .get(APIConstants.SWAGGER_X_AMZN_RESOURCE_TIMEOUT)); } } + /** * Extract the archive file and validates the openAPI definition * - * @param inputByteArray file as input stream - * @param returnContent whether to return the content of the definition in the response DTO + * @param inputByteArray file as input stream + * @param returnContent whether to return the content of the definition in the + * response DTO * @return APIDefinitionValidationResponse * @throws APIManagementException if error occurred while parsing definition */ public static APIDefinitionValidationResponse extractAndValidateOpenAPIArchive(byte[] inputByteArray, - boolean returnContent) throws APIManagementException { + boolean returnContent) throws APIManagementException { String path = System.getProperty(APIConstants.JAVA_IO_TMPDIR) + File.separator + APIConstants.OPENAPI_ARCHIVES_TEMP_FOLDER + File.separator + UUID.randomUUID(); String archivePath = path + File.separator + APIConstants.OPENAPI_ARCHIVE_ZIP_FILE; - String extractedLocation = OASParserUtil.extractUploadedArchive(inputByteArray, APIConstants.OPENAPI_EXTRACTED_DIRECTORY, archivePath, path); + String extractedLocation = OASParserUtil.extractUploadedArchive(inputByteArray, + APIConstants.OPENAPI_EXTRACTED_DIRECTORY, archivePath, path); File[] listOfFiles = new File(extractedLocation).listFiles(); File archiveDirectory = null; if (listOfFiles != null) { if (listOfFiles.length > 1) { throw new APIManagementException("Swagger Definitions should be placed under one root folder."); } - for (File file: listOfFiles) { + for (File file : listOfFiles) { if (file.isDirectory()) { archiveDirectory = file.getAbsoluteFile(); break; } } } - //Verify whether the zipped input is archive or file. - //If it is a single swagger file without remote references it can be imported directly, without zipping. + // Verify whether the zipped input is archive or file. + // If it is a single swagger file without remote references it can be imported + // directly, without zipping. if (archiveDirectory == null) { throw new APIManagementException("Could not find an archive in the given ZIP file."); } @@ -442,8 +448,9 @@ public static APIDefinitionValidationResponse extractAndValidateOpenAPIArchive(b apiDefinitionValidationResponse = OASParserUtil.validateAPIDefinition(openAPIContent, returnContent); return apiDefinitionValidationResponse; } + public static String extractUploadedArchive(byte[] byteArray, String importedDirectoryName, - String apiArchiveLocation, String extractLocation) throws APIManagementException { + String apiArchiveLocation, String extractLocation) throws APIManagementException { String archiveExtractLocation; try (ByteArrayInputStream uploadedApiArchiveInputStream = new ByteArrayInputStream(byteArray)) { @@ -463,6 +470,7 @@ public static String extractUploadedArchive(byte[] byteArray, String importedDir } return archiveExtractLocation; } + /** * Delete a given directory * @@ -485,7 +493,8 @@ public static void deleteDirectory(String path) throws APIManagementException { * @param archiveFilePath path of the zip archive * @param destination extract location * @return name of the extracted zip archive - * @throws APIManagementException if an error occurs while extracting the archive + * @throws APIManagementException if an error occurs while extracting the + * archive */ public static String extractArchive(String archiveFilePath, String destination) throws APIManagementException { @@ -505,7 +514,8 @@ public static String extractArchive(String archiveFilePath, String destination) while ((entry = zis.getNextEntry()) != null) { String currentEntry = entry.getName(); int index = 0; - //This index variable is used to get the extracted folder name; that is root directory + // This index variable is used to get the extracted folder name; that is root + // directory if (index == 0 && currentEntry.indexOf('/') != -1) { archiveName = currentEntry.substring(0, currentEntry.indexOf('/')); --index; @@ -558,6 +568,7 @@ public static String extractArchive(String archiveFilePath, String destination) throw new APIManagementException(errorMsg, e); } } + /** * Creates a zip archive from the given {@link InputStream} inputStream * @@ -575,11 +586,13 @@ public static void createArchiveFromInputStream(InputStream inputStream, String throw new APIManagementException(errorMsg, e); } } + /** * Creates a directory * * @param path path of the directory to create - * @throws APIManagementException if an error occurs while creating the directory + * @throws APIManagementException if an error occurs while creating the + * directory */ public static void createDirectory(String path) throws APIManagementException { try { @@ -591,7 +604,6 @@ public static void createDirectory(String path) throws APIManagementException { } } - /** * Try to validate a give openAPI definition using OpenAPI 3 parser * @@ -605,7 +617,8 @@ public static APIDefinitionValidationResponse validateAPIDefinition(String apiDe String apiDefinitionProcessed = apiDefinition; if (!apiDefinition.trim().startsWith("{")) { try { - JsonNode jsonNode = DeserializationUtils.readYamlTree(apiDefinition, new SwaggerDeserializationResult()); + JsonNode jsonNode = DeserializationUtils.readYamlTree(apiDefinition, + new SwaggerDeserializationResult()); apiDefinitionProcessed = jsonNode.toString(); } catch (IOException e) { throw new APIManagementException("Error while reading API definition yaml", e); @@ -615,14 +628,15 @@ public static APIDefinitionValidationResponse validateAPIDefinition(String apiDe if (apiDefinitionProcessed != null) { apiDefinition = apiDefinitionProcessed; } - APIDefinitionValidationResponse validationResponse = - oas3Parser.validateAPIDefinition(apiDefinition, returnJsonContent); + APIDefinitionValidationResponse validationResponse = oas3Parser.validateAPIDefinition(apiDefinition, + returnJsonContent); if (!validationResponse.isValid()) { -// for (ErrorHandler handler : validationResponse.getErrorItems()) { -// if (ExceptionCodes.INVALID_OAS3_FOUND.getErrorCode() == handler.getErrorCode()) { -// return tryOAS2Validation(apiDefinition, returnJsonContent); -// } -// } + // for (ErrorHandler handler : validationResponse.getErrorItems()) { + // if (ExceptionCodes.INVALID_OAS3_FOUND.getErrorCode() == + // handler.getErrorCode()) { + // return tryOAS2Validation(apiDefinition, returnJsonContent); + // } + // } } return validationResponse; } @@ -671,18 +685,20 @@ public static APIDefinitionValidationResponse validateGraphQLSchema(String apiDe /** * This method removes the unsupported json blocks from the given json string. * - * @param jsonString Open api specification from which unsupported blocks must be removed. - * @return String open api specification without unsupported blocks. Null value if there is no unsupported blocks. + * @param jsonString Open api specification from which unsupported blocks must + * be removed. + * @return String open api specification without unsupported blocks. Null value + * if there is no unsupported blocks. */ public static String removeUnsupportedBlocksFromResources(String jsonString) { JSONObject jsonObject = new JSONObject(jsonString); boolean definitionUpdated = false; if (jsonObject.has(OPENAPI_RESOURCE_KEY)) { JSONObject paths = jsonObject.optJSONObject(OPENAPI_RESOURCE_KEY); - if (paths != null ) { + if (paths != null) { for (String unsupportedBlockKey : UNSUPPORTED_RESOURCE_BLOCKS) { boolean result = removeBlocksRecursivelyFromJsonObject(unsupportedBlockKey, paths, false); - definitionUpdated = definitionUpdated || result; + definitionUpdated = definitionUpdated || result; } } } @@ -700,13 +716,15 @@ public static String removeUnsupportedBlocksFromResources(String jsonString) { return null; } } + /** * This method removes provided key from the json object recursively. * * @param keyToBeRemoved, Key to remove from open api spec. - * @param jsonObject, Open api spec as json object. + * @param jsonObject, Open api spec as json object. */ - private static boolean removeBlocksRecursivelyFromJsonObject(String keyToBeRemoved, JSONObject jsonObject, boolean definitionUpdated) { + private static boolean removeBlocksRecursivelyFromJsonObject(String keyToBeRemoved, JSONObject jsonObject, + boolean definitionUpdated) { if (jsonObject == null) { return definitionUpdated; } @@ -723,4 +741,32 @@ private static boolean removeBlocksRecursivelyFromJsonObject(String keyToBeRemov } return definitionUpdated; } + + /** + * Validate gRPC proto definition + * + * @return Validation response + */ + public static APIDefinitionValidationResponse validateProtoDefinition(String apiDefinition, + boolean returnContent) { + APIDefinitionValidationResponse validationResponse = new APIDefinitionValidationResponse(); + ArrayList errors = new ArrayList<>(); + try { + if (apiDefinition.isBlank()) { + validationResponse.setValid(false); + errors.add(ExceptionCodes.GRPC_PROTO_DEFINTION_CANNOT_BE_NULL); + validationResponse.setErrorItems(errors); + } else { + validationResponse.setValid(true); + validationResponse.setContent(apiDefinition); + } + } catch (Exception e) { + OASParserUtil.addErrorToValidationResponse(validationResponse, e.getMessage()); + validationResponse.setValid(false); + errors.add(new ErrorItem("API Definition Validation Error", "API Definition is invalid", 400, 400)); + validationResponse.setErrorItems(errors); + } + return validationResponse; + + } } diff --git a/runtime/config-deployer-service/java/src/main/java/org/wso2/apk/config/definitions/ProtoParser.java b/runtime/config-deployer-service/java/src/main/java/org/wso2/apk/config/definitions/ProtoParser.java new file mode 100644 index 0000000000..3010905c26 --- /dev/null +++ b/runtime/config-deployer-service/java/src/main/java/org/wso2/apk/config/definitions/ProtoParser.java @@ -0,0 +1,336 @@ +package org.wso2.apk.config.definitions; + +import java.io.*; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.Iterator; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import org.wso2.apk.config.api.APIDefinition; +import org.wso2.apk.config.api.APIDefinitionValidationResponse; +import org.wso2.apk.config.api.APIManagementException; +import org.wso2.apk.config.api.ErrorHandler; +import org.wso2.apk.config.api.ErrorItem; +import org.wso2.apk.config.api.ExceptionCodes; +import org.wso2.apk.config.model.API; +import org.wso2.apk.config.model.SwaggerData; +import org.wso2.apk.config.model.URITemplate; + +import io.swagger.v3.core.util.Json; +import io.swagger.v3.oas.models.OpenAPI; +import io.swagger.v3.oas.models.Operation; +import io.swagger.v3.oas.models.PathItem; +import io.swagger.v3.oas.models.Paths; +import io.swagger.v3.oas.models.info.Contact; +import io.swagger.v3.oas.models.info.Info; +import io.swagger.v3.parser.OpenAPIV3Parser; +import io.swagger.v3.parser.core.models.SwaggerParseResult; + +import com.google.protobuf.DescriptorProtos; +import com.google.protobuf.TextFormat; +import com.google.protobuf.InvalidProtocolBufferException; + +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Path; + +public class ProtoParser extends APIDefinition { + + public ProtoFile protoFile; + + public ProtoParser() { + } + + public void setContent(String content) { + protoFile = parseProtoContent(content); + } + + public String getPackageString(String content) { + Pattern packagePattern = Pattern.compile("package\\s+([\\w\\.]+);"); + Matcher packageMatcher = packagePattern.matcher(content); + if (packageMatcher.find()) { + return packageMatcher.group(1); + } + return null; + } + + public String getVersion(String packageString) { + Pattern versionPattern = Pattern.compile("v\\d+(\\.\\d+)*"); + Matcher versionMatcher = versionPattern.matcher(packageString); + if (versionMatcher.find()) { + return versionMatcher.group(0); + } + System.out.println("No version found"); + return null; + } + + public String getPackageName(String packageString) { + Pattern namePattern = Pattern.compile("v\\d+(\\.\\d+)*\\.(\\w+)"); + Matcher nameMatcher = namePattern.matcher(packageString); + if (nameMatcher.find()) { + return nameMatcher.group(2); + } + System.out.println("No name found"); + return null; + } + + public String getBasePath(String packageString) { + Pattern basePathPattern = Pattern.compile("^(.*?)v\\d"); + + Matcher basePathMatcher = basePathPattern.matcher(packageString); + if (basePathMatcher.find()) { + String basePath = basePathMatcher.group(1); + if (basePath.charAt(basePath.length() - 1) == '.') { + basePath = basePath.substring(0, basePath.length() - 1); + } + return basePath; + } + System.out.println("No base path found"); + return null; + } + + // Method to extract service blocks from a given text + public List extractServiceBlocks(String text) { + // Regular expression pattern to match the service blocks + String patternString = "service\\s+\\w+\\s*\\{[^{}]*(?:\\{[^{}]*\\}[^{}]*)*\\}"; + + // Compile the regular expression + Pattern pattern = Pattern.compile(patternString, Pattern.DOTALL); + Matcher matcher = pattern.matcher(text); + + // Find all matches and append them to the result + List result = new ArrayList<>(); + while (matcher.find()) { + result.add(matcher.group()); + } + return result; + } + + public List extractMethodNames(String serviceBlock) { + // Regular expression pattern to match the method names + String patternString = "(?<=rpc\\s)\\w+"; + + // Compile the regular expression + Pattern pattern = Pattern.compile(patternString); + Matcher matcher = pattern.matcher(serviceBlock); + + // Find all matches and append them to the result + List result = new ArrayList<>(); + while (matcher.find()) { + result.add(matcher.group()); + } + return result; + } + + public String getServiceName(String serviceBlock) { + // Regular expression pattern to match the service name + String patternString = "(?<=service\\s)\\w+"; + + // Compile the regular expression + Pattern pattern = Pattern.compile(patternString); + Matcher matcher = pattern.matcher(serviceBlock); + + // Find the first match and return it + if (matcher.find()) { + return matcher.group(); + } + return null; + } + + public ProtoFile parseProtoContent(String content) { + ProtoFile protoFile = new ProtoFile(); + protoFile.services = new ArrayList<>(); + + List serviceBlocks = extractServiceBlocks(content); + for (String serviceBlock : serviceBlocks) { + Service service = new Service(); + service.name = getServiceName(serviceBlock); + service.methods = new ArrayList<>(); + service.methods.addAll(extractMethodNames(serviceBlock)); + protoFile.services.add(service); + } + + // Extract package name + String packageName = getPackageString(content); + protoFile.packageName = getPackageName(packageName); + protoFile.version = getVersion(packageName); + protoFile.basePath = getBasePath(packageName); + + // System.out.println(protoFile); + + return protoFile; + } + + public List getMethods(Service Service) { + return Service.methods; + } + + public List getServices() { + return this.protoFile.services; + } + + public class ProtoFile { + public String packageName; + public String basePath; + public String version; + public List services; + + @Override + public String toString() { + return "ProtoFile{" + + "packageName='" + packageName + '\'' + + ", basePath='" + basePath + '\'' + + ", version='" + version + '\'' + + ", services=" + services + + '}'; + } + + } + + public class Service { + public String name; + public List methods; + + @Override + public String toString() { + return " Service{" + + "name='" + name + '\'' + + ", methods=" + methods + + '}'; + } + } + + @Override + public Set getURITemplates(String resourceConfigsJSON) throws APIManagementException { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'getURITemplates'"); + } + + @Override + public String[] getScopes(String resourceConfigsJSON) throws APIManagementException { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'getScopes'"); + } + + @Override + public String generateAPIDefinition(API api) throws APIManagementException { + + SwaggerData swaggerData = new SwaggerData(api); + return generateAPIDefinition(swaggerData); + } + + /** + * This method generates API definition to the given api + * + * @param swaggerData api + * @return API definition in string format + * @throws APIManagementException + */ + private String generateAPIDefinition(SwaggerData swaggerData) { + + OpenAPI openAPI = new OpenAPI(); + + // create path if null + if (openAPI.getPaths() == null) { + openAPI.setPaths(new Paths()); + } + + // Create info object + Info info = new Info(); + info.setTitle(swaggerData.getTitle()); + if (swaggerData.getDescription() != null) { + info.setDescription(swaggerData.getDescription()); + } + + Contact contact = new Contact(); + // Create contact object and map business owner info + if (swaggerData.getContactName() != null) { + contact.setName(swaggerData.getContactName()); + } + if (swaggerData.getContactEmail() != null) { + contact.setEmail(swaggerData.getContactEmail()); + } + if (swaggerData.getContactName() != null || swaggerData.getContactEmail() != null) { + // put contact object to info object + info.setContact(contact); + } + + info.setVersion(swaggerData.getVersion()); + openAPI.setInfo(info); + return Json.pretty(openAPI); + } + + /** + * Validate gRPC proto definition + * + * @return Validation response + */ + public APIDefinitionValidationResponse validateAPIDefinition(String apiDefinition, + boolean returnContent) { + APIDefinitionValidationResponse validationResponse = new APIDefinitionValidationResponse(); + ArrayList errors = new ArrayList<>(); + try { + if (apiDefinition.isBlank()) { + validationResponse.setValid(false); + errors.add(ExceptionCodes.GRPC_PROTO_DEFINTION_CANNOT_BE_NULL); + validationResponse.setErrorItems(errors); + } else { + validationResponse.setValid(true); + validationResponse.setContent(apiDefinition); + } + } catch (Exception e) { + OASParserUtil.addErrorToValidationResponse(validationResponse, e.getMessage()); + validationResponse.setValid(false); + errors.add(new ErrorItem("API Definition Validation Error", "API Definition is invalid", 400, 400)); + validationResponse.setErrorItems(errors); + } + return validationResponse; + + } + + @Override + public API getAPIFromDefinition(String content) throws APIManagementException { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'getAPIFromDefinition'"); + } + + @Override + public String processOtherSchemeScopes(String resourceConfigsJSON) throws APIManagementException { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'processOtherSchemeScopes'"); + } + + @Override + public String getType() { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'getType'"); + } + + @Override + public boolean canHandleDefinition(String definition) { + return true; + } + + @Override + public String generateAPIDefinition(API api, String swagger) throws APIManagementException { + return null; + } + + public boolean validateProtoFile(String protoContent) { + try { + DescriptorProtos.FileDescriptorProto.Builder builder = DescriptorProtos.FileDescriptorProto.newBuilder(); + TextFormat.getParser().merge(protoContent, builder); + // If parsing succeeds, return true + return true; + } catch (IOException e) { + // If an exception occurs, the proto file is invalid + System.err.println("Validation failed: " + e.getMessage()); + return false; + } + } + +} \ No newline at end of file diff --git a/runtime/config-deployer-service/java/src/main/java/org/wso2/apk/config/definitions/ProtoParserUtil.java b/runtime/config-deployer-service/java/src/main/java/org/wso2/apk/config/definitions/ProtoParserUtil.java new file mode 100644 index 0000000000..6c65adc04f --- /dev/null +++ b/runtime/config-deployer-service/java/src/main/java/org/wso2/apk/config/definitions/ProtoParserUtil.java @@ -0,0 +1,70 @@ +package org.wso2.apk.config.definitions; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.Set; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.wso2.apk.config.api.APIDefinition; +import org.wso2.apk.config.api.APIDefinitionValidationResponse; +import org.wso2.apk.config.api.ErrorHandler; +import org.wso2.apk.config.api.ErrorItem; +import org.wso2.apk.config.api.ExceptionCodes; + +public class ProtoParserUtil { + /** + * Provide common functions related to OAS + */ + private static final Log log = LogFactory.getLog(ProtoParserUtil.class); + private static final ProtoParser protoParser = new ProtoParser(); + + /** + * Validate graphQL Schema + * + * @return Validation response + */ + public static APIDefinitionValidationResponse validateGRPCAPIDefinition(String apiDefinition, + boolean returnGRPCContent) { + APIDefinitionValidationResponse validationResponse = new APIDefinitionValidationResponse(); + ArrayList errors = new ArrayList<>(); + + if (apiDefinition.isBlank()) { + validationResponse.setValid(false); + errors.add(ExceptionCodes.GRPC_PROTO_DEFINTION_CANNOT_BE_NULL); + validationResponse.setErrorItems(errors); + } + + try { + boolean validated = protoParser.validateProtoFile(apiDefinition); + validationResponse.setValid(validated); + validationResponse.setContent(apiDefinition); + } catch (Exception e) { + ProtoParserUtil.addErrorToValidationResponse(validationResponse, e.getMessage()); + validationResponse.setValid(false); + errors.add(new ErrorItem("API Definition Validation Error", "API Definition is invalid", 400, 400)); + validationResponse.setErrorItems(errors); + } + + return validationResponse; + } + + /** + * Add error item with the provided message to the provided validation response + * object + * + * @param validationResponse APIDefinitionValidationResponse object + * @param errMessage error message + * @return added ErrorItem object + */ + public static ErrorItem addErrorToValidationResponse(APIDefinitionValidationResponse validationResponse, + String errMessage) { + ErrorItem errorItem = new ErrorItem(); + errorItem.setErrorCode(ExceptionCodes.PROTO_DEFINITION_PARSE_EXCEPTION.getErrorCode()); + errorItem.setMessage(ExceptionCodes.PROTO_DEFINITION_PARSE_EXCEPTION.getErrorMessage()); + errorItem.setDescription(errMessage); + validationResponse.getErrorItems().add(errorItem); + return errorItem; + } + +} diff --git a/runtime/config-deployer-service/java/src/main/java/org/wso2/apk/config/model/API.java b/runtime/config-deployer-service/java/src/main/java/org/wso2/apk/config/model/API.java index d2715d77b2..98c810eaa1 100644 --- a/runtime/config-deployer-service/java/src/main/java/org/wso2/apk/config/model/API.java +++ b/runtime/config-deployer-service/java/src/main/java/org/wso2/apk/config/model/API.java @@ -11,6 +11,7 @@ public class API { private String apiSecurity; private String[] scopes; private String graphQLSchema; + private String protoDefinition; private String swaggerDefinition; private String environment; @@ -87,6 +88,14 @@ public void setSwaggerDefinition(String swaggerDefinition) { this.swaggerDefinition = swaggerDefinition; } + public void setProtoDefinition(String protoDefinition) { + this.protoDefinition = protoDefinition; + } + + public String getProtoDefinition() { + return protoDefinition; + } + public String getBasePath() { return basePath; From 2f8353c6b558ebd7408b1a6a532db8da99338df8 Mon Sep 17 00:00:00 2001 From: sgayangi Date: Tue, 27 Aug 2024 15:21:54 +0530 Subject: [PATCH 07/17] Fix bugs during gRPC API invocation --- .../internal/oasparser/constants/constants.go | 2 +- .../envoyconf/routes_with_clusters.go | 29 ++- .../config/crd/bases/dp.wso2.com_apis.yaml | 132 +++++------- .../internal/operator/constants/constants.go | 4 +- .../operator/controllers/dp/api_controller.go | 203 +++++++++++++++--- .../operator/synchronizer/synchronizer.go | 6 +- .../dp/ratelimitpolicy_controller.go | 52 ++--- common-go-libs/apis/dp/v1beta1/api_types.go | 4 +- .../config/crd/bases/dp.wso2.com_apis.yaml | 4 +- .../org/wso2/apk/enforcer/api/APIFactory.java | 1 - .../apk/enforcer/constants/APIConstants.java | 2 +- .../templates/crds/dp.wso2.com_apis.yaml | 4 +- helm-charts/values.yaml | 23 +- .../ballerina/APIClient.bal | 7 - .../ballerina/Ballerina.toml | 7 +- .../ballerina/ConfigGenreatorClient.bal | 8 + .../ballerina/DeployerClient.bal | 6 - .../ballerina/K8sClient.bal | 2 - .../apk/config/DefinitionParserFactory.java | 4 +- 19 files changed, 324 insertions(+), 176 deletions(-) diff --git a/adapter/internal/oasparser/constants/constants.go b/adapter/internal/oasparser/constants/constants.go index ef8ecce263..5d76f86cfe 100644 --- a/adapter/internal/oasparser/constants/constants.go +++ b/adapter/internal/oasparser/constants/constants.go @@ -105,7 +105,7 @@ const ( SOAP string = "SOAP" WS string = "WS" GRAPHQL string = "GraphQL" - GRPC string = "gRPC" + GRPC string = "GRPC" WEBHOOK string = "WEBHOOK" SSE string = "SSE" Prototyped string = "prototyped" diff --git a/adapter/internal/oasparser/envoyconf/routes_with_clusters.go b/adapter/internal/oasparser/envoyconf/routes_with_clusters.go index 59e8c3f10c..e25638341d 100644 --- a/adapter/internal/oasparser/envoyconf/routes_with_clusters.go +++ b/adapter/internal/oasparser/envoyconf/routes_with_clusters.go @@ -1012,8 +1012,14 @@ func createRoutes(params *routeCreateParams) (routes []*routev3.Route, err error decorator *routev3.Decorator ) if params.createDefaultPath { - xWso2Basepath = removeFirstOccurrence(xWso2Basepath, "/"+version) - resourcePath = removeFirstOccurrence(resource.GetPath(), "/"+version) + // check if basepath is separated from version by a . or / + if strings.Contains(basePath, "."+version) { + xWso2Basepath = removeFirstOccurrence(basePath, "."+version) + resourcePath = removeFirstOccurrence(resource.GetPath(), "."+version) + } else { + xWso2Basepath = removeFirstOccurrence(xWso2Basepath, "/"+version) + resourcePath = removeFirstOccurrence(resource.GetPath(), "/"+version) + } } if pathMatchType != gwapiv1.PathMatchExact { @@ -1248,6 +1254,16 @@ func createRoutes(params *routeCreateParams) (routes []*routev3.Route, err error rewritePath := generateRoutePathForReWrite(basePath, resourcePath, pathMatchType) action.Route.RegexRewrite = generateRegexMatchAndSubstitute(rewritePath, resourcePath, pathMatchType) requestHeadersToRemove := make([]string, 0) + + if apiType == constants.GRPC { + match.Headers = nil + newRoutePath := "/" + strings.TrimPrefix(resourcePath, basePath+".") + if newRoutePath == "/"+resourcePath { + temp := removeFirstOccurrence(basePath, "."+version) + newRoutePath = "/" + strings.TrimPrefix(resourcePath, temp+".") + } + action.Route.RegexRewrite = generateRegexMatchAndSubstitute(rewritePath, newRoutePath, pathMatchType) + } route := generateRouteConfig(xWso2Basepath, match, action, nil, metaData, decorator, perRouteFilterConfigs, nil, requestHeadersToRemove, nil, nil) // general headers to add and remove are included in this methods routes = append(routes, route) @@ -1412,8 +1428,13 @@ func CreateAPIDefinitionEndpoint(adapterInternalAPI *model.AdapterInternalAPI, v matchPath := basePath + endpoint if isDefaultversion { - basePathWithoutVersion := removeLastOccurrence(basePath, "/"+version) - matchPath = basePathWithoutVersion + endpoint + if adapterInternalAPI.GetAPIType() == constants.GRPC { + basePathWithoutVersion := removeLastOccurrence(basePath, "."+version) + matchPath = basePathWithoutVersion + "/" + vHost + endpoint + } else { + basePathWithoutVersion := removeLastOccurrence(basePath, "/"+version) + matchPath = basePathWithoutVersion + endpoint + } } matchPath = strings.Replace(matchPath, basePath, regexp.QuoteMeta(basePath), 1) diff --git a/adapter/internal/operator/config/crd/bases/dp.wso2.com_apis.yaml b/adapter/internal/operator/config/crd/bases/dp.wso2.com_apis.yaml index d24c5cafa0..1fa8ea6721 100644 --- a/adapter/internal/operator/config/crd/bases/dp.wso2.com_apis.yaml +++ b/adapter/internal/operator/config/crd/bases/dp.wso2.com_apis.yaml @@ -3,8 +3,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.9.2 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.12.0 name: apis.dp.wso2.com spec: group: dp.wso2.com @@ -260,8 +259,8 @@ spec: pattern: ^[/][a-zA-Z0-9~/_.-]*$ type: string definitionFileRef: - description: DefinitionFileRef contains the OpenAPI 3 or SDL file - in gzipped format. definition of the API in a ConfigMap. + description: DefinitionFileRef contains the definition of the API + in a ConfigMap. type: string definitionPath: default: /api-definition @@ -286,13 +285,13 @@ spec: items: description: EnvConfig contains the environment specific configuration properties: - httpRouteRefs: - description: HTTPRouteRefs denotes the environment of the API. + routeRefs: + description: RouteRefs denotes the environment of the API. items: type: string type: array required: - - httpRouteRefs + - routeRefs type: object maxItems: 1 nullable: true @@ -303,13 +302,13 @@ spec: items: description: EnvConfig contains the environment specific configuration properties: - httpRouteRefs: - description: HTTPRouteRefs denotes the environment of the API. + routeRefs: + description: RouteRefs denotes the environment of the API. items: type: string type: array required: - - httpRouteRefs + - routeRefs type: object maxItems: 1 nullable: true @@ -328,13 +327,11 @@ spec: description: APIStatus defines the observed state of API properties: deploymentStatus: - description: - DeploymentStatus denotes the deployment status of the + description: DeploymentStatus denotes the deployment status of the API properties: accepted: - description: - Accepted represents whether the API is accepted or + description: Accepted represents whether the API is accepted or not. type: boolean events: @@ -343,25 +340,22 @@ spec: type: string type: array message: - description: - Message represents a user friendly message that explains + description: Message represents a user friendly message that explains the current state of the API. type: string status: - description: - Status denotes the state of the API in its lifecycle. + description: Status denotes the state of the API in its lifecycle. Possible values could be Accepted, Invalid, Deploy etc. type: string transitionTime: - description: - TransitionTime represents the last known transition + description: TransitionTime represents the last known transition timestamp. format: date-time type: string required: - - accepted - - status - - transitionTime + - accepted + - status + - transitionTime type: object type: object type: object @@ -370,37 +364,35 @@ spec: subresources: status: {} - additionalPrinterColumns: - - jsonPath: .spec.apiName - name: API Name - type: string - - jsonPath: .spec.apiVersion - name: Version - type: string - - jsonPath: .spec.basePath - name: BasePath - type: string - - jsonPath: .spec.organization - name: Organization - type: string - - jsonPath: .metadata.creationTimestamp - name: Age - type: date + - jsonPath: .spec.apiName + name: API Name + type: string + - jsonPath: .spec.apiVersion + name: Version + type: string + - jsonPath: .spec.basePath + name: BasePath + type: string + - jsonPath: .spec.organization + name: Organization + type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date name: v1beta1 schema: openAPIV3Schema: description: API is the Schema for the apis API properties: apiVersion: - description: - "APIVersion defines the versioned schema of this representation + description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest - internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources" + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' type: string kind: - description: - "Kind is a string value representing the REST resource this + description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client - submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds" + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' type: string metadata: type: object @@ -408,8 +400,7 @@ spec: description: APISpec defines the desired state of API properties: apiName: - description: - APIName is the unique name of the API can be used to + description: APIName is the unique name of the API can be used to uniquely identify an API. maxLength: 60 minLength: 1 @@ -428,13 +419,12 @@ spec: nullable: true type: array apiType: - description: - APIType denotes the type of the API. Possible values - could be REST, GraphQL, gRPC + description: APIType denotes the type of the API. Possible values + could be REST, GraphQL, GRPC enum: - - REST - - GraphQL - - gRPC + - REST + - GraphQL + - GRPC type: string apiVersion: description: APIVersion is the version number of the API. @@ -443,12 +433,11 @@ spec: pattern: ^[^~!@#;:%^*()+={}|\<>"'',&/$\[\]\s+\/]+$ type: string basePath: - description: "BasePath denotes the basepath of the API. e.g: /pet-store-api/1.0.6" + description: 'BasePath denotes the basepath of the API. e.g: /pet-store-api/1.0.6' pattern: ^[/][a-zA-Z0-9~/_.-]*$ type: string definitionFileRef: - description: - DefinitionFileRef contains the definition of the API + description: DefinitionFileRef contains the definition of the API in a ConfigMap. type: string definitionPath: @@ -461,19 +450,16 @@ spec: nullable: true type: string isDefaultVersion: - description: - IsDefaultVersion indicates whether this API version should + description: IsDefaultVersion indicates whether this API version should be used as a default API type: boolean organization: - description: - Organization denotes the organization. related to the + description: Organization denotes the organization. related to the API type: string production: - description: - "Production contains a list of references to HttpRoutes - of type HttpRoute. xref: https://github.com/kubernetes-sigs/gateway-api/blob/main/apis/v1beta1/httproute_types.go" + description: 'Production contains a list of references to HttpRoutes + of type HttpRoute. xref: https://github.com/kubernetes-sigs/gateway-api/blob/main/apis/v1beta1/httproute_types.go' items: description: EnvConfig contains the environment specific configuration properties: @@ -483,15 +469,14 @@ spec: type: string type: array required: - - routeRefs + - routeRefs type: object maxItems: 1 nullable: true type: array sandbox: - description: - "Sandbox contains a list of references to HttpRoutes - of type HttpRoute. xref: https://github.com/kubernetes-sigs/gateway-api/blob/main/apis/v1beta1/httproute_types.go" + description: 'Sandbox contains a list of references to HttpRoutes + of type HttpRoute. xref: https://github.com/kubernetes-sigs/gateway-api/blob/main/apis/v1beta1/httproute_types.go' items: description: EnvConfig contains the environment specific configuration properties: @@ -501,7 +486,7 @@ spec: type: string type: array required: - - routeRefs + - routeRefs type: object maxItems: 1 nullable: true @@ -510,18 +495,17 @@ spec: description: SystemAPI denotes if it is an internal system API. type: boolean required: - - apiName - - apiType - - apiVersion - - basePath - - definitionPath + - apiName + - apiType + - apiVersion + - basePath + - definitionPath type: object status: description: APIStatus defines the observed state of API properties: deploymentStatus: - description: - DeploymentStatus denotes the deployment status of the + description: DeploymentStatus denotes the deployment status of the API properties: accepted: diff --git a/adapter/internal/operator/constants/constants.go b/adapter/internal/operator/constants/constants.go index b642648de3..8e0927e66f 100644 --- a/adapter/internal/operator/constants/constants.go +++ b/adapter/internal/operator/constants/constants.go @@ -71,5 +71,7 @@ const ( // API Types const ( - GRPC = "gRPC" + GRAPHQL = "GraphQL" + REST = "REST" + GRPC = "GRPC" ) diff --git a/adapter/internal/operator/controllers/dp/api_controller.go b/adapter/internal/operator/controllers/dp/api_controller.go index 595990d594..60967fb77d 100644 --- a/adapter/internal/operator/controllers/dp/api_controller.go +++ b/adapter/internal/operator/controllers/dp/api_controller.go @@ -456,7 +456,6 @@ func (apiReconciler *APIReconciler) resolveAPIRefs(ctx context.Context, api dpv1 return nil, fmt.Errorf("error while resolving production gqlRouteref %s in namespace :%s has not found. %s", prodRouteRefs, namespace, err.Error()) } - } if len(sandRouteRefs) > 0 && apiState.APIDefinition.Spec.APIType == "GraphQL" { if apiState.SandGQLRoute, err = apiReconciler.resolveGQLRouteRefs(ctx, sandRouteRefs, namespace, @@ -467,19 +466,36 @@ func (apiReconciler *APIReconciler) resolveAPIRefs(ctx context.Context, api dpv1 } // handle gRPC APIs - if len(prodRouteRefs) > 0 && apiState.APIDefinition.Spec.APIType == "gRPC" { + if len(prodRouteRefs) > 0 && apiState.APIDefinition.Spec.APIType == constants.GRPC { if apiState.ProdGRPCRoute, err = apiReconciler.resolveGRPCRouteRefs(ctx, prodRouteRefs, namespace, api); err != nil { - return nil, fmt.Errorf("error while resolving production grpcRouteref %s in namespace :%s has not found. %s", + return nil, fmt.Errorf("error while resolving production grpcRouteref %s in namespace :%s was not found. %s", prodRouteRefs, namespace, err.Error()) } + if !apiReconciler.ods.IsGatewayAvailable(types.NamespacedName{ + Name: string(apiState.ProdGRPCRoute.GRPCRouteCombined.Spec.ParentRefs[0].Name), + Namespace: utils.GetNamespace(apiState.ProdGRPCRoute.GRPCRouteCombined.Spec.ParentRefs[0].Namespace, + apiState.ProdGRPCRoute.GRPCRouteCombined.Namespace), + }) { + return nil, fmt.Errorf("no gateway available for grpcpRouteref %s in namespace :%s was not found", + prodRouteRefs, namespace) + } } - if len(sandRouteRefs) > 0 && apiState.APIDefinition.Spec.APIType == "gRPC" { + + if len(sandRouteRefs) > 0 && apiState.APIDefinition.Spec.APIType == constants.GRPC { if apiState.SandGRPCRoute, err = apiReconciler.resolveGRPCRouteRefs(ctx, sandRouteRefs, namespace, api); err != nil { - return nil, fmt.Errorf("error while resolving sandbox grpcRouteref %s in namespace :%s has not found. %s", + return nil, fmt.Errorf("error while resolving sandbox grpcRouteref %s in namespace :%s was not found. %s", sandRouteRefs, namespace, err.Error()) } + if !apiReconciler.ods.IsGatewayAvailable(types.NamespacedName{ + Name: string(apiState.SandGRPCRoute.GRPCRouteCombined.Spec.ParentRefs[0].Name), + Namespace: utils.GetNamespace(apiState.SandGRPCRoute.GRPCRouteCombined.Spec.ParentRefs[0].Namespace, + apiState.SandGRPCRoute.GRPCRouteCombined.Namespace), + }) { + return nil, fmt.Errorf("no gateway available for grpcRouteref %s in namespace :%s was not found", + sandRouteRefs, namespace) + } } // Validate resource level extension refs resolved @@ -1156,92 +1172,107 @@ func (apiReconciler *APIReconciler) traverseAPIStateAndUpdateOwnerReferences(ctx // travserse through all the children of this API and trigger update owner reference if apiState.ProdHTTPRoute != nil { for _, httpRoute := range apiState.ProdHTTPRoute.HTTPRoutePartitions { - apiReconciler.retrieveParentAPIsAndUpdateOwnerReferene(ctx, httpRoute) + apiReconciler.retrieveParentAPIsAndUpdateOwnerReference(ctx, httpRoute) } } if apiState.SandHTTPRoute != nil { for _, httpRoute := range apiState.SandHTTPRoute.HTTPRoutePartitions { - apiReconciler.retrieveParentAPIsAndUpdateOwnerReferene(ctx, httpRoute) + apiReconciler.retrieveParentAPIsAndUpdateOwnerReference(ctx, httpRoute) } } if apiState.ProdGQLRoute != nil { for _, gqlRoute := range apiState.ProdGQLRoute.GQLRoutePartitions { - apiReconciler.retrieveParentAPIsAndUpdateOwnerReferene(ctx, gqlRoute) + apiReconciler.retrieveParentAPIsAndUpdateOwnerReference(ctx, gqlRoute) } } if apiState.SandGQLRoute != nil { for _, gqlRoute := range apiState.SandGQLRoute.GQLRoutePartitions { - apiReconciler.retrieveParentAPIsAndUpdateOwnerReferene(ctx, gqlRoute) + apiReconciler.retrieveParentAPIsAndUpdateOwnerReference(ctx, gqlRoute) } } if apiState.ProdGRPCRoute != nil { for _, grpcRoute := range apiState.ProdGRPCRoute.GRPCRoutePartitions { - apiReconciler.retrieveParentAPIsAndUpdateOwnerReferene(ctx, grpcRoute) + apiReconciler.retrieveParentAPIsAndUpdateOwnerReference(ctx, grpcRoute) } } if apiState.SandGRPCRoute != nil { for _, grpcRoute := range apiState.SandGRPCRoute.GRPCRoutePartitions { - apiReconciler.retrieveParentAPIsAndUpdateOwnerReferene(ctx, grpcRoute) + apiReconciler.retrieveParentAPIsAndUpdateOwnerReference(ctx, grpcRoute) } } for _, auth := range apiState.Authentications { - apiReconciler.retrieveParentAPIsAndUpdateOwnerReferene(ctx, &auth) + apiReconciler.retrieveParentAPIsAndUpdateOwnerReference(ctx, &auth) } for _, auth := range apiState.ResourceAuthentications { - apiReconciler.retrieveParentAPIsAndUpdateOwnerReferene(ctx, &auth) + apiReconciler.retrieveParentAPIsAndUpdateOwnerReference(ctx, &auth) } for _, ratelimit := range apiState.RateLimitPolicies { - apiReconciler.retrieveParentAPIsAndUpdateOwnerReferene(ctx, &ratelimit) + apiReconciler.retrieveParentAPIsAndUpdateOwnerReference(ctx, &ratelimit) } for _, ratelimit := range apiState.ResourceRateLimitPolicies { - apiReconciler.retrieveParentAPIsAndUpdateOwnerReferene(ctx, &ratelimit) + apiReconciler.retrieveParentAPIsAndUpdateOwnerReference(ctx, &ratelimit) } for _, apiPolicy := range apiState.APIPolicies { - apiReconciler.retrieveParentAPIsAndUpdateOwnerReferene(ctx, &apiPolicy) + apiReconciler.retrieveParentAPIsAndUpdateOwnerReference(ctx, &apiPolicy) } for _, apiPolicy := range apiState.ResourceAPIPolicies { - apiReconciler.retrieveParentAPIsAndUpdateOwnerReferene(ctx, &apiPolicy) + apiReconciler.retrieveParentAPIsAndUpdateOwnerReference(ctx, &apiPolicy) } for _, interceptorService := range apiState.InterceptorServiceMapping { - apiReconciler.retrieveParentAPIsAndUpdateOwnerReferene(ctx, &interceptorService) + apiReconciler.retrieveParentAPIsAndUpdateOwnerReference(ctx, &interceptorService) } if apiState.ProdHTTPRoute != nil { for _, backend := range apiState.ProdHTTPRoute.BackendMapping { if backend != nil { - apiReconciler.retrieveParentAPIsAndUpdateOwnerReferene(ctx, &backend.Backend) + apiReconciler.retrieveParentAPIsAndUpdateOwnerReference(ctx, &backend.Backend) } } } if apiState.SandHTTPRoute != nil { for _, backend := range apiState.SandHTTPRoute.BackendMapping { if backend != nil { - apiReconciler.retrieveParentAPIsAndUpdateOwnerReferene(ctx, &backend.Backend) + apiReconciler.retrieveParentAPIsAndUpdateOwnerReference(ctx, &backend.Backend) } } } if apiState.ProdGQLRoute != nil { for _, backend := range apiState.ProdGQLRoute.BackendMapping { if backend != nil { - apiReconciler.retrieveParentAPIsAndUpdateOwnerReferene(ctx, &backend.Backend) + apiReconciler.retrieveParentAPIsAndUpdateOwnerReference(ctx, &backend.Backend) } } } if apiState.SandGQLRoute != nil { for _, backend := range apiState.SandGQLRoute.BackendMapping { if backend != nil { - apiReconciler.retrieveParentAPIsAndUpdateOwnerReferene(ctx, &backend.Backend) + apiReconciler.retrieveParentAPIsAndUpdateOwnerReference(ctx, &backend.Backend) + } + } + } + + if apiState.ProdGRPCRoute != nil { + for _, backend := range apiState.ProdGRPCRoute.BackendMapping { + if &backend != nil { + apiReconciler.retrieveParentAPIsAndUpdateOwnerReference(ctx, &backend.Backend) + } + } + } + if apiState.SandGRPCRoute != nil { + for _, backend := range apiState.SandGRPCRoute.BackendMapping { + if &backend != nil { + apiReconciler.retrieveParentAPIsAndUpdateOwnerReference(ctx, &backend.Backend) } } } for _, backendJwt := range apiState.BackendJWTMapping { - apiReconciler.retrieveParentAPIsAndUpdateOwnerReferene(ctx, &backendJwt) + apiReconciler.retrieveParentAPIsAndUpdateOwnerReference(ctx, &backendJwt) } } -func (apiReconciler *APIReconciler) retrieveParentAPIsAndUpdateOwnerReferene(ctx context.Context, obj k8client.Object) { +func (apiReconciler *APIReconciler) retrieveParentAPIsAndUpdateOwnerReference(ctx context.Context, obj k8client.Object) { var requests []reconcile.Request switch obj.(type) { case *dpv1alpha2.Backend: @@ -1813,6 +1844,7 @@ func (apiReconciler *APIReconciler) getAPIsForScope(ctx context.Context, obj k8c if len(httpRouteList.Items) == 0 { loggers.LoggerAPKOperator.Debugf("HTTPRoutes for scope not found: %s", utils.NamespacedName(scope).String()) } + requests := []reconcile.Request{} for item := range httpRouteList.Items { httpRoute := httpRouteList.Items[item] @@ -1830,11 +1862,28 @@ func (apiReconciler *APIReconciler) getAPIsForScope(ctx context.Context, obj k8c if len(gqlRouteList.Items) == 0 { loggers.LoggerAPKOperator.Debugf("GQLRoutes for scope not found: %s", utils.NamespacedName(scope).String()) } + for item := range gqlRouteList.Items { httpRoute := gqlRouteList.Items[item] requests = append(requests, apiReconciler.getAPIForGQLRoute(ctx, &httpRoute)...) } + grpcRouteList := &gwapiv1a2.GRPCRouteList{} + if err := apiReconciler.client.List(ctx, grpcRouteList, &k8client.ListOptions{ + FieldSelector: fields.OneTermEqualSelector(grpcRouteScopeIndex, utils.NamespacedName(scope).String()), + }); err != nil { + loggers.LoggerAPKOperator.ErrorC(logging.PrintError(logging.Error2625, logging.CRITICAL, "Unable to find associated GRPCRoutes: %s", utils.NamespacedName(scope).String())) + return []reconcile.Request{} + } + + if len(grpcRouteList.Items) == 0 { + loggers.LoggerAPKOperator.Debugf("GRPCRoutes for scope not found: %s", utils.NamespacedName(scope).String()) + } + for item := range grpcRouteList.Items { + grpcRoute := grpcRouteList.Items[item] + requests = append(requests, apiReconciler.getAPIForGRPCRoute(ctx, &grpcRoute)...) + } + return requests } @@ -1881,6 +1930,23 @@ func (apiReconciler *APIReconciler) getAPIsForBackend(ctx context.Context, obj k requests = append(requests, apiReconciler.getAPIForGQLRoute(ctx, &gqlRoute)...) } + grpcRouteList := &gwapiv1a2.GRPCRouteList{} + if err := apiReconciler.client.List(ctx, grpcRouteList, &k8client.ListOptions{ + FieldSelector: fields.OneTermEqualSelector(backendGRPCRouteIndex, utils.NamespacedName(backend).String()), + }); err != nil { + loggers.LoggerAPKOperator.ErrorC(logging.PrintError(logging.Error2625, logging.CRITICAL, "Unable to find associated GRPCRoutes: %s", utils.NamespacedName(backend).String())) + return []reconcile.Request{} + } + + if len(grpcRouteList.Items) == 0 { + loggers.LoggerAPKOperator.Debugf("GRPCRoutes for Backend not found: %s", utils.NamespacedName(backend).String()) + } + + for item := range grpcRouteList.Items { + grpcRoute := grpcRouteList.Items[item] + requests = append(requests, apiReconciler.getAPIForGRPCRoute(ctx, &grpcRoute)...) + } + // Create API reconcile events when Backend reffered from InterceptorService interceptorServiceList := &dpv1alpha1.InterceptorServiceList{} if err := apiReconciler.client.List(ctx, interceptorServiceList, &k8client.ListOptions{ @@ -1924,11 +1990,28 @@ func (apiReconciler *APIReconciler) getAPIsForGateway(ctx context.Context, obj k return []reconcile.Request{} } + grpcRouteList := &gwapiv1a2.GRPCRouteList{} + if err := apiReconciler.client.List(ctx, grpcRouteList, &k8client.ListOptions{ + FieldSelector: fields.OneTermEqualSelector(gatewayGRPCRouteIndex, utils.NamespacedName(gateway).String()), + }); err != nil { + loggers.LoggerAPKOperator.ErrorC(logging.PrintError(logging.Error2625, logging.CRITICAL, "Unable to find associated GRPCRoutes: %s", utils.NamespacedName(gateway).String())) + return []reconcile.Request{} + } + + if len(grpcRouteList.Items) == 0 { + loggers.LoggerAPKOperator.Debugf("GRPCRoutes for Gateway not found: %s", utils.NamespacedName(gateway).String()) + return []reconcile.Request{} + } + requests := []reconcile.Request{} for item := range httpRouteList.Items { httpRoute := httpRouteList.Items[item] requests = append(requests, apiReconciler.getAPIForHTTPRoute(ctx, &httpRoute)...) } + for item := range grpcRouteList.Items { + grpcRoute := grpcRouteList.Items[item] + requests = append(requests, apiReconciler.getAPIForGRPCRoute(ctx, &grpcRoute)...) + } return requests } @@ -2005,6 +2088,40 @@ func addIndexes(ctx context.Context, mgr manager.Manager) error { return err } + if err := mgr.GetFieldIndexer().IndexField(ctx, &dpv1beta1.API{}, grpcRouteAPIIndex, + func(rawObj k8client.Object) []string { + api := rawObj.(*dpv1beta1.API) + if api.Spec.APIType != constants.GRPC { + return nil + } + var grpcRoutes []string + if len(api.Spec.Production) > 0 { + for _, ref := range api.Spec.Production[0].RouteRefs { + if ref != "" { + grpcRoutes = append(grpcRoutes, + types.NamespacedName{ + Namespace: api.Namespace, + Name: ref, + }.String()) + } + } + } + if len(api.Spec.Sandbox) > 0 { + for _, ref := range api.Spec.Sandbox[0].RouteRefs { + if ref != "" { + grpcRoutes = append(grpcRoutes, + types.NamespacedName{ + Namespace: api.Namespace, + Name: ref, + }.String()) + } + } + } + return grpcRoutes + }); err != nil { + return err + } + if err := mgr.GetFieldIndexer().IndexField(ctx, &dpv1beta1.API{}, configMapAPIDefinition, func(rawObj k8client.Object) []string { api := rawObj.(*dpv1beta1.API) @@ -2102,6 +2219,27 @@ func addIndexes(ctx context.Context, mgr manager.Manager) error { return err } + // Backend to GRPCRoute indexer + if err := mgr.GetFieldIndexer().IndexField(ctx, &gwapiv1a2.GRPCRoute{}, backendGRPCRouteIndex, + func(rawObj k8client.Object) []string { + grpcRoute := rawObj.(*gwapiv1a2.GRPCRoute) + var backends []string + for _, rule := range grpcRoute.Spec.Rules { + for _, backendRef := range rule.BackendRefs { + if backendRef.Kind != nil && *backendRef.Kind == constants.KindBackend { + backends = append(backends, types.NamespacedName{ + Namespace: utils.GetNamespace(backendRef.Namespace, + grpcRoute.ObjectMeta.Namespace), + Name: string(backendRef.Name), + }.String()) + } + } + } + return backends + }); err != nil { + return err + } + // Gateway to HTTPRoute indexer if err := mgr.GetFieldIndexer().IndexField(ctx, &gwapiv1.HTTPRoute{}, gatewayHTTPRouteIndex, func(rawObj k8client.Object) []string { @@ -2119,6 +2257,23 @@ func addIndexes(ctx context.Context, mgr manager.Manager) error { return err } + //Gateway to GRPCRoute indexer + if err := mgr.GetFieldIndexer().IndexField(ctx, &gwapiv1a2.GRPCRoute{}, gatewayGRPCRouteIndex, + func(rawObj k8client.Object) []string { + grpcRoute := rawObj.(*gwapiv1a2.GRPCRoute) + var gateways []string + for _, parentRef := range grpcRoute.Spec.ParentRefs { + gateways = append(gateways, types.NamespacedName{ + Namespace: utils.GetNamespace(parentRef.Namespace, + grpcRoute.Namespace), + Name: string(parentRef.Name), + }.String()) + } + return gateways + }); err != nil { + return err + } + // ConfigMap to Backend indexer if err := mgr.GetFieldIndexer().IndexField(ctx, &dpv1alpha2.Backend{}, configMapBackend, func(rawObj k8client.Object) []string { diff --git a/adapter/internal/operator/synchronizer/synchronizer.go b/adapter/internal/operator/synchronizer/synchronizer.go index 7f67c3c5d4..450e56d333 100644 --- a/adapter/internal/operator/synchronizer/synchronizer.go +++ b/adapter/internal/operator/synchronizer/synchronizer.go @@ -111,10 +111,10 @@ func HandleAPILifeCycleEvents(ch *chan *APIEvent, successChannel *chan SuccessEv func undeployAPIInGateway(apiEvent *APIEvent) error { var err error apiState := apiEvent.Events[0] - if apiState.APIDefinition.Spec.APIType == "REST" { + if apiState.APIDefinition.Spec.APIType == constants.REST { err = undeployRestAPIInGateway(apiState) } - if apiState.APIDefinition.Spec.APIType == "GraphQL" { + if apiState.APIDefinition.Spec.APIType == constants.GRAPHQL { err = undeployGQLAPIInGateway(apiState) } @@ -204,7 +204,7 @@ func deployMultipleAPIsInGateway(event *APIEvent, successChannel *chan SuccessEv } } - if apiState.APIDefinition.Spec.APIType == "gRPC" { + if apiState.APIDefinition.Spec.APIType == constants.GRPC { if apiState.ProdGRPCRoute != nil { _, updatedLabels, err := updateInternalMapsFromGRPCRoute(apiState, apiState.ProdGRPCRoute, constants.Production) if err != nil { diff --git a/common-controller/internal/operator/controllers/dp/ratelimitpolicy_controller.go b/common-controller/internal/operator/controllers/dp/ratelimitpolicy_controller.go index 369defe31a..a4378ec264 100644 --- a/common-controller/internal/operator/controllers/dp/ratelimitpolicy_controller.go +++ b/common-controller/internal/operator/controllers/dp/ratelimitpolicy_controller.go @@ -67,7 +67,7 @@ const ( // NewratelimitController creates a new ratelimitcontroller instance. func NewratelimitController(mgr manager.Manager, ratelimitStore *cache.RatelimitDataStore) error { - ratelimitReconsiler := &RateLimitPolicyReconciler{ + ratelimitReconciler := &RateLimitPolicyReconciler{ client: mgr.GetClient(), ods: ratelimitStore, } @@ -78,7 +78,7 @@ func NewratelimitController(mgr manager.Manager, ratelimitStore *cache.Ratelimit return err } - c, err := controller.New(constants.RatelimitController, mgr, controller.Options{Reconciler: ratelimitReconsiler}) + c, err := controller.New(constants.RatelimitController, mgr, controller.Options{Reconciler: ratelimitReconciler}) if err != nil { loggers.LoggerAPKOperator.ErrorC(logging.PrintError(logging.Error2663, logging.BLOCKER, "Error creating Ratelimit controller: %v", err.Error())) @@ -89,14 +89,14 @@ func NewratelimitController(mgr manager.Manager, ratelimitStore *cache.Ratelimit predicates := []predicate.Predicate{predicate.NewPredicateFuncs(utils.FilterByNamespaces(conf.CommonController.Operator.Namespaces))} if err := c.Watch(source.Kind(mgr.GetCache(), &dpv1beta1.API{}), - handler.EnqueueRequestsFromMapFunc(ratelimitReconsiler.getRatelimitForAPI), predicates...); err != nil { + handler.EnqueueRequestsFromMapFunc(ratelimitReconciler.getRatelimitForAPI), predicates...); err != nil { loggers.LoggerAPKOperator.ErrorC(logging.PrintError(logging.Error2611, logging.BLOCKER, "Error watching API resources: %v", err)) return err } if err := c.Watch(source.Kind(mgr.GetCache(), &gwapiv1.HTTPRoute{}), - handler.EnqueueRequestsFromMapFunc(ratelimitReconsiler.getRatelimitForHTTPRoute), predicates...); err != nil { + handler.EnqueueRequestsFromMapFunc(ratelimitReconciler.getRatelimitForHTTPRoute), predicates...); err != nil { loggers.LoggerAPKOperator.ErrorC(logging.PrintError(logging.Error2613, logging.BLOCKER, "Error watching HTTPRoute resources: %v", err)) return err @@ -125,7 +125,7 @@ func NewratelimitController(mgr manager.Manager, ratelimitStore *cache.Ratelimit // // For more details, check Reconcile and its Result here: // - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.15.0/pkg/reconcile -func (ratelimitReconsiler *RateLimitPolicyReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { +func (ratelimitReconciler *RateLimitPolicyReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { _ = log.FromContext(ctx) // Check whether the Ratelimit CR exist, if not consider as a DELETE event. loggers.LoggerAPKOperator.Infof("Reconciling ratelimit...") @@ -134,18 +134,18 @@ func (ratelimitReconsiler *RateLimitPolicyReconciler) Reconcile(ctx context.Cont var ratelimitPolicy dpv1alpha3.RateLimitPolicy // Check k8s RatelimitPolicy Availbility - if err := ratelimitReconsiler.client.Get(ctx, ratelimitKey, &ratelimitPolicy); err != nil { - resolveRateLimitAPIPolicyList, found := ratelimitReconsiler.ods.GetResolveRatelimitPolicy(req.NamespacedName) + if err := ratelimitReconciler.client.Get(ctx, ratelimitKey, &ratelimitPolicy); err != nil { + resolveRateLimitAPIPolicyList, found := ratelimitReconciler.ods.GetResolveRatelimitPolicy(req.NamespacedName) // If availble in cache Delete cache and xds if found && k8error.IsNotFound(err) { - ratelimitReconsiler.ods.DeleteResolveRatelimitPolicy(req.NamespacedName) + ratelimitReconciler.ods.DeleteResolveRatelimitPolicy(req.NamespacedName) xds.DeleteAPILevelRateLimitPolicies(resolveRateLimitAPIPolicyList) xds.DeleteResourceLevelRateLimitPolicies(resolveRateLimitAPIPolicyList) xds.UpdateRateLimiterPolicies(conf.CommonController.Server.Label) } - resolveCustomRateLimitPolicy, foundCustom := ratelimitReconsiler.ods.GetCachedCustomRatelimitPolicy(req.NamespacedName) + resolveCustomRateLimitPolicy, foundCustom := ratelimitReconciler.ods.GetCachedCustomRatelimitPolicy(req.NamespacedName) if foundCustom && k8error.IsNotFound(err) { - ratelimitReconsiler.ods.DeleteCachedCustomRatelimitPolicy(req.NamespacedName) + ratelimitReconciler.ods.DeleteCachedCustomRatelimitPolicy(req.NamespacedName) logger.Debug("Deleting CustomRateLimitPolicy : ", resolveCustomRateLimitPolicy) xds.DeleteCustomRateLimitPolicies(resolveCustomRateLimitPolicy) xds.UpdateRateLimiterPolicies(conf.CommonController.Server.Label) @@ -167,8 +167,8 @@ func (ratelimitReconsiler *RateLimitPolicyReconciler) Reconcile(ctx context.Cont } if ratelimitPolicy.Spec.Override != nil && ratelimitPolicy.Spec.Override.Custom != nil { - var customRateLimitPolicy = ratelimitReconsiler.marshelCustomRateLimit(ctx, ratelimitKey, ratelimitPolicy) - ratelimitReconsiler.ods.AddorUpdateCustomRatelimitToStore(ratelimitKey, customRateLimitPolicy) + var customRateLimitPolicy = ratelimitReconciler.marshelCustomRateLimit(ctx, ratelimitKey, ratelimitPolicy) + ratelimitReconciler.ods.AddorUpdateCustomRatelimitToStore(ratelimitKey, customRateLimitPolicy) xds.UpdateRateLimitXDSCacheForCustomPolicies(customRateLimitPolicy) xds.UpdateRateLimiterPolicies(conf.CommonController.Server.Label) } else if ratelimitPolicy.Spec.Override != nil && ratelimitPolicy.Spec.Override.Subscription != nil { @@ -177,10 +177,10 @@ func (ratelimitReconsiler *RateLimitPolicyReconciler) Reconcile(ctx context.Cont xds.UpdateRateLimitXDSCacheForSubscriptionPolicies(resolveSubscriptionRatelimitPolicy) xds.UpdateRateLimiterPolicies(conf.CommonController.Server.Label) } else { - if resolveRatelimitPolicyList, err := ratelimitReconsiler.marshelRateLimit(ctx, ratelimitKey, ratelimitPolicy); err != nil { + if resolveRatelimitPolicyList, err := ratelimitReconciler.marshelRateLimit(ctx, ratelimitKey, ratelimitPolicy); err != nil { return ctrl.Result{}, err } else if len(resolveRatelimitPolicyList) > 0 { - ratelimitReconsiler.ods.AddorUpdateResolveRatelimitToStore(ratelimitKey, resolveRatelimitPolicyList) + ratelimitReconciler.ods.AddorUpdateResolveRatelimitToStore(ratelimitKey, resolveRatelimitPolicyList) xds.UpdateRateLimitXDSCache(resolveRatelimitPolicyList) xds.UpdateRateLimiterPolicies(conf.CommonController.Server.Label) } @@ -189,7 +189,7 @@ func (ratelimitReconsiler *RateLimitPolicyReconciler) Reconcile(ctx context.Cont return ctrl.Result{}, nil } -func (ratelimitReconsiler *RateLimitPolicyReconciler) getRatelimitForAPI(ctx context.Context, obj k8client.Object) []reconcile.Request { +func (ratelimitReconciler *RateLimitPolicyReconciler) getRatelimitForAPI(ctx context.Context, obj k8client.Object) []reconcile.Request { api, ok := obj.(*dpv1beta1.API) if !ok { loggers.LoggerAPKOperator.ErrorC(logging.PrintError(logging.Error2622, logging.TRIVIAL, @@ -200,7 +200,7 @@ func (ratelimitReconsiler *RateLimitPolicyReconciler) getRatelimitForAPI(ctx con requests := []reconcile.Request{} ratelimitPolicyList := &dpv1alpha3.RateLimitPolicyList{} - if err := ratelimitReconsiler.client.List(ctx, ratelimitPolicyList, &k8client.ListOptions{ + if err := ratelimitReconciler.client.List(ctx, ratelimitPolicyList, &k8client.ListOptions{ FieldSelector: fields.OneTermEqualSelector(apiRateLimitIndex, NamespacedName(api).String()), }); err != nil { return []reconcile.Request{} @@ -208,14 +208,14 @@ func (ratelimitReconsiler *RateLimitPolicyReconciler) getRatelimitForAPI(ctx con for item := range ratelimitPolicyList.Items { ratelimitPolicy := ratelimitPolicyList.Items[item] - requests = append(requests, ratelimitReconsiler.AddRatelimitRequest(&ratelimitPolicy)...) + requests = append(requests, ratelimitReconciler.AddRatelimitRequest(&ratelimitPolicy)...) } return requests } // AddRatelimitRequest adds a request to reconcile for the given ratelimit policy -func (ratelimitReconsiler *RateLimitPolicyReconciler) AddRatelimitRequest(obj k8client.Object) []reconcile.Request { +func (ratelimitReconciler *RateLimitPolicyReconciler) AddRatelimitRequest(obj k8client.Object) []reconcile.Request { ratelimitPolicy, ok := obj.(*dpv1alpha3.RateLimitPolicy) if !ok { loggers.LoggerAPKOperator.ErrorC(logging.PrintError(logging.Error2622, logging.TRIVIAL, @@ -231,7 +231,7 @@ func (ratelimitReconsiler *RateLimitPolicyReconciler) AddRatelimitRequest(obj k8 }} } -func (ratelimitReconsiler *RateLimitPolicyReconciler) getRatelimitForHTTPRoute(ctx context.Context, obj k8client.Object) []reconcile.Request { +func (ratelimitReconciler *RateLimitPolicyReconciler) getRatelimitForHTTPRoute(ctx context.Context, obj k8client.Object) []reconcile.Request { httpRoute, ok := obj.(*gwapiv1.HTTPRoute) if !ok { loggers.LoggerAPKOperator.ErrorC(logging.PrintError(logging.Error2622, logging.TRIVIAL, @@ -242,14 +242,14 @@ func (ratelimitReconsiler *RateLimitPolicyReconciler) getRatelimitForHTTPRoute(c requests := []reconcile.Request{} ratelimitPolicyList := &dpv1alpha3.RateLimitPolicyList{} - if err := ratelimitReconsiler.client.List(ctx, ratelimitPolicyList, &k8client.ListOptions{ + if err := ratelimitReconciler.client.List(ctx, ratelimitPolicyList, &k8client.ListOptions{ FieldSelector: fields.OneTermEqualSelector(httprouteRateLimitIndex, NamespacedName(httpRoute).String()), }); err != nil { return []reconcile.Request{} } for item := range ratelimitPolicyList.Items { ratelimitPolicy := ratelimitPolicyList.Items[item] - requests = append(requests, ratelimitReconsiler.AddRatelimitRequest(&ratelimitPolicy)...) + requests = append(requests, ratelimitReconciler.AddRatelimitRequest(&ratelimitPolicy)...) } return requests @@ -345,7 +345,7 @@ func (ratelimitReconciler *RateLimitPolicyReconciler) marshelRateLimit(ctx conte return policyList, nil } -func (ratelimitReconsiler *RateLimitPolicyReconciler) getHTTPRouteResourceList(ctx context.Context, ratelimitKey types.NamespacedName, +func (ratelimitReconciler *RateLimitPolicyReconciler) getHTTPRouteResourceList(ctx context.Context, ratelimitKey types.NamespacedName, ratelimitPolicy dpv1alpha3.RateLimitPolicy, httpRefs []string) ([]dpv1alpha1.ResolveResource, error) { var resolveResourceList []dpv1alpha1.ResolveResource @@ -353,7 +353,7 @@ func (ratelimitReconsiler *RateLimitPolicyReconciler) getHTTPRouteResourceList(c for _, ref := range httpRefs { if ref != "" { - if err := ratelimitReconsiler.client.Get(ctx, types.NamespacedName{ + if err := ratelimitReconciler.client.Get(ctx, types.NamespacedName{ Namespace: ratelimitKey.Namespace, Name: ref}, &httpRoute); err != nil { @@ -391,7 +391,7 @@ func (ratelimitReconsiler *RateLimitPolicyReconciler) getHTTPRouteResourceList(c return resolveResourceList, nil } -func (ratelimitReconsiler *RateLimitPolicyReconciler) marshelCustomRateLimit(ctx context.Context, ratelimitKey types.NamespacedName, +func (ratelimitReconciler *RateLimitPolicyReconciler) marshelCustomRateLimit(ctx context.Context, ratelimitKey types.NamespacedName, ratelimitPolicy dpv1alpha3.RateLimitPolicy) dpv1alpha1.CustomRateLimitPolicyDef { var customRateLimitPolicy dpv1alpha1.CustomRateLimitPolicyDef // Custom Rate limit policy @@ -464,8 +464,8 @@ func GetNamespace(namespace *gwapiv1.Namespace, defaultNamespace string) string } // SetupWithManager sets up the controller with the Manager. -func (ratelimitReconsiler *RateLimitPolicyReconciler) SetupWithManager(mgr ctrl.Manager) error { +func (ratelimitReconciler *RateLimitPolicyReconciler) SetupWithManager(mgr ctrl.Manager) error { return ctrl.NewControllerManagedBy(mgr). For(&dpv1alpha3.RateLimitPolicy{}). - Complete(ratelimitReconsiler) + Complete(ratelimitReconciler) } diff --git a/common-go-libs/apis/dp/v1beta1/api_types.go b/common-go-libs/apis/dp/v1beta1/api_types.go index 96e929bfe8..943e9a1341 100644 --- a/common-go-libs/apis/dp/v1beta1/api_types.go +++ b/common-go-libs/apis/dp/v1beta1/api_types.go @@ -80,9 +80,9 @@ type APISpec struct { Sandbox []EnvConfig `json:"sandbox"` // APIType denotes the type of the API. - // Possible values could be REST, GraphQL, gRPC + // Possible values could be REST, GraphQL, GRPC // - // +kubebuilder:validation:Enum=REST;GraphQL;gRPC + // +kubebuilder:validation:Enum=REST;GraphQL;GRPC APIType string `json:"apiType"` // BasePath denotes the basepath of the API. diff --git a/common-go-libs/config/crd/bases/dp.wso2.com_apis.yaml b/common-go-libs/config/crd/bases/dp.wso2.com_apis.yaml index b3dee8a220..1fa8ea6721 100644 --- a/common-go-libs/config/crd/bases/dp.wso2.com_apis.yaml +++ b/common-go-libs/config/crd/bases/dp.wso2.com_apis.yaml @@ -420,11 +420,11 @@ spec: type: array apiType: description: APIType denotes the type of the API. Possible values - could be REST, GraphQL, gRPC + could be REST, GraphQL, GRPC enum: - REST - GraphQL - - gRPC + - GRPC type: string apiVersion: description: APIVersion is the version number of the API. diff --git a/gateway/enforcer/org.wso2.apk.enforcer/src/main/java/org/wso2/apk/enforcer/api/APIFactory.java b/gateway/enforcer/org.wso2.apk.enforcer/src/main/java/org/wso2/apk/enforcer/api/APIFactory.java index aa6e98eab2..89ec04d640 100644 --- a/gateway/enforcer/org.wso2.apk.enforcer/src/main/java/org/wso2/apk/enforcer/api/APIFactory.java +++ b/gateway/enforcer/org.wso2.apk.enforcer/src/main/java/org/wso2/apk/enforcer/api/APIFactory.java @@ -73,7 +73,6 @@ public void addApis(List apis) { // String apiKey = getApiKey(webSocketAPI); // newApis.put(apiKey, webSocketAPI); // } else - System.out.println(api.getApiType()); if (APIConstants.ApiType.GRAPHQL.equals(api.getApiType())) { GraphQLAPI graphQLAPI = new GraphQLAPI(); graphQLAPI.init(api); diff --git a/gateway/enforcer/org.wso2.apk.enforcer/src/main/java/org/wso2/apk/enforcer/constants/APIConstants.java b/gateway/enforcer/org.wso2.apk.enforcer/src/main/java/org/wso2/apk/enforcer/constants/APIConstants.java index 917944c7b9..ddb0a747f4 100644 --- a/gateway/enforcer/org.wso2.apk.enforcer/src/main/java/org/wso2/apk/enforcer/constants/APIConstants.java +++ b/gateway/enforcer/org.wso2.apk.enforcer/src/main/java/org/wso2/apk/enforcer/constants/APIConstants.java @@ -270,7 +270,7 @@ public static class ApiType { public static final String WEB_SOCKET = "WS"; public static final String GRAPHQL = "GraphQL"; - public static final String GRPC = "gRPC"; + public static final String GRPC = "GRPC"; } /** diff --git a/helm-charts/templates/crds/dp.wso2.com_apis.yaml b/helm-charts/templates/crds/dp.wso2.com_apis.yaml index 9db6f95a04..488a91b203 100644 --- a/helm-charts/templates/crds/dp.wso2.com_apis.yaml +++ b/helm-charts/templates/crds/dp.wso2.com_apis.yaml @@ -433,11 +433,11 @@ spec: type: array apiType: description: APIType denotes the type of the API. Possible values - could be REST, GraphQL, gRPC + could be REST, GraphQL, GRPC enum: - REST - GraphQL - - gRPC + - GRPC type: string apiVersion: description: APIVersion is the version number of the API. diff --git a/helm-charts/values.yaml b/helm-charts/values.yaml index 799b18f26c..6b83d1f197 100644 --- a/helm-charts/values.yaml +++ b/helm-charts/values.yaml @@ -59,8 +59,7 @@ wso2: gateway: listener: hostname: "gw.wso2.com" - service: - {} + service: {} # secretName: "idp-tls" # partitionServer: # enabled: false @@ -246,16 +245,16 @@ wso2: image: wso2/apk-enforcer:1.2.0-m1 security: sslHostname: "enforcer" - # logging: - # level: DEBUG - # configs: - # tls: - # secretName: "router-cert" - # certKeyFilename: "" - # certFilename: "" - # JWKSClient: - # skipSSLVerification: false - # hostnameVerifier: "AllowAll" +# logging: +# level: DEBUG +# configs: +# tls: +# secretName: "router-cert" +# certKeyFilename: "" +# certFilename: "" +# JWKSClient: +# skipSSLVerification: false +# hostnameVerifier: "AllowAll" metrics: enabled: false diff --git a/runtime/config-deployer-service/ballerina/APIClient.bal b/runtime/config-deployer-service/ballerina/APIClient.bal index a5bda731ef..b6b0ce2ecb 100644 --- a/runtime/config-deployer-service/ballerina/APIClient.bal +++ b/runtime/config-deployer-service/ballerina/APIClient.bal @@ -138,11 +138,6 @@ public class APIClient { _ = check self.setRoute(apiArtifact, apkConf, createdEndpoints.hasKey(PRODUCTION_TYPE) ? createdEndpoints.get(PRODUCTION_TYPE) : (), uniqueId, PRODUCTION_TYPE, organization); _ = check self.setRoute(apiArtifact, apkConf, createdEndpoints.hasKey(SANDBOX_TYPE) ? createdEndpoints.get(SANDBOX_TYPE) : (), uniqueId, SANDBOX_TYPE, organization); string|json generatedSwagger = check self.retrieveGeneratedSwaggerDefinition(apkConf, definition); - if generatedSwagger is string { - log:printInfo(generatedSwagger); - } else { - log:printInfo(generatedSwagger.toJsonString()); - } check self.retrieveGeneratedConfigmapForDefinition(apiArtifact, apkConf, generatedSwagger, uniqueId, organization); self.generateAndSetAPICRArtifact(apiArtifact, apkConf, organization); _ = check self.generateAndSetPolicyCRArtifact(apiArtifact, apkConf, organization); @@ -516,8 +511,6 @@ public class APIClient { private isolated function getAPIType(string apiType) returns string { if apiType.toUpperAscii() == "GRAPHQL" { return "GraphQL"; - } else if apiType.toUpperAscii() == "GRPC" { - return "gRPC"; } return apiType; } diff --git a/runtime/config-deployer-service/ballerina/Ballerina.toml b/runtime/config-deployer-service/ballerina/Ballerina.toml index 5aca2f1efa..c71d1d9355 100644 --- a/runtime/config-deployer-service/ballerina/Ballerina.toml +++ b/runtime/config-deployer-service/ballerina/Ballerina.toml @@ -389,9 +389,4 @@ version = "20231013" [[platform.java11.dependency]] groupId = "commons-lang" artifactId = "commons-lang" -version = "2.4" - -[[platform.java11.dependency]] -groupId = "org.wso2.apk" -artifactId = "org.wso2.apk.config" -version = "1.1.0-SNAPSHOT" +version = "2.4" \ No newline at end of file diff --git a/runtime/config-deployer-service/ballerina/ConfigGenreatorClient.bal b/runtime/config-deployer-service/ballerina/ConfigGenreatorClient.bal index 07f4f649ba..ac835cb147 100644 --- a/runtime/config-deployer-service/ballerina/ConfigGenreatorClient.bal +++ b/runtime/config-deployer-service/ballerina/ConfigGenreatorClient.bal @@ -189,6 +189,14 @@ public class ConfigGeneratorClient { string yamlString = check self.convertJsonToYaml(gqlRoute.toJsonString()); _ = check self.storeFile(yamlString, gqlRoute.metadata.name, zipDir); } + foreach model:GRPCRoute grpcRoute in apiArtifact.productionGrpcRoutes { + string yamlString = check self.convertJsonToYaml(grpcRoute.toJsonString()); + _ = check self.storeFile(yamlString, grpcRoute.metadata.name, zipDir); + } + foreach model:GRPCRoute grpcRoute in apiArtifact.sandboxGrpcRoutes { + string yamlString = check self.convertJsonToYaml(grpcRoute.toJsonString()); + _ = check self.storeFile(yamlString, grpcRoute.metadata.name, zipDir); + } foreach model:Backend backend in apiArtifact.backendServices { string yamlString = check self.convertJsonToYaml(backend.toJsonString()); _ = check self.storeFile(yamlString, backend.metadata.name, zipDir); diff --git a/runtime/config-deployer-service/ballerina/DeployerClient.bal b/runtime/config-deployer-service/ballerina/DeployerClient.bal index 943a525b47..3da0d6f928 100644 --- a/runtime/config-deployer-service/ballerina/DeployerClient.bal +++ b/runtime/config-deployer-service/ballerina/DeployerClient.bal @@ -84,7 +84,6 @@ public class DeployerClient { private isolated function deployAPIToK8s(model:APIArtifact apiArtifact) returns commons:APKError|model:API { do { - log:printInfo("DEPLOYING API TO K8s"); model:Partition apiPartition; model:API? existingAPI; model:Partition|() availablePartitionForAPI = check partitionResolver.getAvailablePartitionForAPI(apiArtifact.uniqueId, apiArtifact.organization); @@ -125,7 +124,6 @@ public class DeployerClient { check self.deployInterceptorServiceCRs(apiArtifact, ownerReference); check self.deployBackendJWTConfigs(apiArtifact, ownerReference); check self.deployAPIPolicyCRs(apiArtifact, ownerReference); - log:printInfo("REACHED THE POINT OF DEPLOYING ROUTES"); check self.deployRoutes(apiArtifact.productionHttpRoutes, apiArtifact.productionGqlRoutes, apiArtifact.productionGrpcRoutes, apiArtifact?.namespace, ownerReference); check self.deployRoutes(apiArtifact.sandboxHttpRoutes, apiArtifact.sandboxGqlRoutes, apiArtifact.sandboxGrpcRoutes, apiArtifact?.namespace, ownerReference); @@ -142,15 +140,12 @@ public class DeployerClient { return e909028(); } } else { - log:printInfo("NOT AN API??"); return e909028(); } } on fail var e { if e is commons:APKError { return e; } - log:printInfo("DEPLOYMENT FAAAAILED"); - log:printError("Internal Error occured while deploying API", e); return e909028(); } @@ -331,7 +326,6 @@ public class DeployerClient { if k8sAPIByNameAndNamespace is model:API { k8sAPI.metadata.resourceVersion = k8sAPIByNameAndNamespace.metadata.resourceVersion; http:Response deployAPICRResult = check updateAPICR(k8sAPI, apiArtifact?.namespace); - log:printInfo(deployAPICRResult.statusCode.toString()); if deployAPICRResult.statusCode == http:STATUS_OK { json responsePayLoad = check deployAPICRResult.getJsonPayload(); log:printDebug("Updated K8sAPI Successfully" + responsePayLoad.toJsonString()); diff --git a/runtime/config-deployer-service/ballerina/K8sClient.bal b/runtime/config-deployer-service/ballerina/K8sClient.bal index cf71e4b607..f0d10722a2 100644 --- a/runtime/config-deployer-service/ballerina/K8sClient.bal +++ b/runtime/config-deployer-service/ballerina/K8sClient.bal @@ -20,7 +20,6 @@ import ballerina/http; // under the License. // import ballerina/io; -import ballerina/log; import ballerina/url; import wso2/apk_common_lib as commons; @@ -119,7 +118,6 @@ isolated function deployAPICR(model:API api, string namespace) returns http:Resp isolated function updateAPICR(model:API api, string namespace) returns http:Response|http:ClientError { string endpoint = "/apis/dp.wso2.com/v1beta1/namespaces/" + namespace + "/apis/" + api.metadata.name; - log:printInfo("UPDATING API CR"); return k8sApiServerEp->put(endpoint, api, targetType = http:Response); } diff --git a/runtime/config-deployer-service/java/src/main/java/org/wso2/apk/config/DefinitionParserFactory.java b/runtime/config-deployer-service/java/src/main/java/org/wso2/apk/config/DefinitionParserFactory.java index 3041eaa22e..5f4476d8cd 100644 --- a/runtime/config-deployer-service/java/src/main/java/org/wso2/apk/config/DefinitionParserFactory.java +++ b/runtime/config-deployer-service/java/src/main/java/org/wso2/apk/config/DefinitionParserFactory.java @@ -25,7 +25,7 @@ public static APIDefinition getParser(API api) { if (APIConstants.ParserType.REST.name().equals(api.getType()) || APIConstants.ParserType.GRAPHQL.name().equals(api.getType())) { return new OAS3Parser(); - } else if (APIConstants.ParserType.GRPC.name().equals(api.getType())) { + } else if (APIConstants.ParserType.GRPC.name().equals(api.getType().toUpperCase())) { return new ProtoParser(); } else if (APIConstants.ParserType.ASYNC.name().equals(api.getType())) { return new AsyncApiParser(); @@ -37,7 +37,7 @@ public static APIDefinition getParser(String apiType) { if (APIConstants.ParserType.REST.name().equals(apiType) || APIConstants.ParserType.GRAPHQL.name().equals(apiType)) { return new OAS3Parser(); - } else if (APIConstants.ParserType.GRPC.name().equals(apiType)) { + } else if (APIConstants.ParserType.GRPC.name().equals(apiType.toUpperCase())) { return new ProtoParser(); } else if ("ASYNC".equals(apiType)) { return new AsyncApiParser(); From a9bd97debb05b61660c436696ed740e63cb0896a Mon Sep 17 00:00:00 2001 From: sgayangi Date: Wed, 4 Sep 2024 12:08:51 +0530 Subject: [PATCH 08/17] Add parser for proto files --- .../org/wso2/apk/enforcer/api/APIFactory.java | 1 - .../ballerina/APIClient.bal | 2 +- .../ballerina/ConfigGenreatorClient.bal | 14 +- .../APIDefinitionValidationResponse.bal | 32 +- .../RuntimeAPICommonUtil.bal | 19 +- .../apk/config/DefinitionParserFactory.java | 3 +- .../wso2/apk/config/RuntimeAPICommonUtil.java | 53 +- .../api/APIDefinitionValidationResponse.java | 11 +- .../apk/config/definitions/OASParserUtil.java | 28 -- .../apk/config/definitions/ProtoParser.java | 460 +++++++++--------- .../config/definitions/ProtoParserUtil.java | 31 +- .../java/org/wso2/apk/config/model/API.java | 6 +- .../apk/config/model/proto/ProtoFile.java | 71 +++ .../wso2/apk/config/model/proto/Service.java | 32 ++ 14 files changed, 442 insertions(+), 321 deletions(-) create mode 100644 runtime/config-deployer-service/java/src/main/java/org/wso2/apk/config/model/proto/ProtoFile.java create mode 100644 runtime/config-deployer-service/java/src/main/java/org/wso2/apk/config/model/proto/Service.java diff --git a/gateway/enforcer/org.wso2.apk.enforcer/src/main/java/org/wso2/apk/enforcer/api/APIFactory.java b/gateway/enforcer/org.wso2.apk.enforcer/src/main/java/org/wso2/apk/enforcer/api/APIFactory.java index 89ec04d640..ec2dc512e0 100644 --- a/gateway/enforcer/org.wso2.apk.enforcer/src/main/java/org/wso2/apk/enforcer/api/APIFactory.java +++ b/gateway/enforcer/org.wso2.apk.enforcer/src/main/java/org/wso2/apk/enforcer/api/APIFactory.java @@ -138,7 +138,6 @@ public byte[] getAPIDefinition(final String basePath, final String version, fina public ResourceConfig getMatchedResource(API api, String matchedResourcePath, String method) { List resourceConfigList = api.getAPIConfig().getResources(); - System.out.println(APIConstants.ApiType.GRPC); if (APIConstants.ApiType.GRPC.equals(api.getAPIConfig().getApiType())) { return resourceConfigList.stream() .filter(resourceConfig -> resourceConfig.getPath().equals(matchedResourcePath)) diff --git a/runtime/config-deployer-service/ballerina/APIClient.bal b/runtime/config-deployer-service/ballerina/APIClient.bal index b6b0ce2ecb..f03c001bf1 100644 --- a/runtime/config-deployer-service/ballerina/APIClient.bal +++ b/runtime/config-deployer-service/ballerina/APIClient.bal @@ -290,7 +290,7 @@ public class APIClient { isolated function returnFullGRPCBasePath(string basePath, string 'version) returns string { string fullBasePath = basePath; - if (!string:endsWith(basePath, 'version)) { + if !string:endsWith(basePath, 'version) { fullBasePath = string:'join(".", basePath, 'version); } return fullBasePath; diff --git a/runtime/config-deployer-service/ballerina/ConfigGenreatorClient.bal b/runtime/config-deployer-service/ballerina/ConfigGenreatorClient.bal index ac835cb147..d56bd2ac3a 100644 --- a/runtime/config-deployer-service/ballerina/ConfigGenreatorClient.bal +++ b/runtime/config-deployer-service/ballerina/ConfigGenreatorClient.bal @@ -43,7 +43,17 @@ public class ConfigGeneratorClient { } if validateAndRetrieveDefinitionResult is runtimeapi:APIDefinitionValidationResponse { if validateAndRetrieveDefinitionResult.isValid() { - runtimeModels:API apiFromDefinition = check runtimeUtil:RuntimeAPICommonUtil_getAPIFromDefinition(validateAndRetrieveDefinitionResult.getContent(), apiType); + runtimeModels:API apiFromDefinition; + if apiType == API_TYPE_GRPC { + string fileName = ""; + if definitionBody.definition is record {|byte[] fileContent; string fileName; anydata...;|} { + record {|byte[] fileContent; string fileName; anydata...;|} definition = definitionBody.definition; + fileName = definition.fileName; + } + apiFromDefinition = check runtimeUtil:RuntimeAPICommonUtil_getGRPCAPIFromProtoDefinition(check validateAndRetrieveDefinitionResult.getProtoContent(), fileName); + } else { + apiFromDefinition = check runtimeUtil:RuntimeAPICommonUtil_getAPIFromDefinition(validateAndRetrieveDefinitionResult.getContent(), apiType); + } apiFromDefinition.setType(apiType); APIClient apiclient = new (); APKConf generatedAPKConf = check apiclient.fromAPIModelToAPKConf(apiFromDefinition); @@ -121,7 +131,7 @@ public class ConfigGeneratorClient { if response.statusCode == 200 { return response.getTextPayload(); } else { - log:printError("Error occured while retrieving the definition from the url: " + url, statusCode = response.statusCode); + log:printError("Error occurred while retrieving the definition from the url: " + url, statusCode = response.statusCode); } } return e909044(); diff --git a/runtime/config-deployer-service/ballerina/modules/org.wso2.apk.config.api/APIDefinitionValidationResponse.bal b/runtime/config-deployer-service/ballerina/modules/org.wso2.apk.config.api/APIDefinitionValidationResponse.bal index 46d4d7470b..7f4bd14ecb 100644 --- a/runtime/config-deployer-service/ballerina/modules/org.wso2.apk.config.api/APIDefinitionValidationResponse.bal +++ b/runtime/config-deployer-service/ballerina/modules/org.wso2.apk.config.api/APIDefinitionValidationResponse.bal @@ -1,7 +1,9 @@ -import ballerina/jballerina.java; import config_deployer_service.java.lang as javalang; import config_deployer_service.java.util as javautil; +import ballerina/jballerina.java; +import ballerina/jballerina.java.arrays as jarrays; + # Ballerina class mapping for the Java `org.wso2.apk.config.api.APIDefinitionValidationResponse` class. @java:Binding {'class: "org.wso2.apk.config.api.APIDefinitionValidationResponse"} public distinct class APIDefinitionValidationResponse { @@ -83,6 +85,14 @@ public distinct class APIDefinitionValidationResponse { return newObj; } + # The function that maps to the `getProtoContent` method of `org.wso2.apk.config.api.APIDefinitionValidationResponse`. + # + # + return - The `byte[]` value returning from the Java mapping. + public isolated function getProtoContent() returns byte[]|error { + handle externalObj = org_wso2_apk_config_api_APIDefinitionValidationResponse_getProtoContent(self.jObj); + return check jarrays:fromHandle(externalObj, "byte"); + } + # The function that maps to the `getProtocol` method of `org.wso2.apk.config.api.APIDefinitionValidationResponse`. # # + return - The `string` value returning from the Java mapping. @@ -163,6 +173,14 @@ public distinct class APIDefinitionValidationResponse { org_wso2_apk_config_api_APIDefinitionValidationResponse_setParser(self.jObj, arg0.jObj); } + # The function that maps to the `setProtoContent` method of `org.wso2.apk.config.api.APIDefinitionValidationResponse`. + # + # + arg0 - The `byte[]` value required to map with the Java method parameter. + # + return - The `error?` value returning from the Java mapping. + public function setProtoContent(byte[] arg0) returns error? { + org_wso2_apk_config_api_APIDefinitionValidationResponse_setProtoContent(self.jObj, check jarrays:toHandle(arg0, "byte")); + } + # The function that maps to the `setProtocol` method of `org.wso2.apk.config.api.APIDefinitionValidationResponse`. # # + arg0 - The `string` value required to map with the Java method parameter. @@ -266,6 +284,12 @@ isolated function org_wso2_apk_config_api_APIDefinitionValidationResponse_getPar paramTypes: [] } external; +isolated function org_wso2_apk_config_api_APIDefinitionValidationResponse_getProtoContent(handle receiver) returns handle = @java:Method { + name: "getProtoContent", + 'class: "org.wso2.apk.config.api.APIDefinitionValidationResponse", + paramTypes: [] +} external; + function org_wso2_apk_config_api_APIDefinitionValidationResponse_getProtocol(handle receiver) returns handle = @java:Method { name: "getProtocol", 'class: "org.wso2.apk.config.api.APIDefinitionValidationResponse", @@ -338,6 +362,12 @@ function org_wso2_apk_config_api_APIDefinitionValidationResponse_setParser(handl paramTypes: ["org.wso2.apk.config.api.APIDefinition"] } external; +function org_wso2_apk_config_api_APIDefinitionValidationResponse_setProtoContent(handle receiver, handle arg0) = @java:Method { + name: "setProtoContent", + 'class: "org.wso2.apk.config.api.APIDefinitionValidationResponse", + paramTypes: ["[B"] +} external; + function org_wso2_apk_config_api_APIDefinitionValidationResponse_setProtocol(handle receiver, handle arg0) = @java:Method { name: "setProtocol", 'class: "org.wso2.apk.config.api.APIDefinitionValidationResponse", diff --git a/runtime/config-deployer-service/ballerina/modules/org.wso2.apk.config/RuntimeAPICommonUtil.bal b/runtime/config-deployer-service/ballerina/modules/org.wso2.apk.config/RuntimeAPICommonUtil.bal index 1e5011dc5a..3219532238 100644 --- a/runtime/config-deployer-service/ballerina/modules/org.wso2.apk.config/RuntimeAPICommonUtil.bal +++ b/runtime/config-deployer-service/ballerina/modules/org.wso2.apk.config/RuntimeAPICommonUtil.bal @@ -162,6 +162,17 @@ public isolated function RuntimeAPICommonUtil_getAPIFromDefinition(string arg0, } } +# The function that maps to the `getGRPCAPIFromProtoDefinition` method of `org.wso2.apk.config.RuntimeAPICommonUtil`. +# +# + arg0 - The `byte[]` value required to map with the Java method parameter. +# + arg1 - The `string` value required to map with the Java method parameter. +# + return - The `orgwso2apkconfigmodel:API` value returning from the Java mapping. +public isolated function RuntimeAPICommonUtil_getGRPCAPIFromProtoDefinition(byte[] arg0, string arg1) returns orgwso2apkconfigmodel:API|error { + handle externalObj = org_wso2_apk_config_RuntimeAPICommonUtil_getGRPCAPIFromProtoDefinition(check jarrays:toHandle(arg0, "byte"), java:fromString(arg1)); + orgwso2apkconfigmodel:API newObj = new (externalObj); + return newObj; +} + # The function that maps to the `validateOpenAPIDefinition` method of `org.wso2.apk.config.RuntimeAPICommonUtil`. # # + arg0 - The `string` value required to map with the Java method parameter. @@ -211,12 +222,18 @@ isolated function org_wso2_apk_config_RuntimeAPICommonUtil_getAPIFromDefinition( paramTypes: ["java.lang.String", "java.lang.String"] } external; -function org_wso2_apk_config_RuntimeAPICommonUtil_getClass(handle receiver) returns handle = @java:Method { +isolated function org_wso2_apk_config_RuntimeAPICommonUtil_getClass(handle receiver) returns handle = @java:Method { name: "getClass", 'class: "org.wso2.apk.config.RuntimeAPICommonUtil", paramTypes: [] } external; +isolated function org_wso2_apk_config_RuntimeAPICommonUtil_getGRPCAPIFromProtoDefinition(handle arg0, handle arg1) returns handle = @java:Method { + name: "getGRPCAPIFromProtoDefinition", + 'class: "org.wso2.apk.config.RuntimeAPICommonUtil", + paramTypes: ["[B", "java.lang.String"] +} external; + function org_wso2_apk_config_RuntimeAPICommonUtil_hashCode(handle receiver) returns int = @java:Method { name: "hashCode", 'class: "org.wso2.apk.config.RuntimeAPICommonUtil", diff --git a/runtime/config-deployer-service/java/src/main/java/org/wso2/apk/config/DefinitionParserFactory.java b/runtime/config-deployer-service/java/src/main/java/org/wso2/apk/config/DefinitionParserFactory.java index 5f4476d8cd..e08c5f9a33 100644 --- a/runtime/config-deployer-service/java/src/main/java/org/wso2/apk/config/DefinitionParserFactory.java +++ b/runtime/config-deployer-service/java/src/main/java/org/wso2/apk/config/DefinitionParserFactory.java @@ -12,8 +12,7 @@ public class DefinitionParserFactory { private static final List parsers = new ArrayList<>(); - private DefinitionParserFactory() { - } + private DefinitionParserFactory() { } static { parsers.add(new AsyncApiParser()); diff --git a/runtime/config-deployer-service/java/src/main/java/org/wso2/apk/config/RuntimeAPICommonUtil.java b/runtime/config-deployer-service/java/src/main/java/org/wso2/apk/config/RuntimeAPICommonUtil.java index 68c8b2014d..39dcc43a0f 100644 --- a/runtime/config-deployer-service/java/src/main/java/org/wso2/apk/config/RuntimeAPICommonUtil.java +++ b/runtime/config-deployer-service/java/src/main/java/org/wso2/apk/config/RuntimeAPICommonUtil.java @@ -13,7 +13,6 @@ import graphql.schema.idl.SchemaParser; import graphql.schema.idl.TypeDefinitionRegistry; - import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.List; @@ -66,10 +65,14 @@ public static APIDefinitionValidationResponse validateOpenAPIDefinition(String t "Invalid definition file type provided."); } } else if (APIConstants.ParserType.GRPC.name().equals(type.toUpperCase())) { - if (fileName.endsWith(".proto")) { - validationResponse = ProtoParserUtil.validateGRPCAPIDefinition( - new String(inputByteArray, StandardCharsets.UTF_8), - returnContent); + if (inputByteArray != null && inputByteArray.length > 0) { + if (fileName.endsWith(".zip") || fileName.endsWith(".desc")) { + validationResponse = ProtoParserUtil.validateGRPCAPIDefinition( + inputByteArray, fileName, returnContent); + } else { + ProtoParserUtil.addErrorToValidationResponse(validationResponse, + "Invalid definition file type provided."); + } } else { ProtoParserUtil.addErrorToValidationResponse(validationResponse, "Invalid definition file type provided."); @@ -78,35 +81,13 @@ public static APIDefinitionValidationResponse validateOpenAPIDefinition(String t return validationResponse; } - public static API getGRPCAPIFromProtoDefinition(String definition) { - System.out.println("GETTING API FROM PROTO"); + public static API getGRPCAPIFromProtoDefinition(byte[] definition, String fileName) { ProtoParser protoParser = new ProtoParser(); - protoParser.setContent(definition); - List uriTemplates = new ArrayList<>(); - API api = new API(); - api.setBasePath("/" + protoParser.protoFile.basePath); - api.setVersion(protoParser.protoFile.version); - StringBuilder apiName = new StringBuilder(); - List sortedServices = new ArrayList<>(); - - for (ProtoParser.Service service : protoParser.getServices()) { - sortedServices.add(service.name); - for (String method : service.methods) { - URITemplate uriTemplate = new URITemplate(); - uriTemplate.setUriTemplate(protoParser.protoFile.packageName + "." + service.name); - uriTemplate.setVerb(method); - uriTemplates.add(uriTemplate); - } - } - sortedServices.sort(String::compareTo); - for (String service : sortedServices) { - apiName.append(service).append("-"); + try { + return protoParser.getAPIFromProtoFile(definition, fileName); + } catch (APIManagementException e) { + throw new RuntimeException(e); } - apiName.deleteCharAt(apiName.length() - 1); - api.setName(apiName.toString()); - api.setUriTemplates(uriTemplates.toArray(new URITemplate[uriTemplates.size()])); - - return api; } public static Set generateUriTemplatesFromAPIDefinition(String apiType, String content) @@ -134,12 +115,10 @@ public static String generateDefinition(API api, String definition) throws APIMa return parser.generateAPIDefinition(api, definition); } - public static API getAPIFromDefinition(String definition, String apiType) throws APIManagementException { - - if (apiType.toUpperCase().equals(APIConstants.GRAPHQL_API)) { + public static API getAPIFromDefinition(String definition, String apiType) + throws APIManagementException { + if (apiType.equalsIgnoreCase(APIConstants.GRAPHQL_API)) { return getGQLAPIFromDefinition(definition); - } else if (apiType.toUpperCase().equals(APIConstants.GRPC_API)) { - return getGRPCAPIFromProtoDefinition(definition); } else { APIDefinition parser = DefinitionParserFactory.getParser(apiType); if (parser != null) { diff --git a/runtime/config-deployer-service/java/src/main/java/org/wso2/apk/config/api/APIDefinitionValidationResponse.java b/runtime/config-deployer-service/java/src/main/java/org/wso2/apk/config/api/APIDefinitionValidationResponse.java index ef1cff58cf..aceb1c5ef2 100644 --- a/runtime/config-deployer-service/java/src/main/java/org/wso2/apk/config/api/APIDefinitionValidationResponse.java +++ b/runtime/config-deployer-service/java/src/main/java/org/wso2/apk/config/api/APIDefinitionValidationResponse.java @@ -18,7 +18,6 @@ package org.wso2.apk.config.api; - import java.util.ArrayList; /** @@ -28,6 +27,7 @@ public class APIDefinitionValidationResponse { private boolean isValid = false; private String content; private String jsonContent; + private byte[] protoContent; private String protocol; private Info info; private APIDefinition parser; @@ -82,7 +82,6 @@ public void setInit(boolean init) { isInit = init; } - public APIDefinition getParser() { return parser; } @@ -100,4 +99,12 @@ public void setProtocol(String protocol) { this.protocol = protocol; } + + public byte[] getProtoContent() { + return protoContent; + } + + public void setProtoContent(byte[] protoContent) { + this.protoContent = protoContent; + } } diff --git a/runtime/config-deployer-service/java/src/main/java/org/wso2/apk/config/definitions/OASParserUtil.java b/runtime/config-deployer-service/java/src/main/java/org/wso2/apk/config/definitions/OASParserUtil.java index 77e38e5c33..17d4bb6d91 100644 --- a/runtime/config-deployer-service/java/src/main/java/org/wso2/apk/config/definitions/OASParserUtil.java +++ b/runtime/config-deployer-service/java/src/main/java/org/wso2/apk/config/definitions/OASParserUtil.java @@ -741,32 +741,4 @@ private static boolean removeBlocksRecursivelyFromJsonObject(String keyToBeRemov } return definitionUpdated; } - - /** - * Validate gRPC proto definition - * - * @return Validation response - */ - public static APIDefinitionValidationResponse validateProtoDefinition(String apiDefinition, - boolean returnContent) { - APIDefinitionValidationResponse validationResponse = new APIDefinitionValidationResponse(); - ArrayList errors = new ArrayList<>(); - try { - if (apiDefinition.isBlank()) { - validationResponse.setValid(false); - errors.add(ExceptionCodes.GRPC_PROTO_DEFINTION_CANNOT_BE_NULL); - validationResponse.setErrorItems(errors); - } else { - validationResponse.setValid(true); - validationResponse.setContent(apiDefinition); - } - } catch (Exception e) { - OASParserUtil.addErrorToValidationResponse(validationResponse, e.getMessage()); - validationResponse.setValid(false); - errors.add(new ErrorItem("API Definition Validation Error", "API Definition is invalid", 400, 400)); - validationResponse.setErrorItems(errors); - } - return validationResponse; - - } } diff --git a/runtime/config-deployer-service/java/src/main/java/org/wso2/apk/config/definitions/ProtoParser.java b/runtime/config-deployer-service/java/src/main/java/org/wso2/apk/config/definitions/ProtoParser.java index 3010905c26..80f5c23d0b 100644 --- a/runtime/config-deployer-service/java/src/main/java/org/wso2/apk/config/definitions/ProtoParser.java +++ b/runtime/config-deployer-service/java/src/main/java/org/wso2/apk/config/definitions/ProtoParser.java @@ -1,206 +1,53 @@ package org.wso2.apk.config.definitions; -import java.io.*; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.Iterator; -import java.util.regex.Matcher; -import java.util.regex.Pattern; -import org.wso2.apk.config.api.APIDefinition; -import org.wso2.apk.config.api.APIDefinitionValidationResponse; -import org.wso2.apk.config.api.APIManagementException; -import org.wso2.apk.config.api.ErrorHandler; -import org.wso2.apk.config.api.ErrorItem; -import org.wso2.apk.config.api.ExceptionCodes; +import java.io.ByteArrayInputStream; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.*; +import java.util.zip.ZipEntry; +import java.util.zip.ZipInputStream; + +import com.google.protobuf.*; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.wso2.apk.config.api.*; import org.wso2.apk.config.model.API; +import org.wso2.apk.config.model.proto.ProtoFile; +import org.wso2.apk.config.model.proto.Service; import org.wso2.apk.config.model.SwaggerData; import org.wso2.apk.config.model.URITemplate; import io.swagger.v3.core.util.Json; import io.swagger.v3.oas.models.OpenAPI; -import io.swagger.v3.oas.models.Operation; -import io.swagger.v3.oas.models.PathItem; import io.swagger.v3.oas.models.Paths; import io.swagger.v3.oas.models.info.Contact; import io.swagger.v3.oas.models.info.Info; -import io.swagger.v3.parser.OpenAPIV3Parser; -import io.swagger.v3.parser.core.models.SwaggerParseResult; - -import com.google.protobuf.DescriptorProtos; -import com.google.protobuf.TextFormat; -import com.google.protobuf.InvalidProtocolBufferException; - -import java.io.FileInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.nio.file.Path; public class ProtoParser extends APIDefinition { - - public ProtoFile protoFile; + private static final Log log = LogFactory.getLog(ProtoParser.class); public ProtoParser() { } - public void setContent(String content) { - protoFile = parseProtoContent(content); - } - - public String getPackageString(String content) { - Pattern packagePattern = Pattern.compile("package\\s+([\\w\\.]+);"); - Matcher packageMatcher = packagePattern.matcher(content); - if (packageMatcher.find()) { - return packageMatcher.group(1); - } - return null; + private static Descriptors.FileDescriptor resolveWellKnownType(String descriptorName) + throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException { + // Extract the proto file base name (e.g., "timestamp.proto" -> "Timestamp") + String baseName = descriptorName.substring(descriptorName.lastIndexOf('/') + 1, + descriptorName.lastIndexOf('.')); + // Convert to CamelCase (e.g., "timestamp" -> "Timestamp") + String className = baseName.substring(0, 1).toUpperCase() + baseName.substring(1); + // Find the corresponding class in the com.google.protobuf package + Class clazz = Class.forName("com.google.protobuf." + className); + // Use reflection to get the descriptor + Method getDescriptorMethod = clazz.getMethod("getDescriptor"); + Descriptors.Descriptor descriptor = (Descriptors.Descriptor) getDescriptorMethod.invoke(null); + return descriptor.getFile(); } - public String getVersion(String packageString) { - Pattern versionPattern = Pattern.compile("v\\d+(\\.\\d+)*"); - Matcher versionMatcher = versionPattern.matcher(packageString); - if (versionMatcher.find()) { - return versionMatcher.group(0); - } - System.out.println("No version found"); - return null; - } - - public String getPackageName(String packageString) { - Pattern namePattern = Pattern.compile("v\\d+(\\.\\d+)*\\.(\\w+)"); - Matcher nameMatcher = namePattern.matcher(packageString); - if (nameMatcher.find()) { - return nameMatcher.group(2); - } - System.out.println("No name found"); - return null; - } - - public String getBasePath(String packageString) { - Pattern basePathPattern = Pattern.compile("^(.*?)v\\d"); - - Matcher basePathMatcher = basePathPattern.matcher(packageString); - if (basePathMatcher.find()) { - String basePath = basePathMatcher.group(1); - if (basePath.charAt(basePath.length() - 1) == '.') { - basePath = basePath.substring(0, basePath.length() - 1); - } - return basePath; - } - System.out.println("No base path found"); - return null; - } - - // Method to extract service blocks from a given text - public List extractServiceBlocks(String text) { - // Regular expression pattern to match the service blocks - String patternString = "service\\s+\\w+\\s*\\{[^{}]*(?:\\{[^{}]*\\}[^{}]*)*\\}"; - - // Compile the regular expression - Pattern pattern = Pattern.compile(patternString, Pattern.DOTALL); - Matcher matcher = pattern.matcher(text); - - // Find all matches and append them to the result - List result = new ArrayList<>(); - while (matcher.find()) { - result.add(matcher.group()); - } - return result; - } - - public List extractMethodNames(String serviceBlock) { - // Regular expression pattern to match the method names - String patternString = "(?<=rpc\\s)\\w+"; - - // Compile the regular expression - Pattern pattern = Pattern.compile(patternString); - Matcher matcher = pattern.matcher(serviceBlock); - - // Find all matches and append them to the result - List result = new ArrayList<>(); - while (matcher.find()) { - result.add(matcher.group()); - } - return result; - } - - public String getServiceName(String serviceBlock) { - // Regular expression pattern to match the service name - String patternString = "(?<=service\\s)\\w+"; - - // Compile the regular expression - Pattern pattern = Pattern.compile(patternString); - Matcher matcher = pattern.matcher(serviceBlock); - - // Find the first match and return it - if (matcher.find()) { - return matcher.group(); - } - return null; - } - - public ProtoFile parseProtoContent(String content) { - ProtoFile protoFile = new ProtoFile(); - protoFile.services = new ArrayList<>(); - - List serviceBlocks = extractServiceBlocks(content); - for (String serviceBlock : serviceBlocks) { - Service service = new Service(); - service.name = getServiceName(serviceBlock); - service.methods = new ArrayList<>(); - service.methods.addAll(extractMethodNames(serviceBlock)); - protoFile.services.add(service); - } - - // Extract package name - String packageName = getPackageString(content); - protoFile.packageName = getPackageName(packageName); - protoFile.version = getVersion(packageName); - protoFile.basePath = getBasePath(packageName); - - // System.out.println(protoFile); - - return protoFile; - } - - public List getMethods(Service Service) { - return Service.methods; - } - - public List getServices() { - return this.protoFile.services; - } - - public class ProtoFile { - public String packageName; - public String basePath; - public String version; - public List services; - - @Override - public String toString() { - return "ProtoFile{" + - "packageName='" + packageName + '\'' + - ", basePath='" + basePath + '\'' + - ", version='" + version + '\'' + - ", services=" + services + - '}'; - } - - } - - public class Service { - public String name; - public List methods; - - @Override - public String toString() { - return " Service{" + - "name='" + name + '\'' + - ", methods=" + methods + - '}'; + private static void populateProtoMap(Map protoMap, + DescriptorProtos.FileDescriptorSet fileDescriptorSet) { + for (DescriptorProtos.FileDescriptorProto fileDescriptorProto : fileDescriptorSet.getFileList()) { + protoMap.put(fileDescriptorProto.getName(), fileDescriptorProto); } } @@ -233,7 +80,6 @@ public String generateAPIDefinition(API api) throws APIManagementException { private String generateAPIDefinition(SwaggerData swaggerData) { OpenAPI openAPI = new OpenAPI(); - // create path if null if (openAPI.getPaths() == null) { openAPI.setPaths(new Paths()); @@ -269,45 +115,23 @@ private String generateAPIDefinition(SwaggerData swaggerData) { * * @return Validation response */ - public APIDefinitionValidationResponse validateAPIDefinition(String apiDefinition, - boolean returnContent) { - APIDefinitionValidationResponse validationResponse = new APIDefinitionValidationResponse(); - ArrayList errors = new ArrayList<>(); - try { - if (apiDefinition.isBlank()) { - validationResponse.setValid(false); - errors.add(ExceptionCodes.GRPC_PROTO_DEFINTION_CANNOT_BE_NULL); - validationResponse.setErrorItems(errors); - } else { - validationResponse.setValid(true); - validationResponse.setContent(apiDefinition); - } - } catch (Exception e) { - OASParserUtil.addErrorToValidationResponse(validationResponse, e.getMessage()); - validationResponse.setValid(false); - errors.add(new ErrorItem("API Definition Validation Error", "API Definition is invalid", 400, 400)); - validationResponse.setErrorItems(errors); - } - return validationResponse; - + public APIDefinitionValidationResponse validateAPIDefinition(String apiDefinition, boolean returnContent) { + return new APIDefinitionValidationResponse(); } @Override public API getAPIFromDefinition(String content) throws APIManagementException { - // TODO Auto-generated method stub throw new UnsupportedOperationException("Unimplemented method 'getAPIFromDefinition'"); } @Override public String processOtherSchemeScopes(String resourceConfigsJSON) throws APIManagementException { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'processOtherSchemeScopes'"); + return resourceConfigsJSON; } @Override public String getType() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'getType'"); + return null; } @Override @@ -320,17 +144,213 @@ public String generateAPIDefinition(API api, String swagger) throws APIManagemen return null; } - public boolean validateProtoFile(String protoContent) { + public API getAPIFromProtoFile(byte[] definition, String fileName) throws APIManagementException { + API api = new API(); + ProtoFile protoFile = getProtoFileFromDefinition(definition, fileName); + StringBuilder apiName = new StringBuilder(); + List sortedServices = new ArrayList<>(); + List uriTemplates = new ArrayList<>(); + + if (protoFile == null) { + throw new APIManagementException("Error in validating API definition"); + } + + System.out.println("Printing services"); + for (Service service : protoFile.getServices()) { + sortedServices.add(service.getServiceName()); + for (String method : service.getMethods()) { + System.out.println("Method " + method + "of service " + service.getServiceName()); + URITemplate uriTemplate = new URITemplate(); + uriTemplate.setUriTemplate(protoFile.getPackageName() + "." + service.getServiceName()); + uriTemplate.setVerb(method); + uriTemplates.add(uriTemplate); + } + } + sortedServices.sort(String::compareTo); + for (String service : sortedServices) { + apiName.append(service).append("-"); + } + + api.setBasePath(protoFile.getBasePath()); + System.out.println("Basepath " + protoFile.getBasePath()); + api.setProtoDefinition(definition); + api.setVersion(protoFile.getVersion()); + api.setName(apiName.toString()); + api.setUriTemplates(uriTemplates.toArray(new URITemplate[0])); + return api; + } + + ProtoFile getProtoFileFromDefinition(byte[] fileContent, String fileName) { + Map protoMap = new HashMap<>(); + Map descriptorMap = new HashMap<>(); + ArrayList services = new ArrayList<>(); + String packageName = ""; + ProtoFile tempProtoFile = new ProtoFile(); + Map wellKnownTypesMap = new HashMap<>(); + try { + DescriptorProtos.FileDescriptorSet fileDescriptorSet = DescriptorProtos.FileDescriptorSet.parseFrom( + fileContent); + + populateProtoMap(protoMap, fileDescriptorSet); + + for (DescriptorProtos.FileDescriptorProto fileDescriptorProto : fileDescriptorSet.getFileList()) { + packageName = processFileDescriptor(fileName, descriptorMap, protoMap, services, wellKnownTypesMap, + fileDescriptorProto); + } + + tempProtoFile.setServices(convertServiceDescriptorsToServices(services, packageName)); + String[] info = packageName.split("\\."); + if (info.length < 3) { + throw new APIManagementException( + "Invalid package name: specify in the format of basepath.version.packageName"); + } + tempProtoFile.setVersion(info[info.length - 2]); + tempProtoFile.setPackageName(info[info.length - 1]); + StringBuilder basePath = new StringBuilder("/").append(info[0]); + for (int i = 1; i < info.length - 2; i++) { + System.out.println(info[i]); + basePath.append(".").append(info[i]); + } + tempProtoFile.setBasePath(basePath.toString()); + return tempProtoFile; + } catch (Exception e) { + e.printStackTrace(); + log.error("Proto definition validation failed for " + fileName + ": " + e.getMessage()); + return null; + } + } + + /** + * @param fileName - The name of the .desc file provided as input for the config generator + * @param descriptorMap + * @param protoMap + * @param services + * @param wellKnownTypesMap + * @param fileDescriptorProto + * @return + * @throws Descriptors.DescriptorValidationException + */ + private String processFileDescriptor(String fileName, Map descriptorMap, + Map protoMap, + ArrayList services, + Map wellKnownTypesMap, + DescriptorProtos.FileDescriptorProto fileDescriptorProto) throws Descriptors.DescriptorValidationException { + + String packageName = fileDescriptorProto.getPackage(); + + // Process and resolve dependencies for a given file descriptor + Descriptors.FileDescriptor[] dependencies = fileDescriptorProto.getDependencyList().stream() + .map(descriptorMap::get).toArray(Descriptors.FileDescriptor[]::new); + + // Build the file descriptor based on the proto and its dependencies + Descriptors.FileDescriptor fileDescriptor = Descriptors.FileDescriptor.buildFrom(fileDescriptorProto, + dependencies); + services.addAll(fileDescriptor.getServices()); + descriptorMap.put(fileDescriptor.getName(), fileDescriptor); + return packageName; + } + + private Descriptors.FileDescriptor resolveDependency(Map descriptorMap, + Map protoMap, + Map wellKnownTypesMap, String descriptorName) { + Descriptors.FileDescriptor dependency = descriptorMap.get(descriptorName); + // Dependency has not been resolved yet + if (dependency == null) { + try { + // if the dependency is a well known type + if (descriptorName.startsWith("com.google.protobuf.")) { + dependency = resolveWellKnownType(descriptorName); + wellKnownTypesMap.put(descriptorName, dependency); + } else { + // if the dependency is on another file that was imported, we resolve it and add it to the + // descriptor map + dependency = buildAndCacheDescriptor(descriptorName, protoMap, descriptorMap, wellKnownTypesMap); + } + } catch (Exception e) { + System.err.println("Error loading well-known type: " + descriptorName + " - " + e.getMessage()); + } + } + if (dependency == null) { + System.err.println("Missing dependency for " + descriptorName); + } + return dependency; + } + + private Descriptors.FileDescriptor buildAndCacheDescriptor(String descriptorName, + Map protoMap, + Map descriptorMap, + Map wellKnownTypesMap) { + // this scenario is when you have an import in your proto file but that file hasnt been built yet + // in that scenario, it needs to have its dependencies resolved as well + DescriptorProtos.FileDescriptorProto dependencyProto = protoMap.get(descriptorName); + if (dependencyProto != null) { + // Descriptors.FileDescriptor dependency = resolveDependency(descriptorMap, protoMap, wellKnownTypesMap, + // descriptorName); + // descriptorMap.put(dependency.getName(), dependency); + // return dependency; + } + return null; + } + + boolean validateProtoContent(byte[] fileContent, String fileName) { try { - DescriptorProtos.FileDescriptorProto.Builder builder = DescriptorProtos.FileDescriptorProto.newBuilder(); - TextFormat.getParser().merge(protoContent, builder); - // If parsing succeeds, return true - return true; - } catch (IOException e) { - // If an exception occurs, the proto file is invalid - System.err.println("Validation failed: " + e.getMessage()); + ProtoFile protoFile = getProtoFileFromDefinition(fileContent, fileName); + return protoFile != null; + } catch (Exception e) { + log.error("Proto definition validation failed for " + fileName + ": " + e.getMessage()); return false; } } -} \ No newline at end of file + public ArrayList convertServiceDescriptorsToServices( + ArrayList serviceDescriptors, String packageName) { + ArrayList services = new ArrayList<>(); + for (Descriptors.ServiceDescriptor serviceDescriptor : serviceDescriptors) { + List methodDescriptors = serviceDescriptor.getMethods(); + ArrayList methods = new ArrayList<>(); + for (Descriptors.MethodDescriptor methodDescriptor : methodDescriptors) { + methods.add(methodDescriptor.getName()); + } + services.add(new Service(serviceDescriptor.getName(), methods, packageName)); + } + return services; + } + + public void validateGRPCAPIDefinition(byte[] inputByteArray, String fileName, + APIDefinitionValidationResponse validationResponse, ArrayList errors) { + try { + // if (fileName.endsWith(".zip")) { + // try (ZipInputStream zis = new ZipInputStream(new ByteArrayInputStream(inputByteArray))) { + // ZipEntry zipEntry; + // while ((zipEntry = zis.getNextEntry()) != null) { + // if (!zipEntry.isDirectory() && zipEntry.getName().endsWith(".desc")) { + // byte[] protoFileContentBytes = zis.readAllBytes(); + // boolean validated = validateProtoContent(protoFileContentBytes, fileName); + // if (!validated) { + // throw new APIManagementException( + // "Invalid definition file provided. " + "Please provide a valid .zip or .proto file."); + // } + // } + // } + // } + // } else + if (fileName.endsWith(".desc")) { + boolean validated = validateProtoContent(inputByteArray, fileName); + validationResponse.setValid(validated); + validationResponse.setProtoContent(inputByteArray); + if (!validated) { + throw new APIManagementException( + "Invalid definition file provided. " + "Please provide a valid .zip or .proto file."); + } + } else { + throw new APIManagementException( + "Invalid definition file provided. " + "Please provide a valid .zip or .desc file."); + } + } catch (Exception e) { + ProtoParserUtil.addErrorToValidationResponse(validationResponse, e.getMessage()); + validationResponse.setValid(false); + errors.add(new ErrorItem("API Definition Validation Error", "API Definition is invalid", 400, 400)); + validationResponse.setErrorItems(errors); + } + } +} diff --git a/runtime/config-deployer-service/java/src/main/java/org/wso2/apk/config/definitions/ProtoParserUtil.java b/runtime/config-deployer-service/java/src/main/java/org/wso2/apk/config/definitions/ProtoParserUtil.java index 6c65adc04f..056dc4586e 100644 --- a/runtime/config-deployer-service/java/src/main/java/org/wso2/apk/config/definitions/ProtoParserUtil.java +++ b/runtime/config-deployer-service/java/src/main/java/org/wso2/apk/config/definitions/ProtoParserUtil.java @@ -1,12 +1,9 @@ package org.wso2.apk.config.definitions; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.Set; +import java.util.*; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; -import org.wso2.apk.config.api.APIDefinition; import org.wso2.apk.config.api.APIDefinitionValidationResponse; import org.wso2.apk.config.api.ErrorHandler; import org.wso2.apk.config.api.ErrorItem; @@ -19,52 +16,40 @@ public class ProtoParserUtil { private static final Log log = LogFactory.getLog(ProtoParserUtil.class); private static final ProtoParser protoParser = new ProtoParser(); + /** * Validate graphQL Schema * * @return Validation response */ - public static APIDefinitionValidationResponse validateGRPCAPIDefinition(String apiDefinition, + public static APIDefinitionValidationResponse validateGRPCAPIDefinition(byte[] inputByteArray, String fileName, boolean returnGRPCContent) { APIDefinitionValidationResponse validationResponse = new APIDefinitionValidationResponse(); ArrayList errors = new ArrayList<>(); - if (apiDefinition.isBlank()) { + if (inputByteArray.length == 0) { validationResponse.setValid(false); errors.add(ExceptionCodes.GRPC_PROTO_DEFINTION_CANNOT_BE_NULL); validationResponse.setErrorItems(errors); + } else { + protoParser.validateGRPCAPIDefinition(inputByteArray, fileName, validationResponse, errors); } - - try { - boolean validated = protoParser.validateProtoFile(apiDefinition); - validationResponse.setValid(validated); - validationResponse.setContent(apiDefinition); - } catch (Exception e) { - ProtoParserUtil.addErrorToValidationResponse(validationResponse, e.getMessage()); - validationResponse.setValid(false); - errors.add(new ErrorItem("API Definition Validation Error", "API Definition is invalid", 400, 400)); - validationResponse.setErrorItems(errors); - } - return validationResponse; } /** - * Add error item with the provided message to the provided validation response - * object + * Add error item with the provided message to the provided validation response object * * @param validationResponse APIDefinitionValidationResponse object * @param errMessage error message - * @return added ErrorItem object */ - public static ErrorItem addErrorToValidationResponse(APIDefinitionValidationResponse validationResponse, + public static void addErrorToValidationResponse(APIDefinitionValidationResponse validationResponse, String errMessage) { ErrorItem errorItem = new ErrorItem(); errorItem.setErrorCode(ExceptionCodes.PROTO_DEFINITION_PARSE_EXCEPTION.getErrorCode()); errorItem.setMessage(ExceptionCodes.PROTO_DEFINITION_PARSE_EXCEPTION.getErrorMessage()); errorItem.setDescription(errMessage); validationResponse.getErrorItems().add(errorItem); - return errorItem; } } diff --git a/runtime/config-deployer-service/java/src/main/java/org/wso2/apk/config/model/API.java b/runtime/config-deployer-service/java/src/main/java/org/wso2/apk/config/model/API.java index 98c810eaa1..97ac33629e 100644 --- a/runtime/config-deployer-service/java/src/main/java/org/wso2/apk/config/model/API.java +++ b/runtime/config-deployer-service/java/src/main/java/org/wso2/apk/config/model/API.java @@ -11,7 +11,7 @@ public class API { private String apiSecurity; private String[] scopes; private String graphQLSchema; - private String protoDefinition; + private byte[] protoDefinition; private String swaggerDefinition; private String environment; @@ -88,11 +88,11 @@ public void setSwaggerDefinition(String swaggerDefinition) { this.swaggerDefinition = swaggerDefinition; } - public void setProtoDefinition(String protoDefinition) { + public void setProtoDefinition(byte[] protoDefinition) { this.protoDefinition = protoDefinition; } - public String getProtoDefinition() { + public byte[] getProtoDefinition() { return protoDefinition; } diff --git a/runtime/config-deployer-service/java/src/main/java/org/wso2/apk/config/model/proto/ProtoFile.java b/runtime/config-deployer-service/java/src/main/java/org/wso2/apk/config/model/proto/ProtoFile.java new file mode 100644 index 0000000000..c72df2ca5c --- /dev/null +++ b/runtime/config-deployer-service/java/src/main/java/org/wso2/apk/config/model/proto/ProtoFile.java @@ -0,0 +1,71 @@ +package org.wso2.apk.config.model.proto; + +import java.util.ArrayList; +import java.util.List; + +public class ProtoFile { + String packageName; + String basePath; + String version; + List services; + + public List getServices() { + return services; + } + + public String getPackageName() { + return packageName; + } + + public void setPackageName(String packageName) { + this.packageName = packageName; + } + + public void setBasePath(String basePath) { + this.basePath = basePath; + } + + public void setVersion(String version) { + this.version = version; + } + + public void setServices(List services) { + if (this.services == null){ + this.services = new ArrayList<>(); + } + this.services.addAll(services); + } + + public String getBasePath() { + return basePath; + } + + public String getVersion() { + return version; + } + + public ProtoFile(String packageName, String basePath, String version, List services) { + this.packageName = packageName; + this.basePath = basePath; + this.version = version; + this.services = services; + } + + public ProtoFile() { + this.packageName = "packageName"; + this.basePath = "basePath"; + this.version = "version"; + this.services = new ArrayList<>(); + } + + @Override + public String toString() { + return "ProtoFile{" + + "packageName='" + packageName + '\'' + + ", basePath='" + basePath + '\'' + + ", version='" + version + '\'' + + ", services=" + services + + '}'; + } + +} \ No newline at end of file diff --git a/runtime/config-deployer-service/java/src/main/java/org/wso2/apk/config/model/proto/Service.java b/runtime/config-deployer-service/java/src/main/java/org/wso2/apk/config/model/proto/Service.java new file mode 100644 index 0000000000..988d5a1481 --- /dev/null +++ b/runtime/config-deployer-service/java/src/main/java/org/wso2/apk/config/model/proto/Service.java @@ -0,0 +1,32 @@ +package org.wso2.apk.config.model.proto; + +import java.util.List; + +public class Service { + String serviceName; + String packageName; + List methods; + + public String getServiceName() { + return serviceName; + } + + public String getPackageName() { + return packageName; + } + + public List getMethods() { + return methods; + } + + public Service(String serviceName, List methods, String packageName) { + this.serviceName = serviceName; + this.methods = methods; + this.packageName = packageName; + } + + @Override + public String toString() { + return "Service{" + "serviceName='" + serviceName + '\'' + ", packageName='" + packageName + '\'' + ", methods=" + methods + '}'; + } +} \ No newline at end of file From dff33af84b49acab0c9ee1f2a4bb64505a4fd636 Mon Sep 17 00:00:00 2001 From: sgayangi Date: Fri, 6 Sep 2024 15:01:00 +0530 Subject: [PATCH 09/17] Update webhook to allow for zipped definition files --- .../apis/dp/v1alpha2/api_webhook.go | 62 ++++++++++++++++++- 1 file changed, 59 insertions(+), 3 deletions(-) diff --git a/common-go-libs/apis/dp/v1alpha2/api_webhook.go b/common-go-libs/apis/dp/v1alpha2/api_webhook.go index 7f62170633..482da85481 100644 --- a/common-go-libs/apis/dp/v1alpha2/api_webhook.go +++ b/common-go-libs/apis/dp/v1alpha2/api_webhook.go @@ -18,12 +18,15 @@ package v1alpha2 import ( + "archive/zip" "bytes" "compress/gzip" "context" + "encoding/base64" "errors" "fmt" "io" + "io/ioutil" "regexp" "strings" @@ -289,9 +292,29 @@ func validateGzip(name, namespace string) (string, string) { // config map data key is "swagger.yaml" apiDef = []byte(val) } + + isBase64 := isBase64Encoded(apiDef) + + if isBase64 { + apiDef, err = base64.StdEncoding.DecodeString(string(apiDef)) + if err != nil { + loggers.LoggerAPKOperator.ErrorC(logging.PrintError(logging.Error2600, logging.MINOR, "Error decoding base64 content: %v", err)) + return "", "invalid base64 content" + } + } + + if isZip(apiDef) { + schemaString, err := unzipZip(apiDef) + if err != nil { + loggers.LoggerAPKOperator.ErrorC(logging.PrintError(logging.Error2600, logging.MINOR, "Error while unzipping zip file: %v", err)) + return "", "invalid zip content" + } + return schemaString, "" + } + // unzip gzip bytes var schemaString string - if schemaString, err = unzip(apiDef); err != nil { + if schemaString, err = unzipGzip(apiDef); err != nil { loggers.LoggerAPKOperator.ErrorC(logging.PrintError(logging.Error2600, logging.MINOR, "Error while unzipping gzip bytes: %v", err)) return "", "invalid gzipped content" } @@ -303,8 +326,41 @@ func validateGzip(name, namespace string) (string, string) { return "", "" } -// unzip gzip bytes -func unzip(compressedData []byte) (string, error) { +func isBase64Encoded(data []byte) bool { + _, err := base64.StdEncoding.DecodeString(string(data)) + return err == nil +} + +func isZip(data []byte) bool { + reader := bytes.NewReader(data) + _, err := zip.NewReader(reader, int64(len(data))) + return err == nil +} + +func unzipZip(data []byte) (string, error) { + reader, err := zip.NewReader(bytes.NewReader(data), int64(len(data))) + if err != nil { + return "", fmt.Errorf("failed to open zip archive: %v", err) + } + + var result string + for _, file := range reader.File { + rc, err := file.Open() + if err != nil { + return "", fmt.Errorf("failed to open file in zip: %v", err) + } + defer rc.Close() + + content, err := ioutil.ReadAll(rc) + if err != nil { + return "", fmt.Errorf("failed to read file content in zip: %v", err) + } + result += string(content) + } + return result, nil +} + +func unzipGzip(compressedData []byte) (string, error) { reader, err := gzip.NewReader(bytes.NewBuffer(compressedData)) if err != nil { return "", fmt.Errorf("error creating gzip reader: %v", err) From f1c58248574d01886c4b62ef9c361d88cc465534 Mon Sep 17 00:00:00 2001 From: sgayangi Date: Fri, 6 Sep 2024 15:07:49 +0530 Subject: [PATCH 10/17] Add logic to parse proto files for gRPC services --- .../oasparser/model/adapter_internal_api.go | 171 +++++++----- .../ballerina/APIClient.bal | 16 +- .../ballerina/DeployerClient.bal | 4 +- .../modules/org.wso2.apk.config.model/API.bal | 43 ++- .../wso2/apk/config/RuntimeAPICommonUtil.java | 5 +- .../apk/config/definitions/ProtoParser.java | 259 ++++++++++++++---- .../config/definitions/ProtoParserUtil.java | 15 +- .../java/org/wso2/apk/config/model/API.java | 33 ++- .../apk/config/model/proto/ProtoFile.java | 20 ++ .../wso2/apk/config/model/proto/Service.java | 20 +- 10 files changed, 417 insertions(+), 169 deletions(-) diff --git a/adapter/internal/oasparser/model/adapter_internal_api.go b/adapter/internal/oasparser/model/adapter_internal_api.go index 80747b8151..5dd8f22fc1 100644 --- a/adapter/internal/oasparser/model/adapter_internal_api.go +++ b/adapter/internal/oasparser/model/adapter_internal_api.go @@ -34,7 +34,7 @@ import ( dpv1alpha1 "github.com/wso2/apk/common-go-libs/apis/dp/v1alpha1" dpv1alpha2 "github.com/wso2/apk/common-go-libs/apis/dp/v1alpha2" dpv1alpha3 "github.com/wso2/apk/common-go-libs/apis/dp/v1alpha3" - "github.com/wso2/apk/common-go-libs/apis/dp/v1beta1" + dpv1beta1 "github.com/wso2/apk/common-go-libs/apis/dp/v1beta1" "golang.org/x/exp/maps" "k8s.io/apimachinery/pkg/types" gwapiv1 "sigs.k8s.io/gateway-api/apis/v1" @@ -74,7 +74,7 @@ type AdapterInternalAPI struct { apiDefinitionFile []byte apiDefinitionEndpoint string subscriptionValidation bool - APIProperties []v1beta1.Property + APIProperties []dpv1beta1.Property // GraphQLSchema string // GraphQLComplexities GraphQLComplexityYaml IsSystemAPI bool @@ -650,7 +650,7 @@ func (adapterInternalAPI *AdapterInternalAPI) SetInfoHTTPRouteCR(httpRoute *gwap resourceAuthScheme = concatAuthSchemes(authScheme, &ref) } else { return fmt.Errorf(`auth scheme: %s has not been resolved, spec.targetRef.kind should be - 'Resource' in resource level Authentications`, filter.ExtensionRef.Name) + 'Resource' in resource level Authentications`, filter.ExtensionRef.Name) } } if filter.ExtensionRef.Kind == constants.KindAPIPolicy { @@ -661,7 +661,7 @@ func (adapterInternalAPI *AdapterInternalAPI) SetInfoHTTPRouteCR(httpRoute *gwap resourceAPIPolicy = concatAPIPolicies(apiPolicy, &ref) } else { return fmt.Errorf(`apipolicy: %s has not been resolved, spec.targetRef.kind should be - 'Resource' in resource level APIPolicies`, filter.ExtensionRef.Name) + 'Resource' in resource level APIPolicies`, filter.ExtensionRef.Name) } } if filter.ExtensionRef.Kind == constants.KindScope { @@ -683,7 +683,7 @@ func (adapterInternalAPI *AdapterInternalAPI) SetInfoHTTPRouteCR(httpRoute *gwap resourceRatelimitPolicy = concatRateLimitPolicies(ratelimitPolicy, &ref) } else { return fmt.Errorf(`ratelimitpolicy: %s has not been resolved, spec.targetRef.kind should be - 'Resource' in resource level RateLimitPolicies`, filter.ExtensionRef.Name) + 'Resource' in resource level RateLimitPolicies`, filter.ExtensionRef.Name) } } case gwapiv1.HTTPRouteFilterRequestHeaderModifier: @@ -1040,10 +1040,55 @@ func (adapterInternalAPI *AdapterInternalAPI) SetInfoGQLRouteCR(gqlRoute *dpv1al } resolvedBackend, ok := resourceParams.BackendMapping[backendName.String()] if ok { - securityConfig, healthCheck, endpointCluster := setEndpointConfigs(resolvedBackend, config.Envoy.Upstream.Retry.StatusCodes, backendName, resourceParams) + endpointConfig := &EndpointConfig{} + if resolvedBackend.CircuitBreaker != nil { + endpointConfig.CircuitBreakers = &CircuitBreakers{ + MaxConnections: int32(resolvedBackend.CircuitBreaker.MaxConnections), + MaxRequests: int32(resolvedBackend.CircuitBreaker.MaxRequests), + MaxPendingRequests: int32(resolvedBackend.CircuitBreaker.MaxPendingRequests), + MaxRetries: int32(resolvedBackend.CircuitBreaker.MaxRetries), + MaxConnectionPools: int32(resolvedBackend.CircuitBreaker.MaxConnectionPools), + } + } + if resolvedBackend.Timeout != nil { + endpointConfig.TimeoutInMillis = resolvedBackend.Timeout.UpstreamResponseTimeout * 1000 + endpointConfig.IdleTimeoutInSeconds = resolvedBackend.Timeout.DownstreamRequestIdleTimeout + } + if resolvedBackend.Retry != nil { + statusCodes := config.Envoy.Upstream.Retry.StatusCodes + if len(resolvedBackend.Retry.StatusCodes) > 0 { + statusCodes = resolvedBackend.Retry.StatusCodes + } + endpointConfig.RetryConfig = &RetryConfig{ + Count: int32(resolvedBackend.Retry.Count), + StatusCodes: statusCodes, + BaseIntervalInMillis: int32(resolvedBackend.Retry.BaseIntervalMillis), + } + } + adapterInternalAPI.Endpoints = &EndpointCluster{ + Endpoints: GetEndpoints(backendName, resourceParams.BackendMapping), + Config: endpointConfig, + } + if resolvedBackend.HealthCheck != nil { + adapterInternalAPI.Endpoints.HealthCheck = &HealthCheck{ + Interval: resolvedBackend.HealthCheck.Interval, + Timeout: resolvedBackend.HealthCheck.Timeout, + UnhealthyThreshold: resolvedBackend.HealthCheck.UnhealthyThreshold, + HealthyThreshold: resolvedBackend.HealthCheck.HealthyThreshold, + } + } + + var securityConfig []EndpointSecurity + switch resolvedBackend.Security.Type { + case "Basic": + securityConfig = append(securityConfig, EndpointSecurity{ + Password: string(resolvedBackend.Security.Basic.Password), + Username: string(resolvedBackend.Security.Basic.Username), + Type: string(resolvedBackend.Security.Type), + Enabled: true, + }) + } adapterInternalAPI.EndpointSecurity = utils.GetPtrSlice(securityConfig) - adapterInternalAPI.Endpoints.HealthCheck = healthCheck - adapterInternalAPI.Endpoints = endpointCluster } else { return fmt.Errorf("backend: %s has not been resolved", backendName) } @@ -1063,7 +1108,7 @@ func (adapterInternalAPI *AdapterInternalAPI) SetInfoGQLRouteCR(gqlRoute *dpv1al resourceAuthScheme = concatAuthSchemes(authScheme, &ref) } else { return fmt.Errorf(`auth scheme: %s has not been resolved, spec.targetRef.kind should be - 'Resource' in resource level Authentications`, filter.ExtensionRef.Name) + 'Resource' in resource level Authentications`, filter.ExtensionRef.Name) } } if filter.ExtensionRef != nil && filter.ExtensionRef.Kind == constants.KindScope { @@ -1085,7 +1130,7 @@ func (adapterInternalAPI *AdapterInternalAPI) SetInfoGQLRouteCR(gqlRoute *dpv1al resourceRatelimitPolicy = concatRateLimitPolicies(ratelimitPolicy, &ref) } else { return fmt.Errorf(`ratelimitpolicy: %s has not been resolved, spec.targetRef.kind should be - 'Resource' in resource level RateLimitPolicies`, filter.ExtensionRef.Name) + 'Resource' in resource level RateLimitPolicies`, filter.ExtensionRef.Name) } } } @@ -1143,6 +1188,7 @@ func (adapterInternalAPI *AdapterInternalAPI) SetInfoGRPCRouteCR(grpcRoute *gwap ratelimitPolicy = *outputRatelimitPolicy } + //We are only supporting one backend for now backend := grpcRoute.Spec.Rules[0].BackendRefs[0] backendName := types.NamespacedName{ Name: string(backend.Name), @@ -1150,14 +1196,55 @@ func (adapterInternalAPI *AdapterInternalAPI) SetInfoGRPCRouteCR(grpcRoute *gwap } resolvedBackend, ok := resourceParams.BackendMapping[backendName.String()] if ok { - securityConfig, healthCheck, endpointCluster := setEndpointConfigs(resolvedBackend, config.Envoy.Upstream.Retry.StatusCodes, backendName, resourceParams) - adapterInternalAPI.EndpointSecurity = utils.GetPtrSlice(securityConfig) - if healthCheck != nil { - adapterInternalAPI.Endpoints.HealthCheck = healthCheck + endpointConfig := &EndpointConfig{} + if resolvedBackend.CircuitBreaker != nil { + endpointConfig.CircuitBreakers = &CircuitBreakers{ + MaxConnections: int32(resolvedBackend.CircuitBreaker.MaxConnections), + MaxRequests: int32(resolvedBackend.CircuitBreaker.MaxRequests), + MaxPendingRequests: int32(resolvedBackend.CircuitBreaker.MaxPendingRequests), + MaxRetries: int32(resolvedBackend.CircuitBreaker.MaxRetries), + MaxConnectionPools: int32(resolvedBackend.CircuitBreaker.MaxConnectionPools), + } + } + if resolvedBackend.Timeout != nil { + endpointConfig.TimeoutInMillis = resolvedBackend.Timeout.UpstreamResponseTimeout * 1000 + endpointConfig.IdleTimeoutInSeconds = resolvedBackend.Timeout.DownstreamRequestIdleTimeout + } + if resolvedBackend.Retry != nil { + statusCodes := config.Envoy.Upstream.Retry.StatusCodes + if len(resolvedBackend.Retry.StatusCodes) > 0 { + statusCodes = resolvedBackend.Retry.StatusCodes + } + endpointConfig.RetryConfig = &RetryConfig{ + Count: int32(resolvedBackend.Retry.Count), + StatusCodes: statusCodes, + BaseIntervalInMillis: int32(resolvedBackend.Retry.BaseIntervalMillis), + } + } + adapterInternalAPI.Endpoints = &EndpointCluster{ + Endpoints: GetEndpoints(backendName, resourceParams.BackendMapping), + Config: endpointConfig, } - if endpointCluster != nil { - adapterInternalAPI.Endpoints = endpointCluster + if resolvedBackend.HealthCheck != nil { + adapterInternalAPI.Endpoints.HealthCheck = &HealthCheck{ + Interval: resolvedBackend.HealthCheck.Interval, + Timeout: resolvedBackend.HealthCheck.Timeout, + UnhealthyThreshold: resolvedBackend.HealthCheck.UnhealthyThreshold, + HealthyThreshold: resolvedBackend.HealthCheck.HealthyThreshold, + } } + + var securityConfig []EndpointSecurity + switch resolvedBackend.Security.Type { + case "Basic": + securityConfig = append(securityConfig, EndpointSecurity{ + Password: string(resolvedBackend.Security.Basic.Password), + Username: string(resolvedBackend.Security.Basic.Username), + Type: string(resolvedBackend.Security.Type), + Enabled: true, + }) + } + adapterInternalAPI.EndpointSecurity = utils.GetPtrSlice(securityConfig) } else { return fmt.Errorf("backend: %s has not been resolved", backendName) } @@ -1253,58 +1340,6 @@ func (adapterInternalAPI *AdapterInternalAPI) SetInfoGRPCRouteCR(grpcRoute *gwap return nil } -func setEndpointConfigs(resolvedBackend *dpv1alpha1.ResolvedBackend, statusCodes []uint32, backendName types.NamespacedName, resourceParams ResourceParams) ([]EndpointSecurity, *HealthCheck, *EndpointCluster) { - endpointConfig := &EndpointConfig{} - if resolvedBackend.CircuitBreaker != nil { - endpointConfig.CircuitBreakers = &CircuitBreakers{ - MaxConnections: int32(resolvedBackend.CircuitBreaker.MaxConnections), - MaxRequests: int32(resolvedBackend.CircuitBreaker.MaxRequests), - MaxPendingRequests: int32(resolvedBackend.CircuitBreaker.MaxPendingRequests), - MaxRetries: int32(resolvedBackend.CircuitBreaker.MaxRetries), - MaxConnectionPools: int32(resolvedBackend.CircuitBreaker.MaxConnectionPools), - } - } - if resolvedBackend.Timeout != nil { - endpointConfig.TimeoutInMillis = resolvedBackend.Timeout.UpstreamResponseTimeout * 1000 - endpointConfig.IdleTimeoutInSeconds = resolvedBackend.Timeout.DownstreamRequestIdleTimeout - } - if resolvedBackend.Retry != nil { - if len(resolvedBackend.Retry.StatusCodes) > 0 { - statusCodes = resolvedBackend.Retry.StatusCodes - } - endpointConfig.RetryConfig = &RetryConfig{ - Count: int32(resolvedBackend.Retry.Count), - StatusCodes: statusCodes, - BaseIntervalInMillis: int32(resolvedBackend.Retry.BaseIntervalMillis), - } - } - endpointCluster := &EndpointCluster{ - Endpoints: GetEndpoints(backendName, resourceParams.BackendMapping), - Config: endpointConfig, - } - var healthcheck *HealthCheck - if resolvedBackend.HealthCheck != nil { - healthcheck = &HealthCheck{ - Interval: resolvedBackend.HealthCheck.Interval, - Timeout: resolvedBackend.HealthCheck.Timeout, - UnhealthyThreshold: resolvedBackend.HealthCheck.UnhealthyThreshold, - HealthyThreshold: resolvedBackend.HealthCheck.HealthyThreshold, - } - } - - var securityConfig []EndpointSecurity - switch resolvedBackend.Security.Type { - case "Basic": - securityConfig = append(securityConfig, EndpointSecurity{ - Password: string(resolvedBackend.Security.Basic.Password), - Username: string(resolvedBackend.Security.Basic.Username), - Type: string(resolvedBackend.Security.Type), - Enabled: true, - }) - } - return securityConfig, healthcheck, endpointCluster -} - func (endpoint *Endpoint) validateEndpoint() error { if endpoint.Port == 0 || endpoint.Port > 65535 { return errors.New("endpoint port value should be between 0 and 65535") diff --git a/runtime/config-deployer-service/ballerina/APIClient.bal b/runtime/config-deployer-service/ballerina/APIClient.bal index f03c001bf1..ec7ea40124 100644 --- a/runtime/config-deployer-service/ballerina/APIClient.bal +++ b/runtime/config-deployer-service/ballerina/APIClient.bal @@ -1304,6 +1304,10 @@ public class APIClient { api1.setGraphQLSchema(definition); return definition; } + if apkConf.'type == API_TYPE_GRPC && definition is string { + api1.setProtoDefinition(definition); + return definition; + } if definition is string && definition.toString().trim().length() > 0 { retrievedDefinition = runtimeUtil:RuntimeAPICommonUtil_generateDefinition2(api1, definition); } else { @@ -1991,16 +1995,22 @@ public class APIClient { apkConf = check self.validateAndRetrieveAPKConfiguration(apkConfJson); } string? apiDefinition = (); - string definitionFileContent = check string:fromBytes(definitionFile.fileContent); string apiType = apkConf?.'type; if apiType == API_TYPE_REST { + string definitionFileContent = check string:fromBytes(definitionFile.fileContent); if definitionFile.fileName.endsWith(".yaml") { apiDefinition = check commons:newYamlUtil1().fromYamlStringToJson(definitionFileContent); } else if definitionFile.fileName.endsWith(".json") { apiDefinition = definitionFileContent; } - } else if apiType == API_TYPE_GRAPHQL || apiType == API_TYPE_GRPC { - apiDefinition = definitionFileContent; + } else if apiType == API_TYPE_GRAPHQL { + apiDefinition = check string:fromBytes(definitionFile.fileContent); + } else if apiType == API_TYPE_GRPC { + if definitionFile.fileName.endsWith(".zip") { + apiDefinition = definitionFile.fileContent.toBase64(); + } else if definitionFile.fileName.endsWith(".proto") { + apiDefinition = check string:fromBytes(definitionFile.fileContent); + } } if apkConf is () { return e909022("apkConfiguration is not provided", ()); diff --git a/runtime/config-deployer-service/ballerina/DeployerClient.bal b/runtime/config-deployer-service/ballerina/DeployerClient.bal index 3da0d6f928..8020838869 100644 --- a/runtime/config-deployer-service/ballerina/DeployerClient.bal +++ b/runtime/config-deployer-service/ballerina/DeployerClient.bal @@ -338,10 +338,10 @@ public class DeployerClient { model:StatusCause[] 'causes = details.'causes; foreach model:StatusCause 'cause in 'causes { if 'cause.'field == "spec.basePath" { - log:printError("Error occured while updating K8sAPI due to base path ", e909015(k8sAPI.spec.basePath)); + log:printError("Error occurred while updating K8sAPI due to base path ", e909015(k8sAPI.spec.basePath)); return e909015(k8sAPI.spec.basePath); } else if 'cause.'field == "spec.apiName" { - log:printError("Error occured while updating K8sAPI due to base path ", e909015(k8sAPI.spec.basePath)); + log:printError("Error occurred while updating K8sAPI due to base path ", e909015(k8sAPI.spec.basePath)); return e909016(k8sAPI.spec.apiName); } } diff --git a/runtime/config-deployer-service/ballerina/modules/org.wso2.apk.config.model/API.bal b/runtime/config-deployer-service/ballerina/modules/org.wso2.apk.config.model/API.bal index 8638e05bef..071207dc67 100644 --- a/runtime/config-deployer-service/ballerina/modules/org.wso2.apk.config.model/API.bal +++ b/runtime/config-deployer-service/ballerina/modules/org.wso2.apk.config.model/API.bal @@ -1,6 +1,7 @@ +import config_deployer_service.java.lang as javalang; + import ballerina/jballerina.java; import ballerina/jballerina.java.arrays as jarrays; -import config_deployer_service.java.lang as javalang; # Ballerina class mapping for the Java `org.wso2.apk.config.model.API` class. @java:Binding {'class: "org.wso2.apk.config.model.API"} @@ -40,6 +41,13 @@ public distinct class API { return java:toString(org_wso2_apk_config_model_API_getApiSecurity(self.jObj)) ?: ""; } + # The function that maps to the `getBasePath` method of `org.wso2.apk.config.model.API`. + # + # + return - The `string` value returning from the Java mapping. + public isolated function getBasePath() returns string { + return java:toString(org_wso2_apk_config_model_API_getBasePath(self.jObj)) ?: ""; + } + # The function that maps to the `getClass` method of `org.wso2.apk.config.model.API`. # # + return - The `javalang:Class` value returning from the Java mapping. @@ -49,13 +57,6 @@ public distinct class API { return newObj; } - # The function that maps to the `getBasePath` method of `org.wso2.apk.config.model.API`. - # - # + return - The `string` value returning from the Java mapping. - public isolated function getBasePath() returns string { - return java:toString(org_wso2_apk_config_model_API_getBasePath(self.jObj)) ?: ""; - } - # The function that maps to the `getEndpoint` method of `org.wso2.apk.config.model.API`. # # + return - The `string` value returning from the Java mapping. @@ -84,6 +85,13 @@ public distinct class API { return java:toString(org_wso2_apk_config_model_API_getName(self.jObj)) ?: ""; } + # The function that maps to the `getProtoDefinition` method of `org.wso2.apk.config.model.API`. + # + # + return - The `string` value returning from the Java mapping. + public isolated function getProtoDefinition() returns string { + return java:toString(org_wso2_apk_config_model_API_getProtoDefinition(self.jObj)) ?: ""; + } + # The function that maps to the `getScopes` method of `org.wso2.apk.config.model.API`. # # + return - The `string[]` value returning from the Java mapping. @@ -190,6 +198,13 @@ public distinct class API { org_wso2_apk_config_model_API_setName(self.jObj, java:fromString(arg0)); } + # The function that maps to the `setProtoDefinition` method of `org.wso2.apk.config.model.API`. + # + # + arg0 - The `string` value required to map with the Java method parameter. + public isolated function setProtoDefinition(string arg0) { + org_wso2_apk_config_model_API_setProtoDefinition(self.jObj, java:fromString(arg0)); + } + # The function that maps to the `setScopes` method of `org.wso2.apk.config.model.API`. # # + arg0 - The `string[]` value required to map with the Java method parameter. @@ -334,6 +349,12 @@ isolated function org_wso2_apk_config_model_API_getName(handle receiver) returns paramTypes: [] } external; +isolated function org_wso2_apk_config_model_API_getProtoDefinition(handle receiver) returns handle = @java:Method { + name: "getProtoDefinition", + 'class: "org.wso2.apk.config.model.API", + paramTypes: [] +} external; + isolated function org_wso2_apk_config_model_API_getScopes(handle receiver) returns handle = @java:Method { name: "getScopes", 'class: "org.wso2.apk.config.model.API", @@ -418,6 +439,12 @@ isolated function org_wso2_apk_config_model_API_setName(handle receiver, handle paramTypes: ["java.lang.String"] } external; +isolated function org_wso2_apk_config_model_API_setProtoDefinition(handle receiver, handle arg0) = @java:Method { + name: "setProtoDefinition", + 'class: "org.wso2.apk.config.model.API", + paramTypes: ["java.lang.String"] +} external; + isolated function org_wso2_apk_config_model_API_setScopes(handle receiver, handle arg0) = @java:Method { name: "setScopes", 'class: "org.wso2.apk.config.model.API", diff --git a/runtime/config-deployer-service/java/src/main/java/org/wso2/apk/config/RuntimeAPICommonUtil.java b/runtime/config-deployer-service/java/src/main/java/org/wso2/apk/config/RuntimeAPICommonUtil.java index 39dcc43a0f..463064ecc9 100644 --- a/runtime/config-deployer-service/java/src/main/java/org/wso2/apk/config/RuntimeAPICommonUtil.java +++ b/runtime/config-deployer-service/java/src/main/java/org/wso2/apk/config/RuntimeAPICommonUtil.java @@ -66,9 +66,8 @@ public static APIDefinitionValidationResponse validateOpenAPIDefinition(String t } } else if (APIConstants.ParserType.GRPC.name().equals(type.toUpperCase())) { if (inputByteArray != null && inputByteArray.length > 0) { - if (fileName.endsWith(".zip") || fileName.endsWith(".desc")) { - validationResponse = ProtoParserUtil.validateGRPCAPIDefinition( - inputByteArray, fileName, returnContent); + if (fileName.endsWith(".zip") || fileName.endsWith(".proto")) { + validationResponse = ProtoParserUtil.validateGRPCAPIDefinition(inputByteArray); } else { ProtoParserUtil.addErrorToValidationResponse(validationResponse, "Invalid definition file type provided."); diff --git a/runtime/config-deployer-service/java/src/main/java/org/wso2/apk/config/definitions/ProtoParser.java b/runtime/config-deployer-service/java/src/main/java/org/wso2/apk/config/definitions/ProtoParser.java index 80f5c23d0b..15745ca3ec 100644 --- a/runtime/config-deployer-service/java/src/main/java/org/wso2/apk/config/definitions/ProtoParser.java +++ b/runtime/config-deployer-service/java/src/main/java/org/wso2/apk/config/definitions/ProtoParser.java @@ -1,9 +1,11 @@ package org.wso2.apk.config.definitions; -import java.io.ByteArrayInputStream; +import java.io.*; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; @@ -144,40 +146,98 @@ public String generateAPIDefinition(API api, String swagger) throws APIManagemen return null; } - public API getAPIFromProtoFile(byte[] definition, String fileName) throws APIManagementException { - API api = new API(); - ProtoFile protoFile = getProtoFileFromDefinition(definition, fileName); - StringBuilder apiName = new StringBuilder(); - List sortedServices = new ArrayList<>(); - List uriTemplates = new ArrayList<>(); - - if (protoFile == null) { - throw new APIManagementException("Error in validating API definition"); + public API getAPIFromProtoFile(byte[] content, String fileName) throws APIManagementException { + try { + API api = new API(); + ProtoFile protoFile = new ProtoFile(); + List uriTemplates = new ArrayList<>(); + if (fileName.endsWith(".zip")) { + List protoContents = extractProtoFilesFromZip(content); + for (byte[] protoContent : protoContents) { + uriTemplates.addAll(processProtoFile(protoContent, protoFile)); + } + } else { + uriTemplates = processProtoFile(content, protoFile); + } + api.setBasePath(protoFile.getBasePath()); + api.setProtoDefinition(new String(content, java.nio.charset.StandardCharsets.UTF_8)); + api.setVersion(protoFile.getVersion()); + api.setName(protoFile.getApiName()); + api.setUriTemplates(uriTemplates.toArray(new URITemplate[0])); + return api; + } catch (Exception e) { + e.printStackTrace(); + throw new APIManagementException(e); } + } - System.out.println("Printing services"); - for (Service service : protoFile.getServices()) { - sortedServices.add(service.getServiceName()); + private List processProtoFile(byte[] definition, ProtoFile protoFile) throws APIManagementException { + String content = new String(definition, java.nio.charset.StandardCharsets.UTF_8); + String packageString = getPackageString(content); + List uriTemplates = new ArrayList<>(); + StringBuilder apiName = new StringBuilder().append(protoFile.getApiName()); + String packageName = getPackageName(packageString); + List services = new ArrayList<>(); + protoFile.setVersion(getVersion(packageString)); + protoFile.setBasePath(getBasePath(packageString)); + List serviceBlocks = extractServiceBlocks(content); + + for (String serviceBlock : serviceBlocks) { + String serviceName = getServiceName(serviceBlock); + List methodNames = extractMethodNames(serviceBlock); + Service service = new Service(serviceName, methodNames); + services.add(service); for (String method : service.getMethods()) { - System.out.println("Method " + method + "of service " + service.getServiceName()); URITemplate uriTemplate = new URITemplate(); - uriTemplate.setUriTemplate(protoFile.getPackageName() + "." + service.getServiceName()); + uriTemplate.setUriTemplate(packageName + "." + service.getServiceName()); uriTemplate.setVerb(method); uriTemplates.add(uriTemplate); } } - sortedServices.sort(String::compareTo); - for (String service : sortedServices) { - apiName.append(service).append("-"); + + for (Service service : services) { + apiName.append(service.getServiceName()).append("-"); } + protoFile.setApiName(apiName.toString()); + return uriTemplates; + } - api.setBasePath(protoFile.getBasePath()); - System.out.println("Basepath " + protoFile.getBasePath()); - api.setProtoDefinition(definition); - api.setVersion(protoFile.getVersion()); - api.setName(apiName.toString()); - api.setUriTemplates(uriTemplates.toArray(new URITemplate[0])); - return api; + private List extractProtoFilesFromZip(byte[] zipContent) throws APIManagementException { + List protoFiles = new ArrayList<>(); + try (ByteArrayInputStream bais = new ByteArrayInputStream(zipContent); + ZipInputStream zis = new ZipInputStream(bais)) { + + ZipEntry zipEntry; + while ((zipEntry = zis.getNextEntry()) != null) { + if (zipEntry.getName().endsWith(".proto")) { + protoFiles.add(readProtoFileBytesFromZip(zis)); + } + } + } catch (IOException e) { + e.printStackTrace(); + throw new APIManagementException("Failed to process zip file", e); + } + return protoFiles; + } + + private String readProtoFileFromZip(InputStream zis) throws IOException { + StringBuilder protoContent = new StringBuilder(); + BufferedReader reader = new BufferedReader(new InputStreamReader(zis)); + String line; + while ((line = reader.readLine()) != null) { + protoContent.append(line).append("\n"); + } + return protoContent.toString(); + } + + private byte[] readProtoFileBytesFromZip(ZipInputStream zis) throws IOException { + ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); + byte[] buffer = new byte[1024]; + int bytesRead; + while ((bytesRead = zis.read(buffer)) != -1) { + byteArrayOutputStream.write(buffer, 0, bytesRead); + } + return byteArrayOutputStream.toByteArray(); } ProtoFile getProtoFileFromDefinition(byte[] fileContent, String fileName) { @@ -208,7 +268,6 @@ ProtoFile getProtoFileFromDefinition(byte[] fileContent, String fileName) { tempProtoFile.setPackageName(info[info.length - 1]); StringBuilder basePath = new StringBuilder("/").append(info[0]); for (int i = 1; i < info.length - 2; i++) { - System.out.println(info[i]); basePath.append(".").append(info[i]); } tempProtoFile.setBasePath(basePath.toString()); @@ -221,7 +280,8 @@ ProtoFile getProtoFileFromDefinition(byte[] fileContent, String fileName) { } /** - * @param fileName - The name of the .desc file provided as input for the config generator + * @param fileName - The name of the .desc file provided as input for + * the config generator * @param descriptorMap * @param protoMap * @param services @@ -262,7 +322,8 @@ private Descriptors.FileDescriptor resolveDependency(Map protoMap, Map descriptorMap, Map wellKnownTypesMap) { - // this scenario is when you have an import in your proto file but that file hasnt been built yet + // this scenario is when you have an import in your proto file but that file + // hasnt been built yet // in that scenario, it needs to have its dependencies resolved as well DescriptorProtos.FileDescriptorProto dependencyProto = protoMap.get(descriptorName); if (dependencyProto != null) { - // Descriptors.FileDescriptor dependency = resolveDependency(descriptorMap, protoMap, wellKnownTypesMap, - // descriptorName); - // descriptorMap.put(dependency.getName(), dependency); - // return dependency; + // Descriptors.FileDescriptor dependency = resolveDependency(descriptorMap, + // protoMap, wellKnownTypesMap, + // descriptorName); + // descriptorMap.put(dependency.getName(), dependency); + // return dependency; } return null; } boolean validateProtoContent(byte[] fileContent, String fileName) { try { - ProtoFile protoFile = getProtoFileFromDefinition(fileContent, fileName); - return protoFile != null; + // ProtoFile protoFile = getProtoFileFromDefinition(fileContent, fileName); + return true; } catch (Exception e) { log.error("Proto definition validation failed for " + fileName + ": " + e.getMessage()); return false; @@ -311,7 +374,7 @@ public ArrayList convertServiceDescriptorsToServices( for (Descriptors.MethodDescriptor methodDescriptor : methodDescriptors) { methods.add(methodDescriptor.getName()); } - services.add(new Service(serviceDescriptor.getName(), methods, packageName)); + services.add(new Service(serviceDescriptor.getName(), methods)); } return services; } @@ -319,22 +382,22 @@ public ArrayList convertServiceDescriptorsToServices( public void validateGRPCAPIDefinition(byte[] inputByteArray, String fileName, APIDefinitionValidationResponse validationResponse, ArrayList errors) { try { - // if (fileName.endsWith(".zip")) { - // try (ZipInputStream zis = new ZipInputStream(new ByteArrayInputStream(inputByteArray))) { - // ZipEntry zipEntry; - // while ((zipEntry = zis.getNextEntry()) != null) { - // if (!zipEntry.isDirectory() && zipEntry.getName().endsWith(".desc")) { - // byte[] protoFileContentBytes = zis.readAllBytes(); - // boolean validated = validateProtoContent(protoFileContentBytes, fileName); - // if (!validated) { - // throw new APIManagementException( - // "Invalid definition file provided. " + "Please provide a valid .zip or .proto file."); - // } - // } - // } - // } - // } else - if (fileName.endsWith(".desc")) { + if (fileName.endsWith(".zip")) { + try (ZipInputStream zis = new ZipInputStream(new ByteArrayInputStream(inputByteArray))) { + ZipEntry zipEntry; + while ((zipEntry = zis.getNextEntry()) != null) { + if (!zipEntry.isDirectory() && zipEntry.getName().endsWith(".proto")) { + byte[] protoFileContentBytes = zis.readAllBytes(); + boolean validated = validateProtoContent(protoFileContentBytes, fileName); + if (!validated) { + throw new APIManagementException( + "Invalid definition file provided. " + + "Please provide a valid .zip or .proto file."); + } + } + } + } + } else if (fileName.endsWith(".proto")) { boolean validated = validateProtoContent(inputByteArray, fileName); validationResponse.setValid(validated); validationResponse.setProtoContent(inputByteArray); @@ -353,4 +416,96 @@ public void validateGRPCAPIDefinition(byte[] inputByteArray, String fileName, validationResponse.setErrorItems(errors); } } + + // Method to extract service blocks from a given text + public List extractServiceBlocks(String text) { + // Regular expression pattern to match the service blocks + String patternString = "service\\s+\\w+\\s*\\{[^{}]*(?:\\{[^{}]*\\}[^{}]*)*\\}"; + + // Compile the regular expression + Pattern pattern = Pattern.compile(patternString, Pattern.DOTALL); + Matcher matcher = pattern.matcher(text); + + // Find all matches and append them to the result + List result = new ArrayList<>(); + while (matcher.find()) { + result.add(matcher.group()); + } + return result; + } + + public List extractMethodNames(String serviceBlock) { + // Regular expression pattern to match the method names + String patternString = "(?<=rpc\\s)\\w+"; + + // Compile the regular expression + Pattern pattern = Pattern.compile(patternString); + Matcher matcher = pattern.matcher(serviceBlock); + + // Find all matches and append them to the result + List result = new ArrayList<>(); + while (matcher.find()) { + result.add(matcher.group()); + } + return result; + } + + public String getServiceName(String serviceBlock) { + // Regular expression pattern to match the service name + String patternString = "(?<=service\\s)\\w+"; + + // Compile the regular expression + Pattern pattern = Pattern.compile(patternString); + Matcher matcher = pattern.matcher(serviceBlock); + + // Find the first match and return it + if (matcher.find()) { + return matcher.group(); + } + return null; + } + + public String getPackageString(String content) { + Pattern packagePattern = Pattern.compile("package\\s+([\\w\\.]+);"); + Matcher packageMatcher = packagePattern.matcher(content); + if (packageMatcher.find()) { + return packageMatcher.group(1); + } + log.error("Package has not been defined in the proto file"); + return null; + } + + public String getVersion(String packageString) { + Pattern versionPattern = Pattern.compile("v\\d+(\\.\\d+)*"); + Matcher versionMatcher = versionPattern.matcher(packageString); + if (versionMatcher.find()) { + return versionMatcher.group(0); + } + log.error("Version not found in proto file"); + return null; + } + + public String getPackageName(String packageString) { + Pattern namePattern = Pattern.compile("v\\d+(\\.\\d+)*\\.(\\w+)$"); + Matcher nameMatcher = namePattern.matcher(packageString); + if (nameMatcher.find()) { + return nameMatcher.group(2); + } + log.error("Package name not found in proto file."); + return null; + } + + public String getBasePath(String packageString) { + Pattern basePathPattern = Pattern.compile("^(.*?)v\\d"); + Matcher basePathMatcher = basePathPattern.matcher(packageString); + if (basePathMatcher.find()) { + String basePath = basePathMatcher.group(1); + if (basePath.charAt(basePath.length() - 1) == '.') { + basePath = basePath.substring(0, basePath.length() - 1); + } + return "/" + basePath; + } + log.error("Base path not found in proto file"); + return null; + } } diff --git a/runtime/config-deployer-service/java/src/main/java/org/wso2/apk/config/definitions/ProtoParserUtil.java b/runtime/config-deployer-service/java/src/main/java/org/wso2/apk/config/definitions/ProtoParserUtil.java index 056dc4586e..5c0d9fc91c 100644 --- a/runtime/config-deployer-service/java/src/main/java/org/wso2/apk/config/definitions/ProtoParserUtil.java +++ b/runtime/config-deployer-service/java/src/main/java/org/wso2/apk/config/definitions/ProtoParserUtil.java @@ -14,31 +14,30 @@ public class ProtoParserUtil { * Provide common functions related to OAS */ private static final Log log = LogFactory.getLog(ProtoParserUtil.class); - private static final ProtoParser protoParser = new ProtoParser(); - /** * Validate graphQL Schema - * + * * @return Validation response */ - public static APIDefinitionValidationResponse validateGRPCAPIDefinition(byte[] inputByteArray, String fileName, - boolean returnGRPCContent) { + public static APIDefinitionValidationResponse validateGRPCAPIDefinition(byte[] content) { APIDefinitionValidationResponse validationResponse = new APIDefinitionValidationResponse(); ArrayList errors = new ArrayList<>(); - if (inputByteArray.length == 0) { + if (content == null || content.length == 0) { validationResponse.setValid(false); errors.add(ExceptionCodes.GRPC_PROTO_DEFINTION_CANNOT_BE_NULL); validationResponse.setErrorItems(errors); } else { - protoParser.validateGRPCAPIDefinition(inputByteArray, fileName, validationResponse, errors); + validationResponse.setValid(true); + validationResponse.setProtoContent(content); } return validationResponse; } /** - * Add error item with the provided message to the provided validation response object + * Add error item with the provided message to the provided validation response + * object * * @param validationResponse APIDefinitionValidationResponse object * @param errMessage error message diff --git a/runtime/config-deployer-service/java/src/main/java/org/wso2/apk/config/model/API.java b/runtime/config-deployer-service/java/src/main/java/org/wso2/apk/config/model/API.java index 97ac33629e..74585cb6e4 100644 --- a/runtime/config-deployer-service/java/src/main/java/org/wso2/apk/config/model/API.java +++ b/runtime/config-deployer-service/java/src/main/java/org/wso2/apk/config/model/API.java @@ -5,16 +5,24 @@ public class API { private String basePath; private String version; private String type; - private String endpoint; private URITemplate[] uriTemplates = new URITemplate[0]; private String apiSecurity; private String[] scopes; private String graphQLSchema; - private byte[] protoDefinition; + private String protoDefinition; private String swaggerDefinition; private String environment; + public API(String name, String version, URITemplate[] uriTemplates) { + this.name = name; + this.version = version; + this.uriTemplates = uriTemplates; + } + + public API() { + } + public String getType() { return type; } @@ -47,23 +55,14 @@ public void setUriTemplates(URITemplate[] uriTemplates) { this.uriTemplates = uriTemplates; } - public API(String name, String version, URITemplate[] uriTemplates) { - this.name = name; - this.version = version; - this.uriTemplates = uriTemplates; - } - - public API() { + public String getApiSecurity() { + return apiSecurity; } public void setApiSecurity(String apiSecurity) { this.apiSecurity = apiSecurity; } - public String getApiSecurity() { - return apiSecurity; - } - public String[] getScopes() { return scopes; } @@ -88,12 +87,12 @@ public void setSwaggerDefinition(String swaggerDefinition) { this.swaggerDefinition = swaggerDefinition; } - public void setProtoDefinition(byte[] protoDefinition) { - this.protoDefinition = protoDefinition; + public String getProtoDefinition() { + return protoDefinition; } - public byte[] getProtoDefinition() { - return protoDefinition; + public void setProtoDefinition(String protoDefinition) { + this.protoDefinition = protoDefinition; } public String getBasePath() { diff --git a/runtime/config-deployer-service/java/src/main/java/org/wso2/apk/config/model/proto/ProtoFile.java b/runtime/config-deployer-service/java/src/main/java/org/wso2/apk/config/model/proto/ProtoFile.java index c72df2ca5c..9288a359c0 100644 --- a/runtime/config-deployer-service/java/src/main/java/org/wso2/apk/config/model/proto/ProtoFile.java +++ b/runtime/config-deployer-service/java/src/main/java/org/wso2/apk/config/model/proto/ProtoFile.java @@ -4,11 +4,24 @@ import java.util.List; public class ProtoFile { + + String apiName; String packageName; String basePath; String version; List services; + public String getApiName() { + if (apiName == null) { + return ""; + } + return apiName; + } + + public void setApiName(String apiName) { + this.apiName = apiName; + } + public List getServices() { return services; } @@ -36,6 +49,13 @@ public void setServices(List services) { this.services.addAll(services); } + public void addService(Service service) { + if (this.services == null){ + this.services = new ArrayList<>(); + } + this.services.add(service); + } + public String getBasePath() { return basePath; } diff --git a/runtime/config-deployer-service/java/src/main/java/org/wso2/apk/config/model/proto/Service.java b/runtime/config-deployer-service/java/src/main/java/org/wso2/apk/config/model/proto/Service.java index 988d5a1481..fc34279d55 100644 --- a/runtime/config-deployer-service/java/src/main/java/org/wso2/apk/config/model/proto/Service.java +++ b/runtime/config-deployer-service/java/src/main/java/org/wso2/apk/config/model/proto/Service.java @@ -4,29 +4,33 @@ public class Service { String serviceName; - String packageName; List methods; - public String getServiceName() { - return serviceName; + public void setServiceName(String serviceName) { + this.serviceName = serviceName; } - public String getPackageName() { - return packageName; + public void setMethods(List methods) { + this.methods = methods; + } + + public String getServiceName() { + return serviceName; } public List getMethods() { return methods; } - public Service(String serviceName, List methods, String packageName) { + public Service() { } + + public Service(String serviceName, List methods) { this.serviceName = serviceName; this.methods = methods; - this.packageName = packageName; } @Override public String toString() { - return "Service{" + "serviceName='" + serviceName + '\'' + ", packageName='" + packageName + '\'' + ", methods=" + methods + '}'; + return "Service{" + "serviceName='" + serviceName + '\'' + ", methods=" + methods + '}'; } } \ No newline at end of file From ee4456e5a8d603220530452dfaf3a90c224802fd Mon Sep 17 00:00:00 2001 From: sgayangi Date: Mon, 9 Sep 2024 12:15:48 +0530 Subject: [PATCH 11/17] Resolve merge conflicts with main --- .../envoyconf/routes_with_clusters_test.go | 1 + .../oasparser/model/adapter_internal_api.go | 2 +- .../operator/controllers/dp/api_controller.go | 2 +- .../operator/synchronizer/api_state.go | 2 +- .../synchronizer/zz_generated.deepcopy.go | 35 +++++++++++++++++-- .../dp/ratelimitpolicy_controller.go | 10 +++--- 6 files changed, 41 insertions(+), 11 deletions(-) diff --git a/adapter/internal/oasparser/envoyconf/routes_with_clusters_test.go b/adapter/internal/oasparser/envoyconf/routes_with_clusters_test.go index 4ef7526a2d..c074c36b19 100644 --- a/adapter/internal/oasparser/envoyconf/routes_with_clusters_test.go +++ b/adapter/internal/oasparser/envoyconf/routes_with_clusters_test.go @@ -32,6 +32,7 @@ import ( "github.com/wso2/apk/common-go-libs/apis/dp/v1alpha1" "github.com/wso2/apk/common-go-libs/apis/dp/v1alpha2" "github.com/wso2/apk/common-go-libs/apis/dp/v1alpha3" + "github.com/wso2/apk/common-go-libs/apis/dp/v1beta1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" k8types "k8s.io/apimachinery/pkg/types" gwapiv1 "sigs.k8s.io/gateway-api/apis/v1" diff --git a/adapter/internal/oasparser/model/adapter_internal_api.go b/adapter/internal/oasparser/model/adapter_internal_api.go index 5dd8f22fc1..dcf6006764 100644 --- a/adapter/internal/oasparser/model/adapter_internal_api.go +++ b/adapter/internal/oasparser/model/adapter_internal_api.go @@ -1183,7 +1183,7 @@ func (adapterInternalAPI *AdapterInternalAPI) SetInfoGRPCRouteCR(grpcRoute *gwap if outputAPIPolicy != nil { apiPolicy = *outputAPIPolicy } - var ratelimitPolicy *dpv1alpha1.RateLimitPolicy + var ratelimitPolicy *dpv1alpha3.RateLimitPolicy if outputRatelimitPolicy != nil { ratelimitPolicy = *outputRatelimitPolicy } diff --git a/adapter/internal/operator/controllers/dp/api_controller.go b/adapter/internal/operator/controllers/dp/api_controller.go index 60967fb77d..ac251aab39 100644 --- a/adapter/internal/operator/controllers/dp/api_controller.go +++ b/adapter/internal/operator/controllers/dp/api_controller.go @@ -639,7 +639,7 @@ func (apiReconciler *APIReconciler) concatGRPCRoutes(ctx context.Context, grpcRo } resolvedBackend := utils.GetResolvedBackend(ctx, apiReconciler.client, backendNamespacedName, &api) if resolvedBackend != nil { - grpcRouteState.BackendMapping = map[string]*dpv1alpha1.ResolvedBackend{ + grpcRouteState.BackendMapping = map[string]*dpv1alpha2.ResolvedBackend{ backendNamespacedName.String(): resolvedBackend, } return grpcRouteState, nil diff --git a/adapter/internal/operator/synchronizer/api_state.go b/adapter/internal/operator/synchronizer/api_state.go index 71fc19b489..ac953a68c4 100644 --- a/adapter/internal/operator/synchronizer/api_state.go +++ b/adapter/internal/operator/synchronizer/api_state.go @@ -78,6 +78,6 @@ type GQLRouteState struct { type GRPCRouteState struct { GRPCRouteCombined *gwapiv1a2.GRPCRoute GRPCRoutePartitions map[string]*gwapiv1a2.GRPCRoute - BackendMapping map[string]*v1alpha1.ResolvedBackend + BackendMapping map[string]*v1alpha2.ResolvedBackend Scopes map[string]v1alpha1.Scope } diff --git a/adapter/internal/operator/synchronizer/zz_generated.deepcopy.go b/adapter/internal/operator/synchronizer/zz_generated.deepcopy.go index e6d5500922..22057cc268 100644 --- a/adapter/internal/operator/synchronizer/zz_generated.deepcopy.go +++ b/adapter/internal/operator/synchronizer/zz_generated.deepcopy.go @@ -76,6 +76,13 @@ func (in *APIState) DeepCopyInto(out *APIState) { (*out)[key] = *val.DeepCopy() } } + if in.RateLimitPolicies != nil { + in, out := &in.RateLimitPolicies, &out.RateLimitPolicies + *out = make(map[string]v1alpha3.RateLimitPolicy, len(*in)) + for key, val := range *in { + (*out)[key] = *val.DeepCopy() + } + } if in.ResourceAuthentications != nil { in, out := &in.ResourceAuthentications, &out.ResourceAuthentications *out = make(map[string]v1alpha2.Authentication, len(*in)) @@ -83,6 +90,13 @@ func (in *APIState) DeepCopyInto(out *APIState) { (*out)[key] = *val.DeepCopy() } } + if in.ResourceRateLimitPolicies != nil { + in, out := &in.ResourceRateLimitPolicies, &out.ResourceRateLimitPolicies + *out = make(map[string]v1alpha3.RateLimitPolicy, len(*in)) + for key, val := range *in { + (*out)[key] = *val.DeepCopy() + } + } if in.ResourceAPIPolicies != nil { in, out := &in.ResourceAPIPolicies, &out.ResourceAPIPolicies *out = make(map[string]v1alpha3.APIPolicy, len(*in)) @@ -215,14 +229,14 @@ func (in *GRPCRouteState) DeepCopyInto(out *GRPCRouteState) { } if in.BackendMapping != nil { in, out := &in.BackendMapping, &out.BackendMapping - *out = make(map[string]*v1alpha1.ResolvedBackend, len(*in)) + *out = make(map[string]*v1alpha2.ResolvedBackend, len(*in)) for key, val := range *in { - var outVal *v1alpha1.ResolvedBackend + var outVal *v1alpha2.ResolvedBackend if val == nil { (*out)[key] = nil } else { in, out := &val, &outVal - *out = new(v1alpha1.ResolvedBackend) + *out = new(v1alpha2.ResolvedBackend) (*in).DeepCopyInto(*out) } (*out)[key] = outVal @@ -329,6 +343,21 @@ func (in *GatewayStateData) DeepCopyInto(out *GatewayStateData) { (*out)[key] = *val.DeepCopy() } } + if in.GatewayCustomRateLimitPolicies != nil { + in, out := &in.GatewayCustomRateLimitPolicies, &out.GatewayCustomRateLimitPolicies + *out = make(map[string]*v1alpha3.RateLimitPolicy, len(*in)) + for key, val := range *in { + var outVal *v1alpha3.RateLimitPolicy + if val == nil { + (*out)[key] = nil + } else { + in, out := &val, &outVal + *out = new(v1alpha3.RateLimitPolicy) + (*in).DeepCopyInto(*out) + } + (*out)[key] = outVal + } + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GatewayStateData. diff --git a/common-controller/internal/operator/controllers/dp/ratelimitpolicy_controller.go b/common-controller/internal/operator/controllers/dp/ratelimitpolicy_controller.go index a4378ec264..c9350dde0e 100644 --- a/common-controller/internal/operator/controllers/dp/ratelimitpolicy_controller.go +++ b/common-controller/internal/operator/controllers/dp/ratelimitpolicy_controller.go @@ -150,9 +150,9 @@ func (ratelimitReconciler *RateLimitPolicyReconciler) Reconcile(ctx context.Cont xds.DeleteCustomRateLimitPolicies(resolveCustomRateLimitPolicy) xds.UpdateRateLimiterPolicies(conf.CommonController.Server.Label) } - resolveSubscriptionRatelimitPolicy, foundSubscription := ratelimitReconsiler.ods.GetResolveSubscriptionRatelimitPolicy(req.NamespacedName) + resolveSubscriptionRatelimitPolicy, foundSubscription := ratelimitReconciler.ods.GetResolveSubscriptionRatelimitPolicy(req.NamespacedName) if foundSubscription && k8error.IsNotFound(err) { - ratelimitReconsiler.ods.DeleteSubscriptionRatelimitPolicy(req.NamespacedName) + ratelimitReconciler.ods.DeleteSubscriptionRatelimitPolicy(req.NamespacedName) logger.Debug("Deleting SubscriptionRateLimitPolicy : ", resolveSubscriptionRatelimitPolicy) xds.DeleteSubscriptionRateLimitPolicies(resolveSubscriptionRatelimitPolicy) xds.UpdateRateLimiterPolicies(conf.CommonController.Server.Label) @@ -172,8 +172,8 @@ func (ratelimitReconciler *RateLimitPolicyReconciler) Reconcile(ctx context.Cont xds.UpdateRateLimitXDSCacheForCustomPolicies(customRateLimitPolicy) xds.UpdateRateLimiterPolicies(conf.CommonController.Server.Label) } else if ratelimitPolicy.Spec.Override != nil && ratelimitPolicy.Spec.Override.Subscription != nil { - var resolveSubscriptionRatelimitPolicy = ratelimitReconsiler.marshelSubscriptionRateLimit(ratelimitPolicy) - ratelimitReconsiler.ods.AddorUpdateResolveSubscriptionRatelimitToStore(ratelimitKey, resolveSubscriptionRatelimitPolicy) + var resolveSubscriptionRatelimitPolicy = ratelimitReconciler.marshelSubscriptionRateLimit(ratelimitPolicy) + ratelimitReconciler.ods.AddorUpdateResolveSubscriptionRatelimitToStore(ratelimitKey, resolveSubscriptionRatelimitPolicy) xds.UpdateRateLimitXDSCacheForSubscriptionPolicies(resolveSubscriptionRatelimitPolicy) xds.UpdateRateLimiterPolicies(conf.CommonController.Server.Label) } else { @@ -255,7 +255,7 @@ func (ratelimitReconciler *RateLimitPolicyReconciler) getRatelimitForHTTPRoute(c return requests } -func (ratelimitReconsiler *RateLimitPolicyReconciler) marshelSubscriptionRateLimit( +func (ratelimitReconciler *RateLimitPolicyReconciler) marshelSubscriptionRateLimit( ratelimitPolicy dpv1alpha3.RateLimitPolicy) dpv1alpha3.ResolveSubscriptionRatelimitPolicy { var resolveSubscriptionRatelimit dpv1alpha3.ResolveSubscriptionRatelimitPolicy From 95e8f1f0ea7622a985b8ce03530c125db65984af Mon Sep 17 00:00:00 2001 From: sgayangi Date: Wed, 11 Sep 2024 13:56:52 +0530 Subject: [PATCH 12/17] Update parser for proto files --- .../ballerina/ConfigGenreatorClient.bal | 24 ++- .../RuntimeAPICommonUtil.bal | 15 +- .../wso2/apk/config/RuntimeAPICommonUtil.java | 8 +- .../apk/config/definitions/ProtoParser.java | 166 ++---------------- 4 files changed, 43 insertions(+), 170 deletions(-) diff --git a/runtime/config-deployer-service/ballerina/ConfigGenreatorClient.bal b/runtime/config-deployer-service/ballerina/ConfigGenreatorClient.bal index d56bd2ac3a..108e058290 100644 --- a/runtime/config-deployer-service/ballerina/ConfigGenreatorClient.bal +++ b/runtime/config-deployer-service/ballerina/ConfigGenreatorClient.bal @@ -50,7 +50,14 @@ public class ConfigGeneratorClient { record {|byte[] fileContent; string fileName; anydata...;|} definition = definitionBody.definition; fileName = definition.fileName; } - apiFromDefinition = check runtimeUtil:RuntimeAPICommonUtil_getGRPCAPIFromProtoDefinition(check validateAndRetrieveDefinitionResult.getProtoContent(), fileName); + do { + apiFromDefinition = check runtimeUtil:RuntimeAPICommonUtil_getGRPCAPIFromProtoDefinition(check validateAndRetrieveDefinitionResult.getProtoContent(), fileName); + } + on fail var e { + if e is error { + return e909022("Error occurred while validating the .proto definition", ()); + } + } } else { apiFromDefinition = check runtimeUtil:RuntimeAPICommonUtil_getAPIFromDefinition(validateAndRetrieveDefinitionResult.getContent(), apiType); } @@ -72,17 +79,21 @@ public class ConfigGeneratorClient { BadRequestError badRequest = {body: {code: 90091, message: "Invalid API Definition", 'error: errorItems}}; return badRequest; } - } else if validateAndRetrieveDefinitionResult is runtimeapi:APIManagementException { + } + else if validateAndRetrieveDefinitionResult is runtimeapi:APIManagementException { return e909022("Error occured while validating the definition", validateAndRetrieveDefinitionResult.cause()); } else { return e909022("Error occured while validating the definition", ()); } - } on fail var e { - if e is commons:APKError { + } +on fail var e { + if e + is commons:APKError { return e; } return e909022("Internal error occured while creating APK conf", e); } + } private isolated function prepareDefinitionBodyFromRequest(http:Request request) returns DefinitionBody|error { DefinitionBody definitionBody = {}; @@ -102,6 +113,7 @@ public class ConfigGeneratorClient { } return definitionBody; } + private isolated function validateAndRetrieveDefinition(string 'type, string? url, byte[]? content, string? fileName) returns runtimeapi:APIDefinitionValidationResponse|runtimeapi:APIManagementException|error|commons:APKError { runtimeapi:APIDefinitionValidationResponse|runtimeapi:APIManagementException|error validationResponse; boolean typeAvailable = 'type.length() > 0; @@ -122,6 +134,7 @@ public class ConfigGeneratorClient { } return validationResponse; } + private isolated function retrieveDefinitionFromUrl(string url) returns string|error { string domain = getDomain(url); string path = getPath(url); @@ -136,6 +149,7 @@ public class ConfigGeneratorClient { } return e909044(); } + public isolated function getGeneratedK8sResources(http:Request request, commons:Organization organization) returns http:Response|BadRequestError|InternalServerErrorError|commons:APKError { GenerateK8sResourcesBody body = {}; do { @@ -167,6 +181,7 @@ public class ConfigGeneratorClient { return e909052(e); } } + private isolated function zipAPIArtifact(string apiId, model:APIArtifact apiArtifact) returns [string, string]|error { string zipDir = check file:createTempDir(uuid:createType1AsString()); model:API? k8sAPI = apiArtifact.api; @@ -245,6 +260,7 @@ public class ConfigGeneratorClient { } return e909022("Error while converting json to yaml", convertedYaml); } + private isolated function storeFile(string jsonString, string fileName, string? directroy = ()) returns error? { string fullPath = directroy ?: ""; fullPath = fullPath + file:pathSeparator + fileName + ".yaml"; diff --git a/runtime/config-deployer-service/ballerina/modules/org.wso2.apk.config/RuntimeAPICommonUtil.bal b/runtime/config-deployer-service/ballerina/modules/org.wso2.apk.config/RuntimeAPICommonUtil.bal index 3219532238..5de894a46e 100644 --- a/runtime/config-deployer-service/ballerina/modules/org.wso2.apk.config/RuntimeAPICommonUtil.bal +++ b/runtime/config-deployer-service/ballerina/modules/org.wso2.apk.config/RuntimeAPICommonUtil.bal @@ -167,10 +167,15 @@ public isolated function RuntimeAPICommonUtil_getAPIFromDefinition(string arg0, # + arg0 - The `byte[]` value required to map with the Java method parameter. # + arg1 - The `string` value required to map with the Java method parameter. # + return - The `orgwso2apkconfigmodel:API` value returning from the Java mapping. -public isolated function RuntimeAPICommonUtil_getGRPCAPIFromProtoDefinition(byte[] arg0, string arg1) returns orgwso2apkconfigmodel:API|error { - handle externalObj = org_wso2_apk_config_RuntimeAPICommonUtil_getGRPCAPIFromProtoDefinition(check jarrays:toHandle(arg0, "byte"), java:fromString(arg1)); - orgwso2apkconfigmodel:API newObj = new (externalObj); - return newObj; +public isolated function RuntimeAPICommonUtil_getGRPCAPIFromProtoDefinition(byte[] arg0, string arg1) returns orgwso2apkconfigmodel:API|orgwso2apkconfigapi:APIManagementException|error { + handle|error externalObj = org_wso2_apk_config_RuntimeAPICommonUtil_getGRPCAPIFromProtoDefinition(check jarrays:toHandle(arg0, "byte"), java:fromString(arg1)); + if (externalObj is error) { + orgwso2apkconfigapi:APIManagementException e = error orgwso2apkconfigapi:APIManagementException(orgwso2apkconfigapi:APIMANAGEMENTEXCEPTION, externalObj, message = externalObj.message()); + return e; + } else { + orgwso2apkconfigmodel:API newObj = new (externalObj); + return newObj; + } } # The function that maps to the `validateOpenAPIDefinition` method of `org.wso2.apk.config.RuntimeAPICommonUtil`. @@ -228,7 +233,7 @@ isolated function org_wso2_apk_config_RuntimeAPICommonUtil_getClass(handle recei paramTypes: [] } external; -isolated function org_wso2_apk_config_RuntimeAPICommonUtil_getGRPCAPIFromProtoDefinition(handle arg0, handle arg1) returns handle = @java:Method { +isolated function org_wso2_apk_config_RuntimeAPICommonUtil_getGRPCAPIFromProtoDefinition(handle arg0, handle arg1) returns handle|error = @java:Method { name: "getGRPCAPIFromProtoDefinition", 'class: "org.wso2.apk.config.RuntimeAPICommonUtil", paramTypes: ["[B", "java.lang.String"] diff --git a/runtime/config-deployer-service/java/src/main/java/org/wso2/apk/config/RuntimeAPICommonUtil.java b/runtime/config-deployer-service/java/src/main/java/org/wso2/apk/config/RuntimeAPICommonUtil.java index 463064ecc9..b03e0f3a42 100644 --- a/runtime/config-deployer-service/java/src/main/java/org/wso2/apk/config/RuntimeAPICommonUtil.java +++ b/runtime/config-deployer-service/java/src/main/java/org/wso2/apk/config/RuntimeAPICommonUtil.java @@ -80,13 +80,9 @@ public static APIDefinitionValidationResponse validateOpenAPIDefinition(String t return validationResponse; } - public static API getGRPCAPIFromProtoDefinition(byte[] definition, String fileName) { + public static API getGRPCAPIFromProtoDefinition(byte[] definition, String fileName) throws APIManagementException { ProtoParser protoParser = new ProtoParser(); - try { - return protoParser.getAPIFromProtoFile(definition, fileName); - } catch (APIManagementException e) { - throw new RuntimeException(e); - } + return protoParser.getAPIFromProtoFile(definition, fileName); } public static Set generateUriTemplatesFromAPIDefinition(String apiType, String content) diff --git a/runtime/config-deployer-service/java/src/main/java/org/wso2/apk/config/definitions/ProtoParser.java b/runtime/config-deployer-service/java/src/main/java/org/wso2/apk/config/definitions/ProtoParser.java index 15745ca3ec..1e846bc96d 100644 --- a/runtime/config-deployer-service/java/src/main/java/org/wso2/apk/config/definitions/ProtoParser.java +++ b/runtime/config-deployer-service/java/src/main/java/org/wso2/apk/config/definitions/ProtoParser.java @@ -9,7 +9,6 @@ import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; -import com.google.protobuf.*; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.wso2.apk.config.api.*; @@ -31,28 +30,6 @@ public class ProtoParser extends APIDefinition { public ProtoParser() { } - private static Descriptors.FileDescriptor resolveWellKnownType(String descriptorName) - throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException { - // Extract the proto file base name (e.g., "timestamp.proto" -> "Timestamp") - String baseName = descriptorName.substring(descriptorName.lastIndexOf('/') + 1, - descriptorName.lastIndexOf('.')); - // Convert to CamelCase (e.g., "timestamp" -> "Timestamp") - String className = baseName.substring(0, 1).toUpperCase() + baseName.substring(1); - // Find the corresponding class in the com.google.protobuf package - Class clazz = Class.forName("com.google.protobuf." + className); - // Use reflection to get the descriptor - Method getDescriptorMethod = clazz.getMethod("getDescriptor"); - Descriptors.Descriptor descriptor = (Descriptors.Descriptor) getDescriptorMethod.invoke(null); - return descriptor.getFile(); - } - - private static void populateProtoMap(Map protoMap, - DescriptorProtos.FileDescriptorSet fileDescriptorSet) { - for (DescriptorProtos.FileDescriptorProto fileDescriptorProto : fileDescriptorSet.getFileList()) { - protoMap.put(fileDescriptorProto.getName(), fileDescriptorProto); - } - } - @Override public Set getURITemplates(String resourceConfigsJSON) throws APIManagementException { // TODO Auto-generated method stub @@ -176,7 +153,13 @@ private List processProtoFile(byte[] definition, ProtoFile protoFil String packageString = getPackageString(content); List uriTemplates = new ArrayList<>(); StringBuilder apiName = new StringBuilder().append(protoFile.getApiName()); + if (packageString == null && protoFile.getPackageName() != null) { + throw new APIManagementException("Package string has not been defined in proto file"); + } String packageName = getPackageName(packageString); + if (packageName == null) { + packageName = packageString; + } List services = new ArrayList<>(); protoFile.setVersion(getVersion(packageString)); protoFile.setBasePath(getBasePath(packageString)); @@ -240,121 +223,6 @@ private byte[] readProtoFileBytesFromZip(ZipInputStream zis) throws IOException return byteArrayOutputStream.toByteArray(); } - ProtoFile getProtoFileFromDefinition(byte[] fileContent, String fileName) { - Map protoMap = new HashMap<>(); - Map descriptorMap = new HashMap<>(); - ArrayList services = new ArrayList<>(); - String packageName = ""; - ProtoFile tempProtoFile = new ProtoFile(); - Map wellKnownTypesMap = new HashMap<>(); - try { - DescriptorProtos.FileDescriptorSet fileDescriptorSet = DescriptorProtos.FileDescriptorSet.parseFrom( - fileContent); - - populateProtoMap(protoMap, fileDescriptorSet); - - for (DescriptorProtos.FileDescriptorProto fileDescriptorProto : fileDescriptorSet.getFileList()) { - packageName = processFileDescriptor(fileName, descriptorMap, protoMap, services, wellKnownTypesMap, - fileDescriptorProto); - } - - tempProtoFile.setServices(convertServiceDescriptorsToServices(services, packageName)); - String[] info = packageName.split("\\."); - if (info.length < 3) { - throw new APIManagementException( - "Invalid package name: specify in the format of basepath.version.packageName"); - } - tempProtoFile.setVersion(info[info.length - 2]); - tempProtoFile.setPackageName(info[info.length - 1]); - StringBuilder basePath = new StringBuilder("/").append(info[0]); - for (int i = 1; i < info.length - 2; i++) { - basePath.append(".").append(info[i]); - } - tempProtoFile.setBasePath(basePath.toString()); - return tempProtoFile; - } catch (Exception e) { - e.printStackTrace(); - log.error("Proto definition validation failed for " + fileName + ": " + e.getMessage()); - return null; - } - } - - /** - * @param fileName - The name of the .desc file provided as input for - * the config generator - * @param descriptorMap - * @param protoMap - * @param services - * @param wellKnownTypesMap - * @param fileDescriptorProto - * @return - * @throws Descriptors.DescriptorValidationException - */ - private String processFileDescriptor(String fileName, Map descriptorMap, - Map protoMap, - ArrayList services, - Map wellKnownTypesMap, - DescriptorProtos.FileDescriptorProto fileDescriptorProto) throws Descriptors.DescriptorValidationException { - - String packageName = fileDescriptorProto.getPackage(); - - // Process and resolve dependencies for a given file descriptor - Descriptors.FileDescriptor[] dependencies = fileDescriptorProto.getDependencyList().stream() - .map(descriptorMap::get).toArray(Descriptors.FileDescriptor[]::new); - - // Build the file descriptor based on the proto and its dependencies - Descriptors.FileDescriptor fileDescriptor = Descriptors.FileDescriptor.buildFrom(fileDescriptorProto, - dependencies); - services.addAll(fileDescriptor.getServices()); - descriptorMap.put(fileDescriptor.getName(), fileDescriptor); - return packageName; - } - - private Descriptors.FileDescriptor resolveDependency(Map descriptorMap, - Map protoMap, - Map wellKnownTypesMap, String descriptorName) { - Descriptors.FileDescriptor dependency = descriptorMap.get(descriptorName); - // Dependency has not been resolved yet - if (dependency == null) { - try { - // if the dependency is a well known type - if (descriptorName.startsWith("com.google.protobuf.")) { - dependency = resolveWellKnownType(descriptorName); - wellKnownTypesMap.put(descriptorName, dependency); - } else { - // if the dependency is on another file that was imported, we resolve it and add - // it to the - // descriptor map - dependency = buildAndCacheDescriptor(descriptorName, protoMap, descriptorMap, wellKnownTypesMap); - } - } catch (Exception e) { - System.err.println("Error loading well-known type: " + descriptorName + " - " + e.getMessage()); - } - } - if (dependency == null) { - System.err.println("Missing dependency for " + descriptorName); - } - return dependency; - } - - private Descriptors.FileDescriptor buildAndCacheDescriptor(String descriptorName, - Map protoMap, - Map descriptorMap, - Map wellKnownTypesMap) { - // this scenario is when you have an import in your proto file but that file - // hasnt been built yet - // in that scenario, it needs to have its dependencies resolved as well - DescriptorProtos.FileDescriptorProto dependencyProto = protoMap.get(descriptorName); - if (dependencyProto != null) { - // Descriptors.FileDescriptor dependency = resolveDependency(descriptorMap, - // protoMap, wellKnownTypesMap, - // descriptorName); - // descriptorMap.put(dependency.getName(), dependency); - // return dependency; - } - return null; - } - boolean validateProtoContent(byte[] fileContent, String fileName) { try { // ProtoFile protoFile = getProtoFileFromDefinition(fileContent, fileName); @@ -365,20 +233,6 @@ boolean validateProtoContent(byte[] fileContent, String fileName) { } } - public ArrayList convertServiceDescriptorsToServices( - ArrayList serviceDescriptors, String packageName) { - ArrayList services = new ArrayList<>(); - for (Descriptors.ServiceDescriptor serviceDescriptor : serviceDescriptors) { - List methodDescriptors = serviceDescriptor.getMethods(); - ArrayList methods = new ArrayList<>(); - for (Descriptors.MethodDescriptor methodDescriptor : methodDescriptors) { - methods.add(methodDescriptor.getName()); - } - services.add(new Service(serviceDescriptor.getName(), methods)); - } - return services; - } - public void validateGRPCAPIDefinition(byte[] inputByteArray, String fileName, APIDefinitionValidationResponse validationResponse, ArrayList errors) { try { @@ -391,8 +245,7 @@ public void validateGRPCAPIDefinition(byte[] inputByteArray, String fileName, boolean validated = validateProtoContent(protoFileContentBytes, fileName); if (!validated) { throw new APIManagementException( - "Invalid definition file provided. " - + "Please provide a valid .zip or .proto file."); + "Invalid definition file provided. " + "Please provide a valid .zip or .proto file."); } } } @@ -466,10 +319,13 @@ public String getServiceName(String serviceBlock) { } public String getPackageString(String content) { + // package string has the format "package something" Pattern packagePattern = Pattern.compile("package\\s+([\\w\\.]+);"); Matcher packageMatcher = packagePattern.matcher(content); if (packageMatcher.find()) { - return packageMatcher.group(1); + if (packageMatcher.group().length() > 1) { + return packageMatcher.group(1); + } } log.error("Package has not been defined in the proto file"); return null; From d598d05a9afc5b1ab5ed3ff9be34174fcdd81fec Mon Sep 17 00:00:00 2001 From: sgayangi Date: Wed, 11 Sep 2024 13:57:50 +0530 Subject: [PATCH 13/17] Update API method for GRPC analytics --- adapter/internal/oasparser/model/adapter_internal_api.go | 4 ++-- adapter/internal/operator/utils/utils.go | 2 +- .../collectors/impl/SuccessRequestDataCollector.java | 6 +++++- .../org/wso2/apk/enforcer/commons/model/ResourceConfig.java | 2 +- .../org/wso2/apk/enforcer/analytics/AnalyticsFilter.java | 1 - 5 files changed, 9 insertions(+), 6 deletions(-) diff --git a/adapter/internal/oasparser/model/adapter_internal_api.go b/adapter/internal/oasparser/model/adapter_internal_api.go index dcf6006764..c482d08e99 100644 --- a/adapter/internal/oasparser/model/adapter_internal_api.go +++ b/adapter/internal/oasparser/model/adapter_internal_api.go @@ -1179,7 +1179,7 @@ func (adapterInternalAPI *AdapterInternalAPI) SetInfoGRPCRouteCR(grpcRoute *gwap if outputAuthScheme != nil { authScheme = *outputAuthScheme } - var apiPolicy *dpv1alpha2.APIPolicy + var apiPolicy *dpv1alpha3.APIPolicy if outputAPIPolicy != nil { apiPolicy = *outputAPIPolicy } @@ -1300,7 +1300,7 @@ func (adapterInternalAPI *AdapterInternalAPI) SetInfoGRPCRouteCR(grpcRoute *gwap resourcePath := adapterInternalAPI.GetXWso2Basepath() + "." + *match.Method.Service + "/" + *match.Method.Method endPoints = append(endPoints, GetEndpoints(backendName, resourceParams.BackendMapping)...) resource := &Resource{path: resourcePath, pathMatchType: "Exact", - methods: []*Operation{{iD: uuid.New().String(), method: "post", policies: policies, + methods: []*Operation{{iD: uuid.New().String(), method: "GRPC", policies: policies, auth: apiAuth, rateLimitPolicy: parseRateLimitPolicyToInternal(resourceRatelimitPolicy), scopes: scopes}}, iD: uuid.New().String(), } diff --git a/adapter/internal/operator/utils/utils.go b/adapter/internal/operator/utils/utils.go index a8a316f517..6624481d10 100644 --- a/adapter/internal/operator/utils/utils.go +++ b/adapter/internal/operator/utils/utils.go @@ -628,7 +628,7 @@ func GetBackendJWT(ctx context.Context, client k8client.Client, namespace, // GetAIProvider reads AIProvider when aiProviderReference is given func GetAIProvider(ctx context.Context, client k8client.Client, namespace string, - aiProviderReference string, api *dpv1alpha2.API) *dpv1alpha3.AIProvider { + aiProviderReference string, api *dpv1beta1.API) *dpv1alpha3.AIProvider { aiProvider := &dpv1alpha3.AIProvider{} aiProviderRef := types.NamespacedName{ Namespace: namespace, diff --git a/gateway/enforcer/org.wso2.apk.enforcer.commons/src/main/java/org/wso2/apk/enforcer/commons/analytics/collectors/impl/SuccessRequestDataCollector.java b/gateway/enforcer/org.wso2.apk.enforcer.commons/src/main/java/org/wso2/apk/enforcer/commons/analytics/collectors/impl/SuccessRequestDataCollector.java index 5089b66aff..db328cb7eb 100644 --- a/gateway/enforcer/org.wso2.apk.enforcer.commons/src/main/java/org/wso2/apk/enforcer/commons/analytics/collectors/impl/SuccessRequestDataCollector.java +++ b/gateway/enforcer/org.wso2.apk.enforcer.commons/src/main/java/org/wso2/apk/enforcer/commons/analytics/collectors/impl/SuccessRequestDataCollector.java @@ -32,6 +32,8 @@ import org.wso2.apk.enforcer.commons.analytics.publishers.dto.Target; import org.wso2.apk.enforcer.commons.analytics.publishers.impl.SuccessRequestDataPublisher; +import java.util.Objects; + /** * Success request data collector. */ @@ -81,6 +83,9 @@ public void collectData() throws AnalyticsException { } event.setApi(api); + if (Objects.equals(api.getApiType(), "GRPC")) { + operation.setApiMethod("GRPC"); + } event.setOperation(operation); event.setTarget(target); event.setApplication(application); @@ -91,7 +96,6 @@ public void collectData() throws AnalyticsException { event.setUserAgentHeader(userAgent); event.setUserName(userName); event.setUserIp(userIp); - this.processor.publish(event); } diff --git a/gateway/enforcer/org.wso2.apk.enforcer.commons/src/main/java/org/wso2/apk/enforcer/commons/model/ResourceConfig.java b/gateway/enforcer/org.wso2.apk.enforcer.commons/src/main/java/org/wso2/apk/enforcer/commons/model/ResourceConfig.java index 2d938a80ea..e70bc85597 100644 --- a/gateway/enforcer/org.wso2.apk.enforcer.commons/src/main/java/org/wso2/apk/enforcer/commons/model/ResourceConfig.java +++ b/gateway/enforcer/org.wso2.apk.enforcer.commons/src/main/java/org/wso2/apk/enforcer/commons/model/ResourceConfig.java @@ -42,7 +42,7 @@ public class ResourceConfig { public enum HttpMethods { GET("get"), POST("post"), PUT("put"), DELETE("delete"), HEAD("head"), PATCH("patch"), OPTIONS("options"), QUERY("query"), MUTATION("mutation"), - SUBSCRIPTION("subscription"); + SUBSCRIPTION("subscription"), GRPC("GRPC"); private String value; diff --git a/gateway/enforcer/org.wso2.apk.enforcer/src/main/java/org/wso2/apk/enforcer/analytics/AnalyticsFilter.java b/gateway/enforcer/org.wso2.apk.enforcer/src/main/java/org/wso2/apk/enforcer/analytics/AnalyticsFilter.java index 0eb82d008b..85dc35773b 100644 --- a/gateway/enforcer/org.wso2.apk.enforcer/src/main/java/org/wso2/apk/enforcer/analytics/AnalyticsFilter.java +++ b/gateway/enforcer/org.wso2.apk.enforcer/src/main/java/org/wso2/apk/enforcer/analytics/AnalyticsFilter.java @@ -229,7 +229,6 @@ public void handleFailureRequest(RequestContext requestContext) { analyticsSpanScope = analyticsSpan.getSpan().makeCurrent(); Utils.setTag(analyticsSpan, APIConstants.LOG_TRACE_ID, ThreadContext.get(APIConstants.LOG_TRACE_ID)); - } if (publisher == null) { logger.error("Cannot publish the failure event as analytics publisher is null.", From 73c9cbc8cef487147bf37d176417b45e51fe6323 Mon Sep 17 00:00:00 2001 From: sgayangi Date: Thu, 12 Sep 2024 11:39:15 +0530 Subject: [PATCH 14/17] Convert API version from v1beta1 to v1alpha3 --- .../oasparser/envoyconf/internal_dtos.go | 4 +- .../envoyconf/routes_with_clusters.go | 4 +- .../envoyconf/routes_with_clusters_test.go | 36 ++-- .../oasparser/model/adapter_internal_api.go | 17 +- .../internal/oasparser/model/http_route.go | 3 +- .../config/crd/bases/dp.wso2.com_apis.yaml | 2 +- .../operator/config/webhook/manifests.yaml | 40 ++++ .../operator/controllers/dp/api_controller.go | 83 ++++---- .../operator/synchronizer/api_state.go | 3 +- .../synchronizer/zz_generated.deepcopy.go | 8 +- adapter/internal/operator/utils/utils.go | 23 +-- .../config/crd/bases/dp.wso2.com_apis.yaml | 194 ++++++++++++++++- .../operator/config/webhook/manifests.yaml | 40 ++++ .../dp/ratelimitpolicy_controller.go | 7 +- .../internal/operator/operator.go | 4 +- common-go-libs/PROJECT | 5 +- .../apis/dp/v1alpha1/api_conversion.go | 28 +-- .../apis/dp/v1alpha2/api_conversion.go | 28 +-- .../{v1beta1 => v1alpha3}/api_conversion.go | 2 +- .../dp/{v1beta1 => v1alpha3}/api_types.go | 2 +- .../dp/{v1beta1 => v1alpha3}/api_webhook.go | 6 +- .../webhook_suite_test.go | 4 +- .../apis/dp/v1alpha3/zz_generated.deepcopy.go | 109 ++++++++++ .../apis/dp/v1beta1/groupversion_info.go | 37 ---- .../apis/dp/v1beta1/zz_generated.deepcopy.go | 195 ------------------ .../config/certmanager/certificate.yaml | 39 ---- .../config/certmanager/kustomization.yaml | 5 - .../config/certmanager/kustomizeconfig.yaml | 16 -- .../config/crd/bases/dp.wso2.com_apis.yaml | 2 +- .../crd/patches/cainjection_in_dp_apis.yaml | 7 - .../crd/patches/webhook_in_dp_apis.yaml | 16 -- .../config/default/manager_webhook_patch.yaml | 23 --- .../default/webhookcainjection_patch.yaml | 29 --- .../config/rbac/dp_api_editor_role.yaml | 31 --- .../config/rbac/dp_api_viewer_role.yaml | 27 --- ..._v1beta1_api.yaml => dp_v1alpha3_api.yaml} | 2 +- .../config/webhook/kustomization.yaml | 6 - .../config/webhook/kustomizeconfig.yaml | 25 --- common-go-libs/config/webhook/manifests.yaml | 28 +-- common-go-libs/config/webhook/service.yaml | 20 -- common-go-libs/revive.toml | 2 +- .../org/wso2/apk/enforcer/api/GRPCAPI.java | 2 +- .../templates/crds/dp.wso2.com_apis.yaml | 2 +- .../wso2-apk-config-deployer-api.yaml | 2 +- .../wso2-apk-config-generator-api.yaml | 2 +- .../api/notification-api.yaml | 2 +- .../adapter-mutating-webhook-config.yaml | 4 +- .../adapter-validation-webhook-config.yaml | 4 +- .../gateway-runtime/jwks-domain-api.yaml | 2 +- .../authenticationEndpoint-domain-api.yaml | 2 +- .../templates/idp/commonoauth-domain-api.yaml | 2 +- helm-charts/templates/idp/dcr-domain-api.yaml | 2 +- .../templates/idp/oauth-domain-api.yaml | 2 +- .../ballerina/Ballerina.toml | 6 - .../ballerina/Ballerina.toml.template | 6 - .../ballerina/K8sClient.bal | 8 +- .../ballerina/modules/model/API.bal | 4 +- .../apikey/apikey-different-header.apk-conf | 32 +++ .../apk-confs/apikey/apikey-enabled.apk-conf | 32 +++ 59 files changed, 613 insertions(+), 665 deletions(-) rename common-go-libs/apis/dp/{v1beta1 => v1alpha3}/api_conversion.go (97%) rename common-go-libs/apis/dp/{v1beta1 => v1alpha3}/api_types.go (99%) rename common-go-libs/apis/dp/{v1beta1 => v1alpha3}/api_webhook.go (95%) rename common-go-libs/apis/dp/{v1beta1 => v1alpha3}/webhook_suite_test.go (97%) delete mode 100644 common-go-libs/apis/dp/v1beta1/groupversion_info.go delete mode 100644 common-go-libs/apis/dp/v1beta1/zz_generated.deepcopy.go delete mode 100644 common-go-libs/config/certmanager/certificate.yaml delete mode 100644 common-go-libs/config/certmanager/kustomization.yaml delete mode 100644 common-go-libs/config/certmanager/kustomizeconfig.yaml delete mode 100644 common-go-libs/config/crd/patches/cainjection_in_dp_apis.yaml delete mode 100644 common-go-libs/config/crd/patches/webhook_in_dp_apis.yaml delete mode 100644 common-go-libs/config/default/manager_webhook_patch.yaml delete mode 100644 common-go-libs/config/default/webhookcainjection_patch.yaml delete mode 100644 common-go-libs/config/rbac/dp_api_editor_role.yaml delete mode 100644 common-go-libs/config/rbac/dp_api_viewer_role.yaml rename common-go-libs/config/samples/{dp_v1beta1_api.yaml => dp_v1alpha3_api.yaml} (89%) delete mode 100644 common-go-libs/config/webhook/kustomization.yaml delete mode 100644 common-go-libs/config/webhook/kustomizeconfig.yaml delete mode 100644 common-go-libs/config/webhook/service.yaml create mode 100644 test/cucumber-tests/src/test/resources/artifacts/apk-confs/apikey/apikey-different-header.apk-conf create mode 100644 test/cucumber-tests/src/test/resources/artifacts/apk-confs/apikey/apikey-enabled.apk-conf diff --git a/adapter/internal/oasparser/envoyconf/internal_dtos.go b/adapter/internal/oasparser/envoyconf/internal_dtos.go index 7e27725679..58d752962d 100644 --- a/adapter/internal/oasparser/envoyconf/internal_dtos.go +++ b/adapter/internal/oasparser/envoyconf/internal_dtos.go @@ -19,7 +19,7 @@ package envoyconf import ( "github.com/wso2/apk/adapter/internal/oasparser/model" - "github.com/wso2/apk/common-go-libs/apis/dp/v1beta1" + "github.com/wso2/apk/common-go-libs/apis/dp/v1alpha3" ) // routeCreateParams is the DTO used to provide information to the envoy route create function @@ -41,7 +41,7 @@ type routeCreateParams struct { isDefaultVersion bool createDefaultPath bool apiLevelRateLimitPolicy *model.RateLimitPolicy - apiProperties []v1beta1.Property + apiProperties []v1alpha3.Property environment string envType string mirrorClusterNames map[string][]string diff --git a/adapter/internal/oasparser/envoyconf/routes_with_clusters.go b/adapter/internal/oasparser/envoyconf/routes_with_clusters.go index e25638341d..b11ad5bed0 100644 --- a/adapter/internal/oasparser/envoyconf/routes_with_clusters.go +++ b/adapter/internal/oasparser/envoyconf/routes_with_clusters.go @@ -54,7 +54,7 @@ import ( logging "github.com/wso2/apk/adapter/internal/logging" "github.com/wso2/apk/adapter/internal/oasparser/constants" "github.com/wso2/apk/adapter/internal/oasparser/model" - "github.com/wso2/apk/common-go-libs/apis/dp/v1beta1" + "github.com/wso2/apk/common-go-libs/apis/dp/v1alpha3" "google.golang.org/protobuf/proto" gwapiv1 "sigs.k8s.io/gateway-api/apis/v1" ) @@ -1730,7 +1730,7 @@ func getUpgradeConfig(apiType string) []*routev3.RouteAction_UpgradeConfig { return upgradeConfig } -func getAPIProperties(apiPropertiesConfig []v1beta1.Property) string { +func getAPIProperties(apiPropertiesConfig []v1alpha3.Property) string { var apiProperties = make(map[string]string) for _, val := range apiPropertiesConfig { apiProperties[val.Name] = val.Value diff --git a/adapter/internal/oasparser/envoyconf/routes_with_clusters_test.go b/adapter/internal/oasparser/envoyconf/routes_with_clusters_test.go index c074c36b19..b2cccf41f4 100644 --- a/adapter/internal/oasparser/envoyconf/routes_with_clusters_test.go +++ b/adapter/internal/oasparser/envoyconf/routes_with_clusters_test.go @@ -40,16 +40,16 @@ import ( func TestCreateRoutesWithClustersWithExactAndRegularExpressionRules(t *testing.T) { apiState := synchronizer.APIState{} - apiDefinition := v1beta1.API{ + apiDefinition := v1alpha3.API{ ObjectMeta: metav1.ObjectMeta{ Namespace: "default", Name: "test-api-2", }, - Spec: v1beta1.APISpec{ + Spec: v1alpha3.APISpec{ APIName: "test-api-2", APIVersion: "2.0.0", BasePath: "/test-api/2.0.0", - Production: []v1beta1.EnvConfig{ + Production: []v1alpha3.EnvConfig{ { RouteRefs: []string{ "test-api-2-prod-http-route", @@ -218,16 +218,16 @@ func TestExtractAPIDetailsFromHTTPRouteForSpecificEnvironment(t *testing.T) { func generateSampleAPI(apiName string, apiVersion string, basePath string) synchronizer.APIState { apiState := synchronizer.APIState{} - apiDefinition := v1beta1.API{ + apiDefinition := v1alpha3.API{ ObjectMeta: metav1.ObjectMeta{ Namespace: "default", Name: apiName, }, - Spec: v1beta1.APISpec{ + Spec: v1alpha3.APISpec{ APIName: apiName, APIVersion: apiVersion, BasePath: basePath, - Production: []v1beta1.EnvConfig{ + Production: []v1alpha3.EnvConfig{ { RouteRefs: []string{ apiName + "-prod-http-route", @@ -284,16 +284,16 @@ func generateSampleAPI(apiName string, apiVersion string, basePath string) synch // TODO: Fix this test case func TestCreateRoutesWithClustersWithMultiplePathPrefixRules(t *testing.T) { apiState := synchronizer.APIState{} - apiDefinition := v1beta1.API{ + apiDefinition := v1alpha3.API{ ObjectMeta: metav1.ObjectMeta{ Namespace: "default", Name: "test-api-1", }, - Spec: v1beta1.APISpec{ + Spec: v1alpha3.APISpec{ APIName: "test-api", APIVersion: "1.0.0", BasePath: "/test-api/1.0.0", - Production: []v1beta1.EnvConfig{ + Production: []v1alpha3.EnvConfig{ { RouteRefs: []string{ "test-api-1-prod-http-route", @@ -435,16 +435,16 @@ func TestCreateRoutesWithClustersWithMultiplePathPrefixRules(t *testing.T) { func TestCreateRoutesWithClustersWithBackendTLSConfigs(t *testing.T) { apiState := synchronizer.APIState{} - apiDefinition := v1beta1.API{ + apiDefinition := v1alpha3.API{ ObjectMeta: metav1.ObjectMeta{ Namespace: "default", Name: "test-api-3", }, - Spec: v1beta1.APISpec{ + Spec: v1alpha3.APISpec{ APIName: "test-api-3", APIVersion: "1.0.0", BasePath: "/test-api-3/1.0.0", - Production: []v1beta1.EnvConfig{ + Production: []v1alpha3.EnvConfig{ { RouteRefs: []string{ "test-api-3-prod-http-route", @@ -560,16 +560,16 @@ func TestCreateHealthEndpoint(t *testing.T) { func TestCreateRoutesWithClustersDifferentBackendRefs(t *testing.T) { apiState := synchronizer.APIState{} - apiDefinition := v1beta1.API{ + apiDefinition := v1alpha3.API{ ObjectMeta: metav1.ObjectMeta{ Namespace: "default", Name: "test-api-different-backendrefs", }, - Spec: v1beta1.APISpec{ + Spec: v1alpha3.APISpec{ APIName: "test-api-different-backendrefs", APIVersion: "1.0.0", BasePath: "/test-api-different-backendrefs/1.0.0", - Production: []v1beta1.EnvConfig{ + Production: []v1alpha3.EnvConfig{ { RouteRefs: []string{ "test-api-different-backendrefs-prod-http-route", @@ -654,16 +654,16 @@ func TestCreateRoutesWithClustersDifferentBackendRefs(t *testing.T) { func TestCreateRoutesWithClustersSameBackendRefs(t *testing.T) { apiState := synchronizer.APIState{} - apiDefinition := v1beta1.API{ + apiDefinition := v1alpha3.API{ ObjectMeta: metav1.ObjectMeta{ Namespace: "default", Name: "test-api-same-backendrefs", }, - Spec: v1beta1.APISpec{ + Spec: v1alpha3.APISpec{ APIName: "test-api-same-backendrefs", APIVersion: "1.0.0", BasePath: "/test-api-same-backendrefs/1.0.0", - Production: []v1beta1.EnvConfig{ + Production: []v1alpha3.EnvConfig{ { RouteRefs: []string{ "test-api-same-backendrefs-prod-http-route", diff --git a/adapter/internal/oasparser/model/adapter_internal_api.go b/adapter/internal/oasparser/model/adapter_internal_api.go index c482d08e99..e861a0c95a 100644 --- a/adapter/internal/oasparser/model/adapter_internal_api.go +++ b/adapter/internal/oasparser/model/adapter_internal_api.go @@ -34,7 +34,6 @@ import ( dpv1alpha1 "github.com/wso2/apk/common-go-libs/apis/dp/v1alpha1" dpv1alpha2 "github.com/wso2/apk/common-go-libs/apis/dp/v1alpha2" dpv1alpha3 "github.com/wso2/apk/common-go-libs/apis/dp/v1alpha3" - dpv1beta1 "github.com/wso2/apk/common-go-libs/apis/dp/v1beta1" "golang.org/x/exp/maps" "k8s.io/apimachinery/pkg/types" gwapiv1 "sigs.k8s.io/gateway-api/apis/v1" @@ -74,7 +73,7 @@ type AdapterInternalAPI struct { apiDefinitionFile []byte apiDefinitionEndpoint string subscriptionValidation bool - APIProperties []dpv1beta1.Property + APIProperties []dpv1alpha3.Property // GraphQLSchema string // GraphQLComplexities GraphQLComplexityYaml IsSystemAPI bool @@ -1323,10 +1322,18 @@ func (adapterInternalAPI *AdapterInternalAPI) SetInfoGRPCRouteCR(grpcRoute *gwap adapterInternalAPI.disableAuthentications = *authScheme.Spec.Override.Disabled } authSpec := utils.SelectPolicy(&authScheme.Spec.Override, &authScheme.Spec.Default, nil, nil) - if authSpec != nil && authSpec.AuthTypes != nil && authSpec.AuthTypes.Oauth2.Required != "" { - adapterInternalAPI.SetXWSO2ApplicationSecurity(authSpec.AuthTypes.Oauth2.Required == "mandatory") + if authSpec != nil && authSpec.AuthTypes != nil { + if authSpec.AuthTypes.OAuth2.Required != "" { + adapterInternalAPI.SetApplicationSecurity(constants.OAuth2, authSpec.AuthTypes.OAuth2.Required == "mandatory") + } else { + adapterInternalAPI.SetApplicationSecurity(constants.OAuth2, true) + } + + if authSpec.AuthTypes.APIKey != nil { + adapterInternalAPI.SetApplicationSecurity(constants.APIKey, authSpec.AuthTypes.APIKey.Required == "mandatory") + } } else { - adapterInternalAPI.SetXWSO2ApplicationSecurity(true) + adapterInternalAPI.SetApplicationSecurity(constants.OAuth2, true) } adapterInternalAPI.disableScopes = disableScopes // Check whether the API has a backend JWT token diff --git a/adapter/internal/oasparser/model/http_route.go b/adapter/internal/oasparser/model/http_route.go index 081ed2a00f..6092dd66b3 100644 --- a/adapter/internal/oasparser/model/http_route.go +++ b/adapter/internal/oasparser/model/http_route.go @@ -25,7 +25,6 @@ import ( dpv1alpha1 "github.com/wso2/apk/common-go-libs/apis/dp/v1alpha1" dpv1alpha2 "github.com/wso2/apk/common-go-libs/apis/dp/v1alpha2" dpv1alpha3 "github.com/wso2/apk/common-go-libs/apis/dp/v1alpha3" - "github.com/wso2/apk/common-go-libs/apis/dp/v1beta1" "k8s.io/apimachinery/pkg/types" gwapiv1 "sigs.k8s.io/gateway-api/apis/v1" ) @@ -311,7 +310,7 @@ func getAllowedOperations(httpMethod *gwapiv1.HTTPMethod, policies OperationPoli } // SetInfoAPICR populates ID, ApiType, Version and XWso2BasePath of adapterInternalAPI. -func (swagger *AdapterInternalAPI) SetInfoAPICR(api v1beta1.API) { +func (swagger *AdapterInternalAPI) SetInfoAPICR(api dpv1alpha3.API) { swagger.UUID = string(api.ObjectMeta.UID) swagger.title = api.Spec.APIName swagger.apiType = api.Spec.APIType diff --git a/adapter/internal/operator/config/crd/bases/dp.wso2.com_apis.yaml b/adapter/internal/operator/config/crd/bases/dp.wso2.com_apis.yaml index 1fa8ea6721..d3bd7331be 100644 --- a/adapter/internal/operator/config/crd/bases/dp.wso2.com_apis.yaml +++ b/adapter/internal/operator/config/crd/bases/dp.wso2.com_apis.yaml @@ -379,7 +379,7 @@ spec: - jsonPath: .metadata.creationTimestamp name: Age type: date - name: v1beta1 + name: v1alpha3 schema: openAPIV3Schema: description: API is the Schema for the apis API diff --git a/adapter/internal/operator/config/webhook/manifests.yaml b/adapter/internal/operator/config/webhook/manifests.yaml index 54cbdd40d4..b13c9e7c4d 100644 --- a/adapter/internal/operator/config/webhook/manifests.yaml +++ b/adapter/internal/operator/config/webhook/manifests.yaml @@ -24,6 +24,26 @@ webhooks: resources: - apis sideEffects: None +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: webhook-service + namespace: system + path: /mutate-dp-wso2-com-v1alpha3-api + failurePolicy: Fail + name: mapi.kb.io + rules: + - apiGroups: + - dp.wso2.com + apiVersions: + - v1alpha3 + operations: + - CREATE + - UPDATE + resources: + - apis + sideEffects: None - admissionReviewVersions: - v1 clientConfig: @@ -230,6 +250,26 @@ webhooks: resources: - apis sideEffects: None +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: webhook-service + namespace: system + path: /validate-dp-wso2-com-v1alpha3-api + failurePolicy: Fail + name: vapi.kb.io + rules: + - apiGroups: + - dp.wso2.com + apiVersions: + - v1alpha3 + operations: + - CREATE + - UPDATE + resources: + - apis + sideEffects: None - admissionReviewVersions: - v1 clientConfig: diff --git a/adapter/internal/operator/controllers/dp/api_controller.go b/adapter/internal/operator/controllers/dp/api_controller.go index ac251aab39..ff64f7e359 100644 --- a/adapter/internal/operator/controllers/dp/api_controller.go +++ b/adapter/internal/operator/controllers/dp/api_controller.go @@ -62,7 +62,6 @@ import ( dpv1alpha1 "github.com/wso2/apk/common-go-libs/apis/dp/v1alpha1" dpv1alpha2 "github.com/wso2/apk/common-go-libs/apis/dp/v1alpha2" dpv1alpha3 "github.com/wso2/apk/common-go-libs/apis/dp/v1alpha3" - dpv1beta1 "github.com/wso2/apk/common-go-libs/apis/dp/v1beta1" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) @@ -123,7 +122,7 @@ type APIReconciler struct { apiPropagationEnabled bool } -// NewAPIController creates a new API controller instance. API Controllers watches for dpv1beta1.API and gwapiv1.HTTPRoute. +// NewAPIController creates a new API controller instance. API Controllers watches for dpv1alpha3.API and gwapiv1.HTTPRoute. func NewAPIController(mgr manager.Manager, operatorDataStore *synchronizer.OperatorDataStore, statusUpdater *status.UpdateHandler, ch *chan *synchronizer.APIEvent, successChannel *chan synchronizer.SuccessEvent) error { apiReconciler := &APIReconciler{ @@ -145,7 +144,7 @@ func NewAPIController(mgr manager.Manager, operatorDataStore *synchronizer.Opera apiReconciler.apiPropagationEnabled = conf.Adapter.ControlPlane.EnableAPIPropagation predicates := []predicate.Predicate{predicate.NewPredicateFuncs(utils.FilterByNamespaces(conf.Adapter.Operator.Namespaces))} - if err := c.Watch(source.Kind(mgr.GetCache(), &dpv1beta1.API{}), &handler.EnqueueRequestForObject{}, + if err := c.Watch(source.Kind(mgr.GetCache(), &dpv1alpha3.API{}), &handler.EnqueueRequestForObject{}, predicates...); err != nil { loggers.LoggerAPKOperator.ErrorC(logging.PrintError(logging.Error2611, logging.BLOCKER, "Error watching API resources: %v", err)) return err @@ -287,7 +286,7 @@ func (apiReconciler *APIReconciler) Reconcile(ctx context.Context, req ctrl.Requ applyAllAPIsOnce.Do(apiReconciler.applyStartupAPIs) loggers.LoggerAPKOperator.Infof("Reconciling for API %s", req.NamespacedName.String()) // Check whether the API CR exist, if not consider as a DELETE event. - var apiCR dpv1beta1.API + var apiCR dpv1alpha3.API if err := apiReconciler.client.Get(ctx, req.NamespacedName, &apiCR); err != nil { apiState, found := apiReconciler.ods.GetCachedAPI(req.NamespacedName) if found && k8error.IsNotFound(err) { @@ -354,7 +353,7 @@ func (apiReconciler *APIReconciler) applyStartupAPIs() { // resolveAPIRefs validates following references related to the API // - HTTPRoutes -func (apiReconciler *APIReconciler) resolveAPIRefs(ctx context.Context, api dpv1beta1.API) (*synchronizer.APIEvent, error) { +func (apiReconciler *APIReconciler) resolveAPIRefs(ctx context.Context, api dpv1alpha3.API) (*synchronizer.APIEvent, error) { var prodRouteRefs, sandRouteRefs []string if len(api.Spec.Production) > 0 { prodRouteRefs = api.Spec.Production[0].RouteRefs @@ -574,7 +573,7 @@ func isAPIPropagatable(apiState *synchronizer.APIState) bool { } func (apiReconciler *APIReconciler) resolveGQLRouteRefs(ctx context.Context, gqlRouteRefs []string, - namespace string, api dpv1beta1.API) (*synchronizer.GQLRouteState, error) { + namespace string, api dpv1alpha3.API) (*synchronizer.GQLRouteState, error) { gqlRouteState, err := apiReconciler.concatGQLRoutes(ctx, gqlRouteRefs, namespace, api) if err != nil { return nil, err @@ -587,7 +586,7 @@ func (apiReconciler *APIReconciler) resolveGQLRouteRefs(ctx context.Context, gql // - Authentications func (apiReconciler *APIReconciler) resolveHTTPRouteRefs(ctx context.Context, httpRouteState *synchronizer.HTTPRouteState, httpRouteRefs []string, namespace string, interceptorServiceMapping map[string]dpv1alpha1.InterceptorService, - api dpv1beta1.API) (*synchronizer.HTTPRouteState, error) { + api dpv1alpha3.API) (*synchronizer.HTTPRouteState, error) { var err error httpRouteState.HTTPRouteCombined, httpRouteState.HTTPRoutePartitions, err = apiReconciler.concatHTTPRoutes(ctx, httpRouteRefs, namespace, api) if err != nil { @@ -603,7 +602,7 @@ func (apiReconciler *APIReconciler) resolveHTTPRouteRefs(ctx context.Context, ht } func (apiReconciler *APIReconciler) resolveGRPCRouteRefs(ctx context.Context, grpcRouteRefs []string, - namespace string, api dpv1beta1.API) (*synchronizer.GRPCRouteState, error) { + namespace string, api dpv1alpha3.API) (*synchronizer.GRPCRouteState, error) { grpcRouteState, err := apiReconciler.concatGRPCRoutes(ctx, grpcRouteRefs, namespace, api) if err != nil { return nil, err @@ -613,7 +612,7 @@ func (apiReconciler *APIReconciler) resolveGRPCRouteRefs(ctx context.Context, gr } func (apiReconciler *APIReconciler) concatGRPCRoutes(ctx context.Context, grpcRouteRefs []string, - namespace string, api dpv1beta1.API) (synchronizer.GRPCRouteState, error) { + namespace string, api dpv1alpha3.API) (synchronizer.GRPCRouteState, error) { grpcRouteState := synchronizer.GRPCRouteState{} grpcRoutePartitions := make(map[string]*gwapiv1a2.GRPCRoute) for _, grpcRouteRef := range grpcRouteRefs { @@ -648,7 +647,7 @@ func (apiReconciler *APIReconciler) concatGRPCRoutes(ctx context.Context, grpcRo } func (apiReconciler *APIReconciler) concatGQLRoutes(ctx context.Context, gqlRouteRefs []string, - namespace string, api dpv1beta1.API) (synchronizer.GQLRouteState, error) { + namespace string, api dpv1alpha3.API) (synchronizer.GQLRouteState, error) { gqlRouteState := synchronizer.GQLRouteState{} gqlRoutePartitions := make(map[string]*dpv1alpha2.GQLRoute) for _, gqlRouteRef := range gqlRouteRefs { @@ -682,7 +681,7 @@ func (apiReconciler *APIReconciler) concatGQLRoutes(ctx context.Context, gqlRout } func (apiReconciler *APIReconciler) getScopesForGRPCRoute(ctx context.Context, - grpcRoute *gwapiv1a2.GRPCRoute, api dpv1beta1.API) (map[string]dpv1alpha1.Scope, error) { + grpcRoute *gwapiv1a2.GRPCRoute, api dpv1alpha3.API) (map[string]dpv1alpha1.Scope, error) { scopes := make(map[string]dpv1alpha1.Scope) for _, rule := range grpcRoute.Spec.Rules { for _, filter := range rule.Filters { @@ -702,7 +701,7 @@ func (apiReconciler *APIReconciler) getScopesForGRPCRoute(ctx context.Context, } func (apiReconciler *APIReconciler) concatHTTPRoutes(ctx context.Context, httpRouteRefs []string, - namespace string, api dpv1beta1.API) (*gwapiv1.HTTPRoute, map[string]*gwapiv1.HTTPRoute, error) { + namespace string, api dpv1alpha3.API) (*gwapiv1.HTTPRoute, map[string]*gwapiv1.HTTPRoute, error) { var combinedHTTPRoute *gwapiv1.HTTPRoute httpRoutePartitions := make(map[string]*gwapiv1.HTTPRoute) for _, httpRouteRef := range httpRouteRefs { @@ -723,7 +722,7 @@ func (apiReconciler *APIReconciler) concatHTTPRoutes(ctx context.Context, httpRo } func (apiReconciler *APIReconciler) getAuthenticationsForAPI(ctx context.Context, - api dpv1beta1.API) (map[string]dpv1alpha2.Authentication, error) { + api dpv1alpha3.API) (map[string]dpv1alpha2.Authentication, error) { nameSpacedName := utils.NamespacedName(&api).String() authentications := make(map[string]dpv1alpha2.Authentication) authenticationList := &dpv1alpha2.AuthenticationList{} @@ -740,7 +739,7 @@ func (apiReconciler *APIReconciler) getAuthenticationsForAPI(ctx context.Context } func (apiReconciler *APIReconciler) getRatelimitPoliciesForAPI(ctx context.Context, - api dpv1beta1.API) (map[string]dpv1alpha3.RateLimitPolicy, error) { + api dpv1alpha3.API) (map[string]dpv1alpha3.RateLimitPolicy, error) { nameSpacedName := utils.NamespacedName(&api).String() ratelimitPolicies := make(map[string]dpv1alpha3.RateLimitPolicy) ratelimitPolicyList := &dpv1alpha3.RateLimitPolicyList{} @@ -757,7 +756,7 @@ func (apiReconciler *APIReconciler) getRatelimitPoliciesForAPI(ctx context.Conte } func (apiReconciler *APIReconciler) getScopesForGQLRoute(ctx context.Context, - gqlRoute *dpv1alpha2.GQLRoute, api dpv1beta1.API) (map[string]dpv1alpha1.Scope, error) { + gqlRoute *dpv1alpha2.GQLRoute, api dpv1alpha3.API) (map[string]dpv1alpha1.Scope, error) { scopes := make(map[string]dpv1alpha1.Scope) for _, rule := range gqlRoute.Spec.Rules { for _, filter := range rule.Filters { @@ -777,7 +776,7 @@ func (apiReconciler *APIReconciler) getScopesForGQLRoute(ctx context.Context, } func (apiReconciler *APIReconciler) getScopesForHTTPRoute(ctx context.Context, - httpRoute *gwapiv1.HTTPRoute, api dpv1beta1.API) (map[string]dpv1alpha1.Scope, error) { + httpRoute *gwapiv1.HTTPRoute, api dpv1alpha3.API) (map[string]dpv1alpha1.Scope, error) { scopes := make(map[string]dpv1alpha1.Scope) for _, rule := range httpRoute.Spec.Rules { for _, filter := range rule.Filters { @@ -799,7 +798,7 @@ func (apiReconciler *APIReconciler) getScopesForHTTPRoute(ctx context.Context, } func (apiReconciler *APIReconciler) getAuthenticationsForResources(ctx context.Context, - api dpv1beta1.API) (map[string]dpv1alpha2.Authentication, error) { + api dpv1alpha3.API) (map[string]dpv1alpha2.Authentication, error) { nameSpacedName := utils.NamespacedName(&api).String() authentications := make(map[string]dpv1alpha2.Authentication) authenticationList := &dpv1alpha2.AuthenticationList{} @@ -816,7 +815,7 @@ func (apiReconciler *APIReconciler) getAuthenticationsForResources(ctx context.C } func (apiReconciler *APIReconciler) getRatelimitPoliciesForResources(ctx context.Context, - api dpv1beta1.API) (map[string]dpv1alpha3.RateLimitPolicy, error) { + api dpv1alpha3.API) (map[string]dpv1alpha3.RateLimitPolicy, error) { nameSpacedName := utils.NamespacedName(&api).String() ratelimitpolicies := make(map[string]dpv1alpha3.RateLimitPolicy) ratelimitPolicyList := &dpv1alpha3.RateLimitPolicyList{} @@ -832,7 +831,7 @@ func (apiReconciler *APIReconciler) getRatelimitPoliciesForResources(ctx context return ratelimitpolicies, nil } -func (apiReconciler *APIReconciler) getAPIPoliciesForAPI(ctx context.Context, api dpv1beta1.API) (map[string]dpv1alpha3.APIPolicy, error) { +func (apiReconciler *APIReconciler) getAPIPoliciesForAPI(ctx context.Context, api dpv1alpha3.API) (map[string]dpv1alpha3.APIPolicy, error) { nameSpacedName := utils.NamespacedName(&api).String() apiPolicies := make(map[string]dpv1alpha3.APIPolicy) apiPolicyList := &dpv1alpha3.APIPolicyList{} @@ -850,7 +849,7 @@ func (apiReconciler *APIReconciler) getAPIPoliciesForAPI(ctx context.Context, ap } func (apiReconciler *APIReconciler) getAPIDefinitionForAPI(ctx context.Context, - apiDefinitionFile, namespace string, api dpv1beta1.API) ([]byte, error) { + apiDefinitionFile, namespace string, api dpv1alpha3.API) ([]byte, error) { configMap := &corev1.ConfigMap{} if err := utils.ResolveRef(ctx, apiReconciler.client, &api, types.NamespacedName{Namespace: namespace, Name: apiDefinitionFile}, true, configMap); err != nil { @@ -867,7 +866,7 @@ func (apiReconciler *APIReconciler) getAPIDefinitionForAPI(ctx context.Context, } func (apiReconciler *APIReconciler) getAPIPoliciesForResources(ctx context.Context, - api dpv1beta1.API) (map[string]dpv1alpha3.APIPolicy, error) { + api dpv1alpha3.API) (map[string]dpv1alpha3.APIPolicy, error) { nameSpacedName := utils.NamespacedName(&api).String() apiPolicies := make(map[string]dpv1alpha3.APIPolicy) apiPolicyList := &dpv1alpha3.APIPolicyList{} @@ -889,7 +888,7 @@ func (apiReconciler *APIReconciler) getAPIPoliciesForResources(ctx context.Conte // - subscription validation func (apiReconciler *APIReconciler) getAPIPolicyChildrenRefs(ctx context.Context, apiPolicies, resourceAPIPolicies map[string]dpv1alpha3.APIPolicy, - api dpv1beta1.API) (map[string]dpv1alpha1.InterceptorService, map[string]dpv1alpha1.BackendJWT, bool, *dpv1alpha3.AIProvider, error) { + api dpv1alpha3.API) (map[string]dpv1alpha1.InterceptorService, map[string]dpv1alpha1.BackendJWT, bool, *dpv1alpha3.AIProvider, error) { allAPIPolicies := append(maps.Values(apiPolicies), maps.Values(resourceAPIPolicies)...) interceptorServices := make(map[string]dpv1alpha1.InterceptorService) backendJWTs := make(map[string]dpv1alpha1.BackendJWT) @@ -978,7 +977,7 @@ func (apiReconciler *APIReconciler) resolveAuthentications(ctx context.Context, func (apiReconciler *APIReconciler) getResolvedBackendsMapping(ctx context.Context, httpRouteState *synchronizer.HTTPRouteState, interceptorServiceMapping map[string]dpv1alpha1.InterceptorService, - api dpv1beta1.API) (map[string]*dpv1alpha2.ResolvedBackend, error) { + api dpv1alpha3.API) (map[string]*dpv1alpha2.ResolvedBackend, error) { backendMapping := make(map[string]*dpv1alpha2.ResolvedBackend) // Resolve backends in HTTPRoute @@ -1444,7 +1443,7 @@ func (apiReconciler *APIReconciler) getAPIForGQLRoute(ctx context.Context, obj k loggers.LoggerAPKOperator.ErrorC(logging.PrintError(logging.Error2665, logging.TRIVIAL, "Unexpected object type, bypassing reconciliation: %v", gqlRoute)) return []reconcile.Request{} } - apiList := &dpv1beta1.APIList{} + apiList := &dpv1alpha3.APIList{} if err := apiReconciler.client.List(ctx, apiList, &k8client.ListOptions{ FieldSelector: fields.OneTermEqualSelector(gqlRouteAPIIndex, utils.NamespacedName(gqlRoute).String()), }); err != nil { @@ -1479,7 +1478,7 @@ func (apiReconciler *APIReconciler) getAPIForHTTPRoute(ctx context.Context, obj return []reconcile.Request{} } - apiList := &dpv1beta1.APIList{} + apiList := &dpv1alpha3.APIList{} if err := apiReconciler.client.List(ctx, apiList, &k8client.ListOptions{ FieldSelector: fields.OneTermEqualSelector(httpRouteAPIIndex, utils.NamespacedName(httpRoute).String()), }); err != nil { @@ -1517,7 +1516,7 @@ func (apiReconciler *APIReconciler) getAPIForGRPCRoute(ctx context.Context, obj return []reconcile.Request{} } - apiList := &dpv1beta1.APIList{} + apiList := &dpv1alpha3.APIList{} logrus.Info("=============GETTING API FOR GRPC ROUTE================") if err := apiReconciler.client.List(ctx, apiList, &k8client.ListOptions{ @@ -1570,7 +1569,7 @@ func (apiReconciler *APIReconciler) getAPIsForConfigMap(ctx context.Context, obj return requests } - apiList := &dpv1beta1.APIList{} + apiList := &dpv1alpha3.APIList{} err = apiReconciler.client.List(ctx, apiList, &k8client.ListOptions{ FieldSelector: fields.OneTermEqualSelector(configMapAPIDefinition, utils.NamespacedName(configMap).String()), }) @@ -2026,9 +2025,9 @@ func (apiReconciler *APIReconciler) getAPIsForGateway(ctx context.Context, obj k // apiPolicy schemes related to httproutes // This helps to find apiPolicy schemes binded to HTTPRoute. func addIndexes(ctx context.Context, mgr manager.Manager) error { - if err := mgr.GetFieldIndexer().IndexField(ctx, &dpv1beta1.API{}, httpRouteAPIIndex, + if err := mgr.GetFieldIndexer().IndexField(ctx, &dpv1alpha3.API{}, httpRouteAPIIndex, func(rawObj k8client.Object) []string { - api := rawObj.(*dpv1beta1.API) + api := rawObj.(*dpv1alpha3.API) var httpRoutes []string if len(api.Spec.Production) > 0 { for _, ref := range api.Spec.Production[0].RouteRefs { @@ -2057,9 +2056,9 @@ func addIndexes(ctx context.Context, mgr manager.Manager) error { return err } - if err := mgr.GetFieldIndexer().IndexField(ctx, &dpv1beta1.API{}, gqlRouteAPIIndex, + if err := mgr.GetFieldIndexer().IndexField(ctx, &dpv1alpha3.API{}, gqlRouteAPIIndex, func(rawObj k8client.Object) []string { - api := rawObj.(*dpv1beta1.API) + api := rawObj.(*dpv1alpha3.API) var gqlRoutes []string if len(api.Spec.Production) > 0 { for _, ref := range api.Spec.Production[0].RouteRefs { @@ -2088,9 +2087,9 @@ func addIndexes(ctx context.Context, mgr manager.Manager) error { return err } - if err := mgr.GetFieldIndexer().IndexField(ctx, &dpv1beta1.API{}, grpcRouteAPIIndex, + if err := mgr.GetFieldIndexer().IndexField(ctx, &dpv1alpha3.API{}, grpcRouteAPIIndex, func(rawObj k8client.Object) []string { - api := rawObj.(*dpv1beta1.API) + api := rawObj.(*dpv1alpha3.API) if api.Spec.APIType != constants.GRPC { return nil } @@ -2122,9 +2121,9 @@ func addIndexes(ctx context.Context, mgr manager.Manager) error { return err } - if err := mgr.GetFieldIndexer().IndexField(ctx, &dpv1beta1.API{}, configMapAPIDefinition, + if err := mgr.GetFieldIndexer().IndexField(ctx, &dpv1alpha3.API{}, configMapAPIDefinition, func(rawObj k8client.Object) []string { - api := rawObj.(*dpv1beta1.API) + api := rawObj.(*dpv1alpha3.API) var configMaps []string if api.Spec.DefinitionFileRef != "" { configMaps = append(configMaps, @@ -2705,9 +2704,9 @@ func (apiReconciler *APIReconciler) handleStatus() { for _, apiName := range successEvent.APINamespacedName { // handle startup multiple apis apiReconciler.statusUpdater.Send(status.Update{ NamespacedName: apiName, - Resource: new(dpv1beta1.API), + Resource: new(dpv1alpha3.API), UpdateStatus: func(obj k8client.Object) k8client.Object { - h, ok := obj.(*dpv1beta1.API) + h, ok := obj.(*dpv1alpha3.API) if !ok { loggers.LoggerAPKOperator.ErrorC(logging.PrintError(logging.Error2626, logging.BLOCKER, "Unsupported object type %T", obj)) } @@ -2744,7 +2743,7 @@ func (apiReconciler *APIReconciler) handleLabels(ctx context.Context) { }) } payloadBytes, _ := json.Marshal(patchOps) - apiCR := dpv1beta1.API{ + apiCR := dpv1alpha3.API{ ObjectMeta: metav1.ObjectMeta{ Namespace: labelUpdate.Namespace, Name: labelUpdate.Name, @@ -2755,7 +2754,7 @@ func (apiReconciler *APIReconciler) handleLabels(ctx context.Context) { if err != nil { loggers.LoggerAPKOperator.Errorf("Failed to patch api %s/%s with patch: %+v, error: %+v", labelUpdate.Name, labelUpdate.Namespace, patchOps, err) // Patch did not work it could be due to labels field does not exists. Lets try to update the CR with labels field. - var apiCR dpv1beta1.API + var apiCR dpv1alpha3.API if err := apiReconciler.client.Get(ctx, types.NamespacedName{Namespace: labelUpdate.Namespace, Name: labelUpdate.Name}, &apiCR); err == nil { if apiCR.ObjectMeta.Labels == nil { apiCR.ObjectMeta.Labels = map[string]string{} @@ -2775,9 +2774,9 @@ func (apiReconciler *APIReconciler) handleLabels(ctx context.Context) { } func (apiReconciler *APIReconciler) handleOwnerReference(ctx context.Context, obj k8client.Object, apiRequests *[]reconcile.Request) { - apis := []dpv1beta1.API{} + apis := []dpv1alpha3.API{} for _, req := range *apiRequests { - var apiCR dpv1beta1.API + var apiCR dpv1alpha3.API if err := apiReconciler.client.Get(ctx, req.NamespacedName, &apiCR); err == nil { apis = append(apis, apiCR) } else { @@ -2807,7 +2806,7 @@ func (apiReconciler *APIReconciler) handleOwnerReference(ctx context.Context, ob } } -func prepareOwnerReference(apiItems []dpv1beta1.API) []metav1.OwnerReference { +func prepareOwnerReference(apiItems []dpv1alpha3.API) []metav1.OwnerReference { ownerReferences := []metav1.OwnerReference{} uidMap := make(map[string]bool) for _, ref := range apiItems { diff --git a/adapter/internal/operator/synchronizer/api_state.go b/adapter/internal/operator/synchronizer/api_state.go index ac953a68c4..580f679d47 100644 --- a/adapter/internal/operator/synchronizer/api_state.go +++ b/adapter/internal/operator/synchronizer/api_state.go @@ -21,7 +21,6 @@ import ( "github.com/wso2/apk/common-go-libs/apis/dp/v1alpha1" "github.com/wso2/apk/common-go-libs/apis/dp/v1alpha2" "github.com/wso2/apk/common-go-libs/apis/dp/v1alpha3" - "github.com/wso2/apk/common-go-libs/apis/dp/v1beta1" gwapiv1 "sigs.k8s.io/gateway-api/apis/v1" gwapiv1a2 "sigs.k8s.io/gateway-api/apis/v1alpha2" ) @@ -30,7 +29,7 @@ import ( // the state of the Kubernetes controller cache to detect updates. // +k8s:deepcopy-gen=true type APIState struct { - APIDefinition *v1beta1.API + APIDefinition *v1alpha3.API ProdHTTPRoute *HTTPRouteState SandHTTPRoute *HTTPRouteState ProdGQLRoute *GQLRouteState diff --git a/adapter/internal/operator/synchronizer/zz_generated.deepcopy.go b/adapter/internal/operator/synchronizer/zz_generated.deepcopy.go index 22057cc268..84e61c5617 100644 --- a/adapter/internal/operator/synchronizer/zz_generated.deepcopy.go +++ b/adapter/internal/operator/synchronizer/zz_generated.deepcopy.go @@ -26,7 +26,6 @@ import ( "github.com/wso2/apk/common-go-libs/apis/dp/v1alpha1" "github.com/wso2/apk/common-go-libs/apis/dp/v1alpha2" "github.com/wso2/apk/common-go-libs/apis/dp/v1alpha3" - "github.com/wso2/apk/common-go-libs/apis/dp/v1beta1" "sigs.k8s.io/gateway-api/apis/v1" apisv1alpha2 "sigs.k8s.io/gateway-api/apis/v1alpha2" ) @@ -36,7 +35,7 @@ func (in *APIState) DeepCopyInto(out *APIState) { *out = *in if in.APIDefinition != nil { in, out := &in.APIDefinition, &out.APIDefinition - *out = new(v1beta1.API) + *out = new(v1alpha3.API) (*in).DeepCopyInto(*out) } if in.ProdHTTPRoute != nil { @@ -111,6 +110,11 @@ func (in *APIState) DeepCopyInto(out *APIState) { (*out)[key] = *val.DeepCopy() } } + if in.AIProvider != nil { + in, out := &in.AIProvider, &out.AIProvider + *out = new(v1alpha3.AIProvider) + (*in).DeepCopyInto(*out) + } if in.InterceptorServiceMapping != nil { in, out := &in.InterceptorServiceMapping, &out.InterceptorServiceMapping *out = make(map[string]v1alpha1.InterceptorService, len(*in)) diff --git a/adapter/internal/operator/utils/utils.go b/adapter/internal/operator/utils/utils.go index 6624481d10..989009b6e7 100644 --- a/adapter/internal/operator/utils/utils.go +++ b/adapter/internal/operator/utils/utils.go @@ -36,7 +36,6 @@ import ( dpv1alpha1 "github.com/wso2/apk/common-go-libs/apis/dp/v1alpha1" dpv1alpha2 "github.com/wso2/apk/common-go-libs/apis/dp/v1alpha2" dpv1alpha3 "github.com/wso2/apk/common-go-libs/apis/dp/v1alpha3" - dpv1beta1 "github.com/wso2/apk/common-go-libs/apis/dp/v1beta1" corev1 "k8s.io/api/core/v1" apierrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -350,7 +349,7 @@ func GetResolvedBackendFromService(k8sService *corev1.Service, svcPort int) (*dp // ResolveAndAddBackendToMapping resolves backend from reference and adds it to the backendMapping. func ResolveAndAddBackendToMapping(ctx context.Context, client k8client.Client, backendMapping map[string]*dpv1alpha2.ResolvedBackend, - backendRef dpv1alpha1.BackendReference, interceptorServiceNamespace string, api *dpv1beta1.API) { + backendRef dpv1alpha1.BackendReference, interceptorServiceNamespace string, api *dpv1alpha3.API) { backendName := types.NamespacedName{ Name: backendRef.Name, Namespace: interceptorServiceNamespace, @@ -362,7 +361,7 @@ func ResolveAndAddBackendToMapping(ctx context.Context, client k8client.Client, } // ResolveRef this function will return k8client object and update owner -func ResolveRef(ctx context.Context, client k8client.Client, api *dpv1beta1.API, +func ResolveRef(ctx context.Context, client k8client.Client, api *dpv1alpha3.API, namespacedName types.NamespacedName, isReplace bool, obj k8client.Object, opts ...k8client.GetOption) error { err := client.Get(ctx, namespacedName, obj, opts...) return err @@ -370,7 +369,7 @@ func ResolveRef(ctx context.Context, client k8client.Client, api *dpv1beta1.API, // GetResolvedBackend resolves backend TLS configurations. func GetResolvedBackend(ctx context.Context, client k8client.Client, - backendNamespacedName types.NamespacedName, api *dpv1beta1.API) *dpv1alpha2.ResolvedBackend { + backendNamespacedName types.NamespacedName, api *dpv1alpha3.API) *dpv1alpha2.ResolvedBackend { resolvedBackend := dpv1alpha2.ResolvedBackend{} resolvedTLSConfig := dpv1alpha2.ResolvedTLSConfig{} var backend dpv1alpha2.Backend @@ -596,7 +595,7 @@ func RetrieveNamespaceListOptions(namespaces []string) k8client.ListOptions { // GetInterceptorService reads InterceptorService when interceptorReference is given func GetInterceptorService(ctx context.Context, client k8client.Client, namespace string, - interceptorReference *dpv1alpha3.InterceptorReference, api *dpv1beta1.API) *dpv1alpha1.InterceptorService { + interceptorReference *dpv1alpha3.InterceptorReference, api *dpv1alpha3.API) *dpv1alpha1.InterceptorService { interceptorService := &dpv1alpha1.InterceptorService{} interceptorRef := types.NamespacedName{ Namespace: namespace, @@ -612,7 +611,7 @@ func GetInterceptorService(ctx context.Context, client k8client.Client, namespac // GetBackendJWT reads BackendJWT when backendJWTReference is given func GetBackendJWT(ctx context.Context, client k8client.Client, namespace, - backendJWTReference string, api *dpv1beta1.API) *dpv1alpha1.BackendJWT { + backendJWTReference string, api *dpv1alpha3.API) *dpv1alpha1.BackendJWT { backendJWT := &dpv1alpha1.BackendJWT{} backendJWTRef := types.NamespacedName{ Namespace: namespace, @@ -628,7 +627,7 @@ func GetBackendJWT(ctx context.Context, client k8client.Client, namespace, // GetAIProvider reads AIProvider when aiProviderReference is given func GetAIProvider(ctx context.Context, client k8client.Client, namespace string, - aiProviderReference string, api *dpv1beta1.API) *dpv1alpha3.AIProvider { + aiProviderReference string, api *dpv1alpha3.API) *dpv1alpha3.AIProvider { aiProvider := &dpv1alpha3.AIProvider{} aiProviderRef := types.NamespacedName{ Namespace: namespace, @@ -645,21 +644,21 @@ func GetAIProvider(ctx context.Context, client k8client.Client, namespace string } // RetrieveAPIList retrieves API list from the given kubernetes client -func RetrieveAPIList(k8sclient k8client.Client) ([]dpv1beta1.API, error) { +func RetrieveAPIList(k8sclient k8client.Client) ([]dpv1alpha3.API, error) { ctx := context.Background() conf := config.ReadConfigs() namespaces := conf.Adapter.Operator.Namespaces - var apis []dpv1beta1.API + var apis []dpv1alpha3.API if namespaces == nil { - apiList := &dpv1beta1.APIList{} + apiList := &dpv1alpha3.APIList{} if err := k8sclient.List(ctx, apiList, &k8client.ListOptions{}); err != nil { return nil, err } - apis = make([]dpv1beta1.API, len(apiList.Items)) + apis = make([]dpv1alpha3.API, len(apiList.Items)) copy(apis[:], apiList.Items[:]) } else { for _, namespace := range namespaces { - apiList := &dpv1beta1.APIList{} + apiList := &dpv1alpha3.APIList{} if err := k8sclient.List(ctx, apiList, &k8client.ListOptions{Namespace: namespace}); err != nil { return nil, err } diff --git a/common-controller/internal/operator/config/crd/bases/dp.wso2.com_apis.yaml b/common-controller/internal/operator/config/crd/bases/dp.wso2.com_apis.yaml index 304529c66f..d3bd7331be 100644 --- a/common-controller/internal/operator/config/crd/bases/dp.wso2.com_apis.yaml +++ b/common-controller/internal/operator/config/crd/bases/dp.wso2.com_apis.yaml @@ -259,8 +259,8 @@ spec: pattern: ^[/][a-zA-Z0-9~/_.-]*$ type: string definitionFileRef: - description: DefinitionFileRef contains the OpenAPI 3 or Swagger definition - of the API in a ConfigMap. + description: DefinitionFileRef contains the definition of the API + in a ConfigMap. type: string definitionPath: default: /api-definition @@ -285,13 +285,13 @@ spec: items: description: EnvConfig contains the environment specific configuration properties: - httpRouteRefs: - description: HTTPRouteRefs denotes the environment of the API. + routeRefs: + description: RouteRefs denotes the environment of the API. items: type: string type: array required: - - httpRouteRefs + - routeRefs type: object maxItems: 1 nullable: true @@ -302,13 +302,191 @@ spec: items: description: EnvConfig contains the environment specific configuration properties: - httpRouteRefs: - description: HTTPRouteRefs denotes the environment of the API. + routeRefs: + description: RouteRefs denotes the environment of the API. items: type: string type: array required: - - httpRouteRefs + - routeRefs + type: object + maxItems: 1 + nullable: true + type: array + systemAPI: + description: SystemAPI denotes if it is an internal system API. + type: boolean + required: + - apiName + - apiType + - apiVersion + - basePath + - definitionPath + type: object + status: + description: APIStatus defines the observed state of API + properties: + deploymentStatus: + description: DeploymentStatus denotes the deployment status of the + API + properties: + accepted: + description: Accepted represents whether the API is accepted or + not. + type: boolean + events: + description: Events contains a list of events related to the API. + items: + type: string + type: array + message: + description: Message represents a user friendly message that explains + the current state of the API. + type: string + status: + description: Status denotes the state of the API in its lifecycle. + Possible values could be Accepted, Invalid, Deploy etc. + type: string + transitionTime: + description: TransitionTime represents the last known transition + timestamp. + format: date-time + type: string + required: + - accepted + - status + - transitionTime + type: object + type: object + type: object + served: true + storage: false + subresources: + status: {} + - additionalPrinterColumns: + - jsonPath: .spec.apiName + name: API Name + type: string + - jsonPath: .spec.apiVersion + name: Version + type: string + - jsonPath: .spec.basePath + name: BasePath + type: string + - jsonPath: .spec.organization + name: Organization + type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1alpha3 + schema: + openAPIV3Schema: + description: API is the Schema for the apis API + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: APISpec defines the desired state of API + properties: + apiName: + description: APIName is the unique name of the API can be used to + uniquely identify an API. + maxLength: 60 + minLength: 1 + pattern: ^[^~!@#;:%^*()+={}|\<>"'',&$\[\]\/]*$ + type: string + apiProperties: + description: APIProperties denotes the custom properties of the API. + items: + description: Property holds key value pair of APIProperties + properties: + name: + type: string + value: + type: string + type: object + nullable: true + type: array + apiType: + description: APIType denotes the type of the API. Possible values + could be REST, GraphQL, GRPC + enum: + - REST + - GraphQL + - GRPC + type: string + apiVersion: + description: APIVersion is the version number of the API. + maxLength: 30 + minLength: 1 + pattern: ^[^~!@#;:%^*()+={}|\<>"'',&/$\[\]\s+\/]+$ + type: string + basePath: + description: 'BasePath denotes the basepath of the API. e.g: /pet-store-api/1.0.6' + pattern: ^[/][a-zA-Z0-9~/_.-]*$ + type: string + definitionFileRef: + description: DefinitionFileRef contains the definition of the API + in a ConfigMap. + type: string + definitionPath: + default: /api-definition + description: DefinitionPath contains the path to expose the API definition. + minLength: 1 + type: string + environment: + description: Environment denotes the environment of the API. + nullable: true + type: string + isDefaultVersion: + description: IsDefaultVersion indicates whether this API version should + be used as a default API + type: boolean + organization: + description: Organization denotes the organization. related to the + API + type: string + production: + description: 'Production contains a list of references to HttpRoutes + of type HttpRoute. xref: https://github.com/kubernetes-sigs/gateway-api/blob/main/apis/v1beta1/httproute_types.go' + items: + description: EnvConfig contains the environment specific configuration + properties: + routeRefs: + description: RouteRefs denotes the environment of the API. + items: + type: string + type: array + required: + - routeRefs + type: object + maxItems: 1 + nullable: true + type: array + sandbox: + description: 'Sandbox contains a list of references to HttpRoutes + of type HttpRoute. xref: https://github.com/kubernetes-sigs/gateway-api/blob/main/apis/v1beta1/httproute_types.go' + items: + description: EnvConfig contains the environment specific configuration + properties: + routeRefs: + description: RouteRefs denotes the environment of the API. + items: + type: string + type: array + required: + - routeRefs type: object maxItems: 1 nullable: true diff --git a/common-controller/internal/operator/config/webhook/manifests.yaml b/common-controller/internal/operator/config/webhook/manifests.yaml index 7f48517089..233b8cad7b 100644 --- a/common-controller/internal/operator/config/webhook/manifests.yaml +++ b/common-controller/internal/operator/config/webhook/manifests.yaml @@ -24,6 +24,26 @@ webhooks: resources: - apis sideEffects: None +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: webhook-service + namespace: system + path: /mutate-dp-wso2-com-v1alpha3-api + failurePolicy: Fail + name: mapi.kb.io + rules: + - apiGroups: + - dp.wso2.com + apiVersions: + - v1alpha3 + operations: + - CREATE + - UPDATE + resources: + - apis + sideEffects: None - admissionReviewVersions: - v1 clientConfig: @@ -230,6 +250,26 @@ webhooks: resources: - apis sideEffects: None +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: webhook-service + namespace: system + path: /validate-dp-wso2-com-v1alpha3-api + failurePolicy: Fail + name: vapi.kb.io + rules: + - apiGroups: + - dp.wso2.com + apiVersions: + - v1alpha3 + operations: + - CREATE + - UPDATE + resources: + - apis + sideEffects: None - admissionReviewVersions: - v1 clientConfig: diff --git a/common-controller/internal/operator/controllers/dp/ratelimitpolicy_controller.go b/common-controller/internal/operator/controllers/dp/ratelimitpolicy_controller.go index c9350dde0e..cf8b546585 100644 --- a/common-controller/internal/operator/controllers/dp/ratelimitpolicy_controller.go +++ b/common-controller/internal/operator/controllers/dp/ratelimitpolicy_controller.go @@ -47,7 +47,6 @@ import ( xds "github.com/wso2/apk/common-controller/internal/xds" dpv1alpha1 "github.com/wso2/apk/common-go-libs/apis/dp/v1alpha1" dpv1alpha3 "github.com/wso2/apk/common-go-libs/apis/dp/v1alpha3" - dpv1beta1 "github.com/wso2/apk/common-go-libs/apis/dp/v1beta1" "github.com/wso2/apk/common-go-libs/constants" ) @@ -88,7 +87,7 @@ func NewratelimitController(mgr manager.Manager, ratelimitStore *cache.Ratelimit conf := config.ReadConfigs() predicates := []predicate.Predicate{predicate.NewPredicateFuncs(utils.FilterByNamespaces(conf.CommonController.Operator.Namespaces))} - if err := c.Watch(source.Kind(mgr.GetCache(), &dpv1beta1.API{}), + if err := c.Watch(source.Kind(mgr.GetCache(), &dpv1alpha3.API{}), handler.EnqueueRequestsFromMapFunc(ratelimitReconciler.getRatelimitForAPI), predicates...); err != nil { loggers.LoggerAPKOperator.ErrorC(logging.PrintError(logging.Error2611, logging.BLOCKER, "Error watching API resources: %v", err)) @@ -190,7 +189,7 @@ func (ratelimitReconciler *RateLimitPolicyReconciler) Reconcile(ctx context.Cont } func (ratelimitReconciler *RateLimitPolicyReconciler) getRatelimitForAPI(ctx context.Context, obj k8client.Object) []reconcile.Request { - api, ok := obj.(*dpv1beta1.API) + api, ok := obj.(*dpv1alpha3.API) if !ok { loggers.LoggerAPKOperator.ErrorC(logging.PrintError(logging.Error2622, logging.TRIVIAL, "Unexpected object type, bypassing reconciliation: %v", api)) @@ -275,7 +274,7 @@ func (ratelimitReconciler *RateLimitPolicyReconciler) marshelRateLimit(ctx conte ratelimitPolicy dpv1alpha3.RateLimitPolicy) ([]dpv1alpha1.ResolveRateLimitAPIPolicy, error) { policyList := []dpv1alpha1.ResolveRateLimitAPIPolicy{} - var api dpv1beta1.API + var api dpv1alpha3.API if err := ratelimitReconciler.client.Get(ctx, types.NamespacedName{ Namespace: ratelimitKey.Namespace, diff --git a/common-controller/internal/operator/operator.go b/common-controller/internal/operator/operator.go index b94111c6e5..6ab3078c62 100644 --- a/common-controller/internal/operator/operator.go +++ b/common-controller/internal/operator/operator.go @@ -41,7 +41,6 @@ import ( dpv1alpha1 "github.com/wso2/apk/common-go-libs/apis/dp/v1alpha1" dpv1alpha2 "github.com/wso2/apk/common-go-libs/apis/dp/v1alpha2" dpv1alpha3 "github.com/wso2/apk/common-go-libs/apis/dp/v1alpha3" - dpv1beta1 "github.com/wso2/apk/common-go-libs/apis/dp/v1beta1" "k8s.io/apimachinery/pkg/runtime" utilruntime "k8s.io/apimachinery/pkg/util/runtime" clientgoscheme "k8s.io/client-go/kubernetes/scheme" @@ -69,7 +68,6 @@ func init() { utilruntime.Must(cpv1alpha2.AddToScheme(scheme)) utilruntime.Must(cpv1alpha3.AddToScheme(scheme)) utilruntime.Must(dpv1alpha3.AddToScheme(scheme)) - utilruntime.Must(dpv1beta1.AddToScheme(scheme)) //+kubebuilder:scaffold:scheme } @@ -139,7 +137,7 @@ func InitOperator(metricsConfig config.Metrics) { "Unable to create webhook API, error: %v", err)) } - if err = (&dpv1beta1.API{}).SetupWebhookWithManager(mgr); err != nil { + if err = (&dpv1alpha3.API{}).SetupWebhookWithManager(mgr); err != nil { loggers.LoggerAPKOperator.ErrorC(logging.PrintError(logging.Error2601, logging.MAJOR, "Unable to create webhook API, error: %v", err)) } diff --git a/common-go-libs/PROJECT b/common-go-libs/PROJECT index d0c674b1df..b61863b590 100644 --- a/common-go-libs/PROJECT +++ b/common-go-libs/PROJECT @@ -195,9 +195,10 @@ resources: domain: wso2.com group: dp kind: API - path: github.com/wso2/apk/common-go-libs/apis/dp/v1beta1 - version: v1beta1 + path: github.com/wso2/apk/common-go-libs/apis/dp/v1alpha3 + version: v1alpha3 webhooks: + conversion: true defaulting: true validation: true webhookVersion: v1 diff --git a/common-go-libs/apis/dp/v1alpha1/api_conversion.go b/common-go-libs/apis/dp/v1alpha1/api_conversion.go index e6ffe0296d..571556a11a 100644 --- a/common-go-libs/apis/dp/v1alpha1/api_conversion.go +++ b/common-go-libs/apis/dp/v1alpha1/api_conversion.go @@ -18,15 +18,15 @@ package v1alpha1 import ( - "github.com/wso2/apk/common-go-libs/apis/dp/v1beta1" + "github.com/wso2/apk/common-go-libs/apis/dp/v1alpha3" "sigs.k8s.io/controller-runtime/pkg/conversion" ) -// ConvertTo converts this API CR to the Hub version (v1beta1). -// src is v1alpha1.API and dst is v1beta1.API. +// ConvertTo converts this API CR to the Hub version (v1alpha3). +// src is v1alpha1.API and dst is v1alpha3.API. func (src *API) ConvertTo(dstRaw conversion.Hub) error { - dst := dstRaw.(*v1beta1.API) + dst := dstRaw.(*v1alpha3.API) dst.ObjectMeta = src.ObjectMeta // Spec @@ -41,40 +41,40 @@ func (src *API) ConvertTo(dstRaw conversion.Hub) error { dst.Spec.SystemAPI = src.Spec.SystemAPI if src.Spec.Production != nil { - dst.Spec.Production = []v1beta1.EnvConfig{} + dst.Spec.Production = []v1alpha3.EnvConfig{} for _, productionRef := range src.Spec.Production { - dst.Spec.Production = append(dst.Spec.Production, v1beta1.EnvConfig{ + dst.Spec.Production = append(dst.Spec.Production, v1alpha3.EnvConfig{ RouteRefs: productionRef.HTTPRouteRefs, }) } } if src.Spec.Sandbox != nil { - dst.Spec.Sandbox = []v1beta1.EnvConfig{} + dst.Spec.Sandbox = []v1alpha3.EnvConfig{} for _, sandboxRef := range src.Spec.Sandbox { - dst.Spec.Sandbox = append(dst.Spec.Sandbox, v1beta1.EnvConfig{ + dst.Spec.Sandbox = append(dst.Spec.Sandbox, v1alpha3.EnvConfig{ RouteRefs: sandboxRef.HTTPRouteRefs, }) } } // Convert []Property to []v1alpha2.Property - var properties []v1beta1.Property + var properties []v1alpha3.Property for _, p := range src.Spec.APIProperties { - properties = append(properties, v1beta1.Property(p)) + properties = append(properties, v1alpha3.Property(p)) } dst.Spec.APIProperties = properties // Status - dst.Status.DeploymentStatus = v1beta1.DeploymentStatus(src.Status.DeploymentStatus) + dst.Status.DeploymentStatus = v1alpha3.DeploymentStatus(src.Status.DeploymentStatus) return nil } -// ConvertFrom converts from the Hub version (v1beta1) to this version. -// src is v1alpha1.API and dst is v1beta1.API. +// ConvertFrom converts from the Hub version (v1alpha3) to this version. +// src is v1alpha1.API and dst is v1alpha3.API. func (src *API) ConvertFrom(srcRaw conversion.Hub) error { - dst := srcRaw.(*v1beta1.API) + dst := srcRaw.(*v1alpha3.API) src.ObjectMeta = dst.ObjectMeta // Spec diff --git a/common-go-libs/apis/dp/v1alpha2/api_conversion.go b/common-go-libs/apis/dp/v1alpha2/api_conversion.go index 5fd93d09b4..3bcb5f4aa3 100644 --- a/common-go-libs/apis/dp/v1alpha2/api_conversion.go +++ b/common-go-libs/apis/dp/v1alpha2/api_conversion.go @@ -18,15 +18,15 @@ package v1alpha2 import ( - "github.com/wso2/apk/common-go-libs/apis/dp/v1beta1" + "github.com/wso2/apk/common-go-libs/apis/dp/v1alpha3" "sigs.k8s.io/controller-runtime/pkg/conversion" ) -// ConvertTo converts this API CR to the Hub version (v1beta1). -// src is v1alpha2.API and dst is v1beta1.API. +// ConvertTo converts this API CR to the Hub version (v1alpha3). +// src is v1alpha2.API and dst is v1alpha3.API. func (src *API) ConvertTo(dstRaw conversion.Hub) error { - dst := dstRaw.(*v1beta1.API) + dst := dstRaw.(*v1alpha3.API) dst.ObjectMeta = src.ObjectMeta // Spec @@ -41,40 +41,40 @@ func (src *API) ConvertTo(dstRaw conversion.Hub) error { dst.Spec.SystemAPI = src.Spec.SystemAPI if src.Spec.Production != nil { - dst.Spec.Production = []v1beta1.EnvConfig{} + dst.Spec.Production = []v1alpha3.EnvConfig{} for _, productionRef := range src.Spec.Production { - dst.Spec.Production = append(dst.Spec.Production, v1beta1.EnvConfig{ + dst.Spec.Production = append(dst.Spec.Production, v1alpha3.EnvConfig{ RouteRefs: productionRef.RouteRefs, }) } } if src.Spec.Sandbox != nil { - dst.Spec.Sandbox = []v1beta1.EnvConfig{} + dst.Spec.Sandbox = []v1alpha3.EnvConfig{} for _, sandboxRef := range src.Spec.Sandbox { - dst.Spec.Sandbox = append(dst.Spec.Sandbox, v1beta1.EnvConfig{ + dst.Spec.Sandbox = append(dst.Spec.Sandbox, v1alpha3.EnvConfig{ RouteRefs: sandboxRef.RouteRefs, }) } } // Convert []Property to []v1alpha2.Property - var properties []v1beta1.Property + var properties []v1alpha3.Property for _, p := range src.Spec.APIProperties { - properties = append(properties, v1beta1.Property(p)) + properties = append(properties, v1alpha3.Property(p)) } dst.Spec.APIProperties = properties // Status - dst.Status.DeploymentStatus = v1beta1.DeploymentStatus(src.Status.DeploymentStatus) + dst.Status.DeploymentStatus = v1alpha3.DeploymentStatus(src.Status.DeploymentStatus) return nil } -// ConvertFrom converts from the Hub version (v1beta1) to this version. -// src is v1alpha2.API and dst is v1beta1.API. +// ConvertFrom converts from the Hub version (v1alpha3) to this version. +// src is v1alpha2.API and dst is v1alpha3.API. func (src *API) ConvertFrom(srcRaw conversion.Hub) error { - dst := srcRaw.(*v1beta1.API) + dst := srcRaw.(*v1alpha3.API) src.ObjectMeta = dst.ObjectMeta // Spec diff --git a/common-go-libs/apis/dp/v1beta1/api_conversion.go b/common-go-libs/apis/dp/v1alpha3/api_conversion.go similarity index 97% rename from common-go-libs/apis/dp/v1beta1/api_conversion.go rename to common-go-libs/apis/dp/v1alpha3/api_conversion.go index 5564f83e08..1d99745222 100644 --- a/common-go-libs/apis/dp/v1beta1/api_conversion.go +++ b/common-go-libs/apis/dp/v1alpha3/api_conversion.go @@ -15,7 +15,7 @@ * */ -package v1beta1 +package v1alpha3 // Hub marks this type as a conversion hub. func (*API) Hub() {} diff --git a/common-go-libs/apis/dp/v1beta1/api_types.go b/common-go-libs/apis/dp/v1alpha3/api_types.go similarity index 99% rename from common-go-libs/apis/dp/v1beta1/api_types.go rename to common-go-libs/apis/dp/v1alpha3/api_types.go index 943e9a1341..de1eba9b30 100644 --- a/common-go-libs/apis/dp/v1beta1/api_types.go +++ b/common-go-libs/apis/dp/v1alpha3/api_types.go @@ -15,7 +15,7 @@ * */ -package v1beta1 +package v1alpha3 import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" diff --git a/common-go-libs/apis/dp/v1beta1/api_webhook.go b/common-go-libs/apis/dp/v1alpha3/api_webhook.go similarity index 95% rename from common-go-libs/apis/dp/v1beta1/api_webhook.go rename to common-go-libs/apis/dp/v1alpha3/api_webhook.go index 0303745046..ceb312d0cd 100644 --- a/common-go-libs/apis/dp/v1beta1/api_webhook.go +++ b/common-go-libs/apis/dp/v1alpha3/api_webhook.go @@ -15,7 +15,7 @@ * */ -package v1beta1 +package v1alpha3 import ( "bytes" @@ -62,7 +62,7 @@ func (r *API) SetupWebhookWithManager(mgr ctrl.Manager) error { // TODO(user): EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN! -//+kubebuilder:webhook:path=/mutate-dp-wso2-com-v1beta1-api,mutating=true,failurePolicy=fail,sideEffects=None,groups=dp.wso2.com,resources=apis,verbs=create;update,versions=v1beta1,name=mapi.kb.io,admissionReviewVersions=v1 +//+kubebuilder:webhook:path=/mutate-dp-wso2-com-v1alpha3-api,mutating=true,failurePolicy=fail,sideEffects=None,groups=dp.wso2.com,resources=apis,verbs=create;update,versions=v1alpha3,name=mapi.kb.io,admissionReviewVersions=v1 var _ webhook.Defaulter = &API{} @@ -74,7 +74,7 @@ func (r *API) Default() { } // TODO(user): change verbs to "verbs=create;update;delete" if you want to enable deletion validation. -//+kubebuilder:webhook:path=/validate-dp-wso2-com-v1beta1-api,mutating=false,failurePolicy=fail,sideEffects=None,groups=dp.wso2.com,resources=apis,verbs=create;update,versions=v1beta1,name=vapi.kb.io,admissionReviewVersions=v1 +//+kubebuilder:webhook:path=/validate-dp-wso2-com-v1alpha3-api,mutating=false,failurePolicy=fail,sideEffects=None,groups=dp.wso2.com,resources=apis,verbs=create;update,versions=v1alpha3,name=vapi.kb.io,admissionReviewVersions=v1 var _ webhook.Validator = &API{} diff --git a/common-go-libs/apis/dp/v1beta1/webhook_suite_test.go b/common-go-libs/apis/dp/v1alpha3/webhook_suite_test.go similarity index 97% rename from common-go-libs/apis/dp/v1beta1/webhook_suite_test.go rename to common-go-libs/apis/dp/v1alpha3/webhook_suite_test.go index 4c43950443..53ab76230b 100644 --- a/common-go-libs/apis/dp/v1beta1/webhook_suite_test.go +++ b/common-go-libs/apis/dp/v1alpha3/webhook_suite_test.go @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. + * Copyright (c) 2023, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,7 +15,7 @@ * */ -package v1beta1 +package v1alpha3 import ( "context" diff --git a/common-go-libs/apis/dp/v1alpha3/zz_generated.deepcopy.go b/common-go-libs/apis/dp/v1alpha3/zz_generated.deepcopy.go index 5b95f57d6a..927fb3f465 100644 --- a/common-go-libs/apis/dp/v1alpha3/zz_generated.deepcopy.go +++ b/common-go-libs/apis/dp/v1alpha3/zz_generated.deepcopy.go @@ -372,6 +372,56 @@ func (in *APIRateLimitPolicy) DeepCopy() *APIRateLimitPolicy { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *APISpec) DeepCopyInto(out *APISpec) { + *out = *in + if in.Production != nil { + in, out := &in.Production, &out.Production + *out = make([]EnvConfig, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.Sandbox != nil { + in, out := &in.Sandbox, &out.Sandbox + *out = make([]EnvConfig, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.APIProperties != nil { + in, out := &in.APIProperties, &out.APIProperties + *out = make([]Property, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new APISpec. +func (in *APISpec) DeepCopy() *APISpec { + if in == nil { + return nil + } + out := new(APISpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *APIStatus) DeepCopyInto(out *APIStatus) { + *out = *in + in.DeploymentStatus.DeepCopyInto(&out.DeploymentStatus) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new APIStatus. +func (in *APIStatus) DeepCopy() *APIStatus { + if in == nil { + return nil + } + out := new(APIStatus) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *BackendJWTToken) DeepCopyInto(out *BackendJWTToken) { *out = *in @@ -457,6 +507,50 @@ func (in *CustomRateLimitPolicy) DeepCopy() *CustomRateLimitPolicy { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DeploymentStatus) DeepCopyInto(out *DeploymentStatus) { + *out = *in + if in.TransitionTime != nil { + in, out := &in.TransitionTime, &out.TransitionTime + *out = (*in).DeepCopy() + } + if in.Events != nil { + in, out := &in.Events, &out.Events + *out = make([]string, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DeploymentStatus. +func (in *DeploymentStatus) DeepCopy() *DeploymentStatus { + if in == nil { + return nil + } + out := new(DeploymentStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *EnvConfig) DeepCopyInto(out *EnvConfig) { + *out = *in + if in.RouteRefs != nil { + in, out := &in.RouteRefs, &out.RouteRefs + *out = make([]string, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EnvConfig. +func (in *EnvConfig) DeepCopy() *EnvConfig { + if in == nil { + return nil + } + out := new(EnvConfig) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *InterceptorReference) DeepCopyInto(out *InterceptorReference) { *out = *in @@ -512,6 +606,21 @@ func (in *PolicySpec) DeepCopy() *PolicySpec { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Property) DeepCopyInto(out *Property) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Property. +func (in *Property) DeepCopy() *Property { + if in == nil { + return nil + } + out := new(Property) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *RateLimitAPIPolicy) DeepCopyInto(out *RateLimitAPIPolicy) { *out = *in diff --git a/common-go-libs/apis/dp/v1beta1/groupversion_info.go b/common-go-libs/apis/dp/v1beta1/groupversion_info.go deleted file mode 100644 index 9f7ad3d004..0000000000 --- a/common-go-libs/apis/dp/v1beta1/groupversion_info.go +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -// Package v1beta1 contains API Schema definitions for the dp v1beta1 API group -// +kubebuilder:object:generate=true -// +groupName=dp.wso2.com -package v1beta1 - -import ( - "k8s.io/apimachinery/pkg/runtime/schema" - "sigs.k8s.io/controller-runtime/pkg/scheme" -) - -var ( - // GroupVersion is group version used to register these objects - GroupVersion = schema.GroupVersion{Group: "dp.wso2.com", Version: "v1beta1"} - - // SchemeBuilder is used to add go types to the GroupVersionKind scheme - SchemeBuilder = &scheme.Builder{GroupVersion: GroupVersion} - - // AddToScheme adds the types in this group-version to the given scheme. - AddToScheme = SchemeBuilder.AddToScheme -) diff --git a/common-go-libs/apis/dp/v1beta1/zz_generated.deepcopy.go b/common-go-libs/apis/dp/v1beta1/zz_generated.deepcopy.go deleted file mode 100644 index 79cffd6dbb..0000000000 --- a/common-go-libs/apis/dp/v1beta1/zz_generated.deepcopy.go +++ /dev/null @@ -1,195 +0,0 @@ -//go:build !ignore_autogenerated -// +build !ignore_autogenerated - -/* - * Copyright (c) 2023, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -// Code generated by controller-gen. DO NOT EDIT. - -package v1beta1 - -import ( - "k8s.io/apimachinery/pkg/runtime" -) - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *API) DeepCopyInto(out *API) { - *out = *in - out.TypeMeta = in.TypeMeta - in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) - in.Spec.DeepCopyInto(&out.Spec) - in.Status.DeepCopyInto(&out.Status) -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new API. -func (in *API) DeepCopy() *API { - if in == nil { - return nil - } - out := new(API) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *API) DeepCopyObject() runtime.Object { - if c := in.DeepCopy(); c != nil { - return c - } - return nil -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *APIList) DeepCopyInto(out *APIList) { - *out = *in - out.TypeMeta = in.TypeMeta - in.ListMeta.DeepCopyInto(&out.ListMeta) - if in.Items != nil { - in, out := &in.Items, &out.Items - *out = make([]API, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new APIList. -func (in *APIList) DeepCopy() *APIList { - if in == nil { - return nil - } - out := new(APIList) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *APIList) DeepCopyObject() runtime.Object { - if c := in.DeepCopy(); c != nil { - return c - } - return nil -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *APISpec) DeepCopyInto(out *APISpec) { - *out = *in - if in.Production != nil { - in, out := &in.Production, &out.Production - *out = make([]EnvConfig, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } - if in.Sandbox != nil { - in, out := &in.Sandbox, &out.Sandbox - *out = make([]EnvConfig, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } - if in.APIProperties != nil { - in, out := &in.APIProperties, &out.APIProperties - *out = make([]Property, len(*in)) - copy(*out, *in) - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new APISpec. -func (in *APISpec) DeepCopy() *APISpec { - if in == nil { - return nil - } - out := new(APISpec) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *APIStatus) DeepCopyInto(out *APIStatus) { - *out = *in - in.DeploymentStatus.DeepCopyInto(&out.DeploymentStatus) -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new APIStatus. -func (in *APIStatus) DeepCopy() *APIStatus { - if in == nil { - return nil - } - out := new(APIStatus) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *DeploymentStatus) DeepCopyInto(out *DeploymentStatus) { - *out = *in - if in.TransitionTime != nil { - in, out := &in.TransitionTime, &out.TransitionTime - *out = (*in).DeepCopy() - } - if in.Events != nil { - in, out := &in.Events, &out.Events - *out = make([]string, len(*in)) - copy(*out, *in) - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DeploymentStatus. -func (in *DeploymentStatus) DeepCopy() *DeploymentStatus { - if in == nil { - return nil - } - out := new(DeploymentStatus) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *EnvConfig) DeepCopyInto(out *EnvConfig) { - *out = *in - if in.RouteRefs != nil { - in, out := &in.RouteRefs, &out.RouteRefs - *out = make([]string, len(*in)) - copy(*out, *in) - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EnvConfig. -func (in *EnvConfig) DeepCopy() *EnvConfig { - if in == nil { - return nil - } - out := new(EnvConfig) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *Property) DeepCopyInto(out *Property) { - *out = *in -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Property. -func (in *Property) DeepCopy() *Property { - if in == nil { - return nil - } - out := new(Property) - in.DeepCopyInto(out) - return out -} diff --git a/common-go-libs/config/certmanager/certificate.yaml b/common-go-libs/config/certmanager/certificate.yaml deleted file mode 100644 index 4321a02d48..0000000000 --- a/common-go-libs/config/certmanager/certificate.yaml +++ /dev/null @@ -1,39 +0,0 @@ -# The following manifests contain a self-signed issuer CR and a certificate CR. -# More document can be found at https://docs.cert-manager.io -# WARNING: Targets CertManager v1.0. Check https://cert-manager.io/docs/installation/upgrading/ for breaking changes. -apiVersion: cert-manager.io/v1 -kind: Issuer -metadata: - labels: - app.kubernetes.io/name: issuer - app.kubernetes.io/instance: selfsigned-issuer - app.kubernetes.io/component: certificate - app.kubernetes.io/created-by: operator - app.kubernetes.io/part-of: operator - app.kubernetes.io/managed-by: kustomize - name: selfsigned-issuer - namespace: system -spec: - selfSigned: {} ---- -apiVersion: cert-manager.io/v1 -kind: Certificate -metadata: - labels: - app.kubernetes.io/name: certificate - app.kubernetes.io/instance: serving-cert - app.kubernetes.io/component: certificate - app.kubernetes.io/created-by: operator - app.kubernetes.io/part-of: operator - app.kubernetes.io/managed-by: kustomize - name: serving-cert # this name should match the one appeared in kustomizeconfig.yaml - namespace: system -spec: - # $(SERVICE_NAME) and $(SERVICE_NAMESPACE) will be substituted by kustomize - dnsNames: - - $(SERVICE_NAME).$(SERVICE_NAMESPACE).svc - - $(SERVICE_NAME).$(SERVICE_NAMESPACE).svc.cluster.local - issuerRef: - kind: Issuer - name: selfsigned-issuer - secretName: webhook-server-cert # this secret will not be prefixed, since it's not managed by kustomize diff --git a/common-go-libs/config/certmanager/kustomization.yaml b/common-go-libs/config/certmanager/kustomization.yaml deleted file mode 100644 index bebea5a595..0000000000 --- a/common-go-libs/config/certmanager/kustomization.yaml +++ /dev/null @@ -1,5 +0,0 @@ -resources: -- certificate.yaml - -configurations: -- kustomizeconfig.yaml diff --git a/common-go-libs/config/certmanager/kustomizeconfig.yaml b/common-go-libs/config/certmanager/kustomizeconfig.yaml deleted file mode 100644 index e631f77736..0000000000 --- a/common-go-libs/config/certmanager/kustomizeconfig.yaml +++ /dev/null @@ -1,16 +0,0 @@ -# This configuration is for teaching kustomize how to update name ref and var substitution -nameReference: -- kind: Issuer - group: cert-manager.io - fieldSpecs: - - kind: Certificate - group: cert-manager.io - path: spec/issuerRef/name - -varReference: -- kind: Certificate - group: cert-manager.io - path: spec/commonName -- kind: Certificate - group: cert-manager.io - path: spec/dnsNames diff --git a/common-go-libs/config/crd/bases/dp.wso2.com_apis.yaml b/common-go-libs/config/crd/bases/dp.wso2.com_apis.yaml index 1fa8ea6721..d3bd7331be 100644 --- a/common-go-libs/config/crd/bases/dp.wso2.com_apis.yaml +++ b/common-go-libs/config/crd/bases/dp.wso2.com_apis.yaml @@ -379,7 +379,7 @@ spec: - jsonPath: .metadata.creationTimestamp name: Age type: date - name: v1beta1 + name: v1alpha3 schema: openAPIV3Schema: description: API is the Schema for the apis API diff --git a/common-go-libs/config/crd/patches/cainjection_in_dp_apis.yaml b/common-go-libs/config/crd/patches/cainjection_in_dp_apis.yaml deleted file mode 100644 index 7f949667b8..0000000000 --- a/common-go-libs/config/crd/patches/cainjection_in_dp_apis.yaml +++ /dev/null @@ -1,7 +0,0 @@ -# The following patch adds a directive for certmanager to inject CA into the CRD -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME) - name: apis.dp.wso2.com diff --git a/common-go-libs/config/crd/patches/webhook_in_dp_apis.yaml b/common-go-libs/config/crd/patches/webhook_in_dp_apis.yaml deleted file mode 100644 index ad0c609d56..0000000000 --- a/common-go-libs/config/crd/patches/webhook_in_dp_apis.yaml +++ /dev/null @@ -1,16 +0,0 @@ -# The following patch enables a conversion webhook for the CRD -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - name: apis.dp.wso2.com -spec: - conversion: - strategy: Webhook - webhook: - clientConfig: - service: - namespace: system - name: webhook-service - path: /convert - conversionReviewVersions: - - v1 diff --git a/common-go-libs/config/default/manager_webhook_patch.yaml b/common-go-libs/config/default/manager_webhook_patch.yaml deleted file mode 100644 index 738de350b7..0000000000 --- a/common-go-libs/config/default/manager_webhook_patch.yaml +++ /dev/null @@ -1,23 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: controller-manager - namespace: system -spec: - template: - spec: - containers: - - name: manager - ports: - - containerPort: 9443 - name: webhook-server - protocol: TCP - volumeMounts: - - mountPath: /tmp/k8s-webhook-server/serving-certs - name: cert - readOnly: true - volumes: - - name: cert - secret: - defaultMode: 420 - secretName: webhook-server-cert diff --git a/common-go-libs/config/default/webhookcainjection_patch.yaml b/common-go-libs/config/default/webhookcainjection_patch.yaml deleted file mode 100644 index 76add1fe00..0000000000 --- a/common-go-libs/config/default/webhookcainjection_patch.yaml +++ /dev/null @@ -1,29 +0,0 @@ -# This patch add annotation to admission webhook config and -# the variables $(CERTIFICATE_NAMESPACE) and $(CERTIFICATE_NAME) will be substituted by kustomize. -apiVersion: admissionregistration.k8s.io/v1 -kind: MutatingWebhookConfiguration -metadata: - labels: - app.kubernetes.io/name: mutatingwebhookconfiguration - app.kubernetes.io/instance: mutating-webhook-configuration - app.kubernetes.io/component: webhook - app.kubernetes.io/created-by: operator - app.kubernetes.io/part-of: operator - app.kubernetes.io/managed-by: kustomize - name: mutating-webhook-configuration - annotations: - cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME) ---- -apiVersion: admissionregistration.k8s.io/v1 -kind: ValidatingWebhookConfiguration -metadata: - labels: - app.kubernetes.io/name: validatingwebhookconfiguration - app.kubernetes.io/instance: validating-webhook-configuration - app.kubernetes.io/component: webhook - app.kubernetes.io/created-by: operator - app.kubernetes.io/part-of: operator - app.kubernetes.io/managed-by: kustomize - name: validating-webhook-configuration - annotations: - cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME) diff --git a/common-go-libs/config/rbac/dp_api_editor_role.yaml b/common-go-libs/config/rbac/dp_api_editor_role.yaml deleted file mode 100644 index 2de3070c5b..0000000000 --- a/common-go-libs/config/rbac/dp_api_editor_role.yaml +++ /dev/null @@ -1,31 +0,0 @@ -# permissions for end users to edit apis. -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - labels: - app.kubernetes.io/name: clusterrole - app.kubernetes.io/instance: api-editor-role - app.kubernetes.io/component: rbac - app.kubernetes.io/created-by: operator - app.kubernetes.io/part-of: operator - app.kubernetes.io/managed-by: kustomize - name: api-editor-role -rules: -- apiGroups: - - dp.wso2.com - resources: - - apis - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - dp.wso2.com - resources: - - apis/status - verbs: - - get diff --git a/common-go-libs/config/rbac/dp_api_viewer_role.yaml b/common-go-libs/config/rbac/dp_api_viewer_role.yaml deleted file mode 100644 index 75e4c488a0..0000000000 --- a/common-go-libs/config/rbac/dp_api_viewer_role.yaml +++ /dev/null @@ -1,27 +0,0 @@ -# permissions for end users to view apis. -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - labels: - app.kubernetes.io/name: clusterrole - app.kubernetes.io/instance: api-viewer-role - app.kubernetes.io/component: rbac - app.kubernetes.io/created-by: operator - app.kubernetes.io/part-of: operator - app.kubernetes.io/managed-by: kustomize - name: api-viewer-role -rules: -- apiGroups: - - dp.wso2.com - resources: - - apis - verbs: - - get - - list - - watch -- apiGroups: - - dp.wso2.com - resources: - - apis/status - verbs: - - get diff --git a/common-go-libs/config/samples/dp_v1beta1_api.yaml b/common-go-libs/config/samples/dp_v1alpha3_api.yaml similarity index 89% rename from common-go-libs/config/samples/dp_v1beta1_api.yaml rename to common-go-libs/config/samples/dp_v1alpha3_api.yaml index c84dd75cdb..5e0505f10b 100644 --- a/common-go-libs/config/samples/dp_v1beta1_api.yaml +++ b/common-go-libs/config/samples/dp_v1alpha3_api.yaml @@ -1,4 +1,4 @@ -apiVersion: dp.wso2.com/v1beta1 +apiVersion: dp.wso2.com/v1alpha3 kind: API metadata: labels: diff --git a/common-go-libs/config/webhook/kustomization.yaml b/common-go-libs/config/webhook/kustomization.yaml deleted file mode 100644 index 9cf26134e4..0000000000 --- a/common-go-libs/config/webhook/kustomization.yaml +++ /dev/null @@ -1,6 +0,0 @@ -resources: -- manifests.yaml -- service.yaml - -configurations: -- kustomizeconfig.yaml diff --git a/common-go-libs/config/webhook/kustomizeconfig.yaml b/common-go-libs/config/webhook/kustomizeconfig.yaml deleted file mode 100644 index 25e21e3c96..0000000000 --- a/common-go-libs/config/webhook/kustomizeconfig.yaml +++ /dev/null @@ -1,25 +0,0 @@ -# the following config is for teaching kustomize where to look at when substituting vars. -# It requires kustomize v2.1.0 or newer to work properly. -nameReference: -- kind: Service - version: v1 - fieldSpecs: - - kind: MutatingWebhookConfiguration - group: admissionregistration.k8s.io - path: webhooks/clientConfig/service/name - - kind: ValidatingWebhookConfiguration - group: admissionregistration.k8s.io - path: webhooks/clientConfig/service/name - -namespace: -- kind: MutatingWebhookConfiguration - group: admissionregistration.k8s.io - path: webhooks/clientConfig/service/namespace - create: true -- kind: ValidatingWebhookConfiguration - group: admissionregistration.k8s.io - path: webhooks/clientConfig/service/namespace - create: true - -varReference: -- path: metadata/annotations diff --git a/common-go-libs/config/webhook/manifests.yaml b/common-go-libs/config/webhook/manifests.yaml index 487ae0aae6..f4a9f4cd1e 100644 --- a/common-go-libs/config/webhook/manifests.yaml +++ b/common-go-libs/config/webhook/manifests.yaml @@ -10,9 +10,9 @@ webhooks: service: name: webhook-service namespace: system - path: /mutate-dp-wso2-com-v1alpha3-apipolicy + path: /mutate-dp-wso2-com-v1alpha3-api failurePolicy: Fail - name: mapipolicy.kb.io + name: mapi.kb.io rules: - apiGroups: - dp.wso2.com @@ -22,7 +22,7 @@ webhooks: - CREATE - UPDATE resources: - - apipolicies + - apis sideEffects: None - admissionReviewVersions: - v1 @@ -30,19 +30,19 @@ webhooks: service: name: webhook-service namespace: system - path: /mutate-dp-wso2-com-v1beta1-api + path: /mutate-dp-wso2-com-v1alpha3-apipolicy failurePolicy: Fail - name: mapi.kb.io + name: mapipolicy.kb.io rules: - apiGroups: - dp.wso2.com apiVersions: - - v1beta1 + - v1alpha3 operations: - CREATE - UPDATE resources: - - apis + - apipolicies sideEffects: None - admissionReviewVersions: - v1 @@ -216,9 +216,9 @@ webhooks: service: name: webhook-service namespace: system - path: /validate-dp-wso2-com-v1alpha3-apipolicy + path: /validate-dp-wso2-com-v1alpha3-api failurePolicy: Fail - name: vapipolicy.kb.io + name: vapi.kb.io rules: - apiGroups: - dp.wso2.com @@ -228,7 +228,7 @@ webhooks: - CREATE - UPDATE resources: - - apipolicies + - apis sideEffects: None - admissionReviewVersions: - v1 @@ -236,19 +236,19 @@ webhooks: service: name: webhook-service namespace: system - path: /validate-dp-wso2-com-v1beta1-api + path: /validate-dp-wso2-com-v1alpha3-apipolicy failurePolicy: Fail - name: vapi.kb.io + name: vapipolicy.kb.io rules: - apiGroups: - dp.wso2.com apiVersions: - - v1beta1 + - v1alpha3 operations: - CREATE - UPDATE resources: - - apis + - apipolicies sideEffects: None - admissionReviewVersions: - v1 diff --git a/common-go-libs/config/webhook/service.yaml b/common-go-libs/config/webhook/service.yaml deleted file mode 100644 index 3d52bb199a..0000000000 --- a/common-go-libs/config/webhook/service.yaml +++ /dev/null @@ -1,20 +0,0 @@ - -apiVersion: v1 -kind: Service -metadata: - labels: - app.kubernetes.io/name: service - app.kubernetes.io/instance: webhook-service - app.kubernetes.io/component: webhook - app.kubernetes.io/created-by: operator - app.kubernetes.io/part-of: operator - app.kubernetes.io/managed-by: kustomize - name: webhook-service - namespace: system -spec: - ports: - - port: 443 - protocol: TCP - targetPort: 9443 - selector: - control-plane: controller-manager diff --git a/common-go-libs/revive.toml b/common-go-libs/revive.toml index 2a89307bda..c1f3f2392c 100644 --- a/common-go-libs/revive.toml +++ b/common-go-libs/revive.toml @@ -8,7 +8,7 @@ warningCode = 0 [rule.context-as-argument] [rule.context-keys-type] [rule.dot-imports] -exclude = ["apis/dp/v1alpha1/webhook_suite_test.go", "apis/dp/v1alpha2/webhook_suite_test.go", "apis/dp/v1beta1/webhook_suite_test.go"] +exclude = ["apis/dp/v1alpha1/webhook_suite_test.go", "apis/dp/v1alpha2/webhook_suite_test.go", "apis/dp/v1alpha3/webhook_suite_test.go"] [rule.error-return] [rule.error-strings] [rule.error-naming] diff --git a/gateway/enforcer/org.wso2.apk.enforcer/src/main/java/org/wso2/apk/enforcer/api/GRPCAPI.java b/gateway/enforcer/org.wso2.apk.enforcer/src/main/java/org/wso2/apk/enforcer/api/GRPCAPI.java index 970db479f1..540d4d9130 100644 --- a/gateway/enforcer/org.wso2.apk.enforcer/src/main/java/org/wso2/apk/enforcer/api/GRPCAPI.java +++ b/gateway/enforcer/org.wso2.apk.enforcer/src/main/java/org/wso2/apk/enforcer/api/GRPCAPI.java @@ -54,7 +54,7 @@ public String init(Api api) { List resources = new ArrayList<>(); String mutualSSL = api.getMutualSSL(); - boolean applicationSecurity = api.getApplicationSecurity(); + Map applicationSecurity = api.getApplicationSecurityMap(); EndpointCluster endpoints = Utils.processEndpoints(api.getEndpoints()); EndpointSecurity[] endpointSecurity = APIProcessUtils.convertProtoEndpointSecurity( diff --git a/helm-charts/templates/crds/dp.wso2.com_apis.yaml b/helm-charts/templates/crds/dp.wso2.com_apis.yaml index 488a91b203..86c80490bc 100644 --- a/helm-charts/templates/crds/dp.wso2.com_apis.yaml +++ b/helm-charts/templates/crds/dp.wso2.com_apis.yaml @@ -392,7 +392,7 @@ spec: - jsonPath: .metadata.creationTimestamp name: Age type: date - name: v1beta1 + name: v1alpha3 schema: openAPIV3Schema: description: API is the Schema for the apis API diff --git a/helm-charts/templates/data-plane/config-deployer/wso2-apk-config-deployer-api.yaml b/helm-charts/templates/data-plane/config-deployer/wso2-apk-config-deployer-api.yaml index f81c8731d0..46338fdca8 100644 --- a/helm-charts/templates/data-plane/config-deployer/wso2-apk-config-deployer-api.yaml +++ b/helm-charts/templates/data-plane/config-deployer/wso2-apk-config-deployer-api.yaml @@ -15,7 +15,7 @@ # under the License. {{- if and .Values.wso2.apk.dp.enabled .Values.wso2.apk.dp.configdeployer.enabled }} kind: "API" -apiVersion: "dp.wso2.com/v1beta1" +apiVersion: "dp.wso2.com/v1alpha3" metadata: name: "{{ template "apk-helm.resource.prefix" . }}-wso2-apk-config-deployer-api" namespace: {{ .Release.Namespace }} diff --git a/helm-charts/templates/data-plane/config-deployer/wso2-apk-config-generator-api.yaml b/helm-charts/templates/data-plane/config-deployer/wso2-apk-config-generator-api.yaml index 3835db7756..7293110143 100644 --- a/helm-charts/templates/data-plane/config-deployer/wso2-apk-config-generator-api.yaml +++ b/helm-charts/templates/data-plane/config-deployer/wso2-apk-config-generator-api.yaml @@ -15,7 +15,7 @@ # under the License. {{- if and .Values.wso2.apk.dp.enabled .Values.wso2.apk.dp.configdeployer.enabled }} kind: "API" -apiVersion: "dp.wso2.com/v1beta1" +apiVersion: "dp.wso2.com/v1alpha3" metadata: name: "{{ template "apk-helm.resource.prefix" . }}-wso2-apk-config-generator-api" namespace: {{ .Release.Namespace }} diff --git a/helm-charts/templates/data-plane/gateway-components/common-controller/api/notification-api.yaml b/helm-charts/templates/data-plane/gateway-components/common-controller/api/notification-api.yaml index f6c677cc0c..558f8af605 100644 --- a/helm-charts/templates/data-plane/gateway-components/common-controller/api/notification-api.yaml +++ b/helm-charts/templates/data-plane/gateway-components/common-controller/api/notification-api.yaml @@ -16,7 +16,7 @@ {{- if and .Values.wso2.apk.dp.enabled .Values.wso2.apk.dp.commonController }} kind: "API" -apiVersion: "dp.wso2.com/v1beta1" +apiVersion: "dp.wso2.com/v1alpha3" metadata: name: "{{ template "apk-helm.resource.prefix" . }}-wso2-apk-notification-api" namespace: {{ .Release.Namespace }} diff --git a/helm-charts/templates/data-plane/gateway-components/common-controller/webhook/adapter-mutating-webhook-config.yaml b/helm-charts/templates/data-plane/gateway-components/common-controller/webhook/adapter-mutating-webhook-config.yaml index 92a0934c8f..f254504fdc 100644 --- a/helm-charts/templates/data-plane/gateway-components/common-controller/webhook/adapter-mutating-webhook-config.yaml +++ b/helm-charts/templates/data-plane/gateway-components/common-controller/webhook/adapter-mutating-webhook-config.yaml @@ -31,14 +31,14 @@ webhooks: service: name: {{ template "apk-helm.resource.prefix" . }}-common-controller-service namespace: {{ .Release.Namespace }} - path: /mutate-dp-wso2-com-v1beta1-api + path: /mutate-dp-wso2-com-v1alpha3-api failurePolicy: Fail name: mapi.kb.io rules: - apiGroups: - dp.wso2.com apiVersions: - - v1beta1 + - v1alpha3 operations: - CREATE - UPDATE diff --git a/helm-charts/templates/data-plane/gateway-components/common-controller/webhook/adapter-validation-webhook-config.yaml b/helm-charts/templates/data-plane/gateway-components/common-controller/webhook/adapter-validation-webhook-config.yaml index 6b0aa2146e..3bec9878db 100644 --- a/helm-charts/templates/data-plane/gateway-components/common-controller/webhook/adapter-validation-webhook-config.yaml +++ b/helm-charts/templates/data-plane/gateway-components/common-controller/webhook/adapter-validation-webhook-config.yaml @@ -51,14 +51,14 @@ webhooks: service: name: {{ template "apk-helm.resource.prefix" . }}-common-controller-service namespace: {{ .Release.Namespace }} - path: /validate-dp-wso2-com-v1beta1-api + path: /validate-dp-wso2-com-v1alpha3-api failurePolicy: Fail name: vapi.kb.io rules: - apiGroups: - dp.wso2.com apiVersions: - - v1beta1 + - v1alpha3 operations: - CREATE - UPDATE diff --git a/helm-charts/templates/data-plane/gateway-components/gateway-runtime/jwks-domain-api.yaml b/helm-charts/templates/data-plane/gateway-components/gateway-runtime/jwks-domain-api.yaml index 4e97cb9248..f88b83845a 100644 --- a/helm-charts/templates/data-plane/gateway-components/gateway-runtime/jwks-domain-api.yaml +++ b/helm-charts/templates/data-plane/gateway-components/gateway-runtime/jwks-domain-api.yaml @@ -15,7 +15,7 @@ # under the License. {{- if .Values.idp.enabled }} kind: "API" -apiVersion: "dp.wso2.com/v1beta1" +apiVersion: "dp.wso2.com/v1alpha3" metadata: name: {{ template "apk-helm.resource.prefix" . }}-jwks-endpoint-ds-api namespace: {{ .Release.Namespace }} diff --git a/helm-charts/templates/idp/authenticationEndpoint-domain-api.yaml b/helm-charts/templates/idp/authenticationEndpoint-domain-api.yaml index 4d4325330e..4e2195e425 100644 --- a/helm-charts/templates/idp/authenticationEndpoint-domain-api.yaml +++ b/helm-charts/templates/idp/authenticationEndpoint-domain-api.yaml @@ -15,7 +15,7 @@ # under the License. {{- if .Values.idp.enabled }} kind: "API" -apiVersion: "dp.wso2.com/v1beta1" +apiVersion: "dp.wso2.com/v1alpha3" metadata: name: {{ template "apk-helm.resource.prefix" . }}-authentication-endpoint-ds-api namespace: {{ .Release.Namespace }} diff --git a/helm-charts/templates/idp/commonoauth-domain-api.yaml b/helm-charts/templates/idp/commonoauth-domain-api.yaml index cdd7a2a6d9..07a37772af 100644 --- a/helm-charts/templates/idp/commonoauth-domain-api.yaml +++ b/helm-charts/templates/idp/commonoauth-domain-api.yaml @@ -15,7 +15,7 @@ # under the License. {{- if .Values.idp.enabled }} kind: "API" -apiVersion: "dp.wso2.com/v1beta1" +apiVersion: "dp.wso2.com/v1alpha3" metadata: name: {{ template "apk-helm.resource.prefix" . }}-commonoauth-api namespace: {{ .Release.Namespace }} diff --git a/helm-charts/templates/idp/dcr-domain-api.yaml b/helm-charts/templates/idp/dcr-domain-api.yaml index aa6d70eb31..6507dccc7a 100644 --- a/helm-charts/templates/idp/dcr-domain-api.yaml +++ b/helm-charts/templates/idp/dcr-domain-api.yaml @@ -15,7 +15,7 @@ # under the License. {{- if .Values.idp.enabled }} kind: "API" -apiVersion: "dp.wso2.com/v1beta1" +apiVersion: "dp.wso2.com/v1alpha3" metadata: name: {{ template "apk-helm.resource.prefix" . }}-dcr-api namespace: {{ .Release.Namespace }} diff --git a/helm-charts/templates/idp/oauth-domain-api.yaml b/helm-charts/templates/idp/oauth-domain-api.yaml index f67fecbb48..0a45aad4fd 100644 --- a/helm-charts/templates/idp/oauth-domain-api.yaml +++ b/helm-charts/templates/idp/oauth-domain-api.yaml @@ -15,7 +15,7 @@ # under the License. {{- if .Values.idp.enabled }} kind: "API" -apiVersion: "dp.wso2.com/v1beta1" +apiVersion: "dp.wso2.com/v1alpha3" metadata: name: {{ template "apk-helm.resource.prefix" . }}-oauth-api namespace: {{ .Release.Namespace }} diff --git a/runtime/config-deployer-service/ballerina/Ballerina.toml b/runtime/config-deployer-service/ballerina/Ballerina.toml index c71d1d9355..3dca5c5cc4 100644 --- a/runtime/config-deployer-service/ballerina/Ballerina.toml +++ b/runtime/config-deployer-service/ballerina/Ballerina.toml @@ -373,12 +373,6 @@ groupId = "joda-time" artifactId = "joda-time" version = "2.10.2" -# transitive dependency of com.google.protobuf:protobuf-java:3.21.7 -[[platform.java11.dependency]] -groupId = "com.google.protobuf" -artifactId = "protobuf-java" -version = "3.21.7" - # transitive dependency of org.wso2.apk:org.wso2.apk.config:1.2.0-SNAPSHOT [[platform.java11.dependency]] groupId = "org.json" diff --git a/runtime/config-deployer-service/ballerina/Ballerina.toml.template b/runtime/config-deployer-service/ballerina/Ballerina.toml.template index 92a1405cdd..0a4e99c589 100644 --- a/runtime/config-deployer-service/ballerina/Ballerina.toml.template +++ b/runtime/config-deployer-service/ballerina/Ballerina.toml.template @@ -319,12 +319,6 @@ groupId = "org.reactivestreams" artifactId = "reactive-streams" version = "1.0.3" -# transitive dependency of com.google.protobuf:protobuf-java:3.21.7 -[[platform.java11.dependency]] -groupId = "com.google.protobuf" -artifactId = "protobuf-java" -version = "3.21.7" - # transitive dependency of org.wso2.apk:org.wso2.apk.config:1.0.0-SNAPSHOT [[platform.java11.dependency]] groupId = "com.fasterxml.jackson.core" diff --git a/runtime/config-deployer-service/ballerina/K8sClient.bal b/runtime/config-deployer-service/ballerina/K8sClient.bal index f0d10722a2..ed1219c0d6 100644 --- a/runtime/config-deployer-service/ballerina/K8sClient.bal +++ b/runtime/config-deployer-service/ballerina/K8sClient.bal @@ -57,7 +57,7 @@ isolated function getConfigMapValueFromNameAndNamespace(string name, string name } isolated function deleteAPICR(string name, string namespace) returns http:Response|http:ClientError { - string endpoint = "/apis/dp.wso2.com/v1beta1/namespaces/" + namespace + "/apis/" + name; + string endpoint = "/apis/dp.wso2.com/v1alpha3/namespaces/" + namespace + "/apis/" + name; return k8sApiServerEp->delete(endpoint, targetType = http:Response); } @@ -112,12 +112,12 @@ isolated function deleteConfigMap(string name, string namespace) returns http:Re } isolated function deployAPICR(model:API api, string namespace) returns http:Response|http:ClientError { - string endpoint = "/apis/dp.wso2.com/v1beta1/namespaces/" + namespace + "/apis"; + string endpoint = "/apis/dp.wso2.com/v1alpha3/namespaces/" + namespace + "/apis"; return k8sApiServerEp->post(endpoint, api, targetType = http:Response); } isolated function updateAPICR(model:API api, string namespace) returns http:Response|http:ClientError { - string endpoint = "/apis/dp.wso2.com/v1beta1/namespaces/" + namespace + "/apis/" + api.metadata.name; + string endpoint = "/apis/dp.wso2.com/v1alpha3/namespaces/" + namespace + "/apis/" + api.metadata.name; return k8sApiServerEp->put(endpoint, api, targetType = http:Response); } @@ -152,7 +152,7 @@ isolated function updateGqlRoute(model:GQLRoute gqlroute, string namespace) retu } public isolated function getK8sAPIByNameAndNamespace(string name, string namespace) returns model:API?|commons:APKError { - string endpoint = "/apis/dp.wso2.com/v1beta1/namespaces/" + namespace + "/apis/" + name; + string endpoint = "/apis/dp.wso2.com/v1alpha3/namespaces/" + namespace + "/apis/" + name; do { http:Response response = check k8sApiServerEp->get(endpoint); if response.statusCode == 200 { diff --git a/runtime/config-deployer-service/ballerina/modules/model/API.bal b/runtime/config-deployer-service/ballerina/modules/model/API.bal index d7f6008b82..2fd19f850d 100644 --- a/runtime/config-deployer-service/ballerina/modules/model/API.bal +++ b/runtime/config-deployer-service/ballerina/modules/model/API.bal @@ -18,7 +18,7 @@ public type API record { string kind = "API"; - string apiVersion = "dp.wso2.com/v1beta1"; + string apiVersion = "dp.wso2.com/v1alpha3"; Metadata metadata; APISpec spec; APIStatus? status = (); @@ -62,7 +62,7 @@ public type EnvConfig record { }; public type APIList record { - string apiVersion = "dp.wso2.com/v1beta1"; + string apiVersion = "dp.wso2.com/v1alpha3"; string kind = "APIList"; API[] items; ListMeta metadata; diff --git a/test/cucumber-tests/src/test/resources/artifacts/apk-confs/apikey/apikey-different-header.apk-conf b/test/cucumber-tests/src/test/resources/artifacts/apk-confs/apikey/apikey-different-header.apk-conf new file mode 100644 index 0000000000..949e4829d7 --- /dev/null +++ b/test/cucumber-tests/src/test/resources/artifacts/apk-confs/apikey/apikey-different-header.apk-conf @@ -0,0 +1,32 @@ +name: "EmployeeServiceAPI" +basePath: "/employee" +version: "3.14" +id: "apikey-different-header" +type: "REST" +defaultVersion: false +endpointConfigurations: + production: + endpoint: "http://backend:80/anything" +operations: + - target: "/employee" + verb: "GET" + secured: false + scopes: [] + - target: "/employee" + verb: "POST" + secured: true + scopes: [] + - target: "/employee/{employeeId}" + verb: "PUT" + secured: true + scopes: [] + - target: "/employee/{employeeId}" + verb: "DELETE" + secured: true + scopes: [] +authentication: + - authType: APIKey + enabled: true + required: mandatory + headerEnable: true + headerName: "Test-Header" diff --git a/test/cucumber-tests/src/test/resources/artifacts/apk-confs/apikey/apikey-enabled.apk-conf b/test/cucumber-tests/src/test/resources/artifacts/apk-confs/apikey/apikey-enabled.apk-conf new file mode 100644 index 0000000000..e3e3673dea --- /dev/null +++ b/test/cucumber-tests/src/test/resources/artifacts/apk-confs/apikey/apikey-enabled.apk-conf @@ -0,0 +1,32 @@ +name: "EmployeeServiceAPI" +basePath: "/employee" +id: "apikey-enabled" +version: "3.14" +type: "REST" +defaultVersion: false +endpointConfigurations: + production: + endpoint: "http://backend:80/anything" +operations: + - target: "/employee" + verb: "GET" + secured: false + scopes: [] + - target: "/employee" + verb: "POST" + secured: true + scopes: [] + - target: "/employee/{employeeId}" + verb: "PUT" + secured: true + scopes: [] + - target: "/employee/{employeeId}" + verb: "DELETE" + secured: true + scopes: [] +authentication: + - authType: APIKey + enabled: true + required: mandatory + headerEnable: true + headerName: "APIKey" From 219792272d7f5fb9199bcd77fe0991fddfff253d Mon Sep 17 00:00:00 2001 From: sgayangi Date: Thu, 19 Sep 2024 13:35:22 +0530 Subject: [PATCH 15/17] Update APIs in integration tests --- .../tests/resources/base/manifests.yaml | 92 +++---- .../tests/all-http-methods-for-wildcard.yaml | 36 +-- .../tests/api-different-listener.yaml | 50 ++-- .../tests/api-policy-with-jwt-generator.yaml | 38 +-- .../tests/api-with-backend-base-path.yaml | 38 +-- .../resources/tests/api-with-cors-policy.yaml | 108 ++++---- .../tests/api-with-operational-policy.yaml | 56 ++-- .../resources/tests/api-with-path-params.yaml | 50 ++-- .../tests/api-with-request-header-modify.yaml | 78 +++--- .../api-with-response-header-modify.yaml | 104 +++---- .../tests/api-without-backend-base-path.yaml | 38 +-- .../tests/backend-api-key-security.yaml | 6 +- .../resources/tests/custom-auth-header.yaml | 42 +-- .../tests/custom-policy-ratelimiting.yaml | 10 +- ...-api-version-ratelimit-resource-level.yaml | 64 ++--- .../tests/default-api-version-ratelimit.yaml | 36 +-- .../resources/tests/default-api-version.yaml | 36 +-- .../different-endpoint-with-same-route.yaml | 60 ++-- .../tests/disable-api-level-jwt.yaml | 112 ++++---- .../tests/disable-api-level-security.yaml | 58 ++-- .../tests/disable-resource-level-jwt.yaml | 136 ++++----- .../disable-resource-level-security.yaml | 70 ++--- .../resources/tests/fetch-api-definition.yaml | 48 ++-- .../fetch-non-existing-api-definition.yaml | 48 ++-- .../tests/resources/tests/gql-api.yaml | 80 +++--- .../tests/interceptors-api-level.yaml | 54 ++-- .../tests/interceptors-resource-level.yaml | 94 +++---- .../tests/jwt-api-level-security.yaml | 40 +-- .../tests/jwt-resource-level-security.yaml | 104 +++---- .../tests/multiple_port_listener.yaml | 54 ++-- .../resources/tests/prod-and-sand-apis.yaml | 82 +++--- .../resources/tests/ratelimit-priority.yaml | 64 ++--- .../resources/tests/resource-scopes.yaml | 80 +++--- .../tests/resources/tests/trailing-slash.yaml | 260 +++++++++--------- 34 files changed, 1163 insertions(+), 1163 deletions(-) diff --git a/test/integration/integration/tests/resources/base/manifests.yaml b/test/integration/integration/tests/resources/base/manifests.yaml index 5d4ef24606..3bcf566107 100644 --- a/test/integration/integration/tests/resources/base/manifests.yaml +++ b/test/integration/integration/tests/resources/base/manifests.yaml @@ -50,20 +50,20 @@ spec: app: infra-backend-v1 spec: containers: - - name: infra-backend-v1 - image: gcr.io/k8s-staging-ingressconformance/echoserver:v20221109-7ee2f3e - env: - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - resources: - requests: - cpu: 10m + - name: infra-backend-v1 + image: gcr.io/k8s-staging-ingressconformance/echoserver:v20221109-7ee2f3e + env: + - name: POD_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + - name: NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + resources: + requests: + cpu: 10m --- apiVersion: v1 kind: Service @@ -96,20 +96,20 @@ spec: app: gql-backend-v1 spec: containers: - - name: gql-backend-v1 - image: tharsanan/gq:1.0.0 - env: - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - resources: - requests: - cpu: 10m + - name: gql-backend-v1 + image: tharsanan/gq:1.0.0 + env: + - name: POD_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + - name: NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + resources: + requests: + cpu: 10m --- apiVersion: v1 kind: Service @@ -142,20 +142,20 @@ spec: app: infra-backend-v2 spec: containers: - - name: infra-backend-v2 - image: gcr.io/k8s-staging-ingressconformance/echoserver:v20221109-7ee2f3e - env: - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - resources: - requests: - cpu: "10m" + - name: infra-backend-v2 + image: gcr.io/k8s-staging-ingressconformance/echoserver:v20221109-7ee2f3e + env: + - name: POD_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + - name: NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + resources: + requests: + cpu: "10m" # TODO (Amila): Uncomment once AKS test runner is enabled # --- # apiVersion: v1 @@ -409,11 +409,11 @@ spec: targetRef: group: gateway.networking.k8s.io kind: Gateway - name: wso2-apk-default + name: wso2-apk-default --- -# This is a API with wrong organization(jwt issuer not set). +# This is a API with wrong organization(jwt issuer not set). # When test cases apply the correct API it should overrite the wrong value to correct value -apiVersion: dp.wso2.com/v1alpha2 +apiVersion: dp.wso2.com/v1alpha3 kind: API metadata: name: all-http-methods-for-wildcard @@ -427,5 +427,5 @@ spec: #definitionFileRef: definition-file production: - routeRefs: - - all-http-methods-for-wildcard-httproute + - all-http-methods-for-wildcard-httproute organization: wso2-org1111 diff --git a/test/integration/integration/tests/resources/tests/all-http-methods-for-wildcard.yaml b/test/integration/integration/tests/resources/tests/all-http-methods-for-wildcard.yaml index 2e34fab924..8f39db7403 100644 --- a/test/integration/integration/tests/resources/tests/all-http-methods-for-wildcard.yaml +++ b/test/integration/integration/tests/resources/tests/all-http-methods-for-wildcard.yaml @@ -14,7 +14,7 @@ # specific language governing permissions and limitations # under the License. -apiVersion: dp.wso2.com/v1alpha2 +apiVersion: dp.wso2.com/v1alpha3 kind: API metadata: name: all-http-methods-for-wildcard @@ -28,7 +28,7 @@ spec: #definitionFileRef: definition-file production: - routeRefs: - - all-http-methods-for-wildcard-httproute + - all-http-methods-for-wildcard-httproute organization: wso2-org --- apiVersion: gateway.networking.k8s.io/v1beta1 @@ -38,22 +38,22 @@ metadata: namespace: gateway-integration-test-infra spec: parentRefs: - - group: gateway.networking.k8s.io - kind: Gateway - name: wso2-apk-default - namespace: apk-integration-test - sectionName: httpslistener + - group: gateway.networking.k8s.io + kind: Gateway + name: wso2-apk-default + namespace: apk-integration-test + sectionName: httpslistener hostnames: - - all-http-methods-for-wildcard.test.gw.wso2.com + - all-http-methods-for-wildcard.test.gw.wso2.com rules: - - matches: - - path: - type: PathPrefix - value: /v2/echo-full - backendRefs: - - group: dp.wso2.com - kind: Backend - name: infra-backend-v1 + - matches: + - path: + type: PathPrefix + value: /v2/echo-full + backendRefs: + - group: dp.wso2.com + kind: Backend + name: infra-backend-v1 --- apiVersion: dp.wso2.com/v1alpha2 kind: Backend @@ -62,5 +62,5 @@ metadata: namespace: gateway-integration-test-infra spec: services: - - host: infra-backend-v1.gateway-integration-test-infra - port: 8080 + - host: infra-backend-v1.gateway-integration-test-infra + port: 8080 diff --git a/test/integration/integration/tests/resources/tests/api-different-listener.yaml b/test/integration/integration/tests/resources/tests/api-different-listener.yaml index 829c5c55e8..ebcd96ec1e 100644 --- a/test/integration/integration/tests/resources/tests/api-different-listener.yaml +++ b/test/integration/integration/tests/resources/tests/api-different-listener.yaml @@ -14,7 +14,7 @@ # specific language governing permissions and limitations # under the License. -apiVersion: dp.wso2.com/v1alpha2 +apiVersion: dp.wso2.com/v1alpha3 kind: API metadata: name: test-api-with-different-listener @@ -27,7 +27,7 @@ spec: #definitionFileRef: definition-file production: - routeRefs: - - test-api-with-different-listener-httproute + - test-api-with-different-listener-httproute organization: wso2-org --- apiVersion: gateway.networking.k8s.io/v1beta1 @@ -37,29 +37,29 @@ metadata: namespace: gateway-integration-test-infra spec: parentRefs: - - group: gateway.networking.k8s.io - kind: Gateway - name: wso2-apk-default - namespace: apk-integration-test - sectionName: httpslistener + - group: gateway.networking.k8s.io + kind: Gateway + name: wso2-apk-default + namespace: apk-integration-test + sectionName: httpslistener hostnames: - - api.am.wso2.com + - api.am.wso2.com rules: - - matches: - - path: - type: RegularExpression - value: /user/([^/]+)/playlist/([^/]+) - method: GET - filters: - - type: URLRewrite - urlRewrite: - path: - type: ReplaceFullPath - replaceFullPath: /user/\1/playlist/\2 - backendRefs: - - group: dp.wso2.com - kind: Backend - name: infra-backend-v1 + - matches: + - path: + type: RegularExpression + value: /user/([^/]+)/playlist/([^/]+) + method: GET + filters: + - type: URLRewrite + urlRewrite: + path: + type: ReplaceFullPath + replaceFullPath: /user/\1/playlist/\2 + backendRefs: + - group: dp.wso2.com + kind: Backend + name: infra-backend-v1 --- apiVersion: dp.wso2.com/v1alpha2 kind: Backend @@ -68,5 +68,5 @@ metadata: namespace: gateway-integration-test-infra spec: services: - - host: infra-backend-v1.gateway-integration-test-infra - port: 8080 + - host: infra-backend-v1.gateway-integration-test-infra + port: 8080 diff --git a/test/integration/integration/tests/resources/tests/api-policy-with-jwt-generator.yaml b/test/integration/integration/tests/resources/tests/api-policy-with-jwt-generator.yaml index 8f6d17f22c..9edffc19f1 100644 --- a/test/integration/integration/tests/resources/tests/api-policy-with-jwt-generator.yaml +++ b/test/integration/integration/tests/resources/tests/api-policy-with-jwt-generator.yaml @@ -14,7 +14,7 @@ # specific language governing permissions and limitations # under the License. -apiVersion: dp.wso2.com/v1alpha2 +apiVersion: dp.wso2.com/v1alpha3 kind: API metadata: name: api-policy-with-jwt-generator @@ -28,7 +28,7 @@ spec: #definitionFileRef: definition-file production: - routeRefs: - - api-policy-with-jwt-generator-httproute + - api-policy-with-jwt-generator-httproute organization: wso2-org --- apiVersion: gateway.networking.k8s.io/v1beta1 @@ -38,22 +38,22 @@ metadata: namespace: gateway-integration-test-infra spec: parentRefs: - - group: gateway.networking.k8s.io - kind: Gateway - name: wso2-apk-default - namespace: apk-integration-test - sectionName: httpslistener + - group: gateway.networking.k8s.io + kind: Gateway + name: wso2-apk-default + namespace: apk-integration-test + sectionName: httpslistener hostnames: - - api-policy-with-jwt-generator.test.gw.wso2.com + - api-policy-with-jwt-generator.test.gw.wso2.com rules: - - matches: - - path: - type: PathPrefix - value: /v2/echo-full - backendRefs: - - group: dp.wso2.com - kind: Backend - name: infra-backend-v1 + - matches: + - path: + type: PathPrefix + value: /v2/echo-full + backendRefs: + - group: dp.wso2.com + kind: Backend + name: infra-backend-v1 --- apiVersion: dp.wso2.com/v1alpha3 kind: APIPolicy @@ -67,7 +67,7 @@ spec: targetRef: group: gateway.networking.k8s.io kind: API - name: api-policy-with-jwt-generator + name: api-policy-with-jwt-generator --- apiVersion: dp.wso2.com/v1alpha1 kind: BackendJWT @@ -86,5 +86,5 @@ metadata: namespace: gateway-integration-test-infra spec: services: - - host: infra-backend-v1.gateway-integration-test-infra - port: 8080 \ No newline at end of file + - host: infra-backend-v1.gateway-integration-test-infra + port: 8080 diff --git a/test/integration/integration/tests/resources/tests/api-with-backend-base-path.yaml b/test/integration/integration/tests/resources/tests/api-with-backend-base-path.yaml index 7f8905d4a8..d26c6408ff 100644 --- a/test/integration/integration/tests/resources/tests/api-with-backend-base-path.yaml +++ b/test/integration/integration/tests/resources/tests/api-with-backend-base-path.yaml @@ -22,10 +22,10 @@ metadata: spec: basePath: /backend-base-path services: - - host: infra-backend-v1.gateway-integration-test-infra - port: 8080 + - host: infra-backend-v1.gateway-integration-test-infra + port: 8080 --- -apiVersion: dp.wso2.com/v1alpha2 +apiVersion: dp.wso2.com/v1alpha3 kind: API metadata: name: test-api-with-backend-base-path @@ -39,7 +39,7 @@ spec: #definitionFileRef: swagger-definition-webhook production: - routeRefs: - - test-api-with-backend-base-path-httproute + - test-api-with-backend-base-path-httproute organization: wso2-org --- apiVersion: gateway.networking.k8s.io/v1beta1 @@ -49,20 +49,20 @@ metadata: namespace: gateway-integration-test-infra spec: parentRefs: - - group: gateway.networking.k8s.io - kind: Gateway - name: wso2-apk-default - namespace: apk-integration-test - sectionName: httpslistener + - group: gateway.networking.k8s.io + kind: Gateway + name: wso2-apk-default + namespace: apk-integration-test + sectionName: httpslistener hostnames: - - backend-base-path.test.gw.wso2.com + - backend-base-path.test.gw.wso2.com rules: - - matches: - - path: - type: PathPrefix - value: / - method: GET - backendRefs: - - group: dp.wso2.com - kind: Backend - name: infra-backend-basepath + - matches: + - path: + type: PathPrefix + value: / + method: GET + backendRefs: + - group: dp.wso2.com + kind: Backend + name: infra-backend-basepath diff --git a/test/integration/integration/tests/resources/tests/api-with-cors-policy.yaml b/test/integration/integration/tests/resources/tests/api-with-cors-policy.yaml index a1bff40bfc..40387daa2a 100644 --- a/test/integration/integration/tests/resources/tests/api-with-cors-policy.yaml +++ b/test/integration/integration/tests/resources/tests/api-with-cors-policy.yaml @@ -14,7 +14,7 @@ # specific language governing permissions and limitations # under the License. -apiVersion: dp.wso2.com/v1alpha2 +apiVersion: dp.wso2.com/v1alpha3 kind: API metadata: name: cors-policy-api @@ -27,7 +27,7 @@ spec: isDefaultVersion: true production: - routeRefs: - - cors-policy-httproute + - cors-policy-httproute organization: wso2-org --- apiVersion: gateway.networking.k8s.io/v1beta1 @@ -37,29 +37,29 @@ metadata: namespace: gateway-integration-test-infra spec: hostnames: - - cors-policy.test.gw.wso2.com + - cors-policy.test.gw.wso2.com parentRefs: - - group: gateway.networking.k8s.io - kind: Gateway - name: wso2-apk-default - namespace: apk-integration-test - sectionName: httpslistener + - group: gateway.networking.k8s.io + kind: Gateway + name: wso2-apk-default + namespace: apk-integration-test + sectionName: httpslistener rules: - - matches: - - path: - type: PathPrefix - value: /test - method: GET - filters: - - type: URLRewrite - urlRewrite: - path: - type: ReplacePrefixMatch - replacePrefixMatch: /v2/echo-full - backendRefs: - - group: dp.wso2.com - kind: Backend - name: infra-backend-v1 + - matches: + - path: + type: PathPrefix + value: /test + method: GET + filters: + - type: URLRewrite + urlRewrite: + path: + type: ReplacePrefixMatch + replacePrefixMatch: /v2/echo-full + backendRefs: + - group: dp.wso2.com + kind: Backend + name: infra-backend-v1 --- apiVersion: dp.wso2.com/v1alpha3 kind: APIPolicy @@ -71,20 +71,20 @@ spec: cORSPolicy: accessControlAllowCredentials: true accessControlAllowOrigins: - - "*.wso2.com" + - "*.wso2.com" accessControlAllowHeaders: - - authorization + - authorization accessControlAllowMethods: - - GET - - POST + - GET + - POST accessControlExposeHeaders: - - "*" + - "*" targetRef: group: gateway.networking.k8s.io kind: API name: cors-policy-api --- -apiVersion: dp.wso2.com/v1alpha2 +apiVersion: dp.wso2.com/v1alpha3 kind: API metadata: name: no-cors-policy-api @@ -96,7 +96,7 @@ spec: basePath: /no-cors-policy-api/1.0.0 production: - routeRefs: - - no-cors-policy-httproute + - no-cors-policy-httproute organization: wso2-org --- apiVersion: gateway.networking.k8s.io/v1beta1 @@ -106,29 +106,29 @@ metadata: namespace: gateway-integration-test-infra spec: hostnames: - - cors-policy.test.gw.wso2.com + - cors-policy.test.gw.wso2.com parentRefs: - - group: gateway.networking.k8s.io - kind: Gateway - name: wso2-apk-default - namespace: apk-integration-test - sectionName: httpslistener + - group: gateway.networking.k8s.io + kind: Gateway + name: wso2-apk-default + namespace: apk-integration-test + sectionName: httpslistener rules: - - matches: - - path: - type: PathPrefix - value: /test - method: GET - filters: - - type: URLRewrite - urlRewrite: - path: - type: ReplacePrefixMatch - replacePrefixMatch: /v2/echo-full - backendRefs: - - group: dp.wso2.com - kind: Backend - name: infra-backend-v1 + - matches: + - path: + type: PathPrefix + value: /test + method: GET + filters: + - type: URLRewrite + urlRewrite: + path: + type: ReplacePrefixMatch + replacePrefixMatch: /v2/echo-full + backendRefs: + - group: dp.wso2.com + kind: Backend + name: infra-backend-v1 --- apiVersion: dp.wso2.com/v1alpha2 kind: Backend @@ -137,8 +137,8 @@ metadata: namespace: gateway-integration-test-infra spec: services: - - host: infra-backend-v1.gateway-integration-test-infra - port: 8080 + - host: infra-backend-v1.gateway-integration-test-infra + port: 8080 --- apiVersion: dp.wso2.com/v1alpha3 kind: APIPolicy @@ -152,4 +152,4 @@ spec: targetRef: group: gateway.networking.k8s.io kind: API - name: no-cors-policy-api \ No newline at end of file + name: no-cors-policy-api diff --git a/test/integration/integration/tests/resources/tests/api-with-operational-policy.yaml b/test/integration/integration/tests/resources/tests/api-with-operational-policy.yaml index e444e8271e..3e3f6c10e9 100644 --- a/test/integration/integration/tests/resources/tests/api-with-operational-policy.yaml +++ b/test/integration/integration/tests/resources/tests/api-with-operational-policy.yaml @@ -30,7 +30,7 @@ # specific language governing permissions and limitations # under the License. -apiVersion: dp.wso2.com/v1alpha2 +apiVersion: dp.wso2.com/v1alpha3 kind: API metadata: name: test-api-with-operatinal-policy @@ -44,7 +44,7 @@ spec: #definitionFileRef: swagger-definition-webhook production: - routeRefs: - - test-api-with-operatinal-policy-httproute + - test-api-with-operatinal-policy-httproute organization: wso2-org --- apiVersion: gateway.networking.k8s.io/v1beta1 @@ -54,32 +54,32 @@ metadata: namespace: gateway-integration-test-infra spec: parentRefs: - - group: gateway.networking.k8s.io - kind: Gateway - name: wso2-apk-default - namespace: apk-integration-test - sectionName: httpslistener + - group: gateway.networking.k8s.io + kind: Gateway + name: wso2-apk-default + namespace: apk-integration-test + sectionName: httpslistener hostnames: - - backend-base-path.test.gw.wso2.com + - backend-base-path.test.gw.wso2.com rules: - - matches: - - path: - type: PathPrefix - value: /test-get-path - method: GET - backendRefs: - - group: dp.wso2.com - kind: Backend - name: infra-backend-v1 - - matches: - - path: - type: PathPrefix - value: /test-post-path - method: POST - backendRefs: - - group: dp.wso2.com - kind: Backend - name: infra-backend-v1 + - matches: + - path: + type: PathPrefix + value: /test-get-path + method: GET + backendRefs: + - group: dp.wso2.com + kind: Backend + name: infra-backend-v1 + - matches: + - path: + type: PathPrefix + value: /test-post-path + method: POST + backendRefs: + - group: dp.wso2.com + kind: Backend + name: infra-backend-v1 --- apiVersion: dp.wso2.com/v1alpha2 kind: Backend @@ -88,5 +88,5 @@ metadata: namespace: gateway-integration-test-infra spec: services: - - host: infra-backend-v1.gateway-integration-test-infra - port: 8080 + - host: infra-backend-v1.gateway-integration-test-infra + port: 8080 diff --git a/test/integration/integration/tests/resources/tests/api-with-path-params.yaml b/test/integration/integration/tests/resources/tests/api-with-path-params.yaml index 962c1c9ec7..7ee3375b9c 100644 --- a/test/integration/integration/tests/resources/tests/api-with-path-params.yaml +++ b/test/integration/integration/tests/resources/tests/api-with-path-params.yaml @@ -14,7 +14,7 @@ # specific language governing permissions and limitations # under the License. -apiVersion: dp.wso2.com/v1alpha2 +apiVersion: dp.wso2.com/v1alpha3 kind: API metadata: name: test-api-with-path-params @@ -28,7 +28,7 @@ spec: #definitionFileRef: definition-file production: - routeRefs: - - test-api-with-path-params-httproute + - test-api-with-path-params-httproute organization: wso2-org --- apiVersion: gateway.networking.k8s.io/v1beta1 @@ -38,29 +38,29 @@ metadata: namespace: gateway-integration-test-infra spec: parentRefs: - - group: gateway.networking.k8s.io - kind: Gateway - name: wso2-apk-default - namespace: apk-integration-test - sectionName: httpslistener + - group: gateway.networking.k8s.io + kind: Gateway + name: wso2-apk-default + namespace: apk-integration-test + sectionName: httpslistener hostnames: - - path-param-api.test.gw.wso2.com + - path-param-api.test.gw.wso2.com rules: - - matches: - - path: - type: RegularExpression - value: /user/([^/]+)/playlist/([^/]+) - method: GET - filters: - - type: URLRewrite - urlRewrite: - path: - type: ReplaceFullPath - replaceFullPath: /user/\1/playlist/\2 - backendRefs: - - group: dp.wso2.com - kind: Backend - name: infra-backend-v1 + - matches: + - path: + type: RegularExpression + value: /user/([^/]+)/playlist/([^/]+) + method: GET + filters: + - type: URLRewrite + urlRewrite: + path: + type: ReplaceFullPath + replaceFullPath: /user/\1/playlist/\2 + backendRefs: + - group: dp.wso2.com + kind: Backend + name: infra-backend-v1 --- apiVersion: dp.wso2.com/v1alpha2 kind: Backend @@ -69,5 +69,5 @@ metadata: namespace: gateway-integration-test-infra spec: services: - - host: infra-backend-v1.gateway-integration-test-infra - port: 8080 + - host: infra-backend-v1.gateway-integration-test-infra + port: 8080 diff --git a/test/integration/integration/tests/resources/tests/api-with-request-header-modify.yaml b/test/integration/integration/tests/resources/tests/api-with-request-header-modify.yaml index 1239dae383..a93f44fe85 100644 --- a/test/integration/integration/tests/resources/tests/api-with-request-header-modify.yaml +++ b/test/integration/integration/tests/resources/tests/api-with-request-header-modify.yaml @@ -14,7 +14,7 @@ # specific language governing permissions and limitations # under the License. -apiVersion: dp.wso2.com/v1alpha2 +apiVersion: dp.wso2.com/v1alpha3 kind: API metadata: name: test-api-with-request-header-modify @@ -28,7 +28,7 @@ spec: #definitionFileRef: swagger-definition-webhook production: - routeRefs: - - test-api-with-request-header-modify-httproute + - test-api-with-request-header-modify-httproute organization: wso2-org --- apiVersion: gateway.networking.k8s.io/v1beta1 @@ -38,43 +38,43 @@ metadata: namespace: gateway-integration-test-infra spec: parentRefs: - - group: gateway.networking.k8s.io - kind: Gateway - name: wso2-apk-default - namespace: apk-integration-test - sectionName: httpslistener + - group: gateway.networking.k8s.io + kind: Gateway + name: wso2-apk-default + namespace: apk-integration-test + sectionName: httpslistener hostnames: - - backend-base-path.test.gw.wso2.com + - backend-base-path.test.gw.wso2.com rules: - - matches: - - path: - type: PathPrefix - value: /users - method: GET - filters: - - type: RequestHeaderModifier - requestHeaderModifier: - remove: - - X-Header-Remove - backendRefs: - - group: dp.wso2.com - kind: Backend - name: infra-backend-v1 - - matches: - - path: - type: PathPrefix - value: /orders - method: GET - filters: - - type: RequestHeaderModifier - requestHeaderModifier: - set: - - name: "test-header" - value: "test" - backendRefs: - - group: dp.wso2.com - kind: Backend - name: infra-backend-v1 + - matches: + - path: + type: PathPrefix + value: /users + method: GET + filters: + - type: RequestHeaderModifier + requestHeaderModifier: + remove: + - X-Header-Remove + backendRefs: + - group: dp.wso2.com + kind: Backend + name: infra-backend-v1 + - matches: + - path: + type: PathPrefix + value: /orders + method: GET + filters: + - type: RequestHeaderModifier + requestHeaderModifier: + set: + - name: "test-header" + value: "test" + backendRefs: + - group: dp.wso2.com + kind: Backend + name: infra-backend-v1 --- apiVersion: dp.wso2.com/v1alpha2 kind: Backend @@ -83,5 +83,5 @@ metadata: namespace: gateway-integration-test-infra spec: services: - - host: infra-backend-v1.gateway-integration-test-infra - port: 8080 + - host: infra-backend-v1.gateway-integration-test-infra + port: 8080 diff --git a/test/integration/integration/tests/resources/tests/api-with-response-header-modify.yaml b/test/integration/integration/tests/resources/tests/api-with-response-header-modify.yaml index 398e6f5020..ac7c5171bb 100644 --- a/test/integration/integration/tests/resources/tests/api-with-response-header-modify.yaml +++ b/test/integration/integration/tests/resources/tests/api-with-response-header-modify.yaml @@ -14,7 +14,7 @@ # specific language governing permissions and limitations # under the License. -apiVersion: dp.wso2.com/v1alpha2 +apiVersion: dp.wso2.com/v1alpha3 kind: API metadata: name: test-api-with-response-header-modify @@ -28,7 +28,7 @@ spec: #definitionFileRef: swagger-definition-webhook production: - routeRefs: - - test-api-with-response-header-modify-httproute + - test-api-with-response-header-modify-httproute organization: wso2-org --- apiVersion: gateway.networking.k8s.io/v1beta1 @@ -38,56 +38,56 @@ metadata: namespace: gateway-integration-test-infra spec: parentRefs: - - group: gateway.networking.k8s.io - kind: Gateway - name: wso2-apk-default - namespace: apk-integration-test - sectionName: httpslistener + - group: gateway.networking.k8s.io + kind: Gateway + name: wso2-apk-default + namespace: apk-integration-test + sectionName: httpslistener hostnames: - - gateway-integration-test-infra.test.gw.wso2.com + - gateway-integration-test-infra.test.gw.wso2.com rules: - - matches: - - path: - type: PathPrefix - value: /set - filters: - - type: ResponseHeaderModifier - responseHeaderModifier: - set: - - name: X-Header-Set - value: set-overwrites-values - backendRefs: - - group: dp.wso2.com - kind: Backend - name: infra-backend-v1 - - matches: - - path: - type: PathPrefix - value: /add - method: GET - filters: - - type: ResponseHeaderModifier - responseHeaderModifier: - set: - - name: X-Header-Add - value: add-appends-values - backendRefs: - - group: dp.wso2.com - kind: Backend - name: infra-backend-v1 - - matches: - - path: - type: PathPrefix - value: /remove - filters: - - type: ResponseHeaderModifier - responseHeaderModifier: - remove: - - X-Header-Remove - backendRefs: - - group: dp.wso2.com - kind: Backend - name: infra-backend-v1 + - matches: + - path: + type: PathPrefix + value: /set + filters: + - type: ResponseHeaderModifier + responseHeaderModifier: + set: + - name: X-Header-Set + value: set-overwrites-values + backendRefs: + - group: dp.wso2.com + kind: Backend + name: infra-backend-v1 + - matches: + - path: + type: PathPrefix + value: /add + method: GET + filters: + - type: ResponseHeaderModifier + responseHeaderModifier: + set: + - name: X-Header-Add + value: add-appends-values + backendRefs: + - group: dp.wso2.com + kind: Backend + name: infra-backend-v1 + - matches: + - path: + type: PathPrefix + value: /remove + filters: + - type: ResponseHeaderModifier + responseHeaderModifier: + remove: + - X-Header-Remove + backendRefs: + - group: dp.wso2.com + kind: Backend + name: infra-backend-v1 --- apiVersion: dp.wso2.com/v1alpha2 kind: Backend @@ -96,5 +96,5 @@ metadata: namespace: gateway-integration-test-infra spec: services: - - host: infra-backend-v1.gateway-integration-test-infra - port: 8080 + - host: infra-backend-v1.gateway-integration-test-infra + port: 8080 diff --git a/test/integration/integration/tests/resources/tests/api-without-backend-base-path.yaml b/test/integration/integration/tests/resources/tests/api-without-backend-base-path.yaml index 9926a42d50..a08f8d7740 100644 --- a/test/integration/integration/tests/resources/tests/api-without-backend-base-path.yaml +++ b/test/integration/integration/tests/resources/tests/api-without-backend-base-path.yaml @@ -14,7 +14,7 @@ # specific language governing permissions and limitations # under the License. -apiVersion: dp.wso2.com/v1alpha2 +apiVersion: dp.wso2.com/v1alpha3 kind: API metadata: name: no-backend-base-path @@ -27,7 +27,7 @@ spec: #definitionFileRef: definition-file production: - routeRefs: - - no-backend-base-path-httproute + - no-backend-base-path-httproute organization: wso2-org --- apiVersion: gateway.networking.k8s.io/v1beta1 @@ -37,23 +37,23 @@ metadata: namespace: gateway-integration-test-infra spec: parentRefs: - - group: gateway.networking.k8s.io - kind: Gateway - name: wso2-apk-default - namespace: apk-integration-test - sectionName: httpslistener + - group: gateway.networking.k8s.io + kind: Gateway + name: wso2-apk-default + namespace: apk-integration-test + sectionName: httpslistener hostnames: - - no-base-path.test.gw.wso2.com + - no-base-path.test.gw.wso2.com rules: - - matches: - - path: - type: PathPrefix - value: / - method: GET - backendRefs: - - group: dp.wso2.com - kind: Backend - name: infra-backend-v1 + - matches: + - path: + type: PathPrefix + value: / + method: GET + backendRefs: + - group: dp.wso2.com + kind: Backend + name: infra-backend-v1 --- apiVersion: dp.wso2.com/v1alpha2 kind: Backend @@ -62,5 +62,5 @@ metadata: namespace: gateway-integration-test-infra spec: services: - - host: infra-backend-v1.gateway-integration-test-infra - port: 8080 + - host: infra-backend-v1.gateway-integration-test-infra + port: 8080 diff --git a/test/integration/integration/tests/resources/tests/backend-api-key-security.yaml b/test/integration/integration/tests/resources/tests/backend-api-key-security.yaml index b28877eddf..825dea2fcf 100644 --- a/test/integration/integration/tests/resources/tests/backend-api-key-security.yaml +++ b/test/integration/integration/tests/resources/tests/backend-api-key-security.yaml @@ -14,7 +14,7 @@ # specific language governing permissions and limitations # under the License. -apiVersion: dp.wso2.com/v1alpha2 +apiVersion: dp.wso2.com/v1alpha3 kind: API metadata: name: test-api-with-api-key-backend-security @@ -85,5 +85,5 @@ spec: in: Header name: api-key valueFrom: - name: mysecret - valueKey: apiKey + name: mysecret + valueKey: apiKey diff --git a/test/integration/integration/tests/resources/tests/custom-auth-header.yaml b/test/integration/integration/tests/resources/tests/custom-auth-header.yaml index f5a27cdcbd..0fada39bf9 100644 --- a/test/integration/integration/tests/resources/tests/custom-auth-header.yaml +++ b/test/integration/integration/tests/resources/tests/custom-auth-header.yaml @@ -14,7 +14,7 @@ # specific language governing permissions and limitations # under the License. -apiVersion: dp.wso2.com/v1alpha2 +apiVersion: dp.wso2.com/v1alpha3 kind: API metadata: name: custom-auth-header @@ -28,7 +28,7 @@ spec: #definitionFileRef: definition-file production: - routeRefs: - - custom-auth-header-httproute + - custom-auth-header-httproute organization: wso2-org --- apiVersion: gateway.networking.k8s.io/v1beta1 @@ -38,22 +38,22 @@ metadata: namespace: gateway-integration-test-infra spec: parentRefs: - - group: gateway.networking.k8s.io - kind: Gateway - name: wso2-apk-default - namespace: apk-integration-test - sectionName: httpslistener + - group: gateway.networking.k8s.io + kind: Gateway + name: wso2-apk-default + namespace: apk-integration-test + sectionName: httpslistener hostnames: - - custom-auth-header.test.gw.wso2.com + - custom-auth-header.test.gw.wso2.com rules: - - matches: - - path: - type: PathPrefix - value: /v2/echo-full - backendRefs: - - group: dp.wso2.com - kind: Backend - name: infra-backend-v1 + - matches: + - path: + type: PathPrefix + value: /v2/echo-full + backendRefs: + - group: dp.wso2.com + kind: Backend + name: infra-backend-v1 --- apiVersion: dp.wso2.com/v1alpha2 kind: Authentication @@ -62,14 +62,14 @@ metadata: namespace: gateway-integration-test-infra spec: override: - disabled: false + disabled: false authTypes: oauth2: disabled: false - header: testAuth + header: testAuth jwt: disabled: false - header: testJwt + header: testJwt targetRef: group: gateway.networking.k8s.io kind: API @@ -83,5 +83,5 @@ metadata: namespace: gateway-integration-test-infra spec: services: - - host: infra-backend-v1.gateway-integration-test-infra - port: 8080 + - host: infra-backend-v1.gateway-integration-test-infra + port: 8080 diff --git a/test/integration/integration/tests/resources/tests/custom-policy-ratelimiting.yaml b/test/integration/integration/tests/resources/tests/custom-policy-ratelimiting.yaml index 1df044149b..56f1037cc4 100644 --- a/test/integration/integration/tests/resources/tests/custom-policy-ratelimiting.yaml +++ b/test/integration/integration/tests/resources/tests/custom-policy-ratelimiting.yaml @@ -39,7 +39,7 @@ metadata: spec: override: requestInterceptors: - - name: request-ratelimit-interceptor-api-level + - name: request-ratelimit-interceptor-api-level targetRef: group: gateway.networking.k8s.io kind: API @@ -58,7 +58,7 @@ spec: - request_headers - invocation_context --- -apiVersion: dp.wso2.com/v1alpha2 +apiVersion: dp.wso2.com/v1alpha3 kind: API metadata: name: http-bin-api-basic @@ -71,7 +71,7 @@ spec: #definitionFileRef: swagger-definition-http-bin-api production: - routeRefs: - - prod-http-route-http-bin-api-basic + - prod-http-route-http-bin-api-basic organization: a3b58ccf-6ecc-4557-b5bb-0a35cce38256 --- apiVersion: gateway.networking.k8s.io/v1beta1 @@ -125,5 +125,5 @@ metadata: namespace: gateway-integration-test-infra spec: services: - - host: infra-backend-v1.gateway-integration-test-infra - port: 8080 + - host: infra-backend-v1.gateway-integration-test-infra + port: 8080 diff --git a/test/integration/integration/tests/resources/tests/default-api-version-ratelimit-resource-level.yaml b/test/integration/integration/tests/resources/tests/default-api-version-ratelimit-resource-level.yaml index 2c0aa6d1fc..a5f3f786ff 100644 --- a/test/integration/integration/tests/resources/tests/default-api-version-ratelimit-resource-level.yaml +++ b/test/integration/integration/tests/resources/tests/default-api-version-ratelimit-resource-level.yaml @@ -14,7 +14,7 @@ # specific language governing permissions and limitations # under the License. -apiVersion: dp.wso2.com/v1alpha2 +apiVersion: dp.wso2.com/v1alpha3 kind: API metadata: name: default-api-version-ratelimit-resource-level @@ -28,7 +28,7 @@ spec: #definitionFileRef: definition-file production: - routeRefs: - - default-api-version-ratelimit-resource-level-httproute + - default-api-version-ratelimit-resource-level-httproute organization: wso2-org --- apiVersion: gateway.networking.k8s.io/v1beta1 @@ -38,36 +38,36 @@ metadata: namespace: gateway-integration-test-infra spec: parentRefs: - - group: gateway.networking.k8s.io - kind: Gateway - name: wso2-apk-default - namespace: apk-integration-test - sectionName: httpslistener + - group: gateway.networking.k8s.io + kind: Gateway + name: wso2-apk-default + namespace: apk-integration-test + sectionName: httpslistener hostnames: - - default-api-version-ratelimit-resource-level.test.gw.wso2.com + - default-api-version-ratelimit-resource-level.test.gw.wso2.com rules: - - matches: - - path: - type: PathPrefix - value: /v2/echo-full - backendRefs: - - group: dp.wso2.com - kind: Backend - name: infra-backend-v1 - - matches: - - path: - type: PathPrefix - value: /v2/echo - backendRefs: - - group: dp.wso2.com - kind: Backend - name: infra-backend-v1 - filters: - - type: ExtensionRef - extensionRef: - group: dp.wso2.com - kind: RateLimitPolicy - name: ratelimitter-r1 + - matches: + - path: + type: PathPrefix + value: /v2/echo-full + backendRefs: + - group: dp.wso2.com + kind: Backend + name: infra-backend-v1 + - matches: + - path: + type: PathPrefix + value: /v2/echo + backendRefs: + - group: dp.wso2.com + kind: Backend + name: infra-backend-v1 + filters: + - type: ExtensionRef + extensionRef: + group: dp.wso2.com + kind: RateLimitPolicy + name: ratelimitter-r1 --- apiVersion: dp.wso2.com/v1alpha1 kind: Backend @@ -76,8 +76,8 @@ metadata: namespace: gateway-integration-test-infra spec: services: - - host: infra-backend-v1.gateway-integration-test-infra - port: 8080 + - host: infra-backend-v1.gateway-integration-test-infra + port: 8080 --- apiVersion: dp.wso2.com/v1alpha3 kind: RateLimitPolicy diff --git a/test/integration/integration/tests/resources/tests/default-api-version-ratelimit.yaml b/test/integration/integration/tests/resources/tests/default-api-version-ratelimit.yaml index e6d2e40704..d6c180caf3 100644 --- a/test/integration/integration/tests/resources/tests/default-api-version-ratelimit.yaml +++ b/test/integration/integration/tests/resources/tests/default-api-version-ratelimit.yaml @@ -14,7 +14,7 @@ # specific language governing permissions and limitations # under the License. -apiVersion: dp.wso2.com/v1alpha2 +apiVersion: dp.wso2.com/v1alpha3 kind: API metadata: name: default-api-version-ratelimit @@ -28,7 +28,7 @@ spec: #definitionFileRef: definition-file production: - routeRefs: - - default-api-version-ratelimit-httproute + - default-api-version-ratelimit-httproute organization: wso2-org --- apiVersion: gateway.networking.k8s.io/v1beta1 @@ -38,22 +38,22 @@ metadata: namespace: gateway-integration-test-infra spec: parentRefs: - - group: gateway.networking.k8s.io - kind: Gateway - name: wso2-apk-default - namespace: apk-integration-test - sectionName: httpslistener + - group: gateway.networking.k8s.io + kind: Gateway + name: wso2-apk-default + namespace: apk-integration-test + sectionName: httpslistener hostnames: - - default-api-version-ratelimit.test.gw.wso2.com + - default-api-version-ratelimit.test.gw.wso2.com rules: - - matches: - - path: - type: PathPrefix - value: /v2/echo-full - backendRefs: - - group: dp.wso2.com - kind: Backend - name: infra-backend-v1 + - matches: + - path: + type: PathPrefix + value: /v2/echo-full + backendRefs: + - group: dp.wso2.com + kind: Backend + name: infra-backend-v1 --- apiVersion: dp.wso2.com/v1alpha3 kind: RateLimitPolicy @@ -77,5 +77,5 @@ metadata: namespace: gateway-integration-test-infra spec: services: - - host: infra-backend-v1.gateway-integration-test-infra - port: 8080 + - host: infra-backend-v1.gateway-integration-test-infra + port: 8080 diff --git a/test/integration/integration/tests/resources/tests/default-api-version.yaml b/test/integration/integration/tests/resources/tests/default-api-version.yaml index c1b44e698f..261ca925ef 100644 --- a/test/integration/integration/tests/resources/tests/default-api-version.yaml +++ b/test/integration/integration/tests/resources/tests/default-api-version.yaml @@ -14,7 +14,7 @@ # specific language governing permissions and limitations # under the License. -apiVersion: dp.wso2.com/v1alpha2 +apiVersion: dp.wso2.com/v1alpha3 kind: API metadata: name: default-api-version @@ -28,7 +28,7 @@ spec: #definitionFileRef: definition-file production: - routeRefs: - - default-api-version-httproute + - default-api-version-httproute organization: wso2-org --- apiVersion: gateway.networking.k8s.io/v1beta1 @@ -38,22 +38,22 @@ metadata: namespace: gateway-integration-test-infra spec: parentRefs: - - group: gateway.networking.k8s.io - kind: Gateway - name: wso2-apk-default - namespace: apk-integration-test - sectionName: httpslistener + - group: gateway.networking.k8s.io + kind: Gateway + name: wso2-apk-default + namespace: apk-integration-test + sectionName: httpslistener hostnames: - - default-api-version.test.gw.wso2.com + - default-api-version.test.gw.wso2.com rules: - - matches: - - path: - type: PathPrefix - value: /v2/echo-full - backendRefs: - - group: dp.wso2.com - kind: Backend - name: infra-backend-v1 + - matches: + - path: + type: PathPrefix + value: /v2/echo-full + backendRefs: + - group: dp.wso2.com + kind: Backend + name: infra-backend-v1 --- apiVersion: dp.wso2.com/v1alpha2 kind: Backend @@ -62,5 +62,5 @@ metadata: namespace: gateway-integration-test-infra spec: services: - - host: infra-backend-v1.gateway-integration-test-infra - port: 8080 + - host: infra-backend-v1.gateway-integration-test-infra + port: 8080 diff --git a/test/integration/integration/tests/resources/tests/different-endpoint-with-same-route.yaml b/test/integration/integration/tests/resources/tests/different-endpoint-with-same-route.yaml index b99241f995..173d34167b 100644 --- a/test/integration/integration/tests/resources/tests/different-endpoint-with-same-route.yaml +++ b/test/integration/integration/tests/resources/tests/different-endpoint-with-same-route.yaml @@ -14,7 +14,7 @@ # specific language governing permissions and limitations # under the License. -apiVersion: dp.wso2.com/v1alpha2 +apiVersion: dp.wso2.com/v1alpha3 kind: API metadata: name: different-endpoint-with-same-route @@ -28,7 +28,7 @@ spec: #definitionFileRef: definition-file production: - routeRefs: - - different-endpoint-with-same-route-httproute + - different-endpoint-with-same-route-httproute organization: wso2-org --- apiVersion: gateway.networking.k8s.io/v1beta1 @@ -38,32 +38,32 @@ metadata: namespace: gateway-integration-test-infra spec: parentRefs: - - group: gateway.networking.k8s.io - kind: Gateway - name: wso2-apk-default - namespace: apk-integration-test - sectionName: httpslistener + - group: gateway.networking.k8s.io + kind: Gateway + name: wso2-apk-default + namespace: apk-integration-test + sectionName: httpslistener hostnames: - - different-endpoint-with-same-route.test.gw.wso2.com + - different-endpoint-with-same-route.test.gw.wso2.com rules: - - matches: - - path: - type: PathPrefix - value: /v2/echo-full - method: GET - backendRefs: - - group: dp.wso2.com - kind: Backend - name: infra-backend-v1 - - matches: - - path: - type: PathPrefix - value: /v2/echo-full - method: POST - backendRefs: - - group: dp.wso2.com - kind: Backend - name: infra-backend-v2 + - matches: + - path: + type: PathPrefix + value: /v2/echo-full + method: GET + backendRefs: + - group: dp.wso2.com + kind: Backend + name: infra-backend-v1 + - matches: + - path: + type: PathPrefix + value: /v2/echo-full + method: POST + backendRefs: + - group: dp.wso2.com + kind: Backend + name: infra-backend-v2 --- apiVersion: dp.wso2.com/v1alpha2 kind: Backend @@ -72,8 +72,8 @@ metadata: namespace: gateway-integration-test-infra spec: services: - - host: infra-backend-v1.gateway-integration-test-infra - port: 8080 + - host: infra-backend-v1.gateway-integration-test-infra + port: 8080 --- apiVersion: dp.wso2.com/v1alpha2 kind: Backend @@ -82,5 +82,5 @@ metadata: namespace: gateway-integration-test-infra spec: services: - - host: infra-backend-v2.gateway-integration-test-infra - port: 8080 + - host: infra-backend-v2.gateway-integration-test-infra + port: 8080 diff --git a/test/integration/integration/tests/resources/tests/disable-api-level-jwt.yaml b/test/integration/integration/tests/resources/tests/disable-api-level-jwt.yaml index 6d37cb7b8d..d5e9635e47 100644 --- a/test/integration/integration/tests/resources/tests/disable-api-level-jwt.yaml +++ b/test/integration/integration/tests/resources/tests/disable-api-level-jwt.yaml @@ -14,7 +14,7 @@ # specific language governing permissions and limitations # under the License. -apiVersion: dp.wso2.com/v1alpha2 +apiVersion: dp.wso2.com/v1alpha3 kind: API metadata: name: disable-api-level-jwt @@ -28,7 +28,7 @@ spec: #definitionFileRef: definition-file production: - routeRefs: - - disable-api-level-jwt-httproute + - disable-api-level-jwt-httproute organization: wso2-org --- apiVersion: gateway.networking.k8s.io/v1beta1 @@ -38,22 +38,22 @@ metadata: namespace: gateway-integration-test-infra spec: parentRefs: - - group: gateway.networking.k8s.io - kind: Gateway - name: wso2-apk-default - namespace: apk-integration-test - sectionName: httpslistener + - group: gateway.networking.k8s.io + kind: Gateway + name: wso2-apk-default + namespace: apk-integration-test + sectionName: httpslistener hostnames: - - disable-api-level-jwt.test.gw.wso2.com + - disable-api-level-jwt.test.gw.wso2.com rules: - - matches: - - path: - type: PathPrefix - value: /v2/echo-full - backendRefs: - - group: dp.wso2.com - kind: Backend - name: infra-backend-v1 + - matches: + - path: + type: PathPrefix + value: /v2/echo-full + backendRefs: + - group: dp.wso2.com + kind: Backend + name: infra-backend-v1 --- apiVersion: dp.wso2.com/v1alpha1 kind: Authentication @@ -62,20 +62,20 @@ metadata: namespace: gateway-integration-test-infra spec: override: - disabled: false + disabled: false authTypes: apiKey: - in: Header name: x-api-key oauth2: - disabled: true + disabled: true targetRef: group: gateway.networking.k8s.io kind: API namespace: gateway-integration-test-infra name: disable-api-level-jwt --- -apiVersion: dp.wso2.com/v1alpha2 +apiVersion: dp.wso2.com/v1alpha3 kind: API metadata: name: disable-api-level-jwt1 @@ -89,7 +89,7 @@ spec: #definitionFileRef: definition-file production: - routeRefs: - - disable-api-level-jwt1-httproute + - disable-api-level-jwt1-httproute organization: wso2-org --- apiVersion: gateway.networking.k8s.io/v1beta1 @@ -99,22 +99,22 @@ metadata: namespace: gateway-integration-test-infra spec: parentRefs: - - group: gateway.networking.k8s.io - kind: Gateway - name: wso2-apk-default - namespace: apk-integration-test - sectionName: httpslistener + - group: gateway.networking.k8s.io + kind: Gateway + name: wso2-apk-default + namespace: apk-integration-test + sectionName: httpslistener hostnames: - - disable-api-level-jwt1.test.gw.wso2.com + - disable-api-level-jwt1.test.gw.wso2.com rules: - - matches: - - path: - type: PathPrefix - value: /v2/echo-full - backendRefs: - - group: dp.wso2.com - kind: Backend - name: infra-backend-v1 + - matches: + - path: + type: PathPrefix + value: /v2/echo-full + backendRefs: + - group: dp.wso2.com + kind: Backend + name: infra-backend-v1 --- apiVersion: dp.wso2.com/v1alpha1 kind: Authentication @@ -123,13 +123,13 @@ metadata: namespace: gateway-integration-test-infra spec: override: - disabled: false + disabled: false authTypes: apiKey: - in: Header name: x-api-key oauth2: - disabled: false + disabled: false targetRef: group: gateway.networking.k8s.io kind: API @@ -143,10 +143,10 @@ metadata: namespace: gateway-integration-test-infra spec: services: - - host: infra-backend-v1.gateway-integration-test-infra - port: 8080 + - host: infra-backend-v1.gateway-integration-test-infra + port: 8080 --- -apiVersion: dp.wso2.com/v1alpha2 +apiVersion: dp.wso2.com/v1alpha3 kind: API metadata: name: disable-api-level-jwt2 @@ -160,7 +160,7 @@ spec: #definitionFileRef: definition-file production: - routeRefs: - - disable-api-level-jwt2-httproute + - disable-api-level-jwt2-httproute organization: wso2-org --- apiVersion: gateway.networking.k8s.io/v1beta1 @@ -170,22 +170,22 @@ metadata: namespace: gateway-integration-test-infra spec: parentRefs: - - group: gateway.networking.k8s.io - kind: Gateway - name: wso2-apk-default - namespace: apk-integration-test - sectionName: httpslistener + - group: gateway.networking.k8s.io + kind: Gateway + name: wso2-apk-default + namespace: apk-integration-test + sectionName: httpslistener hostnames: - - disable-api-level-jwt2.test.gw.wso2.com + - disable-api-level-jwt2.test.gw.wso2.com rules: - - matches: - - path: - type: PathPrefix - value: /v2/echo-full - backendRefs: - - group: dp.wso2.com - kind: Backend - name: infra-backend-v1 + - matches: + - path: + type: PathPrefix + value: /v2/echo-full + backendRefs: + - group: dp.wso2.com + kind: Backend + name: infra-backend-v1 --- apiVersion: dp.wso2.com/v1alpha1 kind: Authentication @@ -194,10 +194,10 @@ metadata: namespace: gateway-integration-test-infra spec: override: - disabled: false + disabled: false authTypes: oauth2: - disabled: true + disabled: true targetRef: group: gateway.networking.k8s.io kind: API diff --git a/test/integration/integration/tests/resources/tests/disable-api-level-security.yaml b/test/integration/integration/tests/resources/tests/disable-api-level-security.yaml index 0ad9894d1e..28e4a5da93 100644 --- a/test/integration/integration/tests/resources/tests/disable-api-level-security.yaml +++ b/test/integration/integration/tests/resources/tests/disable-api-level-security.yaml @@ -14,7 +14,7 @@ # specific language governing permissions and limitations # under the License. -apiVersion: dp.wso2.com/v1alpha2 +apiVersion: dp.wso2.com/v1alpha3 kind: API metadata: name: disable-api-security @@ -28,7 +28,7 @@ spec: #definitionFileRef: definition-file production: - routeRefs: - - disable-api-security-httproute + - disable-api-security-httproute organization: wso2-org --- apiVersion: gateway.networking.k8s.io/v1beta1 @@ -38,32 +38,32 @@ metadata: namespace: gateway-integration-test-infra spec: parentRefs: - - group: gateway.networking.k8s.io - kind: Gateway - name: wso2-apk-default - namespace: apk-integration-test - sectionName: httpslistener + - group: gateway.networking.k8s.io + kind: Gateway + name: wso2-apk-default + namespace: apk-integration-test + sectionName: httpslistener hostnames: - - disable-api-security.test.gw.wso2.com + - disable-api-security.test.gw.wso2.com rules: - - matches: - - path: - type: Exact - value: /users - method: GET - backendRefs: - - group: dp.wso2.com - kind: Backend - name: infra-backend-v1 - - matches: - - path: - type: Exact - value: /orders - method: GET - backendRefs: - - group: dp.wso2.com - kind: Backend - name: infra-backend-v1 + - matches: + - path: + type: Exact + value: /users + method: GET + backendRefs: + - group: dp.wso2.com + kind: Backend + name: infra-backend-v1 + - matches: + - path: + type: Exact + value: /orders + method: GET + backendRefs: + - group: dp.wso2.com + kind: Backend + name: infra-backend-v1 --- apiVersion: dp.wso2.com/v1alpha1 kind: Authentication @@ -72,7 +72,7 @@ metadata: namespace: gateway-integration-test-infra spec: override: - disabled: true + disabled: true targetRef: group: gateway.networking.k8s.io kind: API @@ -86,5 +86,5 @@ metadata: namespace: gateway-integration-test-infra spec: services: - - host: infra-backend-v1.gateway-integration-test-infra - port: 8080 \ No newline at end of file + - host: infra-backend-v1.gateway-integration-test-infra + port: 8080 diff --git a/test/integration/integration/tests/resources/tests/disable-resource-level-jwt.yaml b/test/integration/integration/tests/resources/tests/disable-resource-level-jwt.yaml index b29a6207fd..e140490bc7 100644 --- a/test/integration/integration/tests/resources/tests/disable-resource-level-jwt.yaml +++ b/test/integration/integration/tests/resources/tests/disable-resource-level-jwt.yaml @@ -14,7 +14,7 @@ # specific language governing permissions and limitations # under the License. -apiVersion: dp.wso2.com/v1alpha2 +apiVersion: dp.wso2.com/v1alpha3 kind: API metadata: name: disable-resource-level-jwt @@ -28,7 +28,7 @@ spec: #definitionFileRef: definition-file production: - routeRefs: - - disable-resource-level-jwt-httproute + - disable-resource-level-jwt-httproute organization: wso2-org --- apiVersion: gateway.networking.k8s.io/v1beta1 @@ -38,28 +38,28 @@ metadata: namespace: gateway-integration-test-infra spec: parentRefs: - - group: gateway.networking.k8s.io - kind: Gateway - name: wso2-apk-default - namespace: apk-integration-test - sectionName: httpslistener + - group: gateway.networking.k8s.io + kind: Gateway + name: wso2-apk-default + namespace: apk-integration-test + sectionName: httpslistener hostnames: - - disable-resource-level-jwt.test.gw.wso2.com + - disable-resource-level-jwt.test.gw.wso2.com rules: - - matches: - - path: - type: PathPrefix - value: /v2/echo-full - backendRefs: - - group: dp.wso2.com - kind: Backend - name: infra-backend-v1 - filters: - - type: ExtensionRef - extensionRef: - group: dp.wso2.com - kind: Authentication - name: disable-resource-level-jwt-authentication + - matches: + - path: + type: PathPrefix + value: /v2/echo-full + backendRefs: + - group: dp.wso2.com + kind: Backend + name: infra-backend-v1 + filters: + - type: ExtensionRef + extensionRef: + group: dp.wso2.com + kind: Authentication + name: disable-resource-level-jwt-authentication --- apiVersion: dp.wso2.com/v1alpha1 kind: Authentication @@ -81,7 +81,7 @@ spec: group: gateway.networking.k8s.io namespace: gateway-integration-test-infra --- -apiVersion: dp.wso2.com/v1alpha2 +apiVersion: dp.wso2.com/v1alpha3 kind: API metadata: name: disable-resource-level-jwt1 @@ -95,7 +95,7 @@ spec: #definitionFileRef: definition-file production: - routeRefs: - - disable-resource-level-jwt1-httproute + - disable-resource-level-jwt1-httproute organization: wso2-org --- apiVersion: gateway.networking.k8s.io/v1beta1 @@ -105,28 +105,28 @@ metadata: namespace: gateway-integration-test-infra spec: parentRefs: - - group: gateway.networking.k8s.io - kind: Gateway - name: wso2-apk-default - namespace: apk-integration-test - sectionName: httpslistener + - group: gateway.networking.k8s.io + kind: Gateway + name: wso2-apk-default + namespace: apk-integration-test + sectionName: httpslistener hostnames: - - disable-resource-level-jwt1.test.gw.wso2.com + - disable-resource-level-jwt1.test.gw.wso2.com rules: - - matches: - - path: - type: PathPrefix - value: /v2/echo-full - backendRefs: - - group: dp.wso2.com - kind: Backend - name: infra-backend-v1 - filters: - - type: ExtensionRef - extensionRef: - group: dp.wso2.com - kind: Authentication - name: disable-resource-level-jwt1-authentication + - matches: + - path: + type: PathPrefix + value: /v2/echo-full + backendRefs: + - group: dp.wso2.com + kind: Backend + name: infra-backend-v1 + filters: + - type: ExtensionRef + extensionRef: + group: dp.wso2.com + kind: Authentication + name: disable-resource-level-jwt1-authentication --- apiVersion: dp.wso2.com/v1alpha1 kind: Authentication @@ -155,10 +155,10 @@ metadata: namespace: gateway-integration-test-infra spec: services: - - host: infra-backend-v1.gateway-integration-test-infra - port: 8080 + - host: infra-backend-v1.gateway-integration-test-infra + port: 8080 --- -apiVersion: dp.wso2.com/v1alpha2 +apiVersion: dp.wso2.com/v1alpha3 kind: API metadata: name: disable-resource-level-jwt2 @@ -172,7 +172,7 @@ spec: #definitionFileRef: definition-file production: - routeRefs: - - disable-resource-level-jwt2-httproute + - disable-resource-level-jwt2-httproute organization: wso2-org --- apiVersion: gateway.networking.k8s.io/v1beta1 @@ -182,28 +182,28 @@ metadata: namespace: gateway-integration-test-infra spec: parentRefs: - - group: gateway.networking.k8s.io - kind: Gateway - name: wso2-apk-default - namespace: apk-integration-test - sectionName: httpslistener + - group: gateway.networking.k8s.io + kind: Gateway + name: wso2-apk-default + namespace: apk-integration-test + sectionName: httpslistener hostnames: - - disable-resource-level-jwt2.test.gw.wso2.com + - disable-resource-level-jwt2.test.gw.wso2.com rules: - - matches: - - path: - type: PathPrefix - value: /v2/echo-full - backendRefs: - - group: dp.wso2.com - kind: Backend - name: infra-backend-v1 - filters: - - type: ExtensionRef - extensionRef: - group: dp.wso2.com - kind: Authentication - name: disable-resource-level-jwt2-authentication + - matches: + - path: + type: PathPrefix + value: /v2/echo-full + backendRefs: + - group: dp.wso2.com + kind: Backend + name: infra-backend-v1 + filters: + - type: ExtensionRef + extensionRef: + group: dp.wso2.com + kind: Authentication + name: disable-resource-level-jwt2-authentication --- apiVersion: dp.wso2.com/v1alpha1 kind: Authentication diff --git a/test/integration/integration/tests/resources/tests/disable-resource-level-security.yaml b/test/integration/integration/tests/resources/tests/disable-resource-level-security.yaml index 71c706d100..380f6a2615 100644 --- a/test/integration/integration/tests/resources/tests/disable-resource-level-security.yaml +++ b/test/integration/integration/tests/resources/tests/disable-resource-level-security.yaml @@ -14,7 +14,7 @@ # specific language governing permissions and limitations # under the License. -apiVersion: dp.wso2.com/v1alpha2 +apiVersion: dp.wso2.com/v1alpha3 kind: API metadata: name: disable-resoruce-security @@ -28,7 +28,7 @@ spec: #definitionFileRef: definition-file production: - routeRefs: - - disable-resoruce-security-httproute + - disable-resoruce-security-httproute organization: wso2-org --- apiVersion: gateway.networking.k8s.io/v1beta1 @@ -38,38 +38,38 @@ metadata: namespace: gateway-integration-test-infra spec: parentRefs: - - group: gateway.networking.k8s.io - kind: Gateway - name: wso2-apk-default - namespace: apk-integration-test - sectionName: httpslistener + - group: gateway.networking.k8s.io + kind: Gateway + name: wso2-apk-default + namespace: apk-integration-test + sectionName: httpslistener hostnames: - - disable-resource-security.test.gw.wso2.com + - disable-resource-security.test.gw.wso2.com rules: - - matches: - - path: - type: Exact - value: /users - method: GET - filters: - - type: ExtensionRef - extensionRef: - group: dp.wso2.com - kind: Authentication - name: disable-resource-security - backendRefs: - - group: dp.wso2.com - kind: Backend - name: infra-backend-v1 - - matches: - - path: - type: Exact - value: /orders - method: GET - backendRefs: - - group: dp.wso2.com - kind: Backend - name: infra-backend-v1 + - matches: + - path: + type: Exact + value: /users + method: GET + filters: + - type: ExtensionRef + extensionRef: + group: dp.wso2.com + kind: Authentication + name: disable-resource-security + backendRefs: + - group: dp.wso2.com + kind: Backend + name: infra-backend-v1 + - matches: + - path: + type: Exact + value: /orders + method: GET + backendRefs: + - group: dp.wso2.com + kind: Backend + name: infra-backend-v1 --- apiVersion: dp.wso2.com/v1alpha1 kind: Authentication @@ -78,7 +78,7 @@ metadata: namespace: gateway-integration-test-infra spec: override: - disabled: true + disabled: true targetRef: group: gateway.networking.k8s.io kind: Resource @@ -92,5 +92,5 @@ metadata: namespace: gateway-integration-test-infra spec: services: - - host: infra-backend-v1.gateway-integration-test-infra - port: 8080 + - host: infra-backend-v1.gateway-integration-test-infra + port: 8080 diff --git a/test/integration/integration/tests/resources/tests/fetch-api-definition.yaml b/test/integration/integration/tests/resources/tests/fetch-api-definition.yaml index 04d3d8ef6d..ec0a934417 100644 --- a/test/integration/integration/tests/resources/tests/fetch-api-definition.yaml +++ b/test/integration/integration/tests/resources/tests/fetch-api-definition.yaml @@ -14,7 +14,7 @@ # specific language governing permissions and limitations # under the License. -apiVersion: dp.wso2.com/v1alpha2 +apiVersion: dp.wso2.com/v1alpha3 kind: API metadata: name: fetch-api-definition @@ -28,7 +28,7 @@ spec: definitionFileRef: definition-file-swagger production: - routeRefs: - - fetch-api-defintion-httproute + - fetch-api-defintion-httproute organization: wso2-org --- apiVersion: gateway.networking.k8s.io/v1beta1 @@ -38,28 +38,28 @@ metadata: namespace: gateway-integration-test-infra spec: parentRefs: - - group: gateway.networking.k8s.io - kind: Gateway - name: wso2-apk-default - namespace: apk-integration-test - sectionName: httpslistener + - group: gateway.networking.k8s.io + kind: Gateway + name: wso2-apk-default + namespace: apk-integration-test + sectionName: httpslistener hostnames: - - fetch-api-definition.test.gw.wso2.com + - fetch-api-definition.test.gw.wso2.com rules: - - matches: - - path: - type: PathPrefix - value: /fetch-api-definitions/v1.0.0 - filters: - - type: URLRewrite - urlRewrite: - path: - type: ReplacePrefixMatch - replacePrefixMatch: /fetch-api-definition/v1.0.0/gw.wso2.com - backendRefs: - - group: dp.wso2.com - kind: Backend - name: infra-backend-v1 + - matches: + - path: + type: PathPrefix + value: /fetch-api-definitions/v1.0.0 + filters: + - type: URLRewrite + urlRewrite: + path: + type: ReplacePrefixMatch + replacePrefixMatch: /fetch-api-definition/v1.0.0/gw.wso2.com + backendRefs: + - group: dp.wso2.com + kind: Backend + name: infra-backend-v1 --- apiVersion: v1 kind: ConfigMap @@ -76,5 +76,5 @@ metadata: namespace: gateway-integration-test-infra spec: services: - - host: infra-backend-v1.gateway-integration-test-infra - port: 8080 + - host: infra-backend-v1.gateway-integration-test-infra + port: 8080 diff --git a/test/integration/integration/tests/resources/tests/fetch-non-existing-api-definition.yaml b/test/integration/integration/tests/resources/tests/fetch-non-existing-api-definition.yaml index a1ce6f781f..cf119584aa 100644 --- a/test/integration/integration/tests/resources/tests/fetch-non-existing-api-definition.yaml +++ b/test/integration/integration/tests/resources/tests/fetch-non-existing-api-definition.yaml @@ -14,7 +14,7 @@ # specific language governing permissions and limitations # under the License. -apiVersion: dp.wso2.com/v1alpha2 +apiVersion: dp.wso2.com/v1alpha3 kind: API metadata: name: fetch-non-api-definition @@ -26,7 +26,7 @@ spec: basePath: /fetch-non-existing-api-definition/v1.0.0 production: - routeRefs: - - fetch-non-api-definition-httproute + - fetch-non-api-definition-httproute organization: wso2-org --- apiVersion: gateway.networking.k8s.io/v1beta1 @@ -36,28 +36,28 @@ metadata: namespace: gateway-integration-test-infra spec: parentRefs: - - group: gateway.networking.k8s.io - kind: Gateway - name: wso2-apk-default - namespace: apk-integration-test - sectionName: httpslistener + - group: gateway.networking.k8s.io + kind: Gateway + name: wso2-apk-default + namespace: apk-integration-test + sectionName: httpslistener hostnames: - - fetch-non-api-definition.test.gw.wso2.com + - fetch-non-api-definition.test.gw.wso2.com rules: - - matches: - - path: - type: PathPrefix - value: /fetch-non-api-definitions/v1.0.0 - filters: - - type: URLRewrite - urlRewrite: - path: - type: ReplacePrefixMatch - replacePrefixMatch: /fetch-non-api-definitions/v1.0.0/gw.wso2.com - backendRefs: - - group: dp.wso2.com - kind: Backend - name: infra-backend-v1 + - matches: + - path: + type: PathPrefix + value: /fetch-non-api-definitions/v1.0.0 + filters: + - type: URLRewrite + urlRewrite: + path: + type: ReplacePrefixMatch + replacePrefixMatch: /fetch-non-api-definitions/v1.0.0/gw.wso2.com + backendRefs: + - group: dp.wso2.com + kind: Backend + name: infra-backend-v1 --- apiVersion: dp.wso2.com/v1alpha2 kind: Backend @@ -66,5 +66,5 @@ metadata: namespace: gateway-integration-test-infra spec: services: - - host: infra-backend-v1.gateway-integration-test-infra - port: 8080 + - host: infra-backend-v1.gateway-integration-test-infra + port: 8080 diff --git a/test/integration/integration/tests/resources/tests/gql-api.yaml b/test/integration/integration/tests/resources/tests/gql-api.yaml index c5ac136d18..bb963b302c 100644 --- a/test/integration/integration/tests/resources/tests/gql-api.yaml +++ b/test/integration/integration/tests/resources/tests/gql-api.yaml @@ -14,7 +14,7 @@ # specific language governing permissions and limitations # under the License. -apiVersion: dp.wso2.com/v1alpha2 +apiVersion: dp.wso2.com/v1alpha3 kind: API metadata: name: gql-api @@ -28,7 +28,7 @@ spec: definitionFileRef: gql-sdl production: - routeRefs: - - gql-api-gqlroute + - gql-api-gqlroute organization: wso2-org --- apiVersion: dp.wso2.com/v1alpha2 @@ -38,47 +38,47 @@ metadata: namespace: gateway-integration-test-infra spec: parentRefs: - - group: gateway.networking.k8s.io - kind: Gateway - name: wso2-apk-default - namespace: apk-integration-test - sectionName: httpslistener + - group: gateway.networking.k8s.io + kind: Gateway + name: wso2-apk-default + namespace: apk-integration-test + sectionName: httpslistener hostnames: - - gql.test.gw.wso2.com + - gql.test.gw.wso2.com backendRefs: - group: dp.wso2.com kind: Backend name: gql-backend-v1 rules: - - matches: - - path: hero - type: QUERY - - path: droid - type: QUERY - - path: reviews - type: QUERY - - path: search - type: QUERY - - path: character - type: QUERY - - path: allHumans - type: QUERY - - path: allDroids - type: QUERY - - path: allCharacters - type: QUERY - - path: starship - type: QUERY - - path: createReview - type: MUTATION - - matches: - - path: human - type: QUERY - filters: - - extensionRef: - group: dp.wso2.com - kind: Authentication - name: disable-gql-api-security + - matches: + - path: hero + type: QUERY + - path: droid + type: QUERY + - path: reviews + type: QUERY + - path: search + type: QUERY + - path: character + type: QUERY + - path: allHumans + type: QUERY + - path: allDroids + type: QUERY + - path: allCharacters + type: QUERY + - path: starship + type: QUERY + - path: createReview + type: MUTATION + - matches: + - path: human + type: QUERY + filters: + - extensionRef: + group: dp.wso2.com + kind: Authentication + name: disable-gql-api-security --- apiVersion: dp.wso2.com/v1alpha1 kind: Authentication @@ -87,7 +87,7 @@ metadata: namespace: gateway-integration-test-infra spec: override: - disabled: true + disabled: true targetRef: group: gateway.networking.k8s.io kind: Resource @@ -102,8 +102,8 @@ metadata: spec: basePath: /graphql services: - - host: gql-backend-v1.gateway-integration-test-infra - port: 8080 + - host: gql-backend-v1.gateway-integration-test-infra + port: 8080 --- apiVersion: v1 kind: ConfigMap diff --git a/test/integration/integration/tests/resources/tests/interceptors-api-level.yaml b/test/integration/integration/tests/resources/tests/interceptors-api-level.yaml index 3c58fe9d56..f282fd96d0 100644 --- a/test/integration/integration/tests/resources/tests/interceptors-api-level.yaml +++ b/test/integration/integration/tests/resources/tests/interceptors-api-level.yaml @@ -14,7 +14,7 @@ # specific language governing permissions and limitations # under the License. -apiVersion: dp.wso2.com/v1alpha2 +apiVersion: dp.wso2.com/v1alpha3 kind: API metadata: name: api-interceptor-test-api @@ -27,7 +27,7 @@ spec: #definitionFileRef: swagger-definition-webhook production: - routeRefs: - - api-interceptor-route + - api-interceptor-route organization: wso2-org apiProperties: - name: propertyName1 @@ -42,29 +42,29 @@ metadata: namespace: gateway-integration-test-infra spec: hostnames: - - interceptor-api.test.gw.wso2.com + - interceptor-api.test.gw.wso2.com parentRefs: - - group: gateway.networking.k8s.io - kind: Gateway - name: wso2-apk-default - namespace: apk-integration-test - sectionName: httpslistener + - group: gateway.networking.k8s.io + kind: Gateway + name: wso2-apk-default + namespace: apk-integration-test + sectionName: httpslistener rules: - - backendRefs: - - group: dp.wso2.com - kind: Backend - name: legacy-xml-backend-v1 - matches: - - path: - type: RegularExpression - value: /interceptor/2.0.0/books - method: POST - filters: - - type: URLRewrite - urlRewrite: - path: - type: ReplaceFullPath - replaceFullPath: /books + - backendRefs: + - group: dp.wso2.com + kind: Backend + name: legacy-xml-backend-v1 + matches: + - path: + type: RegularExpression + value: /interceptor/2.0.0/books + method: POST + filters: + - type: URLRewrite + urlRewrite: + path: + type: ReplaceFullPath + replaceFullPath: /books --- apiVersion: dp.wso2.com/v1alpha3 kind: APIPolicy @@ -74,9 +74,9 @@ metadata: spec: override: requestInterceptors: - - name: request-interceptor-api-level + - name: request-interceptor-api-level responseInterceptors: - - name: response-interceptor-api-level + - name: response-interceptor-api-level targetRef: group: gateway.networking.k8s.io kind: API @@ -89,9 +89,9 @@ metadata: namespace: gateway-integration-test-infra spec: backendRef: - name: interceptor-backend-v1 + name: interceptor-backend-v1 includes: - - request_body + - request_body --- apiVersion: dp.wso2.com/v1alpha1 kind: InterceptorService diff --git a/test/integration/integration/tests/resources/tests/interceptors-resource-level.yaml b/test/integration/integration/tests/resources/tests/interceptors-resource-level.yaml index f6c3626346..527480e2a1 100644 --- a/test/integration/integration/tests/resources/tests/interceptors-resource-level.yaml +++ b/test/integration/integration/tests/resources/tests/interceptors-resource-level.yaml @@ -14,7 +14,7 @@ # specific language governing permissions and limitations # under the License. -apiVersion: dp.wso2.com/v1alpha2 +apiVersion: dp.wso2.com/v1alpha3 kind: API metadata: name: resource-interceptor-test-api @@ -27,7 +27,7 @@ spec: #definitionFileRef: swagger-definition-webhook production: - routeRefs: - - resource-interceptor-route + - resource-interceptor-route organization: wso2-org --- apiVersion: gateway.networking.k8s.io/v1beta1 @@ -37,49 +37,49 @@ metadata: namespace: gateway-integration-test-infra spec: hostnames: - - interceptor-resource.test.gw.wso2.com + - interceptor-resource.test.gw.wso2.com parentRefs: - - group: gateway.networking.k8s.io - kind: Gateway - name: wso2-apk-default - namespace: apk-integration-test - sectionName: httpslistener + - group: gateway.networking.k8s.io + kind: Gateway + name: wso2-apk-default + namespace: apk-integration-test + sectionName: httpslistener rules: - - backendRefs: - - group: dp.wso2.com - kind: Backend - name: legacy-xml-backend-v1 - matches: - - path: - type: RegularExpression - value: /interceptor/1.0.0/books/with-interceptors - method: POST - filters: - - type: URLRewrite - urlRewrite: - path: - type: ReplaceFullPath - replaceFullPath: /books - - type: ExtensionRef - extensionRef: - group: dp.wso2.com - kind: APIPolicy - name: interceptor-api-policy-resource-level - - backendRefs: - - group: dp.wso2.com - kind: Backend - name: legacy-xml-backend-v1 - matches: - - path: - type: RegularExpression - value: /interceptor/1.0.0/books/without-interceptors - method: POST - filters: - - type: URLRewrite - urlRewrite: - path: - type: ReplaceFullPath - replaceFullPath: /books + - backendRefs: + - group: dp.wso2.com + kind: Backend + name: legacy-xml-backend-v1 + matches: + - path: + type: RegularExpression + value: /interceptor/1.0.0/books/with-interceptors + method: POST + filters: + - type: URLRewrite + urlRewrite: + path: + type: ReplaceFullPath + replaceFullPath: /books + - type: ExtensionRef + extensionRef: + group: dp.wso2.com + kind: APIPolicy + name: interceptor-api-policy-resource-level + - backendRefs: + - group: dp.wso2.com + kind: Backend + name: legacy-xml-backend-v1 + matches: + - path: + type: RegularExpression + value: /interceptor/1.0.0/books/without-interceptors + method: POST + filters: + - type: URLRewrite + urlRewrite: + path: + type: ReplaceFullPath + replaceFullPath: /books --- apiVersion: dp.wso2.com/v1alpha3 kind: APIPolicy @@ -89,9 +89,9 @@ metadata: spec: override: requestInterceptors: - - name: request-interceptor-api-level + - name: request-interceptor-api-level responseInterceptors: - - name: response-interceptor-api-level + - name: response-interceptor-api-level targetRef: group: gateway.networking.k8s.io kind: Resource @@ -106,8 +106,8 @@ spec: backendRef: name: interceptor-backend-v1 includes: - - request_body - - request_headers + - request_body + - request_headers --- apiVersion: dp.wso2.com/v1alpha1 kind: InterceptorService diff --git a/test/integration/integration/tests/resources/tests/jwt-api-level-security.yaml b/test/integration/integration/tests/resources/tests/jwt-api-level-security.yaml index d62ed16417..8a4de30aef 100644 --- a/test/integration/integration/tests/resources/tests/jwt-api-level-security.yaml +++ b/test/integration/integration/tests/resources/tests/jwt-api-level-security.yaml @@ -14,7 +14,7 @@ # specific language governing permissions and limitations # under the License. -apiVersion: dp.wso2.com/v1alpha2 +apiVersion: dp.wso2.com/v1alpha3 kind: API metadata: name: api-level-jwt @@ -28,7 +28,7 @@ spec: #definitionFileRef: definition-file production: - routeRefs: - - api-level-jwt-httproute + - api-level-jwt-httproute organization: wso2-org --- apiVersion: gateway.networking.k8s.io/v1beta1 @@ -38,22 +38,22 @@ metadata: namespace: gateway-integration-test-infra spec: parentRefs: - - group: gateway.networking.k8s.io - kind: Gateway - name: wso2-apk-default - namespace: apk-integration-test - sectionName: httpslistener + - group: gateway.networking.k8s.io + kind: Gateway + name: wso2-apk-default + namespace: apk-integration-test + sectionName: httpslistener hostnames: - - api-level-jwt.test.gw.wso2.com + - api-level-jwt.test.gw.wso2.com rules: - - matches: - - path: - type: PathPrefix - value: /v2/echo-full - backendRefs: - - group: dp.wso2.com - kind: Backend - name: infra-backend-v1 + - matches: + - path: + type: PathPrefix + value: /v2/echo-full + backendRefs: + - group: dp.wso2.com + kind: Backend + name: infra-backend-v1 --- apiVersion: dp.wso2.com/v1alpha2 kind: Authentication @@ -62,10 +62,10 @@ metadata: namespace: gateway-integration-test-infra spec: override: - disabled: false + disabled: false authTypes: jwt: - disabled: false + disabled: false targetRef: group: gateway.networking.k8s.io kind: API @@ -79,5 +79,5 @@ metadata: namespace: gateway-integration-test-infra spec: services: - - host: infra-backend-v1.gateway-integration-test-infra - port: 8080 + - host: infra-backend-v1.gateway-integration-test-infra + port: 8080 diff --git a/test/integration/integration/tests/resources/tests/jwt-resource-level-security.yaml b/test/integration/integration/tests/resources/tests/jwt-resource-level-security.yaml index 5f2cb2da51..5bd064f5d4 100644 --- a/test/integration/integration/tests/resources/tests/jwt-resource-level-security.yaml +++ b/test/integration/integration/tests/resources/tests/jwt-resource-level-security.yaml @@ -14,7 +14,7 @@ # specific language governing permissions and limitations # under the License. -apiVersion: dp.wso2.com/v1alpha2 +apiVersion: dp.wso2.com/v1alpha3 kind: API metadata: name: resource-level-jwt @@ -28,7 +28,7 @@ spec: #definitionFileRef: definition-file production: - routeRefs: - - resource-level-jwt-httproute + - resource-level-jwt-httproute organization: wso2-org --- apiVersion: gateway.networking.k8s.io/v1beta1 @@ -38,56 +38,56 @@ metadata: namespace: gateway-integration-test-infra spec: parentRefs: - - group: gateway.networking.k8s.io - kind: Gateway - name: wso2-apk-default - namespace: apk-integration-test - sectionName: httpslistener + - group: gateway.networking.k8s.io + kind: Gateway + name: wso2-apk-default + namespace: apk-integration-test + sectionName: httpslistener hostnames: - - resource-level-jwt.test.gw.wso2.com + - resource-level-jwt.test.gw.wso2.com rules: - - matches: - - path: - type: PathPrefix - value: /v2/echo-full - backendRefs: - - group: dp.wso2.com - kind: Backend - name: infra-backend-v1 - filters: - - type: ExtensionRef - extensionRef: - group: dp.wso2.com - kind: Authentication - name: resource-level-jwt-authentication - - matches: - - path: - type: PathPrefix - value: /v2/echo-1 - backendRefs: - - group: dp.wso2.com - kind: Backend - name: infra-backend-v1 - filters: - - type: ExtensionRef - extensionRef: - group: dp.wso2.com - kind: Authentication - name: resource-level-jwt-authentication-1 - - matches: - - path: - type: PathPrefix - value: /v2/echo-2 - backendRefs: - - group: dp.wso2.com - kind: Backend - name: infra-backend-v1 - filters: - - type: ExtensionRef - extensionRef: - group: dp.wso2.com - kind: Authentication - name: resource-level-jwt-authentication-2 + - matches: + - path: + type: PathPrefix + value: /v2/echo-full + backendRefs: + - group: dp.wso2.com + kind: Backend + name: infra-backend-v1 + filters: + - type: ExtensionRef + extensionRef: + group: dp.wso2.com + kind: Authentication + name: resource-level-jwt-authentication + - matches: + - path: + type: PathPrefix + value: /v2/echo-1 + backendRefs: + - group: dp.wso2.com + kind: Backend + name: infra-backend-v1 + filters: + - type: ExtensionRef + extensionRef: + group: dp.wso2.com + kind: Authentication + name: resource-level-jwt-authentication-1 + - matches: + - path: + type: PathPrefix + value: /v2/echo-2 + backendRefs: + - group: dp.wso2.com + kind: Backend + name: infra-backend-v1 + filters: + - type: ExtensionRef + extensionRef: + group: dp.wso2.com + kind: Authentication + name: resource-level-jwt-authentication-2 --- apiVersion: dp.wso2.com/v1alpha2 kind: Authentication @@ -151,5 +151,5 @@ metadata: namespace: gateway-integration-test-infra spec: services: - - host: infra-backend-v1.gateway-integration-test-infra - port: 8080 + - host: infra-backend-v1.gateway-integration-test-infra + port: 8080 diff --git a/test/integration/integration/tests/resources/tests/multiple_port_listener.yaml b/test/integration/integration/tests/resources/tests/multiple_port_listener.yaml index a9d49886d9..5e50136ed5 100644 --- a/test/integration/integration/tests/resources/tests/multiple_port_listener.yaml +++ b/test/integration/integration/tests/resources/tests/multiple_port_listener.yaml @@ -14,7 +14,7 @@ # specific language governing permissions and limitations # under the License. -apiVersion: dp.wso2.com/v1alpha2 +apiVersion: dp.wso2.com/v1alpha3 kind: API metadata: name: test-multiple-port-listener @@ -27,10 +27,10 @@ spec: #definitionFileRef: definition-file production: - routeRefs: - - test-api-with-multiple-port-listener-httproute + - test-api-with-multiple-port-listener-httproute organization: wso2-org --- -apiVersion: dp.wso2.com/v1alpha2 +apiVersion: dp.wso2.com/v1alpha3 kind: API metadata: name: test-multiple-port-listener-1 @@ -43,7 +43,7 @@ spec: #definitionFileRef: definition-file production: - routeRefs: - - test-api-with-multiple-port-listener-httproute + - test-api-with-multiple-port-listener-httproute organization: wso2-org --- apiVersion: gateway.networking.k8s.io/v1beta1 @@ -53,29 +53,29 @@ metadata: namespace: gateway-integration-test-infra spec: parentRefs: - - group: gateway.networking.k8s.io - kind: Gateway - name: wso2-apk-default - namespace: apk-integration-test - sectionName: httplistener + - group: gateway.networking.k8s.io + kind: Gateway + name: wso2-apk-default + namespace: apk-integration-test + sectionName: httplistener hostnames: - - api.am.wso2.com + - api.am.wso2.com rules: - - matches: - - path: - type: RegularExpression - value: /user/([^/]+)/playlist/([^/]+) - method: GET - filters: - - type: URLRewrite - urlRewrite: - path: - type: ReplaceFullPath - replaceFullPath: /user/\1/playlist/\2 - backendRefs: - - group: dp.wso2.com - kind: Backend - name: infra-backend-v1 + - matches: + - path: + type: RegularExpression + value: /user/([^/]+)/playlist/([^/]+) + method: GET + filters: + - type: URLRewrite + urlRewrite: + path: + type: ReplaceFullPath + replaceFullPath: /user/\1/playlist/\2 + backendRefs: + - group: dp.wso2.com + kind: Backend + name: infra-backend-v1 --- apiVersion: dp.wso2.com/v1alpha2 kind: Backend @@ -84,5 +84,5 @@ metadata: namespace: gateway-integration-test-infra spec: services: - - host: infra-backend-v1.gateway-integration-test-infra - port: 8080 + - host: infra-backend-v1.gateway-integration-test-infra + port: 8080 diff --git a/test/integration/integration/tests/resources/tests/prod-and-sand-apis.yaml b/test/integration/integration/tests/resources/tests/prod-and-sand-apis.yaml index 3391abd987..211f6685b0 100644 --- a/test/integration/integration/tests/resources/tests/prod-and-sand-apis.yaml +++ b/test/integration/integration/tests/resources/tests/prod-and-sand-apis.yaml @@ -14,7 +14,7 @@ # specific language governing permissions and limitations # under the License. -apiVersion: dp.wso2.com/v1alpha2 +apiVersion: dp.wso2.com/v1alpha3 kind: API metadata: name: prod-and-sand-apis @@ -27,7 +27,7 @@ spec: #definitionFileRef: definition-file production: - routeRefs: - - prod-httproute + - prod-httproute organization: wso2-org --- apiVersion: gateway.networking.k8s.io/v1 @@ -37,23 +37,23 @@ metadata: namespace: gateway-integration-test-infra spec: parentRefs: - - group: gateway.networking.k8s.io - kind: Gateway - name: wso2-apk-default - namespace: apk-integration-test - sectionName: httpslistener + - group: gateway.networking.k8s.io + kind: Gateway + name: wso2-apk-default + namespace: apk-integration-test + sectionName: httpslistener hostnames: - - prod-api.test.gw.wso2.com + - prod-api.test.gw.wso2.com rules: - - matches: - - path: - type: PathPrefix - value: / - method: GET - backendRefs: - - group: dp.wso2.com - kind: Backend - name: infra-backend-v1 + - matches: + - path: + type: PathPrefix + value: / + method: GET + backendRefs: + - group: dp.wso2.com + kind: Backend + name: infra-backend-v1 --- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute @@ -62,24 +62,24 @@ metadata: namespace: gateway-integration-test-infra spec: parentRefs: - - group: gateway.networking.k8s.io - kind: Gateway - name: wso2-apk-default - namespace: apk-integration-test - sectionName: httpslistener + - group: gateway.networking.k8s.io + kind: Gateway + name: wso2-apk-default + namespace: apk-integration-test + sectionName: httpslistener hostnames: - - sand-api.test.gw.wso2.com + - sand-api.test.gw.wso2.com rules: - - matches: - - path: - type: PathPrefix - value: / - method: GET - - backendRefs: - - group: dp.wso2.com - kind: Backend - name: infra-backend-v2 + - matches: + - path: + type: PathPrefix + value: / + method: GET + + backendRefs: + - group: dp.wso2.com + kind: Backend + name: infra-backend-v2 --- apiVersion: dp.wso2.com/v1alpha1 kind: Authentication @@ -88,7 +88,7 @@ metadata: namespace: gateway-integration-test-infra spec: override: - disabled: true + disabled: true targetRef: group: gateway.networking.k8s.io kind: API @@ -102,8 +102,8 @@ metadata: namespace: gateway-integration-test-infra spec: services: - - host: infra-backend-v1.gateway-integration-test-infra - port: 8080 + - host: infra-backend-v1.gateway-integration-test-infra + port: 8080 --- apiVersion: dp.wso2.com/v1alpha2 kind: Backend @@ -112,11 +112,11 @@ metadata: namespace: gateway-integration-test-infra spec: services: - - host: infra-backend-v2.gateway-integration-test-infra - port: 8080 + - host: infra-backend-v2.gateway-integration-test-infra + port: 8080 --- # To test whether we can successfully update already existing API with sandbox endpoints. -apiVersion: dp.wso2.com/v1alpha2 +apiVersion: dp.wso2.com/v1alpha3 kind: API metadata: name: prod-and-sand-apis @@ -129,8 +129,8 @@ spec: #definitionFileRef: definition-file production: - routeRefs: - - prod-httproute + - prod-httproute sandbox: - routeRefs: - - sand-httproute + - sand-httproute organization: wso2-org diff --git a/test/integration/integration/tests/resources/tests/ratelimit-priority.yaml b/test/integration/integration/tests/resources/tests/ratelimit-priority.yaml index 30dab71549..3792f9519c 100644 --- a/test/integration/integration/tests/resources/tests/ratelimit-priority.yaml +++ b/test/integration/integration/tests/resources/tests/ratelimit-priority.yaml @@ -14,7 +14,7 @@ # specific language governing permissions and limitations # under the License. -apiVersion: dp.wso2.com/v1alpha2 +apiVersion: dp.wso2.com/v1alpha3 kind: API metadata: name: ratelimit-priority @@ -28,7 +28,7 @@ spec: #definitionFileRef: definition-file production: - routeRefs: - - ratelimit-priority-httproute + - ratelimit-priority-httproute organization: wso2-org --- apiVersion: gateway.networking.k8s.io/v1beta1 @@ -38,36 +38,36 @@ metadata: namespace: gateway-integration-test-infra spec: parentRefs: - - group: gateway.networking.k8s.io - kind: Gateway - name: wso2-apk-default - namespace: apk-integration-test - sectionName: httpslistener + - group: gateway.networking.k8s.io + kind: Gateway + name: wso2-apk-default + namespace: apk-integration-test + sectionName: httpslistener hostnames: - - ratelimit-priority.test.gw.wso2.com + - ratelimit-priority.test.gw.wso2.com rules: - - matches: - - path: - type: PathPrefix - value: /v2/echo-full - backendRefs: - - group: dp.wso2.com - kind: Backend - name: infra-backend-v1 - - matches: - - path: - type: PathPrefix - value: /v2/echo - backendRefs: - - group: dp.wso2.com - kind: Backend - name: infra-backend-v1 - filters: - - type: ExtensionRef - extensionRef: - group: dp.wso2.com - kind: RateLimitPolicy - name: ratelimitter-r1 + - matches: + - path: + type: PathPrefix + value: /v2/echo-full + backendRefs: + - group: dp.wso2.com + kind: Backend + name: infra-backend-v1 + - matches: + - path: + type: PathPrefix + value: /v2/echo + backendRefs: + - group: dp.wso2.com + kind: Backend + name: infra-backend-v1 + filters: + - type: ExtensionRef + extensionRef: + group: dp.wso2.com + kind: RateLimitPolicy + name: ratelimitter-r1 --- apiVersion: dp.wso2.com/v1alpha2 kind: Backend @@ -76,8 +76,8 @@ metadata: namespace: gateway-integration-test-infra spec: services: - - host: infra-backend-v1.gateway-integration-test-infra - port: 8080 + - host: infra-backend-v1.gateway-integration-test-infra + port: 8080 --- apiVersion: dp.wso2.com/v1alpha3 kind: RateLimitPolicy diff --git a/test/integration/integration/tests/resources/tests/resource-scopes.yaml b/test/integration/integration/tests/resources/tests/resource-scopes.yaml index b2a4444a44..44ef76abce 100644 --- a/test/integration/integration/tests/resources/tests/resource-scopes.yaml +++ b/test/integration/integration/tests/resources/tests/resource-scopes.yaml @@ -14,7 +14,7 @@ # specific language governing permissions and limitations # under the License. -apiVersion: dp.wso2.com/v1alpha2 +apiVersion: dp.wso2.com/v1alpha3 kind: API metadata: name: resource-scopes @@ -27,7 +27,7 @@ spec: #definitionFileRef: definition-file production: - routeRefs: - - resource-scopes-httproute + - resource-scopes-httproute organization: wso2-org --- apiVersion: gateway.networking.k8s.io/v1beta1 @@ -37,44 +37,44 @@ metadata: namespace: gateway-integration-test-infra spec: parentRefs: - - group: gateway.networking.k8s.io - kind: Gateway - name: wso2-apk-default - namespace: apk-integration-test - sectionName: httpslistener + - group: gateway.networking.k8s.io + kind: Gateway + name: wso2-apk-default + namespace: apk-integration-test + sectionName: httpslistener hostnames: - - resource-scopes.test.gw.wso2.com + - resource-scopes.test.gw.wso2.com rules: - - matches: - - path: - type: Exact - value: /pets/findByTags - method: GET - filters: - - type: ExtensionRef - extensionRef: - group: dp.wso2.com - kind: Scope - name: pet-scopes - backendRefs: - - group: dp.wso2.com - kind: Backend - name: infra-backend-v1 - - matches: - - path: - type: RegularExpression - value: /pet/(\d{1,3}) - method: GET - filters: - - type: URLRewrite - urlRewrite: - path: - type: ReplaceFullPath - replaceFullPath: /pet/\1 - backendRefs: - - group: dp.wso2.com - kind: Backend - name: infra-backend-v1 + - matches: + - path: + type: Exact + value: /pets/findByTags + method: GET + filters: + - type: ExtensionRef + extensionRef: + group: dp.wso2.com + kind: Scope + name: pet-scopes + backendRefs: + - group: dp.wso2.com + kind: Backend + name: infra-backend-v1 + - matches: + - path: + type: RegularExpression + value: /pet/(\d{1,3}) + method: GET + filters: + - type: URLRewrite + urlRewrite: + path: + type: ReplaceFullPath + replaceFullPath: /pet/\1 + backendRefs: + - group: dp.wso2.com + kind: Backend + name: infra-backend-v1 --- apiVersion: dp.wso2.com/v1alpha1 kind: Scope @@ -93,5 +93,5 @@ metadata: namespace: gateway-integration-test-infra spec: services: - - host: infra-backend-v1.gateway-integration-test-infra - port: 8080 + - host: infra-backend-v1.gateway-integration-test-infra + port: 8080 diff --git a/test/integration/integration/tests/resources/tests/trailing-slash.yaml b/test/integration/integration/tests/resources/tests/trailing-slash.yaml index eea7d2c912..1734f5618b 100644 --- a/test/integration/integration/tests/resources/tests/trailing-slash.yaml +++ b/test/integration/integration/tests/resources/tests/trailing-slash.yaml @@ -14,7 +14,7 @@ # specific language governing permissions and limitations # under the License. -apiVersion: dp.wso2.com/v1alpha2 +apiVersion: dp.wso2.com/v1alpha3 kind: API metadata: name: trailing-slash @@ -27,7 +27,7 @@ spec: #definitionFileRef: definition-file production: - routeRefs: - - trailing-slash-httproute + - trailing-slash-httproute organization: wso2-org --- apiVersion: gateway.networking.k8s.io/v1beta1 @@ -37,134 +37,134 @@ metadata: namespace: gateway-integration-test-infra spec: parentRefs: - - group: gateway.networking.k8s.io - kind: Gateway - name: wso2-apk-default - namespace: apk-integration-test - sectionName: httpslistener + - group: gateway.networking.k8s.io + kind: Gateway + name: wso2-apk-default + namespace: apk-integration-test + sectionName: httpslistener hostnames: - - trailing-slash.test.gw.wso2.com + - trailing-slash.test.gw.wso2.com rules: - - matches: - - path: - type: Exact - value: /echo-full/no-slash/findByStatus - method: GET - filters: - - type: URLRewrite - urlRewrite: - path: - type: ReplaceFullPath - replaceFullPath: /v2/echo-full/no-slash/findByStatus - backendRefs: - - group: dp.wso2.com - kind: Backend - name: infra-backend-v1 - - matches: - - path: - type: RegularExpression - value: /echo-full/no-slash/([^/]+) - method: GET - filters: - - type: URLRewrite - urlRewrite: - path: - type: ReplaceFullPath - replaceFullPath: /v2/echo-full/no-slash/\1 - backendRefs: - - group: dp.wso2.com - kind: Backend - name: infra-backend-v1 - - matches: - - path: - type: RegularExpression - value: /echo-full/no-slash/([^/]+)/pet/([^/]+) - method: GET - filters: - - type: URLRewrite - urlRewrite: - path: - type: ReplaceFullPath - replaceFullPath: /v2/echo-full/no-slash/\1/pet/\2 - backendRefs: - - group: dp.wso2.com - kind: Backend - name: infra-backend-v1 - - matches: - - path: - type: Exact - value: /echo-full/with-slash/findByStatus - method: GET - filters: - - type: URLRewrite - urlRewrite: - path: - type: ReplaceFullPath - replaceFullPath: /v2/echo-full/with-slash/findByStatus/ - backendRefs: - - group: dp.wso2.com - kind: Backend - name: infra-backend-v1 - - matches: - - path: - type: RegularExpression - value: /echo-full/with-slash/([^/]+) - method: GET - filters: - - type: URLRewrite - urlRewrite: - path: - type: ReplaceFullPath - replaceFullPath: /v2/echo-full/with-slash/\1/ - backendRefs: - - group: dp.wso2.com - kind: Backend - name: infra-backend-v1 - - matches: - - path: - type: RegularExpression - value: /echo-full/with-slash/([^/]+)/pet/([^/]+) - method: GET - filters: - - type: URLRewrite - urlRewrite: - path: - type: ReplaceFullPath - replaceFullPath: /v2/echo-full/with-slash/\1/pet/\2/ - backendRefs: - - group: dp.wso2.com - kind: Backend - name: infra-backend-v1 - - matches: - - path: - type: Exact - value: /echo-full/chars - method: GET - filters: - - type: URLRewrite - urlRewrite: - path: - type: ReplaceFullPath - replaceFullPath: /v2/echo-full/chars - backendRefs: - - group: dp.wso2.com - kind: Backend - name: infra-backend-v1 - - matches: - - path: - type: RegularExpression - value: /echo-full/with-param/([^/]+) - method: GET - filters: - - type: URLRewrite - urlRewrite: - path: - type: ReplaceFullPath - replaceFullPath: /v2/echo-full/with-param/\1 - backendRefs: - - group: dp.wso2.com - kind: Backend - name: infra-backend-v1 + - matches: + - path: + type: Exact + value: /echo-full/no-slash/findByStatus + method: GET + filters: + - type: URLRewrite + urlRewrite: + path: + type: ReplaceFullPath + replaceFullPath: /v2/echo-full/no-slash/findByStatus + backendRefs: + - group: dp.wso2.com + kind: Backend + name: infra-backend-v1 + - matches: + - path: + type: RegularExpression + value: /echo-full/no-slash/([^/]+) + method: GET + filters: + - type: URLRewrite + urlRewrite: + path: + type: ReplaceFullPath + replaceFullPath: /v2/echo-full/no-slash/\1 + backendRefs: + - group: dp.wso2.com + kind: Backend + name: infra-backend-v1 + - matches: + - path: + type: RegularExpression + value: /echo-full/no-slash/([^/]+)/pet/([^/]+) + method: GET + filters: + - type: URLRewrite + urlRewrite: + path: + type: ReplaceFullPath + replaceFullPath: /v2/echo-full/no-slash/\1/pet/\2 + backendRefs: + - group: dp.wso2.com + kind: Backend + name: infra-backend-v1 + - matches: + - path: + type: Exact + value: /echo-full/with-slash/findByStatus + method: GET + filters: + - type: URLRewrite + urlRewrite: + path: + type: ReplaceFullPath + replaceFullPath: /v2/echo-full/with-slash/findByStatus/ + backendRefs: + - group: dp.wso2.com + kind: Backend + name: infra-backend-v1 + - matches: + - path: + type: RegularExpression + value: /echo-full/with-slash/([^/]+) + method: GET + filters: + - type: URLRewrite + urlRewrite: + path: + type: ReplaceFullPath + replaceFullPath: /v2/echo-full/with-slash/\1/ + backendRefs: + - group: dp.wso2.com + kind: Backend + name: infra-backend-v1 + - matches: + - path: + type: RegularExpression + value: /echo-full/with-slash/([^/]+)/pet/([^/]+) + method: GET + filters: + - type: URLRewrite + urlRewrite: + path: + type: ReplaceFullPath + replaceFullPath: /v2/echo-full/with-slash/\1/pet/\2/ + backendRefs: + - group: dp.wso2.com + kind: Backend + name: infra-backend-v1 + - matches: + - path: + type: Exact + value: /echo-full/chars + method: GET + filters: + - type: URLRewrite + urlRewrite: + path: + type: ReplaceFullPath + replaceFullPath: /v2/echo-full/chars + backendRefs: + - group: dp.wso2.com + kind: Backend + name: infra-backend-v1 + - matches: + - path: + type: RegularExpression + value: /echo-full/with-param/([^/]+) + method: GET + filters: + - type: URLRewrite + urlRewrite: + path: + type: ReplaceFullPath + replaceFullPath: /v2/echo-full/with-param/\1 + backendRefs: + - group: dp.wso2.com + kind: Backend + name: infra-backend-v1 --- apiVersion: dp.wso2.com/v1alpha2 kind: Backend @@ -173,5 +173,5 @@ metadata: namespace: gateway-integration-test-infra spec: services: - - host: infra-backend-v1.gateway-integration-test-infra - port: 8080 + - host: infra-backend-v1.gateway-integration-test-infra + port: 8080 From 1e1e0c5197446ebc45067ba494caee24626f1756 Mon Sep 17 00:00:00 2001 From: sgayangi Date: Thu, 19 Sep 2024 14:16:51 +0530 Subject: [PATCH 16/17] Resolve merge conflicts --- .../envoyconf/routes_with_clusters_test.go | 1 - adapter/internal/operator/operator.go | 2 - .../synchronizer/zz_generated.deepcopy.go | 15 +++++ .../apis/dp/v1alpha3/zz_generated.deepcopy.go | 59 +++++++++++++++++++ 4 files changed, 74 insertions(+), 3 deletions(-) diff --git a/adapter/internal/oasparser/envoyconf/routes_with_clusters_test.go b/adapter/internal/oasparser/envoyconf/routes_with_clusters_test.go index b2cccf41f4..f9a3743990 100644 --- a/adapter/internal/oasparser/envoyconf/routes_with_clusters_test.go +++ b/adapter/internal/oasparser/envoyconf/routes_with_clusters_test.go @@ -32,7 +32,6 @@ import ( "github.com/wso2/apk/common-go-libs/apis/dp/v1alpha1" "github.com/wso2/apk/common-go-libs/apis/dp/v1alpha2" "github.com/wso2/apk/common-go-libs/apis/dp/v1alpha3" - "github.com/wso2/apk/common-go-libs/apis/dp/v1beta1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" k8types "k8s.io/apimachinery/pkg/types" gwapiv1 "sigs.k8s.io/gateway-api/apis/v1" diff --git a/adapter/internal/operator/operator.go b/adapter/internal/operator/operator.go index 7777932205..f937468713 100644 --- a/adapter/internal/operator/operator.go +++ b/adapter/internal/operator/operator.go @@ -52,7 +52,6 @@ import ( dpv1alpha1 "github.com/wso2/apk/common-go-libs/apis/dp/v1alpha1" dpv1alpha2 "github.com/wso2/apk/common-go-libs/apis/dp/v1alpha2" dpv1alpha3 "github.com/wso2/apk/common-go-libs/apis/dp/v1alpha3" - dpv1beta1 "github.com/wso2/apk/common-go-libs/apis/dp/v1beta1" //+kubebuilder:scaffold:imports ) @@ -75,7 +74,6 @@ func init() { utilruntime.Must(cpv1alpha2.AddToScheme(scheme)) utilruntime.Must(cpv1alpha3.AddToScheme(scheme)) - utilruntime.Must(dpv1beta1.AddToScheme(scheme)) //+kubebuilder:scaffold:scheme } diff --git a/adapter/internal/operator/synchronizer/zz_generated.deepcopy.go b/adapter/internal/operator/synchronizer/zz_generated.deepcopy.go index 84e61c5617..644cda1e88 100644 --- a/adapter/internal/operator/synchronizer/zz_generated.deepcopy.go +++ b/adapter/internal/operator/synchronizer/zz_generated.deepcopy.go @@ -419,6 +419,21 @@ func (in *HTTPRouteState) DeepCopyInto(out *HTTPRouteState) { (*out)[key] = *val.DeepCopy() } } + if in.RuleIdxToAiRatelimitPolicyMapping != nil { + in, out := &in.RuleIdxToAiRatelimitPolicyMapping, &out.RuleIdxToAiRatelimitPolicyMapping + *out = make(map[int]*v1alpha3.AIRateLimitPolicy, len(*in)) + for key, val := range *in { + var outVal *v1alpha3.AIRateLimitPolicy + if val == nil { + (*out)[key] = nil + } else { + in, out := &val, &outVal + *out = new(v1alpha3.AIRateLimitPolicy) + (*in).DeepCopyInto(*out) + } + (*out)[key] = outVal + } + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HTTPRouteState. diff --git a/common-go-libs/apis/dp/v1alpha3/zz_generated.deepcopy.go b/common-go-libs/apis/dp/v1alpha3/zz_generated.deepcopy.go index 927fb3f465..a437926016 100644 --- a/common-go-libs/apis/dp/v1alpha3/zz_generated.deepcopy.go +++ b/common-go-libs/apis/dp/v1alpha3/zz_generated.deepcopy.go @@ -257,6 +257,65 @@ func (in *AIRateLimitPolicyStatus) DeepCopy() *AIRateLimitPolicyStatus { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *API) DeepCopyInto(out *API) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + in.Status.DeepCopyInto(&out.Status) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new API. +func (in *API) DeepCopy() *API { + if in == nil { + return nil + } + out := new(API) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *API) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *APIList) DeepCopyInto(out *APIList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]API, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new APIList. +func (in *APIList) DeepCopy() *APIList { + if in == nil { + return nil + } + out := new(APIList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *APIList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *APIPolicy) DeepCopyInto(out *APIPolicy) { *out = *in From 18576c5ad99cbc3f7192ff9c3e12bdd750bb8e6e Mon Sep 17 00:00:00 2001 From: sgayangi Date: Thu, 19 Sep 2024 22:38:08 +0530 Subject: [PATCH 17/17] Add integration tests for gRPC APIs --- .../oasparser/envoyconf/http_filters.go | 32 +- .../org/wso2/apk/enforcer/api/GRPCAPI.java | 4 +- test/cucumber-tests/CRs/artifacts.yaml | 48 ++ test/cucumber-tests/build.gradle | 8 + test/cucumber-tests/scripts/setup-hosts.sh | 1 + .../wso2/apk/integration/api/BaseSteps.java | 129 +++- .../apk/integration/api/SharedContext.java | 48 +- .../utils/GenericClientInterceptor.java | 38 + .../clients/SimpleGRPCStudentClient.java | 116 +++ .../clients/studentGrpcClient/Student.java | 64 ++ .../studentGrpcClient/StudentRequest.java | 518 ++++++++++++++ .../StudentRequestOrBuilder.java | 16 + .../studentGrpcClient/StudentResponse.java | 665 ++++++++++++++++++ .../StudentResponseOrBuilder.java | 30 + .../StudentServiceDefaultVersionGrpc.java | 435 ++++++++++++ .../studentGrpcClient/StudentServiceGrpc.java | 435 ++++++++++++ .../apk-confs/grpc/generated-grpc.apk-conf | 24 + .../artifacts/apk-confs/grpc/grpc.apk-conf | 28 + .../apk-confs/grpc/grpc_scopes.apk-conf | 28 + .../grpc/grpc_with_default_version.apk-conf | 28 + .../grpc/grpc_with_disabled_auth.apk-conf | 32 + ...th_mtls_mandatory_oauth2_disabled.apk-conf | 36 + ...h_mtls_mandatory_oauth2_mandatory.apk-conf | 36 + ...ith_mtls_optional_oauth2_optional.apk-conf | 36 + .../artifacts/apk-confs/grpc/order.apk-conf | 24 + .../definitions/order-definition.zip | Bin 0 -> 1393 bytes .../artifacts/definitions/student.proto | 21 + .../src/test/resources/tests/api/GRPC.feature | 123 ++++ .../test/resources/tests/api/GRPCMTLS.feature | 89 +++ .../integration/integration/tests/grpc-api.go | 77 ++ .../tests/resources/base/manifests.yaml | 47 ++ .../tests/resources/tests/grpc-api.yaml | 106 +++ .../utils/grpc-code/student/student.go | 47 ++ .../utils/grpc-code/student/student.pb.go | 254 +++++++ .../grpc-code/student/student_grpc.pb.go | 314 +++++++++ .../student_default_version/student2.go | 47 ++ .../student_default_version.pb.go | 251 +++++++ .../student_default_version_grpc.pb.go | 314 +++++++++ .../integration/utils/grpcutils/helpers.go | 91 +++ test/integration/scripts/run-tests.sh | 18 +- test/integration/scripts/setup-hosts.sh | 8 +- 41 files changed, 4603 insertions(+), 63 deletions(-) create mode 100644 test/cucumber-tests/src/test/java/org/wso2/apk/integration/utils/GenericClientInterceptor.java create mode 100644 test/cucumber-tests/src/test/java/org/wso2/apk/integration/utils/clients/SimpleGRPCStudentClient.java create mode 100644 test/cucumber-tests/src/test/java/org/wso2/apk/integration/utils/clients/studentGrpcClient/Student.java create mode 100644 test/cucumber-tests/src/test/java/org/wso2/apk/integration/utils/clients/studentGrpcClient/StudentRequest.java create mode 100644 test/cucumber-tests/src/test/java/org/wso2/apk/integration/utils/clients/studentGrpcClient/StudentRequestOrBuilder.java create mode 100644 test/cucumber-tests/src/test/java/org/wso2/apk/integration/utils/clients/studentGrpcClient/StudentResponse.java create mode 100644 test/cucumber-tests/src/test/java/org/wso2/apk/integration/utils/clients/studentGrpcClient/StudentResponseOrBuilder.java create mode 100644 test/cucumber-tests/src/test/java/org/wso2/apk/integration/utils/clients/studentGrpcClient/StudentServiceDefaultVersionGrpc.java create mode 100644 test/cucumber-tests/src/test/java/org/wso2/apk/integration/utils/clients/studentGrpcClient/StudentServiceGrpc.java create mode 100644 test/cucumber-tests/src/test/resources/artifacts/apk-confs/grpc/generated-grpc.apk-conf create mode 100644 test/cucumber-tests/src/test/resources/artifacts/apk-confs/grpc/grpc.apk-conf create mode 100644 test/cucumber-tests/src/test/resources/artifacts/apk-confs/grpc/grpc_scopes.apk-conf create mode 100644 test/cucumber-tests/src/test/resources/artifacts/apk-confs/grpc/grpc_with_default_version.apk-conf create mode 100644 test/cucumber-tests/src/test/resources/artifacts/apk-confs/grpc/grpc_with_disabled_auth.apk-conf create mode 100644 test/cucumber-tests/src/test/resources/artifacts/apk-confs/grpc/grpc_with_mtls_mandatory_oauth2_disabled.apk-conf create mode 100644 test/cucumber-tests/src/test/resources/artifacts/apk-confs/grpc/grpc_with_mtls_mandatory_oauth2_mandatory.apk-conf create mode 100644 test/cucumber-tests/src/test/resources/artifacts/apk-confs/grpc/grpc_with_mtls_optional_oauth2_optional.apk-conf create mode 100644 test/cucumber-tests/src/test/resources/artifacts/apk-confs/grpc/order.apk-conf create mode 100644 test/cucumber-tests/src/test/resources/artifacts/definitions/order-definition.zip create mode 100644 test/cucumber-tests/src/test/resources/artifacts/definitions/student.proto create mode 100644 test/cucumber-tests/src/test/resources/tests/api/GRPC.feature create mode 100644 test/cucumber-tests/src/test/resources/tests/api/GRPCMTLS.feature create mode 100644 test/integration/integration/tests/grpc-api.go create mode 100644 test/integration/integration/tests/resources/tests/grpc-api.yaml create mode 100644 test/integration/integration/utils/grpc-code/student/student.go create mode 100644 test/integration/integration/utils/grpc-code/student/student.pb.go create mode 100644 test/integration/integration/utils/grpc-code/student/student_grpc.pb.go create mode 100644 test/integration/integration/utils/grpc-code/student_default_version/student2.go create mode 100644 test/integration/integration/utils/grpc-code/student_default_version/student_default_version.pb.go create mode 100644 test/integration/integration/utils/grpc-code/student_default_version/student_default_version_grpc.pb.go create mode 100644 test/integration/integration/utils/grpcutils/helpers.go diff --git a/adapter/internal/oasparser/envoyconf/http_filters.go b/adapter/internal/oasparser/envoyconf/http_filters.go index cedd7784bd..570354c78e 100644 --- a/adapter/internal/oasparser/envoyconf/http_filters.go +++ b/adapter/internal/oasparser/envoyconf/http_filters.go @@ -29,6 +29,7 @@ import ( cors_filter_v3 "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/cors/v3" ext_authv3 "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/ext_authz/v3" ext_process "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/ext_proc/v3" + grpc_stats_filter_v3 "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/grpc_stats/v3" luav3 "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/lua/v3" ratelimit "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/ratelimit/v3" routerv3 "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/router/v3" @@ -48,9 +49,9 @@ import ( "github.com/golang/protobuf/ptypes/any" ) - // HTTPExternalProcessor HTTP filter const HTTPExternalProcessor = "envoy.filters.http.ext_proc" + // RatelimitFilterName Ratelimit filter name const RatelimitFilterName = "envoy.filters.http.ratelimit" @@ -114,6 +115,27 @@ func getRouterHTTPFilter() *hcmv3.HttpFilter { return &filter } +// getGRPCStatsHTTPFilter gets grpc_stats http filter. +func getGRPCStatsHTTPFilter() *hcmv3.HttpFilter { + + gprcStatsFilterConf := grpc_stats_filter_v3.FilterConfig{ + EnableUpstreamStats: true, + EmitFilterState: true, + } + gprcStatsFilterTypedConf, err := anypb.New(&gprcStatsFilterConf) + + if err != nil { + logger.LoggerOasparser.Error("Error marshaling grpc stats filter configs. ", err) + } + + filter := hcmv3.HttpFilter{ + Name: "grpc_stats", + ConfigType: &hcmv3.HttpFilter_TypedConfig{TypedConfig: gprcStatsFilterTypedConf}, + } + + return &filter +} + // getCorsHTTPFilter gets cors http filter. func getCorsHTTPFilter() *hcmv3.HttpFilter { @@ -136,11 +158,13 @@ func getCorsHTTPFilter() *hcmv3.HttpFilter { func getUpgradeFilters() []*hcmv3.HttpFilter { cors := getCorsHTTPFilter() + grpcStats := getGRPCStatsHTTPFilter() extAauth := getExtAuthzHTTPFilter() apkWebSocketWASM := getAPKWebSocketWASMFilter() router := getRouterHTTPFilter() upgradeFilters := []*hcmv3.HttpFilter{ cors, + grpcStats, extAauth, apkWebSocketWASM, router, @@ -215,8 +239,8 @@ func getExtProcessHTTPFilter() *hcmv3.HttpFilter { }, }, ProcessingMode: &ext_process.ProcessingMode{ - ResponseBodyMode: ext_process.ProcessingMode_BUFFERED, - RequestHeaderMode: ext_process.ProcessingMode_SKIP, + ResponseBodyMode: ext_process.ProcessingMode_BUFFERED, + RequestHeaderMode: ext_process.ProcessingMode_SKIP, ResponseHeaderMode: ext_process.ProcessingMode_SKIP, }, MetadataOptions: &ext_process.MetadataOptions{ @@ -224,7 +248,7 @@ func getExtProcessHTTPFilter() *hcmv3.HttpFilter { Untyped: []string{"envoy.filters.http.ext_authz", "envoy.filters.http.ext_proc"}, }, }, - RequestAttributes: []string{"xds.route_metadata"}, + RequestAttributes: []string{"xds.route_metadata"}, ResponseAttributes: []string{"xds.route_metadata"}, } ext, err2 := anypb.New(externalProcessor) diff --git a/gateway/enforcer/org.wso2.apk.enforcer/src/main/java/org/wso2/apk/enforcer/api/GRPCAPI.java b/gateway/enforcer/org.wso2.apk.enforcer/src/main/java/org/wso2/apk/enforcer/api/GRPCAPI.java index 540d4d9130..f1457fe09f 100644 --- a/gateway/enforcer/org.wso2.apk.enforcer/src/main/java/org/wso2/apk/enforcer/api/GRPCAPI.java +++ b/gateway/enforcer/org.wso2.apk.enforcer/src/main/java/org/wso2/apk/enforcer/api/GRPCAPI.java @@ -106,8 +106,8 @@ public String init(Api api) { .mutualSSL(mutualSSL) .applicationSecurity(applicationSecurity).jwtConfigurationDto(jwtConfigurationDto) .apiDefinition(apiDefinition).environment(api.getEnvironment()) - .environment(api.getEnvironment()) - .subscriptionValidation(api.getSubscriptionValidation()).build(); + .subscriptionValidation(api.getSubscriptionValidation()).transportSecurity(api.getTransportSecurity()) + .build(); initFilters(); logger.info("APIConfig: " + this.apiConfig); return basePath; diff --git a/test/cucumber-tests/CRs/artifacts.yaml b/test/cucumber-tests/CRs/artifacts.yaml index 26ebb0248c..56e41d3177 100644 --- a/test/cucumber-tests/CRs/artifacts.yaml +++ b/test/cucumber-tests/CRs/artifacts.yaml @@ -1729,3 +1729,51 @@ spec: in: "Body" value: "usage.total_tokens" --- +apiVersion: v1 +kind: Service +metadata: + name: grpc-backend + namespace: apk-integration-test +spec: + selector: + app: grpc-backend + ports: + - protocol: TCP + port: 6565 + targetPort: 6565 +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: grpc-backend + namespace: apk-integration-test + labels: + app: grpc-backend +spec: + replicas: 1 + selector: + matchLabels: + app: grpc-backend + template: + metadata: + labels: + app: grpc-backend + spec: + containers: + - name: grpc-backend + image: ddh13/dineth-grpc-demo-server:1.0.0 + imagePullPolicy: Always + env: + - name: POD_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + - name: NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + resources: + requests: + cpu: 10m +--- + diff --git a/test/cucumber-tests/build.gradle b/test/cucumber-tests/build.gradle index dba839001b..c03a239191 100644 --- a/test/cucumber-tests/build.gradle +++ b/test/cucumber-tests/build.gradle @@ -41,6 +41,14 @@ dependencies { testImplementation 'commons-io:commons-io:2.13.0' testImplementation 'com.nimbusds:nimbus-jose-jwt:9.31' testImplementation 'com.googlecode.json-simple:json-simple:1.1.1' + implementation 'javax.annotation:javax.annotation-api:1.3.2' + implementation 'io.grpc:grpc-netty:1.48.0' + implementation 'io.grpc:grpc-protobuf:1.48.0' + implementation 'io.grpc:grpc-stub:1.48.0' + implementation 'io.grpc:grpc-auth:1.48.0' + implementation 'io.grpc:grpc-netty-shaded:1.57.0' + implementation 'io.grpc:grpc-protobuf:1.57.0' + implementation 'io.grpc:grpc-stub:1.57.0' } test { diff --git a/test/cucumber-tests/scripts/setup-hosts.sh b/test/cucumber-tests/scripts/setup-hosts.sh index 5b307165c2..f12e9645e3 100644 --- a/test/cucumber-tests/scripts/setup-hosts.sh +++ b/test/cucumber-tests/scripts/setup-hosts.sh @@ -10,6 +10,7 @@ kubectl wait deployment/llm-deployment-subs -n apk-integration-test --for=condit kubectl wait deployment/llm-deployment-header -n apk-integration-test --for=condition=available --timeout=600s kubectl wait deployment/interceptor-service-deployment -n apk-integration-test --for=condition=available --timeout=600s kubectl wait deployment/graphql-faker -n apk-integration-test --for=condition=available --timeout=600s +kubectl wait deployment/grpc-backend -n apk-integration-test --for=condition=available --timeout=600s kubectl wait --timeout=5m -n apk-integration-test deployment/apk-test-setup-wso2-apk-adapter-deployment --for=condition=Available kubectl wait --timeout=15m -n apk-integration-test deployment/apk-test-setup-wso2-apk-gateway-runtime-deployment --for=condition=Available IP=$(kubectl get svc apk-test-setup-wso2-apk-gateway-service -n apk-integration-test --output jsonpath='{.status.loadBalancer.ingress[0].ip}') diff --git a/test/cucumber-tests/src/test/java/org/wso2/apk/integration/api/BaseSteps.java b/test/cucumber-tests/src/test/java/org/wso2/apk/integration/api/BaseSteps.java index de5b55014e..9f2c924d20 100644 --- a/test/cucumber-tests/src/test/java/org/wso2/apk/integration/api/BaseSteps.java +++ b/test/cucumber-tests/src/test/java/org/wso2/apk/integration/api/BaseSteps.java @@ -50,11 +50,14 @@ import org.testng.Assert; import org.wso2.apk.integration.utils.Constants; import org.wso2.apk.integration.utils.Utils; +import org.wso2.apk.integration.utils.clients.SimpleGRPCStudentClient; import org.wso2.apk.integration.utils.clients.SimpleHTTPClient; +import org.wso2.apk.integration.utils.clients.studentGrpcClient.StudentResponse; import org.apache.http.entity.mime.HttpMultipartMode; import org.apache.http.entity.mime.MultipartEntityBuilder; import org.apache.http.entity.ContentType; - +import org.wso2.apk.integration.utils.clients.studentGrpcClient.StudentResponse; +import org.wso2.apk.integration.utils.clients.SimpleGRPCStudentClient; import java.io.IOException; import java.io.InputStream; import java.net.MalformedURLException; @@ -69,6 +72,8 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import io.grpc.Status; +import io.grpc.StatusRuntimeException; /** * This class contains the common step definitions. @@ -98,18 +103,22 @@ public void systemIsReady() { @Then("the response body should contain {string}") public void theResponseBodyShouldContain(String expectedText) throws IOException { - Assert.assertTrue(sharedContext.getResponseBody().contains(expectedText), "Actual response body: " + sharedContext.getResponseBody()); + Assert.assertTrue(sharedContext.getResponseBody().contains(expectedText), + "Actual response body: " + sharedContext.getResponseBody()); } + @Then("the response body should not contain {string}") public void theResponseBodyShouldNotContain(String expectedText) throws IOException { - Assert.assertFalse(sharedContext.getResponseBody().contains(expectedText), "Actual response body: " + sharedContext.getResponseBody()); + Assert.assertFalse(sharedContext.getResponseBody().contains(expectedText), + "Actual response body: " + sharedContext.getResponseBody()); } @Then("the response body should contain") public void theResponseBodyShouldContain(DataTable dataTable) throws IOException { List responseBodyLines = dataTable.asList(String.class); for (String line : responseBodyLines) { - Assert.assertTrue(sharedContext.getResponseBody().contains(line), "Actual response body: " + sharedContext.getResponseBody()); + Assert.assertTrue(sharedContext.getResponseBody().contains(line), + "Actual response body: " + sharedContext.getResponseBody()); } } @@ -117,7 +126,7 @@ public void theResponseBodyShouldContain(DataTable dataTable) throws IOException public void theResponseStatusCodeShouldBe(int expectedStatusCode) throws IOException { int actualStatusCode = sharedContext.getResponse().getStatusLine().getStatusCode(); - ((CloseableHttpResponse)sharedContext.getResponse()).close(); + ((CloseableHttpResponse) sharedContext.getResponse()).close(); Assert.assertEquals(actualStatusCode, expectedStatusCode); } @@ -146,7 +155,8 @@ public void sendHttpRequest(String httpMethod, String url, String body) throws I // It will send request using a new thread and forget about the response @Then("I send {string} async request to {string} with body {string}") - public void sendAsyncHttpRequest(String httpMethod, String url, String body) throws IOException, NoSuchAlgorithmException, KeyStoreException, KeyManagementException { + public void sendAsyncHttpRequest(String httpMethod, String url, String body) + throws IOException, NoSuchAlgorithmException, KeyStoreException, KeyManagementException { String finalBody = Utils.resolveVariables(body, sharedContext.getValueStore()); if (sharedContext.getResponse() instanceof CloseableHttpResponse) { ((CloseableHttpResponse) sharedContext.getResponse()).close(); @@ -172,6 +182,55 @@ public void sendAsyncHttpRequest(String httpMethod, String url, String body) thr thread.start(); } + @Then("the student response body should contain name: {string} age: {int}") + public void theStudentResponseBodyShouldContainNameAndAge(String arg0, int arg1) { + StudentResponse studentResponse = sharedContext.getStudentResponse(); + if (studentResponse == null) { + Assert.fail("Student response is null."); + } + int age = studentResponse.getAge(); + String name = studentResponse.getName(); + Assert.assertEquals(name, arg0); + Assert.assertEquals(age, arg1); + } + + @Then("the response body should contain endpoint definition for student.proto") + public void theResponseBodyShouldContainEndpointDefinition() throws IOException { + String expectedText = "{\"apiDefinition\":\"syntax = \\\"proto3\\\";\\n\\noption java_multiple_files = true;\\noption java_package = \\\"org.example\\\";\\npackage dineth.grpc.api.v1.student;\\n\\nservice StudentService {\\n rpc GetStudent(StudentRequest) returns (StudentResponse) {};\\n rpc GetStudentStream(StudentRequest) returns (stream StudentResponse) {};\\n rpc SendStudentStream(stream StudentRequest) returns (StudentResponse) {};\\n rpc SendAndGetStudentStream(stream StudentRequest) returns (stream StudentResponse) {}\\n}\\n\\nmessage StudentRequest {\\n int32 id = 3;\\n}\\n\\nmessage StudentResponse {\\n string name = 1;\\n int32 age = 2;\\n}\\n\"}"; + Assert.assertTrue(sharedContext.getResponseBody().contains(expectedText), + "Actual response body: " + sharedContext.getResponseBody()); + } + + @Then("the gRPC response status code should be {int}") + public void theGrpcResponseStatusCodeShouldBe(int expectedStatusCode) throws IOException { + int actualStatusCode = sharedContext.getGrpcStatusCode(); + Assert.assertEquals(actualStatusCode, expectedStatusCode); + } + + @Then("I make grpc request GetStudent to {string} with port {int}") + public void GetStudent(String arg0, int arg1) throws StatusRuntimeException { + try { + SimpleGRPCStudentClient grpcStudentClient = new SimpleGRPCStudentClient(arg0, arg1); + sharedContext.setStudentResponse(grpcStudentClient.GetStudent(sharedContext.getHeaders())); + sharedContext.setGrpcStatusCode(0); + } catch (StatusRuntimeException e) { + sharedContext.setGrpcStatusCode(e.getStatus().getCode().value()); + logger.error(e.getMessage() + " Status code: " + e.getStatus().getCode().value()); + } + } + + @Then("I make grpc request GetStudent default version to {string} with port {int}") + public void GetStudentDefaultVersion(String arg0, int arg1) throws StatusRuntimeException { + try { + SimpleGRPCStudentClient grpcStudentClient = new SimpleGRPCStudentClient(arg0, arg1); + sharedContext.setStudentResponse(grpcStudentClient.GetStudentDefaultVersion(sharedContext.getHeaders())); + sharedContext.setGrpcStatusCode(0); + } catch (StatusRuntimeException e) { + sharedContext.setGrpcStatusCode(e.getStatus().getCode().value()); + logger.error(e.getMessage() + " Status code: " + e.getStatus().getCode().value()); + } + } + @Then("I set headers") public void setHeaders(DataTable dataTable) { List> rows = dataTable.asLists(String.class); @@ -233,7 +292,7 @@ public void waitForNextMinute() throws InterruptedException { if (secondsToWait > MAX_WAIT_FOR_NEXT_MINUTE_IN_SECONDS) { return; } - Thread.sleep((secondsToWait+1) * 1000); + Thread.sleep((secondsToWait + 1) * 1000); logger.info("Current time: " + LocalDateTime.now()); } @@ -242,7 +301,7 @@ public void waitForNextMinuteStrictly() throws InterruptedException { LocalDateTime now = LocalDateTime.now(); LocalDateTime nextMinute = now.plusMinutes(1).withSecond(0).withNano(0); long secondsToWait = now.until(nextMinute, ChronoUnit.SECONDS); - Thread.sleep((secondsToWait+1) * 1000); + Thread.sleep((secondsToWait + 1) * 1000); logger.info("Current time: " + LocalDateTime.now()); } @@ -272,8 +331,9 @@ public void containsHeader(String key, String value) { return; // Any value is acceptable } String actualValue = header.getValue(); - Assert.assertEquals(value, actualValue,"Header with key found but value mismatched."); + Assert.assertEquals(value, actualValue, "Header with key found but value mismatched."); } + @Then("the response headers not contains key {string}") public void notContainsHeader(String key) { key = Utils.resolveVariables(key, sharedContext.getValueStore()); @@ -282,11 +342,12 @@ public void notContainsHeader(String key) { Assert.fail("Response is null."); } Header header = response.getFirstHeader(key); - Assert.assertNull(header,"header contains in response headers"); + Assert.assertNull(header, "header contains in response headers"); } @Then("the {string} jwt should validate from JWKS {string} and contain") - public void decode_header_and_validate(String header,String jwksEndpoint, DataTable dataTable) throws MalformedURLException { + public void decode_header_and_validate(String header, String jwksEndpoint, DataTable dataTable) + throws MalformedURLException { List> claims = dataTable.asMaps(String.class, String.class); JsonObject jsonResponse = (JsonObject) JsonParser.parseString(sharedContext.getResponseBody()); String headerValue = jsonResponse.get("headers").getAsJsonObject().get(header).getAsString(); @@ -321,7 +382,7 @@ public void decode_header_and_validate(String header,String jwksEndpoint, DataTa Assert.assertEquals(claim.get("value"), claim1.toString(), "Actual " + "decoded JWT body: " + claimsSet); } - } catch (BadJOSEException | JOSEException|ParseException e) { + } catch (BadJOSEException | JOSEException | ParseException e) { logger.error("JWT Signature verification fail", e); Assert.fail("JWT Signature verification fail"); } @@ -332,9 +393,11 @@ public void iHaveValidSubscription() throws Exception { Map headers = new HashMap<>(); headers.put(Constants.REQUEST_HEADERS.HOST, Constants.DEFAULT_IDP_HOST); - headers.put(Constants.REQUEST_HEADERS.AUTHORIZATION, "Basic NDVmMWM1YzgtYTkyZS0xMWVkLWFmYTEtMDI0MmFjMTIwMDAyOjRmYmQ2MmVjLWE5MmUtMTFlZC1hZmExLTAyNDJhYzEyMDAwMg=="); + headers.put(Constants.REQUEST_HEADERS.AUTHORIZATION, + "Basic NDVmMWM1YzgtYTkyZS0xMWVkLWFmYTEtMDI0MmFjMTIwMDAyOjRmYmQ2MmVjLWE5MmUtMTFlZC1hZmExLTAyNDJhYzEyMDAwMg=="); - HttpResponse httpResponse = httpClient.doPost(Utils.getTokenEndpointURL(), headers, "grant_type=client_credentials&scope=" + Constants.API_CREATE_SCOPE, + HttpResponse httpResponse = httpClient.doPost(Utils.getTokenEndpointURL(), headers, + "grant_type=client_credentials&scope=" + Constants.API_CREATE_SCOPE, Constants.CONTENT_TYPES.APPLICATION_X_WWW_FORM_URLENCODED); sharedContext.setAccessToken(Utils.extractToken(httpResponse)); sharedContext.addStoreValue("accessToken", sharedContext.getAccessToken()); @@ -346,9 +409,11 @@ public void iHaveValidSubscriptionWithAPICreateScope() throws Exception { Map headers = new HashMap<>(); headers.put(Constants.REQUEST_HEADERS.HOST, Constants.DEFAULT_IDP_HOST); - headers.put(Constants.REQUEST_HEADERS.AUTHORIZATION, "Basic NDVmMWM1YzgtYTkyZS0xMWVkLWFmYTEtMDI0MmFjMTIwMDAyOjRmYmQ2MmVjLWE5MmUtMTFlZC1hZmExLTAyNDJhYzEyMDAwMg=="); + headers.put(Constants.REQUEST_HEADERS.AUTHORIZATION, + "Basic NDVmMWM1YzgtYTkyZS0xMWVkLWFmYTEtMDI0MmFjMTIwMDAyOjRmYmQ2MmVjLWE5MmUtMTFlZC1hZmExLTAyNDJhYzEyMDAwMg=="); - HttpResponse httpResponse = httpClient.doPost(Utils.getTokenEndpointURL(), headers, "grant_type=client_credentials", + HttpResponse httpResponse = httpClient.doPost(Utils.getTokenEndpointURL(), headers, + "grant_type=client_credentials", Constants.CONTENT_TYPES.APPLICATION_X_WWW_FORM_URLENCODED); sharedContext.setAccessToken(Utils.extractToken(httpResponse)); sharedContext.addStoreValue("accessToken", sharedContext.getAccessToken()); @@ -367,8 +432,8 @@ public void iHaveValidSubscriptionWithScope(DataTable dataTable) throws Exceptio headers.put(Constants.REQUEST_HEADERS.AUTHORIZATION, Constants.SUBSCRIPTION_BASIC_AUTH_TOKEN); HttpResponse httpResponse = httpClient.doPost(Utils.getTokenEndpointURL(), headers, - "grant_type=client_credentials&scope=" + scopes, - Constants.CONTENT_TYPES.APPLICATION_X_WWW_FORM_URLENCODED); + "grant_type=client_credentials&scope=" + scopes, + Constants.CONTENT_TYPES.APPLICATION_X_WWW_FORM_URLENCODED); sharedContext.setAccessToken(Utils.extractToken(httpResponse)); sharedContext.addStoreValue(Constants.ACCESS_TOKEN, sharedContext.getAccessToken()); } @@ -386,18 +451,17 @@ public void iHaveADCRApplication() throws Exception { headers.put(Constants.REQUEST_HEADERS.AUTHORIZATION, "Basic YWRtaW46YWRtaW4="); HttpResponse httpResponse = httpClient.doPost(Utils.getDCREndpointURL(), headers, "{\n" + - " \"callbackUrl\":\"www.google.lk\",\n" + - " \"clientName\":\"rest_api_publisher\",\n" + - " \"owner\":\"admin\",\n" + - " \"grantType\":\"client_credentials password refresh_token\",\n" + - " \"saasApp\":true\n" + - " }", + " \"callbackUrl\":\"www.google.lk\",\n" + + " \"clientName\":\"rest_api_publisher\",\n" + + " \"owner\":\"admin\",\n" + + " \"grantType\":\"client_credentials password refresh_token\",\n" + + " \"saasApp\":true\n" + + " }", Constants.CONTENT_TYPES.APPLICATION_JSON); sharedContext.setBasicAuthToken(Utils.extractBasicToken(httpResponse)); sharedContext.addStoreValue("publisherBasicAuthToken", sharedContext.getBasicAuthToken()); } - @Given("I have a valid Publisher access token") public void iHaveValidPublisherAccessToken() throws Exception { @@ -407,7 +471,8 @@ public void iHaveValidPublisherAccessToken() throws Exception { headers.put(Constants.REQUEST_HEADERS.HOST, Constants.DEFAULT_APIM_IDP_HOST); headers.put(Constants.REQUEST_HEADERS.AUTHORIZATION, basicAuthHeader); - HttpResponse httpResponse = httpClient.doPost(Utils.getAPIMTokenEndpointURL(), headers, "grant_type=password&username=admin&password=admin&scope=apim:api_view apim:api_create apim:api_publish apim:api_delete apim:api_manage apim:api_import_export apim:subscription_manage apim:client_certificates_add apim:client_certificates_update", + HttpResponse httpResponse = httpClient.doPost(Utils.getAPIMTokenEndpointURL(), headers, + "grant_type=password&username=admin&password=admin&scope=apim:api_view apim:api_create apim:api_publish apim:api_delete apim:api_manage apim:api_import_export apim:subscription_manage apim:client_certificates_add apim:client_certificates_update", Constants.CONTENT_TYPES.APPLICATION_X_WWW_FORM_URLENCODED); sharedContext.setPublisherAccessToken(Utils.extractToken(httpResponse)); @@ -423,7 +488,8 @@ public void iHaveValidDevportalAccessToken() throws Exception { headers.put(Constants.REQUEST_HEADERS.HOST, Constants.DEFAULT_APIM_IDP_HOST); headers.put(Constants.REQUEST_HEADERS.AUTHORIZATION, basicAuthHeader); - HttpResponse httpResponse = httpClient.doPost(Utils.getAPIMTokenEndpointURL(), headers, "grant_type=password&username=admin&password=admin&scope=apim:app_manage apim:sub_manage apim:subscribe", + HttpResponse httpResponse = httpClient.doPost(Utils.getAPIMTokenEndpointURL(), headers, + "grant_type=password&username=admin&password=admin&scope=apim:app_manage apim:sub_manage apim:subscribe", Constants.CONTENT_TYPES.APPLICATION_X_WWW_FORM_URLENCODED); sharedContext.setDevportalAccessToken(Utils.extractToken(httpResponse)); @@ -440,7 +506,8 @@ public void iHaveValidAdminportalAccessToken() throws Exception { headers.put(Constants.REQUEST_HEADERS.HOST, Constants.DEFAULT_APIM_IDP_HOST); headers.put(Constants.REQUEST_HEADERS.AUTHORIZATION, basicAuthHeader); - HttpResponse httpResponse = httpClient.doPost(Utils.getAPIMTokenEndpointURL(), headers, "grant_type=password&username=admin&password=admin&scope=apim:app_manage apim:admin_tier_view apim:admin_tier_manage", + HttpResponse httpResponse = httpClient.doPost(Utils.getAPIMTokenEndpointURL(), headers, + "grant_type=password&username=admin&password=admin&scope=apim:app_manage apim:admin_tier_view apim:admin_tier_manage", Constants.CONTENT_TYPES.APPLICATION_X_WWW_FORM_URLENCODED); sharedContext.setAdminAccessToken(Utils.extractToken(httpResponse)); sharedContext.addStoreValue("adminportalAccessToken", sharedContext.getAdminAccessToken()); @@ -450,12 +517,12 @@ public void iHaveValidAdminportalAccessToken() throws Exception { @Then("the response should be given as valid") public void theResponseShouldBeGivenAs() throws IOException { Boolean status = sharedContext.getDefinitionValidStatus(); - Assert.assertEquals(true, status,"Actual definition validation status: "+ status); + Assert.assertEquals(true, status, "Actual definition validation status: " + status); } @Then("I set {string} as the new access token") public void set_invalid_access_token(String newToken) throws Exception { - sharedContext.setApiAccessToken(newToken); - sharedContext.addStoreValue("accessToken",sharedContext.getApiAccessToken()); + sharedContext.setApiAccessToken(newToken); + sharedContext.addStoreValue("accessToken", sharedContext.getApiAccessToken()); } } diff --git a/test/cucumber-tests/src/test/java/org/wso2/apk/integration/api/SharedContext.java b/test/cucumber-tests/src/test/java/org/wso2/apk/integration/api/SharedContext.java index 85dd45b441..dcee4142d9 100644 --- a/test/cucumber-tests/src/test/java/org/wso2/apk/integration/api/SharedContext.java +++ b/test/cucumber-tests/src/test/java/org/wso2/apk/integration/api/SharedContext.java @@ -19,6 +19,7 @@ import org.apache.http.HttpResponse; import org.wso2.apk.integration.utils.clients.SimpleHTTPClient; +import org.wso2.apk.integration.utils.clients.studentGrpcClient.StudentResponse; import java.security.KeyManagementException; import java.security.KeyStoreException; @@ -40,6 +41,7 @@ public class SharedContext { private String basicAuthToken; private String apiUUID; private String revisionUUID; + private StudentResponse studentResponse; private String applicationUUID; private String keyManagerUUID; private String oauthKeyUUID; @@ -56,6 +58,8 @@ public class SharedContext { private static String policyID; private HashMap valueStore = new HashMap<>(); private HashMap headers = new HashMap<>(); + private int grpcStatusCode; + private int grpcErrorCode; public SimpleHTTPClient getHttpClient() throws NoSuchAlgorithmException, KeyStoreException, KeyManagementException { if (httpClient == null) { @@ -64,6 +68,24 @@ public SimpleHTTPClient getHttpClient() throws NoSuchAlgorithmException, KeyStor return httpClient; } + public int getGrpcStatusCode() { + return grpcStatusCode; + } + + public void setGrpcStatusCode(int grpcStatusCode) { + this.grpcStatusCode = grpcStatusCode; + } + + public StudentResponse getStudentResponse() { + + return studentResponse; + } + + public void setStudentResponse(StudentResponse studentResponse) { + + this.studentResponse = studentResponse; + } + public String getAccessToken() { return accessToken; @@ -208,16 +230,16 @@ public void setOauthKeyUUID(String oauthKeyUUID) { this.oauthKeyUUID = oauthKeyUUID; } - public void setAPIInternalKey(String internalKey){ + public void setAPIInternalKey(String internalKey) { this.internalKey = internalKey; } - public String getAPIInternalKey(){ + public String getAPIInternalKey() { return internalKey; } public String getConsumerSecret(String keyType) { - if ("production".equals(keyType)) + if ("production".equals(keyType)) return consumerSecret; else if ("sandbox".equals(keyType)) return sandboxConsumerSecret; @@ -225,14 +247,14 @@ else if ("sandbox".equals(keyType)) } public void setConsumerSecret(String consumerSecret, String keyType) { - if ("production".equals(keyType)) + if ("production".equals(keyType)) this.consumerSecret = consumerSecret; else if ("sandbox".equals(keyType)) this.sandboxConsumerSecret = consumerSecret; } public String getConsumerKey(String keyType) { - if ("production".equals(keyType)) + if ("production".equals(keyType)) return consumerKey; else if ("sandbox".equals(keyType)) return sandboxConsumerKey; @@ -240,25 +262,25 @@ else if ("sandbox".equals(keyType)) } public void setConsumerKey(String consumerKey, String keyType) { - if ("production".equals(keyType)) + if ("production".equals(keyType)) this.consumerKey = consumerKey; else if ("sandbox".equals(keyType)) this.sandboxConsumerKey = consumerKey; } - public void setKeyMappingID(String keyMappingID, String keyType){ - if ("production".equals(keyType)) + public void setKeyMappingID(String keyMappingID, String keyType) { + if ("production".equals(keyType)) this.prodKeyMappingID = keyMappingID; else if ("sandbox".equals(keyType)) this.sandboxKeyMappingID = keyMappingID; } - public String getKeyMappingID(String keyType){ - if ("production".equals(keyType)) + public String getKeyMappingID(String keyType) { + if ("production".equals(keyType)) return prodKeyMappingID; else if ("sandbox".equals(keyType)) return sandboxKeyMappingID; - return ""; + return ""; } public String getApiAccessToken() { @@ -271,11 +293,11 @@ public void setApiAccessToken(String apiAccessToken) { this.apiAccessToken = apiAccessToken; } - public void setAPIDefinitionValidStatus(Boolean definitionValidStatus){ + public void setAPIDefinitionValidStatus(Boolean definitionValidStatus) { this.definitionValidStatus = definitionValidStatus; } - public Boolean getDefinitionValidStatus(){ + public Boolean getDefinitionValidStatus() { return definitionValidStatus; } diff --git a/test/cucumber-tests/src/test/java/org/wso2/apk/integration/utils/GenericClientInterceptor.java b/test/cucumber-tests/src/test/java/org/wso2/apk/integration/utils/GenericClientInterceptor.java new file mode 100644 index 0000000000..0467b66f1b --- /dev/null +++ b/test/cucumber-tests/src/test/java/org/wso2/apk/integration/utils/GenericClientInterceptor.java @@ -0,0 +1,38 @@ +package org.wso2.apk.integration.utils; + +import io.grpc.ClientInterceptor; +import io.grpc.ForwardingClientCall; +import io.grpc.Metadata; +import io.grpc.MethodDescriptor; +import io.grpc.CallOptions; +import io.grpc.ClientCall; +import io.grpc.Channel; +import java.util.Map; + +import java.util.Map; + +public class GenericClientInterceptor implements ClientInterceptor { + + private Map headers; + + public GenericClientInterceptor(Map headers) { + this.headers = headers; + } + + @Override + public ClientCall interceptCall( + MethodDescriptor method, CallOptions callOptions, Channel next) { + return new ForwardingClientCall.SimpleForwardingClientCall( + next.newCall(method, callOptions)) { + + @Override + public void start(Listener responseListener, Metadata headersMetadata) { + // Set each header in the map to the Metadata headers + headers.forEach((key, value) -> headersMetadata.put( + Metadata.Key.of(key, Metadata.ASCII_STRING_MARSHALLER), value)); + + super.start(responseListener, headersMetadata); + } + }; + } +} \ No newline at end of file diff --git a/test/cucumber-tests/src/test/java/org/wso2/apk/integration/utils/clients/SimpleGRPCStudentClient.java b/test/cucumber-tests/src/test/java/org/wso2/apk/integration/utils/clients/SimpleGRPCStudentClient.java new file mode 100644 index 0000000000..29c22b8ef4 --- /dev/null +++ b/test/cucumber-tests/src/test/java/org/wso2/apk/integration/utils/clients/SimpleGRPCStudentClient.java @@ -0,0 +1,116 @@ +package org.wso2.apk.integration.utils.clients; + +import io.grpc.StatusRuntimeException; +import io.grpc.netty.GrpcSslContexts; +import io.grpc.netty.NettyChannelBuilder; +import io.netty.handler.ssl.SslContext; +import io.netty.handler.ssl.util.InsecureTrustManagerFactory; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import io.grpc.ManagedChannel; +import org.wso2.apk.integration.utils.GenericClientInterceptor; +import org.wso2.apk.integration.utils.clients.studentGrpcClient.StudentRequest; +import org.wso2.apk.integration.utils.clients.studentGrpcClient.StudentResponse; +import org.wso2.apk.integration.utils.clients.studentGrpcClient.StudentServiceDefaultVersionGrpc; +import org.wso2.apk.integration.utils.clients.studentGrpcClient.StudentServiceGrpc; + +import javax.net.ssl.SSLException; +import java.util.Map; +import java.util.concurrent.TimeUnit; + +public class SimpleGRPCStudentClient { + protected Log log = LogFactory.getLog(SimpleGRPCStudentClient.class); + private static final int EVENTUAL_SUCCESS_RESPONSE_TIMEOUT_IN_SECONDS = 10; + private final String host; + private final int port; + + public SimpleGRPCStudentClient(String host, int port) { + this.host = host; + this.port = port; + } + + public StudentResponse GetStudent(Map headers) throws StatusRuntimeException { + ManagedChannel managedChannel = null; + try { + SslContext sslContext = GrpcSslContexts.forClient() + .trustManager(InsecureTrustManagerFactory.INSTANCE) + .build(); + + GenericClientInterceptor interceptor = new GenericClientInterceptor(headers); + managedChannel = NettyChannelBuilder.forAddress(host, port) + .sslContext(sslContext) + .intercept(interceptor) + .build(); + StudentServiceGrpc.StudentServiceBlockingStub blockingStub = StudentServiceGrpc + .newBlockingStub(managedChannel); + if (blockingStub == null) { + log.error("Failed to create blocking stub"); + throw new RuntimeException("Failed to create blocking stub"); + } + StudentResponse response = blockingStub.getStudent(StudentRequest.newBuilder().setId(1).build()); + if (response == null) { + log.error("Failed to get student"); + throw new RuntimeException("Failed to get student"); + } + return response; + } catch (SSLException e) { + throw new RuntimeException("Failed to create SSL context", e); + } finally { + // Shut down the channel to release resources + if (managedChannel != null) { + managedChannel.shutdown(); // Initiates a graceful shutdown + try { + // Wait at most 5 seconds for the channel to terminate + if (!managedChannel.awaitTermination(5, TimeUnit.SECONDS)) { + managedChannel.shutdownNow(); // Force shutdown if it does not complete within the timeout + } + } catch (InterruptedException ie) { + managedChannel.shutdownNow(); // Force shutdown if the thread is interrupted + } + } + } + } + + public StudentResponse GetStudentDefaultVersion(Map headers) throws StatusRuntimeException { + ManagedChannel managedChannel = null; + try { + SslContext sslContext = GrpcSslContexts.forClient() + .trustManager(InsecureTrustManagerFactory.INSTANCE) + .build(); + + GenericClientInterceptor interceptor = new GenericClientInterceptor(headers); + managedChannel = NettyChannelBuilder.forAddress(host, port) + .sslContext(sslContext) + .intercept(interceptor) + .build(); + StudentServiceDefaultVersionGrpc.StudentServiceBlockingStub blockingStub = StudentServiceDefaultVersionGrpc + .newBlockingStub(managedChannel); + if (blockingStub == null) { + log.error("Failed to create blocking stub"); + throw new RuntimeException("Failed to create blocking stub"); + } + StudentResponse response = blockingStub.getStudent(StudentRequest.newBuilder().setId(1).build()); + if (response == null) { + log.error("Failed to get student"); + throw new RuntimeException("Failed to get student"); + } + return response; + } catch (SSLException e) { + throw new RuntimeException("Failed to create SSL context", e); + } finally { + // Shut down the channel to release resources + if (managedChannel != null) { + managedChannel.shutdown(); // Initiates a graceful shutdown + try { + // Wait at most 5 seconds for the channel to terminate + if (!managedChannel.awaitTermination(5, TimeUnit.SECONDS)) { + managedChannel.shutdownNow(); // Force shutdown if it does not complete within the timeout + } + } catch (InterruptedException ie) { + managedChannel.shutdownNow(); // Force shutdown if the thread is interrupted + } + } + } + } + +} diff --git a/test/cucumber-tests/src/test/java/org/wso2/apk/integration/utils/clients/studentGrpcClient/Student.java b/test/cucumber-tests/src/test/java/org/wso2/apk/integration/utils/clients/studentGrpcClient/Student.java new file mode 100644 index 0000000000..26a3831700 --- /dev/null +++ b/test/cucumber-tests/src/test/java/org/wso2/apk/integration/utils/clients/studentGrpcClient/Student.java @@ -0,0 +1,64 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// source: student.proto + +package org.wso2.apk.integration.utils.clients.studentGrpcClient; + +public final class Student { + private Student() { + } + + public static void registerAllExtensions( + com.google.protobuf.ExtensionRegistryLite registry) { + } + + public static void registerAllExtensions( + com.google.protobuf.ExtensionRegistry registry) { + registerAllExtensions( + (com.google.protobuf.ExtensionRegistryLite) registry); + } + + static final com.google.protobuf.Descriptors.Descriptor internal_static_dineth_grpc_v1_student_StudentRequest_descriptor; + static final com.google.protobuf.GeneratedMessageV3.FieldAccessorTable internal_static_dineth_grpc_v1_student_StudentRequest_fieldAccessorTable; + static final com.google.protobuf.Descriptors.Descriptor internal_static_dineth_grpc_v1_student_StudentResponse_descriptor; + static final com.google.protobuf.GeneratedMessageV3.FieldAccessorTable internal_static_dineth_grpc_v1_student_StudentResponse_fieldAccessorTable; + + public static com.google.protobuf.Descriptors.FileDescriptor getDescriptor() { + return descriptor; + } + + private static com.google.protobuf.Descriptors.FileDescriptor descriptor; + static { + String[] descriptorData = { + "\n\rstudent.proto\022\026dineth.grpc.v1.student\"" + + "\034\n\016StudentRequest\022\n\n\002id\030\003 \001(\005\",\n\017Student" + + "Response\022\014\n\004name\030\001 \001(\t\022\013\n\003age\030\002 \001(\0052\266\003\n\016" + + "StudentService\022_\n\nGetStudent\022&.dineth.gr" + + "pc.v1.student.StudentRequest\032\'.dineth.gr" + + "pc.v1.student.StudentResponse\"\000\022g\n\020GetSt" + + "udentStream\022&.dineth.grpc.v1.student.Stu" + + "dentRequest\032\'.dineth.grpc.v1.student.Stu" + + "dentResponse\"\0000\001\022h\n\021SendStudentStream\022&." + + "dineth.grpc.v1.student.StudentRequest\032\'." + + "dineth.grpc.v1.student.StudentResponse\"\000" + + "(\001\022p\n\027SendAndGetStudentStream\022&.dineth.g" + + "rpc.v1.student.StudentRequest\032\'.dineth.g" + + "rpc.v1.student.StudentResponse\"\000(\0010\001B<\n8" + + "org.wso2.apk.integration.utils.clients.s" + + "tudentGrpcClientP\001b\006proto3" + }; + descriptor = com.google.protobuf.Descriptors.FileDescriptor + .internalBuildGeneratedFileFrom(descriptorData, + new com.google.protobuf.Descriptors.FileDescriptor[] { + }); + internal_static_dineth_grpc_v1_student_StudentRequest_descriptor = getDescriptor().getMessageTypes().get(0); + internal_static_dineth_grpc_v1_student_StudentRequest_fieldAccessorTable = new com.google.protobuf.GeneratedMessageV3.FieldAccessorTable( + internal_static_dineth_grpc_v1_student_StudentRequest_descriptor, + new String[] { "Id", }); + internal_static_dineth_grpc_v1_student_StudentResponse_descriptor = getDescriptor().getMessageTypes().get(1); + internal_static_dineth_grpc_v1_student_StudentResponse_fieldAccessorTable = new com.google.protobuf.GeneratedMessageV3.FieldAccessorTable( + internal_static_dineth_grpc_v1_student_StudentResponse_descriptor, + new String[] { "Name", "Age", }); + } + + // @@protoc_insertion_point(outer_class_scope) +} \ No newline at end of file diff --git a/test/cucumber-tests/src/test/java/org/wso2/apk/integration/utils/clients/studentGrpcClient/StudentRequest.java b/test/cucumber-tests/src/test/java/org/wso2/apk/integration/utils/clients/studentGrpcClient/StudentRequest.java new file mode 100644 index 0000000000..d3df33d1c9 --- /dev/null +++ b/test/cucumber-tests/src/test/java/org/wso2/apk/integration/utils/clients/studentGrpcClient/StudentRequest.java @@ -0,0 +1,518 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// source: student.proto + +package org.wso2.apk.integration.utils.clients.studentGrpcClient; + +/** + * Protobuf type {@code dineth.grpc.v1.student.StudentRequest} + */ +public final class StudentRequest extends + com.google.protobuf.GeneratedMessageV3 implements + // @@protoc_insertion_point(message_implements:dineth.grpc.v1.student.StudentRequest) + StudentRequestOrBuilder { + private static final long serialVersionUID = 0L; + + // Use StudentRequest.newBuilder() to construct. + private StudentRequest(com.google.protobuf.GeneratedMessageV3.Builder builder) { + super(builder); + } + + private StudentRequest() { + } + + @Override + @SuppressWarnings({ "unused" }) + protected Object newInstance( + UnusedPrivateParameter unused) { + return new StudentRequest(); + } + + @Override + public final com.google.protobuf.UnknownFieldSet getUnknownFields() { + return this.unknownFields; + } + + private StudentRequest( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + this(); + if (extensionRegistry == null) { + throw new NullPointerException(); + } + com.google.protobuf.UnknownFieldSet.Builder unknownFields = com.google.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + case 24: { + + id_ = input.readInt32(); + break; + } + default: { + if (!parseUnknownField( + input, unknownFields, extensionRegistry, tag)) { + done = true; + } + break; + } + } + } + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException( + e).setUnfinishedMessage(this); + } finally { + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + + public static final com.google.protobuf.Descriptors.Descriptor getDescriptor() { + return Student.internal_static_dineth_grpc_v1_student_StudentRequest_descriptor; + } + + @Override + protected FieldAccessorTable internalGetFieldAccessorTable() { + return Student.internal_static_dineth_grpc_v1_student_StudentRequest_fieldAccessorTable + .ensureFieldAccessorsInitialized( + StudentRequest.class, Builder.class); + } + + public static final int ID_FIELD_NUMBER = 3; + private int id_; + + /** + * int32 id = 3; + * + * @return The id. + */ + @Override + public int getId() { + return id_; + } + + private byte memoizedIsInitialized = -1; + + @Override + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized == 1) + return true; + if (isInitialized == 0) + return false; + + memoizedIsInitialized = 1; + return true; + } + + @Override + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + if (id_ != 0) { + output.writeInt32(3, id_); + } + unknownFields.writeTo(output); + } + + @Override + public int getSerializedSize() { + int size = memoizedSize; + if (size != -1) + return size; + + size = 0; + if (id_ != 0) { + size += com.google.protobuf.CodedOutputStream + .computeInt32Size(3, id_); + } + size += unknownFields.getSerializedSize(); + memoizedSize = size; + return size; + } + + @Override + public boolean equals(final Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof StudentRequest)) { + return super.equals(obj); + } + StudentRequest other = (StudentRequest) obj; + + if (getId() != other.getId()) + return false; + if (!unknownFields.equals(other.unknownFields)) + return false; + return true; + } + + @Override + public int hashCode() { + if (memoizedHashCode != 0) { + return memoizedHashCode; + } + int hash = 41; + hash = (19 * hash) + getDescriptor().hashCode(); + hash = (37 * hash) + ID_FIELD_NUMBER; + hash = (53 * hash) + getId(); + hash = (29 * hash) + unknownFields.hashCode(); + memoizedHashCode = hash; + return hash; + } + + public static StudentRequest parseFrom( + java.nio.ByteBuffer data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + + public static StudentRequest parseFrom( + java.nio.ByteBuffer data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + + public static StudentRequest parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + + public static StudentRequest parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + + public static StudentRequest parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + + public static StudentRequest parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + + public static StudentRequest parseFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input); + } + + public static StudentRequest parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input, extensionRegistry); + } + + public static StudentRequest parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseDelimitedWithIOException(PARSER, input); + } + + public static StudentRequest parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseDelimitedWithIOException(PARSER, input, extensionRegistry); + } + + public static StudentRequest parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input); + } + + public static StudentRequest parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input, extensionRegistry); + } + + @Override + public Builder newBuilderForType() { + return newBuilder(); + } + + public static Builder newBuilder() { + return DEFAULT_INSTANCE.toBuilder(); + } + + public static Builder newBuilder(StudentRequest prototype) { + return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype); + } + + @Override + public Builder toBuilder() { + return this == DEFAULT_INSTANCE + ? new Builder() + : new Builder().mergeFrom(this); + } + + @Override + protected Builder newBuilderForType( + BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + + /** + * Protobuf type {@code dineth.grpc.v1.student.StudentRequest} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessageV3.Builder implements + // @@protoc_insertion_point(builder_implements:dineth.grpc.v1.student.StudentRequest) + StudentRequestOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor getDescriptor() { + return Student.internal_static_dineth_grpc_v1_student_StudentRequest_descriptor; + } + + @Override + protected FieldAccessorTable internalGetFieldAccessorTable() { + return Student.internal_static_dineth_grpc_v1_student_StudentRequest_fieldAccessorTable + .ensureFieldAccessorsInitialized( + StudentRequest.class, Builder.class); + } + + // Construct using + // org.wso2.apk.integration.utils.clients.studentGrpcClient.StudentRequest.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessageV3.alwaysUseFieldBuilders) { + } + } + + @Override + public Builder clear() { + super.clear(); + id_ = 0; + + return this; + } + + @Override + public com.google.protobuf.Descriptors.Descriptor getDescriptorForType() { + return Student.internal_static_dineth_grpc_v1_student_StudentRequest_descriptor; + } + + @Override + public StudentRequest getDefaultInstanceForType() { + return StudentRequest.getDefaultInstance(); + } + + @Override + public StudentRequest build() { + StudentRequest result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + @Override + public StudentRequest buildPartial() { + StudentRequest result = new StudentRequest(this); + result.id_ = id_; + onBuilt(); + return result; + } + + @Override + public Builder clone() { + return super.clone(); + } + + @Override + public Builder setField( + com.google.protobuf.Descriptors.FieldDescriptor field, + Object value) { + return super.setField(field, value); + } + + @Override + public Builder clearField( + com.google.protobuf.Descriptors.FieldDescriptor field) { + return super.clearField(field); + } + + @Override + public Builder clearOneof( + com.google.protobuf.Descriptors.OneofDescriptor oneof) { + return super.clearOneof(oneof); + } + + @Override + public Builder setRepeatedField( + com.google.protobuf.Descriptors.FieldDescriptor field, + int index, Object value) { + return super.setRepeatedField(field, index, value); + } + + @Override + public Builder addRepeatedField( + com.google.protobuf.Descriptors.FieldDescriptor field, + Object value) { + return super.addRepeatedField(field, value); + } + + @Override + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof StudentRequest) { + return mergeFrom((StudentRequest) other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(StudentRequest other) { + if (other == StudentRequest.getDefaultInstance()) + return this; + if (other.getId() != 0) { + setId(other.getId()); + } + this.mergeUnknownFields(other.unknownFields); + onChanged(); + return this; + } + + @Override + public final boolean isInitialized() { + return true; + } + + @Override + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + StudentRequest parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (StudentRequest) e.getUnfinishedMessage(); + throw e.unwrapIOException(); + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + + private int id_; + + /** + * int32 id = 3; + * + * @return The id. + */ + @Override + public int getId() { + return id_; + } + + /** + * int32 id = 3; + * + * @param value The id to set. + * @return This builder for chaining. + */ + public Builder setId(int value) { + + id_ = value; + onChanged(); + return this; + } + + /** + * int32 id = 3; + * + * @return This builder for chaining. + */ + public Builder clearId() { + + id_ = 0; + onChanged(); + return this; + } + + @Override + public final Builder setUnknownFields( + final com.google.protobuf.UnknownFieldSet unknownFields) { + return super.setUnknownFields(unknownFields); + } + + @Override + public final Builder mergeUnknownFields( + final com.google.protobuf.UnknownFieldSet unknownFields) { + return super.mergeUnknownFields(unknownFields); + } + + // @@protoc_insertion_point(builder_scope:dineth.grpc.v1.student.StudentRequest) + } + + // @@protoc_insertion_point(class_scope:dineth.grpc.v1.student.StudentRequest) + private static final StudentRequest DEFAULT_INSTANCE; + static { + DEFAULT_INSTANCE = new StudentRequest(); + } + + public static StudentRequest getDefaultInstance() { + return DEFAULT_INSTANCE; + } + + private static final com.google.protobuf.Parser PARSER = new com.google.protobuf.AbstractParser() { + @Override + public StudentRequest parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return new StudentRequest(input, extensionRegistry); + } + }; + + public static com.google.protobuf.Parser parser() { + return PARSER; + } + + @Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + @Override + public StudentRequest getDefaultInstanceForType() { + return DEFAULT_INSTANCE; + } + +} diff --git a/test/cucumber-tests/src/test/java/org/wso2/apk/integration/utils/clients/studentGrpcClient/StudentRequestOrBuilder.java b/test/cucumber-tests/src/test/java/org/wso2/apk/integration/utils/clients/studentGrpcClient/StudentRequestOrBuilder.java new file mode 100644 index 0000000000..e7beaba18f --- /dev/null +++ b/test/cucumber-tests/src/test/java/org/wso2/apk/integration/utils/clients/studentGrpcClient/StudentRequestOrBuilder.java @@ -0,0 +1,16 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// source: student.proto + +package org.wso2.apk.integration.utils.clients.studentGrpcClient; + +public interface StudentRequestOrBuilder extends + // @@protoc_insertion_point(interface_extends:dineth.grpc.v1.student.StudentRequest) + com.google.protobuf.MessageOrBuilder { + + /** + * int32 id = 3; + * + * @return The id. + */ + int getId(); +} \ No newline at end of file diff --git a/test/cucumber-tests/src/test/java/org/wso2/apk/integration/utils/clients/studentGrpcClient/StudentResponse.java b/test/cucumber-tests/src/test/java/org/wso2/apk/integration/utils/clients/studentGrpcClient/StudentResponse.java new file mode 100644 index 0000000000..af974ffa31 --- /dev/null +++ b/test/cucumber-tests/src/test/java/org/wso2/apk/integration/utils/clients/studentGrpcClient/StudentResponse.java @@ -0,0 +1,665 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// source: student.proto + +package org.wso2.apk.integration.utils.clients.studentGrpcClient; + +/** + * Protobuf type {@code dineth.grpc.v1.student.StudentResponse} + */ +public final class StudentResponse extends + com.google.protobuf.GeneratedMessageV3 implements + // @@protoc_insertion_point(message_implements:dineth.grpc.v1.student.StudentResponse) + StudentResponseOrBuilder { + private static final long serialVersionUID = 0L; + + // Use StudentResponse.newBuilder() to construct. + private StudentResponse(com.google.protobuf.GeneratedMessageV3.Builder builder) { + super(builder); + } + + private StudentResponse() { + name_ = ""; + } + + @Override + @SuppressWarnings({ "unused" }) + protected Object newInstance( + UnusedPrivateParameter unused) { + return new StudentResponse(); + } + + @Override + public final com.google.protobuf.UnknownFieldSet getUnknownFields() { + return this.unknownFields; + } + + private StudentResponse( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + this(); + if (extensionRegistry == null) { + throw new NullPointerException(); + } + com.google.protobuf.UnknownFieldSet.Builder unknownFields = com.google.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + case 10: { + String s = input.readStringRequireUtf8(); + + name_ = s; + break; + } + case 16: { + + age_ = input.readInt32(); + break; + } + default: { + if (!parseUnknownField( + input, unknownFields, extensionRegistry, tag)) { + done = true; + } + break; + } + } + } + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException( + e).setUnfinishedMessage(this); + } finally { + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + + public static final com.google.protobuf.Descriptors.Descriptor getDescriptor() { + return Student.internal_static_dineth_grpc_v1_student_StudentResponse_descriptor; + } + + @Override + protected FieldAccessorTable internalGetFieldAccessorTable() { + return Student.internal_static_dineth_grpc_v1_student_StudentResponse_fieldAccessorTable + .ensureFieldAccessorsInitialized( + StudentResponse.class, Builder.class); + } + + public static final int NAME_FIELD_NUMBER = 1; + private volatile Object name_; + + /** + * string name = 1; + * + * @return The name. + */ + @Override + public String getName() { + Object ref = name_; + if (ref instanceof String) { + return (String) ref; + } else { + com.google.protobuf.ByteString bs = (com.google.protobuf.ByteString) ref; + String s = bs.toStringUtf8(); + name_ = s; + return s; + } + } + + /** + * string name = 1; + * + * @return The bytes for name. + */ + @Override + public com.google.protobuf.ByteString getNameBytes() { + Object ref = name_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = com.google.protobuf.ByteString.copyFromUtf8( + (String) ref); + name_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + public static final int AGE_FIELD_NUMBER = 2; + private int age_; + + /** + * int32 age = 2; + * + * @return The age. + */ + @Override + public int getAge() { + return age_; + } + + private byte memoizedIsInitialized = -1; + + @Override + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized == 1) + return true; + if (isInitialized == 0) + return false; + + memoizedIsInitialized = 1; + return true; + } + + @Override + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + if (!getNameBytes().isEmpty()) { + com.google.protobuf.GeneratedMessageV3.writeString(output, 1, name_); + } + if (age_ != 0) { + output.writeInt32(2, age_); + } + unknownFields.writeTo(output); + } + + @Override + public int getSerializedSize() { + int size = memoizedSize; + if (size != -1) + return size; + + size = 0; + if (!getNameBytes().isEmpty()) { + size += com.google.protobuf.GeneratedMessageV3.computeStringSize(1, name_); + } + if (age_ != 0) { + size += com.google.protobuf.CodedOutputStream + .computeInt32Size(2, age_); + } + size += unknownFields.getSerializedSize(); + memoizedSize = size; + return size; + } + + @Override + public boolean equals(final Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof StudentResponse)) { + return super.equals(obj); + } + StudentResponse other = (StudentResponse) obj; + + if (!getName() + .equals(other.getName())) + return false; + if (getAge() != other.getAge()) + return false; + if (!unknownFields.equals(other.unknownFields)) + return false; + return true; + } + + @Override + public int hashCode() { + if (memoizedHashCode != 0) { + return memoizedHashCode; + } + int hash = 41; + hash = (19 * hash) + getDescriptor().hashCode(); + hash = (37 * hash) + NAME_FIELD_NUMBER; + hash = (53 * hash) + getName().hashCode(); + hash = (37 * hash) + AGE_FIELD_NUMBER; + hash = (53 * hash) + getAge(); + hash = (29 * hash) + unknownFields.hashCode(); + memoizedHashCode = hash; + return hash; + } + + public static StudentResponse parseFrom( + java.nio.ByteBuffer data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + + public static StudentResponse parseFrom( + java.nio.ByteBuffer data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + + public static StudentResponse parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + + public static StudentResponse parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + + public static StudentResponse parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + + public static StudentResponse parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + + public static StudentResponse parseFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input); + } + + public static StudentResponse parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input, extensionRegistry); + } + + public static StudentResponse parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseDelimitedWithIOException(PARSER, input); + } + + public static StudentResponse parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseDelimitedWithIOException(PARSER, input, extensionRegistry); + } + + public static StudentResponse parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input); + } + + public static StudentResponse parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input, extensionRegistry); + } + + @Override + public Builder newBuilderForType() { + return newBuilder(); + } + + public static Builder newBuilder() { + return DEFAULT_INSTANCE.toBuilder(); + } + + public static Builder newBuilder(StudentResponse prototype) { + return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype); + } + + @Override + public Builder toBuilder() { + return this == DEFAULT_INSTANCE + ? new Builder() + : new Builder().mergeFrom(this); + } + + @Override + protected Builder newBuilderForType( + BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + + /** + * Protobuf type {@code dineth.grpc.v1.student.StudentResponse} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessageV3.Builder implements + // @@protoc_insertion_point(builder_implements:dineth.grpc.v1.student.StudentResponse) + StudentResponseOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor getDescriptor() { + return Student.internal_static_dineth_grpc_v1_student_StudentResponse_descriptor; + } + + @Override + protected FieldAccessorTable internalGetFieldAccessorTable() { + return Student.internal_static_dineth_grpc_v1_student_StudentResponse_fieldAccessorTable + .ensureFieldAccessorsInitialized( + StudentResponse.class, Builder.class); + } + + // Construct using + // org.wso2.apk.integration.utils.clients.studentGrpcClient.StudentResponse.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessageV3.alwaysUseFieldBuilders) { + } + } + + @Override + public Builder clear() { + super.clear(); + name_ = ""; + + age_ = 0; + + return this; + } + + @Override + public com.google.protobuf.Descriptors.Descriptor getDescriptorForType() { + return Student.internal_static_dineth_grpc_v1_student_StudentResponse_descriptor; + } + + @Override + public StudentResponse getDefaultInstanceForType() { + return StudentResponse.getDefaultInstance(); + } + + @Override + public StudentResponse build() { + StudentResponse result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + @Override + public StudentResponse buildPartial() { + StudentResponse result = new StudentResponse(this); + result.name_ = name_; + result.age_ = age_; + onBuilt(); + return result; + } + + @Override + public Builder clone() { + return super.clone(); + } + + @Override + public Builder setField( + com.google.protobuf.Descriptors.FieldDescriptor field, + Object value) { + return super.setField(field, value); + } + + @Override + public Builder clearField( + com.google.protobuf.Descriptors.FieldDescriptor field) { + return super.clearField(field); + } + + @Override + public Builder clearOneof( + com.google.protobuf.Descriptors.OneofDescriptor oneof) { + return super.clearOneof(oneof); + } + + @Override + public Builder setRepeatedField( + com.google.protobuf.Descriptors.FieldDescriptor field, + int index, Object value) { + return super.setRepeatedField(field, index, value); + } + + @Override + public Builder addRepeatedField( + com.google.protobuf.Descriptors.FieldDescriptor field, + Object value) { + return super.addRepeatedField(field, value); + } + + @Override + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof StudentResponse) { + return mergeFrom((StudentResponse) other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(StudentResponse other) { + if (other == StudentResponse.getDefaultInstance()) + return this; + if (!other.getName().isEmpty()) { + name_ = other.name_; + onChanged(); + } + if (other.getAge() != 0) { + setAge(other.getAge()); + } + this.mergeUnknownFields(other.unknownFields); + onChanged(); + return this; + } + + @Override + public final boolean isInitialized() { + return true; + } + + @Override + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + StudentResponse parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (StudentResponse) e.getUnfinishedMessage(); + throw e.unwrapIOException(); + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + + private Object name_ = ""; + + /** + * string name = 1; + * + * @return The name. + */ + public String getName() { + Object ref = name_; + if (!(ref instanceof String)) { + com.google.protobuf.ByteString bs = (com.google.protobuf.ByteString) ref; + String s = bs.toStringUtf8(); + name_ = s; + return s; + } else { + return (String) ref; + } + } + + /** + * string name = 1; + * + * @return The bytes for name. + */ + public com.google.protobuf.ByteString getNameBytes() { + Object ref = name_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = com.google.protobuf.ByteString.copyFromUtf8( + (String) ref); + name_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + /** + * string name = 1; + * + * @param value The name to set. + * @return This builder for chaining. + */ + public Builder setName( + String value) { + if (value == null) { + throw new NullPointerException(); + } + + name_ = value; + onChanged(); + return this; + } + + /** + * string name = 1; + * + * @return This builder for chaining. + */ + public Builder clearName() { + + name_ = getDefaultInstance().getName(); + onChanged(); + return this; + } + + /** + * string name = 1; + * + * @param value The bytes for name to set. + * @return This builder for chaining. + */ + public Builder setNameBytes( + com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + checkByteStringIsUtf8(value); + + name_ = value; + onChanged(); + return this; + } + + private int age_; + + /** + * int32 age = 2; + * + * @return The age. + */ + @Override + public int getAge() { + return age_; + } + + /** + * int32 age = 2; + * + * @param value The age to set. + * @return This builder for chaining. + */ + public Builder setAge(int value) { + + age_ = value; + onChanged(); + return this; + } + + /** + * int32 age = 2; + * + * @return This builder for chaining. + */ + public Builder clearAge() { + + age_ = 0; + onChanged(); + return this; + } + + @Override + public final Builder setUnknownFields( + final com.google.protobuf.UnknownFieldSet unknownFields) { + return super.setUnknownFields(unknownFields); + } + + @Override + public final Builder mergeUnknownFields( + final com.google.protobuf.UnknownFieldSet unknownFields) { + return super.mergeUnknownFields(unknownFields); + } + + // @@protoc_insertion_point(builder_scope:dineth.grpc.v1.student.StudentResponse) + } + + // @@protoc_insertion_point(class_scope:dineth.grpc.v1.student.StudentResponse) + private static final StudentResponse DEFAULT_INSTANCE; + static { + DEFAULT_INSTANCE = new StudentResponse(); + } + + public static StudentResponse getDefaultInstance() { + return DEFAULT_INSTANCE; + } + + private static final com.google.protobuf.Parser PARSER = new com.google.protobuf.AbstractParser() { + @Override + public StudentResponse parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return new StudentResponse(input, extensionRegistry); + } + }; + + public static com.google.protobuf.Parser parser() { + return PARSER; + } + + @Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + @Override + public StudentResponse getDefaultInstanceForType() { + return DEFAULT_INSTANCE; + } + +} diff --git a/test/cucumber-tests/src/test/java/org/wso2/apk/integration/utils/clients/studentGrpcClient/StudentResponseOrBuilder.java b/test/cucumber-tests/src/test/java/org/wso2/apk/integration/utils/clients/studentGrpcClient/StudentResponseOrBuilder.java new file mode 100644 index 0000000000..ffb501c687 --- /dev/null +++ b/test/cucumber-tests/src/test/java/org/wso2/apk/integration/utils/clients/studentGrpcClient/StudentResponseOrBuilder.java @@ -0,0 +1,30 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// source: student.proto + +package org.wso2.apk.integration.utils.clients.studentGrpcClient; + +public interface StudentResponseOrBuilder extends + // @@protoc_insertion_point(interface_extends:dineth.grpc.v1.student.StudentResponse) + com.google.protobuf.MessageOrBuilder { + + /** + * string name = 1; + * + * @return The name. + */ + String getName(); + + /** + * string name = 1; + * + * @return The bytes for name. + */ + com.google.protobuf.ByteString getNameBytes(); + + /** + * int32 age = 2; + * + * @return The age. + */ + int getAge(); +} \ No newline at end of file diff --git a/test/cucumber-tests/src/test/java/org/wso2/apk/integration/utils/clients/studentGrpcClient/StudentServiceDefaultVersionGrpc.java b/test/cucumber-tests/src/test/java/org/wso2/apk/integration/utils/clients/studentGrpcClient/StudentServiceDefaultVersionGrpc.java new file mode 100644 index 0000000000..52b7ed799a --- /dev/null +++ b/test/cucumber-tests/src/test/java/org/wso2/apk/integration/utils/clients/studentGrpcClient/StudentServiceDefaultVersionGrpc.java @@ -0,0 +1,435 @@ +package org.wso2.apk.integration.utils.clients.studentGrpcClient; + +import static io.grpc.MethodDescriptor.generateFullMethodName; + +/** + */ +@javax.annotation.Generated(value = "by gRPC proto compiler (version 1.39.0)", comments = "Source: student.proto") +public final class StudentServiceDefaultVersionGrpc { + + private StudentServiceDefaultVersionGrpc() { + } + + public static final String SERVICE_NAME = "dineth.grpc.api.student.StudentService"; + + // Static method descriptors that strictly reflect the proto. + private static volatile io.grpc.MethodDescriptor getGetStudentMethod; + + @io.grpc.stub.annotations.RpcMethod(fullMethodName = SERVICE_NAME + '/' + + "GetStudent", requestType = StudentRequest.class, responseType = StudentResponse.class, methodType = io.grpc.MethodDescriptor.MethodType.UNARY) + public static io.grpc.MethodDescriptor getGetStudentMethod() { + io.grpc.MethodDescriptor getGetStudentMethod; + if ((getGetStudentMethod = StudentServiceDefaultVersionGrpc.getGetStudentMethod) == null) { + synchronized (StudentServiceDefaultVersionGrpc.class) { + if ((getGetStudentMethod = StudentServiceDefaultVersionGrpc.getGetStudentMethod) == null) { + StudentServiceDefaultVersionGrpc.getGetStudentMethod = getGetStudentMethod = io.grpc.MethodDescriptor + .newBuilder() + .setType(io.grpc.MethodDescriptor.MethodType.UNARY) + .setFullMethodName(generateFullMethodName(SERVICE_NAME, "GetStudent")) + .setSampledToLocalTracing(true) + .setRequestMarshaller(io.grpc.protobuf.ProtoUtils.marshaller( + StudentRequest.getDefaultInstance())) + .setResponseMarshaller(io.grpc.protobuf.ProtoUtils.marshaller( + StudentResponse.getDefaultInstance())) + .setSchemaDescriptor(new StudentServiceMethodDescriptorSupplier("GetStudent")) + .build(); + } + } + } + return getGetStudentMethod; + } + + private static volatile io.grpc.MethodDescriptor getGetStudentStreamMethod; + + @io.grpc.stub.annotations.RpcMethod(fullMethodName = SERVICE_NAME + '/' + + "GetStudentStream", requestType = StudentRequest.class, responseType = StudentResponse.class, methodType = io.grpc.MethodDescriptor.MethodType.SERVER_STREAMING) + public static io.grpc.MethodDescriptor getGetStudentStreamMethod() { + io.grpc.MethodDescriptor getGetStudentStreamMethod; + if ((getGetStudentStreamMethod = StudentServiceDefaultVersionGrpc.getGetStudentStreamMethod) == null) { + synchronized (StudentServiceDefaultVersionGrpc.class) { + if ((getGetStudentStreamMethod = StudentServiceDefaultVersionGrpc.getGetStudentStreamMethod) == null) { + StudentServiceDefaultVersionGrpc.getGetStudentStreamMethod = getGetStudentStreamMethod = io.grpc.MethodDescriptor + .newBuilder() + .setType(io.grpc.MethodDescriptor.MethodType.SERVER_STREAMING) + .setFullMethodName(generateFullMethodName(SERVICE_NAME, "GetStudentStream")) + .setSampledToLocalTracing(true) + .setRequestMarshaller(io.grpc.protobuf.ProtoUtils.marshaller( + StudentRequest.getDefaultInstance())) + .setResponseMarshaller(io.grpc.protobuf.ProtoUtils.marshaller( + StudentResponse.getDefaultInstance())) + .setSchemaDescriptor(new StudentServiceMethodDescriptorSupplier("GetStudentStream")) + .build(); + } + } + } + return getGetStudentStreamMethod; + } + + private static volatile io.grpc.MethodDescriptor getSendStudentStreamMethod; + + @io.grpc.stub.annotations.RpcMethod(fullMethodName = SERVICE_NAME + '/' + + "SendStudentStream", requestType = StudentRequest.class, responseType = StudentResponse.class, methodType = io.grpc.MethodDescriptor.MethodType.CLIENT_STREAMING) + public static io.grpc.MethodDescriptor getSendStudentStreamMethod() { + io.grpc.MethodDescriptor getSendStudentStreamMethod; + if ((getSendStudentStreamMethod = StudentServiceDefaultVersionGrpc.getSendStudentStreamMethod) == null) { + synchronized (StudentServiceDefaultVersionGrpc.class) { + if ((getSendStudentStreamMethod = StudentServiceDefaultVersionGrpc.getSendStudentStreamMethod) == null) { + StudentServiceDefaultVersionGrpc.getSendStudentStreamMethod = getSendStudentStreamMethod = io.grpc.MethodDescriptor + .newBuilder() + .setType(io.grpc.MethodDescriptor.MethodType.CLIENT_STREAMING) + .setFullMethodName(generateFullMethodName(SERVICE_NAME, "SendStudentStream")) + .setSampledToLocalTracing(true) + .setRequestMarshaller(io.grpc.protobuf.ProtoUtils.marshaller( + StudentRequest.getDefaultInstance())) + .setResponseMarshaller(io.grpc.protobuf.ProtoUtils.marshaller( + StudentResponse.getDefaultInstance())) + .setSchemaDescriptor(new StudentServiceMethodDescriptorSupplier("SendStudentStream")) + .build(); + } + } + } + return getSendStudentStreamMethod; + } + + private static volatile io.grpc.MethodDescriptor getSendAndGetStudentStreamMethod; + + @io.grpc.stub.annotations.RpcMethod(fullMethodName = SERVICE_NAME + '/' + + "SendAndGetStudentStream", requestType = StudentRequest.class, responseType = StudentResponse.class, methodType = io.grpc.MethodDescriptor.MethodType.BIDI_STREAMING) + public static io.grpc.MethodDescriptor getSendAndGetStudentStreamMethod() { + io.grpc.MethodDescriptor getSendAndGetStudentStreamMethod; + if ((getSendAndGetStudentStreamMethod = StudentServiceDefaultVersionGrpc.getSendAndGetStudentStreamMethod) == null) { + synchronized (StudentServiceDefaultVersionGrpc.class) { + if ((getSendAndGetStudentStreamMethod = StudentServiceDefaultVersionGrpc.getSendAndGetStudentStreamMethod) == null) { + StudentServiceDefaultVersionGrpc.getSendAndGetStudentStreamMethod = getSendAndGetStudentStreamMethod = io.grpc.MethodDescriptor + .newBuilder() + .setType(io.grpc.MethodDescriptor.MethodType.BIDI_STREAMING) + .setFullMethodName(generateFullMethodName(SERVICE_NAME, "SendAndGetStudentStream")) + .setSampledToLocalTracing(true) + .setRequestMarshaller(io.grpc.protobuf.ProtoUtils.marshaller( + StudentRequest.getDefaultInstance())) + .setResponseMarshaller(io.grpc.protobuf.ProtoUtils.marshaller( + StudentResponse.getDefaultInstance())) + .setSchemaDescriptor(new StudentServiceMethodDescriptorSupplier("SendAndGetStudentStream")) + .build(); + } + } + } + return getSendAndGetStudentStreamMethod; + } + + /** + * Creates a new async stub that supports all call types for the service + */ + public static StudentServiceStub newStub(io.grpc.Channel channel) { + io.grpc.stub.AbstractStub.StubFactory factory = new io.grpc.stub.AbstractStub.StubFactory() { + @Override + public StudentServiceStub newStub(io.grpc.Channel channel, io.grpc.CallOptions callOptions) { + return new StudentServiceStub(channel, callOptions); + } + }; + return StudentServiceStub.newStub(factory, channel); + } + + /** + * Creates a new blocking-style stub that supports unary and streaming output + * calls on the service + */ + public static StudentServiceBlockingStub newBlockingStub( + io.grpc.Channel channel) { + io.grpc.stub.AbstractStub.StubFactory factory = new io.grpc.stub.AbstractStub.StubFactory() { + @Override + public StudentServiceBlockingStub newStub(io.grpc.Channel channel, io.grpc.CallOptions callOptions) { + return new StudentServiceBlockingStub(channel, callOptions); + } + }; + return StudentServiceBlockingStub.newStub(factory, channel); + } + + /** + * Creates a new ListenableFuture-style stub that supports unary calls on the + * service + */ + public static StudentServiceFutureStub newFutureStub( + io.grpc.Channel channel) { + io.grpc.stub.AbstractStub.StubFactory factory = new io.grpc.stub.AbstractStub.StubFactory() { + @Override + public StudentServiceFutureStub newStub(io.grpc.Channel channel, io.grpc.CallOptions callOptions) { + return new StudentServiceFutureStub(channel, callOptions); + } + }; + return StudentServiceFutureStub.newStub(factory, channel); + } + + /** + */ + public static abstract class StudentServiceImplBase implements io.grpc.BindableService { + + /** + */ + public void getStudent(StudentRequest request, + io.grpc.stub.StreamObserver responseObserver) { + io.grpc.stub.ServerCalls.asyncUnimplementedUnaryCall(getGetStudentMethod(), responseObserver); + } + + /** + */ + public void getStudentStream(StudentRequest request, + io.grpc.stub.StreamObserver responseObserver) { + io.grpc.stub.ServerCalls.asyncUnimplementedUnaryCall(getGetStudentStreamMethod(), responseObserver); + } + + /** + */ + public io.grpc.stub.StreamObserver sendStudentStream( + io.grpc.stub.StreamObserver responseObserver) { + return io.grpc.stub.ServerCalls.asyncUnimplementedStreamingCall(getSendStudentStreamMethod(), + responseObserver); + } + + /** + */ + public io.grpc.stub.StreamObserver sendAndGetStudentStream( + io.grpc.stub.StreamObserver responseObserver) { + return io.grpc.stub.ServerCalls.asyncUnimplementedStreamingCall(getSendAndGetStudentStreamMethod(), + responseObserver); + } + + @Override + public final io.grpc.ServerServiceDefinition bindService() { + return io.grpc.ServerServiceDefinition.builder(getServiceDescriptor()) + .addMethod( + getGetStudentMethod(), + io.grpc.stub.ServerCalls.asyncUnaryCall( + new MethodHandlers( + this, METHODID_GET_STUDENT))) + .addMethod( + getGetStudentStreamMethod(), + io.grpc.stub.ServerCalls.asyncServerStreamingCall( + new MethodHandlers( + this, METHODID_GET_STUDENT_STREAM))) + .addMethod( + getSendStudentStreamMethod(), + io.grpc.stub.ServerCalls.asyncClientStreamingCall( + new MethodHandlers( + this, METHODID_SEND_STUDENT_STREAM))) + .addMethod( + getSendAndGetStudentStreamMethod(), + io.grpc.stub.ServerCalls.asyncBidiStreamingCall( + new MethodHandlers( + this, METHODID_SEND_AND_GET_STUDENT_STREAM))) + .build(); + } + } + + /** + */ + public static final class StudentServiceStub extends io.grpc.stub.AbstractAsyncStub { + private StudentServiceStub( + io.grpc.Channel channel, io.grpc.CallOptions callOptions) { + super(channel, callOptions); + } + + @Override + protected StudentServiceStub build( + io.grpc.Channel channel, io.grpc.CallOptions callOptions) { + return new StudentServiceStub(channel, callOptions); + } + + /** + */ + public void getStudent(StudentRequest request, + io.grpc.stub.StreamObserver responseObserver) { + io.grpc.stub.ClientCalls.asyncUnaryCall( + getChannel().newCall(getGetStudentMethod(), getCallOptions()), request, responseObserver); + } + + /** + */ + public void getStudentStream(StudentRequest request, + io.grpc.stub.StreamObserver responseObserver) { + io.grpc.stub.ClientCalls.asyncServerStreamingCall( + getChannel().newCall(getGetStudentStreamMethod(), getCallOptions()), request, responseObserver); + } + + /** + */ + public io.grpc.stub.StreamObserver sendStudentStream( + io.grpc.stub.StreamObserver responseObserver) { + return io.grpc.stub.ClientCalls.asyncClientStreamingCall( + getChannel().newCall(getSendStudentStreamMethod(), getCallOptions()), responseObserver); + } + + /** + */ + public io.grpc.stub.StreamObserver sendAndGetStudentStream( + io.grpc.stub.StreamObserver responseObserver) { + return io.grpc.stub.ClientCalls.asyncBidiStreamingCall( + getChannel().newCall(getSendAndGetStudentStreamMethod(), getCallOptions()), responseObserver); + } + } + + /** + */ + public static final class StudentServiceBlockingStub + extends io.grpc.stub.AbstractBlockingStub { + private StudentServiceBlockingStub( + io.grpc.Channel channel, io.grpc.CallOptions callOptions) { + super(channel, callOptions); + } + + @Override + protected StudentServiceBlockingStub build( + io.grpc.Channel channel, io.grpc.CallOptions callOptions) { + return new StudentServiceBlockingStub(channel, callOptions); + } + + /** + */ + public StudentResponse getStudent(StudentRequest request) { + return io.grpc.stub.ClientCalls.blockingUnaryCall( + getChannel(), getGetStudentMethod(), getCallOptions(), request); + } + + /** + */ + public java.util.Iterator getStudentStream( + StudentRequest request) { + return io.grpc.stub.ClientCalls.blockingServerStreamingCall( + getChannel(), getGetStudentStreamMethod(), getCallOptions(), request); + } + } + + /** + */ + public static final class StudentServiceFutureStub + extends io.grpc.stub.AbstractFutureStub { + private StudentServiceFutureStub( + io.grpc.Channel channel, io.grpc.CallOptions callOptions) { + super(channel, callOptions); + } + + @Override + protected StudentServiceFutureStub build( + io.grpc.Channel channel, io.grpc.CallOptions callOptions) { + return new StudentServiceFutureStub(channel, callOptions); + } + + /** + */ + public com.google.common.util.concurrent.ListenableFuture getStudent( + StudentRequest request) { + return io.grpc.stub.ClientCalls.futureUnaryCall( + getChannel().newCall(getGetStudentMethod(), getCallOptions()), request); + } + } + + private static final int METHODID_GET_STUDENT = 0; + private static final int METHODID_GET_STUDENT_STREAM = 1; + private static final int METHODID_SEND_STUDENT_STREAM = 2; + private static final int METHODID_SEND_AND_GET_STUDENT_STREAM = 3; + + private static final class MethodHandlers implements + io.grpc.stub.ServerCalls.UnaryMethod, + io.grpc.stub.ServerCalls.ServerStreamingMethod, + io.grpc.stub.ServerCalls.ClientStreamingMethod, + io.grpc.stub.ServerCalls.BidiStreamingMethod { + private final StudentServiceImplBase serviceImpl; + private final int methodId; + + MethodHandlers(StudentServiceImplBase serviceImpl, int methodId) { + this.serviceImpl = serviceImpl; + this.methodId = methodId; + } + + @Override + @SuppressWarnings("unchecked") + public void invoke(Req request, io.grpc.stub.StreamObserver responseObserver) { + switch (methodId) { + case METHODID_GET_STUDENT: + serviceImpl.getStudent((StudentRequest) request, + (io.grpc.stub.StreamObserver) responseObserver); + break; + case METHODID_GET_STUDENT_STREAM: + serviceImpl.getStudentStream((StudentRequest) request, + (io.grpc.stub.StreamObserver) responseObserver); + break; + default: + throw new AssertionError(); + } + } + + @Override + @SuppressWarnings("unchecked") + public io.grpc.stub.StreamObserver invoke( + io.grpc.stub.StreamObserver responseObserver) { + switch (methodId) { + case METHODID_SEND_STUDENT_STREAM: + return (io.grpc.stub.StreamObserver) serviceImpl.sendStudentStream( + (io.grpc.stub.StreamObserver) responseObserver); + case METHODID_SEND_AND_GET_STUDENT_STREAM: + return (io.grpc.stub.StreamObserver) serviceImpl.sendAndGetStudentStream( + (io.grpc.stub.StreamObserver) responseObserver); + default: + throw new AssertionError(); + } + } + } + + private static abstract class StudentServiceBaseDescriptorSupplier + implements io.grpc.protobuf.ProtoFileDescriptorSupplier, io.grpc.protobuf.ProtoServiceDescriptorSupplier { + StudentServiceBaseDescriptorSupplier() { + } + + @Override + public com.google.protobuf.Descriptors.FileDescriptor getFileDescriptor() { + return Student.getDescriptor(); + } + + @Override + public com.google.protobuf.Descriptors.ServiceDescriptor getServiceDescriptor() { + return getFileDescriptor().findServiceByName("StudentService"); + } + } + + private static final class StudentServiceFileDescriptorSupplier + extends StudentServiceBaseDescriptorSupplier { + StudentServiceFileDescriptorSupplier() { + } + } + + private static final class StudentServiceMethodDescriptorSupplier + extends StudentServiceBaseDescriptorSupplier + implements io.grpc.protobuf.ProtoMethodDescriptorSupplier { + private final String methodName; + + StudentServiceMethodDescriptorSupplier(String methodName) { + this.methodName = methodName; + } + + @Override + public com.google.protobuf.Descriptors.MethodDescriptor getMethodDescriptor() { + return getServiceDescriptor().findMethodByName(methodName); + } + } + + private static volatile io.grpc.ServiceDescriptor serviceDescriptor; + + public static io.grpc.ServiceDescriptor getServiceDescriptor() { + io.grpc.ServiceDescriptor result = serviceDescriptor; + if (result == null) { + synchronized (StudentServiceDefaultVersionGrpc.class) { + result = serviceDescriptor; + if (result == null) { + serviceDescriptor = result = io.grpc.ServiceDescriptor.newBuilder(SERVICE_NAME) + .setSchemaDescriptor(new StudentServiceFileDescriptorSupplier()) + .addMethod(getGetStudentMethod()) + .addMethod(getGetStudentStreamMethod()) + .addMethod(getSendStudentStreamMethod()) + .addMethod(getSendAndGetStudentStreamMethod()) + .build(); + } + } + } + return result; + } +} \ No newline at end of file diff --git a/test/cucumber-tests/src/test/java/org/wso2/apk/integration/utils/clients/studentGrpcClient/StudentServiceGrpc.java b/test/cucumber-tests/src/test/java/org/wso2/apk/integration/utils/clients/studentGrpcClient/StudentServiceGrpc.java new file mode 100644 index 0000000000..376c17a053 --- /dev/null +++ b/test/cucumber-tests/src/test/java/org/wso2/apk/integration/utils/clients/studentGrpcClient/StudentServiceGrpc.java @@ -0,0 +1,435 @@ +package org.wso2.apk.integration.utils.clients.studentGrpcClient; + +import static io.grpc.MethodDescriptor.generateFullMethodName; + +/** + */ +@javax.annotation.Generated(value = "by gRPC proto compiler (version 1.39.0)", comments = "Source: student.proto") +public final class StudentServiceGrpc { + + private StudentServiceGrpc() { + } + + public static final String SERVICE_NAME = "dineth.grpc.api.v1.student.StudentService"; + + // Static method descriptors that strictly reflect the proto. + private static volatile io.grpc.MethodDescriptor getGetStudentMethod; + + @io.grpc.stub.annotations.RpcMethod(fullMethodName = SERVICE_NAME + '/' + + "GetStudent", requestType = StudentRequest.class, responseType = StudentResponse.class, methodType = io.grpc.MethodDescriptor.MethodType.UNARY) + public static io.grpc.MethodDescriptor getGetStudentMethod() { + io.grpc.MethodDescriptor getGetStudentMethod; + if ((getGetStudentMethod = StudentServiceGrpc.getGetStudentMethod) == null) { + synchronized (StudentServiceGrpc.class) { + if ((getGetStudentMethod = StudentServiceGrpc.getGetStudentMethod) == null) { + StudentServiceGrpc.getGetStudentMethod = getGetStudentMethod = io.grpc.MethodDescriptor + .newBuilder() + .setType(io.grpc.MethodDescriptor.MethodType.UNARY) + .setFullMethodName(generateFullMethodName(SERVICE_NAME, "GetStudent")) + .setSampledToLocalTracing(true) + .setRequestMarshaller(io.grpc.protobuf.ProtoUtils.marshaller( + StudentRequest.getDefaultInstance())) + .setResponseMarshaller(io.grpc.protobuf.ProtoUtils.marshaller( + StudentResponse.getDefaultInstance())) + .setSchemaDescriptor(new StudentServiceMethodDescriptorSupplier("GetStudent")) + .build(); + } + } + } + return getGetStudentMethod; + } + + private static volatile io.grpc.MethodDescriptor getGetStudentStreamMethod; + + @io.grpc.stub.annotations.RpcMethod(fullMethodName = SERVICE_NAME + '/' + + "GetStudentStream", requestType = StudentRequest.class, responseType = StudentResponse.class, methodType = io.grpc.MethodDescriptor.MethodType.SERVER_STREAMING) + public static io.grpc.MethodDescriptor getGetStudentStreamMethod() { + io.grpc.MethodDescriptor getGetStudentStreamMethod; + if ((getGetStudentStreamMethod = StudentServiceGrpc.getGetStudentStreamMethod) == null) { + synchronized (StudentServiceGrpc.class) { + if ((getGetStudentStreamMethod = StudentServiceGrpc.getGetStudentStreamMethod) == null) { + StudentServiceGrpc.getGetStudentStreamMethod = getGetStudentStreamMethod = io.grpc.MethodDescriptor + .newBuilder() + .setType(io.grpc.MethodDescriptor.MethodType.SERVER_STREAMING) + .setFullMethodName(generateFullMethodName(SERVICE_NAME, "GetStudentStream")) + .setSampledToLocalTracing(true) + .setRequestMarshaller(io.grpc.protobuf.ProtoUtils.marshaller( + StudentRequest.getDefaultInstance())) + .setResponseMarshaller(io.grpc.protobuf.ProtoUtils.marshaller( + StudentResponse.getDefaultInstance())) + .setSchemaDescriptor(new StudentServiceMethodDescriptorSupplier("GetStudentStream")) + .build(); + } + } + } + return getGetStudentStreamMethod; + } + + private static volatile io.grpc.MethodDescriptor getSendStudentStreamMethod; + + @io.grpc.stub.annotations.RpcMethod(fullMethodName = SERVICE_NAME + '/' + + "SendStudentStream", requestType = StudentRequest.class, responseType = StudentResponse.class, methodType = io.grpc.MethodDescriptor.MethodType.CLIENT_STREAMING) + public static io.grpc.MethodDescriptor getSendStudentStreamMethod() { + io.grpc.MethodDescriptor getSendStudentStreamMethod; + if ((getSendStudentStreamMethod = StudentServiceGrpc.getSendStudentStreamMethod) == null) { + synchronized (StudentServiceGrpc.class) { + if ((getSendStudentStreamMethod = StudentServiceGrpc.getSendStudentStreamMethod) == null) { + StudentServiceGrpc.getSendStudentStreamMethod = getSendStudentStreamMethod = io.grpc.MethodDescriptor + .newBuilder() + .setType(io.grpc.MethodDescriptor.MethodType.CLIENT_STREAMING) + .setFullMethodName(generateFullMethodName(SERVICE_NAME, "SendStudentStream")) + .setSampledToLocalTracing(true) + .setRequestMarshaller(io.grpc.protobuf.ProtoUtils.marshaller( + StudentRequest.getDefaultInstance())) + .setResponseMarshaller(io.grpc.protobuf.ProtoUtils.marshaller( + StudentResponse.getDefaultInstance())) + .setSchemaDescriptor(new StudentServiceMethodDescriptorSupplier("SendStudentStream")) + .build(); + } + } + } + return getSendStudentStreamMethod; + } + + private static volatile io.grpc.MethodDescriptor getSendAndGetStudentStreamMethod; + + @io.grpc.stub.annotations.RpcMethod(fullMethodName = SERVICE_NAME + '/' + + "SendAndGetStudentStream", requestType = StudentRequest.class, responseType = StudentResponse.class, methodType = io.grpc.MethodDescriptor.MethodType.BIDI_STREAMING) + public static io.grpc.MethodDescriptor getSendAndGetStudentStreamMethod() { + io.grpc.MethodDescriptor getSendAndGetStudentStreamMethod; + if ((getSendAndGetStudentStreamMethod = StudentServiceGrpc.getSendAndGetStudentStreamMethod) == null) { + synchronized (StudentServiceGrpc.class) { + if ((getSendAndGetStudentStreamMethod = StudentServiceGrpc.getSendAndGetStudentStreamMethod) == null) { + StudentServiceGrpc.getSendAndGetStudentStreamMethod = getSendAndGetStudentStreamMethod = io.grpc.MethodDescriptor + .newBuilder() + .setType(io.grpc.MethodDescriptor.MethodType.BIDI_STREAMING) + .setFullMethodName(generateFullMethodName(SERVICE_NAME, "SendAndGetStudentStream")) + .setSampledToLocalTracing(true) + .setRequestMarshaller(io.grpc.protobuf.ProtoUtils.marshaller( + StudentRequest.getDefaultInstance())) + .setResponseMarshaller(io.grpc.protobuf.ProtoUtils.marshaller( + StudentResponse.getDefaultInstance())) + .setSchemaDescriptor(new StudentServiceMethodDescriptorSupplier("SendAndGetStudentStream")) + .build(); + } + } + } + return getSendAndGetStudentStreamMethod; + } + + /** + * Creates a new async stub that supports all call types for the service + */ + public static StudentServiceStub newStub(io.grpc.Channel channel) { + io.grpc.stub.AbstractStub.StubFactory factory = new io.grpc.stub.AbstractStub.StubFactory() { + @Override + public StudentServiceStub newStub(io.grpc.Channel channel, io.grpc.CallOptions callOptions) { + return new StudentServiceStub(channel, callOptions); + } + }; + return StudentServiceStub.newStub(factory, channel); + } + + /** + * Creates a new blocking-style stub that supports unary and streaming output + * calls on the service + */ + public static StudentServiceBlockingStub newBlockingStub( + io.grpc.Channel channel) { + io.grpc.stub.AbstractStub.StubFactory factory = new io.grpc.stub.AbstractStub.StubFactory() { + @Override + public StudentServiceBlockingStub newStub(io.grpc.Channel channel, io.grpc.CallOptions callOptions) { + return new StudentServiceBlockingStub(channel, callOptions); + } + }; + return StudentServiceBlockingStub.newStub(factory, channel); + } + + /** + * Creates a new ListenableFuture-style stub that supports unary calls on the + * service + */ + public static StudentServiceFutureStub newFutureStub( + io.grpc.Channel channel) { + io.grpc.stub.AbstractStub.StubFactory factory = new io.grpc.stub.AbstractStub.StubFactory() { + @Override + public StudentServiceFutureStub newStub(io.grpc.Channel channel, io.grpc.CallOptions callOptions) { + return new StudentServiceFutureStub(channel, callOptions); + } + }; + return StudentServiceFutureStub.newStub(factory, channel); + } + + /** + */ + public static abstract class StudentServiceImplBase implements io.grpc.BindableService { + + /** + */ + public void getStudent(StudentRequest request, + io.grpc.stub.StreamObserver responseObserver) { + io.grpc.stub.ServerCalls.asyncUnimplementedUnaryCall(getGetStudentMethod(), responseObserver); + } + + /** + */ + public void getStudentStream(StudentRequest request, + io.grpc.stub.StreamObserver responseObserver) { + io.grpc.stub.ServerCalls.asyncUnimplementedUnaryCall(getGetStudentStreamMethod(), responseObserver); + } + + /** + */ + public io.grpc.stub.StreamObserver sendStudentStream( + io.grpc.stub.StreamObserver responseObserver) { + return io.grpc.stub.ServerCalls.asyncUnimplementedStreamingCall(getSendStudentStreamMethod(), + responseObserver); + } + + /** + */ + public io.grpc.stub.StreamObserver sendAndGetStudentStream( + io.grpc.stub.StreamObserver responseObserver) { + return io.grpc.stub.ServerCalls.asyncUnimplementedStreamingCall(getSendAndGetStudentStreamMethod(), + responseObserver); + } + + @Override + public final io.grpc.ServerServiceDefinition bindService() { + return io.grpc.ServerServiceDefinition.builder(getServiceDescriptor()) + .addMethod( + getGetStudentMethod(), + io.grpc.stub.ServerCalls.asyncUnaryCall( + new MethodHandlers( + this, METHODID_GET_STUDENT))) + .addMethod( + getGetStudentStreamMethod(), + io.grpc.stub.ServerCalls.asyncServerStreamingCall( + new MethodHandlers( + this, METHODID_GET_STUDENT_STREAM))) + .addMethod( + getSendStudentStreamMethod(), + io.grpc.stub.ServerCalls.asyncClientStreamingCall( + new MethodHandlers( + this, METHODID_SEND_STUDENT_STREAM))) + .addMethod( + getSendAndGetStudentStreamMethod(), + io.grpc.stub.ServerCalls.asyncBidiStreamingCall( + new MethodHandlers( + this, METHODID_SEND_AND_GET_STUDENT_STREAM))) + .build(); + } + } + + /** + */ + public static final class StudentServiceStub extends io.grpc.stub.AbstractAsyncStub { + private StudentServiceStub( + io.grpc.Channel channel, io.grpc.CallOptions callOptions) { + super(channel, callOptions); + } + + @Override + protected StudentServiceStub build( + io.grpc.Channel channel, io.grpc.CallOptions callOptions) { + return new StudentServiceStub(channel, callOptions); + } + + /** + */ + public void getStudent(StudentRequest request, + io.grpc.stub.StreamObserver responseObserver) { + io.grpc.stub.ClientCalls.asyncUnaryCall( + getChannel().newCall(getGetStudentMethod(), getCallOptions()), request, responseObserver); + } + + /** + */ + public void getStudentStream(StudentRequest request, + io.grpc.stub.StreamObserver responseObserver) { + io.grpc.stub.ClientCalls.asyncServerStreamingCall( + getChannel().newCall(getGetStudentStreamMethod(), getCallOptions()), request, responseObserver); + } + + /** + */ + public io.grpc.stub.StreamObserver sendStudentStream( + io.grpc.stub.StreamObserver responseObserver) { + return io.grpc.stub.ClientCalls.asyncClientStreamingCall( + getChannel().newCall(getSendStudentStreamMethod(), getCallOptions()), responseObserver); + } + + /** + */ + public io.grpc.stub.StreamObserver sendAndGetStudentStream( + io.grpc.stub.StreamObserver responseObserver) { + return io.grpc.stub.ClientCalls.asyncBidiStreamingCall( + getChannel().newCall(getSendAndGetStudentStreamMethod(), getCallOptions()), responseObserver); + } + } + + /** + */ + public static final class StudentServiceBlockingStub + extends io.grpc.stub.AbstractBlockingStub { + private StudentServiceBlockingStub( + io.grpc.Channel channel, io.grpc.CallOptions callOptions) { + super(channel, callOptions); + } + + @Override + protected StudentServiceBlockingStub build( + io.grpc.Channel channel, io.grpc.CallOptions callOptions) { + return new StudentServiceBlockingStub(channel, callOptions); + } + + /** + */ + public StudentResponse getStudent(StudentRequest request) { + return io.grpc.stub.ClientCalls.blockingUnaryCall( + getChannel(), getGetStudentMethod(), getCallOptions(), request); + } + + /** + */ + public java.util.Iterator getStudentStream( + StudentRequest request) { + return io.grpc.stub.ClientCalls.blockingServerStreamingCall( + getChannel(), getGetStudentStreamMethod(), getCallOptions(), request); + } + } + + /** + */ + public static final class StudentServiceFutureStub + extends io.grpc.stub.AbstractFutureStub { + private StudentServiceFutureStub( + io.grpc.Channel channel, io.grpc.CallOptions callOptions) { + super(channel, callOptions); + } + + @Override + protected StudentServiceFutureStub build( + io.grpc.Channel channel, io.grpc.CallOptions callOptions) { + return new StudentServiceFutureStub(channel, callOptions); + } + + /** + */ + public com.google.common.util.concurrent.ListenableFuture getStudent( + StudentRequest request) { + return io.grpc.stub.ClientCalls.futureUnaryCall( + getChannel().newCall(getGetStudentMethod(), getCallOptions()), request); + } + } + + private static final int METHODID_GET_STUDENT = 0; + private static final int METHODID_GET_STUDENT_STREAM = 1; + private static final int METHODID_SEND_STUDENT_STREAM = 2; + private static final int METHODID_SEND_AND_GET_STUDENT_STREAM = 3; + + private static final class MethodHandlers implements + io.grpc.stub.ServerCalls.UnaryMethod, + io.grpc.stub.ServerCalls.ServerStreamingMethod, + io.grpc.stub.ServerCalls.ClientStreamingMethod, + io.grpc.stub.ServerCalls.BidiStreamingMethod { + private final StudentServiceImplBase serviceImpl; + private final int methodId; + + MethodHandlers(StudentServiceImplBase serviceImpl, int methodId) { + this.serviceImpl = serviceImpl; + this.methodId = methodId; + } + + @Override + @SuppressWarnings("unchecked") + public void invoke(Req request, io.grpc.stub.StreamObserver responseObserver) { + switch (methodId) { + case METHODID_GET_STUDENT: + serviceImpl.getStudent((StudentRequest) request, + (io.grpc.stub.StreamObserver) responseObserver); + break; + case METHODID_GET_STUDENT_STREAM: + serviceImpl.getStudentStream((StudentRequest) request, + (io.grpc.stub.StreamObserver) responseObserver); + break; + default: + throw new AssertionError(); + } + } + + @Override + @SuppressWarnings("unchecked") + public io.grpc.stub.StreamObserver invoke( + io.grpc.stub.StreamObserver responseObserver) { + switch (methodId) { + case METHODID_SEND_STUDENT_STREAM: + return (io.grpc.stub.StreamObserver) serviceImpl.sendStudentStream( + (io.grpc.stub.StreamObserver) responseObserver); + case METHODID_SEND_AND_GET_STUDENT_STREAM: + return (io.grpc.stub.StreamObserver) serviceImpl.sendAndGetStudentStream( + (io.grpc.stub.StreamObserver) responseObserver); + default: + throw new AssertionError(); + } + } + } + + private static abstract class StudentServiceBaseDescriptorSupplier + implements io.grpc.protobuf.ProtoFileDescriptorSupplier, io.grpc.protobuf.ProtoServiceDescriptorSupplier { + StudentServiceBaseDescriptorSupplier() { + } + + @Override + public com.google.protobuf.Descriptors.FileDescriptor getFileDescriptor() { + return Student.getDescriptor(); + } + + @Override + public com.google.protobuf.Descriptors.ServiceDescriptor getServiceDescriptor() { + return getFileDescriptor().findServiceByName("StudentService"); + } + } + + private static final class StudentServiceFileDescriptorSupplier + extends StudentServiceBaseDescriptorSupplier { + StudentServiceFileDescriptorSupplier() { + } + } + + private static final class StudentServiceMethodDescriptorSupplier + extends StudentServiceBaseDescriptorSupplier + implements io.grpc.protobuf.ProtoMethodDescriptorSupplier { + private final String methodName; + + StudentServiceMethodDescriptorSupplier(String methodName) { + this.methodName = methodName; + } + + @Override + public com.google.protobuf.Descriptors.MethodDescriptor getMethodDescriptor() { + return getServiceDescriptor().findMethodByName(methodName); + } + } + + private static volatile io.grpc.ServiceDescriptor serviceDescriptor; + + public static io.grpc.ServiceDescriptor getServiceDescriptor() { + io.grpc.ServiceDescriptor result = serviceDescriptor; + if (result == null) { + synchronized (StudentServiceGrpc.class) { + result = serviceDescriptor; + if (result == null) { + serviceDescriptor = result = io.grpc.ServiceDescriptor.newBuilder(SERVICE_NAME) + .setSchemaDescriptor(new StudentServiceFileDescriptorSupplier()) + .addMethod(getGetStudentMethod()) + .addMethod(getGetStudentStreamMethod()) + .addMethod(getSendStudentStreamMethod()) + .addMethod(getSendAndGetStudentStreamMethod()) + .build(); + } + } + } + return result; + } +} \ No newline at end of file diff --git a/test/cucumber-tests/src/test/resources/artifacts/apk-confs/grpc/generated-grpc.apk-conf b/test/cucumber-tests/src/test/resources/artifacts/apk-confs/grpc/generated-grpc.apk-conf new file mode 100644 index 0000000000..be0a393228 --- /dev/null +++ b/test/cucumber-tests/src/test/resources/artifacts/apk-confs/grpc/generated-grpc.apk-conf @@ -0,0 +1,24 @@ +--- +name: "6a254687f3229c35dd0189aac7f7fc4b6228e97a" +basePath: "/dineth.grpc.api" +version: "v1" +type: "GRPC" +defaultVersion: false +subscriptionValidation: false +operations: +- target: "student.StudentService" + verb: "GetStudent" + secured: true + scopes: [] +- target: "student.StudentService" + verb: "GetStudentStream" + secured: true + scopes: [] +- target: "student.StudentService" + verb: "SendStudentStream" + secured: true + scopes: [] +- target: "student.StudentService" + verb: "SendAndGetStudentStream" + secured: true + scopes: [] diff --git a/test/cucumber-tests/src/test/resources/artifacts/apk-confs/grpc/grpc.apk-conf b/test/cucumber-tests/src/test/resources/artifacts/apk-confs/grpc/grpc.apk-conf new file mode 100644 index 0000000000..93a02570cd --- /dev/null +++ b/test/cucumber-tests/src/test/resources/artifacts/apk-confs/grpc/grpc.apk-conf @@ -0,0 +1,28 @@ +--- +name: "demo-grpc-api" +basePath: "/dineth.grpc.api" +version: "v1" +type: "GRPC" +id: "grpc-basic-api" +endpointConfigurations: + production: + endpoint: "http://grpc-backend:6565" +defaultVersion: false +subscriptionValidation: false +operations: + - target: "student.StudentService" + verb: "GetStudent" + secured: true + scopes: [] + - target: "student.StudentService" + verb: "GetStudentStream" + secured: true + scopes: [] + - target: "student.StudentService" + verb: "SendStudentStream" + secured: true + scopes: [] + - target: "student.StudentService" + verb: "SendAndGetStudentStream" + secured: true + scopes: [] diff --git a/test/cucumber-tests/src/test/resources/artifacts/apk-confs/grpc/grpc_scopes.apk-conf b/test/cucumber-tests/src/test/resources/artifacts/apk-confs/grpc/grpc_scopes.apk-conf new file mode 100644 index 0000000000..0932a60da2 --- /dev/null +++ b/test/cucumber-tests/src/test/resources/artifacts/apk-confs/grpc/grpc_scopes.apk-conf @@ -0,0 +1,28 @@ +--- +name: "demo-grpc-api" +basePath: "/dineth.grpc.api" +version: "v1" +type: "GRPC" +id: "grpc-scopes" +endpointConfigurations: + production: + endpoint: "http://grpc-backend:6565" +defaultVersion: false +subscriptionValidation: false +operations: + - target: "student.StudentService" + verb: "GetStudent" + secured: true + scopes: ["wso2"] + - target: "student.StudentService" + verb: "GetStudentStream" + secured: true + scopes: [] + - target: "student.StudentService" + verb: "SendStudentStream" + secured: true + scopes: [] + - target: "student.StudentService" + verb: "SendAndGetStudentStream" + secured: true + scopes: [] diff --git a/test/cucumber-tests/src/test/resources/artifacts/apk-confs/grpc/grpc_with_default_version.apk-conf b/test/cucumber-tests/src/test/resources/artifacts/apk-confs/grpc/grpc_with_default_version.apk-conf new file mode 100644 index 0000000000..1c624d2a47 --- /dev/null +++ b/test/cucumber-tests/src/test/resources/artifacts/apk-confs/grpc/grpc_with_default_version.apk-conf @@ -0,0 +1,28 @@ +--- +name: "demo-grpc-api" +basePath: "/dineth.grpc.api" +version: "v1" +type: "GRPC" +id: "grpc-default-version-api" +endpointConfigurations: + production: + endpoint: "http://grpc-backend:6565" +defaultVersion: true +subscriptionValidation: false +operations: + - target: "student.StudentService" + verb: "GetStudent" + secured: true + scopes: [] + - target: "student.StudentService" + verb: "GetStudentStream" + secured: true + scopes: [] + - target: "student.StudentService" + verb: "SendStudentStream" + secured: true + scopes: [] + - target: "student.StudentService" + verb: "SendAndGetStudentStream" + secured: true + scopes: [] diff --git a/test/cucumber-tests/src/test/resources/artifacts/apk-confs/grpc/grpc_with_disabled_auth.apk-conf b/test/cucumber-tests/src/test/resources/artifacts/apk-confs/grpc/grpc_with_disabled_auth.apk-conf new file mode 100644 index 0000000000..2cbadd9219 --- /dev/null +++ b/test/cucumber-tests/src/test/resources/artifacts/apk-confs/grpc/grpc_with_disabled_auth.apk-conf @@ -0,0 +1,32 @@ +--- +name: "demo-grpc-api" +basePath: "/dineth.grpc.api" +version: "v1" +type: "GRPC" +id: "grpc-auth-disabled-api" +endpointConfigurations: + production: + endpoint: "http://grpc-backend:6565" +defaultVersion: false +subscriptionValidation: false +operations: + - target: "student.StudentService" + verb: "GetStudent" + secured: true + scopes: [] + - target: "student.StudentService" + verb: "GetStudentStream" + secured: true + scopes: [] + - target: "student.StudentService" + verb: "SendStudentStream" + secured: true + scopes: [] + - target: "student.StudentService" + verb: "SendAndGetStudentStream" + secured: true + scopes: [] +authentication: + - authType: OAuth2 + required: mandatory + enabled: false diff --git a/test/cucumber-tests/src/test/resources/artifacts/apk-confs/grpc/grpc_with_mtls_mandatory_oauth2_disabled.apk-conf b/test/cucumber-tests/src/test/resources/artifacts/apk-confs/grpc/grpc_with_mtls_mandatory_oauth2_disabled.apk-conf new file mode 100644 index 0000000000..16913e85c8 --- /dev/null +++ b/test/cucumber-tests/src/test/resources/artifacts/apk-confs/grpc/grpc_with_mtls_mandatory_oauth2_disabled.apk-conf @@ -0,0 +1,36 @@ +--- +name: "demo-grpc-api" +basePath: "/dineth.grpc.api" +version: "v1" +type: "GRPC" +id: "grpc-mtls-mandatory-oauth2-disabled" +endpointConfigurations: + production: + endpoint: "http://grpc-backend:6565" +defaultVersion: false +subscriptionValidation: false +operations: + - target: "student.StudentService" + verb: "GetStudent" + secured: true + scopes: [] + - target: "student.StudentService" + verb: "GetStudentStream" + secured: true + scopes: [] + - target: "student.StudentService" + verb: "SendStudentStream" + secured: true + scopes: [] + - target: "student.StudentService" + verb: "SendAndGetStudentStream" + secured: true + scopes: [] +authentication: + - authType: OAuth2 + enabled: false + - authType: mTLS + required: mandatory + certificates: + - name: mtls-test-configmap + key: tls.crt diff --git a/test/cucumber-tests/src/test/resources/artifacts/apk-confs/grpc/grpc_with_mtls_mandatory_oauth2_mandatory.apk-conf b/test/cucumber-tests/src/test/resources/artifacts/apk-confs/grpc/grpc_with_mtls_mandatory_oauth2_mandatory.apk-conf new file mode 100644 index 0000000000..2dca02ea40 --- /dev/null +++ b/test/cucumber-tests/src/test/resources/artifacts/apk-confs/grpc/grpc_with_mtls_mandatory_oauth2_mandatory.apk-conf @@ -0,0 +1,36 @@ +--- +name: "demo-grpc-api" +basePath: "/dineth.grpc.api" +version: "v1" +type: "GRPC" +id: "grpc-mtls-mandatory-oauth2-mandatory" +endpointConfigurations: + production: + endpoint: "http://grpc-backend:6565" +defaultVersion: false +subscriptionValidation: false +operations: + - target: "student.StudentService" + verb: "GetStudent" + secured: true + scopes: [] + - target: "student.StudentService" + verb: "GetStudentStream" + secured: true + scopes: [] + - target: "student.StudentService" + verb: "SendStudentStream" + secured: true + scopes: [] + - target: "student.StudentService" + verb: "SendAndGetStudentStream" + secured: true + scopes: [] +authentication: + - authType: OAuth2 + required: mandatory + - authType: mTLS + required: mandatory + certificates: + - name: mtls-test-configmap + key: tls.crt diff --git a/test/cucumber-tests/src/test/resources/artifacts/apk-confs/grpc/grpc_with_mtls_optional_oauth2_optional.apk-conf b/test/cucumber-tests/src/test/resources/artifacts/apk-confs/grpc/grpc_with_mtls_optional_oauth2_optional.apk-conf new file mode 100644 index 0000000000..d49048609b --- /dev/null +++ b/test/cucumber-tests/src/test/resources/artifacts/apk-confs/grpc/grpc_with_mtls_optional_oauth2_optional.apk-conf @@ -0,0 +1,36 @@ +--- +name: "demo-grpc-api" +basePath: "/dineth.grpc.api" +version: "v1" +type: "GRPC" +id: "grpc-mtls-optional-oauth2-optional" +endpointConfigurations: + production: + endpoint: "http://grpc-backend:6565" +defaultVersion: false +subscriptionValidation: false +operations: + - target: "student.StudentService" + verb: "GetStudent" + secured: true + scopes: [] + - target: "student.StudentService" + verb: "GetStudentStream" + secured: true + scopes: [] + - target: "student.StudentService" + verb: "SendStudentStream" + secured: true + scopes: [] + - target: "student.StudentService" + verb: "SendAndGetStudentStream" + secured: true + scopes: [] +authentication: + - authType: OAuth2 + required: optional + - authType: mTLS + required: optional + certificates: + - name: mtls-test-configmap + key: tls.crt diff --git a/test/cucumber-tests/src/test/resources/artifacts/apk-confs/grpc/order.apk-conf b/test/cucumber-tests/src/test/resources/artifacts/apk-confs/grpc/order.apk-conf new file mode 100644 index 0000000000..f0bfd32ac1 --- /dev/null +++ b/test/cucumber-tests/src/test/resources/artifacts/apk-confs/grpc/order.apk-conf @@ -0,0 +1,24 @@ +--- +name: "32398767b3b64a7ba1c6aabcd042df4fbd42502a" +basePath: "/grpcapi" +version: "v1" +type: "GRPC" +defaultVersion: false +subscriptionValidation: false +operations: +- target: "order.OrderService" + verb: "CreateOrder" + secured: true + scopes: [] +- target: "order.OrderService" + verb: "ServeOrder" + secured: true + scopes: [] +- target: "payment.PaymentService" + verb: "ProcessPayment" + secured: true + scopes: [] +- target: "user.UserService" + verb: "GetUser" + secured: true + scopes: [] diff --git a/test/cucumber-tests/src/test/resources/artifacts/definitions/order-definition.zip b/test/cucumber-tests/src/test/resources/artifacts/definitions/order-definition.zip new file mode 100644 index 0000000000000000000000000000000000000000..3d606bc015a472d358b4b1adba5052b07d1b8dc7 GIT binary patch literal 1393 zcmWIWW@Zs#U|`^2xEQArxwP(dO*W8M3B)`MG7QQ2xw-jydId%KCHbKtoD9s0f3K%) zZ@8IOTEWf0$nuqufdQ;r_KYJJlOa#*!&*PVXAI1XA1$k3Ij^u#*lWVJjh&Aa?%lP> z|NXJ<_N>d&`W7!=-0Kf!3OH4+)svc`HudM$Gdr)}ijGL#yg&A4rmFfMhGS>iw=^}& z`Ls1agReF?H|hUj2!K1-EJ`ghr|SEf_dv%wGcho5109@Sl#*J6 zF z=Im`eY@ZLFIA7;)Z#hrqwQ|k^jwd&-yDkp7@OJt;!78q`e9LTfy!~Y-#Yjrq7>caj zer>l=<&mO^?HwheQ;$jC5o7t*0%{Js+`PufvYlWon3+s36JvJZHzM0gi{W~kt zTrT*2xzD!qKiAYJuMU!ye3fwPS2lLm$vFEGps5-W34^Gc9H%^n!bg+PoJYPA7p{eYpi@4M!j zc{i3`GP4M+WnpPzcL>W`#UYfw(b`b)-rOyo%MT@3x}1AnW4~?Ao=5B@0qrMF{ob{9 zRl_r0_9jKP=7VO(bA>-VIgyYwDccH@!9F)>^** zywhg0`=y_vOnLugGMFcp^u+1?w)~Q2^qUP4UbleZCA8po;~rpml>t*b7cjg^i&2u8 zF)*M&7%i}J{de;nGT^!MSu{*NLhIv=Rp)$k6zmRUt<`F>>4;@<`eW@GykP2w607?^ zYyNC#Dqpei!@dS2v_oAmo8hl>`;b9C9gizJf#`huq% z40e>6wjh^%SMT=lt5Y^*XiR!7>B;t0b<^xKev6K;vE|&R!SRea>qgkCuiW=1F8RY8 zTb=at|1o>Z>hzgze=jI({rgIJ-%GV}>pqL@$NlrQH_TYBuQ=h$!A~g@i>JMOAsyh& z$Rx*%t5}c#h6MuyFfbUFG=f;Dg#;_4kU$F!WD_y-EwYIlfhMBlVxWOoaxk)in28$M xz^6c$q9t*lk)R}w$Bmfrj%=hTGXqL00Gfv-1q67rvVp>f1qj`Mju2%5@c=b(4P5{L literal 0 HcmV?d00001 diff --git a/test/cucumber-tests/src/test/resources/artifacts/definitions/student.proto b/test/cucumber-tests/src/test/resources/artifacts/definitions/student.proto new file mode 100644 index 0000000000..3ca6bbce0f --- /dev/null +++ b/test/cucumber-tests/src/test/resources/artifacts/definitions/student.proto @@ -0,0 +1,21 @@ +syntax = "proto3"; + +option java_multiple_files = true; +option java_package = "org.example"; +package dineth.grpc.api.v1.student; + +service StudentService { + rpc GetStudent(StudentRequest) returns (StudentResponse) {}; + rpc GetStudentStream(StudentRequest) returns (stream StudentResponse) {}; + rpc SendStudentStream(stream StudentRequest) returns (StudentResponse) {}; + rpc SendAndGetStudentStream(stream StudentRequest) returns (stream StudentResponse) {} +} + +message StudentRequest { + int32 id = 3; +} + +message StudentResponse { + string name = 1; + int32 age = 2; +} \ No newline at end of file diff --git a/test/cucumber-tests/src/test/resources/tests/api/GRPC.feature b/test/cucumber-tests/src/test/resources/tests/api/GRPC.feature new file mode 100644 index 0000000000..e84853b1f2 --- /dev/null +++ b/test/cucumber-tests/src/test/resources/tests/api/GRPC.feature @@ -0,0 +1,123 @@ +Feature: Generating APK conf for gRPC API + Scenario: Generating APK conf using a valid GRPC API definition + Given The system is ready + When I use the definition file "artifacts/definitions/student.proto" in resources + And generate the APK conf file for a "GRPC" API + Then the response status code should be 200 + And the response body should be "artifacts/apk-confs/grpc/generated-grpc.apk-conf" in resources + + Scenario: Generating APK conf for a valid GRPC API definition with multiple proto files + Given The system is ready + When I use the definition file "artifacts/definitions/order-definition.zip" in resources + And generate the APK conf file for a "GRPC" API + Then the response status code should be 200 + And the response body should be "artifacts/apk-confs/grpc/order.apk-conf" in resources + + Scenario: Deploying APK conf using a valid gRPC API definition + Given The system is ready + And I have a valid subscription + When I use the APK Conf file "artifacts/apk-confs/grpc/grpc.apk-conf" + And the definition file "artifacts/definitions/student.proto" + And make the API deployment request + Then the response status code should be 200 + Then I set headers + | Authorization | bearer ${accessToken} | + And I make grpc request GetStudent to "default.gw.wso2.com" with port 9095 + And the gRPC response status code should be 0 + And the student response body should contain name: "Dineth" age: 10 + + Scenario: Undeploy API + Given The system is ready + And I have a valid subscription + When I undeploy the API whose ID is "grpc-basic-api" + Then the response status code should be 202 + + Scenario: Checking api-definition endpoint to get proto file + Given The system is ready + And I have a valid subscription + When I use the APK Conf file "artifacts/apk-confs/grpc/grpc.apk-conf" + And the definition file "artifacts/definitions/student.proto" + And make the API deployment request + Then the response status code should be 200 + Then I set headers + | Authorization | bearer ${accessToken} | + | Host | default.gw.wso2.com | + And I send "GET" request to "https://default.gw.wso2.com:9095/dineth.grpc.api.v1/api-definition/" with body "" + And I eventually receive 200 response code, not accepting + | 429 | + | 500 | + And the response body should contain endpoint definition for student.proto + + + Scenario: Undeploy API + Given The system is ready + And I have a valid subscription + When I undeploy the API whose ID is "grpc-basic-api" + Then the response status code should be 202 + + Scenario: Deploying gRPC API with OAuth2 disabled + Given The system is ready + And I have a valid subscription + When I use the APK Conf file "artifacts/apk-confs/grpc/grpc_with_disabled_auth.apk-conf" + And the definition file "artifacts/definitions/student.proto" + And make the API deployment request + Then the response status code should be 200 + And I make grpc request GetStudent to "default.gw.wso2.com" with port 9095 + And the gRPC response status code should be 0 + And the student response body should contain name: "Dineth" age: 10 + + Scenario: Undeploy API + Given The system is ready + And I have a valid subscription + When I undeploy the API whose ID is "grpc-auth-disabled-api" + Then the response status code should be 202 + + Scenario: Deploying gRPC API with scopes + Given The system is ready + And I have a valid subscription + When I use the APK Conf file "artifacts/apk-confs/grpc/grpc_scopes.apk-conf" + And the definition file "artifacts/definitions/student.proto" + And make the API deployment request + Then the response status code should be 200 + Then I set headers + | Authorization | bearer ${accessToken} | + And I make grpc request GetStudent to "default.gw.wso2.com" with port 9095 + And the gRPC response status code should be 7 + Given I have a valid subscription with scopes + | wso2 | + Then I set headers + | Authorization | bearer ${accessToken} | + And I make grpc request GetStudent to "default.gw.wso2.com" with port 9095 + And the gRPC response status code should be 0 + And the student response body should contain name: "Dineth" age: 10 + + Scenario: Undeploy API + Given The system is ready + And I have a valid subscription + When I undeploy the API whose ID is "grpc-scopes" + Then the response status code should be 202 + + Scenario: Deploying gRPC API with default version enabled + Given The system is ready + And I have a valid subscription + When I use the APK Conf file "artifacts/apk-confs/grpc/grpc_with_default_version.apk-conf" + And the definition file "artifacts/definitions/student.proto" + And make the API deployment request + Then the response status code should be 200 + Then I set headers + | Authorization | bearer ${accessToken} | + And I make grpc request GetStudent to "default.gw.wso2.com" with port 9095 + And the gRPC response status code should be 0 + And the student response body should contain name: "Dineth" age: 10 + Given I have a valid subscription + Then I set headers + | Authorization | bearer ${accessToken} | + And I make grpc request GetStudent default version to "default.gw.wso2.com" with port 9095 + And the gRPC response status code should be 0 + And the student response body should contain name: "Dineth" age: 10 + + Scenario: Undeploy API + Given The system is ready + And I have a valid subscription + When I undeploy the API whose ID is "grpc-default-version-api" + Then the response status code should be 202 \ No newline at end of file diff --git a/test/cucumber-tests/src/test/resources/tests/api/GRPCMTLS.feature b/test/cucumber-tests/src/test/resources/tests/api/GRPCMTLS.feature new file mode 100644 index 0000000000..2856375e7b --- /dev/null +++ b/test/cucumber-tests/src/test/resources/tests/api/GRPCMTLS.feature @@ -0,0 +1,89 @@ +Feature: Test mTLS between client and gateway with client certificate sent in header + Scenario: Test API with mandatory mTLS and OAuth2 disabled + Given The system is ready + And I have a valid token with a client certificate "config-map-1.txt" + When I use the APK Conf file "artifacts/apk-confs/grpc/grpc_with_mtls_mandatory_oauth2_disabled.apk-conf" + And the definition file "artifacts/definitions/student.proto" + And make the API deployment request + Then the response status code should be 200 + Then I set headers + | X-WSO2-CLIENT-CERTIFICATE | ${clientCertificate} | + And I make grpc request GetStudent to "default.gw.wso2.com" with port 9095 + And the gRPC response status code should be 0 + And the student response body should contain name: "Dineth" age: 10 + And I have a valid token with a client certificate "invalid-cert.txt" + Then I set headers + | X-WSO2-CLIENT-CERTIFICATE | ${clientCertificate} | + And I make grpc request GetStudent to "default.gw.wso2.com" with port 9095 + And the gRPC response status code should be 16 + + Scenario: Undeploy API + Given The system is ready + And I have a valid subscription + When I undeploy the API whose ID is "grpc-mtls-mandatory-oauth2-disabled" + Then the response status code should be 202 + + Scenario: Test mandatory mTLS and mandatory OAuth2 + Given The system is ready + And I have a valid token with a client certificate "config-map-1.txt" + When I use the APK Conf file "artifacts/apk-confs/grpc/grpc_with_mtls_mandatory_oauth2_mandatory.apk-conf" + And the definition file "artifacts/definitions/student.proto" + And make the API deployment request + Then the response status code should be 200 + Then I set headers + | Authorization | Bearer ${accessToken} | + And I make grpc request GetStudent to "default.gw.wso2.com" with port 9095 + And the gRPC response status code should be 16 + Then I remove header "Authorization" + Then I set headers + | X-WSO2-CLIENT-CERTIFICATE | ${clientCertificate} | + And I make grpc request GetStudent to "default.gw.wso2.com" with port 9095 + And the gRPC response status code should be 16 + Then I remove header "X-WSO2-CLIENT-CERTIFICATE" + And I have a valid token with a client certificate "invalid-cert.txt" + Then I set headers + | X-WSO2-CLIENT-CERTIFICATE | ${clientCertificate} | + | Authorization | Bearer ${accessToken} | + And I make grpc request GetStudent to "default.gw.wso2.com" with port 9095 + And the gRPC response status code should be 16 + + Scenario: Undeploy API + Given The system is ready + And I have a valid subscription + When I undeploy the API whose ID is "grpc-mtls-mandatory-oauth2-mandatory" + Then the response status code should be 202 + + Scenario: Test optional mTLS and optional OAuth2 + Given The system is ready + And I have a valid token with a client certificate "config-map-1.txt" + When I use the APK Conf file "artifacts/apk-confs/grpc/grpc_with_mtls_optional_oauth2_optional.apk-conf" + And the definition file "artifacts/definitions/student.proto" + And make the API deployment request + Then the response status code should be 200 + Then I set headers + | X-WSO2-CLIENT-CERTIFICATE | ${clientCertificate} | + | Authorization | Bearer ${accessToken} | + And I make grpc request GetStudent to "default.gw.wso2.com" with port 9095 + And the gRPC response status code should be 0 + And the student response body should contain name: "Dineth" age: 10 + Then I set headers + | Authorization | Bearer ${accessToken} | + And I make grpc request GetStudent to "default.gw.wso2.com" with port 9095 + And the gRPC response status code should be 0 + And the student response body should contain name: "Dineth" age: 10 + Then I set headers + | X-WSO2-CLIENT-CERTIFICATE | ${clientCertificate} | + And I make grpc request GetStudent to "default.gw.wso2.com" with port 9095 + And the gRPC response status code should be 0 + And the student response body should contain name: "Dineth" age: 10 + And I have a valid token with a client certificate "invalid-cert.txt" + Then I set headers + | X-WSO2-CLIENT-CERTIFICATE | ${clientCertificate} | + And I make grpc request GetStudent to "default.gw.wso2.com" with port 9095 + And the gRPC response status code should be 16 + + Scenario: Undeploy API + Given The system is ready + And I have a valid subscription + When I undeploy the API whose ID is "grpc-mtls-optional-oauth2-optional" + Then the response status code should be 202 \ No newline at end of file diff --git a/test/integration/integration/tests/grpc-api.go b/test/integration/integration/tests/grpc-api.go new file mode 100644 index 0000000000..a5b46aac04 --- /dev/null +++ b/test/integration/integration/tests/grpc-api.go @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package tests + +import ( + "testing" + + "github.com/wso2/apk/test/integration/integration/utils/grpc-code/student" + "github.com/wso2/apk/test/integration/integration/utils/grpc-code/student_default_version" + "github.com/wso2/apk/test/integration/integration/utils/grpcutils" + "github.com/wso2/apk/test/integration/integration/utils/suite" +) + +func init() { + IntegrationTests = append(IntegrationTests, GRPCAPI) +} + +// GRPCAPI tests gRPC API +var GRPCAPI = suite.IntegrationTest{ + ShortName: "GRPCAPI", + Description: "Tests gRPC API", + Manifests: []string{"tests/grpc-api.yaml"}, + Test: func(t *testing.T, suite *suite.IntegrationTestSuite) { + gwAddr := "grpc.test.gw.wso2.com:9095" + + testCases := []grpcutils.GRPCTestCase{ + { + ExpectedResponse: grpcutils.ExpectedResponse{ + Out: &student.StudentResponse{ + Name: "Dineth", + Age: 10, + }, + Err: nil, + }, + ActualResponse: &student.StudentResponse{}, + Name: "Get Student Details", + Method: student.GetStudent, + Satisfier: student.StudentResponseSatisfier{}, + }, + { + ExpectedResponse: grpcutils.ExpectedResponse{ + Out: &student_default_version.StudentResponse{ + Name: "Dineth", + Age: 10, + }, + Err: nil, + }, + ActualResponse: &student_default_version.StudentResponse{}, + Name: "Get Student Details (Default API Version)", + Method: student_default_version.GetStudent, + Satisfier: student_default_version.StudentResponseSatisfier{}, + }, + } + for i := range testCases { + tc := testCases[i] + t.Run("Invoke gRPC API", func(t *testing.T) { + t.Parallel() + grpcutils.InvokeGRPCClientUntilSatisfied(gwAddr, t, tc, tc.Satisfier, tc.Method) + }) + } + }, +} diff --git a/test/integration/integration/tests/resources/base/manifests.yaml b/test/integration/integration/tests/resources/base/manifests.yaml index 3bcf566107..81592bc234 100644 --- a/test/integration/integration/tests/resources/base/manifests.yaml +++ b/test/integration/integration/tests/resources/base/manifests.yaml @@ -113,6 +113,53 @@ spec: --- apiVersion: v1 kind: Service +metadata: + name: grpc-backend-v1 + namespace: gateway-integration-test-infra +spec: + selector: + app: grpc-backend-v1 + ports: + - protocol: TCP + port: 6565 + targetPort: 6565 +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: grpc-backend-v1 + namespace: gateway-integration-test-infra + labels: + app: grpc-backend-v1 +spec: + replicas: 1 + selector: + matchLabels: + app: grpc-backend-v1 + template: + metadata: + labels: + app: grpc-backend-v1 + spec: + containers: + - name: grpc-backend-v1 + image: ddh13/dineth-grpc-demo-server:1.0.0 + imagePullPolicy: Always + env: + - name: POD_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + - name: NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + resources: + requests: + cpu: 10m +--- +apiVersion: v1 +kind: Service metadata: name: infra-backend-v2 namespace: gateway-integration-test-infra diff --git a/test/integration/integration/tests/resources/tests/grpc-api.yaml b/test/integration/integration/tests/resources/tests/grpc-api.yaml new file mode 100644 index 0000000000..885c44eab3 --- /dev/null +++ b/test/integration/integration/tests/resources/tests/grpc-api.yaml @@ -0,0 +1,106 @@ +# Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com) All Rights Reserved. +# +# WSO2 LLC. licenses this file to you under the Apache License, +# Version 2.0 (the "License"); you may not use this file except +# in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +apiVersion: dp.wso2.com/v1alpha3 +kind: API +metadata: + name: grpc-api + namespace: gateway-integration-test-infra +spec: + apiName: GRPC API + apiType: GRPC + apiVersion: v1 + basePath: /dineth.grpc.api.v1 + isDefaultVersion: true + production: + - routeRefs: + - grpc-api-grpcroute + organization: wso2-org +--- +apiVersion: gateway.networking.k8s.io/v1alpha2 +kind: GRPCRoute +metadata: + name: grpc-api-grpcroute + namespace: gateway-integration-test-infra +spec: + parentRefs: + - group: gateway.networking.k8s.io + kind: Gateway + name: wso2-apk-default + namespace: apk-integration-test + sectionName: httpslistener + hostnames: + - grpc.test.gw.wso2.com + rules: + - matches: + - method: + service: student.StudentService + method: GetStudent + backendRefs: + - name: grpc-backend-v1 + kind: Backend + port: 6565 + - matches: + - method: + service: student.StudentService + method: SendStudentStream + backendRefs: + - name: grpc-backend-v1 + kind: Backend + port: 6565 + - matches: + - method: + service: student.StudentService + method: GetStudentStream + backendRefs: + - name: grpc-backend-v1 + kind: Backend + port: 6565 + - matches: + - method: + service: student.StudentService + method: SendAndGetStudentStream + backendRefs: + - name: grpc-backend-v1 + kind: Backend + port: 6565 + +--- +apiVersion: dp.wso2.com/v1alpha2 +kind: Authentication +metadata: + name: disable-grpc-api-security + namespace: gateway-integration-test-infra +spec: + override: + disabled: true + targetRef: + group: gateway.networking.k8s.io + kind: API + namespace: gateway-integration-test-infra + name: grpc-api +--- +apiVersion: dp.wso2.com/v1alpha1 +kind: Backend +metadata: + name: grpc-backend-v1 + namespace: gateway-integration-test-infra +spec: + services: + - host: grpc-backend-v1.gateway-integration-test-infra + port: 6565 + basePath: "" + protocol: http diff --git a/test/integration/integration/utils/grpc-code/student/student.go b/test/integration/integration/utils/grpc-code/student/student.go new file mode 100644 index 0000000000..be01a82f56 --- /dev/null +++ b/test/integration/integration/utils/grpc-code/student/student.go @@ -0,0 +1,47 @@ +package student + +import ( + "context" + "log" + "time" + + "github.com/wso2/apk/test/integration/integration/utils/grpcutils" + "google.golang.org/grpc" +) + +type StudentResponseSatisfier struct{} + +// IsSatisfactory checks if the given response is satisfactory based on the expected response. +func (srs StudentResponseSatisfier) IsSatisfactory(response any, expectedResponse grpcutils.ExpectedResponse) bool { + // Type assert the response to *student.StudentResponse + resp, respOk := response.(*StudentResponse) + if !respOk { + log.Println("Failed to assert response as *student.StudentResponse") + return false + } + // Type assert the expected output to *student.StudentResponse + expectedResp, expOk := expectedResponse.Out.(*StudentResponse) + if !expOk { + log.Println("Failed to assert expectedResponse.Out as *student.StudentResponse") + return false + } + + // Compare the actual response with the expected response + if resp.Name == expectedResp.Name && resp.Age == expectedResp.Age { + return true + } else { + log.Println("Response does not match the expected output") + return false + } +} +func GetStudent(conn *grpc.ClientConn) (any, error) { + c := NewStudentServiceClient(conn) + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + r := &StudentRequest{Id: 1234} + response, err := c.GetStudent(ctx, r) + if err != nil { + return nil, err + } + return response, nil +} diff --git a/test/integration/integration/utils/grpc-code/student/student.pb.go b/test/integration/integration/utils/grpc-code/student/student.pb.go new file mode 100644 index 0000000000..2a1e74b08a --- /dev/null +++ b/test/integration/integration/utils/grpc-code/student/student.pb.go @@ -0,0 +1,254 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.33.0 +// protoc v3.12.4 +// source: student.proto + +package student + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type StudentRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Id int32 `protobuf:"varint,3,opt,name=id,proto3" json:"id,omitempty"` +} + +func (x *StudentRequest) Reset() { + *x = StudentRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_student_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *StudentRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*StudentRequest) ProtoMessage() {} + +func (x *StudentRequest) ProtoReflect() protoreflect.Message { + mi := &file_student_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use StudentRequest.ProtoReflect.Descriptor instead. +func (*StudentRequest) Descriptor() ([]byte, []int) { + return file_student_proto_rawDescGZIP(), []int{0} +} + +func (x *StudentRequest) GetId() int32 { + if x != nil { + return x.Id + } + return 0 +} + +type StudentResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + Age int32 `protobuf:"varint,2,opt,name=age,proto3" json:"age,omitempty"` +} + +func (x *StudentResponse) Reset() { + *x = StudentResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_student_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *StudentResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*StudentResponse) ProtoMessage() {} + +func (x *StudentResponse) ProtoReflect() protoreflect.Message { + mi := &file_student_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use StudentResponse.ProtoReflect.Descriptor instead. +func (*StudentResponse) Descriptor() ([]byte, []int) { + return file_student_proto_rawDescGZIP(), []int{1} +} + +func (x *StudentResponse) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *StudentResponse) GetAge() int32 { + if x != nil { + return x.Age + } + return 0 +} + +var File_student_proto protoreflect.FileDescriptor + +var file_student_proto_rawDesc = []byte{ + 0x0a, 0x0d, 0x73, 0x74, 0x75, 0x64, 0x65, 0x6e, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, + 0x1a, 0x64, 0x69, 0x6e, 0x65, 0x74, 0x68, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x61, 0x70, 0x69, + 0x2e, 0x76, 0x31, 0x2e, 0x73, 0x74, 0x75, 0x64, 0x65, 0x6e, 0x74, 0x22, 0x20, 0x0a, 0x0e, 0x53, + 0x74, 0x75, 0x64, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, + 0x02, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x02, 0x69, 0x64, 0x22, 0x37, 0x0a, + 0x0f, 0x53, 0x74, 0x75, 0x64, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, + 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x05, 0x52, 0x03, 0x61, 0x67, 0x65, 0x32, 0xd6, 0x03, 0x0a, 0x0e, 0x53, 0x74, 0x75, 0x64, 0x65, + 0x6e, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x67, 0x0a, 0x0a, 0x47, 0x65, 0x74, + 0x53, 0x74, 0x75, 0x64, 0x65, 0x6e, 0x74, 0x12, 0x2a, 0x2e, 0x64, 0x69, 0x6e, 0x65, 0x74, 0x68, + 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x73, 0x74, 0x75, + 0x64, 0x65, 0x6e, 0x74, 0x2e, 0x53, 0x74, 0x75, 0x64, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x2b, 0x2e, 0x64, 0x69, 0x6e, 0x65, 0x74, 0x68, 0x2e, 0x67, 0x72, 0x70, + 0x63, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x73, 0x74, 0x75, 0x64, 0x65, 0x6e, 0x74, + 0x2e, 0x53, 0x74, 0x75, 0x64, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x22, 0x00, 0x12, 0x6f, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x53, 0x74, 0x75, 0x64, 0x65, 0x6e, 0x74, + 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, 0x2a, 0x2e, 0x64, 0x69, 0x6e, 0x65, 0x74, 0x68, 0x2e, + 0x67, 0x72, 0x70, 0x63, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x73, 0x74, 0x75, 0x64, + 0x65, 0x6e, 0x74, 0x2e, 0x53, 0x74, 0x75, 0x64, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x2b, 0x2e, 0x64, 0x69, 0x6e, 0x65, 0x74, 0x68, 0x2e, 0x67, 0x72, 0x70, 0x63, + 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x73, 0x74, 0x75, 0x64, 0x65, 0x6e, 0x74, 0x2e, + 0x53, 0x74, 0x75, 0x64, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, + 0x00, 0x30, 0x01, 0x12, 0x70, 0x0a, 0x11, 0x53, 0x65, 0x6e, 0x64, 0x53, 0x74, 0x75, 0x64, 0x65, + 0x6e, 0x74, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, 0x2a, 0x2e, 0x64, 0x69, 0x6e, 0x65, 0x74, + 0x68, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x73, 0x74, + 0x75, 0x64, 0x65, 0x6e, 0x74, 0x2e, 0x53, 0x74, 0x75, 0x64, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2b, 0x2e, 0x64, 0x69, 0x6e, 0x65, 0x74, 0x68, 0x2e, 0x67, 0x72, + 0x70, 0x63, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x73, 0x74, 0x75, 0x64, 0x65, 0x6e, + 0x74, 0x2e, 0x53, 0x74, 0x75, 0x64, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x22, 0x00, 0x28, 0x01, 0x12, 0x78, 0x0a, 0x17, 0x53, 0x65, 0x6e, 0x64, 0x41, 0x6e, 0x64, + 0x47, 0x65, 0x74, 0x53, 0x74, 0x75, 0x64, 0x65, 0x6e, 0x74, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, + 0x12, 0x2a, 0x2e, 0x64, 0x69, 0x6e, 0x65, 0x74, 0x68, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x61, + 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x73, 0x74, 0x75, 0x64, 0x65, 0x6e, 0x74, 0x2e, 0x53, 0x74, + 0x75, 0x64, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2b, 0x2e, 0x64, + 0x69, 0x6e, 0x65, 0x74, 0x68, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, + 0x31, 0x2e, 0x73, 0x74, 0x75, 0x64, 0x65, 0x6e, 0x74, 0x2e, 0x53, 0x74, 0x75, 0x64, 0x65, 0x6e, + 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x28, 0x01, 0x30, 0x01, 0x42, + 0x30, 0x5a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x67, 0x6f, 0x6c, 0x61, 0x6e, 0x67, + 0x2e, 0x6f, 0x72, 0x67, 0x2f, 0x67, 0x72, 0x70, 0x63, 0x2f, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, + 0x65, 0x73, 0x2f, 0x73, 0x63, 0x68, 0x6f, 0x6f, 0x6c, 0x2f, 0x73, 0x74, 0x75, 0x64, 0x65, 0x6e, + 0x74, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_student_proto_rawDescOnce sync.Once + file_student_proto_rawDescData = file_student_proto_rawDesc +) + +func file_student_proto_rawDescGZIP() []byte { + file_student_proto_rawDescOnce.Do(func() { + file_student_proto_rawDescData = protoimpl.X.CompressGZIP(file_student_proto_rawDescData) + }) + return file_student_proto_rawDescData +} + +var file_student_proto_msgTypes = make([]protoimpl.MessageInfo, 2) +var file_student_proto_goTypes = []interface{}{ + (*StudentRequest)(nil), // 0: dineth.grpc.api.v1.student.StudentRequest + (*StudentResponse)(nil), // 1: dineth.grpc.api.v1.student.StudentResponse +} +var file_student_proto_depIdxs = []int32{ + 0, // 0: dineth.grpc.api.v1.student.StudentService.GetStudent:input_type -> dineth.grpc.api.v1.student.StudentRequest + 0, // 1: dineth.grpc.api.v1.student.StudentService.GetStudentStream:input_type -> dineth.grpc.api.v1.student.StudentRequest + 0, // 2: dineth.grpc.api.v1.student.StudentService.SendStudentStream:input_type -> dineth.grpc.api.v1.student.StudentRequest + 0, // 3: dineth.grpc.api.v1.student.StudentService.SendAndGetStudentStream:input_type -> dineth.grpc.api.v1.student.StudentRequest + 1, // 4: dineth.grpc.api.v1.student.StudentService.GetStudent:output_type -> dineth.grpc.api.v1.student.StudentResponse + 1, // 5: dineth.grpc.api.v1.student.StudentService.GetStudentStream:output_type -> dineth.grpc.api.v1.student.StudentResponse + 1, // 6: dineth.grpc.api.v1.student.StudentService.SendStudentStream:output_type -> dineth.grpc.api.v1.student.StudentResponse + 1, // 7: dineth.grpc.api.v1.student.StudentService.SendAndGetStudentStream:output_type -> dineth.grpc.api.v1.student.StudentResponse + 4, // [4:8] is the sub-list for method output_type + 0, // [0:4] is the sub-list for method input_type + 0, // [0:0] is the sub-list for extension type_name + 0, // [0:0] is the sub-list for extension extendee + 0, // [0:0] is the sub-list for field type_name +} + +func init() { file_student_proto_init() } +func file_student_proto_init() { + if File_student_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_student_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*StudentRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_student_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*StudentResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_student_proto_rawDesc, + NumEnums: 0, + NumMessages: 2, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_student_proto_goTypes, + DependencyIndexes: file_student_proto_depIdxs, + MessageInfos: file_student_proto_msgTypes, + }.Build() + File_student_proto = out.File + file_student_proto_rawDesc = nil + file_student_proto_goTypes = nil + file_student_proto_depIdxs = nil +} \ No newline at end of file diff --git a/test/integration/integration/utils/grpc-code/student/student_grpc.pb.go b/test/integration/integration/utils/grpc-code/student/student_grpc.pb.go new file mode 100644 index 0000000000..52cad0142b --- /dev/null +++ b/test/integration/integration/utils/grpc-code/student/student_grpc.pb.go @@ -0,0 +1,314 @@ +// Code generated by protoc-gen-go-grpc. DO NOT EDIT. +// versions: +// - protoc-gen-go-grpc v1.3.0 +// - protoc v3.12.4 +// source: student.proto + +package student + +import ( + context "context" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" +) + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +// Requires gRPC-Go v1.32.0 or later. +const _ = grpc.SupportPackageIsVersion7 + +const ( + StudentService_GetStudent_FullMethodName = "/dineth.grpc.api.v1.student.StudentService/GetStudent" + StudentService_GetStudentStream_FullMethodName = "/dineth.grpc.api.v1.student.StudentService/GetStudentStream" + StudentService_SendStudentStream_FullMethodName = "/dineth.grpc.api.v1.student.StudentService/SendStudentStream" + StudentService_SendAndGetStudentStream_FullMethodName = "/dineth.grpc.api.v1.student.StudentService/SendAndGetStudentStream" +) + +// StudentServiceClient is the client API for StudentService service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +type StudentServiceClient interface { + GetStudent(ctx context.Context, in *StudentRequest, opts ...grpc.CallOption) (*StudentResponse, error) + GetStudentStream(ctx context.Context, in *StudentRequest, opts ...grpc.CallOption) (StudentService_GetStudentStreamClient, error) + SendStudentStream(ctx context.Context, opts ...grpc.CallOption) (StudentService_SendStudentStreamClient, error) + SendAndGetStudentStream(ctx context.Context, opts ...grpc.CallOption) (StudentService_SendAndGetStudentStreamClient, error) +} + +type studentServiceClient struct { + cc grpc.ClientConnInterface +} + +func NewStudentServiceClient(cc grpc.ClientConnInterface) StudentServiceClient { + return &studentServiceClient{cc} +} + +func (c *studentServiceClient) GetStudent(ctx context.Context, in *StudentRequest, opts ...grpc.CallOption) (*StudentResponse, error) { + out := new(StudentResponse) + err := c.cc.Invoke(ctx, StudentService_GetStudent_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *studentServiceClient) GetStudentStream(ctx context.Context, in *StudentRequest, opts ...grpc.CallOption) (StudentService_GetStudentStreamClient, error) { + stream, err := c.cc.NewStream(ctx, &StudentService_ServiceDesc.Streams[0], StudentService_GetStudentStream_FullMethodName, opts...) + if err != nil { + return nil, err + } + x := &studentServiceGetStudentStreamClient{stream} + if err := x.ClientStream.SendMsg(in); err != nil { + return nil, err + } + if err := x.ClientStream.CloseSend(); err != nil { + return nil, err + } + return x, nil +} + +type StudentService_GetStudentStreamClient interface { + Recv() (*StudentResponse, error) + grpc.ClientStream +} + +type studentServiceGetStudentStreamClient struct { + grpc.ClientStream +} + +func (x *studentServiceGetStudentStreamClient) Recv() (*StudentResponse, error) { + m := new(StudentResponse) + if err := x.ClientStream.RecvMsg(m); err != nil { + return nil, err + } + return m, nil +} + +func (c *studentServiceClient) SendStudentStream(ctx context.Context, opts ...grpc.CallOption) (StudentService_SendStudentStreamClient, error) { + stream, err := c.cc.NewStream(ctx, &StudentService_ServiceDesc.Streams[1], StudentService_SendStudentStream_FullMethodName, opts...) + if err != nil { + return nil, err + } + x := &studentServiceSendStudentStreamClient{stream} + return x, nil +} + +type StudentService_SendStudentStreamClient interface { + Send(*StudentRequest) error + CloseAndRecv() (*StudentResponse, error) + grpc.ClientStream +} + +type studentServiceSendStudentStreamClient struct { + grpc.ClientStream +} + +func (x *studentServiceSendStudentStreamClient) Send(m *StudentRequest) error { + return x.ClientStream.SendMsg(m) +} + +func (x *studentServiceSendStudentStreamClient) CloseAndRecv() (*StudentResponse, error) { + if err := x.ClientStream.CloseSend(); err != nil { + return nil, err + } + m := new(StudentResponse) + if err := x.ClientStream.RecvMsg(m); err != nil { + return nil, err + } + return m, nil +} + +func (c *studentServiceClient) SendAndGetStudentStream(ctx context.Context, opts ...grpc.CallOption) (StudentService_SendAndGetStudentStreamClient, error) { + stream, err := c.cc.NewStream(ctx, &StudentService_ServiceDesc.Streams[2], StudentService_SendAndGetStudentStream_FullMethodName, opts...) + if err != nil { + return nil, err + } + x := &studentServiceSendAndGetStudentStreamClient{stream} + return x, nil +} + +type StudentService_SendAndGetStudentStreamClient interface { + Send(*StudentRequest) error + Recv() (*StudentResponse, error) + grpc.ClientStream +} + +type studentServiceSendAndGetStudentStreamClient struct { + grpc.ClientStream +} + +func (x *studentServiceSendAndGetStudentStreamClient) Send(m *StudentRequest) error { + return x.ClientStream.SendMsg(m) +} + +func (x *studentServiceSendAndGetStudentStreamClient) Recv() (*StudentResponse, error) { + m := new(StudentResponse) + if err := x.ClientStream.RecvMsg(m); err != nil { + return nil, err + } + return m, nil +} + +// StudentServiceServer is the server API for StudentService service. +// All implementations must embed UnimplementedStudentServiceServer +// for forward compatibility +type StudentServiceServer interface { + GetStudent(context.Context, *StudentRequest) (*StudentResponse, error) + GetStudentStream(*StudentRequest, StudentService_GetStudentStreamServer) error + SendStudentStream(StudentService_SendStudentStreamServer) error + SendAndGetStudentStream(StudentService_SendAndGetStudentStreamServer) error + mustEmbedUnimplementedStudentServiceServer() +} + +// UnimplementedStudentServiceServer must be embedded to have forward compatible implementations. +type UnimplementedStudentServiceServer struct { +} + +func (UnimplementedStudentServiceServer) GetStudent(context.Context, *StudentRequest) (*StudentResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetStudent not implemented") +} +func (UnimplementedStudentServiceServer) GetStudentStream(*StudentRequest, StudentService_GetStudentStreamServer) error { + return status.Errorf(codes.Unimplemented, "method GetStudentStream not implemented") +} +func (UnimplementedStudentServiceServer) SendStudentStream(StudentService_SendStudentStreamServer) error { + return status.Errorf(codes.Unimplemented, "method SendStudentStream not implemented") +} +func (UnimplementedStudentServiceServer) SendAndGetStudentStream(StudentService_SendAndGetStudentStreamServer) error { + return status.Errorf(codes.Unimplemented, "method SendAndGetStudentStream not implemented") +} +func (UnimplementedStudentServiceServer) mustEmbedUnimplementedStudentServiceServer() {} + +// UnsafeStudentServiceServer may be embedded to opt out of forward compatibility for this service. +// Use of this interface is not recommended, as added methods to StudentServiceServer will +// result in compilation errors. +type UnsafeStudentServiceServer interface { + mustEmbedUnimplementedStudentServiceServer() +} + +func RegisterStudentServiceServer(s grpc.ServiceRegistrar, srv StudentServiceServer) { + s.RegisterService(&StudentService_ServiceDesc, srv) +} + +func _StudentService_GetStudent_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(StudentRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(StudentServiceServer).GetStudent(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: StudentService_GetStudent_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(StudentServiceServer).GetStudent(ctx, req.(*StudentRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _StudentService_GetStudentStream_Handler(srv interface{}, stream grpc.ServerStream) error { + m := new(StudentRequest) + if err := stream.RecvMsg(m); err != nil { + return err + } + return srv.(StudentServiceServer).GetStudentStream(m, &studentServiceGetStudentStreamServer{stream}) +} + +type StudentService_GetStudentStreamServer interface { + Send(*StudentResponse) error + grpc.ServerStream +} + +type studentServiceGetStudentStreamServer struct { + grpc.ServerStream +} + +func (x *studentServiceGetStudentStreamServer) Send(m *StudentResponse) error { + return x.ServerStream.SendMsg(m) +} + +func _StudentService_SendStudentStream_Handler(srv interface{}, stream grpc.ServerStream) error { + return srv.(StudentServiceServer).SendStudentStream(&studentServiceSendStudentStreamServer{stream}) +} + +type StudentService_SendStudentStreamServer interface { + SendAndClose(*StudentResponse) error + Recv() (*StudentRequest, error) + grpc.ServerStream +} + +type studentServiceSendStudentStreamServer struct { + grpc.ServerStream +} + +func (x *studentServiceSendStudentStreamServer) SendAndClose(m *StudentResponse) error { + return x.ServerStream.SendMsg(m) +} + +func (x *studentServiceSendStudentStreamServer) Recv() (*StudentRequest, error) { + m := new(StudentRequest) + if err := x.ServerStream.RecvMsg(m); err != nil { + return nil, err + } + return m, nil +} + +func _StudentService_SendAndGetStudentStream_Handler(srv interface{}, stream grpc.ServerStream) error { + return srv.(StudentServiceServer).SendAndGetStudentStream(&studentServiceSendAndGetStudentStreamServer{stream}) +} + +type StudentService_SendAndGetStudentStreamServer interface { + Send(*StudentResponse) error + Recv() (*StudentRequest, error) + grpc.ServerStream +} + +type studentServiceSendAndGetStudentStreamServer struct { + grpc.ServerStream +} + +func (x *studentServiceSendAndGetStudentStreamServer) Send(m *StudentResponse) error { + return x.ServerStream.SendMsg(m) +} + +func (x *studentServiceSendAndGetStudentStreamServer) Recv() (*StudentRequest, error) { + m := new(StudentRequest) + if err := x.ServerStream.RecvMsg(m); err != nil { + return nil, err + } + return m, nil +} + +// StudentService_ServiceDesc is the grpc.ServiceDesc for StudentService service. +// It's only intended for direct use with grpc.RegisterService, +// and not to be introspected or modified (even as a copy) +var StudentService_ServiceDesc = grpc.ServiceDesc{ + ServiceName: "dineth.grpc.api.v1.student.StudentService", + HandlerType: (*StudentServiceServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "GetStudent", + Handler: _StudentService_GetStudent_Handler, + }, + }, + Streams: []grpc.StreamDesc{ + { + StreamName: "GetStudentStream", + Handler: _StudentService_GetStudentStream_Handler, + ServerStreams: true, + }, + { + StreamName: "SendStudentStream", + Handler: _StudentService_SendStudentStream_Handler, + ClientStreams: true, + }, + { + StreamName: "SendAndGetStudentStream", + Handler: _StudentService_SendAndGetStudentStream_Handler, + ServerStreams: true, + ClientStreams: true, + }, + }, + Metadata: "student.proto", +} \ No newline at end of file diff --git a/test/integration/integration/utils/grpc-code/student_default_version/student2.go b/test/integration/integration/utils/grpc-code/student_default_version/student2.go new file mode 100644 index 0000000000..9a56a4b6bd --- /dev/null +++ b/test/integration/integration/utils/grpc-code/student_default_version/student2.go @@ -0,0 +1,47 @@ +package student_default_version + +import ( + "context" + "log" + "time" + + "github.com/wso2/apk/test/integration/integration/utils/grpcutils" + "google.golang.org/grpc" +) + +type StudentResponseSatisfier struct{} + +// IsSatisfactory checks if the given response is satisfactory based on the expected response. +func (srs StudentResponseSatisfier) IsSatisfactory(response any, expectedResponse grpcutils.ExpectedResponse) bool { + // Type assert the response to *student.StudentResponse + resp, respOk := response.(*StudentResponse) + if !respOk { + log.Println("Failed to assert response as *student.StudentResponse") + return false + } + // Type assert the expected output to *student.StudentResponse + expectedResp, expOk := expectedResponse.Out.(*StudentResponse) + if !expOk { + log.Println("Failed to assert expectedResponse.Out as *student.StudentResponse") + return false + } + + // Compare the actual response with the expected response + if resp.Name == expectedResp.Name && resp.Age == expectedResp.Age { + return true + } else { + log.Println("Response does not match the expected output") + return false + } +} +func GetStudent(conn *grpc.ClientConn) (any, error) { + c := NewStudentServiceClient(conn) + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + r := &StudentRequest{Id: 1234} + response, err := c.GetStudent(ctx, r) + if err != nil { + return nil, err + } + return response, nil +} diff --git a/test/integration/integration/utils/grpc-code/student_default_version/student_default_version.pb.go b/test/integration/integration/utils/grpc-code/student_default_version/student_default_version.pb.go new file mode 100644 index 0000000000..3437a246f7 --- /dev/null +++ b/test/integration/integration/utils/grpc-code/student_default_version/student_default_version.pb.go @@ -0,0 +1,251 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.33.0 +// protoc v3.12.4 +// source: student_default_version.proto + +package student_default_version + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type StudentRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Id int32 `protobuf:"varint,3,opt,name=id,proto3" json:"id,omitempty"` +} + +func (x *StudentRequest) Reset() { + *x = StudentRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_student_default_version_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *StudentRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*StudentRequest) ProtoMessage() {} + +func (x *StudentRequest) ProtoReflect() protoreflect.Message { + mi := &file_student_default_version_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use StudentRequest.ProtoReflect.Descriptor instead. +func (*StudentRequest) Descriptor() ([]byte, []int) { + return file_student_default_version_proto_rawDescGZIP(), []int{0} +} + +func (x *StudentRequest) GetId() int32 { + if x != nil { + return x.Id + } + return 0 +} + +type StudentResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + Age int32 `protobuf:"varint,2,opt,name=age,proto3" json:"age,omitempty"` +} + +func (x *StudentResponse) Reset() { + *x = StudentResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_student_default_version_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *StudentResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*StudentResponse) ProtoMessage() {} + +func (x *StudentResponse) ProtoReflect() protoreflect.Message { + mi := &file_student_default_version_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use StudentResponse.ProtoReflect.Descriptor instead. +func (*StudentResponse) Descriptor() ([]byte, []int) { + return file_student_default_version_proto_rawDescGZIP(), []int{1} +} + +func (x *StudentResponse) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *StudentResponse) GetAge() int32 { + if x != nil { + return x.Age + } + return 0 +} + +var File_student_default_version_proto protoreflect.FileDescriptor + +var file_student_default_version_proto_rawDesc = []byte{ + 0x0a, 0x1d, 0x73, 0x74, 0x75, 0x64, 0x65, 0x6e, 0x74, 0x5f, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, + 0x74, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, + 0x17, 0x64, 0x69, 0x6e, 0x65, 0x74, 0x68, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x61, 0x70, 0x69, + 0x2e, 0x73, 0x74, 0x75, 0x64, 0x65, 0x6e, 0x74, 0x22, 0x20, 0x0a, 0x0e, 0x53, 0x74, 0x75, 0x64, + 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x02, 0x69, 0x64, 0x22, 0x37, 0x0a, 0x0f, 0x53, 0x74, + 0x75, 0x64, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x12, 0x0a, + 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, + 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x03, + 0x61, 0x67, 0x65, 0x32, 0xbe, 0x03, 0x0a, 0x0e, 0x53, 0x74, 0x75, 0x64, 0x65, 0x6e, 0x74, 0x53, + 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x61, 0x0a, 0x0a, 0x47, 0x65, 0x74, 0x53, 0x74, 0x75, + 0x64, 0x65, 0x6e, 0x74, 0x12, 0x27, 0x2e, 0x64, 0x69, 0x6e, 0x65, 0x74, 0x68, 0x2e, 0x67, 0x72, + 0x70, 0x63, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x74, 0x75, 0x64, 0x65, 0x6e, 0x74, 0x2e, 0x53, + 0x74, 0x75, 0x64, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x28, 0x2e, + 0x64, 0x69, 0x6e, 0x65, 0x74, 0x68, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x61, 0x70, 0x69, 0x2e, + 0x73, 0x74, 0x75, 0x64, 0x65, 0x6e, 0x74, 0x2e, 0x53, 0x74, 0x75, 0x64, 0x65, 0x6e, 0x74, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x69, 0x0a, 0x10, 0x47, 0x65, 0x74, + 0x53, 0x74, 0x75, 0x64, 0x65, 0x6e, 0x74, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, 0x27, 0x2e, + 0x64, 0x69, 0x6e, 0x65, 0x74, 0x68, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x61, 0x70, 0x69, 0x2e, + 0x73, 0x74, 0x75, 0x64, 0x65, 0x6e, 0x74, 0x2e, 0x53, 0x74, 0x75, 0x64, 0x65, 0x6e, 0x74, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x28, 0x2e, 0x64, 0x69, 0x6e, 0x65, 0x74, 0x68, 0x2e, + 0x67, 0x72, 0x70, 0x63, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x74, 0x75, 0x64, 0x65, 0x6e, 0x74, + 0x2e, 0x53, 0x74, 0x75, 0x64, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x22, 0x00, 0x30, 0x01, 0x12, 0x6a, 0x0a, 0x11, 0x53, 0x65, 0x6e, 0x64, 0x53, 0x74, 0x75, 0x64, + 0x65, 0x6e, 0x74, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, 0x27, 0x2e, 0x64, 0x69, 0x6e, 0x65, + 0x74, 0x68, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x74, 0x75, 0x64, + 0x65, 0x6e, 0x74, 0x2e, 0x53, 0x74, 0x75, 0x64, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x28, 0x2e, 0x64, 0x69, 0x6e, 0x65, 0x74, 0x68, 0x2e, 0x67, 0x72, 0x70, 0x63, + 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x74, 0x75, 0x64, 0x65, 0x6e, 0x74, 0x2e, 0x53, 0x74, 0x75, + 0x64, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x28, 0x01, + 0x12, 0x72, 0x0a, 0x17, 0x53, 0x65, 0x6e, 0x64, 0x41, 0x6e, 0x64, 0x47, 0x65, 0x74, 0x53, 0x74, + 0x75, 0x64, 0x65, 0x6e, 0x74, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, 0x27, 0x2e, 0x64, 0x69, + 0x6e, 0x65, 0x74, 0x68, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x74, + 0x75, 0x64, 0x65, 0x6e, 0x74, 0x2e, 0x53, 0x74, 0x75, 0x64, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x28, 0x2e, 0x64, 0x69, 0x6e, 0x65, 0x74, 0x68, 0x2e, 0x67, 0x72, + 0x70, 0x63, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x73, 0x74, 0x75, 0x64, 0x65, 0x6e, 0x74, 0x2e, 0x53, + 0x74, 0x75, 0x64, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, + 0x28, 0x01, 0x30, 0x01, 0x42, 0x0f, 0x0a, 0x0b, 0x6f, 0x72, 0x67, 0x2e, 0x65, 0x78, 0x61, 0x6d, + 0x70, 0x6c, 0x65, 0x50, 0x01, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_student_default_version_proto_rawDescOnce sync.Once + file_student_default_version_proto_rawDescData = file_student_default_version_proto_rawDesc +) + +func file_student_default_version_proto_rawDescGZIP() []byte { + file_student_default_version_proto_rawDescOnce.Do(func() { + file_student_default_version_proto_rawDescData = protoimpl.X.CompressGZIP(file_student_default_version_proto_rawDescData) + }) + return file_student_default_version_proto_rawDescData +} + +var file_student_default_version_proto_msgTypes = make([]protoimpl.MessageInfo, 2) +var file_student_default_version_proto_goTypes = []interface{}{ + (*StudentRequest)(nil), // 0: dineth.grpc.api.student.StudentRequest + (*StudentResponse)(nil), // 1: dineth.grpc.api.student.StudentResponse +} +var file_student_default_version_proto_depIdxs = []int32{ + 0, // 0: dineth.grpc.api.student.StudentService.GetStudent:input_type -> dineth.grpc.api.student.StudentRequest + 0, // 1: dineth.grpc.api.student.StudentService.GetStudentStream:input_type -> dineth.grpc.api.student.StudentRequest + 0, // 2: dineth.grpc.api.student.StudentService.SendStudentStream:input_type -> dineth.grpc.api.student.StudentRequest + 0, // 3: dineth.grpc.api.student.StudentService.SendAndGetStudentStream:input_type -> dineth.grpc.api.student.StudentRequest + 1, // 4: dineth.grpc.api.student.StudentService.GetStudent:output_type -> dineth.grpc.api.student.StudentResponse + 1, // 5: dineth.grpc.api.student.StudentService.GetStudentStream:output_type -> dineth.grpc.api.student.StudentResponse + 1, // 6: dineth.grpc.api.student.StudentService.SendStudentStream:output_type -> dineth.grpc.api.student.StudentResponse + 1, // 7: dineth.grpc.api.student.StudentService.SendAndGetStudentStream:output_type -> dineth.grpc.api.student.StudentResponse + 4, // [4:8] is the sub-list for method output_type + 0, // [0:4] is the sub-list for method input_type + 0, // [0:0] is the sub-list for extension type_name + 0, // [0:0] is the sub-list for extension extendee + 0, // [0:0] is the sub-list for field type_name +} + +func init() { file_student_default_version_proto_init() } +func file_student_default_version_proto_init() { + if File_student_default_version_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_student_default_version_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*StudentRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_student_default_version_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*StudentResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_student_default_version_proto_rawDesc, + NumEnums: 0, + NumMessages: 2, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_student_default_version_proto_goTypes, + DependencyIndexes: file_student_default_version_proto_depIdxs, + MessageInfos: file_student_default_version_proto_msgTypes, + }.Build() + File_student_default_version_proto = out.File + file_student_default_version_proto_rawDesc = nil + file_student_default_version_proto_goTypes = nil + file_student_default_version_proto_depIdxs = nil +} \ No newline at end of file diff --git a/test/integration/integration/utils/grpc-code/student_default_version/student_default_version_grpc.pb.go b/test/integration/integration/utils/grpc-code/student_default_version/student_default_version_grpc.pb.go new file mode 100644 index 0000000000..c2ca3f9980 --- /dev/null +++ b/test/integration/integration/utils/grpc-code/student_default_version/student_default_version_grpc.pb.go @@ -0,0 +1,314 @@ +// Code generated by protoc-gen-go-grpc. DO NOT EDIT. +// versions: +// - protoc-gen-go-grpc v1.3.0 +// - protoc v3.12.4 +// source: student_default_version.proto + +package student_default_version + +import ( + context "context" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" +) + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +// Requires gRPC-Go v1.32.0 or later. +const _ = grpc.SupportPackageIsVersion7 + +const ( + StudentService_GetStudent_FullMethodName = "/dineth.grpc.api.student.StudentService/GetStudent" + StudentService_GetStudentStream_FullMethodName = "/dineth.grpc.api.student.StudentService/GetStudentStream" + StudentService_SendStudentStream_FullMethodName = "/dineth.grpc.api.student.StudentService/SendStudentStream" + StudentService_SendAndGetStudentStream_FullMethodName = "/dineth.grpc.api.student.StudentService/SendAndGetStudentStream" +) + +// StudentServiceClient is the client API for StudentService service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +type StudentServiceClient interface { + GetStudent(ctx context.Context, in *StudentRequest, opts ...grpc.CallOption) (*StudentResponse, error) + GetStudentStream(ctx context.Context, in *StudentRequest, opts ...grpc.CallOption) (StudentService_GetStudentStreamClient, error) + SendStudentStream(ctx context.Context, opts ...grpc.CallOption) (StudentService_SendStudentStreamClient, error) + SendAndGetStudentStream(ctx context.Context, opts ...grpc.CallOption) (StudentService_SendAndGetStudentStreamClient, error) +} + +type studentServiceClient struct { + cc grpc.ClientConnInterface +} + +func NewStudentServiceClient(cc grpc.ClientConnInterface) StudentServiceClient { + return &studentServiceClient{cc} +} + +func (c *studentServiceClient) GetStudent(ctx context.Context, in *StudentRequest, opts ...grpc.CallOption) (*StudentResponse, error) { + out := new(StudentResponse) + err := c.cc.Invoke(ctx, StudentService_GetStudent_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *studentServiceClient) GetStudentStream(ctx context.Context, in *StudentRequest, opts ...grpc.CallOption) (StudentService_GetStudentStreamClient, error) { + stream, err := c.cc.NewStream(ctx, &StudentService_ServiceDesc.Streams[0], StudentService_GetStudentStream_FullMethodName, opts...) + if err != nil { + return nil, err + } + x := &studentServiceGetStudentStreamClient{stream} + if err := x.ClientStream.SendMsg(in); err != nil { + return nil, err + } + if err := x.ClientStream.CloseSend(); err != nil { + return nil, err + } + return x, nil +} + +type StudentService_GetStudentStreamClient interface { + Recv() (*StudentResponse, error) + grpc.ClientStream +} + +type studentServiceGetStudentStreamClient struct { + grpc.ClientStream +} + +func (x *studentServiceGetStudentStreamClient) Recv() (*StudentResponse, error) { + m := new(StudentResponse) + if err := x.ClientStream.RecvMsg(m); err != nil { + return nil, err + } + return m, nil +} + +func (c *studentServiceClient) SendStudentStream(ctx context.Context, opts ...grpc.CallOption) (StudentService_SendStudentStreamClient, error) { + stream, err := c.cc.NewStream(ctx, &StudentService_ServiceDesc.Streams[1], StudentService_SendStudentStream_FullMethodName, opts...) + if err != nil { + return nil, err + } + x := &studentServiceSendStudentStreamClient{stream} + return x, nil +} + +type StudentService_SendStudentStreamClient interface { + Send(*StudentRequest) error + CloseAndRecv() (*StudentResponse, error) + grpc.ClientStream +} + +type studentServiceSendStudentStreamClient struct { + grpc.ClientStream +} + +func (x *studentServiceSendStudentStreamClient) Send(m *StudentRequest) error { + return x.ClientStream.SendMsg(m) +} + +func (x *studentServiceSendStudentStreamClient) CloseAndRecv() (*StudentResponse, error) { + if err := x.ClientStream.CloseSend(); err != nil { + return nil, err + } + m := new(StudentResponse) + if err := x.ClientStream.RecvMsg(m); err != nil { + return nil, err + } + return m, nil +} + +func (c *studentServiceClient) SendAndGetStudentStream(ctx context.Context, opts ...grpc.CallOption) (StudentService_SendAndGetStudentStreamClient, error) { + stream, err := c.cc.NewStream(ctx, &StudentService_ServiceDesc.Streams[2], StudentService_SendAndGetStudentStream_FullMethodName, opts...) + if err != nil { + return nil, err + } + x := &studentServiceSendAndGetStudentStreamClient{stream} + return x, nil +} + +type StudentService_SendAndGetStudentStreamClient interface { + Send(*StudentRequest) error + Recv() (*StudentResponse, error) + grpc.ClientStream +} + +type studentServiceSendAndGetStudentStreamClient struct { + grpc.ClientStream +} + +func (x *studentServiceSendAndGetStudentStreamClient) Send(m *StudentRequest) error { + return x.ClientStream.SendMsg(m) +} + +func (x *studentServiceSendAndGetStudentStreamClient) Recv() (*StudentResponse, error) { + m := new(StudentResponse) + if err := x.ClientStream.RecvMsg(m); err != nil { + return nil, err + } + return m, nil +} + +// StudentServiceServer is the server API for StudentService service. +// All implementations must embed UnimplementedStudentServiceServer +// for forward compatibility +type StudentServiceServer interface { + GetStudent(context.Context, *StudentRequest) (*StudentResponse, error) + GetStudentStream(*StudentRequest, StudentService_GetStudentStreamServer) error + SendStudentStream(StudentService_SendStudentStreamServer) error + SendAndGetStudentStream(StudentService_SendAndGetStudentStreamServer) error + mustEmbedUnimplementedStudentServiceServer() +} + +// UnimplementedStudentServiceServer must be embedded to have forward compatible implementations. +type UnimplementedStudentServiceServer struct { +} + +func (UnimplementedStudentServiceServer) GetStudent(context.Context, *StudentRequest) (*StudentResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetStudent not implemented") +} +func (UnimplementedStudentServiceServer) GetStudentStream(*StudentRequest, StudentService_GetStudentStreamServer) error { + return status.Errorf(codes.Unimplemented, "method GetStudentStream not implemented") +} +func (UnimplementedStudentServiceServer) SendStudentStream(StudentService_SendStudentStreamServer) error { + return status.Errorf(codes.Unimplemented, "method SendStudentStream not implemented") +} +func (UnimplementedStudentServiceServer) SendAndGetStudentStream(StudentService_SendAndGetStudentStreamServer) error { + return status.Errorf(codes.Unimplemented, "method SendAndGetStudentStream not implemented") +} +func (UnimplementedStudentServiceServer) mustEmbedUnimplementedStudentServiceServer() {} + +// UnsafeStudentServiceServer may be embedded to opt out of forward compatibility for this service. +// Use of this interface is not recommended, as added methods to StudentServiceServer will +// result in compilation errors. +type UnsafeStudentServiceServer interface { + mustEmbedUnimplementedStudentServiceServer() +} + +func RegisterStudentServiceServer(s grpc.ServiceRegistrar, srv StudentServiceServer) { + s.RegisterService(&StudentService_ServiceDesc, srv) +} + +func _StudentService_GetStudent_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(StudentRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(StudentServiceServer).GetStudent(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: StudentService_GetStudent_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(StudentServiceServer).GetStudent(ctx, req.(*StudentRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _StudentService_GetStudentStream_Handler(srv interface{}, stream grpc.ServerStream) error { + m := new(StudentRequest) + if err := stream.RecvMsg(m); err != nil { + return err + } + return srv.(StudentServiceServer).GetStudentStream(m, &studentServiceGetStudentStreamServer{stream}) +} + +type StudentService_GetStudentStreamServer interface { + Send(*StudentResponse) error + grpc.ServerStream +} + +type studentServiceGetStudentStreamServer struct { + grpc.ServerStream +} + +func (x *studentServiceGetStudentStreamServer) Send(m *StudentResponse) error { + return x.ServerStream.SendMsg(m) +} + +func _StudentService_SendStudentStream_Handler(srv interface{}, stream grpc.ServerStream) error { + return srv.(StudentServiceServer).SendStudentStream(&studentServiceSendStudentStreamServer{stream}) +} + +type StudentService_SendStudentStreamServer interface { + SendAndClose(*StudentResponse) error + Recv() (*StudentRequest, error) + grpc.ServerStream +} + +type studentServiceSendStudentStreamServer struct { + grpc.ServerStream +} + +func (x *studentServiceSendStudentStreamServer) SendAndClose(m *StudentResponse) error { + return x.ServerStream.SendMsg(m) +} + +func (x *studentServiceSendStudentStreamServer) Recv() (*StudentRequest, error) { + m := new(StudentRequest) + if err := x.ServerStream.RecvMsg(m); err != nil { + return nil, err + } + return m, nil +} + +func _StudentService_SendAndGetStudentStream_Handler(srv interface{}, stream grpc.ServerStream) error { + return srv.(StudentServiceServer).SendAndGetStudentStream(&studentServiceSendAndGetStudentStreamServer{stream}) +} + +type StudentService_SendAndGetStudentStreamServer interface { + Send(*StudentResponse) error + Recv() (*StudentRequest, error) + grpc.ServerStream +} + +type studentServiceSendAndGetStudentStreamServer struct { + grpc.ServerStream +} + +func (x *studentServiceSendAndGetStudentStreamServer) Send(m *StudentResponse) error { + return x.ServerStream.SendMsg(m) +} + +func (x *studentServiceSendAndGetStudentStreamServer) Recv() (*StudentRequest, error) { + m := new(StudentRequest) + if err := x.ServerStream.RecvMsg(m); err != nil { + return nil, err + } + return m, nil +} + +// StudentService_ServiceDesc is the grpc.ServiceDesc for StudentService service. +// It's only intended for direct use with grpc.RegisterService, +// and not to be introspected or modified (even as a copy) +var StudentService_ServiceDesc = grpc.ServiceDesc{ + ServiceName: "dineth.grpc.api.student.StudentService", + HandlerType: (*StudentServiceServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "GetStudent", + Handler: _StudentService_GetStudent_Handler, + }, + }, + Streams: []grpc.StreamDesc{ + { + StreamName: "GetStudentStream", + Handler: _StudentService_GetStudentStream_Handler, + ServerStreams: true, + }, + { + StreamName: "SendStudentStream", + Handler: _StudentService_SendStudentStream_Handler, + ClientStreams: true, + }, + { + StreamName: "SendAndGetStudentStream", + Handler: _StudentService_SendAndGetStudentStream_Handler, + ServerStreams: true, + ClientStreams: true, + }, + }, + Metadata: "student_default_version.proto", +} \ No newline at end of file diff --git a/test/integration/integration/utils/grpcutils/helpers.go b/test/integration/integration/utils/grpcutils/helpers.go new file mode 100644 index 0000000000..a4a27546e9 --- /dev/null +++ b/test/integration/integration/utils/grpcutils/helpers.go @@ -0,0 +1,91 @@ +package grpcutils + +import ( + "crypto/tls" + "testing" + "time" + + "google.golang.org/grpc" + "google.golang.org/grpc/credentials" +) + +type Request struct { + Host string + Headers map[string]string +} +type ClientCreator[T any] func(conn *grpc.ClientConn) T +type ExpectedResponse struct { + Out any + Err error +} + +type GRPCTestCase struct { + Request Request + ExpectedResponse ExpectedResponse + ActualResponse any + Name string + Method func(conn *grpc.ClientConn) (any, error) + Satisfier ResponseSatisfier +} +type ResponseSatisfier interface { + IsSatisfactory(response interface{}, expectedResponse ExpectedResponse) bool +} + +func DialGRPCServer(gwAddr string, t *testing.T) (*grpc.ClientConn, error) { + // Set up a connection to the server. + t.Logf("Dialing gRPC server at %s...", gwAddr) + creds := credentials.NewTLS(&tls.Config{InsecureSkipVerify: true}) + conn, err := grpc.Dial(gwAddr, grpc.WithTransportCredentials(creds)) + if err != nil { + t.Fatalf("Could not connect to the server: %v", err) + } + return conn, nil +} +func InvokeGRPCClientUntilSatisfied(gwAddr string, t *testing.T, testCase GRPCTestCase, satisfier ResponseSatisfier, fn ExecuteClientCall) { + //(delay to allow CRs to be applied) + time.Sleep(5 * time.Second) + + var out any + var err error + attempt := 0 + maxAttempts := 4 + expected := testCase.ExpectedResponse + timeoutDuration := 50 * time.Second + for attempt < maxAttempts { + t.Logf("Attempt %d to invoke gRPC client...", attempt+1) + out, err = InvokeGRPCClient(gwAddr, t, fn) + + if err != nil { + t.Logf("Error on attempt %d: %v", attempt+1, err) + } else { + if satisfier.IsSatisfactory(out, expected) { + return + } + } + + if attempt < maxAttempts-1 { + t.Logf("Waiting %s seconds before next attempt...", timeoutDuration) + time.Sleep(timeoutDuration) + } + attempt++ + } + + t.Logf("Failed to receive a satisfactory response after %d attempts", maxAttempts) + t.Fail() +} + +type ExecuteClientCall func(conn *grpc.ClientConn) (any, error) + +func InvokeGRPCClient(gwAddr string, t *testing.T, fn ExecuteClientCall) (any, error) { + + conn, err := DialGRPCServer(gwAddr, t) + if err != nil { + t.Fatalf("Could not connect to the server: %v", err) + } + + response, err := fn(conn) + if err != nil { + return nil, err + } + return response, nil +} diff --git a/test/integration/scripts/run-tests.sh b/test/integration/scripts/run-tests.sh index 4bda1c5905..da0444b819 100644 --- a/test/integration/scripts/run-tests.sh +++ b/test/integration/scripts/run-tests.sh @@ -27,15 +27,14 @@ helm repo add bitnami https://charts.bitnami.com/bitnami helm repo add jetstack https://charts.jetstack.io helm dependency build ../../helm-charts helm install apk-test-setup ../../helm-charts -n apk-integration-test \ ---set wso2.apk.dp.adapter.deployment.image=adapter:test \ ---set wso2.apk.dp.adapter.deployment.imagePullPolicy=IfNotPresent \ ---set wso2.apk.dp.gatewayRuntime.deployment.enforcer.image=enforcer:test \ ---set wso2.apk.dp.gatewayRuntime.deployment.enforcer.imagePullPolicy=IfNotPresent \ ---set wso2.apk.dp.configdeployer.deployment.replicas=0 \ ---set wso2.apk.dp.ratelimiter.enabled=false \ ---set wso2.apk.dp.redis.enabled=false \ ---set wso2.apk.dp.gateway.httpListener.enabled=true - + --set wso2.apk.dp.adapter.deployment.image=adapter:test \ + --set wso2.apk.dp.adapter.deployment.imagePullPolicy=IfNotPresent \ + --set wso2.apk.dp.gatewayRuntime.deployment.enforcer.image=enforcer:test \ + --set wso2.apk.dp.gatewayRuntime.deployment.enforcer.imagePullPolicy=IfNotPresent \ + --set wso2.apk.dp.configdeployer.deployment.replicas=0 \ + --set wso2.apk.dp.ratelimiter.enabled=false \ + --set wso2.apk.dp.redis.enabled=false \ + --set wso2.apk.dp.gateway.httpListener.enabled=true # Wait gateway resources to be available. kubectl wait --timeout=5m -n gateway-system deployment/gateway-api-admission-server --for=condition=Available @@ -80,6 +79,7 @@ sudo echo "$IP different-endpoint-with-same-route.test.gw.wso2.com" | sudo tee - sudo echo "$IP backend-api-key-security.test.gw.wso2.com" | sudo tee -a /etc/hosts sudo echo "$IP custom-auth-header.test.gw.wso2.com" | sudo tee -a /etc/hosts sudo echo "$IP gql.test.gw.wso2.com" | sudo tee -a /etc/hosts +sudo echo "$IP grpc.test.gw.wso2.com" | sudo tee -a /etc/hosts sudo echo "$IP api-level-jwt.test.gw.wso2.com" | sudo tee -a /etc/hosts sudo echo "$IP resource-level-jwt.test.gw.wso2.com" | sudo tee -a /etc/hosts sudo echo "255.255.255.255 broadcasthost" | sudo tee -a /etc/hosts diff --git a/test/integration/scripts/setup-hosts.sh b/test/integration/scripts/setup-hosts.sh index 8e68ef0768..8c44596fe2 100644 --- a/test/integration/scripts/setup-hosts.sh +++ b/test/integration/scripts/setup-hosts.sh @@ -39,17 +39,17 @@ sudo echo "$IP different-endpoint-with-same-route.test.gw.wso2.com" | sudo tee - sudo echo "$IP backend-api-key-security.test.gw.wso2.com" | sudo tee -a /etc/hosts sudo echo "$IP custom-auth-header.test.gw.wso2.com" | sudo tee -a /etc/hosts sudo echo "$IP gql.test.gw.wso2.com" | sudo tee -a /etc/hosts +sudo echo "$IP grpc.test.gw.wso2.com" | sudo tee -a /etc/hosts sudo echo "$IP api-level-jwt.test.gw.wso2.com" | sudo tee -a /etc/hosts sudo echo "$IP resource-level-jwt.test.gw.wso2.com" | sudo tee -a /etc/hosts sudo echo "255.255.255.255 broadcasthost" | sudo tee -a /etc/hosts sudo echo "::1 localhost" | sudo tee -a /etc/hosts - echo "AKS ratelimitter time now: " -kubectl -n apk-integration-test exec -it $(kubectl -n apk-integration-test get pods -l app.kubernetes.io/app=ratelimiter -o jsonpath='{.items[0].metadata.name}') -- /bin/sh -c "date" +kubectl -n apk-integration-test exec -it $(kubectl -n apk-integration-test get pods -l app.kubernetes.io/app=ratelimiter -o jsonpath='{.items[0].metadata.name}') -- /bin/sh -c "date" echo "VM time now: " date echo "AKS ratelimitter time2 now: " -kubectl -n apk-integration-test exec -it $(kubectl -n apk-integration-test get pods -l app.kubernetes.io/app=ratelimiter -o jsonpath='{.items[0].metadata.name}') -- /bin/sh -c "date" +kubectl -n apk-integration-test exec -it $(kubectl -n apk-integration-test get pods -l app.kubernetes.io/app=ratelimiter -o jsonpath='{.items[0].metadata.name}') -- /bin/sh -c "date" echo "VM time2 now: " -date \ No newline at end of file +date