mirror of
https://github.com/yunionio/cloudpods.git
synced 2026-10-31 02:38:07 +08:00
fix(region): support organization (#20337)
This commit is contained in:
@@ -89,7 +89,7 @@ require (
|
||||
k8s.io/cluster-bootstrap v0.19.3
|
||||
k8s.io/cri-api v0.22.17
|
||||
moul.io/http2curl/v2 v2.3.0
|
||||
yunion.io/x/cloudmux v0.3.10-0-alpha.1.0.20240521070416-dbc80ff43103
|
||||
yunion.io/x/cloudmux v0.3.10-0-alpha.1.0.20240522105017-236088f9697b
|
||||
yunion.io/x/executor v0.0.0-20230705125604-c5ac3141db32
|
||||
yunion.io/x/jsonutils v1.0.1-0.20240203102553-4096f103b401
|
||||
yunion.io/x/log v1.0.1-0.20240305175729-7cf2d6cd5a91
|
||||
|
||||
@@ -1213,8 +1213,8 @@ sigs.k8s.io/structured-merge-diff/v4 v4.0.1/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK
|
||||
sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=
|
||||
sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q=
|
||||
sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=
|
||||
yunion.io/x/cloudmux v0.3.10-0-alpha.1.0.20240521070416-dbc80ff43103 h1:f5vNbU6wUTO9DfoES8QqPuPtfo1AjUU6d+W0BdNqWik=
|
||||
yunion.io/x/cloudmux v0.3.10-0-alpha.1.0.20240521070416-dbc80ff43103/go.mod h1:quoJjGTJ2PjAY0+3YeN5JuN136whECKmfkJQwIsXKjM=
|
||||
yunion.io/x/cloudmux v0.3.10-0-alpha.1.0.20240522105017-236088f9697b h1:d3c79OrzQWrmmZfzgw4hoKMIxrrRVlco2vXEpYhOakE=
|
||||
yunion.io/x/cloudmux v0.3.10-0-alpha.1.0.20240522105017-236088f9697b/go.mod h1:quoJjGTJ2PjAY0+3YeN5JuN136whECKmfkJQwIsXKjM=
|
||||
yunion.io/x/executor v0.0.0-20230705125604-c5ac3141db32 h1:v7POYkQwo1XzOxBoIoRVr/k0V9Y5JyjpshlIFa9raug=
|
||||
yunion.io/x/executor v0.0.0-20230705125604-c5ac3141db32/go.mod h1:Uxuou9WQIeJXNpy7t2fPLL0BYLvLiMvGQwY7Qc6aSws=
|
||||
yunion.io/x/jsonutils v0.0.0-20190625054549-a964e1e8a051/go.mod h1:4N0/RVzsYL3kH3WE/H1BjUQdFiWu50JGCFQuuy+Z634=
|
||||
|
||||
Vendored
+1
-1
@@ -1483,7 +1483,7 @@ sigs.k8s.io/structured-merge-diff/v4/value
|
||||
# sigs.k8s.io/yaml v1.2.0
|
||||
## explicit; go 1.12
|
||||
sigs.k8s.io/yaml
|
||||
# yunion.io/x/cloudmux v0.3.10-0-alpha.1.0.20240521070416-dbc80ff43103
|
||||
# yunion.io/x/cloudmux v0.3.10-0-alpha.1.0.20240522105017-236088f9697b
|
||||
## explicit; go 1.18
|
||||
yunion.io/x/cloudmux/pkg/apis
|
||||
yunion.io/x/cloudmux/pkg/apis/billing
|
||||
|
||||
+3
-3
@@ -60,7 +60,7 @@ func (b *SBucket) GetProjectId() string {
|
||||
}
|
||||
|
||||
func (b *SBucket) GetGlobalId() string {
|
||||
if b.getAppId() == b.region.client.appId {
|
||||
if b.getAppId() == b.region.client.GetAppId() {
|
||||
return b.Name
|
||||
} else {
|
||||
return b.getFullName()
|
||||
@@ -152,9 +152,9 @@ func (b *SBucket) getAppId() string {
|
||||
return b.appId
|
||||
}
|
||||
if b.zone != nil {
|
||||
return b.zone.region.client.appId
|
||||
return b.zone.region.client.GetAppId()
|
||||
}
|
||||
return b.region.client.appId
|
||||
return b.region.client.GetAppId()
|
||||
}
|
||||
|
||||
func (b *SBucket) getFullName() string {
|
||||
|
||||
+92
@@ -0,0 +1,92 @@
|
||||
// Copyright 2019 Yunion
|
||||
//
|
||||
// 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 qcloud
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
type SOrganizationMember struct {
|
||||
AppId string
|
||||
AuthName string
|
||||
AuthStatus string
|
||||
BindStatus string
|
||||
CreateTime time.Time
|
||||
IsAllowQuit string
|
||||
MemberType string
|
||||
MemberUin int
|
||||
Name string
|
||||
NodeId int
|
||||
NodeName string
|
||||
OrgIdentity []struct {
|
||||
IdentityAliasName string
|
||||
IdentityId int
|
||||
}
|
||||
OrgPermission []struct {
|
||||
Id string
|
||||
Name string
|
||||
}
|
||||
OrgPolicyName string
|
||||
OrgPolicyType string
|
||||
PayName string
|
||||
PayUin string
|
||||
PermissionStatus string
|
||||
Remark string
|
||||
}
|
||||
|
||||
func (self *SQcloudClient) DescribeOrganizationMembers() ([]SOrganizationMember, error) {
|
||||
params := map[string]string{
|
||||
"Limit": "50",
|
||||
"Offset": "0",
|
||||
}
|
||||
ret := []SOrganizationMember{}
|
||||
for {
|
||||
resp, err := self.orgRequest("DescribeOrganizationMembers", params)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
part := struct {
|
||||
Items []SOrganizationMember
|
||||
Total int
|
||||
}{}
|
||||
err = resp.Unmarshal(&part)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ret = append(ret, part.Items...)
|
||||
if len(ret) >= part.Total || len(part.Items) == 0 {
|
||||
break
|
||||
}
|
||||
params["Offset"] = fmt.Sprintf("%d", len(ret))
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func (self *SQcloudClient) DescribeOrganizationMember(uin string) (*SOrganizationMember, error) {
|
||||
params := map[string]string{
|
||||
"MemberUin": uin,
|
||||
}
|
||||
resp, err := self.orgRequest("DescribeOrganizationMember", params)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ret := &SOrganizationMember{}
|
||||
err = resp.Unmarshal(ret)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
+19
-16
@@ -164,16 +164,13 @@ func (self *SQcloudProviderFactory) ValidateChangeBandwidth(instanceId string, b
|
||||
|
||||
func (self *SQcloudProviderFactory) ValidateCreateCloudaccountData(ctx context.Context, input cloudprovider.SCloudaccountCredential) (cloudprovider.SCloudaccount, error) {
|
||||
output := cloudprovider.SCloudaccount{}
|
||||
if len(input.AppId) == 0 {
|
||||
return output, errors.Wrap(cloudprovider.ErrMissingParameter, "app_id")
|
||||
}
|
||||
if len(input.SecretId) == 0 {
|
||||
return output, errors.Wrap(cloudprovider.ErrMissingParameter, "secret_id")
|
||||
}
|
||||
if len(input.SecretKey) == 0 {
|
||||
return output, errors.Wrap(cloudprovider.ErrMissingParameter, "secret_key")
|
||||
}
|
||||
output.Account = fmt.Sprintf("%s/%s", input.SecretId, input.AppId)
|
||||
output.Account = input.SecretId
|
||||
output.Secret = input.SecretKey
|
||||
return output, nil
|
||||
}
|
||||
@@ -182,10 +179,9 @@ func (self *SQcloudProviderFactory) ValidateUpdateCloudaccountCredential(ctx con
|
||||
output := cloudprovider.SCloudaccount{}
|
||||
if len(input.AppId) == 0 {
|
||||
accountInfo := strings.Split(cloudaccount, "/")
|
||||
if len(accountInfo) < 2 {
|
||||
return output, errors.Wrap(cloudprovider.ErrMissingParameter, "app_id")
|
||||
if len(accountInfo) == 2 {
|
||||
input.AppId = accountInfo[1]
|
||||
}
|
||||
input.AppId = accountInfo[1]
|
||||
}
|
||||
if len(input.SecretId) == 0 {
|
||||
return output, errors.Wrap(cloudprovider.ErrMissingParameter, "secret_id")
|
||||
@@ -193,8 +189,12 @@ func (self *SQcloudProviderFactory) ValidateUpdateCloudaccountCredential(ctx con
|
||||
if len(input.SecretKey) == 0 {
|
||||
return output, errors.Wrap(cloudprovider.ErrMissingParameter, "secret_key")
|
||||
}
|
||||
account := input.SecretId
|
||||
if len(input.AppId) > 0 {
|
||||
account = fmt.Sprintf("%s/%s", input.SecretId, input.AppId)
|
||||
}
|
||||
output = cloudprovider.SCloudaccount{
|
||||
Account: fmt.Sprintf("%s/%s", input.SecretId, input.AppId),
|
||||
Account: account,
|
||||
Secret: input.SecretKey,
|
||||
}
|
||||
return output, nil
|
||||
@@ -202,15 +202,15 @@ func (self *SQcloudProviderFactory) ValidateUpdateCloudaccountCredential(ctx con
|
||||
|
||||
func (self *SQcloudProviderFactory) GetProvider(cfg cloudprovider.ProviderConfig) (cloudprovider.ICloudProvider, error) {
|
||||
secretId := cfg.Account
|
||||
appId := ""
|
||||
accountId := ""
|
||||
if tmp := strings.Split(cfg.Account, "/"); len(tmp) == 2 {
|
||||
secretId = tmp[0]
|
||||
appId = tmp[1]
|
||||
accountId = tmp[1]
|
||||
}
|
||||
client, err := qcloud.NewQcloudClient(
|
||||
qcloud.NewQcloudClientConfig(
|
||||
secretId, cfg.Secret,
|
||||
).AppId(appId).CloudproviderConfig(cfg),
|
||||
).AccountId(accountId).CloudproviderConfig(cfg),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -223,17 +223,20 @@ func (self *SQcloudProviderFactory) GetProvider(cfg cloudprovider.ProviderConfig
|
||||
|
||||
func (self *SQcloudProviderFactory) GetClientRC(info cloudprovider.SProviderInfo) (map[string]string, error) {
|
||||
secretId := info.Account
|
||||
appId := ""
|
||||
accountId := ""
|
||||
if tmp := strings.Split(info.Account, "/"); len(tmp) == 2 {
|
||||
secretId = tmp[0]
|
||||
appId = tmp[1]
|
||||
accountId = tmp[1]
|
||||
}
|
||||
return map[string]string{
|
||||
"QCLOUD_APPID": appId,
|
||||
ret := map[string]string{
|
||||
"QCLOUD_SECRET_ID": secretId,
|
||||
"QCLOUD_SECRET_KEY": info.Secret,
|
||||
"QCLOUD_REGION": qcloud.QCLOUD_DEFAULT_REGION,
|
||||
}, nil
|
||||
}
|
||||
if len(accountId) == 12 {
|
||||
ret["QCLOUD_ACCOUNT_ID"] = accountId
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
|
||||
+70
-11
@@ -72,6 +72,7 @@ const (
|
||||
QCLOUD_STS_API_VERSION = "2018-08-13"
|
||||
QCLOUD_TAG_API_VERSION = "2018-08-13"
|
||||
QCLOUD_WAF_API_VERSION = "2018-01-25"
|
||||
QCLOUD_ORG_API_VERSION = "2021-03-31"
|
||||
)
|
||||
|
||||
type QcloudClientConfig struct {
|
||||
@@ -79,6 +80,7 @@ type QcloudClientConfig struct {
|
||||
|
||||
secretId string
|
||||
secretKey string
|
||||
accountId string
|
||||
appId string
|
||||
|
||||
debug bool
|
||||
@@ -97,8 +99,12 @@ func (cfg *QcloudClientConfig) CloudproviderConfig(cpcfg cloudprovider.ProviderC
|
||||
return cfg
|
||||
}
|
||||
|
||||
func (cfg *QcloudClientConfig) AppId(appId string) *QcloudClientConfig {
|
||||
cfg.appId = appId
|
||||
func (cfg *QcloudClientConfig) AccountId(accountId string) *QcloudClientConfig {
|
||||
if len(accountId) == 12 {
|
||||
cfg.accountId = accountId
|
||||
} else {
|
||||
cfg.appId = accountId
|
||||
}
|
||||
return cfg
|
||||
}
|
||||
|
||||
@@ -158,6 +164,11 @@ func vpcRequest(client *common.Client, apiName string, params map[string]string,
|
||||
return _jsonRequest(client, domain, QCLOUD_API_VERSION, apiName, params, updateFunc, debug, true)
|
||||
}
|
||||
|
||||
func orgRequest(client *common.Client, apiName string, params map[string]string, updateFunc func(string, string), debug bool) (jsonutils.JSONObject, error) {
|
||||
domain := "organization.tencentcloudapi.com"
|
||||
return _jsonRequest(client, domain, QCLOUD_ORG_API_VERSION, apiName, params, updateFunc, debug, true)
|
||||
}
|
||||
|
||||
func auditRequest(client *common.Client, apiName string, params map[string]string, updateFunc func(string, string), debug bool) (jsonutils.JSONObject, error) {
|
||||
domain := apiDomain("cloudaudit", params)
|
||||
return _jsonRequest(client, domain, QCLOUD_AUDIT_API_VERSION, apiName, params, updateFunc, debug, true)
|
||||
@@ -358,6 +369,7 @@ func _baseJsonRequest(client *common.Client, req tchttp.Request, resp qcloudResp
|
||||
"InvalidParameter.RoleNotExist",
|
||||
"ResourceNotFound",
|
||||
"FailedOperation.CertificateNotFound",
|
||||
"ResourceNotFound.OrganizationNotExist",
|
||||
}) {
|
||||
return nil, errors.Wrapf(cloudprovider.ErrNotFound, err.Error())
|
||||
}
|
||||
@@ -440,6 +452,14 @@ func (client *SQcloudClient) getSdkClient(regionId string) (*common.Client, erro
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(client.accountId) > 0 {
|
||||
arn := fmt.Sprintf("qcs::cam::uin/%s:roleName/%s", client.accountId, "OrganizationAccessControlRole")
|
||||
sts := common.DefaultRoleArnProvider(client.secretId, client.secretKey, arn)
|
||||
cli, err = cli.WithProvider(sts)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "WithProvider")
|
||||
}
|
||||
}
|
||||
httpClient := client.cpcfg.AdaptiveTimeoutHttpClient()
|
||||
ts, _ := httpClient.Transport.(*http.Transport)
|
||||
cli.WithHttpTransport(cloudprovider.GetCheckTransport(ts, func(req *http.Request) (func(resp *http.Response) error, error) {
|
||||
@@ -489,6 +509,14 @@ func (client *SQcloudClient) vpcRequest(apiName string, params map[string]string
|
||||
return vpcRequest(cli, apiName, params, client.cpcfg.UpdatePermission, client.debug)
|
||||
}
|
||||
|
||||
func (client *SQcloudClient) orgRequest(apiName string, params map[string]string) (jsonutils.JSONObject, error) {
|
||||
cli, err := client.getDefaultClient(params)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return orgRequest(cli, apiName, params, client.cpcfg.UpdatePermission, client.debug)
|
||||
}
|
||||
|
||||
func (client *SQcloudClient) auditRequest(apiName string, params map[string]string) (jsonutils.JSONObject, error) {
|
||||
cli, err := client.getDefaultClient(params)
|
||||
if err != nil {
|
||||
@@ -750,6 +778,9 @@ func (self *SQcloudClient) invalidateIBuckets() {
|
||||
}
|
||||
|
||||
func (self *SQcloudClient) getIBuckets() ([]cloudprovider.ICloudBucket, error) {
|
||||
if len(self.accountId) > 0 {
|
||||
return nil, errors.Wrapf(cloudprovider.ErrNotSupported, "organization")
|
||||
}
|
||||
if self.ibuckets == nil {
|
||||
err := self.fetchBuckets()
|
||||
if err != nil {
|
||||
@@ -818,8 +849,8 @@ func (client *SQcloudClient) fetchBuckets() error {
|
||||
createAt, _ := timeutils.ParseTimeStr(bInfo.CreationDate)
|
||||
slashPos := strings.LastIndexByte(bInfo.Name, '-')
|
||||
appId := bInfo.Name[slashPos+1:]
|
||||
if appId != client.appId {
|
||||
log.Errorf("[%s %s] Inconsistent appId: %s expect %s", bInfo.Name, bInfo.Region, appId, client.appId)
|
||||
if appId != client.GetAppId() {
|
||||
log.Errorf("[%s %s] Inconsistent appId: %s expect %s", bInfo.Name, bInfo.Region, appId, client.GetAppId())
|
||||
}
|
||||
name := bInfo.Name[:slashPos]
|
||||
region, err := client.getIRegionByRegionId(bInfo.Region)
|
||||
@@ -865,20 +896,33 @@ func (client *SQcloudClient) fetchBuckets() error {
|
||||
}
|
||||
|
||||
func (client *SQcloudClient) GetSubAccounts() ([]cloudprovider.SSubAccount, error) {
|
||||
err := client.fetchRegions()
|
||||
if err != nil {
|
||||
nodes, err := client.DescribeOrganizationMembers()
|
||||
if err != nil && errors.Cause(err) != cloudprovider.ErrNotFound {
|
||||
return nil, err
|
||||
}
|
||||
subAccount := cloudprovider.SSubAccount{}
|
||||
subAccount.Id = client.GetAccountId()
|
||||
subAccount.Name = client.cpcfg.Name
|
||||
subAccount.Account = client.secretId
|
||||
subAccount.HealthStatus = api.CLOUD_PROVIDER_HEALTH_NORMAL
|
||||
subAccount.DefaultProjectId = "0"
|
||||
if len(client.appId) > 0 {
|
||||
if len(client.appId) > 0 { // 兼容旧版本账号,避免订阅删除
|
||||
subAccount.Account = fmt.Sprintf("%s/%s", client.secretId, client.appId)
|
||||
}
|
||||
return []cloudprovider.SSubAccount{subAccount}, nil
|
||||
subAccount.HealthStatus = api.CLOUD_PROVIDER_HEALTH_NORMAL
|
||||
subAccount.DefaultProjectId = "0"
|
||||
ret := []cloudprovider.SSubAccount{subAccount}
|
||||
for _, node := range nodes {
|
||||
uin := fmt.Sprintf("%d", node.MemberUin)
|
||||
if len(subAccount.Id) > 0 && uin != subAccount.Id {
|
||||
account := cloudprovider.SSubAccount{}
|
||||
account.Id = uin
|
||||
account.Name = node.Name
|
||||
account.Account = fmt.Sprintf("%s/%s", client.secretId, uin)
|
||||
account.HealthStatus = api.CLOUD_PROVIDER_HEALTH_NORMAL
|
||||
account.DefaultProjectId = "0"
|
||||
ret = append(ret, account)
|
||||
}
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func (self *SQcloudClient) GetAccountId() string {
|
||||
@@ -1017,6 +1061,18 @@ func (client *SQcloudClient) GetIProjects() ([]cloudprovider.ICloudProject, erro
|
||||
return iprojects, nil
|
||||
}
|
||||
|
||||
func (client *SQcloudClient) GetAppId() string {
|
||||
if len(client.appId) > 0 {
|
||||
return client.appId
|
||||
}
|
||||
resp, err := client.camRequest("GetUserAppId", map[string]string{})
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
client.appId, _ = resp.GetString("AppId")
|
||||
return client.appId
|
||||
}
|
||||
|
||||
func (self *SQcloudClient) GetISSLCertificates() ([]cloudprovider.ICloudSSLCertificate, error) {
|
||||
rs, err := self.GetCertificates("", "", "")
|
||||
if err != nil {
|
||||
@@ -1039,7 +1095,6 @@ func (self *SQcloudClient) GetCapabilities() []string {
|
||||
cloudprovider.CLOUD_CAPABILITY_SECURITY_GROUP,
|
||||
cloudprovider.CLOUD_CAPABILITY_EIP,
|
||||
cloudprovider.CLOUD_CAPABILITY_LOADBALANCER,
|
||||
cloudprovider.CLOUD_CAPABILITY_OBJECTSTORE,
|
||||
cloudprovider.CLOUD_CAPABILITY_RDS,
|
||||
cloudprovider.CLOUD_CAPABILITY_CACHE,
|
||||
cloudprovider.CLOUD_CAPABILITY_EVENT,
|
||||
@@ -1057,5 +1112,9 @@ func (self *SQcloudClient) GetCapabilities() []string {
|
||||
cloudprovider.CLOUD_CAPABILITY_SNAPSHOT_POLICY,
|
||||
cloudprovider.CLOUD_CAPABILITY_WAF + cloudprovider.READ_ONLY_SUFFIX,
|
||||
}
|
||||
// 官方cos sdk 未支持sts
|
||||
if len(self.accountId) == 0 {
|
||||
caps = append(caps, cloudprovider.CLOUD_CAPABILITY_OBJECTSTORE)
|
||||
}
|
||||
return caps
|
||||
}
|
||||
|
||||
+1
-1
@@ -105,7 +105,7 @@ func (self *SStoragecache) UploadImage(ctx context.Context, image *cloudprovider
|
||||
|
||||
func (self *SRegion) getCosUrl(bucket, object string) string {
|
||||
//signature := cosauth.NewSignature(self.client.AppID, bucket, self.client.secretId, time.Now().Add(time.Minute*30).String(), time.Now().String(), "yunion", object).SignOnce(self.client.secretKey)
|
||||
return fmt.Sprintf("http://%s-%s.cos.%s.myqcloud.com/%s", bucket, self.client.appId, self.Region, object)
|
||||
return fmt.Sprintf("http://%s-%s.cos.%s.myqcloud.com/%s", bucket, self.client.GetAppId(), self.Region, object)
|
||||
}
|
||||
|
||||
func (self *SStoragecache) uploadImage(ctx context.Context, image *cloudprovider.SImageCreateOption, callback func(progress float32)) (string, error) {
|
||||
|
||||
Reference in New Issue
Block a user