76 lines
3.6 KiB
Go
76 lines
3.6 KiB
Go
package dispatcher
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
|
|
agentpb "git.ipao.vip/rogee/go-sip/gen/agent"
|
|
"google.golang.org/protobuf/proto"
|
|
)
|
|
|
|
const localAuthorizedOriginationVersion = "agent-authorized-origination.v0.1"
|
|
|
|
// DispatchAuthorizedLocal is the only local path from an accepted task to
|
|
// Agent origination. The Dispatcher claims a bound decision immediately before
|
|
// the RPC; neither an uncertain response nor a rejected deadline is retried.
|
|
func (d *Dispatcher) DispatchAuthorizedLocal(ctx context.Context, executionID, agentID string, coordinator *AgentCoordinator) (DispatchResult, error) {
|
|
result, _, err := d.dispatchAuthorizedLocal(ctx, executionID, agentID, coordinator)
|
|
return result, err
|
|
}
|
|
|
|
func (d *Dispatcher) dispatchAuthorizedLocal(ctx context.Context, executionID, agentID string, coordinator *AgentCoordinator) (DispatchResult, LocalDialAuthorization, error) {
|
|
if coordinator == nil || agentID == "" {
|
|
return DispatchResult{}, LocalDialAuthorization{}, errors.New("activated Agent coordinator and Agent ID are required")
|
|
}
|
|
var result DispatchResult
|
|
var bound LocalDialAuthorization
|
|
err := d.OriginateLocal(ctx, executionID, agentID, func(ctx context.Context, authorized LocalDialAuthorization) error {
|
|
bound = authorized
|
|
var sendErr error
|
|
result, sendErr = coordinator.ExecuteAuthorized(ctx, agentID, authorized)
|
|
return sendErr
|
|
})
|
|
return result, bound, err
|
|
}
|
|
|
|
// ExecuteAuthorized sends precisely the previously claimed D decision. It
|
|
// does not obtain a new permit, reselect a line, or retry an uncertain RPC.
|
|
func (c *AgentCoordinator) ExecuteAuthorized(ctx context.Context, agentID string, authorized LocalDialAuthorization) (DispatchResult, error) {
|
|
if authorized.Binding == nil || authorized.Binding.ExecutionId == "" || authorized.SnapshotSHA256 == "" ||
|
|
authorized.TrunkID == "" || authorized.CallerID == "" || authorized.Callee == "" || authorized.RingTimeoutMS <= 0 ||
|
|
authorized.MaxCallDurationMS <= 0 || authorized.AllowedUntil.IsZero() {
|
|
return DispatchResult{}, errors.New("bound Agent instruction is incomplete")
|
|
}
|
|
client, session, err := c.clientAndSession(agentID)
|
|
if err != nil {
|
|
return DispatchResult{}, err
|
|
}
|
|
meta := c.meta(session, "authorized:"+authorized.Binding.ExecutionId, "authorized:"+authorized.Binding.ExecutionId)
|
|
request := &agentpb.ExecuteAuthorizedRequest{
|
|
SchemaVersion: localAuthorizedOriginationVersion,
|
|
Meta: meta, Binding: proto.Clone(authorized.Binding).(*agentpb.ExecutionBinding),
|
|
SelectedTrunkId: authorized.TrunkID, CallerId: authorized.CallerID, Callee: authorized.Callee,
|
|
RingTimeoutMs: authorized.RingTimeoutMS, MaxCallDurationMs: authorized.MaxCallDurationMS,
|
|
DialBeforeUnixMs: authorized.AllowedUntil.UnixMilli(), BoundSnapshotSha256: authorized.SnapshotSHA256,
|
|
}
|
|
response, err := client.ExecuteAuthorized(ctx, request)
|
|
if err != nil {
|
|
return c.reconcileUnknown(ctx, client, session, authorized.Binding, err)
|
|
}
|
|
if response == nil || response.Receipt == nil {
|
|
return c.reconcileUnknown(ctx, client, session, authorized.Binding, errors.New("Agent omitted authorized execution receipt"))
|
|
}
|
|
result := DispatchResult{Receipt: response.Receipt, State: response.State}
|
|
switch response.Receipt.Result {
|
|
case agentpb.ResultCode_RESULT_CODE_APPLIED:
|
|
if response.State == agentpb.ExecutionState_EXECUTION_STATE_TERMINAL {
|
|
return result, nil
|
|
}
|
|
case agentpb.ResultCode_RESULT_CODE_UNKNOWN:
|
|
result.Unknown = true
|
|
return result, fmt.Errorf("%w: Agent authorized execution is unknown", ErrRemoteResultUnknown)
|
|
}
|
|
return result, fmt.Errorf("Agent authorized execution not applied: result=%s state=%s", response.Receipt.Result, response.State)
|
|
}
|