135 lines
3.7 KiB
Go
135 lines
3.7 KiB
Go
package managed
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
|
|
"gopkg.in/yaml.v3"
|
|
)
|
|
|
|
var ErrServiceNotFound = errors.New("managed service not found")
|
|
|
|
type Registry struct {
|
|
Services []Service `yaml:"services"`
|
|
}
|
|
|
|
type Service struct {
|
|
ID string `yaml:"id" json:"id"`
|
|
DisplayName string `yaml:"display_name" json:"display_name"`
|
|
ProjectType string `yaml:"project_type" json:"project_type"`
|
|
ProjectRoot string `yaml:"project_root" json:"project_root"`
|
|
ContainerName string `yaml:"container_name" json:"container_name"`
|
|
APIBaseURL string `yaml:"api_base_url" json:"api_base_url"`
|
|
ServiceName string `yaml:"service_name" json:"service_name"`
|
|
ConfigPath string `yaml:"config_path" json:"config_path"`
|
|
RTSPField string `yaml:"rtsp_field" json:"rtsp_field"`
|
|
ResultType string `yaml:"result_type" json:"result_type"`
|
|
ResultPaths map[string]string `yaml:"result_paths" json:"result_paths"`
|
|
}
|
|
|
|
func EmptyRegistry() *Registry {
|
|
return &Registry{Services: []Service{}}
|
|
}
|
|
|
|
func LoadRegistry(path string) (*Registry, error) {
|
|
data, err := os.ReadFile(path)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("read managed registry: %w", err)
|
|
}
|
|
|
|
var registry Registry
|
|
if err := yaml.Unmarshal(data, ®istry); err != nil {
|
|
return nil, fmt.Errorf("parse managed registry: %w", err)
|
|
}
|
|
|
|
baseDir := filepath.Dir(path)
|
|
ids := make(map[string]struct{}, len(registry.Services))
|
|
for i := range registry.Services {
|
|
svc := ®istry.Services[i]
|
|
if err := normalizeService(baseDir, svc); err != nil {
|
|
return nil, fmt.Errorf("service[%d]: %w", i, err)
|
|
}
|
|
if _, exists := ids[svc.ID]; exists {
|
|
return nil, fmt.Errorf("duplicate service id %q", svc.ID)
|
|
}
|
|
ids[svc.ID] = struct{}{}
|
|
}
|
|
|
|
if registry.Services == nil {
|
|
registry.Services = []Service{}
|
|
}
|
|
|
|
return ®istry, nil
|
|
}
|
|
|
|
func (r *Registry) Get(id string) (Service, bool) {
|
|
for _, svc := range r.Services {
|
|
if svc.ID == id {
|
|
return svc, true
|
|
}
|
|
}
|
|
return Service{}, false
|
|
}
|
|
|
|
func normalizeService(baseDir string, svc *Service) error {
|
|
svc.ID = strings.TrimSpace(svc.ID)
|
|
svc.DisplayName = strings.TrimSpace(svc.DisplayName)
|
|
svc.ProjectType = strings.TrimSpace(svc.ProjectType)
|
|
svc.ContainerName = strings.TrimSpace(svc.ContainerName)
|
|
svc.APIBaseURL = strings.TrimSpace(svc.APIBaseURL)
|
|
svc.ServiceName = strings.TrimSpace(svc.ServiceName)
|
|
svc.ConfigPath = strings.TrimSpace(svc.ConfigPath)
|
|
svc.RTSPField = strings.TrimSpace(svc.RTSPField)
|
|
svc.ResultType = strings.TrimSpace(svc.ResultType)
|
|
|
|
if svc.ID == "" {
|
|
return errors.New("id is required")
|
|
}
|
|
if svc.DisplayName == "" {
|
|
return errors.New("display_name is required")
|
|
}
|
|
if svc.ProjectType == "" {
|
|
return errors.New("project_type is required")
|
|
}
|
|
if svc.ContainerName == "" {
|
|
return errors.New("container_name is required")
|
|
}
|
|
if svc.APIBaseURL == "" {
|
|
return errors.New("api_base_url is required")
|
|
}
|
|
if svc.ResultType == "" {
|
|
return errors.New("result_type is required")
|
|
}
|
|
|
|
projectRoot := strings.TrimSpace(svc.ProjectRoot)
|
|
if projectRoot == "" {
|
|
return errors.New("project_root is required")
|
|
}
|
|
svc.ProjectRoot = resolvePath(baseDir, projectRoot)
|
|
if svc.ServiceName == "" {
|
|
svc.ServiceName = svc.ContainerName
|
|
}
|
|
if svc.ConfigPath != "" {
|
|
svc.ConfigPath = resolvePath(baseDir, svc.ConfigPath)
|
|
}
|
|
|
|
if svc.ResultPaths == nil {
|
|
svc.ResultPaths = map[string]string{}
|
|
}
|
|
for key, path := range svc.ResultPaths {
|
|
svc.ResultPaths[key] = resolvePath(baseDir, strings.TrimSpace(path))
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func resolvePath(baseDir, path string) string {
|
|
if filepath.IsAbs(path) {
|
|
return filepath.Clean(path)
|
|
}
|
|
return filepath.Clean(filepath.Join(baseDir, path))
|
|
}
|