Skip to content

Commit

Permalink
Add diagnostics to the processor component (#3087)
Browse files Browse the repository at this point in the history
This change adds the same diagnostics event to the `IConfigurationSetProcessorFactory` as the `ConfigurationProcessor` already has.  It also adds a property to control the minimum level of events, enabling filtering to be done at event publishing rather than consumption.

All of the public facing methods in the processor component are enlightened to send any escaping exceptions to the diagnostics before letting them generate an error.  Additionally, verbose logs are added throughout to enable a better understanding of what/when things are happening.
  • Loading branch information
JohnMcPMS authored Mar 21, 2023
1 parent bfcee8e commit 1898da0
Show file tree
Hide file tree
Showing 24 changed files with 418 additions and 120 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#include "pch.h"
#include "ConfigurationSetProcessorFactoryRemoting.h"

using namespace winrt::Windows::Foundation;
using namespace winrt::Microsoft::Management::Configuration;

namespace AppInstaller::CLI::Workflow::ConfigurationRemoting
Expand Down Expand Up @@ -38,6 +39,8 @@ namespace AppInstaller::CLI::Workflow::ConfigurationRemoting
{
RemoteFactory()
{
AICLI_LOG(Config, Verbose, << "Launching process for configuration processing...");

// Security attributes to set handles as inherited.
SECURITY_ATTRIBUTES securityAttributes{};
securityAttributes.nLength = sizeof(securityAttributes);
Expand Down Expand Up @@ -75,6 +78,7 @@ namespace AppInstaller::CLI::Workflow::ConfigurationRemoting
wil::unique_process_information processInformation;

THROW_IF_WIN32_BOOL_FALSE(CreateProcessW(serverPath.c_str(), &arguments[0], nullptr, nullptr, TRUE, DETACHED_PROCESS, nullptr, nullptr, &startupInfo, &processInformation));
AICLI_LOG(Config, Verbose, << " Configuration remote PID is " << processInformation.dwProcessId);

HANDLE waitHandles[2];
waitHandles[0] = initEvent.get();
Expand Down Expand Up @@ -123,8 +127,9 @@ namespace AppInstaller::CLI::Workflow::ConfigurationRemoting
THROW_IF_FAILED(stream->Write(mappedMemory->FactoryObject, mappedMemory->FactorySize, nullptr));
THROW_IF_FAILED(stream->Seek({}, STREAM_SEEK_SET, nullptr));

wil::com_ptr<IUnknown> output;
wil::com_ptr<::IUnknown> output;
THROW_IF_FAILED(CoUnmarshalInterface(stream.get(), winrt::guid_of<IConfigurationSetProcessorFactory>(), reinterpret_cast<void**>(&output)));
AICLI_LOG(Config, Verbose, << "... configuration processing connection established.");
m_remoteFactory = IConfigurationSetProcessorFactory{ output.detach(), winrt::take_ownership_from_abi };
}

Expand All @@ -133,6 +138,26 @@ namespace AppInstaller::CLI::Workflow::ConfigurationRemoting
return m_remoteFactory.CreateSetProcessor(configurationSet);
}

winrt::event_token Diagnostics(const EventHandler<DiagnosticInformation>& handler)
{
return m_remoteFactory.Diagnostics(handler);
}

void Diagnostics(const winrt::event_token& token) noexcept
{
m_remoteFactory.Diagnostics(token);
}

DiagnosticLevel MinimumLevel()
{
return m_remoteFactory.MinimumLevel();
}

void MinimumLevel(DiagnosticLevel value)
{
m_remoteFactory.MinimumLevel(value);
}

private:
IConfigurationSetProcessorFactory m_remoteFactory;
wil::unique_mutex m_completionMutex;
Expand Down Expand Up @@ -163,7 +188,7 @@ HRESULT WindowsPackageManagerConfigurationCompleteOutOfProcessFactoryInitializat
wil::com_ptr<IStream> stream;
RETURN_IF_FAILED(CreateStreamOnHGlobal(nullptr, TRUE, &stream));

RETURN_IF_FAILED(CoMarshalInterface(stream.get(), winrt::guid_of<IConfigurationSetProcessorFactory>(), reinterpret_cast<IUnknown*>(factory), MSHCTX_LOCAL, nullptr, MSHLFLAGS_NORMAL));
RETURN_IF_FAILED(CoMarshalInterface(stream.get(), winrt::guid_of<IConfigurationSetProcessorFactory>(), reinterpret_cast<::IUnknown*>(factory), MSHCTX_LOCAL, nullptr, MSHLFLAGS_NORMAL));

ULARGE_INTEGER streamSize{};
RETURN_IF_FAILED(stream->Seek({}, STREAM_SEEK_CUR, &streamSize));
Expand Down
30 changes: 28 additions & 2 deletions src/AppInstallerCLICore/Workflows/ConfigurationFlow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,20 @@ namespace AppInstaller::CLI::Workflow
return Logging::Level::Info;
}

