diff --git a/.gitignore b/.gitignore index 5931d2ed9..f09ca5887 100644 --- a/.gitignore +++ b/.gitignore @@ -353,3 +353,5 @@ MigrationBackup/ # Ionide (cross platform F# VS Code tools) working folder .ionide/ + +.idea/ diff --git a/src/Microsoft.Tye.Core/AzureFunctionServiceBuilder.cs b/src/Microsoft.Tye.Core/AzureFunctionServiceBuilder.cs index 5216ad846..a75e36a8c 100644 --- a/src/Microsoft.Tye.Core/AzureFunctionServiceBuilder.cs +++ b/src/Microsoft.Tye.Core/AzureFunctionServiceBuilder.cs @@ -6,7 +6,7 @@ namespace Microsoft.Tye { - public class AzureFunctionServiceBuilder : ServiceBuilder + public class AzureFunctionServiceBuilder : LaunchedServiceBuilder { public AzureFunctionServiceBuilder(string name, string path, ServiceSource source) : base(name, source) @@ -14,12 +14,10 @@ public AzureFunctionServiceBuilder(string name, string path, ServiceSource sourc FunctionPath = path; } - public int Replicas { get; set; } = 1; public string? Args { get; set; } public string FunctionPath { get; } public string? FuncExecutablePath { get; set; } public string? ProjectFile { get; set; } public string? AzureFunctionsVersion { get; set; } - public List EnvironmentVariables { get; } = new List(); } } diff --git a/src/Microsoft.Tye.Extensions/Dapr/DaprExtension.cs b/src/Microsoft.Tye.Extensions/Dapr/DaprExtension.cs index 3e682fb75..16534d999 100644 --- a/src/Microsoft.Tye.Extensions/Dapr/DaprExtension.cs +++ b/src/Microsoft.Tye.Extensions/Dapr/DaprExtension.cs @@ -28,7 +28,8 @@ public override async Task ProcessAsync(ExtensionContext context, ExtensionConfi // For local run, enumerate all projects, and add services for each dapr proxy. var projects = context.Application.Services.OfType().Cast(); var executables = context.Application.Services.OfType().Cast(); - var services = projects.Concat(executables).ToList(); + var functions = context.Application.Services.OfType().Cast(); + var services = projects.Concat(executables).Concat(functions).ToList(); foreach (var project in services) { diff --git a/test/E2ETest/Microsoft.Tye.E2ETests.csproj b/test/E2ETest/Microsoft.Tye.E2ETests.csproj index d84fcf593..f7c27d16e 100644 --- a/test/E2ETest/Microsoft.Tye.E2ETests.csproj +++ b/test/E2ETest/Microsoft.Tye.E2ETests.csproj @@ -1,4 +1,4 @@ - + @@ -15,6 +15,7 @@ The Microsoft.Build.Locator package takes care of dynamically loading these assemblies at runtime. We don't need/want to ship them, just to have them as references. --> + @@ -29,6 +30,7 @@ + diff --git a/test/E2ETest/TyeGenerateTests.cs b/test/E2ETest/TyeGenerateTests.cs index d2fd6c9d8..6e4758db8 100644 --- a/test/E2ETest/TyeGenerateTests.cs +++ b/test/E2ETest/TyeGenerateTests.cs @@ -253,6 +253,10 @@ public async Task Generate_DaprApplication() } } + + + + [ConditionalFact] [SkipIfDockerNotRunning] public async Task Generate_ConnectionStringDependency() diff --git a/test/E2ETest/TyeRunTests.cs b/test/E2ETest/TyeRunTests.cs index 09c09434e..6ac1ec6fe 100644 --- a/test/E2ETest/TyeRunTests.cs +++ b/test/E2ETest/TyeRunTests.cs @@ -82,12 +82,13 @@ public async Task FrontendBackendRunTest() }); } - [Fact(Skip = "Need to figure out how to install func before running")] + //[Fact(Skip = "Need to figure out how to install func before running")] + [Fact] public async Task FrontendBackendAzureFunctionTest() { // Install to directory using var tmp = TempDirectory.Create(); - await ProcessUtil.RunAsync("npm", "install azure-functions-core-tools@3`", workingDirectory: tmp.DirectoryPath); + //await ProcessUtil.RunAsync("npm", "install azure-functions-core-tools@3`", workingDirectory: tmp.DirectoryPath); using var projectDirectory = CopyTestProjectDirectory("azure-functions"); var content = @$" @@ -97,7 +98,6 @@ public async Task FrontendBackendAzureFunctionTest() services: - name: backend azureFunction: backend/ - pathToFunc: {tmp.DirectoryPath}/node_modules/azure-functions-core-tools/bin/func.dll - name: frontend project: frontend/frontend.csproj"; @@ -126,6 +126,40 @@ public async Task FrontendBackendAzureFunctionTest() Assert.True(frontendResponse.IsSuccessStatusCode); }); } + [Fact] + public async Task DaprAzureFunctionTest() + { + using var projectDirectory = CopyTestProjectDirectory("dapr-function-app"); + + var projectFile = new FileInfo(Path.Combine(projectDirectory.DirectoryPath, "tye.yaml")); + var outputContext = new OutputContext(_sink, Verbosity.Debug); + var application = await ApplicationFactory.CreateAsync(outputContext, projectFile); + + await application.ProcessExtensionsAsync(new HostOptions(), outputContext, ExtensionContext.OperationKind.LocalRun); + var handler = new HttpClientHandler + { + ServerCertificateCustomValidationCallback = (a, b, c, d) => true, + AllowAutoRedirect = false + }; + + var client = new HttpClient(new RetryHandler(handler)); + + await RunHostingApplication(application, new HostOptions(), async (app, uri) => + { + //The backend calls the Function app via Dapr InvokeMethod. So test that that func has a sidecar, and being proxied. + var backendUri = await GetServiceUrl(client, uri, "dapr-test-project"); + + //Wait for the services to start + await Task.Delay(10000); + + var backendResponse = await client.GetAsync(backendUri); + + Assert.True(backendResponse.IsSuccessStatusCode); + + var responseContent = await backendResponse.Content.ReadAsStringAsync(); + Assert.Contains("Welcome to Azure Functions!", responseContent); + }); + } [ConditionalTheory] [SkipIfDockerNotRunning] diff --git a/test/E2ETest/testassets/projects/azure-functions/backend/backend.csproj b/test/E2ETest/testassets/projects/azure-functions/backend/backend.csproj index 8516359a3..09dcb8b45 100644 --- a/test/E2ETest/testassets/projects/azure-functions/backend/backend.csproj +++ b/test/E2ETest/testassets/projects/azure-functions/backend/backend.csproj @@ -1,10 +1,10 @@ - net6.0 - v3 + net7.0 + v4 - + diff --git a/test/E2ETest/testassets/projects/dapr-function-app/dapr-function-app.sln b/test/E2ETest/testassets/projects/dapr-function-app/dapr-function-app.sln new file mode 100644 index 000000000..9dbc4f09a --- /dev/null +++ b/test/E2ETest/testassets/projects/dapr-function-app/dapr-function-app.sln @@ -0,0 +1,31 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 25.0.1706.3 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "dapr-function-app", "function/dapr-function-app.csproj", "{0979B3EB-D56F-4402-A858-AB19B6D9B902}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "dapr", "dapr\dapr.csproj", "{8336F6C8-57F3-4D80-B4AF-CFE0A9DA446E}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {0979B3EB-D56F-4402-A858-AB19B6D9B902}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0979B3EB-D56F-4402-A858-AB19B6D9B902}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0979B3EB-D56F-4402-A858-AB19B6D9B902}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0979B3EB-D56F-4402-A858-AB19B6D9B902}.Release|Any CPU.Build.0 = Release|Any CPU + {8336F6C8-57F3-4D80-B4AF-CFE0A9DA446E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8336F6C8-57F3-4D80-B4AF-CFE0A9DA446E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8336F6C8-57F3-4D80-B4AF-CFE0A9DA446E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8336F6C8-57F3-4D80-B4AF-CFE0A9DA446E}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {B9930E6C-6675-4B5A-8D01-5BEEABAF024D} + EndGlobalSection +EndGlobal diff --git a/test/E2ETest/testassets/projects/dapr-function-app/dapr/Program.cs b/test/E2ETest/testassets/projects/dapr-function-app/dapr/Program.cs new file mode 100644 index 000000000..7a886b792 --- /dev/null +++ b/test/E2ETest/testassets/projects/dapr-function-app/dapr/Program.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; + +namespace dapr +{ + 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(); + }); + } +} diff --git a/test/E2ETest/testassets/projects/dapr-function-app/dapr/Properties/launchSettings.json b/test/E2ETest/testassets/projects/dapr-function-app/dapr/Properties/launchSettings.json new file mode 100644 index 000000000..58c012189 --- /dev/null +++ b/test/E2ETest/testassets/projects/dapr-function-app/dapr/Properties/launchSettings.json @@ -0,0 +1,27 @@ +{ + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:64181", + "sslPort": 44315 + } + }, + "profiles": { + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "dapr": { + "commandName": "Project", + "launchBrowser": true, + "applicationUrl": "https://localhost:5001;http://localhost:5000", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/test/E2ETest/testassets/projects/dapr-function-app/dapr/Startup.cs b/test/E2ETest/testassets/projects/dapr-function-app/dapr/Startup.cs new file mode 100644 index 000000000..91d45e268 --- /dev/null +++ b/test/E2ETest/testassets/projects/dapr-function-app/dapr/Startup.cs @@ -0,0 +1,51 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net.Http; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Dapr.Client; + +namespace dapr +{ + public class Startup + { + // This method gets called by the runtime. Use this method to add services to the container. + // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940 + public void ConfigureServices(IServiceCollection services) + { + } + + // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. + public void Configure(IApplicationBuilder app, IWebHostEnvironment env) + { + if (env.IsDevelopment()) + { + app.UseDeveloperExceptionPage(); + } + + app.UseRouting(); + + app.UseEndpoints(endpoints => + { + endpoints.MapGet("/", async context => + { + var functionAppId = "dapr-function-app"; + var methodName = "api/HttpTrigger1"; + + + var daprClient = new DaprClientBuilder().Build(); + var daprRequest = daprClient.CreateInvokeMethodRequest(HttpMethod.Post, functionAppId, methodName); + + var response = await daprClient.InvokeMethodWithResponseAsync(daprRequest); + + await context.Response.WriteAsync(await response.Content.ReadAsStringAsync()); + }); + }); + } + } +} diff --git a/test/E2ETest/testassets/projects/dapr-function-app/dapr/appsettings.Development.json b/test/E2ETest/testassets/projects/dapr-function-app/dapr/appsettings.Development.json new file mode 100644 index 000000000..8983e0fc1 --- /dev/null +++ b/test/E2ETest/testassets/projects/dapr-function-app/dapr/appsettings.Development.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + } +} diff --git a/test/E2ETest/testassets/projects/dapr-function-app/dapr/appsettings.json b/test/E2ETest/testassets/projects/dapr-function-app/dapr/appsettings.json new file mode 100644 index 000000000..d9d9a9bff --- /dev/null +++ b/test/E2ETest/testassets/projects/dapr-function-app/dapr/appsettings.json @@ -0,0 +1,10 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + }, + "AllowedHosts": "*" +} diff --git a/test/E2ETest/testassets/projects/dapr-function-app/dapr/dapr.csproj b/test/E2ETest/testassets/projects/dapr-function-app/dapr/dapr.csproj new file mode 100644 index 000000000..891c0a146 --- /dev/null +++ b/test/E2ETest/testassets/projects/dapr-function-app/dapr/dapr.csproj @@ -0,0 +1,11 @@ + + + + net6.0 + + + + + + + diff --git a/test/E2ETest/testassets/projects/dapr-function-app/function/HttpTrigger.cs b/test/E2ETest/testassets/projects/dapr-function-app/function/HttpTrigger.cs new file mode 100644 index 000000000..b7f536e60 --- /dev/null +++ b/test/E2ETest/testassets/projects/dapr-function-app/function/HttpTrigger.cs @@ -0,0 +1,32 @@ +using System.Net; +using Microsoft.Azure.Functions.Worker; +using Microsoft.Azure.Functions.Worker.Http; +using Microsoft.Extensions.Logging; + +namespace dapr_function_app +{ + public class HttpTrigger1 + { + private readonly ILogger _logger; + + public HttpTrigger1(ILoggerFactory loggerFactory) + { + _logger = loggerFactory.CreateLogger(); + } + + [Function("HttpTrigger1")] + public HttpResponseData Run([HttpTrigger(AuthorizationLevel.Anonymous, "get", "post")] HttpRequestData req) + { + _logger.LogInformation("C# HTTP trigger function processed a request."); + + var response = req.CreateResponse(HttpStatusCode.OK); + response.Headers.Add("Content-Type", "text/plain; charset=utf-8"); + + response.WriteString("Welcome to Azure Functions!"); + + return response; + } + + + } +} diff --git a/test/E2ETest/testassets/projects/dapr-function-app/function/Program.cs b/test/E2ETest/testassets/projects/dapr-function-app/function/Program.cs new file mode 100644 index 000000000..1c6cb8e09 --- /dev/null +++ b/test/E2ETest/testassets/projects/dapr-function-app/function/Program.cs @@ -0,0 +1,18 @@ +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; + +internal class Program +{ + private static void Main(string[] args) + { + var host = new HostBuilder() + .ConfigureFunctionsWorkerDefaults(config => + { + config.Services.AddLogging(); + }) + .Build(); + + + host.Run(); + } +} \ No newline at end of file diff --git a/test/E2ETest/testassets/projects/dapr-function-app/function/Properties/launchSettings.json b/test/E2ETest/testassets/projects/dapr-function-app/function/Properties/launchSettings.json new file mode 100644 index 000000000..58c012189 --- /dev/null +++ b/test/E2ETest/testassets/projects/dapr-function-app/function/Properties/launchSettings.json @@ -0,0 +1,27 @@ +{ + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:64181", + "sslPort": 44315 + } + }, + "profiles": { + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "dapr": { + "commandName": "Project", + "launchBrowser": true, + "applicationUrl": "https://localhost:5001;http://localhost:5000", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/test/E2ETest/testassets/projects/dapr-function-app/function/appsettings.Development.json b/test/E2ETest/testassets/projects/dapr-function-app/function/appsettings.Development.json new file mode 100644 index 000000000..8983e0fc1 --- /dev/null +++ b/test/E2ETest/testassets/projects/dapr-function-app/function/appsettings.Development.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + } +} diff --git a/test/E2ETest/testassets/projects/dapr-function-app/function/appsettings.json b/test/E2ETest/testassets/projects/dapr-function-app/function/appsettings.json new file mode 100644 index 000000000..d9d9a9bff --- /dev/null +++ b/test/E2ETest/testassets/projects/dapr-function-app/function/appsettings.json @@ -0,0 +1,10 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + }, + "AllowedHosts": "*" +} diff --git a/test/E2ETest/testassets/projects/dapr-function-app/function/dapr-function-app.csproj b/test/E2ETest/testassets/projects/dapr-function-app/function/dapr-function-app.csproj new file mode 100644 index 000000000..9d671e6c8 --- /dev/null +++ b/test/E2ETest/testassets/projects/dapr-function-app/function/dapr-function-app.csproj @@ -0,0 +1,23 @@ + + + net7.0 + v4 + Exe + enable + + + + + + + + + PreserveNewest + + + PreserveNewest + Never + + + + \ No newline at end of file diff --git a/test/E2ETest/testassets/projects/dapr-function-app/function/host.json b/test/E2ETest/testassets/projects/dapr-function-app/function/host.json new file mode 100644 index 000000000..5df170b64 --- /dev/null +++ b/test/E2ETest/testassets/projects/dapr-function-app/function/host.json @@ -0,0 +1,12 @@ +{ + "version": "2.0", + "logging": { + "applicationInsights": { + "samplingSettings": { + "isEnabled": true, + "excludedTypes": "Request" + }, + "enableLiveMetricsFilters": true + } + } +} \ No newline at end of file diff --git a/test/E2ETest/testassets/projects/dapr-function-app/function/local.settings.json b/test/E2ETest/testassets/projects/dapr-function-app/function/local.settings.json new file mode 100644 index 000000000..f5bff4150 --- /dev/null +++ b/test/E2ETest/testassets/projects/dapr-function-app/function/local.settings.json @@ -0,0 +1,6 @@ +{ + "IsEncrypted": false, + "Values": { + "FUNCTIONS_WORKER_RUNTIME": "dotnet-isolated" + } +} \ No newline at end of file diff --git a/test/E2ETest/testassets/projects/dapr-function-app/tye.yaml b/test/E2ETest/testassets/projects/dapr-function-app/tye.yaml new file mode 100644 index 000000000..bdc065eb8 --- /dev/null +++ b/test/E2ETest/testassets/projects/dapr-function-app/tye.yaml @@ -0,0 +1,10 @@ +name: dapr_test_application +extensions: +- name: dapr + config: tracing + log-level: debug +services: +- name: dapr-function-app + azureFunction: function/ +- name: dapr-test-project + project: dapr/dapr.csproj \ No newline at end of file