2020-03-06 10:50:39 +00:00
|
|
|
/*
|
|
|
|
Copyright 2020 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"
|
|
|
|
"reflect"
|
|
|
|
"strings"
|
|
|
|
|
2022-07-31 16:16:28 +00:00
|
|
|
"github.com/onsi/ginkgo/v2"
|
2020-03-06 10:50:39 +00:00
|
|
|
"github.com/stretchr/testify/assert"
|
|
|
|
|
|
|
|
"k8s.io/ingress-nginx/test/e2e/framework"
|
|
|
|
)
|
|
|
|
|
|
|
|
var _ = framework.DescribeAnnotation("affinitymode", func() {
|
|
|
|
f := framework.NewDefaultFramework("affinity")
|
|
|
|
|
|
|
|
ginkgo.It("Balanced affinity mode should balance", func() {
|
|
|
|
deploymentName := "affinitybalanceecho"
|
|
|
|
replicas := 5
|
2022-02-02 13:12:22 +00:00
|
|
|
f.NewEchoDeployment(
|
|
|
|
framework.WithDeploymentName(deploymentName),
|
|
|
|
framework.WithDeploymentReplicas(replicas),
|
|
|
|
)
|
2020-03-06 10:50:39 +00:00
|
|
|
|
|
|
|
host := "affinity-mode-balance.com"
|
|
|
|
annotations := make(map[string]string)
|
|
|
|
annotations["nginx.ingress.kubernetes.io/affinity"] = "cookie"
|
|
|
|
annotations["ginx.ingress.kubernetes.io/session-cookie-name"] = "hello-cookie"
|
|
|
|
annotations["nginx.ingress.kubernetes.io/session-cookie-expires"] = "172800"
|
|
|
|
annotations["nginx.ingress.kubernetes.io/session-cookie-max-age"] = "172800"
|
|
|
|
annotations["nginx.ingress.kubernetes.io/ssl-redirect"] = "false"
|
|
|
|
annotations["nginx.ingress.kubernetes.io/affinity-mode"] = "balanced"
|
|
|
|
annotations["nginx.ingress.kubernetes.io/session-cookie-hash"] = "sha1"
|
|
|
|
|
|
|
|
ing := framework.NewSingleIngress(host, "/", host, f.Namespace, deploymentName, 80, annotations)
|
|
|
|
f.EnsureIngress(ing)
|
|
|
|
|
|
|
|
f.WaitForNginxServer(host,
|
|
|
|
func(server string) bool {
|
|
|
|
return strings.Contains(server, fmt.Sprintf("server_name %s ;", host))
|
|
|
|
})
|
|
|
|
|
|
|
|
// Check configuration
|
|
|
|
ingress := f.GetIngress(f.Namespace, host)
|
|
|
|
returnedAnnotations := ingress.GetAnnotations()
|
|
|
|
isItEqual := reflect.DeepEqual(annotations, returnedAnnotations)
|
|
|
|
assert.Equal(ginkgo.GinkgoT(), isItEqual, true)
|
|
|
|
})
|
|
|
|
|
|
|
|
ginkgo.It("Check persistent affinity mode", func() {
|
|
|
|
deploymentName := "affinitypersistentecho"
|
|
|
|
replicas := 5
|
2022-02-02 13:12:22 +00:00
|
|
|
f.NewEchoDeployment(
|
|
|
|
framework.WithDeploymentName(deploymentName),
|
|
|
|
framework.WithDeploymentReplicas(replicas),
|
|
|
|
)
|
2020-03-06 10:50:39 +00:00
|
|
|
|
|
|
|
host := "affinity-mode-persistent.com"
|
|
|
|
annotations := make(map[string]string)
|
|
|
|
annotations["nginx.ingress.kubernetes.io/affinity"] = "cookie"
|
|
|
|
annotations["ginx.ingress.kubernetes.io/session-cookie-name"] = "hello-cookie"
|
|
|
|
annotations["nginx.ingress.kubernetes.io/session-cookie-expires"] = "172800"
|
|
|
|
annotations["nginx.ingress.kubernetes.io/session-cookie-max-age"] = "172800"
|
|
|
|
annotations["nginx.ingress.kubernetes.io/ssl-redirect"] = "false"
|
|
|
|
annotations["nginx.ingress.kubernetes.io/affinity-mode"] = "persistent"
|
|
|
|
annotations["nginx.ingress.kubernetes.io/session-cookie-hash"] = "sha1"
|
|
|
|
|
|
|
|
ing := framework.NewSingleIngress(host, "/", host, f.Namespace, deploymentName, 80, annotations)
|
|
|
|
f.EnsureIngress(ing)
|
|
|
|
|
|
|
|
f.WaitForNginxServer(host,
|
|
|
|
func(server string) bool {
|
|
|
|
return strings.Contains(server, fmt.Sprintf("server_name %s ;", host))
|
|
|
|
})
|
|
|
|
|
|
|
|
// Check configuration
|
|
|
|
ingress := f.GetIngress(f.Namespace, host)
|
|
|
|
returnedAnnotations := ingress.GetAnnotations()
|
|
|
|
isItEqual := reflect.DeepEqual(annotations, returnedAnnotations)
|
|
|
|
assert.Equal(ginkgo.GinkgoT(), isItEqual, true)
|
|
|
|
|
|
|
|
// Make a request
|
|
|
|
request := f.HTTPTestClient().GET("/").WithHeader("Host", host)
|
|
|
|
response := request.Expect()
|
|
|
|
|
|
|
|
// Get the responder host name
|
|
|
|
originalHostName := getHostnameFromResponseBody(response.Body().Raw())
|
|
|
|
|
|
|
|
// Send new requests and add new backends. Check which backend responded to the sent request
|
|
|
|
cookies := getCookiesFromHeader(response.Header("Set-Cookie").Raw())
|
|
|
|
for sendRequestNumber := 0; sendRequestNumber < 10; sendRequestNumber++ {
|
|
|
|
replicas = replicas + 1
|
|
|
|
err := framework.UpdateDeployment(f.KubeClientSet, f.Namespace, deploymentName, replicas, nil)
|
|
|
|
assert.Nil(ginkgo.GinkgoT(), err)
|
2020-06-27 15:57:00 +00:00
|
|
|
framework.Sleep()
|
2020-07-01 21:19:51 +00:00
|
|
|
|
2020-03-06 10:50:39 +00:00
|
|
|
response = request.WithCookies(cookies).Expect()
|
|
|
|
newHostName := getHostnameFromResponseBody(response.Body().Raw())
|
|
|
|
assert.Equal(ginkgo.GinkgoT(), originalHostName, newHostName,
|
|
|
|
fmt.Sprintf("Response number %v is not from the same host. Original host: %s, response returned: %s", sendRequestNumber, originalHostName, newHostName))
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
// remove all backends
|
|
|
|
replicas = 0
|
|
|
|
err := framework.UpdateDeployment(f.KubeClientSet, f.Namespace, deploymentName, replicas, nil)
|
|
|
|
assert.Nil(ginkgo.GinkgoT(), err)
|
2020-06-27 15:57:00 +00:00
|
|
|
framework.Sleep()
|
2020-03-06 10:50:39 +00:00
|
|
|
|
|
|
|
// validate, there is no backend to serve the request
|
|
|
|
response = request.WithCookies(cookies).Expect().Status(http.StatusServiceUnavailable)
|
|
|
|
|
|
|
|
// create brand new backends
|
|
|
|
replicas = 2
|
|
|
|
err = framework.UpdateDeployment(f.KubeClientSet, f.Namespace, deploymentName, replicas, nil)
|
|
|
|
assert.Nil(ginkgo.GinkgoT(), err)
|
2020-06-27 15:57:00 +00:00
|
|
|
framework.Sleep()
|
2020-03-06 10:50:39 +00:00
|
|
|
|
|
|
|
// wait brand new backends to spawn
|
|
|
|
response = request.WithCookies(cookies).Expect()
|
|
|
|
try := 0
|
|
|
|
for (response.Raw().StatusCode == http.StatusServiceUnavailable) && (try < 30) {
|
2020-06-27 15:57:00 +00:00
|
|
|
framework.Sleep()
|
2020-03-06 10:50:39 +00:00
|
|
|
response = request.WithCookies(cookies).Expect()
|
|
|
|
try++
|
|
|
|
}
|
2020-09-27 20:32:40 +00:00
|
|
|
assert.LessOrEqual(ginkgo.GinkgoT(), try, 29, "Tries reached it's maximum, backends did not deployed in time")
|
2020-03-06 10:50:39 +00:00
|
|
|
|
|
|
|
// brand new backends equals new hostname
|
|
|
|
newHostName := getHostnameFromResponseBody(response.Body().Raw())
|
|
|
|
assert.NotEqual(ginkgo.GinkgoT(), originalHostName, newHostName,
|
|
|
|
fmt.Sprintf("Response is from the same host (That should not be possible). Original host: %s, response returned: %s", originalHostName, newHostName))
|
|
|
|
})
|
|
|
|
})
|
|
|
|
|
|
|
|
func getHostnameFromResponseBody(rawResponseBody string) string {
|
|
|
|
lines := strings.Split(strings.TrimSpace(rawResponseBody), "\n")
|
|
|
|
for _, line := range lines {
|
|
|
|
if strings.Contains(line, "Hostname") {
|
|
|
|
hostnameParts := strings.Split(strings.TrimSpace(line), ":")
|
|
|
|
if len(hostnameParts) == 2 {
|
|
|
|
return strings.TrimSpace(hostnameParts[1])
|
|
|
|
}
|
|
|
|
return ""
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return ""
|
|
|
|
}
|
|
|
|
|
|
|
|
func getCookiesFromHeader(rawheader string) map[string]string {
|
|
|
|
cookies := make(map[string]string)
|
|
|
|
parts := strings.Split(strings.TrimSpace(rawheader), ";")
|
|
|
|
for _, part := range parts {
|
|
|
|
subparts := strings.Split(strings.TrimSpace(part), "=")
|
|
|
|
if len(subparts) == 2 {
|
|
|
|
cookies[subparts[0]] = subparts[1]
|
|
|
|
} else {
|
|
|
|
cookies[subparts[0]] = ""
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return cookies
|
|
|
|
}
|