runOnce runs the health check for all workspace proxies. If there is an unexpected error, an error is returned. Expected errors will mark a proxy as unreachable.
(ctx context.Context, now time.Time)
| 216 | // unexpected error, an error is returned. Expected errors will mark a proxy as |
| 217 | // unreachable. |
| 218 | func (p *ProxyHealth) runOnce(ctx context.Context, now time.Time) (map[uuid.UUID]ProxyStatus, error) { |
| 219 | // Record from the given time. |
| 220 | defer func() { p.healthCheckDuration.Observe(time.Since(now).Seconds()) }() |
| 221 | |
| 222 | //nolint:gocritic // Proxy health is a system service. |
| 223 | proxies, err := p.db.GetWorkspaceProxies(dbauthz.AsSystemRestricted(ctx)) |
| 224 | if err != nil { |
| 225 | return nil, xerrors.Errorf("get workspace proxies: %w", err) |
| 226 | } |
| 227 | |
| 228 | // Just use a mutex to protect map writes. |
| 229 | var statusMu sync.Mutex |
| 230 | proxyStatus := map[uuid.UUID]ProxyStatus{} |
| 231 | |
| 232 | grp, gctx := errgroup.WithContext(ctx) |
| 233 | // Arbitrary parallelism limit. |
| 234 | grp.SetLimit(5) |
| 235 | |
| 236 | for _, proxy := range proxies { |
| 237 | if proxy.Deleted { |
| 238 | // Ignore deleted proxies. |
| 239 | continue |
| 240 | } |
| 241 | // Each proxy needs to have a status set. Make a local copy for the |
| 242 | // call to be run async. |
| 243 | status := ProxyStatus{ |
| 244 | Proxy: proxy, |
| 245 | CheckedAt: now, |
| 246 | Status: Unknown, |
| 247 | } |
| 248 | |
| 249 | grp.Go(func() error { |
| 250 | if proxy.Url == "" { |
| 251 | // Empty URL means the proxy has not registered yet. |
| 252 | // When the proxy is started, it will update the url. |
| 253 | statusMu.Lock() |
| 254 | defer statusMu.Unlock() |
| 255 | p.healthCheckResults.WithLabelValues(prometheusmetrics.VectorOperationSet, 0, proxy.ID.String()) |
| 256 | status.Status = Unregistered |
| 257 | proxyStatus[proxy.ID] = status |
| 258 | return nil |
| 259 | } |
| 260 | |
| 261 | // Try to hit the healthz-report endpoint for a comprehensive health check. |
| 262 | reqURL := fmt.Sprintf("%s/healthz-report", strings.TrimSuffix(proxy.Url, "/")) |
| 263 | req, err := http.NewRequestWithContext(gctx, http.MethodGet, reqURL, nil) |
| 264 | if err != nil { |
| 265 | return xerrors.Errorf("new request: %w", err) |
| 266 | } |
| 267 | req = req.WithContext(gctx) |
| 268 | |
| 269 | resp, err := p.client.Do(req) |
| 270 | if err == nil { |
| 271 | defer resp.Body.Close() |
| 272 | } |
| 273 | // A switch statement felt easier to categorize the different cases than |
| 274 | // if else statements or nested if statements. |
| 275 | switch { |
no test coverage detected