func()

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