def test1_bad_dockerfile(self): """Test the build fails when the Dockerfile is malformed. Steps: - Force a build with a bad Dockefile. - Check that the build fails. - Check that the failing step is the docker build step. """ with Cluster() as cluster: local_repo = cluster.clone() local_repo.push(yaml=SingleCommandYaml('exit 0', worker={ 'type': 'docker', 'path': 'bad-ubuntu-focal-ctxt' }), dirs=[ abspath( join(__file__, pardir, 'contexts', 'bad-ubuntu-focal-ctxt')) ]) cluster.sanity_check() buildset = cluster.api.force(branch=local_repo.branch) self.assertEqual(buildset.result, 'failure') # Check that the failing build step is The good one fstep = buildset.buildrequest.build.first_failing_step self.assertEqual(fstep.name, '[bad-ubuntu-focal-ctxt_a47b] build') cluster.sanity_check()
def test_artifacts_properties(self): """Test that artifacts properties are well set.""" self.local_repo.push(yaml=SingleCommandYaml('exit 0')) buildset = self.cluster.api.force(branch=self.local_repo.branch) self.assertEqual(buildset.result, 'success') build = buildset.buildrequest.build child_buildsets = build.children self.assertEqual(len(child_buildsets), 1) child_build = child_buildsets[0].buildrequest.build self.assertEqual(child_build.result, 'success') short_hash = self.local_repo.cmd('git rev-parse HEAD') short_hash = short_hash[0:10] timestamp = self.local_repo.cmd('git log -1 --format=%cd ' '--date="format-local:%y%m%d%H%M%S"') expected = 'mock:repo_owner:test:aprefix-0.0.0.r{}.{}.pre-merge.' \ '00000001'.format(timestamp.strip(), short_hash.strip()) self.assertEqual(child_build.properties['artifacts_name'][0], expected) self.assertEqual(child_build.properties['artifacts_public_url'][0], 'https://foo.bar.baz/builds/' + expected) self.assertEqual(child_build.properties['artifacts_private_url'][0], 'http://artifacts/builds/' + expected)
def test_force_parametrized_build(self): """Test forced build with parameters. Steps: - Spawn cluster with a parametrized force build scheduler. - Force a build with 2 parameters out of 5. - Check that the parameters are taken into account by reading the step's stdio log. """ conf = {'FORCE_BUILD_PARAM_COUNT': '5'} with Cluster(extra_conf=conf) as cluster: local_repo = cluster.clone() local_repo.push(yaml=SingleCommandYaml( 'echo The %(prop:color)s %(prop:vehicule)s')) buildset = cluster.api.force(branch=local_repo.branch, prop00_name='vehicule', prop00_value='submarine', prop01_name='color', prop01_value='yellow') self.assertEqual(buildset.result, 'success') child_build = buildset.buildrequest.build.children[ 0].buildrequest.build step = child_build.steps[-1] self.assertIn('The yellow submarine', step.rawlog('stdio'))
def test2_simple_failure_in_docker(self): """Test that a command failure fails the whole build. Steps: - Force a build with a docker worker and a failing command. - Check that the build fails. - Check that the failing step is the failing command execution. """ with Cluster() as cluster: local_repo = cluster.clone() local_repo.push(yaml=SingleCommandYaml('exit 1', worker={ 'type': 'docker', 'path': 'ubuntu-focal-ctxt' }), dirs=[ abspath( join(__file__, pardir, 'contexts', 'ubuntu-focal-ctxt')) ]) cluster.sanity_check() buildset = cluster.api.force(branch=local_repo.branch) self.assertEqual(buildset.result, 'failure') child_build = \ buildset.buildrequest.build.children[0].buildrequest.build self.assertEqual(child_build.first_failing_step.name, 'shell') self.assertEqual(child_build.first_failing_step.state_string, "'exit 1' (failure)") cluster.sanity_check()
def test_patcher_branch_match(self): """Test that a branch skip is taken into account.""" PATCHER_DATA = { 'skip_branches': [ 'spam', 'egg', ], } conf = {'PATCHER_FILE_PATH': 'patcher.yml'} with Cluster(extra_conf=conf) as cluster: for master in cluster._masters.values(): master.add_conf_file(yaml_data=PATCHER_DATA, filename=os.path.join( master._base_path, 'patcher.yml')) repo = cluster.clone() repo.push(branch='spam-branch', yaml=SingleCommandYaml('exit 0')) cluster.api.webhook(repo) cluster.api.getw('/buildsets', get_params={ 'limit': 1, 'results': CANCELLED, }) buildset = cluster.api.force(branch=repo.branch) self.assertEqual(buildset.result, 'success') cluster.sanity_check()
def test_docker_in_docker(self): """Test that we can launch a docker command inside a docker worker. Steps: - Substantiate a docker worker containing docker installation. - Launch a `docker ps` command. - Check that it succeeds. """ with Cluster() as cluster: local_repo = cluster.clone() local_repo.push(yaml=SingleCommandYaml( 'docker ps', worker={ 'type': 'docker', 'path': 'ubuntu-focal-with-docker-ctxt' }), dirs=[ abspath( join(__file__, pardir, 'contexts', 'ubuntu-focal-with-docker-ctxt')) ]) cluster.sanity_check() buildset = cluster.api.force(branch=local_repo.branch) self.assertEqual(buildset.result, 'success') child_build = \ buildset.buildrequest.build.children[0].buildrequest.build self.assertEqual(child_build.result, 'success') cluster.sanity_check()
def test_use_different_dockerfile(self): """Test to build Docker image with a different Dockerfile. By default, ``docker build`` use the dockerfile named **/Dockerfile** inside the Docker context. We can use a different Dockerfile (see ``-f`` option of ``docker build`` command). """ with Cluster() as cluster: local_repo = cluster.clone() local_repo.push(yaml=SingleCommandYaml( 'exit 0', worker={ 'type': 'docker', 'path': 'use-different-dockerfile/foo', 'dockerfile': 'use-different-dockerfile/Dockerfile.buz', }), dirs=[ abspath( join(__file__, pardir, 'contexts', 'use-different-dockerfile')) ]) cluster.sanity_check() buildset = cluster.api.force(branch=local_repo.branch) self.assertEqual(buildset.result, 'success') child_build = \ buildset.buildrequest.build.children[0].buildrequest.build self.assertEqual(child_build.result, 'success') cluster.sanity_check()
def testUnsupportedFormatChar(self): self.setupStep(SingleCommandYaml('echo "%(prop:revision)j"')) self.expectOutcome(FAILURE) self.expectLogfile( 'stderr', 'Error in yaml file:\n' ' Unsupported format character "j" line 8: ' '"%(prop:revision)j"') return self.runStep()
def test_cancel_non_tip_build(self): """Check that commits that are not on tip of branch are cancelled. Steps: - commit twice on a branch - send a webhook to notify the first commit - verify that a build is launched and cancelled immediately """ with Cluster() as cluster: repo = cluster.clone() repo.push(branch='spam', yaml=SingleCommandYaml('exit 0')) old_revision = repo.revision repo.push(branch='spam', yaml=SingleCommandYaml('exit 1')) cluster.webhook(repo, old_revision) build = cluster.api.get_finished_build() self.assertEqual(build['results'], CANCELLED)
def push(self, yaml=None, dirs=(), branch=None): """Create a new commit to trigger a test build. Args: yaml (YamlFactory or str): The yaml file to be pushed. dirs (list): Additional folders to be pushed to the git repo root. branch (str): The branch name to push to. """ if branch is None: branch = 'bugfix/heal_the_world_{}'.format(uuid4()) if yaml is None: yaml = SingleCommandYaml() self.branch = branch try: cmd('git checkout %s' % branch, cwd=self._dir) except CalledProcessError: cmd('git checkout -b %s' % branch, cwd=self._dir) try: mkdir(join(self._dir, 'eve')) except OSError as error: if not error.errno == errno.EEXIST: raise if isinstance(yaml, RawYaml): yaml.filedump(join(self._dir, 'eve', 'main.yml')) else: shutil.copyfile(yaml, join(self._dir, 'eve', 'main.yml')) for src in dirs: shutil.copytree(src, join(self._dir, basename(src))) cmd('git add -A', cwd=self._dir) cmd('git commit -m "add yaml file"', cwd=self._dir) cmd('git push -u origin HEAD:%s' % branch, cwd=self._dir) return self
def test_big_yaml_file(self): """Test a docker build with a big yaml file. Steps: - Setup a big yaml file. - Force a build. - Ensure that the build status is on 'success'. """ with Cluster() as cluster: local_repo = cluster.clone() local_repo.push(yaml=SingleCommandYaml('echo %s' % ('a' * 100000))) cluster.sanity_check() buildset = cluster.api.force(branch=local_repo.branch) self.assertEqual(buildset.result, 'success')
def test_docker_invalid_image_name(self): """Test a docker build with a non existent docker image. Steps: - Set a bad docker image. - Force a build. - Ensure that the build status is on 'exception'. """ with Cluster() as cluster: local_repo = cluster.clone() local_repo.push( yaml=SingleCommandYaml('exit 0', worker={ 'type': 'docker', 'image': 'bad-docker-image' })) buildset = cluster.api.force(branch=local_repo.branch) self.assertEqual(buildset.result, 'exception')
def test_git_repo(self): """Test that a fake git repo works correctly. Steps: - Create a fake remote git repo. - Clone it with the LocalGitRepo class. - Push a specified yaml file on a specified branch. - Check that the file is on that branch. - Check the branch has been pushed to the remote. """ remote = tempfile.mkdtemp(prefix='eve_remote_') cmd('git init --bare', cwd=remote) local = LocalGitRepo(remote=remote) command = 'do something' branch = 'feature/1' local.push(SingleCommandYaml(command), branch=branch) self.assertIn(command, cmd('cat eve/main.yml', cwd=local._dir)) self.assertIn(branch, cmd('git branch', cwd=local._dir)) self.assertIn(branch, cmd('git branch', cwd=remote))
def test_worker_environ(self): """Test worker environment. Steps: - Spawn worker. - Check Eve environment variables are not setted in the worker. """ conf = {'FOO': 'bar'} with Cluster(extra_conf=conf) as cluster: local_repo = cluster.clone() local_repo.push(yaml=SingleCommandYaml('test -z "$FOO"')) buildset = cluster.api.force(branch=local_repo.branch) self.assertEqual(buildset.result, 'failure') child_build = \ buildset.buildrequest.build.children[0].buildrequest.build self.assertEqual(child_build.first_failing_step.name, 'shell') self.assertEqual(child_build.first_failing_step.state_string, "'test -z ...' (failure)")
def test_docker_hook_workers_off(self): """Test the property docker_hook is set when required. Steps: - set the DOCKER_HOOK_... environment to invalid data. - Force a build. - Check that the build succeeds. - Check that bootstrap does not have the property. - Check that the docker build has the property set to False. """ conf = { 'DOCKER_HOOK_IN_USE': '1', 'DOCKER_HOOK_VERSION': '1.2.3', 'DOCKER_HOOK_WORKERS': 'ri;not-ubuntu-focal-with-docker-ctxt;pl', } with Cluster(extra_conf=conf) as cluster: local_repo = cluster.clone() local_repo.push(yaml=SingleCommandYaml( 'exit 0', worker={ 'type': 'docker', 'path': 'ubuntu-focal-with-docker-ctxt' }), dirs=[ abspath( join(__file__, pardir, 'contexts', 'ubuntu-focal-with-docker-ctxt')) ]) buildset = cluster.api.force(branch=local_repo.branch) self.assertEqual(buildset.result, 'success') self.assertTrue( 'docker_hook' not in buildset.buildrequest.build.properties) child_build = \ buildset.buildrequest.build.children[0].buildrequest.build self.assertEqual(child_build.result, 'success') self.assertTrue('docker_hook' not in child_build.properties) cluster.sanity_check()
def test_use_secrets(self): """Test a successful usage of vault secrets. Steps: - start a cluster with vault support - store a secret in its vault - check that the value is transmitted to a woker step by comparing its value : expected failure + expected success - stop the cluster """ conf = {'VAULT_IN_USE': '1', 'VAULT_FILE': 'path/to/file'} with Cluster(extra_conf=conf) as cluster: cluster.sanity_check() cluster.vault.write_secret('path/to/file/secret_id', {'value': 'polichinelle'}) self.assertEqual( cluster.vault.read_secret('path/to/file/secret_id'), {'value': 'polichinelle'}) for secret_value, expected_result in (('marionette', 'failure'), ('polichinelle', 'success')): local_repo = cluster.clone() local_repo.push(yaml=SingleCommandYaml( command='test $SECRET_ID = {}'.format(secret_value), env={'SECRET_ID': '%(secret:secret_id)s'})) buildset = cluster.api.force(branch=local_repo.branch) self.assertEqual(buildset.result, expected_result) child_build = buildset.buildrequest.build.children[ 0].buildrequest.build step = child_build.steps[-1] self.assertIn('SECRET_ID=<secret_id>', step.rawlog('stdio')) self.assertNotIn('SECRET_ID=%s' % secret_value, step.rawlog('stdio')) cluster.sanity_check()
def test_bootstrap_and_master_properties(self): """Check the properties on bootstrap build. Steps: - submit a build via webhook - verify that the build runs correctly - check the expected properties are set """ with Cluster() as cluster: repo = cluster.clone() repo.push(branch='spam', yaml=SingleCommandYaml('exit 0')) cluster.webhook(repo, repo.revision) build = cluster.api.get_finished_build() self.assertEqual(build['results'], SUCCESS) properties = cluster.api.get_build_properties(build) def check_prop(name, value=None, source=None): self.assertTrue(name in properties) if value: self.assertEqual(properties[name][0], value) if source: self.assertEqual(properties[name][1], source) check_prop('artifacts_name') check_prop('artifacts_public_url') check_prop('bootstrap', 1, 'set the bootstrap build number') check_prop('branch', 'spam', 'Build') check_prop('buildbot_version', '2.7.0') check_prop('builddir') check_prop('buildername', 'bootstrap', 'Builder') check_prop('buildnumber', 1, 'Build') check_prop('commit_short_revision') check_prop('commit_timestamp') check_prop('conf_path') check_prop('eve_api_version') check_prop('git_host', 'mock', 'Builder') check_prop('git_owner', 'repo_owner', 'Builder') check_prop('git_slug', 'test', 'Builder') check_prop('got_revision', repo.revision, 'Git') check_prop('master_builddir') check_prop('max_step_duration', 14400, 'Builder') check_prop('product_version', '0.0.0') check_prop('project', 'TEST', 'Build') check_prop('reason', 'branch updated', 'Scheduler') check_prop('repository', 'http://www.example.com/', 'Build') check_prop('revision', repo.revision, 'Build') check_prop('scheduler', 'bootstrap-scheduler', 'Scheduler') check_prop('stage_name', 'bootstrap', 'Builder') check_prop('start_time') check_prop('workername') master_build = cluster.api.get_finished_build('pre-merge') properties = cluster.api.get_build_properties(master_build) check_prop('artifacts_name') check_prop('artifacts_public_url') check_prop('bootstrap', 1, 'set the bootstrap build number') check_prop('bootstrap_reason', 'branch updated', 'BuildOrder') check_prop('branch', 'spam', 'Build') check_prop('buildbot_version', '2.7.0') check_prop('builddir') check_prop('buildername', 'local-test_suffix', 'Builder') check_prop('buildnumber', 1, 'Build') check_prop('commit_short_revision') check_prop('commit_timestamp') check_prop('conf_path') check_prop('eve_api_version') check_prop('git_host', 'mock', 'Builder') check_prop('git_owner', 'repo_owner', 'Builder') check_prop('git_slug', 'test', 'Builder') check_prop('got_revision', repo.revision, 'Git') check_prop('master_builddir') check_prop('max_step_duration', 14400, 'Builder') check_prop('product_version', '0.0.0') check_prop('project', 'TEST', 'Build') check_prop('reason', 'pre-merge (triggered by bootstrap)') check_prop('repository', 'http://www.example.com/', 'Build') check_prop('revision', repo.revision, 'Build') check_prop('scheduler', 'local-test_suffix', 'Scheduler') check_prop('stage_name', 'pre-merge', 'BuildOrder') check_prop('start_time') check_prop('workername')
def test_simple_build(self): """Test a simple build with registry support. Steps: - Check that the tag doesn't belong to the registry. - Build the project. - Check all steps for correctness, including image built and uploaded to the registry. - Check that the tagged image was added to the registry. - Build the project again. - Check all steps again, make sure image was not built, and reused straight from the local images. - Remove all local images. - Build the project again. - Check the image is pulled from the registry. """ local_repo = self.cluster.clone() local_repo.push(yaml=SingleCommandYaml('exit 0', worker={ 'type': 'docker', 'path': 'ubuntu-focal-ctxt' }), dirs=[ abspath( join(__file__, pardir, 'contexts', 'ubuntu-focal-ctxt')) ]) self.cluster.api.force(branch=local_repo.branch) # Check bootstrap bootstrap = self.cluster.api.get_builder('bootstrap') bootstrap_build = self.cluster.api.get_finished_build('bootstrap', timeout=180) self.assertEqual(bootstrap_build['results'], SUCCESS) bootstrap_steps = self.cluster.api.get_build_steps(bootstrap_build) step_names_and_descriptions = [(step['name'], step['state_string']) for step in bootstrap_steps] self.assertEqual( step_names_and_descriptions, [ (u'worker_preparation', u'worker ready'), (u'set the bootstrap build number', u'Set'), (u'check index.lock', u"'test ! ...'"), (u'checkout git branch', u'update'), (u'cancel builds for commits that are not branch tips', u'CancelNonTipBuild'), # noqa (u'set the master_builddir property', u'Set'), (u'get the product version', u"property 'product_version' set"), # noqa (u'check the product version', u"'echo 0.0.0 ...'"), (u'read eve/main.yml', u'uploading main.yml'), (u'get the commit short_revision', u"property 'commit_short_revision' set"), # noqa (u'get the commit timestamp', u"property 'commit_timestamp' set"), (u'set the artifacts name', u"property 'artifacts_name' set"), (u'set the artifacts public url', u"property 'artifacts_public_url' set"), # noqa (u'get the API version', u'Set'), (u'prepare 1 stage(s)', u'finished'), (u'[ubuntu-focal-ctxt_e36a] fingerprint', u"Ran"), # noqa (u'[ubuntu-focal-ctxt_e36a] look up', u"failed (1)"), # noqa (u'[ubuntu-focal-ctxt_e36a] pull', u"failed (1)"), # noqa (u'[ubuntu-focal-ctxt_e36a] build', u'Ran'), (u'[ubuntu-focal-ctxt_e36a] build retry', u'Ran (skipped)'), (u'[ubuntu-focal-ctxt_e36a] push', u'Ran'), (u'trigger', u'triggered pre-merge') ]) # Check build build = self.cluster.api.get_finished_build('pre-merge') self.assertEqual(build['results'], SUCCESS) steps = self.cluster.api.get_build_steps(build) step_names_and_descriptions = [(step['name'], step['state_string']) for step in steps] self.assertEqual(step_names_and_descriptions, [(u'worker_preparation', u'worker ready'), (u'prevent unuseful restarts', u"'[ $(expr ...'"), (u'set the artifacts private url', u"property 'artifacts_private_url' set"), (u'Check worker OS distribution', u'finished'), (u'Set the current builder id', u'finished'), (u'Set the current build url', u'finished'), (u'extract steps from yaml', u'finished'), (u'shell', u"'exit 0'")]) # Check properties bootstrap_properties = self.cluster.api.getw( '/builds/{}'.format(bootstrap_build['buildid']), get_params={'property': '*'}) pprint(bootstrap_properties) # now do the same build again, and check image is reused buildset = self.cluster.api.force(branch=local_repo.branch) buildrequestid = self.cluster.api.getw( '/buildrequests', {'buildsetid': buildset.bsid})['buildrequestid'] bootstrap_build = self.cluster.api.getw( '/builds', { 'buildrequestid': buildrequestid, 'builderid': bootstrap['builderid'], 'results': SUCCESS }) bootstrap_steps = self.cluster.api.get_build_steps(bootstrap_build) step_names_and_descriptions = [(step['name'], step['state_string']) for step in bootstrap_steps] self.assertEqual( step_names_and_descriptions, [ (u'worker_preparation', u'worker ready'), (u'set the bootstrap build number', u'Set'), (u'check index.lock', u"'test ! ...'"), (u'checkout git branch', u'update'), (u'cancel builds for commits that are not branch tips', u'CancelNonTipBuild'), # noqa (u'set the master_builddir property', u'Set'), (u'get the product version', u"property 'product_version' set"), (u'check the product version', u"'echo 0.0.0 ...'"), (u'read eve/main.yml', u'uploading main.yml'), (u'get the commit short_revision', u"property 'commit_short_revision' set"), # noqa (u'get the commit timestamp', u"property 'commit_timestamp' set"), (u'set the artifacts name', u"property 'artifacts_name' set"), (u'set the artifacts public url', u"property 'artifacts_public_url' set"), # noqa (u'get the API version', u'Set'), (u'prepare 1 stage(s)', u'finished'), (u'[ubuntu-focal-ctxt_e36a] fingerprint', u"Ran"), # noqa (u'[ubuntu-focal-ctxt_e36a] look up', u"Ran"), # noqa (u'[ubuntu-focal-ctxt_e36a] pull', u"Ran (skipped)"), # noqa (u'[ubuntu-focal-ctxt_e36a] build', u'Ran (skipped)'), (u'[ubuntu-focal-ctxt_e36a] build retry', u'Ran (skipped)'), (u'[ubuntu-focal-ctxt_e36a] push', u'Ran (skipped)'), (u'trigger', u'triggered pre-merge') ]) # do the same build one last time, but erase the local image first build = self.cluster.api.getw('/builds', { 'builderid': build['builderid'], 'results': SUCCESS }, expected_count=2)[1] image = self.cluster.api.getw( '/builds/{}'.format(build['buildid']), get_params={'property': '*'})['properties']['docker_image'][0] cmd('docker rmi -f {}'.format(image)) buildset = self.cluster.api.force(branch=local_repo.branch) buildrequestid = self.cluster.api.getw( '/buildrequests', {'buildsetid': buildset.bsid})['buildrequestid'] bootstrap_build = self.cluster.api.getw( '/builds', { 'buildrequestid': buildrequestid, 'builderid': bootstrap['builderid'], 'results': SUCCESS }) bootstrap_steps = self.cluster.api.get_build_steps(bootstrap_build) step_names_and_descriptions = [(step['name'], step['state_string']) for step in bootstrap_steps] self.assertEqual( step_names_and_descriptions, [ (u'worker_preparation', u'worker ready'), (u'set the bootstrap build number', u'Set'), (u'check index.lock', u"'test ! ...'"), (u'checkout git branch', u'update'), (u'cancel builds for commits that are not branch tips', u'CancelNonTipBuild'), # noqa (u'set the master_builddir property', u'Set'), (u'get the product version', u"property 'product_version' set"), # noqa (u'check the product version', u"'echo 0.0.0 ...'"), (u'read eve/main.yml', u'uploading main.yml'), (u'get the commit short_revision', u"property 'commit_short_revision' set"), # noqa (u'get the commit timestamp', u"property 'commit_timestamp' set"), # noqa (u'set the artifacts name', u"property 'artifacts_name' set"), (u'set the artifacts public url', u"property 'artifacts_public_url' set"), # noqa (u'get the API version', u'Set'), (u'prepare 1 stage(s)', u'finished'), (u'[ubuntu-focal-ctxt_e36a] fingerprint', u"Ran"), # noqa (u'[ubuntu-focal-ctxt_e36a] look up', u"failed (1)"), # noqa (u'[ubuntu-focal-ctxt_e36a] pull', u"Ran"), # noqa (u'[ubuntu-focal-ctxt_e36a] build', u'Ran (skipped)'), (u'[ubuntu-focal-ctxt_e36a] build retry', u'Ran (skipped)'), (u'[ubuntu-focal-ctxt_e36a] push', u'Ran (skipped)'), (u'trigger', u'triggered pre-merge') ])
def testBasic(self): self.setupStep(SingleCommandYaml('exit 0')) self.expectOutcome(SUCCESS) return self.runStep()