diff --git a/controllers/account/controllers/account_controller.go b/controllers/account/controllers/account_controller.go index f5318c6f3..91ef0aad0 100644 --- a/controllers/account/controllers/account_controller.go +++ b/controllers/account/controllers/account_controller.go @@ -101,6 +101,8 @@ const ( EnvSubscriptionEnabled = "SUBSCRIPTION_ENABLED" EnvJwtSecret = "ACCOUNT_API_JWT_SECRET" EnvDesktopJwtSecret = "DESKTOP_API_JWT_SECRET" + + InitAccountTimeAnnotation = "user.sealos.io/init-account-time" ) var SubscriptionEnabled = false @@ -144,6 +146,9 @@ func (r *AccountReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ct if owner = user.Annotations[userv1.UserAnnotationOwnerKey]; owner == "" { return ctrl.Result{}, fmt.Errorf("user owner is empty") } + if user.Annotations[InitAccountTimeAnnotation] != "" { + return ctrl.Result{}, nil + } // This is only used to monitor and initialize user resource creation data, // determine the resource quota created by the owner user and the resource quota initialized by the account user, // and only the resource quota created by the team user @@ -151,6 +156,10 @@ func (r *AccountReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ct if errors.Is(err, gorm.ErrRecordNotFound) && user.CreationTimestamp.Add(r.SkipExpiredUserTimeDuration).Before(time.Now()) { return ctrl.Result{}, nil } + if err == nil { + user.Annotations[InitAccountTimeAnnotation] = time.Now().Format(time.RFC3339) + return ctrl.Result{}, r.Update(ctx, user) + } return ctrl.Result{}, err } else if client.IgnoreNotFound(err) != nil { return ctrl.Result{}, err @@ -181,7 +190,8 @@ func (r *AccountReconciler) syncAccount(ctx context.Context, owner string, userN } } if err = r.SyncNSQuotaFunc(ctx, owner, userNamespace); err != nil { - r.Logger.Error(err, "sync resource resourceQuota and limitRange failed") + //r.Logger.Error(err, "sync resource resourceQuota and limitRange failed") + return nil, fmt.Errorf("sync resource resourceQuota and limitRange failed: %v", err) } return } diff --git a/controllers/account/controllers/subscription.go b/controllers/account/controllers/subscription.go index 66a932ca0..ec66368b7 100644 --- a/controllers/account/controllers/subscription.go +++ b/controllers/account/controllers/subscription.go @@ -238,7 +238,12 @@ func (sp *SubscriptionProcessor) sendFlushQuotaRequest(userUID, planID uuid.UUID lastErr = nil break } - lastErr = fmt.Errorf("unexpected status code: %d", resp.StatusCode) + body, err := io.ReadAll(resp.Body) + if err != nil { + lastErr = fmt.Errorf("unexpected status code: %d, failed to read response body: %w", resp.StatusCode, err) + } else { + lastErr = fmt.Errorf("unexpected status code: %d, response body: %s", resp.StatusCode, string(body)) + } } // 进行重试 diff --git a/controllers/pkg/database/cockroach/accountv2.go b/controllers/pkg/database/cockroach/accountv2.go index d78df5d23..c49c10af7 100644 --- a/controllers/pkg/database/cockroach/accountv2.go +++ b/controllers/pkg/database/cockroach/accountv2.go @@ -1496,27 +1496,6 @@ func (c *Cockroach) NewAccountWithFreeSubscriptionPlan(ops *types.UserQueryOpts) } ops.UID = userUID } - freePlan, err := c.GetSubscriptionPlan(types.FreeSubscriptionPlanName) - if err != nil { - return nil, fmt.Errorf("failed to get free plan: %w", err) - } - userInfo := &types.UserInfo{} - err = c.DB.Model(&types.UserInfo{}).Where(`"userUid" = ?`, ops.UID).Find(userInfo).Error - if err != nil && err != gorm.ErrRecordNotFound { - return nil, fmt.Errorf("failed to get user info: %w", err) - } - githubDetection := true - if userInfo.Config != nil { - if userInfo.Config.Github.CreatedAt != "" { - createdAt, err := time.Parse(time.RFC3339, userInfo.Config.Github.CreatedAt) - if err != nil { - return nil, fmt.Errorf("failed to parse github created at: %w", err) - } - if time.Since(createdAt) < 7*24*time.Hour { - githubDetection = false - } - } - } now := time.Now().UTC() account := &types.Account{ UserUID: ops.UID, @@ -1530,10 +1509,35 @@ func (c *Cockroach) NewAccountWithFreeSubscriptionPlan(ops *types.UserQueryOpts) // 1. create credits // 2. create account // 3. create subscription - err = c.DB.Transaction(func(tx *gorm.DB) error { - if err := tx.Where(&types.Account{UserUID: ops.UID}).FirstOrCreate(account).Error; err != nil { + err := c.DB.Transaction(func(tx *gorm.DB) error { + result := tx.Where(&types.Account{UserUID: ops.UID}).FirstOrCreate(account) + if err := result.Error; err != nil { return fmt.Errorf("failed to create account: %w", err) } + if result.RowsAffected == 0 { + return nil + } + freePlan, err := c.GetSubscriptionPlan(types.FreeSubscriptionPlanName) + if err != nil { + return fmt.Errorf("failed to get free plan: %w", err) + } + userInfo := &types.UserInfo{} + err = c.DB.Model(&types.UserInfo{}).Where(`"userUid" = ?`, ops.UID).Find(userInfo).Error + if err != nil && err != gorm.ErrRecordNotFound { + return fmt.Errorf("failed to get user info: %w", err) + } + githubDetection := true + if userInfo.Config != nil { + if userInfo.Config.Github.CreatedAt != "" { + createdAt, err := time.Parse(time.RFC3339, userInfo.Config.Github.CreatedAt) + if err != nil { + return fmt.Errorf("failed to parse github created at: %w", err) + } + if time.Since(createdAt) < 7*24*time.Hour { + githubDetection = false + } + } + } if freePlan.GiftAmount > 0 && githubDetection { credits := &types.Credits{ ID: uuid.New(), diff --git a/controllers/user/controllers/user_controller.go b/controllers/user/controllers/user_controller.go index e92296d9b..a6651ced8 100644 --- a/controllers/user/controllers/user_controller.go +++ b/controllers/user/controllers/user_controller.go @@ -157,7 +157,7 @@ func (r *UserReconciler) SetupWithManager(mgr ctrl.Manager, opts ratelimiter.Rat ownerEventHandler := handler.EnqueueRequestForOwner(r.Scheme, r.Client.RESTMapper(), &userv1.User{}, handler.OnlyControllerOwner()) return ctrl.NewControllerManagedBy(mgr). - For(&userv1.User{}, builder.WithPredicates(predicate.Or(predicate.GenerationChangedPredicate{}, predicate.AnnotationChangedPredicate{}))). + For(&userv1.User{}, builder.WithPredicates(predicate.Or(predicate.GenerationChangedPredicate{}))). Watches(&rbacv1.Role{}, ownerEventHandler). Watches(&rbacv1.RoleBinding{}, ownerEventHandler). Watches(&v1.Secret{}, ownerEventHandler). diff --git a/service/account/api/flush.go b/service/account/api/flush.go index ab9590453..06b19ae3a 100644 --- a/service/account/api/flush.go +++ b/service/account/api/flush.go @@ -290,7 +290,12 @@ func AdminFlushSubscriptionQuota(c *gin.Context) { } for _, ns := range nsList { quota := getDefaultResourceQuota(ns, "quota-"+ns, rs) - if err = dao.K8sManager.GetClient().Update(context.Background(), quota); err != nil { + hard := quota.Spec.Hard.DeepCopy() + _, err = controllerutil.CreateOrUpdate(context.Background(), dao.K8sManager.GetClient(), quota, func() error { + quota.Spec.Hard = hard + return nil + }) + if err != nil { c.JSON(http.StatusInternalServerError, helper.ErrorMessage{Error: fmt.Sprintf("update resource quota failed: %v", err)}) return }