mirror of
https://github.com/alibaba/higress.git
synced 2026-03-10 11:40:49 +08:00
Update higress ingress annotation (#49)
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,5 +1,6 @@
|
||||
external
|
||||
out
|
||||
*.out
|
||||
*.tgz
|
||||
*.wasm
|
||||
.idea/
|
||||
|
||||
@@ -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"},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -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{
|
||||
|
||||
@@ -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{
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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{
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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"},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -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")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -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 {
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user