From 5a87ca7c90e9a04e57e8f9d5e18e934a8ce89dab Mon Sep 17 00:00:00 2001 From: Qu Xuan Date: Wed, 22 Sep 2021 20:27:21 +0800 Subject: [PATCH] fix(region): external project support domain sync --- pkg/cloudprovider/resources.go | 3 + pkg/compute/models/cloudproviders.go | 11 ++-- pkg/compute/models/cloudsync.go | 2 +- pkg/compute/models/external_projects.go | 63 ++++++++++++++++++++- pkg/multicloud/aliyun/project.go | 2 +- pkg/multicloud/apsara/project.go | 8 +++ pkg/multicloud/azure/resourcegroup.go | 2 +- pkg/multicloud/esxi/resourcepool.go | 2 +- pkg/multicloud/google/project.go | 2 +- pkg/multicloud/hcso/enterpriceprojects.go | 2 +- pkg/multicloud/huawei/enterpriceprojects.go | 2 +- pkg/multicloud/openstack/project.go | 2 +- pkg/multicloud/project_base.go | 28 +++++++++ pkg/multicloud/qcloud/project.go | 2 +- pkg/multicloud/ucloud/project.go | 2 +- 15 files changed, 117 insertions(+), 16 deletions(-) create mode 100644 pkg/multicloud/project_base.go diff --git a/pkg/cloudprovider/resources.go b/pkg/cloudprovider/resources.go index 00d643fab1..d978d2f27d 100644 --- a/pkg/cloudprovider/resources.go +++ b/pkg/cloudprovider/resources.go @@ -787,6 +787,9 @@ type ICloudSku interface { type ICloudProject interface { ICloudResource + + GetDomainId() string + GetDomainName() string } type ICloudNatGateway interface { diff --git a/pkg/compute/models/cloudproviders.go b/pkg/compute/models/cloudproviders.go index 6de3192faf..0589f4a198 100644 --- a/pkg/compute/models/cloudproviders.go +++ b/pkg/compute/models/cloudproviders.go @@ -419,20 +419,23 @@ func createTenant(ctx context.Context, name, domainId, desc string) (string, str return domainId, projectId, nil } -func (self *SCloudaccount) getOrCreateTenant(ctx context.Context, name, projectId, desc string) (string, string, error) { +func (self *SCloudaccount) getOrCreateTenant(ctx context.Context, name, domainId, projectId, desc string) (string, string, error) { + if len(domainId) == 0 { + domainId = self.DomainId + } tenant, err := getTenant(ctx, projectId, name) if err != nil { if errors.Cause(err) != sql.ErrNoRows { return "", "", errors.Wrapf(err, "getTenan") } - return createTenant(ctx, name, self.DomainId, desc) + return createTenant(ctx, name, domainId, desc) } share := self.GetSharedInfo() if tenant.DomainId == self.DomainId || (share.PublicScope == rbacutils.ScopeSystem || (share.PublicScope == rbacutils.ScopeDomain && utils.IsInStringArray(tenant.DomainId, share.SharedDomains))) { return tenant.DomainId, tenant.Id, nil } - return createTenant(ctx, name, self.DomainId, desc) + return createTenant(ctx, name, domainId, desc) } func (self *SCloudprovider) syncProject(ctx context.Context, userCred mcclient.TokenCredential) error { @@ -442,7 +445,7 @@ func (self *SCloudprovider) syncProject(ctx context.Context, userCred mcclient.T } desc := fmt.Sprintf("auto create from cloud provider %s (%s)", self.Name, self.Id) - domainId, projectId, err := account.getOrCreateTenant(ctx, self.Name, self.ProjectId, desc) + domainId, projectId, err := account.getOrCreateTenant(ctx, self.Name, "", self.ProjectId, desc) if err != nil { return errors.Wrap(err, "getOrCreateTenant") } diff --git a/pkg/compute/models/cloudsync.go b/pkg/compute/models/cloudsync.go index 33605fabd9..3811deac24 100644 --- a/pkg/compute/models/cloudsync.go +++ b/pkg/compute/models/cloudsync.go @@ -1549,7 +1549,7 @@ func SyncCloudProject(userCred mcclient.TokenCredential, model db.IVirtualModel, domainId, projectId, newProj, isMatch := rule.IsMatchTags(extTags) if isMatch { if len(newProj) > 0 { - domainId, projectId, err = account.getOrCreateTenant(context.TODO(), newProj, "", "auto create from tag") + domainId, projectId, err = account.getOrCreateTenant(context.TODO(), newProj, "", "", "auto create from tag") if err != nil { return nil, errors.Wrapf(err, "getOrCreateTenant(%s)", newProj) } diff --git a/pkg/compute/models/external_projects.go b/pkg/compute/models/external_projects.go index 8a6e66c7d1..d6b225c9ed 100644 --- a/pkg/compute/models/external_projects.go +++ b/pkg/compute/models/external_projects.go @@ -26,6 +26,7 @@ import ( "yunion.io/x/pkg/utils" "yunion.io/x/sqlchemy" + "yunion.io/x/onecloud/pkg/apis" api "yunion.io/x/onecloud/pkg/apis/compute" "yunion.io/x/onecloud/pkg/cloudcommon/consts" "yunion.io/x/onecloud/pkg/cloudcommon/db" @@ -66,6 +67,7 @@ type SExternalProject struct { db.SExternalizedResourceBase SManagedResourceBase + ExternalDomainId string `width:"36" charset:"ascii" nullable:"true" list:"user"` // 归属云账号ID CloudaccountId string `width:"36" charset:"ascii" nullable:"false" list:"user"` } @@ -211,7 +213,7 @@ func (self *SExternalProject) SyncWithCloudProject(ctx context.Context, userCred self.DomainId = account.DomainId if account.AutoCreateProject { desc := fmt.Sprintf("auto create from cloud project %s (%s)", self.Name, self.ExternalId) - domainId, projectId, err := account.getOrCreateTenant(ctx, self.Name, "", desc) + domainId, projectId, err := account.getOrCreateTenant(ctx, self.Name, "", "", desc) if err != nil { log.Errorf("failed to get or create tenant %s(%s) %v", self.Name, self.ExternalId, err) } else { @@ -262,6 +264,53 @@ func (self *SExternalProject) SyncWithCloudProject(ctx context.Context, userCred return nil } +func (self *SCloudaccount) getOrCreateDomain(ctx context.Context, userCred mcclient.TokenCredential, id, name string) (string, error) { + lockman.LockRawObject(ctx, self.Id, CloudaccountManager.Keyword()) + defer lockman.ReleaseRawObject(ctx, self.Id, CloudaccountManager.Keyword()) + + domainId := "" + domain, err := db.TenantCacheManager.FetchDomainByIdOrName(ctx, name) + if err != nil { + if errors.Cause(err) != sql.ErrNoRows { + return "", errors.Wrapf(err, "FetchDomainByIdOrName") + } + s := auth.GetAdminSession(ctx, options.Options.Region, "") + params := jsonutils.NewDict() + params.Add(jsonutils.NewString(name), "generate_name") + + desc := fmt.Sprintf("auto create from cloud project %s (%s)", id, name) + params.Add(jsonutils.NewString(desc), "description") + + resp, err := modules.Domains.Create(s, params) + if err != nil { + return "", errors.Wrap(err, "Projects.Create") + } + domainId, err = resp.GetString("id") + if err != nil { + return "", errors.Wrapf(err, "resp.GetString") + } + } else { + domainId = domain.Id + } + + share := self.GetSharedInfo() + if share.PublicScope == rbacutils.ScopeSystem { + return domainId, nil + } + input := api.CloudaccountPerformPublicInput{} + input.ShareMode = string(rbacutils.ScopeSystem) + input.PerformPublicDomainInput = apis.PerformPublicDomainInput{ + Scope: string(rbacutils.ScopeDomain), + SharedDomains: append(share.SharedDomains, domainId), + SharedDomainIds: append(share.SharedDomains, domainId), + } + _, err = self.SInfrasResourceBase.PerformPublic(ctx, userCred, jsonutils.NewDict(), input.PerformPublicDomainInput) + if err != nil { + return "", errors.Wrapf(err, "PerformPublic") + } + return domainId, self.setShareMode(userCred, input.ShareMode) +} + func (manager *SExternalProjectManager) newFromCloudProject(ctx context.Context, userCred mcclient.TokenCredential, account *SCloudaccount, localProject *db.STenant, extProject cloudprovider.ICloudProject) (*SExternalProject, error) { project := SExternalProject{} project.SetModelManager(manager, &project) @@ -273,12 +322,22 @@ func (manager *SExternalProjectManager) newFromCloudProject(ctx context.Context, project.CloudaccountId = account.Id project.DomainId = account.DomainId project.ProjectId = account.ProjectId + project.ExternalDomainId = extProject.GetDomainId() + domainName := extProject.GetDomainName() + if len(project.ExternalDomainId) > 0 && len(domainName) > 0 { + domainId, err := account.getOrCreateDomain(ctx, userCred, project.ExternalDomainId, domainName) + if err != nil { + log.Errorf("getOrCreateDomain for project %s error: %v", account.Name, err) + } else { + project.DomainId = domainId + } + } if localProject != nil { project.DomainId = localProject.DomainId project.ProjectId = localProject.Id } else if account.AutoCreateProject { desc := fmt.Sprintf("auto create from cloud project %s (%s)", project.Name, project.ExternalId) - domainId, projectId, err := account.getOrCreateTenant(ctx, project.Name, "", desc) + domainId, projectId, err := account.getOrCreateTenant(ctx, project.Name, project.DomainId, "", desc) if err != nil { log.Errorf("failed to get or create tenant %s(%s) %v", project.Name, project.ExternalId, err) } else { diff --git a/pkg/multicloud/aliyun/project.go b/pkg/multicloud/aliyun/project.go index 271d3bb78e..1858840190 100644 --- a/pkg/multicloud/aliyun/project.go +++ b/pkg/multicloud/aliyun/project.go @@ -27,7 +27,7 @@ import ( ) type SResourceGroup struct { - multicloud.SResourceBase + multicloud.SProjectBase multicloud.AliyunTags client *SAliyunClient diff --git a/pkg/multicloud/apsara/project.go b/pkg/multicloud/apsara/project.go index 55f4111968..932164e709 100644 --- a/pkg/multicloud/apsara/project.go +++ b/pkg/multicloud/apsara/project.go @@ -58,6 +58,14 @@ func (self *SResourceGroup) GetId() string { return self.Id } +func (self *SResourceGroup) GetDomainName() string { + return self.OrganizationName +} + +func (self *SResourceGroup) GetDomainId() string { + return self.OrganizationId +} + func (self *SResourceGroup) GetName() string { return self.ResourceGroupName } diff --git a/pkg/multicloud/azure/resourcegroup.go b/pkg/multicloud/azure/resourcegroup.go index 5803dbad55..c556e4ce4a 100644 --- a/pkg/multicloud/azure/resourcegroup.go +++ b/pkg/multicloud/azure/resourcegroup.go @@ -30,7 +30,7 @@ type GroupProperties struct { } type SResourceGroup struct { - multicloud.SResourceBase + multicloud.SProjectBase multicloud.AzureTags client *SAzureClient diff --git a/pkg/multicloud/esxi/resourcepool.go b/pkg/multicloud/esxi/resourcepool.go index 332be8a11a..8a7fbaaed8 100644 --- a/pkg/multicloud/esxi/resourcepool.go +++ b/pkg/multicloud/esxi/resourcepool.go @@ -27,7 +27,7 @@ import ( var RESOURCEPOOL_PROPS = []string{"name", "parent", "host"} type SResourcePool struct { - multicloud.SResourceBase + multicloud.SProjectBase multicloud.STagBase SManagedObject } diff --git a/pkg/multicloud/google/project.go b/pkg/multicloud/google/project.go index 41fa57c4e4..bc16104944 100644 --- a/pkg/multicloud/google/project.go +++ b/pkg/multicloud/google/project.go @@ -24,7 +24,7 @@ import ( ) type SProject struct { - multicloud.SResourceBase + multicloud.SProjectBase multicloud.GoogleTags Name string CreateTime time.Time diff --git a/pkg/multicloud/hcso/enterpriceprojects.go b/pkg/multicloud/hcso/enterpriceprojects.go index 9f979839d8..ddbce0c503 100644 --- a/pkg/multicloud/hcso/enterpriceprojects.go +++ b/pkg/multicloud/hcso/enterpriceprojects.go @@ -28,7 +28,7 @@ import ( // https://support.huaweicloud.com/api-em/zh-cn_topic_0121230880.html type SEnterpriseProject struct { - multicloud.SResourceBase + multicloud.SProjectBase multicloud.HuaweiTags Id string diff --git a/pkg/multicloud/huawei/enterpriceprojects.go b/pkg/multicloud/huawei/enterpriceprojects.go index 53d935f66c..1b846ea92e 100644 --- a/pkg/multicloud/huawei/enterpriceprojects.go +++ b/pkg/multicloud/huawei/enterpriceprojects.go @@ -28,7 +28,7 @@ import ( // https://support.huaweicloud.com/api-em/zh-cn_topic_0121230880.html type SEnterpriseProject struct { - multicloud.SResourceBase + multicloud.SProjectBase multicloud.HuaweiTags Id string diff --git a/pkg/multicloud/openstack/project.go b/pkg/multicloud/openstack/project.go index 806eff229b..a4eff5eb5a 100644 --- a/pkg/multicloud/openstack/project.go +++ b/pkg/multicloud/openstack/project.go @@ -28,7 +28,7 @@ import ( ) type SProject struct { - multicloud.SResourceBase + multicloud.SProjectBase multicloud.OpenStackTags client *SOpenStackClient Description string diff --git a/pkg/multicloud/project_base.go b/pkg/multicloud/project_base.go new file mode 100644 index 0000000000..36f4d1826a --- /dev/null +++ b/pkg/multicloud/project_base.go @@ -0,0 +1,28 @@ +// 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 multicloud + +type SProjectBase struct { + SResourceBase + STagBase +} + +func (self *SProjectBase) GetDomainName() string { + return "" +} + +func (self *SProjectBase) GetDomainId() string { + return "" +} diff --git a/pkg/multicloud/qcloud/project.go b/pkg/multicloud/qcloud/project.go index cc59080615..81a2ef1bb8 100644 --- a/pkg/multicloud/qcloud/project.go +++ b/pkg/multicloud/qcloud/project.go @@ -27,7 +27,7 @@ import ( ) type SProject struct { - multicloud.SResourceBase + multicloud.SProjectBase multicloud.QcloudTags client *SQcloudClient diff --git a/pkg/multicloud/ucloud/project.go b/pkg/multicloud/ucloud/project.go index 8205f7712c..4dab5fe695 100644 --- a/pkg/multicloud/ucloud/project.go +++ b/pkg/multicloud/ucloud/project.go @@ -20,7 +20,7 @@ import ( // https://docs.ucloud.cn/api/summary/get_project_list type SProject struct { - multicloud.SResourceBase + multicloud.SProjectBase multicloud.UcloudTags ProjectID string `json:"ProjectId"` ProjectName string `json:"ProjectName"`