lambci / docker-lambda

Docker images and test runners that replicate the live AWS Lambda environment
MIT License
5.83k stars 431 forks source link

Unable to locate config file for .NET Core application #109

Open recumbent opened 6 years ago

recumbent commented 6 years ago

I have a .NET lambda function containing the following code:

        this.config = new ConfigurationBuilder()
          .AddJsonFile("appSettings.json", optional: false, reloadOnChange: true)
          .Build();

And a file appSettings.json that is copied to the publish folder.

If I deploy this to AWS then it runs fine (and I can access the values defined in the file) however if I run it with docker-lambda then i will get an error:

Unhandled Exception: AWSLambda.Internal.Bootstrap.LambdaUserCodeException: An exception was thrown when the constructor for type 'SaleCycle.Enrichments.EarlyBirds.Lambda' was invoked. Check inner exception for more details. ---> System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation. ---> System.IO.FileNotFoundException: The configuration file 'appSettings.json' was not found and is not optional. The physical path is '/var/runtime/appSettings.json'.
   at Microsoft.Extensions.Configuration.FileConfigurationProvider.Load(Boolean reload)
   at Microsoft.Extensions.Configuration.ConfigurationRoot..ctor(IList`1 providers)
   at Microsoft.Extensions.Configuration.ConfigurationBuilder.Build()

This error makes sense given that my settings file is in /var/task not /var/runtime - so there is an issue with exactly how the .NET core runner invokes the function.

I have a workround (make it optional and use an environment variable) and am endeavouring to wrap my head around how the .NET stuff works in order to possibly try and fix.

mhart commented 6 years ago

Hmmm, might be something to do with how the current directory is set (or not set more likely) in the .NET mock files.

What does Directory.GetCurrentDirectory() show for you on both AWS and on docker-lambda?

recumbent commented 6 years ago

"/var/task" in both cases - which was not entirely what I was hoping, I much prefer "easy".

There will now be more digging.

mhart commented 6 years ago

Yeah, that's weird. Is the code you're executing in the function body? Or, like, outside it in a static context or something?

recumbent commented 6 years ago

So its not weird, its just me being slow.

The problem is that .NET is going to look for the config file in the same location as the application - in this case the application is the MockBootstraps program which is copied to /var/runtime/ not the lambda assembly that's getting dynamically loaded by the program from /var/task/.

A brief look at the .NET source code for the configuration extensions has yet not shown me anything hugely helpful (in the sense that I rather not change my application code to support the test scenario, that's part of why I want to use the config system in the first place!). That said there is a handy SetBasePath() extension that I could choose to apply only if an environment variable is set - and which therefore should be minimally impactful (its just one more thing to remember next time).

(There are a couple more things I'd like to see to better understand what's going on, but that will depend on opportunity - time mostly.)

AdamLewisGMSL commented 4 years ago

So this looks very similar to an issue I am seeing... The problem I have is that AppDomain.CurrentDomain.BaseDirectory is resolving to /var/runtime when I'm trying to scan for DLLs in my lambda - perhaps these are related?

Everything works perfectly on AWS

mhart commented 4 years ago

Probably related. I'm not sure of the best fix to be honest – the way the old dotnetcore runtimes execute is very specific.

The new dotnetcore3.1 runtime shouldn't exhibit this problem – if it does, let me know

recumbent commented 4 years ago

So this looks very similar to an issue I am seeing... The problem I have is that AppDomain.CurrentDomain.BaseDirectory is resolving to /var/runtime when I'm trying to scan for DLLs in my lambda - perhaps these are related?

Yes, absolutely - it will be the same problem in that your code is being run from a different folder to the root executable which means that where the .DLL scan would normally find the files because they're in the same place when running in docker lambda they're in a different place and it doesn't work. I got round this by setting a flag (env var) for running in this context and changing the behaviour (root path for config in my case) accordingly