Merge pull request #2358 from coreos-inc/better-logging
Log more information to the action logs and display the namespaces for superusers
This commit is contained in:
commit
2a7d1fbe57
10 changed files with 88 additions and 40 deletions
|
@ -62,13 +62,22 @@ def get_aggregated_logs(start_time, end_time, performer=None, repository=None, n
|
|||
def get_logs_query(start_time, end_time, performer=None, repository=None, namespace=None,
|
||||
ignore=None):
|
||||
Performer = User.alias()
|
||||
Account = User.alias()
|
||||
selections = [LogEntry, Performer]
|
||||
|
||||
if namespace is None and repository is None:
|
||||
selections.append(Account)
|
||||
|
||||
query = _logs_query(selections, start_time, end_time, performer, repository, namespace, ignore)
|
||||
query = (query.switch(LogEntry)
|
||||
.join(Performer, JOIN_LEFT_OUTER,
|
||||
on=(LogEntry.performer == Performer.id).alias('performer')))
|
||||
|
||||
if namespace is None and repository is None:
|
||||
query = (query.switch(LogEntry)
|
||||
.join(Account, JOIN_LEFT_OUTER,
|
||||
on=(LogEntry.account == Account.id).alias('account')))
|
||||
|
||||
return query
|
||||
|
||||
|
||||
|
|
|
@ -20,7 +20,7 @@ LOGS_PER_PAGE = 20
|
|||
SERVICE_LEVEL_LOG_KINDS = set(['service_key_create', 'service_key_approve', 'service_key_delete',
|
||||
'service_key_modify', 'service_key_extend', 'service_key_rotate'])
|
||||
|
||||
def log_view(log, kinds):
|
||||
def log_view(log, kinds, include_namespace):
|
||||
view = {
|
||||
'kind': kinds[log.kind_id],
|
||||
'metadata': json.loads(log.metadata_json),
|
||||
|
@ -33,9 +33,24 @@ def log_view(log, kinds):
|
|||
'kind': 'user',
|
||||
'name': log.performer.username,
|
||||
'is_robot': log.performer.robot,
|
||||
'avatar': avatar.get_data_for_user(log.performer)
|
||||
'avatar': avatar.get_data_for_user(log.performer),
|
||||
}
|
||||
|
||||
if include_namespace:
|
||||
if log.account and log.account.username:
|
||||
if log.account.organization:
|
||||
view['namespace'] = {
|
||||
'kind': 'org',
|
||||
'name': log.account.username,
|
||||
'avatar': avatar.get_data_for_org(log.account),
|
||||
}
|
||||
else:
|
||||
view['namespace'] = {
|
||||
'kind': 'user',
|
||||
'name': log.account.username,
|
||||
'avatar': avatar.get_data_for_user(log.account),
|
||||
}
|
||||
|
||||
return view
|
||||
|
||||
def aggregated_log_view(log, kinds, start_time):
|
||||
|
@ -92,10 +107,11 @@ def get_logs(start_time, end_time, performer_name=None, repository=None, namespa
|
|||
logs, next_page_token = model.modelutil.paginate(logs_query, database.LogEntry, descending=True,
|
||||
page_token=page_token, limit=LOGS_PER_PAGE)
|
||||
|
||||
include_namespace = namespace is None and repository is None
|
||||
return {
|
||||
'start_time': format_date(start_time),
|
||||
'end_time': format_date(end_time),
|
||||
'logs': [log_view(log, kinds) for log in logs],
|
||||
'logs': [log_view(log, kinds, include_namespace) for log in logs],
|
||||
}, next_page_token
|
||||
|
||||
|
||||
|
|
|
@ -98,6 +98,8 @@ class RepositoryManifestLabels(RepositoryParamResource):
|
|||
'value': label_data['value'],
|
||||
'manifest_digest': manifestref,
|
||||
'media_type': label_data['media_type'],
|
||||
'namespace': namespace,
|
||||
'repo': repository,
|
||||
}
|
||||
|
||||
log_action('manifest_label_add', namespace, metadata, repo=tag_manifest.tag.repository)
|
||||
|
@ -150,7 +152,9 @@ class ManageRepositoryManifestLabel(RepositoryParamResource):
|
|||
'id': labelid,
|
||||
'key': deleted.key,
|
||||
'value': deleted.value,
|
||||
'manifest_digest': manifestref
|
||||
'manifest_digest': manifestref,
|
||||
'namespace': namespace,
|
||||
'repo': repository,
|
||||
}
|
||||
|
||||
log_action('manifest_label_delete', namespace, metadata, repo=tag_manifest.tag.repository)
|
||||
|
|
|
@ -194,6 +194,7 @@ class RepositoryUserPermission(RepositoryParamResource):
|
|||
|
||||
log_action('change_repo_permission', namespace,
|
||||
{'username': username, 'repo': repository,
|
||||
'namespace': namespace,
|
||||
'role': new_permission['role']},
|
||||
repo=model.repository.get_repository(namespace, repository))
|
||||
|
||||
|
@ -209,7 +210,7 @@ class RepositoryUserPermission(RepositoryParamResource):
|
|||
raise request_error(exception=ex)
|
||||
|
||||
log_action('delete_repo_permission', namespace,
|
||||
{'username': username, 'repo': repository},
|
||||
{'username': username, 'repo': repository, 'namespace': namespace},
|
||||
repo=model.repository.get_repository(namespace, repository))
|
||||
|
||||
return '', 204
|
||||
|
|
|
@ -340,7 +340,7 @@ class Repository(RepositoryParamResource):
|
|||
repo.save()
|
||||
|
||||
log_action('set_repo_description', namespace,
|
||||
{'repo': repository, 'description': values['description']},
|
||||
{'repo': repository, 'namespace': namespace, 'description': values['description']},
|
||||
repo=repo)
|
||||
return {
|
||||
'success': True
|
||||
|
@ -404,6 +404,6 @@ class RepositoryVisibility(RepositoryParamResource):
|
|||
|
||||
model.repository.set_repository_visibility(repo, visibility)
|
||||
log_action('change_repo_visibility', namespace,
|
||||
{'repo': repository, 'visibility': values['visibility']},
|
||||
{'repo': repository, 'namespace': namespace, 'visibility': values['visibility']},
|
||||
repo=repo)
|
||||
return {'success': True}
|
||||
|
|
|
@ -102,7 +102,8 @@ class RepositoryNotificationList(RepositoryParamResource):
|
|||
|
||||
resp = notification_view(new_notification)
|
||||
log_action('add_repo_notification', namespace,
|
||||
{'repo': repository, 'notification_id': new_notification.uuid,
|
||||
{'repo': repository, 'namespace': namespace,
|
||||
'notification_id': new_notification.uuid,
|
||||
'event': parsed['event'], 'method': parsed['method']},
|
||||
repo=repo)
|
||||
return resp, 201
|
||||
|
@ -143,7 +144,7 @@ class RepositoryNotification(RepositoryParamResource):
|
|||
""" Deletes the specified notification. """
|
||||
deleted = model.notification.delete_repo_notification(namespace, repository, uuid)
|
||||
log_action('delete_repo_notification', namespace,
|
||||
{'repo': repository, 'notification_id': uuid,
|
||||
{'repo': repository, 'namespace': namespace, 'notification_id': uuid,
|
||||
'event': deleted.event.name, 'method': deleted.method.name},
|
||||
repo=model.repository.get_repository(namespace, repository))
|
||||
|
||||
|
|
|
@ -106,7 +106,8 @@ class RepositoryTag(RepositoryParamResource):
|
|||
username = get_authenticated_user().username
|
||||
log_action('move_tag' if original_image_id else 'create_tag', namespace,
|
||||
{'username': username, 'repo': repository, 'tag': tag,
|
||||
'image': image_id, 'original_image': original_image_id},
|
||||
'namespace': namespace, 'image': image_id,
|
||||
'original_image': original_image_id},
|
||||
repo=model.repository.get_repository(namespace, repository))
|
||||
|
||||
return 'Updated', 201
|
||||
|
@ -119,7 +120,7 @@ class RepositoryTag(RepositoryParamResource):
|
|||
|
||||
username = get_authenticated_user().username
|
||||
log_action('delete_tag', namespace,
|
||||
{'username': username, 'repo': repository, 'tag': tag},
|
||||
{'username': username, 'repo': repository, 'namespace': namespace, 'tag': tag},
|
||||
repo=model.repository.get_repository(namespace, repository))
|
||||
|
||||
return '', 204
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
<span class="avatar" size="avatarSize || 16" data="entity.avatar"></span>
|
||||
<span class="entity-name anchor" href="/organization/{{ entity.name }}"
|
||||
is-only-text="!getIsAdmin(entity.name)">
|
||||
{{ entity.name }}
|
||||
</span>
|
||||
</span>
|
||||
|
||||
|
|
|
@ -44,14 +44,20 @@
|
|||
infinite-scroll-distance="2">
|
||||
<table class="cor-table">
|
||||
<thead>
|
||||
<td ng-if="allLogs == 'true'">Namespace</td>
|
||||
<td>Description</td>
|
||||
<td style="min-width: 226px">Date/Time</td>
|
||||
<td>User/Token/App</td>
|
||||
<td>Performing User/Token/App</td>
|
||||
<td>IP Address</td>
|
||||
</thead>
|
||||
|
||||
<tr class="log" ng-repeat="log in (logs | visibleLogFilter:kindsAllowed | filter:search)"
|
||||
bindonce>
|
||||
<td ng-if="allLogs == 'true'">
|
||||
<span ng-if="log.namespace">
|
||||
<span class="entity-reference" entity="log.namespace" namespace="log.namespace.name"></span>
|
||||
</span>
|
||||
</td>
|
||||
<td>
|
||||
<span class="circle" style="{{ 'background: ' + getColor(log.kind, chart) }}"></span>
|
||||
<span class="log-description" bo-html="getDescription(log)"></span>
|
||||
|
|
|
@ -55,20 +55,20 @@ angular.module('quay').directive('logsView', function () {
|
|||
'account_change_cc': 'Update credit card',
|
||||
'account_change_password': 'Change password',
|
||||
'account_convert': 'Convert account to organization',
|
||||
'create_robot': 'Create Robot Account: {robot}',
|
||||
'delete_robot': 'Delete Robot Account: {robot}',
|
||||
'create_repo': 'Create Repository: {repo}',
|
||||
'create_robot': 'Create Robot Account {robot}',
|
||||
'delete_robot': 'Delete Robot Account {robot}',
|
||||
'create_repo': 'Create Repository {namespace}/{repo}',
|
||||
'push_repo': function(metadata) {
|
||||
if (metadata.tag) {
|
||||
return 'Push of {tag}';
|
||||
return 'Push of {tag} to repository {namespace}/{repo}';
|
||||
} else {
|
||||
return 'Repository push';
|
||||
return 'Repository push to {namespace}/{repo}';
|
||||
}
|
||||
},
|
||||
'repo_verb': function(metadata) {
|
||||
var prefix = '';
|
||||
if (metadata.verb == 'squash') {
|
||||
prefix = 'Pull of squashed tag {tag}'
|
||||
prefix = 'Pull of squashed tag {tag} from {namespace}/{repo}'
|
||||
}
|
||||
|
||||
if (metadata.token) {
|
||||
|
@ -86,11 +86,11 @@ angular.module('quay').directive('logsView', function () {
|
|||
return prefix;
|
||||
},
|
||||
'pull_repo': function(metadata) {
|
||||
var description = 'repository';
|
||||
var description = 'repository {namespace}/{repo}';
|
||||
if (metadata.tag) {
|
||||
description = 'tag {tag}';
|
||||
description = 'tag {tag} from repository {namespace}/{repo}';
|
||||
} else if (metadata.manifest_digest) {
|
||||
description = 'digest {manifest_digest}';
|
||||
description = 'digest {manifest_digest} from repository {namespace}/{repo}';
|
||||
}
|
||||
|
||||
if (metadata.token) {
|
||||
|
@ -109,40 +109,40 @@ angular.module('quay').directive('logsView', function () {
|
|||
'delete_repo': 'Delete repository: {repo}',
|
||||
'change_repo_permission': function(metadata) {
|
||||
if (metadata.username) {
|
||||
return 'Change permission for user {username} in repository {repo} to {role}';
|
||||
return 'Change permission for user {username} in repository {namespace}/{repo} to {role}';
|
||||
} else if (metadata.team) {
|
||||
return 'Change permission for team {team} in repository {repo} to {role}';
|
||||
return 'Change permission for team {team} in repository {namespace}/{repo} to {role}';
|
||||
} else if (metadata.token) {
|
||||
return 'Change permission for token {token} in repository {repo} to {role}';
|
||||
return 'Change permission for token {token} in repository {namespace}/{repo} to {role}';
|
||||
}
|
||||
},
|
||||
'delete_repo_permission': function(metadata) {
|
||||
if (metadata.username) {
|
||||
return 'Remove permission for user {username} from repository {repo}';
|
||||
return 'Remove permission for user {username} from repository {namespace}/{repo}';
|
||||
} else if (metadata.team) {
|
||||
return 'Remove permission for team {team} from repository {repo}';
|
||||
return 'Remove permission for team {team} from repository {namespace}/{repo}';
|
||||
} else if (metadata.token) {
|
||||
return 'Remove permission for token {token} from repository {repo}';
|
||||
return 'Remove permission for token {token} from repository {namespace}/{repo}';
|
||||
}
|
||||
},
|
||||
'revert_tag': 'Tag {tag} reverted to image {image} from image {original_image}',
|
||||
'delete_tag': 'Tag {tag} deleted in repository {repo} by user {username}',
|
||||
'create_tag': 'Tag {tag} created in repository {repo} on image {image} by user {username}',
|
||||
'move_tag': 'Tag {tag} moved from image {original_image} to image {image} in repository {repo} by user {username}',
|
||||
'change_repo_visibility': 'Change visibility for repository {repo} to {visibility}',
|
||||
'delete_tag': 'Tag {tag} deleted in repository {namespace}/{repo} by user {username}',
|
||||
'create_tag': 'Tag {tag} created in repository {namespace}/{repo} on image {image} by user {username}',
|
||||
'move_tag': 'Tag {tag} moved from image {original_image} to image {image} in repository {namespace}/{repo} by user {username}',
|
||||
'change_repo_visibility': 'Change visibility for repository {namespace}/{repo} to {visibility}',
|
||||
'add_repo_accesstoken': 'Create access token {token} in repository {repo}',
|
||||
'delete_repo_accesstoken': 'Delete access token {token} in repository {repo}',
|
||||
'set_repo_description': 'Change description for repository {repo}',
|
||||
'set_repo_description': 'Change description for repository {namespace}/{repo}',
|
||||
'build_dockerfile': function(metadata) {
|
||||
if (metadata.trigger_id) {
|
||||
var triggerDescription = TriggerService.getDescription(
|
||||
metadata['service'], metadata['config']);
|
||||
return 'Build image from Dockerfile for repository {repo} triggered by ' + triggerDescription;
|
||||
return 'Build image from Dockerfile for repository {namespace}/{repo} triggered by ' + triggerDescription;
|
||||
}
|
||||
return 'Build image from Dockerfile for repository {repo}';
|
||||
return 'Build image from Dockerfile for repository {namespace}/{repo}';
|
||||
},
|
||||
'org_create_team': 'Create team: {team}',
|
||||
'org_delete_team': 'Delete team: {team}',
|
||||
'org_create_team': 'Create team {team}',
|
||||
'org_delete_team': 'Delete team {team}',
|
||||
'org_add_team_member': 'Add member {member} to team {team}',
|
||||
'org_remove_team_member': 'Remove member {member} from team {team}',
|
||||
'org_invite_team_member': function(metadata) {
|
||||
|
@ -204,12 +204,12 @@ angular.module('quay').directive('logsView', function () {
|
|||
|
||||
'add_repo_notification': function(metadata) {
|
||||
var eventData = ExternalNotificationData.getEventInfo(metadata.event);
|
||||
return 'Add notification of event "' + eventData['title'] + '" for repository {repo}';
|
||||
return 'Add notification of event "' + eventData['title'] + '" for repository {namespace}/{repo}';
|
||||
},
|
||||
|
||||
'delete_repo_notification': function(metadata) {
|
||||
var eventData = ExternalNotificationData.getEventInfo(metadata.event);
|
||||
return 'Delete notification of event "' + eventData['title'] + '" for repository {repo}';
|
||||
return 'Delete notification of event "' + eventData['title'] + '" for repository {namespace}/{repo}';
|
||||
},
|
||||
|
||||
'regenerate_robot_token': 'Regenerated token for robot {robot}',
|
||||
|
@ -236,8 +236,8 @@ angular.module('quay').directive('logsView', function () {
|
|||
}
|
||||
},
|
||||
|
||||
'manifest_label_add': 'Label {key} added to manifest {manifest_digest}',
|
||||
'manifest_label_delete': 'Label {key} deleted from manifest {manifest_digest}',
|
||||
'manifest_label_add': 'Label {key} added to manifest {manifest_digest} under repository {namespace}/{repo}',
|
||||
'manifest_label_delete': 'Label {key} deleted from manifest {manifest_digest} under repository {namespace}/{repo}',
|
||||
|
||||
// Note: These are deprecated.
|
||||
'add_repo_webhook': 'Add webhook in repository {repo}',
|
||||
|
@ -400,6 +400,15 @@ angular.module('quay').directive('logsView', function () {
|
|||
|
||||
$scope.getDescription = function(log) {
|
||||
log.metadata['_ip'] = log.ip ? log.ip : null;
|
||||
|
||||
// Note: This is for back-compat for logs that previously did not have namespaces.
|
||||
// TODO(jschorr): Remove this after a month or two (~April 2017).
|
||||
var namespace = '';
|
||||
if (log.namespace) {
|
||||
namespace = log.namespace.username || log.namespace.name;
|
||||
}
|
||||
|
||||
log.metadata['namespace'] = log.metadata['namespace'] || namespace || '';
|
||||
return StringBuilderService.buildString(logDescriptions[log.kind] || log.kind, log.metadata);
|
||||
};
|
||||
|
||||
|
|
Reference in a new issue