diff --git a/adapter/internal/discovery/xds/conf/config.toml b/adapter/internal/discovery/xds/conf/config.toml index e69de29bb2..06fb27b9ec 100644 --- a/adapter/internal/discovery/xds/conf/config.toml +++ b/adapter/internal/discovery/xds/conf/config.toml @@ -0,0 +1,2 @@ +[envoy] +enableIntelligentRouting = true \ No newline at end of file diff --git a/adapter/internal/discovery/xds/marshaller.go b/adapter/internal/discovery/xds/marshaller.go index d36cee776b..0b4fcfc943 100644 --- a/adapter/internal/discovery/xds/marshaller.go +++ b/adapter/internal/discovery/xds/marshaller.go @@ -34,8 +34,6 @@ const ( DeleteEvent ) -const blockedStatus string = "BLOCKED" - // MarshalConfig will marshal a Config struct - read from the config toml - to // enfocer's CDS resource representation. func MarshalConfig(config *config.Config) *enforcer.Config { diff --git a/adapter/internal/discovery/xds/semantic_versioning.go b/adapter/internal/discovery/xds/semantic_versioning.go index 6a94e19d2d..2a39d1f562 100644 --- a/adapter/internal/discovery/xds/semantic_versioning.go +++ b/adapter/internal/discovery/xds/semantic_versioning.go @@ -25,7 +25,6 @@ import ( "github.com/wso2/apk/adapter/config" logger "github.com/wso2/apk/adapter/internal/loggers" logging "github.com/wso2/apk/adapter/internal/logging" - "github.com/wso2/apk/adapter/internal/oasparser/model" semantic_version "github.com/wso2/apk/adapter/pkg/semanticversion" ) @@ -68,8 +67,7 @@ func GetMinorVersionRange(semVersion semantic_version.SemVersion) string { } func updateRoutingRulesOnAPIUpdate(organizationID, apiIdentifier, apiName, apiVersion, vHost string) { - - apiSemVersion, err := semantic_version.ValidateAndGetVersionComponents(apiVersion, apiName) + apiSemVersion, err := semantic_version.ValidateAndGetVersionComponents(apiVersion) // If the version validation is not success, we just proceed without intelligent version // Valid version pattern: vx.y.z or vx.y where x, y and z are non-negative integers and v is a prefix if err != nil && apiSemVersion == nil { @@ -90,9 +88,9 @@ func updateRoutingRulesOnAPIUpdate(organizationID, apiIdentifier, apiName, apiVe // Remove the existing regexes from the path specifier when latest major and/or minor version is available if (isMajorRangeRegexAvailable || isMinorRangeRegexAvailable) && (isLatestMajorVersion || isLatestMinorVersion) { // Organization's all apis - for _, envoyInternalAPI := range orgAPIMap[organizationID] { + for vuuid, envoyInternalAPI := range orgAPIMap[organizationID] { // API's all versions in the same vHost - if envoyInternalAPI.adapterInternalAPI.GetTitle() == apiName && isVHostMatched(organizationID, vHost) { + if envoyInternalAPI.adapterInternalAPI.GetTitle() == apiName && strings.HasPrefix(vuuid+":", vHost) { if (isMajorRangeRegexAvailable && envoyInternalAPI.adapterInternalAPI.GetVersion() == existingMajorRangeLatestSemVersion.Version) || (isMinorRangeRegexAvailable && envoyInternalAPI.adapterInternalAPI.GetVersion() == existingMinorRangeLatestSemVersion.Version) { @@ -167,8 +165,7 @@ func updateRoutingRulesOnAPIUpdate(organizationID, apiIdentifier, apiName, apiVe } route.Match.PathSpecifier = pathSpecifier - action := &routev3.Route_Route{} - action = route.Action.(*routev3.Route_Route) + action := route.Action.(*routev3.Route_Route) action.Route.RegexRewrite.Pattern.Regex = regexRewritePattern route.Action = action } @@ -176,183 +173,46 @@ func updateRoutingRulesOnAPIUpdate(organizationID, apiIdentifier, apiName, apiVe } } -func updateRoutingRulesOnAPIDelete(organizationID, apiIdentifier string, api model.AdapterInternalAPI) { - // Update the intelligent routing if the deleting API is the latest version of the API range - // and the API range has other versions - vhost, err := ExtractVhostFromAPIIdentifier(apiIdentifier) - if err != nil { - logger.LoggerXds.ErrorC(logging.PrintError(logging.Error1411, logging.MAJOR, - "Error extracting vhost from API identifier: %v for Organization %v. Ignore deploying the API, error: %v", - apiIdentifier, organizationID, err)) - } - apiRangeIdentifier := generateIdentifierForAPIWithoutVersion(vhost, api.GetTitle()) - - latestAPIVersionMap, latestAPIVersionMapExists := orgIDLatestAPIVersionMap[organizationID][apiRangeIdentifier] - if !latestAPIVersionMapExists { - return - } - deletingAPISemVersion, _ := semantic_version.ValidateAndGetVersionComponents(api.GetVersion(), api.GetTitle()) - if deletingAPISemVersion == nil { - return - } - majorVersionRange := GetMajorVersionRange(*deletingAPISemVersion) - newLatestMajorRangeAPIIdentifier := "" - - if deletingAPIsMajorRangeLatestAPISemVersion, ok := latestAPIVersionMap[majorVersionRange]; ok { - if deletingAPIsMajorRangeLatestAPISemVersion.Version == api.GetVersion() { - newLatestMajorRangeAPI := &semantic_version.SemVersion{ - Version: "", - Major: deletingAPISemVersion.Major, - Minor: 0, - Patch: nil, - } - for currentAPIIdentifier, envoyInternalAPI := range orgAPIMap[organizationID] { - // Iterate all the API versions other than the deleting API itself - if envoyInternalAPI.adapterInternalAPI.GetTitle() == api.GetTitle() && currentAPIIdentifier != apiIdentifier { - currentAPISemVersion, _ := semantic_version.ValidateAndGetVersionComponents(envoyInternalAPI.adapterInternalAPI.GetVersion(), envoyInternalAPI.adapterInternalAPI.GetTitle()) - if currentAPISemVersion != nil { - if currentAPISemVersion.Major == deletingAPISemVersion.Major { - if newLatestMajorRangeAPI.Compare(*currentAPISemVersion) { - newLatestMajorRangeAPI = currentAPISemVersion - newLatestMajorRangeAPIIdentifier = currentAPIIdentifier - } - } - } - } - } - if newLatestMajorRangeAPIIdentifier != "" { - orgIDLatestAPIVersionMap[organizationID][apiRangeIdentifier][majorVersionRange] = *newLatestMajorRangeAPI - apiRoutes := getRoutesForAPIIdentifier(organizationID, newLatestMajorRangeAPIIdentifier) - for _, route := range apiRoutes { - regex := route.GetMatch().GetSafeRegex().GetRegex() - regexRewritePattern := route.GetRoute().GetRegexRewrite().GetPattern().GetRegex() - newLatestMajorRangeAPIVersionRegex := GetVersionMatchRegex(newLatestMajorRangeAPI.Version) - // Remove any available minor version range regexes and apply the minor range regex - regex = strings.Replace( - regex, - GetMinorVersionRangeRegex(*newLatestMajorRangeAPI), - newLatestMajorRangeAPIVersionRegex, - 1, - ) - regexRewritePattern = strings.Replace( - regexRewritePattern, - GetMinorVersionRangeRegex(*newLatestMajorRangeAPI), - newLatestMajorRangeAPIVersionRegex, - 1, - ) - regex = strings.Replace( - regex, - newLatestMajorRangeAPIVersionRegex, - GetMajorMinorVersionRangeRegex(*newLatestMajorRangeAPI), - 1, - ) - regexRewritePattern = strings.Replace( - regexRewritePattern, - newLatestMajorRangeAPIVersionRegex, - GetMajorMinorVersionRangeRegex(*newLatestMajorRangeAPI), - 1, - ) - pathSpecifier := &routev3.RouteMatch_SafeRegex{ - SafeRegex: &envoy_type_matcherv3.RegexMatcher{ - Regex: regex, - }, - } - - route.Match.PathSpecifier = pathSpecifier - action := &routev3.Route_Route{} - action = route.Action.(*routev3.Route_Route) - action.Route.RegexRewrite.Pattern.Regex = regexRewritePattern - route.Action = action - } - } else { - delete(orgIDLatestAPIVersionMap[organizationID][apiRangeIdentifier], majorVersionRange) - } +func updateSemanticVersioning(org string, apiRangeIdentifiers map[string]struct{}) { + // Iterate all the APIs in the API range + for vuuid, api := range orgAPIMap[org] { + // get vhost from the api identifier + vhost, _ := ExtractVhostFromAPIIdentifier(vuuid) + apiName := api.adapterInternalAPI.GetTitle() + apiRangeIdentifier := generateIdentifierForAPIWithoutVersion(vhost, apiName) + if _, ok := apiRangeIdentifiers[apiRangeIdentifier]; !ok { + continue } - } - minorVersionRange := GetMinorVersionRange(*deletingAPISemVersion) - - if deletingAPIsMinorRangeLatestAPI, ok := latestAPIVersionMap[minorVersionRange]; ok { - if deletingAPIsMinorRangeLatestAPI.Version == api.GetVersion() { - newLatestMinorRangeAPI := &semantic_version.SemVersion{ - Version: "", - Major: deletingAPISemVersion.Major, - Minor: deletingAPISemVersion.Minor, - Patch: nil, - } - newLatestMinorRangeAPIIdentifier := "" - for currentAPIIdentifier, envoyInternalAPI := range orgAPIMap[organizationID] { - // Iterate all the API versions other than the deleting API itself - if envoyInternalAPI.adapterInternalAPI.GetTitle() == api.GetTitle() && currentAPIIdentifier != apiIdentifier { - currentAPISemVersion, _ := semantic_version.ValidateAndGetVersionComponents(envoyInternalAPI.adapterInternalAPI.GetVersion(), envoyInternalAPI.adapterInternalAPI.GetTitle()) - if currentAPISemVersion != nil { - if currentAPISemVersion.Major == deletingAPISemVersion.Major && - currentAPISemVersion.Minor == deletingAPISemVersion.Minor { - if newLatestMinorRangeAPI.Compare(*currentAPISemVersion) { - newLatestMinorRangeAPI = currentAPISemVersion - newLatestMinorRangeAPIIdentifier = currentAPIIdentifier - } - } - } + // get sem version from the api in orgmap + semVersion, err := semantic_version.ValidateAndGetVersionComponents(api.adapterInternalAPI.GetVersion()) + if err != nil { + logger.LoggerXds.ErrorC(logging.PrintError(logging.Error1410, logging.MAJOR, + "Error validating the version of the API: %v for Organization: %v. Ignore deploying the API, error: %v", + vuuid, org, err)) + continue + } + if currentAPISemVersion, exist := orgIDLatestAPIVersionMap[org][apiRangeIdentifier]; !exist { + orgIDLatestAPIVersionMap[org][apiRangeIdentifier] = make(map[string]semantic_version.SemVersion) + orgIDLatestAPIVersionMap[org][apiRangeIdentifier][GetMajorVersionRange(*semVersion)] = *semVersion + orgIDLatestAPIVersionMap[org][apiRangeIdentifier][GetMinorVersionRange(*semVersion)] = *semVersion + + } else { + if _, ok := currentAPISemVersion[GetMajorVersionRange(*semVersion)]; !ok { + currentAPISemVersion[GetMajorVersionRange(*semVersion)] = *semVersion + } else { + if currentAPISemVersion[GetMajorVersionRange(*semVersion)].Compare(*semVersion) { + currentAPISemVersion[GetMajorVersionRange(*semVersion)] = *semVersion } } - if newLatestMinorRangeAPIIdentifier != "" && newLatestMinorRangeAPIIdentifier != newLatestMajorRangeAPIIdentifier { - orgIDLatestAPIVersionMap[organizationID][apiRangeIdentifier][minorVersionRange] = *newLatestMinorRangeAPI - apiRoutes := getRoutesForAPIIdentifier(organizationID, newLatestMinorRangeAPIIdentifier) - for _, route := range apiRoutes { - regex := route.GetMatch().GetSafeRegex().GetRegex() - newLatestMinorRangeAPIVersionRegex := GetVersionMatchRegex(newLatestMinorRangeAPI.Version) - regex = strings.Replace( - regex, - newLatestMinorRangeAPIVersionRegex, - GetMinorVersionRangeRegex(*newLatestMinorRangeAPI), - 1, - ) - pathSpecifier := &routev3.RouteMatch_SafeRegex{ - SafeRegex: &envoy_type_matcherv3.RegexMatcher{ - Regex: regex, - }, - } - regexRewritePattern := route.GetRoute().GetRegexRewrite().GetPattern().GetRegex() - regexRewritePattern = strings.Replace( - regexRewritePattern, - newLatestMinorRangeAPIVersionRegex, - GetMinorVersionRangeRegex(*newLatestMinorRangeAPI), - 1, - ) - route.Match.PathSpecifier = pathSpecifier - action := &routev3.Route_Route{} - action = route.Action.(*routev3.Route_Route) - action.Route.RegexRewrite.Pattern.Regex = regexRewritePattern - route.Action = action - } + if _, ok := currentAPISemVersion[GetMinorVersionRange(*semVersion)]; !ok { + currentAPISemVersion[GetMinorVersionRange(*semVersion)] = *semVersion } else { - delete(orgIDLatestAPIVersionMap[organizationID][apiRangeIdentifier], minorVersionRange) - } - } - } - - if orgAPIMap, apiAvailable := orgIDLatestAPIVersionMap[organizationID][apiRangeIdentifier]; apiAvailable && len(orgAPIMap) == 0 { - delete(orgIDLatestAPIVersionMap[organizationID], apiRangeIdentifier) - if orgMap := orgIDLatestAPIVersionMap[organizationID]; len(orgMap) == 0 { - delete(orgIDLatestAPIVersionMap, organizationID) - } - } - -} - -func isVHostMatched(organizationID, vHost string) bool { - - if apis, ok := orgIDAPIvHostsMap[organizationID]; ok { - - for _, vHosts := range apis { - for _, vHostEntry := range vHosts { - if vHostEntry == vHost { - return true + if currentAPISemVersion[GetMinorVersionRange(*semVersion)].Compare(*semVersion) { + currentAPISemVersion[GetMinorVersionRange(*semVersion)] = *semVersion } } } } - return false } func getRoutesForAPIIdentifier(organizationID, apiIdentifier string) []*routev3.Route { @@ -374,7 +234,7 @@ func isSemanticVersioningEnabled(apiName, apiVersion string) bool { return false } - apiSemVersion, err := semantic_version.ValidateAndGetVersionComponents(apiVersion, apiName) + apiSemVersion, err := semantic_version.ValidateAndGetVersionComponents(apiVersion) if err != nil && apiSemVersion == nil { return false } diff --git a/adapter/internal/discovery/xds/semantic_versioning_test.go b/adapter/internal/discovery/xds/semantic_versioning_test.go index d08d277185..939a840566 100644 --- a/adapter/internal/discovery/xds/semantic_versioning_test.go +++ b/adapter/internal/discovery/xds/semantic_versioning_test.go @@ -264,68 +264,6 @@ func TestIsSemanticVersioningEnabled(t *testing.T) { } } -func TestIsVHostMatched(t *testing.T) { - // Mock orgIDAPIvHostsMap for testing - orgIDAPIvHostsMap = map[string]map[string][]string{ - "org1": { - "api1": {"example.com", "api.example.com"}, - "api2": {"test.com"}, - }, - "org2": { - "api3": {"example.org"}, - "api4": {"test.org"}, - }, - } - - tests := []struct { - name string - organizationID string - vHost string - expectedResult bool - }{ - { - name: "Matching vHost in org1", - organizationID: "org1", - vHost: "example.com", - expectedResult: true, - }, - { - name: "Matching vHost in org2", - organizationID: "org2", - vHost: "example.org", - expectedResult: true, - }, - { - name: "Non-matching vHost in org1", - organizationID: "org1", - vHost: "nonexistent.com", - expectedResult: false, - }, - { - name: "Non-matching vHost in org2", - organizationID: "org2", - vHost: "nonexistent.org", - expectedResult: false, - }, - { - name: "VHost not found for organization", - organizationID: "org3", - vHost: "example.com", - expectedResult: false, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - result := isVHostMatched(tt.organizationID, tt.vHost) - - if result != tt.expectedResult { - t.Errorf("Expected result: %v, Got: %v", tt.expectedResult, result) - } - }) - } -} - func TestGetRoutesForAPIIdentifier(t *testing.T) { orgAPIMap = map[string]map[string]*EnvoyInternalAPI{ @@ -433,27 +371,20 @@ func TestUpdateRoutingRulesOnAPIUpdate(t *testing.T) { orgAPIMap = map[string]map[string]*EnvoyInternalAPI{ "org1": { "gw.com:apiID1": &EnvoyInternalAPI{ - adapterInternalAPI: apiID1, + adapterInternalAPI: &apiID1, routes: generateRoutes(apiID1ResourcePath), }, "gw.com:apiID2": &EnvoyInternalAPI{ - adapterInternalAPI: apiID2, + adapterInternalAPI: &apiID2, routes: generateRoutes(apiID2ResourcePath), }, "gw.com:apiID3": &EnvoyInternalAPI{ - adapterInternalAPI: apiID3, + adapterInternalAPI: &apiID3, routes: generateRoutes(apiID3ResourcePath), }, }, } - orgIDAPIvHostsMap = map[string]map[string][]string{ - "org1": { - "api1": {"gw.com", "api.example.com"}, - "api2": {"test.com"}, - }, - } - tests := []struct { name string organizationID string @@ -624,17 +555,17 @@ func TestUpdateRoutingRulesOnAPIDelete(t *testing.T) { orgAPIMap = map[string]map[string]*EnvoyInternalAPI{ "org3": { "gw.com:apiID1": &EnvoyInternalAPI{ - adapterInternalAPI: apiID1, + adapterInternalAPI: &apiID1, routes: generateRoutes(apiID1ResourcePath), }, }, "org4": { "gw.com:apiID2": &EnvoyInternalAPI{ - adapterInternalAPI: apiID2, + adapterInternalAPI: &apiID2, routes: generateRoutes(apiID2ResourcePath), }, "gw.com:apiID3": &EnvoyInternalAPI{ - adapterInternalAPI: apiID3, + adapterInternalAPI: &apiID3, routes: generateRoutes(apiID3ResourcePath), }, }, @@ -644,29 +575,28 @@ func TestUpdateRoutingRulesOnAPIDelete(t *testing.T) { name string organizationID string apiIdentifier string - api model.AdapterInternalAPI + api *model.AdapterInternalAPI deleteVersion string }{ { name: "Delete latest major version", organizationID: "org3", apiIdentifier: "gw.com:apiID1", - api: apiID1, + api: &apiID1, deleteVersion: "v1.0", }, { name: "Delete latest minor version", organizationID: "org4", apiIdentifier: "gw.com:apiID3", - api: apiID3, + api: &apiID3, deleteVersion: "v1.5", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - updateRoutingRulesOnAPIDelete(tt.organizationID, tt.apiIdentifier, tt.api) - + updateSemanticVersioning(tt.organizationID, map[string]struct{}{tt.apiIdentifier: {}}) if _, ok := orgIDLatestAPIVersionMap[tt.organizationID]; ok { if _, ok := orgIDLatestAPIVersionMap[tt.organizationID][tt.apiIdentifier]; ok { if _, ok := orgIDLatestAPIVersionMap[tt.organizationID][tt.apiIdentifier][tt.deleteVersion]; ok { diff --git a/adapter/internal/discovery/xds/server.go b/adapter/internal/discovery/xds/server.go index f85195bbf7..8a7b4cf235 100644 --- a/adapter/internal/discovery/xds/server.go +++ b/adapter/internal/discovery/xds/server.go @@ -23,7 +23,6 @@ import ( crand "crypto/rand" "crypto/sha1" "encoding/hex" - "errors" "fmt" "math/big" "math/rand" @@ -45,23 +44,20 @@ import ( logger "github.com/wso2/apk/adapter/internal/loggers" logging "github.com/wso2/apk/adapter/internal/logging" oasParser "github.com/wso2/apk/adapter/internal/oasparser" - "github.com/wso2/apk/adapter/internal/oasparser/constants" "github.com/wso2/apk/adapter/internal/oasparser/envoyconf" "github.com/wso2/apk/adapter/internal/oasparser/model" - operatorconsts "github.com/wso2/apk/adapter/internal/operator/constants" "github.com/wso2/apk/adapter/pkg/discovery/api/wso2/discovery/subscription" wso2_cache "github.com/wso2/apk/adapter/pkg/discovery/protocol/cache/v3" wso2_resource "github.com/wso2/apk/adapter/pkg/discovery/protocol/resource/v3" eventhubTypes "github.com/wso2/apk/adapter/pkg/eventhub/types" semantic_version "github.com/wso2/apk/adapter/pkg/semanticversion" - "github.com/wso2/apk/adapter/pkg/utils/stringutils" gwapiv1b1 "sigs.k8s.io/gateway-api/apis/v1beta1" ) // EnvoyInternalAPI struct use to hold envoy resources and adapter internal resources type EnvoyInternalAPI struct { - adapterInternalAPI model.AdapterInternalAPI - envoyLabels []string + adapterInternalAPI *model.AdapterInternalAPI + envoyLabels map[string]struct{} routes []*routev3.Route clusters []*clusterv3.Cluster endpointAddresses []*corev3.Address @@ -99,11 +95,11 @@ var ( enforcerRevokedTokensCache wso2_cache.SnapshotCache enforcerThrottleDataCache wso2_cache.SnapshotCache - orgAPIMap map[string]map[string]*EnvoyInternalAPI // organizationID -> Vhost:API_UUID -> EnvoyInternalAPI struct map - orgIDvHostBasepathMap map[string]map[string]string // organizationID -> Vhost:basepath -> Vhost:API_UUID - orgIDAPIvHostsMap map[string]map[string][]string // organizationID -> UUID -> prod/sand -> Envoy Vhost Array map + // todo(amali) there can be multiple vhosts for one EnvoyInternalAPI so handle this apiuuid+sand/prod should be the key + + orgAPIMap map[string]map[string]*EnvoyInternalAPI // organizationID -> Vhost:API_UUID -> EnvoyInternalAPI struct map + orgIDLatestAPIVersionMap map[string]map[string]map[string]semantic_version.SemVersion // organizationID -> Vhost:APIName -> VersionRange(vx/vx.x; x is int) -> Latest API Version - orgIDLatestAPIVersionMap map[string]map[string]map[string]semantic_version.SemVersion // organizationID -> Vhost:APIName -> Version Range -> Latest API Version // Envoy Label as map key // TODO(amali) use this without generating all again. gatewayLabelConfigMap map[string]*EnvoyGatewayConfig // GW-Label -> EnvoyGatewayConfig struct map @@ -155,8 +151,6 @@ func init() { enforcerJwtIssuerCache = wso2_cache.NewSnapshotCache(false, IDHash{}, nil) gatewayLabelConfigMap = make(map[string]*EnvoyGatewayConfig) orgAPIMap = make(map[string]map[string]*EnvoyInternalAPI) - orgIDAPIvHostsMap = make(map[string]map[string][]string) // organizationID -> UUID-prod/sand -> Envoy Vhost Array map - orgIDvHostBasepathMap = make(map[string]map[string]string) orgIDLatestAPIVersionMap = make(map[string]map[string]map[string]semantic_version.SemVersion) enforcerLabelMap = make(map[string]*EnforcerInternalAPI) @@ -212,105 +206,17 @@ func GetEnforcerThrottleDataCache() wso2_cache.SnapshotCache { return enforcerThrottleDataCache } -// DeleteAPICREvent deletes API with the given UUID from the given gw environments -func DeleteAPICREvent(labels []string, apiUUID string, organizationID string) error { - mutexForInternalMapUpdate.Lock() - defer mutexForInternalMapUpdate.Unlock() +// DeleteAPI deletes API with the given UUID from the given gw environments +func DeleteAPI(uuid string, gatewayNames map[string]struct{}) error { - prodvHostIdentifier := GetvHostsIdentifier(apiUUID, operatorconsts.Production) - sandvHostIdentifier := GetvHostsIdentifier(apiUUID, operatorconsts.Sandbox) - vHosts := append(orgIDAPIvHostsMap[organizationID][prodvHostIdentifier], - orgIDAPIvHostsMap[organizationID][sandvHostIdentifier]...) - - delete(orgIDAPIvHostsMap[organizationID], prodvHostIdentifier) - delete(orgIDAPIvHostsMap[organizationID], sandvHostIdentifier) - for _, vhost := range vHosts { - apiIdentifier := GenerateIdentifierForAPIWithUUID(vhost, apiUUID) - if err := deleteAPI(apiIdentifier, labels, organizationID); err != nil { - logger.LoggerXds.ErrorC(logging.PrintError(logging.Error1410, logging.MAJOR, "Error undeploying API %v with UUID %v of Organization %v from environments %v, error: %v", - apiIdentifier, apiUUID, organizationID, labels, err.Error())) - return err - } - // if no error, update internal vhost maps - // error only happens when API not found in deleteAPI func - logger.LoggerXds.Infof("Successfully undeployed the API %v with UUID %v under Organization %s and environment %s", - apiIdentifier, apiUUID, organizationID, labels) + oldGatewayNames := RemoveAPIFromAllInternalMaps(uuid) + for oldGatewayName := range oldGatewayNames { + gatewayNames[oldGatewayName] = struct{}{} } + UpdateXdsCacheOnAPIChange(gatewayNames) return nil } -// deleteAPI deletes an API, its resources and updates the caches of given environments -func deleteAPI(apiIdentifier string, environments []string, organizationID string) error { - apiUUID, _ := ExtractUUIDFromAPIIdentifier(apiIdentifier) - var api *EnvoyInternalAPI - - if _, orgExists := orgAPIMap[organizationID]; orgExists { - if oldAPI, apiExists := orgAPIMap[organizationID][apiIdentifier]; apiExists { - api = oldAPI - } else { - logger.LoggerXds.Infof("Unable to delete API: %v from Organization: %v. API Does not exist. API_UUID: %v", apiIdentifier, organizationID, apiUUID) - return errors.New(constants.NotFound) - } - - } else { - logger.LoggerXds.Infof("Unable to delete API: %v from Organization: %v. Organization Does not exist. API_UUID: %v", apiIdentifier, organizationID, apiUUID) - return errors.New(constants.NotFound) - } - - existingLabels := orgAPIMap[organizationID][apiIdentifier].envoyLabels - toBeDelEnvs, toBeKeptEnvs := getEnvironmentsToBeDeleted(existingLabels, environments) - - if isSemanticVersioningEnabled(api.adapterInternalAPI.GetTitle(), api.adapterInternalAPI.GetVersion()) { - updateRoutingRulesOnAPIDelete(organizationID, apiIdentifier, api.adapterInternalAPI) - } - - var isAllowedToDelete bool - updatedLabelsMap := make(map[string]struct{}) - for _, val := range toBeDelEnvs { - updatedLabelsMap[val] = struct{}{} - if stringutils.StringInSlice(val, existingLabels) { - isAllowedToDelete = true - } - } - if isAllowedToDelete { - // do not delete from all environments, hence do not clear routes, clusters, endpoints, enforcerAPIs - orgAPIMap[organizationID][apiIdentifier].envoyLabels = toBeKeptEnvs - if len(toBeKeptEnvs) != 0 { - UpdateXdsCacheOnAPIChange(updatedLabelsMap) - return nil - } - } - - //clean maps of routes, clusters, endpoints, enforcerAPIs - if len(environments) == 0 || isAllowedToDelete { - cleanMapResources(apiIdentifier, organizationID, toBeDelEnvs) - } - UpdateXdsCacheOnAPIChange(updatedLabelsMap) - return nil -} - -func cleanMapResources(apiIdentifier string, organizationID string, toBeDelEnvs []string) { - if _, orgExists := orgAPIMap[organizationID]; orgExists { - delete(orgAPIMap[organizationID], apiIdentifier) - } - - deleteBasepathForVHost(organizationID, apiIdentifier) - //TODO: (SuKSW) clean any remaining in label wise maps, if this is the last API of that label - logger.LoggerXds.Infof("Deleted API %v of organization %v", apiIdentifier, organizationID) -} - -func deleteBasepathForVHost(organizationID, apiIdentifier string) { - // Remove the basepath from map (that is used to avoid duplicate basepaths) - if _, orgExists := orgAPIMap[organizationID]; orgExists { - if oldOrgAPIAPI, ok := orgAPIMap[organizationID][apiIdentifier]; ok { - s := strings.Split(apiIdentifier, apiKeyFieldSeparator) - vHost := s[0] - oldBasepath := oldOrgAPIAPI.adapterInternalAPI.GetXWso2Basepath() - delete(orgIDvHostBasepathMap[organizationID], vHost+":"+oldBasepath) - } - } -} - // UpdateXdsCacheOnAPIChange when this method is called, openAPIEnvoy map is updated. // Old labels refers to the previously assigned labels // New labels refers to the the updated labels @@ -343,40 +249,34 @@ func SetReady() { func GenerateEnvoyResoucesForGateway(gatewayName string) ([]types.Resource, []types.Resource, []types.Resource, []types.Resource, []types.Resource) { var clusterArray []*clusterv3.Cluster + // Warning: Route order is important. The first route that matches the request will be used. var vhostToRouteArrayMap = make(map[string][]*routev3.Route) var endpointArray []*corev3.Address var apis []types.Resource for organizationID, entityMap := range orgAPIMap { for apiKey, envoyInternalAPI := range entityMap { - if stringutils.StringInSlice(gatewayName, envoyInternalAPI.envoyLabels) { - vhost, err := ExtractVhostFromAPIIdentifier(apiKey) - if err != nil { - logger.LoggerXds.ErrorC(logging.PrintError(logging.Error1411, logging.MAJOR, "Error extracting vhost from API identifier: %v for Organization %v. Ignore deploying the API, error: %v", apiKey, organizationID, err)) - continue - } - isDefaultVersion := false - var orgAPI *EnvoyInternalAPI - // If the adapterInternalAPI is not found, proceed with other APIs. (Unreachable condition at this point) - // If that happens, there is no purpose in processing clusters too. - if org, ok := orgAPIMap[organizationID]; !ok { - continue - } else if orgAPI, ok = org[apiKey]; !ok { - continue - } - isDefaultVersion = orgAPI.adapterInternalAPI.IsDefaultVersion - // If it is a default versioned API, the routes are added to the end of the existing array. - // Otherwise the routes would be added to the front. - // /fooContext/2.0.0/* resource path should be matched prior to the /fooContext/* . - if isDefaultVersion { - vhostToRouteArrayMap[vhost] = append(vhostToRouteArrayMap[vhost], orgAPI.routes...) - } else { - vhostToRouteArrayMap[vhost] = append(orgAPI.routes, vhostToRouteArrayMap[vhost]...) - } - clusterArray = append(clusterArray, orgAPI.clusters...) - endpointArray = append(endpointArray, orgAPI.endpointAddresses...) - apis = append(apis, orgAPI.enforcerAPI) + if _, exists := envoyInternalAPI.envoyLabels[gatewayName]; !exists { + // do nothing if the gateway is not found in the envoyInternalAPI + continue + } + vhost, err := ExtractVhostFromAPIIdentifier(apiKey) + if err != nil { + logger.LoggerXds.ErrorC(logging.PrintError(logging.Error1411, logging.MAJOR, "Error extracting vhost from API identifier: %v for Organization %v. Ignore deploying the API, error: %v", apiKey, organizationID, err)) + continue + } + isDefaultVersion := envoyInternalAPI.adapterInternalAPI.IsDefaultVersion + // If it is a default versioned API, the routes are added to the end of the existing array. + // Otherwise the routes would be added to the front. + // /fooContext/2.0.0/* resource path should be matched prior to the /fooContext/* . + if isDefaultVersion { + vhostToRouteArrayMap[vhost] = append(vhostToRouteArrayMap[vhost], envoyInternalAPI.routes...) + } else { + vhostToRouteArrayMap[vhost] = append(envoyInternalAPI.routes, vhostToRouteArrayMap[vhost]...) } + clusterArray = append(clusterArray, envoyInternalAPI.clusters...) + endpointArray = append(endpointArray, envoyInternalAPI.endpointAddresses...) + apis = append(apis, envoyInternalAPI.enforcerAPI) } } @@ -641,99 +541,72 @@ func ExtractUUIDFromAPIIdentifier(id string) (string, error) { return "", err } -// RemoveAPICacheForEnv will remove all the internal mappings for a specific environment -func RemoveAPICacheForEnv(adapterInternalAPI model.AdapterInternalAPI, envType string) { - vHostIdentifier := GetvHostsIdentifier(adapterInternalAPI.UUID, envType) - var oldvHosts []string - if _, ok := orgIDAPIvHostsMap[adapterInternalAPI.OrganizationID]; ok { - oldvHosts = orgIDAPIvHostsMap[adapterInternalAPI.GetOrganizationID()][vHostIdentifier] - for _, oldvhost := range oldvHosts { - apiIdentifier := GenerateIdentifierForAPIWithUUID(oldvhost, adapterInternalAPI.UUID) - if orgMap, orgExists := orgAPIMap[adapterInternalAPI.GetOrganizationID()]; orgExists { - if _, apiExists := orgMap[apiIdentifier]; apiExists { - delete(orgAPIMap[adapterInternalAPI.GetOrganizationID()], apiIdentifier) - } - } - } - } -} +// RemoveAPIFromAllInternalMaps removes api from all maps +func RemoveAPIFromAllInternalMaps(uuid string) map[string]struct{} { + mutexForInternalMapUpdate.Lock() + defer mutexForInternalMapUpdate.Unlock() -// RemoveAPIFromOrgAPIMap removes api from orgAPI map -func RemoveAPIFromOrgAPIMap(uuid string, orgID string) { - if orgMap, ok := orgAPIMap[orgID]; ok { - for apiName := range orgMap { - if strings.Contains(apiName, uuid) { - delete(orgMap, apiName) + tobeUpdatedAPIRangeIdentifiers := make(map[string]struct{}, 0) + updatedLabelsMap := make(map[string]struct{}, 0) + logger.LoggerAPI.Error(orgIDLatestAPIVersionMap) + for orgID, orgAPI := range orgAPIMap { + for apiIdentifier, envoyInternalAPI := range orgAPI { + if strings.HasSuffix(apiIdentifier, ":"+uuid) { + for oldLabel := range envoyInternalAPI.envoyLabels { + updatedLabelsMap[oldLabel] = struct{}{} + } + delete(orgAPIMap[orgID], apiIdentifier) + // get vhost from the apiIdentifier + vhost, _ := ExtractVhostFromAPIIdentifier(apiIdentifier) + apiRangeID := generateIdentifierForAPIWithoutVersion(vhost, envoyInternalAPI.adapterInternalAPI.GetTitle()) + if _, exists := orgIDLatestAPIVersionMap[orgID]; exists { + if apiVersionMap, exists := orgIDLatestAPIVersionMap[orgID][apiRangeID]; exists { + for versionRange, latestVersion := range apiVersionMap { + if latestVersion.Version == envoyInternalAPI.adapterInternalAPI.GetVersion() { + delete(orgIDLatestAPIVersionMap[orgID][apiRangeID], versionRange) + tobeUpdatedAPIRangeIdentifiers[apiRangeID] = struct{}{} + } + } + } + } } } - if len(orgMap) == 0 { + if len(orgAPIMap[orgID]) == 0 { delete(orgAPIMap, orgID) + delete(orgIDLatestAPIVersionMap, orgID) + } + if len(tobeUpdatedAPIRangeIdentifiers) > 0 { + updateSemanticVersioning(orgID, tobeUpdatedAPIRangeIdentifiers) } } + + return updatedLabelsMap } // UpdateAPICache updates the xDS cache related to the API Lifecycle event. -func UpdateAPICache(vHosts []string, newLabels []string, listener string, sectionName string, - adapterInternalAPI model.AdapterInternalAPI) (map[string]struct{}, error) { +func UpdateAPICache(vHosts []string, newLabels map[string]struct{}, listener string, sectionName string, + adapterInternalAPI *model.AdapterInternalAPI) error { mutexForInternalMapUpdate.Lock() defer mutexForInternalMapUpdate.Unlock() - vHostIdentifier := GetvHostsIdentifier(adapterInternalAPI.UUID, adapterInternalAPI.EnvType) - var oldvHosts []string - if _, ok := orgIDAPIvHostsMap[adapterInternalAPI.OrganizationID]; ok { - oldvHosts = orgIDAPIvHostsMap[adapterInternalAPI.GetOrganizationID()][vHostIdentifier] - orgIDAPIvHostsMap[adapterInternalAPI.GetOrganizationID()][vHostIdentifier] = vHosts - } else { - vHostsMap := make(map[string][]string) - vHostsMap[vHostIdentifier] = vHosts - orgIDAPIvHostsMap[adapterInternalAPI.GetOrganizationID()] = vHostsMap - } - - updatedLabelsMap := make(map[string]struct{}, 0) - - // Remove internal mappings for old vHosts - for _, oldvhost := range oldvHosts { - apiIdentifier := GenerateIdentifierForAPIWithUUID(oldvhost, adapterInternalAPI.UUID) - if orgMap, orgExists := orgAPIMap[adapterInternalAPI.GetOrganizationID()]; orgExists { - if _, apiExists := orgMap[apiIdentifier]; apiExists { - for _, oldLabel := range orgMap[apiIdentifier].envoyLabels { - updatedLabelsMap[oldLabel] = struct{}{} - } - delete(orgAPIMap[adapterInternalAPI.GetOrganizationID()], apiIdentifier) - } - } - } - // Create internal mappings for new vHosts for _, vHost := range vHosts { logger.LoggerAPKOperator.Debugf("Creating internal mapping for vhost: %s", vHost) apiUUID := adapterInternalAPI.UUID apiIdentifier := GenerateIdentifierForAPIWithUUID(vHost, apiUUID) - var orgExists bool - // get changing label set - if _, orgExists = orgAPIMap[adapterInternalAPI.GetOrganizationID()]; orgExists { - if _, apiExists := orgAPIMap[adapterInternalAPI.GetOrganizationID()][apiIdentifier]; apiExists { - for _, oldLabel := range orgAPIMap[adapterInternalAPI.GetOrganizationID()][apiIdentifier].envoyLabels { - updatedLabelsMap[oldLabel] = struct{}{} - } - } - } - for _, newLabel := range newLabels { - updatedLabelsMap[newLabel] = struct{}{} - } - - routes, clusters, endpoints, err := oasParser.GetRoutesClustersEndpoints(&adapterInternalAPI, nil, + routes, clusters, endpoints, err := oasParser.GetRoutesClustersEndpoints(adapterInternalAPI, nil, vHost, adapterInternalAPI.GetOrganizationID()) if err != nil { - return nil, fmt.Errorf("error while deploying API. Name: %s Version: %s, OrgID: %s, API_UUID: %v, Error: %s", + return fmt.Errorf("error while deploying API. Name: %s Version: %s, OrgID: %s, API_UUID: %v, Error: %s", adapterInternalAPI.GetTitle(), adapterInternalAPI.GetVersion(), adapterInternalAPI.GetOrganizationID(), apiUUID, err.Error()) } - if !orgExists { + if _, orgExists := orgAPIMap[adapterInternalAPI.GetOrganizationID()]; !orgExists { orgAPIMap[adapterInternalAPI.GetOrganizationID()] = make(map[string]*EnvoyInternalAPI) } + orgAPIMap[adapterInternalAPI.GetOrganizationID()][apiIdentifier] = &EnvoyInternalAPI{ adapterInternalAPI: adapterInternalAPI, envoyLabels: newLabels, @@ -746,11 +619,11 @@ func UpdateAPICache(vHosts []string, newLabels []string, listener string, sectio apiVersion := adapterInternalAPI.GetVersion() apiName := adapterInternalAPI.GetTitle() if isSemanticVersioningEnabled(apiName, apiVersion) { + logger.LoggerAPI.Errorf("Semantic versioning is enabled for API: %v", apiName) updateRoutingRulesOnAPIUpdate(adapterInternalAPI.OrganizationID, apiIdentifier, apiName, apiVersion, vHost) } } - - return updatedLabelsMap, nil + return nil } // UpdateGatewayCache updates the xDS cache related to the Gateway Lifecycle event. diff --git a/adapter/internal/discovery/xds/server_test.go b/adapter/internal/discovery/xds/server_test.go index 8b491a8c25..7e2af4c408 100644 --- a/adapter/internal/discovery/xds/server_test.go +++ b/adapter/internal/discovery/xds/server_test.go @@ -21,136 +21,455 @@ import ( "testing" "github.com/stretchr/testify/assert" + "github.com/wso2/apk/adapter/config" + "github.com/wso2/apk/adapter/internal/loggers" "github.com/wso2/apk/adapter/internal/oasparser/model" + semantic_version "github.com/wso2/apk/adapter/pkg/semanticversion" ) -func TestUpdateAPICache(t *testing.T) { +func TestOrgMapUpdates(t *testing.T) { + orgAPIMap = make(map[string]map[string]*EnvoyInternalAPI) + orgIDLatestAPIVersionMap = make(map[string]map[string]map[string]semantic_version.SemVersion) + conf := config.ReadConfigs() + conf.Envoy.EnableIntelligentRouting = true + + api1uuid := &model.AdapterInternalAPI{ + UUID: "api-1-uuid", + EnvType: "prod", + OrganizationID: "org-1", + } + api1uuid.SetName("api-1") + api1uuid.SetVersion("v1.0.0") + api1sanduuid := &model.AdapterInternalAPI{ + UUID: "api-1-uuid", + EnvType: "sand", + OrganizationID: "org-1", + } + api1sanduuid.SetName("api-1") + api1sanduuid.SetVersion("v1.0.1") + api2uuid := &model.AdapterInternalAPI{ + UUID: "api-2-uuid", + EnvType: "prod", + OrganizationID: "org-1", + } + api2uuid.SetName("api-2") + api2uuid.SetVersion("v0.0.1") + ptrOne := new(int) + *ptrOne = 1 tests := []struct { - name string - vHosts []string - labels []string - listeners []string - adapterInternalAPI model.AdapterInternalAPI - EnvType string - action string - deletedvHosts []string + name string + vHosts []string + labels map[string]struct{} + listeners []string + adapterInternalAPI *model.AdapterInternalAPI + EnvType string + action string + deletedvHosts []string + expectedOrgAPIMap map[string]map[string]*EnvoyInternalAPI + expectedOrgIDLatestAPIVersionMap map[string]map[string]map[string]semantic_version.SemVersion }{ { - name: "Test creating first prod api", - vHosts: []string{"prod1.gw.abc.com", "prod2.gw.abc.com"}, - labels: []string{"default"}, - listeners: []string{"httpslistener"}, - adapterInternalAPI: model.AdapterInternalAPI{ - UUID: "api-1-uuid", - EnvType: "prod", - OrganizationID: "org-1", + name: "Test creating first prod api", + vHosts: []string{"prod1.gw.abc.com", "prod2.gw.abc.com"}, + labels: map[string]struct{}{"default": {}}, + listeners: []string{"httpslistener"}, + adapterInternalAPI: api1uuid, + EnvType: "prod", + action: "CREATE", + expectedOrgAPIMap: map[string]map[string]*EnvoyInternalAPI{ + "org-1": { + "prod1.gw.abc.com:api-1-uuid": &EnvoyInternalAPI{ + envoyLabels: map[string]struct{}{"default": {}}, + adapterInternalAPI: api1uuid, + }, + "prod2.gw.abc.com:api-1-uuid": &EnvoyInternalAPI{ + envoyLabels: map[string]struct{}{"default": {}}, + adapterInternalAPI: api1uuid, + }, + }, }, - EnvType: "prod", - action: "CREATE", - }, - { - name: "Test creating first sand api", - vHosts: []string{"sand3.gw.abc.com", "sand4.gw.abc.com"}, - labels: []string{"default"}, - listeners: []string{"httpslistener"}, - adapterInternalAPI: model.AdapterInternalAPI{ - UUID: "app-1-uuid", - EnvType: "sand", - OrganizationID: "org-1", + expectedOrgIDLatestAPIVersionMap: map[string]map[string]map[string]semantic_version.SemVersion{ + "org-1": { + "prod1.gw.abc.com:api-1": { + "v1": semantic_version.SemVersion{ + Version: "v1.0.0", + Major: 1, + Minor: 0, + Patch: new(int), + }, + "v1.0": semantic_version.SemVersion{ + Version: "v1.0.0", + Major: 1, + Minor: 0, + Patch: new(int), + }, + }, + "prod2.gw.abc.com:api-1": { + "v1": semantic_version.SemVersion{ + Version: "v1.0.0", + Major: 1, + Minor: 0, + Patch: new(int), + }, + "v1.0": semantic_version.SemVersion{ + Version: "v1.0.0", + Major: 1, + Minor: 0, + Patch: new(int), + }, + }, + }, }, - EnvType: "sand", - action: "CREATE", }, { - name: "Test creating second prod api", - vHosts: []string{"prod1.gw.pqr.com", "prod2.gw.pqr.com"}, - labels: []string{"default"}, - listeners: []string{"httpslistener"}, - adapterInternalAPI: model.AdapterInternalAPI{ - UUID: "api-2-uuid", - EnvType: "prod", - OrganizationID: "org-2", + name: "Test creating first sand api", + vHosts: []string{"sand3.gw.abc.com", "sand4.gw.abc.com"}, + labels: map[string]struct{}{"default": {}}, + listeners: []string{"httpslistener"}, + adapterInternalAPI: api1sanduuid, + EnvType: "sand", + action: "UPDATE", + expectedOrgAPIMap: map[string]map[string]*EnvoyInternalAPI{ + "org-1": { + "prod1.gw.abc.com:api-1-uuid": &EnvoyInternalAPI{ + envoyLabels: map[string]struct{}{"default": {}}, + adapterInternalAPI: api1uuid, + }, + "prod2.gw.abc.com:api-1-uuid": &EnvoyInternalAPI{ + envoyLabels: map[string]struct{}{"default": {}}, + adapterInternalAPI: api1uuid, + }, + "sand3.gw.abc.com:api-1-uuid": &EnvoyInternalAPI{ + envoyLabels: map[string]struct{}{"default": {}}, + adapterInternalAPI: api1sanduuid, + }, + "sand4.gw.abc.com:api-1-uuid": &EnvoyInternalAPI{ + envoyLabels: map[string]struct{}{"default": {}}, + adapterInternalAPI: api1sanduuid, + }, + }, }, - EnvType: "prod", - action: "CREATE", - }, - { - name: "Test updating first prod api 1 with new vhosts", - vHosts: []string{"prod1.gw.abc.com", "prod2.gw.abc.com"}, - labels: []string{"default"}, - listeners: []string{"httpslistener"}, - adapterInternalAPI: model.AdapterInternalAPI{ - UUID: "api-1-uuid", - EnvType: "prod", - OrganizationID: "org-1", + expectedOrgIDLatestAPIVersionMap: map[string]map[string]map[string]semantic_version.SemVersion{ + "org-1": { + "prod1.gw.abc.com:api-1": { + "v1": semantic_version.SemVersion{ + Version: "v1.0.0", + Major: 1, + Minor: 0, + Patch: new(int), + }, + "v1.0": semantic_version.SemVersion{ + Version: "v1.0.0", + Major: 1, + Minor: 0, + Patch: new(int), + }, + }, + "prod2.gw.abc.com:api-1": { + "v1": semantic_version.SemVersion{ + Version: "v1.0.0", + Major: 1, + Minor: 0, + Patch: new(int), + }, + "v1.0": semantic_version.SemVersion{ + Version: "v1.0.0", + Major: 1, + Minor: 0, + Patch: new(int), + }, + }, + "sand3.gw.abc.com:api-1": { + "v1": semantic_version.SemVersion{ + Version: "v1.0.1", + Major: 1, + Minor: 0, + Patch: ptrOne, + }, + "v1.0": semantic_version.SemVersion{ + Version: "v1.0.1", + Major: 1, + Minor: 0, + Patch: ptrOne, + }, + }, + "sand4.gw.abc.com:api-1": { + "v1": semantic_version.SemVersion{ + Version: "v1.0.1", + Major: 1, + Minor: 0, + Patch: ptrOne, + }, + "v1.0": semantic_version.SemVersion{ + Version: "v1.0.1", + Major: 1, + Minor: 0, + Patch: ptrOne, + }, + }, + }, }, - action: "UPDATE", }, { - name: "Test deleting api 1 both prod and sand", - labels: []string{"default"}, - listeners: []string{"httpslistener"}, - adapterInternalAPI: model.AdapterInternalAPI{ - UUID: "app-1-uuid", - OrganizationID: "org-1", + name: "Test creating second prod api", + vHosts: []string{"prod1.gw.pqr.com", "prod2.gw.pqr.com"}, + labels: map[string]struct{}{"default": {}}, + listeners: []string{"httpslistener"}, + adapterInternalAPI: api2uuid, + EnvType: "prod", + action: "CREATE", + expectedOrgAPIMap: map[string]map[string]*EnvoyInternalAPI{ + "org-1": { + "prod1.gw.abc.com:api-1-uuid": &EnvoyInternalAPI{ + envoyLabels: map[string]struct{}{"default": {}}, + adapterInternalAPI: api1uuid, + }, + "prod2.gw.abc.com:api-1-uuid": &EnvoyInternalAPI{ + envoyLabels: map[string]struct{}{"default": {}}, + adapterInternalAPI: api1uuid, + }, + "sand3.gw.abc.com:api-1-uuid": &EnvoyInternalAPI{ + envoyLabels: map[string]struct{}{"default": {}}, + adapterInternalAPI: api1sanduuid, + }, + "sand4.gw.abc.com:api-1-uuid": &EnvoyInternalAPI{ + envoyLabels: map[string]struct{}{"default": {}}, + adapterInternalAPI: api1sanduuid, + }, + "prod1.gw.pqr.com:api-2-uuid": &EnvoyInternalAPI{ + envoyLabels: map[string]struct{}{"default": {}}, + adapterInternalAPI: api2uuid, + }, + "prod2.gw.pqr.com:api-2-uuid": &EnvoyInternalAPI{ + envoyLabels: map[string]struct{}{"default": {}}, + adapterInternalAPI: api2uuid, + }, + }, + }, + expectedOrgIDLatestAPIVersionMap: map[string]map[string]map[string]semantic_version.SemVersion{ + "org-1": { + "prod1.gw.abc.com:api-1": { + "v1": semantic_version.SemVersion{ + Version: "v1.0.0", // fix it should still be v1.0.0 + Major: 1, + Minor: 0, + Patch: new(int), + }, + "v1.0": semantic_version.SemVersion{ + Version: "v1.0.0", + Major: 1, + Minor: 0, + Patch: new(int), + }, + }, + "prod2.gw.abc.com:api-1": { + "v1": semantic_version.SemVersion{ + Version: "v1.0.0", + Major: 1, + Minor: 0, + Patch: new(int), + }, + "v1.0": semantic_version.SemVersion{ + Version: "v1.0.0", + Major: 1, + Minor: 0, + Patch: new(int), + }, + }, + "sand3.gw.abc.com:api-1": { + "v1": semantic_version.SemVersion{ + Version: "v1.0.1", + Major: 1, + Minor: 0, + Patch: ptrOne, + }, + "v1.0": semantic_version.SemVersion{ + Version: "v1.0.1", + Major: 1, + Minor: 0, + Patch: ptrOne, + }, + }, + "sand4.gw.abc.com:api-1": { + "v1": semantic_version.SemVersion{ + Version: "v1.0.1", + Major: 1, + Minor: 0, + Patch: ptrOne, + }, + "v1.0": semantic_version.SemVersion{ + Version: "v1.0.1", + Major: 1, + Minor: 0, + Patch: ptrOne, + }, + }, + "prod1.gw.pqr.com:api-2": { + "v0": semantic_version.SemVersion{ + Version: "v0.0.1", + Major: 0, + Minor: 0, + Patch: ptrOne, + }, + "v0.0": semantic_version.SemVersion{ + Version: "v0.0.1", + Major: 0, + Minor: 0, + Patch: ptrOne, + }, + }, + "prod2.gw.pqr.com:api-2": { + "v0": semantic_version.SemVersion{ + Version: "v0.0.1", + Major: 0, + Minor: 0, + Patch: ptrOne, + }, + "v0.0": semantic_version.SemVersion{ + Version: "v0.0.1", + Major: 0, + Minor: 0, + Patch: ptrOne, + }, + }, + }, }, - action: "DELETE", - deletedvHosts: []string{"prod1.gw.abc.com", "prod2.gw.abc.com", - "sand3.gw.abc.com", "sand4.gw.abc.com"}, }, + // { + // name: "Test updating first prod api 1 with new vhosts", + // vHosts: []string{"prod3.gw.abc.com", "prod4.gw.abc.com"}, + // labels: map[string]struct{}{"default": {}}, + // listeners: []string{"httpslistener"}, + // adapterInternalAPI: api1uuid, + // action: "UPDATE", + // expectedOrgAPIMap: map[string]map[string]*EnvoyInternalAPI{ + // "org-1": { + // "prod3.gw.abc.com:api-1-uuid": &EnvoyInternalAPI{ + // envoyLabels: map[string]struct{}{"default": {}}, + // adapterInternalAPI: api1uuid, + // }, + // "prod4.gw.abc.com:api-1-uuid": &EnvoyInternalAPI{ + // envoyLabels: map[string]struct{}{"default": {}}, + // adapterInternalAPI: api1uuid, + // }, + // "sand3.gw.abc.com:api-1-uuid": &EnvoyInternalAPI{ + // envoyLabels: map[string]struct{}{"default": {}}, + // adapterInternalAPI: api1sanduuid, + // }, + // "sand4.gw.abc.com:api-1-uuid": &EnvoyInternalAPI{ + // envoyLabels: map[string]struct{}{"default": {}}, + // adapterInternalAPI: api1sanduuid, + // }, + // "prod1.gw.pqr.com:api-2-uuid": &EnvoyInternalAPI{ + // envoyLabels: map[string]struct{}{"default": {}}, + // adapterInternalAPI: api2uuid, + // }, + // "prod2.gw.pqr.com:api-2-uuid": &EnvoyInternalAPI{ + // envoyLabels: map[string]struct{}{"default": {}}, + // adapterInternalAPI: api2uuid, + // }, + // }, + // }, + // }, + // { + // name: "Test deleting api 1 both prod and sand", + // labels: map[string]struct{}{"default": {}}, + // listeners: []string{"httpslistener"}, + // adapterInternalAPI: api1uuid, + // action: "DELETE", + // expectedOrgAPIMap: map[string]map[string]*EnvoyInternalAPI{ + // "org-1": { + // "prod1.gw.pqr.com:api-2-uuid": &EnvoyInternalAPI{ + // envoyLabels: map[string]struct{}{"default": {}}, + // adapterInternalAPI: api2uuid, + // }, + // "prod2.gw.pqr.com:api-2-uuid": &EnvoyInternalAPI{ + // envoyLabels: map[string]struct{}{"default": {}}, + // adapterInternalAPI: api2uuid, + // }, + // }, + // }, + // }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { switch test.action { case "CREATE": - case "UPDATE": - for _, label := range test.labels { + loggers.LoggerAPI.Infof("Creating API: %v", test.adapterInternalAPI.UUID) + for label := range test.labels { SanitizeGateway(label, true) } + RemoveAPIFromAllInternalMaps(test.adapterInternalAPI.UUID) UpdateAPICache(test.vHosts, test.labels, test.listeners[0], "httpslistener", test.adapterInternalAPI) - identifier := GetvHostsIdentifier(test.adapterInternalAPI.UUID, "prod") - actualvHosts, ok := orgIDAPIvHostsMap[test.adapterInternalAPI.OrganizationID][identifier] - if !ok { - t.Errorf("orgIDAPIvHostsMap has not updated with new entry with the key: %s, %v", - identifier, orgIDAPIvHostsMap) - } - assert.Equal(t, actualvHosts, test.vHosts, "Not expected vHosts found, expected: %v but found: %v", - test.vHosts, actualvHosts) - for _, vhsot := range actualvHosts { - testExistsInMapping(t, orgAPIMap[test.adapterInternalAPI.OrganizationID], - GenerateIdentifierForAPIWithUUID(vhsot, test.adapterInternalAPI.UUID), true) + case "UPDATE": + loggers.LoggerAPI.Infof("Updating API: %v", test.adapterInternalAPI.UUID) + for label := range test.labels { + SanitizeGateway(label, true) } + UpdateAPICache(test.vHosts, test.labels, test.listeners[0], "httpslistener", test.adapterInternalAPI) case "DELETE": - DeleteAPICREvent(test.labels, test.adapterInternalAPI.UUID, test.adapterInternalAPI.OrganizationID) - prodIdentifier := GetvHostsIdentifier(test.adapterInternalAPI.UUID, "prod") - sandIdentifier := GetvHostsIdentifier(test.adapterInternalAPI.UUID, "sand") - _, prodExists := orgIDAPIvHostsMap[test.adapterInternalAPI.OrganizationID][prodIdentifier] - _, sandExists := orgIDAPIvHostsMap[test.adapterInternalAPI.OrganizationID][sandIdentifier] - if prodExists { - t.Errorf("orgIDAPIvHostsMap has a mapping for prod after api deletion") - } - if sandExists { - t.Errorf("orgIDAPIvHostsMap has a mapping for sand after api deletion") + loggers.LoggerAPI.Infof("Deleting API: %v", test.adapterInternalAPI.UUID) + DeleteAPI(test.adapterInternalAPI.UUID, test.labels) + } + assert.Equal(t, len(test.expectedOrgAPIMap), len(orgAPIMap), "orgAPIMap length is different, expected: %v but found: %v", + test.expectedOrgAPIMap, orgAPIMap) + for orgID, orgAPIs := range test.expectedOrgAPIMap { + if orgAPI, ok := orgAPIMap[orgID]; !ok { + t.Errorf("orgAPIMap has no entry with the organization: %s", orgID) + } else { + assert.Equal(t, len(test.expectedOrgAPIMap[orgID]), len(orgAPIs), "orgAPI length is different, expected: %v but found: %v", + len(test.expectedOrgAPIMap[orgID]), len(orgAPIs)) + for vuuid, expectedAPI := range test.expectedOrgAPIMap[orgID] { + if actualAPI, ok := orgAPI[vuuid]; !ok { + t.Errorf("orgAPIMap has not updated with new entry with the key: %s, %v", + vuuid, orgAPIMap) + } else { + assert.Equal(t, expectedAPI.adapterInternalAPI.UUID, actualAPI.adapterInternalAPI.UUID, "Not expected API UUID found, expected: %v but found: %v", + expectedAPI.adapterInternalAPI.UUID, actualAPI.adapterInternalAPI.UUID) + assert.Equal(t, expectedAPI.adapterInternalAPI.EnvType, actualAPI.adapterInternalAPI.EnvType, "Not expected API EnvType found, expected: %v but found: %v", + expectedAPI.adapterInternalAPI.EnvType, actualAPI.adapterInternalAPI.EnvType) + assert.Equal(t, expectedAPI.adapterInternalAPI.OrganizationID, actualAPI.adapterInternalAPI.OrganizationID, "Not expected API OrganizationID found, expected: %v but found: %v", + expectedAPI.adapterInternalAPI.OrganizationID, actualAPI.adapterInternalAPI.OrganizationID) + } + + } } - for _, vhsot := range test.deletedvHosts { - testExistsInMapping(t, orgAPIMap[test.adapterInternalAPI.OrganizationID], - GenerateIdentifierForAPIWithUUID(vhsot, test.adapterInternalAPI.UUID), false) + } + assert.Equal(t, len(test.expectedOrgIDLatestAPIVersionMap), len(orgIDLatestAPIVersionMap), "orgIDLatestAPIVersionMap length is different, expected: %v but found: %v", + len(test.expectedOrgIDLatestAPIVersionMap), len(orgIDLatestAPIVersionMap)) + for orgID, orgAPIs := range test.expectedOrgIDLatestAPIVersionMap { + if orgAPI, ok := orgIDLatestAPIVersionMap[orgID]; !ok { + t.Errorf("orgIDLatestAPIVersionMap has no entry with the organization: %s, %v", orgID, orgIDLatestAPIVersionMap) + } else { + assert.Equal(t, len(test.expectedOrgIDLatestAPIVersionMap[orgID]), len(orgAPIs), "orgAPI length is different, expected: %v but found: %v", + len(test.expectedOrgIDLatestAPIVersionMap[orgID]), len(orgAPIs)) + for vuuid, expectedAPI := range test.expectedOrgIDLatestAPIVersionMap[orgID] { + if actualAPI, ok := orgAPI[vuuid]; !ok { + t.Errorf("orgIDLatestAPIVersionMap has not updated with new entry with the key for %s, %v", + vuuid, orgIDLatestAPIVersionMap) + } else { + assert.Equal(t, len(expectedAPI), len(actualAPI), "orgAPI for %v length is different, expected: %v but found: %v", + vuuid, len(expectedAPI), len(actualAPI)) + for version, expectedVersion := range expectedAPI { + if actualVersion, ok := actualAPI[version]; !ok { + t.Errorf("orgIDLatestAPIVersionMap has not updated with new entry with the key for %v: %s, %v", + vuuid, version, orgIDLatestAPIVersionMap) + } else { + assert.Equal(t, expectedVersion.Version, actualVersion.Version, "Not expected API Version found for %v in %v, expected: %v but found: %v", + vuuid, version, expectedVersion.Version, actualVersion.Version) + assert.Equal(t, expectedVersion.Major, actualVersion.Major, "Not expected API Major found for %v in %v, expected: %v but found: %v", + vuuid, version, expectedVersion.Major, actualVersion.Major) + assert.Equal(t, expectedVersion.Minor, actualVersion.Minor, "Not expected API Minor found for %v in %v, expected: %v but found: %v", + vuuid, version, expectedVersion.Minor, actualVersion.Minor) + assert.Equal(t, *expectedVersion.Patch, *actualVersion.Patch, "Not expected API Patch found for %v in %v, expected: %v but found: %v", + vuuid, version, *expectedVersion.Patch, *actualVersion.Patch) + } + } + } + } } } }) } } - -func testExistsInMapping[V any, M map[string]V](t *testing.T, mapping M, key string, checkExists bool) { - _, ok := mapping[key] - if checkExists { - if !ok { - t.Errorf("Not found mapping for key %s in map %v", key, mapping) - } - } else { - if ok { - t.Errorf("Found mapping for key %s in map %v", key, mapping) - } - } -} diff --git a/adapter/internal/discovery/xds/server_utils.go b/adapter/internal/discovery/xds/server_utils.go index ffb776f58c..671db1dba3 100644 --- a/adapter/internal/discovery/xds/server_utils.go +++ b/adapter/internal/discovery/xds/server_utils.go @@ -19,31 +19,8 @@ package xds import ( "fmt" - - "github.com/wso2/apk/adapter/pkg/utils/stringutils" ) -// getEnvironmentsToBeDeleted returns an slice of environments APIs to be u-deployed from -// by considering existing environments list and environments that APIs are wished to be un-deployed -func getEnvironmentsToBeDeleted(existingEnvs, deleteEnvs []string) (toBeDel []string, toBeKept []string) { - toBeDel = make([]string, 0, len(deleteEnvs)) - toBeKept = make([]string, 0, len(deleteEnvs)) - - // if deleteEnvs is empty (deleteEnvs wished to be deleted), delete all environments - if len(deleteEnvs) == 0 { - return existingEnvs, []string{} - } - // otherwise delete env if it wished to - for _, existingEnv := range existingEnvs { - if stringutils.StringInSlice(existingEnv, deleteEnvs) { - toBeDel = append(toBeDel, existingEnv) - } else { - toBeKept = append(toBeKept, existingEnv) - } - } - return -} - // GetvHostsIdentifier creates a identifier for vHosts for a API considering prod // and sand env func GetvHostsIdentifier(UUID string, envType string) string { diff --git a/adapter/internal/discovery/xds/server_utils_test.go b/adapter/internal/discovery/xds/server_utils_test.go deleted file mode 100644 index 9cc01fba17..0000000000 --- a/adapter/internal/discovery/xds/server_utils_test.go +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright (c) 2022, 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 xds - -import ( - "reflect" - "testing" -) - -func TestGetEnvironmentsToBeDeleted(t *testing.T) { - tests := []struct { - name string - existingEnvs, deleteEnvs []string - toBeDel, toBeKept []string - }{ - { - // Delete all envs - name: "Delete_all_environments_when_envs_supplied", - existingEnvs: []string{"Label1", "Label2"}, - deleteEnvs: []string{"Label1", "Label2"}, - toBeDel: []string{"Label1", "Label2"}, - toBeKept: []string{}, - }, - { - // Delete all envs - name: "Delete_all_environments_when_no_envs_supplied", - existingEnvs: []string{"Label1", "Label2", "Label3"}, - deleteEnvs: []string{}, - toBeDel: []string{"Label1", "Label2", "Label3"}, - toBeKept: []string{}, - }, - { - // Delete all envs - name: "Delete_all_environments_when_no_envs_supplied_nil", - existingEnvs: []string{"Label1", "Label2", "Label3"}, - deleteEnvs: nil, - toBeDel: []string{"Label1", "Label2", "Label3"}, - toBeKept: []string{}, - }, - { - // Delete some envs - name: "Delete_some_envs", - existingEnvs: []string{"Label1", "Label2", "Label3"}, - deleteEnvs: []string{"Label2", "Foo"}, // Foo should be ignored - toBeDel: []string{"Label2"}, - toBeKept: []string{"Label1", "Label3"}, - }, - { - // Delete nothing - name: "Delete_nothing", - existingEnvs: []string{"Label1", "Label2", "Label3"}, - deleteEnvs: []string{"Foo"}, // Foo should be ignored - toBeDel: []string{}, - toBeKept: []string{"Label1", "Label2", "Label3"}, - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - toBeDel, toBeKept := getEnvironmentsToBeDeleted(test.existingEnvs, test.deleteEnvs) - if !reflect.DeepEqual(toBeDel, test.toBeDel) { - t.Errorf("expected deleted environments %v but found %v", test.toBeDel, toBeDel) - } - if !reflect.DeepEqual(toBeKept, test.toBeKept) { - t.Errorf("expected deleted environments %v but found %v", test.toBeKept, toBeKept) - } - }) - } -} diff --git a/adapter/internal/oasparser/config_generator.go b/adapter/internal/oasparser/config_generator.go index e5fc9314d0..5fc04d7864 100644 --- a/adapter/internal/oasparser/config_generator.go +++ b/adapter/internal/oasparser/config_generator.go @@ -141,7 +141,7 @@ func UpdateRoutesConfig(routeConfig *routev3.RouteConfiguration, vhostToRouteArr // GetEnforcerAPI retrieves the ApiDS object model for a given swagger definition // along with the vhost to deploy the API. -func GetEnforcerAPI(adapterInternalAPI model.AdapterInternalAPI, vhost string) *api.Api { +func GetEnforcerAPI(adapterInternalAPI *model.AdapterInternalAPI, vhost string) *api.Api { resources := []*api.Resource{} isMockedAPI := false clientCertificates := []*api.Certificate{} diff --git a/adapter/internal/oasparser/envoyconf/routes_with_clusters.go b/adapter/internal/oasparser/envoyconf/routes_with_clusters.go index fb5e5f4d30..ac556245b2 100644 --- a/adapter/internal/oasparser/envoyconf/routes_with_clusters.go +++ b/adapter/internal/oasparser/envoyconf/routes_with_clusters.go @@ -1616,7 +1616,7 @@ func createInterceptorAPIClusters(adapterInternalAPI *model.AdapterInternalAPI, apiResponseInterceptor = adapterInternalAPI.GetInterceptor(adapterInternalAPI.GetVendorExtensions(), xWso2responseInterceptor, APILevelInterceptor) // if lua filter exists on api level, add cluster if apiResponseInterceptor.Enable { - logger.LoggerOasparser.Debugln("API level response interceptors found for " + adapterInternalAPI.GetID()) + logger.LoggerOasparser.Debugln("API level response interceptors found for " + apiTitle) apiResponseInterceptor.ClusterName = getClusterName(responseInterceptClustersNamePrefix, organizationID, vHost, apiTitle, apiVersion, "") cluster, addresses, err := CreateLuaCluster(interceptorCerts, apiResponseInterceptor) diff --git a/adapter/internal/oasparser/model/adapter_internal_api.go b/adapter/internal/oasparser/model/adapter_internal_api.go index 5e5bddddcc..498f22f1f5 100644 --- a/adapter/internal/oasparser/model/adapter_internal_api.go +++ b/adapter/internal/oasparser/model/adapter_internal_api.go @@ -42,7 +42,6 @@ import ( // adapter internal representation. The values are populated from the operator. The pathItem level information is represented // by the resources array which contains the Resource entries. type AdapterInternalAPI struct { - id string UUID string apiType string description string @@ -314,11 +313,6 @@ func (adapterInternalAPI *AdapterInternalAPI) GetDisableMtls() bool { return adapterInternalAPI.disableMtls } -// GetID returns the Id of the API -func (adapterInternalAPI *AdapterInternalAPI) GetID() string { - return adapterInternalAPI.id -} - // GetXWso2RequestBodyPass returns boolean value to indicate // whether it is allowed to pass request body to the enforcer or not. func (adapterInternalAPI *AdapterInternalAPI) GetXWso2RequestBodyPass() bool { @@ -349,11 +343,6 @@ func (adapterInternalAPI *AdapterInternalAPI) SetClientCerts(apiName string, cer adapterInternalAPI.clientCertificates = clientCerts } -// SetID set the Id of the API -func (adapterInternalAPI *AdapterInternalAPI) SetID(id string) { - adapterInternalAPI.id = id -} - // SetAPIDefinitionFile sets the API Definition File. func (adapterInternalAPI *AdapterInternalAPI) SetAPIDefinitionFile(file []byte) { adapterInternalAPI.apiDefinitionFile = file diff --git a/adapter/internal/oasparser/model/policy_container.go b/adapter/internal/oasparser/model/policy_container.go index 80d87a0d3c..031d43df4f 100644 --- a/adapter/internal/oasparser/model/policy_container.go +++ b/adapter/internal/oasparser/model/policy_container.go @@ -41,6 +41,8 @@ var ( } ) +// todo(amali) remove these files as this is no longer functional + // PolicyFlow holds list of Policies in a operation (in one flow: In, Out or Fault) type PolicyFlow string @@ -98,7 +100,7 @@ func (p PolicyContainerMap) GetFormattedOperationalPolicies(policies OperationPo if fmtPolicy, err := p.getFormattedPolicyFromTemplated(policy, policyInFlow, swagger); err == nil { fmtPolicies.Request = append(fmtPolicies.Request, fmtPolicy) loggers.LoggerOasparser.Debugf("Applying operation policy %q in request flow, for API %q in org %q, formatted policy %v", - policy.GetFullName(), swagger.GetID(), swagger.OrganizationID, fmtPolicy) + policy.GetFullName(), swagger.UUID, swagger.OrganizationID, fmtPolicy) } else { return fmtPolicies, err } @@ -108,7 +110,7 @@ func (p PolicyContainerMap) GetFormattedOperationalPolicies(policies OperationPo if fmtPolicy, err := p.getFormattedPolicyFromTemplated(policy, policyOutFlow, swagger); err == nil { fmtPolicies.Response = append(fmtPolicies.Response, fmtPolicy) loggers.LoggerOasparser.Debugf("Applying operation policy %q in response flow, for API %q in org %q, formatted policy %v", - policy.GetFullName(), swagger.GetID(), swagger.OrganizationID, fmtPolicy) + policy.GetFullName(), swagger.UUID, swagger.OrganizationID, fmtPolicy) } else { return fmtPolicies, err } @@ -118,7 +120,7 @@ func (p PolicyContainerMap) GetFormattedOperationalPolicies(policies OperationPo if fmtPolicy, err := p.getFormattedPolicyFromTemplated(policy, policyFaultFlow, swagger); err == nil { fmtPolicies.Fault = append(fmtPolicies.Fault, fmtPolicy) loggers.LoggerOasparser.Debugf("Applying operation policy %q in fault flow, for API %q in org %q, formatted policy %v", - policy.GetFullName(), swagger.GetID(), swagger.OrganizationID, fmtPolicy) + policy.GetFullName(), swagger.UUID, swagger.OrganizationID, fmtPolicy) } else { return fmtPolicies, err } @@ -132,28 +134,27 @@ func (p PolicyContainerMap) getFormattedPolicyFromTemplated(policy Policy, flow policyFullName := policy.GetFullName() spec := p[policyFullName].Specification if err := spec.validatePolicy(policy, flow); err != nil { - swagger.GetID() - loggers.LoggerOasparser.ErrorC(logging.PrintError(logging.Error2204, logging.MINOR, "Operation policy validation failed for API %q in org %q:, policy %q: %v", swagger.GetID(), swagger.OrganizationID, policyFullName, err)) + loggers.LoggerOasparser.ErrorC(logging.PrintError(logging.Error2204, logging.MINOR, "Operation policy validation failed for API %q in org %q:, policy %q: %v", swagger.UUID, swagger.OrganizationID, policyFullName, err)) return policy, err } defRaw := p[policyFullName].Definition.RawData t, err := template.New("policy-def").Funcs(policyDefFuncMap).Parse(string(defRaw)) if err != nil { - loggers.LoggerOasparser.ErrorC(logging.PrintError(logging.Error2205, logging.MINOR, "Error parsing the operation policy definition %q into go template of the API %q in org %q: %v", policyFullName, swagger.GetID(), swagger.OrganizationID, err)) + loggers.LoggerOasparser.ErrorC(logging.PrintError(logging.Error2205, logging.MINOR, "Error parsing the operation policy definition %q into go template of the API %q in org %q: %v", policyFullName, swagger.UUID, swagger.OrganizationID, err)) return Policy{}, err } var out bytes.Buffer err = t.Execute(&out, policy.Parameters) if err != nil { - loggers.LoggerOasparser.ErrorC(logging.PrintError(logging.Error2206, logging.MINOR, "Error parsing operation policy definition %q of the API %q in org %q: %v", policyFullName, swagger.GetID(), swagger.OrganizationID, err)) + loggers.LoggerOasparser.ErrorC(logging.PrintError(logging.Error2206, logging.MINOR, "Error parsing operation policy definition %q of the API %q in org %q: %v", policyFullName, swagger.UUID, swagger.OrganizationID, err)) return Policy{}, err } def := PolicyDefinition{} if err := yaml.Unmarshal(out.Bytes(), &def); err != nil { - loggers.LoggerOasparser.ErrorC(logging.PrintError(logging.Error2207, logging.MINOR, "Error parsing formalized operation policy definition %q into yaml of the API %q in org %q: %v", policyFullName, swagger.GetID(), swagger.OrganizationID, err)) + loggers.LoggerOasparser.ErrorC(logging.PrintError(logging.Error2207, logging.MINOR, "Error parsing formalized operation policy definition %q into yaml of the API %q in org %q: %v", policyFullName, swagger.UUID, swagger.OrganizationID, err)) return Policy{}, err } @@ -168,7 +169,7 @@ func (p PolicyContainerMap) getFormattedPolicyFromTemplated(policy Policy, flow // Required params may be comming from default values as defined in the policy specification // Hence do the validation after filling default values if err := validatePolicyAction(&policy); err != nil { - loggers.LoggerOasparser.ErrorC(logging.PrintError(logging.Error2208, logging.MINOR, "API policy validation failed, policy: %q of the API %q in org %q: %v", policyFullName, swagger.GetID(), swagger.OrganizationID, err)) + loggers.LoggerOasparser.ErrorC(logging.PrintError(logging.Error2208, logging.MINOR, "API policy validation failed, policy: %q of the API %q in org %q: %v", policyFullName, swagger.UUID, swagger.OrganizationID, err)) return Policy{}, err } return policy, nil diff --git a/adapter/internal/operator/controllers/dp/tokenissuer_controller.go b/adapter/internal/operator/controllers/dp/tokenissuer_controller.go index b4e5b987f9..cc0708136b 100644 --- a/adapter/internal/operator/controllers/dp/tokenissuer_controller.go +++ b/adapter/internal/operator/controllers/dp/tokenissuer_controller.go @@ -73,7 +73,7 @@ func (r *TokenssuerReconciler) Reconcile(ctx context.Context, req ctrl.Request) jwtIssuerMapping, err := getJWTIssuers(ctx, r.client, jwtKey) if err != nil { loggers.LoggerAPKOperator.ErrorC(logging.PrintError(logging.Error2660, logging.CRITICAL, - "Unable to find associated JWTIssuers for %s : %s", req.NamespacedName.String(), err.Error())) + "Unable to resolve JWTIssuers after updating %s : %s", req.NamespacedName.String(), err.Error())) return ctrl.Result{}, nil } UpdateEnforcerJWTIssuers(jwtIssuerMapping) @@ -220,7 +220,7 @@ func getJWTIssuers(ctx context.Context, client k8client.Client, namespace types. jwtIssuer.Spec.SignatureValidation.JWKS.TLS.ConfigMapRef, jwtIssuer.Spec.SignatureValidation.JWKS.TLS.SecretRef) if err != nil { loggers.LoggerAPKOperator.ErrorC(logging.PrintError(logging.Error2659, logging.MAJOR, - "Error resolving certificate for JWKS %v", err.Error())) + "Error resolving certificate for JWKS for issuer %s in CR %s, %v", resolvedJwtIssuer.Issuer, utils.NamespacedName(&jwtIssuer).String(), err.Error())) continue } jwks.TLS = &dpv1alpha1.ResolvedTLSConfig{ResolvedCertificate: tlsCertificate} @@ -233,8 +233,8 @@ func getJWTIssuers(ctx context.Context, client k8client.Client, namespace types. jwtIssuer.Spec.SignatureValidation.Certificate.ConfigMapRef, jwtIssuer.Spec.SignatureValidation.Certificate.SecretRef) if err != nil { loggers.LoggerAPKOperator.ErrorC(logging.PrintError(logging.Error2659, logging.MAJOR, - "Error resolving certificate for JWKS %v", err.Error())) - return nil, err + "Error resolving certificate for JWKS for issuer %s in CR %s, %v", resolvedJwtIssuer.Issuer, utils.NamespacedName(&jwtIssuer).String(), err.Error())) + continue } signatureValidation.Certificate = &dpv1alpha1.ResolvedTLSConfig{ResolvedCertificate: tlsCertificate} } diff --git a/adapter/internal/operator/synchronizer/api_state.go b/adapter/internal/operator/synchronizer/api_state.go index e15717249f..10e50c2235 100644 --- a/adapter/internal/operator/synchronizer/api_state.go +++ b/adapter/internal/operator/synchronizer/api_state.go @@ -41,7 +41,6 @@ type APIState struct { InterceptorServiceMapping map[string]v1alpha1.InterceptorService BackendJWTMapping map[string]v1alpha1.BackendJWT APIDefinitionFile []byte - OldOrganizationID string SubscriptionValidation bool MutualSSL *v1alpha2.MutualSSL } diff --git a/adapter/internal/operator/synchronizer/data_store.go b/adapter/internal/operator/synchronizer/data_store.go index 80265707da..8378998a37 100644 --- a/adapter/internal/operator/synchronizer/data_store.go +++ b/adapter/internal/operator/synchronizer/data_store.go @@ -77,7 +77,6 @@ func (ods *OperatorDataStore) processAPIState(apiNamespacedName types.Namespaced cachedAPI := ods.apiStore[apiNamespacedName] if apiState.APIDefinition.Generation > cachedAPI.APIDefinition.Generation { - cachedAPI.OldOrganizationID = cachedAPI.APIDefinition.Spec.Organization cachedAPI.APIDefinition = apiState.APIDefinition updated = true events = append(events, "API Definition") diff --git a/adapter/internal/operator/synchronizer/gql_api.go b/adapter/internal/operator/synchronizer/gql_api.go index 95d986fda3..86e9513d7e 100644 --- a/adapter/internal/operator/synchronizer/gql_api.go +++ b/adapter/internal/operator/synchronizer/gql_api.go @@ -75,22 +75,18 @@ func generateGQLAdapterInternalAPI(apiState APIState, gqlRoute *GQLRouteState, e return nil, nil, errors.New("failed to find matching listener name for the provided gql route") } - updatedLabelsMap := make(map[string]struct{}) listenerName := listeners[0] sectionName := relativeSectionNames[0] if len(listeners) != 0 { - updatedLabels, err := xds.UpdateAPICache(vHosts, labels, listenerName, sectionName, adapterInternalAPI) + err := xds.UpdateAPICache(vHosts, labels, listenerName, sectionName, &adapterInternalAPI) if err != nil { loggers.LoggerAPKOperator.ErrorC(logging.PrintError(logging.Error2633, logging.MAJOR, "Error updating the API : %s:%s in vhosts: %s, API_UUID: %v. %v", adapterInternalAPI.GetTitle(), adapterInternalAPI.GetVersion(), vHosts, adapterInternalAPI.UUID, err)) return nil, nil, err } - for newLabel := range updatedLabels { - updatedLabelsMap[newLabel] = struct{}{} - } } - return &adapterInternalAPI, updatedLabelsMap, nil + return &adapterInternalAPI, labels, nil } // getVhostForAPI returns the vHosts related to an API. @@ -103,15 +99,14 @@ func getVhostsForGQLAPI(gqlRoute *v1alpha2.GQLRoute) []string { } // getLabelsForAPI returns the labels related to an API. -func getLabelsForGQLAPI(gqlRoute *v1alpha2.GQLRoute) []string { - var labels []string - var err error +func getLabelsForGQLAPI(gqlRoute *v1alpha2.GQLRoute) map[string]struct{} { + labels := make(map[string]struct{}) for _, parentRef := range gqlRoute.Spec.ParentRefs { - err = xds.SanitizeGateway(string(parentRef.Name), false) + 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 = append(labels, string(parentRef.Name)) + labels[string(parentRef.Name)] = struct{}{} } } return labels @@ -133,10 +128,7 @@ func getListenersForGQLAPI(gqlRoute *v1alpha2.GQLRoute, apiUUID string) ([]strin 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 + return string(listener.Name) == string(*parentRef.SectionName) }) if listenerFound { sectionNames = append(sectionNames, string(matchedListener.Name)) @@ -151,9 +143,8 @@ func getListenersForGQLAPI(gqlRoute *v1alpha2.GQLRoute, apiUUID string) ([]strin func deleteGQLAPIFromEnv(gqlRoute *v1alpha2.GQLRoute, apiState APIState) error { labels := getLabelsForGQLAPI(gqlRoute) - org := apiState.APIDefinition.Spec.Organization uuid := string(apiState.APIDefinition.ObjectMeta.UID) - return xds.DeleteAPICREvent(labels, uuid, org) + return xds.DeleteAPI(uuid, labels) } // undeployGQLAPIInGateway undeploys the related API in CREATE and UPDATE events. diff --git a/adapter/internal/operator/synchronizer/rest_api.go b/adapter/internal/operator/synchronizer/rest_api.go index 38e5b218c7..113c0ef5f5 100644 --- a/adapter/internal/operator/synchronizer/rest_api.go +++ b/adapter/internal/operator/synchronizer/rest_api.go @@ -40,7 +40,7 @@ func undeployRestAPIInGateway(apiState APIState) error { if err != nil { loggers.LoggerXds.ErrorC(logging.PrintError(logging.Error2630, logging.MAJOR, "Error undeploying prod httpRoute of API : %v in Organization %v from environments %v."+ " Hence not checking on deleting the sand httpRoute of the API", string(apiState.APIDefinition.ObjectMeta.UID), apiState.APIDefinition.Spec.Organization, - getLabelsForAPI(apiState.ProdHTTPRoute.HTTPRouteCombined))) + getGatewayNameForAPI(apiState.ProdHTTPRoute.HTTPRouteCombined))) return err } if apiState.SandHTTPRoute != nil { @@ -97,7 +97,7 @@ func GenerateAdapterInternalAPI(apiState APIState, httpRoute *HTTPRouteState, en return nil, nil, err } vHosts := getVhostsForAPI(httpRoute.HTTPRouteCombined) - labels := getLabelsForAPI(httpRoute.HTTPRouteCombined) + labels := getGatewayNameForAPI(httpRoute.HTTPRouteCombined) listeners, relativeSectionNames := getListenersForAPI(httpRoute.HTTPRouteCombined, adapterInternalAPI.UUID) // We dont have a use case where a perticular API's two different http routes refer to two different gateway. Hence get the first listener name for the list for processing. if len(listeners) == 0 || len(relativeSectionNames) == 0 { @@ -110,13 +110,13 @@ func GenerateAdapterInternalAPI(apiState APIState, httpRoute *HTTPRouteState, en listenerName := listeners[0] sectionName := relativeSectionNames[0] if len(listeners) != 0 { - updatedLabels, err := xds.UpdateAPICache(vHosts, labels, listenerName, sectionName, adapterInternalAPI) + err := xds.UpdateAPICache(vHosts, labels, listenerName, sectionName, &adapterInternalAPI) if err != nil { loggers.LoggerAPKOperator.ErrorC(logging.PrintError(logging.Error2633, logging.MAJOR, "Error updating the API : %s:%s in vhosts: %s, API_UUID: %v. %v", adapterInternalAPI.GetTitle(), adapterInternalAPI.GetVersion(), vHosts, adapterInternalAPI.UUID, err)) return nil, nil, err } - for newLabel := range updatedLabels { + for newLabel := range labels { updatedLabelsMap[newLabel] = struct{}{} } } @@ -133,16 +133,16 @@ func getVhostsForAPI(httpRoute *gwapiv1b1.HTTPRoute) []string { return vHosts } -// getLabelsForAPI returns the labels related to an API. -func getLabelsForAPI(httpRoute *gwapiv1b1.HTTPRoute) []string { - var labels []string +// getGatewayNameForAPI returns the labels related to an API. +func getGatewayNameForAPI(httpRoute *gwapiv1b1.HTTPRoute) map[string]struct{} { + labels := make(map[string]struct{}) var err error for _, parentRef := range httpRoute.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 = append(labels, string(parentRef.Name)) + labels[string(parentRef.Name)] = struct{}{} } } return labels @@ -178,8 +178,7 @@ func getListenersForAPI(httpRoute *gwapiv1b1.HTTPRoute, apiUUID string) ([]strin } func deleteAPIFromEnv(httpRoute *gwapiv1b1.HTTPRoute, apiState APIState) error { - labels := getLabelsForAPI(httpRoute) - org := apiState.APIDefinition.Spec.Organization + labels := getGatewayNameForAPI(httpRoute) uuid := string(apiState.APIDefinition.ObjectMeta.UID) - return xds.DeleteAPICREvent(labels, uuid, org) + return xds.DeleteAPI(uuid, labels) } diff --git a/adapter/internal/operator/synchronizer/synchronizer.go b/adapter/internal/operator/synchronizer/synchronizer.go index 3305399d5d..38897b6491 100644 --- a/adapter/internal/operator/synchronizer/synchronizer.go +++ b/adapter/internal/operator/synchronizer/synchronizer.go @@ -30,7 +30,6 @@ import ( "github.com/wso2/apk/adapter/config" "github.com/wso2/apk/adapter/internal/discovery/xds" "github.com/wso2/apk/adapter/internal/loggers" - "github.com/wso2/apk/adapter/internal/oasparser/model" "github.com/wso2/apk/adapter/internal/operator/constants" "github.com/wso2/apk/adapter/internal/operator/utils" "github.com/wso2/apk/adapter/pkg/logging" @@ -133,28 +132,20 @@ func deployMultipleAPIsInGateway(event *APIEvent, successChannel *chan SuccessEv var updatedAPIs []types.NamespacedName for i, apiState := range event.Events { loggers.LoggerAPKOperator.Infof("%s event received for %s", event.EventType, apiState.APIDefinition.Name) - if len(apiState.OldOrganizationID) != 0 { - xds.RemoveAPIFromOrgAPIMap(string((*apiState.APIDefinition).ObjectMeta.UID), apiState.OldOrganizationID) + // TODO(amali) move this inside updateAPI method + // Remove the API from the internal maps before adding it again + oldGatewayNames := xds.RemoveAPIFromAllInternalMaps(string((*apiState.APIDefinition).ObjectMeta.UID)) + for label := range oldGatewayNames { + updatedLabelsMap[label] = struct{}{} } if apiState.APIDefinition.Spec.APIType == "REST" { - if apiState.ProdHTTPRoute == nil { - var adapterInternalAPI model.AdapterInternalAPI - adapterInternalAPI.SetInfoAPICR(*apiState.APIDefinition) - xds.RemoveAPICacheForEnv(adapterInternalAPI, constants.Production) - } - if apiState.SandHTTPRoute == nil { - var adapterInternalAPI model.AdapterInternalAPI - adapterInternalAPI.SetInfoAPICR(*apiState.APIDefinition) - xds.RemoveAPICacheForEnv(adapterInternalAPI, constants.Sandbox) - } - if apiState.ProdHTTPRoute != nil { _, updatedLabels, err := GenerateAdapterInternalAPI(apiState, apiState.ProdHTTPRoute, constants.Production) if err != nil { loggers.LoggerAPKOperator.ErrorC(logging.PrintError(logging.Error2665, logging.CRITICAL, "Error deploying prod httpRoute of API : %v in Organization %v from environments %v. Error: %v", string(apiState.APIDefinition.Spec.APIName), apiState.APIDefinition.Spec.Organization, - getLabelsForAPI(apiState.ProdHTTPRoute.HTTPRouteCombined), err)) + getGatewayNameForAPI(apiState.ProdHTTPRoute.HTTPRouteCombined), err)) // removing failed updates from the events list because this will be sent to partition server event.Events = append(event.Events[:i], event.Events[i+1:]...) continue @@ -170,7 +161,7 @@ func deployMultipleAPIsInGateway(event *APIEvent, successChannel *chan SuccessEv loggers.LoggerAPKOperator.ErrorC(logging.PrintError(logging.Error2666, logging.CRITICAL, "Error deploying sand httpRoute of API : %v in Organization %v from environments %v. Error: %v", string(apiState.APIDefinition.Spec.APIName), apiState.APIDefinition.Spec.Organization, - getLabelsForAPI(apiState.ProdHTTPRoute.HTTPRouteCombined), err)) + getGatewayNameForAPI(apiState.ProdHTTPRoute.HTTPRouteCombined), err)) // removing failed updates from the events list because this will be sent to partition server event.Events = append(event.Events[:i], event.Events[i+1:]...) continue @@ -182,16 +173,6 @@ func deployMultipleAPIsInGateway(event *APIEvent, successChannel *chan SuccessEv } if apiState.APIDefinition.Spec.APIType == "GraphQL" { - if apiState.ProdGQLRoute == nil { - var adapterInternalAPI model.AdapterInternalAPI - adapterInternalAPI.SetInfoAPICR(*apiState.APIDefinition) - xds.RemoveAPICacheForEnv(adapterInternalAPI, constants.Production) - } - if apiState.SandGQLRoute == nil { - var adapterInternalAPI model.AdapterInternalAPI - adapterInternalAPI.SetInfoAPICR(*apiState.APIDefinition) - xds.RemoveAPICacheForEnv(adapterInternalAPI, constants.Sandbox) - } if apiState.ProdGQLRoute != nil { _, updatedLabels, err := generateGQLAdapterInternalAPI(apiState, apiState.ProdGQLRoute, constants.Production) if err != nil { diff --git a/adapter/pkg/semanticversion/semantic_version.go b/adapter/pkg/semanticversion/semantic_version.go index 3750e59407..db44db230d 100644 --- a/adapter/pkg/semanticversion/semantic_version.go +++ b/adapter/pkg/semanticversion/semantic_version.go @@ -17,12 +17,9 @@ package semanticversion import ( - "errors" "fmt" "strconv" "strings" - - logger "github.com/wso2/apk/adapter/pkg/loggers" ) // SemVersion is the struct to store the version components of an API @@ -34,16 +31,13 @@ type SemVersion struct { } // ValidateAndGetVersionComponents validates version string and extracts version components -func ValidateAndGetVersionComponents(version string, apiName string) (*SemVersion, error) { +func ValidateAndGetVersionComponents(version string) (*SemVersion, error) { versionComponents := strings.Split(version, ".") // If the versionComponents length is less than 2, return error if len(versionComponents) < 2 || !strings.HasPrefix(versionComponents[0], "v") { - logger.LoggerSemanticVersion.Errorf("API version validation failed for API: %v. API Version: %v", apiName, version) - errMessage := "Invalid version: " + version + " for API: " + apiName + - ". API version should be in the format x.y.z, x.y, vx.y.z or vx.y where x,y,z are non-negative integers" + - " and v is version prefix" - return nil, errors.New(errMessage) + return nil, fmt.Errorf("invalid version: %v. API version should be in the format x.y.z, x.y, vx.y.z or vx.y where x,y,z are non-negative integers"+ + " and v is version prefix", version) } majorVersionStr := strings.TrimPrefix(versionComponents[0], "v") @@ -51,13 +45,11 @@ func ValidateAndGetVersionComponents(version string, apiName string) (*SemVersio majorVersion, majorVersionConvErr := strconv.Atoi(majorVersionStr) minorVersion, minorVersionConvErr := strconv.Atoi(versionComponents[1]) if majorVersionConvErr != nil || majorVersion < 0 { - logger.LoggerSemanticVersion.Errorf(fmt.Sprintf("API major version should be a non-negative integer in API: %v. API Version: %v", apiName, version), majorVersionConvErr) - return nil, errors.New("invalid version format") + return nil, fmt.Errorf("invalid version format. API major version should be a non-negative integer in API Version: %v, %v", version, majorVersionConvErr) } if minorVersionConvErr != nil || minorVersion < 0 { - logger.LoggerSemanticVersion.Errorf(fmt.Sprintf("API minor version should be a non-negative integer in API: %v. API Version: %v", apiName, version), minorVersionConvErr) - return nil, errors.New("invalid version format") + return nil, fmt.Errorf("invalid version format. API minor version should be a non-negative integer in API Version: %v, %v", version, minorVersionConvErr) } if len(versionComponents) == 2 { @@ -71,8 +63,7 @@ func ValidateAndGetVersionComponents(version string, apiName string) (*SemVersio patchVersion, patchVersionConvErr := strconv.Atoi(versionComponents[2]) if patchVersionConvErr != nil { - logger.LoggerSemanticVersion.Errorf(fmt.Sprintf("API patch version should be an integer in API: %v. API Version: %v", apiName, version), patchVersionConvErr) - return nil, errors.New("invalid version format") + return nil, fmt.Errorf("invalid version format. API patch version should be an integer in API Version: %v, %v", version, patchVersionConvErr) } return &SemVersion{ Version: version, diff --git a/adapter/pkg/semanticversion/semantic_version_test.go b/adapter/pkg/semanticversion/semantic_version_test.go index 53a7e1bf2e..ff01d2f205 100644 --- a/adapter/pkg/semanticversion/semantic_version_test.go +++ b/adapter/pkg/semanticversion/semantic_version_test.go @@ -128,34 +128,34 @@ func TestValidateAndGetVersionComponents(t *testing.T) { version: "1.2.3", apiName: "TestAPI", expectedResult: nil, - expectedError: errors.New("Invalid version: 1.2.3 for API: TestAPI. API version should be in the format x.y.z, x.y, vx.y.z or vx.y where x,y,z are non-negative integers and v is version prefix"), + expectedError: errors.New("invalid version: 1.2.3. API version should be in the format x.y.z, x.y, vx.y.z or vx.y where x,y,z are non-negative integers and v is version prefix"), }, { name: "Invalid version format - negative major version", version: "v-1.2.3", apiName: "TestAPI", expectedResult: nil, - expectedError: errors.New("invalid version format"), + expectedError: errors.New("invalid version format. API major version should be a non-negative integer in API Version: v-1.2.3, "), }, { name: "Invalid version format - negative minor version", version: "v1.-2.3", apiName: "TestAPI", expectedResult: nil, - expectedError: errors.New("invalid version format"), + expectedError: errors.New("invalid version format. API minor version should be a non-negative integer in API Version: v1.-2.3, "), }, { name: "Invalid version format - patch version not an integer", version: "v1.2.three", apiName: "TestAPI", expectedResult: nil, - expectedError: errors.New("invalid version format"), + expectedError: errors.New("invalid version format. API patch version should be an integer in API Version: v1.2.three, strconv.Atoi: parsing \"three\": invalid syntax"), }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - result, err := ValidateAndGetVersionComponents(tt.version, tt.apiName) + result, err := ValidateAndGetVersionComponents(tt.version) // Check for errors if (err != nil && tt.expectedError == nil) || (err == nil && tt.expectedError != nil) || (err != nil && tt.expectedError != nil && err.Error() != tt.expectedError.Error()) { diff --git a/developer/tryout/samples/sample-api.yaml b/developer/tryout/samples/sample-api.yaml index e4e9e27d22..63cf542273 100644 --- a/developer/tryout/samples/sample-api.yaml +++ b/developer/tryout/samples/sample-api.yaml @@ -30,7 +30,7 @@ spec: sandbox: - httpRouteRefs: - sand-http-route-http-bin-api - organization: a3b58ccf-6ecc-4557-b5bb-0a35cce38256 + organization: default --- apiVersion: gateway.networking.k8s.io/v1beta1 kind: HTTPRoute diff --git a/test/cucumber-tests/README.md b/test/cucumber-tests/README.md index 7d57f439cb..1159238d4e 100644 --- a/test/cucumber-tests/README.md +++ b/test/cucumber-tests/README.md @@ -46,7 +46,7 @@ To create a new feature, follow these steps: 3. Port forward router-service to use localhost. ```bash - kubectl port-forward svc/apk-test-setup-wso2-apk-router-service -n apk-integration-test 9095:9095 + kubectl port-forward svc/apk-test-setup-wso2-apk-gateway-service -n apk-integration-test 9095:9095 ``` 4. Add the following DNS mappings to `/etc/hosts` file.