The access control aspect can be handled by independent plugins, such as
in Jenkins Enterprise by CloudBees (but there may be other ways of doing this I am less familiar with).
“Waiting for other jobs to complete” is automatic; the ‘node’ step will simply defer its body until it can grab an open executor slot on a matching node.
Well Workflow 1.2 will have a ‘waitUntil’ step so I guess you could make a DIY semaphore:
def acquired = 0
def branch(label) {
node(label) {
acquired++
waitUntil {acquired == 2}
// run stuff on this node
}
}
parallel server: {branch('server')}, client: {branch('client')}
This is prone to deadlocks, though. Making a deadlock-free semaphore is trickier (you need to sometimes back out of a workspace lock and wait before trying again); probably best left to a custom step.