mirror of
https://github.com/goharbor/harbor.git
synced 2024-11-26 20:26:13 +01:00
Merge pull request #7220 from ywk253100/190324_adapter
Update the registry adapter interface
This commit is contained in:
commit
1d4cf53462
@ -2431,38 +2431,11 @@ paths:
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/definitions/ReplicationAdapter'
|
||||
'401':
|
||||
description: Unauthorized.
|
||||
'403':
|
||||
description: Forbidden.
|
||||
'500':
|
||||
description: Unexpected internal errors.
|
||||
/replication/adapters/{type}:
|
||||
get:
|
||||
summary: Get the specified adapter.
|
||||
description: |
|
||||
This endpoint let user get the specified adapter.
|
||||
parameters:
|
||||
- name: type
|
||||
in: path
|
||||
type: string
|
||||
required: true
|
||||
description: The adapter type.
|
||||
tags:
|
||||
- Products
|
||||
responses:
|
||||
'200':
|
||||
description: Success.
|
||||
schema:
|
||||
type: object
|
||||
$ref: '#/definitions/ReplicationAdapter'
|
||||
'401':
|
||||
description: Unauthorized.
|
||||
'403':
|
||||
description: Forbidden.
|
||||
'404':
|
||||
description: Not found.
|
||||
'500':
|
||||
description: Unexpected internal errors.
|
||||
/registries:
|
||||
@ -2595,6 +2568,30 @@ paths:
|
||||
description: Registry does not exist.
|
||||
'500':
|
||||
description: Unexpected internal errors.
|
||||
'/registries/{id}/info':
|
||||
get:
|
||||
summary: Get registry info.
|
||||
description: Get the info of one specific registry.
|
||||
tags:
|
||||
- Products
|
||||
parameters:
|
||||
- name: id
|
||||
in: path
|
||||
type: integer
|
||||
format: int64
|
||||
required: true
|
||||
description: The registry ID.
|
||||
responses:
|
||||
'200':
|
||||
description: Get registry successfully.
|
||||
schema:
|
||||
$ref: '#/definitions/RegistryInfo'
|
||||
'401':
|
||||
description: User need to log in first.
|
||||
'404':
|
||||
description: Registry not found
|
||||
'500':
|
||||
description: Unexpected internal errors.
|
||||
'/targets/{id}/policies/':
|
||||
get:
|
||||
summary: List the target relevant policies.
|
||||
@ -5279,34 +5276,29 @@ definitions:
|
||||
action:
|
||||
type: string
|
||||
description: The permission action
|
||||
ReplicationAdapter:
|
||||
RegistryInfo:
|
||||
type: object
|
||||
description: The replication adapter
|
||||
description: The registry info contains the base info and capability declarations of the registry
|
||||
properties:
|
||||
type:
|
||||
type: string
|
||||
description: The adapter type
|
||||
description: The registry type
|
||||
description:
|
||||
type: string
|
||||
description: The adapter description
|
||||
supported_resource_types:
|
||||
type: array
|
||||
description: The resource types that the adapter supports
|
||||
items:
|
||||
type: string
|
||||
description: The description
|
||||
supported_resource_filters:
|
||||
type: array
|
||||
description: The filters that the adapter supports
|
||||
description: The filters that the registry supports
|
||||
items:
|
||||
$ref: '#/definitions/ReplicationAdapterFilter'
|
||||
$ref: '#/definitions/FilterStyle'
|
||||
supported_triggers:
|
||||
type: array
|
||||
description: The triggers that the adapter supports
|
||||
description: The triggers that the registry supports
|
||||
items:
|
||||
type: string
|
||||
ReplicationAdapterFilter:
|
||||
FilterStyle:
|
||||
type: object
|
||||
description: The replication adapter filter
|
||||
description: The style of the resource filter
|
||||
properties:
|
||||
type:
|
||||
type: string
|
||||
|
@ -154,7 +154,6 @@ func init() {
|
||||
beego.Router("/api/projects/:pid([0-9]+)/robots/:id([0-9]+)", &RobotAPI{}, "get:Get;put:Put;delete:Delete")
|
||||
|
||||
beego.Router("/api/replication/adapters", &ReplicationAdapterAPI{}, "get:List")
|
||||
beego.Router("/api/replication/adapters/:type", &ReplicationAdapterAPI{}, "get:Get")
|
||||
beego.Router("/api/replication/executions", &ReplicationOperationAPI{}, "get:ListExecutions;post:CreateExecution")
|
||||
beego.Router("/api/replication/executions/:id([0-9]+)", &ReplicationOperationAPI{}, "put:StopExecution")
|
||||
beego.Router("/api/replication/executions/:id([0-9]+)/tasks", &ReplicationOperationAPI{}, "get:ListTasks")
|
||||
|
@ -9,6 +9,7 @@ import (
|
||||
"github.com/goharbor/harbor/src/common/utils/log"
|
||||
"github.com/goharbor/harbor/src/core/api/models"
|
||||
"github.com/goharbor/harbor/src/replication/ng"
|
||||
"github.com/goharbor/harbor/src/replication/ng/adapter"
|
||||
"github.com/goharbor/harbor/src/replication/ng/model"
|
||||
"github.com/goharbor/harbor/src/replication/ng/registry"
|
||||
)
|
||||
@ -211,3 +212,63 @@ func (t *RegistryAPI) Delete() {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// GetInfo returns the base info and capability declarations of the registry
|
||||
func (t *RegistryAPI) GetInfo() {
|
||||
id := t.GetIDFromURL()
|
||||
registry, err := t.manager.Get(id)
|
||||
if err != nil {
|
||||
t.HandleInternalServerError(fmt.Sprintf("failed to get registry %d: %v", id, err))
|
||||
return
|
||||
}
|
||||
if registry == nil {
|
||||
t.HandleNotFound(fmt.Sprintf("registry %d not found", id))
|
||||
return
|
||||
}
|
||||
factory, err := adapter.GetFactory(registry.Type)
|
||||
if err != nil {
|
||||
t.HandleInternalServerError(fmt.Sprintf("failed to get the adapter factory for registry type %s: %v", registry.Type, err))
|
||||
return
|
||||
}
|
||||
adp, err := factory(registry)
|
||||
if err != nil {
|
||||
t.HandleInternalServerError(fmt.Sprintf("failed to create the adapter for registry %d: %v", registry.ID, err))
|
||||
return
|
||||
}
|
||||
info, err := adp.Info()
|
||||
if err != nil {
|
||||
t.HandleInternalServerError(fmt.Sprintf("failed to get registry info %d: %v", id, err))
|
||||
return
|
||||
}
|
||||
t.WriteJSONData(process(info))
|
||||
}
|
||||
|
||||
// merge "SupportedResourceTypes" into "SupportedResourceFilters" for UI to render easier
|
||||
func process(info *model.RegistryInfo) *model.RegistryInfo {
|
||||
if info == nil {
|
||||
return nil
|
||||
}
|
||||
in := &model.RegistryInfo{
|
||||
Type: info.Type,
|
||||
Description: info.Description,
|
||||
SupportedTriggers: info.SupportedTriggers,
|
||||
}
|
||||
filters := []*model.FilterStyle{}
|
||||
for _, filter := range info.SupportedResourceFilters {
|
||||
if filter.Type != model.FilterTypeResource {
|
||||
filters = append(filters, filter)
|
||||
}
|
||||
}
|
||||
values := []string{}
|
||||
for _, resourceType := range info.SupportedResourceTypes {
|
||||
values = append(values, string(resourceType))
|
||||
}
|
||||
filters = append(filters, &model.FilterStyle{
|
||||
Type: model.FilterTypeResource,
|
||||
Style: model.FilterStyleTypeRadio,
|
||||
Values: values,
|
||||
})
|
||||
in.SupportedResourceFilters = filters
|
||||
|
||||
return in
|
||||
}
|
||||
|
@ -15,8 +15,6 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/goharbor/harbor/src/replication/ng/adapter"
|
||||
"github.com/goharbor/harbor/src/replication/ng/model"
|
||||
)
|
||||
@ -41,53 +39,7 @@ func (r *ReplicationAdapterAPI) Prepare() {
|
||||
|
||||
// List the replication adapters
|
||||
func (r *ReplicationAdapterAPI) List() {
|
||||
infos := []*adapter.Info{}
|
||||
for _, info := range adapter.ListAdapterInfos() {
|
||||
infos = append(infos, process(info))
|
||||
}
|
||||
r.WriteJSONData(infos)
|
||||
}
|
||||
|
||||
// Get one specified replication adapter
|
||||
func (r *ReplicationAdapterAPI) Get() {
|
||||
t := r.GetStringFromPath(":type")
|
||||
info := adapter.GetAdapterInfo(model.RegistryType(t))
|
||||
if info == nil {
|
||||
r.HandleNotFound(fmt.Sprintf("adapter for %s not found", t))
|
||||
return
|
||||
}
|
||||
info = process(info)
|
||||
r.WriteJSONData(info)
|
||||
}
|
||||
|
||||
// merge "SupportedResourceTypes" into "SupportedResourceFilters" for UI to render easier
|
||||
func process(info *adapter.Info) *adapter.Info {
|
||||
if info == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
in := &adapter.Info{
|
||||
Type: info.Type,
|
||||
Description: info.Description,
|
||||
SupportedTriggers: info.SupportedTriggers,
|
||||
}
|
||||
|
||||
filters := []*adapter.Filter{}
|
||||
for _, filter := range info.SupportedResourceFilters {
|
||||
if filter.Type != model.FilterTypeResource {
|
||||
filters = append(filters, filter)
|
||||
}
|
||||
}
|
||||
values := []string{}
|
||||
for _, resourceType := range info.SupportedResourceTypes {
|
||||
values = append(values, string(resourceType))
|
||||
}
|
||||
filters = append(filters, &adapter.Filter{
|
||||
Type: model.FilterTypeResource,
|
||||
Style: adapter.FilterStyleRadio,
|
||||
Values: values,
|
||||
})
|
||||
in.SupportedResourceFilters = filters
|
||||
|
||||
return in
|
||||
types := []model.RegistryType{}
|
||||
types = append(types, adapter.ListRegisteredAdapterTypes()...)
|
||||
r.WriteJSONData(types)
|
||||
}
|
||||
|
@ -24,57 +24,19 @@ import (
|
||||
"github.com/goharbor/harbor/src/replication/ng/model"
|
||||
)
|
||||
|
||||
func TestReplicationAdapterAPIList(t *testing.T) {
|
||||
cases := []*codeCheckingCase{
|
||||
// 401
|
||||
{
|
||||
request: &testingRequest{
|
||||
method: http.MethodGet,
|
||||
url: "/api/replication/adapters",
|
||||
},
|
||||
code: http.StatusUnauthorized,
|
||||
},
|
||||
// 403
|
||||
{
|
||||
request: &testingRequest{
|
||||
method: http.MethodGet,
|
||||
url: "/api/replication/adapters",
|
||||
credential: nonSysAdmin,
|
||||
},
|
||||
code: http.StatusForbidden,
|
||||
},
|
||||
// 200
|
||||
{
|
||||
request: &testingRequest{
|
||||
method: http.MethodGet,
|
||||
url: "/api/replication/adapters",
|
||||
credential: sysAdmin,
|
||||
},
|
||||
code: http.StatusOK,
|
||||
},
|
||||
}
|
||||
|
||||
runCodeCheckingCases(t, cases...)
|
||||
}
|
||||
|
||||
func fakedFactory(*model.Registry) (adapter.Adapter, error) {
|
||||
return nil, nil
|
||||
}
|
||||
func TestReplicationAdapterAPIGet(t *testing.T) {
|
||||
err := adapter.RegisterFactory(
|
||||
&adapter.Info{
|
||||
Type: "test",
|
||||
SupportedResourceTypes: []model.ResourceType{"image"},
|
||||
SupportedTriggers: []model.TriggerType{"mannual"},
|
||||
}, fakedFactory)
|
||||
require.Nil(t, err)
|
||||
|
||||
func TestReplicationAdapterAPIList(t *testing.T) {
|
||||
err := adapter.RegisterFactory("test", fakedFactory)
|
||||
require.Nil(t, err)
|
||||
cases := []*codeCheckingCase{
|
||||
// 401
|
||||
{
|
||||
request: &testingRequest{
|
||||
method: http.MethodGet,
|
||||
url: "/api/replication/adapters/test",
|
||||
url: "/api/replication/adapters",
|
||||
},
|
||||
code: http.StatusUnauthorized,
|
||||
},
|
||||
@ -82,25 +44,16 @@ func TestReplicationAdapterAPIGet(t *testing.T) {
|
||||
{
|
||||
request: &testingRequest{
|
||||
method: http.MethodGet,
|
||||
url: "/api/replication/adapters/test",
|
||||
url: "/api/replication/adapters",
|
||||
credential: nonSysAdmin,
|
||||
},
|
||||
code: http.StatusForbidden,
|
||||
},
|
||||
// 404
|
||||
{
|
||||
request: &testingRequest{
|
||||
method: http.MethodGet,
|
||||
url: "/api/replication/adapters/gcs",
|
||||
credential: sysAdmin,
|
||||
},
|
||||
code: http.StatusNotFound,
|
||||
},
|
||||
// 200
|
||||
{
|
||||
request: &testingRequest{
|
||||
method: http.MethodGet,
|
||||
url: "/api/replication/adapters/test",
|
||||
url: "/api/replication/adapters",
|
||||
credential: sysAdmin,
|
||||
},
|
||||
code: http.StatusOK,
|
||||
|
@ -101,7 +101,6 @@ func initRouters() {
|
||||
beego.Router("/api/logs", &api.LogAPI{})
|
||||
|
||||
beego.Router("/api/replication/adapters", &api.ReplicationAdapterAPI{}, "get:List")
|
||||
beego.Router("/api/replication/adapters/:type", &api.ReplicationAdapterAPI{}, "get:Get")
|
||||
beego.Router("/api/replication/executions", &api.ReplicationOperationAPI{}, "get:ListExecutions;post:CreateExecution")
|
||||
beego.Router("/api/replication/executions/:id([0-9]+)", &api.ReplicationOperationAPI{}, "put:StopExecution")
|
||||
beego.Router("/api/replication/executions/:id([0-9]+)/tasks", &api.ReplicationOperationAPI{}, "get:ListTasks")
|
||||
@ -136,6 +135,7 @@ func initRouters() {
|
||||
|
||||
beego.Router("/api/registries", &api.RegistryAPI{}, "get:List;post:Post")
|
||||
beego.Router("/api/registries/:id([0-9]+)", &api.RegistryAPI{}, "get:Get;put:Put;delete:Delete")
|
||||
beego.Router("/api/registries/:id([0-9]+)/info", &api.RegistryAPI{}, "get:GetInfo")
|
||||
|
||||
beego.Router("/v2/*", &controllers.RegistryProxy{}, "*:Handle")
|
||||
|
||||
|
@ -21,45 +21,15 @@ import (
|
||||
"github.com/goharbor/harbor/src/replication/ng/model"
|
||||
)
|
||||
|
||||
// As the Info isn't a valid map key, so we use the slice
|
||||
// as the adapter registry
|
||||
var registry = []*item{}
|
||||
|
||||
// const definition
|
||||
const (
|
||||
FilterStyleText = "input"
|
||||
FilterStyleRadio = "radio"
|
||||
)
|
||||
|
||||
// FilterStyle is used for UI to determine how to render the filter
|
||||
type FilterStyle string
|
||||
|
||||
type item struct {
|
||||
info *Info
|
||||
factory Factory
|
||||
}
|
||||
|
||||
// Filter ...
|
||||
type Filter struct {
|
||||
Type model.FilterType `json:"type"`
|
||||
Style FilterStyle `json:"style"`
|
||||
Values []string `json:"values,omitempty"`
|
||||
}
|
||||
|
||||
// Info provides base info and capability declarations of the adapter
|
||||
type Info struct {
|
||||
Type model.RegistryType `json:"type"`
|
||||
Description string `json:"description"`
|
||||
SupportedResourceTypes []model.ResourceType `json:"-"`
|
||||
SupportedResourceFilters []*Filter `json:"supported_resource_filters"`
|
||||
SupportedTriggers []model.TriggerType `json:"supported_triggers"`
|
||||
}
|
||||
var registry = map[model.RegistryType]Factory{}
|
||||
|
||||
// Factory creates a specific Adapter according to the params
|
||||
type Factory func(*model.Registry) (Adapter, error)
|
||||
|
||||
// Adapter interface defines the capabilities of registry
|
||||
type Adapter interface {
|
||||
// Info return the information of this adapter
|
||||
Info() (*model.RegistryInfo, error)
|
||||
// Lists the available namespaces under the specified registry with the
|
||||
// provided credential/token
|
||||
ListNamespaces(*model.NamespaceQuery) ([]*model.Namespace, error)
|
||||
@ -73,56 +43,35 @@ type Adapter interface {
|
||||
}
|
||||
|
||||
// RegisterFactory registers one adapter factory to the registry
|
||||
func RegisterFactory(info *Info, factory Factory) error {
|
||||
if len(info.Type) == 0 {
|
||||
func RegisterFactory(t model.RegistryType, factory Factory) error {
|
||||
if len(t) == 0 {
|
||||
return errors.New("invalid registry type")
|
||||
}
|
||||
if len(info.SupportedResourceTypes) == 0 {
|
||||
return errors.New("must support at least one resource type")
|
||||
}
|
||||
if len(info.SupportedTriggers) == 0 {
|
||||
return errors.New("must support at least one trigger")
|
||||
}
|
||||
if factory == nil {
|
||||
return errors.New("empty adapter factory")
|
||||
}
|
||||
for _, item := range registry {
|
||||
if item.info.Type == info.Type {
|
||||
return fmt.Errorf("adapter factory for %s already exists", info.Type)
|
||||
|
||||
if _, exist := registry[t]; exist {
|
||||
return fmt.Errorf("adapter factory for %s already exists", t)
|
||||
}
|
||||
}
|
||||
registry = append(registry, &item{
|
||||
info: info,
|
||||
factory: factory,
|
||||
})
|
||||
registry[t] = factory
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetFactory gets the adapter factory by the specified name
|
||||
func GetFactory(t model.RegistryType) (Factory, error) {
|
||||
for _, item := range registry {
|
||||
if item.info.Type == t {
|
||||
return item.factory, nil
|
||||
}
|
||||
}
|
||||
factory, exist := registry[t]
|
||||
if !exist {
|
||||
return nil, fmt.Errorf("adapter factory for %s not found", t)
|
||||
}
|
||||
return factory, nil
|
||||
}
|
||||
|
||||
// ListAdapterInfos lists the info of registered Adapters
|
||||
func ListAdapterInfos() []*Info {
|
||||
infos := []*Info{}
|
||||
for _, item := range registry {
|
||||
infos = append(infos, item.info)
|
||||
// ListRegisteredAdapterTypes lists the registered Adapter type
|
||||
func ListRegisteredAdapterTypes() []model.RegistryType {
|
||||
types := []model.RegistryType{}
|
||||
for t := range registry {
|
||||
types = append(types, t)
|
||||
}
|
||||
return infos
|
||||
}
|
||||
|
||||
// GetAdapterInfo returns the info of a specified registry type
|
||||
func GetAdapterInfo(t model.RegistryType) *Info {
|
||||
for _, item := range registry {
|
||||
if item.info.Type == t {
|
||||
return item.info
|
||||
}
|
||||
}
|
||||
return nil
|
||||
return types
|
||||
}
|
||||
|
@ -28,49 +28,18 @@ func fakedFactory(*model.Registry) (Adapter, error) {
|
||||
|
||||
func TestRegisterFactory(t *testing.T) {
|
||||
// empty type
|
||||
assert.NotNil(t, RegisterFactory(&Info{}, nil))
|
||||
// empty supportted resource type
|
||||
assert.NotNil(t, RegisterFactory(
|
||||
&Info{
|
||||
Type: "harbor",
|
||||
}, nil))
|
||||
// empty trigger
|
||||
assert.NotNil(t, RegisterFactory(
|
||||
&Info{
|
||||
Type: "harbor",
|
||||
SupportedResourceTypes: []model.ResourceType{"image"},
|
||||
}, nil))
|
||||
assert.NotNil(t, RegisterFactory("", nil))
|
||||
// empty factory
|
||||
assert.NotNil(t, RegisterFactory(
|
||||
&Info{
|
||||
Type: "harbor",
|
||||
SupportedResourceTypes: []model.ResourceType{"image"},
|
||||
SupportedTriggers: []model.TriggerType{"mannual"},
|
||||
}, nil))
|
||||
assert.NotNil(t, RegisterFactory("harbor", nil))
|
||||
// pass
|
||||
assert.Nil(t, RegisterFactory(
|
||||
&Info{
|
||||
Type: "harbor",
|
||||
SupportedResourceTypes: []model.ResourceType{"image"},
|
||||
SupportedTriggers: []model.TriggerType{"mannual"},
|
||||
}, fakedFactory))
|
||||
assert.Nil(t, RegisterFactory("harbor", fakedFactory))
|
||||
// already exists
|
||||
assert.NotNil(t, RegisterFactory(
|
||||
&Info{
|
||||
Type: "harbor",
|
||||
SupportedResourceTypes: []model.ResourceType{"image"},
|
||||
SupportedTriggers: []model.TriggerType{"mannual"},
|
||||
}, fakedFactory))
|
||||
assert.NotNil(t, RegisterFactory("harbor", fakedFactory))
|
||||
}
|
||||
|
||||
func TestGetFactory(t *testing.T) {
|
||||
registry = []*item{}
|
||||
require.Nil(t, RegisterFactory(
|
||||
&Info{
|
||||
Type: "harbor",
|
||||
SupportedResourceTypes: []model.ResourceType{"image"},
|
||||
SupportedTriggers: []model.TriggerType{"mannual"},
|
||||
}, fakedFactory))
|
||||
registry = map[model.RegistryType]Factory{}
|
||||
require.Nil(t, RegisterFactory("harbor", fakedFactory))
|
||||
// doesn't exist
|
||||
_, err := GetFactory("gcr")
|
||||
assert.NotNil(t, err)
|
||||
@ -79,40 +48,16 @@ func TestGetFactory(t *testing.T) {
|
||||
assert.Nil(t, err)
|
||||
}
|
||||
|
||||
func TestListAdapterInfos(t *testing.T) {
|
||||
registry = []*item{}
|
||||
func TestListRegisteredAdapterTypes(t *testing.T) {
|
||||
registry = map[model.RegistryType]Factory{}
|
||||
// not register, got nothing
|
||||
infos := ListAdapterInfos()
|
||||
assert.Equal(t, 0, len(infos))
|
||||
types := ListRegisteredAdapterTypes()
|
||||
assert.Equal(t, 0, len(types))
|
||||
|
||||
// register one factory
|
||||
require.Nil(t, RegisterFactory(
|
||||
&Info{
|
||||
Type: "harbor",
|
||||
SupportedResourceTypes: []model.ResourceType{"image"},
|
||||
SupportedTriggers: []model.TriggerType{"mannual"},
|
||||
}, fakedFactory))
|
||||
require.Nil(t, RegisterFactory("harbor", fakedFactory))
|
||||
|
||||
infos = ListAdapterInfos()
|
||||
require.Equal(t, 1, len(infos))
|
||||
assert.Equal(t, "harbor", string(infos[0].Type))
|
||||
}
|
||||
|
||||
func TestGetAdapterInfo(t *testing.T) {
|
||||
registry = []*item{}
|
||||
require.Nil(t, RegisterFactory(
|
||||
&Info{
|
||||
Type: "harbor",
|
||||
SupportedResourceTypes: []model.ResourceType{"image"},
|
||||
SupportedTriggers: []model.TriggerType{"mannual"},
|
||||
}, fakedFactory))
|
||||
|
||||
// doesn't exist
|
||||
info := GetAdapterInfo("gcr")
|
||||
assert.Nil(t, info)
|
||||
|
||||
// exist
|
||||
info = GetAdapterInfo("harbor")
|
||||
require.NotNil(t, info)
|
||||
assert.Equal(t, "harbor", string(info.Type))
|
||||
types = ListRegisteredAdapterTypes()
|
||||
require.Equal(t, 1, len(types))
|
||||
assert.Equal(t, model.RegistryType("harbor"), types[0])
|
||||
}
|
||||
|
@ -31,36 +31,10 @@ import (
|
||||
// TODO add UT
|
||||
|
||||
func init() {
|
||||
info := &adp.Info{
|
||||
Type: model.RegistryTypeHarbor,
|
||||
SupportedResourceTypes: []model.ResourceType{
|
||||
model.ResourceTypeRepository,
|
||||
model.ResourceTypeChart,
|
||||
},
|
||||
SupportedResourceFilters: []*adp.Filter{
|
||||
{
|
||||
Type: model.FilterTypeName,
|
||||
Style: adp.FilterStyleText,
|
||||
},
|
||||
{
|
||||
Type: model.FilterTypeVersion,
|
||||
Style: adp.FilterStyleText,
|
||||
},
|
||||
{
|
||||
Type: model.FilterTypeLabel,
|
||||
Style: adp.FilterStyleText,
|
||||
},
|
||||
},
|
||||
SupportedTriggers: []model.TriggerType{
|
||||
model.TriggerTypeManual,
|
||||
model.TriggerTypeScheduled,
|
||||
model.TriggerTypeEventBased,
|
||||
},
|
||||
}
|
||||
// TODO passing coreServiceURL and tokenServiceURL
|
||||
coreServiceURL := "http://core:8080"
|
||||
tokenServiceURL := ""
|
||||
if err := adp.RegisterFactory(info, func(registry *model.Registry) (adp.Adapter, error) {
|
||||
if err := adp.RegisterFactory(model.RegistryTypeHarbor, func(registry *model.Registry) (adp.Adapter, error) {
|
||||
return newAdapter(registry, coreServiceURL, tokenServiceURL), nil
|
||||
}); err != nil {
|
||||
log.Errorf("failed to register factory for %s: %v", model.RegistryTypeHarbor, err)
|
||||
@ -110,6 +84,45 @@ func newAdapter(registry *model.Registry, coreServiceURL string,
|
||||
}
|
||||
}
|
||||
|
||||
func (a *adapter) Info() (*model.RegistryInfo, error) {
|
||||
info := &model.RegistryInfo{
|
||||
Type: model.RegistryTypeHarbor,
|
||||
SupportedResourceTypes: []model.ResourceType{
|
||||
model.ResourceTypeRepository,
|
||||
},
|
||||
SupportedResourceFilters: []*model.FilterStyle{
|
||||
{
|
||||
Type: model.FilterTypeName,
|
||||
Style: model.FilterStyleTypeText,
|
||||
},
|
||||
{
|
||||
Type: model.FilterTypeTag,
|
||||
Style: model.FilterStyleTypeText,
|
||||
},
|
||||
{
|
||||
Type: model.FilterTypeLabel,
|
||||
Style: model.FilterStyleTypeText,
|
||||
},
|
||||
},
|
||||
SupportedTriggers: []model.TriggerType{
|
||||
model.TriggerTypeManual,
|
||||
model.TriggerTypeScheduled,
|
||||
model.TriggerTypeEventBased,
|
||||
},
|
||||
}
|
||||
|
||||
sys := &struct {
|
||||
ChartRegistryEnabled bool `json:"with_chartmuseum"`
|
||||
}{}
|
||||
if err := a.client.Get(a.coreServiceURL+"/api/systeminfo", sys); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if sys.ChartRegistryEnabled {
|
||||
info.SupportedResourceTypes = append(info.SupportedResourceTypes, model.ResourceTypeChart)
|
||||
}
|
||||
return info, nil
|
||||
}
|
||||
|
||||
// TODO implement the function
|
||||
func (a *adapter) ListNamespaces(*model.NamespaceQuery) ([]*model.Namespace, error) {
|
||||
return nil, nil
|
||||
|
@ -47,12 +47,11 @@ func NewCopyFlow(executionMgr execution.Manager, registryMgr registry.Manager,
|
||||
}
|
||||
|
||||
func (c *copyFlow) Run(interface{}) error {
|
||||
srcRegistry, dstRegistry, srcAdapter, dstAdapter, err := initialize(c.registryMgr, c.policy)
|
||||
_, dstRegistry, srcAdapter, dstAdapter, err := initialize(c.registryMgr, c.policy)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// TODO after refactoring the adapter register, the "srcRegistry.Type" is not needed
|
||||
srcResources, err := fetchResources(srcAdapter, srcRegistry.Type, c.policy)
|
||||
srcResources, err := fetchResources(srcAdapter, c.policy)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -87,7 +87,7 @@ func initialize(mgr registry.Manager, policy *model.Policy) (*model.Registry, *m
|
||||
}
|
||||
|
||||
// fetch resources from the source registry
|
||||
func fetchResources(adapter adp.Adapter, adapterType model.RegistryType, policy *model.Policy) ([]*model.Resource, error) {
|
||||
func fetchResources(adapter adp.Adapter, policy *model.Policy) ([]*model.Resource, error) {
|
||||
resTypes := []model.ResourceType{}
|
||||
filters := []*model.Filter{}
|
||||
for _, filter := range policy.Filters {
|
||||
@ -98,7 +98,11 @@ func fetchResources(adapter adp.Adapter, adapterType model.RegistryType, policy
|
||||
resTypes = append(resTypes, filter.Value.(model.ResourceType))
|
||||
}
|
||||
if len(resTypes) == 0 {
|
||||
resTypes = append(resTypes, adp.GetAdapterInfo(adapterType).SupportedResourceTypes...)
|
||||
info, err := adapter.Info()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get the adapter info: %v", err)
|
||||
}
|
||||
resTypes = append(resTypes, info.SupportedResourceTypes...)
|
||||
}
|
||||
|
||||
resources := []*model.Resource{}
|
||||
@ -167,7 +171,7 @@ func filterResources(resources []*model.Resource, filters []*model.Filter) ([]*m
|
||||
match = false
|
||||
break
|
||||
}
|
||||
case model.FilterTypeVersion:
|
||||
case model.FilterTypeTag:
|
||||
pattern, ok := filter.Value.(string)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("%v is not a valid string", filter.Value)
|
||||
|
@ -67,6 +67,16 @@ func fakedAdapterFactory(*model.Registry) (adapter.Adapter, error) {
|
||||
|
||||
type fakedAdapter struct{}
|
||||
|
||||
func (f *fakedAdapter) Info() (*model.RegistryInfo, error) {
|
||||
return &model.RegistryInfo{
|
||||
Type: model.RegistryTypeHarbor,
|
||||
SupportedResourceTypes: []model.ResourceType{
|
||||
model.ResourceTypeRepository,
|
||||
model.ResourceTypeChart,
|
||||
},
|
||||
SupportedTriggers: []model.TriggerType{model.TriggerTypeManual},
|
||||
}, nil
|
||||
}
|
||||
func (f *fakedAdapter) ListNamespaces(*model.NamespaceQuery) ([]*model.Namespace, error) {
|
||||
return nil, nil
|
||||
}
|
||||
@ -225,15 +235,7 @@ func TestMain(m *testing.M) {
|
||||
config.Config = &config.Configuration{
|
||||
RegistryURL: url,
|
||||
}
|
||||
if err := adapter.RegisterFactory(
|
||||
&adapter.Info{
|
||||
Type: model.RegistryTypeHarbor,
|
||||
SupportedResourceTypes: []model.ResourceType{
|
||||
model.ResourceTypeRepository,
|
||||
model.ResourceTypeChart,
|
||||
},
|
||||
SupportedTriggers: []model.TriggerType{model.TriggerTypeManual},
|
||||
}, fakedAdapterFactory); err != nil {
|
||||
if err := adapter.RegisterFactory(model.RegistryTypeHarbor, fakedAdapterFactory); err != nil {
|
||||
os.Exit(1)
|
||||
}
|
||||
os.Exit(m.Run())
|
||||
@ -255,7 +257,7 @@ func TestInitialize(t *testing.T) {
|
||||
func TestFetchResources(t *testing.T) {
|
||||
adapter := &fakedAdapter{}
|
||||
policy := &model.Policy{}
|
||||
resources, err := fetchResources(adapter, model.RegistryTypeHarbor, policy)
|
||||
resources, err := fetchResources(adapter, policy)
|
||||
require.Nil(t, err)
|
||||
assert.Equal(t, 2, len(resources))
|
||||
}
|
||||
@ -313,7 +315,7 @@ func TestFilterResources(t *testing.T) {
|
||||
Value: "library/harbor",
|
||||
},
|
||||
{
|
||||
Type: model.FilterTypeVersion,
|
||||
Type: model.FilterTypeTag,
|
||||
Value: "0.2.?",
|
||||
},
|
||||
}
|
||||
|
@ -23,14 +23,14 @@ import (
|
||||
|
||||
// const definition
|
||||
const (
|
||||
FilterTypeResource = "Resource"
|
||||
FilterTypeName = "Name"
|
||||
FilterTypeVersion = "Version"
|
||||
FilterTypeLabel = "Label"
|
||||
FilterTypeResource = "resource"
|
||||
FilterTypeName = "name"
|
||||
FilterTypeTag = "tag"
|
||||
FilterTypeLabel = "label"
|
||||
|
||||
TriggerTypeManual = "Manual"
|
||||
TriggerTypeScheduled = "Scheduled"
|
||||
TriggerTypeEventBased = "EventBased"
|
||||
TriggerTypeManual = "manual"
|
||||
TriggerTypeScheduled = "scheduled"
|
||||
TriggerTypeEventBased = "event_based"
|
||||
)
|
||||
|
||||
// Policy defines the structure of a replication policy
|
||||
|
@ -20,9 +20,13 @@ import (
|
||||
"github.com/goharbor/harbor/src/common/models"
|
||||
)
|
||||
|
||||
// const definition
|
||||
const (
|
||||
// RegistryTypeHarbor indicates registry type harbor
|
||||
RegistryTypeHarbor = "harbor"
|
||||
|
||||
FilterStyleTypeText = "input"
|
||||
FilterStyleTypeRadio = "radio"
|
||||
)
|
||||
|
||||
// RegistryType indicates the type of registry
|
||||
@ -74,3 +78,19 @@ type RegistryQuery struct {
|
||||
// Pagination specifies the pagination
|
||||
Pagination *models.Pagination
|
||||
}
|
||||
|
||||
// FilterStyle ...
|
||||
type FilterStyle struct {
|
||||
Type FilterType `json:"type"`
|
||||
Style string `json:"style"`
|
||||
Values []string `json:"values,omitempty"`
|
||||
}
|
||||
|
||||
// RegistryInfo provides base info and capability declarations of the registry
|
||||
type RegistryInfo struct {
|
||||
Type RegistryType `json:"type"`
|
||||
Description string `json:"description"`
|
||||
SupportedResourceTypes []ResourceType `json:"-"`
|
||||
SupportedResourceFilters []*FilterStyle `json:"supported_resource_filters"`
|
||||
SupportedTriggers []TriggerType `json:"supported_triggers"`
|
||||
}
|
||||
|
@ -98,7 +98,7 @@ func (d *defaultController) createFlow(executionID int64, policy *model.Policy,
|
||||
Value: resource.Metadata.Name,
|
||||
},
|
||||
{
|
||||
Type: model.FilterTypeVersion,
|
||||
Type: model.FilterTypeTag,
|
||||
// only support replicate one tag
|
||||
Value: resource.Metadata.Vtags[0],
|
||||
},
|
||||
|
Loading…
Reference in New Issue
Block a user