mirror of
https://github.com/goharbor/harbor.git
synced 2025-01-22 23:51:27 +01:00
Merge remote-tracking branch 'upstream/dev' into dev
This commit is contained in:
commit
fd0a6f8b1d
42
.travis.yml
42
.travis.yml
@ -9,27 +9,25 @@ go_import_path: github.com/vmware/harbor
|
||||
|
||||
services:
|
||||
- docker
|
||||
- mysql
|
||||
|
||||
dist: trusty
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
- mysql-server-5.6
|
||||
- mysql-client-core-5.6
|
||||
- mysql-client-5.6
|
||||
|
||||
env:
|
||||
DB_HOST: 127.0.0.1
|
||||
DB_PORT: 3306
|
||||
DB_USR: root
|
||||
DB_PWD:
|
||||
DB_PWD: root123
|
||||
MYSQL_HOST: localhost
|
||||
MYSQL_PORT: 3306
|
||||
MYSQL_USR: root
|
||||
MYSQL_PWD: root123
|
||||
DOCKER_COMPOSE_VERSION: 1.7.1
|
||||
HARBOR_ADMIN: admin
|
||||
HARBOR_ADMIN_PASSWD: Harbor12345
|
||||
UI_SECRET: tempString
|
||||
MAX_JOB_WORKERS: 3
|
||||
SECRET_KEY: 1234567890123456
|
||||
SECRET_KEY: 1234567890123456
|
||||
AUTH_MODE: db_auth
|
||||
|
||||
before_install:
|
||||
- sudo ./tests/hostcfg.sh
|
||||
@ -56,8 +54,8 @@ install:
|
||||
- go get -d github.com/go-sql-driver/mysql
|
||||
- go get github.com/golang/lint/golint
|
||||
- go get github.com/GeertJohan/fgt
|
||||
- sudo apt-get install -y -o Dpkg::Options::="--force-confdef" -o Dpkg::Options::="--force-confold" docker-engine=1.11.1-0~trusty
|
||||
- sudo rm /usr/local/bin/docker-compose
|
||||
|
||||
# - sudo rm /usr/local/bin/docker-compose
|
||||
- curl -L https://github.com/docker/compose/releases/download/${DOCKER_COMPOSE_VERSION}/docker-compose-`uname -s`-`uname -m` > docker-compose
|
||||
- chmod +x docker-compose
|
||||
- sudo mv docker-compose /usr/local/bin
|
||||
@ -70,21 +68,27 @@ install:
|
||||
|
||||
before_script:
|
||||
# create tables and load data
|
||||
- mysql < ./Deploy/db/registry.sql -uroot --verbose
|
||||
# - mysql < ./Deploy/db/registry.sql -uroot --verbose
|
||||
|
||||
script:
|
||||
- go list ./... | grep -v -E 'vendor|tests' | xargs -L1 fgt golint
|
||||
- go list ./... | grep -v -E 'vendor|tests' | xargs -L1 go vet
|
||||
script:
|
||||
- sudo ./tests/testprepare.sh
|
||||
- docker-compose -f Deploy/docker-compose.test.yml up -d
|
||||
- go list ./... | grep -v -E 'vendor|tests|api' | xargs -L1 fgt golint
|
||||
- go list ./... | grep -v -E 'vendor|tests|api' | xargs -L1 go vet
|
||||
- IP=`ip addr s eth0 |grep "inet "|awk '{print $2}' |awk -F "/" '{print $1}'`
|
||||
- export MYSQL_HOST=$IP
|
||||
- export REGISTRY_URL=http://$IP:5000
|
||||
- echo $REGISTRY_URL
|
||||
- ./Deploy/coverage4gotest.sh
|
||||
- goveralls -coverprofile=profile.cov -service=travis-ci
|
||||
|
||||
- docker-compose -f Deploy/docker-compose.test.yml down
|
||||
|
||||
- docker-compose -f Deploy/docker-compose.yml up -d
|
||||
|
||||
- docker ps
|
||||
- go run tests/startuptest.go http://localhost/
|
||||
- go run tests/userlogintest.go -name ${HARBOR_ADMIN} -passwd ${HARBOR_ADMIN_PASSWD}
|
||||
|
||||
|
||||
# test for API
|
||||
- sudo ./tests/testprepare.sh
|
||||
- go test -v ./tests/apitests
|
||||
# - sudo ./tests/testprepare.sh
|
||||
# - go test -v ./tests/apitests
|
||||
|
@ -89,6 +89,6 @@ http {
|
||||
server {
|
||||
listen 80;
|
||||
server_name harbordomain.com;
|
||||
rewrite ^/(.*) https://$server_name/$1 permanent;
|
||||
rewrite ^/(.*) https://$server_name:443/$1 permanent;
|
||||
}
|
||||
}
|
||||
|
@ -48,6 +48,9 @@ max_job_workers = 3
|
||||
#**NOTE** if this changes, previously encrypted password will not be decrypted!
|
||||
secret_key = secretkey1234567
|
||||
|
||||
#The expiration of token used by token service, default is 30 minutes
|
||||
token_expiration = 30
|
||||
|
||||
#Determine whether the job service should verify the ssl cert when it connects to a remote registry.
|
||||
#Set this flag to off when the remote registry uses a self-signed or untrusted certificate.
|
||||
verify_remote_cert = on
|
||||
|
@ -54,6 +54,7 @@ crt_organizationalunit = rcp.get("configuration", "crt_organizationalunit")
|
||||
crt_commonname = rcp.get("configuration", "crt_commonname")
|
||||
crt_email = rcp.get("configuration", "crt_email")
|
||||
max_job_workers = rcp.get("configuration", "max_job_workers")
|
||||
token_expiration = rcp.get("configuration", "token_expiration")
|
||||
verify_remote_cert = rcp.get("configuration", "verify_remote_cert")
|
||||
secret_key = rcp.get("configuration", "secret_key")
|
||||
########
|
||||
@ -109,7 +110,8 @@ render(os.path.join(templates_dir, "ui", "env"),
|
||||
use_compressed_js=use_compressed_js,
|
||||
ui_secret=ui_secret,
|
||||
secret_key=secret_key,
|
||||
verify_remote_cert=verify_remote_cert)
|
||||
verify_remote_cert=verify_remote_cert,
|
||||
token_expiration=token_expiration)
|
||||
|
||||
render(os.path.join(templates_dir, "ui", "app.conf"),
|
||||
ui_conf,
|
||||
|
@ -20,3 +20,4 @@ GODEBUG=netdns=cgo
|
||||
EXT_ENDPOINT=$ui_url
|
||||
TOKEN_URL=http://ui
|
||||
VERIFY_REMOTE_CERT=$verify_remote_cert
|
||||
TOKEN_EXPIRATION=$token_expiration
|
||||
|
@ -5,12 +5,11 @@ MAINTAINER jiangd@vmware.com
|
||||
RUN apt-get update \
|
||||
&& apt-get install -y libldap2-dev \
|
||||
&& rm -r /var/lib/apt/lists/*
|
||||
RUN go get -d github.com/docker/distribution \
|
||||
&& go get -d github.com/docker/libtrust \
|
||||
&& go get -d github.com/go-sql-driver/mysql
|
||||
|
||||
COPY . /go/src/github.com/vmware/harbor
|
||||
|
||||
WORKDIR /go/src/github.com/vmware/harbor/jobservice
|
||||
|
||||
RUN go build -v -a -o /go/bin/harbor_jobservice \
|
||||
&& chmod u+x /go/bin/harbor_jobservice
|
||||
WORKDIR /go/bin/
|
||||
|
@ -7,14 +7,9 @@ RUN apt-get update \
|
||||
&& rm -r /var/lib/apt/lists/*
|
||||
|
||||
COPY . /go/src/github.com/vmware/harbor
|
||||
#golang.org is blocked in China
|
||||
COPY ./vendor/golang.org /go/src/golang.org
|
||||
WORKDIR /go/src/github.com/vmware/harbor/ui
|
||||
|
||||
RUN go get -d github.com/docker/distribution \
|
||||
&& go get -d github.com/docker/libtrust \
|
||||
&& go get -d github.com/go-sql-driver/mysql \
|
||||
&& go build -v -a -o /go/bin/harbor_ui
|
||||
RUN go build -v -a -o /go/bin/harbor_ui
|
||||
|
||||
ENV MYSQL_USR root \
|
||||
MYSQL_PWD root \
|
||||
|
@ -66,6 +66,9 @@ If you are upgrading Harbor from an older version with existing data, you need t
|
||||
### Run
|
||||
For information on how to use Harbor, please take a look at [User Guide](docs/user_guide.md).
|
||||
|
||||
### Community
|
||||
Get connected with Project Harbor's community and sign up with VMware {code} [https://code.vmware.com/join/](https://code.vmware.com/join/) to get invited to VMware {code} Slack group, Channel: #harbor.
|
||||
|
||||
### Contribution
|
||||
We welcome contributions from the community. If you wish to contribute code and you have not signed our contributor license agreement (CLA), our bot will update the issue when you open a pull request. For any questions about the CLA process, please refer to our [FAQ](https://cla.vmware.com/faq).
|
||||
|
||||
|
45
api/base.go
45
api/base.go
@ -86,7 +86,28 @@ func (b *BaseAPI) DecodeJSONReqAndValidate(v interface{}) {
|
||||
|
||||
// ValidateUser checks if the request triggered by a valid user
|
||||
func (b *BaseAPI) ValidateUser() int {
|
||||
userID, needsCheck, ok := b.GetUserIDForRequest()
|
||||
if !ok {
|
||||
log.Warning("No user id in session, canceling request")
|
||||
b.CustomAbort(http.StatusUnauthorized, "")
|
||||
}
|
||||
if needsCheck {
|
||||
u, err := dao.GetUser(models.User{UserID: userID})
|
||||
if err != nil {
|
||||
log.Errorf("Error occurred in GetUser, error: %v", err)
|
||||
b.CustomAbort(http.StatusInternalServerError, "Internal error.")
|
||||
}
|
||||
if u == nil {
|
||||
log.Warningf("User was deleted already, user id: %d, canceling request.", userID)
|
||||
b.CustomAbort(http.StatusUnauthorized, "")
|
||||
}
|
||||
}
|
||||
return userID
|
||||
}
|
||||
|
||||
// GetUserIDForRequest tries to get user ID from basic auth header and session.
|
||||
// It returns the user ID, whether need further verification(when the id is from session) and if the action is successful
|
||||
func (b *BaseAPI) GetUserIDForRequest() (int, bool, bool) {
|
||||
username, password, ok := b.Ctx.Request.BasicAuth()
|
||||
if ok {
|
||||
log.Infof("Requst with Basic Authentication header, username: %s", username)
|
||||
@ -99,25 +120,17 @@ func (b *BaseAPI) ValidateUser() int {
|
||||
user = nil
|
||||
}
|
||||
if user != nil {
|
||||
return user.UserID
|
||||
// User login successfully no further check required.
|
||||
return user.UserID, false, true
|
||||
}
|
||||
}
|
||||
sessionUserID := b.GetSession("userId")
|
||||
if sessionUserID == nil {
|
||||
log.Warning("No user id in session, canceling request")
|
||||
b.CustomAbort(http.StatusUnauthorized, "")
|
||||
sessionUserID, ok := b.GetSession("userId").(int)
|
||||
if ok {
|
||||
// The ID is from session
|
||||
return sessionUserID, true, true
|
||||
}
|
||||
userID := sessionUserID.(int)
|
||||
u, err := dao.GetUser(models.User{UserID: userID})
|
||||
if err != nil {
|
||||
log.Errorf("Error occurred in GetUser, error: %v", err)
|
||||
b.CustomAbort(http.StatusInternalServerError, "Internal error.")
|
||||
}
|
||||
if u == nil {
|
||||
log.Warningf("User was deleted already, user id: %d, canceling request.", userID)
|
||||
b.CustomAbort(http.StatusUnauthorized, "")
|
||||
}
|
||||
return userID
|
||||
log.Debug("No valid user id in session.")
|
||||
return 0, false, false
|
||||
}
|
||||
|
||||
// Redirect does redirection to resource URI with http header status code.
|
||||
|
312
api/harborapi_test.go
Normal file
312
api/harborapi_test.go
Normal file
@ -0,0 +1,312 @@
|
||||
//These APIs provide services for manipulating Harbor project.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http/httptest"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
|
||||
"github.com/vmware/harbor/tests/apitests/apilib"
|
||||
"github.com/vmware/harbor/dao"
|
||||
"github.com/vmware/harbor/models"
|
||||
|
||||
"github.com/astaxie/beego"
|
||||
"github.com/dghubble/sling"
|
||||
|
||||
//for test env prepare
|
||||
_ "github.com/vmware/harbor/auth/db"
|
||||
_ "github.com/vmware/harbor/auth/ldap"
|
||||
)
|
||||
|
||||
type api struct {
|
||||
basePath string
|
||||
}
|
||||
|
||||
func newHarborAPI() *api {
|
||||
return &api{
|
||||
basePath: "",
|
||||
}
|
||||
}
|
||||
|
||||
func newHarborAPIWithBasePath(basePath string) *api {
|
||||
return &api{
|
||||
basePath: basePath,
|
||||
}
|
||||
}
|
||||
|
||||
type usrInfo struct {
|
||||
Name string
|
||||
Passwd string
|
||||
}
|
||||
|
||||
func init() {
|
||||
dao.InitDB()
|
||||
_, file, _, _ := runtime.Caller(1)
|
||||
apppath, _ := filepath.Abs(filepath.Dir(filepath.Join(file, ".."+string(filepath.Separator))))
|
||||
beego.BConfig.WebConfig.Session.SessionOn = true
|
||||
beego.TestBeegoInit(apppath)
|
||||
|
||||
beego.Router("/api/search/", &SearchAPI{})
|
||||
beego.Router("/api/projects/", &ProjectAPI{}, "get:List;post:Post")
|
||||
beego.Router("/api/users/:id([0-9]+)/password", &UserAPI{}, "put:ChangePassword")
|
||||
|
||||
_ = updateInitPassword(1, "Harbor12345")
|
||||
|
||||
}
|
||||
|
||||
//Search for projects and repositories
|
||||
//Implementation Notes
|
||||
//The Search endpoint returns information about the projects and repositories
|
||||
//offered at public status or related to the current logged in user.
|
||||
//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 api) SearchGet (q string) (apilib.Search, error) {
|
||||
func (a api) SearchGet(q string) (apilib.Search, error) {
|
||||
|
||||
_sling := sling.New().Get(a.basePath)
|
||||
|
||||
// create path and map variables
|
||||
path := "/api/search"
|
||||
_sling = _sling.Path(path)
|
||||
|
||||
type QueryParams struct {
|
||||
Query string `url:"q,omitempty"`
|
||||
}
|
||||
|
||||
_sling = _sling.QueryStruct(&QueryParams{Query: q})
|
||||
|
||||
accepts := []string{"application/json", "text/plain"}
|
||||
for key := range accepts {
|
||||
_sling = _sling.Set("Accept", accepts[key])
|
||||
break // only use the first Accept
|
||||
}
|
||||
|
||||
req, err := _sling.Request()
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
beego.BeeApp.Handlers.ServeHTTP(w, req)
|
||||
|
||||
body, err := ioutil.ReadAll(w.Body)
|
||||
if err != nil {
|
||||
// handle error
|
||||
}
|
||||
|
||||
var successPayload = new(apilib.Search)
|
||||
err = json.Unmarshal(body, &successPayload)
|
||||
return *successPayload, err
|
||||
}
|
||||
|
||||
//Create a new project.
|
||||
//Implementation Notes
|
||||
//This endpoint is for user to create a new project.
|
||||
//@param project New created project.
|
||||
//@return void
|
||||
//func (a api) ProjectsPost (prjUsr usrInfo, project apilib.Project) (int, error) {
|
||||
func (a api) ProjectsPost(prjUsr usrInfo, project apilib.Project) (int, error) {
|
||||
|
||||
_sling := sling.New().Post(a.basePath)
|
||||
|
||||
// create path and map variables
|
||||
path := "/api/projects/"
|
||||
|
||||
_sling = _sling.Path(path)
|
||||
|
||||
// accept header
|
||||
accepts := []string{"application/json", "text/plain"}
|
||||
for key := range accepts {
|
||||
_sling = _sling.Set("Accept", accepts[key])
|
||||
break // only use the first Accept
|
||||
}
|
||||
|
||||
// body params
|
||||
_sling = _sling.BodyJSON(project)
|
||||
//fmt.Printf("project post req: %+v\n", _sling)
|
||||
req, err := _sling.Request()
|
||||
req.SetBasicAuth(prjUsr.Name, prjUsr.Passwd)
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
beego.BeeApp.Handlers.ServeHTTP(w, req)
|
||||
|
||||
return w.Code, err
|
||||
}
|
||||
|
||||
//Change password
|
||||
//Implementation Notes
|
||||
//Change the password on a user that already exists.
|
||||
//@param userID user ID
|
||||
//@param password user old and new password
|
||||
//@return error
|
||||
//func (a api) UsersUserIDPasswordPut (user usrInfo, userID int32, password apilib.Password) int {
|
||||
func (a api) UsersUserIDPasswordPut(user usrInfo, userID int32, password apilib.Password) int {
|
||||
|
||||
_sling := sling.New().Put(a.basePath)
|
||||
|
||||
// create path and map variables
|
||||
path := "/api/users/" + fmt.Sprintf("%d", userID) + "/password"
|
||||
fmt.Printf("change passwd path: %s\n", path)
|
||||
fmt.Printf("password %+v\n", password)
|
||||
_sling = _sling.Path(path)
|
||||
|
||||
// accept header
|
||||
accepts := []string{"application/json", "text/plain"}
|
||||
for key := range accepts {
|
||||
_sling = _sling.Set("Accept", accepts[key])
|
||||
break // only use the first Accept
|
||||
}
|
||||
|
||||
// body params
|
||||
_sling = _sling.BodyJSON(password)
|
||||
fmt.Printf("project post req: %+v\n", _sling)
|
||||
req, err := _sling.Request()
|
||||
req.SetBasicAuth(user.Name, user.Passwd)
|
||||
fmt.Printf("project post req: %+v\n", req)
|
||||
if err != nil {
|
||||
// handle error
|
||||
}
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
beego.BeeApp.Handlers.ServeHTTP(w, req)
|
||||
|
||||
return w.Code
|
||||
|
||||
}
|
||||
|
||||
////Delete a repository or a tag in a repository.
|
||||
////Delete a repository or a tag in a repository.
|
||||
////This endpoint let user delete repositories and tags with repo name and tag.\n
|
||||
////@param repoName The name of repository which will be deleted.
|
||||
////@param tag Tag of a repository.
|
||||
////@return void
|
||||
////func (a HarborAPI) RepositoriesDelete(prjUsr UsrInfo, repoName string, tag string) (int, error) {
|
||||
//func (a HarborAPI) RepositoriesDelete(prjUsr UsrInfo, repoName string, tag string) (int, error) {
|
||||
// _sling := sling.New().Delete(a.basePath)
|
||||
|
||||
// // create path and map variables
|
||||
// path := "/api/repositories"
|
||||
|
||||
// _sling = _sling.Path(path)
|
||||
|
||||
// type QueryParams struct {
|
||||
// RepoName string `url:"repo_name,omitempty"`
|
||||
// Tag string `url:"tag,omitempty"`
|
||||
// }
|
||||
|
||||
// _sling = _sling.QueryStruct(&QueryParams{RepoName: repoName, Tag: tag})
|
||||
// // accept header
|
||||
// accepts := []string{"application/json", "text/plain"}
|
||||
// for key := range accepts {
|
||||
// _sling = _sling.Set("Accept", accepts[key])
|
||||
// break // only use the first Accept
|
||||
// }
|
||||
|
||||
// req, err := _sling.Request()
|
||||
// req.SetBasicAuth(prjUsr.Name, prjUsr.Passwd)
|
||||
// //fmt.Printf("request %+v", req)
|
||||
|
||||
// client := &http.Client{}
|
||||
// httpResponse, err := client.Do(req)
|
||||
// defer httpResponse.Body.Close()
|
||||
|
||||
// if err != nil {
|
||||
// // handle error
|
||||
// }
|
||||
// return httpResponse.StatusCode, err
|
||||
//}
|
||||
|
||||
//Return projects created by Harbor
|
||||
//func (a HarborApi) ProjectsGet (projectName string, isPublic int32) ([]Project, error) {
|
||||
// }
|
||||
|
||||
//Check if the project name user provided already exists.
|
||||
//func (a HarborApi) ProjectsHead (projectName string) (error) {
|
||||
//}
|
||||
|
||||
//Get access logs accompany with a relevant project.
|
||||
//func (a HarborApi) ProjectsProjectIdLogsFilterPost (projectId int32, accessLog AccessLog) ([]AccessLog, error) {
|
||||
//}
|
||||
|
||||
//Return a project's relevant role members.
|
||||
//func (a HarborApi) ProjectsProjectIdMembersGet (projectId int32) ([]Role, error) {
|
||||
//}
|
||||
|
||||
//Add project role member accompany with relevant project and user.
|
||||
//func (a HarborApi) ProjectsProjectIdMembersPost (projectId int32, roles RoleParam) (error) {
|
||||
//}
|
||||
|
||||
//Delete project role members accompany with relevant project and user.
|
||||
//func (a HarborApi) ProjectsProjectIdMembersUserIdDelete (projectId int32, userId int32) (error) {
|
||||
//}
|
||||
|
||||
//Return role members accompany with relevant project and user.
|
||||
//func (a HarborApi) ProjectsProjectIdMembersUserIdGet (projectId int32, userId int32) ([]Role, error) {
|
||||
//}
|
||||
|
||||
//Update project role members accompany with relevant project and user.
|
||||
//func (a HarborApi) ProjectsProjectIdMembersUserIdPut (projectId int32, userId int32, roles RoleParam) (error) {
|
||||
//}
|
||||
|
||||
//Update properties for a selected project.
|
||||
//func (a HarborApi) ProjectsProjectIdPut (projectId int32, project Project) (error) {
|
||||
//}
|
||||
|
||||
//Get repositories accompany with relevant project and repo name.
|
||||
//func (a HarborApi) RepositoriesGet (projectId int32, q string) ([]Repository, error) {
|
||||
//}
|
||||
|
||||
//Get manifests of a relevant repository.
|
||||
//func (a HarborApi) RepositoriesManifestGet (repoName string, tag string) (error) {
|
||||
//}
|
||||
|
||||
//Get tags of a relevant repository.
|
||||
//func (a HarborApi) RepositoriesTagsGet (repoName string) (error) {
|
||||
//}
|
||||
|
||||
//Get registered users of Harbor.
|
||||
//func (a HarborApi) UsersGet (userName string) ([]User, error) {
|
||||
//}
|
||||
|
||||
//Creates a new user account.
|
||||
//func (a HarborApi) UsersPost (user User) (error) {
|
||||
//}
|
||||
|
||||
//Mark a registered user as be removed.
|
||||
//func (a HarborApi) UsersUserIdDelete (userId int32) (error) {
|
||||
//}
|
||||
|
||||
//Update a registered user to change to be an administrator of Harbor.
|
||||
//func (a HarborApi) UsersUserIdPut (userId int32) (error) {
|
||||
//}
|
||||
|
||||
func updateInitPassword(userID int, password string) error {
|
||||
queryUser := models.User{UserID: userID}
|
||||
user, err := dao.GetUser(queryUser)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to get user, userID: %d %v", userID, err)
|
||||
}
|
||||
if user == nil {
|
||||
return fmt.Errorf("User id: %d does not exist.", userID)
|
||||
}
|
||||
if user.Salt == "" {
|
||||
salt, err := dao.GenerateRandomString()
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to generate salt for encrypting password, %v", err)
|
||||
}
|
||||
|
||||
user.Salt = salt
|
||||
user.Password = password
|
||||
err = dao.ChangeUserPassword(*user)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to update user encrypted password, userID: %d, err: %v", userID, err)
|
||||
}
|
||||
|
||||
} else {
|
||||
}
|
||||
return nil
|
||||
}
|
65
api/project_test.go
Normal file
65
api/project_test.go
Normal file
@ -0,0 +1,65 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/vmware/harbor/tests/apitests/apilib"
|
||||
)
|
||||
|
||||
func TestAddProject(t *testing.T) {
|
||||
|
||||
fmt.Println("Testing Add Project(ProjectsPost) API")
|
||||
assert := assert.New(t)
|
||||
|
||||
apiTest := newHarborAPI()
|
||||
|
||||
//prepare for test
|
||||
|
||||
admin := &usrInfo{"admin", "Harbor12345"}
|
||||
|
||||
prjUsr := &usrInfo{"unknown", "unknown"}
|
||||
|
||||
var project apilib.Project
|
||||
project.ProjectName = "test_project"
|
||||
project.Public = true
|
||||
|
||||
//case 1: admin not login, expect project creation fail.
|
||||
|
||||
result, err := apiTest.ProjectsPost(*prjUsr, project)
|
||||
if err != nil {
|
||||
t.Error("Error while creat project", err.Error())
|
||||
t.Log(err)
|
||||
} else {
|
||||
assert.Equal(result, int(401), "Case 1: Project creation status should be 401")
|
||||
//t.Log(result)
|
||||
}
|
||||
|
||||
//case 2: admin successful login, expect project creation success.
|
||||
fmt.Println("case 2: admin successful login, expect project creation success.")
|
||||
|
||||
prjUsr = admin
|
||||
|
||||
result, err = apiTest.ProjectsPost(*prjUsr, project)
|
||||
if err != nil {
|
||||
t.Error("Error while creat project", err.Error())
|
||||
t.Log(err)
|
||||
} else {
|
||||
assert.Equal(result, int(201), "Case 2: Project creation status should be 201")
|
||||
//t.Log(result)
|
||||
}
|
||||
|
||||
//case 3: duplicate project name, create project fail
|
||||
fmt.Println("case 3: duplicate project name, create project fail")
|
||||
|
||||
result, err = apiTest.ProjectsPost(*prjUsr, project)
|
||||
if err != nil {
|
||||
t.Error("Error while creat project", err.Error())
|
||||
t.Log(err)
|
||||
} else {
|
||||
assert.Equal(result, int(409), "Case 3: Project creation status should be 409")
|
||||
//t.Log(result)
|
||||
}
|
||||
|
||||
}
|
@ -132,6 +132,10 @@ func (ra *RepositoryAPI) Delete() {
|
||||
ra.CustomAbort(http.StatusInternalServerError, "")
|
||||
}
|
||||
|
||||
if project == nil {
|
||||
ra.CustomAbort(http.StatusNotFound, fmt.Sprintf("project %s not found", projectName))
|
||||
}
|
||||
|
||||
if project.Public == 0 {
|
||||
userID := ra.ValidateUser()
|
||||
if !hasProjectAdminRole(userID, project.ProjectID) {
|
||||
@ -290,6 +294,10 @@ func (ra *RepositoryAPI) GetManifests() {
|
||||
ra.CustomAbort(http.StatusInternalServerError, "")
|
||||
}
|
||||
|
||||
if project == nil {
|
||||
ra.CustomAbort(http.StatusNotFound, fmt.Sprintf("project %s not found", projectName))
|
||||
}
|
||||
|
||||
if project.Public == 0 {
|
||||
userID := ra.ValidateUser()
|
||||
if !checkProjectPermission(userID, project.ProjectID) {
|
||||
|
@ -39,7 +39,7 @@ type searchResult struct {
|
||||
|
||||
// Get ...
|
||||
func (s *SearchAPI) Get() {
|
||||
userID, ok := s.GetSession("userId").(int)
|
||||
userID, _, ok := s.GetUserIDForRequest()
|
||||
if !ok {
|
||||
userID = dao.NonExistUserID
|
||||
}
|
||||
@ -101,18 +101,19 @@ func filterRepositories(repositories []string, projects []models.Project, keywor
|
||||
i, j := 0, 0
|
||||
result := []map[string]interface{}{}
|
||||
for i < len(repositories) && j < len(projects) {
|
||||
r := &utils.Repository{Name: repositories[i]}
|
||||
d := strings.Compare(r.GetProject(), projects[j].Name)
|
||||
r := repositories[i]
|
||||
p, _ := utils.ParseRepository(r)
|
||||
d := strings.Compare(p, projects[j].Name)
|
||||
if d < 0 {
|
||||
i++
|
||||
continue
|
||||
} else if d == 0 {
|
||||
i++
|
||||
if len(keyword) != 0 && !strings.Contains(r.Name, keyword) {
|
||||
if len(keyword) != 0 && !strings.Contains(r, keyword) {
|
||||
continue
|
||||
}
|
||||
entry := make(map[string]interface{})
|
||||
entry["repository_name"] = r.Name
|
||||
entry["repository_name"] = r
|
||||
entry["project_name"] = projects[j].Name
|
||||
entry["project_id"] = projects[j].ProjectID
|
||||
entry["project_public"] = projects[j].Public
|
||||
|
32
api/search_test.go
Normal file
32
api/search_test.go
Normal file
@ -0,0 +1,32 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/vmware/harbor/tests/apitests/apilib"
|
||||
)
|
||||
|
||||
func TestSearch(t *testing.T) {
|
||||
fmt.Println("Testing Search(SearchGet) API")
|
||||
assert := assert.New(t)
|
||||
|
||||
apiTest := newHarborAPI()
|
||||
var result apilib.Search
|
||||
result, err := apiTest.SearchGet("library")
|
||||
//fmt.Printf("%+v\n", result)
|
||||
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)
|
||||
}
|
||||
//if result.Response.StatusCode != 200 {
|
||||
// t.Log(result.Response)
|
||||
//}
|
||||
|
||||
}
|
@ -239,6 +239,11 @@ func (ua *UserAPI) Delete() {
|
||||
ua.RenderError(http.StatusForbidden, "User does not have admin role")
|
||||
return
|
||||
}
|
||||
|
||||
if ua.currentUserID == ua.userID {
|
||||
ua.CustomAbort(http.StatusForbidden, "can not delete yourself")
|
||||
}
|
||||
|
||||
var err error
|
||||
err = dao.DeleteUser(ua.userID)
|
||||
if err != nil {
|
||||
|
@ -1 +1,23 @@
|
||||
docker-compose.sh is used to configure docker-compose.yml to pull images from platform like docker hub, daocloud.io and others. If you don't want to waste time on building images, you can execute this script to pull images from platform you prefer. Currently, we only support daocloud.io and docker hub, the default is docker hub.
|
||||
## Make use of pre-built images of Harbor
|
||||
|
||||
Community members have helped building Harbor's docker images. If you want to save time from building Harbor from source, please follow the below instructions to quickly pull Harbor's pre-built images for installation.
|
||||
|
||||
### Steps
|
||||
|
||||
Run the command `update_compose.sh` :
|
||||
```
|
||||
$ ./update_compose.sh
|
||||
|
||||
Please enter the registry service you want to pull the pre-built images from.
|
||||
Enter 1 for Docker Hub.
|
||||
Enter 2 for Daocloud.io (recommended for Chinese users).
|
||||
or enter other registry URL such as https://my_registry/harbor/ .
|
||||
The default is 1 (Docker Hub):
|
||||
```
|
||||
|
||||
Enter **1** to pull images from Docker Hub,
|
||||
Enter **2** to pull image from Daocloud.io, recommended for Chinese users.
|
||||
or Enter other registry URL like `https://my_registry/harbor/` . Do not forget the "/" and the end.
|
||||
|
||||
This command backs up and updates the file `Deploy/docker-compose.yml` . Next, just follow the [Harbor Installation Guide](../../docs/installation_guide.md) to install Harbor.
|
||||
|
||||
|
@ -1,5 +1,12 @@
|
||||
#/bin/bash
|
||||
read -p "Please input the platform name you want to pull images, for docker hub, enter 1; for daocloud.io, enter 2, otherwise enter the name of the platform, the default is 1:" choice
|
||||
|
||||
echo " "
|
||||
echo "Please enter the registry service you want to pull the pre-built images from."
|
||||
echo "Enter 1 for Docker Hub."
|
||||
echo "Enter 2 for Daocloud.io (recommended for Chinese users)."
|
||||
echo "or enter other registry URL such as https://my_registry/harbor/ ."
|
||||
read -p "The default is 1 (Docker Hub): " choice
|
||||
|
||||
cd ../../Deploy
|
||||
template_file="docker-compose.yml.template"
|
||||
yml_file='docker-compose.yml'
|
||||
@ -29,4 +36,6 @@ sed -i -- '/build: .\/log\//c\ image: '$platform$log$version'' $yml_file
|
||||
sed -i -- '/build: .\/db\//c\ image: '$platform$db$version'' $yml_file
|
||||
sed -i -- '/ui:/{n;N;N;d}' $yml_file && sed -i -- '/ui:/a\\ image: '$platform$ui$version'' $yml_file
|
||||
sed -i -- '/jobservice:/{n;N;N;d}' $yml_file && sed -i -- '/jobservice:/a\\ image: '$platform$job_service$version'' $yml_file
|
||||
echo "succeed! "
|
||||
echo "Succeeded! "
|
||||
echo "Please follow the normal installation process to install Harbor."
|
||||
|
@ -14,7 +14,10 @@ In a test or development environment, you may choose to use a self-signed certif
|
||||
-newkey rsa:4096 -nodes -sha256 -keyout ca.key \
|
||||
-x509 -days 365 -out ca.crt
|
||||
```
|
||||
2) Generate a Certificate Signing Request, be sure to use **reg.yourdomain.com** as the CN (Common Name):
|
||||
2) Generate a Certificate Signing Request:
|
||||
|
||||
If you use FQDN like **reg.yourdomain.com** to connect your registry host, then you must use **reg.yourdomain.com** as CN (Common Name).
|
||||
Otherwise, if you use IP address to connect your registry host, CN can be anything like your name and so on:
|
||||
```
|
||||
openssl req \
|
||||
-newkey rsa:4096 -nodes -sha256 -keyout yourdomain.com.key \
|
||||
@ -22,7 +25,7 @@ In a test or development environment, you may choose to use a self-signed certif
|
||||
```
|
||||
3) Generate the certificate of your registry host:
|
||||
|
||||
You need to configure openssl first. On Ubuntu, the config file locates at **/etc/ssl/openssl.cnf**. Refer to openssl document for more information. The default CA directory of openssl is called demoCA. Let's create necessary directories and files:
|
||||
On Ubuntu, the config file of openssl locates at **/etc/ssl/openssl.cnf**. Refer to openssl document for more information. The default CA directory of openssl is called demoCA. Let's create necessary directories and files:
|
||||
```
|
||||
mkdir demoCA
|
||||
cd demoCA
|
||||
@ -30,11 +33,17 @@ You need to configure openssl first. On Ubuntu, the config file locates at **/et
|
||||
echo '01' > serial
|
||||
cd ..
|
||||
```
|
||||
Then run this command to generate the certificate of your registry host:
|
||||
If you're using FQDN like **reg.yourdomain.com** to connect your registry host, then run this command to generate the certificate of your registry host:
|
||||
```
|
||||
openssl ca -in yourdomain.com.csr -out yourdomain.com.crt -cert ca.crt -keyfile ca.key -outdir .
|
||||
```
|
||||
If you're using **IP** to connect your registry host, you may instead run the command below:
|
||||
```
|
||||
|
||||
echo subjectAltName = IP:your registry host IP > extfile.cnf
|
||||
|
||||
openssl ca -in yourdomain.com.csr -out yourdomain.com.crt -cert ca.crt -keyfile ca.key -extfile extfile.cnf -outdir .
|
||||
```
|
||||
##Configuration of Nginx
|
||||
After obtaining the **yourdomain.com.crt** and **yourdomain.com.key** files, change the directory to Deploy/config/nginx in Harbor project.
|
||||
```
|
||||
@ -54,7 +63,7 @@ Copy the template **nginx.https.conf** as the new configuration file:
|
||||
```
|
||||
cp nginx.https.conf nginx.conf
|
||||
```
|
||||
Edit the file nginx.conf and replace two occurrences of **harbordomain.com** to your own host name, such as reg.yourdomain.com .
|
||||
Edit the file nginx.conf and replace two occurrences of **harbordomain.com** to your own host name, such as reg.yourdomain.com . If you use a customized port rather than the default port 443, replace the port "443" in the line "rewrite ^/(.*) https://$server_name:443/$1 permanent;" as well. Please refer to the [installation guide](https://github.com/vmware/harbor/blob/master/docs/installation_guide.md) for other required steps of port customization.
|
||||
```
|
||||
server {
|
||||
listen 443 ssl;
|
||||
@ -65,7 +74,7 @@ Edit the file nginx.conf and replace two occurrences of **harbordomain.com** to
|
||||
server {
|
||||
listen 80;
|
||||
server_name harbordomain.com;
|
||||
rewrite ^/(.*) https://$server_name$1 permanent;
|
||||
rewrite ^/(.*) https://$server_name:443/$1 permanent;
|
||||
```
|
||||
Then look for the SSL section to make sure the files of your certificates match the names in the config file. Do not change the path of the files.
|
||||
```
|
||||
@ -99,14 +108,22 @@ Finally, restart Harbor:
|
||||
```
|
||||
docker-compose up -d
|
||||
```
|
||||
After setting up HTTPS for Harbor, you can verify it by the follow steps:
|
||||
After setting up HTTPS for Harbor, you can verify it by the following steps:
|
||||
|
||||
1. Open a browser and enter the address: https://reg.yourdomain.com . It should display the user interface of Harbor.
|
||||
|
||||
2. On a machine with Docker daemon, make sure the option "-insecure-registry" does not present, run any docker command to verify the setup, e.g.
|
||||
2. On a machine with Docker daemon, make sure the option "-insecure-registry" does not present, and you must copy ca.crt generated in the above step to /etc/docker/certs.d/yourdomain.com(or your registry host IP), if the directory does not exist, create it.
|
||||
If you mapped nginx port 443 to another port, then you should instead create the directory /etc/docker/certs.d/yourdomain.com:port(or your registry host IP:port). Then run any docker command to verify the setup, e.g.
|
||||
|
||||
```
|
||||
docker login reg.yourdomain.com
|
||||
```
|
||||
If you've mapped nginx 443 port to another, you need to add the port to login, like below:
|
||||
|
||||
```
|
||||
docker login reg.yourdomain.com:port
|
||||
```
|
||||
|
||||
##Troubleshooting
|
||||
1. You may get an intermediate certificate from a certificate issuer. In this case, you should merge the intermediate certificate with your own certificate to create a certificate bundle. You can achieve this by the below command:
|
||||
```
|
||||
@ -124,4 +141,4 @@ After setting up HTTPS for Harbor, you can verify it by the follow steps:
|
||||
cp yourdomain.com.crt /etc/pki/ca-trust/source/anchors/reg.yourdomain.com.crt
|
||||
update-ca-trust
|
||||
```
|
||||
|
||||
|
||||
|
@ -277,6 +277,102 @@ Please check the [Docker Compose command-line reference](https://docs.docker.com
|
||||
By default, registry data is persisted in the target host's `/data/` directory. This data remains unchanged even when Harbor's containers are removed and/or recreated.
|
||||
In addition, Harbor uses `rsyslog` to collect the logs of each container. By default, these log files are stored in the directory `/var/log/harbor/` on the target host.
|
||||
|
||||
## Configuring Harbor listening on a customized port
|
||||
By default, Harbor listens on port 80(HTTP) and 443(HTTPS, if configured) for both admin portal and docker commands, you can configure it with a customized one.
|
||||
|
||||
### For HTTP protocol
|
||||
|
||||
1.Modify Deploy/docker-compose.yml
|
||||
Replace the first "80" to a customized port, e.g. 8888:80.
|
||||
|
||||
```
|
||||
proxy:
|
||||
image: library/nginx:1.9
|
||||
restart: always
|
||||
volumes:
|
||||
- ./config/nginx:/etc/nginx
|
||||
ports:
|
||||
- 8888:80
|
||||
- 443:443
|
||||
depends_on:
|
||||
- mysql
|
||||
- registry
|
||||
- ui
|
||||
- log
|
||||
logging:
|
||||
driver: "syslog"
|
||||
options:
|
||||
syslog-address: "tcp://127.0.0.1:1514"
|
||||
tag: "proxy"
|
||||
```
|
||||
|
||||
2.Modify Deploy/templates/registry/config.yml
|
||||
Add the customized port, e.g. ":8888", after "$ui_url".
|
||||
|
||||
```
|
||||
auth:
|
||||
token:
|
||||
issuer: registry-token-issuer
|
||||
realm: $ui_url:8888/service/token
|
||||
rootcertbundle: /etc/registry/root.crt
|
||||
service: token-service
|
||||
```
|
||||
|
||||
3.Execute Deploy/prepare script and start/restart Harbor.
|
||||
```sh
|
||||
$ cd Deploy
|
||||
$ ./prepare
|
||||
# If Harbor has already been installed, shutdown it first:
|
||||
$ docker-compose down
|
||||
$ docker-compose up -d
|
||||
```
|
||||
### For HTTPS protocol
|
||||
1.Enable HTTPS in Harbor by following this [guide](https://github.com/vmware/harbor/blob/master/docs/configure_https.md).
|
||||
2.Modify Deploy/docker-compose.yml
|
||||
Replace the first "443" to a customized port, e.g. 4443:443.
|
||||
|
||||
```
|
||||
proxy:
|
||||
image: library/nginx:1.9
|
||||
restart: always
|
||||
volumes:
|
||||
- ./config/nginx:/etc/nginx
|
||||
ports:
|
||||
- 80:80
|
||||
- 4443:443
|
||||
depends_on:
|
||||
- mysql
|
||||
- registry
|
||||
- ui
|
||||
- log
|
||||
logging:
|
||||
driver: "syslog"
|
||||
options:
|
||||
syslog-address: "tcp://127.0.0.1:1514"
|
||||
tag: "proxy"
|
||||
```
|
||||
|
||||
3.Modify Deploy/templates/registry/config.yml
|
||||
Add the customized port, e.g. ":4443", after "$ui_url".
|
||||
|
||||
```
|
||||
auth:
|
||||
token:
|
||||
issuer: registry-token-issuer
|
||||
realm: $ui_url:4443/service/token
|
||||
rootcertbundle: /etc/registry/root.crt
|
||||
service: token-service
|
||||
```
|
||||
|
||||
4.Execute Deploy/prepare script and start/restart Harbor.
|
||||
```sh
|
||||
$ cd Deploy
|
||||
$ ./prepare
|
||||
# If Harbor has already been installed, shutdown it first:
|
||||
$ docker-compose down
|
||||
$ docker-compose up -d
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
1.When setting up Harbor behind an nginx proxy or elastic load balancing, look for the line below, in `Deploy/config/nginx/nginx.conf` and remove it from the sections if the proxy already has similar settings: `location /`, `location /v2/` and `location /service/`.
|
||||
```
|
||||
|
@ -32,7 +32,6 @@ paths:
|
||||
description: Search parameter for project and repository name.
|
||||
required: true
|
||||
type: string
|
||||
format: string
|
||||
tags:
|
||||
- Products
|
||||
responses:
|
||||
@ -55,7 +54,6 @@ paths:
|
||||
description: Project name for filtering results.
|
||||
required: false
|
||||
type: string
|
||||
format: string
|
||||
- name: is_public
|
||||
in: query
|
||||
description: Public sign for filtering projects.
|
||||
@ -71,6 +69,8 @@ paths:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/definitions/Project'
|
||||
401:
|
||||
description: User need to log in first.
|
||||
500:
|
||||
description: Internal errors.
|
||||
head:
|
||||
@ -83,7 +83,6 @@ paths:
|
||||
description: Project name for checking exists.
|
||||
required: true
|
||||
type: string
|
||||
format: string
|
||||
tags:
|
||||
- Products
|
||||
responses:
|
||||
@ -119,6 +118,31 @@ paths:
|
||||
description: Project name already exists.
|
||||
500:
|
||||
description: Unexpected internal errors.
|
||||
/projects/{project_id}:
|
||||
get:
|
||||
summary: Return specific project detail infomation
|
||||
description: |
|
||||
This endpoint returns specific project information by project ID.
|
||||
parameters:
|
||||
- name: project_id
|
||||
in: path
|
||||
description: Project ID for filtering results.
|
||||
required: true
|
||||
type: integer
|
||||
format: int64
|
||||
tags:
|
||||
- Products
|
||||
responses:
|
||||
200:
|
||||
description: Return matched project information.
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/definitions/Project'
|
||||
401:
|
||||
description: User need to log in first.
|
||||
500:
|
||||
description: Internal errors.
|
||||
/projects/{project_id}/publicity:
|
||||
put:
|
||||
summary: Update properties for a selected project.
|
||||
@ -128,7 +152,7 @@ paths:
|
||||
- name: project_id
|
||||
in: path
|
||||
type: integer
|
||||
format: int32
|
||||
format: int64
|
||||
required: true
|
||||
description: Selected project ID.
|
||||
- name: project
|
||||
@ -161,7 +185,7 @@ paths:
|
||||
- name: project_id
|
||||
in: path
|
||||
type: integer
|
||||
format: int32
|
||||
format: int64
|
||||
required: true
|
||||
description: Relevant project ID
|
||||
- name: access_log
|
||||
@ -193,7 +217,7 @@ paths:
|
||||
- name: project_id
|
||||
in: path
|
||||
type: integer
|
||||
format: int32
|
||||
format: int64
|
||||
required: true
|
||||
description: Relevant project ID.
|
||||
tags:
|
||||
@ -223,7 +247,7 @@ paths:
|
||||
- name: project_id
|
||||
in: path
|
||||
type: integer
|
||||
format: int32
|
||||
format: int64
|
||||
required: true
|
||||
description: Relevant project ID.
|
||||
- name: roles
|
||||
@ -257,13 +281,13 @@ paths:
|
||||
- name: project_id
|
||||
in: path
|
||||
type: integer
|
||||
format: int32
|
||||
format: int64
|
||||
required: true
|
||||
description: Relevant project ID
|
||||
- name: user_id
|
||||
in: path
|
||||
type: integer
|
||||
format: int32
|
||||
format: int
|
||||
required: true
|
||||
description: Relevant user ID
|
||||
tags:
|
||||
@ -293,13 +317,13 @@ paths:
|
||||
- name: project_id
|
||||
in: path
|
||||
type: integer
|
||||
format: int32
|
||||
format: int64
|
||||
required: true
|
||||
description: Relevant project ID.
|
||||
- name: user_id
|
||||
in: path
|
||||
type: integer
|
||||
format: int32
|
||||
format: int
|
||||
required: true
|
||||
description: Relevant user ID.
|
||||
- name: roles
|
||||
@ -330,13 +354,13 @@ paths:
|
||||
- name: project_id
|
||||
in: path
|
||||
type: integer
|
||||
format: int32
|
||||
format: int64
|
||||
required: true
|
||||
description: Relevant project ID.
|
||||
- name: user_id
|
||||
in: path
|
||||
type: integer
|
||||
format: int32
|
||||
format: int
|
||||
required: true
|
||||
description: Relevant user ID.
|
||||
tags:
|
||||
@ -377,10 +401,9 @@ paths:
|
||||
description: |
|
||||
This endpoint is for user to search registered users, support for filtering results with username.Notice, by now this operation is only for administrator.
|
||||
parameters:
|
||||
- name: user_name
|
||||
- name: username
|
||||
in: query
|
||||
type: string
|
||||
format: string
|
||||
required: false
|
||||
description: Username for filtering results.
|
||||
tags:
|
||||
@ -431,7 +454,7 @@ paths:
|
||||
- name: user_id
|
||||
in: path
|
||||
type: integer
|
||||
format: int32
|
||||
format: int
|
||||
required: true
|
||||
description: Registered user ID
|
||||
- name: profile
|
||||
@ -464,7 +487,7 @@ paths:
|
||||
- name: user_id
|
||||
in: path
|
||||
type: integer
|
||||
format: int32
|
||||
format: int
|
||||
required: true
|
||||
description: User ID for marking as to be removed.
|
||||
tags:
|
||||
@ -491,7 +514,7 @@ paths:
|
||||
- name: user_id
|
||||
in: path
|
||||
type: integer
|
||||
format: int32
|
||||
format: int
|
||||
required: true
|
||||
description: Registered user ID.
|
||||
- name: password
|
||||
@ -508,9 +531,9 @@ paths:
|
||||
400:
|
||||
description: Invalid user ID; Old password is blank; New password is blank.
|
||||
401:
|
||||
description: Old password is not correct.
|
||||
description: Don't have authority to change password. Please check login status.
|
||||
403:
|
||||
description: Guests can only change their own account.
|
||||
description: Old password is not correct.
|
||||
500:
|
||||
description: Unexpected internal errors.
|
||||
/users/{user_id}/sysadmin:
|
||||
@ -523,7 +546,7 @@ paths:
|
||||
- name: user_id
|
||||
in: path
|
||||
type: integer
|
||||
format: int32
|
||||
format: int
|
||||
required: true
|
||||
description: Registered user ID
|
||||
tags:
|
||||
@ -556,7 +579,6 @@ paths:
|
||||
- name: q
|
||||
in: query
|
||||
type: string
|
||||
format: string
|
||||
required: false
|
||||
description: Repo name for filtering results.
|
||||
tags:
|
||||
@ -584,13 +606,11 @@ paths:
|
||||
- name: repo_name
|
||||
in: query
|
||||
type: string
|
||||
format: string
|
||||
required: true
|
||||
description: The name of repository which will be deleted.
|
||||
- name: tag
|
||||
in: query
|
||||
type: string
|
||||
format: string
|
||||
required: false
|
||||
description: Tag of a repository.
|
||||
tags:
|
||||
@ -645,6 +665,10 @@ paths:
|
||||
responses:
|
||||
200:
|
||||
description: Retrieved manifests from a relevant repository successfully.
|
||||
schema:
|
||||
$ref: '#/definitions/Repository'
|
||||
404:
|
||||
description: Retrieved manifests from a relevant repository not found.
|
||||
500:
|
||||
description: Unexpected internal errors.
|
||||
/repositories/top:
|
||||
@ -711,6 +735,437 @@ paths:
|
||||
description: User need to login first.
|
||||
500:
|
||||
description: Unexpected internal errors.
|
||||
/jobs/replication:
|
||||
get:
|
||||
summary: List filters jobs according to the policy and repository
|
||||
description: |
|
||||
This endpoint let user list filters jobs according to the policy and repository. (if start_time and end_time are both null, list jobs of last 10 days)
|
||||
tags:
|
||||
- Products
|
||||
parameters:
|
||||
- name: policy_id
|
||||
in: query
|
||||
type: integer
|
||||
format: int
|
||||
required: false
|
||||
description: The ID of the policy that triggered this job.
|
||||
- name: num
|
||||
in: query
|
||||
type: integer
|
||||
format: int32
|
||||
required: false
|
||||
description: The return list length number.
|
||||
- name: end_time
|
||||
in: query
|
||||
type: integer
|
||||
format: int64
|
||||
required: false
|
||||
description: The end time of jobs done. (Timestamp)
|
||||
- name: start_time
|
||||
in: query
|
||||
type: integer
|
||||
format: int64
|
||||
required: false
|
||||
description: The start time of jobs. (Timestamp)
|
||||
- name: repository
|
||||
in: query
|
||||
type: string
|
||||
required: false
|
||||
description: The respond jobs list filter by repository name.
|
||||
- name: status
|
||||
in: query
|
||||
type: string
|
||||
required: false
|
||||
description: The respond jobs list filter by status.
|
||||
responses:
|
||||
200:
|
||||
description: Get the required logs successfully.
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/definitions/JobStatus'
|
||||
400:
|
||||
description: Bad request because of invalid parameters.
|
||||
401:
|
||||
description: User need to login first.
|
||||
500:
|
||||
description: Unexpected internal errors.
|
||||
/jobs/replication/{id}:
|
||||
delete:
|
||||
summary: Delete specific ID job.
|
||||
description: |
|
||||
This endpoint is aimed to remove specific ID job from jobservice.
|
||||
parameters:
|
||||
- name: id
|
||||
in: path
|
||||
type: integer
|
||||
format: int64
|
||||
required: true
|
||||
description: Delete job ID.
|
||||
tags:
|
||||
- Products
|
||||
responses:
|
||||
200:
|
||||
description: Job deleted successfully.
|
||||
400:
|
||||
description: Job ID is invalid or can't remove this job.
|
||||
401:
|
||||
description: Only admin has this authority.
|
||||
404:
|
||||
description: Project ID does not exist.
|
||||
500:
|
||||
description: Unexpected internal errors.
|
||||
/jobs/replication/{id}/log:
|
||||
get:
|
||||
summary: Get job logs.
|
||||
description: |
|
||||
This endpoint let user search job logs filtered by specific ID.
|
||||
parameters:
|
||||
- name: id
|
||||
in: path
|
||||
type: integer
|
||||
format: int64
|
||||
required: true
|
||||
description: Relevant repository ID
|
||||
tags:
|
||||
- Products
|
||||
responses:
|
||||
200:
|
||||
description: Get job log successfully.
|
||||
400:
|
||||
description: Illegal format of provided ID value.
|
||||
401:
|
||||
description: User need to log in first.
|
||||
404:
|
||||
description: The specific repository ID's log does not exist.
|
||||
500:
|
||||
description: Unexpected internal errors.
|
||||
/policies/replication:
|
||||
get:
|
||||
summary: List filters policies by name and project_id
|
||||
description: |
|
||||
This endpoint let user list filters policies by name and project_id, if name and project_id are nil, list returns all policies
|
||||
parameters:
|
||||
- name: name
|
||||
in: query
|
||||
type: string
|
||||
required: false
|
||||
description: The replication's policy name.
|
||||
- name: project_id
|
||||
in: query
|
||||
type: integer
|
||||
format: int64
|
||||
required: false
|
||||
description: Relevant project ID.
|
||||
tags:
|
||||
- Products
|
||||
responses:
|
||||
200:
|
||||
description: Get policy successfully.
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/definitions/JobStatus'
|
||||
400:
|
||||
description: Invalid project ID.
|
||||
401:
|
||||
description: User need to log in first.
|
||||
500:
|
||||
description: Unexpected internal errors.
|
||||
post:
|
||||
summary: Post creates a policy
|
||||
description: |
|
||||
This endpoint let user creates a policy, and if it is enabled, the replication will be triggered right now.
|
||||
parameters:
|
||||
- name: policyinfo
|
||||
in: body
|
||||
description: Create new policy.
|
||||
required: true
|
||||
schema:
|
||||
$ref: '#/definitions/RepPolicyPost'
|
||||
tags:
|
||||
- Products
|
||||
responses:
|
||||
201:
|
||||
description: Create policy successfully.
|
||||
400:
|
||||
description: Invalid project ID or target ID.
|
||||
401:
|
||||
description: User need to log in first.
|
||||
409:
|
||||
description: Policy name already used or policy already exists with the same project and target.
|
||||
500:
|
||||
description: Unexpected internal errors.
|
||||
/policies/replication/{id}:
|
||||
get:
|
||||
summary: Get replication policy.
|
||||
description: |
|
||||
This endpoint let user search replication policy by specific ID.
|
||||
parameters:
|
||||
- name: id
|
||||
in: path
|
||||
type: integer
|
||||
format: int64
|
||||
required: true
|
||||
description: policy ID
|
||||
tags:
|
||||
- Products
|
||||
responses:
|
||||
200:
|
||||
description: Get job policy successfully.
|
||||
schema:
|
||||
$ref: '#/definitions/RepPolicy'
|
||||
401:
|
||||
description: User need to log in first.
|
||||
404:
|
||||
description: The specific repository ID's policy does not exist.
|
||||
500:
|
||||
description: Unexpected internal errors.
|
||||
put:
|
||||
summary: Put modifies name, description, target and enablement of policy.
|
||||
description: |
|
||||
This endpoint let user update policy name, description, target and enablement.
|
||||
parameters:
|
||||
- name: id
|
||||
in: path
|
||||
type: integer
|
||||
format: int64
|
||||
required: true
|
||||
description: policy ID
|
||||
- name: policyupdate
|
||||
in: body
|
||||
description: Update policy name, description, target and enablement.
|
||||
required: true
|
||||
schema:
|
||||
$ref: '#/definitions/RepPolicyUpdate'
|
||||
tags:
|
||||
- Products
|
||||
responses:
|
||||
200:
|
||||
description: Update job policy content successfully.
|
||||
400:
|
||||
description: policy is enabled or target does not exist
|
||||
401:
|
||||
description: User need to log in first.
|
||||
404:
|
||||
description: The specific repository ID's policy does not exist.
|
||||
409:
|
||||
description: Policy name already used or policy already exists with the same project and target.
|
||||
500:
|
||||
description: Unexpected internal errors.
|
||||
/policies/replication/{id}/enablement:
|
||||
put:
|
||||
summary: Put modifies enablement of the policy.
|
||||
description: |
|
||||
This endpoint let user update policy enablement flag.
|
||||
parameters:
|
||||
- name: id
|
||||
in: path
|
||||
type: integer
|
||||
format: int64
|
||||
required: true
|
||||
description: policy ID
|
||||
- name: enabledflag
|
||||
in: body
|
||||
description: The policy enablement flag.
|
||||
required: true
|
||||
schema:
|
||||
$ref: '#/definitions/RepPolicyEnablementReq'
|
||||
tags:
|
||||
- Products
|
||||
responses:
|
||||
200:
|
||||
description: Update job policy enablement successfully.
|
||||
400:
|
||||
description: Invalid enabled value.
|
||||
401:
|
||||
description: User need to log in first.
|
||||
404:
|
||||
description: The specific repository ID's policy does not exist.
|
||||
500:
|
||||
description: Unexpected internal errors.
|
||||
/targets:
|
||||
get:
|
||||
summary: List filters targets by name.
|
||||
description: |
|
||||
This endpoint let user list filters targets by name, if name is nil, list returns all targets.
|
||||
parameters:
|
||||
- name: name
|
||||
in: query
|
||||
type: string
|
||||
required: false
|
||||
description: The replication's target name.
|
||||
tags:
|
||||
- Products
|
||||
responses:
|
||||
200:
|
||||
description: Get policy successfully.
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/definitions/RepTarget'
|
||||
401:
|
||||
description: User need to log in first.
|
||||
500:
|
||||
description: Unexpected internal errors.
|
||||
post:
|
||||
summary: Create a new replication target.
|
||||
description: |
|
||||
This endpoint is for user to create a new replication target.
|
||||
parameters:
|
||||
- name: reptarget
|
||||
in: body
|
||||
description: New created replication target.
|
||||
required: true
|
||||
schema:
|
||||
$ref: '#/definitions/RepTargetPost'
|
||||
tags:
|
||||
- Products
|
||||
responses:
|
||||
201:
|
||||
description: Replication target created successfully.
|
||||
400:
|
||||
description: Unsatisfied with constraints of the target creation.
|
||||
401:
|
||||
description: User need to log in first.
|
||||
409:
|
||||
description: Replication target name already exists.
|
||||
500:
|
||||
description: Unexpected internal errors.
|
||||
/targets/ping:
|
||||
post:
|
||||
summary: Ping validates target.
|
||||
description: |
|
||||
This endpoint is for ping validates whether the target is reachable and whether the credential is valid.
|
||||
parameters:
|
||||
- name: id
|
||||
in: query
|
||||
type: integer
|
||||
format: int64
|
||||
required: false
|
||||
description: The replication's policy ID.
|
||||
tags:
|
||||
- Products
|
||||
responses:
|
||||
200:
|
||||
description: Ping target successfully.
|
||||
400:
|
||||
description: Target id is invalid/ endpoint is needed/ invaild URL/ network issue.
|
||||
401:
|
||||
description: User need to log in first or wrong username/password for remote target.
|
||||
404:
|
||||
description: Target not found.
|
||||
500:
|
||||
description: Unexpected internal errors.
|
||||
/targets/{id}:
|
||||
put:
|
||||
summary: Update replication's target.
|
||||
description: |
|
||||
This endpoint is for update specific replication's target.
|
||||
parameters:
|
||||
- name: id
|
||||
in: path
|
||||
type: integer
|
||||
format: int64
|
||||
required: true
|
||||
description: The replication's target ID.
|
||||
- name: repo_target
|
||||
in: body
|
||||
required: true
|
||||
schema:
|
||||
$ref: '#/definitions/RepTargetPost'
|
||||
description: Updates of replication's target.
|
||||
tags:
|
||||
- Products
|
||||
responses:
|
||||
200:
|
||||
description: Updated replication's target successfully.
|
||||
400:
|
||||
description: The target is associated with policy which is enabled.
|
||||
401:
|
||||
description: User need to log in first.
|
||||
404:
|
||||
description: Target ID does not exist.
|
||||
409:
|
||||
description: Target name or endpoint is already used.
|
||||
500:
|
||||
description: Unexpected internal errors.
|
||||
get:
|
||||
summary: Get replication's target.
|
||||
description: This endpoint is for get specific replication's target.
|
||||
tags:
|
||||
- Products
|
||||
parameters:
|
||||
- name: id
|
||||
in: path
|
||||
type: integer
|
||||
format: int64
|
||||
required: true
|
||||
description: The replication's target ID.
|
||||
responses:
|
||||
200:
|
||||
description: Get replication's target successfully.
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/definitions/RepTarget'
|
||||
401:
|
||||
description: User need to log in first.
|
||||
404:
|
||||
description: Replication's target not found
|
||||
500:
|
||||
description: Unexpected internal errors.
|
||||
delete:
|
||||
summary: Delete specific replication's target.
|
||||
description: |
|
||||
This endpoint is for to delete specific replication's target.
|
||||
parameters:
|
||||
- name: id
|
||||
in: path
|
||||
type: integer
|
||||
format: int64
|
||||
required: true
|
||||
description: The replication's target ID.
|
||||
tags:
|
||||
- Products
|
||||
responses:
|
||||
200:
|
||||
description: Replication's target deleted successfully.
|
||||
400:
|
||||
description: Replication's target ID is invalid or the target is used by policies.
|
||||
401:
|
||||
description: Only admin has this authority.
|
||||
404:
|
||||
description: Replication's target does not exist.
|
||||
500:
|
||||
description: Unexpected internal errors.
|
||||
/targets/{id}/policies/:
|
||||
get:
|
||||
summary: List the target relevant policies.
|
||||
description: |
|
||||
This endpoint list policies filter with specific replication's target ID.
|
||||
parameters:
|
||||
- name: id
|
||||
in: path
|
||||
type: integer
|
||||
format: int64
|
||||
required: true
|
||||
description: The replication's target ID.
|
||||
tags:
|
||||
- Products
|
||||
responses:
|
||||
200:
|
||||
description: Get relevant policies successfully.
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/definitions/RepPolicy'
|
||||
401:
|
||||
description: User need to log in first.
|
||||
404:
|
||||
description: Replication's target not found
|
||||
500:
|
||||
description: Unexpected internal errors.
|
||||
definitions:
|
||||
Search:
|
||||
type: object
|
||||
@ -765,7 +1220,7 @@ definitions:
|
||||
type: integer
|
||||
format: int32
|
||||
description: The owner ID of the project always means the creator of the project.
|
||||
name:
|
||||
project_name:
|
||||
type: string
|
||||
description: The name of the project.
|
||||
creation_time:
|
||||
@ -798,6 +1253,33 @@ definitions:
|
||||
repo_count:
|
||||
type: integer
|
||||
description: The number of the repositories under this project.
|
||||
Repository:
|
||||
type: object
|
||||
properties:
|
||||
id:
|
||||
type: string
|
||||
description: Repository ID
|
||||
parent:
|
||||
type: string
|
||||
description: Parent of the image.
|
||||
created:
|
||||
type: string
|
||||
description: Repository create time.
|
||||
duration_days:
|
||||
type: string
|
||||
description: Duration days of the image.
|
||||
author:
|
||||
type: string
|
||||
description: Author of the image.
|
||||
architecture:
|
||||
type: string
|
||||
description: Architecture of the image.
|
||||
docker_version:
|
||||
type: string
|
||||
description: Docker version of the image.
|
||||
os:
|
||||
type: string
|
||||
description: OS of the image.
|
||||
User:
|
||||
type: object
|
||||
properties:
|
||||
@ -924,4 +1406,171 @@ definitions:
|
||||
type: integer
|
||||
format: int32
|
||||
description: The count of the total repositories, only be seen when the user is admin.
|
||||
|
||||
JobStatus:
|
||||
type: object
|
||||
properties:
|
||||
id:
|
||||
type: integer
|
||||
format: int64
|
||||
description: The job ID.
|
||||
status:
|
||||
type: string
|
||||
description: The status of the job.
|
||||
repository:
|
||||
type: string
|
||||
description: The repository handled by the job.
|
||||
policy_id:
|
||||
type: integer
|
||||
format: int64
|
||||
description: The ID of the policy that triggered this job.
|
||||
operation:
|
||||
type: string
|
||||
description: The operation of the job.
|
||||
tags:
|
||||
type: array
|
||||
description: The repository's used tag list.
|
||||
items:
|
||||
$ref: '#/definitions/Tags'
|
||||
creation_time:
|
||||
type: string
|
||||
description: The creation time of the job.
|
||||
update_time:
|
||||
type: string
|
||||
description: The update time of the job.
|
||||
Tags:
|
||||
type: object
|
||||
properties:
|
||||
tag:
|
||||
type: string
|
||||
description: The repository's used tag.
|
||||
RepPolicy:
|
||||
type: object
|
||||
properties:
|
||||
id:
|
||||
type: integer
|
||||
format: int64
|
||||
description: The policy ID.
|
||||
project_id:
|
||||
type: integer
|
||||
format: int64
|
||||
description: The project ID.
|
||||
project_name:
|
||||
type: string
|
||||
description: The project name.
|
||||
target_id:
|
||||
type: integer
|
||||
format: int64
|
||||
description: The target ID.
|
||||
target_name:
|
||||
type: string
|
||||
description: The target name.
|
||||
name:
|
||||
type: string
|
||||
description: The policy name.
|
||||
enabled:
|
||||
type: integer
|
||||
format: int
|
||||
description: The policy's enabled status.
|
||||
description:
|
||||
type: string
|
||||
description: The description of the policy.
|
||||
cron_str:
|
||||
type: string
|
||||
description: The cron string for schedule job.
|
||||
start_time:
|
||||
type: string
|
||||
description: The start time of the policy.
|
||||
creation_time:
|
||||
type: string
|
||||
description: The create time of the policy.
|
||||
update_time:
|
||||
type: string
|
||||
description: The update time of the policy.
|
||||
error_job_count:
|
||||
format: int
|
||||
description: The error job count number for the policy.
|
||||
RepPolicyPost:
|
||||
type: object
|
||||
properties:
|
||||
project_id:
|
||||
type: integer
|
||||
format: int64
|
||||
description: The project ID.
|
||||
target_id:
|
||||
type: integer
|
||||
format: int64
|
||||
description: The target ID.
|
||||
name:
|
||||
type: string
|
||||
description: The policy name.
|
||||
RepPolicyUpdate:
|
||||
type: object
|
||||
properties:
|
||||
target_id:
|
||||
type: integer
|
||||
format: int64
|
||||
description: The target ID.
|
||||
name:
|
||||
type: string
|
||||
description: The policy name.
|
||||
enabled:
|
||||
type: integer
|
||||
format: int
|
||||
description: The policy's enabled status.
|
||||
description:
|
||||
type: string
|
||||
description: The description of the policy.
|
||||
cron_str:
|
||||
type: string
|
||||
description: The cron string for schedule job.
|
||||
RepPolicyEnablementReq:
|
||||
type: object
|
||||
properties:
|
||||
enabled:
|
||||
type: integer
|
||||
format: int
|
||||
description: The policy enablement flag.
|
||||
RepTarget:
|
||||
type: object
|
||||
properties:
|
||||
id:
|
||||
type: integer
|
||||
format: int64
|
||||
description: The target ID.
|
||||
endpoint:
|
||||
type: string
|
||||
description: The target address URL string.
|
||||
name:
|
||||
type: string
|
||||
description: The target name.
|
||||
username:
|
||||
type: string
|
||||
description: The target server username.
|
||||
password:
|
||||
type: string
|
||||
description: The target server password.
|
||||
type:
|
||||
type: integer
|
||||
format: int
|
||||
description: Reserved field.
|
||||
creation_time:
|
||||
type: string
|
||||
description: The create time of the policy.
|
||||
update_time:
|
||||
type: string
|
||||
description: The update time of the policy.
|
||||
RepTargetPost:
|
||||
type: object
|
||||
properties:
|
||||
endpoint:
|
||||
type: string
|
||||
description: The target address URL string.
|
||||
name:
|
||||
type: string
|
||||
description: The target name.
|
||||
username:
|
||||
type: string
|
||||
description: The target server username.
|
||||
password:
|
||||
type: string
|
||||
description: The target server password.
|
||||
|
@ -21,6 +21,8 @@ import (
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@ -34,9 +36,32 @@ import (
|
||||
const (
|
||||
issuer = "registry-token-issuer"
|
||||
privateKey = "/etc/ui/private_key.pem"
|
||||
expiration = 5 //minute
|
||||
)
|
||||
|
||||
var (
|
||||
expiration = 30 //minutes
|
||||
)
|
||||
|
||||
func init() {
|
||||
// TODO read it from config
|
||||
expi := os.Getenv("TOKEN_EXPIRATION")
|
||||
if len(expi) != 0 {
|
||||
i, err := strconv.Atoi(expi)
|
||||
if err != nil {
|
||||
log.Errorf("failed to parse token expiration: %v, using default value: %d minutes", err, expiration)
|
||||
return
|
||||
}
|
||||
|
||||
if i <= 0 {
|
||||
log.Warningf("invalid token expiration, using default value: %d minutes", expiration)
|
||||
return
|
||||
}
|
||||
|
||||
expiration = i
|
||||
}
|
||||
log.Infof("token expiration: %d minutes", expiration)
|
||||
}
|
||||
|
||||
// GetResourceActions ...
|
||||
func GetResourceActions(scopes []string) []*token.ResourceActions {
|
||||
log.Debugf("scopes: %+v", scopes)
|
||||
|
@ -73,13 +73,11 @@
|
||||
}
|
||||
|
||||
.sub-pane {
|
||||
margin: 15px;
|
||||
min-height: 380px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.well-custom {
|
||||
|
||||
width: 100%;
|
||||
background-color: #f5f5f5;
|
||||
background-image: none;
|
||||
|
@ -48,7 +48,8 @@
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<paginator ng-if="vm.totalCount > 0" total-count="//vm.totalCount//" page-size="//vm.pageSize//" page="vm.page" display-count="5"></paginator>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -51,8 +51,18 @@
|
||||
'projectId': vm.projectId,
|
||||
'username' : vm.username
|
||||
};
|
||||
|
||||
retrieve(vm.queryParams);
|
||||
|
||||
vm.page = 1;
|
||||
vm.pageSize = 20;
|
||||
|
||||
$scope.$watch('vm.page', function(current, origin) {
|
||||
if(current !== 1) {
|
||||
vm.page = current;
|
||||
retrieve(vm.queryParams, vm.page, vm.pageSize);
|
||||
}
|
||||
});
|
||||
|
||||
retrieve(vm.queryParams, vm.page, vm.pageSize);
|
||||
|
||||
$scope.$on('$locationChangeSuccess', function() {
|
||||
|
||||
@ -69,11 +79,13 @@
|
||||
'username' : vm.username
|
||||
};
|
||||
vm.username = '';
|
||||
retrieve(vm.queryParams);
|
||||
retrieve(vm.queryParams, vm.page, vm.pageSize);
|
||||
});
|
||||
|
||||
function search(e) {
|
||||
|
||||
|
||||
vm.page = 1;
|
||||
|
||||
if(e.op[0] === 'all') {
|
||||
e.op = ['create', 'pull', 'push', 'delete'];
|
||||
}
|
||||
@ -83,11 +95,12 @@
|
||||
|
||||
vm.queryParams.keywords = e.op.join('/');
|
||||
vm.queryParams.username = e.username;
|
||||
|
||||
|
||||
vm.queryParams.beginTimestamp = toUTCSeconds(vm.fromDate, 0, 0, 0);
|
||||
vm.queryParams.endTimestamp = toUTCSeconds(vm.toDate, 23, 59, 59);
|
||||
|
||||
retrieve(vm.queryParams);
|
||||
|
||||
retrieve(vm.queryParams, vm.page, vm.pageSize);
|
||||
|
||||
}
|
||||
|
||||
function showAdvancedSearch() {
|
||||
@ -98,27 +111,30 @@
|
||||
}
|
||||
}
|
||||
|
||||
function retrieve(queryParams) {
|
||||
ListLogService(queryParams)
|
||||
function retrieve(queryParams, page, pageSize) {
|
||||
ListLogService(queryParams, page, pageSize)
|
||||
.then(listLogComplete)
|
||||
.catch(listLogFailed);
|
||||
}
|
||||
|
||||
function listLogComplete(response) {
|
||||
vm.logs = response.data;
|
||||
vm.totalCount = response.headers('X-Total-Count');
|
||||
|
||||
vm.queryParams = {
|
||||
'beginTimestamp' : 0,
|
||||
'endTimestamp' : 0,
|
||||
'keywords' : '',
|
||||
'projectId': vm.projectId,
|
||||
'username' : ''
|
||||
};
|
||||
vm.op = ['all'];
|
||||
vm.fromDate = '';
|
||||
vm.toDate = '';
|
||||
vm.others = '';
|
||||
vm.opOthers = true;
|
||||
console.log('Total Count in logs:' + vm.totalCount + ', page:' + vm.page);
|
||||
|
||||
// vm.queryParams = {
|
||||
// 'beginTimestamp' : 0,
|
||||
// 'endTimestamp' : 0,
|
||||
// 'keywords' : '',
|
||||
// 'projectId': vm.projectId,
|
||||
// 'username' : ''
|
||||
// };
|
||||
// vm.op = ['all'];
|
||||
// vm.fromDate = '';
|
||||
// vm.toDate = '';
|
||||
// vm.others = '';
|
||||
// vm.opOthers = true;
|
||||
vm.isOpen = false;
|
||||
}
|
||||
function listLogFailed(response){
|
||||
|
@ -0,0 +1,14 @@
|
||||
<nav aria-label="Page navigation" class="pull-left">
|
||||
<ul class="pagination" style="margin: 0;">
|
||||
<li>
|
||||
<a href="javascript:void(0);" ng-click="vm.previous()" aria-label="Previous">
|
||||
<span aria-hidden="true">«</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="javascript:void(0);" ng-click="vm.next()" aria-label="Next">
|
||||
<span aria-hidden="true">»</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
196
static/resources/js/components/paginator/paginator.directive.js
Normal file
196
static/resources/js/components/paginator/paginator.directive.js
Normal file
@ -0,0 +1,196 @@
|
||||
(function() {
|
||||
|
||||
'use strict';
|
||||
|
||||
angular
|
||||
.module('harbor.paginator')
|
||||
.directive('paginator', paginator);
|
||||
|
||||
PaginatorController.$inject = [];
|
||||
|
||||
function PaginatorController() {
|
||||
var vm = this;
|
||||
}
|
||||
|
||||
paginator.$inject = [];
|
||||
|
||||
function paginator() {
|
||||
var directive = {
|
||||
'restrict': 'E',
|
||||
'templateUrl': '/static/resources/js/components/paginator/paginator.directive.html',
|
||||
'scope': {
|
||||
'totalCount': '@',
|
||||
'pageSize': '@',
|
||||
'page': '=',
|
||||
'displayCount': '@'
|
||||
},
|
||||
'link': link,
|
||||
'controller': PaginatorController,
|
||||
'controllerAs': 'vm',
|
||||
'bindToController': true
|
||||
};
|
||||
return directive;
|
||||
|
||||
function link(scope, element, attrs, ctrl) {
|
||||
|
||||
scope.$watch('vm.page', function(current) {
|
||||
if(current) {
|
||||
ctrl.page = current;
|
||||
togglePageButton();
|
||||
}
|
||||
});
|
||||
|
||||
var tc;
|
||||
|
||||
scope.$watch('vm.totalCount', function(current) {
|
||||
if(current) {
|
||||
var totalCount = current;
|
||||
|
||||
element.find('ul li:first a').off('click');
|
||||
element.find('ul li:last a').off('click');
|
||||
|
||||
tc = new TimeCounter();
|
||||
|
||||
console.log('Total Count:' + totalCount + ', Page Size:' + ctrl.pageSize + ', Display Count:' + ctrl.displayCount + ', Page:' + ctrl.page);
|
||||
|
||||
ctrl.buttonCount = Math.ceil(totalCount / ctrl.pageSize);
|
||||
|
||||
if(ctrl.buttonCount <= ctrl.displayCount) {
|
||||
tc.setMaximum(1);
|
||||
}else{
|
||||
tc.setMaximum(Math.ceil(ctrl.buttonCount / ctrl.displayCount));
|
||||
}
|
||||
|
||||
element.find('ul li:first a').on('click', previous);
|
||||
element.find('ul li:last a').on('click', next);
|
||||
|
||||
drawButtons(tc.getTime());
|
||||
|
||||
togglePrevious(tc.canDecrement());
|
||||
toggleNext(tc.canIncrement());
|
||||
|
||||
togglePageButton();
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
var TimeCounter = function() {
|
||||
this.time = 0;
|
||||
this.minimum = 0;
|
||||
this.maximum = 0;
|
||||
}
|
||||
|
||||
TimeCounter.prototype.setMaximum = function(maximum) {
|
||||
this.maximum = maximum;
|
||||
};
|
||||
|
||||
TimeCounter.prototype.increment = function() {
|
||||
if(this.time < this.maximum) {
|
||||
++this.time;
|
||||
if((ctrl.page % ctrl.displayCount) != 0) {
|
||||
ctrl.page = this.time * ctrl.displayCount;
|
||||
}
|
||||
++ctrl.page;
|
||||
}
|
||||
scope.$apply();
|
||||
};
|
||||
|
||||
TimeCounter.prototype.canIncrement = function() {
|
||||
if(this.time + 1 < this.maximum) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
TimeCounter.prototype.decrement = function() {
|
||||
if(this.time > this.minimum) {
|
||||
if(this.time === 0) {
|
||||
ctrl.page = ctrl.displayCount;
|
||||
}else if((ctrl.page % ctrl.displayCount) != 0) {
|
||||
ctrl.page = this.time * ctrl.displayCount;
|
||||
}
|
||||
--this.time;
|
||||
--ctrl.page;
|
||||
}
|
||||
scope.$apply();
|
||||
};
|
||||
|
||||
TimeCounter.prototype.canDecrement = function() {
|
||||
if(this.time > this.minimum) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
TimeCounter.prototype.getTime = function() {
|
||||
return this.time;
|
||||
};
|
||||
|
||||
function drawButtons(time) {
|
||||
element.find('li[tag="pagination-button"]').remove();
|
||||
var buttons = [];
|
||||
for(var i = 1; i <= ctrl.displayCount; i++) {
|
||||
var displayNumber = ctrl.displayCount * time + i;
|
||||
if(displayNumber <= ctrl.buttonCount) {
|
||||
buttons.push('<li tag="pagination-button"><a href="javascript:void(0)" page="' + displayNumber + '">' + displayNumber + '<span class="sr-only"></span></a></li>');
|
||||
}
|
||||
}
|
||||
$(buttons.join(''))
|
||||
.insertAfter(element.find('ul li:eq(0)')).end()
|
||||
.on('click', buttonClickHandler);
|
||||
}
|
||||
|
||||
function togglePrevious(status) {
|
||||
if(status){
|
||||
element.find('ul li:first').removeClass('disabled');
|
||||
}else{
|
||||
element.find('ul li:first').addClass('disabled');
|
||||
}
|
||||
}
|
||||
|
||||
function toggleNext(status) {
|
||||
if(status) {
|
||||
element.find('ul li:last').removeClass('disabled');
|
||||
}else{
|
||||
element.find('ul li:last').addClass('disabled');
|
||||
}
|
||||
}
|
||||
|
||||
function buttonClickHandler(e) {
|
||||
ctrl.page = $(e.target).attr('page');
|
||||
togglePageButton();
|
||||
togglePrevious(tc.canDecrement());
|
||||
toggleNext(tc.canIncrement());
|
||||
scope.$apply();
|
||||
}
|
||||
|
||||
function togglePageButton() {
|
||||
element.find('li[tag="pagination-button"]').removeClass('active');
|
||||
element.find('li[tag="pagination-button"] a[page="' + ctrl.page + '"]').parent().addClass('active');
|
||||
}
|
||||
|
||||
function previous() {
|
||||
if(tc.canDecrement()) {
|
||||
tc.decrement();
|
||||
drawButtons(tc.getTime());
|
||||
togglePageButton();
|
||||
togglePrevious(tc.canDecrement());
|
||||
toggleNext(tc.canIncrement());
|
||||
}
|
||||
scope.$apply();
|
||||
}
|
||||
|
||||
function next() {
|
||||
if(tc.canIncrement()) {
|
||||
tc.increment();
|
||||
drawButtons(tc.getTime());
|
||||
togglePageButton();
|
||||
togglePrevious(tc.canDecrement());
|
||||
toggleNext(tc.canIncrement());
|
||||
}
|
||||
scope.$apply();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
})();
|
@ -0,0 +1,8 @@
|
||||
(function() {
|
||||
|
||||
'use strict';
|
||||
|
||||
angular
|
||||
.module('harbor.paginator', []);
|
||||
|
||||
})();
|
@ -12,5 +12,5 @@
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
<button ng-if="vm.isPublic" ng-disabled="!vm.owned" class="btn btn-success" ng-click="vm.toggle()">// 'button_on' | tr //</button>
|
||||
<button ng-if="!vm.isPublic" ng-disabled="!vm.owned" class="btn btn-danger" ng-click="vm.toggle()">// 'button_off' | tr //</button>
|
||||
<button ng-if="vm.isPublic" class="btn btn-success" ng-click="vm.toggle()">// 'button_on' | tr //</button>
|
||||
<button ng-if="!vm.isPublic" class="btn btn-danger" ng-click="vm.toggle()">// 'button_off' | tr //</button>
|
@ -73,7 +73,9 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-xs-4 col-md-12 well well-sm well-custom well-split"><div class="col-md-offset-10">//vm.replicationPolicies ? vm.replicationPolicies.length : 0// // 'items' | tr //</div></div>
|
||||
<div class="col-xs-4 col-md-12 well well-sm well-custom well-split">
|
||||
<div class="col-md-offset-10">//vm.replicationPolicies ? vm.replicationPolicies.length : 0// // 'items' | tr //</div>
|
||||
</div>
|
||||
<p class="split-handle"><span class="glyphicon glyphicon-align-justify"></span></p>
|
||||
<h4 class="h4-custom-down">// 'replication_jobs' | tr //</h4>
|
||||
<hr class="hr-line"/>
|
||||
@ -147,7 +149,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-xs-4 col-md-12 well well-sm well-custom well-split"><div class="col-md-offset-10">//vm.replicationJobs ? vm.replicationJobs.length : 0// // 'items' | tr //</div></div>
|
||||
<paginator ng-if="vm.totalCount > 0" total-count="//vm.totalCount//" page-size="//vm.pageSize//" display-count="5" page="vm.page"></paginator>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -59,6 +59,17 @@
|
||||
vm.retrievePolicy = retrievePolicy;
|
||||
vm.retrieveJob = retrieveJob;
|
||||
|
||||
vm.pageSize = 20;
|
||||
vm.page = 1;
|
||||
|
||||
$scope.$watch('vm.page', function(current) {
|
||||
if(current !== 1) {
|
||||
vm.page = current;
|
||||
console.log('replication job: vm.page:' + current);
|
||||
vm.retrieveJob(vm.lastPolicyId, vm.page, vm.pageSize);
|
||||
}
|
||||
});
|
||||
|
||||
vm.confirmToTogglePolicy = confirmToTogglePolicy;
|
||||
vm.togglePolicy = togglePolicy;
|
||||
|
||||
@ -84,14 +95,14 @@
|
||||
function searchReplicationJob() {
|
||||
if(vm.lastPolicyId !== -1) {
|
||||
vm.searchJobTIP = true;
|
||||
vm.retrieveJob(vm.lastPolicyId);
|
||||
vm.retrieveJob(vm.lastPolicyId, vm.page, vm.pageSize);
|
||||
}
|
||||
}
|
||||
|
||||
function refreshReplicationJob() {
|
||||
if(vm.lastPolicyId !== -1) {
|
||||
vm.refreshJobTIP = true;
|
||||
vm.retrieveJob(vm.lastPolicyId);
|
||||
vm.retrieveJob(vm.lastPolicyId, vm.page, vm.pageSize);
|
||||
}
|
||||
}
|
||||
|
||||
@ -101,11 +112,10 @@
|
||||
.error(listReplicationPolicyFailed);
|
||||
}
|
||||
|
||||
function retrieveJob(policyId) {
|
||||
function retrieveJob(policyId, page, pageSize) {
|
||||
var status = (vm.currentStatus.key === 'all' ? '' : vm.currentStatus.key);
|
||||
ListReplicationJobService(policyId, vm.replicationJobName, status, toUTCSeconds(vm.fromDate, 0, 0, 0), toUTCSeconds(vm.toDate, 23, 59, 59))
|
||||
.success(listReplicationJobSuccess)
|
||||
.error(listReplicationJobFailed);
|
||||
ListReplicationJobService(policyId, vm.replicationJobName, status, toUTCSeconds(vm.fromDate, 0, 0, 0), toUTCSeconds(vm.toDate, 23, 59, 59), page, pageSize)
|
||||
.then(listReplicationJobSuccess, listReplicationJobFailed);
|
||||
}
|
||||
|
||||
function listReplicationPolicySuccess(data, status) {
|
||||
@ -117,8 +127,9 @@
|
||||
console.log('Failed to list replication policy:' + data);
|
||||
}
|
||||
|
||||
function listReplicationJobSuccess(data, status) {
|
||||
vm.replicationJobs = data || [];
|
||||
function listReplicationJobSuccess(response) {
|
||||
vm.replicationJobs = response.data || [];
|
||||
vm.totalCount = response.headers('X-Total-Count');
|
||||
var alertInfo = {
|
||||
'show': false,
|
||||
'message': ''
|
||||
@ -146,8 +157,8 @@
|
||||
vm.refreshJobTIP = false;
|
||||
}
|
||||
|
||||
function listReplicationJobFailed(data, status) {
|
||||
console.log('Failed to list replication job:' + data);
|
||||
function listReplicationJobFailed(response) {
|
||||
console.log('Failed to list replication job:' + response);
|
||||
vm.searchJobTIP = false;
|
||||
vm.refreshJobTIP = false;
|
||||
}
|
||||
@ -259,8 +270,8 @@
|
||||
var uponTableHeight = element.find('#upon-pane .table-body-container').height();
|
||||
var downTableHeight = element.find('#down-pane .table-body-container').height();
|
||||
|
||||
var handleHeight = element.find('.split-handle').height() + element.find('.split-handle').offset().top + element.find('.well').height() - 24;
|
||||
|
||||
var handleHeight = element.find('.split-handle').height() + element.find('.split-handle').offset().top + element.find('.well').height() - 32;
|
||||
console.log('handleHeight:' + handleHeight);
|
||||
var maxDownPaneHeight = 760;
|
||||
|
||||
element.find('.split-handle').on('mousedown', mousedownHandler);
|
||||
@ -328,7 +339,7 @@
|
||||
.css({'color': '#fff'});
|
||||
$('a', this)
|
||||
.css({'color': '#fff'});
|
||||
ctrl.retrieveJob($(this).attr('policy_id'));
|
||||
ctrl.retrieveJob($(this).attr('policy_id'), ctrl.page, ctrl.pageSize);
|
||||
ctrl.lastPolicyId = $(this).attr('policy_id');
|
||||
}
|
||||
|
||||
|
@ -30,6 +30,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<paginator ng-if="vm.totalCount > 0" total-count="//vm.totalCount//" page-size="//vm.pageSize//" page="vm.page" display-count="5"></paginator>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
@ -41,7 +41,9 @@
|
||||
vm.filterInput = hashValue;
|
||||
}
|
||||
}
|
||||
|
||||
vm.page = 1;
|
||||
vm.pageSize = 8;
|
||||
|
||||
vm.retrieve = retrieve;
|
||||
vm.tagCount = {};
|
||||
|
||||
@ -61,6 +63,15 @@
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
|
||||
$scope.$watch('vm.page', function(current) {
|
||||
if(current !== 1) {
|
||||
vm.page = current;
|
||||
vm.retrieve();
|
||||
}
|
||||
});
|
||||
|
||||
$scope.$on('repoName', function(e, val) {
|
||||
vm.repoName = val;
|
||||
});
|
||||
@ -76,19 +87,19 @@
|
||||
$scope.$on('tags', function(e, val) {
|
||||
vm.tags = val;
|
||||
});
|
||||
|
||||
|
||||
vm.deleteByRepo = deleteByRepo;
|
||||
vm.deleteByTag = deleteByTag;
|
||||
vm.deleteImage = deleteImage;
|
||||
|
||||
function retrieve(){
|
||||
ListRepositoryService(vm.projectId, vm.filterInput)
|
||||
.success(getRepositoryComplete)
|
||||
.error(getRepositoryFailed);
|
||||
ListRepositoryService(vm.projectId, vm.filterInput, vm.page, vm.pageSize)
|
||||
.then(getRepositoryComplete, getRepositoryFailed);
|
||||
}
|
||||
|
||||
function getRepositoryComplete(data, status) {
|
||||
vm.repositories = data || [];
|
||||
function getRepositoryComplete(response) {
|
||||
vm.repositories = response.data || [];
|
||||
vm.totalCount = response.headers('X-Total-Count');
|
||||
$scope.$broadcast('refreshTags', true);
|
||||
}
|
||||
|
||||
|
@ -44,10 +44,10 @@
|
||||
<td width="20%">//u.email//</td>
|
||||
<td width="35%">//u.creation_time | dateL : 'YYYY-MM-DD HH:mm:ss'//</td>
|
||||
<td width="15%">
|
||||
<toggle-admin 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 width="20%">
|
||||
<a 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>
|
||||
</tr>
|
||||
</tbody>
|
||||
|
@ -20,9 +20,9 @@
|
||||
.module('harbor.user')
|
||||
.directive('listUser', listUser);
|
||||
|
||||
ListUserController.$inject = ['$scope', 'ListUserService', 'DeleteUserService', '$filter', 'trFilter'];
|
||||
ListUserController.$inject = ['$scope', 'ListUserService', 'DeleteUserService', 'currentUser', '$filter', 'trFilter'];
|
||||
|
||||
function ListUserController($scope, ListUserService, DeleteUserService, $filter, $trFilter) {
|
||||
function ListUserController($scope, ListUserService, DeleteUserService, currentUser, $filter, $trFilter) {
|
||||
|
||||
$scope.subsSubPane = 226;
|
||||
|
||||
@ -33,6 +33,8 @@
|
||||
vm.deleteUser = deleteUser;
|
||||
vm.confirmToDelete = confirmToDelete;
|
||||
vm.retrieve = retrieve;
|
||||
|
||||
vm.currentUser = currentUser.get();
|
||||
|
||||
vm.retrieve();
|
||||
|
||||
|
@ -12,5 +12,5 @@
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
<button ng-show="vm.isAdmin" class="btn btn-success" ng-click="vm.toggle()">// 'button_on' | tr //</button>
|
||||
<button ng-show="vm.isAdmin" ng-disabled="!vm.editable" class="btn btn-success" ng-click="vm.toggle()">// 'button_on' | tr //</button>
|
||||
<button ng-show="!vm.isAdmin" class="btn btn-danger" ng-click="vm.toggle()">// 'button_off' | tr //</button>
|
@ -28,6 +28,7 @@
|
||||
vm.isAdmin = (vm.hasAdminRole === 1);
|
||||
vm.enabled = vm.isAdmin ? 0 : 1;
|
||||
vm.toggle = toggle;
|
||||
vm.editable = (vm.currentUser.user_id !== Number(vm.userId));
|
||||
|
||||
function toggle() {
|
||||
ToggleAdminService(vm.userId, vm.enabled)
|
||||
@ -63,7 +64,8 @@
|
||||
'templateUrl': '/static/resources/js/components/user/toggle-admin.directive.html',
|
||||
'scope': {
|
||||
'hasAdminRole': '=',
|
||||
'userId': '@'
|
||||
'userId': '@',
|
||||
'currentUser': '='
|
||||
},
|
||||
'link': link,
|
||||
'controller': ToggleAdminController,
|
||||
|
@ -60,6 +60,7 @@
|
||||
'harbor.system.management',
|
||||
'harbor.loading.progress',
|
||||
'harbor.inline.help',
|
||||
'harbor.dismissable.alerts'
|
||||
'harbor.dismissable.alerts',
|
||||
'harbor.paginator'
|
||||
]);
|
||||
})();
|
||||
|
@ -26,7 +26,7 @@
|
||||
|
||||
return LogResult;
|
||||
|
||||
function LogResult(queryParams) {
|
||||
function LogResult(queryParams, page, pageSize) {
|
||||
var projectId = queryParams.projectId;
|
||||
var username = queryParams.username;
|
||||
var beginTimestamp = queryParams.beginTimestamp;
|
||||
@ -34,7 +34,7 @@
|
||||
var keywords = queryParams.keywords;
|
||||
|
||||
return $http
|
||||
.post('/api/projects/' + projectId + '/logs/filter', {
|
||||
.post('/api/projects/' + projectId + '/logs/filter?page=' + page + '&page_size=' + pageSize, {
|
||||
'begin_timestamp' : beginTimestamp,
|
||||
'end_timestamp' : endTimestamp,
|
||||
'keywords' : keywords,
|
||||
|
@ -26,9 +26,9 @@
|
||||
|
||||
return listReplicationJob;
|
||||
|
||||
function listReplicationJob(policyId, repository, status, startTime, endTime) {
|
||||
function listReplicationJob(policyId, repository, status, startTime, endTime, page, pageSize) {
|
||||
return $http
|
||||
.get('/api/jobs/replication/', {
|
||||
.get('/api/jobs/replication/?page=' + page + '&page_size=' + pageSize, {
|
||||
'params': {
|
||||
'policy_id': policyId,
|
||||
'repository': repository,
|
||||
|
@ -25,11 +25,11 @@
|
||||
|
||||
return ListRepository;
|
||||
|
||||
function ListRepository(projectId, q) {
|
||||
function ListRepository(projectId, q, page, pageSize) {
|
||||
$log.info('list repositories:' + projectId + ', q:' + q);
|
||||
|
||||
return $http
|
||||
.get('/api/repositories', {
|
||||
.get('/api/repositories?page=' + page + '&page_size=' + pageSize, {
|
||||
'params':{
|
||||
'project_id': projectId,
|
||||
'q': q
|
||||
|
41
tests/apitests/apilib/access_log.go
Normal file
41
tests/apitests/apilib/access_log.go
Normal file
@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Harbor API
|
||||
*
|
||||
* These APIs provide services for manipulating Harbor project.
|
||||
*
|
||||
* OpenAPI spec version: 0.3.0
|
||||
*
|
||||
* Generated by: https://github.com/swagger-api/swagger-codegen.git
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package apilib
|
||||
|
||||
type AccessLog struct {
|
||||
|
||||
// The ID of the log entry.
|
||||
LogId int32 `json:"log_id,omitempty"`
|
||||
|
||||
// Name of the repository in this log entry.
|
||||
RepoName string `json:"repo_name,omitempty"`
|
||||
|
||||
// Tag of the repository in this log entry.
|
||||
RepoTag string `json:"repo_tag,omitempty"`
|
||||
|
||||
// The operation against the repository in this log entry.
|
||||
Operation string `json:"operation,omitempty"`
|
||||
|
||||
// The time when this operation is triggered.
|
||||
OpTime string `json:"op_time,omitempty"`
|
||||
}
|
38
tests/apitests/apilib/access_log_filter.go
Normal file
38
tests/apitests/apilib/access_log_filter.go
Normal file
@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Harbor API
|
||||
*
|
||||
* These APIs provide services for manipulating Harbor project.
|
||||
*
|
||||
* OpenAPI spec version: 0.3.0
|
||||
*
|
||||
* Generated by: https://github.com/swagger-api/swagger-codegen.git
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package apilib
|
||||
|
||||
type AccessLogFilter struct {
|
||||
|
||||
// Relevant user's name that accessed this project.
|
||||
Username string `json:"username,omitempty"`
|
||||
|
||||
// Operation name specified when project created.
|
||||
Keywords string `json:"keywords,omitempty"`
|
||||
|
||||
// Begin timestamp for querying access logs.
|
||||
BeginTimestamp int64 `json:"begin_timestamp,omitempty"`
|
||||
|
||||
// End timestamp for querying accessl logs.
|
||||
EndTimestamp int64 `json:"end_timestamp,omitempty"`
|
||||
}
|
@ -1,23 +0,0 @@
|
||||
/*
|
||||
Copyright (c) 2016 VMware, Inc. All Rights Reserved.
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package HarborAPI
|
||||
|
||||
type AccessLog struct {
|
||||
Username string `json:"username,omitempty"`
|
||||
Keywords string `json:"keywords,omitempty"`
|
||||
BeginTimestamp int32 `json:"beginTimestamp,omitempty"`
|
||||
EndTimestamp int32 `json:"endTimestamp,omitempty"`
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
//Package HarborAPI
|
||||
//Package apilib
|
||||
//These APIs provide services for manipulating Harbor project.
|
||||
package HarborAPI
|
||||
package apilib
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
15
tests/apitests/apilib/harborlogout.bak
Normal file
15
tests/apitests/apilib/harborlogout.bak
Normal file
@ -0,0 +1,15 @@
|
||||
// HarborLogout.go
|
||||
package HarborAPI
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func (a HarborAPI) HarborLogout() (int, error) {
|
||||
|
||||
response, err := http.Get(a.basePath + "/logout")
|
||||
|
||||
defer response.Body.Close()
|
||||
|
||||
return response.StatusCode, err
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
// HarborLogout.go
|
||||
package HarborAPI
|
||||
package apilib
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
28
tests/apitests/apilib/harlogin.bak
Normal file
28
tests/apitests/apilib/harlogin.bak
Normal file
@ -0,0 +1,28 @@
|
||||
// HarborLogon.go
|
||||
package HarborAPI
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func (a HarborAPI) HarborLogin(user UsrInfo) (int, error) {
|
||||
|
||||
v := url.Values{}
|
||||
v.Set("principal", user.Name)
|
||||
v.Set("password", user.Passwd)
|
||||
|
||||
body := ioutil.NopCloser(strings.NewReader(v.Encode())) //endode v:[body struce]
|
||||
|
||||
client := &http.Client{}
|
||||
reqest, err := http.NewRequest("POST", a.basePath+"/login", body)
|
||||
|
||||
reqest.Header.Set("Content-Type", "application/x-www-form-urlencoded;param=value") //setting post head
|
||||
|
||||
resp, err := client.Do(reqest)
|
||||
defer resp.Body.Close() //close resp.Body
|
||||
|
||||
return resp.StatusCode, err
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
// HarborLogon.go
|
||||
package HarborAPI
|
||||
package apilib
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
|
50
tests/apitests/apilib/job_status.go
Normal file
50
tests/apitests/apilib/job_status.go
Normal file
@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Harbor API
|
||||
*
|
||||
* These APIs provide services for manipulating Harbor project.
|
||||
*
|
||||
* OpenAPI spec version: 0.3.0
|
||||
*
|
||||
* Generated by: https://github.com/swagger-api/swagger-codegen.git
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package apilib
|
||||
|
||||
type JobStatus struct {
|
||||
|
||||
// The job ID.
|
||||
Id int64 `json:"id,omitempty"`
|
||||
|
||||
// The status of the job.
|
||||
Status string `json:"status,omitempty"`
|
||||
|
||||
// The repository handled by the job.
|
||||
Repository string `json:"repository,omitempty"`
|
||||
|
||||
// The ID of the policy that triggered this job.
|
||||
PolicyId int64 `json:"policy_id,omitempty"`
|
||||
|
||||
// The operation of the job.
|
||||
Operation string `json:"operation,omitempty"`
|
||||
|
||||
// The repository's used tag list.
|
||||
Tags []Tags `json:"tags,omitempty"`
|
||||
|
||||
// The creation time of the job.
|
||||
CreationTime string `json:"creation_time,omitempty"`
|
||||
|
||||
// The update time of the job.
|
||||
UpdateTime string `json:"update_time,omitempty"`
|
||||
}
|
32
tests/apitests/apilib/password.go
Normal file
32
tests/apitests/apilib/password.go
Normal file
@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Harbor API
|
||||
*
|
||||
* These APIs provide services for manipulating Harbor project.
|
||||
*
|
||||
* OpenAPI spec version: 0.3.0
|
||||
*
|
||||
* Generated by: https://github.com/swagger-api/swagger-codegen.git
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package apilib
|
||||
|
||||
type Password struct {
|
||||
|
||||
// The user's existing password.
|
||||
OldPassword string `json:"old_password,omitempty"`
|
||||
|
||||
// New password for marking as to be updated.
|
||||
NewPassword string `json:"new_password,omitempty"`
|
||||
}
|
@ -1,15 +1,62 @@
|
||||
package HarborAPI
|
||||
/*
|
||||
* Harbor API
|
||||
*
|
||||
* These APIs provide services for manipulating Harbor project.
|
||||
*
|
||||
* OpenAPI spec version: 0.3.0
|
||||
*
|
||||
* Generated by: https://github.com/swagger-api/swagger-codegen.git
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
import ()
|
||||
package apilib
|
||||
|
||||
type Project struct {
|
||||
ProjectId int32 `json:"id,omitempty"`
|
||||
OwnerId int32 `json:"owner_id,omitempty"`
|
||||
ProjectName string `json:"project_name,omitempty"`
|
||||
|
||||
// Project ID
|
||||
ProjectId int32 `json:"project_id,omitempty"`
|
||||
|
||||
// The owner ID of the project always means the creator of the project.
|
||||
OwnerId int32 `json:"owner_id,omitempty"`
|
||||
|
||||
// The name of the project.
|
||||
ProjectName string `json:"project_name,omitempty"`
|
||||
|
||||
// The creation time of the project.
|
||||
CreationTime string `json:"creation_time,omitempty"`
|
||||
Deleted int32 `json:"deleted,omitempty"`
|
||||
UserId int32 `json:"user_id,omitempty"`
|
||||
OwnerName string `json:"owner_name,omitempty"`
|
||||
Public bool `json:"public,omitempty"`
|
||||
Togglable bool `json:"togglable,omitempty"`
|
||||
|
||||
// The update time of the project.
|
||||
UpdateTime string `json:"update_time,omitempty"`
|
||||
|
||||
// A deletion mark of the project (1 means it's deleted, 0 is not)
|
||||
Deleted int32 `json:"deleted,omitempty"`
|
||||
|
||||
// A relation field to the user table.
|
||||
UserId int32 `json:"user_id,omitempty"`
|
||||
|
||||
// The owner name of the project.
|
||||
OwnerName string `json:"owner_name,omitempty"`
|
||||
|
||||
// The public status of the project.
|
||||
Public bool `json:"public,omitempty"`
|
||||
|
||||
// Correspond to the UI about whether the project's publicity is updatable (for UI)
|
||||
Togglable bool `json:"togglable,omitempty"`
|
||||
|
||||
// The role ID of the current user who triggered the API (for UI)
|
||||
CurrentUserRoleId int32 `json:"current_user_role_id,omitempty"`
|
||||
|
||||
// The number of the repositories under this project.
|
||||
RepoCount int32 `json:"repo_count,omitempty"`
|
||||
}
|
||||
|
@ -1,9 +0,0 @@
|
||||
package HarborAPI
|
||||
|
||||
import ()
|
||||
|
||||
type Project4Search struct {
|
||||
ProjectId int32 `json:"id,omitempty"`
|
||||
ProjectName string `json:"name,omitempty"`
|
||||
Public int32 `json:"public,omitempty"`
|
||||
}
|
62
tests/apitests/apilib/rep_policy.go
Normal file
62
tests/apitests/apilib/rep_policy.go
Normal file
@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Harbor API
|
||||
*
|
||||
* These APIs provide services for manipulating Harbor project.
|
||||
*
|
||||
* OpenAPI spec version: 0.3.0
|
||||
*
|
||||
* Generated by: https://github.com/swagger-api/swagger-codegen.git
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package apilib
|
||||
|
||||
type RepPolicy struct {
|
||||
|
||||
// The policy ID.
|
||||
Id int64 `json:"id,omitempty"`
|
||||
|
||||
// The project ID.
|
||||
ProjectId int64 `json:"project_id,omitempty"`
|
||||
|
||||
// The project name.
|
||||
ProjectName string `json:"project_name,omitempty"`
|
||||
|
||||
// The target ID.
|
||||
TargetId int64 `json:"target_id,omitempty"`
|
||||
|
||||
// The target name.
|
||||
TargetName string `json:"target_name,omitempty"`
|
||||
|
||||
// The policy name.
|
||||
Name string `json:"name,omitempty"`
|
||||
|
||||
// The policy's enabled status.
|
||||
Enabled int32 `json:"enabled,omitempty"`
|
||||
|
||||
// The description of the policy.
|
||||
Description string `json:"description,omitempty"`
|
||||
|
||||
// The cron string for schedule job.
|
||||
CronStr string `json:"cron_str,omitempty"`
|
||||
|
||||
// The start time of the policy.
|
||||
StartTime string `json:"start_time,omitempty"`
|
||||
|
||||
// The create time of the policy.
|
||||
CreationTime string `json:"creation_time,omitempty"`
|
||||
|
||||
// The update time of the policy.
|
||||
UpdateTime string `json:"update_time,omitempty"`
|
||||
}
|
29
tests/apitests/apilib/rep_policy_enablement_req.go
Normal file
29
tests/apitests/apilib/rep_policy_enablement_req.go
Normal file
@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Harbor API
|
||||
*
|
||||
* These APIs provide services for manipulating Harbor project.
|
||||
*
|
||||
* OpenAPI spec version: 0.3.0
|
||||
*
|
||||
* Generated by: https://github.com/swagger-api/swagger-codegen.git
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package apilib
|
||||
|
||||
type RepPolicyEnablementReq struct {
|
||||
|
||||
// The policy enablement flag.
|
||||
Enabled int32 `json:"enabled,omitempty"`
|
||||
}
|
35
tests/apitests/apilib/rep_policy_post.go
Normal file
35
tests/apitests/apilib/rep_policy_post.go
Normal file
@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Harbor API
|
||||
*
|
||||
* These APIs provide services for manipulating Harbor project.
|
||||
*
|
||||
* OpenAPI spec version: 0.3.0
|
||||
*
|
||||
* Generated by: https://github.com/swagger-api/swagger-codegen.git
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package apilib
|
||||
|
||||
type RepPolicyPost struct {
|
||||
|
||||
// The project ID.
|
||||
ProjectId int64 `json:"project_id,omitempty"`
|
||||
|
||||
// The target ID.
|
||||
TargetId int64 `json:"target_id,omitempty"`
|
||||
|
||||
// The policy name.
|
||||
Name string `json:"name,omitempty"`
|
||||
}
|
41
tests/apitests/apilib/rep_policy_update.go
Normal file
41
tests/apitests/apilib/rep_policy_update.go
Normal file
@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Harbor API
|
||||
*
|
||||
* These APIs provide services for manipulating Harbor project.
|
||||
*
|
||||
* OpenAPI spec version: 0.3.0
|
||||
*
|
||||
* Generated by: https://github.com/swagger-api/swagger-codegen.git
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package apilib
|
||||
|
||||
type RepPolicyUpdate struct {
|
||||
|
||||
// The target ID.
|
||||
TargetId int64 `json:"target_id,omitempty"`
|
||||
|
||||
// The policy name.
|
||||
Name string `json:"name,omitempty"`
|
||||
|
||||
// The policy's enabled status.
|
||||
Enabled int32 `json:"enabled,omitempty"`
|
||||
|
||||
// The description of the policy.
|
||||
Description string `json:"description,omitempty"`
|
||||
|
||||
// The cron string for schedule job.
|
||||
CronStr string `json:"cron_str,omitempty"`
|
||||
}
|
50
tests/apitests/apilib/rep_target.go
Normal file
50
tests/apitests/apilib/rep_target.go
Normal file
@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Harbor API
|
||||
*
|
||||
* These APIs provide services for manipulating Harbor project.
|
||||
*
|
||||
* OpenAPI spec version: 0.3.0
|
||||
*
|
||||
* Generated by: https://github.com/swagger-api/swagger-codegen.git
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package apilib
|
||||
|
||||
type RepTarget struct {
|
||||
|
||||
// The target ID.
|
||||
Id int64 `json:"id,omitempty"`
|
||||
|
||||
// The target address URL string.
|
||||
Endpoint string `json:"endpoint,omitempty"`
|
||||
|
||||
// The target name.
|
||||
Name string `json:"name,omitempty"`
|
||||
|
||||
// The target server username.
|
||||
Username string `json:"username,omitempty"`
|
||||
|
||||
// The target server password.
|
||||
Password string `json:"password,omitempty"`
|
||||
|
||||
// Reserved field.
|
||||
Type_ int32 `json:"type,omitempty"`
|
||||
|
||||
// The create time of the policy.
|
||||
CreationTime string `json:"creation_time,omitempty"`
|
||||
|
||||
// The update time of the policy.
|
||||
UpdateTime string `json:"update_time,omitempty"`
|
||||
}
|
38
tests/apitests/apilib/rep_target_post.go
Normal file
38
tests/apitests/apilib/rep_target_post.go
Normal file
@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Harbor API
|
||||
*
|
||||
* These APIs provide services for manipulating Harbor project.
|
||||
*
|
||||
* OpenAPI spec version: 0.3.0
|
||||
*
|
||||
* Generated by: https://github.com/swagger-api/swagger-codegen.git
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package apilib
|
||||
|
||||
type RepTargetPost struct {
|
||||
|
||||
// The target address URL string.
|
||||
Endpoint string `json:"endpoint,omitempty"`
|
||||
|
||||
// The target name.
|
||||
Name string `json:"name,omitempty"`
|
||||
|
||||
// The target server username.
|
||||
Username string `json:"username,omitempty"`
|
||||
|
||||
// The target server password.
|
||||
Password string `json:"password,omitempty"`
|
||||
}
|
@ -1,16 +1,50 @@
|
||||
package HarborAPI
|
||||
/*
|
||||
* Harbor API
|
||||
*
|
||||
* These APIs provide services for manipulating Harbor project.
|
||||
*
|
||||
* OpenAPI spec version: 0.3.0
|
||||
*
|
||||
* Generated by: https://github.com/swagger-api/swagger-codegen.git
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
package apilib
|
||||
|
||||
type Repository struct {
|
||||
Id string `json:"id,omitempty"`
|
||||
Parent string `json:"parent,omitempty"`
|
||||
Created time.Time `json:"created,omitempty"`
|
||||
DurationDays string `json:"duration_days,omitempty"`
|
||||
Author string `json:"author,omitempty"`
|
||||
Architecture string `json:"architecture,omitempty"`
|
||||
DockerVersion string `json:"docker_version,omitempty"`
|
||||
Os string `json:"os,omitempty"`
|
||||
|
||||
// Repository ID
|
||||
Id string `json:"id,omitempty"`
|
||||
|
||||
// Parent of the image.
|
||||
Parent string `json:"parent,omitempty"`
|
||||
|
||||
// Repository create time.
|
||||
Created string `json:"created,omitempty"`
|
||||
|
||||
// Duration days of the image.
|
||||
DurationDays string `json:"duration_days,omitempty"`
|
||||
|
||||
// Author of the image.
|
||||
Author string `json:"author,omitempty"`
|
||||
|
||||
// Architecture of the image.
|
||||
Architecture string `json:"architecture,omitempty"`
|
||||
|
||||
// Docker version of the image.
|
||||
DockerVersion string `json:"docker_version,omitempty"`
|
||||
|
||||
// OS of the image.
|
||||
Os string `json:"os,omitempty"`
|
||||
}
|
||||
|
@ -1,9 +0,0 @@
|
||||
package HarborAPI
|
||||
|
||||
type Repository4Search struct {
|
||||
ProjectId int32 `json:"project_id,omitempty"`
|
||||
ProjectName string `json:"project_name,omitempty"`
|
||||
ProjectPublic int32 `json:"project_public,omitempty"`
|
||||
RepoName string `json:"repository_name,omitempty"`
|
||||
}
|
||||
|
@ -1,7 +1,35 @@
|
||||
package HarborAPI
|
||||
/*
|
||||
* Harbor API
|
||||
*
|
||||
* These APIs provide services for manipulating Harbor project.
|
||||
*
|
||||
* OpenAPI spec version: 0.3.0
|
||||
*
|
||||
* Generated by: https://github.com/swagger-api/swagger-codegen.git
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package apilib
|
||||
|
||||
type Role struct {
|
||||
RoleId int32 `json:"role_id,omitempty"`
|
||||
|
||||
// ID in table.
|
||||
RoleId int32 `json:"role_id,omitempty"`
|
||||
|
||||
// Description of permissions for the role.
|
||||
RoleCode string `json:"role_code,omitempty"`
|
||||
|
||||
// Name the the role.
|
||||
RoleName string `json:"role_name,omitempty"`
|
||||
}
|
||||
|
32
tests/apitests/apilib/role_param.go
Normal file
32
tests/apitests/apilib/role_param.go
Normal file
@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Harbor API
|
||||
*
|
||||
* These APIs provide services for manipulating Harbor project.
|
||||
*
|
||||
* OpenAPI spec version: 0.3.0
|
||||
*
|
||||
* Generated by: https://github.com/swagger-api/swagger-codegen.git
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package apilib
|
||||
|
||||
type RoleParam struct {
|
||||
|
||||
// Role ID for updating project role member.
|
||||
Roles []int32 `json:"roles,omitempty"`
|
||||
|
||||
// Username relevant to a project role member.
|
||||
Username string `json:"username,omitempty"`
|
||||
}
|
@ -1,6 +0,0 @@
|
||||
package HarborAPI
|
||||
|
||||
type RoleParam struct {
|
||||
Roles []int32 `json:"roles,omitempty"`
|
||||
UserName string `json:"user_name,omitempty"`
|
||||
}
|
@ -1,8 +1,34 @@
|
||||
package HarborAPI
|
||||
/*
|
||||
* Harbor API
|
||||
*
|
||||
* These APIs provide services for manipulating Harbor project.
|
||||
*
|
||||
* OpenAPI spec version: 0.3.0
|
||||
*
|
||||
* Generated by: https://github.com/swagger-api/swagger-codegen.git
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
import ()
|
||||
package apilib
|
||||
|
||||
import()
|
||||
|
||||
type Search struct {
|
||||
Projects []Project4Search `json:"project,omitempty"`
|
||||
Repositories []Repository4Search `json:"repository,omitempty"`
|
||||
|
||||
// Search results of the projects that matched the filter keywords.
|
||||
Projects []SearchProject `json:"project,omitempty"`
|
||||
|
||||
// Search results of the repositories that matched the filter keywords.
|
||||
Repositories []SearchRepository `json:"repository,omitempty"`
|
||||
}
|
||||
|
35
tests/apitests/apilib/search_project.go
Normal file
35
tests/apitests/apilib/search_project.go
Normal file
@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Harbor API
|
||||
*
|
||||
* These APIs provide services for manipulating Harbor project.
|
||||
*
|
||||
* OpenAPI spec version: 0.3.0
|
||||
*
|
||||
* Generated by: https://github.com/swagger-api/swagger-codegen.git
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package apilib
|
||||
|
||||
type SearchProject struct {
|
||||
|
||||
// The ID of project
|
||||
Id int64 `json:"id,omitempty"`
|
||||
|
||||
// The name of the project
|
||||
Name string `json:"name,omitempty"`
|
||||
|
||||
// The flag to indicate the publicity of the project (1 is public, 0 is non-public)
|
||||
Public int32 `json:"public,omitempty"`
|
||||
}
|
38
tests/apitests/apilib/search_repository.go
Normal file
38
tests/apitests/apilib/search_repository.go
Normal file
@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Harbor API
|
||||
*
|
||||
* These APIs provide services for manipulating Harbor project.
|
||||
*
|
||||
* OpenAPI spec version: 0.3.0
|
||||
*
|
||||
* Generated by: https://github.com/swagger-api/swagger-codegen.git
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package apilib
|
||||
|
||||
type SearchRepository struct {
|
||||
|
||||
// The name of the repository
|
||||
RepositoryName string `json:"repository_name,omitempty"`
|
||||
|
||||
// The name of the project that the repository belongs to
|
||||
ProjectName string `json:"project_name,omitempty"`
|
||||
|
||||
// The ID of the project that the repository belongs to
|
||||
ProjectId int32 `json:"project_id,omitempty"`
|
||||
|
||||
// The flag to indicate the publicity of the project that the repository belongs to (1 is public, 0 is not)
|
||||
ProjectPublic int32 `json:"project_public,omitempty"`
|
||||
}
|
44
tests/apitests/apilib/statistic_map.go
Normal file
44
tests/apitests/apilib/statistic_map.go
Normal file
@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Harbor API
|
||||
*
|
||||
* These APIs provide services for manipulating Harbor project.
|
||||
*
|
||||
* OpenAPI spec version: 0.3.0
|
||||
*
|
||||
* Generated by: https://github.com/swagger-api/swagger-codegen.git
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package apilib
|
||||
|
||||
type StatisticMap struct {
|
||||
|
||||
// The count of the projects which the user is a member of.
|
||||
MyProjectCount int32 `json:"my_project_count,omitempty"`
|
||||
|
||||
// The count of the repositories belonging to the projects which the user is a member of.
|
||||
MyRepoCount int32 `json:"my_repo_count,omitempty"`
|
||||
|
||||
// The count of the public projects.
|
||||
PublicProjectCount int32 `json:"public_project_count,omitempty"`
|
||||
|
||||
// The count of the public repositories belonging to the public projects which the user is a member of.
|
||||
PublicRepoCount int32 `json:"public_repo_count,omitempty"`
|
||||
|
||||
// The count of the total projects, only be seen when the is admin.
|
||||
TotalProjectCount int32 `json:"total_project_count,omitempty"`
|
||||
|
||||
// The count of the total repositories, only be seen when the user is admin.
|
||||
TotalRepoCount int32 `json:"total_repo_count,omitempty"`
|
||||
}
|
29
tests/apitests/apilib/tags.go
Normal file
29
tests/apitests/apilib/tags.go
Normal file
@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Harbor API
|
||||
*
|
||||
* These APIs provide services for manipulating Harbor project.
|
||||
*
|
||||
* OpenAPI spec version: 0.3.0
|
||||
*
|
||||
* Generated by: https://github.com/swagger-api/swagger-codegen.git
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package apilib
|
||||
|
||||
type Tags struct {
|
||||
|
||||
// The repository's used tag.
|
||||
Tag string `json:"tag,omitempty"`
|
||||
}
|
32
tests/apitests/apilib/top_repo.go
Normal file
32
tests/apitests/apilib/top_repo.go
Normal file
@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Harbor API
|
||||
*
|
||||
* These APIs provide services for manipulating Harbor project.
|
||||
*
|
||||
* OpenAPI spec version: 0.3.0
|
||||
*
|
||||
* Generated by: https://github.com/swagger-api/swagger-codegen.git
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package apilib
|
||||
|
||||
type TopRepo struct {
|
||||
|
||||
// The name of the repo
|
||||
RepoName string `json:"repo_name,omitempty"`
|
||||
|
||||
// The access count of the repo
|
||||
AccessCount int32 `json:"access_count,omitempty"`
|
||||
}
|
@ -1,11 +1,41 @@
|
||||
package HarborAPI
|
||||
/*
|
||||
* Harbor API
|
||||
*
|
||||
* These APIs provide services for manipulating Harbor project.
|
||||
*
|
||||
* OpenAPI spec version: 0.3.0
|
||||
*
|
||||
* Generated by: https://github.com/swagger-api/swagger-codegen.git
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package apilib
|
||||
|
||||
type User struct {
|
||||
UserId int32 `json:"user_id,omitempty"`
|
||||
|
||||
// The ID of the user.
|
||||
UserId int32 `json:"user_id,omitempty"`
|
||||
|
||||
Username string `json:"username,omitempty"`
|
||||
Email string `json:"email,omitempty"`
|
||||
|
||||
Email string `json:"email,omitempty"`
|
||||
|
||||
Password string `json:"password,omitempty"`
|
||||
|
||||
Realname string `json:"realname,omitempty"`
|
||||
Comment string `json:"comment,omitempty"`
|
||||
Deleted int32 `json:"deleted,omitempty"`
|
||||
|
||||
Comment string `json:"comment,omitempty"`
|
||||
|
||||
Deleted int32 `json:"deleted,omitempty"`
|
||||
}
|
||||
|
@ -1,95 +0,0 @@
|
||||
package HarborAPItest
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"testing"
|
||||
"github.com/vmware/harbor/tests/apitests/apilib"
|
||||
)
|
||||
|
||||
func TestAddProject(t *testing.T) {
|
||||
|
||||
fmt.Println("Test for Project Add (ProjectsPost) API")
|
||||
assert := assert.New(t)
|
||||
|
||||
apiTest := HarborAPI.NewHarborAPI()
|
||||
|
||||
//prepare for test
|
||||
adminEr := &HarborAPI.UsrInfo{"admin", "Harbor1234"}
|
||||
admin := &HarborAPI.UsrInfo{"admin", "Harbor12345"}
|
||||
|
||||
prjUsr := &HarborAPI.UsrInfo{"unknown", "unknown"}
|
||||
|
||||
var project HarborAPI.Project
|
||||
project.ProjectName = "testproject"
|
||||
project.Public = true
|
||||
|
||||
//case 1: admin login fail, expect project creation fail.
|
||||
fmt.Println("case 1: admin login fail, expect project creation fail.")
|
||||
resault, err := apiTest.HarborLogin(*adminEr)
|
||||
if err != nil {
|
||||
t.Error("Error while admin login", err.Error())
|
||||
t.Log(err)
|
||||
} else {
|
||||
assert.Equal(resault, int(401), "Admin login status should be 401")
|
||||
//t.Log(resault)
|
||||
}
|
||||
|
||||
resault, err = apiTest.ProjectsPost(*prjUsr, project)
|
||||
if err != nil {
|
||||
t.Error("Error while creat project", err.Error())
|
||||
t.Log(err)
|
||||
} else {
|
||||
assert.Equal(resault, int(401), "Case 1: Project creation status should be 401")
|
||||
//t.Log(resault)
|
||||
}
|
||||
|
||||
//case 2: admin successful login, expect project creation success.
|
||||
fmt.Println("case 2: admin successful login, expect project creation success.")
|
||||
resault, err = apiTest.HarborLogin(*admin)
|
||||
if err != nil {
|
||||
t.Error("Error while admin login", err.Error())
|
||||
t.Log(err)
|
||||
} else {
|
||||
assert.Equal(resault, int(200), "Admin login status should be 200")
|
||||
//t.Log(resault)
|
||||
}
|
||||
if resault != 200 {
|
||||
t.Log(resault)
|
||||
} else {
|
||||
prjUsr = admin
|
||||
}
|
||||
|
||||
resault, err = apiTest.ProjectsPost(*prjUsr, project)
|
||||
if err != nil {
|
||||
t.Error("Error while creat project", err.Error())
|
||||
t.Log(err)
|
||||
} else {
|
||||
assert.Equal(resault, int(201), "Case 2: Project creation status should be 201")
|
||||
//t.Log(resault)
|
||||
}
|
||||
|
||||
//case 3: duplicate project name, create project fail
|
||||
fmt.Println("case 3: duplicate project name, create project fail")
|
||||
resault, err = apiTest.ProjectsPost(*prjUsr, project)
|
||||
if err != nil {
|
||||
t.Error("Error while creat project", err.Error())
|
||||
t.Log(err)
|
||||
} else {
|
||||
assert.Equal(resault, int(409), "Case 3: Project creation status should be 409")
|
||||
//t.Log(resault)
|
||||
}
|
||||
|
||||
//resault1, err := apiTest.HarborLogout()
|
||||
//if err != nil {
|
||||
// t.Error("Error while admin logout", err.Error())
|
||||
// t.Log(err)
|
||||
//} else {
|
||||
// assert.Equal(resault1, int(200), "Admin logout status")
|
||||
// //t.Log(resault)
|
||||
//}
|
||||
//if resault1 != 200 {
|
||||
// t.Log(resault)
|
||||
//}
|
||||
|
||||
}
|
@ -1,130 +0,0 @@
|
||||
package HarborAPItest
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/vmware/harbor/tests/apitests/apilib"
|
||||
)
|
||||
|
||||
func TestRepositoryDelete(t *testing.T) {
|
||||
fmt.Println("Test for Project Delete (ProjectDelete) API")
|
||||
assert := assert.New(t)
|
||||
|
||||
//prepare for test
|
||||
adminEr := &HarborAPI.UsrInfo{"admin", "Harbor1234"}
|
||||
admin := &HarborAPI.UsrInfo{"admin", "Harbor12345"}
|
||||
prjUsr := &HarborAPI.UsrInfo{"unknown", "unknown"}
|
||||
|
||||
fmt.Println("Checking repository status...")
|
||||
apiTest := HarborAPI.NewHarborAPI()
|
||||
var searchResault HarborAPI.Search
|
||||
searchResault, err := apiTest.SearchGet("library")
|
||||
//fmt.Printf("%+v\n", resault)
|
||||
|
||||
if err != nil {
|
||||
t.Error("Error while search project or repository", err.Error())
|
||||
t.Log(err)
|
||||
} else {
|
||||
//assert.Equal(searchResault.Repositories[0].RepoName, "library/docker", "1st repo name should be")
|
||||
if !assert.Equal(searchResault.Repositories[0].RepoName, "library/docker", "1st repo name should be") {
|
||||
t.Error("fail to find repo 'library/docker'", err.Error())
|
||||
t.Log(err)
|
||||
} else {
|
||||
fmt.Println("repo 'library/docker' exit")
|
||||
}
|
||||
//assert.Equal(searchResault.Repositories[1].RepoName, "library/hello-world", "2nd repo name should be")
|
||||
if !assert.Equal(searchResault.Repositories[1].RepoName, "library/hello-world", "2nd repo name should be") {
|
||||
t.Error("fail to find repo 'library/hello-world'", err.Error())
|
||||
t.Log(err)
|
||||
} else {
|
||||
fmt.Println("repo 'library/hello-world' exit")
|
||||
}
|
||||
|
||||
//t.Log(resault)
|
||||
}
|
||||
|
||||
//case 1: admin login fail, expect repo delete fail.
|
||||
fmt.Println("case 1: admin login fail, expect repo delete fail.")
|
||||
|
||||
resault, err := apiTest.HarborLogin(*adminEr)
|
||||
if err != nil {
|
||||
t.Error("Error while admin login", err.Error())
|
||||
t.Log(err)
|
||||
} else {
|
||||
assert.Equal(resault, int(401), "Admin login status should be 401")
|
||||
//t.Log(resault)
|
||||
}
|
||||
if resault != 401 {
|
||||
t.Log(resault)
|
||||
} else {
|
||||
prjUsr = adminEr
|
||||
}
|
||||
|
||||
resault, err = apiTest.RepositoriesDelete(*prjUsr, "library/docker", "")
|
||||
if err != nil {
|
||||
t.Error("Error while delete repository", err.Error())
|
||||
t.Log(err)
|
||||
} else {
|
||||
assert.Equal(resault, int(401), "Case 1: Repository delete status should be 401")
|
||||
//t.Log(resault)
|
||||
}
|
||||
|
||||
//case 2: admin successful login, expect repository delete success.
|
||||
fmt.Println("case 2: admin successful login, expect repository delete success.")
|
||||
resault, err = apiTest.HarborLogin(*admin)
|
||||
if err != nil {
|
||||
t.Error("Error while admin login", err.Error())
|
||||
t.Log(err)
|
||||
} else {
|
||||
assert.Equal(resault, int(200), "Admin login status should be 200")
|
||||
//t.Log(resault)
|
||||
}
|
||||
if resault != 200 {
|
||||
t.Log(resault)
|
||||
} else {
|
||||
prjUsr = admin
|
||||
}
|
||||
|
||||
resault, err = apiTest.RepositoriesDelete(*prjUsr, "library/docker", "")
|
||||
if err != nil {
|
||||
t.Error("Error while delete repository", err.Error())
|
||||
t.Log(err)
|
||||
} else {
|
||||
if assert.Equal(resault, int(200), "Case 2: Repository delete status should be 200") {
|
||||
fmt.Println("Repository 'library/docker' delete success.")
|
||||
}
|
||||
//t.Log(resault)
|
||||
}
|
||||
|
||||
resault, err = apiTest.RepositoriesDelete(*prjUsr, "library/hello-world", "")
|
||||
if err != nil {
|
||||
t.Error("Error while delete repository", err.Error())
|
||||
t.Log(err)
|
||||
} else {
|
||||
if assert.Equal(resault, int(200), "Case 2: Repository delete status should be 200") {
|
||||
fmt.Println("Repository 'hello-world' delete success.")
|
||||
}
|
||||
//t.Log(resault)
|
||||
}
|
||||
|
||||
//case 3: delete one repo not exit, expect repo delete fail.
|
||||
fmt.Println("case 3: delete one repo not exit, expect repo delete fail.")
|
||||
|
||||
resault, err = apiTest.RepositoriesDelete(*prjUsr, "library/hello-world", "")
|
||||
if err != nil {
|
||||
t.Error("Error while delete repository", err.Error())
|
||||
t.Log(err)
|
||||
} else {
|
||||
if assert.Equal(resault, int(404), "Case 3: Repository delete status should be 404") {
|
||||
fmt.Println("Repository 'hello-world' not exit.")
|
||||
}
|
||||
//t.Log(resault)
|
||||
}
|
||||
|
||||
//if resault.Response.StatusCode != 200 {
|
||||
// t.Log(resault.Response)
|
||||
//}
|
||||
|
||||
}
|
@ -1,31 +0,0 @@
|
||||
package HarborAPItest
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"testing"
|
||||
"github.com/vmware/harbor/tests/apitests/apilib"
|
||||
)
|
||||
|
||||
func TestSearch(t *testing.T) {
|
||||
fmt.Println("Test for Search (SearchGet) API")
|
||||
assert := assert.New(t)
|
||||
|
||||
apiTest := HarborAPI.NewHarborAPI()
|
||||
var resault HarborAPI.Search
|
||||
resault, err := apiTest.SearchGet("library")
|
||||
//fmt.Printf("%+v\n", resault)
|
||||
if err != nil {
|
||||
t.Error("Error while search project or repository", err.Error())
|
||||
t.Log(err)
|
||||
} else {
|
||||
assert.Equal(resault.Projects[0].ProjectId, int32(1), "Project id should be equal")
|
||||
assert.Equal(resault.Projects[0].ProjectName, "library", "Project name should be library")
|
||||
assert.Equal(resault.Projects[0].Public, int32(1), "Project public status should be 1 (true)")
|
||||
//t.Log(resault)
|
||||
}
|
||||
//if resault.Response.StatusCode != 200 {
|
||||
// t.Log(resault.Response)
|
||||
//}
|
||||
|
||||
}
|
23
tests/docker-compose.test.yml
Normal file
23
tests/docker-compose.test.yml
Normal file
@ -0,0 +1,23 @@
|
||||
version: '2'
|
||||
services:
|
||||
registry:
|
||||
image: library/registry:2.4.0
|
||||
restart: always
|
||||
volumes:
|
||||
- /data/registry:/storage
|
||||
- ./config/registry/:/etc/registry/
|
||||
environment:
|
||||
- GODEBUG=netdns=cgo
|
||||
ports:
|
||||
- 5000:5000
|
||||
command:
|
||||
["serve", "/etc/registry/config.yml"]
|
||||
mysql:
|
||||
build: ./db/
|
||||
restart: always
|
||||
volumes:
|
||||
- /data/database:/var/lib/mysql
|
||||
env_file:
|
||||
- ./config/db/env
|
||||
ports:
|
||||
- 3306:3306
|
@ -1,10 +1,9 @@
|
||||
docker pull hello-world
|
||||
docker pull docker
|
||||
docker login -u admin -p Harbor12345 127.0.0.1
|
||||
#!/bin/bash
|
||||
|
||||
docker tag hello-world 127.0.0.1/library/hello-world
|
||||
docker push 127.0.0.1/library/hello-world
|
||||
cp tests/docker-compose.test.yml Deploy/.
|
||||
|
||||
docker tag docker 127.0.0.1/library/docker
|
||||
docker push 127.0.0.1/library/docker
|
||||
mkdir /etc/ui
|
||||
cp Deploy/config/ui/private_key.pem /etc/ui/.
|
||||
|
||||
mkdir conf
|
||||
cp Deploy/config/ui/app.conf conf/.
|
||||
|
@ -1,9 +0,0 @@
|
||||
package auth
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestMain(t *testing.T) {
|
||||
}
|
||||
|
91
utils/registry/auth/authorizer_test.go
Normal file
91
utils/registry/auth/authorizer_test.go
Normal file
@ -0,0 +1,91 @@
|
||||
/*
|
||||
Copyright (c) 2016 VMware, Inc. All Rights Reserved.
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package auth
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/docker/distribution/registry/client/auth"
|
||||
)
|
||||
|
||||
func TestNewAuthorizerStore(t *testing.T) {
|
||||
server := newRegistryServer()
|
||||
defer server.Close()
|
||||
|
||||
_, err := NewAuthorizerStore(server.URL, false, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create authorizer store: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
type simpleAuthorizer struct {
|
||||
}
|
||||
|
||||
func (s *simpleAuthorizer) Scheme() string {
|
||||
return "bearer"
|
||||
}
|
||||
|
||||
func (s *simpleAuthorizer) Authorize(req *http.Request,
|
||||
params map[string]string) error {
|
||||
req.Header.Set("Authorization", "Bearer token")
|
||||
return nil
|
||||
}
|
||||
|
||||
func TestModify(t *testing.T) {
|
||||
authorizer := &simpleAuthorizer{}
|
||||
challenge := auth.Challenge{
|
||||
Scheme: "bearer",
|
||||
}
|
||||
|
||||
as := &AuthorizerStore{
|
||||
authorizers: []Authorizer{authorizer},
|
||||
challenges: []auth.Challenge{challenge},
|
||||
}
|
||||
|
||||
req, err := http.NewRequest("GET", "http://example.com", nil)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create request: %v", err)
|
||||
}
|
||||
|
||||
if err = as.Modify(req); err != nil {
|
||||
t.Fatalf("failed to modify request: %v", err)
|
||||
}
|
||||
|
||||
header := req.Header.Get("Authorization")
|
||||
if len(header) == 0 {
|
||||
t.Fatal("\"Authorization\" header not found")
|
||||
}
|
||||
|
||||
if !strings.HasPrefix(header, "Bearer") {
|
||||
t.Fatal("\"Authorization\" header does not start with \"Bearer\"")
|
||||
}
|
||||
}
|
||||
|
||||
func newRegistryServer() *httptest.Server {
|
||||
mux := http.NewServeMux()
|
||||
mux.HandleFunc("/v2/", handlePing)
|
||||
|
||||
return httptest.NewServer(mux)
|
||||
}
|
||||
|
||||
func handlePing(w http.ResponseWriter, r *http.Request) {
|
||||
challenge := "Bearer realm=\"https://auth.docker.io/token\",service=\"registry.docker.io\""
|
||||
w.Header().Set("Www-Authenticate", challenge)
|
||||
w.WriteHeader(http.StatusUnauthorized)
|
||||
}
|
67
utils/registry/auth/credential_test.go
Normal file
67
utils/registry/auth/credential_test.go
Normal file
@ -0,0 +1,67 @@
|
||||
/*
|
||||
Copyright (c) 2016 VMware, Inc. All Rights Reserved.
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package auth
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestAddAuthorizationOfBasicAuthCredential(t *testing.T) {
|
||||
cred := NewBasicAuthCredential("usr", "pwd")
|
||||
req, err := http.NewRequest("GET", "http://example.com", nil)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create request: %v", err)
|
||||
}
|
||||
|
||||
cred.AddAuthorization(req)
|
||||
|
||||
usr, pwd, ok := req.BasicAuth()
|
||||
if !ok {
|
||||
t.Fatal("basic auth not found")
|
||||
}
|
||||
|
||||
if usr != "usr" {
|
||||
t.Errorf("unexpected username: %s != usr", usr)
|
||||
}
|
||||
|
||||
if pwd != "pwd" {
|
||||
t.Errorf("unexpected password: %s != pwd", pwd)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAddAuthorizationOfCookieCredential(t *testing.T) {
|
||||
cookie := &http.Cookie{
|
||||
Name: "name",
|
||||
Value: "value",
|
||||
}
|
||||
cred := NewCookieCredential(cookie)
|
||||
req, err := http.NewRequest("GET", "http://example.com", nil)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create request: %v", err)
|
||||
}
|
||||
|
||||
cred.AddAuthorization(req)
|
||||
|
||||
ck, err := req.Cookie("name")
|
||||
if err != nil {
|
||||
t.Fatalf("failed to get cookie: %v", err)
|
||||
}
|
||||
|
||||
if ck.Value != "value" {
|
||||
t.Errorf("unexpected value: %s != value", ck.Value)
|
||||
}
|
||||
}
|
@ -180,7 +180,9 @@ func (s *standardTokenAuthorizer) generateToken(realm, service string, scopes []
|
||||
return
|
||||
}
|
||||
|
||||
s.credential.AddAuthorization(r)
|
||||
if s.credential != nil {
|
||||
s.credential.AddAuthorization(r)
|
||||
}
|
||||
|
||||
resp, err := s.client.Do(r)
|
||||
if err != nil {
|
||||
|
81
utils/registry/auth/tokenauthorizer_test.go
Normal file
81
utils/registry/auth/tokenauthorizer_test.go
Normal file
@ -0,0 +1,81 @@
|
||||
/*
|
||||
Copyright (c) 2016 VMware, Inc. All Rights Reserved.
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package auth
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
token = "token"
|
||||
)
|
||||
|
||||
func TestAuthorizeOfStandardTokenAuthorizer(t *testing.T) {
|
||||
tokenServer := newTokenServer()
|
||||
defer tokenServer.Close()
|
||||
|
||||
authorizer := NewStandardTokenAuthorizer(nil, false, "repository", "library/ubuntu", "pull")
|
||||
req, err := http.NewRequest("GET", "http://registry", nil)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create request: %v", err)
|
||||
}
|
||||
|
||||
params := map[string]string{
|
||||
"realm": tokenServer.URL + "/token",
|
||||
}
|
||||
|
||||
if err := authorizer.Authorize(req, params); err != nil {
|
||||
t.Fatalf("failed to authorize request: %v", err)
|
||||
}
|
||||
|
||||
tk := req.Header.Get("Authorization")
|
||||
if tk != "Bearer "+token {
|
||||
t.Errorf("unexpected token: %s != %s", tk, "Bearer "+token)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSchemeOfStandardTokenAuthorizer(t *testing.T) {
|
||||
authorizer := &standardTokenAuthorizer{}
|
||||
if authorizer.Scheme() != "bearer" {
|
||||
t.Errorf("unexpected scheme: %s != %s", authorizer.Scheme(), "bearer")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func newTokenServer() *httptest.Server {
|
||||
mux := http.NewServeMux()
|
||||
mux.HandleFunc("/token", handleToken)
|
||||
|
||||
return httptest.NewServer(mux)
|
||||
}
|
||||
|
||||
func handleToken(w http.ResponseWriter, r *http.Request) {
|
||||
result := map[string]interface{}{}
|
||||
result["token"] = token
|
||||
result["expires_in"] = 300
|
||||
result["issued_at"] = time.Now().Format(time.RFC3339)
|
||||
|
||||
encoder := json.NewEncoder(w)
|
||||
if err := encoder.Encode(result); err != nil {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
w.Write([]byte(err.Error()))
|
||||
return
|
||||
}
|
||||
}
|
@ -4,6 +4,14 @@ import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestMain(t *testing.T) {
|
||||
}
|
||||
func TestError(t *testing.T) {
|
||||
err := &Error{
|
||||
StatusCode: 404,
|
||||
Detail: "not found",
|
||||
}
|
||||
|
||||
if err.Error() != "404 not found" {
|
||||
t.Fatalf("unexpected content: %s != %s",
|
||||
err.Error(), "404 not found")
|
||||
}
|
||||
}
|
||||
|
@ -20,17 +20,6 @@ import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Repository holds information about repository
|
||||
type Repository struct {
|
||||
Name string
|
||||
}
|
||||
|
||||
// GetProject parses the repository and return the name of project.
|
||||
func (r *Repository) GetProject() string {
|
||||
project, _ := ParseRepository(r.Name)
|
||||
return project
|
||||
}
|
||||
|
||||
// FormatEndpoint formats endpoint
|
||||
func FormatEndpoint(endpoint string) string {
|
||||
endpoint = strings.TrimSpace(endpoint)
|
||||
|
@ -21,7 +21,36 @@ import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestMain(t *testing.T) {
|
||||
func TestParseEndpoint(t *testing.T) {
|
||||
endpoint := "example.com"
|
||||
u, err := ParseEndpoint(endpoint)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to parse endpoint %s: %v", endpoint, err)
|
||||
}
|
||||
|
||||
if u.String() != "http://example.com" {
|
||||
t.Errorf("unexpected endpoint: %s != %s", endpoint, "http://example.com")
|
||||
}
|
||||
|
||||
endpoint = "https://example.com"
|
||||
u, err = ParseEndpoint(endpoint)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to parse endpoint %s: %v", endpoint, err)
|
||||
}
|
||||
|
||||
if u.String() != "https://example.com" {
|
||||
t.Errorf("unexpected endpoint: %s != %s", endpoint, "https://example.com")
|
||||
}
|
||||
|
||||
endpoint = " example.com/ "
|
||||
u, err = ParseEndpoint(endpoint)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to parse endpoint %s: %v", endpoint, err)
|
||||
}
|
||||
|
||||
if u.String() != "http://example.com" {
|
||||
t.Errorf("unexpected endpoint: %s != %s", endpoint, "http://example.com")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseRepository(t *testing.T) {
|
||||
@ -64,6 +93,16 @@ func TestParseRepository(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestEncrypt(t *testing.T) {
|
||||
content := "content"
|
||||
salt := "salt"
|
||||
result := Encrypt(content, salt)
|
||||
|
||||
if result != "dc79e76c88415c97eb089d9cc80b4ab0" {
|
||||
t.Errorf("unexpected result: %s != %s", result, "dc79e76c88415c97eb089d9cc80b4ab0")
|
||||
}
|
||||
}
|
||||
|
||||
func TestReversibleEncrypt(t *testing.T) {
|
||||
password := "password"
|
||||
key := "1234567890123456"
|
||||
|
1
vendor/github.com/Sirupsen/logrus/.gitignore
generated
vendored
1
vendor/github.com/Sirupsen/logrus/.gitignore
generated
vendored
@ -1 +0,0 @@
|
||||
logrus
|
7
vendor/github.com/Sirupsen/logrus/.travis.yml
generated
vendored
7
vendor/github.com/Sirupsen/logrus/.travis.yml
generated
vendored
@ -1,7 +0,0 @@
|
||||
language: go
|
||||
go:
|
||||
- 1.3
|
||||
- 1.4
|
||||
- tip
|
||||
install:
|
||||
- go get -t ./...
|
14
vendor/github.com/Sirupsen/logrus/CHANGELOG.md
generated
vendored
14
vendor/github.com/Sirupsen/logrus/CHANGELOG.md
generated
vendored
@ -1,9 +1,21 @@
|
||||
# 0.9.0 (Unreleased)
|
||||
# 0.10.0
|
||||
|
||||
* feature: Add a test hook (#180)
|
||||
* feature: `ParseLevel` is now case-insensitive (#326)
|
||||
* feature: `FieldLogger` interface that generalizes `Logger` and `Entry` (#308)
|
||||
* performance: avoid re-allocations on `WithFields` (#335)
|
||||
|
||||
# 0.9.0
|
||||
|
||||
* logrus/text_formatter: don't emit empty msg
|
||||
* logrus/hooks/airbrake: move out of main repository
|
||||
* logrus/hooks/sentry: move out of main repository
|
||||
* logrus/hooks/papertrail: move out of main repository
|
||||
* logrus/hooks/bugsnag: move out of main repository
|
||||
* logrus/core: run tests with `-race`
|
||||
* logrus/core: detect TTY based on `stderr`
|
||||
* logrus/core: support `WithError` on logger
|
||||
* logrus/core: Solaris support
|
||||
|
||||
# 0.8.7
|
||||
|
||||
|
67
vendor/github.com/Sirupsen/logrus/README.md
generated
vendored
67
vendor/github.com/Sirupsen/logrus/README.md
generated
vendored
@ -1,4 +1,4 @@
|
||||
# Logrus <img src="http://i.imgur.com/hTeVwmJ.png" width="40" height="40" alt=":walrus:" class="emoji" title=":walrus:"/> [![Build Status](https://travis-ci.org/Sirupsen/logrus.svg?branch=master)](https://travis-ci.org/Sirupsen/logrus) [![godoc reference](https://godoc.org/github.com/Sirupsen/logrus?status.png)][godoc]
|
||||
# Logrus <img src="http://i.imgur.com/hTeVwmJ.png" width="40" height="40" alt=":walrus:" class="emoji" title=":walrus:"/> [![Build Status](https://travis-ci.org/Sirupsen/logrus.svg?branch=master)](https://travis-ci.org/Sirupsen/logrus) [![GoDoc](https://godoc.org/github.com/Sirupsen/logrus?status.svg)](https://godoc.org/github.com/Sirupsen/logrus)
|
||||
|
||||
Logrus is a structured logger for Go (golang), completely API compatible with
|
||||
the standard library logger. [Godoc][godoc]. **Please note the Logrus API is not
|
||||
@ -12,7 +12,7 @@ plain text):
|
||||
|
||||
![Colored](http://i.imgur.com/PY7qMwd.png)
|
||||
|
||||
With `log.Formatter = new(logrus.JSONFormatter)`, for easy parsing by logstash
|
||||
With `log.SetFormatter(&log.JSONFormatter{})`, for easy parsing by logstash
|
||||
or Splunk:
|
||||
|
||||
```json
|
||||
@ -32,7 +32,7 @@ ocean","size":10,"time":"2014-03-10 19:57:38.562264131 -0400 EDT"}
|
||||
"time":"2014-03-10 19:57:38.562543128 -0400 EDT"}
|
||||
```
|
||||
|
||||
With the default `log.Formatter = new(&log.TextFormatter{})` when a TTY is not
|
||||
With the default `log.SetFormatter(&log.TextFormatter{})` when a TTY is not
|
||||
attached, the output is compatible with the
|
||||
[logfmt](http://godoc.org/github.com/kr/logfmt) format:
|
||||
|
||||
@ -204,7 +204,7 @@ Note: Syslog hook also support connecting to local syslog (Ex. "/dev/log" or "/v
|
||||
| [Airbrake "legacy"](https://github.com/gemnasium/logrus-airbrake-legacy-hook) | Send errors to an exception tracking service compatible with the Airbrake API V2. Uses [`airbrake-go`](https://github.com/tobi/airbrake-go) behind the scenes. |
|
||||
| [Papertrail](https://github.com/polds/logrus-papertrail-hook) | Send errors to the [Papertrail](https://papertrailapp.com) hosted logging service via UDP. |
|
||||
| [Syslog](https://github.com/Sirupsen/logrus/blob/master/hooks/syslog/syslog.go) | Send errors to remote syslog server. Uses standard library `log/syslog` behind the scenes. |
|
||||
| [BugSnag](https://github.com/Sirupsen/logrus/blob/master/hooks/bugsnag/bugsnag.go) | Send errors to the Bugsnag exception tracking service. |
|
||||
| [Bugsnag](https://github.com/Shopify/logrus-bugsnag/blob/master/bugsnag.go) | Send errors to the Bugsnag exception tracking service. |
|
||||
| [Sentry](https://github.com/evalphobia/logrus_sentry) | Send errors to the Sentry error logging and aggregation service. |
|
||||
| [Hiprus](https://github.com/nubo/hiprus) | Send errors to a channel in hipchat. |
|
||||
| [Logrusly](https://github.com/sebest/logrusly) | Send logs to [Loggly](https://www.loggly.com/) |
|
||||
@ -218,7 +218,17 @@ Note: Syslog hook also support connecting to local syslog (Ex. "/dev/log" or "/v
|
||||
| [Rollrus](https://github.com/heroku/rollrus) | Hook for sending errors to rollbar |
|
||||
| [Fluentd](https://github.com/evalphobia/logrus_fluent) | Hook for logging to fluentd |
|
||||
| [Mongodb](https://github.com/weekface/mgorus) | Hook for logging to mongodb |
|
||||
| [Influxus] (http://github.com/vlad-doru/influxus) | Hook for concurrently logging to [InfluxDB] (http://influxdata.com/) |
|
||||
| [InfluxDB](https://github.com/Abramovic/logrus_influxdb) | Hook for logging to influxdb |
|
||||
| [Octokit](https://github.com/dorajistyle/logrus-octokit-hook) | Hook for logging to github via octokit |
|
||||
| [DeferPanic](https://github.com/deferpanic/dp-logrus) | Hook for logging to DeferPanic |
|
||||
| [Redis-Hook](https://github.com/rogierlommers/logrus-redis-hook) | Hook for logging to a ELK stack (through Redis) |
|
||||
| [Amqp-Hook](https://github.com/vladoatanasov/logrus_amqp) | Hook for logging to Amqp broker (Like RabbitMQ) |
|
||||
| [KafkaLogrus](https://github.com/goibibo/KafkaLogrus) | Hook for logging to kafka |
|
||||
| [Typetalk](https://github.com/dragon3/logrus-typetalk-hook) | Hook for logging to [Typetalk](https://www.typetalk.in/) |
|
||||
| [ElasticSearch](https://github.com/sohlich/elogrus) | Hook for logging to ElasticSearch|
|
||||
| [Sumorus](https://github.com/doublefree/sumorus) | Hook for logging to [SumoLogic](https://www.sumologic.com/)|
|
||||
| [Logstash](https://github.com/bshuster-repo/logrus-logstash-hook) | Hook for logging to [Logstash](https://www.elastic.co/products/logstash) |
|
||||
|
||||
#### Level logging
|
||||
|
||||
@ -296,15 +306,12 @@ The built-in logging formatters are:
|
||||
field to `true`. To force no colored output even if there is a TTY set the
|
||||
`DisableColors` field to `true`
|
||||
* `logrus.JSONFormatter`. Logs fields as JSON.
|
||||
* `logrus/formatters/logstash.LogstashFormatter`. Logs fields as [Logstash](http://logstash.net) Events.
|
||||
|
||||
```go
|
||||
logrus.SetFormatter(&logstash.LogstashFormatter{Type: “application_name"})
|
||||
```
|
||||
|
||||
Third party logging formatters:
|
||||
|
||||
* [`zalgo`](https://github.com/aybabtme/logzalgo): invoking the P͉̫o̳̼̊w̖͈̰͎e̬͔̭͂r͚̼̹̲ ̫͓͉̳͈ō̠͕͖̚f̝͍̠ ͕̲̞͖͑Z̖̫̤̫ͪa͉̬͈̗l͖͎g̳̥o̰̥̅!̣͔̲̻͊̄ ̙̘̦̹̦.
|
||||
* [`logstash`](https://github.com/bshuster-repo/logrus-logstash-hook). Logs fields as [Logstash](http://logstash.net) Events.
|
||||
* [`prefixed`](https://github.com/x-cray/logrus-prefixed-formatter). Displays log entry source along with alternative layout.
|
||||
* [`zalgo`](https://github.com/aybabtme/logzalgo). Invoking the P͉̫o̳̼̊w̖͈̰͎e̬͔̭͂r͚̼̹̲ ̫͓͉̳͈ō̠͕͖̚f̝͍̠ ͕̲̞͖͑Z̖̫̤̫ͪa͉̬͈̗l͖͎g̳̥o̰̥̅!̣͔̲̻͊̄ ̙̘̦̹̦.
|
||||
|
||||
You can define your formatter by implementing the `Formatter` interface,
|
||||
requiring a `Format` method. `Format` takes an `*Entry`. `entry.Data` is a
|
||||
@ -353,5 +360,43 @@ Log rotation is not provided with Logrus. Log rotation should be done by an
|
||||
external program (like `logrotate(8)`) that can compress and delete old log
|
||||
entries. It should not be a feature of the application-level logger.
|
||||
|
||||
#### Tools
|
||||
|
||||
[godoc]: https://godoc.org/github.com/Sirupsen/logrus
|
||||
| Tool | Description |
|
||||
| ---- | ----------- |
|
||||
|[Logrus Mate](https://github.com/gogap/logrus_mate)|Logrus mate is a tool for Logrus to manage loggers, you can initial logger's level, hook and formatter by config file, the logger will generated with different config at different environment.|
|
||||
|
||||
#### Testing
|
||||
|
||||
Logrus has a built in facility for asserting the presence of log messages. This is implemented through the `test` hook and provides:
|
||||
|
||||
* decorators for existing logger (`test.NewLocal` and `test.NewGlobal`) which basically just add the `test` hook
|
||||
* a test logger (`test.NewNullLogger`) that just records log messages (and does not output any):
|
||||
|
||||
```go
|
||||
logger, hook := NewNullLogger()
|
||||
logger.Error("Hello error")
|
||||
|
||||
assert.Equal(1, len(hook.Entries))
|
||||
assert.Equal(logrus.ErrorLevel, hook.LastEntry().Level)
|
||||
assert.Equal("Hello error", hook.LastEntry().Message)
|
||||
|
||||
hook.Reset()
|
||||
assert.Nil(hook.LastEntry())
|
||||
```
|
||||
|
||||
#### Fatal handlers
|
||||
|
||||
Logrus can register one or more functions that will be called when any `fatal`
|
||||
level message is logged. The registered handlers will be executed before
|
||||
logrus performs a `os.Exit(1)`. This behavior may be helpful if callers need
|
||||
to gracefully shutdown. Unlike a `panic("Something went wrong...")` call which can be intercepted with a deferred `recover` a call to `os.Exit(1)` can not be intercepted.
|
||||
|
||||
```
|
||||
...
|
||||
handler := func() {
|
||||
// gracefully shutdown something...
|
||||
}
|
||||
logrus.RegisterExitHandler(handler)
|
||||
...
|
||||
```
|
||||
|
64
vendor/github.com/Sirupsen/logrus/alt_exit.go
generated
vendored
Normal file
64
vendor/github.com/Sirupsen/logrus/alt_exit.go
generated
vendored
Normal file
@ -0,0 +1,64 @@
|
||||
package logrus
|
||||
|
||||
// The following code was sourced and modified from the
|
||||
// https://bitbucket.org/tebeka/atexit package governed by the following license:
|
||||
//
|
||||
// Copyright (c) 2012 Miki Tebeka <miki.tebeka@gmail.com>.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
// this software and associated documentation files (the "Software"), to deal in
|
||||
// the Software without restriction, including without limitation the rights to
|
||||
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
// the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
// subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
)
|
||||
|
||||
var handlers = []func(){}
|
||||
|
||||
func runHandler(handler func()) {
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
fmt.Fprintln(os.Stderr, "Error: Logrus exit handler error:", err)
|
||||
}
|
||||
}()
|
||||
|
||||
handler()
|
||||
}
|
||||
|
||||
func runHandlers() {
|
||||
for _, handler := range handlers {
|
||||
runHandler(handler)
|
||||
}
|
||||
}
|
||||
|
||||
// Exit runs all the Logrus atexit handlers and then terminates the program using os.Exit(code)
|
||||
func Exit(code int) {
|
||||
runHandlers()
|
||||
os.Exit(code)
|
||||
}
|
||||
|
||||
// RegisterExitHandler adds a Logrus Exit handler, call logrus.Exit to invoke
|
||||
// all handlers. The handlers will also be invoked when any Fatal log entry is
|
||||
// made.
|
||||
//
|
||||
// This method is useful when a caller wishes to use logrus to log a fatal
|
||||
// message but also needs to gracefully shutdown. An example usecase could be
|
||||
// closing database connections, or sending a alert that the application is
|
||||
// closing.
|
||||
func RegisterExitHandler(handler func()) {
|
||||
handlers = append(handlers, handler)
|
||||
}
|
8
vendor/github.com/Sirupsen/logrus/entry.go
generated
vendored
8
vendor/github.com/Sirupsen/logrus/entry.go
generated
vendored
@ -68,7 +68,7 @@ func (entry *Entry) WithField(key string, value interface{}) *Entry {
|
||||
|
||||
// Add a map of fields to the Entry.
|
||||
func (entry *Entry) WithFields(fields Fields) *Entry {
|
||||
data := Fields{}
|
||||
data := make(Fields, len(entry.Data)+len(fields))
|
||||
for k, v := range entry.Data {
|
||||
data[k] = v
|
||||
}
|
||||
@ -150,7 +150,7 @@ func (entry *Entry) Fatal(args ...interface{}) {
|
||||
if entry.Logger.Level >= FatalLevel {
|
||||
entry.log(FatalLevel, fmt.Sprint(args...))
|
||||
}
|
||||
os.Exit(1)
|
||||
Exit(1)
|
||||
}
|
||||
|
||||
func (entry *Entry) Panic(args ...interface{}) {
|
||||
@ -198,7 +198,7 @@ func (entry *Entry) Fatalf(format string, args ...interface{}) {
|
||||
if entry.Logger.Level >= FatalLevel {
|
||||
entry.Fatal(fmt.Sprintf(format, args...))
|
||||
}
|
||||
os.Exit(1)
|
||||
Exit(1)
|
||||
}
|
||||
|
||||
func (entry *Entry) Panicf(format string, args ...interface{}) {
|
||||
@ -245,7 +245,7 @@ func (entry *Entry) Fatalln(args ...interface{}) {
|
||||
if entry.Logger.Level >= FatalLevel {
|
||||
entry.Fatal(entry.sprintlnn(args...))
|
||||
}
|
||||
os.Exit(1)
|
||||
Exit(1)
|
||||
}
|
||||
|
||||
func (entry *Entry) Panicln(args ...interface{}) {
|
||||
|
77
vendor/github.com/Sirupsen/logrus/entry_test.go
generated
vendored
77
vendor/github.com/Sirupsen/logrus/entry_test.go
generated
vendored
@ -1,77 +0,0 @@
|
||||
package logrus
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestEntryWithError(t *testing.T) {
|
||||
|
||||
assert := assert.New(t)
|
||||
|
||||
defer func() {
|
||||
ErrorKey = "error"
|
||||
}()
|
||||
|
||||
err := fmt.Errorf("kaboom at layer %d", 4711)
|
||||
|
||||
assert.Equal(err, WithError(err).Data["error"])
|
||||
|
||||
logger := New()
|
||||
logger.Out = &bytes.Buffer{}
|
||||
entry := NewEntry(logger)
|
||||
|
||||
assert.Equal(err, entry.WithError(err).Data["error"])
|
||||
|
||||
ErrorKey = "err"
|
||||
|
||||
assert.Equal(err, entry.WithError(err).Data["err"])
|
||||
|
||||
}
|
||||
|
||||
func TestEntryPanicln(t *testing.T) {
|
||||
errBoom := fmt.Errorf("boom time")
|
||||
|
||||
defer func() {
|
||||
p := recover()
|
||||
assert.NotNil(t, p)
|
||||
|
||||
switch pVal := p.(type) {
|
||||
case *Entry:
|
||||
assert.Equal(t, "kaboom", pVal.Message)
|
||||
assert.Equal(t, errBoom, pVal.Data["err"])
|
||||
default:
|
||||
t.Fatalf("want type *Entry, got %T: %#v", pVal, pVal)
|
||||
}
|
||||
}()
|
||||
|
||||
logger := New()
|
||||
logger.Out = &bytes.Buffer{}
|
||||
entry := NewEntry(logger)
|
||||
entry.WithField("err", errBoom).Panicln("kaboom")
|
||||
}
|
||||
|
||||
func TestEntryPanicf(t *testing.T) {
|
||||
errBoom := fmt.Errorf("boom again")
|
||||
|
||||
defer func() {
|
||||
p := recover()
|
||||
assert.NotNil(t, p)
|
||||
|
||||
switch pVal := p.(type) {
|
||||
case *Entry:
|
||||
assert.Equal(t, "kaboom true", pVal.Message)
|
||||
assert.Equal(t, errBoom, pVal.Data["err"])
|
||||
default:
|
||||
t.Fatalf("want type *Entry, got %T: %#v", pVal, pVal)
|
||||
}
|
||||
}()
|
||||
|
||||
logger := New()
|
||||
logger.Out = &bytes.Buffer{}
|
||||
entry := NewEntry(logger)
|
||||
entry.WithField("err", errBoom).Panicf("kaboom %v", true)
|
||||
}
|
50
vendor/github.com/Sirupsen/logrus/examples/basic/basic.go
generated
vendored
50
vendor/github.com/Sirupsen/logrus/examples/basic/basic.go
generated
vendored
@ -1,50 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/Sirupsen/logrus"
|
||||
)
|
||||
|
||||
var log = logrus.New()
|
||||
|
||||
func init() {
|
||||
log.Formatter = new(logrus.JSONFormatter)
|
||||
log.Formatter = new(logrus.TextFormatter) // default
|
||||
log.Level = logrus.DebugLevel
|
||||
}
|
||||
|
||||
func main() {
|
||||
defer func() {
|
||||
err := recover()
|
||||
if err != nil {
|
||||
log.WithFields(logrus.Fields{
|
||||
"omg": true,
|
||||
"err": err,
|
||||
"number": 100,
|
||||
}).Fatal("The ice breaks!")
|
||||
}
|
||||
}()
|
||||
|
||||
log.WithFields(logrus.Fields{
|
||||
"animal": "walrus",
|
||||
"number": 8,
|
||||
}).Debug("Started observing beach")
|
||||
|
||||
log.WithFields(logrus.Fields{
|
||||
"animal": "walrus",
|
||||
"size": 10,
|
||||
}).Info("A group of walrus emerges from the ocean")
|
||||
|
||||
log.WithFields(logrus.Fields{
|
||||
"omg": true,
|
||||
"number": 122,
|
||||
}).Warn("The group's number increased tremendously!")
|
||||
|
||||
log.WithFields(logrus.Fields{
|
||||
"temperature": -4,
|
||||
}).Debug("Temperature changes")
|
||||
|
||||
log.WithFields(logrus.Fields{
|
||||
"animal": "orca",
|
||||
"size": 9009,
|
||||
}).Panic("It's over 9000!")
|
||||
}
|
30
vendor/github.com/Sirupsen/logrus/examples/hook/hook.go
generated
vendored
30
vendor/github.com/Sirupsen/logrus/examples/hook/hook.go
generated
vendored
@ -1,30 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/Sirupsen/logrus"
|
||||
"gopkg.in/gemnasium/logrus-airbrake-hook.v2"
|
||||
)
|
||||
|
||||
var log = logrus.New()
|
||||
|
||||
func init() {
|
||||
log.Formatter = new(logrus.TextFormatter) // default
|
||||
log.Hooks.Add(airbrake.NewHook(123, "xyz", "development"))
|
||||
}
|
||||
|
||||
func main() {
|
||||
log.WithFields(logrus.Fields{
|
||||
"animal": "walrus",
|
||||
"size": 10,
|
||||
}).Info("A group of walrus emerges from the ocean")
|
||||
|
||||
log.WithFields(logrus.Fields{
|
||||
"omg": true,
|
||||
"number": 122,
|
||||
}).Warn("The group's number increased tremendously!")
|
||||
|
||||
log.WithFields(logrus.Fields{
|
||||
"omg": true,
|
||||
"number": 100,
|
||||
}).Fatal("The ice breaks!")
|
||||
}
|
15
vendor/github.com/Sirupsen/logrus/formatter.go
generated
vendored
15
vendor/github.com/Sirupsen/logrus/formatter.go
generated
vendored
@ -31,18 +31,15 @@ type Formatter interface {
|
||||
// It's not exported because it's still using Data in an opinionated way. It's to
|
||||
// avoid code duplication between the two default formatters.
|
||||
func prefixFieldClashes(data Fields) {
|
||||
_, ok := data["time"]
|
||||
if ok {
|
||||
data["fields.time"] = data["time"]
|
||||
if t, ok := data["time"]; ok {
|
||||
data["fields.time"] = t
|
||||
}
|
||||
|
||||
_, ok = data["msg"]
|
||||
if ok {
|
||||
data["fields.msg"] = data["msg"]
|
||||
if m, ok := data["msg"]; ok {
|
||||
data["fields.msg"] = m
|
||||
}
|
||||
|
||||
_, ok = data["level"]
|
||||
if ok {
|
||||
data["fields.level"] = data["level"]
|
||||
if l, ok := data["level"]; ok {
|
||||
data["fields.level"] = l
|
||||
}
|
||||
}
|
||||
|
98
vendor/github.com/Sirupsen/logrus/formatter_bench_test.go
generated
vendored
98
vendor/github.com/Sirupsen/logrus/formatter_bench_test.go
generated
vendored
@ -1,98 +0,0 @@
|
||||
package logrus
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
// smallFields is a small size data set for benchmarking
|
||||
var smallFields = Fields{
|
||||
"foo": "bar",
|
||||
"baz": "qux",
|
||||
"one": "two",
|
||||
"three": "four",
|
||||
}
|
||||
|
||||
// largeFields is a large size data set for benchmarking
|
||||
var largeFields = Fields{
|
||||
"foo": "bar",
|
||||
"baz": "qux",
|
||||
"one": "two",
|
||||
"three": "four",
|
||||
"five": "six",
|
||||
"seven": "eight",
|
||||
"nine": "ten",
|
||||
"eleven": "twelve",
|
||||
"thirteen": "fourteen",
|
||||
"fifteen": "sixteen",
|
||||
"seventeen": "eighteen",
|
||||
"nineteen": "twenty",
|
||||
"a": "b",
|
||||
"c": "d",
|
||||
"e": "f",
|
||||
"g": "h",
|
||||
"i": "j",
|
||||
"k": "l",
|
||||
"m": "n",
|
||||
"o": "p",
|
||||
"q": "r",
|
||||
"s": "t",
|
||||
"u": "v",
|
||||
"w": "x",
|
||||
"y": "z",
|
||||
"this": "will",
|
||||
"make": "thirty",
|
||||
"entries": "yeah",
|
||||
}
|
||||
|
||||
var errorFields = Fields{
|
||||
"foo": fmt.Errorf("bar"),
|
||||
"baz": fmt.Errorf("qux"),
|
||||
}
|
||||
|
||||
func BenchmarkErrorTextFormatter(b *testing.B) {
|
||||
doBenchmark(b, &TextFormatter{DisableColors: true}, errorFields)
|
||||
}
|
||||
|
||||
func BenchmarkSmallTextFormatter(b *testing.B) {
|
||||
doBenchmark(b, &TextFormatter{DisableColors: true}, smallFields)
|
||||
}
|
||||
|
||||
func BenchmarkLargeTextFormatter(b *testing.B) {
|
||||
doBenchmark(b, &TextFormatter{DisableColors: true}, largeFields)
|
||||
}
|
||||
|
||||
func BenchmarkSmallColoredTextFormatter(b *testing.B) {
|
||||
doBenchmark(b, &TextFormatter{ForceColors: true}, smallFields)
|
||||
}
|
||||
|
||||
func BenchmarkLargeColoredTextFormatter(b *testing.B) {
|
||||
doBenchmark(b, &TextFormatter{ForceColors: true}, largeFields)
|
||||
}
|
||||
|
||||
func BenchmarkSmallJSONFormatter(b *testing.B) {
|
||||
doBenchmark(b, &JSONFormatter{}, smallFields)
|
||||
}
|
||||
|
||||
func BenchmarkLargeJSONFormatter(b *testing.B) {
|
||||
doBenchmark(b, &JSONFormatter{}, largeFields)
|
||||
}
|
||||
|
||||
func doBenchmark(b *testing.B, formatter Formatter, fields Fields) {
|
||||
entry := &Entry{
|
||||
Time: time.Time{},
|
||||
Level: InfoLevel,
|
||||
Message: "message",
|
||||
Data: fields,
|
||||
}
|
||||
var d []byte
|
||||
var err error
|
||||
for i := 0; i < b.N; i++ {
|
||||
d, err = formatter.Format(entry)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
b.SetBytes(int64(len(d)))
|
||||
}
|
||||
}
|
56
vendor/github.com/Sirupsen/logrus/formatters/logstash/logstash.go
generated
vendored
56
vendor/github.com/Sirupsen/logrus/formatters/logstash/logstash.go
generated
vendored
@ -1,56 +0,0 @@
|
||||
package logstash
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
)
|
||||
|
||||
// Formatter generates json in logstash format.
|
||||
// Logstash site: http://logstash.net/
|
||||
type LogstashFormatter struct {
|
||||
Type string // if not empty use for logstash type field.
|
||||
|
||||
// TimestampFormat sets the format used for timestamps.
|
||||
TimestampFormat string
|
||||
}
|
||||
|
||||
func (f *LogstashFormatter) Format(entry *logrus.Entry) ([]byte, error) {
|
||||
entry.Data["@version"] = 1
|
||||
|
||||
if f.TimestampFormat == "" {
|
||||
f.TimestampFormat = logrus.DefaultTimestampFormat
|
||||
}
|
||||
|
||||
entry.Data["@timestamp"] = entry.Time.Format(f.TimestampFormat)
|
||||
|
||||
// set message field
|
||||
v, ok := entry.Data["message"]
|
||||
if ok {
|
||||
entry.Data["fields.message"] = v
|
||||
}
|
||||
entry.Data["message"] = entry.Message
|
||||
|
||||
// set level field
|
||||
v, ok = entry.Data["level"]
|
||||
if ok {
|
||||
entry.Data["fields.level"] = v
|
||||
}
|
||||
entry.Data["level"] = entry.Level.String()
|
||||
|
||||
// set type field
|
||||
if f.Type != "" {
|
||||
v, ok = entry.Data["type"]
|
||||
if ok {
|
||||
entry.Data["fields.type"] = v
|
||||
}
|
||||
entry.Data["type"] = f.Type
|
||||
}
|
||||
|
||||
serialized, err := json.Marshal(entry.Data)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Failed to marshal fields to JSON, %v", err)
|
||||
}
|
||||
return append(serialized, '\n'), nil
|
||||
}
|
52
vendor/github.com/Sirupsen/logrus/formatters/logstash/logstash_test.go
generated
vendored
52
vendor/github.com/Sirupsen/logrus/formatters/logstash/logstash_test.go
generated
vendored
@ -1,52 +0,0 @@
|
||||
package logstash
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestLogstashFormatter(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
lf := LogstashFormatter{Type: "abc"}
|
||||
|
||||
fields := logrus.Fields{
|
||||
"message": "def",
|
||||
"level": "ijk",
|
||||
"type": "lmn",
|
||||
"one": 1,
|
||||
"pi": 3.14,
|
||||
"bool": true,
|
||||
}
|
||||
|
||||
entry := logrus.WithFields(fields)
|
||||
entry.Message = "msg"
|
||||
entry.Level = logrus.InfoLevel
|
||||
|
||||
b, _ := lf.Format(entry)
|
||||
|
||||
var data map[string]interface{}
|
||||
dec := json.NewDecoder(bytes.NewReader(b))
|
||||
dec.UseNumber()
|
||||
dec.Decode(&data)
|
||||
|
||||
// base fields
|
||||
assert.Equal(json.Number("1"), data["@version"])
|
||||
assert.NotEmpty(data["@timestamp"])
|
||||
assert.Equal("abc", data["type"])
|
||||
assert.Equal("msg", data["message"])
|
||||
assert.Equal("info", data["level"])
|
||||
|
||||
// substituted fields
|
||||
assert.Equal("def", data["fields.message"])
|
||||
assert.Equal("ijk", data["fields.level"])
|
||||
assert.Equal("lmn", data["fields.type"])
|
||||
|
||||
// formats
|
||||
assert.Equal(json.Number("1"), data["one"])
|
||||
assert.Equal(json.Number("3.14"), data["pi"])
|
||||
assert.Equal(true, data["bool"])
|
||||
}
|
122
vendor/github.com/Sirupsen/logrus/hook_test.go
generated
vendored
122
vendor/github.com/Sirupsen/logrus/hook_test.go
generated
vendored
@ -1,122 +0,0 @@
|
||||
package logrus
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
type TestHook struct {
|
||||
Fired bool
|
||||
}
|
||||
|
||||
func (hook *TestHook) Fire(entry *Entry) error {
|
||||
hook.Fired = true
|
||||
return nil
|
||||
}
|
||||
|
||||
func (hook *TestHook) Levels() []Level {
|
||||
return []Level{
|
||||
DebugLevel,
|
||||
InfoLevel,
|
||||
WarnLevel,
|
||||
ErrorLevel,
|
||||
FatalLevel,
|
||||
PanicLevel,
|
||||
}
|
||||
}
|
||||
|
||||
func TestHookFires(t *testing.T) {
|
||||
hook := new(TestHook)
|
||||
|
||||
LogAndAssertJSON(t, func(log *Logger) {
|
||||
log.Hooks.Add(hook)
|
||||
assert.Equal(t, hook.Fired, false)
|
||||
|
||||
log.Print("test")
|
||||
}, func(fields Fields) {
|
||||
assert.Equal(t, hook.Fired, true)
|
||||
})
|
||||
}
|
||||
|
||||
type ModifyHook struct {
|
||||
}
|
||||
|
||||
func (hook *ModifyHook) Fire(entry *Entry) error {
|
||||
entry.Data["wow"] = "whale"
|
||||
return nil
|
||||
}
|
||||
|
||||
func (hook *ModifyHook) Levels() []Level {
|
||||
return []Level{
|
||||
DebugLevel,
|
||||
InfoLevel,
|
||||
WarnLevel,
|
||||
ErrorLevel,
|
||||
FatalLevel,
|
||||
PanicLevel,
|
||||
}
|
||||
}
|
||||
|
||||
func TestHookCanModifyEntry(t *testing.T) {
|
||||
hook := new(ModifyHook)
|
||||
|
||||
LogAndAssertJSON(t, func(log *Logger) {
|
||||
log.Hooks.Add(hook)
|
||||
log.WithField("wow", "elephant").Print("test")
|
||||
}, func(fields Fields) {
|
||||
assert.Equal(t, fields["wow"], "whale")
|
||||
})
|
||||
}
|
||||
|
||||
func TestCanFireMultipleHooks(t *testing.T) {
|
||||
hook1 := new(ModifyHook)
|
||||
hook2 := new(TestHook)
|
||||
|
||||
LogAndAssertJSON(t, func(log *Logger) {
|
||||
log.Hooks.Add(hook1)
|
||||
log.Hooks.Add(hook2)
|
||||
|
||||
log.WithField("wow", "elephant").Print("test")
|
||||
}, func(fields Fields) {
|
||||
assert.Equal(t, fields["wow"], "whale")
|
||||
assert.Equal(t, hook2.Fired, true)
|
||||
})
|
||||
}
|
||||
|
||||
type ErrorHook struct {
|
||||
Fired bool
|
||||
}
|
||||
|
||||
func (hook *ErrorHook) Fire(entry *Entry) error {
|
||||
hook.Fired = true
|
||||
return nil
|
||||
}
|
||||
|
||||
func (hook *ErrorHook) Levels() []Level {
|
||||
return []Level{
|
||||
ErrorLevel,
|
||||
}
|
||||
}
|
||||
|
||||
func TestErrorHookShouldntFireOnInfo(t *testing.T) {
|
||||
hook := new(ErrorHook)
|
||||
|
||||
LogAndAssertJSON(t, func(log *Logger) {
|
||||
log.Hooks.Add(hook)
|
||||
log.Info("test")
|
||||
}, func(fields Fields) {
|
||||
assert.Equal(t, hook.Fired, false)
|
||||
})
|
||||
}
|
||||
|
||||
func TestErrorHookShouldFireOnError(t *testing.T) {
|
||||
hook := new(ErrorHook)
|
||||
|
||||
LogAndAssertJSON(t, func(log *Logger) {
|
||||
log.Hooks.Add(hook)
|
||||
log.Error("test")
|
||||
}, func(fields Fields) {
|
||||
assert.Equal(t, hook.Fired, true)
|
||||
})
|
||||
}
|
68
vendor/github.com/Sirupsen/logrus/hooks/bugsnag/bugsnag.go
generated
vendored
68
vendor/github.com/Sirupsen/logrus/hooks/bugsnag/bugsnag.go
generated
vendored
@ -1,68 +0,0 @@
|
||||
package logrus_bugsnag
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/bugsnag/bugsnag-go"
|
||||
)
|
||||
|
||||
type bugsnagHook struct{}
|
||||
|
||||
// ErrBugsnagUnconfigured is returned if NewBugsnagHook is called before
|
||||
// bugsnag.Configure. Bugsnag must be configured before the hook.
|
||||
var ErrBugsnagUnconfigured = errors.New("bugsnag must be configured before installing this logrus hook")
|
||||
|
||||
// ErrBugsnagSendFailed indicates that the hook failed to submit an error to
|
||||
// bugsnag. The error was successfully generated, but `bugsnag.Notify()`
|
||||
// failed.
|
||||
type ErrBugsnagSendFailed struct {
|
||||
err error
|
||||
}
|
||||
|
||||
func (e ErrBugsnagSendFailed) Error() string {
|
||||
return "failed to send error to Bugsnag: " + e.err.Error()
|
||||
}
|
||||
|
||||
// NewBugsnagHook initializes a logrus hook which sends exceptions to an
|
||||
// exception-tracking service compatible with the Bugsnag API. Before using
|
||||
// this hook, you must call bugsnag.Configure(). The returned object should be
|
||||
// registered with a log via `AddHook()`
|
||||
//
|
||||
// Entries that trigger an Error, Fatal or Panic should now include an "error"
|
||||
// field to send to Bugsnag.
|
||||
func NewBugsnagHook() (*bugsnagHook, error) {
|
||||
if bugsnag.Config.APIKey == "" {
|
||||
return nil, ErrBugsnagUnconfigured
|
||||
}
|
||||
return &bugsnagHook{}, nil
|
||||
}
|
||||
|
||||
// Fire forwards an error to Bugsnag. Given a logrus.Entry, it extracts the
|
||||
// "error" field (or the Message if the error isn't present) and sends it off.
|
||||
func (hook *bugsnagHook) Fire(entry *logrus.Entry) error {
|
||||
var notifyErr error
|
||||
err, ok := entry.Data["error"].(error)
|
||||
if ok {
|
||||
notifyErr = err
|
||||
} else {
|
||||
notifyErr = errors.New(entry.Message)
|
||||
}
|
||||
|
||||
bugsnagErr := bugsnag.Notify(notifyErr)
|
||||
if bugsnagErr != nil {
|
||||
return ErrBugsnagSendFailed{bugsnagErr}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Levels enumerates the log levels on which the error should be forwarded to
|
||||
// bugsnag: everything at or above the "Error" level.
|
||||
func (hook *bugsnagHook) Levels() []logrus.Level {
|
||||
return []logrus.Level{
|
||||
logrus.ErrorLevel,
|
||||
logrus.FatalLevel,
|
||||
logrus.PanicLevel,
|
||||
}
|
||||
}
|
64
vendor/github.com/Sirupsen/logrus/hooks/bugsnag/bugsnag_test.go
generated
vendored
64
vendor/github.com/Sirupsen/logrus/hooks/bugsnag/bugsnag_test.go
generated
vendored
@ -1,64 +0,0 @@
|
||||
package logrus_bugsnag
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/bugsnag/bugsnag-go"
|
||||
)
|
||||
|
||||
type notice struct {
|
||||
Events []struct {
|
||||
Exceptions []struct {
|
||||
Message string `json:"message"`
|
||||
} `json:"exceptions"`
|
||||
} `json:"events"`
|
||||
}
|
||||
|
||||
func TestNoticeReceived(t *testing.T) {
|
||||
msg := make(chan string, 1)
|
||||
expectedMsg := "foo"
|
||||
|
||||
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
var notice notice
|
||||
data, _ := ioutil.ReadAll(r.Body)
|
||||
if err := json.Unmarshal(data, ¬ice); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
_ = r.Body.Close()
|
||||
|
||||
msg <- notice.Events[0].Exceptions[0].Message
|
||||
}))
|
||||
defer ts.Close()
|
||||
|
||||
hook := &bugsnagHook{}
|
||||
|
||||
bugsnag.Configure(bugsnag.Configuration{
|
||||
Endpoint: ts.URL,
|
||||
ReleaseStage: "production",
|
||||
APIKey: "12345678901234567890123456789012",
|
||||
Synchronous: true,
|
||||
})
|
||||
|
||||
log := logrus.New()
|
||||
log.Hooks.Add(hook)
|
||||
|
||||
log.WithFields(logrus.Fields{
|
||||
"error": errors.New(expectedMsg),
|
||||
}).Error("Bugsnag will not see this string")
|
||||
|
||||
select {
|
||||
case received := <-msg:
|
||||
if received != expectedMsg {
|
||||
t.Errorf("Unexpected message received: %s", received)
|
||||
}
|
||||
case <-time.After(time.Second):
|
||||
t.Error("Timed out; no notice received by Bugsnag API")
|
||||
}
|
||||
}
|
39
vendor/github.com/Sirupsen/logrus/hooks/syslog/README.md
generated
vendored
39
vendor/github.com/Sirupsen/logrus/hooks/syslog/README.md
generated
vendored
@ -1,39 +0,0 @@
|
||||
# Syslog Hooks for Logrus <img src="http://i.imgur.com/hTeVwmJ.png" width="40" height="40" alt=":walrus:" class="emoji" title=":walrus:"/>
|
||||
|
||||
## Usage
|
||||
|
||||
```go
|
||||
import (
|
||||
"log/syslog"
|
||||
"github.com/Sirupsen/logrus"
|
||||
logrus_syslog "github.com/Sirupsen/logrus/hooks/syslog"
|
||||
)
|
||||
|
||||
func main() {
|
||||
log := logrus.New()
|
||||
hook, err := logrus_syslog.NewSyslogHook("udp", "localhost:514", syslog.LOG_INFO, "")
|
||||
|
||||
if err == nil {
|
||||
log.Hooks.Add(hook)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
If you want to connect to local syslog (Ex. "/dev/log" or "/var/run/syslog" or "/var/run/log"). Just assign empty string to the first two parameters of `NewSyslogHook`. It should look like the following.
|
||||
|
||||
```go
|
||||
import (
|
||||
"log/syslog"
|
||||
"github.com/Sirupsen/logrus"
|
||||
logrus_syslog "github.com/Sirupsen/logrus/hooks/syslog"
|
||||
)
|
||||
|
||||
func main() {
|
||||
log := logrus.New()
|
||||
hook, err := logrus_syslog.NewSyslogHook("", "", syslog.LOG_INFO, "")
|
||||
|
||||
if err == nil {
|
||||
log.Hooks.Add(hook)
|
||||
}
|
||||
}
|
||||
```
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user