github.com/kubewharf/katalyst-core@v0.5.3/pkg/controller/spd/spd_test.go (about) 1 /* 2 Copyright 2022 The Katalyst Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package spd 18 19 import ( 20 "context" 21 "encoding/json" 22 "testing" 23 "time" 24 25 "github.com/stretchr/testify/assert" 26 appsv1 "k8s.io/api/apps/v1" 27 v1 "k8s.io/api/core/v1" 28 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 29 "k8s.io/apimachinery/pkg/runtime" 30 "k8s.io/apimachinery/pkg/runtime/schema" 31 "k8s.io/apimachinery/pkg/types" 32 "k8s.io/client-go/tools/cache" 33 "k8s.io/utils/pointer" 34 35 apis "github.com/kubewharf/katalyst-api/pkg/apis/autoscaling/v1alpha1" 36 apiworkload "github.com/kubewharf/katalyst-api/pkg/apis/workload/v1alpha1" 37 apiconsts "github.com/kubewharf/katalyst-api/pkg/consts" 38 katalystbase "github.com/kubewharf/katalyst-core/cmd/base" 39 "github.com/kubewharf/katalyst-core/pkg/config/controller" 40 "github.com/kubewharf/katalyst-core/pkg/config/generic" 41 "github.com/kubewharf/katalyst-core/pkg/consts" 42 indicator_plugin "github.com/kubewharf/katalyst-core/pkg/controller/spd/indicator-plugin" 43 "github.com/kubewharf/katalyst-core/pkg/util/native" 44 ) 45 46 var ( 47 stsGVK = schema.GroupVersionKind{Group: "apps", Version: "v1", Kind: "StatefulSet"} 48 stsGVR = schema.GroupVersionResource{Group: "apps", Version: "v1", Resource: "statefulsets"} 49 ) 50 51 func TestSPDController_Run(t *testing.T) { 52 t.Parallel() 53 54 type fields struct { 55 pod *v1.Pod 56 workload *appsv1.StatefulSet 57 spd *apiworkload.ServiceProfileDescriptor 58 } 59 tests := []struct { 60 name string 61 fields fields 62 wantWorkload *appsv1.StatefulSet 63 wantSPD *apiworkload.ServiceProfileDescriptor 64 }{ 65 { 66 name: "delete unwanted spd", 67 fields: fields{ 68 pod: &v1.Pod{ 69 ObjectMeta: metav1.ObjectMeta{ 70 Name: "pod1", 71 Namespace: "default", 72 OwnerReferences: []metav1.OwnerReference{ 73 { 74 APIVersion: "apps/v1", 75 Kind: "StatefulSet", 76 Name: "sts1", 77 }, 78 }, 79 Annotations: map[string]string{ 80 apiconsts.PodAnnotationSPDNameKey: "spd1", 81 }, 82 Labels: map[string]string{ 83 "workload": "sts1", 84 }, 85 }, 86 }, 87 workload: &appsv1.StatefulSet{ 88 TypeMeta: metav1.TypeMeta{ 89 Kind: "StatefulSet", 90 APIVersion: "apps/v1", 91 }, 92 ObjectMeta: metav1.ObjectMeta{ 93 Name: "sts1", 94 Namespace: "default", 95 Annotations: map[string]string{}, 96 }, 97 Spec: appsv1.StatefulSetSpec{ 98 Selector: &metav1.LabelSelector{ 99 MatchLabels: map[string]string{ 100 "workload": "sts1", 101 }, 102 }, 103 }, 104 }, 105 spd: &apiworkload.ServiceProfileDescriptor{ 106 ObjectMeta: metav1.ObjectMeta{ 107 Namespace: "default", 108 Name: "sts1", 109 }, 110 Spec: apiworkload.ServiceProfileDescriptorSpec{ 111 TargetRef: apis.CrossVersionObjectReference{ 112 Kind: stsGVK.Kind, 113 Name: "sts1", 114 APIVersion: stsGVK.GroupVersion().String(), 115 }, 116 }, 117 Status: apiworkload.ServiceProfileDescriptorStatus{}, 118 }, 119 }, 120 wantWorkload: &appsv1.StatefulSet{ 121 TypeMeta: metav1.TypeMeta{ 122 Kind: "StatefulSet", 123 APIVersion: "apps/v1", 124 }, 125 ObjectMeta: metav1.ObjectMeta{ 126 Name: "sts1", 127 Namespace: "default", 128 }, 129 Spec: appsv1.StatefulSetSpec{ 130 Selector: &metav1.LabelSelector{ 131 MatchLabels: map[string]string{ 132 "workload": "sts1", 133 }, 134 }, 135 }, 136 }, 137 wantSPD: nil, 138 }, 139 { 140 name: "auto create spd", 141 fields: fields{ 142 workload: &appsv1.StatefulSet{ 143 TypeMeta: metav1.TypeMeta{ 144 Kind: "StatefulSet", 145 APIVersion: "apps/v1", 146 }, 147 ObjectMeta: metav1.ObjectMeta{ 148 Name: "sts1", 149 Namespace: "default", 150 Annotations: map[string]string{ 151 apiconsts.WorkloadAnnotationSPDEnableKey: apiconsts.WorkloadAnnotationSPDEnabled, 152 }, 153 }, 154 Spec: appsv1.StatefulSetSpec{ 155 Selector: &metav1.LabelSelector{ 156 MatchLabels: map[string]string{ 157 "workload": "sts1", 158 }, 159 }, 160 Template: v1.PodTemplateSpec{ 161 ObjectMeta: metav1.ObjectMeta{ 162 Annotations: map[string]string{ 163 "katalyst.kubewharf.io/qos_level": "dedicated_cores", 164 }, 165 }, 166 Spec: v1.PodSpec{}, 167 }, 168 }, 169 }, 170 spd: nil, 171 }, 172 wantWorkload: &appsv1.StatefulSet{ 173 TypeMeta: metav1.TypeMeta{ 174 Kind: "StatefulSet", 175 APIVersion: "apps/v1", 176 }, 177 ObjectMeta: metav1.ObjectMeta{ 178 Name: "sts1", 179 Namespace: "default", 180 Annotations: map[string]string{ 181 apiconsts.WorkloadAnnotationSPDEnableKey: apiconsts.WorkloadAnnotationSPDEnabled, 182 }, 183 }, 184 Spec: appsv1.StatefulSetSpec{ 185 Selector: &metav1.LabelSelector{ 186 MatchLabels: map[string]string{ 187 "workload": "sts1", 188 }, 189 }, 190 Template: v1.PodTemplateSpec{ 191 ObjectMeta: metav1.ObjectMeta{ 192 Annotations: map[string]string{ 193 "katalyst.kubewharf.io/qos_level": "dedicated_cores", 194 }, 195 }, 196 Spec: v1.PodSpec{}, 197 }, 198 }, 199 }, 200 wantSPD: &apiworkload.ServiceProfileDescriptor{ 201 ObjectMeta: metav1.ObjectMeta{ 202 Namespace: "default", 203 Name: "sts1", 204 Annotations: map[string]string{ 205 consts.ServiceProfileDescriptorAnnotationKeyConfigHash: "51131be1b092", 206 }, 207 OwnerReferences: []metav1.OwnerReference{ 208 { 209 APIVersion: "apps/v1", 210 Kind: "StatefulSet", 211 Name: "sts1", 212 }, 213 }, 214 }, 215 Spec: apiworkload.ServiceProfileDescriptorSpec{ 216 TargetRef: apis.CrossVersionObjectReference{ 217 Kind: stsGVK.Kind, 218 Name: "sts1", 219 APIVersion: stsGVK.GroupVersion().String(), 220 }, 221 BaselinePercent: pointer.Int32(100), 222 }, 223 Status: apiworkload.ServiceProfileDescriptorStatus{ 224 AggMetrics: []apiworkload.AggPodMetrics{}, 225 }, 226 }, 227 }, 228 { 229 name: "auto create spd(dedicated_cores)", 230 fields: fields{ 231 workload: &appsv1.StatefulSet{ 232 TypeMeta: metav1.TypeMeta{ 233 Kind: "StatefulSet", 234 APIVersion: "apps/v1", 235 }, 236 ObjectMeta: metav1.ObjectMeta{ 237 Name: "sts1", 238 Namespace: "default", 239 Annotations: map[string]string{ 240 apiconsts.WorkloadAnnotationSPDEnableKey: apiconsts.WorkloadAnnotationSPDEnabled, 241 }, 242 }, 243 Spec: appsv1.StatefulSetSpec{ 244 Selector: &metav1.LabelSelector{ 245 MatchLabels: map[string]string{ 246 "workload": "sts1", 247 }, 248 }, 249 Template: v1.PodTemplateSpec{ 250 ObjectMeta: metav1.ObjectMeta{ 251 Annotations: map[string]string{"katalyst.kubewharf.io/qos_level": "dedicated_cores"}, 252 }, 253 Spec: v1.PodSpec{}, 254 }, 255 }, 256 }, 257 spd: nil, 258 }, 259 wantWorkload: &appsv1.StatefulSet{ 260 TypeMeta: metav1.TypeMeta{ 261 Kind: "StatefulSet", 262 APIVersion: "apps/v1", 263 }, 264 ObjectMeta: metav1.ObjectMeta{ 265 Name: "sts1", 266 Namespace: "default", 267 Annotations: map[string]string{ 268 apiconsts.WorkloadAnnotationSPDEnableKey: apiconsts.WorkloadAnnotationSPDEnabled, 269 }, 270 }, 271 Spec: appsv1.StatefulSetSpec{ 272 Selector: &metav1.LabelSelector{ 273 MatchLabels: map[string]string{ 274 "workload": "sts1", 275 }, 276 }, 277 Template: v1.PodTemplateSpec{ 278 ObjectMeta: metav1.ObjectMeta{ 279 Annotations: map[string]string{"katalyst.kubewharf.io/qos_level": "dedicated_cores"}, 280 }, 281 Spec: v1.PodSpec{}, 282 }, 283 }, 284 }, 285 wantSPD: &apiworkload.ServiceProfileDescriptor{ 286 ObjectMeta: metav1.ObjectMeta{ 287 Namespace: "default", 288 Name: "sts1", 289 Annotations: map[string]string{ 290 consts.ServiceProfileDescriptorAnnotationKeyConfigHash: "51131be1b092", 291 }, 292 OwnerReferences: []metav1.OwnerReference{ 293 { 294 APIVersion: "apps/v1", 295 Kind: "StatefulSet", 296 Name: "sts1", 297 }, 298 }, 299 }, 300 Spec: apiworkload.ServiceProfileDescriptorSpec{ 301 TargetRef: apis.CrossVersionObjectReference{ 302 Kind: stsGVK.Kind, 303 Name: "sts1", 304 APIVersion: stsGVK.GroupVersion().String(), 305 }, 306 BaselinePercent: pointer.Int32(100), 307 }, 308 Status: apiworkload.ServiceProfileDescriptorStatus{ 309 AggMetrics: []apiworkload.AggPodMetrics{}, 310 }, 311 }, 312 }, 313 { 314 name: "auto create spd(shared_cores)", 315 fields: fields{ 316 workload: &appsv1.StatefulSet{ 317 TypeMeta: metav1.TypeMeta{ 318 Kind: "StatefulSet", 319 APIVersion: "apps/v1", 320 }, 321 ObjectMeta: metav1.ObjectMeta{ 322 Name: "sts1", 323 Namespace: "default", 324 Annotations: map[string]string{ 325 apiconsts.WorkloadAnnotationSPDEnableKey: apiconsts.WorkloadAnnotationSPDEnabled, 326 }, 327 }, 328 Spec: appsv1.StatefulSetSpec{ 329 Selector: &metav1.LabelSelector{ 330 MatchLabels: map[string]string{ 331 "workload": "sts1", 332 }, 333 }, 334 Template: v1.PodTemplateSpec{ 335 ObjectMeta: metav1.ObjectMeta{ 336 Annotations: map[string]string{"katalyst.kubewharf.io/qos_level": "shared_cores"}, 337 }, 338 Spec: v1.PodSpec{}, 339 }, 340 }, 341 }, 342 spd: nil, 343 }, 344 wantWorkload: &appsv1.StatefulSet{ 345 TypeMeta: metav1.TypeMeta{ 346 Kind: "StatefulSet", 347 APIVersion: "apps/v1", 348 }, 349 ObjectMeta: metav1.ObjectMeta{ 350 Name: "sts1", 351 Namespace: "default", 352 Annotations: map[string]string{ 353 apiconsts.WorkloadAnnotationSPDEnableKey: apiconsts.WorkloadAnnotationSPDEnabled, 354 }, 355 }, 356 Spec: appsv1.StatefulSetSpec{ 357 Selector: &metav1.LabelSelector{ 358 MatchLabels: map[string]string{ 359 "workload": "sts1", 360 }, 361 }, 362 Template: v1.PodTemplateSpec{ 363 ObjectMeta: metav1.ObjectMeta{ 364 Annotations: map[string]string{"katalyst.kubewharf.io/qos_level": "shared_cores"}, 365 }, 366 Spec: v1.PodSpec{}, 367 }, 368 }, 369 }, 370 wantSPD: &apiworkload.ServiceProfileDescriptor{ 371 ObjectMeta: metav1.ObjectMeta{ 372 Namespace: "default", 373 Name: "sts1", 374 Annotations: map[string]string{ 375 consts.ServiceProfileDescriptorAnnotationKeyConfigHash: "a62e4c90e3ed", 376 }, 377 OwnerReferences: []metav1.OwnerReference{ 378 { 379 APIVersion: "apps/v1", 380 Kind: "StatefulSet", 381 Name: "sts1", 382 }, 383 }, 384 }, 385 Spec: apiworkload.ServiceProfileDescriptorSpec{ 386 TargetRef: apis.CrossVersionObjectReference{ 387 Kind: stsGVK.Kind, 388 Name: "sts1", 389 APIVersion: stsGVK.GroupVersion().String(), 390 }, 391 BaselinePercent: pointer.Int32(0), 392 }, 393 Status: apiworkload.ServiceProfileDescriptorStatus{ 394 AggMetrics: []apiworkload.AggPodMetrics{}, 395 }, 396 }, 397 }, 398 } 399 for _, tt := range tests { 400 tt := tt 401 t.Run(tt.name, func(t *testing.T) { 402 t.Parallel() 403 spdConfig := &controller.SPDConfig{ 404 SPDWorkloadGVResources: []string{"statefulsets.v1.apps"}, 405 BaselinePercent: map[string]int64{ 406 apiconsts.PodAnnotationQoSLevelSharedCores: 0, 407 apiconsts.PodAnnotationQoSLevelDedicatedCores: 100, 408 }, 409 } 410 genericConfig := &generic.GenericConfiguration{} 411 controllerConf := &controller.GenericControllerConfiguration{ 412 DynamicGVResources: []string{"statefulsets.v1.apps"}, 413 } 414 415 ctx := context.TODO() 416 controlCtx, err := katalystbase.GenerateFakeGenericContext([]runtime.Object{tt.fields.pod}, 417 []runtime.Object{tt.fields.spd}, []runtime.Object{tt.fields.workload}) 418 assert.NoError(t, err) 419 420 spdController, err := NewSPDController(ctx, controlCtx, genericConfig, controllerConf, spdConfig, generic.NewQoSConfiguration(), struct{}{}) 421 assert.NoError(t, err) 422 423 controlCtx.StartInformer(ctx) 424 go spdController.Run() 425 synced := cache.WaitForCacheSync(ctx.Done(), spdController.syncedFunc...) 426 assert.True(t, synced) 427 time.Sleep(1 * time.Second) 428 429 targetSPD := tt.fields.spd 430 if targetSPD == nil { 431 targetSPD = tt.wantSPD 432 } 433 newSPD, _ := controlCtx.Client.InternalClient.WorkloadV1alpha1(). 434 ServiceProfileDescriptors(targetSPD.Namespace).Get(ctx, targetSPD.Name, metav1.GetOptions{}) 435 assert.Equal(t, tt.wantSPD, newSPD) 436 437 newObject, _ := controlCtx.Client.DynamicClient.Resource(stsGVR). 438 Namespace(tt.fields.workload.GetNamespace()).Get(ctx, tt.fields.workload.GetName(), metav1.GetOptions{}) 439 440 newWorkload := &appsv1.StatefulSet{} 441 err = runtime.DefaultUnstructuredConverter.FromUnstructured(newObject.UnstructuredContent(), newWorkload) 442 assert.NoError(t, err) 443 assert.Equal(t, tt.wantWorkload, newWorkload) 444 }) 445 } 446 } 447 448 func TestPodIndexerDuplicate(t *testing.T) { 449 t.Parallel() 450 451 spdConf := controller.NewSPDConfig() 452 genericConfig := &generic.GenericConfiguration{} 453 controllerConf := &controller.GenericControllerConfiguration{} 454 controlCtx, err := katalystbase.GenerateFakeGenericContext(nil, nil, nil) 455 assert.NoError(t, err) 456 457 spdConf.SPDPodLabelIndexerKeys = []string{"test-1"} 458 459 _, err = NewSPDController(context.TODO(), controlCtx, genericConfig, controllerConf, spdConf, nil, struct{}{}) 460 assert.NoError(t, err) 461 462 _, err = NewSPDController(context.TODO(), controlCtx, genericConfig, controllerConf, spdConf, nil, struct{}{}) 463 assert.NoError(t, err) 464 465 indexers := controlCtx.KubeInformerFactory.Core().V1().Pods().Informer().GetIndexer().GetIndexers() 466 assert.Equal(t, 2, len(indexers)) 467 _, exist := indexers["test-1"] 468 assert.Equal(t, true, exist) 469 } 470 471 func TestIndicatorUpdater(t *testing.T) { 472 t.Parallel() 473 474 var current float32 = 8.3 475 var value float32 = 23.1 476 477 workload := &appsv1.StatefulSet{ 478 TypeMeta: metav1.TypeMeta{ 479 Kind: "StatefulSet", 480 APIVersion: "apps/v1", 481 }, 482 ObjectMeta: metav1.ObjectMeta{ 483 Name: "sts1", 484 Namespace: "default", 485 Annotations: map[string]string{ 486 apiconsts.WorkloadAnnotationSPDEnableKey: apiconsts.WorkloadAnnotationSPDEnabled, 487 }, 488 }, 489 Spec: appsv1.StatefulSetSpec{ 490 Selector: &metav1.LabelSelector{ 491 MatchLabels: map[string]string{ 492 "workload": "sts1", 493 }, 494 }, 495 }, 496 } 497 498 spd := &apiworkload.ServiceProfileDescriptor{ 499 ObjectMeta: metav1.ObjectMeta{ 500 Namespace: "default", 501 Name: "spd1", 502 ResourceVersion: "1", 503 }, 504 Spec: apiworkload.ServiceProfileDescriptorSpec{ 505 TargetRef: apis.CrossVersionObjectReference{ 506 Kind: stsGVK.Kind, 507 Name: "sts1", 508 APIVersion: stsGVK.GroupVersion().String(), 509 }, 510 BusinessIndicator: []apiworkload.ServiceBusinessIndicatorSpec{ 511 { 512 Name: "none-exist-b", 513 Indicators: []apiworkload.Indicator{ 514 { 515 IndicatorLevel: apiworkload.IndicatorLevelLowerBound, 516 Value: 10.2, 517 }, 518 }, 519 }, 520 }, 521 SystemIndicator: []apiworkload.ServiceSystemIndicatorSpec{ 522 { 523 Name: "none-exist-s", 524 Indicators: []apiworkload.Indicator{ 525 { 526 IndicatorLevel: apiworkload.IndicatorLevelUpperBound, 527 Value: 10.5, 528 }, 529 }, 530 }, 531 { 532 Name: "system-3", 533 Indicators: []apiworkload.Indicator{ 534 { 535 IndicatorLevel: apiworkload.IndicatorLevelUpperBound, 536 Value: 4.5, 537 }, 538 }, 539 }, 540 }, 541 }, 542 Status: apiworkload.ServiceProfileDescriptorStatus{ 543 BusinessStatus: []apiworkload.ServiceBusinessIndicatorStatus{ 544 { 545 Name: "none-exist-status", 546 Current: ¤t, 547 }, 548 { 549 Name: "system-2", 550 Current: ¤t, 551 }, 552 }, 553 }, 554 } 555 556 expectedSpd := &apiworkload.ServiceProfileDescriptor{ 557 ObjectMeta: metav1.ObjectMeta{ 558 Namespace: "default", 559 Name: "spd1", 560 }, 561 Spec: apiworkload.ServiceProfileDescriptorSpec{ 562 TargetRef: apis.CrossVersionObjectReference{ 563 Kind: stsGVK.Kind, 564 Name: "sts1", 565 APIVersion: stsGVK.GroupVersion().String(), 566 }, 567 BaselinePercent: pointer.Int32(20), 568 ExtendedIndicator: []apiworkload.ServiceExtendedIndicatorSpec{ 569 { 570 Name: "TestExtended", 571 Indicators: runtime.RawExtension{ 572 Raw: func() []byte { 573 data, _ := json.Marshal(&apiworkload.TestExtendedIndicators{ 574 Indicators: &apiworkload.TestIndicators{}, 575 }) 576 return data 577 }(), 578 }, 579 }, 580 }, 581 BusinessIndicator: []apiworkload.ServiceBusinessIndicatorSpec{ 582 { 583 Name: "business-1", 584 Indicators: []apiworkload.Indicator{ 585 { 586 IndicatorLevel: apiworkload.IndicatorLevelLowerBound, 587 Value: 10.2, 588 }, 589 }, 590 }, 591 { 592 Name: "business-2", 593 Indicators: []apiworkload.Indicator{ 594 { 595 IndicatorLevel: apiworkload.IndicatorLevelUpperBound, 596 Value: 18.3, 597 }, 598 }, 599 }, 600 { 601 Name: "business-3", 602 Indicators: []apiworkload.Indicator{ 603 { 604 IndicatorLevel: apiworkload.IndicatorLevelUpperBound, 605 Value: 16.8, 606 }, 607 }, 608 }, 609 }, 610 SystemIndicator: []apiworkload.ServiceSystemIndicatorSpec{ 611 { 612 Name: "system-3", 613 Indicators: []apiworkload.Indicator{ 614 { 615 IndicatorLevel: apiworkload.IndicatorLevelUpperBound, 616 Value: 4.5, 617 }, 618 }, 619 }, 620 { 621 Name: "system-1", 622 Indicators: []apiworkload.Indicator{ 623 { 624 IndicatorLevel: apiworkload.IndicatorLevelLowerBound, 625 Value: 10.5, 626 }, 627 { 628 IndicatorLevel: apiworkload.IndicatorLevelUpperBound, 629 Value: 10.5, 630 }, 631 }, 632 }, 633 { 634 Name: "system-2", 635 Indicators: []apiworkload.Indicator{ 636 { 637 IndicatorLevel: apiworkload.IndicatorLevelUpperBound, 638 Value: 10.5, 639 }, 640 }, 641 }, 642 }, 643 }, 644 Status: apiworkload.ServiceProfileDescriptorStatus{ 645 BusinessStatus: []apiworkload.ServiceBusinessIndicatorStatus{ 646 { 647 Name: "system-2", 648 Current: &value, 649 }, 650 }, 651 }, 652 } 653 654 nn := types.NamespacedName{ 655 Namespace: "default", 656 Name: "spd1", 657 } 658 659 d1 := indicator_plugin.DummyIndicatorPlugin{ 660 ExtendedSpecNames: []string{ 661 "TestExtended", 662 }, 663 SystemSpecNames: []apiworkload.ServiceSystemIndicatorName{ 664 "system-1", 665 }, 666 BusinessSpecNames: []apiworkload.ServiceBusinessIndicatorName{ 667 "business-1", 668 "business-2", 669 }, 670 BusinessStatusNames: []apiworkload.ServiceBusinessIndicatorName{ 671 "business-2", 672 }, 673 } 674 d2 := indicator_plugin.DummyIndicatorPlugin{ 675 SystemSpecNames: []apiworkload.ServiceSystemIndicatorName{ 676 "system-2", 677 "system-3", 678 }, 679 BusinessSpecNames: []apiworkload.ServiceBusinessIndicatorName{ 680 "business-3", 681 }, 682 BusinessStatusNames: []apiworkload.ServiceBusinessIndicatorName{ 683 "business-3", 684 }, 685 } 686 687 indicator_plugin.RegisterPluginInitializer("d1", func(_ context.Context, _ *controller.SPDConfig, 688 _ interface{}, _ map[schema.GroupVersionResource]native.DynamicInformer, _ *katalystbase.GenericContext, 689 _ indicator_plugin.IndicatorUpdater, 690 ) (indicator_plugin.IndicatorPlugin, error) { 691 return d1, nil 692 }) 693 indicator_plugin.RegisterPluginInitializer("d2", func(_ context.Context, _ *controller.SPDConfig, 694 _ interface{}, _ map[schema.GroupVersionResource]native.DynamicInformer, _ *katalystbase.GenericContext, 695 _ indicator_plugin.IndicatorUpdater, 696 ) (indicator_plugin.IndicatorPlugin, error) { 697 return d2, nil 698 }) 699 700 spdConfig := &controller.SPDConfig{ 701 SPDWorkloadGVResources: []string{"statefulsets.v1.apps"}, 702 IndicatorPlugins: []string{"d1", "d2"}, 703 } 704 genericConfig := &generic.GenericConfiguration{} 705 controllerConf := &controller.GenericControllerConfiguration{ 706 DynamicGVResources: []string{"statefulsets.v1.apps"}, 707 } 708 709 ctx := context.TODO() 710 controlCtx, err := katalystbase.GenerateFakeGenericContext([]runtime.Object{}, 711 []runtime.Object{spd}, []runtime.Object{workload}) 712 assert.NoError(t, err) 713 714 sc, err := NewSPDController(ctx, controlCtx, genericConfig, controllerConf, spdConfig, nil, struct{}{}) 715 assert.NoError(t, err) 716 717 controlCtx.StartInformer(ctx) 718 go sc.Run() 719 synced := cache.WaitForCacheSync(ctx.Done(), sc.syncedFunc...) 720 assert.True(t, synced) 721 722 sc.indicatorManager.UpdateExtendedIndicatorSpec(nn, []apiworkload.ServiceExtendedIndicatorSpec{ 723 { 724 Name: "TestExtended", 725 Indicators: runtime.RawExtension{ 726 Object: &apiworkload.TestExtendedIndicators{ 727 Indicators: &apiworkload.TestIndicators{}, 728 }, 729 }, 730 }, 731 }) 732 733 sc.indicatorManager.UpdateBusinessIndicatorSpec(nn, []apiworkload.ServiceBusinessIndicatorSpec{ 734 { 735 Name: "business-1", 736 Indicators: []apiworkload.Indicator{ 737 { 738 IndicatorLevel: apiworkload.IndicatorLevelLowerBound, 739 Value: 10.2, 740 }, 741 }, 742 }, 743 { 744 Name: "business-2", 745 Indicators: []apiworkload.Indicator{ 746 { 747 IndicatorLevel: apiworkload.IndicatorLevelUpperBound, 748 Value: 18.3, 749 }, 750 }, 751 }, 752 }) 753 sc.indicatorManager.UpdateBusinessIndicatorSpec(nn, []apiworkload.ServiceBusinessIndicatorSpec{ 754 { 755 Name: "business-3", 756 Indicators: []apiworkload.Indicator{ 757 { 758 IndicatorLevel: apiworkload.IndicatorLevelUpperBound, 759 Value: 13.3, 760 }, 761 }, 762 }, 763 { 764 Name: "business-3", 765 Indicators: []apiworkload.Indicator{ 766 { 767 IndicatorLevel: apiworkload.IndicatorLevelUpperBound, 768 Value: 16.8, 769 }, 770 }, 771 }, 772 }) 773 774 sc.indicatorManager.UpdateSystemIndicatorSpec(nn, []apiworkload.ServiceSystemIndicatorSpec{ 775 { 776 Name: "system-1", 777 Indicators: []apiworkload.Indicator{ 778 { 779 IndicatorLevel: apiworkload.IndicatorLevelLowerBound, 780 Value: 10.5, 781 }, 782 { 783 IndicatorLevel: apiworkload.IndicatorLevelUpperBound, 784 Value: 10.5, 785 }, 786 }, 787 }, 788 }) 789 sc.indicatorManager.UpdateSystemIndicatorSpec(nn, []apiworkload.ServiceSystemIndicatorSpec{ 790 { 791 Name: "system-2", 792 Indicators: []apiworkload.Indicator{ 793 { 794 IndicatorLevel: apiworkload.IndicatorLevelUpperBound, 795 Value: 10.5, 796 }, 797 }, 798 }, 799 }) 800 801 sc.indicatorManager.UpdateBusinessIndicatorStatus(nn, []apiworkload.ServiceBusinessIndicatorStatus{ 802 { 803 Name: "system-1", 804 Current: &value, 805 }, 806 { 807 Name: "system-2", 808 Current: &value, 809 }, 810 }) 811 time.Sleep(time.Second) 812 newSPD, err := controlCtx.Client.InternalClient.WorkloadV1alpha1(). 813 ServiceProfileDescriptors("default").Get(ctx, "spd1", metav1.GetOptions{}) 814 assert.NoError(t, err) 815 assert.Equal(t, expectedSpd.Spec.ExtendedIndicator, newSPD.Spec.ExtendedIndicator) 816 assert.Equal(t, expectedSpd.Spec.BusinessIndicator, newSPD.Spec.BusinessIndicator) 817 assert.Equal(t, expectedSpd.Spec.SystemIndicator, newSPD.Spec.SystemIndicator) 818 assert.Equal(t, expectedSpd.Status.BusinessStatus, newSPD.Status.BusinessStatus) 819 }