Change buildlogsarchiver to use a data interface

This commit is contained in:
Joseph Schorr 2017-07-11 15:33:28 +03:00
parent b7a2a4390b
commit 8ba71f7a45
4 changed files with 76 additions and 12 deletions

View file

@ -1,13 +1,13 @@
import logging import logging
from tempfile import SpooledTemporaryFile
from gzip import GzipFile from gzip import GzipFile
from tempfile import SpooledTemporaryFile
from data import model from app import build_logs, log_archive, app
from data.archivedlogs import JSON_MIMETYPE from data.archivedlogs import JSON_MIMETYPE
from data.database import CloseForLongOperation from data.database import CloseForLongOperation
from app import build_logs, log_archive, app
from util.streamingjsonencoder import StreamingJSONEncoder from util.streamingjsonencoder import StreamingJSONEncoder
from workers.buildlogsarchiver.models_pre_oci import pre_oci_model as model
from workers.worker import Worker from workers.worker import Worker
POLL_PERIOD_SECONDS = 30 POLL_PERIOD_SECONDS = 30
@ -25,7 +25,7 @@ class ArchiveBuildLogsWorker(Worker):
""" Archive a single build, choosing a candidate at random. This process must be idempotent to """ Archive a single build, choosing a candidate at random. This process must be idempotent to
avoid needing two-phase commit. """ avoid needing two-phase commit. """
# Get a random build to archive # Get a random build to archive
to_archive = model.build.get_archivable_build() to_archive = model.get_archivable_build()
if to_archive is None: if to_archive is None:
logger.debug('No more builds to archive') logger.debug('No more builds to archive')
return return
@ -50,7 +50,7 @@ class ArchiveBuildLogsWorker(Worker):
log_archive.store_file(tempfile, JSON_MIMETYPE, content_encoding='gzip', log_archive.store_file(tempfile, JSON_MIMETYPE, content_encoding='gzip',
file_id=to_archive.uuid) file_id=to_archive.uuid)
we_updated = model.build.mark_build_archived(to_archive.uuid) we_updated = model.mark_build_archived(to_archive.uuid)
if we_updated: if we_updated:
build_logs.expire_status(to_archive.uuid) build_logs.expire_status(to_archive.uuid)
build_logs.delete_log_entries(to_archive.uuid) build_logs.delete_log_entries(to_archive.uuid)

View file

@ -0,0 +1,37 @@
from abc import ABCMeta, abstractmethod
from collections import namedtuple
from six import add_metaclass
class Build(namedtuple('Build', ['uuid', 'logs_archived'])):
"""
Build represents a single build in the build system.
"""
@add_metaclass(ABCMeta)
class BuildLogsArchiverWorkerDataInterface(object):
"""
Interface that represents all data store interactions required by the build logs archiver worker.
"""
@abstractmethod
def get_archivable_build(self):
""" Returns a build whose logs are available for archiving. If none, returns None. """
pass
@abstractmethod
def get_build(self, build_uuid):
""" Returns the build with the matching UUID or None if none. """
pass
@abstractmethod
def mark_build_archived(self, build_uuid):
""" Marks the build with the given UUID as having its logs archived. Returns False if
the build was already marked as archived.
"""
pass
@abstractmethod
def create_build_for_testing(self):
""" Creates an unarchived build for testing of archiving. """
pass

View file

@ -0,0 +1,30 @@
from data import model
from workers.buildlogsarchiver.models_interface import Build, BuildLogsArchiverWorkerDataInterface
class PreOCIModel(BuildLogsArchiverWorkerDataInterface):
def get_archivable_build(self):
build = model.build.get_archivable_build()
if build is None:
return None
return Build(build.uuid, build.logs_archived)
def mark_build_archived(self, build_uuid):
return model.build.mark_build_archived(build_uuid)
def create_build_for_testing(self):
repo = model.repository.get_repository('devtable', 'simple')
access_token = model.token.create_access_token(repo, 'admin')
build = model.build.create_repository_build(repo, access_token, {}, None, 'foo')
build.phase = 'error'
build.save()
return Build(build.uuid, build.logs_archived)
def get_build(self, build_uuid):
build = model.build.get_repository_build(build_uuid)
if build is None:
return None
return Build(build.uuid, build.logs_archived)
pre_oci_model = PreOCIModel()

View file

@ -1,22 +1,19 @@
from mock import patch, Mock from mock import patch, Mock
from app import storage from app import storage
from data import model
from workers.buildlogsarchiver.buildlogsarchiver import ArchiveBuildLogsWorker from workers.buildlogsarchiver.buildlogsarchiver import ArchiveBuildLogsWorker
from test.fixtures import * from test.fixtures import *
from workers.buildlogsarchiver.models_pre_oci import pre_oci_model as model
def test_logarchiving(app): def test_logarchiving(app):
worker = ArchiveBuildLogsWorker() worker = ArchiveBuildLogsWorker()
logs_mock = Mock() logs_mock = Mock()
logs_mock.get_log_entries = Mock(return_value=(1, [{'some': 'entry'}])) logs_mock.get_log_entries = Mock(return_value=(1, [{'some': 'entry'}]))
# Add a build that is ready for archiving. # Add a build that is ready for archiving.
repo = model.repository.get_repository('devtable', 'simple') build = model.create_build_for_testing()
access_token = model.token.create_access_token(repo, 'admin')
build = model.build.create_repository_build(repo, access_token, {}, None, 'foo')
build.phase = 'error'
build.save()
with patch('workers.buildlogsarchiver.buildlogsarchiver.build_logs', logs_mock): with patch('workers.buildlogsarchiver.buildlogsarchiver.build_logs', logs_mock):
worker._archive_redis_buildlogs() worker._archive_redis_buildlogs()
@ -27,7 +24,7 @@ def test_logarchiving(app):
logs_mock.delete_log_entries.assert_called_once() logs_mock.delete_log_entries.assert_called_once()
# Ensure the build was marked as archived. # Ensure the build was marked as archived.
assert model.build.get_repository_build(build.uuid).logs_archived assert model.get_build(build.uuid).logs_archived
# Ensure a file was written to storage. # Ensure a file was written to storage.
assert storage.exists(['local_us'], 'logarchive/%s' % build.uuid) assert storage.exists(['local_us'], 'logarchive/%s' % build.uuid)