Merge ~chad.smith/cloud-init:pyver-fix into cloud-init:master

Proposed by Chad Smith
Status: Merged
Approved by: Scott Moser
Approved revision: a4f5cf8dfc587b59662af3754146d85798263292
Merged at revision: e7c95208451422cfd3da7edfbd67dd271e7aa337
Proposed branch: ~chad.smith/cloud-init:pyver-fix
Merge into: cloud-init:master
Diff against target: 485 lines (+209/-123)
8 files modified
Makefile (+8/-1)
packages/bddeb (+11/-32)
packages/brpm (+12/-28)
packages/debian/control.in (+2/-9)
packages/pkg-deps.json (+1/-0)
packages/redhat/cloud-init.spec.in (+6/-15)
packages/suse/cloud-init.spec.in (+3/-12)
tools/read-dependencies (+166/-26)
Reviewer Review Type Date Requested Status
Scott Moser Approve
Server Team CI bot continuous-integration Needs Fixing
Review via email: mp+325283@code.launchpad.net

Description of the change

makefile: fix python 2/3 detection in the Makefile

Fix detection of python2 in a non-python3 environment.
The previous version ahead ended up using python 3.

To post a comment you must log in.
Revision history for this message
Server Team CI bot (server-team-bot) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Scott Moser (smoser) wrote :

