mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-09-10 02:25:56 +00:00
Extend MSP install proof for portal rollups
This commit is contained in:
@@ -65,6 +65,8 @@ type providerMSPInstallProofReport struct {
|
||||
SetupFactsTokenUseVisible bool
|
||||
AgentReportIngestVerified bool
|
||||
TokenRotationVerified bool
|
||||
ReportScheduleVisible bool
|
||||
ActiveAlertRollupVisible bool
|
||||
RotatedOutTokenRejectionVerified bool
|
||||
NonProofTenantCountPreserved bool
|
||||
InitialStatusTotalTenants int
|
||||
@@ -495,6 +497,8 @@ func populateProviderMSPInstallProofProof(report *providerMSPInstallProofReport,
|
||||
report.SetupFactsTokenUseVisible = proof.SetupFactsTokenUseVisible
|
||||
report.AgentReportIngestVerified = proof.AgentReportIngestVerified
|
||||
report.TokenRotationVerified = proof.TokenRotationVerified
|
||||
report.ReportScheduleVisible = proof.ReportScheduleVisible
|
||||
report.ActiveAlertRollupVisible = proof.ActiveAlertRollupVisible
|
||||
report.TenantIsolationVerified = proof.InstallTokenBoundaryOK
|
||||
report.DefaultRuntimeIsolationVerified = proof.AgentReportIngestVerified
|
||||
report.RotatedOutTokenRejectionVerified = providerMSPInstallProofAllRotatedOutTokensRejected(proof.Workspaces)
|
||||
@@ -596,6 +600,8 @@ func printProviderMSPInstallProofReport(report *providerMSPInstallProofReport) {
|
||||
fmt.Printf("setup_facts_token_use_visible=%t\n", report.SetupFactsTokenUseVisible)
|
||||
fmt.Printf("agent_report_ingest_verified=%t\n", report.AgentReportIngestVerified)
|
||||
fmt.Printf("token_rotation_verified=%t\n", report.TokenRotationVerified)
|
||||
fmt.Printf("report_schedule_visible=%t\n", report.ReportScheduleVisible)
|
||||
fmt.Printf("active_alert_rollup_visible=%t\n", report.ActiveAlertRollupVisible)
|
||||
fmt.Printf("rotated_out_token_rejection_verified=%t\n", report.RotatedOutTokenRejectionVerified)
|
||||
fmt.Printf("non_proof_tenant_count_preserved=%t\n", report.NonProofTenantCountPreserved)
|
||||
fmt.Printf("initial_status_total_tenants=%d\n", report.InitialStatusTotalTenants)
|
||||
@@ -605,7 +611,7 @@ func printProviderMSPInstallProofReport(report *providerMSPInstallProofReport) {
|
||||
fmt.Printf("final_status_unhealthy_tenants=%d\n", report.FinalStatusUnhealthyTenants)
|
||||
fmt.Printf("final_status_stuck_provisioning_tenants=%d\n", report.FinalStatusStuckProvisioningTenants)
|
||||
for _, workspace := range report.Workspaces {
|
||||
fmt.Printf("workspace=%s display_name=%q state=%s plan_version=%s container_id=%s public_url=%s install_type=%s install_token_id=%s install_command_generated=%t agent_token_auth_verified=%t setup_facts_token_use_visible=%t agent_report_ingest_verified=%t agent_report_agent_id=%s agent_report_hostname=%s token_rotation_verified=%t rotated_install_token_id=%s old_install_token_rejected=%t rotated_agent_report_verified=%t handoff_exchange_verified=%t handoff_target_path=%s entitlement_lease_checked=%t entitlement_lease_verified=%t entitlement_white_label=%t entitlement_skipped_reason=%s\n",
|
||||
fmt.Printf("workspace=%s display_name=%q state=%s plan_version=%s container_id=%s public_url=%s install_type=%s install_token_id=%s install_command_generated=%t agent_token_auth_verified=%t setup_facts_token_use_visible=%t agent_report_ingest_verified=%t agent_report_agent_id=%s agent_report_hostname=%s token_rotation_verified=%t rotated_install_token_id=%s old_install_token_rejected=%t rotated_agent_report_verified=%t handoff_exchange_verified=%t handoff_target_path=%s entitlement_lease_checked=%t entitlement_lease_verified=%t entitlement_white_label=%t entitlement_skipped_reason=%s report_schedule_created=%t report_schedule_id=%s report_schedule_visible=%t report_schedule_count=%d disabled_report_schedule_count=%d active_alert_persisted=%t active_alert_rollup_visible=%t critical_alert_count=%d warning_alert_count=%d\n",
|
||||
workspace.TenantID,
|
||||
workspace.DisplayName,
|
||||
workspace.State,
|
||||
@@ -630,6 +636,15 @@ func printProviderMSPInstallProofReport(report *providerMSPInstallProofReport) {
|
||||
workspace.EntitlementLeaseVerified,
|
||||
workspace.EntitlementWhiteLabel,
|
||||
workspace.EntitlementSkippedReason,
|
||||
workspace.ReportScheduleCreated,
|
||||
workspace.ReportScheduleID,
|
||||
workspace.ReportScheduleVisible,
|
||||
workspace.ReportScheduleCount,
|
||||
workspace.DisabledReportScheduleCount,
|
||||
workspace.ActiveAlertPersisted,
|
||||
workspace.ActiveAlertRollupVisible,
|
||||
workspace.CriticalAlertCount,
|
||||
workspace.WarningAlertCount,
|
||||
)
|
||||
}
|
||||
for _, failure := range report.Failures {
|
||||
|
||||
@@ -159,7 +159,7 @@ func TestProviderMSPInstallProofRunsFreshInstallSequence(t *testing.T) {
|
||||
if report.WorkspaceCount != 2 || len(report.Workspaces) != 2 {
|
||||
t.Fatalf("workspace proof count = %d len=%d", report.WorkspaceCount, len(report.Workspaces))
|
||||
}
|
||||
if !report.TenantIsolationVerified || !report.DefaultRuntimeIsolationVerified || !report.TokenRotationVerified || !report.RotatedOutTokenRejectionVerified {
|
||||
if !report.TenantIsolationVerified || !report.DefaultRuntimeIsolationVerified || !report.TokenRotationVerified || !report.RotatedOutTokenRejectionVerified || !report.ReportScheduleVisible || !report.ActiveAlertRollupVisible {
|
||||
t.Fatalf("isolation/token proof missing: %#v", report)
|
||||
}
|
||||
if !report.NonProofTenantCountPreserved {
|
||||
@@ -336,6 +336,8 @@ func healthyProviderMSPInstallProofProofReport() *providerMSPProofReport {
|
||||
SetupFactsTokenUseVisible: true,
|
||||
AgentReportIngestVerified: true,
|
||||
TokenRotationVerified: true,
|
||||
ReportScheduleVisible: true,
|
||||
ActiveAlertRollupVisible: true,
|
||||
Workspaces: []providerMSPProofWorkspace{
|
||||
healthyProviderMSPInstallProofWorkspace("ws-proof-01", "Provider MSP Proof 01"),
|
||||
healthyProviderMSPInstallProofWorkspace("ws-proof-02", "Provider MSP Proof 02"),
|
||||
@@ -345,26 +347,35 @@ func healthyProviderMSPInstallProofProofReport() *providerMSPProofReport {
|
||||
|
||||
func healthyProviderMSPInstallProofWorkspace(tenantID, displayName string) providerMSPProofWorkspace {
|
||||
return providerMSPProofWorkspace{
|
||||
TenantID: tenantID,
|
||||
DisplayName: displayName,
|
||||
State: "active",
|
||||
PlanVersion: "msp_growth",
|
||||
ContainerID: "ctr-" + tenantID,
|
||||
PublicURL: "https://" + tenantID + ".msp.example.com",
|
||||
InstallType: "pve",
|
||||
InstallTokenID: "tok-" + tenantID,
|
||||
InstallCommandGenerated: true,
|
||||
AgentTokenAuthVerified: true,
|
||||
SetupFactsTokenUseVisible: true,
|
||||
AgentReportIngestVerified: true,
|
||||
AgentReportAgentID: "agent-" + tenantID,
|
||||
AgentReportHostname: "pve1",
|
||||
TokenRotationVerified: true,
|
||||
RotatedInstallTokenID: "tok-rotated-" + tenantID,
|
||||
OldInstallTokenRejected: true,
|
||||
RotatedAgentReportVerified: true,
|
||||
HandoffExchangeVerified: true,
|
||||
HandoffTargetPath: "/settings/infrastructure?add=linux-host",
|
||||
TenantID: tenantID,
|
||||
DisplayName: displayName,
|
||||
State: "active",
|
||||
PlanVersion: "msp_growth",
|
||||
ContainerID: "ctr-" + tenantID,
|
||||
PublicURL: "https://" + tenantID + ".msp.example.com",
|
||||
InstallType: "pve",
|
||||
InstallTokenID: "tok-" + tenantID,
|
||||
InstallCommandGenerated: true,
|
||||
AgentTokenAuthVerified: true,
|
||||
SetupFactsTokenUseVisible: true,
|
||||
AgentReportIngestVerified: true,
|
||||
AgentReportAgentID: "agent-" + tenantID,
|
||||
AgentReportHostname: "pve1",
|
||||
TokenRotationVerified: true,
|
||||
RotatedInstallTokenID: "tok-rotated-" + tenantID,
|
||||
OldInstallTokenRejected: true,
|
||||
RotatedAgentReportVerified: true,
|
||||
HandoffExchangeVerified: true,
|
||||
HandoffTargetPath: "/settings/infrastructure?add=linux-host",
|
||||
ReportScheduleCreated: true,
|
||||
ReportScheduleID: "sched-" + tenantID,
|
||||
ReportScheduleVisible: true,
|
||||
ReportScheduleCount: 1,
|
||||
DisabledReportScheduleCount: 0,
|
||||
ActiveAlertPersisted: true,
|
||||
ActiveAlertRollupVisible: true,
|
||||
CriticalAlertCount: 1,
|
||||
WarningAlertCount: 1,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/rcourtman/pulse-go-rewrite/internal/alerts"
|
||||
"github.com/rcourtman/pulse-go-rewrite/internal/api"
|
||||
"github.com/rcourtman/pulse-go-rewrite/internal/cloudcp"
|
||||
"github.com/rcourtman/pulse-go-rewrite/internal/cloudcp/account"
|
||||
@@ -55,32 +56,41 @@ type providerMSPProofRuntime struct {
|
||||
}
|
||||
|
||||
type providerMSPProofWorkspace struct {
|
||||
TenantID string
|
||||
DisplayName string
|
||||
State string
|
||||
PlanVersion string
|
||||
ContainerID string
|
||||
PublicURL string
|
||||
InstallType string
|
||||
InstallToken string
|
||||
InstallTokenID string
|
||||
InstallCommandGenerated bool
|
||||
AgentTokenAuthVerified bool
|
||||
SetupFactsTokenUseVisible bool
|
||||
AgentReportIngestVerified bool
|
||||
AgentReportAgentID string
|
||||
AgentReportHostname string
|
||||
TokenRotationVerified bool
|
||||
RotatedInstallToken string
|
||||
RotatedInstallTokenID string
|
||||
OldInstallTokenRejected bool
|
||||
RotatedAgentReportVerified bool
|
||||
HandoffExchangeVerified bool
|
||||
HandoffTargetPath string
|
||||
EntitlementLeaseChecked bool
|
||||
EntitlementLeaseVerified bool
|
||||
EntitlementWhiteLabel bool
|
||||
EntitlementSkippedReason string
|
||||
TenantID string
|
||||
DisplayName string
|
||||
State string
|
||||
PlanVersion string
|
||||
ContainerID string
|
||||
PublicURL string
|
||||
InstallType string
|
||||
InstallToken string
|
||||
InstallTokenID string
|
||||
InstallCommandGenerated bool
|
||||
AgentTokenAuthVerified bool
|
||||
SetupFactsTokenUseVisible bool
|
||||
AgentReportIngestVerified bool
|
||||
AgentReportAgentID string
|
||||
AgentReportHostname string
|
||||
TokenRotationVerified bool
|
||||
RotatedInstallToken string
|
||||
RotatedInstallTokenID string
|
||||
OldInstallTokenRejected bool
|
||||
RotatedAgentReportVerified bool
|
||||
HandoffExchangeVerified bool
|
||||
HandoffTargetPath string
|
||||
EntitlementLeaseChecked bool
|
||||
EntitlementLeaseVerified bool
|
||||
EntitlementWhiteLabel bool
|
||||
EntitlementSkippedReason string
|
||||
ReportScheduleCreated bool
|
||||
ReportScheduleID string
|
||||
ReportScheduleVisible bool
|
||||
ReportScheduleCount int
|
||||
DisabledReportScheduleCount int
|
||||
ActiveAlertPersisted bool
|
||||
ActiveAlertRollupVisible bool
|
||||
CriticalAlertCount int
|
||||
WarningAlertCount int
|
||||
}
|
||||
|
||||
type providerMSPProofReport struct {
|
||||
@@ -102,6 +112,8 @@ type providerMSPProofReport struct {
|
||||
SetupFactsTokenUseVisible bool
|
||||
AgentReportIngestVerified bool
|
||||
TokenRotationVerified bool
|
||||
ReportScheduleVisible bool
|
||||
ActiveAlertRollupVisible bool
|
||||
Cleanup bool
|
||||
}
|
||||
|
||||
@@ -268,6 +280,8 @@ func (rt *providerMSPProofRuntime) runProviderMSPProof(ctx context.Context, opts
|
||||
SetupFactsTokenUseVisible: true,
|
||||
AgentReportIngestVerified: true,
|
||||
TokenRotationVerified: true,
|
||||
ReportScheduleVisible: true,
|
||||
ActiveAlertRollupVisible: true,
|
||||
Cleanup: opts.Cleanup,
|
||||
}
|
||||
|
||||
@@ -299,6 +313,12 @@ func (rt *providerMSPProofRuntime) runProviderMSPProof(ctx context.Context, opts
|
||||
if !workspace.TokenRotationVerified {
|
||||
report.TokenRotationVerified = false
|
||||
}
|
||||
if !workspace.ReportScheduleVisible {
|
||||
report.ReportScheduleVisible = false
|
||||
}
|
||||
if !workspace.ActiveAlertRollupVisible {
|
||||
report.ActiveAlertRollupVisible = false
|
||||
}
|
||||
report.Workspaces = append(report.Workspaces, workspace)
|
||||
}
|
||||
|
||||
@@ -461,36 +481,236 @@ func (rt *providerMSPProofRuntime) proveProviderMSPWorkspace(ctx context.Context
|
||||
return providerMSPProofWorkspace{}, err
|
||||
}
|
||||
|
||||
portalRollup, err := rt.verifyProviderMSPProofPortalRollup(ctx, tenant, tenantDataDir, agentReport)
|
||||
if err != nil {
|
||||
return providerMSPProofWorkspace{}, err
|
||||
}
|
||||
|
||||
return providerMSPProofWorkspace{
|
||||
TenantID: tenant.ID,
|
||||
DisplayName: tenant.DisplayName,
|
||||
State: string(tenant.State),
|
||||
PlanVersion: tenant.PlanVersion,
|
||||
ContainerID: tenant.ContainerID,
|
||||
PublicURL: publicURL,
|
||||
InstallType: install.InstallType,
|
||||
InstallToken: install.Token,
|
||||
InstallTokenID: install.TokenID,
|
||||
InstallCommandGenerated: true,
|
||||
AgentTokenAuthVerified: tokenAuthVerified,
|
||||
SetupFactsTokenUseVisible: setupFactsVisible,
|
||||
AgentReportIngestVerified: agentReport.Verified,
|
||||
AgentReportAgentID: agentReport.AgentID,
|
||||
AgentReportHostname: agentReport.Hostname,
|
||||
TokenRotationVerified: rotation.Verified,
|
||||
RotatedInstallToken: rotation.RotatedToken,
|
||||
RotatedInstallTokenID: rotation.RotatedTokenID,
|
||||
OldInstallTokenRejected: rotation.OldTokenRejected,
|
||||
RotatedAgentReportVerified: rotation.RotatedAgentReportVerified,
|
||||
HandoffExchangeVerified: exchangedTargetPath == targetPath,
|
||||
HandoffTargetPath: exchangedTargetPath,
|
||||
EntitlementLeaseChecked: entitlement.Checked,
|
||||
EntitlementLeaseVerified: entitlement.Verified,
|
||||
EntitlementWhiteLabel: entitlement.WhiteLabel,
|
||||
EntitlementSkippedReason: entitlement.SkippedReason,
|
||||
TenantID: tenant.ID,
|
||||
DisplayName: tenant.DisplayName,
|
||||
State: string(tenant.State),
|
||||
PlanVersion: tenant.PlanVersion,
|
||||
ContainerID: tenant.ContainerID,
|
||||
PublicURL: publicURL,
|
||||
InstallType: install.InstallType,
|
||||
InstallToken: install.Token,
|
||||
InstallTokenID: install.TokenID,
|
||||
InstallCommandGenerated: true,
|
||||
AgentTokenAuthVerified: tokenAuthVerified,
|
||||
SetupFactsTokenUseVisible: setupFactsVisible,
|
||||
AgentReportIngestVerified: agentReport.Verified,
|
||||
AgentReportAgentID: agentReport.AgentID,
|
||||
AgentReportHostname: agentReport.Hostname,
|
||||
TokenRotationVerified: rotation.Verified,
|
||||
RotatedInstallToken: rotation.RotatedToken,
|
||||
RotatedInstallTokenID: rotation.RotatedTokenID,
|
||||
OldInstallTokenRejected: rotation.OldTokenRejected,
|
||||
RotatedAgentReportVerified: rotation.RotatedAgentReportVerified,
|
||||
HandoffExchangeVerified: exchangedTargetPath == targetPath,
|
||||
HandoffTargetPath: exchangedTargetPath,
|
||||
EntitlementLeaseChecked: entitlement.Checked,
|
||||
EntitlementLeaseVerified: entitlement.Verified,
|
||||
EntitlementWhiteLabel: entitlement.WhiteLabel,
|
||||
EntitlementSkippedReason: entitlement.SkippedReason,
|
||||
ReportScheduleCreated: portalRollup.ReportScheduleCreated,
|
||||
ReportScheduleID: portalRollup.ReportScheduleID,
|
||||
ReportScheduleVisible: portalRollup.ReportScheduleVisible,
|
||||
ReportScheduleCount: portalRollup.ReportScheduleCount,
|
||||
DisabledReportScheduleCount: portalRollup.DisabledReportScheduleCount,
|
||||
ActiveAlertPersisted: portalRollup.ActiveAlertPersisted,
|
||||
ActiveAlertRollupVisible: portalRollup.ActiveAlertRollupVisible,
|
||||
CriticalAlertCount: portalRollup.CriticalAlertCount,
|
||||
WarningAlertCount: portalRollup.WarningAlertCount,
|
||||
}, nil
|
||||
}
|
||||
|
||||
type providerMSPProofPortalRollup struct {
|
||||
ReportScheduleCreated bool
|
||||
ReportScheduleID string
|
||||
ReportScheduleVisible bool
|
||||
ReportScheduleCount int
|
||||
DisabledReportScheduleCount int
|
||||
ActiveAlertPersisted bool
|
||||
ActiveAlertRollupVisible bool
|
||||
CriticalAlertCount int
|
||||
WarningAlertCount int
|
||||
}
|
||||
|
||||
func (rt *providerMSPProofRuntime) verifyProviderMSPProofPortalRollup(ctx context.Context, tenant *registry.Tenant, tenantDataDir string, agentReport providerMSPProofAgentReportIngest) (providerMSPProofPortalRollup, error) {
|
||||
result := providerMSPProofPortalRollup{}
|
||||
if tenant == nil {
|
||||
return result, fmt.Errorf("tenant is required")
|
||||
}
|
||||
tenantID := strings.TrimSpace(tenant.ID)
|
||||
if tenantID == "" {
|
||||
return result, fmt.Errorf("tenant id is required")
|
||||
}
|
||||
if strings.TrimSpace(agentReport.AgentID) == "" {
|
||||
return result, fmt.Errorf("tenant %s agent report id is required before portal rollup proof", tenantID)
|
||||
}
|
||||
|
||||
scheduleID, err := rt.createProviderMSPProofReportSchedule(ctx, tenant, tenantDataDir, agentReport.AgentID, agentReport.Hostname)
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
result.ReportScheduleCreated = true
|
||||
result.ReportScheduleID = scheduleID
|
||||
|
||||
orgDir := filepath.Join(tenantDataDir, "orgs", tenantID)
|
||||
if err := writeProviderMSPProofActiveAlerts(orgDir, agentReport.AgentID, agentReport.Hostname); err != nil {
|
||||
return result, fmt.Errorf("write provider MSP proof active alerts for tenant %s: %w", tenantID, err)
|
||||
}
|
||||
result.ActiveAlertPersisted = true
|
||||
|
||||
facts := portal.NewTenantDirWorkspaceSetupFactReader(rt.cfg.TenantsDir()).FactsForWorkspace(tenantID)
|
||||
result.ReportScheduleCount = providerMSPProofFactInt(facts.ReportScheduleCount)
|
||||
result.DisabledReportScheduleCount = providerMSPProofFactInt(facts.DisabledReportScheduleCount)
|
||||
result.CriticalAlertCount = providerMSPProofFactInt(facts.ActiveCriticalAlertCount)
|
||||
result.WarningAlertCount = providerMSPProofFactInt(facts.ActiveWarningAlertCount)
|
||||
result.ReportScheduleVisible = result.ReportScheduleCount == 1 && result.DisabledReportScheduleCount == 0
|
||||
result.ActiveAlertRollupVisible = result.CriticalAlertCount == 1 && result.WarningAlertCount == 1 && facts.ActiveAlertsUpdatedAt != nil
|
||||
if !result.ReportScheduleVisible {
|
||||
return result, fmt.Errorf("portal setup facts for tenant %s report schedules = enabled:%d disabled:%d, want enabled:1 disabled:0", tenantID, result.ReportScheduleCount, result.DisabledReportScheduleCount)
|
||||
}
|
||||
if !result.ActiveAlertRollupVisible {
|
||||
return result, fmt.Errorf("portal setup facts for tenant %s active alerts = critical:%d warning:%d updated:%t, want critical:1 warning:1 updated:true", tenantID, result.CriticalAlertCount, result.WarningAlertCount, facts.ActiveAlertsUpdatedAt != nil)
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (rt *providerMSPProofRuntime) createProviderMSPProofReportSchedule(ctx context.Context, tenant *registry.Tenant, tenantDataDir, resourceID, resourceName string) (string, error) {
|
||||
if tenant == nil {
|
||||
return "", fmt.Errorf("tenant is required")
|
||||
}
|
||||
tenantID := strings.TrimSpace(tenant.ID)
|
||||
if tenantID == "" {
|
||||
return "", fmt.Errorf("tenant id is required")
|
||||
}
|
||||
if strings.TrimSpace(resourceName) == "" {
|
||||
resourceName = strings.TrimSpace(resourceID)
|
||||
}
|
||||
tenantCfg := &runtimeconfig.Config{
|
||||
DataPath: tenantDataDir,
|
||||
ConfigPath: tenantDataDir,
|
||||
PublicURL: rt.providerMSPProofTenantPublicURL(tenantID),
|
||||
}
|
||||
tenantPersistence := runtimeconfig.NewMultiTenantPersistence(tenantDataDir)
|
||||
if !tenantPersistence.OrgExists(tenantID) {
|
||||
return "", fmt.Errorf("tenant %s organization metadata is missing before report schedule proof", tenantID)
|
||||
}
|
||||
tenantMonitor := monitoring.NewMultiTenantMonitor(tenantCfg, tenantPersistence, nil)
|
||||
defer tenantMonitor.Stop()
|
||||
|
||||
handler := api.NewReportingHandlers(tenantMonitor, nil)
|
||||
payload := runtimeconfig.ReportSchedule{
|
||||
Name: "Provider MSP proof monthly report",
|
||||
Enabled: true,
|
||||
Cadence: runtimeconfig.ReportScheduleCadence{
|
||||
Type: runtimeconfig.ReportScheduleCadenceMonthly,
|
||||
DayOfMonth: 1,
|
||||
Time: "09:00",
|
||||
Timezone: "UTC",
|
||||
},
|
||||
Scope: runtimeconfig.ReportScheduleScope{
|
||||
Resources: []runtimeconfig.ReportScheduleResource{
|
||||
{
|
||||
ResourceType: "agent",
|
||||
ResourceID: strings.TrimSpace(resourceID),
|
||||
Name: strings.TrimSpace(resourceName),
|
||||
},
|
||||
},
|
||||
},
|
||||
Format: runtimeconfig.ReportScheduleFormatPDF,
|
||||
Delivery: runtimeconfig.ReportScheduleDelivery{
|
||||
Method: runtimeconfig.ReportScheduleDeliveryDisk,
|
||||
Attach: true,
|
||||
SaveToDisk: true,
|
||||
},
|
||||
}
|
||||
body, err := json.Marshal(payload)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("marshal provider MSP proof report schedule: %w", err)
|
||||
}
|
||||
req := httptest.NewRequest(http.MethodPost, "/api/reports/schedules", bytes.NewReader(body))
|
||||
req = req.WithContext(context.WithValue(ctx, api.OrgIDContextKey, tenantID))
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
rec := httptest.NewRecorder()
|
||||
handler.HandleCreateReportSchedule(rec, req)
|
||||
if rec.Code != http.StatusCreated {
|
||||
return "", fmt.Errorf("tenant %s report schedule create status=%d body=%s", tenantID, rec.Code, strings.TrimSpace(rec.Body.String()))
|
||||
}
|
||||
|
||||
var created runtimeconfig.ReportSchedule
|
||||
if err := json.Unmarshal(rec.Body.Bytes(), &created); err != nil {
|
||||
return "", fmt.Errorf("decode provider MSP proof report schedule: %w", err)
|
||||
}
|
||||
if strings.TrimSpace(created.ID) == "" {
|
||||
return "", fmt.Errorf("tenant %s report schedule create response missing id", tenantID)
|
||||
}
|
||||
return created.ID, nil
|
||||
}
|
||||
|
||||
func writeProviderMSPProofActiveAlerts(orgDir, resourceID, resourceName string) error {
|
||||
if strings.TrimSpace(orgDir) == "" {
|
||||
return fmt.Errorf("org dir is required")
|
||||
}
|
||||
if strings.TrimSpace(resourceName) == "" {
|
||||
resourceName = strings.TrimSpace(resourceID)
|
||||
}
|
||||
now := time.Now().UTC()
|
||||
active := []alerts.Alert{
|
||||
{
|
||||
ID: "provider-msp-proof-critical",
|
||||
Type: "cpu",
|
||||
Level: alerts.AlertLevelCritical,
|
||||
ResourceID: strings.TrimSpace(resourceID),
|
||||
ResourceName: strings.TrimSpace(resourceName),
|
||||
Node: strings.TrimSpace(resourceName),
|
||||
Instance: "provider-msp-proof",
|
||||
Message: "Provider MSP proof critical alert",
|
||||
Value: 96,
|
||||
Threshold: 80,
|
||||
StartTime: now,
|
||||
LastSeen: now,
|
||||
},
|
||||
{
|
||||
ID: "provider-msp-proof-warning",
|
||||
Type: "memory",
|
||||
Level: alerts.AlertLevelWarning,
|
||||
ResourceID: strings.TrimSpace(resourceID),
|
||||
ResourceName: strings.TrimSpace(resourceName),
|
||||
Node: strings.TrimSpace(resourceName),
|
||||
Instance: "provider-msp-proof",
|
||||
Message: "Provider MSP proof warning alert",
|
||||
Value: 88,
|
||||
Threshold: 85,
|
||||
StartTime: now,
|
||||
LastSeen: now,
|
||||
},
|
||||
}
|
||||
data, err := json.Marshal(active)
|
||||
if err != nil {
|
||||
return fmt.Errorf("marshal active alerts: %w", err)
|
||||
}
|
||||
alertsDir := filepath.Join(orgDir, "alerts")
|
||||
if err := os.MkdirAll(alertsDir, 0o700); err != nil {
|
||||
return fmt.Errorf("create active alerts dir: %w", err)
|
||||
}
|
||||
path := filepath.Join(alertsDir, "active-alerts.json")
|
||||
if err := os.WriteFile(path, data, 0o600); err != nil {
|
||||
return fmt.Errorf("write active alerts file: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func providerMSPProofFactInt(value *int) int {
|
||||
if value == nil {
|
||||
return 0
|
||||
}
|
||||
return *value
|
||||
}
|
||||
|
||||
type providerMSPProofEntitlementLease struct {
|
||||
Checked bool
|
||||
Verified bool
|
||||
@@ -1101,9 +1321,11 @@ func printProviderMSPProofReport(report *providerMSPProofReport) {
|
||||
fmt.Printf("setup_facts_token_use_visible=%t\n", report.SetupFactsTokenUseVisible)
|
||||
fmt.Printf("agent_report_ingest_verified=%t\n", report.AgentReportIngestVerified)
|
||||
fmt.Printf("token_rotation_verified=%t\n", report.TokenRotationVerified)
|
||||
fmt.Printf("report_schedule_visible=%t\n", report.ReportScheduleVisible)
|
||||
fmt.Printf("active_alert_rollup_visible=%t\n", report.ActiveAlertRollupVisible)
|
||||
fmt.Printf("cleanup=%t\n", report.Cleanup)
|
||||
for _, workspace := range report.Workspaces {
|
||||
fmt.Printf("workspace=%s display_name=%q state=%s plan_version=%s container_id=%s public_url=%s install_type=%s install_token_id=%s install_command_generated=%t agent_token_auth_verified=%t setup_facts_token_use_visible=%t agent_report_ingest_verified=%t agent_report_agent_id=%s agent_report_hostname=%s token_rotation_verified=%t rotated_install_token_id=%s old_install_token_rejected=%t rotated_agent_report_verified=%t handoff_exchange_verified=%t handoff_target_path=%s entitlement_lease_checked=%t entitlement_lease_verified=%t entitlement_white_label=%t entitlement_skipped_reason=%s\n",
|
||||
fmt.Printf("workspace=%s display_name=%q state=%s plan_version=%s container_id=%s public_url=%s install_type=%s install_token_id=%s install_command_generated=%t agent_token_auth_verified=%t setup_facts_token_use_visible=%t agent_report_ingest_verified=%t agent_report_agent_id=%s agent_report_hostname=%s token_rotation_verified=%t rotated_install_token_id=%s old_install_token_rejected=%t rotated_agent_report_verified=%t handoff_exchange_verified=%t handoff_target_path=%s entitlement_lease_checked=%t entitlement_lease_verified=%t entitlement_white_label=%t entitlement_skipped_reason=%s report_schedule_created=%t report_schedule_id=%s report_schedule_visible=%t report_schedule_count=%d disabled_report_schedule_count=%d active_alert_persisted=%t active_alert_rollup_visible=%t critical_alert_count=%d warning_alert_count=%d\n",
|
||||
workspace.TenantID,
|
||||
workspace.DisplayName,
|
||||
workspace.State,
|
||||
@@ -1128,6 +1350,15 @@ func printProviderMSPProofReport(report *providerMSPProofReport) {
|
||||
workspace.EntitlementLeaseVerified,
|
||||
workspace.EntitlementWhiteLabel,
|
||||
workspace.EntitlementSkippedReason,
|
||||
workspace.ReportScheduleCreated,
|
||||
workspace.ReportScheduleID,
|
||||
workspace.ReportScheduleVisible,
|
||||
workspace.ReportScheduleCount,
|
||||
workspace.DisabledReportScheduleCount,
|
||||
workspace.ActiveAlertPersisted,
|
||||
workspace.ActiveAlertRollupVisible,
|
||||
workspace.CriticalAlertCount,
|
||||
workspace.WarningAlertCount,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -141,6 +141,12 @@ func TestProviderMSPProofExercisesWorkspaceInstallHandoffAndIsolation(t *testing
|
||||
if !report.TokenRotationVerified {
|
||||
t.Fatal("token rotation was not verified")
|
||||
}
|
||||
if !report.ReportScheduleVisible {
|
||||
t.Fatal("report schedule portal fact was not verified")
|
||||
}
|
||||
if !report.ActiveAlertRollupVisible {
|
||||
t.Fatal("active alert portal rollup was not verified")
|
||||
}
|
||||
|
||||
seenTenants := map[string]struct{}{}
|
||||
for _, workspace := range report.Workspaces {
|
||||
@@ -202,6 +208,18 @@ func TestProviderMSPProofExercisesWorkspaceInstallHandoffAndIsolation(t *testing
|
||||
if !workspace.EntitlementWhiteLabel {
|
||||
t.Fatalf("entitlement lease for %s is missing white_label", workspace.TenantID)
|
||||
}
|
||||
if !workspace.ReportScheduleCreated || workspace.ReportScheduleID == "" || !workspace.ReportScheduleVisible {
|
||||
t.Fatalf("report schedule portal proof incomplete for %s: %#v", workspace.TenantID, workspace)
|
||||
}
|
||||
if workspace.ReportScheduleCount != 1 || workspace.DisabledReportScheduleCount != 0 {
|
||||
t.Fatalf("report schedule facts for %s = enabled:%d disabled:%d, want enabled:1 disabled:0", workspace.TenantID, workspace.ReportScheduleCount, workspace.DisabledReportScheduleCount)
|
||||
}
|
||||
if !workspace.ActiveAlertPersisted || !workspace.ActiveAlertRollupVisible {
|
||||
t.Fatalf("active alert portal rollup proof incomplete for %s: %#v", workspace.TenantID, workspace)
|
||||
}
|
||||
if workspace.CriticalAlertCount != 1 || workspace.WarningAlertCount != 1 {
|
||||
t.Fatalf("active alert facts for %s = critical:%d warning:%d, want critical:1 warning:1", workspace.TenantID, workspace.CriticalAlertCount, workspace.WarningAlertCount)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -254,7 +272,7 @@ func TestProviderMSPProofLoadsSignedLicenseFilePlan(t *testing.T) {
|
||||
if report.LicenseEmail != "provider@example.com" {
|
||||
t.Fatalf("LicenseEmail = %q, want provider@example.com", report.LicenseEmail)
|
||||
}
|
||||
if !report.AgentReportIngestVerified || !report.InstallTokenBoundaryOK || !report.TokenRotationVerified || !report.HandoffExchangeVerified {
|
||||
if !report.AgentReportIngestVerified || !report.InstallTokenBoundaryOK || !report.TokenRotationVerified || !report.HandoffExchangeVerified || !report.ReportScheduleVisible || !report.ActiveAlertRollupVisible {
|
||||
t.Fatalf("provider MSP proof did not complete core runtime checks: %#v", report)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -679,6 +679,11 @@ or other self-hosted uncapped continuity plans.
|
||||
age labels from read-only setup facts so providers can prioritize the
|
||||
workspace list, but it must not become an alert console or expose alert
|
||||
bodies, remediation state, acknowledgements, or cross-client alert streams.
|
||||
Those setup facts must read report schedule counts from the client runtime's
|
||||
org-scoped `report_schedules.json` store and active-alert counts from the
|
||||
org-scoped `alerts/active-alerts.json` runtime file before falling back to a
|
||||
legacy tenant-root active-alert file, because tenant monitors own
|
||||
org-scoped runtime persistence.
|
||||
Pulse Account also owns the provider-facing setup progression for client
|
||||
workspaces: after workspace creation the portal should select the created
|
||||
workspace, reveal the setup job, and preserve workspace/target context in
|
||||
|
||||
@@ -165,9 +165,11 @@ TLS floor in the dynamic config.
|
||||
`pulse-control-plane provider-msp proof` must exercise the first-client
|
||||
onboarding path through workspace creation, client-bound install token
|
||||
generation, tenant-local unified-agent report ingest, tenant-bound install
|
||||
token rotation, rotated-out token rejection, handoff exchange, and
|
||||
duplicate-hostname isolation before provider-hosted MSP installability is
|
||||
treated as proven. The proof is license-backed by default: `license_file` must be the
|
||||
token rotation, rotated-out token rejection, handoff exchange,
|
||||
tenant-runtime report schedule creation, portal-visible active-alert rollup
|
||||
facts, and duplicate-hostname isolation before provider-hosted MSP
|
||||
installability is treated as proven. The proof is license-backed by default:
|
||||
`license_file` must be the
|
||||
resolved provider MSP plan source unless the operator explicitly opts into
|
||||
the local-development `--allow-env-plan` escape hatch.
|
||||
The same proof surface must also keep adversarial client-boundary probes in
|
||||
|
||||
@@ -66,7 +66,7 @@ func readWorkspaceSetupFacts(tenantID, tenantDataDir, orgDir string) WorkspaceSe
|
||||
|
||||
facts.AgentCount, facts.AgentTokenCount, facts.UnusedAgentTokenCount, facts.LastAgentSeenAt = readAgentSetupFacts(tenantID, tenantDataDir, orgDir)
|
||||
facts.AlertRouteCount, facts.DisabledAlertRouteCount = readAlertRouteFacts(orgDir)
|
||||
facts.ActiveCriticalAlertCount, facts.ActiveWarningAlertCount, facts.ActiveAlertsUpdatedAt = readActiveAlertFacts(tenantDataDir)
|
||||
facts.ActiveCriticalAlertCount, facts.ActiveWarningAlertCount, facts.ActiveAlertsUpdatedAt = readActiveAlertFacts(tenantDataDir, orgDir)
|
||||
facts.ReportScheduleCount, facts.DisabledReportScheduleCount = readReportScheduleFacts(orgDir)
|
||||
return facts
|
||||
}
|
||||
@@ -219,26 +219,53 @@ func nonBlankCount(values []string) int {
|
||||
return count
|
||||
}
|
||||
|
||||
func readActiveAlertFacts(tenantDataDir string) (*int, *int, *time.Time) {
|
||||
path, ok := safeConfigLeafPath(tenantDataDir, filepath.Join("alerts", "active-alerts.json"))
|
||||
func readActiveAlertFacts(tenantDataDir, orgDir string) (*int, *int, *time.Time) {
|
||||
for _, dir := range uniqueActiveAlertFactDirs(orgDir, tenantDataDir) {
|
||||
critical, warning, updatedAt, ok := readActiveAlertFactsFromDir(dir)
|
||||
if ok {
|
||||
return critical, warning, updatedAt
|
||||
}
|
||||
}
|
||||
return nil, nil, nil
|
||||
}
|
||||
|
||||
func uniqueActiveAlertFactDirs(values ...string) []string {
|
||||
dirs := make([]string, 0, len(values))
|
||||
seen := map[string]struct{}{}
|
||||
for _, value := range values {
|
||||
value = strings.TrimSpace(value)
|
||||
if value == "" {
|
||||
continue
|
||||
}
|
||||
if _, exists := seen[value]; exists {
|
||||
continue
|
||||
}
|
||||
seen[value] = struct{}{}
|
||||
dirs = append(dirs, value)
|
||||
}
|
||||
return dirs
|
||||
}
|
||||
|
||||
func readActiveAlertFactsFromDir(configDir string) (*int, *int, *time.Time, bool) {
|
||||
path, ok := safeConfigLeafPath(configDir, filepath.Join("alerts", "active-alerts.json"))
|
||||
if !ok {
|
||||
return nil, nil, nil
|
||||
return nil, nil, nil, false
|
||||
}
|
||||
data, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
if !errors.Is(err, os.ErrNotExist) {
|
||||
log.Warn().Err(err).Str("tenant_data_dir", tenantDataDir).Msg("cloudcp.portal.setup_facts: read active alerts")
|
||||
log.Warn().Err(err).Str("config_dir", configDir).Msg("cloudcp.portal.setup_facts: read active alerts")
|
||||
}
|
||||
return nil, nil, nil
|
||||
return nil, nil, nil, false
|
||||
}
|
||||
if len(strings.TrimSpace(string(data))) == 0 {
|
||||
return nil, nil, nil
|
||||
return nil, nil, nil, true
|
||||
}
|
||||
|
||||
var active []alerts.Alert
|
||||
if err := json.Unmarshal(data, &active); err != nil {
|
||||
log.Warn().Err(err).Str("tenant_data_dir", tenantDataDir).Msg("cloudcp.portal.setup_facts: parse active alerts")
|
||||
return nil, nil, nil
|
||||
log.Warn().Err(err).Str("config_dir", configDir).Msg("cloudcp.portal.setup_facts: parse active alerts")
|
||||
return nil, nil, nil, true
|
||||
}
|
||||
|
||||
criticalCount := 0
|
||||
@@ -257,7 +284,7 @@ func readActiveAlertFacts(tenantDataDir string) (*int, *int, *time.Time) {
|
||||
ts := info.ModTime().UTC()
|
||||
updatedAt = &ts
|
||||
}
|
||||
return intPtr(criticalCount), intPtr(warningCount), updatedAt
|
||||
return intPtr(criticalCount), intPtr(warningCount), updatedAt, true
|
||||
}
|
||||
|
||||
func readReportScheduleFacts(orgDir string) (*int, *int) {
|
||||
|
||||
@@ -264,6 +264,30 @@ func TestTenantDirWorkspaceSetupFactReaderTreatsCorruptActiveAlertFileAsUnknown(
|
||||
}
|
||||
}
|
||||
|
||||
func TestTenantDirWorkspaceSetupFactReaderPrefersRuntimeOrgActiveAlerts(t *testing.T) {
|
||||
tenantsDir := t.TempDir()
|
||||
tenantDir := filepath.Join(tenantsDir, "ws_runtime_alerts")
|
||||
orgDir := filepath.Join(tenantDir, "orgs", "ws_runtime_alerts")
|
||||
if err := os.MkdirAll(orgDir, 0o755); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
writeSetupFactJSON(t, tenantDir, filepath.Join("alerts", "active-alerts.json"), []alerts.Alert{
|
||||
{ID: "legacy-warning", Level: alerts.AlertLevelWarning},
|
||||
})
|
||||
writeSetupFactJSON(t, orgDir, filepath.Join("alerts", "active-alerts.json"), []alerts.Alert{
|
||||
{ID: "runtime-critical", Level: alerts.AlertLevelCritical},
|
||||
{ID: "runtime-warning", Level: alerts.AlertLevelWarning},
|
||||
})
|
||||
|
||||
facts := NewTenantDirWorkspaceSetupFactReader(tenantsDir).FactsForWorkspace("ws_runtime_alerts")
|
||||
if facts.ActiveCriticalAlertCount == nil || *facts.ActiveCriticalAlertCount != 1 {
|
||||
t.Fatalf("ActiveCriticalAlertCount = %v, want 1", facts.ActiveCriticalAlertCount)
|
||||
}
|
||||
if facts.ActiveWarningAlertCount == nil || *facts.ActiveWarningAlertCount != 1 {
|
||||
t.Fatalf("ActiveWarningAlertCount = %v, want 1", facts.ActiveWarningAlertCount)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTenantDirWorkspaceSetupFactReaderCountsRuntimePersistedReportSchedules(t *testing.T) {
|
||||
tenantsDir := t.TempDir()
|
||||
orgDir := filepath.Join(tenantsDir, "ws_runtime", "orgs", "ws_runtime")
|
||||
|
||||
Reference in New Issue
Block a user