Put aggregated log query and log exports behind feature flags
This commit is contained in:
parent
4ba4d9141b
commit
204eb74c4f
7 changed files with 60 additions and 20 deletions
|
@ -553,3 +553,9 @@ class DefaultConfig(ImmutableConfig):
|
|||
|
||||
# Feature Flag: Whether to record when users were last accessed.
|
||||
FEATURE_USER_LAST_ACCESSED = True
|
||||
|
||||
# Feature Flag: Whether to allow users to retrieve aggregated log counts.
|
||||
FEATURE_AGGREGATED_LOG_COUNT_RETRIEVAL = True
|
||||
|
||||
# Feature Flag: Whether to support log exporting.
|
||||
FEATURE_LOG_EXPORT = True
|
||||
|
|
|
@ -6,11 +6,13 @@ from datetime import datetime, timedelta
|
|||
|
||||
from flask import request
|
||||
|
||||
import features
|
||||
|
||||
from app import export_action_logs_queue
|
||||
from endpoints.api import (resource, nickname, ApiResource, query_param, parse_args,
|
||||
RepositoryParamResource, require_repo_admin, related_user_resource,
|
||||
format_date, require_user_admin, path_param, require_scope, page_support,
|
||||
validate_json_request, InvalidRequest)
|
||||
validate_json_request, InvalidRequest, show_if)
|
||||
from data import model as data_model
|
||||
from endpoints.api.logs_models_pre_oci import pre_oci_model as model
|
||||
from endpoints.exception import Unauthorized, NotFound
|
||||
|
@ -150,6 +152,7 @@ class OrgLogs(ApiResource):
|
|||
|
||||
|
||||
@resource('/v1/repository/<apirepopath:repository>/aggregatelogs')
|
||||
@show_if(features.AGGREGATED_LOG_COUNT_RETRIEVAL)
|
||||
@path_param('repository', 'The full path of the repository. e.g. namespace/name')
|
||||
class RepositoryAggregateLogs(RepositoryParamResource):
|
||||
""" Resource for fetching aggregated logs for the specific repository. """
|
||||
|
@ -170,6 +173,7 @@ class RepositoryAggregateLogs(RepositoryParamResource):
|
|||
|
||||
|
||||
@resource('/v1/user/aggregatelogs')
|
||||
@show_if(features.AGGREGATED_LOG_COUNT_RETRIEVAL)
|
||||
class UserAggregateLogs(ApiResource):
|
||||
""" Resource for fetching aggregated logs for the current user. """
|
||||
|
||||
|
@ -191,6 +195,7 @@ class UserAggregateLogs(ApiResource):
|
|||
|
||||
|
||||
@resource('/v1/organization/<orgname>/aggregatelogs')
|
||||
@show_if(features.AGGREGATED_LOG_COUNT_RETRIEVAL)
|
||||
@path_param('orgname', 'The name of the organization')
|
||||
@related_user_resource(UserLogs)
|
||||
class OrgAggregateLogs(ApiResource):
|
||||
|
@ -314,6 +319,7 @@ class ExportUserLogs(ApiResource):
|
|||
|
||||
|
||||
@resource('/v1/organization/<orgname>/exportlogs')
|
||||
@show_if(features.LOG_EXPORT)
|
||||
@path_param('orgname', 'The name of the organization')
|
||||
@related_user_resource(ExportUserLogs)
|
||||
class ExportOrgLogs(ApiResource):
|
||||
|
@ -329,7 +335,7 @@ class ExportOrgLogs(ApiResource):
|
|||
@require_scope(scopes.ORG_ADMIN)
|
||||
@validate_json_request('ExportLogs')
|
||||
def post(self, orgname, parsed_args):
|
||||
""" Gets the aggregated logs for the specified organization. """
|
||||
""" Exports the logs for the specified organization. """
|
||||
permission = AdministerOrganizationPermission(orgname)
|
||||
if permission.can():
|
||||
start_time = parsed_args['starttime']
|
||||
|
|
|
@ -22,14 +22,16 @@
|
|||
</span>
|
||||
<span class="hidden-xs right">
|
||||
<i class="fa fa-bar-chart-o toggle-icon" ng-class="chartVisible ? 'active' : ''"
|
||||
ng-click="toggleChart()" data-title="Toggle Chart" bs-tooltip="tooltip.title"></i>
|
||||
ng-click="toggleChart()" data-title="Toggle Chart" bs-tooltip="tooltip.title"
|
||||
quay-show="Features.AGGREGATED_LOG_COUNT_RETRIEVAL"></i>
|
||||
<button class="btn btn-default download-btn" ng-click="showExportLogs()"
|
||||
ng-if="user || organization || repository"><i class="fa fa-download"></i>Export Logs</button>
|
||||
ng-if="(user || organization || repository) && Features.LOG_EXPORT"><i class="fa fa-download"></i>Export Logs</button>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div id="bar-chart" style="width: 800px; height: 500px;" ng-show="chartVisible">
|
||||
<div id="bar-chart" style="width: 800px; height: 500px;"
|
||||
quay-show="chartVisible && Features.AGGREGATED_LOG_COUNT_RETRIEVAL">
|
||||
<svg style="width: 800px; height: 500px;"></svg>
|
||||
<div class="cor-loader" ng-if="chartLoading"></div>
|
||||
</div>
|
||||
|
|
|
@ -19,7 +19,9 @@ angular.module('quay').directive('logsView', function () {
|
|||
'allLogs': '@allLogs'
|
||||
},
|
||||
controller: function($scope, $element, $sce, Restangular, ApiService, TriggerService,
|
||||
StringBuilderService, ExternalNotificationData, UtilService) {
|
||||
StringBuilderService, ExternalNotificationData, UtilService,
|
||||
Features) {
|
||||
$scope.Features = Features;
|
||||
$scope.loading = true;
|
||||
$scope.loadCounter = -1;
|
||||
$scope.logs = null;
|
||||
|
@ -405,20 +407,22 @@ angular.module('quay').directive('logsView', function () {
|
|||
return;
|
||||
}
|
||||
|
||||
$scope.chartLoading = true;
|
||||
if (Features.AGGREGATED_LOG_COUNT_RETRIEVAL) {
|
||||
$scope.chartLoading = true;
|
||||
|
||||
var aggregateUrl = getUrl('aggregatelogs').toString();
|
||||
var loadAggregate = Restangular.one(aggregateUrl);
|
||||
loadAggregate.customGET().then(function(resp) {
|
||||
$scope.chart = new LogUsageChart(logKinds);
|
||||
$($scope.chart).bind('filteringChanged', function(e) {
|
||||
$scope.$apply(function() { $scope.kindsAllowed = e.allowed; });
|
||||
var aggregateUrl = getUrl('aggregatelogs').toString();
|
||||
var loadAggregate = Restangular.one(aggregateUrl);
|
||||
loadAggregate.customGET().then(function(resp) {
|
||||
$scope.chart = new LogUsageChart(logKinds);
|
||||
$($scope.chart).bind('filteringChanged', function(e) {
|
||||
$scope.$apply(function() { $scope.kindsAllowed = e.allowed; });
|
||||
});
|
||||
|
||||
$scope.chart.draw('bar-chart', resp.aggregated, $scope.options.logStartDate,
|
||||
$scope.options.logEndDate);
|
||||
$scope.chartLoading = false;
|
||||
});
|
||||
|
||||
$scope.chart.draw('bar-chart', resp.aggregated, $scope.options.logStartDate,
|
||||
$scope.options.logEndDate);
|
||||
$scope.chartLoading = false;
|
||||
});
|
||||
}
|
||||
|
||||
$scope.nextPageToken = null;
|
||||
$scope.hasAdditional = true;
|
||||
|
|
|
@ -784,6 +784,20 @@ CONFIG_SCHEMA = {
|
|||
'pattern': '^[0-9]+(w|m|d|h|s)$',
|
||||
},
|
||||
|
||||
# Feature Flag: Aggregated log retrieval.
|
||||
'FEATURE_AGGREGATED_LOG_COUNT_RETRIEVAL': {
|
||||
'type': 'boolean',
|
||||
'description': 'Whether to allow retrieval of aggregated log counts. Defaults to True',
|
||||
'x-example': True,
|
||||
},
|
||||
|
||||
# Feature Flag: Log export.
|
||||
'FEATURE_LOG_EXPORT': {
|
||||
'type': 'boolean',
|
||||
'description': 'Whether to allow exporting of action logs. Defaults to True',
|
||||
'x-example': True,
|
||||
},
|
||||
|
||||
# Feature Flag: User last accessed.
|
||||
'FEATURE_USER_LAST_ACCESSED': {
|
||||
'type': 'boolean',
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import logging
|
||||
import os.path
|
||||
import json
|
||||
import time
|
||||
import uuid
|
||||
|
||||
from datetime import datetime, timedelta
|
||||
|
@ -8,6 +9,8 @@ from io import BytesIO
|
|||
|
||||
from enum import Enum, unique
|
||||
|
||||
import features
|
||||
|
||||
from app import app, export_action_logs_queue, storage, get_app_url
|
||||
from data import model
|
||||
from endpoints.api import format_date
|
||||
|
@ -277,6 +280,11 @@ def _run_and_time(fn):
|
|||
if __name__ == "__main__":
|
||||
logging.config.fileConfig(logfile_path(debug=False), disable_existing_loggers=False)
|
||||
|
||||
if not features.LOG_EXPORT:
|
||||
logger.debug('Log export not enabled; skipping')
|
||||
while True:
|
||||
time.sleep(100000)
|
||||
|
||||
logger.debug('Starting export action logs worker')
|
||||
worker = ExportActionLogsWorker(export_action_logs_queue,
|
||||
poll_period_seconds=POLL_PERIOD_SECONDS)
|
||||
|
|
|
@ -129,8 +129,8 @@ def log_dict(log):
|
|||
def main():
|
||||
logging.config.fileConfig(logfile_path(debug=False), disable_existing_loggers=False)
|
||||
|
||||
if not features.ACTION_LOG_ROTATION or None in [SAVE_PATH, SAVE_LOCATION]:
|
||||
logger.debug('Action log rotation worker not enabled; skipping')
|
||||
if not features.LOG_EXPORT:
|
||||
logger.debug('Log export not enabled; skipping')
|
||||
while True:
|
||||
time.sleep(100000)
|
||||
|
||||
|
|
Reference in a new issue