mirror of
https://github.com/goharbor/harbor.git
synced 2024-09-26 04:23:22 +02: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
|
##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.
|
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.
|
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)
|
![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.
|
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
|
FROM library/photon:latest
|
||||||
|
|
||||||
# run logrotate hourly, disable imklog model, provides TCP/UDP syslog reception
|
# run logrotate hourly, disable imklog model, provides TCP/UDP syslog reception
|
||||||
RUN tdnf install -y cronie rsyslog logrotate shadow\
|
RUN tdnf install -y cronie rsyslog shadow tar gzip \
|
||||||
&& mv /etc/cron.daily/logrotate /etc/cron.hourly/ \
|
|
||||||
&& mkdir /etc/rsyslog.d/ \
|
&& mkdir /etc/rsyslog.d/ \
|
||||||
&& mkdir /var/spool/rsyslog \
|
&& mkdir /var/spool/rsyslog \
|
||||||
&& groupadd syslog \
|
&& groupadd syslog \
|
||||||
@ -10,15 +9,13 @@ RUN tdnf install -y cronie rsyslog logrotate shadow\
|
|||||||
|
|
||||||
ADD make/common/log/rsyslog.conf /etc/rsyslog.conf
|
ADD make/common/log/rsyslog.conf /etc/rsyslog.conf
|
||||||
|
|
||||||
COPY make/photon/log/logrotate.conf.photon /etc/logrotate.conf
|
# rotate logs weekly
|
||||||
|
# notes: file name cannot contain dot, or the script will not run
|
||||||
# logrotate configuration file for docker
|
ADD make/common/log/rotate.sh /etc/cron.weekly/rotate
|
||||||
ADD make/common/log/logrotate_docker.conf /etc/logrotate.d/
|
|
||||||
|
|
||||||
# rsyslog configuration file for docker
|
# rsyslog configuration file for docker
|
||||||
ADD make/common/log/rsyslog_docker.conf /etc/rsyslog.d/
|
ADD make/common/log/rsyslog_docker.conf /etc/rsyslog.d/
|
||||||
|
|
||||||
|
|
||||||
VOLUME /var/log/docker/
|
VOLUME /var/log/docker/
|
||||||
|
|
||||||
EXPOSE 514
|
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
|
FROM library/ubuntu:14.04
|
||||||
|
|
||||||
# run logrotate hourly, disable imklog model, provides TCP/UDP syslog reception
|
RUN rm /etc/rsyslog.d/* && rm /etc/rsyslog.conf
|
||||||
RUN mv /etc/cron.daily/logrotate /etc/cron.hourly/ \
|
|
||||||
&& rm /etc/rsyslog.d/* \
|
|
||||||
&& rm /etc/rsyslog.conf
|
|
||||||
ADD make/common/log/rsyslog.conf /etc/rsyslog.conf
|
ADD make/common/log/rsyslog.conf /etc/rsyslog.conf
|
||||||
|
|
||||||
# logrotate configuration file for docker
|
# rotate logs weekly
|
||||||
ADD make/common/log/logrotate_docker.conf /etc/logrotate.d/
|
# 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
|
# rsyslog configuration file for docker
|
||||||
ADD make/common/log/rsyslog_docker.conf /etc/rsyslog.d/
|
ADD make/common/log/rsyslog_docker.conf /etc/rsyslog.d/
|
||||||
|
@ -6,6 +6,7 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
"log"
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"runtime"
|
"runtime"
|
||||||
@ -90,6 +91,11 @@ func init() {
|
|||||||
|
|
||||||
_ = updateInitPassword(1, "Harbor12345")
|
_ = updateInitPassword(1, "Harbor12345")
|
||||||
|
|
||||||
|
//syncRegistry
|
||||||
|
if err := SyncRegistry(); err != nil {
|
||||||
|
log.Fatalf("failed to sync repositories from registry: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
//Init user Info
|
//Init user Info
|
||||||
admin = &usrInfo{adminName, adminPwd}
|
admin = &usrInfo{adminName, adminPwd}
|
||||||
unknownUsr = &usrInfo{"unknown", "unknown"}
|
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.
|
//The response includes the project and repository list in a proper display order.
|
||||||
//@param q Search parameter for project and repository name.
|
//@param q Search parameter for project and repository name.
|
||||||
//@return []Search
|
//@return []Search
|
||||||
//func (a testapi) SearchGet (q string) (apilib.Search, error) {
|
func (a testapi) SearchGet(q string, authInfo ...usrInfo) (int, apilib.Search, error) {
|
||||||
func (a testapi) SearchGet(q string) (apilib.Search, error) {
|
var httpCode int
|
||||||
|
var body []byte
|
||||||
|
var err error
|
||||||
|
|
||||||
_sling := sling.New().Get(a.basePath)
|
_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})
|
_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)
|
var successPayload = new(apilib.Search)
|
||||||
err = json.Unmarshal(body, &successPayload)
|
err = json.Unmarshal(body, &successPayload)
|
||||||
return *successPayload, err
|
return httpCode, *successPayload, err
|
||||||
}
|
}
|
||||||
|
|
||||||
//Create a new project.
|
//Create a new project.
|
||||||
|
@ -21,7 +21,7 @@ func TestLogGet(t *testing.T) {
|
|||||||
project.ProjectName = "my_project"
|
project.ProjectName = "my_project"
|
||||||
project.Public = 1
|
project.Public = 1
|
||||||
now := fmt.Sprintf("%v", time.Now().Unix())
|
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 {
|
if err != nil {
|
||||||
t.Error("Error while get log information", err.Error())
|
t.Error("Error while get log information", err.Error())
|
||||||
t.Log(err)
|
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")
|
assert.Equal(int(201), reply, "Case 2: Project creation status should be 201")
|
||||||
}
|
}
|
||||||
//case 1: right parameters, expect the right output
|
//case 1: right parameters, expect the right output
|
||||||
|
now = fmt.Sprintf("%v", time.Now().Unix())
|
||||||
statusCode, result, err = apiTest.LogGet(*admin, "0", now, "1000")
|
statusCode, result, err = apiTest.LogGet(*admin, "0", now, "1000")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error("Error while get log information", err.Error())
|
t.Error("Error while get log information", err.Error())
|
||||||
@ -58,7 +59,6 @@ func TestLogGet(t *testing.T) {
|
|||||||
}
|
}
|
||||||
fmt.Println("log ", result)
|
fmt.Println("log ", result)
|
||||||
//case 2: wrong format of start_time parameter, expect the wrong output
|
//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")
|
statusCode, result, err = apiTest.LogGet(*admin, "ss", now, "3")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error("Error occured while get log information since the format of start_time parameter is not right.", err.Error())
|
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
|
//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")
|
statusCode, result, err = apiTest.LogGet(*admin, "0", now, "s")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error("Error occured while get log information since the format of lines parameter is not right.", err.Error())
|
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
|
//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")
|
statusCode, result, err = apiTest.LogGet(*admin, "0", now, "-5")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error("Error occured while get log information since the format of lines parameter is not right.", err.Error())
|
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")
|
fmt.Printf("\n")
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func getLog(result []apilib.AccessLog) (int, int) {
|
func getLog(result []apilib.AccessLog) (int, int) {
|
||||||
|
@ -16,13 +16,14 @@
|
|||||||
package api
|
package api
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/vmware/harbor/src/common/api"
|
||||||
"github.com/vmware/harbor/src/common/dao"
|
"github.com/vmware/harbor/src/common/dao"
|
||||||
"github.com/vmware/harbor/src/common/models"
|
"github.com/vmware/harbor/src/common/models"
|
||||||
"github.com/vmware/harbor/src/common/utils/log"
|
"github.com/vmware/harbor/src/common/utils/log"
|
||||||
"github.com/vmware/harbor/src/common/api"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// ProjectMemberAPI handles request to /api/projects/{}/members/{}
|
// ProjectMemberAPI handles request to /api/projects/{}/members/{}
|
||||||
@ -98,6 +99,11 @@ func (pma *ProjectMemberAPI) Get() {
|
|||||||
log.Errorf("Error occurred in GetUserProjectRoles, error: %v", err)
|
log.Errorf("Error occurred in GetUserProjectRoles, error: %v", err)
|
||||||
pma.CustomAbort(http.StatusInternalServerError, "Internal error.")
|
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
|
//return empty role list to indicate if a user is not a member
|
||||||
result := make(map[string]interface{})
|
result := make(map[string]interface{})
|
||||||
user, err := dao.GetUser(models.User{UserID: pma.memberID})
|
user, err := dao.GetUser(models.User{UserID: pma.memberID})
|
||||||
|
@ -4,9 +4,10 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"strconv"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/vmware/harbor/tests/apitests/apilib"
|
"github.com/vmware/harbor/tests/apitests/apilib"
|
||||||
"strconv"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestMemGet(t *testing.T) {
|
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")
|
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"
|
"net/http"
|
||||||
"regexp"
|
"regexp"
|
||||||
|
|
||||||
|
"github.com/vmware/harbor/src/common/api"
|
||||||
"github.com/vmware/harbor/src/common/dao"
|
"github.com/vmware/harbor/src/common/dao"
|
||||||
"github.com/vmware/harbor/src/common/models"
|
"github.com/vmware/harbor/src/common/models"
|
||||||
"github.com/vmware/harbor/src/common/utils/log"
|
"github.com/vmware/harbor/src/common/utils/log"
|
||||||
"github.com/vmware/harbor/src/common/api"
|
|
||||||
|
|
||||||
"strconv"
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
@ -192,7 +192,8 @@ func (p *ProjectAPI) Delete() {
|
|||||||
if err := dao.AddAccessLog(models.AccessLog{
|
if err := dao.AddAccessLog(models.AccessLog{
|
||||||
UserID: userID,
|
UserID: userID,
|
||||||
ProjectID: p.projectID,
|
ProjectID: p.projectID,
|
||||||
RepoName: p.projectName,
|
RepoName: p.projectName + "/",
|
||||||
|
RepoTag: "N/A",
|
||||||
Operation: "delete",
|
Operation: "delete",
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
log.Errorf("failed to add access log: %v", err)
|
log.Errorf("failed to add access log: %v", err)
|
||||||
|
@ -14,19 +14,29 @@ func TestSearch(t *testing.T) {
|
|||||||
|
|
||||||
apiTest := newHarborAPI()
|
apiTest := newHarborAPI()
|
||||||
var result apilib.Search
|
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 {
|
if err != nil {
|
||||||
t.Error("Error while search project or repository", err.Error())
|
t.Error("Error while search project or repository", err.Error())
|
||||||
t.Log(err)
|
t.Log(err)
|
||||||
} else {
|
} else {
|
||||||
assert.Equal(result.Projects[0].Id, int64(1), "Project id should be equal")
|
assert.Equal(int(200), httpStatusCode, "httpStatusCode should be 200")
|
||||||
assert.Equal(result.Projects[0].Name, "library", "Project name should be library")
|
assert.Equal(int64(1), result.Projects[0].Id, "Project id should be equal")
|
||||||
assert.Equal(result.Projects[0].Public, int32(1), "Project public status should be 1 (true)")
|
assert.Equal("library", result.Projects[0].Name, "Project name should be library")
|
||||||
//t.Log(result)
|
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) {
|
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")
|
fmt.Println("Testing Statistic API")
|
||||||
assert := assert.New(t)
|
assert := assert.New(t)
|
||||||
|
|
||||||
|
@ -1,9 +1,5 @@
|
|||||||
package controllers
|
package controllers
|
||||||
|
|
||||||
import (
|
|
||||||
"net/http"
|
|
||||||
)
|
|
||||||
|
|
||||||
// AccountSettingController handles request to /account_setting
|
// AccountSettingController handles request to /account_setting
|
||||||
type AccountSettingController struct {
|
type AccountSettingController struct {
|
||||||
BaseController
|
BaseController
|
||||||
@ -11,8 +7,14 @@ type AccountSettingController struct {
|
|||||||
|
|
||||||
// Get renders the account settings page
|
// Get renders the account settings page
|
||||||
func (asc *AccountSettingController) Get() {
|
func (asc *AccountSettingController) Get() {
|
||||||
if asc.AuthMode != "db_auth" {
|
var isAdminForLdap bool
|
||||||
asc.CustomAbort(http.StatusForbidden, "")
|
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
|
package controllers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/vmware/harbor/src/common/dao"
|
||||||
|
"github.com/vmware/harbor/src/common/utils/log"
|
||||||
|
)
|
||||||
|
|
||||||
// AdminOptionController handles requests to /admin_option
|
// AdminOptionController handles requests to /admin_option
|
||||||
type AdminOptionController struct {
|
type AdminOptionController struct {
|
||||||
BaseController
|
BaseController
|
||||||
@ -7,5 +12,16 @@ type AdminOptionController struct {
|
|||||||
|
|
||||||
// Get renders the admin options page
|
// Get renders the admin options page
|
||||||
func (aoc *AdminOptionController) Get() {
|
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/astaxie/beego"
|
||||||
"github.com/beego/i18n"
|
"github.com/beego/i18n"
|
||||||
"github.com/vmware/harbor/src/ui/auth"
|
|
||||||
"github.com/vmware/harbor/src/common/dao"
|
"github.com/vmware/harbor/src/common/dao"
|
||||||
"github.com/vmware/harbor/src/common/models"
|
"github.com/vmware/harbor/src/common/models"
|
||||||
"github.com/vmware/harbor/src/common/utils/log"
|
"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.
|
// BaseController wraps common methods such as i18n support, forward, which can be leveraged by other UI render controllers.
|
||||||
|
@ -1,18 +1,20 @@
|
|||||||
package controllers
|
package controllers
|
||||||
|
|
||||||
import (
|
|
||||||
"net/http"
|
|
||||||
)
|
|
||||||
|
|
||||||
// ChangePasswordController handles request to /change_password
|
// ChangePasswordController handles request to /change_password
|
||||||
type ChangePasswordController struct {
|
type ChangePasswordController struct {
|
||||||
BaseController
|
BaseController
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get renders the change password page
|
// Get renders the change password page
|
||||||
func (asc *ChangePasswordController) Get() {
|
func (cpc *ChangePasswordController) Get() {
|
||||||
if asc.AuthMode != "db_auth" {
|
var isAdminForLdap bool
|
||||||
asc.CustomAbort(http.StatusForbidden, "")
|
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()
|
w = httptest.NewRecorder()
|
||||||
beego.BeeApp.Handlers.ServeHTTP(w, r)
|
beego.BeeApp.Handlers.ServeHTTP(w, r)
|
||||||
assert.Equal(int(200), w.Code, "'/account_setting' httpStatusCode should be 200")
|
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)
|
r, _ = http.NewRequest("GET", "/change_password", nil)
|
||||||
w = httptest.NewRecorder()
|
w = httptest.NewRecorder()
|
||||||
beego.BeeApp.Handlers.ServeHTTP(w, r)
|
beego.BeeApp.Handlers.ServeHTTP(w, r)
|
||||||
assert.Equal(int(200), w.Code, "'/change_password' httpStatusCode should be 200")
|
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)
|
r, _ = http.NewRequest("GET", "/admin_option", nil)
|
||||||
w = httptest.NewRecorder()
|
w = httptest.NewRecorder()
|
||||||
beego.BeeApp.Handlers.ServeHTTP(w, r)
|
beego.BeeApp.Handlers.ServeHTTP(w, r)
|
||||||
assert.Equal(int(200), w.Code, "'/admin_option' httpStatusCode should be 200")
|
assert.Equal(int(302), w.Code, "'/admin_option' httpStatusCode should be 302")
|
||||||
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>'")
|
|
||||||
|
|
||||||
r, _ = http.NewRequest("GET", "/forgot_password", nil)
|
r, _ = http.NewRequest("GET", "/forgot_password", nil)
|
||||||
w = httptest.NewRecorder()
|
w = httptest.NewRecorder()
|
||||||
|
@ -19,6 +19,8 @@ func (omc *OptionalMenuController) Get() {
|
|||||||
|
|
||||||
var hasLoggedIn bool
|
var hasLoggedIn bool
|
||||||
var allowAddNew bool
|
var allowAddNew bool
|
||||||
|
|
||||||
|
var isAdminForLdap bool
|
||||||
var allowSettingAccount bool
|
var allowSettingAccount bool
|
||||||
|
|
||||||
if sessionUserID != nil {
|
if sessionUserID != nil {
|
||||||
@ -35,7 +37,11 @@ func (omc *OptionalMenuController) Get() {
|
|||||||
}
|
}
|
||||||
omc.Data["Username"] = u.Username
|
omc.Data["Username"] = u.Username
|
||||||
|
|
||||||
if omc.AuthMode == "db_auth" {
|
if userID == 1 {
|
||||||
|
isAdminForLdap = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if omc.AuthMode == "db_auth" || isAdminForLdap {
|
||||||
allowSettingAccount = true
|
allowSettingAccount = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -88,7 +88,6 @@
|
|||||||
|
|
||||||
|
|
||||||
function getProjectSuccess(response) {
|
function getProjectSuccess(response) {
|
||||||
|
|
||||||
var partialProjects = response.data || [];
|
var partialProjects = response.data || [];
|
||||||
for(var i in partialProjects) {
|
for(var i in partialProjects) {
|
||||||
vm.projects.push(partialProjects[i]);
|
vm.projects.push(partialProjects[i]);
|
||||||
@ -114,11 +113,13 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$location.search('project_id', vm.selectedProject.project_id);
|
if(vm.selectedProject) {
|
||||||
vm.checkProjectMember(vm.selectedProject.project_id);
|
$location.search('project_id', vm.selectedProject.project_id);
|
||||||
|
vm.checkProjectMember(vm.selectedProject.project_id);
|
||||||
|
}
|
||||||
|
|
||||||
vm.resultCount = vm.projects.length;
|
vm.resultCount = vm.projects.length;
|
||||||
|
|
||||||
$scope.$watch('vm.filterInput', function(current, origin) {
|
$scope.$watch('vm.filterInput', function(current, origin) {
|
||||||
vm.resultCount = $filter('name')(vm.projects, vm.filterInput, 'name').length;
|
vm.resultCount = $filter('name')(vm.projects, vm.filterInput, 'name').length;
|
||||||
});
|
});
|
||||||
|
@ -19,9 +19,9 @@
|
|||||||
.module('harbor.repository')
|
.module('harbor.repository')
|
||||||
.directive('listRepository', listRepository);
|
.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;
|
$scope.subsTabPane = 30;
|
||||||
|
|
||||||
@ -104,7 +104,23 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
function getRepositoryFailed(response) {
|
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() {
|
function searchRepo() {
|
||||||
|
@ -32,7 +32,7 @@
|
|||||||
</tr>
|
</tr>
|
||||||
<tr ng-if="vm.integratedLogs.length > 0" ng-repeat="t in vm.integratedLogs">
|
<tr ng-if="vm.integratedLogs.length > 0" ng-repeat="t in vm.integratedLogs">
|
||||||
<td width="18%">//t.username//</td>
|
<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="15%">//t.repo_tag//</td>
|
||||||
<td width="14%">//t.operation//</td>
|
<td width="14%">//t.operation//</td>
|
||||||
<td width="25%">//t.op_time | dateL : 'YYYY-MM-DD HH:mm:ss'//</td>
|
<td width="25%">//t.op_time | dateL : 'YYYY-MM-DD HH:mm:ss'//</td>
|
||||||
|
@ -29,7 +29,7 @@
|
|||||||
.success(listIntegratedLogSuccess)
|
.success(listIntegratedLogSuccess)
|
||||||
.error(listIntegratedLogFailed);
|
.error(listIntegratedLogFailed);
|
||||||
|
|
||||||
vm.gotoLog = gotoLog;
|
vm.gotoRepo = gotoRepo;
|
||||||
|
|
||||||
function listIntegratedLogSuccess(data) {
|
function listIntegratedLogSuccess(data) {
|
||||||
vm.integratedLogs = data || [];
|
vm.integratedLogs = data || [];
|
||||||
@ -42,8 +42,8 @@
|
|||||||
console.log('Failed to get user logs:' + data);
|
console.log('Failed to get user logs:' + data);
|
||||||
}
|
}
|
||||||
|
|
||||||
function gotoLog(projectId, username) {
|
function gotoRepo(projectId, repoName) {
|
||||||
$window.location.href = '/repository#/logs?project_id=' + projectId + '#' + encodeURIComponent(username);
|
$window.location.href = '/repository#/repositories?project_id=' + projectId + '#' + encodeURIComponent(repoName);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -31,7 +31,7 @@
|
|||||||
<th width="20%">// 'email' | tr //</th>
|
<th width="20%">// 'email' | tr //</th>
|
||||||
<th width="35%">// 'registration_time' | tr //</th>
|
<th width="35%">// 'registration_time' | tr //</th>
|
||||||
<th width="15%">// 'administrator' | 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>
|
</thead>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
@ -46,7 +46,7 @@
|
|||||||
<td width="15%">
|
<td width="15%">
|
||||||
<toggle-admin current-user="vm.currentUser" has-admin-role="u.has_admin_role" user-id="//u.user_id//"></toggle-admin>
|
<toggle-admin current-user="vm.currentUser" has-admin-role="u.has_admin_role" user-id="//u.user_id//"></toggle-admin>
|
||||||
</td>
|
</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>
|
<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>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
@ -98,6 +98,9 @@
|
|||||||
'restrict': 'E',
|
'restrict': 'E',
|
||||||
'templateUrl': '/static/resources/js/components/user/list-user.directive.html',
|
'templateUrl': '/static/resources/js/components/user/list-user.directive.html',
|
||||||
'link': link,
|
'link': link,
|
||||||
|
'scope': {
|
||||||
|
'authMode': '@'
|
||||||
|
},
|
||||||
'controller': ListUserController,
|
'controller': ListUserController,
|
||||||
'controllerAs': 'vm',
|
'controllerAs': 'vm',
|
||||||
'bindToController': true
|
'bindToController': true
|
||||||
|
@ -233,6 +233,7 @@ var locale_messages = {
|
|||||||
'failed_to_get_project_member': 'Failed to get current project member.',
|
'failed_to_get_project_member': 'Failed to get current project member.',
|
||||||
'failed_to_delete_repo': 'Failed to delete repository. ',
|
'failed_to_delete_repo': 'Failed to delete repository. ',
|
||||||
'failed_to_delete_repo_insuffient_permissions': 'Failed to delete repository, insuffient permissions.',
|
'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_tag': 'Failed to get tag.',
|
||||||
'failed_to_get_log': 'Failed to get logs.',
|
'failed_to_get_log': 'Failed to get logs.',
|
||||||
'failed_to_get_project': 'Failed to get projects.',
|
'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_update_destination': 'Failed to update destination.',
|
||||||
'failed_to_toggle_publicity_insuffient_permissions': 'Failed to toggle project publicity, insuffient permissions.',
|
'failed_to_toggle_publicity_insuffient_permissions': 'Failed to toggle project publicity, insuffient permissions.',
|
||||||
'failed_to_toggle_publicity': 'Failed to toggle project publicity.',
|
'failed_to_toggle_publicity': 'Failed to toggle project publicity.',
|
||||||
|
'project_does_not_exist': 'Project does not exist.',
|
||||||
'project_admin': 'Project Admin',
|
'project_admin': 'Project Admin',
|
||||||
'developer': 'Developer',
|
'developer': 'Developer',
|
||||||
'guest': 'Guest',
|
'guest': 'Guest',
|
||||||
|
@ -233,6 +233,7 @@ var locale_messages = {
|
|||||||
'failed_to_get_project_member': '无法获取当前项目成员。',
|
'failed_to_get_project_member': '无法获取当前项目成员。',
|
||||||
'failed_to_delete_repo': '无法删除镜像仓库。',
|
'failed_to_delete_repo': '无法删除镜像仓库。',
|
||||||
'failed_to_delete_repo_insuffient_permissions': '无法删除镜像仓库,权限不足。',
|
'failed_to_delete_repo_insuffient_permissions': '无法删除镜像仓库,权限不足。',
|
||||||
|
'failed_to_get_repo': '获取镜像仓库数据失败。',
|
||||||
'failed_to_get_tag': '获取标签数据失败。',
|
'failed_to_get_tag': '获取标签数据失败。',
|
||||||
'failed_to_get_log': '获取日志数据失败。',
|
'failed_to_get_log': '获取日志数据失败。',
|
||||||
'failed_to_get_project': '获取项目数据失败。',
|
'failed_to_get_project': '获取项目数据失败。',
|
||||||
@ -263,6 +264,7 @@ var locale_messages = {
|
|||||||
'failed_to_update_destination': '修改目标失败。',
|
'failed_to_update_destination': '修改目标失败。',
|
||||||
'failed_to_toggle_publicity_insuffient_permissions': '切换项目公开性失败,权限不足。',
|
'failed_to_toggle_publicity_insuffient_permissions': '切换项目公开性失败,权限不足。',
|
||||||
'failed_to_toggle_publicity': '切换项目公开性失败。',
|
'failed_to_toggle_publicity': '切换项目公开性失败。',
|
||||||
|
'project_does_not_exist': '项目不存在。',
|
||||||
'project_admin': '项目管理员',
|
'project_admin': '项目管理员',
|
||||||
'developer': '开发人员',
|
'developer': '开发人员',
|
||||||
'guest': '访客',
|
'guest': '访客',
|
||||||
|
@ -24,7 +24,7 @@
|
|||||||
<span ng-if="vm.toggle">// 'system_management' | tr //</span>
|
<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>
|
<a ng-if="!vm.toggle" href="#/destinations" class="title-color" ng-click="vm.toggleAdminOption({target: 'system_management'})">// 'system_management' | tr //</a>
|
||||||
</h4>
|
</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>
|
<system-management ng-if="vm.target === 'system_management'"></system-management>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -47,8 +47,8 @@
|
|||||||
<th width="15%">// 'repositories' | tr //</th>
|
<th width="15%">// 'repositories' | tr //</th>
|
||||||
<th width="15%" ng-if="!vm.isPublic">// 'role' | tr //</th>
|
<th width="15%" ng-if="!vm.isPublic">// 'role' | tr //</th>
|
||||||
<th width="20%">// 'creation_time' | tr //</th>
|
<th width="20%">// 'creation_time' | tr //</th>
|
||||||
<th width="15%">// 'publicity' | tr //</th>
|
<th width="15%" ng-if="!vm.isPublic">// 'publicity' | tr //</th>
|
||||||
<th width="10%">// 'operation' | tr //</th>
|
<th width="10%" ng-if="!vm.isPublic">// 'operation' | tr //</th>
|
||||||
</thead>
|
</thead>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
@ -63,8 +63,8 @@
|
|||||||
<td width="15%">//p.repo_count//</td>
|
<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="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="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="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%">
|
<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>
|
<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>
|
</td>
|
||||||
</tr>
|
</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