DiagnosticLevel ConvertLevel(Logging::Level level)
{
switch (level)
{
case Logging::Level::Verbose: return DiagnosticLevel::Verbose;
case Logging::Level::Info: return DiagnosticLevel::Informational;
case Logging::Level::Warning: return DiagnosticLevel::Warning;
case Logging::Level::Error: return DiagnosticLevel::Error;
case Logging::Level::Crit: return DiagnosticLevel::Critical;
}

return DiagnosticLevel::Informational;
}

Resource::StringId ToResource(ConfigurationUnitIntent intent)
{
switch (intent)
Expand Down Expand Up @@ -102,7 +116,7 @@ namespace AppInstaller::CLI::Workflow
break;
default:
// TODO: Sort out how we actually want to handle this given that we don't expect anything but strings
out << " [PropertyType="_liv << property.Type() << "]\n"_liv;
out << " [Debug:PropertyType="_liv << property.Type() << "]\n"_liv;
break;
}
}
Expand Down Expand Up @@ -337,6 +351,7 @@ namespace AppInstaller::CLI::Workflow
{
AICLI_LOG(Config, Error, << "Configuration unit " << Utility::ConvertToUTF8(unit.UnitName()) << "[" << Utility::ConvertToUTF8(unit.Identifier()) << "] failed with code 0x"
<< Logging::SetHRFormat << resultInformation.ResultCode() << " and error message:\n" << Utility::ConvertToUTF8(resultInformation.Description()));
// TODO: Improve error reporting for failures: use message, known HRs, getting HR system string, etc.
m_context.Reporter.Error() << " "_liv << Resource::String::ConfigurationUnitFailed << " 0x"_liv << Logging::SetHRFormat << resultInformation.ResultCode() << std::endl;
}
OutputUnitCompletionProgress();
Expand Down Expand Up @@ -383,6 +398,9 @@ namespace AppInstaller::CLI::Workflow
{
ConfigurationProcessor processor{ CreateConfigurationSetProcessorFactory() };

// Set the processor to the current level of the logging.
processor.MinimumLevel(ConvertLevel(Logging::Log().GetLevel()));

// Route the configuration diagnostics into the context's diagnostics logging
processor.Diagnostics([&context](const winrt::Windows::Foundation::IInspectable&, const DiagnosticInformation& diagnostics)
{
Expand Down Expand Up @@ -429,7 +447,15 @@ namespace AppInstaller::CLI::Workflow
AICLI_TERMINATE_CONTEXT(openResult.ResultCode());
}

context.Get<Data::ConfigurationContext>().Set(openResult.Set());
ConfigurationSet result = openResult.Set();

// Fill out the information about the set based on it coming from a file.
// TODO: Consider how to properly determine a good value for name and origin.
result.Name(absolutePath.filename().wstring());
result.Origin(absolutePath.parent_path().wstring());
result.Path(absolutePath.wstring());

context.Get<Data::ConfigurationContext>().Set(result);
}

void ShowConfigurationSet(Context& context)
Expand Down
19 changes: 19 additions & 0 deletions src/AppInstallerCLITests/TestConfiguration.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,25 @@ namespace TestCommon
}
}

winrt::event_token TestConfigurationSetProcessorFactory::Diagnostics(const EventHandler<DiagnosticInformation>& handler)
{
return m_diagnostics.add(handler);
}

void TestConfigurationSetProcessorFactory::Diagnostics(const winrt::event_token& token) noexcept
{
m_diagnostics.remove(token);
}

DiagnosticLevel TestConfigurationSetProcessorFactory::MinimumLevel()
{
return DiagnosticLevel::Informational;
}

void TestConfigurationSetProcessorFactory::MinimumLevel(DiagnosticLevel)
{
}

