Centralized Version Aggregation Service¶
Overview¶
This document outlines the planned architecture for centralizing service version information through the API service, eliminating the need for public ingresses on internal services.
Current Architecture (Temporary)¶
Currently, the web application fetches version information directly from multiple backend services:
┌─────────────────┐
│ Web Frontend │
└────────┬────────┘
│
┌────┴────┬────────────┬────────────┐
▼ ▼ ▼ ▼
┌───────┐ ┌───────┐ ┌────────────┐ ┌────────┐
│ API │ │ PM │ │ Quartz │ │ Lambda │
│/health│ │/health│ │ /health │ │ (AWS) │
└───────┘ └───────┘ └────────────┘ └────────┘
│ │ │ │
▼ ▼ ▼ ▼
Public Public Public Version in
Ingress Ingress Ingress config only
Current Implementation Notes:
- API, PM, and Quartz all expose /health/live endpoints publicly
- Web app makes 3 parallel HTTP requests to fetch version info
- S3 Notifier version comes from static configuration (Lambda has no health endpoint)
- Each service maintains its own GitVersion information
Why Public Ingresses Were Added (Temporary): - Quick solution to display version info in version dialog - Allows debugging in staging/production environments - Enables health monitoring per-service
Target Architecture (Planned)¶
Centralize all version information through the API service:
┌─────────────────┐
│ Web Frontend │
└────────┬────────┘
│
▼ HTTP + SignalR
┌─────────┐
│ API │ ◄── Public Ingress (single entry point)
│/version │
└────┬────┘
│
┌────┴────┬────────────┐
▼ ▼ ▼
┌───────┐ ┌───────┐ ┌────────────┐
│ PM │ │Quartz │ │ Lambda │
│(K8s) │ │(K8s) │ │ (AWS) │
└───────┘ └───────┘ └────────────┘
│ │ │
▼ ▼ ▼
Internal Internal Version
Service Service via config
Key Changes¶
- Remove Public Ingresses: Disable public access to PM and Quartz
- API Version Endpoint: New
/api/version/servicesendpoint - SignalR Integration: Real-time version updates via existing SignalR hub
- MassTransit Health Checks: Periodic internal health aggregation
Implementation Plan¶
Phase 1: API Version Endpoint¶
New Endpoint: GET /api/version/services
public class ServicesVersionResponse
{
public ServiceVersion Api { get; set; }
public ServiceVersion ProjectManagement { get; set; }
public ServiceVersion Quartz { get; set; }
public ServiceVersion S3Notifier { get; set; }
public DateTime Timestamp { get; set; }
}
public class ServiceVersion
{
public string Name { get; set; }
public string Version { get; set; }
public string Status { get; set; } // healthy, unhealthy, unavailable
public string Sha { get; set; }
public string HealthStatus { get; set; }
}
Implementation Location: src/services/api/SyRF.API.Endpoint/Controllers/VersionController.cs
Phase 2: Internal Health Aggregation¶
MassTransit Health Consumer:
public class HealthAggregationService : IHostedService
{
private readonly IHttpClientFactory _httpClientFactory;
private readonly IOptions<ServicesConfiguration> _config;
private readonly IHubContext<SyrfHub> _hubContext;
// Poll internal services every 30 seconds
// Compare with previous state
// Broadcast changes via SignalR
}
Internal Service Discovery:
- PM: http://project-management.{namespace}.svc.cluster.local:8081/health/live
- Quartz: http://quartz.{namespace}.svc.cluster.local:8082/health/live
- S3 Notifier: Version from configuration (no runtime check)
Phase 3: SignalR Integration¶
Hub Method:
public class SyrfHub : Hub
{
// Existing hub methods...
// New: Called by HealthAggregationService when versions change
public async Task BroadcastServiceVersionsChanged(ServicesVersionResponse versions)
{
await Clients.All.SendAsync("ServiceVersionsChanged", versions);
}
}
Web Frontend Changes:
// version-info-dialog.component.ts
private setupSignalRSubscription(): void {
this.signalRService.on<ServicesVersionResponse>('ServiceVersionsChanged', (versions) => {
this.updateVersionsFromSignalR(versions);
});
}
Phase 4: Remove Public Ingresses¶
cluster-gitops Changes:
# syrf/environments/staging/quartz/values.yaml
ingress:
enabled: false # Changed from true
# syrf/environments/production/quartz/values.yaml
ingress:
enabled: false # Changed from true
Web Configuration Changes:
# syrf/environments/staging/web/values.yaml
quartz:
origin: "" # No longer needed - API aggregates
pm:
origin: "" # No longer needed - API aggregates
Benefits¶
- Security: Reduces attack surface by removing public endpoints
- Simplicity: Single entry point for version information
- Real-time Updates: SignalR provides instant notifications of version changes
- Consistency: Version info comes from authoritative source (API)
- Cost: Fewer ingress resources and TLS certificates
Migration Path¶
- Current State: Public ingresses enabled (temporary)
- Implement API endpoint: Add
/api/version/servicesto API - Update Web: Switch version dialog to use new API endpoint
- Add SignalR: Implement real-time version notifications
- Remove Ingresses: Disable public access to PM/Quartz
- Clean up config: Remove unused origin configuration
Security Considerations¶
- Internal service discovery uses Kubernetes DNS
- No credentials needed for internal health checks
- Version information is not sensitive (already public in UI)
- SignalR already uses existing authentication
Configuration¶
Services Configuration (API appsettings):
{
"Services": {
"ProjectManagement": {
"InternalUrl": "http://project-management:8081"
},
"Quartz": {
"InternalUrl": "http://quartz:8082"
},
"S3Notifier": {
"Version": "${S3NotifierVersion}"
}
},
"HealthAggregation": {
"PollIntervalSeconds": 30,
"TimeoutSeconds": 5
}
}
Timeline¶
| Phase | Description | Estimated Effort |
|---|---|---|
| 1 | API Version Endpoint | 2-3 hours |
| 2 | Internal Health Aggregation | 3-4 hours |
| 3 | SignalR Integration | 2-3 hours |
| 4 | Web Frontend Changes | 2-3 hours |
| 5 | Remove Ingresses + Config | 1 hour |
Total Estimated Effort: 10-14 hours
Related Documents¶
Open Questions¶
- Should we cache version info in Redis for faster responses?
- How should we handle version info when services are starting up?
- Should the health aggregation run on all API replicas or just one?