Skip to content

Commit 2820958

Browse files
authored
fix(region): 修复本地到期释放功能和资源同步冲突 (#22859)
1 parent f2e0f62 commit 2820958

32 files changed

+298
-524
lines changed

pkg/apis/compute/api.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
package compute
1616

1717
import (
18+
"time"
19+
1820
"yunion.io/x/jsonutils"
1921

2022
"yunion.io/x/onecloud/pkg/apis"
@@ -648,6 +650,8 @@ type ServerCreateInput struct {
648650
BillingType string `json:"billing_type"`
649651
// swagger:ignore
650652
BillingCycle string `json:"billing_cycle"`
653+
// 到期释放时间
654+
ReleaseAt time.Time `json:"release_at"`
651655

652656
// swagger:ignore
653657
// Deprecated

pkg/apis/compute/dbinstance.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -82,8 +82,8 @@ type DBInstanceCreateInput struct {
8282
// default: false
8383
AutoRenew bool `json:"auto_renew"`
8484

85-
// swagger:ignore
86-
ExpiredAt time.Time `json:"expired_at"`
85+
// 到期释放时间
86+
ReleaseAt time.Time `json:"release_at"`
8787

8888
// 计费方式
8989
// enum: ["postpaid", "prepaid"]

pkg/apis/compute/elasticcache.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -246,13 +246,13 @@ type ElasticcacheCreateInput struct {
246246
// 包年包月时间周期
247247
Duration string `json:"duration"`
248248

249+
// 到期释放时间
250+
ReleaseAt time.Time `json:"release_at"`
251+
249252
// 是否自动续费(仅包年包月时生效)
250253
// default: false
251254
AutoRenew bool `json:"auto_renew"`
252255

253-
// swagger:ignore
254-
ExpiredAt time.Time `json:"expired_at"`
255-
256256
// 计费方式
257257
// enum: ["postpaid", "prepaid"]
258258
BillingType string

pkg/apis/compute/filesystem.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -87,8 +87,8 @@ type FileSystemCreateInput struct {
8787
// default: false
8888
AutoRenew bool `json:"auto_renew"`
8989

90-
// 到期释放时间,仅后付费支持
91-
ExpiredAt time.Time `json:"expired_at"`
90+
// 到期释放时间
91+
ReleaseAt time.Time `json:"release_at"`
9292

9393
// 计费方式
9494
// enum: ["postpaid", "prepaid"]

pkg/apis/compute/natgateway.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -117,8 +117,8 @@ type NatgatewayCreateInput struct {
117117
// default: false
118118
AutoRenew bool `json:"auto_renew"`
119119

120-
// 到期释放时间,仅后付费支持
121-
ExpiredAt time.Time `json:"expired_at"`
120+
// 到期释放时间
121+
ReleaseAt time.Time `json:"release_at"`
122122

123123
// 计费方式
124124
// enum: ["postpaid", "prepaid"]

pkg/apis/input.go

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,13 @@
1414

1515
package apis
1616

17-
import "time"
17+
import (
18+
"time"
19+
20+
"yunion.io/x/pkg/util/billing"
21+
22+
"yunion.io/x/onecloud/pkg/httperrors"
23+
)
1824

1925
type DomainizedResourceInput struct {
2026
// 指定项目归属域名称或ID
@@ -390,8 +396,25 @@ type DistinctFieldsInput struct {
390396
}
391397

392398
type PostpaidExpireInput struct {
393-
Duration string `json:"duration"`
394-
ExpireTime time.Time `json:"expire_time"`
399+
Duration string `json:"duration"`
400+
// swagger:ignore
401+
ExpireTime time.Time `json:"expire_time" yunion-deprecated-by:"release_at"`
402+
// 到期释放时间
403+
ReleaseAt time.Time `json:"release_at"`
404+
}
405+
406+
func (input *PostpaidExpireInput) GetReleaseAt() (time.Time, error) {
407+
if !input.ReleaseAt.IsZero() {
408+
return input.ReleaseAt, nil
409+
}
410+
if len(input.Duration) == 0 {
411+
return time.Time{}, httperrors.NewInputParameterError("missing duration/expire_time")
412+
}
413+
bc, err := billing.ParseBillingCycle(input.Duration)
414+
if err != nil {
415+
return time.Time{}, httperrors.NewInputParameterError("invalid duration: %s", input.Duration)
416+
}
417+
return bc.EndAt(time.Now()), nil
395418
}
396419

397420
type AutoRenewInput struct {

pkg/cloudcommon/db/opslog_const.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,8 @@ const (
5151
ACT_BACKUP_START = "backup_start"
5252
ACT_BACKUP_START_FAILED = "backup_start_fail"
5353

54+
ACT_SET_RELEASE_TIME = "set_release_time"
55+
5456
ACT_FREEZE = "freeze"
5557
ACT_FREEZE_FAIL = "freeze_fail"
5658
ACT_UNFREEZE = "unfreeze"

pkg/compute/guestdrivers/base.go

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ func (drv *SBaseGuestDriver) OnGuestCreateTaskComplete(ctx context.Context, gues
7878
if len(duration) > 0 {
7979
bc, err := billing.ParseBillingCycle(duration)
8080
if err == nil && guest.ExpiredAt.IsZero() {
81-
guest.SaveRenewInfo(ctx, task.GetUserCred(), &bc, nil, "")
81+
models.SaveRenewInfo(ctx, task.GetUserCred(), guest, &bc, nil, "")
8282
}
8383
if jsonutils.QueryBoolean(task.GetParams(), "auto_prepaid_recycle", false) {
8484
err := guest.CanPerformPrepaidRecycle()
@@ -395,11 +395,6 @@ func (drv *SBaseGuestDriver) RequestSyncSecgroupsOnHost(ctx context.Context, gue
395395
return nil // do nothing
396396
}
397397

398-
func (drv *SBaseGuestDriver) CancelExpireTime(
399-
ctx context.Context, userCred mcclient.TokenCredential, guest *models.SGuest) error {
400-
return guest.CancelExpireTime(ctx, userCred)
401-
}
402-
403398
func (drv *SBaseGuestDriver) IsSupportPublicipToEip() bool {
404399
return false
405400
}

pkg/compute/guestdrivers/managedvirtual.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1040,6 +1040,7 @@ func (drv *SManagedVirtualizedGuestDriver) RequestChangeBillingType(ctx context.
10401040
guest.BillingType = ivm.GetBillingType()
10411041
guest.Status = ivm.GetStatus()
10421042
guest.ExpiredAt = time.Time{}
1043+
guest.AutoRenew = false
10431044
if guest.BillingType == billing_api.BILLING_TYPE_PREPAID {
10441045
guest.AutoRenew = ivm.IsAutoRenew()
10451046
guest.ExpiredAt = ivm.GetExpiredAt()
@@ -1293,7 +1294,7 @@ func (drv *SManagedVirtualizedGuestDriver) OnGuestDeployTaskDataReceived(ctx con
12931294

12941295
exp, err := data.GetTime("expired_at")
12951296
if err == nil && !guest.IsPrepaidRecycle() {
1296-
guest.SaveRenewInfo(ctx, task.GetUserCred(), nil, &exp, "")
1297+
models.SaveRenewInfo(ctx, task.GetUserCred(), guest, nil, &exp, "")
12971298
}
12981299

12991300
driver, _ := guest.GetDriver()

pkg/compute/models/billingresource.go

Lines changed: 83 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -26,26 +26,27 @@ import (
2626
"yunion.io/x/pkg/util/billing"
2727
"yunion.io/x/sqlchemy"
2828

29-
"yunion.io/x/onecloud/pkg/apis"
3029
api "yunion.io/x/onecloud/pkg/apis/billing"
3130
billing_api "yunion.io/x/onecloud/pkg/apis/billing"
3231
notifyapi "yunion.io/x/onecloud/pkg/apis/notify"
3332
"yunion.io/x/onecloud/pkg/cloudcommon/db"
3433
"yunion.io/x/onecloud/pkg/cloudcommon/notifyclient"
3534
"yunion.io/x/onecloud/pkg/compute/options"
36-
"yunion.io/x/onecloud/pkg/httperrors"
3735
"yunion.io/x/onecloud/pkg/mcclient"
3836
"yunion.io/x/onecloud/pkg/mcclient/auth"
3937
"yunion.io/x/onecloud/pkg/mcclient/modules/notify"
38+
"yunion.io/x/onecloud/pkg/util/logclient"
4039
"yunion.io/x/onecloud/pkg/util/stringutils2"
4140
)
4241

4342
type SBillingResourceBase struct {
4443
// 计费类型, 按量、包年包月
4544
// example: postpaid
4645
BillingType string `width:"36" charset:"ascii" nullable:"true" default:"postpaid" list:"user" create:"optional" json:"billing_type"`
47-
// 过期时间
48-
ExpiredAt time.Time `nullable:"true" list:"user" create:"optional" json:"expired_at"`
46+
// 包年包月到期时间
47+
ExpiredAt time.Time `nullable:"true" list:"user" json:"expired_at"`
48+
// 到期释放时间
49+
ReleaseAt time.Time `nullable:"true" list:"user" create:"optional" json:"release_at"`
4950
// 计费周期
5051
BillingCycle string `width:"10" charset:"ascii" nullable:"true" list:"user" create:"optional" json:"billing_cycle"`
5152
// 是否自动续费
@@ -62,10 +63,35 @@ func (self *SBillingResourceBase) GetChargeType() string {
6263
}
6364
}
6465

66+
func (self *SBillingResourceBase) SetReleaseAt(releaseAt time.Time) {
67+
self.ReleaseAt = releaseAt
68+
}
69+
70+
func (self *SBillingResourceBase) GetExpiredAt() time.Time {
71+
return self.ExpiredAt
72+
}
73+
74+
func (self *SBillingResourceBase) SetExpiredAt(expireAt time.Time) {
75+
self.ExpiredAt = expireAt
76+
}
77+
78+
func (self *SBillingResourceBase) SetBillingCycle(billingCycle string) {
79+
self.BillingCycle = billingCycle
80+
}
81+
82+
func (self *SBillingResourceBase) SetBillingType(billingType string) {
83+
self.BillingType = billingType
84+
}
85+
86+
func (self *SBillingResourceBase) GetBillingType() string {
87+
return self.BillingType
88+
}
89+
6590
func (self *SBillingResourceBase) getBillingBaseInfo() SBillingBaseInfo {
6691
info := SBillingBaseInfo{}
6792
info.ChargeType = self.GetChargeType()
6893
info.ExpiredAt = self.ExpiredAt
94+
info.ReleaseAt = self.ReleaseAt
6995
if self.GetChargeType() == api.BILLING_TYPE_PREPAID {
7096
info.BillingCycle = self.BillingCycle
7197
}
@@ -93,7 +119,7 @@ func (self *SBillingResourceBase) IsValidPrePaid() bool {
93119
func (self *SBillingResourceBase) IsValidPostPaid() bool {
94120
if self.BillingType == api.BILLING_TYPE_POSTPAID {
95121
now := time.Now().UTC()
96-
if self.ExpiredAt.After(now) {
122+
if self.ReleaseAt.After(now) {
97123
return true
98124
}
99125
}
@@ -103,6 +129,7 @@ func (self *SBillingResourceBase) IsValidPostPaid() bool {
103129
type SBillingBaseInfo struct {
104130
ChargeType string `json:",omitempty"`
105131
ExpiredAt time.Time `json:",omitempty"`
132+
ReleaseAt time.Time `json:",omitempty"`
106133
BillingCycle string `json:",omitempty"`
107134
}
108135

@@ -170,43 +197,14 @@ func (manager *SBillingResourceBaseManager) OrderByExtraFields(
170197
func ListExpiredPostpaidResources(
171198
q *sqlchemy.SQuery, limit int) *sqlchemy.SQuery {
172199
q = q.Equals("billing_type", api.BILLING_TYPE_POSTPAID)
173-
q = q.IsNotNull("expired_at")
174-
q = q.LT("expired_at", time.Now())
200+
q = q.IsNotNull("release_at")
201+
q = q.LT("release_at", time.Now())
175202
if limit > 0 {
176203
q = q.Limit(limit)
177204
}
178205
return q
179206
}
180207

181-
func ParseBillingCycleInput(billingBase *SBillingResourceBase, input apis.PostpaidExpireInput) (*billing.SBillingCycle, error) {
182-
var (
183-
bc billing.SBillingCycle
184-
err error
185-
durationStr string
186-
)
187-
if len(input.Duration) == 0 {
188-
if input.ExpireTime.IsZero() {
189-
return nil, httperrors.NewInputParameterError("missing duration/expire_time")
190-
}
191-
timeC := billingBase.ExpiredAt
192-
if timeC.IsZero() {
193-
timeC = time.Now()
194-
}
195-
dur := input.ExpireTime.Sub(timeC)
196-
if dur <= 0 {
197-
return nil, httperrors.NewInputParameterError("expire time is before current expire at")
198-
}
199-
bc = billing.DurationToBillingCycle(dur)
200-
} else {
201-
bc, err = billing.ParseBillingCycle(durationStr)
202-
if err != nil {
203-
return nil, httperrors.NewInputParameterError("invalid duration %s: %s", durationStr, err)
204-
}
205-
}
206-
207-
return &bc, nil
208-
}
209-
210208
type SBillingResourceCheckManager struct {
211209
db.SResourceBaseManager
212210
}
@@ -242,6 +240,55 @@ type IBillingModelManager interface {
242240
type IBillingModel interface {
243241
db.IModel
244242
GetExpiredAt() time.Time
243+
SetReleaseAt(releaseAt time.Time)
244+
SetExpiredAt(expireAt time.Time)
245+
SetBillingCycle(billingCycle string)
246+
SetBillingType(billingType string)
247+
GetBillingType() string
248+
}
249+
250+
func SaveReleaseAt(ctx context.Context, model IBillingModel, userCred mcclient.TokenCredential, releaseAt time.Time) error {
251+
diff, err := db.Update(model, func() error {
252+
model.SetReleaseAt(releaseAt)
253+
return nil
254+
})
255+
if err != nil {
256+
return errors.Wrap(err, "Update")
257+
}
258+
if len(diff) > 0 {
259+
db.OpsLog.LogEvent(model, db.ACT_SET_RELEASE_TIME, fmt.Sprintf("release at: %s", releaseAt), userCred)
260+
}
261+
if len(diff) > 0 && userCred != nil {
262+
logclient.AddActionLogWithContext(ctx, model, logclient.ACT_SET_RELEASE_TIME, diff, userCred, true)
263+
}
264+
return nil
265+
}
266+
267+
func SaveRenewInfo(
268+
ctx context.Context, userCred mcclient.TokenCredential,
269+
model IBillingModel, bc *billing.SBillingCycle, expireAt *time.Time, billingType string,
270+
) error {
271+
_, err := db.Update(model, func() error {
272+
if billingType == "" {
273+
billingType = billing_api.BILLING_TYPE_PREPAID
274+
}
275+
if model.GetBillingType() == "" {
276+
model.SetBillingType(billingType)
277+
}
278+
if expireAt != nil && !expireAt.IsZero() {
279+
model.SetExpiredAt(*expireAt)
280+
} else if bc != nil {
281+
model.SetBillingCycle(bc.String())
282+
model.SetExpiredAt(bc.EndAt(model.GetExpiredAt()))
283+
}
284+
return nil
285+
})
286+
if err != nil {
287+
log.Errorf("UpdateItem error %s", err)
288+
return err
289+
}
290+
db.OpsLog.LogEvent(model, db.ACT_RENEW, model.GetShortDesc(ctx), userCred)
291+
return nil
245292
}
246293

247294
func fetchExpiredModels(manager db.IModelManager, advanceDay int) ([]IBillingModel, error) {

0 commit comments

Comments
 (0)