Skip to content

Commit

Permalink
Add support for optional limitation set in configuration remote serve…
Browse files Browse the repository at this point in the history
…r and processors (#4349)

Added support for optional limitation set in configuration remote server
and processors. When running in limitation mode, incoming units would
need to match units in limitation set. And units can only be asserted or
applied once.

Added tests. And some hacky manual verification. E2E tests will come
along when calling side is updated in separate pr.

Also updated .net target OS version to 22000 to match other parts. (I
thought there was a bug in IInputStreamAdaptor for MemoryStream)
  • Loading branch information
yao-msft authored Apr 5, 2024
1 parent 72ea504 commit 9505489
Show file tree
Hide file tree
Showing 29 changed files with 928 additions and 88 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -96,8 +96,8 @@ The client is built around the concept of sources; a set of packages effectively
* .NET Desktop Development
* Desktop Development with C++
* Universal Windows Platform Development
* [Windows 10 SDK, version 2004 (10.0.19041.0)](https://developer.microsoft.com/en-us/windows/downloads/sdk-archive/)
> **Note**: You can also get it through `winget install Microsoft.WindowsSDK --version 10.0.19041.685` (use --force if you have a newer version installed) or via Visual Studio > Get Tools and Features > Individual Components > Windows 10 SDK (10.0.19041.0)
* [Windows SDK for Windows 11 (10.0.22000.194)](https://developer.microsoft.com/en-us/windows/downloads/sdk-archive/)
> **Note**: You can also get it through `winget install Microsoft.WindowsSDK --version 10.0.22000.832` (use --force if you have a newer version installed) or via Visual Studio > Get Tools and Features > Individual Components > Windows 10 SDK (10.0.22000.0)
* The following extensions:
* [Microsoft Visual Studio Installer Projects](https://marketplace.visualstudio.com/items?itemName=VisualStudioClient.MicrosoftVisualStudio2022InstallerProjects)

Expand Down
2 changes: 1 addition & 1 deletion azure-pipelines.yml
Original file line number Diff line number Diff line change
Expand Up @@ -413,7 +413,7 @@ jobs:
inputs:
SourceFolder: $(buildOutDirAnyCpu)
Contents: |
Microsoft.Management.Configuration.Projection\net6.0-windows10.0.19041.0\Microsoft.Management.Configuration.Projection.dll
Microsoft.Management.Configuration.Projection\net6.0-windows10.0.22000.0\Microsoft.Management.Configuration.Projection.dll
TargetFolder: $(buildOutDirAnyCpu)\PowerShell\Microsoft.WinGet.Configuration\SharedDependencies\$(BuildPlatform)
flattenFolders: true
condition: succeededOrFailed()
Expand Down
6 changes: 3 additions & 3 deletions src/AppInstallerCLICore/AppInstallerCLICore.vcxproj
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(SolutionDir)\packages\Microsoft.Windows.CppWinRT.2.0.230706.1\build\native\Microsoft.Windows.CppWinRT.props" Condition="Exists('$(SolutionDir)\packages\Microsoft.Windows.CppWinRT.2.0.230706.1\build\native\Microsoft.Windows.CppWinRT.props')" />
<PropertyGroup Label="Globals">
Expand Down Expand Up @@ -537,7 +537,7 @@
<Error Condition="!Exists('$(SolutionDir)\packages\Microsoft.Windows.CppWinRT.2.0.230706.1\build\native\Microsoft.Windows.CppWinRT.targets')" Text="$([System.String]::Format('$(ErrorTextNuget)', '$(SolutionDir)\packages\Microsoft.Windows.CppWinRT.2.0.230706.1\build\native\Microsoft.Windows.CppWinRT.targets'))" />
<PropertyGroup>
<MicrosoftManagementConfigurationProcessorPath>$(OutputPath)..\Microsoft.Management.Configuration.Processor\Microsoft.Management.Configuration.Processor.winmd</MicrosoftManagementConfigurationProcessorPath>
<MicrosoftManagementConfigurationProcessorPath Condition="!Exists('$(MicrosoftManagementConfigurationProcessorPath)')">$(SolutionDir)\AnyCPU\$(Configuration)\Microsoft.Management.Configuration.Processor\net6.0-windows10.0.19041.0\win\Microsoft.Management.Configuration.Processor.winmd</MicrosoftManagementConfigurationProcessorPath>
<MicrosoftManagementConfigurationProcessorPath Condition="!Exists('$(MicrosoftManagementConfigurationProcessorPath)')">$(SolutionDir)\AnyCPU\$(Configuration)\Microsoft.Management.Configuration.Processor\net6.0-windows10.0.22000.0\win\Microsoft.Management.Configuration.Processor.winmd</MicrosoftManagementConfigurationProcessorPath>
</PropertyGroup>
<Message Importance="high" Text="Microsoft.Management.Configuration.Processor.winmd -&gt; $(MicrosoftManagementConfigurationProcessorPath)" />
<Error Condition="!Exists('$(MicrosoftManagementConfigurationProcessorPath)')" Text="Microsoft.Management.Configuration.Processor.winmd was not found in $(MicrosoftManagementConfigurationProcessorPath)" />
Expand All @@ -548,4 +548,4 @@
</Reference>
</ItemGroup>
</Target>
</Project>
</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,6 @@ namespace AppInstaller::CLI::ConfigurationRemoting

namespace
{
// The name of the directory containing additional modules.
constexpr std::wstring_view s_ExternalModulesName = L"ExternalModules";

// The executable file name for the remote server process.
constexpr std::wstring_view s_RemoteServerFileName = L"ConfigurationRemotingServer\\ConfigurationRemotingServer.exe";

Expand Down Expand Up @@ -146,17 +143,6 @@ namespace AppInstaller::CLI::ConfigurationRemoting
AICLI_LOG(Config, Verbose, << "... configuration processing connection established.");
m_remoteFactory = IConfigurationSetProcessorFactory{ output.detach(), winrt::take_ownership_from_abi };

// The additional modules path is a direct child directory to the package root
std::filesystem::path externalModules = Runtime::GetPathTo(Runtime::PathName::SelfPackageRoot) / s_ExternalModulesName;
THROW_HR_IF(HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND), !std::filesystem::is_directory(externalModules));
m_internalAdditionalModulePaths.emplace_back(externalModules.wstring());
m_remoteAdditionalModulePaths = winrt::single_threaded_vector<winrt::hstring>(std::vector<winrt::hstring>{ m_internalAdditionalModulePaths });

auto properties = m_remoteFactory.as<Processor::IPowerShellConfigurationProcessorFactoryProperties>();
AICLI_LOG(Config, Verbose, << "Applying built in additional module path: " << externalModules.u8string());
properties.AdditionalModulePaths(m_remoteAdditionalModulePaths.GetView());
properties.ProcessorType(Processor::PowerShellConfigurationProcessorType::Hosted);

completeEventIfFailureDuringConstruction.release();
}

Expand Down Expand Up @@ -201,11 +187,8 @@ namespace AppInstaller::CLI::ConfigurationRemoting
std::vector<winrt::hstring> newModulePaths{ value.Size() };
value.GetMany(0, newModulePaths);

// Combine with our own values
// Create a copy for remote and set remote module paths
std::vector<winrt::hstring> newRemotePaths{ newModulePaths };
newRemotePaths.insert(newRemotePaths.end(), m_internalAdditionalModulePaths.begin(), m_internalAdditionalModulePaths.end());

// Apply the new combined paths and pass to remote factory
m_remoteAdditionalModulePaths = winrt::single_threaded_vector<winrt::hstring>(std::move(newRemotePaths));
m_remoteFactory.as<Processor::IPowerShellConfigurationProcessorFactoryProperties>().AdditionalModulePaths(m_remoteAdditionalModulePaths.GetView());

Expand Down Expand Up @@ -276,7 +259,6 @@ namespace AppInstaller::CLI::ConfigurationRemoting
IConfigurationSetProcessorFactory m_remoteFactory;
wil::unique_event m_completionEvent;
Collections::IVector<winrt::hstring> m_additionalModulePaths{ winrt::single_threaded_vector<winrt::hstring>() };
std::vector<winrt::hstring> m_internalAdditionalModulePaths;
Collections::IVector<winrt::hstring> m_remoteAdditionalModulePaths{ winrt::single_threaded_vector<winrt::hstring>() };
};
}
Expand Down
4 changes: 2 additions & 2 deletions src/AppInstallerCLIPackage/AppInstallerCLIPackage.wapproj
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup Condition="'$(VisualStudioVersion)' == '' or '$(VisualStudioVersion)' &lt; '15.0'">
<VisualStudioVersion>15.0</VisualStudioVersion>
Expand Down Expand Up @@ -251,7 +251,7 @@
<WinGetAdditionalPackageFile Include="$(WinGetAdditonalPackageFileRoot)\AnyCPU\$(Configuration)\Microsoft.Management.Configuration.Projection\**\Microsoft.Management.Configuration.Projection.dll">
<PackagePath>ConfigurationRemotingServer\Microsoft.Management.Configuration.Projection.dll</PackagePath>
</WinGetAdditionalPackageFile>
<WinGetAdditionalPackageFile Include="$(WinGetAdditonalPackageFileRoot)\$(PlatformTarget)\$(Configuration)\ConfigurationRemotingServer\net6.0-windows10.0.19041.0\$(ConfigServerRid)\**\*">
<WinGetAdditionalPackageFile Include="$(WinGetAdditonalPackageFileRoot)\$(PlatformTarget)\$(Configuration)\ConfigurationRemotingServer\net6.0-windows10.0.22000.0\$(ConfigServerRid)\**\*">
<PackagePath>ConfigurationRemotingServer</PackagePath>
<Recurse>true</Recurse>
</WinGetAdditionalPackageFile>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0-windows10.0.19041.0</TargetFramework>
<TargetFramework>net6.0-windows10.0.22000.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<SupportedOSPlatformVersion>10.0.17763.0</SupportedOSPlatformVersion>
Expand Down
109 changes: 107 additions & 2 deletions src/ConfigurationRemotingServer/Program.cs
Original file line number Diff line number Diff line change
@@ -1,14 +1,23 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using Microsoft.Management.Configuration.Processor;
using System.IO;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Text;
using System.Text.Json;
using System.Text.Json.Serialization;
using Microsoft.Management.Configuration;
using Microsoft.Management.Configuration.Processor;
using Windows.Storage.Streams;
using WinRT;

namespace ConfigurationRemotingServer
{
internal class Program
{
private const string CommandLineSectionSeparator = "~~~~~~";
private const string ExternalModulesName = "ExternalModules";

static int Main(string[] args)
{
ulong memoryHandle = ulong.Parse(args[0]);
Expand All @@ -21,6 +30,74 @@ static int Main(string[] args)

PowerShellConfigurationSetProcessorFactory factory = new PowerShellConfigurationSetProcessorFactory();

// Set default properties.
var externalModulesPath = GetExternalModulesPath();
if (string.IsNullOrWhiteSpace(externalModulesPath))
{
throw new DirectoryNotFoundException("Failed to get ExternalModules.");
}

// Set as implicit module paths so it will be always included in AdditionalModulePaths
factory.ImplicitModulePaths = new List<string>() { externalModulesPath };
factory.ProcessorType = PowerShellConfigurationProcessorType.Hosted;

// Parse limitation set if applicable.
// The format will be:
// <Common args for initialization> ~~~~~~ <Metadata json> ~~~~~~ <Limitation Set in yaml>
// Metadata json format:
// {
// "path": "C:\full\file\path.yaml"
// }
// If a limitation set is provided, the processor will be limited
// to only work on units defined inside the limitation set.
var commandPtr = GetCommandLineW();
var commandStr = Marshal.PtrToStringUni(commandPtr) ?? string.Empty;

// In case the limitation set content contains the separator, we'll not use Split method.
var firstSeparatorIndex = commandStr.IndexOf(CommandLineSectionSeparator);
if (firstSeparatorIndex > 0)
{
var secondSeparatorIndex = commandStr.IndexOf(CommandLineSectionSeparator, firstSeparatorIndex + CommandLineSectionSeparator.Length);
if (secondSeparatorIndex <= 0)
{
throw new ArgumentException("The input command contains only one separator string.");
}

// Parse limitation set.
byte[] limitationSetBytes = Encoding.UTF8.GetBytes(commandStr.Substring(secondSeparatorIndex + CommandLineSectionSeparator.Length));
InMemoryRandomAccessStream limitationSetStream = new InMemoryRandomAccessStream();
DataWriter streamWriter = new DataWriter(limitationSetStream);
streamWriter.WriteBytes(limitationSetBytes);
streamWriter.StoreAsync().GetAwaiter().GetResult();
streamWriter.DetachStream();
limitationSetStream.Seek(0);
ConfigurationProcessor processor = new ConfigurationProcessor(factory);
var limitationSetResult = processor.OpenConfigurationSet(limitationSetStream);
if (limitationSetResult.ResultCode != null)
{
throw limitationSetResult.ResultCode;
}

var limitationSet = limitationSetResult.Set;
if (limitationSet == null)
{
throw new ArgumentException("The limitation set cannot be parsed.");
}

// Now parse metadata json and update the limitation set
var metadataJson = JsonSerializer.Deserialize<LimitationSetMetadata>(commandStr.Substring(
firstSeparatorIndex + CommandLineSectionSeparator.Length,
secondSeparatorIndex - firstSeparatorIndex - CommandLineSectionSeparator.Length));

if (metadataJson != null)
{
limitationSet.Path = metadataJson.Path;
}

// Set the limitation set in factory.
factory.LimitationSet = limitationSet;
}

IObjectReference factoryInterface = MarshalInterface<global::Microsoft.Management.Configuration.IConfigurationSetProcessorFactory>.CreateMarshaler(factory);

return WindowsPackageManagerConfigurationCompleteOutOfProcessFactoryInitialization(0, factoryInterface.ThisPtr, memoryHandle, initEventHandle, completionEventHandle, parentProcessHandle);
Expand All @@ -32,7 +109,35 @@ static int Main(string[] args)
}
}

private class LimitationSetMetadata
{
[JsonPropertyName("path")]
public string Path { get; set; } = string.Empty;
}

private static string GetExternalModulesPath()
{
var currentAssemblyDirectoryPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
if (currentAssemblyDirectoryPath != null)
{
var packageRootPath = Directory.GetParent(currentAssemblyDirectoryPath)?.FullName;
if (packageRootPath != null)
{
var externalModulesPath = Path.Combine(packageRootPath, ExternalModulesName);
if (Directory.Exists(externalModulesPath))
{
return externalModulesPath;
}
}
}

return string.Empty;
}

[DllImport("WindowsPackageManager.dll")]
private static extern int WindowsPackageManagerConfigurationCompleteOutOfProcessFactoryInitialization(int result, IntPtr factory, ulong memoryHandle, ulong initEventHandle, ulong completionMutexHandle, ulong parentProcessHandle);

[DllImport("kernel32.dll", CharSet = CharSet.Unicode)]
private static extern IntPtr GetCommandLineW();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,13 @@ param(

# Copy the winmd into the unit test directory since it will be needed for marshalling
$Local:winmdSourcePath = Join-Path $BuildOutputPath "Microsoft.Management.Configuration\Microsoft.Management.Configuration.winmd"
$Local:winmdTargetPath = Join-Path $BuildOutputPath "Microsoft.Management.Configuration.UnitTests\net6.0-windows10.0.19041.0\Microsoft.Management.Configuration.winmd"
$Local:winmdTargetPath = Join-Path $BuildOutputPath "Microsoft.Management.Configuration.UnitTests\net6.0-windows10.0.22000.0\Microsoft.Management.Configuration.winmd"

Copy-Item $Local:winmdSourcePath $Local:winmdTargetPath -Force

# Copy the OOP helper dll into the unit test directory to make activation look the same as in-proc
$Local:dllSourcePath = Join-Path $BuildOutputPath "Microsoft.Management.Configuration.OutOfProc\Microsoft.Management.Configuration.OutOfProc.dll"
$Local:dllTargetPath = Join-Path $BuildOutputPath "Microsoft.Management.Configuration.UnitTests\net6.0-windows10.0.19041.0\Microsoft.Management.Configuration.dll"
$Local:dllTargetPath = Join-Path $BuildOutputPath "Microsoft.Management.Configuration.UnitTests\net6.0-windows10.0.22000.0\Microsoft.Management.Configuration.dll"

Copy-Item $Local:dllSourcePath $Local:dllTargetPath -Force

Expand Down
Loading

0 comments on commit 9505489

Please sign in to comment.