diff --git a/internal/ingress/annotations/auth/main_test.go b/internal/ingress/annotations/auth/main_test.go index 3546bd0252..146ba32c28 100644 --- a/internal/ingress/annotations/auth/main_test.go +++ b/internal/ingress/annotations/auth/main_test.go @@ -30,6 +30,7 @@ import ( meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/util/intstr" "k8s.io/ingress-nginx/internal/ingress/annotations/parser" + ing_errors "k8s.io/ingress-nginx/internal/ingress/errors" "k8s.io/ingress-nginx/internal/ingress/resolver" ) @@ -96,6 +97,42 @@ func TestIngressWithoutAuth(t *testing.T) { } } +func TestIngressAuthBadAuthType(t *testing.T) { + ing := buildIngress() + + data := map[string]string{} + data[parser.GetAnnotationWithPrefix("auth-type")] = "invalid" + ing.SetAnnotations(data) + + _, dir, _ := dummySecretContent(t) + defer os.RemoveAll(dir) + + expected := ing_errors.NewLocationDenied("invalid authentication type") + _, err := NewParser(dir, &mockSecret{}).Parse(ing) + if err.Error() != expected.Error() { + t.Errorf("expected '%v' but got '%v'", expected, err) + } +} + +func TestInvalidIngressAuthNoSecret(t *testing.T) { + ing := buildIngress() + + data := map[string]string{} + data[parser.GetAnnotationWithPrefix("auth-type")] = "basic" + ing.SetAnnotations(data) + + _, dir, _ := dummySecretContent(t) + defer os.RemoveAll(dir) + + expected := ing_errors.LocationDenied{ + Reason: errors.New("error reading secret name from annotation: ingress rule without annotations"), + } + _, err := NewParser(dir, &mockSecret{}).Parse(ing) + if err.Error() != expected.Reason.Error() { + t.Errorf("expected '%v' but got '%v'", expected, err) + } +} + func TestIngressAuth(t *testing.T) { ing := buildIngress() diff --git a/internal/ingress/annotations/authtls/main_test.go b/internal/ingress/annotations/authtls/main_test.go index a3613bb159..fbd0154bf6 100644 --- a/internal/ingress/annotations/authtls/main_test.go +++ b/internal/ingress/annotations/authtls/main_test.go @@ -23,6 +23,9 @@ import ( extensions "k8s.io/api/extensions/v1beta1" meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/util/intstr" + "k8s.io/ingress-nginx/internal/ingress/annotations/parser" + "k8s.io/ingress-nginx/internal/ingress/errors" + "k8s.io/ingress-nginx/internal/ingress/resolver" ) func buildIngress() *extensions.Ingress { @@ -60,49 +63,66 @@ func buildIngress() *extensions.Ingress { } } +// mocks the resolver for authTLS +type mockSecret struct { + resolver.Mock +} + +// GetAuthCertificate from mockSecret mocks the GetAuthCertificate for authTLS +func (m mockSecret) GetAuthCertificate(name string) (*resolver.AuthSSLCert, error) { + if name != "default/demo-secret" { + return nil, errors.Errorf("there is no secret with name %v", name) + } + + return &resolver.AuthSSLCert{ + Secret: "default/demo-secret", + CAFileName: "/ssl/ca.crt", + PemSHA: "abc", + }, nil + +} + func TestAnnotations(t *testing.T) { ing := buildIngress() - data := map[string]string{} + + data[parser.GetAnnotationWithPrefix("auth-tls-secret")] = "default/demo-secret" + data[parser.GetAnnotationWithPrefix("auth-tls-verify-client")] = "off" + data[parser.GetAnnotationWithPrefix("auth-tls-verify-depth")] = "1" + data[parser.GetAnnotationWithPrefix("auth-tls-error-page")] = "ok.com/error" + data[parser.GetAnnotationWithPrefix("auth-tls-pass-certificate-to-upstream")] = "true" + ing.SetAnnotations(data) - /* - tests := []struct { - title string - url string - method string - sendBody bool - expErr bool - }{ - {"empty", "", "", false, true}, - {"no scheme", "bar", "", false, true}, - {"invalid host", "http://", "", false, true}, - {"invalid host (multiple dots)", "http://foo..bar.com", "", false, true}, - {"valid URL", "http://bar.foo.com/external-auth", "", false, false}, - {"valid URL - send body", "http://foo.com/external-auth", "POST", true, false}, - {"valid URL - send body", "http://foo.com/external-auth", "GET", true, false}, - } - - for _, test := range tests { - data[authTLSSecret] = "" - test.title - - u, err := ParseAnnotations(ing) - - if test.expErr { - if err == nil { - t.Errorf("%v: expected error but retuned nil", test.title) - } - continue - } - - if u.URL != test.url { - t.Errorf("%v: expected \"%v\" but \"%v\" was returned", test.title, test.url, u.URL) - } - if u.Method != test.method { - t.Errorf("%v: expected \"%v\" but \"%v\" was returned", test.title, test.method, u.Method) - } - if u.SendBody != test.sendBody { - t.Errorf("%v: expected \"%v\" but \"%v\" was returned", test.title, test.sendBody, u.SendBody) - } - }*/ + + fakeSecret := &mockSecret{} + i, err := NewParser(fakeSecret).Parse(ing) + if err != nil { + t.Errorf("Uxpected error with ingress: %v", err) + } + + u, ok := i.(*Config) + if !ok { + t.Errorf("expected *Config but got %v", u) + } + + secret, err := fakeSecret.GetAuthCertificate("default/demo-secret") + if err != nil { + t.Errorf("unexpected error getting secret %v", err) + } + + if u.AuthSSLCert.Secret != secret.Secret { + t.Errorf("expected %v but got %v", secret.Secret, u.AuthSSLCert.Secret) + } + if u.VerifyClient != "off" { + t.Errorf("expected %v but got %v", "off", u.VerifyClient) + } + if u.ValidationDepth != 1 { + t.Errorf("expected %v but got %v", 1, u.ValidationDepth) + } + if u.ErrorPage != "ok.com/error" { + t.Errorf("expected %v but got %v", "ok.com/error", u.ErrorPage) + } + if u.PassCertToUpstream != true { + t.Errorf("expected %v but got %v", true, u.PassCertToUpstream) + } } diff --git a/internal/ingress/annotations/backendprotocol/main_test.go b/internal/ingress/annotations/backendprotocol/main_test.go index 50a7fbcdd8..539d095627 100644 --- a/internal/ingress/annotations/backendprotocol/main_test.go +++ b/internal/ingress/annotations/backendprotocol/main_test.go @@ -42,18 +42,62 @@ func buildIngress() *extensions.Ingress { }, } } - -func TestParseAnnotations(t *testing.T) { +func TestParseInvalidAnnotations(t *testing.T) { ing := buildIngress() - _, err := NewParser(&resolver.Mock{}).Parse(ing) + // Test no annotations set + i, err := NewParser(&resolver.Mock{}).Parse(ing) + if err != nil { + t.Errorf("unexpected error parsing ingress with backend-protocol") + } + val, ok := i.(string) + if !ok { + t.Errorf("expected a string type") + } + if val != "HTTP" { + t.Errorf("expected HTTPS but %v returned", val) + } + + data := map[string]string{} + ing.SetAnnotations(data) + + // Test with empty annotations + i, err = NewParser(&resolver.Mock{}).Parse(ing) + if err != nil { + t.Errorf("unexpected error parsing ingress with backend-protocol") + } + val, ok = i.(string) + if !ok { + t.Errorf("expected a string type") + } + if val != "HTTP" { + t.Errorf("expected HTTPS but %v returned", val) + } + + // Test invalid annotation set + data[parser.GetAnnotationWithPrefix("backend-protocol")] = "INVALID" + ing.SetAnnotations(data) + + i, err = NewParser(&resolver.Mock{}).Parse(ing) if err != nil { - t.Errorf("unexpected error: %v", err) + t.Errorf("unexpected error parsing ingress with backend-protocol") } + val, ok = i.(string) + if !ok { + t.Errorf("expected a string type") + } + if val != "HTTP" { + t.Errorf("expected HTTPS but %v returned", val) + } +} + +func TestParseAnnotations(t *testing.T) { + ing := buildIngress() data := map[string]string{} data[parser.GetAnnotationWithPrefix("backend-protocol")] = "HTTPS" ing.SetAnnotations(data) + i, err := NewParser(&resolver.Mock{}).Parse(ing) if err != nil { t.Errorf("unexpected error parsing ingress with backend-protocol") diff --git a/internal/ingress/annotations/canary/main_test.go b/internal/ingress/annotations/canary/main_test.go index ddac491f2d..3ad8b3d7fd 100644 --- a/internal/ingress/annotations/canary/main_test.go +++ b/internal/ingress/annotations/canary/main_test.go @@ -17,15 +17,17 @@ limitations under the License. package canary import ( + "testing" + api "k8s.io/api/core/v1" extensions "k8s.io/api/extensions/v1beta1" metaV1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/util/intstr" "k8s.io/ingress-nginx/internal/ingress/annotations/parser" - "testing" - "k8s.io/ingress-nginx/internal/ingress/resolver" "strconv" + + "k8s.io/ingress-nginx/internal/ingress/resolver" ) func buildIngress() *extensions.Ingress { @@ -63,6 +65,30 @@ func buildIngress() *extensions.Ingress { } } +func TestCanaryInvalid(t *testing.T) { + ing := buildIngress() + + data := map[string]string{} + ing.SetAnnotations(data) + + i, err := NewParser(&resolver.Mock{}).Parse(ing) + if err != nil { + t.Errorf("Error Parsing Canary Annotations") + } + + val, ok := i.(*Config) + if !ok { + t.Errorf("Expected %v and got %v", "*Config", val) + } + if val.Enabled != false { + t.Errorf("Expected %v but got %v", false, val.Enabled) + } + if val.Weight != 0 { + t.Errorf("Expected %v but got %v", 0, val.Weight) + } + +} + func TestAnnotations(t *testing.T) { ing := buildIngress() diff --git a/internal/ingress/annotations/customhttperrors/main_test.go b/internal/ingress/annotations/customhttperrors/main_test.go new file mode 100644 index 0000000000..610165b5dc --- /dev/null +++ b/internal/ingress/annotations/customhttperrors/main_test.go @@ -0,0 +1,91 @@ +/* +Copyright 2019 The Kubernetes Authors. + +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 customhttperrors + +import ( + "reflect" + "sort" + "testing" + + api "k8s.io/api/core/v1" + extensions "k8s.io/api/extensions/v1beta1" + meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/ingress-nginx/internal/ingress/annotations/parser" + "k8s.io/ingress-nginx/internal/ingress/resolver" + + "k8s.io/apimachinery/pkg/util/intstr" +) + +func buildIngress() *extensions.Ingress { + return &extensions.Ingress{ + ObjectMeta: meta_v1.ObjectMeta{ + Name: "foo", + Namespace: api.NamespaceDefault, + }, + Spec: extensions.IngressSpec{ + Backend: &extensions.IngressBackend{ + ServiceName: "default-backend", + ServicePort: intstr.FromInt(80), + }, + }, + } +} + +func TestParseInvalidAnnotations(t *testing.T) { + ing := buildIngress() + + _, err := NewParser(&resolver.Mock{}).Parse(ing) + if err == nil { + t.Errorf("expected error parsing ingress with custom-http-errors") + } + + data := map[string]string{} + data[parser.GetAnnotationWithPrefix("custom-http-errors")] = "400,404,abc,502" + ing.SetAnnotations(data) + + i, err := NewParser(&resolver.Mock{}).Parse(ing) + if err == nil { + t.Errorf("expected error parsing ingress with custom-http-errors") + } + if i != nil { + t.Errorf("expected %v but got %v", nil, i) + } +} + +func TestParseAnnotations(t *testing.T) { + ing := buildIngress() + + data := map[string]string{} + data[parser.GetAnnotationWithPrefix("custom-http-errors")] = "400,404,500,502" + ing.SetAnnotations(data) + + i, err := NewParser(&resolver.Mock{}).Parse(ing) + if err != nil { + t.Errorf("unexpected error parsing ingress with custom-http-errors") + } + val, ok := i.([]int) + if !ok { + t.Errorf("expected a []int type") + } + + expected := []int{400, 404, 500, 502} + sort.Ints(val) + + if !reflect.DeepEqual(expected, val) { + t.Errorf("expected %v but got %v", expected, val) + } +} diff --git a/internal/ingress/annotations/defaultbackend/main_test.go b/internal/ingress/annotations/defaultbackend/main_test.go new file mode 100644 index 0000000000..c344a8e035 --- /dev/null +++ b/internal/ingress/annotations/defaultbackend/main_test.go @@ -0,0 +1,105 @@ +/* +Copyright 2015 The Kubernetes Authors. + +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 defaultbackend + +import ( + "testing" + + api "k8s.io/api/core/v1" + extensions "k8s.io/api/extensions/v1beta1" + meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/ingress-nginx/internal/ingress/annotations/parser" + "k8s.io/ingress-nginx/internal/ingress/errors" + "k8s.io/ingress-nginx/internal/ingress/resolver" + + "k8s.io/apimachinery/pkg/util/intstr" +) + +func buildIngress() *extensions.Ingress { + defaultBackend := extensions.IngressBackend{ + ServiceName: "default-backend", + ServicePort: intstr.FromInt(80), + } + + return &extensions.Ingress{ + ObjectMeta: meta_v1.ObjectMeta{ + Name: "foo", + Namespace: api.NamespaceDefault, + }, + Spec: extensions.IngressSpec{ + Backend: &extensions.IngressBackend{ + ServiceName: "default-backend", + ServicePort: intstr.FromInt(80), + }, + Rules: []extensions.IngressRule{ + { + Host: "foo.bar.com", + IngressRuleValue: extensions.IngressRuleValue{ + HTTP: &extensions.HTTPIngressRuleValue{ + Paths: []extensions.HTTPIngressPath{ + { + Path: "/foo", + Backend: defaultBackend, + }, + }, + }, + }, + }, + }, + }, + } +} + +type mockService struct { + resolver.Mock +} + +// GetService mocks the GetService call from the defaultbackend package +func (m mockService) GetService(name string) (*api.Service, error) { + if name != "default/demo-service" { + return nil, errors.Errorf("there is no service with name %v", name) + } + + return &api.Service{ + ObjectMeta: meta_v1.ObjectMeta{ + Namespace: api.NamespaceDefault, + Name: "demo-service", + }, + }, nil +} + +func TestAnnotations(t *testing.T) { + ing := buildIngress() + + data := map[string]string{} + data[parser.GetAnnotationWithPrefix("default-backend")] = "demo-service" + ing.SetAnnotations(data) + + fakeService := &mockService{} + i, err := NewParser(fakeService).Parse(ing) + if err != nil { + t.Errorf("unexpected error %v", err) + } + + svc, ok := i.(*api.Service) + if !ok { + t.Errorf("expected *api.Service but got %v", svc) + } + if svc.Name != "demo-service" { + t.Errorf("expected %v but got %v", "demo-service", svc.Name) + } +} diff --git a/internal/ingress/annotations/influxdb/main_test.go b/internal/ingress/annotations/influxdb/main_test.go index b8c67ba590..a022ab66d7 100644 --- a/internal/ingress/annotations/influxdb/main_test.go +++ b/internal/ingress/annotations/influxdb/main_test.go @@ -62,6 +62,36 @@ func buildIngress() *extensions.Ingress { } } +func TestIngressInvalidInfluxDB(t *testing.T) { + ing := buildIngress() + + influx, _ := NewParser(&resolver.Mock{}).Parse(ing) + nginxInflux, ok := influx.(*Config) + if !ok { + t.Errorf("expected a Config type") + } + + if nginxInflux.InfluxDBEnabled == true { + t.Errorf("expected influxdb enabled but returned %v", nginxInflux.InfluxDBEnabled) + } + + if nginxInflux.InfluxDBMeasurement != "default" { + t.Errorf("expected measurement name not found. Found %v", nginxInflux.InfluxDBMeasurement) + } + + if nginxInflux.InfluxDBPort != "8089" { + t.Errorf("expected port not found. Found %v", nginxInflux.InfluxDBPort) + } + + if nginxInflux.InfluxDBHost != "127.0.0.1" { + t.Errorf("expected host not found. Found %v", nginxInflux.InfluxDBHost) + } + + if nginxInflux.InfluxDBServerName != "nginx-ingress" { + t.Errorf("expected server name not found. Found %v", nginxInflux.InfluxDBServerName) + } +} + func TestIngressInfluxDB(t *testing.T) { ing := buildIngress() diff --git a/internal/ingress/annotations/ratelimit/main_test.go b/internal/ingress/annotations/ratelimit/main_test.go index 06ced468b8..056f09a1d0 100644 --- a/internal/ingress/annotations/ratelimit/main_test.go +++ b/internal/ingress/annotations/ratelimit/main_test.go @@ -17,6 +17,8 @@ limitations under the License. package ratelimit import ( + "reflect" + "sort" "testing" api "k8s.io/api/core/v1" @@ -83,7 +85,24 @@ func TestWithoutAnnotations(t *testing.T) { } } -func TestBadRateLimiting(t *testing.T) { +func TestParseCIDRs(t *testing.T) { + cidr, _ := parseCIDRs("invalid.com") + if cidr != nil { + t.Errorf("expected %v but got %v", nil, cidr) + } + + expected := []string{"192.0.0.1", "192.0.1.0/24"} + cidr, err := parseCIDRs("192.0.0.1, 192.0.1.0/24") + if err != nil { + t.Errorf("unexpected error %v", err) + } + sort.Strings(cidr) + if !reflect.DeepEqual(expected, cidr) { + t.Errorf("expected %v but got %v", expected, cidr) + } +} + +func TestRateLimiting(t *testing.T) { ing := buildIngress() data := map[string]string{} diff --git a/internal/ingress/annotations/redirect/redirect_test.go b/internal/ingress/annotations/redirect/redirect_test.go index 6c60341aa0..95e80b95a9 100644 --- a/internal/ingress/annotations/redirect/redirect_test.go +++ b/internal/ingress/annotations/redirect/redirect_test.go @@ -18,12 +18,15 @@ package redirect import ( "net/http" + "net/url" + "reflect" "strconv" "testing" extensions "k8s.io/api/extensions/v1beta1" "k8s.io/ingress-nginx/internal/ingress/annotations/parser" + "k8s.io/ingress-nginx/internal/ingress/errors" "k8s.io/ingress-nginx/internal/ingress/resolver" ) @@ -99,3 +102,56 @@ func TestPermanentRedirectWithCustomCode(t *testing.T) { }) } } + +func TestTemporalRedirect(t *testing.T) { + rp := NewParser(resolver.Mock{}) + if rp == nil { + t.Fatalf("Expected a parser.IngressAnnotation but returned nil") + } + + ing := new(extensions.Ingress) + + data := make(map[string]string, 1) + data[parser.GetAnnotationWithPrefix("from-to-www-redirect")] = "true" + data[parser.GetAnnotationWithPrefix("temporal-redirect")] = defRedirectURL + ing.SetAnnotations(data) + + i, err := rp.Parse(ing) + if err != nil { + t.Errorf("Unexpected error with ingress: %v", err) + } + redirect, ok := i.(*Config) + if !ok { + t.Errorf("Expected a Redirect type") + } + if redirect.URL != defRedirectURL { + t.Errorf("Expected %v as redirect but returned %s", defRedirectURL, redirect.URL) + } + if redirect.Code != http.StatusFound { + t.Errorf("Expected %v as redirect to have a code %d but had %d", defRedirectURL, defaultPermanentRedirectCode, redirect.Code) + } + if redirect.FromToWWW != true { + t.Errorf("Expected %v as redirect to have from-to-www as %v but got %v", defRedirectURL, true, redirect.FromToWWW) + } +} + +func TestIsValidURL(t *testing.T) { + + invalid := "ok.com" + urlParse, err := url.Parse(invalid) + if err != nil { + t.Errorf("unexpected error %v", err) + } + + expected := errors.Errorf("only http and https are valid protocols (%v)", urlParse.Scheme) + err = isValidURL(invalid) + if reflect.DeepEqual(expected.Error, err.Error) { + t.Errorf("expected '%v' but got '%v'", expected, err) + } + + valid := "http://ok.com" + err = isValidURL(valid) + if err != nil { + t.Errorf("expected nil but got %v", err) + } +} diff --git a/internal/ingress/annotations/secureupstream/main_test.go b/internal/ingress/annotations/secureupstream/main_test.go index adee3c63ae..8acea321d2 100644 --- a/internal/ingress/annotations/secureupstream/main_test.go +++ b/internal/ingress/annotations/secureupstream/main_test.go @@ -76,6 +76,22 @@ func (cfg mockCfg) GetAuthCertificate(secret string) (*resolver.AuthSSLCert, err return nil, fmt.Errorf("secret not found: %v", secret) } +func TestNoCA(t *testing.T) { + ing := buildIngress() + data := map[string]string{} + data[parser.GetAnnotationWithPrefix("backend-protocol")] = "HTTPS" + ing.SetAnnotations(data) + + _, err := NewParser(mockCfg{ + certs: map[string]resolver.AuthSSLCert{ + "default/secure-verify-ca": {}, + }, + }).Parse(ing) + if err != nil { + t.Errorf("Unexpected error on ingress: %v", err) + } +} + func TestAnnotations(t *testing.T) { ing := buildIngress() data := map[string]string{} diff --git a/internal/ingress/annotations/upstreamvhost/main_test.go b/internal/ingress/annotations/upstreamvhost/main_test.go new file mode 100644 index 0000000000..737d0d11d7 --- /dev/null +++ b/internal/ingress/annotations/upstreamvhost/main_test.go @@ -0,0 +1,55 @@ +/* +Copyright 2019 The Kubernetes Authors. + +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 upstreamvhost + +import ( + "testing" + + api "k8s.io/api/core/v1" + extensions "k8s.io/api/extensions/v1beta1" + meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/ingress-nginx/internal/ingress/annotations/parser" + "k8s.io/ingress-nginx/internal/ingress/resolver" +) + +func TestParse(t *testing.T) { + ing := &extensions.Ingress{ + ObjectMeta: meta_v1.ObjectMeta{ + Name: "foo", + Namespace: api.NamespaceDefault, + }, + Spec: extensions.IngressSpec{}, + } + + data := map[string]string{} + data[parser.GetAnnotationWithPrefix("upstream-vhost")] = "ok.com" + + ing.SetAnnotations(data) + + i, err := NewParser(&resolver.Mock{}).Parse(ing) + if err != nil { + t.Errorf("unexpected error %v", err) + } + + vhost, ok := i.(string) + if !ok { + t.Errorf("expected string but got %v", vhost) + } + if vhost != "ok.com" { + t.Errorf("expected %v but got %v", "ok.com", vhost) + } +}