Fix Set-Service -Status Stopped to stop services with dependencies (#5525)

This commit is contained in:
zhenggu 2018-08-29 02:03:12 +08:00 committed by Aditya Patwardhan
parent 2b61159ca7
commit 358e8abefe
9 changed files with 140 additions and 14 deletions

View file

@ -1000,6 +1000,7 @@ function Publish-PSTestTools {
$tools = @(
@{Path="${PSScriptRoot}/test/tools/TestExe";Output="testexe"}
@{Path="${PSScriptRoot}/test/tools/WebListener";Output="WebListener"}
@{Path="${PSScriptRoot}/test/tools/TestService";Output="TestService"}
)
$Options = Get-PSOptions -DefaultToNew

View file

@ -1540,6 +1540,15 @@ namespace Microsoft.PowerShell.Commands
}
internal string serviceStatus = null;
/// <summary>
/// The following is the definition of the input parameter "Force".
/// This parameter is useful only when parameter "Stop" is enabled.
/// If "Force" is enabled, it will also stop the dependent services.
/// If not, it will send an error when this service has dependent ones.
/// </summary>
[Parameter]
public SwitchParameter Force { get; set; }
/// <summary>
/// This is not a parameter for this cmdlet.
/// </summary>
@ -1786,24 +1795,17 @@ namespace Microsoft.PowerShell.Commands
{
if (!service.Status.Equals(ServiceControllerStatus.Stopped))
{
//check for the dependent services as set-service dont have force parameter
// Check for the dependent services as set-service dont have force parameter
ServiceController[] dependentServices = service.DependentServices;
if ((dependentServices != null) && (dependentServices.Length > 0))
if ((!Force) && (dependentServices != null) && (dependentServices.Length > 0))
{
WriteNonTerminatingError(service, null, "ServiceHasDependentServicesNoForce", ServiceResources.ServiceHasDependentServicesNoForce, ErrorCategory.InvalidOperation);
return;
}
ServiceController[] servicedependedon = service.ServicesDependedOn;
if ((servicedependedon != null) && (servicedependedon.Length > 0))
{
WriteNonTerminatingError(service, null, "ServiceIsDependentOnNoForce", ServiceResources.ServiceIsDependentOnNoForce, ErrorCategory.InvalidOperation);
return;
}
// Stop service, pass 'true' to the force parameter as we have already checked for the dependent services.
DoStopService(service, force: true, waitForServiceToStop: true);
DoStopService(service, Force, waitForServiceToStop: true);
}
}
else if (Status.Equals("Paused", StringComparison.CurrentCultureIgnoreCase))

View file

@ -132,9 +132,6 @@
<data name="ServiceHasDependentServicesNoForce" xml:space="preserve">
<value>Cannot stop service '{1} ({0})' because it has dependent services.</value>
</data>
<data name="ServiceIsDependentOnNoForce" xml:space="preserve">
<value>Cannot stop service '{1} ({0})' because it is dependent on other services.</value>
</data>
<data name="CouldNotStopService" xml:space="preserve">
<value>Service '{1} ({0})' cannot be stopped due to the following error: {2}</value>
</data>

View file

@ -12,12 +12,28 @@ Describe "Set/New/Remove-Service cmdlet tests" -Tags "Feature", "RequireAdminOnW
net user $userName $testPass /add > $null
$password = ConvertTo-SecureString $testPass -AsPlainText -Force
$creds = [pscredential]::new(".\$userName", $password)
$testservicename1 = "testservice1"
$testservicename2 = "testservice2"
$svcbinaryname = "TestService"
$svccmd = Get-Command $svcbinaryname
$svccmd | Should -Not -BeNullOrEmpty
$svcfullpath = $svccmd.Path
$testservice1 = New-Service -BinaryPathName $svcfullpath -Name $testservicename1
$testservice1 | Should -Not -BeNullOrEmpty
$testservice2 = New-Service -BinaryPathName $svcfullpath -Name $testservicename2 -DependsOn $testservicename1
$testservice2 | Should -Not -BeNullOrEmpty
}
}
AfterAll {
$global:PSDefaultParameterValues = $originalDefaultParameterValues
if ($IsWindows) {
net user $userName /delete > $null
Stop-Service $testservicename2
Stop-Service $testservicename1
Remove-Service $testservicename2
Remove-Service $testservicename1
}
}
@ -329,4 +345,32 @@ Describe "Set/New/Remove-Service cmdlet tests" -Tags "Feature", "RequireAdminOnW
}
{ & $cmdlet @parameters } | Should -Throw -ErrorId $errorid
}
Context "Set-Service test cases on the services with dependent relationship" {
BeforeEach {
{ Set-Service -Status Running $testservicename2 } | Should -Not -Throw
(Get-Service $testservicename1).Status | Should -BeExactly "Running"
(Get-Service $testservicename2).Status | Should -BeExactly "Running"
}
It "Set-Service can stop a service with dependency" {
$script = { Set-Service -Status Stopped $testservicename2 -ErrorAction Stop }
{ & $script } | Should -Not -Throw
(Get-Service $testservicename2).Status | Should -BeExactly "Stopped"
}
It "Set-Service cannot stop a service with running dependent service" {
$script = { Set-Service -Status Stopped $testservicename1 -ErrorAction Stop }
{ & $script } | Should -Throw
(Get-Service $testservicename1).Status | Should -BeExactly "Running"
(Get-Service $testservicename2).Status | Should -BeExactly "Running"
}
It "Set-Service can stop a service with running dependent service by parameter -Force" {
$script = { Set-Service -Status Stopped -Force $testservicename1 -ErrorAction Stop }
{ & $script } | Should -Not -Throw
(Get-Service $testservicename1).Status | Should -BeExactly "Stopped"
(Get-Service $testservicename2).Status | Should -BeExactly "Stopped"
}
}
}