IConfigurationUnitProcessorDetails TestConfigurationSetProcessor::GetUnitProcessorDetails(const ConfigurationUnit& unit, ConfigurationUnitDetailLevel detailLevel)
{
if (GetUnitProcessorDetailsFunc)
Expand Down
9 changes: 9 additions & 0 deletions src/AppInstallerCLITests/TestConfiguration.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,16 @@ namespace TestCommon
{
winrt::Microsoft::Management::Configuration::IConfigurationSetProcessor CreateSetProcessor(const winrt::Microsoft::Management::Configuration::ConfigurationSet& configurationSet);

winrt::event_token Diagnostics(const winrt::Windows::Foundation::EventHandler<winrt::Microsoft::Management::Configuration::DiagnosticInformation>& handler);
void Diagnostics(const winrt::event_token& token) noexcept;

winrt::Microsoft::Management::Configuration::DiagnosticLevel MinimumLevel();
void MinimumLevel(winrt::Microsoft::Management::Configuration::DiagnosticLevel value);

std::function<winrt::Microsoft::Management::Configuration::IConfigurationSetProcessor(const winrt::Microsoft::Management::Configuration::ConfigurationSet&)> CreateSetProcessorFunc;

private:
winrt::event<winrt::Windows::Foundation::EventHandler<winrt::Microsoft::Management::Configuration::DiagnosticInformation>> m_diagnostics;
};

struct TestConfigurationSetProcessor : winrt::implements<TestConfigurationSetProcessor, winrt::Microsoft::Management::Configuration::IConfigurationSetProcessor>
Expand Down
5 changes: 5 additions & 0 deletions src/AppInstallerSharedLib/AppInstallerLogging.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,11 @@ namespace AppInstaller::Logging
m_enabledLevel = level;
}

Level DiagnosticLogger::GetLevel() const
{
return m_enabledLevel;
}

