Skip to content
This repository has been archived by the owner on Dec 15, 2021. It is now read-only.

Support of Roslyn C# Scripting #758

Closed
wants to merge 6 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions docker/runtime/csharpx/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
FROM microsoft/aspnetcore:2.0.8
WORKDIR /app
COPY ./publish ./
ENTRYPOINT ["dotnet", "Kubeless.CSharpX.dll"]
25 changes: 25 additions & 0 deletions docker/runtime/csharpx/Kubeless.CSharpX.sln
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.27703.2000
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Kubeless.CSharpX", "Kubeless.CSharpX\Kubeless.CSharpX.csproj", "{69B0583F-623B-475D-9B36-5BE9235772F7}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{69B0583F-623B-475D-9B36-5BE9235772F7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{69B0583F-623B-475D-9B36-5BE9235772F7}.Debug|Any CPU.Build.0 = Debug|Any CPU
{69B0583F-623B-475D-9B36-5BE9235772F7}.Release|Any CPU.ActiveCfg = Release|Any CPU
{69B0583F-623B-475D-9B36-5BE9235772F7}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {58289017-92BF-46C4-B2E4-E10DC398FB80}
EndGlobalSection
EndGlobal
30 changes: 30 additions & 0 deletions docker/runtime/csharpx/Kubeless.CSharpX/Context.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace Kubeless.CSharpX
{
public class Context
{
public string ModuleName { get; }
public string FunctionName { get; }
public int FunctionPort { get; }
public int Timeout { get; }
public string Runtime { get; }
public string MemoryLimit { get; }

public Context()
{
ModuleName = Environment.GetEnvironmentVariable( "MOD_NAME" );
FunctionName = Environment.GetEnvironmentVariable( "FUNC_HANDLER" );
FunctionPort = int.TryParse( Environment.GetEnvironmentVariable( "FUNC_PORT" ), out int p ) ? p : 8080;
Timeout = int.TryParse( Environment.GetEnvironmentVariable( "FUNC_TIMEOUT" ), out int t ) ? t : 180;
Runtime = Environment.GetEnvironmentVariable( "FUNC_RUNTIME" );
MemoryLimit = Environment.GetEnvironmentVariable( "FUNC_MEMORY_LIMIT" );
}

public override string ToString()
=> $"{ModuleName} {FunctionName} {FunctionPort} {Timeout} {Runtime} {MemoryLimit}";
}
}
37 changes: 37 additions & 0 deletions docker/runtime/csharpx/Kubeless.CSharpX/Event.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Internal;

namespace Kubeless.CSharpX
{
public class Event
{
public string Data { get; }
public string EventId { get; }
public string EventType { get; }
public string EventTime { get; }
public string EventNamespace { get; }
public Extensions Extensions { get; }

public Event( HttpContext context )
{
context.Request.EnableRewind();
Data = new StreamReader( context.Request.Body ).ReadToEnd();
EventId = context.Request.Headers[ "event-id" ];
EventType = context.Request.Headers[ "event-type" ];
EventTime = context.Request.Headers[ "event-time" ];
EventNamespace = context.Request.Headers[ "event-namespace" ];
Extensions = new Extensions(
context.Request,
context,
context.Response );
}

public override string ToString()
=> $"{Data} {EventId} {EventType} {EventTime} {EventNamespace} {Extensions}";
}
}
22 changes: 22 additions & 0 deletions docker/runtime/csharpx/Kubeless.CSharpX/Extensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;

namespace Kubeless.CSharpX
{
public class Extensions
{
public HttpRequest Request { get; }
Copy link
Contributor

@allantargino allantargino May 19, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Really great you implementation that adds HttpRequest, HttpContext and HttpResponse to Extensions 👍

public HttpContext Context { get; }
public HttpResponse Response { get; }

public Extensions( HttpRequest request, HttpContext context, HttpResponse response )
{
Request = request;
Context = context;
Response = response;
}
}
}
19 changes: 19 additions & 0 deletions docker/runtime/csharpx/Kubeless.CSharpX/Globals.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace Kubeless.CSharpX
{
public sealed class Globals
{
public Event KubelessEvent { get; }
public Context KubelessContext { get; }

public Globals( Event ev, Context ctx )
{
KubelessEvent = ev;
KubelessContext = ctx;
}
}
}
29 changes: 29 additions & 0 deletions docker/runtime/csharpx/Kubeless.CSharpX/Kubeless.CSharpX.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<Project Sdk="Microsoft.NET.Sdk.Web">

<PropertyGroup>
<TargetFramework>netcoreapp2.0</TargetFramework>
<ApplicationIcon />
<OutputType>Exe</OutputType>
<StartupObject />
<Version>2.8.0-1</Version>
<Authors>FROM / Kenji Yamazaki</Authors>
<Company>Kumakami Koubou</Company>
<Product>Roslyn C# Scripting Runtime for Kubeless</Product>
<AssemblyVersion>2.8.0.1</AssemblyVersion>
<FileVersion>2.8.0.1</FileVersion>
</PropertyGroup>

<ItemGroup>
<Folder Include="wwwroot\" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.All" Version="2.0.8" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Scripting" Version="2.8.0" />
</ItemGroup>

<ItemGroup>
<DotNetCliToolReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Tools" Version="2.0.4" />
</ItemGroup>

</Project>
30 changes: 30 additions & 0 deletions docker/runtime/csharpx/Kubeless.CSharpX/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;

namespace Kubeless.CSharpX
{
public class Program
{
public static void Main( string[] args )
{
BuildWebHost( args ).Run();
}

public static IWebHost BuildWebHost( string[] args )
{
var port = int.TryParse( Environment.GetEnvironmentVariable( "FUNC_PORT" ), out int p ) ? p : 8080;

return WebHost.CreateDefaultBuilder( args )
.UseStartup<Startup>()
.UseUrls( $"http://*:{port}" ) // The port used to expose the service can be modified using an environment variable FUNC_PORT
.Build();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
このファイルは、Web プロジェクトの発行 / パッケージ処理で使用されます。この MSBuild ファイルを編集すると、
この処理の動作をカスタマイズできます。詳細については、https://go.microsoft.com/fwlink/?LinkID=208121 を参照してください。
-->
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<WebPublishMethod>FileSystem</WebPublishMethod>
<PublishProvider>FileSystem</PublishProvider>
<LastUsedBuildConfiguration>Release</LastUsedBuildConfiguration>
<LastUsedPlatform>Any CPU</LastUsedPlatform>
<SiteUrlToLaunchAfterPublish />
<LaunchSiteAfterPublish>True</LaunchSiteAfterPublish>
<ExcludeApp_Data>False</ExcludeApp_Data>
<TargetFramework>netcoreapp2.0</TargetFramework>
<ProjectGuid>69b0583f-623b-475d-9b36-5be9235772f7</ProjectGuid>
<SelfContained>false</SelfContained>
<_IsPortable>true</_IsPortable>
<publishUrl>..\publish</publishUrl>
<DeleteExistingFiles>False</DeleteExistingFiles>
</PropertyGroup>
</Project>
108 changes: 108 additions & 0 deletions docker/runtime/csharpx/Kubeless.CSharpX/Startup.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Internal;
using Microsoft.CodeAnalysis.Scripting;
using Microsoft.CodeAnalysis.CSharp.Scripting;

namespace Kubeless.CSharpX
{
public class Startup
{
public Startup( IConfiguration configuration, IHostingEnvironment env )
{
}

public void ConfigureServices( IServiceCollection services )
{
}

public void Configure( IApplicationBuilder app, IHostingEnvironment env )
{
app.Use( next =>
httpContext => {
httpContext.Request.EnableRewind();
return next( httpContext );
} );

app.Map( "/healthz",
builder => builder.Run( this.GetHealthz ) );

app.Run( this.Get );
}

async Task Get( HttpContext httpContext )
{
await Console.Out.WriteLineAsync( $"{DateTime.Now}: Request: {httpContext.Request.Method} {httpContext.Request.Path}" );

var _globals = new Globals( new Event( httpContext ), new Context() );

try
{
// The function to load can be specified using an environment variable MOD_NAME
string modulePath = $"/kubeless/{_globals.KubelessContext.ModuleName}.csx";
string funcCode = await System.IO.File.ReadAllTextAsync( modulePath ); // throw exception if not exist

// The function to load can be specified using an environment variable FUNC_HANDLER.
// Functions should receive two parameters: event and context and should return the value that will be used as HTTP response.
string code =
$"{funcCode}\n" +
$"return {_globals.KubelessContext.FunctionName}( KubelessEvent, KubelessContext );\n";

// Functions should run FUNC_TIMEOUT as maximum.
var timeoutTokenSource = new CancellationTokenSource( TimeSpan.FromSeconds( _globals.KubelessContext.Timeout ) );
var requestAbortToken = httpContext.RequestAborted;
var linkedToken = CancellationTokenSource.CreateLinkedTokenSource( timeoutTokenSource.Token, requestAbortToken ); // Timeout & Aborted

// evaluate function
await Console.Out.WriteLineAsync( $"{DateTime.Now}: Evaluate: MOD_NAME:{_globals.KubelessContext.ModuleName}, FUNC_HANDLER:{_globals.KubelessContext.FunctionName}" );

var scriptResult = await CSharpScript.EvaluateAsync<string>(
Copy link
Contributor

@allantargino allantargino May 19, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is beautiful 👍
Support both .csx and .cs really would open the doors for people who is both developing traditional applications or migrating their scripts/Azure Functions!

code,
ScriptOptions.Default
.WithReferences( new[] {
typeof( Event ).Assembly,
typeof( Context ).Assembly,
} ),
_globals,
typeof( Globals ), linkedToken.Token );

httpContext.Response.StatusCode = 200;
await httpContext.Response.WriteAsync( scriptResult );

await Console.Out.WriteLineAsync( $"{DateTime.Now}: Response: {httpContext.Response.StatusCode}" );
}
catch( OperationCanceledException )
{
httpContext.Response.StatusCode = 408;
await Console.Out.WriteLineAsync( $"{DateTime.Now}: Response: {httpContext.Response.StatusCode} - Timeout" );
}
catch( Exception ex )
{
// Exceptions in the function should be caught. The server should not exit due to a function error.
httpContext.Response.StatusCode = 500;
await Console.Out.WriteLineAsync( $"{DateTime.Now}: Response: {httpContext.Response.StatusCode} - {ex.Message}" );
await httpContext.Response.WriteAsync( $"Internal Server Error: {ex.Message}" );
}
}

async Task GetHealthz( HttpContext httpContext )
{
await Console.Out.WriteLineAsync( $"{DateTime.Now}: Request: {httpContext.Request.Method} {httpContext.Request.Path}" );

httpContext.Response.StatusCode = 200;
await httpContext.Response.WriteAsync( "Ok" );

await Console.Out.WriteLineAsync( $"{DateTime.Now}: Response: {httpContext.Response.StatusCode}" );
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"Logging": {
"IncludeScopes": false,
"LogLevel": {
"Default": "Debug",
"System": "Information",
"Microsoft": "Information"
}
}
}
15 changes: 15 additions & 0 deletions docker/runtime/csharpx/Kubeless.CSharpX/appsettings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"Logging": {
"IncludeScopes": false,
"Debug": {
"LogLevel": {
"Default": "Warning"
}
},
"Console": {
"LogLevel": {
"Default": "Warning"
}
}
}
}
3 changes: 3 additions & 0 deletions docker/runtime/csharpx/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
runtime-image:
docker build -f Dockerfile -t from/kubeless-csharpx:2.8.0-1 .

Binary file not shown.
Loading