Browse Source

Support additional Dapr configuration properties (#1209)

* Support more Dapr properties.

* Update schema with Dapr extension properties.

* Add more Dapr properties.

* Fix formatting.
pull/1218/head
Phillip Hoff 5 years ago
committed by GitHub
parent
commit
1f51824cbc
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 95
      docs/reference/schema.md
  2. 109
      src/Microsoft.Tye.Extensions/Dapr/DaprExtension.cs
  3. 13
      src/Microsoft.Tye.Extensions/Dapr/DaprExtensionConfiguration.cs
  4. 44
      src/Microsoft.Tye.Extensions/Dapr/DaprExtensionConfigurationReader.cs

95
docs/reference/schema.md

@ -707,3 +707,98 @@ If both `port` and `protocol` are provided, Tye selects the first biding with th
#### `headers` (`(name, value)[]`)
Array of headers that are sent as part of the HTTP request that probes the replicas of the service.
## Extensions
Each of the Tye extensions have their own custom configuration schema.
### Dapr
For the Dapr extension, the available properties closely follow those found on the [`dapr run` command line](https://docs.dapr.io/reference/cli/dapr-run/). The Dapr extension allows many properties to be set and/or overridden on a service-by-service basis using the `services` dictionary.
```yaml
extensions:
- name: dapr
config: common
enable-profiling: true
services:
frontend:
components-path: "./frontend/components"
backend:
components-path: "./backend/components"
services:
- name: frontend
- name: backend
```
The following properties are annotated as follows:
- *extension-level*: Can only be set at the root of the extension configuration.
- *service-level*: Can only be set for a specific service within the `services` dictionary.
- *overridable*: Can be set at both the root of the extension configuration or be overridden for a specific service within the `services` dictionary.
#### `app-id` (`string`) *service-level*
The ID for your application, used for service discovery.
#### `app-max-concurrency` (`integer`) *overridable*
The concurrency level of the
application (otherwise unlimited).
#### `app-protocol` (`string`) *overridable*
The protocol (gRPC or HTTP) Dapr uses to talk to the application (with HTTP being the default).
#### `app-ssl` (`boolean`) *overridable*
Enable HTTPS when Dapr invokes the application.
#### `components-path` (`string`) *overridable*
The path of the components directory. If relative, is relative to the root directory of the application (i.e. of the `tye.yaml`).
#### `config` (`string`) *overridable*
The name of the Dapr configuration file (without extension). Assumed to be relative to the `components-path` directory, if specified, else the `components` folder in the root directory of the application (i.e. of the `tye.yaml`).
#### `enabled` (`boolean`) *service-level*
Whether a Dapr sidecar is created for the service. If `true`, a sidecar is created even if the default for the service type would be not to create one. If `false`, a sidecar is *not* created even if the default for the service type would be to create one.
#### `enable-profiling` (`boolean`) *overridable*
Enable `pprof` profiling via an HTTP endpoint.
#### `grpc-port` (`integer`) *service-level*
The gRPC port for Dapr to listen on.
#### `http-max-request-size` (`integer`) *overridable*
The maximum size of an HTTP request body in MB.
#### `http-port` (`integer`) *service-level*
The HTTP port for Dapr to listen on.
#### `log-level` (`string`) *overridable*
The log verbosity. Valid values are: `debug`, `info`, `warn`, `error`, `fatal`, and `panic`.
#### `metrics-port` (`integer`) *service-level*
The port used to collect Dapr metrics.
#### `placement-port` (`integer`) *service-level*
The port of the Dapr placement service.
#### `profile-port` (`integer`) *service-level*
The port for the Dapr profile servicer to listen on.
#### `services` (`(string, object)[]`) *extension-level*
The dictionary in which service-level configuration can be set.

109
src/Microsoft.Tye.Extensions/Dapr/DaprExtension.cs

@ -19,12 +19,12 @@ namespace Microsoft.Tye.Extensions.Dapr
// If we're getting called then the user configured dapr in their tye.yaml.
// We don't have any of our own config.
var extensionConfiguration = DaprExtensionConfigurationReader.ReadConfiguration(config.Data);
if (context.Operation == ExtensionContext.OperationKind.LocalRun)
{
await VerifyDaprInitialized(context);
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>();
var executables = context.Application.Services.OfType<ExecutableServiceBuilder>().Cast<LaunchedServiceBuilder>();
@ -32,7 +32,9 @@ namespace Microsoft.Tye.Extensions.Dapr
foreach (var project in services)
{
extensionConfiguration.Services.TryGetValue(project.Name, out DaprExtensionServiceConfiguration? serviceConfiguration);
DaprExtensionServiceConfiguration? serviceConfiguration = null;
extensionConfiguration?.Services.TryGetValue(project.Name, out serviceConfiguration);
if (serviceConfiguration?.Enabled != null && serviceConfiguration.Enabled.Value == false)
{
@ -68,13 +70,15 @@ namespace Microsoft.Tye.Extensions.Dapr
var daprExecutablePath = GetDaprExecutablePath();
string appId = serviceConfiguration?.AppId ?? project.Name;
var proxy = new ExecutableServiceBuilder($"{project.Name}-dapr", daprExecutablePath, ServiceSource.Extension)
{
WorkingDirectory = context.Application.Source.DirectoryName,
// These environment variables are replaced with environment variables
// defined for this service.
Args = $"run --app-id {project.Name} --dapr-grpc-port %DAPR_GRPC_PORT% --dapr-http-port %DAPR_HTTP_PORT% --metrics-port %METRICS_PORT%",
Args = $"run --app-id {appId} --dapr-grpc-port %DAPR_GRPC_PORT% --dapr-http-port %DAPR_HTTP_PORT% --metrics-port %METRICS_PORT%",
};
if (httpBinding != null)
@ -82,7 +86,28 @@ namespace Microsoft.Tye.Extensions.Dapr
proxy.Args += $" --app-port %APP_PORT%";
}
var daprPlacementPort = serviceConfiguration?.PlacementPort ?? extensionConfiguration.PlacementPort;
var appMaxConcurrency = serviceConfiguration?.AppMaxConcurrency ?? extensionConfiguration?.AppMaxConcurrency;
if (appMaxConcurrency != null)
{
proxy.Args += $" --app-max-concurrency {appMaxConcurrency}";
}
var appProtocol = serviceConfiguration?.AppProtocol ?? extensionConfiguration?.AppProtocol;
if (appProtocol != null)
{
proxy.Args += $" --app-protocol {appProtocol}";
}
var appSsl = serviceConfiguration?.AppSsl ?? extensionConfiguration?.AppSsl;
if (appSsl == true)
{
proxy.Args += " --app-ssl";
}
var daprPlacementPort = serviceConfiguration?.PlacementPort ?? extensionConfiguration?.PlacementPort;
if (daprPlacementPort.HasValue)
{
@ -90,11 +115,21 @@ namespace Microsoft.Tye.Extensions.Dapr
proxy.Args += $" --placement-host-address localhost:{daprPlacementPort.Value}";
}
string? componentsPath = serviceConfiguration?.ComponentsPath ?? extensionConfiguration?.ComponentsPath;
if (componentsPath != null)
{
proxy.Args += $" --components-path {componentsPath}";
}
string? daprConfig = serviceConfiguration?.Config ?? extensionConfiguration?.Config;
// 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 var obj) && obj?.ToString() is string daprConfig)
if (daprConfig != null)
{
var configFile = Path.Combine(context.Application.Source.DirectoryName!, "components", $"{daprConfig}.yaml");
string configDirectory = componentsPath ?? Path.Combine(context.Application.Source.DirectoryName!, "components");
var configFile = Path.Combine(configDirectory, $"{daprConfig}.yaml");
if (File.Exists(configFile))
{
proxy.Args += $" --config \"{configFile}\"";
@ -105,15 +140,25 @@ namespace Microsoft.Tye.Extensions.Dapr
}
}
if (config.Data.TryGetValue("log-level", out obj) && obj?.ToString() is string logLevel)
int? httpMaxRequestSize = serviceConfiguration?.HttpMaxRequestSize ?? extensionConfiguration?.HttpMaxRequestSize;
if (httpMaxRequestSize != null)
{
proxy.Args += $" --log-level {logLevel}";
proxy.Args += $" --dapr-http-max-request-size {httpMaxRequestSize}";
}
if (config.Data.TryGetValue("components-path", out obj) && obj?.ToString() is string componentsPath)
if ((serviceConfiguration?.EnableProfiling ?? extensionConfiguration?.EnableProfiling) == true)
{
proxy.Args += $" --components-path {componentsPath}";
proxy.Args += " --enable-profiling";
}
string? logLevel = serviceConfiguration?.LogLevel ?? extensionConfiguration?.LogLevel;
if (logLevel != null)
{
proxy.Args += $" --log-level {logLevel}";
}
// Add dapr proxy as a service available to everyone.
proxy.Dependencies.UnionWith(context.Application.Services.Select(s => s.Name));
@ -129,6 +174,7 @@ namespace Microsoft.Tye.Extensions.Dapr
{
Name = "grpc",
Protocol = "https",
Port = serviceConfiguration?.GrpcPort
};
proxy.Bindings.Add(grpc);
@ -137,6 +183,7 @@ namespace Microsoft.Tye.Extensions.Dapr
{
Name = "http",
Protocol = "http",
Port = serviceConfiguration?.HttpPort
};
proxy.Bindings.Add(http);
@ -145,6 +192,7 @@ namespace Microsoft.Tye.Extensions.Dapr
{
Name = "metrics",
Protocol = "http",
Port = serviceConfiguration?.MetricsPort
};
proxy.Bindings.Add(metrics);
@ -210,6 +258,29 @@ namespace Microsoft.Tye.Extensions.Dapr
},
};
proxy.EnvironmentVariables.Add(metricsPort);
// TODO: Do we add a means of dynamically using the profile port?
if (serviceConfiguration?.ProfilePort != null)
{
proxy.Args += $" --profile-port %PROFILE_PORT%";
var profile = new BindingBuilder()
{
Name = "profile",
Protocol = "http",
Port = serviceConfiguration.ProfilePort
};
proxy.Bindings.Add(profile);
var profilePort = new EnvironmentVariableBuilder("PROFILE_PORT")
{
Source = new EnvironmentVariableSourceBuilder(proxy.Name, binding: "profile")
{
Kind = EnvironmentVariableSourceBuilder.SourceKind.Port,
},
};
proxy.EnvironmentVariables.Add(profilePort);
}
}
}
else
@ -218,6 +289,10 @@ namespace Microsoft.Tye.Extensions.Dapr
var projects = context.Application.Services.OfType<ProjectServiceBuilder>();
foreach (var project in projects)
{
DaprExtensionServiceConfiguration? serviceConfiguration = null;
extensionConfiguration?.Services.TryGetValue(project.Name, out serviceConfiguration);
// Dapr requires http. If this project isn't listening to HTTP then it's not daprized.
var httpBinding = project.Bindings.FirstOrDefault(b => b.Protocol == "http");
if (httpBinding == null)
@ -230,16 +305,22 @@ namespace Microsoft.Tye.Extensions.Dapr
continue;
}
string appId = serviceConfiguration?.AppId ?? project.Name;
deployment.Annotations.Add("dapr.io/enabled", "true");
deployment.Annotations.Add("dapr.io/app-id", project.Name);
deployment.Annotations.Add("dapr.io/app-id", appId);
deployment.Annotations.Add("dapr.io/app-port", (httpBinding.Port ?? 80).ToString(CultureInfo.InvariantCulture));
if (config.Data.TryGetValue("config", out var obj) && obj?.ToString() is string daprConfig)
string? daprConfig = serviceConfiguration?.Config ?? extensionConfiguration?.Config;
if (daprConfig != null)
{
deployment.Annotations.TryAdd("dapr.io/config", daprConfig);
}
if (config.Data.TryGetValue("log-level", out obj) && obj?.ToString() is string logLevel)
string? logLevel = serviceConfiguration?.LogLevel ?? extensionConfiguration?.LogLevel;
if (logLevel != null)
{
deployment.Annotations.TryAdd("dapr.io/log-level", logLevel);
}

