Skip to content

Commit

Permalink
feat: support regex in path
Browse files Browse the repository at this point in the history
Signed-off-by: lxm <lxm.xupt@gmail.com>
  • Loading branch information
lxm committed Dec 1, 2021
1 parent f470867 commit ebd0747
Show file tree
Hide file tree
Showing 6 changed files with 146 additions and 60 deletions.
8 changes: 4 additions & 4 deletions pkg/kube/translation/annotations/cors.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,10 @@ import (
)

const (
_enableCors = "k8s.apisix.apache.org/enable-cors"
_corsAllowOrigin = "k8s.apisix.apache.org/cors-allow-origin"
_corsAllowHeaders = "k8s.apisix.apache.org/cors-allow-headers"
_corsAllowMethods = "k8s.apisix.apache.org/cors-allow-methods"
_enableCors = AnnotationsPrefix + "enable-cors"
_corsAllowOrigin = AnnotationsPrefix + "cors-allow-origin"
_corsAllowHeaders = AnnotationsPrefix + "cors-allow-headers"
_corsAllowMethods = AnnotationsPrefix + "cors-allow-methods"
)

type cors struct{}
Expand Down
4 changes: 2 additions & 2 deletions pkg/kube/translation/annotations/iprestriction.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ import (
)

const (
_allowlistSourceRange = "k8s.apisix.apache.org/allowlist-source-range"
_blocklistSourceRange = "k8s.apisix.apache.org/blocklist-source-range"
_allowlistSourceRange = AnnotationsPrefix + "allowlist-source-range"
_blocklistSourceRange = AnnotationsPrefix + "blocklist-source-range"
)

type ipRestriction struct{}
Expand Down
2 changes: 1 addition & 1 deletion pkg/kube/translation/annotations/redirect.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import (
)

const (
_httpToHttps = "k8s.apisix.apache.org/http-to-https"
_httpToHttps = AnnotationsPrefix + "http-to-https"
)

type redirect struct{}
Expand Down
6 changes: 3 additions & 3 deletions pkg/kube/translation/annotations/rewrite.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,9 @@ import (
)

const (
_rewriteTarget = "k8s.apisix.apache.org/rewrite-target"
_rewriteTargetRegex = "k8s.apisix.apache.org/rewrite-target-regex"
_rewriteTargetRegexTemplate = "k8s.apisix.apache.org/rewrite-target-regex-template"
_rewriteTarget = AnnotationsPrefix + "rewrite-target"
_rewriteTargetRegex = AnnotationsPrefix + "rewrite-target-regex"
_rewriteTargetRegexTemplate = AnnotationsPrefix + "rewrite-target-regex-template"
)

type rewrite struct{}
Expand Down
4 changes: 4 additions & 0 deletions pkg/kube/translation/annotations/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@ import (
"strings"
)

const (
AnnotationsPrefix = "k8s.apisix.apache.org/"
)

