mirror of
https://github.com/goharbor/harbor
synced 2025-04-15 18:29:19 +00:00

bump golang 1.21.5 update golangci-lint && fix revive error fix white space lint Signed-off-by: yminer <yminer@vmware.com>
314 lines
8.0 KiB
Go
314 lines
8.0 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 huawei
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
"net/http"
|
|
"regexp"
|
|
"strings"
|
|
|
|
common_http "github.com/goharbor/harbor/src/common/http"
|
|
"github.com/goharbor/harbor/src/common/http/modifier"
|
|
"github.com/goharbor/harbor/src/lib/log"
|
|
adp "github.com/goharbor/harbor/src/pkg/reg/adapter"
|
|
"github.com/goharbor/harbor/src/pkg/reg/adapter/native"
|
|
"github.com/goharbor/harbor/src/pkg/reg/model"
|
|
"github.com/goharbor/harbor/src/pkg/registry/auth/basic"
|
|
)
|
|
|
|
func init() {
|
|
err := adp.RegisterFactory(model.RegistryTypeHuawei, new(factory))
|
|
if err != nil {
|
|
log.Errorf("failed to register factory for Huawei: %v", err)
|
|
return
|
|
}
|
|
log.Infof("the factory of Huawei adapter was registered")
|
|
}
|
|
|
|
type factory struct {
|
|
}
|
|
|
|
// Create ...
|
|
func (f *factory) Create(r *model.Registry) (adp.Adapter, error) {
|
|
return newAdapter(r)
|
|
}
|
|
|
|
// AdapterPattern ...
|
|
func (f *factory) AdapterPattern() *model.AdapterPattern {
|
|
return nil
|
|
}
|
|
|
|
var (
|
|
_ adp.Adapter = (*adapter)(nil)
|
|
_ adp.ArtifactRegistry = (*adapter)(nil)
|
|
)
|
|
|
|
// Adapter is for images replications between harbor and Huawei image repository(SWR)
|
|
type adapter struct {
|
|
*native.Adapter
|
|
registry *model.Registry
|
|
client *common_http.Client
|
|
// original http client with no modifer,
|
|
// huawei's some api interface with basic authorization,
|
|
// some with bearer token authorization.
|
|
oriClient *http.Client
|
|
}
|
|
|
|
// Info gets info about Huawei SWR
|
|
func (a *adapter) Info() (*model.RegistryInfo, error) {
|
|
registryInfo := model.RegistryInfo{
|
|
Type: model.RegistryTypeHuawei,
|
|
Description: "Adapter for SWR -- The image registry of Huawei Cloud",
|
|
SupportedResourceTypes: []string{model.ResourceTypeImage},
|
|
SupportedResourceFilters: []*model.FilterStyle{
|
|
{
|
|
Type: model.FilterTypeName,
|
|
Style: model.FilterStyleTypeText,
|
|
},
|
|
{
|
|
Type: model.FilterTypeTag,
|
|
Style: model.FilterStyleTypeText,
|
|
},
|
|
},
|
|
SupportedTriggers: []string{
|
|
model.TriggerTypeManual,
|
|
model.TriggerTypeScheduled,
|
|
},
|
|
}
|
|
return ®istryInfo, nil
|
|
}
|
|
|
|
// ListNamespaces lists namespaces from Huawei SWR with the provided query conditions.
|
|
func (a *adapter) ListNamespaces(query *model.NamespaceQuery) ([]*model.Namespace, error) {
|
|
var namespaces []*model.Namespace
|
|
|
|
urls := fmt.Sprintf("%s/dockyard/v2/visible/namespaces", a.registry.URL)
|
|
|
|
r, err := http.NewRequest("GET", urls, nil)
|
|
if err != nil {
|
|
return namespaces, err
|
|
}
|
|
|
|
r.Header.Add("content-type", "application/json; charset=utf-8")
|
|
|
|
resp, err := a.client.Do(r)
|
|
if err != nil {
|
|
return namespaces, err
|
|
}
|
|
|
|
defer resp.Body.Close()
|
|
code := resp.StatusCode
|
|
if code >= 300 || code < 200 {
|
|
body, _ := io.ReadAll(resp.Body)
|
|
return namespaces, fmt.Errorf("[%d][%s]", code, string(body))
|
|
}
|
|
body, err := io.ReadAll(resp.Body)
|
|
if err != nil {
|
|
return namespaces, err
|
|
}
|
|
|
|
var namespacesData hwNamespaceList
|
|
err = json.Unmarshal(body, &namespacesData)
|
|
if err != nil {
|
|
return namespaces, err
|
|
}
|
|
reg := fmt.Sprintf(".*%s.*", strings.Replace(query.Name, " ", "", -1))
|
|
|
|
for _, namespaceData := range namespacesData.Namespace {
|
|
namespace := model.Namespace{
|
|
Name: namespaceData.Name,
|
|
Metadata: namespaceData.metadata(),
|
|
}
|
|
b, err := regexp.MatchString(reg, namespace.Name)
|
|
if err != nil {
|
|
return namespaces, nil
|
|
}
|
|
if b {
|
|
namespaces = append(namespaces, &namespace)
|
|
}
|
|
}
|
|
return namespaces, nil
|
|
}
|
|
|
|
// ConvertResourceMetadata convert resource metadata for Huawei SWR
|
|
func (a *adapter) ConvertResourceMetadata(resourceMetadata *model.ResourceMetadata, _ *model.Namespace) (*model.ResourceMetadata, error) {
|
|
metadata := &model.ResourceMetadata{
|
|
Repository: resourceMetadata.Repository,
|
|
Vtags: resourceMetadata.Vtags,
|
|
}
|
|
return metadata, nil
|
|
}
|
|
|
|
// PrepareForPush prepare for push to Huawei SWR
|
|
func (a *adapter) PrepareForPush(resources []*model.Resource) error {
|
|
namespaces := map[string]struct{}{}
|
|
for _, resource := range resources {
|
|
var namespace string
|
|
paths := strings.Split(resource.Metadata.Repository.Name, "/")
|
|
if len(paths) > 0 {
|
|
namespace = paths[0]
|
|
}
|
|
ns, err := a.GetNamespace(namespace)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if ns != nil && ns.Name == namespace {
|
|
continue
|
|
}
|
|
namespaces[namespace] = struct{}{}
|
|
}
|
|
|
|
url := fmt.Sprintf("%s/dockyard/v2/namespaces", a.registry.URL)
|
|
|
|
for namespace := range namespaces {
|
|
namespacebyte, err := json.Marshal(struct {
|
|
Namespace string `json:"namespace"`
|
|
}{
|
|
Namespace: namespace,
|
|
})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
r, err := http.NewRequest("POST", url, strings.NewReader(string(namespacebyte)))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
r.Header.Add("content-type", "application/json; charset=utf-8")
|
|
|
|
resp, err := a.client.Do(r)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer resp.Body.Close()
|
|
code := resp.StatusCode
|
|
if code >= 300 || code < 200 {
|
|
body, _ := io.ReadAll(resp.Body)
|
|
return fmt.Errorf("[%d][%s]", code, string(body))
|
|
}
|
|
|
|
log.Debugf("namespace %s created", namespace)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// GetNamespace gets a namespace from Huawei SWR
|
|
func (a *adapter) GetNamespace(namespaceStr string) (*model.Namespace, error) {
|
|
var namespace = &model.Namespace{
|
|
Name: "",
|
|
Metadata: make(map[string]interface{}),
|
|
}
|
|
|
|
urls := fmt.Sprintf("%s/dockyard/v2/namespaces/%s", a.registry.URL, namespaceStr)
|
|
r, err := http.NewRequest("GET", urls, nil)
|
|
if err != nil {
|
|
return namespace, err
|
|
}
|
|
|
|
r.Header.Add("content-type", "application/json; charset=utf-8")
|
|
|
|
resp, err := a.client.Do(r)
|
|
if err != nil {
|
|
return namespace, err
|
|
}
|
|
|
|
defer resp.Body.Close()
|
|
code := resp.StatusCode
|
|
if code >= 300 || code < 200 {
|
|
body, _ := io.ReadAll(resp.Body)
|
|
return namespace, fmt.Errorf("[%d][%s]", code, string(body))
|
|
}
|
|
body, err := io.ReadAll(resp.Body)
|
|
if err != nil {
|
|
return namespace, err
|
|
}
|
|
|
|
var namespaceData hwNamespace
|
|
err = json.Unmarshal(body, &namespaceData)
|
|
if err != nil {
|
|
return namespace, err
|
|
}
|
|
|
|
namespace.Name = namespaceData.Name
|
|
namespace.Metadata = namespaceData.metadata()
|
|
|
|
return namespace, nil
|
|
}
|
|
|
|
// HealthCheck check health for huawei SWR
|
|
func (a *adapter) HealthCheck() (string, error) {
|
|
return model.Healthy, nil
|
|
}
|
|
|
|
func newAdapter(registry *model.Registry) (adp.Adapter, error) {
|
|
var (
|
|
modifiers = []modifier.Modifier{}
|
|
authorizer modifier.Modifier
|
|
)
|
|
if registry.Credential != nil {
|
|
authorizer = basic.NewAuthorizer(
|
|
registry.Credential.AccessKey,
|
|
registry.Credential.AccessSecret)
|
|
modifiers = append(modifiers, authorizer)
|
|
}
|
|
|
|
transport := common_http.GetHTTPTransport(common_http.WithInsecure(registry.Insecure))
|
|
return &adapter{
|
|
Adapter: native.NewAdapter(registry),
|
|
registry: registry,
|
|
client: common_http.NewClient(
|
|
&http.Client{
|
|
Transport: transport,
|
|
},
|
|
modifiers...,
|
|
),
|
|
oriClient: &http.Client{
|
|
Transport: transport,
|
|
},
|
|
}, nil
|
|
}
|
|
|
|
type hwNamespaceList struct {
|
|
Namespace []hwNamespace `json:"namespaces"`
|
|
}
|
|
|
|
type hwNamespace struct {
|
|
ID int64 `json:"id" orm:"column(id)"`
|
|
Name string `json:"name"`
|
|
CreatorName string `json:"creator_name,omitempty"`
|
|
DomainPublic int `json:"-"`
|
|
Auth int `json:"auth"`
|
|
DomainName string `json:"-"`
|
|
UserCount int64 `json:"user_count"`
|
|
ImageCount int64 `json:"image_count"`
|
|
}
|
|
|
|
func (ns hwNamespace) metadata() map[string]interface{} {
|
|
var metadata = make(map[string]interface{})
|
|
metadata["id"] = ns.ID
|
|
metadata["creator_name"] = ns.CreatorName
|
|
metadata["domain_public"] = ns.DomainPublic
|
|
metadata["auth"] = ns.Auth
|
|
metadata["domain_name"] = ns.DomainName
|
|
metadata["user_count"] = ns.UserCount
|
|
metadata["image_count"] = ns.ImageCount
|
|
|
|
return metadata
|
|
}
|