Skip to content

Commit

Permalink
Allow a 'location-modifier' annotation for regex paths
Browse files Browse the repository at this point in the history
In nginx a modifier can be passed which decides how locations
will be parsed, i.e. regex or no regex.

This change allows a location modifier to be passed as an annotatione e.g. ingress.kubernetes.io/location-modifier: "~"
If no location-modifier is passed but 'rewrite-target' is provided, then a default of "*~" is assumed.
  • Loading branch information
abetterway2feel authored and Frank Ittermann committed Apr 8, 2018
1 parent ab83490 commit b2fbd09
Show file tree
Hide file tree
Showing 8 changed files with 554 additions and 58 deletions.
8 changes: 8 additions & 0 deletions internal/ingress/annotations/rewrite/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ type Config struct {
ForceSSLRedirect bool `json:"forceSSLRedirect"`
// AppRoot defines the Application Root that the Controller must redirect if it's in '/' context
AppRoot string `json:"appRoot"`
// Location modifier
LocationModifier string `json:"locationModifier"`
}

// Equal tests for equality between two Redirect types
Expand All @@ -51,6 +53,9 @@ func (r1 *Config) Equal(r2 *Config) bool {
if r1.Target != r2.Target {
return false
}
if r1.LocationModifier != r2.LocationModifier {
return false
}
if r1.AddBaseURL != r2.AddBaseURL {
return false
}
Expand Down Expand Up @@ -83,6 +88,7 @@ func NewParser(r resolver.Resolver) parser.IngressAnnotation {
// rule used to rewrite the defined paths
func (a rewrite) Parse(ing *extensions.Ingress) (interface{}, error) {
rt, _ := parser.GetStringAnnotation("rewrite-target", ing)

sslRe, err := parser.GetBoolAnnotation("ssl-redirect", ing)
if err != nil {
sslRe = a.r.GetDefaultBackend().SSLRedirect
Expand All @@ -94,6 +100,7 @@ func (a rewrite) Parse(ing *extensions.Ingress) (interface{}, error) {
abu, _ := parser.GetBoolAnnotation("add-base-url", ing)
bus, _ := parser.GetStringAnnotation("base-url-scheme", ing)
ar, _ := parser.GetStringAnnotation("app-root", ing)
locMod, _ := parser.GetStringAnnotation("location-modifier", ing)

return &Config{
Target: rt,
Expand All @@ -102,5 +109,6 @@ func (a rewrite) Parse(ing *extensions.Ingress) (interface{}, error) {
SSLRedirect: sslRe,
ForceSSLRedirect: fSslRe,
AppRoot: ar,
LocationModifier: locMod,
}, nil
}
123 changes: 79 additions & 44 deletions internal/ingress/annotations/rewrite/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,95 +85,130 @@ func TestWithoutAnnotations(t *testing.T) {
}
}

func TestRedirect(t *testing.T) {
func buildRedirect(t *testing.T, data *map[string]string, mock *mockBackend) *Config {
ing := buildIngress()
ing.SetAnnotations(*data)

data := map[string]string{}
data[parser.GetAnnotationWithPrefix("rewrite-target")] = defRoute
ing.SetAnnotations(data)
if mock == nil {
mock = &mockBackend{}
}

i, err := NewParser(mockBackend{}).Parse(ing)
i1, err := NewParser(mock).Parse(ing)
if err != nil {
t.Errorf("Unexpected error with ingress: %v", err)
}
redirect, ok := i.(*Config)
redirect, ok := i1.(*Config)
if !ok {
t.Errorf("expected a Redirect type")
}
return redirect
}

func buildDifferentRedirect(t *testing.T, key string, val string) *Config {
data := map[string]string{}
data[parser.GetAnnotationWithPrefix(key)] = val
return buildRedirect(t, &data, nil)
}

func TestEqual(t *testing.T) {
data := map[string]string{}

redirect := buildRedirect(t, &data, nil)

if !redirect.Equal(redirect) {
t.Errorf("Expect the both redirect types to be equal")
}

if redirect.Equal(nil) {
t.Errorf("Expect the both redirect types to be different")
}

if redirect.Equal(buildDifferentRedirect(t, "location-modifier", "=")) {
t.Errorf("Expect the both redirect types to be different")
}

if redirect.Equal(buildDifferentRedirect(t, "rewrite-target", "/")) {
t.Errorf("Expect the both redirect types to be different")
}

if redirect.Equal(buildDifferentRedirect(t, "ssl-redirect", "true")) {
t.Errorf("Expect the both redirect types to be different")
}

if redirect.Equal(buildDifferentRedirect(t, "force-ssl-redirect", "true")) {
t.Errorf("Expect the both redirect types to be different")
}

if redirect.Equal(buildDifferentRedirect(t, "add-base-url", "true")) {
t.Errorf("Expect the both redirect types to be different")
}

if redirect.Equal(buildDifferentRedirect(t, "base-url-scheme", "/scheme")) {
t.Errorf("Expect the both redirect types to be different")
}

if redirect.Equal(buildDifferentRedirect(t, "app-root", "/root")) {
t.Errorf("Expect the both redirect types to be different")
}
}

func TestRedirect(t *testing.T) {
data := map[string]string{}
data[parser.GetAnnotationWithPrefix("rewrite-target")] = defRoute
redirect := buildRedirect(t, &data, nil)

if redirect.Target != defRoute {
t.Errorf("Expected %v as redirect but returned %s", defRoute, redirect.Target)
}
}

func TestSSLRedirect(t *testing.T) {
ing := buildIngress()
func TestRegex(t *testing.T) {
data := map[string]string{}
modifier := "~"
data[parser.GetAnnotationWithPrefix("location-modifier")] = modifier

redirect := buildRedirect(t, &data, nil)
if redirect.LocationModifier != modifier {
t.Errorf("Expected %v as location modifier but returned %s", modifier, redirect.LocationModifier)
}
}

func TestSSLRedirect(t *testing.T) {
data := map[string]string{}
data[parser.GetAnnotationWithPrefix("rewrite-target")] = defRoute
ing.SetAnnotations(data)

i, _ := NewParser(mockBackend{redirect: true}).Parse(ing)
redirect, ok := i.(*Config)
if !ok {
t.Errorf("expected a Redirect type")
}
redirect := buildRedirect(t, &data, &mockBackend{redirect: true})
if !redirect.SSLRedirect {
t.Errorf("Expected true but returned false")
}

data[parser.GetAnnotationWithPrefix("ssl-redirect")] = "false"
ing.SetAnnotations(data)

i, _ = NewParser(mockBackend{redirect: false}).Parse(ing)
redirect, ok = i.(*Config)
if !ok {
t.Errorf("expected a Redirect type")
}
redirect = buildRedirect(t, &data, &mockBackend{redirect: false})
if redirect.SSLRedirect {
t.Errorf("Expected false but returned true")
}
}

func TestForceSSLRedirect(t *testing.T) {
ing := buildIngress()

data := map[string]string{}
data[parser.GetAnnotationWithPrefix("rewrite-target")] = defRoute
ing.SetAnnotations(data)

i, _ := NewParser(mockBackend{redirect: true}).Parse(ing)
redirect, ok := i.(*Config)
if !ok {
t.Errorf("expected a Redirect type")
}
redirect := buildRedirect(t, &data, nil)
if redirect.ForceSSLRedirect {
t.Errorf("Expected false but returned true")
}

data[parser.GetAnnotationWithPrefix("force-ssl-redirect")] = "true"
ing.SetAnnotations(data)

i, _ = NewParser(mockBackend{redirect: false}).Parse(ing)
redirect, ok = i.(*Config)
if !ok {
t.Errorf("expected a Redirect type")
}
redirect = buildRedirect(t, &data, nil)
if !redirect.ForceSSLRedirect {
t.Errorf("Expected true but returned false")
}
}
func TestAppRoot(t *testing.T) {
ing := buildIngress()

data := map[string]string{}
data[parser.GetAnnotationWithPrefix("app-root")] = "/app1"
ing.SetAnnotations(data)

i, _ := NewParser(mockBackend{redirect: true}).Parse(ing)
redirect, ok := i.(*Config)
if !ok {
t.Errorf("expected a App Context")
}
redirect := buildRedirect(t, &data, nil)
if redirect.AppRoot != "/app1" {
t.Errorf("Unexpected value got in AppRoot")
}
Expand Down
24 changes: 19 additions & 5 deletions internal/ingress/controller/template/template.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,10 @@ import (
)

const (
slash = "/"
nonIdempotent = "non_idempotent"
defBufferSize = 65535
slash = "/"
nonIdempotent = "non_idempotent"
defBufferSize = 65535
defaultLocationModifier = "~*"
)

// Template ...
Expand Down Expand Up @@ -215,19 +216,32 @@ func buildLocation(input interface{}) string {
}

path := location.Path
locationModifier := location.Rewrite.LocationModifier

if len(location.Rewrite.Target) > 0 && location.Rewrite.Target != path {

if len(locationModifier) <= 0 {
locationModifier = defaultLocationModifier
}

if path == slash {
return fmt.Sprintf("~* %s", path)
return fmt.Sprintf("%s %s", locationModifier, path)
}
// baseuri regex will parse basename from the given location
baseuri := `(?<baseuri>.*)`
if !strings.HasSuffix(path, slash) {
// Not treat the slash after "location path" as a part of baseuri
baseuri = fmt.Sprintf(`\/?%s`, baseuri)
}
return fmt.Sprintf(`~* ^%s%s`, path, baseuri)
return fmt.Sprintf(`%s ^%s%s`, locationModifier, path, baseuri)
}

if len(locationModifier) > 0 {
if path == slash {
return fmt.Sprintf("%s %s", locationModifier, path)
}
return fmt.Sprintf("%s ^%s", locationModifier, path)
}
return path
}

Expand Down
Loading

0 comments on commit b2fbd09

Please sign in to comment.