github.com/pingcap/tiflow@v0.0.0-20240520035814-5bf52d54e205/engine/pkg/externalresource/manager/test_utils.go (about)

     1  // Copyright 2022 PingCAP, Inc.
     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  // See the License for the specific language governing permissions and
    12  // limitations under the License.
    13  
    14  package manager
    15  
    16  import (
    17  	"context"
    18  	"sync"
    19  	"testing"
    20  	"time"
    21  
    22  	frameModel "github.com/pingcap/tiflow/engine/framework/model"
    23  	"github.com/pingcap/tiflow/engine/model"
    24  	"github.com/pingcap/tiflow/engine/pkg/externalresource/internal"
    25  	resModel "github.com/pingcap/tiflow/engine/pkg/externalresource/model"
    26  	"github.com/pingcap/tiflow/engine/pkg/notifier"
    27  	pkgOrm "github.com/pingcap/tiflow/engine/pkg/orm"
    28  	"github.com/pingcap/tiflow/pkg/errors"
    29  	"github.com/stretchr/testify/require"
    30  )
    31  
    32  // MockExecutorInfoProvider implements ExecutorInfoProvider interface
    33  type MockExecutorInfoProvider struct {
    34  	mu          sync.RWMutex
    35  	executorSet map[resModel.ExecutorID]string
    36  	notifier    *notifier.Notifier[model.ExecutorStatusChange]
    37  }
    38  
    39  // NewMockExecutorInfoProvider creates a new MockExecutorInfoProvider instance
    40  func NewMockExecutorInfoProvider() *MockExecutorInfoProvider {
    41  	return &MockExecutorInfoProvider{
    42  		executorSet: make(map[resModel.ExecutorID]string),
    43  		notifier:    notifier.NewNotifier[model.ExecutorStatusChange](),
    44  	}
    45  }
    46  
    47  // AddExecutor adds an executor to the mock.
    48  func (p *MockExecutorInfoProvider) AddExecutor(executorID string, addr string) {
    49  	p.mu.Lock()
    50  	defer p.mu.Unlock()
    51  
    52  	p.executorSet[resModel.ExecutorID(executorID)] = addr
    53  	p.notifier.Notify(model.ExecutorStatusChange{
    54  		ID:   model.ExecutorID(executorID),
    55  		Tp:   model.EventExecutorOnline,
    56  		Addr: addr,
    57  	})
    58  }
    59  
    60  // RemoveExecutor removes an executor from the mock.
    61  func (p *MockExecutorInfoProvider) RemoveExecutor(executorID string) {
    62  	p.mu.Lock()
    63  	defer p.mu.Unlock()
    64  
    65  	addr := p.executorSet[resModel.ExecutorID(executorID)]
    66  	delete(p.executorSet, resModel.ExecutorID(executorID))
    67  	p.notifier.Notify(model.ExecutorStatusChange{
    68  		ID:   model.ExecutorID(executorID),
    69  		Tp:   model.EventExecutorOffline,
    70  		Addr: addr,
    71  	})
    72  }
    73  
    74  // HasExecutor returns whether the mock contains the given executor.
    75  func (p *MockExecutorInfoProvider) HasExecutor(executorID string) bool {
    76  	p.mu.RLock()
    77  	defer p.mu.RUnlock()
    78  
    79  	_, exists := p.executorSet[resModel.ExecutorID(executorID)]
    80  	return exists
    81  }
    82  
    83  // ListExecutors lists all executors.
    84  func (p *MockExecutorInfoProvider) ListExecutors() (ret []string) {
    85  	p.mu.RLock()
    86  	defer p.mu.RUnlock()
    87  
    88  	for id := range p.executorSet {
    89  		ret = append(ret, string(id))
    90  	}
    91  	return
    92  }
    93  
    94  // WatchExecutors implements ExecutorManager.WatchExecutors
    95  func (p *MockExecutorInfoProvider) WatchExecutors(
    96  	ctx context.Context,
    97  ) (map[model.ExecutorID]string, *notifier.Receiver[model.ExecutorStatusChange], error) {
    98  	p.mu.Lock()
    99  	defer p.mu.Unlock()
   100  
   101  	executors := make(map[model.ExecutorID]string, len(p.executorSet))
   102  	for id, addr := range p.executorSet {
   103  		executors[id] = addr
   104  	}
   105  
   106  	return executors, p.notifier.NewReceiver(), nil
   107  }
   108  
   109  // MockJobStatusProvider implements ExecutorManager interface
   110  type MockJobStatusProvider struct {
   111  	mu       sync.RWMutex
   112  	jobInfos JobStatusesSnapshot
   113  	notifier *notifier.Notifier[JobStatusChangeEvent]
   114  }
   115  
   116  // NewMockJobStatusProvider returns a new instance of MockJobStatusProvider.
   117  func NewMockJobStatusProvider() *MockJobStatusProvider {
   118  	return &MockJobStatusProvider{
   119  		jobInfos: make(JobStatusesSnapshot),
   120  		notifier: notifier.NewNotifier[JobStatusChangeEvent](),
   121  	}
   122  }
   123  
   124  // SetJobStatus upserts the status of a given job.
   125  func (jp *MockJobStatusProvider) SetJobStatus(jobID frameModel.MasterID, status JobStatus) {
   126  	jp.mu.Lock()
   127  	defer jp.mu.Unlock()
   128  
   129  	jp.jobInfos[jobID] = status
   130  }
   131  
   132  // RemoveJob removes a job from the mock.
   133  func (jp *MockJobStatusProvider) RemoveJob(jobID frameModel.MasterID) {
   134  	jp.mu.Lock()
   135  	defer jp.mu.Unlock()
   136  
   137  	delete(jp.jobInfos, jobID)
   138  	jp.notifier.Notify(JobStatusChangeEvent{
   139  		EventType: JobRemovedEvent,
   140  		JobID:     jobID,
   141  	})
   142  }
   143  
   144  // GetJobStatuses implements the interface JobStatusProvider.
   145  func (jp *MockJobStatusProvider) GetJobStatuses(ctx context.Context) (JobStatusesSnapshot, error) {
   146  	jp.mu.RLock()
   147  	defer jp.mu.RUnlock()
   148  
   149  	return jp.jobInfos, nil
   150  }
   151  
   152  // WatchJobStatuses implements the interface JobStatusProvider.
   153  func (jp *MockJobStatusProvider) WatchJobStatuses(
   154  	ctx context.Context,
   155  ) (JobStatusesSnapshot, *notifier.Receiver[JobStatusChangeEvent], error) {
   156  	jp.mu.Lock()
   157  	defer jp.mu.Unlock()
   158  
   159  	snapCopy := make(JobStatusesSnapshot, len(jp.jobInfos))
   160  	for k, v := range jp.jobInfos {
   161  		snapCopy[k] = v
   162  	}
   163  
   164  	return snapCopy, jp.notifier.NewReceiver(), nil
   165  }
   166  
   167  // MockGCRunner implements the interface GCRunner.
   168  type MockGCRunner struct {
   169  	GCRunner
   170  	notifyCh chan struct{}
   171  }
   172  
   173  // NewMockGCRunner returns a new MockGCNotifier
   174  func NewMockGCRunner(resClient pkgOrm.ResourceClient) *MockGCRunner {
   175  	runner := NewGCRunner(resClient, nil, nil)
   176  	runner.gcHandlers[resModel.ResourceTypeS3] = &mockResourceController{
   177  		gcExecutorCh: make(chan []*resModel.ResourceMeta, 128),
   178  	}
   179  	return &MockGCRunner{
   180  		GCRunner: runner,
   181  		notifyCh: make(chan struct{}, 1),
   182  	}
   183  }
   184  
   185  // GCNotify pushes a new notification to the internal channel so
   186  // it can be waited on by WaitNotify().
   187  func (n *MockGCRunner) GCNotify() {
   188  	select {
   189  	case n.notifyCh <- struct{}{}:
   190  	default:
   191  	}
   192  }
   193  
   194  // WaitNotify waits for a pending notification with timeout.
   195  func (n *MockGCRunner) WaitNotify(t *testing.T, timeout time.Duration) {
   196  	timer := time.NewTimer(timeout)
   197  	defer timer.Stop()
   198  
   199  	select {
   200  	case <-timer.C:
   201  		require.FailNow(t, "WaitNotify has timed out")
   202  	case <-n.notifyCh:
   203  	}
   204  }
   205  
   206  type mockResourceController struct {
   207  	internal.ResourceController
   208  	gcRequestCh  chan *resModel.ResourceMeta
   209  	gcExecutorCh chan []*resModel.ResourceMeta
   210  }
   211  
   212  func (r *mockResourceController) GCSingleResource(
   213  	ctx context.Context, res *resModel.ResourceMeta,
   214  ) error {
   215  	select {
   216  	case <-ctx.Done():
   217  		return errors.Trace(ctx.Err())
   218  	case r.gcRequestCh <- res:
   219  	}
   220  	return nil
   221  }
   222  
   223  func (r *mockResourceController) GCExecutor(
   224  	ctx context.Context, resources []*resModel.ResourceMeta, executorID model.ExecutorID,
   225  ) error {
   226  	select {
   227  	case <-ctx.Done():
   228  		return errors.Trace(ctx.Err())
   229  	case r.gcExecutorCh <- resources:
   230  	}
   231  	return nil
   232  }