Skip to content

Commit

Permalink
send notfication to user one day before expiration date
Browse files Browse the repository at this point in the history
  • Loading branch information
Eslam-Nawara committed Dec 12, 2023
1 parent 67fd8ff commit 2fdfd33
Show file tree
Hide file tree
Showing 9 changed files with 115 additions and 43 deletions.
4 changes: 4 additions & 0 deletions server/app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,10 @@ func (a *App) startBackgroundWorkers(ctx context.Context) {
go a.deployer.PeriodicRequests(ctx, substrateBlockDiffInSeconds)
go a.deployer.PeriodicDeploy(ctx, substrateBlockDiffInSeconds)

// send notification about vms and k8s expiration
go a.deployer.WarnUsersWithExpiredVMs(ctx)
go a.deployer.WarnUsersWithExpiredK8s(ctx)

// remove expired vms and k8s
go a.deployer.CleanExpiredVMs(ctx)
go a.deployer.CleanExpiredK8S(ctx)
Expand Down
30 changes: 15 additions & 15 deletions server/app/user_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,16 +66,16 @@ type EmailInput struct {

// ApplyForVoucherInput struct for user to apply for voucher
type ApplyForVoucherInput struct {
VMs int `json:"vms" binding:"required" validate:"min=0"`
PublicIPs int `json:"public_ips" binding:"required" validate:"min=0"`
Reason string `json:"reason" binding:"required" validate:"nonzero"`
VoucherDuration int `json:"voucher_duration" binding:"required"`
VMs int `json:"vms" binding:"required" validate:"min=0"`
PublicIPs int `json:"public_ips" binding:"required" validate:"min=0"`
Reason string `json:"reason" binding:"required" validate:"nonzero"`
VoucherDurationInMonth int `json:"voucher_duration_in_month" binding:"required"`
}

// AddVoucherInput struct for voucher applied by user
type AddVoucherInput struct {
Voucher string `json:"voucher" binding:"required"`
RequestedDuration int `json:"requestedDuration" binding:"required"`
Voucher string `json:"voucher" binding:"required"`
VoucherDurationInMonth int `json:"voucher_duration_in_month" binding:"required"`
}

// SignUpHandler creates account for user
Expand Down Expand Up @@ -574,19 +574,19 @@ func (a *App) ApplyForVoucherHandler(req *http.Request) (interface{}, Response)
}

// make sure the requested duration is less that the maximum allowed duration
if input.VoucherDuration > a.config.VouchersMaxDuration {
if input.VoucherDurationInMonth > a.config.VouchersMaxDuration {
return nil, BadRequest(fmt.Errorf("invalid voucher duration, max duration is %d", a.config.VouchersMaxDuration))
}

// generate voucher for user but can't use it until admin approves it
v := internal.GenerateRandomVoucher(5)
voucher := models.Voucher{
Voucher: v,
UserID: userID,
VMs: input.VMs,
Reason: input.Reason,
PublicIPs: input.PublicIPs,
Duration: input.VoucherDuration,
Voucher: v,
UserID: userID,
VMs: input.VMs,
Reason: input.Reason,
PublicIPs: input.PublicIPs,
VoucherDurationInMonth: input.VoucherDurationInMonth,
}

err = a.db.CreateVoucher(&voucher)
Expand Down Expand Up @@ -631,7 +631,7 @@ func (a *App) ActivateVoucherHandler(req *http.Request) (interface{}, Response)
return nil, InternalServerError(errors.New(internalServerErrorMsg))
}

userQuotaVMs, err := a.db.GetUserQuotaVMs(quota.ID.String(), voucherQuota.Duration)
userQuotaVMs, err := a.db.GetUserQuotaVMs(quota.ID.String(), voucherQuota.VoucherDurationInMonth)
if err == gorm.ErrRecordNotFound {
return nil, NotFound(errors.New("user quota vms is not found"))
}
Expand Down Expand Up @@ -664,7 +664,7 @@ func (a *App) ActivateVoucherHandler(req *http.Request) (interface{}, Response)
return nil, InternalServerError(errors.New(internalServerErrorMsg))
}

