Files
sub2api-cn-relay-manager/internal/provision/provider_status_service.go
phamnazage-jpg 71cbaf5fa6 test(project): achieve ≥70% package coverage across all internal packages
- store/sqlite: 75.4% (repos + db coverage)
- host/sub2api: 80.8% (httptest mock server, pure function tests)
- app: 74.2% (handler error paths, NewActionSet closures)
- pack: 72.4%
- provision: 75.2%
- access: 77.3%
- config: 94.7% (lookup mock tests)

All tests pass: build, vet, race, coverage gates.
2026-05-15 19:26:25 +08:00

151 lines
4.7 KiB
Go

package provision
import (
"context"
"encoding/json"
"fmt"
"strings"
"sub2api-cn-relay-manager/internal/store/sqlite"
)
type ProviderQuery struct {
ProviderID string
PackID string
}
type ProviderSnapshot struct {
Host sqlite.Host
Pack sqlite.Pack
Provider sqlite.Provider
Batch sqlite.ImportBatch
ManagedResources []sqlite.ManagedResource
AccessClosures []sqlite.AccessClosureRecord
ReconcileRuns []sqlite.ReconcileRun
ProviderStatus string
LatestAccessStatus string
LatestReconcileStatus string
LatestReconcileSummary map[string]any
}
type ProviderStatusService struct {
store *sqlite.DB
}
func NewProviderStatusService(store *sqlite.DB) *ProviderStatusService {
return &ProviderStatusService{store: store}
}
func (s *ProviderStatusService) GetStatus(ctx context.Context, query ProviderQuery) (ProviderSnapshot, error) {
return s.snapshot(ctx, query)
}
func (s *ProviderStatusService) GetResources(ctx context.Context, query ProviderQuery) (ProviderSnapshot, error) {
return s.snapshot(ctx, query)
}
func (s *ProviderStatusService) snapshot(ctx context.Context, query ProviderQuery) (ProviderSnapshot, error) {
if s == nil || s.store == nil {
return ProviderSnapshot{}, fmt.Errorf("store is required")
}
provider, err := s.resolveProvider(ctx, query)
if err != nil {
return ProviderSnapshot{}, err
}
packRow, err := s.store.Packs().GetByID(ctx, provider.PackID)
if err != nil {
return ProviderSnapshot{}, err
}
batchRow, err := s.store.ImportBatches().GetLatestByProviderID(ctx, provider.ID)
if err != nil {
return ProviderSnapshot{}, err
}
hostRow, err := s.store.Hosts().GetByID(ctx, batchRow.HostID)
if err != nil {
return ProviderSnapshot{}, err
}
managedResources, err := s.store.ManagedResources().GetByBatchID(ctx, batchRow.ID)
if err != nil {
return ProviderSnapshot{}, err
}
accessClosures, err := s.store.AccessClosures().GetByBatchID(ctx, batchRow.ID)
if err != nil {
return ProviderSnapshot{}, err
}
reconcileRuns, err := s.store.ReconcileRuns().GetByProviderID(ctx, provider.ID)
if err != nil {
return ProviderSnapshot{}, err
}
latestAccessStatus := batchRow.AccessStatus
if len(accessClosures) > 0 {
latestAccessStatus = firstNonEmpty(accessClosures[len(accessClosures)-1].Status, latestAccessStatus)
}
latestReconcileStatus := "not_run"
latestReconcileSummary := map[string]any{}
if len(reconcileRuns) > 0 {
latestReconcileStatus = firstNonEmpty(reconcileRuns[0].Status, latestReconcileStatus)
if strings.TrimSpace(reconcileRuns[0].SummaryJSON) != "" {
if err := json.Unmarshal([]byte(reconcileRuns[0].SummaryJSON), &latestReconcileSummary); err != nil {
return ProviderSnapshot{}, fmt.Errorf("decode reconcile summary: %w", err)
}
}
}
providerStatus := deriveProviderStatus(batchRow.BatchStatus, latestReconcileStatus)
return ProviderSnapshot{
Host: hostRow,
Pack: packRow,
Provider: provider,
Batch: batchRow,
ManagedResources: managedResources,
AccessClosures: accessClosures,
ReconcileRuns: reconcileRuns,
ProviderStatus: providerStatus,
LatestAccessStatus: latestAccessStatus,
LatestReconcileStatus: latestReconcileStatus,
LatestReconcileSummary: latestReconcileSummary,
}, nil
}
func (s *ProviderStatusService) resolveProvider(ctx context.Context, query ProviderQuery) (sqlite.Provider, error) {
providerID := strings.TrimSpace(query.ProviderID)
packID := strings.TrimSpace(query.PackID)
if providerID == "" {
return sqlite.Provider{}, fmt.Errorf("provider_id is required")
}
if packID != "" {
packRow, err := s.store.Packs().GetByPackID(ctx, packID)
if err != nil {
return sqlite.Provider{}, err
}
return s.store.Providers().GetByPackIDAndProviderID(ctx, packRow.ID, providerID)
}
providers, err := s.store.Providers().ListByProviderID(ctx, providerID)
if err != nil {
return sqlite.Provider{}, err
}
if len(providers) == 0 {
return sqlite.Provider{}, fmt.Errorf("provider %q not found", providerID)
}
if len(providers) > 1 {
return sqlite.Provider{}, fmt.Errorf("provider %q exists in multiple packs; pack_id is required", providerID)
}
return providers[0], nil
}
func deriveProviderStatus(batchStatus, reconcileStatus string) string {
reconcileStatus = strings.TrimSpace(reconcileStatus)
if reconcileStatus != "" && reconcileStatus != "not_run" {
return reconcileStatus
}
switch strings.TrimSpace(batchStatus) {
case BatchStatusSucceeded:
return ProviderStatusActive
case BatchStatusFailed:
return ProviderStatusFailed
case "running":
return "running"
default:
return firstNonEmpty(batchStatus, "unknown")
}
}