Skip to content

Commit aa16e1f

Browse files
committed
Improved handling blocked APIs by not invoking /apis endpoint unless it is the startup. The JMS/ASB event's details are used to populate the necessary maps(api-metadata)
APIList object is not passed to the enforcer now, instead of that EnforcerAPI's lifecycle state is used. Any API within the api metadata map should be kept only if the API is a default API or API is in blocked state. If the API becomes unblocked, it is removed from the map. During the startup also only blocked and default versioned APIs are pulled.
1 parent 932b1a9 commit aa16e1f

File tree

28 files changed

+735
-704
lines changed

28 files changed

+735
-704
lines changed

adapter/internal/discovery/xds/marshaller.go

+43-69
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ package xds
33
import (
44
"encoding/json"
55
"fmt"
6-
"strconv"
76

87
"github.com/wso2/product-microgateway/adapter/config"
98
logger "github.com/wso2/product-microgateway/adapter/internal/loggers"
@@ -14,8 +13,8 @@ import (
1413
)
1514

1615
var (
17-
// APIListMap has the following mapping label -> apiUUID -> API (Metadata)
18-
APIListMap map[string]map[string]*subscription.APIs
16+
// APIMetadataMap has the following mapping apiUUID -> API (Metadata)
17+
APIMetadataMap map[string]*subscription.APIs
1918
// SubscriptionMap contains the subscriptions recieved from API Manager Control Plane
2019
SubscriptionMap map[int32]*subscription.Subscription
2120
// ApplicationMap contains the applications recieved from API Manager Control Plane
@@ -40,8 +39,6 @@ const (
4039
DeleteEvent
4140
)
4241

43-
const blockedStatus string = "BLOCKED"
44-
4542
// MarshalConfig will marshal a Config struct - read from the config toml - to
4643
// enfocer's CDS resource representation.
4744
func MarshalConfig(config *config.Config) *enforcer.Config {
@@ -480,20 +477,15 @@ func MarshalSubscriptionPolicyEventAndReturnList(policy *types.SubscriptionPolic
480477
return marshalSubscriptionPolicyMapToList(SubscriptionPolicyMap)
481478
}
482479

483-
// MarshalAPIMetataAndReturnList updates the internal APIListMap and returns the XDS compatible APIList.
484-
// apiList is the internal APIList object (For single API, this would contain a List with just one API)
480+
// UpdateAPIMetataMapWithMultipleAPIs updates the internal APIMetadataMap and it needs to be called during the startup.
481+
// The purpose here is to store default versioned APIs and blocked APIs.
485482
// initialAPIUUIDListMap is assigned during startup when global adapter is associated. This would be empty otherwise.
486-
// gatewayLabel is the environment.
487-
func MarshalAPIMetataAndReturnList(apiList *types.APIList, initialAPIUUIDListMap map[string]int, gatewayLabel string) *subscription.APIList {
483+
func UpdateAPIMetataMapWithMultipleAPIs(apiList *types.APIList, initialAPIUUIDListMap map[string]int) {
488484

489-
if APIListMap == nil {
490-
APIListMap = make(map[string]map[string]*subscription.APIs)
491-
}
492-
// var resourceMapForLabel map[string]*subscription.APIs
493-
if _, ok := APIListMap[gatewayLabel]; !ok {
494-
APIListMap[gatewayLabel] = make(map[string]*subscription.APIs)
485+
if APIMetadataMap == nil {
486+
APIMetadataMap = make(map[string]*subscription.APIs)
495487
}
496-
resourceMapForLabel := APIListMap[gatewayLabel]
488+
497489
for _, api := range apiList.List {
498490
// initialAPIUUIDListMap is not null if the adapter is running with global adapter enabled, and it is
499491
// the first method invocation.
@@ -503,44 +495,44 @@ func MarshalAPIMetataAndReturnList(apiList *types.APIList, initialAPIUUIDListMap
503495
}
504496
}
505497
newAPI := marshalAPIMetadata(&api)
506-
resourceMapForLabel[api.UUID] = newAPI
507-
}
508-
return marshalAPIListMapToList(resourceMapForLabel)
509-
}
510-
511-
// DeleteAPIAndReturnList removes the API from internal maps and returns the marshalled API List.
512-
// If the apiUUID is not found in the internal map under the provided environment, then it would return a
513-
// nil value. Hence it is required to check if the return value is nil, prior to updating the XDS cache.
514-
func DeleteAPIAndReturnList(apiUUID, organizationUUID string, gatewayLabel string) *subscription.APIList {
515-
if _, ok := APIListMap[gatewayLabel]; !ok {
516-
logger.LoggerXds.Debugf("No API Metadata is available under gateway Environment : %s", gatewayLabel)
517-
return nil
498+
APIMetadataMap[api.UUID] = newAPI
518499
}
519-
delete(APIListMap[gatewayLabel], apiUUID)
520-
return marshalAPIListMapToList(APIListMap[gatewayLabel])
521500
}
522501

523-
// MarshalAPIForLifeCycleChangeEventAndReturnList updates the internal map's API instances lifecycle state only if
524-
// stored API Instance's or input status event is a blocked event.
525-
// If no change is applied, it would return nil. Hence the XDS cache should not be updated.
526-
func MarshalAPIForLifeCycleChangeEventAndReturnList(apiUUID, status, gatewayLabel string) *subscription.APIList {
527-
if _, ok := APIListMap[gatewayLabel]; !ok {
528-
logger.LoggerXds.Debugf("No API Metadata is available under gateway Environment : %s", gatewayLabel)
529-
return nil
530-
}
531-
if _, ok := APIListMap[gatewayLabel][apiUUID]; !ok {
532-
logger.LoggerXds.Debugf("No API Metadata for API ID: %s is available under gateway Environment : %s",
533-
apiUUID, gatewayLabel)
534-
return nil
535-
}
536-
storedAPILCState := APIListMap[gatewayLabel][apiUUID].LcState
537-
538-
// Because the adapter only required to update the XDS if it is related to blocked state.
539-
if !(storedAPILCState == blockedStatus || status == blockedStatus) {
540-
return nil
541-
}
542-
APIListMap[gatewayLabel][apiUUID].LcState = status
543-
return marshalAPIListMapToList(APIListMap[gatewayLabel])
502+
// UpdateAPIMetataMapWithAPILCEvent updates the internal map's API instances lifecycle state only if
503+
// stored API Instance's or input status event is a blocked event. If the API's state is changed to un-BLOCKED state,
504+
// the record will be removed.
505+
func UpdateAPIMetataMapWithAPILCEvent(apiUUID, status string) {
506+
507+
apiEntry, apiFound := APIMetadataMap[apiUUID]
508+
if !apiFound {
509+
// IF API is not stored within the metadata Map and the lifecycle state is something else other BLOCKED state, those are discarded.
510+
if status != blockedStatus {
511+
logger.LoggerXds.Debugf("API life cycle state change event is discarded for the API : %s", apiUUID)
512+
return
513+
}
514+
// IF API is not available and state is BLOCKED, needs to create a new instance and store within the Map.
515+
logger.LoggerXds.Debugf("No API Metadata for API ID: %s is available. Hence a new record is added.", apiUUID)
516+
APIMetadataMap[apiUUID] = &subscription.APIs{
517+
Uuid: apiUUID,
518+
LcState: status,
519+
}
520+
logger.LoggerXds.Infof("API life cycle state change event with state %q is updated for the API : %s",
521+
status, apiUUID)
522+
return
523+
}
524+
// If the API is available in the metadata map it should be either a BLOCKED API and/or default versioned API.
525+
// If the update for existing API entry is not "BLOCKED" then the API can be removed from the list.
526+
// when the API is unavailable in the api metadata list received in the enforcer, it would be treated as
527+
// an unblocked API.
528+
// But if the API is not the default version, those records won't be stored.
529+
if status != blockedStatus && !apiEntry.IsDefaultVersion {
530+
delete(APIMetadataMap, apiUUID)
531+
return
532+
}
533+
APIMetadataMap[apiUUID].LcState = status
534+
logger.LoggerXds.Infof("API life cycle state change event with state %q is updated for the API : %s",
535+
status, apiUUID)
544536
}
545537

546538
func marshalSubscription(subscriptionInternal *types.Subscription) *subscription.Subscription {
@@ -599,13 +591,6 @@ func marshalKeyMapping(keyMappingInternal *types.ApplicationKeyMapping) *subscri
599591

600592
func marshalAPIMetadata(api *types.API) *subscription.APIs {
601593
return &subscription.APIs{
602-
ApiId: strconv.Itoa(api.APIID),
603-
Name: api.Name,
604-
Provider: api.Provider,
605-
Version: api.Version,
606-
Context: api.Context,
607-
Policy: api.Policy,
608-
ApiType: api.APIType,
609594
Uuid: api.UUID,
610595
IsDefaultVersion: api.IsDefaultVersion,
611596
LcState: api.APIStatus,
@@ -642,14 +627,3 @@ func marshalSubscriptionPolicy(policy *types.SubscriptionPolicy) *subscription.S
642627
func GetApplicationKeyMappingReference(keyMapping *types.ApplicationKeyMapping) string {
643628
return keyMapping.ConsumerKey + ":" + keyMapping.KeyManager
644629
}
645-
646-
// CheckIfAPIMetadataIsAlreadyAvailable returns true only if the API Metadata for the given API UUID
647-
// is already available
648-
func CheckIfAPIMetadataIsAlreadyAvailable(apiUUID, label string) bool {
649-
if _, labelAvailable := APIListMap[label]; labelAvailable {
650-
if _, apiAvailale := APIListMap[label][apiUUID]; apiAvailale {
651-
return true
652-
}
653-
}
654-
return false
655-
}

adapter/internal/discovery/xds/server.go

+80-9
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ import (
4545
"github.com/wso2/product-microgateway/adapter/internal/oasparser/model"
4646
mgw "github.com/wso2/product-microgateway/adapter/internal/oasparser/model"
4747
"github.com/wso2/product-microgateway/adapter/internal/svcdiscovery"
48+
"github.com/wso2/product-microgateway/adapter/pkg/discovery/api/wso2/discovery/api"
4849
subscription "github.com/wso2/product-microgateway/adapter/pkg/discovery/api/wso2/discovery/subscription"
4950
throttle "github.com/wso2/product-microgateway/adapter/pkg/discovery/api/wso2/discovery/throttle"
5051
wso2_cache "github.com/wso2/product-microgateway/adapter/pkg/discovery/protocol/cache/v3"
@@ -83,7 +84,7 @@ var (
8384
orgIDOpenAPIRoutesMap map[string]map[string][]*routev3.Route // organizationID -> Vhost:API_UUID -> Envoy Routes map
8485
orgIDOpenAPIClustersMap map[string]map[string][]*clusterv3.Cluster // organizationID -> Vhost:API_UUID -> Envoy Clusters map
8586
orgIDOpenAPIEndpointsMap map[string]map[string][]*corev3.Address // organizationID -> Vhost:API_UUID -> Envoy Endpoints map
86-
orgIDOpenAPIEnforcerApisMap map[string]map[string]types.Resource // organizationID -> Vhost:API_UUID -> API Resource map
87+
orgIDOpenAPIEnforcerApisMap map[string]map[string]*api.Api // organizationID -> Vhost:API_UUID -> API Resource map
8788
orgIDvHostBasepathMap map[string]map[string]string // organizationID -> Vhost:basepath -> Vhost:API_UUID
8889

8990
reverseAPINameVersionMap map[string]string
@@ -119,6 +120,8 @@ const (
119120
maxRandomInt int = 999999999
120121
prototypedAPI string = "PROTOTYPED"
121122
apiKeyFieldSeparator string = ":"
123+
blockedStatus string = "BLOCKED"
124+
nonBlockedStatus string = "CREATED/PUBLISHED"
122125
)
123126

124127
// IDHash uses ID field as the node hash.
@@ -161,7 +164,7 @@ func init() {
161164
orgIDOpenAPIRoutesMap = make(map[string]map[string][]*routev3.Route) // organizationID -> Vhost:API_UUID -> Envoy Routes map
162165
orgIDOpenAPIClustersMap = make(map[string]map[string][]*clusterv3.Cluster) // organizationID -> Vhost:API_UUID -> Envoy Clusters map
163166
orgIDOpenAPIEndpointsMap = make(map[string]map[string][]*corev3.Address) // organizationID -> Vhost:API_UUID -> Envoy Endpoints map
164-
orgIDOpenAPIEnforcerApisMap = make(map[string]map[string]types.Resource) // organizationID -> Vhost:API_UUID -> API Resource map
167+
orgIDOpenAPIEnforcerApisMap = make(map[string]map[string]*api.Api) // organizationID -> Vhost:API_UUID -> API Resource map
165168
orgIDvHostBasepathMap = make(map[string]map[string]string)
166169

167170
reverseAPINameVersionMap = make(map[string]string)
@@ -334,6 +337,8 @@ func UpdateAPI(vHost string, apiProject mgw.ProjectAPI, environments []string) (
334337
apiYaml.Name, apiYaml.Version, organizationID)
335338
return nil, validationErr
336339
}
340+
// Update the LifecycleStatus of the API.
341+
updateLCStateOfMgwSwagger(&mgwSwagger)
337342

338343
// -------- Finished updating mgwSwagger struct
339344

@@ -434,28 +439,60 @@ func UpdateAPI(vHost string, apiProject mgw.ProjectAPI, environments []string) (
434439
}
435440

436441
if _, ok := orgIDOpenAPIEnforcerApisMap[organizationID]; ok {
437-
orgIDOpenAPIEnforcerApisMap[organizationID][apiIdentifier] = oasParser.GetEnforcerAPI(mgwSwagger,
438-
apiProject.APILifeCycleStatus, vHost)
442+
orgIDOpenAPIEnforcerApisMap[organizationID][apiIdentifier] = oasParser.GetEnforcerAPI(mgwSwagger, vHost)
439443
} else {
440-
enforcerAPIMap := make(map[string]types.Resource)
441-
enforcerAPIMap[apiIdentifier] = oasParser.GetEnforcerAPI(mgwSwagger, apiProject.APILifeCycleStatus,
442-
vHost)
444+
enforcerAPIMap := make(map[string]*api.Api)
445+
enforcerAPIMap[apiIdentifier] = oasParser.GetEnforcerAPI(mgwSwagger, vHost)
443446
orgIDOpenAPIEnforcerApisMap[organizationID] = enforcerAPIMap
444447
}
445448

446449
// TODO: (VirajSalaka) Fault tolerance mechanism implementation
447450
revisionStatus := updateXdsCacheOnAPIAdd(oldLabels, newLabels)
448451
if revisionStatus {
449452
// send updated revision to control plane
450-
deployedRevision = notifier.UpdateDeployedRevisions(apiYaml.ID, apiYaml.RevisionID, environments,
451-
vHost)
453+
deployedRevision = notifier.UpdateDeployedRevisions(apiYaml.ID, apiYaml.RevisionID, environments, vHost)
452454
}
453455
if svcdiscovery.IsServiceDiscoveryEnabled {
454456
startConsulServiceDiscovery(organizationID) //consul service discovery starting point
455457
}
456458
return deployedRevision, nil
457459
}
458460

461+
// UpdateAPIInEnforcerForBlockedAPIUpdate updates the state of APIMetadataMap under apiID with the lifecycle state and then
462+
// XDS cache for enforcerAPIs is updated.
463+
func UpdateAPIInEnforcerForBlockedAPIUpdate(apiID, organizationID, state string) {
464+
mutexForInternalMapUpdate.Lock()
465+
defer mutexForInternalMapUpdate.Unlock()
466+
// First needs to update the API Metadata Map
467+
UpdateAPIMetataMapWithAPILCEvent(apiID, state)
468+
var apiReferenceArray []string
469+
// Iterate through the enforcerAPIsMap and update the lifecycle status for the map. This is the map representing
470+
// runtime-artifact for each API
471+
if openAPIEnforcerAPIsMap, orgFound := orgIDOpenAPIEnforcerApisMap[organizationID]; orgFound {
472+
// The reference is vhost:apiUUID. Hence it is required to iterate through all API entries as there could be multiple deployments of the same API
473+
// under different vhosts.
474+
for apiReference, enforcerAPI := range openAPIEnforcerAPIsMap {
475+
if strings.HasSuffix(apiReference, ":"+apiID) && (state == blockedStatus || enforcerAPI.ApiLifeCycleState == blockedStatus) {
476+
logger.LoggerXds.Infof("API Lifecycle status is updated for the API %s to %s state", apiReference, state)
477+
enforcerAPI.ApiLifeCycleState = state
478+
apiReferenceArray = append(apiReferenceArray, apiReference)
479+
}
480+
}
481+
} else {
482+
logger.LoggerXds.Infof("API Life Cycle event is not applied due to irrelevant tenant domain : %s.", organizationID)
483+
return
484+
}
485+
486+
// For all the gateway labels containing the API, the enforcer XDS cache needs to be updated.
487+
if openAPIEnvoyLabelMap, ok := orgIDOpenAPIEnvoyMap[organizationID]; ok {
488+
for _, apiReference := range apiReferenceArray {
489+
if labels, labelsFound := openAPIEnvoyLabelMap[apiReference]; labelsFound {
490+
updateXdsCacheForEnforcerAPIsOnly(labels)
491+
}
492+
}
493+
}
494+
}
495+
459496
// GetAllEnvironments returns all the environments merging new environments with already deployed environments
460497
// of the given vhost of the API
461498
func GetAllEnvironments(apiUUID, vhost string, newEnvironments []string) []string {
@@ -782,6 +819,12 @@ func updateXdsCacheOnAPIAdd(oldLabels []string, newLabels []string) bool {
782819
return revisionStatus
783820
}
784821

822+
func updateXdsCacheForEnforcerAPIsOnly(labels []string) {
823+
for _, label := range labels {
824+
UpdateEnforcerApis(label, generateEnforcerAPIsForLabel(label), "")
825+
}
826+
}
827+
785828
// GenerateEnvoyResoucesForLabel generates envoy resources for a given label
786829
// This method will list out all APIs mapped to the label. and generate envoy resources for all of these APIs.
787830
func GenerateEnvoyResoucesForLabel(label string) ([]types.Resource, []types.Resource, []types.Resource,
@@ -850,6 +893,24 @@ func GenerateEnvoyResoucesForLabel(label string) ([]types.Resource, []types.Reso
850893
return endpoints, clusters, listeners, routeConfigs, apis
851894
}
852895

896+
// generateEnforcerAPIsForLabel generates ebforcerAPIs resource array for a given label.
897+
// This is used when the envoy resources are not required to have changes but the enforcer APIs are required to (Blocked State APIs)
898+
func generateEnforcerAPIsForLabel(label string) []types.Resource {
899+
var apis []types.Resource
900+
901+
for organizationID, entityMap := range orgIDOpenAPIEnvoyMap {
902+
for apiKey, labels := range entityMap {
903+
if arrayContains(labels, label) {
904+
enforcerAPI, ok := orgIDOpenAPIEnforcerApisMap[organizationID][apiKey]
905+
if ok {
906+
apis = append(apis, enforcerAPI)
907+
}
908+
}
909+
}
910+
}
911+
return apis
912+
}
913+
853914
// GenerateGlobalClusters generates the globally available clusters and endpoints.
854915
func GenerateGlobalClusters(label string) {
855916
clusters, endpoints := oasParser.GetGlobalClusters()
@@ -1239,3 +1300,13 @@ func UpdateEnforcerThrottleData(throttleData *throttle.ThrottleData) {
12391300
enforcerThrottleData = t
12401301
logger.LoggerXds.Infof("New Throttle Data cache update for the label: " + label + " version: " + fmt.Sprint(version))
12411302
}
1303+
1304+
func updateLCStateOfMgwSwagger(mgwSwagger *model.MgwSwagger) {
1305+
// If there are any metadata stored under the APIMetadataMap and Life Cycle state is blocked, update the mgwSwagger
1306+
apiEntry, apiFound := APIMetadataMap[mgwSwagger.GetID()]
1307+
if apiFound && apiEntry.LcState == blockedStatus {
1308+
mgwSwagger.LifeCycleState = blockedStatus
1309+
return
1310+
}
1311+
mgwSwagger.LifeCycleState = nonBlockedStatus
1312+
}

0 commit comments

Comments
 (0)