Work in progress: Add the UI for the build status and start on the file drop stuff

This commit is contained in:
Joseph Schorr 2013-10-26 16:03:11 -04:00
parent dc7a62db67
commit fc6e3258a8
13 changed files with 278 additions and 31 deletions

View file

@ -23,4 +23,4 @@ if application.config.get('INCLUDE_TEST_ENDPOINTS', False):
application.debug = True
if __name__ == '__main__':
application.run(port=5000, debug=True, host='0.0.0.0')
application.run(port=5000, debug=True, threaded=True, host='0.0.0.0')

View file

@ -180,22 +180,22 @@ user_files = UserRequestFiles(app.config['AWS_ACCESS_KEY'],
app.config['REGISTRY_S3_BUCKET'])
@app.route('/api/repository/', methods=['POST'])
@app.route('/api/repository', methods=['POST'])
@api_login_required
def create_repo_api():
owner = current_user.db_user()
namespace_name = owner.username
repository_name = request.values['repository']
visibility = request.values['visibility']
repository_name = request.get_json()['repository']
visibility = request.get_json()['visibility']
repo = model.create_repository(namespace_name, repository_name, owner,
visibility)
resp = make_response('Created', 201)
resp.headers['Location'] = url_for('get_repo_api', namespace=namespace_name,
repository=repository_name)
return resp
return jsonify({
'namespace': namespace_name,
'name': repository_name
})
@app.route('/api/find/repository', methods=['GET'])

View file

