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 }