13
src/Microsoft.Tye.Extensions/Dapr/DaprExtensionConfiguration.cs

@ -8,12 +8,25 @@ namespace Microsoft.Tye.Extensions.Dapr
{
internal abstract class DaprExtensionCommonConfiguration
{
public int? AppMaxConcurrency { get; set; }
public string? AppProtocol { get; set; }
public bool? AppSsl { get; set; }
public string? ComponentsPath { get; set; }
public string? Config { get; set; }
public bool? EnableProfiling { get; set; }
public int? HttpMaxRequestSize { get; set; }
public string? LogLevel { get; set; }
public int? PlacementPort { get; set; }
}
internal sealed class DaprExtensionServiceConfiguration : DaprExtensionCommonConfiguration
{
public string? AppId { get; set; }
public bool? Enabled { get; set; }
public int? GrpcPort { get; set; }
public int? HttpPort { get; set; }
public int? MetricsPort { get; set; }
public int? ProfilePort { get; set; }
}
internal sealed class DaprExtensionConfiguration : DaprExtensionCommonConfiguration

44
src/Microsoft.Tye.Extensions/Dapr/DaprExtensionConfigurationReader.cs

@ -41,18 +41,50 @@ namespace Microsoft.Tye.Extensions.Dapr
{
ReadCommonConfiguration(rawConfiguration, serviceConfiguration);
if (rawConfiguration.TryGetValue("enabled", out var obj) && obj is string && Boolean.TryParse(obj.ToString(), out var enabled))
{
serviceConfiguration.Enabled = enabled;
}
serviceConfiguration.AppId = TryGetValue(rawConfiguration, "app-id");
serviceConfiguration.Enabled = TryGetValue<bool>(rawConfiguration, "enabled");
serviceConfiguration.GrpcPort = TryGetValue<int>(rawConfiguration, "grpc-port");
serviceConfiguration.HttpPort = TryGetValue<int>(rawConfiguration, "http-port");
serviceConfiguration.MetricsPort = TryGetValue<int>(rawConfiguration, "metrics-port");
serviceConfiguration.ProfilePort = TryGetValue<int>(rawConfiguration, "profile-port");
}
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.AppMaxConcurrency = TryGetValue<int>(rawConfiguration, "app-max-concurrency");
commonConfiguration.AppProtocol = TryGetValue(rawConfiguration, "app-protocol");
commonConfiguration.AppSsl = TryGetValue<bool>(rawConfiguration, "app-ssl");
commonConfiguration.ComponentsPath = TryGetValue(rawConfiguration, "components-path");
commonConfiguration.Config = TryGetValue(rawConfiguration, "config");
commonConfiguration.EnableProfiling = TryGetValue<bool>(rawConfiguration, "enable-profiling");
commonConfiguration.HttpMaxRequestSize = TryGetValue<int>(rawConfiguration, "http-max-request-size");
commonConfiguration.LogLevel = TryGetValue(rawConfiguration, "log-level");
commonConfiguration.PlacementPort = TryGetValue<int>(rawConfiguration, "placement-port");
}
private static string? TryGetValue(IDictionary<string, object> rawConfiguration, string name)
{
return rawConfiguration.TryGetValue(name, out var obj) && obj?.ToString() is string
? obj.ToString()
: null;
}
private static T? TryGetValue<T>(IDictionary<string, object> rawConfiguration, string name)
where T : struct
{
if (rawConfiguration.TryGetValue(name, out var obj) && obj?.ToString() is string)
{
commonConfiguration.PlacementPort = customPlacementPort;
try
{
return (T?)Convert.ChangeType(obj.ToString(), typeof(T));
}
catch
{
// No-op.
}
}
return null;
}
}
}

Loading…
Cancel
Save