From a2b6355fb593aef968599964d1dc3ffe15aa1ec8 Mon Sep 17 00:00:00 2001 From: Wenkai Yin Date: Tue, 28 Jun 2016 17:33:39 +0800 Subject: [PATCH 01/21] audit deleting repository --- api/repository.go | 10 ---- service/notification.go | 107 +++++++++++++++++++++++++++------------- 2 files changed, 72 insertions(+), 45 deletions(-) diff --git a/api/repository.go b/api/repository.go index b4b36c090..57564e61f 100644 --- a/api/repository.go +++ b/api/repository.go @@ -154,17 +154,7 @@ func (ra *RepositoryAPI) Delete() { ra.CustomAbort(http.StatusInternalServerError, "internal error") } log.Infof("delete tag: %s %s", repoName, t) - go TriggerReplicationByRepository(repoName, []string{t}, models.RepOpDelete) - } - - go func() { - log.Debug("refreshing catalog cache") - if err := cache.RefreshCatalogCache(); err != nil { - log.Errorf("error occurred while refresh catalog cache: %v", err) - } - }() - } type tag struct { diff --git a/service/notification.go b/service/notification.go index 2bd391924..e72b13419 100644 --- a/service/notification.go +++ b/service/notification.go @@ -39,55 +39,92 @@ const manifestPattern = `^application/vnd.docker.distribution.manifest.v\d\+json // Post handles POST request, and records audit log or refreshes cache based on event. func (n *NotificationHandler) Post() { var notification models.Notification - //log.Info("Notification Handler triggered!\n") - // log.Infof("request body in string: %s", string(n.Ctx.Input.CopyBody())) + log.Infof("request body in string: %s", string(n.Ctx.Input.CopyBody(1<<32))) err := json.Unmarshal(n.Ctx.Input.CopyBody(1<<32), ¬ification) if err != nil { - log.Errorf("error while decoding json: %v", err) + log.Errorf("failed to decode notification: %v", err) return } - var username, action, repo, project, repoTag string - var matched bool - for _, e := range notification.Events { - matched, err = regexp.MatchString(manifestPattern, e.Target.MediaType) - if err != nil { - log.Errorf("Failed to match the media type against pattern, error: %v", err) - matched = false + + events, err := filterEvents(¬ification) + if err != nil { + log.Errorf("failed to filter events: %v", err) + return + } + + for _, event := range events { + repository := event.Target.Repository + + project := "" + if strings.Contains(repository, "/") { + project = repository[0:strings.LastIndex(repository, "/")] } - if matched && (strings.HasPrefix(e.Request.UserAgent, "docker") || - strings.ToLower(strings.TrimSpace(e.Request.UserAgent)) == "harbor-registry-client") { - username = e.Actor.Name - action = e.Action - repo = e.Target.Repository - repoTag = e.Target.Tag - log.Debugf("repo tag is : %v ", repoTag) - if strings.Contains(repo, "/") { - project = repo[0:strings.LastIndex(repo, "/")] - } - if username == "" { - username = "anonymous" - } + tag := event.Target.Tag + action := event.Action - if action == "pull" && username == "job-service-user" { - return - } + user := event.Actor.Name + if len(user) == 0 { + user = "anonymous" + } - go dao.AccessLog(username, project, repo, repoTag, action) + go dao.AccessLog(user, project, repository, tag, action) + if action == "push" || action == "delete" { + go func() { + if err := cache.RefreshCatalogCache(); err != nil { + log.Errorf("failed to refresh cache: %v", err) + } + }() + + operation := "" if action == "push" { - go func() { - err2 := cache.RefreshCatalogCache() - if err2 != nil { - log.Errorf("Error happens when refreshing cache: %v", err2) - } - }() - - go api.TriggerReplicationByRepository(repo, []string{repoTag}, models.RepOpTransfer) + operation = models.RepOpTransfer + } else { + operation = models.RepOpDelete } + + go api.TriggerReplicationByRepository(repository, []string{tag}, operation) + } + } +} + +func filterEvents(notification *models.Notification) ([]*models.Event, error) { + events := []*models.Event{} + + for _, event := range notification.Events { + + //delete + // TODO add tag field + if event.Action == "delete" { + events = append(events, &event) + continue + } + + isManifest, err := regexp.MatchString(manifestPattern, event.Target.MediaType) + if err != nil { + log.Errorf("failed to match the media type against pattern: %v", err) + continue + } + + if !isManifest { + continue + } + + //pull and push manifest by docker-client + if strings.HasPrefix(event.Request.UserAgent, "docker") && (event.Action == "pull" || event.Action == "push") { + events = append(events, &event) + continue + } + + //push manifest by docker-client or job-service + if strings.ToLower(strings.TrimSpace(event.Request.UserAgent)) == "harbor-registry-client" && event.Action == "push" { + events = append(events, &event) + continue } } + return events, nil } // Render returns nil as it won't render any template. From 10189913127eb8b2f803bfa4518ff707cd4fd108 Mon Sep 17 00:00:00 2001 From: Wenkai Yin Date: Tue, 28 Jun 2016 21:39:38 +0800 Subject: [PATCH 02/21] print log if error occurs --- service/notification.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/service/notification.go b/service/notification.go index e72b13419..ae3747b79 100644 --- a/service/notification.go +++ b/service/notification.go @@ -69,7 +69,11 @@ func (n *NotificationHandler) Post() { user = "anonymous" } - go dao.AccessLog(user, project, repository, tag, action) + go func() { + if err := dao.AccessLog(user, project, repository, tag, action); err != nil { + log.Errorf("failed to add access log: %v", err) + } + }() if action == "push" || action == "delete" { go func() { if err := cache.RefreshCatalogCache(); err != nil { From ac17546d38f317b8d26666bb7f4b86778fdc54f6 Mon Sep 17 00:00:00 2001 From: Wenkai Yin Date: Tue, 28 Jun 2016 21:47:52 +0800 Subject: [PATCH 03/21] remove useless log --- service/notification.go | 1 - 1 file changed, 1 deletion(-) diff --git a/service/notification.go b/service/notification.go index ae3747b79..cc1585bc3 100644 --- a/service/notification.go +++ b/service/notification.go @@ -39,7 +39,6 @@ const manifestPattern = `^application/vnd.docker.distribution.manifest.v\d\+json // Post handles POST request, and records audit log or refreshes cache based on event. func (n *NotificationHandler) Post() { var notification models.Notification - log.Infof("request body in string: %s", string(n.Ctx.Input.CopyBody(1<<32))) err := json.Unmarshal(n.Ctx.Input.CopyBody(1<<32), ¬ification) if err != nil { From 2fafa3929c54d78487113ce095f83cd378a88e4f Mon Sep 17 00:00:00 2001 From: kunw Date: Wed, 29 Jun 2016 14:18:36 +0800 Subject: [PATCH 04/21] update for auto-resizing height to table body. --- static/resources/css/dashboard.css | 2 +- .../components/top-repository/top-repository.directive.html | 4 ++-- .../js/components/top-repository/top-repository.directive.js | 4 +++- .../resources/js/components/user-log/user-log.directive.html | 2 +- static/resources/js/layout/dashboard/dashboard.controller.js | 3 ++- static/resources/js/layout/index/index.controller.js | 5 ++++- views/dashboard.htm | 4 ++-- views/index.htm | 2 +- 8 files changed, 16 insertions(+), 10 deletions(-) diff --git a/static/resources/css/dashboard.css b/static/resources/css/dashboard.css index 1fe3e142b..eeee030c4 100644 --- a/static/resources/css/dashboard.css +++ b/static/resources/css/dashboard.css @@ -1,6 +1,6 @@ .up-section .up-table-pane { overflow-y: auto; - height: 180px; + height: 220px; margin-top: -10px; } diff --git a/static/resources/js/components/top-repository/top-repository.directive.html b/static/resources/js/components/top-repository/top-repository.directive.html index 372aae120..601799587 100644 --- a/static/resources/js/components/top-repository/top-repository.directive.html +++ b/static/resources/js/components/top-repository/top-repository.directive.html @@ -1,6 +1,6 @@
-
+
@@ -9,7 +9,7 @@
// 'repository_name' | tr //
-
+
diff --git a/static/resources/js/components/top-repository/top-repository.directive.js b/static/resources/js/components/top-repository/top-repository.directive.js index 549c967c3..12a099a2c 100644 --- a/static/resources/js/components/top-repository/top-repository.directive.js +++ b/static/resources/js/components/top-repository/top-repository.directive.js @@ -30,7 +30,9 @@ 'restrict': 'E', 'templateUrl': '/static/resources/js/components/top-repository/top-repository.directive.html', 'controller': TopRepositoryController, - 'scope' : true, + 'scope' : { + 'customBodyHeight': '=' + }, 'controllerAs': 'vm', 'bindToController': true }; diff --git a/static/resources/js/components/user-log/user-log.directive.html b/static/resources/js/components/user-log/user-log.directive.html index 43f5e1207..3b093a2d7 100644 --- a/static/resources/js/components/user-log/user-log.directive.html +++ b/static/resources/js/components/user-log/user-log.directive.html @@ -9,7 +9,7 @@
-
+
diff --git a/static/resources/js/layout/dashboard/dashboard.controller.js b/static/resources/js/layout/dashboard/dashboard.controller.js index 4beb06f1c..90386b9fa 100644 --- a/static/resources/js/layout/dashboard/dashboard.controller.js +++ b/static/resources/js/layout/dashboard/dashboard.controller.js @@ -9,7 +9,8 @@ DashboardController.$inject = ['$scope']; function DashboardController($scope) { - + var vm = this; + vm.customBodyHeight = {'height': '165px'}; } })(); \ No newline at end of file diff --git a/static/resources/js/layout/index/index.controller.js b/static/resources/js/layout/index/index.controller.js index b0c5e18b3..fee10c856 100644 --- a/static/resources/js/layout/index/index.controller.js +++ b/static/resources/js/layout/index/index.controller.js @@ -14,8 +14,11 @@ $scope.subsSection = 32; $scope.subsSubPane = 226; + var vm = this; - + + vm.customBodyHeight = {'height': '180px'}; + var indexDesc = $filter('tr')('index_desc', []); var indexDesc1 = $filter('tr')('index_desc_1', []); var indexDesc2 = $filter('tr')('index_desc_2', []); diff --git a/views/dashboard.htm b/views/dashboard.htm index 98137b7b0..be6b177bd 100644 --- a/views/dashboard.htm +++ b/views/dashboard.htm @@ -10,7 +10,7 @@
- +
@@ -20,7 +20,7 @@
- +
diff --git a/views/index.htm b/views/index.htm index fc8296781..e1095b6c6 100644 --- a/views/index.htm +++ b/views/index.htm @@ -52,7 +52,7 @@
- +
From a0e11f7a96d2728f53e7343a1700b5a885cafc72 Mon Sep 17 00:00:00 2001 From: kunw Date: Wed, 29 Jun 2016 14:19:20 +0800 Subject: [PATCH 05/21] update for some i18n words translations. --- .../replication/create-policy.directive.html | 2 +- .../list-replication.directive.html | 2 +- .../repository/list-repository.directive.html | 36 ++++++++++--------- .../replication.directive.html | 2 +- .../js/services/i18n/locale_messages_en-US.js | 4 +-- .../js/services/i18n/locale_messages_zh-CN.js | 3 +- 6 files changed, 26 insertions(+), 23 deletions(-) diff --git a/static/resources/js/components/replication/create-policy.directive.html b/static/resources/js/components/replication/create-policy.directive.html index 5decf7ccb..a78c7d069 100644 --- a/static/resources/js/components/replication/create-policy.directive.html +++ b/static/resources/js/components/replication/create-policy.directive.html @@ -92,7 +92,7 @@ diff --git a/static/resources/js/components/replication/list-replication.directive.html b/static/resources/js/components/replication/list-replication.directive.html index 07317480b..450823dab 100644 --- a/static/resources/js/components/replication/list-replication.directive.html +++ b/static/resources/js/components/replication/list-replication.directive.html @@ -28,7 +28,7 @@
- + diff --git a/static/resources/js/components/repository/list-repository.directive.html b/static/resources/js/components/repository/list-repository.directive.html index 4c623e09a..0996bfd6b 100644 --- a/static/resources/js/components/repository/list-repository.directive.html +++ b/static/resources/js/components/repository/list-repository.directive.html @@ -1,4 +1,4 @@ -
+
@@ -8,24 +8,26 @@
-
-

