Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add annotation to manually specify project command line arguments. (Needed for AWS Lambda support) #6120

Open
1 task done
normj opened this issue Oct 4, 2024 · 22 comments
Labels
area-app-model Issues pertaining to the APIs in Aspire.Hosting, e.g. DistributedApplication untriaged New issue has not been triaged

Comments

@normj
Copy link
Contributor

normj commented Oct 4, 2024

Is there an existing issue for this?

  • I have searched the existing issues

Is your feature request related to a problem? Please describe the problem.

We are looking into how we can support adding a AWS Lambda Function to the DistributedApplicationBuilder. A Lambda function can be written as an executable or as a class library. The class library programming model being the more common approach.

The executable approach is not an issue for Aspire but for the class library we need to customize the execution arguments. Lambda functions coded as a class library are started with a host executable and the depsfile and runtimeconfig are overriden at startup. The class library is passed in as an argument to the host executable. For example if we had .NET project called "ClassLibraryLambdaFunction" that contained a Lambda function it would be executed using the following:

dotnet exec --depsfile ./ClassLibraryLambdaFunction.deps.json --runtimeconfig ./ClassLibraryLambdaFunction.runtimeconfig.json ../../../../AspireExtensions/LambdaHost/Amazon.Lambda.RuntimeSupport.dll ClassLibraryLambdaFunction::ClassLibraryLambdaFunction.Function::FunctionHandler

Having this configured in the launchSettings.json file by the user is not practical and as part of an AddAWSLambdaFunction method I want to be able to setup all of these command line arguments for the user and return a LambdaProjectResource that is a subclass of ProjectResource.

Describe the solution you'd like

When adding project to the DistributedApplicationBuilder you can only specify the launchSettings profile name for configuring how the project should be started. I would like to propose adding a LaunchSettingsOverrideAnnotation annotation where all of the command line arguments for the "dotnet" command can be specified. The ApplicationExecutor would look for the annotation and if exists use it for setting up the parameters for the process.

Additional context

I'm happy to contribute this work if the team agrees on it.

@davidfowl davidfowl added the area-app-model Issues pertaining to the APIs in Aspire.Hosting, e.g. DistributedApplication label Oct 4, 2024
@joperezr joperezr added the untriaged New issue has not been triaged label Oct 15, 2024
@eerhardt
Copy link
Member

eerhardt commented Nov 7, 2024

@normj - have you considered making the "class library" dotnet run-able? This can be done by adding MSBuild logic to set

  • RunCommand
  • RunArguments

See https://github.com/dotnet/sdk/blob/f7a5c4e3dc7f7ac35a2684192413530a43877a05/src/Tasks/Microsoft.NET.Build.Tasks/targets/Microsoft.NET.Sdk.targets#L1111-L1171

These MSBuild properties are used by dotnet run and by VS (and thus by the Aspire AppHost) to launch projects.

