Taritsyn / JavaScriptEngineSwitcher

JavaScript Engine Switcher determines unified interface for access to the basic features of popular JavaScript engines. This library allows you to quickly and easily switch to using of another JavaScript engine.
Apache License 2.0
439 stars 49 forks source link

JavascriptEngineSwitcher.ChakraCore can't find JavaScriptEngineSwitcher.ChakraCore.Native.linux-x64 in Docker container #99

Closed atlidev closed 2 years ago

atlidev commented 2 years ago

I'm using these library versions:

ReactJS.NET: 5.2.12
JavaScriptEngineSwitcher: Chakra 3.16.0
react and react-dom: (bundled)
docker: v20.10.6

Runtime environment:

OS: Windows 10 (64bit)
.NET Framework or .NET Core Version: .NET core 3.1

Steps to reproduce

  1. Install the React.NET QuickStart template

    dotnet new -i React.Template
    dotnet new reactnet-vanilla
  2. Add the following files: Dockerfile

    
    FROM mcr.microsoft.com/dotnet/core/sdk:3.1-alpine AS base

ARG BUILDCONFIG=RELEASE

RUN apk update && apk add libgdiplus libgdiplus-dev --update-cache --repository http://dl-3.alpinelinux.org/alpine/edge/testing/ --allow-untrusted

copy csproj and restore as distinct layers

COPY ./tutorial-code.csproj ./ReactDotNetProject/ RUN dotnet restore ReactDotNetProject/tutorial-code.csproj

copy everything else and build

COPY ./ ./ReactDotNetProject/ RUN dotnet publish ReactDotNetProject/tutorial-code.csproj --runtime linux-musl-x64 -c $BUILDCONFIG -o out

build runtime image

FROM mcr.microsoft.com/dotnet/core/runtime-deps:3.1-alpine

RUN apk update && apk add terminus-font libgdiplus libgdiplus-dev --update-cache --repository http://dl-3.alpinelinux.org/alpine/edge/testing/ --allow-untrusted ENV ASPNETCORE_URL=http//+:5000 ENV ASPNETCORE_ENVIRONMENT Production

Expose port 5000 on the container

COPY . /app WORKDIR /app

COPY --from=base /out ./

EXPOSE 5000 ENTRYPOINT ["./tutorial-code"]


**docker-compose.yml**

reactdotnet: build: . ports:

namespace ReactDemo { public class Program { public static void Main(string[] args) { CreateHostBuilder(args) .Build() .Run(); }

    public static IHostBuilder CreateHostBuilder(string[] args) =>
        Host.CreateDefaultBuilder(args)
            .ConfigureWebHostDefaults(webBuilder =>
            {
                webBuilder.UseStartup<Startup>().UseUrls(new[] { "http://0.0.0.0:5000" });  // used for docker connection, will be on localhost on own machine
            });
}

}

4. Run docker-compose up --build

5. Visit localhost:5000 in browser.

Expected Error:

