Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ public class DefaultHostnameSynchronizerTests
{
private readonly Mock<ILogger<DefaultHostnameSynchronizer>> _loggerMock;
private readonly Mock<IIngressManager> _ingressManagerMock;
private readonly Mock<INamespaceManager> _namespaceManagerMock;
private readonly Mock<IServiceManager> _serviceManagerMock;
private readonly Mock<ICache> _cacheMock;
private readonly Mock<IHostApplicationLifetime> _lifetimeMock;
Expand All @@ -31,7 +30,6 @@ public DefaultHostnameSynchronizerTests()
{
_loggerMock = new Mock<ILogger<DefaultHostnameSynchronizer>>();
_ingressManagerMock = new Mock<IIngressManager>();
_namespaceManagerMock = new Mock<INamespaceManager>();
_serviceManagerMock = new Mock<IServiceManager>();
_cacheMock = new Mock<ICache>();
_lifetimeMock = new Mock<IHostApplicationLifetime>();
Expand All @@ -56,7 +54,6 @@ private DefaultHostnameSynchronizer CreateSynchronizer()
return new DefaultHostnameSynchronizer(
_loggerMock.Object,
_ingressManagerMock.Object,
_namespaceManagerMock.Object,
_serviceManagerMock.Object,
_cacheMock.Object,
Options.Create(_options),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,21 +10,18 @@ public class DefaultIngressManagerTests
{
private readonly Mock<ILogger<DefaultIngressManager>> _loggerMock;
private readonly Mock<KubeOps.KubernetesClient.IKubernetesClient> _kubernetesClientMock;
private readonly Mock<INamespaceManager> _namespaceManagerMock;
private readonly Mock<IServiceManager> _serviceManagerMock;
private readonly DefaultIngressManager _ingressManager;

public DefaultIngressManagerTests()
{
_loggerMock = new Mock<ILogger<DefaultIngressManager>>();
_kubernetesClientMock = new Mock<KubeOps.KubernetesClient.IKubernetesClient>();
_namespaceManagerMock = new Mock<INamespaceManager>();
_serviceManagerMock = new Mock<IServiceManager>();

_ingressManager = new DefaultIngressManager(
_loggerMock.Object,
_kubernetesClientMock.Object,
_namespaceManagerMock.Object,
_serviceManagerMock.Object);
}

Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -168,8 +168,7 @@ public async Task GetLoadBalancerServicesAsync_WithEmptyServices_ReturnsEmpty()
private DefaultServiceManager CreateServiceManager()
{
var kubernetesClientMock = new Mock<KubeOps.KubernetesClient.IKubernetesClient>();
var namespaceManagerMock = new Mock<INamespaceManager>();
return new DefaultServiceManager(_loggerMock.Object, kubernetesClientMock.Object, namespaceManagerMock.Object);
return new DefaultServiceManager(_loggerMock.Object, kubernetesClientMock.Object);
}

private V1EndpointSlice CreateEndpointSlice(int readyCount, int notReadyCount)
Expand Down
6 changes: 4 additions & 2 deletions src/Vecc.K8s.MultiCluster.Api/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -107,14 +107,16 @@
throw new Exception($"Expected one of {OperatorFlag}, {OrchestratorFlag}, {DnsServerFlag} or {FrontEndFlag}");
}

builder.Services.AddMemoryCache();
builder.Services.AddSingleton<IBasicCache, BasicCache>();
builder.Services.AddSingleton<IKubernetesCache, KubernetesApiCache>();
builder.Services.AddSingleton<LeaderStatus>();
builder.Services.AddSingleton<DefaultDnsResolver>();
builder.Services.AddSingleton<IGslbManager, DefaultGslbManager>();
builder.Services.AddSingleton<IIngressManager, DefaultIngressManager>();
builder.Services.AddSingleton<INamespaceManager, DefaultNamespaceManager>();
builder.Services.AddSingleton<IServiceManager, DefaultServiceManager>();
builder.Services.AddSingleton<IHostnameSynchronizer, DefaultHostnameSynchronizer>();
builder.Services.AddSingleton<ICache, KubernetesApiCache>();
builder.Services.AddSingleton<ICache, MemoryCache>();
builder.Services.AddSingleton<IDateTimeProvider, DefaultDateTimeProvider>();
builder.Services.AddSingleton<IRandom, DefaultRandom>();
builder.Services.AddSingleton<DefaultDnsResolver>();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
using Microsoft.Extensions.Caching.Memory;
using M = Microsoft.Extensions.Caching.Memory;

namespace Vecc.K8s.MultiCluster.Api.Services.Default
{
public class BasicCache : IBasicCache
{
private readonly M.MemoryCache _cache;

public BasicCache(M.IMemoryCache cache)
{
_cache = cache as M.MemoryCache ?? throw new ArgumentException("Expected MemoryCache", nameof(cache));
}

public IEnumerable<object> Keys => _cache.Keys;

public T? Get<T>(string key)
=> _cache.Get<T>(key);

public T? GetOrCreate<T>(string key, Func<T> createFunc)
=> _cache.GetOrCreate(key, (_) => createFunc());

public bool TryGetValue<T>(string key, out T? value)
=> _cache.TryGetValue(key, out value);

public void Set<T>(string key, T value)
=> _cache.Set(key, value);

public void Remove(string key)
=> _cache.Remove(key);
}
}
136 changes: 136 additions & 0 deletions src/Vecc.K8s.MultiCluster.Api/Services/Default/DefaultCache.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
using Microsoft.Extensions.Options;
using Vecc.K8s.MultiCluster.Api.Models.K8sEntities;
using k8s.Models;

namespace Vecc.K8s.MultiCluster.Api.Services.Default;

public class MemoryCache(
IBasicCache _cache,
IKubernetesCache _kubernetesCache,
IOptions<MultiClusterOptions> _options) : ICache
{
public Task<DateTime?> GetClusterHeartbeatTimeAsync(string clusterIdentifier)
=> _kubernetesCache.GetClusterHeartbeatTimeAsync(clusterIdentifier);

public Task<string[]> GetClusterIdentifiersAsync()
=> _kubernetesCache.GetClusterIdentifiersAsync();

public async Task<int> GetEndpointsCountAsync(string ns, string name)
{
var service = await GetOrCreateServiceCache(ns, name, false);
var result = service?.EndpointCount ?? 0;
return result;
}

public Task<Models.Core.Host?> GetHostInformationAsync(string hostname)
=> _kubernetesCache.GetHostInformationAsync(hostname);

public Task<string[]> GetHostnamesAsync()
=> _kubernetesCache.GetHostnamesAsync();

public Task<Models.Core.Host[]?> GetHostsAsync(string clusterIdentifier)
=> _kubernetesCache.GetHostsAsync(clusterIdentifier);

public Task<string> GetLastResourceVersionAsync(string uniqueIdentifier)
{
lock (_cache)
{
_cache.TryGetValue(GetResourceVersionKey(uniqueIdentifier), out string? cacheValue);
var result = cacheValue ?? string.Empty;
return Task.FromResult(result);
}
}

public Task<bool> IsServiceMonitoredAsync(string ns, string name)
=> Task.FromResult(_cache.TryGetValue<V1ServiceCache>(GetServiceCacheKey(ns, name), out _));

public Task RemoveClusterCacheAsync(string clusterIdentifier)
=> _kubernetesCache.RemoveClusterCacheAsync(clusterIdentifier);

public Task SetClusterCacheAsync(string identifier, Models.Core.Host[] hosts)
=> _kubernetesCache.SetClusterCacheAsync(identifier, hosts);

public Task SetClusterHeartbeatAsync(string clusterIdentifier, DateTime heartbeat)
=> _kubernetesCache.SetClusterHeartbeatAsync(clusterIdentifier, heartbeat);

public async Task SetEndpointsCountAsync(string ns, string name, int count)
{
var serviceCache = await GetOrCreateServiceCache(ns, name);
serviceCache!.EndpointCount = count;
}

public Task SetResourceVersionAsync(string uniqueIdentifier, string version)
{
lock (_cache)
{
_cache.Set(GetResourceVersionKey(uniqueIdentifier), version);
}
return Task.CompletedTask;

}

public Task SynchronizeCachesAsync()
=> _kubernetesCache.SynchronizeCachesAsync();

public Task TrackServiceAsync(string ns, string name)
=> GetOrCreateServiceCache(ns, name);

public Task UntrackAllServicesAsync()
{
var rawKeys = Array.Empty<object>();

lock (_cache)
{
rawKeys = _cache.Keys.ToArray();
}

var keys = rawKeys.OfType<string>()
.Where(key => key.StartsWith("service-"));

foreach (var key in keys)
{
_cache.Remove(key);
}

Comment thread
EdwardCooke marked this conversation as resolved.
return Task.CompletedTask;
}

private Task<V1ServiceCache?> GetOrCreateServiceCache(string namespaceName, string name, bool createMissing = true)
{
if (createMissing)
{
var cacheKey = GetServiceCacheKey(namespaceName, name);
var result = _cache.GetOrCreate(cacheKey, () =>
{
var serviceCache = new V1ServiceCache();
var metadata = serviceCache.EnsureMetadata();
var labels = metadata.EnsureLabels();

labels["namespace"] = namespaceName;
labels["name"] = name;

metadata.Name = namespaceName + "." + name;
metadata.SetNamespace(_options.Value.Namespace);
serviceCache.Service = new V1ObjectReference
{
Name = name,
NamespaceProperty = namespaceName
};
return serviceCache;
}
);
return Task.FromResult(result);
}

return Task.FromResult(_cache.Get<V1ServiceCache>(GetServiceCacheKey(namespaceName, name)));
}

private string GetIngressKey(string ns, string name)
=> $"ingress-{ns}-{name}";

private string GetResourceVersionKey(string uniqueIdentifier)
=> $"resourceVersion-{uniqueIdentifier}";

private string GetServiceCacheKey(string ns, string name)
=> $"service-{ns}-{name}";
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ public class DefaultHostnameSynchronizer : IHostnameSynchronizer
{
private readonly ILogger<DefaultHostnameSynchronizer> _logger;
private readonly IIngressManager _ingressManager;
private readonly INamespaceManager _namespaceManager;
private readonly IServiceManager _serviceManager;
private readonly ICache _cache;
private readonly IOptions<MultiClusterOptions> _multiClusterOptions;
Expand All @@ -24,12 +23,11 @@ public class DefaultHostnameSynchronizer : IHostnameSynchronizer
private readonly CancellationTokenSource _shutdownCancellationTokenSource;
private readonly CancellationToken _shutdownCancellationToken;
private readonly ManualResetEvent _shutdownEvent;
private readonly AutoResetEvent _synchronizeLocalClusterHolder;
private readonly ManualResetEventSlim _synchronizingLocalClustersEvent = new ManualResetEventSlim(true);

public DefaultHostnameSynchronizer(
ILogger<DefaultHostnameSynchronizer> logger,
IIngressManager ingressManager,
INamespaceManager namespaceManager,
IServiceManager serviceManager,
ICache cache,
IOptions<MultiClusterOptions> multiClusterOptions,
Expand All @@ -41,7 +39,6 @@ public DefaultHostnameSynchronizer(
{
_logger = logger;
_ingressManager = ingressManager;
_namespaceManager = namespaceManager;
_serviceManager = serviceManager;
_cache = cache;
_multiClusterOptions = multiClusterOptions;
Expand All @@ -54,13 +51,17 @@ public DefaultHostnameSynchronizer(
_shutdownCancellationTokenSource = new CancellationTokenSource();
_shutdownEvent = new ManualResetEvent(true);
_shutdownCancellationToken = _shutdownCancellationTokenSource.Token;
_synchronizeLocalClusterHolder = new AutoResetEvent(true);
}

[Trace]
public async Task SynchronizeLocalClusterAsync()
{
_synchronizeLocalClusterHolder.WaitOne();
if (!_synchronizingLocalClustersEvent.IsSet)
{
return;
}
_synchronizingLocalClustersEvent.Wait(_lifetime.ApplicationStopping);

using var scope = _logger.BeginScope(new { SyncId = Guid.NewGuid() });
try
{
Expand Down Expand Up @@ -328,7 +329,7 @@ await Task.WhenAll(
}
finally
{
_synchronizeLocalClusterHolder.Set();
_synchronizingLocalClustersEvent.Set();
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,12 @@ public class DefaultIngressManager : IIngressManager
private const string _serviceNameLabel = "kubernetes.io/service-name";
private readonly ILogger<DefaultIngressManager> _logger;
private readonly IKubernetesClient _kubernetesClient;
private readonly INamespaceManager _namespaceManager;
private readonly IServiceManager _serviceManager;

public DefaultIngressManager(ILogger<DefaultIngressManager> logger, IKubernetesClient kubernetesClient, INamespaceManager namespaceManager, IServiceManager serviceManager)
public DefaultIngressManager(ILogger<DefaultIngressManager> logger, IKubernetesClient kubernetesClient, IServiceManager serviceManager)
{
_logger = logger;
_kubernetesClient = kubernetesClient;
_namespaceManager = namespaceManager;
_serviceManager = serviceManager;
}

Expand Down
Loading
Loading