support tag transfer and deletion

This commit is contained in:
Wenkai Yin 2016-05-27 10:45:21 +08:00
parent 039d94c13d
commit cdac114955
9 changed files with 78 additions and 29 deletions

View File

@ -74,7 +74,7 @@ func (pa *RepPolicyAPI) Post() {
}
go func() {
if err := TriggerReplication(pid, "", models.RepOpTransfer); err != nil {
if err := TriggerReplication(pid, "", nil, models.RepOpTransfer); err != nil {
log.Errorf("failed to trigger replication of %d: %v", pid, err)
} else {
log.Infof("replication of %d triggered", pid)
@ -108,7 +108,7 @@ func (pa *RepPolicyAPI) UpdateEnablement() {
if e.Enabled == 1 {
go func() {
if err := TriggerReplication(pa.policyID, "", models.RepOpTransfer); err != nil {
if err := TriggerReplication(pa.policyID, "", nil, models.RepOpTransfer); err != nil {
log.Errorf("failed to trigger replication of %d: %v", pa.policyID, err)
} else {
log.Infof("replication of %d triggered", pa.policyID)

View File

@ -145,6 +145,8 @@ func (ra *RepositoryAPI) Delete() {
ra.CustomAbort(http.StatusInternalServerError, "internal error")
}
log.Infof("delete tag: %s %s", repoName, t)
go TriggerReplicationByRepository(repoName, []string{t}, models.RepOpDelete)
}
go func() {

View File

@ -16,7 +16,6 @@
package api
import (
"encoding/base64"
"fmt"
"net"
"net/http"
@ -25,6 +24,7 @@ import (
"github.com/vmware/harbor/dao"
"github.com/vmware/harbor/models"
"github.com/vmware/harbor/utils"
"github.com/vmware/harbor/utils/log"
registry_util "github.com/vmware/harbor/utils/registry"
"github.com/vmware/harbor/utils/registry/auth"
@ -76,13 +76,11 @@ func (t *TargetAPI) Ping() {
password = target.Password
if len(password) != 0 {
b, err := base64.StdEncoding.DecodeString(password)
password, err = utils.ReversibleDecrypt(password)
if err != nil {
log.Errorf("failed to decode password: %v", err)
log.Errorf("failed to decrypt password: %v", err)
t.CustomAbort(http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError))
}
password = string(b)
}
} else {
endpoint = t.GetString("endpoint")
@ -142,12 +140,12 @@ func (t *TargetAPI) Get() {
for _, target := range targets {
if len(target.Password) != 0 {
b, err := base64.StdEncoding.DecodeString(target.Password)
str, err := utils.ReversibleDecrypt(target.Password)
if err != nil {
log.Errorf("failed to decode password: %v", err)
log.Errorf("failed to decrypt password: %v", err)
t.CustomAbort(http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError))
}
target.Password = string(b)
target.Password = str
}
}
@ -167,12 +165,12 @@ func (t *TargetAPI) Get() {
}
if len(target.Password) != 0 {
b, err := base64.StdEncoding.DecodeString(target.Password)
pwd, err := utils.ReversibleDecrypt(target.Password)
if err != nil {
log.Errorf("failed to decode password: %v", err)
log.Errorf("failed to decrypt password: %v", err)
t.CustomAbort(http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError))
}
target.Password = string(b)
target.Password = pwd
}
t.Data["json"] = target
@ -189,7 +187,7 @@ func (t *TargetAPI) Post() {
}
if len(target.Password) != 0 {
target.Password = base64.StdEncoding.EncodeToString([]byte(target.Password))
target.Password = utils.ReversibleEncrypt(target.Password)
}
id, err := dao.AddRepTarget(*target)
@ -216,7 +214,7 @@ func (t *TargetAPI) Put() {
}
if len(target.Password) != 0 {
target.Password = base64.StdEncoding.EncodeToString([]byte(target.Password))
target.Password = utils.ReversibleEncrypt(target.Password)
}
if err := dao.UpdateRepTarget(*target); err != nil {

View File

@ -21,6 +21,7 @@ import (
"fmt"
"io/ioutil"
"net/http"
"os"
"strings"
"github.com/vmware/harbor/dao"
@ -90,18 +91,21 @@ func checkUserExists(name string) int {
}
// TriggerReplication triggers the replication according to the policy
func TriggerReplication(policyID int64, repository, operation string) error {
func TriggerReplication(policyID int64, repository string,
tags []string, operation string) error {
data := struct {
PolicyID int64 `json:"policy_id"`
Repo string `json:"repository"`
Operation string `json:"operation"`
PolicyID int64 `json:"policy_id"`
Repo string `json:"repository"`
Operation string `json:"operation"`
TagList []string `json:"tags"`
}{
PolicyID: policyID,
Repo: repository,
TagList: tags,
Operation: operation,
}
b, err := json.Marshal(data)
b, err := json.Marshal(&data)
if err != nil {
return err
}
@ -146,7 +150,7 @@ func GetPoliciesByRepository(repository string) ([]*models.RepPolicy, error) {
return policies, nil
}
func TriggerReplicationByRepository(repository, operation string) {
func TriggerReplicationByRepository(repository string, tags []string, operation string) {
policies, err := GetPoliciesByRepository(repository)
if err != nil {
log.Errorf("failed to get policies for repository %s: %v", repository, err)
@ -154,7 +158,7 @@ func TriggerReplicationByRepository(repository, operation string) {
}
for _, policy := range policies {
if err := TriggerReplication(policy.ProjectID, repository, operation); err != nil {
if err := TriggerReplication(policy.ProjectID, repository, tags, operation); err != nil {
log.Errorf("failed to trigger replication of %d for %s: %v", policy.ID, repository, err)
} else {
log.Infof("replication of %d for %s triggered", policy.ID, repository)
@ -163,9 +167,25 @@ func TriggerReplicationByRepository(repository, operation string) {
}
func buildReplicationURL() string {
return "http://job_service/api/replicationJobs"
url := getJobServiceURL()
url = strings.TrimSpace(url)
url = strings.TrimRight(url, "/")
return fmt.Sprintf("%s/api/replicationJobs", url)
}
func buildJobLogURL(jobID string) string {
return fmt.Sprintf("http://job_service/api/replicationJobs/%s/log", jobID)
url := getJobServiceURL()
url = strings.TrimSpace(url)
url = strings.TrimRight(url, "/")
return fmt.Sprintf("%s/api/replicationJobs/%s/log", url, jobID)
}
func getJobServiceURL() string {
url := os.Getenv("JOB_SERVICE_URL")
if len(url) == 0 {
url = "http://job_service"
}
return url
}

View File

@ -153,7 +153,7 @@ func (c *Checker) Enter() (string, error) {
c.logger.Infof("project %s already exists on %s", c.project, c.dstURL)
if !canWrite {
err = fmt.Errorf("the user %s has no write privilege to project %s on %s", c.dstUsr, c.project, c.dstURL)
err = fmt.Errorf("the user %s is unauthorized to write to project %s on %s", c.dstUsr, c.project, c.dstURL)
c.logger.Errorf("%v", err)
return "", err
}

View File

@ -9,6 +9,7 @@ import (
"github.com/vmware/harbor/job/replication"
"github.com/vmware/harbor/job/utils"
"github.com/vmware/harbor/models"
uti "github.com/vmware/harbor/utils"
"github.com/vmware/harbor/utils/log"
)
@ -18,6 +19,7 @@ type RepJobParm struct {
TargetUsername string
TargetPassword string
Repository string
Tags []string
Enabled int
Operation string
}
@ -182,6 +184,7 @@ func (sm *JobSM) Reset(jid int64) error {
sm.Parms = &RepJobParm{
LocalRegURL: config.LocalHarborURL(),
Repository: job.Repository,
Tags: job.TagList,
Enabled: policy.Enabled,
Operation: job.Operation,
}
@ -198,7 +201,17 @@ func (sm *JobSM) Reset(jid int64) error {
}
sm.Parms.TargetURL = target.URL
sm.Parms.TargetUsername = target.Username
sm.Parms.TargetPassword = target.Password
pwd := target.Password
if len(pwd) != 0 {
pwd, err = uti.ReversibleDecrypt(pwd)
if err != nil {
return fmt.Errorf("failed to decrypt password: %v", err)
}
}
sm.Parms.TargetPassword = pwd
//init states handlers
sm.Logger = utils.NewLogger(sm.JobID)
sm.CurrentState = models.JobPending
@ -222,7 +235,7 @@ func (sm *JobSM) Reset(jid int64) error {
func addImgTransferTransition(sm *JobSM) error {
base, err := replication.InitBaseHandler(sm.Parms.Repository, sm.Parms.LocalRegURL, config.UISecret(),
sm.Parms.TargetURL, sm.Parms.TargetUsername, sm.Parms.TargetPassword,
nil, sm.Logger)
sm.Parms.Tags, sm.Logger)
if err != nil {
return err
}
@ -236,7 +249,7 @@ func addImgTransferTransition(sm *JobSM) error {
}
func addImgDeleteTransition(sm *JobSM) error {
deleter := replication.NewDeleter(sm.Parms.Repository, nil, sm.Parms.TargetURL,
deleter := replication.NewDeleter(sm.Parms.Repository, sm.Parms.Tags, sm.Parms.TargetURL,
sm.Parms.TargetUsername, sm.Parms.TargetPassword, sm.Logger)
sm.AddTransition(models.JobRunning, replication.StateDelete, deleter)

View File

@ -79,7 +79,7 @@ func (n *NotificationHandler) Post() {
}
}()
go api.TriggerReplicationByRepository(repo, models.RepOpTransfer)
go api.TriggerReplicationByRepository(repo, []string{repoTag}, models.RepOpTransfer)
}
}
}

View File

@ -63,6 +63,10 @@ func initRouters() {
beego.Router("/api/repositories", &api.RepositoryAPI{})
beego.Router("/api/repositories/tags", &api.RepositoryAPI{}, "get:GetTags")
beego.Router("/api/repositories/manifests", &api.RepositoryAPI{}, "get:GetManifests")
beego.Router("/api/replicationJobs", &api.RepJobAPI{})
beego.Router("/api/replicationJobs/:id/log", &api.RepJobAPI{}, "get:GetLog")
beego.Router("/api/replicationPolicies", &api.RepPolicyAPI{})
beego.Router("/api/replicationPolicies/:id/enablement", &api.RepPolicyAPI{}, "put:UpdateEnablement")
beego.Router("/api/targets/?:id", &api.TargetAPI{})
beego.Router("/api/target_ping", &api.TargetAPI{}, "get:Ping")

View File

@ -17,6 +17,7 @@ package utils
import (
"crypto/sha1"
"encoding/base64"
"fmt"
"golang.org/x/crypto/pbkdf2"
@ -26,3 +27,14 @@ import (
func Encrypt(content string, salt string) string {
return fmt.Sprintf("%x", pbkdf2.Key([]byte(content), []byte(salt), 4096, 16, sha1.New))
}
// ReversibleEncrypt encrypts the str with base64
func ReversibleEncrypt(str string) string {
return base64.StdEncoding.EncodeToString([]byte(str))
}
// ReversibleDecrypt decrypts the str with base64
func ReversibleDecrypt(str string) (string, error) {
b, err := base64.StdEncoding.DecodeString(str)
return string(b), err
}