github.com/alibaba/ilogtail/pkg@v0.0.0-20250526110833-c53b480d046c/helper/containercenter/cri_runtime_api_interface_test.go (about)

     1  // Copyright 2025 iLogtail Authors
     2  // Licensed under Apache License, Version 2.0 (the "License")
     3  
     4  package containercenter
     5  
     6  import (
     7  	"context"
     8  	"fmt"
     9  	"net"
    10  	"sync"
    11  	"testing"
    12  	"time"
    13  
    14  	"github.com/stretchr/testify/assert"
    15  	"google.golang.org/grpc"
    16  	"google.golang.org/grpc/test/bufconn"
    17  	"google.golang.org/protobuf/runtime/protoimpl"
    18  )
    19  
    20  // 测试服务端实现
    21  type testRuntimeServiceServer struct {
    22  	mu sync.Mutex
    23  
    24  	// 控制返回值
    25  	versionResp *CriVersionResponse
    26  }
    27  
    28  func (s *testRuntimeServiceServer) Version(ctx context.Context) (*CriVersionResponse, error) {
    29  	s.mu.Lock()
    30  	defer s.mu.Unlock()
    31  	if s.versionResp.RuntimeAPIVersion == "v1" {
    32  		return s.versionResp, nil
    33  	} else if s.versionResp.RuntimeAPIVersion == "v1alpha2" {
    34  		return s.versionResp, nil
    35  	}
    36  	return nil, fmt.Errorf("failed to initialize RuntimeServiceClient")
    37  }
    38  
    39  func (s *testRuntimeServiceServer) ListContainers(ctx context.Context) (*CriListContainersResponse, error) {
    40  	return nil, nil
    41  }
    42  
    43  func (s *testRuntimeServiceServer) ContainerStatus(ctx context.Context, containerID string, verbose bool) (*CriContainerStatusResponse, error) {
    44  	return nil, nil
    45  }
    46  
    47  func (s *testRuntimeServiceServer) ListPodSandbox(ctx context.Context) (*CriListPodSandboxResponse, error) {
    48  	return nil, nil
    49  }
    50  
    51  func (s *testRuntimeServiceServer) PodSandboxStatus(ctx context.Context, sandboxID string, verbose bool) (*CriPodSandboxStatusResponse, error) {
    52  	return nil, nil
    53  }
    54  
    55  func (m *CriVersionResponse) Descriptor() ([]byte, []int) {
    56  	return protoimpl.X.CompressGZIP([]byte{
    57  		0x0a, 0x0a, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x04, 0x74, 0x65,
    58  		0x73, 0x74, 0x22, 0xa6, 0x01, 0x0a, 0x12, 0x43, 0x72, 0x69, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f,
    59  		0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x56, 0x65, 0x72,
    60  		0x73, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x56, 0x65, 0x72, 0x73,
    61  		0x69, 0x6f, 0x6e, 0x12, 0x20, 0x0a, 0x0b, 0x52, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x4e, 0x61,
    62  		0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x52, 0x75, 0x6e, 0x74, 0x69, 0x6d,
    63  		0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x26, 0x0a, 0x0e, 0x52, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65,
    64  		0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x52,
    65  		0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x2c, 0x0a,
    66  		0x11, 0x52, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x41, 0x50, 0x49, 0x56, 0x65, 0x72, 0x73, 0x69,
    67  		0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x52, 0x75, 0x6e, 0x74, 0x69, 0x6d,
    68  		0x65, 0x41, 0x50, 0x49, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x42, 0x08, 0x5a, 0x06, 0x2e,
    69  		0x2f, 0x74, 0x65, 0x73, 0x74, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
    70  	}), []int{0}
    71  }
    72  
    73  func (m *CriVersionResponse) Reset() {
    74  	*m = CriVersionResponse{}
    75  }
    76  
    77  func (m *CriVersionResponse) String() string {
    78  	return fmt.Sprintf("{Version: %s, RuntimeName: %s, RuntimeVersion: %s, RuntimeAPIVersion: %s}",
    79  		m.Version, m.RuntimeName, m.RuntimeVersion, m.RuntimeAPIVersion)
    80  }
    81  
    82  func (m *CriVersionResponse) ProtoMessage() {}
    83  
    84  // 创建测试服务端
    85  func createTestServer(t *testing.T, serviceName string) (*testRuntimeServiceServer, *bufconn.Listener) {
    86  	listener := bufconn.Listen(1024 * 1024)
    87  	server := grpc.NewServer()
    88  
    89  	testServer := &testRuntimeServiceServer{}
    90  	serviceDesc := grpc.ServiceDesc{
    91  		ServiceName: serviceName,
    92  		HandlerType: (*RuntimeService)(nil),
    93  		Methods: []grpc.MethodDesc{
    94  			{
    95  				MethodName: "Version",
    96  				Handler: func(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
    97  					return srv.(*testRuntimeServiceServer).Version(ctx)
    98  				},
    99  			},
   100  			{
   101  				MethodName: "ListContainers",
   102  				Handler: func(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
   103  					return srv.(*testRuntimeServiceServer).ListContainers(ctx)
   104  				},
   105  			},
   106  			{
   107  				MethodName: "ContainerStatus",
   108  				Handler: func(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
   109  					return srv.(*testRuntimeServiceServer).ContainerStatus(ctx, "", false)
   110  				},
   111  			},
   112  			{
   113  				MethodName: "ListPodSandbox",
   114  				Handler: func(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
   115  					return srv.(*testRuntimeServiceServer).ListPodSandbox(ctx)
   116  				},
   117  			},
   118  			{
   119  				MethodName: "PodSandboxStatus",
   120  				Handler: func(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
   121  					return srv.(*testRuntimeServiceServer).PodSandboxStatus(ctx, "", false)
   122  				},
   123  			},
   124  		},
   125  		Streams:  []grpc.StreamDesc{},
   126  		Metadata: "manual",
   127  	}
   128  
   129  	server.RegisterService(&serviceDesc, testServer)
   130  
   131  	go func() {
   132  		if err := server.Serve(listener); err != nil {
   133  			t.Errorf("Server serve error: %v", err)
   134  		}
   135  	}()
   136  
   137  	t.Cleanup(func() {
   138  		server.Stop()
   139  	})
   140  
   141  	return testServer, listener
   142  }
   143  
   144  func TestNewRuntimeServiceClient(t *testing.T) {
   145  	// 准备测试数据
   146  	v1Resp := &CriVersionResponse{
   147  		Version:           "0.1.0",
   148  		RuntimeName:       "containerd",
   149  		RuntimeVersion:    "v2.0.5",
   150  		RuntimeAPIVersion: "v1",
   151  	}
   152  
   153  	v1alpha2Resp := &CriVersionResponse{
   154  		Version:           "0.1.0",
   155  		RuntimeName:       "containerd",
   156  		RuntimeVersion:    "v1.6.0",
   157  		RuntimeAPIVersion: "v1alpha2",
   158  	}
   159  
   160  	invalidResp := &CriVersionResponse{
   161  		Version:           "0.1.0",
   162  		RuntimeName:       "containerd",
   163  		RuntimeVersion:    "v0.0.0",
   164  		RuntimeAPIVersion: "invalid",
   165  	}
   166  
   167  	tests := []struct {
   168  		name            string
   169  		serviceName     string
   170  		expectError     bool
   171  		expectedVersion string
   172  		setupServer     func(*testRuntimeServiceServer) // 自定义服务端配置
   173  	}{
   174  		{
   175  			name:            "V1_Success",
   176  			serviceName:     "runtime.v1.RuntimeService",
   177  			expectError:     false,
   178  			expectedVersion: "v1",
   179  			setupServer: func(s *testRuntimeServiceServer) {
   180  				s.versionResp = v1Resp
   181  			},
   182  		},
   183  		{
   184  			name:            "V1Alpha2_Success",
   185  			serviceName:     "runtime.v1alpha2.RuntimeService",
   186  			expectError:     false,
   187  			expectedVersion: "v1alpha2",
   188  			setupServer: func(s *testRuntimeServiceServer) {
   189  				s.versionResp = v1alpha2Resp
   190  			},
   191  		},
   192  		{
   193  			name:            "Both_Failed",
   194  			serviceName:     "invalid.service.name",
   195  			expectError:     true,
   196  			expectedVersion: "invalid",
   197  			setupServer: func(s *testRuntimeServiceServer) {
   198  				s.versionResp = invalidResp
   199  			},
   200  		},
   201  	}
   202  
   203  	for _, tt := range tests {
   204  		t.Run(tt.name, func(t *testing.T) {
   205  			// 创建测试服务端
   206  			testServer, listener := createTestServer(t, tt.serviceName)
   207  
   208  			// 应用自定义配置
   209  			if tt.setupServer != nil {
   210  				tt.setupServer(testServer)
   211  			}
   212  
   213  			// 替换地址解析函数
   214  			getAddressAndDialer = func(socket string) (string, func(string, time.Duration) (net.Conn, error), error) {
   215  				return "bufnet", func(addr string, timeout time.Duration) (net.Conn, error) {
   216  					return listener.Dial()
   217  				}, nil
   218  			}
   219  
   220  			// 替换拨号函数
   221  			grpcDialContext = func(ctx context.Context, addr string, opts ...grpc.DialOption) (*grpc.ClientConn, error) {
   222  				return grpc.DialContext(ctx, "bufnet", opts...)
   223  			}
   224  
   225  			// 执行测试
   226  			client, err := NewRuntimeServiceClient(time.Second, 1024*1024)
   227  
   228  			// 验证结果
   229  			if tt.expectError {
   230  				assert.Error(t, err)
   231  				assert.Nil(t, client)
   232  			} else {
   233  				assert.NoError(t, err)
   234  				assert.NotNil(t, client)
   235  				assert.Equal(t, tt.expectedVersion, client.info.RuntimeAPIVersion)
   236  			}
   237  
   238  			// 清理
   239  			if client != nil {
   240  				client.Close()
   241  			}
   242  		})
   243  	}
   244  }