Add tests for alias annotation

This commit is contained in:
Manuel de Brito Fontes 2017-11-09 23:00:38 -03:00
parent d4fd127a1f
commit 4c1c707e9c
10 changed files with 479 additions and 57 deletions

View file

@ -1016,8 +1016,7 @@ func (n *NGINXController) createServers(data []*extensions.Ingress,
}
if !found {
glog.Warningf("ingress %v/%v for host %v contains a TLS section but none of the host match",
ing.Namespace, ing.Name, host)
// does not contains a TLS section but none of the host match
continue
}

View file

@ -0,0 +1,150 @@
/*
Copyright 2017 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package annotations
import (
"fmt"
"net/http"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"github.com/parnurzeal/gorequest"
v1beta1 "k8s.io/api/extensions/v1beta1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/intstr"
"k8s.io/ingress-nginx/test/e2e/framework"
)
var _ = framework.IngressNginxDescribe("Annotations - Alias", func() {
f := framework.NewDefaultFramework("alias")
BeforeEach(func() {
err := f.NewEchoDeployment()
Expect(err).NotTo(HaveOccurred())
})
AfterEach(func() {
})
It("should return status code 200 for host 'foo' and 404 for 'bar'", func() {
host := "foo"
ing, err := f.EnsureIngress(&v1beta1.Ingress{
ObjectMeta: metav1.ObjectMeta{
Name: host,
Namespace: f.Namespace.Name,
},
Spec: v1beta1.IngressSpec{
Rules: []v1beta1.IngressRule{
{
Host: host,
IngressRuleValue: v1beta1.IngressRuleValue{
HTTP: &v1beta1.HTTPIngressRuleValue{
Paths: []v1beta1.HTTPIngressPath{
{
Path: "/",
Backend: v1beta1.IngressBackend{
ServiceName: "http-svc",
ServicePort: intstr.FromInt(80),
},
},
},
},
},
},
},
},
})
Expect(err).NotTo(HaveOccurred())
Expect(ing).NotTo(BeNil())
err = f.WaitForNginxServer(host)
Expect(err).NotTo(HaveOccurred())
resp, body, errs := gorequest.New().
Get(f.NginxHTTPURL).
Set("Host", host).
End()
Expect(len(errs)).Should(BeNumerically("==", 0))
Expect(resp.StatusCode).Should(Equal(http.StatusOK))
Expect(body).Should(ContainSubstring(fmt.Sprintf("host=%v", host)))
resp, body, errs = gorequest.New().
Get(f.NginxHTTPURL).
Set("Host", "bar").
End()
Expect(len(errs)).Should(BeNumerically("==", 0))
Expect(resp.StatusCode).Should(Equal(http.StatusNotFound))
Expect(body).Should(ContainSubstring("default backend - 404"))
})
It("should return status code 200 for host 'foo' and 'bar'", func() {
host := "bar"
ing, err := f.EnsureIngress(&v1beta1.Ingress{
ObjectMeta: metav1.ObjectMeta{
Name: host,
Namespace: f.Namespace.Name,
Annotations: map[string]string{
"nginx.ingress.kubernetes.io/server-alias": host,
},
},
Spec: v1beta1.IngressSpec{
Rules: []v1beta1.IngressRule{
{
Host: "foo",
IngressRuleValue: v1beta1.IngressRuleValue{
HTTP: &v1beta1.HTTPIngressRuleValue{
Paths: []v1beta1.HTTPIngressPath{
{
Path: "/",
Backend: v1beta1.IngressBackend{
ServiceName: "http-svc",
ServicePort: intstr.FromInt(80),
},
},
},
},
},
},
},
},
})
Expect(err).NotTo(HaveOccurred())
Expect(ing).NotTo(BeNil())
err = f.WaitForNginxServer(host)
Expect(err).NotTo(HaveOccurred())
hosts := []string{"foo", "bar"}
for _, host := range hosts {
resp, body, errs := gorequest.New().
Get(f.NginxHTTPURL).
Set("Host", host).
End()
Expect(len(errs)).Should(BeNumerically("==", 0))
Expect(resp.StatusCode).Should(Equal(http.StatusOK))
Expect(body).Should(ContainSubstring(fmt.Sprintf("host=%v", host)))
}
})
})

View file

@ -0,0 +1,15 @@
package annotations
// Tests:
// No auth
// Basic
// 401
// Realm name
// Auth ok
// Auth error
// Digest
// 401
// Realm name
// Auth ok
// Auth error
// Check return 403 if there's an error retrieving the secret

View file

@ -39,14 +39,6 @@ var _ = framework.IngressNginxDescribe("Default backend", func() {
})
It("should return 404 sending requests when only a default backend is running", func() {
httpURL, err := f.GetNginxURL(framework.HTTP)
Expect(err).NotTo(HaveOccurred())
httpsURL, err := f.GetNginxURL(framework.HTTPS)
Expect(err).NotTo(HaveOccurred())
request := gorequest.New()
testCases := []struct {
Name string
Host string
@ -55,38 +47,39 @@ var _ = framework.IngressNginxDescribe("Default backend", func() {
Path string
Status int
}{
{"basic HTTP GET request without host to path / should return 404", "", framework.HTTP, "GET", "/", 404},
{"basic HTTP GET request without host to path /demo should return 404", "", framework.HTTP, "GET", "/demo", 404},
{"basic HTTPS GET request without host to path / should return 404", "", framework.HTTPS, "GET", "/", 404},
{"basic HTTPS GET request without host to path /demo should return 404", "", framework.HTTPS, "GET", "/demo", 404},
{"basic HTTP GET request without host to path / should return 404", "", framework.HTTP, "GET", "/", http.StatusNotFound},
{"basic HTTP GET request without host to path /demo should return 404", "", framework.HTTP, "GET", "/demo", http.StatusNotFound},
{"basic HTTPS GET request without host to path / should return 404", "", framework.HTTPS, "GET", "/", http.StatusNotFound},
{"basic HTTPS GET request without host to path /demo should return 404", "", framework.HTTPS, "GET", "/demo", http.StatusNotFound},
{"basic HTTP POST request without host to path / should return 404", "", framework.HTTP, "POST", "/", 404},
{"basic HTTP POST request without host to path /demo should return 404", "", framework.HTTP, "POST", "/demo", 404},
{"basic HTTPS POST request without host to path / should return 404", "", framework.HTTPS, "POST", "/", 404},
{"basic HTTPS POST request without host to path /demo should return 404", "", framework.HTTPS, "POST", "/demo", 404},
{"basic HTTP POST request without host to path / should return 404", "", framework.HTTP, "POST", "/", http.StatusNotFound},
{"basic HTTP POST request without host to path /demo should return 404", "", framework.HTTP, "POST", "/demo", http.StatusNotFound},
{"basic HTTPS POST request without host to path / should return 404", "", framework.HTTPS, "POST", "/", http.StatusNotFound},
{"basic HTTPS POST request without host to path /demo should return 404", "", framework.HTTPS, "POST", "/demo", http.StatusNotFound},
{"basic HTTP GET request to host foo.bar.com and path / should return 404", " foo.bar.com", framework.HTTP, "GET", "/", 404},
{"basic HTTP GET request to host foo.bar.com and path /demo should return 404", " foo.bar.com", framework.HTTP, "GET", "/demo", 404},
{"basic HTTPS GET request to host foo.bar.com and path / should return 404", " foo.bar.com", framework.HTTPS, "GET", "/", 404},
{"basic HTTPS GET request to host foo.bar.com and path /demo should return 404", " foo.bar.com", framework.HTTPS, "GET", "/demo", 404},
{"basic HTTP GET request to host foo.bar.com and path / should return 404", " foo.bar.com", framework.HTTP, "GET", "/", http.StatusNotFound},
{"basic HTTP GET request to host foo.bar.com and path /demo should return 404", " foo.bar.com", framework.HTTP, "GET", "/demo", http.StatusNotFound},
{"basic HTTPS GET request to host foo.bar.com and path / should return 404", " foo.bar.com", framework.HTTPS, "GET", "/", http.StatusNotFound},
{"basic HTTPS GET request to host foo.bar.com and path /demo should return 404", " foo.bar.com", framework.HTTPS, "GET", "/demo", http.StatusNotFound},
{"basic HTTP POST request to host foo.bar.com and path / should return 404", " foo.bar.com", framework.HTTP, "POST", "/", 404},
{"basic HTTP POST request to host foo.bar.com and path /demo should return 404", " foo.bar.com", framework.HTTP, "POST", "/demo", 404},
{"basic HTTPS POST request to host foo.bar.com and path / should return 404", " foo.bar.com", framework.HTTPS, "POST", "/", 404},
{"basic HTTPS POST request to host foo.bar.com and path /demo should return 404", " foo.bar.com", framework.HTTPS, "POST", "/demo", 404},
{"basic HTTP POST request to host foo.bar.com and path / should return 404", " foo.bar.com", framework.HTTP, "POST", "/", http.StatusNotFound},
{"basic HTTP POST request to host foo.bar.com and path /demo should return 404", " foo.bar.com", framework.HTTP, "POST", "/demo", http.StatusNotFound},
{"basic HTTPS POST request to host foo.bar.com and path / should return 404", " foo.bar.com", framework.HTTPS, "POST", "/", http.StatusNotFound},
{"basic HTTPS POST request to host foo.bar.com and path /demo should return 404", " foo.bar.com", framework.HTTPS, "POST", "/demo", http.StatusNotFound},
}
for _, test := range testCases {
By(test.Name)
var errs []error
request := gorequest.New()
var cm *gorequest.SuperAgent
switch test.Scheme {
case framework.HTTP:
cm = request.CustomMethod(test.Method, httpURL)
cm = request.CustomMethod(test.Method, f.NginxHTTPURL)
break
case framework.HTTPS:
cm = request.CustomMethod(test.Method, httpsURL)
cm = request.CustomMethod(test.Method, f.NginxHTTPSURL)
// the default backend uses a self generated certificate
cm.Transport = &http.Transport{
TLSClientConfig: &tls.Config{

View file

@ -18,7 +18,6 @@ package defaultbackend
import (
"crypto/tls"
"net/http"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
@ -37,36 +36,29 @@ var _ = framework.IngressNginxDescribe("Default backend - SSL", func() {
})
It("should return a self generated SSL certificate", func() {
httpsURL, err := f.GetNginxURL(framework.HTTPS)
Expect(err).NotTo(HaveOccurred())
request := gorequest.New()
By("checking SSL Certificate using the NGINX IP address")
cm := request.Post(httpsURL)
// the default backend uses a self generated certificate
cm.Transport = &http.Transport{
TLSClientConfig: &tls.Config{
resp, _, errs := gorequest.New().
Post(f.NginxHTTPSURL).
TLSClientConfig(&tls.Config{
// the default backend uses a self generated certificate
InsecureSkipVerify: true,
},
}
resp, _, errs := cm.End()
}).End()
Expect(len(errs)).Should(BeNumerically("==", 0))
Expect(len(resp.TLS.PeerCertificates)).Should(BeNumerically("==", 1))
for _, pc := range resp.TLS.PeerCertificates {
Expect(pc.Issuer.CommonName).Should(Equal("Kubernetes Ingress Controller Fake Certificate"))
}
By("checking SSL Certificate using the NGINX catch all server")
cm = request.Post(httpsURL)
// the default backend uses a self generated certificate
cm.Transport = &http.Transport{
TLSClientConfig: &tls.Config{
resp, _, errs = gorequest.New().
Post(f.NginxHTTPSURL).
TLSClientConfig(&tls.Config{
// the default backend uses a self generated certificate
InsecureSkipVerify: true,
},
}
cm.Set("Host", "foo.bar.com")
resp, _, errs = cm.End()
}).
Set("Host", "foo.bar.com").End()
Expect(len(errs)).Should(BeNumerically("==", 0))
Expect(len(resp.TLS.PeerCertificates)).Should(BeNumerically("==", 1))
for _, pc := range resp.TLS.PeerCertificates {

View file

@ -26,6 +26,7 @@ import (
"k8s.io/apiserver/pkg/util/logs"
_ "k8s.io/client-go/plugin/pkg/client/auth"
_ "k8s.io/ingress-nginx/test/e2e/annotations"
_ "k8s.io/ingress-nginx/test/e2e/defaultbackend"
"k8s.io/ingress-nginx/test/e2e/framework"
)

118
test/e2e/framework/echo.go Normal file
View file

@ -0,0 +1,118 @@
/*
Copyright 2017 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package framework
import (
"fmt"
"time"
"github.com/pkg/errors"
corev1 "k8s.io/api/core/v1"
extensions "k8s.io/api/extensions/v1beta1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/fields"
"k8s.io/apimachinery/pkg/util/intstr"
)
// NewEchoDeployment creates a new deployment of the echoserver image in a particular namespace
func (f *Framework) NewEchoDeployment() error {
deployment := &extensions.Deployment{
ObjectMeta: metav1.ObjectMeta{
Name: "http-svc",
Namespace: f.Namespace.Name,
DeletionGracePeriodSeconds: NewInt64(5),
},
Spec: extensions.DeploymentSpec{
Replicas: NewInt32(1),
Selector: &metav1.LabelSelector{
MatchLabels: map[string]string{
"app": "http-svc",
},
},
Template: corev1.PodTemplateSpec{
ObjectMeta: metav1.ObjectMeta{
Labels: map[string]string{
"app": "http-svc",
},
},
Spec: corev1.PodSpec{
Containers: []corev1.Container{
{
Name: "http-svc",
Image: "gcr.io/google_containers/echoserver:1.8",
Env: []corev1.EnvVar{},
Ports: []corev1.ContainerPort{
{
Name: "http",
ContainerPort: 8080,
},
},
},
},
},
},
},
}
d, err := f.EnsureDeployment(deployment)
if err != nil {
return err
}
if d == nil {
return fmt.Errorf("unexpected error creating deployement for echoserver")
}
err = f.WaitForPodsReady(10*time.Second, 1, metav1.ListOptions{
LabelSelector: fields.SelectorFromSet(fields.Set(d.Spec.Template.ObjectMeta.Labels)).String(),
})
if err != nil {
return errors.Wrap(err, "failed to wait for to become ready")
}
service := &corev1.Service{
ObjectMeta: metav1.ObjectMeta{
Name: "http-svc",
Namespace: f.Namespace.Name,
},
Spec: corev1.ServiceSpec{
Ports: []corev1.ServicePort{
{
Name: "http",
Port: 80,
TargetPort: intstr.FromInt(8080),
Protocol: "TCP",
},
},
Selector: map[string]string{
"app": "http-svc",
},
},
}
s, err := f.EnsureService(service)
if err != nil {
return err
}
if s == nil {
return fmt.Errorf("unexpected error creating service for echoserver deployment")
}
return nil
}

View file

@ -17,10 +17,11 @@ import (
"fmt"
"os/exec"
"strings"
"time"
"k8s.io/api/core/v1"
apiextcs "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"
. "github.com/onsi/ginkgo"
@ -31,6 +32,11 @@ const (
podName = "test-ingress-controller"
)
const (
MaxRetry = 200
NoRetry = 1
)
type RequestScheme string
// These are valid test request schemes.
@ -54,9 +60,12 @@ type Framework struct {
// we install a Cleanup action before each test and clear it after. If we
// should abort, the AfterSuite hook should run all Cleanup actions.
cleanupHandle CleanupActionHandle
NginxHTTPURL string
NginxHTTPSURL string
}
// NewFramework makes a new framework and sets up a BeforeEach/AfterEach for
// NewDefaultFramework makes a new framework and sets up a BeforeEach/AfterEach for
// you (you can write additional before/after each functions).
func NewDefaultFramework(baseName string) *Framework {
f := &Framework{
@ -83,6 +92,14 @@ func (f *Framework) BeforeEach() {
By("Building a namespace api object")
f.Namespace, err = CreateKubeNamespace(f.BaseName, f.KubeClientSet)
Expect(err).NotTo(HaveOccurred())
By("Building NGINX HTTP URL")
f.NginxHTTPURL, err = f.GetNginxURL(HTTP)
Expect(err).NotTo(HaveOccurred())
By("Building NGINX HTTPS URL")
f.NginxHTTPSURL, err = f.GetNginxURL(HTTPS)
Expect(err).NotTo(HaveOccurred())
}
// AfterEach deletes the namespace, after reading its events.
@ -94,7 +111,7 @@ func (f *Framework) AfterEach() {
Expect(err).NotTo(HaveOccurred())
By("Waiting for test namespace to no longer exist")
err = WaitForKubeNamespaceNotExist(f.KubeClientSet, f.Namespace.Name)
err = WaitForNoPodsInNamespace(f.KubeClientSet, f.Namespace.Name)
Expect(err).NotTo(HaveOccurred())
}
@ -115,7 +132,7 @@ func (f *Framework) GetNginxIP() (string, error) {
// GetNginxPort returns the number of TCP port where NGINX is running
func (f *Framework) GetNginxPort(name string) (int, error) {
s, err := f.KubeClientSet.CoreV1().Services("ingress-nginx").Get("ingress-nginx", meta_v1.GetOptions{})
s, err := f.KubeClientSet.CoreV1().Services("ingress-nginx").Get("ingress-nginx", metav1.GetOptions{})
if err != nil {
return -1, err
}
@ -143,3 +160,8 @@ func (f *Framework) GetNginxURL(scheme RequestScheme) (string, error) {
return fmt.Sprintf("%v://%v:%v", scheme, ip, port), nil
}
func (f *Framework) WaitForNginxServer(name string) error {
time.Sleep(5 * time.Second)
return nil
}

95
test/e2e/framework/k8s.go Normal file
View file

@ -0,0 +1,95 @@
/*
Copyright 2017 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package framework
import (
"time"
api "k8s.io/api/core/v1"
core "k8s.io/api/core/v1"
extensions "k8s.io/api/extensions/v1beta1"
k8sErrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/wait"
)
func (f *Framework) EnsureSecret(secret *api.Secret) (*api.Secret, error) {
s, err := f.KubeClientSet.CoreV1().Secrets(secret.Namespace).Create(secret)
if err != nil {
if k8sErrors.IsAlreadyExists(err) {
return f.KubeClientSet.CoreV1().Secrets(secret.Namespace).Update(secret)
}
return nil, err
}
return s, nil
}
func (f *Framework) EnsureIngress(ingress *extensions.Ingress) (*extensions.Ingress, error) {
s, err := f.KubeClientSet.ExtensionsV1beta1().Ingresses(ingress.Namespace).Update(ingress)
if err != nil {
if k8sErrors.IsNotFound(err) {
return f.KubeClientSet.ExtensionsV1beta1().Ingresses(ingress.Namespace).Create(ingress)
}
return nil, err
}
return s, nil
}
func (f *Framework) EnsureService(service *core.Service) (*core.Service, error) {
s, err := f.KubeClientSet.CoreV1().Services(service.Namespace).Update(service)
if err != nil {
if k8sErrors.IsNotFound(err) {
return f.KubeClientSet.CoreV1().Services(service.Namespace).Create(service)
}
return nil, err
}
return s, nil
}
func (f *Framework) EnsureDeployment(deployment *extensions.Deployment) (*extensions.Deployment, error) {
d, err := f.KubeClientSet.Extensions().Deployments(deployment.Namespace).Update(deployment)
if err != nil {
if k8sErrors.IsNotFound(err) {
return f.KubeClientSet.Extensions().Deployments(deployment.Namespace).Create(deployment)
}
return nil, err
}
return d, nil
}
func (f *Framework) WaitForPodsReady(timeout time.Duration, expectedReplicas int, opts metav1.ListOptions) error {
return wait.Poll(time.Second, timeout, func() (bool, error) {
pl, err := f.KubeClientSet.Core().Pods(f.Namespace.Name).List(opts)
if err != nil {
return false, err
}
r := 0
for _, p := range pl.Items {
if p.Status.Phase != core.PodRunning {
continue
}
r++
}
if r == expectedReplicas {
return true, nil
}
return false, nil
})
}

View file

@ -120,7 +120,11 @@ func CreateKubeNamespace(baseName string, c kubernetes.Interface) (*v1.Namespace
}
func DeleteKubeNamespace(c kubernetes.Interface, namespace string) error {
return c.Core().Namespaces().Delete(namespace, nil)
deletePolicy := metav1.DeletePropagationForeground
return c.Core().Namespaces().Delete(namespace, &metav1.DeleteOptions{
GracePeriodSeconds: NewInt64(0),
PropagationPolicy: &deletePolicy,
})
}
func ExpectNoError(err error, explain ...interface{}) {
@ -131,7 +135,7 @@ func ExpectNoError(err error, explain ...interface{}) {
}
func WaitForKubeNamespaceNotExist(c kubernetes.Interface, namespace string) error {
return wait.PollImmediate(Poll, time.Minute*2, namespaceNotExist(c, namespace))
return wait.PollImmediate(Poll, time.Minute*1, namespaceNotExist(c, namespace))
}
func namespaceNotExist(c kubernetes.Interface, namespace string) wait.ConditionFunc {
@ -147,7 +151,28 @@ func namespaceNotExist(c kubernetes.Interface, namespace string) wait.ConditionF
}
}
// Waits default amount of time (PodStartTimeout) for the specified pod to become running.
func WaitForNoPodsInNamespace(c kubernetes.Interface, namespace string) error {
return wait.PollImmediate(Poll, time.Minute*2, noPodsInNamespace(c, namespace))
}
func noPodsInNamespace(c kubernetes.Interface, namespace string) wait.ConditionFunc {
return func() (bool, error) {
items, err := c.CoreV1().Pods(namespace).List(metav1.ListOptions{})
if apierrors.IsNotFound(err) {
return true, nil
}
if err != nil {
return false, err
}
if len(items.Items) == 0 {
return true, nil
}
return false, nil
}
}
// WaitForPodRunningInNamespace waits default amount of time (PodStartTimeout) for the specified pod to become running.
// Returns an error if timeout occurs first, or pod goes in to failed state.
func WaitForPodRunningInNamespace(c kubernetes.Interface, pod *v1.Pod) error {
if pod.Status.Phase == v1.PodRunning {
@ -175,3 +200,15 @@ func podRunning(c kubernetes.Interface, podName, namespace string) wait.Conditio
return false, nil
}
}
func NewInt32(val int32) *int32 {
p := new(int32)
*p = val
return p
}
func NewInt64(val int64) *int64 {
p := new(int64)
*p = val
return p
}