in plugins/targetlocker/dblocker/dblocker.go [258:298]
func (d *DBLocker) handleUnlock(ctx xcontext.Context, jobID int64, targets []string) error {
if len(targets) == 0 {
return nil
}
tx, err := d.db.Begin()
if err != nil {
return fmt.Errorf("unable to start database transaction: %w", err)
}
defer func() {
// this always fails if tx.Commit() was called before, ignore error
_ = tx.Rollback()
}()
// check lock states: must own locks for all targets (expired is ok too).
locks, err := d.queryLocks(tx, targets)
if err != nil {
return err
}
for _, t := range targets {
l, ok := locks[t]
if !ok {
return fmt.Errorf("target %q is not locked", t)
}
if l.jobID != jobID {
return fmt.Errorf("unlock request: target %q is locked by %q, not by %q", t, l.jobID, jobID)
}
}
// drop non-conflicting locks
del := "DELETE FROM locks WHERE job_id = ? AND target_id IN " + listQueryString(uint(len(targets)))
queryList := make([]interface{}, 0, len(targets)+1)
queryList = append(queryList, jobID)
for _, targetID := range targets {
queryList = append(queryList, targetID)
}
if _, err := tx.Exec(del, queryList...); err != nil {
return fmt.Errorf("unable to unlock targets %v, owner %d: %w", targets, jobID, err)
}
return tx.Commit()
}