In .NET 9, we added functionality to allow MSBuild <Target>s to set these properties, so you can run any logic to determine these values. See Extend dotnet run to invoke a run-command-producing Target (dotnet/sdk#42240).

@normj
Copy link
Contributor Author

normj commented Nov 8, 2024

@eerhardt That is an interesting idea. If this was launched via the IDE would the IDE attach the debugger to it?

I do have the challenge that within a class library there could me multiple Lambda functions that need to be started up separately with different argument to identity the .NET method to call. Imagine in an Aspire AppHost the same project being added multiple times to the distributed application but with a different "handler" string that identifies what method to call. Maybe what I could do is have the MSBuild targets for running the class library look at an environment variable that the Aspire AppHost sets on the process. I'll play around and see what I can do.

@eerhardt
Copy link
Member

eerhardt commented Nov 8, 2024

If this was launched via the IDE would the IDE attach the debugger to it?

Yes. That is my understanding (although I have much less experience in the IDE tooling parts).

Look at the '$(UseAppHost)' != 'true' path:

https://github.com/dotnet/sdk/blob/f7a5c4e3dc7f7ac35a2684192413530a43877a05/src/Tasks/Microsoft.NET.Build.Tasks/targets/Microsoft.NET.Sdk.targets#L1134-L1138

        <RunCommand Condition="'$(RunCommand)' == ''">dotnet</RunCommand>

        <_NetCoreRunArguments>exec &quot;$(TargetPath)&quot;</_NetCoreRunArguments>
        <RunArguments Condition="'$(RunArguments)' == '' and '$(StartArguments)' != ''">$(_NetCoreRunArguments) $(StartArguments)</RunArguments>
        <RunArguments Condition="'$(RunArguments)' == ''">$(_NetCoreRunArguments)</RunArguments>

This is basically the same thing you have. dotnet exec <some .dll>

@normj
Copy link
Contributor Author

normj commented Dec 5, 2024

@davidfowl @eerhardt Has something changed with the IDE integration with Aspire. I my previous hack of using the launchsettings.json to configure a class library to be runnable fails. It shows running in the dashboard but I know it must have failed quickly and the PID is 0 in the dashboard. When I run this AppHost from the commandline dotnet run. I get the following error which I'm pretty sure I didn't get before but not a hundred percent I tried before.

2024-12-05T18:06:58.8810000
 Unable to run your project.
2024-12-05T18:06:58.8810000
 Ensure you have a runnable project type and ensure 'dotnet run' supports this project.
2024-12-05T18:06:58.8810000
 A runnable project should target a runnable TFM (for instance, net5.0) and have OutputType 'Exe'.
2024-12-05T18:06:58.8810000
 The current OutputType is 'Library'.

@eerhardt I did try your trick of setting the RunCommand and RunArguments instead of relying on the launchSettings.json file and that got things working for running the AppHost outside of the IDE but within it fails like I described above.

@davidfowl
Copy link
Member

cc @vijayrkn @tlmii

@eerhardt
Copy link
Member

eerhardt commented Dec 5, 2024

The only thing that has changed around that area (that I know of) is the dotnet run functionality that @baronfel added in 9.0 -

[release/9.0.1xx] Extend dotnet run to invoke a run-command-producing Target (dotnet/sdk#42987)

Don't forward along binlog arguments to runnable applications (dotnet/sdk#43430)

@tlmii
Copy link
Member

tlmii commented Dec 5, 2024

I'm not aware of anything specific that changed in this area from the IDE side - but that doesn't mean there wasn't something that impacted it.

@normj do you know the before/after of your setup of when it worked to now (aspire version, VS version, etc)?

If those error messages you listed were happening from the command line execution of the class library project too, then it seems like maybe its not the IDE integration that's preventing it from working (though you might be hitting a related issue we're tracking about how we display quickly-exiting executables)?

Is the launchsettings.json hack you did something we can try to replicate with any class library to try to repro this? If so do you have complete steps for it to make sure we're doing the same things?

@normj
Copy link
Contributor Author

normj commented Dec 6, 2024

You can reproduce the error using my Lambda Aspire proof of concept (POC). It doesn't require an AWS account or credentials to run. The POC was made back in September and using a class library worked using the launchsettings.json file. We are picking the project back up now turning the POC into a real product and now using the latest bits the class library no longer seems to work.

To repo:

  • Clone https://github.com/normj/LambdaAspirePOC
  • checkout branch classlibrary-failure
  • In VS launch the AppHost
  • Notice the PID for ToLowerLambdaFunction project resource is 0 and there is nothing written to the logs. My guess is Aspire didn't even try to run the project because the output type was not an executable. Similar to the error we saw when using dotnet run complaining about the output type. "A runnable project should target a runnable TFM (for instance, net5.0) and have OutputType 'Exe'."

What is supposed to happen is the ClassLibraryLambdaFunction project has a profile in the launchSettings.json that provides the actual executable entry point that should be used.

{
  "profiles": {
    "LambdaRuntimeClient_FunctionHandler": {
      "commandName": "Executable",
      "commandLineArgs": "exec --depsfile ./ClassLibraryLambdaFunction.deps.json --runtimeconfig ./ClassLibraryLambdaFunction.runtimeconfig.json ../../../../AspireExtensions/LambdaHost/Amazon.Lambda.RuntimeSupport.dll ClassLibraryLambdaFunction::ClassLibraryLambdaFunction.Function::FunctionHandler",
      "workingDirectory": ".\\bin\\$(Configuration)\\net8.0",
      "executablePath": "dotnet"
    }
  }
}

In the AddAWSLambdaFunction extension method you can see I set the launch setting profile. https://github.com/normj/LambdaAspirePOC/blob/classlibrary-failure/AspireExtensions/Aspire.Hosting.AWS.Lambda/LambdaExtensions.cs#L22

If you make the ClassLibraryLambdaFunction the startup project you can confirm the profile actually starts up till you get an expected error about "Invalid URI". Through the AppHost it makes sure that URI is correctly set up.

Keep in mind my ultimate goal and the original reason for this issue is I want to get rid of dealing with the launchsettings.json file and from my AddAWSLambdaFunction extension method tell Aspire all of the command line arguments needed to run the class library.

@eerhardt
Copy link
Member

eerhardt commented Dec 6, 2024

Keep in mind my ultimate goal and the original reason for this issue is I want to get rid of dealing with the launchsettings.json file

The best way to do this is to make the project dotnet run-able. We did just that for Azure Functions support in 9.0. Check out:

https://github.com/Azure/azure-functions-dotnet-worker/blob/6338dc2e08d04a78956dd48dd6738888ab215bde/sdk/Sdk/Targets/Microsoft.Azure.Functions.Worker.Sdk.targets#L83-L90

  <!-- This target is used to set up the run arguments for the function app, supporting `dotnet run` -->
  <Target Name="_FunctionsComputeRunArguments" BeforeTargets="ComputeRunArguments">
    <PropertyGroup>
      <RunCommand>func</RunCommand>
      <RunArguments>host start $(RunArguments)</RunArguments>
      <RunWorkingDirectory>$(OutDir)</RunWorkingDirectory>
    </PropertyGroup>
  </Target>

You don't even need to use the ComputeRunArguments target if the RunCommand and RunArguments are static - you can just set the properties directly in a .targets file.

@normj
Copy link
Contributor Author

normj commented Dec 6, 2024

@eerhardt I tried making it runnable basically taking what is in the launchsettings into the run properties

  <Target Name="_FunctionsComputeRunArguments" BeforeTargets="ComputeRunArguments">
    <PropertyGroup>
      <RunCommand>dotnet</RunCommand>
      <RunArguments>exec --depsfile ./ClassLibraryLambdaFunction.deps.json --runtimeconfig ./ClassLibraryLambdaFunction.runtimeconfig.json ../../../../AspireExtensions/LambdaHost/Amazon.Lambda.RuntimeSupport.dll ClassLibraryLambdaFunction::ClassLibraryLambdaFunction.Function::FunctionHandler</RunArguments>
      <RunWorkingDirectory>$(OutDir)</RunWorkingDirectory>
    </PropertyGroup>
  </Target>	

and it works when I run the AppHost from the commandline outside of the IDE. But when I run it from the IDE I get the following error complaining about the project being a class library.

fail: Aspire.Hosting.Dcp.dcpctrl.ExecutableReconciler[0]
      run session could not be started: IDE returned a response indicating failure      {"Executable": {"name":"ToLowerLambdaFunction-ccvsfwhd"}, "Reconciliation": 2, "Status": "500 Internal Server Error", "Body": "A project with an Output Type of Class Library cannot be started directly.\r\n\r\nIn order to debug this project, add an executable project to this solution which references the library project. Set the executable project as the startup project."}

If there is a way to get the RunCommand and RunArguments be honored via launching in Visual Studio and the project is attached to the debugger that would give me a great working path. The launchsettings.json path isn't not the right long term solution for us.

@normj
Copy link
Contributor Author

normj commented Dec 12, 2024

@tlmii Were you able to try by POC and reproduce the problem? Let me know if I need to create a simpler repo case.

@tlmii
Copy link
Member

tlmii commented Dec 13, 2024

@normj I can repro the problem with your repo above (thanks for making that easy!) and I can see what appears to be the issue, including the change that caused it.

Trying to track down the reason for the change (it appears to have been a bug fix in coordinating profile information between DCP and the IDE) to determine the best path forward. More soon...

@normj
Copy link
Contributor Author

normj commented Dec 14, 2024

@tlmii Thanks for the update

@tlmii
Copy link
Member

tlmii commented Dec 17, 2024

We're pretty sure we've identified the fix and have the first of a couple PRs out needed to flow it through to VS. There's an issue with the API definition between DCP and the IDE that has been there for a while, but it was only exposed recently when another fix was made in VS to more closely adhere to the rules in the spec around the invocation arguments.

At the moment I'm not aware of a workaround. @karolz-ms would there be any way to force DCP to pass these same arguments so that VS effectively overrides them with themselves? Obviously it'd be a short term hack, but just until we were able to flow the real fix to a public/preview build?

@normj
Copy link
Contributor Author

normj commented Dec 17, 2024

Thanks for the update @tlmii. Trying to understand what is going on the PR. The arguments in being talked about in the PR are the commandline arguments specified in the launchSettings.json file? Looking at the spec and the code for Aspire.Hosting it doesn't look like the application model has been expanded for Aspire resources to be able to specify the command line arguments which is my ideal solution.

@tlmii
Copy link
Member

tlmii commented Dec 17, 2024

Hey @normj - in the PR description when I mention the user-specified command line arguments, I mean the ones in launchsettings.json. The invocation arguments (args in the API definition that we are changing in that PR) are arguments that can override those in the launch profile, if specified. They are passed from DCP to the IDE as part of the run session request.

I'm not sure what populates the invocation arguments. You may well be right that there's nothing in the API to do so. I was just hoping Karol might know of a way to finesse it to pass what you need in the short term, even if definitely not something you'd do long term. Otherwise I don't think we have a workaround (from this angle at least) until the fixes flow through.

@karolz-ms
Copy link
Member

I think that @normj is right; there does not seem to be a way to override Executable invocation arguments in app host code. What Norm proposed originally (an annotation to override invocation arguments) makes a lot of sense to me.

@mitchdenny toughts?

@tlmii
Copy link
Member

tlmii commented Jan 8, 2025

@normj Just wanted to let you know that the fix for this has made its way through the pieces and will be in the next public preview of VS. Apologies that we didn't come up with a way to get that to you earlier or find a workaround in the interim; but hopefully not too much longer until you're unblocked.

@normj
Copy link
Contributor Author

normj commented Jan 8, 2025

@tlmii Great, I was just about to ping on the status here.

@normj
Copy link
Contributor Author

normj commented Jan 9, 2025

@tlmii Can I guestimate the next VS public preview in the next month or so?

@tlmii
Copy link
Member

tlmii commented Jan 10, 2025

@normj The dates tend not to be pre-announced, but... looking at prior release dates they do tend to be on each patch Tuesdays (i.e. 2nd Tuesday, which for this month would be next Tuesday)... though the holidays probably bumped the next release by a couple weeks. Hope that's helpful enough for estimating?

@normj
Copy link
Contributor Author

normj commented Jan 11, 2025

After syncing with @davidfowl about our needs to be able to control all of the command line arguments from Aspire. An interim solution we could is we could managed our command line arguments by creating special launch profiles in the launchsettings.json file. But that means we need to configure the launch profile from the Aspire side. We can't currently do that because we are working with our own subclass of ProjectResource and we don't have access to the LaunchProfileAnnotation class because it is internal.

Can we make LaunchProfileAnnotation a public class to allow us to managed the launch profile used outside of Aspire.Hosting?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-app-model Issues pertaining to the APIs in Aspire.Hosting, e.g. DistributedApplication untriaged New issue has not been triaged
Projects
None yet
Development

No branches or pull requests

6 participants