Ansible 2.7 is in security fix only maintenance mode since quite some time and will be end of life soon. It further blocks upgrade of zuul to Python 2.8 due to incompatibilities. Thus drop support. Change-Id: I13802db3314450ad149fdadacd1e2e70dd8468ef Depends-On: https://review.opendev.org/727345
229 lines
9.8 KiB
Python
229 lines
9.8 KiB
Python
# Copyright 2018 Red Hat, Inc.
|
|
#
|
|
# 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 os
|
|
import re
|
|
import textwrap
|
|
|
|
from tests.base import AnsibleZuulTestCase
|
|
|
|
|
|
class FunctionalZuulStreamMixIn:
|
|
tenant_config_file = 'config/remote-zuul-stream/main.yaml'
|
|
# This should be overriden in child classes.
|
|
ansible_version = '2.9'
|
|
wait_timeout = 120
|
|
|
|
def _setUp(self):
|
|
self.log_console_port = 19000 + int(self.ansible_version.split('.')[1])
|
|
self.fake_nodepool.remote_ansible = True
|
|
|
|
ansible_remote = os.environ.get('ZUUL_REMOTE_IPV4')
|
|
self.assertIsNotNone(ansible_remote)
|
|
|
|
def _run_job(self, job_name, create=True):
|
|
# Keep the jobdir around so we can inspect contents if an
|
|
# assert fails. It will be cleaned up anyway as it is contained
|
|
# in a tmp dir which gets cleaned up after the test.
|
|
self.executor_server.keep_jobdir = True
|
|
|
|
# Output extra ansible info so we might see errors.
|
|
self.executor_server.verbose = True
|
|
if create:
|
|
conf = textwrap.dedent(
|
|
"""
|
|
- job:
|
|
name: {job_name}
|
|
run: playbooks/{job_name}.yaml
|
|
ansible-version: {version}
|
|
vars:
|
|
test_console_port: {console_port}
|
|
roles:
|
|
- zuul: org/common-config
|
|
nodeset:
|
|
nodes:
|
|
- name: compute1
|
|
label: whatever
|
|
- name: controller
|
|
label: whatever
|
|
|
|
- project:
|
|
check:
|
|
jobs:
|
|
- {job_name}
|
|
""".format(
|
|
job_name=job_name,
|
|
version=self.ansible_version,
|
|
console_port=self.log_console_port))
|
|
else:
|
|
conf = textwrap.dedent(
|
|
"""
|
|
- project:
|
|
check:
|
|
jobs:
|
|
- {job_name}
|
|
""".format(job_name=job_name))
|
|
file_dict = {'zuul.yaml': conf}
|
|
A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A',
|
|
files=file_dict)
|
|
self.fake_gerrit.addEvent(A.getPatchsetCreatedEvent(1))
|
|
self.waitUntilSettled()
|
|
|
|
job = self.getJobFromHistory(job_name)
|
|
return job
|
|
|
|
def _get_job_output(self, build):
|
|
path = os.path.join(self.jobdir_root, build.uuid,
|
|
'work', 'logs', 'job-output.txt')
|
|
with open(path) as f:
|
|
return f.read()
|
|
|
|
def assertLogLine(self, line, log):
|
|
pattern = (r'^\d\d\d\d-\d\d-\d\d \d\d:\d\d\:\d\d\.\d\d\d\d\d\d \| %s$'
|
|
% line)
|
|
log_re = re.compile(pattern, re.MULTILINE)
|
|
m = log_re.search(log)
|
|
if m is None:
|
|
raise Exception("'%s' not found in log" % (line,))
|
|
|
|
def test_command(self):
|
|
job = self._run_job('command')
|
|
with self.jobLog(job):
|
|
build = self.history[-1]
|
|
self.assertEqual(build.result, 'SUCCESS')
|
|
|
|
text = self._get_job_output(build)
|
|
self.assertLogLine(
|
|
r'RUN START: \[untrusted : review.example.com/org/project/'
|
|
r'playbooks/command.yaml@master\]', text)
|
|
self.assertLogLine(r'PLAY \[all\]', text)
|
|
self.assertLogLine(
|
|
r'Ansible version={}'.format(self.ansible_version), text)
|
|
self.assertLogLine(r'TASK \[Show contents of first file\]', text)
|
|
self.assertLogLine(r'controller \| command test one', text)
|
|
self.assertLogLine(
|
|
r'controller \| ok: Runtime: \d:\d\d:\d\d\.\d\d\d\d\d\d', text)
|
|
self.assertLogLine(r'TASK \[Show contents of second file\]', text)
|
|
self.assertLogLine(r'compute1 \| command test two', text)
|
|
self.assertLogLine(r'controller \| command test two', text)
|
|
self.assertLogLine(r'compute1 \| This is a rescue task', text)
|
|
self.assertLogLine(r'controller \| This is a rescue task', text)
|
|
self.assertLogLine(r'compute1 \| This is an always task', text)
|
|
self.assertLogLine(r'controller \| This is an always task', text)
|
|
self.assertLogLine(r'compute1 \| This is a handler', text)
|
|
self.assertLogLine(r'controller \| This is a handler', text)
|
|
self.assertLogLine(r'controller \| First free task', text)
|
|
self.assertLogLine(r'controller \| Second free task', text)
|
|
self.assertLogLine(r'controller \| This is a shell task after an '
|
|
'included role', text)
|
|
self.assertLogLine(r'compute1 \| This is a shell task after an '
|
|
'included role', text)
|
|
self.assertLogLine(r'controller \| This is a command task after '
|
|
'an included role', text)
|
|
self.assertLogLine(r'compute1 \| This is a command task after an '
|
|
'included role', text)
|
|
self.assertLogLine(r'controller \| This is a shell task with '
|
|
'delegate compute1', text)
|
|
self.assertLogLine(r'controller \| This is a shell task with '
|
|
'delegate controller', text)
|
|
self.assertLogLine(r'compute1 \| item_in_loop1', text)
|
|
self.assertLogLine(r'compute1 \| ok: Item: item_in_loop1 '
|
|
r'Runtime: \d:\d\d:\d\d\.\d\d\d\d\d\d', text)
|
|
self.assertLogLine(r'compute1 \| item_in_loop2', text)
|
|
self.assertLogLine(r'compute1 \| ok: Item: item_in_loop2 '
|
|
r'Runtime: \d:\d\d:\d\d\.\d\d\d\d\d\d', text)
|
|
self.assertLogLine(r'compute1 \| failed_in_loop1', text)
|
|
self.assertLogLine(r'compute1 \| ok: Item: failed_in_loop1 '
|
|
r'Result: 1', text)
|
|
self.assertLogLine(r'compute1 \| failed_in_loop2', text)
|
|
self.assertLogLine(r'compute1 \| ok: Item: failed_in_loop2 '
|
|
r'Result: 1', text)
|
|
self.assertLogLine(r'compute1 \| .*No such file or directory: .*'
|
|
r'\'/remote-shelltask/somewhere/'
|
|
r'that/does/not/exist\'', text)
|
|
self.assertLogLine(r'controller \| .*No such file or directory: .*'
|
|
r'\'/remote-shelltask/somewhere/'
|
|
r'that/does/not/exist\'', text)
|
|
self.assertLogLine(
|
|
r'controller \| ok: Runtime: \d:\d\d:\d\d\.\d\d\d\d\d\d', text)
|
|
self.assertLogLine('PLAY RECAP', text)
|
|
# NOTE(pabelanger): Ansible 2.8 added new stats
|
|
# skipped, rescued, ignored.
|
|
self.assertLogLine(
|
|
r'controller \| ok: \d+ changed: \d+ unreachable: 0 failed: 0 '
|
|
'skipped: 0 rescued: 1 ignored: 0', text)
|
|
self.assertLogLine(
|
|
r'RUN END RESULT_NORMAL: \[untrusted : review.example.com/'
|
|
r'org/project/playbooks/command.yaml@master]', text)
|
|
|
|
# Run a pre-defined job that is defined in a trusted repo to test
|
|
# localhost tasks.
|
|
job = self._run_job('command-localhost', create=False)
|
|
with self.jobLog(job):
|
|
build = self.history[-1]
|
|
self.assertEqual(build.result, 'SUCCESS')
|
|
|
|
text = self._get_job_output(build)
|
|
self.assertLogLine(r'localhost \| .*No such file or directory: .*'
|
|
r'\'/local-shelltask/somewhere/'
|
|
r'that/does/not/exist\'', text)
|
|
|
|
def test_module_exception(self):
|
|
job = self._run_job('module_failure_exception')
|
|
with self.jobLog(job):
|
|
build = self.history[-1]
|
|
self.assertEqual(build.result, 'FAILURE')
|
|
|
|
text = self._get_job_output(build)
|
|
self.assertLogLine(r'TASK \[Module failure\]', text)
|
|
self.assertLogLine(
|
|
r'controller \| MODULE FAILURE:', text)
|
|
self.assertLogLine(
|
|
r'controller \| Exception: This module is broken', text)
|
|
|
|
def test_module_no_result(self):
|
|
job = self._run_job('module_failure_no_result')
|
|
with self.jobLog(job):
|
|
build = self.history[-1]
|
|
self.assertEqual(build.result, 'FAILURE')
|
|
|
|
text = self._get_job_output(build)
|
|
self.assertLogLine(r'TASK \[Module failure\]', text)
|
|
|
|
if self.ansible_version in ('2.5', '2.6'):
|
|
regex = r'controller \| MODULE FAILURE: This module is broken'
|
|
else:
|
|
# Ansible starting with 2.7 emits a different error message
|
|
# if a module exits without an exception or the ansible
|
|
# supplied methods.
|
|
regex = r'controller \| "msg": "New-style module did not ' \
|
|
r'handle its own exit"'
|
|
self.assertLogLine(regex, text)
|
|
|
|
|
|
class TestZuulStream28(AnsibleZuulTestCase, FunctionalZuulStreamMixIn):
|
|
ansible_version = '2.8'
|
|
|
|
def setUp(self):
|
|
super().setUp()
|
|
self._setUp()
|
|
|
|
|
|
class TestZuulStream29(AnsibleZuulTestCase, FunctionalZuulStreamMixIn):
|
|
ansible_version = '2.9'
|
|
|
|
def setUp(self):
|
|
super().setUp()
|
|
self._setUp()
|