reactdotnet_1 | warn: Microsoft.AspNetCore.DataProtection.Repositories.FileSystemXmlRepository[60] reactdotnet_1 | Storing keys in a directory '/root/.aspnet/DataProtection-Keys' that may not be persisted outside of the container. Protected data will be unavailable when container is destroyed. reactdotnet_1 | info: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[0] reactdotnet_1 | User profile is available. Using '/root/.aspnet/DataProtection-Keys' as key repository; keys will not be encrypted at rest. reactdotnet_1 | info: Microsoft.Hosting.Lifetime[0] reactdotnet_1 | Now listening on: http://0.0.0.0:5000 reactdotnet_1 | info: Microsoft.Hosting.Lifetime[0] reactdotnet_1 | Application started. Press Ctrl+C to shut down. reactdotnet_1 | info: Microsoft.Hosting.Lifetime[0] reactdotnet_1 | Hosting environment: Production reactdotnet_1 | info: Microsoft.Hosting.Lifetime[0] reactdotnet_1 | Content root path: /app reactdotnet_1 | info: Microsoft.AspNetCore.Hosting.Diagnostics[1] reactdotnet_1 | Request starting HTTP/1.1 GET http://localhost:5000/ reactdotnet_1 | info: Microsoft.AspNetCore.Routing.EndpointMiddleware[0] reactdotnet_1 | Executing endpoint 'ReactDemo.Controllers.HomeController.Index (tutorial-code)' reactdotnet_1 | info: Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker[3] reactdotnet_1 | Route matched with {action = "Index", controller = "Home"}. Executing controller action with signature Microsoft.AspNetCore.Mvc.ActionResult Index() on controller ReactDemo.Controllers.HomeController (tutorial-code). reactdotnet_1 | info: Microsoft.AspNetCore.Mvc.ViewFeatures.ViewResultExecutor[1] reactdotnet_1 | Executing ViewResult, running view Index. reactdotnet_1 | info: Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker[2] reactdotnet_1 | Executed action ReactDemo.Controllers.HomeController.Index (tutorial-code) in 121.9638ms reactdotnet_1 | info: Microsoft.AspNetCore.Routing.EndpointMiddleware[1] reactdotnet_1 | Executed endpoint 'ReactDemo.Controllers.HomeController.Index (tutorial-code)' reactdotnet_1 | fail: Microsoft.AspNetCore.Diagnostics.ExceptionHandlerMiddleware[1] reactdotnet_1 | An unhandled exception has occurred while executing the request. reactdotnet_1 | React.Exceptions.ReactNotInitialisedException: ReactJS.NET has not been initialised correctly. Please ensure you have called services.AddReact() and app.UseReact() in your Startup.cs file. reactdotnet_1 | ---> React.TinyIoC.TinyIoCResolutionException: Unable to resolve type: React.ReactEnvironment reactdotnet_1 | ---> React.TinyIoC.TinyIoCResolutionException: Unable to resolve type: React.JavaScriptEngineFactory reactdotnet_1 | ---> JavaScriptEngineSwitcher.Core.JsEngineLoadException: Failed to create instance of the ChakraCoreJsEngine. Most likely it happened, because the 'libChakraCore.so' assembly or one of its dependencies was not found. Try to install the JavaScriptEngineSwitcher.ChakraCore.Native.linux-x64 package via NuGet. reactdotnet_1 | reactdotnet_1 | Engine name: ChakraCoreJsEngine reactdotnet_1 | Engine version: 1.11.13 reactdotnet_1 | Category: Engine load error reactdotnet_1 | Description: Most likely it happened, because the 'libChakraCore.so' assembly or one of its dependencies was not found. Try to install the JavaScriptEngineSwitcher.ChakraCore.Native.linux-x64 package via NuGet. ---> System.DllNotFoundException: Unable to load shared library 'ChakraCore' or one of its dependencies. In order to help diagnose loading problems, consider setting the LD_DEBUG environment variable: Error loading shared library libChakraCore: No such file or directory reactdotnet_1 | at JavaScriptEngineSwitcher.ChakraCore.JsRt.NativeMethods.JsCreateRuntime(JsRuntimeAttributes attributes, JsThreadServiceCallback threadService, JsRuntime& runtime) reactdotnet_1 | at JavaScriptEngineSwitcher.ChakraCore.JsRt.JsRuntime.Create(JsRuntimeAttributes attributes, JsThreadServiceCallback threadServiceCallback) reactdotnet_1 | at JavaScriptEngineSwitcher.ChakraCore.ChakraCoreJsEngine.<>cDisplayClass10_1.<.ctor>b__0() reactdotnet_1 | at JavaScriptEngineSwitcher.ChakraCore.ScriptDispatcher.<>cDisplayClass11_0.b0() reactdotnet_1 | at JavaScriptEngineSwitcher.ChakraCore.ScriptDispatcher.StartThread() reactdotnet_1 | --- End of stack trace from previous location where exception was thrown --- reactdotnet_1 | at JavaScriptEngineSwitcher.ChakraCore.ScriptDispatcher.InnnerInvoke(Func1 del) reactdotnet_1 | at JavaScriptEngineSwitcher.ChakraCore.ScriptDispatcher.Invoke(Action action) reactdotnet_1 | at JavaScriptEngineSwitcher.ChakraCore.ChakraCoreJsEngine..ctor(ChakraCoreSettings settings) reactdotnet_1 | at JavaScriptEngineSwitcher.ChakraCore.ChakraCoreJsEngine..ctor(ChakraCoreSettings settings) reactdotnet_1 | at JavaScriptEngineSwitcher.ChakraCore.ChakraCoreJsEngineFactory.CreateEngine() reactdotnet_1 | at JSPool.JsPool2.CreateEngine() reactdotnet_1 | at JSPool.JsPool2.PopulateEngines() reactdotnet_1 | at JSPool.JsPool2..ctor(JsPoolConfig1 config) reactdotnet_1 | at JSPool.JsPool..ctor(JsPoolConfig config) reactdotnet_1 | at React.JavaScriptEngineFactory.CreatePool() reactdotnet_1 | at React.JavaScriptEngineFactory..ctor(IJsEngineSwitcher jsEngineSwitcher, IReactSiteConfiguration config, ICache cache, IFileSystem fileSystem) reactdotnet_1 | at lambda_method(Closure , Object[] ) reactdotnet_1 | at React.TinyIoC.TinyIoCContainer.ConstructType(Type requestedType, Type implementationType, ConstructorInfo constructor, NamedParameterOverloads parameters, ResolveOptions options) reactdotnet_1 | reactdotnet_1 | --- End of inner exception stack trace --- reactdotnet_1 | at React.TinyIoC.TinyIoCContainer.ConstructType(Type requestedType, Type implementationType, ConstructorInfo constructor, NamedParameterOverloads parameters, ResolveOptions options) reactdotnet_1 | at React.TinyIoC.TinyIoCContainer.ConstructType(Type requestedType, Type implementationType, ConstructorInfo constructor, ResolveOptions options) reactdotnet_1 | at React.TinyIoC.TinyIoCContainer.SingletonFactory.GetObject(Type requestedType, TinyIoCContainer container, NamedParameterOverloads parameters, ResolveOptions options) reactdotnet_1 | at React.TinyIoC.TinyIoCContainer.ResolveInternal(TypeRegistration registration, NamedParameterOverloads parameters, ResolveOptions options) reactdotnet_1 | at React.TinyIoC.TinyIoCContainer.ConstructType(Type requestedType, Type implementationType, ConstructorInfo constructor, NamedParameterOverloads parameters, ResolveOptions options) reactdotnet_1 | --- End of inner exception stack trace --- reactdotnet_1 | at React.TinyIoC.TinyIoCContainer.ConstructType(Type requestedType, Type implementationType, ConstructorInfo constructor, NamedParameterOverloads parameters, ResolveOptions options) reactdotnet_1 | at React.TinyIoC.TinyIoCContainer.ConstructType(Type requestedType, Type implementationType, ConstructorInfo constructor, ResolveOptions options) reactdotnet_1 | at React.TinyIoC.TinyIoCContainer.CustomObjectLifetimeFactory.GetObject(Type requestedType, TinyIoCContainer container, NamedParameterOverloads parameters, ResolveOptions options) reactdotnet_1 | at React.TinyIoC.TinyIoCContainer.ResolveInternal(TypeRegistration registration, NamedParameterOverloads parameters, ResolveOptions options) reactdotnet_1 | at React.TinyIoC.TinyIoCContainer.Resolve(Type resolveType) reactdotnet_1 | at React.TinyIoC.TinyIoCContainer.Resolve[ResolveType]() reactdotnet_1 | at React.ReactEnvironment.get_Current() reactdotnet_1 | at React.ReactEnvironment.get_GetCurrentOrThrow() reactdotnet_1 | --- End of inner exception stack trace --- reactdotnet_1 | at React.ReactEnvironment.get_GetCurrentOrThrow() reactdotnet_1 | at React.AspNet.HtmlHelperExtensions.get_Environment() reactdotnet_1 | at React.AspNet.HtmlHelperExtensions.React[T](IHtmlHelper htmlHelper, String componentName, T props, String htmlTag, String containerId, Boolean clientOnly, Boolean serverOnly, String containerClass, Action3 exceptionHandler, IRenderFunctions renderFunctions) reactdotnet_1 | at AspNetCore.Views_Home_Index.b__8_1() in /ReactDotNetProject/Views/Home/Index.cshtml:line 10 reactdotnet_1 | at Microsoft.AspNetCore.Razor.Runtime.TagHelpers.TagHelperExecutionContext.SetOutputContentAsync() reactdotnet_1 | at AspNetCore.Views_Home_Index.ExecuteAsync() in /ReactDotNetProject/Views/Home/Index.cshtml:line 3 reactdotnet_1 | at Microsoft.AspNetCore.Mvc.Razor.RazorView.RenderPageCoreAsync(IRazorPage page, ViewContext context) reactdotnet_1 | at Microsoft.AspNetCore.Mvc.Razor.RazorView.RenderPageAsync(IRazorPage page, ViewContext context, Boolean invokeViewStarts) reactdotnet_1 | at Microsoft.AspNetCore.Mvc.Razor.RazorView.RenderAsync(ViewContext context) reactdotnet_1 | at Microsoft.AspNetCore.Mvc.ViewFeatures.ViewExecutor.ExecuteAsync(ViewContext viewContext, String contentType, Nullable1 statusCode) reactdotnet_1 | at Microsoft.AspNetCore.Mvc.ViewFeatures.ViewExecutor.ExecuteAsync(ActionContext actionContext, IView view, ViewDataDictionary viewData, ITempDataDictionary tempData, String contentType, Nullable1 statusCode) reactdotnet_1 | at Microsoft.AspNetCore.Mvc.ViewFeatures.ViewResultExecutor.ExecuteAsync(ActionContext context, ViewResult result) reactdotnet_1 | at Microsoft.AspNetCore.Mvc.ViewResult.ExecuteResultAsync(ActionContext context) reactdotnet_1 | at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.gAwaited|29_0[TFilter,TFilterAsync](ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted) reactdotnet_1 | at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Rethrow(ResultExecutedContextSealed context) reactdotnet_1 | at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.ResultNext[TFilter,TFilterAsync](State& next, Scope& scope, Object& state, Boolean& isCompleted) reactdotnet_1 | at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.InvokeResultFilters() reactdotnet_1 | --- End of stack trace from previous location where exception was thrown --- reactdotnet_1 | at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.gAwaited|24_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted) reactdotnet_1 | at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Rethrow(ResourceExecutedContextSealed context) reactdotnet_1 | at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted) reactdotnet_1 | at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.InvokeFilterPipelineAsync() reactdotnet_1 | --- End of stack trace from previous location where exception was thrown --- reactdotnet_1 | at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.gLogged|17_1(ResourceInvoker invoker) reactdotnet_1 | at Microsoft.AspNetCore.Routing.EndpointMiddleware.g__AwaitRequestTask|6_0(Endpoint endpoint, Task requestTask, ILogger logger) reactdotnet_1 | at React.AspNet.BabelFileMiddleware.Invoke(HttpContext context) reactdotnet_1 | at Microsoft.AspNetCore.Diagnostics.ExceptionHandlerMiddleware.g__Awaited|6_0(ExceptionHandlerMiddleware middleware, HttpContext context, Task task) reactdotnet_1 | info: Microsoft.AspNetCore.Hosting.Diagnostics[2] reactdotnet_1 | Request finished in 228.19910000000002ms 404 reactdotnet_1 | info: Microsoft.AspNetCore.Hosting.Diagnostics[1] reactdotnet_1 | Request starting HTTP/1.1 GET http://localhost:5000/favicon.ico reactdotnet_1 | info: Microsoft.AspNetCore.Hosting.Diagnostics[2] reactdotnet_1 | Request finished in 6.6058ms 404



