- Add YarpGateway.slnx solution file - Move all project files to src/ directory - Update Dockerfile for new src/ path structure - Update CI/CD workflow with src/ project path - Fix NuGet package references (use Gitea NuGet packages) - Add CPM (Central Package Management) with Directory.Packages.props
210 lines
7.3 KiB
C#
210 lines
7.3 KiB
C#
using Microsoft.AspNetCore.Mvc;
|
|
using Microsoft.EntityFrameworkCore;
|
|
using YarpGateway.Data;
|
|
using YarpGateway.Models;
|
|
|
|
namespace YarpGateway.Controllers;
|
|
|
|
[ApiController]
|
|
[Route("api/gateway/pending-services")]
|
|
public class PendingServicesController : ControllerBase
|
|
{
|
|
private readonly IDbContextFactory<GatewayDbContext> _dbContextFactory;
|
|
private readonly ILogger<PendingServicesController> _logger;
|
|
|
|
public PendingServicesController(
|
|
IDbContextFactory<GatewayDbContext> dbContextFactory,
|
|
ILogger<PendingServicesController> logger)
|
|
{
|
|
_dbContextFactory = dbContextFactory;
|
|
_logger = logger;
|
|
}
|
|
|
|
[HttpGet]
|
|
public async Task<IActionResult> GetPendingServices(
|
|
[FromQuery] int page = 1,
|
|
[FromQuery] int pageSize = 10,
|
|
[FromQuery] int? status = null)
|
|
{
|
|
await using var db = _dbContextFactory.CreateDbContext();
|
|
var query = db.PendingServiceDiscoveries.Where(p => !p.IsDeleted);
|
|
|
|
if (status.HasValue)
|
|
{
|
|
query = query.Where(p => p.Status == status.Value);
|
|
}
|
|
|
|
var total = await query.CountAsync();
|
|
var items = await query
|
|
.OrderByDescending(p => p.DiscoveredAt)
|
|
.Skip((page - 1) * pageSize)
|
|
.Take(pageSize)
|
|
.Select(p => new
|
|
{
|
|
p.Id,
|
|
p.K8sServiceName,
|
|
p.K8sNamespace,
|
|
p.K8sClusterIP,
|
|
DiscoveredPorts = System.Text.Json.JsonSerializer.Deserialize<List<int>>(p.DiscoveredPorts) ?? new List<int>(),
|
|
Labels = System.Text.Json.JsonSerializer.Deserialize<Dictionary<string, string>>(p.Labels) ?? new Dictionary<string, string>(),
|
|
p.PodCount,
|
|
Status = (PendingServiceStatus)p.Status,
|
|
p.AssignedClusterId,
|
|
p.AssignedBy,
|
|
p.AssignedAt,
|
|
p.DiscoveredAt
|
|
})
|
|
.ToListAsync();
|
|
|
|
return Ok(new { items, total, page, pageSize });
|
|
}
|
|
|
|
[HttpGet("{id}")]
|
|
public async Task<IActionResult> GetPendingService(long id)
|
|
{
|
|
await using var db = _dbContextFactory.CreateDbContext();
|
|
var service = await db.PendingServiceDiscoveries.FindAsync(id);
|
|
|
|
if (service == null || service.IsDeleted)
|
|
{
|
|
return NotFound(new { message = "Pending service not found" });
|
|
}
|
|
|
|
return Ok(new
|
|
{
|
|
service.Id,
|
|
service.K8sServiceName,
|
|
service.K8sNamespace,
|
|
service.K8sClusterIP,
|
|
DiscoveredPorts = System.Text.Json.JsonSerializer.Deserialize<List<int>>(service.DiscoveredPorts) ?? new List<int>(),
|
|
Labels = System.Text.Json.JsonSerializer.Deserialize<Dictionary<string, string>>(service.Labels) ?? new Dictionary<string, string>(),
|
|
service.PodCount,
|
|
Status = (PendingServiceStatus)service.Status,
|
|
service.AssignedClusterId,
|
|
service.AssignedBy,
|
|
service.AssignedAt,
|
|
service.DiscoveredAt
|
|
});
|
|
}
|
|
|
|
[HttpPost("{id}/assign")]
|
|
public async Task<IActionResult> AssignService(long id, [FromBody] AssignServiceRequest request)
|
|
{
|
|
await using var db = _dbContextFactory.CreateDbContext();
|
|
|
|
var pendingService = await db.PendingServiceDiscoveries.FindAsync(id);
|
|
if (pendingService == null || pendingService.IsDeleted)
|
|
{
|
|
return NotFound(new { message = "Pending service not found" });
|
|
}
|
|
|
|
if (pendingService.Status != (int)PendingServiceStatus.Pending)
|
|
{
|
|
return BadRequest(new { message = $"Service is already {((PendingServiceStatus)pendingService.Status)}, cannot assign" });
|
|
}
|
|
|
|
if (string.IsNullOrEmpty(request.ClusterId))
|
|
{
|
|
return BadRequest(new { message = "ClusterId is required" });
|
|
}
|
|
|
|
var existingCluster = await db.ServiceInstances
|
|
.AnyAsync(i => i.ClusterId == request.ClusterId && !i.IsDeleted);
|
|
|
|
if (!existingCluster)
|
|
{
|
|
return BadRequest(new { message = $"Cluster '{request.ClusterId}' does not exist. Please create the cluster first." });
|
|
}
|
|
|
|
var discoveredPorts = System.Text.Json.JsonSerializer.Deserialize<List<int>>(pendingService.DiscoveredPorts) ?? new List<int>();
|
|
var primaryPort = discoveredPorts.FirstOrDefault() > 0 ? discoveredPorts.First() : 80;
|
|
|
|
var instanceNumber = await db.ServiceInstances
|
|
.CountAsync(i => i.ClusterId == request.ClusterId && !i.IsDeleted);
|
|
|
|
var newInstance = new GwServiceInstance
|
|
{
|
|
ClusterId = request.ClusterId,
|
|
DestinationId = $"{pendingService.K8sServiceName}-{instanceNumber + 1}",
|
|
Address = $"http://{pendingService.K8sClusterIP}:{primaryPort}",
|
|
Health = 1,
|
|
Weight = 100,
|
|
Status = 1,
|
|
CreatedTime = DateTime.UtcNow,
|
|
Version = 1
|
|
};
|
|
|
|
db.ServiceInstances.Add(newInstance);
|
|
|
|
pendingService.Status = (int)PendingServiceStatus.Approved;
|
|
pendingService.AssignedClusterId = request.ClusterId;
|
|
pendingService.AssignedBy = "admin";
|
|
pendingService.AssignedAt = DateTime.UtcNow;
|
|
pendingService.Version++;
|
|
|
|
await db.SaveChangesAsync();
|
|
|
|
_logger.LogInformation("Service {ServiceName} assigned to cluster {ClusterId} by admin",
|
|
pendingService.K8sServiceName, request.ClusterId);
|
|
|
|
return Ok(new
|
|
{
|
|
success = true,
|
|
message = $"Service '{pendingService.K8sServiceName}' assigned to cluster '{request.ClusterId}'",
|
|
instanceId = newInstance.Id
|
|
});
|
|
}
|
|
|
|
[HttpPost("{id}/reject")]
|
|
public async Task<IActionResult> RejectService(long id)
|
|
{
|
|
await using var db = _dbContextFactory.CreateDbContext();
|
|
|
|
var pendingService = await db.PendingServiceDiscoveries.FindAsync(id);
|
|
if (pendingService == null || pendingService.IsDeleted)
|
|
{
|
|
return NotFound(new { message = "Pending service not found" });
|
|
}
|
|
|
|
if (pendingService.Status != (int)PendingServiceStatus.Pending)
|
|
{
|
|
return BadRequest(new { message = $"Service is already {((PendingServiceStatus)pendingService.Status)}, cannot reject" });
|
|
}
|
|
|
|
pendingService.Status = (int)PendingServiceStatus.Rejected;
|
|
pendingService.AssignedBy = "admin";
|
|
pendingService.AssignedAt = DateTime.UtcNow;
|
|
pendingService.Version++;
|
|
|
|
await db.SaveChangesAsync();
|
|
|
|
_logger.LogInformation("Service {ServiceName} rejected by admin", pendingService.K8sServiceName);
|
|
|
|
return Ok(new { success = true, message = $"Service '{pendingService.K8sServiceName}' rejected" });
|
|
}
|
|
|
|
[HttpGet("clusters")]
|
|
public async Task<IActionResult> GetClusters()
|
|
{
|
|
await using var db = _dbContextFactory.CreateDbContext();
|
|
|
|
var clusters = await db.ServiceInstances
|
|
.Where(i => !i.IsDeleted)
|
|
.GroupBy(i => i.ClusterId)
|
|
.Select(g => new
|
|
{
|
|
ClusterId = g.Key,
|
|
InstanceCount = g.Count(),
|
|
HealthyCount = g.Count(i => i.Health == 1)
|
|
})
|
|
.ToListAsync();
|
|
|
|
return Ok(clusters);
|
|
}
|
|
}
|
|
|
|
public class AssignServiceRequest
|
|
{
|
|
public string ClusterId { get; set; } = string.Empty;
|
|
}
|