harbor/src/server/v2.0/handler/registry.go

311 lines
9.8 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 handler
import (
"context"
"fmt"
"strings"
"github.com/go-openapi/runtime/middleware"
"github.com/goharbor/harbor/src/common/rbac"
"github.com/goharbor/harbor/src/controller/registry"
"github.com/goharbor/harbor/src/lib"
"github.com/goharbor/harbor/src/lib/errors"
"github.com/goharbor/harbor/src/lib/q"
"github.com/goharbor/harbor/src/pkg/reg/model"
"github.com/goharbor/harbor/src/server/v2.0/models"
operation "github.com/goharbor/harbor/src/server/v2.0/restapi/operations/registry"
)
func newRegistryAPI() *registryAPI {
return &registryAPI{
ctl: registry.Ctl,
}
}
type registryAPI struct {
BaseAPI
ctl registry.Controller
}
func (r *registryAPI) CreateRegistry(ctx context.Context, params operation.CreateRegistryParams) middleware.Responder {
if err := r.RequireSystemAccess(ctx, rbac.ActionCreate, rbac.ResourceRegistry); err != nil {
return r.SendError(ctx, err)
}
registry := &model.Registry{
Name: params.Registry.Name,
Description: params.Registry.Description,
Type: params.Registry.Type,
URL: params.Registry.URL,
Insecure: params.Registry.Insecure,
}
if params.Registry.Credential != nil {
registry.Credential = &model.Credential{
Type: params.Registry.Credential.Type,
AccessKey: params.Registry.Credential.AccessKey,
AccessSecret: params.Registry.Credential.AccessSecret,
}
}
id, err := r.ctl.Create(ctx, registry)
if err != nil {
return r.SendError(ctx, err)
}
location := fmt.Sprintf("%s/%d", strings.TrimSuffix(params.HTTPRequest.URL.Path, "/"), id)
return operation.NewCreateRegistryCreated().WithLocation(location)
}
func (r *registryAPI) GetRegistry(ctx context.Context, params operation.GetRegistryParams) middleware.Responder {
if err := r.RequireSystemAccess(ctx, rbac.ActionRead, rbac.ResourceRegistry); err != nil {
return r.SendError(ctx, err)
}
registry, err := r.ctl.Get(ctx, params.ID)
if err != nil {
return r.SendError(ctx, err)
}
return operation.NewGetRegistryOK().WithPayload(convertRegistry(registry))
}
func (r *registryAPI) ListRegistries(ctx context.Context, params operation.ListRegistriesParams) middleware.Responder {
if err := r.RequireSystemAccess(ctx, rbac.ActionList, rbac.ResourceRegistry); err != nil {
return r.SendError(ctx, err)
}
query, err := r.BuildQuery(ctx, params.Q, params.Sort, params.Page, params.PageSize)
if err != nil {
return r.SendError(ctx, err)
}
// keep backward compatibility for the "name" query
if params.Name != nil {
query.Keywords["Name"] = q.NewFuzzyMatchValue(*params.Name)
}
total, err := r.ctl.Count(ctx, query)
if err != nil {
return r.SendError(ctx, err)
}
registries, err := r.ctl.List(ctx, query)
if err != nil {
return r.SendError(ctx, err)
}
var regs []*models.Registry
for _, registry := range registries {
regs = append(regs, convertRegistry(registry))
}
return operation.NewListRegistriesOK().WithXTotalCount(total).
WithLink(r.Links(ctx, params.HTTPRequest.URL, total, query.PageNumber, query.PageSize).String()).
WithPayload(regs)
}
func (r *registryAPI) DeleteRegistry(ctx context.Context, params operation.DeleteRegistryParams) middleware.Responder {
if err := r.RequireSystemAccess(ctx, rbac.ActionDelete, rbac.ResourceRegistry); err != nil {
return r.SendError(ctx, err)
}
if err := r.ctl.Delete(ctx, params.ID); err != nil {
return r.SendError(ctx, err)
}
return operation.NewDeleteRegistryOK()
}
func (r *registryAPI) UpdateRegistry(ctx context.Context, params operation.UpdateRegistryParams) middleware.Responder {
if err := r.RequireSystemAccess(ctx, rbac.ActionUpdate, rbac.ResourceRegistry); err != nil {
return r.SendError(ctx, err)
}
registry, err := r.ctl.Get(ctx, params.ID)
if err != nil {
return r.SendError(ctx, err)
}
if params.Registry != nil {
if params.Registry.Name != nil {
registry.Name = *params.Registry.Name
}
if params.Registry.Description != nil {
registry.Description = *params.Registry.Description
}
if params.Registry.URL != nil {
registry.URL = *params.Registry.URL
}
if params.Registry.Insecure != nil {
registry.Insecure = *params.Registry.Insecure
}
if registry.Credential == nil {
registry.Credential = &model.Credential{}
}
if params.Registry.CredentialType != nil {
registry.Credential.Type = *params.Registry.CredentialType
}
if params.Registry.AccessKey != nil {
registry.Credential.AccessKey = *params.Registry.AccessKey
}
if params.Registry.AccessSecret != nil {
registry.Credential.AccessSecret = *params.Registry.AccessSecret
}
}
if err := r.ctl.Update(ctx, registry); err != nil {
return r.SendError(ctx, err)
}
return operation.NewUpdateRegistryOK()
}
func (r *registryAPI) GetRegistryInfo(ctx context.Context, params operation.GetRegistryInfoParams) middleware.Responder {
if err := r.RequireSystemAccess(ctx, rbac.ActionRead, rbac.ResourceRegistry); err != nil {
return r.SendError(ctx, err)
}
info, err := r.ctl.GetInfo(ctx, params.ID)
if err != nil {
return r.SendError(ctx, err)
}
in := &models.RegistryInfo{
Description: info.Description,
Type: string(info.Type),
}
for _, filter := range info.SupportedResourceFilters {
in.SupportedResourceFilters = append(in.SupportedResourceFilters, &models.FilterStyle{
Style: filter.Style,
Type: string(filter.Type),
Values: filter.Values,
})
}
for _, trigger := range info.SupportedTriggers {
in.SupportedTriggers = append(in.SupportedTriggers, string(trigger))
}
// whether support copy by chunk
if info.SupportedCopyByChunk {
in.SupportedCopyByChunk = &info.SupportedCopyByChunk
}
return operation.NewGetRegistryInfoOK().WithPayload(in)
}
func (r *registryAPI) ListRegistryProviderTypes(ctx context.Context, params operation.ListRegistryProviderTypesParams) middleware.Responder {
if err := r.RequireSystemAccess(ctx, rbac.ActionList, rbac.ResourceReplicationAdapter); err != nil {
return r.SendError(ctx, err)
}
types, err := r.ctl.ListRegistryProviderTypes(ctx)
if err != nil {
return r.SendError(ctx, err)
}
return operation.NewListRegistryProviderTypesOK().WithPayload(types)
}
func (r *registryAPI) PingRegistry(ctx context.Context, params operation.PingRegistryParams) middleware.Responder {
if err := r.RequireSystemAccess(ctx, rbac.ActionRead, rbac.ResourceRegistry); err != nil {
return r.SendError(ctx, err)
}
registry := &model.Registry{}
var err error
if params.Registry != nil {
if params.Registry.ID != nil {
registry, err = r.ctl.Get(ctx, *params.Registry.ID)
if err != nil {
return r.SendError(ctx, err)
}
}
if params.Registry.Type != nil {
registry.Type = *params.Registry.Type
}
if params.Registry.URL != nil {
url, err := lib.ValidateHTTPURL(*params.Registry.URL)
if err != nil {
return r.SendError(ctx, err)
}
registry.URL = url
}
if params.Registry.Insecure != nil {
registry.Insecure = *params.Registry.Insecure
}
if params.Registry.CredentialType != nil {
if registry.Credential == nil {
registry.Credential = &model.Credential{}
}
registry.Credential.Type = *params.Registry.CredentialType
}
if params.Registry.AccessKey != nil {
if registry.Credential == nil {
registry.Credential = &model.Credential{}
}
registry.Credential.AccessKey = *params.Registry.AccessKey
}
if params.Registry.AccessSecret != nil {
if registry.Credential == nil {
registry.Credential = &model.Credential{}
}
registry.Credential.AccessSecret = *params.Registry.AccessSecret
}
}
if len(registry.Type) == 0 || len(registry.URL) == 0 {
return r.SendError(ctx, errors.New(nil).WithCode(errors.BadRequestCode).WithMessage("type or url cannot be empty"))
}
healthy, err := r.ctl.IsHealthy(ctx, registry)
if err != nil {
return r.SendError(ctx, err)
}
if !healthy {
return r.SendError(ctx, errors.New(nil).WithCode(errors.BadRequestCode).WithMessage("the registry is unhealthy"))
}
return operation.NewPingRegistryOK()
}
func (r *registryAPI) ListRegistryProviderInfos(ctx context.Context, params operation.ListRegistryProviderInfosParams) middleware.Responder {
if err := r.RequireSystemAccess(ctx, rbac.ActionList, rbac.ResourceReplicationAdapter); err != nil {
return r.SendError(ctx, err)
}
infos, err := r.ctl.ListRegistryProviderInfos(ctx)
if err != nil {
return r.SendError(ctx, err)
}
result := map[string]models.RegistryProviderInfo{}
for key, info := range infos {
item := models.RegistryProviderInfo{}
if info.CredentialPattern != nil {
item.CredentialPattern = &models.RegistryProviderCredentialPattern{
AccessKeyData: info.CredentialPattern.AccessKeyData,
AccessKeyType: info.CredentialPattern.AccessKeyType,
AccessSecretData: info.CredentialPattern.AccessSecretData,
AccessSecretType: info.CredentialPattern.AccessSecretType,
}
}
if info.EndpointPattern != nil {
item.EndpointPattern = &models.RegistryProviderEndpointPattern{
EndpointType: info.EndpointPattern.EndpointType,
}
for _, endpoint := range info.EndpointPattern.Endpoints {
item.EndpointPattern.Endpoints = append(item.EndpointPattern.Endpoints, &models.RegistryEndpoint{
Key: endpoint.Key,
Value: endpoint.Value,
})
}
}
result[key] = item
}
return operation.NewListRegistryProviderInfosOK().WithPayload(result)
}