github.com/kubewharf/katalyst-core@v0.5.3/pkg/agent/resourcemanager/fetcher/kubelet/kubeletplugin_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 kubelet
    18  
    19  import (
    20  	"context"
    21  	"io/ioutil"
    22  	"net"
    23  	"os"
    24  	"path"
    25  	"testing"
    26  	"time"
    27  
    28  	info "github.com/google/cadvisor/info/v1"
    29  	"github.com/stretchr/testify/assert"
    30  	"google.golang.org/grpc"
    31  	v1 "k8s.io/api/core/v1"
    32  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    33  	"k8s.io/klog/v2"
    34  	kubeletconfigv1beta1 "k8s.io/kubelet/config/v1beta1"
    35  	podresv1 "k8s.io/kubelet/pkg/apis/podresources/v1"
    36  	apisconfig "k8s.io/kubernetes/pkg/kubelet/apis/config"
    37  	"k8s.io/kubernetes/pkg/kubelet/checkpointmanager"
    38  	testutil "k8s.io/kubernetes/pkg/kubelet/cm/cpumanager/state/testing"
    39  
    40  	"github.com/kubewharf/katalyst-api/pkg/consts"
    41  	"github.com/kubewharf/katalyst-api/pkg/protocol/reporterplugin/v1alpha1"
    42  	"github.com/kubewharf/katalyst-core/cmd/katalyst-agent/app/options"
    43  	"github.com/kubewharf/katalyst-core/pkg/agent/resourcemanager/fetcher/kubelet/topology"
    44  	"github.com/kubewharf/katalyst-core/pkg/config"
    45  	pkgconsts "github.com/kubewharf/katalyst-core/pkg/consts"
    46  	"github.com/kubewharf/katalyst-core/pkg/metaserver"
    47  	"github.com/kubewharf/katalyst-core/pkg/metaserver/agent"
    48  	"github.com/kubewharf/katalyst-core/pkg/metaserver/agent/kubeletconfig"
    49  	"github.com/kubewharf/katalyst-core/pkg/metaserver/agent/pod"
    50  	"github.com/kubewharf/katalyst-core/pkg/metrics"
    51  	"github.com/kubewharf/katalyst-core/pkg/util/machine"
    52  )
    53  
    54  type fakePodResourcesServer struct {
    55  	podResources         *podresv1.ListPodResourcesResponse
    56  	allocatableResources *podresv1.AllocatableResourcesResponse
    57  	podresv1.UnimplementedPodResourcesListerServer
    58  }
    59  
    60  func (m *fakePodResourcesServer) List(_ context.Context, _ *podresv1.ListPodResourcesRequest) (*podresv1.ListPodResourcesResponse, error) {
    61  	return m.podResources, nil
    62  }
    63  
    64  func (m *fakePodResourcesServer) GetAllocatableResources(_ context.Context, _ *podresv1.AllocatableResourcesRequest) (*podresv1.AllocatableResourcesResponse, error) {
    65  	return m.allocatableResources, nil
    66  }
    67  
    68  func newFakePodResourcesServer(podResources *podresv1.ListPodResourcesResponse, allocatableResources *podresv1.AllocatableResourcesResponse) *grpc.Server {
    69  	server := grpc.NewServer()
    70  	podresv1.RegisterPodResourcesListerServer(server, &fakePodResourcesServer{
    71  		podResources:         podResources,
    72  		allocatableResources: allocatableResources,
    73  	})
    74  	return server
    75  }
    76  
    77  func generateTestConfiguration(t *testing.T, dir string) *config.Configuration {
    78  	testConfiguration, err := options.NewOptions().Config()
    79  	assert.NoError(t, err)
    80  	assert.NotNil(t, testConfiguration)
    81  
    82  	testConfiguration.PodResourcesServerEndpoints = []string{
    83  		path.Join(dir, "podresources.sock"),
    84  	}
    85  	testConfiguration.KubeletResourcePluginPaths = []string{
    86  		path.Join(dir, "resource-plugins/"),
    87  	}
    88  	return testConfiguration
    89  }
    90  
    91  func generateTestMetaServer(podList ...*v1.Pod) *metaserver.MetaServer {
    92  	fakeKubeletConfig := kubeletconfigv1beta1.KubeletConfiguration{
    93  		TopologyManagerPolicy: apisconfig.SingleNumaNodeTopologyManagerPolicy,
    94  		TopologyManagerScope:  apisconfig.ContainerTopologyManagerScope,
    95  	}
    96  
    97  	return &metaserver.MetaServer{
    98  		MetaAgent: &agent.MetaAgent{
    99  			PodFetcher: &pod.PodFetcherStub{PodList: podList},
   100  			KatalystMachineInfo: &machine.KatalystMachineInfo{
   101  				MachineInfo: &info.MachineInfo{
   102  					Topology: []info.Node{
   103  						{
   104  							Id: 0,
   105  							Cores: []info.Core{
   106  								{SocketID: 0, Id: 0, Threads: []int{0, 4}},
   107  								{SocketID: 0, Id: 1, Threads: []int{1, 5}},
   108  								{SocketID: 0, Id: 2, Threads: []int{2, 2}}, // Wrong case - should fail here
   109  								{SocketID: 0, Id: 3, Threads: []int{3, 7}},
   110  							},
   111  						},
   112  						{
   113  							Id: 1,
   114  							Cores: []info.Core{
   115  								{SocketID: 1, Id: 4, Threads: []int{8, 12}},
   116  								{SocketID: 1, Id: 5, Threads: []int{9, 13}},
   117  								{SocketID: 1, Id: 6, Threads: []int{10, 14}}, // Wrong case - should fail here
   118  								{SocketID: 1, Id: 7, Threads: []int{11, 15}},
   119  							},
   120  						},
   121  					},
   122  				},
   123  			},
   124  			KubeletConfigFetcher: kubeletconfig.NewFakeKubeletConfigFetcher(fakeKubeletConfig),
   125  		},
   126  	}
   127  }
   128  
   129  func tmpSocketDir() (socketDir string, err error) {
   130  	socketDir, err = ioutil.TempDir("", "pod_resources")
   131  	if err != nil {
   132  		return
   133  	}
   134  	err = os.MkdirAll(socketDir, 0o755)
   135  	if err != nil {
   136  		return "", err
   137  	}
   138  	return
   139  }
   140  
   141  func TestNewKubeletReporterPlugin(t *testing.T) {
   142  	t.Parallel()
   143  
   144  	dir, err := tmpSocketDir()
   145  	assert.NoError(t, err)
   146  	defer os.RemoveAll(dir)
   147  
   148  	conf := generateTestConfiguration(t, dir)
   149  
   150  	listener, err := net.Listen("unix", conf.PodResourcesServerEndpoints[0])
   151  	if err != nil {
   152  		t.Fatalf("failed to create listener: %v", err)
   153  	}
   154  
   155  	server := newFakePodResourcesServer(
   156  		&podresv1.ListPodResourcesResponse{
   157  			PodResources: []*podresv1.PodResources{
   158  				{
   159  					Name:      "pod-1",
   160  					Namespace: "default",
   161  					PodRole:   "pod-role-1",
   162  					PodType:   "pod-type-1",
   163  					Containers: []*podresv1.ContainerResources{
   164  						{
   165  							Name: "container-1",
   166  							Devices: []*podresv1.ContainerDevices{
   167  								{
   168  									ResourceName: "resource-1",
   169  									Topology: &podresv1.TopologyInfo{
   170  										Nodes: []*podresv1.NUMANode{
   171  											{
   172  												ID: 1,
   173  											},
   174  										},
   175  									},
   176  								},
   177  							},
   178  							Memory: []*podresv1.ContainerMemory{
   179  								{
   180  									MemoryType: "test",
   181  									Size_:      100,
   182  									Topology: &podresv1.TopologyInfo{
   183  										Nodes: []*podresv1.NUMANode{
   184  											{
   185  												ID: 1,
   186  											},
   187  										},
   188  									},
   189  								},
   190  							},
   191  							Resources: []*podresv1.TopologyAwareResource{
   192  								{
   193  									ResourceName:     "cpu",
   194  									IsScalarResource: true,
   195  									TopologyAwareQuantityList: []*podresv1.TopologyAwareQuantity{
   196  										{
   197  											ResourceValue: 10.0,
   198  											Node:          1,
   199  										},
   200  									},
   201  								},
   202  							},
   203  						},
   204  					},
   205  					Labels: map[string]string{
   206  						"aa": "bb",
   207  					},
   208  					Annotations: map[string]string{
   209  						"aa": "bb",
   210  					},
   211  				},
   212  			},
   213  		},
   214  		&podresv1.AllocatableResourcesResponse{
   215  			Devices: []*podresv1.ContainerDevices{
   216  				{
   217  					ResourceName: "resource-1",
   218  					Topology: &podresv1.TopologyInfo{
   219  						Nodes: []*podresv1.NUMANode{
   220  							{
   221  								ID: 1,
   222  							},
   223  						},
   224  					},
   225  				},
   226  			},
   227  			Memory: []*podresv1.ContainerMemory{
   228  				{
   229  					MemoryType: "test",
   230  					Size_:      100,
   231  					Topology: &podresv1.TopologyInfo{
   232  						Nodes: []*podresv1.NUMANode{
   233  							{
   234  								ID: 1,
   235  							},
   236  						},
   237  					},
   238  				},
   239  			},
   240  			Resources: []*podresv1.AllocatableTopologyAwareResource{
   241  				{
   242  					ResourceName:     "cpu",
   243  					IsScalarResource: true,
   244  					TopologyAwareAllocatableQuantityList: []*podresv1.TopologyAwareQuantity{
   245  						{
   246  							ResourceValue: 10.0,
   247  							Node:          1,
   248  						},
   249  					},
   250  				},
   251  			},
   252  		},
   253  	)
   254  
   255  	go func() {
   256  		err := server.Serve(listener)
   257  		assert.NoError(t, err)
   258  	}()
   259  
   260  	meta := generateTestMetaServer(&v1.Pod{
   261  		ObjectMeta: metav1.ObjectMeta{
   262  			Name:      "pod-1",
   263  			Namespace: "default",
   264  			UID:       "pod-1-uid",
   265  			Annotations: map[string]string{
   266  				consts.PodAnnotationQoSLevelKey:          consts.PodAnnotationQoSLevelDedicatedCores,
   267  				consts.PodAnnotationMemoryEnhancementKey: `{"numa_binding": "true"}`,
   268  			},
   269  		},
   270  	})
   271  
   272  	callback := func(name string, resp *v1alpha1.GetReportContentResponse) {
   273  		klog.Infof("Callback called with name: %s, resp: %#v", name, resp)
   274  	}
   275  
   276  	plugin, err := NewKubeletReporterPlugin(metrics.DummyMetrics{}, meta, conf, callback)
   277  	assert.NoError(t, err)
   278  
   279  	success := make(chan bool)
   280  	go plugin.Run(success)
   281  
   282  	<-success
   283  
   284  	checkpointManager, err := checkpointmanager.NewCheckpointManager(conf.KubeletResourcePluginPaths[0])
   285  	assert.NoError(t, err)
   286  
   287  	err = checkpointManager.CreateCheckpoint(pkgconsts.KubeletQoSResourceManagerCheckpoint, &testutil.MockCheckpoint{})
   288  	assert.NoError(t, err)
   289  
   290  	time.Sleep(10 * time.Millisecond)
   291  	plugin.Stop()
   292  
   293  	time.Sleep(10 * time.Millisecond)
   294  }
   295  
   296  func TestGetTopologyPolicyReportContent(t *testing.T) {
   297  	t.Parallel()
   298  
   299  	dir, err := tmpSocketDir()
   300  	assert.NoError(t, err)
   301  	defer os.RemoveAll(dir)
   302  
   303  	conf := generateTestConfiguration(t, dir)
   304  	conf.EnableReportTopologyPolicy = true
   305  
   306  	meta := generateTestMetaServer()
   307  
   308  	callback := func(name string, resp *v1alpha1.GetReportContentResponse) {
   309  		klog.Infof("Callback called with name: %s, resp: %#v", name, resp)
   310  	}
   311  
   312  	plugin, err := NewKubeletReporterPlugin(metrics.DummyMetrics{}, meta, conf, callback)
   313  	assert.NoError(t, err)
   314  	kubePlugin := plugin.(*kubeletPlugin)
   315  
   316  	_, err = kubePlugin.getTopologyPolicyReportContent(context.TODO())
   317  	assert.NoError(t, err)
   318  }
   319  
   320  func TestGetTopologyStatusContent(t *testing.T) {
   321  	t.Parallel()
   322  
   323  	dir, err := tmpSocketDir()
   324  	assert.NoError(t, err)
   325  	defer os.RemoveAll(dir)
   326  
   327  	conf := generateTestConfiguration(t, dir)
   328  	conf.EnableReportTopologyPolicy = true
   329  
   330  	meta := generateTestMetaServer()
   331  
   332  	callback := func(name string, resp *v1alpha1.GetReportContentResponse) {
   333  		klog.Infof("Callback called with name: %s, resp: %#v", name, resp)
   334  	}
   335  
   336  	plugin, err := NewKubeletReporterPlugin(metrics.DummyMetrics{}, meta, conf, callback)
   337  	assert.NoError(t, err)
   338  	kubePlugin := plugin.(*kubeletPlugin)
   339  
   340  	kubePlugin.topologyStatusAdapter = topology.DummyAdapter{}
   341  	_, err = kubePlugin.getReportContent(context.TODO())
   342  	assert.NoError(t, err)
   343  }