Skip to content
This repository was archived by the owner on Aug 31, 2021. It is now read-only.
Closed
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
88 changes: 80 additions & 8 deletions openstack/endpoint_location.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,26 @@
package openstack

import (
"errors"
"regexp"

"github.com/huaweicloud/golangsdk"
tokens2 "github.com/huaweicloud/golangsdk/openstack/identity/v2/tokens"
tokens3 "github.com/huaweicloud/golangsdk/openstack/identity/v3/tokens"
)

// a regular patten for searching servicename in an existing endpoint
var replaceEndpointUrl = regexp.MustCompile(`https://.+?\.`)

// a regular patten for searching service name and location
var repWithoutLocal = regexp.MustCompile(`https://.+?\..+?\.`)

// service have same endpoint address in different location, refer to https://developer.huaweicloud.com/endpoint
var allRegionInOneEndpoint = map[string]struct{}{
"cdn": struct{}{},
"dns": struct{}{},
}

/*
V2EndpointURL discovers the endpoint URL for a specific service from a
ServiceCatalog acquired during the v2 identity service.
Expand All @@ -20,7 +35,7 @@ func V2EndpointURL(catalog *tokens2.ServiceCatalog, opts golangsdk.EndpointOpts)
// Extract Endpoints from the catalog entries that match the requested Type, Name if provided, and Region if provided.
var endpoints = make([]tokens2.Endpoint, 0, 1)
for _, entry := range catalog.Entries {
if (entry.Type == opts.Type) && (opts.Name == "" || entry.Name == opts.Name) {
if (opts.Type == "" || entry.Type == opts.Type) && (opts.Name == "" || entry.Name == opts.Name) {
for _, endpoint := range entry.Endpoints {
if opts.Region == "" || endpoint.Region == opts.Region {
endpoints = append(endpoints, endpoint)
Expand All @@ -30,10 +45,18 @@ func V2EndpointURL(catalog *tokens2.ServiceCatalog, opts golangsdk.EndpointOpts)
}

// Report an error if the options were ambiguous.
if len(endpoints) > 1 {
err := &ErrMultipleMatchingEndpointsV2{}
err.Endpoints = endpoints
return "", err
if opts.Type != "" {
if len(endpoints) > 1 {
err := &ErrMultipleMatchingEndpointsV2{}
err.Endpoints = endpoints
return "", err
} else if len(endpoints) < 1 {
return buildUrlIfNotFoundV2(catalog, opts)
}
} else {
if len(endpoints) < 1 {
return "", &golangsdk.ErrEndpointNotFound{}
}
}

// Extract the appropriate URL from the matching Endpoint.
Expand Down Expand Up @@ -73,7 +96,7 @@ func V3EndpointURL(catalog *tokens3.ServiceCatalog, opts golangsdk.EndpointOpts)
// Name if provided, and Region if provided.
var endpoints = make([]tokens3.Endpoint, 0, 1)
for _, entry := range catalog.Entries {
if (entry.Type == opts.Type) && (opts.Name == "" || entry.Name == opts.Name) {
if (opts.Type == "" || entry.Type == opts.Type) && (opts.Name == "" || entry.Name == opts.Name) {
for _, endpoint := range entry.Endpoints {
if opts.Availability != golangsdk.AvailabilityAdmin &&
opts.Availability != golangsdk.AvailabilityPublic &&
Expand All @@ -92,8 +115,18 @@ func V3EndpointURL(catalog *tokens3.ServiceCatalog, opts golangsdk.EndpointOpts)
}

// Report an error if the options were ambiguous.
if len(endpoints) > 1 {
return "", ErrMultipleMatchingEndpointsV3{Endpoints: endpoints}
if opts.Type != "" {
if len(endpoints) > 1 {
return "", ErrMultipleMatchingEndpointsV3{Endpoints: endpoints}
} else if len(endpoints) < 1 {
return buildUrlIfNotFoundV3(catalog, opts)
}
} else {
if len(endpoints) > 1 {
return endpoints[0].URL, nil
} else if len(endpoints) < 1 {
return "", &golangsdk.ErrEndpointNotFound{}
}
}

// Extract the URL from the matching Endpoint.
Expand All @@ -105,3 +138,42 @@ func V3EndpointURL(catalog *tokens3.ServiceCatalog, opts golangsdk.EndpointOpts)
err := &golangsdk.ErrEndpointNotFound{}
return "", err
}

/*
buildUrlIfNotFound builds an endpoint if it is not found in identity service response
*/
func buildUrlIfNotFoundV2(catalog *tokens2.ServiceCatalog, opts golangsdk.EndpointOpts) (string, error) {

tmpOpts := opts
tmpOpts.Type = ""

existingUrl, err := V2EndpointURL(catalog, tmpOpts)
return generateEndpointUrlWithExisting(existingUrl, opts, err)
}

