fix(region): support organization (#20337)

This commit is contained in:
屈轩
2024-05-22 19:12:20 +08:00
committed by GitHub
parent b4cd3d758c
commit 88db3152d1
8 changed files with 189 additions and 35 deletions
+1 -1
View File
@@ -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
+2 -2
View File
@@ -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=
+1 -1
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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) {