Merge pull request #16348 from swordqiu/hotfix/qj-auto-fix-cloud-account-missing-default-tenant

fix: allow set project-id auto-crate-project of cloudaccount
This commit is contained in:
Zexi Li
2023-04-06 11:17:45 +08:00
committed by GitHub
6 changed files with 114 additions and 41 deletions
+4
View File
@@ -513,6 +513,10 @@ type CloudaccountSyncSkusInput struct {
}
type CloudaccountProjectMappingInput struct {
AutoCreateProject bool `json:"auto_create_project"`
ProjectId string `json:"project_id"`
// 同步策略Id, 若不传此参数则解绑
// 绑定同步策略要求当前云账号此刻未绑定其他同步策略
ProjectMappingId string `json:"project_mapping_id"`
+1 -1
View File
@@ -410,7 +410,7 @@ func (tenant *STenant) GetDomainId() string {
}*/
func (manager *STenantCacheManager) findFirstProjectOfDomain(domainId string) (*STenant, error) {
q := manager.Query().Equals("domain_id", domainId)
q := manager.Query().Equals("domain_id", domainId).Asc("created_at")
tenant := STenant{}
tenant.SetModelManager(manager, &tenant)
err := q.First(&tenant)
+68 -21
View File
@@ -1474,6 +1474,32 @@ func (manager *SCloudaccountManager) initializeVMWareAccountId() error {
return nil
}
func (manager *SCloudaccountManager) initializeDefaultTenantId() error {
// init accountid
q := manager.Query().IsNullOrEmpty("tenant_id")
cloudaccounts := make([]SCloudaccount, 0)
err := db.FetchModelObjects(manager, q, &cloudaccounts)
if err != nil {
return errors.Wrap(err, "fetch empty defaullt tenant_id fail")
}
for i := range cloudaccounts {
account := cloudaccounts[i]
// auto fix accounts without default project
defaultTenant, err := db.TenantCacheManager.FindFirstProjectOfDomain(context.Background(), account.DomainId)
if err != nil {
return errors.Wrapf(err, "FindFirstProjectOfDomain(%s)", account.DomainId)
}
_, err = db.Update(&account, func() error {
account.ProjectId = defaultTenant.Id
return nil
})
if err != nil {
return errors.Wrap(err, "db.Update for account")
}
}
return nil
}
func (manager *SCloudaccountManager) InitializeData() error {
cloudproviders := []SCloudprovider{}
q := CloudproviderManager.Query()
@@ -1505,6 +1531,10 @@ func (manager *SCloudaccountManager) InitializeData() error {
if err != nil {
return errors.Wrap(err, "initializePublicScope")
}
err = manager.initializeDefaultTenantId()
if err != nil {
return errors.Wrap(err, "initializeDefaultTenantId")
}
return nil
}
@@ -1570,15 +1600,15 @@ func (account *SCloudaccount) PerformChangeOwner(ctx context.Context, userCred m
return nil, errors.Wrap(httperrors.ErrForbidden, "can't change domain owner of cloudaccount, use PerformChangeProject instead")
}
func (self *SCloudaccount) PerformChangeProject(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input apis.PerformChangeProjectOwnerInput) (jsonutils.JSONObject, error) {
if self.IsShared() {
func (account *SCloudaccount) PerformChangeProject(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input apis.PerformChangeProjectOwnerInput) (jsonutils.JSONObject, error) {
if account.IsShared() {
return nil, errors.Wrap(httperrors.ErrInvalidStatus, "cannot change owner when shared!")
}
project := input.ProjectId
domain := input.ProjectDomainId
if len(domain) == 0 {
domain = self.DomainId
domain = account.DomainId
}
tenant, err := db.TenantCacheManager.FetchTenantByIdOrNameInDomain(ctx, project, domain)
@@ -1586,34 +1616,34 @@ func (self *SCloudaccount) PerformChangeProject(ctx context.Context, userCred mc
return nil, httperrors.NewNotFoundError("project %s not found", project)
}
if tenant.Id == self.ProjectId {
if tenant.Id == account.ProjectId {
return nil, nil
}
providers := self.GetCloudproviders()
if len(self.ProjectId) > 0 {
providers := account.GetCloudproviders()
if len(account.ProjectId) > 0 {
if len(providers) > 0 {
for i := range providers {
if providers[i].ProjectId != self.ProjectId {
if providers[i].ProjectId != account.ProjectId {
return nil, errors.Wrap(httperrors.ErrConflict, "cloudproviders' project is different from cloudaccount's")
}
}
}
}
if tenant.DomainId != self.DomainId {
if tenant.DomainId != account.DomainId {
// do change domainId
input2 := apis.PerformChangeDomainOwnerInput{}
input2.ProjectDomainId = tenant.DomainId
_, err := self.SEnabledStatusInfrasResourceBase.PerformChangeOwner(ctx, userCred, query, input2)
_, err := account.SEnabledStatusInfrasResourceBase.PerformChangeOwner(ctx, userCred, query, input2)
if err != nil {
return nil, errors.Wrap(err, "SEnabledStatusInfrasResourceBase.PerformChangeOwner")
}
}
// save project_id change
diff, err := db.Update(self, func() error {
self.ProjectId = tenant.Id
diff, err := db.Update(account, func() error {
account.ProjectId = tenant.Id
return nil
})
@@ -1621,7 +1651,7 @@ func (self *SCloudaccount) PerformChangeProject(ctx context.Context, userCred mc
return nil, errors.Wrap(err, "db.Update ProjectId")
}
db.OpsLog.LogEvent(self, db.ACT_UPDATE, diff, userCred)
db.OpsLog.LogEvent(account, db.ACT_UPDATE, diff, userCred)
if len(providers) > 0 {
for i := range providers {
@@ -1898,6 +1928,7 @@ func (account *SCloudaccount) probeAccountStatus(ctx context.Context, userCred m
}
}
}
version := manager.GetVersion()
sysInfo, err := manager.GetSysInfo()
if err != nil {
@@ -1921,6 +1952,7 @@ func (account *SCloudaccount) probeAccountStatus(ctx context.Context, userCred m
account.Version = version
account.Sysinfo = sysInfo
account.IamLoginUrl = iamLoginUrl
return nil
})
if err != nil {
@@ -2831,28 +2863,43 @@ func (cd *SCloudaccount) GetHost2Wire(ctx context.Context, userCred mcclient.Tok
}
// 绑定同步策略
func (self *SCloudaccount) PerformProjectMapping(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.CloudaccountProjectMappingInput) (jsonutils.JSONObject, error) {
func (account *SCloudaccount) PerformProjectMapping(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.CloudaccountProjectMappingInput) (jsonutils.JSONObject, error) {
if len(input.ProjectMappingId) > 0 {
_, err := validators.ValidateModel(userCred, ProjectMappingManager, &input.ProjectMappingId)
if err != nil {
return nil, err
return nil, errors.Wrap(err, "ValidateModel")
}
if len(self.ProjectMappingId) > 0 && self.ProjectMappingId != input.ProjectMappingId {
return nil, httperrors.NewInputParameterError("account %s has aleady bind project mapping %s", self.Name, self.ProjectMappingId)
/*if len(account.ProjectMappingId) > 0 && account.ProjectMappingId != input.ProjectMappingId {
return nil, httperrors.NewInputParameterError("account %s has aleady bind project mapping %s", account.Name, account.ProjectMappingId)
}*/
if (input.EnableProjectSync == nil || !*input.EnableProjectSync) && (input.EnableResourceSync == nil || !*input.EnableResourceSync) {
return nil, errors.Wrap(httperrors.ErrInputParameter, "either enable_project_sync or enable_resource_sync must be set")
}
}
_, err := db.Update(self, func() error {
self.ProjectMappingId = input.ProjectMappingId
if len(input.ProjectId) == 0 {
return nil, errors.Wrap(httperrors.ErrInputParameter, "empty project_id")
}
t, err := db.TenantCacheManager.FetchTenantByIdOrNameInDomain(ctx, input.ProjectId, account.DomainId)
if err != nil {
return nil, errors.Wrap(err, "FetchTenantByIdOrNameInDomain")
}
input.ProjectId = t.Id
_, err = db.Update(account, func() error {
account.ProjectId = input.ProjectId
account.AutoCreateProject = input.AutoCreateProject
account.ProjectMappingId = input.ProjectMappingId
if input.EnableProjectSync != nil {
self.EnableProjectSync = tristate.NewFromBool(*input.EnableProjectSync)
account.EnableProjectSync = tristate.NewFromBool(*input.EnableProjectSync)
}
if input.EnableResourceSync != nil {
self.EnableResourceSync = tristate.NewFromBool(*input.EnableResourceSync)
account.EnableResourceSync = tristate.NewFromBool(*input.EnableResourceSync)
}
return nil
})
if err != nil {
return nil, err
return nil, errors.Wrap(err, "update")
}
return nil, refreshPmCaches()
}
+37 -17
View File
@@ -483,9 +483,9 @@ func createTenant(ctx context.Context, name, domainId, desc string) (string, str
return domainId, projectId, nil
}
func (self *SCloudaccount) getOrCreateTenant(ctx context.Context, name, domainId, projectId, desc string) (string, string, error) {
func (account *SCloudaccount) getOrCreateTenant(ctx context.Context, name, domainId, projectId, desc string) (string, string, error) {
if len(domainId) == 0 {
domainId = self.DomainId
domainId = account.DomainId
}
ctx = context.WithValue(ctx, time.Now().String(), utils.GenRequestId(20))
uuid := stringutils.UUID4()
@@ -499,8 +499,8 @@ func (self *SCloudaccount) getOrCreateTenant(ctx context.Context, name, domainId
}
return createTenant(ctx, name, domainId, desc)
}
share := self.GetSharedInfo()
if tenant.DomainId == self.DomainId || (share.PublicScope == rbacscope.ScopeSystem ||
share := account.GetSharedInfo()
if tenant.DomainId == account.DomainId || (share.PublicScope == rbacscope.ScopeSystem ||
(share.PublicScope == rbacscope.ScopeDomain && utils.IsInStringArray(tenant.DomainId, share.SharedDomains))) {
return tenant.DomainId, tenant.Id, nil
}
@@ -1117,27 +1117,47 @@ func (manager *SCloudproviderManager) FetchCustomizeColumns(
return rows
}
func (manager *SCloudproviderManager) InitializeData() error {
// fill empty projectId with system project ID
func (manager *SCloudproviderManager) initializeDefaultTenantId() error {
// init accountid
q := manager.Query().IsNullOrEmpty("tenant_id")
providers := make([]SCloudprovider, 0)
q := CloudproviderManager.Query()
q = q.Filter(sqlchemy.OR(sqlchemy.IsEmpty(q.Field("tenant_id")), sqlchemy.IsNull(q.Field("tenant_id"))))
err := db.FetchModelObjects(CloudproviderManager, q, &providers)
err := db.FetchModelObjects(manager, q, &providers)
if err != nil {
log.Errorf("query cloudproviders with empty tenant_id fail %s", err)
return err
return errors.Wrap(err, "fetch empty defaullt tenant_id fail")
}
for i := 0; i < len(providers); i += 1 {
_, err := db.Update(&providers[i], func() error {
providers[i].DomainId = auth.AdminCredential().GetProjectDomainId()
providers[i].ProjectId = auth.AdminCredential().GetProjectId()
for i := range providers {
provider := providers[i]
domainId := provider.DomainId
if len(domainId) == 0 {
account, err := provider.GetCloudaccount()
if err != nil {
log.Errorf("GetCloudaccount fail %s", err)
continue
}
domainId = account.DomainId
}
// auto fix accounts without default project
defaultTenant, err := db.TenantCacheManager.FindFirstProjectOfDomain(context.Background(), domainId)
if err != nil {
return errors.Wrapf(err, "FindFirstProjectOfDomain(%s)", provider.DomainId)
}
_, err = db.Update(&provider, func() error {
provider.ProjectId = defaultTenant.Id
provider.DomainId = defaultTenant.DomainId
return nil
})
if err != nil {
log.Errorf("update cloudprovider project fail %s", err)
return err
return errors.Wrap(err, "db.Update for account")
}
}
return nil
}
func (manager *SCloudproviderManager) InitializeData() error {
err := manager.initializeDefaultTenantId()
if err != nil {
log.Errorf("initializeDefaultTenantId %s", err)
}
return nil
}
+1 -1
View File
@@ -32,8 +32,8 @@ func InitDB() error {
QuotaManager,
CloudproviderManager,
CloudaccountManager,
CloudproviderManager,
CloudregionManager,
ZoneManager,
VpcManager,
+3 -1
View File
@@ -1123,13 +1123,15 @@ func (opts *SubscriptionCreateOptions) Params() (jsonutils.JSONObject, error) {
type ClouaccountProjectMappingOptions struct {
SCloudAccountIdOptions
ProjectId string `json:"project_id" help:"default project id"`
AutoCreateProject bool `help:"auto create project"`
ProjectMappingId string `json:"project_mapping_id" help:"project mapping id"`
EnableProjectSync bool
EnableResourceSync bool
}
func (opts *ClouaccountProjectMappingOptions) Params() (jsonutils.JSONObject, error) {
return jsonutils.Marshal(map[string]string{"project_mapping_id": opts.ProjectMappingId}), nil
return jsonutils.Marshal(opts), nil
}
type SNutanixCloudAccountCreateOptions struct {