err = a.db.UpdateUserQuotaVMs(quota.ID.String(), voucherQuota.Duration, userQuotaVMs.Vms+voucherQuota.VMs)
err = a.db.UpdateUserQuotaVMs(quota.ID.String(), voucherQuota.VoucherDurationInMonth, userQuotaVMs.Vms+voucherQuota.VMs)
if err != nil {
log.Error().Err(err).Send()
return nil, InternalServerError(errors.New(internalServerErrorMsg))
Expand Down
20 changes: 10 additions & 10 deletions server/app/voucher_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,10 @@ import (

// GenerateVoucherInput struct for data needed when user generate vouchers
type GenerateVoucherInput struct {
Length int `json:"length" binding:"required" validate:"min=3,max=20"`
VMs int `json:"vms" binding:"required"`
PublicIPs int `json:"public_ips" binding:"required"`
VoucherDuration int `json:"voucher_duration" binding:"required"`
Length int `json:"length" binding:"required" validate:"min=3,max=20"`
VMs int `json:"vms" binding:"required"`
PublicIPs int `json:"public_ips" binding:"required"`
VoucherDurationInMonth int `json:"voucher_duration_in_month" binding:"required"`
}

// UpdateVoucherInput struct for data needed when user update voucher
Expand All @@ -45,16 +45,16 @@ func (a *App) GenerateVoucherHandler(req *http.Request) (interface{}, Response)
}
voucher := internal.GenerateRandomVoucher(input.Length)

if input.VoucherDuration > a.config.VouchersMaxDuration {
if input.VoucherDurationInMonth > a.config.VouchersMaxDuration {
return nil, BadRequest(fmt.Errorf("invalid voucher duration, max duration is %d", a.config.VouchersMaxDuration))
}

v := models.Voucher{
Voucher: voucher,
VMs: input.VMs,
PublicIPs: input.PublicIPs,
Approved: true,
Duration: input.VoucherDuration,
Voucher: voucher,
VMs: input.VMs,
PublicIPs: input.PublicIPs,
Approved: true,
VoucherDurationInMonth: input.VoucherDurationInMonth,
}

err = a.db.CreateVoucher(&v)
Expand Down
72 changes: 70 additions & 2 deletions server/deployer/deployer.go
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,74 @@ func (d *Deployer) CancelDeployment(contractID uint64, netContractID uint64, dlT
return nil
}

func (d *Deployer) WarnUsersWithExpiredVMs(ctx context.Context) {
ticker := time.NewTicker(24 * time.Hour)
for range ticker.C {
users, err := d.db.ListAllUsers()
if err != nil {
log.Error().Err(err).Msg("failed to get all users")
return
}

for _, user := range users {
vms, err := d.db.GetAllVms(user.UserID)
if err != nil {
log.Error().Err(err).Msg("failed to get all user vms")
continue
}

for _, vm := range vms {
if time.Now().Before(vm.ExpiresAt) && time.Until(vm.ExpiresAt) < time.Hour*24 {
notification := models.Notification{
UserID: user.UserID,
Msg: fmt.Sprintf("Warning: vm with id %d expires in one day", vm.ID),
Type: models.VMsType,
}

err = d.db.CreateNotification(&notification)
if err != nil {
log.Error().Err(err).Msgf("failed to create notification: %+v", notification)
}
}
}
}
}
}

func (d *Deployer) WarnUsersWithExpiredK8s(ctx context.Context) {
ticker := time.NewTicker(24 * time.Hour)
for range ticker.C {
users, err := d.db.ListAllUsers()
if err != nil {
log.Error().Err(err).Msg("failed to get all users")
return
}

for _, user := range users {
k8s, err := d.db.GetAllK8s(user.UserID)
if err != nil {
log.Error().Err(err).Msg("failed to get all user k8s clusters")
continue
}

for _, k := range k8s {
if time.Now().Before(k.ExpiresAt) && time.Until(k.ExpiresAt) < time.Hour*24 {
notification := models.Notification{
UserID: user.UserID,
Msg: fmt.Sprintf("Warning: k8s cluster with id %d expires in one day", k.ID),
Type: models.K8sType,
}

err = d.db.CreateNotification(&notification)
if err != nil {
log.Error().Err(err).Msgf("failed to create notification: %+v", notification)
}
}
}
}
}
}

func (d *Deployer) CleanExpiredVMs(ctx context.Context) {
ticker := time.NewTicker(24 * time.Hour)
for range ticker.C {
Expand All @@ -183,7 +251,7 @@ func (d *Deployer) CleanExpiredVMs(ctx context.Context) {
}

for _, vm := range vms {
if vm.ExpirationDate.Before(time.Now()) {
if vm.ExpiresAt.Before(time.Now()) {
err = d.CancelDeployment(vm.ContractID, vm.NetworkContractID, "vm", vm.Name)
if err != nil {
log.Error().Err(err).Msg("failed to cancel contract of expired vm")
Expand Down Expand Up @@ -215,7 +283,7 @@ func (d *Deployer) CleanExpiredK8S(ctx context.Context) {
}

for _, k := range k8s {
if k.ExpirationDate.Before(time.Now()) {
if k.ExpiresAt.Before(time.Now()) {
err = d.CancelDeployment(uint64(k.ClusterContract), uint64(k.NetworkContract), "k8s", k.Master.Name)
if err != nil {
log.Error().Err(err).Msg("failed to cancel contract of expired k8s cluster")
Expand Down
2 changes: 1 addition & 1 deletion server/deployer/k8s_deployer.go
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ func (d *Deployer) loadK8s(k8sDeployInput models.K8sDeployInput, userID string,
ClusterContract: int(k8sContractID),
Master: master,
Workers: workers,
ExpirationDate: time.Now().Add(time.Duration(k8sDeployInput.Duration) * 30 * 24 * time.Hour).Truncate(24 * time.Hour),
ExpiresAt: time.Now().Add(time.Duration(k8sDeployInput.Duration) * 30 * 24 * time.Hour).Truncate(24 * time.Hour),
}

return k8sCluster, nil
Expand Down
2 changes: 1 addition & 1 deletion server/deployer/vms_deployer.go
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@ func (d *Deployer) deployVMRequest(ctx context.Context, user models.User, input
MRU: uint64(vm.Memory),
ContractID: contractID,
NetworkContractID: networkContractID,
ExpirationDate: time.Now().Add(time.Duration(quotaVMs.Duration) * 30 * 24 * time.Hour).Truncate(24 * time.Hour),
ExpiresAt: time.Now().Add(time.Duration(quotaVMs.Duration) * 30 * 24 * time.Hour).Truncate(24 * time.Hour),
}

err = d.db.CreateVM(&userVM)
Expand Down
2 changes: 1 addition & 1 deletion server/models/k8s.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ type K8sCluster struct {
ClusterContract int `json:"contract_id"`
Master Master `json:"master" gorm:"foreignKey:ClusterID"`
Workers []Worker `json:"workers" gorm:"foreignKey:ClusterID"`
ExpirationDate time.Time `json:"expiration_date"`
ExpiresAt time.Time `json:"expires_at"`
}

// Master struct for kubernetes master data
Expand Down
2 changes: 1 addition & 1 deletion server/models/vm.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ type VM struct {
MRU uint64 `json:"mru"`
ContractID uint64 `json:"contractID"`
NetworkContractID uint64 `json:"networkContractID"`
ExpirationDate time.Time `json:"expirationDate" binding:"required"`
ExpiresAt time.Time `json:"expires_at" binding:"required"`
}

// DeploymentsCount has the vms and ips reserved in the grid
Expand Down
24 changes: 12 additions & 12 deletions server/models/voucher.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,16 @@ import "time"

// Voucher struct holds data of vouchers
type Voucher struct {
ID int `json:"id" gorm:"primaryKey"`
UserID string `json:"user_id" binding:"required"`
Voucher string `json:"voucher" gorm:"unique"`
VMs int `json:"vms" binding:"required"`
PublicIPs int `json:"public_ips" binding:"required"`
Reason string `json:"reason" binding:"required"`
Used bool `json:"used" binding:"required"`
Approved bool `json:"approved" binding:"required"`
Rejected bool `json:"rejected" binding:"required"`
Duration int `json:"duration" binding:"required"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
ID int `json:"id" gorm:"primaryKey"`
UserID string `json:"user_id" binding:"required"`
Voucher string `json:"voucher" gorm:"unique"`
VMs int `json:"vms" binding:"required"`
PublicIPs int `json:"public_ips" binding:"required"`
Reason string `json:"reason" binding:"required"`
Used bool `json:"used" binding:"required"`
Approved bool `json:"approved" binding:"required"`
Rejected bool `json:"rejected" binding:"required"`
VoucherDurationInMonth int `json:"voucher_duration_in_month" binding:"required"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}

0 comments on commit 2fdfd33

Please sign in to comment.