github.com/minio/minio@v0.0.0-20240328213742-3f72439b8a27/internal/grid/debug.go (about) 1 // Copyright (c) 2015-2023 MinIO, Inc. 2 // 3 // This file is part of MinIO Object Storage stack 4 // 5 // This program is free software: you can redistribute it and/or modify 6 // it under the terms of the GNU Affero General Public License as published by 7 // the Free Software Foundation, either version 3 of the License, or 8 // (at your option) any later version. 9 // 10 // This program is distributed in the hope that it will be useful 11 // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 // GNU Affero General Public License for more details. 14 // 15 // You should have received a copy of the GNU Affero General Public License 16 // along with this program. If not, see <http://www.gnu.org/licenses/>. 17 18 package grid 19 20 import ( 21 "context" 22 "fmt" 23 "net" 24 "net/http" 25 "net/http/httptest" 26 "sync" 27 "time" 28 29 xioutil "github.com/minio/minio/internal/ioutil" 30 "github.com/minio/mux" 31 ) 32 33 //go:generate stringer -type=debugMsg $GOFILE 34 35 // debugMsg is a debug message for testing purposes. 36 // may only be used for tests. 37 type debugMsg int 38 39 const ( 40 debugPrint = false 41 debugReqs = false 42 ) 43 44 const ( 45 debugShutdown debugMsg = iota 46 debugKillInbound 47 debugKillOutbound 48 debugWaitForExit 49 debugSetConnPingDuration 50 debugSetClientPingDuration 51 debugAddToDeadline 52 debugIsOutgoingClosed 53 ) 54 55 // TestGrid contains a grid of servers for testing purposes. 56 type TestGrid struct { 57 Servers []*httptest.Server 58 Listeners []net.Listener 59 Managers []*Manager 60 Mux []*mux.Router 61 Hosts []string 62 cleanupOnce sync.Once 63 cancel context.CancelFunc 64 } 65 66 // SetupTestGrid creates a new grid for testing purposes. 67 // Select the number of hosts to create. 68 // Call (TestGrid).Cleanup() when done. 69 func SetupTestGrid(n int) (*TestGrid, error) { 70 hosts, listeners, err := getHosts(n) 71 if err != nil { 72 return nil, err 73 } 74 dialer := &net.Dialer{ 75 Timeout: 5 * time.Second, 76 } 77 var res TestGrid 78 res.Hosts = hosts 79 ready := make(chan struct{}) 80 ctx, cancel := context.WithCancel(context.Background()) 81 res.cancel = cancel 82 for i, host := range hosts { 83 manager, err := NewManager(ctx, ManagerOptions{ 84 Dialer: dialer.DialContext, 85 Local: host, 86 Hosts: hosts, 87 AuthRequest: func(r *http.Request) error { 88 return nil 89 }, 90 AddAuth: func(aud string) string { return aud }, 91 BlockConnect: ready, 92 }) 93 if err != nil { 94 return nil, err 95 } 96 m := mux.NewRouter() 97 m.Handle(RoutePath, manager.Handler()) 98 res.Managers = append(res.Managers, manager) 99 res.Servers = append(res.Servers, startHTTPServer(listeners[i], m)) 100 res.Listeners = append(res.Listeners, listeners[i]) 101 res.Mux = append(res.Mux, m) 102 } 103 xioutil.SafeClose(ready) 104 for _, m := range res.Managers { 105 for _, remote := range m.Targets() { 106 if err := m.Connection(remote).WaitForConnect(ctx); err != nil { 107 return nil, err 108 } 109 } 110 } 111 return &res, nil 112 } 113 114 // Cleanup will clean up the test grid. 115 func (t *TestGrid) Cleanup() { 116 t.cancel() 117 t.cleanupOnce.Do(func() { 118 for _, manager := range t.Managers { 119 manager.debugMsg(debugShutdown) 120 } 121 for _, server := range t.Servers { 122 server.Close() 123 } 124 for _, listener := range t.Listeners { 125 listener.Close() 126 } 127 }) 128 } 129 130 // WaitAllConnect will wait for all connections to be established. 131 func (t *TestGrid) WaitAllConnect(ctx context.Context) { 132 for _, manager := range t.Managers { 133 for _, remote := range manager.Targets() { 134 if manager.HostName() == remote { 135 continue 136 } 137 if err := manager.Connection(remote).WaitForConnect(ctx); err != nil { 138 panic(err) 139 } 140 } 141 } 142 } 143 144 func getHosts(n int) (hosts []string, listeners []net.Listener, err error) { 145 for i := 0; i < n; i++ { 146 l, err := net.Listen("tcp", "127.0.0.1:0") 147 if err != nil { 148 if l, err = net.Listen("tcp6", "[::1]:0"); err != nil { 149 return nil, nil, fmt.Errorf("httptest: failed to listen on a port: %v", err) 150 } 151 } 152 addr := l.Addr() 153 hosts = append(hosts, "http://"+addr.String()) 154 listeners = append(listeners, l) 155 } 156 return 157 } 158 159 func startHTTPServer(listener net.Listener, handler http.Handler) (server *httptest.Server) { 160 server = httptest.NewUnstartedServer(handler) 161 server.Config.Addr = listener.Addr().String() 162 server.Listener = listener 163 server.Start() 164 return server 165 }