From 86d700c845a228a431f77d3ef78668eaf8f5d030 Mon Sep 17 00:00:00 2001 From: Ludovic Fernandez Date: Mon, 1 Apr 2019 17:02:06 +0200 Subject: [PATCH] Enhance k8s tests maintainability --- Gopkg.lock | 4 +- .../kubernetes/builder_configuration_test.go | 20 +- provider/kubernetes/builder_endpoint_test.go | 7 - provider/kubernetes/builder_service_test.go | 33 - provider/kubernetes/client_mock_test.go | 38 +- .../1_path_2_service_ingresses.yml | 21 + .../1_path_4_service_ingresses.yml | 31 + .../2_path_2_service_ingresses.yml | 29 + .../2_path_3_service_ingresses.yml | 30 + .../2_path_no_service_ingresses.yml | 23 + .../2_path_overflow_ingresses.yml | 22 + .../2_path_without_weight_ingresses.yml | 19 + .../computeServiceWeights_endpoints.yml | 62 + .../computeServiceWeights_services.yml | 26 + .../divergingIngressDefinitions_endpoints.yml | 16 + .../divergingIngressDefinitions_ingresses.yml | 26 + .../divergingIngressDefinitions_services.yml | 10 + .../fixtures/getPassHostHeader_ingresses.yml | 13 + .../fixtures/getPassHostHeader_services.yml | 9 + .../fixtures/getPassTLSCert_ingresses.yml | 13 + .../fixtures/getPassTLSCert_services.yml | 9 + .../fixtures/hostlessIngress_ingresses.yml | 12 + .../fixtures/hostlessIngress_services.yml | 10 + .../fixtures/ingressAnnotations_ingresses.yml | 431 ++ .../fixtures/ingressAnnotations_secrets.yml | 7 + .../fixtures/ingressAnnotations_services.yml | 37 + .../ingressClassAnnotation_endpoints.yml | 11 + .../ingressClassAnnotation_ingresses.yml | 81 + .../ingressClassAnnotation_services.yml | 24 + .../invalidPassHostHeaderValue_ingresses.yml | 15 + .../invalidPassHostHeaderValue_services.yml | 12 + .../invalidPassTLSCertValue_ingresses.yml | 15 + .../invalidPassTLSCertValue_services.yml | 12 + ...lobalIngressWithExternalName_ingresses.yml | 8 + ...GlobalIngressWithExternalName_services.yml | 10 + ...balIngressWithHttpsPortNames_endpoints.yml | 11 + ...balIngressWithHttpsPortNames_ingresses.yml | 8 + ...obalIngressWithHttpsPortNames_services.yml | 10 + ...GlobalIngressWithPortNumbers_endpoints.yml | 10 + ...GlobalIngressWithPortNumbers_ingresses.yml | 8 + ...dGlobalIngressWithPortNumbers_services.yml | 9 + .../loadIngressesBasicAuth_ingresses.yml | 17 + .../loadIngressesBasicAuth_secrets.yml | 7 + .../loadIngressesBasicAuth_services.yml | 12 + ...gressesForwardAuthMissingURL_endpoints.yml | 10 + ...gressesForwardAuthMissingURL_ingresses.yml | 15 + ...ngressesForwardAuthMissingURL_services.yml | 9 + ...ardAuthWithTLSSecretFailures_endpoints.yml | 10 + ...ardAuthWithTLSSecretFailures_ingresses.yml | 17 + ...wardAuthWithTLSSecretFailures_services.yml | 10 + ...ssesForwardAuthWithTLSSecret_endpoints.yml | 10 + ...ssesForwardAuthWithTLSSecret_ingresses.yml | 18 + ...ressesForwardAuthWithTLSSecret_secrets.yml | 8 + ...essesForwardAuthWithTLSSecret_services.yml | 9 + .../loadIngressesForwardAuth_endpoints.yml | 10 + .../loadIngressesForwardAuth_ingresses.yml | 18 + .../loadIngressesForwardAuth_services.yml | 9 + .../fixtures/loadIngresses_endpoints.yml | 74 + .../fixtures/loadIngresses_ingresses.yml | 49 + .../fixtures/loadIngresses_services.yml | 95 + .../fixtures/missingResources_endpoints.yml | 18 + .../fixtures/missingResources_ingresses.yml | 30 + .../fixtures/missingResources_services.yml | 31 + .../fixtures/multiPortServices_endpoints.yml | 18 + .../fixtures/multiPortServices_ingresses.yml | 16 + .../fixtures/multiPortServices_services.yml | 12 + ...ncesServicesFromOwnNamespace_ingresses.yml | 12 + ...encesServicesFromOwnNamespace_services.yml | 22 + ...ntageWeightServiceAnnotation_endpoints.yml | 24 + ...ntageWeightServiceAnnotation_ingresses.yml | 30 + ...entageWeightServiceAnnotation_services.yml | 32 + .../priorityHeaderValue_ingresses.yml | 15 + .../fixtures/priorityHeaderValue_services.yml | 12 + .../providerUpdateIngressStatus_services.yml | 24 + .../fixtures/serviceAnnotations_endpoints.yml | 84 + .../fixtures/serviceAnnotations_ingresses.yml | 37 + .../fixtures/serviceAnnotations_services.yml | 74 + .../fixtures/tLSSecretLoad_ingresses.yml | 40 + .../fixtures/tLSSecretLoad_secrets.yml | 8 + .../fixtures/tLSSecretLoad_services.yml | 24 + ...mplateBreakingIngresssValues_ingresses.yml | 45 + provider/kubernetes/kubernetes_test.go | 4233 +++++------------ provider/kubernetes/parser_test.go | 79 + provider/kubernetes/weight_allocator_test.go | 217 +- 84 files changed, 3560 insertions(+), 3186 deletions(-) create mode 100644 provider/kubernetes/fixtures/computeServiceWeights/1_path_2_service_ingresses.yml create mode 100644 provider/kubernetes/fixtures/computeServiceWeights/1_path_4_service_ingresses.yml create mode 100644 provider/kubernetes/fixtures/computeServiceWeights/2_path_2_service_ingresses.yml create mode 100644 provider/kubernetes/fixtures/computeServiceWeights/2_path_3_service_ingresses.yml create mode 100644 provider/kubernetes/fixtures/computeServiceWeights/2_path_no_service_ingresses.yml create mode 100644 provider/kubernetes/fixtures/computeServiceWeights/2_path_overflow_ingresses.yml create mode 100644 provider/kubernetes/fixtures/computeServiceWeights/2_path_without_weight_ingresses.yml create mode 100644 provider/kubernetes/fixtures/computeServiceWeights_endpoints.yml create mode 100644 provider/kubernetes/fixtures/computeServiceWeights_services.yml create mode 100644 provider/kubernetes/fixtures/divergingIngressDefinitions_endpoints.yml create mode 100644 provider/kubernetes/fixtures/divergingIngressDefinitions_ingresses.yml create mode 100644 provider/kubernetes/fixtures/divergingIngressDefinitions_services.yml create mode 100644 provider/kubernetes/fixtures/getPassHostHeader_ingresses.yml create mode 100644 provider/kubernetes/fixtures/getPassHostHeader_services.yml create mode 100644 provider/kubernetes/fixtures/getPassTLSCert_ingresses.yml create mode 100644 provider/kubernetes/fixtures/getPassTLSCert_services.yml create mode 100644 provider/kubernetes/fixtures/hostlessIngress_ingresses.yml create mode 100644 provider/kubernetes/fixtures/hostlessIngress_services.yml create mode 100644 provider/kubernetes/fixtures/ingressAnnotations_ingresses.yml create mode 100644 provider/kubernetes/fixtures/ingressAnnotations_secrets.yml create mode 100644 provider/kubernetes/fixtures/ingressAnnotations_services.yml create mode 100644 provider/kubernetes/fixtures/ingressClassAnnotation_endpoints.yml create mode 100644 provider/kubernetes/fixtures/ingressClassAnnotation_ingresses.yml create mode 100644 provider/kubernetes/fixtures/ingressClassAnnotation_services.yml create mode 100644 provider/kubernetes/fixtures/invalidPassHostHeaderValue_ingresses.yml create mode 100644 provider/kubernetes/fixtures/invalidPassHostHeaderValue_services.yml create mode 100644 provider/kubernetes/fixtures/invalidPassTLSCertValue_ingresses.yml create mode 100644 provider/kubernetes/fixtures/invalidPassTLSCertValue_services.yml create mode 100644 provider/kubernetes/fixtures/loadGlobalIngressWithExternalName_ingresses.yml create mode 100644 provider/kubernetes/fixtures/loadGlobalIngressWithExternalName_services.yml create mode 100644 provider/kubernetes/fixtures/loadGlobalIngressWithHttpsPortNames_endpoints.yml create mode 100644 provider/kubernetes/fixtures/loadGlobalIngressWithHttpsPortNames_ingresses.yml create mode 100644 provider/kubernetes/fixtures/loadGlobalIngressWithHttpsPortNames_services.yml create mode 100644 provider/kubernetes/fixtures/loadGlobalIngressWithPortNumbers_endpoints.yml create mode 100644 provider/kubernetes/fixtures/loadGlobalIngressWithPortNumbers_ingresses.yml create mode 100644 provider/kubernetes/fixtures/loadGlobalIngressWithPortNumbers_services.yml create mode 100644 provider/kubernetes/fixtures/loadIngressesBasicAuth_ingresses.yml create mode 100644 provider/kubernetes/fixtures/loadIngressesBasicAuth_secrets.yml create mode 100644 provider/kubernetes/fixtures/loadIngressesBasicAuth_services.yml create mode 100644 provider/kubernetes/fixtures/loadIngressesForwardAuthMissingURL_endpoints.yml create mode 100644 provider/kubernetes/fixtures/loadIngressesForwardAuthMissingURL_ingresses.yml create mode 100644 provider/kubernetes/fixtures/loadIngressesForwardAuthMissingURL_services.yml create mode 100644 provider/kubernetes/fixtures/loadIngressesForwardAuthWithTLSSecretFailures_endpoints.yml create mode 100644 provider/kubernetes/fixtures/loadIngressesForwardAuthWithTLSSecretFailures_ingresses.yml create mode 100644 provider/kubernetes/fixtures/loadIngressesForwardAuthWithTLSSecretFailures_services.yml create mode 100644 provider/kubernetes/fixtures/loadIngressesForwardAuthWithTLSSecret_endpoints.yml create mode 100644 provider/kubernetes/fixtures/loadIngressesForwardAuthWithTLSSecret_ingresses.yml create mode 100644 provider/kubernetes/fixtures/loadIngressesForwardAuthWithTLSSecret_secrets.yml create mode 100644 provider/kubernetes/fixtures/loadIngressesForwardAuthWithTLSSecret_services.yml create mode 100644 provider/kubernetes/fixtures/loadIngressesForwardAuth_endpoints.yml create mode 100644 provider/kubernetes/fixtures/loadIngressesForwardAuth_ingresses.yml create mode 100644 provider/kubernetes/fixtures/loadIngressesForwardAuth_services.yml create mode 100644 provider/kubernetes/fixtures/loadIngresses_endpoints.yml create mode 100644 provider/kubernetes/fixtures/loadIngresses_ingresses.yml create mode 100644 provider/kubernetes/fixtures/loadIngresses_services.yml create mode 100644 provider/kubernetes/fixtures/missingResources_endpoints.yml create mode 100644 provider/kubernetes/fixtures/missingResources_ingresses.yml create mode 100644 provider/kubernetes/fixtures/missingResources_services.yml create mode 100644 provider/kubernetes/fixtures/multiPortServices_endpoints.yml create mode 100644 provider/kubernetes/fixtures/multiPortServices_ingresses.yml create mode 100644 provider/kubernetes/fixtures/multiPortServices_services.yml create mode 100644 provider/kubernetes/fixtures/onlyReferencesServicesFromOwnNamespace_ingresses.yml create mode 100644 provider/kubernetes/fixtures/onlyReferencesServicesFromOwnNamespace_services.yml create mode 100644 provider/kubernetes/fixtures/percentageWeightServiceAnnotation_endpoints.yml create mode 100644 provider/kubernetes/fixtures/percentageWeightServiceAnnotation_ingresses.yml create mode 100644 provider/kubernetes/fixtures/percentageWeightServiceAnnotation_services.yml create mode 100644 provider/kubernetes/fixtures/priorityHeaderValue_ingresses.yml create mode 100644 provider/kubernetes/fixtures/priorityHeaderValue_services.yml create mode 100644 provider/kubernetes/fixtures/providerUpdateIngressStatus_services.yml create mode 100644 provider/kubernetes/fixtures/serviceAnnotations_endpoints.yml create mode 100644 provider/kubernetes/fixtures/serviceAnnotations_ingresses.yml create mode 100644 provider/kubernetes/fixtures/serviceAnnotations_services.yml create mode 100644 provider/kubernetes/fixtures/tLSSecretLoad_ingresses.yml create mode 100644 provider/kubernetes/fixtures/tLSSecretLoad_secrets.yml create mode 100644 provider/kubernetes/fixtures/tLSSecretLoad_services.yml create mode 100644 provider/kubernetes/fixtures/templateBreakingIngresssValues_ingresses.yml create mode 100644 provider/kubernetes/parser_test.go diff --git a/Gopkg.lock b/Gopkg.lock index 15ebcb547..6ac534143 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -1716,13 +1716,11 @@ revision = "c4434f09ec131ecf30f986d5dcb1636508bfa49a" [[projects]] - branch = "containous-fork" digest = "1:84b9a5318d8ce3b8a9b1509bf15734f4f9dcd4decf9d9e9c7346a16c7b64d49e" name = "github.com/thoas/stats" packages = ["."] pruneopts = "NUT" revision = "4975baf6a358ed3ddaa42133996e1959f96c9300" - source = "github.com/containous/stats" [[projects]] branch = "master" @@ -2478,11 +2476,13 @@ "k8s.io/apimachinery/pkg/api/errors", "k8s.io/apimachinery/pkg/apis/meta/v1", "k8s.io/apimachinery/pkg/labels", + "k8s.io/apimachinery/pkg/runtime", "k8s.io/apimachinery/pkg/runtime/schema", "k8s.io/apimachinery/pkg/types", "k8s.io/apimachinery/pkg/util/intstr", "k8s.io/client-go/informers", "k8s.io/client-go/kubernetes", + "k8s.io/client-go/kubernetes/scheme", "k8s.io/client-go/rest", "k8s.io/client-go/tools/cache", ] diff --git a/provider/kubernetes/builder_configuration_test.go b/provider/kubernetes/builder_configuration_test.go index 2c04034c2..2f422664d 100644 --- a/provider/kubernetes/builder_configuration_test.go +++ b/provider/kubernetes/builder_configuration_test.go @@ -230,9 +230,25 @@ func auth(opt func(*types.Auth)) func(*types.Frontend) { } } -func basicAuth(users ...string) func(*types.Auth) { +func basicAuth(opts ...func(*types.Basic)) func(*types.Auth) { return func(a *types.Auth) { - a.Basic = &types.Basic{Users: users} + basic := &types.Basic{} + for _, opt := range opts { + opt(basic) + } + a.Basic = basic + } +} + +func baUsers(users ...string) func(*types.Basic) { + return func(b *types.Basic) { + b.Users = users + } +} + +func baRemoveHeaders() func(*types.Basic) { + return func(b *types.Basic) { + b.RemoveHeader = true } } diff --git a/provider/kubernetes/builder_endpoint_test.go b/provider/kubernetes/builder_endpoint_test.go index 365d2c27c..84de61c3c 100644 --- a/provider/kubernetes/builder_endpoint_test.go +++ b/provider/kubernetes/builder_endpoint_test.go @@ -61,13 +61,6 @@ func eAddress(ip string) func(*corev1.EndpointAddress) { } } -func eAddressWithTargetRef(targetRef, ip string) func(*corev1.EndpointAddress) { - return func(address *corev1.EndpointAddress) { - address.TargetRef = &corev1.ObjectReference{Name: targetRef} - address.IP = ip - } -} - func ePorts(opts ...func(port *corev1.EndpointPort)) func(*corev1.EndpointSubset) { return func(spec *corev1.EndpointSubset) { for _, opt := range opts { diff --git a/provider/kubernetes/builder_service_test.go b/provider/kubernetes/builder_service_test.go index 1225be328..e9c8c81ce 100644 --- a/provider/kubernetes/builder_service_test.go +++ b/provider/kubernetes/builder_service_test.go @@ -35,15 +35,6 @@ func sUID(value types.UID) func(*corev1.Service) { } } -func sAnnotation(name string, value string) func(*corev1.Service) { - return func(s *corev1.Service) { - if s.Annotations == nil { - s.Annotations = make(map[string]string) - } - s.Annotations[name] = value - } -} - func sSpec(opts ...func(*corev1.ServiceSpec)) func(*corev1.Service) { return func(s *corev1.Service) { spec := &corev1.ServiceSpec{} @@ -54,30 +45,6 @@ func sSpec(opts ...func(*corev1.ServiceSpec)) func(*corev1.Service) { } } -func sLoadBalancerStatus(opts ...func(*corev1.LoadBalancerStatus)) func(service *corev1.Service) { - return func(s *corev1.Service) { - loadBalancer := &corev1.LoadBalancerStatus{} - for _, opt := range opts { - if opt != nil { - opt(loadBalancer) - } - } - s.Status = corev1.ServiceStatus{ - LoadBalancer: *loadBalancer, - } - } -} - -func sLoadBalancerIngress(ip string, hostname string) func(*corev1.LoadBalancerStatus) { - return func(status *corev1.LoadBalancerStatus) { - ingress := corev1.LoadBalancerIngress{ - IP: ip, - Hostname: hostname, - } - status.Ingress = append(status.Ingress, ingress) - } -} - func clusterIP(ip string) func(*corev1.ServiceSpec) { return func(spec *corev1.ServiceSpec) { spec.ClusterIP = ip diff --git a/provider/kubernetes/client_mock_test.go b/provider/kubernetes/client_mock_test.go index a855e2388..1c82a9624 100644 --- a/provider/kubernetes/client_mock_test.go +++ b/provider/kubernetes/client_mock_test.go @@ -1,21 +1,57 @@ package kubernetes import ( + "fmt" + "io/ioutil" + corev1 "k8s.io/api/core/v1" extensionsv1beta1 "k8s.io/api/extensions/v1beta1" + v1beta12 "k8s.io/api/extensions/v1beta1" ) +var _ Client = (*clientMock)(nil) + type clientMock struct { ingresses []*extensionsv1beta1.Ingress services []*corev1.Service secrets []*corev1.Secret endpoints []*corev1.Endpoints - watchChan chan interface{} apiServiceError error apiSecretError error apiEndpointsError error apiIngressStatusError error + + watchChan chan interface{} +} + +func newClientMock(paths ...string) clientMock { + var c clientMock + + for _, path := range paths { + yamlContent, err := ioutil.ReadFile(path) + if err != nil { + panic(err) + } + + k8sObjects := MustDecodeYaml(yamlContent) + for _, obj := range k8sObjects { + switch o := obj.(type) { + case *corev1.Service: + c.services = append(c.services, o) + case *corev1.Secret: + c.secrets = append(c.secrets, o) + case *corev1.Endpoints: + c.endpoints = append(c.endpoints, o) + case *v1beta12.Ingress: + c.ingresses = append(c.ingresses, o) + default: + panic(fmt.Sprintf("Unknown runtime object %+v %T", o, o)) + } + } + } + + return c } func (c clientMock) GetIngresses() []*extensionsv1beta1.Ingress { diff --git a/provider/kubernetes/fixtures/computeServiceWeights/1_path_2_service_ingresses.yml b/provider/kubernetes/fixtures/computeServiceWeights/1_path_2_service_ingresses.yml new file mode 100644 index 000000000..b8860dceb --- /dev/null +++ b/provider/kubernetes/fixtures/computeServiceWeights/1_path_2_service_ingresses.yml @@ -0,0 +1,21 @@ +apiVersion: extensions/v1beta1 +kind: Ingress +metadata: + annotations: + ingress.kubernetes.io/service-weights: |2 + + service1: 10% + namespace: testing +spec: + rules: + - host: foo.test + http: + paths: + - backend: + serviceName: service1 + servicePort: 8080 + path: /foo + - backend: + serviceName: service2 + servicePort: 8080 + path: /foo diff --git a/provider/kubernetes/fixtures/computeServiceWeights/1_path_4_service_ingresses.yml b/provider/kubernetes/fixtures/computeServiceWeights/1_path_4_service_ingresses.yml new file mode 100644 index 000000000..5924e4637 --- /dev/null +++ b/provider/kubernetes/fixtures/computeServiceWeights/1_path_4_service_ingresses.yml @@ -0,0 +1,31 @@ +apiVersion: extensions/v1beta1 +kind: Ingress +metadata: + annotations: + ingress.kubernetes.io/service-weights: |2 + + service1: 20% + service2: 40% + service3: 40% + namespace: testing +spec: + rules: + - host: foo.test + http: + paths: + - backend: + serviceName: service1 + servicePort: 8080 + path: /foo + - backend: + serviceName: service2 + servicePort: 8080 + path: /foo + - backend: + serviceName: service3 + servicePort: 8080 + path: /foo + - backend: + serviceName: service4 + servicePort: 8080 + path: /foo diff --git a/provider/kubernetes/fixtures/computeServiceWeights/2_path_2_service_ingresses.yml b/provider/kubernetes/fixtures/computeServiceWeights/2_path_2_service_ingresses.yml new file mode 100644 index 000000000..8e6426ebb --- /dev/null +++ b/provider/kubernetes/fixtures/computeServiceWeights/2_path_2_service_ingresses.yml @@ -0,0 +1,29 @@ +apiVersion: extensions/v1beta1 +kind: Ingress +metadata: + annotations: + ingress.kubernetes.io/service-weights: |2 + + service1: 60% + namespace: testing +spec: + rules: + - host: foo.test + http: + paths: + - backend: + serviceName: service1 + servicePort: 8080 + path: /foo + - backend: + serviceName: service2 + servicePort: 8080 + path: /foo + - backend: + serviceName: service1 + servicePort: 8080 + path: /bar + - backend: + serviceName: service3 + servicePort: 8080 + path: /bar diff --git a/provider/kubernetes/fixtures/computeServiceWeights/2_path_3_service_ingresses.yml b/provider/kubernetes/fixtures/computeServiceWeights/2_path_3_service_ingresses.yml new file mode 100644 index 000000000..820399ae2 --- /dev/null +++ b/provider/kubernetes/fixtures/computeServiceWeights/2_path_3_service_ingresses.yml @@ -0,0 +1,30 @@ +apiVersion: extensions/v1beta1 +kind: Ingress +metadata: + annotations: + ingress.kubernetes.io/service-weights: |2 + + service1: 20% + service3: 20% + namespace: testing +spec: + rules: + - host: foo.test + http: + paths: + - backend: + serviceName: service1 + servicePort: 8080 + path: /foo + - backend: + serviceName: service2 + servicePort: 8080 + path: /foo + - backend: + serviceName: service2 + servicePort: 8080 + path: /bar + - backend: + serviceName: service3 + servicePort: 8080 + path: /bar diff --git a/provider/kubernetes/fixtures/computeServiceWeights/2_path_no_service_ingresses.yml b/provider/kubernetes/fixtures/computeServiceWeights/2_path_no_service_ingresses.yml new file mode 100644 index 000000000..36054e7b6 --- /dev/null +++ b/provider/kubernetes/fixtures/computeServiceWeights/2_path_no_service_ingresses.yml @@ -0,0 +1,23 @@ +apiVersion: extensions/v1beta1 +kind: Ingress +metadata: + annotations: + ingress.kubernetes.io/service-weights: |2 + + service1: 20% + service2: 40% + service3: 40% + namespace: testing +spec: + rules: + - host: foo.test + http: + paths: + - backend: + serviceName: noservice + servicePort: 8080 + path: /foo + - backend: + serviceName: noservice + servicePort: 8080 + path: /bar diff --git a/provider/kubernetes/fixtures/computeServiceWeights/2_path_overflow_ingresses.yml b/provider/kubernetes/fixtures/computeServiceWeights/2_path_overflow_ingresses.yml new file mode 100644 index 000000000..0766d3777 --- /dev/null +++ b/provider/kubernetes/fixtures/computeServiceWeights/2_path_overflow_ingresses.yml @@ -0,0 +1,22 @@ +apiVersion: extensions/v1beta1 +kind: Ingress +metadata: + annotations: + ingress.kubernetes.io/service-weights: |2 + + service1: 70% + service2: 80% + namespace: testing +spec: + rules: + - host: foo.test + http: + paths: + - backend: + serviceName: service1 + servicePort: 8080 + path: /foo + - backend: + serviceName: service2 + servicePort: 8080 + path: /foo diff --git a/provider/kubernetes/fixtures/computeServiceWeights/2_path_without_weight_ingresses.yml b/provider/kubernetes/fixtures/computeServiceWeights/2_path_without_weight_ingresses.yml new file mode 100644 index 000000000..8cd71702f --- /dev/null +++ b/provider/kubernetes/fixtures/computeServiceWeights/2_path_without_weight_ingresses.yml @@ -0,0 +1,19 @@ +apiVersion: extensions/v1beta1 +kind: Ingress +metadata: + annotations: + ingress.kubernetes.io/service-weights: "" + namespace: testing +spec: + rules: + - host: foo.test + http: + paths: + - backend: + serviceName: service1 + servicePort: 8080 + path: /foo + - backend: + serviceName: service2 + servicePort: 8080 + path: /bar diff --git a/provider/kubernetes/fixtures/computeServiceWeights_endpoints.yml b/provider/kubernetes/fixtures/computeServiceWeights_endpoints.yml new file mode 100644 index 000000000..ccf97a97f --- /dev/null +++ b/provider/kubernetes/fixtures/computeServiceWeights_endpoints.yml @@ -0,0 +1,62 @@ +apiVersion: v1 +kind: Endpoints +metadata: + name: service1 + namespace: testing +subsets: +- addresses: + - ip: 10.10.0.1 + ports: + - port: 8080 +- addresses: + - ip: 10.21.0.2 + ports: + - port: 8080 + +--- +apiVersion: v1 +kind: Endpoints +metadata: + name: service2 + namespace: testing +subsets: +- addresses: + - ip: 10.10.0.3 + ports: + - port: 8080 + +--- +apiVersion: v1 +kind: Endpoints +metadata: + name: service3 + namespace: testing +subsets: +- addresses: + - ip: 10.10.0.4 + ports: + - port: 8080 +- addresses: + - ip: 10.21.0.5 + ports: + - port: 8080 +- addresses: + - ip: 10.21.0.6 + ports: + - port: 8080 +- addresses: + - ip: 10.21.0.7 + ports: + - port: 8080 + +--- +apiVersion: v1 +kind: Endpoints +metadata: + name: service4 + namespace: testing +subsets: +- addresses: + - ip: 10.10.0.7 + ports: + - port: 8080 diff --git a/provider/kubernetes/fixtures/computeServiceWeights_services.yml b/provider/kubernetes/fixtures/computeServiceWeights_services.yml new file mode 100644 index 000000000..e284f20fc --- /dev/null +++ b/provider/kubernetes/fixtures/computeServiceWeights_services.yml @@ -0,0 +1,26 @@ +apiVersion: v1 +kind: Service +metadata: + name: service1 + namespace: testing + +--- +apiVersion: v1 +kind: Service +metadata: + name: service2 + namespace: testing + +--- +apiVersion: v1 +kind: Service +metadata: + name: service3 + namespace: testing + +--- +apiVersion: v1 +kind: Service +metadata: + name: service4 + namespace: testing diff --git a/provider/kubernetes/fixtures/divergingIngressDefinitions_endpoints.yml b/provider/kubernetes/fixtures/divergingIngressDefinitions_endpoints.yml new file mode 100644 index 000000000..0a92bc5d1 --- /dev/null +++ b/provider/kubernetes/fixtures/divergingIngressDefinitions_endpoints.yml @@ -0,0 +1,16 @@ +apiVersion: v1 +kind: Endpoints +metadata: + name: service1 + namespace: testing +subsets: +- addresses: + - ip: 10.10.0.1 + ports: + - name: http + port: 80 +- addresses: + - ip: 10.10.0.2 + ports: + - name: http + port: 80 diff --git a/provider/kubernetes/fixtures/divergingIngressDefinitions_ingresses.yml b/provider/kubernetes/fixtures/divergingIngressDefinitions_ingresses.yml new file mode 100644 index 000000000..f7a827dfa --- /dev/null +++ b/provider/kubernetes/fixtures/divergingIngressDefinitions_ingresses.yml @@ -0,0 +1,26 @@ +apiVersion: extensions/v1beta1 +kind: Ingress +metadata: + namespace: testing +spec: + rules: + - host: host-a + http: + paths: + - backend: + serviceName: service1 + servicePort: "80" + +--- +apiVersion: extensions/v1beta1 +kind: Ingress +metadata: + namespace: testing +spec: + rules: + - host: host-a + http: + paths: + - backend: + serviceName: missing + servicePort: "80" diff --git a/provider/kubernetes/fixtures/divergingIngressDefinitions_services.yml b/provider/kubernetes/fixtures/divergingIngressDefinitions_services.yml new file mode 100644 index 000000000..0b4176fb9 --- /dev/null +++ b/provider/kubernetes/fixtures/divergingIngressDefinitions_services.yml @@ -0,0 +1,10 @@ +apiVersion: v1 +kind: Service +metadata: + name: service1 + namespace: testing +spec: + clusterIP: 10.0.0.1 + ports: + - name: http + port: 80 diff --git a/provider/kubernetes/fixtures/getPassHostHeader_ingresses.yml b/provider/kubernetes/fixtures/getPassHostHeader_ingresses.yml new file mode 100644 index 000000000..203f91af9 --- /dev/null +++ b/provider/kubernetes/fixtures/getPassHostHeader_ingresses.yml @@ -0,0 +1,13 @@ +apiVersion: extensions/v1beta1 +kind: Ingress +metadata: + namespace: awesome +spec: + rules: + - host: foo + http: + paths: + - backend: + serviceName: service1 + servicePort: 801 + path: /bar diff --git a/provider/kubernetes/fixtures/getPassHostHeader_services.yml b/provider/kubernetes/fixtures/getPassHostHeader_services.yml new file mode 100644 index 000000000..ce117e33e --- /dev/null +++ b/provider/kubernetes/fixtures/getPassHostHeader_services.yml @@ -0,0 +1,9 @@ +apiVersion: v1 +kind: Service +metadata: + name: service1 + namespace: awesome +spec: + ports: + - name: http + port: 801 diff --git a/provider/kubernetes/fixtures/getPassTLSCert_ingresses.yml b/provider/kubernetes/fixtures/getPassTLSCert_ingresses.yml new file mode 100644 index 000000000..8d21daf12 --- /dev/null +++ b/provider/kubernetes/fixtures/getPassTLSCert_ingresses.yml @@ -0,0 +1,13 @@ +apiVersion: extensions/v1beta1 +kind: Ingress +metadata: + namespace: awesome +spec: + rules: + - host: foo + http: + paths: + - backend: + serviceName: service1 + servicePort: 80 + path: /bar diff --git a/provider/kubernetes/fixtures/getPassTLSCert_services.yml b/provider/kubernetes/fixtures/getPassTLSCert_services.yml new file mode 100644 index 000000000..ce117e33e --- /dev/null +++ b/provider/kubernetes/fixtures/getPassTLSCert_services.yml @@ -0,0 +1,9 @@ +apiVersion: v1 +kind: Service +metadata: + name: service1 + namespace: awesome +spec: + ports: + - name: http + port: 801 diff --git a/provider/kubernetes/fixtures/hostlessIngress_ingresses.yml b/provider/kubernetes/fixtures/hostlessIngress_ingresses.yml new file mode 100644 index 000000000..78347af6b --- /dev/null +++ b/provider/kubernetes/fixtures/hostlessIngress_ingresses.yml @@ -0,0 +1,12 @@ +apiVersion: extensions/v1beta1 +kind: Ingress +metadata: + namespace: awesome +spec: + rules: + - http: + paths: + - backend: + serviceName: service1 + servicePort: 801 + path: /bar diff --git a/provider/kubernetes/fixtures/hostlessIngress_services.yml b/provider/kubernetes/fixtures/hostlessIngress_services.yml new file mode 100644 index 000000000..0dfbb956c --- /dev/null +++ b/provider/kubernetes/fixtures/hostlessIngress_services.yml @@ -0,0 +1,10 @@ +apiVersion: v1 +kind: Service +metadata: + name: service1 + namespace: awesome +spec: + clusterIP: 10.0.0.1 + ports: + - name: http + port: 801 diff --git a/provider/kubernetes/fixtures/ingressAnnotations_ingresses.yml b/provider/kubernetes/fixtures/ingressAnnotations_ingresses.yml new file mode 100644 index 000000000..7a39fc6aa --- /dev/null +++ b/provider/kubernetes/fixtures/ingressAnnotations_ingresses.yml @@ -0,0 +1,431 @@ +apiVersion: extensions/v1beta1 +kind: Ingress +metadata: + annotations: + ingress.kubernetes.io/preserve-host: "false" + namespace: testing +spec: + rules: + - host: foo + http: + paths: + - backend: + serviceName: service1 + servicePort: 80 + path: /bar + +--- +apiVersion: extensions/v1beta1 +kind: Ingress +metadata: + annotations: + ingress.kubernetes.io/preserve-host: "true" + kubernetes.io/ingress.class: traefik + namespace: testing +spec: + rules: + - host: other + http: + paths: + - backend: + serviceName: service1 + servicePort: 80 + path: /stuff + +--- +apiVersion: extensions/v1beta1 +kind: Ingress +metadata: + annotations: + ingress.kubernetes.io/pass-client-tls-cert: |2 + + pem: true + infos: + notafter: true + notbefore: true + sans: true + subject: + country: true + province: true + locality: true + organization: true + commonname: true + serialnumber: true + domaincomponent: true + issuer: + country: true + province: true + locality: true + organization: true + commonname: true + serialnumber: true + domaincomponent: true + ingress.kubernetes.io/pass-tls-cert: "true" + kubernetes.io/ingress.class: traefik + namespace: testing +spec: + rules: + - host: other + http: + paths: + - backend: + serviceName: service1 + servicePort: 80 + path: /sslstuff + +--- +apiVersion: extensions/v1beta1 +kind: Ingress +metadata: + annotations: + ingress.kubernetes.io/frontend-entry-points: http,https + kubernetes.io/ingress.class: traefik + namespace: testing +spec: + rules: + - host: other + http: + paths: + - backend: + serviceName: service1 + servicePort: 80 + path: / + +--- +apiVersion: extensions/v1beta1 +kind: Ingress +metadata: + annotations: + ingress.kubernetes.io/auth-secret: mySecret + ingress.kubernetes.io/auth-type: basic + namespace: testing +spec: + rules: + - host: basic + http: + paths: + - backend: + serviceName: service1 + servicePort: 80 + path: /auth + +--- +apiVersion: extensions/v1beta1 +kind: Ingress +metadata: + annotations: + kubernetes.io/ingress.class: traefik-other + namespace: testing +spec: + rules: + - host: herp + http: + paths: + - backend: + serviceName: service2 + servicePort: 80 + path: /derp + +--- +apiVersion: extensions/v1beta1 +kind: Ingress +metadata: + annotations: + ingress.kubernetes.io/whitelist-source-range: 1.1.1.1/24, 1234:abcd::42/32 + ingress.kubernetes.io/whitelist-x-forwarded-for: "true" + namespace: testing +spec: + rules: + - host: test + http: + paths: + - backend: + serviceName: service1 + servicePort: 80 + path: /whitelist-source-range + +--- +apiVersion: extensions/v1beta1 +kind: Ingress +metadata: + annotations: + ingress.kubernetes.io/rewrite-target: / + namespace: testing +spec: + rules: + - host: rewrite + http: + paths: + - backend: + serviceName: service1 + servicePort: 80 + path: /api + +--- +apiVersion: extensions/v1beta1 +kind: Ingress +metadata: + annotations: + ingress.kubernetes.io/auth-realm: customized + namespace: testing +spec: + rules: + - host: auth-realm-customized + http: + paths: + - backend: + serviceName: service1 + servicePort: 80 + path: /auth-realm-customized + +--- +apiVersion: extensions/v1beta1 +kind: Ingress +metadata: + annotations: + ingress.kubernetes.io/redirect-entry-point: https + namespace: testing +spec: + rules: + - host: redirect + http: + paths: + - backend: + serviceName: service1 + servicePort: 80 + path: /https + +--- +apiVersion: extensions/v1beta1 +kind: Ingress +metadata: + annotations: + ingress.kubernetes.io/error-pages: |2 + + foo: + status: + - "123" + - "456" + backend: bar + query: /bar + kubernetes.io/ingress.class: traefik + namespace: testing +spec: + rules: + - host: error-pages + http: + paths: + - backend: + serviceName: service1 + servicePort: 80 + path: /errorpages + +--- +apiVersion: extensions/v1beta1 +kind: Ingress +metadata: + annotations: + ingress.kubernetes.io/rate-limit: |2 + + extractorfunc: client.ip + rateset: + bar: + period: 3s + average: 6 + burst: 9 + foo: + period: 6s + average: 12 + burst: 18 + kubernetes.io/ingress.class: traefik + namespace: testing +spec: + rules: + - host: rate-limit + http: + paths: + - backend: + serviceName: service1 + servicePort: 80 + path: /ratelimit + +--- +apiVersion: extensions/v1beta1 +kind: Ingress +metadata: + annotations: + ingress.kubernetes.io/app-root: /root + namespace: testing +spec: + rules: + - host: root + http: + paths: + - backend: + serviceName: service1 + servicePort: 80 + path: / + - backend: + serviceName: service1 + servicePort: 80 + path: /root1 + +--- +apiVersion: extensions/v1beta1 +kind: Ingress +metadata: + annotations: + ingress.kubernetes.io/app-root: /root2 + ingress.kubernetes.io/rewrite-target: /abc + namespace: testing +spec: + rules: + - host: root2 + http: + paths: + - backend: + serviceName: service2 + servicePort: 80 + path: / + +--- +apiVersion: extensions/v1beta1 +kind: Ingress +metadata: + annotations: + ingress.kubernetes.io/rewrite-target: /abc + ingress.kubernetes.io/rule-type: ReplacePath + namespace: testing +spec: + rules: + - host: root2 + http: + paths: + - backend: + serviceName: service2 + servicePort: 80 + path: / + +--- +apiVersion: extensions/v1beta1 +kind: Ingress +metadata: + annotations: + ingress.kubernetes.io/app-root: /root + namespace: testing +spec: + rules: + - host: root3 + http: + paths: + - backend: + serviceName: service1 + servicePort: 80 + +--- +apiVersion: extensions/v1beta1 +kind: Ingress +metadata: + annotations: + ingress.kubernetes.io/allowed-hosts: foo, fii, fuu + ingress.kubernetes.io/browser-xss-filter: "true" + ingress.kubernetes.io/content-security-policy: foo + ingress.kubernetes.io/content-type-nosniff: "true" + ingress.kubernetes.io/custom-browser-xss-value: foo + ingress.kubernetes.io/custom-frame-options-value: foo + ingress.kubernetes.io/custom-request-headers: 'Access-Control-Allow-Methods:POST,GET,OPTIONS + || Content-type: application/json; charset=utf-8' + ingress.kubernetes.io/custom-response-headers: 'Access-Control-Allow-Methods:POST,GET,OPTIONS + || Content-type: application/json; charset=utf-8' + ingress.kubernetes.io/force-hsts: "true" + ingress.kubernetes.io/frame-deny: "true" + ingress.kubernetes.io/hsts-include-subdomains: "true" + ingress.kubernetes.io/hsts-max-age: "666" + ingress.kubernetes.io/hsts-preload: "true" + ingress.kubernetes.io/is-development: "true" + ingress.kubernetes.io/proxy-headers: foo, fii, fuu + ingress.kubernetes.io/public-key: foo + ingress.kubernetes.io/referrer-policy: foo + ingress.kubernetes.io/ssl-force-host: "true" + ingress.kubernetes.io/ssl-host: foo + ingress.kubernetes.io/ssl-proxy-headers: 'Access-Control-Allow-Methods:POST,GET,OPTIONS + || Content-type: application/json; charset=utf-8' + ingress.kubernetes.io/ssl-redirect: "true" + ingress.kubernetes.io/ssl-temporary-redirect: "true" + kubernetes.io/ingress.class: traefik + namespace: testing +spec: + rules: + - host: custom-headers + http: + paths: + - backend: + serviceName: service1 + servicePort: 80 + path: /customheaders + +--- +apiVersion: extensions/v1beta1 +kind: Ingress +metadata: + annotations: + ingress.kubernetes.io/protocol: h2c + namespace: testing +spec: + rules: + - host: protocol + http: + paths: + - backend: + serviceName: service1 + servicePort: 80 + path: /valid + +--- +apiVersion: extensions/v1beta1 +kind: Ingress +metadata: + annotations: + ingress.kubernetes.io/protocol: foobar + namespace: testing +spec: + rules: + - host: protocol + http: + paths: + - backend: + serviceName: service1 + servicePort: 80 + path: /notvalid + +--- +apiVersion: extensions/v1beta1 +kind: Ingress +metadata: + annotations: + ingress.kubernetes.io/protocol: http + namespace: testing +spec: + rules: + - host: protocol + http: + paths: + - backend: + serviceName: serviceHTTPS + servicePort: 443 + path: /missmatch + +--- +apiVersion: extensions/v1beta1 +kind: Ingress +metadata: + namespace: testing +spec: + rules: + - host: protocol + http: + paths: + - backend: + serviceName: serviceHTTPS + servicePort: 443 + path: /noAnnotation diff --git a/provider/kubernetes/fixtures/ingressAnnotations_secrets.yml b/provider/kubernetes/fixtures/ingressAnnotations_secrets.yml new file mode 100644 index 000000000..50a504e96 --- /dev/null +++ b/provider/kubernetes/fixtures/ingressAnnotations_secrets.yml @@ -0,0 +1,7 @@ +apiVersion: v1 +data: + auth: bXlVc2VyOm15RW5jb2RlZFBX +kind: Secret +metadata: + name: mySecret + namespace: testing diff --git a/provider/kubernetes/fixtures/ingressAnnotations_services.yml b/provider/kubernetes/fixtures/ingressAnnotations_services.yml new file mode 100644 index 000000000..208b0920f --- /dev/null +++ b/provider/kubernetes/fixtures/ingressAnnotations_services.yml @@ -0,0 +1,37 @@ +apiVersion: v1 +kind: Service +metadata: + name: service1 + namespace: testing +spec: + clusterIP: 10.0.0.1 + externalName: example.com + ports: + - name: http + port: 80 + type: ExternalName + +--- +apiVersion: v1 +kind: Service +metadata: + name: service2 + namespace: testing +spec: + clusterIP: 10.0.0.2 + ports: + - port: 802 + +--- +apiVersion: v1 +kind: Service +metadata: + name: serviceHTTPS + namespace: testing +spec: + clusterIP: 10.0.0.3 + externalName: example.com + ports: + - name: https + port: 443 + type: ExternalName diff --git a/provider/kubernetes/fixtures/ingressClassAnnotation_endpoints.yml b/provider/kubernetes/fixtures/ingressClassAnnotation_endpoints.yml new file mode 100644 index 000000000..b87d22dcd --- /dev/null +++ b/provider/kubernetes/fixtures/ingressClassAnnotation_endpoints.yml @@ -0,0 +1,11 @@ +apiVersion: v1 +kind: Endpoints +metadata: + name: service2 + namespace: testing +subsets: +- addresses: + - ip: 10.10.0.1 + ports: + - name: http + port: 80 diff --git a/provider/kubernetes/fixtures/ingressClassAnnotation_ingresses.yml b/provider/kubernetes/fixtures/ingressClassAnnotation_ingresses.yml new file mode 100644 index 000000000..95a9e5f69 --- /dev/null +++ b/provider/kubernetes/fixtures/ingressClassAnnotation_ingresses.yml @@ -0,0 +1,81 @@ +apiVersion: extensions/v1beta1 +kind: Ingress +metadata: + annotations: + kubernetes.io/ingress.class: traefik + namespace: testing +spec: + rules: + - host: other + http: + paths: + - backend: + serviceName: service1 + servicePort: 80 + path: /stuff + +--- +apiVersion: extensions/v1beta1 +kind: Ingress +metadata: + annotations: + kubernetes.io/ingress.class: "" + namespace: testing +spec: + rules: + - host: other + http: + paths: + - backend: + serviceName: service1 + servicePort: 80 + path: /sslstuff + +--- +apiVersion: extensions/v1beta1 +kind: Ingress +metadata: + namespace: testing +spec: + rules: + - host: other + http: + paths: + - backend: + serviceName: service1 + servicePort: 80 + path: / + +--- +apiVersion: extensions/v1beta1 +kind: Ingress +metadata: + annotations: + kubernetes.io/ingress.class: traefik-other + namespace: testing +spec: + rules: + - host: foo + http: + paths: + - backend: + serviceName: service1 + servicePort: 80 + path: /bar + +--- +apiVersion: extensions/v1beta1 +kind: Ingress +metadata: + annotations: + kubernetes.io/ingress.class: custom + namespace: testing +spec: + rules: + - host: foo + http: + paths: + - backend: + serviceName: service2 + servicePort: 80 + path: /bar diff --git a/provider/kubernetes/fixtures/ingressClassAnnotation_services.yml b/provider/kubernetes/fixtures/ingressClassAnnotation_services.yml new file mode 100644 index 000000000..1f44c7f2b --- /dev/null +++ b/provider/kubernetes/fixtures/ingressClassAnnotation_services.yml @@ -0,0 +1,24 @@ +apiVersion: v1 +kind: Service +metadata: + name: service1 + namespace: testing +spec: + clusterIP: 10.0.0.1 + externalName: example.com + ports: + - name: http + port: 80 + type: ExternalName + +--- +apiVersion: v1 +kind: Service +metadata: + name: service2 + namespace: testing +spec: + clusterIP: 10.0.0.2 + ports: + - name: http + port: 80 diff --git a/provider/kubernetes/fixtures/invalidPassHostHeaderValue_ingresses.yml b/provider/kubernetes/fixtures/invalidPassHostHeaderValue_ingresses.yml new file mode 100644 index 000000000..4392749b7 --- /dev/null +++ b/provider/kubernetes/fixtures/invalidPassHostHeaderValue_ingresses.yml @@ -0,0 +1,15 @@ +apiVersion: extensions/v1beta1 +kind: Ingress +metadata: + annotations: + ingress.kubernetes.io/preserve-host: herpderp + namespace: testing +spec: + rules: + - host: foo + http: + paths: + - backend: + serviceName: service1 + servicePort: 80 + path: /bar diff --git a/provider/kubernetes/fixtures/invalidPassHostHeaderValue_services.yml b/provider/kubernetes/fixtures/invalidPassHostHeaderValue_services.yml new file mode 100644 index 000000000..400026c21 --- /dev/null +++ b/provider/kubernetes/fixtures/invalidPassHostHeaderValue_services.yml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: Service +metadata: + name: service1 + namespace: testing +spec: + clusterIP: 10.0.0.1 + externalName: example.com + ports: + - name: http + port: 80 + type: ExternalName diff --git a/provider/kubernetes/fixtures/invalidPassTLSCertValue_ingresses.yml b/provider/kubernetes/fixtures/invalidPassTLSCertValue_ingresses.yml new file mode 100644 index 000000000..c5792945d --- /dev/null +++ b/provider/kubernetes/fixtures/invalidPassTLSCertValue_ingresses.yml @@ -0,0 +1,15 @@ +apiVersion: extensions/v1beta1 +kind: Ingress +metadata: + annotations: + ingress.kubernetes.io/pass-tls-cert: herpderp + namespace: testing +spec: + rules: + - host: foo + http: + paths: + - backend: + serviceName: service1 + servicePort: 80 + path: /bar diff --git a/provider/kubernetes/fixtures/invalidPassTLSCertValue_services.yml b/provider/kubernetes/fixtures/invalidPassTLSCertValue_services.yml new file mode 100644 index 000000000..400026c21 --- /dev/null +++ b/provider/kubernetes/fixtures/invalidPassTLSCertValue_services.yml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: Service +metadata: + name: service1 + namespace: testing +spec: + clusterIP: 10.0.0.1 + externalName: example.com + ports: + - name: http + port: 80 + type: ExternalName diff --git a/provider/kubernetes/fixtures/loadGlobalIngressWithExternalName_ingresses.yml b/provider/kubernetes/fixtures/loadGlobalIngressWithExternalName_ingresses.yml new file mode 100644 index 000000000..59d5a17e8 --- /dev/null +++ b/provider/kubernetes/fixtures/loadGlobalIngressWithExternalName_ingresses.yml @@ -0,0 +1,8 @@ +apiVersion: extensions/v1beta1 +kind: Ingress +metadata: + namespace: testing +spec: + backend: + serviceName: service1 + servicePort: 80 diff --git a/provider/kubernetes/fixtures/loadGlobalIngressWithExternalName_services.yml b/provider/kubernetes/fixtures/loadGlobalIngressWithExternalName_services.yml new file mode 100644 index 000000000..a81cb162f --- /dev/null +++ b/provider/kubernetes/fixtures/loadGlobalIngressWithExternalName_services.yml @@ -0,0 +1,10 @@ +apiVersion: v1 +kind: Service +metadata: + name: service1 + namespace: testing +spec: + externalName: some-external-name + ports: + - port: 80 + type: ExternalName diff --git a/provider/kubernetes/fixtures/loadGlobalIngressWithHttpsPortNames_endpoints.yml b/provider/kubernetes/fixtures/loadGlobalIngressWithHttpsPortNames_endpoints.yml new file mode 100644 index 000000000..6bcbf6730 --- /dev/null +++ b/provider/kubernetes/fixtures/loadGlobalIngressWithHttpsPortNames_endpoints.yml @@ -0,0 +1,11 @@ +apiVersion: v1 +kind: Endpoints +metadata: + name: service1 + namespace: testing +subsets: +- addresses: + - ip: 10.10.0.1 + ports: + - name: https-global + port: 8080 diff --git a/provider/kubernetes/fixtures/loadGlobalIngressWithHttpsPortNames_ingresses.yml b/provider/kubernetes/fixtures/loadGlobalIngressWithHttpsPortNames_ingresses.yml new file mode 100644 index 000000000..67c3603b2 --- /dev/null +++ b/provider/kubernetes/fixtures/loadGlobalIngressWithHttpsPortNames_ingresses.yml @@ -0,0 +1,8 @@ +apiVersion: extensions/v1beta1 +kind: Ingress +metadata: + namespace: testing +spec: + backend: + serviceName: service1 + servicePort: https-global diff --git a/provider/kubernetes/fixtures/loadGlobalIngressWithHttpsPortNames_services.yml b/provider/kubernetes/fixtures/loadGlobalIngressWithHttpsPortNames_services.yml new file mode 100644 index 000000000..f1e1ef974 --- /dev/null +++ b/provider/kubernetes/fixtures/loadGlobalIngressWithHttpsPortNames_services.yml @@ -0,0 +1,10 @@ +apiVersion: v1 +kind: Service +metadata: + name: service1 + namespace: testing +spec: + clusterIP: 10.0.0.1 + ports: + - name: https-global + port: 8443 diff --git a/provider/kubernetes/fixtures/loadGlobalIngressWithPortNumbers_endpoints.yml b/provider/kubernetes/fixtures/loadGlobalIngressWithPortNumbers_endpoints.yml new file mode 100644 index 000000000..1991d7e93 --- /dev/null +++ b/provider/kubernetes/fixtures/loadGlobalIngressWithPortNumbers_endpoints.yml @@ -0,0 +1,10 @@ +apiVersion: v1 +kind: Endpoints +metadata: + name: service1 + namespace: testing +subsets: +- addresses: + - ip: 10.10.0.1 + ports: + - port: 8080 diff --git a/provider/kubernetes/fixtures/loadGlobalIngressWithPortNumbers_ingresses.yml b/provider/kubernetes/fixtures/loadGlobalIngressWithPortNumbers_ingresses.yml new file mode 100644 index 000000000..59d5a17e8 --- /dev/null +++ b/provider/kubernetes/fixtures/loadGlobalIngressWithPortNumbers_ingresses.yml @@ -0,0 +1,8 @@ +apiVersion: extensions/v1beta1 +kind: Ingress +metadata: + namespace: testing +spec: + backend: + serviceName: service1 + servicePort: 80 diff --git a/provider/kubernetes/fixtures/loadGlobalIngressWithPortNumbers_services.yml b/provider/kubernetes/fixtures/loadGlobalIngressWithPortNumbers_services.yml new file mode 100644 index 000000000..0a5124d40 --- /dev/null +++ b/provider/kubernetes/fixtures/loadGlobalIngressWithPortNumbers_services.yml @@ -0,0 +1,9 @@ +apiVersion: v1 +kind: Service +metadata: + name: service1 + namespace: testing +spec: + clusterIP: 10.0.0.1 + ports: + - port: 80 diff --git a/provider/kubernetes/fixtures/loadIngressesBasicAuth_ingresses.yml b/provider/kubernetes/fixtures/loadIngressesBasicAuth_ingresses.yml new file mode 100644 index 000000000..996e3a339 --- /dev/null +++ b/provider/kubernetes/fixtures/loadIngressesBasicAuth_ingresses.yml @@ -0,0 +1,17 @@ +apiVersion: extensions/v1beta1 +kind: Ingress +metadata: + annotations: + ingress.kubernetes.io/auth-remove-header: "true" + ingress.kubernetes.io/auth-secret: mySecret + ingress.kubernetes.io/auth-type: basic + namespace: testing +spec: + rules: + - host: basic + http: + paths: + - backend: + serviceName: service1 + servicePort: 80 + path: /auth diff --git a/provider/kubernetes/fixtures/loadIngressesBasicAuth_secrets.yml b/provider/kubernetes/fixtures/loadIngressesBasicAuth_secrets.yml new file mode 100644 index 000000000..50a504e96 --- /dev/null +++ b/provider/kubernetes/fixtures/loadIngressesBasicAuth_secrets.yml @@ -0,0 +1,7 @@ +apiVersion: v1 +data: + auth: bXlVc2VyOm15RW5jb2RlZFBX +kind: Secret +metadata: + name: mySecret + namespace: testing diff --git a/provider/kubernetes/fixtures/loadIngressesBasicAuth_services.yml b/provider/kubernetes/fixtures/loadIngressesBasicAuth_services.yml new file mode 100644 index 000000000..400026c21 --- /dev/null +++ b/provider/kubernetes/fixtures/loadIngressesBasicAuth_services.yml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: Service +metadata: + name: service1 + namespace: testing +spec: + clusterIP: 10.0.0.1 + externalName: example.com + ports: + - name: http + port: 80 + type: ExternalName diff --git a/provider/kubernetes/fixtures/loadIngressesForwardAuthMissingURL_endpoints.yml b/provider/kubernetes/fixtures/loadIngressesForwardAuthMissingURL_endpoints.yml new file mode 100644 index 000000000..1991d7e93 --- /dev/null +++ b/provider/kubernetes/fixtures/loadIngressesForwardAuthMissingURL_endpoints.yml @@ -0,0 +1,10 @@ +apiVersion: v1 +kind: Endpoints +metadata: + name: service1 + namespace: testing +subsets: +- addresses: + - ip: 10.10.0.1 + ports: + - port: 8080 diff --git a/provider/kubernetes/fixtures/loadIngressesForwardAuthMissingURL_ingresses.yml b/provider/kubernetes/fixtures/loadIngressesForwardAuthMissingURL_ingresses.yml new file mode 100644 index 000000000..7b274ae1f --- /dev/null +++ b/provider/kubernetes/fixtures/loadIngressesForwardAuthMissingURL_ingresses.yml @@ -0,0 +1,15 @@ +apiVersion: extensions/v1beta1 +kind: Ingress +metadata: + annotations: + ingress.kubernetes.io/auth-type: forward + namespace: testing +spec: + rules: + - host: foo + http: + paths: + - backend: + serviceName: service1 + servicePort: 80 + path: /bar diff --git a/provider/kubernetes/fixtures/loadIngressesForwardAuthMissingURL_services.yml b/provider/kubernetes/fixtures/loadIngressesForwardAuthMissingURL_services.yml new file mode 100644 index 000000000..0a5124d40 --- /dev/null +++ b/provider/kubernetes/fixtures/loadIngressesForwardAuthMissingURL_services.yml @@ -0,0 +1,9 @@ +apiVersion: v1 +kind: Service +metadata: + name: service1 + namespace: testing +spec: + clusterIP: 10.0.0.1 + ports: + - port: 80 diff --git a/provider/kubernetes/fixtures/loadIngressesForwardAuthWithTLSSecretFailures_endpoints.yml b/provider/kubernetes/fixtures/loadIngressesForwardAuthWithTLSSecretFailures_endpoints.yml new file mode 100644 index 000000000..1991d7e93 --- /dev/null +++ b/provider/kubernetes/fixtures/loadIngressesForwardAuthWithTLSSecretFailures_endpoints.yml @@ -0,0 +1,10 @@ +apiVersion: v1 +kind: Endpoints +metadata: + name: service1 + namespace: testing +subsets: +- addresses: + - ip: 10.10.0.1 + ports: + - port: 8080 diff --git a/provider/kubernetes/fixtures/loadIngressesForwardAuthWithTLSSecretFailures_ingresses.yml b/provider/kubernetes/fixtures/loadIngressesForwardAuthWithTLSSecretFailures_ingresses.yml new file mode 100644 index 000000000..f756cf69b --- /dev/null +++ b/provider/kubernetes/fixtures/loadIngressesForwardAuthWithTLSSecretFailures_ingresses.yml @@ -0,0 +1,17 @@ +apiVersion: extensions/v1beta1 +kind: Ingress +metadata: + annotations: + ingress.kubernetes.io/auth-tls-secret: secret + ingress.kubernetes.io/auth-type: forward + ingress.kubernetes.io/auth-url: https://auth.host + namespace: testing +spec: + rules: + - host: foo + http: + paths: + - backend: + serviceName: service1 + servicePort: 80 + path: /bar diff --git a/provider/kubernetes/fixtures/loadIngressesForwardAuthWithTLSSecretFailures_services.yml b/provider/kubernetes/fixtures/loadIngressesForwardAuthWithTLSSecretFailures_services.yml new file mode 100644 index 000000000..b81541434 --- /dev/null +++ b/provider/kubernetes/fixtures/loadIngressesForwardAuthWithTLSSecretFailures_services.yml @@ -0,0 +1,10 @@ +apiVersion: v1 +kind: Service +metadata: + name: service1 + namespace: testing +spec: + clusterIP: 10.0.0.1 + ports: + - port: 80 + targetPort: 0 diff --git a/provider/kubernetes/fixtures/loadIngressesForwardAuthWithTLSSecret_endpoints.yml b/provider/kubernetes/fixtures/loadIngressesForwardAuthWithTLSSecret_endpoints.yml new file mode 100644 index 000000000..1991d7e93 --- /dev/null +++ b/provider/kubernetes/fixtures/loadIngressesForwardAuthWithTLSSecret_endpoints.yml @@ -0,0 +1,10 @@ +apiVersion: v1 +kind: Endpoints +metadata: + name: service1 + namespace: testing +subsets: +- addresses: + - ip: 10.10.0.1 + ports: + - port: 8080 diff --git a/provider/kubernetes/fixtures/loadIngressesForwardAuthWithTLSSecret_ingresses.yml b/provider/kubernetes/fixtures/loadIngressesForwardAuthWithTLSSecret_ingresses.yml new file mode 100644 index 000000000..df2619d57 --- /dev/null +++ b/provider/kubernetes/fixtures/loadIngressesForwardAuthWithTLSSecret_ingresses.yml @@ -0,0 +1,18 @@ +apiVersion: extensions/v1beta1 +kind: Ingress +metadata: + annotations: + ingress.kubernetes.io/auth-tls-insecure: "true" + ingress.kubernetes.io/auth-tls-secret: secret + ingress.kubernetes.io/auth-type: forward + ingress.kubernetes.io/auth-url: https://auth.host + namespace: testing +spec: + rules: + - host: foo + http: + paths: + - backend: + serviceName: service1 + servicePort: 80 + path: /bar diff --git a/provider/kubernetes/fixtures/loadIngressesForwardAuthWithTLSSecret_secrets.yml b/provider/kubernetes/fixtures/loadIngressesForwardAuthWithTLSSecret_secrets.yml new file mode 100644 index 000000000..388631f3d --- /dev/null +++ b/provider/kubernetes/fixtures/loadIngressesForwardAuthWithTLSSecret_secrets.yml @@ -0,0 +1,8 @@ +apiVersion: v1 +data: + tls.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0= + tls.key: LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCi0tLS0tRU5EIFBSSVZBVEUgS0VZLS0tLS0= +kind: Secret +metadata: + name: secret + namespace: testing diff --git a/provider/kubernetes/fixtures/loadIngressesForwardAuthWithTLSSecret_services.yml b/provider/kubernetes/fixtures/loadIngressesForwardAuthWithTLSSecret_services.yml new file mode 100644 index 000000000..0a5124d40 --- /dev/null +++ b/provider/kubernetes/fixtures/loadIngressesForwardAuthWithTLSSecret_services.yml @@ -0,0 +1,9 @@ +apiVersion: v1 +kind: Service +metadata: + name: service1 + namespace: testing +spec: + clusterIP: 10.0.0.1 + ports: + - port: 80 diff --git a/provider/kubernetes/fixtures/loadIngressesForwardAuth_endpoints.yml b/provider/kubernetes/fixtures/loadIngressesForwardAuth_endpoints.yml new file mode 100644 index 000000000..1991d7e93 --- /dev/null +++ b/provider/kubernetes/fixtures/loadIngressesForwardAuth_endpoints.yml @@ -0,0 +1,10 @@ +apiVersion: v1 +kind: Endpoints +metadata: + name: service1 + namespace: testing +subsets: +- addresses: + - ip: 10.10.0.1 + ports: + - port: 8080 diff --git a/provider/kubernetes/fixtures/loadIngressesForwardAuth_ingresses.yml b/provider/kubernetes/fixtures/loadIngressesForwardAuth_ingresses.yml new file mode 100644 index 000000000..803a049d8 --- /dev/null +++ b/provider/kubernetes/fixtures/loadIngressesForwardAuth_ingresses.yml @@ -0,0 +1,18 @@ +apiVersion: extensions/v1beta1 +kind: Ingress +metadata: + annotations: + ingress.kubernetes.io/auth-response-headers: X-Auth,X-Test,X-Secret + ingress.kubernetes.io/auth-trust-headers: "true" + ingress.kubernetes.io/auth-type: forward + ingress.kubernetes.io/auth-url: https://auth.host + namespace: testing +spec: + rules: + - host: foo + http: + paths: + - backend: + serviceName: service1 + servicePort: 80 + path: /bar diff --git a/provider/kubernetes/fixtures/loadIngressesForwardAuth_services.yml b/provider/kubernetes/fixtures/loadIngressesForwardAuth_services.yml new file mode 100644 index 000000000..0a5124d40 --- /dev/null +++ b/provider/kubernetes/fixtures/loadIngressesForwardAuth_services.yml @@ -0,0 +1,9 @@ +apiVersion: v1 +kind: Service +metadata: + name: service1 + namespace: testing +spec: + clusterIP: 10.0.0.1 + ports: + - port: 80 diff --git a/provider/kubernetes/fixtures/loadIngresses_endpoints.yml b/provider/kubernetes/fixtures/loadIngresses_endpoints.yml new file mode 100644 index 000000000..43d810d13 --- /dev/null +++ b/provider/kubernetes/fixtures/loadIngresses_endpoints.yml @@ -0,0 +1,74 @@ +apiVersion: v1 +kind: Endpoints +metadata: + name: service1 + namespace: testing +subsets: +- addresses: + - ip: 10.10.0.1 + ports: + - port: 8080 +- addresses: + - ip: 10.21.0.1 + ports: + - port: 8080 + +--- +apiVersion: v1 +kind: Endpoints +metadata: + name: service3 + namespace: testing +subsets: +- addresses: + - ip: 10.15.0.1 + ports: + - name: http + port: 8080 + - name: https + port: 8443 +- addresses: + - ip: 10.15.0.2 + ports: + - name: http + port: 9080 + - name: https + port: 9443 + +--- +apiVersion: v1 +kind: Endpoints +metadata: + name: service6 + namespace: testing +subsets: +- addresses: + - ip: 10.15.0.3 + targetRef: + name: http://10.15.0.3:80 + ports: + - port: 80 + +--- +apiVersion: v1 +kind: Endpoints +metadata: + name: service7 + namespace: testing +subsets: +- addresses: + - ip: 10.10.0.7 + ports: + - port: 80 + +--- +apiVersion: v1 +kind: Endpoints +metadata: + name: service8 + namespace: testing +subsets: +- addresses: + - ip: 10.10.0.8 + ports: + - port: 80 diff --git a/provider/kubernetes/fixtures/loadIngresses_ingresses.yml b/provider/kubernetes/fixtures/loadIngresses_ingresses.yml new file mode 100644 index 000000000..caf030cef --- /dev/null +++ b/provider/kubernetes/fixtures/loadIngresses_ingresses.yml @@ -0,0 +1,49 @@ +apiVersion: extensions/v1beta1 +kind: Ingress +metadata: + namespace: testing +spec: + rules: + - host: foo + http: + paths: + - backend: + serviceName: service1 + servicePort: 80 + path: /bar + - backend: + serviceName: service4 + servicePort: https + path: /namedthing + - host: bar + http: + paths: + - backend: + serviceName: service3 + servicePort: https + - backend: + serviceName: service2 + servicePort: 802 + - host: service5 + http: + paths: + - backend: + serviceName: service5 + servicePort: 8888 + - host: service6 + http: + paths: + - backend: + serviceName: service6 + servicePort: 80 + - host: '*.service7' + http: + paths: + - backend: + serviceName: service7 + servicePort: 80 + - http: + paths: + - backend: + serviceName: service8 + servicePort: 80 diff --git a/provider/kubernetes/fixtures/loadIngresses_services.yml b/provider/kubernetes/fixtures/loadIngresses_services.yml new file mode 100644 index 000000000..7961308bc --- /dev/null +++ b/provider/kubernetes/fixtures/loadIngresses_services.yml @@ -0,0 +1,95 @@ +apiVersion: v1 +kind: Service +metadata: + name: service1 + namespace: testing +spec: + clusterIP: 10.0.0.1 + ports: + - port: 80 + +--- +apiVersion: v1 +kind: Service +metadata: + name: service2 + namespace: testing +spec: + clusterIP: 10.0.0.2 + ports: + - port: 802 + +--- +apiVersion: v1 +kind: Service +metadata: + name: service3 + namespace: testing +spec: + clusterIP: 10.0.0.3 + ports: + - name: http + port: 80 + - name: https + port: 443 + +--- +apiVersion: v1 +kind: Service +metadata: + name: service4 + namespace: testing +spec: + clusterIP: 10.0.0.4 + externalName: example.com + ports: + - name: https + port: 443 + type: ExternalName + +--- +apiVersion: v1 +kind: Service +metadata: + name: service5 + namespace: testing +spec: + clusterIP: 10.0.0.5 + externalName: example.com + ports: + - name: http + port: 8888 + type: ExternalName + +--- +apiVersion: v1 +kind: Service +metadata: + name: service6 + namespace: testing +spec: + clusterIP: 10.0.0.6 + ports: + - port: 80 + +--- +apiVersion: v1 +kind: Service +metadata: + name: service7 + namespace: testing +spec: + clusterIP: 10.0.0.7 + ports: + - port: 80 + +--- +apiVersion: v1 +kind: Service +metadata: + name: service8 + namespace: testing +spec: + clusterIP: 10.0.0.8 + ports: + - port: 80 diff --git a/provider/kubernetes/fixtures/missingResources_endpoints.yml b/provider/kubernetes/fixtures/missingResources_endpoints.yml new file mode 100644 index 000000000..320abcedd --- /dev/null +++ b/provider/kubernetes/fixtures/missingResources_endpoints.yml @@ -0,0 +1,18 @@ +apiVersion: v1 +kind: Endpoints +metadata: + name: fully_working_service + namespace: testing +subsets: +- addresses: + - ip: 10.10.0.1 + ports: + - port: 8080 + +--- +apiVersion: v1 +kind: Endpoints +metadata: + name: missing_endpoint_subsets_service + namespace: testing +subsets: null diff --git a/provider/kubernetes/fixtures/missingResources_ingresses.yml b/provider/kubernetes/fixtures/missingResources_ingresses.yml new file mode 100644 index 000000000..ed521450c --- /dev/null +++ b/provider/kubernetes/fixtures/missingResources_ingresses.yml @@ -0,0 +1,30 @@ +apiVersion: extensions/v1beta1 +kind: Ingress +metadata: + namespace: testing +spec: + rules: + - host: fully_working + http: + paths: + - backend: + serviceName: fully_working_service + servicePort: 80 + - host: missing_service + http: + paths: + - backend: + serviceName: missing_service_service + servicePort: 80 + - host: missing_endpoints + http: + paths: + - backend: + serviceName: missing_endpoints_service + servicePort: 80 + - host: missing_endpoint_subsets + http: + paths: + - backend: + serviceName: missing_endpoint_subsets_service + servicePort: 80 diff --git a/provider/kubernetes/fixtures/missingResources_services.yml b/provider/kubernetes/fixtures/missingResources_services.yml new file mode 100644 index 000000000..6fb319fb5 --- /dev/null +++ b/provider/kubernetes/fixtures/missingResources_services.yml @@ -0,0 +1,31 @@ +apiVersion: v1 +kind: Service +metadata: + name: fully_working_service + namespace: testing +spec: + clusterIP: 10.0.0.1 + ports: + - port: 80 + +--- +apiVersion: v1 +kind: Service +metadata: + name: missing_endpoints_service + namespace: testing +spec: + clusterIP: 10.0.0.3 + ports: + - port: 80 + +--- +apiVersion: v1 +kind: Service +metadata: + name: missing_endpoint_subsets_service + namespace: testing +spec: + clusterIP: 10.0.0.4 + ports: + - port: 80 diff --git a/provider/kubernetes/fixtures/multiPortServices_endpoints.yml b/provider/kubernetes/fixtures/multiPortServices_endpoints.yml new file mode 100644 index 000000000..15dd546bc --- /dev/null +++ b/provider/kubernetes/fixtures/multiPortServices_endpoints.yml @@ -0,0 +1,18 @@ +apiVersion: v1 +kind: Endpoints +metadata: + name: service + namespace: testing +subsets: +- addresses: + - ip: 10.10.0.1 + - ip: 10.10.0.2 + ports: + - name: cheddar + port: 8080 +- addresses: + - ip: 10.20.0.1 + - ip: 10.20.0.2 + ports: + - name: stilton + port: 8081 diff --git a/provider/kubernetes/fixtures/multiPortServices_ingresses.yml b/provider/kubernetes/fixtures/multiPortServices_ingresses.yml new file mode 100644 index 000000000..30d5dd950 --- /dev/null +++ b/provider/kubernetes/fixtures/multiPortServices_ingresses.yml @@ -0,0 +1,16 @@ +apiVersion: extensions/v1beta1 +kind: Ingress +metadata: + namespace: testing +spec: + rules: + - http: + paths: + - backend: + serviceName: service + servicePort: cheddar + path: /cheddar + - backend: + serviceName: service + servicePort: stilton + path: /stilton diff --git a/provider/kubernetes/fixtures/multiPortServices_services.yml b/provider/kubernetes/fixtures/multiPortServices_services.yml new file mode 100644 index 000000000..c193458f9 --- /dev/null +++ b/provider/kubernetes/fixtures/multiPortServices_services.yml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: Service +metadata: + name: service + namespace: testing +spec: + clusterIP: 10.0.0.1 + ports: + - name: cheddar + port: 80 + - name: stilton + port: 81 diff --git a/provider/kubernetes/fixtures/onlyReferencesServicesFromOwnNamespace_ingresses.yml b/provider/kubernetes/fixtures/onlyReferencesServicesFromOwnNamespace_ingresses.yml new file mode 100644 index 000000000..1766ca484 --- /dev/null +++ b/provider/kubernetes/fixtures/onlyReferencesServicesFromOwnNamespace_ingresses.yml @@ -0,0 +1,12 @@ +apiVersion: extensions/v1beta1 +kind: Ingress +metadata: + namespace: awesome +spec: + rules: + - host: foo + http: + paths: + - backend: + serviceName: service + servicePort: 80 diff --git a/provider/kubernetes/fixtures/onlyReferencesServicesFromOwnNamespace_services.yml b/provider/kubernetes/fixtures/onlyReferencesServicesFromOwnNamespace_services.yml new file mode 100644 index 000000000..6f8246755 --- /dev/null +++ b/provider/kubernetes/fixtures/onlyReferencesServicesFromOwnNamespace_services.yml @@ -0,0 +1,22 @@ +apiVersion: v1 +kind: Service +metadata: + name: service + namespace: awesome +spec: + clusterIP: 10.0.0.1 + ports: + - name: http + port: 80 + +--- +apiVersion: v1 +kind: Service +metadata: + name: service + namespace: not-awesome +spec: + clusterIP: 10.0.0.2 + ports: + - name: http + port: 80 diff --git a/provider/kubernetes/fixtures/percentageWeightServiceAnnotation_endpoints.yml b/provider/kubernetes/fixtures/percentageWeightServiceAnnotation_endpoints.yml new file mode 100644 index 000000000..c5d68d66b --- /dev/null +++ b/provider/kubernetes/fixtures/percentageWeightServiceAnnotation_endpoints.yml @@ -0,0 +1,24 @@ +apiVersion: v1 +kind: Endpoints +metadata: + name: service1 + namespace: testing +subsets: +- addresses: + - ip: 10.10.0.1 + - ip: 10.10.0.2 + ports: + - port: 8080 + +--- +apiVersion: v1 +kind: Endpoints +metadata: + name: service2 + namespace: testing +subsets: +- addresses: + - ip: 10.10.0.3 + - ip: 10.10.0.4 + ports: + - port: 7070 diff --git a/provider/kubernetes/fixtures/percentageWeightServiceAnnotation_ingresses.yml b/provider/kubernetes/fixtures/percentageWeightServiceAnnotation_ingresses.yml new file mode 100644 index 000000000..ad0dc1c0e --- /dev/null +++ b/provider/kubernetes/fixtures/percentageWeightServiceAnnotation_ingresses.yml @@ -0,0 +1,30 @@ +apiVersion: extensions/v1beta1 +kind: Ingress +metadata: + annotations: + ingress.kubernetes.io/service-weights: |2 + + service1: 10% + service3: 20% + namespace: testing +spec: + rules: + - host: host1 + http: + paths: + - backend: + serviceName: service1 + servicePort: "8080" + path: /foo + - backend: + serviceName: service2 + servicePort: "7070" + path: /foo + - backend: + serviceName: service3 + servicePort: "9090" + path: /foo + - backend: + serviceName: service2 + servicePort: "7070" + path: /bar diff --git a/provider/kubernetes/fixtures/percentageWeightServiceAnnotation_services.yml b/provider/kubernetes/fixtures/percentageWeightServiceAnnotation_services.yml new file mode 100644 index 000000000..a2792fdc0 --- /dev/null +++ b/provider/kubernetes/fixtures/percentageWeightServiceAnnotation_services.yml @@ -0,0 +1,32 @@ +apiVersion: v1 +kind: Service +metadata: + name: service1 + namespace: testing +spec: + clusterIP: 10.0.0.1 + ports: + - port: 8080 + +--- +apiVersion: v1 +kind: Service +metadata: + name: service2 + namespace: testing +spec: + clusterIP: 10.0.0.1 + ports: + - port: 7070 + +--- +apiVersion: v1 +kind: Service +metadata: + name: service3 + namespace: testing +spec: + externalName: example.com + ports: + - port: 9090 + type: ExternalName diff --git a/provider/kubernetes/fixtures/priorityHeaderValue_ingresses.yml b/provider/kubernetes/fixtures/priorityHeaderValue_ingresses.yml new file mode 100644 index 000000000..b09fd4813 --- /dev/null +++ b/provider/kubernetes/fixtures/priorityHeaderValue_ingresses.yml @@ -0,0 +1,15 @@ +apiVersion: extensions/v1beta1 +kind: Ingress +metadata: + annotations: + ingress.kubernetes.io/priority: "1337" + namespace: testing +spec: + rules: + - host: foo + http: + paths: + - backend: + serviceName: service1 + servicePort: 80 + path: /bar diff --git a/provider/kubernetes/fixtures/priorityHeaderValue_services.yml b/provider/kubernetes/fixtures/priorityHeaderValue_services.yml new file mode 100644 index 000000000..400026c21 --- /dev/null +++ b/provider/kubernetes/fixtures/priorityHeaderValue_services.yml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: Service +metadata: + name: service1 + namespace: testing +spec: + clusterIP: 10.0.0.1 + externalName: example.com + ports: + - name: http + port: 80 + type: ExternalName diff --git a/provider/kubernetes/fixtures/providerUpdateIngressStatus_services.yml b/provider/kubernetes/fixtures/providerUpdateIngressStatus_services.yml new file mode 100644 index 000000000..065cafd7d --- /dev/null +++ b/provider/kubernetes/fixtures/providerUpdateIngressStatus_services.yml @@ -0,0 +1,24 @@ +apiVersion: v1 +kind: Service +metadata: + name: service-empty-status + namespace: testing +spec: + clusterIP: 10.0.0.1 + ports: + - port: 80 + +--- +apiVersion: v1 +kind: Service +metadata: + name: service + namespace: testing +spec: + clusterIP: 10.0.0.2 + ports: + - port: 80 +status: + loadBalancer: + ingress: + - ip: 127.0.0.1 diff --git a/provider/kubernetes/fixtures/serviceAnnotations_endpoints.yml b/provider/kubernetes/fixtures/serviceAnnotations_endpoints.yml new file mode 100644 index 000000000..fcf9cf697 --- /dev/null +++ b/provider/kubernetes/fixtures/serviceAnnotations_endpoints.yml @@ -0,0 +1,84 @@ +apiVersion: v1 +kind: Endpoints +metadata: + name: service1 + namespace: testing +subsets: +- addresses: + - ip: 10.10.0.1 + ports: + - port: 8080 +- addresses: + - ip: 10.21.0.1 + ports: + - port: 8080 + +--- +apiVersion: v1 +kind: Endpoints +metadata: + name: service2 + namespace: testing +subsets: +- addresses: + - ip: 10.15.0.1 + ports: + - port: 8080 +- addresses: + - ip: 10.15.0.2 + ports: + - port: 8080 + +--- +apiVersion: v1 +kind: Endpoints +metadata: + name: service3 + namespace: testing +subsets: +- addresses: + - ip: 10.14.0.1 + ports: + - name: http + port: 8080 +- addresses: + - ip: 10.12.0.1 + ports: + - name: http + port: 8080 + +--- +apiVersion: v1 +kind: Endpoints +metadata: + name: service4 + namespace: testing +subsets: +- addresses: + - ip: 10.4.0.1 + ports: + - name: http + port: 8080 +- addresses: + - ip: 10.4.0.2 + ports: + - name: http + port: 8080 + +--- +apiVersion: v1 +kind: Endpoints +metadata: + name: service5 + namespace: testing +subsets: +- addresses: + - ip: 10.4.0.1 + ports: + - name: http + port: 8080 +- addresses: + - ip: 10.4.0.2 + ports: + - name: http + port: 8080 diff --git a/provider/kubernetes/fixtures/serviceAnnotations_ingresses.yml b/provider/kubernetes/fixtures/serviceAnnotations_ingresses.yml new file mode 100644 index 000000000..e389b7d95 --- /dev/null +++ b/provider/kubernetes/fixtures/serviceAnnotations_ingresses.yml @@ -0,0 +1,37 @@ +apiVersion: extensions/v1beta1 +kind: Ingress +metadata: + namespace: testing +spec: + rules: + - host: foo + http: + paths: + - backend: + serviceName: service1 + servicePort: 80 + path: /bar + - host: bar + http: + paths: + - backend: + serviceName: service2 + servicePort: 802 + - host: baz + http: + paths: + - backend: + serviceName: service3 + servicePort: 803 + - host: max-conn + http: + paths: + - backend: + serviceName: service4 + servicePort: 804 + - host: flush + http: + paths: + - backend: + serviceName: service5 + servicePort: 805 diff --git a/provider/kubernetes/fixtures/serviceAnnotations_services.yml b/provider/kubernetes/fixtures/serviceAnnotations_services.yml new file mode 100644 index 000000000..f93777623 --- /dev/null +++ b/provider/kubernetes/fixtures/serviceAnnotations_services.yml @@ -0,0 +1,74 @@ +apiVersion: v1 +kind: Service +metadata: + annotations: + ingress.kubernetes.io/circuit-breaker-expression: NetworkErrorRatio() > 0.5 + ingress.kubernetes.io/load-balancer-method: drr + name: service1 + namespace: testing +spec: + clusterIP: 10.0.0.1 + ports: + - port: 80 + +--- +apiVersion: v1 +kind: Service +metadata: + annotations: + ingress.kubernetes.io/circuit-breaker-expression: "" + traefik.backend.loadbalancer.sticky: "true" + name: service2 + namespace: testing +spec: + clusterIP: 10.0.0.2 + ports: + - port: 802 + +--- +apiVersion: v1 +kind: Service +metadata: + annotations: + ingress.kubernetes.io/buffering: |2 + + maxrequestbodybytes: 10485760 + memrequestbodybytes: 2097153 + maxresponsebodybytes: 10485761 + memresponsebodybytes: 2097152 + retryexpression: IsNetworkError() && Attempts() <= 2 + name: service3 + namespace: testing +spec: + clusterIP: 10.0.0.3 + ports: + - name: http + port: 803 + +--- +apiVersion: v1 +kind: Service +metadata: + annotations: + ingress.kubernetes.io/max-conn-amount: "6" + ingress.kubernetes.io/max-conn-extractor-func: client.ip + name: service4 + namespace: testing +spec: + clusterIP: 10.0.0.4 + ports: + - name: http + port: 804 + +--- +apiVersion: v1 +kind: Service +metadata: + annotations: + ingress.kubernetes.io/responseforwarding-flushinterval: 10ms + name: service5 + namespace: testing +spec: + clusterIP: 10.0.0.5 + ports: + - port: 80 diff --git a/provider/kubernetes/fixtures/tLSSecretLoad_ingresses.yml b/provider/kubernetes/fixtures/tLSSecretLoad_ingresses.yml new file mode 100644 index 000000000..b67366fde --- /dev/null +++ b/provider/kubernetes/fixtures/tLSSecretLoad_ingresses.yml @@ -0,0 +1,40 @@ +apiVersion: extensions/v1beta1 +kind: Ingress +metadata: + annotations: + ingress.kubernetes.io/frontend-entry-points: ep1,ep2 + namespace: testing +spec: + rules: + - host: example.com + http: + paths: + - backend: + serviceName: example-com + servicePort: 80 + - host: example.org + http: + paths: + - backend: + serviceName: example-org + servicePort: 80 + tls: + - secretName: myTlsSecret + +--- +apiVersion: extensions/v1beta1 +kind: Ingress +metadata: + annotations: + ingress.kubernetes.io/frontend-entry-points: ep3 + namespace: testing +spec: + rules: + - host: example.fail + http: + paths: + - backend: + serviceName: example-fail + servicePort: 80 + tls: + - secretName: myUndefinedSecret diff --git a/provider/kubernetes/fixtures/tLSSecretLoad_secrets.yml b/provider/kubernetes/fixtures/tLSSecretLoad_secrets.yml new file mode 100644 index 000000000..32d547861 --- /dev/null +++ b/provider/kubernetes/fixtures/tLSSecretLoad_secrets.yml @@ -0,0 +1,8 @@ +apiVersion: v1 +data: + tls.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0= + tls.key: LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCi0tLS0tRU5EIFBSSVZBVEUgS0VZLS0tLS0= +kind: Secret +metadata: + name: myTlsSecret + namespace: testing diff --git a/provider/kubernetes/fixtures/tLSSecretLoad_services.yml b/provider/kubernetes/fixtures/tLSSecretLoad_services.yml new file mode 100644 index 000000000..81cfedf6f --- /dev/null +++ b/provider/kubernetes/fixtures/tLSSecretLoad_services.yml @@ -0,0 +1,24 @@ +apiVersion: v1 +kind: Service +metadata: + name: example-com + namespace: testing +spec: + clusterIP: 10.0.0.1 + ports: + - name: http + port: 80 + type: ClusterIP + +--- +apiVersion: v1 +kind: Service +metadata: + name: example-org + namespace: testing +spec: + clusterIP: 10.0.0.2 + ports: + - name: http + port: 80 + type: ClusterIP diff --git a/provider/kubernetes/fixtures/templateBreakingIngresssValues_ingresses.yml b/provider/kubernetes/fixtures/templateBreakingIngresssValues_ingresses.yml new file mode 100644 index 000000000..dca7a3d4f --- /dev/null +++ b/provider/kubernetes/fixtures/templateBreakingIngresssValues_ingresses.yml @@ -0,0 +1,45 @@ +apiVersion: extensions/v1beta1 +kind: Ingress +metadata: + annotations: + kubernetes.io/ingress.class: testing-"foo" + namespace: testing +spec: + rules: + - host: foo + http: + paths: + - backend: + serviceName: service1 + servicePort: 80 + path: /bar + +--- +apiVersion: extensions/v1beta1 +kind: Ingress +metadata: + namespace: testing +spec: + rules: + - host: testing-"foo" + http: + paths: + - backend: + serviceName: service1 + servicePort: 80 + path: /bar + +--- +apiVersion: extensions/v1beta1 +kind: Ingress +metadata: + namespace: testing +spec: + rules: + - host: foo + http: + paths: + - backend: + serviceName: service1 + servicePort: 80 + path: /testing-"foo" diff --git a/provider/kubernetes/kubernetes_test.go b/provider/kubernetes/kubernetes_test.go index 6c21b4fa3..8642a3654 100644 --- a/provider/kubernetes/kubernetes_test.go +++ b/provider/kubernetes/kubernetes_test.go @@ -4,11 +4,11 @@ import ( "errors" "fmt" "os" + "path/filepath" "strings" "testing" "time" - "github.com/containous/traefik/provider/label" "github.com/containous/traefik/tls" "github.com/containous/traefik/types" "github.com/stretchr/testify/assert" @@ -19,450 +19,1272 @@ import ( "k8s.io/apimachinery/pkg/util/intstr" ) -func TestLoadIngresses(t *testing.T) { - ingresses := []*extensionsv1beta1.Ingress{ - buildIngress( - iNamespace("testing"), - iRules( - iRule(iHost("foo"), - iPaths( - onePath(iPath("/bar"), iBackend("service1", intstr.FromInt(80))), - onePath(iPath("/namedthing"), iBackend("service4", intstr.FromString("https")))), - ), - iRule(iHost("bar"), - iPaths( - onePath(iBackend("service3", intstr.FromString("https"))), - onePath(iBackend("service2", intstr.FromInt(802))), +func TestProvider_loadIngresses(t *testing.T) { + testCases := []struct { + desc string + provider Provider + fixtures []string + expected *types.Configuration + }{ + { + desc: "simple", + fixtures: []string{ + filepath.Join("fixtures", "loadIngresses_ingresses.yml"), + filepath.Join("fixtures", "loadIngresses_services.yml"), + filepath.Join("fixtures", "loadIngresses_endpoints.yml"), + }, + expected: buildConfiguration( + backends( + backend("foo/bar", + lbMethod("wrr"), + servers( + server("http://10.10.0.1:8080", weight(1)), + server("http://10.21.0.1:8080", weight(1))), + ), + backend("foo/namedthing", + lbMethod("wrr"), + servers( + server("https://example.com", weight(1)), + ), + ), + backend("bar", + lbMethod("wrr"), + servers( + server("https://10.15.0.1:8443", weight(1)), + server("https://10.15.0.2:9443", weight(1)), + ), + ), + backend("service5", + lbMethod("wrr"), + servers( + server("http://example.com:8888", weight(1)), + ), + ), + backend("service6", + lbMethod("wrr"), + servers( + server("http://10.15.0.3:80", weight(1)), + ), + ), + backend("*.service7", + lbMethod("wrr"), + servers( + server("http://10.10.0.7:80", weight(1)), + ), + ), + backend("service8", + lbMethod("wrr"), + servers( + server("http://10.10.0.8:80", weight(1)), + ), ), ), - iRule(iHost("service5"), - iPaths( - onePath(iBackend("service5", intstr.FromInt(8888))), + frontends( + frontend("foo/bar", + passHostHeader(), + routes( + route("/bar", "PathPrefix:/bar"), + route("foo", "Host:foo")), ), - ), - iRule(iHost("service6"), - iPaths( - onePath(iBackend("service6", intstr.FromInt(80))), + frontend("foo/namedthing", + passHostHeader(), + routes( + route("/namedthing", "PathPrefix:/namedthing"), + route("foo", "Host:foo")), ), - ), - iRule(iHost("*.service7"), - iPaths( - onePath(iBackend("service7", intstr.FromInt(80))), + frontend("bar", + passHostHeader(), + routes(route("bar", "Host:bar")), ), - ), - iRule(iHost(""), - iPaths( - onePath(iBackend("service8", intstr.FromInt(80))), + frontend("service5", + passHostHeader(), + routes(route("service5", "Host:service5")), + ), + frontend("service6", + passHostHeader(), + routes(route("service6", "Host:service6")), + ), + frontend("*.service7", + passHostHeader(), + routes(route("*.service7", "HostRegexp:{subdomain:[A-Za-z0-9-_]+}.service7")), + ), + frontend("service8", + passHostHeader(), + routes(route("/", "PathPrefix:/")), ), ), ), - ), + }, + { + desc: "loadGlobalIngressWithExternalName", + fixtures: []string{ + filepath.Join("fixtures", "loadGlobalIngressWithExternalName_ingresses.yml"), + filepath.Join("fixtures", "loadGlobalIngressWithExternalName_services.yml"), + }, + expected: buildConfiguration( + backends( + backend("global-default-backend", + lbMethod("wrr"), + servers( + server("http://some-external-name", weight(1)), + ), + ), + ), + frontends( + frontend("global-default-backend", + frontendName("global-default-frontend"), + passHostHeader(), + routes( + route("/", "PathPrefix:/"), + ), + ), + ), + ), + }, + { + desc: "loadGlobalIngressWithPortNumbers", + fixtures: []string{ + filepath.Join("fixtures", "loadGlobalIngressWithPortNumbers_ingresses.yml"), + filepath.Join("fixtures", "loadGlobalIngressWithPortNumbers_services.yml"), + filepath.Join("fixtures", "loadGlobalIngressWithPortNumbers_endpoints.yml"), + }, + expected: buildConfiguration( + backends( + backend("global-default-backend", + lbMethod("wrr"), + servers( + server("http://10.10.0.1:8080", weight(1)), + ), + ), + ), + frontends( + frontend("global-default-backend", + frontendName("global-default-frontend"), + passHostHeader(), + routes( + route("/", "PathPrefix:/"), + ), + ), + ), + ), + }, + { + desc: "loadGlobalIngressWithHttpsPortNames", + fixtures: []string{ + filepath.Join("fixtures", "loadGlobalIngressWithHttpsPortNames_ingresses.yml"), + filepath.Join("fixtures", "loadGlobalIngressWithHttpsPortNames_services.yml"), + filepath.Join("fixtures", "loadGlobalIngressWithHttpsPortNames_endpoints.yml"), + }, + expected: buildConfiguration( + backends( + backend("global-default-backend", + lbMethod("wrr"), + servers( + server("https://10.10.0.1:8080", weight(1)), + ), + ), + ), + frontends( + frontend("global-default-backend", + frontendName("global-default-frontend"), + passHostHeader(), + routes( + route("/", "PathPrefix:/"), + ), + ), + ), + ), + }, + { + desc: "getPassHostHeader", + fixtures: []string{ + filepath.Join("fixtures", "getPassHostHeader_ingresses.yml"), + filepath.Join("fixtures", "getPassHostHeader_services.yml"), + }, + provider: Provider{DisablePassHostHeaders: true}, + expected: buildConfiguration( + backends(backend("foo/bar", lbMethod("wrr"), servers())), + frontends( + frontend("foo/bar", + routes( + route("/bar", "PathPrefix:/bar"), + route("foo", "Host:foo")), + ), + ), + ), + }, + { + desc: "getPassTLSCert", // Deprecated + fixtures: []string{ + filepath.Join("fixtures", "getPassTLSCert_ingresses.yml"), + filepath.Join("fixtures", "getPassTLSCert_services.yml"), + }, + provider: Provider{EnablePassTLSCert: true}, + expected: buildConfiguration( + backends(backend("foo/bar", lbMethod("wrr"), servers())), + frontends(frontend("foo/bar", + passHostHeader(), + passTLSCert(), + routes( + route("/bar", "PathPrefix:/bar"), + route("foo", "Host:foo")), + )), + ), + }, + { + desc: "onlyReferencesServicesFromOwnNamespace", + fixtures: []string{ + filepath.Join("fixtures", "onlyReferencesServicesFromOwnNamespace_ingresses.yml"), + filepath.Join("fixtures", "onlyReferencesServicesFromOwnNamespace_services.yml"), + }, + expected: buildConfiguration( + backends(backend("foo", lbMethod("wrr"), servers())), + frontends(frontend("foo", + passHostHeader(), + routes(route("foo", "Host:foo")), + )), + ), + }, + { + desc: "hostlessIngress", + fixtures: []string{ + filepath.Join("fixtures", "hostlessIngress_ingresses.yml"), + filepath.Join("fixtures", "hostlessIngress_services.yml"), + }, + provider: Provider{DisablePassHostHeaders: true}, + expected: buildConfiguration( + backends(backend("/bar", lbMethod("wrr"), servers())), + frontends(frontend("/bar", + routes(route("/bar", "PathPrefix:/bar")))), + ), + }, + { + desc: "serviceAnnotations", + fixtures: []string{ + filepath.Join("fixtures", "serviceAnnotations_ingresses.yml"), + filepath.Join("fixtures", "serviceAnnotations_services.yml"), + filepath.Join("fixtures", "serviceAnnotations_endpoints.yml"), + }, + expected: buildConfiguration( + backends( + backend("foo/bar", + servers( + server("http://10.10.0.1:8080", weight(1)), + server("http://10.21.0.1:8080", weight(1))), + lbMethod("drr"), + circuitBreaker("NetworkErrorRatio() > 0.5"), + ), + backend("flush", + servers(), + lbMethod("wrr"), + responseForwarding("10ms"), + ), + backend("bar", + servers( + server("http://10.15.0.1:8080", weight(1)), + server("http://10.15.0.2:8080", weight(1))), + lbMethod("wrr"), lbSticky(), + ), + backend("baz", + servers( + server("http://10.14.0.1:8080", weight(1)), + server("http://10.12.0.1:8080", weight(1))), + lbMethod("wrr"), + buffering( + maxRequestBodyBytes(10485760), + memRequestBodyBytes(2097153), + maxResponseBodyBytes(10485761), + memResponseBodyBytes(2097152), + retrying("IsNetworkError() && Attempts() <= 2"), + ), + ), + backend("max-conn", + servers( + server("http://10.4.0.1:8080", weight(1)), + server("http://10.4.0.2:8080", weight(1))), + maxConnExtractorFunc("client.ip"), + maxConnAmount(6), + lbMethod("wrr"), + ), + ), + frontends( + frontend("foo/bar", + passHostHeader(), + routes( + route("/bar", "PathPrefix:/bar"), + route("foo", "Host:foo")), + ), + frontend("bar", + passHostHeader(), + routes(route("bar", "Host:bar"))), + frontend("baz", + passHostHeader(), + routes(route("baz", "Host:baz"))), + frontend("max-conn", + passHostHeader(), + routes( + route("max-conn", "Host:max-conn"))), + frontend("flush", + passHostHeader(), + routes( + route("flush", "Host:flush"))), + ), + ), + }, + { + desc: "ingressAnnotations", + fixtures: []string{ + filepath.Join("fixtures", "ingressAnnotations_ingresses.yml"), + filepath.Join("fixtures", "ingressAnnotations_services.yml"), + filepath.Join("fixtures", "ingressAnnotations_secrets.yml"), + }, + expected: buildConfiguration( + backends( + backend("foo/bar", + servers( + server("http://example.com", weight(1)), + server("http://example.com", weight(1))), + lbMethod("wrr"), + ), + backend("other/stuff", + servers( + server("http://example.com", weight(1)), + server("http://example.com", weight(1))), + lbMethod("wrr"), + ), + backend("http-https_other/", + servers( + server("http://example.com", weight(1)), + server("http://example.com", weight(1))), + lbMethod("wrr"), + ), + backend("other/sslstuff", + servers( + server("http://example.com", weight(1)), + server("http://example.com", weight(1))), + lbMethod("wrr"), + ), + backend("basic/auth", + servers( + server("http://example.com", weight(1)), + server("http://example.com", weight(1))), + lbMethod("wrr"), + ), + backend("redirect/https", + servers( + server("http://example.com", weight(1)), + server("http://example.com", weight(1))), + lbMethod("wrr"), + ), + backend("test/whitelist-source-range", + servers( + server("http://example.com", weight(1)), + server("http://example.com", weight(1))), + lbMethod("wrr"), + ), + backend("rewrite/api", + servers( + server("http://example.com", weight(1)), + server("http://example.com", weight(1))), + lbMethod("wrr"), + ), + backend("error-pages/errorpages", + servers( + server("http://example.com", weight(1)), + server("http://example.com", weight(1))), + lbMethod("wrr"), + ), + backend("rate-limit/ratelimit", + servers( + server("http://example.com", weight(1)), + server("http://example.com", weight(1))), + lbMethod("wrr"), + ), + backend("custom-headers/customheaders", + servers( + server("http://example.com", weight(1)), + server("http://example.com", weight(1))), + lbMethod("wrr"), + ), + backend("root/", + servers( + server("http://example.com", weight(1))), + lbMethod("wrr"), + ), + backend("root/root1", + servers( + server("http://example.com", weight(1))), + lbMethod("wrr"), + ), + backend("root2/", + servers(), + lbMethod("wrr"), + ), + backend("root3", + servers( + server("http://example.com", weight(1))), + lbMethod("wrr"), + ), + backend("protocol/valid", + servers( + server("h2c://example.com", weight(1)), + server("h2c://example.com", weight(1))), + lbMethod("wrr"), + ), + backend("protocol/notvalid", + servers(), + lbMethod("wrr"), + ), + backend("protocol/missmatch", + servers( + server("http://example.com", weight(1)), + server("http://example.com", weight(1))), + lbMethod("wrr"), + ), + backend("protocol/noAnnotation", + servers( + server("https://example.com", weight(1)), + server("https://example.com", weight(1))), + lbMethod("wrr"), + ), + ), + frontends( + frontend("foo/bar", + routes( + route("/bar", "PathPrefix:/bar"), + route("foo", "Host:foo")), + ), + frontend("other/stuff", + passHostHeader(), + routes( + route("/stuff", "PathPrefix:/stuff"), + route("other", "Host:other")), + ), + frontend("http-https_other/", + passHostHeader(), + entryPoints("http", "https"), + routes( + route("/", "PathPrefix:/"), + route("other", "Host:other")), + ), + frontend("other/sslstuff", + passHostHeader(), + passTLSClientCert(), + passTLSCert(), + routes( + route("/sslstuff", "PathPrefix:/sslstuff"), + route("other", "Host:other")), + ), + frontend("basic/auth", + passHostHeader(), + basicAuthDeprecated("myUser:myEncodedPW"), + routes( + route("/auth", "PathPrefix:/auth"), + route("basic", "Host:basic")), + ), + frontend("redirect/https", + passHostHeader(), + redirectEntryPoint("https"), + routes( + route("/https", "PathPrefix:/https"), + route("redirect", "Host:redirect")), + ), + frontend("test/whitelist-source-range", + passHostHeader(), + whiteList(true, "1.1.1.1/24", "1234:abcd::42/32"), + routes( + route("/whitelist-source-range", "PathPrefix:/whitelist-source-range"), + route("test", "Host:test")), + ), + frontend("rewrite/api", + passHostHeader(), + routes( + route("/api", "PathPrefix:/api;ReplacePathRegex: ^/api(.*) $1"), + route("rewrite", "Host:rewrite")), + ), + frontend("error-pages/errorpages", + passHostHeader(), + errorPage("foo", errorQuery("/bar"), errorStatus("123", "456"), errorBackend("bar")), + routes( + route("/errorpages", "PathPrefix:/errorpages"), + route("error-pages", "Host:error-pages")), + ), + frontend("rate-limit/ratelimit", + passHostHeader(), + rateLimit(rateExtractorFunc("client.ip"), + rateSet("foo", limitPeriod(6*time.Second), limitAverage(12), limitBurst(18)), + rateSet("bar", limitPeriod(3*time.Second), limitAverage(6), limitBurst(9))), + routes( + route("/ratelimit", "PathPrefix:/ratelimit"), + route("rate-limit", "Host:rate-limit")), + ), + frontend("custom-headers/customheaders", + passHostHeader(), + headers(&types.Headers{ + CustomRequestHeaders: map[string]string{ + "Access-Control-Allow-Methods": "POST,GET,OPTIONS", + "Content-Type": "application/json; charset=utf-8", + }, + CustomResponseHeaders: map[string]string{ + "Access-Control-Allow-Methods": "POST,GET,OPTIONS", + "Content-Type": "application/json; charset=utf-8", + }, + SSLProxyHeaders: map[string]string{ + "Access-Control-Allow-Methods": "POST,GET,OPTIONS", + "Content-Type": "application/json; charset=utf-8", + }, + AllowedHosts: []string{"foo", "fii", "fuu"}, + HostsProxyHeaders: []string{"foo", "fii", "fuu"}, + STSSeconds: 666, + SSLForceHost: true, + SSLRedirect: true, + SSLTemporaryRedirect: true, + STSIncludeSubdomains: true, + STSPreload: true, + ForceSTSHeader: true, + FrameDeny: true, + ContentTypeNosniff: true, + BrowserXSSFilter: true, + IsDevelopment: true, + CustomFrameOptionsValue: "foo", + SSLHost: "foo", + ContentSecurityPolicy: "foo", + PublicKey: "foo", + ReferrerPolicy: "foo", + CustomBrowserXSSValue: "foo", + }), + routes( + route("/customheaders", "PathPrefix:/customheaders"), + route("custom-headers", "Host:custom-headers")), + ), + frontend("root/", + passHostHeader(), + redirectRegex("root/$", "root/root"), + routes( + route("/", "PathPrefix:/"), + route("root", "Host:root"), + ), + ), + frontend("root2/", + passHostHeader(), + redirectRegex("root2/$", "root2/root2"), + routes( + route("/", "PathPrefix:/;ReplacePathRegex: ^/(.*) /abc$1"), + route("root2", "Host:root2"), + ), + ), + frontend("root/root1", + passHostHeader(), + routes( + route("/root1", "PathPrefix:/root1"), + route("root", "Host:root"), + ), + ), + frontend("root3", + passHostHeader(), + redirectRegex("root3/$", "root3/root"), + routes( + route("root3", "Host:root3"), + ), + ), + frontend("protocol/valid", + passHostHeader(), + routes( + route("/valid", "PathPrefix:/valid"), + route("protocol", "Host:protocol"), + ), + ), + frontend("protocol/notvalid", + passHostHeader(), + routes( + route("/notvalid", "PathPrefix:/notvalid"), + route("protocol", "Host:protocol"), + ), + ), + frontend("protocol/missmatch", + passHostHeader(), + routes( + route("/missmatch", "PathPrefix:/missmatch"), + route("protocol", "Host:protocol"), + ), + ), + frontend("protocol/noAnnotation", + passHostHeader(), + routes( + route("/noAnnotation", "PathPrefix:/noAnnotation"), + route("protocol", "Host:protocol"), + ), + ), + ), + ), + }, + { + desc: "priorityHeaderValue", + fixtures: []string{ + filepath.Join("fixtures", "priorityHeaderValue_ingresses.yml"), + filepath.Join("fixtures", "priorityHeaderValue_services.yml"), + }, + expected: buildConfiguration( + backends( + backend("1337-foo/bar", + servers(server("http://example.com", weight(1))), + lbMethod("wrr"), + ), + ), + frontends( + frontend("1337-foo/bar", + passHostHeader(), + priority(1337), + routes( + route("/bar", "PathPrefix:/bar"), + route("foo", "Host:foo")), + ), + ), + ), + }, + { + desc: "invalidPassTLSCertValue", + fixtures: []string{ + filepath.Join("fixtures", "invalidPassTLSCertValue_ingresses.yml"), + filepath.Join("fixtures", "invalidPassTLSCertValue_services.yml"), + }, + expected: buildConfiguration( + backends( + backend("foo/bar", + servers(server("http://example.com", weight(1))), + lbMethod("wrr"), + ), + ), + frontends( + frontend("foo/bar", + passHostHeader(), + routes( + route("/bar", "PathPrefix:/bar"), + route("foo", "Host:foo")), + ), + ), + ), + }, + { + desc: "invalidPassHostHeaderValue", + fixtures: []string{ + filepath.Join("fixtures", "invalidPassHostHeaderValue_ingresses.yml"), + filepath.Join("fixtures", "invalidPassHostHeaderValue_services.yml"), + }, + expected: buildConfiguration( + backends( + backend("foo/bar", + servers(server("http://example.com", weight(1))), + lbMethod("wrr"), + ), + ), + frontends( + frontend("foo/bar", + passHostHeader(), + routes( + route("/bar", "PathPrefix:/bar"), + route("foo", "Host:foo")), + ), + ), + ), + }, + { + desc: "missingResources", + fixtures: []string{ + filepath.Join("fixtures", "missingResources_ingresses.yml"), + filepath.Join("fixtures", "missingResources_services.yml"), + filepath.Join("fixtures", "missingResources_endpoints.yml"), + }, + expected: buildConfiguration( + backends( + backend("fully_working", + servers(server("http://10.10.0.1:8080", weight(1))), + lbMethod("wrr"), + ), + backend("missing_service", + servers(), + lbMethod("wrr"), + ), + backend("missing_endpoints", + servers(), + lbMethod("wrr"), + ), + backend("missing_endpoint_subsets", + servers(), + lbMethod("wrr"), + ), + ), + frontends( + frontend("fully_working", + passHostHeader(), + routes(route("fully_working", "Host:fully_working")), + ), + frontend("missing_endpoints", + passHostHeader(), + routes(route("missing_endpoints", "Host:missing_endpoints")), + ), + frontend("missing_endpoint_subsets", + passHostHeader(), + routes(route("missing_endpoint_subsets", "Host:missing_endpoint_subsets")), + ), + ), + ), + }, + { + desc: "ForwardAuth", + fixtures: []string{ + filepath.Join("fixtures", "loadIngressesForwardAuth_ingresses.yml"), + filepath.Join("fixtures", "loadIngressesForwardAuth_services.yml"), + filepath.Join("fixtures", "loadIngressesForwardAuth_endpoints.yml"), + }, + expected: buildConfiguration( + backends( + backend("foo/bar", + lbMethod("wrr"), + servers( + server("http://10.10.0.1:8080", weight(1))), + ), + ), + frontends( + frontend("foo/bar", + passHostHeader(), + auth(forwardAuth("https://auth.host", + fwdTrustForwardHeader(), + fwdAuthResponseHeaders("X-Auth", "X-Test", "X-Secret"))), + routes( + route("/bar", "PathPrefix:/bar"), + route("foo", "Host:foo")), + ), + ), + ), + }, + { + desc: "ForwardAuthMissingURL", + fixtures: []string{ + filepath.Join("fixtures", "loadIngressesForwardAuthMissingURL_ingresses.yml"), + filepath.Join("fixtures", "loadIngressesForwardAuthMissingURL_services.yml"), + filepath.Join("fixtures", "loadIngressesForwardAuthMissingURL_endpoints.yml"), + }, + expected: buildConfiguration( + backends( + backend("foo/bar", + lbMethod("wrr"), + servers(), + ), + ), + frontends(), + ), + }, + { + desc: "ForwardAuthWithTLSSecret", + fixtures: []string{ + filepath.Join("fixtures", "loadIngressesForwardAuthWithTLSSecret_ingresses.yml"), + filepath.Join("fixtures", "loadIngressesForwardAuthWithTLSSecret_services.yml"), + filepath.Join("fixtures", "loadIngressesForwardAuthWithTLSSecret_endpoints.yml"), + filepath.Join("fixtures", "loadIngressesForwardAuthWithTLSSecret_secrets.yml"), + }, + expected: buildConfiguration( + backends( + backend("foo/bar", + lbMethod("wrr"), + servers( + server("http://10.10.0.1:8080", weight(1))), + ), + ), + frontends( + frontend("foo/bar", + passHostHeader(), + auth( + forwardAuth("https://auth.host", + fwdAuthTLS( + "-----BEGIN CERTIFICATE-----\n-----END CERTIFICATE-----", + "-----BEGIN PRIVATE KEY-----\n-----END PRIVATE KEY-----", + true))), + routes( + route("/bar", "PathPrefix:/bar"), + route("foo", "Host:foo")), + ), + ), + ), + }, + { + desc: "tLSSecretLoad", + fixtures: []string{ + filepath.Join("fixtures", "tLSSecretLoad_ingresses.yml"), + filepath.Join("fixtures", "tLSSecretLoad_services.yml"), + filepath.Join("fixtures", "tLSSecretLoad_secrets.yml"), + }, + expected: buildConfiguration( + backends( + backend("ep1-ep2_example.com", + servers(), + lbMethod("wrr"), + ), + backend("ep1-ep2_example.org", + servers(), + lbMethod("wrr"), + ), + ), + frontends( + frontend("ep1-ep2_example.com", + entryPoints("ep1", "ep2"), + passHostHeader(), + routes( + route("example.com", "Host:example.com"), + ), + ), + frontend("ep1-ep2_example.org", + entryPoints("ep1", "ep2"), + passHostHeader(), + routes( + route("example.org", "Host:example.org"), + ), + ), + ), + tlsesSection( + tlsSection( + tlsEntryPoints("ep1", "ep2"), + certificate( + "-----BEGIN CERTIFICATE-----\n-----END CERTIFICATE-----", + "-----BEGIN PRIVATE KEY-----\n-----END PRIVATE KEY-----"), + ), + ), + ), + }, + { + desc: "multiPortServices", + fixtures: []string{ + filepath.Join("fixtures", "multiPortServices_ingresses.yml"), + filepath.Join("fixtures", "multiPortServices_services.yml"), + filepath.Join("fixtures", "multiPortServices_endpoints.yml"), + }, + expected: buildConfiguration( + backends( + backend("/cheddar", + lbMethod("wrr"), + servers( + server("http://10.10.0.1:8080", weight(1)), + server("http://10.10.0.2:8080", weight(1)), + ), + ), + backend("/stilton", + lbMethod("wrr"), + servers( + server("http://10.20.0.1:8081", weight(1)), + server("http://10.20.0.2:8081", weight(1)), + ), + ), + ), + frontends( + frontend("/cheddar", + passHostHeader(), + routes(route("/cheddar", "PathPrefix:/cheddar")), + ), + frontend("/stilton", + passHostHeader(), + routes(route("/stilton", "PathPrefix:/stilton")), + ), + ), + ), + }, + { + desc: "percentageWeightServiceAnnotation", + fixtures: []string{ + filepath.Join("fixtures", "percentageWeightServiceAnnotation_ingresses.yml"), + filepath.Join("fixtures", "percentageWeightServiceAnnotation_services.yml"), + filepath.Join("fixtures", "percentageWeightServiceAnnotation_endpoints.yml"), + }, + expected: buildConfiguration( + backends( + backend("host1/foo", + servers( + server("http://10.10.0.1:8080", weight(int(newPercentageValueFromFloat64(0.05)))), + server("http://10.10.0.2:8080", weight(int(newPercentageValueFromFloat64(0.05)))), + server("http://10.10.0.3:7070", weight(int(newPercentageValueFromFloat64(0.35)))), + server("http://10.10.0.4:7070", weight(int(newPercentageValueFromFloat64(0.35)))), + server("http://example.com:9090", weight(int(newPercentageValueFromFloat64(0.2)))), + ), + lbMethod("wrr"), + ), + backend("host1/bar", + servers( + server("http://10.10.0.3:7070", weight(int(newPercentageValueFromFloat64(0.5)))), + server("http://10.10.0.4:7070", weight(int(newPercentageValueFromFloat64(0.5)))), + ), + lbMethod("wrr"), + ), + ), + frontends( + frontend("host1/bar", + passHostHeader(), + routes( + route("/bar", "PathPrefix:/bar"), + route("host1", "Host:host1")), + ), + frontend("host1/foo", + passHostHeader(), + routes( + route("/foo", "PathPrefix:/foo"), + route("host1", "Host:host1")), + ), + ), + ), + }, + { + desc: "templateBreakingIngresssValues", + fixtures: []string{ + filepath.Join("fixtures", "templateBreakingIngresssValues_ingresses.yml"), + }, + expected: buildConfiguration( + backends(), + frontends(), + ), + }, + { + desc: "divergingIngressDefinitions", + fixtures: []string{ + filepath.Join("fixtures", "divergingIngressDefinitions_ingresses.yml"), + filepath.Join("fixtures", "divergingIngressDefinitions_services.yml"), + filepath.Join("fixtures", "divergingIngressDefinitions_endpoints.yml"), + }, + expected: buildConfiguration( + backends( + backend("host-a", + servers( + server("http://10.10.0.1:80", weight(1)), + server("http://10.10.0.2:80", weight(1)), + ), + lbMethod("wrr"), + ), + ), + frontends( + frontend("host-a", + passHostHeader(), + routes( + route("host-a", "Host:host-a")), + ), + ), + ), + }, + { + desc: "Empty IngressClass annotation", + provider: Provider{}, + fixtures: []string{ + filepath.Join("fixtures", "ingressClassAnnotation_ingresses.yml"), + filepath.Join("fixtures", "ingressClassAnnotation_services.yml"), + filepath.Join("fixtures", "ingressClassAnnotation_endpoints.yml"), + }, + expected: buildConfiguration( + backends( + backend("other/stuff", + servers( + server("http://example.com", weight(1)), + server("http://example.com", weight(1))), + lbMethod("wrr"), + ), + backend("other/", + servers( + server("http://example.com", weight(1)), + server("http://example.com", weight(1))), + lbMethod("wrr"), + ), + backend("other/sslstuff", + servers( + server("http://example.com", weight(1)), + server("http://example.com", weight(1))), + lbMethod("wrr"), + ), + ), + frontends( + frontend("other/stuff", + passHostHeader(), + routes( + route("/stuff", "PathPrefix:/stuff"), + route("other", "Host:other")), + ), + frontend("other/", + passHostHeader(), + routes( + route("/", "PathPrefix:/"), + route("other", "Host:other")), + ), + frontend("other/sslstuff", + passHostHeader(), + routes( + route("/sslstuff", "PathPrefix:/sslstuff"), + route("other", "Host:other")), + ), + ), + ), + }, + { + desc: "Provided IngressClass annotation", + provider: Provider{IngressClass: traefikDefaultRealm + "-other"}, + fixtures: []string{ + filepath.Join("fixtures", "ingressClassAnnotation_ingresses.yml"), + filepath.Join("fixtures", "ingressClassAnnotation_services.yml"), + filepath.Join("fixtures", "ingressClassAnnotation_endpoints.yml"), + }, + expected: buildConfiguration( + backends( + backend("foo/bar", + servers( + server("http://example.com", weight(1))), + lbMethod("wrr"), + ), + ), + frontends( + frontend("foo/bar", + passHostHeader(), + routes( + route("/bar", "PathPrefix:/bar"), + route("foo", "Host:foo")), + ), + ), + ), + }, + { + desc: "Provided IngressClass annotation", + provider: Provider{IngressClass: "custom"}, + fixtures: []string{ + filepath.Join("fixtures", "ingressClassAnnotation_ingresses.yml"), + filepath.Join("fixtures", "ingressClassAnnotation_services.yml"), + filepath.Join("fixtures", "ingressClassAnnotation_endpoints.yml"), + }, + expected: buildConfiguration( + backends( + backend("foo/bar", + servers( + server("http://10.10.0.1:80", weight(1))), + lbMethod("wrr"), + ), + ), + frontends( + frontend("foo/bar", + passHostHeader(), + routes( + route("/bar", "PathPrefix:/bar"), + route("foo", "Host:foo")), + ), + ), + ), + }, + { + desc: "BasicAuth", + provider: Provider{}, + fixtures: []string{ + filepath.Join("fixtures", "loadIngressesBasicAuth_ingresses.yml"), + filepath.Join("fixtures", "loadIngressesBasicAuth_services.yml"), + filepath.Join("fixtures", "loadIngressesBasicAuth_secrets.yml"), + }, + expected: buildConfiguration( + backends( + backend("basic/auth", + servers( + server("http://example.com", weight(1))), + lbMethod("wrr"), + ), + ), + frontends( + frontend("basic/auth", + auth(basicAuth(baUsers("myUser:myEncodedPW"), baRemoveHeaders())), + passHostHeader(), + routes( + route("/auth", "PathPrefix:/auth"), + route("basic", "Host:basic")), + ), + ), + ), + }, } - services := []*corev1.Service{ - buildService( - sName("service1"), - sNamespace("testing"), - sUID("1"), - sSpec( - clusterIP("10.0.0.1"), - sPorts(sPort(80, ""))), - ), - buildService( - sName("service2"), - sNamespace("testing"), - sUID("2"), - sSpec( - clusterIP("10.0.0.2"), - sPorts(sPort(802, ""))), - ), - buildService( - sName("service3"), - sNamespace("testing"), - sUID("3"), - sSpec( - clusterIP("10.0.0.3"), - sPorts( - sPort(80, "http"), - sPort(443, "https")), - ), - ), - buildService( - sName("service4"), - sNamespace("testing"), - sUID("4"), - sSpec( - clusterIP("10.0.0.4"), - sType("ExternalName"), - sExternalName("example.com"), - sPorts(sPort(443, "https"))), - ), - buildService( - sName("service5"), - sNamespace("testing"), - sUID("5"), - sSpec( - clusterIP("10.0.0.5"), - sType("ExternalName"), - sExternalName("example.com"), - sPorts(sPort(8888, "http"))), - ), - buildService( - sName("service6"), - sNamespace("testing"), - sUID("6"), - sSpec( - clusterIP("10.0.0.6"), - sPorts(sPort(80, ""))), - ), - buildService( - sName("service7"), - sNamespace("testing"), - sUID("7"), - sSpec( - clusterIP("10.0.0.7"), - sPorts(sPort(80, ""))), - ), - buildService( - sName("service8"), - sNamespace("testing"), - sUID("8"), - sSpec( - clusterIP("10.0.0.8"), - sPorts(sPort(80, ""))), - ), + for _, test := range testCases { + test := test + t.Run(test.desc, func(t *testing.T) { + t.Parallel() + + client := newClientMock(test.fixtures...) + client.watchChan = make(chan interface{}) + + actual, err := test.provider.loadIngresses(client) + require.NoError(t, err, "error loading ingresses") + + // f, err := os.Create(filepath.Join("temp", test.desc+".toml")) + // require.NoError(t, err, "error creating file") + // err = toml.NewEncoder(f).Encode(test.expected) + // require.NoError(t, err, "error writing TOML") + + assert.Equal(t, test.expected, actual) + }) } - - endpoints := []*corev1.Endpoints{ - buildEndpoint( - eNamespace("testing"), - eName("service1"), - eUID("1"), - subset( - eAddresses(eAddress("10.10.0.1")), - ePorts(ePort(8080, ""))), - subset( - eAddresses(eAddress("10.21.0.1")), - ePorts(ePort(8080, ""))), - ), - buildEndpoint( - eNamespace("testing"), - eName("service3"), - eUID("3"), - subset( - eAddresses(eAddress("10.15.0.1")), - ePorts( - ePort(8080, "http"), - ePort(8443, "https")), - ), - subset( - eAddresses(eAddress("10.15.0.2")), - ePorts( - ePort(9080, "http"), - ePort(9443, "https")), - ), - ), - buildEndpoint( - eNamespace("testing"), - eName("service6"), - eUID("6"), - subset( - eAddresses(eAddressWithTargetRef("http://10.15.0.3:80", "10.15.0.3")), - ePorts(ePort(80, ""))), - ), - buildEndpoint( - eNamespace("testing"), - eName("service7"), - eUID("7"), - subset( - eAddresses(eAddress("10.10.0.7")), - ePorts(ePort(80, ""))), - ), - buildEndpoint( - eNamespace("testing"), - eName("service8"), - eUID("8"), - subset( - eAddresses(eAddress("10.10.0.8")), - ePorts(ePort(80, ""))), - ), - } - - watchChan := make(chan interface{}) - client := clientMock{ - ingresses: ingresses, - services: services, - endpoints: endpoints, - watchChan: watchChan, - } - provider := Provider{} - - actual, err := provider.loadIngresses(client) - require.NoError(t, err, "error loading ingresses") - - expected := buildConfiguration( - backends( - backend("foo/bar", - lbMethod("wrr"), - servers( - server("http://10.10.0.1:8080", weight(1)), - server("http://10.21.0.1:8080", weight(1))), - ), - backend("foo/namedthing", - lbMethod("wrr"), - servers( - server("https://example.com", weight(1)), - ), - ), - backend("bar", - lbMethod("wrr"), - servers( - server("https://10.15.0.1:8443", weight(1)), - server("https://10.15.0.2:9443", weight(1)), - ), - ), - backend("service5", - lbMethod("wrr"), - servers( - server("http://example.com:8888", weight(1)), - ), - ), - backend("service6", - lbMethod("wrr"), - servers( - server("http://10.15.0.3:80", weight(1)), - ), - ), - backend("*.service7", - lbMethod("wrr"), - servers( - server("http://10.10.0.7:80", weight(1)), - ), - ), - backend("service8", - lbMethod("wrr"), - servers( - server("http://10.10.0.8:80", weight(1)), - ), - ), - ), - frontends( - frontend("foo/bar", - passHostHeader(), - routes( - route("/bar", "PathPrefix:/bar"), - route("foo", "Host:foo")), - ), - frontend("foo/namedthing", - passHostHeader(), - routes( - route("/namedthing", "PathPrefix:/namedthing"), - route("foo", "Host:foo")), - ), - frontend("bar", - passHostHeader(), - routes(route("bar", "Host:bar")), - ), - frontend("service5", - passHostHeader(), - routes(route("service5", "Host:service5")), - ), - frontend("service6", - passHostHeader(), - routes(route("service6", "Host:service6")), - ), - frontend("*.service7", - passHostHeader(), - routes(route("*.service7", "HostRegexp:{subdomain:[A-Za-z0-9-_]+}.service7")), - ), - frontend("service8", - passHostHeader(), - routes(route("/", "PathPrefix:/")), - ), - ), - ) - assert.Equal(t, expected, actual) } -func TestLoadGlobalIngressWithExternalName(t *testing.T) { - ingresses := []*extensionsv1beta1.Ingress{ - buildIngress( - iNamespace("testing"), - iSpecBackends(iSpecBackend(iIngressBackend("service1", intstr.FromInt(80)))), - ), - } - - services := []*corev1.Service{ - buildService( - sName("service1"), - sNamespace("testing"), - sUID("1"), - sSpec( - sType("ExternalName"), - sExternalName("some-external-name"), - sPorts(sPort(80, ""))), - ), - } - - watchChan := make(chan interface{}) - client := clientMock{ - ingresses: ingresses, - services: services, - watchChan: watchChan, - } - provider := Provider{} - - actual, err := provider.loadIngresses(client) - require.NoError(t, err, "error loading ingresses") - - expected := buildConfiguration( - backends( - backend("global-default-backend", - lbMethod("wrr"), - servers( - server("http://some-external-name", weight(1)), +func Test_addGlobalBackend(t *testing.T) { + testCases := []struct { + desc string + client clientMock + ingress *extensionsv1beta1.Ingress + config *types.Configuration + expected string + }{ + { + desc: "Duplicate Frontend", + client: clientMock{}, + ingress: buildIngress( + iNamespace("testing"), + iSpecBackends(iSpecBackend(iIngressBackend("service1", intstr.FromInt(80)))), + ), + config: buildConfiguration( + frontends( + frontend("global-default-backend", + frontendName("global-default-frontend"), + passHostHeader(), + routes( + route("/", "PathPrefix:/"), + ), + ), ), ), - ), - frontends( - frontend("global-default-backend", - frontendName("global-default-frontend"), - passHostHeader(), - routes( - route("/", "PathPrefix:/"), - ), + expected: "duplicate frontend: global-default-frontend", + }, + { + desc: "Duplicate Backend", + client: clientMock{}, + ingress: buildIngress( + iNamespace("testing"), + iSpecBackends(iSpecBackend(iIngressBackend("service1", intstr.FromInt(80)))), ), - ), - ) - assert.Equal(t, expected, actual) + config: buildConfiguration( + backends( + backend("global-default-backend", + lbMethod("wrr"), + servers( + server("http://10.10.0.1:8080", weight(1)), + ), + ), + )), + expected: "duplicate backend: global-default-backend", + }, + { + desc: "ServiceMissing", + client: clientMock{}, + ingress: buildIngress( + iNamespace("testing"), + iSpecBackends(iSpecBackend(iIngressBackend("service1", intstr.FromInt(80)))), + ), + config: buildConfiguration( + frontends(), + backends(), + ), + expected: "service not found for testing/service1", + }, + { + desc: "ServiceAPIError", + client: clientMock{ + apiServiceError: errors.New("failed kube api call"), + }, + ingress: buildIngress( + iNamespace("testing"), + iSpecBackends(iSpecBackend(iIngressBackend("service1", intstr.FromInt(80)))), + ), + config: buildConfiguration( + frontends(), + backends(), + ), + expected: "error while retrieving service information from k8s API testing/service1: failed kube api call", + }, + { + desc: "EndpointMissing", + client: clientMock{ + services: []*corev1.Service{ + buildService( + sName("service"), + sNamespace("testing"), + sUID("1"), + sSpec( + clusterIP("10.0.0.1"), + sPorts(sPort(80, "")), + ), + ), + }, + }, + ingress: buildIngress( + iNamespace("testing"), + iSpecBackends(iSpecBackend(iIngressBackend("service", intstr.FromInt(80)))), + ), + config: buildConfiguration( + frontends(), + backends(), + ), + expected: "endpoints not found for testing/service", + }, + { + desc: "EndpointAPIError", + client: clientMock{ + apiEndpointsError: errors.New("failed kube api call"), + services: []*corev1.Service{ + buildService( + sName("service"), + sNamespace("testing"), + sUID("1"), + sSpec( + clusterIP("10.0.0.1"), + sPorts(sPort(80, "")), + ), + ), + }, + }, + ingress: buildIngress( + iNamespace("testing"), + iSpecBackends(iSpecBackend(iIngressBackend("service", intstr.FromInt(80)))), + ), + config: buildConfiguration( + frontends(), + backends(), + ), + expected: "error retrieving endpoint information from k8s API testing/service: failed kube api call", + }, + } + + for _, test := range testCases { + test := test + t.Run(test.desc, func(t *testing.T) { + t.Parallel() + + provider := Provider{} + + mock := test.client + mock.watchChan = make(chan interface{}) + + err := provider.addGlobalBackend(mock, test.ingress, test.config) + assert.EqualError(t, err, test.expected) + }) + } } -func TestLoadGlobalIngressWithPortNumbers(t *testing.T) { - ingresses := []*extensionsv1beta1.Ingress{ - buildIngress( - iNamespace("testing"), - iSpecBackends(iSpecBackend(iIngressBackend("service1", intstr.FromInt(80)))), - ), - } - - services := []*corev1.Service{ - buildService( - sName("service1"), - sNamespace("testing"), - sUID("1"), - sSpec( - clusterIP("10.0.0.1"), - sPorts(sPort(80, ""))), - ), - } - - endpoints := []*corev1.Endpoints{ - buildEndpoint( - eNamespace("testing"), - eName("service1"), - eUID("1"), - subset( - eAddresses(eAddress("10.10.0.1")), - ePorts(ePort(8080, ""))), - ), - } - - watchChan := make(chan interface{}) - client := clientMock{ - ingresses: ingresses, - services: services, - endpoints: endpoints, - watchChan: watchChan, - } - provider := Provider{} - - actual, err := provider.loadIngresses(client) - require.NoError(t, err, "error loading ingresses") - - expected := buildConfiguration( - backends( - backend("global-default-backend", - lbMethod("wrr"), - servers( - server("http://10.10.0.1:8080", weight(1)), - ), - ), - ), - frontends( - frontend("global-default-backend", - frontendName("global-default-frontend"), - passHostHeader(), - routes( - route("/", "PathPrefix:/"), - ), - ), - ), - ) - assert.Equal(t, expected, actual) +func TestProvider_newK8sClient_inCluster(t *testing.T) { + p := Provider{} + os.Setenv("KUBERNETES_SERVICE_HOST", "localhost") + os.Setenv("KUBERNETES_SERVICE_PORT", "443") + defer os.Clearenv() + _, err := p.newK8sClient("") + assert.EqualError(t, err, "failed to create in-cluster configuration: open /var/run/secrets/kubernetes.io/serviceaccount/token: no such file or directory") } -func TestLoadGlobalIngressWithHttpsPortNames(t *testing.T) { - ingresses := []*extensionsv1beta1.Ingress{ - buildIngress( - iNamespace("testing"), - iSpecBackends(iSpecBackend(iIngressBackend("service1", intstr.FromString("https-global")))), - ), - } +func TestProvider_newK8sClient_inCluster_failLabelSel(t *testing.T) { + p := Provider{} + os.Setenv("KUBERNETES_SERVICE_HOST", "localhost") + os.Setenv("KUBERNETES_SERVICE_PORT", "443") + defer os.Clearenv() + _, err := p.newK8sClient("%") + assert.EqualError(t, err, "invalid ingress label selector: \"%\"") +} - services := []*corev1.Service{ - buildService( - sName("service1"), - sNamespace("testing"), - sUID("1"), - sSpec( - clusterIP("10.0.0.1"), - sPorts(sPort(8443, "https-global"))), - ), - } - - endpoints := []*corev1.Endpoints{ - buildEndpoint( - eNamespace("testing"), - eName("service1"), - eUID("1"), - subset( - eAddresses(eAddress("10.10.0.1")), - ePorts(ePort(8080, "https-global"))), - ), - } - - watchChan := make(chan interface{}) - client := clientMock{ - ingresses: ingresses, - services: services, - endpoints: endpoints, - watchChan: watchChan, - } - provider := Provider{} - - actual, err := provider.loadIngresses(client) - require.NoError(t, err, "error loading ingresses") - - expected := buildConfiguration( - backends( - backend("global-default-backend", - lbMethod("wrr"), - servers( - server("https://10.10.0.1:8080", weight(1)), - ), - ), - ), - frontends( - frontend("global-default-backend", - frontendName("global-default-frontend"), - passHostHeader(), - routes( - route("/", "PathPrefix:/"), - ), - ), - ), - ) - assert.Equal(t, expected, actual) +func TestProvider_newK8sClient_outOfCluster(t *testing.T) { + p := Provider{} + p.Endpoint = "localhost" + _, err := p.newK8sClient("") + assert.NoError(t, err) } func TestRuleType(t *testing.T) { @@ -736,97 +1558,7 @@ func TestModifierFails(t *testing.T) { } } -func TestGetPassHostHeader(t *testing.T) { - ingresses := []*extensionsv1beta1.Ingress{ - buildIngress( - iNamespace("awesome"), - iRules(iRule( - iHost("foo"), - iPaths(onePath( - iPath("/bar"), - iBackend("service1", intstr.FromInt(801)))), - )), - ), - } - - services := []*corev1.Service{ - buildService( - sNamespace("awesome"), sName("service1"), sUID("1"), - sSpec(sPorts(sPort(801, "http"))), - ), - } - - watchChan := make(chan interface{}) - client := clientMock{ - ingresses: ingresses, - services: services, - watchChan: watchChan, - } - provider := Provider{DisablePassHostHeaders: true} - - actual, err := provider.loadIngresses(client) - require.NoError(t, err, "error loading ingresses") - - expected := buildConfiguration( - backends(backend("foo/bar", lbMethod("wrr"), servers())), - frontends( - frontend("foo/bar", - routes( - route("/bar", "PathPrefix:/bar"), - route("foo", "Host:foo")), - ), - ), - ) - - assert.Equal(t, expected, actual) -} - -// Deprecated -func TestGetPassTLSCert(t *testing.T) { - ingresses := []*extensionsv1beta1.Ingress{ - buildIngress(iNamespace("awesome"), - iRules(iRule( - iHost("foo"), - iPaths(onePath(iPath("/bar"), iBackend("service1", intstr.FromInt(80))))), - ), - ), - } - - services := []*corev1.Service{ - buildService( - sName("service1"), - sNamespace("awesome"), - sUID("1"), - sSpec(sPorts(sPort(801, "http"))), - ), - } - - watchChan := make(chan interface{}) - client := clientMock{ - ingresses: ingresses, - services: services, - watchChan: watchChan, - } - provider := Provider{EnablePassTLSCert: true} - - actual, err := provider.loadIngresses(client) - require.NoError(t, err, "error loading ingresses") - - expected := buildConfiguration( - backends(backend("foo/bar", lbMethod("wrr"), servers())), - frontends(frontend("foo/bar", - passHostHeader(), - passTLSCert(), - routes( - route("/bar", "PathPrefix:/bar"), - route("foo", "Host:foo")), - )), - ) - - assert.Equal(t, expected, actual) -} - -func TestInvalidRedirectAnnotation(t *testing.T) { +func Test_getFrontendRedirect_InvalidRedirectAnnotation(t *testing.T) { ingresses := []*extensionsv1beta1.Ingress{ buildIngress(iNamespace("awesome"), iAnnotation(annotationKubernetesRedirectRegex, `bad\.regex`), @@ -854,1301 +1586,7 @@ func TestInvalidRedirectAnnotation(t *testing.T) { } } -func TestOnlyReferencesServicesFromOwnNamespace(t *testing.T) { - ingresses := []*extensionsv1beta1.Ingress{ - buildIngress(iNamespace("awesome"), - iRules(iRule( - iHost("foo"), - iPaths(onePath(iBackend("service", intstr.FromInt(80))))), - ), - ), - } - - services := []*corev1.Service{ - buildService( - sNamespace("awesome"), - sName("service"), - sUID("1"), - sSpec( - clusterIP("10.0.0.1"), - sPorts(sPort(80, "http"))), - ), - buildService( - sNamespace("not-awesome"), - sName("service"), - sUID("2"), - sSpec( - clusterIP("10.0.0.2"), - sPorts(sPort(80, "http"))), - ), - } - - watchChan := make(chan interface{}) - client := clientMock{ - ingresses: ingresses, - services: services, - watchChan: watchChan, - } - provider := Provider{} - - actual, err := provider.loadIngresses(client) - require.NoError(t, err, "error loading ingresses") - - expected := buildConfiguration( - backends(backend("foo", lbMethod("wrr"), servers())), - frontends(frontend("foo", - passHostHeader(), - routes(route("foo", "Host:foo")), - )), - ) - - assert.Equal(t, expected, actual) -} - -func TestHostlessIngress(t *testing.T) { - ingresses := []*extensionsv1beta1.Ingress{ - buildIngress(iNamespace("awesome"), - iRules(iRule( - iPaths(onePath(iPath("/bar"), iBackend("service1", intstr.FromInt(801))))), - ), - ), - } - - services := []*corev1.Service{ - buildService( - sName("service1"), - sNamespace("awesome"), - sUID("1"), - sSpec( - clusterIP("10.0.0.1"), - sPorts(sPort(801, "http"))), - ), - } - - watchChan := make(chan interface{}) - client := clientMock{ - ingresses: ingresses, - services: services, - watchChan: watchChan, - } - provider := Provider{DisablePassHostHeaders: true} - - actual, err := provider.loadIngresses(client) - require.NoError(t, err, "error loading ingresses") - - expected := buildConfiguration( - backends(backend("/bar", lbMethod("wrr"), servers())), - frontends(frontend("/bar", - routes(route("/bar", "PathPrefix:/bar")))), - ) - - assert.Equal(t, expected, actual) -} - -func TestServiceAnnotations(t *testing.T) { - ingresses := []*extensionsv1beta1.Ingress{ - buildIngress(iNamespace("testing"), - iRules( - iRule( - iHost("foo"), - iPaths(onePath(iPath("/bar"), iBackend("service1", intstr.FromInt(80))))), - iRule( - iHost("bar"), - iPaths(onePath(iBackend("service2", intstr.FromInt(802))))), - iRule( - iHost("baz"), - iPaths(onePath(iBackend("service3", intstr.FromInt(803))))), - iRule( - iHost("max-conn"), - iPaths(onePath(iBackend("service4", intstr.FromInt(804))))), - iRule( - iHost("flush"), - iPaths(onePath(iBackend("service5", intstr.FromInt(805))))), - ), - ), - } - - services := []*corev1.Service{ - buildService( - sName("service1"), - sNamespace("testing"), - sUID("1"), - sAnnotation(annotationKubernetesCircuitBreakerExpression, "NetworkErrorRatio() > 0.5"), - sAnnotation(annotationKubernetesLoadBalancerMethod, "drr"), - sSpec( - clusterIP("10.0.0.1"), - sPorts(sPort(80, ""))), - ), - buildService( - sName("service2"), - sNamespace("testing"), - sUID("2"), - sAnnotation(annotationKubernetesCircuitBreakerExpression, ""), - sAnnotation(label.TraefikBackendLoadBalancerSticky, "true"), - sSpec( - clusterIP("10.0.0.2"), - sPorts(sPort(802, ""))), - ), - buildService( - sName("service3"), - sNamespace("testing"), - sUID("3"), - sAnnotation(annotationKubernetesBuffering, ` -maxrequestbodybytes: 10485760 -memrequestbodybytes: 2097153 -maxresponsebodybytes: 10485761 -memresponsebodybytes: 2097152 -retryexpression: IsNetworkError() && Attempts() <= 2 -`), - sSpec( - clusterIP("10.0.0.3"), - sPorts(sPort(803, "http"))), - ), - buildService( - sName("service4"), - sNamespace("testing"), - sUID("4"), - sAnnotation(annotationKubernetesMaxConnExtractorFunc, "client.ip"), - sAnnotation(annotationKubernetesMaxConnAmount, "6"), - sSpec( - clusterIP("10.0.0.4"), - sPorts(sPort(804, "http"))), - ), - buildService( - sName("service5"), - sNamespace("testing"), - sUID("5"), - sAnnotation(annotationKubernetesResponseForwardingFlushInterval, "10ms"), - sSpec( - clusterIP("10.0.0.5"), - sPorts(sPort(80, ""))), - ), - } - - endpoints := []*corev1.Endpoints{ - buildEndpoint( - eNamespace("testing"), - eName("service1"), - eUID("1"), - subset( - eAddresses(eAddress("10.10.0.1")), - ePorts(ePort(8080, ""))), - subset( - eAddresses(eAddress("10.21.0.1")), - ePorts(ePort(8080, ""))), - ), - buildEndpoint( - eNamespace("testing"), - eName("service2"), - eUID("2"), - subset( - eAddresses(eAddress("10.15.0.1")), - ePorts(ePort(8080, ""))), - subset( - eAddresses(eAddress("10.15.0.2")), - ePorts(ePort(8080, ""))), - ), - buildEndpoint( - eNamespace("testing"), - eName("service3"), - eUID("3"), - subset( - eAddresses(eAddress("10.14.0.1")), - ePorts(ePort(8080, "http"))), - subset( - eAddresses(eAddress("10.12.0.1")), - ePorts(ePort(8080, "http"))), - ), - buildEndpoint( - eNamespace("testing"), - eName("service4"), - eUID("4"), - subset( - eAddresses(eAddress("10.4.0.1")), - ePorts(ePort(8080, "http"))), - subset( - eAddresses(eAddress("10.4.0.2")), - ePorts(ePort(8080, "http"))), - ), - buildEndpoint( - eNamespace("testing"), - eName("service5"), - eUID("5"), - subset( - eAddresses(eAddress("10.4.0.1")), - ePorts(ePort(8080, "http"))), - subset( - eAddresses(eAddress("10.4.0.2")), - ePorts(ePort(8080, "http"))), - ), - } - - watchChan := make(chan interface{}) - client := clientMock{ - ingresses: ingresses, - services: services, - endpoints: endpoints, - watchChan: watchChan, - } - provider := Provider{} - - actual, err := provider.loadIngresses(client) - require.NoError(t, err, "error loading ingresses") - - expected := buildConfiguration( - backends( - backend("foo/bar", - servers( - server("http://10.10.0.1:8080", weight(1)), - server("http://10.21.0.1:8080", weight(1))), - lbMethod("drr"), - circuitBreaker("NetworkErrorRatio() > 0.5"), - ), - backend("flush", - servers(), - lbMethod("wrr"), - responseForwarding("10ms"), - ), - backend("bar", - servers( - server("http://10.15.0.1:8080", weight(1)), - server("http://10.15.0.2:8080", weight(1))), - lbMethod("wrr"), lbSticky(), - ), - backend("baz", - servers( - server("http://10.14.0.1:8080", weight(1)), - server("http://10.12.0.1:8080", weight(1))), - lbMethod("wrr"), - buffering( - maxRequestBodyBytes(10485760), - memRequestBodyBytes(2097153), - maxResponseBodyBytes(10485761), - memResponseBodyBytes(2097152), - retrying("IsNetworkError() && Attempts() <= 2"), - ), - ), - backend("max-conn", - servers( - server("http://10.4.0.1:8080", weight(1)), - server("http://10.4.0.2:8080", weight(1))), - maxConnExtractorFunc("client.ip"), - maxConnAmount(6), - lbMethod("wrr"), - ), - ), - frontends( - frontend("foo/bar", - passHostHeader(), - routes( - route("/bar", "PathPrefix:/bar"), - route("foo", "Host:foo")), - ), - frontend("bar", - passHostHeader(), - routes(route("bar", "Host:bar"))), - frontend("baz", - passHostHeader(), - routes(route("baz", "Host:baz"))), - frontend("max-conn", - passHostHeader(), - routes( - route("max-conn", "Host:max-conn"))), - frontend("flush", - passHostHeader(), - routes( - route("flush", "Host:flush"))), - ), - ) - - assert.EqualValues(t, expected, actual) -} - -func TestIngressAnnotations(t *testing.T) { - ingresses := []*extensionsv1beta1.Ingress{ - buildIngress( - iNamespace("testing"), - iAnnotation(annotationKubernetesPreserveHost, "false"), - iRules( - iRule( - iHost("foo"), - iPaths(onePath(iPath("/bar"), iBackend("service1", intstr.FromInt(80))))), - ), - ), - buildIngress( - iNamespace("testing"), - iAnnotation(annotationKubernetesPreserveHost, "true"), - iAnnotation(annotationKubernetesIngressClass, traefikDefaultRealm), - iRules( - iRule( - iHost("other"), - iPaths(onePath(iPath("/stuff"), iBackend("service1", intstr.FromInt(80))))), - ), - ), - buildIngress( - iNamespace("testing"), - iAnnotation(annotationKubernetesPassTLSCert, "true"), - iAnnotation(annotationKubernetesPassTLSClientCert, ` -pem: true -infos: - notafter: true - notbefore: true - sans: true - subject: - country: true - province: true - locality: true - organization: true - commonname: true - serialnumber: true - domaincomponent: true - issuer: - country: true - province: true - locality: true - organization: true - commonname: true - serialnumber: true - domaincomponent: true -`), - iAnnotation(annotationKubernetesIngressClass, traefikDefaultRealm), - iRules( - iRule( - iHost("other"), - iPaths(onePath(iPath("/sslstuff"), iBackend("service1", intstr.FromInt(80))))), - ), - ), - buildIngress( - iNamespace("testing"), - iAnnotation(annotationKubernetesFrontendEntryPoints, "http,https"), - iAnnotation(annotationKubernetesIngressClass, traefikDefaultRealm), - iRules( - iRule( - iHost("other"), - iPaths(onePath(iPath("/"), iBackend("service1", intstr.FromInt(80))))), - ), - ), - buildIngress( - iNamespace("testing"), - iAnnotation(annotationKubernetesAuthType, "basic"), - iAnnotation(annotationKubernetesAuthSecret, "mySecret"), - iRules( - iRule( - iHost("basic"), - iPaths(onePath(iPath("/auth"), iBackend("service1", intstr.FromInt(80))))), - ), - ), - buildIngress( - iNamespace("testing"), - iAnnotation(annotationKubernetesIngressClass, traefikDefaultRealm+"-other"), - iRules( - iRule( - iHost("herp"), - iPaths(onePath(iPath("/derp"), iBackend("service2", intstr.FromInt(80))))), - ), - ), - buildIngress( - iNamespace("testing"), - iAnnotation(annotationKubernetesWhiteListSourceRange, "1.1.1.1/24, 1234:abcd::42/32"), - iAnnotation(annotationKubernetesWhiteListUseXForwardedFor, "true"), - iRules( - iRule( - iHost("test"), - iPaths(onePath(iPath("/whitelist-source-range"), iBackend("service1", intstr.FromInt(80))))), - ), - ), - buildIngress( - iNamespace("testing"), - iAnnotation(annotationKubernetesRewriteTarget, "/"), - iRules( - iRule( - iHost("rewrite"), - iPaths(onePath(iPath("/api"), iBackend("service1", intstr.FromInt(80))))), - ), - ), - buildIngress( - iNamespace("testing"), - iAnnotation(annotationKubernetesAuthRealm, "customized"), - iRules( - iRule( - iHost("auth-realm-customized"), - iPaths(onePath(iPath("/auth-realm-customized"), iBackend("service1", intstr.FromInt(80))))), - ), - ), - buildIngress( - iNamespace("testing"), - iAnnotation(annotationKubernetesRedirectEntryPoint, "https"), - iRules( - iRule( - iHost("redirect"), - iPaths(onePath(iPath("/https"), iBackend("service1", intstr.FromInt(80))))), - ), - ), - buildIngress( - iNamespace("testing"), - iAnnotation(annotationKubernetesIngressClass, "traefik"), - iAnnotation(annotationKubernetesErrorPages, ` -foo: - status: - - "123" - - "456" - backend: bar - query: /bar -`), - iRules( - iRule( - iHost("error-pages"), - iPaths(onePath(iPath("/errorpages"), iBackend("service1", intstr.FromInt(80))))), - ), - ), - buildIngress( - iNamespace("testing"), - iAnnotation(annotationKubernetesIngressClass, "traefik"), - iAnnotation(annotationKubernetesRateLimit, ` -extractorfunc: client.ip -rateset: - bar: - period: 3s - average: 6 - burst: 9 - foo: - period: 6s - average: 12 - burst: 18 -`), - iRules( - iRule( - iHost("rate-limit"), - iPaths(onePath(iPath("/ratelimit"), iBackend("service1", intstr.FromInt(80))))), - ), - ), - buildIngress( - iNamespace("testing"), - iAnnotation(annotationKubernetesAppRoot, "/root"), - iRules( - iRule( - iHost("root"), - iPaths( - onePath(iPath("/"), iBackend("service1", intstr.FromInt(80))), - onePath(iPath("/root1"), iBackend("service1", intstr.FromInt(80))), - ), - ), - ), - ), - buildIngress( - iNamespace("testing"), - iAnnotation(annotationKubernetesAppRoot, "/root2"), - iAnnotation(annotationKubernetesRewriteTarget, "/abc"), - iRules( - iRule( - iHost("root2"), - iPaths( - onePath(iPath("/"), iBackend("service2", intstr.FromInt(80))), - ), - ), - ), - ), - buildIngress( - iNamespace("testing"), - iAnnotation(annotationKubernetesRuleType, ruleTypeReplacePath), - iAnnotation(annotationKubernetesRewriteTarget, "/abc"), - iRules( - iRule( - iHost("root2"), - iPaths( - onePath(iPath("/"), iBackend("service2", intstr.FromInt(80))), - ), - ), - ), - ), - buildIngress( - iNamespace("testing"), - iAnnotation(annotationKubernetesAppRoot, "/root"), - iRules( - iRule( - iHost("root3"), - iPaths( - onePath(iBackend("service1", intstr.FromInt(80))), - ), - ), - ), - ), - buildIngress( - iNamespace("testing"), - iAnnotation(annotationKubernetesIngressClass, "traefik"), - iAnnotation(annotationKubernetesCustomRequestHeaders, "Access-Control-Allow-Methods:POST,GET,OPTIONS || Content-type: application/json; charset=utf-8"), - iAnnotation(annotationKubernetesCustomResponseHeaders, "Access-Control-Allow-Methods:POST,GET,OPTIONS || Content-type: application/json; charset=utf-8"), - iAnnotation(annotationKubernetesSSLProxyHeaders, "Access-Control-Allow-Methods:POST,GET,OPTIONS || Content-type: application/json; charset=utf-8"), - iAnnotation(annotationKubernetesAllowedHosts, "foo, fii, fuu"), - iAnnotation(annotationKubernetesProxyHeaders, "foo, fii, fuu"), - iAnnotation(annotationKubernetesHSTSMaxAge, "666"), - iAnnotation(annotationKubernetesSSLForceHost, "true"), - iAnnotation(annotationKubernetesSSLRedirect, "true"), - iAnnotation(annotationKubernetesSSLTemporaryRedirect, "true"), - iAnnotation(annotationKubernetesHSTSIncludeSubdomains, "true"), - iAnnotation(annotationKubernetesForceHSTSHeader, "true"), - iAnnotation(annotationKubernetesHSTSPreload, "true"), - iAnnotation(annotationKubernetesFrameDeny, "true"), - iAnnotation(annotationKubernetesContentTypeNosniff, "true"), - iAnnotation(annotationKubernetesBrowserXSSFilter, "true"), - iAnnotation(annotationKubernetesCustomBrowserXSSValue, "foo"), - iAnnotation(annotationKubernetesIsDevelopment, "true"), - iAnnotation(annotationKubernetesSSLHost, "foo"), - iAnnotation(annotationKubernetesCustomFrameOptionsValue, "foo"), - iAnnotation(annotationKubernetesContentSecurityPolicy, "foo"), - iAnnotation(annotationKubernetesPublicKey, "foo"), - iAnnotation(annotationKubernetesReferrerPolicy, "foo"), - iRules( - iRule( - iHost("custom-headers"), - iPaths(onePath(iPath("/customheaders"), iBackend("service1", intstr.FromInt(80))))), - ), - ), - buildIngress( - iNamespace("testing"), - iAnnotation(annotationKubernetesProtocol, "h2c"), - iRules( - iRule( - iHost("protocol"), - iPaths(onePath(iPath("/valid"), iBackend("service1", intstr.FromInt(80))))), - ), - ), - buildIngress( - iNamespace("testing"), - iAnnotation(annotationKubernetesProtocol, "foobar"), - iRules( - iRule( - iHost("protocol"), - iPaths(onePath(iPath("/notvalid"), iBackend("service1", intstr.FromInt(80))))), - ), - ), - buildIngress( - iNamespace("testing"), - iAnnotation(annotationKubernetesProtocol, "http"), - iRules( - iRule( - iHost("protocol"), - iPaths(onePath(iPath("/missmatch"), iBackend("serviceHTTPS", intstr.FromInt(443))))), - ), - ), - buildIngress( - iNamespace("testing"), - iRules( - iRule( - iHost("protocol"), - iPaths(onePath(iPath("/noAnnotation"), iBackend("serviceHTTPS", intstr.FromInt(443))))), - ), - ), - } - - services := []*corev1.Service{ - buildService( - sName("service1"), - sNamespace("testing"), - sUID("1"), - sSpec( - clusterIP("10.0.0.1"), - sType("ExternalName"), - sExternalName("example.com"), - sPorts(sPort(80, "http"))), - ), - buildService( - sName("service2"), - sNamespace("testing"), - sUID("2"), - sSpec( - clusterIP("10.0.0.2"), - sPorts(sPort(802, ""))), - ), - buildService( - sName("serviceHTTPS"), - sNamespace("testing"), - sUID("2"), - sSpec( - clusterIP("10.0.0.3"), - sType("ExternalName"), - sExternalName("example.com"), - sPorts(sPort(443, "https"))), - ), - } - - secrets := []*corev1.Secret{ - { - ObjectMeta: metav1.ObjectMeta{ - Name: "mySecret", - UID: "1", - Namespace: "testing", - }, - Data: map[string][]byte{"auth": []byte("myUser:myEncodedPW")}, - }, - } - - watchChan := make(chan interface{}) - client := clientMock{ - ingresses: ingresses, - services: services, - secrets: secrets, - watchChan: watchChan, - } - provider := Provider{} - - actual, err := provider.loadIngresses(client) - require.NoError(t, err, "error loading ingresses") - - expected := buildConfiguration( - backends( - backend("foo/bar", - servers( - server("http://example.com", weight(1)), - server("http://example.com", weight(1))), - lbMethod("wrr"), - ), - backend("other/stuff", - servers( - server("http://example.com", weight(1)), - server("http://example.com", weight(1))), - lbMethod("wrr"), - ), - backend("http-https_other/", - servers( - server("http://example.com", weight(1)), - server("http://example.com", weight(1))), - lbMethod("wrr"), - ), - backend("other/sslstuff", - servers( - server("http://example.com", weight(1)), - server("http://example.com", weight(1))), - lbMethod("wrr"), - ), - backend("basic/auth", - servers( - server("http://example.com", weight(1)), - server("http://example.com", weight(1))), - lbMethod("wrr"), - ), - backend("redirect/https", - servers( - server("http://example.com", weight(1)), - server("http://example.com", weight(1))), - lbMethod("wrr"), - ), - backend("test/whitelist-source-range", - servers( - server("http://example.com", weight(1)), - server("http://example.com", weight(1))), - lbMethod("wrr"), - ), - backend("rewrite/api", - servers( - server("http://example.com", weight(1)), - server("http://example.com", weight(1))), - lbMethod("wrr"), - ), - backend("error-pages/errorpages", - servers( - server("http://example.com", weight(1)), - server("http://example.com", weight(1))), - lbMethod("wrr"), - ), - backend("rate-limit/ratelimit", - servers( - server("http://example.com", weight(1)), - server("http://example.com", weight(1))), - lbMethod("wrr"), - ), - backend("custom-headers/customheaders", - servers( - server("http://example.com", weight(1)), - server("http://example.com", weight(1))), - lbMethod("wrr"), - ), - backend("root/", - servers( - server("http://example.com", weight(1))), - lbMethod("wrr"), - ), - backend("root/root1", - servers( - server("http://example.com", weight(1))), - lbMethod("wrr"), - ), - backend("root2/", - servers(), - lbMethod("wrr"), - ), - backend("root3", - servers( - server("http://example.com", weight(1))), - lbMethod("wrr"), - ), - backend("protocol/valid", - servers( - server("h2c://example.com", weight(1)), - server("h2c://example.com", weight(1))), - lbMethod("wrr"), - ), - backend("protocol/notvalid", - servers(), - lbMethod("wrr"), - ), - backend("protocol/missmatch", - servers( - server("http://example.com", weight(1)), - server("http://example.com", weight(1))), - lbMethod("wrr"), - ), - backend("protocol/noAnnotation", - servers( - server("https://example.com", weight(1)), - server("https://example.com", weight(1))), - lbMethod("wrr"), - ), - ), - frontends( - frontend("foo/bar", - routes( - route("/bar", "PathPrefix:/bar"), - route("foo", "Host:foo")), - ), - frontend("other/stuff", - passHostHeader(), - routes( - route("/stuff", "PathPrefix:/stuff"), - route("other", "Host:other")), - ), - frontend("http-https_other/", - passHostHeader(), - entryPoints("http", "https"), - routes( - route("/", "PathPrefix:/"), - route("other", "Host:other")), - ), - frontend("other/sslstuff", - passHostHeader(), - passTLSClientCert(), - passTLSCert(), - routes( - route("/sslstuff", "PathPrefix:/sslstuff"), - route("other", "Host:other")), - ), - frontend("basic/auth", - passHostHeader(), - basicAuthDeprecated("myUser:myEncodedPW"), - routes( - route("/auth", "PathPrefix:/auth"), - route("basic", "Host:basic")), - ), - frontend("redirect/https", - passHostHeader(), - redirectEntryPoint("https"), - routes( - route("/https", "PathPrefix:/https"), - route("redirect", "Host:redirect")), - ), - frontend("test/whitelist-source-range", - passHostHeader(), - whiteList(true, "1.1.1.1/24", "1234:abcd::42/32"), - routes( - route("/whitelist-source-range", "PathPrefix:/whitelist-source-range"), - route("test", "Host:test")), - ), - frontend("rewrite/api", - passHostHeader(), - routes( - route("/api", "PathPrefix:/api;ReplacePathRegex: ^/api(.*) $1"), - route("rewrite", "Host:rewrite")), - ), - frontend("error-pages/errorpages", - passHostHeader(), - errorPage("foo", errorQuery("/bar"), errorStatus("123", "456"), errorBackend("bar")), - routes( - route("/errorpages", "PathPrefix:/errorpages"), - route("error-pages", "Host:error-pages")), - ), - frontend("rate-limit/ratelimit", - passHostHeader(), - rateLimit(rateExtractorFunc("client.ip"), - rateSet("foo", limitPeriod(6*time.Second), limitAverage(12), limitBurst(18)), - rateSet("bar", limitPeriod(3*time.Second), limitAverage(6), limitBurst(9))), - routes( - route("/ratelimit", "PathPrefix:/ratelimit"), - route("rate-limit", "Host:rate-limit")), - ), - frontend("custom-headers/customheaders", - passHostHeader(), - headers(&types.Headers{ - CustomRequestHeaders: map[string]string{ - "Access-Control-Allow-Methods": "POST,GET,OPTIONS", - "Content-Type": "application/json; charset=utf-8", - }, - CustomResponseHeaders: map[string]string{ - "Access-Control-Allow-Methods": "POST,GET,OPTIONS", - "Content-Type": "application/json; charset=utf-8", - }, - SSLProxyHeaders: map[string]string{ - "Access-Control-Allow-Methods": "POST,GET,OPTIONS", - "Content-Type": "application/json; charset=utf-8", - }, - AllowedHosts: []string{"foo", "fii", "fuu"}, - HostsProxyHeaders: []string{"foo", "fii", "fuu"}, - STSSeconds: 666, - SSLForceHost: true, - SSLRedirect: true, - SSLTemporaryRedirect: true, - STSIncludeSubdomains: true, - STSPreload: true, - ForceSTSHeader: true, - FrameDeny: true, - ContentTypeNosniff: true, - BrowserXSSFilter: true, - IsDevelopment: true, - CustomFrameOptionsValue: "foo", - SSLHost: "foo", - ContentSecurityPolicy: "foo", - PublicKey: "foo", - ReferrerPolicy: "foo", - CustomBrowserXSSValue: "foo", - }), - routes( - route("/customheaders", "PathPrefix:/customheaders"), - route("custom-headers", "Host:custom-headers")), - ), - frontend("root/", - passHostHeader(), - redirectRegex("root/$", "root/root"), - routes( - route("/", "PathPrefix:/"), - route("root", "Host:root"), - ), - ), - frontend("root2/", - passHostHeader(), - redirectRegex("root2/$", "root2/root2"), - routes( - route("/", "PathPrefix:/;ReplacePathRegex: ^/(.*) /abc$1"), - route("root2", "Host:root2"), - ), - ), - frontend("root/root1", - passHostHeader(), - routes( - route("/root1", "PathPrefix:/root1"), - route("root", "Host:root"), - ), - ), - frontend("root3", - passHostHeader(), - redirectRegex("root3/$", "root3/root"), - routes( - route("root3", "Host:root3"), - ), - ), - frontend("protocol/valid", - passHostHeader(), - routes( - route("/valid", "PathPrefix:/valid"), - route("protocol", "Host:protocol"), - ), - ), - frontend("protocol/notvalid", - passHostHeader(), - routes( - route("/notvalid", "PathPrefix:/notvalid"), - route("protocol", "Host:protocol"), - ), - ), - frontend("protocol/missmatch", - passHostHeader(), - routes( - route("/missmatch", "PathPrefix:/missmatch"), - route("protocol", "Host:protocol"), - ), - ), - frontend("protocol/noAnnotation", - passHostHeader(), - routes( - route("/noAnnotation", "PathPrefix:/noAnnotation"), - route("protocol", "Host:protocol"), - ), - ), - ), - ) - - assert.Equal(t, expected, actual) -} - -func TestIngressClassAnnotation(t *testing.T) { - ingresses := []*extensionsv1beta1.Ingress{ - buildIngress( - iNamespace("testing"), - iAnnotation(annotationKubernetesIngressClass, traefikDefaultIngressClass), - iRules( - iRule( - iHost("other"), - iPaths(onePath(iPath("/stuff"), iBackend("service1", intstr.FromInt(80))))), - ), - ), - buildIngress( - iNamespace("testing"), - iAnnotation(annotationKubernetesIngressClass, ""), - iRules( - iRule( - iHost("other"), - iPaths(onePath(iPath("/sslstuff"), iBackend("service1", intstr.FromInt(80))))), - ), - ), - buildIngress( - iNamespace("testing"), - iRules( - iRule( - iHost("other"), - iPaths(onePath(iPath("/"), iBackend("service1", intstr.FromInt(80))))), - ), - ), - buildIngress( - iNamespace("testing"), - iAnnotation(annotationKubernetesIngressClass, traefikDefaultIngressClass+"-other"), - iRules( - iRule( - iHost("foo"), - iPaths(onePath(iPath("/bar"), iBackend("service1", intstr.FromInt(80))))), - ), - ), - buildIngress( - iNamespace("testing"), - iAnnotation(annotationKubernetesIngressClass, "custom"), - iRules( - iRule( - iHost("foo"), - iPaths(onePath(iPath("/bar"), iBackend("service2", intstr.FromInt(80))))), - ), - ), - } - - services := []*corev1.Service{ - buildService( - sName("service1"), - sNamespace("testing"), - sUID("1"), - sSpec( - clusterIP("10.0.0.1"), - sType("ExternalName"), - sExternalName("example.com"), - sPorts(sPort(80, "http"))), - ), - buildService( - sName("service2"), - sNamespace("testing"), - sUID("2"), - sSpec( - clusterIP("10.0.0.2"), - sPorts(sPort(80, "http"))), - ), - } - - endpoints := []*corev1.Endpoints{ - buildEndpoint( - eName("service2"), - eUID("1"), - eNamespace("testing"), - subset( - eAddresses(eAddress("10.10.0.1")), - ePorts(ePort(80, "http"))), - ), - } - - watchChan := make(chan interface{}) - client := clientMock{ - ingresses: ingresses, - services: services, - endpoints: endpoints, - watchChan: watchChan, - } - - testCases := []struct { - desc string - provider Provider - expected *types.Configuration - }{ - { - desc: "Empty IngressClass annotation", - provider: Provider{}, - expected: buildConfiguration( - backends( - backend("other/stuff", - servers( - server("http://example.com", weight(1)), - server("http://example.com", weight(1))), - lbMethod("wrr"), - ), - backend("other/", - servers( - server("http://example.com", weight(1)), - server("http://example.com", weight(1))), - lbMethod("wrr"), - ), - backend("other/sslstuff", - servers( - server("http://example.com", weight(1)), - server("http://example.com", weight(1))), - lbMethod("wrr"), - ), - ), - frontends( - frontend("other/stuff", - passHostHeader(), - routes( - route("/stuff", "PathPrefix:/stuff"), - route("other", "Host:other")), - ), - frontend("other/", - passHostHeader(), - routes( - route("/", "PathPrefix:/"), - route("other", "Host:other")), - ), - frontend("other/sslstuff", - passHostHeader(), - routes( - route("/sslstuff", "PathPrefix:/sslstuff"), - route("other", "Host:other")), - ), - ), - ), - }, - { - desc: "Provided IngressClass annotation", - provider: Provider{IngressClass: traefikDefaultRealm + "-other"}, - expected: buildConfiguration( - backends( - backend("foo/bar", - servers( - server("http://example.com", weight(1))), - lbMethod("wrr"), - ), - ), - frontends( - frontend("foo/bar", - passHostHeader(), - routes( - route("/bar", "PathPrefix:/bar"), - route("foo", "Host:foo")), - ), - ), - ), - }, - { - desc: "Provided IngressClass annotation", - provider: Provider{IngressClass: "custom"}, - expected: buildConfiguration( - backends( - backend("foo/bar", - servers( - server("http://10.10.0.1:80", weight(1))), - lbMethod("wrr"), - ), - ), - frontends( - frontend("foo/bar", - passHostHeader(), - routes( - route("/bar", "PathPrefix:/bar"), - route("foo", "Host:foo")), - ), - ), - ), - }, - } - - for _, test := range testCases { - test := test - t.Run(test.desc, func(t *testing.T) { - t.Parallel() - - actual, err := test.provider.loadIngresses(client) - require.NoError(t, err, "error loading ingresses") - - assert.Equal(t, test.expected, actual) - }) - } -} - -func TestPriorityHeaderValue(t *testing.T) { - ingresses := []*extensionsv1beta1.Ingress{ - buildIngress( - iNamespace("testing"), - iAnnotation(annotationKubernetesPriority, "1337"), - iRules( - iRule( - iHost("foo"), - iPaths(onePath(iPath("/bar"), iBackend("service1", intstr.FromInt(80))))), - ), - ), - } - - services := []*corev1.Service{ - buildService( - sName("service1"), - sNamespace("testing"), - sUID("1"), - sSpec( - clusterIP("10.0.0.1"), - sType("ExternalName"), - sExternalName("example.com"), - sPorts(sPort(80, "http"))), - ), - } - - var endpoints []*corev1.Endpoints - watchChan := make(chan interface{}) - client := clientMock{ - ingresses: ingresses, - services: services, - endpoints: endpoints, - watchChan: watchChan, - } - provider := Provider{} - - actual, err := provider.loadIngresses(client) - require.NoError(t, err, "error loading ingresses") - - expected := buildConfiguration( - backends( - backend("1337-foo/bar", - servers(server("http://example.com", weight(1))), - lbMethod("wrr"), - ), - ), - frontends( - frontend("1337-foo/bar", - passHostHeader(), - priority(1337), - routes( - route("/bar", "PathPrefix:/bar"), - route("foo", "Host:foo")), - ), - ), - ) - - assert.Equal(t, expected, actual) -} - -func TestInvalidPassTLSCertValue(t *testing.T) { - ingresses := []*extensionsv1beta1.Ingress{ - buildIngress( - iNamespace("testing"), - iAnnotation(annotationKubernetesPassTLSCert, "herpderp"), - iRules( - iRule( - iHost("foo"), - iPaths(onePath(iPath("/bar"), iBackend("service1", intstr.FromInt(80))))), - ), - ), - } - - services := []*corev1.Service{ - buildService( - sName("service1"), - sNamespace("testing"), - sUID("1"), - sSpec( - clusterIP("10.0.0.1"), - sType("ExternalName"), - sExternalName("example.com"), - sPorts(sPort(80, "http"))), - ), - } - - watchChan := make(chan interface{}) - client := clientMock{ - ingresses: ingresses, - services: services, - watchChan: watchChan, - } - provider := Provider{} - - actual, err := provider.loadIngresses(client) - require.NoError(t, err, "error loading ingresses") - - expected := buildConfiguration( - backends( - backend("foo/bar", - servers(server("http://example.com", weight(1))), - lbMethod("wrr"), - ), - ), - frontends( - frontend("foo/bar", - passHostHeader(), - routes( - route("/bar", "PathPrefix:/bar"), - route("foo", "Host:foo")), - ), - ), - ) - - assert.Equal(t, expected, actual) -} - -func TestInvalidPassHostHeaderValue(t *testing.T) { - ingresses := []*extensionsv1beta1.Ingress{ - buildIngress( - iNamespace("testing"), - iAnnotation(annotationKubernetesPreserveHost, "herpderp"), - iRules( - iRule( - iHost("foo"), - iPaths(onePath(iPath("/bar"), iBackend("service1", intstr.FromInt(80))))), - ), - ), - } - - services := []*corev1.Service{ - buildService( - sName("service1"), - sNamespace("testing"), - sUID("1"), - sSpec( - clusterIP("10.0.0.1"), - sType("ExternalName"), - sExternalName("example.com"), - sPorts(sPort(80, "http"))), - ), - } - - watchChan := make(chan interface{}) - client := clientMock{ - ingresses: ingresses, - services: services, - watchChan: watchChan, - } - provider := Provider{} - - actual, err := provider.loadIngresses(client) - require.NoError(t, err, "error loading ingresses") - - expected := buildConfiguration( - backends( - backend("foo/bar", - servers(server("http://example.com", weight(1))), - lbMethod("wrr"), - ), - ), - frontends( - frontend("foo/bar", - passHostHeader(), - routes( - route("/bar", "PathPrefix:/bar"), - route("foo", "Host:foo")), - ), - ), - ) - - assert.Equal(t, expected, actual) -} - -func TestKubeAPIErrors(t *testing.T) { +func TestProvider_loadIngresses_KubeAPIErrors(t *testing.T) { ingresses := []*extensionsv1beta1.Ingress{ buildIngress( iNamespace("testing"), @@ -2189,17 +1627,17 @@ func TestKubeAPIErrors(t *testing.T) { }, } - for _, tc := range testCases { - tc := tc - t.Run(tc.desc, func(t *testing.T) { + for _, test := range testCases { + test := test + t.Run(test.desc, func(t *testing.T) { t.Parallel() client := clientMock{ ingresses: ingresses, services: services, watchChan: watchChan, - apiServiceError: tc.apiServiceErr, - apiEndpointsError: tc.apiEndpointsErr, + apiServiceError: test.apiServiceErr, + apiEndpointsError: test.apiEndpointsErr, } provider := Provider{} @@ -2216,406 +1654,7 @@ func TestKubeAPIErrors(t *testing.T) { } } -func TestMissingResources(t *testing.T) { - ingresses := []*extensionsv1beta1.Ingress{ - buildIngress( - iNamespace("testing"), - iRules( - iRule( - iHost("fully_working"), - iPaths(onePath(iBackend("fully_working_service", intstr.FromInt(80))))), - iRule( - iHost("missing_service"), - iPaths(onePath(iBackend("missing_service_service", intstr.FromInt(80))))), - iRule( - iHost("missing_endpoints"), - iPaths(onePath(iBackend("missing_endpoints_service", intstr.FromInt(80))))), - iRule( - iHost("missing_endpoint_subsets"), - iPaths(onePath(iBackend("missing_endpoint_subsets_service", intstr.FromInt(80))))), - ), - ), - } - - services := []*corev1.Service{ - buildService( - sName("fully_working_service"), - sNamespace("testing"), - sUID("1"), - sSpec( - clusterIP("10.0.0.1"), - sPorts(sPort(80, ""))), - ), - buildService( - sName("missing_endpoints_service"), - sNamespace("testing"), - sUID("3"), - sSpec( - clusterIP("10.0.0.3"), - sPorts(sPort(80, ""))), - ), - buildService( - sName("missing_endpoint_subsets_service"), - sNamespace("testing"), - sUID("4"), - sSpec( - clusterIP("10.0.0.4"), - sPorts(sPort(80, ""))), - ), - } - - endpoints := []*corev1.Endpoints{ - buildEndpoint( - eName("fully_working_service"), - eUID("1"), - eNamespace("testing"), - subset( - eAddresses(eAddress("10.10.0.1")), - ePorts(ePort(8080, ""))), - ), - buildEndpoint( - eName("missing_endpoint_subsets_service"), - eUID("4"), - eNamespace("testing"), - ), - } - - watchChan := make(chan interface{}) - client := clientMock{ - ingresses: ingresses, - services: services, - endpoints: endpoints, - watchChan: watchChan, - } - - provider := Provider{} - - actual, err := provider.loadIngresses(client) - require.NoError(t, err, "error loading ingresses") - - expected := buildConfiguration( - backends( - backend("fully_working", - servers(server("http://10.10.0.1:8080", weight(1))), - lbMethod("wrr"), - ), - backend("missing_service", - servers(), - lbMethod("wrr"), - ), - backend("missing_endpoints", - servers(), - lbMethod("wrr"), - ), - backend("missing_endpoint_subsets", - servers(), - lbMethod("wrr"), - ), - ), - frontends( - frontend("fully_working", - passHostHeader(), - routes(route("fully_working", "Host:fully_working")), - ), - frontend("missing_endpoints", - passHostHeader(), - routes(route("missing_endpoints", "Host:missing_endpoints")), - ), - frontend("missing_endpoint_subsets", - passHostHeader(), - routes(route("missing_endpoint_subsets", "Host:missing_endpoint_subsets")), - ), - ), - ) - - assert.Equal(t, expected, actual) -} - -func TestLoadIngressesBasicAuth(t *testing.T) { - ingresses := []*extensionsv1beta1.Ingress{ - buildIngress( - iNamespace("testing"), - iAnnotation(annotationKubernetesAuthType, "basic"), - iAnnotation(annotationKubernetesAuthSecret, "mySecret"), - iAnnotation(annotationKubernetesAuthRemoveHeader, "true"), - iRules( - iRule( - iHost("basic"), - iPaths(onePath(iPath("/auth"), iBackend("service1", intstr.FromInt(80))))), - ), - ), - } - - services := []*corev1.Service{ - buildService( - sName("service1"), - sNamespace("testing"), - sUID("1"), - sSpec( - clusterIP("10.0.0.1"), - sType("ExternalName"), - sExternalName("example.com"), - sPorts(sPort(80, "http"))), - ), - } - - secrets := []*corev1.Secret{{ - ObjectMeta: metav1.ObjectMeta{ - Name: "mySecret", - UID: "1", - Namespace: "testing", - }, - Data: map[string][]byte{ - "auth": []byte("myUser:myEncodedPW"), - }, - }} - - var endpoints []*corev1.Endpoints - watchChan := make(chan interface{}) - client := clientMock{ - ingresses: ingresses, - services: services, - secrets: secrets, - endpoints: endpoints, - watchChan: watchChan, - } - provider := Provider{} - - actual, err := provider.loadIngresses(client) - require.NoError(t, err, "error loading ingresses") - - actual = provider.loadConfig(*actual) - require.NotNil(t, actual) - actualBasicAuth := actual.Frontends["basic/auth"].Auth.Basic - assert.Equal(t, types.Users{"myUser:myEncodedPW"}, actualBasicAuth.Users) - assert.True(t, actualBasicAuth.RemoveHeader, "Bad RemoveHeader flag") -} - -func TestLoadIngressesForwardAuth(t *testing.T) { - ingresses := []*extensionsv1beta1.Ingress{ - buildIngress( - iNamespace("testing"), - iAnnotation(annotationKubernetesAuthType, "forward"), - iAnnotation(annotationKubernetesAuthForwardURL, "https://auth.host"), - iAnnotation(annotationKubernetesAuthForwardTrustHeaders, "true"), - iAnnotation(annotationKubernetesAuthForwardResponseHeaders, "X-Auth,X-Test,X-Secret"), - iRules( - iRule(iHost("foo"), - iPaths( - onePath(iPath("/bar"), iBackend("service1", intstr.FromInt(80))))), - ), - ), - } - - services := []*corev1.Service{ - buildService( - sName("service1"), - sNamespace("testing"), - sUID("1"), - sSpec( - clusterIP("10.0.0.1"), - sPorts(sPort(80, ""))), - ), - } - - endpoints := []*corev1.Endpoints{ - buildEndpoint( - eNamespace("testing"), - eName("service1"), - eUID("1"), - subset( - eAddresses(eAddress("10.10.0.1")), - ePorts(ePort(8080, ""))), - ), - } - - watchChan := make(chan interface{}) - client := clientMock{ - ingresses: ingresses, - services: services, - endpoints: endpoints, - watchChan: watchChan, - } - provider := Provider{} - - actual, err := provider.loadIngresses(client) - require.NoError(t, err, "error loading ingresses") - - expected := buildConfiguration( - backends( - backend("foo/bar", - lbMethod("wrr"), - servers( - server("http://10.10.0.1:8080", weight(1))), - ), - ), - frontends( - frontend("foo/bar", - passHostHeader(), - auth(forwardAuth("https://auth.host", - fwdTrustForwardHeader(), - fwdAuthResponseHeaders("X-Auth", "X-Test", "X-Secret"))), - routes( - route("/bar", "PathPrefix:/bar"), - route("foo", "Host:foo")), - ), - ), - ) - - assert.Equal(t, expected, actual) -} - -func TestLoadIngressesForwardAuthMissingURL(t *testing.T) { - ingresses := []*extensionsv1beta1.Ingress{ - buildIngress( - iNamespace("testing"), - iAnnotation(annotationKubernetesAuthType, "forward"), - iRules( - iRule(iHost("foo"), - iPaths( - onePath(iPath("/bar"), iBackend("service1", intstr.FromInt(80))))), - ), - ), - } - - services := []*corev1.Service{ - buildService( - sName("service1"), - sNamespace("testing"), - sUID("1"), - sSpec( - clusterIP("10.0.0.1"), - sPorts(sPort(80, ""))), - ), - } - - endpoints := []*corev1.Endpoints{ - buildEndpoint( - eNamespace("testing"), - eName("service1"), - eUID("1"), - subset( - eAddresses(eAddress("10.10.0.1")), - ePorts(ePort(8080, ""))), - ), - } - - watchChan := make(chan interface{}) - client := clientMock{ - ingresses: ingresses, - services: services, - endpoints: endpoints, - watchChan: watchChan, - } - provider := Provider{} - - actual, err := provider.loadIngresses(client) - require.NoError(t, err, "error loading ingresses") - - expected := buildConfiguration( - backends( - backend("foo/bar", - lbMethod("wrr"), - servers(), - ), - ), - frontends(), - ) - - assert.Equal(t, expected, actual) -} - -func TestLoadIngressesForwardAuthWithTLSSecret(t *testing.T) { - ingresses := []*extensionsv1beta1.Ingress{ - buildIngress( - iNamespace("testing"), - iAnnotation(annotationKubernetesAuthType, "forward"), - iAnnotation(annotationKubernetesAuthForwardURL, "https://auth.host"), - iAnnotation(annotationKubernetesAuthForwardTLSSecret, "secret"), - iAnnotation(annotationKubernetesAuthForwardTLSInsecure, "true"), - iRules( - iRule(iHost("foo"), - iPaths( - onePath(iPath("/bar"), iBackend("service1", intstr.FromInt(80))))), - ), - ), - } - - secrets := []*corev1.Secret{{ - ObjectMeta: metav1.ObjectMeta{ - Name: "secret", - UID: "1", - Namespace: "testing", - }, - Data: map[string][]byte{ - "tls.crt": []byte("-----BEGIN CERTIFICATE-----\n-----END CERTIFICATE-----"), - "tls.key": []byte("-----BEGIN PRIVATE KEY-----\n-----END PRIVATE KEY-----"), - }, - }} - - services := []*corev1.Service{ - buildService( - sName("service1"), - sNamespace("testing"), - sUID("1"), - sSpec( - clusterIP("10.0.0.1"), - sPorts(sPort(80, ""))), - ), - } - - endpoints := []*corev1.Endpoints{ - buildEndpoint( - eNamespace("testing"), - eName("service1"), - eUID("1"), - subset( - eAddresses(eAddress("10.10.0.1")), - ePorts(ePort(8080, ""))), - ), - } - - watchChan := make(chan interface{}) - client := clientMock{ - ingresses: ingresses, - services: services, - endpoints: endpoints, - secrets: secrets, - watchChan: watchChan, - } - provider := Provider{} - - actual, err := provider.loadIngresses(client) - require.NoError(t, err, "error loading ingresses") - - expected := buildConfiguration( - backends( - backend("foo/bar", - lbMethod("wrr"), - servers( - server("http://10.10.0.1:8080", weight(1))), - ), - ), - frontends( - frontend("foo/bar", - passHostHeader(), - auth( - forwardAuth("https://auth.host", - fwdAuthTLS( - "-----BEGIN CERTIFICATE-----\n-----END CERTIFICATE-----", - "-----BEGIN PRIVATE KEY-----\n-----END PRIVATE KEY-----", - true))), - routes( - route("/bar", "PathPrefix:/bar"), - route("foo", "Host:foo")), - ), - ), - ) - - assert.Equal(t, expected, actual) -} - -func TestLoadIngressesForwardAuthWithTLSSecretFailures(t *testing.T) { +func TestProvider_loadIngresses_ForwardAuthWithTLSSecretFailures(t *testing.T) { testCases := []struct { desc string secretName string @@ -2674,42 +1713,6 @@ func TestLoadIngressesForwardAuthWithTLSSecretFailures(t *testing.T) { }, } - ingresses := []*extensionsv1beta1.Ingress{ - buildIngress( - iNamespace("testing"), - iAnnotation(annotationKubernetesAuthType, "forward"), - iAnnotation(annotationKubernetesAuthForwardURL, "https://auth.host"), - iAnnotation(annotationKubernetesAuthForwardTLSSecret, "secret"), - iRules( - iRule(iHost("foo"), - iPaths( - onePath(iPath("/bar"), iBackend("service1", intstr.FromInt(80))))), - ), - ), - } - - services := []*corev1.Service{ - buildService( - sName("service1"), - sNamespace("testing"), - sUID("1"), - sSpec( - clusterIP("10.0.0.1"), - sPorts(sPort(80, ""))), - ), - } - - endpoints := []*corev1.Endpoints{ - buildEndpoint( - eNamespace("testing"), - eName("service1"), - eUID("1"), - subset( - eAddresses(eAddress("10.10.0.1")), - ePorts(ePort(8080, ""))), - ), - } - for _, test := range testCases { test := test t.Run(test.desc, func(t *testing.T) { @@ -2727,14 +1730,15 @@ func TestLoadIngressesForwardAuthWithTLSSecretFailures(t *testing.T) { }, }} - watchChan := make(chan interface{}) - client := clientMock{ - ingresses: ingresses, - services: services, - endpoints: endpoints, - secrets: secrets, - watchChan: watchChan, - } + client := newClientMock( + filepath.Join("fixtures", "loadIngressesForwardAuthWithTLSSecretFailures_services.yml"), + filepath.Join("fixtures", "loadIngressesForwardAuthWithTLSSecretFailures_ingresses.yml"), + filepath.Join("fixtures", "loadIngressesForwardAuthWithTLSSecretFailures_endpoints.yml"), + ) + + client.secrets = secrets + client.watchChan = make(chan interface{}) + provider := Provider{} actual, err := provider.loadIngresses(client) @@ -2752,128 +1756,9 @@ func TestLoadIngressesForwardAuthWithTLSSecretFailures(t *testing.T) { assert.Equal(t, expected, actual) }) - } } -func TestTLSSecretLoad(t *testing.T) { - ingresses := []*extensionsv1beta1.Ingress{ - buildIngress( - iNamespace("testing"), - iAnnotation(annotationKubernetesFrontendEntryPoints, "ep1,ep2"), - iRules( - iRule(iHost("example.com"), iPaths( - onePath(iBackend("example-com", intstr.FromInt(80))), - )), - iRule(iHost("example.org"), iPaths( - onePath(iBackend("example-org", intstr.FromInt(80))), - )), - ), - iTLSes( - iTLS("myTlsSecret"), - ), - ), - buildIngress( - iNamespace("testing"), - iAnnotation(annotationKubernetesFrontendEntryPoints, "ep3"), - iRules( - iRule(iHost("example.fail"), iPaths( - onePath(iBackend("example-fail", intstr.FromInt(80))), - )), - ), - iTLSes( - iTLS("myUndefinedSecret"), - ), - ), - } - services := []*corev1.Service{ - buildService( - sName("example-com"), - sNamespace("testing"), - sUID("1"), - sSpec( - clusterIP("10.0.0.1"), - sType("ClusterIP"), - sPorts(sPort(80, "http"))), - ), - buildService( - sName("example-org"), - sNamespace("testing"), - sUID("2"), - sSpec( - clusterIP("10.0.0.2"), - sType("ClusterIP"), - sPorts(sPort(80, "http"))), - ), - } - secrets := []*corev1.Secret{ - { - ObjectMeta: metav1.ObjectMeta{ - Name: "myTlsSecret", - UID: "1", - Namespace: "testing", - }, - Data: map[string][]byte{ - "tls.crt": []byte("-----BEGIN CERTIFICATE-----\n-----END CERTIFICATE-----"), - "tls.key": []byte("-----BEGIN PRIVATE KEY-----\n-----END PRIVATE KEY-----"), - }, - }, - } - var endpoints []*corev1.Endpoints - watchChan := make(chan interface{}) - client := clientMock{ - ingresses: ingresses, - services: services, - secrets: secrets, - endpoints: endpoints, - watchChan: watchChan, - } - provider := Provider{} - actual, err := provider.loadIngresses(client) - if err != nil { - t.Fatalf("error %+v", err) - } - - expected := buildConfiguration( - backends( - backend("ep1-ep2_example.com", - servers(), - lbMethod("wrr"), - ), - backend("ep1-ep2_example.org", - servers(), - lbMethod("wrr"), - ), - ), - frontends( - frontend("ep1-ep2_example.com", - entryPoints("ep1", "ep2"), - passHostHeader(), - routes( - route("example.com", "Host:example.com"), - ), - ), - frontend("ep1-ep2_example.org", - entryPoints("ep1", "ep2"), - passHostHeader(), - routes( - route("example.org", "Host:example.org"), - ), - ), - ), - tlsesSection( - tlsSection( - tlsEntryPoints("ep1", "ep2"), - certificate( - "-----BEGIN CERTIFICATE-----\n-----END CERTIFICATE-----", - "-----BEGIN PRIVATE KEY-----\n-----END PRIVATE KEY-----"), - ), - ), - ) - - assert.Equal(t, expected, actual) -} - func TestGetTLS(t *testing.T) { testIngressWithoutHostname := buildIngress( iNamespace("testing"), @@ -3080,99 +1965,7 @@ func TestGetTLS(t *testing.T) { } } -func TestMultiPortServices(t *testing.T) { - ingresses := []*extensionsv1beta1.Ingress{ - buildIngress( - iNamespace("testing"), - iRules( - iRule(iPaths( - onePath(iPath("/cheddar"), iBackend("service", intstr.FromString("cheddar"))), - onePath(iPath("/stilton"), iBackend("service", intstr.FromString("stilton"))), - )), - ), - ), - } - - services := []*corev1.Service{ - buildService( - sName("service"), - sNamespace("testing"), - sUID("1"), - sSpec( - clusterIP("10.0.0.1"), - sPorts(sPort(80, "cheddar")), - sPorts(sPort(81, "stilton")), - ), - ), - } - - endpoints := []*corev1.Endpoints{ - buildEndpoint( - eNamespace("testing"), - eName("service"), - eUID("1"), - subset( - eAddresses( - eAddress("10.10.0.1"), - eAddress("10.10.0.2"), - ), - ePorts(ePort(8080, "cheddar")), - ), - subset( - eAddresses( - eAddress("10.20.0.1"), - eAddress("10.20.0.2"), - ), - ePorts(ePort(8081, "stilton")), - ), - ), - } - - watchChan := make(chan interface{}) - client := clientMock{ - ingresses: ingresses, - services: services, - endpoints: endpoints, - watchChan: watchChan, - } - provider := Provider{} - - actual, err := provider.loadIngresses(client) - require.NoError(t, err, "error loading ingresses") - - expected := buildConfiguration( - backends( - backend("/cheddar", - lbMethod("wrr"), - servers( - server("http://10.10.0.1:8080", weight(1)), - server("http://10.10.0.2:8080", weight(1)), - ), - ), - backend("/stilton", - lbMethod("wrr"), - servers( - server("http://10.20.0.1:8081", weight(1)), - server("http://10.20.0.2:8081", weight(1)), - ), - ), - ), - frontends( - frontend("/cheddar", - passHostHeader(), - routes(route("/cheddar", "PathPrefix:/cheddar")), - ), - frontend("/stilton", - passHostHeader(), - routes(route("/stilton", "PathPrefix:/stilton")), - ), - ), - ) - - assert.Equal(t, expected, actual) -} - -func TestProviderUpdateIngressStatus(t *testing.T) { +func TestProvider_updateIngressStatus(t *testing.T) { testCases := []struct { desc string ingressEndpoint *IngressEndpoint @@ -3270,505 +2063,21 @@ func TestProviderUpdateIngressStatus(t *testing.T) { t.Run(test.desc, func(t *testing.T) { t.Parallel() + client := newClientMock(filepath.Join("fixtures", "providerUpdateIngressStatus_services.yml")) + client.apiServiceError = test.apiServiceError + client.apiIngressStatusError = test.apiIngressStatusError + p := &Provider{ IngressEndpoint: test.ingressEndpoint, } - services := []*corev1.Service{ - buildService( - sName("service-empty-status"), - sNamespace("testing"), - sLoadBalancerStatus(), - sUID("1"), - sSpec( - clusterIP("10.0.0.1"), - sPorts(sPort(80, ""))), - ), - buildService( - sName("service"), - sNamespace("testing"), - sLoadBalancerStatus(sLoadBalancerIngress("127.0.0.1", "")), - sUID("2"), - sSpec( - clusterIP("10.0.0.2"), - sPorts(sPort(80, ""))), - ), - } - - client := clientMock{ - services: services, - apiServiceError: test.apiServiceError, - apiIngressStatusError: test.apiIngressStatusError, - } - - i := &extensionsv1beta1.Ingress{} - - err := p.updateIngressStatus(i, client) + err := p.updateIngressStatus(&extensionsv1beta1.Ingress{}, client) if test.expectedError { - assert.Error(t, err) + require.Error(t, err) } else { - assert.NoError(t, err) + require.NoError(t, err) } }) } } - -func TestPercentageWeightServiceAnnotation(t *testing.T) { - ingresses := []*extensionsv1beta1.Ingress{ - buildIngress( - iAnnotation(annotationKubernetesServiceWeights, ` -service1: 10% -service3: 20% -`), - iNamespace("testing"), - iRules( - iRule( - iHost("host1"), - iPaths( - onePath(iPath("/foo"), iBackend("service1", intstr.FromString("8080"))), - onePath(iPath("/foo"), iBackend("service2", intstr.FromString("7070"))), - onePath(iPath("/foo"), iBackend("service3", intstr.FromString("9090"))), - onePath(iPath("/bar"), iBackend("service2", intstr.FromString("7070"))), - )), - ), - ), - } - services := []*corev1.Service{ - buildService( - sName("service1"), - sNamespace("testing"), - sUID("1"), - sSpec( - clusterIP("10.0.0.1"), - sPorts(sPort(8080, "")), - ), - ), - buildService( - sName("service2"), - sNamespace("testing"), - sUID("1"), - sSpec( - clusterIP("10.0.0.1"), - sPorts(sPort(7070, "")), - ), - ), - buildService( - sName("service3"), - sNamespace("testing"), - sUID("1"), - sSpec( - sType(corev1.ServiceTypeExternalName), - sExternalName("example.com"), - sPorts(sPort(9090, "")), - ), - ), - } - - endpoints := []*corev1.Endpoints{ - buildEndpoint( - eNamespace("testing"), - eName("service1"), - eUID("1"), - subset( - eAddresses( - eAddress("10.10.0.1"), - eAddress("10.10.0.2"), - ), - ePorts(ePort(8080, "")), - ), - ), - buildEndpoint( - eNamespace("testing"), - eName("service2"), - eUID("1"), - subset( - eAddresses( - eAddress("10.10.0.3"), - eAddress("10.10.0.4"), - ), - ePorts(ePort(7070, "")), - ), - ), - } - - watchChan := make(chan interface{}) - client := clientMock{ - ingresses: ingresses, - services: services, - endpoints: endpoints, - watchChan: watchChan, - } - provider := Provider{} - - actual, err := provider.loadIngresses(client) - require.NoError(t, err, "error loading ingresses") - - expected := buildConfiguration( - backends( - backend("host1/foo", - servers( - server("http://10.10.0.1:8080", weight(int(newPercentageValueFromFloat64(0.05)))), - server("http://10.10.0.2:8080", weight(int(newPercentageValueFromFloat64(0.05)))), - server("http://10.10.0.3:7070", weight(int(newPercentageValueFromFloat64(0.35)))), - server("http://10.10.0.4:7070", weight(int(newPercentageValueFromFloat64(0.35)))), - server("http://example.com:9090", weight(int(newPercentageValueFromFloat64(0.2)))), - ), - lbMethod("wrr"), - ), - backend("host1/bar", - servers( - server("http://10.10.0.3:7070", weight(int(newPercentageValueFromFloat64(0.5)))), - server("http://10.10.0.4:7070", weight(int(newPercentageValueFromFloat64(0.5)))), - ), - lbMethod("wrr"), - ), - ), - frontends( - frontend("host1/bar", - passHostHeader(), - routes( - route("/bar", "PathPrefix:/bar"), - route("host1", "Host:host1")), - ), - frontend("host1/foo", - passHostHeader(), - routes( - route("/foo", "PathPrefix:/foo"), - route("host1", "Host:host1")), - ), - ), - ) - - assert.Equal(t, expected, actual, "error loading percentage weight annotation") -} - -func TestProviderNewK8sInClusterClient(t *testing.T) { - p := Provider{} - os.Setenv("KUBERNETES_SERVICE_HOST", "localhost") - os.Setenv("KUBERNETES_SERVICE_PORT", "443") - defer os.Clearenv() - _, err := p.newK8sClient("") - assert.EqualError(t, err, "failed to create in-cluster configuration: open /var/run/secrets/kubernetes.io/serviceaccount/token: no such file or directory") -} - -func TestProviderNewK8sInClusterClientFailLabelSel(t *testing.T) { - p := Provider{} - os.Setenv("KUBERNETES_SERVICE_HOST", "localhost") - os.Setenv("KUBERNETES_SERVICE_PORT", "443") - defer os.Clearenv() - _, err := p.newK8sClient("%") - assert.EqualError(t, err, "invalid ingress label selector: \"%\"") -} - -func TestProviderNewK8sOutOfClusterClient(t *testing.T) { - p := Provider{} - p.Endpoint = "localhost" - _, err := p.newK8sClient("") - assert.NoError(t, err) -} - -func TestAddGlobalBackendDuplicateFailures(t *testing.T) { - testCases := []struct { - desc string - previousConfig *types.Configuration - err string - }{ - { - desc: "Duplicate Frontend", - previousConfig: buildConfiguration( - frontends( - frontend("global-default-backend", - frontendName("global-default-frontend"), - passHostHeader(), - routes( - route("/", "PathPrefix:/"), - ), - ), - ), - ), - err: "duplicate frontend: global-default-frontend", - }, - { - desc: "Duplicate Backend", - previousConfig: buildConfiguration( - backends( - backend("global-default-backend", - lbMethod("wrr"), - servers( - server("http://10.10.0.1:8080", weight(1)), - ), - ), - )), - err: "duplicate backend: global-default-backend", - }, - } - - ingress := buildIngress( - iNamespace("testing"), - iSpecBackends(iSpecBackend(iIngressBackend("service1", intstr.FromInt(80)))), - ) - - for _, test := range testCases { - test := test - t.Run(test.desc, func(t *testing.T) { - t.Parallel() - - watchChan := make(chan interface{}) - client := clientMock{ - watchChan: watchChan, - } - provider := Provider{} - - err := provider.addGlobalBackend(client, ingress, test.previousConfig) - assert.EqualError(t, err, test.err) - }) - } -} - -func TestAddGlobalBackendServiceMissing(t *testing.T) { - ingresses := buildIngress( - iNamespace("testing"), - iSpecBackends(iSpecBackend(iIngressBackend("service1", intstr.FromInt(80)))), - ) - - config := buildConfiguration( - frontends(), - backends(), - ) - watchChan := make(chan interface{}) - client := clientMock{ - watchChan: watchChan, - } - provider := Provider{} - - err := provider.addGlobalBackend(client, ingresses, config) - assert.Error(t, err) -} - -func TestAddGlobalBackendServiceAPIError(t *testing.T) { - ingresses := buildIngress( - iNamespace("testing"), - iSpecBackends(iSpecBackend(iIngressBackend("service1", intstr.FromInt(80)))), - ) - - config := buildConfiguration( - frontends(), - backends(), - ) - - apiErr := errors.New("failed kube api call") - - watchChan := make(chan interface{}) - client := clientMock{ - apiServiceError: apiErr, - watchChan: watchChan, - } - provider := Provider{} - err := provider.addGlobalBackend(client, ingresses, config) - assert.Error(t, err) -} - -func TestAddGlobalBackendEndpointMissing(t *testing.T) { - ingresses := buildIngress( - iNamespace("testing"), - iSpecBackends(iSpecBackend(iIngressBackend("service", intstr.FromInt(80)))), - ) - - services := []*corev1.Service{ - buildService( - sName("service"), - sNamespace("testing"), - sUID("1"), - sSpec( - clusterIP("10.0.0.1"), - sPorts(sPort(80, "")), - ), - ), - } - - config := buildConfiguration( - frontends(), - backends(), - ) - watchChan := make(chan interface{}) - client := clientMock{ - services: services, - watchChan: watchChan, - } - provider := Provider{} - - err := provider.addGlobalBackend(client, ingresses, config) - assert.Error(t, err) -} - -func TestAddGlobalBackendEndpointAPIError(t *testing.T) { - ingresses := buildIngress( - iNamespace("testing"), - iSpecBackends(iSpecBackend(iIngressBackend("service", intstr.FromInt(80)))), - ) - - config := buildConfiguration( - frontends(), - backends(), - ) - - services := []*corev1.Service{ - buildService( - sName("service"), - sNamespace("testing"), - sUID("1"), - sSpec( - clusterIP("10.0.0.1"), - sPorts(sPort(80, "")), - ), - ), - } - - apiErr := errors.New("failed kube api call") - - watchChan := make(chan interface{}) - client := clientMock{ - apiEndpointsError: apiErr, - services: services, - watchChan: watchChan, - } - provider := Provider{} - err := provider.addGlobalBackend(client, ingresses, config) - assert.Error(t, err) -} - -func TestTemplateBreakingIngresssValues(t *testing.T) { - ingresses := []*extensionsv1beta1.Ingress{ - buildIngress( - iNamespace("testing"), - iAnnotation(annotationKubernetesIngressClass, "testing-\"foo\""), - iRules( - iRule( - iHost("foo"), - iPaths(onePath(iPath("/bar"), iBackend("service1", intstr.FromInt(80))))), - ), - ), - buildIngress( - iNamespace("testing"), - iRules( - iRule( - iHost("testing-\"foo\""), - iPaths(onePath(iPath("/bar"), iBackend("service1", intstr.FromInt(80))))), - ), - ), - buildIngress( - iNamespace("testing"), - iRules( - iRule( - iHost("foo"), - iPaths(onePath(iPath("/testing-\"foo\""), iBackend("service1", intstr.FromInt(80))))), - ), - ), - } - - client := clientMock{ - ingresses: ingresses, - } - provider := Provider{} - - actual, err := provider.loadIngresses(client) - require.NoError(t, err, "error loading ingresses") - - expected := buildConfiguration( - backends(), - frontends(), - ) - - assert.Equal(t, expected, actual) -} - -func TestDivergingIngressDefinitions(t *testing.T) { - ingresses := []*extensionsv1beta1.Ingress{ - buildIngress( - iNamespace("testing"), - iRules( - iRule( - iHost("host-a"), - iPaths( - onePath(iBackend("service1", intstr.FromString("80"))), - )), - ), - ), - buildIngress( - iNamespace("testing"), - iRules( - iRule( - iHost("host-a"), - iPaths( - onePath(iBackend("missing", intstr.FromString("80"))), - )), - ), - ), - } - - services := []*corev1.Service{ - buildService( - sName("service1"), - sNamespace("testing"), - sUID("1"), - sSpec( - clusterIP("10.0.0.1"), - sPorts(sPort(80, "http")), - ), - ), - } - - endpoints := []*corev1.Endpoints{ - buildEndpoint( - eNamespace("testing"), - eName("service1"), - eUID("1"), - subset( - eAddresses( - eAddress("10.10.0.1"), - ), - ePorts(ePort(80, "http")), - ), - subset( - eAddresses( - eAddress("10.10.0.2"), - ), - ePorts(ePort(80, "http")), - ), - ), - } - - watchChan := make(chan interface{}) - client := clientMock{ - ingresses: ingresses, - services: services, - endpoints: endpoints, - watchChan: watchChan, - } - provider := Provider{} - - actual, err := provider.loadIngresses(client) - require.NoError(t, err, "error loading ingresses") - - expected := buildConfiguration( - backends( - backend("host-a", - servers( - server("http://10.10.0.1:80", weight(1)), - server("http://10.10.0.2:80", weight(1)), - ), - lbMethod("wrr"), - ), - ), - frontends( - frontend("host-a", - passHostHeader(), - routes( - route("host-a", "Host:host-a")), - ), - ), - ) - - assert.Equal(t, expected, actual, "error merging multiple backends") -} diff --git a/provider/kubernetes/parser_test.go b/provider/kubernetes/parser_test.go new file mode 100644 index 000000000..bcf98718e --- /dev/null +++ b/provider/kubernetes/parser_test.go @@ -0,0 +1,79 @@ +package kubernetes + +import ( + "fmt" + "io" + "os" + "path/filepath" + "regexp" + "strings" + + "github.com/containous/traefik/log" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/client-go/kubernetes/scheme" +) + +// MustEncodeYaml Encode object to YAML. +// +// ex: +// MustEncodeYaml(ingresses[0], "extensions/v1beta1", "ingress.yml") +// MustEncodeYaml(services[0], "v1", "service.yml") +// MustEncodeYaml(endpoints[0], "v1", "endpoint.yml") +func MustEncodeYaml(object runtime.Object, groupName string, w io.Writer) { + info, ok := runtime.SerializerInfoForMediaType(scheme.Codecs.SupportedMediaTypes(), "application/yaml") + if !ok { + panic("oops") + } + + gv, err := schema.ParseGroupVersion(groupName) + if err != nil { + panic(err) + } + + err = scheme.Codecs.EncoderForVersion(info.Serializer, gv).Encode(object, w) + if err != nil { + panic(err) + } +} + +// MustDecodeYaml Decode a YAML to objects. +func MustDecodeYaml(content []byte) []runtime.Object { + acceptedK8sTypes := regexp.MustCompile(`(Deployment|Endpoints|Service|Ingress|Secret)`) + + files := strings.Split(string(content), "---") + retVal := make([]runtime.Object, 0, len(files)) + for _, file := range files { + if file == "\n" || file == "" { + continue + } + + decode := scheme.Codecs.UniversalDeserializer().Decode + obj, groupVersionKind, err := decode([]byte(file), nil, nil) + if err != nil { + panic(fmt.Sprintf("Error while decoding YAML object. Err was: %s", err)) + } + + if !acceptedK8sTypes.MatchString(groupVersionKind.Kind) { + log.Debugf("The custom-roles configMap contained K8s object types which are not supported! Skipping object with type: %s", groupVersionKind.Kind) + } else { + retVal = append(retVal, obj) + } + } + return retVal +} + +func mustCreateFile(filename string) *os.File { + fp := filepath.Join("fixtures", filename) + err := os.MkdirAll(filepath.Dir(fp), 0777) + if err != nil { + panic(err) + } + + file, err := os.Create(fp) + if err != nil { + panic(err) + } + + return file +} diff --git a/provider/kubernetes/weight_allocator_test.go b/provider/kubernetes/weight_allocator_test.go index 2af98904e..db6e65aeb 100644 --- a/provider/kubernetes/weight_allocator_test.go +++ b/provider/kubernetes/weight_allocator_test.go @@ -2,14 +2,13 @@ package kubernetes import ( "fmt" + "io/ioutil" + "path/filepath" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - corev1 "k8s.io/api/core/v1" extensionsv1beta1 "k8s.io/api/extensions/v1beta1" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/util/intstr" ) func TestString(t *testing.T) { @@ -147,13 +146,9 @@ service1: 1000% t.Run(test.desc, func(t *testing.T) { t.Parallel() - ingress := &extensionsv1beta1.Ingress{ - ObjectMeta: v1.ObjectMeta{ - Annotations: map[string]string{ - annotationKubernetesServiceWeights: test.annotationValue, - }, - }, - } + ingress := buildIngress( + iAnnotation(annotationKubernetesServiceWeights, test.annotationValue), + ) weights, err := getServicesPercentageWeights(ingress) @@ -168,93 +163,20 @@ service1: 1000% } func TestComputeServiceWeights(t *testing.T) { - client := clientMock{ - services: []*corev1.Service{ - buildService( - sName("service1"), - sNamespace("testing"), - ), - buildService( - sName("service2"), - sNamespace("testing"), - ), - buildService( - sName("service3"), - sNamespace("testing"), - ), - buildService( - sName("service4"), - sNamespace("testing"), - ), - }, - endpoints: []*corev1.Endpoints{ - buildEndpoint( - eNamespace("testing"), - eName("service1"), - eUID("1"), - subset( - eAddresses(eAddress("10.10.0.1")), - ePorts(ePort(8080, ""))), - subset( - eAddresses(eAddress("10.21.0.2")), - ePorts(ePort(8080, ""))), - ), - buildEndpoint( - eNamespace("testing"), - eName("service2"), - eUID("2"), - subset( - eAddresses(eAddress("10.10.0.3")), - ePorts(ePort(8080, ""))), - ), - buildEndpoint( - eNamespace("testing"), - eName("service3"), - eUID("3"), - subset( - eAddresses(eAddress("10.10.0.4")), - ePorts(ePort(8080, ""))), - subset( - eAddresses(eAddress("10.21.0.5")), - ePorts(ePort(8080, ""))), - subset( - eAddresses(eAddress("10.21.0.6")), - ePorts(ePort(8080, ""))), - subset( - eAddresses(eAddress("10.21.0.7")), - ePorts(ePort(8080, ""))), - ), - buildEndpoint( - eNamespace("testing"), - eName("service4"), - eUID("4"), - subset( - eAddresses(eAddress("10.10.0.7")), - ePorts(ePort(8080, ""))), - ), - }, - } + client := newClientMock( + filepath.Join("fixtures", "computeServiceWeights_endpoints.yml"), + filepath.Join("fixtures", "computeServiceWeights_services.yml"), + ) testCases := []struct { desc string - ingress *extensionsv1beta1.Ingress + ingress string expectError bool expectedWeights map[ingressService]percentageValue }{ { - desc: "1 path 2 service", - ingress: buildIngress( - iNamespace("testing"), - iAnnotation(annotationKubernetesServiceWeights, ` -service1: 10% -`), - iRules( - iRule(iHost("foo.test"), iPaths( - onePath(iPath("/foo"), iBackend("service1", intstr.FromInt(8080))), - onePath(iPath("/foo"), iBackend("service2", intstr.FromInt(8080))), - )), - ), - ), + desc: "1 path 2 service", + ingress: filepath.Join("fixtures", "computeServiceWeights", "1_path_2_service_ingresses.yml"), expectError: false, expectedWeights: map[ingressService]percentageValue{ { @@ -270,21 +192,8 @@ service1: 10% }, }, { - desc: "2 path 2 service", - ingress: buildIngress( - iNamespace("testing"), - iAnnotation(annotationKubernetesServiceWeights, ` -service1: 60% -`), - iRules( - iRule(iHost("foo.test"), iPaths( - onePath(iPath("/foo"), iBackend("service1", intstr.FromInt(8080))), - onePath(iPath("/foo"), iBackend("service2", intstr.FromInt(8080))), - onePath(iPath("/bar"), iBackend("service1", intstr.FromInt(8080))), - onePath(iPath("/bar"), iBackend("service3", intstr.FromInt(8080))), - )), - ), - ), + desc: "2 path 2 service", + ingress: filepath.Join("fixtures", "computeServiceWeights", "2_path_2_service_ingresses.yml"), expectError: false, expectedWeights: map[ingressService]percentageValue{ { @@ -310,22 +219,8 @@ service1: 60% }, }, { - desc: "2 path 3 service", - ingress: buildIngress( - iNamespace("testing"), - iAnnotation(annotationKubernetesServiceWeights, ` -service1: 20% -service3: 20% -`), - iRules( - iRule(iHost("foo.test"), iPaths( - onePath(iPath("/foo"), iBackend("service1", intstr.FromInt(8080))), - onePath(iPath("/foo"), iBackend("service2", intstr.FromInt(8080))), - onePath(iPath("/bar"), iBackend("service2", intstr.FromInt(8080))), - onePath(iPath("/bar"), iBackend("service3", intstr.FromInt(8080))), - )), - ), - ), + desc: "2 path 3 service", + ingress: filepath.Join("fixtures", "computeServiceWeights", "2_path_3_service_ingresses.yml"), expectError: false, expectedWeights: map[ingressService]percentageValue{ { @@ -351,23 +246,8 @@ service3: 20% }, }, { - desc: "1 path 4 service", - ingress: buildIngress( - iNamespace("testing"), - iAnnotation(annotationKubernetesServiceWeights, ` -service1: 20% -service2: 40% -service3: 40% -`), - iRules( - iRule(iHost("foo.test"), iPaths( - onePath(iPath("/foo"), iBackend("service1", intstr.FromInt(8080))), - onePath(iPath("/foo"), iBackend("service2", intstr.FromInt(8080))), - onePath(iPath("/foo"), iBackend("service3", intstr.FromInt(8080))), - onePath(iPath("/foo"), iBackend("service4", intstr.FromInt(8080))), - )), - ), - ), + desc: "1 path 4 service", + ingress: filepath.Join("fixtures", "computeServiceWeights", "1_path_4_service_ingresses.yml"), expectError: false, expectedWeights: map[ingressService]percentageValue{ { @@ -393,35 +273,13 @@ service3: 40% }, }, { - desc: "2 path no service", - ingress: buildIngress( - iNamespace("testing"), - iAnnotation(annotationKubernetesServiceWeights, ` -service1: 20% -service2: 40% -service3: 40% -`), - iRules( - iRule(iHost("foo.test"), iPaths( - onePath(iPath("/foo"), iBackend("noservice", intstr.FromInt(8080))), - onePath(iPath("/bar"), iBackend("noservice", intstr.FromInt(8080))), - )), - ), - ), + desc: "2 path no service", + ingress: filepath.Join("fixtures", "computeServiceWeights", "2_path_no_service_ingresses.yml"), expectError: true, }, { - desc: "2 path without weight", - ingress: buildIngress( - iNamespace("testing"), - iAnnotation(annotationKubernetesServiceWeights, ``), - iRules( - iRule(iHost("foo.test"), iPaths( - onePath(iPath("/foo"), iBackend("service1", intstr.FromInt(8080))), - onePath(iPath("/bar"), iBackend("service2", intstr.FromInt(8080))), - )), - ), - ), + desc: "2 path without weight", + ingress: filepath.Join("fixtures", "computeServiceWeights", "2_path_without_weight_ingresses.yml"), expectError: false, expectedWeights: map[ingressService]percentageValue{ { @@ -437,20 +295,8 @@ service3: 40% }, }, { - desc: "2 path overflow", - ingress: buildIngress( - iNamespace("testing"), - iAnnotation(annotationKubernetesServiceWeights, ` -service1: 70% -service2: 80% -`), - iRules( - iRule(iHost("foo.test"), iPaths( - onePath(iPath("/foo"), iBackend("service1", intstr.FromInt(8080))), - onePath(iPath("/foo"), iBackend("service2", intstr.FromInt(8080))), - )), - ), - ), + desc: "2 path overflow", + ingress: filepath.Join("fixtures", "computeServiceWeights", "2_path_overflow_ingresses.yml"), expectError: true, }, } @@ -460,7 +306,20 @@ service2: 80% t.Run(test.desc, func(t *testing.T) { t.Parallel() - weightAllocator, err := newFractionalWeightAllocator(test.ingress, client) + yamlContent, err := ioutil.ReadFile(test.ingress) + require.NoError(t, err) + k8sObjects := MustDecodeYaml(yamlContent) + require.Len(t, k8sObjects, 1) + + var ingress *extensionsv1beta1.Ingress + switch o := k8sObjects[0].(type) { + case *extensionsv1beta1.Ingress: + ingress = o + default: + require.Fail(t, fmt.Sprintf("Unknown runtime object %+v %T", o, o)) + } + + weightAllocator, err := newFractionalWeightAllocator(ingress, client) if test.expectError { require.Error(t, err) } else {