/*
buildUrlIfNotFound builds an endpoint if it is not found in identity service response
*/
func buildUrlIfNotFoundV3(catalog *tokens3.ServiceCatalog, opts golangsdk.EndpointOpts) (string, error) {

tmpOpts := opts
tmpOpts.Type = ""

existingUrl, err := V3EndpointURL(catalog, tmpOpts)
return generateEndpointUrlWithExisting(existingUrl, opts, err)
}

// internal method for extract a valid endpoint address
func generateEndpointUrlWithExisting(existingUrl string, opts golangsdk.EndpointOpts, err error) (string, error) {
if err != nil || existingUrl == "" {
return "", errors.New("No suitable endpoint could be found in the service catalog.")
}

existingUrl = golangsdk.NormalizeURL(existingUrl)
if _, ok := allRegionInOneEndpoint[opts.Type]; ok {
existingUrl = repWithoutLocal.ReplaceAllString(existingUrl, ("https://" + opts.Type + "."))
} else {
existingUrl = replaceEndpointUrl.ReplaceAllString(existingUrl, ("https://" + opts.Type + "."))
}
return existingUrl, nil
}
44 changes: 42 additions & 2 deletions openstack/testing/endpoint_location_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,8 +80,28 @@ func TestV2EndpointExact(t *testing.T) {
}
}

func TestV2EndpointExactNonExist(t *testing.T) {
expectedURLs := map[golangsdk.Availability]string{
golangsdk.AvailabilityPublic: "https://none.correct.com/",
golangsdk.AvailabilityAdmin: "https://none.correct.com/",
golangsdk.AvailabilityInternal: "https://none.correct.com/",
}

for availability, expected := range expectedURLs {
actual, err := openstack.V2EndpointURL(&catalog2, golangsdk.EndpointOpts{
Type: "none",
Name: "same",
Region: "same",
Availability: availability,
})
th.AssertNoErr(t, err)
th.CheckEquals(t, expected, actual)
}
}

func TestV2EndpointNone(t *testing.T) {
_, actual := openstack.V2EndpointURL(&catalog2, golangsdk.EndpointOpts{
_, actual := openstack.V2EndpointURL(&tokens2.ServiceCatalog{
Entries: []tokens2.CatalogEntry{}}, golangsdk.EndpointOpts{
Type: "nope",
Availability: golangsdk.AvailabilityPublic,
})
Expand Down Expand Up @@ -200,8 +220,28 @@ func TestV3EndpointExact(t *testing.T) {
}
}

func TestV3EndpointExactNonExist(t *testing.T) {
expectedURLs := map[golangsdk.Availability]string{
golangsdk.AvailabilityPublic: "https://none.correct.com/",
golangsdk.AvailabilityAdmin: "https://none.correct.com/",
golangsdk.AvailabilityInternal: "https://none.correct.com/",
}

for availability, expected := range expectedURLs {
actual, err := openstack.V3EndpointURL(&catalog3, golangsdk.EndpointOpts{
Type: "none",
Name: "same",
Region: "same",
Availability: availability,
})
th.AssertNoErr(t, err)
th.CheckEquals(t, expected, actual)
}
}

func TestV3EndpointNone(t *testing.T) {
_, actual := openstack.V3EndpointURL(&catalog3, golangsdk.EndpointOpts{
_, actual := openstack.V3EndpointURL(&tokens3.ServiceCatalog{
Entries: []tokens3.CatalogEntry{}}, golangsdk.EndpointOpts{
Type: "nope",
Availability: golangsdk.AvailabilityPublic,
})
Expand Down