github.com/matrixorigin/matrixone@v1.2.0/pkg/logservice/hakeeper_client_test.go (about)

     1  // Copyright 2021 - 2022 Matrix Origin
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //      http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package logservice
    16  
    17  import (
    18  	"context"
    19  	"sync"
    20  	"testing"
    21  	"time"
    22  
    23  	"github.com/matrixorigin/matrixone/pkg/pb/metadata"
    24  
    25  	"github.com/google/uuid"
    26  	"github.com/lni/dragonboat/v4"
    27  	"github.com/lni/goutils/leaktest"
    28  	"github.com/lni/vfs"
    29  	"github.com/stretchr/testify/assert"
    30  	"github.com/stretchr/testify/require"
    31  
    32  	"github.com/matrixorigin/matrixone/pkg/common/moerr"
    33  	"github.com/matrixorigin/matrixone/pkg/common/morpc"
    34  	"github.com/matrixorigin/matrixone/pkg/hakeeper"
    35  	pb "github.com/matrixorigin/matrixone/pkg/pb/logservice"
    36  )
    37  
    38  func TestHAKeeperClientConfigIsValidated(t *testing.T) {
    39  	cfg := HAKeeperClientConfig{}
    40  	cc1, err := NewCNHAKeeperClient(context.TODO(), cfg)
    41  	assert.Nil(t, cc1)
    42  	assert.Error(t, err)
    43  	cc2, err := NewTNHAKeeperClient(context.TODO(), cfg)
    44  	assert.Nil(t, cc2)
    45  	assert.Error(t, err)
    46  	cc3, err := NewLogHAKeeperClient(context.TODO(), cfg)
    47  	assert.Nil(t, cc3)
    48  	assert.Error(t, err)
    49  }
    50  
    51  func TestHAKeeperClientsCanBeCreated(t *testing.T) {
    52  	fn := func(t *testing.T, s *Service) {
    53  		cfg := HAKeeperClientConfig{
    54  			ServiceAddresses: []string{testServiceAddress},
    55  		}
    56  		ctx, cancel := context.WithTimeout(context.Background(), time.Second)
    57  		defer cancel()
    58  		c1, err := NewCNHAKeeperClient(ctx, cfg)
    59  		require.NoError(t, err)
    60  		assert.NoError(t, c1.Close())
    61  		c2, err := NewTNHAKeeperClient(ctx, cfg)
    62  		assert.NoError(t, err)
    63  		assert.NoError(t, c2.Close())
    64  		c3, err := NewLogHAKeeperClient(ctx, cfg)
    65  		assert.NoError(t, err)
    66  		assert.NoError(t, c3.Close())
    67  	}
    68  	runServiceTest(t, true, true, fn)
    69  }
    70  
    71  func TestHAKeeperClientCanNotConnectToNonHAKeeperNode(t *testing.T) {
    72  	fn := func(t *testing.T, s *Service) {
    73  		cfg := HAKeeperClientConfig{
    74  			ServiceAddresses: []string{testServiceAddress},
    75  		}
    76  		ctx, cancel := context.WithTimeout(context.Background(), time.Second)
    77  		defer cancel()
    78  		_, err := NewCNHAKeeperClient(ctx, cfg)
    79  		require.True(t, moerr.IsMoErrCode(err, moerr.ErrNoHAKeeper))
    80  		_, err = NewTNHAKeeperClient(ctx, cfg)
    81  		require.True(t, moerr.IsMoErrCode(err, moerr.ErrNoHAKeeper))
    82  		_, err = NewLogHAKeeperClient(ctx, cfg)
    83  		require.True(t, moerr.IsMoErrCode(err, moerr.ErrNoHAKeeper))
    84  	}
    85  	runServiceTest(t, false, true, fn)
    86  }
    87  
    88  func TestHAKeeperClientConnectByReverseProxy(t *testing.T) {
    89  	fn := func(t *testing.T, s *Service) {
    90  		done := false
    91  		for i := 0; i < 1000; i++ {
    92  			si, ok, err := GetShardInfo(testServiceAddress, hakeeper.DefaultHAKeeperShardID)
    93  			if err != nil || !ok {
    94  				time.Sleep(10 * time.Millisecond)
    95  				continue
    96  			}
    97  			done = true
    98  			require.NoError(t, err)
    99  			assert.True(t, ok)
   100  			assert.Equal(t, uint64(1), si.ReplicaID)
   101  			addr, ok := si.Replicas[si.ReplicaID]
   102  			assert.True(t, ok)
   103  			assert.Equal(t, testServiceAddress, addr)
   104  			break
   105  		}
   106  		if !done {
   107  			t.Fatalf("failed to get shard info")
   108  		}
   109  		// now shard info can be queried
   110  		cfg := HAKeeperClientConfig{
   111  			ServiceAddresses: []string{"localhost:53033"}, // obvious not reachable
   112  			DiscoveryAddress: testServiceAddress,
   113  		}
   114  		ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
   115  		defer cancel()
   116  		c, err := NewLogHAKeeperClient(ctx, cfg)
   117  		require.NoError(t, err)
   118  		defer func() {
   119  			assert.NoError(t, c.Close())
   120  		}()
   121  
   122  		hb := s.store.getHeartbeatMessage()
   123  		cb, err := c.SendLogHeartbeat(ctx, hb)
   124  		require.NoError(t, err)
   125  		assert.Equal(t, 0, len(cb.Commands))
   126  
   127  		sc := pb.ScheduleCommand{
   128  			UUID:        s.ID(),
   129  			ServiceType: pb.TNService,
   130  			ShutdownStore: &pb.ShutdownStore{
   131  				StoreID: "hello world",
   132  			},
   133  		}
   134  		require.NoError(t, s.store.addScheduleCommands(ctx, 0, []pb.ScheduleCommand{sc}))
   135  		cb, err = c.SendLogHeartbeat(ctx, hb)
   136  		require.NoError(t, err)
   137  		require.Equal(t, 1, len(cb.Commands))
   138  		require.Equal(t, sc, cb.Commands[0])
   139  	}
   140  	runServiceTest(t, true, true, fn)
   141  }
   142  
   143  func TestHAKeeperClientSendCNHeartbeat(t *testing.T) {
   144  	fn := func(t *testing.T, s *Service) {
   145  		cfg := HAKeeperClientConfig{
   146  			ServiceAddresses: []string{testServiceAddress},
   147  		}
   148  		ctx, cancel := context.WithTimeout(context.Background(), time.Second)
   149  		defer cancel()
   150  		c1, err := NewCNHAKeeperClient(ctx, cfg)
   151  		require.NoError(t, err)
   152  		defer func() {
   153  			assert.NoError(t, c1.Close())
   154  		}()
   155  
   156  		// should be transparently handled
   157  		cc := c1.(*managedHAKeeperClient)
   158  		assert.NoError(t, cc.mu.client.close())
   159  		cc.mu.client = nil
   160  
   161  		hb := pb.CNStoreHeartbeat{
   162  			UUID:           s.ID(),
   163  			ServiceAddress: "addr1",
   164  		}
   165  		_, err = c1.SendCNHeartbeat(ctx, hb)
   166  		require.NoError(t, err)
   167  
   168  		c2, err := NewTNHAKeeperClient(ctx, cfg)
   169  		require.NoError(t, err)
   170  		defer func() {
   171  			assert.NoError(t, c2.Close())
   172  		}()
   173  
   174  		// should be transparently handled
   175  		cc = c2.(*managedHAKeeperClient)
   176  		assert.NoError(t, cc.mu.client.close())
   177  		cc.mu.client = nil
   178  
   179  		hb2 := pb.TNStoreHeartbeat{
   180  			UUID:                 s.ID(),
   181  			ServiceAddress:       "addr2",
   182  			LogtailServerAddress: "addr3",
   183  		}
   184  		cb, err := c2.SendTNHeartbeat(ctx, hb2)
   185  		require.NoError(t, err)
   186  		assert.Equal(t, 0, len(cb.Commands))
   187  
   188  		// should be transparently handled
   189  		cc = c1.(*managedHAKeeperClient)
   190  		assert.NoError(t, cc.mu.client.close())
   191  		cc.mu.client = nil
   192  
   193  		cd, err := c1.GetClusterDetails(ctx)
   194  		require.NoError(t, err)
   195  		cn := pb.CNStore{
   196  			UUID:           s.ID(),
   197  			ServiceAddress: "addr1",
   198  			WorkState:      metadata.WorkState_Working,
   199  			UpTime:         cd.CNStores[0].UpTime,
   200  		}
   201  		tn := pb.TNStore{
   202  			UUID:                 s.ID(),
   203  			ServiceAddress:       "addr2",
   204  			LogtailServerAddress: "addr3",
   205  		}
   206  		assert.Equal(t, []pb.CNStore{cn}, cd.CNStores)
   207  		assert.Equal(t, []pb.TNStore{tn}, cd.TNStores)
   208  	}
   209  	runServiceTest(t, true, true, fn)
   210  }
   211  
   212  func TestHAKeeperClientSendTNHeartbeat(t *testing.T) {
   213  	fn := func(t *testing.T, s *Service) {
   214  		cfg := HAKeeperClientConfig{
   215  			ServiceAddresses: []string{testServiceAddress},
   216  		}
   217  		ctx, cancel := context.WithTimeout(context.Background(), time.Second)
   218  		defer cancel()
   219  		c, err := NewTNHAKeeperClient(ctx, cfg)
   220  		require.NoError(t, err)
   221  		defer func() {
   222  			assert.NoError(t, c.Close())
   223  		}()
   224  		hb := pb.TNStoreHeartbeat{
   225  			UUID: s.ID(),
   226  		}
   227  		cb, err := c.SendTNHeartbeat(ctx, hb)
   228  		require.NoError(t, err)
   229  		assert.Equal(t, 0, len(cb.Commands))
   230  
   231  		sc := pb.ScheduleCommand{
   232  			UUID:        s.ID(),
   233  			ServiceType: pb.TNService,
   234  			ShutdownStore: &pb.ShutdownStore{
   235  				StoreID: "hello world",
   236  			},
   237  		}
   238  		require.NoError(t, s.store.addScheduleCommands(ctx, 0, []pb.ScheduleCommand{sc}))
   239  		cb, err = c.SendTNHeartbeat(ctx, hb)
   240  		require.NoError(t, err)
   241  		require.Equal(t, 1, len(cb.Commands))
   242  		require.Equal(t, sc, cb.Commands[0])
   243  	}
   244  	runServiceTest(t, true, true, fn)
   245  }
   246  
   247  func TestHAKeeperClientSendLogHeartbeat(t *testing.T) {
   248  	fn := func(t *testing.T, s *Service) {
   249  		cfg := HAKeeperClientConfig{
   250  			ServiceAddresses: []string{testServiceAddress},
   251  		}
   252  		ctx, cancel := context.WithTimeout(context.Background(), time.Second)
   253  		defer cancel()
   254  		c, err := NewLogHAKeeperClient(ctx, cfg)
   255  		require.NoError(t, err)
   256  		defer func() {
   257  			assert.NoError(t, c.Close())
   258  		}()
   259  
   260  		// should be transparently handled
   261  		cc := c.(*managedHAKeeperClient)
   262  		assert.NoError(t, cc.mu.client.close())
   263  		cc.mu.client = nil
   264  
   265  		hb := s.store.getHeartbeatMessage()
   266  		cb, err := c.SendLogHeartbeat(ctx, hb)
   267  		require.NoError(t, err)
   268  		assert.Equal(t, 0, len(cb.Commands))
   269  
   270  		sc := pb.ScheduleCommand{
   271  			UUID:        s.ID(),
   272  			ServiceType: pb.TNService,
   273  			ShutdownStore: &pb.ShutdownStore{
   274  				StoreID: "hello world",
   275  			},
   276  		}
   277  		require.NoError(t, s.store.addScheduleCommands(ctx, 0, []pb.ScheduleCommand{sc}))
   278  		cb, err = c.SendLogHeartbeat(ctx, hb)
   279  		require.NoError(t, err)
   280  		require.Equal(t, 1, len(cb.Commands))
   281  		require.Equal(t, sc, cb.Commands[0])
   282  	}
   283  	runServiceTest(t, true, true, fn)
   284  }
   285  
   286  func testNotHAKeeperErrorIsHandled(t *testing.T, fn func(*testing.T, *managedHAKeeperClient)) {
   287  	defer leaktest.AfterTest(t)()
   288  	cfg1 := DefaultConfig()
   289  	cfg1.UUID = uuid.New().String()
   290  	cfg1.FS = vfs.NewStrictMem()
   291  	cfg1.DeploymentID = 1
   292  	cfg1.RTTMillisecond = 5
   293  	cfg1.DataDir = "data-1"
   294  	cfg1.LogServicePort = 9002
   295  	cfg1.RaftPort = 9000
   296  	cfg1.GossipPort = 9001
   297  	cfg1.GossipSeedAddresses = []string{"127.0.0.1:9011"}
   298  	cfg1.DisableWorkers = true
   299  	cfg2 := DefaultConfig()
   300  	cfg2.UUID = uuid.New().String()
   301  	cfg2.FS = vfs.NewStrictMem()
   302  	cfg2.DeploymentID = 1
   303  	cfg2.RTTMillisecond = 5
   304  	cfg2.DataDir = "data-2"
   305  	cfg2.LogServicePort = 9012
   306  	cfg2.RaftPort = 9010
   307  	cfg2.GossipPort = 9011
   308  	cfg2.GossipSeedAddresses = []string{"127.0.0.1:9001"}
   309  	cfg2.DisableWorkers = true
   310  	service1, err := NewService(cfg1,
   311  		newFS(),
   312  		nil,
   313  		WithBackendFilter(func(msg morpc.Message, backendAddr string) bool {
   314  			return true
   315  		}),
   316  	)
   317  	require.NoError(t, err)
   318  	defer func() {
   319  		assert.NoError(t, service1.Close())
   320  	}()
   321  	service2, err := NewService(cfg2,
   322  		newFS(),
   323  		nil,
   324  		WithBackendFilter(func(msg morpc.Message, backendAddr string) bool {
   325  			return true
   326  		}),
   327  	)
   328  	require.NoError(t, err)
   329  	defer func() {
   330  		assert.NoError(t, service2.Close())
   331  	}()
   332  	// service2 is HAKeeper
   333  	peers := make(map[uint64]dragonboat.Target)
   334  	peers[1] = service2.ID()
   335  	assert.NoError(t, service2.store.startHAKeeperReplica(1, peers, false))
   336  	// manually construct a HAKeeper client that is connected to service1
   337  	pool := &sync.Pool{}
   338  	pool.New = func() interface{} {
   339  		return &RPCRequest{pool: pool}
   340  	}
   341  	respPool := &sync.Pool{}
   342  	respPool.New = func() interface{} {
   343  		return &RPCResponse{pool: respPool}
   344  	}
   345  	cfg := HAKeeperClientConfig{
   346  		ServiceAddresses: []string{cfg1.LogServiceServiceAddr(), cfg2.LogServiceServiceAddr()},
   347  	}
   348  	c := &hakeeperClient{
   349  		cfg:      cfg,
   350  		pool:     pool,
   351  		respPool: respPool,
   352  	}
   353  	ctx, cancel := context.WithTimeout(context.Background(), time.Second)
   354  	defer cancel()
   355  	cc, err := getRPCClient(
   356  		ctx,
   357  		cfg1.LogServiceServiceAddr(),
   358  		c.respPool,
   359  		defaultMaxMessageSize,
   360  		false,
   361  		0,
   362  	)
   363  	require.NoError(t, err)
   364  	c.addr = cfg1.LogServiceServiceAddr()
   365  	c.client = cc
   366  	client := &managedHAKeeperClient{cfg: cfg}
   367  	client.mu.client = c
   368  	defer func() {
   369  		require.NoError(t, client.Close())
   370  	}()
   371  	fn(t, client)
   372  }
   373  
   374  func TestGetClusterDetailsWhenNotConnectedToHAKeeper(t *testing.T) {
   375  	fn := func(t *testing.T, c *managedHAKeeperClient) {
   376  		oldc := c.mu.client
   377  		ctx, cancel := context.WithTimeout(context.Background(), time.Second)
   378  		defer cancel()
   379  		_, err := c.GetClusterDetails(ctx)
   380  		require.NoError(t, err)
   381  		require.True(t, oldc != c.mu.client)
   382  	}
   383  	testNotHAKeeperErrorIsHandled(t, fn)
   384  }
   385  
   386  func TestSendCNHeartbeatWhenNotConnectedToHAKeeper(t *testing.T) {
   387  	fn := func(t *testing.T, c *managedHAKeeperClient) {
   388  		oldc := c.mu.client
   389  		ctx, cancel := context.WithTimeout(context.Background(), time.Second)
   390  		defer cancel()
   391  		_, err := c.SendCNHeartbeat(ctx, pb.CNStoreHeartbeat{})
   392  		require.NoError(t, err)
   393  		require.True(t, oldc != c.mu.client)
   394  	}
   395  	testNotHAKeeperErrorIsHandled(t, fn)
   396  }
   397  
   398  func TestSendTNHeartbeatWhenNotConnectedToHAKeeper(t *testing.T) {
   399  	fn := func(t *testing.T, c *managedHAKeeperClient) {
   400  		oldc := c.mu.client
   401  		ctx, cancel := context.WithTimeout(context.Background(), time.Second)
   402  		defer cancel()
   403  		_, err := c.SendTNHeartbeat(ctx, pb.TNStoreHeartbeat{})
   404  		require.NoError(t, err)
   405  		require.True(t, oldc != c.mu.client)
   406  	}
   407  	testNotHAKeeperErrorIsHandled(t, fn)
   408  }
   409  
   410  func TestSendLogHeartbeatWhenNotConnectedToHAKeeper(t *testing.T) {
   411  	fn := func(t *testing.T, c *managedHAKeeperClient) {
   412  		oldc := c.mu.client
   413  		ctx, cancel := context.WithTimeout(context.Background(), time.Second)
   414  		defer cancel()
   415  		_, err := c.SendLogHeartbeat(ctx, pb.LogStoreHeartbeat{})
   416  		require.NoError(t, err)
   417  		require.True(t, oldc != c.mu.client)
   418  	}
   419  	testNotHAKeeperErrorIsHandled(t, fn)
   420  }
   421  
   422  func TestHAKeeperClientUpdateCNLabel(t *testing.T) {
   423  	fn := func(t *testing.T, s *Service) {
   424  		cfg := HAKeeperClientConfig{
   425  			ServiceAddresses: []string{testServiceAddress},
   426  		}
   427  		ctx, cancel := context.WithTimeout(context.Background(), time.Second)
   428  		defer cancel()
   429  		c1, err := NewProxyHAKeeperClient(ctx, cfg)
   430  		require.NoError(t, err)
   431  		c2, err := NewCNHAKeeperClient(ctx, cfg)
   432  		require.NoError(t, err)
   433  		defer func() {
   434  			assert.NoError(t, c1.Close())
   435  			assert.NoError(t, c2.Close())
   436  		}()
   437  
   438  		label := pb.CNStoreLabel{
   439  			UUID: s.ID(),
   440  			Labels: map[string]metadata.LabelList{
   441  				"account": {Labels: []string{"a", "b"}},
   442  				"role":    {Labels: []string{"1", "2"}},
   443  			},
   444  		}
   445  		err = c1.UpdateCNLabel(ctx, label)
   446  		require.Error(t, err)
   447  
   448  		hb := pb.CNStoreHeartbeat{
   449  			UUID:           s.ID(),
   450  			ServiceAddress: "addr1",
   451  		}
   452  		_, err = c2.SendCNHeartbeat(ctx, hb)
   453  		require.NoError(t, err)
   454  
   455  		label = pb.CNStoreLabel{
   456  			UUID: s.ID(),
   457  			Labels: map[string]metadata.LabelList{
   458  				"account": {Labels: []string{"a", "b"}},
   459  				"role":    {Labels: []string{"1", "2"}},
   460  			},
   461  		}
   462  		err = c1.UpdateCNLabel(ctx, label)
   463  		require.NoError(t, err)
   464  
   465  		state, err := c1.GetClusterState(ctx)
   466  		info, ok1 := state.CNState.Stores[s.ID()]
   467  		assert.True(t, ok1)
   468  		labels1, ok2 := info.Labels["account"]
   469  		assert.True(t, ok2)
   470  		assert.Equal(t, labels1.Labels, []string{"a", "b"})
   471  		labels2, ok3 := info.Labels["role"]
   472  		assert.True(t, ok3)
   473  		assert.Equal(t, labels2.Labels, []string{"1", "2"})
   474  		require.NoError(t, err)
   475  
   476  		label = pb.CNStoreLabel{
   477  			UUID: s.ID(),
   478  			Labels: map[string]metadata.LabelList{
   479  				"account": {Labels: []string{"a", "b"}},
   480  			},
   481  		}
   482  		err = c1.UpdateCNLabel(ctx, label)
   483  		require.NoError(t, err)
   484  
   485  		state, err = c1.GetClusterState(ctx)
   486  		require.NoError(t, err)
   487  		info, ok1 = state.CNState.Stores[s.ID()]
   488  		assert.True(t, ok1)
   489  		labels1, ok2 = info.Labels["account"]
   490  		assert.True(t, ok2)
   491  		assert.Equal(t, labels1.Labels, []string{"a", "b"})
   492  		_, ok3 = info.Labels["role"]
   493  		assert.False(t, ok3)
   494  	}
   495  	runServiceTest(t, true, true, fn)
   496  }
   497  
   498  func TestHAKeeperClientUpdateCNWorkState(t *testing.T) {
   499  	fn := func(t *testing.T, s *Service) {
   500  		cfg := HAKeeperClientConfig{
   501  			ServiceAddresses: []string{testServiceAddress},
   502  		}
   503  		ctx, cancel := context.WithTimeout(context.Background(), time.Second)
   504  		defer cancel()
   505  		c1, err := NewProxyHAKeeperClient(ctx, cfg)
   506  		require.NoError(t, err)
   507  		c2, err := NewCNHAKeeperClient(ctx, cfg)
   508  		require.NoError(t, err)
   509  		defer func() {
   510  			assert.NoError(t, c1.Close())
   511  			assert.NoError(t, c2.Close())
   512  		}()
   513  
   514  		workState := pb.CNWorkState{
   515  			UUID:  s.ID(),
   516  			State: metadata.WorkState_Unknown,
   517  		}
   518  		err = c1.UpdateCNWorkState(ctx, workState)
   519  		require.Error(t, err)
   520  
   521  		hb := pb.CNStoreHeartbeat{
   522  			UUID:           s.ID(),
   523  			ServiceAddress: "addr1",
   524  		}
   525  		_, err = c2.SendCNHeartbeat(ctx, hb)
   526  		require.NoError(t, err)
   527  
   528  		workState = pb.CNWorkState{
   529  			UUID:  s.ID(),
   530  			State: metadata.WorkState_Working,
   531  		}
   532  		err = c1.UpdateCNWorkState(ctx, workState)
   533  		require.NoError(t, err)
   534  
   535  		state, err := c1.GetClusterState(ctx)
   536  		require.NoError(t, err)
   537  		info, ok1 := state.CNState.Stores[s.ID()]
   538  		assert.True(t, ok1)
   539  		require.Equal(t, metadata.WorkState_Working, info.WorkState)
   540  
   541  		workState = pb.CNWorkState{
   542  			UUID:  s.ID(),
   543  			State: metadata.WorkState_Draining,
   544  		}
   545  		err = c1.UpdateCNWorkState(ctx, workState)
   546  		require.NoError(t, err)
   547  
   548  		state, err = c1.GetClusterState(ctx)
   549  		require.NoError(t, err)
   550  		info, ok1 = state.CNState.Stores[s.ID()]
   551  		assert.True(t, ok1)
   552  		require.Equal(t, metadata.WorkState_Draining, info.WorkState)
   553  
   554  		workState = pb.CNWorkState{
   555  			UUID:  s.ID(),
   556  			State: metadata.WorkState_Working,
   557  		}
   558  		err = c1.UpdateCNWorkState(ctx, workState)
   559  		require.NoError(t, err)
   560  
   561  		state, err = c1.GetClusterState(ctx)
   562  		require.NoError(t, err)
   563  		info, ok1 = state.CNState.Stores[s.ID()]
   564  		assert.True(t, ok1)
   565  		require.Equal(t, metadata.WorkState_Working, info.WorkState)
   566  	}
   567  	runServiceTest(t, true, true, fn)
   568  }
   569  
   570  func TestHAKeeperClientPatchCNStore(t *testing.T) {
   571  	fn := func(t *testing.T, s *Service) {
   572  		cfg := HAKeeperClientConfig{
   573  			ServiceAddresses: []string{testServiceAddress},
   574  		}
   575  		ctx, cancel := context.WithTimeout(context.Background(), time.Second)
   576  		defer cancel()
   577  		c1, err := NewProxyHAKeeperClient(ctx, cfg)
   578  		require.NoError(t, err)
   579  		c2, err := NewCNHAKeeperClient(ctx, cfg)
   580  		require.NoError(t, err)
   581  		defer func() {
   582  			assert.NoError(t, c1.Close())
   583  			assert.NoError(t, c2.Close())
   584  		}()
   585  
   586  		stateLabel := pb.CNStateLabel{
   587  			UUID:  s.ID(),
   588  			State: metadata.WorkState_Unknown,
   589  			Labels: map[string]metadata.LabelList{
   590  				"account": {Labels: []string{"a", "b"}},
   591  				"role":    {Labels: []string{"1", "2"}},
   592  			},
   593  		}
   594  		err = c1.PatchCNStore(ctx, stateLabel)
   595  		require.Error(t, err)
   596  
   597  		hb := pb.CNStoreHeartbeat{
   598  			UUID:           s.ID(),
   599  			ServiceAddress: "addr1",
   600  		}
   601  		_, err = c2.SendCNHeartbeat(ctx, hb)
   602  		require.NoError(t, err)
   603  
   604  		stateLabel = pb.CNStateLabel{
   605  			UUID:  s.ID(),
   606  			State: metadata.WorkState_Working,
   607  			Labels: map[string]metadata.LabelList{
   608  				"account": {Labels: []string{"a", "b"}},
   609  				"role":    {Labels: []string{"1", "2"}},
   610  			},
   611  		}
   612  		err = c1.PatchCNStore(ctx, stateLabel)
   613  		require.NoError(t, err)
   614  
   615  		state, err := c1.GetClusterState(ctx)
   616  		require.NoError(t, err)
   617  		info, ok1 := state.CNState.Stores[s.ID()]
   618  		assert.True(t, ok1)
   619  		require.Equal(t, metadata.WorkState_Working, info.WorkState)
   620  		labels1, ok2 := info.Labels["account"]
   621  		assert.True(t, ok2)
   622  		assert.Equal(t, labels1.Labels, []string{"a", "b"})
   623  		labels2, ok3 := info.Labels["role"]
   624  		assert.True(t, ok3)
   625  		assert.Equal(t, labels2.Labels, []string{"1", "2"})
   626  
   627  		stateLabel = pb.CNStateLabel{
   628  			UUID:  s.ID(),
   629  			State: metadata.WorkState_Draining,
   630  		}
   631  		err = c1.PatchCNStore(ctx, stateLabel)
   632  		require.NoError(t, err)
   633  
   634  		state, err = c1.GetClusterState(ctx)
   635  		require.NoError(t, err)
   636  		info, ok1 = state.CNState.Stores[s.ID()]
   637  		assert.True(t, ok1)
   638  		require.Equal(t, metadata.WorkState_Draining, info.WorkState)
   639  		labels1, ok2 = info.Labels["account"]
   640  		assert.True(t, ok2)
   641  		labels2, ok3 = info.Labels["role"]
   642  		assert.True(t, ok3)
   643  		assert.Equal(t, labels2.Labels, []string{"1", "2"})
   644  
   645  		stateLabel = pb.CNStateLabel{
   646  			UUID: s.ID(),
   647  			Labels: map[string]metadata.LabelList{
   648  				"account": {Labels: []string{"a", "b"}},
   649  			},
   650  		}
   651  		err = c1.PatchCNStore(ctx, stateLabel)
   652  		require.NoError(t, err)
   653  
   654  		state, err = c1.GetClusterState(ctx)
   655  		require.NoError(t, err)
   656  		info, ok1 = state.CNState.Stores[s.ID()]
   657  		assert.True(t, ok1)
   658  		require.Equal(t, metadata.WorkState_Working, info.WorkState)
   659  		labels1, ok2 = info.Labels["account"]
   660  		assert.True(t, ok2)
   661  		assert.Equal(t, labels1.Labels, []string{"a", "b"})
   662  		labels2, ok3 = info.Labels["role"]
   663  		assert.False(t, ok3)
   664  	}
   665  	runServiceTest(t, true, true, fn)
   666  }
   667  
   668  func TestHAKeeperClientDeleteCNStore(t *testing.T) {
   669  	fn := func(t *testing.T, s *Service) {
   670  		cfg := HAKeeperClientConfig{
   671  			ServiceAddresses: []string{testServiceAddress},
   672  		}
   673  		ctx, cancel := context.WithTimeout(context.Background(), time.Second)
   674  		defer cancel()
   675  		c1, err := NewProxyHAKeeperClient(ctx, cfg)
   676  		require.NoError(t, err)
   677  		c2, err := NewCNHAKeeperClient(ctx, cfg)
   678  		require.NoError(t, err)
   679  		defer func() {
   680  			assert.NoError(t, c1.Close())
   681  			assert.NoError(t, c2.Close())
   682  		}()
   683  
   684  		hb := pb.CNStoreHeartbeat{
   685  			UUID:           s.ID(),
   686  			ServiceAddress: "addr1",
   687  		}
   688  		_, err = c2.SendCNHeartbeat(ctx, hb)
   689  		require.NoError(t, err)
   690  		state, err := c1.GetClusterState(ctx)
   691  		require.NoError(t, err)
   692  		_, ok := state.CNState.Stores[s.ID()]
   693  		assert.True(t, ok)
   694  
   695  		cnStore := pb.DeleteCNStore{
   696  			StoreID: s.ID(),
   697  		}
   698  		err = c1.DeleteCNStore(ctx, cnStore)
   699  		require.NoError(t, err)
   700  
   701  		state, err = c1.GetClusterState(ctx)
   702  		require.NoError(t, err)
   703  		_, ok = state.CNState.Stores[s.ID()]
   704  		assert.False(t, ok)
   705  	}
   706  	runServiceTest(t, true, true, fn)
   707  }
   708  
   709  func TestHAKeeperClientSendProxyHeartbeat(t *testing.T) {
   710  	fn := func(t *testing.T, s *Service) {
   711  		cfg := HAKeeperClientConfig{
   712  			ServiceAddresses: []string{testServiceAddress},
   713  		}
   714  		ctx, cancel := context.WithTimeout(context.Background(), time.Second)
   715  		defer cancel()
   716  		c1, err := NewProxyHAKeeperClient(ctx, cfg)
   717  		require.NoError(t, err)
   718  		defer func() {
   719  			assert.NoError(t, c1.Close())
   720  		}()
   721  
   722  		hb := pb.ProxyHeartbeat{
   723  			UUID:          s.ID(),
   724  			ListenAddress: "addr1",
   725  		}
   726  		cb, err := c1.SendProxyHeartbeat(ctx, hb)
   727  		require.NoError(t, err)
   728  		assert.Equal(t, 0, len(cb.Commands))
   729  
   730  		cd, err := c1.GetClusterDetails(ctx)
   731  		require.NoError(t, err)
   732  		p := pb.ProxyStore{
   733  			UUID:          s.ID(),
   734  			ListenAddress: "addr1",
   735  		}
   736  		assert.Equal(t, []pb.ProxyStore{p}, cd.ProxyStores)
   737  	}
   738  	runServiceTest(t, true, true, fn)
   739  }