Files
flagger/pkg/router/kubernetes_default_test.go
T
Tanner Altares 92937a8f48 kubectl annotation support
rebase and squash

fix fmt issues

revert Dockerfile

revert go.mod and go.sum

introduction of finalizer

introduction of finalizer

remove test for finalizer add istio tests

fix fmt issues

revert go.mod and go.sum

revert Dockerfile and main.go

fmt deployment controller

introduction of finalizer

rebase and squash

fix fmt issues

revert Dockerfile

revert go.mod and go.sum

introduction of finalizer

introduction of finalizer

remove test for finalizer add istio tests

fix fmt issues

revert go.mod and go.sum

revert Dockerfile and main.go

fmt deployment controller

add unit tests for finalizing

introduction of finalizer

rebase and squash

fix fmt issues

revert Dockerfile

revert go.mod and go.sum

introduction of finalizer

introduction of finalizer

remove test for finalizer add istio tests

fix fmt issues

revert go.mod and go.sum

revert Dockerfile and main.go

fmt deployment controller

run fmt to clean up formatting

review changes

add kubectl annotation

add kubectl annotation support

introduction of finalizer

introduction of finalizer

rebase and squash

fix fmt issues

revert Dockerfile

revert go.mod and go.sum

introduction of finalizer

introduction of finalizer

remove test for finalizer add istio tests

fix fmt issues

revert go.mod and go.sum

revert Dockerfile and main.go

fmt deployment controller

introduction of finalizer

rebase and squash

fix fmt issues

revert Dockerfile

revert go.mod and go.sum

introduction of finalizer

introduction of finalizer

remove test for finalizer add istio tests

fix fmt issues

revert go.mod and go.sum

revert Dockerfile and main.go

fmt deployment controller

add unit tests for finalizing

introduction of finalizer

rebase and squash

fix fmt issues

revert Dockerfile

revert go.mod and go.sum

introduction of finalizer

introduction of finalizer

remove test for finalizer add istio tests

fix fmt issues

revert go.mod and go.sum

revert Dockerfile and main.go

fmt deployment controller

run fmt to clean up formatting

review changes

introduction of finalizer

introduction of finalizer

rebase and squash

fix fmt issues

revert Dockerfile

revert go.mod and go.sum

introduction of finalizer

introduction of finalizer

remove test for finalizer add istio tests

fix fmt issues

revert go.mod and go.sum

revert Dockerfile and main.go

fmt deployment controller

introduction of finalizer

rebase and squash

fix fmt issues

revert Dockerfile

revert go.mod and go.sum

introduction of finalizer

introduction of finalizer

remove test for finalizer add istio tests

fix fmt issues

revert go.mod and go.sum

revert Dockerfile and main.go

fmt deployment controller

add unit tests for finalizing

introduction of finalizer

rebase and squash

fix fmt issues

revert Dockerfile

revert go.mod and go.sum

introduction of finalizer

introduction of finalizer

remove test for finalizer add istio tests

fix fmt issues

revert go.mod and go.sum

revert Dockerfile and main.go

fmt deployment controller

run fmt to clean up formatting

review changes
2020-03-20 15:13:51 -05:00

365 lines
10 KiB
Go

