Update higress ingress annotation (#49)

This commit is contained in:
Yang
2022-11-10 20:53:23 +08:00
committed by GitHub
parent 268c73301e
commit da93352a30
23 changed files with 86 additions and 1427 deletions

1
.gitignore vendored
View File

@@ -1,5 +1,6 @@
external
out
*.out
*.tgz
*.wasm
.idea/

View File

@@ -182,8 +182,7 @@ func TestConvertGatewaysForIngress(t *testing.T) {
},
AnnotationsConfig: &annotations.Ingress{
DownstreamTLS: &annotations.DownstreamTLSConfig{
TlsMinVersion: annotations.TLSProtocolVersion("TLSv1.1"),
CipherSuites: []string{"ECDHE-RSA-AES128-GCM-SHA256", "AES256-SHA"},
CipherSuites: []string{"ECDHE-RSA-AES128-GCM-SHA256", "AES256-SHA"},
},
},
},
@@ -249,8 +248,7 @@ func TestConvertGatewaysForIngress(t *testing.T) {
},
AnnotationsConfig: &annotations.Ingress{
DownstreamTLS: &annotations.DownstreamTLSConfig{
TlsMinVersion: annotations.TLSProtocolVersion("TLSv1.2"),
CipherSuites: []string{"ECDHE-RSA-AES128-GCM-SHA256"},
CipherSuites: []string{"ECDHE-RSA-AES128-GCM-SHA256"},
},
},
},
@@ -284,10 +282,9 @@ func TestConvertGatewaysForIngress(t *testing.T) {
},
Hosts: []string{"foo.com"},
Tls: &networking.ServerTLSSettings{
Mode: networking.ServerTLSSettings_SIMPLE,
CredentialName: "kubernetes-ingress://ingress-v1beta1__/wakanda/foo-com",
MinProtocolVersion: networking.ServerTLSSettings_TLSV1_1,
CipherSuites: []string{"ECDHE-RSA-AES128-GCM-SHA256", "AES256-SHA"},
Mode: networking.ServerTLSSettings_SIMPLE,
CredentialName: "kubernetes-ingress://ingress-v1beta1__/wakanda/foo-com",
CipherSuites: []string{"ECDHE-RSA-AES128-GCM-SHA256", "AES256-SHA"},
},
},
},
@@ -321,10 +318,9 @@ func TestConvertGatewaysForIngress(t *testing.T) {
},
Hosts: []string{"test.com"},
Tls: &networking.ServerTLSSettings{
Mode: networking.ServerTLSSettings_SIMPLE,
CredentialName: "kubernetes-ingress://ingress-v1beta1__/wakanda/test-com",
MinProtocolVersion: networking.ServerTLSSettings_TLSV1_1,
CipherSuites: []string{"ECDHE-RSA-AES128-GCM-SHA256", "AES256-SHA"},
Mode: networking.ServerTLSSettings_SIMPLE,
CredentialName: "kubernetes-ingress://ingress-v1beta1__/wakanda/test-com",
CipherSuites: []string{"ECDHE-RSA-AES128-GCM-SHA256", "AES256-SHA"},
},
},
},
@@ -466,8 +462,7 @@ func TestConvertGatewaysForIngress(t *testing.T) {
},
AnnotationsConfig: &annotations.Ingress{
DownstreamTLS: &annotations.DownstreamTLSConfig{
TlsMinVersion: annotations.TLSProtocolVersion("TLSv1.2"),
CipherSuites: []string{"ECDHE-RSA-AES128-GCM-SHA256"},
CipherSuites: []string{"ECDHE-RSA-AES128-GCM-SHA256"},
},
},
},
@@ -501,10 +496,9 @@ func TestConvertGatewaysForIngress(t *testing.T) {
},
Hosts: []string{"foo.com"},
Tls: &networking.ServerTLSSettings{
Mode: networking.ServerTLSSettings_SIMPLE,
CredentialName: "kubernetes-ingress://ingress-v1__/wakanda/foo-com",
MinProtocolVersion: networking.ServerTLSSettings_TLSV1_2,
CipherSuites: []string{"ECDHE-RSA-AES128-GCM-SHA256"},
Mode: networking.ServerTLSSettings_SIMPLE,
CredentialName: "kubernetes-ingress://ingress-v1__/wakanda/foo-com",
CipherSuites: []string{"ECDHE-RSA-AES128-GCM-SHA256"},
},
},
},
@@ -538,10 +532,9 @@ func TestConvertGatewaysForIngress(t *testing.T) {
},
Hosts: []string{"test.com"},
Tls: &networking.ServerTLSSettings{
Mode: networking.ServerTLSSettings_SIMPLE,
CredentialName: "kubernetes-ingress://ingress-v1__/wakanda/test-com",
MinProtocolVersion: networking.ServerTLSSettings_TLSV1_2,
CipherSuites: []string{"ECDHE-RSA-AES128-GCM-SHA256"},
Mode: networking.ServerTLSSettings_SIMPLE,
CredentialName: "kubernetes-ingress://ingress-v1__/wakanda/test-com",
CipherSuites: []string{"ECDHE-RSA-AES128-GCM-SHA256"},
},
},
},

View File

