From a70fdbfaa943903a0469bd61d0bb7edc662805c5 Mon Sep 17 00:00:00 2001 From: George Date: Tue, 24 Mar 2020 16:20:55 +0000 Subject: [PATCH] Add new labels to Services (#1555) * Add new labels in pod creation Signed-off-by: glindsell * Remove test pod labels and add service labels Signed-off-by: glindsell * Replace default bool with canary bool Signed-off-by: glindsell * Remove canary field in CRD and update Canary logic Signed-off-by: glindsell * Move labels logic and add tests Signed-off-by: glindsell --- .../v1/seldondeployment_types.go | 22 ++- operator/controllers/labels.go | 40 ++++++ operator/controllers/labels_test.go | 132 ++++++++++++++++++ .../seldondeployment_controller.go | 20 +-- 4 files changed, 200 insertions(+), 14 deletions(-) create mode 100644 operator/controllers/labels.go create mode 100644 operator/controllers/labels_test.go diff --git a/operator/apis/machinelearning/v1/seldondeployment_types.go b/operator/apis/machinelearning/v1/seldondeployment_types.go index f47e47f77d..5efa94c6a6 100644 --- a/operator/apis/machinelearning/v1/seldondeployment_types.go +++ b/operator/apis/machinelearning/v1/seldondeployment_types.go @@ -19,17 +19,29 @@ package v1 import ( "crypto/md5" "encoding/hex" + "strconv" + autoscalingv2beta2 "k8s.io/api/autoscaling/v2beta1" v1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "strconv" ) const ( - Label_seldon_id = "seldon-deployment-id" - Label_seldon_app = "seldon-app" - Label_seldon_app_svc = "seldon-app-svc" - Label_svc_orch = "seldon-deployment-contains-svcorch" + Label_seldon_id = "seldon-deployment-id" + Label_seldon_app = "seldon-app" + Label_seldon_app_svc = "seldon-app-svc" + Label_svc_orch = "seldon-deployment-contains-svcorch" + Label_app = "app" + Label_fluentd = "fluentd" + Label_router = "router" + Label_combiner = "combiner" + Label_model = "model" + Label_transformer = "transformer" + Label_output_transformer = "output-transformer" + Label_default = "default" + Label_shadow = "shadow" + Label_canary = "canary" + Label_explainer = "explainer" PODINFO_VOLUME_NAME = "podinfo" PODINFO_VOLUME_PATH = "/etc/podinfo" diff --git a/operator/controllers/labels.go b/operator/controllers/labels.go new file mode 100644 index 0000000000..5760f8f9c4 --- /dev/null +++ b/operator/controllers/labels.go @@ -0,0 +1,40 @@ +package controllers + +import ( + machinelearningv1 "github.com/seldonio/seldon-core/operator/apis/machinelearning/v1" + appsv1 "k8s.io/api/apps/v1" + corev1 "k8s.io/api/core/v1" +) + +func addLabelsToService(svc *corev1.Service, pu *machinelearningv1.PredictiveUnit, p machinelearningv1.PredictorSpec) { + switch *pu.Type { + case machinelearningv1.ROUTER: + svc.Labels[machinelearningv1.Label_router] = "true" + case machinelearningv1.COMBINER: + svc.Labels[machinelearningv1.Label_combiner] = "true" + case machinelearningv1.MODEL: + svc.Labels[machinelearningv1.Label_model] = "true" + case machinelearningv1.TRANSFORMER: + svc.Labels[machinelearningv1.Label_transformer] = "true" + case machinelearningv1.OUTPUT_TRANSFORMER: + svc.Labels[machinelearningv1.Label_output_transformer] = "true" + } + if p.Shadow != true && (p.Traffic >= 50 || p.Traffic == 0) { + svc.Labels[machinelearningv1.Label_default] = "true" + } + if p.Shadow == true { + svc.Labels[machinelearningv1.Label_shadow] = "true" + } + if p.Traffic < 50 && p.Traffic > 0 { + svc.Labels[machinelearningv1.Label_canary] = "true" + } + if p.Explainer != nil { + svc.Labels[machinelearningv1.Label_explainer] = "true" + } +} + +func addLabelsToDeployment(deploy *appsv1.Deployment, containerServiceKey, containerServiceValue string) { + deploy.ObjectMeta.Labels[containerServiceKey] = containerServiceValue + deploy.Spec.Selector.MatchLabels[containerServiceKey] = containerServiceValue + deploy.Spec.Template.ObjectMeta.Labels[containerServiceKey] = containerServiceValue +} diff --git a/operator/controllers/labels_test.go b/operator/controllers/labels_test.go new file mode 100644 index 0000000000..5c842f6153 --- /dev/null +++ b/operator/controllers/labels_test.go @@ -0,0 +1,132 @@ +package controllers + +import ( + "testing" + + . "github.com/onsi/gomega" + machinelearningv1 "github.com/seldonio/seldon-core/operator/apis/machinelearning/v1" + appsv1 "k8s.io/api/apps/v1" + corev1 "k8s.io/api/core/v1" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +func TestAddLabelsToDeployment(t *testing.T) { + g := NewGomegaWithT(t) + d := &appsv1.Deployment{ + ObjectMeta: metav1.ObjectMeta{ + Labels: map[string]string{}, + }, + Spec: appsv1.DeploymentSpec{ + Selector: &metav1.LabelSelector{ + MatchLabels: map[string]string{}, + }, + Template: corev1.PodTemplateSpec{ + ObjectMeta: metav1.ObjectMeta{ + Labels: map[string]string{}, + }, + }, + }, + } + t.Run("adds correct label to Deployment", func(t *testing.T) { + addLabelsToDeployment(d, "TestKey", "TestValue") + g.Expect(d.ObjectMeta.Labels["TestKey"]).To(Equal("TestValue")) + g.Expect(d.Spec.Selector.MatchLabels["TestKey"]).To(Equal("TestValue")) + g.Expect(d.Spec.Template.ObjectMeta.Labels["TestKey"]).To(Equal("TestValue")) + }) +} + +func TestAddLabelsToService(t *testing.T) { + g := NewGomegaWithT(t) + cases1 := []struct { + puType machinelearningv1.PredictiveUnitType + result string + }{ + { + puType: machinelearningv1.ROUTER, + result: machinelearningv1.Label_router, + }, + { + puType: machinelearningv1.COMBINER, + result: machinelearningv1.Label_combiner, + }, + { + puType: machinelearningv1.MODEL, + result: machinelearningv1.Label_model, + }, + { + puType: machinelearningv1.TRANSFORMER, + result: machinelearningv1.Label_transformer, + }, + { + puType: machinelearningv1.OUTPUT_TRANSFORMER, + result: machinelearningv1.Label_output_transformer, + }, + } + cases2 := []struct { + shadow bool + explainer *machinelearningv1.Explainer + traffic int32 + result string + }{ + { + shadow: false, + explainer: nil, + traffic: 0, + result: machinelearningv1.Label_default, + }, + { + shadow: false, + explainer: nil, + traffic: 50, + result: machinelearningv1.Label_default, + }, + { + shadow: false, + explainer: nil, + traffic: 75, + result: machinelearningv1.Label_default, + }, + { + shadow: true, + explainer: nil, + traffic: 0, + result: machinelearningv1.Label_shadow, + }, + { + shadow: false, + explainer: nil, + traffic: 49, + result: machinelearningv1.Label_canary, + }, + { + shadow: false, + explainer: &machinelearningv1.Explainer{}, + traffic: 0, + result: machinelearningv1.Label_explainer, + }, + } + svc := &corev1.Service{ + ObjectMeta: metav1.ObjectMeta{ + Labels: map[string]string{}, + }, + } + p := machinelearningv1.PredictorSpec{} + pu := &machinelearningv1.PredictiveUnit{} + t.Run("adds correct label to Service from Predictive Unit Type", func(t *testing.T) { + for _, c := range cases1 { + pu.Type = &c.puType + addLabelsToService(svc, pu, p) + g.Expect(svc.Labels[c.result]).To(Equal("true")) + } + }) + t.Run("adds correct label to Service for Default, Shadow, Canary & Explainer from Predictor Spec", func(t *testing.T) { + for _, c := range cases2 { + p.Shadow = c.shadow + p.Explainer = c.explainer + p.Traffic = c.traffic + addLabelsToService(svc, pu, p) + g.Expect(svc.Labels[c.result]).To(Equal("true")) + } + }) +} diff --git a/operator/controllers/seldondeployment_controller.go b/operator/controllers/seldondeployment_controller.go index 67bd5ff5ab..d18e96dbbb 100644 --- a/operator/controllers/seldondeployment_controller.go +++ b/operator/controllers/seldondeployment_controller.go @@ -20,6 +20,9 @@ import ( "bytes" "context" "fmt" + "strconv" + "strings" + types2 "github.com/gogo/protobuf/types" "github.com/seldonio/seldon-core/operator/constants" "github.com/seldonio/seldon-core/operator/utils" @@ -31,8 +34,6 @@ import ( "k8s.io/client-go/tools/record" "knative.dev/pkg/kmp" "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" - "strconv" - "strings" "github.com/go-logr/logr" ctrl "sigs.k8s.io/controller-runtime" @@ -681,11 +682,8 @@ func createContainerService(deploy *appsv1.Deployment, p machinelearningv1.Predi SessionAffinity: corev1.ServiceAffinityNone, }, } - - //Add labels for this service to deployment - deploy.ObjectMeta.Labels[containerServiceKey] = containerServiceValue - deploy.Spec.Selector.MatchLabels[containerServiceKey] = containerServiceValue - deploy.Spec.Template.ObjectMeta.Labels[containerServiceKey] = containerServiceValue + addLabelsToService(svc, pu, p) + addLabelsToDeployment(deploy, containerServiceKey, containerServiceValue) if existingPort == nil || con.Ports == nil { con.Ports = append(con.Ports, corev1.ContainerPort{Name: portType, ContainerPort: portNum, Protocol: corev1.ProtocolTCP}) @@ -754,7 +752,11 @@ func createDeploymentWithoutEngine(depName string, seldonId string, seldonPodSpe }, Template: corev1.PodTemplateSpec{ ObjectMeta: metav1.ObjectMeta{ - Labels: map[string]string{machinelearningv1.Label_seldon_id: seldonId, "app": depName, "fluentd": "true"}, + Labels: map[string]string{ + machinelearningv1.Label_seldon_id: seldonId, + machinelearningv1.Label_app: depName, + machinelearningv1.Label_fluentd: "true", + }, Annotations: mlDep.Spec.Annotations, }, }, @@ -770,7 +772,7 @@ func createDeploymentWithoutEngine(depName string, seldonId string, seldonPodSpe deploy.Spec.Template.Annotations["prometheus.io/scrape"] = "true" if p.Shadow == true { - deploy.Spec.Template.ObjectMeta.Labels["shadow"] = "true" + deploy.Spec.Template.ObjectMeta.Labels[machinelearningv1.Label_shadow] = "true" } if seldonPodSpec != nil {