mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-05-21 18:46:08 +00:00
Revalidate and harden the Pulse multi-tenant GA surface across org ownership transfer, MSP invite lifecycle, tenant state fallback, registry workspace limits, and the embedded Pulse Account portal bundle.
125 lines
3.7 KiB
Go
125 lines
3.7 KiB
Go
package api
|
|
|
|
import (
|
|
"errors"
|
|
"net/http"
|
|
"time"
|
|
|
|
"github.com/rcourtman/pulse-go-rewrite/internal/monitoring"
|
|
"github.com/rcourtman/pulse-go-rewrite/internal/unifiedresources"
|
|
"github.com/rs/zerolog/log"
|
|
)
|
|
|
|
var errTenantMonitorUnavailable = errors.New("tenant monitor unavailable")
|
|
|
|
// getMonitor returns the tenant-specific monitor instance for the request.
|
|
// It uses the OrgID from the context (injected by TenantMiddleware).
|
|
// Non-default orgs fail closed when tenant monitor resolution is unavailable.
|
|
func (r *Router) getMonitor(req *http.Request) (*monitoring.Monitor, error) {
|
|
orgID := GetOrgID(req.Context())
|
|
if orgID == "" || orgID == "default" {
|
|
return r.monitor, nil
|
|
}
|
|
if r.mtMonitor == nil {
|
|
return nil, errTenantMonitorUnavailable
|
|
}
|
|
|
|
return r.mtMonitor.GetMonitor(orgID)
|
|
}
|
|
|
|
// MultiTenantStateProvider wraps a MultiTenantMonitor to provide state for specific tenants.
|
|
type MultiTenantStateProvider struct {
|
|
mtMonitor *monitoring.MultiTenantMonitor
|
|
defaultMonitor *monitoring.Monitor
|
|
}
|
|
|
|
// NewMultiTenantStateProvider creates a new tenant state provider.
|
|
func NewMultiTenantStateProvider(mtm *monitoring.MultiTenantMonitor, defaultM *monitoring.Monitor) *MultiTenantStateProvider {
|
|
return &MultiTenantStateProvider{
|
|
mtMonitor: mtm,
|
|
defaultMonitor: defaultM,
|
|
}
|
|
}
|
|
|
|
func (p *MultiTenantStateProvider) monitorForTenant(orgID string) *monitoring.Monitor {
|
|
if orgID == "" || orgID == "default" {
|
|
return p.defaultMonitor
|
|
}
|
|
|
|
if p.mtMonitor != nil {
|
|
monitor, err := p.mtMonitor.GetMonitor(orgID)
|
|
if err != nil {
|
|
log.Warn().Err(err).Str("org_id", orgID).Msg("Failed to get tenant monitor")
|
|
return nil
|
|
}
|
|
return monitor
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// UnifiedReadStateForTenant returns the canonical typed unified read-state for a
|
|
// specific tenant, falling back to a snapshot-backed adapter only when the
|
|
// monitor has not been wired with a resource store yet.
|
|
func (p *MultiTenantStateProvider) UnifiedReadStateForTenant(orgID string) unifiedresources.ReadState {
|
|
monitor := p.monitorForTenant(orgID)
|
|
if monitor == nil {
|
|
return nil
|
|
}
|
|
return monitor.GetUnifiedReadStateOrSnapshot()
|
|
}
|
|
|
|
// UnifiedResourceSnapshotForTenant returns the canonical unified-resource seed
|
|
// for a specific tenant, along with its freshness marker.
|
|
func (p *MultiTenantStateProvider) UnifiedResourceSnapshotForTenant(orgID string) ([]unifiedresources.Resource, time.Time) {
|
|
monitor := p.monitorForTenant(orgID)
|
|
if monitor == nil {
|
|
return nil, time.Time{}
|
|
}
|
|
|
|
return monitor.UnifiedResourceSnapshot()
|
|
}
|
|
|
|
// SetMultiTenantMonitor updates the multi-tenant monitor manager.
|
|
// Used during reload.
|
|
func (r *Router) SetMultiTenantMonitor(mtm *monitoring.MultiTenantMonitor) {
|
|
r.mtMonitor = mtm
|
|
if r.configHandlers != nil {
|
|
r.configHandlers.SetMultiTenantMonitor(mtm)
|
|
}
|
|
if r.alertHandlers != nil {
|
|
r.alertHandlers.SetMultiTenantMonitor(mtm)
|
|
}
|
|
if r.notificationHandlers != nil {
|
|
r.notificationHandlers.SetMultiTenantMonitor(mtm)
|
|
}
|
|
if r.dockerAgentHandlers != nil {
|
|
r.dockerAgentHandlers.SetMultiTenantMonitor(mtm)
|
|
}
|
|
if r.unifiedAgentHandlers != nil {
|
|
r.unifiedAgentHandlers.SetMultiTenantMonitor(mtm)
|
|
}
|
|
if r.kubernetesAgentHandlers != nil {
|
|
r.kubernetesAgentHandlers.SetMultiTenantMonitor(mtm)
|
|
}
|
|
if r.systemSettingsHandler != nil {
|
|
r.systemSettingsHandler.SetMultiTenantMonitor(mtm)
|
|
}
|
|
if r.aiSettingsHandler != nil {
|
|
r.aiSettingsHandler.SetMultiTenantMonitor(mtm)
|
|
}
|
|
if r.aiHandler != nil {
|
|
r.aiHandler.SetMultiTenantMonitor(mtm)
|
|
}
|
|
if mtm != nil {
|
|
if m, err := mtm.GetMonitor("default"); err == nil {
|
|
r.monitor = m
|
|
}
|
|
mtm.SetMonitorInitializer(r.configureMonitorDependencies)
|
|
}
|
|
|
|
// Wire tenant state provider to resource handlers
|
|
if r.resourceHandlers != nil {
|
|
r.resourceHandlers.SetTenantStateProvider(NewMultiTenantStateProvider(mtm, r.monitor))
|
|
}
|
|
}
|