cloud-init-dev team mailing list archive
-
cloud-init-dev team
-
Mailing list archive
-
Message #02631
Re: [Merge] ~chad.smith/cloud-init:ci-deps into cloud-init:master
Addressed review comments plus:
- dropped argparse filtering from read-dependencies and brpm now that master has baked argparse direclty into the spec for py26
- added dry-run param to read-dependencies to allow for viewing intended install actions
- made sure Requires in redhat and suse specs no longer contained any dependencies from test-requirements.txt
Diff comments:
> diff --git a/Makefile b/Makefile
> index a3bfaf7..faa391c 100644
> --- a/Makefile
> +++ b/Makefile
> @@ -78,6 +86,7 @@ clean_pyc:
> clean: clean_pyc
> rm -rf /var/log/cloud-init.log /var/lib/cloud/
>
> +
accidental, dropped.
> yaml:
> @$(PYVER) $(CWD)/tools/validate-yaml.py $(YAML_FILES)
>
> diff --git a/tools/read-dependencies b/tools/read-dependencies
> index f434905..149c2b0 100755
> --- a/tools/read-dependencies
> +++ b/tools/read-dependencies
> @@ -1,43 +1,194 @@
> #!/usr/bin/env python
> +"""List pip dependencies or system package dependencies for cloud-init."""
>
> # You might be tempted to rewrite this as a shell script, but you
> # would be surprised to discover that things like 'egrep' or 'sed' may
> # differ between Linux and *BSD.
>
> +try:
> + from argparse import ArgumentParser
> +except ImportError:
> + raise RuntimeError(
> + 'Could not import python-argparse. Please install python-argparse '
> + 'package to continue')
> +
> +import json
> import os
> import re
> -import sys
> import subprocess
> +import sys
>
> -if 'CLOUD_INIT_TOP_D' in os.environ:
> - topd = os.path.realpath(os.environ.get('CLOUD_INIT_TOP_D'))
> -else:
> - topd = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
>
> -for fname in ("setup.py", "requirements.txt"):
> - if not os.path.isfile(os.path.join(topd, fname)):
> - sys.stderr.write("Unable to locate '%s' file that should "
> - "exist in cloud-init root directory." % fname)
> - sys.exit(1)
> -
> -if len(sys.argv) > 1:
> - reqfile = sys.argv[1]
> -else:
> - reqfile = "requirements.txt"
> -
> -with open(os.path.join(topd, reqfile), "r") as fp:
> - for line in fp:
> - line = line.strip()
> - if not line or line.startswith("#"):
> +# Map the appropriate package dir needed for each distro choice
> +DISTRO_PKG_TYPE_MAP = {
> + 'centos': 'redhat',
> + 'redhat': 'redhat',
> + 'debian': 'debian',
> + 'ubuntu': 'debian',
> + 'opensuse': 'suse',
> + 'suse': 'suse'
> +}
> +
> +DISTRO_INSTALL_PKG_CMD = {
> + 'centos': ['yum', 'install', '--assumeyes'],
> + 'redhat': ['yum', 'install', '--assumeyes'],
> + 'debian': ['apt', 'install', '-y'],
> + 'ubuntu': ['apt', 'install', '-y'],
> + 'opensuse': ['zypper', 'install'],
> + 'suse': ['zypper', 'install']
> +}
> +
> +PY_26_ONLY = ['argparse']
> +
> +# List of base system packages required to start using make
> +EXTRA_SYSTEM_BASE_PKGS = ['make', 'sudo', 'tar']
> +
> +
> +# JSON definition of distro-specific package dependencies
> +DISTRO_PKG_DEPS_PATH = "packages/pkg-deps.json"
> +
> +
> +def get_parser():
> + """Return an argument parser for this command."""
> + parser = ArgumentParser(description=__doc__)
> + parser.add_argument(
> + '-r', '--requirements-file', type=str, dest='req_file',
> + default='requirements.txt', help='The pip-style requirements file')
> + parser.add_argument(
> + '-d', '--distro', type=str, choices=DISTRO_PKG_TYPE_MAP.keys(),
> + help='The name of the distro to generate package deps for.')
> + parser.add_argument(
> + '-s', '--system-pkg-names', action='store_true', default=False,
> + dest='system_pkg_names',
> + help='The name of the distro to generate package deps for.')
> + parser.add_argument(
> + '-i', '--install', action='store_true', default=False,
> + dest='install',
> + help='When specified, install the required system packages.')
> + parser.add_argument(
> + '-v', '--python-version', type=str, dest='python_version', default="2",
> + choices=["2", "3"],
> + help='The version of python we want to generate system package '
> + 'dependencies for.')
> + return parser
> +
> +
> +def get_package_deps_from_json(topdir, distro):
> + """Get a dict of build and runtime package requirements for a distro.
> +
> + @param topdir: The root directory in which to search for the
> + DISTRO_PKG_DEPS_PATH json blob of package requirements information.
> + @param distro: The specific distribution shortname to pull dependencies
> + for.
> + @return: Dict containing "requires", "build-requires" and "rename" lists
> + for a given distribution.
> + """
> + with open(os.path.join(topdir, DISTRO_PKG_DEPS_PATH), 'r') as stream:
> + deps = json.loads(stream.read())
> + if distro is None:
> + return {}
> + return deps[DISTRO_PKG_TYPE_MAP[distro]]
> +
> +
> +def parse_pip_requirements(requirements_path):
> + """Return the pip requirement names from pip-style requirements_path."""
> + dep_names = []
> + with open(requirements_path, "r") as fp:
> + for line in fp:
> + line = line.strip()
> + if not line or line.startswith("#"):
> + continue
> +
> + # remove pip-style markers
> + dep = line.split(';')[0]
> +
> + # remove version requirements
> + if re.search('[>=.<]+', dep):
> + dep_names.append(re.split("[>=.<]*", dep)[0].strip())
> + else:
> + dep_names.append(dep)
> + return dep_names
> +
> +
> +def translate_pip_to_system_pkg(pip_requires, renames, python_ver="2"):
> + """Translate pip package names to distro-specific package names.
> +
> + @param pip_requires: List of versionless pip package names to translate.
> + @param renames: Dict containg special case renames from pip name to system
> + package name for the distro.
> + """
> + if python_ver == "2":
> + prefix = "python-"
> + else:
> + prefix = "python3-"
> + standard_pkg_name = "{0}{1}"
> + translated_names = []
> + for pip_name in pip_requires:
> + pip_name = pip_name.lower()
> + if pip_name in PY_26_ONLY and sys.version_info[0:2] > (2, 6):
> + # Skip PY26 pkg deps unless we are actually on python 2.6
> continue
> + # Find a rename if present for the distro package and python version
> + rename = renames.get(pip_name, {}).get(python_ver, None)
> + if rename:
> + translated_names.append(rename)
> + else:
> + translated_names.append(
> + standard_pkg_name.format(prefix, pip_name))
> + return translated_names
> +
> +
> +def main(distro):
> + parser = get_parser()
> + args = parser.parse_args()
> + if 'CLOUD_INIT_TOP_D' in os.environ:
> + topd = os.path.realpath(os.environ.get('CLOUD_INIT_TOP_D'))
> + else:
> + topd = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
> + req_path = os.path.join(topd, args.req_file)
> + if not os.path.isfile(req_path):
> + sys.stderr.write("Unable to locate '%s' file that should "
> + "exist in cloud-init root directory." % req_path)
> + return 1
> + pip_pkg_names = parse_pip_requirements(req_path)
> + deps_from_json = get_package_deps_from_json(topd, args.distro)
> + renames = deps_from_json.get('renames', {})
> + translated_pip_names = translate_pip_to_system_pkg(
> + pip_pkg_names, renames, args.python_version)
> + all_deps = []
> + if args.distro:
> + all_deps.extend(
> + translated_pip_names + deps_from_json['requires'] +
> + deps_from_json['build-requires'])
> + else:
> + if args.system_pkg_names:
> + all_deps = translated_pip_names
> + else:
> + all_deps = pip_pkg_names
> + if args.install:
> + pkg_install(all_deps, args.distro)
> + else:
> + print('\n'.join(all_deps))
> +
>
> - # remove pip-style markers
> - dep = line.split(';')[0]
> +def pkg_install(pkg_list, distro):
> + """Install a list of packages using the DISTRO_INSTALL_PKG_CMD"""
> + print("Installing deps:", ' '.join(pkg_list))
> + pkg_list.extend(EXTRA_SYSTEM_BASE_PKGS)
> + if distro == 'centos':
fixed.
> + # CentOs needs epel-release to access oauthlib and jsonschema
fixed.
> + subprocess.check_call(DISTRO_INSTALL_PKG_CMD[distro] + ['epel-release'])
> + if distro in ['suse', 'opensuse', 'redhat', 'centos']:
> + pkg_list.append('rpm-build')
> + cmd = DISTRO_INSTALL_PKG_CMD[distro] + pkg_list
> + if os.getuid() != 0:
> + cmd.insert(0, 'sudo') # We aren't root so let's try sudo
> + subprocess.check_call(cmd)
>
> - # remove version requirements
> - dep = re.split("[>=.<]*", dep)[0].strip()
> - print(dep)
>
> -sys.exit(0)
> +if __name__ == "__main__":
> + parser = get_parser()
> + args = parser.parse_args()
> + sys.exit(main(args.distro))
>
> # vi: ts=4 expandtab
--
https://code.launchpad.net/~chad.smith/cloud-init/+git/cloud-init/+merge/325342
Your team cloud-init commiters is requested to review the proposed merge of ~chad.smith/cloud-init:ci-deps into cloud-init:master.
References