github.com/opentelekomcloud/gophertelekomcloud@v0.9.3/openstack/client.go (about)

     1  package openstack
     2  
     3  import (
     4  	"fmt"
     5  	"net/url"
     6  	"reflect"
     7  	"regexp"
     8  	"strings"
     9  
    10  	golangsdk "github.com/opentelekomcloud/gophertelekomcloud"
    11  	"github.com/opentelekomcloud/gophertelekomcloud/openstack/identity/v3/catalog"
    12  	"github.com/opentelekomcloud/gophertelekomcloud/openstack/identity/v3/domains"
    13  	"github.com/opentelekomcloud/gophertelekomcloud/openstack/identity/v3/projects"
    14  	tokens3 "github.com/opentelekomcloud/gophertelekomcloud/openstack/identity/v3/tokens"
    15  	"github.com/opentelekomcloud/gophertelekomcloud/openstack/utils"
    16  	"github.com/opentelekomcloud/gophertelekomcloud/pagination"
    17  )
    18  
    19  const (
    20  	// v3 represents Keystone v3.
    21  	// The version can be anything from v3 to v3.x.
    22  	v3 = "v3"
    23  )
    24  
    25  /*
    26  NewClient prepares an unauthenticated ProviderClient instance.
    27  Most users will probably prefer using the AuthenticatedClient function
    28  instead.
    29  
    30  This is useful if you wish to explicitly control the version of the identity
    31  service that's used for authentication explicitly, for example.
    32  
    33  A basic example of using this would be:
    34  
    35  	ao, err := openstack.AuthOptionsFromEnv()
    36  	provider, err := openstack.NewClient(ao.IdentityEndpoint)
    37  	client, err := openstack.NewIdentityV3(provider, golangsdk.EndpointOpts{})
    38  */
    39  func NewClient(endpoint string) (*golangsdk.ProviderClient, error) {
    40  	u, err := url.Parse(endpoint)
    41  	if err != nil {
    42  		return nil, err
    43  	}
    44  
    45  	u.RawQuery, u.Fragment = "", ""
    46  
    47  	var base string
    48  	versionRe := regexp.MustCompile("v[0-9.]+/?")
    49  	if version := versionRe.FindString(u.Path); version != "" {
    50  		base = strings.Replace(u.String(), version, "", -1)
    51  	} else {
    52  		base = u.String()
    53  	}
    54  
    55  	endpoint = golangsdk.NormalizeURL(endpoint)
    56  	base = golangsdk.NormalizeURL(base)
    57  
    58  	p := new(golangsdk.ProviderClient)
    59  	p.IdentityBase = base
    60  	p.IdentityEndpoint = endpoint
    61  	p.UseTokenLock()
    62  
    63  	return p, nil
    64  }
    65  
    66  /*
    67  AuthenticatedClient logs in to an OpenStack cloud found at the identity endpoint
    68  specified by the options, acquires a token, and returns a Provider Client
    69  instance that's ready to operate.
    70  
    71  If the full path to a versioned identity endpoint was specified  (example:
    72  http://example.com:5000/v3), that path will be used as the endpoint to query.
    73  
    74  If a versionless endpoint was specified (example: http://example.com:5000/),
    75  the endpoint will be queried to determine which versions of the identity service
    76  are available, then chooses the most recent or most supported version.
    77  
    78  Example:
    79  
    80  	ao, err := openstack.AuthOptionsFromEnv()
    81  	provider, err := openstack.AuthenticatedClient(ao)
    82  	client, err := openstack.NewNetworkV2(client, golangsdk.EndpointOpts{
    83  		Region: utils.GetRegion(ao),
    84  	})
    85  */
    86  func AuthenticatedClient(options golangsdk.AuthOptionsProvider) (*golangsdk.ProviderClient, error) {
    87  	client, err := NewClient(options.GetIdentityEndpoint())
    88  	if err != nil {
    89  		return nil, err
    90  	}
    91  
    92  	if err := Authenticate(client, options); err != nil {
    93  		return nil, err
    94  	}
    95  	return client, nil
    96  }
    97  
    98  // Authenticate or re-authenticate against the most recent identity service
    99  // supported at the provided endpoint.
   100  func Authenticate(client *golangsdk.ProviderClient, options golangsdk.AuthOptionsProvider) error {
   101  	versions := []*utils.Version{
   102  		{ID: v3, Priority: 30, Suffix: "/v3/"},
   103  	}
   104  
   105  	chosen, endpoint, err := utils.ChooseVersion(client, versions)
   106  	if err != nil {
   107  		return err
   108  	}
   109  
   110  	authOptions, isTokenAuthOptions := options.(golangsdk.AuthOptions)
   111  
   112  	if isTokenAuthOptions {
   113  		switch chosen.ID {
   114  		case v3:
   115  			if authOptions.AgencyDomainName != "" && authOptions.AgencyName != "" {
   116  				return v3authWithAgency(client, endpoint, &authOptions, golangsdk.EndpointOpts{})
   117  			}
   118  			return v3auth(client, endpoint, &authOptions, golangsdk.EndpointOpts{})
   119  		default:
   120  			// The switch statement must be out of date from the versions list.
   121  			return fmt.Errorf("unrecognized identity version: %s", chosen.ID)
   122  		}
   123  	} else {
   124  		akskAuthOptions, isAkSkOptions := options.(golangsdk.AKSKAuthOptions)
   125  
   126  		if isAkSkOptions {
   127  			if akskAuthOptions.AgencyDomainName != "" && akskAuthOptions.AgencyName != "" {
   128  				return authWithAgencyByAKSK(client, endpoint, akskAuthOptions, golangsdk.EndpointOpts{})
   129  			}
   130  			return v3AKSKAuth(client, endpoint, akskAuthOptions, golangsdk.EndpointOpts{})
   131  
   132  		}
   133  		return fmt.Errorf("unrecognized auth options provider: %s", reflect.TypeOf(options))
   134  	}
   135  }
   136  
   137  // AuthenticateV3 explicitly authenticates against the identity v3 service.
   138  func AuthenticateV3(client *golangsdk.ProviderClient, options tokens3.AuthOptionsBuilder, eo golangsdk.EndpointOpts) error {
   139  	return v3auth(client, "", options, eo)
   140  }
   141  
   142  type token3Result interface {
   143  	Extract() (*tokens3.Token, error)
   144  	ExtractToken() (*tokens3.Token, error)
   145  	ExtractServiceCatalog() (*tokens3.ServiceCatalog, error)
   146  	ExtractUser() (*tokens3.User, error)
   147  	ExtractRoles() ([]tokens3.Role, error)
   148  	ExtractProject() (*tokens3.Project, error)
   149  }
   150  
   151  func v3auth(client *golangsdk.ProviderClient, endpoint string, opts tokens3.AuthOptionsBuilder, eo golangsdk.EndpointOpts) error {
   152  	// Override the generated service endpoint with the one returned by the version endpoint.
   153  	v3Client, err := NewIdentityV3(client, eo)
   154  	if err != nil {
   155  		return err
   156  	}
   157  
   158  	if endpoint != "" {
   159  		v3Client.Endpoint = endpoint
   160  	}
   161  
   162  	var result token3Result
   163  
   164  	if opts.AuthTokenID() != "" { // TODO: Check token validity with Token-By-Token
   165  		v3Client.SetToken(opts.AuthTokenID())
   166  		result = tokens3.Get(v3Client, opts.AuthTokenID())
   167  	} else {
   168  		result = tokens3.Create(v3Client, opts)
   169  	}
   170  
   171  	token, err := result.ExtractToken()
   172  	if err != nil {
   173  		return fmt.Errorf("error extracting token: %w", err)
   174  	}
   175  
   176  	project, err := result.ExtractProject()
   177  	if err != nil {
   178  		return fmt.Errorf("error extracting project info: %w", err)
   179  	}
   180  
   181  	user, err := result.ExtractUser()
   182  	if err != nil {
   183  		return fmt.Errorf("error extracting user info: %w", err)
   184  	}
   185  
   186  	serviceCatalog, err := result.ExtractServiceCatalog()
   187  	if err != nil {
   188  		return fmt.Errorf("error extracting service catalog info: %s", err)
   189  	}
   190  
   191  	client.TokenID = token.ID
   192  	if project != nil {
   193  		client.ProjectID = project.ID
   194  		client.DomainID = project.Domain.ID
   195  	}
   196  	if user != nil {
   197  		client.UserID = user.ID
   198  		client.DomainID = user.Domain.ID
   199  	}
   200  
   201  	if opts.CanReauth() {
   202  		client.ReauthFunc = func() error {
   203  			client.TokenID = ""
   204  			return v3auth(client, endpoint, opts, eo)
   205  		}
   206  	}
   207  
   208  	clientRegion := ""
   209  	if aOpts, ok := opts.(*golangsdk.AuthOptions); ok {
   210  		if aOpts.TenantName == "" && project != nil {
   211  			aOpts.TenantName = project.Name
   212  		}
   213  		clientRegion = utils.GetRegion(*aOpts)
   214  	}
   215  	client.RegionID = clientRegion
   216  
   217  	client.EndpointLocator = func(opts golangsdk.EndpointOpts) (string, error) {
   218  		// use client region as default one
   219  		if opts.Region == "" && clientRegion != "" {
   220  			opts.Region = clientRegion
   221  		}
   222  		return V3EndpointURL(serviceCatalog, opts)
   223  	}
   224  
   225  	return nil
   226  }
   227  
   228  func v3authWithAgency(client *golangsdk.ProviderClient, endpoint string, opts *golangsdk.AuthOptions, eo golangsdk.EndpointOpts) error {
   229  	if opts.TokenID == "" {
   230  		err := v3auth(client, endpoint, opts, eo)
   231  		if err != nil {
   232  			return err
   233  		}
   234  	} else {
   235  		client.TokenID = opts.TokenID
   236  	}
   237  
   238  	opts1 := golangsdk.AgencyAuthOptions{
   239  		AgencyName:       opts.AgencyName,
   240  		AgencyDomainName: opts.AgencyDomainName,
   241  		DelegatedProject: opts.DelegatedProject,
   242  	}
   243  
   244  	return v3auth(client, endpoint, &opts1, eo)
   245  }
   246  
   247  func getProjectID(client *golangsdk.ServiceClient, name string) (string, error) {
   248  	opts := projects.ListOpts{
   249  		Name: name,
   250  	}
   251  	allPages, err := projects.List(client, opts).AllPages()
   252  	if err != nil {
   253  		return "", err
   254  	}
   255  
   256  	extractProjects, err := projects.ExtractProjects(allPages)
   257  
   258  	if err != nil {
   259  		return "", err
   260  	}
   261  
   262  	if len(extractProjects) < 1 {
   263  		return "", fmt.Errorf("[DEBUG] cannot find the tenant: %s", name)
   264  	}
   265  
   266  	return extractProjects[0].ID, nil
   267  }
   268  
   269  func v3AKSKAuth(client *golangsdk.ProviderClient, endpoint string, options golangsdk.AKSKAuthOptions, eo golangsdk.EndpointOpts) error {
   270  	v3Client, err := NewIdentityV3(client, eo)
   271  	if err != nil {
   272  		return err
   273  	}
   274  
   275  	// Override the generated service endpoint with the one returned by the version endpoint.
   276  	if endpoint != "" {
   277  		v3Client.Endpoint = endpoint
   278  	}
   279  
   280  	// update AKSKAuthOptions of ProviderClient
   281  	// ProviderClient(client) is a reference to the ServiceClient(v3Client)
   282  	defer func() {
   283  		client.AKSKAuthOptions.ProjectId = options.ProjectId
   284  		client.AKSKAuthOptions.DomainID = options.DomainID
   285  	}()
   286  
   287  	client.AKSKAuthOptions = options
   288  	client.AKSKAuthOptions.DomainID = ""
   289  
   290  	if options.ProjectId == "" && options.ProjectName != "" {
   291  		id, err := getProjectID(v3Client, options.ProjectName)
   292  		if err != nil {
   293  			return err
   294  		}
   295  		options.ProjectId = id
   296  		client.AKSKAuthOptions.ProjectId = options.ProjectId
   297  	}
   298  
   299  	if options.DomainID == "" && options.Domain != "" {
   300  		id, err := getDomainID(options.Domain, v3Client)
   301  		if err != nil {
   302  			options.DomainID = ""
   303  		} else {
   304  			options.DomainID = id
   305  		}
   306  	}
   307  
   308  	if options.BssDomainID == "" && options.BssDomain != "" {
   309  		id, err := getDomainID(options.BssDomain, v3Client)
   310  		if err != nil {
   311  			options.BssDomainID = ""
   312  		} else {
   313  			options.BssDomainID = id
   314  		}
   315  	}
   316  
   317  	client.ProjectID = options.ProjectId
   318  	client.DomainID = options.BssDomainID
   319  
   320  	var entries = make([]tokens3.CatalogEntry, 0, 1)
   321  	err = catalog.List(v3Client).EachPage(func(page pagination.Page) (bool, error) {
   322  		catalogList, err := catalog.ExtractServiceCatalog(page)
   323  		if err != nil {
   324  			return false, err
   325  		}
   326  
   327  		entries = append(entries, catalogList...)
   328  
   329  		return true, nil
   330  	})
   331  
   332  	if err != nil {
   333  		return err
   334  	}
   335  	clientRegion := utils.GetRegionFromAKSK(options)
   336  	client.RegionID = clientRegion
   337  
   338  	client.EndpointLocator = func(opts golangsdk.EndpointOpts) (string, error) {
   339  		return V3EndpointURL(&tokens3.ServiceCatalog{
   340  			Entries: entries,
   341  		}, opts)
   342  	}
   343  	return nil
   344  }
   345  
   346  func authWithAgencyByAKSK(client *golangsdk.ProviderClient, endpoint string, opts golangsdk.AKSKAuthOptions, eo golangsdk.EndpointOpts) error {
   347  	err := v3AKSKAuth(client, endpoint, opts, eo)
   348  	if err != nil {
   349  		return err
   350  	}
   351  
   352  	v3Client, err := NewIdentityV3(client, eo)
   353  	if err != nil {
   354  		return err
   355  	}
   356  
   357  	if v3Client.AKSKAuthOptions.DomainID == "" {
   358  		return fmt.Errorf("must config domain name")
   359  	}
   360  
   361  	opts2 := golangsdk.AgencyAuthOptions{
   362  		AgencyName:       opts.AgencyName,
   363  		AgencyDomainName: opts.AgencyDomainName,
   364  		DelegatedProject: opts.DelegatedProject,
   365  	}
   366  	result := tokens3.Create(v3Client, &opts2)
   367  	token, err := result.ExtractToken()
   368  	if err != nil {
   369  		return err
   370  	}
   371  
   372  	project, err := result.ExtractProject()
   373  	if err != nil {
   374  		return fmt.Errorf("error extracting project info: %s", err)
   375  	}
   376  
   377  	user, err := result.ExtractUser()
   378  	if err != nil {
   379  		return fmt.Errorf("error extracting user info: %s", err)
   380  	}
   381  
   382  	serviceCatalog, err := result.ExtractServiceCatalog()
   383  	if err != nil {
   384  		return err
   385  	}
   386  
   387  	client.TokenID = token.ID
   388  	if project != nil {
   389  		client.ProjectID = project.ID
   390  	}
   391  	if user != nil {
   392  		client.UserID = user.ID
   393  	}
   394  
   395  	client.ReauthFunc = func() error {
   396  		client.TokenID = ""
   397  		return authWithAgencyByAKSK(client, endpoint, opts, eo)
   398  	}
   399  
   400  	client.EndpointLocator = func(opts golangsdk.EndpointOpts) (string, error) {
   401  		return V3EndpointURL(serviceCatalog, opts)
   402  	}
   403  
   404  	client.AKSKAuthOptions.AccessKey = ""
   405  	return nil
   406  }
   407  
   408  func getDomainID(name string, client *golangsdk.ServiceClient) (string, error) {
   409  	old := client.Endpoint
   410  	defer func() { client.Endpoint = old }()
   411  
   412  	client.Endpoint = old + "auth/"
   413  
   414  	opts := domains.ListOpts{
   415  		Name: name,
   416  	}
   417  	allPages, err := domains.List(client, &opts).AllPages()
   418  	if err != nil {
   419  		return "", fmt.Errorf("list domains failed, err=%s", err)
   420  	}
   421  
   422  	all, err := domains.ExtractDomains(allPages)
   423  	if err != nil {
   424  		return "", fmt.Errorf("extract domains failed, err=%s", err)
   425  	}
   426  
   427  	count := len(all)
   428  	switch count {
   429  	case 0:
   430  		err := &golangsdk.ErrResourceNotFound{}
   431  		err.ResourceType = "iam"
   432  		err.Name = name
   433  		return "", err
   434  	case 1:
   435  		return all[0].ID, nil
   436  	default:
   437  		err := &golangsdk.ErrMultipleResourcesFound{}
   438  		err.ResourceType = "iam"
   439  		err.Name = name
   440  		err.Count = count
   441  		return "", err
   442  	}
   443  }
   444  
   445  // NewIdentityV3 creates a ServiceClient that may be used to access the v3
   446  // identity service.
   447  func NewIdentityV3(client *golangsdk.ProviderClient, eo golangsdk.EndpointOpts) (*golangsdk.ServiceClient, error) {
   448  	endpoint := client.IdentityBase + "v3/"
   449  	clientType := "identity"
   450  	var err error
   451  	if !reflect.DeepEqual(eo, golangsdk.EndpointOpts{}) {
   452  		eo.ApplyDefaults(clientType)
   453  		endpoint, err = client.EndpointLocator(eo)
   454  		if err != nil {
   455  			return nil, err
   456  		}
   457  	}
   458  
   459  	// Ensure endpoint still has a suffix of v3.
   460  	// This is because EndpointLocator might have found a versionless
   461  	// endpoint and requests will fail unless targeted at /v3.
   462  	if !strings.HasSuffix(endpoint, "v3/") {
   463  		endpoint = endpoint + "v3/"
   464  	}
   465  
   466  	return &golangsdk.ServiceClient{
   467  		ProviderClient: client,
   468  		Endpoint:       endpoint,
   469  		Type:           clientType,
   470  	}, nil
   471  }
   472  
   473  func initClientOpts(client *golangsdk.ProviderClient, eo golangsdk.EndpointOpts, clientType string) (*golangsdk.ServiceClient, error) {
   474  	sc := new(golangsdk.ServiceClient)
   475  	eo.ApplyDefaults(clientType)
   476  	locator, err := client.EndpointLocator(eo)
   477  	if err != nil {
   478  		return sc, err
   479  	}
   480  	sc.ProviderClient = client
   481  	sc.Endpoint = locator
   482  	sc.Type = clientType
   483  	return sc, nil
   484  }
   485  
   486  // initCommonServiceClient is a workaround for services missing from the catalog.
   487  // Firstly, we initialize a service client by "volumev2" type, the endpoint likes https://evs.{region}.{xxx.com}/v2/{project_id}
   488  // then we replace the endpoint with the specified srv and version.
   489  func initCommonServiceClient(client *golangsdk.ProviderClient, eo golangsdk.EndpointOpts, srv string, version string) (*golangsdk.ServiceClient, error) {
   490  	sc, err := initClientOpts(client, eo, "volumev2")
   491  	if err != nil {
   492  		return nil, err
   493  	}
   494  
   495  	e := strings.Replace(sc.Endpoint, "v2", version, 1)
   496  	sc.Endpoint = strings.Replace(e, "evs", srv, 1)
   497  	sc.ResourceBase = sc.Endpoint
   498  	return sc, err
   499  }
   500  
   501  // NewComputeV2 creates a ServiceClient that may be used with the v2 compute
   502  // package.
   503  func NewComputeV2(client *golangsdk.ProviderClient, eo golangsdk.EndpointOpts) (*golangsdk.ServiceClient, error) {
   504  	return initClientOpts(client, eo, "compute")
   505  }
   506  
   507  // NewNetworkV2 creates a ServiceClient that may be used with the v2 network
   508  // package.
   509  func NewNetworkV2(client *golangsdk.ProviderClient, eo golangsdk.EndpointOpts) (*golangsdk.ServiceClient, error) {
   510  	sc, err := initClientOpts(client, eo, "network")
   511  	if err != nil {
   512  		return nil, err
   513  	}
   514  	sc.ResourceBase = sc.Endpoint + "v2.0/"
   515  	return sc, err
   516  }
   517  
   518  // NewBlockStorageV1 creates a ServiceClient that may be used to access the v1
   519  // block storage service.
   520  func NewBlockStorageV1(client *golangsdk.ProviderClient, eo golangsdk.EndpointOpts) (*golangsdk.ServiceClient, error) {
   521  	return initClientOpts(client, eo, "volume")
   522  }
   523  
   524  // NewBlockStorageV2 creates a ServiceClient that may be used to access the v2
   525  // block storage service.
   526  func NewBlockStorageV2(client *golangsdk.ProviderClient, eo golangsdk.EndpointOpts) (*golangsdk.ServiceClient, error) {
   527  	return initClientOpts(client, eo, "volumev2")
   528  }
   529  
   530  // NewBlockStorageV3 creates a ServiceClient that may be used to access the v3 block storage service.
   531  func NewBlockStorageV3(client *golangsdk.ProviderClient, eo golangsdk.EndpointOpts) (*golangsdk.ServiceClient, error) {
   532  	return initClientOpts(client, eo, "volumev3")
   533  }
   534  
   535  // NewSharedFileSystemV2 creates a ServiceClient that may be used to access the v2 shared file system service.
   536  func NewSharedFileSystemV2(client *golangsdk.ProviderClient, eo golangsdk.EndpointOpts) (*golangsdk.ServiceClient, error) {
   537  	return initClientOpts(client, eo, "sharev2")
   538  }
   539  
   540  // NewSharedFileSystemTurboV1 creates a ServiceClient that may be used to access the v2 shared file system turbo service.
   541  func NewSharedFileSystemTurboV1(client *golangsdk.ProviderClient, eo golangsdk.EndpointOpts) (*golangsdk.ServiceClient, error) {
   542  	return initClientOpts(client, eo, "sfsturbo")
   543  }
   544  
   545  // NewOrchestrationV1 creates a ServiceClient that may be used to access the v1
   546  // orchestration service.
   547  func NewOrchestrationV1(client *golangsdk.ProviderClient, eo golangsdk.EndpointOpts) (*golangsdk.ServiceClient, error) {
   548  	return initClientOpts(client, eo, "orchestration")
   549  }
   550  
   551  // NewDNSV2 creates a ServiceClient that may be used to access the v2 DNS
   552  // service.
   553  func NewDNSV2(client *golangsdk.ProviderClient, eo golangsdk.EndpointOpts) (*golangsdk.ServiceClient, error) {
   554  	sc, err := initClientOpts(client, eo, "dns")
   555  	if err != nil {
   556  		return nil, err
   557  	}
   558  	sc.ResourceBase = sc.Endpoint + "v2/"
   559  	return sc, err
   560  }
   561  
   562  func NewDWSV1(client *golangsdk.ProviderClient, eo golangsdk.EndpointOpts) (*golangsdk.ServiceClient, error) {
   563  	sc, err := initClientOpts(client, eo, "dws")
   564  	if err != nil {
   565  		return nil, err
   566  	}
   567  	sc.ResourceBase = sc.Endpoint + "v1.0/" + client.ProjectID + "/"
   568  	return sc, err
   569  }
   570  
   571  func NewIMSV1(client *golangsdk.ProviderClient, eo golangsdk.EndpointOpts) (*golangsdk.ServiceClient, error) {
   572  	sc, err := initClientOpts(client, eo, "image")
   573  	if err != nil {
   574  		return nil, err
   575  	}
   576  	sc.ResourceBase = sc.Endpoint + "v1/"
   577  	return sc, err
   578  }
   579  
   580  func NewIMSV2(client *golangsdk.ProviderClient, eo golangsdk.EndpointOpts) (*golangsdk.ServiceClient, error) {
   581  	sc, err := initClientOpts(client, eo, "image")
   582  	if err != nil {
   583  		return nil, err
   584  	}
   585  	sc.ResourceBase = sc.Endpoint + "v2/"
   586  	return sc, err
   587  }
   588  
   589  // NewOtcV1 creates a ServiceClient that may be used with the v1 network package.
   590  func NewElbV1(client *golangsdk.ProviderClient, eo golangsdk.EndpointOpts, otctype string) (*golangsdk.ServiceClient, error) {
   591  	sc, err := initClientOpts(client, eo, "compute")
   592  	if err != nil {
   593  		return nil, err
   594  	}
   595  	sc.Endpoint = strings.Replace(strings.Replace(sc.Endpoint, "ecs", otctype, 1), "/v2/", "/v1.0/", 1)
   596  	sc.ResourceBase = sc.Endpoint
   597  	sc.Type = otctype
   598  	return sc, err
   599  }
   600  
   601  func NewCESClient(client *golangsdk.ProviderClient, eo golangsdk.EndpointOpts) (*golangsdk.ServiceClient, error) {
   602  	sc, err := initClientOpts(client, eo, "volumev2")
   603  	if err != nil {
   604  		return nil, err
   605  	}
   606  	e := strings.Replace(sc.Endpoint, "v2", "V1.0", 1)
   607  	sc.Endpoint = strings.Replace(e, "evs", "ces", 1)
   608  	sc.ResourceBase = sc.Endpoint
   609  	return sc, err
   610  }
   611  
   612  // NewComputeV1 creates a ServiceClient that may be used with the v1 compute
   613  // package.
   614  func NewComputeV1(client *golangsdk.ProviderClient, eo golangsdk.EndpointOpts) (*golangsdk.ServiceClient, error) {
   615  	return initClientOpts(client, eo, "ecs")
   616  }
   617  
   618  func NewRdsTagV1(client *golangsdk.ProviderClient, eo golangsdk.EndpointOpts) (*golangsdk.ServiceClient, error) {
   619  	sc, err := initClientOpts(client, eo, "network")
   620  	if err != nil {
   621  		return nil, err
   622  	}
   623  	sc.Endpoint = strings.Replace(sc.Endpoint, "vpc", "rds", 1)
   624  	sc.Endpoint = sc.Endpoint + "v1/"
   625  	sc.ResourceBase = sc.Endpoint + client.ProjectID + "/rds/"
   626  	return sc, err
   627  }
   628  
   629  // NewAutoScalingV1 creates a ServiceClient that may be used to access the
   630  // auto-scaling service of OpenTelekomCloud public cloud
   631  func NewAutoScalingV1(client *golangsdk.ProviderClient, eo golangsdk.EndpointOpts) (*golangsdk.ServiceClient, error) {
   632  	return initClientOpts(client, eo, "asv1")
   633  }
   634  
   635  // NewAutoScalingV2 creates a ServiceClient that may be used to access the
   636  // auto-scaling service of OpenTelekomCloud public cloud
   637  func NewAutoScalingV2(client *golangsdk.ProviderClient, eo golangsdk.EndpointOpts) (*golangsdk.ServiceClient, error) {
   638  	sc, err := initClientOpts(client, eo, "asv1")
   639  	if err != nil {
   640  		return nil, err
   641  	}
   642  	sc.Endpoint = strings.Replace(sc.Endpoint, "v1", "v2", 1)
   643  	return sc, err
   644  }
   645  
   646  // NewNetworkV1 creates a ServiceClient that may be used with the v1 network
   647  // package.
   648  func NewNetworkV1(client *golangsdk.ProviderClient, eo golangsdk.EndpointOpts) (*golangsdk.ServiceClient, error) {
   649  	sc, err := initClientOpts(client, eo, "network")
   650  	if err != nil {
   651  		return nil, err
   652  	}
   653  	sc.ResourceBase = sc.Endpoint + "v1/"
   654  	return sc, err
   655  }
   656  
   657  // NewVpcEpV1 creates a ServiceClient that may be used with the v1 VPC Endpoint service
   658  func NewVpcEpV1(client *golangsdk.ProviderClient, eo golangsdk.EndpointOpts) (*golangsdk.ServiceClient, error) {
   659  	sc, err := initClientOpts(client, eo, "vpcep")
   660  	if err != nil {
   661  		return nil, err
   662  	}
   663  	return sc, err
   664  }
   665  
   666  // NewVpcV3 creates a ServiceClient that may be used with the v3 VPC service
   667  func NewVpcV3(client *golangsdk.ProviderClient, eo golangsdk.EndpointOpts) (*golangsdk.ServiceClient, error) {
   668  	sc, err := initClientOpts(client, eo, "vpc")
   669  	if err != nil {
   670  		return nil, err
   671  	}
   672  	sc.Endpoint = strings.Replace(sc.Endpoint, "v1", "v3", 1)
   673  	sc.ResourceBase = sc.Endpoint + "vpc/"
   674  	return sc, err
   675  }
   676  
   677  // NewNatV2 creates a ServiceClient that may be used with the v2 nat package.
   678  func NewNatV2(client *golangsdk.ProviderClient, eo golangsdk.EndpointOpts) (*golangsdk.ServiceClient, error) {
   679  	return initClientOpts(client, eo, "nat")
   680  }
   681  
   682  // NewMapReduceV1 creates a ServiceClient that may be used with the v1 MapReduce service.
   683  func NewMapReduceV1(client *golangsdk.ProviderClient, eo golangsdk.EndpointOpts) (*golangsdk.ServiceClient, error) {
   684  	sc, err := initClientOpts(client, eo, "mrs")
   685  	if err != nil {
   686  		return nil, err
   687  	}
   688  	sc.ResourceBase = sc.Endpoint + client.ProjectID + "/"
   689  	return sc, err
   690  }
   691  
   692  // NewAntiDDoSV1 creates a ServiceClient that may be used with the v1 Anti DDoS Service
   693  // package.
   694  func NewAntiDDoSV1(client *golangsdk.ProviderClient, eo golangsdk.EndpointOpts) (*golangsdk.ServiceClient, error) {
   695  	return initClientOpts(client, eo, "antiddos")
   696  }
   697  
   698  // NewDCaaSV2 creates a ServiceClient that may be used to access the v1 Distributed Message Service.
   699  func NewDCaaSV2(client *golangsdk.ProviderClient, eo golangsdk.EndpointOpts) (*golangsdk.ServiceClient, error) {
   700  	return initClientOpts(client, eo, "dcaas")
   701  }
   702  
   703  // NewDMSServiceV1 creates a ServiceClient that may be used to access the v1 Distributed Message Service.
   704  func NewDMSServiceV1(client *golangsdk.ProviderClient, eo golangsdk.EndpointOpts) (*golangsdk.ServiceClient, error) {
   705  	return initClientOpts(client, eo, "dmsv1")
   706  }
   707  
   708  func NewDMSServiceV11(client *golangsdk.ProviderClient, eo golangsdk.EndpointOpts) (*golangsdk.ServiceClient, error) {
   709  	sc, err := initClientOpts(client, eo, "dmsv1")
   710  	if err != nil {
   711  		return nil, err
   712  	}
   713  	sc.Endpoint = strings.Replace(sc.Endpoint, "v1.0", "v1", 1)
   714  	return sc, err
   715  }
   716  
   717  // NewDMSServiceV2 creates a ServiceClient that may be used to access the v2 Distributed Message Service.
   718  func NewDMSServiceV2(client *golangsdk.ProviderClient, eo golangsdk.EndpointOpts) (*golangsdk.ServiceClient, error) {
   719  	return initClientOpts(client, eo, "dmsv2")
   720  }
   721  
   722  // NewDCSServiceV1 creates a ServiceClient that may be used to access the v1 Distributed Cache Service.
   723  func NewDCSServiceV1(client *golangsdk.ProviderClient, eo golangsdk.EndpointOpts) (*golangsdk.ServiceClient, error) {
   724  	sc, err := initClientOpts(client, eo, "network")
   725  	if err != nil {
   726  		return nil, err
   727  	}
   728  	sc.Endpoint = strings.Replace(sc.Endpoint, "vpc", "dcs", 1)
   729  	sc.ResourceBase = sc.Endpoint + "v1.0/" + client.ProjectID + "/"
   730  	return sc, err
   731  }
   732  
   733  // NewDDSServiceV3 creates a ServiceClient that may be used to access the Document Database Service.
   734  func NewDDSServiceV3(client *golangsdk.ProviderClient, eo golangsdk.EndpointOpts) (*golangsdk.ServiceClient, error) {
   735  	sc, err := initClientOpts(client, eo, "ddsv3")
   736  	return sc, err
   737  }
   738  
   739  func NewDISServiceV2(client *golangsdk.ProviderClient, eo golangsdk.EndpointOpts) (*golangsdk.ServiceClient, error) {
   740  	sc, err := initClientOpts(client, eo, "dis")
   741  	if err != nil {
   742  		return nil, err
   743  	}
   744  	sc.ResourceBase = sc.Endpoint + "v2/" + client.ProjectID + "/"
   745  	return sc, err
   746  }
   747  
   748  // NewDRSServiceV3 creates a ServiceClient that may be used to access the Document Database Service.
   749  func NewDRSServiceV3(client *golangsdk.ProviderClient, eo golangsdk.EndpointOpts) (*golangsdk.ServiceClient, error) {
   750  	sc, err := initCommonServiceClient(client, eo, "drs", "v3")
   751  	if err != nil {
   752  		return nil, err
   753  	}
   754  	return sc, nil
   755  }
   756  
   757  // NewOBSService creates a ServiceClient that may be used to access the Object Storage Service.
   758  func NewOBSService(client *golangsdk.ProviderClient, eo golangsdk.EndpointOpts) (*golangsdk.ServiceClient, error) {
   759  	sc, err := initClientOpts(client, eo, "object")
   760  	return sc, err
   761  }
   762  
   763  // NewDeHServiceV1 creates a ServiceClient that may be used to access the v1 Dedicated Hosts service.
   764  func NewDeHServiceV1(client *golangsdk.ProviderClient, eo golangsdk.EndpointOpts) (*golangsdk.ServiceClient, error) {
   765  	sc, err := initClientOpts(client, eo, "deh")
   766  	return sc, err
   767  }
   768  
   769  // NewCSBSService creates a ServiceClient that can be used to access the Cloud Server Backup service.
   770  func NewCSBSService(client *golangsdk.ProviderClient, eo golangsdk.EndpointOpts) (*golangsdk.ServiceClient, error) {
   771  	sc, err := initClientOpts(client, eo, "data-protect")
   772  	return sc, err
   773  }
   774  
   775  // NewCBRService create a ServiceClient that can be used to access the Cloud Backup and Recovery service.
   776  func NewCBRService(client *golangsdk.ProviderClient, eo golangsdk.EndpointOpts) (*golangsdk.ServiceClient, error) {
   777  	return initClientOpts(client, eo, "cbr")
   778  }
   779  
   780  // NewCSSService creates a ServiceClient that can be used to access the Cloud Search service.
   781  func NewCSSService(client *golangsdk.ProviderClient, eo golangsdk.EndpointOpts) (*golangsdk.ServiceClient, error) {
   782  	return initClientOpts(client, eo, "css")
   783  }
   784  
   785  // NewVBS creates a service client that is used for VBS.
   786  func NewVBS(client *golangsdk.ProviderClient, eo golangsdk.EndpointOpts) (*golangsdk.ServiceClient, error) {
   787  	sc, err := initClientOpts(client, eo, "volumev2")
   788  	if err != nil {
   789  		return nil, err
   790  	}
   791  	sc.Endpoint = strings.Replace(sc.Endpoint, "evs", "vbs", 1)
   792  	sc.ResourceBase = sc.Endpoint
   793  	return sc, err
   794  }
   795  
   796  func NewVBSServiceV2(client *golangsdk.ProviderClient, eo golangsdk.EndpointOpts) (*golangsdk.ServiceClient, error) {
   797  	return initClientOpts(client, eo, "vbsv2")
   798  }
   799  
   800  // NewCTSV1 creates a ServiceClient that can be used to access the Cloud Trace service.
   801  func NewCTSV1(client *golangsdk.ProviderClient, eo golangsdk.EndpointOpts) (*golangsdk.ServiceClient, error) {
   802  	sc, err := initClientOpts(client, eo, "cts")
   803  	return sc, err
   804  }
   805  
   806  // NewCTSV2 creates a ServiceClient that can be used to access the Cloud Trace service.
   807  func NewCTSV2(client *golangsdk.ProviderClient, eo golangsdk.EndpointOpts) (*golangsdk.ServiceClient, error) {
   808  	sc, err := initClientOpts(client, eo, "cts")
   809  	sc.Endpoint = strings.Replace(sc.Endpoint, "v1", "v2", 1)
   810  	return sc, err
   811  }
   812  
   813  // NewCTSV3 creates a ServiceClient that can be used to access the Cloud Trace service.
   814  func NewCTSV3(client *golangsdk.ProviderClient, eo golangsdk.EndpointOpts) (*golangsdk.ServiceClient, error) {
   815  	sc, err := initClientOpts(client, eo, "cts")
   816  	sc.Endpoint = strings.Replace(sc.Endpoint, "v1.0", "v3", 1)
   817  	return sc, err
   818  }
   819  
   820  // NewELBV1 creates a ServiceClient that may be used to access the ELB service.
   821  func NewELBV1(client *golangsdk.ProviderClient, eo golangsdk.EndpointOpts) (*golangsdk.ServiceClient, error) {
   822  	sc, err := initClientOpts(client, eo, "elbv1")
   823  	return sc, err
   824  }
   825  
   826  // NewELBV2 creates a ServiceClient that may be used to access the ELBv2 service.
   827  func NewELBV2(client *golangsdk.ProviderClient, eo golangsdk.EndpointOpts) (*golangsdk.ServiceClient, error) {
   828  	sc, err := initClientOpts(client, eo, "elb")
   829  	if suf := "v1.0/"; strings.HasSuffix(sc.Endpoint, suf) {
   830  		sc.Endpoint = strings.TrimSuffix(sc.Endpoint, suf)
   831  	}
   832  	sc.ResourceBase = sc.Endpoint + "v2.0/"
   833  	return sc, err
   834  }
   835  
   836  // NewELBV3 creates a ServiceClient that may be used to access the ELBv3 service.
   837  func NewELBV3(client *golangsdk.ProviderClient, eo golangsdk.EndpointOpts) (*golangsdk.ServiceClient, error) {
   838  	sc, err := initClientOpts(client, eo, "elbv3")
   839  	if err != nil {
   840  		return nil, err
   841  	}
   842  	sc.ResourceBase = sc.Endpoint + "elb/"
   843  	return sc, nil
   844  }
   845  
   846  // NewRDSV1 creates a ServiceClient that may be used to access the RDS service.
   847  func NewRDSV1(client *golangsdk.ProviderClient, eo golangsdk.EndpointOpts) (*golangsdk.ServiceClient, error) {
   848  	sc, err := initClientOpts(client, eo, "rdsv1")
   849  	return sc, err
   850  }
   851  
   852  // NewKMSV1 creates a ServiceClient that may be used to access the KMS service.
   853  func NewKMSV1(client *golangsdk.ProviderClient, eo golangsdk.EndpointOpts) (*golangsdk.ServiceClient, error) {
   854  	return initClientOpts(client, eo, "kmsv1")
   855  }
   856  
   857  // NewSMNV2 creates a ServiceClient that may be used to access the SMN service.
   858  func NewSMNV2(client *golangsdk.ProviderClient, eo golangsdk.EndpointOpts) (*golangsdk.ServiceClient, error) {
   859  	sc, err := initClientOpts(client, eo, "smnv2")
   860  	if err != nil {
   861  		return nil, err
   862  	}
   863  	sc.ResourceBase = sc.Endpoint + "notifications/"
   864  	return sc, err
   865  }
   866  
   867  // NewSMNV2Tags creates a ServiceClient that may be used to access the SMN tags service.
   868  func NewSMNV2Tags(client *golangsdk.ProviderClient, eo golangsdk.EndpointOpts) (*golangsdk.ServiceClient, error) {
   869  	sc, err := initClientOpts(client, eo, "smnv2")
   870  	if err != nil {
   871  		return nil, err
   872  	}
   873  	return sc, err
   874  }
   875  
   876  // NewCCEv1 creates a ServiceClient that may be used to access the CCE k8s service.
   877  func NewCCEv1(client *golangsdk.ProviderClient, eo golangsdk.EndpointOpts) (*golangsdk.ServiceClient, error) {
   878  	return initClientOpts(client, eo, "cce")
   879  }
   880  
   881  // NewCCE creates a ServiceClient that may be used to access the CCE service.
   882  func NewCCE(client *golangsdk.ProviderClient, eo golangsdk.EndpointOpts) (*golangsdk.ServiceClient, error) {
   883  	sc, err := initClientOpts(client, eo, "ccev2.0")
   884  	if err != nil {
   885  		return nil, err
   886  	}
   887  	sc.ResourceBase = sc.Endpoint + "api/v3/projects/" + client.ProjectID + "/"
   888  	return sc, err
   889  }
   890  
   891  // NewWAFV1 creates a ServiceClient that may be used to access the WAF service.
   892  func NewWAFV1(client *golangsdk.ProviderClient, eo golangsdk.EndpointOpts) (*golangsdk.ServiceClient, error) {
   893  	sc, err := initClientOpts(client, eo, "waf")
   894  	if err != nil {
   895  		return nil, err
   896  	}
   897  	sc.ResourceBase = sc.Endpoint + "v1/" + client.ProjectID + "/waf/"
   898  	return sc, err
   899  }
   900  
   901  // NewWAFDSwissV1 creates a ServiceClient that may be used to access the premium WAF service.
   902  func NewWAFDSwissV1(client *golangsdk.ProviderClient, eo golangsdk.EndpointOpts) (*golangsdk.ServiceClient, error) {
   903  	sc, err := initClientOpts(client, eo, "waf")
   904  	if err != nil {
   905  		return nil, err
   906  	}
   907  	sc.ResourceBase = sc.Endpoint + "v1/" + client.ProjectID + "/"
   908  	return sc, err
   909  }
   910  
   911  // NewWAFDV1 creates a ServiceClient that may be used to access the premium WAF service.
   912  func NewWAFDV1(client *golangsdk.ProviderClient, eo golangsdk.EndpointOpts) (*golangsdk.ServiceClient, error) {
   913  	sc, err := initClientOpts(client, eo, "premium-waf")
   914  	if err != nil {
   915  		return nil, err
   916  	}
   917  	sc.ResourceBase = sc.Endpoint
   918  	return sc, err
   919  }
   920  
   921  // NewRDSV3 creates a ServiceClient that may be used to access the RDS service.
   922  func NewRDSV3(client *golangsdk.ProviderClient, eo golangsdk.EndpointOpts) (*golangsdk.ServiceClient, error) {
   923  	return initClientOpts(client, eo, "rdsv3")
   924  }
   925  
   926  // NewSDRSV1 creates a ServiceClient that may be used with the v1 SDRS service.
   927  func NewSDRSV1(client *golangsdk.ProviderClient, eo golangsdk.EndpointOpts) (*golangsdk.ServiceClient, error) {
   928  	return initClientOpts(client, eo, "sdrs")
   929  }
   930  
   931  // NewLTSV2 creates a ServiceClient that may be used to access the LTS service.
   932  func NewLTSV2(client *golangsdk.ProviderClient, eo golangsdk.EndpointOpts) (*golangsdk.ServiceClient, error) {
   933  	sc, err := initCommonServiceClient(client, eo, "lts", "v2")
   934  	return sc, err
   935  }
   936  
   937  // NewSWRV2 creates a ServiceClient that may be used to access the SWR service.
   938  func NewSWRV2(client *golangsdk.ProviderClient, eo golangsdk.EndpointOpts) (*golangsdk.ServiceClient, error) {
   939  	serviceClient, err := initClientOpts(client, eo, "smn") // SMN is v2 and has no project ID
   940  	if err != nil {
   941  		return nil, err
   942  	}
   943  	serviceClient.Endpoint = strings.Replace(serviceClient.Endpoint, "smn", "swr-api", 1)
   944  	serviceClient.ResourceBase = serviceClient.Endpoint
   945  	return serviceClient, err
   946  }
   947  
   948  // NewTMSV1 creates a ServiceClient that may be used to access the TMS service.
   949  func NewTMSV1(client *golangsdk.ProviderClient, eo golangsdk.EndpointOpts) (*golangsdk.ServiceClient, error) {
   950  	return initClientOpts(client, eo, "tms")
   951  }
   952  
   953  func NewGaussDBV3(client *golangsdk.ProviderClient, eo golangsdk.EndpointOpts) (*golangsdk.ServiceClient, error) {
   954  	sc, err := initCommonServiceClient(client, eo, "gaussdb", "mysql/v3")
   955  	return sc, err
   956  }
   957  
   958  func NewDataArtsV11(client *golangsdk.ProviderClient, eo golangsdk.EndpointOpts) (*golangsdk.ServiceClient, error) {
   959  	return initCommonServiceClient(client, eo, "cdm", "v1.1")
   960  }
   961  
   962  func NewAPIGW(client *golangsdk.ProviderClient, eo golangsdk.EndpointOpts) (*golangsdk.ServiceClient, error) {
   963  	sc, err := initCommonServiceClient(client, eo, "apig", "v2")
   964  	return sc, err
   965  }
   966  
   967  func NewFuncGraph(client *golangsdk.ProviderClient, eo golangsdk.EndpointOpts) (*golangsdk.ServiceClient, error) {
   968  	sc, err := initCommonServiceClient(client, eo, "functiongraph", "v2")
   969  	return sc, err
   970  }