diff --git a/README.md b/README.md index 01ca6b799..2f608b81b 100644 --- a/README.md +++ b/README.md @@ -30,6 +30,7 @@ start the workers: ``` STACK=prod python -m workers.diffsworker -D +STACK=prod python -m workers.dockerfilebuild -D ``` bouncing the servers: diff --git a/buildserver/buildserver.py b/buildserver/buildserver.py index 87dc53878..1f5c7f0d8 100644 --- a/buildserver/buildserver.py +++ b/buildserver/buildserver.py @@ -169,9 +169,9 @@ def start_build(): 'id': job_id, 'total_commands': num_steps, 'total_images': None, - 'current_command': 0, - 'current_image': 0, - 'image_completion_percent': 0, + 'current_command': None, + 'current_image': None, + 'image_completion_percent': None, 'status': 'waiting', 'message': None, } diff --git a/data/database.py b/data/database.py index 0c979a2c2..7b5168ff5 100644 --- a/data/database.py +++ b/data/database.py @@ -153,7 +153,7 @@ class RepositoryTag(BaseModel): class RepositoryBuild(BaseModel): repository = ForeignKeyField(Repository) resource_key = CharField() - digitalocean_build_node_id = IntegerField(null=True) + build_node_id = IntegerField(null=True) phase = CharField(default='waiting') status_url = CharField(null=True) diff --git a/data/model.py b/data/model.py index 4320ca31a..7888543a9 100644 --- a/data/model.py +++ b/data/model.py @@ -562,5 +562,12 @@ def get_repository_build(request_dbid): raise InvalidRepositoryBuildException(msg) +def list_repository_builds(namespace_name, repository_name): + joined = RepositoryBuild.select().join(Repository) + fetched = list(joined.where(Repository.name == repository_name, + Repository.namespace == namespace_name)) + return fetched + + def create_repository_build(repo, resource_key): return RepositoryBuild.create(repository=repo, resource_key=resource_key) diff --git a/endpoints/api.py b/endpoints/api.py index 9d30ef141..a8b3c59c9 100644 --- a/endpoints/api.py +++ b/endpoints/api.py @@ -1,6 +1,7 @@ import logging import stripe import re +import requests from flask import request, make_response, jsonify, abort, url_for from flask.ext.login import login_required, current_user, logout_user @@ -180,11 +181,12 @@ user_files = UserRequestFiles(app.config['AWS_ACCESS_KEY'], @app.route('/api/repository/', methods=['POST']) @api_login_required def create_repo_api(): - namespace_name = request.values['namespace'] + owner = current_user.db_user() + + namespace_name = owner.username repository_name = request.values['repository'] visibility = request.values['visibility'] - owner = current_user.db_user() repo = model.create_repository(namespace_name, repository_name, owner, visibility) @@ -349,6 +351,8 @@ def get_repo_api(namespace, repository): tag_dict = {tag.name: tag_view(tag) for tag in tags} can_write = ModifyRepositoryPermission(namespace, repository).can() can_admin = AdministerRepositoryPermission(namespace, repository).can() + builds = model.list_repository_builds(namespace, repository) + return jsonify({ 'namespace': namespace, 'name': repository, @@ -356,13 +360,44 @@ def get_repo_api(namespace, repository): 'tags': tag_dict, 'can_write': can_write, 'can_admin': can_admin, - 'is_public': is_public + 'is_public': is_public, + 'is_building': len(builds) > 0, }) abort(404) # Not fount abort(403) # Permission denied +@app.route('/api/repository//build/', methods=['GET']) +@parse_repository_name +def get_repo_builds(namespace, repository): + permission = AdministerRepositoryPermission(namespace, repository) + if permission.can(): + def build_view(build_obj): + if build_obj.status_url: + # Delegate the status to the build node + node_status = requests.get(build_obj.status_url).json() + node_status['id'] = build_obj.id + return node_status + + # If there was no status url, do the best we can + return { + 'id': build_obj.id, + 'total_commands': None, + 'total_images': None, + 'current_command': None, + 'current_image': None, + 'image_completion_percent': None, + 'status': build_obj.phase, + 'message': None, + } + + builds = model.list_repository_builds(namespace, repository) + return jsonify({ + 'builds': [build_view(build) for build in builds] + }) + + def role_view(repo_perm_obj): return { 'role': repo_perm_obj.role.name diff --git a/workers/dockerfilebuild.py b/workers/dockerfilebuild.py index e153354b1..4fbc48cac 100644 --- a/workers/dockerfilebuild.py +++ b/workers/dockerfilebuild.py @@ -45,7 +45,7 @@ def babysit_builder(request): repository_build = model.get_repository_build(request['build_id']) # check if there is already a DO node for this build job, if so clean it up - old_id = repository_build.digitalocean_build_node_id + old_id = repository_build.build_node_id if old_id old_droplet = digitalocean.Droplet(old_id) old_droplet.destroy() @@ -60,7 +60,7 @@ def babysit_builder(request): size_id=66, # 512MB, backup_active=False) droplet.create(ssh_key_ids=[app.config['DO_SSH_KEY_ID']]) - repository_build.digitalocean_build_node_id = droplet.id + repository_build.build_node_id = droplet.id repository_build.phase = 'starting' repository_build.save() @@ -115,6 +115,8 @@ def babysit_builder(request): # wait for the job to be complete status_url = start_build.headers['Location'] + repository_build.status_url = status_url + repository_build.save() logger.debug('Waiting for job to be complete') status = get_status(status_url) @@ -128,12 +130,15 @@ def babysit_builder(request): repository_build.phase = 'error' else: repository_build.phase = 'completed' - repository_build.save() # clean up the DO node logger.debug('Cleaning up DO node.') droplet.destroy() + repository_build.status_url = None + repository_build.build_node_id = None; + repository_build.save() + return True