View file

@ -667,7 +667,8 @@ function Invoke-OpenCover
$updatedEnvPath = "${PowerShellExeDirectory}\Modules;$TestToolsModulesPath"
$testToolsExePath = (Resolve-Path(Join-Path $TestPath -ChildPath "..\tools\TestExe\bin")).Path
$updatedProcessEnvPath = "${testToolsExePath};${env:PATH}"
$testServiceExePath = (Resolve-Path(Join-Path $TestPath -ChildPath "..\tools\TestService\bin")).Path
$updatedProcessEnvPath = "${testServiceExePath};${testToolsExePath};${env:PATH}"
$startupArgs = "Set-ExecutionPolicy Bypass -Force -Scope Process; `$env:PSModulePath = '${updatedEnvPath}'; `$env:Path = '${updatedProcessEnvPath}';"
$targetArgs = "${startupArgs}", "Invoke-Pester","${TestPath}","-OutputFormat $PesterLogFormat"

View file

@ -0,0 +1,19 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
using System.ServiceProcess;
namespace TestService
{
static class Program
{
static void Main()
{
ServiceBase[] ServicesToRun;
ServicesToRun = new ServiceBase[]
{
new Service1()
};
ServiceBase.Run(ServicesToRun);
}
}
}

View file

@ -0,0 +1,24 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
namespace TestService
{
partial class Service1
{
private System.ComponentModel.IContainer components = null;
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
private void InitializeComponent()
{
components = new System.ComponentModel.Container();
this.ServiceName = "Service1";
}
}
}

View file

@ -0,0 +1,22 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
using System.ServiceProcess;
namespace TestService
{
public partial class Service1 : ServiceBase
{
public Service1()
{
InitializeComponent();
}
protected override void OnStart(string[] args)
{
}
protected override void OnStop()
{
}
}
}

View file

@ -0,0 +1,16 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\..\Test.Common.props"/>
<PropertyGroup>
<Description>Very tiny windows service to do service testing</Description>
<AssemblyName>TestService</AssemblyName>
<OutputType>Exe</OutputType>
<RuntimeIdentifiers>win7-x86;win7-x64</RuntimeIdentifiers>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Windows.Compatibility" Version="2.0.0" />
</ItemGroup>
</Project>