github.com/kubewharf/katalyst-core@v0.5.3/pkg/metaserver/spd/manager_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  	"fmt"
    23  	"io/ioutil"
    24  	"os"
    25  	"testing"
    26  	"time"
    27  
    28  	"github.com/stretchr/testify/require"
    29  	v1 "k8s.io/api/core/v1"
    30  	apiequality "k8s.io/apimachinery/pkg/api/equality"
    31  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    32  	"k8s.io/apimachinery/pkg/runtime"
    33  	"k8s.io/utils/pointer"
    34  
    35  	"github.com/kubewharf/katalyst-api/pkg/apis/config/v1alpha1"
    36  	workloadapis "github.com/kubewharf/katalyst-api/pkg/apis/workload/v1alpha1"
    37  	"github.com/kubewharf/katalyst-api/pkg/consts"
    38  	katalyst_base "github.com/kubewharf/katalyst-core/cmd/base"
    39  	pkgconsts "github.com/kubewharf/katalyst-core/pkg/consts"
    40  	"github.com/kubewharf/katalyst-core/pkg/metaserver/agent/cnc"
    41  	"github.com/kubewharf/katalyst-core/pkg/metrics"
    42  )
    43  
    44  func Test_serviceProfilingManager_ServiceBusinessPerformanceLevel(t *testing.T) {
    45  	t.Parallel()
    46  
    47  	type fields struct {
    48  		nodeName string
    49  		spd      *workloadapis.ServiceProfileDescriptor
    50  		cnc      *v1alpha1.CustomNodeConfig
    51  	}
    52  	type args struct {
    53  		pod *v1.Pod
    54  	}
    55  	tests := []struct {
    56  		name    string
    57  		fields  fields
    58  		args    args
    59  		want    PerformanceLevel
    60  		wantErr bool
    61  	}{
    62  		{
    63  			name: "service performance is good",
    64  			fields: fields{
    65  				nodeName: "node-1",
    66  				spd: &workloadapis.ServiceProfileDescriptor{
    67  					ObjectMeta: metav1.ObjectMeta{
    68  						Name:      "spd-1",
    69  						Namespace: "default",
    70  						Annotations: map[string]string{
    71  							pkgconsts.ServiceProfileDescriptorAnnotationKeyConfigHash: "3c7e3ff3f218",
    72  						},
    73  					},
    74  					Spec: workloadapis.ServiceProfileDescriptorSpec{
    75  						BusinessIndicator: []workloadapis.ServiceBusinessIndicatorSpec{
    76  							{
    77  								Name: workloadapis.ServiceBusinessIndicatorNameRPCLatency,
    78  								Indicators: []workloadapis.Indicator{
    79  									{
    80  										IndicatorLevel: workloadapis.IndicatorLevelLowerBound,
    81  										Value:          10,
    82  									},
    83  									{
    84  										IndicatorLevel: workloadapis.IndicatorLevelUpperBound,
    85  										Value:          100,
    86  									},
    87  								},
    88  							},
    89  						},
    90  					},
    91  					Status: workloadapis.ServiceProfileDescriptorStatus{
    92  						BusinessStatus: []workloadapis.ServiceBusinessIndicatorStatus{
    93  							{
    94  								Name:    workloadapis.ServiceBusinessIndicatorNameRPCLatency,
    95  								Current: pointer.Float32(40),
    96  							},
    97  						},
    98  					},
    99  				},
   100  				cnc: &v1alpha1.CustomNodeConfig{
   101  					ObjectMeta: metav1.ObjectMeta{
   102  						Name: "node-1",
   103  					},
   104  					Status: v1alpha1.CustomNodeConfigStatus{
   105  						ServiceProfileConfigList: []v1alpha1.TargetConfig{
   106  							{
   107  								ConfigName:      "spd-1",
   108  								ConfigNamespace: "default",
   109  								Hash:            "3c7e3ff3f218",
   110  							},
   111  						},
   112  					},
   113  				},
   114  			},
   115  			args: args{
   116  				pod: &v1.Pod{
   117  					ObjectMeta: metav1.ObjectMeta{
   118  						Name:      "pod-1",
   119  						Namespace: "default",
   120  						Annotations: map[string]string{
   121  							consts.PodAnnotationSPDNameKey: "spd-1",
   122  						},
   123  					},
   124  				},
   125  			},
   126  			want: PerformanceLevelGood,
   127  		},
   128  		{
   129  			name: "service performance large than upper bound",
   130  			fields: fields{
   131  				nodeName: "node-1",
   132  				spd: &workloadapis.ServiceProfileDescriptor{
   133  					ObjectMeta: metav1.ObjectMeta{
   134  						Name:      "spd-1",
   135  						Namespace: "default",
   136  						Annotations: map[string]string{
   137  							pkgconsts.ServiceProfileDescriptorAnnotationKeyConfigHash: "3c7e3ff3f218",
   138  						},
   139  					},
   140  					Spec: workloadapis.ServiceProfileDescriptorSpec{
   141  						BusinessIndicator: []workloadapis.ServiceBusinessIndicatorSpec{
   142  							{
   143  								Name: workloadapis.ServiceBusinessIndicatorNameRPCLatency,
   144  								Indicators: []workloadapis.Indicator{
   145  									{
   146  										IndicatorLevel: workloadapis.IndicatorLevelUpperBound,
   147  										Value:          50,
   148  									},
   149  								},
   150  							},
   151  						},
   152  					},
   153  					Status: workloadapis.ServiceProfileDescriptorStatus{
   154  						BusinessStatus: []workloadapis.ServiceBusinessIndicatorStatus{
   155  							{
   156  								Name:    workloadapis.ServiceBusinessIndicatorNameRPCLatency,
   157  								Current: pointer.Float32(60),
   158  							},
   159  						},
   160  					},
   161  				},
   162  				cnc: &v1alpha1.CustomNodeConfig{
   163  					ObjectMeta: metav1.ObjectMeta{
   164  						Name: "node-1",
   165  					},
   166  					Status: v1alpha1.CustomNodeConfigStatus{
   167  						ServiceProfileConfigList: []v1alpha1.TargetConfig{
   168  							{
   169  								ConfigName:      "spd-1",
   170  								ConfigNamespace: "default",
   171  								Hash:            "3c7e3ff3f218",
   172  							},
   173  						},
   174  					},
   175  				},
   176  			},
   177  			args: args{
   178  				pod: &v1.Pod{
   179  					ObjectMeta: metav1.ObjectMeta{
   180  						Name:      "pod-1",
   181  						Namespace: "default",
   182  						Annotations: map[string]string{
   183  							consts.PodAnnotationSPDNameKey: "spd-1",
   184  						},
   185  					},
   186  				},
   187  			},
   188  			want: PerformanceLevelPoor,
   189  		},
   190  		{
   191  			name: "service performance lower than lower bound",
   192  			fields: fields{
   193  				nodeName: "node-1",
   194  				spd: &workloadapis.ServiceProfileDescriptor{
   195  					ObjectMeta: metav1.ObjectMeta{
   196  						Name:      "spd-1",
   197  						Namespace: "default",
   198  						Annotations: map[string]string{
   199  							pkgconsts.ServiceProfileDescriptorAnnotationKeyConfigHash: "3c7e3ff3f218",
   200  						},
   201  					},
   202  					Spec: workloadapis.ServiceProfileDescriptorSpec{
   203  						BusinessIndicator: []workloadapis.ServiceBusinessIndicatorSpec{
   204  							{
   205  								Name: workloadapis.ServiceBusinessIndicatorNameRPCLatency,
   206  								Indicators: []workloadapis.Indicator{
   207  									{
   208  										IndicatorLevel: workloadapis.IndicatorLevelLowerBound,
   209  										Value:          20,
   210  									},
   211  								},
   212  							},
   213  						},
   214  					},
   215  					Status: workloadapis.ServiceProfileDescriptorStatus{
   216  						BusinessStatus: []workloadapis.ServiceBusinessIndicatorStatus{
   217  							{
   218  								Name:    workloadapis.ServiceBusinessIndicatorNameRPCLatency,
   219  								Current: pointer.Float32(10),
   220  							},
   221  						},
   222  					},
   223  				},
   224  				cnc: &v1alpha1.CustomNodeConfig{
   225  					ObjectMeta: metav1.ObjectMeta{
   226  						Name: "node-1",
   227  					},
   228  					Status: v1alpha1.CustomNodeConfigStatus{
   229  						ServiceProfileConfigList: []v1alpha1.TargetConfig{
   230  							{
   231  								ConfigName:      "spd-1",
   232  								ConfigNamespace: "default",
   233  								Hash:            "3c7e3ff3f218",
   234  							},
   235  						},
   236  					},
   237  				},
   238  			},
   239  			args: args{
   240  				pod: &v1.Pod{
   241  					ObjectMeta: metav1.ObjectMeta{
   242  						Name:      "pod-1",
   243  						Namespace: "default",
   244  						Annotations: map[string]string{
   245  							consts.PodAnnotationSPDNameKey: "spd-1",
   246  						},
   247  					},
   248  				},
   249  			},
   250  			want: PerformanceLevelPerfect,
   251  		},
   252  	}
   253  	for _, tt := range tests {
   254  		tt := tt
   255  		t.Run(tt.name, func(t *testing.T) {
   256  			t.Parallel()
   257  
   258  			dir, err := ioutil.TempDir("", "checkpoint-Test_serviceProfilingManager_ServiceBusinessPerformanceLevel")
   259  			require.NoError(t, err)
   260  			defer os.RemoveAll(dir)
   261  
   262  			conf := generateTestConfiguration(t, tt.fields.nodeName, dir)
   263  			genericCtx, err := katalyst_base.GenerateFakeGenericContext(nil, []runtime.Object{
   264  				tt.fields.spd,
   265  				tt.fields.cnc,
   266  			})
   267  			require.NoError(t, err)
   268  
   269  			cncFetcher := cnc.NewCachedCNCFetcher(conf.BaseConfiguration, conf.CNCConfiguration, genericCtx.Client.InternalClient.ConfigV1alpha1().CustomNodeConfigs())
   270  			s, err := NewSPDFetcher(genericCtx.Client, metrics.DummyMetrics{}, cncFetcher, conf)
   271  			require.NoError(t, err)
   272  			require.NotNil(t, s)
   273  
   274  			m := NewServiceProfilingManager(s)
   275  			require.NoError(t, err)
   276  
   277  			// first get spd add spd key to cache
   278  			_, _ = s.GetSPD(context.Background(), tt.args.pod.ObjectMeta)
   279  			go m.Run(context.Background())
   280  			time.Sleep(1 * time.Second)
   281  
   282  			got, err := m.ServiceBusinessPerformanceLevel(context.Background(), tt.args.pod.ObjectMeta)
   283  			if (err != nil) != tt.wantErr {
   284  				t.Errorf("ServiceBusinessPerformanceLevel() error = %v, wantErr %v", err, tt.wantErr)
   285  				return
   286  			}
   287  			if got != tt.want {
   288  				t.Errorf("ServiceBusinessPerformanceLevel() got = %v, want %v", got, tt.want)
   289  			}
   290  		})
   291  	}
   292  }
   293  
   294  func Test_serviceProfilingManager_ServiceSystemPerformanceTarget(t *testing.T) {
   295  	t.Parallel()
   296  
   297  	type fields struct {
   298  		nodeName string
   299  		spd      *workloadapis.ServiceProfileDescriptor
   300  		cnc      *v1alpha1.CustomNodeConfig
   301  	}
   302  	type args struct {
   303  		pod *v1.Pod
   304  	}
   305  	tests := []struct {
   306  		name    string
   307  		fields  fields
   308  		args    args
   309  		want    IndicatorTarget
   310  		wantErr bool
   311  	}{
   312  		{
   313  			name: "service performance is good",
   314  			fields: fields{
   315  				nodeName: "node-1",
   316  				spd: &workloadapis.ServiceProfileDescriptor{
   317  					ObjectMeta: metav1.ObjectMeta{
   318  						Name:      "spd-1",
   319  						Namespace: "default",
   320  						Annotations: map[string]string{
   321  							pkgconsts.ServiceProfileDescriptorAnnotationKeyConfigHash: "3c7e3ff3f218",
   322  						},
   323  					},
   324  					Spec: workloadapis.ServiceProfileDescriptorSpec{
   325  						SystemIndicator: []workloadapis.ServiceSystemIndicatorSpec{
   326  							{
   327  								Name: workloadapis.ServiceSystemIndicatorNameCPUSchedWait,
   328  								Indicators: []workloadapis.Indicator{
   329  									{
   330  										IndicatorLevel: workloadapis.IndicatorLevelLowerBound,
   331  										Value:          10,
   332  									},
   333  									{
   334  										IndicatorLevel: workloadapis.IndicatorLevelUpperBound,
   335  										Value:          100,
   336  									},
   337  								},
   338  							},
   339  							{
   340  								Name: workloadapis.ServiceSystemIndicatorNameCPI,
   341  								Indicators: []workloadapis.Indicator{
   342  									{
   343  										IndicatorLevel: workloadapis.IndicatorLevelLowerBound,
   344  										Value:          1.4,
   345  									},
   346  									{
   347  										IndicatorLevel: workloadapis.IndicatorLevelUpperBound,
   348  										Value:          2.4,
   349  									},
   350  								},
   351  							},
   352  						},
   353  					},
   354  				},
   355  				cnc: &v1alpha1.CustomNodeConfig{
   356  					ObjectMeta: metav1.ObjectMeta{
   357  						Name: "node-1",
   358  					},
   359  					Status: v1alpha1.CustomNodeConfigStatus{
   360  						ServiceProfileConfigList: []v1alpha1.TargetConfig{
   361  							{
   362  								ConfigName:      "spd-1",
   363  								ConfigNamespace: "default",
   364  								Hash:            "3c7e3ff3f218",
   365  							},
   366  						},
   367  					},
   368  				},
   369  			},
   370  			args: args{
   371  				pod: &v1.Pod{
   372  					ObjectMeta: metav1.ObjectMeta{
   373  						Name:      "pod-1",
   374  						Namespace: "default",
   375  						Annotations: map[string]string{
   376  							consts.PodAnnotationSPDNameKey: "spd-1",
   377  						},
   378  					},
   379  				},
   380  			},
   381  			want: IndicatorTarget{
   382  				string(workloadapis.ServiceSystemIndicatorNameCPUSchedWait): {
   383  					UpperBound: pointer.Float64(100),
   384  					LowerBound: pointer.Float64(10),
   385  				},
   386  				string(workloadapis.ServiceSystemIndicatorNameCPI): {
   387  					UpperBound: pointer.Float64(2.4),
   388  					LowerBound: pointer.Float64(1.4),
   389  				},
   390  			},
   391  		},
   392  	}
   393  	for i, tt := range tests {
   394  		i := i
   395  		tt := tt
   396  		t.Run(tt.name, func(t *testing.T) {
   397  			t.Parallel()
   398  
   399  			dir, err := ioutil.TempDir("", fmt.Sprintf("checkpoint-Test_serviceProfilingManager_ServiceSystemPerformanceTarget_%d", i))
   400  			require.NoError(t, err)
   401  			defer os.RemoveAll(dir)
   402  
   403  			conf := generateTestConfiguration(t, tt.fields.nodeName, dir)
   404  			genericCtx, err := katalyst_base.GenerateFakeGenericContext(nil, []runtime.Object{
   405  				tt.fields.spd,
   406  				tt.fields.cnc,
   407  			})
   408  			require.NoError(t, err)
   409  
   410  			cncFetcher := cnc.NewCachedCNCFetcher(conf.BaseConfiguration, conf.CNCConfiguration, genericCtx.Client.InternalClient.ConfigV1alpha1().CustomNodeConfigs())
   411  			s, err := NewSPDFetcher(genericCtx.Client, metrics.DummyMetrics{}, cncFetcher, conf)
   412  			require.NoError(t, err)
   413  			require.NotNil(t, s)
   414  
   415  			m := NewServiceProfilingManager(s)
   416  			require.NoError(t, err)
   417  
   418  			// first get spd add pod spd key to cache
   419  			_, _ = s.GetSPD(context.Background(), tt.args.pod.ObjectMeta)
   420  			go m.Run(context.Background())
   421  			time.Sleep(1 * time.Second)
   422  
   423  			got, err := m.ServiceSystemPerformanceTarget(context.Background(), tt.args.pod.ObjectMeta)
   424  			if (err != nil) != tt.wantErr {
   425  				t.Errorf("ServiceSystemPerformanceTarget() error = %v, wantErr %v", err, tt.wantErr)
   426  				return
   427  			}
   428  			if apiequality.Semantic.DeepEqual(tt.want, got) {
   429  				t.Errorf("ServiceSystemPerformanceTarget() got = %v, want %v", got, tt.want)
   430  			}
   431  		})
   432  	}
   433  }
   434  
   435  func Test_serviceProfilingManager_ServiceExtendedIndicator(t *testing.T) {
   436  	t.Parallel()
   437  
   438  	type fields struct {
   439  		nodeName string
   440  		spd      *workloadapis.ServiceProfileDescriptor
   441  		cnc      *v1alpha1.CustomNodeConfig
   442  	}
   443  	type args struct {
   444  		pod *v1.Pod
   445  	}
   446  	tests := []struct {
   447  		name       string
   448  		fields     fields
   449  		args       args
   450  		want       *workloadapis.TestExtendedIndicators
   451  		isBaseline bool
   452  		wantErr    bool
   453  	}{
   454  		{
   455  			name: "without baseline",
   456  			fields: fields{
   457  				nodeName: "node-1",
   458  				spd: &workloadapis.ServiceProfileDescriptor{
   459  					ObjectMeta: metav1.ObjectMeta{
   460  						Name:      "spd-1",
   461  						Namespace: "default",
   462  						Annotations: map[string]string{
   463  							pkgconsts.ServiceProfileDescriptorAnnotationKeyConfigHash: "3c7e3ff3f218",
   464  						},
   465  					},
   466  					Spec: workloadapis.ServiceProfileDescriptorSpec{
   467  						ExtendedIndicator: []workloadapis.ServiceExtendedIndicatorSpec{
   468  							{
   469  								Name: "TestExtended",
   470  								Indicators: runtime.RawExtension{
   471  									Object: &workloadapis.TestExtendedIndicators{
   472  										Indicators: &workloadapis.TestIndicators{},
   473  									},
   474  								},
   475  							},
   476  						},
   477  					},
   478  				},
   479  				cnc: &v1alpha1.CustomNodeConfig{
   480  					ObjectMeta: metav1.ObjectMeta{
   481  						Name: "node-1",
   482  					},
   483  					Status: v1alpha1.CustomNodeConfigStatus{
   484  						ServiceProfileConfigList: []v1alpha1.TargetConfig{
   485  							{
   486  								ConfigName:      "spd-1",
   487  								ConfigNamespace: "default",
   488  								Hash:            "3c7e3ff3f218",
   489  							},
   490  						},
   491  					},
   492  				},
   493  			},
   494  			args: args{
   495  				pod: &v1.Pod{
   496  					ObjectMeta: metav1.ObjectMeta{
   497  						Name:      "pod-1",
   498  						Namespace: "default",
   499  						Annotations: map[string]string{
   500  							consts.PodAnnotationSPDNameKey: "spd-1",
   501  						},
   502  					},
   503  				},
   504  			},
   505  			want: &workloadapis.TestExtendedIndicators{
   506  				Indicators: &workloadapis.TestIndicators{},
   507  			},
   508  		},
   509  		{
   510  			name: "with baseline",
   511  			fields: fields{
   512  				nodeName: "node-1",
   513  				spd: &workloadapis.ServiceProfileDescriptor{
   514  					ObjectMeta: metav1.ObjectMeta{
   515  						Name:      "spd-1",
   516  						Namespace: "default",
   517  						Annotations: map[string]string{
   518  							pkgconsts.ServiceProfileDescriptorAnnotationKeyConfigHash: "3c7e3ff3f218",
   519  							consts.SPDAnnotationExtendedBaselineSentinelKey:           "{\"TestExtended\":{\"timeStamp\":\"2023-08-01T00:00:01Z\",\"podName\":\"pod1\"}}",
   520  						},
   521  					},
   522  					Spec: workloadapis.ServiceProfileDescriptorSpec{
   523  						ExtendedIndicator: []workloadapis.ServiceExtendedIndicatorSpec{
   524  							{
   525  								Name:            "TestExtended",
   526  								BaselinePercent: pointer.Int32(10),
   527  								Indicators: runtime.RawExtension{
   528  									Object: &workloadapis.TestExtendedIndicators{
   529  										Indicators: &workloadapis.TestIndicators{},
   530  									},
   531  								},
   532  							},
   533  						},
   534  					},
   535  				},
   536  				cnc: &v1alpha1.CustomNodeConfig{
   537  					ObjectMeta: metav1.ObjectMeta{
   538  						Name: "node-1",
   539  					},
   540  					Status: v1alpha1.CustomNodeConfigStatus{
   541  						ServiceProfileConfigList: []v1alpha1.TargetConfig{
   542  							{
   543  								ConfigName:      "spd-1",
   544  								ConfigNamespace: "default",
   545  								Hash:            "3c7e3ff3f218",
   546  							},
   547  						},
   548  					},
   549  				},
   550  			},
   551  			args: args{
   552  				pod: &v1.Pod{
   553  					ObjectMeta: metav1.ObjectMeta{
   554  						Name:      "pod-1",
   555  						Namespace: "default",
   556  						Annotations: map[string]string{
   557  							consts.PodAnnotationSPDNameKey: "spd-1",
   558  						},
   559  					},
   560  				},
   561  			},
   562  			want: &workloadapis.TestExtendedIndicators{
   563  				Indicators: &workloadapis.TestIndicators{},
   564  			},
   565  			isBaseline: true,
   566  		},
   567  		{
   568  			name: "use raw data",
   569  			fields: fields{
   570  				nodeName: "node-1",
   571  				spd: &workloadapis.ServiceProfileDescriptor{
   572  					ObjectMeta: metav1.ObjectMeta{
   573  						Name:      "spd-1",
   574  						Namespace: "default",
   575  						Annotations: map[string]string{
   576  							pkgconsts.ServiceProfileDescriptorAnnotationKeyConfigHash: "3c7e3ff3f218",
   577  							consts.SPDAnnotationExtendedBaselineSentinelKey:           "{\"TestExtended\":{\"timeStamp\":\"2023-08-01T00:00:01Z\",\"podName\":\"pod1\"}}",
   578  						},
   579  					},
   580  					Spec: workloadapis.ServiceProfileDescriptorSpec{
   581  						ExtendedIndicator: []workloadapis.ServiceExtendedIndicatorSpec{
   582  							{
   583  								Name:            "TestExtended",
   584  								BaselinePercent: pointer.Int32(10),
   585  								Indicators: runtime.RawExtension{
   586  									Raw: func(object runtime.Object) []byte {
   587  										marshal, err := json.Marshal(object)
   588  										if err != nil {
   589  											return nil
   590  										}
   591  										return marshal
   592  									}(
   593  										&workloadapis.TestExtendedIndicators{
   594  											Indicators: &workloadapis.TestIndicators{},
   595  										},
   596  									),
   597  								},
   598  							},
   599  						},
   600  					},
   601  				},
   602  				cnc: &v1alpha1.CustomNodeConfig{
   603  					ObjectMeta: metav1.ObjectMeta{
   604  						Name: "node-1",
   605  					},
   606  					Status: v1alpha1.CustomNodeConfigStatus{
   607  						ServiceProfileConfigList: []v1alpha1.TargetConfig{
   608  							{
   609  								ConfigName:      "spd-1",
   610  								ConfigNamespace: "default",
   611  								Hash:            "3c7e3ff3f218",
   612  							},
   613  						},
   614  					},
   615  				},
   616  			},
   617  			args: args{
   618  				pod: &v1.Pod{
   619  					ObjectMeta: metav1.ObjectMeta{
   620  						Name:      "pod-1",
   621  						Namespace: "default",
   622  						Annotations: map[string]string{
   623  							consts.PodAnnotationSPDNameKey: "spd-1",
   624  						},
   625  					},
   626  				},
   627  			},
   628  			want: &workloadapis.TestExtendedIndicators{
   629  				Indicators: &workloadapis.TestIndicators{},
   630  			},
   631  			isBaseline: true,
   632  		},
   633  	}
   634  	for _, tt := range tests {
   635  		tt := tt
   636  		t.Run(tt.name, func(t *testing.T) {
   637  			t.Parallel()
   638  			dir, err := ioutil.TempDir("", "checkpoint-Test_serviceProfilingManager_ServiceExtendedIndicator")
   639  			require.NoError(t, err)
   640  			defer os.RemoveAll(dir)
   641  
   642  			conf := generateTestConfiguration(t, tt.fields.nodeName, dir)
   643  			genericCtx, err := katalyst_base.GenerateFakeGenericContext(nil, []runtime.Object{
   644  				tt.fields.spd,
   645  				tt.fields.cnc,
   646  			})
   647  			require.NoError(t, err)
   648  
   649  			cncFetcher := cnc.NewCachedCNCFetcher(conf.BaseConfiguration, conf.CNCConfiguration, genericCtx.Client.InternalClient.ConfigV1alpha1().CustomNodeConfigs())
   650  			s, err := NewSPDFetcher(genericCtx.Client, metrics.DummyMetrics{}, cncFetcher, conf)
   651  			require.NoError(t, err)
   652  			require.NotNil(t, s)
   653  
   654  			m := NewServiceProfilingManager(s)
   655  			require.NoError(t, err)
   656  
   657  			// first get spd add pod spd key to cache
   658  			_, _ = s.GetSPD(context.Background(), tt.args.pod.ObjectMeta)
   659  			go m.Run(context.Background())
   660  			time.Sleep(1 * time.Second)
   661  
   662  			got := &workloadapis.TestExtendedIndicators{}
   663  			isBaseline, err := m.ServiceExtendedIndicator(context.Background(), tt.args.pod.ObjectMeta, got)
   664  			if (err != nil) != tt.wantErr {
   665  				t.Errorf("ServiceExtendedIndicator() error = %v, wantErr %v", err, tt.wantErr)
   666  				return
   667  			}
   668  			if !apiequality.Semantic.DeepEqual(got, tt.want) {
   669  				t.Errorf("ServiceExtendedIndicator() got = %v, want %v", got, tt.want)
   670  			}
   671  			if isBaseline != tt.isBaseline {
   672  				t.Errorf("ServiceExtendedIndicator() isBaseline = %v, want %v", isBaseline, tt.isBaseline)
   673  			}
   674  		})
   675  	}
   676  }
   677  
   678  func Test_serviceProfilingManager_ServiceBaseline(t *testing.T) {
   679  	t.Parallel()
   680  
   681  	type fields struct {
   682  		nodeName string
   683  		spd      *workloadapis.ServiceProfileDescriptor
   684  		cnc      *v1alpha1.CustomNodeConfig
   685  	}
   686  	type args struct {
   687  		pod *v1.Pod
   688  	}
   689  	tests := []struct {
   690  		name       string
   691  		fields     fields
   692  		args       args
   693  		isBaseline bool
   694  		wantErr    bool
   695  	}{
   696  		{
   697  			name: "without baseline",
   698  			fields: fields{
   699  				nodeName: "node-1",
   700  				spd: &workloadapis.ServiceProfileDescriptor{
   701  					ObjectMeta: metav1.ObjectMeta{
   702  						Name:      "spd-1",
   703  						Namespace: "default",
   704  						Annotations: map[string]string{
   705  							pkgconsts.ServiceProfileDescriptorAnnotationKeyConfigHash: "3c7e3ff3f218",
   706  						},
   707  					},
   708  					Spec: workloadapis.ServiceProfileDescriptorSpec{},
   709  				},
   710  				cnc: &v1alpha1.CustomNodeConfig{
   711  					ObjectMeta: metav1.ObjectMeta{
   712  						Name: "node-1",
   713  					},
   714  					Status: v1alpha1.CustomNodeConfigStatus{
   715  						ServiceProfileConfigList: []v1alpha1.TargetConfig{
   716  							{
   717  								ConfigName:      "spd-1",
   718  								ConfigNamespace: "default",
   719  								Hash:            "3c7e3ff3f218",
   720  							},
   721  						},
   722  					},
   723  				},
   724  			},
   725  			args: args{
   726  				pod: &v1.Pod{
   727  					ObjectMeta: metav1.ObjectMeta{
   728  						Name:      "pod-1",
   729  						Namespace: "default",
   730  						Annotations: map[string]string{
   731  							consts.PodAnnotationSPDNameKey: "spd-1",
   732  						},
   733  					},
   734  				},
   735  			},
   736  		},
   737  		{
   738  			name: "with baseline",
   739  			fields: fields{
   740  				nodeName: "node-1",
   741  				spd: &workloadapis.ServiceProfileDescriptor{
   742  					ObjectMeta: metav1.ObjectMeta{
   743  						Name:      "spd-1",
   744  						Namespace: "default",
   745  						Annotations: map[string]string{
   746  							pkgconsts.ServiceProfileDescriptorAnnotationKeyConfigHash: "3c7e3ff3f218",
   747  							consts.SPDAnnotationBaselineSentinelKey:                   "{\"timeStamp\":\"2023-08-01T00:00:01Z\",\"podName\":\"pod1\"}",
   748  						},
   749  					},
   750  					Spec: workloadapis.ServiceProfileDescriptorSpec{
   751  						BaselinePercent: pointer.Int32(10),
   752  					},
   753  				},
   754  				cnc: &v1alpha1.CustomNodeConfig{
   755  					ObjectMeta: metav1.ObjectMeta{
   756  						Name: "node-1",
   757  					},
   758  					Status: v1alpha1.CustomNodeConfigStatus{
   759  						ServiceProfileConfigList: []v1alpha1.TargetConfig{
   760  							{
   761  								ConfigName:      "spd-1",
   762  								ConfigNamespace: "default",
   763  								Hash:            "3c7e3ff3f218",
   764  							},
   765  						},
   766  					},
   767  				},
   768  			},
   769  			args: args{
   770  				pod: &v1.Pod{
   771  					ObjectMeta: metav1.ObjectMeta{
   772  						Name:      "pod-1",
   773  						Namespace: "default",
   774  						Annotations: map[string]string{
   775  							consts.PodAnnotationSPDNameKey: "spd-1",
   776  						},
   777  					},
   778  				},
   779  			},
   780  			isBaseline: true,
   781  		},
   782  	}
   783  	for _, tt := range tests {
   784  		tt := tt
   785  		t.Run(tt.name, func(t *testing.T) {
   786  			t.Parallel()
   787  
   788  			dir, err := ioutil.TempDir("", "checkpoint-Test_serviceProfilingManager_ServiceBaseline")
   789  			require.NoError(t, err)
   790  			defer os.RemoveAll(dir)
   791  
   792  			conf := generateTestConfiguration(t, tt.fields.nodeName, dir)
   793  			genericCtx, err := katalyst_base.GenerateFakeGenericContext(nil, []runtime.Object{
   794  				tt.fields.spd,
   795  				tt.fields.cnc,
   796  			})
   797  			require.NoError(t, err)
   798  
   799  			cncFetcher := cnc.NewCachedCNCFetcher(conf.BaseConfiguration, conf.CNCConfiguration, genericCtx.Client.InternalClient.ConfigV1alpha1().CustomNodeConfigs())
   800  			s, err := NewSPDFetcher(genericCtx.Client, metrics.DummyMetrics{}, cncFetcher, conf)
   801  			require.NoError(t, err)
   802  			require.NotNil(t, s)
   803  
   804  			m := NewServiceProfilingManager(s)
   805  			require.NoError(t, err)
   806  
   807  			// first get spd add pod spd key to cache
   808  			_, _ = s.GetSPD(context.Background(), tt.args.pod.ObjectMeta)
   809  			go m.Run(context.Background())
   810  			time.Sleep(1 * time.Second)
   811  
   812  			isBaseline, err := m.ServiceBaseline(context.Background(), tt.args.pod.ObjectMeta)
   813  			if (err != nil) != tt.wantErr {
   814  				t.Errorf("ServiceBaseline() error = %v, wantErr %v", err, tt.wantErr)
   815  				return
   816  			}
   817  			if isBaseline != tt.isBaseline {
   818  				t.Errorf("ServiceBaseline() isBaseline = %v, want %v", isBaseline, tt.isBaseline)
   819  			}
   820  		})
   821  	}
   822  }