mirror of
https://github.com/labring/sealos.git
synced 2026-09-01 04:58:47 +08:00
optimize transfer controller, add encryption to account and adapt (#3463)
* optimize transfer controller, add encryption to account and adapt to existing environment * fix semgrep ci
This commit is contained in:
@@ -63,13 +63,13 @@ type AccountSpec struct{}
|
||||
// AccountStatus defines the observed state of Account
|
||||
type AccountStatus struct {
|
||||
// EncryptBalance is to encrypt balance
|
||||
EncryptBalance string `json:"encryptBalance,omitempty"`
|
||||
EncryptBalance *string `json:"encryptBalance,omitempty"`
|
||||
// Recharge amount
|
||||
Balance int64 `json:"balance,omitempty"`
|
||||
//Deduction amount
|
||||
DeductionBalance int64 `json:"deductionBalance,omitempty"`
|
||||
// EncryptDeductionBalance is to encrypt DeductionBalance
|
||||
EncryptDeductionBalance string `json:"encryptDeductionBalance,omitempty"`
|
||||
EncryptDeductionBalance *string `json:"encryptDeductionBalance,omitempty"`
|
||||
// delete in the future
|
||||
ChargeList []Charge `json:"chargeList,omitempty"`
|
||||
}
|
||||
|
||||
@@ -17,6 +17,8 @@ limitations under the License.
|
||||
package v1
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
@@ -34,7 +36,8 @@ const (
|
||||
|
||||
// TransferSpec defines the desired state of Transfer
|
||||
type TransferSpec struct {
|
||||
To string `json:"to"`
|
||||
From string `json:"from"`
|
||||
To string `json:"to"`
|
||||
// +kubebuilder:validation:Minimum=1000000
|
||||
Amount int64 `json:"amount"`
|
||||
}
|
||||
@@ -69,3 +72,17 @@ type TransferList struct {
|
||||
func init() {
|
||||
SchemeBuilder.Register(&Transfer{}, &TransferList{})
|
||||
}
|
||||
|
||||
func (t *Transfer) ToJSON() string {
|
||||
return `{
|
||||
"spec": {
|
||||
"from": "` + t.Spec.From + `",
|
||||
"to": "` + t.Spec.To + `",
|
||||
"amount": ` + fmt.Sprint(t.Spec.Amount) + `
|
||||
},
|
||||
"status": {
|
||||
"reason": "` + t.Status.Reason + `",
|
||||
"progress": "` + string(rune(t.Status.Progress)) + `"
|
||||
}
|
||||
}`
|
||||
}
|
||||
|
||||
@@ -70,6 +70,12 @@ spec:
|
||||
description: Deduction amount
|
||||
format: int64
|
||||
type: integer
|
||||
encryptBalance:
|
||||
description: EncryptBalance is to encrypt balance
|
||||
type: string
|
||||
encryptDeductionBalance:
|
||||
description: EncryptDeductionBalance is to encrypt DeductionBalance
|
||||
type: string
|
||||
type: object
|
||||
type: object
|
||||
served: true
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
---
|
||||
apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
controller-gen.kubebuilder.io/version: v0.8.0
|
||||
creationTimestamp: null
|
||||
name: transfers.account.sealos.io
|
||||
spec:
|
||||
group: account.sealos.io
|
||||
names:
|
||||
kind: Transfer
|
||||
listKind: TransferList
|
||||
plural: transfers
|
||||
singular: transfer
|
||||
scope: Namespaced
|
||||
versions:
|
||||
- name: v1
|
||||
schema:
|
||||
openAPIV3Schema:
|
||||
description: Transfer is the Schema for the transfers API
|
||||
properties:
|
||||
apiVersion:
|
||||
description: 'APIVersion defines the versioned schema of this representation
|
||||
of an object. Servers should convert recognized schemas to the latest
|
||||
internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
|
||||
type: string
|
||||
kind:
|
||||
description: 'Kind is a string value representing the REST resource this
|
||||
object represents. Servers may infer this from the endpoint the client
|
||||
submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
|
||||
type: string
|
||||
metadata:
|
||||
type: object
|
||||
spec:
|
||||
description: TransferSpec defines the desired state of Transfer
|
||||
properties:
|
||||
amount:
|
||||
format: int64
|
||||
minimum: 1000000
|
||||
type: integer
|
||||
from:
|
||||
type: string
|
||||
to:
|
||||
type: string
|
||||
required:
|
||||
- amount
|
||||
- to
|
||||
type: object
|
||||
status:
|
||||
description: TransferStatus defines the observed state of Transfer
|
||||
properties:
|
||||
progress:
|
||||
type: integer
|
||||
reason:
|
||||
type: string
|
||||
type: object
|
||||
type: object
|
||||
served: true
|
||||
storage: true
|
||||
subresources:
|
||||
status: {}
|
||||
status:
|
||||
acceptedNames:
|
||||
kind: ""
|
||||
plural: ""
|
||||
conditions: []
|
||||
storedVersions: []
|
||||
@@ -0,0 +1,24 @@
|
||||
# permissions for end users to edit transfers.
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
name: transfer-editor-role
|
||||
rules:
|
||||
- apiGroups:
|
||||
- account.sealos.io
|
||||
resources:
|
||||
- transfers
|
||||
verbs:
|
||||
- create
|
||||
- delete
|
||||
- get
|
||||
- list
|
||||
- patch
|
||||
- update
|
||||
- watch
|
||||
- apiGroups:
|
||||
- account.sealos.io
|
||||
resources:
|
||||
- transfers/status
|
||||
verbs:
|
||||
- get
|
||||
@@ -0,0 +1,20 @@
|
||||
# permissions for end users to view transfers.
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
name: transfer-viewer-role
|
||||
rules:
|
||||
- apiGroups:
|
||||
- account.sealos.io
|
||||
resources:
|
||||
- transfers
|
||||
verbs:
|
||||
- get
|
||||
- list
|
||||
- watch
|
||||
- apiGroups:
|
||||
- account.sealos.io
|
||||
resources:
|
||||
- transfers/status
|
||||
verbs:
|
||||
- get
|
||||
@@ -0,0 +1,6 @@
|
||||
apiVersion: account.sealos.io/v1
|
||||
kind: Transfer
|
||||
metadata:
|
||||
name: transfer-sample
|
||||
spec:
|
||||
# TODO(user): Add fields here
|
||||
@@ -23,6 +23,11 @@ import (
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"sigs.k8s.io/controller-runtime/pkg/event"
|
||||
"sigs.k8s.io/controller-runtime/pkg/predicate"
|
||||
|
||||
"sigs.k8s.io/controller-runtime/pkg/builder"
|
||||
|
||||
"github.com/labring/sealos/controllers/pkg/crypto"
|
||||
|
||||
retry2 "k8s.io/client-go/util/retry"
|
||||
@@ -58,7 +63,6 @@ import (
|
||||
|
||||
const (
|
||||
ACCOUNTNAMESPACEENV = "ACCOUNT_NAMESPACE"
|
||||
PrivateDeployEnv = "PRIVATE_DEPLOY"
|
||||
DEFAULTACCOUNTNAMESPACE = "sealos-system"
|
||||
AccountAnnotationNewAccount = "account.sealos.io/new-account"
|
||||
NEWACCOUNTAMOUNTENV = "NEW_ACCOUNT_AMOUNT"
|
||||
@@ -66,7 +70,6 @@ const (
|
||||
|
||||
// AccountReconciler reconciles a Account object
|
||||
type AccountReconciler struct {
|
||||
PrivateDeploy bool
|
||||
client.Client
|
||||
Scheme *runtime.Scheme
|
||||
Logger logr.Logger
|
||||
@@ -99,8 +102,10 @@ func (r *AccountReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ct
|
||||
|
||||
accountBalance := accountv1.AccountBalance{}
|
||||
if err := r.Get(ctx, req.NamespacedName, &accountBalance); err == nil {
|
||||
if err := r.updateDeductionBalance(ctx, &accountBalance); err != nil {
|
||||
r.Logger.Error(err, err.Error())
|
||||
err = retry2.RetryOnConflict(retry2.DefaultBackoff, func() error {
|
||||
return r.updateDeductionBalance(ctx, &accountBalance)
|
||||
})
|
||||
if err != nil {
|
||||
return ctrl.Result{}, err
|
||||
}
|
||||
} else if client.IgnoreNotFound(err) != nil {
|
||||
@@ -126,21 +131,6 @@ func (r *AccountReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ct
|
||||
return ctrl.Result{}, fmt.Errorf("get account failed: %v", err)
|
||||
}
|
||||
|
||||
if r.PrivateDeploy {
|
||||
encryptBalance := account.Status.EncryptBalance
|
||||
encryptDeductionBalance := account.Status.EncryptDeductionBalance
|
||||
balance, err := crypto.DecryptInt64(encryptBalance)
|
||||
if err != nil {
|
||||
return ctrl.Result{}, fmt.Errorf("decrypt balance failed: %v", err)
|
||||
}
|
||||
account.Status.Balance = balance
|
||||
deductionBalance, err := crypto.DecryptInt64(encryptDeductionBalance)
|
||||
if err != nil {
|
||||
return ctrl.Result{}, fmt.Errorf("decrypt deduction balance failed: %v", err)
|
||||
}
|
||||
account.Status.DeductionBalance = deductionBalance
|
||||
}
|
||||
|
||||
orderResp, err := pay.QueryOrder(payment.Status.TradeNO)
|
||||
if err != nil {
|
||||
return ctrl.Result{}, fmt.Errorf("query order failed: %v", err)
|
||||
@@ -163,17 +153,11 @@ func (r *AccountReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ct
|
||||
now := time.Now().UTC()
|
||||
payAmount := *orderResp.Amount.Total * 10000
|
||||
//1¥ = 100WechatPayAmount; 1 WechatPayAmount = 10000 SealosAmount
|
||||
var gift = giveGift(payAmount)
|
||||
if r.PrivateDeploy {
|
||||
encryptBalance := account.Status.EncryptBalance
|
||||
encryptBalance, err = crypto.RechargeBalance(encryptBalance, payAmount+gift)
|
||||
if err != nil {
|
||||
return ctrl.Result{}, fmt.Errorf("recharge encrypt balance failed: %v", err)
|
||||
}
|
||||
account.Status.EncryptBalance = encryptBalance
|
||||
err = crypto.RechargeBalance(account.Status.EncryptBalance, giveGift(payAmount))
|
||||
if err != nil {
|
||||
return ctrl.Result{}, fmt.Errorf("recharge encrypt balance failed: %v", err)
|
||||
}
|
||||
account.Status.Balance += payAmount + gift
|
||||
if err := r.Status().Update(ctx, account); err != nil {
|
||||
if err := r.updateAccountStatus(ctx, account); err != nil {
|
||||
return ctrl.Result{}, fmt.Errorf("update account failed: %v", err)
|
||||
}
|
||||
payment.Status.Status = pay.StatusSuccess
|
||||
@@ -255,17 +239,16 @@ func (r *AccountReconciler) syncAccount(ctx context.Context, name, accountNamesp
|
||||
}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if r.PrivateDeploy {
|
||||
encryptBalance := account.Status.EncryptBalance
|
||||
encryptBalance, err = crypto.RechargeBalance(encryptBalance, int64(amount))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("recharge balance failed: %v", err)
|
||||
}
|
||||
account.Status.EncryptBalance = encryptBalance
|
||||
err = r.initBalance(&account)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("sync init balance failed: %v", err)
|
||||
}
|
||||
account.Status.Balance += int64(amount)
|
||||
if err := r.Status().Update(ctx, &account); err != nil {
|
||||
return nil, err
|
||||
err = crypto.RechargeBalance(account.Status.EncryptBalance, int64(amount))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("recharge balance failed: %v", err)
|
||||
}
|
||||
if err := r.updateAccountStatus(ctx, &account); err != nil {
|
||||
return nil, fmt.Errorf("update account failed: %v", err)
|
||||
}
|
||||
r.Logger.Info("account created,will charge new account some money", "account", account, "stringAmount", stringAmount)
|
||||
|
||||
@@ -376,33 +359,34 @@ func (r *AccountReconciler) updateDeductionBalance(ctx context.Context, accountB
|
||||
r.Logger.Error(err, err.Error())
|
||||
return err
|
||||
}
|
||||
|
||||
if accountBalance.Spec.Type == accountv1.TransferIn {
|
||||
account.Status.Balance += accountBalance.Spec.Amount
|
||||
} else {
|
||||
account.Status.DeductionBalance += accountBalance.Spec.Amount
|
||||
err = r.initBalance(account)
|
||||
if err != nil {
|
||||
return fmt.Errorf("sync balance failed: %v", err)
|
||||
}
|
||||
if r.PrivateDeploy {
|
||||
if accountBalance.Spec.Type == accountv1.TransferIn {
|
||||
encryptBalance := account.Status.EncryptBalance
|
||||
encryptBalance, err = crypto.RechargeBalance(encryptBalance, accountBalance.Spec.Amount)
|
||||
if err != nil {
|
||||
r.Logger.Error(err, err.Error())
|
||||
return err
|
||||
}
|
||||
account.Status.EncryptBalance = encryptBalance
|
||||
} else {
|
||||
encryptDeductionBalance := account.Status.EncryptDeductionBalance
|
||||
encryptDeductionBalance, err = crypto.RechargeBalance(encryptDeductionBalance, accountBalance.Spec.Amount)
|
||||
if err != nil {
|
||||
r.Logger.Error(err, err.Error())
|
||||
return err
|
||||
}
|
||||
account.Status.EncryptDeductionBalance = encryptDeductionBalance
|
||||
switch accountBalance.Spec.Type {
|
||||
case accountv1.TransferIn:
|
||||
err = crypto.RechargeBalance(account.Status.EncryptBalance, accountBalance.Spec.Amount)
|
||||
if err != nil {
|
||||
r.Logger.Error(err, err.Error())
|
||||
return err
|
||||
}
|
||||
case accountv1.TransferOut:
|
||||
err = crypto.DeductBalance(account.Status.EncryptBalance, accountBalance.Spec.Amount)
|
||||
if err != nil {
|
||||
r.Logger.Error(err, err.Error())
|
||||
return err
|
||||
}
|
||||
case accountv1.Consumption:
|
||||
err = crypto.RechargeBalance(account.Status.EncryptDeductionBalance, accountBalance.Spec.Amount)
|
||||
if err != nil {
|
||||
r.Logger.Error(err, err.Error())
|
||||
return err
|
||||
}
|
||||
default:
|
||||
return fmt.Errorf("unknown accountbalance type: %v", accountBalance.Spec.Type)
|
||||
}
|
||||
|
||||
if err := r.Status().Update(ctx, account); err != nil {
|
||||
if err := r.updateAccountStatus(ctx, account); err != nil {
|
||||
r.Logger.Error(err, err.Error())
|
||||
return err
|
||||
}
|
||||
@@ -431,6 +415,38 @@ func (r *AccountReconciler) updateDeductionBalance(ctx context.Context, accountB
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *AccountReconciler) updateAccountStatus(ctx context.Context, account *accountv1.Account) error {
|
||||
balance, err := crypto.DecryptInt64(*account.Status.EncryptBalance)
|
||||
if err != nil {
|
||||
return fmt.Errorf("update decrypt balance failed: %v", err)
|
||||
}
|
||||
deductionBalance, err := crypto.DecryptInt64(*account.Status.EncryptDeductionBalance)
|
||||
if err != nil {
|
||||
return fmt.Errorf("update decrypt deduction balance failed: %v", err)
|
||||
}
|
||||
account.Status.Balance = balance
|
||||
account.Status.DeductionBalance = deductionBalance
|
||||
return r.Status().Update(ctx, account)
|
||||
}
|
||||
|
||||
func (r *AccountReconciler) initBalance(account *accountv1.Account) (err error) {
|
||||
if account.Status.EncryptBalance == nil {
|
||||
encryptBalance, err := crypto.EncryptInt64(account.Status.Balance)
|
||||
if err != nil {
|
||||
return fmt.Errorf("sync encrypt balance failed: %v", err)
|
||||
}
|
||||
account.Status.EncryptBalance = encryptBalance
|
||||
}
|
||||
if account.Status.EncryptDeductionBalance == nil {
|
||||
encryptDeductionBalance, err := crypto.EncryptInt64(account.Status.DeductionBalance)
|
||||
if err != nil {
|
||||
return fmt.Errorf("sync encrypt deduction balance failed: %v", err)
|
||||
}
|
||||
account.Status.EncryptDeductionBalance = encryptDeductionBalance
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetupWithManager sets up the controller with the Manager.
|
||||
func (r *AccountReconciler) SetupWithManager(mgr ctrl.Manager, rateOpts controller.Options) error {
|
||||
const controllerName = "account_controller"
|
||||
@@ -447,12 +463,33 @@ func (r *AccountReconciler) SetupWithManager(mgr ctrl.Manager, rateOpts controll
|
||||
return ctrl.NewControllerManagedBy(mgr).
|
||||
For(&accountv1.Account{}).
|
||||
Watches(&source.Kind{Type: &accountv1.Payment{}}, &handler.EnqueueRequestForObject{}).
|
||||
Watches(&source.Kind{Type: &accountv1.AccountBalance{}}, &handler.EnqueueRequestForObject{}).
|
||||
Watches(&source.Kind{Type: &accountv1.AccountBalance{}}, &handler.EnqueueRequestForObject{}, builder.WithPredicates(&NamespaceFilterPredicate{Namespace: r.AccountSystemNamespace})).
|
||||
Watches(&source.Kind{Type: &userV1.User{}}, &handler.EnqueueRequestForObject{}).
|
||||
WithOptions(rateOpts).
|
||||
Complete(r)
|
||||
}
|
||||
|
||||
type NamespaceFilterPredicate struct {
|
||||
Namespace string
|
||||
predicate.Funcs
|
||||
}
|
||||
|
||||
func (p *NamespaceFilterPredicate) Create(e event.CreateEvent) bool {
|
||||
return e.Object.GetNamespace() == p.Namespace
|
||||
}
|
||||
|
||||
func (p *NamespaceFilterPredicate) Delete(e event.DeleteEvent) bool {
|
||||
return e.Object.GetNamespace() == p.Namespace
|
||||
}
|
||||
|
||||
func (p *NamespaceFilterPredicate) Update(e event.UpdateEvent) bool {
|
||||
return e.ObjectOld.GetNamespace() == p.Namespace
|
||||
}
|
||||
|
||||
func (p *NamespaceFilterPredicate) Generic(e event.GenericEvent) bool {
|
||||
return e.Object.GetNamespace() == p.Namespace
|
||||
}
|
||||
|
||||
const (
|
||||
BaseUnit = 1_000_000
|
||||
Threshold1 = 299 * BaseUnit
|
||||
@@ -483,5 +520,5 @@ func giveGift(amount int64) int64 {
|
||||
default:
|
||||
ratio = Ratio5
|
||||
}
|
||||
return amount * ratio / 100
|
||||
return (amount * ratio / 100) + amount
|
||||
}
|
||||
|
||||
@@ -67,10 +67,10 @@ func (r *TransferReconciler) Reconcile(ctx context.Context, req ctrl.Request) (c
|
||||
if err := r.Get(ctx, req.NamespacedName, &transfer); err != nil {
|
||||
return ctrl.Result{}, client.IgnoreNotFound(err)
|
||||
}
|
||||
transfer.Spec.From = getUsername(transfer.Namespace)
|
||||
if time.Since(transfer.CreationTimestamp.Time) > time.Minute*3 {
|
||||
return ctrl.Result{}, r.Delete(ctx, &transfer)
|
||||
}
|
||||
transfer.Status.Progress = accountv1.TransferStateCompleted
|
||||
pipeLine := []func(ctx context.Context, transfer *accountv1.Transfer) error{
|
||||
r.check,
|
||||
r.TransferOutSaver,
|
||||
@@ -83,6 +83,9 @@ func (r *TransferReconciler) Reconcile(ctx context.Context, req ctrl.Request) (c
|
||||
break
|
||||
}
|
||||
}
|
||||
if transfer.Status.Progress != accountv1.TransferStateFailed {
|
||||
transfer.Status.Progress = accountv1.TransferStateCompleted
|
||||
}
|
||||
if err := r.Status().Update(ctx, &transfer); err != nil {
|
||||
return ctrl.Result{}, fmt.Errorf("update transfer status failed: %w", err)
|
||||
}
|
||||
@@ -124,6 +127,7 @@ func (r *TransferReconciler) TransferOutSaver(ctx context.Context, transfer *acc
|
||||
Owner: getUsername(transfer.Namespace),
|
||||
Time: metav1.Time{Time: time.Now().UTC()},
|
||||
Type: accountv1.TransferOut,
|
||||
Details: transfer.ToJSON(),
|
||||
}
|
||||
from := accountv1.AccountBalance{
|
||||
ObjectMeta: objMeta,
|
||||
@@ -150,6 +154,7 @@ func (r *TransferReconciler) TransferInSaver(ctx context.Context, transfer *acco
|
||||
Owner: getUsername(transfer.Spec.To),
|
||||
Time: metav1.Time{Time: time.Now().UTC()},
|
||||
Type: accountv1.TransferIn,
|
||||
Details: transfer.ToJSON(),
|
||||
}
|
||||
to := accountv1.AccountBalance{
|
||||
ObjectMeta: objMeta,
|
||||
@@ -168,6 +173,9 @@ func (r *TransferReconciler) check(ctx context.Context, transfer *accountv1.Tran
|
||||
if transfer.Status.Progress == accountv1.TransferStateFailed {
|
||||
return fmt.Errorf(transfer.Status.Reason)
|
||||
}
|
||||
if transfer.Status.Progress == accountv1.TransferStateCompleted {
|
||||
return fmt.Errorf("transfer already completed")
|
||||
}
|
||||
from := transfer.Namespace
|
||||
to := transfer.Spec.To
|
||||
fromAccount := accountv1.Account{}
|
||||
|
||||
@@ -160,6 +160,12 @@ spec:
|
||||
description: Deduction amount
|
||||
format: int64
|
||||
type: integer
|
||||
encryptBalance:
|
||||
description: EncryptBalance is to encrypt balance
|
||||
type: string
|
||||
encryptDeductionBalance:
|
||||
description: EncryptDeductionBalance is to encrypt DeductionBalance
|
||||
type: string
|
||||
type: object
|
||||
type: object
|
||||
served: true
|
||||
@@ -551,6 +557,8 @@ spec:
|
||||
format: int64
|
||||
minimum: 1000000
|
||||
type: integer
|
||||
from:
|
||||
type: string
|
||||
to:
|
||||
type: string
|
||||
required:
|
||||
|
||||
@@ -181,16 +181,13 @@ func (r *LicenseReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ct
|
||||
}
|
||||
charge := amount * cloud.BaseCount
|
||||
account.Status.Balance += charge
|
||||
encryptBalance := account.Status.EncryptBalance
|
||||
encryptBalance, err = crypto.RechargeBalance(encryptBalance, charge)
|
||||
err = crypto.RechargeBalance(account.Status.EncryptBalance, charge)
|
||||
if err != nil {
|
||||
r.logger.Error(err, "Recharge Failed, failed to encrypt the balance", "namespace:", req.Namespace, "name:", req.Name)
|
||||
pack := cloud.NewNotificationPackage(cloud.RechargeFailedTitle, cloud.SEALOS, cloud.RechargeFailedContent)
|
||||
util.SubmitNotificationWithUser(ctx, r.Client, r.logger, req.Namespace, pack)
|
||||
return ctrl.Result{RequeueAfter: time.Second * 5}, nil
|
||||
}
|
||||
|
||||
account.Status.EncryptBalance = encryptBalance
|
||||
err = r.Client.Status().Update(ctx, &account)
|
||||
if err != nil {
|
||||
r.logger.Error(err, "Recharge Failed, failed to modify the status", "namespace:", req.Namespace, "name:", req.Name)
|
||||
|
||||
@@ -56,8 +56,9 @@ func Encrypt(plaintext []byte) (string, error) {
|
||||
return base64.StdEncoding.EncodeToString(append(nonce, ciphertext...)), nil
|
||||
}
|
||||
|
||||
func EncryptInt64(in int64) (string, error) {
|
||||
return Encrypt([]byte(strconv.FormatInt(in, 10)))
|
||||
func EncryptInt64(in int64) (*string, error) {
|
||||
out, err := Encrypt([]byte(strconv.FormatInt(in, 10)))
|
||||
return &out, err
|
||||
}
|
||||
|
||||
func DecryptInt64(in string) (int64, error) {
|
||||
@@ -68,13 +69,32 @@ func DecryptInt64(in string) (int64, error) {
|
||||
return strconv.ParseInt(string(out), 10, 64)
|
||||
}
|
||||
|
||||
func RechargeBalance(balance string, amount int64) (string, error) {
|
||||
balanceInt, err := DecryptInt64(balance)
|
||||
func RechargeBalance(rawBalance *string, amount int64) error {
|
||||
balanceInt, err := DecryptInt64(*rawBalance)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to recharge balance: %w", err)
|
||||
return fmt.Errorf("failed to recharge balance: %w", err)
|
||||
}
|
||||
balanceInt += amount
|
||||
return EncryptInt64(balanceInt)
|
||||
encryptBalance, err := EncryptInt64(balanceInt)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to recharge balance: %w", err)
|
||||
}
|
||||
*rawBalance = *encryptBalance
|
||||
return nil
|
||||
}
|
||||
|
||||
func DeductBalance(balance *string, amount int64) error {
|
||||
balanceInt, err := DecryptInt64(*balance)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to deduct balance: %w", err)
|
||||
}
|
||||
balanceInt -= amount
|
||||
encryptBalance, err := EncryptInt64(balanceInt)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to deduct balance: %w", err)
|
||||
}
|
||||
*balance = *encryptBalance
|
||||
return nil
|
||||
}
|
||||
|
||||
// Decrypt decrypts the given ciphertext using AES-GCM.
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
package crypto
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestRechargeBalance(t *testing.T) {
|
||||
rawInt := int64(100_000_000)
|
||||
addInt := int64(200_000_000)
|
||||
expectedInt := int64(300_000_000)
|
||||
|
||||
account := createEncryptedAccount(t, rawInt)
|
||||
|
||||
rechargeAccount(t, account, addInt)
|
||||
|
||||
validateAccount(t, account, expectedInt, "recharge balance failed")
|
||||
}
|
||||
|
||||
func TestDeductBalance(t *testing.T) {
|
||||
rawInt := int64(300_000_000)
|
||||
deductInt := int64(200_000_000)
|
||||
expectedInt := int64(100_000_000)
|
||||
|
||||
account := createEncryptedAccount(t, rawInt)
|
||||
|
||||
deductAccount(t, account, deductInt)
|
||||
|
||||
validateAccount(t, account, expectedInt, "deduct balance failed")
|
||||
}
|
||||
|
||||
func createEncryptedAccount(t *testing.T, rawInt int64) *string {
|
||||
account, err := EncryptInt64(rawInt)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
return account
|
||||
}
|
||||
|
||||
func rechargeAccount(t *testing.T, account *string, amount int64) {
|
||||
err := RechargeBalance(account, amount)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func deductAccount(t *testing.T, account *string, amount int64) {
|
||||
err := DeductBalance(account, amount)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func validateAccount(t *testing.T, account *string, expectedInt int64, errMsg string) {
|
||||
decryptedInt, err := DecryptInt64(*account)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if decryptedInt != expectedInt {
|
||||
t.Fatalf("%s: expected %d, got %d", errMsg, expectedInt, decryptedInt)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user