def send_notifications(self, context): exit_code = context['exit_code'] template = 'test_success' if exit_code == 0 else 'test_failure' html = render_template('mail/' + template + '.html', **context) html = sanitize_sensitive_data(html) # Save log html log_dir = os.path.join(current_app.config['BADWOLF_LOG_DIR'], self.commit_hash) if not os.path.exists(log_dir): os.makedirs(log_dir) log_file = os.path.join(log_dir, 'build.html') with open(log_file, 'wb') as f: f.write(to_binary(html)) if exit_code == 0: subject = 'Test succeed for repository {}'.format( self.repo_full_name) else: subject = 'Test failed for repository {}'.format( self.repo_full_name) notification = self.spec.notification emails = notification['emails'] if emails: send_mail(emails, subject, html) slack_webhooks = notification['slack_webhooks'] if slack_webhooks: message = render_template('slack_webhook/' + template + '.md', **context) trigger_slack_webhook(slack_webhooks, message)
def send_notifications(self, context): exit_code = context['exit_code'] template = 'test_success' if exit_code == 0 else 'test_failure' html = render_template('mail/' + template + '.html', **context) html = sanitize_sensitive_data(html) # Save log html log_dir = os.path.join(current_app.config['BADWOLF_LOG_DIR'], self.commit_hash, self.context.task_id) os.makedirs(log_dir, exist_ok=True) log_file = os.path.join(log_dir, 'build.html') with open(log_file, 'wb') as f: f.write(to_binary(html)) if exit_code == 137: logger.info('Build cancelled, will not sending notification') return test_status = 'succeed' if exit_code == 0 else 'failed' subject = 'Test {} for repository {}'.format(test_status, self.context.repository) notification = self.spec.notification email = notification.email should_send_mail = email and email.recipients and ( (exit_code == 0 and email.on_success == 'always') or (exit_code != 0 and email.on_failure == 'always')) if should_send_mail: send_mail(email.recipients, subject, html) slack_webhook = notification.slack_webhook if slack_webhook and slack_webhook.webhooks: if exit_code == 0 and slack_webhook.on_success == 'always': trigger_slack_webhook(slack_webhook.webhooks, context) if exit_code != 0 and slack_webhook.on_failure == 'always': trigger_slack_webhook(slack_webhook.webhooks, context)
def shell_script(self): def _trace(command): return 'echo + {}\n{} '.format(shlex.quote(command), command) commands = [] after_success = [_trace(cmd) for cmd in self.after_success] after_failure = [_trace(cmd) for cmd in self.after_failure] for service in self.services: commands.append(_trace('service {} start'.format(service))) for script in self.scripts: commands.append(_trace(script)) command_encoded = shlex.quote( to_text(base64.b64encode(to_binary('\n'.join(commands))))) context = { 'command': command_encoded, 'after_success': ' \n'.join(after_success), 'after_failure': ' \n'.join(after_failure), } script = render_template('script.sh', **context) logger.debug('Build script: \n%s', script) return script
def run_in_container(self, docker_image_name): environment = {} if self.spec.environments: # TODO: Support run in multiple environments environment = self.spec.environments[0] # TODO: Add more test context related env vars script = shlex.quote( to_text(base64.b64encode(to_binary(self.spec.shell_script)))) environment.update({ 'DEBIAN_FRONTEND': 'noninteractive', 'HOME': '/root', 'SHELL': '/bin/sh', 'CI': 'true', 'CI_NAME': 'badwolf', 'BADWOLF_COMMIT': self.commit_hash, 'BADWOLF_BUILD_DIR': self.context.clone_path, 'BADWOLF_REPO_SLUG': self.context.repository, 'BADWOLF_SCRIPT': script, }) environment.setdefault('TERM', 'xterm-256color') branch = self.context.source['branch'] labels = { 'repo': self.context.repository, 'commit': self.commit_hash, 'task_id': self.context.task_id, } if self.context.type == 'tag': environment['BADWOLF_TAG'] = branch['name'] labels['tag'] = branch['name'] else: environment['BADWOLF_BRANCH'] = branch['name'] labels['branch'] = branch['name'] if self.context.pr_id: environment['BADWOLF_PULL_REQUEST'] = str(self.context.pr_id) labels['pull_request'] = str(self.context.pr_id) volumes = { self.context.clone_path: { 'bind': self.context.clone_path, 'mode': 'rw', }, } if self.spec.docker: volumes['/var/run/docker.sock'] = { 'bind': '/var/run/docker.sock', 'mode': 'ro', } environment.setdefault('DOCKER_HOST', 'unix:///var/run/docker.sock') self._populate_more_envvars(environment) logger.debug('Docker container environment: \n %r', environment) container = self.docker.containers.create( docker_image_name, entrypoint=['/bin/sh', '-c'], command=['echo $BADWOLF_SCRIPT | base64 --decode | /bin/sh'], environment=environment, working_dir=self.context.clone_path, volumes=volumes, privileged=self.spec.privileged, stdin_open=False, tty=True, labels=labels, ) container_id = container.id logger.info('Created container %s from image %s', container_id, docker_image_name) output = [] try: container.start() self.update_build_status('INPROGRESS', 'Running tests in Docker container') exit_code = container.wait( timeout=current_app.config['DOCKER_RUN_TIMEOUT']) except (APIError, DockerException, ReadTimeout) as e: exit_code = -1 output.append(str(e) + '\n') logger.exception('Docker error') finally: try: output.append(to_text(container.logs())) container.remove(force=True) except NotFound: pass except APIError as api_e: if 'can not get logs from container which is dead or marked for removal' in str( api_e): output.append('Build cancelled') else: logger.exception('Error removing docker container') except (DockerException, ReadTimeout): logger.exception('Error removing docker container') return exit_code, ''.join(output)
def decrypt(encrypted): fernet = Fernet(to_binary(current_app.config['SECURE_TOKEN_KEY'])) text = fernet.decrypt(to_binary(encrypted)) return to_text(text)
def encrypt(text): fernet = Fernet(to_binary(current_app.config['SECURE_TOKEN_KEY'])) return fernet.encrypt(to_binary(text))