Add UI for viewing and changing the expiration of tags

This commit is contained in:
Joseph Schorr 2017-06-21 21:33:26 -04:00
parent 977539bf08
commit 99d7fde8ee
13 changed files with 329 additions and 26 deletions

View file

@ -57,6 +57,12 @@
<i class="fa fa-times"></i><span class="text">Delete Tags</span>
</a>
</li>
<li ng-if="repository.can_write">
<a ng-click="askChangeTagsExpiration(checkedTags.checked)"
ng-class="repository.tag_operations_disabled ? 'disabled-option' : ''">
<i class="fa fa-clock-o"></i><span class="text">Change Tags Expiration</span>
</a>
</li>
</ul>
</div>
</span>
@ -105,6 +111,11 @@
style="width: 80px;">
<a ng-click="orderBy('size')" data-title="The compressed size of the tag's image" data-container="body" bs-tooltip>Size</a>
</td>
<td class="hidden-sm hidden-xs"
ng-class="tablePredicateClass('expiration_date', options.predicate, options.reverse)"
style="width: 140px;">
<a ng-click="orderBy('expiration_date')" data-title="When the tag expires" data-container="body" bs-tooltip>Expires</a>
</td>
<td class="hidden-xs hidden-sm"
ng-class="tablePredicateClass('image_id', options.predicate, options.reverse)"
style="width: 140px;">
@ -133,12 +144,16 @@
ng-if="repository.trust_enabled">
<tag-signing-display tag="tag" delegations="repoDelegationsInfo" compact="true"></tag-signing-display>
</td>
<!-- Last Modified -->
<td class="hidden-xs">
<span bo-if="tag.last_modified" data-title="{{ tag.last_modified | amDateFormat:'dddd, MMMM Do YYYY, h:mm:ss a' }}" bs-tooltip>
<span am-time-ago="tag.last_modified"></span>
</span>
<span bo-if="!tag.last_modified">Unknown</span>
</td>
<!-- Security scanning -->
<td quay-require="['SECURITY_SCANNER']" class="security-scan-col hidden-xs">
<span class="cor-loader-inline" ng-if="getTagVulnerabilities(tag).loading"></span>
<span class="vuln-load-error" ng-if="getTagVulnerabilities(tag).hasError"
@ -212,7 +227,20 @@
</span>
</span>
</td>
<!-- Size -->
<td class="hidden-sm hidden-xs" bo-text="tag.size | bytes"></td>
<!-- Expiration -->
<td class="hidden-xs hidden-sm">
<a ng-click="askChangeTagsExpiration([tag])"
ng-if="!repository.tag_operations_disabled && repository.can_write">
<expiration-status-view expiration-date="tag.expiration_date"></expiration-status-view>
</a>
<expiration-status-view expiration-date="tag.expiration_date" ng-if="repository.tag_operations_disabled || !repository.can_write"></expiration-status-view>
</td>
<!-- Image link -->
<td class="hidden-xs hidden-sm image-id-col">
<span class="image-link" repository="repository" image-id="tag.image_id" manifest-digest="tag.manifest_digest"></span>
</td>
@ -254,6 +282,10 @@
ng-class="repository.tag_operations_disabled ? 'disabled-option' : ''">
<i class="fa fa-times"></i> Delete Tag
</span>
<span class="cor-option" option-click="askChangeTagsExpiration([tag])"
ng-class="repository.tag_operations_disabled ? 'disabled-option' : ''">
<i class="fa fa-clock-o"></i> Change Expiration
</span>
</span>
</span>
</td>
@ -261,7 +293,7 @@
</tr>
<tr ng-if="expandedView">
<td class="checkbox-col"></td>
<td class="labels-col" colspan="{{5 + (Features.SECURITY_SCANNER ? 1 : 0) + (repository.trust_enabled ? 1 : 0) }}">
<td class="labels-col" colspan="{{6 + (Features.SECURITY_SCANNER ? 1 : 0) + (repository.trust_enabled ? 1 : 0) }}">
<!-- Labels -->
<div class="manifest-label-list" repository="repository"
manifest-digest="tag.manifest_digest" cache="labelCache"></div>

View file