I'm going to grab this.
your request included a bunch of unrelated stuff, so I'll just cherry pick the Makefile commit.
thanks.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
diff --git a/Makefile b/Makefile
index 09cd147..1cec9ae 100644
--- a/Makefile
+++ b/Makefile
@@ -1,6 +1,6 @@
1CWD=$(shell pwd)1CWD=$(shell pwd)
2PYVER ?= $(shell for p in python3 python2; do \2PYVER ?= $(shell for p in python3 python2; do \
3 out=$(which $$p 2>&1) && echo $$p && exit; done; \3 out=$$(command -v $$p 2>&1) && echo $$p && exit; done; \
4 exit 1)4 exit 1)
5noseopts ?= -v5noseopts ?= -v
66
@@ -53,6 +53,12 @@ unittest: clean_pyc
53unittest3: clean_pyc53unittest3: clean_pyc
54 nosetests3 $(noseopts) tests/unittests54 nosetests3 $(noseopts) tests/unittests
5555
56ci-deps-ubuntu:
57 @$(PYVER) $(CWD)/tools/read-dependencies --distro ubuntu --install
58
59ci-deps-centos:
60 @$(PYVER) $(CWD)/tools/read-dependencies --distro centos --install
61
56pip-requirements:62pip-requirements:
57 @echo "Installing cloud-init dependencies..."63 @echo "Installing cloud-init dependencies..."
58 $(PIP_INSTALL) -r "$@.txt" -q64 $(PIP_INSTALL) -r "$@.txt" -q
@@ -75,6 +81,7 @@ clean_pyc:
75clean: clean_pyc81clean: clean_pyc
76 rm -rf /var/log/cloud-init.log /var/lib/cloud/82 rm -rf /var/log/cloud-init.log /var/lib/cloud/
7783
84
78yaml:85yaml:
79 @$(PYVER) $(CWD)/tools/validate-yaml.py $(YAML_FILES)86 @$(PYVER) $(CWD)/tools/validate-yaml.py $(YAML_FILES)
8087
diff --git a/packages/bddeb b/packages/bddeb
index f415209..e45af6e 100755
--- a/packages/bddeb
+++ b/packages/bddeb
@@ -24,19 +24,6 @@ if "avoid-pep8-E402-import-not-top-of-file":
24 from cloudinit import templater24 from cloudinit import templater
25 from cloudinit import util25 from cloudinit import util
2626
27# Package names that will showup in requires which have unique package names.
28# Format is '<pypi-name>': {'<python_major_version>': <pkg_name_or_none>, ...}.
29NONSTD_NAMED_PACKAGES = {
30 'argparse': {'2': 'python-argparse', '3': None},
31 'contextlib2': {'2': 'python-contextlib2', '3': None},
32 'cheetah': {'2': 'python-cheetah', '3': None},
33 'pyserial': {'2': 'python-serial', '3': 'python3-serial'},
34 'pyyaml': {'2': 'python-yaml', '3': 'python3-yaml'},
35 'six': {'2': 'python-six', '3': 'python3-six'},
36 'pep8': {'2': 'pep8', '3': 'python3-pep8'},
37 'pyflakes': {'2': 'pyflakes', '3': 'pyflakes'},
38}
39
40DEBUILD_ARGS = ["-S", "-d"]27DEBUILD_ARGS = ["-S", "-d"]
4128
4229
@@ -59,7 +46,6 @@ def write_debian_folder(root, templ_data, is_python2, cloud_util_deps):
59 else:46 else:
60 pyver = "3"47 pyver = "3"
61 python = "python3"48 python = "python3"
62 pkgfmt = "{}-{}"
6349
64 deb_dir = util.abs_join(root, 'debian')50 deb_dir = util.abs_join(root, 'debian')
6551
@@ -74,30 +60,23 @@ def write_debian_folder(root, templ_data, is_python2, cloud_util_deps):
74 params=templ_data)60 params=templ_data)
7561
76 # Write out the control file template62 # Write out the control file template
77 reqs = run_helper('read-dependencies').splitlines()63 reqs_output = run_helper(
64 'read-dependencies',
65 args=['--distro', 'debian', '--python-version', pyver])
66 reqs = reqs_output.splitlines()
78 test_reqs = run_helper(67 test_reqs = run_helper(
79 'read-dependencies', ['test-requirements.txt']).splitlines()68 'read-dependencies',
8069 ['--requirements-file', 'test-requirements.txt',
81 pypi_pkgs = [p.lower().strip() for p in reqs]70 '--system-pkg-names', '--python-version', pyver]).splitlines()
82 pypi_test_pkgs = [p.lower().strip() for p in test_reqs]
8371
84 # Map to known packages
85 requires = ['cloud-utils | cloud-guest-utils'] if cloud_util_deps else []72 requires = ['cloud-utils | cloud-guest-utils'] if cloud_util_deps else []
86 test_requires = []73 # We consolidate all deps as Build-Depends as our package build runs all
87 lists = ((pypi_pkgs, requires), (pypi_test_pkgs, test_requires))74 # tests so we need all runtime dependencies anyway.
88 for pypilist, target in lists:75 requires.extend(reqs + test_reqs + [python])
89 for p in pypilist:
90 if p in NONSTD_NAMED_PACKAGES:
91 if NONSTD_NAMED_PACKAGES[p][pyver]:
92 target.append(NONSTD_NAMED_PACKAGES[p][pyver])
93 else: # Then standard package prefix
94 target.append(pkgfmt.format(python, p))
95
96 templater.render_to_file(util.abs_join(find_root(),76 templater.render_to_file(util.abs_join(find_root(),
97 'packages', 'debian', 'control.in'),77 'packages', 'debian', 'control.in'),
98 util.abs_join(deb_dir, 'control'),78 util.abs_join(deb_dir, 'control'),
99 params={'requires': ','.join(requires),79 params={'build_depends': ','.join(requires),
100 'test_requires': ','.join(test_requires),
101 'python': python})80 'python': python})
10281
103 templater.render_to_file(util.abs_join(find_root(),82 templater.render_to_file(util.abs_join(find_root(),
diff --git a/packages/brpm b/packages/brpm
index 89696ab..b8e7c72 100755
--- a/packages/brpm
+++ b/packages/brpm
@@ -27,17 +27,6 @@ if "avoid-pep8-E402-import-not-top-of-file":
27 from cloudinit import templater27 from cloudinit import templater
28 from cloudinit import util28 from cloudinit import util
2929
30# Map python requirements to package names. If a match isn't found
31# here, we assume 'python-<pypi_name>'.
32PACKAGE_MAP = {
33 'redhat': {
34 'pyserial': 'pyserial',
35 'pyyaml': 'PyYAML',
36 },
37 'suse': {
38 'pyyaml': 'python-yaml',
39 }
40}
4130
42# Subdirectories of the ~/rpmbuild dir31# Subdirectories of the ~/rpmbuild dir
43RPM_BUILD_SUBDIRS = ['BUILD', 'RPMS', 'SOURCES', 'SPECS', 'SRPMS']32RPM_BUILD_SUBDIRS = ['BUILD', 'RPMS', 'SOURCES', 'SPECS', 'SRPMS']
@@ -57,19 +46,15 @@ def read_dependencies():
57 '''Returns the Python depedencies from requirements.txt. This explicitly46 '''Returns the Python depedencies from requirements.txt. This explicitly
58 removes 'argparse' from the list of requirements for python >= 2.7,47 removes 'argparse' from the list of requirements for python >= 2.7,
59 because with 2.7 argparse became part of the standard library.'''48 because with 2.7 argparse became part of the standard library.'''
60 stdout = run_helper('read-dependencies')49 pkg_deps = run_helper(
61 return [p.lower().strip() for p in stdout.splitlines()50 'read-dependencies', args=['--distro', 'redhat']).splitlines()
62 if p != 'argparse' or (p == 'argparse' and51 test_deps = run_helper(
63 sys.version_info[0:2] < (2, 7))]52 'read-dependencies', args=[
6453 '--requirements-file', 'test-requirements.txt',
6554 '--system-pkg-names']).splitlines()
66def translate_dependencies(deps, distro):55 return [pkg_dep for pkg_dep in pkg_deps + test_deps
67 '''Maps python requirements into package names. We assume56 if p != 'python-argparse' or (p == 'python-argparse' and
68 python-<pypi_name> for packages not listed explicitly in57 sys.version_info[0:2] < (2, 7))]
69 PACKAGE_MAP.'''
70 return [PACKAGE_MAP[distro][req]
71 if req in PACKAGE_MAP[distro] else 'python-%s' % req
72 for req in deps]
7358
7459
75def read_version():60def read_version():
@@ -99,10 +84,9 @@ def generate_spec_contents(args, version_data, tmpl_fn, top_dir, arc_fn):
99 rpm_upstream_version = version_data['version']84 rpm_upstream_version = version_data['version']
100 subs['rpm_upstream_version'] = rpm_upstream_version85 subs['rpm_upstream_version'] = rpm_upstream_version
10186
102 # Map to known packages87 deps = read_dependencies()
103 python_deps = read_dependencies()88 subs['buildrequires'] = deps
104 package_deps = translate_dependencies(python_deps, args.distro)89 subs['requires'] = deps
105 subs['requires'] = package_deps
10690
107 if args.boot == 'sysvinit':91 if args.boot == 'sysvinit':
108 subs['sysvinit'] = True92 subs['sysvinit'] = True
diff --git a/packages/debian/control.in b/packages/debian/control.in
index 6c39d53..265b261 100644
--- a/packages/debian/control.in
+++ b/packages/debian/control.in
@@ -3,20 +3,13 @@ Source: cloud-init
3Section: admin3Section: admin
4Priority: optional4Priority: optional
5Maintainer: Scott Moser <smoser@ubuntu.com>5Maintainer: Scott Moser <smoser@ubuntu.com>
6Build-Depends: debhelper (>= 9),6Build-Depends: ${build_depends}
7 dh-python,
8 dh-systemd,
9 ${python},
10 ${test_requires},
11 ${requires}
12XS-Python-Version: all7XS-Python-Version: all
13Standards-Version: 3.9.68Standards-Version: 3.9.6
149
15Package: cloud-init10Package: cloud-init
16Architecture: all11Architecture: all
17Depends: procps,12Depends: ${misc:Depends},
18 ${python},
19 ${misc:Depends},
20 ${${python}:Depends}13 ${${python}:Depends}
21Recommends: eatmydata, sudo, software-properties-common, gdisk14Recommends: eatmydata, sudo, software-properties-common, gdisk
22XB-Python-Version: ${python:Versions}15XB-Python-Version: ${python:Versions}
diff --git a/packages/pkg-deps.json b/packages/pkg-deps.json
23new file mode 10064416new file mode 100644
index 0000000..042cde7
--- /dev/null
+++ b/packages/pkg-deps.json
@@ -0,0 +1 @@
1{"debian": {"build-requires": ["debhelper", "dh-python", "dh-systemd"], "requires": ["procps"], "renames": {"argparse": {"2": "python-argparse", "3": null},"contextlib2": {"2": "python-contextlib2", "3": null}, "cheetah": {"2": "python-cheetah", "3": null}, "pyserial": {"2": "python-serial", "3": "python3-serial"}, "pyyaml": {"2": "python-yaml", "3": "python3-yaml"}, "six": {"2": "python-six", "3": "python3-six"}, "pep8": {"2": "pep8", "3": "python3-pep8"}, "pyflakes": {"2": "pyflakes", "3": "pyflakes"}}}, "redhat": {"requires": ["shadow-utils", "rsyslog", "iproute", "e2fsprogs", "net-tools", "procps", "shadow-utils", "sudo >= 1.7.2p2-3"], "build-requires": ["python-devel", "python-setuptools", "python-cheetah"], "renames": {"pyserial": {"2": "pyserial", "3": null}, "pyyaml": {"2": "PyYAML", "3": null}}}, "suse": {"build-requires": ["fdupes", "filesystem", "python-devel", "python-setuptools", "python-cheetah"], "requires": ["iproute2", "e2fsprogs", "net-tools", "procps", "sudo"], "renames": {"pyyaml": {"2": "python-yaml", "3": null}}}}
diff --git a/packages/redhat/cloud-init.spec.in b/packages/redhat/cloud-init.spec.in
index fd3cf93..a62d860 100644
--- a/packages/redhat/cloud-init.spec.in
+++ b/packages/redhat/cloud-init.spec.in
@@ -18,21 +18,12 @@ Source0: ${archive_name}
18BuildArch: noarch18BuildArch: noarch
19BuildRoot: %{_tmppath}19BuildRoot: %{_tmppath}
2020
21BuildRequires: python-devel21# Install 'dynamic' build reqs from *requirements.txt and pkg-deps.json
22BuildRequires: python-setuptools22#for $r in $buildrequires
23BuildRequires: python-cheetah23BuildRequires: ${r}
2424#end for
25# System util packages needed25
26Requires: shadow-utils26# Install 'dynamic' runtime reqs from *requirements.txt and pkg-deps.json
27Requires: rsyslog
28Requires: iproute
29Requires: e2fsprogs
30Requires: net-tools
31Requires: procps
32Requires: shadow-utils
33Requires: sudo >= 1.7.2p2-3
34
35# Install pypi 'dynamic' requirements
36#for $r in $requires27#for $r in $requires
37Requires: ${r}28Requires: ${r}
38#end for29#end for
diff --git a/packages/suse/cloud-init.spec.in b/packages/suse/cloud-init.spec.in
index 6ce0be8..444401c 100644
--- a/packages/suse/cloud-init.spec.in
+++ b/packages/suse/cloud-init.spec.in
@@ -22,11 +22,9 @@ BuildRoot: %{_tmppath}/%{name}-%{version}-build
22BuildArch: noarch22BuildArch: noarch
23%endif23%endif
2424
25BuildRequires: fdupes25#for $r in $buildrequires
26BuildRequires: filesystem26BuildRequires: ${r}
27BuildRequires: python-devel27#end for
28BuildRequires: python-setuptools
29BuildRequires: python-cheetah
3028
31%if 0%{?suse_version} && 0%{?suse_version} <= 121029%if 0%{?suse_version} && 0%{?suse_version} <= 1210
32 %define initsys sysvinit30 %define initsys sysvinit
@@ -34,13 +32,6 @@ BuildRequires: python-cheetah
34 %define initsys systemd32 %define initsys systemd
35%endif33%endif
3634
37# System util packages needed
38Requires: iproute2
39Requires: e2fsprogs
40Requires: net-tools
41Requires: procps
42Requires: sudo
43
44# Install pypi 'dynamic' requirements35# Install pypi 'dynamic' requirements
45#for $r in $requires36#for $r in $requires
46Requires: ${r}37Requires: ${r}
diff --git a/tools/read-dependencies b/tools/read-dependencies
index f434905..e01e718 100755
--- a/tools/read-dependencies
+++ b/tools/read-dependencies
@@ -1,43 +1,183 @@
1#!/usr/bin/env python1#!/usr/bin/env python
2"""List pip dependencies or system package dependencies for cloud-init."""
23
3# You might be tempted to rewrite this as a shell script, but you4# You might be tempted to rewrite this as a shell script, but you
4# would be surprised to discover that things like 'egrep' or 'sed' may5# would be surprised to discover that things like 'egrep' or 'sed' may
5# differ between Linux and *BSD.6# differ between Linux and *BSD.
67
8try:
9 from argparse import ArgumentParser
10except ImportError:
11 raise RuntimeError(
12 'Could not import python-argparse. Please install python-argparse '
13 'package to continue')
14
15import json
7import os16import os
8import re17import re
9import sys
10import subprocess18import subprocess
19import sys
1120
12if 'CLOUD_INIT_TOP_D' in os.environ:
13 topd = os.path.realpath(os.environ.get('CLOUD_INIT_TOP_D'))
14else:
15 topd = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
1621
17for fname in ("setup.py", "requirements.txt"):22# Map the appropriate package dir needed for each distro choice
18 if not os.path.isfile(os.path.join(topd, fname)):23DISTRO_PKG_TYPE_MAP = {
19 sys.stderr.write("Unable to locate '%s' file that should "24 'centos': 'redhat',
20 "exist in cloud-init root directory." % fname)25 'redhat': 'redhat',
21 sys.exit(1)26 'debian': 'debian',
2227 'ubuntu': 'debian',
23if len(sys.argv) > 1:28 'opensuse': 'suse',
24 reqfile = sys.argv[1]29 'suse': 'suse'
25else:30}
26 reqfile = "requirements.txt"31
2732DISTRO_INSTALL_PKG_CMD = {
28with open(os.path.join(topd, reqfile), "r") as fp:33 'cent': ['yum', 'install'],
29 for line in fp:34 'redhat': ['yum', 'install'],
30 line = line.strip()35 'debian': ['apt', 'install'],
31 if not line or line.startswith("#"):36 'ubuntu': ['apt', 'install'],
37 'opensuse': ['zypper', 'install'],
38 'suse': ['zypper', 'install']
39}
40
41PY_26_ONLY = ['argparse']
42
43
44# JSON definition of distro-specific package dependencies
45DISTRO_PKG_DEPS_PATH = "packages/pkg-deps.json"
46
47
48def get_parser():
49 """Return an argument parser for this command."""
50 parser = ArgumentParser(description=__doc__)
51 parser.add_argument(
52 '-r', '--requirements-file', type=str, dest='req_file',
53 default='requirements.txt', help='The pip-style requirements file')
54 parser.add_argument(
55 '-d', '--distro', type=str, choices=DISTRO_PKG_TYPE_MAP.keys(),
56 help='The name of the distro to generate package deps for.')
57 parser.add_argument(
58 '-s', '--system-pkg-names', action='store_true', default=False,
59 dest='system_pkg_names',
60 help='The name of the distro to generate package deps for.')
61 parser.add_argument(
62 '-i', '--install', action='store_true', default=False,
63 dest='install',
64 help='When specified, install the required system packages.')
65 parser.add_argument(
66 '-v', '--python-version', type=str, dest='python_version', default="2",
67 choices=["2", "3"],
68 help='The version of python we want to generate system package '
69 'dependencies for.')
70 return parser
71
72
73def get_package_deps_from_json(topdir, distro):
74 """Get a dict of build and runtime package requirements for a distro.
75
76 @param topdir: The root directory in which to search for the
77 DISTRO_PKG_DEPS_PATH json blob of package requirements information.
78 @param distro: The specific distribution shortname to pull dependencies
79 for.
80 @return: Dict containing "requires", "build-requires" and "rename" lists
81 for a given distribution.
82 """
83 with open(os.path.join(topdir, DISTRO_PKG_DEPS_PATH), 'r') as stream:
84 deps = json.loads(stream.read())
85 if distro is None:
86 return {}
87 return deps[DISTRO_PKG_TYPE_MAP[distro]]
88
89
90def parse_pip_requirements(requirements_path):
91 """Return the pip requirement names from pip-style requirements_path."""
92 dep_names = []
93 with open(requirements_path, "r") as fp:
94 for line in fp:
95 line = line.strip()
96 if not line or line.startswith("#"):
97 continue
98
99 # remove pip-style markers
100 dep = line.split(';')[0]
101
102 # remove version requirements
103 if re.search('[>=.<]+', dep):
104 dep_names.append(re.split("[>=.<]*", dep)[0].strip())
105 else:
106 dep_names.append(dep)
107 return dep_names
108
109
110def translate_pip_to_system_pkg(pip_requires, renames, python_ver="2"):
111 """Translate pip package names to distro-specific package names.
112
113 @param pip_requires: List of versionless pip package names to translate.
114 @param renames: Dict containg special case renames from pip name to system
115 package name for the distro.
116 """
117 if python_ver == "2":
118 prefix = "python-"
119 else:
120 prefix = "python3-"
121 standard_pkg_name = "{0}{1}"
122 translated_names = []
123 for pip_name in pip_requires:
124 pip_name = pip_name.lower()
125 if pip_name in PY_26_ONLY and sys.version_info[0:2] > (2, 6):
126 # Skip PY26 pkg deps unless we are actually on python 2.6
32 continue127 continue
128 # Find a rename if present for the distro package and python version
129 rename = renames.get(pip_name, {}).get(python_ver, None)
130 if rename:
131 translated_names.append(rename)
132 else:
133 translated_names.append(
134 standard_pkg_name.format(prefix, pip_name))
135 return translated_names
136
137
138def main(distro):
139 parser = get_parser()
140 args = parser.parse_args()
141 if 'CLOUD_INIT_TOP_D' in os.environ:
142 topd = os.path.realpath(os.environ.get('CLOUD_INIT_TOP_D'))
143 else:
144 topd = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
145 req_path = os.path.join(topd, args.req_file)
146 if not os.path.isfile(req_path):
147 sys.stderr.write("Unable to locate '%s' file that should "
148 "exist in cloud-init root directory." % req_path)
149 return 1
150 pip_pkg_names = parse_pip_requirements(req_path)
151 deps_from_json = get_package_deps_from_json(topd, args.distro)
152 renames = deps_from_json.get('renames', {})
153 translated_pip_names = translate_pip_to_system_pkg(
154 pip_pkg_names, renames, args.python_version)
155 all_deps = []
156 if args.distro:
157 all_deps.extend(
158 translated_pip_names + deps_from_json['requires'] +
159 deps_from_json['build-requires'])
160 else:
161 if args.system_pkg_names:
162 all_deps = translated_pip_names
163 else:
164 all_deps = pip_pkg_names
165 if args.install:
166 pkg_install(all_deps, args.distro)
167 else:
168 print('\n'.join(all_deps))
169
33170
34 # remove pip-style markers171def pkg_install(pkg_list, distro):
35 dep = line.split(';')[0]172 """Install a list of packages using the DISTRO_INSTALL_PKG_CMD"""
173 print("Installing deps:", ' '.join(pkg_list))
174 cmd = ['sudo'] + DISTRO_INSTALL_PKG_CMD[distro] + pkg_list
175 subprocess.check_call(cmd)
36176
37 # remove version requirements
38 dep = re.split("[>=.<]*", dep)[0].strip()
39 print(dep)
40177
41sys.exit(0)178if __name__ == "__main__":
179 parser = get_parser()
180 args = parser.parse_args()
181 sys.exit(main(args.distro))
42182
43# vi: ts=4 expandtab183# vi: ts=4 expandtab

Subscribers

People subscribed via source and target branches