mirror of
https://github.com/goharbor/harbor.git
synced 2024-09-29 13:57:33 +02:00
c9c2db7c13
Don't fail the process when one tag replication failed Signed-off-by: Wenkai Yin <yinw@vmware.com>
197 lines
5.1 KiB
Go
197 lines
5.1 KiB
Go
// Copyright Project Harbor Authors
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package event
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
|
|
"github.com/goharbor/harbor/src/replication/util"
|
|
|
|
"github.com/goharbor/harbor/src/common/utils/log"
|
|
"github.com/goharbor/harbor/src/replication/config"
|
|
"github.com/goharbor/harbor/src/replication/model"
|
|
"github.com/goharbor/harbor/src/replication/operation"
|
|
"github.com/goharbor/harbor/src/replication/policy"
|
|
"github.com/goharbor/harbor/src/replication/registry"
|
|
)
|
|
|
|
// Handler is the handler to handle event
|
|
type Handler interface {
|
|
Handle(event *Event) error
|
|
}
|
|
|
|
// NewHandler ...
|
|
func NewHandler(policyCtl policy.Controller, registryMgr registry.Manager, opCtl operation.Controller) Handler {
|
|
return &handler{
|
|
policyCtl: policyCtl,
|
|
registryMgr: registryMgr,
|
|
opCtl: opCtl,
|
|
}
|
|
}
|
|
|
|
type handler struct {
|
|
policyCtl policy.Controller
|
|
registryMgr registry.Manager
|
|
opCtl operation.Controller
|
|
}
|
|
|
|
func (h *handler) Handle(event *Event) error {
|
|
if event == nil || event.Resource == nil ||
|
|
event.Resource.Metadata == nil ||
|
|
len(event.Resource.Metadata.Vtags) == 0 {
|
|
return errors.New("invalid event")
|
|
}
|
|
var policies []*model.Policy
|
|
var err error
|
|
switch event.Type {
|
|
case EventTypeImagePush, EventTypeChartUpload,
|
|
EventTypeImageDelete, EventTypeChartDelete:
|
|
policies, err = h.getRelatedPolicies(event.Resource)
|
|
default:
|
|
return fmt.Errorf("unsupported event type %s", event.Type)
|
|
}
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if len(policies) == 0 {
|
|
log.Debugf("no policy found for the event %v, do nothing", event)
|
|
return nil
|
|
}
|
|
|
|
for _, policy := range policies {
|
|
if err := PopulateRegistries(h.registryMgr, policy); err != nil {
|
|
return err
|
|
}
|
|
id, err := h.opCtl.StartReplication(policy, event.Resource, model.TriggerTypeEventBased)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
log.Debugf("%s event received, the replication execution %d started", event.Type, id)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (h *handler) getRelatedPolicies(resource *model.Resource) ([]*model.Policy, error) {
|
|
_, policies, err := h.policyCtl.List()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
result := []*model.Policy{}
|
|
for _, policy := range policies {
|
|
// disabled
|
|
if !policy.Enabled {
|
|
continue
|
|
}
|
|
// currently, the events are produced only by local Harbor,
|
|
// so they should only apply to the policies whose source registry is local Harbor
|
|
if !(policy.SrcRegistry == nil || policy.SrcRegistry.ID == 0) {
|
|
continue
|
|
}
|
|
// has no trigger
|
|
if policy.Trigger == nil {
|
|
continue
|
|
}
|
|
// trigger type isn't event based
|
|
if policy.Trigger.Type != model.TriggerTypeEventBased {
|
|
continue
|
|
}
|
|
// doesn't replicate deletion
|
|
if resource.Deleted && !policy.Deletion {
|
|
continue
|
|
}
|
|
// doesn't match the name filter
|
|
m, err := match(policy.Filters, resource)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if !m {
|
|
continue
|
|
}
|
|
result = append(result, policy)
|
|
}
|
|
return result, nil
|
|
}
|
|
|
|
// TODO unify the match logic with other?
|
|
func match(filters []*model.Filter, resource *model.Resource) (bool, error) {
|
|
match := true
|
|
repository := resource.Metadata.Repository.Name
|
|
for _, filter := range filters {
|
|
if filter.Type != model.FilterTypeName {
|
|
continue
|
|
}
|
|
m, err := util.Match(filter.Value.(string), repository)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
if !m {
|
|
match = false
|
|
break
|
|
}
|
|
}
|
|
return match, nil
|
|
}
|
|
|
|
// PopulateRegistries populates the source registry and destination registry properties for policy
|
|
func PopulateRegistries(registryMgr registry.Manager, policy *model.Policy) error {
|
|
if policy == nil {
|
|
return nil
|
|
}
|
|
registry, err := getRegistry(registryMgr, policy.SrcRegistry)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
policy.SrcRegistry = registry
|
|
registry, err = getRegistry(registryMgr, policy.DestRegistry)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
policy.DestRegistry = registry
|
|
return nil
|
|
}
|
|
|
|
func getRegistry(registryMgr registry.Manager, registry *model.Registry) (*model.Registry, error) {
|
|
if registry == nil || registry.ID == 0 {
|
|
return GetLocalRegistry(), nil
|
|
}
|
|
reg, err := registryMgr.Get(registry.ID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if reg == nil {
|
|
return nil, fmt.Errorf("registry %d not found", registry.ID)
|
|
}
|
|
return reg, nil
|
|
}
|
|
|
|
// GetLocalRegistry returns the info of the local Harbor registry
|
|
func GetLocalRegistry() *model.Registry {
|
|
return &model.Registry{
|
|
Type: model.RegistryTypeHarbor,
|
|
Name: "Local",
|
|
URL: config.Config.CoreURL,
|
|
TokenServiceURL: config.Config.TokenServiceURL,
|
|
Status: "healthy",
|
|
Credential: &model.Credential{
|
|
Type: model.CredentialTypeSecret,
|
|
// use secret to do the auth for the local Harbor
|
|
AccessSecret: config.Config.JobserviceSecret,
|
|
},
|
|
Insecure: true,
|
|
}
|
|
}
|