import logging import json from flask import request from app import app, dockerfile_build_queue from data import model from data.database import db from auth.auth_context import get_authenticated_user from endpoints.notificationhelper import spawn_notification logger = logging.getLogger(__name__) def start_build(repository, prepared_build, pull_robot_name=None): host = app.config['SERVER_HOSTNAME'] repo_path = '%s/%s/%s' % (host, repository.namespace_user.username, repository.name) new_token = model.token.create_access_token(repository, 'write', kind='build-worker', friendly_name='Repository Build Token') logger.debug('Creating build %s with repo %s tags %s', prepared_build.build_name, repo_path, prepared_build.tags) job_config = { 'docker_tags': prepared_build.tags, 'registry': host, 'build_subdir': prepared_build.subdirectory, 'trigger_metadata': prepared_build.metadata or {}, 'is_manual': prepared_build.is_manual, 'manual_user': get_authenticated_user().username if get_authenticated_user() else None } with app.config['DB_TRANSACTION_FACTORY'](db): build_request = model.build.create_repository_build(repository, new_token, job_config, prepared_build.dockerfile_id, prepared_build.build_name, prepared_build.trigger, pull_robot_name=pull_robot_name) pull_creds = model.user.get_pull_credentials(pull_robot_name) if pull_robot_name else None json_data = json.dumps({ 'build_uuid': build_request.uuid, 'pull_credentials': pull_creds }) queue_id = dockerfile_build_queue.put([repository.namespace_user.username, repository.name], json_data, retries_remaining=3) build_request.queue_id = queue_id build_request.save() # Add the build to the repo's log and spawn the build_queued notification. event_log_metadata = { 'build_id': build_request.uuid, 'docker_tags': prepared_build.tags, 'repo': repository.name, 'namespace': repository.namespace_user.username, 'is_manual': prepared_build.is_manual, 'manual_user': get_authenticated_user().username if get_authenticated_user() else None } if prepared_build.trigger: event_log_metadata['trigger_id'] = prepared_build.trigger.uuid event_log_metadata['trigger_kind'] = prepared_build.trigger.service.name event_log_metadata['trigger_metadata'] = prepared_build.metadata or {} model.log.log_action('build_dockerfile', repository.namespace_user.username, ip=request.remote_addr, metadata=event_log_metadata, repository=repository) spawn_notification(repository, 'build_queued', event_log_metadata, subpage='build/%s' % build_request.uuid, pathargs=['build', build_request.uuid]) return build_request class PreparedBuild(object): """ Class which holds all the information about a prepared build. The build queuing service will use this result to actually invoke the build. """ def __init__(self, trigger=None): self._dockerfile_id = None self._tags = None self._build_name = None self._subdirectory = None self._metadata = None self._trigger = trigger self._is_manual = None @staticmethod def get_display_name(sha): return sha[0:7] def tags_from_ref(self, ref, default_branch='master'): branch = ref.split('/')[-1] tags = {branch} if branch == default_branch: tags.add('latest') self.tags = tags def name_from_sha(self, sha): self.build_name = PreparedBuild.get_display_name(sha) @property def is_manual(self): if self._is_manual is None: raise Exception('Property is_manual not set') return self._is_manual @is_manual.setter def is_manual(self, value): if self._is_manual is not None: raise Exception('Property is_manual already set') self._is_manual = value @property def trigger(self): return self._trigger @property def dockerfile_id(self): return self._dockerfile_id @dockerfile_id.setter def dockerfile_id(self, value): if self._dockerfile_id: raise Exception('Property dockerfile_id already set') self._dockerfile_id = value @property def tags(self): if not self._tags: raise Exception('Missing property tags') return self._tags @tags.setter def tags(self, value): if self._tags: raise Exception('Property tags already set') self._tags = list(value) @property def build_name(self): if not self._build_name: raise Exception('Missing property build_name') return self._build_name @build_name.setter def build_name(self, value): if self._build_name: raise Exception('Property build_name already set') self._build_name = value @property def subdirectory(self): if self._subdirectory is None: raise Exception('Missing property subdirectory') return self._subdirectory @subdirectory.setter def subdirectory(self, value): if self._subdirectory: raise Exception('Property subdirectory already set') self._subdirectory = value @property def metadata(self): if self._metadata is None: raise Exception('Missing property metadata') return self._metadata @metadata.setter def metadata(self, value): if self._metadata: raise Exception('Property metadata already set') self._metadata = value