Skip to content

Commit

Permalink
crypto prices && add default prices (labring#3575)
Browse files Browse the repository at this point in the history
default metering all queryProperties;
  • Loading branch information
bxy4543 authored Jul 26, 2023
1 parent 1cc7563 commit 9d234f6
Show file tree
Hide file tree
Showing 8 changed files with 121 additions and 29 deletions.
2 changes: 1 addition & 1 deletion controllers/account/controllers/account_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -460,7 +460,7 @@ func (r *AccountReconciler) SetupWithManager(mgr ctrl.Manager, rateOpts controll
if r.AccountSystemNamespace == "" {
r.AccountSystemNamespace = DEFAULTACCOUNTNAMESPACE
}
if r.MongoDBURI = os.Getenv(database.MongoURL); r.MongoDBURI == "" {
if r.MongoDBURI = os.Getenv(database.MongoURI); r.MongoDBURI == "" {
return fmt.Errorf("mongo url is empty")
}
return ctrl.NewControllerManagedBy(mgr).
Expand Down
12 changes: 6 additions & 6 deletions controllers/account/controllers/billing_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ const BillingAnnotationLastUpdateTime = "account.sealos.io/last-update-time"
type BillingReconciler struct {
client.Client
Scheme *runtime.Scheme
mongoURL string
mongoURI string
logr.Logger
AccountSystemNamespace string
}
Expand All @@ -77,7 +77,7 @@ type BillingReconciler struct {
func (r *BillingReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
r.Logger.V(1).Info("Reconcile Billing: ", "req.NamespacedName", req.NamespacedName)
dbCtx := context.Background()
dbClient, err := database.NewMongoDB(dbCtx, r.mongoURL)
dbClient, err := database.NewMongoDB(dbCtx, r.mongoURI)
if err != nil {
r.Logger.Error(err, "connect mongo client failed")
return ctrl.Result{Requeue: true}, err
Expand Down Expand Up @@ -161,7 +161,7 @@ func (r *BillingReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ct

func (r *BillingReconciler) billingWithHourTime(ctx context.Context, queryTime time.Time, nsListStr []string, ownNs string, dbClient database.Interface) error {
r.Logger.Info("queryTime", "queryTime", queryTime.Format(time.RFC3339), "ownNs", ownNs, "nsListStr", nsListStr)
billing, err := dbClient.GetMeteringOwnerTimeResult(queryTime, nsListStr, []string{"cpu", "memory", "storage"}, ownNs)
billing, err := dbClient.GetMeteringOwnerTimeResult(queryTime, nsListStr, nil, ownNs)
if err != nil {
return fmt.Errorf("get metering owner time result failed: %w", err)
}
Expand Down Expand Up @@ -201,7 +201,7 @@ func (r *BillingReconciler) billingWithHourTime(ctx context.Context, queryTime t

func (r *BillingReconciler) initDB() error {
dbCtx := context.Background()
mongoClient, err := database.NewMongoDB(dbCtx, r.mongoURL)
mongoClient, err := database.NewMongoDB(dbCtx, r.mongoURI)
if err != nil {
r.Logger.Error(err, "connect mongo client failed")
return err
Expand Down Expand Up @@ -256,8 +256,8 @@ func (r *BillingReconciler) initDB() error {

// SetupWithManager sets up the controller with the Manager.
func (r *BillingReconciler) SetupWithManager(mgr ctrl.Manager, rateOpts controller.Options) error {
if r.mongoURL = os.Getenv(database.MongoURL); r.mongoURL == "" {
return fmt.Errorf("env %s is empty", database.MongoURL)
if r.mongoURI = os.Getenv(database.MongoURI); r.mongoURI == "" {
return fmt.Errorf("env %s is empty", database.MongoURI)
}
r.Logger = ctrl.Log.WithName("controller").WithName("Billing")
if err := r.initDB(); err != nil {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -122,8 +122,8 @@ func CheckOpts(billingRecordQuery *accountv1.BillingRecordQuery) error {

// SetupWithManager sets up the controller with the Manager.
func (r *BillingRecordQueryReconciler) SetupWithManager(mgr ctrl.Manager, rateOpts controller.Options) error {
if r.MongoDBURI = os.Getenv(database.MongoURL); r.MongoDBURI == "" {
return fmt.Errorf("env %s is empty", database.MongoURL)
if r.MongoDBURI = os.Getenv(database.MongoURI); r.MongoDBURI == "" {
return fmt.Errorf("env %s is empty", database.MongoURI)
}
r.Logger = log.Log.WithName("billingrecordquery-controller")
return ctrl.NewControllerManagedBy(mgr).
Expand Down
25 changes: 25 additions & 0 deletions controllers/pkg/crypto/crypto.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,11 @@ var encryptionKey = []byte("0123456789ABCDEF0123456789ABCDEF")

// Encrypt encrypts the given plaintext using AES-GCM.
func Encrypt(plaintext []byte) (string, error) {
return EncryptWithKey(plaintext, encryptionKey)
}

// EncryptWithKey encrypts the given plaintext using AES-GCM.
func EncryptWithKey(plaintext []byte, encryptionKey []byte) (string, error) {
block, err := aes.NewCipher(encryptionKey)
if err != nil {
return "", err
Expand All @@ -61,6 +66,12 @@ func EncryptInt64(in int64) (*string, error) {
return &out, err
}

// EncryptWithKey encrypts the given plaintext using AES-GCM.
func EncryptInt64WithKey(in int64, encryptionKey []byte) (*string, error) {
out, err := EncryptWithKey([]byte(strconv.FormatInt(in, 10)), encryptionKey)
return &out, err
}

func DecryptInt64(in string) (int64, error) {
out, err := Decrypt(in)
if err != nil {
Expand All @@ -69,6 +80,15 @@ func DecryptInt64(in string) (int64, error) {
return strconv.ParseInt(string(out), 10, 64)
}

// DecryptInt64WithKey decrypts the given ciphertext using AES-GCM.
func DecryptInt64WithKey(in string, encryptionKey []byte) (int64, error) {
out, err := DecryptWithKey(in, encryptionKey)
if err != nil {
return 0, fmt.Errorf("failed to decrpt balance: %w", err)
}
return strconv.ParseInt(string(out), 10, 64)
}

func RechargeBalance(rawBalance *string, amount int64) error {
balanceInt, err := DecryptInt64(*rawBalance)
if err != nil {
Expand Down Expand Up @@ -99,6 +119,11 @@ func DeductBalance(balance *string, amount int64) error {

// Decrypt decrypts the given ciphertext using AES-GCM.
func Decrypt(ciphertextBase64 string) ([]byte, error) {
return DecryptWithKey(ciphertextBase64, encryptionKey)
}

// DecryptWithKey decrypts the given ciphertext using AES-GCM.
func DecryptWithKey(ciphertextBase64 string, encryptionKey []byte) ([]byte, error) {
ciphertext, err := base64.StdEncoding.DecodeString(ciphertextBase64)
if err != nil {
return nil, err
Expand Down
35 changes: 27 additions & 8 deletions controllers/pkg/database/mongodb.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import (
"strings"
"time"

"github.com/labring/sealos/controllers/pkg/crypto"

accountv1 "github.com/labring/sealos/controllers/account/api/v1"
"github.com/labring/sealos/controllers/pkg/common"
"github.com/labring/sealos/pkg/utils/logger"
Expand All @@ -27,11 +29,13 @@ const (
)

const (
MongoURL = "MONGO_URI"
MongoURI = "MONGO_URI"
MongoUsername = "MONGO_USERNAME"
MongoPassword = "MONGO_PASSWORD"
)

var cryptoKey = []byte("Af0b2Bc5e9d0C84adF0A5887cF43aB63")

type MongoDB struct {
URL string
Client *mongo.Client
Expand Down Expand Up @@ -101,12 +105,15 @@ func (m *MongoDB) SaveBillingsWithAccountBalance(accountBalanceSpec *accountv1.A
}

func (m *MongoDB) GetMeteringOwnerTimeResult(queryTime time.Time, queryCategories, queryProperties []string, queryOwner string) (*MeteringOwnerTimeResult, error) {
matchValue := bson.M{
"time": queryTime,
"category": bson.M{"$in": queryCategories},
}
if len(queryProperties) > 0 {
matchValue["property"] = bson.M{"$in": queryProperties}
}
pipeline := bson.A{
bson.D{{Key: "$match", Value: bson.M{
"time": queryTime,
"category": bson.M{"$in": queryCategories},
"property": bson.M{"$in": queryProperties},
}}},
bson.D{{Key: "$match", Value: matchValue}},
bson.D{{Key: "$group", Value: bson.M{
"_id": bson.M{"property": "$property"},
"propertyTotal": bson.M{"$sum": "$amount"},
Expand Down Expand Up @@ -176,13 +183,25 @@ func (m *MongoDB) GetAllPricesMap() (map[string]common.Price, error) {
if err != nil {
return nil, fmt.Errorf("get all prices error: %v", err)
}
var prices []common.Price
var prices []struct {
Property string `json:"property" bson:"property"`
Price string `json:"price" bson:"price"`
Detail string `json:"detail" bson:"detail"`
}
if err = cursor.All(ctx, &prices); err != nil {
return nil, fmt.Errorf("get all prices error: %v", err)
}
var pricesMap = make(map[string]common.Price, len(prices))
for i := range prices {
pricesMap[strings.ToLower(prices[i].Property)] = prices[i]
price, err := crypto.DecryptInt64WithKey(prices[i].Price, cryptoKey)
if err != nil {
return nil, fmt.Errorf("decrypt price error: %v", err)
}
pricesMap[strings.ToLower(prices[i].Property)] = common.Price{
Price: price,
Detail: prices[i].Detail,
Property: prices[i].Property,
}
}
return pricesMap, nil
}
Expand Down
27 changes: 26 additions & 1 deletion controllers/pkg/database/mongodb_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,14 +30,20 @@ func TestMongoDB_GetMeteringOwnerTimeResult(t *testing.T) {
}()

// 2023-04-30T17:00:00.000+00:00
queryTime := time.Date(2023, 4, 30, 17, 0, 0, 0, time.UTC)
queryTime := time.Date(2023, 7, 14, 04, 0, 0, 0, time.UTC)

//[]string{"ns-vd1k1dk3", "ns-cv46sqlr", "ns-wu8nptea", "ns-a2sh413v", "ns-l8ad16ee"}
got, err := m.GetMeteringOwnerTimeResult(queryTime, []string{"ns-vd1k1dk3"}, []string{"cpu", "memory", "storage"}, "ns-vd1k1dk3")
if err != nil {
t.Errorf("failed to get metering owner time result: error = %v", err)
}
t.Logf("got: %v", got)

got, err = m.GetMeteringOwnerTimeResult(queryTime, []string{"ns-7wdqa36k"}, nil, "ns-7wdqa36k")
if err != nil {
t.Errorf("failed to get metering owner time result: error = %v", err)
}
t.Logf("all properties got: %v", got)
}

func TestMongoDB_QueryBillingRecords(t *testing.T) {
Expand Down Expand Up @@ -350,3 +356,22 @@ func TestMongoDB_GetBillingLastUpdateTime(t *testing.T) {
}
t.Logf("lastUpdateTime: %v", lastUpdateTime)
}

func TestMongoDB_GetAllPricesMap(t *testing.T) {
dbCTX := context.Background()

m, err := NewMongoDB(dbCTX, os.Getenv("MONGODB_URI"))
if err != nil {
t.Errorf("failed to connect mongo: error = %v", err)
}
defer func() {
if err = m.Disconnect(dbCTX); err != nil {
t.Errorf("failed to disconnect mongo: error = %v", err)
}
}()
pricesMap, err := m.GetAllPricesMap()
if err != nil {
t.Fatalf("failed to get all prices map: %v", err)
}
t.Logf("pricesMap: %v", pricesMap)
}
10 changes: 5 additions & 5 deletions controllers/resources/controllers/monitor_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ type MonitorReconciler struct {
logr.Logger
Interval time.Duration
Scheme *runtime.Scheme
mongoURL string
mongoURI string
stopCh chan struct{}
wg sync.WaitGroup
periodicReconcile time.Duration
Expand Down Expand Up @@ -81,9 +81,9 @@ func NewMonitorReconciler(mgr ctrl.Manager) (*MonitorReconciler, error) {
Logger: ctrl.Log.WithName("controllers").WithName("Monitor"),
stopCh: make(chan struct{}),
periodicReconcile: 1 * time.Minute,
mongoURL: os.Getenv(database.MongoURL),
mongoURI: os.Getenv(database.MongoURI),
}
if r.mongoURL == "" {
if r.mongoURI == "" {
return nil, fmt.Errorf("mongo uri is empty")
}
r.initNamespaceFuncs()
Expand Down Expand Up @@ -187,7 +187,7 @@ func (r *MonitorReconciler) processNamespaceList(ctx context.Context, namespaceL
wg := sync.WaitGroup{}
wg.Add(len(namespaceList.Items))
dbCtx := context.Background()
dbClient, err := database.NewMongoDB(dbCtx, r.mongoURL)
dbClient, err := database.NewMongoDB(dbCtx, r.mongoURI)
if err != nil {
r.Logger.Error(err, "connect mongo client failed")
return err
Expand Down Expand Up @@ -241,7 +241,7 @@ func (r *MonitorReconciler) processNamespace(ctx context.Context, dbClient datab

func (r *MonitorReconciler) preApply() error {
ctx := context.Background()
dbClient, err := database.NewMongoDB(ctx, r.mongoURL)
dbClient, err := database.NewMongoDB(ctx, r.mongoURI)
if err != nil {
return fmt.Errorf("failed to connect mongo: %v", err)
}
Expand Down
35 changes: 29 additions & 6 deletions controllers/resources/metering/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import (
"syscall"
"time"

"github.com/labring/sealos/controllers/pkg/common"

"github.com/labring/sealos/controllers/pkg/database"

"github.com/labring/sealos/pkg/utils/flags"
Expand All @@ -20,7 +22,7 @@ import (

type Config struct {
// mongodb connect url
MongoConnectURL string
MongoConnectURI string
MongoUsername string
MongoPassword string

Expand Down Expand Up @@ -77,11 +79,11 @@ func ResourcesMetering() {

func PreApply() error {
switch {
case config.MongoConnectURL == "":
case config.MongoConnectURI == "":
return fmt.Errorf("mongo connect url is empty")
}
dbCtx := context.Background()
dbClient, err := database.NewMongoDB(dbCtx, config.MongoConnectURL)
dbClient, err := database.NewMongoDB(dbCtx, config.MongoConnectURI)
if err != nil {
return fmt.Errorf("connect mongo client failed: %v", err)
}
Expand All @@ -104,7 +106,7 @@ func PreApply() error {
func executeTask() error {
//opts := options.Client().ApplyURI("mongodb://192.168.64.17:27017")
dbCtx := context.Background()
dbClient, err := database.NewMongoDB(dbCtx, config.MongoConnectURL)
dbClient, err := database.NewMongoDB(dbCtx, config.MongoConnectURI)
if err != nil {
return fmt.Errorf("connect mongo client failed: %v", err)
}
Expand All @@ -116,7 +118,11 @@ func executeTask() error {
}()
prices, err := dbClient.GetAllPricesMap()
if err != nil {
return fmt.Errorf("failed to get all prices map: %v", err)
logger.Error("failed to get all prices map: %v", err)
}
//prices is empty, use default price
if len(prices) == 0 || err != nil {
prices = getDefaultPrices()
}
now := time.Now().UTC()
startTime := time.Date(now.Year(), now.Month(), now.Day(), now.Hour()-1, 0, 0, 0, time.UTC)
Expand All @@ -131,6 +137,23 @@ func executeTask() error {
return nil
}

func getDefaultPrices() map[string]common.Price {
return map[string]common.Price{
"cpu": {
Property: "cpu",
Price: 67,
},
"memory": {
Property: "memory",
Price: 33,
},
"storage": {
Property: "storage",
Price: 2,
},
}
}

func CreateMonitorTimeSeries(dbClient database.Interface, collTime time.Time) error {
return dbClient.CreateMonitorTimeSeriesIfNotExist(collTime)
}
Expand Down Expand Up @@ -189,7 +212,7 @@ func init() {
rootCmd.PersistentFlags().BoolVar(&showPath, "show-path", false, "enable show code path")
rootCmd.AddCommand(newStartCmd())
config = &Config{
MongoConnectURL: os.Getenv(database.MongoURL),
MongoConnectURI: os.Getenv(database.MongoURI),
MongoUsername: os.Getenv(database.MongoUsername),
MongoPassword: os.Getenv(database.MongoPassword),
}
Expand Down

0 comments on commit 9d234f6

Please sign in to comment.