Enhance Unit Tests for Annotations

Adds unit tests for a variety of different annotations.
This commit is contained in:
Fernando Diaz 2019-02-03 19:53:01 -06:00
parent 5c4854b537
commit 1da2900b9b
11 changed files with 544 additions and 45 deletions

View file

@ -30,6 +30,7 @@ import (
meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1" meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/intstr" "k8s.io/apimachinery/pkg/util/intstr"
"k8s.io/ingress-nginx/internal/ingress/annotations/parser" "k8s.io/ingress-nginx/internal/ingress/annotations/parser"
ing_errors "k8s.io/ingress-nginx/internal/ingress/errors"
"k8s.io/ingress-nginx/internal/ingress/resolver" "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) { func TestIngressAuth(t *testing.T) {
ing := buildIngress() ing := buildIngress()

View file

@ -23,6 +23,9 @@ import (
extensions "k8s.io/api/extensions/v1beta1" extensions "k8s.io/api/extensions/v1beta1"
meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1" meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/intstr" "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 { 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) { func TestAnnotations(t *testing.T) {
ing := buildIngress() ing := buildIngress()
data := map[string]string{} 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) ing.SetAnnotations(data)
/*
tests := []struct { fakeSecret := &mockSecret{}
title string i, err := NewParser(fakeSecret).Parse(ing)
url string if err != nil {
method string t.Errorf("Uxpected error with ingress: %v", err)
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 { u, ok := i.(*Config)
data[authTLSSecret] = "" if !ok {
test.title t.Errorf("expected *Config but got %v", u)
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 { secret, err := fakeSecret.GetAuthCertificate("default/demo-secret")
t.Errorf("%v: expected \"%v\" but \"%v\" was returned", test.title, test.url, u.URL) if err != nil {
t.Errorf("unexpected error getting secret %v", err)
} }
if u.Method != test.method {
t.Errorf("%v: expected \"%v\" but \"%v\" was returned", test.title, test.method, u.Method) if u.AuthSSLCert.Secret != secret.Secret {
t.Errorf("expected %v but got %v", secret.Secret, u.AuthSSLCert.Secret)
} }
if u.SendBody != test.sendBody { if u.VerifyClient != "off" {
t.Errorf("%v: expected \"%v\" but \"%v\" was returned", test.title, test.sendBody, u.SendBody) 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)
} }
}*/
} }

View file

@ -42,18 +42,62 @@ func buildIngress() *extensions.Ingress {
}, },
} }
} }
func TestParseInvalidAnnotations(t *testing.T) {
ing := buildIngress()
// 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 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) { func TestParseAnnotations(t *testing.T) {
ing := buildIngress() ing := buildIngress()
_, err := NewParser(&resolver.Mock{}).Parse(ing)
if err != nil {
t.Errorf("unexpected error: %v", err)
}
data := map[string]string{} data := map[string]string{}
data[parser.GetAnnotationWithPrefix("backend-protocol")] = "HTTPS" data[parser.GetAnnotationWithPrefix("backend-protocol")] = "HTTPS"
ing.SetAnnotations(data) ing.SetAnnotations(data)
i, err := NewParser(&resolver.Mock{}).Parse(ing) i, err := NewParser(&resolver.Mock{}).Parse(ing)
if err != nil { if err != nil {
t.Errorf("unexpected error parsing ingress with backend-protocol") t.Errorf("unexpected error parsing ingress with backend-protocol")

View file

@ -17,15 +17,17 @@ limitations under the License.
package canary package canary
import ( import (
"testing"
api "k8s.io/api/core/v1" api "k8s.io/api/core/v1"
extensions "k8s.io/api/extensions/v1beta1" extensions "k8s.io/api/extensions/v1beta1"
metaV1 "k8s.io/apimachinery/pkg/apis/meta/v1" metaV1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/intstr" "k8s.io/apimachinery/pkg/util/intstr"
"k8s.io/ingress-nginx/internal/ingress/annotations/parser" "k8s.io/ingress-nginx/internal/ingress/annotations/parser"
"testing"
"strconv"
"k8s.io/ingress-nginx/internal/ingress/resolver" "k8s.io/ingress-nginx/internal/ingress/resolver"
"strconv"
) )
func buildIngress() *extensions.Ingress { 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) { func TestAnnotations(t *testing.T) {
ing := buildIngress() ing := buildIngress()

View file

@ -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)
}
}

View file

@ -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)
}
}

View file

@ -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) { func TestIngressInfluxDB(t *testing.T) {
ing := buildIngress() ing := buildIngress()

View file

@ -17,6 +17,8 @@ limitations under the License.
package ratelimit package ratelimit
import ( import (
"reflect"
"sort"
"testing" "testing"
api "k8s.io/api/core/v1" 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() ing := buildIngress()
data := map[string]string{} data := map[string]string{}

View file

@ -18,12 +18,15 @@ package redirect
import ( import (
"net/http" "net/http"
"net/url"
"reflect"
"strconv" "strconv"
"testing" "testing"
extensions "k8s.io/api/extensions/v1beta1" extensions "k8s.io/api/extensions/v1beta1"
"k8s.io/ingress-nginx/internal/ingress/annotations/parser" "k8s.io/ingress-nginx/internal/ingress/annotations/parser"
"k8s.io/ingress-nginx/internal/ingress/errors"
"k8s.io/ingress-nginx/internal/ingress/resolver" "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)
}
}

View file

@ -76,6 +76,22 @@ func (cfg mockCfg) GetAuthCertificate(secret string) (*resolver.AuthSSLCert, err
return nil, fmt.Errorf("secret not found: %v", secret) 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) { func TestAnnotations(t *testing.T) {
ing := buildIngress() ing := buildIngress()
data := map[string]string{} data := map[string]string{}

View file

@ -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)
}
}