зеркало из https://github.com/microsoft/PSRule.git
Родитель
fadde8c7bd
Коммит
fe0ef097cc
|
@ -0,0 +1,10 @@
|
|||
# Feature flagging
|
||||
|
||||
!!! Abstract
|
||||
Feature flags are a way to enable or disable functionality.
|
||||
Rule and module authors can use feature flags to toggle functionality on or off.
|
||||
|
||||
## Using feature flags in emitters
|
||||
|
||||
When an emitter is executed `IEmitterContext` is passed into each call.
|
||||
This context includes a `Configuration` property that exposes `IConfiguration`.
|
|
@ -29,7 +29,7 @@ public interface IEmitterContext
|
|||
void Emit(ITargetObject value);
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// Determine if a specified path should be queued for processing.
|
||||
/// </summary>
|
||||
bool ShouldQueue(string path);
|
||||
}
|
||||
|
|
|
@ -35,8 +35,4 @@
|
|||
</EmbeddedResource>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Folder Include="Runtime\" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
|
|
@ -0,0 +1,63 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
namespace PSRule.Runtime;
|
||||
|
||||
/// <summary>
|
||||
/// Access configuration values at runtime.
|
||||
/// </summary>
|
||||
public interface IConfiguration
|
||||
{
|
||||
/// <summary>
|
||||
/// Try to the configuration item if it exists.
|
||||
/// </summary>
|
||||
/// <param name="configurationKey">The name of the configuration item.</param>
|
||||
/// <param name="defaultValue">The default value to use if the configuration item does not exist.</param>
|
||||
/// <returns>Returns the configuration item or the specified default value.</returns>
|
||||
object? GetValueOrDefault(string configurationKey, object? defaultValue = default);
|
||||
|
||||
/// <summary>
|
||||
/// Get the specified configuration item as a string if it exists.
|
||||
/// </summary>
|
||||
/// <param name="configurationKey">The name of the configuration item.</param>
|
||||
/// <param name="defaultValue">The default value to use if the configuration item does not exist.</param>
|
||||
/// <returns>Returns the configuration item or the specified default value.</returns>
|
||||
string? GetStringOrDefault(string configurationKey, string? defaultValue = default);
|
||||
|
||||
/// <summary>
|
||||
/// Get the specified configuration item as a boolean if it exists.
|
||||
/// </summary>
|
||||
/// <param name="configurationKey">The name of the configuration item.</param>
|
||||
/// <param name="defaultValue">The default value to use if the configuration item does not exist.</param>
|
||||
/// <returns>Returns the configuration item or the specified default value.</returns>
|
||||
bool? GetBoolOrDefault(string configurationKey, bool? defaultValue = default);
|
||||
|
||||
/// <summary>
|
||||
/// Get the specified configuration item as an integer if it exists.
|
||||
/// </summary>
|
||||
/// <param name="configurationKey">The name of the configuration item.</param>
|
||||
/// <param name="defaultValue">The default value to use if the configuration item does not exist.</param>
|
||||
/// <returns>Returns the configuration item or the specified default value.</returns>
|
||||
int? GetIntegerOrDefault(string configurationKey, int? defaultValue = default);
|
||||
|
||||
/// <summary>
|
||||
/// Get the specified configuration item as a string array.
|
||||
/// </summary>
|
||||
/// <param name="configurationKey">The name of the configuration item.</param>
|
||||
/// <returns>
|
||||
/// Returns an array of strings.
|
||||
/// If the configuration key does not exist and empty array is returned.
|
||||
/// If the configuration key is a string, an array with a single element is returned.
|
||||
/// </returns>
|
||||
string[] GetStringValues(string configurationKey);
|
||||
|
||||
/// <summary>
|
||||
/// Check if specified configuration item is enabled.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Use this method to check if a feature is enabled.
|
||||
/// </remarks>
|
||||
/// <param name="configurationKey">The name of the configuration item.</param>
|
||||
/// <returns>Returns <c>true</c> when the configuration item exists and it set to <c>true</c>. Otherwise <c>false</c> is returned.</returns>
|
||||
bool IsEnabled(string configurationKey);
|
||||
}
|
|
@ -2,6 +2,7 @@
|
|||
// Licensed under the MIT License.
|
||||
|
||||
using System.Collections.Concurrent;
|
||||
using PSRule.Configuration;
|
||||
using PSRule.Data;
|
||||
using PSRule.Emitters;
|
||||
using PSRule.Options;
|
||||
|
@ -19,8 +20,8 @@ internal sealed class EmitterContext : BaseEmitterContext
|
|||
/// <summary>
|
||||
/// Create an instance containing context for an <see cref="IEmitter"/>.
|
||||
/// </summary>
|
||||
internal EmitterContext(ConcurrentQueue<ITargetObject> queue, PathFilter inputFilter, InputFormat? inputFormat, string objectPath, bool? shouldEmitFile)
|
||||
: base(inputFormat ?? InputFormat.None, objectPath, shouldEmitFile ?? false)
|
||||
internal EmitterContext(ConcurrentQueue<ITargetObject> queue, PathFilter inputFilter, PSRuleOption option)
|
||||
: base(option?.Input?.Format ?? InputFormat.None, option?.Input?.ObjectPath, option?.Input?.FileObjects ?? false)
|
||||
{
|
||||
_Queue = queue;
|
||||
_InputFilter = inputFilter;
|
||||
|
|
|
@ -99,13 +99,6 @@ internal sealed class GetTargetPipelineBuilder : PipelineBuilderBase, IGetTarget
|
|||
return PipelineReceiverActions.ConvertFromPowerShellData(sourceObject, next);
|
||||
});
|
||||
}
|
||||
//else if (Option.Input.Format == InputFormat.Detect && _InputPath != null)
|
||||
//{
|
||||
// AddVisitTargetObjectAction((sourceObject, next) =>
|
||||
// {
|
||||
// return PipelineReceiverActions.DetectInputFormat(sourceObject, next);
|
||||
// });
|
||||
//}
|
||||
return new PipelineInputStream(VisitTargetObject, _InputPath, GetInputObjectSourceFilter(), Option.Input.Format, Option.Input.ObjectPath, Option.Input.FileObjects);
|
||||
return new PipelineInputStream(VisitTargetObject, _InputPath, GetInputObjectSourceFilter(), Option);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
namespace PSRule.Pipeline;
|
||||
|
||||
/// <summary>
|
||||
/// A helper to build a pipeline for executing rules and conventions within a PSRule sandbox.
|
||||
/// </summary>
|
||||
public interface IInvokePipelineBuilder : IPipelineBuilder
|
||||
{
|
||||
/// <summary>
|
||||
/// Configures paths that will be scanned for input.
|
||||
/// </summary>
|
||||
/// <param name="path">An array of relative or absolute path specs to be scanned. Directories will be recursively scanned for all files not excluded matching the file path spec.</param>
|
||||
void InputPath(string[] path);
|
||||
|
||||
/// <summary>
|
||||
/// Configures a variable that will receive all results in addition to the host context.
|
||||
/// </summary>
|
||||
/// <param name="variableName">The name of the variable to set.</param>
|
||||
void ResultVariable(string variableName);
|
||||
|
||||
/// <summary>
|
||||
/// Unblocks PowerShell sources from trusted publishers that originate from an Internet zone.
|
||||
/// </summary>
|
||||
/// <param name="publisher">The trusted publisher to unblock.</param>
|
||||
void UnblockPublisher(string publisher);
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
using PSRule.Configuration;
|
||||
|
@ -7,30 +7,6 @@ using PSRule.Options;
|
|||
|
||||
namespace PSRule.Pipeline;
|
||||
|
||||
/// <summary>
|
||||
/// A helper to build a pipeline for executing rules and conventions within a PSRule sandbox.
|
||||
/// </summary>
|
||||
public interface IInvokePipelineBuilder : IPipelineBuilder
|
||||
{
|
||||
/// <summary>
|
||||
/// Configures paths that will be scanned for input.
|
||||
/// </summary>
|
||||
/// <param name="path">An array of relative or absolute path specs to be scanned. Directories will be recursively scanned for all files not excluded matching the file path spec.</param>
|
||||
void InputPath(string[] path);
|
||||
|
||||
/// <summary>
|
||||
/// Configures a variable that will receive all results in addition to the host context.
|
||||
/// </summary>
|
||||
/// <param name="variableName">The name of the variable to set.</param>
|
||||
void ResultVariable(string variableName);
|
||||
|
||||
/// <summary>
|
||||
/// Unblocks PowerShell sources from trusted publishers that originate from an Internet zone.
|
||||
/// </summary>
|
||||
/// <param name="publisher">The trusted publisher to unblock.</param>
|
||||
void UnblockPublisher(string publisher);
|
||||
}
|
||||
|
||||
internal abstract class InvokePipelineBuilderBase : PipelineBuilderBase, IInvokePipelineBuilder
|
||||
{
|
||||
protected InputPathBuilder _InputPath;
|
||||
|
@ -190,22 +166,6 @@ internal abstract class InvokePipelineBuilderBase : PipelineBuilderBase, IInvoke
|
|||
return PipelineReceiverActions.ConvertFromPowerShellData(sourceObject, next);
|
||||
});
|
||||
}
|
||||
//else if (Option.Input.Format == InputFormat.Detect && _InputPath != null)
|
||||
//{
|
||||
// AddVisitTargetObjectAction((sourceObject, next) =>
|
||||
// {
|
||||
// return PipelineReceiverActions.DetectInputFormat(sourceObject, next);
|
||||
// });
|
||||
//}
|
||||
return new PipelineInputStream(VisitTargetObject, _InputPath, GetInputObjectSourceFilter(), Option.Input.Format, Option.Input.ObjectPath, Option.Input.FileObjects);
|
||||
return new PipelineInputStream(VisitTargetObject, _InputPath, GetInputObjectSourceFilter(), Option);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A helper to construct the pipeline for Invoke-PSRule.
|
||||
/// </summary>
|
||||
internal sealed class InvokeRulePipelineBuilder : InvokePipelineBuilderBase
|
||||
{
|
||||
internal InvokeRulePipelineBuilder(Source[] source, IHostContext hostContext)
|
||||
: base(source, hostContext) { }
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
namespace PSRule.Pipeline;
|
||||
|
||||
/// <summary>
|
||||
/// A helper to construct the pipeline for Invoke-PSRule.
|
||||
/// </summary>
|
||||
internal sealed class InvokeRulePipelineBuilder : InvokePipelineBuilderBase
|
||||
{
|
||||
internal InvokeRulePipelineBuilder(Source[] source, IHostContext hostContext)
|
||||
: base(source, hostContext) { }
|
||||
}
|
|
@ -387,7 +387,7 @@ internal abstract class PipelineBuilderBase : IPipelineBuilder
|
|||
|
||||
protected virtual PipelineInputStream PrepareReader()
|
||||
{
|
||||
return new PipelineInputStream(null, null, GetInputObjectSourceFilter(), Option.Input.Format, Option.Input.ObjectPath, Option.Input.FileObjects);
|
||||
return new PipelineInputStream(null, null, GetInputObjectSourceFilter(), Option);
|
||||
}
|
||||
|
||||
protected virtual PipelineWriter PrepareWriter()
|
||||
|
|
|
@ -3,8 +3,8 @@
|
|||
|
||||
using System.Collections.Concurrent;
|
||||
using System.Management.Automation;
|
||||
using PSRule.Configuration;
|
||||
using PSRule.Data;
|
||||
using PSRule.Options;
|
||||
using PSRule.Pipeline.Emitters;
|
||||
|
||||
namespace PSRule.Pipeline;
|
||||
|
@ -22,13 +22,13 @@ internal sealed class PipelineInputStream
|
|||
private readonly ConcurrentQueue<ITargetObject> _Queue;
|
||||
private readonly EmitterCollection _EmitterCollection;
|
||||
|
||||
public PipelineInputStream(VisitTargetObject input, InputPathBuilder inputPath, PathFilter inputFilter, InputFormat? inputFormat, string objectPath, bool? shouldEmitFile)
|
||||
public PipelineInputStream(VisitTargetObject input, InputPathBuilder inputPath, PathFilter inputFilter, PSRuleOption option)
|
||||
{
|
||||
_Input = input;
|
||||
_InputPath = inputPath;
|
||||
_InputFilter = inputFilter;
|
||||
_Queue = new ConcurrentQueue<ITargetObject>();
|
||||
_EmitterCollection = new EmitterBuilder().Build(new EmitterContext(_Queue, inputFilter, inputFormat, objectPath, shouldEmitFile));
|
||||
_EmitterCollection = new EmitterBuilder().Build(new EmitterContext(_Queue, inputFilter, option));
|
||||
}
|
||||
|
||||
public int Count => _Queue.Count;
|
||||
|
|
|
@ -6,10 +6,12 @@ using System.Dynamic;
|
|||
|
||||
namespace PSRule.Runtime;
|
||||
|
||||
#nullable enable
|
||||
|
||||
/// <summary>
|
||||
/// A set of rule configuration values that are exposed at runtime and automatically failback to defaults when not set in configuration.
|
||||
/// A set of rule configuration values that are exposed at runtime and automatically fallback to defaults when not set in configuration.
|
||||
/// </summary>
|
||||
public sealed class Configuration : DynamicObject
|
||||
public sealed class Configuration : DynamicObject, IConfiguration
|
||||
{
|
||||
private readonly RunspaceContext _Context;
|
||||
|
||||
|
@ -19,7 +21,7 @@ public sealed class Configuration : DynamicObject
|
|||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override bool TryGetMember(GetMemberBinder binder, out object result)
|
||||
public override bool TryGetMember(GetMemberBinder binder, out object? result)
|
||||
{
|
||||
result = null;
|
||||
if (binder == null || string.IsNullOrEmpty(binder.Name))
|
||||
|
@ -29,18 +31,47 @@ public sealed class Configuration : DynamicObject
|
|||
return TryGetValue(binder.Name, out result);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the specified configuration key as a string array.
|
||||
/// </summary>
|
||||
/// <param name="configurationKey">A key for the configuration value.</param>
|
||||
/// <returns>Returns an array of strings. If the configuration key does not exist and empty array is returned.</returns>
|
||||
/// <inheritdoc/>
|
||||
public object? GetValueOrDefault(string configurationKey, object? defaultValue = default)
|
||||
{
|
||||
return TryGetValue(configurationKey, out var value) && value != null ? value : defaultValue;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string? GetStringOrDefault(string configurationKey, string? defaultValue = default)
|
||||
{
|
||||
return TryGetValue(configurationKey, out var value) &&
|
||||
value != null &&
|
||||
TryString(value, out var result) &&
|
||||
result != null ? result : defaultValue;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool? GetBoolOrDefault(string configurationKey, bool? defaultValue = default)
|
||||
{
|
||||
return TryGetValue(configurationKey, out var value) &&
|
||||
value != null &&
|
||||
TryBool(value, out var result) &&
|
||||
result != null ? result : defaultValue;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public int? GetIntegerOrDefault(string configurationKey, int? defaultValue = default)
|
||||
{
|
||||
return TryGetValue(configurationKey, out var value) &&
|
||||
value != null &&
|
||||
TryInt(value, out var result) &&
|
||||
result != null ? result : defaultValue;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string[] GetStringValues(string configurationKey)
|
||||
{
|
||||
if (!TryGetValue(configurationKey, out var value) || value == null)
|
||||
return Array.Empty<string>();
|
||||
return [];
|
||||
|
||||
if (value is string valueT)
|
||||
return new string[] { valueT };
|
||||
return [valueT];
|
||||
|
||||
if (value is string[] result)
|
||||
return result;
|
||||
|
@ -49,56 +80,34 @@ public sealed class Configuration : DynamicObject
|
|||
{
|
||||
var cList = new List<string>();
|
||||
foreach (var v in c)
|
||||
{
|
||||
cList.Add(v.ToString());
|
||||
}
|
||||
|
||||
return cList.ToArray();
|
||||
return [.. cList];
|
||||
}
|
||||
return new string[] { value.ToString() };
|
||||
return [value.ToString()];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Try to the configuration key or use the specified default value if the key does not exist.
|
||||
/// </summary>
|
||||
/// <param name="configurationKey">A key for the configuration value.</param>
|
||||
/// <param name="defaultValue">The default value to use if the configuration key does not exist.</param>
|
||||
/// <returns>Returns the configured value or the default.</returns>
|
||||
public object GetValueOrDefault(string configurationKey, object defaultValue)
|
||||
/// <inheritdoc/>
|
||||
public bool IsEnabled(string configurationKey)
|
||||
{
|
||||
return !TryGetValue(configurationKey, out var value) || value == null ? defaultValue : value;
|
||||
return TryGetValue(configurationKey, out var value) &&
|
||||
value != null &&
|
||||
TryBool(value, out var result) &&
|
||||
result == true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Try to get the configuration key as a <seealso cref="bool"/>.
|
||||
/// </summary>
|
||||
/// <param name="configurationKey">A key for the configuration value.</param>
|
||||
/// <param name="defaultValue">The default value to use if the configuration key does not exist.</param>
|
||||
/// <returns>Returns the configured value or the default.</returns>
|
||||
public bool GetBoolOrDefault(string configurationKey, bool defaultValue)
|
||||
private bool TryGetValue(string name, out object? value)
|
||||
{
|
||||
return !TryGetValue(configurationKey, out var value) || !TryBool(value, out var result) ? defaultValue : result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Try to get the configuration key as an <seealso cref="int"/>.
|
||||
/// </summary>
|
||||
/// <param name="configurationKey">A key for the configuration value.</param>
|
||||
/// <param name="defaultValue">The default value to use if the configuration key does not exist.</param>
|
||||
/// <returns>Returns the configured value or the default.</returns>
|
||||
public int GetIntegerOrDefault(string configurationKey, int defaultValue)
|
||||
{
|
||||
return !TryGetValue(configurationKey, out var value) || !TryInt(value, out var result) ? defaultValue : result;
|
||||
}
|
||||
|
||||
private bool TryGetValue(string name, out object value)
|
||||
{
|
||||
value = null;
|
||||
value = default;
|
||||
return _Context != null && _Context.TryGetConfigurationValue(name, out value);
|
||||
}
|
||||
|
||||
private static bool TryBool(object o, out bool value)
|
||||
private static bool TryBool(object o, out bool? value)
|
||||
{
|
||||
value = default;
|
||||
if (o is bool result || (o is string svalue && bool.TryParse(svalue, out result)))
|
||||
if (o is bool result || (o is string s && bool.TryParse(s, out result)))
|
||||
{
|
||||
value = result;
|
||||
return true;
|
||||
|
@ -106,10 +115,21 @@ public sealed class Configuration : DynamicObject
|
|||
return false;
|
||||
}
|
||||
|
||||
private static bool TryInt(object o, out int value)
|
||||
private static bool TryInt(object o, out int? value)
|
||||
{
|
||||
value = default;
|
||||
if (o is int result || (o is string svalue && int.TryParse(svalue, out result)))
|
||||
if (o is int result || (o is string s && int.TryParse(s, out result)))
|
||||
{
|
||||
value = result;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool TryString(object o, out string? value)
|
||||
{
|
||||
value = default;
|
||||
if (o is string result)
|
||||
{
|
||||
value = result;
|
||||
return true;
|
||||
|
@ -117,3 +137,5 @@ public sealed class Configuration : DynamicObject
|
|||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
#nullable restore
|
||||
|
|
|
@ -9,8 +9,14 @@ namespace PSRule.Runtime;
|
|||
[Flags]
|
||||
public enum RunspaceScope
|
||||
{
|
||||
/// <summary>
|
||||
/// Unknown scope.
|
||||
/// </summary>
|
||||
None = 0,
|
||||
|
||||
/// <summary>
|
||||
/// During source discovery.
|
||||
/// </summary>
|
||||
Source = 1,
|
||||
|
||||
/// <summary>
|
||||
|
@ -28,13 +34,43 @@ public enum RunspaceScope
|
|||
/// </summary>
|
||||
Resource = 8,
|
||||
|
||||
/// <summary>
|
||||
/// When a convention is executing the begin block.
|
||||
/// </summary>
|
||||
ConventionBegin = 16,
|
||||
|
||||
/// <summary>
|
||||
/// When a convention is executing the process block.
|
||||
/// </summary>
|
||||
ConventionProcess = 32,
|
||||
|
||||
/// <summary>
|
||||
/// When a convention is executing the end block.
|
||||
/// </summary>
|
||||
ConventionEnd = 64,
|
||||
|
||||
/// <summary>
|
||||
/// When a convention is executing the initialize block.
|
||||
/// </summary>
|
||||
ConventionInitialize = 128,
|
||||
|
||||
/// <summary>
|
||||
/// When any convention block is executing.
|
||||
/// </summary>
|
||||
Convention = ConventionInitialize | ConventionBegin | ConventionProcess | ConventionEnd,
|
||||
|
||||
/// <summary>
|
||||
/// When a runtime block is executing and the target is available.
|
||||
/// </summary>
|
||||
Target = Rule | Precondition | ConventionBegin | ConventionProcess,
|
||||
|
||||
/// <summary>
|
||||
/// When any runtime block is executing within a rule or convention.
|
||||
/// </summary>
|
||||
Runtime = Rule | Precondition | Convention,
|
||||
|
||||
/// <summary>
|
||||
/// All scopes.
|
||||
/// </summary>
|
||||
All = Source | Rule | Precondition | Resource | Convention,
|
||||
}
|
||||
|
|
|
@ -180,7 +180,7 @@ public sealed class PipelineTests
|
|||
Environment.UseCurrentCulture(CultureInfo.InvariantCulture);
|
||||
var context = PipelineContext.New(GetOption(), null, null, null, null, null, new OptionContextBuilder(), null);
|
||||
var writer = new TestWriter(GetOption());
|
||||
var pipeline = new GetRulePipeline(context, GetSource(), new PipelineInputStream(null, null, null, null, null, null), writer, false);
|
||||
var pipeline = new GetRulePipeline(context, GetSource(), new PipelineInputStream(null, null, null, null), writer, false);
|
||||
try
|
||||
{
|
||||
pipeline.Begin();
|
||||
|
@ -202,7 +202,7 @@ public sealed class PipelineTests
|
|||
option.Execution.InvariantCulture = ExecutionActionPreference.Ignore;
|
||||
var context = PipelineContext.New(option, null, null, null, null, null, new OptionContextBuilder(), null);
|
||||
var writer = new TestWriter(option);
|
||||
var pipeline = new GetRulePipeline(context, GetSource(), new PipelineInputStream(null, null, null, null, null, null), writer, false);
|
||||
var pipeline = new GetRulePipeline(context, GetSource(), new PipelineInputStream(null, null, null, null), writer, false);
|
||||
try
|
||||
{
|
||||
pipeline.Begin();
|
||||
|
|
Загрузка…
Ссылка в новой задаче