feat: Phase 7 — SeedService, SecretsService, ProvisioningService + ssh Provisioner
Seed ingest/export moves behind ports.SeedRepository (SeedRepo in the postgres adapter; knowledge ingest absorbed from internal/knowledge, package deleted). pct_create flow (defaults, template/VMID/gateway pre-flights, pct create, graph registration) moves from httpapi's approved-execution path into app.ProvisioningService + the ssh provisioner adapter; CLI seed/export/secret become adapters over the services. EntityCreateInput gains EnrolledAt. Plan status corrected: phases 0-7 shipped, 8 + 9 gates open. VERSION 0.34.1.
This commit is contained in:
95
internal/core/app/seed.go
Normal file
95
internal/core/app/seed.go
Normal file
@@ -0,0 +1,95 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"os"
|
||||
|
||||
"github.com/dtoro/oikos/internal/core/ports"
|
||||
)
|
||||
|
||||
// SeedService is the bootstrap/DR use-case: ingest the seeds/*.yaml files
|
||||
// into the DB (idempotent, ontology-validated) and regenerate them from
|
||||
// the DB for version control. Absorbed the ingest orchestration that
|
||||
// lived in cmd/oikos runSeed and the export entry point in runExport
|
||||
// (Phase 7); the SQL stays behind ports.SeedRepository in the postgres
|
||||
// adapter.
|
||||
type SeedService struct {
|
||||
repo ports.SeedRepository
|
||||
}
|
||||
|
||||
// NewSeedService wires the service.
|
||||
func NewSeedService(repo ports.SeedRepository) *SeedService {
|
||||
return &SeedService{repo: repo}
|
||||
}
|
||||
|
||||
// Ingest reads the seed YAMLs from dir and ingests them in dependency
|
||||
// order: ontology (types) before inventory (entities/edges validated
|
||||
// against them), then policy, then knowledge — which is optional and
|
||||
// skipped silently when absent. Files whose content hash is unchanged
|
||||
// are no-ops at the repository layer.
|
||||
func (s *SeedService) Ingest(ctx context.Context, dir string) error {
|
||||
type step struct {
|
||||
file string
|
||||
ingest func(ctx context.Context, filename string, content []byte) (ports.SeedCounts, bool, error)
|
||||
optional bool
|
||||
}
|
||||
steps := []step{
|
||||
{"ontology.yaml", s.repo.IngestOntology, false},
|
||||
{"inventory.yaml", s.repo.IngestInventory, false},
|
||||
{"policy.yaml", s.repo.IngestPolicy, false},
|
||||
{"knowledge.yaml", s.repo.IngestKnowledge, true},
|
||||
}
|
||||
|
||||
for _, st := range steps {
|
||||
content, err := os.ReadFile(dir + "/" + st.file)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) && st.optional {
|
||||
slog.Info("knowledge seed not found, skipping")
|
||||
continue
|
||||
}
|
||||
return fmt.Errorf("read %s seed: %w", st.file, err)
|
||||
}
|
||||
|
||||
counts, applied, err := st.ingest(ctx, st.file, content)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !applied {
|
||||
continue
|
||||
}
|
||||
|
||||
switch st.file {
|
||||
case "ontology.yaml":
|
||||
slog.Info("ontology ingested",
|
||||
"lifecycles", counts.Lifecycles,
|
||||
"entity_types", counts.EntityTypes,
|
||||
"relationship_types", counts.RelationshipTypes)
|
||||
case "inventory.yaml":
|
||||
slog.Info("inventory ingested",
|
||||
"entities", counts.Entities,
|
||||
"relationships", counts.Relationships)
|
||||
case "policy.yaml":
|
||||
slog.Info("policy ingested",
|
||||
"risk_classes", counts.RiskClasses,
|
||||
"approval_rules", counts.ApprovalRules,
|
||||
"autonomy_settings", counts.AutonomySettings)
|
||||
case "knowledge.yaml":
|
||||
slog.Info("knowledge ingested",
|
||||
"documents", counts.Documents,
|
||||
"investigations", counts.Investigations,
|
||||
"runbooks", counts.Runbooks)
|
||||
}
|
||||
}
|
||||
|
||||
slog.Info("seed ingest complete")
|
||||
return nil
|
||||
}
|
||||
|
||||
// Export regenerates the seed YAMLs from the DB (DR / version control).
|
||||
// Deterministic: the repository orders lists and sorts map keys so
|
||||
// export → ingest → export is byte-stable.
|
||||
func (s *SeedService) Export(ctx context.Context) (map[string][]byte, error) {
|
||||
return s.repo.Export(ctx)
|
||||
}
|
||||
Reference in New Issue
Block a user