@ -13,6 +13,30 @@
color: #428bca;
}
.build-statuses {
}
.build-status-container {
padding: 4px;
margin-bottom: 10px;
border-bottom: 1px solid #eee;
}
.build-status-container .build-message {
display: block;
white-space: nowrap;
}
.build-status-container .progress {
height: 12px;
margin: 0px;
margin-top: 10px;
}
.build-status-container:last-child {
margin-bottom: 0px;
border-bottom: 0px solid white;
}
.repo-circle {
position: relative;
@ -579,6 +603,7 @@ p.editable:hover i {
}
.repo .description {
margin-top: 10px;
margin-bottom: 40px;
}
@ -608,22 +633,70 @@ p.editable:hover i {
display: inline-block;
}
.repo .status-boxes {
float: right;
margin-bottom: 20px;
}
.repo .status-boxes .status-box {
cursor: pointer;
display: inline-block;
border: 1px solid #eee;
border-radius: 4px;
}
.repo .status-boxes .status-box .title {
padding: 4px;
display: inline-block;
padding-left: 10px;
padding-right: 10px;
}
.repo .status-boxes .status-box .title i {
margin-right: 6px;
}
.repo .status-boxes .status-box .count {
display: inline-block;
background-image: linear-gradient(to bottom,#e8e8e8 0,#f5f5f5 100%);
padding: 4px;
padding-left: 10px;
padding-right: 10px;
font-weight: bold;
transform: scaleX(0);
-webkit-transform: scaleX(0);
-moz-transform: scaleX(0);
transition: transform 500ms ease-in-out;
-webkit-transition: -webkit-transform 500ms ease-in-out;
-moz-transition: -moz-transform 500ms ease-in-out;
}
.repo .status-boxes .status-box .count.visible {
transform: scaleX(1);
-webkit-transform: scaleX(1);
-moz-transform: scaleX(1);
}
.repo .pull-command {
float: right;
display: inline-block;
font-size: 1.2em;
font-size: 0.8em;
position: relative;
margin-right: 10px;
margin-top: 30px;
margin-right: 26px;
}
.repo .pull-command .pull-container {
.repo .pull-container {
display: inline-block;
width: 300px;
margin-left: 10px;
margin-right: 10px;
vertical-align: middle;
}
.repo .pull-command input {
.repo .pull-container input {
cursor: default;
background: white;
color: #666;
@ -788,8 +861,22 @@ p.editable:hover i {
cursor: pointer;
}
.repo .description p {
margin-bottom: 6px;
.repo .build-info {
padding: 10px;
margin: 0px;
}
.repo .build-info .progress {
margin: 0px;
margin-top: 10px;
}
.repo .section {
display: block;
margin-bottom: 20px;
padding-bottom: 20px;
border-bottom: 1px solid #eee;
}
.repo .description p:last-child {
@ -967,7 +1054,7 @@ p.editable:hover i {
background: rgb(253, 191, 191);
}
.repo-admin .repo-access-state .state-icon i.fa-unlock-alt {
.repo-admin .repo-access-state .state-icon i.fa-unlock {
background: rgb(170, 236, 170);
}

View file

@ -0,0 +1,8 @@
<div class="build-status-container">
<span class="build-message">{{ getBuildMessage(build) }}</span>
<div class="progress" ng-class="getBuildProgress(build) < 100 ? 'active progress-striped' : ''" ng-show="getBuildProgress(build) >= 0">
<div class="progress-bar" role="progressbar" aria-valuenow="{{ getBuildProgress(build) }}" aria-valuemin="0" aria-valuemax="100" style="{{ 'width: ' + getBuildProgress(build) + '%' }}">
</div>
</div>
</div>

View file

@ -1,5 +1,5 @@
// Start the application code itself.
quayApp = angular.module('quay', ['restangular', 'angularMoment', 'angulartics', 'angulartics.mixpanel'], function($provide) {
quayApp = angular.module('quay', ['restangular', 'angularMoment', 'angulartics', 'angulartics.mixpanel', '$strap.directives'], function($provide) {
$provide.factory('UserService', ['Restangular', function(Restangular) {
var userResponse = {
verified: false,
@ -177,7 +177,72 @@ quayApp.directive('repoCircle', function () {
'repo': '=repo'
},
controller: function($scope, $element) {
window.console.log($scope);
}
};
return directiveDefinitionObject;
});
quayApp.directive('buildStatus', function () {
var directiveDefinitionObject = {
priority: 0,
templateUrl: '/static/directives/build-status.html',
replace: false,
transclude: false,
restrict: 'C',
scope: {
'build': '=build'
},
controller: function($scope, $element) {
$scope.getBuildProgress = function(buildInfo) {
switch (buildInfo.status) {
case 'building':
return (buildInfo.current_command / buildInfo.total_commands) * 100;
break;
case 'pushing':
return (buildInfo.current_image / buildInfo.total_images) * 100;
break;
case 'complete':
return 100;
break;
case 'initializing':
case 'starting':
case 'waiting':
return 0;
break;
}
return -1;
};
$scope.getBuildMessage = function(buildInfo) {
switch (buildInfo.status) {
case 'initializing':
return 'Starting Dockerfile build';
break;
case 'starting':
case 'waiting':
case 'building':
return 'Building image from Dockerfile';
break;
case 'pushing':
return 'Pushing image built from Dockerfile';
break;
case 'complete':
return 'Dockerfile build completed and pushed';
break;
case 'error':
return 'Dockerfile build failed: ' + buildInfo.message;
break;
}
};
}
};
return directiveDefinitionObject;

View file

@ -339,6 +339,19 @@ function RepoCtrl($scope, Restangular, $routeParams, $rootScope, $location, $tim
}
});
var startBuildInfoTimer = function(repo) {
setInterval(function() {
$scope.$apply(function() { getBuildInfo(repo); });
}, 5000);
};
var getBuildInfo = function(repo) {
var buildInfo = Restangular.one('repository/' + repo.namespace + '/' + repo.name + '/build/');
buildInfo.get().then(function(resp) {
$scope.buildsInfo = resp.builds;
});
};
var listImages = function() {
if ($scope.imageHistory) { return; }
@ -443,6 +456,10 @@ function RepoCtrl($scope, Restangular, $routeParams, $rootScope, $location, $tim
$('#copyClipboard').clipboardCopy();
$scope.loading = false;
if (repo.is_building) {
getBuildInfo(repo);
}
}, function() {
$scope.repo = null;
$scope.loading = false;
@ -895,7 +912,7 @@ function V1Ctrl($scope, UserService) {
};
}
function NewRepoCtrl($scope, UserService) {
function NewRepoCtrl($scope, $location, UserService, Restangular) {
$scope.repo = {
'is_public': 1,
'description': '',
@ -906,7 +923,7 @@ function NewRepoCtrl($scope, UserService) {
$scope.user = currentUser;
if ($scope.user.anonymous) {
document.location = '/signin/';
$location.path('/signin/');
}
}, true);
@ -931,4 +948,24 @@ function NewRepoCtrl($scope, UserService) {
$('#editModal').modal('hide');
$scope.repo.description = $('#wmd-input-description')[0].value;
};
$scope.createNewRepo = function() {
$scope.creating = true;
var repo = $scope.repo;
var data = {
'repository': repo.name,
'visibility': repo.is_public == '1' ? 'public' : 'private'
};
var createPost = Restangular.one('repository');
createPost.customPOST(data).then(function(created) {
// Repository created. Start the upload process if applicable.
// Otherwise, redirect to the repo page.
$location.path('/repository/' + created.namespace + '/' + created.name);
}, function() {
$('#cannotcreateModal').modal();
$scope.creating = false;
});
};
}

8
static/lib/angular-strap.min.js vendored Normal file

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,5 @@
<div class="build-statuses">
<div ng-repeat="build in buildsInfo">
<div class="build-status" build="build"></div>
</div>
</div>

View file

@ -1,4 +1,8 @@
<div class="container new-repo" ng-show="!user.anonymous">
<div class="container" ng-show="creating">
<i class="fa fa-spinner fa-spin fa-3x"></i>
</div>
<div class="container new-repo" ng-show="!user.anonymous && !creating">
<form method="post" name="newRepoForm" ng-submit="createNewRepo()">
<!-- Header -->
@ -74,7 +78,7 @@
<div class="row">
<div class="col-md-1"></div>
<div class="col-md-8">
<button class="btn btn-large btn-success" type="submit" ng-disabled="newRepoForm.$invalid">Create Repository</button>
<button class="btn btn-large btn-success" type="submit" ng-click="createNewRepo" ng-disabled="newRepoForm.$invalid">Create Repository</button>
</div>
</div>
@ -105,3 +109,21 @@
</div><!-- /.modal-dialog -->
</div><!-- /.modal -->
<!-- Modal message dialog -->
<div class="modal fade" id="cannotcreateModal">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
<h4 class="modal-title">Cannot create repository</h4>
</div>
<div class="modal-body">
The repository could not be created.
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
</div>
</div><!-- /.modal-content -->
</div><!-- /.modal-dialog -->
</div><!-- /.modal -->

View file

@ -124,7 +124,7 @@
</div>
<div class="repo-access-state" ng-show="repo.is_public">
<div class="state-icon"><i class="fa fa-unlock-alt"></i></div>
<div class="state-icon"><i class="fa fa-unlock"></i></div>
This repository is currently <b>public</b> and is visible to all users, and may be pulled by all users.

View file

@ -20,11 +20,10 @@
</a>
</span>
</h3>
<!-- Pull command -->
<div class="pull-command">
Get this repository:
<div class="pull-command visible-md visible-lg" style="display: none;">
<span class="pull-command-title">Pull repository:</span>
<div class="pull-container">
<div class="input-group">
<input id="pull-text" type="text" class="form-control" value="{{ 'docker pull quay.io/' + repo.namespace + '/' + repo.name }}" readonly>
@ -40,17 +39,32 @@
</div>
</div>
<!-- Status boxes -->
<div class="status-boxes">
<div id="buildInfoBox" class="status-box" ng-show="repo.is_building"
bs-popover="'static/partials/build-status-item.html'" data-placement="bottom">
<span class="title">
<i class="fa fa-spinner fa-spin"></i>
<b>Building Images</b>
</span>
<span class="count" ng-class="buildsInfo ? 'visible' : ''"><span>{{ buildsInfo ? buildsInfo.length : '-' }}</span></span>
</div>
</div>
<!-- Description -->
<p ng-class="'description lead ' + (repo.can_write ? 'editable' : 'noteditable')" ng-click="editDescription()">
<span class="content" ng-bind-html-unsafe="getMarkedDown(repo.description)"></span>
<i class="fa fa-edit"></i>
</p>
<div class="description">
<p ng-class="'lead ' + (repo.can_write ? 'editable' : 'noteditable')" ng-click="editDescription()">
<span class="content" ng-bind-html-unsafe="getMarkedDown(repo.description)"></span>
<i class="fa fa-edit"></i>
</p>
</div>
<div class="repo-content" ng-show="!currentTag.image">
<!-- Empty message -->
<div class="repo-content" ng-show="!currentTag.image && !repo.is_building">
<div class="empty-message">(This repository is empty)</div>
</div>
<!-- Content view -->
<div class="repo-content" ng-show="currentTag.image">
<!-- Image History -->
<div id="image-history">

View file

@ -43,6 +43,7 @@
<script src="//cdn.jsdelivr.net/underscorejs/1.5.2/underscore-min.js"></script>
<script src="//cdn.jsdelivr.net/restangular/1.1.3/restangular.js"></script>
<script src="static/lib/angular-strap.min.js"></script>
<script src="static/lib/angulartics.js"></script>
<script src="static/lib/angulartics-mixpanel.js"></script>

Binary file not shown.