67 lines
2.3 KiB
Go
67 lines
2.3 KiB
Go
package store
|
|
|
|
import (
|
|
"bytes"
|
|
"database/sql"
|
|
"errors"
|
|
|
|
agentv1 "git.ipao.vip/rogee/go-sip/gen/agent/v1"
|
|
"google.golang.org/protobuf/proto"
|
|
)
|
|
|
|
// BindExecutionAgent records the controlled endpoint identity before any remote
|
|
// execution. An execution may not be reassigned after a lost response.
|
|
func (s *Store) BindExecutionAgent(agentID string, binding *agentv1.ExecutionBinding) error {
|
|
if err := validateExecutionAgentBinding(agentID, binding); err != nil {
|
|
return err
|
|
}
|
|
s.mu.Lock()
|
|
defer s.mu.Unlock()
|
|
tx, err := s.db.Begin()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer tx.Rollback()
|
|
if err := bindExecutionAgentTx(tx, agentID, binding); err != nil {
|
|
return err
|
|
}
|
|
return tx.Commit()
|
|
}
|
|
|
|
func validateExecutionAgentBinding(agentID string, binding *agentv1.ExecutionBinding) error {
|
|
if agentID == "" || binding == nil {
|
|
return errors.New("agent and execution binding are required")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// bindExecutionAgentTx is shared by the legacy control route and the local
|
|
// Mock origination claim, which must commit its Agent assignment atomically.
|
|
func bindExecutionAgentTx(tx *sql.Tx, agentID string, binding *agentv1.ExecutionBinding) error {
|
|
encoded, err := (proto.MarshalOptions{Deterministic: true}).Marshal(binding)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
var count int
|
|
if err := tx.QueryRow(`SELECT COUNT(*) FROM tasks WHERE execution_id=? AND tenant_id=? AND tenant_key=? AND task_id=? AND task_item_id=? AND task_revision=? AND agent_version_id=? AND route_policy_id=? AND caller_profile_id=? AND status='reserved'`, binding.ExecutionId, binding.TenantId, binding.TenantKey, binding.TaskId, binding.TaskItemId, binding.TaskRevision, binding.AgentVersionId, binding.RoutePolicyId, binding.CallerProfileId).Scan(&count); err != nil {
|
|
return err
|
|
}
|
|
if count != 1 {
|
|
return ErrCASConflict
|
|
}
|
|
var previousAgent string
|
|
var previousBinding []byte
|
|
err = tx.QueryRow(`SELECT agent_id,binding FROM execution_agents WHERE execution_id=?`, binding.ExecutionId).Scan(&previousAgent, &previousBinding)
|
|
if err == nil {
|
|
if previousAgent != agentID || !bytes.Equal(previousBinding, encoded) {
|
|
return ErrIdempotencyConflict
|
|
}
|
|
return nil
|
|
}
|
|
if !errors.Is(err, sql.ErrNoRows) {
|
|
return err
|
|
}
|
|
_, err = tx.Exec(`INSERT INTO execution_agents(execution_id,agent_id,binding) VALUES(?,?,?)`, binding.ExecutionId, agentID, encoded)
|
|
return err
|
|
}
|