Merge remote-tracking branch 'upstream/new-ui-with-sync-image' into job-service

This commit is contained in:
Tan Jiang 2016-06-27 17:40:43 +08:00
commit 6397e92193
16 changed files with 76 additions and 94 deletions

View File

@ -58,7 +58,7 @@ func (ra *RepJobAPI) Prepare() {
// List filters jobs according to the policy and repository
func (ra *RepJobAPI) List() {
var policyID int64
var repository string
var repository, status string
var err error
policyIDStr := ra.GetString("policy_id")
@ -70,10 +70,11 @@ func (ra *RepJobAPI) List() {
}
repository = ra.GetString("repository")
status = ra.GetString("status")
jobs, err := dao.FilterRepJobs(repository, policyID)
jobs, err := dao.FilterRepJobs(policyID, repository, status)
if err != nil {
log.Errorf("failed to filter jobs according policy ID %d and repository %s: %v", policyID, repository, err)
log.Errorf("failed to filter jobs according policy ID %d, repository %s, status %s: %v", policyID, repository, status, err)
ra.RenderError(http.StatusInternalServerError, "Failed to query job")
return
}

View File

@ -68,7 +68,7 @@ func (s *SearchAPI) Get() {
}
}
projectSorter := &utils.ProjectSorter{Projects: projects}
projectSorter := &models.ProjectSorter{Projects: projects}
sort.Sort(projectSorter)
projectResult := []map[string]interface{}{}
for _, p := range projects {

View File

@ -1141,7 +1141,7 @@ func TestDeleteRepJob(t *testing.T) {
}
func TestFilterRepJobs(t *testing.T) {
jobs, err := FilterRepJobs("", policyID)
jobs, err := FilterRepJobs(policyID, "", "")
if err != nil {
log.Errorf("Error occured in FilterRepJobs: %v, policy ID: %d", err, policyID)
return

View File

@ -302,29 +302,24 @@ func GetRepJobByPolicy(policyID int64) ([]*models.RepJob, error) {
}
// FilterRepJobs filters jobs by repo and policy ID
func FilterRepJobs(repo string, policyID int64) ([]*models.RepJob, error) {
func FilterRepJobs(policyID int64, repository, status string) ([]*models.RepJob, error) {
o := GetOrmer()
var args []interface{}
sql := `select * from replication_job `
if len(repo) != 0 && policyID != 0 {
sql += `where repository like ? and policy_id = ? `
args = append(args, "%"+repo+"%")
args = append(args, policyID)
} else if len(repo) != 0 {
sql += `where repository like ? `
args = append(args, "%"+repo+"%")
} else if policyID != 0 {
sql += `where policy_id = ? `
args = append(args, policyID)
qs := o.QueryTable(new(models.RepJob))
if policyID != 0 {
qs = qs.Filter("PolicyID", policyID)
}
sql += `order by creation_time`
if len(repository) != 0 {
qs = qs.Filter("Repository__icontains", repository)
}
if len(status) != 0 {
qs = qs.Filter("Status__icontains", status)
}
qs = qs.OrderBy("CreationTime")
var jobs []*models.RepJob
if _, err := o.Raw(sql, args).QueryRows(&jobs); err != nil {
_, err := qs.All(&jobs)
if err != nil {
return nil, err
}

View File

@ -37,3 +37,23 @@ type Project struct {
Role int `json:"current_user_role_id"`
RepoCount int `json:"repo_count"`
}
// ProjectSorter holds an array of projects
type ProjectSorter struct {
Projects []Project
}
// Len returns the length of array in ProjectSorter
func (ps *ProjectSorter) Len() int {
return len(ps.Projects)
}
// Less defines the comparison rules of project
func (ps *ProjectSorter) Less(i, j int) bool {
return ps.Projects[i].Name < ps.Projects[j].Name
}
// Swap swaps the position of i and j
func (ps *ProjectSorter) Swap(i, j int) {
ps.Projects[i], ps.Projects[j] = ps.Projects[j], ps.Projects[i]
}

View File

@ -19,6 +19,7 @@ import (
"time"
"github.com/astaxie/beego/validation"
"github.com/vmware/harbor/utils"
)
const (
@ -129,6 +130,8 @@ func (r *RepTarget) Valid(v *validation.Validation) {
v.SetError("endpoint", "can not be empty")
}
r.URL = utils.FormatEndpoint(r.URL)
if len(r.URL) > 64 {
v.SetError("endpoint", "max length is 64")
}

View File

@ -32,6 +32,7 @@ body {
.container-fluid-custom {
background-color: #EFEFEF;
width: 100%;
height: 100%;
min-width: 1px;
overflow: auto;
min-height: 1px;

View File

@ -23,15 +23,13 @@
if(!angular.isDefined(scope.subsHeight)) scope.subsHeight = 110;
if(!angular.isDefined(scope.subsSection)) scope.subsSection = 32;
if(!angular.isDefined(scope.subsSubPane)) scope.subsSubPane = 226;
if(!angular.isDefined(scope.subsTabPane)) scope.subsTabPane = 66;
scope.$watch(scope.getDimension, function(current) {
if(current) {
var h = current.h;
element.css({'height' : (h - scope.subsHeight) + 'px'});
var h = current.h;
element.find('.section').css({'height': (h - scope.subsHeight - scope.subsSection) + 'px'});
element.find('.sub-pane').css({'height': (h - scope.subsHeight - scope.subsSubPane) + 'px'});
element.find('.tab-pane').css({'height': (h - scope.subsHeight - scope.subsTabPane) + 'px'});
element.find('.tab-pane').css({'height': (h - scope.subsHeight - scope.subsSubPane) + 'px'});
}
}, true);

View File

@ -138,7 +138,7 @@
}else{
element.find('#down-pane').css({'height' : (maxDownPaneHeight) + 'px'});
$(document).off('mousemove');
}
}
}
function mouseupHandler(e) {
$(document).off('mousedown');
@ -171,7 +171,8 @@
element
.find('#upon-pane table>tbody>tr')
.css({'background-color': '#FFFFFF'})
.css({'color': '#000'});
.css({'color': '#000'})
.css({'cursor': 'default'});
element
.find('#upon-pane table>tbody>tr a')
.css({'color': '#337ab7'});

View File

@ -1,4 +1,4 @@
<div class="tab-pane active" id="repositories" >
<div class="tab-pane active" id="repositories" element-height>
<div class="col-xs-12 col-md-12 each-tab-pane">
<div class="form-inline">
<div class="input-group">
@ -11,6 +11,7 @@
<div ng-if="vm.repositories.length === 0" class="empty-hint">
<h3 style="margin-top: 200px;" class="text-muted">// 'no_repositories' | tr //</h3>
</div>
<div class="sub-pane">
<div ng-if="vm.repositories.length > 0" class="panel-group" id="accordion" role="tablist" aria-multiselectable="true">
<modal-dialog action="vm.deleteImage()" content-type="text/html" modal-title="//vm.modalTitle//" modal-message="//vm.modalMessage//"></modal-dialog>
<div class="panel panel-default" ng-repeat="repo in vm.repositories">
@ -25,5 +26,6 @@
<list-tag associate-id="$index + 1" repo-name="repo" tag-count="vm.tagCount"></list-tag>
</div>
</div>
</div>
</div>
</div>

View File

@ -8,6 +8,9 @@
ListRepositoryController.$inject = ['$scope', 'ListRepositoryService', 'DeleteRepositoryService', '$filter', 'trFilter', '$location', 'getParameterByName'];
function ListRepositoryController($scope, ListRepositoryService, DeleteRepositoryService, $filter, trFilter, $location, getParameterByName) {
$scope.subsTabPane = 30;
var vm = this;
vm.filterInput = '';

View File

@ -21,7 +21,7 @@ import (
"net/http"
au "github.com/docker/distribution/registry/client/auth"
"github.com/vmware/harbor/utils/registry/utils"
"github.com/vmware/harbor/utils"
)
// Authorizer authorizes requests according to the schema

View File

@ -21,8 +21,8 @@ import (
"net/url"
"strings"
"github.com/vmware/harbor/utils"
registry_error "github.com/vmware/harbor/utils/registry/error"
"github.com/vmware/harbor/utils/registry/utils"
)
// Registry holds information of a registry entity

View File

@ -30,8 +30,8 @@ import (
"github.com/docker/distribution/manifest/schema1"
"github.com/docker/distribution/manifest/schema2"
"github.com/vmware/harbor/utils"
registry_error "github.com/vmware/harbor/utils/registry/error"
"github.com/vmware/harbor/utils/registry/utils"
)
// Repository holds information of a repository entity

View File

@ -1,44 +0,0 @@
/*
Copyright (c) 2016 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package utils
import (
"net/url"
"strings"
)
// FormatEndpoint formats endpoint
func FormatEndpoint(endpoint string) string {
endpoint = strings.TrimSpace(endpoint)
endpoint = strings.TrimRight(endpoint, "/")
if !strings.HasPrefix(endpoint, "http://") &&
!strings.HasPrefix(endpoint, "https://") {
endpoint = "http://" + endpoint
}
return endpoint
}
// ParseEndpoint parses endpoint to a URL
func ParseEndpoint(endpoint string) (*url.URL, error) {
endpoint = FormatEndpoint(endpoint)
u, err := url.Parse(endpoint)
if err != nil {
return nil, err
}
return u, nil
}

View File

@ -16,9 +16,8 @@
package utils
import (
"net/url"
"strings"
"github.com/vmware/harbor/models"
)
// Repository holds information about repository
@ -34,22 +33,25 @@ func (r *Repository) GetProject() string {
return r.Name[0:strings.LastIndex(r.Name, "/")]
}
// ProjectSorter holds an array of projects
type ProjectSorter struct {
Projects []models.Project
// FormatEndpoint formats endpoint
func FormatEndpoint(endpoint string) string {
endpoint = strings.TrimSpace(endpoint)
endpoint = strings.TrimRight(endpoint, "/")
if !strings.HasPrefix(endpoint, "http://") &&
!strings.HasPrefix(endpoint, "https://") {
endpoint = "http://" + endpoint
}
return endpoint
}
// Len returns the length of array in ProjectSorter
func (ps *ProjectSorter) Len() int {
return len(ps.Projects)
}
// ParseEndpoint parses endpoint to a URL
func ParseEndpoint(endpoint string) (*url.URL, error) {
endpoint = FormatEndpoint(endpoint)
// Less defines the comparison rules of project
func (ps *ProjectSorter) Less(i, j int) bool {
return ps.Projects[i].Name < ps.Projects[j].Name
}
// Swap swaps the position of i and j
func (ps *ProjectSorter) Swap(i, j int) {
ps.Projects[i], ps.Projects[j] = ps.Projects[j], ps.Projects[i]
u, err := url.Parse(endpoint)
if err != nil {
return nil, err
}
return u, nil
}