github.com/kubewharf/katalyst-core@v0.5.3/pkg/agent/evictionmanager/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 evictionmanager
    18  
    19  import (
    20  	"context"
    21  	"testing"
    22  	"time"
    23  
    24  	"github.com/stretchr/testify/assert"
    25  	v1 "k8s.io/api/core/v1"
    26  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    27  	"k8s.io/apimachinery/pkg/util/sets"
    28  
    29  	pluginapi "github.com/kubewharf/katalyst-api/pkg/protocol/evictionplugin/v1alpha1"
    30  	endpointpkg "github.com/kubewharf/katalyst-core/pkg/agent/evictionmanager/endpoint"
    31  	"github.com/kubewharf/katalyst-core/pkg/client"
    32  	"github.com/kubewharf/katalyst-core/pkg/config"
    33  	"github.com/kubewharf/katalyst-core/pkg/config/agent/dynamic/adminqos/eviction"
    34  	"github.com/kubewharf/katalyst-core/pkg/consts"
    35  	"github.com/kubewharf/katalyst-core/pkg/metaserver"
    36  	"github.com/kubewharf/katalyst-core/pkg/metaserver/agent"
    37  	"github.com/kubewharf/katalyst-core/pkg/metaserver/agent/pod"
    38  	"github.com/kubewharf/katalyst-core/pkg/metrics"
    39  	"github.com/kubewharf/katalyst-core/pkg/util/credential"
    40  	"github.com/kubewharf/katalyst-core/pkg/util/credential/authorization"
    41  )
    42  
    43  var (
    44  	evictionManagerSyncPeriod = 10 * time.Second
    45  	pods                      = []*v1.Pod{
    46  		{
    47  			ObjectMeta: metav1.ObjectMeta{
    48  				Name: "pod-1",
    49  				UID:  "pod-1",
    50  			},
    51  			Status: v1.PodStatus{
    52  				Phase: v1.PodRunning,
    53  			},
    54  		},
    55  		{
    56  			ObjectMeta: metav1.ObjectMeta{
    57  				Name: "pod-2",
    58  				UID:  "pod-2",
    59  			},
    60  			Status: v1.PodStatus{
    61  				Phase: v1.PodRunning,
    62  			},
    63  		},
    64  		{
    65  			ObjectMeta: metav1.ObjectMeta{
    66  				Name: "pod-3",
    67  				UID:  "pod-3",
    68  			},
    69  			Status: v1.PodStatus{
    70  				Phase: v1.PodRunning,
    71  			},
    72  		},
    73  		{
    74  			ObjectMeta: metav1.ObjectMeta{
    75  				Name: "pod-4",
    76  				UID:  "pod-4",
    77  			},
    78  			Status: v1.PodStatus{
    79  				Phase: v1.PodRunning,
    80  			},
    81  		},
    82  		{
    83  			ObjectMeta: metav1.ObjectMeta{
    84  				Name: "pod-5",
    85  				UID:  "pod-5",
    86  			},
    87  			Status: v1.PodStatus{
    88  				Phase: v1.PodRunning,
    89  			},
    90  		},
    91  	}
    92  )
    93  
    94  func makeConf() *config.Configuration {
    95  	conf := config.NewConfiguration()
    96  	conf.EvictionManagerSyncPeriod = evictionManagerSyncPeriod
    97  	conf.GetDynamicConfiguration().MemoryPressureEvictionConfiguration.GracePeriod = eviction.DefaultGracePeriod
    98  	conf.PodKiller = consts.KillerNameEvictionKiller
    99  	conf.GenericConfiguration.AuthConfiguration.AuthType = credential.AuthTypeInsecure
   100  	conf.GenericConfiguration.AuthConfiguration.AccessControlType = authorization.AccessControlTypeInsecure
   101  
   102  	return conf
   103  }
   104  
   105  func makeMetaServer() *metaserver.MetaServer {
   106  	return &metaserver.MetaServer{
   107  		MetaAgent: &agent.MetaAgent{
   108  			PodFetcher: &pod.PodFetcherStub{PodList: pods},
   109  		},
   110  	}
   111  }
   112  
   113  type pluginSkeleton struct{}
   114  
   115  func (p *pluginSkeleton) Start() {
   116  }
   117  
   118  func (p *pluginSkeleton) Stop() {
   119  }
   120  
   121  func (p *pluginSkeleton) IsStopped() bool {
   122  	return false
   123  }
   124  
   125  func (p *pluginSkeleton) StopGracePeriodExpired() bool {
   126  	return false
   127  }
   128  
   129  type plugin1 struct {
   130  	pluginSkeleton
   131  }
   132  
   133  func (p *plugin1) ThresholdMet(_ context.Context) (*pluginapi.ThresholdMetResponse, error) {
   134  	return &pluginapi.ThresholdMetResponse{
   135  		MetType: pluginapi.ThresholdMetType_NOT_MET,
   136  	}, nil
   137  }
   138  
   139  func (p *plugin1) GetTopEvictionPods(_ context.Context, _ *pluginapi.GetTopEvictionPodsRequest) (*pluginapi.GetTopEvictionPodsResponse, error) {
   140  	return &pluginapi.GetTopEvictionPodsResponse{}, nil
   141  }
   142  
   143  func (p *plugin1) GetEvictPods(_ context.Context, _ *pluginapi.GetEvictPodsRequest) (*pluginapi.GetEvictPodsResponse, error) {
   144  	return &pluginapi.GetEvictPodsResponse{
   145  		EvictPods: []*pluginapi.EvictPod{
   146  			{
   147  				Pod: &v1.Pod{
   148  					ObjectMeta: metav1.ObjectMeta{
   149  						Name: "pod-1",
   150  						UID:  "pod-1",
   151  					},
   152  					Status: v1.PodStatus{
   153  						Phase: v1.PodRunning,
   154  					},
   155  				},
   156  				ForceEvict: false,
   157  			},
   158  			{
   159  				Pod: &v1.Pod{
   160  					ObjectMeta: metav1.ObjectMeta{
   161  						Name: "pod-2",
   162  						UID:  "pod-2",
   163  					},
   164  					Status: v1.PodStatus{
   165  						Phase: v1.PodRunning,
   166  					},
   167  				},
   168  				ForceEvict: true,
   169  			},
   170  			{
   171  				Pod: &v1.Pod{
   172  					ObjectMeta: metav1.ObjectMeta{
   173  						Name: "pod-5",
   174  						UID:  "pod-5",
   175  					},
   176  					Status: v1.PodStatus{
   177  						Phase: v1.PodRunning,
   178  					},
   179  				},
   180  				ForceEvict: false,
   181  			},
   182  		},
   183  	}, nil
   184  }
   185  
   186  type plugin2 struct {
   187  	pluginSkeleton
   188  }
   189  
   190  func (p plugin2) ThresholdMet(_ context.Context) (*pluginapi.ThresholdMetResponse, error) {
   191  	return &pluginapi.ThresholdMetResponse{
   192  		MetType:            pluginapi.ThresholdMetType_HARD_MET,
   193  		ThresholdValue:     0.8,
   194  		ObservedValue:      0.9,
   195  		ThresholdOperator:  pluginapi.ThresholdOperator_GREATER_THAN,
   196  		EvictionScope:      "plugin2_scope",
   197  		GracePeriodSeconds: -1,
   198  		Condition: &pluginapi.Condition{
   199  			ConditionType: pluginapi.ConditionType_NODE_CONDITION,
   200  			Effects:       []string{"NoSchedule"},
   201  			ConditionName: "diskPressure",
   202  			MetCondition:  true,
   203  		},
   204  	}, nil
   205  }
   206  
   207  func (p plugin2) GetTopEvictionPods(_ context.Context, _ *pluginapi.GetTopEvictionPodsRequest) (*pluginapi.GetTopEvictionPodsResponse, error) {
   208  	return &pluginapi.GetTopEvictionPodsResponse{TargetPods: []*v1.Pod{
   209  		{
   210  			ObjectMeta: metav1.ObjectMeta{
   211  				Name: "pod-3",
   212  				UID:  "pod-3",
   213  			},
   214  			Status: v1.PodStatus{
   215  				Phase: v1.PodRunning,
   216  			},
   217  		},
   218  	}}, nil
   219  }
   220  
   221  func (p plugin2) GetEvictPods(_ context.Context, _ *pluginapi.GetEvictPodsRequest) (*pluginapi.GetEvictPodsResponse, error) {
   222  	return &pluginapi.GetEvictPodsResponse{EvictPods: []*pluginapi.EvictPod{}}, nil
   223  }
   224  
   225  func makeEvictionManager(t *testing.T) *EvictionManger {
   226  	mgr, err := NewEvictionManager(&client.GenericClientSet{}, nil, makeMetaServer(), metrics.DummyMetrics{}, makeConf())
   227  	assert.NoError(t, err)
   228  	mgr.endpoints = map[string]endpointpkg.Endpoint{
   229  		"plugin1": &plugin1{},
   230  		"plugin2": &plugin2{},
   231  	}
   232  
   233  	return mgr
   234  }
   235  
   236  func TestEvictionManger_collectEvictionResult(t *testing.T) {
   237  	t.Parallel()
   238  
   239  	tests := []struct {
   240  		name               string
   241  		dryrun             []string
   242  		wantSoftEvictPods  sets.String
   243  		wantForceEvictPods sets.String
   244  		wantConditions     sets.String
   245  	}{
   246  		{
   247  			name:   "no dryrun",
   248  			dryrun: []string{},
   249  			wantSoftEvictPods: sets.String{
   250  				"pod-1": sets.Empty{},
   251  				"pod-5": sets.Empty{},
   252  			},
   253  			wantForceEvictPods: sets.String{
   254  				"pod-2": sets.Empty{},
   255  				"pod-3": sets.Empty{},
   256  			},
   257  			wantConditions: sets.String{
   258  				"diskPressure": sets.Empty{},
   259  			},
   260  		},
   261  		{
   262  			name:              "dryrun plugin1",
   263  			dryrun:            []string{"plugin1"},
   264  			wantSoftEvictPods: sets.String{},
   265  			wantForceEvictPods: sets.String{
   266  				"pod-3": sets.Empty{},
   267  			},
   268  			wantConditions: sets.String{
   269  				"diskPressure": sets.Empty{},
   270  			},
   271  		},
   272  		{
   273  			name:   "dryrun plugin2",
   274  			dryrun: []string{"plugin2"},
   275  			wantSoftEvictPods: sets.String{
   276  				"pod-1": sets.Empty{},
   277  				"pod-5": sets.Empty{},
   278  			},
   279  			wantForceEvictPods: sets.String{
   280  				"pod-2": sets.Empty{},
   281  			},
   282  			wantConditions: sets.String{},
   283  		},
   284  		{
   285  			name:               "dryrun plugin1 & plugin2",
   286  			dryrun:             []string{"plugin1", "plugin2"},
   287  			wantSoftEvictPods:  sets.String{},
   288  			wantForceEvictPods: sets.String{},
   289  			wantConditions:     sets.String{},
   290  		},
   291  		{
   292  			name:               "dryrun *",
   293  			dryrun:             []string{"*"},
   294  			wantSoftEvictPods:  sets.String{},
   295  			wantForceEvictPods: sets.String{},
   296  			wantConditions:     sets.String{},
   297  		},
   298  	}
   299  	for _, tt := range tests {
   300  		tt := tt
   301  		t.Run(tt.name, func(t *testing.T) {
   302  			t.Parallel()
   303  
   304  			mgr := makeEvictionManager(t)
   305  			mgr.conf.GetDynamicConfiguration().DryRun = tt.dryrun
   306  
   307  			collector, _ := mgr.collectEvictionResult(pods)
   308  			gotForceEvictPods := sets.String{}
   309  			gotSoftEvictPods := sets.String{}
   310  			gotConditions := sets.String{}
   311  			for _, evictPod := range collector.getForceEvictPods() {
   312  				gotForceEvictPods.Insert(evictPod.Pod.Name)
   313  			}
   314  
   315  			for _, evictPod := range collector.getSoftEvictPods() {
   316  				gotSoftEvictPods.Insert(evictPod.Pod.Name)
   317  			}
   318  
   319  			for name := range collector.getCurrentConditions() {
   320  				gotConditions.Insert(name)
   321  			}
   322  			assert.Equal(t, tt.wantForceEvictPods, gotForceEvictPods)
   323  			assert.Equal(t, tt.wantSoftEvictPods, gotSoftEvictPods)
   324  			assert.Equal(t, tt.wantConditions, gotConditions)
   325  		})
   326  	}
   327  }