mirror of
https://github.com/goharbor/harbor.git
synced 2024-12-22 16:48:30 +01:00
Merge pull request #6093 from cd1989/replication-record-id
Add op uuid to image replication
This commit is contained in:
commit
ec2ad4d0b8
@ -1506,6 +1506,11 @@ paths:
|
|||||||
format: int
|
format: int
|
||||||
required: true
|
required: true
|
||||||
description: The ID of the policy that triggered this job.
|
description: The ID of the policy that triggered this job.
|
||||||
|
- name: op_uuid
|
||||||
|
in: query
|
||||||
|
type: string
|
||||||
|
required: false
|
||||||
|
description: The UUID of one trigger of replication policy.
|
||||||
- name: num
|
- name: num
|
||||||
in: query
|
in: query
|
||||||
type: integer
|
type: integer
|
||||||
@ -2018,6 +2023,8 @@ paths:
|
|||||||
responses:
|
responses:
|
||||||
'200':
|
'200':
|
||||||
description: Trigger the replication successfully.
|
description: Trigger the replication successfully.
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/ReplicationResponse'
|
||||||
'401':
|
'401':
|
||||||
description: User need to log in first.
|
description: User need to log in first.
|
||||||
'404':
|
'404':
|
||||||
@ -4083,6 +4090,12 @@ definitions:
|
|||||||
policy_id:
|
policy_id:
|
||||||
type: integer
|
type: integer
|
||||||
description: The ID of replication policy
|
description: The ID of replication policy
|
||||||
|
ReplicationResponse:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
uuid:
|
||||||
|
type: string
|
||||||
|
description: UUID of the replication
|
||||||
RepositoryDescription:
|
RepositoryDescription:
|
||||||
type: object
|
type: object
|
||||||
properties:
|
properties:
|
||||||
|
@ -0,0 +1 @@
|
|||||||
|
ALTER TABLE replication_job ADD COLUMN op_uuid varchar(64);
|
@ -356,6 +356,9 @@ func repJobQueryConditions(query ...*models.RepJobQuery) orm.QuerySeter {
|
|||||||
if q.PolicyID != 0 {
|
if q.PolicyID != 0 {
|
||||||
qs = qs.Filter("PolicyID", q.PolicyID)
|
qs = qs.Filter("PolicyID", q.PolicyID)
|
||||||
}
|
}
|
||||||
|
if len(q.OpUUID) > 0 {
|
||||||
|
qs = qs.Filter("OpUUID__exact", q.OpUUID)
|
||||||
|
}
|
||||||
if len(q.Repository) > 0 {
|
if len(q.Repository) > 0 {
|
||||||
qs = qs.Filter("Repository__icontains", q.Repository)
|
qs = qs.Filter("Repository__icontains", q.Repository)
|
||||||
}
|
}
|
||||||
|
@ -54,15 +54,15 @@ type RepPolicy struct {
|
|||||||
// 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 {
|
||||||
ID int64 `orm:"pk;auto;column(id)" json:"id"`
|
ID int64 `orm:"pk;auto;column(id)" json:"id"`
|
||||||
Status string `orm:"column(status)" json:"status"`
|
Status string `orm:"column(status)" json:"status"`
|
||||||
Repository string `orm:"column(repository)" json:"repository"`
|
Repository string `orm:"column(repository)" json:"repository"`
|
||||||
PolicyID int64 `orm:"column(policy_id)" json:"policy_id"`
|
PolicyID int64 `orm:"column(policy_id)" json:"policy_id"`
|
||||||
Operation string `orm:"column(operation)" json:"operation"`
|
OpUUID string `orm:"column(op_uuid)" json:"op_uuid"`
|
||||||
Tags string `orm:"column(tags)" json:"-"`
|
Operation string `orm:"column(operation)" json:"operation"`
|
||||||
TagList []string `orm:"-" json:"tags"`
|
Tags string `orm:"column(tags)" json:"-"`
|
||||||
UUID string `orm:"column(job_uuid)" json:"-"`
|
TagList []string `orm:"-" json:"tags"`
|
||||||
// Policy RepPolicy `orm:"-" json:"policy"`
|
UUID string `orm:"column(job_uuid)" json:"-"`
|
||||||
CreationTime time.Time `orm:"column(creation_time);auto_now_add" json:"creation_time"`
|
CreationTime time.Time `orm:"column(creation_time);auto_now_add" json:"creation_time"`
|
||||||
UpdateTime time.Time `orm:"column(update_time);auto_now" json:"update_time"`
|
UpdateTime time.Time `orm:"column(update_time);auto_now" json:"update_time"`
|
||||||
}
|
}
|
||||||
@ -126,6 +126,7 @@ func (r *RepPolicy) TableName() string {
|
|||||||
// RepJobQuery holds query conditions for replication job
|
// RepJobQuery holds query conditions for replication job
|
||||||
type RepJobQuery struct {
|
type RepJobQuery struct {
|
||||||
PolicyID int64
|
PolicyID int64
|
||||||
|
OpUUID string
|
||||||
Repository string
|
Repository string
|
||||||
Statuses []string
|
Statuses []string
|
||||||
Operations []string
|
Operations []string
|
||||||
|
@ -23,6 +23,11 @@ type Replication struct {
|
|||||||
PolicyID int64 `json:"policy_id"`
|
PolicyID int64 `json:"policy_id"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ReplicationResponse describes response of a replication request, it gives
|
||||||
|
type ReplicationResponse struct {
|
||||||
|
UUID string `json:"uuid"`
|
||||||
|
}
|
||||||
|
|
||||||
// Valid ...
|
// Valid ...
|
||||||
func (r *Replication) Valid(v *validation.Validation) {
|
func (r *Replication) Valid(v *validation.Validation) {
|
||||||
if r.PolicyID <= 0 {
|
if r.PolicyID <= 0 {
|
||||||
|
@ -17,6 +17,7 @@ package api
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/goharbor/harbor/src/common/dao"
|
"github.com/goharbor/harbor/src/common/dao"
|
||||||
"github.com/goharbor/harbor/src/common/models"
|
"github.com/goharbor/harbor/src/common/models"
|
||||||
@ -26,6 +27,8 @@ import (
|
|||||||
"github.com/goharbor/harbor/src/replication/core"
|
"github.com/goharbor/harbor/src/replication/core"
|
||||||
"github.com/goharbor/harbor/src/replication/event/notification"
|
"github.com/goharbor/harbor/src/replication/event/notification"
|
||||||
"github.com/goharbor/harbor/src/replication/event/topic"
|
"github.com/goharbor/harbor/src/replication/event/topic"
|
||||||
|
|
||||||
|
"github.com/docker/distribution/uuid"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ReplicationAPI handles API calls for replication
|
// ReplicationAPI handles API calls for replication
|
||||||
@ -78,16 +81,27 @@ func (r *ReplicationAPI) Post() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = startReplication(replication.PolicyID); err != nil {
|
opUUID, err := startReplication(replication.PolicyID)
|
||||||
|
if err != nil {
|
||||||
r.HandleInternalServerError(fmt.Sprintf("failed to publish replication topic for policy %d: %v", replication.PolicyID, err))
|
r.HandleInternalServerError(fmt.Sprintf("failed to publish replication topic for policy %d: %v", replication.PolicyID, err))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
log.Infof("replication signal for policy %d sent", replication.PolicyID)
|
log.Infof("replication signal for policy %d sent", replication.PolicyID)
|
||||||
|
|
||||||
|
r.Data["json"] = api_models.ReplicationResponse{
|
||||||
|
UUID: opUUID,
|
||||||
|
}
|
||||||
|
r.ServeJSON()
|
||||||
}
|
}
|
||||||
|
|
||||||
func startReplication(policyID int64) error {
|
// startReplication triggers a replication and return the uuid of this replication.
|
||||||
return notifier.Publish(topic.StartReplicationTopic,
|
func startReplication(policyID int64) (string, error) {
|
||||||
|
opUUID := strings.Replace(uuid.Generate().String(), "-", "", -1)
|
||||||
|
return opUUID, notifier.Publish(topic.StartReplicationTopic,
|
||||||
notification.StartReplicationNotification{
|
notification.StartReplicationNotification{
|
||||||
PolicyID: policyID,
|
PolicyID: policyID,
|
||||||
|
Metadata: map[string]interface{}{
|
||||||
|
"op_uuid": opUUID,
|
||||||
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -94,6 +94,7 @@ func (ra *RepJobAPI) List() {
|
|||||||
|
|
||||||
query.Repository = ra.GetString("repository")
|
query.Repository = ra.GetString("repository")
|
||||||
query.Statuses = ra.GetStrings("status")
|
query.Statuses = ra.GetStrings("status")
|
||||||
|
query.OpUUID = ra.GetString("op_uuid")
|
||||||
|
|
||||||
startTimeStr := ra.GetString("start_time")
|
startTimeStr := ra.GetString("start_time")
|
||||||
if len(startTimeStr) != 0 {
|
if len(startTimeStr) != 0 {
|
||||||
|
@ -192,7 +192,7 @@ func (pa *RepPolicyAPI) Post() {
|
|||||||
|
|
||||||
if policy.ReplicateExistingImageNow {
|
if policy.ReplicateExistingImageNow {
|
||||||
go func() {
|
go func() {
|
||||||
if err = startReplication(id); err != nil {
|
if _, err = startReplication(id); err != nil {
|
||||||
log.Errorf("failed to send replication signal for policy %d: %v", id, err)
|
log.Errorf("failed to send replication signal for policy %d: %v", id, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -304,7 +304,7 @@ func (pa *RepPolicyAPI) Put() {
|
|||||||
|
|
||||||
if policy.ReplicateExistingImageNow {
|
if policy.ReplicateExistingImageNow {
|
||||||
go func() {
|
go func() {
|
||||||
if err = startReplication(id); err != nil {
|
if _, err = startReplication(id); err != nil {
|
||||||
log.Errorf("failed to send replication signal for policy %d: %v", id, err)
|
log.Errorf("failed to send replication signal for policy %d: %v", id, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -16,6 +16,8 @@ package core
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
"strings"
|
||||||
|
|
||||||
common_models "github.com/goharbor/harbor/src/common/models"
|
common_models "github.com/goharbor/harbor/src/common/models"
|
||||||
"github.com/goharbor/harbor/src/common/utils/log"
|
"github.com/goharbor/harbor/src/common/utils/log"
|
||||||
@ -27,6 +29,8 @@ import (
|
|||||||
"github.com/goharbor/harbor/src/replication/source"
|
"github.com/goharbor/harbor/src/replication/source"
|
||||||
"github.com/goharbor/harbor/src/replication/target"
|
"github.com/goharbor/harbor/src/replication/target"
|
||||||
"github.com/goharbor/harbor/src/replication/trigger"
|
"github.com/goharbor/harbor/src/replication/trigger"
|
||||||
|
|
||||||
|
"github.com/docker/distribution/uuid"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Controller defines the methods that a replicatoin controllter should implement
|
// Controller defines the methods that a replicatoin controllter should implement
|
||||||
@ -220,9 +224,16 @@ func (ctl *DefaultController) Replicate(policyID int64, metadata ...map[string]i
|
|||||||
targets = append(targets, target)
|
targets = append(targets, target)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get operation uuid from metadata, if none provided, generate one.
|
||||||
|
opUUID, err := getOpUUID(metadata...)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
// submit the replication
|
// submit the replication
|
||||||
return ctl.replicator.Replicate(&replicator.Replication{
|
return ctl.replicator.Replicate(&replicator.Replication{
|
||||||
PolicyID: policyID,
|
PolicyID: policyID,
|
||||||
|
OpUUID: opUUID,
|
||||||
Candidates: candidates,
|
Candidates: candidates,
|
||||||
Targets: targets,
|
Targets: targets,
|
||||||
})
|
})
|
||||||
@ -290,3 +301,26 @@ func buildFilterChain(policy *models.ReplicationPolicy, sourcer *source.Sourcer)
|
|||||||
|
|
||||||
return source.NewDefaultFilterChain(filters)
|
return source.NewDefaultFilterChain(filters)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// getOpUUID get operation uuid from metadata or generate one if none found.
|
||||||
|
func getOpUUID(metadata ...map[string]interface{}) (string, error) {
|
||||||
|
if len(metadata) <= 0 {
|
||||||
|
return strings.Replace(uuid.Generate().String(), "-", "", -1), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
opUUID, ok := metadata[0]["op_uuid"]
|
||||||
|
if !ok {
|
||||||
|
return strings.Replace(uuid.Generate().String(), "-", "", -1), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
id, ok := opUUID.(string)
|
||||||
|
if !ok {
|
||||||
|
return "", fmt.Errorf("operation uuid should have type 'string', but got '%s'", reflect.TypeOf(opUUID).Name())
|
||||||
|
}
|
||||||
|
|
||||||
|
if id == "" {
|
||||||
|
return "", fmt.Errorf("provided operation uuid is empty")
|
||||||
|
}
|
||||||
|
|
||||||
|
return id, nil
|
||||||
|
}
|
||||||
|
@ -156,3 +156,26 @@ func TestBuildFilterChain(t *testing.T) {
|
|||||||
chain := buildFilterChain(policy, sourcer)
|
chain := buildFilterChain(policy, sourcer)
|
||||||
assert.Equal(t, 3, len(chain.Filters()))
|
assert.Equal(t, 3, len(chain.Filters()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestGetOpUUID(t *testing.T) {
|
||||||
|
uuid, err := getOpUUID()
|
||||||
|
assert.Nil(t, err)
|
||||||
|
assert.NotEmpty(t, uuid)
|
||||||
|
|
||||||
|
uuid, err = getOpUUID(map[string]interface{}{
|
||||||
|
"name": "test",
|
||||||
|
})
|
||||||
|
assert.Nil(t, err)
|
||||||
|
assert.NotEmpty(t, uuid)
|
||||||
|
|
||||||
|
uuid, err = getOpUUID(map[string]interface{}{
|
||||||
|
"op_uuid": 0,
|
||||||
|
})
|
||||||
|
assert.NotNil(t, err)
|
||||||
|
|
||||||
|
uuid, err = getOpUUID(map[string]interface{}{
|
||||||
|
"op_uuid": "0",
|
||||||
|
})
|
||||||
|
assert.Nil(t, err)
|
||||||
|
assert.Equal(t, uuid, "0")
|
||||||
|
}
|
||||||
|
@ -30,6 +30,7 @@ import (
|
|||||||
// Replication holds information for a replication
|
// Replication holds information for a replication
|
||||||
type Replication struct {
|
type Replication struct {
|
||||||
PolicyID int64
|
PolicyID int64
|
||||||
|
OpUUID string
|
||||||
Candidates []models.FilterItem
|
Candidates []models.FilterItem
|
||||||
Targets []*common_models.RepTarget
|
Targets []*common_models.RepTarget
|
||||||
Operation string
|
Operation string
|
||||||
@ -60,6 +61,9 @@ func (d *DefaultReplicator) Replicate(replication *Replication) error {
|
|||||||
operation := ""
|
operation := ""
|
||||||
for _, candidate := range replication.Candidates {
|
for _, candidate := range replication.Candidates {
|
||||||
strs := strings.SplitN(candidate.Value, ":", 2)
|
strs := strings.SplitN(candidate.Value, ":", 2)
|
||||||
|
if len(strs) != 2 {
|
||||||
|
return fmt.Errorf("malforld image '%s'", candidate.Value)
|
||||||
|
}
|
||||||
repositories[strs[0]] = append(repositories[strs[0]], strs[1])
|
repositories[strs[0]] = append(repositories[strs[0]], strs[1])
|
||||||
operation = candidate.Operation
|
operation = candidate.Operation
|
||||||
}
|
}
|
||||||
@ -69,6 +73,7 @@ func (d *DefaultReplicator) Replicate(replication *Replication) error {
|
|||||||
// create job in database
|
// create job in database
|
||||||
id, err := dao.AddRepJob(common_models.RepJob{
|
id, err := dao.AddRepJob(common_models.RepJob{
|
||||||
PolicyID: replication.PolicyID,
|
PolicyID: replication.PolicyID,
|
||||||
|
OpUUID: replication.OpUUID,
|
||||||
Repository: repository,
|
Repository: repository,
|
||||||
TagList: tags,
|
TagList: tags,
|
||||||
Operation: operation,
|
Operation: operation,
|
||||||
|
Loading…
Reference in New Issue
Block a user