fix: replace fmt.Printf with structured slog logging across all services

All 10 service files now use slog.Error for failure logging instead of
fmt.Printf. Audit event recording errors are checked and logged rather
than silently discarded. Adds consistent structured context (resource IDs,
operation names) to all error log statements.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
shankar0123
2026-03-20 01:20:03 -04:00
parent 393cf548b2
commit e03a75ed9a
10 changed files with 166 additions and 93 deletions
+15 -10
View File
@@ -5,6 +5,7 @@ import (
"crypto/sha256"
"encoding/hex"
"fmt"
"log/slog"
"math/rand"
"time"
@@ -73,7 +74,7 @@ func (s *AgentService) Register(ctx context.Context, name string, hostname strin
if err := s.auditService.RecordEvent(ctx, "system", domain.ActorTypeSystem,
"agent_registered", "agent", agent.ID,
map[string]interface{}{"name": name, "hostname": hostname}); err != nil {
fmt.Printf("failed to record audit event: %v\n", err)
slog.Error("failed to record audit event", "error", err)
}
// Return the API key only once; the agent must save it securely
@@ -96,7 +97,7 @@ func (s *AgentService) HeartbeatWithContext(ctx context.Context, agentID string)
if agent.Status != domain.AgentStatusOnline {
agent.Status = domain.AgentStatusOnline
if err := s.agentRepo.Update(ctx, agent); err != nil {
fmt.Printf("failed to update agent status: %v\n", err)
slog.Error("failed to update agent status", "error", err)
}
}
@@ -140,13 +141,15 @@ func (s *AgentService) SubmitCSR(ctx context.Context, agentID string, certID str
}
// Record audit event
_ = s.auditService.RecordEvent(ctx, agent.ID, domain.ActorTypeAgent,
if auditErr := s.auditService.RecordEvent(ctx, agent.ID, domain.ActorTypeAgent,
"csr_submitted", "certificate", certID,
map[string]interface{}{
"agent_hostname": agent.Hostname,
"keygen_mode": "agent",
"job_id": awaitingJobs[0].ID,
})
}); auditErr != nil {
slog.Error("failed to record audit event", "error", auditErr)
}
return nil
}
@@ -181,15 +184,17 @@ func (s *AgentService) SubmitCSR(ctx context.Context, agentID string, certID str
cert.LastRenewalAt = &now
cert.UpdatedAt = now
if err := s.certRepo.Update(ctx, cert); err != nil {
fmt.Printf("failed to update certificate: %v\n", err)
slog.Error("failed to update certificate", "error", err)
}
}
}
// Record audit event
_ = s.auditService.RecordEvent(ctx, agent.ID, domain.ActorTypeAgent,
if auditErr := s.auditService.RecordEvent(ctx, agent.ID, domain.ActorTypeAgent,
"csr_submitted", "certificate", certID,
map[string]interface{}{"agent_hostname": agent.Hostname})
map[string]interface{}{"agent_hostname": agent.Hostname}); auditErr != nil {
slog.Error("failed to record audit event", "error", auditErr)
}
return nil
}
@@ -224,7 +229,7 @@ func (s *AgentService) GetCertificateForAgent(ctx context.Context, agentID strin
if err := s.auditService.RecordEvent(ctx, agentID, domain.ActorTypeAgent,
"certificate_retrieved", "certificate", certID,
map[string]interface{}{"version": latestVersion.SerialNumber}); err != nil {
fmt.Printf("failed to record audit event: %v\n", err)
slog.Error("failed to record audit event", "error", err)
}
return []byte(latestVersion.PEMChain), nil
@@ -283,7 +288,7 @@ func (s *AgentService) ReportJobStatus(ctx context.Context, agentID string, jobI
if err := s.auditService.RecordEvent(ctx, agentID, domain.ActorTypeAgent,
"job_status_reported", "job", jobID,
map[string]interface{}{"status": status, "error": errMsg}); err != nil {
fmt.Printf("failed to record audit event: %v\n", err)
slog.Error("failed to record audit event", "error", err)
}
return nil
@@ -302,7 +307,7 @@ func (s *AgentService) MarkStaleAgentsOffline(ctx context.Context, threshold tim
if agent.Status == domain.AgentStatusOnline && agent.LastHeartbeatAt != nil && agent.LastHeartbeatAt.Before(cutoff) {
agent.Status = domain.AgentStatusOffline
if err := s.agentRepo.Update(ctx, agent); err != nil {
fmt.Printf("failed to mark agent %s offline: %v\n", agent.ID, err)
slog.Error("failed to mark agent offline", "agent_id", agent.ID, "error", err)
continue
}
}
+14 -9
View File
@@ -3,6 +3,7 @@ package service
import (
"context"
"fmt"
"log/slog"
"time"
"github.com/shankar0123/certctl/internal/domain"
@@ -62,9 +63,11 @@ func (s *CertificateService) Create(ctx context.Context, cert *domain.ManagedCer
if len(violations) > 0 {
// Record violations but do not block creation
for _, v := range violations {
_ = s.auditService.RecordEvent(ctx, actor, domain.ActorTypeUser,
if auditErr := s.auditService.RecordEvent(ctx, actor, domain.ActorTypeUser,
"policy_violation_detected", "certificate", cert.ID,
map[string]interface{}{"rule_id": v.RuleID, "message": v.Message})
map[string]interface{}{"rule_id": v.RuleID, "message": v.Message}); auditErr != nil {
slog.Error("failed to record audit event", "error", auditErr)
}
}
}
@@ -78,7 +81,7 @@ func (s *CertificateService) Create(ctx context.Context, cert *domain.ManagedCer
"certificate_created", "certificate", cert.ID,
map[string]interface{}{"common_name": cert.CommonName}); err != nil {
// Log but don't fail the operation
fmt.Printf("failed to record audit event: %v\n", err)
slog.Error("failed to record audit event", "error", err)
}
return nil
@@ -98,9 +101,11 @@ func (s *CertificateService) Update(ctx context.Context, cert *domain.ManagedCer
}
if len(violations) > 0 {
for _, v := range violations {
_ = s.auditService.RecordEvent(ctx, actor, domain.ActorTypeUser,
if auditErr := s.auditService.RecordEvent(ctx, actor, domain.ActorTypeUser,
"policy_violation_detected", "certificate", cert.ID,
map[string]interface{}{"rule_id": v.RuleID, "message": v.Message})
map[string]interface{}{"rule_id": v.RuleID, "message": v.Message}); auditErr != nil {
slog.Error("failed to record audit event", "error", auditErr)
}
}
}
@@ -120,7 +125,7 @@ func (s *CertificateService) Update(ctx context.Context, cert *domain.ManagedCer
if err := s.auditService.RecordEvent(ctx, actor, domain.ActorTypeUser,
"certificate_updated", "certificate", cert.ID, changes); err != nil {
fmt.Printf("failed to record audit event: %v\n", err)
slog.Error("failed to record audit event", "error", err)
}
return nil
@@ -140,7 +145,7 @@ func (s *CertificateService) Archive(ctx context.Context, id string, actor strin
if err := s.auditService.RecordEvent(ctx, actor, domain.ActorTypeUser,
"certificate_archived", "certificate", id,
map[string]interface{}{"common_name": cert.CommonName}); err != nil {
fmt.Printf("failed to record audit event: %v\n", err)
slog.Error("failed to record audit event", "error", err)
}
return nil
@@ -185,7 +190,7 @@ func (s *CertificateService) TriggerRenewalWithActor(ctx context.Context, certID
if err := s.auditService.RecordEvent(ctx, actor, domain.ActorTypeUser,
"renewal_triggered", "certificate", certID,
map[string]interface{}{"common_name": cert.CommonName}); err != nil {
fmt.Printf("failed to record audit event: %v\n", err)
slog.Error("failed to record audit event", "error", err)
}
return nil
@@ -207,7 +212,7 @@ func (s *CertificateService) TriggerDeploymentWithActor(ctx context.Context, cer
if err := s.auditService.RecordEvent(ctx, actor, domain.ActorTypeUser,
"deployment_triggered", "certificate", certID,
map[string]interface{}{"common_name": cert.CommonName}); err != nil {
fmt.Printf("failed to record audit event: %v\n", err)
slog.Error("failed to record audit event", "error", err)
}
return nil
+36 -23
View File
@@ -3,6 +3,7 @@ package service
import (
"context"
"fmt"
"log/slog"
"time"
"github.com/shankar0123/certctl/internal/domain"
@@ -68,7 +69,7 @@ func (s *DeploymentService) CreateDeploymentJobs(ctx context.Context, certID str
}
if err := s.jobRepo.Create(ctx, job); err != nil {
fmt.Printf("failed to create deployment job for target %s: %v\n", target.ID, err)
slog.Error("failed to create deployment job for target", "target_id", target.ID, "error", err)
continue
}
@@ -80,9 +81,11 @@ func (s *DeploymentService) CreateDeploymentJobs(ctx context.Context, certID str
}
// Record audit event
_ = s.auditService.RecordEvent(ctx, "system", domain.ActorTypeSystem,
if auditErr := s.auditService.RecordEvent(ctx, "system", domain.ActorTypeSystem,
"deployment_jobs_created", "certificate", certID,
map[string]interface{}{"target_count": len(targets), "job_count": len(jobIDs)})
map[string]interface{}{"target_count": len(targets), "job_count": len(jobIDs)}); auditErr != nil {
slog.Error("failed to record audit event", "error", auditErr)
}
return jobIDs, nil
}
@@ -99,7 +102,7 @@ func (s *DeploymentService) ProcessDeploymentJob(ctx context.Context, job *domai
if err != nil {
updateErr := s.jobRepo.UpdateStatus(ctx, job.ID, domain.JobStatusFailed, fmt.Sprintf("certificate fetch failed: %v", err))
if updateErr != nil {
fmt.Printf("failed to update job status: %v\n", updateErr)
slog.Error("failed to update job status", "job_id", job.ID, "error", updateErr)
}
return fmt.Errorf("failed to fetch certificate: %w", err)
}
@@ -112,7 +115,7 @@ func (s *DeploymentService) ProcessDeploymentJob(ctx context.Context, job *domai
if targetID == "" {
updateErr := s.jobRepo.UpdateStatus(ctx, job.ID, domain.JobStatusFailed, "target_id not found in job")
if updateErr != nil {
fmt.Printf("failed to update job status: %v\n", updateErr)
slog.Error("failed to update job status", "job_id", job.ID, "error", updateErr)
}
return fmt.Errorf("target_id not found in job")
}
@@ -121,7 +124,7 @@ func (s *DeploymentService) ProcessDeploymentJob(ctx context.Context, job *domai
if err != nil {
updateErr := s.jobRepo.UpdateStatus(ctx, job.ID, domain.JobStatusFailed, fmt.Sprintf("target fetch failed: %v", err))
if updateErr != nil {
fmt.Printf("failed to update job status: %v\n", updateErr)
slog.Error("failed to update job status", "job_id", job.ID, "error", updateErr)
}
return fmt.Errorf("failed to fetch target: %w", err)
}
@@ -132,7 +135,7 @@ func (s *DeploymentService) ProcessDeploymentJob(ctx context.Context, job *domai
if err != nil {
updateErr := s.jobRepo.UpdateStatus(ctx, job.ID, domain.JobStatusFailed, fmt.Sprintf("agent fetch failed: %v", err))
if updateErr != nil {
fmt.Printf("failed to update job status: %v\n", updateErr)
slog.Error("failed to update job status", "job_id", job.ID, "error", updateErr)
}
return fmt.Errorf("failed to fetch agent: %w", err)
}
@@ -141,13 +144,17 @@ func (s *DeploymentService) ProcessDeploymentJob(ctx context.Context, job *domai
if agent.LastHeartbeatAt != nil && time.Since(*agent.LastHeartbeatAt) > 5*time.Minute {
updateErr := s.jobRepo.UpdateStatus(ctx, job.ID, domain.JobStatusFailed, "agent is offline")
if updateErr != nil {
fmt.Printf("failed to update job status: %v\n", updateErr)
slog.Error("failed to update job status", "job_id", job.ID, "error", updateErr)
}
_ = s.notificationSvc.SendDeploymentNotification(ctx, cert, target, false, fmt.Errorf("agent offline"))
_ = s.auditService.RecordEvent(ctx, "system", domain.ActorTypeSystem,
if notifErr := s.notificationSvc.SendDeploymentNotification(ctx, cert, target, false, fmt.Errorf("agent offline")); notifErr != nil {
slog.Error("failed to send deployment notification", "error", notifErr)
}
if auditErr := s.auditService.RecordEvent(ctx, "system", domain.ActorTypeSystem,
"deployment_job_failed", "certificate", job.CertificateID,
map[string]interface{}{"job_id": job.ID, "reason": "agent offline", "target_id": targetID})
map[string]interface{}{"job_id": job.ID, "reason": "agent offline", "target_id": targetID}); auditErr != nil {
slog.Error("failed to record audit event", "error", auditErr)
}
return fmt.Errorf("agent %s is offline", agentID)
}
@@ -157,9 +164,11 @@ func (s *DeploymentService) ProcessDeploymentJob(ctx context.Context, job *domai
// For now, we mark it as pending and rely on agent polling.
// Record audit event
_ = s.auditService.RecordEvent(ctx, "system", domain.ActorTypeSystem,
if auditErr := s.auditService.RecordEvent(ctx, "system", domain.ActorTypeSystem,
"deployment_job_dispatched", "certificate", job.CertificateID,
map[string]interface{}{"job_id": job.ID, "target_id": targetID, "agent_id": agentID})
map[string]interface{}{"job_id": job.ID, "target_id": targetID, "agent_id": agentID}); auditErr != nil {
slog.Error("failed to record audit event", "error", auditErr)
}
return nil
}
@@ -217,7 +226,7 @@ func (s *DeploymentService) MarkDeploymentComplete(ctx context.Context, jobID st
// Fetch certificate and target for notification
cert, err := s.certRepo.Get(ctx, job.CertificateID)
if err != nil {
fmt.Printf("failed to fetch certificate for notification: %v\n", err)
slog.Error("failed to fetch certificate for notification", "error", err)
return nil
}
@@ -229,20 +238,22 @@ func (s *DeploymentService) MarkDeploymentComplete(ctx context.Context, jobID st
if targetID != "" {
target, err := s.targetRepo.Get(ctx, targetID)
if err != nil {
fmt.Printf("failed to fetch target for notification: %v\n", err)
slog.Error("failed to fetch target for notification", "error", err)
return nil
}
// Send deployment success notification
if err := s.notificationSvc.SendDeploymentNotification(ctx, cert, target, true, nil); err != nil {
fmt.Printf("failed to send deployment notification: %v\n", err)
slog.Error("failed to send deployment notification", "error", err)
}
}
// Record audit event
_ = s.auditService.RecordEvent(ctx, "system", domain.ActorTypeSystem,
if auditErr := s.auditService.RecordEvent(ctx, "system", domain.ActorTypeSystem,
"deployment_job_completed", "certificate", job.CertificateID,
map[string]interface{}{"job_id": jobID, "target_id": targetID})
map[string]interface{}{"job_id": jobID, "target_id": targetID}); auditErr != nil {
slog.Error("failed to record audit event", "error", auditErr)
}
return nil
}
@@ -262,7 +273,7 @@ func (s *DeploymentService) MarkDeploymentFailed(ctx context.Context, jobID stri
// Fetch certificate and target for notification
cert, err := s.certRepo.Get(ctx, job.CertificateID)
if err != nil {
fmt.Printf("failed to fetch certificate for notification: %v\n", err)
slog.Error("failed to fetch certificate for notification", "error", err)
return nil
}
@@ -274,20 +285,22 @@ func (s *DeploymentService) MarkDeploymentFailed(ctx context.Context, jobID stri
if targetID != "" {
target, err := s.targetRepo.Get(ctx, targetID)
if err != nil {
fmt.Printf("failed to fetch target for notification: %v\n", err)
slog.Error("failed to fetch target for notification", "error", err)
return nil
}
// Send deployment failure notification
if err := s.notificationSvc.SendDeploymentNotification(ctx, cert, target, false, fmt.Errorf("%s", errMsg)); err != nil {
fmt.Printf("failed to send deployment notification: %v\n", err)
slog.Error("failed to send deployment notification", "error", err)
}
}
// Record audit event
_ = s.auditService.RecordEvent(ctx, "system", domain.ActorTypeSystem,
if auditErr := s.auditService.RecordEvent(ctx, "system", domain.ActorTypeSystem,
"deployment_job_failed", "certificate", job.CertificateID,
map[string]interface{}{"job_id": jobID, "target_id": targetID, "error": errMsg})
map[string]interface{}{"job_id": jobID, "target_id": targetID, "error": errMsg}); auditErr != nil {
slog.Error("failed to record audit event", "error", auditErr)
}
return nil
}
+10 -3
View File
@@ -3,6 +3,7 @@ package service
import (
"context"
"fmt"
"log/slog"
"time"
"github.com/shankar0123/certctl/internal/domain"
@@ -81,7 +82,9 @@ func (s *IssuerService) Create(ctx context.Context, issuer *domain.Issuer, actor
}
if s.auditService != nil {
_ = s.auditService.RecordEvent(ctx, actor, domain.ActorTypeUser, "create_issuer", "issuer", issuer.ID, nil)
if auditErr := s.auditService.RecordEvent(ctx, actor, domain.ActorTypeUser, "create_issuer", "issuer", issuer.ID, nil); auditErr != nil {
slog.Error("failed to record audit event", "error", auditErr)
}
}
return nil
@@ -99,7 +102,9 @@ func (s *IssuerService) Update(ctx context.Context, id string, issuer *domain.Is
}
if s.auditService != nil {
_ = s.auditService.RecordEvent(ctx, actor, domain.ActorTypeUser, "update_issuer", "issuer", id, nil)
if auditErr := s.auditService.RecordEvent(ctx, actor, domain.ActorTypeUser, "update_issuer", "issuer", id, nil); auditErr != nil {
slog.Error("failed to record audit event", "error", auditErr)
}
}
return nil
@@ -112,7 +117,9 @@ func (s *IssuerService) Delete(ctx context.Context, id string, actor string) err
}
if s.auditService != nil {
_ = s.auditService.RecordEvent(ctx, actor, domain.ActorTypeUser, "delete_issuer", "issuer", id, nil)
if auditErr := s.auditService.RecordEvent(ctx, actor, domain.ActorTypeUser, "delete_issuer", "issuer", id, nil); auditErr != nil {
slog.Error("failed to record audit event", "error", auditErr)
}
}
return nil
+9 -4
View File
@@ -3,6 +3,7 @@ package service
import (
"context"
"fmt"
"log/slog"
"time"
"github.com/shankar0123/certctl/internal/domain"
@@ -188,7 +189,7 @@ func (s *NotificationService) ProcessPendingNotifications(ctx context.Context) e
for _, notif := range pending {
if err := s.sendNotification(ctx, notif); err != nil {
fmt.Printf("failed to send notification %s: %v\n", notif.ID, err)
slog.Error("failed to send notification", "notification_id", notif.ID, "error", err)
failedCount++
}
}
@@ -206,20 +207,24 @@ func (s *NotificationService) sendNotification(ctx context.Context, notif *domai
notifier, ok := s.notifierRegistry[string(notif.Channel)]
if !ok {
// No notifier configured for this channel — mark as sent (demo mode)
_ = s.notifRepo.UpdateStatus(ctx, notif.ID, "sent", time.Now())
if updateErr := s.notifRepo.UpdateStatus(ctx, notif.ID, "sent", time.Now()); updateErr != nil {
slog.Error("failed to update notification status", "notification_id", notif.ID, "error", updateErr)
}
return nil
}
// Send the notification
if err := notifier.Send(ctx, notif.Recipient, string(notif.Type), notif.Message); err != nil {
// Update status to failed
_ = s.notifRepo.UpdateStatus(ctx, notif.ID, "failed", time.Time{})
if updateErr := s.notifRepo.UpdateStatus(ctx, notif.ID, "failed", time.Time{}); updateErr != nil {
slog.Error("failed to update notification status", "notification_id", notif.ID, "error", updateErr)
}
return fmt.Errorf("failed to send via %s: %w", notif.Channel, err)
}
// Update status to sent
if err := s.notifRepo.UpdateStatus(ctx, notif.ID, "sent", time.Now()); err != nil {
fmt.Printf("failed to update notification status: %v\n", err)
slog.Error("failed to update notification status", "notification_id", notif.ID, "error", err)
}
return nil
+10 -3
View File
@@ -3,6 +3,7 @@ package service
import (
"context"
"fmt"
"log/slog"
"time"
"github.com/shankar0123/certctl/internal/domain"
@@ -81,7 +82,9 @@ func (s *OwnerService) Create(ctx context.Context, owner *domain.Owner, actor st
}
if s.auditService != nil {
_ = s.auditService.RecordEvent(ctx, actor, domain.ActorTypeUser, "create_owner", "owner", owner.ID, nil)
if auditErr := s.auditService.RecordEvent(ctx, actor, domain.ActorTypeUser, "create_owner", "owner", owner.ID, nil); auditErr != nil {
slog.Error("failed to record audit event", "error", auditErr)
}
}
return nil
@@ -99,7 +102,9 @@ func (s *OwnerService) Update(ctx context.Context, id string, owner *domain.Owne
}
if s.auditService != nil {
_ = s.auditService.RecordEvent(ctx, actor, domain.ActorTypeUser, "update_owner", "owner", id, nil)
if auditErr := s.auditService.RecordEvent(ctx, actor, domain.ActorTypeUser, "update_owner", "owner", id, nil); auditErr != nil {
slog.Error("failed to record audit event", "error", auditErr)
}
}
return nil
@@ -112,7 +117,9 @@ func (s *OwnerService) Delete(ctx context.Context, id string, actor string) erro
}
if s.auditService != nil {
_ = s.auditService.RecordEvent(ctx, actor, domain.ActorTypeUser, "delete_owner", "owner", id, nil)
if auditErr := s.auditService.RecordEvent(ctx, actor, domain.ActorTypeUser, "delete_owner", "owner", id, nil); auditErr != nil {
slog.Error("failed to record audit event", "error", auditErr)
}
}
return nil
+5 -4
View File
@@ -3,6 +3,7 @@ package service
import (
"context"
"fmt"
"log/slog"
"time"
"github.com/shankar0123/certctl/internal/domain"
@@ -44,7 +45,7 @@ func (s *PolicyService) ValidateCertificate(ctx context.Context, cert *domain.Ma
// Evaluate rule against certificate
v, err := s.evaluateRule(rule, cert)
if err != nil {
fmt.Printf("failed to evaluate rule %s: %v\n", rule.ID, err)
slog.Error("failed to evaluate rule", "rule_id", rule.ID, "error", err)
continue
}
@@ -149,7 +150,7 @@ func (s *PolicyService) CreateRule(ctx context.Context, rule *domain.PolicyRule,
if err := s.auditService.RecordEvent(ctx, actor, domain.ActorTypeUser,
"policy_rule_created", "policy", rule.ID,
map[string]interface{}{"rule_type": rule.Type}); err != nil {
fmt.Printf("failed to record audit event: %v\n", err)
slog.Error("failed to record audit event", "error", err)
}
return nil
@@ -175,7 +176,7 @@ func (s *PolicyService) UpdateRule(ctx context.Context, rule *domain.PolicyRule,
if err := s.auditService.RecordEvent(ctx, actor, domain.ActorTypeUser,
"policy_rule_updated", "policy", rule.ID, changes); err != nil {
fmt.Printf("failed to record audit event: %v\n", err)
slog.Error("failed to record audit event", "error", err)
}
return nil
@@ -213,7 +214,7 @@ func (s *PolicyService) DeleteRule(ctx context.Context, id string, actor string)
if err := s.auditService.RecordEvent(ctx, actor, domain.ActorTypeUser,
"policy_rule_deleted", "policy", id,
map[string]interface{}{"rule_type": rule.Type}); err != nil {
fmt.Printf("failed to record audit event: %v\n", err)
slog.Error("failed to record audit event", "error", err)
}
return nil
+47 -31
View File
@@ -10,6 +10,7 @@ import (
"encoding/hex"
"encoding/pem"
"fmt"
"log/slog"
"time"
"github.com/shankar0123/certctl/internal/domain"
@@ -103,8 +104,7 @@ func (s *RenewalService) CheckExpiringCertificates(ctx context.Context) error {
policy, err = s.renewalPolicyRepo.Get(ctx, cert.RenewalPolicyID)
if err != nil {
// Log but continue with defaults
fmt.Printf("failed to fetch renewal policy %s for cert %s, using defaults: %v\n",
cert.RenewalPolicyID, cert.ID, err)
slog.Error("failed to fetch renewal policy, using defaults", "policy_id", cert.RenewalPolicyID, "cert_id", cert.ID, "error", err)
} else {
policyCache[cert.RenewalPolicyID] = policy
}
@@ -153,20 +153,22 @@ func (s *RenewalService) CheckExpiringCertificates(ctx context.Context) error {
}
if err := s.jobRepo.Create(ctx, job); err != nil {
fmt.Printf("failed to create renewal job for cert %s: %v\n", cert.ID, err)
slog.Error("failed to create renewal job for cert", "cert_id", cert.ID, "error", err)
continue
}
// Update certificate status to RenewalInProgress
cert.Status = domain.CertificateStatusRenewalInProgress
if err := s.certRepo.Update(ctx, cert); err != nil {
fmt.Printf("failed to update cert status for %s: %v\n", cert.ID, err)
slog.Error("failed to update cert status", "cert_id", cert.ID, "error", err)
}
// Record audit event
_ = s.auditService.RecordEvent(ctx, "system", domain.ActorTypeSystem,
if auditErr := s.auditService.RecordEvent(ctx, "system", domain.ActorTypeSystem,
"renewal_job_created", "certificate", cert.ID,
map[string]interface{}{"days_until_expiry": daysUntil, "job_id": job.ID})
map[string]interface{}{"days_until_expiry": daysUntil, "job_id": job.ID}); auditErr != nil {
slog.Error("failed to record audit event", "error", auditErr)
}
}
return nil
@@ -186,8 +188,7 @@ func (s *RenewalService) sendThresholdAlerts(ctx context.Context, cert *domain.M
// Check if we already sent a notification for this threshold (deduplication)
alreadySent, err := s.notificationSvc.HasThresholdNotification(ctx, cert.ID, threshold)
if err != nil {
fmt.Printf("failed to check notification dedup for cert %s threshold %d: %v\n",
cert.ID, threshold, err)
slog.Error("failed to check notification dedup", "cert_id", cert.ID, "threshold", threshold, "error", err)
continue
}
if alreadySent {
@@ -196,17 +197,18 @@ func (s *RenewalService) sendThresholdAlerts(ctx context.Context, cert *domain.M
// Send the threshold alert
if err := s.notificationSvc.SendThresholdAlert(ctx, cert, daysUntil, threshold); err != nil {
fmt.Printf("failed to send threshold alert for cert %s at %d days: %v\n",
cert.ID, threshold, err)
slog.Error("failed to send threshold alert for cert", "cert_id", cert.ID, "threshold", threshold, "error", err)
}
// Record audit event for the alert
_ = s.auditService.RecordEvent(ctx, "system", domain.ActorTypeSystem,
if auditErr := s.auditService.RecordEvent(ctx, "system", domain.ActorTypeSystem,
"expiration_alert_sent", "certificate", cert.ID,
map[string]interface{}{
"threshold_days": threshold,
"days_until_expiry": daysUntil,
})
}); auditErr != nil {
slog.Error("failed to record audit event", "error", auditErr)
}
}
}
@@ -234,7 +236,7 @@ func (s *RenewalService) updateCertExpiryStatus(ctx context.Context, cert *domai
cert.Status = newStatus
cert.UpdatedAt = time.Now()
if err := s.certRepo.Update(ctx, cert); err != nil {
fmt.Printf("failed to update cert %s status to %s: %v\n", cert.ID, newStatus, err)
slog.Error("failed to update cert status", "cert_id", cert.ID, "new_status", newStatus, "error", err)
}
}
@@ -290,13 +292,15 @@ func (s *RenewalService) processRenewalAgentKeygen(ctx context.Context, job *dom
cert.Status = domain.CertificateStatusRenewalInProgress
cert.UpdatedAt = time.Now()
if err := s.certRepo.Update(ctx, cert); err != nil {
fmt.Printf("failed to update cert status for %s: %v\n", cert.ID, err)
slog.Error("failed to update cert status", "cert_id", cert.ID, "error", err)
}
// Record audit event
_ = s.auditService.RecordEvent(ctx, "system", domain.ActorTypeSystem,
if auditErr := s.auditService.RecordEvent(ctx, "system", domain.ActorTypeSystem,
"renewal_awaiting_csr", "certificate", job.CertificateID,
map[string]interface{}{"job_id": job.ID, "keygen_mode": "agent"})
map[string]interface{}{"job_id": job.ID, "keygen_mode": "agent"}); auditErr != nil {
slog.Error("failed to record audit event", "error", auditErr)
}
return nil
}
@@ -343,10 +347,14 @@ func (s *RenewalService) processRenewalServerKeygen(ctx context.Context, job *do
result, err := connector.RenewCertificate(ctx, cert.CommonName, cert.SANs, csrPEM)
if err != nil {
s.failJob(ctx, job, fmt.Sprintf("issuer renewal failed: %v", err))
_ = s.notificationSvc.SendRenewalNotification(ctx, cert, false, err)
_ = s.auditService.RecordEvent(ctx, "system", domain.ActorTypeSystem,
if notifErr := s.notificationSvc.SendRenewalNotification(ctx, cert, false, err); notifErr != nil {
slog.Error("failed to send renewal failure notification", "error", notifErr)
}
if auditErr := s.auditService.RecordEvent(ctx, "system", domain.ActorTypeSystem,
"renewal_job_failed", "certificate", job.CertificateID,
map[string]interface{}{"job_id": job.ID, "error": err.Error()})
map[string]interface{}{"job_id": job.ID, "error": err.Error()}); auditErr != nil {
slog.Error("failed to record audit event", "error", auditErr)
}
return fmt.Errorf("issuer renewal failed: %w", err)
}
@@ -392,18 +400,20 @@ func (s *RenewalService) processRenewalServerKeygen(ctx context.Context, job *do
// Send success notification
if err := s.notificationSvc.SendRenewalNotification(ctx, cert, true, nil); err != nil {
fmt.Printf("failed to send renewal notification: %v\n", err)
slog.Error("failed to send renewal notification", "error", err)
}
// Record audit event
_ = s.auditService.RecordEvent(ctx, "system", domain.ActorTypeSystem,
if auditErr := s.auditService.RecordEvent(ctx, "system", domain.ActorTypeSystem,
"renewal_job_completed", "certificate", job.CertificateID,
map[string]interface{}{
"job_id": job.ID,
"serial": result.Serial,
"not_after": result.NotAfter,
"keygen_mode": "server",
})
}); auditErr != nil {
slog.Error("failed to record audit event", "error", auditErr)
}
return nil
}
@@ -427,10 +437,14 @@ func (s *RenewalService) CompleteAgentCSRRenewal(ctx context.Context, job *domai
result, err := connector.RenewCertificate(ctx, cert.CommonName, cert.SANs, csrPEM)
if err != nil {
s.failJob(ctx, job, fmt.Sprintf("issuer signing failed: %v", err))
_ = s.notificationSvc.SendRenewalNotification(ctx, cert, false, err)
_ = s.auditService.RecordEvent(ctx, "system", domain.ActorTypeSystem,
if notifErr := s.notificationSvc.SendRenewalNotification(ctx, cert, false, err); notifErr != nil {
slog.Error("failed to send renewal failure notification", "error", notifErr)
}
if auditErr := s.auditService.RecordEvent(ctx, "system", domain.ActorTypeSystem,
"renewal_job_failed", "certificate", job.CertificateID,
map[string]interface{}{"job_id": job.ID, "error": err.Error()})
map[string]interface{}{"job_id": job.ID, "error": err.Error()}); auditErr != nil {
slog.Error("failed to record audit event", "error", auditErr)
}
return fmt.Errorf("issuer signing failed: %w", err)
}
@@ -475,18 +489,20 @@ func (s *RenewalService) CompleteAgentCSRRenewal(ctx context.Context, job *domai
// Send success notification
if err := s.notificationSvc.SendRenewalNotification(ctx, cert, true, nil); err != nil {
fmt.Printf("failed to send renewal notification: %v\n", err)
slog.Error("failed to send renewal notification", "error", err)
}
// Record audit event
_ = s.auditService.RecordEvent(ctx, "system", domain.ActorTypeSystem,
if auditErr := s.auditService.RecordEvent(ctx, "system", domain.ActorTypeSystem,
"renewal_job_completed", "certificate", cert.ID,
map[string]interface{}{
"job_id": job.ID,
"serial": result.Serial,
"not_after": result.NotAfter,
"keygen_mode": "agent",
})
}); auditErr != nil {
slog.Error("failed to record audit event", "error", auditErr)
}
return nil
}
@@ -509,7 +525,7 @@ func (s *RenewalService) createDeploymentJobs(ctx context.Context, cert *domain.
CreatedAt: time.Now(),
}
if err := s.jobRepo.Create(ctx, deployJob); err != nil {
fmt.Printf("failed to create deployment job for target %s: %v\n", targetID, err)
slog.Error("failed to create deployment job for target", "target_id", targetID, "error", err)
}
}
}
@@ -532,7 +548,7 @@ func (s *RenewalService) GetAwaitingCSRJobs(ctx context.Context, certID string)
// failJob is a helper to mark a job as failed with an error message.
func (s *RenewalService) failJob(ctx context.Context, job *domain.Job, errMsg string) {
if updateErr := s.jobRepo.UpdateStatus(ctx, job.ID, domain.JobStatusFailed, errMsg); updateErr != nil {
fmt.Printf("failed to update job status: %v\n", updateErr)
slog.Error("failed to update job status", "job_id", job.ID, "error", updateErr)
}
}
@@ -565,7 +581,7 @@ func (s *RenewalService) RetryFailedJobs(ctx context.Context, maxRetries int) er
// Reset status to pending for retry
if err := s.jobRepo.UpdateStatus(ctx, job.ID, domain.JobStatusPending, ""); err != nil {
fmt.Printf("failed to reset job status for retry: %v\n", err)
slog.Error("failed to reset job status for retry", "job_id", job.ID, "error", err)
continue
}
}
+10 -3
View File
@@ -3,6 +3,7 @@ package service
import (
"context"
"fmt"
"log/slog"
"time"
"github.com/shankar0123/certctl/internal/domain"
@@ -81,7 +82,9 @@ func (s *TargetService) Create(ctx context.Context, target *domain.DeploymentTar
}
if s.auditService != nil {
_ = s.auditService.RecordEvent(ctx, actor, domain.ActorTypeUser, "create_target", "target", target.ID, nil)
if auditErr := s.auditService.RecordEvent(ctx, actor, domain.ActorTypeUser, "create_target", "target", target.ID, nil); auditErr != nil {
slog.Error("failed to record audit event", "error", auditErr)
}
}
return nil
@@ -99,7 +102,9 @@ func (s *TargetService) Update(ctx context.Context, id string, target *domain.De
}
if s.auditService != nil {
_ = s.auditService.RecordEvent(ctx, actor, domain.ActorTypeUser, "update_target", "target", id, nil)
if auditErr := s.auditService.RecordEvent(ctx, actor, domain.ActorTypeUser, "update_target", "target", id, nil); auditErr != nil {
slog.Error("failed to record audit event", "error", auditErr)
}
}
return nil
@@ -112,7 +117,9 @@ func (s *TargetService) Delete(ctx context.Context, id string, actor string) err
}
if s.auditService != nil {
_ = s.auditService.RecordEvent(ctx, actor, domain.ActorTypeUser, "delete_target", "target", id, nil)
if auditErr := s.auditService.RecordEvent(ctx, actor, domain.ActorTypeUser, "delete_target", "target", id, nil); auditErr != nil {
slog.Error("failed to record audit event", "error", auditErr)
}
}
return nil
+10 -3
View File
@@ -3,6 +3,7 @@ package service
import (
"context"
"fmt"
"log/slog"
"time"
"github.com/shankar0123/certctl/internal/domain"
@@ -81,7 +82,9 @@ func (s *TeamService) Create(ctx context.Context, team *domain.Team, actor strin
}
if s.auditService != nil {
_ = s.auditService.RecordEvent(ctx, actor, domain.ActorTypeUser, "create_team", "team", team.ID, nil)
if auditErr := s.auditService.RecordEvent(ctx, actor, domain.ActorTypeUser, "create_team", "team", team.ID, nil); auditErr != nil {
slog.Error("failed to record audit event", "error", auditErr)
}
}
return nil
@@ -99,7 +102,9 @@ func (s *TeamService) Update(ctx context.Context, id string, team *domain.Team,
}
if s.auditService != nil {
_ = s.auditService.RecordEvent(ctx, actor, domain.ActorTypeUser, "update_team", "team", id, nil)
if auditErr := s.auditService.RecordEvent(ctx, actor, domain.ActorTypeUser, "update_team", "team", id, nil); auditErr != nil {
slog.Error("failed to record audit event", "error", auditErr)
}
}
return nil
@@ -112,7 +117,9 @@ func (s *TeamService) Delete(ctx context.Context, id string, actor string) error
}
if s.auditService != nil {
_ = s.auditService.RecordEvent(ctx, actor, domain.ActorTypeUser, "delete_team", "team", id, nil)
if auditErr := s.auditService.RecordEvent(ctx, actor, domain.ActorTypeUser, "delete_team", "team", id, nil); auditErr != nil {
slog.Error("failed to record audit event", "error", auditErr)
}
}
return nil