From b5e0799d89f38e47aecc849e2c314e276a0661f3 Mon Sep 17 00:00:00 2001 From: zhenlei520 Date: Thu, 4 May 2023 21:56:51 +0800 Subject: [PATCH 1/5] fix: Fix issues-507 and --- Masa.Framework.sln.DotSettings | 1 + .../{Constant.cs => DaprStarterConstant.cs} | 4 +- .../IDaprProcess.cs | 3 + .../IDaprProvider.cs | 16 ++ .../Options/DaprOptions.cs | 106 +------- .../Options/DaprOptionsBase.cs} | 94 ++------ .../ServiceCollectionExtensions.cs | 3 +- .../DaprEnvironmentProvider.cs | 18 +- .../DaprProcess.cs | 184 ++++++++------ .../DaprProcessBase.cs | 100 ++++---- .../DaprProvider.cs | 29 +-- .../IDaprEnvironmentProvider.cs | 8 - ...aprProvider.cs => IDaprProcessProvider.cs} | 4 +- .../Internal/ChildProcessTracker.cs | 152 ++++++++++++ .../Internal/DefaultDaprProvider.cs | 55 +++++ .../Internal/Options/SidecarOptions.cs | 60 +++++ .../Internal/ProcessUtils.cs | 11 +- .../ServiceCollectionExtensions.cs | 4 +- .../_Imports.cs | 3 + .../CommandLineBuilderTest.cs | 2 +- .../DaprEnvironmentProviderTest.cs | 27 --- .../DefaultDaprProviderTest.cs | 226 ++++++++++++++++++ ...ntrib.Development.DaprStarter.Tests.csproj | 1 + .../_Imports.cs | 5 + .../appsettings.json | 3 +- 25 files changed, 750 insertions(+), 369 deletions(-) rename src/BuildingBlocks/Development/Masa.BuildingBlocks.Development.DaprStarter/{Constant.cs => DaprStarterConstant.cs} (86%) create mode 100644 src/BuildingBlocks/Development/Masa.BuildingBlocks.Development.DaprStarter/IDaprProvider.cs rename src/{Contrib/Development/Masa.Contrib.Development.DaprStarter/Internal/Options/DaprCoreOptions.cs => BuildingBlocks/Development/Masa.BuildingBlocks.Development.DaprStarter/Options/DaprOptionsBase.cs} (54%) rename src/Contrib/Development/Masa.Contrib.Development.DaprStarter/{IDaprProvider.cs => IDaprProcessProvider.cs} (87%) create mode 100644 src/Contrib/Development/Masa.Contrib.Development.DaprStarter/Internal/ChildProcessTracker.cs create mode 100644 src/Contrib/Development/Masa.Contrib.Development.DaprStarter/Internal/DefaultDaprProvider.cs create mode 100644 src/Contrib/Development/Masa.Contrib.Development.DaprStarter/Internal/Options/SidecarOptions.cs create mode 100644 src/Contrib/Development/Tests/Masa.Contrib.Development.DaprStarter.Tests/DefaultDaprProviderTest.cs diff --git a/Masa.Framework.sln.DotSettings b/Masa.Framework.sln.DotSettings index 1e0e7b13e..38420906f 100644 --- a/Masa.Framework.sln.DotSettings +++ b/Masa.Framework.sln.DotSettings @@ -1,5 +1,6 @@  True + True True True True \ No newline at end of file diff --git a/src/BuildingBlocks/Development/Masa.BuildingBlocks.Development.DaprStarter/Constant.cs b/src/BuildingBlocks/Development/Masa.BuildingBlocks.Development.DaprStarter/DaprStarterConstant.cs similarity index 86% rename from src/BuildingBlocks/Development/Masa.BuildingBlocks.Development.DaprStarter/Constant.cs rename to src/BuildingBlocks/Development/Masa.BuildingBlocks.Development.DaprStarter/DaprStarterConstant.cs index e32be40df..4ce67c1ae 100644 --- a/src/BuildingBlocks/Development/Masa.BuildingBlocks.Development.DaprStarter/Constant.cs +++ b/src/BuildingBlocks/Development/Masa.BuildingBlocks.Development.DaprStarter/DaprStarterConstant.cs @@ -3,12 +3,14 @@ namespace Masa.BuildingBlocks.Development.DaprStarter; -public static class Constant +public static class DaprStarterConstant { public const string DEFAULT_APPID_DELIMITER = "-"; public const string DEFAULT_FILE_NAME = "dapr"; + public const string DEFAULT_PROCESS_NAME = "dapr-starter"; + public const string DEFAULT_ARGUMENT_PREFIX = "--"; /// diff --git a/src/BuildingBlocks/Development/Masa.BuildingBlocks.Development.DaprStarter/IDaprProcess.cs b/src/BuildingBlocks/Development/Masa.BuildingBlocks.Development.DaprStarter/IDaprProcess.cs index db173dd12..d8f39e0e3 100644 --- a/src/BuildingBlocks/Development/Masa.BuildingBlocks.Development.DaprStarter/IDaprProcess.cs +++ b/src/BuildingBlocks/Development/Masa.BuildingBlocks.Development.DaprStarter/IDaprProcess.cs @@ -3,6 +3,9 @@ namespace Masa.BuildingBlocks.Development.DaprStarter; +/// +/// Manage dapr sidecar start or stop +/// public interface IDaprProcess : IDisposable { void Start(); diff --git a/src/BuildingBlocks/Development/Masa.BuildingBlocks.Development.DaprStarter/IDaprProvider.cs b/src/BuildingBlocks/Development/Masa.BuildingBlocks.Development.DaprStarter/IDaprProvider.cs new file mode 100644 index 000000000..b6e51d88d --- /dev/null +++ b/src/BuildingBlocks/Development/Masa.BuildingBlocks.Development.DaprStarter/IDaprProvider.cs @@ -0,0 +1,16 @@ +// Copyright (c) MASA Stack All rights reserved. +// Licensed under the MIT License. See LICENSE.txt in the project root for license information. + +namespace Masa.BuildingBlocks.Development.DaprStarter; + +public interface IDaprProvider +{ + /// + /// Complete dapr appid + /// + /// + string CompletionAppId(string? appId = null, + bool disableAppIdSuffix = false, + string? appIdSuffix = null, + string appIdDelimiter = DaprStarterConstant.DEFAULT_APPID_DELIMITER); +} diff --git a/src/BuildingBlocks/Development/Masa.BuildingBlocks.Development.DaprStarter/Options/DaprOptions.cs b/src/BuildingBlocks/Development/Masa.BuildingBlocks.Development.DaprStarter/Options/DaprOptions.cs index 387ed3720..5e94c46d3 100644 --- a/src/BuildingBlocks/Development/Masa.BuildingBlocks.Development.DaprStarter/Options/DaprOptions.cs +++ b/src/BuildingBlocks/Development/Masa.BuildingBlocks.Development.DaprStarter/Options/DaprOptions.cs @@ -9,14 +9,14 @@ namespace Masa.BuildingBlocks.Development.DaprStarter; /// dapr startup configuration information /// When the specified attribute is configured as null, the default value of the parameter is subject to the default value of dapr of the current version /// -public class DaprOptions +public class DaprOptions: DaprOptionsBase { /// /// The id for your application, used for service discovery /// public string? AppId { get; set; } - private string _appIdDelimiter = Constant.DEFAULT_APPID_DELIMITER; + private string _appIdDelimiter = DaprStarterConstant.DEFAULT_APPID_DELIMITER; /// /// Separator used to splice AppId and AppIdSuffix @@ -64,7 +64,7 @@ public string? AppIdSuffix /// The concurrency level of the application, otherwise is unlimited /// Must be greater than 0 /// - public int? MaxConcurrency + public override int? MaxConcurrency { get => _maxConcurrency; set @@ -76,58 +76,13 @@ public int? MaxConcurrency } } - private ushort? _appPort; - - /// - /// The port your application is listening on - /// Required. Must be between 0-65535 - /// - public ushort? AppPort - { - get => _appPort; - set - { - if (value != null) - MasaArgumentException.ThrowIfLessThanOrEqual(value.Value, (ushort)0, nameof(AppPort)); - - _appPort = value; - } - } - - /// - /// The protocol (gRPC or HTTP) Dapr uses to talk to the application. Valid values are: http or grpc - /// - public Protocol? AppProtocol { get; set; } - - /// - /// Enable https when Dapr invokes the application - /// default: null (don't use https) - /// - public bool? EnableSsl { get; set; } - - /// - /// Dapr configuration file - /// default: - /// Linux & Mac: $HOME/.dapr/config.yaml - /// Windows: %USERPROFILE%\.dapr\config.yaml - /// - public string? Config { get; set; } - - /// - /// The path for components directory - /// default: - /// Linux & Mac: $HOME/.dapr/components - /// Windows: %USERPROFILE%\.dapr\components - /// - public string? ComponentPath { get; set; } - private ushort? _daprGrpcPort; /// /// The gRPC port for Dapr to listen on /// Must be greater than 0 /// - public ushort? DaprGrpcPort + public override ushort? DaprGrpcPort { get => _daprGrpcPort; set @@ -145,7 +100,7 @@ public ushort? DaprGrpcPort /// The HTTP port for Dapr to listen on /// Must be greater than 0 /// - public ushort? DaprHttpPort + public override ushort? DaprHttpPort { get => _daprHttpPort; set @@ -157,39 +112,13 @@ public ushort? DaprHttpPort } } - /// - /// Enable pprof profiling via an HTTP endpoint - /// - public bool? EnableProfiling { get; set; } - - /// - /// The image to build the code in. Input is: repository/image - /// - public string? Image { get; set; } - - /// - /// The log verbosity. Valid values are: debug, info, warn, error, fatal, or panic - /// default: info - /// - public LogLevel? LogLevel { get; set; } - - /// - /// default: localhost - /// - public string? PlacementHostAddress { get; set; } - - /// - /// Address for the Sentry CA service - /// - public string? SentryAddress { get; set; } - private ushort? _metricsPort; /// /// The port that Dapr sends its metrics information to /// Must be greater than 0 /// - public ushort? MetricsPort + public override ushort? MetricsPort { get => _metricsPort; set @@ -207,7 +136,7 @@ public ushort? MetricsPort /// The port for the profile server to listen on /// Must be greater than 0 /// - public ushort? ProfilePort + public override ushort? ProfilePort { get => _profilePort; set @@ -219,20 +148,13 @@ public ushort? ProfilePort } } - /// - /// Path to a unix domain socket dir mount. If specified - /// communication with the Dapr sidecar uses unix domain sockets for lower latency and greater throughput when compared to using TCP ports - /// Not available on Windows OS - /// - public string? UnixDomainSocket { get; set; } - private int? _daprMaxRequestSize; /// /// Max size of request body in MB. /// Must be greater than 0 /// - public int? DaprMaxRequestSize + public override int? DaprMaxRequestSize { get => _daprMaxRequestSize; set @@ -244,14 +166,14 @@ public int? DaprMaxRequestSize } } - private int _heartBeatInterval = Constant.DEFAULT_HEARTBEAT_INTERVAL; + private int _heartBeatInterval = DaprStarterConstant.DEFAULT_HEARTBEAT_INTERVAL; /// /// Heartbeat detection interval, used to detect dapr status /// default: 5000 ms /// Must be greater than 0 /// - public int HeartBeatInterval + public override int HeartBeatInterval { get => _heartBeatInterval; set @@ -262,14 +184,6 @@ public int HeartBeatInterval } } - /// - /// Start the heartbeat check to ensure that the dapr program is active. - /// When the heartbeat check is turned off, dapr will not start automatically after it exits abnormally. - /// - public bool EnableHeartBeat { get; set; } = true; - - public bool CreateNoWindow { get; set; } = true; - public bool IsIncompleteAppId() { return !DisableAppIdSuffix && (AppIdSuffix == null || AppIdSuffix.Trim() != string.Empty); diff --git a/src/Contrib/Development/Masa.Contrib.Development.DaprStarter/Internal/Options/DaprCoreOptions.cs b/src/BuildingBlocks/Development/Masa.BuildingBlocks.Development.DaprStarter/Options/DaprOptionsBase.cs similarity index 54% rename from src/Contrib/Development/Masa.Contrib.Development.DaprStarter/Internal/Options/DaprCoreOptions.cs rename to src/BuildingBlocks/Development/Masa.BuildingBlocks.Development.DaprStarter/Options/DaprOptionsBase.cs index 2f19d375c..a303f7e1b 100644 --- a/src/Contrib/Development/Masa.Contrib.Development.DaprStarter/Internal/Options/DaprCoreOptions.cs +++ b/src/BuildingBlocks/Development/Masa.BuildingBlocks.Development.DaprStarter/Options/DaprOptionsBase.cs @@ -3,53 +3,59 @@ // ReSharper disable once CheckNamespace -namespace Masa.Contrib.Development.DaprStarter; +namespace Masa.BuildingBlocks.Development.DaprStarter; -[ExcludeFromCodeCoverage] -internal class DaprCoreOptions +public abstract class DaprOptionsBase { - /// - /// The id for your application, used for service discovery - /// Required, no blanks allowed - /// - public string AppId { get; } + private ushort? _appPort; /// /// The port your application is listening on + /// Required. Must be between 0-65535 /// - public ushort AppPort { get; } + public ushort? AppPort + { + get => _appPort; + set + { + if (value != null) + MasaArgumentException.ThrowIfLessThanOrEqual(value.Value, (ushort)0, nameof(AppPort)); + + _appPort = value; + } + } /// /// The protocol (gRPC or HTTP) Dapr uses to talk to the application. Valid values are: http or grpc /// - public Protocol? AppProtocol { get; } + public Protocol? AppProtocol { get; protected set; } /// /// Enable https when Dapr invokes the application /// - public bool? EnableSsl { get; } + public bool? EnableSsl { get; set; } /// /// The gRPC port for Dapr to listen on /// // ReSharper disable once InconsistentNaming - public ushort? DaprGrpcPort { get; private set; } + public virtual ushort? DaprGrpcPort { get; set; } /// /// The HTTP port for Dapr to listen on /// - public ushort? DaprHttpPort { get; private set; } + public virtual ushort? DaprHttpPort { get; set; } - public bool EnableHeartBeat { get; private set; } + public bool EnableHeartBeat { get; set; } - public int HeartBeatInterval { get; set; } + public virtual int HeartBeatInterval { get; set; } public bool CreateNoWindow { get; set; } = true; /// /// The concurrency level of the application, otherwise is unlimited /// - public int? MaxConcurrency { get; set; } + public virtual int? MaxConcurrency { get; set; } /// /// Dapr configuration file @@ -72,22 +78,12 @@ internal class DaprCoreOptions /// public bool? EnableProfiling { get; set; } - /// - /// The image to build the code in. Input is: repository/image - /// - public string? Image { get; set; } - /// /// The log verbosity. Valid values are: debug, info, warn, error, fatal, or panic /// default: info /// public LogLevel? LogLevel { get; set; } - /// - /// default: localhost - /// - public string? PlacementHostAddress { get; set; } - /// /// Address for the Sentry CA service /// @@ -96,12 +92,12 @@ internal class DaprCoreOptions /// /// The port that Dapr sends its metrics information to /// - public ushort? MetricsPort { get; set; } + public virtual ushort? MetricsPort { get; set; } /// /// The port for the profile server to listen on /// - public ushort? ProfilePort { get; set; } + public virtual ushort? ProfilePort { get; set; } /// /// Path to a unix domain socket dir mount. If specified @@ -113,45 +109,5 @@ internal class DaprCoreOptions /// /// Max size of request body in MB. /// - public int? DaprMaxRequestSize { get; set; } - - // ReSharper disable once InconsistentNaming - public DaprCoreOptions( - string appId, - ushort appPort, - Protocol? appProtocol, - bool? enableSsl, - ushort? daprGRPCPort, - ushort? daprHttpPort, - bool enableHeartBeat) - { - AppId = appId; - AppPort = appPort; - AppProtocol = appProtocol; - EnableSsl = enableSsl; - DaprGrpcPort = daprGRPCPort; - DaprHttpPort = daprHttpPort; - EnableHeartBeat = enableHeartBeat; - } - - public bool TrySetHttpPort(ushort? httpPort) - { - if (DaprHttpPort == null && httpPort is > 0) - { - DaprHttpPort = httpPort; - return true; - } - return false; - } - - // ReSharper disable once InconsistentNaming - public bool TrySetGrpcPort(ushort? grpcPort) - { - if (DaprGrpcPort == null && grpcPort is > 0) - { - DaprGrpcPort = grpcPort; - return true; - } - return false; - } + public virtual int? DaprMaxRequestSize { get; set; } } diff --git a/src/Contrib/Development/Masa.Contrib.Development.DaprStarter.AspNetCore/ServiceCollectionExtensions.cs b/src/Contrib/Development/Masa.Contrib.Development.DaprStarter.AspNetCore/ServiceCollectionExtensions.cs index bf279c6f2..f9865c930 100644 --- a/src/Contrib/Development/Masa.Contrib.Development.DaprStarter.AspNetCore/ServiceCollectionExtensions.cs +++ b/src/Contrib/Development/Masa.Contrib.Development.DaprStarter.AspNetCore/ServiceCollectionExtensions.cs @@ -54,7 +54,8 @@ private static IServiceCollection AddDaprStarter(this IServiceCollection service var serviceProvider = services.BuildServiceProvider(); var options = serviceProvider.GetRequiredService>(); var daprEnvironmentProvider = serviceProvider.GetRequiredService(); - daprEnvironmentProvider.CompleteDaprEnvironment(options.CurrentValue.DaprHttpPort, options.CurrentValue.DaprGrpcPort); + daprEnvironmentProvider.TrySetGrpcPort(options.CurrentValue.DaprGrpcPort); + daprEnvironmentProvider.TrySetHttpPort(options.CurrentValue.DaprHttpPort); if (isDelay) return services.AddHostedService(); diff --git a/src/Contrib/Development/Masa.Contrib.Development.DaprStarter/DaprEnvironmentProvider.cs b/src/Contrib/Development/Masa.Contrib.Development.DaprStarter/DaprEnvironmentProvider.cs index bb1d11e7a..fbb6fe968 100644 --- a/src/Contrib/Development/Masa.Contrib.Development.DaprStarter/DaprEnvironmentProvider.cs +++ b/src/Contrib/Development/Masa.Contrib.Development.DaprStarter/DaprEnvironmentProvider.cs @@ -25,33 +25,19 @@ public bool TrySetHttpPort(ushort? httpPort) { if (httpPort is > 0) { - SetHttpPort(httpPort.Value); + Environment.SetEnvironmentVariable(HTTP_PORT, httpPort.Value.ToString()); return true; } return false; } - // ReSharper disable once InconsistentNaming public bool TrySetGrpcPort(ushort? grpcPort) { if (grpcPort is > 0) { - SetGrpcPort(grpcPort.Value); + Environment.SetEnvironmentVariable(GRPC_PORT, grpcPort.Value.ToString()); return true; } return false; } - - public void SetHttpPort(ushort httpPort) => Environment.SetEnvironmentVariable(HTTP_PORT, httpPort.ToString()); - - // ReSharper disable once InconsistentNaming - public void SetGrpcPort(ushort grpcPort) => Environment.SetEnvironmentVariable(GRPC_PORT, grpcPort.ToString()); - - // ReSharper disable once InconsistentNaming - public void CompleteDaprEnvironment(ushort? httpPort, ushort? grpcPort) - { - if (grpcPort is > 0) SetGrpcPort(grpcPort.Value); - - if (httpPort is > 0) SetHttpPort(httpPort.Value); - } } diff --git a/src/Contrib/Development/Masa.Contrib.Development.DaprStarter/DaprProcess.cs b/src/Contrib/Development/Masa.Contrib.Development.DaprStarter/DaprProcess.cs index b4f6c9871..38a1983f5 100644 --- a/src/Contrib/Development/Masa.Contrib.Development.DaprStarter/DaprProcess.cs +++ b/src/Contrib/Development/Masa.Contrib.Development.DaprStarter/DaprProcess.cs @@ -8,11 +8,17 @@ public class DaprProcess : DaprProcessBase, IDaprProcess { private readonly object _lock = new(); - private readonly IDaprProvider _daprProvider; + private readonly IDaprProcessProvider _daprProvider; private readonly IProcessProvider _processProvider; private readonly ILogger? _logger; private readonly IOptionsMonitor _daprOptions; private DaprProcessStatus Status { get; set; } + + /// + /// A sidecar that is terminated by the user or the system can only be started by start + /// + private bool _isStopByManually; + private System.Timers.Timer? _heartBeatTimer; private Process? _process; private int _retryTime; @@ -23,12 +29,14 @@ public class DaprProcess : DaprProcessBase, IDaprProcess private bool _isFirst = true; public DaprProcess( - IDaprProvider daprProvider, + IDaprProcessProvider daprProvider, IProcessProvider processProvider, IOptionsMonitor daprOptions, IDaprEnvironmentProvider daprEnvironmentProvider, + IDaprProvider daprProvide, ILogger? logger = null, - IOptions? masaAppConfigureOptions = null) : base(daprEnvironmentProvider, masaAppConfigureOptions) + IOptions? masaAppConfigureOptions = null) + : base(daprEnvironmentProvider, masaAppConfigureOptions, daprProvide) { _daprProvider = daprProvider; _processProvider = processProvider; @@ -39,30 +47,34 @@ public DaprProcess( public void Start() { + if (Status == DaprProcessStatus.Started) + { + _logger?.LogInformation("The sidecar has been successfully started. If you want to restart, please stop and start again"); + return; + } + lock (_lock) { - var options = ConvertToDaprCoreOptions(_daprOptions.CurrentValue); + _isStopByManually = false; + var sidecarOptions = ConvertToSidecarOptions(_daprOptions.CurrentValue); - StartCore(options); + StartCore(sidecarOptions); } } - private void StartCore(DaprCoreOptions options) + private void StartCore(SidecarOptions options) { UpdateStatus(DaprProcessStatus.Starting); var commandLineBuilder = CreateCommandLineBuilder(options); - StopCore(SuccessDaprOptions); - - if (_isFirst) - { - CompleteDaprEnvironment(options.DaprHttpPort, options.DaprGrpcPort); - } _process = _daprProvider.DaprStart( commandLineBuilder.ToString(), options.CreateNoWindow, (_, args) => { + if (args.Data == null) + return; + if (_isFirst) CheckAndCompleteDaprEnvironment(args.Data); }, () => UpdateStatus(DaprProcessStatus.Stopped)); @@ -83,94 +95,111 @@ private void StartCore(DaprCoreOptions options) } } - public void CompleteDaprEnvironment(ushort? httpPort, ushort? grpcPort) + private void CheckAndCompleteDaprEnvironment(string data) { - var setHttpPortResult = DaprEnvironmentProvider.TrySetHttpPort(httpPort); - if (setHttpPortResult) - { - SuccessDaprOptions!.TrySetHttpPort(httpPort); - _logger?.LogInformation("Update Dapr environment variables, DaprHttpPort: {HttpPort}", httpPort); - } - - var setGrpcPortResult = DaprEnvironmentProvider.TrySetGrpcPort(grpcPort); - if (setGrpcPortResult) - { - SuccessDaprOptions!.TrySetGrpcPort(grpcPort); - _logger?.LogInformation("Update Dapr environment variables, DAPR_GRPC_PORT: {grpcPort}", grpcPort); - } + var httpPort = GetHttpPort(data); + var grpcPort = GetGrpcPort(data); - if (setHttpPortResult && setGrpcPortResult) _isFirst = false; + SuccessDaprOptions!.TrySetHttpPort(httpPort); + SuccessDaprOptions!.TrySetGrpcPort(grpcPort); + CompleteDaprEnvironment(); } - public void CheckAndCompleteDaprEnvironment(string? data) + private void CompleteDaprEnvironment() { - if (data == null) - return; - - var httpPort = GetHttpPort(data); - var grpcPort = GetgRPCPort(data); + if (SuccessDaprOptions!.DaprHttpPort is > 0 && SuccessDaprOptions!.DaprGrpcPort is > 0) + { + // Register the child process to the job object to ensure that the child process terminates when the parent process terminates + // Windows only + ChildProcessTracker.AddProcess(_process); - CompleteDaprEnvironment(httpPort, grpcPort); + DaprEnvironmentProvider.TrySetHttpPort(SuccessDaprOptions.DaprHttpPort); + DaprEnvironmentProvider.TrySetGrpcPort(SuccessDaprOptions.DaprGrpcPort); + _isFirst = false; + _retryTime = 0; + UpdateStatus(DaprProcessStatus.Started); + } } + /// + /// Only stop sidecars started by the current program + /// public void Stop() { lock (_lock) { - StopCore(SuccessDaprOptions); - _heartBeatTimer?.Stop(); + switch (Status) + { + case DaprProcessStatus.Stopped: + _logger?.LogDebug("dapr sidecar stopped, do not repeat stop"); + return; + case DaprProcessStatus.Stopping: + _logger?.LogDebug("dapr sidecar is stopping, do not repeat, please wait..."); + return; + default: + if (SuccessDaprOptions == null) + { + _logger?.LogDebug("There is no dapr sidecar successfully launched by the current program"); + return; + } + + UpdateStatus(DaprProcessStatus.Stopping); + StopCore(); + _heartBeatTimer?.Stop(); + _isStopByManually = true; + return; + } } } - private void StopCore(DaprCoreOptions? options) + private void StopCore() { - if (options != null) + // In https mode, the dapr process cannot be stopped by dapr stop + _process?.Kill(); + if (SuccessDaprOptions!.EnableSsl is not true) { - // In https mode, the dapr process cannot be stopped by dapr stop - if (options.EnableSsl is true) - { - _process?.Kill(); - } - else - { - _daprProvider.DaprStop(options.AppId); - } - - if (options.DaprHttpPort != null) - CheckPortAndKill(options.DaprHttpPort.Value); - if (options.DaprGrpcPort != null) - CheckPortAndKill(options.DaprGrpcPort.Value); + _daprProvider.DaprStop(SuccessDaprOptions.AppId); } + + if (SuccessDaprOptions.DaprHttpPort != null) + CheckPortAndKill(SuccessDaprOptions.DaprHttpPort.Value); + if (SuccessDaprOptions.DaprGrpcPort != null) + CheckPortAndKill(SuccessDaprOptions.DaprGrpcPort.Value); + + UpdateStatus(DaprProcessStatus.Stopped); } /// /// Refresh the dapr configuration, the source dapr process will be killed and the new dapr process will be restarted /// /// - public void Refresh(DaprOptions options) + private void Refresh(DaprOptions options) { lock (_lock) { - _logger?.LogDebug("Dapr configuration refresh, Dapr AppId is {AppId}, please wait...", SuccessDaprOptions!.AppId); + if (_isStopByManually) + { + _logger?.LogDebug("Dapr sidecar configuration update, you need to start dapr through Start"); + return; + } + _logger?.LogDebug("Dapr sidecar configuration updated, Dapr AppId is {AppId}, please wait...", SuccessDaprOptions!.AppId); if (SuccessDaprOptions != null) { - options.AppPort = SuccessDaprOptions.AppPort; - options.EnableSsl = SuccessDaprOptions.EnableSsl; - options.DaprHttpPort = SuccessDaprOptions.DaprHttpPort; - options.DaprGrpcPort = SuccessDaprOptions.DaprGrpcPort; + options.AppPort ??= SuccessDaprOptions.AppPort; + options.EnableSsl ??= SuccessDaprOptions.EnableSsl; + options.DaprHttpPort ??= SuccessDaprOptions.DaprHttpPort; + options.DaprGrpcPort ??= SuccessDaprOptions.DaprGrpcPort; UpdateStatus(DaprProcessStatus.Restarting); - _logger?.LogDebug( - "Dapr configuration refresh, Dapr AppId is {AppId}, closing dapr, please wait...", - SuccessDaprOptions!.AppId); - StopCore(SuccessDaprOptions); + _logger?.LogDebug("Dapr sidecar configuration updated, Dapr AppId is {AppId}, closing dapr, please wait...", SuccessDaprOptions!.AppId); + StopCore(); } _isFirst = true; SuccessDaprOptions = null; - _logger?.LogDebug("Dapr configuration refresh, Dapr AppId is {AppId}, restarting dapr, please wait...", options.AppId); - StartCore(ConvertToDaprCoreOptions(options)); + _logger?.LogDebug("Dapr sidecar configuration updated, Dapr AppId is {AppId}, restarting dapr, please wait...", options.AppId); + StartCore(ConvertToSidecarOptions(options)); } } @@ -186,7 +215,7 @@ private void CheckPortAndKill(ushort port) port, pId, process.Name, - Constant.DEFAULT_FILE_NAME); + DaprStarterConstant.DEFAULT_PROCESS_NAME); process.Kill(); } } @@ -202,16 +231,22 @@ private void HeartBeat() return; } - if (!_daprProvider.IsExist(SuccessDaprOptions!.AppId)) + var daprList = _daprProvider.GetDaprList(SuccessDaprOptions.AppId); + if (daprList.Count > 1) { - if (Status == DaprProcessStatus.Started || Status == DaprProcessStatus.Stopped) + _logger?.LogDebug("dapr sidecar appears more than 1 same appid, this may cause error"); + } + + if (!daprList.Any()) + { + if (Status == DaprProcessStatus.Started) { - _logger?.LogWarning("Dapr stopped, restarting, please wait..."); + _logger?.LogWarning("Dapr sidecar terminated abnormally, restarting, please wait..."); StartCore(SuccessDaprOptions); } else if (Status == DaprProcessStatus.Starting) { - if (_retryTime < Constant.DEFAULT_RETRY_TIME) + if (_retryTime < DaprStarterConstant.DEFAULT_RETRY_TIME) { _retryTime++; _logger?.LogDebug("Dapr is not started: The {Retries}th heartbeat check. AppId is {AppId}", @@ -226,15 +261,20 @@ private void HeartBeat() StartCore(SuccessDaprOptions); } } - else + else if (Status == DaprProcessStatus.Restarting) { _logger?.LogWarning("Dapr is restarting, the current state is {State}, please wait...", Status); } } else { - _retryTime = 0; - UpdateStatus(DaprProcessStatus.Started); + if (Status == DaprProcessStatus.Starting) + { + // Execute only when getting HttpPort, gRPCPort exception + var daprSidecar = daprList.First(); + SuccessDaprOptions.TrySetHttpPort(daprSidecar.HttpPort); + SuccessDaprOptions.TrySetGrpcPort(daprSidecar.GrpcPort); + } } } } diff --git a/src/Contrib/Development/Masa.Contrib.Development.DaprStarter/DaprProcessBase.cs b/src/Contrib/Development/Masa.Contrib.Development.DaprStarter/DaprProcessBase.cs index df1caef81..74ec026eb 100644 --- a/src/Contrib/Development/Masa.Contrib.Development.DaprStarter/DaprProcessBase.cs +++ b/src/Contrib/Development/Masa.Contrib.Development.DaprStarter/DaprProcessBase.cs @@ -6,71 +6,68 @@ namespace Masa.Contrib.Development.DaprStarter; [ExcludeFromCodeCoverage] public abstract class DaprProcessBase { + protected IDaprEnvironmentProvider DaprEnvironmentProvider { get; } + private readonly IOptions? _masaAppConfigureOptions; - protected IDaprEnvironmentProvider DaprEnvironmentProvider { get; } + private readonly IDaprProvider _daprProvider; /// /// Use after getting dapr AppId and global AppId fails /// private static readonly string DefaultAppId = (Assembly.GetEntryAssembly() ?? Assembly.GetCallingAssembly()).GetName().Name!.Replace( ".", - Constant.DEFAULT_APPID_DELIMITER); + DaprStarterConstant.DEFAULT_APPID_DELIMITER); private const string HTTP_PORT_PATTERN = @"HTTP Port: ([0-9]+)"; private const string GRPC_PORT_PATTERN = @"gRPC Port: ([0-9]+)"; - internal DaprCoreOptions? SuccessDaprOptions; + internal SidecarOptions? SuccessDaprOptions; - protected DaprProcessBase(IDaprEnvironmentProvider daprEnvironmentProvider, IOptions? masaAppConfigureOptions) + protected DaprProcessBase( + IDaprEnvironmentProvider daprEnvironmentProvider, + IOptions? masaAppConfigureOptions, + IDaprProvider daprProvider) { DaprEnvironmentProvider = daprEnvironmentProvider; _masaAppConfigureOptions = masaAppConfigureOptions; + _daprProvider = daprProvider; } - internal DaprCoreOptions ConvertToDaprCoreOptions(DaprOptions options) + internal SidecarOptions ConvertToSidecarOptions(DaprOptions options) { - var appId = options.AppId; - if (appId.IsNullOrWhiteSpace()) - appId = _masaAppConfigureOptions?.Value.AppId; - if (appId.IsNullOrWhiteSpace()) - appId = DefaultAppId; - if (options.IsIncompleteAppId()) - appId = $"{appId}{options.AppIdDelimiter}{options.AppIdSuffix ?? NetworkUtils.GetPhysicalAddress()}"; - DaprCoreOptions - dataOptions = new( - appId!, - options.AppPort ?? throw new ArgumentNullException(nameof(options), $"{options.AppPort} must be greater than 0"), - options.AppProtocol, - options.EnableSsl, - options.DaprGrpcPort ?? DaprEnvironmentProvider.GetGrpcPort(), - options.DaprHttpPort ?? DaprEnvironmentProvider.GetHttpPort(), - options.EnableHeartBeat) - { - HeartBeatInterval = options.HeartBeatInterval, - CreateNoWindow = options.CreateNoWindow, - MaxConcurrency = options.MaxConcurrency, - Config = options.Config, - ComponentPath = options.ComponentPath, - EnableProfiling = options.EnableProfiling, - Image = options.Image, - LogLevel = options.LogLevel, - PlacementHostAddress = options.PlacementHostAddress, - SentryAddress = options.PlacementHostAddress, - MetricsPort = options.MetricsPort, - ProfilePort = options.ProfilePort, - UnixDomainSocket = options.UnixDomainSocket, - DaprMaxRequestSize = options.DaprMaxRequestSize - }; - return dataOptions; + var sidecarOptions = new SidecarOptions( + _daprProvider.CompletionAppId(options.AppId), + options.AppPort, + options.AppProtocol, + options.EnableSsl, + options.EnableHeartBeat) + { + HeartBeatInterval = options.HeartBeatInterval, + CreateNoWindow = options.CreateNoWindow, + MaxConcurrency = options.MaxConcurrency, + Config = options.Config, + ComponentPath = options.ComponentPath, + EnableProfiling = options.EnableProfiling, + LogLevel = options.LogLevel, + SentryAddress = options.SentryAddress, + MetricsPort = options.MetricsPort, + ProfilePort = options.ProfilePort, + UnixDomainSocket = options.UnixDomainSocket, + DaprMaxRequestSize = options.DaprMaxRequestSize + }; + sidecarOptions.TrySetHttpPort(options.DaprHttpPort ?? DaprEnvironmentProvider.GetHttpPort()); + sidecarOptions.TrySetGrpcPort(options.DaprGrpcPort ?? DaprEnvironmentProvider.GetGrpcPort()); + + return sidecarOptions; } - internal CommandLineBuilder CreateCommandLineBuilder(DaprCoreOptions options) + internal CommandLineBuilder CreateCommandLineBuilder(SidecarOptions options) { - var commandLineBuilder = new CommandLineBuilder(Constant.DEFAULT_ARGUMENT_PREFIX); + var commandLineBuilder = new CommandLineBuilder(DaprStarterConstant.DEFAULT_ARGUMENT_PREFIX); commandLineBuilder .Add("app-id", options.AppId) - .Add("app-port", options.AppPort.ToString()) + .Add("app-port", options.GetAppPort().ToString()) .Add("app-protocol", options.AppProtocol?.ToString().ToLower() ?? string.Empty, options.AppProtocol == null) .Add("app-ssl", "", options.EnableSsl != true) .Add("components-path", options.ComponentPath ?? string.Empty, options.ComponentPath == null) @@ -79,9 +76,7 @@ internal CommandLineBuilder CreateCommandLineBuilder(DaprCoreOptions options) .Add("dapr-grpc-port", options.DaprGrpcPort?.ToString() ?? string.Empty, !(options.DaprGrpcPort > 0)) .Add("dapr-http-port", options.DaprHttpPort?.ToString() ?? string.Empty, !(options.DaprHttpPort > 0)) .Add("enable-profiling", options.EnableProfiling?.ToString().ToLower() ?? string.Empty, options.EnableProfiling == null) - .Add("image", options.Image ?? string.Empty, options.Image == null) .Add("log-level", options.LogLevel?.ToString().ToLower() ?? string.Empty, options.LogLevel == null) - .Add("placement-host-address", options.PlacementHostAddress ?? string.Empty, options.PlacementHostAddress == null) .Add("sentry-address", options.SentryAddress ?? string.Empty, options.SentryAddress == null) .Add("metrics-port", options.MetricsPort?.ToString() ?? string.Empty, options.MetricsPort == null) .Add("profile-port", options.ProfilePort?.ToString() ?? string.Empty, options.ProfilePort == null) @@ -91,27 +86,28 @@ internal CommandLineBuilder CreateCommandLineBuilder(DaprCoreOptions options) SuccessDaprOptions ??= options; return commandLineBuilder; } -#pragma warning disable S6444 - protected static ushort GetHttpPort(string data) + + protected static ushort? GetHttpPort(string data) { - ushort httpPort = 0; + ushort? httpPort = 0; var httpPortMatch = Regex.Matches(data, HTTP_PORT_PATTERN); if (httpPortMatch.Count > 0) { httpPort = ushort.Parse(httpPortMatch[0].Groups[1].ToString()); } + return httpPort; } - protected static ushort GetgRPCPort(string data) + protected static ushort? GetGrpcPort(string data) { - ushort grpcPort = 0; - var gRPCPortMatch = Regex.Matches(data, GRPC_PORT_PATTERN); - if (gRPCPortMatch.Count > 0) + ushort? grpcPort = 0; + var grpcPortMatch = Regex.Matches(data, GRPC_PORT_PATTERN); + if (grpcPortMatch.Count > 0) { - grpcPort = ushort.Parse(gRPCPortMatch[0].Groups[1].ToString()); + grpcPort = ushort.Parse(grpcPortMatch[0].Groups[1].ToString()); } + return grpcPort; } -#pragma warning restore S6444 } diff --git a/src/Contrib/Development/Masa.Contrib.Development.DaprStarter/DaprProvider.cs b/src/Contrib/Development/Masa.Contrib.Development.DaprStarter/DaprProvider.cs index 7faa4fc44..0d911eb75 100644 --- a/src/Contrib/Development/Masa.Contrib.Development.DaprStarter/DaprProvider.cs +++ b/src/Contrib/Development/Masa.Contrib.Development.DaprStarter/DaprProvider.cs @@ -4,25 +4,23 @@ namespace Masa.Contrib.Development.DaprStarter; [ExcludeFromCodeCoverage] -public class DaprProvider : IDaprProvider +public class DaprProcessProvider : IDaprProcessProvider { - private readonly ILoggerFactory? _loggerFactory; - private readonly ILogger? _logger; + private readonly ILogger? _logger; - public DaprProvider(ILoggerFactory? loggerFactory) + public DaprProcessProvider(ILogger? logger) { - _loggerFactory = loggerFactory; - _logger = loggerFactory?.CreateLogger(); + _logger = logger; } public List GetDaprList(string appId) { - var processUtils = new ProcessUtils(_loggerFactory); + var processUtils = new ProcessUtils(_logger); processUtils.Exit += delegate { - _logger?.LogDebug("{Name} process has exited", Constant.DEFAULT_FILE_NAME); + _logger?.LogDebug("{Name} process has exited, appid: {AppId}", DaprStarterConstant.DEFAULT_PROCESS_NAME, appId); }; - processUtils.Run(Constant.DEFAULT_FILE_NAME, "list -o json", out string response, true, true); + processUtils.Run(DaprStarterConstant.DEFAULT_FILE_NAME, "list -o json", out string response, true, true); List daprList = new(); try { @@ -48,6 +46,7 @@ public List GetDaprList(string appId) _logger?.LogWarning(exception, "----- Error getting list of running dapr, response message is {Response}", response); return new List(); } + return daprList.Where(dapr => dapr.AppId == appId).ToList(); } @@ -56,7 +55,7 @@ public Process DaprStart(string arguments, Action outputDataReceivedAction, Action exitAction) { - var processUtils = new ProcessUtils(_loggerFactory); + var processUtils = new ProcessUtils(_logger); processUtils.OutputDataReceived += delegate(object? sender, DataReceivedEventArgs args) { @@ -67,9 +66,9 @@ public Process DaprStart(string arguments, processUtils.Exit += delegate { exitAction.Invoke(); - _logger?.LogDebug("{Name} process has exited", Constant.DEFAULT_FILE_NAME); + _logger?.LogDebug("{Name} process has exited", DaprStarterConstant.DEFAULT_PROCESS_NAME); }; - return processUtils.Run(Constant.DEFAULT_FILE_NAME, $"run {arguments}", createNoWindow); + return processUtils.Run(DaprStarterConstant.DEFAULT_FILE_NAME, $"run {arguments}", createNoWindow); } private static void DaprProcess_OutputDataReceived(DataReceivedEventArgs e) @@ -114,13 +113,11 @@ private static void DaprProcess_ErrorDataReceived(object? sender, DataReceivedEv public void DaprStop(string appId) { - var process = new ProcessUtils(_loggerFactory).Run( - $"{Constant.DEFAULT_FILE_NAME}", + var process = new ProcessUtils(_logger).Run( + $"{DaprStarterConstant.DEFAULT_FILE_NAME}", $"stop {appId}", createNoWindow: false, isWait: false); process.WaitForExit(); } - - public bool IsExist(string appId) => GetDaprList(appId).Any(); } diff --git a/src/Contrib/Development/Masa.Contrib.Development.DaprStarter/IDaprEnvironmentProvider.cs b/src/Contrib/Development/Masa.Contrib.Development.DaprStarter/IDaprEnvironmentProvider.cs index 364559292..5b97ac936 100644 --- a/src/Contrib/Development/Masa.Contrib.Development.DaprStarter/IDaprEnvironmentProvider.cs +++ b/src/Contrib/Development/Masa.Contrib.Development.DaprStarter/IDaprEnvironmentProvider.cs @@ -25,12 +25,4 @@ public interface IDaprEnvironmentProvider /// // ReSharper disable once InconsistentNaming bool TrySetGrpcPort(ushort? grpcPort); - - void SetHttpPort(ushort httpPort); - - // ReSharper disable once InconsistentNaming - void SetGrpcPort(ushort grpcPort); - - // ReSharper disable once InconsistentNaming - void CompleteDaprEnvironment(ushort? httpPort, ushort? grpcPort); } diff --git a/src/Contrib/Development/Masa.Contrib.Development.DaprStarter/IDaprProvider.cs b/src/Contrib/Development/Masa.Contrib.Development.DaprStarter/IDaprProcessProvider.cs similarity index 87% rename from src/Contrib/Development/Masa.Contrib.Development.DaprStarter/IDaprProvider.cs rename to src/Contrib/Development/Masa.Contrib.Development.DaprStarter/IDaprProcessProvider.cs index 1d0df3c3f..1db0c5dd9 100644 --- a/src/Contrib/Development/Masa.Contrib.Development.DaprStarter/IDaprProvider.cs +++ b/src/Contrib/Development/Masa.Contrib.Development.DaprStarter/IDaprProcessProvider.cs @@ -3,7 +3,7 @@ namespace Masa.Contrib.Development.DaprStarter; -public interface IDaprProvider +public interface IDaprProcessProvider { List GetDaprList(string appId); @@ -13,6 +13,4 @@ Process DaprStart(string arguments, Action exitAction); void DaprStop(string appId); - - bool IsExist(string appId); } diff --git a/src/Contrib/Development/Masa.Contrib.Development.DaprStarter/Internal/ChildProcessTracker.cs b/src/Contrib/Development/Masa.Contrib.Development.DaprStarter/Internal/ChildProcessTracker.cs new file mode 100644 index 000000000..5dd21091a --- /dev/null +++ b/src/Contrib/Development/Masa.Contrib.Development.DaprStarter/Internal/ChildProcessTracker.cs @@ -0,0 +1,152 @@ +// Copyright (c) MASA Stack All rights reserved. +// Licensed under the MIT License. See LICENSE.txt in the project root for license information. + +// ReSharper disable once CheckNamespace + +namespace Masa.Contrib.Development.DaprStarter; + +/// +/// Allows processes to be automatically killed if this parent process unexpectedly quits. +/// This feature requires Windows 8 or greater. On Windows 7, nothing is done. +/// References: +/// https://stackoverflow.com/a/4657392/386091 +/// https://stackoverflow.com/a/9164742/386091 +[ExcludeFromCodeCoverage] +public static class ChildProcessTracker +{ + /// + /// Add the process to be tracked. If our current process is killed, the child processes + /// that we are tracking will be automatically killed, too. If the child process terminates + /// first, that's fine, too. + /// + public static void AddProcess(Process? process) + { + if (Environment.OSVersion.Platform != PlatformID.Win32NT) + { + //Only supported on windows + return; + } + + if (process == null) + return; + + if (s_jobHandle != IntPtr.Zero) + { + bool success = AssignProcessToJobObject(s_jobHandle, process.Handle); + if (!success && !process.HasExited) + throw new System.ComponentModel.Win32Exception(); + } + } + + static ChildProcessTracker() + { + // This feature requires Windows 8 or later. To support Windows 7 requires + // registry settings to be added if you are using Visual Studio plus an + // app.manifest change. + // https://stackoverflow.com/a/4232259/386091 + // https://stackoverflow.com/a/9507862/386091 + if (Environment.OSVersion.Version < new Version(6, 2)) + return; + + // The job name is optional (and can be null) but it helps with diagnostics. + // If it's not null, it has to be unique. Use SysInternals' Handle command-line + // utility: handle -a ChildProcessTracker + string jobName = "ChildProcessTracker" + Process.GetCurrentProcess().Id; + s_jobHandle = CreateJobObject(IntPtr.Zero, jobName); + + var info = new JOBOBJECT_BASIC_LIMIT_INFORMATION(); + + // This is the key flag. When our process is killed, Windows will automatically + // close the job handle, and when that happens, we want the child processes to + // be killed, too. + info.LimitFlags = JOBOBJECTLIMIT.JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE; + + var extendedInfo = new JOBOBJECT_EXTENDED_LIMIT_INFORMATION(); + extendedInfo.BasicLimitInformation = info; + + int length = Marshal.SizeOf(typeof(JOBOBJECT_EXTENDED_LIMIT_INFORMATION)); + IntPtr extendedInfoPtr = Marshal.AllocHGlobal(length); + try + { + Marshal.StructureToPtr(extendedInfo, extendedInfoPtr, false); + + if (!SetInformationJobObject(s_jobHandle, JobObjectInfoType.ExtendedLimitInformation, + extendedInfoPtr, (uint)length)) + { + throw new System.ComponentModel.Win32Exception(); + } + } + finally + { + Marshal.FreeHGlobal(extendedInfoPtr); + } + } + + [DllImport("kernel32.dll", CharSet = CharSet.Unicode)] + static extern IntPtr CreateJobObject(IntPtr lpJobAttributes, string name); + + [DllImport("kernel32.dll")] + static extern bool SetInformationJobObject(IntPtr job, JobObjectInfoType infoType, + IntPtr lpJobObjectInfo, uint cbJobObjectInfoLength); + + [DllImport("kernel32.dll", SetLastError = true)] + static extern bool AssignProcessToJobObject(IntPtr job, IntPtr process); + + // Windows will automatically close any open job handles when our process terminates. + // This can be verified by using SysInternals' Handle utility. When the job handle + // is closed, the child processes will be killed. + private static readonly IntPtr s_jobHandle; +} + +public enum JobObjectInfoType +{ + AssociateCompletionPortInformation = 7, + BasicLimitInformation = 2, + BasicUIRestrictions = 4, + EndOfJobTimeInformation = 6, + ExtendedLimitInformation = 9, + SecurityLimitInformation = 5, + GroupInformation = 11 +} + +[StructLayout(LayoutKind.Sequential)] +public struct JOBOBJECT_BASIC_LIMIT_INFORMATION +{ + public Int64 PerProcessUserTimeLimit; + public Int64 PerJobUserTimeLimit; + public JOBOBJECTLIMIT LimitFlags; + public UIntPtr MinimumWorkingSetSize; + public UIntPtr MaximumWorkingSetSize; + public UInt32 ActiveProcessLimit; + public Int64 Affinity; + public UInt32 PriorityClass; + public UInt32 SchedulingClass; +} + +[Flags] +public enum JOBOBJECTLIMIT : uint +{ + JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE = 0x2000 +} + +[StructLayout(LayoutKind.Sequential)] +public struct IO_COUNTERS +{ + public UInt64 ReadOperationCount; + public UInt64 WriteOperationCount; + public UInt64 OtherOperationCount; + public UInt64 ReadTransferCount; + public UInt64 WriteTransferCount; + public UInt64 OtherTransferCount; +} + +[StructLayout(LayoutKind.Sequential)] +public struct JOBOBJECT_EXTENDED_LIMIT_INFORMATION +{ + public JOBOBJECT_BASIC_LIMIT_INFORMATION BasicLimitInformation; + public IO_COUNTERS IoInfo; + public UIntPtr ProcessMemoryLimit; + public UIntPtr JobMemoryLimit; + public UIntPtr PeakProcessMemoryUsed; + public UIntPtr PeakJobMemoryUsed; +} diff --git a/src/Contrib/Development/Masa.Contrib.Development.DaprStarter/Internal/DefaultDaprProvider.cs b/src/Contrib/Development/Masa.Contrib.Development.DaprStarter/Internal/DefaultDaprProvider.cs new file mode 100644 index 000000000..ae6d1e9cf --- /dev/null +++ b/src/Contrib/Development/Masa.Contrib.Development.DaprStarter/Internal/DefaultDaprProvider.cs @@ -0,0 +1,55 @@ +// Copyright (c) MASA Stack All rights reserved. +// Licensed under the MIT License. See LICENSE.txt in the project root for license information. + +[assembly: InternalsVisibleTo("Masa.Contrib.Development.DaprStarter.Tests")] + +// ReSharper disable once CheckNamespace + +namespace Masa.Contrib.Development.DaprStarter; + +internal class DefaultDaprProvider : IDaprProvider +{ + private readonly IOptions? _masaAppConfigureOptions; + private readonly IConfiguration? _configuration; + private readonly IMasaConfiguration? _masaConfiguration; + + /// + /// Use after getting dapr AppId and global AppId fails + /// + private static readonly string DefaultAppId = ( + Assembly.GetEntryAssembly() ?? + Assembly.GetCallingAssembly()).GetName().Name!.Replace(".", DaprStarterConstant.DEFAULT_APPID_DELIMITER); + + public DefaultDaprProvider(IOptions? masaAppConfigureOptions = null, + IConfiguration? configuration = null, + IMasaConfiguration? masaConfiguration = null) + { + _masaAppConfigureOptions = masaAppConfigureOptions; + _configuration = configuration; + _masaConfiguration = masaConfiguration; + } + + public string CompletionAppId(string? appId = null, + bool disableAppIdSuffix = false, + string? appIdSuffix = null, + string appIdDelimiter = DaprStarterConstant.DEFAULT_APPID_DELIMITER) + { + string? actualAppId = appId; + if (actualAppId.IsNullOrWhiteSpace()) + actualAppId = _masaAppConfigureOptions?.Value.AppId; + if (actualAppId.IsNullOrWhiteSpace()) + actualAppId = DefaultAppId; + if (IsIncompleteAppId()) + actualAppId = $"{actualAppId}{appIdDelimiter}{appIdSuffix ?? NetworkUtils.GetPhysicalAddress()}"; + + var value = _configuration == null ? Environment.GetEnvironmentVariable(actualAppId) : _configuration?.GetSection(actualAppId).Value; + if (value.IsNullOrWhiteSpace()) + value = _masaConfiguration?.Local.GetSection(actualAppId).Value; + + if (value.IsNullOrWhiteSpace()) return actualAppId; + + return value; + + bool IsIncompleteAppId() => !disableAppIdSuffix && (appIdSuffix == null || appIdSuffix.Trim() != string.Empty); + } +} diff --git a/src/Contrib/Development/Masa.Contrib.Development.DaprStarter/Internal/Options/SidecarOptions.cs b/src/Contrib/Development/Masa.Contrib.Development.DaprStarter/Internal/Options/SidecarOptions.cs new file mode 100644 index 000000000..a6886fa81 --- /dev/null +++ b/src/Contrib/Development/Masa.Contrib.Development.DaprStarter/Internal/Options/SidecarOptions.cs @@ -0,0 +1,60 @@ +// Copyright (c) MASA Stack All rights reserved. +// Licensed under the MIT License. See LICENSE.txt in the project root for license information. + +// ReSharper disable once CheckNamespace + +namespace Masa.Contrib.Development.DaprStarter; + +[ExcludeFromCodeCoverage] +internal class SidecarOptions : DaprOptionsBase +{ + /// + /// The id for your application, used for service discovery + /// Required, no blanks allowed + /// + public string AppId { get; } + + // ReSharper disable once InconsistentNaming + public SidecarOptions( + string appId, + ushort? appPort, + Protocol? appProtocol, + bool? enableSsl, + bool enableHeartBeat) + { + AppId = appId; + AppPort = appPort; + AppProtocol = appProtocol; + EnableSsl = enableSsl; + EnableHeartBeat = enableHeartBeat; + } + + public bool TrySetHttpPort(ushort? httpPort) + { + if (DaprHttpPort == null && httpPort is > 0) + { + DaprHttpPort = httpPort; + return true; + } + + return false; + } + + // ReSharper disable once InconsistentNaming + public bool TrySetGrpcPort(ushort? grpcPort) + { + if (DaprGrpcPort == null && grpcPort is > 0) + { + DaprGrpcPort = grpcPort; + return true; + } + + return false; + } + + public ushort GetAppPort() + { + MasaArgumentException.ThrowIfNull(AppPort); + return AppPort.Value; + } +} diff --git a/src/Contrib/Development/Masa.Contrib.Development.DaprStarter/Internal/ProcessUtils.cs b/src/Contrib/Development/Masa.Contrib.Development.DaprStarter/Internal/ProcessUtils.cs index c46db6cfd..bec517695 100644 --- a/src/Contrib/Development/Masa.Contrib.Development.DaprStarter/Internal/ProcessUtils.cs +++ b/src/Contrib/Development/Masa.Contrib.Development.DaprStarter/Internal/ProcessUtils.cs @@ -8,11 +8,11 @@ namespace Masa.Contrib.Development.DaprStarter; [ExcludeFromCodeCoverage] internal sealed class ProcessUtils { - private readonly ILogger? _logger; + private readonly ILogger? _logger; - public ProcessUtils(ILoggerFactory? loggerFactory = null) + public ProcessUtils(ILogger? logger) { - _logger = loggerFactory?.CreateLogger(); + _logger = logger; } public Process Run( @@ -34,7 +34,7 @@ public Process Run( { FileName = fileName, Arguments = arguments, - UseShellExecute = !createNoWindow, + UseShellExecute = false, CreateNoWindow = createNoWindow }; var daprProcess = new Process() @@ -53,12 +53,14 @@ public Process Run( daprProcess.ErrorDataReceived += (_, args) => OnErrorDataReceived(args); } } + daprProcess.Start(); if (createNoWindow && !isWait) { daprProcess.BeginOutputReadLine(); daprProcess.BeginErrorReadLine(); } + daprProcess.Exited += (_, _) => OnExited(); string command = $"{fileName} {arguments}"; _logger?.LogDebug("Process: {ProcessName}, Command: {Command}, PID: {ProcessId} executed successfully", fileName, @@ -73,6 +75,7 @@ public Process Run( { response = string.Empty; } + return daprProcess; } diff --git a/src/Contrib/Development/Masa.Contrib.Development.DaprStarter/ServiceCollectionExtensions.cs b/src/Contrib/Development/Masa.Contrib.Development.DaprStarter/ServiceCollectionExtensions.cs index e8d721294..a57d50a1c 100644 --- a/src/Contrib/Development/Masa.Contrib.Development.DaprStarter/ServiceCollectionExtensions.cs +++ b/src/Contrib/Development/Masa.Contrib.Development.DaprStarter/ServiceCollectionExtensions.cs @@ -34,7 +34,8 @@ private static IServiceCollection AddDaprStarter(this IServiceCollection service MasaApp.TrySetServiceCollection(services); services.TryAddSingleton(); - services.TryAddSingleton(); + services.TryAddSingleton(); + services.TryAddSingleton(); services.TryAddSingleton(); services.TryAddSingleton(); return services; @@ -42,6 +43,5 @@ private static IServiceCollection AddDaprStarter(this IServiceCollection service private sealed class DaprService { - } } diff --git a/src/Contrib/Development/Masa.Contrib.Development.DaprStarter/_Imports.cs b/src/Contrib/Development/Masa.Contrib.Development.DaprStarter/_Imports.cs index 54d2bf6b8..fd696ecdc 100644 --- a/src/Contrib/Development/Masa.Contrib.Development.DaprStarter/_Imports.cs +++ b/src/Contrib/Development/Masa.Contrib.Development.DaprStarter/_Imports.cs @@ -1,10 +1,12 @@ // Copyright (c) MASA Stack All rights reserved. // Licensed under the MIT License. See LICENSE.txt in the project root for license information. +global using Masa.BuildingBlocks.Configuration; global using Masa.BuildingBlocks.Configuration.Options; global using Masa.BuildingBlocks.Data; global using Masa.BuildingBlocks.Development.DaprStarter; global using Masa.Contrib.Development.DaprStarter; +global using Masa.Utils.Caching.Memory; global using Microsoft.Extensions.Configuration; global using Microsoft.Extensions.DependencyInjection.Extensions; global using Microsoft.Extensions.Logging; @@ -13,6 +15,7 @@ global using System.Diagnostics.CodeAnalysis; global using System.Net.NetworkInformation; global using System.Reflection; +global using System.Runtime.CompilerServices; global using System.Runtime.InteropServices; global using System.Text.Json.Serialization; global using System.Text.RegularExpressions; diff --git a/src/Contrib/Development/Tests/Masa.Contrib.Development.DaprStarter.Tests/CommandLineBuilderTest.cs b/src/Contrib/Development/Tests/Masa.Contrib.Development.DaprStarter.Tests/CommandLineBuilderTest.cs index ccd098e24..4f2cb6186 100644 --- a/src/Contrib/Development/Tests/Masa.Contrib.Development.DaprStarter.Tests/CommandLineBuilderTest.cs +++ b/src/Contrib/Development/Tests/Masa.Contrib.Development.DaprStarter.Tests/CommandLineBuilderTest.cs @@ -14,7 +14,7 @@ public class CommandLineBuilderTest [DataRow(5001, 5000, "--app-id test --dapr-grpc-port 5001 --dapr-http-port 5000")] public void TestToString(int? grpcPort, int? httpPort, string expectedResult) { - var builder = new CommandLineBuilder(Masa.BuildingBlocks.Development.DaprStarter.Constant.DEFAULT_ARGUMENT_PREFIX); + var builder = new CommandLineBuilder(Masa.BuildingBlocks.Development.DaprStarter.DaprStarterConstant.DEFAULT_ARGUMENT_PREFIX); builder .Add("app-id", "test", false) .Add("dapr-grpc-port", grpcPort?.ToString() ?? string.Empty, !(grpcPort > 0)) diff --git a/src/Contrib/Development/Tests/Masa.Contrib.Development.DaprStarter.Tests/DaprEnvironmentProviderTest.cs b/src/Contrib/Development/Tests/Masa.Contrib.Development.DaprStarter.Tests/DaprEnvironmentProviderTest.cs index 8befd7e64..91a52e1c5 100644 --- a/src/Contrib/Development/Tests/Masa.Contrib.Development.DaprStarter.Tests/DaprEnvironmentProviderTest.cs +++ b/src/Contrib/Development/Tests/Masa.Contrib.Development.DaprStarter.Tests/DaprEnvironmentProviderTest.cs @@ -55,31 +55,4 @@ public void TestTrySetGrpcPort() var port = _daprEnvironmentProvider.GetGrpcPort(); Assert.AreEqual(grpcPort, port); } - - [TestMethod] - // ReSharper disable once InconsistentNaming - public void TestCompleteDaprEnvironment() - { - // ReSharper disable once InconsistentNaming - ushort? grpcPort = null; - ushort? httpPort = null; - _daprEnvironmentProvider.CompleteDaprEnvironment(httpPort, grpcPort); - - grpcPort = _daprEnvironmentProvider.GetGrpcPort(); - Assert.AreEqual(null, grpcPort); - - httpPort = _daprEnvironmentProvider.GetHttpPort(); - Assert.AreEqual(null, httpPort); - - httpPort = 9; - grpcPort = 10; - - _daprEnvironmentProvider.CompleteDaprEnvironment(httpPort, grpcPort); - - httpPort = _daprEnvironmentProvider.GetHttpPort(); - Assert.AreEqual((ushort)9, httpPort); - - grpcPort = _daprEnvironmentProvider.GetGrpcPort(); - Assert.AreEqual((ushort)10, grpcPort); - } } diff --git a/src/Contrib/Development/Tests/Masa.Contrib.Development.DaprStarter.Tests/DefaultDaprProviderTest.cs b/src/Contrib/Development/Tests/Masa.Contrib.Development.DaprStarter.Tests/DefaultDaprProviderTest.cs new file mode 100644 index 000000000..aa6a0cb16 --- /dev/null +++ b/src/Contrib/Development/Tests/Masa.Contrib.Development.DaprStarter.Tests/DefaultDaprProviderTest.cs @@ -0,0 +1,226 @@ +// Copyright (c) MASA Stack All rights reserved. +// Licensed under the MIT License. See LICENSE.txt in the project root for license information. + +namespace Masa.Contrib.Development.DaprStarter.Tests; + +[TestClass] +public class DefaultDaprProviderTest +{ + private static readonly string DefaultAppIdSuffix = NetworkUtils.GetPhysicalAddress(); + + private static readonly string DefaultAppId = ( + Assembly.GetEntryAssembly() ?? + Assembly.GetCallingAssembly()).GetName().Name!.Replace(".", DaprStarterConstant.DEFAULT_APPID_DELIMITER); + + [DataRow(null, "test2", "test2-{default-suffix}", "test3", false, null, null, "test2-{default-suffix}")] + [DataRow(null, "test2", "test2-{default-suffix}-2", "test3", false, null, null, "test2-{default-suffix}")] + [DataRow(null, null, "{default-appid}-{default-suffix}", "test3", false, null, null, "{default-appid}-{default-suffix}")] + [DataRow(null, null, "{default-appid}-{default-suffix}", "test3", false, null, null, "{default-appid}-{default-suffix}")] + [DataRow(null, "test2", "test2-masa", "test3", false, "masa", null, "test2-masa")] + [DataRow(null, "test2", "test2-masa-2", "test3", false, "masa", null, "test2-masa")] + [DataRow(null, null, "{default-appid}-masa", "test3", false, "masa", null, "{default-appid}-masa")] + [DataRow(null, null, "{default-appid}-masa-2", "test3", false, "masa", null, "{default-appid}-masa")] + [DataRow(null, "test2", "test2|masa", "test3", false, "masa", "|", "test2|masa")] + [DataRow(null, "test2", "test2|masa-2", "test3", false, "masa", "|", "test2|masa")] + [DataRow(null, null, "{default-appid}|masa", "test3", false, "masa", "|", "{default-appid}|masa")] + [DataRow(null, null, "{default-appid}|masa-2", "test3", false, "masa", "|", "{default-appid}|masa")] + [DataRow(null, "test2", "test2|{default-suffix}", "test3", false, null, "|", "test2|{default-suffix}")] + [DataRow(null, "test2", "test2|{default-suffix}-2", "test3", false, null, "|", "test2|{default-suffix}")] + [DataRow(null, null, "{default-appid}|{default-suffix}", "test3", false, null, "|", "{default-appid}|{default-suffix}")] + [DataRow(null, null, "{default-appid}|{default-suffix}-2", "test3", false, null, "|", "{default-appid}|{default-suffix}")] + [DataRow("test", "test2", "test-{default-suffix}", "test3", false, null, null, "test-{default-suffix}")] + [DataRow("test", "test2", "test-{default-suffix}-2", "test3", false, null, null, "test-{default-suffix}")] + [DataRow("test", null, "test-{default-suffix}", "test3", false, null, null, "test-{default-suffix}")] + [DataRow("test", null, "test-{default-suffix}-2", "test3", false, null, null, "test-{default-suffix}")] + [DataRow("test", "test2", "test-masa", "test3", false, "masa", null, "test-masa")] + [DataRow("test", "test2", "test-masa-2", "test3", false, "masa", null, "test-masa")] + [DataRow("test", null, "test-masa", "test3", false, "masa", null, "test-masa")] + [DataRow("test", null, "test-masa-2", "test3", false, "masa", null, "test-masa")] + [DataRow("test", "test2", "test|masa", "test3", false, "masa", "|", "test|masa")] + [DataRow("test", "test2", "test|masa-2", "test3", false, "masa", "|", "test|masa")] + [DataRow("test", null, "test|masa", "test3", false, "masa", "|", "test|masa")] + [DataRow("test", null, "test|masa-2", "test3", false, "masa", "|", "test|masa")] + [DataRow("test", "test2", "test|{default-suffix}", "test3", false, null, "|", "test|{default-suffix}")] + [DataRow("test", "test2", "test|{default-suffix}-2", "test3", false, null, "|", "test|{default-suffix}")] + [DataRow("test", null, "test|{default-suffix}", "test3", false, null, "|", "test|{default-suffix}")] + [DataRow("test", null, "test|{default-suffix}-2", "test3", false, null, "|", "test|{default-suffix}")] + [DataRow("test", "test2", "test", "test3", true, null, null, "test")] + [DataRow("test", "test2", "test-2", "test3", true, null, null, "test")] + [DataRow("test", null, "test", "test3", true, null, null, "test")] + [DataRow("test", null, "test-2", "test3", true, null, null, "test")] + [DataRow("test", "test2", "test", "test3", true, "masa", null, "test")] + [DataRow("test", "test2", "test-2", "test3", true, "masa", null, "test")] + [DataRow("test", null, "test", "test3", true, "masa", null, "test")] + [DataRow("test", null, "test-2", "test3", true, "masa", null, "test")] + [DataRow("test", "test2", "test", "test3", true, "masa", "|", "test")] + [DataRow("test", "test2", "test-2", "test3", true, "masa", "|", "test")] + [DataRow("test", null, "test", "test3", true, "masa", "|", "test")] + [DataRow("test", null, "test-2", "test3", true, "masa", "|", "test")] + [DataRow("test", "test2", "test", "test3", true, null, "|", "test")] + [DataRow("test", "test2", "test-2", "test3", true, null, "|", "test")] + [DataRow("test", null, "test", "test3", true, null, "|", "test")] + [DataRow("test", null, "test-2", "test3", true, null, "|", "test")] + [DataTestMethod] + public void TestCompletionAppId( + string? inputAppId, + string? inputGlobalAppId, + string? environmentAppIdKey, + string? environmentAppIdValue, + bool inputDisableAppIdSuffix, + string? inputAppIdSuffix, + string? inputAppIdDelimiter, + string expectedAppId) + { + if (environmentAppIdKey != null && environmentAppIdValue != null) + { + Environment.SetEnvironmentVariable( + environmentAppIdKey.Replace("{default-appid}", DefaultAppId).Replace("{default-suffix}", DefaultAppIdSuffix), + environmentAppIdValue); + } + + IOptions? masaAppConfigureOptions = inputGlobalAppId == null + ? null + : Options.Create(new MasaAppConfigureOptions() + { + AppId = inputGlobalAppId + }); + var defaultDaprProvider = new DefaultDaprProvider(masaAppConfigureOptions); + + var acceptAppId = inputAppIdDelimiter != null + ? defaultDaprProvider.CompletionAppId(inputAppId, inputDisableAppIdSuffix, inputAppIdSuffix, inputAppIdDelimiter) + : defaultDaprProvider.CompletionAppId(inputAppId, inputDisableAppIdSuffix, inputAppIdSuffix); + + + if (environmentAppIdKey != null && !environmentAppIdValue.IsNullOrWhiteSpace()) + { + Assert.AreEqual( + environmentAppIdKey.Replace("{default-appid}", DefaultAppId).Replace("{default-suffix}", DefaultAppIdSuffix) == + expectedAppId.Replace("{default-appid}", DefaultAppId).Replace("{default-suffix}", DefaultAppIdSuffix) + ? environmentAppIdValue + : expectedAppId.Replace("{default-appid}", DefaultAppId).Replace("{default-suffix}", DefaultAppIdSuffix), acceptAppId); + } + else + { + Assert.AreEqual( + expectedAppId.Replace("{default-appid}", DefaultAppId).Replace("{default-suffix}", DefaultAppIdSuffix), acceptAppId); + } + + if (environmentAppIdKey != null && environmentAppIdValue != null) + { + Environment.SetEnvironmentVariable( + environmentAppIdKey.Replace("{default-appid}", DefaultAppId).Replace("{default-suffix}", DefaultAppIdSuffix), + ""); + } + } + + [DataRow(null, "test2", false, null, null, "test2-{default-suffix}")] + [DataRow(null, null, false, null, null, "{default-appid}-{default-suffix}")] + [DataRow(null, "test2", false, "masa", null, "test2-masa")] + [DataRow(null, null, false, "masa", null, "{default-appid}-masa")] + [DataRow(null, "test2", false, "masa", "|", "test2|masa")] + [DataRow(null, null, false, "masa", "|", "{default-appid}|masa")] + [DataRow(null, "test2", false, null, "|", "test2|{default-suffix}")] + [DataRow(null, null, false, null, "|", "{default-appid}|{default-suffix}")] + [DataRow("test", "test2", false, null, null, "test-{default-suffix}")] + [DataRow("test", null, false, null, null, "test-{default-suffix}")] + [DataRow("test", "test2", false, "masa", null, "test-masa")] + [DataRow("test", null, false, "masa", null, "test-masa")] + [DataRow("test", "test2", false, "masa", "|", "test|masa")] + [DataRow("test", null, false, "masa", "|", "test|masa")] + [DataRow("test", "test2", false, null, "|", "test|{default-suffix}")] + [DataRow("test", null, false, null, "|", "test|{default-suffix}")] + [DataRow("test", "test2", true, null, null, "test")] + [DataRow("test", null, true, null, null, "test")] + [DataRow("test", "test2", true, "masa", null, "test")] + [DataRow("test", null, true, "masa", null, "test")] + [DataRow("test", "test2", true, "masa", "|", "test")] + [DataRow("test", null, true, "masa", "|", "test")] + [DataRow("test", "test2", true, null, "|", "test")] + [DataRow("test", null, true, null, "|", "test")] + [DataTestMethod] + public void TestCompletionAppIdByConfiguration( + string? inputAppId, + string? inputGlobalAppId, + bool inputDisableAppIdSuffix, + string? inputAppIdSuffix, + string? inputAppIdDelimiter, + string expectedAppId) + { + IOptions? masaAppConfigureOptions = inputGlobalAppId == null + ? null + : Options.Create(new MasaAppConfigureOptions() + { + AppId = inputGlobalAppId + }); + var configuration = new ConfigurationBuilder().AddJsonFile("appsettings.json").Build(); + var defaultDaprProvider = new DefaultDaprProvider(masaAppConfigureOptions, configuration); + + var acceptAppId = inputAppIdDelimiter != null + ? defaultDaprProvider.CompletionAppId(inputAppId, inputDisableAppIdSuffix, inputAppIdSuffix, inputAppIdDelimiter) + : defaultDaprProvider.CompletionAppId(inputAppId, inputDisableAppIdSuffix, inputAppIdSuffix); + + + Assert.AreEqual( + expectedAppId == "test-masa" + ? "test3" + : expectedAppId.Replace("{default-appid}", DefaultAppId).Replace("{default-suffix}", DefaultAppIdSuffix), acceptAppId); + } + + [DataRow(null, "test2", false, null, null, "test2-{default-suffix}")] + [DataRow(null, null, false, null, null, "{default-appid}-{default-suffix}")] + [DataRow(null, "test2", false, "masa", null, "test2-masa")] + [DataRow(null, null, false, "masa", null, "{default-appid}-masa")] + [DataRow(null, "test2", false, "masa", "|", "test2|masa")] + [DataRow(null, null, false, "masa", "|", "{default-appid}|masa")] + [DataRow(null, "test2", false, null, "|", "test2|{default-suffix}")] + [DataRow(null, null, false, null, "|", "{default-appid}|{default-suffix}")] + [DataRow("test", "test2", false, null, null, "test-{default-suffix}")] + [DataRow("test", null, false, null, null, "test-{default-suffix}")] + [DataRow("test", "test2", false, "masa", null, "test-masa")] + [DataRow("test", null, false, "masa", null, "test-masa")] + [DataRow("test", "test2", false, "masa", "|", "test|masa")] + [DataRow("test", null, false, "masa", "|", "test|masa")] + [DataRow("test", "test2", false, null, "|", "test|{default-suffix}")] + [DataRow("test", null, false, null, "|", "test|{default-suffix}")] + [DataRow("test", "test2", true, null, null, "test")] + [DataRow("test", null, true, null, null, "test")] + [DataRow("test", "test2", true, "masa", null, "test")] + [DataRow("test", null, true, "masa", null, "test")] + [DataRow("test", "test2", true, "masa", "|", "test")] + [DataRow("test", null, true, "masa", "|", "test")] + [DataRow("test", "test2", true, null, "|", "test")] + [DataRow("test", null, true, null, "|", "test")] + [DataTestMethod] + public void TestCompletionAppIdByMasaConfiguration( + string? inputAppId, + string? inputGlobalAppId, + bool inputDisableAppIdSuffix, + string? inputAppIdSuffix, + string? inputAppIdDelimiter, + string expectedAppId) + { + IOptions? masaAppConfigureOptions = inputGlobalAppId == null + ? null + : Options.Create(new MasaAppConfigureOptions() + { + AppId = inputGlobalAppId + }); + var services = new ServiceCollection(); + services.AddMasaConfiguration(builder => { builder.AddJsonFile("appsettings.json"); }); + var serviceProvider = services.BuildServiceProvider(); + var defaultDaprProvider = new DefaultDaprProvider( + masaAppConfigureOptions, + serviceProvider.GetService(), + serviceProvider.GetService()); + + var acceptAppId = inputAppIdDelimiter != null + ? defaultDaprProvider.CompletionAppId(inputAppId, inputDisableAppIdSuffix, inputAppIdSuffix, inputAppIdDelimiter) + : defaultDaprProvider.CompletionAppId(inputAppId, inputDisableAppIdSuffix, inputAppIdSuffix); + + + Assert.AreEqual( + expectedAppId == "test-masa" + ? "test3" + : expectedAppId.Replace("{default-appid}", DefaultAppId).Replace("{default-suffix}", DefaultAppIdSuffix), acceptAppId); + } +} diff --git a/src/Contrib/Development/Tests/Masa.Contrib.Development.DaprStarter.Tests/Masa.Contrib.Development.DaprStarter.Tests.csproj b/src/Contrib/Development/Tests/Masa.Contrib.Development.DaprStarter.Tests/Masa.Contrib.Development.DaprStarter.Tests.csproj index 209e4e854..8e952906e 100644 --- a/src/Contrib/Development/Tests/Masa.Contrib.Development.DaprStarter.Tests/Masa.Contrib.Development.DaprStarter.Tests.csproj +++ b/src/Contrib/Development/Tests/Masa.Contrib.Development.DaprStarter.Tests/Masa.Contrib.Development.DaprStarter.Tests.csproj @@ -24,6 +24,7 @@ + diff --git a/src/Contrib/Development/Tests/Masa.Contrib.Development.DaprStarter.Tests/_Imports.cs b/src/Contrib/Development/Tests/Masa.Contrib.Development.DaprStarter.Tests/_Imports.cs index 74cdf9eb2..ebec82e72 100644 --- a/src/Contrib/Development/Tests/Masa.Contrib.Development.DaprStarter.Tests/_Imports.cs +++ b/src/Contrib/Development/Tests/Masa.Contrib.Development.DaprStarter.Tests/_Imports.cs @@ -1,8 +1,13 @@ // Copyright (c) MASA Stack All rights reserved. // Licensed under the MIT License. See LICENSE.txt in the project root for license information. +global using Masa.BuildingBlocks.Configuration; +global using Masa.BuildingBlocks.Configuration.Options; global using Masa.BuildingBlocks.Development.DaprStarter; global using Microsoft.AspNetCore.Builder; +global using Microsoft.Extensions.Configuration; global using Microsoft.Extensions.DependencyInjection; global using Microsoft.Extensions.Options; global using Microsoft.VisualStudio.TestTools.UnitTesting; +global using System.Net.NetworkInformation; +global using System.Reflection; diff --git a/src/Contrib/Development/Tests/Masa.Contrib.Development.DaprStarter.Tests/appsettings.json b/src/Contrib/Development/Tests/Masa.Contrib.Development.DaprStarter.Tests/appsettings.json index 729646383..3079eb181 100644 --- a/src/Contrib/Development/Tests/Masa.Contrib.Development.DaprStarter.Tests/appsettings.json +++ b/src/Contrib/Development/Tests/Masa.Contrib.Development.DaprStarter.Tests/appsettings.json @@ -4,5 +4,6 @@ }, "DaprOptions2": { "AppId": "Test2" - } + }, + "test-masa": "test3" } From 1b95a0cfa66543a156da44898e16215474d426e4 Mon Sep 17 00:00:00 2001 From: zhenlei520 Date: Thu, 4 May 2023 22:56:49 +0800 Subject: [PATCH 2/5] fix: Fixed sidecar not terminating when project stopped unexpectedly --- .../DaprStarterConstant.cs | 2 ++ .../Options/DaprOptions.cs | 2 ++ .../DaprProcess.cs | 13 ++++++----- .../DaprProcessBase.cs | 12 ++++++++-- .../DaprProvider.cs | 23 +++++++++++++------ .../Internal/Options/SidecarOptions.cs | 8 +++++++ 6 files changed, 45 insertions(+), 15 deletions(-) diff --git a/src/BuildingBlocks/Development/Masa.BuildingBlocks.Development.DaprStarter/DaprStarterConstant.cs b/src/BuildingBlocks/Development/Masa.BuildingBlocks.Development.DaprStarter/DaprStarterConstant.cs index 4ce67c1ae..f06c5cc07 100644 --- a/src/BuildingBlocks/Development/Masa.BuildingBlocks.Development.DaprStarter/DaprStarterConstant.cs +++ b/src/BuildingBlocks/Development/Masa.BuildingBlocks.Development.DaprStarter/DaprStarterConstant.cs @@ -9,6 +9,8 @@ public static class DaprStarterConstant public const string DEFAULT_FILE_NAME = "dapr"; + public const string DEFAULT_SIDECAR_FILE_NAME = "daprd"; + public const string DEFAULT_PROCESS_NAME = "dapr-starter"; public const string DEFAULT_ARGUMENT_PREFIX = "--"; diff --git a/src/BuildingBlocks/Development/Masa.BuildingBlocks.Development.DaprStarter/Options/DaprOptions.cs b/src/BuildingBlocks/Development/Masa.BuildingBlocks.Development.DaprStarter/Options/DaprOptions.cs index 5e94c46d3..2ba4f4dfa 100644 --- a/src/BuildingBlocks/Development/Masa.BuildingBlocks.Development.DaprStarter/Options/DaprOptions.cs +++ b/src/BuildingBlocks/Development/Masa.BuildingBlocks.Development.DaprStarter/Options/DaprOptions.cs @@ -184,6 +184,8 @@ public override int HeartBeatInterval } } + public string PlacementHostAddress { get; set; } + public bool IsIncompleteAppId() { return !DisableAppIdSuffix && (AppIdSuffix == null || AppIdSuffix.Trim() != string.Empty); diff --git a/src/Contrib/Development/Masa.Contrib.Development.DaprStarter/DaprProcess.cs b/src/Contrib/Development/Masa.Contrib.Development.DaprStarter/DaprProcess.cs index 38a1983f5..33dc75d5f 100644 --- a/src/Contrib/Development/Masa.Contrib.Development.DaprStarter/DaprProcess.cs +++ b/src/Contrib/Development/Masa.Contrib.Development.DaprStarter/DaprProcess.cs @@ -8,7 +8,7 @@ public class DaprProcess : DaprProcessBase, IDaprProcess { private readonly object _lock = new(); - private readonly IDaprProcessProvider _daprProvider; + private readonly IDaprProcessProvider _daprProcessProvider; private readonly IProcessProvider _processProvider; private readonly ILogger? _logger; private readonly IOptionsMonitor _daprOptions; @@ -29,7 +29,7 @@ public class DaprProcess : DaprProcessBase, IDaprProcess private bool _isFirst = true; public DaprProcess( - IDaprProcessProvider daprProvider, + IDaprProcessProvider daprProcessProvider, IProcessProvider processProvider, IOptionsMonitor daprOptions, IDaprEnvironmentProvider daprEnvironmentProvider, @@ -38,7 +38,7 @@ public DaprProcess( IOptions? masaAppConfigureOptions = null) : base(daprEnvironmentProvider, masaAppConfigureOptions, daprProvide) { - _daprProvider = daprProvider; + _daprProcessProvider = daprProcessProvider; _processProvider = processProvider; _logger = logger; _daprOptions = daprOptions; @@ -67,7 +67,7 @@ private void StartCore(SidecarOptions options) UpdateStatus(DaprProcessStatus.Starting); var commandLineBuilder = CreateCommandLineBuilder(options); - _process = _daprProvider.DaprStart( + _process = _daprProcessProvider.DaprStart( commandLineBuilder.ToString(), options.CreateNoWindow, (_, args) => @@ -111,6 +111,7 @@ private void CompleteDaprEnvironment() { // Register the child process to the job object to ensure that the child process terminates when the parent process terminates // Windows only + ChildProcessTracker.AddProcess(_process); DaprEnvironmentProvider.TrySetHttpPort(SuccessDaprOptions.DaprHttpPort); @@ -158,7 +159,7 @@ private void StopCore() _process?.Kill(); if (SuccessDaprOptions!.EnableSsl is not true) { - _daprProvider.DaprStop(SuccessDaprOptions.AppId); + _daprProcessProvider.DaprStop(SuccessDaprOptions.AppId); } if (SuccessDaprOptions.DaprHttpPort != null) @@ -231,7 +232,7 @@ private void HeartBeat() return; } - var daprList = _daprProvider.GetDaprList(SuccessDaprOptions.AppId); + var daprList = _daprProcessProvider.GetDaprList(SuccessDaprOptions.AppId); if (daprList.Count > 1) { _logger?.LogDebug("dapr sidecar appears more than 1 same appid, this may cause error"); diff --git a/src/Contrib/Development/Masa.Contrib.Development.DaprStarter/DaprProcessBase.cs b/src/Contrib/Development/Masa.Contrib.Development.DaprStarter/DaprProcessBase.cs index 74ec026eb..61e4997e6 100644 --- a/src/Contrib/Development/Masa.Contrib.Development.DaprStarter/DaprProcessBase.cs +++ b/src/Contrib/Development/Masa.Contrib.Development.DaprStarter/DaprProcessBase.cs @@ -54,11 +54,18 @@ internal SidecarOptions ConvertToSidecarOptions(DaprOptions options) MetricsPort = options.MetricsPort, ProfilePort = options.ProfilePort, UnixDomainSocket = options.UnixDomainSocket, - DaprMaxRequestSize = options.DaprMaxRequestSize + DaprMaxRequestSize = options.DaprMaxRequestSize, + PlacementHostAddress = options.PlacementHostAddress }; sidecarOptions.TrySetHttpPort(options.DaprHttpPort ?? DaprEnvironmentProvider.GetHttpPort()); sidecarOptions.TrySetGrpcPort(options.DaprGrpcPort ?? DaprEnvironmentProvider.GetGrpcPort()); + if (sidecarOptions.EnableDefaultPlacementHostAddress && sidecarOptions.PlacementHostAddress.IsNullOrWhiteSpace()) + { + var port = Environment.OSVersion.Platform == PlatformID.Win32NT ? 6050 : 50005; + sidecarOptions.PlacementHostAddress = $"127.0.0.1:{port}"; + } + return sidecarOptions; } @@ -81,7 +88,8 @@ internal CommandLineBuilder CreateCommandLineBuilder(SidecarOptions options) .Add("metrics-port", options.MetricsPort?.ToString() ?? string.Empty, options.MetricsPort == null) .Add("profile-port", options.ProfilePort?.ToString() ?? string.Empty, options.ProfilePort == null) .Add("unix-domain-socket", options.UnixDomainSocket ?? string.Empty, options.UnixDomainSocket == null) - .Add("dapr-http-max-request-size", options.DaprMaxRequestSize?.ToString() ?? string.Empty, options.DaprMaxRequestSize == null); + .Add("dapr-http-max-request-size", options.DaprMaxRequestSize?.ToString() ?? string.Empty, options.DaprMaxRequestSize == null) + .Add("placement-host-address", options.PlacementHostAddress, options.PlacementHostAddress.IsNullOrWhiteSpace()); SuccessDaprOptions ??= options; return commandLineBuilder; diff --git a/src/Contrib/Development/Masa.Contrib.Development.DaprStarter/DaprProvider.cs b/src/Contrib/Development/Masa.Contrib.Development.DaprStarter/DaprProvider.cs index 0d911eb75..d0f77d59f 100644 --- a/src/Contrib/Development/Masa.Contrib.Development.DaprStarter/DaprProvider.cs +++ b/src/Contrib/Development/Masa.Contrib.Development.DaprStarter/DaprProvider.cs @@ -68,7 +68,7 @@ public Process DaprStart(string arguments, exitAction.Invoke(); _logger?.LogDebug("{Name} process has exited", DaprStarterConstant.DEFAULT_PROCESS_NAME); }; - return processUtils.Run(DaprStarterConstant.DEFAULT_FILE_NAME, $"run {arguments}", createNoWindow); + return processUtils.Run(DaprStarterConstant.DEFAULT_SIDECAR_FILE_NAME, $" {arguments}", createNoWindow); } private static void DaprProcess_OutputDataReceived(DataReceivedEventArgs e) @@ -113,11 +113,20 @@ private static void DaprProcess_ErrorDataReceived(object? sender, DataReceivedEv public void DaprStop(string appId) { - var process = new ProcessUtils(_logger).Run( - $"{DaprStarterConstant.DEFAULT_FILE_NAME}", - $"stop {appId}", - createNoWindow: false, - isWait: false); - process.WaitForExit(); + var daprList = GetDaprList(appId); + var pid = 0; + foreach (var dapr in daprList) + { + try + { + pid = dapr.PId; + var process = Process.GetProcessById(dapr.PId); + process.Kill(); + } + catch (Exception ex) + { + _logger?.LogWarning(ex, "Error stopping dapr sidecar, appid is {AppId}, pid is {PId}", appId, pid); + } + } } } diff --git a/src/Contrib/Development/Masa.Contrib.Development.DaprStarter/Internal/Options/SidecarOptions.cs b/src/Contrib/Development/Masa.Contrib.Development.DaprStarter/Internal/Options/SidecarOptions.cs index a6886fa81..adf3cdf7d 100644 --- a/src/Contrib/Development/Masa.Contrib.Development.DaprStarter/Internal/Options/SidecarOptions.cs +++ b/src/Contrib/Development/Masa.Contrib.Development.DaprStarter/Internal/Options/SidecarOptions.cs @@ -14,6 +14,14 @@ internal class SidecarOptions : DaprOptionsBase /// public string AppId { get; } + /// + /// Whether to use the default placement host address when no PlacementHostAddress is specified + /// default: true + /// + public bool EnableDefaultPlacementHostAddress { get; set; } = true; + + public string PlacementHostAddress { get; set; } + // ReSharper disable once InconsistentNaming public SidecarOptions( string appId, From 6e6cd031f8a0c75e6073d90368a887ce384ff561 Mon Sep 17 00:00:00 2001 From: zhenlei520 Date: Fri, 5 May 2023 17:19:37 +0800 Subject: [PATCH 3/5] chore: Adjust the startup sidecar process to daprd --- .../Options/DaprOptions.cs | 60 ++++++++- .../Options/DaprOptionsBase.cs | 10 +- .../DefaultAvailabilityPortProvider.cs | 41 ++++++ .../IAvailabilityPortProvider.cs | 9 ++ .../ServiceCollectionExtensions.cs | 68 +++++++--- .../_Imports.cs | 1 + .../CommandLineBuilder.cs | 4 +- .../DaprProcess.cs | 40 ++++-- .../DaprProcessBase.cs | 126 ++++++++++++------ .../Internal/Options/SidecarOptions.cs | 96 ++++++++++++- .../CommandLineBuilderTest.cs | 8 +- 11 files changed, 367 insertions(+), 96 deletions(-) create mode 100644 src/Contrib/Development/Masa.Contrib.Development.DaprStarter.AspNetCore/DefaultAvailabilityPortProvider.cs create mode 100644 src/Contrib/Development/Masa.Contrib.Development.DaprStarter.AspNetCore/IAvailabilityPortProvider.cs diff --git a/src/BuildingBlocks/Development/Masa.BuildingBlocks.Development.DaprStarter/Options/DaprOptions.cs b/src/BuildingBlocks/Development/Masa.BuildingBlocks.Development.DaprStarter/Options/DaprOptions.cs index 2ba4f4dfa..84fc10868 100644 --- a/src/BuildingBlocks/Development/Masa.BuildingBlocks.Development.DaprStarter/Options/DaprOptions.cs +++ b/src/BuildingBlocks/Development/Masa.BuildingBlocks.Development.DaprStarter/Options/DaprOptions.cs @@ -9,7 +9,7 @@ namespace Masa.BuildingBlocks.Development.DaprStarter; /// dapr startup configuration information /// When the specified attribute is configured as null, the default value of the parameter is subject to the default value of dapr of the current version /// -public class DaprOptions: DaprOptionsBase +public class DaprOptions : DaprOptionsBase { /// /// The id for your application, used for service discovery @@ -148,21 +148,21 @@ public override ushort? ProfilePort } } - private int? _daprMaxRequestSize; + private int? _daprHttpMaxRequestSize; /// /// Max size of request body in MB. /// Must be greater than 0 /// - public override int? DaprMaxRequestSize + public int? DaprHttpMaxRequestSize { - get => _daprMaxRequestSize; + get => _daprHttpMaxRequestSize; set { if (value != null) - MasaArgumentException.ThrowIfLessThanOrEqual(value.Value, (ushort)0, nameof(DaprMaxRequestSize)); + MasaArgumentException.ThrowIfLessThanOrEqual(value.Value, (ushort)0, nameof(DaprHttpMaxRequestSize)); - _daprMaxRequestSize = value; + _daprHttpMaxRequestSize = value; } } @@ -186,6 +186,54 @@ public override int HeartBeatInterval public string PlacementHostAddress { get; set; } + /// + /// Start the heartbeat check to ensure that the dapr program is active. + /// When the heartbeat check is turned off, dapr will not start automatically after it exits abnormally. + /// + public override bool EnableHeartBeat { get; set; } = true; + + public override bool CreateNoWindow { get; set; } = true; + + /// + /// Allowed HTTP origins (default "*") + /// + public string AllowedOrigins { get; set; } + + /// + /// Address for a Dapr control plane + /// + public string ControlPlaneAddress { get; set; } + + /// + /// Increasing max size of read buffer in KB to handle sending multi-KB headers (default 4) + /// + public int? DaprHttpReadBufferSize { get; set; } + + /// + /// gRPC port for the Dapr Internal API to listen on. + /// + public int? DaprInternalGrpcPort { get; set; } + + /// + /// Enable API logging for API calls + /// + public bool? EnableApiLogging { get; set; } + + /// + /// Enable prometheus metric (default true) + /// + public bool? EnableMetrics { get; set; } + + /// + /// Runtime mode for Dapr (default "standalone") + /// + public string Mode { get; set; } + + /// + /// Path for resources directory. If empty, resources will not be loaded. Self-hosted mode only + /// + public string ResourcesPath { get; set; } + public bool IsIncompleteAppId() { return !DisableAppIdSuffix && (AppIdSuffix == null || AppIdSuffix.Trim() != string.Empty); diff --git a/src/BuildingBlocks/Development/Masa.BuildingBlocks.Development.DaprStarter/Options/DaprOptionsBase.cs b/src/BuildingBlocks/Development/Masa.BuildingBlocks.Development.DaprStarter/Options/DaprOptionsBase.cs index a303f7e1b..1d8f64b95 100644 --- a/src/BuildingBlocks/Development/Masa.BuildingBlocks.Development.DaprStarter/Options/DaprOptionsBase.cs +++ b/src/BuildingBlocks/Development/Masa.BuildingBlocks.Development.DaprStarter/Options/DaprOptionsBase.cs @@ -32,6 +32,7 @@ public ushort? AppPort /// /// Enable https when Dapr invokes the application + /// Sets the URI scheme of the app to https and attempts an SSL connection /// public bool? EnableSsl { get; set; } @@ -46,11 +47,11 @@ public ushort? AppPort /// public virtual ushort? DaprHttpPort { get; set; } - public bool EnableHeartBeat { get; set; } + public virtual bool EnableHeartBeat { get; set; } public virtual int HeartBeatInterval { get; set; } - public bool CreateNoWindow { get; set; } = true; + public virtual bool CreateNoWindow { get; set; } /// /// The concurrency level of the application, otherwise is unlimited @@ -105,9 +106,4 @@ public ushort? AppPort /// Not available on Windows OS /// public string? UnixDomainSocket { get; set; } - - /// - /// Max size of request body in MB. - /// - public virtual int? DaprMaxRequestSize { get; set; } } diff --git a/src/Contrib/Development/Masa.Contrib.Development.DaprStarter.AspNetCore/DefaultAvailabilityPortProvider.cs b/src/Contrib/Development/Masa.Contrib.Development.DaprStarter.AspNetCore/DefaultAvailabilityPortProvider.cs new file mode 100644 index 000000000..87b3a2bc9 --- /dev/null +++ b/src/Contrib/Development/Masa.Contrib.Development.DaprStarter.AspNetCore/DefaultAvailabilityPortProvider.cs @@ -0,0 +1,41 @@ +// Copyright (c) MASA Stack All rights reserved. +// Licensed under the MIT License. See LICENSE.txt in the project root for license information. + +namespace Masa.Contrib.Development.DaprStarter.AspNetCore; + +[ExcludeFromCodeCoverage] +public class DefaultAvailabilityPortProvider : IAvailabilityPortProvider +{ + private readonly ILogger? _logger; + + public DefaultAvailabilityPortProvider(ILogger? logger = null) + { + _logger = logger; + } + + public ushort? GetAvailablePort(ushort startingPort, IEnumerable? reservedPorts = null) + { + MasaArgumentException.ThrowIfGreaterThan(startingPort, ushort.MaxValue); + try + { + var ipGlobalProperties = IPGlobalProperties.GetIPGlobalProperties(); + + var connectionsEndpoints = ipGlobalProperties.GetActiveTcpConnections().Select(c => c.LocalEndPoint); + var ports = connectionsEndpoints.Concat(ipGlobalProperties.GetActiveTcpListeners()) + .Concat(ipGlobalProperties.GetActiveUdpListeners()) + .Select(point => point.Port) + .ToList(); + if (reservedPorts != null) + { + ports.AddRange(reservedPorts); + } + + return (ushort)Enumerable.Range(startingPort, ushort.MaxValue - startingPort + 1).Except(ports).FirstOrDefault(); + } + catch (Exception ex) + { + _logger?.LogWarning(ex, "Failed to get available ports, startingPort: {StartingPort}", startingPort); + return null; + } + } +} diff --git a/src/Contrib/Development/Masa.Contrib.Development.DaprStarter.AspNetCore/IAvailabilityPortProvider.cs b/src/Contrib/Development/Masa.Contrib.Development.DaprStarter.AspNetCore/IAvailabilityPortProvider.cs new file mode 100644 index 000000000..99428c9e5 --- /dev/null +++ b/src/Contrib/Development/Masa.Contrib.Development.DaprStarter.AspNetCore/IAvailabilityPortProvider.cs @@ -0,0 +1,9 @@ +// Copyright (c) MASA Stack All rights reserved. +// Licensed under the MIT License. See LICENSE.txt in the project root for license information. + +namespace Masa.Contrib.Development.DaprStarter.AspNetCore; + +public interface IAvailabilityPortProvider +{ + ushort? GetAvailablePort(ushort startingPort, IEnumerable? reservedPorts = null); +} diff --git a/src/Contrib/Development/Masa.Contrib.Development.DaprStarter.AspNetCore/ServiceCollectionExtensions.cs b/src/Contrib/Development/Masa.Contrib.Development.DaprStarter.AspNetCore/ServiceCollectionExtensions.cs index f9865c930..cddbf19aa 100644 --- a/src/Contrib/Development/Masa.Contrib.Development.DaprStarter.AspNetCore/ServiceCollectionExtensions.cs +++ b/src/Contrib/Development/Masa.Contrib.Development.DaprStarter.AspNetCore/ServiceCollectionExtensions.cs @@ -11,10 +11,7 @@ public static IServiceCollection AddDaprStarter(this IServiceCollection services string sectionName = nameof(DaprOptions), bool isDelay = true) { - return services.AddDaprStarter(() => - { - services.AddDaprStarterCore(sectionName); - }, isDelay); + return services.AddDaprStarter(() => { services.AddDaprStarterCore(sectionName); }, isDelay); } public static IServiceCollection AddDaprStarter( @@ -24,10 +21,7 @@ public static IServiceCollection AddDaprStarter( { ArgumentNullException.ThrowIfNull(daprOptionAction); - return services.AddDaprStarter(() => - { - services.AddDaprStarterCore(daprOptionAction); - }, isDelay); + return services.AddDaprStarter(() => { services.AddDaprStarterCore(daprOptionAction); }, isDelay); } public static IServiceCollection AddDaprStarter( @@ -35,10 +29,7 @@ public static IServiceCollection AddDaprStarter( IConfiguration configuration, bool isDelay = true) { - return services.AddDaprStarter(() => - { - services.AddDaprStarterCore(configuration); - }, isDelay); + return services.AddDaprStarter(() => { services.AddDaprStarterCore(configuration); }, isDelay); } private static IServiceCollection AddDaprStarter(this IServiceCollection services, Action action, bool isDelay = true) @@ -49,13 +40,12 @@ private static IServiceCollection AddDaprStarter(this IServiceCollection service services.AddSingleton(); services.TryAddSingleton(); + services.TryAddSingleton(); action.Invoke(); var serviceProvider = services.BuildServiceProvider(); var options = serviceProvider.GetRequiredService>(); - var daprEnvironmentProvider = serviceProvider.GetRequiredService(); - daprEnvironmentProvider.TrySetGrpcPort(options.CurrentValue.DaprGrpcPort); - daprEnvironmentProvider.TrySetHttpPort(options.CurrentValue.DaprHttpPort); + CheckCompletionHttPortAndGrpcPort(options.CurrentValue, serviceProvider); if (isDelay) return services.AddHostedService(); @@ -65,9 +55,55 @@ private static IServiceCollection AddDaprStarter(this IServiceCollection service return services; } + private static void CheckCompletionHttPortAndGrpcPort(DaprOptions daprOptions, IServiceProvider serviceProvider) + { + var daprEnvironmentProvider = serviceProvider.GetRequiredService(); + + daprOptions.DaprHttpPort ??= daprEnvironmentProvider.GetHttpPort(); + daprOptions.DaprGrpcPort ??= daprEnvironmentProvider.GetGrpcPort(); + + var httpPortStatus = IsAvailablePort(daprOptions.DaprHttpPort); + var gRpcPortStatus = IsAvailablePort(daprOptions.DaprGrpcPort); + var httpPortByAvailability = daprOptions.DaprHttpPort; + var gRpcPortByAvailability = daprOptions.DaprGrpcPort; + if (!httpPortStatus || !gRpcPortStatus) + { + var reservedPorts = new List(); + var availabilityPortProvider = serviceProvider.GetRequiredService(); + + if (!httpPortStatus && !gRpcPortStatus) + { + //httpPort and grpcPort are not available + httpPortByAvailability = availabilityPortProvider.GetAvailablePort(3500, reservedPorts); + if (httpPortByAvailability != null) reservedPorts.Add(httpPortByAvailability.Value); + gRpcPortByAvailability = availabilityPortProvider.GetAvailablePort(50001, reservedPorts); + } + else if (!httpPortStatus) + { + //httpPort is not available + reservedPorts.Add(gRpcPortByAvailability!.Value); + httpPortByAvailability = availabilityPortProvider.GetAvailablePort(3500); + } + else if (!gRpcPortStatus) + { + //gRpcPort is not available + reservedPorts.Add(httpPortByAvailability!.Value); + gRpcPortByAvailability = availabilityPortProvider.GetAvailablePort(50001, reservedPorts); + } + } + + daprEnvironmentProvider.TrySetHttpPort(httpPortByAvailability); + daprEnvironmentProvider.TrySetGrpcPort(gRpcPortByAvailability); + + // Environment variables need to be improved + bool IsAvailablePort([NotNullWhen(true)] ushort? port) + { + return port is > 0; + } + } + private sealed class DaprService { - } } diff --git a/src/Contrib/Development/Masa.Contrib.Development.DaprStarter.AspNetCore/_Imports.cs b/src/Contrib/Development/Masa.Contrib.Development.DaprStarter.AspNetCore/_Imports.cs index fc7e9a731..90406d76d 100644 --- a/src/Contrib/Development/Masa.Contrib.Development.DaprStarter.AspNetCore/_Imports.cs +++ b/src/Contrib/Development/Masa.Contrib.Development.DaprStarter.AspNetCore/_Imports.cs @@ -12,3 +12,4 @@ global using Microsoft.Extensions.Logging; global using Microsoft.Extensions.Options; global using System.Diagnostics.CodeAnalysis; +global using System.Net.NetworkInformation; diff --git a/src/Contrib/Development/Masa.Contrib.Development.DaprStarter/CommandLineBuilder.cs b/src/Contrib/Development/Masa.Contrib.Development.DaprStarter/CommandLineBuilder.cs index 80704841f..a05d2157b 100644 --- a/src/Contrib/Development/Masa.Contrib.Development.DaprStarter/CommandLineBuilder.cs +++ b/src/Contrib/Development/Masa.Contrib.Development.DaprStarter/CommandLineBuilder.cs @@ -15,11 +15,11 @@ public CommandLineBuilder(string prefix) Arguments = new(); } - public CommandLineBuilder Add(string name, string value, bool isSkip = false) + public CommandLineBuilder Add(string name, Func valueFunc, bool isSkip = false) { if (!isSkip) { - Arguments.Add($"{Prefix}{name} {value}"); + Arguments.Add($"{Prefix}{name} {valueFunc.Invoke()}"); } return this; diff --git a/src/Contrib/Development/Masa.Contrib.Development.DaprStarter/DaprProcess.cs b/src/Contrib/Development/Masa.Contrib.Development.DaprStarter/DaprProcess.cs index 33dc75d5f..afc834de4 100644 --- a/src/Contrib/Development/Masa.Contrib.Development.DaprStarter/DaprProcess.cs +++ b/src/Contrib/Development/Masa.Contrib.Development.DaprStarter/DaprProcess.cs @@ -34,9 +34,8 @@ public DaprProcess( IOptionsMonitor daprOptions, IDaprEnvironmentProvider daprEnvironmentProvider, IDaprProvider daprProvide, - ILogger? logger = null, - IOptions? masaAppConfigureOptions = null) - : base(daprEnvironmentProvider, masaAppConfigureOptions, daprProvide) + ILogger? logger = null) + : base(daprEnvironmentProvider, daprProvide) { _daprProcessProvider = daprProcessProvider; _processProvider = processProvider; @@ -93,6 +92,12 @@ private void StartCore(SidecarOptions options) { _heartBeatTimer?.Start(); } + + // Register the child process to the job object to ensure that the child process terminates when the parent process terminates + // Windows only + + ChildProcessTracker.AddProcess(_process); + } private void CheckAndCompleteDaprEnvironment(string data) @@ -109,11 +114,6 @@ private void CompleteDaprEnvironment() { if (SuccessDaprOptions!.DaprHttpPort is > 0 && SuccessDaprOptions!.DaprGrpcPort is > 0) { - // Register the child process to the job object to ensure that the child process terminates when the parent process terminates - // Windows only - - ChildProcessTracker.AddProcess(_process); - DaprEnvironmentProvider.TrySetHttpPort(SuccessDaprOptions.DaprHttpPort); DaprEnvironmentProvider.TrySetGrpcPort(SuccessDaprOptions.DaprGrpcPort); _isFirst = false; @@ -183,24 +183,32 @@ private void Refresh(DaprOptions options) _logger?.LogDebug("Dapr sidecar configuration update, you need to start dapr through Start"); return; } + _logger?.LogDebug("Dapr sidecar configuration updated, Dapr AppId is {AppId}, please wait...", SuccessDaprOptions!.AppId); + var sidecarOptionsByRefresh = ConvertToSidecarOptions(options); if (SuccessDaprOptions != null) { - options.AppPort ??= SuccessDaprOptions.AppPort; - options.EnableSsl ??= SuccessDaprOptions.EnableSsl; - options.DaprHttpPort ??= SuccessDaprOptions.DaprHttpPort; - options.DaprGrpcPort ??= SuccessDaprOptions.DaprGrpcPort; + sidecarOptionsByRefresh.AppPort ??= SuccessDaprOptions.AppPort; + sidecarOptionsByRefresh.EnableSsl ??= SuccessDaprOptions.EnableSsl; + sidecarOptionsByRefresh.DaprHttpPort ??= SuccessDaprOptions.DaprHttpPort; + sidecarOptionsByRefresh.DaprGrpcPort ??= SuccessDaprOptions.DaprGrpcPort; + + if (sidecarOptionsByRefresh.Equals(SuccessDaprOptions)) + { + return; + } UpdateStatus(DaprProcessStatus.Restarting); - _logger?.LogDebug("Dapr sidecar configuration updated, Dapr AppId is {AppId}, closing dapr, please wait...", SuccessDaprOptions!.AppId); + _logger?.LogDebug("Dapr sidecar configuration updated, Dapr AppId is {AppId}, closing dapr, please wait...", + SuccessDaprOptions!.AppId); StopCore(); } _isFirst = true; SuccessDaprOptions = null; _logger?.LogDebug("Dapr sidecar configuration updated, Dapr AppId is {AppId}, restarting dapr, please wait...", options.AppId); - StartCore(ConvertToSidecarOptions(options)); + StartCore(sidecarOptionsByRefresh); } } @@ -226,6 +234,9 @@ private void HeartBeat() { lock (_lock) { + if (SuccessDaprOptions == null) + return; + if (SuccessDaprOptions!.EnableSsl is true) { _logger?.LogDebug("The dapr status cannot be monitored in https mode, the check has been skipped"); @@ -275,6 +286,7 @@ private void HeartBeat() var daprSidecar = daprList.First(); SuccessDaprOptions.TrySetHttpPort(daprSidecar.HttpPort); SuccessDaprOptions.TrySetGrpcPort(daprSidecar.GrpcPort); + UpdateStatus(DaprProcessStatus.Started); } } } diff --git a/src/Contrib/Development/Masa.Contrib.Development.DaprStarter/DaprProcessBase.cs b/src/Contrib/Development/Masa.Contrib.Development.DaprStarter/DaprProcessBase.cs index 61e4997e6..f20134e3a 100644 --- a/src/Contrib/Development/Masa.Contrib.Development.DaprStarter/DaprProcessBase.cs +++ b/src/Contrib/Development/Masa.Contrib.Development.DaprStarter/DaprProcessBase.cs @@ -8,29 +8,18 @@ public abstract class DaprProcessBase { protected IDaprEnvironmentProvider DaprEnvironmentProvider { get; } - private readonly IOptions? _masaAppConfigureOptions; - private readonly IDaprProvider _daprProvider; - /// - /// Use after getting dapr AppId and global AppId fails - /// - private static readonly string DefaultAppId = (Assembly.GetEntryAssembly() ?? Assembly.GetCallingAssembly()).GetName().Name!.Replace( - ".", - DaprStarterConstant.DEFAULT_APPID_DELIMITER); - - private const string HTTP_PORT_PATTERN = @"HTTP Port: ([0-9]+)"; - private const string GRPC_PORT_PATTERN = @"gRPC Port: ([0-9]+)"; + private static readonly string[] HttpPortPatterns = { "HTTP Port: ([0-9]+)", "http server is running on port ([0-9]+)" }; + private static readonly string[] GrpcPortPatterns = { "gRPC Port: ([0-9]+)", "API gRPC server is running on port ([0-9]+)" }; internal SidecarOptions? SuccessDaprOptions; protected DaprProcessBase( IDaprEnvironmentProvider daprEnvironmentProvider, - IOptions? masaAppConfigureOptions, IDaprProvider daprProvider) { DaprEnvironmentProvider = daprEnvironmentProvider; - _masaAppConfigureOptions = masaAppConfigureOptions; _daprProvider = daprProvider; } @@ -40,9 +29,9 @@ internal SidecarOptions ConvertToSidecarOptions(DaprOptions options) _daprProvider.CompletionAppId(options.AppId), options.AppPort, options.AppProtocol, - options.EnableSsl, - options.EnableHeartBeat) + options.EnableSsl) { + EnableHeartBeat = options.EnableHeartBeat, HeartBeatInterval = options.HeartBeatInterval, CreateNoWindow = options.CreateNoWindow, MaxConcurrency = options.MaxConcurrency, @@ -54,8 +43,17 @@ internal SidecarOptions ConvertToSidecarOptions(DaprOptions options) MetricsPort = options.MetricsPort, ProfilePort = options.ProfilePort, UnixDomainSocket = options.UnixDomainSocket, - DaprMaxRequestSize = options.DaprMaxRequestSize, - PlacementHostAddress = options.PlacementHostAddress + DaprHttpMaxRequestSize = options.DaprHttpMaxRequestSize, + PlacementHostAddress = options.PlacementHostAddress, + + AllowedOrigins = options.AllowedOrigins, + ControlPlaneAddress = options.ControlPlaneAddress, + DaprHttpReadBufferSize = options.DaprHttpReadBufferSize, + DaprInternalGrpcPort = options.DaprInternalGrpcPort, + EnableApiLogging = options.EnableApiLogging, + EnableMetrics = options.EnableMetrics, + Mode = options.Mode, + ResourcesPath = options.ResourcesPath, }; sidecarOptions.TrySetHttpPort(options.DaprHttpPort ?? DaprEnvironmentProvider.GetHttpPort()); sidecarOptions.TrySetGrpcPort(options.DaprGrpcPort ?? DaprEnvironmentProvider.GetGrpcPort()); @@ -73,49 +71,89 @@ internal CommandLineBuilder CreateCommandLineBuilder(SidecarOptions options) { var commandLineBuilder = new CommandLineBuilder(DaprStarterConstant.DEFAULT_ARGUMENT_PREFIX); commandLineBuilder - .Add("app-id", options.AppId) - .Add("app-port", options.GetAppPort().ToString()) - .Add("app-protocol", options.AppProtocol?.ToString().ToLower() ?? string.Empty, options.AppProtocol == null) - .Add("app-ssl", "", options.EnableSsl != true) - .Add("components-path", options.ComponentPath ?? string.Empty, options.ComponentPath == null) - .Add("app-max-concurrency", options.MaxConcurrency?.ToString() ?? string.Empty, options.MaxConcurrency == null) - .Add("config", options.Config ?? string.Empty, options.Config == null) - .Add("dapr-grpc-port", options.DaprGrpcPort?.ToString() ?? string.Empty, !(options.DaprGrpcPort > 0)) - .Add("dapr-http-port", options.DaprHttpPort?.ToString() ?? string.Empty, !(options.DaprHttpPort > 0)) - .Add("enable-profiling", options.EnableProfiling?.ToString().ToLower() ?? string.Empty, options.EnableProfiling == null) - .Add("log-level", options.LogLevel?.ToString().ToLower() ?? string.Empty, options.LogLevel == null) - .Add("sentry-address", options.SentryAddress ?? string.Empty, options.SentryAddress == null) - .Add("metrics-port", options.MetricsPort?.ToString() ?? string.Empty, options.MetricsPort == null) - .Add("profile-port", options.ProfilePort?.ToString() ?? string.Empty, options.ProfilePort == null) - .Add("unix-domain-socket", options.UnixDomainSocket ?? string.Empty, options.UnixDomainSocket == null) - .Add("dapr-http-max-request-size", options.DaprMaxRequestSize?.ToString() ?? string.Empty, options.DaprMaxRequestSize == null) - .Add("placement-host-address", options.PlacementHostAddress, options.PlacementHostAddress.IsNullOrWhiteSpace()); + .Add("app-id", () => options.AppId) + .Add("app-port", () => options.GetAppPort().ToString()) + .Add("app-protocol", () => options.AppProtocol!.Value.ToString().ToLower(), options.AppProtocol == null) + .Add("app-ssl", () => "", options.EnableSsl != true) + .Add("components-path", () => options.ComponentPath!, options.ComponentPath == null) + .Add("app-max-concurrency", () => options.MaxConcurrency!.Value.ToString(), options.MaxConcurrency == null) + .Add("config", () => options.Config!, options.Config == null) + .Add("dapr-grpc-port", () => options.DaprGrpcPort!.Value.ToString(), !(options.DaprGrpcPort > 0)) + .Add("dapr-http-port", () => options.DaprHttpPort!.Value.ToString(), !(options.DaprHttpPort > 0)) + .Add("enable-profiling", () => options.EnableProfiling!.Value.ToString().ToLower(), options.EnableProfiling == null) + .Add("log-level", () => options.LogLevel!.Value.ToString().ToLower(), options.LogLevel == null) + .Add("sentry-address", () => options.SentryAddress!, options.SentryAddress == null) + .Add("metrics-port", () => options.MetricsPort!.Value.ToString(), options.MetricsPort == null) + .Add("profile-port", () => options.ProfilePort!.Value.ToString(), options.ProfilePort == null) + .Add("unix-domain-socket", () => options.UnixDomainSocket!, options.UnixDomainSocket == null) + .Add("dapr-http-max-request-size", () => options.DaprHttpMaxRequestSize!.Value.ToString(), + options.DaprHttpMaxRequestSize == null) + .Add("placement-host-address", () => options.PlacementHostAddress, options.PlacementHostAddress.IsNullOrWhiteSpace()) + .Add("allowed-origins", () => options.AllowedOrigins, options.AllowedOrigins.IsNullOrWhiteSpace()) + .Add("control-plane-address", () => options.ControlPlaneAddress, options.ControlPlaneAddress.IsNullOrWhiteSpace()) + .Add("dapr-http-read-buffer-size", () => options.DaprHttpReadBufferSize!.Value.ToString(), + options.DaprHttpReadBufferSize == null) + .Add("dapr-internal-grpc-port", () => options.DaprInternalGrpcPort!.Value.ToString(), options.DaprInternalGrpcPort == null) + .Add("enable-api-logging", () => "", options.EnableApiLogging is not true) + + .Add("enable-metrics", () => "", options.EnableMetrics is not true) + .Add("mode", () => options.Mode, options.Mode.IsNullOrWhiteSpace()) + .Add("resources-path", () => options.ResourcesPath, options.ResourcesPath.IsNullOrWhiteSpace()); SuccessDaprOptions ??= options; return commandLineBuilder; } + #region GetHttpPort、GetGrpcPort + + /// + /// Get the HttpPort according to the output, but it is unreliable + /// After the prompt information output by dapr is adjusted, it may cause the failure to obtain the port + /// + /// + /// protected static ushort? GetHttpPort(string data) { - ushort? httpPort = 0; - var httpPortMatch = Regex.Matches(data, HTTP_PORT_PATTERN); - if (httpPortMatch.Count > 0) + foreach (var pattern in HttpPortPatterns) + { + var port = GetPort(data, pattern); + if (port is > 0) + return port; + } + + return null; + } + + static ushort? GetPort(string data, string pattern) + { + ushort? port = null; + var regex = new Regex(pattern, RegexOptions.IgnoreCase); + var match = regex.Matches(data); + if (match.Count > 0) { - httpPort = ushort.Parse(httpPortMatch[0].Groups[1].ToString()); + port = ushort.Parse(match[0].Groups[1].ToString()); } - return httpPort; + return port; } + /// + /// Get the gRpcPort according to the output, but it is unreliable + /// After the prompt information output by dapr is adjusted, it may cause the failure to obtain the port + /// + /// + /// protected static ushort? GetGrpcPort(string data) { - ushort? grpcPort = 0; - var grpcPortMatch = Regex.Matches(data, GRPC_PORT_PATTERN); - if (grpcPortMatch.Count > 0) + foreach (var pattern in GrpcPortPatterns) { - grpcPort = ushort.Parse(grpcPortMatch[0].Groups[1].ToString()); + var port = GetPort(data, pattern); + if (port is > 0) + return port; } - return grpcPort; + return null; } + + #endregion } diff --git a/src/Contrib/Development/Masa.Contrib.Development.DaprStarter/Internal/Options/SidecarOptions.cs b/src/Contrib/Development/Masa.Contrib.Development.DaprStarter/Internal/Options/SidecarOptions.cs index adf3cdf7d..9a7e177f2 100644 --- a/src/Contrib/Development/Masa.Contrib.Development.DaprStarter/Internal/Options/SidecarOptions.cs +++ b/src/Contrib/Development/Masa.Contrib.Development.DaprStarter/Internal/Options/SidecarOptions.cs @@ -22,19 +22,64 @@ internal class SidecarOptions : DaprOptionsBase public string PlacementHostAddress { get; set; } + public override bool EnableHeartBeat { get; set; } + + /// + /// Allowed HTTP origins (default "*") + /// + public string AllowedOrigins { get; set; } + + /// + /// Address for a Dapr control plane + /// + public string ControlPlaneAddress { get; set; } + + /// + /// Increasing max size of request body in MB to handle uploading of big files (default 4) + /// + public int? DaprHttpMaxRequestSize { get; set; } + + /// + /// Increasing max size of read buffer in KB to handle sending multi-KB headers (default 4) + /// + public int? DaprHttpReadBufferSize { get; set; } + + /// + /// gRPC port for the Dapr Internal API to listen on. + /// + public int? DaprInternalGrpcPort { get; set; } + + /// + /// Enable API logging for API calls + /// + public bool? EnableApiLogging { get; set; } + + /// + /// Enable prometheus metric (default true) + /// + public bool? EnableMetrics { get; set; } + + /// + /// Runtime mode for Dapr (default "standalone") + /// + public string Mode { get; set; } + + /// + /// Path for resources directory. If empty, resources will not be loaded. Self-hosted mode only + /// + public string ResourcesPath { get; set; } + // ReSharper disable once InconsistentNaming public SidecarOptions( string appId, ushort? appPort, Protocol? appProtocol, - bool? enableSsl, - bool enableHeartBeat) + bool? enableSsl) { AppId = appId; AppPort = appPort; AppProtocol = appProtocol; EnableSsl = enableSsl; - EnableHeartBeat = enableHeartBeat; } public bool TrySetHttpPort(ushort? httpPort) @@ -65,4 +110,49 @@ public ushort GetAppPort() MasaArgumentException.ThrowIfNull(AppPort); return AppPort.Value; } + + public override int GetHashCode() + { + return GetContent().Aggregate(0, HashCode.Combine); + } + + public override bool Equals(object? obj) + { + if (this is null ^ obj is null) return false; + + if (obj is SidecarOptions other) + { + return other.GetContent().SequenceEqual(GetContent()); + } + + return false; + } + + private IEnumerable GetContent() + { + return new List + { + GetAppPort(), + AppProtocol, + EnableSsl, + DaprGrpcPort, + DaprHttpPort, + EnableHeartBeat, + HeartBeatInterval, + CreateNoWindow, + MaxConcurrency, + Config, + ComponentPath, + EnableProfiling, + LogLevel, + SentryAddress, + MetricsPort, + ProfilePort, + UnixDomainSocket, + DaprHttpReadBufferSize, + AppId, + EnableDefaultPlacementHostAddress, + PlacementHostAddress + }; + } } diff --git a/src/Contrib/Development/Tests/Masa.Contrib.Development.DaprStarter.Tests/CommandLineBuilderTest.cs b/src/Contrib/Development/Tests/Masa.Contrib.Development.DaprStarter.Tests/CommandLineBuilderTest.cs index 4f2cb6186..211c31863 100644 --- a/src/Contrib/Development/Tests/Masa.Contrib.Development.DaprStarter.Tests/CommandLineBuilderTest.cs +++ b/src/Contrib/Development/Tests/Masa.Contrib.Development.DaprStarter.Tests/CommandLineBuilderTest.cs @@ -14,11 +14,11 @@ public class CommandLineBuilderTest [DataRow(5001, 5000, "--app-id test --dapr-grpc-port 5001 --dapr-http-port 5000")] public void TestToString(int? grpcPort, int? httpPort, string expectedResult) { - var builder = new CommandLineBuilder(Masa.BuildingBlocks.Development.DaprStarter.DaprStarterConstant.DEFAULT_ARGUMENT_PREFIX); + var builder = new CommandLineBuilder(DaprStarterConstant.DEFAULT_ARGUMENT_PREFIX); builder - .Add("app-id", "test", false) - .Add("dapr-grpc-port", grpcPort?.ToString() ?? string.Empty, !(grpcPort > 0)) - .Add("dapr-http-port", httpPort?.ToString() ?? string.Empty, !(httpPort > 0)); + .Add("app-id", () => "test", false) + .Add("dapr-grpc-port", () => grpcPort?.ToString() ?? string.Empty, !(grpcPort > 0)) + .Add("dapr-http-port", () => httpPort?.ToString() ?? string.Empty, !(httpPort > 0)); var result = builder.ToString(); Assert.AreEqual(expectedResult, result); } From bb39b2437e1722b838979cd5813dfe89f6c032de Mon Sep 17 00:00:00 2001 From: zhenlei520 Date: Fri, 5 May 2023 23:31:23 +0800 Subject: [PATCH 4/5] chore: Dealing with Code Smells --- .../Options/DaprOptionsBase.cs | 4 +- .../DaprBackgroundService.cs | 28 +++++-- .../DefaultAppPortProvider.cs | 44 +++++++--- .../IAppPortProvider.cs | 2 + .../Internal/PortUtils.cs | 57 +++++++++++++ .../ServiceCollectionExtensions.cs | 80 ++++--------------- .../_Imports.cs | 2 +- .../DaprProcess.cs | 25 +++--- .../DaprProcessBase.cs | 36 +++++---- .../DaprProvider.cs | 8 +- .../Internal/ChildProcessTracker.cs | 65 ++++++++------- .../Internal/Options/SidecarOptions.cs | 2 + .../DefaultAppPortProviderTest.cs | 25 +++++- 13 files changed, 228 insertions(+), 150 deletions(-) create mode 100644 src/Contrib/Development/Masa.Contrib.Development.DaprStarter.AspNetCore/Internal/PortUtils.cs diff --git a/src/BuildingBlocks/Development/Masa.BuildingBlocks.Development.DaprStarter/Options/DaprOptionsBase.cs b/src/BuildingBlocks/Development/Masa.BuildingBlocks.Development.DaprStarter/Options/DaprOptionsBase.cs index b724689c3..530bb9b57 100644 --- a/src/BuildingBlocks/Development/Masa.BuildingBlocks.Development.DaprStarter/Options/DaprOptionsBase.cs +++ b/src/BuildingBlocks/Development/Masa.BuildingBlocks.Development.DaprStarter/Options/DaprOptionsBase.cs @@ -5,6 +5,7 @@ namespace Masa.BuildingBlocks.Development.DaprStarter; +#pragma warning disable S3236 public abstract class DaprOptionsBase { private ushort? _appPort; @@ -29,7 +30,7 @@ public ushort? AppPort /// The protocol (gRPC or HTTP) Dapr uses to talk to the application. Valid values are: http or grpc /// default: HTTP /// - public Protocol? AppProtocol { get; protected set; } + public Protocol? AppProtocol { get; protected set; } = Protocol.Http; /// /// Enable https when Dapr invokes the application @@ -121,3 +122,4 @@ public ushort? AppPort /// public string? UnixDomainSocket { get; set; } } +#pragma warning restore S3236 diff --git a/src/Contrib/Development/Masa.Contrib.Development.DaprStarter.AspNetCore/DaprBackgroundService.cs b/src/Contrib/Development/Masa.Contrib.Development.DaprStarter.AspNetCore/DaprBackgroundService.cs index 62cfab234..b57edc807 100644 --- a/src/Contrib/Development/Masa.Contrib.Development.DaprStarter.AspNetCore/DaprBackgroundService.cs +++ b/src/Contrib/Development/Masa.Contrib.Development.DaprStarter.AspNetCore/DaprBackgroundService.cs @@ -11,18 +11,21 @@ public class DaprBackgroundService : BackgroundService private readonly DaprOptions _options; private readonly IHostApplicationLifetime _hostApplicationLifetime; private readonly ILogger? _logger; + private readonly IServiceProvider _serviceProvider; public DaprBackgroundService( IAppPortProvider appPortProvider, IDaprProcess daprProcess, IOptionsMonitor options, IHostApplicationLifetime hostApplicationLifetime, + IServiceProvider serviceProvider, ILogger? logger) { _appPortProvider = appPortProvider; _daprProcess = daprProcess; _options = options.CurrentValue; _hostApplicationLifetime = hostApplicationLifetime; + _serviceProvider = serviceProvider; _logger = logger; } @@ -41,21 +44,36 @@ protected override async Task ExecuteAsync(CancellationToken stoppingToken) { _logger?.LogInformation("{Name} is Starting ...", nameof(DaprBackgroundService)); - CheckCompletionAppPort(_options); + CheckCompletionAppPortAndEnableSSl(_options); + PortUtils.CheckCompletionPort(_options, _serviceProvider); _daprProcess.Start(); } } - private void CheckCompletionAppPort(DaprOptions daprOptions) + private void CheckCompletionAppPortAndEnableSSl(DaprOptions daprOptions) { - if (daprOptions.AppPort == null) + if (daprOptions.AppPort == null || daprOptions.EnableSsl == null) { - CompletionAppPort(daprOptions); + if (daprOptions.EnableSsl == null && daprOptions.AppPort != null) + { + daprOptions.EnableSsl = _appPortProvider.GetEnableSsl(daprOptions.AppPort.Value); + } + else + { + CompletionAppPortAndEnableSSl(daprOptions); + } + } + else + { + if (daprOptions.EnableSsl != _appPortProvider.GetEnableSsl(daprOptions.AppPort.Value)) + { + throw new UserFriendlyException($"The current AppPort: {daprOptions.AppPort.Value} is not an {(daprOptions.EnableSsl is true ? "Https" : "Http")} port, Dapr failed to start"); + } } } - private void CompletionAppPort(DaprOptions daprOptions) + private void CompletionAppPortAndEnableSSl(DaprOptions daprOptions) { var item = _appPortProvider.GetAppPort(daprOptions.EnableSsl); if (daprOptions.EnableSsl == null) diff --git a/src/Contrib/Development/Masa.Contrib.Development.DaprStarter.AspNetCore/DefaultAppPortProvider.cs b/src/Contrib/Development/Masa.Contrib.Development.DaprStarter.AspNetCore/DefaultAppPortProvider.cs index fcc04b556..6eace1372 100644 --- a/src/Contrib/Development/Masa.Contrib.Development.DaprStarter.AspNetCore/DefaultAppPortProvider.cs +++ b/src/Contrib/Development/Masa.Contrib.Development.DaprStarter.AspNetCore/DefaultAppPortProvider.cs @@ -9,7 +9,36 @@ public class DefaultAppPortProvider : IAppPortProvider public DefaultAppPortProvider(IServer server) => _server = server; + public bool GetEnableSsl(ushort appPort) + { + var ports = GetPorts(); + if (ports.Any(p => p.Port == appPort)) + { + var port = ports.First(p => p.Port == appPort); + return port.Scheme.Equals(Uri.UriSchemeHttps, StringComparison.OrdinalIgnoreCase); + } + + throw new UserFriendlyException($"The current port {appPort} is unavailable, Dapr failed to start"); + } + public (bool EnableSsl, ushort AppPort) GetAppPort(bool? enableSsl) + { + var ports = GetPorts(); + + if (ports.Count == 1) + { + return new(ports[0].Scheme.Equals(Uri.UriSchemeHttps, StringComparison.OrdinalIgnoreCase), (ushort)ports[0].Item2); + } + + if (enableSsl is false && ports.Any(p => p.Scheme.Equals(Uri.UriSchemeHttp, StringComparison.OrdinalIgnoreCase))) + { + return new(false, GetAppPort(ports, false)); + } + + return new(true, GetAppPort(ports, true)); + } + + private List<(string Scheme, int Port)> GetPorts() { var addresses = _server.Features.Get()?.Addresses; if (addresses is { IsReadOnly: false, Count: 0 }) @@ -19,19 +48,9 @@ public class DefaultAppPortProvider : IAppPortProvider .Select(address => new Uri(address)) .Where(address => address.Scheme.Equals(Uri.UriSchemeHttps, StringComparison.OrdinalIgnoreCase) - || address.Scheme.Equals(Uri.UriSchemeHttp, StringComparison.OrdinalIgnoreCase)) + || address.Scheme.Equals(Uri.UriSchemeHttp, StringComparison.OrdinalIgnoreCase)) .Select(address => new ValueTuple(address.Scheme, address.Port)).ToList(); - - if (ports.Count == 1) - { - return new(ports[0].Item1.Equals(Uri.UriSchemeHttps, StringComparison.OrdinalIgnoreCase), (ushort)ports[0].Item2); - } - - if (enableSsl is true && ports.Any(p => p.Item1.Equals(Uri.UriSchemeHttps, StringComparison.OrdinalIgnoreCase))) - { - return new(true, GetAppPort(ports, true)); - } - return new(false, GetAppPort(ports, false)); + return ports; } public static ushort GetAppPort(List> ports, bool enableSsl) @@ -43,6 +62,7 @@ public static ushort GetAppPort(List> ports, bool enable .Select(p => (ushort)p.Item2) .FirstOrDefault(); } + return ports .Where(p => p.Item1.Equals(Uri.UriSchemeHttp, StringComparison.OrdinalIgnoreCase)) .Select(p => (ushort)p.Item2) diff --git a/src/Contrib/Development/Masa.Contrib.Development.DaprStarter.AspNetCore/IAppPortProvider.cs b/src/Contrib/Development/Masa.Contrib.Development.DaprStarter.AspNetCore/IAppPortProvider.cs index c2490405b..fe5e8b9db 100644 --- a/src/Contrib/Development/Masa.Contrib.Development.DaprStarter.AspNetCore/IAppPortProvider.cs +++ b/src/Contrib/Development/Masa.Contrib.Development.DaprStarter.AspNetCore/IAppPortProvider.cs @@ -5,5 +5,7 @@ namespace Masa.Contrib.Development.DaprStarter.AspNetCore; public interface IAppPortProvider { + bool GetEnableSsl(ushort appPort); + (bool EnableSsl, ushort AppPort) GetAppPort(bool? enableSsl); } diff --git a/src/Contrib/Development/Masa.Contrib.Development.DaprStarter.AspNetCore/Internal/PortUtils.cs b/src/Contrib/Development/Masa.Contrib.Development.DaprStarter.AspNetCore/Internal/PortUtils.cs new file mode 100644 index 000000000..ae0502df1 --- /dev/null +++ b/src/Contrib/Development/Masa.Contrib.Development.DaprStarter.AspNetCore/Internal/PortUtils.cs @@ -0,0 +1,57 @@ +// Copyright (c) MASA Stack All rights reserved. +// Licensed under the MIT License. See LICENSE.txt in the project root for license information. + +// ReSharper disable once CheckNamespace + +namespace Masa.Contrib.Development.DaprStarter.AspNetCore; + +internal static class PortUtils +{ + public static void CheckCompletionPort(DaprOptions daprOptions, IServiceProvider serviceProvider) + { + var daprEnvironmentProvider = serviceProvider.GetRequiredService(); + + daprOptions.DaprHttpPort ??= daprEnvironmentProvider.GetHttpPort(); + daprOptions.DaprGrpcPort ??= daprEnvironmentProvider.GetGrpcPort(); + + var reservedPorts = new List(); + AddReservedPorts(daprOptions.DaprHttpPort); + AddReservedPorts(daprOptions.DaprGrpcPort); + AddReservedPorts(daprOptions.MetricsPort); + AddReservedPorts(daprOptions.ProfilePort); + AddReservedPorts(daprOptions.AppPort); + + var availabilityPortProvider = serviceProvider.GetRequiredService(); + + daprEnvironmentProvider.TrySetHttpPort(GetPortAndAddReservedPortsByAvailability(daprOptions.DaprHttpPort, 3500)); + daprEnvironmentProvider.TrySetGrpcPort(GetPortAndAddReservedPortsByAvailability(daprOptions.DaprGrpcPort, 5001)); + daprEnvironmentProvider.TrySetMetricsPort(GetPortAndAddReservedPortsByAvailability(daprOptions.MetricsPort, 9090)); + + // Environment variables need to be improved + bool IsAvailablePort([NotNullWhen(true)] ushort? port) + { + return port is > 0; + } + + void AddReservedPorts(ushort? port) + { + if (port is > 0) reservedPorts.Add(port.Value); + } + + ushort? GetPortAndAddReservedPortsByAvailability(ushort? port, ushort startingPort) + { + ushort? portByAvailability; + if (IsAvailablePort(port)) + { + portByAvailability = port.Value; + } + else + { + portByAvailability = availabilityPortProvider.GetAvailablePort(startingPort, reservedPorts); + if (portByAvailability != null) reservedPorts.Add(portByAvailability.Value); + } + + return portByAvailability; + } + } +} diff --git a/src/Contrib/Development/Masa.Contrib.Development.DaprStarter.AspNetCore/ServiceCollectionExtensions.cs b/src/Contrib/Development/Masa.Contrib.Development.DaprStarter.AspNetCore/ServiceCollectionExtensions.cs index 34112b421..77ddbe16f 100644 --- a/src/Contrib/Development/Masa.Contrib.Development.DaprStarter.AspNetCore/ServiceCollectionExtensions.cs +++ b/src/Contrib/Development/Masa.Contrib.Development.DaprStarter.AspNetCore/ServiceCollectionExtensions.cs @@ -11,7 +11,10 @@ public static IServiceCollection AddDaprStarter(this IServiceCollection services string sectionName = nameof(DaprOptions), bool isDelay = true) { - return services.AddDaprStarter(() => { services.AddDaprStarterCore(sectionName); }, isDelay); + return services.AddDaprStarter(() => + { + services.AddDaprStarterCore(sectionName); + }, isDelay); } public static IServiceCollection AddDaprStarter( @@ -21,7 +24,10 @@ public static IServiceCollection AddDaprStarter( { ArgumentNullException.ThrowIfNull(daprOptionAction); - return services.AddDaprStarter(() => { services.AddDaprStarterCore(daprOptionAction); }, isDelay); + return services.AddDaprStarter(() => + { + services.AddDaprStarterCore(daprOptionAction); + }, isDelay); } public static IServiceCollection AddDaprStarter( @@ -29,7 +35,10 @@ public static IServiceCollection AddDaprStarter( IConfiguration configuration, bool isDelay = true) { - return services.AddDaprStarter(() => { services.AddDaprStarterCore(configuration); }, isDelay); + return services.AddDaprStarter(() => + { + services.AddDaprStarterCore(configuration); + }, isDelay); } private static IServiceCollection AddDaprStarter(this IServiceCollection services, Action action, bool isDelay = true) @@ -45,79 +54,18 @@ private static IServiceCollection AddDaprStarter(this IServiceCollection service var serviceProvider = services.BuildServiceProvider(); var options = serviceProvider.GetRequiredService>(); - CheckCompletionPort(options.CurrentValue, serviceProvider); if (isDelay) return services.AddHostedService(); + PortUtils.CheckCompletionPort(options.CurrentValue, serviceProvider); + ArgumentNullException.ThrowIfNull(options.CurrentValue.AppPort); var daprProcess = serviceProvider.GetRequiredService(); daprProcess.Start(); return services; } - private static void CheckCompletionPort(DaprOptions daprOptions, IServiceProvider serviceProvider) - { - var daprEnvironmentProvider = serviceProvider.GetRequiredService(); - - daprOptions.DaprHttpPort ??= daprEnvironmentProvider.GetHttpPort(); - daprOptions.DaprGrpcPort ??= daprEnvironmentProvider.GetGrpcPort(); - var httpPortStatus = IsAvailablePort(daprOptions.DaprHttpPort); - var gRpcPortStatus = IsAvailablePort(daprOptions.DaprGrpcPort); - var metricsStatus = IsAvailablePort(daprOptions.MetricsPort); - - var httpPortByAvailability = daprOptions.DaprHttpPort; - var gRpcPortByAvailability = daprOptions.DaprGrpcPort; - var metricsPortByAvailability = daprOptions.MetricsPort; - - if (!httpPortStatus || !gRpcPortStatus || !metricsStatus) - { - var reservedPorts = new List(); - AddReservedPorts(httpPortByAvailability); - AddReservedPorts(gRpcPortByAvailability); - AddReservedPorts(metricsPortByAvailability); - AddReservedPorts(daprOptions.ProfilePort); - AddReservedPorts(daprOptions.AppPort); - - var availabilityPortProvider = serviceProvider.GetRequiredService(); - - if (!httpPortStatus) - { - httpPortByAvailability = availabilityPortProvider.GetAvailablePort(3500, reservedPorts); - if (httpPortByAvailability != null) reservedPorts.Add(httpPortByAvailability.Value); - } - - if (!gRpcPortStatus) - { - gRpcPortByAvailability = availabilityPortProvider.GetAvailablePort(50001, reservedPorts); - if (gRpcPortByAvailability != null) reservedPorts.Add(gRpcPortByAvailability.Value); - } - - if (!metricsStatus) - { - metricsPortByAvailability = availabilityPortProvider.GetAvailablePort(9090, reservedPorts); - if (metricsPortByAvailability != null) reservedPorts.Add(metricsPortByAvailability.Value); - } - - void AddReservedPorts(ushort? port) - { - if (port is > 0) - { - reservedPorts.Add(port.Value); - } - } - } - - daprEnvironmentProvider.TrySetHttpPort(httpPortByAvailability); - daprEnvironmentProvider.TrySetGrpcPort(gRpcPortByAvailability); - daprEnvironmentProvider.TrySetMetricsPort(metricsPortByAvailability); - - // Environment variables need to be improved - bool IsAvailablePort([NotNullWhen(true)] ushort? port) - { - return port is > 0; - } - } private sealed class DaprService diff --git a/src/Contrib/Development/Masa.Contrib.Development.DaprStarter.AspNetCore/_Imports.cs b/src/Contrib/Development/Masa.Contrib.Development.DaprStarter.AspNetCore/_Imports.cs index 90406d76d..8b11e86ae 100644 --- a/src/Contrib/Development/Masa.Contrib.Development.DaprStarter.AspNetCore/_Imports.cs +++ b/src/Contrib/Development/Masa.Contrib.Development.DaprStarter.AspNetCore/_Imports.cs @@ -2,11 +2,11 @@ // Licensed under the MIT License. See LICENSE.txt in the project root for license information. global using Masa.BuildingBlocks.Development.DaprStarter; -global using Masa.Contrib.Development.DaprStarter; global using Masa.Contrib.Development.DaprStarter.AspNetCore; global using Microsoft.AspNetCore.Hosting.Server; global using Microsoft.AspNetCore.Hosting.Server.Features; global using Microsoft.Extensions.Configuration; +global using Microsoft.Extensions.DependencyInjection; global using Microsoft.Extensions.DependencyInjection.Extensions; global using Microsoft.Extensions.Hosting; global using Microsoft.Extensions.Logging; diff --git a/src/Contrib/Development/Masa.Contrib.Development.DaprStarter/DaprProcess.cs b/src/Contrib/Development/Masa.Contrib.Development.DaprStarter/DaprProcess.cs index 7a0edcbf1..ae8028868 100644 --- a/src/Contrib/Development/Masa.Contrib.Development.DaprStarter/DaprProcess.cs +++ b/src/Contrib/Development/Masa.Contrib.Development.DaprStarter/DaprProcess.cs @@ -48,7 +48,7 @@ public void Start() lock (_lock) { _isStopByManually = false; - var sidecarOptions = ConvertToSidecarOptions(_daprOptions.CurrentValue); + var sidecarOptions = ConvertToSidecarOptions(DaprOptions.CurrentValue); StartCore(sidecarOptions); } @@ -60,7 +60,7 @@ private void StartCore(SidecarOptions options) var commandLineBuilder = CreateCommandLineBuilder(options); _process = _daprProcessProvider.DaprStart( - _defaultSidecarFileName!, + GetDefaultSidecarFileName(), commandLineBuilder.ToString(), options.CreateNoWindow, (_, args) => @@ -68,7 +68,7 @@ private void StartCore(SidecarOptions options) if (args.Data == null) return; - if (_isFirst) CheckAndCompleteDaprEnvironment(args.Data); + if (IsFirst) CheckAndCompleteDaprEnvironment(args.Data); }, () => UpdateStatus(DaprProcessStatus.Stopped)); _retryTime = 0; @@ -90,6 +90,11 @@ private void StartCore(SidecarOptions options) // Register the child process to the job object to ensure that the child process terminates when the parent process terminates // Windows only + if (Environment.OSVersion.Platform != PlatformID.Win32NT) + { + //Only supported on windows + return; + } ChildProcessTracker.AddProcess(_process); } @@ -109,7 +114,7 @@ private void CompleteDaprEnvironment() { DaprEnvironmentProvider.TrySetHttpPort(SuccessDaprOptions.DaprHttpPort); DaprEnvironmentProvider.TrySetGrpcPort(SuccessDaprOptions.DaprGrpcPort); - _isFirst = false; + IsFirst = false; _retryTime = 0; UpdateStatus(DaprProcessStatus.Started); } @@ -152,7 +157,7 @@ private void StopCore() _process?.Kill(); if (SuccessDaprOptions!.EnableSsl is not true) { - _daprProcessProvider.DaprStop(_defaultSidecarFileName!, SuccessDaprOptions.AppId); + _daprProcessProvider.DaprStop(GetDefaultDaprFileName(), SuccessDaprOptions.AppId); } if (SuccessDaprOptions.DaprHttpPort != null) @@ -194,7 +199,7 @@ private void Refresh(DaprOptions options) StopCore(); } - _isFirst = true; + IsFirst = true; SuccessDaprOptions = null; _logger?.LogDebug("Dapr sidecar configuration updated, Dapr AppId is {AppId}, restarting dapr, please wait...", options.AppId); StartCore(sidecarOptionsByRefresh); @@ -226,13 +231,7 @@ private void HeartBeat() if (SuccessDaprOptions == null) return; - if (SuccessDaprOptions!.EnableSsl is true) - { - _logger?.LogDebug("The dapr status cannot be monitored in https mode, the check has been skipped"); - return; - } - - var daprList = _daprProcessProvider.GetDaprList(_defaultDaprFileName!, SuccessDaprOptions.AppId, out bool isException); + var daprList = _daprProcessProvider.GetDaprList(GetDefaultDaprFileName(), SuccessDaprOptions.AppId, out bool isException); if (daprList.Count > 1) { _logger?.LogDebug("dapr sidecar appears more than 1 same appid, this may cause error"); diff --git a/src/Contrib/Development/Masa.Contrib.Development.DaprStarter/DaprProcessBase.cs b/src/Contrib/Development/Masa.Contrib.Development.DaprStarter/DaprProcessBase.cs index 6f38ad1ce..0e0c21fba 100644 --- a/src/Contrib/Development/Masa.Contrib.Development.DaprStarter/DaprProcessBase.cs +++ b/src/Contrib/Development/Masa.Contrib.Development.DaprStarter/DaprProcessBase.cs @@ -15,15 +15,14 @@ public abstract class DaprProcessBase private static readonly string UserFilePath = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile); internal SidecarOptions? SuccessDaprOptions; - protected readonly IOptionsMonitor _daprOptions; - protected static string? _defaultDaprFileName; - protected static string? _defaultSidecarFileName; + protected readonly IOptionsMonitor DaprOptions; + private static string? _defaultDaprFileName; + private static string? _defaultSidecarFileName; /// /// record whether dapr is initialized for the first time /// - protected bool _isFirst = true; - + protected bool IsFirst = true; protected DaprProcessBase( IDaprEnvironmentProvider daprEnvironmentProvider, @@ -32,7 +31,7 @@ protected DaprProcessBase( { DaprEnvironmentProvider = daprEnvironmentProvider; _daprProvider = daprProvider; - _daprOptions = daprOptions; + DaprOptions = daprOptions; } internal SidecarOptions ConvertToSidecarOptions(DaprOptions options) @@ -131,14 +130,13 @@ internal CommandLineBuilder CreateCommandLineBuilder(SidecarOptions options) .Add("enable-api-logging", () => "", options.EnableApiLogging is not true) .Add("enable-metrics", () => "", options.EnableMetrics is not true) .Add("mode", () => options.Mode, options.Mode.IsNullOrWhiteSpace()) - .Add(options.ExtendedParameter, () => ""); + .Add(options.ExtendedParameter, () => "", options.ExtendedParameter.IsNullOrWhiteSpace()); - if (_isFirst) - { - _defaultDaprFileName = Path.Combine(options.DaprRootPath, GetFileName(DaprStarterConstant.DEFAULT_DAPR_FILE_NAME)); - _defaultSidecarFileName = Path.Combine(options.RootPath, "bin", GetFileName(DaprStarterConstant.DEFAULT_FILE_NAME)); - SuccessDaprOptions = options; - } + if (!IsFirst) + return commandLineBuilder; + + SetDefaultFileName(options.DaprRootPath, options.RootPath); + SuccessDaprOptions = options; return commandLineBuilder; } @@ -166,7 +164,7 @@ internal CommandLineBuilder CreateCommandLineBuilder(SidecarOptions options) static ushort? GetPort(string data, string pattern) { ushort? port = null; - var regex = new Regex(pattern, RegexOptions.IgnoreCase); + var regex = new Regex(pattern, RegexOptions.IgnoreCase, TimeSpan.FromSeconds(1)); var match = regex.Matches(data); if (match.Count > 0) { @@ -197,4 +195,14 @@ internal CommandLineBuilder CreateCommandLineBuilder(SidecarOptions options) #endregion static string GetFileName(string fileName) => RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? $"{fileName}.exe" : fileName; + + protected static string GetDefaultDaprFileName() => _defaultDaprFileName!; + + protected static string GetDefaultSidecarFileName() => _defaultSidecarFileName!; + + private static void SetDefaultFileName(string daprRootPath, string sidecarRootPath) + { + _defaultDaprFileName = Path.Combine(daprRootPath, GetFileName(DaprStarterConstant.DEFAULT_DAPR_FILE_NAME)); + _defaultSidecarFileName = Path.Combine(sidecarRootPath, "bin", GetFileName(DaprStarterConstant.DEFAULT_FILE_NAME)); + } } diff --git a/src/Contrib/Development/Masa.Contrib.Development.DaprStarter/DaprProvider.cs b/src/Contrib/Development/Masa.Contrib.Development.DaprStarter/DaprProvider.cs index 581fbf0ed..41fd540e4 100644 --- a/src/Contrib/Development/Masa.Contrib.Development.DaprStarter/DaprProvider.cs +++ b/src/Contrib/Development/Masa.Contrib.Development.DaprStarter/DaprProvider.cs @@ -117,13 +117,13 @@ private static void DaprProcess_ErrorDataReceived(object? sender, DataReceivedEv public void DaprStop(string fileName, string appId) { var daprList = GetDaprList(fileName, appId, out _); - var pid = 0; - foreach (var dapr in daprList) + + var pidList = daprList.Select(dapr => dapr.PId).ToList(); + foreach (var pid in pidList) { try { - pid = dapr.PId; - var process = Process.GetProcessById(dapr.PId); + var process = Process.GetProcessById(pid); process.Kill(); } catch (Exception ex) diff --git a/src/Contrib/Development/Masa.Contrib.Development.DaprStarter/Internal/ChildProcessTracker.cs b/src/Contrib/Development/Masa.Contrib.Development.DaprStarter/Internal/ChildProcessTracker.cs index 5dd21091a..ddd0c8d4c 100644 --- a/src/Contrib/Development/Masa.Contrib.Development.DaprStarter/Internal/ChildProcessTracker.cs +++ b/src/Contrib/Development/Masa.Contrib.Development.DaprStarter/Internal/ChildProcessTracker.cs @@ -21,23 +21,18 @@ public static class ChildProcessTracker /// public static void AddProcess(Process? process) { - if (Environment.OSVersion.Platform != PlatformID.Win32NT) - { - //Only supported on windows - return; - } - if (process == null) return; - if (s_jobHandle != IntPtr.Zero) + if (SJobHandle != IntPtr.Zero) { - bool success = AssignProcessToJobObject(s_jobHandle, process.Handle); + bool success = AssignProcessToJobObject(SJobHandle, process.Handle); if (!success && !process.HasExited) throw new System.ComponentModel.Win32Exception(); } } - +#pragma warning disable S3963 +#pragma warning disable S3877 static ChildProcessTracker() { // This feature requires Windows 8 or later. To support Windows 7 requires @@ -51,26 +46,29 @@ static ChildProcessTracker() // The job name is optional (and can be null) but it helps with diagnostics. // If it's not null, it has to be unique. Use SysInternals' Handle command-line // utility: handle -a ChildProcessTracker - string jobName = "ChildProcessTracker" + Process.GetCurrentProcess().Id; - s_jobHandle = CreateJobObject(IntPtr.Zero, jobName); - - var info = new JOBOBJECT_BASIC_LIMIT_INFORMATION(); + string jobName = "ChildProcessTracker" + Environment.ProcessId; + SJobHandle = CreateJobObject(IntPtr.Zero, jobName); - // This is the key flag. When our process is killed, Windows will automatically - // close the job handle, and when that happens, we want the child processes to - // be killed, too. - info.LimitFlags = JOBOBJECTLIMIT.JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE; + var info = new JobObjectBasicLimitInformation + { + // be killed, too. + // close the job handle, and when that happens, we want the child processes to + // This is the key flag. When our process is killed, Windows will automatically + LimitFlags = JobObjectLimits.JobObjectLimitKillOnJobClose + }; - var extendedInfo = new JOBOBJECT_EXTENDED_LIMIT_INFORMATION(); - extendedInfo.BasicLimitInformation = info; + var extendedInfo = new JobObjectExtendedLimitInformation + { + BasicLimitInformation = info + }; - int length = Marshal.SizeOf(typeof(JOBOBJECT_EXTENDED_LIMIT_INFORMATION)); + int length = Marshal.SizeOf(typeof(JobObjectExtendedLimitInformation)); IntPtr extendedInfoPtr = Marshal.AllocHGlobal(length); try { Marshal.StructureToPtr(extendedInfo, extendedInfoPtr, false); - if (!SetInformationJobObject(s_jobHandle, JobObjectInfoType.ExtendedLimitInformation, + if (!SetInformationJobObject(SJobHandle, JobObjectInfoType.ExtendedLimitInformation, extendedInfoPtr, (uint)length)) { throw new System.ComponentModel.Win32Exception(); @@ -81,13 +79,14 @@ static ChildProcessTracker() Marshal.FreeHGlobal(extendedInfoPtr); } } +#pragma warning restore S3877 +#pragma warning restore S3963 [DllImport("kernel32.dll", CharSet = CharSet.Unicode)] static extern IntPtr CreateJobObject(IntPtr lpJobAttributes, string name); [DllImport("kernel32.dll")] - static extern bool SetInformationJobObject(IntPtr job, JobObjectInfoType infoType, - IntPtr lpJobObjectInfo, uint cbJobObjectInfoLength); + static extern bool SetInformationJobObject(IntPtr job, JobObjectInfoType infoType, IntPtr lpJobObjectInfo, uint cbJobObjectInfoLength); [DllImport("kernel32.dll", SetLastError = true)] static extern bool AssignProcessToJobObject(IntPtr job, IntPtr process); @@ -95,14 +94,14 @@ static extern bool SetInformationJobObject(IntPtr job, JobObjectInfoType infoTyp // Windows will automatically close any open job handles when our process terminates. // This can be verified by using SysInternals' Handle utility. When the job handle // is closed, the child processes will be killed. - private static readonly IntPtr s_jobHandle; + private static readonly IntPtr SJobHandle; } public enum JobObjectInfoType { AssociateCompletionPortInformation = 7, BasicLimitInformation = 2, - BasicUIRestrictions = 4, + BasicUiRestrictions = 4, EndOfJobTimeInformation = 6, ExtendedLimitInformation = 9, SecurityLimitInformation = 5, @@ -110,11 +109,11 @@ public enum JobObjectInfoType } [StructLayout(LayoutKind.Sequential)] -public struct JOBOBJECT_BASIC_LIMIT_INFORMATION +public struct JobObjectBasicLimitInformation { public Int64 PerProcessUserTimeLimit; public Int64 PerJobUserTimeLimit; - public JOBOBJECTLIMIT LimitFlags; + public JobObjectLimits LimitFlags; public UIntPtr MinimumWorkingSetSize; public UIntPtr MaximumWorkingSetSize; public UInt32 ActiveProcessLimit; @@ -124,13 +123,13 @@ public struct JOBOBJECT_BASIC_LIMIT_INFORMATION } [Flags] -public enum JOBOBJECTLIMIT : uint +public enum JobObjectLimits : uint { - JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE = 0x2000 + JobObjectLimitKillOnJobClose = 0x2000 } [StructLayout(LayoutKind.Sequential)] -public struct IO_COUNTERS +public struct IoCounters { public UInt64 ReadOperationCount; public UInt64 WriteOperationCount; @@ -141,10 +140,10 @@ public struct IO_COUNTERS } [StructLayout(LayoutKind.Sequential)] -public struct JOBOBJECT_EXTENDED_LIMIT_INFORMATION +public struct JobObjectExtendedLimitInformation { - public JOBOBJECT_BASIC_LIMIT_INFORMATION BasicLimitInformation; - public IO_COUNTERS IoInfo; + public JobObjectBasicLimitInformation BasicLimitInformation; + public IoCounters IoInfo; public UIntPtr ProcessMemoryLimit; public UIntPtr JobMemoryLimit; public UIntPtr PeakProcessMemoryUsed; diff --git a/src/Contrib/Development/Masa.Contrib.Development.DaprStarter/Internal/Options/SidecarOptions.cs b/src/Contrib/Development/Masa.Contrib.Development.DaprStarter/Internal/Options/SidecarOptions.cs index fa9f66f3f..12cbec83c 100644 --- a/src/Contrib/Development/Masa.Contrib.Development.DaprStarter/Internal/Options/SidecarOptions.cs +++ b/src/Contrib/Development/Masa.Contrib.Development.DaprStarter/Internal/Options/SidecarOptions.cs @@ -5,6 +5,7 @@ namespace Masa.Contrib.Development.DaprStarter; +#pragma warning disable S3236 [ExcludeFromCodeCoverage] internal class SidecarOptions : DaprOptionsBase { @@ -184,3 +185,4 @@ public override bool Equals(object? obj) }; } } +#pragma warning restore S3236 diff --git a/src/Contrib/Development/Tests/Masa.Contrib.Development.DaprStarter.AspNetCore.Tests/DefaultAppPortProviderTest.cs b/src/Contrib/Development/Tests/Masa.Contrib.Development.DaprStarter.AspNetCore.Tests/DefaultAppPortProviderTest.cs index 96e829688..b5bfc6aaf 100644 --- a/src/Contrib/Development/Tests/Masa.Contrib.Development.DaprStarter.AspNetCore.Tests/DefaultAppPortProviderTest.cs +++ b/src/Contrib/Development/Tests/Masa.Contrib.Development.DaprStarter.AspNetCore.Tests/DefaultAppPortProviderTest.cs @@ -18,7 +18,7 @@ public void TestGetAppPort() } [DataTestMethod] - [DataRow(null, false, 5000)] + [DataRow(null, true, 5001)] [DataRow(false, false, 5000)] [DataRow(true, true, 5001)] public void TestGetAppPort2(bool? enableSsl, bool expectedEnableSsl, int expectedAppPort) @@ -34,6 +34,29 @@ public void TestGetAppPort2(bool? enableSsl, bool expectedEnableSsl, int expecte Assert.AreEqual(expectedEnableSsl, result.EnableSsl); } + [DataTestMethod] + [DataRow(5000, false)] + [DataRow(5001, true)] + [DataRow(6000, null)] + public void TestGetAppPort2(ushort appPort, bool? expectedEnableSsl) + { + var service = new Mock(); + IServerAddressesFeature serverAddressesFeature = new ServerAddressesFeature(); + serverAddressesFeature.Addresses.Add("https://localhost:5001"); + serverAddressesFeature.Addresses.Add("http://localhost:5000"); + service.Setup(s => s.Features.Get()).Returns(() => serverAddressesFeature).Verifiable(); + var provider = new DefaultAppPortProvider(service.Object); + var enableSsl = provider.GetEnableSsl(appPort); + if (expectedEnableSsl != null) + { + Assert.AreEqual(expectedEnableSsl, enableSsl); + } + else + { + Assert.ThrowsException(() => Assert.AreEqual(expectedEnableSsl, enableSsl)); + } + } + [DataTestMethod] [DataRow(null, true, 5001)] [DataRow(true, true, 5001)] From 6554a8efa4d5adb31e913b4e46ec8193db07c6f8 Mon Sep 17 00:00:00 2001 From: zhenlei520 Date: Sat, 6 May 2023 18:34:16 +0800 Subject: [PATCH 5/5] chore: Dealing with Code Smells --- .../Options/DaprOptions.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/BuildingBlocks/Development/Masa.BuildingBlocks.Development.DaprStarter/Options/DaprOptions.cs b/src/BuildingBlocks/Development/Masa.BuildingBlocks.Development.DaprStarter/Options/DaprOptions.cs index 56a6262a4..0646e883d 100644 --- a/src/BuildingBlocks/Development/Masa.BuildingBlocks.Development.DaprStarter/Options/DaprOptions.cs +++ b/src/BuildingBlocks/Development/Masa.BuildingBlocks.Development.DaprStarter/Options/DaprOptions.cs @@ -5,6 +5,7 @@ namespace Masa.BuildingBlocks.Development.DaprStarter; +#pragma warning disable S3236 /// /// dapr startup configuration information /// When the specified attribute is configured as null, the default value of the parameter is subject to the default value of dapr of the current version @@ -239,3 +240,4 @@ public bool IsIncompleteAppId() return !DisableAppIdSuffix && (AppIdSuffix == null || AppIdSuffix.Trim() != string.Empty); } } +#pragma warning restore S3236