Classify maintenance refusals as activator responses
3 files changed,
+9
−5
internal/activator/activator.go+3 −3internal/activator/maintenance.go+3 −1internal/activator/maintenance_test.go+3 −1
diff --git a/internal/activator/activator.go b/internal/activator/activator.go| index a22fd3e9a..20fe0e837 100644 |
| --- a/internal/activator/activator.go |
| +++ b/internal/activator/activator.go |
| @@ -1354,7 +1354,7 @@ func (a *Service) serve(w http.ResponseWriter, r *http.Request, rt registry.Rout |
| fenced := a.fences[rt.Host] != 0 |
| a.mu.Unlock() |
| if fenced { |
| - maintenanceResponse(w) |
| + maintenanceResponse(w, r) |
| return true |
| } |
| // Per-host gating: only a hibernate-flagged instance is activator-managed; |
| @@ -1383,7 +1383,7 @@ func (a *Service) serve(w http.ResponseWriter, r *http.Request, rt registry.Rout |
| r.Header.Del(SidecarHeader) |
| if err := a.BeginSidecarRequest(r.Context(), rt); err != nil { |
| if errors.Is(err, ErrFenced) { |
| - maintenanceResponse(w) |
| + maintenanceResponse(w, r) |
| return true |
| } |
| if errors.Is(err, context.Canceled) { |
| @@ -1411,7 +1411,7 @@ func (a *Service) serve(w http.ResponseWriter, r *http.Request, rt registry.Rout |
| var held *errLeaseHeld |
| switch { |
| case errors.Is(err, ErrFenced): |
| - maintenanceResponse(w) |
| + maintenanceResponse(w, r) |
| case errors.As(err, &held): |
| retry := int(held.retryAfter/time.Second) + 1 |
| w.Header().Set("Retry-After", strconv.Itoa(retry)) |
diff --git a/internal/activator/maintenance.go b/internal/activator/maintenance.go| index a643d3eb5..8981dbf77 100644 |
| --- a/internal/activator/maintenance.go |
| +++ b/internal/activator/maintenance.go |
| @@ -6,6 +6,7 @@ import ( |
| "net/http" |
| "time" |
| + "github.com/carlosframework/platform/internal/edge" |
| "github.com/carlosframework/platform/internal/provision" |
| ) |
| @@ -138,7 +139,8 @@ func (a *Service) rememberStopLocked(host string, observation *stopObservation) |
| } |
| a.stopHistory[host] = observation |
| } |
| -func maintenanceResponse(w http.ResponseWriter) { |
| +func maintenanceResponse(w http.ResponseWriter, r *http.Request) { |
| + edge.MarkAnswered(r) |
| w.Header().Set("Cache-Control", "no-store") |
| w.Header().Set("Retry-After", "5") |
| http.Error(w, "instance failed to wake", http.StatusServiceUnavailable) |
diff --git a/internal/activator/maintenance_test.go b/internal/activator/maintenance_test.go| index 82cc6cf0c..ae4533382 100644 |
| --- a/internal/activator/maintenance_test.go |
| +++ b/internal/activator/maintenance_test.go |
| @@ -3,6 +3,7 @@ package activator |
| import ( |
| "context" |
| "errors" |
| + "github.com/carlosframework/platform/internal/edge" |
| "github.com/carlosframework/platform/internal/registry" |
| "net/http" |
| "net/http/httptest" |
| @@ -130,6 +131,7 @@ func TestMaintenanceFenceBlocksEveryAdmissionIncludingStaleRoute(t *testing.T) { |
| route.Kind = "service" |
| } |
| req := httptest.NewRequest("GET", "https://"+host+"/", nil) |
| + req, answered := edge.WithAnswerFlag(req) |
| if sidecar { |
| req.Header.Set(SidecarHeader, "1") |
| } |
| @@ -142,7 +144,7 @@ func TestMaintenanceFenceBlocksEveryAdmissionIncludingStaleRoute(t *testing.T) { |
| } else { |
| handled = a.ServeHTTP(out, req, route, proxy) |
| } |
| - if !handled || proxied || out.Code != 503 || out.Header().Get("Cache-Control") != "no-store" { |
| + if !handled || proxied || !*answered || out.Code != 503 || out.Header().Get("Cache-Control") != "no-store" { |
| t.Fatal("fence bypassed by stale or scheduled route") |
| } |
| } |