// 'no_repositories' | tr //

-
-
-
- -
-
\ No newline at end of file diff --git a/static/resources/js/components/system-management/replication.directive.html b/static/resources/js/components/system-management/replication.directive.html index ab12f1af9..3efa640c4 100644 --- a/static/resources/js/components/system-management/replication.directive.html +++ b/static/resources/js/components/system-management/replication.directive.html @@ -28,7 +28,7 @@

// 'no_replication_policies' | tr //

// 'no_replication_policies_add_new' | tr //

//r.name//
- + diff --git a/static/resources/js/services/i18n/locale_messages_en-US.js b/static/resources/js/services/i18n/locale_messages_en-US.js index 1827ada4d..838fc5150 100644 --- a/static/resources/js/services/i18n/locale_messages_en-US.js +++ b/static/resources/js/services/i18n/locale_messages_en-US.js @@ -154,8 +154,8 @@ var locale_messages = { 'logs' : 'Logs', 'enabled': 'Enabled', 'disabled': 'Disabled', - 'no_replication_policies': 'No replication policies, add new replication policy.', - 'no_replications': 'No replications.', + 'no_replication_policies_add_new': 'No replication policies, add new replication policy.', + 'no_replication_policies': 'No replication policies.', 'no_replication_jobs': 'No replication jobs.', 'no_destinations': 'No destinations, add new destination.', 'name_is_required': 'Name is required.', diff --git a/static/resources/js/services/i18n/locale_messages_zh-CN.js b/static/resources/js/services/i18n/locale_messages_zh-CN.js index c6dff75e7..6dc856aeb 100644 --- a/static/resources/js/services/i18n/locale_messages_zh-CN.js +++ b/static/resources/js/services/i18n/locale_messages_zh-CN.js @@ -152,7 +152,8 @@ var locale_messages = { 'logs': '日志', 'enabled': '启用', 'disabled': '停用', - 'no_replication_policies': '没有复制策略,请新增复制策略。', + 'no_replication_policies_add_new': '没有复制策略,请新增复制策略。', + 'no_replication_policies': '没有复制策略。', 'no_replications': '没有复制策略。', 'no_replication_jobs': '没有复制任务。', 'no_destinations': '没有目标设置,请新增目标。', From b86a839f66d36191de99dfe58bfde76bf1611eee Mon Sep 17 00:00:00 2001 From: yhua123 Date: Wed, 29 Jun 2016 15:30:13 +0800 Subject: [PATCH 06/21] Update add-project.directive.js Close pop input bar when create project success. --- .../resources/js/components/project/add-project.directive.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/static/resources/js/components/project/add-project.directive.js b/static/resources/js/components/project/add-project.directive.js index bc849e7ce..d3d73b654 100644 --- a/static/resources/js/components/project/add-project.directive.js +++ b/static/resources/js/components/project/add-project.directive.js @@ -36,6 +36,7 @@ $scope.$emit('addedSuccess', true); vm.hasError = false; vm.errorMessage = ''; + vm.isOpen = false; } function addProjectFailed(data, status) { @@ -91,4 +92,4 @@ } } -})(); \ No newline at end of file +})(); From 91a4a5297779f8540ad50223a4c720d448e20e5c Mon Sep 17 00:00:00 2001 From: yhua123 Date: Wed, 29 Jun 2016 15:33:28 +0800 Subject: [PATCH 07/21] Update add-project-member.directive.js Close pop input bar after add project member success. --- .../components/project-member/add-project-member.directive.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/static/resources/js/components/project-member/add-project-member.directive.js b/static/resources/js/components/project-member/add-project-member.directive.js index 2a79db0a3..eddbdd638 100644 --- a/static/resources/js/components/project-member/add-project-member.directive.js +++ b/static/resources/js/components/project-member/add-project-member.directive.js @@ -49,6 +49,7 @@ function addProjectMemberComplete(data, status, header) { console.log('addProjectMemberComplete: status:' + status + ', data:' + data); vm.reload(); + vm.isOpen = false; } function addProjectMemberFailed(data, status, headers) { @@ -97,4 +98,4 @@ } } -})(); \ No newline at end of file +})(); From 60e15cc1fae8e4c776b4bd1135057385c5509251 Mon Sep 17 00:00:00 2001 From: Wenkai Yin Date: Wed, 29 Jun 2016 16:58:16 +0800 Subject: [PATCH 08/21] filter jobs by time --- api/replication_job.go | 30 +++++++++++++++++++++++++++++- dao/replication_job.go | 18 +++++++++++++++++- 2 files changed, 46 insertions(+), 2 deletions(-) diff --git a/api/replication_job.go b/api/replication_job.go index 380018c7c..7ced0bcac 100644 --- a/api/replication_job.go +++ b/api/replication_job.go @@ -21,6 +21,7 @@ import ( "io/ioutil" "net/http" "strconv" + "time" "github.com/vmware/harbor/dao" "github.com/vmware/harbor/models" @@ -59,6 +60,7 @@ func (ra *RepJobAPI) Prepare() { func (ra *RepJobAPI) List() { var policyID int64 var repository, status string + var startTime, endTime *time.Time var err error policyIDStr := ra.GetString("policy_id") @@ -69,10 +71,36 @@ func (ra *RepJobAPI) List() { } } + endTimeStr := ra.GetString("end_time") + if len(endTimeStr) != 0 { + i, err := strconv.ParseInt(endTimeStr, 10, 64) + if err != nil { + ra.CustomAbort(http.StatusBadRequest, "invalid end_time") + } + t := time.Unix(i, 0) + endTime = &t + } + + startTimeStr := ra.GetString("start_time") + if len(startTimeStr) != 0 { + i, err := strconv.ParseInt(startTimeStr, 10, 64) + if err != nil { + ra.CustomAbort(http.StatusBadRequest, "invalid start_time") + } + t := time.Unix(i, 0) + startTime = &t + } + + if startTime == nil && endTime == nil { + // if start_time and end_time are both null, list jobs of last 10 days + t := time.Now().UTC().AddDate(0, 0, -10) + startTime = &t + } + repository = ra.GetString("repository") status = ra.GetString("status") - jobs, err := dao.FilterRepJobs(policyID, repository, status) + jobs, err := dao.FilterRepJobs(policyID, repository, status, startTime, endTime, 1000) if err != nil { 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") diff --git a/dao/replication_job.go b/dao/replication_job.go index 6de5af82d..f0d4782cd 100644 --- a/dao/replication_job.go +++ b/dao/replication_job.go @@ -311,7 +311,8 @@ func GetRepJobByPolicy(policyID int64) ([]*models.RepJob, error) { } // FilterRepJobs filters jobs by repo and policy ID -func FilterRepJobs(policyID int64, repository, status string) ([]*models.RepJob, error) { +func FilterRepJobs(policyID int64, repository, status string, startTime, + endTime *time.Time, limit int) ([]*models.RepJob, error) { o := GetOrmer() qs := o.QueryTable(new(models.RepJob)) @@ -324,6 +325,21 @@ func FilterRepJobs(policyID int64, repository, status string) ([]*models.RepJob, if len(status) != 0 { qs = qs.Filter("Status__icontains", status) } + + if startTime != nil { + fmt.Printf("%v\n", startTime) + qs = qs.Filter("CreationTime__gte", startTime) + } + + if endTime != nil { + fmt.Printf("%v\n", endTime) + qs = qs.Filter("CreationTime__lte", endTime) + } + + if limit != 0 { + qs = qs.Limit(limit) + } + qs = qs.OrderBy("-CreationTime") var jobs []*models.RepJob From 23b026655cba0a00ff29273835c8639d405bca2d Mon Sep 17 00:00:00 2001 From: Wenkai Yin Date: Wed, 29 Jun 2016 17:10:17 +0800 Subject: [PATCH 09/21] handle repository deletion event in API --- api/repository.go | 8 ++++++++ service/notification.go | 12 +----------- 2 files changed, 9 insertions(+), 11 deletions(-) diff --git a/api/repository.go b/api/repository.go index 57564e61f..f19d99a5e 100644 --- a/api/repository.go +++ b/api/repository.go @@ -154,7 +154,15 @@ func (ra *RepositoryAPI) Delete() { ra.CustomAbort(http.StatusInternalServerError, "internal error") } log.Infof("delete tag: %s %s", repoName, t) + go TriggerReplicationByRepository(repoName, []string{t}, models.RepOpDelete) } + + go func() { + log.Debug("refreshing catalog cache") + if err := cache.RefreshCatalogCache(); err != nil { + log.Errorf("error occurred while refresh catalog cache: %v", err) + } + }() } type tag struct { diff --git a/service/notification.go b/service/notification.go index cc1585bc3..77ade18d3 100644 --- a/service/notification.go +++ b/service/notification.go @@ -73,7 +73,7 @@ func (n *NotificationHandler) Post() { log.Errorf("failed to add access log: %v", err) } }() - if action == "push" || action == "delete" { + if action == "push" { go func() { if err := cache.RefreshCatalogCache(); err != nil { log.Errorf("failed to refresh cache: %v", err) @@ -83,8 +83,6 @@ func (n *NotificationHandler) Post() { operation := "" if action == "push" { operation = models.RepOpTransfer - } else { - operation = models.RepOpDelete } go api.TriggerReplicationByRepository(repository, []string{tag}, operation) @@ -96,14 +94,6 @@ func filterEvents(notification *models.Notification) ([]*models.Event, error) { events := []*models.Event{} for _, event := range notification.Events { - - //delete - // TODO add tag field - if event.Action == "delete" { - events = append(events, &event) - continue - } - isManifest, err := regexp.MatchString(manifestPattern, event.Target.MediaType) if err != nil { log.Errorf("failed to match the media type against pattern: %v", err) From cfb741c089c3fc34e08e6e0f0db141c6e4ea7c6c Mon Sep 17 00:00:00 2001 From: kunw Date: Wed, 29 Jun 2016 17:14:00 +0800 Subject: [PATCH 10/21] update for replication status and time filters. --- static/resources/css/replication.css | 4 ++ .../list-replication.directive.html | 46 ++++++++++++---- .../replication/list-replication.directive.js | 55 +++++++++++++++++-- .../js/services/i18n/locale_messages_en-US.js | 10 +++- .../js/services/i18n/locale_messages_zh-CN.js | 10 +++- .../services.list-replication-job.js | 5 +- 6 files changed, 111 insertions(+), 19 deletions(-) diff --git a/static/resources/css/replication.css b/static/resources/css/replication.css index e0f109296..e5dbd1b8a 100644 --- a/static/resources/css/replication.css +++ b/static/resources/css/replication.css @@ -73,4 +73,8 @@ .color-warning { color: #f0ad4e; +} + +.label-custom { + margin: 0 5px 0 10px; } \ No newline at end of file diff --git a/static/resources/js/components/replication/list-replication.directive.html b/static/resources/js/components/replication/list-replication.directive.html index 450823dab..69258a8a0 100644 --- a/static/resources/js/components/replication/list-replication.directive.html +++ b/static/resources/js/components/replication/list-replication.directive.html @@ -32,14 +32,14 @@ - - - - + + + - - - - - - + + + + +

// 'no_replications' | tr //

// 'no_replication_policies' | tr //

//r.name//
//r.name////r.description////r.target_name////r.start_time | dateL : 'YYYY-MM-DD HH:mm:ss'// + //r.description////r.target_name////r.start_time | dateL : 'YYYY-MM-DD HH:mm:ss'// // 'enabled' | tr // // 'disabled' | tr // +
@@ -66,6 +66,30 @@
+
+ +
+ +
+
+
+ +
+ + + + +
+
+
+ +
+ + + + +
+
@@ -94,11 +118,13 @@
//r.repository// //r.operation////r.creation_time | dateL : 'YYYY-MM-DD HH:mm:ss'////r.creation_time | dateL : 'YYYY-MM-DD HH:mm:ss'////r.update_time | dateL : 'YYYY-MM-DD HH:mm:ss'////r.status////r.creation_time | dateL : 'YYYY-MM-DD HH:mm:ss'////r.creation_time | dateL : 'YYYY-MM-DD HH:mm:ss'////r.update_time | dateL : 'YYYY-MM-DD HH:mm:ss'////r.status// + +
diff --git a/static/resources/js/components/replication/list-replication.directive.js b/static/resources/js/components/replication/list-replication.directive.js index 0920a16f8..c4b05ff55 100644 --- a/static/resources/js/components/replication/list-replication.directive.js +++ b/static/resources/js/components/replication/list-replication.directive.js @@ -4,11 +4,27 @@ angular .module('harbor.replication') - .directive('listReplication', listReplication); - - ListReplicationController.$inject = ['$scope', 'getParameterByName', '$location', 'ListReplicationPolicyService', 'ToggleReplicationPolicyService', 'ListReplicationJobService', '$window', '$filter', 'trFilter']; + .directive('listReplication', listReplication) + .factory('jobStatus', jobStatus); + + jobStatus.inject = ['$filter', 'trFilter']; + function jobStatus($filter, trFilter) { + return function() { + return [ + {'key': 'all' , 'value': $filter('tr')('all')}, + {'key': 'pending', 'value': $filter('tr')('pending')}, + {'key': 'running', 'value': $filter('tr')('running')}, + {'key': 'error' , 'value': $filter('tr')('error')}, + {'key': 'stopped', 'value': $filter('tr')('stopped')}, + {'key': 'finished', 'value':$filter('tr')('finished')}, + {'key': 'canceled', 'value': $filter('tr')('canceled')} + ]; + } + } - function ListReplicationController($scope, getParameterByName, $location, ListReplicationPolicyService, ToggleReplicationPolicyService, ListReplicationJobService, $window, $filter, trFilter) { + ListReplicationController.$inject = ['$scope', 'getParameterByName', '$location', 'ListReplicationPolicyService', 'ToggleReplicationPolicyService', 'ListReplicationJobService', '$window', '$filter', 'trFilter', 'jobStatus']; + + function ListReplicationController($scope, getParameterByName, $location, ListReplicationPolicyService, ToggleReplicationPolicyService, ListReplicationJobService, $window, $filter, trFilter, jobStatus) { var vm = this; vm.sectionHeight = {'min-height': '1200px'}; @@ -36,6 +52,9 @@ vm.retrievePolicy(); vm.refreshPending = false; + vm.jobStatus = jobStatus; + vm.currentStatus = vm.jobStatus()[0]; + function searchReplicationPolicy() { vm.refreshPending = true; vm.retrievePolicy(); @@ -54,7 +73,8 @@ } function retrieveJob(policyId) { - ListReplicationJobService(policyId, vm.replicationJobName) + var status = (vm.currentStatus.key === 'all' ? '' : vm.currentStatus.key); + ListReplicationJobService(policyId, vm.replicationJobName, status) .success(listReplicationJobSuccess) .error(listReplicationJobFailed); } @@ -70,6 +90,18 @@ function listReplicationJobSuccess(data, status) { vm.replicationJobs = data || []; + angular.forEach(vm.replicationJobs, function(item) { + for(var key in item) { + var value = item[key] + switch(key) { + case 'operation': + case 'status': + item[key] = $filter('tr')(value); + default: + break; + } + } + }); vm.refreshPending = false; } @@ -193,6 +225,19 @@ ctrl.retrieveJob($(this).attr('policy_id')); ctrl.lastPolicyId = $(this).attr('policy_id'); } + + element.find('.datetimepicker').datetimepicker({ + locale: 'en-US', + ignoreReadonly: true, + format: 'L', + showClear: true + }); + element.find('#fromDatePicker').on('blur', function(){ + ctrl.pickUp({'key': 'fromDate', 'value': $(this).val()}); + }); + element.find('#toDatePicker').on('blur', function(){ + ctrl.pickUp({'key': 'toDate', 'value': $(this).val()}); + }); } } diff --git a/static/resources/js/services/i18n/locale_messages_en-US.js b/static/resources/js/services/i18n/locale_messages_en-US.js index 838fc5150..ad8710ca9 100644 --- a/static/resources/js/services/i18n/locale_messages_en-US.js +++ b/static/resources/js/services/i18n/locale_messages_en-US.js @@ -200,5 +200,13 @@ var locale_messages = { 'delete_repo': 'Delete Repo', 'download_log': 'Download Logs', 'edit': 'Edit', - 'delete': 'Delete' + 'delete': 'Delete', + 'transfer': 'Transfer', + 'all': 'All', + 'pending': 'Pending', + 'running': 'Running', + 'finished': 'Finished', + 'canceled': 'Canceled', + 'stopped': 'Stopped', + 'error': 'Error' }; \ No newline at end of file diff --git a/static/resources/js/services/i18n/locale_messages_zh-CN.js b/static/resources/js/services/i18n/locale_messages_zh-CN.js index 6dc856aeb..62cb1bfeb 100644 --- a/static/resources/js/services/i18n/locale_messages_zh-CN.js +++ b/static/resources/js/services/i18n/locale_messages_zh-CN.js @@ -200,5 +200,13 @@ var locale_messages = { 'delete_repo': '删除镜像仓库', 'download_log': '下载日志', 'edit': '修改', - 'delete': '删除' + 'delete': '删除', + 'all': '全部', + 'transfer': '传输', + 'pending': '挂起', + 'running': '进行中', + 'finished': '已完成', + 'canceled': '取消', + 'stopped': '停止', + 'error': '错误' }; \ No newline at end of file diff --git a/static/resources/js/services/replication-job/services.list-replication-job.js b/static/resources/js/services/replication-job/services.list-replication-job.js index 6a5bbd34d..375427eb1 100644 --- a/static/resources/js/services/replication-job/services.list-replication-job.js +++ b/static/resources/js/services/replication-job/services.list-replication-job.js @@ -12,12 +12,13 @@ return listReplicationJob; - function listReplicationJob(policyId, repository) { + function listReplicationJob(policyId, repository, status) { return $http .get('/api/jobs/replication/', { 'params': { 'policy_id': policyId, - 'repository': repository + 'repository': repository, + 'status': status } }); } From 0c7f2142ff8eea9f2f18fb00f9e33dabd9f9ee5b Mon Sep 17 00:00:00 2001 From: Wenkai Yin Date: Wed, 29 Jun 2016 17:15:49 +0800 Subject: [PATCH 11/21] pass go test --- dao/dao_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dao/dao_test.go b/dao/dao_test.go index e0c8a0fbb..60377bc49 100644 --- a/dao/dao_test.go +++ b/dao/dao_test.go @@ -1141,7 +1141,7 @@ func TestDeleteRepJob(t *testing.T) { } func TestFilterRepJobs(t *testing.T) { - jobs, err := FilterRepJobs(policyID, "", "") + jobs, err := FilterRepJobs(policyID, "", "", nil, nil, 1000) if err != nil { log.Errorf("Error occured in FilterRepJobs: %v, policy ID: %d", err, policyID) return From 2f9ace7eebbb9b7c5d2e9693618e06bcbe87e61c Mon Sep 17 00:00:00 2001 From: Wenkai Yin Date: Wed, 29 Jun 2016 17:52:35 +0800 Subject: [PATCH 12/21] update --- api/repository.go | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/api/repository.go b/api/repository.go index f19d99a5e..5f3883246 100644 --- a/api/repository.go +++ b/api/repository.go @@ -144,6 +144,18 @@ func (ra *RepositoryAPI) Delete() { tags = append(tags, tag) } + project := "" + if strings.Contains(repoName, "/") { + project = repoName[0:strings.LastIndex(repoName, "/")] + } + user, _, ok := ra.Ctx.Request.BasicAuth() + if !ok { + user, err = ra.getUsername() + if err != nil { + log.Errorf("failed to get user: %v", err) + } + } + for _, t := range tags { if err := rc.DeleteTag(t); err != nil { if regErr, ok := err.(*registry_error.Error); ok { @@ -155,6 +167,13 @@ func (ra *RepositoryAPI) Delete() { } log.Infof("delete tag: %s %s", repoName, t) go TriggerReplicationByRepository(repoName, []string{t}, models.RepOpDelete) + + go func() { + + if err := dao.AccessLog(user, project, repoName, t, "delete"); err != nil { + log.Errorf("failed to add access log: %v", err) + } + }() } go func() { From 90e025febe200f2b0c8d34fd430b66d632fd378f Mon Sep 17 00:00:00 2001 From: kunw Date: Wed, 29 Jun 2016 17:56:38 +0800 Subject: [PATCH 13/21] update for switching projects when no projects. --- .../js/components/details/retrieve-projects.directive.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/static/resources/js/components/details/retrieve-projects.directive.js b/static/resources/js/components/details/retrieve-projects.directive.js index d69da1a7e..71d7c0802 100644 --- a/static/resources/js/components/details/retrieve-projects.directive.js +++ b/static/resources/js/components/details/retrieve-projects.directive.js @@ -53,7 +53,11 @@ console.log('vm.projects is undefined, load public projects.'); } - vm.selectedProject = vm.projects[0]; + if(angular.isArray(vm.projects) && vm.projects.length > 0) { + vm.selectedProject = vm.projects[0]; + }else{ + $window.location.href = '/project'; + } if(getParameterByName('project_id', $location.absUrl())){ angular.forEach(vm.projects, function(value, index) { @@ -64,8 +68,8 @@ } $location.search('project_id', vm.selectedProject.project_id); - vm.checkProjectMember(vm.selectedProject.project_id); + vm.checkProjectMember(vm.selectedProject.project_id); vm.resultCount = vm.projects.length; $scope.$watch('vm.filterInput', function(current, origin) { From f31d469758a53e0118f9f960b209eaa22d0b56b3 Mon Sep 17 00:00:00 2001 From: Wenkai Yin Date: Wed, 29 Jun 2016 18:09:47 +0800 Subject: [PATCH 14/21] pass insecure env to ui --- Deploy/prepare | 3 ++- Deploy/templates/ui/env | 1 + api/config.go | 35 +++++++++++++++++++++++++++++++++++ api/repository.go | 6 ++---- api/target.go | 4 +--- 5 files changed, 41 insertions(+), 8 deletions(-) create mode 100644 api/config.go diff --git a/Deploy/prepare b/Deploy/prepare index d861f58e3..5a301ce61 100755 --- a/Deploy/prepare +++ b/Deploy/prepare @@ -98,7 +98,8 @@ render(os.path.join(templates_dir, "ui", "env"), ldap_url=ldap_url, ldap_basedn=ldap_basedn, self_registration=self_registration, - ui_secret=ui_secret) + ui_secret=ui_secret, + verify_remote_cert=verify_remote_cert) render(os.path.join(templates_dir, "ui", "app.conf"), ui_conf, diff --git a/Deploy/templates/ui/env b/Deploy/templates/ui/env index 5098fa1a7..a77452f41 100644 --- a/Deploy/templates/ui/env +++ b/Deploy/templates/ui/env @@ -17,3 +17,4 @@ LOG_LEVEL=debug GODEBUG=netdns=cgo EXT_ENDPOINT=$ui_url TOKEN_URL=http://ui +VERIFY_REMOTE_CERT=$verify_remote_cert diff --git a/api/config.go b/api/config.go new file mode 100644 index 000000000..aceb1c1b3 --- /dev/null +++ b/api/config.go @@ -0,0 +1,35 @@ +/* + 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 api + +import ( + "os" +) + +var ( + Insecure bool +) + +func init() { + verifyRemoteCert := os.Getenv("VERIFY_REMOTE_CERT") + if len(verifyRemoteCert) == 0 { + verifyRemoteCert = "on" + } + + if verifyRemoteCert == "off" { + Insecure = false + } +} diff --git a/api/repository.go b/api/repository.go index b4b36c090..254349901 100644 --- a/api/repository.go +++ b/api/repository.go @@ -255,12 +255,10 @@ func (ra *RepositoryAPI) GetManifests() { func (ra *RepositoryAPI) initRepositoryClient(repoName string) (r *registry.Repository, err error) { endpoint := os.Getenv("REGISTRY_URL") - // TODO read variable from config file - insecure := true username, password, ok := ra.Ctx.Request.BasicAuth() if ok { - return newRepositoryClient(endpoint, insecure, username, password, + return newRepositoryClient(endpoint, Insecure, username, password, repoName, "repository", repoName, "pull", "push", "*") } @@ -269,7 +267,7 @@ func (ra *RepositoryAPI) initRepositoryClient(repoName string) (r *registry.Repo return nil, err } - return cache.NewRepositoryClient(endpoint, insecure, username, repoName, + return cache.NewRepositoryClient(endpoint, Insecure, username, repoName, "repository", repoName, "pull", "push", "*") } diff --git a/api/target.go b/api/target.go index 2271b142f..e22d91b62 100644 --- a/api/target.go +++ b/api/target.go @@ -92,9 +92,7 @@ func (t *TargetAPI) Ping() { password = t.GetString("password") } - // TODO read variable from config file - insecure := true - registry, err := newRegistryClient(endpoint, insecure, username, password, + registry, err := newRegistryClient(endpoint, Insecure, username, password, "", "", "") if err != nil { // timeout, dns resolve error, connection refused, etc. From d7f78502d7ef26781dd78afc94334c1f5460e710 Mon Sep 17 00:00:00 2001 From: Wenkai Yin Date: Wed, 29 Jun 2016 18:14:50 +0800 Subject: [PATCH 15/21] pass govet --- api/repository.go | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/api/repository.go b/api/repository.go index 5f3883246..a303ff11e 100644 --- a/api/repository.go +++ b/api/repository.go @@ -168,12 +168,11 @@ func (ra *RepositoryAPI) Delete() { log.Infof("delete tag: %s %s", repoName, t) go TriggerReplicationByRepository(repoName, []string{t}, models.RepOpDelete) - go func() { - - if err := dao.AccessLog(user, project, repoName, t, "delete"); err != nil { + go func(tag string) { + if err := dao.AccessLog(user, project, repoName, tag, "delete"); err != nil { log.Errorf("failed to add access log: %v", err) } - }() + }(t) } go func() { From 5f75156e1dc3b7448b16d40adcac0ee4a6c34909 Mon Sep 17 00:00:00 2001 From: Wenkai Yin Date: Wed, 29 Jun 2016 18:20:53 +0800 Subject: [PATCH 16/21] pass golint --- api/config.go | 1 + 1 file changed, 1 insertion(+) diff --git a/api/config.go b/api/config.go index aceb1c1b3..3857fc408 100644 --- a/api/config.go +++ b/api/config.go @@ -20,6 +20,7 @@ import ( ) var ( + // Insecure represents whether verify cert if connecting to a https server. Insecure bool ) From 92193f34566d4e64beb0039dd7dc8ac6e5329962 Mon Sep 17 00:00:00 2001 From: Wenkai Yin Date: Wed, 29 Jun 2016 18:52:24 +0800 Subject: [PATCH 17/21] update --- api/base.go | 12 ++++++++++++ api/config.go | 36 ------------------------------------ api/repository.go | 4 ++-- api/target.go | 2 +- 4 files changed, 15 insertions(+), 39 deletions(-) delete mode 100644 api/config.go diff --git a/api/base.go b/api/base.go index 7fac8e9b8..72f9da50b 100644 --- a/api/base.go +++ b/api/base.go @@ -19,6 +19,7 @@ import ( "encoding/json" "fmt" "net/http" + "os" "strconv" "github.com/astaxie/beego/validation" @@ -136,3 +137,14 @@ func (b *BaseAPI) GetIDFromURL() int64 { return id } + +func getIsInsecure() bool { + insecure := false + + verifyRemoteCert := os.Getenv("VERIFY_REMOTE_CERT") + if verifyRemoteCert == "off" { + insecure = true + } + + return insecure +} diff --git a/api/config.go b/api/config.go deleted file mode 100644 index 3857fc408..000000000 --- a/api/config.go +++ /dev/null @@ -1,36 +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 api - -import ( - "os" -) - -var ( - // Insecure represents whether verify cert if connecting to a https server. - Insecure bool -) - -func init() { - verifyRemoteCert := os.Getenv("VERIFY_REMOTE_CERT") - if len(verifyRemoteCert) == 0 { - verifyRemoteCert = "on" - } - - if verifyRemoteCert == "off" { - Insecure = false - } -} diff --git a/api/repository.go b/api/repository.go index 254349901..c6f7ee9a2 100644 --- a/api/repository.go +++ b/api/repository.go @@ -258,7 +258,7 @@ func (ra *RepositoryAPI) initRepositoryClient(repoName string) (r *registry.Repo username, password, ok := ra.Ctx.Request.BasicAuth() if ok { - return newRepositoryClient(endpoint, Insecure, username, password, + return newRepositoryClient(endpoint, getIsInsecure(), username, password, repoName, "repository", repoName, "pull", "push", "*") } @@ -267,7 +267,7 @@ func (ra *RepositoryAPI) initRepositoryClient(repoName string) (r *registry.Repo return nil, err } - return cache.NewRepositoryClient(endpoint, Insecure, username, repoName, + return cache.NewRepositoryClient(endpoint, getIsInsecure(), username, repoName, "repository", repoName, "pull", "push", "*") } diff --git a/api/target.go b/api/target.go index e22d91b62..c89f1a3fd 100644 --- a/api/target.go +++ b/api/target.go @@ -92,7 +92,7 @@ func (t *TargetAPI) Ping() { password = t.GetString("password") } - registry, err := newRegistryClient(endpoint, Insecure, username, password, + registry, err := newRegistryClient(endpoint, getIsInsecure(), username, password, "", "", "") if err != nil { // timeout, dns resolve error, connection refused, etc. From a24974d62975ddab34d339a00aedcb680f91414b Mon Sep 17 00:00:00 2001 From: yhua123 Date: Wed, 29 Jun 2016 20:42:35 +0800 Subject: [PATCH 18/21] Update summary.directive.html add link on dashboard --- .../resources/js/components/summary/summary.directive.html | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/static/resources/js/components/summary/summary.directive.html b/static/resources/js/components/summary/summary.directive.html index 6d22e3e71..bcb1a87b3 100644 --- a/static/resources/js/components/summary/summary.directive.html +++ b/static/resources/js/components/summary/summary.directive.html @@ -1,4 +1,7 @@ - +
-
// key | tr //:
//value//
+
// key | tr //:
//value//
+
// key | tr //:
//value//
+
// key | tr //:
//value//
+
// key | tr //:
//value//
From dbb35067a6ad9af02f0f2d1078d62a6e67414a2d Mon Sep 17 00:00:00 2001 From: kunw Date: Wed, 29 Jun 2016 20:47:32 +0800 Subject: [PATCH 19/21] update for i18n set cookie by server-side. --- controllers/base.go | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/controllers/base.go b/controllers/base.go index 0225efa11..4ce094eab 100644 --- a/controllers/base.go +++ b/controllers/base.go @@ -55,16 +55,23 @@ func (b *BaseController) Prepare() { b.SetSession("Lang", lang) lang = sessionLang.(string) } else { - lang = defaultLang + al := b.Ctx.Request.Header.Get("Accept-Language") + if len(al) > 4 { + al = al[:5] // Only compare first 5 letters. + if i18n.IsExist(al) { + lang = al + } + } } } - b.SetSession("Lang", lang) - if _, exist := supportLanguages[lang]; !exist { //Check if support the request language. lang = defaultLang //Set default language if not supported. } + b.Ctx.SetCookie("language", lang, 0, "/") + b.SetSession("Lang", lang) + curLang := langType{ Lang: lang, } From 0d7d34c059ce21b9e3a82a7580992b4b5eaf845f Mon Sep 17 00:00:00 2001 From: kunw Date: Wed, 29 Jun 2016 20:48:45 +0800 Subject: [PATCH 20/21] update for updating policies when existing target. --- .../replication/create-policy.directive.js | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/static/resources/js/components/replication/create-policy.directive.js b/static/resources/js/components/replication/create-policy.directive.js index 304ccdfd4..c7c7efdda 100644 --- a/static/resources/js/components/replication/create-policy.directive.js +++ b/static/resources/js/components/replication/create-policy.directive.js @@ -69,7 +69,7 @@ $scope.$watch('vm.targetId', function(current) { if(current) { - vm1.selection.id = current; + vm1.selection.id = current || vm.destinations[0].id; } }); @@ -125,15 +125,19 @@ function saveOrUpdateDestination() { var target = { + 'name' : vm1.name, 'endpoint': vm1.endpoint, 'username': vm1.username, 'password': vm1.password }; - if(vm.checkedAddTarget) { - target.name = vm1.name; + + if(vm.checkedAddTarget){ CreateDestinationService(target.name, target.endpoint, target.username, target.password) .success(createDestinationSuccess) .error(createDestinationFailed); + }else{ + vm.policy.targetId = vm1.selection.id; + saveOrUpdatePolicy(); } } @@ -154,7 +158,10 @@ function update(policy) { vm.policy = policy; - saveOrUpdateDestination(); + if(vm.targetEditable) { + vm.policy.targetId = vm1.selection.id; + saveOrUpdatePolicy(); + } } function pingDestination() { From e9a7e4da6135ae042ee0deafc1b59a996ecdcfd7 Mon Sep 17 00:00:00 2001 From: kunw Date: Wed, 29 Jun 2016 20:49:15 +0800 Subject: [PATCH 21/21] update for disabling button when target is in used. --- .../system-management/create-destination.directive.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/static/resources/js/components/system-management/create-destination.directive.html b/static/resources/js/components/system-management/create-destination.directive.html index 9ba5229ed..1fcf7070a 100644 --- a/static/resources/js/components/system-management/create-destination.directive.html +++ b/static/resources/js/components/system-management/create-destination.directive.html @@ -53,7 +53,7 @@