diff --git a/app.py b/app.py index aa663418c..f1b029b55 100644 --- a/app.py +++ b/app.py @@ -13,6 +13,7 @@ from data.userfiles import Userfiles from util.analytics import Analytics from util.exceptionlog import Sentry from data.billing import Billing +from data.buildlogs import BuildLogs OVERRIDE_CONFIG_FILENAME = 'conf/stack/config.py' @@ -46,3 +47,4 @@ userfiles = Userfiles(app) analytics = Analytics(app) billing = Billing(app) sentry = Sentry(app) +build_logs = BuildLogs(app) diff --git a/config.py b/config.py index d5fc126cb..54650e566 100644 --- a/config.py +++ b/config.py @@ -73,7 +73,7 @@ class DefaultConfig(object): STORAGE_PATH = 'test/data/registry' # Build logs - BUILDLOGS = BuildLogs('logs.quay.io') # Change me + BUILDLOGS_OPTIONS = ['logs.quay.io'] # Real-time user events USER_EVENTS = UserEventBuilder('logs.quay.io') diff --git a/data/buildlogs.py b/data/buildlogs.py index 43723e211..033a6a043 100644 --- a/data/buildlogs.py +++ b/data/buildlogs.py @@ -1,10 +1,12 @@ import redis import json +from util.dynamic import import_class + class BuildStatusRetrievalError(Exception): pass -class BuildLogs(object): +class RedisBuildLogs(object): ERROR = 'error' COMMAND = 'command' PHASE = 'phase' @@ -70,3 +72,30 @@ class BuildLogs(object): raise BuildStatusRetrievalError('Cannot retrieve build status') return json.loads(fetched) if fetched else None + +class BuildLogs(object): + def __init__(self, app=None): + self.app = app + if app is not None: + self.state = self.init_app(app) + else: + self.state = None + + def init_app(self, app): + buildlogs_options = app.config.get('BUILDLOGS_OPTIONS', []) + buildlogs_import = app.config.get('BUILDLOGS_MODULE_AND_CLASS', None) + + if buildlogs_import is None: + klass = RedisBuildLogs + else: + klass = import_class(buildlogs_import[0], buildlogs_import[1]) + + buildlogs = klass(*buildlogs_options) + + # register extension with app + app.extensions = getattr(app, 'extensions', {}) + app.extensions['buildlogs'] = buildlogs + return buildlogs + + def __getattr__(self, name): + return getattr(self.state, name, None) \ No newline at end of file diff --git a/endpoints/api/build.py b/endpoints/api/build.py index c9bd2cf3a..0255f1c60 100644 --- a/endpoints/api/build.py +++ b/endpoints/api/build.py @@ -3,7 +3,7 @@ import json from flask import request -from app import app, userfiles as user_files +from app import app, userfiles as user_files, build_logs from endpoints.api import (RepositoryParamResource, parse_args, query_param, nickname, resource, require_repo_read, require_repo_write, validate_json_request, ApiResource, internal_only, format_date, api, Unauthorized, NotFound) @@ -17,7 +17,6 @@ from util.names import parse_robot_username logger = logging.getLogger(__name__) -build_logs = app.config['BUILDLOGS'] def get_trigger_config(trigger): diff --git a/test/testconfig.py b/test/testconfig.py index d012af469..e03c2328f 100644 --- a/test/testconfig.py +++ b/test/testconfig.py @@ -24,7 +24,8 @@ class TestConfig(DefaultConfig): STORAGE_TYPE = 'FakeStorage' - BUILDLOGS = TestBuildLogs('logs.quay.io', 'devtable', 'building', - 'deadbeef-dead-beef-dead-beefdeadbeef') + BUILDLOGS_MODULE_AND_CLASS = ('test.testlogs', 'testlogs.TestBuildLogs') + BUILDLOGS_OPTIONS = ['logs.quay.io', 'devtable', 'building', + 'deadbeef-dead-beef-dead-beefdeadbeef'] USERFILES_TYPE = 'FakeUserfiles' diff --git a/test/testlogs.py b/test/testlogs.py index b8e399dad..fa3c2bec2 100644 --- a/test/testlogs.py +++ b/test/testlogs.py @@ -5,7 +5,7 @@ from loremipsum import get_sentence from functools import wraps from copy import deepcopy -from data.buildlogs import BuildLogs +from data.buildlogs import RedisBuildLogs logger = logging.getLogger(__name__) @@ -32,7 +32,7 @@ def maybe_advance_script(is_get_status=False): return inner_advance -class TestBuildLogs(BuildLogs): +class TestBuildLogs(RedisBuildLogs): COMMAND_TYPES = ['FROM', 'MAINTAINER', 'RUN', 'CMD', 'EXPOSE', 'ENV', 'ADD', 'ENTRYPOINT', 'VOLUME', 'USER', 'WORKDIR'] STATUS_TEMPLATE = { diff --git a/util/dynamic.py b/util/dynamic.py new file mode 100644 index 000000000..975a4d930 --- /dev/null +++ b/util/dynamic.py @@ -0,0 +1,7 @@ +def import_class(module_name, class_name): + """ Import a class given the specified module name and class name. """ + klass = __import__(module_name) + class_segments = class_name.split('.') + for segment in class_segments: + klass = getattr(klass, segment) + return klass diff --git a/workers/dockerfilebuild.py b/workers/dockerfilebuild.py index b6a098fe5..66ee4a9b1 100644 --- a/workers/dockerfilebuild.py +++ b/workers/dockerfilebuild.py @@ -21,7 +21,7 @@ from collections import defaultdict from data.queue import dockerfile_build_queue from data import model from workers.worker import Worker, WorkerUnhealthyException, JobException -from app import app, userfiles as user_files +from app import app, userfiles as user_files, build_logs from util.safetar import safe_extractall from util.dockerfileparse import parse_dockerfile, ParsedDockerfile, serialize_dockerfile @@ -34,8 +34,6 @@ formatter = logging.Formatter(FORMAT) logger = logging.getLogger(__name__) -build_logs = app.config['BUILDLOGS'] - TIMEOUT_PERIOD_MINUTES = 20 CACHE_EXPIRATION_PERIOD_HOURS = 24 NO_TAGS = [':']