* Add openssh-client to default docker container.
* Include Azure requirements in default container.
To do so, handling of pip requirements was updated to install each
set of requirements separately and then run a verification pass to
make sure there are no conflicts between requirements.
* Add missing --docker-no-pull option.
* Add documentation for the azure-requirements test.
(cherry picked from commit 36b13e3e3d2d7d976adb3f976527fc916285777c)
1 | 1 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,10 @@ |
0 |
+Sanity Tests ยป azure-requirements |
|
1 |
+================================= |
|
2 |
+ |
|
3 |
+Update the Azure integration test requirements file when changes are made to the Azure packaging requirements file: |
|
4 |
+ |
|
5 |
+.. code-block:: bash |
|
6 |
+ |
|
7 |
+ cp packaging/requirements/requirements-azure.txt test/runner/requirements/integration.cloud.azure.txt |
|
8 |
+ |
|
9 |
+This copy of the requirements file is used when building the ``ansible/ansible:default`` Docker container from ``test/runner/Dockerfile``. |
1 | 11 |
deleted file mode 100644 |
... | ... |
@@ -1,8 +0,0 @@ |
1 |
-- name: install cryptography requirement |
|
2 |
- # preempt the installation of cryptography from requirements-azure.txt to limit the version installed |
|
3 |
- # requests[security] requires cryptography >= 1.3.4 |
|
4 |
- # cryptography 2.1 requires pip 8.1.2+ |
|
5 |
- command: pip install cryptography>=1.3.4,<2.1 |
|
6 |
- |
|
7 |
-- pip: |
|
8 |
- requirements: '{{ role_path }}/../../../../packaging/requirements/requirements-azure.txt' |
... | ... |
@@ -11,13 +11,60 @@ requirements=() |
11 | 11 |
|
12 | 12 |
for requirement in *.txt; do |
13 | 13 |
if [ "${requirement}" != "constraints.txt" ]; then |
14 |
- requirements+=("-r" "${requirement}") |
|
14 |
+ requirements+=("${requirement}") |
|
15 | 15 |
fi |
16 | 16 |
done |
17 | 17 |
|
18 | 18 |
for python_version in "${python_versions[@]}"; do |
19 |
+ version_requirements=() |
|
20 |
+ |
|
21 |
+ for requirement in "${requirements[@]}"; do |
|
22 |
+ case "${python_version}" in |
|
23 |
+ "2.6") |
|
24 |
+ case "${requirement}" in |
|
25 |
+ "integration.cloud.azure.txt") continue ;; |
|
26 |
+ esac |
|
27 |
+ esac |
|
28 |
+ |
|
29 |
+ version_requirements+=("${requirement}") |
|
30 |
+ done |
|
31 |
+ |
|
32 |
+ echo "==> Installing pip for python ${python_version} ..." |
|
33 |
+ |
|
19 | 34 |
set -x |
20 | 35 |
"python${python_version}" /tmp/get-pip.py -c constraints.txt |
21 |
- "pip${python_version}" install --disable-pip-version-check -c constraints.txt "${requirements[@]}" |
|
22 | 36 |
set +x |
37 |
+ |
|
38 |
+ echo "==> Installing requirements for python ${python_version} ..." |
|
39 |
+ |
|
40 |
+ for requirement in "${version_requirements[@]}"; do |
|
41 |
+ set -x |
|
42 |
+ "pip${python_version}" install --disable-pip-version-check -c constraints.txt -r "${requirement}" |
|
43 |
+ set +x |
|
44 |
+ done |
|
45 |
+ |
|
46 |
+ echo "==> Checking for requirements conflicts for ${python_version} ..." |
|
47 |
+ |
|
48 |
+ after=$("pip${python_version}" list) |
|
49 |
+ |
|
50 |
+ for requirement in "${version_requirements[@]}"; do |
|
51 |
+ before="${after}" |
|
52 |
+ |
|
53 |
+ set -x |
|
54 |
+ "pip${python_version}" install --disable-pip-version-check -c constraints.txt -r "${requirement}" |
|
55 |
+ set +x |
|
56 |
+ |
|
57 |
+ after=$("pip${python_version}" list) |
|
58 |
+ |
|
59 |
+ if [ "${before}" != "${after}" ]; then |
|
60 |
+ echo "==> Conflicts detected in requirements for python ${python_version}: ${requirement}" |
|
61 |
+ echo ">>> Before" |
|
62 |
+ echo "${before}" |
|
63 |
+ echo ">>> After" |
|
64 |
+ echo "${after}" |
|
65 |
+ exit 1 |
|
66 |
+ fi |
|
67 |
+ done |
|
68 |
+ |
|
69 |
+ echo "==> Finished with requirements for python ${python_version}." |
|
23 | 70 |
done |
... | ... |
@@ -13,6 +13,7 @@ import textwrap |
13 | 13 |
import functools |
14 | 14 |
import shutil |
15 | 15 |
import stat |
16 |
+import pipes |
|
16 | 17 |
import random |
17 | 18 |
import string |
18 | 19 |
import atexit |
... | ... |
@@ -162,30 +163,78 @@ def install_command_requirements(args): |
162 | 162 |
if args.junit: |
163 | 163 |
packages.append('junit-xml') |
164 | 164 |
|
165 |
- extras = [] |
|
165 |
+ commands = [generate_pip_install(args.command, packages=packages)] |
|
166 | 166 |
|
167 | 167 |
if isinstance(args, IntegrationConfig): |
168 |
- extras += ['cloud.%s' % cp for cp in get_cloud_platforms(args)] |
|
168 |
+ for cloud_platform in get_cloud_platforms(args): |
|
169 |
+ commands.append(generate_pip_install('%s.cloud.%s' % (args.command, cloud_platform))) |
|
169 | 170 |
|
170 |
- cmd = generate_pip_install(args.command, packages, extras) |
|
171 |
+ # only look for changes when more than one requirements file is needed |
|
172 |
+ detect_pip_changes = len(commands) > 1 |
|
171 | 173 |
|
172 |
- if not cmd: |
|
173 |
- return |
|
174 |
+ # first pass to install requirements, changes expected unless environment is already set up |
|
175 |
+ changes = run_pip_commands(args, commands, detect_pip_changes) |
|
174 | 176 |
|
175 |
- try: |
|
176 |
- run_command(args, cmd) |
|
177 |
- except SubprocessError as ex: |
|
178 |
- if ex.status != 2: |
|
179 |
- raise |
|
177 |
+ if not changes: |
|
178 |
+ return # no changes means we can stop early |
|
179 |
+ |
|
180 |
+ # second pass to check for conflicts in requirements, changes are not expected here |
|
181 |
+ changes = run_pip_commands(args, commands, detect_pip_changes) |
|
182 |
+ |
|
183 |
+ if not changes: |
|
184 |
+ return # no changes means no conflicts |
|
185 |
+ |
|
186 |
+ raise ApplicationError('Conflicts detected in requirements. The following commands reported changes during verification:\n%s' % |
|
187 |
+ '\n'.join((' '.join(pipes.quote(c) for c in cmd) for cmd in changes))) |
|
188 |
+ |
|
189 |
+ |
|
190 |
+def run_pip_commands(args, commands, detect_pip_changes=False): |
|
191 |
+ """ |
|
192 |
+ :type args: EnvironmentConfig |
|
193 |
+ :type commands: list[list[str]] |
|
194 |
+ :type detect_pip_changes: bool |
|
195 |
+ :rtype: list[list[str]] |
|
196 |
+ """ |
|
197 |
+ changes = [] |
|
198 |
+ |
|
199 |
+ after_list = pip_list(args) if detect_pip_changes else None |
|
200 |
+ |
|
201 |
+ for cmd in commands: |
|
202 |
+ if not cmd: |
|
203 |
+ continue |
|
180 | 204 |
|
181 |
- # If pip is too old it won't understand the arguments we passed in, so we'll need to upgrade it. |
|
205 |
+ before_list = after_list |
|
182 | 206 |
|
183 |
- # Installing "coverage" on ubuntu 16.04 fails with the error: |
|
184 |
- # AttributeError: 'Requirement' object has no attribute 'project_name' |
|
185 |
- # See: https://bugs.launchpad.net/ubuntu/xenial/+source/python-pip/+bug/1626258 |
|
186 |
- # Upgrading pip works around the issue. |
|
187 |
- run_command(args, ['pip', 'install', '--upgrade', 'pip']) |
|
188 |
- run_command(args, cmd) |
|
207 |
+ try: |
|
208 |
+ run_command(args, cmd) |
|
209 |
+ except SubprocessError as ex: |
|
210 |
+ if ex.status != 2: |
|
211 |
+ raise |
|
212 |
+ |
|
213 |
+ # If pip is too old it won't understand the arguments we passed in, so we'll need to upgrade it. |
|
214 |
+ |
|
215 |
+ # Installing "coverage" on ubuntu 16.04 fails with the error: |
|
216 |
+ # AttributeError: 'Requirement' object has no attribute 'project_name' |
|
217 |
+ # See: https://bugs.launchpad.net/ubuntu/xenial/+source/python-pip/+bug/1626258 |
|
218 |
+ # Upgrading pip works around the issue. |
|
219 |
+ run_command(args, ['pip', 'install', '--upgrade', 'pip']) |
|
220 |
+ run_command(args, cmd) |
|
221 |
+ |
|
222 |
+ after_list = pip_list(args) if detect_pip_changes else None |
|
223 |
+ |
|
224 |
+ if before_list != after_list: |
|
225 |
+ changes.append(cmd) |
|
226 |
+ |
|
227 |
+ return changes |
|
228 |
+ |
|
229 |
+ |
|
230 |
+def pip_list(args): |
|
231 |
+ """ |
|
232 |
+ :type args: EnvironmentConfig |
|
233 |
+ :rtype: str |
|
234 |
+ """ |
|
235 |
+ stdout, _ = run_command(args, ['pip', 'list'], capture=True, always=True) |
|
236 |
+ return stdout |
|
189 | 237 |
|
190 | 238 |
|
191 | 239 |
def generate_egg_info(args): |
192 | 240 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,16 @@ |
0 |
+packaging |
|
1 |
+requests[security] |
|
2 |
+azure-mgmt-compute>=2.0.0,<3 |
|
3 |
+azure-mgmt-network>=1.3.0,<2 |
|
4 |
+azure-mgmt-storage>=1.2.0,<2 |
|
5 |
+azure-mgmt-resource>=1.1.0,<2 |
|
6 |
+azure-storage>=0.35.1,<0.36 |
|
7 |
+azure-cli-core>=2.0.12,<3 |
|
8 |
+msrest!=0.4.15 |
|
9 |
+msrestazure>=0.4.11,<0.5 |
|
10 |
+azure-mgmt-dns>=1.0.1,<2 |
|
11 |
+azure-mgmt-keyvault>=0.40.0,<0.41 |
|
12 |
+azure-mgmt-batch>=4.1.0,<5 |
|
13 |
+azure-mgmt-sql>=0.7.1,<0.8 |
|
14 |
+azure-mgmt-web>=0.32.0,<0.33 |
|
15 |
+azure-mgmt-containerservice>=1.0.0 |
|
0 | 16 |
\ No newline at end of file |
... | ... |
@@ -255,6 +255,8 @@ def parse_args(): |
255 | 255 |
targets=walk_network_integration_targets, |
256 | 256 |
config=NetworkIntegrationConfig) |
257 | 257 |
|
258 |
+ add_extra_docker_options(network_integration, integration=False) |
|
259 |
+ |
|
258 | 260 |
network_integration.add_argument('--platform', |
259 | 261 |
metavar='PLATFORM', |
260 | 262 |
action='append', |
... | ... |
@@ -272,6 +274,8 @@ def parse_args(): |
272 | 272 |
targets=walk_windows_integration_targets, |
273 | 273 |
config=WindowsIntegrationConfig) |
274 | 274 |
|
275 |
+ add_extra_docker_options(windows_integration, integration=False) |
|
276 |
+ |
|
275 | 277 |
windows_integration.add_argument('--windows', |
276 | 278 |
metavar='VERSION', |
277 | 279 |
action='append', |
278 | 280 |
new file mode 100755 |
... | ... |
@@ -0,0 +1,12 @@ |
0 |
+#!/usr/bin/env python |
|
1 |
+"""Make sure the Azure requirements files match.""" |
|
2 |
+ |
|
3 |
+import filecmp |
|
4 |
+ |
|
5 |
+src = 'packaging/requirements/requirements-azure.txt' |
|
6 |
+dst = 'test/runner/requirements/integration.cloud.azure.txt' |
|
7 |
+ |
|
8 |
+if not filecmp.cmp(src, dst): |
|
9 |
+ print('Update the Azure integration test requirements with the packaging test requirements:') |
|
10 |
+ print('cp %s %s' % (src, dst)) |
|
11 |
+ exit(1) |