Merge branch 'job-service' into new-ui-with-sync-image

This commit is contained in:
kunw 2016-06-07 14:25:17 +08:00
commit efc141e811
6 changed files with 273 additions and 34 deletions

View File

@ -17,8 +17,10 @@ package api
import ( import (
"encoding/json" "encoding/json"
"fmt"
"net/http" "net/http"
"github.com/astaxie/beego/validation"
"github.com/vmware/harbor/auth" "github.com/vmware/harbor/auth"
"github.com/vmware/harbor/dao" "github.com/vmware/harbor/dao"
"github.com/vmware/harbor/models" "github.com/vmware/harbor/models"
@ -51,6 +53,30 @@ func (b *BaseAPI) DecodeJSONReq(v interface{}) {
} }
} }
// Validate validates v if it implements interface validation.ValidFormer
func (b *BaseAPI) Validate(v interface{}) {
validator := validation.Validation{}
isValid, err := validator.Valid(v)
if err != nil {
log.Errorf("failed to validate: %v", err)
b.CustomAbort(http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError))
}
if !isValid {
message := ""
for _, e := range validator.Errors {
message += fmt.Sprintf("%s %s \n", e.Field, e.Message)
}
b.CustomAbort(http.StatusBadRequest, message)
}
}
// DecodeJSONReqAndValidate does both decoding and validation
func (b *BaseAPI) DecodeJSONReqAndValidate(v interface{}) {
b.DecodeJSONReq(v)
b.Validate(v)
}
// ValidateUser checks if the request triggered by a valid user // ValidateUser checks if the request triggered by a valid user
func (b *BaseAPI) ValidateUser() int { func (b *BaseAPI) ValidateUser() int {

View File

@ -69,9 +69,40 @@ func (pa *RepPolicyAPI) Get() {
// Post creates a policy, and if it is enbled, the replication will be triggered right now. // Post creates a policy, and if it is enbled, the replication will be triggered right now.
func (pa *RepPolicyAPI) Post() { func (pa *RepPolicyAPI) Post() {
policy := models.RepPolicy{} policy := &models.RepPolicy{}
pa.DecodeJSONReq(&policy) pa.DecodeJSONReqAndValidate(policy)
pid, err := dao.AddRepPolicy(policy)
po, err := dao.GetRepPolicyByName(policy.Name)
if err != nil {
log.Errorf("failed to get policy %s: %v", policy.Name, err)
pa.CustomAbort(http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError))
}
if po != nil {
pa.CustomAbort(http.StatusConflict, "name is already used")
}
project, err := dao.GetProjectByID(policy.ProjectID)
if err != nil {
log.Errorf("failed to get project %d: %v", policy.ProjectID, err)
pa.CustomAbort(http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError))
}
if project == nil {
pa.CustomAbort(http.StatusBadRequest, fmt.Sprintf("project %d does not exist", policy.ProjectID))
}
target, err := dao.GetRepTarget(policy.TargetID)
if err != nil {
log.Errorf("failed to get target %d: %v", policy.TargetID, err)
pa.CustomAbort(http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError))
}
if target == nil {
pa.CustomAbort(http.StatusBadRequest, fmt.Sprintf("target %d does not exist", policy.TargetID))
}
pid, err := dao.AddRepPolicy(*policy)
if err != nil { if err != nil {
log.Errorf("Failed to add policy to DB, error: %v", err) log.Errorf("Failed to add policy to DB, error: %v", err)
pa.RenderError(http.StatusInternalServerError, "Internal Error") pa.RenderError(http.StatusInternalServerError, "Internal Error")

View File

@ -164,10 +164,16 @@ func (t *TargetAPI) Get() {
// Post ... // Post ...
func (t *TargetAPI) Post() { func (t *TargetAPI) Post() {
target := &models.RepTarget{} target := &models.RepTarget{}
t.DecodeJSONReq(target) t.DecodeJSONReqAndValidate(target)
if len(target.Name) == 0 || len(target.URL) == 0 { ta, err := dao.GetRepTargetByName(target.Name)
t.CustomAbort(http.StatusBadRequest, "name or URL is nil") if err != nil {
log.Errorf("failed to get target %s: %v", target.Name, err)
t.CustomAbort(http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError))
}
if ta != nil {
t.CustomAbort(http.StatusConflict, "name is already used")
} }
if len(target.Password) != 0 { if len(target.Password) != 0 {
@ -187,16 +193,32 @@ func (t *TargetAPI) Post() {
func (t *TargetAPI) Put() { func (t *TargetAPI) Put() {
id := t.getIDFromURL() id := t.getIDFromURL()
if id == 0 { if id == 0 {
t.CustomAbort(http.StatusBadRequest, http.StatusText(http.StatusBadRequest)) t.CustomAbort(http.StatusBadRequest, "id can not be empty or 0")
} }
target := &models.RepTarget{} target := &models.RepTarget{}
t.DecodeJSONReq(target) t.DecodeJSONReqAndValidate(target)
if target.ID == 0 { originTarget, err := dao.GetRepTarget(id)
target.ID = id if err != nil {
log.Errorf("failed to get target %d: %v", id, err)
t.CustomAbort(http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError))
} }
if target.Name != originTarget.Name {
ta, err := dao.GetRepTargetByName(target.Name)
if err != nil {
log.Errorf("failed to get target %s: %v", target.Name, err)
t.CustomAbort(http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError))
}
if ta != nil {
t.CustomAbort(http.StatusConflict, "name is already used")
}
}
target.ID = id
if len(target.Password) != 0 { if len(target.Password) != 0 {
target.Password = utils.ReversibleEncrypt(target.Password) target.Password = utils.ReversibleEncrypt(target.Password)
} }

View File

@ -766,6 +766,78 @@ func TestAddRepTarget(t *testing.T) {
} }
} }
func TestGetRepTargetByName(t *testing.T) {
target, err := GetRepTarget(targetID)
if err != nil {
t.Fatalf("failed to get target %d: %v", targetID, err)
}
target2, err := GetRepTargetByName(target.Name)
if err != nil {
t.Fatalf("failed to get target %s: %v", target.Name, err)
}
if target.Name != target2.Name {
t.Errorf("unexpected target name: %s, expected: %s", target2.Name, target.Name)
}
}
func TestUpdateRepTarget(t *testing.T) {
target := &models.RepTarget{
Name: "name",
URL: "http://url",
Username: "username",
Password: "password",
}
id, err := AddRepTarget(*target)
if err != nil {
t.Fatalf("failed to add target: %v", err)
}
defer func() {
if err := DeleteRepTarget(id); err != nil {
t.Logf("failed to delete target %d: %v", id, err)
}
}()
target.ID = id
target.Name = "new_name"
target.URL = "http://new_url"
target.Username = "new_username"
target.Password = "new_password"
if err = UpdateRepTarget(*target); err != nil {
t.Fatalf("failed to update target: %v", err)
}
target, err = GetRepTarget(id)
if err != nil {
t.Fatalf("failed to get target %d: %v", id, err)
}
if target.Name != "new_name" {
t.Errorf("unexpected name: %s, expected: %s", target.Name, "new_name")
}
if target.URL != "http://new_url" {
t.Errorf("unexpected url: %s, expected: %s", target.URL, "http://new_url")
}
if target.Username != "new_username" {
t.Errorf("unexpected username: %s, expected: %s", target.Username, "new_username")
}
if target.Password != "new_password" {
t.Errorf("unexpected password: %s, expected: %s", target.Password, "new_password")
}
}
func TestGetAllRepTargets(t *testing.T) {
if _, err := GetAllRepTargets(); err != nil {
t.Fatalf("failed to get all targets: %v", err)
}
}
func TestAddRepPolicy(t *testing.T) { func TestAddRepPolicy(t *testing.T) {
policy := models.RepPolicy{ policy := models.RepPolicy{
ProjectID: 1, ProjectID: 1,
@ -800,6 +872,23 @@ func TestAddRepPolicy(t *testing.T) {
} }
func TestGetRepPolicyByName(t *testing.T) {
policy, err := GetRepPolicy(policyID)
if err != nil {
t.Fatalf("failed to get policy %d: %v", policyID, err)
}
policy2, err := GetRepPolicyByName(policy.Name)
if err != nil {
t.Fatalf("failed to get policy %s: %v", policy.Name, err)
}
if policy.Name != policy2.Name {
t.Errorf("unexpected name: %s, expected: %s", policy2.Name, policy.Name)
}
}
func TestDisableRepPolicy(t *testing.T) { func TestDisableRepPolicy(t *testing.T) {
err := DisableRepPolicy(policyID) err := DisableRepPolicy(policyID)
if err != nil { if err != nil {

View File

@ -11,13 +11,13 @@ import (
// AddRepTarget ... // AddRepTarget ...
func AddRepTarget(target models.RepTarget) (int64, error) { func AddRepTarget(target models.RepTarget) (int64, error) {
o := orm.NewOrm() o := GetOrmer()
return o.Insert(&target) return o.Insert(&target)
} }
// GetRepTarget ... // GetRepTarget ...
func GetRepTarget(id int64) (*models.RepTarget, error) { func GetRepTarget(id int64) (*models.RepTarget, error) {
o := orm.NewOrm() o := GetOrmer()
t := models.RepTarget{ID: id} t := models.RepTarget{ID: id}
err := o.Read(&t) err := o.Read(&t)
if err == orm.ErrNoRows { if err == orm.ErrNoRows {
@ -26,28 +26,34 @@ func GetRepTarget(id int64) (*models.RepTarget, error) {
return &t, err return &t, err
} }
// GetRepTargetByName ...
func GetRepTargetByName(name string) (*models.RepTarget, error) {
o := GetOrmer()
t := models.RepTarget{Name: name}
err := o.Read(&t, "Name")
if err == orm.ErrNoRows {
return nil, nil
}
return &t, err
}
// DeleteRepTarget ... // DeleteRepTarget ...
func DeleteRepTarget(id int64) error { func DeleteRepTarget(id int64) error {
o := orm.NewOrm() o := GetOrmer()
_, err := o.Delete(&models.RepTarget{ID: id}) _, err := o.Delete(&models.RepTarget{ID: id})
return err return err
} }
// UpdateRepTarget ... // UpdateRepTarget ...
func UpdateRepTarget(target models.RepTarget) error { func UpdateRepTarget(target models.RepTarget) error {
o := orm.NewOrm() o := GetOrmer()
if len(target.Password) != 0 { _, err := o.Update(&target, "URL", "Name", "Username", "Password")
_, err := o.Update(&target)
return err
}
_, err := o.Update(&target, "URL", "Name", "Username")
return err return err
} }
// GetAllRepTargets ... // GetAllRepTargets ...
func GetAllRepTargets() ([]*models.RepTarget, error) { func GetAllRepTargets() ([]*models.RepTarget, error) {
o := orm.NewOrm() o := GetOrmer()
qs := o.QueryTable(&models.RepTarget{}) qs := o.QueryTable(&models.RepTarget{})
var targets []*models.RepTarget var targets []*models.RepTarget
_, err := qs.All(&targets) _, err := qs.All(&targets)
@ -56,7 +62,7 @@ func GetAllRepTargets() ([]*models.RepTarget, error) {
// AddRepPolicy ... // AddRepPolicy ...
func AddRepPolicy(policy models.RepPolicy) (int64, error) { func AddRepPolicy(policy models.RepPolicy) (int64, error) {
o := orm.NewOrm() o := GetOrmer()
sqlTpl := `insert into replication_policy (name, project_id, target_id, enabled, description, cron_str, start_time, creation_time, update_time ) values (?, ?, ?, ?, ?, ?, %s, NOW(), NOW())` sqlTpl := `insert into replication_policy (name, project_id, target_id, enabled, description, cron_str, start_time, creation_time, update_time ) values (?, ?, ?, ?, ?, ?, %s, NOW(), NOW())`
var sql string var sql string
if policy.Enabled == 1 { if policy.Enabled == 1 {
@ -78,7 +84,7 @@ func AddRepPolicy(policy models.RepPolicy) (int64, error) {
// GetRepPolicy ... // GetRepPolicy ...
func GetRepPolicy(id int64) (*models.RepPolicy, error) { func GetRepPolicy(id int64) (*models.RepPolicy, error) {
o := orm.NewOrm() o := GetOrmer()
p := models.RepPolicy{ID: id} p := models.RepPolicy{ID: id}
err := o.Read(&p) err := o.Read(&p)
if err == orm.ErrNoRows { if err == orm.ErrNoRows {
@ -87,24 +93,35 @@ func GetRepPolicy(id int64) (*models.RepPolicy, error) {
return &p, err return &p, err
} }
// GetRepPolicyByName ...
func GetRepPolicyByName(name string) (*models.RepPolicy, error) {
o := GetOrmer()
p := models.RepPolicy{Name: name}
err := o.Read(&p, "Name")
if err == orm.ErrNoRows {
return nil, nil
}
return &p, err
}
// GetRepPolicyByProject ... // GetRepPolicyByProject ...
func GetRepPolicyByProject(projectID int64) ([]*models.RepPolicy, error) { func GetRepPolicyByProject(projectID int64) ([]*models.RepPolicy, error) {
var res []*models.RepPolicy var res []*models.RepPolicy
o := orm.NewOrm() o := GetOrmer()
_, err := o.QueryTable("replication_policy").Filter("project_id", projectID).All(&res) _, err := o.QueryTable("replication_policy").Filter("project_id", projectID).All(&res)
return res, err return res, err
} }
// DeleteRepPolicy ... // DeleteRepPolicy ...
func DeleteRepPolicy(id int64) error { func DeleteRepPolicy(id int64) error {
o := orm.NewOrm() o := GetOrmer()
_, err := o.Delete(&models.RepPolicy{ID: id}) _, err := o.Delete(&models.RepPolicy{ID: id})
return err return err
} }
// UpdateRepPolicyEnablement ... // UpdateRepPolicyEnablement ...
func UpdateRepPolicyEnablement(id int64, enabled int) error { func UpdateRepPolicyEnablement(id int64, enabled int) error {
o := orm.NewOrm() o := GetOrmer()
p := models.RepPolicy{ p := models.RepPolicy{
ID: id, ID: id,
Enabled: enabled} Enabled: enabled}
@ -125,7 +142,7 @@ func DisableRepPolicy(id int64) error {
// AddRepJob ... // AddRepJob ...
func AddRepJob(job models.RepJob) (int64, error) { func AddRepJob(job models.RepJob) (int64, error) {
o := orm.NewOrm() o := GetOrmer()
if len(job.Status) == 0 { if len(job.Status) == 0 {
job.Status = models.JobPending job.Status = models.JobPending
} }
@ -137,7 +154,7 @@ func AddRepJob(job models.RepJob) (int64, error) {
// GetRepJob ... // GetRepJob ...
func GetRepJob(id int64) (*models.RepJob, error) { func GetRepJob(id int64) (*models.RepJob, error) {
o := orm.NewOrm() o := GetOrmer()
j := models.RepJob{ID: id} j := models.RepJob{ID: id}
err := o.Read(&j) err := o.Read(&j)
if err == orm.ErrNoRows { if err == orm.ErrNoRows {
@ -164,20 +181,20 @@ func GetRepJobToStop(policyID int64) ([]*models.RepJob, error) {
} }
func repJobPolicyIDQs(policyID int64) orm.QuerySeter { func repJobPolicyIDQs(policyID int64) orm.QuerySeter {
o := orm.NewOrm() o := GetOrmer()
return o.QueryTable("replication_job").Filter("policy_id", policyID) return o.QueryTable("replication_job").Filter("policy_id", policyID)
} }
// DeleteRepJob ... // DeleteRepJob ...
func DeleteRepJob(id int64) error { func DeleteRepJob(id int64) error {
o := orm.NewOrm() o := GetOrmer()
_, err := o.Delete(&models.RepJob{ID: id}) _, err := o.Delete(&models.RepJob{ID: id})
return err return err
} }
// UpdateRepJobStatus ... // UpdateRepJobStatus ...
func UpdateRepJobStatus(id int64, status string) error { func UpdateRepJobStatus(id int64, status string) error {
o := orm.NewOrm() o := GetOrmer()
j := models.RepJob{ j := models.RepJob{
ID: id, ID: id,
Status: status, Status: status,

View File

@ -2,6 +2,8 @@ package models
import ( import (
"time" "time"
"github.com/astaxie/beego/validation"
) )
const ( const (
@ -42,6 +44,33 @@ type RepPolicy struct {
UpdateTime time.Time `orm:"column(update_time);auto_now" json:"update_time"` UpdateTime time.Time `orm:"column(update_time);auto_now" json:"update_time"`
} }
// Valid ...
func (r *RepPolicy) Valid(v *validation.Validation) {
if len(r.Name) == 0 {
v.SetError("name", "can not be empty")
}
if len(r.Name) > 256 {
v.SetError("name", "max length is 256")
}
if r.ProjectID <= 0 {
v.SetError("project_id", "invalid")
}
if r.TargetID <= 0 {
v.SetError("target_id", "invalid")
}
if r.Enabled != 0 && r.Enabled != 1 {
v.SetError("enabled", "must be 0 or 1")
}
if len(r.CronStr) > 256 {
v.SetError("cron_str", "max length is 256")
}
}
// RepJob is the model for a replication job, which is the execution unit on job service, currently it is used to transfer/remove // RepJob is the model for a replication job, which is the execution unit on job service, currently it is used to transfer/remove
// a repository to/from a remote registry instance. // a repository to/from a remote registry instance.
type RepJob struct { type RepJob struct {
@ -68,17 +97,42 @@ type RepTarget struct {
UpdateTime time.Time `orm:"column(update_time);auto_now" json:"update_time"` UpdateTime time.Time `orm:"column(update_time);auto_now" json:"update_time"`
} }
// Valid ...
func (r *RepTarget) Valid(v *validation.Validation) {
if len(r.Name) == 0 {
v.SetError("name", "can not be empty")
}
if len(r.Name) > 64 {
v.SetError("name", "max length is 64")
}
if len(r.URL) == 0 {
v.SetError("endpoint", "can not be empty")
}
if len(r.URL) > 64 {
v.SetError("endpoint", "max length is 64")
}
// password is encoded using base64, the length of this field
// in DB is 64, so the max length in request is 48
if len(r.Password) > 48 {
v.SetError("password", "max length is 48")
}
}
//TableName is required by by beego orm to map RepTarget to table replication_target //TableName is required by by beego orm to map RepTarget to table replication_target
func (rt *RepTarget) TableName() string { func (r *RepTarget) TableName() string {
return "replication_target" return "replication_target"
} }
//TableName is required by by beego orm to map RepJob to table replication_job //TableName is required by by beego orm to map RepJob to table replication_job
func (rj *RepJob) TableName() string { func (r *RepJob) TableName() string {
return "replication_job" return "replication_job"
} }
//TableName is required by by beego orm to map RepPolicy to table replication_policy //TableName is required by by beego orm to map RepPolicy to table replication_policy
func (rp *RepPolicy) TableName() string { func (r *RepPolicy) TableName() string {
return "replication_policy" return "replication_policy"
} }