Files
go-sip/internal/dispatcher/authorized_agent.go
T

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)
}