mirror of
https://github.com/goharbor/harbor.git
synced 2025-02-02 04:51:22 +01:00
Merge remote-tracking branch 'upstream/dev' into dev
This commit is contained in:
commit
7f55520b27
@ -67,6 +67,9 @@ You can update or remove a member by clicking the icon on the right.
|
||||
|
||||
##Replicating images
|
||||
If you are a system administrator, you can replicate images to a remote registry, which is called destination in Harbor. Only Harbor instance is supported as a destination for now.
|
||||
|
||||
**Note:** The replication feature is incompatible between Harbor instance before version 0.3.5(included) and after version 0.3.5.
|
||||
|
||||
Click "Add New Policy" on the "Replication" tab, fill the necessary fields and click "OK", a policy for this project will be created. If "Enable" is chosen, the project will be replicated to the remote immediately, and when a new repository is pushed to this project or an existing repository is deleted from this project, the same operation will also be replicated to the destination.
|
||||
|
||||
![browse project](img/new_create_policy.png)
|
||||
@ -169,4 +172,4 @@ $ docker-compose start
|
||||
|
||||
Option "--dry-run" will print the progress without removing any data.
|
||||
|
||||
About the details of GC, please see [GC](https://github.com/docker/distribution/blob/master/docs/garbage-collection.md).
|
||||
About the details of GC, please see [GC](https://github.com/docker/docker.github.io/blob/master/registry/garbage-collection.md).
|
@ -1,7 +0,0 @@
|
||||
# Logrotate configuartion file for docker.
|
||||
|
||||
/var/log/docker/*/*.log {
|
||||
rotate 100
|
||||
size 10M
|
||||
copytruncate
|
||||
}
|
26
make/common/log/rotate.sh
Executable file
26
make/common/log/rotate.sh
Executable file
@ -0,0 +1,26 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
echo "Log rotate starting..."
|
||||
|
||||
#The logs n days before will be compressed.
|
||||
n=14
|
||||
path=/var/log/docker
|
||||
|
||||
list=""
|
||||
n_days_before=$(($(date +%s) - 3600*24*$n))
|
||||
for dir in $(ls $path | grep -v "tar.gz");
|
||||
do
|
||||
if [ $(date --date=$dir +%s) -lt $n_days_before ]
|
||||
then
|
||||
echo "$dir will be compressed"
|
||||
list="$list $dir"
|
||||
fi
|
||||
done
|
||||
|
||||
if [ -n "$list" ]
|
||||
then
|
||||
cd $path
|
||||
tar --remove-files -zcvf $(date -d @$n_days_before +%F)-.tar.gz $list
|
||||
fi
|
||||
|
||||
echo "Log rotate finished."
|
@ -1,8 +1,7 @@
|
||||
FROM library/photon:latest
|
||||
|
||||
# run logrotate hourly, disable imklog model, provides TCP/UDP syslog reception
|
||||
RUN tdnf install -y cronie rsyslog logrotate shadow\
|
||||
&& mv /etc/cron.daily/logrotate /etc/cron.hourly/ \
|
||||
RUN tdnf install -y cronie rsyslog shadow tar gzip \
|
||||
&& mkdir /etc/rsyslog.d/ \
|
||||
&& mkdir /var/spool/rsyslog \
|
||||
&& groupadd syslog \
|
||||
@ -10,15 +9,13 @@ RUN tdnf install -y cronie rsyslog logrotate shadow\
|
||||
|
||||
ADD make/common/log/rsyslog.conf /etc/rsyslog.conf
|
||||
|
||||
COPY make/photon/log/logrotate.conf.photon /etc/logrotate.conf
|
||||
|
||||
# logrotate configuration file for docker
|
||||
ADD make/common/log/logrotate_docker.conf /etc/logrotate.d/
|
||||
# rotate logs weekly
|
||||
# notes: file name cannot contain dot, or the script will not run
|
||||
ADD make/common/log/rotate.sh /etc/cron.weekly/rotate
|
||||
|
||||
# rsyslog configuration file for docker
|
||||
ADD make/common/log/rsyslog_docker.conf /etc/rsyslog.d/
|
||||
|
||||
|
||||
VOLUME /var/log/docker/
|
||||
|
||||
EXPOSE 514
|
||||
|
@ -1,35 +0,0 @@
|
||||
# see "man logrotate" for details
|
||||
# rotate log files weekly
|
||||
weekly
|
||||
|
||||
# keep 4 weeks worth of backlogs
|
||||
rotate 4
|
||||
|
||||
# create new (empty) log files after rotating old ones
|
||||
create
|
||||
|
||||
# use date as a suffix of the rotated file
|
||||
dateext
|
||||
|
||||
# uncomment this if you want your log files compressed
|
||||
#compress
|
||||
|
||||
# RPM packages drop log rotation information into this directory
|
||||
include /etc/logrotate.d
|
||||
|
||||
# no packages own wtmp and btmp -- we'll rotate them here
|
||||
#/var/log/wtmp {
|
||||
# monthly
|
||||
# create 0664 root utmp
|
||||
# minsize 1M
|
||||
# rotate 1
|
||||
#}
|
||||
|
||||
/var/log/btmp {
|
||||
missingok
|
||||
monthly
|
||||
create 0600 root utmp
|
||||
rotate 1
|
||||
}
|
||||
|
||||
# system-specific logs may be also be configured here.
|
@ -1,13 +1,12 @@
|
||||
FROM library/ubuntu:14.04
|
||||
|
||||
# run logrotate hourly, disable imklog model, provides TCP/UDP syslog reception
|
||||
RUN mv /etc/cron.daily/logrotate /etc/cron.hourly/ \
|
||||
&& rm /etc/rsyslog.d/* \
|
||||
&& rm /etc/rsyslog.conf
|
||||
RUN rm /etc/rsyslog.d/* && rm /etc/rsyslog.conf
|
||||
|
||||
ADD make/common/log/rsyslog.conf /etc/rsyslog.conf
|
||||
|
||||
# logrotate configuration file for docker
|
||||
ADD make/common/log/logrotate_docker.conf /etc/logrotate.d/
|
||||
# rotate logs weekly
|
||||
# notes: file name cannot contain dot, or the script will not run
|
||||
ADD make/common/log/rotate.sh /etc/cron.weekly/rotate
|
||||
|
||||
# rsyslog configuration file for docker
|
||||
ADD make/common/log/rsyslog_docker.conf /etc/rsyslog.d/
|
||||
|
@ -6,6 +6,7 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net/http/httptest"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
@ -90,6 +91,11 @@ func init() {
|
||||
|
||||
_ = updateInitPassword(1, "Harbor12345")
|
||||
|
||||
//syncRegistry
|
||||
if err := SyncRegistry(); err != nil {
|
||||
log.Fatalf("failed to sync repositories from registry: %v", err)
|
||||
}
|
||||
|
||||
//Init user Info
|
||||
admin = &usrInfo{adminName, adminPwd}
|
||||
unknownUsr = &usrInfo{"unknown", "unknown"}
|
||||
@ -119,8 +125,10 @@ func request(_sling *sling.Sling, acceptHeader string, authInfo ...usrInfo) (int
|
||||
//The response includes the project and repository list in a proper display order.
|
||||
//@param q Search parameter for project and repository name.
|
||||
//@return []Search
|
||||
//func (a testapi) SearchGet (q string) (apilib.Search, error) {
|
||||
func (a testapi) SearchGet(q string) (apilib.Search, error) {
|
||||
func (a testapi) SearchGet(q string, authInfo ...usrInfo) (int, apilib.Search, error) {
|
||||
var httpCode int
|
||||
var body []byte
|
||||
var err error
|
||||
|
||||
_sling := sling.New().Get(a.basePath)
|
||||
|
||||
@ -134,10 +142,15 @@ func (a testapi) SearchGet(q string) (apilib.Search, error) {
|
||||
|
||||
_sling = _sling.QueryStruct(&QueryParams{Query: q})
|
||||
|
||||
_, body, err := request(_sling, jsonAcceptHeader)
|
||||
if len(authInfo) > 0 {
|
||||
httpCode, body, err = request(_sling, jsonAcceptHeader, authInfo[0])
|
||||
} else {
|
||||
httpCode, body, err = request(_sling, jsonAcceptHeader)
|
||||
}
|
||||
|
||||
var successPayload = new(apilib.Search)
|
||||
err = json.Unmarshal(body, &successPayload)
|
||||
return *successPayload, err
|
||||
return httpCode, *successPayload, err
|
||||
}
|
||||
|
||||
//Create a new project.
|
||||
|
@ -21,7 +21,7 @@ func TestLogGet(t *testing.T) {
|
||||
project.ProjectName = "my_project"
|
||||
project.Public = 1
|
||||
now := fmt.Sprintf("%v", time.Now().Unix())
|
||||
statusCode, result, err := apiTest.LogGet(*admin, "0", now, "")
|
||||
statusCode, result, err := apiTest.LogGet(*admin, "0", now, "1000")
|
||||
if err != nil {
|
||||
t.Error("Error while get log information", err.Error())
|
||||
t.Log(err)
|
||||
@ -41,6 +41,7 @@ func TestLogGet(t *testing.T) {
|
||||
assert.Equal(int(201), reply, "Case 2: Project creation status should be 201")
|
||||
}
|
||||
//case 1: right parameters, expect the right output
|
||||
now = fmt.Sprintf("%v", time.Now().Unix())
|
||||
statusCode, result, err = apiTest.LogGet(*admin, "0", now, "1000")
|
||||
if err != nil {
|
||||
t.Error("Error while get log information", err.Error())
|
||||
@ -58,7 +59,6 @@ func TestLogGet(t *testing.T) {
|
||||
}
|
||||
fmt.Println("log ", result)
|
||||
//case 2: wrong format of start_time parameter, expect the wrong output
|
||||
now = fmt.Sprintf("%v", time.Now().Unix())
|
||||
statusCode, result, err = apiTest.LogGet(*admin, "ss", now, "3")
|
||||
if err != nil {
|
||||
t.Error("Error occured while get log information since the format of start_time parameter is not right.", err.Error())
|
||||
@ -77,7 +77,6 @@ func TestLogGet(t *testing.T) {
|
||||
}
|
||||
|
||||
//case 4: wrong format of lines parameter, expect the wrong output
|
||||
now = fmt.Sprintf("%v", time.Now().Unix())
|
||||
statusCode, result, err = apiTest.LogGet(*admin, "0", now, "s")
|
||||
if err != nil {
|
||||
t.Error("Error occured while get log information since the format of lines parameter is not right.", err.Error())
|
||||
@ -87,7 +86,6 @@ func TestLogGet(t *testing.T) {
|
||||
}
|
||||
|
||||
//case 5: wrong format of lines parameter, expect the wrong output
|
||||
now = fmt.Sprintf("%v", time.Now().Unix())
|
||||
statusCode, result, err = apiTest.LogGet(*admin, "0", now, "-5")
|
||||
if err != nil {
|
||||
t.Error("Error occured while get log information since the format of lines parameter is not right.", err.Error())
|
||||
@ -143,7 +141,6 @@ func TestLogGet(t *testing.T) {
|
||||
}
|
||||
|
||||
fmt.Printf("\n")
|
||||
|
||||
}
|
||||
|
||||
func getLog(result []apilib.AccessLog) (int, int) {
|
||||
|
@ -16,13 +16,14 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"github.com/vmware/harbor/src/common/api"
|
||||
"github.com/vmware/harbor/src/common/dao"
|
||||
"github.com/vmware/harbor/src/common/models"
|
||||
"github.com/vmware/harbor/src/common/utils/log"
|
||||
"github.com/vmware/harbor/src/common/api"
|
||||
)
|
||||
|
||||
// ProjectMemberAPI handles request to /api/projects/{}/members/{}
|
||||
@ -98,6 +99,11 @@ func (pma *ProjectMemberAPI) Get() {
|
||||
log.Errorf("Error occurred in GetUserProjectRoles, error: %v", err)
|
||||
pma.CustomAbort(http.StatusInternalServerError, "Internal error.")
|
||||
}
|
||||
|
||||
if len(roleList) == 0 {
|
||||
pma.CustomAbort(http.StatusNotFound, fmt.Sprintf("user %d is not a member of the project", pma.memberID))
|
||||
}
|
||||
|
||||
//return empty role list to indicate if a user is not a member
|
||||
result := make(map[string]interface{})
|
||||
user, err := dao.GetUser(models.User{UserID: pma.memberID})
|
||||
|
@ -4,9 +4,10 @@ import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"strconv"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/vmware/harbor/tests/apitests/apilib"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
func TestMemGet(t *testing.T) {
|
||||
@ -51,8 +52,19 @@ func TestMemGet(t *testing.T) {
|
||||
assert.Equal(int(404), httpStatusCode, "Case 3: Project creation status should be 404")
|
||||
}
|
||||
|
||||
fmt.Printf("\n")
|
||||
//------------case 4: Response Code=404, member does not exist-----------//
|
||||
fmt.Println("case 4: Response Code=404, member does not exist")
|
||||
projectID = "1"
|
||||
memberID := "10000"
|
||||
httpStatusCode, err = apiTest.GetMemByPIDUID(*admin, projectID, memberID)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to get member %s of project %s: %v", memberID, projectID, err)
|
||||
}
|
||||
|
||||
assert.Equal(int(404), httpStatusCode,
|
||||
fmt.Sprintf("response status code should be 404 other than %d", httpStatusCode))
|
||||
|
||||
fmt.Printf("\n")
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -20,10 +20,10 @@ import (
|
||||
"net/http"
|
||||
"regexp"
|
||||
|
||||
"github.com/vmware/harbor/src/common/api"
|
||||
"github.com/vmware/harbor/src/common/dao"
|
||||
"github.com/vmware/harbor/src/common/models"
|
||||
"github.com/vmware/harbor/src/common/utils/log"
|
||||
"github.com/vmware/harbor/src/common/api"
|
||||
|
||||
"strconv"
|
||||
"time"
|
||||
@ -192,7 +192,8 @@ func (p *ProjectAPI) Delete() {
|
||||
if err := dao.AddAccessLog(models.AccessLog{
|
||||
UserID: userID,
|
||||
ProjectID: p.projectID,
|
||||
RepoName: p.projectName,
|
||||
RepoName: p.projectName + "/",
|
||||
RepoTag: "N/A",
|
||||
Operation: "delete",
|
||||
}); err != nil {
|
||||
log.Errorf("failed to add access log: %v", err)
|
||||
|
@ -14,19 +14,29 @@ func TestSearch(t *testing.T) {
|
||||
|
||||
apiTest := newHarborAPI()
|
||||
var result apilib.Search
|
||||
result, err := apiTest.SearchGet("library")
|
||||
//fmt.Printf("%+v\n", result)
|
||||
|
||||
//-------------case 1 : Response Code = 200, Not sysAdmin --------------//
|
||||
httpStatusCode, result, err := apiTest.SearchGet("library")
|
||||
if err != nil {
|
||||
t.Error("Error while search project or repository", err.Error())
|
||||
t.Log(err)
|
||||
} else {
|
||||
assert.Equal(result.Projects[0].Id, int64(1), "Project id should be equal")
|
||||
assert.Equal(result.Projects[0].Name, "library", "Project name should be library")
|
||||
assert.Equal(result.Projects[0].Public, int32(1), "Project public status should be 1 (true)")
|
||||
//t.Log(result)
|
||||
assert.Equal(int(200), httpStatusCode, "httpStatusCode should be 200")
|
||||
assert.Equal(int64(1), result.Projects[0].Id, "Project id should be equal")
|
||||
assert.Equal("library", result.Projects[0].Name, "Project name should be library")
|
||||
assert.Equal(int32(1), result.Projects[0].Public, "Project public status should be 1 (true)")
|
||||
}
|
||||
|
||||
//--------case 2 : Response Code = 200, sysAdmin and search repo--------//
|
||||
httpStatusCode, result, err = apiTest.SearchGet("docker", *admin)
|
||||
if err != nil {
|
||||
t.Error("Error while search project or repository", err.Error())
|
||||
t.Log(err)
|
||||
} else {
|
||||
assert.Equal(int(200), httpStatusCode, "httpStatusCode should be 200")
|
||||
assert.Equal("library", result.Repositories[0].ProjectName, "Project name should be library")
|
||||
assert.Equal("library/docker", result.Repositories[0].RepositoryName, "Repository name should be library/docker")
|
||||
assert.Equal(int32(1), result.Repositories[0].ProjectPublic, "Project public status should be 1 (true)")
|
||||
}
|
||||
//if result.Response.StatusCode != 200 {
|
||||
// t.Log(result.Response)
|
||||
//}
|
||||
|
||||
}
|
||||
|
@ -10,10 +10,6 @@ import (
|
||||
)
|
||||
|
||||
func TestStatisticGet(t *testing.T) {
|
||||
if err := SyncRegistry(); err != nil {
|
||||
t.Fatalf("failed to sync repositories from registry: %v", err)
|
||||
}
|
||||
|
||||
fmt.Println("Testing Statistic API")
|
||||
assert := assert.New(t)
|
||||
|
||||
|
@ -1,9 +1,5 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// AccountSettingController handles request to /account_setting
|
||||
type AccountSettingController struct {
|
||||
BaseController
|
||||
@ -11,8 +7,14 @@ type AccountSettingController struct {
|
||||
|
||||
// Get renders the account settings page
|
||||
func (asc *AccountSettingController) Get() {
|
||||
if asc.AuthMode != "db_auth" {
|
||||
asc.CustomAbort(http.StatusForbidden, "")
|
||||
var isAdminForLdap bool
|
||||
sessionUserID, ok := asc.GetSession("userId").(int)
|
||||
if ok && sessionUserID == 1 {
|
||||
isAdminForLdap = true
|
||||
}
|
||||
if asc.AuthMode == "db_auth" || isAdminForLdap {
|
||||
asc.Forward("page_title_account_setting", "account-settings.htm")
|
||||
} else {
|
||||
asc.Redirect("/dashboard", 302)
|
||||
}
|
||||
asc.Forward("page_title_account_setting", "account-settings.htm")
|
||||
}
|
||||
|
@ -1,5 +1,10 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"github.com/vmware/harbor/src/common/dao"
|
||||
"github.com/vmware/harbor/src/common/utils/log"
|
||||
)
|
||||
|
||||
// AdminOptionController handles requests to /admin_option
|
||||
type AdminOptionController struct {
|
||||
BaseController
|
||||
@ -7,5 +12,16 @@ type AdminOptionController struct {
|
||||
|
||||
// Get renders the admin options page
|
||||
func (aoc *AdminOptionController) Get() {
|
||||
aoc.Forward("page_title_admin_option", "admin-options.htm")
|
||||
sessionUserID, ok := aoc.GetSession("userId").(int)
|
||||
if ok {
|
||||
isAdmin, err := dao.IsAdminRole(sessionUserID)
|
||||
if err != nil {
|
||||
log.Errorf("Error occurred in IsAdminRole: %v", err)
|
||||
}
|
||||
if isAdmin {
|
||||
aoc.Forward("page_title_admin_option", "admin-options.htm")
|
||||
return
|
||||
}
|
||||
}
|
||||
aoc.Redirect("/dashboard", 302)
|
||||
}
|
||||
|
@ -8,10 +8,10 @@ import (
|
||||
|
||||
"github.com/astaxie/beego"
|
||||
"github.com/beego/i18n"
|
||||
"github.com/vmware/harbor/src/ui/auth"
|
||||
"github.com/vmware/harbor/src/common/dao"
|
||||
"github.com/vmware/harbor/src/common/models"
|
||||
"github.com/vmware/harbor/src/common/utils/log"
|
||||
"github.com/vmware/harbor/src/ui/auth"
|
||||
)
|
||||
|
||||
// BaseController wraps common methods such as i18n support, forward, which can be leveraged by other UI render controllers.
|
||||
|
@ -1,18 +1,20 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// ChangePasswordController handles request to /change_password
|
||||
type ChangePasswordController struct {
|
||||
BaseController
|
||||
}
|
||||
|
||||
// Get renders the change password page
|
||||
func (asc *ChangePasswordController) Get() {
|
||||
if asc.AuthMode != "db_auth" {
|
||||
asc.CustomAbort(http.StatusForbidden, "")
|
||||
func (cpc *ChangePasswordController) Get() {
|
||||
var isAdminForLdap bool
|
||||
sessionUserID, ok := cpc.GetSession("userId").(int)
|
||||
if ok && sessionUserID == 1 {
|
||||
isAdminForLdap = true
|
||||
}
|
||||
if cpc.AuthMode == "db_auth" || isAdminForLdap {
|
||||
cpc.Forward("page_title_change_password", "change-password.htm")
|
||||
} else {
|
||||
cpc.Redirect("/dashboard", 302)
|
||||
}
|
||||
asc.Forward("page_title_change_password", "change-password.htm")
|
||||
}
|
||||
|
@ -113,19 +113,16 @@ func TestMain(t *testing.T) {
|
||||
w = httptest.NewRecorder()
|
||||
beego.BeeApp.Handlers.ServeHTTP(w, r)
|
||||
assert.Equal(int(200), w.Code, "'/account_setting' httpStatusCode should be 200")
|
||||
assert.Equal(true, strings.Contains(fmt.Sprintf("%s", w.Body), "<title>page_title_account_setting</title>"), "http respond should have '<title>page_title_account_setting</title>'")
|
||||
|
||||
r, _ = http.NewRequest("GET", "/change_password", nil)
|
||||
w = httptest.NewRecorder()
|
||||
beego.BeeApp.Handlers.ServeHTTP(w, r)
|
||||
assert.Equal(int(200), w.Code, "'/change_password' httpStatusCode should be 200")
|
||||
assert.Equal(true, strings.Contains(fmt.Sprintf("%s", w.Body), "<title>page_title_change_password</title>"), "http respond should have '<title>page_title_change_password</title>'")
|
||||
|
||||
r, _ = http.NewRequest("GET", "/admin_option", nil)
|
||||
w = httptest.NewRecorder()
|
||||
beego.BeeApp.Handlers.ServeHTTP(w, r)
|
||||
assert.Equal(int(200), w.Code, "'/admin_option' httpStatusCode should be 200")
|
||||
assert.Equal(true, strings.Contains(fmt.Sprintf("%s", w.Body), "<title>page_title_admin_option</title>"), "http respond should have '<title>page_title_admin_option</title>'")
|
||||
assert.Equal(int(302), w.Code, "'/admin_option' httpStatusCode should be 302")
|
||||
|
||||
r, _ = http.NewRequest("GET", "/forgot_password", nil)
|
||||
w = httptest.NewRecorder()
|
||||
|
@ -19,6 +19,8 @@ func (omc *OptionalMenuController) Get() {
|
||||
|
||||
var hasLoggedIn bool
|
||||
var allowAddNew bool
|
||||
|
||||
var isAdminForLdap bool
|
||||
var allowSettingAccount bool
|
||||
|
||||
if sessionUserID != nil {
|
||||
@ -35,7 +37,11 @@ func (omc *OptionalMenuController) Get() {
|
||||
}
|
||||
omc.Data["Username"] = u.Username
|
||||
|
||||
if omc.AuthMode == "db_auth" {
|
||||
if userID == 1 {
|
||||
isAdminForLdap = true
|
||||
}
|
||||
|
||||
if omc.AuthMode == "db_auth" || isAdminForLdap {
|
||||
allowSettingAccount = true
|
||||
}
|
||||
|
||||
|
@ -88,7 +88,6 @@
|
||||
|
||||
|
||||
function getProjectSuccess(response) {
|
||||
|
||||
var partialProjects = response.data || [];
|
||||
for(var i in partialProjects) {
|
||||
vm.projects.push(partialProjects[i]);
|
||||
@ -114,11 +113,13 @@
|
||||
}
|
||||
}
|
||||
|
||||
$location.search('project_id', vm.selectedProject.project_id);
|
||||
vm.checkProjectMember(vm.selectedProject.project_id);
|
||||
|
||||
if(vm.selectedProject) {
|
||||
$location.search('project_id', vm.selectedProject.project_id);
|
||||
vm.checkProjectMember(vm.selectedProject.project_id);
|
||||
}
|
||||
|
||||
vm.resultCount = vm.projects.length;
|
||||
|
||||
|
||||
$scope.$watch('vm.filterInput', function(current, origin) {
|
||||
vm.resultCount = $filter('name')(vm.projects, vm.filterInput, 'name').length;
|
||||
});
|
||||
|
@ -19,9 +19,9 @@
|
||||
.module('harbor.repository')
|
||||
.directive('listRepository', listRepository);
|
||||
|
||||
ListRepositoryController.$inject = ['$scope', 'ListRepositoryService', 'DeleteRepositoryService', '$filter', 'trFilter', '$location', 'getParameterByName'];
|
||||
ListRepositoryController.$inject = ['$scope', 'ListRepositoryService', 'DeleteRepositoryService', '$filter', 'trFilter', '$location', 'getParameterByName', '$window'];
|
||||
|
||||
function ListRepositoryController($scope, ListRepositoryService, DeleteRepositoryService, $filter, trFilter, $location, getParameterByName) {
|
||||
function ListRepositoryController($scope, ListRepositoryService, DeleteRepositoryService, $filter, trFilter, $location, getParameterByName, $window) {
|
||||
|
||||
$scope.subsTabPane = 30;
|
||||
|
||||
@ -104,7 +104,23 @@
|
||||
}
|
||||
|
||||
function getRepositoryFailed(response) {
|
||||
console.log('Failed to list repositories:' + response);
|
||||
var errorMessage = '';
|
||||
if(response.status === 404) {
|
||||
errorMessage = $filter('tr')('project_does_not_exist');
|
||||
}else{
|
||||
errorMessage = $filter('tr')('failed_to_get_project');
|
||||
}
|
||||
$scope.$emit('modalTitle', $filter('tr')('error'));
|
||||
$scope.$emit('modalMessage', errorMessage);
|
||||
var emitInfo = {
|
||||
'confirmOnly': true,
|
||||
'contentType': 'text/html',
|
||||
'action' : function() {
|
||||
$window.location.href = '/dashboard';
|
||||
}
|
||||
};
|
||||
$scope.$emit('raiseInfo', emitInfo);
|
||||
console.log('Failed to list repositories:' + response.data);
|
||||
}
|
||||
|
||||
function searchRepo() {
|
||||
|
@ -32,7 +32,7 @@
|
||||
</tr>
|
||||
<tr ng-if="vm.integratedLogs.length > 0" ng-repeat="t in vm.integratedLogs">
|
||||
<td width="18%">//t.username//</td>
|
||||
<td width="28%"><a href="javascript:void(0);" ng-click="vm.gotoLog(t.project_id, t.username)">//t.repo_name//</a></td>
|
||||
<td width="28%"><a href="javascript:void(0);" ng-click="vm.gotoRepo(t.project_id, t.repo_name)">//t.repo_name//</a></td>
|
||||
<td width="15%">//t.repo_tag//</td>
|
||||
<td width="14%">//t.operation//</td>
|
||||
<td width="25%">//t.op_time | dateL : 'YYYY-MM-DD HH:mm:ss'//</td>
|
||||
|
@ -29,7 +29,7 @@
|
||||
.success(listIntegratedLogSuccess)
|
||||
.error(listIntegratedLogFailed);
|
||||
|
||||
vm.gotoLog = gotoLog;
|
||||
vm.gotoRepo = gotoRepo;
|
||||
|
||||
function listIntegratedLogSuccess(data) {
|
||||
vm.integratedLogs = data || [];
|
||||
@ -42,8 +42,8 @@
|
||||
console.log('Failed to get user logs:' + data);
|
||||
}
|
||||
|
||||
function gotoLog(projectId, username) {
|
||||
$window.location.href = '/repository#/logs?project_id=' + projectId + '#' + encodeURIComponent(username);
|
||||
function gotoRepo(projectId, repoName) {
|
||||
$window.location.href = '/repository#/repositories?project_id=' + projectId + '#' + encodeURIComponent(repoName);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -31,7 +31,7 @@
|
||||
<th width="20%">// 'email' | tr //</th>
|
||||
<th width="35%">// 'registration_time' | tr //</th>
|
||||
<th width="15%">// 'administrator' | tr //</th>
|
||||
<th width="20%">// 'operation' | tr //</th>
|
||||
<th width="20%" ng-if="vm.authMode === 'db_auth'">// 'operation' | tr //</th>
|
||||
</thead>
|
||||
</table>
|
||||
</div>
|
||||
@ -46,7 +46,7 @@
|
||||
<td width="15%">
|
||||
<toggle-admin current-user="vm.currentUser" has-admin-role="u.has_admin_role" user-id="//u.user_id//"></toggle-admin>
|
||||
</td>
|
||||
<td width="20%">
|
||||
<td width="20%" ng-if="vm.authMode === 'db_auth'">
|
||||
<a ng-if="vm.currentUser.user_id != u.user_id" href="javascript:void(0)" ng-click="vm.confirmToDelete(u.user_id, u.username)"><span class="glyphicon glyphicon-trash"></span></a>
|
||||
</td>
|
||||
</tr>
|
||||
|
@ -98,6 +98,9 @@
|
||||
'restrict': 'E',
|
||||
'templateUrl': '/static/resources/js/components/user/list-user.directive.html',
|
||||
'link': link,
|
||||
'scope': {
|
||||
'authMode': '@'
|
||||
},
|
||||
'controller': ListUserController,
|
||||
'controllerAs': 'vm',
|
||||
'bindToController': true
|
||||
|
@ -233,6 +233,7 @@ var locale_messages = {
|
||||
'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.',
|
||||
'failed_to_get_repo': 'Failed to get repositories.',
|
||||
'failed_to_get_tag': 'Failed to get tag.',
|
||||
'failed_to_get_log': 'Failed to get logs.',
|
||||
'failed_to_get_project': 'Failed to get projects.',
|
||||
@ -263,6 +264,7 @@ var locale_messages = {
|
||||
'failed_to_update_destination': 'Failed to update destination.',
|
||||
'failed_to_toggle_publicity_insuffient_permissions': 'Failed to toggle project publicity, insuffient permissions.',
|
||||
'failed_to_toggle_publicity': 'Failed to toggle project publicity.',
|
||||
'project_does_not_exist': 'Project does not exist.',
|
||||
'project_admin': 'Project Admin',
|
||||
'developer': 'Developer',
|
||||
'guest': 'Guest',
|
||||
|
@ -233,6 +233,7 @@ var locale_messages = {
|
||||
'failed_to_get_project_member': '无法获取当前项目成员。',
|
||||
'failed_to_delete_repo': '无法删除镜像仓库。',
|
||||
'failed_to_delete_repo_insuffient_permissions': '无法删除镜像仓库,权限不足。',
|
||||
'failed_to_get_repo': '获取镜像仓库数据失败。',
|
||||
'failed_to_get_tag': '获取标签数据失败。',
|
||||
'failed_to_get_log': '获取日志数据失败。',
|
||||
'failed_to_get_project': '获取项目数据失败。',
|
||||
@ -263,6 +264,7 @@ var locale_messages = {
|
||||
'failed_to_update_destination': '修改目标失败。',
|
||||
'failed_to_toggle_publicity_insuffient_permissions': '切换项目公开性失败,权限不足。',
|
||||
'failed_to_toggle_publicity': '切换项目公开性失败。',
|
||||
'project_does_not_exist': '项目不存在。',
|
||||
'project_admin': '项目管理员',
|
||||
'developer': '开发人员',
|
||||
'guest': '访客',
|
||||
|
@ -24,7 +24,7 @@
|
||||
<span ng-if="vm.toggle">// 'system_management' | tr //</span>
|
||||
<a ng-if="!vm.toggle" href="#/destinations" class="title-color" ng-click="vm.toggleAdminOption({target: 'system_management'})">// 'system_management' | tr //</a>
|
||||
</h4>
|
||||
<list-user ng-if="vm.target === 'users'"></list-user>
|
||||
<list-user ng-if="vm.target === 'users'" auth-mode="{{ .AuthMode }}"></list-user>
|
||||
<system-management ng-if="vm.target === 'system_management'"></system-management>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -47,8 +47,8 @@
|
||||
<th width="15%">// 'repositories' | tr //</th>
|
||||
<th width="15%" ng-if="!vm.isPublic">// 'role' | tr //</th>
|
||||
<th width="20%">// 'creation_time' | tr //</th>
|
||||
<th width="15%">// 'publicity' | tr //</th>
|
||||
<th width="10%">// 'operation' | tr //</th>
|
||||
<th width="15%" ng-if="!vm.isPublic">// 'publicity' | tr //</th>
|
||||
<th width="10%" ng-if="!vm.isPublic">// 'operation' | tr //</th>
|
||||
</thead>
|
||||
</table>
|
||||
</div>
|
||||
@ -63,8 +63,8 @@
|
||||
<td width="15%">//p.repo_count//</td>
|
||||
<td width="15%" ng-if="vm.isPublic === 0">//vm.getProjectRole(p.current_user_role_id) | tr//</td>
|
||||
<td width="20%">//p.creation_time | dateL : 'YYYY-MM-DD HH:mm:ss'//</td>
|
||||
<td width="15%"><publicity-button is-public="p.public" project-id="p.project_id" role-id="//p.current_user_role_id//"></publicity-button></td>
|
||||
<td width="10%">
|
||||
<td width="15%" ng-if="!vm.isPublic"><publicity-button is-public="p.public" project-id="p.project_id" role-id="//p.current_user_role_id//"></publicity-button></td>
|
||||
<td width="10%" ng-if="!vm.isPublic">
|
||||
<a ng-if="p.current_user_role_id == 1" href="javascript:void(0)" ng-click="vm.confirmToDelete(p.project_id, p.name)"><span class="glyphicon glyphicon-trash"></span></a>
|
||||
</td>
|
||||
</tr>
|
||||
|
7
tools/ova/deps/docker-compose-1.7.1/install.sh
Executable file
7
tools/ova/deps/docker-compose-1.7.1/install.sh
Executable file
@ -0,0 +1,7 @@
|
||||
#!/bin/bash
|
||||
|
||||
echo "docker-compose version 1.7.1"
|
||||
cd "$( dirname "${BASH_SOURCE[0]}" )"
|
||||
cp ./docker-compose-Linux-x86_64 /usr/local/bin/docker-compose
|
||||
chmod +x /usr/local/bin/docker-compose
|
||||
|
15
tools/ova/script/app_post_install.sh
Executable file
15
tools/ova/script/app_post_install.sh
Executable file
@ -0,0 +1,15 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
tdnf install -y docker
|
||||
systemctl enable docker.service
|
||||
|
||||
mkdir -p /var/log/harbor
|
||||
|
||||
echo "Downloading harbor..."
|
||||
wget -O /ova.tar.gz http://10.117.5.62/ISV/appliancePackages/ova.tar.gz
|
||||
|
||||
echo "Downloading notice file..."
|
||||
wget -O /NOTICE_Harbor_0.4.1_Beta.txt http://10.117.5.62/ISV/appliancePackages/NOTICE_Harbor_0.4.1_Beta.txt
|
||||
|
||||
echo "Downloading license file..."
|
||||
wget -O /LICENSE_Harbor_0.4.1_Beta_100216.txt http://10.117.5.62/ISV/appliancePackages/LICENSE_Harbor_0.4.1_Beta_100216.txt
|
54
tools/ova/script/common.sh
Executable file
54
tools/ova/script/common.sh
Executable file
@ -0,0 +1,54 @@
|
||||
#!/bin/bash
|
||||
|
||||
#Shut down Harbor
|
||||
function down {
|
||||
base_dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
||||
docker-compose -f $base_dir/../harbor/docker-compose*.yml down
|
||||
}
|
||||
|
||||
#Start Harbor
|
||||
function up {
|
||||
base_dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
||||
$base_dir/start_harbor.sh
|
||||
}
|
||||
|
||||
#Configure Harbor
|
||||
function configure {
|
||||
base_dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
||||
$base_dir/config.sh
|
||||
}
|
||||
|
||||
#Garbage collectoin
|
||||
function gc {
|
||||
echo "======================= $(date)====================="
|
||||
|
||||
#the registry image
|
||||
image=$1
|
||||
|
||||
base_dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
||||
|
||||
docker run --name gc --rm --volume /data/registry:/storage \
|
||||
--volume $base_dir/../harbor/common/config/registry/:/etc/registry/ \
|
||||
$image garbage-collect /etc/registry/config.yml
|
||||
|
||||
echo "===================================================="
|
||||
}
|
||||
|
||||
#Add rules to iptables
|
||||
function addIptableRules {
|
||||
iptables -A INPUT -p tcp --dport 5480 -j ACCEPT
|
||||
iptables -A INPUT -p tcp --dport 5488 -j ACCEPT
|
||||
iptables -A INPUT -p tcp --dport 5489 -j ACCEPT
|
||||
}
|
||||
|
||||
#Install docker-compose
|
||||
function installDockerCompose {
|
||||
base_dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
||||
$base_dir/../deps/docker-compose-1.7.1/install.sh
|
||||
}
|
||||
|
||||
#Load images
|
||||
function load {
|
||||
basedir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
||||
docker load -i $basedir/../harbor/harbor*.tgz
|
||||
}
|
88
tools/ova/script/config.sh
Executable file
88
tools/ova/script/config.sh
Executable file
@ -0,0 +1,88 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
attrs=(
|
||||
harbor_admin_password
|
||||
auth_mode
|
||||
ldap_url
|
||||
ldap_searchdn
|
||||
ldap_search_pwd
|
||||
ldap_basedn
|
||||
ldap_uid
|
||||
email_server
|
||||
email_server_port
|
||||
email_username
|
||||
email_password
|
||||
email_from
|
||||
email_ssl
|
||||
db_password
|
||||
verify_remote_cert
|
||||
)
|
||||
|
||||
base_dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )/../" && pwd )"
|
||||
|
||||
#The location of harbor.cfg
|
||||
cfg=$base_dir/harbor/harbor.cfg
|
||||
|
||||
#Format cert and key files
|
||||
function format {
|
||||
file=$1
|
||||
head=$(sed -rn 's/(-+[A-Za-z ]*-+)([^-]*)(-+[A-Za-z ]*-+)/\1/p' $file)
|
||||
body=$(sed -rn 's/(-+[A-Za-z ]*-+)([^-]*)(-+[A-Za-z ]*-+)/\2/p' $file)
|
||||
tail=$(sed -rn 's/(-+[A-Za-z ]*-+)([^-]*)(-+[A-Za-z ]*-+)/\3/p' $file)
|
||||
echo $head > $file
|
||||
echo $body | sed 's/\s\+/\n/g' >> $file
|
||||
echo $tail >> $file
|
||||
}
|
||||
|
||||
#Modify hostname
|
||||
ip=$(ip addr show eth0|grep "inet "|tr -s ' '|cut -d ' ' -f 3|cut -d '/' -f 1)
|
||||
if [ -n "$ip" ]
|
||||
then
|
||||
echo "Read IP address: [ IP - $ip ]"
|
||||
sed -i -r s/"hostname = .*"/"hostname = $ip"/ $cfg
|
||||
else
|
||||
echo "Failed to get the IP address"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
#Handle http/https
|
||||
protocal=http
|
||||
echo "Read attribute using ovfenv: [ ssl_cert ]"
|
||||
ssl_cert=$(ovfenv -k ssl_cert)
|
||||
echo "Read attribute using ovfenv: [ ssl_cert_key ]"
|
||||
ssl_cert_key=$(ovfenv -k ssl_cert_key)
|
||||
if [ -n "$ssl_cert" ] && [ -n "$ssl_cert_key" ]
|
||||
then
|
||||
echo "ssl_cert and ssl_cert_key are set, using HTTPS protocal"
|
||||
protocal=https
|
||||
sed -i -r s%"#?ui_url_protocol = .*"%"ui_url_protocol = $protocal"% $cfg
|
||||
mkdir -p /path/to
|
||||
echo $ssl_cert > /path/to/server.crt
|
||||
format /path/to/server.crt
|
||||
echo $ssl_cert_key > /path/to/server.key
|
||||
format /path/to/server.key
|
||||
else
|
||||
echo "ssl_cert and ssl_cert_key are not set, using HTTP protocal"
|
||||
fi
|
||||
|
||||
for attr in "${attrs[@]}"
|
||||
do
|
||||
echo "Read attribute using ovfenv: [ $attr ]"
|
||||
value=$(ovfenv -k $attr)
|
||||
|
||||
#ldap search password and email password can be null
|
||||
if [ -n "$value" ] || [ "$attr" = "ldap_search_pwd" ] \
|
||||
|| [ "$attr" = "email_password" ]
|
||||
then
|
||||
if [ "$attr" = ldap_search_pwd ] \
|
||||
|| [ "$attr" = email_password ] \
|
||||
|| [ "$attr" = db_password ] \
|
||||
|| [ "$attr" = harbor_admin_password ]
|
||||
then
|
||||
bs=$(echo $value | base64)
|
||||
#value={base64}$bs
|
||||
fi
|
||||
sed -i -r s%"#?$attr = .*"%"$attr = $value"% $cfg
|
||||
fi
|
||||
done
|
43
tools/ova/script/firstboot.sh
Executable file
43
tools/ova/script/firstboot.sh
Executable file
@ -0,0 +1,43 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
echo "======================= $(date)====================="
|
||||
|
||||
export PATH=$PATH:/usr/local/bin
|
||||
|
||||
base_dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
||||
source $base_dir/common.sh
|
||||
|
||||
#Reset root password
|
||||
value=$(ovfenv -k root_pwd)
|
||||
if [ -n "$value" ]
|
||||
then
|
||||
echo "Resetting root password..."
|
||||
printf "$value\n$value\n" | passwd root
|
||||
fi
|
||||
|
||||
#echo "Adding rules to iptables..."
|
||||
#addIptableRules
|
||||
|
||||
echo "Installing docker compose..."
|
||||
installDockerCompose
|
||||
|
||||
echo "Starting docker service..."
|
||||
systemctl start docker
|
||||
|
||||
echo "Uncompress Harbor offline instaler tar..."
|
||||
tar -zxvf $base_dir/../harbor-offline-installer*.tgz -C $base_dir/../
|
||||
|
||||
echo "Loading images..."
|
||||
load
|
||||
|
||||
#Configure Harbor
|
||||
echo "Configuring Harbor..."
|
||||
chmod 600 $base_dir/../harbor/harbor.cfg
|
||||
configure
|
||||
|
||||
#Start Harbor
|
||||
echo "Starting Harbor..."
|
||||
up
|
||||
|
||||
echo "===================================================="
|
31
tools/ova/script/start_harbor.sh
Executable file
31
tools/ova/script/start_harbor.sh
Executable file
@ -0,0 +1,31 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
workdir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
||||
cd $workdir/../harbor
|
||||
|
||||
echo "[Step 1]: preparing environment ..."
|
||||
./prepare
|
||||
|
||||
echo "[Step 2]: starting Harbor ..."
|
||||
docker-compose -f docker-compose*.yml up -d
|
||||
|
||||
protocol=http
|
||||
hostname=reg.mydomain.com
|
||||
|
||||
if [[ $(cat ./harbor.cfg) =~ ui_url_protocol[[:blank:]]*=[[:blank:]]*(https?) ]]
|
||||
then
|
||||
protocol=${BASH_REMATCH[1]}
|
||||
fi
|
||||
|
||||
if [[ $(grep 'hostname[[:blank:]]*=' ./harbor.cfg) =~ hostname[[:blank:]]*=[[:blank:]]*(.*) ]]
|
||||
then
|
||||
hostname=${BASH_REMATCH[1]}
|
||||
fi
|
||||
|
||||
echo $"
|
||||
----Harbor has been installed and started successfully.----
|
||||
|
||||
Now you should be able to visit the admin portal at ${protocol}://${hostname}.
|
||||
For more details, please visit https://github.com/vmware/harbor .
|
||||
"
|
37
tools/ova/script/subsequentboot.sh
Executable file
37
tools/ova/script/subsequentboot.sh
Executable file
@ -0,0 +1,37 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
echo "======================= $(date)====================="
|
||||
|
||||
export PATH=$PATH:/usr/local/bin
|
||||
|
||||
base_dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
||||
source $base_dir/common.sh
|
||||
|
||||
#echo "Adding rules to iptables..."
|
||||
#addIptableRules
|
||||
|
||||
#Stop Harbor
|
||||
echo "Shutting down Harbor..."
|
||||
down
|
||||
|
||||
#Garbage collection
|
||||
value=$(ovfenv -k gc_enabled)
|
||||
if [ "$value" = "true" ]
|
||||
then
|
||||
echo "GC enabled, starting garbage collection..."
|
||||
#If the registry contains no images, the gc will fail.
|
||||
#So append a true to avoid failure.
|
||||
gc registry:2.5.0 2>&1 >> /var/log/harbor/gc.log || true
|
||||
else
|
||||
echo "GC disabled, skip garbage collection"
|
||||
fi
|
||||
|
||||
#Configure Harbor
|
||||
echo "Configuring Harbor..."
|
||||
configure
|
||||
|
||||
#Start Harbor
|
||||
echo "Starting Harbor..."
|
||||
up
|
||||
|
||||
echo "===================================================="
|
Loading…
Reference in New Issue
Block a user