flagger/pkg/controller/scheduler_test.go

399 lines
10 KiB
Go

package controller
import (
"fmt"
"github.com/weaveworks/flagger/pkg/apis/flagger/v1alpha3"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"testing"
)
func TestScheduler_Init(t *testing.T) {
mocks := SetupMocks(false)
mocks.ctrl.advanceCanary("podinfo", "default", true)
_, err := mocks.kubeClient.AppsV1().Deployments("default").Get("podinfo-primary", metav1.GetOptions{})
if err != nil {
t.Fatal(err.Error())
}
}
func TestScheduler_NewRevision(t *testing.T) {
mocks := SetupMocks(false)
mocks.ctrl.advanceCanary("podinfo", "default", true)
// update
dep2 := newTestDeploymentV2()
_, err := mocks.kubeClient.AppsV1().Deployments("default").Update(dep2)
if err != nil {
t.Fatal(err.Error())
}
// detect changes
mocks.ctrl.advanceCanary("podinfo", "default", true)
c, err := mocks.kubeClient.AppsV1().Deployments("default").Get("podinfo", metav1.GetOptions{})
if err != nil {
t.Fatal(err.Error())
}
if *c.Spec.Replicas != 1 {
t.Errorf("Got canary replicas %v wanted %v", *c.Spec.Replicas, 1)
}
}
func TestScheduler_Rollback(t *testing.T) {
mocks := SetupMocks(false)
// init
mocks.ctrl.advanceCanary("podinfo", "default", true)
// update failed checks to max
err := mocks.deployer.SyncStatus(mocks.canary, v1alpha3.CanaryStatus{Phase: v1alpha3.CanaryPhaseProgressing, FailedChecks: 11})
if err != nil {
t.Fatal(err.Error())
}
// detect changes
mocks.ctrl.advanceCanary("podinfo", "default", true)
c, err := mocks.flaggerClient.FlaggerV1alpha3().Canaries("default").Get("podinfo", metav1.GetOptions{})
if err != nil {
t.Fatal(err.Error())
}
if c.Status.Phase != v1alpha3.CanaryPhaseFailed {
t.Errorf("Got canary state %v wanted %v", c.Status.Phase, v1alpha3.CanaryPhaseFailed)
}
}
func TestScheduler_SkipAnalysis(t *testing.T) {
mocks := SetupMocks(false)
// init
mocks.ctrl.advanceCanary("podinfo", "default", true)
// enable skip
cd, err := mocks.flaggerClient.FlaggerV1alpha3().Canaries("default").Get("podinfo", metav1.GetOptions{})
if err != nil {
t.Fatal(err.Error())
}
cd.Spec.SkipAnalysis = true
_, err = mocks.flaggerClient.FlaggerV1alpha3().Canaries("default").Update(cd)
if err != nil {
t.Fatal(err.Error())
}
// update
dep2 := newTestDeploymentV2()
_, err = mocks.kubeClient.AppsV1().Deployments("default").Update(dep2)
if err != nil {
t.Fatal(err.Error())
}
// detect changes
mocks.ctrl.advanceCanary("podinfo", "default", true)
// advance
mocks.ctrl.advanceCanary("podinfo", "default", true)
c, err := mocks.flaggerClient.FlaggerV1alpha3().Canaries("default").Get("podinfo", metav1.GetOptions{})
if err != nil {
t.Fatal(err.Error())
}
if !c.Spec.SkipAnalysis {
t.Errorf("Got skip analysis %v wanted %v", c.Spec.SkipAnalysis, true)
}
if c.Status.Phase != v1alpha3.CanaryPhaseSucceeded {
t.Errorf("Got canary state %v wanted %v", c.Status.Phase, v1alpha3.CanaryPhaseSucceeded)
}
}
func TestScheduler_NewRevisionReset(t *testing.T) {
mocks := SetupMocks(false)
// init
mocks.ctrl.advanceCanary("podinfo", "default", true)
// first update
dep2 := newTestDeploymentV2()
_, err := mocks.kubeClient.AppsV1().Deployments("default").Update(dep2)
if err != nil {
t.Fatal(err.Error())
}
// detect changes
mocks.ctrl.advanceCanary("podinfo", "default", true)
// advance
mocks.ctrl.advanceCanary("podinfo", "default", true)
primaryWeight, canaryWeight, err := mocks.router.GetRoutes(mocks.canary)
if err != nil {
t.Fatal(err.Error())
}
if primaryWeight != 90 {
t.Errorf("Got primary route %v wanted %v", primaryWeight, 90)
}
if canaryWeight != 10 {
t.Errorf("Got canary route %v wanted %v", canaryWeight, 10)
}
// second update
dep2.Spec.Template.Spec.ServiceAccountName = "test"
_, err = mocks.kubeClient.AppsV1().Deployments("default").Update(dep2)
if err != nil {
t.Fatal(err.Error())
}
// detect changes
mocks.ctrl.advanceCanary("podinfo", "default", true)
primaryWeight, canaryWeight, err = mocks.router.GetRoutes(mocks.canary)
if err != nil {
t.Fatal(err.Error())
}
if primaryWeight != 100 {
t.Errorf("Got primary route %v wanted %v", primaryWeight, 100)
}
if canaryWeight != 0 {
t.Errorf("Got canary route %v wanted %v", canaryWeight, 0)
}
}
func TestScheduler_Promotion(t *testing.T) {
mocks := SetupMocks(false)
// init
mocks.ctrl.advanceCanary("podinfo", "default", true)
// update
dep2 := newTestDeploymentV2()
_, err := mocks.kubeClient.AppsV1().Deployments("default").Update(dep2)
if err != nil {
t.Fatal(err.Error())
}
// detect pod spec changes
mocks.ctrl.advanceCanary("podinfo", "default", true)
config2 := NewTestConfigMapV2()
_, err = mocks.kubeClient.CoreV1().ConfigMaps("default").Update(config2)
if err != nil {
t.Fatal(err.Error())
}
secret2 := NewTestSecretV2()
_, err = mocks.kubeClient.CoreV1().Secrets("default").Update(secret2)
if err != nil {
t.Fatal(err.Error())
}
// detect configs changes
mocks.ctrl.advanceCanary("podinfo", "default", true)
primaryWeight, canaryWeight, err := mocks.router.GetRoutes(mocks.canary)
if err != nil {
t.Fatal(err.Error())
}
primaryWeight = 60
canaryWeight = 40
err = mocks.router.SetRoutes(mocks.canary, primaryWeight, canaryWeight)
if err != nil {
t.Fatal(err.Error())
}
// advance
mocks.ctrl.advanceCanary("podinfo", "default", true)
// promote
mocks.ctrl.advanceCanary("podinfo", "default", true)
primaryWeight, canaryWeight, err = mocks.router.GetRoutes(mocks.canary)
if err != nil {
t.Fatal(err.Error())
}
if primaryWeight != 100 {
t.Errorf("Got primary route %v wanted %v", primaryWeight, 100)
}
if canaryWeight != 0 {
t.Errorf("Got canary route %v wanted %v", canaryWeight, 0)
}
primaryDep, err := mocks.kubeClient.AppsV1().Deployments("default").Get("podinfo-primary", metav1.GetOptions{})
if err != nil {
t.Fatal(err.Error())
}
primaryImage := primaryDep.Spec.Template.Spec.Containers[0].Image
canaryImage := dep2.Spec.Template.Spec.Containers[0].Image
if primaryImage != canaryImage {
t.Errorf("Got primary image %v wanted %v", primaryImage, canaryImage)
}
configPrimary, err := mocks.kubeClient.CoreV1().ConfigMaps("default").Get("podinfo-config-env-primary", metav1.GetOptions{})
if err != nil {
t.Fatal(err.Error())
}
if configPrimary.Data["color"] != config2.Data["color"] {
t.Errorf("Got primary ConfigMap color %s wanted %s", configPrimary.Data["color"], config2.Data["color"])
}
secretPrimary, err := mocks.kubeClient.CoreV1().Secrets("default").Get("podinfo-secret-env-primary", metav1.GetOptions{})
if err != nil {
t.Fatal(err.Error())
}
if string(secretPrimary.Data["apiKey"]) != string(secret2.Data["apiKey"]) {
t.Errorf("Got primary secret %s wanted %s", secretPrimary.Data["apiKey"], secret2.Data["apiKey"])
}
// check finalising status
c, err := mocks.flaggerClient.FlaggerV1alpha3().Canaries("default").Get("podinfo", metav1.GetOptions{})
if err != nil {
t.Fatal(err.Error())
}
// scale canary to zero
mocks.ctrl.advanceCanary("podinfo", "default", true)
c, err = mocks.flaggerClient.FlaggerV1alpha3().Canaries("default").Get("podinfo", metav1.GetOptions{})
if err != nil {
t.Fatal(err.Error())
}
if c.Status.Phase != v1alpha3.CanaryPhaseSucceeded {
t.Errorf("Got canary state %v wanted %v", c.Status.Phase, v1alpha3.CanaryPhaseSucceeded)
}
}
func TestScheduler_ABTesting(t *testing.T) {
mocks := SetupMocks(true)
// init
mocks.ctrl.advanceCanary("podinfo", "default", true)
// update
dep2 := newTestDeploymentV2()
_, err := mocks.kubeClient.AppsV1().Deployments("default").Update(dep2)
if err != nil {
t.Fatal(err.Error())
}
// detect pod spec changes
mocks.ctrl.advanceCanary("podinfo", "default", true)
// advance
mocks.ctrl.advanceCanary("podinfo", "default", true)
// check if traffic is routed to canary
primaryWeight, canaryWeight, err := mocks.router.GetRoutes(mocks.canary)
if err != nil {
t.Fatal(err.Error())
}
if primaryWeight != 0 {
t.Errorf("Got primary route %v wanted %v", primaryWeight, 0)
}
if canaryWeight != 100 {
t.Errorf("Got canary route %v wanted %v", canaryWeight, 100)
}
cd, err := mocks.flaggerClient.FlaggerV1alpha3().Canaries("default").Get("podinfo", metav1.GetOptions{})
if err != nil {
t.Fatal(err.Error())
}
// set max iterations
if err := mocks.deployer.SetStatusIterations(cd, 10); err != nil {
t.Fatal(err.Error())
}
// advance
mocks.ctrl.advanceCanary("podinfo", "default", true)
// finalising
mocks.ctrl.advanceCanary("podinfo", "default", true)
// check finalising status
c, err := mocks.flaggerClient.FlaggerV1alpha3().Canaries("default").Get("podinfo", metav1.GetOptions{})
if err != nil {
t.Fatal(err.Error())
}
if c.Status.Phase != v1alpha3.CanaryPhaseFinalising {
t.Errorf("Got canary state %v wanted %v", c.Status.Phase, v1alpha3.CanaryPhaseFinalising)
}
// check if the container image tag was updated
primaryDep, err := mocks.kubeClient.AppsV1().Deployments("default").Get("podinfo-primary", metav1.GetOptions{})
if err != nil {
t.Fatal(err.Error())
}
primaryImage := primaryDep.Spec.Template.Spec.Containers[0].Image
canaryImage := dep2.Spec.Template.Spec.Containers[0].Image
if primaryImage != canaryImage {
t.Errorf("Got primary image %v wanted %v", primaryImage, canaryImage)
}
// shutdown canary
mocks.ctrl.advanceCanary("podinfo", "default", true)
// check rollout status
c, err = mocks.flaggerClient.FlaggerV1alpha3().Canaries("default").Get("podinfo", metav1.GetOptions{})
if err != nil {
t.Fatal(err.Error())
}
if c.Status.Phase != v1alpha3.CanaryPhaseSucceeded {
t.Errorf("Got canary state %v wanted %v", c.Status.Phase, v1alpha3.CanaryPhaseSucceeded)
}
}
func TestScheduler_PortDiscovery(t *testing.T) {
mocks := SetupMocks(false)
// enable port discovery
cd, err := mocks.flaggerClient.FlaggerV1alpha3().Canaries("default").Get("podinfo", metav1.GetOptions{})
if err != nil {
t.Fatal(err.Error())
}
cd.Spec.Service.PortDiscovery = true
_, err = mocks.flaggerClient.FlaggerV1alpha3().Canaries("default").Update(cd)
if err != nil {
t.Fatal(err.Error())
}
mocks.ctrl.advanceCanary("podinfo", "default", true)
canarySvc, err := mocks.kubeClient.CoreV1().Services("default").Get("podinfo-canary", metav1.GetOptions{})
if err != nil {
t.Fatal(err.Error())
}
if len(canarySvc.Spec.Ports) != 3 {
t.Fatalf("Got svc port count %v wanted %v", len(canarySvc.Spec.Ports), 3)
}
matchPorts := func(lookup string) bool {
switch lookup {
case
"http 9898",
"http-metrics 8080",
"tcp-podinfo-2 8888":
return true
}
return false
}
for _, port := range canarySvc.Spec.Ports {
if !matchPorts(fmt.Sprintf("%s %v", port.Name, port.Port)) {
t.Fatalf("Got wrong svc port %v", port.Name)
}
}
}