package router
import (
"fmt"
"testing"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/errors"
"k8s.io/client-go/kubernetes/fake"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/weaveworks/flagger/pkg/apis/flagger/v1beta1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
func TestServiceRouter_Create(t *testing.T) {
mocks := newFixture(nil)
router := &KubernetesDefaultRouter{
kubeClient: mocks.kubeClient,
flaggerClient: mocks.flaggerClient,
logger: mocks.logger,
}
err := router.Initialize(mocks.canary)
require.NoError(t, err)
err = router.Reconcile(mocks.canary)
require.NoError(t, err)
canarySvc, err := mocks.kubeClient.CoreV1().Services("default").Get("podinfo-canary", metav1.GetOptions{})
require.NoError(t, err)
assert.Equal(t, "http", canarySvc.Spec.Ports[0].Name)
assert.Equal(t, int32(9898), canarySvc.Spec.Ports[0].Port)
primarySvc, err := mocks.kubeClient.CoreV1().Services("default").Get("podinfo-primary", metav1.GetOptions{})
require.NoError(t, err)
assert.Equal(t, "http", primarySvc.Spec.Ports[0].Name)
assert.Equal(t, int32(9898), primarySvc.Spec.Ports[0].Port)
}
func TestServiceRouter_Update(t *testing.T) {
mocks := newFixture(nil)
router := &KubernetesDefaultRouter{
kubeClient: mocks.kubeClient,
flaggerClient: mocks.flaggerClient,
logger: mocks.logger,
}
err := router.Initialize(mocks.canary)
require.NoError(t, err)
err = router.Reconcile(mocks.canary)
require.NoError(t, err)
canary, err := mocks.flaggerClient.FlaggerV1beta1().Canaries("default").Get("podinfo", metav1.GetOptions{})
require.NoError(t, err)
canaryClone := canary.DeepCopy()
canaryClone.Spec.Service.PortName = "grpc"
c, err := mocks.flaggerClient.FlaggerV1beta1().Canaries("default").Update(canaryClone)
require.NoError(t, err)
// apply changes
err = router.Initialize(c)
require.NoError(t, err)
err = router.Reconcile(c)
require.NoError(t, err)
canarySvc, err := mocks.kubeClient.CoreV1().Services("default").Get("podinfo-canary", metav1.GetOptions{})
require.NoError(t, err)
assert.Equal(t, "grpc", canarySvc.Spec.Ports[0].Name)
}
func TestServiceRouter_Undo(t *testing.T) {
mocks := newFixture(nil)
router := &KubernetesDefaultRouter{
kubeClient: mocks.kubeClient,
flaggerClient: mocks.flaggerClient,
logger: mocks.logger,
}
err := router.Initialize(mocks.canary)
require.NoError(t, err)
err = router.Reconcile(mocks.canary)
require.NoError(t, err)
canarySvc, err := mocks.kubeClient.CoreV1().Services("default").Get("podinfo-canary", metav1.GetOptions{})
require.NoError(t, err)
svcClone := canarySvc.DeepCopy()
svcClone.Spec.Ports[0].Name = "http2-podinfo"
svcClone.Spec.Ports[0].Port = 8080
_, err = mocks.kubeClient.CoreV1().Services("default").Update(svcClone)
require.NoError(t, err)
// undo changes
err = router.Initialize(mocks.canary)
require.NoError(t, err)
err = router.Reconcile(mocks.canary)
require.NoError(t, err)
canarySvc, err = mocks.kubeClient.CoreV1().Services("default").Get("podinfo-canary", metav1.GetOptions{})
require.NoError(t, err)
assert.Equal(t, "http", canarySvc.Spec.Ports[0].Name)
assert.Equal(t, int32(9898), canarySvc.Spec.Ports[0].Port)
}
func TestServiceRouter_isOwnedByCanary(t *testing.T) {
mocks := newFixture(nil)
router := &KubernetesDefaultRouter{
kubeClient: mocks.kubeClient,
flaggerClient: mocks.flaggerClient,
logger: mocks.logger,
}
isController := new(bool)
*isController = true
tables := []struct {
svc *corev1.Service
isOwned bool
hasOwnerRef bool
}{
//owned
{
svc: &corev1.Service{
ObjectMeta: metav1.ObjectMeta{
Name: "podinfo",
Namespace: "default",
OwnerReferences: []metav1.OwnerReference{
{
APIVersion: "flagger.app/v1alpha3",
Kind: "Canary",
Name: "podinfo",
Controller: isController,
},
},
},
Spec: corev1.ServiceSpec{
Ports: []corev1.ServicePort{{
Name: "http",
Protocol: "TCP",
Port: 8080,
}},
Selector: map[string]string{"app": "podinfo"},
},
}, isOwned: true, hasOwnerRef: true,
},
//Owner ref but kind not Canary
{
svc: &corev1.Service{
ObjectMeta: metav1.ObjectMeta{
Name: "podinfo",
Namespace: "default",
OwnerReferences: []metav1.OwnerReference{
{
APIVersion: "flagger.app/v1alpha3",
Kind: "Deployment",
Name: "podinfo",
Controller: isController,
},
},
},
Spec: corev1.ServiceSpec{
Ports: []corev1.ServicePort{{
Name: "http",
Protocol: "TCP",
Port: 8080,
}},
Selector: map[string]string{"app": "podinfo"},
},
}, isOwned: false, hasOwnerRef: false,
},
//Owner ref but name doesn't match
{
svc: &corev1.Service{
ObjectMeta: metav1.ObjectMeta{
Name: "podinfo",
Namespace: "default",
OwnerReferences: []metav1.OwnerReference{
{
APIVersion: "flagger.app/v1alpha3",
Kind: "Canary",
Name: "notpodinfo",
Controller: isController,
},
},
},
Spec: corev1.ServiceSpec{
Ports: []corev1.ServicePort{{
Name: "http",
Protocol: "TCP",
Port: 8080,
}},
Selector: map[string]string{"app": "podinfo"},
},
}, isOwned: false, hasOwnerRef: true,
},
//No ownerRef
{
svc: &corev1.Service{
ObjectMeta: metav1.ObjectMeta{
Name: "podinfo",
Namespace: "default",
},
Spec: corev1.ServiceSpec{
Ports: []corev1.ServicePort{{
Name: "http",
Protocol: "TCP",
Port: 8080,
}},
Selector: map[string]string{"app": "podinfo"},
},
}, isOwned: false, hasOwnerRef: false,
},
}
for _, table := range tables {
hasOwnerRef, wasOwned := router.isOwnedByCanary(table.svc, mocks.canary.Name)
if table.isOwned && !wasOwned {
t.Error("Expected to be owned, but was not")
} else if !table.isOwned && wasOwned {
t.Error("Expected not to be owned but was")
} else if table.hasOwnerRef && !hasOwnerRef {
t.Error("Expected to contain OwnerReference but not present")
} else if !table.hasOwnerRef && hasOwnerRef {
t.Error("Expected not to have an OwnerReference but present")
}
}
}
func TestServiceRouter_Finalize(t *testing.T) {
mocks := newFixture(nil)
router := &KubernetesDefaultRouter{
kubeClient: mocks.kubeClient,
flaggerClient: mocks.flaggerClient,
logger: mocks.logger,
labelSelector: "app",
}
isController := new(bool)
*isController = true
svc := &corev1.Service{
ObjectMeta: metav1.ObjectMeta{
Name: "podinfo",
Namespace: "default",
OwnerReferences: []metav1.OwnerReference{
{
APIVersion: "flagger.app/v1alpha3",
Kind: "Canary",
Name: "NotOwned",
Controller: isController,
},
},
},
Spec: corev1.ServiceSpec{
Ports: []corev1.ServicePort{{
Name: "http",
Protocol: "TCP",
Port: 9898,
}},
Selector: map[string]string{"app": "podinfo"},
},
}
kubectlSvc := &corev1.Service{
ObjectMeta: metav1.ObjectMeta{
Name: "podinfo",
Namespace: "default",
OwnerReferences: []metav1.OwnerReference{
{
APIVersion: "flagger.app/v1alpha3",
Kind: "Canary",
Name: "NotOwned",
Controller: isController,
},
},
Annotations: map[string]string{
kubectlAnnotation: `{"apiVersion":"v1","kind":"Service","metadata":{"annotations":{},"labels":{"app":"podinfo"},"name":"podinfo","namespace":"test"},"spec":{"ports":[{"name":"http","port":9898,"protocol":"TCP","targetPort":9898}],"selector":{"app":"podinfo"},"type":"ClusterIP"}}`,
},
},
Spec: corev1.ServiceSpec{
Ports: []corev1.ServicePort{{
Name: "http",
Protocol: "TCP",
Port: 9898,
}},
Selector: map[string]string{"app": "podinfo"},
},
}
tables := []struct {
router *KubernetesDefaultRouter
callSetupMethods bool
shouldError bool
canary *v1beta1.Canary
shouldMutate bool
}{
//Won't reconcile since it is owned and would be garbage collected
{router: router, callSetupMethods: true, shouldError: false, canary: mocks.canary, shouldMutate: false},
//Service not found
{router: &KubernetesDefaultRouter{kubeClient: fake.NewSimpleClientset(), logger: mocks.logger}, callSetupMethods: false, shouldError: true, canary: mocks.canary, shouldMutate: false},
//Not owned
{router: &KubernetesDefaultRouter{kubeClient: fake.NewSimpleClientset(svc), logger: mocks.logger}, callSetupMethods: false, shouldError: false, canary: mocks.canary, shouldMutate: true},
//Kubectl annotation
{router: &KubernetesDefaultRouter{kubeClient: fake.NewSimpleClientset(kubectlSvc), logger: mocks.logger}, callSetupMethods: false, shouldError: false, canary: mocks.canary, shouldMutate: true},
}
for _, table := range tables {
if table.callSetupMethods {
err := table.router.Initialize(table.canary)
if err != nil {
t.Fatal(err.Error())
}
err = table.router.Reconcile(table.canary)
if err != nil {
t.Fatal(err.Error())
}
}
err := table.router.Finalize(table.canary)
if table.shouldError && err == nil {
t.Error("Should have errored")
} else if !table.shouldError && err != nil {
t.Errorf("Shouldn't error but did %s", err)
}
svc, err := table.router.kubeClient.CoreV1().Services(table.canary.Namespace).Get(table.canary.Name, metav1.GetOptions{})
if err != nil {
if !errors.IsNotFound(err) {
if svc.Spec.Ports[0].Name != "http" {
t.Errorf("Got svc port name %s wanted %s", svc.Spec.Ports[0].Name, "http")
}
if svc.Spec.Ports[0].Port != 9898 {
t.Errorf("Got svc port %v wanted %v", svc.Spec.Ports[0].Port, 9898)
}
if table.shouldMutate {
if svc.Spec.Selector["app"] != table.canary.Name {
t.Errorf("Got svc selector %v wanted %v", svc.Spec.Selector["app"], table.canary.Name)
}
} else {
if svc.Spec.Selector["app"] != fmt.Sprintf("%s-primary", table.canary.Name) {
t.Errorf("Got svc selector %v wanted %v", svc.Spec.Selector["app"], fmt.Sprintf("%s-primary", table.canary.Name))
}
}
}
}
}
}