mirror of
https://github.com/goharbor/harbor.git
synced 2024-11-19 08:45:27 +01:00
Merge remote-tracking branch 'upstream/master'
Conflicts: Deploy/templates/ui/env api/repository.go
This commit is contained in:
commit
2462405c1f
@ -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,
|
||||||
|
@ -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
|
||||||
|
@ -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()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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)
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
@ -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)"`
|
||||||
|
@ -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.
|
||||||
|
@ -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"`
|
||||||
|
}
|
||||||
|
@ -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()
|
||||||
|
@ -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
|
||||||
|
@ -59,6 +59,7 @@ repo = 镜像仓库
|
|||||||
user = 用户
|
user = 用户
|
||||||
logs = 日志
|
logs = 日志
|
||||||
repo_name = 镜像名称
|
repo_name = 镜像名称
|
||||||
|
repo_tag = 镜像标签
|
||||||
add_members = 添加成员
|
add_members = 添加成员
|
||||||
operation = 操作
|
operation = 操作
|
||||||
advance = 高级检索
|
advance = 高级检索
|
||||||
|
@ -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>');
|
||||||
|
@ -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>
|
||||||
|
Loading…
Reference in New Issue
Block a user