github.com/kaisenlinux/docker.io@v0.0.0-20230510090727-ea55db55fac7/swarmkit/agent/testutils/fakes.go (about) 1 package testutils 2 3 import ( 4 "context" 5 "io/ioutil" 6 "net" 7 "os" 8 "path/filepath" 9 "sync" 10 "testing" 11 "time" 12 13 "google.golang.org/grpc" 14 15 "github.com/docker/swarmkit/agent/exec" 16 "github.com/docker/swarmkit/api" 17 "github.com/docker/swarmkit/ca" 18 "github.com/docker/swarmkit/identity" 19 "github.com/docker/swarmkit/log" 20 "github.com/stretchr/testify/require" 21 ) 22 23 // TestExecutor is executor for integration tests 24 type TestExecutor struct { 25 mu sync.Mutex 26 desc *api.NodeDescription 27 } 28 29 // Describe just returns empty NodeDescription. 30 func (e *TestExecutor) Describe(ctx context.Context) (*api.NodeDescription, error) { 31 e.mu.Lock() 32 defer e.mu.Unlock() 33 if e.desc == nil { 34 return &api.NodeDescription{}, nil 35 } 36 return e.desc.Copy(), nil 37 } 38 39 // Configure does nothing. 40 func (e *TestExecutor) Configure(ctx context.Context, node *api.Node) error { 41 return nil 42 } 43 44 // SetNetworkBootstrapKeys does nothing. 45 func (e *TestExecutor) SetNetworkBootstrapKeys([]*api.EncryptionKey) error { 46 return nil 47 } 48 49 // Controller returns TestController. 50 func (e *TestExecutor) Controller(t *api.Task) (exec.Controller, error) { 51 return &TestController{ 52 ch: make(chan struct{}), 53 }, nil 54 } 55 56 // UpdateNodeDescription sets the node description on the test executor 57 func (e *TestExecutor) UpdateNodeDescription(newDesc *api.NodeDescription) { 58 e.mu.Lock() 59 defer e.mu.Unlock() 60 e.desc = newDesc 61 } 62 63 // TestController is dummy channel based controller for tests. 64 type TestController struct { 65 ch chan struct{} 66 closeOnce sync.Once 67 } 68 69 // Update does nothing. 70 func (t *TestController) Update(ctx context.Context, task *api.Task) error { 71 return nil 72 } 73 74 // Prepare does nothing. 75 func (t *TestController) Prepare(ctx context.Context) error { 76 return nil 77 } 78 79 // Start does nothing. 80 func (t *TestController) Start(ctx context.Context) error { 81 return nil 82 } 83 84 // Wait waits on internal channel. 85 func (t *TestController) Wait(ctx context.Context) error { 86 select { 87 case <-t.ch: 88 case <-ctx.Done(): 89 } 90 return nil 91 } 92 93 // Shutdown closes internal channel 94 func (t *TestController) Shutdown(ctx context.Context) error { 95 t.closeOnce.Do(func() { 96 close(t.ch) 97 }) 98 return nil 99 } 100 101 // Terminate closes internal channel if it wasn't closed before. 102 func (t *TestController) Terminate(ctx context.Context) error { 103 t.closeOnce.Do(func() { 104 close(t.ch) 105 }) 106 return nil 107 } 108 109 // Remove does nothing. 110 func (t *TestController) Remove(ctx context.Context) error { 111 return nil 112 } 113 114 // Close does nothing. 115 func (t *TestController) Close() error { 116 t.closeOnce.Do(func() { 117 close(t.ch) 118 }) 119 return nil 120 } 121 122 // SessionHandler is an injectable function that can be used handle session requests 123 type SessionHandler func(*api.SessionRequest, api.Dispatcher_SessionServer) error 124 125 // MockDispatcher is a fake dispatcher that one agent at a time can connect to 126 type MockDispatcher struct { 127 mu sync.Mutex 128 sessionCh chan *api.SessionMessage 129 openSession *api.SessionRequest 130 closedSessions []*api.SessionRequest 131 sessionHandler SessionHandler 132 133 Addr string 134 } 135 136 // UpdateTaskStatus is not implemented 137 func (m *MockDispatcher) UpdateTaskStatus(context.Context, *api.UpdateTaskStatusRequest) (*api.UpdateTaskStatusResponse, error) { 138 panic("not implemented") 139 } 140 141 // Tasks keeps an open stream until canceled 142 func (m *MockDispatcher) Tasks(_ *api.TasksRequest, stream api.Dispatcher_TasksServer) error { 143 <-stream.Context().Done() 144 return nil 145 } 146 147 // Assignments keeps an open stream until canceled 148 func (m *MockDispatcher) Assignments(_ *api.AssignmentsRequest, stream api.Dispatcher_AssignmentsServer) error { 149 <-stream.Context().Done() 150 return nil 151 } 152 153 // Heartbeat always successfully heartbeats 154 func (m *MockDispatcher) Heartbeat(context.Context, *api.HeartbeatRequest) (*api.HeartbeatResponse, error) { 155 return &api.HeartbeatResponse{Period: time.Second * 5}, nil 156 } 157 158 // Session allows a session to be established, and sends the node info 159 func (m *MockDispatcher) Session(r *api.SessionRequest, stream api.Dispatcher_SessionServer) error { 160 m.mu.Lock() 161 handler := m.sessionHandler 162 m.openSession = r 163 m.mu.Unlock() 164 sessionID := identity.NewID() 165 166 defer func() { 167 m.mu.Lock() 168 defer m.mu.Unlock() 169 log.G(stream.Context()).Debugf("non-dispatcher side closed session: %s", sessionID) 170 m.closedSessions = append(m.closedSessions, r) 171 if m.openSession == r { // only overwrite session if it hasn't changed 172 m.openSession = nil 173 } 174 }() 175 176 if handler != nil { 177 return handler(r, stream) 178 } 179 180 // send the initial message first 181 if err := stream.Send(&api.SessionMessage{ 182 SessionID: sessionID, 183 Managers: []*api.WeightedPeer{ 184 { 185 Peer: &api.Peer{Addr: m.Addr}, 186 }, 187 }, 188 }); err != nil { 189 return err 190 } 191 192 ctx := stream.Context() 193 for { 194 select { 195 case msg := <-m.sessionCh: 196 msg.SessionID = sessionID 197 if err := stream.Send(msg); err != nil { 198 return err 199 } 200 case <-ctx.Done(): 201 return nil 202 } 203 } 204 } 205 206 // GetSessions return all the established and closed sessions 207 func (m *MockDispatcher) GetSessions() (*api.SessionRequest, []*api.SessionRequest) { 208 m.mu.Lock() 209 defer m.mu.Unlock() 210 return m.openSession, m.closedSessions 211 } 212 213 // SessionMessageChannel returns a writable channel to inject session messages 214 func (m *MockDispatcher) SessionMessageChannel() chan<- *api.SessionMessage { 215 return m.sessionCh 216 } 217 218 // SetSessionHandler lets you inject a custom function to handle session requests 219 func (m *MockDispatcher) SetSessionHandler(s SessionHandler) { 220 m.mu.Lock() 221 defer m.mu.Unlock() 222 m.sessionHandler = s 223 } 224 225 // NewMockDispatcher starts and returns a mock dispatcher instance that can be connected to 226 func NewMockDispatcher(t *testing.T, secConfig *ca.SecurityConfig, local bool) (*MockDispatcher, func()) { 227 var ( 228 l net.Listener 229 err error 230 addr string 231 cleanup func() 232 ) 233 if local { 234 tempDir, err := ioutil.TempDir("", "local-dispatcher-socket") 235 require.NoError(t, err) 236 addr = filepath.Join(tempDir, "socket") 237 l, err = net.Listen("unix", addr) 238 require.NoError(t, err) 239 cleanup = func() { 240 os.RemoveAll(tempDir) 241 } 242 } else { 243 l, err = net.Listen("tcp", "127.0.0.1:0") 244 require.NoError(t, err) 245 addr = l.Addr().String() 246 } 247 248 serverOpts := []grpc.ServerOption{grpc.Creds(secConfig.ServerTLSCreds)} 249 s := grpc.NewServer(serverOpts...) 250 251 m := &MockDispatcher{ 252 Addr: addr, 253 sessionCh: make(chan *api.SessionMessage, 1), 254 } 255 api.RegisterDispatcherServer(s, m) 256 go s.Serve(l) 257 return m, func() { 258 l.Close() 259 s.Stop() 260 if cleanup != nil { 261 cleanup() 262 } 263 } 264 }