// Licensed to the LF AI & Data foundation under one // or more contributor license agreements. See the NOTICE file // distributed with this work for additional information // regarding copyright ownership. The ASF licenses this file // to you under the Apache License, Version 2.0 (the // "License"); you may not use this file except in compliance // with the License. You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package proxy import ( "context" "testing" "github.com/pkg/errors" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" "github.com/milvus-io/milvus-proto/go-api/commonpb" "github.com/milvus-io/milvus-proto/go-api/milvuspb" "github.com/milvus-io/milvus/internal/log" "github.com/milvus-io/milvus/internal/mocks" "github.com/milvus-io/milvus/internal/proto/proxypb" "github.com/milvus-io/milvus/internal/util/dependency" "github.com/milvus-io/milvus/internal/util/paramtable" "github.com/milvus-io/milvus/internal/util/sessionutil" ) func TestProxy_InvalidateCollectionMetaCache_remove_stream(t *testing.T) { paramtable.Init() cache := globalMetaCache globalMetaCache = nil defer func() { globalMetaCache = cache }() chMgr := newMockChannelsMgr() chMgr.removeDMLStreamFuncType = func(collectionID UniqueID) error { log.Debug("TestProxy_InvalidateCollectionMetaCache_remove_stream, remove dml stream") return nil } node := &Proxy{chMgr: chMgr} node.stateCode.Store(commonpb.StateCode_Healthy) ctx := context.Background() req := &proxypb.InvalidateCollMetaCacheRequest{ Base: &commonpb.MsgBase{MsgType: commonpb.MsgType_DropCollection}, } status, err := node.InvalidateCollectionMetaCache(ctx, req) assert.NoError(t, err) assert.Equal(t, commonpb.ErrorCode_Success, status.GetErrorCode()) } func TestProxy_CheckHealth(t *testing.T) { t.Run("not healthy", func(t *testing.T) { node := &Proxy{session: &sessionutil.Session{ServerID: 1}} node.multiRateLimiter = NewMultiRateLimiter() node.stateCode.Store(commonpb.StateCode_Abnormal) ctx := context.Background() resp, err := node.CheckHealth(ctx, &milvuspb.CheckHealthRequest{}) assert.NoError(t, err) assert.Equal(t, false, resp.IsHealthy) assert.Equal(t, 1, len(resp.Reasons)) }) t.Run("proxy health check is ok", func(t *testing.T) { node := &Proxy{ rootCoord: NewRootCoordMock(), queryCoord: NewQueryCoordMock(), dataCoord: NewDataCoordMock(), session: &sessionutil.Session{ServerID: 1}, } node.multiRateLimiter = NewMultiRateLimiter() node.stateCode.Store(commonpb.StateCode_Healthy) ctx := context.Background() resp, err := node.CheckHealth(ctx, &milvuspb.CheckHealthRequest{}) assert.NoError(t, err) assert.Equal(t, true, resp.IsHealthy) assert.Empty(t, resp.Reasons) }) t.Run("proxy health check is fail", func(t *testing.T) { checkHealthFunc1 := func(ctx context.Context, req *milvuspb.CheckHealthRequest) (*milvuspb.CheckHealthResponse, error) { return &milvuspb.CheckHealthResponse{ IsHealthy: false, Reasons: []string{"unHealth"}, }, nil } checkHealthFunc2 := func(ctx context.Context, req *milvuspb.CheckHealthRequest) (*milvuspb.CheckHealthResponse, error) { return nil, errors.New("test") } dataCoordMock := NewDataCoordMock() dataCoordMock.checkHealthFunc = checkHealthFunc1 node := &Proxy{ session: &sessionutil.Session{ServerID: 1}, rootCoord: NewRootCoordMock(func(mock *RootCoordMock) { mock.checkHealthFunc = checkHealthFunc1 }), queryCoord: NewQueryCoordMock(func(mock *QueryCoordMock) { mock.checkHealthFunc = checkHealthFunc2 }), dataCoord: dataCoordMock} node.multiRateLimiter = NewMultiRateLimiter() node.stateCode.Store(commonpb.StateCode_Healthy) ctx := context.Background() resp, err := node.CheckHealth(ctx, &milvuspb.CheckHealthRequest{}) assert.NoError(t, err) assert.Equal(t, false, resp.IsHealthy) assert.Equal(t, 3, len(resp.Reasons)) }) t.Run("check quota state", func(t *testing.T) { node := &Proxy{ rootCoord: NewRootCoordMock(), dataCoord: NewDataCoordMock(), queryCoord: NewQueryCoordMock(), } node.multiRateLimiter = NewMultiRateLimiter() node.stateCode.Store(commonpb.StateCode_Healthy) resp, err := node.CheckHealth(context.Background(), &milvuspb.CheckHealthRequest{}) assert.NoError(t, err) assert.Equal(t, true, resp.IsHealthy) assert.Equal(t, 0, len(resp.GetQuotaStates())) assert.Equal(t, 0, len(resp.GetReasons())) states := []milvuspb.QuotaState{milvuspb.QuotaState_DenyToWrite, milvuspb.QuotaState_DenyToRead} codes := []commonpb.ErrorCode{commonpb.ErrorCode_MemoryQuotaExhausted, commonpb.ErrorCode_ForceDeny} node.multiRateLimiter.SetQuotaStates(states, codes) resp, err = node.CheckHealth(context.Background(), &milvuspb.CheckHealthRequest{}) assert.NoError(t, err) assert.Equal(t, true, resp.IsHealthy) assert.Equal(t, 2, len(resp.GetQuotaStates())) assert.Equal(t, 2, len(resp.GetReasons())) }) } func TestProxyRenameCollection(t *testing.T) { t.Run("not healthy", func(t *testing.T) { node := &Proxy{session: &sessionutil.Session{ServerID: 1}} node.stateCode.Store(commonpb.StateCode_Abnormal) ctx := context.Background() resp, err := node.RenameCollection(ctx, &milvuspb.RenameCollectionRequest{}) assert.NoError(t, err) assert.Equal(t, commonpb.ErrorCode_UnexpectedError, resp.GetErrorCode()) }) t.Run("rename with illegal new collection name", func(t *testing.T) { node := &Proxy{session: &sessionutil.Session{ServerID: 1}} node.stateCode.Store(commonpb.StateCode_Healthy) ctx := context.Background() resp, err := node.RenameCollection(ctx, &milvuspb.RenameCollectionRequest{NewName: "$#^%#&#$*!)#@!"}) assert.NoError(t, err) assert.Equal(t, commonpb.ErrorCode_IllegalCollectionName, resp.GetErrorCode()) }) t.Run("rename fail", func(t *testing.T) { rc := mocks.NewRootCoord(t) rc.On("RenameCollection", mock.Anything, mock.Anything). Return(nil, errors.New("fail")) node := &Proxy{ session: &sessionutil.Session{ServerID: 1}, rootCoord: rc, } node.stateCode.Store(commonpb.StateCode_Healthy) ctx := context.Background() resp, err := node.RenameCollection(ctx, &milvuspb.RenameCollectionRequest{NewName: "new"}) assert.Error(t, err) assert.Equal(t, commonpb.ErrorCode_UnexpectedError, resp.GetErrorCode()) }) t.Run("rename ok", func(t *testing.T) { rc := mocks.NewRootCoord(t) rc.On("RenameCollection", mock.Anything, mock.Anything). Return(&commonpb.Status{ ErrorCode: commonpb.ErrorCode_Success, }, nil) node := &Proxy{ session: &sessionutil.Session{ServerID: 1}, rootCoord: rc, } node.stateCode.Store(commonpb.StateCode_Healthy) ctx := context.Background() resp, err := node.RenameCollection(ctx, &milvuspb.RenameCollectionRequest{NewName: "new"}) assert.NoError(t, err) assert.Equal(t, commonpb.ErrorCode_Success, resp.GetErrorCode()) }) } func TestProxy_ResourceGroup(t *testing.T) { factory := dependency.NewDefaultFactory(true) ctx := context.Background() node, err := NewProxy(ctx, factory) assert.NoError(t, err) node.multiRateLimiter = NewMultiRateLimiter() node.stateCode.Store(commonpb.StateCode_Healthy) qc := NewQueryCoordMock() node.SetQueryCoordClient(qc) tsoAllocatorIns := newMockTsoAllocator() node.sched, err = newTaskScheduler(node.ctx, tsoAllocatorIns, node.factory) assert.NoError(t, err) node.sched.Start() defer node.sched.Close() rc := &MockRootCoordClientInterface{} mgr := newShardClientMgr() InitMetaCache(ctx, rc, qc, mgr) t.Run("create resource group", func(t *testing.T) { resp, err := node.CreateResourceGroup(ctx, &milvuspb.CreateResourceGroupRequest{ ResourceGroup: "rg", }) assert.NoError(t, err) assert.Equal(t, resp.ErrorCode, commonpb.ErrorCode_Success) }) t.Run("drop resource group", func(t *testing.T) { resp, err := node.DropResourceGroup(ctx, &milvuspb.DropResourceGroupRequest{ ResourceGroup: "rg", }) assert.NoError(t, err) assert.Equal(t, resp.ErrorCode, commonpb.ErrorCode_Success) }) t.Run("transfer node", func(t *testing.T) { resp, err := node.TransferNode(ctx, &milvuspb.TransferNodeRequest{ SourceResourceGroup: "rg1", TargetResourceGroup: "rg2", NumNode: 1, }) assert.NoError(t, err) assert.Equal(t, resp.ErrorCode, commonpb.ErrorCode_Success) }) t.Run("transfer replica", func(t *testing.T) { resp, err := node.TransferReplica(ctx, &milvuspb.TransferReplicaRequest{ SourceResourceGroup: "rg1", TargetResourceGroup: "rg2", NumReplica: 1, CollectionName: "collection1", }) assert.NoError(t, err) assert.Equal(t, resp.ErrorCode, commonpb.ErrorCode_Success) }) t.Run("list resource group", func(t *testing.T) { resp, err := node.ListResourceGroups(ctx, &milvuspb.ListResourceGroupsRequest{}) assert.NoError(t, err) assert.Equal(t, resp.Status.ErrorCode, commonpb.ErrorCode_Success) }) t.Run("describe resource group", func(t *testing.T) { resp, err := node.DescribeResourceGroup(ctx, &milvuspb.DescribeResourceGroupRequest{ ResourceGroup: "rg", }) assert.NoError(t, err) assert.Equal(t, resp.Status.ErrorCode, commonpb.ErrorCode_Success) }) } func TestProxy_InvalidResourceGroupName(t *testing.T) { factory := dependency.NewDefaultFactory(true) ctx := context.Background() node, err := NewProxy(ctx, factory) assert.NoError(t, err) node.multiRateLimiter = NewMultiRateLimiter() node.stateCode.Store(commonpb.StateCode_Healthy) qc := NewQueryCoordMock() node.SetQueryCoordClient(qc) tsoAllocatorIns := newMockTsoAllocator() node.sched, err = newTaskScheduler(node.ctx, tsoAllocatorIns, node.factory) assert.NoError(t, err) node.sched.Start() defer node.sched.Close() rc := &MockRootCoordClientInterface{} mgr := newShardClientMgr() InitMetaCache(ctx, rc, qc, mgr) t.Run("create resource group", func(t *testing.T) { resp, err := node.CreateResourceGroup(ctx, &milvuspb.CreateResourceGroupRequest{ ResourceGroup: "...", }) assert.NoError(t, err) assert.Equal(t, resp.ErrorCode, commonpb.ErrorCode_IllegalArgument) }) t.Run("drop resource group", func(t *testing.T) { resp, err := node.DropResourceGroup(ctx, &milvuspb.DropResourceGroupRequest{ ResourceGroup: "...", }) assert.NoError(t, err) assert.Equal(t, resp.ErrorCode, commonpb.ErrorCode_Success) }) t.Run("transfer node", func(t *testing.T) { resp, err := node.TransferNode(ctx, &milvuspb.TransferNodeRequest{ SourceResourceGroup: "...", TargetResourceGroup: "!!!", NumNode: 1, }) assert.NoError(t, err) assert.Equal(t, resp.ErrorCode, commonpb.ErrorCode_IllegalArgument) }) t.Run("transfer replica", func(t *testing.T) { resp, err := node.TransferReplica(ctx, &milvuspb.TransferReplicaRequest{ SourceResourceGroup: "...", TargetResourceGroup: "!!!", NumReplica: 1, CollectionName: "collection1", }) assert.NoError(t, err) assert.Equal(t, resp.ErrorCode, commonpb.ErrorCode_IllegalArgument) }) }