M-2 PR-B: Collapse IssuerService + TargetService to ctx-first signatures

- Delete bare TestConnection wrapper in IssuerService; rename
  TestConnectionWithContext → TestConnection
- Delete TestTargetConnection delegate shim in TargetService (canonical
  TestConnection already ctx-first)
- Add ctx first param to 10 handler-interface methods
  (ListIssuers/GetIssuer/CreateIssuer/UpdateIssuer/DeleteIssuer and
  ListTargets/GetTarget/CreateTarget/UpdateTarget/DeleteTarget)
- Replace 16 context.Background() call sites with received ctx
- Thread r.Context() through 12 HTTP handler sites in issuers.go and
  targets.go (outer TargetHandler.TestTargetConnection HTTP method name
  preserved for router compatibility)
- Update MockIssuerService, MockTargetService, and mockTargetService
  (integration) for ctx-first forwarding; update test callsite literals

Audit complete. Commit: 1f6cf0eafa. Sections: 12. Findings: 2/7/10/4/6.
This commit is contained in:
shankar0123
2026-04-18 00:46:58 +00:00
parent bbb628243f
commit eb14236166
9 changed files with 150 additions and 146 deletions
+15 -20
View File
@@ -260,9 +260,9 @@ func (s *IssuerService) Delete(ctx context.Context, id string, actor string) err
return nil
}
// TestConnectionWithContext tests the connection to an issuer by instantiating a throwaway
// TestConnection tests the connection to an issuer by instantiating a throwaway
// connector and calling ValidateConfig. Records the result in the database.
func (s *IssuerService) TestConnectionWithContext(ctx context.Context, id string) error {
func (s *IssuerService) TestConnection(ctx context.Context, id string) error {
iss, err := s.issuerRepo.Get(ctx, id)
if err != nil {
return fmt.Errorf("issuer not found: %w", err)
@@ -291,11 +291,6 @@ func (s *IssuerService) TestConnectionWithContext(ctx context.Context, id string
return nil
}
// TestConnection verifies the issuer connection (handler interface method).
func (s *IssuerService) TestConnection(id string) error {
return s.TestConnectionWithContext(context.Background(), id)
}
// BuildRegistry loads all enabled issuers from the database and rebuilds the dynamic registry.
// Called at server startup. Partial failures (individual issuers failing to load) are logged
// as warnings but don't prevent the server from starting.
@@ -633,7 +628,7 @@ func (s *IssuerService) buildEnvVarSeeds(cfg *config.Config) []*domain.Issuer {
}
// ListIssuers returns paginated issuers (handler interface method).
func (s *IssuerService) ListIssuers(page, perPage int) ([]domain.Issuer, int64, error) {
func (s *IssuerService) ListIssuers(ctx context.Context, page, perPage int) ([]domain.Issuer, int64, error) {
if page < 1 {
page = 1
}
@@ -641,7 +636,7 @@ func (s *IssuerService) ListIssuers(page, perPage int) ([]domain.Issuer, int64,
perPage = 50
}
issuers, err := s.issuerRepo.List(context.Background())
issuers, err := s.issuerRepo.List(ctx)
if err != nil {
return nil, 0, fmt.Errorf("failed to list issuers: %w", err)
}
@@ -658,12 +653,12 @@ func (s *IssuerService) ListIssuers(page, perPage int) ([]domain.Issuer, int64,
}
// GetIssuer returns a single issuer (handler interface method).
func (s *IssuerService) GetIssuer(id string) (*domain.Issuer, error) {
return s.issuerRepo.Get(context.Background(), id)
func (s *IssuerService) GetIssuer(ctx context.Context, id string) (*domain.Issuer, error) {
return s.issuerRepo.Get(ctx, id)
}
// CreateIssuer creates a new issuer (handler interface method).
func (s *IssuerService) CreateIssuer(iss domain.Issuer) (*domain.Issuer, error) {
func (s *IssuerService) CreateIssuer(ctx context.Context, iss domain.Issuer) (*domain.Issuer, error) {
iss.Type = normalizeIssuerType(iss.Type)
if !isValidIssuerType(iss.Type) {
return nil, fmt.Errorf("unsupported issuer type: %s", iss.Type)
@@ -700,26 +695,26 @@ func (s *IssuerService) CreateIssuer(iss domain.Issuer) (*domain.Issuer, error)
iss.Config = redactConfigJSON(iss.Config)
}
if err := s.issuerRepo.Create(context.Background(), &iss); err != nil {
if err := s.issuerRepo.Create(ctx, &iss); err != nil {
return nil, fmt.Errorf("failed to create issuer: %w", err)
}
// Rebuild registry
if iss.Enabled {
s.rebuildRegistryQuiet(context.Background())
s.rebuildRegistryQuiet(ctx)
}
return &iss, nil
}
// UpdateIssuer modifies an issuer (handler interface method).
func (s *IssuerService) UpdateIssuer(id string, iss domain.Issuer) (*domain.Issuer, error) {
func (s *IssuerService) UpdateIssuer(ctx context.Context, id string, iss domain.Issuer) (*domain.Issuer, error) {
iss.ID = id
iss.UpdatedAt = time.Now()
// Merge redacted fields with existing config
if len(iss.Config) > 0 {
mergedConfig, err := s.mergeRedactedConfig(context.Background(), id, iss.Config)
mergedConfig, err := s.mergeRedactedConfig(ctx, id, iss.Config)
if err != nil {
return nil, fmt.Errorf("failed to merge config: %w", err)
}
@@ -732,18 +727,18 @@ func (s *IssuerService) UpdateIssuer(id string, iss domain.Issuer) (*domain.Issu
iss.Config = redactConfigJSON(json.RawMessage(mergedConfig))
}
if err := s.issuerRepo.Update(context.Background(), &iss); err != nil {
if err := s.issuerRepo.Update(ctx, &iss); err != nil {
return nil, fmt.Errorf("failed to update issuer: %w", err)
}
s.rebuildRegistryQuiet(context.Background())
s.rebuildRegistryQuiet(ctx)
return &iss, nil
}
// DeleteIssuer removes an issuer (handler interface method).
func (s *IssuerService) DeleteIssuer(id string) error {
if err := s.issuerRepo.Delete(context.Background(), id); err != nil {
func (s *IssuerService) DeleteIssuer(ctx context.Context, id string) error {
if err := s.issuerRepo.Delete(ctx, id); err != nil {
return err
}
if s.registry != nil {
+11 -7
View File
@@ -484,10 +484,10 @@ func TestIssuerService_TestConnection_Success(t *testing.T) {
svc := NewIssuerService(repo, auditService, NewIssuerRegistry(slog.Default()), "", slog.Default())
err := svc.TestConnectionWithContext(ctx, "iss-test-conn")
err := svc.TestConnection(ctx, "iss-test-conn")
if err != nil {
t.Fatalf("TestConnectionWithContext failed: %v", err)
t.Fatalf("TestConnection failed: %v", err)
}
}
@@ -502,7 +502,7 @@ func TestIssuerService_TestConnection_NotFound(t *testing.T) {
registry := NewIssuerRegistry(slog.Default())
service := NewIssuerService(repo, auditService, registry, "", slog.Default())
err := service.TestConnectionWithContext(ctx, "nonexistent-issuer")
err := service.TestConnection(ctx, "nonexistent-issuer")
if err == nil {
t.Fatal("expected error for nonexistent issuer")
@@ -542,7 +542,8 @@ func TestIssuerService_ListIssuers_HandlerInterface(t *testing.T) {
service := NewIssuerService(repo, auditService, NewIssuerRegistry(slog.Default()), "", slog.Default())
issuers, total, err := service.ListIssuers(1, 50)
ctx := context.Background()
issuers, total, err := service.ListIssuers(ctx, 1, 50)
if err != nil {
t.Fatalf("ListIssuers failed: %v", err)
@@ -580,7 +581,8 @@ func TestIssuerService_CreateIssuer_HandlerInterface(t *testing.T) {
Enabled: true,
}
result, err := service.CreateIssuer(issuer)
ctx := context.Background()
result, err := service.CreateIssuer(ctx, issuer)
if err != nil {
t.Fatalf("CreateIssuer failed: %v", err)
@@ -608,7 +610,8 @@ func TestIssuerService_DeleteIssuer_HandlerInterface(t *testing.T) {
registry := NewIssuerRegistry(slog.Default())
service := NewIssuerService(repo, auditService, registry, "", slog.Default())
err := service.DeleteIssuer("iss-handler-delete")
ctx := context.Background()
err := service.DeleteIssuer(ctx, "iss-handler-delete")
if err != nil {
t.Fatalf("DeleteIssuer failed: %v", err)
@@ -722,7 +725,8 @@ func TestIssuerService_CreateIssuer_LowercaseType(t *testing.T) {
Enabled: true,
}
result, err := service.CreateIssuer(issuer)
ctx := context.Background()
result, err := service.CreateIssuer(ctx, issuer)
if err != nil {
t.Fatalf("CreateIssuer with lowercase 'stepca' should succeed, got: %v", err)
}
+11 -16
View File
@@ -242,7 +242,7 @@ func (s *TargetService) TestConnection(ctx context.Context, id string) error {
}
// ListTargets returns paginated targets (handler interface method).
func (s *TargetService) ListTargets(page, perPage int) ([]domain.DeploymentTarget, int64, error) {
func (s *TargetService) ListTargets(ctx context.Context, page, perPage int) ([]domain.DeploymentTarget, int64, error) {
if page < 1 {
page = 1
}
@@ -250,7 +250,7 @@ func (s *TargetService) ListTargets(page, perPage int) ([]domain.DeploymentTarge
perPage = 50
}
targets, err := s.targetRepo.List(context.Background())
targets, err := s.targetRepo.List(ctx)
if err != nil {
return nil, 0, fmt.Errorf("failed to list targets: %w", err)
}
@@ -267,12 +267,12 @@ func (s *TargetService) ListTargets(page, perPage int) ([]domain.DeploymentTarge
}
// GetTarget returns a single target (handler interface method).
func (s *TargetService) GetTarget(id string) (*domain.DeploymentTarget, error) {
return s.targetRepo.Get(context.Background(), id)
func (s *TargetService) GetTarget(ctx context.Context, id string) (*domain.DeploymentTarget, error) {
return s.targetRepo.Get(ctx, id)
}
// CreateTarget creates a new target (handler interface method).
func (s *TargetService) CreateTarget(target domain.DeploymentTarget) (*domain.DeploymentTarget, error) {
func (s *TargetService) CreateTarget(ctx context.Context, target domain.DeploymentTarget) (*domain.DeploymentTarget, error) {
if !isValidTargetType(target.Type) {
return nil, fmt.Errorf("unsupported target type: %s", target.Type)
}
@@ -308,20 +308,20 @@ func (s *TargetService) CreateTarget(target domain.DeploymentTarget) (*domain.De
target.Config = redactConfigJSON(target.Config)
}
if err := s.targetRepo.Create(context.Background(), &target); err != nil {
if err := s.targetRepo.Create(ctx, &target); err != nil {
return nil, fmt.Errorf("failed to create target: %w", err)
}
return &target, nil
}
// UpdateTarget modifies a target (handler interface method).
func (s *TargetService) UpdateTarget(id string, target domain.DeploymentTarget) (*domain.DeploymentTarget, error) {
func (s *TargetService) UpdateTarget(ctx context.Context, id string, target domain.DeploymentTarget) (*domain.DeploymentTarget, error) {
target.ID = id
target.UpdatedAt = time.Now()
// Merge redacted fields with existing config
if len(target.Config) > 0 {
mergedConfig, err := s.mergeRedactedConfig(context.Background(), id, target.Config)
mergedConfig, err := s.mergeRedactedConfig(ctx, id, target.Config)
if err != nil {
return nil, fmt.Errorf("failed to merge config: %w", err)
}
@@ -334,20 +334,15 @@ func (s *TargetService) UpdateTarget(id string, target domain.DeploymentTarget)
target.Config = redactConfigJSON(json.RawMessage(mergedConfig))
}
if err := s.targetRepo.Update(context.Background(), &target); err != nil {
if err := s.targetRepo.Update(ctx, &target); err != nil {
return nil, fmt.Errorf("failed to update target: %w", err)
}
return &target, nil
}
// DeleteTarget removes a target (handler interface method).
func (s *TargetService) DeleteTarget(id string) error {
return s.targetRepo.Delete(context.Background(), id)
}
// TestTargetConnection tests target connectivity (handler interface method).
func (s *TargetService) TestTargetConnection(id string) error {
return s.TestConnection(context.Background(), id)
func (s *TargetService) DeleteTarget(ctx context.Context, id string) error {
return s.targetRepo.Delete(ctx, id)
}
// --- Internal helpers ---
+12 -6
View File
@@ -344,7 +344,8 @@ func TestTargetService_ListTargets_Success(t *testing.T) {
targetRepo.AddTarget(target2)
// Call handler-interface method
targets, total, err := svc.ListTargets(1, 50)
ctx := context.Background()
targets, total, err := svc.ListTargets(ctx, 1, 50)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
@@ -364,7 +365,8 @@ func TestTargetService_GetTarget_Success(t *testing.T) {
target := &domain.DeploymentTarget{ID: "t-1", Name: "Target 1", Type: domain.TargetTypeNGINX}
targetRepo.AddTarget(target)
result, err := svc.GetTarget("t-1")
ctx := context.Background()
result, err := svc.GetTarget(ctx, "t-1")
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
@@ -382,7 +384,8 @@ func TestTargetService_CreateTarget_Success(t *testing.T) {
Type: domain.TargetTypeNGINX,
}
result, err := svc.CreateTarget(target)
ctx := context.Background()
result, err := svc.CreateTarget(ctx, target)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
@@ -405,7 +408,8 @@ func TestTargetService_CreateTarget_InvalidType(t *testing.T) {
Type: domain.TargetType("Unknown"),
}
_, err := svc.CreateTarget(target)
ctx := context.Background()
_, err := svc.CreateTarget(ctx, target)
if err == nil {
t.Fatalf("expected error for invalid type, got nil")
}
@@ -424,7 +428,8 @@ func TestTargetService_UpdateTarget_Success(t *testing.T) {
Type: domain.TargetTypeApache,
}
result, err := svc.UpdateTarget("t-1", updated)
ctx := context.Background()
result, err := svc.UpdateTarget(ctx, "t-1", updated)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
@@ -442,7 +447,8 @@ func TestTargetService_DeleteTarget_Success(t *testing.T) {
targetRepo.AddTarget(target)
// Delete it
err := svc.DeleteTarget("t-1")
ctx := context.Background()
err := svc.DeleteTarget(ctx, "t-1")
if err != nil {
t.Fatalf("unexpected error: %v", err)
}