bool DiagnosticLogger::IsEnabled(Channel channel, Level level) const
{
return (!m_loggers.empty() &&
Expand Down
3 changes: 3 additions & 0 deletions src/AppInstallerSharedLib/Public/AppInstallerLogging.h
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,9 @@ namespace AppInstaller::Logging
// For example; SetLevel(Verbose) will enable all logs.
void SetLevel(Level level);

// Gets the enabled level.
Level GetLevel() const;

// Checks whether a given channel and level are enabled.
bool IsEnabled(Channel channel, Level level) const;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
namespace Microsoft.Management.Configuration.Processor.Exceptions
{
using System;
using Microsoft.PowerShell.Commands;

/// <summary>
/// Resource not found by Find-DscResource.
Expand All @@ -16,16 +17,24 @@ internal class FindDscResourceNotFoundException : Exception
/// <summary>
/// Initializes a new instance of the <see cref="FindDscResourceNotFoundException"/> class.
/// </summary>
/// <param name="unitName">Unit name.</param>
public FindDscResourceNotFoundException(string unitName)
/// <param name="resourceName">Resource name.</param>
/// <param name="module">Optional module.</param>
public FindDscResourceNotFoundException(string resourceName, ModuleSpecification? module)
: base($"Could not find resource: {resourceName} [{module?.ToString() ?? "<no module>"}]")
{
this.HResult = ErrorCodes.WinGetConfigUnitNotFoundRepository;
this.UnitName = unitName;
this.ResourceName = resourceName;
this.Module = module;
}

/// <summary>
/// Gets the unit name.
/// Gets the resource name.
/// </summary>
public string UnitName { get; }
public string ResourceName { get; }

/// <summary>
/// Gets the module, if any.
/// </summary>
public ModuleSpecification? Module { get; }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ internal class GetDscResourceMultipleMatches : Exception
/// <param name="resourceName">Resource name.</param>
/// <param name="module">Optional module.</param>
public GetDscResourceMultipleMatches(string resourceName, ModuleSpecification? module)
: base($"Multiple matches found for resource: {resourceName} [{module?.ToString() ?? "<no module>"}]")
{
this.HResult = ErrorCodes.WinGetConfigUnitMultipleMatches;
this.ResourceName = resourceName;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ internal class InstallDscResourceException : Exception
/// <param name="resourceName">Resource name.</param>
/// <param name="module">Module.</param>
public InstallDscResourceException(string resourceName, ModuleSpecification? module)
: base($"Unable to find resource after install: {resourceName} [{module?.ToString() ?? "<no module>"}]")
{
this.HResult = ErrorCodes.WinGetConfigUnitNotFound;
this.ResourceName = resourceName;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ internal class InvokeDscResourceGetException : Exception
/// <param name="resourceName">Resource name.</param>
/// <param name="module">Optional module.</param>
public InvokeDscResourceGetException(string resourceName, ModuleSpecification? module)
: base($"Failed when calling `Get` for resource: {resourceName} [{module?.ToString() ?? "<no module>"}]")
{
this.HResult = ErrorCodes.WinGetConfigUnitInvokeGet;
this.ResourceName = resourceName;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ internal class InvokeDscResourceSetException : Exception
/// <param name="resourceName">Resource name.</param>
/// <param name="module">Optional module.</param>
public InvokeDscResourceSetException(string resourceName, ModuleSpecification? module)
: base($"Failed when calling `Set` for resource: {resourceName} [{module?.ToString() ?? "<no module>"}]")
{
this.HResult = ErrorCodes.WinGetConfigUnitInvokeSet;
this.ResourceName = resourceName;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ internal class InvokeDscResourceTestException : Exception
/// <param name="resourceName">Resource name.</param>
/// <param name="module">Optional module.</param>
public InvokeDscResourceTestException(string resourceName, ModuleSpecification? module)
: base($"Failed when calling `Test` for resource: {resourceName} [{module?.ToString() ?? "<no module>"}]")
{
this.HResult = ErrorCodes.WinGetConfigUnitInvokeTest;
this.ResourceName = resourceName;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ namespace Microsoft.Management.Configuration.Processor.Helpers
/// </summary>
internal class ConfigurationUnitAndResource
{
private readonly ConfigurationUnitInternal configurationUnitInternal;
private readonly DscResourceInfoInternal dscResourceInfoInternal;

/// <summary>
Expand All @@ -35,24 +34,29 @@ public ConfigurationUnitAndResource(
throw new ArgumentException();
}

this.configurationUnitInternal = configurationUnitInternal;
this.UnitInternal = configurationUnitInternal;
this.dscResourceInfoInternal = dscResourceInfoInternal;
}

/// <summary>
/// Gets or initializes the internal unit.
/// </summary>
public ConfigurationUnitInternal UnitInternal { get; private init; }

/// <summary>
/// Gets the configuration unit.
/// </summary>
public ConfigurationUnit Unit
{
get { return this.configurationUnitInternal.Unit; }
get { return this.UnitInternal.Unit; }
}

/// <summary>
/// Gets the directives overlay.
/// </summary>
public IReadOnlyDictionary<string, object>? DirectivesOverlay
{
get { return this.configurationUnitInternal.DirectivesOverlay; }
get { return this.UnitInternal.DirectivesOverlay; }
}

/// <summary>
Expand All @@ -69,7 +73,7 @@ public string ResourceName
/// </summary>
public ModuleSpecification? Module
{
get { return this.configurationUnitInternal.Module; }
get { return this.UnitInternal.Module; }
}

/// <summary>
Expand All @@ -79,7 +83,7 @@ public ModuleSpecification? Module
/// <returns>The value of the directive. Null if doesn't exist.</returns>
public string? GetDirective(string directiveName)
{
return this.configurationUnitInternal.GetDirective(directiveName);
return this.UnitInternal.GetDirective(directiveName);
}

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,15 @@ public ConfigurationUnitInternal(
/// </summary>
public ModuleSpecification? Module { get; }

/// <summary>
/// Creates a string that identifies this unit for diagnostics.
/// </summary>
/// <returns>The string that identifies this unit for diagnostics.</returns>
public string ToIdentifyingString()
{
return $"{this.Unit.UnitName} [{this.Module?.ToString() ?? "<no module>"}]";
}

/// <summary>
/// Get a directive from the unit taking into account the directives overlay.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ public void ValidateRunspace()
// Only support PowerShell Core.
if (this.GetVariable<string>(Variables.PSEdition) != Core)
{
throw new NotSupportedException();
throw new NotSupportedException("Only PowerShell Core is supported.");
}

var powerShellGet = PowerShellHelpers.CreateModuleSpecification(
Expand Down
Loading

0 comments on commit 1898da0

Please sign in to comment.