From 8d11e482fece9df4d70b5d1ba93ef6a820b2a728 Mon Sep 17 00:00:00 2001 From: Dan Jacques Date: Tue, 15 Nov 2016 14:25:56 -0800 Subject: [PATCH] Strip netrc hack, rewrite file. Strip the netrc hack; apparently it only works on some versions of Python, and the bots have many different versions. Instead, we will actually emit the modified netrc as a new file without comments or newlines, then read that directly from the vanilla netrc module. BUG=chromium:664664 TEST=None Change-Id: I245759e2eae4e6ddcdffa0aaef59866a62bd0240 Reviewed-on: https://chromium-review.googlesource.com/411416 Commit-Queue: Daniel Jacques Reviewed-by: Ryan Tseng Reviewed-by: Michael Moss --- gerrit_util.py | 72 ++++++++++++++++++++++++++++++-------------------- 1 file changed, 43 insertions(+), 29 deletions(-) diff --git a/gerrit_util.py b/gerrit_util.py index ff3785819..eff98e319 100755 --- a/gerrit_util.py +++ b/gerrit_util.py @@ -9,6 +9,7 @@ https://gerrit-review.googlesource.com/Documentation/rest-api.html """ import base64 +import contextlib import cookielib import httplib import json @@ -16,14 +17,17 @@ import logging import netrc import os import re +import shutil import socket import stat import sys +import tempfile import time import urllib import urlparse from cStringIO import StringIO +import gclient_utils LOGGER = logging.getLogger() TRY_LIMIT = 5 @@ -34,21 +38,6 @@ TRY_LIMIT = 5 GERRIT_PROTOCOL = 'https' -# Processing comments in "netrc" can trigger a bug in Windows. -# See crbug.com/664664 -class safeNetrc(netrc.netrc): - # pylint: disable=redefined-builtin - def __init__(self, file=None): - self._orig_parse, self._parse = self._parse, self._safe_parse - netrc.netrc.__init__(self, file=file) - - # pylint: disable=redefined-builtin - def _safe_parse(self, file, fp, default_netrc): - # Buffer the file. - sio = StringIO(''.join(l for l in fp - if l.strip() and not l.strip().startswith('#'))) - return self._orig_parse(file, sio, default_netrc) - class GerritError(Exception): """Exception class for errors commuicating with the gerrit-on-borg service.""" @@ -129,27 +118,41 @@ class CookiesAuthenticator(Authenticator): @classmethod def _get_netrc(cls): + # Buffer the '.netrc' path. Use an empty file if it doesn't exist. path = cls.get_netrc_path() - if not os.path.exists(path): - return safeNetrc(os.devnull) - - try: - return safeNetrc(path) - except IOError: - print >> sys.stderr, 'WARNING: Could not read netrc file %s' % path - return safeNetrc(os.devnull) - except netrc.NetrcParseError: + content = '' + if os.path.exists(path): st = os.stat(path) if st.st_mode & (stat.S_IRWXG | stat.S_IRWXO): print >> sys.stderr, ( 'WARNING: netrc file %s cannot be used because its file ' 'permissions are insecure. netrc file permissions should be ' '600.' % path) - else: - print >> sys.stderr, ('ERROR: Cannot use netrc file %s due to a ' - 'parsing error.' % path) - raise - return safeNetrc(os.devnull) + with open(path) as fd: + content = fd.read() + + # Load the '.netrc' file. We strip comments from it because processing them + # can trigger a bug in Windows. See crbug.com/664664. + content = '\n'.join(l for l in content.splitlines() + if l.strip() and not l.strip().startswith('#')) + with tempdir() as tdir: + netrc_path = os.path.join(tdir, 'netrc') + with open(netrc_path, 'w') as fd: + fd.write(content) + os.chmod(netrc_path, (stat.S_IRUSR | stat.S_IWUSR)) + return cls._get_netrc_from_path(netrc_path) + + @classmethod + def _get_netrc_from_path(cls, path): + try: + return netrc.netrc(path) + except IOError: + print >> sys.stderr, 'WARNING: Could not read netrc file %s' % path + return netrc.netrc(os.devnull) + except netrc.NetrcParseError as e: + print >> sys.stderr, ('ERROR: Cannot use netrc file %s due to a ' + 'parsing error: %s' % (path, e)) + return netrc.netrc(os.devnull) @classmethod def get_gitcookies_path(cls): @@ -749,3 +752,14 @@ def ResetReviewLabels(host, change, label, value='0', message=None, elif jmsg[0]['current_revision'] != revision: raise GerritError(200, 'While resetting labels on change "%s", ' 'a new patchset was uploaded.' % change) + + +@contextlib.contextmanager +def tempdir(): + tdir = None + try: + tdir = tempfile.mkdtemp(suffix='gerrit_util') + yield tdir + finally: + if tdir: + gclient_utils.rmtree(tdir)