feat: 实现可靠事件与证据 connector (#153)
This commit is contained in:
@@ -0,0 +1,119 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/ed25519"
|
||||
"crypto/rand"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/go-admin-team/go-admin-core/sdk"
|
||||
"gorm.io/driver/sqlite"
|
||||
"gorm.io/gorm"
|
||||
|
||||
"git.ilapage.cn/ila/yovision/Sense/server/app/sense/integration/bell_connector"
|
||||
"git.ilapage.cn/ila/yovision/Sense/server/app/sense/outbox"
|
||||
)
|
||||
|
||||
func TestBellConnectorRuntimeWiringIsOptionalAndMigrationGated(t *testing.T) {
|
||||
gin.SetMode(gin.TestMode)
|
||||
db, err := gorm.Open(sqlite.Open("file:sense-api-bell-connector?mode=memory&cache=shared"), &gorm.Config{})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
engine := gin.New()
|
||||
disabled := func(string) string { return "" }
|
||||
if err = startBellConnectorRuntime(context.Background(), engine, db, disabled); err != nil {
|
||||
t.Fatalf("disabled connector prevented Sense startup: %v", err)
|
||||
}
|
||||
if len(engine.Routes()) != 0 {
|
||||
t.Fatalf("disabled connector registered routes: %#v", engine.Routes())
|
||||
}
|
||||
enabled := func(key string) string {
|
||||
if key == "SENSE_EVENT_INGRESS_ENABLED" {
|
||||
return "true"
|
||||
}
|
||||
return ""
|
||||
}
|
||||
if err = startBellConnectorRuntime(context.Background(), gin.New(), db, enabled); err == nil {
|
||||
t.Fatal("enabled connector started without its formal migration")
|
||||
}
|
||||
|
||||
if err = db.AutoMigrate(
|
||||
&bell_connector.InboundEvent{}, &bell_connector.EvidenceRecord{}, &bell_connector.ReplayToken{},
|
||||
&outbox.Message{}, &outbox.DeliveryRecord{}, &outbox.Attempt{},
|
||||
); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
registryPath := writeSenseRegistry(t)
|
||||
runtimeEnvironment := func(key string) string {
|
||||
switch key {
|
||||
case "SENSE_EVENT_INGRESS_ENABLED":
|
||||
return "true"
|
||||
case "SENSE_MACHINE_PRINCIPAL_REGISTRY":
|
||||
return registryPath
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
registered := gin.New()
|
||||
if err = startBellConnectorRuntime(context.Background(), registered, db, runtimeEnvironment); err != nil {
|
||||
t.Fatalf("enabled connector did not register after migration: %v", err)
|
||||
}
|
||||
routes := registered.Routes()
|
||||
if len(routes) != 2 || routes[0].Path != "/v1/events" || routes[1].Path != "/v1/evidence/:evidence_id" {
|
||||
t.Fatalf("unexpected connector routes: %#v", routes)
|
||||
}
|
||||
}
|
||||
|
||||
func TestConnectorRuntimeUsesOnlyTheExplicitDefaultDatabase(t *testing.T) {
|
||||
defaultDB, err := gorm.Open(sqlite.Open("file:sense-default-runtime?mode=memory&cache=shared"), &gorm.Config{})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
secondaryDB, err := gorm.Open(sqlite.Open("file:sense-secondary-runtime?mode=memory&cache=shared"), &gorm.Config{})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
sdk.Runtime.SetDb("", defaultDB)
|
||||
sdk.Runtime.SetDb("analytics", secondaryDB)
|
||||
t.Cleanup(func() {
|
||||
sdk.Runtime.SetDb("", nil)
|
||||
sdk.Runtime.SetDb("analytics", nil)
|
||||
})
|
||||
if selected := defaultRuntimeDatabase(); selected != defaultDB {
|
||||
t.Fatalf("runtime selected a non-default database: %p", selected)
|
||||
}
|
||||
}
|
||||
|
||||
func writeSenseRegistry(t *testing.T) string {
|
||||
t.Helper()
|
||||
publicKey, _, err := ed25519.GenerateKey(rand.Reader)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
document := map[string]any{
|
||||
"version": "yovision.machine-principal-registry/v1",
|
||||
"audience": "yovision-sense",
|
||||
"principals": []any{map[string]any{
|
||||
"principal_id": "yv:brain:school-a", "enabled": true,
|
||||
"keys": []any{map[string]any{
|
||||
"kid": "brain-key-0001", "public_key_base64url": base64.RawURLEncoding.EncodeToString(publicKey),
|
||||
"status": "active", "scopes": []string{"events:ingest"},
|
||||
}},
|
||||
}},
|
||||
}
|
||||
encoded, err := json.Marshal(document)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
path := filepath.Join(t.TempDir(), "sense-registry.json")
|
||||
if err = os.WriteFile(path, encoded, 0o600); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
return path
|
||||
}
|
||||
@@ -17,9 +17,11 @@ import (
|
||||
"github.com/go-admin-team/go-admin-core/sdk/pkg"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/spf13/cobra"
|
||||
"gorm.io/gorm"
|
||||
|
||||
"git.ilapage.cn/ila/yovision/Sense/server/app/admin/models"
|
||||
"git.ilapage.cn/ila/yovision/Sense/server/app/admin/router"
|
||||
"git.ilapage.cn/ila/yovision/Sense/server/app/sense/integration/bell_connector"
|
||||
"git.ilapage.cn/ila/yovision/Sense/server/app/sense/media"
|
||||
"git.ilapage.cn/ila/yovision/Sense/server/common/database"
|
||||
"git.ilapage.cn/ila/yovision/Sense/server/common/global"
|
||||
@@ -88,15 +90,19 @@ func run() error {
|
||||
}
|
||||
runtimeCtx, runtimeCancel := context.WithCancel(context.Background())
|
||||
defer runtimeCancel()
|
||||
var runtimeDBFound bool
|
||||
for _, db := range sdk.Runtime.GetDb() {
|
||||
runtimeDBFound = true
|
||||
db := defaultRuntimeDatabase()
|
||||
if db != nil {
|
||||
engine, engineOK := sdk.Runtime.GetEngine().(*gin.Engine)
|
||||
if !engineOK || engine == nil {
|
||||
return errors.New("Sense connector runtime requires Gin engine")
|
||||
}
|
||||
if err := startBellConnectorRuntime(runtimeCtx, engine, db, os.Getenv); err != nil {
|
||||
return fmt.Errorf("Bell connector runtime unavailable: %w", err)
|
||||
}
|
||||
if err := media.StartRuntime(runtimeCtx, db); err != nil {
|
||||
return fmt.Errorf("MediaMTX runtime unavailable: %w", err)
|
||||
}
|
||||
break
|
||||
}
|
||||
if !runtimeDBFound {
|
||||
} else {
|
||||
log.Error("MediaMTX runtime unavailable: Sense database is not initialized")
|
||||
}
|
||||
|
||||
@@ -166,6 +172,14 @@ func run() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func startBellConnectorRuntime(ctx context.Context, engine *gin.Engine, db *gorm.DB, getenv bell_connector.Environment) error {
|
||||
return bell_connector.StartRuntime(ctx, engine, db, getenv)
|
||||
}
|
||||
|
||||
func defaultRuntimeDatabase() *gorm.DB {
|
||||
return sdk.Runtime.GetDbByKey("")
|
||||
}
|
||||
|
||||
//var Router runtime.Router
|
||||
|
||||
func tip() {
|
||||
|
||||
Reference in New Issue
Block a user