zuul-jobs/roles/fetch-zuul-cloner/templates/zuul-cloner-shim.py.j2
Ian Wienand 26b66a6a9e zuul-cloner: enter directory before copying
The "-a" argument to cp implies "--preserve=all" which changes the
permissions of the target directory; e.g.

 mkdir /tmp/foo
 chmod 700 /tmp/foo
 cp -al /some/dir/. /tmp/foo
 ls -l /tmp/foo

and you will find that /tmp/foo is probably not 700 any more.  This
causes problems when /tmp/foo is a home directory, because it can make
them world-writable and hence ssh won't log into them any more.

For simplicity, switch into the directory we want to copy into, and
then copy everything in.  This should stop cp touching the parent
directory in any way.

Change-Id: Icd842b6fba220d41601adeaa0c5a41b2dab582bc
2017-09-27 11:42:26 +10:00

161 lines
5.4 KiB
Django/Jinja

#!{{ destination | dirname }}/python
# Copyright 2017 Red Hat
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import argparse
import os
import re
import subprocess
import sys
import yaml
from collections import defaultdict
from collections import OrderedDict
REPO_SRC_DIR = "{{ repo_src_dir }}"
# Class copied from zuul/lib/conemapper.py with minor logging changes
class CloneMapper(object):
def __init__(self, clonemap, projects):
self.clonemap = clonemap
self.projects = projects
def expand(self, workspace):
print("Workspace path set to: %s" % workspace)
is_valid = True
ret = OrderedDict()
errors = []
for project in self.projects:
dests = []
for mapping in self.clonemap:
if re.match(r'^%s$' % mapping['name'], project):
# Might be matched more than one time
dests.append(
re.sub(mapping['name'], mapping['dest'], project))
if len(dests) > 1:
errors.append(
"Duplicate destinations for %s: %s." % (project, dests))
is_valid = False
elif len(dests) == 0:
print("Using %s as destination (unmatched)" % project)
ret[project] = [project]
else:
ret[project] = dests
if not is_valid:
raise Exception("Expansion errors: %s" % errors)
print("Mapping projects to workspace...")
for project, dest in ret.items():
dest = os.path.normpath(os.path.join(workspace, dest[0]))
ret[project] = dest
print(" %s -> %s" % (project, dest))
print("Checking overlap in destination directories...")
check = defaultdict(list)
for project, dest in ret.items():
check[dest].append(project)
dupes = dict((d, p) for (d, p) in check.items() if len(p) > 1)
if dupes:
raise Exception("Some projects share the same destination: %s",
dupes)
print("Expansion completed.")
return ret
def parseArgs():
ZUUL_ENV_SUFFIXES = ('branch', 'ref', 'url', 'project', 'newrev')
parser = argparse.ArgumentParser()
# Ignored arguments
parser.add_argument('-v', '--verbose', dest='verbose',
action='store_true', help='IGNORED')
parser.add_argument('--color', dest='color', action='store_true',
help='IGNORED')
parser.add_argument('--cache-dir', dest='cache_dir', help='IGNORED')
parser.add_argument('git_base_url', help='IGNORED')
parser.add_argument('--branch', help='IGNORED')
parser.add_argument('--project-branch', nargs=1, action='append',
metavar='PROJECT=BRANCH', help='IGNORED')
for zuul_suffix in ZUUL_ENV_SUFFIXES:
env_name = 'ZUUL_%s' % zuul_suffix.upper()
parser.add_argument(
'--zuul-%s' % zuul_suffix, metavar='$' + env_name,
help='IGNORED'
)
# Active arguments
parser.add_argument('-m', '--map', dest='clone_map_file',
help='specify clone map file')
parser.add_argument('--workspace', dest='workspace',
default=os.getcwd(),
help='where to clone repositories too')
parser.add_argument('projects', nargs='+',
help='list of Gerrit projects to clone')
return parser.parse_args()
def readCloneMap(clone_map):
clone_map_file = os.path.expanduser(clone_map)
if not os.path.exists(clone_map_file):
raise Exception("Unable to read clone map file at %s." %
clone_map_file)
clone_map_file = open(clone_map_file)
clone_map = yaml.safe_load(clone_map_file).get('clonemap')
return clone_map
def main():
args = parseArgs()
clone_map = []
if args.clone_map_file:
clone_map = readCloneMap(args.clone_map_file)
mapper = CloneMapper(clone_map, args.projects)
dests = mapper.expand(workspace=args.workspace)
for project in args.projects:
src = os.path.join(os.path.expanduser(REPO_SRC_DIR), project)
dst = dests[project]
# Remove the tail end of the path (since the copy operation will
# automatically create that)
d = dst.rstrip('/')
if not os.path.exists(d):
print("Creating %s" % d)
os.makedirs(d)
# Create hard link copy of the source directory. Note we cd
# into the target directory to avoid "cp -a" modifying the
# permissions of dst itself, which in particular can be fatal
# to ssh access when dst is a homedir
cmd = "cd %s; cp -al %s/. ." % (dst, src)
print("%s" % cmd)
if subprocess.call(cmd, shell=True) != 0:
print("Error executing: %s" % cmd)
sys.exit(1)
if __name__ == "__main__":
main()