Merge remote-tracking branch 'upstream/master'

Conflicts:
	Deploy/templates/ui/env
	api/repository.go
This commit is contained in:
Wenkai Yin 2016-05-03 10:33:02 +08:00
commit 2462405c1f
14 changed files with 52 additions and 33 deletions

View File

@ -94,6 +94,7 @@ create table access_log (
user_id int NOT NULL, user_id int NOT NULL,
project_id int NOT NULL, project_id int NOT NULL,
repo_name varchar (40), repo_name varchar (40),
repo_tag varchar (20),
GUID varchar(64), GUID varchar(64),
operation varchar(20) NOT NULL, operation varchar(20) NOT NULL,
op_time timestamp, op_time timestamp,

View File

@ -13,7 +13,7 @@ Project Harbor is an enterprise-class registry server. It extends the open sourc
* **Graphical user portal**: User can easily browse, search docker repositories, manage projects/namespaces. * **Graphical user portal**: User can easily browse, search docker repositories, manage projects/namespaces.
* **AD/LDAP support**: Harbor integrates with existing AD/LDAP of the enterprise for user authentication and management. * **AD/LDAP support**: Harbor integrates with existing AD/LDAP of the enterprise for user authentication and management.
* **Auditing**: All the operations to the repositories are tracked and can be used for auditing purpose. * **Auditing**: All the operations to the repositories are tracked and can be used for auditing purpose.
* **Internationalization**: Localized for English and Chinese languages. More languages can be added. * **Internationalization**: Localized for English, Chinese and German languages. More languages can be added.
* **RESTful API**: RESTful APIs are provided for most administrative operations of Harbor. The integration with other management softwares becomes easy. * **RESTful API**: RESTful APIs are provided for most administrative operations of Harbor. The integration with other management softwares becomes easy.
### Getting Started ### Getting Started

View File

@ -185,6 +185,7 @@ func (p *ProjectAPI) FilterAccessLog() {
p.CustomAbort(http.StatusInternalServerError, "Internal error.") p.CustomAbort(http.StatusInternalServerError, "Internal error.")
} }
p.Data["json"] = accessLogList p.Data["json"] = accessLogList
p.ServeJSON() p.ServeJSON()
} }

View File

