createProxyReplicas creates and runs a set of proxy replicas and ensures that they are all functioning correctly and aware of each other with no errors.
(ctx context.Context, t *testing.T, opts *createProxyReplicasOptions)
| 1136 | // createProxyReplicas creates and runs a set of proxy replicas and ensures that |
| 1137 | // they are all functioning correctly and aware of each other with no errors. |
| 1138 | func createProxyReplicas(ctx context.Context, t *testing.T, opts *createProxyReplicasOptions) []coderdenttest.WorkspaceProxy { |
| 1139 | t.Helper() |
| 1140 | |
| 1141 | var ( |
| 1142 | proxies = make([]coderdenttest.WorkspaceProxy, opts.Count) |
| 1143 | // replicaPingSuccessful tracks whether the replica ping callback |
| 1144 | // was called with no errors for each replica. |
| 1145 | replicaPingMutex sync.Mutex |
| 1146 | replicaPingSuccessful = make([]bool, opts.Count) |
| 1147 | ) |
| 1148 | for i := range proxies { |
| 1149 | proxies[i] = coderdenttest.NewWorkspaceProxyReplica(t, opts.API, opts.Client, &coderdenttest.ProxyOptions{ |
| 1150 | Name: opts.Name, |
| 1151 | Token: opts.ProxyToken, |
| 1152 | ProxyURL: opts.ProxyURL, |
| 1153 | ReplicaPingCallback: func(siblings []codersdk.Replica, err string) { |
| 1154 | t.Logf("got wsproxy ping callback: i=%d, siblings=%v, err=%s", i, len(siblings), err) |
| 1155 | |
| 1156 | replicaPingMutex.Lock() |
| 1157 | defer replicaPingMutex.Unlock() |
| 1158 | // The replica only "successfully" pinged if it has the |
| 1159 | // correct number of siblings and no error. |
| 1160 | replicaPingSuccessful[i] = len(siblings) == opts.Count-1 && err == "" |
| 1161 | }, |
| 1162 | }) |
| 1163 | if i == 0 { |
| 1164 | // The first proxy will have a new token if we just created a new |
| 1165 | // proxy region. |
| 1166 | opts.ProxyToken = proxies[i].Options.ProxySessionToken |
| 1167 | } |
| 1168 | } |
| 1169 | |
| 1170 | // Force all proxies to re-register immediately. This ensures the DERP |
| 1171 | // mesh is up-to-date. In production this will happen automatically |
| 1172 | // after about 15 seconds. |
| 1173 | for i, proxy := range proxies { |
| 1174 | err := proxy.RegisterNow(ctx) |
| 1175 | require.NoErrorf(t, err, "failed to force proxy %d to re-register", i) |
| 1176 | } |
| 1177 | |
| 1178 | // Ensure that all proxies have pinged successfully. If replicas haven't |
| 1179 | // successfully pinged yet, force them to re-register again. We don't |
| 1180 | // use require.Eventually here because it runs the condition function in |
| 1181 | // a goroutine. |
| 1182 | ticker := time.NewTicker(testutil.IntervalSlow) |
| 1183 | defer ticker.Stop() |
| 1184 | for { |
| 1185 | var ( |
| 1186 | ok = true |
| 1187 | wg sync.WaitGroup |
| 1188 | ) |
| 1189 | |
| 1190 | // Copy the replicaPingSuccessful slice to a local variable so we can |
| 1191 | // view the state of all proxies at the same point in time. |
| 1192 | replicaPingSuccessfulCopy := make([]bool, len(replicaPingSuccessful)) |
| 1193 | replicaPingMutex.Lock() |
| 1194 | copy(replicaPingSuccessfulCopy, replicaPingSuccessful) |
| 1195 | replicaPingMutex.Unlock() |