@@ -54,16 +54,10 @@ type Ingress struct {
IPAccessControl *IPAccessControlConfig
HeaderControl *HeaderControlConfig
Timeout *TimeoutConfig
Retry *RetryConfig
LoadBalance *LoadBalanceConfig
localRateLimit *localRateLimitConfig
Fallback *FallbackConfig
Auth *AuthConfig
@@ -105,22 +99,6 @@ func (i *Ingress) NeedTrafficPolicy() bool {
i.LoadBalance != nil
}
func (i *Ingress) MergeHostIPAccessControlIfNotExist(ac *IPAccessControlConfig) {
if i.IPAccessControl != nil && i.IPAccessControl.Domain != nil {
return
}
if ac != nil && ac.Domain != nil {
if i.IPAccessControl == nil {
i.IPAccessControl = &IPAccessControlConfig{
Domain: ac.Domain,
}
} else {
i.IPAccessControl.Domain = ac.Domain
}
}
}
type AnnotationHandler interface {
Parser
GatewayHandler
@@ -147,11 +125,8 @@ func NewAnnotationHandlerManager() AnnotationHandler {
rewrite{},
upstreamTLS{},
ipAccessControl{},
headerControl{},
timeout{},
retry{},
loadBalance{},
localRateLimit{},
fallback{},
auth{},
},
@@ -166,10 +141,7 @@ func NewAnnotationHandlerManager() AnnotationHandler {
redirect{},
rewrite{},
ipAccessControl{},
headerControl{},
timeout{},
retry{},
localRateLimit{},
fallback{},
},
trafficPolicyHandlers: []TrafficPolicyHandler{

View File

@@ -68,8 +68,8 @@ func TestAuthParse(t *testing.T) {
},
{
input: map[string]string{
buildNginxAnnotationKey(authType): defaultAuthType,
buildMSEAnnotationKey(authSecretAnn): "foo/bar",
buildNginxAnnotationKey(authType): defaultAuthType,
buildHigressAnnotationKey(authSecretAnn): "foo/bar",
},
secret: &v1.Secret{
ObjectMeta: metav1.ObjectMeta{
@@ -96,7 +96,7 @@ func TestAuthParse(t *testing.T) {
{
input: map[string]string{
buildNginxAnnotationKey(authType): defaultAuthType,
buildMSEAnnotationKey(authSecretAnn): "foo/bar",
buildHigressAnnotationKey(authSecretAnn): "foo/bar",
buildNginxAnnotationKey(authSecretTypeAnn): string(authMapAuthSecretType),
},
secret: &v1.Secret{
@@ -125,7 +125,7 @@ func TestAuthParse(t *testing.T) {
{
input: map[string]string{
buildNginxAnnotationKey(authType): defaultAuthType,
buildMSEAnnotationKey(authSecretAnn): "bar",
buildHigressAnnotationKey(authSecretAnn): "bar",
buildNginxAnnotationKey(authSecretTypeAnn): string(authFileAuthSecretType),
},
secret: &v1.Secret{

View File

@@ -106,8 +106,6 @@ func ApplyByWeight(canary, route *networking.HTTPRoute, canaryIngress *Ingress)
// We will process total weight in the end.
route.Route = append(route.Route, canary.Route[0])
// canary route use the header control applied on itself.
headerControl{}.ApplyRoute(canary, canaryIngress)
// Move route level to destination level
canary.Route[0].Headers = canary.Headers
@@ -168,10 +166,6 @@ func ApplyByHeader(canary, route *networking.HTTPRoute, canaryIngress *Ingress)
}
}
canary.Headers = nil
// canary route use the header control applied on itself.
headerControl{}.ApplyRoute(canary, canaryIngress)
// First add normal route cluster
canary.Route[0].FallbackClusters = append(canary.Route[0].FallbackClusters,
route.Route[0].Destination.DeepCopy())

View File

@@ -166,89 +166,3 @@ func TestApplyWeight(t *testing.T) {
t.Fatal("Should be equal")
}
}
func TestApplyHeader(t *testing.T) {
route := &networking.HTTPRoute{
Headers: &networking.Headers{
Request: &networking.Headers_HeaderOperations{
Add: map[string]string{
"normal": "true",
},
},
},
Route: []*networking.HTTPRouteDestination{
{
Destination: &networking.Destination{
Host: "normal",
Port: &networking.PortSelector{
Number: 80,
},
},
},
},
}
canary := &networking.HTTPRoute{
Headers: &networking.Headers{
Request: &networking.Headers_HeaderOperations{
Add: map[string]string{
"canary": "true",
},
},
},
Route: []*networking.HTTPRouteDestination{
{
Destination: &networking.Destination{
Host: "canary",
Port: &networking.PortSelector{
Number: 80,
},
},
},
},
}
ApplyByHeader(canary, route, &Ingress{
Canary: &CanaryConfig{},
HeaderControl: &HeaderControlConfig{
Request: &HeaderOperation{
Add: map[string]string{
"canary": "true",
},
},
},
})
expect := &networking.HTTPRoute{
Headers: &networking.Headers{
Request: &networking.Headers_HeaderOperations{
Add: map[string]string{
"canary": "true",
},
},
Response: &networking.Headers_HeaderOperations{},
},
Route: []*networking.HTTPRouteDestination{
{
Destination: &networking.Destination{
Host: "canary",
Port: &networking.PortSelector{
Number: 80,
},
},
FallbackClusters: []*networking.Destination{
{
Host: "normal",
Port: &networking.PortSelector{
Number: 80,
},
},
},
},
},
}
if !reflect.DeepEqual(canary, expect) {
t.Fatal("Should be equal")
}
}

View File

@@ -131,9 +131,9 @@ func TestCorsParse(t *testing.T) {
},
{
input: Annotations{
buildMSEAnnotationKey(enableCors): "true",
buildHigressAnnotationKey(enableCors): "true",
buildNginxAnnotationKey(allowOrigin): "https://origin-site.com:4443, http://origin-site.com, https://example.org:1199",
buildMSEAnnotationKey(allowMethods): "GET, PUT",
buildHigressAnnotationKey(allowMethods): "GET, PUT",
buildNginxAnnotationKey(allowHeaders): "foo,bar",
buildNginxAnnotationKey(allowCredentials): "false",
buildNginxAnnotationKey(maxAge): "100",

View File

@@ -73,7 +73,7 @@ func TestFallbackParse(t *testing.T) {
},
{
input: map[string]string{
buildMSEAnnotationKey(annDefaultBackend): "app",
buildHigressAnnotationKey(annDefaultBackend): "app",
},
expect: &FallbackConfig{
DefaultBackend: model.NamespacedName{
@@ -85,13 +85,13 @@ func TestFallbackParse(t *testing.T) {
},
{
input: map[string]string{
buildMSEAnnotationKey(annDefaultBackend): "foo/app",
buildHigressAnnotationKey(annDefaultBackend): "foo/app",
},
},
{
input: map[string]string{
buildMSEAnnotationKey(annDefaultBackend): "test/app",
buildNginxAnnotationKey(customHTTPError): "404,503",
buildHigressAnnotationKey(annDefaultBackend): "test/app",
buildNginxAnnotationKey(customHTTPError): "404,503",
},
expect: &FallbackConfig{
DefaultBackend: model.NamespacedName{
@@ -104,8 +104,8 @@ func TestFallbackParse(t *testing.T) {
},
{
input: map[string]string{
buildMSEAnnotationKey(annDefaultBackend): "test/app",
buildNginxAnnotationKey(customHTTPError): "404,5ac",
buildHigressAnnotationKey(annDefaultBackend): "test/app",
buildNginxAnnotationKey(customHTTPError): "404,5ac",
},
expect: &FallbackConfig{
DefaultBackend: model.NamespacedName{

View File

@@ -29,48 +29,18 @@ import (
const (
authTLSSecret = "auth-tls-secret"
tlsMinVersion = "tls-min-protocol-version"
tlsMaxVersion = "tls-max-protocol-version"
sslCipher = "ssl-cipher"
)
type TLSProtocolVersion string
const (
tlsV10 TLSProtocolVersion = "TLSv1.0"
tlsV11 TLSProtocolVersion = "TLSv1.1"
tlsV12 TLSProtocolVersion = "TLSv1.2"
tlsV13 TLSProtocolVersion = "TLSv1.3"
)
var (
_ Parser = &downstreamTLS{}
_ GatewayHandler = &downstreamTLS{}
tlsProtocol = map[TLSProtocolVersion]networking.ServerTLSSettings_TLSProtocol{
tlsV10: networking.ServerTLSSettings_TLSV1_0,
tlsV11: networking.ServerTLSSettings_TLSV1_1,
tlsV12: networking.ServerTLSSettings_TLSV1_2,
tlsV13: networking.ServerTLSSettings_TLSV1_3,
}
)
func isValidTLSProtocolVersion(protocol string) bool {
tls := TLSProtocolVersion(protocol)
_, exist := tlsProtocol[tls]
return exist
}
func Convert(protocol string) networking.ServerTLSSettings_TLSProtocol {
return tlsProtocol[TLSProtocolVersion(protocol)]
}
type DownstreamTLSConfig struct {
TlsMinVersion TLSProtocolVersion
TlsMaxVersion TLSProtocolVersion
CipherSuites []string
Mode networking.ServerTLSSettings_TLSmode
CASecretName model.NamespacedName
CipherSuites []string
Mode networking.ServerTLSSettings_TLSmode
CASecretName model.NamespacedName
}
type downstreamTLS struct{}
@@ -100,16 +70,6 @@ func (d downstreamTLS) Parse(annotations Annotations, config *Ingress, _ *Global
}
}
if minVersion, err := annotations.ParseStringForMSE(tlsMinVersion); err == nil &&
isValidTLSProtocolVersion(minVersion) {
downstreamTLSConfig.TlsMinVersion = TLSProtocolVersion(minVersion)
}
if maxVersion, err := annotations.ParseStringForMSE(tlsMaxVersion); err == nil &&
isValidTLSProtocolVersion(maxVersion) {
downstreamTLSConfig.TlsMaxVersion = TLSProtocolVersion(maxVersion)
}
if rawTlsCipherSuite, err := annotations.ParseStringASAP(sslCipher); err == nil {
var validCipherSuite []string
cipherList := strings.Split(rawTlsCipherSuite, ":")
@@ -144,12 +104,6 @@ func (d downstreamTLS) ApplyGateway(gateway *networking.Gateway, config *Ingress
}
}
if downstreamTLSConfig.TlsMinVersion != "" {
server.Tls.MinProtocolVersion = tlsProtocol[downstreamTLSConfig.TlsMinVersion]
}
if downstreamTLSConfig.TlsMaxVersion != "" {
server.Tls.MaxProtocolVersion = tlsProtocol[downstreamTLSConfig.TlsMaxVersion]
}
if len(downstreamTLSConfig.CipherSuites) != 0 {
server.Tls.CipherSuites = downstreamTLSConfig.CipherSuites
}
@@ -158,8 +112,6 @@ func (d downstreamTLS) ApplyGateway(gateway *networking.Gateway, config *Ingress
}
func needDownstreamTLS(annotations Annotations) bool {
return annotations.HasMSE(tlsMinVersion) ||
annotations.HasMSE(tlsMaxVersion) ||
annotations.HasASAP(sslCipher) ||
return annotations.HasASAP(sslCipher) ||
annotations.HasASAP(authTLSSecret)
}

View File

@@ -32,28 +32,7 @@ func TestParse(t *testing.T) {
{},
{
input: map[string]string{
MSEAnnotationsPrefix + "/" + tlsMinVersion: "TLSv1.0",
},
expect: &DownstreamTLSConfig{
Mode: networking.ServerTLSSettings_SIMPLE,
TlsMinVersion: tlsV10,
},
},
{
input: map[string]string{
MSEAnnotationsPrefix + "/" + tlsMinVersion: "TLSv1.3",
DefaultAnnotationsPrefix + "/" + sslCipher: "ECDHE-RSA-AES256-GCM-SHA384:AES128-SHA",
},
expect: &DownstreamTLSConfig{
Mode: networking.ServerTLSSettings_SIMPLE,
TlsMinVersion: tlsV13,
CipherSuites: []string{"ECDHE-RSA-AES256-GCM-SHA384", "AES128-SHA"},
},
},
{
input: map[string]string{
MSEAnnotationsPrefix + "/" + tlsMinVersion: "xxx",
DefaultAnnotationsPrefix + "/" + sslCipher: "ECDHE-RSA-AES256-GCM-SHA384:AES128-SHA",
buildNginxAnnotationKey(sslCipher): "ECDHE-RSA-AES256-GCM-SHA384:AES128-SHA",
},
expect: &DownstreamTLSConfig{
Mode: networking.ServerTLSSettings_SIMPLE,
@@ -62,19 +41,8 @@ func TestParse(t *testing.T) {
},
{
input: map[string]string{
MSEAnnotationsPrefix + "/" + tlsMinVersion: "xxx",
MSEAnnotationsPrefix + "/" + sslCipher: "ECDHE-RSA-AES256-GCM-SHA384:AES128-SHA",
},
expect: &DownstreamTLSConfig{
Mode: networking.ServerTLSSettings_SIMPLE,
CipherSuites: []string{"ECDHE-RSA-AES256-GCM-SHA384", "AES128-SHA"},
},
},
{
input: map[string]string{
buildNginxAnnotationKey(authTLSSecret): "test",
MSEAnnotationsPrefix + "/" + tlsMinVersion: "xxx",
MSEAnnotationsPrefix + "/" + sslCipher: "ECDHE-RSA-AES256-GCM-SHA384:AES128-SHA",
buildNginxAnnotationKey(authTLSSecret): "test",
buildNginxAnnotationKey(sslCipher): "ECDHE-RSA-AES256-GCM-SHA384:AES128-SHA",
},
expect: &DownstreamTLSConfig{
CASecretName: model.NamespacedName{
@@ -87,8 +55,7 @@ func TestParse(t *testing.T) {
},
{
input: map[string]string{
buildMSEAnnotationKey(authTLSSecret): "test/foo",
MSEAnnotationsPrefix + "/" + tlsMinVersion: "TLSv1.3",
buildHigressAnnotationKey(authTLSSecret): "test/foo",
DefaultAnnotationsPrefix + "/" + sslCipher: "ECDHE-RSA-AES256-GCM-SHA384:AES128-SHA",
},
expect: &DownstreamTLSConfig{
@@ -96,9 +63,8 @@ func TestParse(t *testing.T) {
Namespace: "test",
Name: "foo",
},
Mode: networking.ServerTLSSettings_MUTUAL,
TlsMinVersion: tlsV13,
CipherSuites: []string{"ECDHE-RSA-AES256-GCM-SHA384", "AES128-SHA"},
Mode: networking.ServerTLSSettings_MUTUAL,
CipherSuites: []string{"ECDHE-RSA-AES256-GCM-SHA384", "AES128-SHA"},
},
},
}
@@ -124,31 +90,6 @@ func TestApplyGateway(t *testing.T) {
config *Ingress
expect *networking.Gateway
}{
{
input: &networking.Gateway{
Servers: []*networking.Server{
{
Port: &networking.Port{
Protocol: "HTTP",
},
},
},
},
config: &Ingress{
DownstreamTLS: &DownstreamTLSConfig{
TlsMinVersion: tlsV10,
},
},
expect: &networking.Gateway{
Servers: []*networking.Server{
{
Port: &networking.Port{
Protocol: "HTTP",
},
},
},
},
},
{
input: &networking.Gateway{
Servers: []*networking.Server{
@@ -164,7 +105,7 @@ func TestApplyGateway(t *testing.T) {
},
config: &Ingress{
DownstreamTLS: &DownstreamTLSConfig{
TlsMinVersion: tlsV12,
CipherSuites: []string{"ECDHE-RSA-AES256-GCM-SHA384"},
},
},
expect: &networking.Gateway{
@@ -174,42 +115,8 @@ func TestApplyGateway(t *testing.T) {
Protocol: "HTTPS",
},
Tls: &networking.ServerTLSSettings{
Mode: networking.ServerTLSSettings_SIMPLE,
MinProtocolVersion: networking.ServerTLSSettings_TLSV1_2,
},
},
},
},
},
{
input: &networking.Gateway{
Servers: []*networking.Server{
{
Port: &networking.Port{
Protocol: "HTTPS",
},
Tls: &networking.ServerTLSSettings{
Mode: networking.ServerTLSSettings_SIMPLE,
},
},
},
},
config: &Ingress{
DownstreamTLS: &DownstreamTLSConfig{
TlsMaxVersion: tlsV13,
CipherSuites: []string{"ECDHE-RSA-AES256-GCM-SHA384"},
},
},
expect: &networking.Gateway{
Servers: []*networking.Server{
{
Port: &networking.Port{
Protocol: "HTTPS",
},
Tls: &networking.ServerTLSSettings{
Mode: networking.ServerTLSSettings_SIMPLE,
MaxProtocolVersion: networking.ServerTLSSettings_TLSV1_3,
CipherSuites: []string{"ECDHE-RSA-AES256-GCM-SHA384"},
Mode: networking.ServerTLSSettings_SIMPLE,
CipherSuites: []string{"ECDHE-RSA-AES256-GCM-SHA384"},
},
},
},
@@ -235,9 +142,8 @@ func TestApplyGateway(t *testing.T) {
Namespace: "foo",
Name: "bar",
},
Mode: networking.ServerTLSSettings_MUTUAL,
TlsMaxVersion: tlsV13,
CipherSuites: []string{"ECDHE-RSA-AES256-GCM-SHA384"},
Mode: networking.ServerTLSSettings_MUTUAL,
CipherSuites: []string{"ECDHE-RSA-AES256-GCM-SHA384"},
},
},
expect: &networking.Gateway{
@@ -247,10 +153,9 @@ func TestApplyGateway(t *testing.T) {
Protocol: "HTTPS",
},
Tls: &networking.ServerTLSSettings{
CredentialName: "kubernetes-ingress://cluster/foo/bar",
Mode: networking.ServerTLSSettings_MUTUAL,
MaxProtocolVersion: networking.ServerTLSSettings_TLSV1_3,
CipherSuites: []string{"ECDHE-RSA-AES256-GCM-SHA384"},
CredentialName: "kubernetes-ingress://cluster/foo/bar",
Mode: networking.ServerTLSSettings_MUTUAL,
CipherSuites: []string{"ECDHE-RSA-AES256-GCM-SHA384"},
},
},
},
@@ -276,9 +181,8 @@ func TestApplyGateway(t *testing.T) {
Namespace: "foo",
Name: "bar-cacert",
},
Mode: networking.ServerTLSSettings_MUTUAL,
TlsMaxVersion: tlsV13,
CipherSuites: []string{"ECDHE-RSA-AES256-GCM-SHA384"},
Mode: networking.ServerTLSSettings_MUTUAL,
CipherSuites: []string{"ECDHE-RSA-AES256-GCM-SHA384"},
},
},
expect: &networking.Gateway{
@@ -288,10 +192,9 @@ func TestApplyGateway(t *testing.T) {
Protocol: "HTTPS",
},
Tls: &networking.ServerTLSSettings{
CredentialName: "kubernetes-ingress://cluster/foo/bar",
Mode: networking.ServerTLSSettings_MUTUAL,
MaxProtocolVersion: networking.ServerTLSSettings_TLSV1_3,
CipherSuites: []string{"ECDHE-RSA-AES256-GCM-SHA384"},
CredentialName: "kubernetes-ingress://cluster/foo/bar",
Mode: networking.ServerTLSSettings_MUTUAL,
CipherSuites: []string{"ECDHE-RSA-AES256-GCM-SHA384"},
},
},
},
@@ -317,9 +220,8 @@ func TestApplyGateway(t *testing.T) {
Namespace: "bar",
Name: "foo",
},
Mode: networking.ServerTLSSettings_MUTUAL,
TlsMaxVersion: tlsV13,
CipherSuites: []string{"ECDHE-RSA-AES256-GCM-SHA384"},
Mode: networking.ServerTLSSettings_MUTUAL,
CipherSuites: []string{"ECDHE-RSA-AES256-GCM-SHA384"},
},
},
expect: &networking.Gateway{
@@ -329,10 +231,9 @@ func TestApplyGateway(t *testing.T) {
Protocol: "HTTPS",
},
Tls: &networking.ServerTLSSettings{
CredentialName: "kubernetes-ingress://cluster/foo/bar",
Mode: networking.ServerTLSSettings_SIMPLE,
MaxProtocolVersion: networking.ServerTLSSettings_TLSV1_3,
CipherSuites: []string{"ECDHE-RSA-AES256-GCM-SHA384"},
CredentialName: "kubernetes-ingress://cluster/foo/bar",
Mode: networking.ServerTLSSettings_SIMPLE,
CipherSuites: []string{"ECDHE-RSA-AES256-GCM-SHA384"},
},
},
},

View File

@@ -1,160 +0,0 @@
// Copyright (c) 2022 Alibaba Group Holding Ltd.
//
// 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 annotations
import (
"strings"
networking "istio.io/api/networking/v1alpha3"
. "github.com/alibaba/higress/pkg/ingress/log"
)
const (
// request
requestHeaderAdd = "request-header-control-add"
requestHeaderUpdate = "request-header-control-update"
requestHeaderRemove = "request-header-control-remove"
// response
responseHeaderAdd = "response-header-control-add"
responseHeaderUpdate = "response-header-control-update"
responseHeaderRemove = "response-header-control-remove"
)
var (
_ Parser = headerControl{}
_ RouteHandler = headerControl{}
)
type HeaderOperation struct {
Add map[string]string
Update map[string]string
Remove []string
}
// HeaderControlConfig enforces header operations on route level.
// Note: Canary route don't use header control applied on the normal route.
type HeaderControlConfig struct {
Request *HeaderOperation
Response *HeaderOperation
}
type headerControl struct{}
func (h headerControl) Parse(annotations Annotations, config *Ingress, _ *GlobalContext) error {
if !needHeaderControlConfig(annotations) {
return nil
}
config.HeaderControl = &HeaderControlConfig{}
var requestAdd map[string]string
var requestUpdate map[string]string
var requestRemove []string
if add, err := annotations.ParseStringForMSE(requestHeaderAdd); err == nil {
requestAdd = convertAddOrUpdate(add)
}
if update, err := annotations.ParseStringForMSE(requestHeaderUpdate); err == nil {
requestUpdate = convertAddOrUpdate(update)
}
if remove, err := annotations.ParseStringForMSE(requestHeaderRemove); err == nil {
requestRemove = splitBySeparator(remove, ",")
}
if len(requestAdd) > 0 || len(requestUpdate) > 0 || len(requestRemove) > 0 {
config.HeaderControl.Request = &HeaderOperation{
Add: requestAdd,
Update: requestUpdate,
Remove: requestRemove,
}
}
var responseAdd map[string]string
var responseUpdate map[string]string
var responseRemove []string
if add, err := annotations.ParseStringForMSE(responseHeaderAdd); err == nil {
responseAdd = convertAddOrUpdate(add)
}
if update, err := annotations.ParseStringForMSE(responseHeaderUpdate); err == nil {
responseUpdate = convertAddOrUpdate(update)
}
if remove, err := annotations.ParseStringForMSE(responseHeaderRemove); err == nil {
responseRemove = splitBySeparator(remove, ",")
}
if len(responseAdd) > 0 || len(responseUpdate) > 0 || len(responseRemove) > 0 {
config.HeaderControl.Response = &HeaderOperation{
Add: responseAdd,
Update: responseUpdate,
Remove: responseRemove,
}
}
return nil
}
func (h headerControl) ApplyRoute(route *networking.HTTPRoute, config *Ingress) {
headerControlConfig := config.HeaderControl
if headerControlConfig == nil {
return
}
headers := &networking.Headers{
Request: &networking.Headers_HeaderOperations{},
Response: &networking.Headers_HeaderOperations{},
}
if headerControlConfig.Request != nil {
headers.Request.Add = headerControlConfig.Request.Add
headers.Request.Set = headerControlConfig.Request.Update
headers.Request.Remove = headerControlConfig.Request.Remove
}
if headerControlConfig.Response != nil {
headers.Response.Add = headerControlConfig.Response.Add
headers.Response.Set = headerControlConfig.Response.Update
headers.Response.Remove = headerControlConfig.Response.Remove
}
route.Headers = headers
}
func needHeaderControlConfig(annotations Annotations) bool {
return annotations.HasMSE(requestHeaderAdd) ||
annotations.HasMSE(requestHeaderUpdate) ||
annotations.HasMSE(requestHeaderRemove) ||
annotations.HasMSE(responseHeaderAdd) ||
annotations.HasMSE(responseHeaderUpdate) ||
annotations.HasMSE(responseHeaderRemove)
}
func convertAddOrUpdate(headers string) map[string]string {
result := map[string]string{}
parts := strings.Split(headers, "\n")
for _, part := range parts {
part = strings.TrimSpace(part)
if part == "" {
continue
}
keyValue := strings.Fields(part)
if len(keyValue) != 2 {
IngressLog.Infof("Header format %s is invalid.", keyValue)
continue
}
key := strings.TrimSpace(keyValue[0])
value := strings.TrimSpace(keyValue[1])
result[key] = value
}
return result
}

View File

@@ -1,235 +0,0 @@
// Copyright (c) 2022 Alibaba Group Holding Ltd.
//
// 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 annotations
import (
"reflect"
"testing"
networking "istio.io/api/networking/v1alpha3"
)
func TestHeaderControlParse(t *testing.T) {
headerControl := &headerControl{}
inputCases := []struct {
input map[string]string
expect *HeaderControlConfig
}{
{},
{
input: map[string]string{
buildMSEAnnotationKey(requestHeaderAdd): "one 1",
buildMSEAnnotationKey(responseHeaderAdd): "A a",
},
expect: &HeaderControlConfig{
Request: &HeaderOperation{
Add: map[string]string{
"one": "1",
},
},
Response: &HeaderOperation{
Add: map[string]string{
"A": "a",
},
},
},
},
{
input: map[string]string{
buildMSEAnnotationKey(requestHeaderAdd): "one 1\n two 2\nthree 3 \n",
buildMSEAnnotationKey(requestHeaderUpdate): "two 2",
buildMSEAnnotationKey(requestHeaderRemove): "one, two,three\n",
buildMSEAnnotationKey(responseHeaderAdd): "A a\nB b\n",
buildMSEAnnotationKey(responseHeaderUpdate): "X x\nY y\n",
buildMSEAnnotationKey(responseHeaderRemove): "x",
},
expect: &HeaderControlConfig{
Request: &HeaderOperation{
Add: map[string]string{
"one": "1",
"two": "2",
"three": "3",
},
Update: map[string]string{
"two": "2",
},
Remove: []string{"one", "two", "three"},
},
Response: &HeaderOperation{
Add: map[string]string{
"A": "a",
"B": "b",
},
Update: map[string]string{
"X": "x",
"Y": "y",
},
Remove: []string{"x"},
},
},
},
}
for _, inputCase := range inputCases {
t.Run("", func(t *testing.T) {
config := &Ingress{}
_ = headerControl.Parse(inputCase.input, config, nil)
if !reflect.DeepEqual(inputCase.expect, config.HeaderControl) {
t.Fatal("Should be equal")
}
})
}
}
func TestHeaderControlApplyRoute(t *testing.T) {
headerControl := headerControl{}
inputCases := []struct {
config *Ingress
input *networking.HTTPRoute
expect *networking.HTTPRoute
}{
{
config: &Ingress{},
input: &networking.HTTPRoute{},
expect: &networking.HTTPRoute{},
},
{
config: &Ingress{
HeaderControl: &HeaderControlConfig{},
},
input: &networking.HTTPRoute{},
expect: &networking.HTTPRoute{
Headers: &networking.Headers{
Request: &networking.Headers_HeaderOperations{},
Response: &networking.Headers_HeaderOperations{},
},
},
},
{
config: &Ingress{
HeaderControl: &HeaderControlConfig{
Request: &HeaderOperation{
Add: map[string]string{
"one": "1",
"two": "2",
"three": "3",
},
Update: map[string]string{
"two": "2",
},
Remove: []string{"one", "two", "three"},
},
},
},
input: &networking.HTTPRoute{},
expect: &networking.HTTPRoute{
Headers: &networking.Headers{
Request: &networking.Headers_HeaderOperations{
Add: map[string]string{
"one": "1",
"two": "2",
"three": "3",
},
Set: map[string]string{
"two": "2",
},
Remove: []string{"one", "two", "three"},
},
Response: &networking.Headers_HeaderOperations{},
},
},
},
{
config: &Ingress{
HeaderControl: &HeaderControlConfig{
Response: &HeaderOperation{
Add: map[string]string{
"A": "a",
"B": "b",
},
Update: map[string]string{
"X": "x",
"Y": "y",
},
Remove: []string{"x"},
},
},
},
input: &networking.HTTPRoute{},
expect: &networking.HTTPRoute{
Headers: &networking.Headers{
Request: &networking.Headers_HeaderOperations{},
Response: &networking.Headers_HeaderOperations{
Add: map[string]string{
"A": "a",
"B": "b",
},
Set: map[string]string{
"X": "x",
"Y": "y",
},
Remove: []string{"x"},
},
},
},
},
{
config: &Ingress{
HeaderControl: &HeaderControlConfig{
Request: &HeaderOperation{
Update: map[string]string{
"two": "2",
},
Remove: []string{"one", "two", "three"},
},
Response: &HeaderOperation{
Add: map[string]string{
"A": "a",
"B": "b",
},
Remove: []string{"x"},
},
},
},
input: &networking.HTTPRoute{},
expect: &networking.HTTPRoute{
Headers: &networking.Headers{
Request: &networking.Headers_HeaderOperations{
Set: map[string]string{
"two": "2",
},
Remove: []string{"one", "two", "three"},
},
Response: &networking.Headers_HeaderOperations{
Add: map[string]string{
"A": "a",
"B": "b",
},
Remove: []string{"x"},
},
},
},
},
}
for _, inputCase := range inputCases {
t.Run("", func(t *testing.T) {
headerControl.ApplyRoute(inputCase.input, inputCase.config)
if !reflect.DeepEqual(inputCase.input, inputCase.expect) {
t.Fatal("Should be equal")
}
})
}
}

View File

@@ -20,10 +20,7 @@ import (
)
const (
domainWhitelist = "domain-whitelist-source-range"
domainBlacklist = "domain-blacklist-source-range"
whitelist = "whitelist-source-range"
blacklist = "blacklist-source-range"
whitelist = "whitelist-source-range"
)
var (
@@ -37,8 +34,7 @@ type IPAccessControl struct {
}
type IPAccessControlConfig struct {
Domain *IPAccessControl
Route *IPAccessControl
Route *IPAccessControl
}
type ipAccessControl struct{}
@@ -53,40 +49,14 @@ func (i ipAccessControl) Parse(annotations Annotations, config *Ingress, _ *Glob
config.IPAccessControl = ipConfig
}()
var domain *IPAccessControl
rawWhitelist, err := annotations.ParseStringForMSE(domainWhitelist)
if err == nil {
domain = &IPAccessControl{
isWhite: true,
remoteIp: splitStringWithSpaceTrim(rawWhitelist),
}
} else {
if rawBlacklist, err := annotations.ParseStringForMSE(domainBlacklist); err == nil {
domain = &IPAccessControl{
isWhite: false,
remoteIp: splitStringWithSpaceTrim(rawBlacklist),
}
}
}
if domain != nil {
ipConfig.Domain = domain
}
var route *IPAccessControl
rawWhitelist, err = annotations.ParseStringASAP(whitelist)
if err == nil {
if rawWhitelist, err := annotations.ParseStringASAP(whitelist); err == nil {
route = &IPAccessControl{
isWhite: true,
remoteIp: splitStringWithSpaceTrim(rawWhitelist),
}
} else {
if rawBlacklist, err := annotations.ParseStringForMSE(blacklist); err == nil {
route = &IPAccessControl{
isWhite: false,
remoteIp: splitStringWithSpaceTrim(rawBlacklist),
}
}
}
if route != nil {
ipConfig.Route = route
}
@@ -94,25 +64,8 @@ func (i ipAccessControl) Parse(annotations Annotations, config *Ingress, _ *Glob
return nil
}
func (i ipAccessControl) ApplyVirtualServiceHandler(virtualService *networking.VirtualService, config *Ingress) {
ac := config.IPAccessControl
if ac == nil || ac.Domain == nil {
return
}
filter := &networking.IPAccessControl{}
if ac.Domain.isWhite {
filter.RemoteIpBlocks = ac.Domain.remoteIp
} else {
filter.NotRemoteIpBlocks = ac.Domain.remoteIp
}
virtualService.HostHTTPFilters = append(virtualService.HostHTTPFilters, &networking.HTTPFilter{
Name: mseingress.IPAccessControl,
Filter: &networking.HTTPFilter_IpAccessControl{
IpAccessControl: filter,
},
})
func (i ipAccessControl) ApplyVirtualServiceHandler(_ *networking.VirtualService, _ *Ingress) {
// DO NOTHING
}
func (i ipAccessControl) ApplyRoute(route *networking.HTTPRoute, config *Ingress) {
@@ -137,8 +90,5 @@ func (i ipAccessControl) ApplyRoute(route *networking.HTTPRoute, config *Ingress
}
func needIPAccessControlConfig(annotations Annotations) bool {
return annotations.HasMSE(domainWhitelist) ||
annotations.HasMSE(domainBlacklist) ||
annotations.HasASAP(whitelist) ||
annotations.HasMSE(blacklist)
return annotations.HasASAP(whitelist)
}

View File

@@ -31,8 +31,7 @@ func TestIPAccessControlParse(t *testing.T) {
{},
{
input: map[string]string{
DefaultAnnotationsPrefix + "/" + whitelist: "1.1.1.1",
MSEAnnotationsPrefix + "/" + blacklist: "2.2.2.2",
buildNginxAnnotationKey(whitelist): "1.1.1.1",
},
expect: &IPAccessControlConfig{
Route: &IPAccessControl{
@@ -41,44 +40,6 @@ func TestIPAccessControlParse(t *testing.T) {
},
},
},
{
input: map[string]string{
MSEAnnotationsPrefix + "/" + blacklist: "2.2.2.2",
},
expect: &IPAccessControlConfig{
Route: &IPAccessControl{
isWhite: false,
remoteIp: []string{"2.2.2.2"},
},
},
},
{
input: map[string]string{
MSEAnnotationsPrefix + "/" + domainWhitelist: "1.1.1.1",
},
expect: &IPAccessControlConfig{
Domain: &IPAccessControl{
isWhite: true,
remoteIp: []string{"1.1.1.1"},
},
},
},
{
input: map[string]string{
MSEAnnotationsPrefix + "/" + whitelist: "1.1.1.1, 3.3.3.3",
MSEAnnotationsPrefix + "/" + domainBlacklist: "2.2.2.2",
},
expect: &IPAccessControlConfig{
Route: &IPAccessControl{
isWhite: true,
remoteIp: []string{"1.1.1.1", "3.3.3.3"},
},
Domain: &IPAccessControl{
isWhite: false,
remoteIp: []string{"2.2.2.2"},
},
},
},
}
for _, testCase := range testCases {
@@ -92,80 +53,6 @@ func TestIPAccessControlParse(t *testing.T) {
}
}
func TestIpAccessControl_ApplyVirtualServiceHandler(t *testing.T) {
parser := ipAccessControl{}
testCases := []struct {
config *Ingress
input *networking.VirtualService
expect *networking.HTTPFilter
}{
{
config: &Ingress{},
input: &networking.VirtualService{},
expect: nil,
},
{
config: &Ingress{
IPAccessControl: &IPAccessControlConfig{
Domain: &IPAccessControl{
isWhite: true,
remoteIp: []string{"1.1.1.1"},
},
},
},
input: &networking.VirtualService{},
expect: &networking.HTTPFilter{
Name: "ip-access-control",
Disable: false,
Filter: &networking.HTTPFilter_IpAccessControl{
IpAccessControl: &networking.IPAccessControl{
RemoteIpBlocks: []string{"1.1.1.1"},
},
},
},
},
{
config: &Ingress{
IPAccessControl: &IPAccessControlConfig{
Domain: &IPAccessControl{
isWhite: false,
remoteIp: []string{"2.2.2.2"},
},
},
},
input: &networking.VirtualService{},
expect: &networking.HTTPFilter{
Name: "ip-access-control",
Disable: false,
Filter: &networking.HTTPFilter_IpAccessControl{
IpAccessControl: &networking.IPAccessControl{
NotRemoteIpBlocks: []string{"2.2.2.2"},
},
},
},
},
}
for _, testCase := range testCases {
t.Run("", func(t *testing.T) {
parser.ApplyVirtualServiceHandler(testCase.input, testCase.config)
if testCase.config.IPAccessControl == nil {
if len(testCase.input.HostHTTPFilters) != 0 {
t.Fatalf("Should be empty")
}
} else {
if len(testCase.input.HostHTTPFilters) == 0 {
t.Fatalf("Should be not empty")
}
if !reflect.DeepEqual(testCase.expect, testCase.input.HostHTTPFilters[0]) {
t.Fatalf("Should be equal")
}
}
})
}
}
func TestIpAccessControl_ApplyRoute(t *testing.T) {
parser := ipAccessControl{}
@@ -199,26 +86,6 @@ func TestIpAccessControl_ApplyRoute(t *testing.T) {
},
},
},
{
config: &Ingress{
IPAccessControl: &IPAccessControlConfig{
Route: &IPAccessControl{
isWhite: false,
remoteIp: []string{"2.2.2.2"},
},
},
},
input: &networking.HTTPRoute{},
expect: &networking.HTTPFilter{
Name: "ip-access-control",
Disable: false,
Filter: &networking.HTTPFilter_IpAccessControl{
IpAccessControl: &networking.IPAccessControl{
NotRemoteIpBlocks: []string{"2.2.2.2"},
},
},
},
},
}
for _, testCase := range testCases {

View File

@@ -34,7 +34,6 @@ const (
sessionCookiePath = "session-cookie-path"
sessionCookieMaxAge = "session-cookie-max-age"
sessionCookieExpires = "session-cookie-expires"
warmup = "warmup"
varIndicator = "$"
headerIndicator = "$http_"
@@ -68,7 +67,6 @@ type consistentHashByCookie struct {
type LoadBalanceConfig struct {
simple networking.LoadBalancerSettings_SimpleLB
warmup *types.Duration
other *consistentHashByOther
cookie *consistentHashByCookie
}
@@ -133,12 +131,6 @@ func (l loadBalance) Parse(annotations Annotations, config *Ingress, _ *GlobalCo
lb = strings.ToUpper(lb)
loadBalanceConfig.simple = networking.LoadBalancerSettings_SimpleLB(networking.LoadBalancerSettings_SimpleLB_value[lb])
}
if warmup, err := annotations.ParseIntForMSE(warmup); err == nil && warmup != 0 {
loadBalanceConfig.warmup = &types.Duration{
Seconds: int64(warmup),
}
}
}
return nil
@@ -190,7 +182,6 @@ func (l loadBalance) ApplyTrafficPolicy(trafficPolicy *networking.TrafficPolicy_
Simple: loadBalanceConfig.simple,
},
}
trafficPolicy.LoadBalancer.WarmupDurationSecs = loadBalanceConfig.warmup
}
}
@@ -206,7 +197,6 @@ func isOtherAffinity(annotations Annotations) bool {
func needLoadBalanceConfig(annotations Annotations) bool {
return annotations.HasASAP(loadBalanceAnnotation) ||
annotations.HasMSE(warmup) ||
isCookieAffinity(annotations) ||
isOtherAffinity(annotations)
}

View File

@@ -127,41 +127,6 @@ func TestLoadBalanceParse(t *testing.T) {
},
},
},
{
input: map[string]string{
buildMSEAnnotationKey(warmup): "100",
},
expect: &LoadBalanceConfig{
simple: networking.LoadBalancerSettings_ROUND_ROBIN,
warmup: &types.Duration{
Seconds: 100,
},
},
},
{
input: map[string]string{
buildNginxAnnotationKey(loadBalanceAnnotation): "LEAST_CONN",
buildMSEAnnotationKey(warmup): "100",
},
expect: &LoadBalanceConfig{
simple: networking.LoadBalancerSettings_LEAST_CONN,
warmup: &types.Duration{
Seconds: 100,
},
},
},
{
input: map[string]string{
buildNginxAnnotationKey(loadBalanceAnnotation): "random",
buildMSEAnnotationKey(warmup): "100",
},
expect: &LoadBalanceConfig{
simple: networking.LoadBalancerSettings_RANDOM,
warmup: &types.Duration{
Seconds: 100,
},
},
},
}
for _, inputCase := range inputCases {
@@ -260,27 +225,6 @@ func TestLoadBalanceApplyTrafficPolicy(t *testing.T) {
},
},
},
{
config: &Ingress{
LoadBalance: &LoadBalanceConfig{
simple: networking.LoadBalancerSettings_ROUND_ROBIN,
warmup: &types.Duration{
Seconds: 100,
},
},
},
input: &networking.TrafficPolicy_PortTrafficPolicy{},
expect: &networking.TrafficPolicy_PortTrafficPolicy{
LoadBalancer: &networking.LoadBalancerSettings{
LbPolicy: &networking.LoadBalancerSettings_Simple{
Simple: networking.LoadBalancerSettings_ROUND_ROBIN,
},
WarmupDurationSecs: &types.Duration{
Seconds: 100,
},
},
},
},
}
for _, inputCase := range inputCases {

View File

@@ -1,110 +0,0 @@
// Copyright (c) 2022 Alibaba Group Holding Ltd.
//
// 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 annotations
import (
"github.com/gogo/protobuf/types"
networking "istio.io/api/networking/v1alpha3"
"istio.io/istio/pilot/pkg/networking/core/v1alpha3/mseingress"
)
const (
limitRPM = "route-limit-rpm"
limitRPS = "route-limit-rps"
limitBurstMultiplier = "route-limit-burst-multiplier"
defaultBurstMultiplier = 5
defaultStatusCode = 503
)
var (
_ Parser = localRateLimit{}
_ RouteHandler = localRateLimit{}
second = &types.Duration{
Seconds: 1,
}
minute = &types.Duration{
Seconds: 60,
}
)
type localRateLimitConfig struct {
TokensPerFill uint32
MaxTokens uint32
FillInterval *types.Duration
}
type localRateLimit struct{}
func (l localRateLimit) Parse(annotations Annotations, config *Ingress, _ *GlobalContext) error {
if !needLocalRateLimitConfig(annotations) {
return nil
}
var local *localRateLimitConfig
defer func() {
config.localRateLimit = local
}()
var multiplier uint32 = defaultBurstMultiplier
if m, err := annotations.ParseUint32ForMSE(limitBurstMultiplier); err == nil {
multiplier = m
}
if rpm, err := annotations.ParseUint32ForMSE(limitRPM); err == nil {
local = &localRateLimitConfig{
MaxTokens: rpm * multiplier,
TokensPerFill: rpm,
FillInterval: minute,
}
} else if rps, err := annotations.ParseUint32ForMSE(limitRPS); err == nil {
local = &localRateLimitConfig{
MaxTokens: rps * multiplier,
TokensPerFill: rps,
FillInterval: second,
}
}
return nil
}
func (l localRateLimit) ApplyRoute(route *networking.HTTPRoute, config *Ingress) {
localRateLimitConfig := config.localRateLimit
if localRateLimitConfig == nil {
return
}
route.RouteHTTPFilters = append(route.RouteHTTPFilters, &networking.HTTPFilter{
Name: mseingress.LocalRateLimit,
Filter: &networking.HTTPFilter_LocalRateLimit{
LocalRateLimit: &networking.LocalRateLimit{
TokenBucket: &networking.TokenBucket{
MaxTokens: localRateLimitConfig.MaxTokens,
TokensPefFill: localRateLimitConfig.TokensPerFill,
FillInterval: localRateLimitConfig.FillInterval,
},
StatusCode: defaultStatusCode,
},
},
})
}
func needLocalRateLimitConfig(annotations Annotations) bool {
return annotations.HasMSE(limitRPM) ||
annotations.HasMSE(limitRPS)
}

View File

@@ -1,127 +0,0 @@
// Copyright (c) 2022 Alibaba Group Holding Ltd.
//
// 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 annotations
import (
"reflect"
"testing"
networking "istio.io/api/networking/v1alpha3"
"istio.io/istio/pilot/pkg/networking/core/v1alpha3/mseingress"
)
func TestLocalRateLimitParse(t *testing.T) {
localRateLimit := localRateLimit{}
inputCases := []struct {
input map[string]string
expect *localRateLimitConfig
}{
{},
{
input: map[string]string{
buildMSEAnnotationKey(limitRPM): "2",
},
expect: &localRateLimitConfig{
MaxTokens: 10,
TokensPerFill: 2,
FillInterval: minute,
},
},
{
input: map[string]string{
buildMSEAnnotationKey(limitRPM): "2",
buildMSEAnnotationKey(limitRPS): "3",
buildMSEAnnotationKey(limitBurstMultiplier): "10",
},
expect: &localRateLimitConfig{
MaxTokens: 20,
TokensPerFill: 2,
FillInterval: minute,
},
},
{
input: map[string]string{
buildMSEAnnotationKey(limitRPS): "3",
buildMSEAnnotationKey(limitBurstMultiplier): "10",
},
expect: &localRateLimitConfig{
MaxTokens: 30,
TokensPerFill: 3,
FillInterval: second,
},
},
}
for _, inputCase := range inputCases {
t.Run("", func(t *testing.T) {
config := &Ingress{}
_ = localRateLimit.Parse(inputCase.input, config, nil)
if !reflect.DeepEqual(inputCase.expect, config.localRateLimit) {
t.Fatal("Should be equal")
}
})
}
}
func TestLocalRateLimitApplyRoute(t *testing.T) {
localRateLimit := localRateLimit{}
inputCases := []struct {
config *Ingress
input *networking.HTTPRoute
expect *networking.HTTPRoute
}{
{
config: &Ingress{},
input: &networking.HTTPRoute{},
expect: &networking.HTTPRoute{},
},
{
config: &Ingress{
localRateLimit: &localRateLimitConfig{
MaxTokens: 60,
TokensPerFill: 20,
FillInterval: second,
},
},
input: &networking.HTTPRoute{},
expect: &networking.HTTPRoute{
RouteHTTPFilters: []*networking.HTTPFilter{
{
Name: mseingress.LocalRateLimit,
Filter: &networking.HTTPFilter_LocalRateLimit{
LocalRateLimit: &networking.LocalRateLimit{
TokenBucket: &networking.TokenBucket{
MaxTokens: 60,
TokensPefFill: 20,
FillInterval: second,
},
StatusCode: defaultStatusCode,
},
},
},
},
},
},
}
for _, inputCase := range inputCases {
t.Run("", func(t *testing.T) {
localRateLimit.ApplyRoute(inputCase.input, inputCase.config)
if !reflect.DeepEqual(inputCase.input, inputCase.expect) {
t.Fatal("Should be equal")
}
})
}
}

View File

@@ -24,8 +24,8 @@ const (
// DefaultAnnotationsPrefix defines the common prefix used in the nginx ingress controller
DefaultAnnotationsPrefix = "nginx.ingress.kubernetes.io"
// MSEAnnotationsPrefix defines the common prefix used in the mse ingress controller
MSEAnnotationsPrefix = "mse.ingress.kubernetes.io"
// HigressAnnotationsPrefix defines the common prefix used in the higress ingress controller
HigressAnnotationsPrefix = "higress.io"
)
var (
@@ -67,12 +67,12 @@ func (a Annotations) ParseBool(key string) (bool, error) {
return false, ErrMissingAnnotations
}
func (a Annotations) ParseBoolForMSE(key string) (bool, error) {
func (a Annotations) ParseBoolForHigress(key string) (bool, error) {
if len(a) == 0 {
return false, ErrMissingAnnotations
}
val, ok := a[buildMSEAnnotationKey(key)]
val, ok := a[buildHigressAnnotationKey(key)]
if ok {
b, err := strconv.ParseBool(val)
if err != nil {
@@ -88,7 +88,7 @@ func (a Annotations) ParseBoolASAP(key string) (bool, error) {
if result, err := a.ParseBool(key); err == nil {
return result, nil
}
return a.ParseBoolForMSE(key)
return a.ParseBoolForHigress(key)
}
func (a Annotations) ParseString(key string) (string, error) {
@@ -108,12 +108,12 @@ func (a Annotations) ParseString(key string) (string, error) {
return "", ErrMissingAnnotations
}
func (a Annotations) ParseStringForMSE(key string) (string, error) {
func (a Annotations) ParseStringForHigress(key string) (string, error) {
if len(a) == 0 {
return "", ErrMissingAnnotations
}
val, ok := a[buildMSEAnnotationKey(key)]
val, ok := a[buildHigressAnnotationKey(key)]
if ok {
s := normalizeString(val)
if s == "" {
@@ -126,12 +126,12 @@ func (a Annotations) ParseStringForMSE(key string) (string, error) {
}
// ParseStringASAP will first extra config from nginx annotation, then will
// try to extra config from mse annotation if the first step fails.
// try to extra config from Higress annotation if the first step fails.
func (a Annotations) ParseStringASAP(key string) (string, error) {
if result, err := a.ParseString(key); err == nil {
return result, nil
}
return a.ParseStringForMSE(key)
return a.ParseStringForHigress(key)
}
func (a Annotations) ParseInt(key string) (int, error) {
@@ -150,12 +150,12 @@ func (a Annotations) ParseInt(key string) (int, error) {
return 0, ErrMissingAnnotations
}
func (a Annotations) ParseIntForMSE(key string) (int, error) {
func (a Annotations) ParseIntForHigress(key string) (int, error) {
if len(a) == 0 {
return 0, ErrMissingAnnotations
}
val, ok := a[buildMSEAnnotationKey(key)]
val, ok := a[buildHigressAnnotationKey(key)]
if ok {
i, err := strconv.Atoi(val)
if err != nil {
@@ -182,12 +182,12 @@ func (a Annotations) ParseInt32(key string) (int32, error) {
return 0, ErrMissingAnnotations
}
func (a Annotations) ParseInt32ForMSE(key string) (int32, error) {
func (a Annotations) ParseInt32ForHigress(key string) (int32, error) {
if len(a) == 0 {
return 0, ErrMissingAnnotations
}
val, ok := a[buildMSEAnnotationKey(key)]
val, ok := a[buildHigressAnnotationKey(key)]
if ok {
i, err := strconv.ParseInt(val, 10, 32)
if err != nil {
@@ -198,12 +198,12 @@ func (a Annotations) ParseInt32ForMSE(key string) (int32, error) {
return 0, ErrMissingAnnotations
}
func (a Annotations) ParseUint32ForMSE(key string) (uint32, error) {
func (a Annotations) ParseUint32ForHigress(key string) (uint32, error) {
if len(a) == 0 {
return 0, ErrMissingAnnotations
}
val, ok := a[buildMSEAnnotationKey(key)]
val, ok := a[buildHigressAnnotationKey(key)]
if ok {
i, err := strconv.ParseUint(val, 10, 32)
if err != nil {
@@ -218,14 +218,14 @@ func (a Annotations) ParseIntASAP(key string) (int, error) {
if result, err := a.ParseInt(key); err == nil {
return result, nil
}
return a.ParseIntForMSE(key)
return a.ParseIntForHigress(key)
}
func (a Annotations) ParseInt32ASAP(key string) (int32, error) {
if result, err := a.ParseInt32(key); err == nil {
return result, nil
}
return a.ParseInt32ForMSE(key)
return a.ParseInt32ForHigress(key)
}
func (a Annotations) Has(key string) bool {
@@ -237,12 +237,12 @@ func (a Annotations) Has(key string) bool {
return exist
}
func (a Annotations) HasMSE(key string) bool {
func (a Annotations) HasHigress(key string) bool {
if len(a) == 0 {
return false
}
_, exist := a[buildMSEAnnotationKey(key)]
_, exist := a[buildHigressAnnotationKey(key)]
return exist
}
@@ -250,15 +250,15 @@ func (a Annotations) HasASAP(key string) bool {
if a.Has(key) {
return true
}
return a.HasMSE(key)
return a.HasHigress(key)
}
func buildNginxAnnotationKey(key string) string {
return DefaultAnnotationsPrefix + "/" + key
}
func buildMSEAnnotationKey(key string) string {
return MSEAnnotationsPrefix + "/" + key
func buildHigressAnnotationKey(key string) string {
return HigressAnnotationsPrefix + "/" + key
}
func normalizeString(input string) string {

View File

@@ -1,62 +0,0 @@
// Copyright (c) 2022 Alibaba Group Holding Ltd.
//
// 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 annotations
import (
"github.com/gogo/protobuf/types"
networking "istio.io/api/networking/v1alpha3"
)
const timeoutAnnotation = "timeout"
var (
_ Parser = timeout{}
_ RouteHandler = timeout{}
)
type TimeoutConfig struct {
time *types.Duration
}
type timeout struct{}
func (t timeout) Parse(annotations Annotations, config *Ingress, _ *GlobalContext) error {
if !needTimeoutConfig(annotations) {
return nil
}
if time, err := annotations.ParseIntForMSE(timeoutAnnotation); err == nil {
config.Timeout = &TimeoutConfig{
time: &types.Duration{
Seconds: int64(time),
},
}
}
return nil
}
func (t timeout) ApplyRoute(route *networking.HTTPRoute, config *Ingress) {
timeout := config.Timeout
if timeout == nil || timeout.time == nil || timeout.time.Seconds == 0 {
return
}
route.Timeout = timeout.time
}
func needTimeoutConfig(annotations Annotations) bool {
return annotations.HasMSE(timeoutAnnotation)
}

View File

@@ -1,121 +0,0 @@
// Copyright (c) 2022 Alibaba Group Holding Ltd.
//
// 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 annotations
import (
"reflect"
"testing"
"github.com/gogo/protobuf/types"
networking "istio.io/api/networking/v1alpha3"
)
func TestTimeoutParse(t *testing.T) {
timeout := timeout{}
inputCases := []struct {
input map[string]string
expect *TimeoutConfig
}{
{},
{
input: map[string]string{
MSEAnnotationsPrefix + "/" + timeoutAnnotation: "",
},
},
{
input: map[string]string{
MSEAnnotationsPrefix + "/" + timeoutAnnotation: "0",
},
expect: &TimeoutConfig{
time: &types.Duration{},
},
},
{
input: map[string]string{
MSEAnnotationsPrefix + "/" + timeoutAnnotation: "10",
},
expect: &TimeoutConfig{
time: &types.Duration{
Seconds: 10,
},
},
},
}
for _, c := range inputCases {
t.Run("", func(t *testing.T) {
config := &Ingress{}
_ = timeout.Parse(c.input, config, nil)
if !reflect.DeepEqual(c.expect, config.Timeout) {
t.Fatalf("Should be equal.")
}
})
}
}
func TestTimeoutApplyRoute(t *testing.T) {
timeout := timeout{}
inputCases := []struct {
config *Ingress
input *networking.HTTPRoute
expect *networking.HTTPRoute
}{
{
config: &Ingress{},
input: &networking.HTTPRoute{},
expect: &networking.HTTPRoute{},
},
{
config: &Ingress{
Timeout: &TimeoutConfig{},
},
input: &networking.HTTPRoute{},
expect: &networking.HTTPRoute{},
},
{
config: &Ingress{
Timeout: &TimeoutConfig{
time: &types.Duration{},
},
},
input: &networking.HTTPRoute{},
expect: &networking.HTTPRoute{},
},
{
config: &Ingress{
Timeout: &TimeoutConfig{
time: &types.Duration{
Seconds: 10,
},
},
},
input: &networking.HTTPRoute{},
expect: &networking.HTTPRoute{
Timeout: &types.Duration{
Seconds: 10,
},
},
},
}
for _, inputCase := range inputCases {
t.Run("", func(t *testing.T) {
timeout.ApplyRoute(inputCase.input, inputCase.config)
if !reflect.DeepEqual(inputCase.input, inputCase.expect) {
t.Fatalf("Should be equal")
}
})
}
}

View File

@@ -497,8 +497,6 @@ func (c *controller) ConvertHTTPRoute(convertOptions *common.ConvertOptions, wra
WrapperConfig: wrapper,
}
convertOptions.VirtualServices[rule.Host] = wrapperVS
} else {
wrapperVS.WrapperConfig.AnnotationsConfig.MergeHostIPAccessControlIfNotExist(wrapper.AnnotationsConfig.IPAccessControl)
}
// Record the latest app root for per host.

View File

@@ -492,8 +492,6 @@ func (c *controller) ConvertHTTPRoute(convertOptions *common.ConvertOptions, wra
WrapperConfig: wrapper,
}
convertOptions.VirtualServices[rule.Host] = wrapperVS
} else {
wrapperVS.WrapperConfig.AnnotationsConfig.MergeHostIPAccessControlIfNotExist(wrapper.AnnotationsConfig.IPAccessControl)
}
// Record the latest app root for per host.