72 lines
3.1 KiB
Go
72 lines
3.1 KiB
Go
package rpc
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"log/slog"
|
|
|
|
agentpb "git.ipao.vip/rogee/go-sip/gen/agent"
|
|
"git.ipao.vip/rogee/go-sip/internal/asterisk"
|
|
"git.ipao.vip/rogee/go-sip/internal/configread"
|
|
"google.golang.org/grpc/codes"
|
|
"google.golang.org/grpc/status"
|
|
)
|
|
|
|
// ApplySIP is deliberately unavailable on the Mock call server. The real
|
|
// SIP-only Agent accepts a complete Dispatcher-owned snapshot, then reports
|
|
// only revisions it actually applied and inspected in native Asterisk.
|
|
func (s *Server) ApplySIP(ctx context.Context, req *agentpb.ApplySIPRequest) (*agentpb.ApplySIPResponse, error) {
|
|
if req == nil || req.Meta == nil || len(req.ApprovedSnapshotJson) == 0 || len(req.ApprovedSnapshotJson) > 1<<20 {
|
|
return nil, status.Error(codes.InvalidArgument, "approved SIP request or bounded snapshot is required")
|
|
}
|
|
if err := s.authorize(ctx, req.Meta); err != nil {
|
|
return nil, err
|
|
}
|
|
dispatcherID, err := s.sessions.ApprovedDispatcher(req.Meta, s.now())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if s.mode != "sip-only" || s.applySIP == nil {
|
|
return nil, status.Error(codes.FailedPrecondition, "real SIP apply is unavailable on this Agent")
|
|
}
|
|
var approved configread.SIP
|
|
if err := json.Unmarshal(req.ApprovedSnapshotJson, &approved); err != nil || approved.DispatcherID != dispatcherID || approved.Revision <= 0 {
|
|
return nil, status.Error(codes.InvalidArgument, "SIP snapshot owner or content is invalid")
|
|
}
|
|
if _, err := asterisk.Render(approved); err != nil {
|
|
return nil, status.Error(codes.InvalidArgument, "SIP snapshot is not approved for native reload")
|
|
}
|
|
var trunks []struct {
|
|
ID string `json:"trunk_id"`
|
|
Enabled bool `json:"enabled"`
|
|
}
|
|
if err := json.Unmarshal(approved.Trunks, &trunks); err != nil {
|
|
return nil, status.Error(codes.InvalidArgument, "SIP trunk list is invalid")
|
|
}
|
|
expected := make(map[string]bool)
|
|
for _, trunk := range trunks {
|
|
if trunk.Enabled {
|
|
expected[trunk.ID] = true
|
|
}
|
|
}
|
|
s.sipMu.Lock()
|
|
defer s.sipMu.Unlock()
|
|
loaded, err := s.applySIP(ctx, req.ApprovedSnapshotJson)
|
|
if err != nil {
|
|
slog.Error("native SIP apply failed", "dispatcher_id", dispatcherID, "revision", approved.Revision, "error", err)
|
|
return nil, status.Error(codes.Unavailable, "native Asterisk SIP reload failed")
|
|
}
|
|
if len(loaded) != len(expected) {
|
|
slog.Error("native SIP loaded-set mismatch", "dispatcher_id", dispatcherID, "revision", approved.Revision, "expected_count", len(expected), "loaded_count", len(loaded))
|
|
return nil, status.Error(codes.Unavailable, "native Asterisk SIP loaded-set mismatch")
|
|
}
|
|
for trunk, revision := range loaded {
|
|
if !expected[trunk] || revision != approved.Revision {
|
|
slog.Error("native SIP loaded-revision mismatch", "dispatcher_id", dispatcherID, "revision", approved.Revision, "trunk_id", trunk, "observed_revision", revision)
|
|
return nil, status.Error(codes.Unavailable, "native Asterisk SIP loaded-revision mismatch")
|
|
}
|
|
}
|
|
slog.Info("native Asterisk SIP revision confirmed", "dispatcher_id", dispatcherID, "revision", approved.Revision, "trunk_count", len(loaded))
|
|
return &agentpb.ApplySIPResponse{TrunkRevision: loaded}, nil
|
|
}
|