mirror of
https://github.com/goharbor/harbor.git
synced 2024-11-22 02:05:41 +01:00
Feat: add acr ee support (#19658)
* feat: add acr ee support Signed-off-by: njucjc <njucjc@gmail.com> Co-authored-by: Wang Yan <wangyan@vmware.com> Co-authored-by: Orlix <7236111+OrlinVasilev@users.noreply.github.com>
This commit is contained in:
parent
0d9dc7096f
commit
69bea4daa3
@ -5,7 +5,7 @@ go 1.23.2
|
|||||||
require (
|
require (
|
||||||
github.com/FZambia/sentinel v1.1.0
|
github.com/FZambia/sentinel v1.1.0
|
||||||
github.com/Masterminds/semver v1.5.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/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2
|
||||||
github.com/aws/aws-sdk-go v1.55.5
|
github.com/aws/aws-sdk-go v1.55.5
|
||||||
github.com/beego/beego/v2 v2.2.1
|
github.com/beego/beego/v2 v2.2.1
|
||||||
|
@ -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/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 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 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/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 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so=
|
||||||
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw=
|
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw=
|
||||||
|
@ -15,16 +15,12 @@
|
|||||||
package aliacr
|
package aliacr
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"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"
|
commonhttp "github.com/goharbor/harbor/src/common/http"
|
||||||
"github.com/goharbor/harbor/src/common/utils"
|
"github.com/goharbor/harbor/src/common/utils"
|
||||||
"github.com/goharbor/harbor/src/lib/log"
|
"github.com/goharbor/harbor/src/lib/log"
|
||||||
@ -45,48 +41,80 @@ func init() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// example:
|
// example:
|
||||||
// https://registry.%s.aliyuncs.com
|
|
||||||
// https://cr.%s.aliyuncs.com
|
// https://cr.%s.aliyuncs.com
|
||||||
// https://registry-vpc.%s.aliyuncs.com
|
var regACRServiceURL = regexp.MustCompile(`https://cr\.([\w\-]+)\.aliyuncs\.com`)
|
||||||
// https://registry-internal.%s.aliyuncs.com
|
|
||||||
var regRegion = regexp.MustCompile(`https://(registry|cr|registry-vpc|registry-internal)\.([\w\-]+)\.aliyuncs\.com`)
|
|
||||||
|
|
||||||
func getRegion(url string) (region string, err error) {
|
func getRegistryURL(url string) (string, error) {
|
||||||
if url == "" {
|
if url == "" {
|
||||||
return "", errors.New("empty url")
|
return "", errors.New("empty url")
|
||||||
}
|
}
|
||||||
rs := regRegion.FindStringSubmatch(url)
|
rs := regACRServiceURL.FindStringSubmatch(url)
|
||||||
if rs == nil {
|
if rs == nil {
|
||||||
return "", errors.New("invalid Rgistry|CR service url")
|
return url, nil
|
||||||
}
|
}
|
||||||
// fmt.Println(rs)
|
return fmt.Sprintf(registryEndpointTpl, rs[1]), nil
|
||||||
return rs[2], 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 ®istryServiceInfo{
|
||||||
|
IsACREE: true,
|
||||||
|
RegionID: parts[1],
|
||||||
|
InstanceID: parts[length-1],
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
return ®istryServiceInfo{
|
||||||
|
IsACREE: false,
|
||||||
|
RegionID: parts[1],
|
||||||
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func newAdapter(registry *model.Registry) (*adapter, error) {
|
func newAdapter(registry *model.Registry) (*adapter, error) {
|
||||||
region, err := getRegion(registry.URL)
|
url, err := getRegistryURL(registry.URL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
switch true {
|
registry.URL = url
|
||||||
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)
|
|
||||||
}
|
|
||||||
realm, service, err := util.Ping(registry)
|
realm, service, err := util.Ping(registry)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
authorizer := bearer.NewAuthorizer(realm, service, NewAuth(acrAPI), commonhttp.GetHTTPTransport(commonhttp.WithInsecure(registry.Insecure)))
|
||||||
return &adapter{
|
return &adapter{
|
||||||
region: region,
|
acrAPI: acrAPI,
|
||||||
registry: registry,
|
registry: registry,
|
||||||
domain: fmt.Sprintf(endpointTpl, region),
|
|
||||||
Adapter: native.NewAdapterWithAuthorizer(registry, authorizer),
|
Adapter: native.NewAdapterWithAuthorizer(registry, authorizer),
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
@ -112,16 +140,15 @@ var (
|
|||||||
// adapter for to aliyun docker registry
|
// adapter for to aliyun docker registry
|
||||||
type adapter struct {
|
type adapter struct {
|
||||||
*native.Adapter
|
*native.Adapter
|
||||||
region string
|
acrAPI openapi
|
||||||
domain string
|
|
||||||
registry *model.Registry
|
registry *model.Registry
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ adp.Adapter = &adapter{}
|
var _ adp.Adapter = &adapter{}
|
||||||
|
|
||||||
// Info ...
|
// Info ...
|
||||||
func (a *adapter) Info() (info *model.RegistryInfo, err error) {
|
func (a *adapter) Info() (*model.RegistryInfo, error) {
|
||||||
info = &model.RegistryInfo{
|
info := &model.RegistryInfo{
|
||||||
Type: model.RegistryTypeAliAcr,
|
Type: model.RegistryTypeAliAcr,
|
||||||
SupportedResourceTypes: []string{
|
SupportedResourceTypes: []string{
|
||||||
model.ResourceTypeImage,
|
model.ResourceTypeImage,
|
||||||
@ -141,7 +168,7 @@ func (a *adapter) Info() (info *model.RegistryInfo, err error) {
|
|||||||
model.TriggerTypeScheduled,
|
model.TriggerTypeScheduled,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
return
|
return info, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func getAdapterInfo() *model.AdapterPattern {
|
func getAdapterInfo() *model.AdapterPattern {
|
||||||
@ -184,6 +211,16 @@ func getAdapterInfo() *model.AdapterPattern {
|
|||||||
Key: e + "-internal",
|
Key: e + "-internal",
|
||||||
Value: fmt.Sprintf("https://registry-internal.%s.aliyuncs.com", e),
|
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{
|
info := &model.AdapterPattern{
|
||||||
EndpointPattern: &model.EndpointPattern{
|
EndpointPattern: &model.EndpointPattern{
|
||||||
@ -194,30 +231,8 @@ func getAdapterInfo() *model.AdapterPattern {
|
|||||||
return info
|
return info
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *adapter) listNamespaces(c *cr.Client) (namespaces []string, err error) {
|
func (a *adapter) listCandidateNamespaces(namespacePattern string) ([]string, error) {
|
||||||
// list namespaces
|
var namespaces []string
|
||||||
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) {
|
|
||||||
if len(namespacePattern) > 0 {
|
if len(namespacePattern) > 0 {
|
||||||
if nms, ok := util.IsSpecificPathComponent(namespacePattern); ok {
|
if nms, ok := util.IsSpecificPathComponent(namespacePattern); ok {
|
||||||
namespaces = append(namespaces, nms...)
|
namespaces = append(namespaces, nms...)
|
||||||
@ -228,19 +243,22 @@ func (a *adapter) listCandidateNamespaces(c *cr.Client, namespacePattern string)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return a.listNamespaces(c)
|
if a.acrAPI == nil {
|
||||||
|
return nil, errors.New("acr api is nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
return a.acrAPI.ListNamespace()
|
||||||
}
|
}
|
||||||
|
|
||||||
// FetchArtifacts AliACR not support /v2/_catalog of Registry, we'll list all resources via Aliyun's API
|
// 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)
|
log.Debugf("FetchArtifacts.filters: %#v\n", filters)
|
||||||
|
|
||||||
var client *cr.Client
|
if a.acrAPI == nil {
|
||||||
client, err = cr.NewClientWithAccessKey(a.region, a.registry.Credential.AccessKey, a.registry.Credential.AccessSecret)
|
return nil, errors.New("acr api is nil")
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var resources []*model.Resource
|
||||||
// get filter pattern
|
// get filter pattern
|
||||||
var repoPattern string
|
var repoPattern string
|
||||||
var tagsPattern string
|
var tagsPattern string
|
||||||
@ -254,31 +272,29 @@ func (a *adapter) FetchArtifacts(filters []*model.Filter) (resources []*model.Re
|
|||||||
log.Debugf("\nrepoPattern=%s tagsPattern=%s\n\n", repoPattern, tagsPattern)
|
log.Debugf("\nrepoPattern=%s tagsPattern=%s\n\n", repoPattern, tagsPattern)
|
||||||
|
|
||||||
// get namespaces
|
// get namespaces
|
||||||
var namespaces []string
|
namespaces, err := a.listCandidateNamespaces(namespacePattern)
|
||||||
namespaces, err = a.listCandidateNamespaces(client, namespacePattern)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return nil, err
|
||||||
}
|
}
|
||||||
log.Debugf("got namespaces: %v \n", namespaces)
|
log.Debugf("got namespaces: %v \n", namespaces)
|
||||||
|
|
||||||
// list repos
|
// list repos
|
||||||
var repositories []aliRepo
|
var repositories []*repository
|
||||||
for _, namespace := range namespaces {
|
for _, namespace := range namespaces {
|
||||||
var repos []aliRepo
|
repos, err := a.acrAPI.ListRepository(namespace)
|
||||||
repos, err = a.listReposByNamespace(a.region, namespace, client)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Debugf("\nnamespace: %s \t repositories: %#v\n\n", namespace, repos)
|
log.Debugf("\nnamespace: %s \t repositories: %#v\n\n", namespace, repos)
|
||||||
|
|
||||||
for _, repo := range repos {
|
for _, repo := range repos {
|
||||||
var ok bool
|
var ok bool
|
||||||
var repoName = filepath.Join(repo.RepoNamespace, repo.RepoName)
|
var repoName = filepath.Join(repo.Namespace, repo.Name)
|
||||||
ok, err = util.Match(repoPattern, repoName)
|
ok, err = util.Match(repoPattern, repoName)
|
||||||
log.Debugf("\n Repository: %s\t repoPattern: %s\t Match: %v\n", repoName, repoPattern, ok)
|
log.Debugf("\n Repository: %s\t repoPattern: %s\t Match: %v\n", repoName, repoPattern, ok)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return nil, err
|
||||||
}
|
}
|
||||||
if ok {
|
if ok {
|
||||||
repositories = append(repositories, repo)
|
repositories = append(repositories, repo)
|
||||||
@ -295,9 +311,9 @@ func (a *adapter) FetchArtifacts(filters []*model.Filter) (resources []*model.Re
|
|||||||
repo := r
|
repo := r
|
||||||
runner.AddTask(func() error {
|
runner.AddTask(func() error {
|
||||||
var tags []string
|
var tags []string
|
||||||
tags, err = a.getTags(repo, client)
|
tags, err = a.acrAPI.ListRepoTag(repo)
|
||||||
if err != nil {
|
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)
|
||||||
}
|
}
|
||||||
|
|
||||||
var artifacts []*model.Artifact
|
var artifacts []*model.Artifact
|
||||||
@ -317,13 +333,12 @@ func (a *adapter) FetchArtifacts(filters []*model.Filter) (resources []*model.Re
|
|||||||
Registry: a.registry,
|
Registry: a.registry,
|
||||||
Metadata: &model.ResourceMetadata{
|
Metadata: &model.ResourceMetadata{
|
||||||
Repository: &model.Repository{
|
Repository: &model.Repository{
|
||||||
Name: filepath.Join(repo.RepoNamespace, repo.RepoName),
|
Name: filepath.Join(repo.Namespace, repo.Name),
|
||||||
},
|
},
|
||||||
Artifacts: filterArtifacts,
|
Artifacts: filterArtifacts,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -336,65 +351,5 @@ func (a *adapter) FetchArtifacts(filters []*model.Filter) (resources []*model.Re
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
return resources, nil
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
@ -62,8 +62,6 @@ func getMockAdapter(t *testing.T, hasCred, health bool) (*adapter, *httptest.Ser
|
|||||||
}
|
}
|
||||||
return &adapter{
|
return &adapter{
|
||||||
Adapter: native.NewAdapter(registry),
|
Adapter: native.NewAdapter(registry),
|
||||||
region: "test-region",
|
|
||||||
domain: server.URL,
|
|
||||||
registry: registry,
|
registry: registry,
|
||||||
}, server
|
}, server
|
||||||
}
|
}
|
||||||
@ -79,46 +77,91 @@ func TestAdapter_Info(t *testing.T) {
|
|||||||
assert.EqualValues(t, model.ResourceTypeImage, info.SupportedResourceTypes[0])
|
assert.EqualValues(t, model.ResourceTypeImage, info.SupportedResourceTypes[0])
|
||||||
}
|
}
|
||||||
|
|
||||||
func Test_getRegion(t *testing.T) {
|
func Test_getRegistryURL(t *testing.T) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
url string
|
url string
|
||||||
wantRegion string
|
want string
|
||||||
wantErr bool
|
wantErr bool
|
||||||
}{
|
}{
|
||||||
{"registry shanghai", "https://registry.cn-shanghai.aliyuncs.com", "cn-shanghai", false},
|
{
|
||||||
{"invalid registry shanghai", "http://registry.cn-shanghai.aliyuncs.com", "", true},
|
"empty url",
|
||||||
{"registry hangzhou", "https://registry.cn-hangzhou.aliyuncs.com", "cn-hangzhou", false},
|
"",
|
||||||
{"registry hangzhou vpc", "https://registry-vpc.cn-hangzhou.aliyuncs.com", "cn-hangzhou", false},
|
"",
|
||||||
{"registry hangzhou internal", "https://registry-internal.cn-hangzhou.aliyuncs.com", "cn-hangzhou", false},
|
true,
|
||||||
{"cr shanghai", "https://cr.cn-shanghai.aliyuncs.com", "cn-shanghai", false},
|
},
|
||||||
{"cr hangzhou", "https://cr.cn-hangzhou.aliyuncs.com", "cn-hangzhou", false},
|
{
|
||||||
{"invalid cr url", "https://acr.cn-hangzhou.aliyuncs.com", "", true},
|
"just return url",
|
||||||
{"invalid registry url", "https://registry.cn-hangzhou.ali.com", "", true},
|
"https://cr.cn-hangzhou.aliyun.com",
|
||||||
|
"https://cr.cn-hangzhou.aliyun.com",
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"change match url",
|
||||||
|
"https://cr.cn-hangzhou.aliyuncs.com",
|
||||||
|
"https://registry.cn-hangzhou.aliyuncs.com",
|
||||||
|
false,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
gotRegion, err := getRegion(tt.url)
|
got, err := getRegistryURL(tt.url)
|
||||||
if tt.wantErr {
|
if tt.wantErr {
|
||||||
assert.NotNil(t, err)
|
assert.NotNil(t, err)
|
||||||
}
|
}
|
||||||
assert.Equal(t, tt.wantRegion, gotRegion)
|
assert.Equal(t, tt.want, got)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var urlForBenchmark = []string{
|
func Test_parseRegistryService(t *testing.T) {
|
||||||
"https://cr.cn-hangzhou.aliyuncs.com",
|
tests := []struct {
|
||||||
"https://registry.cn-shanghai.aliyuncs.com",
|
name string
|
||||||
"https://registry-vpc.cn-shanghai.aliyuncs.com",
|
service string
|
||||||
"https://registry-internal.cn-shanghai.aliyuncs.com",
|
wantInfo *registryServiceInfo
|
||||||
}
|
wantErr bool
|
||||||
|
}{
|
||||||
func BenchmarkGetRegion(b *testing.B) {
|
{
|
||||||
for i := 0; i < b.N; i++ {
|
"not acr Service",
|
||||||
for _, url := range urlForBenchmark {
|
"otherregistry.cn-hangzhou:china",
|
||||||
getRegion(url)
|
nil,
|
||||||
}
|
true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"empty Service",
|
||||||
|
"",
|
||||||
|
nil,
|
||||||
|
true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"acr ee service",
|
||||||
|
"registry.aliyuncs.com:cn-hangzhou:china:cri-xxxxxxxxx",
|
||||||
|
®istryServiceInfo{
|
||||||
|
IsACREE: true,
|
||||||
|
RegionID: "cn-hangzhou",
|
||||||
|
InstanceID: "cri-xxxxxxxxx",
|
||||||
|
},
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"acr service",
|
||||||
|
"registry.aliyuncs.com:cn-hangzhou:26842",
|
||||||
|
®istryServiceInfo{
|
||||||
|
IsACREE: false,
|
||||||
|
RegionID: "cn-hangzhou",
|
||||||
|
InstanceID: "",
|
||||||
|
},
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
info, err := parseRegistryService(tt.service)
|
||||||
|
if tt.wantErr {
|
||||||
|
assert.NotNil(t, err)
|
||||||
|
}
|
||||||
|
assert.Equal(t, tt.wantInfo, info)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -132,9 +175,6 @@ func Test_adapter_FetchArtifacts(t *testing.T) {
|
|||||||
}
|
}
|
||||||
func Test_aliyunAuthCredential_isCacheTokenValid(t *testing.T) {
|
func Test_aliyunAuthCredential_isCacheTokenValid(t *testing.T) {
|
||||||
type fields struct {
|
type fields struct {
|
||||||
region string
|
|
||||||
accessKey string
|
|
||||||
secretKey string
|
|
||||||
cacheToken *registryTemporaryToken
|
cacheToken *registryTemporaryToken
|
||||||
cacheTokenExpiredAt time.Time
|
cacheTokenExpiredAt time.Time
|
||||||
}
|
}
|
||||||
@ -145,23 +185,22 @@ func Test_aliyunAuthCredential_isCacheTokenValid(t *testing.T) {
|
|||||||
fields fields
|
fields fields
|
||||||
want bool
|
want bool
|
||||||
}{
|
}{
|
||||||
{"nil cacheTokenExpiredAt", fields{"test-region", "MockAccessKey", "MockSecretKey", nil, nilTime}, false},
|
{"nil cacheTokenExpiredAt", fields{nil, nilTime}, false},
|
||||||
{"nil cacheToken", fields{"test-region", "MockAccessKey", "MockSecretKey", nil, time.Time{}}, false},
|
{"nil cacheToken", fields{nil, time.Time{}}, false},
|
||||||
{"expired", fields{"test-region", "MockAccessKey", "MockSecretKey", ®istryTemporaryToken{}, time.Now().AddDate(0, 0, -1)}, false},
|
{"expired", fields{®istryTemporaryToken{}, time.Now().AddDate(0, 0, -1)}, false},
|
||||||
{"ok", fields{"test-region", "MockAccessKey", "MockSecretKey", ®istryTemporaryToken{}, time.Now().AddDate(0, 0, 1)}, true},
|
{"ok", fields{®istryTemporaryToken{}, time.Now().AddDate(0, 0, 1)}, true},
|
||||||
}
|
}
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
a := &aliyunAuthCredential{
|
a := &aliyunAuthCredential{
|
||||||
region: tt.fields.region,
|
|
||||||
accessKey: tt.fields.accessKey,
|
|
||||||
secretKey: tt.fields.secretKey,
|
|
||||||
cacheToken: tt.fields.cacheToken,
|
cacheToken: tt.fields.cacheToken,
|
||||||
cacheTokenExpiredAt: tt.fields.cacheTokenExpiredAt,
|
cacheTokenExpiredAt: tt.fields.cacheTokenExpiredAt,
|
||||||
}
|
}
|
||||||
if got := a.isCacheTokenValid(); got != tt.want {
|
if got := a.isCacheTokenValid(); got != tt.want {
|
||||||
|
fmt.Println(got)
|
||||||
assert.Equal(t, got, tt.want)
|
assert.Equal(t, got, tt.want)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -15,13 +15,10 @@
|
|||||||
package aliacr
|
package aliacr
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"errors"
|
||||||
"fmt"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/aliyun/alibaba-cloud-sdk-go/services/cr"
|
|
||||||
|
|
||||||
"github.com/goharbor/harbor/src/common/http/modifier"
|
"github.com/goharbor/harbor/src/common/http/modifier"
|
||||||
"github.com/goharbor/harbor/src/lib/log"
|
"github.com/goharbor/harbor/src/lib/log"
|
||||||
)
|
)
|
||||||
@ -31,9 +28,7 @@ type Credential modifier.Modifier
|
|||||||
|
|
||||||
// Implements interface Credential
|
// Implements interface Credential
|
||||||
type aliyunAuthCredential struct {
|
type aliyunAuthCredential struct {
|
||||||
region string
|
acrAPI openapi
|
||||||
accessKey string
|
|
||||||
secretKey string
|
|
||||||
cacheToken *registryTemporaryToken
|
cacheToken *registryTemporaryToken
|
||||||
cacheTokenExpiredAt time.Time
|
cacheTokenExpiredAt time.Time
|
||||||
}
|
}
|
||||||
@ -46,11 +41,9 @@ type registryTemporaryToken struct {
|
|||||||
var _ Credential = &aliyunAuthCredential{}
|
var _ Credential = &aliyunAuthCredential{}
|
||||||
|
|
||||||
// NewAuth will get a temporary docker registry username and password via aliyun cr service API.
|
// NewAuth will get a temporary docker registry username and password via aliyun cr service API.
|
||||||
func NewAuth(region, accessKey, secretKey string) Credential {
|
func NewAuth(acrAPI openapi) Credential {
|
||||||
return &aliyunAuthCredential{
|
return &aliyunAuthCredential{
|
||||||
region: region,
|
acrAPI: acrAPI,
|
||||||
accessKey: accessKey,
|
|
||||||
secretKey: secretKey,
|
|
||||||
cacheToken: ®istryTemporaryToken{},
|
cacheToken: ®istryTemporaryToken{},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -58,27 +51,16 @@ func NewAuth(region, accessKey, secretKey string) Credential {
|
|||||||
func (a *aliyunAuthCredential) Modify(r *http.Request) (err error) {
|
func (a *aliyunAuthCredential) Modify(r *http.Request) (err error) {
|
||||||
if !a.isCacheTokenValid() {
|
if !a.isCacheTokenValid() {
|
||||||
log.Debugf("[aliyunAuthCredential.Modify.updateToken]Host: %s\n", r.Host)
|
log.Debugf("[aliyunAuthCredential.Modify.updateToken]Host: %s\n", r.Host)
|
||||||
var client *cr.Client
|
if a.acrAPI == nil {
|
||||||
client, err = cr.NewClientWithAccessKey(a.region, a.accessKey, a.secretKey)
|
return errors.New("acr api is nil")
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
v, err := a.acrAPI.GetAuthorizationToken()
|
||||||
var tokenRequest = cr.CreateGetAuthorizationTokenRequest()
|
|
||||||
var tokenResponse *cr.GetAuthorizationTokenResponse
|
|
||||||
tokenRequest.SetDomain(fmt.Sprintf(endpointTpl, a.region))
|
|
||||||
tokenResponse, err = client.GetAuthorizationToken(tokenRequest)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return err
|
||||||
}
|
}
|
||||||
var v authorizationToken
|
a.cacheTokenExpiredAt = v.expiresAt
|
||||||
err = json.Unmarshal(tokenResponse.GetHttpContentBytes(), &v)
|
a.cacheToken.user = v.user
|
||||||
if err != nil {
|
a.cacheToken.password = v.password
|
||||||
return
|
|
||||||
}
|
|
||||||
a.cacheTokenExpiredAt = v.Data.ExpireDate.ToTime()
|
|
||||||
a.cacheToken.user = v.Data.TempUserName
|
|
||||||
a.cacheToken.password = v.Data.AuthorizationToken
|
|
||||||
} else {
|
} else {
|
||||||
log.Debug("[aliyunAuthCredential] USE CACHE TOKEN!!!")
|
log.Debug("[aliyunAuthCredential] USE CACHE TOKEN!!!")
|
||||||
}
|
}
|
||||||
|
315
src/pkg/reg/adapter/aliacr/openapi.go
Normal file
315
src/pkg/reg/adapter/aliacr/openapi.go
Normal file
@ -0,0 +1,315 @@
|
|||||||
|
// 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 aliacr
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/aliyun/alibaba-cloud-sdk-go/sdk/requests"
|
||||||
|
"github.com/aliyun/alibaba-cloud-sdk-go/services/cr"
|
||||||
|
"github.com/aliyun/alibaba-cloud-sdk-go/services/cr_ee"
|
||||||
|
)
|
||||||
|
|
||||||
|
type repository struct {
|
||||||
|
Name string
|
||||||
|
Namespace string
|
||||||
|
ID string
|
||||||
|
}
|
||||||
|
|
||||||
|
type authToken struct {
|
||||||
|
user string
|
||||||
|
password string
|
||||||
|
expiresAt time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
// openapi is an interface that defines methods for interacting with an open API.
|
||||||
|
type openapi interface {
|
||||||
|
// ListNamespace returns a list of all namespaces.
|
||||||
|
ListNamespace() ([]string, error)
|
||||||
|
// ListRepository returns a list of all repositories for a specified namespace.
|
||||||
|
ListRepository(namespaceName string) ([]*repository, error)
|
||||||
|
// ListRepoTag returns a list of all tags for a specified repository.
|
||||||
|
ListRepoTag(repo *repository) ([]string, error)
|
||||||
|
// GetAuthorizationToken returns the authorization token for repository.
|
||||||
|
GetAuthorizationToken() (*authToken, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type acrOpenapi struct {
|
||||||
|
client *cr.Client
|
||||||
|
domain string
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ openapi = &acrOpenapi{}
|
||||||
|
|
||||||
|
// newAcrOpenapi creates a new acrOpenapi instance.
|
||||||
|
func newAcrOpenapi(accessKeyID string, accessKeySecret string, regionID string) (openapi, error) {
|
||||||
|
client, err := cr.NewClientWithAccessKey(regionID, accessKeyID, accessKeySecret)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &acrOpenapi{
|
||||||
|
client: client,
|
||||||
|
domain: fmt.Sprintf(endpointTpl, regionID),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListNamespace returns a list of namespaces
|
||||||
|
func (acr *acrOpenapi) ListNamespace() ([]string, error) {
|
||||||
|
var namespaces []string
|
||||||
|
nsReq := cr.CreateGetNamespaceListRequest()
|
||||||
|
nsReq.SetDomain(acr.domain)
|
||||||
|
nsResp, err := acr.client.GetNamespaceList(nsReq)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
var resp = &aliACRNamespaceResp{}
|
||||||
|
err = json.Unmarshal(nsResp.GetHttpContentBytes(), resp)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
for _, ns := range resp.Data.Namespaces {
|
||||||
|
namespaces = append(namespaces, ns.Namespace)
|
||||||
|
}
|
||||||
|
return namespaces, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListRepository returns a list of repositories in the specified namespace
|
||||||
|
func (acr *acrOpenapi) ListRepository(namespaceName string) ([]*repository, error) {
|
||||||
|
var repos []*repository
|
||||||
|
reposReq := cr.CreateGetRepoListByNamespaceRequest()
|
||||||
|
reposReq.SetDomain(acr.domain)
|
||||||
|
reposReq.RepoNamespace = namespaceName
|
||||||
|
var page = 1
|
||||||
|
for {
|
||||||
|
reposReq.Page = requests.NewInteger(page)
|
||||||
|
reposResp, err := acr.client.GetRepoListByNamespace(reposReq)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
var resp = &aliReposResp{}
|
||||||
|
err = json.Unmarshal(reposResp.GetHttpContentBytes(), resp)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, repo := range resp.Data.Repos {
|
||||||
|
repos = append(repos, &repository{
|
||||||
|
Name: repo.RepoName,
|
||||||
|
Namespace: repo.RepoNamespace,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if resp.Data.Total <= (resp.Data.Page * resp.Data.PageSize) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
page++
|
||||||
|
}
|
||||||
|
return repos, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListRepoTag returns a list of tags in the specified repository
|
||||||
|
func (acr *acrOpenapi) ListRepoTag(repo *repository) ([]string, error) {
|
||||||
|
var tags []string
|
||||||
|
tagsReq := cr.CreateGetRepoTagsRequest()
|
||||||
|
tagsReq.SetDomain(acr.domain)
|
||||||
|
tagsReq.RepoNamespace = repo.Namespace
|
||||||
|
tagsReq.RepoName = repo.Name
|
||||||
|
var page = 1
|
||||||
|
for {
|
||||||
|
tagsReq.Page = requests.NewInteger(page)
|
||||||
|
tagsResp, err := acr.client.GetRepoTags(tagsReq)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
var resp = &aliTagResp{}
|
||||||
|
err = json.Unmarshal(tagsResp.GetHttpContentBytes(), resp)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
for _, tag := range resp.Data.Tags {
|
||||||
|
tags = append(tags, tag.Tag)
|
||||||
|
}
|
||||||
|
|
||||||
|
if resp.Data.Total <= (resp.Data.Page * resp.Data.PageSize) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
page++
|
||||||
|
}
|
||||||
|
return tags, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetAuthorizationToken returns the authorization token for repository.
|
||||||
|
func (acr *acrOpenapi) GetAuthorizationToken() (*authToken, error) {
|
||||||
|
tokenRequest := cr.CreateGetAuthorizationTokenRequest()
|
||||||
|
tokenRequest.SetDomain(acr.domain)
|
||||||
|
tokenResponse, err := acr.client.GetAuthorizationToken(tokenRequest)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
var v authorizationToken
|
||||||
|
err = json.Unmarshal(tokenResponse.GetHttpContentBytes(), &v)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &authToken{
|
||||||
|
user: v.Data.TempUserName,
|
||||||
|
password: v.Data.AuthorizationToken,
|
||||||
|
expiresAt: v.Data.ExpireDate.ToTime(),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type acreeOpenapi struct {
|
||||||
|
client *cr_ee.Client
|
||||||
|
domain string
|
||||||
|
instanceID string
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ openapi = &acreeOpenapi{}
|
||||||
|
|
||||||
|
// newAcreeOpenapi creates a new acreeOpenapi instance.
|
||||||
|
func newAcreeOpenapi(accessKeyID string, accessKeySecret string, regionID string, instanceID string) (openapi, error) {
|
||||||
|
client, err := cr_ee.NewClientWithAccessKey(regionID, accessKeyID, accessKeySecret)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &acreeOpenapi{
|
||||||
|
client: client,
|
||||||
|
domain: fmt.Sprintf(endpointTpl, regionID),
|
||||||
|
instanceID: instanceID,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListNamespace returns a list of namespaces
|
||||||
|
func (acree *acreeOpenapi) ListNamespace() ([]string, error) {
|
||||||
|
var namespaces []string
|
||||||
|
nsReq := cr_ee.CreateListNamespaceRequest()
|
||||||
|
nsReq.SetDomain(acree.domain)
|
||||||
|
nsReq.InstanceId = acree.instanceID
|
||||||
|
page := 1
|
||||||
|
for {
|
||||||
|
nsReq.PageNo = requests.NewInteger(page)
|
||||||
|
nsResp, err := acree.client.ListNamespace(nsReq)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
for _, ns := range nsResp.Namespaces {
|
||||||
|
namespaces = append(namespaces, ns.NamespaceName)
|
||||||
|
}
|
||||||
|
if !nsResp.ListNamespaceIsSuccess {
|
||||||
|
return nil, fmt.Errorf("failed to list namespace: %v", nsResp)
|
||||||
|
}
|
||||||
|
total, err := strconv.Atoi(nsResp.TotalCount)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if total-(nsResp.PageNo*nsResp.PageSize) <= 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
page++
|
||||||
|
}
|
||||||
|
return namespaces, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListRepository returns a list of repositories in the specified namespace
|
||||||
|
func (acree *acreeOpenapi) ListRepository(namespaceName string) ([]*repository, error) {
|
||||||
|
var repos []*repository
|
||||||
|
reposReq := cr_ee.CreateListRepositoryRequest()
|
||||||
|
reposReq.SetDomain(acree.domain)
|
||||||
|
reposReq.InstanceId = acree.instanceID
|
||||||
|
reposReq.RepoNamespaceName = namespaceName
|
||||||
|
reposReq.RepoStatus = "NORMAL"
|
||||||
|
page := 1
|
||||||
|
for {
|
||||||
|
reposReq.PageNo = requests.NewInteger(page)
|
||||||
|
reposResp, err := acree.client.ListRepository(reposReq)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if !reposResp.ListRepositoryIsSuccess {
|
||||||
|
return nil, fmt.Errorf("failed to list repo: %s", reposResp.GetHttpContentString())
|
||||||
|
}
|
||||||
|
for _, repo := range reposResp.Repositories {
|
||||||
|
repos = append(repos, &repository{
|
||||||
|
Name: repo.RepoName,
|
||||||
|
Namespace: repo.RepoNamespaceName,
|
||||||
|
ID: repo.RepoId,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
total, err := strconv.Atoi(reposResp.TotalCount)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if total <= page*reposResp.PageSize {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
page++
|
||||||
|
}
|
||||||
|
return repos, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListRepoTag returns a list of tags in the specified repository
|
||||||
|
func (acree *acreeOpenapi) ListRepoTag(repo *repository) ([]string, error) {
|
||||||
|
var tags []string
|
||||||
|
tagsReq := cr_ee.CreateListRepoTagRequest()
|
||||||
|
tagsReq.SetDomain(acree.domain)
|
||||||
|
tagsReq.InstanceId = acree.instanceID
|
||||||
|
tagsReq.RepoId = repo.ID
|
||||||
|
var page = 1
|
||||||
|
for {
|
||||||
|
tagsReq.PageNo = requests.NewInteger(page)
|
||||||
|
tagsResp, err := acree.client.ListRepoTag(tagsReq)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
for _, image := range tagsResp.Images {
|
||||||
|
tags = append(tags, image.Tag)
|
||||||
|
}
|
||||||
|
if !tagsResp.ListRepoTagIsSuccess {
|
||||||
|
return nil, fmt.Errorf("failed to list repo tag: %s", tagsResp.GetHttpContentString())
|
||||||
|
}
|
||||||
|
total, err := strconv.Atoi(tagsResp.TotalCount)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if total <= page*tagsResp.PageSize {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
page++
|
||||||
|
}
|
||||||
|
return tags, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetAuthorizationToken returns the authorization token for repository.
|
||||||
|
func (acree *acreeOpenapi) GetAuthorizationToken() (*authToken, error) {
|
||||||
|
tokenRequest := cr_ee.CreateGetAuthorizationTokenRequest()
|
||||||
|
// FIXME: use vpc endpoint if vpc is enabled
|
||||||
|
tokenRequest.SetDomain(acree.domain)
|
||||||
|
tokenRequest.InstanceId = acree.instanceID
|
||||||
|
tokenResponse, err := acree.client.GetAuthorizationToken(tokenRequest)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if !tokenResponse.GetAuthorizationTokenIsSuccess {
|
||||||
|
return nil, fmt.Errorf("failed to get authorization token: %s", tokenResponse.GetHttpContentString())
|
||||||
|
}
|
||||||
|
return &authToken{
|
||||||
|
user: tokenResponse.TempUsername,
|
||||||
|
password: tokenResponse.AuthorizationToken,
|
||||||
|
expiresAt: time.Unix(tokenResponse.ExpireTime/1000, 0),
|
||||||
|
}, nil
|
||||||
|
}
|
262
src/pkg/reg/adapter/aliacr/openapi_test.go
Normal file
262
src/pkg/reg/adapter/aliacr/openapi_test.go
Normal file
@ -0,0 +1,262 @@
|
|||||||
|
// 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 aliacr
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/goharbor/harbor/src/pkg/reg/model"
|
||||||
|
)
|
||||||
|
|
||||||
|
var acrAdapter *adapter
|
||||||
|
var acreeAdapter *adapter
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
accessKey := os.Getenv("ALIYUN_ACCESS_KEY")
|
||||||
|
accessSecret := os.Getenv("ALIYUN_ACCESS_SECRET")
|
||||||
|
acrEndpoint := os.Getenv("ALIYUN_ACR_ENDPOINT")
|
||||||
|
acreeEndpoint := os.Getenv("ALIYUN_ACREE_ENDPOINT")
|
||||||
|
|
||||||
|
if accessKey == "" || accessSecret == "" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if acrEndpoint != "" {
|
||||||
|
acrAdapter, _ = newAdapter(&model.Registry{
|
||||||
|
URL: acrEndpoint,
|
||||||
|
Credential: &model.Credential{
|
||||||
|
AccessKey: accessKey,
|
||||||
|
AccessSecret: accessSecret,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if acreeEndpoint != "" {
|
||||||
|
acreeAdapter, _ = newAdapter(&model.Registry{
|
||||||
|
URL: acreeEndpoint,
|
||||||
|
Credential: &model.Credential{
|
||||||
|
AccessKey: accessKey,
|
||||||
|
AccessSecret: accessSecret,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func skipCheck(adapter *adapter) bool {
|
||||||
|
return adapter == nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_ACREE_ListNamespace(t *testing.T) {
|
||||||
|
if skipCheck(acreeAdapter) {
|
||||||
|
t.Skip("skip test acree ListNamespace")
|
||||||
|
}
|
||||||
|
testcases := []struct {
|
||||||
|
name string
|
||||||
|
wantedNamespace []string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "test list namespace",
|
||||||
|
wantedNamespace: []string{"ut_acree_namespace"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tc := range testcases {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
namespaces, err := acreeAdapter.acrAPI.ListNamespace()
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("ListNamespace error: %v", err)
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(namespaces, tc.wantedNamespace) {
|
||||||
|
t.Errorf("ListNamespace error, wants=%v, actual=%v", tc.wantedNamespace, namespaces)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_ACREE_ListRepositoryAndTag(t *testing.T) {
|
||||||
|
if skipCheck(acreeAdapter) {
|
||||||
|
t.Skip("skip test acree ListRepository")
|
||||||
|
}
|
||||||
|
testcases := []struct {
|
||||||
|
name string
|
||||||
|
namespace string
|
||||||
|
wantedRepo []string
|
||||||
|
wantedTag []string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "test list repository",
|
||||||
|
namespace: "ut_acree_namespace",
|
||||||
|
wantedRepo: []string{"ut_acree_repo"},
|
||||||
|
wantedTag: []string{"latest"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tc := range testcases {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
// List repository
|
||||||
|
repos, err := acreeAdapter.acrAPI.ListRepository(tc.namespace)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("ListRepository error: %v", err)
|
||||||
|
}
|
||||||
|
var actualRepo []string
|
||||||
|
for _, repo := range repos {
|
||||||
|
actualRepo = append(actualRepo, repo.Name)
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(actualRepo, tc.wantedRepo) {
|
||||||
|
t.Errorf("ListRepository error, wants=%v, actual=%v", tc.wantedRepo, actualRepo)
|
||||||
|
}
|
||||||
|
|
||||||
|
// List tag
|
||||||
|
var actualTag []string
|
||||||
|
for _, repo := range repos {
|
||||||
|
tags, err := acreeAdapter.acrAPI.ListRepoTag(repo)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("ListRepoTag error: %v", err)
|
||||||
|
}
|
||||||
|
actualTag = append(actualTag, tags...)
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(actualTag, tc.wantedTag) {
|
||||||
|
t.Errorf("ListRepoTag error, wants=%v, actual=%v", tc.wantedTag, actualTag)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_ACREE_GetAuthorizationToken(t *testing.T) {
|
||||||
|
if skipCheck(acreeAdapter) {
|
||||||
|
t.Skip("skip test acree GetAuthorizationToken")
|
||||||
|
}
|
||||||
|
testcases := []struct {
|
||||||
|
name string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "test get authorization token",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tc := range testcases {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
token, err := acreeAdapter.acrAPI.GetAuthorizationToken()
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("GetAuthorizationToken error: %v", err)
|
||||||
|
}
|
||||||
|
if token == nil {
|
||||||
|
t.Errorf("GetAuthorizationToken error, token is nil")
|
||||||
|
}
|
||||||
|
if !time.Now().Before(token.expiresAt) {
|
||||||
|
t.Errorf("GetAuthorizationToken error, token expiresAt is not valid")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_ACR_ListNamespace(t *testing.T) {
|
||||||
|
if skipCheck(acrAdapter) {
|
||||||
|
t.Skip("skip test acr ListNamespace")
|
||||||
|
}
|
||||||
|
testcases := []struct {
|
||||||
|
name string
|
||||||
|
wantedNamespace []string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "test list namespace",
|
||||||
|
wantedNamespace: []string{"ut_acr_namespace"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tc := range testcases {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
namespaces, err := acrAdapter.acrAPI.ListNamespace()
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("ListNamespace error: %v", err)
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(namespaces, tc.wantedNamespace) {
|
||||||
|
t.Errorf("ListNamespace error, wants=%v, actual=%v", tc.wantedNamespace, namespaces)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_ACR_ListRepositoryAndTag(t *testing.T) {
|
||||||
|
if skipCheck(acrAdapter) {
|
||||||
|
t.Skip("skip test acr ListRepository")
|
||||||
|
}
|
||||||
|
testcases := []struct {
|
||||||
|
name string
|
||||||
|
namespace string
|
||||||
|
wantedRepo []string
|
||||||
|
wantedTag []string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "test list repository",
|
||||||
|
namespace: "ut_acr_namespace",
|
||||||
|
wantedRepo: []string{"ut_acr_repo"},
|
||||||
|
wantedTag: []string{"latest"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tc := range testcases {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
// List repository
|
||||||
|
repos, err := acrAdapter.acrAPI.ListRepository(tc.namespace)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("ListRepository error: %v", err)
|
||||||
|
}
|
||||||
|
var actualRepo []string
|
||||||
|
for _, repo := range repos {
|
||||||
|
actualRepo = append(actualRepo, repo.Name)
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(actualRepo, tc.wantedRepo) {
|
||||||
|
t.Errorf("ListRepository error, wants=%v, actual=%v", tc.wantedRepo, actualRepo)
|
||||||
|
}
|
||||||
|
// List tag
|
||||||
|
var actualTag []string
|
||||||
|
for _, repo := range repos {
|
||||||
|
tags, err := acrAdapter.acrAPI.ListRepoTag(repo)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("ListRepoTag error: %v", err)
|
||||||
|
}
|
||||||
|
actualTag = append(actualTag, tags...)
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(actualTag, tc.wantedTag) {
|
||||||
|
t.Errorf("ListRepoTag error, wants=%v, actual=%v", tc.wantedTag, actualTag)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_ACR_GetAuthorizationToken(t *testing.T) {
|
||||||
|
if skipCheck(acrAdapter) {
|
||||||
|
t.Skip("skip test acr GetAuthorizationToken")
|
||||||
|
}
|
||||||
|
testcases := []struct {
|
||||||
|
name string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "test get authorization token",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tc := range testcases {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
token, err := acrAdapter.acrAPI.GetAuthorizationToken()
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("GetAuthorizationToken error: %v", err)
|
||||||
|
}
|
||||||
|
if token == nil {
|
||||||
|
t.Errorf("GetAuthorizationToken error, token is nil")
|
||||||
|
}
|
||||||
|
if !time.Now().Before(token.expiresAt) {
|
||||||
|
t.Errorf("GetAuthorizationToken error, token expiresAt is not valid")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
@ -17,12 +17,18 @@ package aliacr
|
|||||||
import "time"
|
import "time"
|
||||||
|
|
||||||
const (
|
const (
|
||||||
registryEndpointTpl = "https://registry.%s.aliyuncs.com"
|
registryEndpointTpl = "https://registry.%s.aliyuncs.com"
|
||||||
registryVPCEndpointTpl = "https://registry-vpc.%s.aliyuncs.com"
|
endpointTpl = "cr.%s.aliyuncs.com"
|
||||||
registryInternalEndpointTpl = "https://registry-internal.%s.aliyuncs.com"
|
|
||||||
endpointTpl = "cr.%s.aliyuncs.com"
|
registryACRService = "registry.aliyuncs.com"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type registryServiceInfo struct {
|
||||||
|
IsACREE bool
|
||||||
|
RegionID string
|
||||||
|
InstanceID string
|
||||||
|
}
|
||||||
|
|
||||||
type authorizationToken struct {
|
type authorizationToken struct {
|
||||||
Data struct {
|
Data struct {
|
||||||
ExpireDate timeUnix `json:"expireDate"`
|
ExpireDate timeUnix `json:"expireDate"`
|
||||||
|
@ -273,6 +273,166 @@ describe('CreateEditEndpointComponent (inline template)', () => {
|
|||||||
key: 'me-east-1-internal',
|
key: 'me-east-1-internal',
|
||||||
value: 'https://registry-internal.me-east-1.aliyuncs.com',
|
value: 'https://registry-internal.me-east-1.aliyuncs.com',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
key: 'cn-hangzhou-ee-vpc',
|
||||||
|
value: `https://instanceName-registry-vpc.cn-hangzhou.cr.aliyuncs.com`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'cn-shanghai-ee-vpc',
|
||||||
|
value: 'https://instanceName-registry-vpc.cn-shanghai.cr.aliyuncs.com',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'cn-qingdao-ee-vpc',
|
||||||
|
value: 'https://instanceName-registry-vpc.cn-qingdao.cr.aliyuncs.com',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'cn-beijing-ee-vpc',
|
||||||
|
value: 'https://instanceName-registry-vpc.cn-beijing.cr.aliyuncs.com',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'cn-zhangjiakou-ee-vpc',
|
||||||
|
value: 'https://instanceName-registry-vpc.cn-zhangjiakou.cr.aliyuncs.com',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'cn-huhehaote-ee-vpc',
|
||||||
|
value: 'https://instanceName-registry-vpc.cn-huhehaote.cr.aliyuncs.com',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'cn-shenzhen-ee-vpc',
|
||||||
|
value: 'https://instanceName-registry-vpc.cn-shenzhen.cr.aliyuncs.com',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'cn-chengdu-ee-vpc',
|
||||||
|
value: 'https://instanceName-registry-vpc.cn-chengdu.cr.aliyuncs.com',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'cn-hongkong-ee-vpc',
|
||||||
|
value: 'https://instanceName-registry-vpc.cn-hongkong.cr.aliyuncs.com',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'ap-southeast-1-ee-vpc',
|
||||||
|
value: 'https://instanceName-registry-vpc.ap-southeast-1.cr.aliyuncs.com',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'ap-southeast-2-ee-vpc',
|
||||||
|
value: 'https://instanceName-registry-vpc.ap-southeast-2.cr.aliyuncs.com',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'ap-southeast-3-ee-vpc',
|
||||||
|
value: 'https://instanceName-registry-vpc.ap-southeast-3.cr.aliyuncs.com',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'ap-southeast-5-ee-vpc',
|
||||||
|
value: 'https://instanceName-registry-vpc.ap-southeast-5.aliyuncs.cr.com',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'ap-northeast-1-ee-vpc',
|
||||||
|
value: 'https://instanceName-registry-vpc.ap-northeast-1.cr.aliyuncs.com',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'ap-south-1-ee-vpc',
|
||||||
|
value: 'https:/instanceName-/registry-vpc.ap-south-1.cr.aliyuncs.com',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'eu-central-1-ee-vpc',
|
||||||
|
value: 'https://instanceName-registry-vpc.eu-central-1.cr.aliyuncs.com',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'eu-west-1-ee-vpc',
|
||||||
|
value: 'https://instanceName-registry-vpc.eu-west-1.cr.aliyuncs.com',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'us-west-1-ee-vpc',
|
||||||
|
value: 'https://instanceName-registry-vpc.us-west-1.cr.aliyuncs.com',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'us-east-1-ee-vpc',
|
||||||
|
value: 'https://instanceName-registry-vpc.us-east-1.cr.aliyuncs.com',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'me-east-1-ee-vpc',
|
||||||
|
value: 'https://instanceName-registry-vpc.me-east-1.cr.aliyuncs.com',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'cn-hangzhou-ee',
|
||||||
|
value: `https://instanceName-registry.cn-hangzhou.cr.aliyuncs.com`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'cn-shanghai-ee',
|
||||||
|
value: 'https://instanceName-registry.cn-shanghai.cr.aliyuncs.com',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'cn-qingdao-ee',
|
||||||
|
value: 'https://instanceName-registry.cn-qingdao.cr.aliyuncs.com',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'cn-beijing-ee',
|
||||||
|
value: 'https://instanceName-registry.cn-beijing.cr.aliyuncs.com',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'cn-zhangjiakou-ee',
|
||||||
|
value: 'https://instanceName-registry.cn-zhangjiakou.cr.aliyuncs.com',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'cn-huhehaote-ee',
|
||||||
|
value: 'https://instanceName-registry.cn-huhehaote.cr.aliyuncs.com',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'cn-shenzhen-ee',
|
||||||
|
value: 'https://instanceName-registry.cn-shenzhen.cr.aliyuncs.com',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'cn-chengdu-ee',
|
||||||
|
value: 'https://instanceName-registry.cn-chengdu.cr.aliyuncs.com',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'cn-hongkong-ee',
|
||||||
|
value: 'https://instanceName-registry.cn-hongkong.cr.aliyuncs.com',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'ap-southeast-1-ee',
|
||||||
|
value: 'https://instanceName-registry.ap-southeast-1.cr.aliyuncs.com',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'ap-southeast-2-ee',
|
||||||
|
value: 'https://instanceName-registry.ap-southeast-2.cr.aliyuncs.com',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'ap-southeast-3-ee',
|
||||||
|
value: 'https://instanceName-registry.ap-southeast-3.cr.aliyuncs.com',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'ap-southeast-5-ee',
|
||||||
|
value: 'https://instanceName-registry.ap-southeast-5.aliyuncs.cr.com',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'ap-northeast-1-ee',
|
||||||
|
value: 'https://instanceName-registry.ap-northeast-1.cr.aliyuncs.com',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'ap-south-1-ee',
|
||||||
|
value: 'https:/instanceName-/registry.ap-south-1.cr.aliyuncs.com',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'eu-central-1-ee',
|
||||||
|
value: 'https://instanceName-registry.eu-central-1.cr.aliyuncs.com',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'eu-west-1-ee',
|
||||||
|
value: 'https://instanceName-registry.eu-west-1.cr.aliyuncs.com',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'us-west-1-ee',
|
||||||
|
value: 'https://instanceName-registry.us-west-1.cr.aliyuncs.com',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'us-east-1-ee',
|
||||||
|
value: 'https://instanceName-registry-.us-east-1.cr.aliyuncs.com',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'me-east-1-ee',
|
||||||
|
value: 'https://instanceName-registry.me-east-1.cr.aliyuncs.com',
|
||||||
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
credential_pattern: null,
|
credential_pattern: null,
|
||||||
|
@ -263,6 +263,166 @@ describe('EndpointComponent (inline template)', () => {
|
|||||||
key: 'me-east-1-internal',
|
key: 'me-east-1-internal',
|
||||||
value: 'https://registry-internal.me-east-1.aliyuncs.com',
|
value: 'https://registry-internal.me-east-1.aliyuncs.com',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
key: 'cn-hangzhou-ee-vpc',
|
||||||
|
value: `https://instanceName-registry-vpc.cn-hangzhou.cr.aliyuncs.com`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'cn-shanghai-ee-vpc',
|
||||||
|
value: 'https://instanceName-registry-vpc.cn-shanghai.cr.aliyuncs.com',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'cn-qingdao-ee-vpc',
|
||||||
|
value: 'https://instanceName-registry-vpc.cn-qingdao.cr.aliyuncs.com',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'cn-beijing-ee-vpc',
|
||||||
|
value: 'https://instanceName-registry-vpc.cn-beijing.cr.aliyuncs.com',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'cn-zhangjiakou-ee-vpc',
|
||||||
|
value: 'https://instanceName-registry-vpc.cn-zhangjiakou.cr.aliyuncs.com',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'cn-huhehaote-ee-vpc',
|
||||||
|
value: 'https://instanceName-registry-vpc.cn-huhehaote.cr.aliyuncs.com',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'cn-shenzhen-ee-vpc',
|
||||||
|
value: 'https://instanceName-registry-vpc.cn-shenzhen.cr.aliyuncs.com',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'cn-chengdu-ee-vpc',
|
||||||
|
value: 'https://instanceName-registry-vpc.cn-chengdu.cr.aliyuncs.com',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'cn-hongkong-ee-vpc',
|
||||||
|
value: 'https://instanceName-registry-vpc.cn-hongkong.cr.aliyuncs.com',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'ap-southeast-1-ee-vpc',
|
||||||
|
value: 'https://instanceName-registry-vpc.ap-southeast-1.cr.aliyuncs.com',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'ap-southeast-2-ee-vpc',
|
||||||
|
value: 'https://instanceName-registry-vpc.ap-southeast-2.cr.aliyuncs.com',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'ap-southeast-3-ee-vpc',
|
||||||
|
value: 'https://instanceName-registry-vpc.ap-southeast-3.cr.aliyuncs.com',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'ap-southeast-5-ee-vpc',
|
||||||
|
value: 'https://instanceName-registry-vpc.ap-southeast-5.aliyuncs.cr.com',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'ap-northeast-1-ee-vpc',
|
||||||
|
value: 'https://instanceName-registry-vpc.ap-northeast-1.cr.aliyuncs.com',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'ap-south-1-ee-vpc',
|
||||||
|
value: 'https:/instanceName-/registry-vpc.ap-south-1.cr.aliyuncs.com',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'eu-central-1-ee-vpc',
|
||||||
|
value: 'https://instanceName-registry-vpc.eu-central-1.cr.aliyuncs.com',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'eu-west-1-ee-vpc',
|
||||||
|
value: 'https://instanceName-registry-vpc.eu-west-1.cr.aliyuncs.com',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'us-west-1-ee-vpc',
|
||||||
|
value: 'https://instanceName-registry-vpc.us-west-1.cr.aliyuncs.com',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'us-east-1-ee-vpc',
|
||||||
|
value: 'https://instanceName-registry-vpc.us-east-1.cr.aliyuncs.com',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'me-east-1-ee-vpc',
|
||||||
|
value: 'https://instanceName-registry-vpc.me-east-1.cr.aliyuncs.com',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'cn-hangzhou-ee',
|
||||||
|
value: `https://instanceName-registry.cn-hangzhou.cr.aliyuncs.com`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'cn-shanghai-ee',
|
||||||
|
value: 'https://instanceName-registry.cn-shanghai.cr.aliyuncs.com',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'cn-qingdao-ee',
|
||||||
|
value: 'https://instanceName-registry.cn-qingdao.cr.aliyuncs.com',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'cn-beijing-ee',
|
||||||
|
value: 'https://instanceName-registry.cn-beijing.cr.aliyuncs.com',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'cn-zhangjiakou-ee',
|
||||||
|
value: 'https://instanceName-registry.cn-zhangjiakou.cr.aliyuncs.com',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'cn-huhehaote-ee',
|
||||||
|
value: 'https://instanceName-registry.cn-huhehaote.cr.aliyuncs.com',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'cn-shenzhen-ee',
|
||||||
|
value: 'https://instanceName-registry.cn-shenzhen.cr.aliyuncs.com',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'cn-chengdu-ee',
|
||||||
|
value: 'https://instanceName-registry.cn-chengdu.cr.aliyuncs.com',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'cn-hongkong-ee',
|
||||||
|
value: 'https://instanceName-registry.cn-hongkong.cr.aliyuncs.com',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'ap-southeast-1-ee',
|
||||||
|
value: 'https://instanceName-registry.ap-southeast-1.cr.aliyuncs.com',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'ap-southeast-2-ee',
|
||||||
|
value: 'https://instanceName-registry.ap-southeast-2.cr.aliyuncs.com',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'ap-southeast-3-ee',
|
||||||
|
value: 'https://instanceName-registry.ap-southeast-3.cr.aliyuncs.com',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'ap-southeast-5-ee',
|
||||||
|
value: 'https://instanceName-registry.ap-southeast-5.aliyuncs.cr.com',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'ap-northeast-1-ee',
|
||||||
|
value: 'https://instanceName-registry.ap-northeast-1.cr.aliyuncs.com',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'ap-south-1-ee',
|
||||||
|
value: 'https:/instanceName-/registry.ap-south-1.cr.aliyuncs.com',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'eu-central-1-ee',
|
||||||
|
value: 'https://instanceName-registry.eu-central-1.cr.aliyuncs.com',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'eu-west-1-ee',
|
||||||
|
value: 'https://instanceName-registry.eu-west-1.cr.aliyuncs.com',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'us-west-1-ee',
|
||||||
|
value: 'https://instanceName-registry.us-west-1.cr.aliyuncs.com',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'us-east-1-ee',
|
||||||
|
value: 'https://instanceName-registry-.us-east-1.cr.aliyuncs.com',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'me-east-1-ee',
|
||||||
|
value: 'https://instanceName-registry.me-east-1.cr.aliyuncs.com',
|
||||||
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
credential_pattern: null,
|
credential_pattern: null,
|
||||||
|
Loading…
Reference in New Issue
Block a user