// Extractor encapsulates some auxiliary methods to extract annotations.
type Extractor interface {
// GetStringAnnotation returns the string value of the target annotation.
Expand Down
182 changes: 132 additions & 50 deletions pkg/kube/translation/ingress.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,16 +29,23 @@ import (

"github.com/apache/apisix-ingress-controller/pkg/id"
apisixv12 "github.com/apache/apisix-ingress-controller/pkg/kube/apisix/apis/config/v1"
configv2alpha1 "github.com/apache/apisix-ingress-controller/pkg/kube/apisix/apis/config/v2alpha1"
"github.com/apache/apisix-ingress-controller/pkg/kube/translation/annotations"
"github.com/apache/apisix-ingress-controller/pkg/log"
apisixv1 "github.com/apache/apisix-ingress-controller/pkg/types/apisix/v1"
)

const (
_regexPriority = 100
)

func (t *translator) translateIngressV1(ing *networkingv1.Ingress) (*TranslateContext, error) {
ctx := &TranslateContext{
upstreamMap: make(map[string]struct{}),
}
plugins := t.translateAnnotations(ing.Annotations)

annoExtractor := annotations.NewExtractor(ing.Annotations)
useRegex := annoExtractor.GetBoolAnnotation(annotations.AnnotationsPrefix + "use-regex")
// add https
for _, tls := range ing.Spec.TLS {
apisixTls := apisixv12.ApisixTls{
Expand Down Expand Up @@ -87,29 +94,52 @@ func (t *translator) translateIngressV1(ing *networkingv1.Ingress) (*TranslateCo
ctx.addUpstream(ups)
}
uris := []string{pathRule.Path}
if pathRule.PathType != nil && *pathRule.PathType == networkingv1.PathTypePrefix {
// As per the specification of Ingress path matching rule:
// if the last element of the path is a substring of the
// last element in request path, it is not a match, e.g. /foo/bar
// matches /foo/bar/baz, but does not match /foo/barbaz.
// While in APISIX, /foo/bar matches both /foo/bar/baz and
// /foo/barbaz.
// In order to be conformant with Ingress specification, here
// we create two paths here, the first is the path itself
// (exact match), the other is path + "/*" (prefix match).
prefix := pathRule.Path
if strings.HasSuffix(prefix, "/") {
prefix += "*"
} else {
prefix += "/*"
var nginxVars []configv2alpha1.ApisixRouteHTTPMatchExpr
if pathRule.PathType != nil {
if *pathRule.PathType == networkingv1.PathTypePrefix {
// As per the specification of Ingress path matching rule:
// if the last element of the path is a substring of the
// last element in request path, it is not a match, e.g. /foo/bar
// matches /foo/bar/baz, but does not match /foo/barbaz.
// While in APISIX, /foo/bar matches both /foo/bar/baz and
// /foo/barbaz.
// In order to be conformant with Ingress specification, here
// we create two paths here, the first is the path itself
// (exact match), the other is path + "/*" (prefix match).
prefix := pathRule.Path
if strings.HasSuffix(prefix, "/") {
prefix += "*"
} else {
prefix += "/*"
}
uris = append(uris, prefix, pathRule.Path)
} else if *pathRule.PathType == networkingv1.PathTypeExact {
uris = append(uris, pathRule.Path)
} else if *pathRule.PathType == networkingv1.PathTypeImplementationSpecific && useRegex {
nginxVars = append(nginxVars, configv2alpha1.ApisixRouteHTTPMatchExpr{
Subject: configv2alpha1.ApisixRouteHTTPMatchExprSubject{
Scope: configv2alpha1.ScopePath,
},
Op: configv2alpha1.OpRegexMatch,
Value: &pathRule.Path,
})
}
uris = append(uris, prefix)
} else {
uris = append(uris, pathRule.Path)
}
route := apisixv1.NewDefaultRoute()
route.Name = composeIngressRouteName(rule.Host, pathRule.Path)
route.ID = id.GenID(route.Name)
route.Host = rule.Host
route.Uris = uris
if len(nginxVars) > 0 {
routeVars, err := t.translateRouteMatchExprs(nginxVars)
if err != nil {
return nil, err
}
route.Vars = routeVars
route.Priority = _regexPriority
}
if len(plugins) > 0 {
route.Plugins = *(plugins.DeepCopy())
}
Expand All @@ -127,6 +157,8 @@ func (t *translator) translateIngressV1beta1(ing *networkingv1beta1.Ingress) (*T
upstreamMap: make(map[string]struct{}),
}
plugins := t.translateAnnotations(ing.Annotations)
annoExtractor := annotations.NewExtractor(ing.Annotations)
useRegex := annoExtractor.GetBoolAnnotation(annotations.AnnotationsPrefix + "use-regex")
// add https
for _, tls := range ing.Spec.TLS {
apisixTls := apisixv12.ApisixTls{
Expand Down Expand Up @@ -174,30 +206,54 @@ func (t *translator) translateIngressV1beta1(ing *networkingv1beta1.Ingress) (*T
}
ctx.addUpstream(ups)
}
uris := []string{pathRule.Path}
if pathRule.PathType != nil && *pathRule.PathType == networkingv1beta1.PathTypePrefix {
// As per the specification of Ingress path matching rule:
// if the last element of the path is a substring of the
// last element in request path, it is not a match, e.g. /foo/bar
// matches /foo/bar/baz, but does not match /foo/barbaz.
// While in APISIX, /foo/bar matches both /foo/bar/baz and
// /foo/barbaz.
// In order to be conformant with Ingress specification, here
// we create two paths here, the first is the path itself
// (exact match), the other is path + "/*" (prefix match).
prefix := pathRule.Path
if strings.HasSuffix(prefix, "/") {
prefix += "*"
} else {
prefix += "/*"
uris := []string{}
var nginxVars []configv2alpha1.ApisixRouteHTTPMatchExpr
if pathRule.PathType != nil {
if *pathRule.PathType == networkingv1beta1.PathTypePrefix {
// As per the specification of Ingress path matching rule:
// if the last element of the path is a substring of the
// last element in request path, it is not a match, e.g. /foo/bar
// matches /foo/bar/baz, but does not match /foo/barbaz.
// While in APISIX, /foo/bar matches both /foo/bar/baz and
// /foo/barbaz.
// In order to be conformant with Ingress specification, here
// we create two paths here, the first is the path itself
// (exact match), the other is path + "/*" (prefix match).
prefix := pathRule.Path
if strings.HasSuffix(prefix, "/") {
prefix += "*"
} else {
prefix += "/*"
}
uris = append(uris, prefix, pathRule.Path)
} else if *pathRule.PathType == networkingv1beta1.PathTypeExact {
uris = append(uris, pathRule.Path)
} else if *pathRule.PathType == networkingv1beta1.PathTypeImplementationSpecific && useRegex {
nginxVars = append(nginxVars, configv2alpha1.ApisixRouteHTTPMatchExpr{
Subject: configv2alpha1.ApisixRouteHTTPMatchExprSubject{
Scope: configv2alpha1.ScopePath,
},
Op: configv2alpha1.OpRegexMatch,
Value: &pathRule.Path,
})
uris = append(uris, "/*")
}
uris = append(uris, prefix)
} else {
uris = append(uris, pathRule.Path)
}
route := apisixv1.NewDefaultRoute()
route.Name = composeIngressRouteName(rule.Host, pathRule.Path)
route.ID = id.GenID(route.Name)
route.Host = rule.Host
route.Uris = uris
if len(nginxVars) > 0 {
routeVars, err := t.translateRouteMatchExprs(nginxVars)
if err != nil {
return nil, err
}
route.Vars = routeVars
route.Priority = _regexPriority
}
if len(plugins) > 0 {
route.Plugins = *(plugins.DeepCopy())
}
Expand Down Expand Up @@ -246,6 +302,8 @@ func (t *translator) translateIngressExtensionsV1beta1(ing *extensionsv1beta1.In
upstreamMap: make(map[string]struct{}),
}
plugins := t.translateAnnotations(ing.Annotations)
annoExtractor := annotations.NewExtractor(ing.Annotations)
useRegex := annoExtractor.GetBoolAnnotation(annotations.AnnotationsPrefix + "use-regex")

for _, rule := range ing.Spec.Rules {
for _, pathRule := range rule.HTTP.Paths {
Expand All @@ -266,29 +324,53 @@ func (t *translator) translateIngressExtensionsV1beta1(ing *extensionsv1beta1.In
ctx.addUpstream(ups)
}
uris := []string{pathRule.Path}
if pathRule.PathType != nil && *pathRule.PathType == extensionsv1beta1.PathTypePrefix {
// As per the specification of Ingress path matching rule:
// if the last element of the path is a substring of the
// last element in request path, it is not a match, e.g. /foo/bar
// matches /foo/bar/baz, but does not match /foo/barbaz.
// While in APISIX, /foo/bar matches both /foo/bar/baz and
// /foo/barbaz.
// In order to be conformant with Ingress specification, here
// we create two paths here, the first is the path itself
// (exact match), the other is path + "/*" (prefix match).
prefix := pathRule.Path
if strings.HasSuffix(prefix, "/") {
prefix += "*"
} else {
prefix += "/*"
var nginxVars []configv2alpha1.ApisixRouteHTTPMatchExpr
if pathRule.PathType != nil {
if *pathRule.PathType == extensionsv1beta1.PathTypePrefix {
// As per the specification of Ingress path matching rule:
// if the last element of the path is a substring of the
// last element in request path, it is not a match, e.g. /foo/bar
// matches /foo/bar/baz, but does not match /foo/barbaz.
// While in APISIX, /foo/bar matches both /foo/bar/baz and
// /foo/barbaz.
// In order to be conformant with Ingress specification, here
// we create two paths here, the first is the path itself
// (exact match), the other is path + "/*" (prefix match).
prefix := pathRule.Path
if strings.HasSuffix(prefix, "/") {
prefix += "*"
} else {
prefix += "/*"
}
uris = append(uris, prefix, pathRule.Path)
} else if *pathRule.PathType == extensionsv1beta1.PathTypeExact {
uris = append(uris, pathRule.Path)
} else if *pathRule.PathType == extensionsv1beta1.PathTypeImplementationSpecific && useRegex {
nginxVars = append(nginxVars, configv2alpha1.ApisixRouteHTTPMatchExpr{
Subject: configv2alpha1.ApisixRouteHTTPMatchExprSubject{
Scope: configv2alpha1.ScopePath,
},
Op: configv2alpha1.OpRegexMatch,
Value: &pathRule.Path,
})
uris = append(uris, "/*")
}
uris = append(uris, prefix)
} else {
uris = append(uris, pathRule.Path)
}
route := apisixv1.NewDefaultRoute()
route.Name = composeIngressRouteName(rule.Host, pathRule.Path)
route.ID = id.GenID(route.Name)
route.Host = rule.Host
route.Uris = uris
if len(nginxVars) > 0 {
routeVars, err := t.translateRouteMatchExprs(nginxVars)
if err != nil {
return nil, err
}
route.Vars = routeVars
route.Priority = _regexPriority
}
if len(plugins) > 0 {
route.Plugins = *(plugins.DeepCopy())
}
Expand Down

0 comments on commit ebd0747

Please sign in to comment.