Skip to content

Commit

Permalink
Split out context interfaces for discovery (#2546)
Browse files Browse the repository at this point in the history
  • Loading branch information
BernieWhite authored Sep 19, 2024
1 parent 272b874 commit 4650fcc
Show file tree
Hide file tree
Showing 12 changed files with 391 additions and 71 deletions.
38 changes: 38 additions & 0 deletions src/PSRule/Definitions/IResourceDiscoveryContext.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

using PSRule.Pipeline;
using PSRule.Runtime;

namespace PSRule.Definitions;

#nullable enable

/// <summary>
/// A context that is used for discovery of resources.
/// </summary>
internal interface IResourceDiscoveryContext
{
/// <summary>
/// A writer to log messages.
/// </summary>
IPipelineWriter Writer { get; }

/// <summary>
/// Enter a language scope.
/// </summary>
/// <param name="file">The source file to enter.</param>
void EnterLanguageScope(ISourceFile file);

/// <summary>
/// Exit a language scope.
/// </summary>
/// <param name="file">The source file to exit.</param>
void ExitLanguageScope(ISourceFile file);

void PushScope(RunspaceScope scope);

void PopScope(RunspaceScope scope);
}

#nullable restore
17 changes: 17 additions & 0 deletions src/PSRule/Definitions/IScriptResourceDiscoveryContext.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

using System.Management.Automation;
using PSRule.Options;

namespace PSRule.Definitions;

/// <summary>
/// A context that is used for discovery of resources defined as script blocks.
/// </summary>
internal interface IScriptResourceDiscoveryContext : IResourceDiscoveryContext
{
PowerShell GetPowerShell();

ExecutionOption GetExecutionOption();
}
2 changes: 1 addition & 1 deletion src/PSRule/Definitions/InternalResource.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ namespace PSRule.Definitions;
private protected InternalResource(ResourceKind kind, string apiVersion, SourceFile source, ResourceMetadata metadata, IResourceHelpInfo info, ISourceExtent extent, TSpec spec)
: base(kind, apiVersion, source, metadata, info, extent, spec)
{
_Annotations = new Dictionary<Type, ResourceAnnotation>();
_Annotations = [];
Obsolete = ResourceHelper.IsObsolete(metadata);
Flags |= ResourceHelper.IsObsolete(metadata) ? ResourceFlags.Obsolete : ResourceFlags.None;
}
Expand Down
9 changes: 5 additions & 4 deletions src/PSRule/Definitions/Resource.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// Licensed under the MIT License.

using System.Diagnostics;
using System.Net.Http.Headers;
using PSRule.Pipeline;
using YamlDotNet.Serialization;

Expand All @@ -22,12 +23,12 @@ protected internal Resource(ResourceKind kind, string apiVersion, SourceFile sou
Kind = kind;
ApiVersion = apiVersion;
Info = info;
Source = source;
Source = source ?? throw new ArgumentNullException(nameof(source));
Extent = extent;
Spec = spec;
Metadata = metadata;
Spec = spec ?? throw new ArgumentNullException(nameof(spec));
Metadata = metadata ?? throw new ArgumentNullException(nameof(metadata));
Name = metadata.Name;
Id = new ResourceId(source.Module, Name, ResourceIdKind.Id);
Id = new ResourceId(Source.Module, Name, ResourceIdKind.Id);
}

/// <summary>
Expand Down
50 changes: 22 additions & 28 deletions src/PSRule/Host/HostHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -57,11 +57,13 @@ internal static DependencyGraph<RuleBlock> GetRuleBlockGraph(Source[] source, Ru
/// <summary>
/// Get meta resources which are resource defined in YAML or JSON.
/// </summary>
private static IEnumerable<ILanguageBlock> GetYamlJsonLanguageBlocks(Source[] source, RunspaceContext context)
internal static IEnumerable<T> GetMetaResources<T>(Source[] source, IResourceDiscoveryContext context) where T : ILanguageBlock
{
var results = new List<ILanguageBlock>();
results.AddRange(GetYamlLanguageBlocks(source, context));
results.AddRange(GetJsonLanguageBlocks(source, context));
if (source == null || source.Length == 0) return [];

var results = new List<T>();
results.AddRange(GetYamlLanguageBlocks(source, context).OfType<T>());
results.AddRange(GetJsonLanguageBlocks(source, context).OfType<T>());
return results;
}

Expand All @@ -70,39 +72,31 @@ private static IEnumerable<ILanguageBlock> GetYamlJsonLanguageBlocks(Source[] so
/// </summary>
internal static IEnumerable<Baseline> GetBaseline(Source[] source, RunspaceContext context)
{
return ToBaselineV1(GetYamlJsonLanguageBlocks(source, context), context);
return ToBaselineV1(GetMetaResources<ILanguageBlock>(source, context), context);
}

/// <summary>
/// Read YAML/JSON objects and return module configurations.
/// </summary>
internal static IEnumerable<ModuleConfigV1> GetModuleConfigForTests(Source[] source, RunspaceContext context)
{
return ToModuleConfigV1(GetYamlJsonLanguageBlocks(source, context), context);
return ToModuleConfigV1(GetMetaResources<ILanguageBlock>(source, context), context);
}

/// <summary>
/// Read YAML/JSON objects and return selectors.
/// </summary>
internal static IEnumerable<SelectorV1> GetSelectorForTests(Source[] source, RunspaceContext context)
{
return ToSelectorV1(GetYamlJsonLanguageBlocks(source, context), context);
return ToSelectorV1(GetMetaResources<ILanguageBlock>(source, context), context);
}

/// <summary>
/// Read YAML/JSON objects and return suppression groups.
/// </summary>
internal static IEnumerable<SuppressionGroupV1> GetSuppressionGroupForTests(Source[] source, RunspaceContext context)
{
return ToSuppressionGroupV1(GetYamlJsonLanguageBlocks(source, context), context);
}

/// <summary>
/// Import meta resources which are resource defined in YAML or JSON.
/// </summary>
internal static IEnumerable<ILanguageBlock> ImportResource(Source[] source, RunspaceContext context)
{
return source == null || source.Length == 0 ? Array.Empty<ILanguageBlock>() : GetYamlJsonLanguageBlocks(source, context);
return ToSuppressionGroupV1(GetMetaResources<ILanguageBlock>(source, context), context);
}

/// <summary>
Expand Down Expand Up @@ -167,16 +161,16 @@ private static ILanguageBlock[] GetLanguageBlock(RunspaceContext context, Source
{
var results = new List<ILanguageBlock>();
results.AddRange(GetPSLanguageBlocks(context, sources));
results.AddRange(GetYamlJsonLanguageBlocks(sources, context));
results.AddRange(GetMetaResources<ILanguageBlock>(sources, context));
return [.. results];
}

/// <summary>
/// Execute PowerShell script files to get language blocks.
/// </summary>
private static ILanguageBlock[] GetPSLanguageBlocks(RunspaceContext context, Source[] sources)
private static ILanguageBlock[] GetPSLanguageBlocks(IScriptResourceDiscoveryContext context, Source[] sources)
{
if (context.Pipeline.Option.Execution.RestrictScriptSource == Options.RestrictScriptSource.DisablePowerShell)
if (context.GetExecutionOption().RestrictScriptSource == Options.RestrictScriptSource.DisablePowerShell)
return [];

var results = new List<ILanguageBlock>();
Expand All @@ -196,7 +190,7 @@ private static ILanguageBlock[] GetPSLanguageBlocks(RunspaceContext context, Sou
continue;

ps.Commands.Clear();
context.VerboseRuleDiscovery(path: file.Path);
context.Writer?.VerboseRuleDiscovery(path: file.Path);
context.EnterLanguageScope(file);
try
{
Expand All @@ -207,14 +201,14 @@ private static ILanguageBlock[] GetPSLanguageBlocks(RunspaceContext context, Sou
if (visitor.Errors != null && visitor.Errors.Count > 0)
{
foreach (var record in visitor.Errors)
context.WriteError(record);
context.Writer?.WriteError(record);

continue;
}
if (errors != null && errors.Length > 0)
{
foreach (var error in errors)
context.WriteError(error);
context.Writer?.WriteError(error);

continue;
}
Expand Down Expand Up @@ -242,7 +236,7 @@ private static ILanguageBlock[] GetPSLanguageBlocks(RunspaceContext context, Sou
}
finally
{
context.Writer.ExitScope();
context.Writer?.ExitScope();
context.PopScope(RunspaceScope.Source);
ps.Runspace = null;
ps.Dispose();
Expand All @@ -253,7 +247,7 @@ private static ILanguageBlock[] GetPSLanguageBlocks(RunspaceContext context, Sou
/// <summary>
/// Get language blocks from YAML source files.
/// </summary>
private static ILanguageBlock[] GetYamlLanguageBlocks(Source[] sources, RunspaceContext context)
private static ILanguageBlock[] GetYamlLanguageBlocks(Source[] sources, IResourceDiscoveryContext context)
{
var result = new Collection<ILanguageBlock>();
var visitor = new ResourceValidator(context.Writer);
Expand Down Expand Up @@ -284,7 +278,7 @@ private static ILanguageBlock[] GetYamlLanguageBlocks(Source[] sources, Runspace
if (file.Type != SourceType.Yaml)
continue;

context.VerboseRuleDiscovery(path: file.Path);
context.Writer?.VerboseRuleDiscovery(path: file.Path);
context.EnterLanguageScope(file);
try
{
Expand Down Expand Up @@ -319,7 +313,7 @@ private static ILanguageBlock[] GetYamlLanguageBlocks(Source[] sources, Runspace
/// <summary>
/// Get language blocks from JSON source files.
/// </summary>
private static ILanguageBlock[] GetJsonLanguageBlocks(Source[] sources, RunspaceContext context)
private static ILanguageBlock[] GetJsonLanguageBlocks(Source[] sources, IResourceDiscoveryContext context)
{
var result = new Collection<ILanguageBlock>();
var visitor = new ResourceValidator(context.Writer);
Expand All @@ -344,7 +338,7 @@ private static ILanguageBlock[] GetJsonLanguageBlocks(Source[] sources, Runspace
if (file.Type != SourceType.Json)
continue;

context.VerboseRuleDiscovery(file.Path);
context.Writer?.VerboseRuleDiscovery(file.Path);
context.EnterLanguageScope(file);
try
{
Expand Down Expand Up @@ -636,7 +630,7 @@ private static RuleHelpInfo[] ToRuleHelp(IEnumerable<ILanguageBlock> blocks, Run
private static Baseline[] ToBaselineV1(IEnumerable<ILanguageBlock> blocks, RunspaceContext context)
{
if (blocks == null)
return Array.Empty<Baseline>();
return [];

// Index baselines by BaselineId
var results = new Dictionary<string, Baseline>(StringComparer.OrdinalIgnoreCase);
Expand Down
14 changes: 14 additions & 0 deletions src/PSRule/Pipeline/IResourceCache.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

using PSRule.Definitions;

namespace PSRule.Pipeline;

/// <summary>
/// A cache that stores resources.
/// </summary>
internal interface IResourceCache
{
bool Import(IResource resource);
}
16 changes: 16 additions & 0 deletions src/PSRule/Pipeline/PipelineWriterExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// Licensed under the MIT License.

using System.Management.Automation;
using System.Management.Automation.Language;
using PSRule.Resources;

namespace PSRule.Pipeline;
Expand Down Expand Up @@ -98,6 +99,21 @@ internal static void WriteError(this IPipelineWriter writer, PipelineException e
writer.WriteError(new ErrorRecord(exception, errorId, errorCategory, null));
}

internal static void WriteError(this IPipelineWriter writer, ParseError error)
{
if (writer == null || !writer.ShouldWriteError())
return;

var record = new ErrorRecord
(
exception: new Pipeline.ParseException(message: error.Message, errorId: error.ErrorId),
errorId: error.ErrorId,
errorCategory: ErrorCategory.InvalidOperation,
targetObject: null
);
writer.WriteError(errorRecord: record);
}

internal static void WriteDebug(this IPipelineWriter writer, string message, params object[] args)
{
if (writer == null || !writer.ShouldWriteDebug() || string.IsNullOrEmpty(message))
Expand Down
Loading

0 comments on commit 4650fcc

Please sign in to comment.