Files
CLICD/backend/internal/lxc/expiry.go
T
2026-06-07 09:24:09 +08:00

138 lines
3.8 KiB
Go

package lxc
import (
"fmt"
"time"
"clicd/internal/config"
)
// IsExpired checks if a container has passed its expiration date
func IsExpired(c config.Container) bool {
return isContainerExpired(c, time.Now())
}
// StopExpiredContainers stops running containers whose expiration date has passed.
func (m *Manager) StopExpiredContainers(now time.Time) {
for _, container := range config.AppConfig.Containers {
if container.IsKVM() || !isContainerExpired(container, now) {
continue
}
status, err := m.GetContainerStatus(container.LxcName())
if err != nil {
status = container.Status
}
if status != "running" {
continue
}
fmt.Printf("Container %s (ID=%d) expired at %s, stopping...\n", container.Name, container.ID, container.ExpiresAt)
if err := m.StopContainer(container.ID); err != nil {
fmt.Printf("Warning: failed to stop expired container %s: %v\n", container.Name, err)
}
}
}
// StartExpiryScanner runs a background loop that tracks traffic & stops expired/over-traffic containers every 30 seconds
func (m *Manager) StartExpiryScanner() {
go func() {
for {
time.Sleep(30 * time.Second)
now := time.Now()
m.AccumulateTraffic() // track network traffic deltas
m.StopExpiredContainers(now)
m.StopTrafficExceededContainers(now)
}
}()
}
// StopTrafficExceededContainers stops running containers that have exceeded their monthly traffic limit
func (m *Manager) StopTrafficExceededContainers(now time.Time) {
currentMonth := now.Format("2006-01")
saved := false
for i := range config.AppConfig.Containers {
c := &config.AppConfig.Containers[i]
if c.IsKVM() || c.Status != "running" {
continue
}
// Reset traffic if new month
if c.TrafficResetDate != currentMonth {
c.TrafficUsedRX = 0
c.TrafficUsedTX = 0
c.TrafficResetDate = currentMonth
saved = true
continue
}
// Check traffic limits
if isTrafficExceeded(*c) {
fmt.Printf("Container %s (ID=%d) exceeded traffic limit, stopping...\n", c.Name, c.ID)
if err := m.StopContainer(c.ID); err != nil {
fmt.Printf("Warning: failed to stop traffic-exceeded container %s: %v\n", c.Name, err)
}
}
}
if saved {
config.SaveConfig()
}
}
func isTrafficExceeded(c config.Container) bool {
if c.TrafficMode == "in_out" {
inLimit := int64(c.TrafficInGB) * 1073741824
outLimit := int64(c.TrafficOutGB) * 1073741824
if inLimit > 0 && c.TrafficUsedRX >= inLimit {
return true
}
if outLimit > 0 && c.TrafficUsedTX >= outLimit {
return true
}
return false
}
totalLimit := int64(c.MonthlyTrafficGB) * 1073741824
return totalLimit > 0 && (c.TrafficUsedRX+c.TrafficUsedTX) >= totalLimit
}
// ResetTraffic resets traffic counters for a container
func (m *Manager) ResetTraffic(id int) error {
c := config.FindContainer(id)
if c == nil {
return fmt.Errorf("container not found: %d", id)
}
c.TrafficUsedRX = 0
c.TrafficUsedTX = 0
c.TrafficResetDate = time.Now().Format("2006-01")
config.SaveConfig()
return nil
}
// IsTrafficExceeded checks if a container has exceeded its traffic limit
func IsTrafficExceeded(c config.Container) bool {
return isTrafficExceeded(c)
}
func isContainerExpired(container config.Container, now time.Time) bool {
expiresAt, ok := ParseExpiration(container.ExpiresAt)
return ok && !now.Before(expiresAt)
}
// ParseExpiration parses an expiration string. A YYYY-MM-DD value expires at the
// end of that local day, while RFC3339 values are treated as exact timestamps.
func ParseExpiration(value string) (time.Time, bool) {
if value == "" {
return time.Time{}, false
}
if parsed, err := time.Parse(time.RFC3339, value); err == nil {
return parsed, true
}
if parsed, err := time.ParseInLocation("2006-01-02", value, time.Local); err == nil {
return parsed.Add(24 * time.Hour), true
}
return time.Time{}, false
}