clavitor/clavis/clavis-vault/edition/backup_mode.go

72 lines
2.2 KiB
Go

//go:build commercial
// Package edition - Backup mode detection for Commercial Edition.
// This file is built ONLY when the "commercial" build tag is specified.
//
// Backup POPs serve read-only traffic when primary is down.
// Community Edition does not have backup functionality.
package edition
import (
"context"
"net/http"
"os"
)
// BackupModeContextKey is used to store backup mode in request context.
type BackupModeContextKey struct{}
// isBackupMode returns true if this POP is currently operating as a backup.
// Assigned to edition.IsBackupMode in commercial builds.
func isBackupMode() bool {
// Check environment variable first
if os.Getenv("CLAVITOR_BACKUP_MODE") == "true" {
return true
}
// TODO: Check with control plane if this POP has been promoted to active
return false
}
// BackupModeMiddleware detects if this is a backup POP and marks context.
// Rejects write operations with 503 and X-Primary-Location header.
func BackupModeMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if !isBackupMode() {
next.ServeHTTP(w, r)
return
}
// This is a backup POP - mark context
ctx := context.WithValue(r.Context(), BackupModeContextKey{}, true)
r = r.WithContext(ctx)
// Check if this is a write operation
if isWriteMethod(r.Method) {
// Tell client where the primary is
primaryURL := ""
if globalConfig != nil && globalConfig.ReplicationConfig != nil {
// TODO: Need to add primary_pop URL to config for backup role
primaryURL = globalConfig.TelemetryHost // Fallback - should be primary POP URL
}
if primaryURL != "" {
w.Header().Set("X-Primary-Location", primaryURL)
}
http.Error(w, "Write operations not available on backup POP", http.StatusServiceUnavailable)
return
}
// Read operations allowed
next.ServeHTTP(w, r)
})
}
func isWriteMethod(method string) bool {
return method == "POST" || method == "PUT" || method == "DELETE" || method == "PATCH"
}
// isBackupRequest returns true if the request context indicates backup mode.
func isBackupRequest(ctx context.Context) bool {
v, _ := ctx.Value(BackupModeContextKey{}).(bool)
return v
}