mirror of
https://github.com/goharbor/harbor
synced 2025-05-13 23:29:58 +00:00
support tag transfer and deletion
This commit is contained in:
parent
039d94c13d
commit
cdac114955
@ -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)
|
||||
|
@ -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() {
|
||||
|
@ -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 {
|
||||
|
38
api/utils.go
38
api/utils.go
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -79,7 +79,7 @@ func (n *NotificationHandler) Post() {
|
||||
}
|
||||
}()
|
||||
|
||||
go api.TriggerReplicationByRepository(repo, models.RepOpTransfer)
|
||||
go api.TriggerReplicationByRepository(repo, []string{repoTag}, models.RepOpTransfer)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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")
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user