Detect and fix environment tampering in tests.
This commit is contained in:
parent
99d328976d
commit
b8cb3f519b
3 changed files with 109 additions and 4 deletions
|
@ -17,13 +17,24 @@
|
||||||
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
|
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
# ============================================================
|
# ============================================================
|
||||||
|
- name: python 2
|
||||||
|
set_fact:
|
||||||
|
python_suffix: ""
|
||||||
|
when: ansible_python_version | version_compare('3', '<')
|
||||||
|
|
||||||
|
- name: python 3
|
||||||
|
set_fact:
|
||||||
|
python_suffix: "-py3"
|
||||||
|
when: ansible_python_version | version_compare('3', '>=')
|
||||||
|
|
||||||
- include_vars: '{{ item }}'
|
- include_vars: '{{ item }}'
|
||||||
with_first_found:
|
with_first_found:
|
||||||
- files:
|
- files:
|
||||||
- '{{ ansible_distribution }}-{{ ansible_distribution_major_version }}.yml'
|
- '{{ ansible_distribution }}-{{ ansible_distribution_major_version }}{{ python_suffix }}.yml'
|
||||||
- '{{ ansible_os_family }}-{{ ansible_distribution_major_version }}.yml'
|
- '{{ ansible_os_family }}-{{ ansible_distribution_major_version }}{{ python_suffix }}.yml'
|
||||||
- '{{ ansible_distribution }}.yml'
|
- '{{ ansible_distribution }}{{ python_suffix }}.yml'
|
||||||
- '{{ ansible_os_family }}.yml'
|
- '{{ ansible_os_family }}{{ python_suffix }}.yml'
|
||||||
|
- 'default{{ python_suffix }}.yml'
|
||||||
paths: '../vars'
|
paths: '../vars'
|
||||||
|
|
||||||
- name: install mysqldb_test rpm dependencies
|
- name: install mysqldb_test rpm dependencies
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
mysql_service: 'mysql'
|
||||||
|
|
||||||
|
mysql_packages:
|
||||||
|
- mysql-server
|
||||||
|
- python3-mysqldb
|
||||||
|
- bzip2
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
from __future__ import absolute_import, print_function
|
from __future__ import absolute_import, print_function
|
||||||
|
|
||||||
|
import json
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
import tempfile
|
import tempfile
|
||||||
|
@ -48,6 +49,8 @@ from lib.util import (
|
||||||
make_dirs,
|
make_dirs,
|
||||||
is_shippable,
|
is_shippable,
|
||||||
is_binary_file,
|
is_binary_file,
|
||||||
|
find_executable,
|
||||||
|
raw_command,
|
||||||
)
|
)
|
||||||
|
|
||||||
from lib.test import (
|
from lib.test import (
|
||||||
|
@ -518,6 +521,10 @@ def command_integration_filtered(args, targets):
|
||||||
|
|
||||||
cloud_environment = get_cloud_environment(args, target)
|
cloud_environment = get_cloud_environment(args, target)
|
||||||
|
|
||||||
|
original_environment = EnvironmentDescription()
|
||||||
|
|
||||||
|
display.info('>>> Environment Description\n%s' % original_environment, verbosity=3)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
while tries:
|
while tries:
|
||||||
tries -= 1
|
tries -= 1
|
||||||
|
@ -538,11 +545,16 @@ def command_integration_filtered(args, targets):
|
||||||
if cloud_environment:
|
if cloud_environment:
|
||||||
cloud_environment.on_failure(target, tries)
|
cloud_environment.on_failure(target, tries)
|
||||||
|
|
||||||
|
if not original_environment.validate(target.name, throw=False):
|
||||||
|
raise
|
||||||
|
|
||||||
if not tries:
|
if not tries:
|
||||||
raise
|
raise
|
||||||
|
|
||||||
display.warning('Retrying test target "%s" with maximum verbosity.' % target.name)
|
display.warning('Retrying test target "%s" with maximum verbosity.' % target.name)
|
||||||
display.verbosity = args.verbosity = 6
|
display.verbosity = args.verbosity = 6
|
||||||
|
|
||||||
|
original_environment.validate(target.name, throw=True)
|
||||||
except:
|
except:
|
||||||
display.notice('To resume at this test target, use the option: --start-at %s' % target.name)
|
display.notice('To resume at this test target, use the option: --start-at %s' % target.name)
|
||||||
|
|
||||||
|
@ -1135,6 +1147,82 @@ def get_integration_remote_filter(args, targets):
|
||||||
return exclude
|
return exclude
|
||||||
|
|
||||||
|
|
||||||
|
class EnvironmentDescription(object):
|
||||||
|
"""Description of current running environment."""
|
||||||
|
def __init__(self):
|
||||||
|
"""Initialize snapshot of environment configuration."""
|
||||||
|
versions = ['']
|
||||||
|
versions += SUPPORTED_PYTHON_VERSIONS
|
||||||
|
versions += list(set(v.split('.')[0] for v in SUPPORTED_PYTHON_VERSIONS))
|
||||||
|
|
||||||
|
python_paths = dict((v, find_executable('python%s' % v, required=False)) for v in sorted(versions))
|
||||||
|
python_versions = dict((v, self.get_version([python_paths[v], '-V'])) for v in sorted(python_paths) if python_paths[v])
|
||||||
|
|
||||||
|
pip_paths = dict((v, find_executable('pip%s' % v, required=False)) for v in sorted(versions))
|
||||||
|
pip_versions = dict((v, self.get_version([pip_paths[v], '--version'])) for v in sorted(pip_paths) if pip_paths[v])
|
||||||
|
pip_interpreters = dict((v, self.get_shebang(pip_paths[v])) for v in sorted(pip_paths) if pip_paths[v])
|
||||||
|
|
||||||
|
self.data = dict(
|
||||||
|
python_paths=python_paths,
|
||||||
|
python_versions=python_versions,
|
||||||
|
pip_paths=pip_paths,
|
||||||
|
pip_versions=pip_versions,
|
||||||
|
pip_interpreters=pip_interpreters,
|
||||||
|
)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
"""
|
||||||
|
:rtype: str
|
||||||
|
"""
|
||||||
|
return json.dumps(self.data, sort_keys=True, indent=4)
|
||||||
|
|
||||||
|
def validate(self, target_name, throw):
|
||||||
|
"""
|
||||||
|
:type target_name: str
|
||||||
|
:type throw: bool
|
||||||
|
:rtype: bool
|
||||||
|
"""
|
||||||
|
current = EnvironmentDescription()
|
||||||
|
|
||||||
|
original_json = str(self)
|
||||||
|
current_json = str(current)
|
||||||
|
|
||||||
|
if original_json == current_json:
|
||||||
|
return True
|
||||||
|
|
||||||
|
message = ('Test target "%s" has changed the test environment!\n'
|
||||||
|
'If these changes are necessary, they must be reverted before the test finishes.\n'
|
||||||
|
'>>> Original Environment\n'
|
||||||
|
'%s\n'
|
||||||
|
'>>> Current Environment\n'
|
||||||
|
'%s' % (target_name, original_json, current_json))
|
||||||
|
|
||||||
|
if throw:
|
||||||
|
raise ApplicationError(message)
|
||||||
|
|
||||||
|
display.error(message)
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_version(command):
|
||||||
|
"""
|
||||||
|
:type command: list[str]
|
||||||
|
:rtype: str
|
||||||
|
"""
|
||||||
|
stdout, stderr = raw_command(command, capture=True)
|
||||||
|
return (stdout or '').strip() + (stderr or '').strip()
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_shebang(path):
|
||||||
|
"""
|
||||||
|
:type path: str
|
||||||
|
:rtype: str
|
||||||
|
"""
|
||||||
|
with open(path) as script_fd:
|
||||||
|
return script_fd.readline()
|
||||||
|
|
||||||
|
|
||||||
class NoChangesDetected(ApplicationWarning):
|
class NoChangesDetected(ApplicationWarning):
|
||||||
"""Exception when change detection was performed, but no changes were found."""
|
"""Exception when change detection was performed, but no changes were found."""
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
|
Loading…
Reference in a new issue