blob: 7d47f466cfba746890150911aeaf771467012ec1 [file] [log] [blame]
# Copyright (C) 2018-2019 Apple Inc. All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR
# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
from __future__ import unicode_literals
import logging
from django.db import models
from ews.config import ERR_UNEXPECTED, SUCCESS
from ews.models.buildbotinstance import BuildbotInstance
from ews.models.patch import Patch
import ews.common.util as util
_log = logging.getLogger(__name__)
class Build(models.Model):
patch = models.ForeignKey(Patch, on_delete=models.CASCADE, db_constraint=False)
uid = models.TextField(primary_key=True)
builder_id = models.IntegerField()
builder_name = models.TextField()
builder_display_name = models.TextField()
number = models.IntegerField()
result = models.IntegerField(null=True, blank=True)
state_string = models.TextField()
started_at = models.IntegerField(null=True, blank=True)
complete_at = models.IntegerField(null=True, blank=True)
retried = models.BooleanField(default=False)
created = models.DateTimeField(auto_now_add=True)
modified = models.DateTimeField(auto_now=True)
def __str__(self):
return str(self.uid)
@classmethod
def save_build(cls, patch_id, hostname, build_id, builder_id, builder_name, builder_display_name, number, result, state_string, started_at, complete_at=None):
if not Build.is_valid_result(patch_id, build_id, builder_id, number, result, state_string, started_at, complete_at):
return ERR_UNEXPECTED
if state_string is None:
state_string = ''
uid = BuildbotInstance.get_uid(hostname, build_id)
build = Build.get_existing_build(uid)
if build:
# If the build data is already present in database, update it, e.g.: build complete event.
return Build.update_build(build, patch_id, uid, builder_id, builder_name, builder_display_name, number, result, state_string, started_at, complete_at)
if not Patch.is_existing_patch_id(patch_id):
Patch.save_patch(patch_id)
_log.info('Received result for unknown patch. Saved patch {} to database'.format(patch_id))
# Save the new build data, e.g.: build start event.
Build(patch_id, uid, builder_id, builder_name, builder_display_name, number, result, state_string, started_at, complete_at).save()
_log.info('Saved build {} in database for patch_id: {}'.format(uid, patch_id))
return SUCCESS
@classmethod
def update_build(cls, build, patch_id, uid, builder_id, builder_name, builder_display_name, number, result, state_string, started_at, complete_at):
if build.patch_id != patch_id:
_log.error('patch_id {} does not match with patch_id {}. Ignoring new data.'.format(build.patch_id, patch_id))
return ERR_UNEXPECTED
if build.uid != uid:
_log.error('uid {} does not match with uid {}. Ignoring new data.'.format(build.uid, uid))
return ERR_UNEXPECTED
if build.builder_id != builder_id:
_log.error('builder_id {} does not match with builder_id {}. Ignoring new data.'.format(build.builder_id, builder_id))
return ERR_UNEXPECTED
if build.number != number:
_log.error('build number {} does not match with number {}. Ignoring new data.'.format(build.number, number))
return ERR_UNEXPECTED
build.result = result
build.state_string = state_string
build.started_at = started_at
build.complete_at = complete_at
build.save(update_fields=['result', 'state_string', 'started_at', 'complete_at', 'modified'])
_log.debug('Updated build {} in database for patch_id: {}'.format(uid, patch_id))
return SUCCESS
@classmethod
def set_retried(cls, uid, retried=True):
build = Build.get_existing_build(uid)
if not build:
return
build.retried = retried
build.save(update_fields=['retried', 'modified'])
_log.info('Updated build {} in database with retried={}'.format(uid, retried))
@classmethod
def get_existing_build(cls, uid):
try:
return Build.objects.get(uid=uid)
except Build.DoesNotExist:
return None
@classmethod
def is_valid_result(cls, patch_id, build_id, builder_id, number, result, state_string, started_at, complete_at=None):
if not (util.is_valid_id(patch_id) and util.is_valid_id(build_id) and util.is_valid_id(builder_id) and util.is_valid_id(number)):
return False
return True