From 811401f40e8c7cca1d34e8daef06e8eeecbad772 Mon Sep 17 00:00:00 2001 From: kunw Date: Tue, 25 Oct 2016 12:44:27 +0800 Subject: [PATCH 1/3] Added volume info of UI. --- make/docker-compose.yml | 1 + src/ui/api/systeminfo.go | 62 +++++++++++++++++++ src/ui/router.go | 2 + .../optional-menu/optional-menu.directive.js | 36 ++++++++--- src/ui/static/resources/js/harbor.module.js | 1 + .../js/services/i18n/locale_messages_en-US.js | 1 + .../js/services/i18n/locale_messages_zh-CN.js | 1 + .../services.system-info.module.js | 20 ++++++ .../system-info/services.volume-info.js | 33 ++++++++++ src/ui/views/sections/script-include.htm | 3 + 10 files changed, 152 insertions(+), 8 deletions(-) create mode 100644 src/ui/api/systeminfo.go create mode 100644 src/ui/static/resources/js/services/system-info/services.system-info.module.js create mode 100644 src/ui/static/resources/js/services/system-info/services.volume-info.js diff --git a/make/docker-compose.yml b/make/docker-compose.yml index 1e7e81f71..fd7945edb 100644 --- a/make/docker-compose.yml +++ b/make/docker-compose.yml @@ -50,6 +50,7 @@ services: volumes: - ./common/config/ui/app.conf:/etc/ui/app.conf - ./common/config/ui/private_key.pem:/etc/ui/private_key.pem + - /data:/harbor_storage depends_on: - log logging: diff --git a/src/ui/api/systeminfo.go b/src/ui/api/systeminfo.go new file mode 100644 index 000000000..86f4e9673 --- /dev/null +++ b/src/ui/api/systeminfo.go @@ -0,0 +1,62 @@ +package api + +import ( + "net/http" + "path/filepath" + "syscall" + + "github.com/vmware/harbor/src/common/api" + "github.com/vmware/harbor/src/common/dao" + "github.com/vmware/harbor/src/common/utils/log" +) + +type SystemInfoApi struct { + api.BaseAPI + currentUserID int + isAdmin bool +} + +const harbor_storage_path = "/harbor_storage" + +type SystemInfo struct { + HarborStorage Storage `json:"harbor_storage"` +} + +type Storage struct { + Total uint64 `json:"total"` + Free uint64 `json:"free"` +} + +var systemInfo SystemInfo = SystemInfo{} + +func (sia *SystemInfoApi) Prepare() { + sia.currentUserID = sia.ValidateUser() + + var err error + sia.isAdmin, err = dao.IsAdminRole(sia.currentUserID) + if err != nil { + log.Errorf("Error occurred in IsAdminRole:%v", err) + sia.CustomAbort(http.StatusInternalServerError, "Internal error.") + } +} + +func (sia *SystemInfoApi) GetVolumeInfo() { + if !sia.isAdmin { + sia.RenderError(http.StatusForbidden, "User does not have admin role.") + return + } + var stat syscall.Statfs_t + err := syscall.Statfs(filepath.Join("/", harbor_storage_path), &stat) + if err != nil { + log.Errorf("Error occurred in syscall.Statfs: %v", err) + sia.CustomAbort(http.StatusInternalServerError, "Internal error.") + return + } + storage := Storage{ + Total: stat.Blocks * uint64(stat.Bsize), + Free: stat.Bfree * uint64(stat.Bsize), + } + systemInfo.HarborStorage = storage + sia.Data["json"] = systemInfo + sia.ServeJSON() +} diff --git a/src/ui/router.go b/src/ui/router.go index 5dbab9bae..097ff39d8 100644 --- a/src/ui/router.go +++ b/src/ui/router.go @@ -84,6 +84,8 @@ func initRouters() { beego.Router("/api/users/:id/sysadmin", &api.UserAPI{}, "put:ToggleUserAdminRole") beego.Router("/api/repositories/top", &api.RepositoryAPI{}, "get:GetTopRepos") beego.Router("/api/logs", &api.LogAPI{}) + + beego.Router("/api/systeminfo/volumes", &api.SystemInfoApi{}, "get:GetVolumeInfo") //external service that hosted on harbor process: beego.Router("/service/notifications", &service.NotificationHandler{}) beego.Router("/service/token", &token.Handler{}) diff --git a/src/ui/static/resources/js/components/optional-menu/optional-menu.directive.js b/src/ui/static/resources/js/components/optional-menu/optional-menu.directive.js index 64b7121ef..c7bfda6cf 100644 --- a/src/ui/static/resources/js/components/optional-menu/optional-menu.directive.js +++ b/src/ui/static/resources/js/components/optional-menu/optional-menu.directive.js @@ -20,9 +20,9 @@ .module('harbor.optional.menu') .directive('optionalMenu', optionalMenu); - OptionalMenuController.$inject = ['$scope', '$window', 'I18nService', 'LogOutService', 'currentUser', '$timeout', 'trFilter', '$filter']; + OptionalMenuController.$inject = ['$scope', '$window', 'I18nService', 'LogOutService', 'currentUser', '$timeout', 'trFilter', '$filter', 'GetVolumeInfoService']; - function OptionalMenuController($scope, $window, I18nService, LogOutService, currentUser, $timeoutm, trFilter, $filter) { + function OptionalMenuController($scope, $window, I18nService, LogOutService, currentUser, $timeoutm, trFilter, $filter, GetVolumeInfoService) { var vm = this; vm.currentLanguage = I18nService().getCurrentLanguage(); @@ -55,16 +55,36 @@ function logOutFailed(data, status) { console.log('Failed to log out:' + data); } + + var raiseInfo = { + 'confirmOnly': true, + 'contentType': 'text/html', + 'action': function() {} + }; + function about() { $scope.$emit('modalTitle', $filter('tr')('about_harbor')); - $scope.$emit('modalMessage', $filter('tr')('current_version', [vm.version || 'Unknown'])); - var raiseInfo = { - 'confirmOnly': true, - 'contentType': 'text/html', - 'action': function() {} - }; + vm.modalMessage = $filter('tr')('current_version', [vm.version || 'Unknown']); + GetVolumeInfoService("data") + .then(getVolumeInfoSuccess, getVolumeInfoFailed); + + } + function getVolumeInfoSuccess(response) { + var storage = response.data; + vm.modalMessage += '
' + $filter('tr')('current_storage', + [toGigaBytes(storage['harbor_storage']['free']), toGigaBytes(storage['harbor_storage']['total'])]); + $scope.$emit('modalMessage', vm.modalMessage); + $scope.$emit('raiseInfo', raiseInfo); + + } + function getVolumeInfoFailed(response) { + $scope.$emit('modalMessage', vm.modalMessage); $scope.$emit('raiseInfo', raiseInfo); } + function toGigaBytes(val) { + return Math.round(val / (1024 * 1024 * 1024)); + } + } function optionalMenu() { diff --git a/src/ui/static/resources/js/harbor.module.js b/src/ui/static/resources/js/harbor.module.js index 64dec5ea4..345dfd0e5 100644 --- a/src/ui/static/resources/js/harbor.module.js +++ b/src/ui/static/resources/js/harbor.module.js @@ -42,6 +42,7 @@ 'harbor.services.replication.policy', 'harbor.services.replication.job', 'harbor.services.destination', + 'harbor.services.system.info', 'harbor.summary', 'harbor.user.log', 'harbor.top.repository', diff --git a/src/ui/static/resources/js/services/i18n/locale_messages_en-US.js b/src/ui/static/resources/js/services/i18n/locale_messages_en-US.js index 0ac5025c5..ca2654210 100644 --- a/src/ui/static/resources/js/services/i18n/locale_messages_en-US.js +++ b/src/ui/static/resources/js/services/i18n/locale_messages_en-US.js @@ -230,6 +230,7 @@ var locale_messages = { 'about': 'About', 'about_harbor': 'About Harbor', 'current_version': '  $0', + 'current_storage': '  $0 GB available of $1 GB.', 'failed_to_get_project_member': 'Failed to get current project member.', 'failed_to_delete_repo': 'Failed to delete repository. ', 'failed_to_delete_repo_insuffient_permissions': 'Failed to delete repository, insuffient permissions.', diff --git a/src/ui/static/resources/js/services/i18n/locale_messages_zh-CN.js b/src/ui/static/resources/js/services/i18n/locale_messages_zh-CN.js index a896ba848..c91801d5f 100644 --- a/src/ui/static/resources/js/services/i18n/locale_messages_zh-CN.js +++ b/src/ui/static/resources/js/services/i18n/locale_messages_zh-CN.js @@ -230,6 +230,7 @@ var locale_messages = { 'about': '关于', 'about_harbor': '关于 Harbor', 'current_version': '  $0', + 'current_storage': '  可用: $0 GB,总共: $1 GB。', 'failed_to_get_project_member': '无法获取当前项目成员。', 'failed_to_delete_repo': '无法删除镜像仓库。', 'failed_to_delete_repo_insuffient_permissions': '无法删除镜像仓库,权限不足。', diff --git a/src/ui/static/resources/js/services/system-info/services.system-info.module.js b/src/ui/static/resources/js/services/system-info/services.system-info.module.js new file mode 100644 index 000000000..cc38c1b2d --- /dev/null +++ b/src/ui/static/resources/js/services/system-info/services.system-info.module.js @@ -0,0 +1,20 @@ +/* + 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. +*/ +(function() { + 'use strict'; + + angular.module('harbor.services.system.info', []); + +})(); \ No newline at end of file diff --git a/src/ui/static/resources/js/services/system-info/services.volume-info.js b/src/ui/static/resources/js/services/system-info/services.volume-info.js new file mode 100644 index 000000000..545db5561 --- /dev/null +++ b/src/ui/static/resources/js/services/system-info/services.volume-info.js @@ -0,0 +1,33 @@ +/* + 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. +*/ +(function() { + 'use strict'; + + angular + .module('harbor.services.system.info') + .factory('GetVolumeInfoService', GetVolumeInfoService); + + GetVolumeInfoService.$inject = ['$http']; + + function GetVolumeInfoService($http) { + return getVolumeInfo; + + function getVolumeInfo(path) { + return $http + .get('/api/systeminfo/volumes'); + } + } + +})(); \ No newline at end of file diff --git a/src/ui/views/sections/script-include.htm b/src/ui/views/sections/script-include.htm index f07b60649..74805fc4b 100644 --- a/src/ui/views/sections/script-include.htm +++ b/src/ui/views/sections/script-include.htm @@ -110,6 +110,9 @@ + + + From 98a7c4adf6399b29434fc88c288e7059d85dbe28 Mon Sep 17 00:00:00 2001 From: kunw Date: Wed, 2 Nov 2016 12:50:53 +0800 Subject: [PATCH 2/3] Changes for golint checkings. --- src/ui/api/systeminfo.go | 28 +++++++++++++++++----------- src/ui/router.go | 2 +- 2 files changed, 18 insertions(+), 12 deletions(-) diff --git a/src/ui/api/systeminfo.go b/src/ui/api/systeminfo.go index 86f4e9673..637e47e47 100644 --- a/src/ui/api/systeminfo.go +++ b/src/ui/api/systeminfo.go @@ -10,26 +10,28 @@ import ( "github.com/vmware/harbor/src/common/utils/log" ) -type SystemInfoApi struct { +//SystemInfoAPI handle requests for getting system info /api/systeminfo +type SystemInfoAPI struct { api.BaseAPI currentUserID int isAdmin bool } -const harbor_storage_path = "/harbor_storage" +const harborStoragePath = "/harbor_storage" +//SystemInfo models for system info. type SystemInfo struct { HarborStorage Storage `json:"harbor_storage"` } +//Storage models for storage. type Storage struct { Total uint64 `json:"total"` Free uint64 `json:"free"` } -var systemInfo SystemInfo = SystemInfo{} - -func (sia *SystemInfoApi) Prepare() { +// Prepare for validating user if an admin. +func (sia *SystemInfoAPI) Prepare() { sia.currentUserID = sia.ValidateUser() var err error @@ -40,23 +42,27 @@ func (sia *SystemInfoApi) Prepare() { } } -func (sia *SystemInfoApi) GetVolumeInfo() { +// GetVolumeInfo gets specific volume storage info. +func (sia *SystemInfoAPI) GetVolumeInfo() { if !sia.isAdmin { sia.RenderError(http.StatusForbidden, "User does not have admin role.") return } var stat syscall.Statfs_t - err := syscall.Statfs(filepath.Join("/", harbor_storage_path), &stat) + err := syscall.Statfs(filepath.Join("/", harborStoragePath), &stat) if err != nil { log.Errorf("Error occurred in syscall.Statfs: %v", err) sia.CustomAbort(http.StatusInternalServerError, "Internal error.") return } - storage := Storage{ - Total: stat.Blocks * uint64(stat.Bsize), - Free: stat.Bfree * uint64(stat.Bsize), + + systemInfo := SystemInfo{ + HarborStorage: Storage{ + Total: stat.Blocks * uint64(stat.Bsize), + Free: stat.Bfree * uint64(stat.Bsize), + }, } - systemInfo.HarborStorage = storage + sia.Data["json"] = systemInfo sia.ServeJSON() } diff --git a/src/ui/router.go b/src/ui/router.go index 097ff39d8..0f5245d92 100644 --- a/src/ui/router.go +++ b/src/ui/router.go @@ -85,7 +85,7 @@ func initRouters() { beego.Router("/api/repositories/top", &api.RepositoryAPI{}, "get:GetTopRepos") beego.Router("/api/logs", &api.LogAPI{}) - beego.Router("/api/systeminfo/volumes", &api.SystemInfoApi{}, "get:GetVolumeInfo") + beego.Router("/api/systeminfo/volumes", &api.SystemInfoAPI{}, "get:GetVolumeInfo") //external service that hosted on harbor process: beego.Router("/service/notifications", &service.NotificationHandler{}) beego.Router("/service/token", &token.Handler{}) From 51a23baeea2890e1919513443eee4fd3110a3f2c Mon Sep 17 00:00:00 2001 From: kunw Date: Wed, 16 Nov 2016 14:35:10 +0800 Subject: [PATCH 3/3] Updates for changing JSON attribute name. --- src/ui/api/systeminfo.go | 2 +- .../js/components/optional-menu/optional-menu.directive.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ui/api/systeminfo.go b/src/ui/api/systeminfo.go index 637e47e47..f3813f750 100644 --- a/src/ui/api/systeminfo.go +++ b/src/ui/api/systeminfo.go @@ -21,7 +21,7 @@ const harborStoragePath = "/harbor_storage" //SystemInfo models for system info. type SystemInfo struct { - HarborStorage Storage `json:"harbor_storage"` + HarborStorage Storage `json:"storage"` } //Storage models for storage. diff --git a/src/ui/static/resources/js/components/optional-menu/optional-menu.directive.js b/src/ui/static/resources/js/components/optional-menu/optional-menu.directive.js index 1b371aa40..534a49b98 100644 --- a/src/ui/static/resources/js/components/optional-menu/optional-menu.directive.js +++ b/src/ui/static/resources/js/components/optional-menu/optional-menu.directive.js @@ -70,7 +70,7 @@ function getVolumeInfoSuccess(response) { var storage = response.data; vm.modalMessage += '
' + $filter('tr')('current_storage', - [toGigaBytes(storage['harbor_storage']['free']), toGigaBytes(storage['harbor_storage']['total'])]); + [toGigaBytes(storage['storage']['free']), toGigaBytes(storage['storage']['total'])]); $scope.$emit('modalMessage', vm.modalMessage); $scope.$emit('raiseInfo', raiseInfo);