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:
josephschorr 2017-02-14 16:38:35 -05:00 committed by GitHub
commit 2a7d1fbe57
10 changed files with 88 additions and 40 deletions

View file

@ -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

View file

@ -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

View file

@ -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)

View file

@ -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

View file

@ -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}

View file

@ -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))

View file

@ -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

View file

@ -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>

View file

@ -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>

View file

@ -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);
};