Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat: add acr ee support #19658

Merged
merged 8 commits into from
Oct 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ go 1.23.2
require (
github.com/FZambia/sentinel v1.1.0
github.com/Masterminds/semver v1.5.0
github.com/aliyun/alibaba-cloud-sdk-go v0.0.0-20190726115642-cd293c93fd97
github.com/aliyun/alibaba-cloud-sdk-go v1.61.1193
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2
github.com/aws/aws-sdk-go v1.55.5
github.com/beego/beego/v2 v2.2.1
Expand Down
2 changes: 2 additions & 0 deletions src/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ github.com/alexbrainman/sspi v0.0.0-20210105120005-909beea2cc74 h1:Kk6a4nehpJ3Uu
github.com/alexbrainman/sspi v0.0.0-20210105120005-909beea2cc74/go.mod h1:cEWa1LVoE5KvSD9ONXsZrj0z6KqySlCCNKHlLzbqAt4=
github.com/aliyun/alibaba-cloud-sdk-go v0.0.0-20190726115642-cd293c93fd97 h1:bNE5ID4C3YOkROfvBjXJUG53gyb+8az3TQN02LqnGBk=
github.com/aliyun/alibaba-cloud-sdk-go v0.0.0-20190726115642-cd293c93fd97/go.mod h1:myCDvQSzCW+wB1WAlocEru4wMGJxy+vlxHdhegi1CDQ=
github.com/aliyun/alibaba-cloud-sdk-go v1.61.1193 h1:C5LuIDWuQlugv30EBsSLKFF6jdtrqoVH84nYCdVYTC4=
github.com/aliyun/alibaba-cloud-sdk-go v1.61.1193/go.mod h1:pUKYbK5JQ+1Dfxk80P0qxGqe5dkxDoabbZS7zOcouyA=
github.com/aliyun/aliyun-oss-go-sdk v0.0.0-20190307165228-86c17b95fcd5/go.mod h1:T/Aws4fEfogEE9v+HPhhw+CntffsBHJ8nXQCwKr0/g8=
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so=
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw=
Expand Down
227 changes: 91 additions & 136 deletions src/pkg/reg/adapter/aliacr/adapter.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,12 @@
package aliacr

import (
"encoding/json"
"errors"
"fmt"
"path/filepath"
"regexp"
"strings"

"github.com/aliyun/alibaba-cloud-sdk-go/sdk/requests"
"github.com/aliyun/alibaba-cloud-sdk-go/services/cr"

commonhttp "github.com/goharbor/harbor/src/common/http"
"github.com/goharbor/harbor/src/common/utils"
"github.com/goharbor/harbor/src/lib/log"
Expand All @@ -45,48 +41,80 @@
}

// example:
// https://registry.%s.aliyuncs.com
// https://cr.%s.aliyuncs.com
// https://registry-vpc.%s.aliyuncs.com
// https://registry-internal.%s.aliyuncs.com
var regRegion = regexp.MustCompile(`https://(registry|cr|registry-vpc|registry-internal)\.([\w\-]+)\.aliyuncs\.com`)
var regACRServiceURL = regexp.MustCompile(`https://cr\.([\w\-]+)\.aliyuncs\.com`)

func getRegion(url string) (region string, err error) {
func getRegistryURL(url string) (string, error) {
if url == "" {
return "", errors.New("empty url")
}
rs := regRegion.FindStringSubmatch(url)
rs := regACRServiceURL.FindStringSubmatch(url)
if rs == nil {
return "", errors.New("invalid Rgistry|CR service url")
return url, nil
}
return fmt.Sprintf(registryEndpointTpl, rs[1]), nil
}

// example:
// registry.aliyuncs.com:cn-hangzhou:china:cri-xxxxxxxxx
// registry.aliyuncs.com:cn-hangzhou:26842
func parseRegistryService(service string) (*registryServiceInfo, error) {
parts := strings.Split(service, ":")
length := len(parts)
if length < 2 {
return nil, errors.New("invalid service format: expected 'registry.aliyuncs.com:region:xxxxx'")
}

if !strings.EqualFold(parts[0], registryACRService) {
return nil, errors.New("not a acr service")
}

if strings.HasPrefix(parts[length-1], "cri-") {
return &registryServiceInfo{
IsACREE: true,
RegionID: parts[1],
InstanceID: parts[length-1],
}, nil
}
// fmt.Println(rs)
return rs[2], nil
return &registryServiceInfo{
IsACREE: false,
RegionID: parts[1],
}, nil
}

func newAdapter(registry *model.Registry) (*adapter, error) {
region, err := getRegion(registry.URL)
url, err := getRegistryURL(registry.URL)

Check warning on line 86 in src/pkg/reg/adapter/aliacr/adapter.go

View check run for this annotation

Codecov / codecov/patch

src/pkg/reg/adapter/aliacr/adapter.go#L86

Added line #L86 was not covered by tests
if err != nil {
return nil, err
}
switch true {
case strings.Contains(registry.URL, "registry-vpc"):
registry.URL = fmt.Sprintf(registryVPCEndpointTpl, region)
case strings.Contains(registry.URL, "registry-internal"):
registry.URL = fmt.Sprintf(registryInternalEndpointTpl, region)
default:
// fix url (allow user input cr service url)
registry.URL = fmt.Sprintf(registryEndpointTpl, region)
}
registry.URL = url

Check warning on line 91 in src/pkg/reg/adapter/aliacr/adapter.go

View check run for this annotation

Codecov / codecov/patch

src/pkg/reg/adapter/aliacr/adapter.go#L90-L91

Added lines #L90 - L91 were not covered by tests
realm, service, err := util.Ping(registry)
if err != nil {
return nil, err
}
credential := NewAuth(region, registry.Credential.AccessKey, registry.Credential.AccessSecret)
authorizer := bearer.NewAuthorizer(realm, service, credential, commonhttp.GetHTTPTransport(commonhttp.WithInsecure(registry.Insecure)))

info, err := parseRegistryService(service)
if err != nil {
return nil, err
}

Check warning on line 100 in src/pkg/reg/adapter/aliacr/adapter.go

View check run for this annotation

Codecov / codecov/patch

src/pkg/reg/adapter/aliacr/adapter.go#L97-L100

Added lines #L97 - L100 were not covered by tests

var acrAPI openapi
if !info.IsACREE {
acrAPI, err = newAcrOpenapi(registry.Credential.AccessKey, registry.Credential.AccessSecret, info.RegionID)
if err != nil {
return nil, err
}
} else {
acrAPI, err = newAcreeOpenapi(registry.Credential.AccessKey, registry.Credential.AccessSecret, info.RegionID, info.InstanceID)
if err != nil {
return nil, err
}

Check warning on line 112 in src/pkg/reg/adapter/aliacr/adapter.go

View check run for this annotation

Codecov / codecov/patch

src/pkg/reg/adapter/aliacr/adapter.go#L102-L112

Added lines #L102 - L112 were not covered by tests
}
authorizer := bearer.NewAuthorizer(realm, service, NewAuth(acrAPI), commonhttp.GetHTTPTransport(commonhttp.WithInsecure(registry.Insecure)))

Check warning on line 114 in src/pkg/reg/adapter/aliacr/adapter.go

View check run for this annotation

Codecov / codecov/patch

src/pkg/reg/adapter/aliacr/adapter.go#L114

Added line #L114 was not covered by tests
return &adapter{
region: region,
acrAPI: acrAPI,

Check warning on line 116 in src/pkg/reg/adapter/aliacr/adapter.go

View check run for this annotation

Codecov / codecov/patch

src/pkg/reg/adapter/aliacr/adapter.go#L116

Added line #L116 was not covered by tests
registry: registry,
domain: fmt.Sprintf(endpointTpl, region),
Adapter: native.NewAdapterWithAuthorizer(registry, authorizer),
}, nil
}
Expand All @@ -112,16 +140,15 @@
// adapter for to aliyun docker registry
type adapter struct {
*native.Adapter
region string
domain string
acrAPI openapi
registry *model.Registry
}

var _ adp.Adapter = &adapter{}

// Info ...
func (a *adapter) Info() (info *model.RegistryInfo, err error) {
info = &model.RegistryInfo{
func (a *adapter) Info() (*model.RegistryInfo, error) {
info := &model.RegistryInfo{
Type: model.RegistryTypeAliAcr,
SupportedResourceTypes: []string{
model.ResourceTypeImage,
Expand All @@ -141,7 +168,7 @@
model.TriggerTypeScheduled,
},
}
return
return info, nil
}

func getAdapterInfo() *model.AdapterPattern {
Expand Down Expand Up @@ -184,6 +211,16 @@
Key: e + "-internal",
Value: fmt.Sprintf("https://registry-internal.%s.aliyuncs.com", e),
})

endpoints = append(endpoints, &model.Endpoint{
Key: e + "-ee-vpc",
Value: fmt.Sprintf("https://instanceName-registry-vpc.%s.cr.aliyuncs.com", e),
})

endpoints = append(endpoints, &model.Endpoint{
Key: e + "-ee",
Value: fmt.Sprintf("https://instanceName-registry.%s.cr.aliyuncs.com", e),
})
}
info := &model.AdapterPattern{
EndpointPattern: &model.EndpointPattern{
Expand All @@ -194,30 +231,8 @@
return info
}

func (a *adapter) listNamespaces(c *cr.Client) (namespaces []string, err error) {
// list namespaces
var nsReq = cr.CreateGetNamespaceListRequest()
var nsResp *cr.GetNamespaceListResponse
nsReq.SetDomain(a.domain)
nsResp, err = c.GetNamespaceList(nsReq)
if err != nil {
return
}
var resp = &aliACRNamespaceResp{}
err = json.Unmarshal(nsResp.GetHttpContentBytes(), resp)
if err != nil {
return
}
for _, ns := range resp.Data.Namespaces {
namespaces = append(namespaces, ns.Namespace)
}

log.Debugf("FetchArtifacts.listNamespaces: %#v\n", namespaces)

return
}

func (a *adapter) listCandidateNamespaces(c *cr.Client, namespacePattern string) (namespaces []string, err error) {
func (a *adapter) listCandidateNamespaces(namespacePattern string) ([]string, error) {
var namespaces []string

Check warning on line 235 in src/pkg/reg/adapter/aliacr/adapter.go

View check run for this annotation

Codecov / codecov/patch

src/pkg/reg/adapter/aliacr/adapter.go#L234-L235

Added lines #L234 - L235 were not covered by tests
if len(namespacePattern) > 0 {
if nms, ok := util.IsSpecificPathComponent(namespacePattern); ok {
namespaces = append(namespaces, nms...)
Expand All @@ -228,19 +243,22 @@
}
}

return a.listNamespaces(c)
if a.acrAPI == nil {
return nil, errors.New("acr api is nil")
}

Check warning on line 248 in src/pkg/reg/adapter/aliacr/adapter.go

View check run for this annotation

Codecov / codecov/patch

src/pkg/reg/adapter/aliacr/adapter.go#L246-L248

Added lines #L246 - L248 were not covered by tests

return a.acrAPI.ListNamespace()

Check warning on line 250 in src/pkg/reg/adapter/aliacr/adapter.go

View check run for this annotation

Codecov / codecov/patch

src/pkg/reg/adapter/aliacr/adapter.go#L250

Added line #L250 was not covered by tests
}

// FetchArtifacts AliACR not support /v2/_catalog of Registry, we'll list all resources via Aliyun's API
func (a *adapter) FetchArtifacts(filters []*model.Filter) (resources []*model.Resource, err error) {
func (a *adapter) FetchArtifacts(filters []*model.Filter) ([]*model.Resource, error) {
log.Debugf("FetchArtifacts.filters: %#v\n", filters)

var client *cr.Client
client, err = cr.NewClientWithAccessKey(a.region, a.registry.Credential.AccessKey, a.registry.Credential.AccessSecret)
if err != nil {
return
if a.acrAPI == nil {
return nil, errors.New("acr api is nil")
}

var resources []*model.Resource

Check warning on line 261 in src/pkg/reg/adapter/aliacr/adapter.go

View check run for this annotation

Codecov / codecov/patch

src/pkg/reg/adapter/aliacr/adapter.go#L261

Added line #L261 was not covered by tests
// get filter pattern
var repoPattern string
var tagsPattern string
Expand All @@ -254,31 +272,29 @@
log.Debugf("\nrepoPattern=%s tagsPattern=%s\n\n", repoPattern, tagsPattern)

// get namespaces
var namespaces []string
namespaces, err = a.listCandidateNamespaces(client, namespacePattern)
namespaces, err := a.listCandidateNamespaces(namespacePattern)

Check warning on line 275 in src/pkg/reg/adapter/aliacr/adapter.go

View check run for this annotation

Codecov / codecov/patch

src/pkg/reg/adapter/aliacr/adapter.go#L275

Added line #L275 was not covered by tests
if err != nil {
return
return nil, err

Check warning on line 277 in src/pkg/reg/adapter/aliacr/adapter.go

View check run for this annotation

Codecov / codecov/patch

src/pkg/reg/adapter/aliacr/adapter.go#L277

Added line #L277 was not covered by tests
}
log.Debugf("got namespaces: %v \n", namespaces)

// list repos
var repositories []aliRepo
var repositories []*repository

Check warning on line 282 in src/pkg/reg/adapter/aliacr/adapter.go

View check run for this annotation

Codecov / codecov/patch

src/pkg/reg/adapter/aliacr/adapter.go#L282

Added line #L282 was not covered by tests
for _, namespace := range namespaces {
var repos []aliRepo
repos, err = a.listReposByNamespace(a.region, namespace, client)
repos, err := a.acrAPI.ListRepository(namespace)

Check warning on line 284 in src/pkg/reg/adapter/aliacr/adapter.go

View check run for this annotation

Codecov / codecov/patch

src/pkg/reg/adapter/aliacr/adapter.go#L284

Added line #L284 was not covered by tests
if err != nil {
return
return nil, err

Check warning on line 286 in src/pkg/reg/adapter/aliacr/adapter.go

View check run for this annotation

Codecov / codecov/patch

src/pkg/reg/adapter/aliacr/adapter.go#L286

Added line #L286 was not covered by tests
}

log.Debugf("\nnamespace: %s \t repositories: %#v\n\n", namespace, repos)

for _, repo := range repos {
var ok bool
var repoName = filepath.Join(repo.RepoNamespace, repo.RepoName)
var repoName = filepath.Join(repo.Namespace, repo.Name)

Check warning on line 293 in src/pkg/reg/adapter/aliacr/adapter.go

View check run for this annotation

Codecov / codecov/patch

src/pkg/reg/adapter/aliacr/adapter.go#L293

Added line #L293 was not covered by tests
ok, err = util.Match(repoPattern, repoName)
log.Debugf("\n Repository: %s\t repoPattern: %s\t Match: %v\n", repoName, repoPattern, ok)
if err != nil {
return
return nil, err

Check warning on line 297 in src/pkg/reg/adapter/aliacr/adapter.go

View check run for this annotation

Codecov / codecov/patch

src/pkg/reg/adapter/aliacr/adapter.go#L297

Added line #L297 was not covered by tests
}
if ok {
repositories = append(repositories, repo)
Expand All @@ -295,9 +311,9 @@
repo := r
runner.AddTask(func() error {
var tags []string
tags, err = a.getTags(repo, client)
tags, err = a.acrAPI.ListRepoTag(repo)

Check warning on line 314 in src/pkg/reg/adapter/aliacr/adapter.go

View check run for this annotation

Codecov / codecov/patch

src/pkg/reg/adapter/aliacr/adapter.go#L314

Added line #L314 was not covered by tests
if err != nil {
return fmt.Errorf("list tags for repo '%s' error: %v", repo.RepoName, err)
return fmt.Errorf("list tags for repo '%s' error: %v", repo.Name, err)

Check warning on line 316 in src/pkg/reg/adapter/aliacr/adapter.go

View check run for this annotation

Codecov / codecov/patch

src/pkg/reg/adapter/aliacr/adapter.go#L316

Added line #L316 was not covered by tests
}

var artifacts []*model.Artifact
Expand All @@ -317,13 +333,12 @@
Registry: a.registry,
Metadata: &model.ResourceMetadata{
Repository: &model.Repository{
Name: filepath.Join(repo.RepoNamespace, repo.RepoName),
Name: filepath.Join(repo.Namespace, repo.Name),

Check warning on line 336 in src/pkg/reg/adapter/aliacr/adapter.go

View check run for this annotation

Codecov / codecov/patch

src/pkg/reg/adapter/aliacr/adapter.go#L336

Added line #L336 was not covered by tests
},
Artifacts: filterArtifacts,
},
}
}

return nil
})
}
Expand All @@ -336,65 +351,5 @@
}
}

return
}

func (a *adapter) listReposByNamespace(_ string, namespace string, c *cr.Client) (repos []aliRepo, err error) {
var reposReq = cr.CreateGetRepoListByNamespaceRequest()
var reposResp = cr.CreateGetRepoListByNamespaceResponse()
reposReq.SetDomain(a.domain)
reposReq.RepoNamespace = namespace
var page = 1
for {
reposReq.Page = requests.NewInteger(page)
reposResp, err = c.GetRepoListByNamespace(reposReq)
if err != nil {
return
}
var resp = &aliReposResp{}
err = json.Unmarshal(reposResp.GetHttpContentBytes(), resp)
if err != nil {
return
}
repos = append(repos, resp.Data.Repos...)

if resp.Data.Total-(resp.Data.Page*resp.Data.PageSize) <= 0 {
break
}
page++
}
return
}

func (a *adapter) getTags(repo aliRepo, c *cr.Client) (tags []string, err error) {
log.Debugf("[ali-acr.getTags]%s: %#v\n", a.domain, repo)
var tagsReq = cr.CreateGetRepoTagsRequest()
var tagsResp = cr.CreateGetRepoTagsResponse()
tagsReq.SetDomain(a.domain)
tagsReq.RepoNamespace = repo.RepoNamespace
tagsReq.RepoName = repo.RepoName
var page = 1
for {
tagsReq.Page = requests.NewInteger(page)
tagsResp, err = c.GetRepoTags(tagsReq)
if err != nil {
return
}

var resp = &aliTagResp{}
err = json.Unmarshal(tagsResp.GetHttpContentBytes(), resp)
if err != nil {
return
}
for _, tag := range resp.Data.Tags {
tags = append(tags, tag.Tag)
}

if resp.Data.Total-(resp.Data.Page*resp.Data.PageSize) <= 0 {
break
}
page++
}

return
return resources, nil

Check warning on line 354 in src/pkg/reg/adapter/aliacr/adapter.go

View check run for this annotation

Codecov / codecov/patch

src/pkg/reg/adapter/aliacr/adapter.go#L354

Added line #L354 was not covered by tests
}
Loading
Loading