This is a surprising error since the tutorial-code.csproj file has the JavascriptEngineSwitcher.ChakraCore.native.linux-x64 package.
When I run dotnet run and visit localhost:5000 (on Windows), the program works as expected.
When I shell into the docker container it also seems to be installing the JavascriptEngineSwitcher.ChakraCore.native.linux-x64 package.

What can be done to fix this? All help is appreciated
Taritsyn commented 2 years ago

Hello, Atli!

Try replacing this line:

RUN dotnet publish ReactDotNetProject/tutorial-code.csproj --runtime linux-musl-x64 -c $BUILDCONFIG -o out

By the following line:

RUN dotnet publish ReactDotNetProject/tutorial-code.csproj --runtime linux-x64 -c $BUILDCONFIG -o out
atlidev commented 2 years ago

@Taritsyn Hi thanks for the quick response. I tried that and now I get the following error

standard_init_linux.go:219: exec user process caused: no such file or directory
Taritsyn commented 2 years ago

I tried that and now I get the following error

standard_init_linux.go:219: exec user process caused: no such file or directory

It looks like I didn't guess right here :-(

You write that you are using the JavaScriptEngineSwitcher.ChakraCore module version 3.16.0, but the log entries say otherwise:

reactdotnet_1  | Engine name: ChakraCoreJsEngine
reactdotnet_1  | Engine version: 1.11.13

From which we can conclude that old version of the JavaScriptEngineSwitcher.ChakraCore (version 3.1.8) module is being used.

It's very similar to the fact that the libChakraCore.so assembly is missing from the container. First of all, you need to understand what exactly is being added to the container.