diff --git a/README.md b/README.md
index e65edb1e..b1a5b676 100644
--- a/README.md
+++ b/README.md
@@ -205,7 +205,7 @@ spec:
- name: BASE_FUNCTION_URL
value: "http://{function_name}.{namespace}.svc.cluster.local:8080"
- name: BASE_FUNCTION_POD_URL # require for publish route
- value: "http://{pod_name}.{function_name}.{namespace}.svc.cluster.local:8080"
+ value: "http://{pod_ip}.svc.cluster.local:8080"
- name: BASE_SLIMDATA_URL
value: "http://{pod_name}.slimfaas.{namespace}.svc.cluster.local:3262/" # Don't expose this port, it can also be like "http://{pod_ip}:3262/" but if you can use DNS it's better
- name: SLIMFAAS_PORTS
@@ -331,6 +331,9 @@ spec:
- **SlimFaas/PathsStartWithVisibility** : ""
- Comma separated list of path prefixed by the default visibility. example: "Private:/mypath/subpath,Public:/mypath2"
- "Public:" or "Private:" are prefix that set the path visibility, if not set, "SlimFaas/DefaultVisibility" is used
+- **SlimFaas/ExcludeDeploymentsFromVisibilityPrivate** : ""
+ - Comma separated list of deployment names or statefulset names
+ - Message from that pods will be considered as public. It is useful if you want to exclude some pods from the private visibility, for example for a backend for frontend.
- **SlimFaas/Schedule** : "" #json configuration
- Allows you to define a schedule for your functions. If you want to wake up your infrastructure at 07:00 or for example scale down after 60 seconds of inactivity after 07:00 and scale down after 10 seconds of inactivity after 21:00
diff --git a/src/SlimFaas/Kubernetes/KubernetesService.cs b/src/SlimFaas/Kubernetes/KubernetesService.cs
index 3ba81a5d..0436683b 100644
--- a/src/SlimFaas/Kubernetes/KubernetesService.cs
+++ b/src/SlimFaas/Kubernetes/KubernetesService.cs
@@ -66,7 +66,9 @@ public record DeploymentInformation(string Deployment, string Namespace, IList
? SubscribeEvents = null,
FunctionVisibility Visibility = FunctionVisibility.Public,
- IList? PathsStartWithVisibility = null);
+ IList? PathsStartWithVisibility = null,
+ IList? ExcludeDeploymentsFromVisibilityPrivate = null
+ );
public record PodInformation(string Name, bool? Started, bool? Ready, string Ip, string DeploymentName);
@@ -81,6 +83,7 @@ public class KubernetesService : IKubernetesService
private const string SubscribeEvents = "SlimFaas/SubscribeEvents";
private const string DefaultVisibility = "SlimFaas/DefaultVisibility";
private const string PathsStartWithVisibility = "SlimFaas/PathsStartWithVisibility";
+ private const string ExcludeDeploymentsFromVisibilityPrivate = "SlimFaas/ExcludeDeploymentsFromVisibilityPrivate";
private const string ReplicasStartAsSoonAsOneFunctionRetrieveARequest =
"SlimFaas/ReplicasStartAsSoonAsOneFunctionRetrieveARequest";
@@ -182,7 +185,7 @@ public async Task ListFunctionsAsync(string kubeNamespa
podList.Where(p => p.Name.StartsWith(deploymentListItem.Metadata.Name)).ToList()))
.FirstOrDefault();
- IEnumerable podInformations = podList as PodInformation[] ?? podList.ToArray();
+ IEnumerable podInformations = podList.ToArray();
AddDeployments(kubeNamespace, deploymentList, podInformations, deploymentInformationList, _logger);
AddStatefulSets(kubeNamespace, statefulSetList, podInformations, deploymentInformationList, _logger);
@@ -217,7 +220,7 @@ private static void AddDeployments(string kubeNamespace, V1DeploymentList deploy
DeploymentInformation deploymentInformation = new(
name,
kubeNamespace,
- podList.Where(p => p.DeploymentName == deploymentListItem.Metadata.Name).ToList(),
+ podList.Where(p => p.DeploymentName.StartsWith(name)).ToList(),
deploymentListItem.Spec.Replicas ?? 0,
annotations.TryGetValue(ReplicasAtStart, out string? annotationReplicasAtStart)
? int.Parse(annotationReplicasAtStart)
@@ -245,7 +248,9 @@ private static void AddDeployments(string kubeNamespace, V1DeploymentList deploy
: FunctionVisibility.Public,
annotations.TryGetValue(PathsStartWithVisibility, out string? valueUrlsStartWithVisibility)
? valueUrlsStartWithVisibility.Split(',').ToList()
- : new List());
+ : new List(),
+ annotations.TryGetValue(ExcludeDeploymentsFromVisibilityPrivate, out string? valueExcludeDeploymentsFromVisibilityPrivate) ? valueExcludeDeploymentsFromVisibilityPrivate.Split(',').ToList() : new List()
+ );
deploymentInformationList.Add(deploymentInformation);
}
catch (Exception e)
@@ -292,7 +297,7 @@ private static void AddStatefulSets(string kubeNamespace, V1StatefulSetList depl
DeploymentInformation deploymentInformation = new(
name,
kubeNamespace,
- podList.Where(p => p.DeploymentName == deploymentListItem.Metadata.Name).ToList(),
+ podList.Where(p => p.DeploymentName.StartsWith(name)).ToList(),
deploymentListItem.Spec.Replicas ?? 0,
annotations.TryGetValue(ReplicasAtStart, out string? annotationReplicasAtStart)
? int.Parse(annotationReplicasAtStart)
@@ -317,7 +322,8 @@ private static void AddStatefulSets(string kubeNamespace, V1StatefulSetList depl
: new List(),
annotations.TryGetValue(DefaultVisibility, out string? visibility)
? Enum.Parse(visibility)
- : FunctionVisibility.Public);
+ : FunctionVisibility.Public,
+ annotations.TryGetValue(ExcludeDeploymentsFromVisibilityPrivate, out string? valueExcludeDeploymentsFromVisibilityPrivate) ? valueExcludeDeploymentsFromVisibilityPrivate.Split(',').ToList() : new List());
deploymentInformationList.Add(deploymentInformation);
}
diff --git a/src/SlimFaas/SendClient.cs b/src/SlimFaas/SendClient.cs
index 7c69011d..d653b65a 100644
--- a/src/SlimFaas/SendClient.cs
+++ b/src/SlimFaas/SendClient.cs
@@ -10,7 +10,7 @@ Task SendHttpRequestSync(HttpContext httpContext, string fu
string functionQuery, string? baseUrl = null);
}
-public class SendClient(HttpClient httpClient) : ISendClient
+public class SendClient(HttpClient httpClient, ILogger logger) : ISendClient
{
private readonly string _baseFunctionUrl =
Environment.GetEnvironmentVariable(EnvironmentVariables.BaseFunctionUrl) ??
@@ -27,6 +27,7 @@ public async Task SendHttpRequestAsync(CustomRequest custom
string customRequestQuery = customRequest.Query;
string targetUrl =
ComputeTargetUrl(functionUrl, customRequestFunctionName, customRequestPath, customRequestQuery, _namespaceSlimFaas);
+ logger.LogDebug("Sending async request to {TargetUrl}", targetUrl);
HttpRequestMessage targetRequestMessage = CreateTargetMessage(customRequest, new Uri(targetUrl));
if (context != null)
{
@@ -41,8 +42,9 @@ public async Task SendHttpRequestAsync(CustomRequest custom
public async Task SendHttpRequestSync(HttpContext context, string functionName,
string functionPath, string functionQuery, string? baseUrl = null)
{
- string targetUri = ComputeTargetUrl(baseUrl ?? _baseFunctionUrl, functionName, functionPath, functionQuery, _namespaceSlimFaas);
- HttpRequestMessage targetRequestMessage = CreateTargetMessage(context, new Uri(targetUri));
+ string targetUrl = ComputeTargetUrl(baseUrl ?? _baseFunctionUrl, functionName, functionPath, functionQuery, _namespaceSlimFaas);
+ logger.LogDebug("Sending sync request to {TargetUrl}", targetUrl);
+ HttpRequestMessage targetRequestMessage = CreateTargetMessage(context, new Uri(targetUrl));
HttpResponseMessage responseMessage = await httpClient.SendAsync(targetRequestMessage,
HttpCompletionOption.ResponseHeadersRead, context.RequestAborted);
return responseMessage;
diff --git a/src/SlimFaas/SlimDataEndpoint.cs b/src/SlimFaas/SlimDataEndpoint.cs
index 73d7271c..16b71ae2 100644
--- a/src/SlimFaas/SlimDataEndpoint.cs
+++ b/src/SlimFaas/SlimDataEndpoint.cs
@@ -14,6 +14,7 @@ public static string Get(PodInformation podInformation, string? baseUrl = null)
baseSlimDataUrl = baseSlimDataUrl.Replace("{pod_name}", podInformation.Name);
baseSlimDataUrl = baseSlimDataUrl.Replace("{pod_ip}", podInformation.Ip);
baseSlimDataUrl = baseSlimDataUrl.Replace("{namespace}", namespaceSlimFaas);
+ baseSlimDataUrl = baseSlimDataUrl.Replace("{function_name}", podInformation.DeploymentName);
}
return baseSlimDataUrl;
diff --git a/src/SlimFaas/SlimProxyMiddleware.cs b/src/SlimFaas/SlimProxyMiddleware.cs
index 6e2189fe..36aff192 100644
--- a/src/SlimFaas/SlimProxyMiddleware.cs
+++ b/src/SlimFaas/SlimProxyMiddleware.cs
@@ -83,29 +83,53 @@ await BuildPublishResponseAsync(context, historyHttpService, sendClient, replica
case FunctionType.Async:
default:
{
- await BuildAsyncResponseAsync(context, replicasService, functionName, functionPath);
+ await BuildAsyncResponseAsync(logger, context, replicasService, functionName, functionPath);
break;
}
}
}
- private static Boolean MessageComeFromNamepaceInternal(HttpContext context, IReplicasService replicasService)
+ private static Boolean MessageComeFromNamespaceInternal(ILogger logger, HttpContext context, IReplicasService replicasService, DeploymentInformation currentFunction)
{
- IList podIps = replicasService.Deployments.Pods.Select(p => p.Ip).ToList();
+ IList podIps = replicasService.Deployments.Functions.Select(p => p.Pods).SelectMany(p => p).Where(p => currentFunction?.ExcludeDeploymentsFromVisibilityPrivate?.Contains(p.DeploymentName) == false).Select(p => p.Ip).ToList();
var forwardedFor = context.Request.Headers["X-Forwarded-For"].FirstOrDefault();
var remoteIp = context.Connection.RemoteIpAddress?.ToString();
+ logger.LogDebug("ForwardedFor: {ForwardedFor}, RemoteIp: {RemoteIp}", forwardedFor, remoteIp);
+ if(logger.IsEnabled(LogLevel.Debug))
+ {
+ foreach (var podIp in podIps)
+ {
+ logger.LogDebug("PodIp: {PodIp}", podIp);
+ }
+ }
if (IsInternalIp(forwardedFor, podIps) || IsInternalIp(remoteIp, podIps))
{
+ logger.LogDebug("Request come from internal namespace ForwardedFor: {ForwardedFor}, RemoteIp: {RemoteIp}", forwardedFor, remoteIp);
return true;
}
+ logger.LogDebug("Request come from external namespace ForwardedFor: {ForwardedFor}, RemoteIp: {RemoteIp}", forwardedFor, remoteIp);
return false;
}
private static bool IsInternalIp(string? ipAddress, IList podIps)
{
- return ipAddress != null && podIps.Contains(ipAddress);
+
+ if (string.IsNullOrEmpty(ipAddress))
+ {
+ return false;
+ }
+
+ foreach (string podIp in podIps)
+ {
+ if (ipAddress.Contains(podIp))
+ {
+ return true;
+ }
+ }
+
+ return false;
}
private static void BuildStatusResponse(IReplicasService replicasService,
@@ -148,7 +172,7 @@ private static void BuildWakeResponse(IReplicasService replicasService, IWakeUpF
}
}
- private static List SearchFunctions(HttpContext context, IReplicasService replicasService, string eventName)
+ private static List SearchFunctions(ILogger logger, HttpContext context, IReplicasService replicasService, string eventName)
{
// example: "Public:my-event-name1,Private:my-event-name2,my-event-name3"
var result = new List();
@@ -164,7 +188,7 @@ private static List SearchFunctions(HttpContext context,
if (splits.Length == 1 && splits[0] == eventName)
{
if (deploymentInformation.Visibility == FunctionVisibility.Public ||
- MessageComeFromNamepaceInternal(context, replicasService))
+ MessageComeFromNamespaceInternal(logger, context, replicasService, deploymentInformation))
{
result.Add(deploymentInformation);
}
@@ -173,7 +197,7 @@ private static List SearchFunctions(HttpContext context,
{
var visibility = splits[0];
var visibilityEnum = Enum.Parse(visibility, true);
- if(visibilityEnum == FunctionVisibility.Private && MessageComeFromNamepaceInternal(context, replicasService))
+ if(visibilityEnum == FunctionVisibility.Private && MessageComeFromNamespaceInternal(logger, context, replicasService, deploymentInformation))
{
result.Add(deploymentInformation);
} else if(visibilityEnum == FunctionVisibility.Public)
@@ -215,7 +239,7 @@ private static FunctionVisibility GetFunctionVisibility(ILogger logger, HttpContext context, IReplicasService replicasService, string functionName,
string functionPath)
{
DeploymentInformation? function = SearchFunction(replicasService, functionName);
@@ -227,7 +251,7 @@ private async Task BuildAsyncResponseAsync(HttpContext context, IReplicasService
var visibility = GetFunctionVisibility(logger, function, functionPath);
- if (visibility == FunctionVisibility.Private && !MessageComeFromNamepaceInternal(context, replicasService))
+ if (visibility == FunctionVisibility.Private && !MessageComeFromNamespaceInternal(logger, context, replicasService, function))
{
context.Response.StatusCode = 404;
return;
@@ -245,10 +269,11 @@ private async Task BuildPublishResponseAsync(HttpContext context, HistoryHttpMem
ISendClient sendClient, IReplicasService replicasService, string eventName, string functionPath)
{
logger.LogDebug("Receiving event: {EventName}", eventName);
- var functions = SearchFunctions(context, replicasService, eventName);
+ var functions = SearchFunctions(logger, context, replicasService, eventName);
var slimFaasSubscribeEvents = _slimFaasSubscribeEvents.Where(s => s.Key == eventName);
if (functions.Count <= 0 && !slimFaasSubscribeEvents.Any())
{
+ logger.LogDebug("Return 404 from event: {EventName}", eventName);
context.Response.StatusCode = 404;
return;
}
@@ -260,6 +285,7 @@ private async Task BuildPublishResponseAsync(HttpContext context, HistoryHttpMem
historyHttpService.SetTickLastCall(function.Deployment, lastSetTicks);
foreach (var pod in function.Pods)
{
+ logger.LogDebug("Pod {PodName} is ready: {PodReady}", pod.Name, pod.Ready);
if (pod.Ready != true)
{
continue;
@@ -304,6 +330,18 @@ private async Task BuildPublishResponseAsync(HttpContext context, HistoryHttpMem
}
}
+ if (logger.IsEnabled(LogLevel.Debug))
+ {
+ foreach (Task task in tasks)
+ {
+ if (task.IsCompleted)
+ {
+ using HttpResponseMessage responseMessage = task.Result;
+ logger.LogDebug("Response from event {EventName} with status code {StatusCode}", eventName, responseMessage.StatusCode);
+ }
+ }
+ }
+
context.Response.StatusCode = 204;
}
@@ -319,7 +357,7 @@ private async Task BuildSyncResponseAsync(HttpContext context, HistoryHttpMemory
var visibility = GetFunctionVisibility(logger, function, functionPath);
- if (visibility == FunctionVisibility.Private && !MessageComeFromNamepaceInternal(context, replicasService))
+ if (visibility == FunctionVisibility.Private && !MessageComeFromNamespaceInternal(logger, context, replicasService, function))
{
context.Response.StatusCode = 404;
return;
diff --git a/src/SlimFaas/appsettings.json b/src/SlimFaas/appsettings.json
index 77222be0..443523a9 100644
--- a/src/SlimFaas/appsettings.json
+++ b/src/SlimFaas/appsettings.json
@@ -4,7 +4,8 @@
"Default": "Warning",
"Microsoft.AspNetCore": "Error",
"DotNext.Net.Cluster": "Error",
- "SlimData": "Error"
+ "SlimData": "Error",
+ "SlimFaas": "Error"
}
},
"UseKubeConfig": false,
diff --git a/tests/SlimFaas.Tests/SendClientShould.cs b/tests/SlimFaas.Tests/SendClientShould.cs
index f39b755c..175289c1 100644
--- a/tests/SlimFaas.Tests/SendClientShould.cs
+++ b/tests/SlimFaas.Tests/SendClientShould.cs
@@ -1,5 +1,7 @@
using System.Net;
using Microsoft.AspNetCore.Http;
+using Microsoft.Extensions.Logging;
+using Moq;
namespace SlimFaas.Tests;
@@ -28,7 +30,9 @@ public async Task CallFunctionAsync(string httpMethod)
return await Task.FromResult(responseMessage);
}));
- SendClient sendClient = new SendClient(httpClient);
+ var mockLogger = new Mock>();
+
+ SendClient sendClient = new(httpClient, mockLogger.Object);
CustomRequest customRequest =
new CustomRequest(new List { new() { Key = "key", Values = new[] { "value1" } } },
new byte[1], "fibonacci", "health", httpMethod, "");
@@ -62,8 +66,8 @@ public async Task CallFunctionSync(string httpMethod)
sendedRequest = request;
return await Task.FromResult(responseMessage);
}));
-
- SendClient sendClient = new SendClient(httpClient);
+ var mockLogger = new Mock>();
+ SendClient sendClient = new SendClient(httpClient, mockLogger.Object);
DefaultHttpContext httpContext = new DefaultHttpContext();
HttpRequest httpContextRequest = httpContext.Request;
diff --git a/tests/SlimFaas.Tests/SlimProxyMiddlewareTests.cs b/tests/SlimFaas.Tests/SlimProxyMiddlewareTests.cs
index c1f3d733..9dde809b 100644
--- a/tests/SlimFaas.Tests/SlimProxyMiddlewareTests.cs
+++ b/tests/SlimFaas.Tests/SlimProxyMiddlewareTests.cs
@@ -109,8 +109,8 @@ public class ProxyMiddlewareTests
[Theory]
[InlineData("/publish-event/toto/hello", HttpStatusCode.NoContent, "http://localhost:5002/hello" )]
- [InlineData("/publish-event/reload/hello", HttpStatusCode.NoContent, "http://fibonacci-2.{function_name}:8080//hello,http://fibonacci-1.{function_name}:8080//hello,http://localhost:5002/hello" )]
- [InlineData("/publish-event/reloadnoprefix/hello", HttpStatusCode.NoContent, "http://fibonacci-2.{function_name}:8080//hello,http://fibonacci-1.{function_name}:8080//hello")]
+ [InlineData("/publish-event/reload/hello", HttpStatusCode.NoContent, "http://fibonacci-2.fibonacci:8080//hello,http://fibonacci-1.fibonacci:8080//hello,http://localhost:5002/hello" )]
+ [InlineData("/publish-event/reloadnoprefix/hello", HttpStatusCode.NoContent, "http://fibonacci-2.fibonacci:8080//hello,http://fibonacci-1.fibonacci:8080//hello")]
[InlineData("/publish-event/wrong/download", HttpStatusCode.NotFound, null)]
[InlineData("/publish-event/reloadprivate/hello", HttpStatusCode.NotFound, null)]
public async Task CallPublishInSyncModeAndReturnOk(string path, HttpStatusCode expected, string? times)