From adf015759fb04a1ab524e98b5ec9471f45325114 Mon Sep 17 00:00:00 2001 From: David Chase Date: Wed, 20 Aug 2025 13:04:43 -0400 Subject: [PATCH 1/6] feat: add .NET 8 runtime and builder --- dotnet8/Dockerfile | 32 ++ .../Fission.DotNet.Common.csproj | 26 + .../Fission.DotNet.Common.sln | 24 + .../Fission.DotNet.Common/FissionContext.cs | 76 +++ .../FissionHttpContext.cs | 44 ++ .../Fission.DotNet.Common/FissionMqContext.cs | 17 + dotnet8/Fission.DotNet.Common/ICorsPolicy.cs | 16 + dotnet8/Fission.DotNet.Common/ILogger.cs | 14 + dotnet8/Fission.DotNet.Common/README.md | 46 ++ .../net8.0/Fission.DotNet.Common.deps.json | 23 + .../Release/net8.0/Fission.DotNet.Common.dll | Bin 0 -> 10752 bytes .../Release/net8.0/Fission.DotNet.Common.pdb | Bin 0 -> 12348 bytes ...ion.DotNet.Common.csproj.nuget.dgspec.json | 66 +++ ...Fission.DotNet.Common.csproj.nuget.g.props | 15 + ...ssion.DotNet.Common.csproj.nuget.g.targets | 2 + ...CoreApp,Version=v8.0.AssemblyAttributes.cs | 4 + .../Fission.DotNet.Common.AssemblyInfo.cs | 24 + ...ion.DotNet.Common.AssemblyInfoInputs.cache | 1 + ....GeneratedMSBuildEditorConfig.editorconfig | 13 + .../Fission.DotNet.Common.GlobalUsings.g.cs | 8 + .../net8.0/Fission.DotNet.Common.assets.cache | Bin 0 -> 154 bytes ...tNet.Common.csproj.CoreCompileInputs.cache | 1 + ....DotNet.Common.csproj.FileListAbsolute.txt | 26 + .../Release/net8.0/Fission.DotNet.Common.dll | Bin 0 -> 10752 bytes .../Release/net8.0/Fission.DotNet.Common.pdb | Bin 0 -> 12348 bytes .../Fission.DotNet.Common.sourcelink.json | 1 + .../net8.0/ref/Fission.DotNet.Common.dll | Bin 0 -> 8704 bytes .../net8.0/refint/Fission.DotNet.Common.dll | Bin 0 -> 8704 bytes .../obj/project.assets.json | 71 +++ .../obj/project.nuget.cache | 8 + .../Adapter/FissionLoggerAdapter.cs | 44 ++ .../Controllers/BuildController.cs | 2 + .../Controllers/FunctionController.cs | 226 ++++++++ .../Controllers/HealthController.cs | 15 + .../Controllers/SpecializeController.cs | 57 ++ dotnet8/Fission.DotNet/Fission.DotNet.csproj | 18 + .../Interfaces/IFunctionService.cs | 12 + .../Interfaces/IFunctionStoreService.cs | 10 + .../Interfaces/ISpecializeService.cs | 9 + .../Model/FissionSpecializeRequest.cs | 10 + dotnet8/Fission.DotNet/Model/FunctionStore.cs | 27 + dotnet8/Fission.DotNet/Program.cs | 43 ++ .../Properties/launchSettings.json | 41 ++ .../Services/BuilderModeService.cs | 12 + dotnet8/Fission.DotNet/Services/CorsPolicy.cs | 115 ++++ .../Services/CustomAssemblyLoadContext.cs | 36 ++ .../Services/FunctionService.cs | 444 +++++++++++++++ .../Services/FunctionStoreService.cs | 19 + .../Services/SpecializeService.cs | 416 ++++++++++++++ .../appsettings.Development.json | 8 + dotnet8/Fission.DotNet/appsettings.json | 9 + dotnet8/Makefile | 15 + dotnet8/README.md | 521 ++++++++++++++++++ dotnet8/builder/Dockerfile | 31 ++ dotnet8/builder/Makefile | 8 + dotnet8/builder/defaultBuildCmd | 38 ++ dotnet8/envconfig.json | 22 + .../AsyncFunctionExample.csproj | 14 + .../AsyncFunctionExample/MyFunction.cs | 17 + dotnet8/examples/HelloWorld/HelloWorld.csproj | 11 + dotnet8/examples/HelloWorld/MyFunction.cs | 9 + .../HttpTriggerExample.csproj | 11 + .../examples/HttpTriggerExample/MyFunction.cs | 14 + .../Controllers/ApiController.cs | 129 +++++ .../MultiFileExample/Models/DataModels.cs | 43 ++ .../examples/MultiFileExample/Models/User.cs | 23 + .../MultiFileExample/Models/Weather.cs | 34 ++ .../MultiFileExample/MultiFileExample.csproj | 19 + .../examples/MultiFileExample/MyFunction.cs | 23 + dotnet8/examples/MultiFileExample/README.md | 155 ++++++ .../Services/DataProcessor.cs | 81 +++ .../MultiFileExample/Services/UserService.cs | 65 +++ .../Services/WeatherService.cs | 64 +++ .../MultiFileExample/multifile-test.zip | Bin 0 -> 6710 bytes 74 files changed, 3478 insertions(+) create mode 100644 dotnet8/Dockerfile create mode 100644 dotnet8/Fission.DotNet.Common/Fission.DotNet.Common.csproj create mode 100644 dotnet8/Fission.DotNet.Common/Fission.DotNet.Common.sln create mode 100644 dotnet8/Fission.DotNet.Common/FissionContext.cs create mode 100644 dotnet8/Fission.DotNet.Common/FissionHttpContext.cs create mode 100644 dotnet8/Fission.DotNet.Common/FissionMqContext.cs create mode 100644 dotnet8/Fission.DotNet.Common/ICorsPolicy.cs create mode 100644 dotnet8/Fission.DotNet.Common/ILogger.cs create mode 100644 dotnet8/Fission.DotNet.Common/README.md create mode 100644 dotnet8/Fission.DotNet.Common/bin/Release/net8.0/Fission.DotNet.Common.deps.json create mode 100644 dotnet8/Fission.DotNet.Common/bin/Release/net8.0/Fission.DotNet.Common.dll create mode 100644 dotnet8/Fission.DotNet.Common/bin/Release/net8.0/Fission.DotNet.Common.pdb create mode 100644 dotnet8/Fission.DotNet.Common/obj/Fission.DotNet.Common.csproj.nuget.dgspec.json create mode 100644 dotnet8/Fission.DotNet.Common/obj/Fission.DotNet.Common.csproj.nuget.g.props create mode 100644 dotnet8/Fission.DotNet.Common/obj/Fission.DotNet.Common.csproj.nuget.g.targets create mode 100644 dotnet8/Fission.DotNet.Common/obj/Release/net8.0/.NETCoreApp,Version=v8.0.AssemblyAttributes.cs create mode 100644 dotnet8/Fission.DotNet.Common/obj/Release/net8.0/Fission.DotNet.Common.AssemblyInfo.cs create mode 100644 dotnet8/Fission.DotNet.Common/obj/Release/net8.0/Fission.DotNet.Common.AssemblyInfoInputs.cache create mode 100644 dotnet8/Fission.DotNet.Common/obj/Release/net8.0/Fission.DotNet.Common.GeneratedMSBuildEditorConfig.editorconfig create mode 100644 dotnet8/Fission.DotNet.Common/obj/Release/net8.0/Fission.DotNet.Common.GlobalUsings.g.cs create mode 100644 dotnet8/Fission.DotNet.Common/obj/Release/net8.0/Fission.DotNet.Common.assets.cache create mode 100644 dotnet8/Fission.DotNet.Common/obj/Release/net8.0/Fission.DotNet.Common.csproj.CoreCompileInputs.cache create mode 100644 dotnet8/Fission.DotNet.Common/obj/Release/net8.0/Fission.DotNet.Common.csproj.FileListAbsolute.txt create mode 100644 dotnet8/Fission.DotNet.Common/obj/Release/net8.0/Fission.DotNet.Common.dll create mode 100644 dotnet8/Fission.DotNet.Common/obj/Release/net8.0/Fission.DotNet.Common.pdb create mode 100644 dotnet8/Fission.DotNet.Common/obj/Release/net8.0/Fission.DotNet.Common.sourcelink.json create mode 100644 dotnet8/Fission.DotNet.Common/obj/Release/net8.0/ref/Fission.DotNet.Common.dll create mode 100644 dotnet8/Fission.DotNet.Common/obj/Release/net8.0/refint/Fission.DotNet.Common.dll create mode 100644 dotnet8/Fission.DotNet.Common/obj/project.assets.json create mode 100644 dotnet8/Fission.DotNet.Common/obj/project.nuget.cache create mode 100644 dotnet8/Fission.DotNet/Adapter/FissionLoggerAdapter.cs create mode 100644 dotnet8/Fission.DotNet/Controllers/BuildController.cs create mode 100644 dotnet8/Fission.DotNet/Controllers/FunctionController.cs create mode 100644 dotnet8/Fission.DotNet/Controllers/HealthController.cs create mode 100644 dotnet8/Fission.DotNet/Controllers/SpecializeController.cs create mode 100644 dotnet8/Fission.DotNet/Fission.DotNet.csproj create mode 100644 dotnet8/Fission.DotNet/Interfaces/IFunctionService.cs create mode 100644 dotnet8/Fission.DotNet/Interfaces/IFunctionStoreService.cs create mode 100644 dotnet8/Fission.DotNet/Interfaces/ISpecializeService.cs create mode 100644 dotnet8/Fission.DotNet/Model/FissionSpecializeRequest.cs create mode 100644 dotnet8/Fission.DotNet/Model/FunctionStore.cs create mode 100644 dotnet8/Fission.DotNet/Program.cs create mode 100644 dotnet8/Fission.DotNet/Properties/launchSettings.json create mode 100644 dotnet8/Fission.DotNet/Services/BuilderModeService.cs create mode 100644 dotnet8/Fission.DotNet/Services/CorsPolicy.cs create mode 100644 dotnet8/Fission.DotNet/Services/CustomAssemblyLoadContext.cs create mode 100644 dotnet8/Fission.DotNet/Services/FunctionService.cs create mode 100644 dotnet8/Fission.DotNet/Services/FunctionStoreService.cs create mode 100644 dotnet8/Fission.DotNet/Services/SpecializeService.cs create mode 100644 dotnet8/Fission.DotNet/appsettings.Development.json create mode 100644 dotnet8/Fission.DotNet/appsettings.json create mode 100644 dotnet8/Makefile create mode 100644 dotnet8/README.md create mode 100644 dotnet8/builder/Dockerfile create mode 100644 dotnet8/builder/Makefile create mode 100755 dotnet8/builder/defaultBuildCmd create mode 100644 dotnet8/envconfig.json create mode 100644 dotnet8/examples/AsyncFunctionExample/AsyncFunctionExample.csproj create mode 100644 dotnet8/examples/AsyncFunctionExample/MyFunction.cs create mode 100644 dotnet8/examples/HelloWorld/HelloWorld.csproj create mode 100644 dotnet8/examples/HelloWorld/MyFunction.cs create mode 100644 dotnet8/examples/HttpTriggerExample/HttpTriggerExample.csproj create mode 100644 dotnet8/examples/HttpTriggerExample/MyFunction.cs create mode 100644 dotnet8/examples/MultiFileExample/Controllers/ApiController.cs create mode 100644 dotnet8/examples/MultiFileExample/Models/DataModels.cs create mode 100644 dotnet8/examples/MultiFileExample/Models/User.cs create mode 100644 dotnet8/examples/MultiFileExample/Models/Weather.cs create mode 100644 dotnet8/examples/MultiFileExample/MultiFileExample.csproj create mode 100644 dotnet8/examples/MultiFileExample/MyFunction.cs create mode 100644 dotnet8/examples/MultiFileExample/README.md create mode 100644 dotnet8/examples/MultiFileExample/Services/DataProcessor.cs create mode 100644 dotnet8/examples/MultiFileExample/Services/UserService.cs create mode 100644 dotnet8/examples/MultiFileExample/Services/WeatherService.cs create mode 100644 dotnet8/examples/MultiFileExample/multifile-test.zip diff --git a/dotnet8/Dockerfile b/dotnet8/Dockerfile new file mode 100644 index 00000000..5f52f1c5 --- /dev/null +++ b/dotnet8/Dockerfile @@ -0,0 +1,32 @@ +# Use the .NET 8 SDK image for runtime (needed for compilation support) +FROM mcr.microsoft.com/dotnet/sdk:8.0 + +# Install required tools +RUN apt-get update && apt-get install -y unzip curl && rm -rf /var/lib/apt/lists/* + +# Set working directory +WORKDIR /app + +# Copy the Fission .NET runtime application +COPY Fission.DotNet/ /app/Fission.DotNet/ +COPY Fission.DotNet.Common/ /app/Fission.DotNet.Common/ + +# Build the runtime application +RUN dotnet restore Fission.DotNet/Fission.DotNet.csproj +RUN dotnet publish Fission.DotNet/Fission.DotNet.csproj -c Release -o /app/publish + +# Build and copy Fission.DotNet.Common.dll to /app for runtime compilation +RUN dotnet build Fission.DotNet.Common/Fission.DotNet.Common.csproj -c Release -o /app/ + +# Create function directory +RUN mkdir -p /function + +# Set environment variables +ENV ASPNETCORE_URLS=http://*:8888 +ENV ASPNETCORE_ENVIRONMENT=Production + +# Expose port +EXPOSE 8888 + +# Start the runtime +CMD ["dotnet", "/app/publish/Fission.DotNet.dll"] diff --git a/dotnet8/Fission.DotNet.Common/Fission.DotNet.Common.csproj b/dotnet8/Fission.DotNet.Common/Fission.DotNet.Common.csproj new file mode 100644 index 00000000..0ffdcef5 --- /dev/null +++ b/dotnet8/Fission.DotNet.Common/Fission.DotNet.Common.csproj @@ -0,0 +1,26 @@ + + + + net8.0 + enable + enable + + + + Fission.DotNet.Common + 1.1.0 + Lorenzo Caldon + Fission dotnet environment common package + GPL-3.0-or-later + https://github.com/lcsoft77/fission-env-dotnet8 + ./nupkg + + + + README.md + + + + + + diff --git a/dotnet8/Fission.DotNet.Common/Fission.DotNet.Common.sln b/dotnet8/Fission.DotNet.Common/Fission.DotNet.Common.sln new file mode 100644 index 00000000..7ea97c8d --- /dev/null +++ b/dotnet8/Fission.DotNet.Common/Fission.DotNet.Common.sln @@ -0,0 +1,24 @@ +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.5.2.0 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Fission.DotNet.Common", "Fission.DotNet.Common.csproj", "{66CA3524-4798-6315-18DF-47CD0B936395}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {66CA3524-4798-6315-18DF-47CD0B936395}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {66CA3524-4798-6315-18DF-47CD0B936395}.Debug|Any CPU.Build.0 = Debug|Any CPU + {66CA3524-4798-6315-18DF-47CD0B936395}.Release|Any CPU.ActiveCfg = Release|Any CPU + {66CA3524-4798-6315-18DF-47CD0B936395}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {AF6A70F1-84DB-4983-A581-08BC54DF98A5} + EndGlobalSection +EndGlobal diff --git a/dotnet8/Fission.DotNet.Common/FissionContext.cs b/dotnet8/Fission.DotNet.Common/FissionContext.cs new file mode 100644 index 00000000..be09f9fd --- /dev/null +++ b/dotnet8/Fission.DotNet.Common/FissionContext.cs @@ -0,0 +1,76 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using System.Text.Json; +using System.Threading.Tasks; + +namespace Fission.DotNet.Common; + +public class FissionContext +{ + protected Stream _content; + protected Dictionary _arguments; + protected Dictionary _headers; + protected Dictionary _parameters; + + public FissionContext(Stream body, Dictionary arguments, Dictionary headers, Dictionary parameters) + { + if (body == null) throw new ArgumentNullException(nameof(body)); + if (arguments == null) throw new ArgumentNullException(nameof(arguments)); + if (headers == null) throw new ArgumentNullException(nameof(headers)); + if (parameters == null) throw new ArgumentNullException(nameof(parameters)); + + _content = body; + _arguments = arguments; + _headers = headers; + _parameters = parameters; + } + + protected string GetHeaderValue(string key, string defaultValue = null) + { + return _headers.ContainsKey(key) ? _headers[key] : defaultValue; + } + + public Dictionary Arguments => _arguments; + public Dictionary Parameters => _parameters; + + public string TraceID => GetHeaderValue("traceparent", Guid.NewGuid().ToString()); + public string FunctionName => GetHeaderValue("X-Fission-Function-Name"); + public string Namespace => GetHeaderValue("X-Fission-Function-Namespace"); + public string ResourceVersion => GetHeaderValue("X-Fission-Function-Resourceversion"); + public string UID => GetHeaderValue("X-Fission-Function-Uid"); + public string Trigger => GetHeaderValue("Source-Name"); + public string ContentType => GetHeaderValue("Content-Type"); + public Int32 ContentLength => GetHeaderValue("Content-Length") != null ? Int32.Parse(GetHeaderValue("Content-Length")) : 0; + public Stream Content => _content; + + public async Task ContentAsString() + { + if (_content == null) + { + return null; + } + + _content.Position = 0; + using (StreamReader reader = new StreamReader(_content, Encoding.UTF8, leaveOpen: true)) + { + return await reader.ReadToEndAsync(); + } + } + + public async Task ContentAs(JsonSerializerOptions? options = null) + { + if (_content == null) + { + return default; + } + + _content.Position = 0; + using (StreamReader reader = new StreamReader(_content, Encoding.UTF8, leaveOpen: true)) + { + string content = await reader.ReadToEndAsync(); + return JsonSerializer.Deserialize(content, options); + } + } +} diff --git a/dotnet8/Fission.DotNet.Common/FissionHttpContext.cs b/dotnet8/Fission.DotNet.Common/FissionHttpContext.cs new file mode 100644 index 00000000..b9ef85ec --- /dev/null +++ b/dotnet8/Fission.DotNet.Common/FissionHttpContext.cs @@ -0,0 +1,44 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using System.Text.Json; + +namespace Fission.DotNet.Common; + +public class FissionHttpContext : FissionContext +{ + private string _method; + + public FissionHttpContext(Stream body, string method, Dictionary arguments, Dictionary headers, Dictionary parameters) : base(body, arguments, headers, parameters) + { + _method = method; + } + + public Dictionary Headers => _headers; + public string Url + { + get + { + var urlHeader = GetHeaderValue("X-Fission-Full-Url"); + + if (urlHeader != null) + { + if (urlHeader.Contains("?")) + { + urlHeader = urlHeader.Substring(0, urlHeader.IndexOf("?")); + } + + return urlHeader; + } + else + { + return "/"; + } + } + } + public string Method => _method; + public string Host => GetHeaderValue("X-Forwarded-Host"); + public int Port => _headers.ContainsKey("X-Forwarded-Port") ? Int32.Parse(GetHeaderValue("X-Forwarded-Port")) : 0; + public string UserAgent => GetHeaderValue("User-Agent"); +} diff --git a/dotnet8/Fission.DotNet.Common/FissionMqContext.cs b/dotnet8/Fission.DotNet.Common/FissionMqContext.cs new file mode 100644 index 00000000..5e5a7660 --- /dev/null +++ b/dotnet8/Fission.DotNet.Common/FissionMqContext.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.IO; + +namespace Fission.DotNet.Common; + +public class FissionMqContext : FissionContext +{ + public FissionMqContext(Stream body, Dictionary arguments, Dictionary headers, Dictionary parameters) : base(body, arguments, headers, parameters) + { + + } + + public string Topic => GetHeaderValue("Topic"); + public string ErrorTopic => GetHeaderValue("Errortopic"); + public string ResponseTopic => GetHeaderValue("Resptopic"); +} diff --git a/dotnet8/Fission.DotNet.Common/ICorsPolicy.cs b/dotnet8/Fission.DotNet.Common/ICorsPolicy.cs new file mode 100644 index 00000000..7f10a326 --- /dev/null +++ b/dotnet8/Fission.DotNet.Common/ICorsPolicy.cs @@ -0,0 +1,16 @@ +using System; + +namespace Fission.DotNet.Common +{ + public interface ICorsPolicy + { + void AllowAnyOrigin(); + void AllowAnyHeader(); + void AllowAnyMethod(); + void AllowCredentials(); + + void WithOrigin(string[] origin); + void WithHeader(string[] header); + void WithMethod(string[] method); + } +} diff --git a/dotnet8/Fission.DotNet.Common/ILogger.cs b/dotnet8/Fission.DotNet.Common/ILogger.cs new file mode 100644 index 00000000..0b58d4f8 --- /dev/null +++ b/dotnet8/Fission.DotNet.Common/ILogger.cs @@ -0,0 +1,14 @@ +using System; + +namespace Fission.DotNet.Common +{ + public interface ILogger + { + void LogInformation(string message); + void LogDebug(string message); + void LogWarning(string message); + void LogError(string message); + void LogCritical(string message); + void LogError(string message, Exception exception); + } +} \ No newline at end of file diff --git a/dotnet8/Fission.DotNet.Common/README.md b/dotnet8/Fission.DotNet.Common/README.md new file mode 100644 index 00000000..973384fd --- /dev/null +++ b/dotnet8/Fission.DotNet.Common/README.md @@ -0,0 +1,46 @@ +# Fission.DotNet.Common + +This project is a common library for the .NET environment of [Fission](https://fission.io/), a serverless plugin for Kubernetes. The library provides common functionalities and utilities to facilitate the development of serverless functions with Fission on .NET. + +## Main Features + +- **Common Utilities**: Provides a set of common utilities to simplify the development of serverless functions. +- **.NET 8 Compatibility**: Designed to work with **.NET 8 on Linux**, offering an updated and improved experience. +- **Multi-Assembly Management**: Supports projects composed of multiple linked assemblies, simplifying the deployment and integration process. + +## Inspiration + +This project is inspired by the official Fission environment for .NET Core 2.0 but focuses on improvements and updates requested by the community, allowing developers to work with newer versions of the .NET framework and complex projects that include multiple assemblies. + +## Usage + +1. **Add the library to your project**: + Add the NuGet package `Fission.DotNet.Common` to your .NET project. + + ```bash + dotnet add package Fission.DotNet.Common + +2. **Create the project**: +- Create a **class library project** in .NET. +- Add the NuGet package `Fission.DotNet.Common` to your project. +- Create a class with the following function: + + ```csharp + using Fission.DotNet.Common; + + public class MyFunction + { + public object Execute(FissionContext input) + { + return "Hello World"; + } + } + ``` +3. **Compression**: Compress the assemblies and related files into a ZIP file. + +4. **Deploy to Fission**: Use this library to deploy your project to Fission, leveraging the ability to manage multiple linked assemblies. After compressing your project into a ZIP file, you can create the function in Fission with the following command: + + ```bash + fission fn create --name --env dotnet8 --code --entrypoint :: + ``` + Replace `` with the name of your function and `` with the path to your ZIP file. \ No newline at end of file diff --git a/dotnet8/Fission.DotNet.Common/bin/Release/net8.0/Fission.DotNet.Common.deps.json b/dotnet8/Fission.DotNet.Common/bin/Release/net8.0/Fission.DotNet.Common.deps.json new file mode 100644 index 00000000..43b6c3ad --- /dev/null +++ b/dotnet8/Fission.DotNet.Common/bin/Release/net8.0/Fission.DotNet.Common.deps.json @@ -0,0 +1,23 @@ +{ + "runtimeTarget": { + "name": ".NETCoreApp,Version=v8.0", + "signature": "" + }, + "compilationOptions": {}, + "targets": { + ".NETCoreApp,Version=v8.0": { + "Fission.DotNet.Common/1.1.0": { + "runtime": { + "Fission.DotNet.Common.dll": {} + } + } + } + }, + "libraries": { + "Fission.DotNet.Common/1.1.0": { + "type": "project", + "serviceable": false, + "sha512": "" + } + } +} \ No newline at end of file diff --git a/dotnet8/Fission.DotNet.Common/bin/Release/net8.0/Fission.DotNet.Common.dll b/dotnet8/Fission.DotNet.Common/bin/Release/net8.0/Fission.DotNet.Common.dll new file mode 100644 index 0000000000000000000000000000000000000000..3f79155da8f7b704ec2217a27026a50f6e715712 GIT binary patch literal 10752 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 literal 0 HcmV?d00001 diff --git a/dotnet8/Fission.DotNet.Common/bin/Release/net8.0/Fission.DotNet.Common.pdb b/dotnet8/Fission.DotNet.Common/bin/Release/net8.0/Fission.DotNet.Common.pdb new file mode 100644 index 0000000000000000000000000000000000000000..ced88a76d622010530ad94cbe2e2523eb5088e30 GIT binary patch literal 12348 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 + + + True + NuGet + $(MSBuildThisFileDirectory)project.assets.json + /Users/davidcasa/.nuget/packages/ + /Users/davidcasa/.nuget/packages/ + PackageReference + 6.11.1 + + + + + \ No newline at end of file diff --git a/dotnet8/Fission.DotNet.Common/obj/Fission.DotNet.Common.csproj.nuget.g.targets b/dotnet8/Fission.DotNet.Common/obj/Fission.DotNet.Common.csproj.nuget.g.targets new file mode 100644 index 00000000..3dc06ef3 --- /dev/null +++ b/dotnet8/Fission.DotNet.Common/obj/Fission.DotNet.Common.csproj.nuget.g.targets @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/dotnet8/Fission.DotNet.Common/obj/Release/net8.0/.NETCoreApp,Version=v8.0.AssemblyAttributes.cs b/dotnet8/Fission.DotNet.Common/obj/Release/net8.0/.NETCoreApp,Version=v8.0.AssemblyAttributes.cs new file mode 100644 index 00000000..dca70aa4 --- /dev/null +++ b/dotnet8/Fission.DotNet.Common/obj/Release/net8.0/.NETCoreApp,Version=v8.0.AssemblyAttributes.cs @@ -0,0 +1,4 @@ +// +using System; +using System.Reflection; +[assembly: global::System.Runtime.Versioning.TargetFrameworkAttribute(".NETCoreApp,Version=v8.0", FrameworkDisplayName = ".NET 8.0")] diff --git a/dotnet8/Fission.DotNet.Common/obj/Release/net8.0/Fission.DotNet.Common.AssemblyInfo.cs b/dotnet8/Fission.DotNet.Common/obj/Release/net8.0/Fission.DotNet.Common.AssemblyInfo.cs new file mode 100644 index 00000000..4a736849 --- /dev/null +++ b/dotnet8/Fission.DotNet.Common/obj/Release/net8.0/Fission.DotNet.Common.AssemblyInfo.cs @@ -0,0 +1,24 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +using System; +using System.Reflection; + +[assembly: System.Reflection.AssemblyCompanyAttribute("Lorenzo Caldon")] +[assembly: System.Reflection.AssemblyConfigurationAttribute("Release")] +[assembly: System.Reflection.AssemblyDescriptionAttribute("Fission dotnet environment common package")] +[assembly: System.Reflection.AssemblyFileVersionAttribute("1.1.0.0")] +[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.1.0+da8ab386daab96def321895f43b0f975caf774ca")] +[assembly: System.Reflection.AssemblyProductAttribute("Fission.DotNet.Common")] +[assembly: System.Reflection.AssemblyTitleAttribute("Fission.DotNet.Common")] +[assembly: System.Reflection.AssemblyVersionAttribute("1.1.0.0")] +[assembly: System.Reflection.AssemblyMetadataAttribute("RepositoryUrl", "https://github.com/lcsoft77/fission-env-dotnet8")] + +// Generated by the MSBuild WriteCodeFragment class. + diff --git a/dotnet8/Fission.DotNet.Common/obj/Release/net8.0/Fission.DotNet.Common.AssemblyInfoInputs.cache b/dotnet8/Fission.DotNet.Common/obj/Release/net8.0/Fission.DotNet.Common.AssemblyInfoInputs.cache new file mode 100644 index 00000000..30df6806 --- /dev/null +++ b/dotnet8/Fission.DotNet.Common/obj/Release/net8.0/Fission.DotNet.Common.AssemblyInfoInputs.cache @@ -0,0 +1 @@ +63b7bdec7d16ea8774fceb5335d6bcd9b1cb54c905159b91a92d392e27eb7b7b diff --git a/dotnet8/Fission.DotNet.Common/obj/Release/net8.0/Fission.DotNet.Common.GeneratedMSBuildEditorConfig.editorconfig b/dotnet8/Fission.DotNet.Common/obj/Release/net8.0/Fission.DotNet.Common.GeneratedMSBuildEditorConfig.editorconfig new file mode 100644 index 00000000..ceffaad8 --- /dev/null +++ b/dotnet8/Fission.DotNet.Common/obj/Release/net8.0/Fission.DotNet.Common.GeneratedMSBuildEditorConfig.editorconfig @@ -0,0 +1,13 @@ +is_global = true +build_property.TargetFramework = net8.0 +build_property.TargetPlatformMinVersion = +build_property.UsingMicrosoftNETSdkWeb = +build_property.ProjectTypeGuids = +build_property.InvariantGlobalization = +build_property.PlatformNeutralAssembly = +build_property.EnforceExtendedAnalyzerRules = +build_property._SupportedPlatformList = Linux,macOS,Windows +build_property.RootNamespace = Fission.DotNet.Common +build_property.ProjectDir = /Users/davidcasa/environments/dotnet8/Fission.DotNet.Common/ +build_property.EnableComHosting = +build_property.EnableGeneratedComInterfaceComImportInterop = diff --git a/dotnet8/Fission.DotNet.Common/obj/Release/net8.0/Fission.DotNet.Common.GlobalUsings.g.cs b/dotnet8/Fission.DotNet.Common/obj/Release/net8.0/Fission.DotNet.Common.GlobalUsings.g.cs new file mode 100644 index 00000000..8578f3d0 --- /dev/null +++ b/dotnet8/Fission.DotNet.Common/obj/Release/net8.0/Fission.DotNet.Common.GlobalUsings.g.cs @@ -0,0 +1,8 @@ +// +global using global::System; +global using global::System.Collections.Generic; +global using global::System.IO; +global using global::System.Linq; +global using global::System.Net.Http; +global using global::System.Threading; +global using global::System.Threading.Tasks; diff --git a/dotnet8/Fission.DotNet.Common/obj/Release/net8.0/Fission.DotNet.Common.assets.cache b/dotnet8/Fission.DotNet.Common/obj/Release/net8.0/Fission.DotNet.Common.assets.cache new file mode 100644 index 0000000000000000000000000000000000000000..8913e0ca546e05900d9eca1a6dc436ecac54bf46 GIT binary patch literal 154 zcmWIWc6a1qU|>kxE2HM4&cCGNP+nJDbxy!n9>ZUvl{>4h@JV(!%w8Wf4XB1#zz9^R os2^IKT2!o`l313RlAKtasIQk-nx0ysUyzubotU0ltWU@^07D!bz5oCK literal 0 HcmV?d00001 diff --git a/dotnet8/Fission.DotNet.Common/obj/Release/net8.0/Fission.DotNet.Common.csproj.CoreCompileInputs.cache b/dotnet8/Fission.DotNet.Common/obj/Release/net8.0/Fission.DotNet.Common.csproj.CoreCompileInputs.cache new file mode 100644 index 00000000..97d6d932 --- /dev/null +++ b/dotnet8/Fission.DotNet.Common/obj/Release/net8.0/Fission.DotNet.Common.csproj.CoreCompileInputs.cache @@ -0,0 +1 @@ +3e116a7e531ffaf5633e32e0bf4deb38175e8205076e04a01b1d750018d2f736 diff --git a/dotnet8/Fission.DotNet.Common/obj/Release/net8.0/Fission.DotNet.Common.csproj.FileListAbsolute.txt b/dotnet8/Fission.DotNet.Common/obj/Release/net8.0/Fission.DotNet.Common.csproj.FileListAbsolute.txt new file mode 100644 index 00000000..1bbec192 --- /dev/null +++ b/dotnet8/Fission.DotNet.Common/obj/Release/net8.0/Fission.DotNet.Common.csproj.FileListAbsolute.txt @@ -0,0 +1,26 @@ +/src/Fission.DotNet.Common/bin/Release/net8.0/Fission.DotNet.Common.deps.json +/src/Fission.DotNet.Common/bin/Release/net8.0/Fission.DotNet.Common.dll +/src/Fission.DotNet.Common/bin/Release/net8.0/Fission.DotNet.Common.pdb +/src/Fission.DotNet.Common/obj/Release/net8.0/Fission.DotNet.Common.GeneratedMSBuildEditorConfig.editorconfig +/src/Fission.DotNet.Common/obj/Release/net8.0/Fission.DotNet.Common.AssemblyInfoInputs.cache +/src/Fission.DotNet.Common/obj/Release/net8.0/Fission.DotNet.Common.AssemblyInfo.cs +/src/Fission.DotNet.Common/obj/Release/net8.0/Fission.DotNet.Common.csproj.CoreCompileInputs.cache +/src/Fission.DotNet.Common/obj/Release/net8.0/Fission.DotNet.Common.dll +/src/Fission.DotNet.Common/obj/Release/net8.0/refint/Fission.DotNet.Common.dll +/src/Fission.DotNet.Common/obj/Release/net8.0/Fission.DotNet.Common.pdb +/src/Fission.DotNet.Common/obj/Release/net8.0/ref/Fission.DotNet.Common.dll +/Users/davidcasa/environments/dotnet8/Fission.DotNet.Common/obj/Release/net8.0/Fission.DotNet.Common.GeneratedMSBuildEditorConfig.editorconfig +/Users/davidcasa/environments/dotnet8/Fission.DotNet.Common/obj/Release/net8.0/Fission.DotNet.Common.AssemblyInfoInputs.cache +/Users/davidcasa/environments/dotnet8/Fission.DotNet.Common/obj/Release/net8.0/Fission.DotNet.Common.AssemblyInfo.cs +/Users/davidcasa/environments/dotnet8/Fission.DotNet.Common/obj/Release/net8.0/Fission.DotNet.Common.csproj.CoreCompileInputs.cache +/Users/davidcasa/environments/dotnet8/Fission.DotNet.Common/obj/Release/net8.0/Fission.DotNet.Common.sourcelink.json +/Users/davidcasa/environments/dotnet8/Fission.DotNet.Common/obj/Release/net8.0/Fission.DotNet.Common.dll +/Users/davidcasa/environments/dotnet8/Fission.DotNet.Common/obj/Release/net8.0/refint/Fission.DotNet.Common.dll +/Users/davidcasa/environments/dotnet8/Fission.DotNet.Common/obj/Release/net8.0/Fission.DotNet.Common.pdb +/Users/davidcasa/environments/dotnet8/Fission.DotNet.Common.deps.json +/Users/davidcasa/environments/dotnet8/Fission.DotNet.Common.dll +/Users/davidcasa/environments/dotnet8/Fission.DotNet.Common/obj/Release/net8.0/ref/Fission.DotNet.Common.dll +/Users/davidcasa/environments/dotnet8/Fission.DotNet.Common.pdb +/Users/davidcasa/environments/dotnet8/Fission.DotNet.Common/bin/Release/net8.0/Fission.DotNet.Common.deps.json +/Users/davidcasa/environments/dotnet8/Fission.DotNet.Common/bin/Release/net8.0/Fission.DotNet.Common.dll +/Users/davidcasa/environments/dotnet8/Fission.DotNet.Common/bin/Release/net8.0/Fission.DotNet.Common.pdb diff --git a/dotnet8/Fission.DotNet.Common/obj/Release/net8.0/Fission.DotNet.Common.dll b/dotnet8/Fission.DotNet.Common/obj/Release/net8.0/Fission.DotNet.Common.dll new file mode 100644 index 0000000000000000000000000000000000000000..3f79155da8f7b704ec2217a27026a50f6e715712 GIT binary patch literal 10752 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 literal 0 HcmV?d00001 diff --git a/dotnet8/Fission.DotNet.Common/obj/Release/net8.0/Fission.DotNet.Common.pdb b/dotnet8/Fission.DotNet.Common/obj/Release/net8.0/Fission.DotNet.Common.pdb new file mode 100644 index 0000000000000000000000000000000000000000..ced88a76d622010530ad94cbe2e2523eb5088e30 GIT binary patch literal 12348 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 kBcld(+Tt0Gog#Z8m literal 0 HcmV?d00001 diff --git a/dotnet8/Fission.DotNet.Common/obj/Release/net8.0/refint/Fission.DotNet.Common.dll b/dotnet8/Fission.DotNet.Common/obj/Release/net8.0/refint/Fission.DotNet.Common.dll new file mode 100644 index 0000000000000000000000000000000000000000..1673fab32c484ccb7c9fafa51cc8a9a2b1d3c791 GIT binary patch literal 8704 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 kBcld(+Tt0Gog#Z8m literal 0 HcmV?d00001 diff --git a/dotnet8/Fission.DotNet.Common/obj/project.assets.json b/dotnet8/Fission.DotNet.Common/obj/project.assets.json new file mode 100644 index 00000000..bc01dccc --- /dev/null +++ b/dotnet8/Fission.DotNet.Common/obj/project.assets.json @@ -0,0 +1,71 @@ +{ + "version": 3, + "targets": { + "net8.0": {} + }, + "libraries": {}, + "projectFileDependencyGroups": { + "net8.0": [] + }, + "packageFolders": { + "/Users/davidcasa/.nuget/packages/": {} + }, + "project": { + "version": "1.1.0", + "restore": { + "projectUniqueName": "/Users/davidcasa/environments/dotnet8/Fission.DotNet.Common/Fission.DotNet.Common.csproj", + "projectName": "Fission.DotNet.Common", + "projectPath": "/Users/davidcasa/environments/dotnet8/Fission.DotNet.Common/Fission.DotNet.Common.csproj", + "packagesPath": "/Users/davidcasa/.nuget/packages/", + "outputPath": "/Users/davidcasa/environments/dotnet8/Fission.DotNet.Common/obj/", + "projectStyle": "PackageReference", + "configFilePaths": [ + "/Users/davidcasa/.nuget/NuGet/NuGet.Config" + ], + "originalTargetFrameworks": [ + "net8.0" + ], + "sources": { + "https://api.nuget.org/v3/index.json": {} + }, + "frameworks": { + "net8.0": { + "targetAlias": "net8.0", + "projectReferences": {} + } + }, + "warningProperties": { + "warnAsError": [ + "NU1605" + ] + }, + "restoreAuditProperties": { + "enableAudit": "true", + "auditLevel": "low", + "auditMode": "direct" + } + }, + "frameworks": { + "net8.0": { + "targetAlias": "net8.0", + "imports": [ + "net461", + "net462", + "net47", + "net471", + "net472", + "net48", + "net481" + ], + "assetTargetFallback": true, + "warn": true, + "frameworkReferences": { + "Microsoft.NETCore.App": { + "privateAssets": "all" + } + }, + "runtimeIdentifierGraphPath": "/usr/local/share/dotnet/sdk/8.0.413/PortableRuntimeIdentifierGraph.json" + } + } + } +} \ No newline at end of file diff --git a/dotnet8/Fission.DotNet.Common/obj/project.nuget.cache b/dotnet8/Fission.DotNet.Common/obj/project.nuget.cache new file mode 100644 index 00000000..77e4a130 --- /dev/null +++ b/dotnet8/Fission.DotNet.Common/obj/project.nuget.cache @@ -0,0 +1,8 @@ +{ + "version": 2, + "dgSpecHash": "j1Y/WP+AzWA=", + "success": true, + "projectFilePath": "/Users/davidcasa/environments/dotnet8/Fission.DotNet.Common/Fission.DotNet.Common.csproj", + "expectedPackageFiles": [], + "logs": [] +} \ No newline at end of file diff --git a/dotnet8/Fission.DotNet/Adapter/FissionLoggerAdapter.cs b/dotnet8/Fission.DotNet/Adapter/FissionLoggerAdapter.cs new file mode 100644 index 00000000..cca1072a --- /dev/null +++ b/dotnet8/Fission.DotNet/Adapter/FissionLoggerAdapter.cs @@ -0,0 +1,44 @@ +using System; +using Fission.DotNet.Common; + +namespace Fission.DotNet.Adapter; + +public class FissionLoggerAdapter : Common.ILogger +{ + private Microsoft.Extensions.Logging.ILogger _logger; + + public FissionLoggerAdapter(Microsoft.Extensions.Logging.ILogger logger) + { + this._logger = logger; + } + + public void LogCritical(string message) + { + _logger.LogCritical(message); + } + + public void LogDebug(string message) + { + _logger.LogDebug(message); + } + + public void LogError(string message, Exception exception) + { + _logger.LogError(exception, message); + } + + public void LogError(string message) + { + _logger.LogError(message); + } + + public void LogInformation(string message) + { + _logger.LogInformation(message); + } + + public void LogWarning(string message) + { + _logger.LogWarning(message); + } +} diff --git a/dotnet8/Fission.DotNet/Controllers/BuildController.cs b/dotnet8/Fission.DotNet/Controllers/BuildController.cs new file mode 100644 index 00000000..25ae35ae --- /dev/null +++ b/dotnet8/Fission.DotNet/Controllers/BuildController.cs @@ -0,0 +1,2 @@ +// This controller is not needed in the runtime environment +// Build functionality is handled by the separate builder container \ No newline at end of file diff --git a/dotnet8/Fission.DotNet/Controllers/FunctionController.cs b/dotnet8/Fission.DotNet/Controllers/FunctionController.cs new file mode 100644 index 00000000..b505fd47 --- /dev/null +++ b/dotnet8/Fission.DotNet/Controllers/FunctionController.cs @@ -0,0 +1,226 @@ +using System.Text; +using Fission.DotNet.Common; +using Microsoft.AspNetCore.Mvc; +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.Loader; +using Fission.DotNet.Interfaces; +using System.Threading.Tasks.Dataflow; +using Fission.DotNet.Services; + +namespace Fission.DotNet.Controllers +{ + [ApiController] + public class FunctionController : Controller + { + private readonly ILogger _logger; + private readonly IFunctionService _functionService; + + public FunctionController(ILogger logger, IFunctionService functionService) + { + this._logger = logger; + this._functionService = functionService; + } + /// + /// Handle HTTP GET requests by forwarding to the Fission function. + /// + /// The function return value. + [HttpGet] + [Route("/")] + [Route("/{*path}")] + public async Task Get() + { + return await this.Run(Request); + } + + /// + /// Handle HTTP POST requests by forwarding to the Fission function. + /// + /// The function return value. + [HttpPost] + [Route("/")] + [Route("/{*path}", Order = int.MaxValue)] + public async Task Post() + { + return await this.Run(Request); + } + + /// + /// Handle HTTP PUT requests by forwarding to the Fission function. + /// + /// The function return value. + [HttpPut] + [Route("/")] + [Route("/{*path}", Order = int.MaxValue)] + public async Task Put() + { + return await this.Run(Request); + } + + /// + /// Handle HTTP HEAD requests by forwarding to the Fission function. + /// + /// The function return value. + [HttpHead] + [Route("/")] + [Route("/{*path}", Order = int.MaxValue)] + public async Task Head() + { + return await this.Run(Request); + } + + /// + /// Handle HTTP OPTIONS requests by forwarding to the Fission function. + /// + /// The function return value. + [HttpOptions] + [Route("/")] + [Route("/{*path}", Order = int.MaxValue)] + public async Task Options() + { + return await this.Run(Request); + } + + /// + /// Handle HTTP DELETE requests by forwarding to the Fission function. + /// + /// The function return value. + [HttpDelete] + [Route("/")] + [Route("/{*path}", Order = int.MaxValue)] + public async Task Delete() + { + return await this.Run(Request); + } + + /// + /// Invokes the Fission function on behalf of the caller. + /// + /// + /// 200 OK with the Fission function return value; or 400 Bad Request with the exception message if an exception + /// occurred in the Fission function; or 500 Internal Server Error if the environment container has not yet been + /// specialized. + /// + private async Task Run(HttpRequest request) + { + _logger.LogInformation("FunctionController.Run"); + + _functionService.Load(); + try + { + if (request.Method == "OPTIONS") + { + var corsPolicy = _functionService.GetCorsPolicy(); + var headers = (corsPolicy as CorsPolicy).GetCorsHeaders(); + foreach (var header in headers) + { + Response.Headers.Add(header.Key, header.Value); + } + return Ok(); + } + + // Copy the body to a MemoryStream so it can be read again + request.EnableBuffering(); + using (var memoryStream = new MemoryStream()) + { + await request.Body.CopyToAsync(memoryStream); + memoryStream.Seek(0, SeekOrigin.Begin); + + request.Body = memoryStream; + + + Fission.DotNet.Common.FissionContext context = null; + try + { + var httpArgs = request.Query.ToDictionary(x => x.Key, x => (object)x.Value); + var headers = request.Headers.ToDictionary(x => x.Key, x => (string)x.Value); + var parameters = new Dictionary(); + + // Add the actual request path so the function can route correctly + headers["X-Fission-Full-Url"] = request.Path.Value ?? "/"; + + // Extract headers that start with "X-Fission-Params-" + foreach (var header in request.Headers) + { + if (header.Key.StartsWith("X-Fission-Params-")) + { + var key = header.Key.Substring("X-Fission-Params-".Length); + var value = header.Value.ToString(); + parameters[key] = value; + } + } + + if (request.Headers.ContainsKey("Topic")) + { + _logger.LogInformation("FunctionController.Run: FissionMqContext"); + context = new Fission.DotNet.Common.FissionMqContext(request.Body, httpArgs, headers, parameters); + } + else + { + _logger.LogInformation("FunctionController.Run: FissionHttpContext"); + context = new Fission.DotNet.Common.FissionHttpContext(request.Body, request.Method, httpArgs, headers, parameters); + } + + if (context is FissionHttpContext) + { + _logger.LogInformation($"Body stream: {context.Content.Position}-{context.Content.Length}"); + _logger.LogInformation($"Request Method: {(context as FissionHttpContext).Method}"); + _logger.LogInformation($"Request Arguments: {string.Join(", ", (context as FissionHttpContext).Arguments.Select(x => $"{x.Key}={x.Value}"))}"); + } + _logger.LogInformation($"Request Method: {request.Method}"); + _logger.LogInformation($"Request Headers: {string.Join(", ", request.Headers.Select(x => $"{x.Key}={x.Value}"))}"); + _logger.LogInformation($"Request Query: {string.Join(", ", request.Query.Select(x => $"{x.Key}={x.Value}"))}"); + + } + catch (Exception ex) + { + _logger.LogError(ex, "FunctionController.Run"); + return BadRequest(ex.Message); + } + + try + { + var result = await _functionService.Execute(context); + if (context is FissionHttpContext) + { + var corsPolicy = _functionService.GetCorsPolicy(); + var headers = (corsPolicy as CorsPolicy).GetRequestCorsHeaders(); + foreach (var header in headers) + { + Response.Headers.Add(header.Key, header.Value); + } + + // Set Content-Type based on result content + if (result is string stringResult && stringResult.TrimStart().StartsWith(" logger; + private readonly ISpecializeService specializeService; + + public SpecializeController(ILogger logger, ISpecializeService specializeService) + { + this.logger = logger; + this.specializeService = specializeService; + } + + // GET: SpecializeController + [HttpPost, Route("/specialize")] + public Task Specialize() + { + logger.LogInformation("Specialize called"); + + return Task.FromResult(Results.Ok()); + } + + + [HttpPost, Route("/v2/specialize")] + public async Task SpecializeV2() + { + logger.LogInformation("SpecializeV2 called"); + + try + { + + + using (var reader = new StreamReader(Request.Body)) + { + var body = await reader.ReadToEndAsync(); + logger.LogInformation($"Body: {body}"); + + var request = JsonSerializer.Deserialize(body); + + specializeService.Specialize(request); + + return Task.FromResult(Results.Ok()); + } + } + catch (Exception ex) + { + logger.LogError(ex, "Error when specializing"); + return Task.FromResult(Results.BadRequest(ex.Message)); + } + } + } +} diff --git a/dotnet8/Fission.DotNet/Fission.DotNet.csproj b/dotnet8/Fission.DotNet/Fission.DotNet.csproj new file mode 100644 index 00000000..8b901b5d --- /dev/null +++ b/dotnet8/Fission.DotNet/Fission.DotNet.csproj @@ -0,0 +1,18 @@ + + + + net8.0 + enable + enable + + + + + + + + + + + + diff --git a/dotnet8/Fission.DotNet/Interfaces/IFunctionService.cs b/dotnet8/Fission.DotNet/Interfaces/IFunctionService.cs new file mode 100644 index 00000000..381cc9ab --- /dev/null +++ b/dotnet8/Fission.DotNet/Interfaces/IFunctionService.cs @@ -0,0 +1,12 @@ +using System; +using Fission.DotNet.Common; + +namespace Fission.DotNet.Interfaces; + +public interface IFunctionService +{ + void Load(); + void Unload(); + Task Execute(FissionContext context); + ICorsPolicy GetCorsPolicy(); +} diff --git a/dotnet8/Fission.DotNet/Interfaces/IFunctionStoreService.cs b/dotnet8/Fission.DotNet/Interfaces/IFunctionStoreService.cs new file mode 100644 index 00000000..e1daad1e --- /dev/null +++ b/dotnet8/Fission.DotNet/Interfaces/IFunctionStoreService.cs @@ -0,0 +1,10 @@ +using System; +using Fission.DotNet.Model; + +namespace Fission.DotNet.Interfaces; + +public interface IFunctionStoreService +{ + void SetFunction(FunctionStore function); + FunctionStore GetFunction(); +} diff --git a/dotnet8/Fission.DotNet/Interfaces/ISpecializeService.cs b/dotnet8/Fission.DotNet/Interfaces/ISpecializeService.cs new file mode 100644 index 00000000..bf42f4d6 --- /dev/null +++ b/dotnet8/Fission.DotNet/Interfaces/ISpecializeService.cs @@ -0,0 +1,9 @@ +using System; +using Fission.DotNet.Model; + +namespace Fission.DotNet.Interfaces; + +public interface ISpecializeService +{ + void Specialize(FissionSpecializeRequest request); +} diff --git a/dotnet8/Fission.DotNet/Model/FissionSpecializeRequest.cs b/dotnet8/Fission.DotNet/Model/FissionSpecializeRequest.cs new file mode 100644 index 00000000..f545aa16 --- /dev/null +++ b/dotnet8/Fission.DotNet/Model/FissionSpecializeRequest.cs @@ -0,0 +1,10 @@ +using System; + +namespace Fission.DotNet.Model; + +public class FissionSpecializeRequest +{ + public string filepath { get; set; } + public string functionName { get; set; } + public string url { get; set; } +} diff --git a/dotnet8/Fission.DotNet/Model/FunctionStore.cs b/dotnet8/Fission.DotNet/Model/FunctionStore.cs new file mode 100644 index 00000000..2a3069a8 --- /dev/null +++ b/dotnet8/Fission.DotNet/Model/FunctionStore.cs @@ -0,0 +1,27 @@ +using System; + +namespace Fission.DotNet.Model; + +public class FunctionStore +{ + public FunctionStore(string function) + { + if (string.IsNullOrEmpty(function)) + { + throw new ArgumentException("Function string cannot be null or empty", nameof(function)); + } + + var parts = function.Split(':'); + if (parts.Length != 3) + { + throw new ArgumentException("Function string must contain exactly three parts separated by dots", nameof(function)); + } + + Assembly = $"{parts[0]}.dll"; + Namespace = parts[1]; + FunctionName = parts[2]; + } + public string Assembly { get; private set; } + public string Namespace { get; private set; } + public string FunctionName { get; private set; } +} diff --git a/dotnet8/Fission.DotNet/Program.cs b/dotnet8/Fission.DotNet/Program.cs new file mode 100644 index 00000000..cb2aa97a --- /dev/null +++ b/dotnet8/Fission.DotNet/Program.cs @@ -0,0 +1,43 @@ +using System.ComponentModel; +using System.Text.Json; +using Fission.DotNet; +using Fission.DotNet.Adapter; +using Fission.DotNet.Interfaces; +using Fission.DotNet.Services; + +var builder = WebApplication.CreateBuilder(args); + +// Use port 8888 for runtime +builder.WebHost.UseUrls("http://*:8888"); + +// Add services to the container. +// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle +builder.Services.AddEndpointsApiExplorer(); +builder.Services.AddSwaggerGen(); + +builder.Services.AddControllers(); + +builder.Services.AddSingleton(); +builder.Services.AddTransient(); +builder.Services.AddSingleton(); + +// Add the builder mode flag as a singleton service so controllers can access it +builder.Services.AddSingleton(new BuilderModeService(false)); + +var app = builder.Build(); + +// Configure the HTTP request pipeline. +if (app.Environment.IsDevelopment()) +{ + app.UseSwagger(); + app.UseSwaggerUI(); +} + +app.UseRouting(); // Add the routing middleware + +app.UseEndpoints(endpoints => +{ + endpoints.MapControllers(); +}); + +app.Run(); diff --git a/dotnet8/Fission.DotNet/Properties/launchSettings.json b/dotnet8/Fission.DotNet/Properties/launchSettings.json new file mode 100644 index 00000000..657c5fa9 --- /dev/null +++ b/dotnet8/Fission.DotNet/Properties/launchSettings.json @@ -0,0 +1,41 @@ +{ + "$schema": "http://json.schemastore.org/launchsettings.json", + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:24776", + "sslPort": 44362 + } + }, + "profiles": { + "http": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "launchUrl": "swagger", + "applicationUrl": "http://localhost:5074", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "https": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "launchUrl": "swagger", + "applicationUrl": "https://localhost:7199;http://localhost:5074", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "launchUrl": "swagger", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/dotnet8/Fission.DotNet/Services/BuilderModeService.cs b/dotnet8/Fission.DotNet/Services/BuilderModeService.cs new file mode 100644 index 00000000..7e0514c8 --- /dev/null +++ b/dotnet8/Fission.DotNet/Services/BuilderModeService.cs @@ -0,0 +1,12 @@ +namespace Fission.DotNet.Services +{ + public class BuilderModeService + { + public bool IsBuilderMode { get; } + + public BuilderModeService(bool isBuilderMode) + { + IsBuilderMode = isBuilderMode; + } + } +} \ No newline at end of file diff --git a/dotnet8/Fission.DotNet/Services/CorsPolicy.cs b/dotnet8/Fission.DotNet/Services/CorsPolicy.cs new file mode 100644 index 00000000..320cdd54 --- /dev/null +++ b/dotnet8/Fission.DotNet/Services/CorsPolicy.cs @@ -0,0 +1,115 @@ +using System; +using System.Collections.Generic; +using Fission.DotNet.Common; + +namespace Fission.DotNet.Services; + +public class CorsPolicy : ICorsPolicy +{ + private HashSet _origins = new HashSet(); + private HashSet _headers = new HashSet(); + private HashSet _methods = new HashSet(); + private bool _allowAnyOrigin = false; + private bool _allowAnyHeader = false; + private bool _allowAnyMethod = false; + private bool _allowCredentials = false; + + public void AllowAnyOrigin() + { + _allowAnyOrigin = true; + } + + public void AllowAnyHeader() + { + _allowAnyHeader = true; + } + + public void AllowAnyMethod() + { + _allowAnyMethod = true; + } + + public void AllowCredentials() + { + _allowCredentials = true; + } + + public void WithOrigin(string[] origins) + { + foreach (var origin in origins) + { + _origins.Add(origin); + } + } + + public void WithHeader(string[] headers) + { + foreach (var header in headers) + { + _headers.Add(header); + } + } + + public void WithMethod(string[] methods) + { + foreach (var method in methods) + { + _methods.Add(method); + } + } + + public Dictionary GetCorsHeaders() + { + var headers = new Dictionary(); + + if (_allowAnyOrigin) + { + headers["Access-Control-Allow-Origin"] = "*"; + } + else if (_origins.Count > 0) + { + headers["Access-Control-Allow-Origin"] = string.Join(", ", _origins); + } + + if (_allowAnyHeader) + { + headers["Access-Control-Allow-Headers"] = "*"; + } + else if (_headers.Count > 0) + { + headers["Access-Control-Allow-Headers"] = string.Join(", ", _headers); + } + + if (_allowAnyMethod) + { + headers["Access-Control-Allow-Methods"] = "*"; + } + else if (_methods.Count > 0) + { + headers["Access-Control-Allow-Methods"] = string.Join(", ", _methods); + } + + if (_allowCredentials) + { + headers["Access-Control-Allow-Credentials"] = "true"; + } + + return headers; + } + + public IDictionary GetRequestCorsHeaders() + { + var headers = new Dictionary(); + + if (_allowAnyOrigin) + { + headers["Access-Control-Allow-Origin"] = "*"; + } + else if (_origins.Count > 0) + { + headers["Access-Control-Allow-Origin"] = string.Join(", ", _origins); + } + + return headers; + } +} \ No newline at end of file diff --git a/dotnet8/Fission.DotNet/Services/CustomAssemblyLoadContext.cs b/dotnet8/Fission.DotNet/Services/CustomAssemblyLoadContext.cs new file mode 100644 index 00000000..dc751b27 --- /dev/null +++ b/dotnet8/Fission.DotNet/Services/CustomAssemblyLoadContext.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.Loader; + +namespace Fission.DotNet.Services +{ + public class CustomAssemblyLoadContext : AssemblyLoadContext + { + private AssemblyDependencyResolver _resolver; + + public CustomAssemblyLoadContext(string pluginPath, bool isCollectible = false) + : base(pluginPath, isCollectible) + { + _resolver = new AssemblyDependencyResolver(pluginPath); + } + + protected override Assembly? Load(AssemblyName assemblyName) + { + var assemblyPath = _resolver.ResolveAssemblyToPath(assemblyName); + if (assemblyPath != null) + { + return LoadFromAssemblyPath(assemblyPath); + } + return null; + } + + protected override IntPtr LoadUnmanagedDll(string unmanagedDllName) + { + var libraryPath = _resolver.ResolveUnmanagedDllToPath(unmanagedDllName); + if (libraryPath != null) + { + return LoadUnmanagedDllFromPath(libraryPath); + } + return IntPtr.Zero; + } + } +} \ No newline at end of file diff --git a/dotnet8/Fission.DotNet/Services/FunctionService.cs b/dotnet8/Fission.DotNet/Services/FunctionService.cs new file mode 100644 index 00000000..63237dc6 --- /dev/null +++ b/dotnet8/Fission.DotNet/Services/FunctionService.cs @@ -0,0 +1,444 @@ +using System; +using Fission.DotNet.Common; +using Fission.DotNet.Interfaces; +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.Loader; +using Fission.DotNet.Adapter; + +namespace Fission.DotNet.Services; + +public class FunctionService : IFunctionService +{ + private readonly IFunctionStoreService _functionStoreService; + private readonly ILogger _logger; + private CustomAssemblyLoadContext _alc; + private WeakReference _alcWeakRef; + private Assembly _assemblyFunction; + private Type _classFunctionNameType; + + public FunctionService(IFunctionStoreService functionStoreService, ILogger logger) + { + this._functionStoreService = functionStoreService; + this._logger = logger; + } + + public ICorsPolicy GetCorsPolicy() + { + _logger.LogInformation("GetCorsPolicy"); + var policy = new CorsPolicy(); + + if (_classFunctionNameType != null) + { + // Method to execute + var executeMethod = _classFunctionNameType.GetMethod("SetCorsPolicy", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); + + if (executeMethod != null) + { + executeMethod.Invoke(null, new object[] { policy }); + var headers = policy.GetCorsHeaders(); + _logger.LogInformation($"CorsPolicy: {string.Join(", ", headers)}"); + } + } + else + { + throw new Exception("Type not found."); + } + + return policy; + } + + public Task Execute(FissionContext context) + { + if (context == null) + { + throw new ArgumentNullException(nameof(context)); + } + + /*var function = this._functionStoreService.GetFunction(); + + if (function == null) + { + throw new Exception("Function not specialized."); + } + + return ExecuteAndUnload(function.Assembly, function.Namespace, function.FunctionName, context);*/ + return ExecuteMethod(context); + } + + /*[MethodImpl(MethodImplOptions.NoInlining)] + private async Task ExecuteAndUnload(string assemblyPath, string nameSpace, string classFunctionName, FissionContext context) + { + ExecuteMethod(context); + /*if (!System.IO.File.Exists($"/function/{assemblyPath}")) + { + throw new Exception($"File /function/{assemblyPath} not found."); + } + + // Delete the common library if it exists. This is to ensure that the latest version is always loaded. + if (File.Exists($"/function/Fission.DotNet.Common.dll")) + { + File.Delete($"/function/Fission.DotNet.Common.dll"); + } + + if (File.Exists($"/function/Microsoft.Extensions.DependencyInjection.Abstractions.dll")) + { + File.Delete($"/function/Microsoft.Extensions.DependencyInjection.Abstractions.dll"); + } + + WeakReference alcWeakRef = null; + + var alc = new CustomAssemblyLoadContext($"/function/{assemblyPath}", isCollectible: true); + try + { + var assemblyFunction = alc.LoadFromAssemblyPath($"/function/{assemblyPath}"); + + alcWeakRef = new WeakReference(alc, trackResurrection: true); + + // Ottieni tutte le classi nell'assembly + Type[] types = assemblyFunction.GetTypes(); + _logger.LogInformation("Elenco delle classi nell'assembly:"); + foreach (var type1 in types) + { + _logger.LogInformation(type1.FullName); + } + + _logger.LogInformation($"Class try found: {nameSpace}.{classFunctionName}"); + + var classFunctionNameType = assemblyFunction.GetType($"{nameSpace}.{classFunctionName}"); + + if (classFunctionNameType != null) + { + MethodInfo configureServicesMethod = classFunctionNameType.GetMethod("ConfigureServices", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); + ServiceCollection serviceCollection = null; + ServiceProvider serviceProvider = null; + if (configureServicesMethod != null) + { + serviceCollection = new ServiceCollection(); + configureServicesMethod.Invoke(null, new object[] { serviceCollection }); + serviceCollection.AddTransient(classFunctionNameType); + serviceProvider = serviceCollection.BuildServiceProvider(); + } + + // Method to execute + var executeMethod = classFunctionNameType.GetMethod("Execute"); + + if (executeMethod != null) + { + // Create an instance of the object + object classInstance = null; + + if (serviceProvider != null) + { + classInstance = serviceProvider.GetService(classFunctionNameType); + } + else + { + classInstance = Activator.CreateInstance(classFunctionNameType); + } + + if (classInstance == null) + { + throw new Exception("Instance not created."); + } + + var executeMethodParameters = new object[] { context }; + + _logger.LogDebug($"Executing {classFunctionNameType.FullName}.{executeMethod.Name}"); + + var parameters = executeMethod.GetParameters(); + if (parameters.Length > 1) + { + _logger.LogDebug($"Method {executeMethod.Name} has more than one parameter."); + if (parameters[1].ParameterType == typeof(Common.ILogger)) + { + executeMethodParameters = new object[] { context, new FissionLoggerAdapter(_logger) }; + } + } + + _logger.LogDebug($"Method {executeMethod.Name} has {parameters.Length} parameters."); + + // Execute the method + var result = executeMethod.Invoke(classInstance, executeMethodParameters); + + if (result is Task task) + { + _logger.LogInformation("Task found."); + await task.ConfigureAwait(false); + var taskType = task.GetType(); + if (taskType.IsGenericType) + { + return taskType.GetProperty("Result").GetValue(task); + } + return null; + } + else + { + _logger.LogInformation("Task not found."); + } + + return result; + } + else + { + throw new Exception("Method not found."); + } + } + else + { + throw new Exception("Type not found."); + } + } + finally + { + alc.Unload(); + + for (int i = 0; alcWeakRef.IsAlive && (i < 10); i++) + { + GC.Collect(); + GC.WaitForPendingFinalizers(); + } + }* + }*/ + + public void Load() + { + var function = this._functionStoreService.GetFunction(); + + if (function == null) + { + throw new Exception("Function not specialized."); + } + + LoadAssembly(function.Assembly, function.Namespace, function.FunctionName); + } + + public void Unload() + { + if (_alc != null) + { + _alc.Unload(); + + for (int i = 0; _alcWeakRef != null && _alcWeakRef.IsAlive && (i < 10); i++) + { + GC.Collect(); + GC.WaitForPendingFinalizers(); + } + } + } + + + + private void LoadAssembly(string assemblyPath, string nameSpace, string classFunctionName) + { + if (!System.IO.File.Exists($"/function/{assemblyPath}")) + { + throw new Exception($"File /function/{assemblyPath} not found."); + } + + // Delete the common library if it exists. This is to ensure that the latest version is always loaded. + if (File.Exists($"/function/Fission.DotNet.Common.dll")) + { + File.Delete($"/function/Fission.DotNet.Common.dll"); + } + + if (File.Exists($"/function/Microsoft.Extensions.DependencyInjection.Abstractions.dll")) + { + File.Delete($"/function/Microsoft.Extensions.DependencyInjection.Abstractions.dll"); + } + + // Try simpler assembly loading first for debugging + try + { + _logger.LogInformation("Attempting standard Assembly.LoadFrom for debugging"); + _assemblyFunction = Assembly.LoadFrom($"/function/{assemblyPath}"); + _logger.LogInformation("Standard Assembly.LoadFrom succeeded"); + } + catch (Exception ex) + { + _logger.LogError(ex, "Standard Assembly.LoadFrom failed, trying CustomAssemblyLoadContext"); + + _alc = new CustomAssemblyLoadContext($"/function/{assemblyPath}", isCollectible: true); + + // Ensure dependencies are available in the load context + var fissionCommonPath = "/function/Fission.DotNet.Common.dll"; + if (System.IO.File.Exists(fissionCommonPath)) + { + _logger.LogInformation("Loading Fission.DotNet.Common.dll dependency"); + _alc.LoadFromAssemblyPath(fissionCommonPath); + } + + _assemblyFunction = _alc.LoadFromAssemblyPath($"/function/{assemblyPath}"); + _alcWeakRef = new WeakReference(_alc, trackResurrection: true); + } + + // First try with namespace + _classFunctionNameType = _assemblyFunction.GetType($"{nameSpace}.{classFunctionName}"); + + // If not found with namespace, try without namespace (global namespace) + if (_classFunctionNameType == null) + { + _logger.LogInformation("Type {TypeName} not found with namespace, trying without namespace", $"{nameSpace}.{classFunctionName}"); + _classFunctionNameType = _assemblyFunction.GetType(classFunctionName); + } + + // If still not found, list all available types for debugging + if (_classFunctionNameType == null) + { + try + { + _logger.LogInformation("Assembly full name: {AssemblyFullName}", _assemblyFunction.FullName); + _logger.LogInformation("Assembly location: {Location}", _assemblyFunction.Location); + _logger.LogInformation("Assembly entry point: {EntryPoint}", _assemblyFunction.EntryPoint?.Name ?? "None"); + + var allTypes = _assemblyFunction.GetTypes(); + _logger.LogInformation("Total types in assembly: {TypeCount}", allTypes.Length); + + var availableTypes = allTypes.Select(t => t.FullName ?? t.Name).ToArray(); + _logger.LogError("Type {TypeName} not found. Available types: {AvailableTypes}", classFunctionName, string.Join(", ", availableTypes)); + + // Additional debugging - check if assembly has any exported types + var exportedTypes = _assemblyFunction.GetExportedTypes().Select(t => t.FullName ?? t.Name).ToArray(); + _logger.LogInformation("Exported types: {ExportedTypes}", string.Join(", ", exportedTypes)); + + // Check assembly file path and if it exists + _logger.LogInformation("Assembly loaded from: {Location}, Exists: {FileExists}", + _assemblyFunction.Location, System.IO.File.Exists(_assemblyFunction.Location)); + + // Check if the assembly file has any content + if (System.IO.File.Exists(_assemblyFunction.Location)) + { + var fileInfo = new System.IO.FileInfo(_assemblyFunction.Location); + _logger.LogInformation("Assembly file size: {FileSize} bytes", fileInfo.Length); + } + } + catch (Exception ex) + { + _logger.LogError(ex, "Error getting types from assembly"); + } + + throw new Exception("Type not found."); + } + + _logger.LogInformation("Successfully found type: {TypeName}", _classFunctionNameType.FullName); + } + + + [MethodImpl(MethodImplOptions.NoInlining)] + private async Task ExecuteMethod(FissionContext context) + { + if (_classFunctionNameType != null) + { + MethodInfo configureServicesMethod = _classFunctionNameType.GetMethod("ConfigureServices", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); + ServiceProvider serviceProvider = null; + if (configureServicesMethod != null) + { + var serviceCollection = new ServiceCollection(); + configureServicesMethod.Invoke(null, new object[] { serviceCollection }); + serviceCollection.AddTransient(_classFunctionNameType); + serviceProvider = serviceCollection.BuildServiceProvider(); + } + + // Method to execute + var executeMethod = _classFunctionNameType.GetMethod("Execute"); + + if (executeMethod != null) + { + // Create an instance of the object + object classInstance = null; + + if (serviceProvider != null) + { + classInstance = serviceProvider.GetService(_classFunctionNameType); + } + else + { + classInstance = Activator.CreateInstance(_classFunctionNameType); + } + + if (classInstance == null) + { + throw new Exception("Instance not created."); + } + + var executeMethodParameters = new object[] { context }; + + _logger.LogDebug($"Executing {_classFunctionNameType.FullName}.{executeMethod.Name}"); + + var parameters = executeMethod.GetParameters(); + if (parameters.Length > 1) + { + _logger.LogDebug($"Method {executeMethod.Name} has more than one parameter."); + if (parameters[1].ParameterType == typeof(Common.ILogger)) + { + executeMethodParameters = new object[] { context, new FissionLoggerAdapter(_logger) }; + } + } + + _logger.LogDebug($"Method {executeMethod.Name} has {parameters.Length} parameters."); + + // Execute the method + var result = executeMethod.Invoke(classInstance, executeMethodParameters); + + if (result is Task task) + { + _logger.LogInformation("Task found."); + await task.ConfigureAwait(false); + var taskType = task.GetType(); + if (taskType.IsGenericType) + { + return taskType.GetProperty("Result").GetValue(task); + } + return null; + } + else + { + _logger.LogInformation("Task not found."); + } + + return result; + } + else + { + throw new Exception("Method not found."); + } + } + else + { + throw new Exception("Type not found."); + } + + + /*MethodInfo method = _classFunctionNameType.GetMethod(methodName); + + if (method == null) + { + throw new Exception("Method not found."); + } + + object classInstance = Activator.CreateInstance(_classFunctionNameType); + var executeMethodParameters = new object[] { context }; + + var parameters = method.GetParameters(); + if (parameters.Length > 1 && parameters[1].ParameterType == typeof(Common.ILogger)) + { + executeMethodParameters = new object[] { context, new FissionLoggerAdapter(_logger) }; + } + + var result = method.Invoke(classInstance, executeMethodParameters); + + if (result is Task task) + { + await task.ConfigureAwait(false); + var taskType = task.GetType(); + if (taskType.IsGenericType) + { + return taskType.GetProperty("Result").GetValue(task); + } + return null; + } + + return result;*/ + } +} + diff --git a/dotnet8/Fission.DotNet/Services/FunctionStoreService.cs b/dotnet8/Fission.DotNet/Services/FunctionStoreService.cs new file mode 100644 index 00000000..cd00e202 --- /dev/null +++ b/dotnet8/Fission.DotNet/Services/FunctionStoreService.cs @@ -0,0 +1,19 @@ +using System; +using Fission.DotNet.Interfaces; +using Fission.DotNet.Model; + +namespace Fission.DotNet.Services; + +public class FunctionStoreService : IFunctionStoreService +{ + private FunctionStore _function; + public FunctionStore GetFunction() + { + return _function; + } + + public void SetFunction(FunctionStore function) + { + _function = function; + } +} diff --git a/dotnet8/Fission.DotNet/Services/SpecializeService.cs b/dotnet8/Fission.DotNet/Services/SpecializeService.cs new file mode 100644 index 00000000..20e04778 --- /dev/null +++ b/dotnet8/Fission.DotNet/Services/SpecializeService.cs @@ -0,0 +1,416 @@ +using System; +using System.Diagnostics; +using System.IO.Compression; +using Fission.DotNet.Interfaces; +using Fission.DotNet.Model; + +namespace Fission.DotNet.Services; + +public class SpecializeService : ISpecializeService +{ + private readonly ILogger _logger; + private readonly IFunctionStoreService _functionStoreService; + + public SpecializeService(ILogger logger, IFunctionStoreService functionStoreService) + { + this._logger = logger; + this._functionStoreService = functionStoreService; + } + + public void Specialize(FissionSpecializeRequest request) + { + if (request == null) + { + throw new NullReferenceException("Request is null"); + } + + if (System.IO.File.Exists("/userfunc/deployarchive.tmp")) + { + _logger.LogInformation("Deploy archive exists"); + UnzipDeployArchive("/userfunc/deployarchive.tmp", "/function"); + } + else + { + if (System.IO.File.Exists("/userfunc/deployarchive")) + { + _logger.LogInformation("Deploy archive exists"); + UnzipDeployArchive("/userfunc/deployarchive", "/function"); + } + else + { + _logger.LogInformation("Deploy archive does not exist"); + throw new Exception("Deploy archive does not exist"); + } + } + + // Check if this is a builder-compiled deployment by looking for pre-built application DLLs + var dllFiles = Directory.GetFiles("/function", "*.dll", SearchOption.AllDirectories); + + // Filter out dependency DLLs to find actual application DLLs + var knownDependencies = new HashSet(StringComparer.OrdinalIgnoreCase) + { + "Fission.DotNet.Common.dll", + "Microsoft.Extensions.DependencyInjection.Abstractions.dll", + "Newtonsoft.Json.dll", + "System.Text.Json.dll", + "Microsoft.OpenApi.dll" + }; + + // Look for DLLs that match common patterns for function assemblies + var applicationDlls = dllFiles.Where(dll => + { + var fileName = Path.GetFileName(dll); + // Skip known dependencies + if (knownDependencies.Contains(fileName)) + return false; + // Skip system libraries + if (fileName.StartsWith("System.", StringComparison.OrdinalIgnoreCase) || + fileName.StartsWith("Microsoft.", StringComparison.OrdinalIgnoreCase)) + return false; + return true; + }).ToArray(); + + var hasPreBuiltDlls = applicationDlls.Length > 0; + + _logger.LogInformation("Found {DllCount} total DLL files, {AppDllCount} application DLLs, has pre-built DLLs: {HasPreBuilt}", + dllFiles.Length, applicationDlls.Length, hasPreBuiltDlls); + + if (hasPreBuiltDlls) + { + // This is a builder-compiled deployment - use the pre-built DLL + // If we have multiple candidates, prefer one that matches the function name or contains "Example" + var mainDll = applicationDlls.FirstOrDefault(dll => + Path.GetFileNameWithoutExtension(dll).Contains("Example", StringComparison.OrdinalIgnoreCase) || + Path.GetFileNameWithoutExtension(dll).Equals(request.functionName, StringComparison.OrdinalIgnoreCase)) + ?? applicationDlls.FirstOrDefault(); + if (mainDll != null) + { + var assemblyName = Path.GetFileName(mainDll); + var functionName = string.IsNullOrEmpty(request.functionName) ? "MyFunction" : request.functionName; + + _logger.LogInformation("Using pre-built assembly: {AssemblyName} with function: {FunctionName}", assemblyName, functionName); + + // For builder deployments, the function identifier format is: assemblyFileName:namespace:functionName + // Try to detect namespace from assembly name (e.g., MultiFileExample.dll -> MultiFileExample namespace) + var assemblyBaseName = Path.GetFileNameWithoutExtension(assemblyName); + var nameSpace = ""; + + // If the assembly name suggests it has a namespace (e.g., MultiFileExample), use it + if (assemblyBaseName.Contains("Example") || assemblyBaseName.Contains("File")) + { + nameSpace = assemblyBaseName; + } + + var functionIdentifier = $"{assemblyBaseName}:{nameSpace}:{functionName}"; + _logger.LogInformation("Function identifier: {FunctionIdentifier}", functionIdentifier); + var functionStore = new FunctionStore(functionIdentifier); + _functionStoreService.SetFunction(functionStore); + } + else + { + throw new Exception("No main assembly found in pre-built deployment"); + } + } + else + { + // This is a source code deployment - compile it + var functionName = request.functionName; + if (string.IsNullOrEmpty(functionName)) + { + // Try to determine function name from the extracted files + var csFiles = Directory.GetFiles("/function", "*.cs", SearchOption.AllDirectories); + if (csFiles.Length > 0) + { + // Use the first .cs file name as the function name + functionName = Path.GetFileNameWithoutExtension(csFiles[0]); + _logger.LogInformation("Using function name from file: {FunctionName}", functionName); + } + else + { + // Default function name for single file deployments + functionName = "MyFunction"; + _logger.LogInformation("Using default function name: {FunctionName}", functionName); + } + } + + // For source code deployments, construct the function identifier and compile + var functionIdentifier = $"SingleFileProject:{functionName}:{functionName}"; + var functionStore = new FunctionStore(functionIdentifier); + + // Check if we need to compile source code + if (HasSourceCode(functionStore.Assembly)) + { + _logger.LogInformation("Source code detected, compiling..."); + CompileSourceCode(functionStore.Assembly); + } + + _functionStoreService.SetFunction(functionStore); + } + } + + private void UnzipDeployArchive(string zipFilePath, string extractPath) + { + if (System.IO.File.Exists(zipFilePath)) + { + _logger.LogInformation("Deploy archive exists at {ZipFilePath}", zipFilePath); + + if (!Directory.Exists(extractPath)) + { + Directory.CreateDirectory(extractPath); + } + + try + { + // Try to unzip the ZIP file into the /function folder + ZipFile.ExtractToDirectory(zipFilePath, extractPath); + _logger.LogInformation("Deploy archive unzipped to {ExtractPath}", extractPath); + } + catch (System.IO.InvalidDataException ex) + { + _logger.LogWarning("ZIP file is corrupted: {Error}. Attempting to handle as single file.", ex.Message); + + // Handle corrupted ZIP by treating it as a single file + HandleCorruptedZipAsSingleFile(zipFilePath, extractPath); + } + } + else + { + _logger.LogWarning("Deploy archive does not exist at {ZipFilePath}", zipFilePath); + } + } + + private void HandleCorruptedZipAsSingleFile(string zipFilePath, string extractPath) + { + try + { + // Try to read the file as a single .cs file + var fileContent = System.IO.File.ReadAllText(zipFilePath); + + // Check if it looks like C# code + if (fileContent.Contains("using") || fileContent.Contains("class") || fileContent.Contains("public")) + { + _logger.LogInformation("File appears to be C# code, treating as single file"); + + // Create a simple function file + var functionPath = Path.Combine(extractPath, "MyFunction.cs"); + System.IO.File.WriteAllText(functionPath, fileContent); + + // Create a minimal .csproj file using ProjectReference (like the examples) + var csprojContent = @" + + + Library + net8.0 + + + + +"; + + var csprojPath = Path.Combine(extractPath, "SingleFileProject.csproj"); + System.IO.File.WriteAllText(csprojPath, csprojContent); + + _logger.LogInformation("Created single file project structure in {ExtractPath}", extractPath); + } + else + { + _logger.LogError("File does not appear to be valid C# code"); + throw new Exception("Invalid file content"); + } + } + catch (Exception ex) + { + _logger.LogError(ex, "Failed to handle corrupted ZIP as single file"); + throw; + } + } + + private bool HasSourceCode(string assemblyName) + { + // Check if the DLL exists, if not, check for .csproj or .cs files + var dllPath = $"/function/{assemblyName}"; + if (System.IO.File.Exists(dllPath)) + { + _logger.LogInformation("DLL already exists at {DllPath}", dllPath); + return false; // DLL exists, no need to compile + } + + // Look for project files or source files + var projectFiles = Directory.GetFiles("/function", "*.csproj", SearchOption.AllDirectories); + var sourceFiles = Directory.GetFiles("/function", "*.cs", SearchOption.AllDirectories); + + _logger.LogInformation("Found {ProjectFileCount} .csproj files and {SourceFileCount} .cs files", + projectFiles.Length, sourceFiles.Length); + + return projectFiles.Length > 0 || sourceFiles.Length > 0; + } + + private void CompileSourceCode(string expectedAssemblyName) + { + try + { + // Copy required libraries to function directory + CopyRequiredLibraries(); + + // Look for .csproj files + var projectFiles = Directory.GetFiles("/function", "*.csproj", SearchOption.AllDirectories); + + if (projectFiles.Length > 0) + { + var projectFile = projectFiles[0]; // Use the first project file found + var projectDir = Path.GetDirectoryName(projectFile); + + _logger.LogInformation("Compiling project: {ProjectFile}", projectFile); + + // Run dotnet publish to get all dependencies (instead of build) + var process = new Process + { + StartInfo = new ProcessStartInfo + { + FileName = "dotnet", + Arguments = $"publish \"{projectFile}\" -c Release -o \"/function/output\"", + WorkingDirectory = projectDir, + RedirectStandardOutput = true, + RedirectStandardError = true, + UseShellExecute = false, + CreateNoWindow = true + } + }; + + process.Start(); + var output = process.StandardOutput.ReadToEnd(); + var error = process.StandardError.ReadToEnd(); + process.WaitForExit(); + + _logger.LogInformation("Build output: {Output}", output); + + if (process.ExitCode != 0) + { + _logger.LogError("Build failed with exit code {ExitCode}: {Error}", process.ExitCode, error); + throw new Exception($"Compilation failed: {error}"); + } + + _logger.LogInformation("Compilation successful"); + + // Copy all published files from /function/output to function root + var projectName = Path.GetFileNameWithoutExtension(projectFile); + var publishOutputDir = "/function/output"; + var sourceDllPath = Path.Combine(publishOutputDir, $"{projectName}.dll"); + var destDllPath = Path.Combine("/function", $"{projectName}.dll"); + + _logger.LogInformation("Looking for published DLL at: {SourcePath}", sourceDllPath); + _logger.LogInformation("Will copy to: {DestPath}", destDllPath); + + if (Directory.Exists(publishOutputDir)) + { + var publishedFiles = Directory.GetFiles(publishOutputDir, "*.*"); + _logger.LogInformation("Published files: {Files}", string.Join(", ", publishedFiles.Select(Path.GetFileName))); + + // Copy all published files to function root + foreach (var publishedFile in publishedFiles) + { + var fileName = Path.GetFileName(publishedFile); + var destPath = Path.Combine("/function", fileName); + if (System.IO.File.Exists(destPath)) + { + System.IO.File.Delete(destPath); // Remove old version + } + System.IO.File.Copy(publishedFile, destPath); + _logger.LogInformation("Copied published file {FileName} to function directory", fileName); + } + + // Verify the main DLL was published and copied + if (System.IO.File.Exists(destDllPath)) + { + var fileInfo = new FileInfo(destDllPath); + _logger.LogInformation("Verified DLL exists at {DestPath}, size: {Size} bytes", destDllPath, fileInfo.Length); + } + else + { + _logger.LogError("Main DLL not found after publish: {DestPath}", destDllPath); + throw new Exception($"Main DLL not found after publish: {destDllPath}"); + } + } + else + { + _logger.LogError("Publish output directory does not exist: {PublishDir}", publishOutputDir); + throw new Exception($"Publish output directory does not exist: {publishOutputDir}"); + } + + // Check what files exist after compilation + var dllFiles = Directory.GetFiles("/function", "*.dll"); + _logger.LogInformation("DLL files after compilation: {Files}", string.Join(", ", dllFiles)); + } + else + { + _logger.LogWarning("No .csproj files found for compilation"); + throw new Exception("No .csproj files found for compilation"); + } + } + catch (Exception ex) + { + _logger.LogError(ex, "Error during compilation"); + throw; + } + } + + private void CopyRequiredLibraries() + { + try + { + // Copy Fission.DotNet.Common.dll to function directory for compilation + var sourcePath = "/app/Fission.DotNet.Common.dll"; + var destPath = "/function/Fission.DotNet.Common.dll"; + + if (System.IO.File.Exists(sourcePath)) + { + _logger.LogInformation("Source path exists: {SourcePath}", sourcePath); + if (!System.IO.File.Exists(destPath)) + { + System.IO.File.Copy(sourcePath, destPath); + _logger.LogInformation("Copied Fission.DotNet.Common.dll to function directory"); + + // Verify the copy worked + if (System.IO.File.Exists(destPath)) + { + _logger.LogInformation("Copy verified: {DestPath} exists", destPath); + } + else + { + _logger.LogError("Copy failed: {DestPath} does not exist after copy", destPath); + } + } + else + { + _logger.LogInformation("Destination already exists: {DestPath}", destPath); + } + } + else + { + _logger.LogError("Source path does not exist: {SourcePath}", sourcePath); + } + + // Also copy other dependencies if needed + var dependencies = new[] + { + "Microsoft.Extensions.DependencyInjection.Abstractions.dll" + }; + + foreach (var dep in dependencies) + { + var depSourcePath = $"/app/{dep}"; + var depDestPath = $"/function/{dep}"; + + if (System.IO.File.Exists(depSourcePath) && !System.IO.File.Exists(depDestPath)) + { + System.IO.File.Copy(depSourcePath, depDestPath); + _logger.LogInformation("Copied {Dependency} to function directory", dep); + } + } + } + catch (Exception ex) + { + _logger.LogWarning(ex, "Error copying required libraries"); + } + } +} diff --git a/dotnet8/Fission.DotNet/appsettings.Development.json b/dotnet8/Fission.DotNet/appsettings.Development.json new file mode 100644 index 00000000..0c208ae9 --- /dev/null +++ b/dotnet8/Fission.DotNet/appsettings.Development.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + } +} diff --git a/dotnet8/Fission.DotNet/appsettings.json b/dotnet8/Fission.DotNet/appsettings.json new file mode 100644 index 00000000..10f68b8c --- /dev/null +++ b/dotnet8/Fission.DotNet/appsettings.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*" +} diff --git a/dotnet8/Makefile b/dotnet8/Makefile new file mode 100644 index 00000000..b8c53c79 --- /dev/null +++ b/dotnet8/Makefile @@ -0,0 +1,15 @@ +PLATFORMS ?= linux/amd64 + +REPO ?= fission +TAG ?= latest + +.PHONY: all +all: dotnet8-env-img dotnet8-builder-img + +dotnet8-env-img: Dockerfile + @echo === Building image $(REPO)/dotnet8-env:$(TAG) using context $(CURDIR) and dockerfile $< + docker build --platform=$(PLATFORMS) -t $(REPO)/dotnet8-env:$(TAG) -f $< . + +dotnet8-builder-img: builder/Dockerfile + @echo === Building image $(REPO)/dotnet8-builder:$(TAG) using context $(CURDIR)/builder and dockerfile $< + docker build --platform=$(PLATFORMS) -t $(REPO)/dotnet8-builder:$(TAG) -f $< builder/ \ No newline at end of file diff --git a/dotnet8/README.md b/dotnet8/README.md new file mode 100644 index 00000000..e89ae40c --- /dev/null +++ b/dotnet8/README.md @@ -0,0 +1,521 @@ +# Fission: dotnet 8.0 C# Environment + +This is a simple dotnet core 8.0 C# environment for Fission. + +It's a Docker image containing the dotnet 8.0.0 runtime. The image +uses Kestrel with ASP.NET Core to host the internal web server and uses +Roslyn to compile the uploaded code. + +The image supports compiling and running code with types defined in +mscorlib and does not at present support other library references. +One workaround for this would be to add the references to this project's +.csproj file and rebuild the container. + +The environment works via convention where you create a C# class +containing a method named Execute taking a single +parameter, a FissionContext object. + +The FissionContext object gives access to the arguments and other items +like logging. Please see FissionContext.cs for public API. + +Example of simplest possible class to be executed: + +``` +using System; +using Fission.DotNet.Common; + +public class MyFunction { + public string Execute(FissionContext context) { + return null; + } +} +``` + +Please see examples below. Ready-to-run examples are available in the examples directory: +- **HelloWorld** - Basic function demonstration +- **HttpTriggerExample** - HTTP request handling with context detection +- **AsyncFunctionExample** - Demonstrates async/await patterns +- **MultiFileExample** - Complex multi-file project with MVC pattern + +## Rebuilding and pushing the image + +To rebuild the image you will have to install Docker with version higher than 17.05+ +in order to support multi-stage builds feature. + +### Rebuild containers + +Move to the directory containing the source and start the container build process: + +``` +docker build -t USER/dotnet8-env . +``` + +After the build finishes push the new image to a Docker registry using the +standard procedure. + +## Echo example + +### Setup fission environment +First you need to setup the fission according to your cluster setup as +specified here: https://github.com/fission/fission + + +### Create the class to run + +Secondly you need to create a file /tmp/func.cs containing the following code: + +``` +using System; +using Fission.DotNet.Common; + +public class MyFunction +{ + public string Execute(FissionContext context){ + context.Logger.WriteInfo("executing.. {0}", context.Arguments["text"]); + return (string)context.Arguments["text"]; + } +} +``` +### Run the example + +Lastly to run the example: + +``` +$ fission env create --name dotnet8 --image fission/dotnet8-env + +$ fission function create --name echo --env dotnet8 --code /tmp/func.cs + +$ fission route create --method GET --url /echo --function echo + +$ curl http://$FISSION_ROUTER/echo?text=hello%20world! + hello world +``` + +## Addition service example + +### Setup fission environment +First you need to setup the fission according to your cluster setup as +specified here: https://github.com/fission/fission + + +### Create the class to run + +Secondly you need to create a file /tmp/func.cs containing the following code: + +``` +using System; +using Fission.DotNet.Common; + +public class MyFunction +{ + public string Execute(FissionContext context){ + var x = Convert.ToInt32(context.Arguments["x"]); + var y = Convert.ToInt32(context.Arguments["y"]); + return (x+y).ToString(); + } +} +``` +### Run the example + +Lastly to run the example: + +``` +$ fission env create --name dotnet8 --image fission/dotnet8-env + +$ fission function create --name addition --env dotnet8 --code /tmp/func.cs + +$ fission route create --method GET --url /add --function addition + +$ curl "http://$FISSION_ROUTER/add?x=30&y=12" + 42 +``` + +## Accessing http request information example + +### Setup fission environment +First you need to setup the fission according to your cluster setup as +specified here: https://github.com/fission/fission + + +### Create the class to run + +Secondly you need to create a file /tmp/func.cs containing the following code: + +``` +using System; +using Fission.DotNet.Common; + +public class MyFunction +{ + public string Execute(FissionContext context){ + var httpContext = context as FissionHttpContext; + if (httpContext != null) { + var buffer = new System.Text.StringBuilder(); + foreach(var header in httpContext.Headers){ + buffer.AppendLine(header.Key); + foreach(var item in header.Value){ + buffer.AppendLine($"\t{item}"); + } + } + buffer.AppendLine($"Url: {httpContext.Url}, method: {httpContext.Method}"); + return buffer.ToString(); + } + return "Not an HTTP request"; + } +} + +``` +### Run the example + +Lastly to run the example: + +``` +$ fission env create --name dotnet8 --image fission/dotnet8-env + +$ fission function create --name httpinfo --env dotnet8 --code /tmp/func.cs + +$ fission route create --method GET --url /http_info --function httpinfo + +$ curl "http://$FISSION_ROUTER/http_info" +Accept + */*;q=1 +Host + fissionserver:8888 +User-Agent + curl/7.47.0 +Url: http://fissionserver:8888, method: GET + +``` + +## Accessing http request body example + +### Setup fission environment +First you need to setup the fission according to your cluster setup as +specified here: https://github.com/fission/fission + + +### Create the class to run + +Secondly you need to create a file /tmp/func.cs containing the following code: + +``` +using System.IO; +using System.Text.Json; +using Fission.DotNet.Common; + +public class MyFunction +{ + public string Execute(FissionContext context) + { + var httpContext = context as FissionHttpContext; + if (httpContext != null && httpContext.Body != null) { + var person = JsonSerializer.Deserialize(httpContext.Body); + return $"Hello, my name is {person.Name} and I am {person.Age} years old."; + } + return "No body found"; + } +} + +public class Person +{ + public string Name { get; set; } + public int Age { get; set; } +} + +``` +### Run the example + +Lastly to run the example: + +``` +$ fission env create --name dotnet8 --image fission/dotnet8-env + +$ fission function create --name httpbody --env dotnet8 --code /tmp/func.cs + +$ fission route create --method POST --url /http_body --function httpbody + +$ curl -XPOST "http://$FISSION_ROUTER/http_body" -d '{ "Name":"Arthur", "Age":42}' +Hello, my name is Arthur and I am 42 years old. + +``` + +## Builder Support + +The dotnet8 environment includes a builder image for compiling source packages with NuGet dependencies. + +### Create the environment with builder + +``` +$ fission env create --name dotnet8 \ + --image fission/dotnet8-env \ + --builder fission/dotnet8-builder +``` + +### Deploy a source package + +Create a project structure: +``` +MyProject/ +├── MyProject.csproj +├── MyFunction.cs +└── nuget.txt (optional - for additional NuGet packages) +``` + +Example MyProject.csproj: +```xml + + + Library + net8.0 + + + + + + + ../Fission.DotNet.Common.dll + + + +``` + +Create a zip package and deploy: +``` +$ zip -r myproject.zip MyProject/ +$ fission fn create --name myproject --env dotnet8 --src myproject.zip +``` + +## Async Function Support + +The environment supports async/await patterns: + +``` +using System.Threading.Tasks; +using Fission.DotNet.Common; + +public class MyFunction +{ + public async Task Execute(FissionContext context) + { + // Simulate async work + await Task.Delay(100); + + // Async HTTP calls, database operations, etc. + return "Async result"; + } +} +``` + +## Logging Support + +Functions can use structured logging via ILogger: + +``` +using Microsoft.Extensions.Logging; +using Fission.DotNet.Common; + +public class MyFunction +{ + public object Execute(FissionContext context, ILogger logger) + { + logger.LogInformation("Processing request"); + + try + { + // Function logic + return "Success"; + } + catch (Exception ex) + { + logger.LogError(ex, "Error processing request"); + throw; + } + } +} +``` + +## CORS Support + +Enable CORS for cross-origin requests: + +``` +using Fission.DotNet.Common; + +public class MyFunction +{ + public object Execute(FissionContext context) + { + // Your function logic + return "API Response"; + } + + public static void SetCorsPolicy(ICorsPolicy policy) + { + policy.AllowAnyHeader(); + policy.AllowAnyMethod(); + policy.AllowAnyOrigin(); + policy.AllowCredentials(); + } +} +``` + + +## Developing/debugging the environment locally + +The easiest way to debug the environment is to open the directory in +Visual Studio Code (VSCode) as that will setup debugger for you the +first time. + +Remember to install the excellent extension +"C# for Visual Studio Code (powered by OmniSharp)" to get statement completion + +To debug locally: +1. Open the directory in VSCode. +This will prompt restore of packages and query if debugger setup is needed. Accept both prompts. +2. Press F5 to start the web server. Set breakpoints etc. +3. Add a code file containing valid C# at /tmp/func.cs +4. Specialize the service with curl via post +``` +$ curl -XPOST http://localhost:8888/specialize +``` +5. Call your function with curl +``` +$ curl -XGET http://localhost:8888 +``` + +## Additional Features + +**1. Namespace support:** + +You can use namespaces for your function classes. The main execution class can have any name, and the entry method is Execute: + +``` +using System; +using Fission.DotNet.Common; + +namespace MyNamespace +{ + public class MyFunction + { + public string Execute(FissionContext context){ + var helper = new HelperClass(); + return helper.Process(context); + } + } + + public class HelperClass + { + public string Process(FissionContext context){ + // Helper logic + return "Processed"; + } + } +} +``` + +**2. Configuration file support:** + +With Fission builder support, you can include additional configuration files in your source package that can be read by your function. + +Example package structure: +``` +Source Package zip: +--source.zip + |--MyFunction.cs + |--MyProject.csproj + |--appsettings.json + |--nuget.txt (optional) +``` + +Example appsettings.json: +```json +{ + "name": "Alpha", + "endpoints": [ + { "port": 1002 }, + { "port": 3004 } + ] +} +``` + +Example function using configuration: +``` +using System; +using System.IO; +using System.Text.Json; +using Fission.DotNet.Common; + +public class MyFunction +{ + public string Execute(FissionContext context){ + // Read configuration file + var configPath = Path.Combine(context.ExecutionPath, "appsettings.json"); + if (File.Exists(configPath)) { + var json = File.ReadAllText(configPath); + var settings = JsonSerializer.Deserialize(json); + return $"Endpoint port: {settings.Endpoints[0].Port}"; + } + return "No config found"; + } +} + +public class AppSettings +{ + public string Name { get; set; } + public List Endpoints { get; set; } +} + +public class Endpoint +{ + public int Port { get; set; } +} +``` + +**3. NuGet support:** + +With the builder environment, you can add NuGet packages to your functions. Create a nuget.txt file in your source package listing the packages: + +``` +Newtonsoft.Json +Dapper +Serilog +``` + +These packages will be automatically restored during the build process. + +## Troubleshooting + +### Common Issues + +**Build Failures:** +- Ensure .csproj file has correct format +- Check that Fission.DotNet.Common reference path is correct +- Verify all .cs files are included with `` + +**Function Timeouts:** +- Check function logs: `fission fn logs --name function-name` +- Verify package build status: `fission pkg list` +- Check build logs: `fission pkg info --name package-name` + +**HTTP Context Issues:** +- Cast context appropriately: `context as FissionHttpContext` +- Check if X-Forwarded-Proto header is set for HTTP detection + +### Debug Commands + +```bash +# Check environment status +fission env list + +# Check package build status +fission pkg list + +# View package build logs +fission pkg info --name package-name + +# View function logs +fission fn logs --name function-name + +# Test function directly +fission fn test --name function-name +``` \ No newline at end of file diff --git a/dotnet8/builder/Dockerfile b/dotnet8/builder/Dockerfile new file mode 100644 index 00000000..ad690ba3 --- /dev/null +++ b/dotnet8/builder/Dockerfile @@ -0,0 +1,31 @@ +ARG BUILDER_IMAGE=ghcr.io/fission/builder:v1.21.0 +ARG DOTNET_BASE_IMG=mcr.microsoft.com/dotnet/sdk:8.0 + +FROM ${BUILDER_IMAGE} +FROM ${DOTNET_BASE_IMG} + +COPY --from=0 /builder /builder + +# Install required tools +RUN apt-get update && apt-get install -y unzip zip curl && rm -rf /var/lib/apt/lists/* + +# Create Fission.DotNet.Common project directory +RUN mkdir -p /app/Fission.DotNet.Common + +# Copy Fission.DotNet.Common source files +COPY Fission.DotNet.Common/ /app/Fission.DotNet.Common/ + +# Build the Fission.DotNet.Common library properly +WORKDIR /app/Fission.DotNet.Common +RUN dotnet build -c Release +RUN dotnet publish -c Release -o /app + +# Verify the DLL was created +RUN ls -la /app/Fission.DotNet.Common.dll + +# Copy the build script +COPY builder/defaultBuildCmd /usr/local/bin/build +COPY builder/defaultBuildCmd /build +RUN chmod +x /usr/local/bin/build /build + +EXPOSE 8001 \ No newline at end of file diff --git a/dotnet8/builder/Makefile b/dotnet8/builder/Makefile new file mode 100644 index 00000000..5bb6fbc0 --- /dev/null +++ b/dotnet8/builder/Makefile @@ -0,0 +1,8 @@ +PLATFORMS ?= linux/amd64,linux/arm64 + +-include ../../rules.mk + +.PHONY: all +all: dotnet8-builder-img + +dotnet8-builder-img: Dockerfile \ No newline at end of file diff --git a/dotnet8/builder/defaultBuildCmd b/dotnet8/builder/defaultBuildCmd new file mode 100755 index 00000000..ca34289e --- /dev/null +++ b/dotnet8/builder/defaultBuildCmd @@ -0,0 +1,38 @@ +#!/bin/bash +set -ex + +echo "Building .NET 8 project from ${SRC_PKG} to ${DEPLOY_PKG}" + +# Create destination directory +mkdir -p ${DEPLOY_PKG} + +# Copy all source files to destination +cp -r ${SRC_PKG}/* ${DEPLOY_PKG}/ + +# Copy Fission.DotNet.Common library +cp /app/Fission.DotNet.Common.dll ${DEPLOY_PKG}/ +cp /app/Fission.DotNet.Common.deps.json ${DEPLOY_PKG}/ 2>/dev/null || true + +# Find project file +CSPROJ_FILE=$(find ${DEPLOY_PKG} -name "*.csproj" | head -1) + +if [ -n "$CSPROJ_FILE" ]; then + echo "Found project file: $CSPROJ_FILE" + PROJECT_DIR=$(dirname "$CSPROJ_FILE") + + # Ensure Fission.DotNet.Common.dll is in the project directory + cp /app/Fission.DotNet.Common.dll ${PROJECT_DIR}/ + + cd ${PROJECT_DIR} + + # First restore packages + dotnet restore + + # Then publish with restore (removing --no-restore flag) + dotnet publish -c Release -o ${DEPLOY_PKG} +else + echo "No project file found, treating as single file deployment" + # For single .cs files, they will be compiled at runtime +fi + +echo "Build complete" \ No newline at end of file diff --git a/dotnet8/envconfig.json b/dotnet8/envconfig.json new file mode 100644 index 00000000..dfd1be17 --- /dev/null +++ b/dotnet8/envconfig.json @@ -0,0 +1,22 @@ +[ + { + "builder": "dotnet8-builder", + "examples": "https://github.com/fission/environments/tree/master/dotnet8/examples", + "icon": "./logo/logo_NETcore.svg", + "image": "dotnet8-env", + "keywords": [], + "kind": "environment", + "maintainers": [ + { + "link": "https://github.com/sanketsudake", + "name": "sanketsudake" + } + ], + "name": "Dotnet 8 Environment", + "readme": "https://github.com/fission/environments/tree/master/dotnet8", + "runtimeVersion": "8.0.0", + "shortDescription": "Fission Dotnet 8.0.0 runtime uses Kestrel to host the internal web server. Supports both single file compilation (--code) and project-based compilation (--src).", + "status": "Stable", + "version": "1.1.0" + } +] diff --git a/dotnet8/examples/AsyncFunctionExample/AsyncFunctionExample.csproj b/dotnet8/examples/AsyncFunctionExample/AsyncFunctionExample.csproj new file mode 100644 index 00000000..8f23c4c9 --- /dev/null +++ b/dotnet8/examples/AsyncFunctionExample/AsyncFunctionExample.csproj @@ -0,0 +1,14 @@ + + + + Library + net8.0 + + + + + ./Fission.DotNet.Common.dll + + + + diff --git a/dotnet8/examples/AsyncFunctionExample/MyFunction.cs b/dotnet8/examples/AsyncFunctionExample/MyFunction.cs new file mode 100644 index 00000000..be5603fb --- /dev/null +++ b/dotnet8/examples/AsyncFunctionExample/MyFunction.cs @@ -0,0 +1,17 @@ +using Fission.DotNet.Common; +using System.Threading.Tasks; + +public class MyFunction +{ + public object Execute(FissionContext context) + { + // Call the async method and wait for it to complete + return ExecuteAsync(context).GetAwaiter().GetResult(); + } + + public async Task ExecuteAsync(FissionContext context) + { + await Task.Delay(1000); // Simulate an asynchronous operation + return "Hello from async function!"; + } +} diff --git a/dotnet8/examples/HelloWorld/HelloWorld.csproj b/dotnet8/examples/HelloWorld/HelloWorld.csproj new file mode 100644 index 00000000..36a12871 --- /dev/null +++ b/dotnet8/examples/HelloWorld/HelloWorld.csproj @@ -0,0 +1,11 @@ + + + Library + net8.0 + + + + ./Fission.DotNet.Common.dll + + + diff --git a/dotnet8/examples/HelloWorld/MyFunction.cs b/dotnet8/examples/HelloWorld/MyFunction.cs new file mode 100644 index 00000000..3b387273 --- /dev/null +++ b/dotnet8/examples/HelloWorld/MyFunction.cs @@ -0,0 +1,9 @@ +using Fission.DotNet.Common; + +public class MyFunction +{ + public object Execute(FissionContext context) + { + return "Hello World!x"; + } +} diff --git a/dotnet8/examples/HttpTriggerExample/HttpTriggerExample.csproj b/dotnet8/examples/HttpTriggerExample/HttpTriggerExample.csproj new file mode 100644 index 00000000..8b490311 --- /dev/null +++ b/dotnet8/examples/HttpTriggerExample/HttpTriggerExample.csproj @@ -0,0 +1,11 @@ + + + Library + net8.0 + + + + ./Fission.DotNet.Common.dll + + + diff --git a/dotnet8/examples/HttpTriggerExample/MyFunction.cs b/dotnet8/examples/HttpTriggerExample/MyFunction.cs new file mode 100644 index 00000000..9f40bcd0 --- /dev/null +++ b/dotnet8/examples/HttpTriggerExample/MyFunction.cs @@ -0,0 +1,14 @@ +using Fission.DotNet.Common; + +public class MyFunction +{ + public object Execute(FissionContext context) + { + var httpContext = context as FissionHttpContext; + if (httpContext != null) + { + return $"Hello from HTTP trigger! Method: {httpContext.Method}, URL: {httpContext.Url}"; + } + return "Hello from non-HTTP trigger!"; + } +} diff --git a/dotnet8/examples/MultiFileExample/Controllers/ApiController.cs b/dotnet8/examples/MultiFileExample/Controllers/ApiController.cs new file mode 100644 index 00000000..579d94f0 --- /dev/null +++ b/dotnet8/examples/MultiFileExample/Controllers/ApiController.cs @@ -0,0 +1,129 @@ +using System; +using System.Threading.Tasks; +using MultiFileExample.Services; +using MultiFileExample.Models; +using Fission.DotNet.Common; + +namespace MultiFileExample.Controllers +{ + public class ApiController + { + private readonly WeatherService _weatherService; + private readonly UserService _userService; + private readonly DataProcessor _dataProcessor; + + public ApiController() + { + // In production, use dependency injection + _weatherService = new WeatherService(); + _userService = new UserService(); + _dataProcessor = new DataProcessor(); + } + + public async Task RouteRequest(FissionContext context) + { + var path = ExtractPath(context); + var method = ExtractMethod(context); + + // Route to appropriate handler based on path + return path.ToLower() switch + { + "" or "multifile" => GetApiInfo(), + "weather" => await _weatherService.GetWeatherAsync(), + "weather/forecast" => await _weatherService.GetForecastAsync(), + "users" => _userService.GetAllUsers(), + "users/active" => _userService.GetActiveUsers(), + "process" => _dataProcessor.ProcessData(new DataRequest { Input = "test-data" }), + "health" => GetHealthStatus(), + _ => GetNotFoundResponse(path) + }; + } + + private string ExtractPath(FissionContext context) + { + // Try to get path from HTTP context + if (context is FissionHttpContext httpContext) + { + return httpContext.Url?.TrimStart('/') ?? ""; + } + + // Fallback to subpath argument for testing + if (context?.Arguments?.ContainsKey("subpath") == true) + { + return context.Arguments["subpath"]?.ToString() ?? ""; + } + + return ""; + } + + private string ExtractMethod(FissionContext context) + { + if (context is FissionHttpContext httpContext) + { + return httpContext.Method?.ToUpper() ?? "GET"; + } + return "GET"; + } + + private object GetApiInfo() + { + return new + { + name = "Multi-File Example API", + description = "MVC-pattern Fission function with controller-based routing", + version = "2.0.0", + architecture = new + { + pattern = "MVC", + entryPoint = "MyFunction.cs", + controller = "ApiController.cs", + services = new[] { "WeatherService.cs", "UserService.cs", "DataProcessor.cs" }, + models = new[] { "User.cs", "Weather.cs", "DataModels.cs" } + }, + endpoints = GetAvailableEndpoints() + }; + } + + private object GetHealthStatus() + { + return new + { + status = "healthy", + service = "MultiFileExample", + version = "2.0.0", + timestamp = DateTime.UtcNow, + components = new + { + weatherService = "operational", + userService = "operational", + dataProcessor = "operational" + } + }; + } + + private object GetNotFoundResponse(string path) + { + return new + { + error = "Endpoint not found", + path = path, + statusCode = 404, + availableEndpoints = GetAvailableEndpoints() + }; + } + + private string[] GetAvailableEndpoints() + { + return new[] + { + "/ - API information", + "/weather - Current weather", + "/weather/forecast - 5-day forecast", + "/users - List all users", + "/users/active - List active users", + "/process - Data processing demo", + "/health - Health check" + }; + } + } +} \ No newline at end of file diff --git a/dotnet8/examples/MultiFileExample/Models/DataModels.cs b/dotnet8/examples/MultiFileExample/Models/DataModels.cs new file mode 100644 index 00000000..464dc61b --- /dev/null +++ b/dotnet8/examples/MultiFileExample/Models/DataModels.cs @@ -0,0 +1,43 @@ +using System; + +namespace MultiFileExample.Models +{ + public class DataRequest + { + public string Input { get; set; } + public string Format { get; set; } + public bool Validate { get; set; } + } + + public class ProcessedData + { + public string Original { get; set; } + public string Uppercase { get; set; } + public string Lowercase { get; set; } + public string Reversed { get; set; } + public int Length { get; set; } + public int WordCount { get; set; } + public string Hash { get; set; } + public string Base64Encoded { get; set; } + } + + public class ProcessingStats + { + public int CharacterCount { get; set; } + public int AlphaCount { get; set; } + public int DigitCount { get; set; } + public int SpaceCount { get; set; } + public int SpecialCharCount { get; set; } + } + + public class DataResponse + { + public bool Success { get; set; } + public string Error { get; set; } + public ProcessedData ProcessedData { get; set; } + public ProcessingStats Statistics { get; set; } + public DateTime ProcessedAt { get; set; } + public string ProcessingTime { get; set; } + public string Source { get; set; } + } +} \ No newline at end of file diff --git a/dotnet8/examples/MultiFileExample/Models/User.cs b/dotnet8/examples/MultiFileExample/Models/User.cs new file mode 100644 index 00000000..e3e9123a --- /dev/null +++ b/dotnet8/examples/MultiFileExample/Models/User.cs @@ -0,0 +1,23 @@ +using System; +using System.Collections.Generic; + +namespace MultiFileExample.Models +{ + public class User + { + public int Id { get; set; } + public string Name { get; set; } + public string Email { get; set; } + public bool IsActive { get; set; } + public DateTime CreatedAt { get; set; } + } + + public class UserListResponse + { + public List Users { get; set; } + public int Total { get; set; } + public string FilteredBy { get; set; } + public DateTime Timestamp { get; set; } + public string Source { get; set; } + } +} \ No newline at end of file diff --git a/dotnet8/examples/MultiFileExample/Models/Weather.cs b/dotnet8/examples/MultiFileExample/Models/Weather.cs new file mode 100644 index 00000000..3fd7c486 --- /dev/null +++ b/dotnet8/examples/MultiFileExample/Models/Weather.cs @@ -0,0 +1,34 @@ +using System; +using System.Collections.Generic; + +namespace MultiFileExample.Models +{ + public class WeatherResponse + { + public string City { get; set; } + public int Temperature { get; set; } + public string Condition { get; set; } + public int Humidity { get; set; } + public int WindSpeed { get; set; } + public DateTime Timestamp { get; set; } + public string Source { get; set; } + } + + public class DailyForecast + { + public DateTime Date { get; set; } + public int High { get; set; } + public int Low { get; set; } + public string Condition { get; set; } + public int ChanceOfRain { get; set; } + } + + public class ForecastResponse + { + public string City { get; set; } + public int Days { get; set; } + public List Forecasts { get; set; } + public DateTime GeneratedAt { get; set; } + public string Source { get; set; } + } +} \ No newline at end of file diff --git a/dotnet8/examples/MultiFileExample/MultiFileExample.csproj b/dotnet8/examples/MultiFileExample/MultiFileExample.csproj new file mode 100644 index 00000000..796cb33c --- /dev/null +++ b/dotnet8/examples/MultiFileExample/MultiFileExample.csproj @@ -0,0 +1,19 @@ + + + Library + net8.0 + MultiFileExample + MultiFileExample + true + + + + + ./Fission.DotNet.Common.dll + + + + + + + \ No newline at end of file diff --git a/dotnet8/examples/MultiFileExample/MyFunction.cs b/dotnet8/examples/MultiFileExample/MyFunction.cs new file mode 100644 index 00000000..ce325ca2 --- /dev/null +++ b/dotnet8/examples/MultiFileExample/MyFunction.cs @@ -0,0 +1,23 @@ +using Fission.DotNet.Common; +using System.Threading.Tasks; +using MultiFileExample.Controllers; + +namespace MultiFileExample +{ + + public class MyFunction + { + private readonly ApiController _controller; + + public MyFunction() + { + _controller = new ApiController(); + } + + public async Task Execute(FissionContext context) + { + // Delegate all routing and business logic to the controller + return await _controller.RouteRequest(context); + } + } +} \ No newline at end of file diff --git a/dotnet8/examples/MultiFileExample/README.md b/dotnet8/examples/MultiFileExample/README.md new file mode 100644 index 00000000..5103ba0d --- /dev/null +++ b/dotnet8/examples/MultiFileExample/README.md @@ -0,0 +1,155 @@ +# Multi-File Example for Fission .NET 8 + +This example demonstrates how to organize a Fission function across multiple files using proper .NET project structure with services, models, and separation of concerns. + +## Project Structure (MVC Pattern) + +``` +MultiFileExample/ +├── MyFunction.cs # Thin Fission entry point (delegates to controller) +├── MultiFileExample.csproj # Project file +├── Controllers/ # MVC Controllers +│ └── ApiController.cs # Main controller handling routing and orchestration +├── Services/ # Business logic layer +│ ├── WeatherService.cs # Weather-related operations +│ ├── UserService.cs # User management +│ └── DataProcessor.cs # Data processing utilities +└── Models/ # Data models + ├── User.cs # User models + ├── Weather.cs # Weather models + └── DataModels.cs # Data processing models +``` + +## Architecture Benefits + +1. **MVC Pattern** - Clean separation between entry point, controller, and services +2. **Thin Entry Point** - MyFunction only handles Fission integration +3. **Controller-based Routing** - All routing logic centralized in ApiController +4. **Separation of Concerns** - Each service handles specific functionality +5. **Reusability** - Services can be used by multiple endpoints +6. **Testability** - Controller and services can be unit tested independently +7. **Maintainability** - Easy to find and modify specific features +8. **Type Safety** - Strongly typed models ensure data consistency + +## Deploy to Fission + +```bash +# Create environment (if not already created) +fission env create --name dotnet8 \ + --image davidchase03/dotnet8-env:v17.0 \ + --builder davidchase03/dotnet8-builder:v16.0 \ + --poolsize 1 + +# Create package with all source files +cd /Users/davidcasa/environments/dotnet8/examples/MultiFileExample +fission pkg create --name multifile-pkg \ + --env dotnet8 \ + --src . \ + --buildcmd "/usr/local/bin/build" + +# Wait for build to complete +fission pkg list + +# Create function +fission fn create --name multifile-fn \ + --pkg multifile-pkg \ + --env dotnet8 \ + --entrypoint "MultiFileExample.MyFunction" + +# Create HTTP route +fission route create --name multifile-route \ + --function multifile-fn \ + --url "/multifile/*" \ + --method GET POST + +# Test the function +fission fn test --name multifile-fn +``` + +## Available Endpoints + +```bash +# Port forward to access locally +kubectl port-forward -n fission service/router 8080:80 & + +# API Information +curl http://localhost:8080/fission-function/multifile-fn/ + +# Weather Service endpoints +curl http://localhost:8080/fission-function/multifile-fn/weather +curl http://localhost:8080/fission-function/multifile-fn/weather/forecast + +# User Service endpoints +curl http://localhost:8080/fission-function/multifile-fn/users +curl http://localhost:8080/fission-function/multifile-fn/users/active + +# Data Processing endpoint +curl http://localhost:8080/fission-function/multifile-fn/process + +# Health check +curl http://localhost:8080/fission-function/multifile-fn/health +``` + +## How It Works + +1. **Entry Point**: `MyFunction.cs` implements the Fission function interface (thin wrapper) +2. **Controller**: `ApiController.cs` handles all routing and orchestration +3. **Dependency Injection**: Services are instantiated in the controller's constructor +4. **Routing**: Controller routes requests to appropriate services based on path +5. **Services**: Each service encapsulates related business logic +6. **Models**: Shared data structures used across services + +## Local Development + +```bash +# Copy Fission.DotNet.Common.dll locally for development +cp /Users/davidcasa/environments/dotnet8/Fission.DotNet.Common/bin/Release/net8.0/Fission.DotNet.Common.dll ./ + +# Build locally +dotnet build + +# Run tests (if you add them) +dotnet test +``` + +## Adding New Features + +To add a new service: + +1. Create a new service file in `Services/` directory +2. Create models in `Models/` directory +3. Add routing in `MyFunction.cs` +4. Rebuild and redeploy the package + +Example: +```csharp +// Services/OrderService.cs +namespace MultiFileExample.Services +{ + public class OrderService + { + public object GetOrders() { /* ... */ } + } +} + +// In MyFunction.cs +private readonly OrderService _orderService = new OrderService(); +// Add to switch: "orders" => _orderService.GetOrders() +``` + +## Performance Considerations + +- Services are instantiated once per function invocation +- Consider using static methods for stateless operations +- For production, implement proper dependency injection +- Add caching for frequently accessed data + +## Production Enhancements + +For production use, consider: +- Implementing ILogger for structured logging +- Adding OpenAPI/Swagger documentation +- Implementing proper error handling and validation +- Using dependency injection container +- Adding health checks and metrics +- Implementing data access layer with repository pattern \ No newline at end of file diff --git a/dotnet8/examples/MultiFileExample/Services/DataProcessor.cs b/dotnet8/examples/MultiFileExample/Services/DataProcessor.cs new file mode 100644 index 00000000..fbadc8c8 --- /dev/null +++ b/dotnet8/examples/MultiFileExample/Services/DataProcessor.cs @@ -0,0 +1,81 @@ +using System; +using System.Linq; +using System.Text; +using MultiFileExample.Models; + +namespace MultiFileExample.Services +{ + public class DataProcessor + { + public DataResponse ProcessData(DataRequest request) + { + if (request == null || string.IsNullOrEmpty(request.Input)) + { + return new DataResponse + { + Success = false, + Error = "Input data is required", + ProcessedAt = DateTime.UtcNow + }; + } + + // Simulate various data processing operations + var processed = new ProcessedData + { + Original = request.Input, + Uppercase = request.Input.ToUpper(), + Lowercase = request.Input.ToLower(), + Reversed = new string(request.Input.Reverse().ToArray()), + Length = request.Input.Length, + WordCount = request.Input.Split(' ', StringSplitOptions.RemoveEmptyEntries).Length, + Hash = ComputeSimpleHash(request.Input), + Base64Encoded = Convert.ToBase64String(Encoding.UTF8.GetBytes(request.Input)) + }; + + // Calculate some statistics + var stats = new ProcessingStats + { + CharacterCount = request.Input.Length, + AlphaCount = request.Input.Count(char.IsLetter), + DigitCount = request.Input.Count(char.IsDigit), + SpaceCount = request.Input.Count(char.IsWhiteSpace), + SpecialCharCount = request.Input.Count(c => !char.IsLetterOrDigit(c) && !char.IsWhiteSpace(c)) + }; + + return new DataResponse + { + Success = true, + ProcessedData = processed, + Statistics = stats, + ProcessedAt = DateTime.UtcNow, + ProcessingTime = "< 1ms", + Source = "DataProcessor.cs" + }; + } + + private string ComputeSimpleHash(string input) + { + // Simple hash for demonstration + int hash = 0; + foreach (char c in input) + { + hash = ((hash << 5) - hash) + c; + } + return Math.Abs(hash).ToString("X8"); + } + + public object AnalyzeData(string data) + { + if (string.IsNullOrEmpty(data)) + return new { error = "No data provided" }; + + return new + { + analysis = "Complete", + dataPoints = data.Length, + checksum = ComputeSimpleHash(data), + timestamp = DateTime.UtcNow + }; + } + } +} \ No newline at end of file diff --git a/dotnet8/examples/MultiFileExample/Services/UserService.cs b/dotnet8/examples/MultiFileExample/Services/UserService.cs new file mode 100644 index 00000000..255396b5 --- /dev/null +++ b/dotnet8/examples/MultiFileExample/Services/UserService.cs @@ -0,0 +1,65 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using MultiFileExample.Models; + +namespace MultiFileExample.Services +{ + public class UserService + { + private readonly List _users; + + public UserService() + { + // Initialize with sample data + _users = new List + { + new User { Id = 1, Name = "Alice Johnson", Email = "alice@example.com", IsActive = true, CreatedAt = DateTime.UtcNow.AddDays(-30) }, + new User { Id = 2, Name = "Bob Smith", Email = "bob@example.com", IsActive = true, CreatedAt = DateTime.UtcNow.AddDays(-25) }, + new User { Id = 3, Name = "Charlie Brown", Email = "charlie@example.com", IsActive = false, CreatedAt = DateTime.UtcNow.AddDays(-20) }, + new User { Id = 4, Name = "Diana Prince", Email = "diana@example.com", IsActive = true, CreatedAt = DateTime.UtcNow.AddDays(-15) }, + new User { Id = 5, Name = "Edward Norton", Email = "edward@example.com", IsActive = false, CreatedAt = DateTime.UtcNow.AddDays(-10) } + }; + } + + public UserListResponse GetAllUsers() + { + return new UserListResponse + { + Users = _users, + Total = _users.Count, + Timestamp = DateTime.UtcNow, + Source = "UserService.cs" + }; + } + + public UserListResponse GetActiveUsers() + { + var activeUsers = _users.Where(u => u.IsActive).ToList(); + + return new UserListResponse + { + Users = activeUsers, + Total = activeUsers.Count, + FilteredBy = "IsActive = true", + Timestamp = DateTime.UtcNow, + Source = "UserService.cs" + }; + } + + public User GetUserById(int id) + { + return _users.FirstOrDefault(u => u.Id == id); + } + + public bool AddUser(User user) + { + if (user == null || _users.Any(u => u.Id == user.Id)) + return false; + + user.CreatedAt = DateTime.UtcNow; + _users.Add(user); + return true; + } + } +} \ No newline at end of file diff --git a/dotnet8/examples/MultiFileExample/Services/WeatherService.cs b/dotnet8/examples/MultiFileExample/Services/WeatherService.cs new file mode 100644 index 00000000..efc350c5 --- /dev/null +++ b/dotnet8/examples/MultiFileExample/Services/WeatherService.cs @@ -0,0 +1,64 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using MultiFileExample.Models; + +namespace MultiFileExample.Services +{ + public class WeatherService + { + private readonly Random _random = new Random(); + private readonly string[] _conditions = { "Sunny", "Cloudy", "Rainy", "Stormy", "Snowy", "Foggy", "Clear" }; + private readonly string[] _cities = { "New York", "London", "Tokyo", "Paris", "Sydney", "Toronto", "Berlin" }; + + public async Task GetWeatherAsync() + { + // Simulate async operation + await Task.Delay(10); + + var city = _cities[_random.Next(_cities.Length)]; + + return new WeatherResponse + { + City = city, + Temperature = _random.Next(-10, 35), + Condition = _conditions[_random.Next(_conditions.Length)], + Humidity = _random.Next(30, 95), + WindSpeed = _random.Next(0, 50), + Timestamp = DateTime.UtcNow, + Source = "WeatherService.cs" + }; + } + + public async Task GetForecastAsync() + { + // Simulate async operation + await Task.Delay(10); + + var city = _cities[_random.Next(_cities.Length)]; + var forecasts = new List(); + + for (int i = 0; i < 5; i++) + { + forecasts.Add(new DailyForecast + { + Date = DateTime.UtcNow.AddDays(i), + High = _random.Next(10, 35), + Low = _random.Next(-5, 15), + Condition = _conditions[_random.Next(_conditions.Length)], + ChanceOfRain = _random.Next(0, 100) + }); + } + + return new ForecastResponse + { + City = city, + Days = 5, + Forecasts = forecasts, + GeneratedAt = DateTime.UtcNow, + Source = "WeatherService.cs" + }; + } + } +} \ No newline at end of file diff --git a/dotnet8/examples/MultiFileExample/multifile-test.zip b/dotnet8/examples/MultiFileExample/multifile-test.zip new file mode 100644 index 0000000000000000000000000000000000000000..0ed42324b9b61ffd06097c6bd69e7bebecd97251 GIT binary patch literal 6710 zcmai&by$=8`^QH!x*4c|!VqccZbnIilyrxTR6sf%AkuQ^4(Udb7Eq+SJEk;9C?LO~ zo+DrJoZq$U*|q(#*Ynx?zMnhZDhep5Bme*a6X2&$qUUCqibIJ607xMN0OWw{07X|j z7i);Mow>A|@k0kYb5>Jl2PdeFrUnpzBC(lcC555dqWUjjx2H}p2{Ms%_=LYvat?dDQezd)*I z(VvLbu-HP>Sg9(CXgd5l9alEzGbJWZeC?Y(Txe!BmU5`Ey>r0%@!v4(K39Z5*OglV zA{9C<=bvD+I$?Uz{xK|1d(u@|;)3^>pX6oC*X}R7VBU`qhv5yw8;Mv?MIQ-ztN1;! zas;_~S@iK8P#T?1o@)!6hHe|%6SbN`ka_{*MCvjjed}hY7<1u5_y)I1$p(ri&fUI` z4dcvx$1}lmjEiH0qALGLt!qG}x5P0Fz(t5gj*uGnLTY!2tG%gSHm67~-1(@dR3{!`CVCfB1ecaJ7B=*06yidgzKHa1X8 z6>)P84fJA4nW0hpyi;7GB)4$ppKMbkdHT38;P2ATDV(oB;q(OSYQ!ai6IFrb6J?cB z@LMvViF9aAD@Gg{D}D^aTL0?i1;K|M2q-<3dxYnYo=a+jliNg|n>W5!Y{O{tC(k zK=VVujyNJHNN4~6$uEGEv5WB!*59$Ypb>3dN&dLl4@xL^aiov-NxvZ9f2ydJgYP$t zHM~&%h;x)njUHIuCaHOzl8r`cB|vOJXdSzB+%cv^W3H>}Ltx7Lk`d)bGz!dwf~)Y= zAm5XaUh6m7ljz)7f~xPU$>K_xVBs)%0%hOK3DJnMo1|4tQ<7`jNrwp$#m3K2 zIN5t)pui_}SyIIc(LtQ>5&=z*?P950cG1fQ#g7fDyL?%rPvF-*fUz--@@yMZqHxm2 z%;%ve#^&10{Z(%$R#S_i^gIb1O!mI$y{E{fOV3JJEg>Bu?+Z3}X@kdb?zziEuf9hY zl7$%32<@6~@~FI1${Kp`Zq%A5R3{e8CEA|$l^^mi=i5wT#n8rkBQ7MnJ9oZxM5EKzX-8dLUYSHUWhM2WuC zVlZ+iUVJy1{H&LpjAU&PUD;O}=3~0^uMcG;w7N3E!2+ba^a>nM7Awd$gF6xik}G4N zI=4y|*E{{H@M~2&rv;xnBr5jNnPHWYXEuvT(k#`QQhTVOOqd~!72D|K^VjOdVQmf` z!P8xp%zo-}l5p<;xHeUWJq<$y(&~b();11aC3CI!GwwvqTT1YI1Aqi~E-f6HB5^#w zI$qnj<<`#hyxjG-h9S;DH8aNmT|(s(!7dOFlU;L#Zf?!DrP*(maZZ$n+ZaLWpNBt> zwQL_ZJBiriH`o#P$wLi(lZsV0%0*U%R7EaEBAMpPcW zU#e<4|C00?Le3jkC4Cm9`d!jFFRTH?eO|0}s?jY#32mrYc>wr%e006m)}ac- zY6-(wFAaRw6s987d`K7SwcgcJphItgBY#JJKZ1}mX!I5QNo)&7prUaOugQZOUZU(g zNy9h81O>~uV};dyn+;GK(e@7xc^}8*<(F;E<6sPvWe4wy(k8BJr;WOk)qN7)^>Ruh zYSSg7-(C|R&?>E?wQcCOnzGLzK85E>`$WQDd@-k5tU`)&dkNn^@SCR2ovc>)ME&lB zvsFaquIZ6e;+=AI)sr5bmD+-3_LW(9$E8}r^WcAL2@XOfNvOSx6V%Sm-05dQ8I#Gf z+CW_YT2L-1Q<6IG3Qz%nd;$Q1@xK&t2kT$Zzmxfb`iH_&tu?2-#7E%0Rtj1%SGSfM zu-=B57KR3)&G-|<)?)-lCe;ngn5^05<>(&xH0?gGa*Wwo-}(x0<(M1i-0wNI%>nrY zCdCa_@l6*}7MgLqh&4N=`HSEn`%^8sX34~z41IUC@ z)kBOH$g)l&1f@1hhiTWg-nMC1XV*AUyr+n1xS*?%XfV{o^wMR+-Be8~jXQhG$1ssh zN&?ohz^{~AO;^y^s`(%bPp<=Q#5w9=58K9Tj50mKCo28OvamRDDiCEiu&*qd9UN*w zQzxwK8>^u;D6CAs7^0o{IzX2rB{_DS4l|;$_3_InsWngf#g;Xm zx9ZJ_(O_eb7o9$weCI|a7QC{=FLEd+LO#NjytBT9HM(*V30FTJymR4}@bB%h|L^;*w z2{R{6Qxrgw_zXj{hVVjP73=zqwW;j@t%S{_Og~wBa}jy^Xwn4Xf+giPWfah}GK|)e z2TkKyd31ewbY2{e1vWNqxAw{z8OD6g(AAahJuo!OGTSH~c{eK?=y)Kg-yU^DhC2_( ztSW+Ell&Y}>%GlWvUS(M(G;7aiTD0?1I#TB>^<4={Py|diJ6Rh^!6VHBOozPw{ORg zJFA9JPr8~l&v3N#Zvcnu->UA&6F}M$-W!p2H%76~bK``U_GUdAved|lj$3_QQzsg6 zRQ@e-(Ox8ly|AvK7pXMp6$?xq^JL2)%I(1Drl2RA!=8In6CK8T@ep9t$7Jhn3Gf;2 z9EUZAwKE+5WAF6BglFWSlPefhJG5uw`iWLfZ6shEVoBgf^9kclsi;2S@eyBUmddy` zvlQ;`VjAk+TcH5gBdVadvYs2^oZIct(VdSZrOxAfW94WzwS&v=H;j(!Xqa!SlQbXf z9}AN=$B!?h>_aDti>TEp9#<9%U}f?+-JOivrOwK?IIa}SLg8v19t4)e=%pBhnoseO zqRsv_4Bp$cv$tN7oquyPxR7q!SU>;S9AG@I6DeCU8h@ld=f-I)KKL!h2jv_Wj8Yxo zgM=jFqjk)QFz9}1061Q*a~y27w=%ELX=YR1>@!HoN5;Xm`OgYUgy=BLSG&xwZncR(d`nI^_CR#1TFf6S$u9(=U;X^@ckw(E`eX6z z(Nus^-X&<dK)8TFz&y>Tjg8kT9_!j#4j6*sr-_8hEx?Vn1d#D3=+O?Oa|zoI*`Wdvz@A$va{ zX#c@6axOd>-4BSt*1zQKSd8Vx7IDtVD=Zh-=kc=W{tBauDLowph(csRw@kKkcDzzC z?F9vk3w;Y?RRam0`_PxaBF-s8L2XfCUp=UJ__xb~aA#-Ulr~p*L=U?{UgmVNKW)Hs zmoOD>#7tSAs)SFQxlF2CWQ$o*2(wv{6ytn5+V zWom8;729(v>5EtfOB!C&5>if}WTn-7md#kEJ6hZHf!8+DW+e%V(O5tulx{08H*{#E zO6dYqohn`(OUthrcrB$>{kn8kMUcMs(cBi%%B}S1q@Iz6k@7#Yih3Wml`dMcR!NP3O)C#A!d*>N~+pqr+#`idve;4 zMz0>rrjpErYh4<5Be^xh2g3N{aPeZ>yfT5hLUdo>1`jf62f z)>GsLF^o#&wnv0Ln+k6?QB6dNr<(jmRbU(#?O-GHjd8ilfa7j2ru}2*@~)tnX%o4) ztZrgF_ly{S+q^T(Xw@yNIkzwulQ?I8yYjE*N|w1)^h$M7TImWC&AMM&s7~W;>wEiD z?+t}du!o+!rKGF*_;P#!H+Rx8h}MYjNKn;bK1#n&`xfyddw4eYzc8H%11JzP9rv0{r@e?WRKLxC?)88@!&>C)aSq^Ur{vtMYbMWYIJ#oL!$Vn_Kqy7IqvUJ_Fft!Reh3wWPY(qG30qB7}!Y)#n_wU!L?X4JpGV#}E!e7$(&-6;nNZk z9%O!`Kh$0^`{1@rO9^b8%OYY}^!5^mHXZPonr`QR+w+$WFQRpbKld2SD`{iyw>-awdx&}5#I>2ow2l$WRYYaQ6UhlEE28U%0`8m4Ge14Hp^(mK+X zU!ze`MwFd7Qx-BSnc41vMo)(uCS@}2XrNPwj37UpPmDjLAe+ubxlv~c>kwwkFOAhe zwXT0^bnk<2KdX6$!}Ot^W?ZY|3$>c^1=(!S49NRV8YLMtgnK}*@bFa=zGM^`klzoe zE@BPtTP^)TOQ-?U4>8eVx_|;6N1SR|cw;5IVb<4FlyjHKt*1U>>P>XL zvZAC?CvEcCv`p4*9e011k6II%lN-+D0C@7r9+HPKpv={&9^kQ5K;;0L zvkT1&LWh!5!E?qw*RUszunz^qS+z}U%LYhYyqiOGAt)=z*lw7Dg3dE0ch&4>XOF6e zSM6DJ3si&hVn!43GFBDe$DAZ920EWk%=eBNjJ3kt$t;q-5PHg0=RL+ZewlIC` z`lQ-se@yz!2~5AGQP;N0-ySDXXIf*S8^4*#OjFY{pPJj3ARr5=DQO#%VREEpebTY^ zmEfkJnGkv>?|k3`{hC)*UW`!jd4q_Ec|FXuWUj#H&04od_Ozr^uq_tzpX8-rO!*bg z4t?|L$2)Un+NRvw!goT^r{o**f!kAPwrtc*xzz6%fRk0nWgTGiY`&Erz3;4Xy57nU zs^PsJ&O&O&(XqLTfD{pJP!|KzJi z)@AZ@ggNk&eA(7QU@vT~|2XhfC+9MD1z}+R{qGTT^7jE5!MyNw{>c2H=vCk2vZB}+ zfZtpY1o6VJ_#^R$tXDmd%coTRjd;}&`L8?t+nRCt%qlp5%S#6WchPG8`v* z`5)YG3kU*!v4H#s{OS;R8E$}o>8yVk2@(8@k?=qGR~zqTzC6(-{?#`8Z`z=tUp)Fa Oh}TU-uAY-#eEUD1R>D;P literal 0 HcmV?d00001 From 0b2361ee93b199b867c13f846d5973073ab3b2b1 Mon Sep 17 00:00:00 2001 From: David Chase Date: Wed, 20 Aug 2025 13:16:09 -0400 Subject: [PATCH 2/6] feat: add env related items for dotnet 8 --- .github/workflows/filters/filters.yaml | 2 ++ .github/workflows/filters/version_filter.yaml | 2 ++ Makefile | 1 + environments.json | 29 +++++++++++++++++++ skaffold.yaml | 7 +++++ 5 files changed, 41 insertions(+) diff --git a/.github/workflows/filters/filters.yaml b/.github/workflows/filters/filters.yaml index f0698f8b..a73ed8f3 100644 --- a/.github/workflows/filters/filters.yaml +++ b/.github/workflows/filters/filters.yaml @@ -8,6 +8,8 @@ dotnet: - 'dotnet/**' dotnet20: - 'dotnet20/**' +dotnet8: + - 'dotnet8/**' go: - 'go/**' jvm: diff --git a/.github/workflows/filters/version_filter.yaml b/.github/workflows/filters/version_filter.yaml index 5a94c4d7..fe0a9678 100644 --- a/.github/workflows/filters/version_filter.yaml +++ b/.github/workflows/filters/version_filter.yaml @@ -8,6 +8,8 @@ dotnet: - 'dotnet/envconfig.json' dotnet20: - 'dotnet20/envconfig.json' +dotnet8: + - 'dotnet8/envconfig.json' go: - 'go/envconfig.json' jvm: diff --git a/Makefile b/Makefile index bcfa381a..d8043365 100644 --- a/Makefile +++ b/Makefile @@ -26,6 +26,7 @@ FISSION_ENVS := nodejs-envs \ binary-envs \ tensorflow-serving-envs \ dotnet20-envs \ + dotnet8-envs \ ruby-envs all: $(FISSION_ENVS) diff --git a/environments.json b/environments.json index 42d762ea..ad67ffaa 100644 --- a/environments.json +++ b/environments.json @@ -253,6 +253,35 @@ "status": "Stable", "version": "1.31.1" } +],[ + { + "builder": "dotnet8-builder", + "examples": "https://github.com/fission/environments/tree/master/dotnet8/examples", + "icon": "./logo/logo_NETcore.svg", + "image": "dotnet8-env", + "keywords": [], + "kind": "environment", + "maintainers": [ + { + "link": "https://github.com/life1347", + "name": "life1347" + }, + { + "link": "https://github.com/soamvasani", + "name": "soamvasani" + }, + { + "link": "https://github.com/vishal-biyani", + "name": "vishal-biyani" + } + ], + "name": "Dotnet 8 Environment", + "readme": "https://github.com/fission/environments/tree/master/dotnet8", + "runtimeVersion": "8.0.18", + "shortDescription": "Fission Dotnet 8.0.18 runtime w/ .NET SDK 8.0.412 and ASP.NET Core 8.0.18", + "status": "Stable", + "version": "1.0.0" + } ] , [ diff --git a/skaffold.yaml b/skaffold.yaml index 3c58f2cc..ae02bb61 100644 --- a/skaffold.yaml +++ b/skaffold.yaml @@ -40,6 +40,13 @@ profiles: context: dotnet20/ docker: dockerfile: Dockerfile +- name: dotnet8 + build: + artifacts: + - image: dotnet8-env + context: dotnet8/ + docker: + dockerfile: Dockerfile - name: go build: artifacts: From af44bce1032cf50604dd8614c7a47b334b7f44c4 Mon Sep 17 00:00:00 2001 From: David Chase Date: Wed, 20 Aug 2025 13:23:05 -0400 Subject: [PATCH 3/6] fix: update README --- dotnet8/examples/MultiFileExample/README.md | 49 +++------------------ 1 file changed, 5 insertions(+), 44 deletions(-) diff --git a/dotnet8/examples/MultiFileExample/README.md b/dotnet8/examples/MultiFileExample/README.md index 5103ba0d..a04206b8 100644 --- a/dotnet8/examples/MultiFileExample/README.md +++ b/dotnet8/examples/MultiFileExample/README.md @@ -2,7 +2,7 @@ This example demonstrates how to organize a Fission function across multiple files using proper .NET project structure with services, models, and separation of concerns. -## Project Structure (MVC Pattern) +## Project Structure ``` MultiFileExample/ @@ -20,28 +20,18 @@ MultiFileExample/ └── DataModels.cs # Data processing models ``` -## Architecture Benefits - -1. **MVC Pattern** - Clean separation between entry point, controller, and services -2. **Thin Entry Point** - MyFunction only handles Fission integration -3. **Controller-based Routing** - All routing logic centralized in ApiController -4. **Separation of Concerns** - Each service handles specific functionality -5. **Reusability** - Services can be used by multiple endpoints -6. **Testability** - Controller and services can be unit tested independently -7. **Maintainability** - Easy to find and modify specific features -8. **Type Safety** - Strongly typed models ensure data consistency ## Deploy to Fission ```bash # Create environment (if not already created) fission env create --name dotnet8 \ - --image davidchase03/dotnet8-env:v17.0 \ - --builder davidchase03/dotnet8-builder:v16.0 \ + --image fission/dotnet8-env \ + --builder fission/dotnet8-builder \ --poolsize 1 # Create package with all source files -cd /Users/davidcasa/environments/dotnet8/examples/MultiFileExample +cd /dotnet8/examples/MultiFileExample fission pkg create --name multifile-pkg \ --env dotnet8 \ --src . \ @@ -99,18 +89,6 @@ curl http://localhost:8080/fission-function/multifile-fn/health 5. **Services**: Each service encapsulates related business logic 6. **Models**: Shared data structures used across services -## Local Development - -```bash -# Copy Fission.DotNet.Common.dll locally for development -cp /Users/davidcasa/environments/dotnet8/Fission.DotNet.Common/bin/Release/net8.0/Fission.DotNet.Common.dll ./ - -# Build locally -dotnet build - -# Run tests (if you add them) -dotnet test -``` ## Adding New Features @@ -135,21 +113,4 @@ namespace MultiFileExample.Services // In MyFunction.cs private readonly OrderService _orderService = new OrderService(); // Add to switch: "orders" => _orderService.GetOrders() -``` - -## Performance Considerations - -- Services are instantiated once per function invocation -- Consider using static methods for stateless operations -- For production, implement proper dependency injection -- Add caching for frequently accessed data - -## Production Enhancements - -For production use, consider: -- Implementing ILogger for structured logging -- Adding OpenAPI/Swagger documentation -- Implementing proper error handling and validation -- Using dependency injection container -- Adding health checks and metrics -- Implementing data access layer with repository pattern \ No newline at end of file +``` \ No newline at end of file From 2f0ad0e00c1e9c35583dac2470886a32ab53306c Mon Sep 17 00:00:00 2001 From: David Chase Date: Thu, 21 Aug 2025 13:50:36 -0400 Subject: [PATCH 4/6] feat: add tests and changes to .gitignore --- .github/workflows/environment.yaml | 21 ++++ Makefile | 4 + dotnet8/.gitignore | 60 +++++++++++ dotnet8/builder/defaultBuildCmd | 9 ++ .../MultiFileExample/multifile-test.zip | Bin 6710 -> 0 bytes dotnet8/tests/test_dotnet8_env.sh | 99 ++++++++++++++++++ skaffold.yaml | 4 + 7 files changed, 197 insertions(+) create mode 100644 dotnet8/.gitignore delete mode 100644 dotnet8/examples/MultiFileExample/multifile-test.zip create mode 100755 dotnet8/tests/test_dotnet8_env.sh diff --git a/.github/workflows/environment.yaml b/.github/workflows/environment.yaml index fbc4ef5a..3e49e806 100644 --- a/.github/workflows/environment.yaml +++ b/.github/workflows/environment.yaml @@ -267,3 +267,24 @@ jobs: with: command: run profile: tensorflow-serving + dotnet8: + runs-on: ubuntu-latest + needs: check + if: contains(needs.check.outputs.packages, 'dotnet8') + steps: + - name: Checkout sources + uses: actions/checkout@v4 + - name: Setup Fission Environment + uses: ./.github/actions/setup-cluster + - name: Fission and Test images + run: | + SKAFFOLD_PROFILE=dotnet8 make skaffold-run + make dotnet8-test-images + make router-port-forward + - name: dotnet8-tests + run: ./test_utils/run_test.sh ./dotnet8/tests/test_dotnet8_env.sh + - name: Collect Fission Dump + uses: ./.github/actions/collect-fission-dump + if: ${{ failure() }} + with: + workflow-name: dotnet8 diff --git a/Makefile b/Makefile index d8043365..4571f810 100644 --- a/Makefile +++ b/Makefile @@ -92,5 +92,9 @@ python-fastapi-test-images: @kind load docker-image python-fastapi-env @kind load docker-image python-fastapi-builder +dotnet8-test-images: + @kind load docker-image dotnet8-env + @kind load docker-image dotnet8-builder + router-port-forward: @kubectl port-forward svc/router 8888:80 -nfission & diff --git a/dotnet8/.gitignore b/dotnet8/.gitignore new file mode 100644 index 00000000..40c6316f --- /dev/null +++ b/dotnet8/.gitignore @@ -0,0 +1,60 @@ +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +[Ww][Ii][Nn]32/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ +[Ll]ogs/ + +# Visual Studio cache/options directory +.vs/ + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ + +# Files built by Visual Studio +*.user +*.userosscache +*.sln.docstates + +# Build results +*.dll +*.exe +*.pdb +*.cache +*.log + +# NuGet Packages +*.nupkg +*.snupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!?*.[Cc]ache/ + +# Example ZIP files (for testing) +examples/**/*.zip + +# macOS +.DS_Store \ No newline at end of file diff --git a/dotnet8/builder/defaultBuildCmd b/dotnet8/builder/defaultBuildCmd index ca34289e..cc20101f 100755 --- a/dotnet8/builder/defaultBuildCmd +++ b/dotnet8/builder/defaultBuildCmd @@ -35,4 +35,13 @@ else # For single .cs files, they will be compiled at runtime fi +echo "Build completed successfully" + +# Ensure Fission.DotNet.Common.dll is in the output directory +cp /app/Fission.DotNet.Common.dll ${DEPLOY_PKG}/ + +# List the build output +echo "Build output:" +ls -la ${DEPLOY_PKG}/Fission.DotNet.Common.dll ${DEPLOY_PKG}/*.dll 2>/dev/null || true + echo "Build complete" \ No newline at end of file diff --git a/dotnet8/examples/MultiFileExample/multifile-test.zip b/dotnet8/examples/MultiFileExample/multifile-test.zip deleted file mode 100644 index 0ed42324b9b61ffd06097c6bd69e7bebecd97251..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6710 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 Oh}TU-uAY-#eEUD1R>D;P diff --git a/dotnet8/tests/test_dotnet8_env.sh b/dotnet8/tests/test_dotnet8_env.sh new file mode 100755 index 00000000..f5de0532 --- /dev/null +++ b/dotnet8/tests/test_dotnet8_env.sh @@ -0,0 +1,99 @@ +#!/bin/bash + +set -euo pipefail +ROOT=$(dirname $0)/../.. +source $ROOT/test_utils/utils.sh + +TEST_ID=$(generate_test_id) +echo "TEST_ID = $TEST_ID" + +tmp_dir="/tmp/test-$TEST_ID" +mkdir -p $tmp_dir + +cleanup() { + log "Cleaning up..." + clean_resource_by_id $TEST_ID + rm -rf $tmp_dir +} + +if [ -z "${TEST_NOCLEANUP:-}" ]; then + trap cleanup EXIT +else + log "TEST_NOCLEANUP is set; not cleaning up test artifacts afterwards." +fi + +env=dotnet8-$TEST_ID +fn_poolmgr=hello-dotnet8-pm-$TEST_ID +fn_nd=hello-dotnet8-nd-$TEST_ID + +cd $ROOT/dotnet8/examples + +DOTNET8_BUILDER_IMAGE=${DOTNET8_BUILDER_IMAGE:-davidchase03/dotnet8-builder:v21.1} +DOTNET8_RUNTIME_IMAGE=${DOTNET8_RUNTIME_IMAGE:-davidchase03/dotnet8-env:v21.0} + +log "Creating environment for .NET 8" +log "DOTNET8_RUNTIME_IMAGE = $DOTNET8_RUNTIME_IMAGE" +log "DOTNET8_BUILDER_IMAGE = $DOTNET8_BUILDER_IMAGE" +fission env create --name $env --image $DOTNET8_RUNTIME_IMAGE --builder $DOTNET8_BUILDER_IMAGE --poolsize 1 + +# Wait for builder to be ready +log "Waiting for builder to be ready (this may take a few minutes)..." +timeout 300s bash -c "wait_for_builder $env" || { + log "Warning: Builder may not be fully ready, continuing anyway..." +} + +# Test 1: Simple HelloWorld with builder +log "===== Test 1: HelloWorld with builder =====" +pushd HelloWorld > /dev/null +zip -qr $tmp_dir/hello.zip MyFunction.cs HelloWorld.csproj -x "bin/*" -x "obj/*" -x "*.dll" +popd > /dev/null + +pkgName=$(generate_test_id) +fission package create --name $pkgName --src $tmp_dir/hello.zip --env $env + +log "Waiting for build to complete..." +timeout 120s bash -c "waitBuild $pkgName" + +log "Creating pool manager & new deployment functions" +fission fn create --name $fn_poolmgr --env $env --pkg $pkgName --executortype poolmgr +fission fn create --name $fn_nd --env $env --pkg $pkgName --executortype newdeploy --minscale 0 --maxscale 1 + +log "Creating routes" +fission route create --name $fn_poolmgr --function $fn_poolmgr --url /$fn_poolmgr --method GET +fission route create --name $fn_nd --function $fn_nd --url /$fn_nd --method GET + +log "Waiting for router to catch up..." +sleep 5 + +log "Testing pool manager function" +timeout 60 bash -c "test_fn $fn_poolmgr 'Hello World'" + +log "Testing new deployment function" +timeout 60 bash -c "test_fn $fn_nd 'Hello World'" + +# Test 2: Multi-file project +log "===== Test 2: Multi-file project with builder =====" +pushd MultiFileExample > /dev/null +zip -qr $tmp_dir/multifile.zip * -x "*.zip" -x "bin/*" -x "obj/*" -x "*.dll" +popd > /dev/null + +pkgName=$(generate_test_id) +fission package create --name $pkgName --src $tmp_dir/multifile.zip --env $env + +log "Waiting for build to complete..." +timeout 120s bash -c "waitBuild $pkgName" + +log "Updating functions with new package" +fission fn update --name $fn_poolmgr --pkg $pkgName +fission fn update --name $fn_nd --pkg $pkgName + +log "Waiting for updates to propagate..." +sleep 5 + +log "Testing pool manager function with multifile package" +timeout 60 bash -c "test_fn $fn_poolmgr 'Multi-File Example API'" + +log "Testing new deployment function with multifile package" +timeout 60 bash -c "test_fn $fn_nd 'Multi-File Example API'" + +log "Test PASSED" \ No newline at end of file diff --git a/skaffold.yaml b/skaffold.yaml index ae02bb61..e5c169dd 100644 --- a/skaffold.yaml +++ b/skaffold.yaml @@ -47,6 +47,10 @@ profiles: context: dotnet8/ docker: dockerfile: Dockerfile + - image: dotnet8-builder + context: dotnet8/ + docker: + dockerfile: builder/Dockerfile - name: go build: artifacts: From fbf0f850480ef7086124ca747a1401cc7cd9ee2c Mon Sep 17 00:00:00 2001 From: David Chase Date: Fri, 22 Aug 2025 14:31:12 -0400 Subject: [PATCH 5/6] fix: correct build context for dotnet8-builder Docker image MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The dotnet8 builder Dockerfile requires files from both the builder/ subdirectory and the parent dotnet8/ directory (Fission.DotNet.Common source files). This fix updates both Makefiles to use the parent dotnet8 directory as the Docker build context, matching the approach used by dotnet8-env-img. Changes: - dotnet8/builder/Makefile: Use parent directory as build context - dotnet8/Makefile: Use parent directory as build context for builder image This resolves the GitHub Actions build failure where files were not found. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- dotnet8/Makefile | 4 ++-- dotnet8/builder/Makefile | 5 ++++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/dotnet8/Makefile b/dotnet8/Makefile index b8c53c79..c44d48b4 100644 --- a/dotnet8/Makefile +++ b/dotnet8/Makefile @@ -11,5 +11,5 @@ dotnet8-env-img: Dockerfile docker build --platform=$(PLATFORMS) -t $(REPO)/dotnet8-env:$(TAG) -f $< . dotnet8-builder-img: builder/Dockerfile - @echo === Building image $(REPO)/dotnet8-builder:$(TAG) using context $(CURDIR)/builder and dockerfile $< - docker build --platform=$(PLATFORMS) -t $(REPO)/dotnet8-builder:$(TAG) -f $< builder/ \ No newline at end of file + @echo === Building image $(REPO)/dotnet8-builder:$(TAG) using context $(CURDIR) and dockerfile $< + docker build --platform=$(PLATFORMS) -t $(REPO)/dotnet8-builder:$(TAG) -f $< . \ No newline at end of file diff --git a/dotnet8/builder/Makefile b/dotnet8/builder/Makefile index 5bb6fbc0..a2a171af 100644 --- a/dotnet8/builder/Makefile +++ b/dotnet8/builder/Makefile @@ -5,4 +5,7 @@ PLATFORMS ?= linux/amd64,linux/arm64 .PHONY: all all: dotnet8-builder-img -dotnet8-builder-img: Dockerfile \ No newline at end of file +# Override the rule to use parent directory as context +dotnet8-builder-img: Dockerfile + @echo === Building image $(REPO)/dotnet8-builder:$(TAG) using parent directory as context + cd .. && docker buildx build --platform=$(PLATFORMS) -t $(REPO)/dotnet8-builder:$(TAG) $(DOCKER_FLAGS) -f builder/Dockerfile . \ No newline at end of file From c1693965ad5f337a748084b240380c2c2ee8f228 Mon Sep 17 00:00:00 2001 From: David Chase Date: Sun, 24 Aug 2025 21:28:23 -0400 Subject: [PATCH 6/6] fix: use docker buildx with push flag for dotnet8-env image The dotnet8-env image was not being pushed to ghcr.io because it used 'docker build' instead of 'docker buildx build' with the DOCKER_FLAGS variable that contains --push during CI/CD. This aligns dotnet8-env with the standard build pattern used by other environments and ensures the image gets published to the registry. --- dotnet8/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dotnet8/Makefile b/dotnet8/Makefile index c44d48b4..f16cc1a0 100644 --- a/dotnet8/Makefile +++ b/dotnet8/Makefile @@ -8,7 +8,7 @@ all: dotnet8-env-img dotnet8-builder-img dotnet8-env-img: Dockerfile @echo === Building image $(REPO)/dotnet8-env:$(TAG) using context $(CURDIR) and dockerfile $< - docker build --platform=$(PLATFORMS) -t $(REPO)/dotnet8-env:$(TAG) -f $< . + docker buildx build --platform=$(PLATFORMS) -t $(REPO)/dotnet8-env:$(TAG) $(DOCKER_FLAGS) -f $< . dotnet8-builder-img: builder/Dockerfile @echo === Building image $(REPO)/dotnet8-builder:$(TAG) using context $(CURDIR) and dockerfile $<