mirror of
https://gerrit.googlesource.com/git-repo
synced 2024-12-21 07:16:21 +00:00
Merge branch 'stable'
* stable: Fixed race condition in 'repo sync -jN' that would open multiple masters.
This commit is contained in:
commit
9275fd4329
146
git_config.py
146
git_config.py
@ -18,6 +18,10 @@ import os
|
|||||||
import re
|
import re
|
||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
|
try:
|
||||||
|
import threading as _threading
|
||||||
|
except ImportError:
|
||||||
|
import dummy_threading as _threading
|
||||||
import time
|
import time
|
||||||
import urllib2
|
import urllib2
|
||||||
|
|
||||||
@ -371,76 +375,97 @@ class RefSpec(object):
|
|||||||
_master_processes = []
|
_master_processes = []
|
||||||
_master_keys = set()
|
_master_keys = set()
|
||||||
_ssh_master = True
|
_ssh_master = True
|
||||||
|
_master_keys_lock = None
|
||||||
|
|
||||||
|
def init_ssh():
|
||||||
|
"""Should be called once at the start of repo to init ssh master handling.
|
||||||
|
|
||||||
|
At the moment, all we do is to create our lock.
|
||||||
|
"""
|
||||||
|
global _master_keys_lock
|
||||||
|
assert _master_keys_lock is None, "Should only call init_ssh once"
|
||||||
|
_master_keys_lock = _threading.Lock()
|
||||||
|
|
||||||
def _open_ssh(host, port=None):
|
def _open_ssh(host, port=None):
|
||||||
global _ssh_master
|
global _ssh_master
|
||||||
|
|
||||||
# Check to see whether we already think that the master is running; if we
|
# Acquire the lock. This is needed to prevent opening multiple masters for
|
||||||
# think it's already running, return right away.
|
# the same host when we're running "repo sync -jN" (for N > 1) _and_ the
|
||||||
if port is not None:
|
# manifest <remote fetch="ssh://xyz"> specifies a different host from the
|
||||||
key = '%s:%s' % (host, port)
|
# one that was passed to repo init.
|
||||||
else:
|
_master_keys_lock.acquire()
|
||||||
key = host
|
|
||||||
|
|
||||||
if key in _master_keys:
|
|
||||||
return True
|
|
||||||
|
|
||||||
if not _ssh_master \
|
|
||||||
or 'GIT_SSH' in os.environ \
|
|
||||||
or sys.platform in ('win32', 'cygwin'):
|
|
||||||
# failed earlier, or cygwin ssh can't do this
|
|
||||||
#
|
|
||||||
return False
|
|
||||||
|
|
||||||
# We will make two calls to ssh; this is the common part of both calls.
|
|
||||||
command_base = ['ssh',
|
|
||||||
'-o','ControlPath %s' % ssh_sock(),
|
|
||||||
host]
|
|
||||||
if port is not None:
|
|
||||||
command_base[1:1] = ['-p',str(port)]
|
|
||||||
|
|
||||||
# Since the key wasn't in _master_keys, we think that master isn't running.
|
|
||||||
# ...but before actually starting a master, we'll double-check. This can
|
|
||||||
# be important because we can't tell that that 'git@myhost.com' is the same
|
|
||||||
# as 'myhost.com' where "User git" is setup in the user's ~/.ssh/config file.
|
|
||||||
check_command = command_base + ['-O','check']
|
|
||||||
try:
|
try:
|
||||||
Trace(': %s', ' '.join(check_command))
|
|
||||||
check_process = subprocess.Popen(check_command,
|
|
||||||
stdout=subprocess.PIPE,
|
|
||||||
stderr=subprocess.PIPE)
|
|
||||||
check_process.communicate() # read output, but ignore it...
|
|
||||||
isnt_running = check_process.wait()
|
|
||||||
|
|
||||||
if not isnt_running:
|
# Check to see whether we already think that the master is running; if we
|
||||||
# Our double-check found that the master _was_ infact running. Add to
|
# think it's already running, return right away.
|
||||||
# the list of keys.
|
if port is not None:
|
||||||
_master_keys.add(key)
|
key = '%s:%s' % (host, port)
|
||||||
|
else:
|
||||||
|
key = host
|
||||||
|
|
||||||
|
if key in _master_keys:
|
||||||
return True
|
return True
|
||||||
except Exception:
|
|
||||||
# Ignore excpetions. We we will fall back to the normal command and print
|
|
||||||
# to the log there.
|
|
||||||
pass
|
|
||||||
|
|
||||||
command = command_base[:1] + \
|
if not _ssh_master \
|
||||||
['-M', '-N'] + \
|
or 'GIT_SSH' in os.environ \
|
||||||
command_base[1:]
|
or sys.platform in ('win32', 'cygwin'):
|
||||||
try:
|
# failed earlier, or cygwin ssh can't do this
|
||||||
Trace(': %s', ' '.join(command))
|
#
|
||||||
p = subprocess.Popen(command)
|
return False
|
||||||
except Exception, e:
|
|
||||||
_ssh_master = False
|
|
||||||
print >>sys.stderr, \
|
|
||||||
'\nwarn: cannot enable ssh control master for %s:%s\n%s' \
|
|
||||||
% (host,port, str(e))
|
|
||||||
return False
|
|
||||||
|
|
||||||
_master_processes.append(p)
|
# We will make two calls to ssh; this is the common part of both calls.
|
||||||
_master_keys.add(key)
|
command_base = ['ssh',
|
||||||
time.sleep(1)
|
'-o','ControlPath %s' % ssh_sock(),
|
||||||
return True
|
host]
|
||||||
|
if port is not None:
|
||||||
|
command_base[1:1] = ['-p',str(port)]
|
||||||
|
|
||||||
|
# Since the key wasn't in _master_keys, we think that master isn't running.
|
||||||
|
# ...but before actually starting a master, we'll double-check. This can
|
||||||
|
# be important because we can't tell that that 'git@myhost.com' is the same
|
||||||
|
# as 'myhost.com' where "User git" is setup in the user's ~/.ssh/config file.
|
||||||
|
check_command = command_base + ['-O','check']
|
||||||
|
try:
|
||||||
|
Trace(': %s', ' '.join(check_command))
|
||||||
|
check_process = subprocess.Popen(check_command,
|
||||||
|
stdout=subprocess.PIPE,
|
||||||
|
stderr=subprocess.PIPE)
|
||||||
|
check_process.communicate() # read output, but ignore it...
|
||||||
|
isnt_running = check_process.wait()
|
||||||
|
|
||||||
|
if not isnt_running:
|
||||||
|
# Our double-check found that the master _was_ infact running. Add to
|
||||||
|
# the list of keys.
|
||||||
|
_master_keys.add(key)
|
||||||
|
return True
|
||||||
|
except Exception:
|
||||||
|
# Ignore excpetions. We we will fall back to the normal command and print
|
||||||
|
# to the log there.
|
||||||
|
pass
|
||||||
|
|
||||||
|
command = command_base[:1] + \
|
||||||
|
['-M', '-N'] + \
|
||||||
|
command_base[1:]
|
||||||
|
try:
|
||||||
|
Trace(': %s', ' '.join(command))
|
||||||
|
p = subprocess.Popen(command)
|
||||||
|
except Exception, e:
|
||||||
|
_ssh_master = False
|
||||||
|
print >>sys.stderr, \
|
||||||
|
'\nwarn: cannot enable ssh control master for %s:%s\n%s' \
|
||||||
|
% (host,port, str(e))
|
||||||
|
return False
|
||||||
|
|
||||||
|
_master_processes.append(p)
|
||||||
|
_master_keys.add(key)
|
||||||
|
time.sleep(1)
|
||||||
|
return True
|
||||||
|
finally:
|
||||||
|
_master_keys_lock.release()
|
||||||
|
|
||||||
def close_ssh():
|
def close_ssh():
|
||||||
|
global _master_keys_lock
|
||||||
|
|
||||||
terminate_ssh_clients()
|
terminate_ssh_clients()
|
||||||
|
|
||||||
for p in _master_processes:
|
for p in _master_processes:
|
||||||
@ -459,6 +484,9 @@ def close_ssh():
|
|||||||
except OSError:
|
except OSError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
# We're done with the lock, so we can delete it.
|
||||||
|
_master_keys_lock = None
|
||||||
|
|
||||||
URI_SCP = re.compile(r'^([^@:]*@?[^:/]{1,}):')
|
URI_SCP = re.compile(r'^([^@:]*@?[^:/]{1,}):')
|
||||||
URI_ALL = re.compile(r'^([a-z][a-z+]*)://([^@/]*@?[^/]*)/')
|
URI_ALL = re.compile(r'^([a-z][a-z+]*)://([^@/]*@?[^/]*)/')
|
||||||
|
|
||||||
|
3
main.py
3
main.py
@ -28,7 +28,7 @@ import re
|
|||||||
import sys
|
import sys
|
||||||
|
|
||||||
from trace import SetTrace
|
from trace import SetTrace
|
||||||
from git_config import close_ssh
|
from git_config import init_ssh, close_ssh
|
||||||
from command import InteractiveCommand
|
from command import InteractiveCommand
|
||||||
from command import MirrorSafeCommand
|
from command import MirrorSafeCommand
|
||||||
from command import PagedCommand
|
from command import PagedCommand
|
||||||
@ -212,6 +212,7 @@ def _Main(argv):
|
|||||||
repo = _Repo(opt.repodir)
|
repo = _Repo(opt.repodir)
|
||||||
try:
|
try:
|
||||||
try:
|
try:
|
||||||
|
init_ssh()
|
||||||
repo._Run(argv)
|
repo._Run(argv)
|
||||||
finally:
|
finally:
|
||||||
close_ssh()
|
close_ssh()
|
||||||
|
Loading…
Reference in New Issue
Block a user