@ -168,18 +168,6 @@ type tag struct {
Tags []string `json:"tags"` Tags []string `json:"tags"`
} }
type histroyItem struct {
V1Compatibility string `json:"v1Compatibility"`
}
type manifest struct {
Name string `json:"name"`
Tag string `json:"tag"`
Architecture string `json:"architecture"`
SchemaVersion int `json:"schemaVersion"`
History []histroyItem `json:"history"`
}
// GetTags handles GET /api/repositories/tags // GetTags handles GET /api/repositories/tags
func (ra *RepositoryAPI) GetTags() { func (ra *RepositoryAPI) GetTags() {
repoName := ra.GetString("repo_name") repoName := ra.GetString("repo_name")
@ -193,9 +181,8 @@ func (ra *RepositoryAPI) GetTags() {
ra.CustomAbort(http.StatusInternalServerError, "internal error") ra.CustomAbort(http.StatusInternalServerError, "internal error")
} }
tags := []string{} repoName := ra.GetString("repo_name")
tags, err := ra.registry.ListTag(repoName)
ts, err := rc.ListTag()
if err != nil { if err != nil {
e, ok := errors.ParseError(err) e, ok := errors.ParseError(err)
if ok { if ok {
@ -240,8 +227,7 @@ func (ra *RepositoryAPI) GetManifests() {
ra.CustomAbort(http.StatusInternalServerError, "internal error") ra.CustomAbort(http.StatusInternalServerError, "internal error")
} }
} }
mani := models.Manifest{}
mani := manifest{}
err = json.Unmarshal(payload, &mani) err = json.Unmarshal(payload, &mani)
if err != nil { if err != nil {
log.Errorf("Failed to decode json from response for manifests, repo name: %s, tag: %s, error: %v", repoName, tag, err) log.Errorf("Failed to decode json from response for manifests, repo name: %s, tag: %s, error: %v", repoName, tag, err)

View File

@ -19,6 +19,7 @@ import (
"strings" "strings"
"github.com/vmware/harbor/models" "github.com/vmware/harbor/models"
"github.com/vmware/harbor/utils/log"
"github.com/astaxie/beego/orm" "github.com/astaxie/beego/orm"
) )
@ -27,14 +28,14 @@ import (
func AddAccessLog(accessLog models.AccessLog) error { func AddAccessLog(accessLog models.AccessLog) error {
o := orm.NewOrm() o := orm.NewOrm()
p, err := o.Raw(`insert into access_log p, err := o.Raw(`insert into access_log
(user_id, project_id, repo_name, guid, operation, op_time) (user_id, project_id, repo_name, repo_tag, guid, operation, op_time)
values (?, ?, ?, ?, ?, now())`).Prepare() values (?, ?, ?, ?, ?, ?, now())`).Prepare()
if err != nil { if err != nil {
return err return err
} }
defer p.Close() defer p.Close()
_, err = p.Exec(accessLog.UserID, accessLog.ProjectID, accessLog.RepoName, accessLog.GUID, accessLog.Operation) _, err = p.Exec(accessLog.UserID, accessLog.ProjectID, accessLog.RepoName, accessLog.RepoTag, accessLog.GUID, accessLog.Operation)
return err return err
} }
@ -43,7 +44,7 @@ func AddAccessLog(accessLog models.AccessLog) error {
func GetAccessLogs(accessLog models.AccessLog) ([]models.AccessLog, error) { func GetAccessLogs(accessLog models.AccessLog) ([]models.AccessLog, error) {
o := orm.NewOrm() o := orm.NewOrm()
sql := `select a.log_id, u.username, a.repo_name, a.operation, a.op_time sql := `select a.log_id, u.username, a.repo_name, a.repo_tag, a.operation, a.op_time
from access_log a left join user u on a.user_id = u.user_id from access_log a left join user u on a.user_id = u.user_id
where a.project_id = ? ` where a.project_id = ? `
queryParam := make([]interface{}, 1) queryParam := make([]interface{}, 1)
@ -96,12 +97,15 @@ func GetAccessLogs(accessLog models.AccessLog) ([]models.AccessLog, error) {
} }
// AccessLog ... // AccessLog ...
func AccessLog(username, projectName, repoName, action string) error { func AccessLog(username, projectName, repoName, repoTag, action string) error {
o := orm.NewOrm() o := orm.NewOrm()
sql := "insert into access_log (user_id, project_id, repo_name, operation, op_time) " + sql := "insert into access_log (user_id, project_id, repo_name, repo_tag, operation, op_time) " +
"select (select user_id as user_id from user where username=?), " + "select (select user_id as user_id from user where username=?), " +
"(select project_id as project_id from project where name=?), ?, ?, now() " "(select project_id as project_id from project where name=?), ?, ?, ?, now() "
_, err := o.Raw(sql, username, projectName, repoName, action).Exec() _, err := o.Raw(sql, username, projectName, repoName, repoTag, action).Exec()
if err != nil {
log.Errorf("error in AccessLog: %v ", err)
}
return err return err
} }

View File

@ -60,7 +60,7 @@ func AddProject(project models.Project) (int64, error) {
return projectID, err return projectID, err
} }
accessLog := models.AccessLog{UserID: project.OwnerID, ProjectID: projectID, RepoName: project.Name + "/", GUID: "N/A", Operation: "create", OpTime: time.Now()} accessLog := models.AccessLog{UserID: project.OwnerID, ProjectID: projectID, RepoName: project.Name + "/", RepoTag: "N/A", GUID: "N/A", Operation: "create", OpTime: time.Now()}
err = AddAccessLog(accessLog) err = AddAccessLog(accessLog)
return projectID, err return projectID, err

View File

@ -25,6 +25,7 @@ type AccessLog struct {
UserID int `orm:"column(user_id)" json:"UserId"` UserID int `orm:"column(user_id)" json:"UserId"`
ProjectID int64 `orm:"column(project_id)" json:"ProjectId"` ProjectID int64 `orm:"column(project_id)" json:"ProjectId"`
RepoName string `orm:"column(repo_name)"` RepoName string `orm:"column(repo_name)"`
RepoTag string `orm:"column(repo_tag)"`
GUID string `orm:"column(GUID)" json:"Guid"` GUID string `orm:"column(GUID)" json:"Guid"`
Operation string `orm:"column(operation)"` Operation string `orm:"column(operation)"`
OpTime time.Time `orm:"column(op_time)"` OpTime time.Time `orm:"column(op_time)"`

View File

@ -40,6 +40,7 @@ type Target struct {
Digest string Digest string
Repository string Repository string
URL string `json:"Url"` URL string `json:"Url"`
Tag string
} }
// Actor holds information about actor. // Actor holds information about actor.

View File

@ -42,3 +42,21 @@ type Tag struct {
Version string `json:"version"` Version string `json:"version"`
ImageID string `json:"image_id"` ImageID string `json:"image_id"`
} }
// Manifest ...
type Manifest struct {
SchemaVersion int `json:"schemaVersion"`
Name string `json:"name"`
Tag string `json:"tag"`
Architecture string `json:"architecture"`
FsLayers []blobSumItem `json:"fsLayers"`
History []histroyItem `json:"history"`
}
type histroyItem struct {
V1Compatibility string `json:"v1Compatibility"`
}
type blobSumItem struct {
BlobSum string `json:"blobSum"`
}

View File

@ -46,7 +46,7 @@ func (n *NotificationHandler) Post() {
log.Errorf("error while decoding json: %v", err) log.Errorf("error while decoding json: %v", err)
return return
} }
var username, action, repo, project string var username, action, repo, project, repoTag string
var matched bool var matched bool
for _, e := range notification.Events { for _, e := range notification.Events {
matched, err = regexp.MatchString(manifestPattern, e.Target.MediaType) matched, err = regexp.MatchString(manifestPattern, e.Target.MediaType)
@ -58,13 +58,16 @@ func (n *NotificationHandler) Post() {
username = e.Actor.Name username = e.Actor.Name
action = e.Action action = e.Action
repo = e.Target.Repository repo = e.Target.Repository
repoTag = e.Target.Tag
log.Debugf("repo tag is : %v ", repoTag)
if strings.Contains(repo, "/") { if strings.Contains(repo, "/") {
project = repo[0:strings.LastIndex(repo, "/")] project = repo[0:strings.LastIndex(repo, "/")]
} }
if username == "" { if username == "" {
username = "anonymous" username = "anonymous"
} }
go dao.AccessLog(username, project, repo, action) go dao.AccessLog(username, project, repo, repoTag, action)
if action == "push" { if action == "push" {
go func() { go func() {
err2 := svc_utils.RefreshCatalogCache() err2 := svc_utils.RefreshCatalogCache()

View File

@ -59,6 +59,7 @@ repo = Repositories
user = Users user = Users
logs = Logs logs = Logs
repo_name = Repository Name repo_name = Repository Name
repo_tag = Tag
add_members = Add Members add_members = Add Members
operation = Operation operation = Operation
advance = Advanced Search advance = Advanced Search

View File

@ -59,6 +59,7 @@ repo = 镜像仓库
user = 用户 user = 用户
logs = 日志 logs = 日志
repo_name = 镜像名称 repo_name = 镜像名称
repo_tag = 镜像标签
add_members = 添加成员 add_members = 添加成员
operation = 操作 operation = 操作
advance = 高级检索 advance = 高级检索

View File

@ -286,6 +286,7 @@ jQuery(function(){
'<tr>' + '<tr>' +
'<td>' + e.Username + '</td>' + '<td>' + e.Username + '</td>' +
'<td>' + e.RepoName + '</td>' + '<td>' + e.RepoName + '</td>' +
'<td>' + e.RepoTag + '</td>' +
'<td>' + e.Operation + '</td>' + '<td>' + e.Operation + '</td>' +
'<td>' + moment(new Date(e.OpTime)).format("YYYY-MM-DD HH:mm:ss") + '</td>' + '<td>' + moment(new Date(e.OpTime)).format("YYYY-MM-DD HH:mm:ss") + '</td>' +
'</tr>'); '</tr>');

View File

@ -159,10 +159,11 @@
<table id="tblAccessLog" class="table table-hover" > <table id="tblAccessLog" class="table table-hover" >
<thead> <thead>
<tr> <tr>
<th width="20%">{{i18n .Lang "username"}}</th> <th width="15%">{{i18n .Lang "username"}}</th>
<th width="40%">{{i18n .Lang "repo_name"}}</th> <th width="30%">{{i18n .Lang "repo_name"}}</th>
<th width="20%">{{i18n .Lang "operation"}}</th> <th width="15%">{{i18n .Lang "repo_tag"}}</th>
<th width="20%">{{i18n .Lang "timestamp"}}</th> <th width="15%">{{i18n .Lang "operation"}}</th>
<th width="15%">{{i18n .Lang "timestamp"}}</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>