Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add backend&subscription based AI ratelimit #2446

Merged
merged 24 commits into from
Sep 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 16 additions & 16 deletions adapter/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ module github.com/wso2/apk/adapter
go 1.22

require (
github.com/envoyproxy/go-control-plane v0.12.0
github.com/envoyproxy/go-control-plane v0.13.0
github.com/fsnotify/fsnotify v1.7.0
github.com/golang/protobuf v1.5.3
github.com/golang/protobuf v1.5.4
github.com/google/uuid v1.6.0
github.com/onsi/ginkgo/v2 v2.14.0
github.com/onsi/gomega v1.30.0
Expand All @@ -14,7 +14,7 @@ require (
github.com/sirupsen/logrus v1.9.0
github.com/wso2/apk/common-go-libs v0.0.0-20231208100153-24bee7b4bd81
golang.org/x/exp v0.0.0-20231206192017-f3f8817b8deb
google.golang.org/grpc v1.62.0
google.golang.org/grpc v1.65.0
google.golang.org/protobuf v1.34.1
gopkg.in/yaml.v2 v2.4.0
k8s.io/api v0.29.2
Expand All @@ -24,11 +24,12 @@ require (
)

require (
cel.dev/expr v0.15.0 // indirect
github.com/agnivade/levenshtein v1.1.1 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/census-instrumentation/opencensus-proto v0.4.1 // indirect
github.com/cespare/xxhash/v2 v2.2.0 // indirect
github.com/cncf/xds/go v0.0.0-20231128003011-0fa0005c9caa // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/cncf/xds/go v0.0.0-20240423153145-555b57ec207b // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/emicklei/go-restful/v3 v3.11.0 // indirect
github.com/envoyproxy/protoc-gen-validate v1.0.4 // indirect
Expand Down Expand Up @@ -56,9 +57,10 @@ require (
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect
github.com/prometheus/client_model v0.5.0 // indirect
github.com/prometheus/client_model v0.6.0 // indirect
github.com/prometheus/common v0.45.0 // indirect
github.com/prometheus/procfs v0.12.0 // indirect
github.com/shirou/gopsutil/v3 v3.24.2 // indirect
Expand All @@ -70,17 +72,15 @@ require (
github.com/yusufpapurcu/wmi v1.2.4 // indirect
go.uber.org/multierr v1.11.0 // indirect
go.uber.org/zap v1.26.0 // indirect
golang.org/x/net v0.21.0 // indirect
golang.org/x/oauth2 v0.16.0 // indirect
golang.org/x/term v0.17.0 // indirect
golang.org/x/text v0.14.0 // indirect
golang.org/x/net v0.25.0 // indirect
golang.org/x/oauth2 v0.20.0 // indirect
golang.org/x/term v0.20.0 // indirect
golang.org/x/text v0.15.0 // indirect
golang.org/x/time v0.3.0 // indirect
golang.org/x/tools v0.16.1 // indirect
gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect
google.golang.org/appengine v1.6.8 // indirect
google.golang.org/genproto v0.0.0-20240123012728-ef4313101c80 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20240123012728-ef4313101c80 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240123012728-ef4313101c80 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20240528184218-531527333157 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240528184218-531527333157 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
k8s.io/apiextensions-apiserver v0.29.2 // indirect
Expand All @@ -99,8 +99,8 @@ replace github.com/wso2/apk/common-go-libs => ../common-go-libs

require (
github.com/ghodss/yaml v1.0.0
github.com/stretchr/testify v1.8.4
golang.org/x/sys v0.17.0 // indirect
github.com/stretchr/testify v1.9.0
golang.org/x/sys v0.20.0 // indirect
gopkg.in/natefinch/lumberjack.v2 v2.2.1
sigs.k8s.io/gateway-api v1.0.0
)
82 changes: 32 additions & 50 deletions adapter/go.sum

Large diffs are not rendered by default.

21 changes: 16 additions & 5 deletions adapter/internal/discovery/xds/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,9 +97,10 @@ var (

// 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

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
vHostToSubscriptionBasedAIRLMap map[string]bool
vHostToSubscriptionBasedRLMap map[string]bool
// Envoy Label as map key
// TODO(amali) use this without generating all again.
gatewayLabelConfigMap map[string]*EnvoyGatewayConfig // GW-Label -> EnvoyGatewayConfig struct map
Expand Down Expand Up @@ -152,6 +153,8 @@ func init() {
gatewayLabelConfigMap = make(map[string]*EnvoyGatewayConfig)
orgAPIMap = make(map[string]map[string]*EnvoyInternalAPI)
orgIDLatestAPIVersionMap = make(map[string]map[string]map[string]semantic_version.SemVersion)
vHostToSubscriptionBasedAIRLMap = make(map[string]bool)
vHostToSubscriptionBasedRLMap = make(map[string]bool)

enforcerLabelMap = make(map[string]*EnforcerInternalAPI)
// currently subscriptions, configs, applications, applicationPolicies, subscriptionPolicies,
Expand Down Expand Up @@ -330,7 +333,7 @@ func GenerateEnvoyResoucesForGateway(gatewayName string) ([]types.Resource,
if found {
// Prepare the route config name based on the gateway listener section name.
routeConfigName := common.GetEnvoyRouteConfigName(listener.Name, string(listenerSection.Name))
routesConfig := oasParser.GetRouteConfigs(map[string][]*routev3.Route{vhost: routes}, routeConfigName, envoyGatewayConfig.customRateLimitPolicies)
routesConfig := oasParser.GetRouteConfigs(map[string][]*routev3.Route{vhost: routes}, routeConfigName, envoyGatewayConfig.customRateLimitPolicies, vHostToSubscriptionBasedAIRLMap, vHostToSubscriptionBasedRLMap)

routeConfigMatched, alreadyExistsInRouteConfigList := routeConfigs[routeConfigName]
if alreadyExistsInRouteConfigList {
Expand All @@ -353,7 +356,7 @@ func GenerateEnvoyResoucesForGateway(gatewayName string) ([]types.Resource,
var vhostToRouteArrayFilteredMapForSystemEndpoints = make(map[string][]*routev3.Route)
vhostToRouteArrayFilteredMapForSystemEndpoints[systemHost] = vhostToRouteArrayMap[systemHost]
routeConfigName := common.GetEnvoyRouteConfigName(common.GetEnvoyListenerName(string(listener.Protocol), uint32(listener.Port)), string(listener.Name))
systemRoutesConfig := oasParser.GetRouteConfigs(vhostToRouteArrayFilteredMapForSystemEndpoints, routeConfigName, envoyGatewayConfig.customRateLimitPolicies)
systemRoutesConfig := oasParser.GetRouteConfigs(vhostToRouteArrayFilteredMapForSystemEndpoints, routeConfigName, envoyGatewayConfig.customRateLimitPolicies, vHostToSubscriptionBasedAIRLMap, vHostToSubscriptionBasedRLMap)
routeConfigs[routeConfigName] = systemRoutesConfig
}
}
Expand Down Expand Up @@ -560,6 +563,14 @@ func PopulateInternalMaps(adapterInternalAPI *model.AdapterInternalAPI, labels,
}

err := UpdateOrgAPIMap(vHosts, labels, listenerName, sectionName, adapterInternalAPI)
for vhost := range vHosts {
if adapterInternalAPI.AIProvider.Enabled && adapterInternalAPI.GetSubscriptionValidation() {
vHostToSubscriptionBasedAIRLMap[vhost] = true
}
if adapterInternalAPI.GetSubscriptionValidation() {
vHostToSubscriptionBasedRLMap[vhost] = true
}
}
if err != nil {
logger.LoggerXds.ErrorC(logging.PrintError(logging.Error1415, logging.MAJOR,
"Error updating the API : %s:%s in vhosts: %s, API_UUID: %v. %v",
Expand Down
10 changes: 2 additions & 8 deletions adapter/internal/oasparser/config_generator.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,8 +85,8 @@ func GetProductionListener(gateway *gwapiv1.Gateway, resolvedListenerCerts map[s
//
// The RouteConfiguration is named as "default"
func GetRouteConfigs(vhostToRouteArrayMap map[string][]*routev3.Route, routeConfigName string,
customRateLimitPolicies []*model.CustomRateLimitPolicy) *routev3.RouteConfiguration {
vHosts := envoy.CreateVirtualHosts(vhostToRouteArrayMap, customRateLimitPolicies)
customRateLimitPolicies []*model.CustomRateLimitPolicy, vhostToSubscriptionAIRL map[string]bool, vhostToSubscriptionRL map[string]bool) *routev3.RouteConfiguration {
vHosts := envoy.CreateVirtualHosts(vhostToRouteArrayMap, customRateLimitPolicies, vhostToSubscriptionAIRL, vhostToSubscriptionRL)
routeConfig := envoy.CreateRoutesConfigForRds(vHosts, routeConfigName)
return routeConfig
}
Expand Down Expand Up @@ -119,12 +119,6 @@ func GetCacheResources(endpoints []*corev3.Address, clusters []*clusterv3.Cluste
return listenerRes, clusterRes, routeConfigRes, endpointRes
}

// UpdateRoutesConfig updates the existing routes configuration with the provided map of vhost to array of routes.
// All the already existing routes (within the routeConfiguration) will be removed.
func UpdateRoutesConfig(routeConfig *routev3.RouteConfiguration, vhostToRouteArrayMap map[string][]*routev3.Route) {
routeConfig.VirtualHosts = envoy.CreateVirtualHosts(vhostToRouteArrayMap, nil)
}

// 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 {
Expand Down
2 changes: 2 additions & 0 deletions adapter/internal/oasparser/envoyconf/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ const (
const (
httpConManagerStartPrefix string = "ingress_http"
extAuthzPerRouteName string = "type.googleapis.com/envoy.extensions.filters.http.ext_authz.v3.ExtAuthzPerRoute"
extProcPerRouteName string = "type.googleapis.com/envoy.extensions.filters.http.ext_proc.v3.ExtProcPerRoute"
ratelimitPerRouteName string = "type.googleapis.com/envoy.extensions.filters.http.ratelimit.v3.RateLimitPerRoute"
luaPerRouteName string = "type.googleapis.com/envoy.extensions.filters.http.lua.v3.LuaPerRoute"
corsFilterName string = "type.googleapis.com/envoy.extensions.filters.http.cors.v3.Cors"
localRateLimitPerRouteName string = "type.googleapis.com/envoy.extensions.filters.http.local_ratelimit.v3.LocalRateLimit"
Expand Down
50 changes: 50 additions & 0 deletions adapter/internal/oasparser/envoyconf/http_filters.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import (
envoy_config_ratelimit_v3 "github.com/envoyproxy/go-control-plane/envoy/config/ratelimit/v3"
cors_filter_v3 "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/cors/v3"
ext_authv3 "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/ext_authz/v3"
ext_process "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/ext_proc/v3"
luav3 "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/lua/v3"
ratelimit "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/ratelimit/v3"
routerv3 "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/router/v3"
Expand All @@ -47,9 +48,16 @@ import (
"github.com/golang/protobuf/ptypes/any"
)


// HTTPExternalProcessor HTTP filter
const HTTPExternalProcessor = "envoy.filters.http.ext_proc"
// RatelimitFilterName Ratelimit filter name
const RatelimitFilterName = "envoy.filters.http.ratelimit"

// getHTTPFilters generates httpFilter configuration
func getHTTPFilters(globalLuaScript string) []*hcmv3.HttpFilter {
extAuth := getExtAuthzHTTPFilter()
extProcessor := getExtProcessHTTPFilter()
router := getRouterHTTPFilter()
luaLocal := getLuaFilter(LuaLocal, `
function envoy_on_request(request_handle)
Expand All @@ -64,6 +72,7 @@ end`)
extAuth,
luaLocal,
luaGlobal,
extProcessor,
}
conf := config.ReadConfigs()
if conf.Envoy.RateLimit.Enabled {
Expand Down Expand Up @@ -162,6 +171,10 @@ func getRateLimitFilter() *hcmv3.HttpFilter {
Domain: RateLimiterDomain,
FailureModeDeny: conf.Envoy.RateLimit.FailureModeDeny,
EnableXRatelimitHeaders: enableXRatelimitHeaders,
Timeout: &durationpb.Duration{
Nanos: (int32(conf.Envoy.RateLimit.RequestTimeoutInMillis) % 1000) * 1000000,
Seconds: conf.Envoy.RateLimit.RequestTimeoutInMillis / 1000,
},
RateLimitService: &envoy_config_ratelimit_v3.RateLimitServiceConfig{
TransportApiVersion: corev3.ApiVersion_V3,
GrpcService: &corev3.GrpcService{
Expand Down Expand Up @@ -190,6 +203,43 @@ func getRateLimitFilter() *hcmv3.HttpFilter {
return &rlFilter
}

// getExtProcessHTTPFilter gets ExtAauthz http filter.
func getExtProcessHTTPFilter() *hcmv3.HttpFilter {
// conf := config.ReadConfigs()
externalProcessor := &ext_process.ExternalProcessor{
GrpcService: &corev3.GrpcService{
TargetSpecifier: &corev3.GrpcService_EnvoyGrpc_{
EnvoyGrpc: &corev3.GrpcService_EnvoyGrpc{
ClusterName: extAuthzClusterName,
},
},
},
ProcessingMode: &ext_process.ProcessingMode{
ResponseBodyMode: ext_process.ProcessingMode_BUFFERED,
RequestHeaderMode: ext_process.ProcessingMode_SKIP,
ResponseHeaderMode: ext_process.ProcessingMode_SKIP,
},
MetadataOptions: &ext_process.MetadataOptions{
ForwardingNamespaces: &ext_process.MetadataOptions_MetadataNamespaces{
Untyped: []string{"envoy.filters.http.ext_authz", "envoy.filters.http.ext_proc"},
},
},
RequestAttributes: []string{"xds.route_metadata"},
ResponseAttributes: []string{"xds.route_metadata"},
}
ext, err2 := anypb.New(externalProcessor)
if err2 != nil {
logger.LoggerOasparser.Error(err2)
}
extProcessFilter := hcmv3.HttpFilter{
Name: HTTPExternalProcessor,
ConfigType: &hcmv3.HttpFilter_TypedConfig{
TypedConfig: ext,
},
}
return &extProcessFilter
}

// getExtAuthzHTTPFilter gets ExtAauthz http filter.
func getExtAuthzHTTPFilter() *hcmv3.HttpFilter {
conf := config.ReadConfigs()
Expand Down
1 change: 1 addition & 0 deletions adapter/internal/oasparser/envoyconf/internal_dtos.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ type routeCreateParams struct {
environment string
envType string
mirrorClusterNames map[string][]string
isAiAPI bool
}

// RatelimitCriteria criterias of rate limiting
Expand Down
Loading
Loading