Files

66 lines
2.2 KiB
Go

package config
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"io"
"os"
"strings"
)
// AgentEndpoint is deployment-only Dispatcher configuration. It is not a SaaS
// command field and cannot be supplied by a tenant or a call task.
type AgentEndpoint struct {
AgentID string `json:"agent_id"`
CellID string `json:"cell_id"`
Address string `json:"address"`
ServerName string `json:"server_name"`
}
// LoadAgentEndpoints reads the Dispatcher-owned endpoint inventory. Strict JSON
// decoding prevents silently accepting misspelled authorization or identity
// fields.
func LoadAgentEndpoints(path string) ([]AgentEndpoint, error) {
if strings.TrimSpace(path) == "" {
return nil, nil
}
data, err := os.ReadFile(path)
if err != nil {
return nil, fmt.Errorf("read Agent endpoint inventory: %w", err)
}
decoder := json.NewDecoder(bytes.NewReader(data))
decoder.DisallowUnknownFields()
var endpoints []AgentEndpoint
if err := decoder.Decode(&endpoints); err != nil {
return nil, fmt.Errorf("decode Agent endpoint inventory: %w", err)
}
var trailing any
if err := decoder.Decode(&trailing); !errors.Is(err, io.EOF) {
if err == nil {
return nil, errors.New("Agent endpoint inventory contains trailing JSON")
}
return nil, fmt.Errorf("read Agent endpoint inventory trailer: %w", err)
}
if len(endpoints) == 0 {
return nil, errors.New("Agent endpoint inventory must not be empty")
}
seenAgents := make(map[string]struct{}, len(endpoints))
seenCells := make(map[string]struct{}, len(endpoints))
for index, endpoint := range endpoints {
if strings.TrimSpace(endpoint.AgentID) == "" || strings.TrimSpace(endpoint.CellID) == "" || strings.TrimSpace(endpoint.Address) == "" || strings.TrimSpace(endpoint.ServerName) == "" {
return nil, fmt.Errorf("Agent endpoint %d requires agent_id, cell_id, address, and server_name", index)
}
if _, exists := seenAgents[endpoint.AgentID]; exists {
return nil, fmt.Errorf("duplicate Agent ID %q", endpoint.AgentID)
}
if _, exists := seenCells[endpoint.CellID]; exists {
return nil, fmt.Errorf("duplicate Cell ID %q", endpoint.CellID)
}
seenAgents[endpoint.AgentID] = struct{}{}
seenCells[endpoint.CellID] = struct{}{}
}
return endpoints, nil
}