Merge branch 'laffa' of bitbucket.org:yackob03/quay into laffa

This commit is contained in:
Jake Moshenko 2014-09-18 17:27:08 -04:00
commit 99d57752f5
8 changed files with 197 additions and 79 deletions

View file

@ -2206,37 +2206,55 @@ p.editable:hover i {
font-size: 0.8em;
position: relative;
margin-top: 30px;
margin-right: 26px;
}
.repo .pull-container {
display: inline-block;
width: 300px;
width: 460px;
margin-left: 10px;
margin-right: 10px;
vertical-align: middle;
position: relative;
}
.repo .pull-container input {
cursor: default;
background: white;
color: #666;
padding: 4px;
border: 1px solid #ddd;
width: 300px;
}
.repo-image-view .id-container {
.repo .pull-container .pull-selector {
display: inline-block;
margin-top: 10px;
width: 114px;
font-size: 14px;
height: 36px;
vertical-align: top;
border: 1px solid #ddd;
margin-right: -3px;
background: #f8f8f8;
outline: none;
}
.repo-image-view .id-container input {
background: #fefefe;
.repo .pull-container .pull-selector i {
display: inline-block;
margin-right: 6px;
}
.repo-image-view .id-container .input-group {
width: 542px;
.repo .pull-container .copy-box {
width: 340px;
display: inline-block;
}
.repo .pull-container .copy-box .copy-container {
border-top-left-radius: 0px !important;
border-bottom-left-radius: 0px !important;
border-left: 0px;
}
.repo .pull-container .dropdown-menu li i.fa {
text-align: center;
width: 12px;
display: inline-block;
}
.repo .pull-container sup {
margin-left: 4px;
color: red;
}
.repo-image-view #clipboardCopied {
@ -2272,25 +2290,45 @@ p.editable:hover i {
position: relative;
}
.copy-box-element.disabled .input-group-addon {
display: none;
.copy-box-element .copy-container {
border-radius: 4px !important;
border: 1px solid #ddd;
position: relative;
}
.copy-box-element input {
border: 0px;
padding-right: 32px;
}
.copy-box-element .copy-container .copy-icon {
position: absolute;
top: 8px;
right: 10px;
display: inline-block;
color: #ddd;
font-size: 16px;
cursor: pointer;
transition: color 0.5s ease-in-out;
}
.copy-box-element .copy-container .copy-icon.zeroclipboard-is-hover {
color: #444;
}
.copy-box-element.disabled input {
border-radius: 4px !important;
margin-right: 0px;
}
.copy-box-element.disabled .copy-icon {
display: none;
}
.global-zeroclipboard-container embed {
cursor: pointer;
}
#copyClipboard.zeroclipboard-is-hover, .copy-box-element .zeroclipboard-is-hover {
background: #428bca;
color: white;
cursor: pointer !important;
}
#clipboardCopied.hovering, .copy-box-element .hovering {
.copy-box-element .hovering {
position: absolute;
right: 0px;
top: 40px;
@ -2298,16 +2336,11 @@ p.editable:hover i {
z-index: 100;
}
.copy-box-element .id-container {
display: inline-block;
vertical-align: middle;
}
.copy-box-element input {
background-color: white !important;
}
#clipboardCopied, .clipboard-copied-message {
.clipboard-copied-message {
font-size: 0.8em;
display: inline-block;
margin-right: 10px;
@ -2318,7 +2351,7 @@ p.editable:hover i {
border-radius: 4px;
}
#clipboardCopied.animated, .clipboard-copied-message {
.clipboard-copied-message {
-webkit-animation: fadeOut 4s ease-in-out 0s 1 forwards;
-moz-animation: fadeOut 4s ease-in-out 0s 1 forwards;
-ms-animation: fadeOut 4s ease-in-out 0s 1 forwards;

View file

@ -1,9 +1,12 @@
<div class="copy-box-element" ng-class="disabled ? 'disabled' : ''">
<div class="id-container">
<div class="input-group">
<div class="copy-container">
<input type="text" class="form-control" value="{{ value }}" readonly>
<span class="input-group-addon" data-title="Copy to Clipboard">
<i class="fa fa-copy"></i>
<span class="copy-icon" data-title="Copy to Clipboard"
data-container="body"
data-placement="bottom"
bs-tooltip>
<i class="fa fa-clipboard"></i>
</span>
</div>
</div>

View file

@ -812,6 +812,15 @@ quayApp = angular.module('quay', quayDependencies, function($provide, cfpLoading
return config['SERVER_HOSTNAME'];
};
config.getHost = function(opt_auth) {
var auth = opt_auth;
if (auth) {
auth = auth + '@';
}
return config['PREFERRED_URL_SCHEME'] + '://' + auth + config['SERVER_HOSTNAME'];
};
config.getUrl = function(opt_path) {
var path = opt_path || '';
return config['PREFERRED_URL_SCHEME'] + '://' + config['SERVER_HOSTNAME'] + path;
@ -2455,7 +2464,7 @@ quayApp.directive('copyBox', function () {
restrict: 'C',
scope: {
'value': '=value',
'hoveringMessage': '=hoveringMessage'
'hoveringMessage': '=hoveringMessage',
},
controller: function($scope, $element, $rootScope) {
$scope.disabled = false;
@ -2464,7 +2473,7 @@ quayApp.directive('copyBox', function () {
$rootScope.__copyBoxIdCounter = number + 1;
$scope.inputId = "copy-box-input-" + number;
var button = $($element).find('.input-group-addon');
var button = $($element).find('.copy-icon');
var input = $($element).find('input');
input.attr('id', $scope.inputId);

View file

@ -361,6 +361,9 @@ function RepoCtrl($scope, $sanitize, Restangular, ImageMetadataService, ApiServi
var namespace = $routeParams.namespace;
var name = $routeParams.name;
$scope.pullCommands = [];
$scope.currentPullCommand = null;
$rootScope.title = 'Loading...';
// Watch for the destruction of the scope.
@ -395,6 +398,47 @@ function RepoCtrl($scope, $sanitize, Restangular, ImageMetadataService, ApiServi
$scope.buildDialogShowCounter = 0;
$scope.getFormattedCommand = ImageMetadataService.getFormattedCommand;
$scope.setCurrentPullCommand = function(pullCommand) {
$scope.currentPullCommand = pullCommand;
};
$scope.updatePullCommand = function() {
$scope.pullCommands = [];
if ($scope.currentTag) {
$scope.pullCommands.push({
'title': 'docker pull (Tag ' + $scope.currentTag.name + ')',
'shortTitle': 'Pull Tag',
'icon': 'fa-tag',
'command': 'docker pull ' + Config.getDomain() + '/' + namespace + '/' + name + ':' + $scope.currentTag.name
});
}
$scope.pullCommands.push({
'title': 'docker pull (Full Repository)',
'shortTitle': 'Pull Repo',
'icon': 'fa-code-fork',
'command': 'docker pull ' + Config.getDomain() + '/' + namespace + '/' + name
});
if ($scope.currentTag) {
var squash = 'docker import ' + Config.getHost('ACCOUNTNAME:PASSWORDORTOKEN');
squash += '/verbs/v1/' + namespace + '/' + name + '/' + $scope.currentTag.name + '/squash';
squash += ' ';
squash += Config.getDomain() + '/' + namespace + '/' + name + '/' + $scope.currentTag.name + '.squash';
$scope.pullCommands.push({
'title': 'Squashed image (Tag ' + $scope.currentTag.name + ')',
'shortTitle': 'Squashed',
'icon': 'fa-file-archive-o',
'command': squash,
'experimental': true
});
}
$scope.currentPullCommand = $scope.pullCommands[0];
};
$scope.showNewBuildDialog = function() {
$scope.buildDialogShowCounter++;
};
@ -587,6 +631,8 @@ function RepoCtrl($scope, $sanitize, Restangular, ImageMetadataService, ApiServi
$location.search('tag', null);
$location.search('image', imageId.substr(0, 12));
}
$scope.updatePullCommand();
};
$scope.setTag = function(tagName, opt_updateURL) {
@ -621,6 +667,8 @@ function RepoCtrl($scope, $sanitize, Restangular, ImageMetadataService, ApiServi
$scope.currentTag = null;
$scope.currentImage = null;
}
$scope.updatePullCommand();
};
$scope.getFirstTextLine = getFirstTextLine;

View file

@ -56,10 +56,21 @@
<!-- Pull Command -->
<span class="pull-command visible-md-inline">
<div class="pull-container" data-title="Pull repository" bs-tooltip="tooltip.title">
<div class="input-group">
<div class="copy-box" hovering-message="true" value="'docker pull ' + Config.getDomain() + '/' + repo.namespace + '/' + repo.name"></div>
</div>
<div class="pull-container" ng-show="currentPullCommand">
<button class="pull-selector dropdown-toggle" data-toggle="dropdown">
<i class="fa" ng-class="currentPullCommand.icon"></i>
{{ currentPullCommand.shortTitle }}
<b class="caret"></b>
</button>
<ul class="dropdown-menu">
<li ng-repeat="pullCommand in pullCommands">
<a href="javascript:void(0)" ng-click="setCurrentPullCommand(pullCommand)"><i class="fa" ng-class="pullCommand.icon"></i>
{{ pullCommand.title }}
<sup ng-if="pullCommand.experimental">Experimental</sup>
</a>
</li>
</ul>
<div class="copy-box" hovering-message="true" value="currentPullCommand.command"></div>
</div>
</span>
</div>

31
util/aufs.py Normal file
View file

@ -0,0 +1,31 @@
import os
AUFS_METADATA = u'.wh..wh.'
AUFS_WHITEOUT = u'.wh.'
AUFS_WHITEOUT_PREFIX_LENGTH = len(AUFS_WHITEOUT)
def is_aufs_metadata(filepath):
""" Returns whether the given filepath references an AUFS metadata file. """
filename = os.path.basename(filepath)
return filename.startswith(AUFS_METADATA) or filepath.startswith(AUFS_METADATA)
def get_deleted_filename(filepath):
""" Returns the name of the deleted file referenced by the AUFS whiteout file at
the given path or None if the file path does not reference a whiteout file.
"""
filename = os.path.basename(filepath)
if not filename.startswith(AUFS_WHITEOUT):
return None
return filename[AUFS_WHITEOUT_PREFIX_LENGTH:]
def get_deleted_prefix(filepath):
""" Returns the path prefix of the deleted file referenced by the AUFS whiteout file at
the given path or None if the file path does not reference a whiteout file.
"""
deleted_filename = get_deleted_filename(filepath)
if deleted_filename is None:
return None
dirname = os.path.dirname(filepath)
return os.path.join('/', dirname, deleted_filename)

View file

@ -1,16 +1,10 @@
import marisa_trie
import os
import tarfile
AUFS_METADATA = u'.wh..wh.'
AUFS_WHITEOUT = u'.wh.'
AUFS_WHITEOUT_PREFIX_LENGTH = len(AUFS_WHITEOUT)
from aufs import is_aufs_metadata, get_deleted_prefix
ALLOWED_TYPES = {tarfile.REGTYPE, tarfile.AREGTYPE}
def files_and_dirs_from_tar(source_stream, removed_prefix_collector):
try:
tar_stream = tarfile.open(mode='r|*', fileobj=source_stream)
@ -20,22 +14,19 @@ def files_and_dirs_from_tar(source_stream, removed_prefix_collector):
for tar_info in tar_stream:
absolute = os.path.relpath(tar_info.name.decode('utf-8'), './')
dirname = os.path.dirname(absolute)
filename = os.path.basename(absolute)
# Skip directories and metadata
if (filename.startswith(AUFS_METADATA) or
absolute.startswith(AUFS_METADATA)):
# Skip
# Skip metadata.
if is_aufs_metadata(absolute):
continue
elif filename.startswith(AUFS_WHITEOUT):
removed_filename = filename[AUFS_WHITEOUT_PREFIX_LENGTH:]
removed_prefix = os.path.join('/', dirname, removed_filename)
removed_prefix_collector.add(removed_prefix)
# Add prefixes of removed paths to the collector.
deleted_prefix = get_deleted_prefix(absolute)
if deleted_prefix is not None:
deleted_prefix.add(deleted_prefix)
continue
elif tar_info.type in ALLOWED_TYPES:
# Otherwise, yield the path if it is in the allowed types.
if tar_info.type in ALLOWED_TYPES:
yield '/' + absolute

View file

@ -1,8 +1,8 @@
import marisa_trie
import os
import tarfile
import StringIO
import traceback
from aufs import is_aufs_metadata, get_deleted_prefix
AUFS_METADATA = u'.wh..wh.'
@ -70,19 +70,15 @@ class StreamLayerMerger(object):
def process_tar_info(self, tar_info):
absolute = os.path.relpath(tar_info.name.decode('utf-8'), './')
dirname = os.path.dirname(absolute)
filename = os.path.basename(absolute)
# Skip directories and metadata
if (filename.startswith(AUFS_METADATA) or
absolute.startswith(AUFS_METADATA)):
# Skip
# Skip metadata.
if is_aufs_metadata(absolute):
return None
elif filename.startswith(AUFS_WHITEOUT):
removed_filename = filename[AUFS_WHITEOUT_PREFIX_LENGTH:]
removed_prefix = os.path.join('/', dirname, removed_filename)
self.encountered.append(removed_prefix)
# Add any prefix of deleted paths to the prefix list.
deleted_prefix = get_deleted_prefix(absolute)
if deleted_prefix is not None:
self.encountered.append(deleted_prefix)
return None
# Check if this file has already been encountered somewhere. If so,
@ -90,10 +86,6 @@ class StreamLayerMerger(object):
if unicode(absolute) in self.trie:
return None
# Otherwise, add the path to the encountered list and return it.
self.encountered.append(absolute)
if tar_info.isdir() or tar_info.issym() or tar_info.islnk():
return (tar_info, False)
elif tar_info.isfile():
return (tar_info, True)
return (tar_info, tar_info.isfile() or tar_info.isdev())