@ -111,6 +111,28 @@
</div>
</div>
<!-- Change Tags Expiration -->
<div class="cor-confirm-dialog"
dialog-context="changeTagsExpirationInfo"
dialog-action="changeTagsExpiration(info.tags, info.expiration_date, callback)"
dialog-title="Change Tags Expiration"
dialog-action-title="Change Expiration">
<form class="expiration-form">
<label>Tags that will be updated:</label>
<ul class="delete-tag-list">
<li ng-repeat="tag_info in changeTagsExpirationInfo.tags">
<span class="label label-default tag">{{ tag_info.name }}</span>
</li>
</ul>
<label style="margin-top: 20px;">Expiration Date:</label>
<span class="datetime-picker" datetime="changeTagsExpirationInfo.expiration_date"></span>
<span class="co-help-text">
If specified, the date and time that the key expires. If set to none, the tag(s) will not expire.
</span>
</form>
</div>
<!-- Delete Tag Confirm -->
<div class="cor-confirm-dialog"
dialog-context="deleteTagInfo"

View file

@ -74,7 +74,8 @@ angular.module('quay').directive('repoPanelTags', function () {
var tagData = $scope.repository.tags[tag];
var tagInfo = $.extend(tagData, {
'name': tag,
'last_modified_datetime': TableService.getReversedTimestamp(tagData.last_modified)
'last_modified_datetime': TableService.getReversedTimestamp(tagData.last_modified),
'expiration_date': tagData.expiration ? TableService.getReversedTimestamp(tagData.expiration) : null,
});
allTags.push(tagInfo);
@ -355,6 +356,10 @@ angular.module('quay').directive('repoPanelTags', function () {
$scope.tagActionHandler.askDeleteMultipleTags(tags);
};
$scope.askChangeTagsExpiration = function(tags) {
$scope.tagActionHandler.askChangeTagsExpiration(tags);
};
$scope.askAddTag = function(tag) {
$scope.tagActionHandler.askAddTag(tag.image_id);
};

View file

@ -271,6 +271,18 @@ angular.module('quay').directive('logsView', function () {
'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}',
'change_tag_expiration': function(metadata) {
if (metadata.expiration_date && metadata.old_expiration_date) {
return 'Tag {tag} set to expire on {expiration_date} (previously {old_expiration_date})';
} else if (metadata.expiration_date) {
return 'Tag {tag} set to expire on {expiration_date}';
} else if (metadata.old_expiration_date) {
return 'Tag {tag} set to no longer expire (previously {old_expiration_date})';
} else {
return 'Tag {tag} set to no longer expire';
}
},
// Note: These are deprecated.
'add_repo_webhook': 'Add webhook in repository {repo}',
'delete_repo_webhook': 'Delete webhook in repository {repo}'
@ -332,6 +344,7 @@ angular.module('quay').directive('logsView', function () {
'take_ownership': 'Take Namespace Ownership',
'manifest_label_add': 'Add Manifest Label',
'manifest_label_delete': 'Delete Manifest Label',
'change_tag_expiration': 'Change tag expiration',
// Note: these are deprecated.
'add_repo_webhook': 'Add webhook',

View file

@ -81,13 +81,58 @@ angular.module('quay').directive('tagOperationsDialog', function () {
$element.find('#createOrMoveTagModal').modal('hide');
});
ApiService.changeTagImage(data, params).then(function(resp) {
ApiService.changeTag(data, params).then(function(resp) {
$element.find('#createOrMoveTagModal').modal('hide');
$scope.addingTag = false;
markChanged([tag], []);
}, errorHandler);
};
$scope.changeTagsExpiration = function(tags, expiration_date, callback) {
if (!$scope.repository.can_write) { return; }
var count = tags.length;
var perform = function(index) {
if (index >= count) {
callback(true);
markChanged(tags, []);
return;
}
var tag_info = tags[index];
if (!tag_info) { return; }
$scope.changeTagExpiration(tag_info.name, expiration_date, function(result) {
if (!result) {
callback(false);
return;
}
perform(index + 1);
}, true);
};
perform(0);
};
$scope.changeTagExpiration = function(tag, expiration_date, callback) {
if (!$scope.repository.can_write) { return; }
var params = {
'repository': $scope.repository.namespace + '/' + $scope.repository.name,
'tag': tag
};
var data = {
'expiration': expiration_date
};
var errorHandler = ApiService.errorDisplay('Cannot change tag expiration', callback);
ApiService.changeTag(data, params).then(function() {
callback(true);
}, errorHandler);
};
$scope.deleteMultipleTags = function(tags, callback) {
if (!$scope.repository.can_write) { return; }
@ -296,6 +341,17 @@ angular.module('quay').directive('tagOperationsDialog', function () {
}, ApiService.errorDisplay('Could not load manifest labels'));
},
'askChangeTagsExpiration': function(tags) {
if ($scope.alertOnTagOpsDisabled()) {
return;
}
$scope.changeTagsExpirationInfo ={
'tags': tags,
'expiration_date': null
};
},
'askRestoreTag': function(tag, image_id, opt_manifest_digest) {
if ($scope.alertOnTagOpsDisabled()) {
return;