From 95e63d9f87b5bee6f836053c122434085cb9cdff Mon Sep 17 00:00:00 2001 From: Zexi Li Date: Wed, 22 Aug 2018 03:14:57 +0800 Subject: [PATCH 1/4] climc: kube cluster, node and service resource rename --- pkg/mcclient/modules/k8s/cluster.go | 2 +- pkg/mcclient/modules/k8s/node.go | 2 +- pkg/mcclient/modules/k8s/service.go | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pkg/mcclient/modules/k8s/cluster.go b/pkg/mcclient/modules/k8s/cluster.go index 42ee41d6b1..46b7bb4fea 100644 --- a/pkg/mcclient/modules/k8s/cluster.go +++ b/pkg/mcclient/modules/k8s/cluster.go @@ -9,7 +9,7 @@ var ( ) func init() { - Clusters = NewManager("cluster", "clusters", + Clusters = NewManager("kube_cluster", "kube_clusters", NewResourceCols("mode", "k8s_version", "status", "api_endpoint"), NewColumns()) modules.Register(Clusters) diff --git a/pkg/mcclient/modules/k8s/node.go b/pkg/mcclient/modules/k8s/node.go index 60f5bd6ade..630c0ffb8c 100644 --- a/pkg/mcclient/modules/k8s/node.go +++ b/pkg/mcclient/modules/k8s/node.go @@ -9,6 +9,6 @@ var ( ) func init() { - Nodes = NewManager("node", "nodes", NewResourceCols("cluster", "roles", "address", "status"), NewColumns()) + Nodes = NewManager("kube_node", "kube_nodes", NewResourceCols("cluster", "roles", "address", "status"), NewColumns()) modules.Register(Nodes) } diff --git a/pkg/mcclient/modules/k8s/service.go b/pkg/mcclient/modules/k8s/service.go index e66f38dda3..7a153c4bc4 100644 --- a/pkg/mcclient/modules/k8s/service.go +++ b/pkg/mcclient/modules/k8s/service.go @@ -13,7 +13,7 @@ type ServiceManager struct { func init() { Services = &ServiceManager{ ResourceManager: *NewManager( - "service", "services", + "k8s_service", "k8s_services", NewNamespaceCols("clusterIP", "selector", "internalEndpoint", "externalEndpoints"), NewClusterCols())} modules.Register(Services) From af61f13df6383b21354337bfe01f4d09a81959a2 Mon Sep 17 00:00:00 2001 From: Zexi Li Date: Wed, 22 Aug 2018 11:05:39 +0800 Subject: [PATCH 2/4] =?UTF-8?q?=E4=BF=AE=E6=AD=A3:=20=E4=B8=8D=E5=90=8C=20?= =?UTF-8?q?ServiceType=20=E7=9A=84=20module=20=E6=B3=A8=E5=86=8C=E4=B8=8D?= =?UTF-8?q?=E4=BC=9A=E6=8A=A5=E9=94=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pkg/mcclient/modules/modules.go | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/pkg/mcclient/modules/modules.go b/pkg/mcclient/modules/modules.go index 2381f50232..1ba4171f98 100644 --- a/pkg/mcclient/modules/modules.go +++ b/pkg/mcclient/modules/modules.go @@ -2,10 +2,10 @@ package modules import ( "fmt" - "log" "strings" "yunion.io/x/jsonutils" + "yunion.io/x/log" "yunion.io/x/onecloud/pkg/mcclient" ) @@ -142,6 +142,17 @@ func _getJointKey(mod1 Manager, mod2 Manager) string { return fmt.Sprintf("%s-%s", mod1.KeyString(), mod2.KeyString()) } +func ensureModuleNotRegistered(mod, newMod BaseManagerInterface) { + modSvcType := mod.ServiceType() + newModSvcType := newMod.ServiceType() + if mod == newMod { + log.Fatalf("Module %#v duplicate registered, service type: %q", mod, modSvcType) + } + if modSvcType != newModSvcType { + log.Fatalf("Module %#v already registered, service type is %q.\nSo new module %#v can't be registered, service type is %q", mod, modSvcType, newMod, newModSvcType) + } +} + func _register(version string, mod BaseManagerInterface) { if modules == nil { modules = make(map[string]map[string][]BaseManagerInterface) @@ -156,9 +167,7 @@ func _register(version string, mod BaseManagerInterface) { mods = make([]BaseManagerInterface, 0) } for _, m := range mods { - if m == mod { - log.Fatal("Module already registered", mod) - } + ensureModuleNotRegistered(m, mod) } modtable[mod.KeyString()] = append(mods, mod) } @@ -179,7 +188,7 @@ func _registerJointModule(version string, mod BaseManagerInterface) { } for _, m := range jointMods { if m == jointMod { - log.Fatal("Module already registered", jointMod) + ensureModuleNotRegistered(m, jointMod) } } modtable[jointKey] = append(jointMods, jointMod) From 80571f480f3d4b4dca88954db9836de09d1eb0a3 Mon Sep 17 00:00:00 2001 From: Qiu Jian Date: Wed, 22 Aug 2018 12:36:28 +0800 Subject: [PATCH 3/4] =?UTF-8?q?=E4=BF=AE=E6=AD=A3=EF=BC=9Asimpletoken?= =?UTF-8?q?=E6=B2=A1=E6=9C=89servicecatalog=EF=BC=8C=E5=AF=BC=E8=87=B4?= =?UTF-8?q?=E5=9F=BA=E4=BA=8Esimpletoken=E7=9A=84session=E6=97=A0=E6=B3=95?= =?UTF-8?q?=E8=8E=B7=E5=BE=97service=20URL?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Makefile | 2 +- pkg/mcclient/catalog.go | 6 +++++ pkg/mcclient/mcclient.go | 51 +++++++++++++++++-------------------- pkg/mcclient/session.go | 21 ++++++++++++++- pkg/mcclient/token.go | 6 ++++- pkg/mcclient/token2.go | 20 +++++++++++++-- pkg/mcclient/token3.go | 20 +++++++++++++-- pkg/mcclient/tokensimple.go | 8 ++++++ 8 files changed, 99 insertions(+), 35 deletions(-) create mode 100644 pkg/mcclient/catalog.go diff --git a/Makefile b/Makefile index 6537fbd733..5dadd4f7bb 100644 --- a/Makefile +++ b/Makefile @@ -3,7 +3,7 @@ REPO_PREFIX := yunion.io/x/onecloud VENDOR_PATH := $(REPO_PREFIX)/vendor VERSION_PKG := $(VENDOR_PATH)/yunion.io/x/pkg/util/version -ROOT_DIR := $(shell readlink -f `pwd`) +ROOT_DIR := $(shell pwd -P) BUILD_DIR := $(ROOT_DIR)/_output BIN_DIR := $(BUILD_DIR)/bin BUILD_SCRIPT := $(ROOT_DIR)/build/build.sh diff --git a/pkg/mcclient/catalog.go b/pkg/mcclient/catalog.go new file mode 100644 index 0000000000..1238bf6723 --- /dev/null +++ b/pkg/mcclient/catalog.go @@ -0,0 +1,6 @@ +package mcclient + +type IServiceCatalog interface { + GetServiceURL(service, region, zone, endpointType string) (string, error) + GetServiceURLs(service, region, zone, endpointType string) ([]string, error) +} diff --git a/pkg/mcclient/mcclient.go b/pkg/mcclient/mcclient.go index 4df0561160..45ee26bc1c 100644 --- a/pkg/mcclient/mcclient.go +++ b/pkg/mcclient/mcclient.go @@ -10,36 +10,16 @@ import ( "yunion.io/x/jsonutils" "yunion.io/x/onecloud/pkg/util/httputils" + "yunion.io/x/log" ) -/*const ( - USER_AGENT = "yunioncloud-go/201708" -) - -var ( - red = color.New(color.FgRed, color.Bold).PrintlnFunc() - green = color.New(color.FgGreen, color.Bold).PrintlnFunc() - yellow = color.New(color.FgYellow, color.Bold).PrintlnFunc() - cyan = color.New(color.FgHiCyan, color.Bold).PrintlnFunc() -)*/ - -/*type JSONClientError struct { - Code int - Class string - Details string -} - -func (e *JSONClientError) Error() string { - return fmt.Sprintf("JSONClientError: %s %d %s", e.Details, e.Code, e.Class) -}*/ - type Client struct { authUrl string timeout int debug bool httpconn *http.Client - // serviceCatalog *KeystoneServiceCatalog + serviceCatalog IServiceCatalog } func NewClient(authUrl string, timeout int, debug bool, insecure bool) *Client { @@ -222,7 +202,7 @@ func (this *Client) _authV3(domainName, uname, passwd, projectId, projectName, t if len(tokenId) == 0 { return nil, fmt.Errorf("No X-Subject-Token in header") } - ret, err := UnmarshalV3Token(rbody, tokenId) + ret, err := this.unmarshalV3Token(rbody, tokenId) return ret, err } @@ -245,7 +225,7 @@ func (this *Client) _authV2(uname, passwd, tenantId, tenantName, token string) ( if err != nil { return nil, err } - return UnmarshalV2Token(rbody) + return this.unmarshalV2Token(rbody) } func (this *Client) Authenticate(uname, passwd, domainName, tenantName string) (TokenCredential, error) { @@ -255,16 +235,21 @@ func (this *Client) Authenticate(uname, passwd, domainName, tenantName string) ( return this._authV2(uname, passwd, "", tenantName, "") } -func UnmarshalV3Token(rbody jsonutils.JSONObject, tokenId string) (cred TokenCredential, err error) { +func (this *Client) unmarshalV3Token(rbody jsonutils.JSONObject, tokenId string) (cred TokenCredential, err error) { cred = &TokenCredentialV3{Id: tokenId} err = rbody.Unmarshal(cred) if err != nil { err = fmt.Errorf("Invalid response when unmarshal V3 Token: %v", err) } + cata := cred.GetServiceCatalog() + if cata == nil { + log.Fatalf("No srvice catalog avaiable") + } + this.serviceCatalog = cata return } -func UnmarshalV2Token(rbody jsonutils.JSONObject) (cred TokenCredential, err error) { +func (this *Client) unmarshalV2Token(rbody jsonutils.JSONObject) (cred TokenCredential, err error) { access, err := rbody.Get("access") if err == nil { cred = &TokenCredentialV2{} @@ -272,6 +257,11 @@ func UnmarshalV2Token(rbody jsonutils.JSONObject) (cred TokenCredential, err err if err != nil { err = fmt.Errorf("Invalid response when unmarshal V2 Token: %s", err) } + cata := cred.GetServiceCatalog() + if cata == nil { + log.Fatalf("No srvice catalog avaiable") + } + this.serviceCatalog = cata return } err = fmt.Errorf("Invalid response: no access object") @@ -286,7 +276,7 @@ func (this *Client) verifyV3(adminToken, token string) (TokenCredential, error) if err != nil { return nil, err } - return UnmarshalV3Token(rbody, token) + return this.unmarshalV3Token(rbody, token) } func (this *Client) verifyV2(adminToken, token string) (TokenCredential, error) { @@ -297,7 +287,7 @@ func (this *Client) verifyV2(adminToken, token string) (TokenCredential, error) if err != nil { return nil, err } - return UnmarshalV2Token(rbody) + return this.unmarshalV2Token(rbody) } func (this *Client) Verify(adminToken, token string) (cred TokenCredential, err error) { @@ -320,6 +310,11 @@ func (this *Client) SetProject(tenantId, tenantName string, token TokenCredentia } func (this *Client) NewSession(region, zone, endpointType string, token TokenCredential, apiVersion string) *ClientSession { + cata := token.GetServiceCatalog() + if cata == nil { + log.Fatalf("Missing service catalog in token") + } + this.serviceCatalog = cata return &ClientSession{client: this, region: region, zone: zone, endpointType: endpointType, token: token, apiVersion: apiVersion, diff --git a/pkg/mcclient/session.go b/pkg/mcclient/session.go index 3526f6eea4..527e05c4e4 100644 --- a/pkg/mcclient/session.go +++ b/pkg/mcclient/session.go @@ -75,7 +75,26 @@ func (this *ClientSession) GetServiceURL(service, endpointType string) (string, if len(this.apiVersion) > 0 && this.apiVersion != DEFAULT_API_VERSION { service = fmt.Sprintf("%s_%s", service, this.apiVersion) } - return this.token.GetServiceURL(service, this.region, this.zone, endpointType) + url, err := this.token.GetServiceURL(service, this.region, this.zone, endpointType) + if err != nil { + url, err = this.client.serviceCatalog.GetServiceURL(service, this.region, this.zone, endpointType) + } + return url, err +} + +func (this *ClientSession) GetServiceURLs(service, endpointType string) ([]string, error) { + if len(this.endpointType) > 0 { + // session specific endpoint type should override the input endpointType, which is supplied by manager + endpointType = this.endpointType + } + if len(this.apiVersion) > 0 && this.apiVersion != DEFAULT_API_VERSION { + service = fmt.Sprintf("%s_%s", service, this.apiVersion) + } + urls, err := this.token.GetServiceURLs(service, this.region, this.zone, endpointType) + if err != nil { + urls, err = this.client.serviceCatalog.GetServiceURLs(service, this.region, this.zone, endpointType) + } + return urls, err } func (this *ClientSession) getBaseUrl(service, endpointType string) (string, error) { diff --git a/pkg/mcclient/token.go b/pkg/mcclient/token.go index 7a9ba2751b..8c0e5e00ed 100644 --- a/pkg/mcclient/token.go +++ b/pkg/mcclient/token.go @@ -23,6 +23,8 @@ type Endpoint struct { type TokenCredential interface { gotypes.ISerializable + IServiceCatalog + GetTokenString() string GetDomainId() string GetDomainName() string @@ -39,7 +41,9 @@ type TokenCredential interface { IsAdmin() bool IsSystemAdmin() bool GetRegions() []string - GetServiceURL(service, region, zone, endpointType string) (string, error) + + GetServiceCatalog() IServiceCatalog + GetInternalServices(region string) []string GetExternalServices(region string) []ExternalService GetEndpoints(region string, endpointType string) []Endpoint diff --git a/pkg/mcclient/token2.go b/pkg/mcclient/token2.go index d38bd052b9..daca173d17 100644 --- a/pkg/mcclient/token2.go +++ b/pkg/mcclient/token2.go @@ -133,7 +133,11 @@ func (this *TokenCredentialV2) IsSystemAdmin() bool { } func (this *TokenCredentialV2) GetServiceURL(service, region, zone, endpointType string) (string, error) { - return this.ServiceCatalog.getServiceURL(service, region, zone, endpointType) + return this.ServiceCatalog.GetServiceURL(service, region, zone, endpointType) +} + +func (this *TokenCredentialV2) GetServiceURLs(service, region, zone, endpointType string) ([]string, error) { + return this.ServiceCatalog.GetServiceURLs(service, region, zone, endpointType) } func (this *TokenCredentialV2) GetInternalServices(region string) []string { @@ -148,6 +152,10 @@ func (this *TokenCredentialV2) GetEndpoints(region string, endpointType string) return nil } +func (this *TokenCredentialV2) GetServiceCatalog() IServiceCatalog { + return this.ServiceCatalog +} + func stringArrayContains(arr []string, needle string) bool { for i := 0; i < len(arr); i++ { if arr[i] == needle { @@ -211,7 +219,7 @@ func (catalog KeystoneServiceCatalogV2) getServiceEndpoint(service, region, zone return selected, fmt.Errorf("No such service %s", service) } -func (catalog KeystoneServiceCatalogV2) getServiceURL(service, region, zone, endpointType string) (string, error) { +func (catalog KeystoneServiceCatalogV2) GetServiceURL(service, region, zone, endpointType string) (string, error) { ep, err := catalog.getServiceEndpoint(service, region, zone) if err != nil { return "", err @@ -219,6 +227,14 @@ func (catalog KeystoneServiceCatalogV2) getServiceURL(service, region, zone, end return ep.getURL(endpointType), nil } +func (catalog KeystoneServiceCatalogV2) GetServiceURLs(service, region, zone, endpointType string) ([]string, error) { + url, err := catalog.GetServiceURL(service, region, zone, endpointType) + if err != nil { + return nil, err + } + return []string{url}, nil +} + func (ep KeystoneEndpointV2) getURL(epType string) string { switch epType { case "publicURL": diff --git a/pkg/mcclient/token3.go b/pkg/mcclient/token3.go index f7265da44e..09d49b870b 100644 --- a/pkg/mcclient/token3.go +++ b/pkg/mcclient/token3.go @@ -141,7 +141,11 @@ func (this *TokenCredentialV3) GetRegions() []string { } func (this *TokenCredentialV3) GetServiceURL(service, region, zone, endpointType string) (string, error) { - return this.Token.Catalog.getServiceURL(service, region, zone, endpointType) + return this.Token.Catalog.GetServiceURL(service, region, zone, endpointType) +} + +func (this *TokenCredentialV3) GetServiceURLs(service, region, zone, endpointType string) ([]string, error) { + return this.Token.Catalog.GetServiceURLs(service, region, zone, endpointType) } func (this *TokenCredentialV3) GetInternalServices(region string) []string { @@ -156,6 +160,10 @@ func (this *TokenCredentialV3) GetEndpoints(region string, endpointType string) return this.Token.Catalog.getEndpoints(region, endpointType) } +func (this *TokenCredentialV3) GetServiceCatalog() IServiceCatalog { + return this.Token.Catalog +} + func (catalog KeystoneServiceCatalogV3) getInternalServices(region string) []string { services := make([]string, 0) for i := 0; i < len(catalog); i++ { @@ -242,7 +250,15 @@ func Id2RegionZone(id string) (string, string) { } } -func (catalog KeystoneServiceCatalogV3) getServiceURL(service, region, zone, endpointType string) (string, error) { +func (catalog KeystoneServiceCatalogV3) GetServiceURLs(service, region, zone, endpointType string) ([]string, error) { + url, err := catalog.GetServiceURL(service, region, zone, endpointType) + if err != nil { + return nil, err + } + return []string{url}, nil +} + +func (catalog KeystoneServiceCatalogV3) GetServiceURL(service, region, zone, endpointType string) (string, error) { if endpointType == "" { endpointType = "internalURL" } diff --git a/pkg/mcclient/tokensimple.go b/pkg/mcclient/tokensimple.go index 2054560440..5be331fbd9 100644 --- a/pkg/mcclient/tokensimple.go +++ b/pkg/mcclient/tokensimple.go @@ -96,6 +96,10 @@ func (self *SSimpleToken) GetServiceURL(service, region, zone, endpointType stri return "", fmt.Errorf("Not available") } +func (self *SSimpleToken) GetServiceURLs(service, region, zone, endpointType string) ([]string, error) { + return nil, fmt.Errorf("Not available") +} + func (self *SSimpleToken) GetInternalServices(region string) []string { return nil } @@ -108,6 +112,10 @@ func (this *SSimpleToken) GetEndpoints(region string, endpointType string) []End return nil } +func (this *SSimpleToken) GetServiceCatalog() IServiceCatalog { + return nil +} + func SimplifyToken(token TokenCredential) TokenCredential { simToken, ok := token.(*SSimpleToken) if ok { From b77f39d194077c6721d47e9e79da7e8095116afd Mon Sep 17 00:00:00 2001 From: Qiu Jian Date: Wed, 22 Aug 2018 16:11:18 +0800 Subject: [PATCH 4/4] minor updates --- Makefile | 2 +- cmd/climc/climc.go | 29 +++++++++++++++++------------ pkg/mcclient/token3.go | 18 +++++++++--------- 3 files changed, 27 insertions(+), 22 deletions(-) diff --git a/Makefile b/Makefile index 5dadd4f7bb..923270969f 100644 --- a/Makefile +++ b/Makefile @@ -3,7 +3,7 @@ REPO_PREFIX := yunion.io/x/onecloud VENDOR_PATH := $(REPO_PREFIX)/vendor VERSION_PKG := $(VENDOR_PATH)/yunion.io/x/pkg/util/version -ROOT_DIR := $(shell pwd -P) +ROOT_DIR := $(CURDIR) BUILD_DIR := $(ROOT_DIR)/_output BIN_DIR := $(BUILD_DIR)/bin BUILD_SCRIPT := $(ROOT_DIR)/build/build.sh diff --git a/cmd/climc/climc.go b/cmd/climc/climc.go index 1d4bdec473..a8cc818dd9 100644 --- a/cmd/climc/climc.go +++ b/cmd/climc/climc.go @@ -5,28 +5,32 @@ import ( "fmt" "io/ioutil" "os" + "path/filepath" "strings" "time" prompt "github.com/c-bata/go-prompt" + "yunion.io/x/log" "yunion.io/x/pkg/util/version" "yunion.io/x/structarg" "yunion.io/x/onecloud/cmd/climc/promputils" "yunion.io/x/onecloud/cmd/climc/shell" - _ "yunion.io/x/onecloud/cmd/climc/shell/k8s" "yunion.io/x/onecloud/pkg/mcclient" + + _ "yunion.io/x/onecloud/cmd/climc/shell/k8s" ) type BaseOptions struct { - Help bool `help:"Show help" short-token:"h"` - Debug bool `help:"Show debug information"` - Version bool `help:"Show version"` - Timeout int `default:"600" help:"Number of seconds to wait for a response"` - Insecure bool `default:"False" help:"Allow skip server cert verification if URL is https" short-token:"k"` - OsUsername string `default:"$OS_USERNAME" help:"Username, defaults to env[OS_USERNAME]"` - OsPassword string `default:"$OS_PASSWORD" help:"Password, defaults to env[OS_PASSWORD]"` + Help bool `help:"Show help" short-token:"h"` + Debug bool `help:"Show debug information"` + Version bool `help:"Show version"` + Timeout int `default:"600" help:"Number of seconds to wait for a response"` + Insecure bool `default:"false" help:"Allow skip server cert verification if URL is https" short-token:"k"` + NoCachedToken bool `default:"false" help:"Force not use cached token"` + OsUsername string `default:"$OS_USERNAME" help:"Username, defaults to env[OS_USERNAME]"` + OsPassword string `default:"$OS_PASSWORD" help:"Password, defaults to env[OS_PASSWORD]"` // OsProjectId string `default:"$OS_PROJECT_ID" help:"Proejct ID, defaults to env[OS_PROJECT_ID]"` OsProjectName string `default:"$OS_PROJECT_NAME" help:"Project name, defaults to env[OS_PROJECT_NAME]"` OsDomainName string `default:"$OS_DOMAIN_NAME" help:"Domain name, defaults to env[OS_DOMAIN_NAME]"` @@ -122,8 +126,9 @@ func newClientSession(options *BaseOptions) (*mcclient.ClientSession, error) { options.Insecure) var cacheToken mcclient.TokenCredential - cacheFile, err := os.Open("/tmp/OS_AUTH_CACHE_TOKEN") - if err == nil && cacheFile != nil { + tokenCachePath := filepath.Join(os.TempDir(), "OS_AUTH_CACHE_TOKEN") + cacheFile, err := os.Open(tokenCachePath) + if err == nil && cacheFile != nil && !options.NoCachedToken { fileInfo, _ := cacheFile.Stat() dur, err := time.ParseDuration("-24h") if fileInfo != nil && err == nil && fileInfo.ModTime().After(time.Now().Add(dur)) { @@ -153,12 +158,12 @@ func newClientSession(options *BaseOptions) (*mcclient.ClientSession, error) { if err != nil { fmt.Printf("Marshal token error:%s", err) } else { - fo, _ := os.Create("/tmp/OS_AUTH_CACHE_TOKEN") + fo, _ := os.Create(tokenCachePath) fo.Write(bytesCacheToken) fo.Close() } } else { - fmt.Println("******** Use Token Cache At /tmp/OS_AUTH_CACHE_TOKEN ********") + fmt.Printf("******** Use Token Cache At %s ********\n", tokenCachePath) } session := client.NewSession(options.OsRegionName, diff --git a/pkg/mcclient/token3.go b/pkg/mcclient/token3.go index 09d49b870b..233c336be2 100644 --- a/pkg/mcclient/token3.go +++ b/pkg/mcclient/token3.go @@ -250,15 +250,15 @@ func Id2RegionZone(id string) (string, string) { } } -func (catalog KeystoneServiceCatalogV3) GetServiceURLs(service, region, zone, endpointType string) ([]string, error) { - url, err := catalog.GetServiceURL(service, region, zone, endpointType) +func (catalog KeystoneServiceCatalogV3) GetServiceURL(service, region, zone, endpointType string) (string, error) { + urls, err := catalog.GetServiceURLs(service, region, zone, endpointType) if err != nil { - return nil, err + return "", err } - return []string{url}, nil + return urls[rand.Intn(len(urls))], nil } -func (catalog KeystoneServiceCatalogV3) GetServiceURL(service, region, zone, endpointType string) (string, error) { +func (catalog KeystoneServiceCatalogV3) GetServiceURLs(service, region, zone, endpointType string) ([]string, error) { if endpointType == "" { endpointType = "internalURL" } @@ -289,7 +289,7 @@ func (catalog KeystoneServiceCatalogV3) GetServiceURL(service, region, zone, end break } } else { - return "", fmt.Errorf("No default region") + return nil, fmt.Errorf("No default region") } } else { _, ok := regeps[regionzone] @@ -298,14 +298,14 @@ func (catalog KeystoneServiceCatalogV3) GetServiceURL(service, region, zone, end } else { selected, ok = regeps[region] if !ok { - return "", fmt.Errorf("No valid %s endpoints for %s in region %s", endpointType, service, RegionID(region, zone)) + return nil, fmt.Errorf("No valid %s endpoints for %s in region %s", endpointType, service, RegionID(region, zone)) } } } - return selected[rand.Intn(len(selected))], nil + return selected, nil } } - return "", fmt.Errorf("No such service %s", service) + return nil, fmt.Errorf("No such service %s", service) } func (self *TokenCredentialV3) String() string {