...

Source file src/github.com/Microsoft/hcsshim/cmd/ncproxy/server_test.go

Documentation: github.com/Microsoft/hcsshim/cmd/ncproxy

     1  //go:build windows
     2  
     3  package main
     4  
     5  import (
     6  	"context"
     7  	"net"
     8  	"path/filepath"
     9  	"testing"
    10  	"time"
    11  
    12  	computeagentMock "github.com/Microsoft/hcsshim/internal/computeagent/mock"
    13  	ncproxystore "github.com/Microsoft/hcsshim/internal/ncproxy/store"
    14  	"github.com/Microsoft/hcsshim/internal/ncproxyttrpc"
    15  	nodenetsvcV0 "github.com/Microsoft/hcsshim/pkg/ncproxy/nodenetsvc/v0"
    16  	nodenetsvcMockV0 "github.com/Microsoft/hcsshim/pkg/ncproxy/nodenetsvc/v0/mock"
    17  	nodenetsvc "github.com/Microsoft/hcsshim/pkg/ncproxy/nodenetsvc/v1"
    18  	nodenetsvcMock "github.com/Microsoft/hcsshim/pkg/ncproxy/nodenetsvc/v1/mock"
    19  	"github.com/containerd/ttrpc"
    20  	"github.com/golang/mock/gomock"
    21  	"github.com/pkg/errors"
    22  	bolt "go.etcd.io/bbolt"
    23  	"google.golang.org/grpc/codes"
    24  	"google.golang.org/grpc/status"
    25  )
    26  
    27  func TestRegisterComputeAgent(t *testing.T) {
    28  	ctx := context.Background()
    29  
    30  	// setup test database
    31  	tempDir := t.TempDir()
    32  
    33  	db, err := bolt.Open(filepath.Join(tempDir, "networkproxy.db.test"), 0600, nil)
    34  	if err != nil {
    35  		t.Fatal(err)
    36  	}
    37  	defer db.Close()
    38  
    39  	// create test TTRPC service
    40  	store := ncproxystore.NewComputeAgentStore(db)
    41  	agentCache := newComputeAgentCache()
    42  	tService := newTTRPCService(ctx, agentCache, store)
    43  
    44  	// setup mocked calls
    45  	winioDialPipe = func(path string, timeout *time.Duration) (net.Conn, error) {
    46  		rPipe, _ := net.Pipe()
    47  		return rPipe, nil
    48  	}
    49  	ttrpcNewClient = func(conn net.Conn, opts ...ttrpc.ClientOpts) *ttrpc.Client {
    50  		return &ttrpc.Client{}
    51  	}
    52  
    53  	containerID := t.Name() + "-containerID"
    54  	req := &ncproxyttrpc.RegisterComputeAgentRequest{
    55  		AgentAddress: t.Name() + "-agent-address",
    56  		ContainerID:  containerID,
    57  	}
    58  	if _, err := tService.RegisterComputeAgent(ctx, req); err != nil {
    59  		t.Fatalf("expected to get no error, instead got %v", err)
    60  	}
    61  
    62  	// validate that the entry was added to the agent
    63  	actual, err := agentCache.get(containerID)
    64  	if err != nil {
    65  		t.Fatalf("failed to get the agent entry %v", err)
    66  	}
    67  	if actual == nil {
    68  		t.Fatal("compute agent client was not put into agent cache")
    69  	}
    70  }
    71  
    72  func TestConfigureNetworking_V1(t *testing.T) {
    73  	ctx := context.Background()
    74  
    75  	// setup test database
    76  	tempDir := t.TempDir()
    77  
    78  	db, err := bolt.Open(filepath.Join(tempDir, "networkproxy.db.test"), 0600, nil)
    79  	if err != nil {
    80  		t.Fatal(err)
    81  	}
    82  	defer db.Close()
    83  
    84  	// create test TTRPC service
    85  	store := ncproxystore.NewComputeAgentStore(db)
    86  	agentCache := newComputeAgentCache()
    87  	tService := newTTRPCService(ctx, agentCache, store)
    88  
    89  	// setup mocked client and mocked calls for nodenetsvc
    90  	nodeNetCtrl := gomock.NewController(t)
    91  	defer nodeNetCtrl.Finish()
    92  	mockedClient := nodenetsvcMock.NewMockNodeNetworkServiceClient(nodeNetCtrl)
    93  	mockedClientV0 := nodenetsvcMockV0.NewMockNodeNetworkServiceClient(nodeNetCtrl)
    94  	nodeNetSvcClient = &nodeNetSvcConn{
    95  		addr:     "",
    96  		client:   mockedClient,
    97  		v0Client: mockedClientV0,
    98  	}
    99  
   100  	// allow calls to v1 mock api
   101  	mockedClient.EXPECT().ConfigureNetworking(gomock.Any(), gomock.Any()).Return(&nodenetsvc.ConfigureNetworkingResponse{}, nil).AnyTimes()
   102  
   103  	type config struct {
   104  		name          string
   105  		containerID   string
   106  		requestType   ncproxyttrpc.RequestTypeInternal
   107  		errorExpected bool
   108  	}
   109  	containerID := t.Name() + "-containerID"
   110  	tests := []config{
   111  		{
   112  			name:          "Configure Networking setup returns no error",
   113  			containerID:   containerID,
   114  			requestType:   ncproxyttrpc.RequestTypeInternal_Setup,
   115  			errorExpected: false,
   116  		},
   117  		{
   118  			name:          "Configure Networking teardown returns no error",
   119  			containerID:   containerID,
   120  			requestType:   ncproxyttrpc.RequestTypeInternal_Teardown,
   121  			errorExpected: false,
   122  		},
   123  		{
   124  			name:          "Configure Networking setup returns error when container ID is empty",
   125  			containerID:   "",
   126  			requestType:   ncproxyttrpc.RequestTypeInternal_Setup,
   127  			errorExpected: true,
   128  		},
   129  		{
   130  			name:          "Configure Networking setup returns error when request type is not supported",
   131  			containerID:   containerID,
   132  			requestType:   3, // unsupported request type
   133  			errorExpected: true,
   134  		},
   135  	}
   136  
   137  	for _, test := range tests {
   138  		t.Run(test.name, func(_ *testing.T) {
   139  			req := &ncproxyttrpc.ConfigureNetworkingInternalRequest{
   140  				ContainerID: test.containerID,
   141  				RequestType: test.requestType,
   142  			}
   143  			_, err := tService.ConfigureNetworking(ctx, req)
   144  			if test.errorExpected && err == nil {
   145  				t.Fatalf("expected ConfigureNetworking to return an error")
   146  			}
   147  			if !test.errorExpected && err != nil {
   148  				t.Fatalf("expected ConfigureNetworking to return no error, instead got %v", err)
   149  			}
   150  		})
   151  	}
   152  }
   153  
   154  func TestConfigureNetworking_V0(t *testing.T) {
   155  	ctx := context.Background()
   156  
   157  	// setup test database
   158  	tempDir := t.TempDir()
   159  
   160  	db, err := bolt.Open(filepath.Join(tempDir, "networkproxy.db.test"), 0600, nil)
   161  	if err != nil {
   162  		t.Fatal(err)
   163  	}
   164  	defer db.Close()
   165  
   166  	// create test TTRPC service
   167  	store := ncproxystore.NewComputeAgentStore(db)
   168  	agentCache := newComputeAgentCache()
   169  	tService := newTTRPCService(ctx, agentCache, store)
   170  
   171  	// setup mocked client and mocked calls for nodenetsvc
   172  	nodeNetCtrl := gomock.NewController(t)
   173  	defer nodeNetCtrl.Finish()
   174  	mockedClient := nodenetsvcMock.NewMockNodeNetworkServiceClient(nodeNetCtrl)
   175  	mockedClientV0 := nodenetsvcMockV0.NewMockNodeNetworkServiceClient(nodeNetCtrl)
   176  	nodeNetSvcClient = &nodeNetSvcConn{
   177  		addr:     "",
   178  		client:   mockedClient,
   179  		v0Client: mockedClientV0,
   180  	}
   181  
   182  	// v1 api calls should return "Unimplemented" so that we will try the v0 code path
   183  	// allow succcessful calls to v0 api
   184  	mockedClientV0.EXPECT().ConfigureNetworking(gomock.Any(), gomock.Any()).Return(&nodenetsvcV0.ConfigureNetworkingResponse{}, nil).AnyTimes()
   185  	mockedClient.EXPECT().ConfigureNetworking(gomock.Any(), gomock.Any()).Return(nil, status.Error(codes.Unimplemented, "mock the v1 api not implemented")).AnyTimes()
   186  
   187  	type config struct {
   188  		name          string
   189  		containerID   string
   190  		requestType   ncproxyttrpc.RequestTypeInternal
   191  		errorExpected bool
   192  	}
   193  	containerID := t.Name() + "-containerID"
   194  	tests := []config{
   195  		{
   196  			name:          "Configure Networking setup returns no error",
   197  			containerID:   containerID,
   198  			requestType:   ncproxyttrpc.RequestTypeInternal_Setup,
   199  			errorExpected: false,
   200  		},
   201  		{
   202  			name:          "Configure Networking teardown returns no error",
   203  			containerID:   containerID,
   204  			requestType:   ncproxyttrpc.RequestTypeInternal_Teardown,
   205  			errorExpected: false,
   206  		},
   207  		{
   208  			name:          "Configure Networking setup returns error when container ID is empty",
   209  			containerID:   "",
   210  			requestType:   ncproxyttrpc.RequestTypeInternal_Setup,
   211  			errorExpected: true,
   212  		},
   213  		{
   214  			name:          "Configure Networking setup returns error when request type is not supported",
   215  			containerID:   containerID,
   216  			requestType:   3, // unsupported request type
   217  			errorExpected: true,
   218  		},
   219  	}
   220  
   221  	for _, test := range tests {
   222  		t.Run(test.name, func(_ *testing.T) {
   223  			req := &ncproxyttrpc.ConfigureNetworkingInternalRequest{
   224  				ContainerID: test.containerID,
   225  				RequestType: test.requestType,
   226  			}
   227  			_, err := tService.ConfigureNetworking(ctx, req)
   228  			if test.errorExpected && err == nil {
   229  				t.Fatalf("expected ConfigureNetworking to return an error")
   230  			}
   231  			if !test.errorExpected && err != nil {
   232  				t.Fatalf("expected ConfigureNetworking to return no error, instead got %v", err)
   233  			}
   234  		})
   235  	}
   236  }
   237  
   238  func TestReconnectComputeAgents_Success(t *testing.T) {
   239  	ctx := context.Background()
   240  
   241  	// setup test database
   242  	tempDir := t.TempDir()
   243  
   244  	db, err := bolt.Open(filepath.Join(tempDir, "networkproxy.db.test"), 0600, nil)
   245  	if err != nil {
   246  		t.Fatal(err)
   247  	}
   248  	defer db.Close()
   249  
   250  	// create test TTRPC service
   251  	store := ncproxystore.NewComputeAgentStore(db)
   252  	agentCache := newComputeAgentCache()
   253  
   254  	// setup mocked calls
   255  	winioDialPipe = func(path string, timeout *time.Duration) (net.Conn, error) {
   256  		rPipe, _ := net.Pipe()
   257  		return rPipe, nil
   258  	}
   259  	ttrpcNewClient = func(conn net.Conn, opts ...ttrpc.ClientOpts) *ttrpc.Client {
   260  		return &ttrpc.Client{}
   261  	}
   262  
   263  	// add test entry in database
   264  	containerID := "fake-container-id"
   265  	address := "123412341234"
   266  
   267  	if err := store.UpdateComputeAgent(ctx, containerID, address); err != nil {
   268  		t.Fatal(err)
   269  	}
   270  
   271  	reconnectComputeAgents(ctx, store, agentCache)
   272  
   273  	// validate that the agent cache has the entry now
   274  	actualClient, err := agentCache.get(containerID)
   275  	if err != nil {
   276  		t.Fatal(err)
   277  	}
   278  	if actualClient == nil {
   279  		t.Fatal("no entry added on reconnect to agent client cache")
   280  	}
   281  }
   282  
   283  func TestReconnectComputeAgents_Failure(t *testing.T) {
   284  	ctx := context.Background()
   285  
   286  	// setup test database
   287  	tempDir := t.TempDir()
   288  
   289  	db, err := bolt.Open(filepath.Join(tempDir, "networkproxy.db.test"), 0600, nil)
   290  	if err != nil {
   291  		t.Fatal(err)
   292  	}
   293  	defer db.Close()
   294  
   295  	// create test TTRPC service
   296  	store := ncproxystore.NewComputeAgentStore(db)
   297  	agentCache := newComputeAgentCache()
   298  
   299  	// setup mocked calls
   300  	winioDialPipe = func(path string, timeout *time.Duration) (net.Conn, error) {
   301  		// this will cause the reconnect compute agents call to run into an error
   302  		// trying to reconnect to the fake container address
   303  		return nil, errors.New("fake error")
   304  	}
   305  	ttrpcNewClient = func(conn net.Conn, opts ...ttrpc.ClientOpts) *ttrpc.Client {
   306  		return &ttrpc.Client{}
   307  	}
   308  
   309  	// add test entry in database
   310  	containerID := "fake-container-id"
   311  	address := "123412341234"
   312  
   313  	if err := store.UpdateComputeAgent(ctx, containerID, address); err != nil {
   314  		t.Fatal(err)
   315  	}
   316  
   317  	reconnectComputeAgents(ctx, store, agentCache)
   318  
   319  	// validate that the agent cache does NOT have an entry
   320  	actualClient, err := agentCache.get(containerID)
   321  	if err != nil {
   322  		t.Fatal(err)
   323  	}
   324  	if actualClient != nil {
   325  		t.Fatalf("expected no entry on failure, instead found %v", actualClient)
   326  	}
   327  
   328  	// validate that the agent store no longer has an entry for this container
   329  	value, err := store.GetComputeAgent(ctx, containerID)
   330  	if err == nil {
   331  		t.Fatalf("expected an error, instead found value %s", value)
   332  	}
   333  }
   334  
   335  func TestDisconnectComputeAgents(t *testing.T) {
   336  	ctx := context.Background()
   337  	containerID := "fake-container-id"
   338  
   339  	agentCache := newComputeAgentCache()
   340  
   341  	// create mocked compute agent service
   342  	computeAgentCtrl := gomock.NewController(t)
   343  	defer computeAgentCtrl.Finish()
   344  	mockedService := computeagentMock.NewMockComputeAgentService(computeAgentCtrl)
   345  	mockedAgentClient := &computeAgentClient{nil, mockedService}
   346  
   347  	// put mocked compute agent in agent cache for test
   348  	if err := agentCache.put(containerID, mockedAgentClient); err != nil {
   349  		t.Fatal(err)
   350  	}
   351  
   352  	if err := disconnectComputeAgents(ctx, agentCache); err != nil {
   353  		t.Fatal(err)
   354  	}
   355  
   356  	// validate there is no longer an entry for the compute agent client
   357  	actual, err := agentCache.get(containerID)
   358  	if err == nil {
   359  		t.Fatalf("expected to find the cache empty, instead found %v", actual)
   360  	}
   361  }
   362  

View as plain text