diff --git a/provider-integration/im2/pkg/config/00_module.go b/provider-integration/im2/pkg/config/00_module.go index 329e9cf1fc..e67981d0eb 100644 --- a/provider-integration/im2/pkg/config/00_module.go +++ b/provider-integration/im2/pkg/config/00_module.go @@ -285,12 +285,18 @@ type ProviderBranding struct { Sections []ProviderBrandingSection `yaml:"sections"` ProductDescription []ProviderBrandingProductDescription `yaml:"productDescription"` } +type JobAuditLog struct { + RetentionPeriodInDays int `yaml:"retentionPeriodInDays"` +} + type ProviderConfiguration struct { Id string ProviderBranding ProviderBranding `yaml:"providerBranding"` ProviderBrandingImageAbsolutePath map[string]string + JobAuditLog JobAuditLog `yaml:"jobAuditLog"` + Hosts struct { UCloud HostInfo Self HostInfo @@ -395,6 +401,13 @@ func parseProvider(filePath string, provider *yaml.Node) (bool, ProviderConfigur if providerBranding != nil { populateProviderBranding(&cfg, filePath, providerBranding) } + + // Job audit log section + jobAuditLog, _ := cfgutil.GetChildOrNil(filePath, provider, "jobAuditLog") + if jobAuditLog != nil { + cfgutil.Decode(filePath, jobAuditLog, &cfg.JobAuditLog, &success) + } + // Hosts section hosts := cfgutil.RequireChild(filePath, provider, "hosts", &success) ucloudHost := cfgutil.RequireChild(filePath, hosts, "ucloud", &success) diff --git a/provider-integration/im2/pkg/controller/00_module.go b/provider-integration/im2/pkg/controller/00_module.go index a458d7da63..5f2a22c050 100644 --- a/provider-integration/im2/pkg/controller/00_module.go +++ b/provider-integration/im2/pkg/controller/00_module.go @@ -36,6 +36,7 @@ func Init(mux *http.ServeMux) { initTasks() initSshKeys() initProviderBranding() + initJobAuditLog() initLiveness() if RunsServerCode() { diff --git a/provider-integration/im2/pkg/controller/job_audit_log.go b/provider-integration/im2/pkg/controller/job_audit_log.go new file mode 100644 index 0000000000..1041469998 --- /dev/null +++ b/provider-integration/im2/pkg/controller/job_audit_log.go @@ -0,0 +1,96 @@ +package controller + +import ( + "os" + "path/filepath" + "regexp" + "time" + + "ucloud.dk/pkg/config" + "ucloud.dk/shared/pkg/log" +) + +var jobAuditFileRegex = regexp.MustCompile(`^audit-(\d+)-(\d{4}-\d{2}-\d{2})\.jsonl$`) + +func initJobAuditLog() { + k8sCfg := config.Services.Kubernetes() + if k8sCfg == nil { + log.Error("Job audit log server failed to get Kubernetes config") + return + } + jobAuditLogFolder := filepath.Join(k8sCfg.FileSystem.MountPoint, "audit") + retentionDays := config.Provider.JobAuditLog.RetentionPeriodInDays + go func() { + cleanupLogs(jobAuditLogFolder, retentionDays) // run once at startup + + ticker := time.NewTicker(4 * time.Hour) + defer ticker.Stop() + + for range ticker.C { + cleanupLogs(jobAuditLogFolder, retentionDays) + } + }() +} + +func cleanupLogs(jobAuditLogFolder string, retentionDays int) { + cutoff := time.Now().AddDate(0, 0, -retentionDays) + today := time.Now().Format("2006-01-02") + + walkErr := filepath.WalkDir(jobAuditLogFolder, func(path string, d os.DirEntry, err error) error { + if err != nil { + log.Error("Walk error: %s", err) + return nil + } + // Only process files + if !d.IsDir() { + name := d.Name() + matches := jobAuditFileRegex.FindStringSubmatch(name) + if matches == nil { + return nil + } + + dateStr := matches[2] + + if dateStr == today { + return nil + } + + fileDate, err := time.Parse("2006-01-02", dateStr) + if err != nil { + return nil + } + + if fileDate.Before(cutoff) { + err := os.Remove(path) + if err != nil { + log.Error("Could not remove file: %s", err) + } else { + log.Info("Removed audit log file: %s, since it is older than %d days", path, retentionDays) + } + } + + return nil + } + // After walking a directory, check if it's empty (skip root) + if path == jobAuditLogFolder { + return nil + } + + entries, err := os.ReadDir(path) + if err != nil { + return nil + } + + // Delete the child folder if it is empty + if len(entries) == 0 { + err := os.Remove(path) + if err == nil { + log.Info("Removed empty audit log folder: %s", path) + } + } + return nil + }) + if walkErr != nil { + log.Error("Error cleaning up job audit logs: %s", walkErr) + } +}