diff --git a/api/cluster/controller.go b/api/cluster/controller.go index 7a838e520..e490f6929 100644 --- a/api/cluster/controller.go +++ b/api/cluster/controller.go @@ -288,12 +288,21 @@ func (c *controller) Deploy(ctx context.Context, modelService *models.Service) ( inferenceURL = vsCfg.getInferenceURL(vs) } - // Delete previous inference service + // Delete previous inference service and pdb if modelService.CurrentIsvcName != "" { if err := c.deleteInferenceService(ctx, modelService.CurrentIsvcName, modelService.Namespace); err != nil { log.Errorf("unable to delete prevision revision %s with error %v", modelService.CurrentIsvcName, err) return nil, errors.Wrapf(err, fmt.Sprintf("%v (%s)", ErrUnableToDeletePreviousInferenceService, modelService.CurrentIsvcName)) } + + unusedPdbs, err := c.getUnusedPodDisruptionBudgets(ctx, modelService) + if err != nil { + log.Warnf("unable to get model name %s, version %s unused pdb: %v", modelService.ModelName, modelService.ModelVersion, err) + } else { + if err := c.deletePodDisruptionBudgets(ctx, unusedPdbs); err != nil { + log.Warnf("unable to delete model name %s, version %s unused pdb: %v", modelService.ModelName, modelService.ModelVersion, err) + } + } } return &models.Service{ diff --git a/api/cluster/controller_test.go b/api/cluster/controller_test.go index d570ead6d..155da2ea3 100644 --- a/api/cluster/controller_test.go +++ b/api/cluster/controller_test.go @@ -353,7 +353,7 @@ func TestController_DeployInferenceService_NamespaceCreation(t *testing.T) { } func TestController_DeployInferenceService(t *testing.T) { - defaultMaxUnavailablePDB := 20 + minAvailablePercentage := 80 deployTimeout := 2 * tickDurationSecond * time.Second model := &models.Model{ @@ -729,8 +729,8 @@ func TestController_DeployInferenceService(t *testing.T) { DefaultModelResourceRequests: &config.ResourceRequests{}, DefaultTransformerResourceRequests: &config.ResourceRequests{}, PodDisruptionBudget: config.PodDisruptionBudgetConfig{ - Enabled: true, - MaxUnavailablePercentage: &defaultMaxUnavailablePDB, + Enabled: true, + MinAvailablePercentage: &minAvailablePercentage, }, StandardTransformer: config.StandardTransformerConfig{ ImageName: "ghcr.io/caraml-dev/merlin-transformer-test", @@ -758,6 +758,223 @@ func TestController_DeployInferenceService(t *testing.T) { } } +func TestController_DeployInferenceService_PodDisruptionBudgetsRemoval(t *testing.T) { + err := models.InitKubernetesLabeller("gojek.com/", "dev") + assert.Nil(t, err) + + minAvailablePercentage := 80 + + model := &models.Model{ + Name: "my-model", + } + project := mlp.Project{ + Name: "my-project", + } + version := &models.Version{ + ID: 1, + } + revisionID := models.ID(5) + + isvcName := models.CreateInferenceServiceName(model.Name, version.ID.String(), revisionID.String()) + statusReady := createServiceReadyStatus(isvcName, project.Name, baseUrl) + statusReady.Components = make(map[kservev1beta1.ComponentType]kservev1beta1.ComponentStatusSpec) + pdb := &policyv1.PodDisruptionBudget{} + vs := fakeVirtualService(model.Name, version.ID.String()) + + metadata := models.Metadata{ + App: "mymodel", + Component: models.ComponentModelVersion, + Stream: "mystream", + Team: "myteam", + } + modelSvc := &models.Service{ + Name: isvcName, + ModelName: model.Name, + ModelVersion: version.ID.String(), + RevisionID: revisionID, + Namespace: project.Name, + Metadata: metadata, + CurrentIsvcName: isvcName, + } + + defaultMatchLabel := map[string]string{ + "gojek.com/app": "mymodel", + "gojek.com/component": "model-version", + "gojek.com/environment": "dev", + "gojek.com/orchestrator": "merlin", + "gojek.com/stream": "mystream", + "gojek.com/team": "myteam", + "serving.kserve.io/inferenceservice": "mymodel-1-r4", + "model-version-id": "1", + } + + tests := []struct { + name string + modelService *models.Service + existingPdbs *policyv1.PodDisruptionBudgetList + createPdbResult *pdbReactor + deletedPdbResult *pdbReactor + wantPdbs []string + }{ + { + name: "success: remove pdbs", + modelService: modelSvc, + existingPdbs: &policyv1.PodDisruptionBudgetList{ + Items: []policyv1.PodDisruptionBudget{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: "my-model-1-r4-predictor-pdb", + Labels: defaultMatchLabel, + Namespace: modelSvc.Namespace, + }, + }, + { + ObjectMeta: metav1.ObjectMeta{ + Name: "my-model-1-r4-transformer-pdb", + Labels: defaultMatchLabel, + Namespace: modelSvc.Namespace, + }, + }, + }, + }, + createPdbResult: &pdbReactor{pdb, nil}, + deletedPdbResult: &pdbReactor{err: nil}, + wantPdbs: []string{ + "my-model-1-r4-predictor-pdb", + "my-model-1-r4-transformer-pdb", + }, + }, + { + name: "success: only remove pdb with same labels", + modelService: modelSvc, + existingPdbs: &policyv1.PodDisruptionBudgetList{ + Items: []policyv1.PodDisruptionBudget{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: "my-model-1-r4-predictor-pdb", + Labels: defaultMatchLabel, + Namespace: modelSvc.Namespace, + }, + }, + { + ObjectMeta: metav1.ObjectMeta{ + Name: "my-model-2-r4-predictor-pdb", + Labels: map[string]string{ + "gojek.com/app": "mymodel", + "gojek.com/component": "model-version", + "gojek.com/environment": "dev", + "gojek.com/orchestrator": "merlin", + "gojek.com/stream": "mystream", + "gojek.com/team": "myteam", + "component": "predictor", + "serving.kserve.io/inferenceservice": "mymodel-2-r4", + "model-version-id": "2", + }, + Namespace: modelSvc.Namespace, + }, + }, + }, + }, + createPdbResult: &pdbReactor{pdb, nil}, + deletedPdbResult: &pdbReactor{err: nil}, + wantPdbs: []string{ + "my-model-1-r4-predictor-pdb", + }, + }, + { + name: "success: no pdb found or to delete", + modelService: modelSvc, + existingPdbs: &policyv1.PodDisruptionBudgetList{ + Items: []policyv1.PodDisruptionBudget{}, + }, + createPdbResult: &pdbReactor{pdb, nil}, + deletedPdbResult: &pdbReactor{err: nil}, + wantPdbs: []string{}, + }, + { + name: "fail deleting pdb should not return error", + modelService: modelSvc, + existingPdbs: &policyv1.PodDisruptionBudgetList{ + Items: []policyv1.PodDisruptionBudget{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: "my-model-1-r4-predictor-pdb", + Labels: defaultMatchLabel, + Namespace: modelSvc.Namespace, + }, + }, + }, + }, + createPdbResult: &pdbReactor{pdb, nil}, + deletedPdbResult: &pdbReactor{err: ErrUnableToDeletePDB}, + wantPdbs: []string{}, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + knClient := knservingfake.NewSimpleClientset() + + kfClient := fakekserve.NewSimpleClientset().ServingV1beta1().(*fakekservev1beta1.FakeServingV1beta1) + isvcWatchReactor := newIsvcWatchReactor(&kservev1beta1.InferenceService{ + ObjectMeta: metav1.ObjectMeta{Name: isvcName, Namespace: project.Name}, + Status: statusReady, + }) + kfClient.WatchReactionChain = []ktesting.WatchReactor{isvcWatchReactor} + + kfClient.PrependReactor(getMethod, inferenceServiceResource, func(action ktesting.Action) (handled bool, ret runtime.Object, err error) { + return true, &kservev1beta1.InferenceService{Status: statusReady}, nil + }) + + v1Client := fake.NewSimpleClientset().CoreV1() + + policyV1Client := fake.NewSimpleClientset(tt.existingPdbs).PolicyV1().(*fakepolicyv1.FakePolicyV1) + policyV1Client.Fake.PrependReactor(patchMethod, pdbResource, func(action ktesting.Action) (handled bool, ret runtime.Object, err error) { + return true, tt.createPdbResult.pdb, tt.createPdbResult.err + }) + + deletedPdbNames := []string{} + policyV1Client.Fake.PrependReactor(deleteMethod, pdbResource, func(action ktesting.Action) (handled bool, ret runtime.Object, err error) { + deletedPdbNames = append(deletedPdbNames, action.(ktesting.DeleteAction).GetName()) + return true, nil, tt.deletedPdbResult.err + }) + + istioClient := fakeistio.NewSimpleClientset().NetworkingV1beta1().(*fakeistionetworking.FakeNetworkingV1beta1) + istioClient.PrependReactor(patchMethod, virtualServiceResource, func(action ktesting.Action) (handled bool, ret runtime.Object, err error) { + return true, vs, nil + }) + + deployConfig := config.DeploymentConfig{ + DeploymentTimeout: 2 * tickDurationSecond * time.Second, + NamespaceTimeout: 2 * tickDurationSecond * time.Second, + DefaultModelResourceRequests: &config.ResourceRequests{}, + DefaultTransformerResourceRequests: &config.ResourceRequests{}, + MaxAllowedReplica: 4, + PodDisruptionBudget: config.PodDisruptionBudgetConfig{ + Enabled: true, + MinAvailablePercentage: &minAvailablePercentage, + }, + StandardTransformer: config.StandardTransformerConfig{}, + UserContainerCPUDefaultLimit: userContainerCPUDefaultLimit, + UserContainerCPULimitRequestFactor: userContainerCPULimitRequestFactor, + UserContainerMemoryLimitRequestFactor: userContainerMemoryLimitRequestFactor, + } + + containerFetcher := NewContainerFetcher(v1Client, clusterMetadata) + templater := clusterresource.NewInferenceServiceTemplater(deployConfig) + + ctl, _ := newController(knClient.ServingV1(), kfClient, v1Client, nil, policyV1Client, istioClient, deployConfig, containerFetcher, templater) + iSvc, err := ctl.Deploy(context.Background(), tt.modelService) + + if tt.deletedPdbResult.err == nil { + assert.ElementsMatch(t, deletedPdbNames, tt.wantPdbs) + } + assert.NoError(t, err) + assert.NotNil(t, iSvc) + }) + } +} + func TestGetCurrentDeploymentScale(t *testing.T) { testNamespace := "test-namespace" var testDesiredReplicas int32 = 5 diff --git a/api/cluster/pdb.go b/api/cluster/pdb.go index ac595b996..58793d65f 100644 --- a/api/cluster/pdb.go +++ b/api/cluster/pdb.go @@ -5,6 +5,7 @@ import ( "encoding/json" "fmt" "math" + "strings" policyv1 "k8s.io/api/policy/v1" kerrors "k8s.io/apimachinery/pkg/api/errors" @@ -16,23 +17,30 @@ import ( "github.com/caraml-dev/merlin/models" ) +const ( + componentLabel = "component" + modelVersionIDLabel = "model-version-id" + kserveIsvcLabel = "serving.kserve.io/inferenceservice" +) + type PodDisruptionBudget struct { Name string Namespace string Labels map[string]string + Selectors map[string]string MaxUnavailablePercentage *int MinAvailablePercentage *int } func NewPodDisruptionBudget(modelService *models.Service, componentType string, pdbConfig config.PodDisruptionBudgetConfig) *PodDisruptionBudget { - labels := modelService.Metadata.ToLabel() - labels["component"] = componentType - labels["serving.kserve.io/inferenceservice"] = modelService.Name + labels := buildPDBSelector(modelService, componentType) + labels[modelVersionIDLabel] = modelService.ModelVersion return &PodDisruptionBudget{ - Name: fmt.Sprintf("%s-%s-%s-%s", modelService.ModelName, modelService.ModelVersion, componentType, models.PDBComponentType), + Name: buildPDBName(modelService, componentType), Namespace: modelService.Namespace, Labels: labels, + Selectors: buildPDBSelector(modelService, componentType), MaxUnavailablePercentage: pdbConfig.MaxUnavailablePercentage, MinAvailablePercentage: pdbConfig.MinAvailablePercentage, } @@ -55,7 +63,7 @@ func (cfg PodDisruptionBudget) BuildPDBSpec() (*policyv1.PodDisruptionBudget, er }, Spec: policyv1.PodDisruptionBudgetSpec{ Selector: &metav1.LabelSelector{ - MatchLabels: cfg.Labels, + MatchLabels: cfg.Selectors, }, }, } @@ -73,36 +81,34 @@ func (cfg PodDisruptionBudget) BuildPDBSpec() (*policyv1.PodDisruptionBudget, er return pdb, nil } +func buildPDBName(modelService *models.Service, componentType string) string { + return fmt.Sprintf("%s-%s-%s", modelService.Name, componentType, models.PDBComponentType) +} + +func buildPDBSelector(modelService *models.Service, componentType string) map[string]string { + labelSelectors := modelService.Metadata.ToLabel() + labelSelectors[componentLabel] = componentType + labelSelectors[kserveIsvcLabel] = modelService.Name + + return labelSelectors +} + func generatePDBSpecs(modelService *models.Service, pdbConfig config.PodDisruptionBudgetConfig) []*PodDisruptionBudget { pdbs := []*PodDisruptionBudget{} - // Only create PDB if: ceil(minReplica * minAvailablePercent) < minReplica - // If not, the replicas may be unable to be removed. - // Note that we only care about minReplica here because, for active replicas > minReplica, - // the condition will be satisfied if it was satisfied for the minReplica case. - - var minAvailablePercent float64 - if pdbConfig.MinAvailablePercentage != nil { - minAvailablePercent = float64(*pdbConfig.MinAvailablePercentage) / 100.0 - } else if pdbConfig.MaxUnavailablePercentage != nil { - minAvailablePercent = float64(100-*pdbConfig.MaxUnavailablePercentage) / 100.0 - } else { - // PDB config is not set properly, we can't apply it. + // PDB config is not set properly, we can't apply it. + if pdbConfig.MinAvailablePercentage == nil && pdbConfig.MaxUnavailablePercentage == nil { return pdbs } - if modelService.ResourceRequest != nil && - math.Ceil(float64(modelService.ResourceRequest.MinReplica)* - minAvailablePercent) < float64(modelService.ResourceRequest.MinReplica) { + if (modelService.ResourceRequest != nil) && + doesPDBAllowDisruption(pdbConfig, modelService.ResourceRequest.MinReplica) { predictorPdb := NewPodDisruptionBudget(modelService, models.PredictorComponentType, pdbConfig) pdbs = append(pdbs, predictorPdb) } - if modelService.Transformer != nil && - modelService.Transformer.Enabled && - modelService.Transformer.ResourceRequest != nil && - math.Ceil(float64(modelService.Transformer.ResourceRequest.MinReplica)* - minAvailablePercent) < float64(modelService.Transformer.ResourceRequest.MinReplica) { + if (modelService.Transformer != nil && modelService.Transformer.Enabled && modelService.Transformer.ResourceRequest != nil) && + doesPDBAllowDisruption(pdbConfig, modelService.Transformer.ResourceRequest.MinReplica) { transformerPdb := NewPodDisruptionBudget(modelService, models.TransformerComponentType, pdbConfig) pdbs = append(pdbs, transformerPdb) } @@ -110,6 +116,25 @@ func generatePDBSpecs(modelService *models.Service, pdbConfig config.PodDisrupti return pdbs } +// doesPDBAllowDisruption determine if pdb config & minReplica will allow +// any disruption, this to avoid the replicas may be unable to be removed. rule: +// ceil(minReplica * minAvailablePercent) < minReplica || maxUnavailablePercent > 0. +// Note that we only care about minReplica for minAvailablePercent because, for +// active replicas > minReplica, the condition will be satisfied if it was +// satisfied for the minReplica case. +func doesPDBAllowDisruption(pdbConfig config.PodDisruptionBudgetConfig, minReplica int) bool { + if pdbConfig.MinAvailablePercentage != nil && + math.Ceil(float64(minReplica)*(float64(*pdbConfig.MinAvailablePercentage)/100.0)) < float64(minReplica) { + return true + } + + if pdbConfig.MaxUnavailablePercentage != nil && *pdbConfig.MaxUnavailablePercentage > 0 { + return true + } + + return false +} + func (c *controller) deployPodDisruptionBudgets(ctx context.Context, pdbs []*PodDisruptionBudget) error { for _, pdb := range pdbs { if err := c.deployPodDisruptionBudget(ctx, pdb); err != nil { @@ -157,3 +182,32 @@ func (c *controller) deletePodDisruptionBudget(ctx context.Context, pdb *PodDisr } return nil } + +func (c *controller) getUnusedPodDisruptionBudgets(ctx context.Context, modelService *models.Service) ([]*PodDisruptionBudget, error) { + pdbs := []*PodDisruptionBudget{} + + labels := modelService.Metadata.ToLabel() + + labelSelectors := []string{} + for key, val := range labels { + labelSelectors = append(labelSelectors, fmt.Sprintf("%s=%s", key, val)) + } + labelSelectors = append(labelSelectors, fmt.Sprintf("%s=%s", modelVersionIDLabel, modelService.ModelVersion)) + labelSelectors = append(labelSelectors, fmt.Sprintf("%s!=%s", kserveIsvcLabel, modelService.Name)) + + list, err := c.policyClient.PodDisruptionBudgets(modelService.Namespace).List(ctx, metav1.ListOptions{ + LabelSelector: strings.Join(labelSelectors, ","), + }) + if err != nil { + return nil, err + } + + for _, item := range list.Items { + pdbs = append(pdbs, &PodDisruptionBudget{ + Name: item.Name, + Namespace: modelService.Namespace, + }) + } + + return pdbs, nil +} diff --git a/api/cluster/pdb_test.go b/api/cluster/pdb_test.go index 668433425..1ca496559 100644 --- a/api/cluster/pdb_test.go +++ b/api/cluster/pdb_test.go @@ -13,10 +13,70 @@ import ( "github.com/caraml-dev/merlin/models" ) +func Test_NewPodDisruptionBudget(t *testing.T) { + err := models.InitKubernetesLabeller("gojek.com/", "dev") + assert.Nil(t, err) + + twenty := 20 + + defaultMetadata := models.Metadata{ + App: "mymodel", + Component: models.ComponentModelVersion, + Stream: "mystream", + Team: "myteam", + } + modelService := &models.Service{ + Name: "model-1-r1", + ModelName: "model", + ModelVersion: "1", + RevisionID: 1, + Namespace: "pdb-test", + Metadata: defaultMetadata, + } + pdbConfig := config.PodDisruptionBudgetConfig{ + Enabled: true, + MinAvailablePercentage: &twenty, + } + componentType := models.PredictorComponentType + + t.Run("new pod disruption budget", func(t *testing.T) { + pdb := NewPodDisruptionBudget(modelService, componentType, pdbConfig) + assert.Equal(t, pdb, &PodDisruptionBudget{ + Name: "model-1-r1-predictor-pdb", + Namespace: "pdb-test", + Labels: map[string]string{ + "gojek.com/app": "mymodel", + "gojek.com/component": "model-version", + "gojek.com/environment": "dev", + "gojek.com/orchestrator": "merlin", + "gojek.com/stream": "mystream", + "gojek.com/team": "myteam", + "component": "predictor", + "model-version-id": "1", + "serving.kserve.io/inferenceservice": "model-1-r1", + }, + Selectors: map[string]string{ + "gojek.com/app": "mymodel", + "gojek.com/component": "model-version", + "gojek.com/environment": "dev", + "gojek.com/orchestrator": "merlin", + "gojek.com/stream": "mystream", + "gojek.com/team": "myteam", + "component": "predictor", + "serving.kserve.io/inferenceservice": "model-1-r1", + }, + MinAvailablePercentage: &twenty, + }) + }) +} func TestPodDisruptionBudget_BuildPDBSpec(t *testing.T) { defaultInt := 20 defaultIntOrString := intstr.FromString("20%") defaultLabels := map[string]string{ + "gojek.com/app": "sklearn-sample-s", + "model-version-id": "1", + } + defaultSelectors := map[string]string{ "gojek.com/app": "sklearn-sample-s", } @@ -24,6 +84,7 @@ func TestPodDisruptionBudget_BuildPDBSpec(t *testing.T) { Name string Namespace string Labels map[string]string + Selectors map[string]string MaxUnavailablePercentage *int MinAvailablePercentage *int Selector *metav1.LabelSelector @@ -40,6 +101,7 @@ func TestPodDisruptionBudget_BuildPDBSpec(t *testing.T) { Name: "sklearn-sample-s-1-model-pdb", Namespace: "pdb-test", Labels: defaultLabels, + Selectors: defaultSelectors, MaxUnavailablePercentage: nil, MinAvailablePercentage: &defaultInt, }, @@ -55,7 +117,7 @@ func TestPodDisruptionBudget_BuildPDBSpec(t *testing.T) { }, Spec: policyv1.PodDisruptionBudgetSpec{ Selector: &metav1.LabelSelector{ - MatchLabels: defaultLabels, + MatchLabels: defaultSelectors, }, MinAvailable: &defaultIntOrString, }, @@ -68,6 +130,7 @@ func TestPodDisruptionBudget_BuildPDBSpec(t *testing.T) { Name: "sklearn-sample-s-1-model-pdb", Namespace: "pdb-test", Labels: defaultLabels, + Selectors: defaultSelectors, MaxUnavailablePercentage: &defaultInt, MinAvailablePercentage: &defaultInt, }, @@ -83,7 +146,7 @@ func TestPodDisruptionBudget_BuildPDBSpec(t *testing.T) { }, Spec: policyv1.PodDisruptionBudgetSpec{ Selector: &metav1.LabelSelector{ - MatchLabels: defaultLabels, + MatchLabels: defaultSelectors, }, MinAvailable: &defaultIntOrString, }, @@ -96,6 +159,7 @@ func TestPodDisruptionBudget_BuildPDBSpec(t *testing.T) { Name: "sklearn-sample-s-1-model-pdb", Namespace: "pdb-test", Labels: map[string]string{}, + Selectors: map[string]string{}, MaxUnavailablePercentage: nil, MinAvailablePercentage: nil, }, @@ -109,6 +173,7 @@ func TestPodDisruptionBudget_BuildPDBSpec(t *testing.T) { Name: tt.fields.Name, Namespace: tt.fields.Namespace, Labels: tt.fields.Labels, + Selectors: tt.fields.Selectors, MaxUnavailablePercentage: tt.fields.MaxUnavailablePercentage, MinAvailablePercentage: tt.fields.MinAvailablePercentage, } @@ -128,7 +193,7 @@ func Test_generatePDBSpecs(t *testing.T) { err := models.InitKubernetesLabeller("gojek.com/", "dev") assert.Nil(t, err) - twenty, eighty := 20, 80 + ten, twenty := 10, 20 defaultMetadata := models.Metadata{ App: "mymodel", @@ -137,6 +202,38 @@ func Test_generatePDBSpecs(t *testing.T) { Team: "myteam", } + defaultTransformerSelectors := map[string]string{ + "gojek.com/app": "mymodel", + "gojek.com/component": "model-version", + "gojek.com/environment": "dev", + "gojek.com/orchestrator": "merlin", + "gojek.com/stream": "mystream", + "gojek.com/team": "myteam", + "component": "transformer", + "serving.kserve.io/inferenceservice": "mymodel-1", + } + + defaultPredictorSelectors := map[string]string{ + "gojek.com/app": "mymodel", + "gojek.com/component": "model-version", + "gojek.com/environment": "dev", + "gojek.com/orchestrator": "merlin", + "gojek.com/stream": "mystream", + "gojek.com/team": "myteam", + "component": "predictor", + "serving.kserve.io/inferenceservice": "mymodel-1", + } + + defaultTransformerLabels := map[string]string{"model-version-id": "1"} + for k, v := range defaultTransformerSelectors { + defaultTransformerLabels[k] = v + } + + defaultPredictorLabels := map[string]string{"model-version-id": "1"} + for k, v := range defaultPredictorSelectors { + defaultPredictorLabels[k] = v + } + tests := map[string]struct { modelService *models.Service pdbConfig config.PodDisruptionBudgetConfig @@ -176,18 +273,10 @@ func Test_generatePDBSpecs(t *testing.T) { }, expected: []*PodDisruptionBudget{ { - Name: "mymodel-1-transformer-pdb", - Namespace: "mynamespace", - Labels: map[string]string{ - "gojek.com/app": "mymodel", - "gojek.com/component": "model-version", - "gojek.com/environment": "dev", - "gojek.com/orchestrator": "merlin", - "gojek.com/stream": "mystream", - "gojek.com/team": "myteam", - "component": "transformer", - "serving.kserve.io/inferenceservice": "mymodel-1", - }, + Name: "mymodel-1-transformer-pdb", + Namespace: "mynamespace", + Labels: defaultTransformerLabels, + Selectors: defaultTransformerSelectors, MinAvailablePercentage: &twenty, }, }, @@ -215,18 +304,10 @@ func Test_generatePDBSpecs(t *testing.T) { }, expected: []*PodDisruptionBudget{ { - Name: "mymodel-1-predictor-pdb", - Namespace: "mynamespace", - Labels: map[string]string{ - "gojek.com/app": "mymodel", - "gojek.com/component": "model-version", - "gojek.com/environment": "dev", - "gojek.com/orchestrator": "merlin", - "gojek.com/stream": "mystream", - "gojek.com/team": "myteam", - "component": "predictor", - "serving.kserve.io/inferenceservice": "mymodel-1", - }, + Name: "mymodel-1-predictor-pdb", + Namespace: "mynamespace", + Labels: defaultPredictorLabels, + Selectors: defaultPredictorSelectors, MinAvailablePercentage: &twenty, }, }, @@ -254,33 +335,17 @@ func Test_generatePDBSpecs(t *testing.T) { }, expected: []*PodDisruptionBudget{ { - Name: "mymodel-1-predictor-pdb", - Namespace: "mynamespace", - Labels: map[string]string{ - "gojek.com/app": "mymodel", - "gojek.com/component": "model-version", - "gojek.com/environment": "dev", - "gojek.com/orchestrator": "merlin", - "gojek.com/stream": "mystream", - "gojek.com/team": "myteam", - "component": "predictor", - "serving.kserve.io/inferenceservice": "mymodel-1", - }, + Name: "mymodel-1-predictor-pdb", + Namespace: "mynamespace", + Labels: defaultPredictorLabels, + Selectors: defaultPredictorSelectors, MinAvailablePercentage: &twenty, }, { - Name: "mymodel-1-transformer-pdb", - Namespace: "mynamespace", - Labels: map[string]string{ - "gojek.com/app": "mymodel", - "gojek.com/component": "model-version", - "gojek.com/environment": "dev", - "gojek.com/orchestrator": "merlin", - "gojek.com/stream": "mystream", - "gojek.com/team": "myteam", - "component": "transformer", - "serving.kserve.io/inferenceservice": "mymodel-1", - }, + Name: "mymodel-1-transformer-pdb", + Namespace: "mynamespace", + Labels: defaultTransformerLabels, + Selectors: defaultTransformerSelectors, MinAvailablePercentage: &twenty, }, }, @@ -304,38 +369,22 @@ func Test_generatePDBSpecs(t *testing.T) { }, pdbConfig: config.PodDisruptionBudgetConfig{ Enabled: true, - MaxUnavailablePercentage: &eighty, + MaxUnavailablePercentage: &ten, }, expected: []*PodDisruptionBudget{ { - Name: "mymodel-1-predictor-pdb", - Namespace: "mynamespace", - Labels: map[string]string{ - "gojek.com/app": "mymodel", - "gojek.com/component": "model-version", - "gojek.com/environment": "dev", - "gojek.com/orchestrator": "merlin", - "gojek.com/stream": "mystream", - "gojek.com/team": "myteam", - "component": "predictor", - "serving.kserve.io/inferenceservice": "mymodel-1", - }, - MaxUnavailablePercentage: &eighty, + Name: "mymodel-1-predictor-pdb", + Namespace: "mynamespace", + Labels: defaultPredictorLabels, + Selectors: defaultPredictorSelectors, + MaxUnavailablePercentage: &ten, }, { - Name: "mymodel-1-transformer-pdb", - Namespace: "mynamespace", - Labels: map[string]string{ - "gojek.com/app": "mymodel", - "gojek.com/component": "model-version", - "gojek.com/environment": "dev", - "gojek.com/orchestrator": "merlin", - "gojek.com/stream": "mystream", - "gojek.com/team": "myteam", - "component": "transformer", - "serving.kserve.io/inferenceservice": "mymodel-1", - }, - MaxUnavailablePercentage: &eighty, + Name: "mymodel-1-transformer-pdb", + Namespace: "mynamespace", + Labels: defaultTransformerLabels, + Selectors: defaultTransformerSelectors, + MaxUnavailablePercentage: &ten, }, }, },