Skip to content
This repository has been archived by the owner on Nov 20, 2023. It is now read-only.

Commit

Permalink
Support Dapr "standalone" applications (#1194)
Browse files Browse the repository at this point in the history
* Try inverting default placement service behavior.

* Eliminate placement service management.

* Remove references to placement service options.

* Try to determine Dapr status.

* Update formatting.

* Sketch service-based override of Dapr configuration.

* Move types to separate files.

* Fix formatting.
  • Loading branch information
philliphoff authored Oct 8, 2021
1 parent 565b7d3 commit bbf5475
Show file tree
Hide file tree
Showing 5 changed files with 147 additions and 26 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,7 @@ public static void HandleExtensionsMapping(YamlSequenceNode yamlSequenceNode, Li
switch (child.NodeType)
{
case YamlNodeType.Mapping:
var extensionDictionary = new Dictionary<string, object>();
foreach (var mapping in (YamlMappingNode)child)
{
var key = YamlParser.GetScalarValue(mapping.Key);
extensionDictionary[key] = YamlParser.GetScalarValue(key, mapping.Value)!;
}

var extensionDictionary = YamlParser.GetDictionary(child);
extensions.Add(extensionDictionary);
break;
default:
Expand Down
26 changes: 26 additions & 0 deletions src/Microsoft.Tye.Core/Serialization/YamlParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,32 @@ public ConfigApplication ParseConfigApplication()
return app;
}

public static Dictionary<string, object> GetDictionary(YamlNode node)
{
if (node.NodeType != YamlNodeType.Mapping)
{
throw new TyeYamlException(node.Start,
CoreStrings.FormatUnexpectedType(YamlNodeType.Mapping.ToString(), node.NodeType.ToString()));
}

var dictionary = new Dictionary<string, object>();

foreach (var mapping in (YamlMappingNode)node)
{
var key = YamlParser.GetScalarValue(mapping.Key);

dictionary[key] = mapping.Value.NodeType switch
{
YamlNodeType.Scalar => YamlParser.GetScalarValue(key, mapping.Value)!,
YamlNodeType.Mapping => YamlParser.GetDictionary(mapping.Value),
_ => throw new TyeYamlException(mapping.Value.Start,
CoreStrings.FormatUnexpectedType(YamlNodeType.Mapping.ToString(), mapping.Value.NodeType.ToString()))
};
}

return dictionary;
}

public static string GetScalarValue(YamlNode node)
{
if (node.NodeType != YamlNodeType.Scalar)
Expand Down
57 changes: 38 additions & 19 deletions src/Microsoft.Tye.Extensions/Dapr/DaprExtension.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,7 @@ public override async Task ProcessAsync(ExtensionContext context, ExtensionConfi
{
await VerifyDaprInitialized(context);

int? daprPlacementPort = null;

// see if a placement port number has been defined
if (config.Data.TryGetValue("placement-port", out var obj) && obj?.ToString() is string && int.TryParse(obj.ToString(), out var customPlacementPort))
{
context.Output.WriteDebugLine($"Using Dapr placement service host port {customPlacementPort} from 'placement-port'");
daprPlacementPort = customPlacementPort;
}
var extensionConfiguration = DaprExtensionConfigurationReader.ReadConfiguration(config.Data);

// For local run, enumerate all projects, and add services for each dapr proxy.
var projects = context.Application.Services.OfType<ProjectServiceBuilder>().Cast<LaunchedServiceBuilder>();
Expand All @@ -39,10 +32,25 @@ public override async Task ProcessAsync(ExtensionContext context, ExtensionConfi

foreach (var project in services)
{
// Dapr requires http. If this project isn't listening to HTTP then it's not daprized.
extensionConfiguration.Services.TryGetValue(project.Name, out DaprExtensionServiceConfiguration? serviceConfiguration);

if (serviceConfiguration?.Enabled != null && serviceConfiguration.Enabled.Value == false)
{
context.Output.WriteDebugLine($"Dapr has been disabled for service {project.Name}.");
continue;
}

var httpBinding = project.Bindings.FirstOrDefault(b => b.Protocol == "http");
if (httpBinding == null)

if (httpBinding == null && project.Bindings.Count == 1 && project.Bindings[0].Protocol == null)
{
// Assume the only untyped binding is HTTP...
httpBinding = project.Bindings[0];
}

if (httpBinding == null && (serviceConfiguration?.Enabled == null || !serviceConfiguration.Enabled.Value))
{
context.Output.WriteDebugLine($"Dapr has been disabled for unbound service {project.Name}.");
continue;
}

Expand All @@ -66,17 +74,25 @@ public override async Task ProcessAsync(ExtensionContext context, ExtensionConfi

// These environment variables are replaced with environment variables
// defined for this service.
Args = $"run --app-id {project.Name} --app-port %APP_PORT% --dapr-grpc-port %DAPR_GRPC_PORT% --dapr-http-port %DAPR_HTTP_PORT% --metrics-port %METRICS_PORT%",
Args = $"run --app-id {project.Name} --dapr-grpc-port %DAPR_GRPC_PORT% --dapr-http-port %DAPR_HTTP_PORT% --metrics-port %METRICS_PORT%",
};

if (httpBinding != null)
{
proxy.Args += $" --app-port %APP_PORT%";
}

var daprPlacementPort = serviceConfiguration?.PlacementPort ?? extensionConfiguration.PlacementPort;

if (daprPlacementPort.HasValue)
{
context.Output.WriteDebugLine($"Using Dapr placement service host port {daprPlacementPort.Value} from 'placement-port' for service {project.Name}.");
proxy.Args += $" --placement-host-address localhost:{daprPlacementPort.Value}";
}

// When running locally `-config` specifies a filename, not a configuration name. By convention
// we'll assume the filename and config name are the same.
if (config.Data.TryGetValue("config", out obj) && obj?.ToString() is string daprConfig)
if (config.Data.TryGetValue("config", out var obj) && obj?.ToString() is string daprConfig)
{
var configFile = Path.Combine(context.Application.Source.DirectoryName!, "components", $"{daprConfig}.yaml");
if (File.Exists(configFile))
Expand Down Expand Up @@ -132,15 +148,18 @@ public override async Task ProcessAsync(ExtensionContext context, ExtensionConfi
};
proxy.Bindings.Add(metrics);

// Set APP_PORT based on the project's assigned port for http
var appPort = new EnvironmentVariableBuilder("APP_PORT")
if (httpBinding != null)
{
Source = new EnvironmentVariableSourceBuilder(project.Name, binding: httpBinding.Name)
// Set APP_PORT based on the project's assigned port for http
var appPort = new EnvironmentVariableBuilder("APP_PORT")
{
Kind = EnvironmentVariableSourceBuilder.SourceKind.Port,
},
};
proxy.EnvironmentVariables.Add(appPort);
Source = new EnvironmentVariableSourceBuilder(project.Name, binding: httpBinding.Name)
{
Kind = EnvironmentVariableSourceBuilder.SourceKind.Port,
},
};
proxy.EnvironmentVariables.Add(appPort);
}

// Set DAPR_GRPC_PORT based on this service's assigned port
var daprGrpcPort = new EnvironmentVariableBuilder("DAPR_GRPC_PORT")
Expand Down
24 changes: 24 additions & 0 deletions src/Microsoft.Tye.Extensions/Dapr/DaprExtensionConfiguration.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System.Collections.Generic;

namespace Microsoft.Tye.Extensions.Dapr
{
internal abstract class DaprExtensionCommonConfiguration
{
public int? PlacementPort { get; set; }
}

internal sealed class DaprExtensionServiceConfiguration : DaprExtensionCommonConfiguration
{
public bool? Enabled { get; set; }
}

internal sealed class DaprExtensionConfiguration : DaprExtensionCommonConfiguration
{
public IReadOnlyDictionary<string, DaprExtensionServiceConfiguration> Services { get; set; }
= new Dictionary<string, DaprExtensionServiceConfiguration>();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System;
using System.Collections.Generic;

namespace Microsoft.Tye.Extensions.Dapr
{
internal static class DaprExtensionConfigurationReader
{
public static DaprExtensionConfiguration ReadConfiguration(IDictionary<string, object> rawConfiguration)
{
var configuration = new DaprExtensionConfiguration();

ReadCommonConfiguration(rawConfiguration, configuration);

if (rawConfiguration.TryGetValue("services", out var servicesObject) && servicesObject is Dictionary<string, object> rawServicesConfiguration)
{
var services = new Dictionary<string, DaprExtensionServiceConfiguration>();

foreach (var kvp in rawServicesConfiguration)
{
if (kvp.Value is Dictionary<string, object> rawServiceConfiguration)
{
var serviceConfiguration = new DaprExtensionServiceConfiguration();

ReadServiceConfiguration(rawServiceConfiguration, serviceConfiguration);

services.Add(kvp.Key, serviceConfiguration);
}
}

configuration.Services = services;
}

return configuration;
}

private static void ReadServiceConfiguration(IDictionary<string, object> rawConfiguration, DaprExtensionServiceConfiguration serviceConfiguration)
{
ReadCommonConfiguration(rawConfiguration, serviceConfiguration);

if (rawConfiguration.TryGetValue("enabled", out var obj) && obj is string && Boolean.TryParse(obj.ToString(), out var enabled))
{
serviceConfiguration.Enabled = enabled;
}
}

private static void ReadCommonConfiguration(IDictionary<string, object> rawConfiguration, DaprExtensionCommonConfiguration commonConfiguration)
{
if (rawConfiguration.TryGetValue("placement-port", out var obj) && obj?.ToString() is string && int.TryParse(obj.ToString(), out var customPlacementPort))
{
commonConfiguration.PlacementPort = customPlacementPort;
}
}
}
}

0 comments on commit bbf5475

Please sign in to comment.