From f3905bb822ed324b81dcc6e60a75657f20f53c25 Mon Sep 17 00:00:00 2001 From: Daniel Legt Date: Sun, 21 Jan 2024 21:42:58 +0200 Subject: [PATCH] Cleanup Service * Updated .env + Implemented automatical removal of old logs --- .env.example | 13 +++++++++++ lib/config/config.go | 41 ++++++++++++++++++---------------- lib/svc/service.go | 15 ++++++++----- lib/svc/service_cleanup.go | 45 ++++++++++++++++++++++++++++++++++++++ lib/web/api.go | 2 ++ main.go | 16 ++++++++++---- 6 files changed, 104 insertions(+), 28 deletions(-) create mode 100644 lib/svc/service_cleanup.go diff --git a/.env.example b/.env.example index 013f50b..4aaea2b 100644 --- a/.env.example +++ b/.env.example @@ -1,7 +1,20 @@ +# +# All time references are in seconds +# +# +############################################################ + # The frequency at which to fetch the temperature of ALL disks and add it to the database DISK_FETCH_FREQUENCY=5 +# How ofthen should the program clean the database of old logs +CLEANUP_SERVICE_FREQUENCY=3600 + # The maximum age of logs in seconds +# 1 Day = 86400 +# 1 Week = 604800 +# 1 Month ~= 2592000 +# Recommended 1 week MAX_HISTORY_AGE=2592000 # The ip:port to listen to for the application diff --git a/lib/config/config.go b/lib/config/config.go index 5968c4c..793d213 100644 --- a/lib/config/config.go +++ b/lib/config/config.go @@ -1,34 +1,31 @@ package config import ( - "log" "os" "strconv" - - "github.com/joho/godotenv" ) type DHConfig struct { - DiskFetchFrequency int `json:"diskFetchFrequency"` - MaxHistoryAge int `json:"maxHistoryAge"` - DatabaseFilePath string - Listen string - IdentityUsername string - IdentityPassword string + CleanupServiceFrequency int `json:"cleanupServiceFrequency"` + DiskFetchFrequency int `json:"diskFetchFrequency"` + MaxHistoryAge int `json:"maxHistoryAge"` + + DatabaseFilePath string `json:"databaseFilePath"` + + Listen string `json:"listen"` + + IdentityUsername string `json:"identityUsername"` + IdentityPassword string `json:"identityPassword"` } func GetConfiguration() DHConfig { - // Load .env file if it exists - if err := godotenv.Load(); err != nil { - log.Println("No .env file found") - } - config := DHConfig{ - DiskFetchFrequency: 5, // default value - MaxHistoryAge: 2592000, // default value - DatabaseFilePath: "./data.sqlite", - IdentityUsername: "admin", - IdentityPassword: "admin", + DiskFetchFrequency: 5, + CleanupServiceFrequency: 60, + MaxHistoryAge: 2592000, + DatabaseFilePath: "./data.sqlite", + IdentityUsername: "admin", + IdentityPassword: "admin", Listen: ":8080", } @@ -39,6 +36,12 @@ func GetConfiguration() DHConfig { } } + if val, exists := os.LookupEnv("CLEANUP_SERVICE_FREQUENCY"); exists { + if intValue, err := strconv.Atoi(val); err == nil { + config.CleanupServiceFrequency = intValue + } + } + if val, exists := os.LookupEnv("MAX_HISTORY_AGE"); exists { if intValue, err := strconv.Atoi(val); err == nil { config.MaxHistoryAge = intValue diff --git a/lib/svc/service.go b/lib/svc/service.go index a33636b..7e0557b 100644 --- a/lib/svc/service.go +++ b/lib/svc/service.go @@ -14,15 +14,16 @@ import ( var db *gorm.DB +// Initialize the database connection func InitDB() { var err error dbPath := config.GetConfiguration().DatabaseFilePath - if dbPath == "" { - dbPath = "./data.sqlite" - } db, err = gorm.Open(sqlite.Open(dbPath), &gorm.Config{}) if err != nil { + // This should basically never happen, unless the path to the database + // is inaccessible, doesn't exist or there's no permission to it, which + // should and will crash the program panic("failed to connect database") } @@ -30,10 +31,12 @@ func InitDB() { db.AutoMigrate(&hardware.HardDrive{}, &hardware.HardDriveTemperature{}) } +// Fetch the open database pointer func GetDatabaseRef() *gorm.DB { return db } +// Log the temperature of the disks func LogDriveTemps() error { drives, err := hardware.GetSystemHardDrives(db, nil, nil) if err != nil { @@ -52,8 +55,9 @@ func LogDriveTemps() error { return nil } +// Run the logging service, this will periodically log the temperature of the disks with the LogDriveTemps function func RunLoggerService() { - fmt.Println("Initializing Temperature Logging Service...") + fmt.Println("[🦝] Initializing Temperature Logging Service...") tickTime := time.Duration(config.GetConfiguration().DiskFetchFrequency) * time.Second @@ -63,12 +67,13 @@ func RunLoggerService() { time.Sleep(tickTime) err := LogDriveTemps() if err != nil { - fmt.Printf("🛑 Temperature logging failed: %s\n", err) + fmt.Printf("[🛑] Temperature logging failed: %s\n", err) } } }() } +// Generate a PNG based upon a HDD id and a date range func GetDiskGraphImage(hddID int, newerThan *time.Time, olderThan *time.Time) (*bytes.Buffer, error) { var hdd hardware.HardDrive // Fetch by a combination of fields diff --git a/lib/svc/service_cleanup.go b/lib/svc/service_cleanup.go new file mode 100644 index 0000000..0e96801 --- /dev/null +++ b/lib/svc/service_cleanup.go @@ -0,0 +1,45 @@ +package svc + +import ( + "fmt" + "time" + + "tea.chunkbyte.com/kato/drive-health/lib/config" + "tea.chunkbyte.com/kato/drive-health/lib/hardware" +) + +// Delete all thermal entries that are older than X amount of seconds +func CleanupOldData() error { + cfg := config.GetConfiguration() + + beforeDate := time.Now().Add(-1 * time.Duration(cfg.MaxHistoryAge) * time.Second) + + deleteResult := db.Where("time_stamp < ?", beforeDate).Delete(&hardware.HardDriveTemperature{}) + if deleteResult.Error != nil { + fmt.Printf("[🛑] Error during cleanup: %s\n", deleteResult.Error) + return db.Error + } + + if deleteResult.RowsAffected > 0 { + fmt.Printf("[🛑] Cleaned up %v entries before %s\n", deleteResult.RowsAffected, beforeDate) + } + + return nil +} + +func RunCleanupService() { + fmt.Println("[🦝] Initializing Log Cleanup Service...") + + tickTime := time.Duration(config.GetConfiguration().CleanupServiceFrequency) * time.Second + + // Snapshot taking routine + go func() { + for { + time.Sleep(tickTime) + err := CleanupOldData() + if err != nil { + fmt.Printf("🛑 Cleanup process failed: %s\n", err) + } + } + }() +} diff --git a/lib/web/api.go b/lib/web/api.go index 4217346..b9e4028 100644 --- a/lib/web/api.go +++ b/lib/web/api.go @@ -13,6 +13,7 @@ import ( func setupApi(r *gin.Engine) { api := r.Group("/api/v1") + // Fetch the chart image for the disk's temperature api.GET("/disks/:diskid/chart", func(ctx *gin.Context) { diskIDString := ctx.Param("diskid") diskId, err := strconv.Atoi(diskIDString) @@ -51,6 +52,7 @@ func setupApi(r *gin.Engine) { } }) + // Get a list of all the disks api.GET("/disks", func(ctx *gin.Context) { olderThan := time.Now().Add(time.Minute * time.Duration(10) * -1) diff --git a/main.go b/main.go index 43e5cc5..d7c5b26 100644 --- a/main.go +++ b/main.go @@ -9,12 +9,18 @@ import ( "syscall" "time" + "github.com/joho/godotenv" "tea.chunkbyte.com/kato/drive-health/lib/config" "tea.chunkbyte.com/kato/drive-health/lib/svc" "tea.chunkbyte.com/kato/drive-health/lib/web" ) func main() { + // Load .env file if it exists + if err := godotenv.Load(); err != nil { + log.Println("[🟨] No .env file found") + } + // Init the database svc.InitDB() cfg := config.GetConfiguration() @@ -29,12 +35,14 @@ func main() { // Run the server in a goroutine go func() { if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed { - log.Fatalf("listen: %s\n", err) + log.Fatalf("[🛑] listening failed: %s\n", err) } }() // Run the hardware service svc.RunLoggerService() + // Run the cleanup service + svc.RunCleanupService() // Setting up signal capturing quit := make(chan os.Signal, 1) @@ -42,14 +50,14 @@ func main() { // Block until a signal is received <-quit - log.Println("Shutting down server...") + log.Println("[🦝] Shutting down server...") // Graceful shutdown ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() if err := srv.Shutdown(ctx); err != nil { - log.Fatal("Server forced to shutdown:", err) + log.Fatal("[🛑] Server forced to shutdown:", err) } - log.Println("Server exiting") + log.Println("[🦝] Server exiting") }