diff --git a/all.sln b/all.sln
index 2ebeb4c..12a7fb3 100644
--- a/all.sln
+++ b/all.sln
@@ -101,6 +101,13 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "PlacementSample", "Placemen
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PlacementSample", "samples\AspNetCore\PlacementSample\PlacementSample\PlacementSample.csproj", "{2FC86574-6A81-4E2B-A0D4-78D46528A917}"
EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "SchedulerSample", "SchedulerSample", "{8FD8AFF0-A56A-4BDC-B40E-F498AA147790}"
+ ProjectSection(SolutionItems) = preProject
+ samples\AspNetCore\SchedulerSample\README.md = samples\AspNetCore\SchedulerSample\README.md
+ EndProjectSection
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SchedulerSample", "samples\AspNetCore\SchedulerSample\SchedulerSample\SchedulerSample.csproj", "{C0B2943F-3EA8-43A9-8714-7D6B84AD788E}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -315,6 +322,18 @@ Global
{2FC86574-6A81-4E2B-A0D4-78D46528A917}.Release|x64.Build.0 = Release|Any CPU
{2FC86574-6A81-4E2B-A0D4-78D46528A917}.Release|x86.ActiveCfg = Release|Any CPU
{2FC86574-6A81-4E2B-A0D4-78D46528A917}.Release|x86.Build.0 = Release|Any CPU
+ {C0B2943F-3EA8-43A9-8714-7D6B84AD788E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {C0B2943F-3EA8-43A9-8714-7D6B84AD788E}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {C0B2943F-3EA8-43A9-8714-7D6B84AD788E}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {C0B2943F-3EA8-43A9-8714-7D6B84AD788E}.Debug|x64.Build.0 = Debug|Any CPU
+ {C0B2943F-3EA8-43A9-8714-7D6B84AD788E}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {C0B2943F-3EA8-43A9-8714-7D6B84AD788E}.Debug|x86.Build.0 = Debug|Any CPU
+ {C0B2943F-3EA8-43A9-8714-7D6B84AD788E}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {C0B2943F-3EA8-43A9-8714-7D6B84AD788E}.Release|Any CPU.Build.0 = Release|Any CPU
+ {C0B2943F-3EA8-43A9-8714-7D6B84AD788E}.Release|x64.ActiveCfg = Release|Any CPU
+ {C0B2943F-3EA8-43A9-8714-7D6B84AD788E}.Release|x64.Build.0 = Release|Any CPU
+ {C0B2943F-3EA8-43A9-8714-7D6B84AD788E}.Release|x86.ActiveCfg = Release|Any CPU
+ {C0B2943F-3EA8-43A9-8714-7D6B84AD788E}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -348,6 +367,8 @@ Global
{9F4DA8E9-F253-4312-A0BB-E2873A21C41A} = {7F6A5D8C-9780-4824-8284-CC3149683C70}
{AE430C04-78BD-4CAE-86D7-EBC599774D9C} = {DA3D8137-F2DD-465D-81AA-3CA5C75087D2}
{2FC86574-6A81-4E2B-A0D4-78D46528A917} = {AE430C04-78BD-4CAE-86D7-EBC599774D9C}
+ {8FD8AFF0-A56A-4BDC-B40E-F498AA147790} = {DA3D8137-F2DD-465D-81AA-3CA5C75087D2}
+ {C0B2943F-3EA8-43A9-8714-7D6B84AD788E} = {8FD8AFF0-A56A-4BDC-B40E-F498AA147790}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {E906E97D-7D56-4E02-A13F-1C48AEB47A88}
diff --git a/all.v3.ncrunchsolution b/all.v3.ncrunchsolution
index 94494bd..bc095f7 100644
--- a/all.v3.ncrunchsolution
+++ b/all.v3.ncrunchsolution
@@ -7,6 +7,7 @@
True
Run Solution Unit Tests automatically [SnapshotShared]
Optimised
+ True
True
diff --git a/samples/AspNetCore/ControllerSample/ControllerSample/appsettings.json b/samples/AspNetCore/ControllerSample/ControllerSample/appsettings.json
index d9d9a9b..37d58f2 100644
--- a/samples/AspNetCore/ControllerSample/ControllerSample/appsettings.json
+++ b/samples/AspNetCore/ControllerSample/ControllerSample/appsettings.json
@@ -1,10 +1,13 @@
{
- "Logging": {
- "LogLevel": {
- "Default": "Information",
- "Microsoft": "Warning",
- "Microsoft.Hosting.Lifetime": "Information"
- }
+ "Serilog": {
+ "MinimumLevel": "Debug"
},
- "AllowedHosts": "*"
-}
+ "Logging": {
+ "LogLevel": {
+ "Default": "Debug",
+ "Microsoft": "Warning",
+ "Microsoft.Hosting.Lifetime": "Information"
+ }
+ },
+ "AllowedHosts": "*"
+ }
diff --git a/samples/AspNetCore/PlacementSample/README.md b/samples/AspNetCore/PlacementSample/README.md
index ac72ff5..d7aea36 100644
--- a/samples/AspNetCore/PlacementSample/README.md
+++ b/samples/AspNetCore/PlacementSample/README.md
@@ -14,7 +14,7 @@ builder.Services.AddDaprSidekick(builder.Configuration)
.AddPlacement();
```
-Typically when installing Dapr in self-hosted mode, a Placement service container is added to Docker exposing the default port 6500. If this samle is run
+Typically when installing Dapr in self-hosted mode, a Placement service container is added to Docker exposing the default port 6050. If this same is run
while that container is up it will be unable to start due to a port conflict. Instead a different port 6501 is assigned to Placement in configuration:
```json5
@@ -30,7 +30,7 @@ unless a specific remote address is defined. For example the following specifies
```json5
"Sidecar": {
- "PlacementHostAddress": "remote-host-1:6050,remote-host-2:6050,remote-host-3:6050"
+ "PlacementHostAddress": "remote-host-1:50005,remote-host-2:50005,remote-host-3:50005"
}
```
diff --git a/samples/AspNetCore/SchedulerSample/README.md b/samples/AspNetCore/SchedulerSample/README.md
new file mode 100644
index 0000000..213ab32
--- /dev/null
+++ b/samples/AspNetCore/SchedulerSample/README.md
@@ -0,0 +1,42 @@
+# Scheduler example
+
+This sample shows how Dapr Sidekick can be used to host the Dapr Scheduler service alongside a Dapr Sidecar instance. Sidekick will launch the Scheduler service and wait for it
+to enter a healthy running state, then will launch the Sidecar alongside. The Sidecar will be configured to use the launched Scheduler service by default.
+As with the Sidecar, the Scheduler service will be continually monitored and maintained for the lifetime of the application.
+
+## How Dapr Sidekick was added
+
+This is an ASP.NET Core minimal Web API project. Dapr Sidekick was added to the [Program.cs](SchedulerSample\Program.cs) file as follows:
+
+```csharp
+// Add Dapr Sidekick with Scheduler
+builder.Services.AddDaprSidekick(builder.Configuration)
+ .AddScheduler();
+```
+
+Typically when installing Dapr in self-hosted mode, a Scheduler service container is added to Docker exposing the default port 6060. If this same is run
+while that container is up it will be unable to start due to a port conflict. Instead a different port 6061 is assigned to Scheduler in configuration:
+
+```json5
+"Scheduler": {
+ "RuntimeDirectory": "scheduler",
+ "Id": "dapr-scheduler-server-0", // Optional unique identifier when used in a cluster
+ "Port": 6061 // To avoid conflicts with local Dapr Scheduler container. Sidecar will use this automatically as well.
+}
+```
+
+By default the Sidecar that is launched alongside the Scheduler service will look for the Scheduler service locally on this custom port,
+unless a specific remote address is defined. For example the following specifies a three-host remote Scheduler cluster:
+
+```json5
+"Sidecar": {
+ "SchedulerHostAddress": "remote-host-1:50006,remote-host-2:50006,remote-host-3:50006"
+}
+```
+
+## Running the sample
+
+ To run the sample simply set `SchedulerSample` as the startup project and run it in Visual Studio,
+ it will launch first the Scheduler service then the Dapr sidecar, then open a browser and display
+ the configured launch options for both.
+
diff --git a/samples/AspNetCore/SchedulerSample/SchedulerSample/Program.cs b/samples/AspNetCore/SchedulerSample/SchedulerSample/Program.cs
new file mode 100644
index 0000000..c21ef7c
--- /dev/null
+++ b/samples/AspNetCore/SchedulerSample/SchedulerSample/Program.cs
@@ -0,0 +1,39 @@
+using Man.Dapr.Sidekick;
+using Serilog;
+
+// Add Serilog for enhanced console logging.
+Log.Logger = new LoggerConfiguration()
+ .Enrich.FromLogContext()
+ .WriteTo.Console()
+ .CreateLogger();
+
+var builder = WebApplication.CreateBuilder(args);
+
+builder.Services.AddControllers();
+
+// Add Dapr Sidekick with Scheduler
+builder.Services.AddDaprSidekick(builder.Configuration)
+ .AddScheduler();
+
+builder.Host.UseSerilog();
+
+var app = builder.Build();
+
+app.MapGet("/status", (IDaprSidecarHost sidecarHost, IDaprSchedulerHost schedulerHost) => Results.Ok(new
+{
+ sidecar = new
+ {
+ process = sidecarHost.GetProcessInfo(), // Information about the sidecar process such as if it is running
+ options = sidecarHost.GetProcessOptions() // The sidecar options if running, including ports and locations
+ },
+ scheduler = new
+ {
+ process = schedulerHost.GetProcessInfo(), // Information about the sentry process such as if it is running
+ options = schedulerHost.GetProcessOptions() // The sentry options if running, including ports and locations
+ },
+}));
+
+// For Dapr
+app.MapHealthChecks("/health");
+
+app.Run();
diff --git a/samples/AspNetCore/SchedulerSample/SchedulerSample/Properties/launchSettings.json b/samples/AspNetCore/SchedulerSample/SchedulerSample/Properties/launchSettings.json
new file mode 100644
index 0000000..45c3fef
--- /dev/null
+++ b/samples/AspNetCore/SchedulerSample/SchedulerSample/Properties/launchSettings.json
@@ -0,0 +1,13 @@
+{
+ "profiles": {
+ "SchedulerSample": {
+ "commandName": "Project",
+ "launchBrowser": true,
+ "launchUrl": "status",
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "Development"
+ },
+ "applicationUrl": "http://localhost:5000"
+ }
+ }
+}
\ No newline at end of file
diff --git a/samples/AspNetCore/SchedulerSample/SchedulerSample/SchedulerSample.csproj b/samples/AspNetCore/SchedulerSample/SchedulerSample/SchedulerSample.csproj
new file mode 100644
index 0000000..ef1bd3f
--- /dev/null
+++ b/samples/AspNetCore/SchedulerSample/SchedulerSample/SchedulerSample.csproj
@@ -0,0 +1,26 @@
+
+
+
+ net8.0
+ enable
+ enable
+
+
+
+
+
+
+
+
+
+
+
+
+ Always
+
+
+ Always
+
+
+
+
diff --git a/samples/AspNetCore/SchedulerSample/SchedulerSample/appsettings.Development.json b/samples/AspNetCore/SchedulerSample/SchedulerSample/appsettings.Development.json
new file mode 100644
index 0000000..0c208ae
--- /dev/null
+++ b/samples/AspNetCore/SchedulerSample/SchedulerSample/appsettings.Development.json
@@ -0,0 +1,8 @@
+{
+ "Logging": {
+ "LogLevel": {
+ "Default": "Information",
+ "Microsoft.AspNetCore": "Warning"
+ }
+ }
+}
diff --git a/samples/AspNetCore/SchedulerSample/SchedulerSample/appsettings.json b/samples/AspNetCore/SchedulerSample/SchedulerSample/appsettings.json
new file mode 100644
index 0000000..d2f67e0
--- /dev/null
+++ b/samples/AspNetCore/SchedulerSample/SchedulerSample/appsettings.json
@@ -0,0 +1,19 @@
+{
+ "DaprSidekick": {
+ "Sidecar": {
+ "RuntimeDirectory": "dapr"
+ },
+ "Scheduler": {
+ "RuntimeDirectory": "scheduler",
+ "Id": "dapr-scheduler-server-0", // Optional unique identifier when used in a cluster
+ "Port": 6061 // To avoid conflicts with local Dapr Scheduler container. Sidecar will use this automatically as well.
+ }
+ },
+ "Logging": {
+ "LogLevel": {
+ "Default": "Information",
+ "Microsoft.AspNetCore": "Warning"
+ }
+ },
+ "AllowedHosts": "*"
+}
diff --git a/samples/AspNetCore/SchedulerSample/SchedulerSample/dapr/config.yaml b/samples/AspNetCore/SchedulerSample/SchedulerSample/dapr/config.yaml
new file mode 100644
index 0000000..4b11e3a
--- /dev/null
+++ b/samples/AspNetCore/SchedulerSample/SchedulerSample/dapr/config.yaml
@@ -0,0 +1,8 @@
+apiVersion: dapr.io/v1alpha1
+kind: Configuration
+metadata:
+ name: daprsystem
+ namespace: default
+spec:
+ mtls:
+ enabled: true
\ No newline at end of file
diff --git a/samples/AspNetCore/SchedulerSample/SchedulerSample/scheduler/config.yaml b/samples/AspNetCore/SchedulerSample/SchedulerSample/scheduler/config.yaml
new file mode 100644
index 0000000..60702f9
--- /dev/null
+++ b/samples/AspNetCore/SchedulerSample/SchedulerSample/scheduler/config.yaml
@@ -0,0 +1,6 @@
+apiVersion: dapr.io/v1alpha1
+kind: Configuration
+metadata:
+ name: daprsystem
+ namespace: default
+spec:
diff --git a/src/Man.Dapr.Sidekick.AspNetCore/DaprSidekickBuilder.cs b/src/Man.Dapr.Sidekick.AspNetCore/DaprSidekickBuilder.cs
index e15ac3e..48ba9cf 100644
--- a/src/Man.Dapr.Sidekick.AspNetCore/DaprSidekickBuilder.cs
+++ b/src/Man.Dapr.Sidekick.AspNetCore/DaprSidekickBuilder.cs
@@ -1,6 +1,7 @@
using System.Linq;
using Man.Dapr.Sidekick.AspNetCore.Metrics;
using Man.Dapr.Sidekick.AspNetCore.Placement;
+using Man.Dapr.Sidekick.AspNetCore.Scheduler;
using Man.Dapr.Sidekick.AspNetCore.Sentry;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
@@ -33,6 +34,22 @@ public IDaprSidekickBuilder AddPlacement()
return this;
}
+ public IDaprSidekickBuilder AddScheduler()
+ {
+ // Add the scheduler host
+ _services.TryAddSingleton();
+ _services.TryAddHostedService();
+
+ // Add the health checks and metrics
+ _services.AddHealthChecks().AddDaprScheduler();
+ _services.AddSingleton();
+
+ // Override the default sidecar hosted service, to one that only starts when the Scheduler service is available.
+ ReplaceSidecarHostedService();
+
+ return this;
+ }
+
public IDaprSidekickBuilder AddSentry()
{
// Add the Sentry host
diff --git a/src/Man.Dapr.Sidekick.AspNetCore/IDaprSidekickBuilder.cs b/src/Man.Dapr.Sidekick.AspNetCore/IDaprSidekickBuilder.cs
index 1f3c5db..bf107ff 100644
--- a/src/Man.Dapr.Sidekick.AspNetCore/IDaprSidekickBuilder.cs
+++ b/src/Man.Dapr.Sidekick.AspNetCore/IDaprSidekickBuilder.cs
@@ -11,6 +11,12 @@ public interface IDaprSidekickBuilder
/// This instance to allow calls to be chained.
public IDaprSidekickBuilder AddPlacement();
+ ///
+ /// Adds the Dapr Scheduler service.
+ ///
+ /// This instance to allow calls to be chained.
+ public IDaprSidekickBuilder AddScheduler();
+
///
/// Adds the Dapr Sentry service.
///
diff --git a/src/Man.Dapr.Sidekick.AspNetCore/Metrics/DaprMetricsConstants.cs b/src/Man.Dapr.Sidekick.AspNetCore/Metrics/DaprMetricsConstants.cs
index 09e44f3..d09363d 100644
--- a/src/Man.Dapr.Sidekick.AspNetCore/Metrics/DaprMetricsConstants.cs
+++ b/src/Man.Dapr.Sidekick.AspNetCore/Metrics/DaprMetricsConstants.cs
@@ -13,6 +13,7 @@ public static class DaprMetricsConstants
public static readonly string DaprSidecarLabel = "dapr-sidecar";
public static readonly string DaprPlacementLabel = "dapr-placement";
+ public static readonly string DaprSchedulerLabel = "dapr-scheduler";
public static readonly string DaprSentryLabel = "dapr-sentry";
public static readonly string ExporterContentType = "text/plain; charset=utf-8";
diff --git a/src/Man.Dapr.Sidekick.AspNetCore/Scheduler/DaprSchedulerHealthCheck.cs b/src/Man.Dapr.Sidekick.AspNetCore/Scheduler/DaprSchedulerHealthCheck.cs
new file mode 100644
index 0000000..0601428
--- /dev/null
+++ b/src/Man.Dapr.Sidekick.AspNetCore/Scheduler/DaprSchedulerHealthCheck.cs
@@ -0,0 +1,11 @@
+namespace Man.Dapr.Sidekick.AspNetCore.Scheduler
+{
+ public class DaprSchedulerHealthCheck : DaprProcessHealthCheck
+ {
+ public DaprSchedulerHealthCheck(
+ IDaprSchedulerHost daprSchedulerHost)
+ : base(daprSchedulerHost)
+ {
+ }
+ }
+}
diff --git a/src/Man.Dapr.Sidekick.AspNetCore/Scheduler/DaprSchedulerHealthCheckBuilderExtensions.cs b/src/Man.Dapr.Sidekick.AspNetCore/Scheduler/DaprSchedulerHealthCheckBuilderExtensions.cs
new file mode 100644
index 0000000..77174d7
--- /dev/null
+++ b/src/Man.Dapr.Sidekick.AspNetCore/Scheduler/DaprSchedulerHealthCheckBuilderExtensions.cs
@@ -0,0 +1,31 @@
+using System.Collections.Generic;
+using Man.Dapr.Sidekick.AspNetCore.Metrics;
+using Man.Dapr.Sidekick.AspNetCore.Scheduler;
+using Microsoft.Extensions.Diagnostics.HealthChecks;
+
+namespace Microsoft.Extensions.DependencyInjection
+{
+ public static class DaprSchedulerHealthCheckBuilderExtensions
+ {
+ ///
+ /// Add a health check for the Dapr Scheduler.
+ ///
+ /// The .
+ /// The health check name. Optional. If null the type name 'dapr_Scheduler' will be used for the name.
+ ///
+ /// The that should be reported when the health check fails. Optional. If null then
+ /// the default status of will be reported.
+ ///
+ /// A list of tags that can be used to filter sets of health checks. Optional.
+ /// The to allow calls to be chained.
+ public static IHealthChecksBuilder AddDaprScheduler(
+ this IHealthChecksBuilder builder,
+ string name = default,
+ HealthStatus? failureStatus = default,
+ IEnumerable tags = default)
+ {
+ builder.AddCheck(name ?? DaprMetricsConstants.DaprSchedulerLabel, failureStatus, tags);
+ return builder;
+ }
+ }
+}
diff --git a/src/Man.Dapr.Sidekick.AspNetCore/Scheduler/DaprSchedulerHostedService.cs b/src/Man.Dapr.Sidekick.AspNetCore/Scheduler/DaprSchedulerHostedService.cs
new file mode 100644
index 0000000..0800f5a
--- /dev/null
+++ b/src/Man.Dapr.Sidekick.AspNetCore/Scheduler/DaprSchedulerHostedService.cs
@@ -0,0 +1,25 @@
+using System.Threading;
+using Man.Dapr.Sidekick.AspNetCore.Metrics;
+using Microsoft.Extensions.Options;
+
+namespace Man.Dapr.Sidekick.AspNetCore.Scheduler
+{
+ public class DaprSchedulerHostedService : DaprHostedService
+ {
+ public DaprSchedulerHostedService(
+ IDaprSchedulerHost daprSchedulerHost,
+ IOptionsMonitor optionsAccessor)
+ : base(daprSchedulerHost, optionsAccessor)
+ {
+ }
+
+ protected override void OnStarting(DaprOptions options, CancellationToken cancellationToken)
+ {
+ // Assign metrics
+ options.Scheduler ??= new DaprSchedulerOptions();
+ options.Scheduler.Metrics ??= new DaprMetricsOptions();
+ options.Scheduler.Metrics.SetLabel(DaprMetricsConstants.ServiceLabelName, options.Sidecar?.AppId);
+ options.Scheduler.Metrics.SetLabel(DaprMetricsConstants.AppLabelName, DaprMetricsConstants.DaprSchedulerLabel);
+ }
+ }
+}
diff --git a/src/Man.Dapr.Sidekick.AspNetCore/Scheduler/DaprSchedulerMetricsCollector.cs b/src/Man.Dapr.Sidekick.AspNetCore/Scheduler/DaprSchedulerMetricsCollector.cs
new file mode 100644
index 0000000..95759a4
--- /dev/null
+++ b/src/Man.Dapr.Sidekick.AspNetCore/Scheduler/DaprSchedulerMetricsCollector.cs
@@ -0,0 +1,12 @@
+using Man.Dapr.Sidekick.AspNetCore.Metrics;
+
+namespace Man.Dapr.Sidekick.AspNetCore.Scheduler
+{
+ public class DaprSchedulerMetricsCollector : DaprProcessHostPrometheusCollector
+ {
+ public DaprSchedulerMetricsCollector(IDaprSchedulerHost daprSchedulerHost)
+ : base(daprSchedulerHost)
+ {
+ }
+ }
+}
diff --git a/src/Man.Dapr.Sidekick.AspNetCore/Scheduler/DaprSchedulerSidecarHostedService.cs b/src/Man.Dapr.Sidekick.AspNetCore/Scheduler/DaprSchedulerSidecarHostedService.cs
new file mode 100644
index 0000000..c74a0b9
--- /dev/null
+++ b/src/Man.Dapr.Sidekick.AspNetCore/Scheduler/DaprSchedulerSidecarHostedService.cs
@@ -0,0 +1,54 @@
+using System;
+using System.Threading;
+using System.Threading.Tasks;
+using Man.Dapr.Sidekick.AspNetCore.Sidecar;
+using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.Options;
+
+namespace Man.Dapr.Sidekick.AspNetCore.Scheduler
+{
+ ///
+ /// A hosted service for managing the sidecar lifetime.
+ /// Specifically waits for the Scheduler service to start successfully and allocate ports/environment variables
+ /// before starting itself.
+ ///
+ public class DaprSchedulerSidecarHostedService : DaprSidecarHostedService
+ {
+ private readonly IDaprSchedulerHost _daprSchedulerHost;
+ private readonly ILogger _logger;
+
+ public DaprSchedulerSidecarHostedService(
+ IDaprSidecarHost daprSidecarHost,
+ IDaprSchedulerHost daprSchedulerHost,
+ IOptionsMonitor optionsAccessor,
+ ILogger logger,
+ IServiceProvider serviceProvider = null)
+ : base(daprSidecarHost, optionsAccessor, serviceProvider)
+ {
+ _daprSchedulerHost = daprSchedulerHost;
+ _logger = logger;
+ }
+
+ public override async Task StartAsync(CancellationToken cancellationToken)
+ {
+ await Task.Run(
+ async () =>
+ {
+ var processInfo = _daprSchedulerHost.GetProcessInfo();
+ while (!processInfo.IsRunning && processInfo.Status != Process.DaprProcessStatus.Disabled)
+ {
+ _logger.LogInformation("Dapr Sidecar process is waiting for the Dapr Scheduler process to finish starting up...");
+ await Task.Delay(250);
+ if (cancellationToken.IsCancellationRequested)
+ {
+ break;
+ }
+
+ processInfo = _daprSchedulerHost.GetProcessInfo();
+ }
+ },
+ cancellationToken)
+ .ContinueWith(_ => base.StartAsync(cancellationToken));
+ }
+ }
+}
diff --git a/src/Man.Dapr.Sidekick/DaprConstants.cs b/src/Man.Dapr.Sidekick/DaprConstants.cs
index 523280a..0cb5b4c 100644
--- a/src/Man.Dapr.Sidekick/DaprConstants.cs
+++ b/src/Man.Dapr.Sidekick/DaprConstants.cs
@@ -31,6 +31,9 @@ public class DaprConstants
public const string DaprPlacementMetricsPortEnvironmentVariable = "DAPR_PLACEMENT_METRICS_PORT";
public const string DaprPlacementPortEnvironmentVariable = "DAPR_PLACEMENT_PORT";
public const string DaprProfilePortEnvironmentVariable = "DAPR_PROFILE_PORT";
+ public const string DaprSchedulerHealthPortEnvironmentVariable = "DAPR_SCHEDULER_HEALTH_PORT";
+ public const string DaprSchedulerMetricsPortEnvironmentVariable = "DAPR_SCHEDULER_METRICS_PORT";
+ public const string DaprSchedulerPortEnvironmentVariable = "DAPR_SCHEDULER_PORT";
public const string DaprSentryMetricsPortEnvironmentVariable = "DAPR_SENTRY_METRICS_PORT";
public const string DaprTrustAnchorsEnvironmentVariable = "DAPR_TRUST_ANCHORS";
public const string NamespaceEnvironmentVariable = "NAMESPACE";
@@ -40,6 +43,7 @@ public class DaprConstants
// Processes
public const string DaprSidecarProcessName = "daprd";
+ public const string DaprSchedulerProcessName = "scheduler";
public const string DaprSentryProcessName = "sentry";
public const string DaprPlacementProcessName = "placement";
diff --git a/src/Man.Dapr.Sidekick/DaprSchedulerHost.cs b/src/Man.Dapr.Sidekick/DaprSchedulerHost.cs
new file mode 100644
index 0000000..0383163
--- /dev/null
+++ b/src/Man.Dapr.Sidekick/DaprSchedulerHost.cs
@@ -0,0 +1,14 @@
+using Man.Dapr.Sidekick.Http;
+using Man.Dapr.Sidekick.Logging;
+using Man.Dapr.Sidekick.Process;
+
+namespace Man.Dapr.Sidekick
+{
+ public sealed class DaprSchedulerHost : DaprProcessHost, IDaprSchedulerHost
+ {
+ public DaprSchedulerHost(IDaprProcessFactory daprProcessFactory, IDaprProcessHttpClientFactory daprHttpClientFactory, IDaprLoggerFactory loggerFactory)
+ : base(() => daprProcessFactory.CreateDaprSchedulerProcess(), daprHttpClientFactory, new DaprLogger(loggerFactory))
+ {
+ }
+ }
+}
diff --git a/src/Man.Dapr.Sidekick/IDaprSchedulerHost.cs b/src/Man.Dapr.Sidekick/IDaprSchedulerHost.cs
new file mode 100644
index 0000000..b111a87
--- /dev/null
+++ b/src/Man.Dapr.Sidekick/IDaprSchedulerHost.cs
@@ -0,0 +1,8 @@
+using Man.Dapr.Sidekick.Process;
+
+namespace Man.Dapr.Sidekick
+{
+ public interface IDaprSchedulerHost : IDaprProcessHost
+ {
+ }
+}
diff --git a/src/Man.Dapr.Sidekick/Options/DaprOptions.cs b/src/Man.Dapr.Sidekick/Options/DaprOptions.cs
index 2eb522d..b951dc8 100644
--- a/src/Man.Dapr.Sidekick/Options/DaprOptions.cs
+++ b/src/Man.Dapr.Sidekick/Options/DaprOptions.cs
@@ -15,6 +15,8 @@ public DaprOptions()
public DaprPlacementOptions Placement { get; set; }
+ public DaprSchedulerOptions Scheduler { get; set; }
+
public DaprSentryOptions Sentry { get; set; }
///
@@ -26,6 +28,7 @@ public DaprOptions()
var clone = (DaprOptions)base.Clone();
clone.Sidecar = Sidecar?.Clone();
clone.Placement = Placement?.Clone();
+ clone.Scheduler = Scheduler?.Clone();
clone.Sentry = Sentry?.Clone();
return clone;
}
diff --git a/src/Man.Dapr.Sidekick/Options/DaprPlacementOptions.cs b/src/Man.Dapr.Sidekick/Options/DaprPlacementOptions.cs
index da8729f..fdace79 100644
--- a/src/Man.Dapr.Sidekick/Options/DaprPlacementOptions.cs
+++ b/src/Man.Dapr.Sidekick/Options/DaprPlacementOptions.cs
@@ -21,6 +21,11 @@ public class DaprPlacementOptions : Options.DaprProcessOptions
///
public bool? EnableMetrics { get; set; }
+ ///
+ /// Gets or sets the listening address for the healthz server.
+ ///
+ public string HealthListenAddress { get; set; }
+
///
/// Gets or sets the HTTP port for the health server (default 8081).
///
@@ -41,11 +46,31 @@ public class DaprPlacementOptions : Options.DaprProcessOptions
///
public bool? InmemStoreEnabled { get; set; }
+ ///
+ /// Gets or sets the address for the placenment server to listen on.
+ ///
+ public string ListenAddress { get; set; }
+
+ ///
+ /// Gets or sets a value that determines if the placement tables are exposed on the healthz server (default false).
+ ///
+ public bool? MetadataEnabled { get; set; }
+
+ ///
+ /// Gets or sets the address for the metrics server (default "0.0.0.0").
+ ///
+ public string MetricsListenAddress { get; set; }
+
///
/// Gets or sets the port for the metrics server (default 9091).
///
public int? MetricsPort { get; set; }
+ ///
+ /// Gets or sets the runtime mode for the placement service (default "standalone").
+ ///
+ public string Mode { get; set; }
+
///
/// Gets or sets a value that determines if TLS should be enabled for the placement gRPC server.
///
@@ -66,6 +91,11 @@ public class DaprPlacementOptions : Options.DaprProcessOptions
///
public int? ReplicationFactor { get; set; }
+ ///
+ /// Gets or sets a value that determines if TLS should be enabled for the placement gRPC server.
+ ///
+ public bool? TlsEnabled { get; set; }
+
///
/// Gets or sets the filepath to the trust anchors for the Dapr control plane (default "/var/run/secrets/dapr.io/tls/ca.crt").
/// Available since 1.12.0.
diff --git a/src/Man.Dapr.Sidekick/Options/DaprSchedulerOptions.cs b/src/Man.Dapr.Sidekick/Options/DaprSchedulerOptions.cs
new file mode 100644
index 0000000..9112bbb
--- /dev/null
+++ b/src/Man.Dapr.Sidekick/Options/DaprSchedulerOptions.cs
@@ -0,0 +1,149 @@
+using System;
+
+namespace Man.Dapr.Sidekick
+{
+ public class DaprSchedulerOptions : Options.DaprProcessOptions
+ {
+ ///
+ /// Gets or sets the path to the credentials directory holding the issuer data.
+ /// If not specified this will default to a directory called "certs" under the runtime folder.
+ /// Provided for backwards compatibility logic only.
+ ///
+ public string CertsDirectory { get; set; }
+
+ ///
+ /// Gets or sets custom arguments for the process. These will be appended "as-is" after all other arguments specified through these options.
+ ///
+ public string CustomArguments { get; set; }
+
+ ///
+ /// Gets or sets a value that determines if Prometheus metrics are enabled in the scheduler service (default true).
+ ///
+ public bool? EnableMetrics { get; set; }
+
+ ///
+ /// Gets or sets the ports for etcd client http communication.
+ ///
+ public string EtcdClientHttpPorts { get; set; }
+
+ ///
+ /// Gets or sets the ports for etcd client communication (default [dapr-scheduler-server-0=2379]).
+ ///
+ public string EtcdClientPorts { get; set; }
+
+ ///
+ /// Gets or sets the compaction mode for etcd. Can be 'periodic' or 'revision' (default "periodic").
+ ///
+ public string EtcdCompactionMode { get; set; }
+
+ ///
+ /// Gets or sets the compaction retention for etcd.
+ /// Can express time or number of revisions, depending on the value of (default "24h").
+ ///
+ public string EtcdCompactionRetention { get; set; }
+
+ ///
+ /// Gets or sets the directory to store scheduler etcd data (default "./data").
+ ///
+ public string EtcdDataDir { get; set; }
+
+ ///
+ /// Gets or sets the space quota for etcd (default 2147483648).
+ ///
+ public int? EtcdSpaceQuota { get; set; }
+
+ ///
+ /// Gets or sets the listening address for the healthz server.
+ ///
+ public string HealthListenAddress { get; set; }
+
+ ///
+ /// Gets or sets the HTTP port for the health server (default 8082).
+ ///
+ public int? HealthPort { get; set; }
+
+ ///
+ /// Gets or sets the scheduler server ID (default "dapr-scheduler-server-0").
+ ///
+ public string Id { get; set; }
+
+ ///
+ /// Gets or sets the raft cluster peers (default "dapr-scheduler-server-0=http://localhost:2380").
+ ///
+ public string InitialCluster { get; set; }
+
+ ///
+ /// Gets or sets the address for the scheduler server to listen on.
+ ///
+ public string ListenAddress { get; set; }
+
+ ///
+ /// Gets or sets the address for the metrics server (default "0.0.0.0").
+ ///
+ public string MetricsListenAddress { get; set; }
+
+ ///
+ /// Gets or sets the port for the metrics server (default 9093).
+ ///
+ public int? MetricsPort { get; set; }
+
+ ///
+ /// Gets or sets the runtime mode for the scheduler service (default "standalone").
+ ///
+ public string Mode { get; set; }
+
+ ///
+ /// Gets or sets the gRPC port for the scheduler service (defaults to 6060 on Windows and 50006 on other platforms).
+ ///
+ public int? Port { get; set; }
+
+ ///
+ /// Gets or sets the total number of scheduler replicas in the cluster (default 1).
+ ///
+ public uint? ReplicaCount { get; set; }
+
+ ///
+ /// Gets or sets a value that determines if TLS should be enabled for the scheduler gRPC server.
+ ///
+ public bool? TlsEnabled { get; set; }
+
+ ///
+ /// Gets or sets the filepath to the trust anchors for the Dapr control plane (default "/var/run/secrets/dapr.io/tls/ca.crt").
+ ///
+ public string TrustAnchorsFile { get; set; }
+
+ ///
+ /// Gets or sets the trust domain for the Dapr control plane (default "localhost").
+ ///
+ public string TrustDomain { get; set; }
+
+ ///
+ /// Creates a deep clone of this instance.
+ ///
+ /// A deep clone of this insteance.
+ public new DaprSchedulerOptions Clone() => (DaprSchedulerOptions)base.Clone();
+
+ protected override bool AddHealthUri(UriBuilder builder)
+ {
+ if (!HealthPort.HasValue)
+ {
+ return false;
+ }
+
+ builder.Port = HealthPort.Value;
+ builder.Path = "healthz";
+ return true;
+ }
+
+ protected override bool AddMetricsUri(UriBuilder builder)
+ {
+ if (!MetricsPort.HasValue || EnableMetrics == false)
+ {
+ return false;
+ }
+
+ builder.Port = MetricsPort.Value;
+ return true;
+ }
+ }
+}
diff --git a/src/Man.Dapr.Sidekick/Options/DaprSidecarOptions.cs b/src/Man.Dapr.Sidekick/Options/DaprSidecarOptions.cs
index 5a979a9..396a09e 100644
--- a/src/Man.Dapr.Sidekick/Options/DaprSidecarOptions.cs
+++ b/src/Man.Dapr.Sidekick/Options/DaprSidecarOptions.cs
@@ -153,6 +153,11 @@ public DaprSidecarOptions()
///
public bool? Profiling { get; set; }
+ ///
+ /// Gets or sets the scheduler service host address. This is typically a comma-separated list of host:port endpoints.
+ ///
+ public string SchedulerHostAddress { get; set; }
+
///
/// Gets or sets the address for the Sentry CA service.
///
@@ -173,6 +178,11 @@ public DaprSidecarOptions()
///
public bool? UseDefaultPlacementHostAddress { get; set; }
+ ///
+ /// Gets or sets a value that determines if the default Scheduler Host address is used when is not specified. Defaults to true.
+ ///
+ public bool? UseDefaultSchedulerHostAddress { get; set; }
+
///
/// Gets the address of the metdata endpoint, such as http://127.0.0.1:3500/v1.0/metadata.
///
diff --git a/src/Man.Dapr.Sidekick/Process/DaprPlacementProcess.cs b/src/Man.Dapr.Sidekick/Process/DaprPlacementProcess.cs
index 64fafcc..107cdc8 100644
--- a/src/Man.Dapr.Sidekick/Process/DaprPlacementProcess.cs
+++ b/src/Man.Dapr.Sidekick/Process/DaprPlacementProcess.cs
@@ -7,13 +7,18 @@ internal class DaprPlacementProcess : DaprProcess, IDaprPl
{
private const string CertChainArgument = "certchain";
private const string EnableMetricsArgument = "enable-metrics";
+ private const string HealthzListenAddressArgument = "healthz-listen-address";
private const string HealthzPortArgument = "healthz-port";
private const string IdArgument = "id";
private const string InitialClusterArgument = "initial-cluster";
private const string InmemStoreEnabledArgument = "inmem-store-enabled";
+ private const string ListenAddressArgument = "listen-address";
private const string LogAsJsonArgument = "log-as-json";
private const string LogLevelArgument = "log-level";
+ private const string MetadataEnabledArgument = "metadata-enabled";
+ private const string MetricsListenAddressArgument = "metrics-listen-address";
private const string MetricsPortArgument = "metrics-port";
+ private const string ModeArgument = "mode";
private const string PortArgument = "port";
private const string RaftLogstorePathArgument = "raft-logstore-path";
private const string ReplicationFactorArgument = "replicationfactor";
@@ -71,17 +76,22 @@ protected override void AssignLocations(DaprPlacementOptions options, string dap
protected override void AddCommandLineArguments(DaprPlacementOptions source, CommandLineArgumentBuilder builder) => builder
.Add(CertChainArgument, source.CertsDirectory)
.Add(EnableMetricsArgument, source.EnableMetrics)
+ .Add(HealthzListenAddressArgument, source.HealthListenAddress)
.Add(HealthzPortArgument, source.HealthPort)
.Add(IdArgument, source.Id)
.Add(InitialClusterArgument, source.InitialCluster)
.Add(InmemStoreEnabledArgument, source.InmemStoreEnabled)
+ .Add(ListenAddressArgument, source.ListenAddress)
.Add(LogAsJsonArgument, true) // All logging must be JSON
.Add(LogLevelArgument, source.LogLevel)
+ .Add(MetadataEnabledArgument, source.MetadataEnabled)
+ .Add(MetricsListenAddressArgument, source.MetricsListenAddress)
.Add(MetricsPortArgument, source.MetricsPort)
+ .Add(ModeArgument, source.Mode)
.Add(PortArgument, source.Port)
.Add(RaftLogstorePathArgument, source.RaftLogstorePath)
.Add(ReplicationFactorArgument, source.ReplicationFactor)
- .Add(TlsEnabledArgument, source.Mtls)
+ .Add(TlsEnabledArgument, source.TlsEnabled ?? source.Mtls) // Check Mtls for backwards comaptibility
.Add(TrustAnchorsFileArgument, source.TrustAnchorsFile)
.Add(TrustDomainArgument, source.TrustDomain)
.Add(source.CustomArguments, requiresValue: false);
@@ -103,6 +113,10 @@ protected override void ParseCommandLineArgument(DaprPlacementOptions target, st
target.EnableMetrics = !bool.TryParse(value, out var enableMetrics) || enableMetrics;
break;
+ case HealthzListenAddressArgument:
+ target.HealthListenAddress = value;
+ break;
+
case HealthzPortArgument:
target.HealthPort = int.TryParse(value, out var healthzPort) ? (int?)healthzPort : null;
break;
@@ -119,14 +133,30 @@ protected override void ParseCommandLineArgument(DaprPlacementOptions target, st
target.InmemStoreEnabled = !bool.TryParse(value, out var inmemStoreEnabled) || inmemStoreEnabled;
break;
+ case ListenAddressArgument:
+ target.ListenAddress = value;
+ break;
+
case LogLevelArgument:
target.LogLevel = value;
break;
+ case MetadataEnabledArgument:
+ target.MetadataEnabled = !bool.TryParse(value, out var metadataEnabled) || metadataEnabled;
+ break;
+
+ case MetricsListenAddressArgument:
+ target.MetricsListenAddress = value;
+ break;
+
case MetricsPortArgument:
target.MetricsPort = int.TryParse(value, out var metricsPort) ? (int?)metricsPort : null;
break;
+ case ModeArgument:
+ target.Mode = value;
+ break;
+
case PortArgument:
target.Port = int.TryParse(value, out var port) ? (int?)port : null;
break;
@@ -140,7 +170,15 @@ protected override void ParseCommandLineArgument(DaprPlacementOptions target, st
break;
case TlsEnabledArgument:
- target.Mtls = !bool.TryParse(value, out var tlsEnabled) || tlsEnabled;
+ target.TlsEnabled = !bool.TryParse(value, out var tlsEnabled) || tlsEnabled;
+ break;
+
+ case TrustAnchorsFileArgument:
+ target.TrustAnchorsFile = value;
+ break;
+
+ case TrustDomainArgument:
+ target.TrustDomain = value;
break;
}
}
diff --git a/src/Man.Dapr.Sidekick/Process/DaprProcessFactory.cs b/src/Man.Dapr.Sidekick/Process/DaprProcessFactory.cs
index 1e1004c..dfb2c6a 100644
--- a/src/Man.Dapr.Sidekick/Process/DaprProcessFactory.cs
+++ b/src/Man.Dapr.Sidekick/Process/DaprProcessFactory.cs
@@ -6,6 +6,8 @@ public class DaprProcessFactory : IDaprProcessFactory
public IDaprPlacementProcess CreateDaprPlacementProcess() => new DaprPlacementProcess();
+ public IDaprSchedulerProcess CreateDaprSchedulerProcess() => new DaprSchedulerProcess();
+
public IDaprSentryProcess CreateDaprSentryProcess() => new DaprSentryProcess();
}
}
diff --git a/src/Man.Dapr.Sidekick/Process/DaprSchedulerProcess.cs b/src/Man.Dapr.Sidekick/Process/DaprSchedulerProcess.cs
new file mode 100644
index 0000000..6ea69aa
--- /dev/null
+++ b/src/Man.Dapr.Sidekick/Process/DaprSchedulerProcess.cs
@@ -0,0 +1,215 @@
+using System;
+using System.IO;
+
+namespace Man.Dapr.Sidekick.Process
+{
+ internal class DaprSchedulerProcess : DaprProcess, IDaprSchedulerProcess
+ {
+ private const string EnableMetricsArgument = "enable-metrics";
+ private const string EtcdClientHttpPortsArgument = "etcd-client-http-ports";
+ private const string EtcdClientPortsArgument = "etcd-client-ports";
+ private const string EtcdCompactionModeArgument = "etcd-compaction-mode";
+ private const string EtcdCompactionRetentionArgument = "etcd-compaction-retention";
+ private const string EtcdDataDirArgument = "etcd-data-dir";
+ private const string EtcdSpaceQuotaArgument = "etcd-space-quota";
+ private const string HealthzListenAddressArgument = "healthz-listen-address";
+ private const string HealthzPortArgument = "healthz-port";
+ private const string IdArgument = "id";
+ private const string InitialClusterArgument = "initial-cluster";
+ private const string ListenAddressArgument = "listen-address";
+ private const string LogAsJsonArgument = "log-as-json";
+ private const string LogLevelArgument = "log-level";
+ private const string MetricsListenAddressArgument = "metrics-listen-address";
+ private const string MetricsPortArgument = "metrics-port";
+ private const string ModeArgument = "mode";
+ private const string PortArgument = "port";
+ private const string ReplicaCountArgument = "replica-count";
+ private const string TlsEnabledArgument = "tls-enabled";
+ private const string TrustAnchorsFileArgument = "trust-anchors-file";
+ private const string TrustDomainArgument = "trust-domain";
+
+ public DaprSchedulerProcess()
+ : base(DaprConstants.DaprSchedulerProcessName)
+ {
+ }
+
+ protected override DaprSchedulerOptions GetProcessOptions(DaprOptions daprOptions)
+ {
+ // Get a clone of the current options as we will be modifying it.
+ var options = daprOptions.Scheduler?.Clone() ?? new DaprSchedulerOptions();
+ options.EnrichFrom(daprOptions);
+
+ // The scheduler service needs to maintain the same ports on restarts to ensure if either
+ // it or the sidecar are restarted out of sequence the port allocations remain the same.
+ // This should be fine in general as the scheduler service normally runs on a dedicated host.
+ options.RetainPortsOnRestart = true;
+
+ return options;
+ }
+
+ protected override void AssignPorts(PortAssignmentBuilder builder) =>
+ builder
+ .Add(x => x.HealthPort, 8082)
+ .Add(x => x.MetricsPort, 9093)
+ .Add(x => x.Port, DaprConstants.IsWindows ? 6060 : 50006);
+
+ protected override void AssignLocations(DaprSchedulerOptions options, string daprFolder)
+ {
+ // Certs directory - defaults to \certs
+ var certsDirectory = string.IsNullOrEmpty(options.CertsDirectory) ?
+ Path.Combine(daprFolder, DaprConstants.DaprCertsDirectory) :
+ Path.GetFullPath(options.CertsDirectory);
+
+ // Write any defined certificate options
+ WriteDefaultCertificates(certsDirectory, options);
+
+ // Pass the trust anchors file to scheduler
+ options.TrustAnchorsFile ??= Path.Combine(certsDirectory, DaprConstants.TrustAnchorsCertificateFilename);
+ }
+
+ protected override void AddCommandLineArguments(DaprSchedulerOptions source, CommandLineArgumentBuilder builder) => builder
+ .Add(EnableMetricsArgument, source.EnableMetrics)
+ .Add(EtcdClientHttpPortsArgument, source.EtcdClientHttpPorts)
+ .Add(EtcdClientPortsArgument, source.EtcdClientPorts)
+ .Add(EtcdCompactionModeArgument, source.EtcdCompactionMode)
+ .Add(EtcdCompactionRetentionArgument, source.EtcdCompactionRetention)
+ .Add(EtcdDataDirArgument, source.EtcdDataDir)
+ .Add(EtcdSpaceQuotaArgument, source.EtcdSpaceQuota)
+ .Add(HealthzListenAddressArgument, source.HealthListenAddress)
+ .Add(HealthzPortArgument, source.HealthPort)
+ .Add(IdArgument, source.Id)
+ .Add(InitialClusterArgument, source.InitialCluster)
+ .Add(ListenAddressArgument, source.ListenAddress)
+ .Add(LogAsJsonArgument, true) // All logging must be JSON
+ .Add(LogLevelArgument, source.LogLevel)
+ .Add(MetricsListenAddressArgument, source.MetricsListenAddress)
+ .Add(MetricsPortArgument, source.MetricsPort)
+ .Add(ModeArgument, source.Mode)
+ .Add(PortArgument, source.Port)
+ .Add(ReplicaCountArgument, source.ReplicaCount)
+ .Add(TlsEnabledArgument, source.TlsEnabled)
+ .Add(TrustAnchorsFileArgument, source.TrustAnchorsFile)
+ .Add(TrustDomainArgument, source.TrustDomain)
+ .Add(source.CustomArguments, requiresValue: false);
+
+ protected override void AddEnvironmentVariables(DaprSchedulerOptions source, EnvironmentVariableBuilder builder) => builder
+ .Add(DaprConstants.DaprSchedulerPortEnvironmentVariable, source.Port)
+ .Add(DaprConstants.DaprSchedulerHealthPortEnvironmentVariable, source.HealthPort)
+ .Add(DaprConstants.DaprSchedulerMetricsPortEnvironmentVariable, source.MetricsPort, () => source.EnableMetrics != false);
+
+ protected override void ParseCommandLineArgument(DaprSchedulerOptions target, string name, string value)
+ {
+ switch (name.ToLower())
+ {
+ case EnableMetricsArgument:
+ target.EnableMetrics = !bool.TryParse(value, out var enableMetrics) || enableMetrics;
+ break;
+
+ case EtcdClientHttpPortsArgument:
+ target.EtcdClientHttpPorts = value;
+ break;
+
+ case EtcdClientPortsArgument:
+ target.EtcdClientPorts = value;
+ break;
+
+ case EtcdCompactionModeArgument:
+ target.EtcdCompactionMode = value;
+ break;
+
+ case EtcdCompactionRetentionArgument:
+ target.EtcdCompactionRetention = value;
+ break;
+
+ case EtcdDataDirArgument:
+ target.EtcdDataDir = value;
+ break;
+
+ case EtcdSpaceQuotaArgument:
+ target.EtcdSpaceQuota = int.TryParse(value, out var etcdSpaceQuota) ? (int?)etcdSpaceQuota : null;
+ break;
+
+ case HealthzListenAddressArgument:
+ target.HealthListenAddress = value;
+ break;
+
+ case HealthzPortArgument:
+ target.HealthPort = int.TryParse(value, out var healthzPort) ? (int?)healthzPort : null;
+ break;
+
+ case IdArgument:
+ target.Id = value;
+ break;
+
+ case InitialClusterArgument:
+ target.InitialCluster = value;
+ break;
+
+ case ListenAddressArgument:
+ target.ListenAddress = value;
+ break;
+
+ case LogLevelArgument:
+ target.LogLevel = value;
+ break;
+
+ case MetricsListenAddressArgument:
+ target.MetricsListenAddress = value;
+ break;
+
+ case MetricsPortArgument:
+ target.MetricsPort = int.TryParse(value, out var metricsPort) ? (int?)metricsPort : null;
+ break;
+
+ case ModeArgument:
+ target.Mode = value;
+ break;
+
+ case PortArgument:
+ target.Port = int.TryParse(value, out var port) ? (int?)port : null;
+ break;
+
+ case ReplicaCountArgument:
+ target.ReplicaCount = uint.TryParse(value, out var replicaCount) ? (uint?)replicaCount : null;
+ break;
+
+ case TlsEnabledArgument:
+ target.TlsEnabled = !bool.TryParse(value, out var tlsEnabled) || tlsEnabled;
+ break;
+
+ case TrustAnchorsFileArgument:
+ target.TrustAnchorsFile = value;
+ break;
+
+ case TrustDomainArgument:
+ target.TrustDomain = value;
+ break;
+ }
+ }
+
+ protected override ProcessComparison CompareProcessOptions(DaprSchedulerOptions proposedProcessOptions, DaprSchedulerOptions existingProcessOptions, IProcess existingProcess)
+ {
+ if (!string.Equals(proposedProcessOptions.Id, existingProcessOptions.Id, StringComparison.InvariantCultureIgnoreCase))
+ {
+ // Processes are for different app-ids
+ return ProcessComparison.None;
+ }
+ else if (proposedProcessOptions.Port.HasValue && existingProcessOptions.Port.HasValue && proposedProcessOptions.Port == existingProcessOptions.Port)
+ {
+ // Same app-id, same app port. Attachable.
+ Logger?.LogDebug(
+ "Found attachable process {DaprProcessName} PID:{DaprProcessId} with proposed id {DaprSchedulerId} and port {DaprSchedulerPort}",
+ existingProcess.Name,
+ existingProcess.Id,
+ proposedProcessOptions.Id,
+ proposedProcessOptions.Port);
+ return ProcessComparison.Attachable;
+ }
+ else
+ {
+ // Duplicate
+ return ProcessComparison.Duplicate;
+ }
+ }
+ }
+}
diff --git a/src/Man.Dapr.Sidekick/Process/DaprSidecarProcess.cs b/src/Man.Dapr.Sidekick/Process/DaprSidecarProcess.cs
index 13f260b..2176ba5 100644
--- a/src/Man.Dapr.Sidekick/Process/DaprSidecarProcess.cs
+++ b/src/Man.Dapr.Sidekick/Process/DaprSidecarProcess.cs
@@ -29,6 +29,7 @@ internal class DaprSidecarProcess : DaprProcess, IDaprSideca
private const string ModeArgument = "mode";
private const string PlacementHostAddressArgument = "placement-host-address";
private const string ProfilePortArgument = "profile-port";
+ private const string SchedulerHostAddressArgument = "scheduler-host-address";
private const string SentryAddressArgument = "sentry-address";
public DaprSidecarProcess()
@@ -61,6 +62,16 @@ protected override DaprSidecarOptions GetProcessOptions(DaprOptions daprOptions)
options.PlacementHostAddress = $"{DaprConstants.LocalhostAddress}:{port}";
}
+ // Set local scheduler information
+ if (string.IsNullOrEmpty(options.SchedulerHostAddress) && options.UseDefaultSchedulerHostAddress != false)
+ {
+ // If we have a local enabled scheduler running in this solution, then use that port
+ // else use the defaults from the Dapr CLI - 6060 (Windows) or 50006 (Non-Windows)
+ var port = daprOptions.Scheduler?.Enabled != false && daprOptions.Scheduler?.Port != null ? daprOptions.Scheduler?.Port.Value :
+ DaprConstants.IsWindows ? 6060 : 50006;
+ options.SchedulerHostAddress = $"{DaprConstants.LocalhostAddress}:{port}";
+ }
+
// Make sure we have a namespace
if (string.IsNullOrEmpty(options.Namespace) && options.Mtls == true)
{
@@ -124,6 +135,7 @@ protected override void AddCommandLineArguments(DaprSidecarOptions source, Comma
.Add(ModeArgument, source.Mode)
.Add(PlacementHostAddressArgument, source.PlacementHostAddress)
.Add(ProfilePortArgument, source.ProfilePort, predicate: () => source.Profiling == true)
+ .Add(SchedulerHostAddressArgument, source.SchedulerHostAddress)
.Add(SentryAddressArgument, source.SentryAddress, predicate: () => !source.SentryAddress.IsNullOrWhiteSpaceEx())
.Add(ConfigFileArgument, source.ConfigFile, predicate: () => File.Exists(source.ConfigFile))
.Add(ResourcesPathArgument, source.ResourcesDirectory, predicate: () => Directory.Exists(source.ResourcesDirectory))
@@ -229,6 +241,10 @@ protected override void ParseCommandLineArgument(DaprSidecarOptions target, stri
target.ProfilePort = int.TryParse(value, out var profilePort) ? (int?)profilePort : null;
break;
+ case SchedulerHostAddressArgument:
+ target.SchedulerHostAddress = value;
+ break;
+
case SentryAddressArgument:
target.SentryAddress = value;
break;
diff --git a/src/Man.Dapr.Sidekick/Process/IDaprProcessFactory.cs b/src/Man.Dapr.Sidekick/Process/IDaprProcessFactory.cs
index c18df3a..b7e07d2 100644
--- a/src/Man.Dapr.Sidekick/Process/IDaprProcessFactory.cs
+++ b/src/Man.Dapr.Sidekick/Process/IDaprProcessFactory.cs
@@ -6,6 +6,8 @@ public interface IDaprProcessFactory
IDaprPlacementProcess CreateDaprPlacementProcess();
+ IDaprSchedulerProcess CreateDaprSchedulerProcess();
+
IDaprSentryProcess CreateDaprSentryProcess();
}
}
diff --git a/src/Man.Dapr.Sidekick/Process/IDaprSchedulerProcess.cs b/src/Man.Dapr.Sidekick/Process/IDaprSchedulerProcess.cs
new file mode 100644
index 0000000..48af783
--- /dev/null
+++ b/src/Man.Dapr.Sidekick/Process/IDaprSchedulerProcess.cs
@@ -0,0 +1,6 @@
+namespace Man.Dapr.Sidekick.Process
+{
+ public interface IDaprSchedulerProcess : IDaprProcess
+ {
+ }
+}
diff --git a/tests/Man.Dapr.Sidekick.Tests/Options/DaprSidecarOptionsTests.cs b/tests/Man.Dapr.Sidekick.Tests/Options/DaprSidecarOptionsTests.cs
index 293b522..e0512ea 100644
--- a/tests/Man.Dapr.Sidekick.Tests/Options/DaprSidecarOptionsTests.cs
+++ b/tests/Man.Dapr.Sidekick.Tests/Options/DaprSidecarOptionsTests.cs
@@ -147,6 +147,7 @@ private static void Compare(DaprSidecarOptions source, DaprSidecarOptions target
Assert.That(target.PlacementHostAddress, Is.EqualTo(source.PlacementHostAddress));
Assert.That(target.ProfilePort, Is.EqualTo(source.ProfilePort));
Assert.That(target.Profiling, Is.EqualTo(source.Profiling));
+ Assert.That(target.SchedulerHostAddress, Is.EqualTo(source.SchedulerHostAddress));
Assert.That(target.SentryAddress, Is.EqualTo(source.SentryAddress));
Assert.That(target.UseDefaultAppApiToken, Is.EqualTo(source.UseDefaultAppApiToken));
Assert.That(target.UseDefaultDaprApiToken, Is.EqualTo(source.UseDefaultDaprApiToken));
@@ -179,6 +180,7 @@ private static void Compare(DaprSidecarOptions source, DaprSidecarOptions target
PlacementHostAddress = "PlacementHostAddress",
ProfilePort = 800,
Profiling = true,
+ SchedulerHostAddress = "SchedulerHostAddress",
SentryAddress = "SentryAddress",
UseDefaultAppApiToken = true,
UseDefaultDaprApiToken = true
diff --git a/tests/Man.Dapr.Sidekick.Tests/Process/DaprPlacementProcessTests.cs b/tests/Man.Dapr.Sidekick.Tests/Process/DaprPlacementProcessTests.cs
index ed5fba1..21cf5db 100644
--- a/tests/Man.Dapr.Sidekick.Tests/Process/DaprPlacementProcessTests.cs
+++ b/tests/Man.Dapr.Sidekick.Tests/Process/DaprPlacementProcessTests.cs
@@ -124,16 +124,22 @@ public void Should_add_all_arguments()
{
CertsDirectory = "CertsDirectory",
EnableMetrics = true,
+ HealthListenAddress = "HealthzListenAddress",
HealthPort = 1234,
Id = "Id",
InitialCluster = "InitialCluster",
InmemStoreEnabled = true,
LogLevel = "LogLevel",
+ MetadataEnabled = true,
+ MetricsListenAddress = "MetricsListenAddress",
MetricsPort = 2345,
+ Mode = "Mode",
Port = 3456,
RaftLogstorePath = "RaftLogstorePath",
ReplicationFactor = 100,
- Mtls = true,
+ TlsEnabled = true,
+ TrustAnchorsFile = "TrustAnchorsFile",
+ TrustDomain = "TrustDomain",
CustomArguments = "arg1 val1"
};
@@ -142,17 +148,23 @@ public void Should_add_all_arguments()
Assert.That(builder.ToString(), Is.EqualTo(
"--certchain CertsDirectory " +
"--enable-metrics " +
+ "--healthz-listen-address HealthzListenAddress " +
"--healthz-port 1234 " +
"--id Id " +
"--initial-cluster InitialCluster " +
"--inmem-store-enabled " +
"--log-as-json " +
"--log-level LogLevel " +
+ "--metadata-enabled " +
+ "--metrics-listen-address MetricsListenAddress " +
"--metrics-port 2345 " +
+ "--mode Mode " +
"--port 3456 " +
"--raft-logstore-path RaftLogstorePath " +
"--replicationfactor 100 " +
"--tls-enabled " +
+ "--trust-anchors-file TrustAnchorsFile " +
+ "--trust-domain TrustDomain " +
"--arg1 val1"));
}
}
@@ -190,29 +202,44 @@ public void Should_parse_all_arguments()
p.ParseCommandLineArgument(options, "certchain", "CertsDirectory");
p.ParseCommandLineArgument(options, "enable-metrics", null);
+ p.ParseCommandLineArgument(options, "healthz-listen-address", "HealthzListenAddress");
p.ParseCommandLineArgument(options, "healthz-port", "1234");
p.ParseCommandLineArgument(options, "id", "Id");
p.ParseCommandLineArgument(options, "initial-cluster", "InitialCluster");
p.ParseCommandLineArgument(options, "inmem-store-enabled", null);
+ p.ParseCommandLineArgument(options, "listen-address", "ListenAddress");
p.ParseCommandLineArgument(options, "log-level", "LogLevel");
+ p.ParseCommandLineArgument(options, "metadata-enabled", null);
+ p.ParseCommandLineArgument(options, "metrics-listen-address", "MetricsListenAddress");
p.ParseCommandLineArgument(options, "metrics-port", "2345");
+ p.ParseCommandLineArgument(options, "mode", "Mode");
p.ParseCommandLineArgument(options, "port", "3456");
p.ParseCommandLineArgument(options, "raft-logstore-path", "RaftLogstorePath");
p.ParseCommandLineArgument(options, "replicationfactor", "100");
p.ParseCommandLineArgument(options, "tls-enabled", null);
+ p.ParseCommandLineArgument(options, "trust-anchors-file", "TrustAnchorsFile");
+ p.ParseCommandLineArgument(options, "trust-domain", "TrustDomain");
Assert.That(options.CertsDirectory, Is.EqualTo("CertsDirectory"));
Assert.That(options.EnableMetrics, Is.True);
+ Assert.That(options.HealthListenAddress, Is.EqualTo("HealthzListenAddress"));
Assert.That(options.HealthPort, Is.EqualTo(1234));
Assert.That(options.Id, Is.EqualTo("Id"));
Assert.That(options.InitialCluster, Is.EqualTo("InitialCluster"));
Assert.That(options.InmemStoreEnabled, Is.True);
+ Assert.That(options.ListenAddress, Is.EqualTo("ListenAddress"));
Assert.That(options.LogLevel, Is.EqualTo("LogLevel"));
+ Assert.That(options.MetadataEnabled, Is.True);
+ Assert.That(options.MetricsListenAddress, Is.EqualTo("MetricsListenAddress"));
Assert.That(options.MetricsPort, Is.EqualTo(2345));
+ Assert.That(options.Mtls, Is.Null);
+ Assert.That(options.Mode, Is.EqualTo("Mode"));
Assert.That(options.Port, Is.EqualTo(3456));
Assert.That(options.RaftLogstorePath, Is.EqualTo("RaftLogstorePath"));
Assert.That(options.ReplicationFactor, Is.EqualTo(100));
- Assert.That(options.Mtls, Is.True);
+ Assert.That(options.TlsEnabled, Is.True);
+ Assert.That(options.TrustAnchorsFile, Is.EqualTo("TrustAnchorsFile"));
+ Assert.That(options.TrustDomain, Is.EqualTo("TrustDomain"));
}
}
diff --git a/tests/Man.Dapr.Sidekick.Tests/Process/DaprSchedulerProcessTests.cs b/tests/Man.Dapr.Sidekick.Tests/Process/DaprSchedulerProcessTests.cs
new file mode 100644
index 0000000..66453e6
--- /dev/null
+++ b/tests/Man.Dapr.Sidekick.Tests/Process/DaprSchedulerProcessTests.cs
@@ -0,0 +1,323 @@
+using System.IO;
+using Man.Dapr.Sidekick.Logging;
+using NSubstitute;
+using NUnit.Framework;
+
+namespace Man.Dapr.Sidekick.Process
+{
+ public class DaprSchedulerProcessTests
+ {
+ public class GetProcessOptions
+ {
+ [Test]
+ public void Should_create_scheduler_section_if_null()
+ {
+ var p = new MockDaprSchedulerProcess();
+ var options = new DaprOptions();
+
+ Assert.That(options.Scheduler, Is.Null);
+
+ var newOptions = p.GetProcessOptions(options);
+ Assert.That(newOptions, Is.Not.Null);
+ }
+
+ [Test]
+ public void Should_use_existing_scheduler_section()
+ {
+ var p = new MockDaprSchedulerProcess();
+ var options = new DaprOptions
+ {
+ Scheduler = new DaprSchedulerOptions
+ {
+ Id = "TEST",
+ RetainPortsOnRestart = false
+ }
+ };
+
+ var newOptions = p.GetProcessOptions(options);
+ Assert.That(newOptions, Is.Not.Null);
+ Assert.That(newOptions.Id, Is.EqualTo("TEST"));
+ Assert.That(newOptions.RetainPortsOnRestart, Is.True);
+ }
+ }
+
+ public class AssignPorts
+ {
+ [Test]
+ public void Should_assign_expected_values()
+ {
+ var p = new MockDaprSchedulerProcess();
+ var builder = new PortAssignmentBuilder(new MockPortAvailabilityChecker());
+ var options = new DaprSchedulerOptions();
+ var logger = Substitute.For();
+
+ p.AssignPorts(builder);
+ builder.Build(options, new DaprSchedulerOptions(), logger);
+ Assert.That(options.HealthPort, Is.EqualTo(8082));
+ Assert.That(options.MetricsPort, Is.EqualTo(9093));
+ Assert.That(options.Port, Is.EqualTo(DaprConstants.IsWindows ? 6060 : 50006));
+ }
+ }
+
+ public class AssignLocations
+ {
+ [Test]
+ public void Should_assign_default_paths()
+ {
+ var p = new MockDaprSchedulerProcess();
+ var folder = Path.GetTempPath();
+ var options = new DaprSchedulerOptions();
+
+ p.AssignLocations(options, folder);
+ Assert.That(options.CertsDirectory, Is.Null);
+ Assert.That(options.TrustAnchorsFile, Is.EqualTo(Path.Combine(folder, Path.Combine(DaprConstants.DaprCertsDirectory, DaprConstants.TrustAnchorsCertificateFilename))));
+ }
+
+ [Test]
+ public void Should_assign_specified_paths()
+ {
+ var p = new MockDaprSchedulerProcess();
+ var folder = Path.GetTempPath();
+ var options = new DaprSchedulerOptions
+ {
+ CertsDirectory = folder + @"relative\path\cert.txt"
+ };
+
+ p.AssignLocations(options, folder);
+
+ Assert.That(options.CertsDirectory, Is.EqualTo(Path.Combine(folder, @"relative\path\cert.txt")));
+ }
+ }
+
+ public class AddCommandLineArguments
+ {
+ [Test]
+ public void Should_add_default_arguments()
+ {
+ var p = new MockDaprSchedulerProcess();
+ var builder = new CommandLineArgumentBuilder();
+ var options = new DaprSchedulerOptions();
+
+ p.AddCommandLineArguments(options, builder);
+
+ Assert.That(builder.ToString(), Is.EqualTo("--log-as-json"));
+ }
+
+ [Test]
+ public void Should_add_all_arguments()
+ {
+ var p = new MockDaprSchedulerProcess();
+ var builder = new CommandLineArgumentBuilder();
+ var options = new DaprSchedulerOptions
+ {
+ CertsDirectory = "CertsDirectory",
+ EnableMetrics = true,
+ EtcdClientHttpPorts = "EtcdClientHttpPorts",
+ EtcdClientPorts = "EtcdClientPorts",
+ EtcdCompactionMode = "EtcdCompactionMode",
+ EtcdCompactionRetention = "EtcdCompactionRetention",
+ EtcdDataDir = "EtcdDataDir",
+ EtcdSpaceQuota = 9876,
+ HealthListenAddress = "HealthzListenAddress",
+ HealthPort = 1234,
+ Id = "Id",
+ InitialCluster = "InitialCluster",
+ ListenAddress = "ListenAddress",
+ LogLevel = "LogLevel",
+ MetricsListenAddress = "MetricsListenAddress",
+ MetricsPort = 2345,
+ Mode = "Mode",
+ Port = 3456,
+ ReplicaCount = 5,
+ TlsEnabled = true,
+ TrustAnchorsFile = "TrustAnchorsFile",
+ TrustDomain = "TrustDomain",
+ CustomArguments = "arg1 val1"
+ };
+
+ p.AddCommandLineArguments(options, builder);
+
+ Assert.That(builder.ToString(), Is.EqualTo(
+ "--enable-metrics " +
+ "--etcd-client-http-ports EtcdClientHttpPorts " +
+ "--etcd-client-ports EtcdClientPorts " +
+ "--etcd-compaction-mode EtcdCompactionMode " +
+ "--etcd-compaction-retention EtcdCompactionRetention " +
+ "--etcd-data-dir EtcdDataDir " +
+ "--etcd-space-quota 9876 " +
+ "--healthz-listen-address HealthzListenAddress " +
+ "--healthz-port 1234 " +
+ "--id Id " +
+ "--initial-cluster InitialCluster " +
+ "--listen-address ListenAddress " +
+ "--log-as-json " +
+ "--log-level LogLevel " +
+ "--metrics-listen-address MetricsListenAddress " +
+ "--metrics-port 2345 " +
+ "--mode Mode " +
+ "--port 3456 " +
+ "--replica-count 5 " +
+ "--tls-enabled " +
+ "--trust-anchors-file TrustAnchorsFile " +
+ "--trust-domain TrustDomain " +
+ "--arg1 val1"));
+ }
+ }
+
+ public class AddEnvironmentVariables
+ {
+ [Test]
+ public void Should_add_expected_values()
+ {
+ var p = new MockDaprSchedulerProcess();
+ var builder = new EnvironmentVariableBuilder();
+ var options = new DaprSchedulerOptions
+ {
+ HealthPort = 1234,
+ MetricsPort = 2345,
+ Port = 3456
+ };
+
+ p.AddEnvironmentVariables(options, builder);
+
+ var values = builder.ToDictionary();
+ Assert.That(values["DAPR_SCHEDULER_HEALTH_PORT"], Is.EqualTo(1234));
+ Assert.That(values["DAPR_SCHEDULER_METRICS_PORT"], Is.EqualTo(2345));
+ Assert.That(values["DAPR_SCHEDULER_PORT"], Is.EqualTo(3456));
+ }
+ }
+
+ public class ParseCommandLineArgument
+ {
+ [Test]
+ public void Should_parse_all_arguments()
+ {
+ var p = new MockDaprSchedulerProcess();
+ var options = new DaprSchedulerOptions();
+
+ p.ParseCommandLineArgument(options, "enable-metrics", null);
+ p.ParseCommandLineArgument(options, "etcd-client-http-ports", "EtcdClientHttpPorts");
+ p.ParseCommandLineArgument(options, "etcd-client-ports", "EtcdClientPorts");
+ p.ParseCommandLineArgument(options, "etcd-compaction-mode", "EtcdCompactionMode");
+ p.ParseCommandLineArgument(options, "etcd-compaction-retention", "EtcdCompactionRetention");
+ p.ParseCommandLineArgument(options, "etcd-data-dir", "EtcdDataDir");
+ p.ParseCommandLineArgument(options, "etcd-space-quota", "9876");
+ p.ParseCommandLineArgument(options, "healthz-listen-address", "HealthzListenAddress");
+ p.ParseCommandLineArgument(options, "healthz-port", "1234");
+ p.ParseCommandLineArgument(options, "id", "Id");
+ p.ParseCommandLineArgument(options, "initial-cluster", "InitialCluster");
+ p.ParseCommandLineArgument(options, "listen-address", "ListenAddress");
+ p.ParseCommandLineArgument(options, "log-level", "LogLevel");
+ p.ParseCommandLineArgument(options, "metrics-listen-address", "MetricsListenAddress");
+ p.ParseCommandLineArgument(options, "metrics-port", "2345");
+ p.ParseCommandLineArgument(options, "mode", "Mode");
+ p.ParseCommandLineArgument(options, "port", "3456");
+ p.ParseCommandLineArgument(options, "port", "3456");
+ p.ParseCommandLineArgument(options, "replica-count", "5");
+ p.ParseCommandLineArgument(options, "tls-enabled", null);
+ p.ParseCommandLineArgument(options, "trust-anchors-file", "TrustAnchorsFile");
+ p.ParseCommandLineArgument(options, "trust-domain", "TrustDomain");
+
+ Assert.That(options.EnableMetrics, Is.True);
+ Assert.That(options.EtcdClientHttpPorts, Is.EqualTo("EtcdClientHttpPorts"));
+ Assert.That(options.EtcdClientPorts, Is.EqualTo("EtcdClientPorts"));
+ Assert.That(options.EtcdCompactionMode, Is.EqualTo("EtcdCompactionMode"));
+ Assert.That(options.EtcdCompactionRetention, Is.EqualTo("EtcdCompactionRetention"));
+ Assert.That(options.EtcdDataDir, Is.EqualTo("EtcdDataDir"));
+ Assert.That(options.EtcdSpaceQuota, Is.EqualTo(9876));
+ Assert.That(options.HealthListenAddress, Is.EqualTo("HealthzListenAddress"));
+ Assert.That(options.HealthPort, Is.EqualTo(1234));
+ Assert.That(options.Id, Is.EqualTo("Id"));
+ Assert.That(options.InitialCluster, Is.EqualTo("InitialCluster"));
+ Assert.That(options.ListenAddress, Is.EqualTo("ListenAddress"));
+ Assert.That(options.LogLevel, Is.EqualTo("LogLevel"));
+ Assert.That(options.MetricsListenAddress, Is.EqualTo("MetricsListenAddress"));
+ Assert.That(options.MetricsPort, Is.EqualTo(2345));
+ Assert.That(options.Mode, Is.EqualTo("Mode"));
+ Assert.That(options.Port, Is.EqualTo(3456));
+ Assert.That(options.ReplicaCount, Is.EqualTo(5));
+ Assert.That(options.TlsEnabled, Is.True);
+ Assert.That(options.TrustAnchorsFile, Is.EqualTo("TrustAnchorsFile"));
+ Assert.That(options.TrustDomain, Is.EqualTo("TrustDomain"));
+ }
+ }
+
+ public class CompareProcessOptions
+ {
+ [Test]
+ public void Should_return_none()
+ {
+ var p = new MockDaprSchedulerProcess();
+ var process = Substitute.For();
+ var o1 = new DaprSchedulerOptions
+ {
+ Id = "P1"
+ };
+
+ var o2 = new DaprSchedulerOptions
+ {
+ Id = "P2"
+ };
+
+ Assert.That(p.CompareProcessOptions(o1, o2, process), Is.EqualTo(ProcessComparison.None));
+ }
+
+ [Test]
+ public void Should_return_duplicate()
+ {
+ var p = new MockDaprSchedulerProcess();
+ var process = Substitute.For();
+ var o1 = new DaprSchedulerOptions
+ {
+ Id = "P1",
+ Port = 1234
+ };
+
+ var o2 = new DaprSchedulerOptions
+ {
+ Id = "P1",
+ Port = 2345
+ };
+
+ Assert.That(p.CompareProcessOptions(o1, o2, process), Is.EqualTo(ProcessComparison.Duplicate));
+ }
+
+ [Test]
+ public void Should_return_attachable()
+ {
+ var p = new MockDaprSchedulerProcess();
+ var process = Substitute.For();
+ var o1 = new DaprSchedulerOptions
+ {
+ Id = "P1",
+ Port = 1234
+ };
+
+ var o2 = new DaprSchedulerOptions
+ {
+ Id = "P1",
+ Port = 1234
+ };
+
+ Assert.That(p.CompareProcessOptions(o1, o2, process), Is.EqualTo(ProcessComparison.Attachable));
+ }
+ }
+
+ private class MockDaprSchedulerProcess : DaprSchedulerProcess
+ {
+ public new DaprSchedulerOptions GetProcessOptions(DaprOptions daprOptions) => base.GetProcessOptions(daprOptions);
+
+ public new void AssignPorts(PortAssignmentBuilder builder) => base.AssignPorts(builder);
+
+ public new void AssignLocations(DaprSchedulerOptions options, string daprFolder) => base.AssignLocations(options, daprFolder);
+
+ public new void AddCommandLineArguments(DaprSchedulerOptions source, CommandLineArgumentBuilder builder) => base.AddCommandLineArguments(source, builder);
+
+ public new void AddEnvironmentVariables(DaprSchedulerOptions source, EnvironmentVariableBuilder builder) => base.AddEnvironmentVariables(source, builder);
+
+ public new void ParseCommandLineArgument(DaprSchedulerOptions target, string name, string value) => base.ParseCommandLineArgument(target, name, value);
+
+ public new ProcessComparison CompareProcessOptions(DaprSchedulerOptions proposedProcessOptions, DaprSchedulerOptions existingProcessOptions, IProcess existingProcess) => base.CompareProcessOptions(proposedProcessOptions, existingProcessOptions, existingProcess);
+ }
+ }
+}
diff --git a/tests/Man.Dapr.Sidekick.Tests/Process/DaprSidecarProcessTests.cs b/tests/Man.Dapr.Sidekick.Tests/Process/DaprSidecarProcessTests.cs
index ecbfbd3..6916250 100644
--- a/tests/Man.Dapr.Sidekick.Tests/Process/DaprSidecarProcessTests.cs
+++ b/tests/Man.Dapr.Sidekick.Tests/Process/DaprSidecarProcessTests.cs
@@ -52,6 +52,15 @@ public void Should_use_default_placementhost_address()
Assert.That(newOptions.PlacementHostAddress, Is.EqualTo("127.0.0.1:6050"));
}
+ [Test]
+ public void Should_use_default_schedulerhost_address()
+ {
+ var p = new MockDaprSidecarProcess();
+ var options = new DaprOptions();
+ var newOptions = p.GetProcessOptions(options);
+ Assert.That(newOptions.SchedulerHostAddress, Is.EqualTo("127.0.0.1:6060"));
+ }
+
[Test]
public void Should_not_use_default_placementhost_address()
{
@@ -68,6 +77,22 @@ public void Should_not_use_default_placementhost_address()
Assert.That(newOptions.PlacementHostAddress, Is.Null);
}
+ [Test]
+ public void Should_not_use_default_schedulerhost_address()
+ {
+ var p = new MockDaprSidecarProcess();
+ var options = new DaprOptions
+ {
+ Sidecar = new DaprSidecarOptions
+ {
+ UseDefaultSchedulerHostAddress = false
+ }
+ };
+
+ var newOptions = p.GetProcessOptions(options);
+ Assert.That(newOptions.SchedulerHostAddress, Is.Null);
+ }
+
[Test]
public void Should_use_default_namespace()
{
@@ -201,6 +226,7 @@ public void Should_add_all_arguments()
Mode = "Mode",
PlacementHostAddress = "PlacementHostAddress",
ProfilePort = 6789,
+ SchedulerHostAddress = "SchedulerHostAddress",
SentryAddress = "SentryAddress",
CustomArguments = "arg1 val1"
};
@@ -228,6 +254,7 @@ public void Should_add_all_arguments()
"--mode Mode " +
"--placement-host-address PlacementHostAddress " +
"--profile-port 6789 " +
+ "--scheduler-host-address SchedulerHostAddress " +
"--sentry-address SentryAddress " +
"--config " + configFile + " " +
"--resources-path " + componentsPath + " " +
@@ -319,6 +346,7 @@ public void Should_parse_all_arguments()
p.ParseCommandLineArgument(options, "mode", "Mode");
p.ParseCommandLineArgument(options, "placement-host-address", "PlacementHostAddress");
p.ParseCommandLineArgument(options, "profile-port", "6789");
+ p.ParseCommandLineArgument(options, "scheduler-host-address", "SchedulerHostAddress");
p.ParseCommandLineArgument(options, "sentry-address", "SentryAddress");
Assert.That(options.AllowedOrigins, Is.EqualTo("AllowedOrigins"));
@@ -342,6 +370,7 @@ public void Should_parse_all_arguments()
Assert.That(options.Mode, Is.EqualTo("Mode"));
Assert.That(options.PlacementHostAddress, Is.EqualTo("PlacementHostAddress"));
Assert.That(options.ProfilePort, Is.EqualTo(6789));
+ Assert.That(options.SchedulerHostAddress, Is.EqualTo("SchedulerHostAddress"));
Assert.That(options.SentryAddress, Is.EqualTo("SentryAddress"));
}
}