From 61e1fc0b459aa8e95b2f55a811b424e76326fca3 Mon Sep 17 00:00:00 2001 From: anmenaga Date: Mon, 10 Aug 2020 18:48:32 -0700 Subject: [PATCH 01/64] Initial code --- .../DscSupport/ConvertCimMofToJsonCommand.cs | 49 + .../DscSupport/JsonCimDSCParser.cs | 77 + .../DscSupport/JsonDeserializer.cs | 47 + .../DscSupport/JsonDscClassCache.cs | 3807 +++++++++++++++++ .../DscSupport/MofCimDSCParser.cs | 484 +++ .../{CimDSCParser.cs => MofDscClassCache.cs} | 545 +-- .../engine/InitialSessionState.cs | 1 + 7 files changed, 4545 insertions(+), 465 deletions(-) create mode 100755 src/System.Management.Automation/DscSupport/ConvertCimMofToJsonCommand.cs create mode 100755 src/System.Management.Automation/DscSupport/JsonCimDSCParser.cs create mode 100755 src/System.Management.Automation/DscSupport/JsonDeserializer.cs create mode 100755 src/System.Management.Automation/DscSupport/JsonDscClassCache.cs create mode 100755 src/System.Management.Automation/DscSupport/MofCimDSCParser.cs rename src/System.Management.Automation/DscSupport/{CimDSCParser.cs => MofDscClassCache.cs} (88%) mode change 100644 => 100755 diff --git a/src/System.Management.Automation/DscSupport/ConvertCimMofToJsonCommand.cs b/src/System.Management.Automation/DscSupport/ConvertCimMofToJsonCommand.cs new file mode 100755 index 00000000000..697590cea72 --- /dev/null +++ b/src/System.Management.Automation/DscSupport/ConvertCimMofToJsonCommand.cs @@ -0,0 +1,49 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Collections; +using System.Collections.Generic; +using System.IO; +using System.Management.Automation; +using System.Text; + +namespace Microsoft.PowerShell.DesiredStateConfiguration.Internal +{ + /// + /// Convert-CimMofToJson cmdlet implementation + /// + [Cmdlet(VerbsData.Convert, "CimMofToJson")] + public sealed class ConvertCimMofToJsonCommand : Cmdlet + { + /// + /// Top level directory to serach for .mof files + /// + /// Test + [Parameter(ValueFromPipeline = true, Position = 0)] + [ValidateNotNullOrEmpty] + public string Directory { get; set; } + + /// + /// Main cmdlet method + /// + protected override void ProcessRecord() + { + // Mof parser uses DSC_HOME env var which is normally set by PSDesiredStateConfiguration module + // Because this cmlet can be run without loading PSDesiredStateConfiguration module, we are setting this env var here. + string varName = "DSC_HOME"; + if (string.IsNullOrEmpty(Environment.GetEnvironmentVariable(varName, EnvironmentVariableTarget.Process))) + { + var pshome = Utils.DefaultPowerShellAppBase; + var dsc_home = Path.Combine(pshome, "Modules", "PSDesiredStateConfiguration", "Configuration"); + Environment.SetEnvironmentVariable(varName, dsc_home, EnvironmentVariableTarget.Process); + } + + Mof.DscClassCache.Initialize(); + foreach(var mofPath in System.IO.Directory.GetFiles(this.Directory, "*.mof", SearchOption.AllDirectories)) + { + Mof.DscClassCache.ConvertCimMofToJson(mofPath); + } + } + } +} diff --git a/src/System.Management.Automation/DscSupport/JsonCimDSCParser.cs b/src/System.Management.Automation/DscSupport/JsonCimDSCParser.cs new file mode 100755 index 00000000000..58eb720a411 --- /dev/null +++ b/src/System.Management.Automation/DscSupport/JsonCimDSCParser.cs @@ -0,0 +1,77 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Globalization; +using System.IO; +using System.Management.Automation; +using System.Security; + +namespace Microsoft.PowerShell.DesiredStateConfiguration +{ + /// + /// Class that does high level Cim schema parsing + /// + internal class CimDSCParser + { + private JsonDeserializer _json_deserializer; + + internal CimDSCParser() + { + _json_deserializer = JsonDeserializer.Create(); + } + + internal List ParseSchemaJson(string filePath) + { + string json = System.IO.File.ReadAllText(filePath); + try + { + string fileNameDefiningClass = Path.GetFileNameWithoutExtension(filePath); + int dotIndex = fileNameDefiningClass.IndexOf('.'); + if (dotIndex != -1) + { + fileNameDefiningClass = fileNameDefiningClass.Substring(0, dotIndex); + } + + var result = new List(_json_deserializer.DeserializeClasses(json)); + foreach (dynamic c in result) + { + string superClassName = c.CimSuperClassName; + string className = c.CimSystemProperties.ClassName; + if ((superClassName != null) && (superClassName.Equals("OMI_BaseResource", StringComparison.OrdinalIgnoreCase))) + { + // Get the name of the file without schema.mof/json extension + if (!(className.Equals(fileNameDefiningClass, StringComparison.OrdinalIgnoreCase))) + { + PSInvalidOperationException e = PSTraceSource.NewInvalidOperationException( + ParserStrings.ClassNameNotSameAsDefiningFile, className, fileNameDefiningClass); + throw e; + } + } + } + + return result; + } + catch (Exception exception) + { + PSInvalidOperationException e = PSTraceSource.NewInvalidOperationException( + exception, ParserStrings.CimDeserializationError, filePath); + + e.SetErrorId("CimDeserializationError"); + throw e; + } + } + + /// + /// Make sure that the instance conforms to the the schema. + /// + /// + internal void ValidateInstanceText(string classText) + { + throw new NotImplementedException("Instance parsing/validation is not yet suported by JSON-based parser"); + } + } +} diff --git a/src/System.Management.Automation/DscSupport/JsonDeserializer.cs b/src/System.Management.Automation/DscSupport/JsonDeserializer.cs new file mode 100755 index 00000000000..db38badf019 --- /dev/null +++ b/src/System.Management.Automation/DscSupport/JsonDeserializer.cs @@ -0,0 +1,47 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Management.Automation; + +namespace Microsoft.PowerShell.DesiredStateConfiguration +{ + internal class JsonDeserializer + { + #region Constructors + + /// + /// Instantiates a default deserializer + /// + public static JsonDeserializer Create() + { + return new JsonDeserializer(); + } + + #endregion Constructors + + #region Methods + + /// + /// Returns schema of Cim classes from specified json file + /// + public IEnumerable DeserializeClasses(string json) + { + IEnumerable result = null; + using (var powerShell = System.Management.Automation.PowerShell.Create(RunspaceMode.NewRunspace)) + { + powerShell.AddCommand("ConvertFrom-Json"); + powerShell.AddParameter("InputObject", json); + powerShell.AddParameter("Depth", 100); // maximum supported by cmdlet + + result = powerShell.Invoke(); + } + + return result; + } + + #endregion Methods + } +} diff --git a/src/System.Management.Automation/DscSupport/JsonDscClassCache.cs b/src/System.Management.Automation/DscSupport/JsonDscClassCache.cs new file mode 100755 index 00000000000..dd52be2666c --- /dev/null +++ b/src/System.Management.Automation/DscSupport/JsonDscClassCache.cs @@ -0,0 +1,3807 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Globalization; +using System.IO; +using System.Linq; +using System.Management.Automation; +using System.Management.Automation.Language; +using System.Reflection; +using System.Runtime.InteropServices; +using System.Security; +using System.Text; + +using Microsoft.PowerShell.Commands; + +namespace Microsoft.PowerShell.DesiredStateConfiguration.Internal +{ + /// + /// + [SuppressMessage("Microsoft.MSInternal", "CA903:InternalNamespaceShouldNotContainPublicTypes", + Justification = "Needed Internal use only")] + internal class DscClassCacheEntry + { + /// + /// Store the RunAs Credentials that this DSC resource will use. + /// + public DSCResourceRunAsCredential DscResRunAsCred; + + /// + /// If we have implicitly imported this resource, we will set this field to true. This will + /// only happen to InBox resources. + /// + public bool IsImportedImplicitly; + + /// + /// A CimClass instance for this resource. + /// + public PSObject CimClassInstance; + + /// + /// Initializes variables with default values. + /// + public DscClassCacheEntry() : this(DSCResourceRunAsCredential.Default, false, null) { } + + /// + /// Initializes all values. + /// + /// + /// + /// + public DscClassCacheEntry(DSCResourceRunAsCredential aDSCResourceRunAsCredential, bool aIsImportedImplicitly, PSObject aCimClassInstance) + { + DscResRunAsCred = aDSCResourceRunAsCredential; + IsImportedImplicitly = aIsImportedImplicitly; + CimClassInstance = aCimClassInstance; + } + } + + /// + /// + [SuppressMessage("Microsoft.MSInternal", "CA903:InternalNamespaceShouldNotContainPublicTypes", + Justification = "Needed Internal use only")] + public static class DscClassCache + { + private const string InboxDscResourceModulePath = "WindowsPowershell\\v1.0\\Modules\\PsDesiredStateConfiguration"; + private const string reservedDynamicKeywords = "^(Synchronization|Certificate|IIS|SQL)$"; + + private const string reservedProperties = "^(Require|Trigger|Notify|Before|After|Subscribe)$"; + + private static PSTraceSource s_tracer = PSTraceSource.GetTracer("DSC", "DSC Class Cache"); + + // Constants for items in the module qualified name (Module\Version\ClassName) + private const int IndexModuleName = 0; + private const int IndexModuleVersion = 1; + private const int IndexClassName = 2; + private const int IndexFriendlyName = 3; + + // Create a list of classes which are not actual DSC resources similar to what we do inside PSDesiredStateConfiguration.psm1 + private static readonly string[] s_hiddenResourceList = + { + "MSFT_BaseConfigurationProviderRegistration", + "MSFT_CimConfigurationProviderRegistration", + "MSFT_PSConfigurationProviderRegistration", + }; + + // Create a HashSet for fast lookup. According to MSDN, the time complexity of search for an element in a HashSet is O(1) + private static readonly HashSet s_hiddenResourceCache = new HashSet(s_hiddenResourceList, + StringComparer.OrdinalIgnoreCase); + + // a collection to hold current importing script based resource file + // this prevent circular importing case when the script resource existing in the same module with resources it import-dscresource + private static readonly HashSet s_currentImportingScriptFiles = new HashSet(StringComparer.OrdinalIgnoreCase); + + /// + /// DSC class cache for this runspace. + /// Cache stores the DSCRunAsBehavior, cim class and boolean to indicate if an Inbox resource has been implicitly imported. + /// + private static Dictionary ClassCache + { + get + { + if (t_classCache == null) + { + t_classCache = new Dictionary(StringComparer.OrdinalIgnoreCase); + } + + return t_classCache; + } + } + + [ThreadStatic] + private static Dictionary t_classCache; + + /// + /// DSC classname to source module mapper. + /// + private static Dictionary> ByClassModuleCache + { + get + { + if (t_byClassModuleCache == null) + { + t_byClassModuleCache = new Dictionary>(StringComparer.OrdinalIgnoreCase); + } + + return t_byClassModuleCache; + } + } + + [ThreadStatic] + private static Dictionary> t_byClassModuleCache; + + /// + /// DSC filename to defined class mapper. + /// + private static Dictionary> ByFileClassCache + { + get + { + if (t_byFileClassCache == null) + { + t_byFileClassCache = new Dictionary>(StringComparer.OrdinalIgnoreCase); + } + + return t_byFileClassCache; + } + } + + [ThreadStatic] + private static Dictionary> t_byFileClassCache; + + /// + /// Filenames from which we have imported script dynamic keywords. + /// + private static HashSet ScriptKeywordFileCache + { + get + { + if (t_scriptKeywordFileCache == null) + { + t_scriptKeywordFileCache = new HashSet(StringComparer.OrdinalIgnoreCase); + } + + return t_scriptKeywordFileCache; + } + } + + [ThreadStatic] + private static HashSet t_scriptKeywordFileCache; + + /// + /// Default ModuleName and ModuleVersion to use. + /// + private static readonly Tuple s_defaultModuleInfoForResource = new Tuple("PSDesiredStateConfiguration", new Version("1.1")); + + /// + /// Default ModuleName and ModuleVersion to use for meta configuration resources. + /// + internal static readonly Tuple DefaultModuleInfoForMetaConfigResource = new Tuple("PSDesiredStateConfigurationEngine", new Version("2.0")); + + /// + /// A set of dynamic keywords that can be used in both configuration and meta configuration. + /// + internal static readonly HashSet SystemResourceNames = + new HashSet(StringComparer.OrdinalIgnoreCase) { "Node", "OMI_ConfigurationDocument" }; + + /// + /// When this property is set to true, DSC Cache will cache multiple versions of a resource. + /// That means it will cache duplicate resource classes (class names for a resource in two different module versions are same). + /// NOTE: This property should be set to false for DSC compiler related methods/functionality, such as Import-DscResource, + /// because the Mof serializer does not support deserialization of classes with different versions. + /// + [ThreadStatic] + private static bool t_cacheResourcesFromMultipleModuleVersions; + + private static bool CacheResourcesFromMultipleModuleVersions + { + get + { + return t_cacheResourcesFromMultipleModuleVersions; + } + + set + { + t_cacheResourcesFromMultipleModuleVersions = value; + } + } + + /// + /// Initialize the class cache with the default classes in $ENV:SystemDirectory\Configuration. + /// + public static void Initialize() + { + Initialize(null, null); + } + + /// + /// Initialize the class cache with the default classes in $ENV:SystemDirectory\Configuration. + /// + /// Collection of any errors encountered during initialization. + /// List of module path from where DSC PS modules will be loaded. + public static void Initialize(Collection errors, List modulePathList) + { + s_tracer.WriteLine("Initializing DSC class cache force={0}"); + + if (Platform.IsLinux || Platform.IsMacOS) + { + //WriteVerbose("Initialize / Platform.IsLinux || Platform.IsMacOS"); + // + // Load the base schema files. + // + ClearCache(); + var dscConfigurationDirectory = Environment.GetEnvironmentVariable("DSC_HOME") ?? + "/etc/opt/omi/conf/dsc/configuration"; + + if (!Directory.Exists(dscConfigurationDirectory)) + { + throw new DirectoryNotFoundException("Unable to find DSC schema store at " + dscConfigurationDirectory + ". Please ensure PS DSC for Linux is installed."); + } + + //WriteVerbose("ImportClasses : BaseRegistration/BaseResource.schema.json"); + var resourceBaseFile = Path.Combine(dscConfigurationDirectory, "BaseRegistration/BaseResource.schema.json"); + ImportClasses(resourceBaseFile, s_defaultModuleInfoForResource, errors); + //WriteVerbose("ImportClasses : BaseRegistration/MSFT_DSCMetaConfiguration.json"); + var metaConfigFile = Path.Combine(dscConfigurationDirectory, "BaseRegistration/MSFT_DSCMetaConfiguration.json"); + ImportClasses(metaConfigFile, s_defaultModuleInfoForResource, errors); + + var allResourceRoots = new string[] { dscConfigurationDirectory }; + + // + // Load all of the system resource schema files, searching + // + string resources; + foreach (var resourceRoot in allResourceRoots) + { + resources = Path.Combine(resourceRoot, "schema"); + if (!Directory.Exists(resources)) + { + continue; + } + + foreach (var schemaFile in Directory.EnumerateDirectories(resources).SelectMany(d => Directory.EnumerateFiles(d, "*.schema.json"))) + { + WriteVerbose("ImportClasses : " + schemaFile); + ImportClasses(schemaFile, s_defaultModuleInfoForResource, errors); + } + } + + // Linux DSC Modules are installed to the dscConfigurationDirectory, so no need to load them. + } + else + { + // DSC SxS scenario + var configSystemPath = Utils.DefaultPowerShellAppBase; + var systemResourceRoot = Path.Combine(configSystemPath, "Configuration"); + var inboxModulePath = "Modules\\PSDesiredStateConfiguration"; + + if (!Directory.Exists(systemResourceRoot)) + { + configSystemPath = Platform.GetFolderPath(Environment.SpecialFolder.System); + systemResourceRoot = Path.Combine(configSystemPath, "Configuration"); + inboxModulePath = InboxDscResourceModulePath; + } + + var programFilesDirectory = Platform.GetFolderPath(Environment.SpecialFolder.ProgramFiles); + Debug.Assert(programFilesDirectory != null, "Program Files environment variable does not exist!"); + var customResourceRoot = Path.Combine(programFilesDirectory, "WindowsPowerShell\\Configuration"); + Debug.Assert(Directory.Exists(customResourceRoot), "%ProgramFiles%\\WindowsPowerShell\\Configuration Directory does not exist"); + var allResourceRoots = new string[] { systemResourceRoot, customResourceRoot }; + // + // Load the base schema files. + // + ClearCache(); + var resourceBaseFile = Path.Combine(systemResourceRoot, "BaseRegistration\\BaseResource.schema.json"); + ImportClasses(resourceBaseFile, s_defaultModuleInfoForResource, errors); + + var metaConfigFile = Path.Combine(systemResourceRoot, "BaseRegistration\\MSFT_DSCMetaConfiguration.json"); + ImportClasses(metaConfigFile, s_defaultModuleInfoForResource, errors); + + var metaConfigExtensionFile = Path.Combine(systemResourceRoot, "BaseRegistration\\MSFT_MetaConfigurationExtensionClasses.schema.json"); + ImportClasses(metaConfigExtensionFile, DefaultModuleInfoForMetaConfigResource, errors); + + // + // Load all of the system resource schema files, searching + // + string resources; + foreach (var resourceRoot in allResourceRoots) + { + resources = Path.Combine(resourceRoot, "Schema"); + if (!Directory.Exists(resources)) + { + continue; + } + + foreach (var schemaFile in Directory.EnumerateDirectories(resources).SelectMany(d => Directory.EnumerateFiles(d, "*.schema.json"))) + { + ImportClasses(schemaFile, s_defaultModuleInfoForResource, errors); + } + } + + // Load Regular and DSC PS modules + bool importInBoxResourcesImplicitly = false; + List modulePaths = new List(); + if (modulePathList == null || modulePathList.Count == 0) + { + modulePaths.Add(Path.Combine(configSystemPath, inboxModulePath)); + importInBoxResourcesImplicitly = true; + } + else + { + foreach (string moduleFolderPath in modulePathList) + { + if (!Directory.Exists(moduleFolderPath)) + { + continue; + } + + foreach (string moduleDir in Directory.EnumerateDirectories(moduleFolderPath)) + { + modulePaths.Add(moduleDir); + } + } + } + + LoadDSCResourceIntoCache(errors, modulePaths, importInBoxResourcesImplicitly); + } + } + + /// + /// Load DSC resources into Cache from moduleFolderPath. + /// + /// Collection of any errors encountered during initialization. + /// Module path from where DSC PS modules will be loaded. + /// + /// if module is inbox. + /// + private static void LoadDSCResourceIntoCache(Collection errors, List modulePathList, bool importInBoxResourcesImplicitly) + { + foreach (string moduleDir in modulePathList) + { + if (!Directory.Exists(moduleDir)) continue; + + var dscResourcesPath = Path.Combine(moduleDir, "DscResources"); + if (Directory.Exists(dscResourcesPath)) + { + foreach (string resourceDir in Directory.EnumerateDirectories(dscResourcesPath)) + { + IEnumerable schemaFiles = Directory.EnumerateFiles(resourceDir, "*.schema.json"); + if (!schemaFiles.Any()) + { + continue; + } + + Tuple moduleInfo = GetModuleInfoHelper(moduleDir, importInBoxResourcesImplicitly, isPsProviderModule: false); + if (moduleInfo == null) + { + continue; + } + + foreach (string schemaFile in schemaFiles) + { + ImportClasses(schemaFile, moduleInfo, errors, importInBoxResourcesImplicitly); + } + } + } + } + } + + /// + /// Get the module name and module version. + /// + /// + /// Path to the module folder + /// + /// + /// if module is inbox and we are importing resources implicitly + /// + /// + /// Indicate a internal DSC module + /// + /// + private static Tuple GetModuleInfoHelper(string moduleFolderPath, bool importInBoxResourcesImplicitly, bool isPsProviderModule) + { + string moduleName = "PsDesiredStateConfiguration"; + if (!importInBoxResourcesImplicitly) + { + moduleName = Path.GetFileName(moduleFolderPath); + } + + string manifestPath = Path.Combine(moduleFolderPath, moduleName + ".psd1"); + s_tracer.WriteLine("DSC GetModuleVersion: Try retrieving module version information from file: {0}.", manifestPath); + + if (!File.Exists(manifestPath)) + { + if (isPsProviderModule) + { + // Some internal PSProviders do not come with a .psd1 file, such + // as MSFT_LogResource. We don't report error in this case. + return new Tuple(moduleName, new Version("1.0")); + } + else + { + s_tracer.WriteLine("DSC GetModuleVersion: Manifest file '{0}' not exist.", manifestPath); + return null; + } + } + + try + { + Hashtable dataFileSetting = + PsUtils.GetModuleManifestProperties( + manifestPath, + PsUtils.ManifestModuleVersionPropertyName); + + object versionValue = dataFileSetting["ModuleVersion"]; + if (versionValue != null) + { + Version moduleVersion; + if (LanguagePrimitives.TryConvertTo(versionValue, out moduleVersion)) + { + return new Tuple(moduleName, moduleVersion); + } + else + { + s_tracer.WriteLine( + "DSC GetModuleVersion: ModuleVersion value '{0}' cannot be converted to System.Version. Skip the module '{1}'.", + versionValue, moduleName); + } + } + else + { + s_tracer.WriteLine( + "DSC GetModuleVersion: Manifest file '{0}' does not contain ModuleVersion. Skip the module '{1}'.", + manifestPath, moduleName); + } + } + catch (PSInvalidOperationException ex) + { + s_tracer.WriteLine( + "DSC GetModuleVersion: Error evaluating module manifest file '{0}', with error '{1}'. Skip the module '{2}'.", + manifestPath, ex, moduleName); + } + + return null; + } + + + private static void WriteVerbose(string warning) + { + var executionContext = System.Management.Automation.Runspaces.Runspace.DefaultRunspace.ExecutionContext; + if (executionContext != null && executionContext.InternalHost != null && executionContext.InternalHost.UI != null) + { + executionContext.InternalHost.UI.WriteVerboseLine(warning); + } + } + + /// + /// Parses json file without adding it to caches or creating dynamic keywords + /// + /// Path to json file + /// List of classes from json file + public static List ReadClassesFromJson(string jsonFilePath) + { + if (! jsonFilePath.EndsWith(".json", StringComparison.InvariantCultureIgnoreCase)) + { + WriteVerbose(string.Format("Cannot parse non-JSON file {0}", jsonFilePath)); + return null; + } + + var parser = new Microsoft.PowerShell.DesiredStateConfiguration.CimDSCParser(); + List classes = parser.ParseSchemaJson(jsonFilePath); + return classes; + } + + /// + /// Import CIM classes from the given file. + /// + /// + /// + /// + /// + /// + public static List ImportClasses(string path, Tuple moduleInfo, Collection errors, bool importInBoxResourcesImplicitly = false) + { + if (string.IsNullOrEmpty(path)) + { + throw PSTraceSource.NewArgumentNullException(nameof(path)); + } + + if (! path.EndsWith(".json", StringComparison.InvariantCultureIgnoreCase)) + { + WriteVerbose(string.Format("Cannot parse non-JSON file {0}", path)); + return null; + } + + s_tracer.WriteLine("DSC ClassCache: importing file: {0}", path); + + var parser = new Microsoft.PowerShell.DesiredStateConfiguration.CimDSCParser(); + + List classes = null; + try + { + classes = parser.ParseSchemaJson(path); + } + catch (PSInvalidOperationException e) + { + // Ignore modules with invalid schemas. + s_tracer.WriteLine("DSC ClassCache: Error importing file '{0}', with error '{1}'. Skipping file.", path, e); + if (errors != null) + { + errors.Add(e); + } + } + + if (classes != null) + { + foreach (dynamic c in classes) + { + // Only add the class once... + var className = c.CimSystemProperties.ClassName; + string alias = GetFriendlyName(c); + var friendlyName = string.IsNullOrEmpty(alias) ? className : alias; + string moduleQualifiedResourceName = GetModuleQualifiedResourceName(moduleInfo.Item1, moduleInfo.Item2.ToString(), className, friendlyName); + DscClassCacheEntry cimClassInfo; + + if (ClassCache.TryGetValue(moduleQualifiedResourceName, out cimClassInfo)) + { + PSObject cimClass = cimClassInfo.CimClassInstance; + + // If this is a nested object and we already have exactly same nested object, we will + // allow sharing of nested objects. + if (!IsSameNestedObject(cimClass, c)) + { + var files = string.Join(",", GetFileDefiningClass(className)); + PSInvalidOperationException e = PSTraceSource.NewInvalidOperationException( + ParserStrings.DuplicateCimClassDefinition, className, path, files); + + e.SetErrorId("DuplicateCimClassDefinition"); + if (errors != null) + { + errors.Add(e); + } + } + } + + if (s_hiddenResourceCache.Contains(className)) + { + continue; + } + + if (!CacheResourcesFromMultipleModuleVersions) + { + // Find & remove the previous version of the resource. + List> resourceList = FindResourceInCache(moduleInfo.Item1, className, friendlyName); + if (resourceList.Count > 0 && !string.IsNullOrEmpty(resourceList[0].Key)) + { + ClassCache.Remove(resourceList[0].Key); + + // keyword is already defined and it is a Inbox resource, remove it + if (DynamicKeyword.ContainsKeyword(friendlyName) && resourceList[0].Value.IsImportedImplicitly) + { + DynamicKeyword.RemoveKeyword(friendlyName); + } + } + } + + ClassCache[moduleQualifiedResourceName] = new DscClassCacheEntry(DSCResourceRunAsCredential.Default, importInBoxResourcesImplicitly, c); + ByClassModuleCache[className] = moduleInfo; + } + + var sb = new System.Text.StringBuilder(); + foreach (dynamic c in classes) + { + sb.Append(c.CimSystemProperties.ClassName); + sb.Append(","); + } + + s_tracer.WriteLine("DSC ClassCache: loading file '{0}' added the following classes to the cache: {1}", path, sb.ToString()); + } + else + { + s_tracer.WriteLine("DSC ClassCache: loading file '{0}' added no classes to the cache."); + } + + ByFileClassCache[path] = classes; + return classes; + } + + /// + /// Get text from SecureString. + /// + /// Value of SecureString. + /// Decoded string. + public static string GetStringFromSecureString(SecureString value) + { + string passwordValueToAdd = string.Empty; + + if (value != null) + { + IntPtr ptr = Marshal.SecureStringToCoTaskMemUnicode(value); + passwordValueToAdd = Marshal.PtrToStringUni(ptr); + Marshal.ZeroFreeCoTaskMemUnicode(ptr); + } + + return passwordValueToAdd; + } + + /// + /// Clear out the existing collection of CIM classes and associated keywords. + /// + public static void ClearCache() + { + s_tracer.WriteLine("DSC class: clearing the cache and associated keywords."); + ClassCache.Clear(); + ByClassModuleCache.Clear(); + ByFileClassCache.Clear(); + ScriptKeywordFileCache.Clear(); + CacheResourcesFromMultipleModuleVersions = false; + } + + /// + /// Returns module qualified resource name in "Module\Version\Class" format. + /// + /// + /// + /// + /// + /// + private static string GetModuleQualifiedResourceName(string moduleName, string moduleVersion, string className, string resourceName) + { + return string.Format(CultureInfo.InvariantCulture, "{0}\\{1}\\{2}\\{3}", moduleName, moduleVersion, className, resourceName); + } + + /// + /// Finds resources in the that which matches the specified class and module name. + /// + /// Module name. + /// Resource type name. + /// Resource friendly name. + /// List of found resources in the form of Dictionary{moduleQualifiedName, cimClass}, otherwise empty list. + private static List> FindResourceInCache(string moduleName, string className, string resourceName) + { + return (from cacheEntry in ClassCache + let splittedName = cacheEntry.Key.Split(Utils.Separators.Backslash) + let cachedClassName = splittedName[IndexClassName] + let cachedModuleName = splittedName[IndexModuleName] + let cachedResourceName = splittedName[IndexFriendlyName] + where (string.Equals(cachedResourceName, resourceName, StringComparison.OrdinalIgnoreCase) + || (string.Equals(cachedClassName, className, StringComparison.OrdinalIgnoreCase) + && string.Equals(cachedModuleName, moduleName, StringComparison.OrdinalIgnoreCase))) + select cacheEntry).ToList(); + } + + /// + /// + /// + private static List GetCachedClasses() + { + return ClassCache.Values.ToList(); + } + + /// + /// Find cached cim classes defined under specified module. + /// + /// + /// List of cached cim classes. + public static List GetCachedClassesForModule(PSModuleInfo module) + { + List cachedClasses = new List(); + var moduleQualifiedName = string.Format(CultureInfo.InvariantCulture, "{0}\\{1}", module.Name, module.Version.ToString()); + foreach (var dscClassCacheEntry in ClassCache) + { + if (dscClassCacheEntry.Key.StartsWith(moduleQualifiedName, StringComparison.OrdinalIgnoreCase)) + { + cachedClasses.Add(dscClassCacheEntry.Value.CimClassInstance); + } + } + + return cachedClasses; + } + + /// + /// Get the file that defined this class. + /// + /// + /// + public static List GetFileDefiningClass(string className) + { + List files = new List(); + foreach (var pair in ByFileClassCache) + { + var file = pair.Key; + var classList = pair.Value; + if (classList != null) + { + foreach(dynamic c in classList) + { + if (string.Equals(c.CimSystemProperties.ClassName, className, StringComparison.OrdinalIgnoreCase)) + { + files.Add(file); + } + } + } + } + + return files; + } + + /// + /// Get a list of files from which classes have been loaded. + /// + /// + public static string[] GetLoadedFiles() + { + return ByFileClassCache.Keys.ToArray(); + } + + /// + /// Returns the classes that we loaded from the specified file name. + /// + /// + /// + public static List GetCachedClassByFileName(string fileName) + { + if (string.IsNullOrWhiteSpace(fileName)) + { + throw PSTraceSource.NewArgumentNullException(nameof(fileName)); + } + + List listCimClass; + ByFileClassCache.TryGetValue(fileName, out listCimClass); + return listCimClass; + } + + /// + /// Returns the classes associated with the specified module name. + /// Per PowerShell the module name is the base name of the schema file. + /// + /// + /// + public static List GetCachedClassByModuleName(string moduleName) + { + if (string.IsNullOrWhiteSpace(moduleName)) + { + throw PSTraceSource.NewArgumentNullException(nameof(moduleName)); + } + + var moduleFileName = moduleName + ".schema.json"; + return (from filename in ByFileClassCache.Keys where string.Equals(Path.GetFileName(filename), moduleFileName, StringComparison.OrdinalIgnoreCase) select GetCachedClassByFileName(filename)).FirstOrDefault(); + } + +/*TODO-AM: + /// + /// Routine used to load a set of CIM instances from a .mof file using the + /// current set of cached classes for schema validation. + /// + /// The file to load the classes from. + /// + public static List ImportInstances(string path) + { + if (string.IsNullOrEmpty(path)) + { + throw PSTraceSource.NewArgumentNullException(nameof(path)); + } + + var parser = new Microsoft.PowerShell.DesiredStateConfiguration.CimDSCParser(); + + return parser.ParseInstanceMof(path); + } + + /// + /// Routine used to load a set of CIM instances from a .mof file using the + /// current set of cached classes for schema validation. + /// + /// + /// + /// + public static List ImportInstances(string path, int schemaValidationOption) + { + if (string.IsNullOrEmpty(path)) + { + throw PSTraceSource.NewArgumentNullException(nameof(path)); + } + + if (schemaValidationOption < (int)Microsoft.Management.Infrastructure.Serialization.MofDeserializerSchemaValidationOption.Default || + schemaValidationOption > (int)Microsoft.Management.Infrastructure.Serialization.MofDeserializerSchemaValidationOption.Ignore) + { + throw new IndexOutOfRangeException("schemaValidationOption"); + } + + var parser = new Microsoft.PowerShell.DesiredStateConfiguration.CimDSCParser(MyClassCallback, (Microsoft.Management.Infrastructure.Serialization.MofDeserializerSchemaValidationOption)schemaValidationOption); + + return parser.ParseInstanceMof(path); + }*/ + + /// + /// A routine that validates a string containing MOF instances against the + /// current set of cached classes. + /// + /// + public static void ValidateInstanceText(string instanceText) + { + if (string.IsNullOrEmpty(instanceText)) + { + throw PSTraceSource.NewArgumentNullException(nameof(instanceText)); + } + + var parser = new Microsoft.PowerShell.DesiredStateConfiguration.CimDSCParser(); + + parser.ValidateInstanceText(instanceText); + } + + private static bool IsMagicProperty(string propertyName) + { + return System.Text.RegularExpressions.Regex.Match(propertyName, + "^(ResourceId|SourceInfo|ModuleName|ModuleVersion|ConfigurationName)$", + System.Text.RegularExpressions.RegexOptions.IgnoreCase).Success; + } + + private static string GetFriendlyName(dynamic cimClass) + { + try + { + foreach(dynamic qualifier in cimClass.CimClassQualifiers) + { + if (qualifier.Name == "FriendlyName") + { + return qualifier.Value as string; + } + } + } + catch (Microsoft.Management.Infrastructure.CimException) + { + // exception means no DSCAlias + } + + return null; + } + + /// + /// Method to get the cached classes in the form of DynamicKeyword. + /// + public static Collection GetCachedKeywords() + { + Collection keywords = new Collection(); + + foreach (KeyValuePair cachedClass in ClassCache) + { + string[] splittedName = cachedClass.Key.Split(Utils.Separators.Backslash); + string moduleName = splittedName[IndexModuleName]; + string moduleVersion = splittedName[IndexModuleVersion]; + + var keyword = CreateKeywordFromCimClass(moduleName, Version.Parse(moduleVersion), cachedClass.Value.CimClassInstance, null, cachedClass.Value.DscResRunAsCred); + if (keyword != null) + { + keywords.Add(keyword); + } + } + + return keywords; + } + + /// + /// A method to generate a keyword from a CIM class object and register it to DynamicKeyword table. + /// + /// + /// + /// + /// If true, don't define the keywords, just create the functions. + /// To Specify RunAsBehavior of the class. + private static void CreateAndRegisterKeywordFromCimClass(string moduleName, Version moduleVersion, PSObject cimClass, Dictionary functionsToDefine, DSCResourceRunAsCredential runAsBehavior) + { + var keyword = CreateKeywordFromCimClass(moduleName, moduleVersion, cimClass, functionsToDefine, runAsBehavior); + if (keyword == null) + { + return; + } + + // keyword is already defined and we don't allow redefine it + if (!CacheResourcesFromMultipleModuleVersions && DynamicKeyword.ContainsKeyword(keyword.Keyword)) + { + var oldKeyword = DynamicKeyword.GetKeyword(keyword.Keyword); + if (oldKeyword.ImplementingModule == null || + !oldKeyword.ImplementingModule.Equals(moduleName, StringComparison.OrdinalIgnoreCase) || oldKeyword.ImplementingModuleVersion != moduleVersion) + { + var e = PSTraceSource.NewInvalidOperationException(ParserStrings.DuplicateKeywordDefinition, keyword.Keyword); + e.SetErrorId("DuplicateKeywordDefinition"); + throw e; + } + } + // Add the dynamic keyword to the table + DynamicKeyword.AddKeyword(keyword); + + // And now define the driver functions in the current scope... + if (functionsToDefine != null) + { + functionsToDefine[moduleName + "\\" + keyword.Keyword] = CimKeywordImplementationFunction; + } + } + + /// + /// A method to generate a keyword from a CIM class object. This is used for DSC. + /// + /// + /// + /// + /// If true, don't define the keywords, just create the functions. + /// To specify RunAs behavior of the class. + private static DynamicKeyword CreateKeywordFromCimClass(string moduleName, Version moduleVersion, dynamic cimClass, Dictionary functionsToDefine, DSCResourceRunAsCredential runAsBehavior) + { + var resourceName = cimClass.CimSystemProperties.ClassName; + string alias = GetFriendlyName(cimClass); + var keywordString = string.IsNullOrEmpty(alias) ? resourceName : alias; + + // + // Skip all of the base, meta, registration and other classes that are not intended to be used directly by a script author + // + if (System.Text.RegularExpressions.Regex.Match(keywordString, "^OMI_Base|^OMI_.*Registration", System.Text.RegularExpressions.RegexOptions.IgnoreCase).Success) + { + return null; + } + + var keyword = new DynamicKeyword() + { + BodyMode = DynamicKeywordBodyMode.Hashtable, + Keyword = keywordString, + ResourceName = resourceName, + ImplementingModule = moduleName, + ImplementingModuleVersion = moduleVersion, + SemanticCheck = CheckMandatoryPropertiesPresent + }; + + // If it's one of reserved dynamic keyword, mark it + if (System.Text.RegularExpressions.Regex.Match(keywordString, reservedDynamicKeywords, System.Text.RegularExpressions.RegexOptions.IgnoreCase).Success) + { + keyword.IsReservedKeyword = true; + } + + // see if it's a resource type i.e. it inherits from OMI_BaseResource + bool isResourceType = false; + /*for (var classToCheck = cimClass; !string.IsNullOrEmpty(classToCheck.CimSuperClassName); classToCheck = classToCheck.CimSuperClass) + { + if (string.Equals("OMI_BaseResource", classToCheck.CimSuperClassName, StringComparison.OrdinalIgnoreCase) || string.Equals("OMI_MetaConfigurationResource", classToCheck.CimSuperClassName, StringComparison.OrdinalIgnoreCase)) + { + isResourceType = true; + break; + } + }*/ + // code above is the only place that references CimSuperClass + // so to simplify things we just check superclass to be OMI_BaseResource + // with assumption that current code will not work for multi-level class inheritance (which is never used in practice according to DSC team) + if ((!string.IsNullOrEmpty(cimClass.CimSuperClassName)) && string.Equals("OMI_BaseResource", cimClass.CimSuperClassName, StringComparison.OrdinalIgnoreCase)) + { + isResourceType = true; + } + + // If it's a resource type, then a resource name is required. + keyword.NameMode = isResourceType ? DynamicKeywordNameMode.NameRequired : DynamicKeywordNameMode.NoName; + + // + // Add the settable properties to the keyword object + // + foreach (var prop in cimClass.CimClassProperties) + { + // If the property is marked as readonly, skip it... + if ((prop.Flags != null) && prop.Flags.Contains("ReadOnly")) + { + continue; + } + + // If the property has the Read qualifier, also skip it. + foreach(var qualifier in prop.Qualifiers) + { + if (qualifier.Name == "Read") + { + continue; + } + } + + // If it's one of our magic properties, skip it + if (IsMagicProperty(prop.Name)) + { + continue; + } + + if (runAsBehavior == DSCResourceRunAsCredential.NotSupported) + { + if (string.Equals(prop.Name, "PsDscRunAsCredential", StringComparison.OrdinalIgnoreCase)) + { + // skip adding PsDscRunAsCredential to the dynamic word for the dsc resource. + continue; + } + } + // If it's one of our reserved properties, save it for error reporting + if (System.Text.RegularExpressions.Regex.Match(prop.Name, reservedProperties, System.Text.RegularExpressions.RegexOptions.IgnoreCase).Success) + { + keyword.HasReservedProperties = true; + continue; + } + + // Otherwise, add it to the Keyword List. + var keyProp = new System.Management.Automation.Language.DynamicKeywordProperty(); + keyProp.Name = prop.Name; + + // Set the mandatory flag if appropriate + if ((prop.Flags != null) && prop.Flags.Contains("Key")) + { + keyProp.Mandatory = true; + keyProp.IsKey = true; + } + + // Copy the type name string. If it's an embedded instance, need to grab it from the ReferenceClassName + bool referenceClassNameIsNullOrEmpty = string.IsNullOrEmpty(prop.ReferenceClassName); + if (prop.CimType == "Instance" && !referenceClassNameIsNullOrEmpty) + { + keyProp.TypeConstraint = prop.ReferenceClassName; + } + else if (prop.CimType == "InstanceArray" && !referenceClassNameIsNullOrEmpty) + { + keyProp.TypeConstraint = prop.ReferenceClassName + "[]"; + } + else + { + keyProp.TypeConstraint = prop.CimType.ToString(); + } + + string[] valueMap = null; + foreach (var qualifier in prop.Qualifiers) + { + // Check to see if there is a Values attribute and save the list of allowed values if so. + if (string.Equals(qualifier.Name, "Values", StringComparison.OrdinalIgnoreCase) && qualifier.CimType == "StringArray") + { + int count = qualifier.Value.Length; + string[] values = new string[count]; + for(int i=0; i 0) + { + if (valueMap.Length != keyProp.Values.Count) + { + s_tracer.WriteLine( + "DSC CreateDynamicKeywordFromClass: the count of values for qualifier 'Values' and 'ValueMap' doesn't match. count of 'Values': {0}, count of 'ValueMap': {1}. Skip the keyword '{2}'.", + keyProp.Values.Count, valueMap.Length, keyword.Keyword); + return null; + } + + for (int index = 0; index < valueMap.Length; index++) + { + string key = keyProp.Values[index]; + string value = valueMap[index]; + + if (keyProp.ValueMap.ContainsKey(key)) + { + s_tracer.WriteLine( + "DSC CreateDynamicKeywordFromClass: same string value '{0}' appears more than once in qualifier 'Values'. Skip the keyword '{1}'.", + key, keyword.Keyword); + return null; + } + + keyProp.ValueMap.Add(key, value); + } + } + + keyword.Properties.Add(prop.Name, keyProp); + } + + // update specific keyword with range constraints + UpdateKnownRestriction(keyword); + + return keyword; + } + + /// + /// Update range restriction for meta configuration keywords + /// the restrictions are for + /// ConfigurationModeFrequency: 15-44640 + /// RefreshFrequency: 30-44640. + /// + /// + private static void UpdateKnownRestriction(DynamicKeyword keyword) + { + if ( + string.Equals(keyword.ResourceName, "MSFT_DSCMetaConfigurationV2", + StringComparison.OrdinalIgnoreCase) + || + string.Equals(keyword.ResourceName, "MSFT_DSCMetaConfiguration", + StringComparison.OrdinalIgnoreCase)) + { + if (keyword.Properties["RefreshFrequencyMins"] != null) + { + keyword.Properties["RefreshFrequencyMins"].Range = new Tuple(30, 44640); + } + + if (keyword.Properties["ConfigurationModeFrequencyMins"] != null) + { + keyword.Properties["ConfigurationModeFrequencyMins"].Range = new Tuple(15, 44640); + } + + if (keyword.Properties["DebugMode"] != null) + { + keyword.Properties["DebugMode"].Values.Remove("ResourceScriptBreakAll"); + keyword.Properties["DebugMode"].ValueMap.Remove("ResourceScriptBreakAll"); + } + } + } + + /// + /// Load the default system CIM classes and create the corresponding keywords. + /// + public static void LoadDefaultCimKeywords() + { + LoadDefaultCimKeywords(null, null, null, false); + } + + /// + /// Load the default system CIM classes and create the corresponding keywords. + /// + /// List of module path from where DSC PS modules will be loaded. + public static void LoadDefaultCimKeywords(List modulePathList) + { + LoadDefaultCimKeywords(null, null, modulePathList, false); + } + + /// + /// Load the default system CIM classes and create the corresponding keywords. + /// + /// Collection of any errors encountered while loading keywords. + public static void LoadDefaultCimKeywords(Collection errors) + { + LoadDefaultCimKeywords(null, errors, null, false); + } + + /// + /// Load the default system CIM classes and create the corresponding keywords. + /// A dictionary to add the defined functions to, may be null. + /// + public static void LoadDefaultCimKeywords(Dictionary functionsToDefine) + { + LoadDefaultCimKeywords(functionsToDefine, null, null, false); + } + + /// + /// Load the default system CIM classes and create the corresponding keywords. + /// + /// Collection of any errors encountered while loading keywords. + /// Allow caching the resources from multiple versions of modules. + public static void LoadDefaultCimKeywords(Collection errors, bool cacheResourcesFromMultipleModuleVersions) + { + LoadDefaultCimKeywords(null, errors, null, cacheResourcesFromMultipleModuleVersions); + } + + /// + /// Load the default system CIM classes and create the corresponding keywords. + /// + /// A dictionary to add the defined functions to, may be null. + /// Collection of any errors encountered while loading keywords. + /// List of module path from where DSC PS modules will be loaded. + /// Allow caching the resources from multiple versions of modules. + private static void LoadDefaultCimKeywords(Dictionary functionsToDefine, Collection errors, + List modulePathList, bool cacheResourcesFromMultipleModuleVersions) + { + DynamicKeyword.Reset(); + Initialize(errors, modulePathList); + + // Initialize->ClearCache resets CacheResourcesFromMultipleModuleVersions to false, + // workaround is to set it after Initialize method call. + // Initialize method imports all the Inbox resources and internal classes which belongs to only one version + // of the module, so it is ok if this property is not set during cache initialization. + CacheResourcesFromMultipleModuleVersions = cacheResourcesFromMultipleModuleVersions; + + foreach (dynamic cimClass in GetCachedClasses()) + { + var className = cimClass.CimClassInstance.CimSystemProperties.ClassName; + var moduleInfo = ByClassModuleCache[className]; + CreateAndRegisterKeywordFromCimClass(moduleInfo.Item1, moduleInfo.Item2, cimClass.CimClassInstance, functionsToDefine, cimClass.DscResRunAsCred); + } + + // And add the Node keyword definitions + if (!DynamicKeyword.ContainsKeyword("Node")) + { + // Implement dispatch to the Node keyword. + var nodeKeyword = new DynamicKeyword() + { + BodyMode = DynamicKeywordBodyMode.ScriptBlock, + ImplementingModule = s_defaultModuleInfoForResource.Item1, + ImplementingModuleVersion = s_defaultModuleInfoForResource.Item2, + NameMode = DynamicKeywordNameMode.NameRequired, + Keyword = "Node", + }; + DynamicKeyword.AddKeyword(nodeKeyword); + } + + // And add the Import-DscResource keyword definitions + if (!DynamicKeyword.ContainsKeyword("Import-DscResource")) + { + // Implement dispatch to the Node keyword. + var nodeKeyword = new DynamicKeyword() + { + BodyMode = DynamicKeywordBodyMode.Command, + ImplementingModule = s_defaultModuleInfoForResource.Item1, + ImplementingModuleVersion = s_defaultModuleInfoForResource.Item2, + NameMode = DynamicKeywordNameMode.NoName, + Keyword = "Import-DscResource", + MetaStatement = true, + PostParse = ImportResourcePostParse, + SemanticCheck = ImportResourceCheckSemantics + }; + DynamicKeyword.AddKeyword(nodeKeyword); + } + } + + // This function is called after parsing the Import-DscResource keyword and it's arguments, but before parsing + // anything else. + // + // + private static ParseError[] ImportResourcePostParse(DynamicKeywordStatementAst kwAst) + { + var elements = Ast.CopyElements(kwAst.CommandElements); + + Diagnostics.Assert(elements[0] is StringConstantExpressionAst && + ((StringConstantExpressionAst)elements[0]).Value.Equals("Import-DscResource", StringComparison.OrdinalIgnoreCase), + "Incorrect ast for expected keyword"); + var commandAst = new CommandAst(kwAst.Extent, elements, TokenKind.Unknown, null); + + const string nameParam = "Name"; + const string moduleNameParam = "ModuleName"; + const string moduleVersionParam = "ModuleVersion"; + + StaticBindingResult bindingResult = StaticParameterBinder.BindCommand(commandAst, false); + + var errorList = new List(); + foreach (var bindingException in bindingResult.BindingExceptions.Values) + { + errorList.Add(new ParseError(bindingException.CommandElement.Extent, + "ParameterBindingException", + bindingException.BindingException.Message)); + } + + ParameterBindingResult moduleNameBindingResult = null; + ParameterBindingResult resourceNameBindingResult = null; + ParameterBindingResult moduleVersionBindingResult = null; + + foreach (var binding in bindingResult.BoundParameters) + { + // Error case when positional parameter values are specified + var boundParameterName = binding.Key; + var parameterBindingResult = binding.Value; + if (boundParameterName.All(char.IsDigit)) + { + errorList.Add(new ParseError(parameterBindingResult.Value.Extent, + "ImportDscResourcePositionalParamsNotSupported", + string.Format(CultureInfo.CurrentCulture, ParserStrings.ImportDscResourcePositionalParamsNotSupported))); + continue; + } + + if (nameParam.StartsWith(boundParameterName, StringComparison.OrdinalIgnoreCase)) + { + resourceNameBindingResult = parameterBindingResult; + } + else if (moduleNameParam.StartsWith(boundParameterName, StringComparison.OrdinalIgnoreCase)) + { + moduleNameBindingResult = parameterBindingResult; + } + else if (moduleVersionParam.StartsWith(boundParameterName, StringComparison.OrdinalIgnoreCase)) + { + moduleVersionBindingResult = parameterBindingResult; + } + else + { + errorList.Add(new ParseError(parameterBindingResult.Value.Extent, + "ImportDscResourceNeedParams", + string.Format(CultureInfo.CurrentCulture, ParserStrings.ImportDscResourceNeedParams))); + } + } + + if (errorList.Count == 0 && moduleNameBindingResult == null && resourceNameBindingResult == null) + { + errorList.Add(new ParseError(kwAst.Extent, + "ImportDscResourceNeedParams", + string.Format(CultureInfo.CurrentCulture, ParserStrings.ImportDscResourceNeedParams))); + } + + // Check here if Version is specified but modulename is not specified + if (moduleVersionBindingResult != null && moduleNameBindingResult == null) + { + // only add this error again to the error list if resources is not null + // if resources and modules are both null we have already added this error in collection + // we do not want to do this twice. since we are giving same error ImportDscResourceNeedParams in both cases + // once we have different error messages for 2 scenarios we can remove this check + if (resourceNameBindingResult != null) + { + errorList.Add(new ParseError(kwAst.Extent, + "ImportDscResourceNeedModuleNameWithModuleVersion", + string.Format(CultureInfo.CurrentCulture, ParserStrings.ImportDscResourceNeedParams))); + } + } + + string[] resourceNames = null; + if (resourceNameBindingResult != null) + { + object resourceName = null; + if (!IsConstantValueVisitor.IsConstant(resourceNameBindingResult.Value, out resourceName, true, true) || + !LanguagePrimitives.TryConvertTo(resourceName, out resourceNames)) + { + errorList.Add(new ParseError(resourceNameBindingResult.Value.Extent, + "RequiresInvalidStringArgument", + string.Format(CultureInfo.CurrentCulture, ParserStrings.RequiresInvalidStringArgument, nameParam))); + } + } + + System.Version moduleVersion = null; + if (moduleVersionBindingResult != null) + { + object moduleVer = null; + if (!IsConstantValueVisitor.IsConstant(moduleVersionBindingResult.Value, out moduleVer, true, true)) + { + errorList.Add(new ParseError(moduleVersionBindingResult.Value.Extent, + "RequiresArgumentMustBeConstant", + ParserStrings.RequiresArgumentMustBeConstant)); + } + + if (moduleVer is double) + { + // this happens in case -ModuleVersion 1.0, then use extent text for that. + // The better way to do it would be define static binding API against CommandInfo, that holds information about parameter types. + // This way, we can avoid this ugly special-casing and say that -ModuleVersion has type [System.Version]. + moduleVer = moduleVersionBindingResult.Value.Extent.Text; + } + + if (!LanguagePrimitives.TryConvertTo(moduleVer, out moduleVersion)) + { + errorList.Add(new ParseError(moduleVersionBindingResult.Value.Extent, + "RequiresVersionInvalid", + ParserStrings.RequiresVersionInvalid)); + } + } + + ModuleSpecification[] moduleSpecifications = null; + if (moduleNameBindingResult != null) + { + object moduleName = null; + if (!IsConstantValueVisitor.IsConstant(moduleNameBindingResult.Value, out moduleName, true, true)) + { + errorList.Add(new ParseError(moduleNameBindingResult.Value.Extent, + "RequiresArgumentMustBeConstant", + ParserStrings.RequiresArgumentMustBeConstant)); + } + + if (LanguagePrimitives.TryConvertTo(moduleName, out moduleSpecifications)) + { + // if resourceNames are specified then we can not specify multiple modules name + if (moduleSpecifications != null && moduleSpecifications.Length > 1 && resourceNames != null) + { + errorList.Add(new ParseError(moduleNameBindingResult.Value.Extent, + "ImportDscResourceMultipleModulesNotSupportedWithName", + string.Format(CultureInfo.CurrentCulture, ParserStrings.ImportDscResourceMultipleModulesNotSupportedWithName))); + } + + // if moduleversion is specified then we can not specify multiple modules name + if (moduleSpecifications != null && moduleSpecifications.Length > 1 && moduleVersion != null) + { + errorList.Add(new ParseError(moduleNameBindingResult.Value.Extent, + "ImportDscResourceMultipleModulesNotSupportedWithVersion", + string.Format(CultureInfo.CurrentCulture, ParserStrings.ImportDscResourceNeedParams))); + } + + // if moduleversion is specified then we can not specify another version in modulespecification object of ModuleName + if (moduleSpecifications != null && (moduleSpecifications[0].Version != null || moduleSpecifications[0].MaximumVersion != null) && moduleVersion != null) + { + errorList.Add(new ParseError(moduleNameBindingResult.Value.Extent, + "ImportDscResourceMultipleModuleVersionsNotSupported", + string.Format(CultureInfo.CurrentCulture, ParserStrings.ImportDscResourceNeedParams))); + } + + // If moduleVersion is specified we have only one module Name in valid scenario + // So update it's version property in module specification object that will be used to load modules + if (moduleSpecifications != null && moduleSpecifications[0].Version == null && moduleSpecifications[0].MaximumVersion == null && moduleVersion != null) + { + moduleSpecifications[0].Version = moduleVersion; + } + } + else + { + errorList.Add(new ParseError(moduleNameBindingResult.Value.Extent, + "RequiresInvalidStringArgument", + string.Format(CultureInfo.CurrentCulture, ParserStrings.RequiresInvalidStringArgument, moduleNameParam))); + } + } + + if (errorList.Count == 0) + { + // No errors, try to load the resources + LoadResourcesFromModule(kwAst.Extent, moduleSpecifications, resourceNames, errorList); + } + + return errorList.ToArray(); + } + + // This function performs semantic checks for Import-DscResource + private static ParseError[] ImportResourceCheckSemantics(DynamicKeywordStatementAst kwAst) + { + List errorList = null; + + var keywordAst = Ast.GetAncestorAst(kwAst.Parent); + while (keywordAst != null) + { + if (keywordAst.Keyword.Keyword.Equals("Node")) + { + if (errorList == null) + { + errorList = new List(); + } + + errorList.Add(new ParseError(kwAst.Extent, + "ImportDscResourceInsideNode", + string.Format(CultureInfo.CurrentCulture, ParserStrings.ImportDscResourceInsideNode))); + break; + } + + keywordAst = Ast.GetAncestorAst(keywordAst.Parent); + } + + if (errorList != null) + { + return errorList.ToArray(); + } + else + { + return null; + } + } + + // This function performs semantic checks for all DSC Resources keywords. + private static ParseError[] CheckMandatoryPropertiesPresent(DynamicKeywordStatementAst kwAst) + { + HashSet mandatoryPropertiesNames = new HashSet(StringComparer.OrdinalIgnoreCase); + foreach (var pair in kwAst.Keyword.Properties) + { + if (pair.Value.Mandatory) + { + mandatoryPropertiesNames.Add(pair.Key); + } + } + + // by design mandatoryPropertiesNames are not empty at this point: + // every resource must have at least one Key property. + + HashtableAst hashtableAst = null; + foreach (var ast in kwAst.CommandElements) + { + hashtableAst = ast as HashtableAst; + if (hashtableAst != null) + { + break; + } + } + + if (hashtableAst == null) + { + // nothing to validate + return null; + } + + foreach (var pair in hashtableAst.KeyValuePairs) + { + object evalResultObject; + if (IsConstantValueVisitor.IsConstant(pair.Item1, out evalResultObject, forAttribute: false, forRequires: false)) + { + var presentName = evalResultObject as string; + if (presentName != null) + { + if (mandatoryPropertiesNames.Remove(presentName) && mandatoryPropertiesNames.Count == 0) + { + // optimization, once all mandatory properties are specified, we can safely exit. + return null; + } + } + } + } + + if (mandatoryPropertiesNames.Count > 0) + { + ParseError[] errors = new ParseError[mandatoryPropertiesNames.Count]; + var extent = kwAst.CommandElements[0].Extent; + int i = 0; + foreach (string name in mandatoryPropertiesNames) + { + errors[i] = new ParseError(extent, "MissingValueForMandatoryProperty", + string.Format(CultureInfo.CurrentCulture, ParserStrings.MissingValueForMandatoryProperty, + kwAst.Keyword.Keyword, kwAst.Keyword.Properties.First( + p => StringComparer.OrdinalIgnoreCase.Equals(p.Value.Name, name)).Value.TypeConstraint, name)); + i++; + } + + return errors; + } + + return null; + } + + /// + /// Load DSC resources from specified module. + /// + /// Script statement loading the module, can be null. + /// Module information, can be null. + /// Name of the resource to be loaded from module. + /// List of errors reported by the method. + public static void LoadResourcesFromModule(IScriptExtent scriptExtent, + ModuleSpecification[] moduleSpecifications, + string[] resourceNames, + List errorList) + { + // get all required modules + var modules = new Collection(); + if (moduleSpecifications != null) + { + foreach (var moduleToImport in moduleSpecifications) + { + bool foundModule = false; + var moduleInfos = ModuleCmdletBase.GetModuleIfAvailable(moduleToImport); + + if (moduleInfos.Count >= 1 && (moduleToImport.Version != null || moduleToImport.Guid != null)) + { + foreach (var psModuleInfo in moduleInfos) + { + if ((moduleToImport.Guid.HasValue && moduleToImport.Guid.Equals(psModuleInfo.Guid)) || + (moduleToImport.Version != null && + moduleToImport.Version.Equals(psModuleInfo.Version))) + { + modules.Add(psModuleInfo); + foundModule = true; + break; + } + } + } + else if (moduleInfos.Count == 1) + { + modules.Add(moduleInfos[0]); + foundModule = true; + } + + if (!foundModule) + { + if (moduleInfos.Count > 1) + { + errorList.Add(new ParseError(scriptExtent, + "MultipleModuleEntriesFoundDuringParse", + string.Format(CultureInfo.CurrentCulture, + ParserStrings.MultipleModuleEntriesFoundDuringParse, + moduleToImport.Name))); + } + else + { + string moduleString = moduleToImport.Version == null + ? moduleToImport.Name + : string.Format(CultureInfo.CurrentCulture, "<{0}, {1}>", moduleToImport.Name, moduleToImport.Version); + + errorList.Add(new ParseError(scriptExtent, "ModuleNotFoundDuringParse", + string.Format(CultureInfo.CurrentCulture, ParserStrings.ModuleNotFoundDuringParse, moduleString))); + } + + return; + } + } + } + else if (resourceNames != null) + { + // Lookup the required resources under available PowerShell modules when modulename is not specified + using (var powerShell = System.Management.Automation.PowerShell.Create(RunspaceMode.CurrentRunspace)) + { + powerShell.AddCommand("Get-Module"); + powerShell.AddParameter("ListAvailable"); + modules = powerShell.Invoke(); + } + } + + // When ModuleName only specified, we need to import all resources from that module + var resourcesToImport = new List(); + if (resourceNames == null || resourceNames.Length == 0) + { + resourcesToImport.Add("*"); + } + else + { + resourcesToImport.AddRange(resourceNames); + } + + foreach (var moduleInfo in modules) + { + var dscResourcesPath = Path.Combine(moduleInfo.ModuleBase, "DscResources"); + + var resourcesFound = new List(); + LoadPowerShellClassResourcesFromModule(moduleInfo, moduleInfo, resourcesToImport, resourcesFound, errorList, null, true, scriptExtent); + + if (Directory.Exists(dscResourcesPath)) + { + foreach (var resourceToImport in resourcesToImport) + { + bool foundResources = false; + foreach (var resourceDir in Directory.EnumerateDirectories(dscResourcesPath, resourceToImport)) + { + var resourceName = Path.GetFileName(resourceDir); + + bool foundCimSchema = false; + bool foundScriptSchema = false; + string schemaMofFilePath = string.Empty; + + try + { + foundCimSchema = ImportCimKeywordsFromModule(moduleInfo, resourceName, out schemaMofFilePath); + } + catch (FileNotFoundException) + { + errorList.Add(new ParseError(scriptExtent, + "SchemaFileNotFound", + string.Format(CultureInfo.CurrentCulture, ParserStrings.SchemaFileNotFound, schemaMofFilePath))); + } + catch (PSInvalidOperationException e) + { + errorList.Add(new ParseError(scriptExtent, + e.ErrorRecord.FullyQualifiedErrorId, + e.Message)); + } + catch (Exception e) + { + errorList.Add(new ParseError(scriptExtent, + "ExceptionParsingMOFFile", + string.Format(CultureInfo.CurrentCulture, ParserStrings.ExceptionParsingMOFFile, schemaMofFilePath, e.Message))); + } + + var schemaScriptFilePath = string.Empty; + + try + { + foundScriptSchema = ImportScriptKeywordsFromModule(moduleInfo, resourceName, out schemaScriptFilePath); + } + catch (FileNotFoundException) + { + errorList.Add(new ParseError(scriptExtent, + "SchemaFileNotFound", + string.Format(CultureInfo.CurrentCulture, ParserStrings.SchemaFileNotFound, schemaScriptFilePath))); + } + catch (Exception e) + { + // This shouldn't happen so just report the error as is + errorList.Add(new ParseError(scriptExtent, + "UnexpectedParseError", + string.Format(CultureInfo.CurrentCulture, e.ToString()))); + } + + if (foundCimSchema || foundScriptSchema) + { + foundResources = true; + } + } + + // + // resourceToImport may be the friendly name of the DSC resource + // + if (!foundResources) + { + try + { + string unused; + foundResources = ImportCimKeywordsFromModule(moduleInfo, resourceToImport, out unused); + } + catch (Exception) + { + } + } + + // resource name without wildcard (*) should be imported only once + if (!resourceToImport.Contains("*") && foundResources) + { + resourcesFound.Add(resourceToImport); + } + } + } + + foreach (var resource in resourcesFound) + { + resourcesToImport.Remove(resource); + } + + if (resourcesToImport.Count == 0) + { + break; + } + } + + if (resourcesToImport.Count > 0) + { + foreach (var resourceNameToImport in resourcesToImport) + { + if (!resourceNameToImport.Contains("*")) + { + errorList.Add(new ParseError(scriptExtent, + "DscResourcesNotFoundDuringParsing", + string.Format(CultureInfo.CurrentCulture, ParserStrings.DscResourcesNotFoundDuringParsing, resourceNameToImport))); + } + } + } + } + + private static void LoadPowerShellClassResourcesFromModule(PSModuleInfo primaryModuleInfo, PSModuleInfo moduleInfo, ICollection resourcesToImport, ICollection resourcesFound, + List errorList, + Dictionary functionsToDefine = null, + bool recurse = true, + IScriptExtent extent = null) + { + if (primaryModuleInfo._declaredDscResourceExports == null || primaryModuleInfo._declaredDscResourceExports.Count == 0) + { + return; + } + + if (moduleInfo.ModuleType == ModuleType.Binary) + { +#if CORECLR + throw PSTraceSource.NewArgumentException("isConfiguration", ParserStrings.ConfigurationNotSupportedInPowerShellCore); +#else + ResolveEventHandler reh = (sender, args) => CurrentDomain_ReflectionOnlyAssemblyResolve(sender, args, moduleInfo); + + try + { + AppDomain.CurrentDomain.ReflectionOnlyAssemblyResolve += reh; + var assembly = moduleInfo.ImplementingAssembly; + if (assembly == null && moduleInfo.Path != null) + { + try + { + var path = moduleInfo.Path; + + if (moduleInfo.RootModule != null && !Path.GetExtension(moduleInfo.Path).Equals(".dll", StringComparison.OrdinalIgnoreCase)) + { + path = moduleInfo.ModuleBase + "\\" + moduleInfo.RootModule; + } + + assembly = Assembly.ReflectionOnlyLoadFrom(path); + } + catch { } + } + + // Ignore the module if we can't find the assembly. + if (assembly != null) + { + ImportKeywordsFromAssembly(moduleInfo, resourcesToImport, resourcesFound, functionsToDefine, assembly); + } + } + finally + { + AppDomain.CurrentDomain.ReflectionOnlyAssemblyResolve -= reh; + } +#endif + } + else + { + string scriptPath = null; + // handle RootModule and nestedModule together + if (moduleInfo.RootModule != null) + { + scriptPath = Path.Combine(moduleInfo.ModuleBase, moduleInfo.RootModule); + } + else if (moduleInfo.Path != null) + { + scriptPath = moduleInfo.Path; + } + + ImportKeywordsFromScriptFile(scriptPath, primaryModuleInfo, resourcesToImport, resourcesFound, functionsToDefine, errorList, extent); + } + + if (moduleInfo.NestedModules != null && recurse) + { + foreach (var nestedModule in moduleInfo.NestedModules) + { + LoadPowerShellClassResourcesFromModule(primaryModuleInfo, nestedModule, resourcesToImport, resourcesFound, errorList, functionsToDefine, recurse: false, extent: extent); + } + } + } + +#if !CORECLR + private static Assembly CurrentDomain_ReflectionOnlyAssemblyResolve(object sender, ResolveEventArgs args, PSModuleInfo moduleInfo) + { + AssemblyName name = new AssemblyName(args.Name); + + if (moduleInfo != null && !string.IsNullOrEmpty(moduleInfo.Path)) + { + string asmToCheck = Path.GetDirectoryName(moduleInfo.Path) + "\\" + name.Name + ".dll"; + if (File.Exists(asmToCheck)) + { + return Assembly.ReflectionOnlyLoadFrom(asmToCheck); + } + + asmToCheck = Path.GetDirectoryName(moduleInfo.Path) + "\\" + name.Name + ".exe"; + if (File.Exists(asmToCheck)) + { + return Assembly.ReflectionOnlyLoadFrom(asmToCheck); + } + } + + return Assembly.ReflectionOnlyLoad(args.Name); + } +#endif + + /// + /// + /// + /// + /// + /// The list of resources imported from this module. + public static List ImportClassResourcesFromModule(PSModuleInfo moduleInfo, ICollection resourcesToImport, Dictionary functionsToDefine) + { + var resourcesImported = new List(); + LoadPowerShellClassResourcesFromModule(moduleInfo, moduleInfo, resourcesToImport, resourcesImported, null, functionsToDefine); + return resourcesImported; + } + + internal static PSObject[] GenerateJsonForAst(TypeDefinitionAst typeAst) + { + var embeddedInstanceTypes = new List(); + var sb = new StringBuilder(); + + var result = GenerateJsonForAst(typeAst, sb, embeddedInstanceTypes); + var visitedInstances = new List(); + visitedInstances.Add(typeAst); + + /*TODO-AM: add support for embeddedInstanceTypes + ProcessEmbeddedInstanceTypes(embeddedInstanceTypes, visitedInstances, sb);*/ + + return result; + } + + internal static string MapTypeNameToMofType(ITypeName typeName, string memberName, string className, out bool isArrayType, out string embeddedInstanceType, List embeddedInstanceTypes, ref string[] enumNames) + { + TypeName propTypeName; + var arrayTypeName = typeName as ArrayTypeName; + if (arrayTypeName != null) + { + isArrayType = true; + propTypeName = arrayTypeName.ElementType as TypeName; + } + else + { + isArrayType = false; + propTypeName = typeName as TypeName; + } + + if (propTypeName == null || propTypeName._typeDefinitionAst == null) + { + throw new NotSupportedException(string.Format( + CultureInfo.InvariantCulture, + ParserStrings.UnsupportedPropertyTypeOfDSCResourceClass, + memberName, + typeName.FullName, + typeName)); + } + + if (propTypeName._typeDefinitionAst.IsEnum) + { + enumNames = propTypeName._typeDefinitionAst.Members.Select(m => m.Name).ToArray(); + isArrayType = false; + embeddedInstanceType = null; + return "string"; + } + + if (!embeddedInstanceTypes.Contains(propTypeName._typeDefinitionAst)) + { + embeddedInstanceTypes.Add(propTypeName._typeDefinitionAst); + } + + // The type is obviously not a string, but in the mof, we represent + // it as string (really, embeddedinstance of the class type) + embeddedInstanceType = propTypeName.Name.Replace('.', '_'); + return "string"; + } + + /*private static void GenerateJsonForAst(TypeDefinitionAst typeAst, StringBuilder sb, List embeddedInstanceTypes) + { + var className = typeAst.Name; + sb.AppendFormat(CultureInfo.InvariantCulture, "[ClassVersion(\"1.0.0\"), FriendlyName(\"{0}\")]\nclass {0}", className); + + if (typeAst.Attributes.Any(a => a.TypeName.GetReflectionAttributeType() == typeof(DscResourceAttribute))) + { + sb.Append(" : OMI_BaseResource"); + } + + sb.Append("\n{\n"); + + ProcessMembers(sb, embeddedInstanceTypes, typeAst, className); + + Queue bases = new Queue(); + foreach (var b in typeAst.BaseTypes) + { + bases.Enqueue(b); + } + + while (bases.Count > 0) + { + var b = bases.Dequeue(); + var tc = b as TypeConstraintAst; + + if (tc != null) + { + b = tc.TypeName.GetReflectionType(); + if (b == null) + { + var td = tc.TypeName as TypeName; + if (td != null && td._typeDefinitionAst != null) + { + ProcessMembers(sb, embeddedInstanceTypes, td._typeDefinitionAst, className); + foreach (var b1 in td._typeDefinitionAst.BaseTypes) + { + bases.Enqueue(b1); + } + } + + continue; + } + } + + var type = b as Type; + if (type != null) + { + ProcessMembers(type, sb, embeddedInstanceTypes, className); + var t = type.BaseType; + if (t != null) + { + bases.Enqueue(t); + } + } + } + + sb.Append("};"); + }*/ + + private static PSObject[] GenerateJsonForAst(TypeDefinitionAst typeAst, StringBuilder sb, List embeddedInstanceTypes) + { + var className = typeAst.Name; + /*TODO-AM: add base classes: + if (typeAst.Attributes.Any(a => a.TypeName.GetReflectionAttributeType() == typeof(DscResourceAttribute))) + { + sb.Append(" : OMI_BaseResource"); + }*/ + + var _ClassVersion = new PSObject(); + _ClassVersion.Properties.Add(new PSNoteProperty("Name", "ClassVersion")); + _ClassVersion.Properties.Add(new PSNoteProperty("Value", "1.0.0")); + _ClassVersion.Properties.Add(new PSNoteProperty("CimType", "String")); + _ClassVersion.Properties.Add(new PSNoteProperty("Flags", "EnableOverride, Restricted")); + + var _FriendlyName = new PSObject(); + _FriendlyName.Properties.Add(new PSNoteProperty("Name", "FriendlyName")); + _FriendlyName.Properties.Add(new PSNoteProperty("Value", className)); + _FriendlyName.Properties.Add(new PSNoteProperty("CimType", "String")); + _FriendlyName.Properties.Add(new PSNoteProperty("Flags", "EnableOverride, Restricted")); + + var _CimClassQualifiers = new PSObject[] {_ClassVersion, _FriendlyName}; + + var _CimSystemProperties = new PSObject(); + _CimSystemProperties.Properties.Add(new PSNoteProperty("Namespace", null)); + _CimSystemProperties.Properties.Add(new PSNoteProperty("ServerName", null)); + _CimSystemProperties.Properties.Add(new PSNoteProperty("ClassName", className)); + _CimSystemProperties.Properties.Add(new PSNoteProperty("Path", null)); + + var _CimClassProperties = ProcessMembers(sb, embeddedInstanceTypes, typeAst, className).ToArray(); + + + var result = new PSObject(); + result.Properties.Add(new PSNoteProperty("CimSuperClassName", null)); //TODO-AM: this has to change based on parent class + result.Properties.Add(new PSNoteProperty("CimSuperClass", null)); //TODO-AM: this has to change based on parent class + result.Properties.Add(new PSNoteProperty("CimClassProperties", _CimClassProperties)); + result.Properties.Add(new PSNoteProperty("CimClassQualifiers", _CimClassQualifiers)); + result.Properties.Add(new PSNoteProperty("CimClassMethods", new PSObject[0])); //TODO-AM: this has to change + result.Properties.Add(new PSNoteProperty("CimSystemProperties", _CimSystemProperties)); + + Queue bases = new Queue(); + foreach (var b in typeAst.BaseTypes) + { + bases.Enqueue(b); + } + + if (bases.Count > 0) + { + WriteVerbose(string.Format("BaseTypes count for type {0} is {1} and not implemented yet", className, bases.Count)); + } + + return new PSObject[] {result}; + } + + /// + /// Gets the line no for DSC Class Resource Get/Set/Test methods. + /// + /// + /// + private static bool GetResourceMethodsLineNumber(TypeDefinitionAst typeDefinitionAst, out Dictionary methodsLinePosition) + { + const string getMethodName = "Get"; + const string setMethodName = "Set"; + const string testMethodName = "Test"; + + methodsLinePosition = new Dictionary(); + foreach (var member in typeDefinitionAst.Members) + { + var functionMemberAst = member as FunctionMemberAst; + if (functionMemberAst != null) + { + if (functionMemberAst.Name.Equals(getMethodName, StringComparison.OrdinalIgnoreCase)) + { + methodsLinePosition[getMethodName] = functionMemberAst.NameExtent.StartLineNumber; + } + else if (functionMemberAst.Name.Equals(setMethodName, StringComparison.OrdinalIgnoreCase)) + { + methodsLinePosition[setMethodName] = functionMemberAst.NameExtent.StartLineNumber; + } + else if (functionMemberAst.Name.Equals(testMethodName, StringComparison.OrdinalIgnoreCase)) + { + methodsLinePosition[testMethodName] = functionMemberAst.NameExtent.StartLineNumber; + } + } + } + + // All 3 methods (Get/Set/Test) position should be found. + return (methodsLinePosition.Count == 3); + } + + /// + /// Gets the line no for DSC Class Resource Get/Set/Test methods. + /// + /// + /// + /// + /// + public static bool GetResourceMethodsLinePosition(PSModuleInfo moduleInfo, string resourceName, out Dictionary resourceMethodsLinePosition, out string resourceFilePath) + { + resourceMethodsLinePosition = null; + resourceFilePath = string.Empty; + if (moduleInfo == null || string.IsNullOrEmpty(resourceName)) + { + return false; + } + + IEnumerable resourceDefinitions; + List moduleFiles = new List(); + if (moduleInfo.RootModule != null) + { + moduleFiles.Add(moduleInfo.Path); + } + + if (moduleInfo.NestedModules != null) + { + foreach (var nestedModule in moduleInfo.NestedModules.Where(m => !string.IsNullOrEmpty(m.Path))) + { + moduleFiles.Add(nestedModule.Path); + } + } + + foreach (string moduleFile in moduleFiles) + { + if (GetResourceDefinitionsFromModule(moduleFile, out resourceDefinitions, null, null)) + { + foreach (var r in resourceDefinitions) + { + var resourceDefnAst = (TypeDefinitionAst)r; + if (!resourceName.Equals(resourceDefnAst.Name, StringComparison.OrdinalIgnoreCase)) + { + continue; + } + + if (GetResourceMethodsLineNumber(resourceDefnAst, out resourceMethodsLinePosition)) + { + resourceFilePath = moduleFile; + return true; + } + } + } + } + + return false; + } + + /*private static void ProcessMembers(StringBuilder sb, List embeddedInstanceTypes, TypeDefinitionAst typeDefinitionAst, string className) + { + foreach (var member in typeDefinitionAst.Members) + { + var property = member as PropertyMemberAst; + + if (property == null || property.IsStatic || + property.Attributes.All(a => a.TypeName.GetReflectionAttributeType() != typeof(DscPropertyAttribute))) + { + continue; + } + + var memberType = property.PropertyType == null + ? typeof(object) + : property.PropertyType.TypeName.GetReflectionType(); + + var attributes = new List(); + for (int i = 0; i < property.Attributes.Count; i++) + { + attributes.Add(property.Attributes[i].GetAttribute()); + } + + string mofType; + bool isArrayType; + string embeddedInstanceType; + string[] enumNames = null; + + if (memberType != null) + { + // TODO - validate type and name + mofType = MapTypeToMofType(memberType, member.Name, className, out isArrayType, + out embeddedInstanceType, + embeddedInstanceTypes); + if (memberType.IsEnum) + { + enumNames = Enum.GetNames(memberType); + } + } + else + { + // PropertyType can't be null, we used typeof(object) above in that case so we don't get here. + mofType = MapTypeNameToMofType(property.PropertyType.TypeName, member.Name, className, + out isArrayType, + out embeddedInstanceType, embeddedInstanceTypes, ref enumNames); + } + + string arrayAffix = isArrayType ? "[]" : string.Empty; + + sb.AppendFormat(CultureInfo.InvariantCulture, + " {0}{1} {2}{3};\n", + MapAttributesToMof(enumNames, attributes, embeddedInstanceType), + mofType, + member.Name, + arrayAffix); + } + }*/ + + private static List ProcessMembers(StringBuilder sb, List embeddedInstanceTypes, TypeDefinitionAst typeDefinitionAst, string className) + { + List result = new List(); + + foreach (var member in typeDefinitionAst.Members) + { + var property = member as PropertyMemberAst; + + if (property == null || property.IsStatic || + property.Attributes.All(a => a.TypeName.GetReflectionAttributeType() != typeof(DscPropertyAttribute))) + { + continue; + } + + var memberType = property.PropertyType == null + ? typeof(object) + : property.PropertyType.TypeName.GetReflectionType(); + + var attributes = new List(); + for (int i = 0; i < property.Attributes.Count; i++) + { + attributes.Add(property.Attributes[i].GetAttribute()); + } + + string mofType; + bool isArrayType; + string embeddedInstanceType; + string[] enumNames = null; + + if (memberType != null) + { + // TODO - validate type and name + mofType = MapTypeToMofType(memberType, member.Name, className, out isArrayType, + out embeddedInstanceType, + embeddedInstanceTypes); + if (memberType.IsEnum) + { + enumNames = Enum.GetNames(memberType); + } + } + else + { + // PropertyType can't be null, we used typeof(object) above in that case so we don't get here. + mofType = MapTypeNameToMofType(property.PropertyType.TypeName, member.Name, className, + out isArrayType, + out embeddedInstanceType, embeddedInstanceTypes, ref enumNames); + } + + string arrayAffix = isArrayType ? "[]" : string.Empty; + + sb.AppendFormat(CultureInfo.InvariantCulture, + " {0}{1} {2}{3};\n", + MapAttributesToMof(enumNames, attributes, embeddedInstanceType), + mofType, + member.Name, + arrayAffix); + + var propertyObject = new PSObject(); + propertyObject.Properties.Add(new PSNoteProperty(@"Name", member.Name)); + propertyObject.Properties.Add(new PSNoteProperty(@"Value", "null")); + propertyObject.Properties.Add(new PSNoteProperty(@"CimType", mofType + (isArrayType ? "Array" : ""))); + //TODO-AM: fill-in rest of attributes + propertyObject.Properties.Add(new PSNoteProperty(@"Flags", "Property, NullValue")); + propertyObject.Properties.Add(new PSNoteProperty(@"Qualifiers", new PSObject[0])); + + result.Add(propertyObject); + /* + { + "Name": "Path", + "Value": null, + "CimType": "String", + "Flags": "Property, Key, NullValue", + "Qualifiers": [ + { + "Name": "Key", + "Value": true, + "CimType": "Boolean", + "Flags": "DisableOverride, ToSubclass" + } + ], + "ReferenceClassName": null + } + */ + } + + return result; + } + + /// + /// + /// + /// + /// + /// + /// + private static bool GetResourceDefinitionsFromModule(string fileName, out IEnumerable resourceDefinitions, List errorList, IScriptExtent extent) + { + resourceDefinitions = null; + + if (string.IsNullOrEmpty(fileName)) + { + return false; + } + + if (!".psm1".Equals(Path.GetExtension(fileName), StringComparison.OrdinalIgnoreCase) && + !".ps1".Equals(Path.GetExtension(fileName), StringComparison.OrdinalIgnoreCase)) + { + return false; + } + + // If script dynamic keywords has already been loaded from the file, don't load them again. + // The ScriptKeywordFile cache is always initialized from scratch by the top-level + // configuration statement so within a single compile, things shouldn't change. + if (!File.Exists(fileName) || ScriptKeywordFileCache.Contains(fileName)) + { + return false; + } + + // BUGBUG - need to fix up how the module gets set. + Token[] tokens; + ParseError[] errors; + var ast = Parser.ParseFile(fileName, out tokens, out errors); + + if (errors != null && errors.Length > 0) + { + if (errorList != null && extent != null) + { + List errorMessages = new List(); + foreach (var error in errors) + { + errorMessages.Add(error.ToString()); + } + + errorList.Add(new ParseError(extent, "FailToParseModuleScriptFile", + string.Format(CultureInfo.CurrentCulture, ParserStrings.FailToParseModuleScriptFile, fileName, string.Join(Environment.NewLine, errorMessages)))); + } + + return false; + } + + resourceDefinitions = ast.FindAll(n => + { + var typeAst = n as TypeDefinitionAst; + if (typeAst != null) + { + for (int i = 0; i < typeAst.Attributes.Count; i++) + { + var a = typeAst.Attributes[i]; + if (a.TypeName.GetReflectionAttributeType() == typeof(DscResourceAttribute)) return true; + } + } + + return false; + }, false); + + return true; + } + + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + private static bool ImportKeywordsFromScriptFile(string fileName, PSModuleInfo module, ICollection resourcesToImport, ICollection resourcesFound, Dictionary functionsToDefine, List errorList, IScriptExtent extent) + { + IEnumerable resourceDefinitions; + if (!GetResourceDefinitionsFromModule(fileName, out resourceDefinitions, errorList, extent)) + { + return false; + } + + var result = false; + var parser = new CimDSCParser(); + + const WildcardOptions wildcardOptions = WildcardOptions.IgnoreCase | WildcardOptions.CultureInvariant; + IEnumerable patternList = SessionStateUtilities.CreateWildcardsFromStrings(module._declaredDscResourceExports, wildcardOptions); + + foreach (var r in resourceDefinitions) + { + result = true; + var resourceDefnAst = (TypeDefinitionAst)r; + + if (!SessionStateUtilities.MatchesAnyWildcardPattern(resourceDefnAst.Name, patternList, true)) + { + continue; + } + + bool skip = true; + foreach (var toImport in resourcesToImport) + { + if ((WildcardPattern.Get(toImport, WildcardOptions.IgnoreCase)).IsMatch(resourceDefnAst.Name)) + { + skip = false; + break; + } + } + + if (skip) continue; + + // Parse the Resource Attribute to see if RunAs behavior is specified for the resource. + DSCResourceRunAsCredential runAsBehavior = DSCResourceRunAsCredential.Default; + foreach (var attr in resourceDefnAst.Attributes) + { + if (attr.TypeName.GetReflectionAttributeType() == typeof(DscResourceAttribute)) + { + foreach (var na in attr.NamedArguments) + { + if (na.ArgumentName.Equals("RunAsCredential", StringComparison.OrdinalIgnoreCase)) + { + var dscResourceAttribute = attr.GetAttribute() as DscResourceAttribute; + if (dscResourceAttribute != null) + { + runAsBehavior = dscResourceAttribute.RunAsCredential; + } + } + } + } + } + + var classes = GenerateJsonForAst(resourceDefnAst); + + ProcessJsonForDynamicKeywords(module, resourcesFound, functionsToDefine, classes, runAsBehavior); + } + + return result; + } + + private static readonly Dictionary s_mapPrimitiveDotNetTypeToMof = new Dictionary() + { + { typeof(sbyte), "sint8" }, + { typeof(byte) , "uint8"}, + { typeof(short) , "sint16"}, + { typeof(ushort) , "uint16"}, + { typeof(int) , "sint32"}, + { typeof(uint) , "uint32"}, + { typeof(long) , "sint64"}, + { typeof(ulong), "uint64" }, + { typeof(float) , "real32"}, + { typeof(double) , "real64"}, + { typeof(bool) , "boolean"}, + { typeof(string), "string" }, + { typeof(DateTime), "datetime" }, + { typeof(PSCredential), "string" }, + { typeof(char), "char16" }, + }; + + /*TODO-AM: private static bool AreQualifiersSame(CimReadOnlyKeyedCollection oldQualifier, CimReadOnlyKeyedCollection newQualifiers) + { + if (oldQualifier.Count != newQualifiers.Count) + { + return false; + } + + foreach (var qual in oldQualifier) + { + // Find the qualifier in new class + var newQual = newQualifiers[qual.Name]; + if (newQual == null) + { + return false; + } + + if ((qual.CimType != newQual.CimType) || + (qual.Flags != newQual.Flags)) + { + return false; + } + + if ((qual.Value == null && newQual.Value != null) || + (qual.Value != null && newQual.Value == null) || + (qual.Value != null && newQual.Value != null && + !string.Equals(qual.Value.ToString(), newQual.Value.ToString(), StringComparison.OrdinalIgnoreCase) + ) + ) + { + return false; + } + } + + return true; + } + + private static bool ArePropertiesSame(CimReadOnlyKeyedCollection oldProperties, CimReadOnlyKeyedCollection newProperties) + { + if (oldProperties.Count != newProperties.Count) + { + return false; + } + + foreach (var prop in oldProperties) + { + // Find the property in new class + var newProp = newProperties[prop.Name]; + if (newProp == null) + { + return false; + } + // flags and type should match + if ((prop.CimType != newProp.CimType) || + (prop.Flags != newProp.Flags)) + { + return false; + } + + if (!AreQualifiersSame(prop.Qualifiers, newProp.Qualifiers)) + { + return false; + } + } + + return true; + }*/ + + private static bool IsSameNestedObject(dynamic oldClass, dynamic newClass) + { + // #1 both the classes should be nested class and not DSC resource + if ((oldClass.CimSuperClassName != null && string.Equals("OMI_BaseResource", oldClass.CimSuperClassName, StringComparison.OrdinalIgnoreCase)) || + (newClass.CimSuperClassName != null && string.Equals("OMI_BaseResource", newClass.CimSuperClassName, StringComparison.OrdinalIgnoreCase))) + { + return false; + } + // #2 qualifier count, names, values and types should be same + /*TODO-AM: if (!AreQualifiersSame(oldClass.CimClassQualifiers, newClass.CimClassQualifiers)) + { + return false; + } + + // #3 property count, names, values, qualifiers and types should be same + if (!ArePropertiesSame(oldClass.CimClassProperties, newClass.CimClassProperties)) + { + return false; + }*/ + + return true; + } + + internal static string MapTypeToMofType(Type type, string memberName, string className, out bool isArrayType, out string embeddedInstanceType, List embeddedInstanceTypes) + { + isArrayType = false; + if (type.IsValueType) + { + type = Nullable.GetUnderlyingType(type) ?? type; + } + + if (type.IsEnum) + { + embeddedInstanceType = null; + return "string"; + } + + if (type == typeof(Hashtable)) + { + // Hashtable is obviously not an array, but in the mof, we represent + // it as string[] (really, embeddedinstance of MSFT_KeyValuePair), but + // we need an array to hold each entry in the hashtable. + isArrayType = true; + embeddedInstanceType = "MSFT_KeyValuePair"; + return "string"; + } + + if (type == typeof(PSCredential)) + { + embeddedInstanceType = "MSFT_Credential"; + return "string"; + } + + if (type.IsArray) + { + isArrayType = true; + bool temp; + var elementType = type.GetElementType(); + if (!elementType.IsArray) + return MapTypeToMofType(type.GetElementType(), memberName, className, out temp, out embeddedInstanceType, embeddedInstanceTypes); + } + else + { + string cimType; + if (s_mapPrimitiveDotNetTypeToMof.TryGetValue(type, out cimType)) + { + embeddedInstanceType = null; + return cimType; + } + } + + bool supported = false; + bool missingDefaultConstructor = false; + if (type.IsValueType) + { + if (s_mapPrimitiveDotNetTypeToMof.ContainsKey(type)) + { + supported = true; + } + } + else if (!type.IsAbstract) + { + // Must have default constructor, at least 1 public property/field, and no base classes + if (type.GetConstructor(Type.EmptyTypes) == null) + { + missingDefaultConstructor = true; + } + else if (type.BaseType == typeof(object) && + (type.GetProperties(BindingFlags.Instance | BindingFlags.Public).Length > 0 || + type.GetFields(BindingFlags.Instance | BindingFlags.Public).Length > 0)) + { + supported = true; + } + } + + if (supported) + { + if (!embeddedInstanceTypes.Contains(type)) + { + embeddedInstanceTypes.Add(type); + } + + // The type is obviously not a string, but in the mof, we represent + // it as string (really, embeddedinstance of the class type) + embeddedInstanceType = type.FullName.Replace('.', '_'); + return "string"; + } + + if (missingDefaultConstructor) + { + throw new NotSupportedException(string.Format( + CultureInfo.InvariantCulture, + ParserStrings.DscResourceMissingDefaultConstructor, + type.Name)); + } + else + { + throw new NotSupportedException(string.Format( + CultureInfo.InvariantCulture, + ParserStrings.UnsupportedPropertyTypeOfDSCResourceClass, + memberName, + type.Name, + className)); + } + } + + private static string MapAttributesToMof(string[] enumNames, IEnumerable customAttributes, string embeddedInstanceType) + { + var sb = new StringBuilder(); + + sb.Append("["); + bool needComma = false; + foreach (var attr in customAttributes) + { + var dscProperty = attr as DscPropertyAttribute; + if (dscProperty != null) + { + if (dscProperty.Key) + { + sb.AppendFormat(CultureInfo.InvariantCulture, "{0}key", needComma ? ", " : string.Empty); + needComma = true; + } + + if (dscProperty.Mandatory) + { + sb.AppendFormat(CultureInfo.InvariantCulture, "{0}required", needComma ? ", " : string.Empty); + needComma = true; + } + + if (dscProperty.NotConfigurable) + { + sb.AppendFormat(CultureInfo.InvariantCulture, "{0}read", needComma ? ", " : string.Empty); + needComma = true; + } + + continue; + } + + var validateSet = attr as ValidateSetAttribute; + if (validateSet != null) + { + bool valueMapComma = false; + StringBuilder sbValues = new StringBuilder(", Values{"); + sb.AppendFormat(CultureInfo.InvariantCulture, "{0}ValueMap{{", needComma ? ", " : string.Empty); + needComma = true; + + foreach (var value in validateSet.ValidValues) + { + sb.AppendFormat(CultureInfo.InvariantCulture, "{0}\"{1}\"", valueMapComma ? ", " : string.Empty, value); + sbValues.AppendFormat(CultureInfo.InvariantCulture, "{0}\"{1}\"", valueMapComma ? ", " : string.Empty, value); + valueMapComma = true; + } + + sb.Append("}"); + sb.Append(sbValues); + sb.Append("}"); + } + } + + // Default is write - skipped if we already have some attributes + if (sb.Length == 1) + { + sb.Append("write"); + needComma = true; + } + + if (enumNames != null) + { + sb.AppendFormat(CultureInfo.InvariantCulture, "{0}ValueMap{{", needComma ? ", " : string.Empty); + needComma = false; + foreach (var name in enumNames) + { + sb.AppendFormat(CultureInfo.InvariantCulture, "{0}\"{1}\"", needComma ? ", " : string.Empty, name); + needComma = true; + } + + sb.Append("}, Values{"); + needComma = false; + foreach (var name in enumNames) + { + sb.AppendFormat(CultureInfo.InvariantCulture, "{0}\"{1}\"", needComma ? ", " : string.Empty, name); + needComma = true; + } + + sb.Append("}"); + } + else if (embeddedInstanceType != null) + { + sb.AppendFormat(CultureInfo.InvariantCulture, "{0}EmbeddedInstance(\"{1}\")", needComma ? ", " : string.Empty, embeddedInstanceType); + } + + sb.Append("]"); + return sb.ToString(); + } + + /// + /// + /// + /// + public static string GenerateMofForType(Type type) + { + var embeddedInstanceTypes = new List(); + var sb = new StringBuilder(); + + GenerateMofForType(type, sb, embeddedInstanceTypes); + var visitedInstances = new List(); + visitedInstances.Add(type); + ProcessEmbeddedInstanceTypes(embeddedInstanceTypes, visitedInstances, sb); + + return sb.ToString(); + } + + private static void ProcessEmbeddedInstanceTypes(List embeddedInstanceTypes, List visitedInstances, StringBuilder sb) + { + StringBuilder nestedSb = null; + + while (embeddedInstanceTypes.Count > 0) + { + if (nestedSb == null) + { + nestedSb = new StringBuilder(); + } + else + { + nestedSb.Clear(); + } + + var batchedTypes = embeddedInstanceTypes.Where(x => !visitedInstances.Contains(x)).ToArray(); + embeddedInstanceTypes.Clear(); + + for (int i = batchedTypes.Length - 1; i >= 0; i--) + { + visitedInstances.Add(batchedTypes[i]); + var type = batchedTypes[i] as Type; + if (type != null) + { + GenerateMofForType(type, nestedSb, embeddedInstanceTypes); + } + else + { + GenerateJsonForAst((TypeDefinitionAst)batchedTypes[i], nestedSb, embeddedInstanceTypes); + } + + nestedSb.Append('\n'); + } + + sb.Insert(0, nestedSb.ToString()); + } + } + + private static void GenerateMofForType(Type type, StringBuilder sb, List embeddedInstanceTypes) + { + var className = type.Name; + // Friendly name is required by module validator to verify resource instance against the exclusive resource name list. + sb.AppendFormat(CultureInfo.InvariantCulture, "[ClassVersion(\"1.0.0\"), FriendlyName(\"{0}\")]\nclass {0}", className); + + if (type.GetCustomAttributes().Any()) + { + sb.Append(" : OMI_BaseResource"); + } + + sb.Append("\n{\n"); + + ProcessMembers(type, sb, embeddedInstanceTypes, className); + sb.Append("};"); + } + + private static void ProcessMembers(Type type, StringBuilder sb, List embeddedInstanceTypes, string className) + { + foreach (var member in type.GetMembers(BindingFlags.Instance | BindingFlags.Public).Where(m => m is PropertyInfo || m is FieldInfo)) + { + if (member.CustomAttributes.All(cad => cad.AttributeType != typeof(DscPropertyAttribute))) + { + continue; + } + + Type memberType; + var propertyInfo = member as PropertyInfo; + if (propertyInfo == null) + { + var fieldInfo = (FieldInfo)member; + memberType = fieldInfo.FieldType; + } + else + { + if (propertyInfo.GetSetMethod() == null) + { + continue; + } + + memberType = propertyInfo.PropertyType; + } + + // TODO - validate type and name + bool isArrayType; + string embeddedInstanceType; + string mofType = MapTypeToMofType(memberType, member.Name, className, out isArrayType, out embeddedInstanceType, + embeddedInstanceTypes); + string arrayAffix = isArrayType ? "[]" : string.Empty; + + var enumNames = memberType.IsEnum + ? Enum.GetNames(memberType) + : null; + sb.AppendFormat(CultureInfo.InvariantCulture, + " {0}{1} {2}{3};\n", + MapAttributesToMof(enumNames, member.GetCustomAttributes(true), embeddedInstanceType), + mofType, + member.Name, + arrayAffix); + } + } + + private static bool ImportKeywordsFromAssembly(PSModuleInfo module, + ICollection resourcesToImport, + ICollection resourcesFound, + Dictionary functionsToDefine, + Assembly assembly) + { + bool result = false; + + var parser = new Microsoft.PowerShell.DesiredStateConfiguration.CimDSCParser(); + + IEnumerable resourceDefinitions = + assembly.GetTypes().Where(t => t.GetCustomAttributes().Any()); + + foreach (var r in resourceDefinitions) + { + result = true; + bool skip = true; + + foreach (var toImport in resourcesToImport) + { + if ((WildcardPattern.Get(toImport, WildcardOptions.IgnoreCase)).IsMatch(r.Name)) + { + skip = false; + break; + } + } + + if (skip) continue; + + var mof = GenerateMofForType(r); + + /*TODO-AM: update and re-enable + ProcessJsonForDynamicKeywords(module, resourcesFound, functionsToDefine, parser, mof, DSCResourceRunAsCredential.Default);*/ + } + + return result; + } + + private static void ProcessJsonForDynamicKeywords(PSModuleInfo module, ICollection resourcesFound, + Dictionary functionsToDefine, PSObject[] classes, DSCResourceRunAsCredential runAsBehavior) + { + foreach (dynamic c in classes) + { + var className = c.CimSystemProperties.ClassName; + string alias = GetFriendlyName(c); + var friendlyName = string.IsNullOrEmpty(alias) ? className : alias; + if (!CacheResourcesFromMultipleModuleVersions) + { + // Find & remove the previous version of the resource. + List> resourceList = FindResourceInCache(module.Name, className, friendlyName); + + if (resourceList.Count > 0 && !string.IsNullOrEmpty(resourceList[0].Key)) + { + ClassCache.Remove(resourceList[0].Key); + + // keyword is already defined and it is a Inbox resource, remove it + if (DynamicKeyword.ContainsKeyword(friendlyName) && resourceList[0].Value.IsImportedImplicitly) + { + DynamicKeyword.RemoveKeyword(friendlyName); + } + } + } + + var moduleQualifiedResourceName = GetModuleQualifiedResourceName(module.Name, module.Version.ToString(), className, friendlyName); + ClassCache[moduleQualifiedResourceName] = new DscClassCacheEntry(runAsBehavior, false, c); + ByClassModuleCache[className] = new Tuple(module.Name, module.Version); + resourcesFound.Add(className); + CreateAndRegisterKeywordFromCimClass(module.Name, module.Version, c, functionsToDefine, runAsBehavior); + } + } + + /// + /// Import the CIM functions from a module... + /// + /// + /// + /// Full path of the loaded schema file... + /// + public static bool ImportCimKeywordsFromModule(PSModuleInfo module, string resourceName, out string schemaFilePath) + { + return ImportCimKeywordsFromModule(module, resourceName, out schemaFilePath, null); + } + + /// + /// Import the CIM functions from a module... + /// + /// + /// + /// Full path of the loaded schema file... + /// + /// + public static bool ImportCimKeywordsFromModule(PSModuleInfo module, string resourceName, out string schemaFilePath, Dictionary functionsToDefine) + { + return ImportCimKeywordsFromModule(module, resourceName, out schemaFilePath, functionsToDefine, null); + } + + /// + /// Import the CIM functions from a module... + /// + /// + /// + /// Full path of the loaded schema file... + /// + /// Error reported during deserialization. + /// + public static bool ImportCimKeywordsFromModule(PSModuleInfo module, string resourceName, out string schemaFilePath, Dictionary functionsToDefine, Collection errors) + { + if (module == null) + { + throw PSTraceSource.NewArgumentNullException(nameof(module)); + } + + if (resourceName == null) + { + throw PSTraceSource.NewArgumentNullException(nameof(resourceName)); + } + + string dscResourcesPath = Path.Combine(module.ModuleBase, "DscResources"); + schemaFilePath = Path.Combine(Path.Combine(dscResourcesPath, resourceName), resourceName + ".schema.json"); + + if (File.Exists(schemaFilePath)) + { + // If the file has already been loaded, don't load it again. + // The class cache is always initialized from scratch by the top-level + // configuration statement so within a single compile, things shouldn't + // change. + var classes = GetCachedClassByFileName(schemaFilePath) ?? ImportClasses(schemaFilePath, new Tuple(module.Name, module.Version), errors); + if (classes != null) + { + foreach (var c in classes) + { + CreateAndRegisterKeywordFromCimClass(module.Name, module.Version, c, functionsToDefine, DSCResourceRunAsCredential.Default); + ClearImplicitlyImportedFlagFromResourceInClassCache(module, c); + } + } + + return true; + } + else if (Directory.Exists(dscResourcesPath)) + { + // + // Cannot find the schema file, then resourceName may be a friendly name, + // try to search all DscResources' schemas under DscResources folder + // + try + { + var dscResourceDirectories = Directory.GetDirectories(dscResourcesPath); + foreach (var directory in dscResourceDirectories) + { + var schemaFiles = Directory.GetFiles(directory, "*.schema.json", SearchOption.TopDirectoryOnly); + if (schemaFiles.Length > 0) + { + Debug.Assert(schemaFiles.Length == 1, "A valid DSCResource module can have only one schema mof file"); + var tempSchemaFilepath = schemaFiles[0]; + var classes = GetCachedClassByFileName(tempSchemaFilepath) ?? ImportClasses(tempSchemaFilepath, new Tuple(module.Name, module.Version), errors); + if (classes != null) + { + // + // search if class's friendly name is the given resourceName + // + foreach (var c in classes) + { + var alias = GetFriendlyName(c); + if (string.Equals(alias, resourceName, StringComparison.OrdinalIgnoreCase)) + { + CreateAndRegisterKeywordFromCimClass(module.Name, module.Version, c, functionsToDefine, DSCResourceRunAsCredential.Default); + ClearImplicitlyImportedFlagFromResourceInClassCache(module, c); + return true; + } + } + } + } + } + } + catch (Exception) + { + // + // silent in case of exception + // + } + } + + return false; + } + + /// + /// Clear the 'IsImportedImplicitly' flag when explicitly importing a resource. + /// + /// + /// + private static void ClearImplicitlyImportedFlagFromResourceInClassCache(PSModuleInfo module, dynamic cimClass) + { + var className = cimClass.CimSystemProperties.ClassName; + var alias = GetFriendlyName(cimClass); + var friendlyName = string.IsNullOrEmpty(alias) ? className : alias; + var moduleQualifiedResourceName = GetModuleQualifiedResourceName(module.Name, module.Version.ToString(), className, friendlyName); + ClassCache[moduleQualifiedResourceName].IsImportedImplicitly = false; + } + + /// + /// Imports configuration keywords from a .psm1 file. + /// + /// + /// + /// + public static bool ImportScriptKeywordsFromModule(PSModuleInfo module, string resourceName, out string schemaFilePath) + { + return ImportScriptKeywordsFromModule(module, resourceName, out schemaFilePath, null); + } + + /// + /// Imports configuration keywords from a .psm1 file. + /// + /// + /// + /// + /// + public static bool ImportScriptKeywordsFromModule(PSModuleInfo module, string resourceName, out string schemaFilePath, Dictionary functionsToDefine) + { + if (module == null) + { + throw PSTraceSource.NewArgumentNullException(nameof(module)); + } + + if (resourceName == null) + { + throw PSTraceSource.NewArgumentNullException(nameof(resourceName)); + } + + schemaFilePath = Path.Combine(Path.Combine(Path.Combine(module.ModuleBase, "DscResources"), resourceName), resourceName + ".Schema.psm1"); + + if (File.Exists(schemaFilePath) && !s_currentImportingScriptFiles.Contains(schemaFilePath)) + { + // If script dynamic keywords has already been loaded from the file, don't load them again. + // The ScriptKeywordFile cache is always initialized from scratch by the top-level + // configuration statement so within a single compile, things shouldn't change. + if (!ScriptKeywordFileCache.Contains(schemaFilePath)) + { + // Parsing the file is all that needs to be done to add the keywords + // BUGBUG - need to fix up how the module gets set. + // BUGBUG - should fail somehow if errors is not empty + Token[] tokens; ParseError[] errors; + s_currentImportingScriptFiles.Add(schemaFilePath); + Parser.ParseFile(schemaFilePath, out tokens, out errors); + s_currentImportingScriptFiles.Remove(schemaFilePath); + ScriptKeywordFileCache.Add(schemaFilePath); + } + + return true; + } + + return false; + } + + /// + /// Returns an error record to use in the case of a malformed resource reference in the DependsOn list. + /// + /// The malformed resource. + /// The referencing resource instance. + /// + public static ErrorRecord GetBadlyFormedRequiredResourceIdErrorRecord(string badDependsOnReference, string definingResource) + { + PSInvalidOperationException e = PSTraceSource.NewInvalidOperationException( + ParserStrings.GetBadlyFormedRequiredResourceId, badDependsOnReference, definingResource); + + e.SetErrorId("GetBadlyFormedRequiredResourceId"); + return e.ErrorRecord; + } + + /// + /// Returns an error record to use in the case of a malformed resource reference in the exclusive resources list. + /// + /// The malformed resource. + /// The referencing resource instance. + /// + public static ErrorRecord GetBadlyFormedExclusiveResourceIdErrorRecord(string badExclusiveResourcereference, string definingResource) + { + PSInvalidOperationException e = PSTraceSource.NewInvalidOperationException( + ParserStrings.GetBadlyFormedExclusiveResourceId, badExclusiveResourcereference, definingResource); + + e.SetErrorId("GetBadlyFormedExclusiveResourceId"); + return e.ErrorRecord; + } + + /// + /// If a partial configuration is in 'Pull' Mode, it needs a configuration source. + /// + /// + /// + public static ErrorRecord GetPullModeNeedConfigurationSource(string resourceId) + { + PSInvalidOperationException e = PSTraceSource.NewInvalidOperationException( + ParserStrings.GetPullModeNeedConfigurationSource, resourceId); + + e.SetErrorId("GetPullModeNeedConfigurationSource"); + return e.ErrorRecord; + } + + /// + /// Refresh Mode can not be Disabled for the Partial Configurations. + /// + /// + /// + public static ErrorRecord DisabledRefreshModeNotValidForPartialConfig(string resourceId) + { + PSInvalidOperationException e = PSTraceSource.NewInvalidOperationException( + ParserStrings.DisabledRefreshModeNotValidForPartialConfig, resourceId); + + e.SetErrorId("DisabledRefreshModeNotValidForPartialConfig"); + return e.ErrorRecord; + } + + /// + /// Returns an error record to use in the case of a malformed resource reference in the DependsOn list. + /// + /// The duplicate resource identifier. + /// The node being defined. + /// The error record to use. + public static ErrorRecord DuplicateResourceIdInNodeStatementErrorRecord(string duplicateResourceId, string nodeName) + { + PSInvalidOperationException e = PSTraceSource.NewInvalidOperationException( + ParserStrings.DuplicateResourceIdInNodeStatement, duplicateResourceId, nodeName); + + e.SetErrorId("DuplicateResourceIdInNodeStatement"); + return e.ErrorRecord; + } + + /// + /// Returns an error record to use in the case of a configuration name is invalid. + /// + /// + /// + public static ErrorRecord InvalidConfigurationNameErrorRecord(string configurationName) + { + PSInvalidOperationException e = PSTraceSource.NewInvalidOperationException( + ParserStrings.InvalidConfigurationName, configurationName); + + e.SetErrorId("InvalidConfigurationName"); + return e.ErrorRecord; + } + + /// + /// Returns an error record to use in the case of the given value for a property is invalid. + /// + /// + /// + /// + /// + /// + public static ErrorRecord InvalidValueForPropertyErrorRecord(string propertyName, string value, string keywordName, string validValues) + { + PSInvalidOperationException e = PSTraceSource.NewInvalidOperationException( + ParserStrings.InvalidValueForProperty, value, propertyName, keywordName, validValues); + + e.SetErrorId("InvalidValueForProperty"); + return e.ErrorRecord; + } + + /// + /// Returns an error record to use in case the given property is not valid LocalConfigurationManager property. + /// + /// + /// + /// + public static ErrorRecord InvalidLocalConfigurationManagerPropertyErrorRecord(string propertyName, string validProperties) + { + PSInvalidOperationException e = PSTraceSource.NewInvalidOperationException( + ParserStrings.InvalidLocalConfigurationManagerProperty, propertyName, validProperties); + + e.SetErrorId("InvalidLocalConfigurationManagerProperty"); + return e.ErrorRecord; + } + + /// + /// Returns an error record to use in the case of the given value for a property is not supported. + /// + /// + /// + /// + /// + /// + public static ErrorRecord UnsupportedValueForPropertyErrorRecord(string propertyName, string value, string keywordName, string validValues) + { + PSInvalidOperationException e = PSTraceSource.NewInvalidOperationException( + ParserStrings.UnsupportedValueForProperty, value, propertyName, keywordName, validValues); + + e.SetErrorId("UnsupportedValueForProperty"); + return e.ErrorRecord; + } + + /// + /// Returns an error record to use in the case of no value is provided for a mandatory property. + /// + /// + /// + /// + /// + public static ErrorRecord MissingValueForMandatoryPropertyErrorRecord(string keywordName, string typeName, string propertyName) + { + PSInvalidOperationException e = PSTraceSource.NewInvalidOperationException( + ParserStrings.MissingValueForMandatoryProperty, keywordName, typeName, propertyName); + + e.SetErrorId("MissingValueForMandatoryProperty"); + return e.ErrorRecord; + } + + /// + /// Returns an error record to use in the case of more than one values are provided for DebugMode property. + /// + /// + public static ErrorRecord DebugModeShouldHaveOneValue() + { + PSInvalidOperationException e = PSTraceSource.NewInvalidOperationException( + ParserStrings.DebugModeShouldHaveOneValue); + + e.SetErrorId("DebugModeShouldHaveOneValue"); + return e.ErrorRecord; + } + + /// + /// Return an error to indicate a value is out of range for a dynamic keyword property. + /// + /// + /// + /// + /// + /// + /// + public static ErrorRecord ValueNotInRangeErrorRecord(string property, string name, int providedValue, int lower, int upper) + { + PSInvalidOperationException e = PSTraceSource.NewInvalidOperationException( + ParserStrings.ValueNotInRange, property, name, providedValue, lower, upper); + + e.SetErrorId("ValueNotInRange"); + return e.ErrorRecord; + } + + /// + /// Returns an error record to use when composite resource and its resource instances both has PsDscRunAsCredentials value. + /// + /// ResourceId of resource. + /// + public static ErrorRecord PsDscRunAsCredentialMergeErrorForCompositeResources(string resourceId) + { + PSInvalidOperationException e = PSTraceSource.NewInvalidOperationException( + ParserStrings.PsDscRunAsCredentialMergeErrorForCompositeResources, resourceId); + + e.SetErrorId("PsDscRunAsCredentialMergeErrorForCompositeResources"); + return e.ErrorRecord; + } + + /// + /// Routine to format a usage string from keyword. The resulting string should look like: + /// User [string] #ResourceName + /// { + /// UserName = [string] + /// [ Description = [string] ] + /// [ Disabled = [bool] ] + /// [ Ensure = [string] { Absent | Present } ] + /// [ Force = [bool] ] + /// [ FullName = [string] ] + /// [ Password = [PSCredential] ] + /// [ PasswordChangeNotAllowed = [bool] ] + /// [ PasswordChangeRequired = [bool] ] + /// [ PasswordNeverExpires = [bool] ] + /// [ DependsOn = [string[]] ] + /// } + /// + /// + /// + public static string GetDSCResourceUsageString(DynamicKeyword keyword) + { + StringBuilder usageString; + switch (keyword.NameMode) + { + // Name must be present and simple non-empty bare word + case DynamicKeywordNameMode.SimpleNameRequired: + usageString = new StringBuilder(keyword.Keyword + " [string] # Resource Name"); + break; + + // Name must be present but can also be an expression + case DynamicKeywordNameMode.NameRequired: + usageString = new StringBuilder(keyword.Keyword + " [string[]] # Name List"); + break; + + // Name may be optionally present, but if it is present, it must be a non-empty bare word. + case DynamicKeywordNameMode.SimpleOptionalName: + usageString = new StringBuilder(keyword.Keyword + " [ [string] ] # Optional Name"); + break; + + // Name may be optionally present, expression or bare word + case DynamicKeywordNameMode.OptionalName: + usageString = new StringBuilder(keyword.Keyword + " [ [string[]] ] # Optional NameList"); + break; + + // Does not take a name + default: + usageString = new StringBuilder(keyword.Keyword); + break; + } + + usageString.Append("\n{\n"); + + bool listKeyProperties = true; + while (true) + { + foreach (var prop in keyword.Properties.OrderBy(ob => ob.Key)) + { + if (string.Equals(prop.Key, "ResourceId", StringComparison.OrdinalIgnoreCase)) + { + continue; + } + + var propVal = prop.Value; + if (listKeyProperties && propVal.IsKey || !listKeyProperties && !propVal.IsKey) + { + usageString.Append(propVal.Mandatory ? " " : " [ "); + usageString.Append(prop.Key); + usageString.Append(" = "); + usageString.Append(FormatCimPropertyType(propVal, !propVal.Mandatory)); + } + } + + if (listKeyProperties) + { + listKeyProperties = false; + } + else + { + break; + } + } + + usageString.Append("}"); + + return usageString.ToString(); + } + + /// + /// Format the type name of a CIM property in a presentable way. + /// + /// + /// + /// + private static StringBuilder FormatCimPropertyType(DynamicKeywordProperty prop, bool isOptionalProperty) + { + string cimTypeName = prop.TypeConstraint; + StringBuilder formattedTypeString = new StringBuilder(); + + if (string.Equals(cimTypeName, "MSFT_Credential", StringComparison.OrdinalIgnoreCase)) + { + formattedTypeString.Append("[PSCredential]"); + } + else if (string.Equals(cimTypeName, "MSFT_KeyValuePair", StringComparison.OrdinalIgnoreCase) || string.Equals(cimTypeName, "MSFT_KeyValuePair[]", StringComparison.OrdinalIgnoreCase)) + { + formattedTypeString.Append("[Hashtable]"); + } + else + { + string convertedTypeString = System.Management.Automation.LanguagePrimitives.ConvertTypeNameToPSTypeName(cimTypeName); + if (!string.IsNullOrEmpty(convertedTypeString) && !string.Equals(convertedTypeString, "[]", StringComparison.OrdinalIgnoreCase)) + { + formattedTypeString.Append(convertedTypeString); + } + else + { + formattedTypeString.Append("[" + cimTypeName + "]"); + } + } + + // Do the property values map + if (prop.ValueMap != null && prop.ValueMap.Count > 0) + { + formattedTypeString.Append(" { " + string.Join(" | ", prop.ValueMap.Keys.OrderBy(x => x)) + " }"); + } + + // We prepend optional property with "[" so close out it here. This way it is shown with [ ] to indication optional + if (isOptionalProperty) + { + formattedTypeString.Append("]"); + } + + formattedTypeString.Append("\n"); + + return formattedTypeString; + } + + /// + /// The scriptblock that implements the CIM keyword functionality. + /// + private static ScriptBlock CimKeywordImplementationFunction + { + get + { + // The scriptblock cache will handle mutual exclusion + return s_cimKeywordImplementationFunction ?? + (s_cimKeywordImplementationFunction = ScriptBlock.Create(CimKeywordImplementationFunctionText)); + } + } + + private static ScriptBlock s_cimKeywordImplementationFunction; + + private const string CimKeywordImplementationFunctionText = @" + param ( + [Parameter(Mandatory)] + $KeywordData, + [Parameter(Mandatory)] + $Name, + [Parameter(Mandatory)] + [Hashtable] + $Value, + [Parameter(Mandatory)] + $SourceMetadata + ) + +# walk the call stack to get at all of the enclosing configuration resource IDs + $stackedConfigs = @(Get-PSCallStack | + where { ($null -ne $_.InvocationInfo.MyCommand) -and ($_.InvocationInfo.MyCommand.CommandType -eq 'Configuration') }) +# keep all but the top-most + $stackedConfigs = $stackedConfigs[0..(@($stackedConfigs).Length - 2)] +# and build the complex resource ID suffix. + $complexResourceQualifier = ( $stackedConfigs | ForEach-Object { '[' + $_.Command + ']' + $_.InvocationInfo.BoundParameters['InstanceName'] } ) -join '::' + +# +# Utility function used to validate that the DependsOn arguments are well-formed. +# The function also adds them to the define nodes resource collection. +# in the case of resources generated inside a script resource, this routine +# will also fix up the DependsOn references to '[Type]Instance::[OuterType]::OuterInstance +# + function Test-DependsOn + { + +# make sure the references are well-formed + $updatedDependsOn = foreach ($DependsOnVar in $value['DependsOn']) { +# match [ResourceType]ResourceName. ResourceName should starts with [a-z_0-9] followed by [a-z_0-9\p{Zs}\.\\-]* + if ($DependsOnVar -notmatch '^\[[a-z]\w*\][a-z_0-9][a-z_0-9\p{Zs}\.\\-]*$') + { + Update-ConfigurationErrorCount + Write-Error -ErrorRecord ([Microsoft.PowerShell.DesiredStateConfiguration.Internal.DscClassCache]::GetBadlyFormedRequiredResourceIdErrorRecord($DependsOnVar, $resourceId)) + } + +# Fix up DependsOn for nested names + if ($MyTypeName -and $typeName -ne $MyTypeName -and $InstanceName) + { + ""$DependsOnVar::$complexResourceQualifier"" + } + else + { + $DependsOnVar + } + } + + $value['DependsOn']= $updatedDependsOn + + if($null -ne $DependsOn) + { +# +# Combine DependsOn with dependson from outer composite resource +# which is set as local variable $DependsOn at the composite resource context +# + $value['DependsOn']= @($value['DependsOn']) + $DependsOn + } + +# Save the resource id in a per-node dictionary to do cross validation at the end + Set-NodeResources $resourceId @( $value['DependsOn']) + +# Remove depends on because it need to be fixed up for composite resources +# We do it in ValidateNodeResource and Update-Depends on in configuration/Node function + $value.Remove('DependsOn') + } + +# A copy of the value object with correctly-cased property names + $canonicalizedValue = @{} + + $typeName = $keywordData.ResourceName # CIM type + $keywordName = $keywordData.Keyword # user-friendly alias that is used in scripts + $keyValues = '' + $debugPrefix = "" ${TypeName}:"" # set up a debug prefix string that makes it easier to track what's happening. + + Write-Debug ""${debugPrefix} RESOURCE PROCESSING STARTED [KeywordName='$keywordName'] Function='$($myinvocation.Invocationname)']"" + +# Check whether it's an old style metaconfig + $OldMetaConfig = $false + if ((-not $IsMetaConfig) -and ($keywordName -ieq 'LocalConfigurationManager')) { + $OldMetaConfig = $true + } + +# Check to see if it's a resource keyword. If so add the meta-properties to the canonical property collection. + $resourceId = $null +# todo: need to include configuration managers and partial configuration + if (($keywordData.Properties.Keys -contains 'DependsOn') -or (($KeywordData.ImplementingModule -ieq 'PSDesiredStateConfigurationEngine') -and ($KeywordData.NameMode -eq [System.Management.Automation.Language.DynamicKeywordNameMode]::NameRequired))) + { + + $resourceId = ""[$keywordName]$name"" + if ($MyTypeName -and $keywordName -ne $MyTypeName -and $InstanceName) + { + $resourceId += ""::$complexResourceQualifier"" + } + + Write-Debug ""${debugPrefix} ResourceID = $resourceId"" + +# copy the meta-properties + $canonicalizedValue['ResourceID'] = $resourceId + $canonicalizedValue['SourceInfo'] = $SourceMetadata + if(-not $IsMetaConfig) + { + $canonicalizedValue['ModuleName'] = $keywordData.ImplementingModule + $canonicalizedValue['ModuleVersion'] = $keywordData.ImplementingModuleVersion -as [string] + } + +# see if there is already a resource with this ID. + if (Test-NodeResources $resourceId) + { + Update-ConfigurationErrorCount + Write-Error -ErrorRecord ([Microsoft.PowerShell.DesiredStateConfiguration.Internal.DscClassCache]::DuplicateResourceIdInNodeStatementErrorRecord($resourceId, (Get-PSCurrentConfigurationNode))) + } + else + { +# If there are prerequisite resources, validate that the references are well-formed strings +# This routine also adds the resource to the global node resources table. + Test-DependsOn + +# Check if PsDscRunCredential is being specified as Arguments to Configuration + if($null -ne $PsDscRunAsCredential) + { +# Check if resource is also trying to set the value for RunAsCred +# In that case we will generate error during compilation, this is merge error + if($null -ne $value['PsDscRunAsCredential']) + { + Update-ConfigurationErrorCount + Write-Error -ErrorRecord ([Microsoft.PowerShell.DesiredStateConfiguration.Internal.DscClassCache]::PsDscRunAsCredentialMergeErrorForCompositeResources($resourceId)) + } +# Set the Value of RunAsCred to that of outer configuration + else + { + $value['PsDscRunAsCredential'] = $PsDscRunAsCredential + } + } + +# Save the resource id in a per-node dictionary to do cross validation at the end + if($keywordData.ImplementingModule -ieq ""PSDesiredStateConfigurationEngine"") + { +#$keywordName is PartialConfiguration + if($keywordName -eq 'PartialConfiguration') + { +# RefreshMode is 'Pull' and .ConfigurationSource is empty + if($value['RefreshMode'] -eq 'Pull' -and -not $value['ConfigurationSource']) + { + Update-ConfigurationErrorCount + Write-Error -ErrorRecord ([Microsoft.PowerShell.DesiredStateConfiguration.Internal.DscClassCache]::GetPullModeNeedConfigurationSource($resourceId)) + } + +# Verify that RefreshMode is not Disabled for Partial configuration + if($value['RefreshMode'] -eq 'Disabled') + { + Update-ConfigurationErrorCount + Write-Error -ErrorRecord ([Microsoft.PowerShell.DesiredStateConfiguration.Internal.DscClassCache]::DisabledRefreshModeNotValidForPartialConfig($resourceId)) + } + + if($null -ne $value['ConfigurationSource']) + { + Set-NodeManager $resourceId $value['ConfigurationSource'] + } + + if($null -ne $value['ResourceModuleSource']) + { + Set-NodeResourceSource $resourceId $value['ResourceModuleSource'] + } + } + + if($null -ne $value['ExclusiveResources']) + { +# make sure the references are well-formed + foreach ($ExclusiveResource in $value['ExclusiveResources']) { + if (($ExclusiveResource -notmatch '^[a-z][a-z_0-9]*\\[a-z][a-z_0-9]*$') -and ($ExclusiveResource -notmatch '^[a-z][a-z_0-9]*$') -and ($ExclusiveResource -notmatch '^[a-z][a-z_0-9]*\\\*$')) + { + Update-ConfigurationErrorCount + Write-Error -ErrorRecord ([Microsoft.PowerShell.DesiredStateConfiguration.Internal.DscClassCache]::GetBadlyFormedExclusiveResourceIdErrorRecord($ExclusiveResource, $resourceId)) + } + } + +# Save the resource id in a per-node dictionary to do cross validation at the end +# Validate resource exist +# Also update the resource reference from module\friendlyname to module\name + $value['ExclusiveResources'] = @(Set-NodeExclusiveResources $resourceId @( $value['ExclusiveResources'] )) + } + } + } + } + else + { + Write-Debug ""${debugPrefix} TYPE IS NOT AS DSC RESOURCE"" + } + +# +# Copy the user-supplied values into a new collection with canonicalized property names +# + foreach ($key in $keywordData.Properties.Keys) + { + Write-Debug ""${debugPrefix} Processing property '$key' ["" + + if ($value.Contains($key)) + { + if ($OldMetaConfig -and (-not ($V1MetaConfigPropertyList -contains $key))) + { + Write-Error -ErrorRecord ([Microsoft.PowerShell.DesiredStateConfiguration.Internal.DscClassCache]::InvalidLocalConfigurationManagerPropertyErrorRecord($key, ($V1MetaConfigPropertyList -join ', '))) + Update-ConfigurationErrorCount + } +# see if there is a list of allowed values for this property (similar to an enum) + $allowedValues = $keywordData.Properties[$key].Values +# If there is and user-provided value is not in that list, write an error. + if ($allowedValues) + { + if(($null -eq $value[$key]) -and ($allowedValues -notcontains $value[$key])) + { + Write-Error -ErrorRecord ([Microsoft.PowerShell.DesiredStateConfiguration.Internal.DscClassCache]::InvalidValueForPropertyErrorRecord($key, ""$($value[$key])"", $keywordData.Keyword, ($allowedValues -join ', '))) + Update-ConfigurationErrorCount + } + else + { + $notAllowedValue=$null + foreach($v in $value[$key]) + { + if($allowedValues -notcontains $v) + { + $notAllowedValue +=$v.ToString() + ', ' + } + } + + if($notAllowedValue) + { + $notAllowedValue = $notAllowedValue.Substring(0, $notAllowedValue.Length -2) + Write-Error -ErrorRecord ([Microsoft.PowerShell.DesiredStateConfiguration.Internal.DscClassCache]::UnsupportedValueForPropertyErrorRecord($key, $notAllowedValue, $keywordData.Keyword, ($allowedValues -join ', '))) + Update-ConfigurationErrorCount + } + } + } + +# see if a value range is defined for this property + $allowedRange = $keywordData.Properties[$key].Range + if($allowedRange) + { + $castedValue = $value[$key] -as [int] + if((($castedValue -is [int]) -and (($castedValue -lt $keywordData.Properties[$key].Range.Item1) -or ($castedValue -gt $keywordData.Properties[$key].Range.Item2))) -or ($null -eq $castedValue)) + { + Write-Error -ErrorRecord ([Microsoft.PowerShell.DesiredStateConfiguration.Internal.DscClassCache]::ValueNotInRangeErrorRecord($key, $keywordName, $value[$key], $keywordData.Properties[$key].Range.Item1, $keywordData.Properties[$key].Range.Item2)) + Update-ConfigurationErrorCount + } + } + + Write-Debug ""${debugPrefix} Canonicalized property '$key' = '$($value[$key])'"" + + if ($keywordData.Properties[$key].IsKey) + { + if($null -eq $value[$key]) + { + $keyValues += ""::__NULL__"" + } + else + { + $keyValues += ""::"" + $value[$key] + } + } + +# see if ValueMap is also defined for this property (actual values) + $allowedValueMap = $keywordData.Properties[$key].ValueMap +#if it is and the ValueMap contains the user-provided value as a key, use the actual value + if ($allowedValueMap -and $allowedValueMap.ContainsKey($value[$key])) + { + $canonicalizedValue[$key] = $allowedValueMap[$value[$key]] + } + else + { + $canonicalizedValue[$key] = $value[$key] + } + } + elseif ($keywordData.Properties[$key].Mandatory) + { +# If the property was mandatory but the user didn't provide a value, write and error. + Write-Error -ErrorRecord ([Microsoft.PowerShell.DesiredStateConfiguration.Internal.DscClassCache]::MissingValueForMandatoryPropertyErrorRecord($keywordData.Keyword, $keywordData.Properties[$key].TypeConstraint, $Key)) + Update-ConfigurationErrorCount + } + + Write-Debug ""${debugPrefix} Processing completed '$key' ]"" + } + + if($keyValues) + { + $keyValues = $keyValues.Substring(2) # Remove the leading '::' + Add-NodeKeys $keyValues $keywordName + Test-ConflictingResources $keywordName $canonicalizedValue $keywordData + } + +# update OMI_ConfigurationDocument + if($IsMetaConfig) + { + if($keywordData.ResourceName -eq 'OMI_ConfigurationDocument') + { + if($(Get-PSMetaConfigurationProcessed)) + { + $PSMetaConfigDocumentInstVersionInfo = Get-PSMetaConfigDocumentInstVersionInfo + $canonicalizedValue['MinimumCompatibleVersion']=$PSMetaConfigDocumentInstVersionInfo['MinimumCompatibleVersion'] + } + else + { + Set-PSMetaConfigDocInsProcessedBeforeMeta + $canonicalizedValue['MinimumCompatibleVersion']='1.0.0' + } + } + + if(($keywordData.ResourceName -eq 'MSFT_WebDownloadManager') ` + -or ($keywordData.ResourceName -eq 'MSFT_FileDownloadManager') ` + -or ($keywordData.ResourceName -eq 'MSFT_WebResourceManager') ` + -or ($keywordData.ResourceName -eq 'MSFT_FileResourceManager') ` + -or ($keywordData.ResourceName -eq 'MSFT_WebReportManager') ` + -or ($keywordData.ResourceName -eq 'MSFT_SignatureValidation') ` + -or ($keywordData.ResourceName -eq 'MSFT_PartialConfiguration')) + { + Set-PSMetaConfigVersionInfoV2 + } + } + elseif($keywordData.ResourceName -eq 'OMI_ConfigurationDocument') + { + $canonicalizedValue['MinimumCompatibleVersion']='1.0.0' + $canonicalizedValue['CompatibleVersionAdditionalProperties']=@('Omi_BaseResource:ConfigurationName') + } + + if(($keywordData.ResourceName -eq 'MSFT_DSCMetaConfiguration') -or ($keywordData.ResourceName -eq 'MSFT_DSCMetaConfigurationV2')) + { + if($canonicalizedValue['DebugMode'] -and @($canonicalizedValue['DebugMode']).Length -gt 1) + { +# we only allow one value for debug mode now. + Write-Error -ErrorRecord ([Microsoft.PowerShell.DesiredStateConfiguration.Internal.DscClassCache]::DebugModeShouldHaveOneValue()) + Update-ConfigurationErrorCount + } + } + +# Generate the MOF text for this resource instance. +# when generate mof text for OMI_ConfigurationDocument we handle below two cases: +# 1. we will add versioning related property based on meta configuration instance already process +# 2. we update the existing OMI_ConfigurationDocument instance if it already exists when process meta configuration instance + $aliasId = ConvertTo-MOFInstance $keywordName $canonicalizedValue + +# If a OMI_ConfigurationDocument is executed outside of a node statement, it becomes the default +# for all nodes that don't have an explicit OMI_ConfigurationDocument declaration + if ($keywordData.ResourceName -eq 'OMI_ConfigurationDocument' -and -not (Get-PSCurrentConfigurationNode)) + { + $data = Get-MoFInstanceText $aliasId + Write-Debug ""${debugPrefix} DEFINING DEFAULT CONFIGURATION DOCUMENT: $data"" + Set-PSDefaultConfigurationDocument $data + } + + Write-Debug ""${debugPrefix} MOF alias for this resource is '$aliasId'"" + +# always return the aliasId so the generated file will be well-formed if not valid + $aliasId + + Write-Debug ""${debugPrefix} RESOURCE PROCESSING COMPLETED. TOTAL ERROR COUNT: $(Get-ConfigurationErrorCount)"" + + "; + } +} diff --git a/src/System.Management.Automation/DscSupport/MofCimDSCParser.cs b/src/System.Management.Automation/DscSupport/MofCimDSCParser.cs new file mode 100755 index 00000000000..64623d55d43 --- /dev/null +++ b/src/System.Management.Automation/DscSupport/MofCimDSCParser.cs @@ -0,0 +1,484 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Globalization; +using System.IO; +using System.Linq; +using System.Management.Automation; +using System.Management.Automation.Language; +using System.Reflection; +using System.Runtime.InteropServices; +using System.Security; +using System.Text; + +using Microsoft.Management.Infrastructure; +using Microsoft.Management.Infrastructure.Generic; +using Microsoft.Management.Infrastructure.Serialization; +using Microsoft.PowerShell.Commands; + +namespace Microsoft.PowerShell.DesiredStateConfiguration.Internal.Mof +{ + /// + /// + [SuppressMessage("Microsoft.MSInternal", "CA903:InternalNamespaceShouldNotContainPublicTypes", + Justification = "Needed Internal use only")] + public static class DscRemoteOperationsClass + { + /// + /// Convert Cim Instance representing Resource desired state to Powershell Class Object. + /// + public static object ConvertCimInstanceToObject(Type targetType, CimInstance instance, string moduleName) + { + var className = instance.CimClass.CimSystemProperties.ClassName; + object targetObject = null; + string errorMessage; + + using (System.Management.Automation.PowerShell powerShell = System.Management.Automation.PowerShell.Create(RunspaceMode.CurrentRunspace)) + { + string script = "param($targetType,$moduleName) & (Microsoft.PowerShell.Core\\Get-Module $moduleName) { New-Object $targetType } "; + + powerShell.AddScript(script); + powerShell.AddArgument(targetType); + powerShell.AddArgument(moduleName); + + Collection psExecutionResult = powerShell.Invoke(); + if (psExecutionResult.Count == 1) + { + targetObject = psExecutionResult[0].BaseObject; + } + else + { + Exception innerException = null; + if (powerShell.Streams.Error != null && powerShell.Streams.Error.Count > 0) + { + innerException = powerShell.Streams.Error[0].Exception; + } + + errorMessage = string.Format(CultureInfo.CurrentCulture, ParserStrings.InstantiatePSClassObjectFailed, className); + var invalidOperationException = new InvalidOperationException(errorMessage, innerException); + throw invalidOperationException; + } + } + + foreach (var property in instance.CimInstanceProperties) + { + if (property.Value != null) + { + MemberInfo[] memberInfo = targetType.GetMember(property.Name, BindingFlags.Public | BindingFlags.Instance); + + // verify property exists in corresponding class type + if (memberInfo == null || memberInfo.Length > 1 || !(memberInfo[0] is PropertyInfo || memberInfo[0] is FieldInfo)) + { + errorMessage = string.Format(CultureInfo.CurrentCulture, ParserStrings.PropertyNotDeclaredInPSClass, new object[] { property.Name, className }); + var invalidOperationException = new InvalidOperationException(errorMessage); + throw invalidOperationException; + } + + var member = memberInfo[0]; + var memberType = (member is FieldInfo) + ? ((FieldInfo)member).FieldType + : ((PropertyInfo)member).PropertyType; + + object targetValue = null; + switch (property.CimType) + { + case Microsoft.Management.Infrastructure.CimType.Instance: + { + var cimPropertyInstance = property.Value as CimInstance; + if (cimPropertyInstance != null && + cimPropertyInstance.CimClass != null && + cimPropertyInstance.CimClass.CimSystemProperties != null && + string.Equals( + cimPropertyInstance.CimClass.CimSystemProperties.ClassName, + "MSFT_Credential", StringComparison.OrdinalIgnoreCase)) + { + targetValue = ConvertCimInstancePsCredential(moduleName, cimPropertyInstance); + } + else + { + targetValue = ConvertCimInstanceToObject(memberType, cimPropertyInstance, moduleName); + } + + if (targetValue == null) + { + return null; + } + } + + break; + case Microsoft.Management.Infrastructure.CimType.InstanceArray: + { + if (memberType == typeof(Hashtable)) + { + targetValue = ConvertCimInstanceHashtable(moduleName, (CimInstance[])property.Value); + } + else + { + var instanceArray = (CimInstance[])property.Value; + if (!memberType.IsArray) + { + errorMessage = string.Format(CultureInfo.CurrentCulture, ParserStrings.ExpectArrayTypeOfPropertyInPSClass, new object[] { property.Name, className }); + var invalidOperationException = new InvalidOperationException(errorMessage); + throw invalidOperationException; + } + + var elementType = memberType.GetElementType(); + var targetArray = Array.CreateInstance(elementType, instanceArray.Length); + for (int i = 0; i < instanceArray.Length; i++) + { + var obj = ConvertCimInstanceToObject(elementType, instanceArray[i], moduleName); + if (obj == null) + { + return null; + } + + targetArray.SetValue(obj, i); + } + + targetValue = targetArray; + } + } + + break; + default: + targetValue = LanguagePrimitives.ConvertTo(property.Value, memberType, CultureInfo.InvariantCulture); + break; + } + + if (targetValue == null) + { + errorMessage = string.Format(CultureInfo.CurrentCulture, ParserStrings.ConvertCimPropertyToObjectPropertyFailed, new object[] { property.Name, className }); + var invalidOperationException = new InvalidOperationException(errorMessage); + throw invalidOperationException; + } + + if (member is FieldInfo) + { + ((FieldInfo)member).SetValue(targetObject, targetValue); + } + + if (member is PropertyInfo) + { + ((PropertyInfo)member).SetValue(targetObject, targetValue); + } + } + } + + return targetObject; + } + + /// + /// Convert hashtable from Ciminstance to hashtable primitive type. + /// + /// + /// + /// + private static object ConvertCimInstanceHashtable(string providerName, CimInstance[] arrayInstance) + { + var result = new Hashtable(); + string errorMessage; + + try + { + foreach (var keyValuePair in arrayInstance) + { + var key = keyValuePair.CimInstanceProperties["Key"]; + var value = keyValuePair.CimInstanceProperties["Value"]; + + if (key == null || value == null) + { + errorMessage = string.Format(CultureInfo.CurrentCulture, ParserStrings.InvalidHashtable, providerName); + var invalidOperationException = new InvalidOperationException(errorMessage); + throw invalidOperationException; + } + + result.Add(LanguagePrimitives.ConvertTo(key.Value), LanguagePrimitives.ConvertTo(value.Value)); + } + } + catch (Exception exception) + { + errorMessage = string.Format(CultureInfo.CurrentCulture, ParserStrings.InvalidHashtable, providerName); + var invalidOperationException = new InvalidOperationException(errorMessage, exception); + throw invalidOperationException; + } + + return result; + } + /// + /// Convert CIM instance to PS Credential. + /// + /// + /// + /// + private static object ConvertCimInstancePsCredential(string providerName, CimInstance propertyInstance) + { + string errorMessage; + string userName; + string plainPassWord; + + try + { + userName = propertyInstance.CimInstanceProperties["UserName"].Value as string; + if (string.IsNullOrEmpty(userName)) + { + errorMessage = string.Format(CultureInfo.CurrentCulture, ParserStrings.InvalidUserName, providerName); + var invalidOperationException = new InvalidOperationException(errorMessage); + throw invalidOperationException; + } + } + catch (CimException exception) + { + errorMessage = string.Format(CultureInfo.CurrentCulture, ParserStrings.InvalidUserName, providerName); + var invalidOperationException = new InvalidOperationException(errorMessage, exception); + throw invalidOperationException; + } + + try + { + plainPassWord = propertyInstance.CimInstanceProperties["PassWord"].Value as string; + + // In future we might receive password in an encrypted format. Make sure we add + // the decryption login in this method. + if (string.IsNullOrEmpty(plainPassWord)) + { + errorMessage = string.Format(CultureInfo.CurrentCulture, ParserStrings.InvalidPassword, providerName); + var invalidOperationException = new InvalidOperationException(errorMessage); + throw invalidOperationException; + } + } + catch (CimException exception) + { + errorMessage = string.Format(CultureInfo.CurrentCulture, ParserStrings.InvalidPassword, providerName); + var invalidOperationException = new InvalidOperationException(errorMessage, exception); + throw invalidOperationException; + } + + // Extract the password into a SecureString. + var password = new SecureString(); + foreach (char t in plainPassWord) + { + password.AppendChar(t); + } + + password.MakeReadOnly(); + return new PSCredential(userName, password); + } + } +} + +namespace Microsoft.PowerShell.DesiredStateConfiguration.Mof +{ + /// + /// To make it easier to specify -ConfigurationData parameter, we add an ArgumentTransformationAttribute here. + /// When the input data is of type string and is valid path to a file that can be converted to hashtable, we do + /// the conversion and return the converted value. Otherwise, we just return the input data. + /// + [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property | AttributeTargets.Parameter, AllowMultiple = false)] + public sealed class ArgumentToConfigurationDataTransformationAttribute : ArgumentTransformationAttribute + { + /// + /// Convert a file of ConfigurationData into a hashtable. + /// + /// + /// + /// + public override object Transform(EngineIntrinsics engineIntrinsics, object inputData) + { + var configDataPath = inputData as string; + if (string.IsNullOrEmpty(configDataPath)) + { + return inputData; + } + + if (engineIntrinsics == null) + { + throw PSTraceSource.NewArgumentNullException(nameof(engineIntrinsics)); + } + + return PsUtils.EvaluatePowerShellDataFileAsModuleManifest( + "ConfigurationData", + configDataPath, + engineIntrinsics.SessionState.Internal.ExecutionContext, + skipPathValidation: false); + } + } + + /// + /// + /// Represents a communication channel to a CIM server. + /// + /// + /// This is the main entry point of the Microsoft.Management.Infrastructure API. + /// All CIM operations are represented as methods of this class. + /// + /// + internal class CimDSCParser + { + private CimMofDeserializer _deserializer; + private CimMofDeserializer.OnClassNeeded _onClassNeeded; + /// + /// + internal CimDSCParser(CimMofDeserializer.OnClassNeeded onClassNeeded) + { + _deserializer = CimMofDeserializer.Create(); + _onClassNeeded = onClassNeeded; + + //TODO-AM: this is for debugging: + _deserializer.SchemaValidationOption = MofDeserializerSchemaValidationOption.Ignore; + } + + /// + /// + internal CimDSCParser(CimMofDeserializer.OnClassNeeded onClassNeeded, Microsoft.Management.Infrastructure.Serialization.MofDeserializerSchemaValidationOption validationOptions) + { + _deserializer = CimMofDeserializer.Create(); + //_deserializer.SchemaValidationOption = validationOptions; + //TODO-AM: this is for debugging: + _deserializer.SchemaValidationOption = MofDeserializerSchemaValidationOption.Ignore; + + _onClassNeeded = onClassNeeded; + } + + /// + /// + /// + /// + [SuppressMessage("Microsoft.Design", "CA1045:DoNotPassTypesByReference", MessageId = "3#", Justification = "Have to return 2 things. Wrapping those 2 things in a class will result in a more, not less complexity")] + internal List ParseInstanceMof(string filePath) + { + uint offset = 0; + var buffer = GetFileContent(filePath); + try + { + var result = new List(_deserializer.DeserializeInstances(buffer, ref offset, _onClassNeeded, null)); + return result; + } + catch (CimException exception) + { + PSInvalidOperationException e = PSTraceSource.NewInvalidOperationException( + exception, ParserStrings.CimDeserializationError, filePath); + + e.SetErrorId("CimDeserializationError"); + throw e; + } + } + + /// + /// Read file content to byte array. + /// + /// + /// + internal static byte[] GetFileContent(string fullFilePath) + { + if (string.IsNullOrEmpty(fullFilePath)) + { + throw PSTraceSource.NewArgumentNullException(nameof(fullFilePath)); + } + + if (!File.Exists(fullFilePath)) + { + var errorMessage = string.Format(CultureInfo.CurrentCulture, ParserStrings.FileNotFound, fullFilePath); + throw PSTraceSource.NewArgumentException(nameof(fullFilePath), errorMessage); + } + + using (FileStream fs = File.OpenRead(fullFilePath)) + { + var bytes = new byte[fs.Length]; + fs.Read(bytes, 0, Convert.ToInt32(fs.Length)); + return bytes; + } + } + + internal List ParseSchemaMofFileBuffer(string mof) + { + uint offset = 0; +#if UNIX + // OMI only supports UTF-8 without BOM + var encoding = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false); +#else + // This is what we traditionally use with Windows + // DSC asked to keep it UTF-32 for Windows + var encoding = new UnicodeEncoding(); +#endif + + var buffer = encoding.GetBytes(mof); + + var result = new List(_deserializer.DeserializeClasses(buffer, ref offset, null, null, null, _onClassNeeded, null)); + return result; + } + + /// + /// + /// + /// + [SuppressMessage("Microsoft.Design", "CA1045:DoNotPassTypesByReference", MessageId = "3#", Justification = "Have to return 2 things. Wrapping those 2 things in a class will result in a more, not less complexity")] + internal List ParseSchemaMof(string filePath) + { + uint offset = 0; + var buffer = GetFileContent(filePath); + try + { + string fileNameDefiningClass = Path.GetFileNameWithoutExtension(filePath); + int dotIndex = fileNameDefiningClass.IndexOf('.'); + if (dotIndex != -1) + { + fileNameDefiningClass = fileNameDefiningClass.Substring(0, dotIndex); + } + + var result = new List(_deserializer.DeserializeClasses(buffer, ref offset, null, null, null, _onClassNeeded, null)); + foreach (CimClass c in result) + { + string superClassName = c.CimSuperClassName; + string className = c.CimSystemProperties.ClassName; + if ((superClassName != null) && (superClassName.Equals("OMI_BaseResource", StringComparison.OrdinalIgnoreCase))) + { + // Get the name of the file without schema.mof extension + if (!(className.Equals(fileNameDefiningClass, StringComparison.OrdinalIgnoreCase))) + { + PSInvalidOperationException e = PSTraceSource.NewInvalidOperationException( + ParserStrings.ClassNameNotSameAsDefiningFile, className, fileNameDefiningClass); + throw e; + } + } + } + + return result; + } + catch (CimException exception) + { + PSInvalidOperationException e = PSTraceSource.NewInvalidOperationException( + exception, ParserStrings.CimDeserializationError, filePath); + + e.SetErrorId("CimDeserializationError"); + throw e; + } + } + + /// + /// Make sure that the instance conforms to the the schema. + /// + /// + internal void ValidateInstanceText(string classText) + { + uint offset = 0; + byte[] bytes = null; + + if (Platform.IsLinux || Platform.IsMacOS) + { + bytes = System.Text.Encoding.UTF8.GetBytes(classText); + } + else + { + bytes = System.Text.Encoding.Unicode.GetBytes(classText); + } + + _deserializer.DeserializeInstances(bytes, ref offset, _onClassNeeded, null); + } + } +} diff --git a/src/System.Management.Automation/DscSupport/CimDSCParser.cs b/src/System.Management.Automation/DscSupport/MofDscClassCache.cs old mode 100644 new mode 100755 similarity index 88% rename from src/System.Management.Automation/DscSupport/CimDSCParser.cs rename to src/System.Management.Automation/DscSupport/MofDscClassCache.cs index 58fc81715bb..ba83b51e96b --- a/src/System.Management.Automation/DscSupport/CimDSCParser.cs +++ b/src/System.Management.Automation/DscSupport/MofDscClassCache.cs @@ -22,462 +22,7 @@ using Microsoft.Management.Infrastructure.Serialization; using Microsoft.PowerShell.Commands; -namespace Microsoft.PowerShell.DesiredStateConfiguration.Internal -{ - /// - /// - [SuppressMessage("Microsoft.MSInternal", "CA903:InternalNamespaceShouldNotContainPublicTypes", - Justification = "Needed Internal use only")] - public static class DscRemoteOperationsClass - { - /// - /// Convert Cim Instance representing Resource desired state to Powershell Class Object. - /// - public static object ConvertCimInstanceToObject(Type targetType, CimInstance instance, string moduleName) - { - var className = instance.CimClass.CimSystemProperties.ClassName; - object targetObject = null; - string errorMessage; - - using (System.Management.Automation.PowerShell powerShell = System.Management.Automation.PowerShell.Create(RunspaceMode.CurrentRunspace)) - { - string script = "param($targetType,$moduleName) & (Microsoft.PowerShell.Core\\Get-Module $moduleName) { New-Object $targetType } "; - - powerShell.AddScript(script); - powerShell.AddArgument(targetType); - powerShell.AddArgument(moduleName); - - Collection psExecutionResult = powerShell.Invoke(); - if (psExecutionResult.Count == 1) - { - targetObject = psExecutionResult[0].BaseObject; - } - else - { - Exception innerException = null; - if (powerShell.Streams.Error != null && powerShell.Streams.Error.Count > 0) - { - innerException = powerShell.Streams.Error[0].Exception; - } - - errorMessage = string.Format(CultureInfo.CurrentCulture, ParserStrings.InstantiatePSClassObjectFailed, className); - var invalidOperationException = new InvalidOperationException(errorMessage, innerException); - throw invalidOperationException; - } - } - - foreach (var property in instance.CimInstanceProperties) - { - if (property.Value != null) - { - MemberInfo[] memberInfo = targetType.GetMember(property.Name, BindingFlags.Public | BindingFlags.Instance); - - // verify property exists in corresponding class type - if (memberInfo == null || memberInfo.Length > 1 || !(memberInfo[0] is PropertyInfo || memberInfo[0] is FieldInfo)) - { - errorMessage = string.Format(CultureInfo.CurrentCulture, ParserStrings.PropertyNotDeclaredInPSClass, new object[] { property.Name, className }); - var invalidOperationException = new InvalidOperationException(errorMessage); - throw invalidOperationException; - } - - var member = memberInfo[0]; - var memberType = (member is FieldInfo) - ? ((FieldInfo)member).FieldType - : ((PropertyInfo)member).PropertyType; - - object targetValue = null; - switch (property.CimType) - { - case CimType.Instance: - { - var cimPropertyInstance = property.Value as CimInstance; - if (cimPropertyInstance != null && - cimPropertyInstance.CimClass != null && - cimPropertyInstance.CimClass.CimSystemProperties != null && - string.Equals( - cimPropertyInstance.CimClass.CimSystemProperties.ClassName, - "MSFT_Credential", StringComparison.OrdinalIgnoreCase)) - { - targetValue = ConvertCimInstancePsCredential(moduleName, cimPropertyInstance); - } - else - { - targetValue = ConvertCimInstanceToObject(memberType, cimPropertyInstance, moduleName); - } - - if (targetValue == null) - { - return null; - } - } - - break; - case CimType.InstanceArray: - { - if (memberType == typeof(Hashtable)) - { - targetValue = ConvertCimInstanceHashtable(moduleName, (CimInstance[])property.Value); - } - else - { - var instanceArray = (CimInstance[])property.Value; - if (!memberType.IsArray) - { - errorMessage = string.Format(CultureInfo.CurrentCulture, ParserStrings.ExpectArrayTypeOfPropertyInPSClass, new object[] { property.Name, className }); - var invalidOperationException = new InvalidOperationException(errorMessage); - throw invalidOperationException; - } - - var elementType = memberType.GetElementType(); - var targetArray = Array.CreateInstance(elementType, instanceArray.Length); - for (int i = 0; i < instanceArray.Length; i++) - { - var obj = ConvertCimInstanceToObject(elementType, instanceArray[i], moduleName); - if (obj == null) - { - return null; - } - - targetArray.SetValue(obj, i); - } - - targetValue = targetArray; - } - } - - break; - default: - targetValue = LanguagePrimitives.ConvertTo(property.Value, memberType, CultureInfo.InvariantCulture); - break; - } - - if (targetValue == null) - { - errorMessage = string.Format(CultureInfo.CurrentCulture, ParserStrings.ConvertCimPropertyToObjectPropertyFailed, new object[] { property.Name, className }); - var invalidOperationException = new InvalidOperationException(errorMessage); - throw invalidOperationException; - } - - if (member is FieldInfo) - { - ((FieldInfo)member).SetValue(targetObject, targetValue); - } - - if (member is PropertyInfo) - { - ((PropertyInfo)member).SetValue(targetObject, targetValue); - } - } - } - - return targetObject; - } - - /// - /// Convert hashtable from Ciminstance to hashtable primitive type. - /// - /// - /// - /// - private static object ConvertCimInstanceHashtable(string providerName, CimInstance[] arrayInstance) - { - var result = new Hashtable(); - string errorMessage; - - try - { - foreach (var keyValuePair in arrayInstance) - { - var key = keyValuePair.CimInstanceProperties["Key"]; - var value = keyValuePair.CimInstanceProperties["Value"]; - - if (key == null || value == null) - { - errorMessage = string.Format(CultureInfo.CurrentCulture, ParserStrings.InvalidHashtable, providerName); - var invalidOperationException = new InvalidOperationException(errorMessage); - throw invalidOperationException; - } - - result.Add(LanguagePrimitives.ConvertTo(key.Value), LanguagePrimitives.ConvertTo(value.Value)); - } - } - catch (Exception exception) - { - errorMessage = string.Format(CultureInfo.CurrentCulture, ParserStrings.InvalidHashtable, providerName); - var invalidOperationException = new InvalidOperationException(errorMessage, exception); - throw invalidOperationException; - } - - return result; - } - /// - /// Convert CIM instance to PS Credential. - /// - /// - /// - /// - private static object ConvertCimInstancePsCredential(string providerName, CimInstance propertyInstance) - { - string errorMessage; - string userName; - string plainPassWord; - - try - { - userName = propertyInstance.CimInstanceProperties["UserName"].Value as string; - if (string.IsNullOrEmpty(userName)) - { - errorMessage = string.Format(CultureInfo.CurrentCulture, ParserStrings.InvalidUserName, providerName); - var invalidOperationException = new InvalidOperationException(errorMessage); - throw invalidOperationException; - } - } - catch (CimException exception) - { - errorMessage = string.Format(CultureInfo.CurrentCulture, ParserStrings.InvalidUserName, providerName); - var invalidOperationException = new InvalidOperationException(errorMessage, exception); - throw invalidOperationException; - } - - try - { - plainPassWord = propertyInstance.CimInstanceProperties["PassWord"].Value as string; - - // In future we might receive password in an encrypted format. Make sure we add - // the decryption login in this method. - if (string.IsNullOrEmpty(plainPassWord)) - { - errorMessage = string.Format(CultureInfo.CurrentCulture, ParserStrings.InvalidPassword, providerName); - var invalidOperationException = new InvalidOperationException(errorMessage); - throw invalidOperationException; - } - } - catch (CimException exception) - { - errorMessage = string.Format(CultureInfo.CurrentCulture, ParserStrings.InvalidPassword, providerName); - var invalidOperationException = new InvalidOperationException(errorMessage, exception); - throw invalidOperationException; - } - - // Extract the password into a SecureString. - var password = new SecureString(); - foreach (char t in plainPassWord) - { - password.AppendChar(t); - } - - password.MakeReadOnly(); - return new PSCredential(userName, password); - } - } -} - -namespace Microsoft.PowerShell.DesiredStateConfiguration -{ - /// - /// To make it easier to specify -ConfigurationData parameter, we add an ArgumentTransformationAttribute here. - /// When the input data is of type string and is valid path to a file that can be converted to hashtable, we do - /// the conversion and return the converted value. Otherwise, we just return the input data. - /// - [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property | AttributeTargets.Parameter, AllowMultiple = false)] - public sealed class ArgumentToConfigurationDataTransformationAttribute : ArgumentTransformationAttribute - { - /// - /// Convert a file of ConfigurationData into a hashtable. - /// - /// - /// - /// - public override object Transform(EngineIntrinsics engineIntrinsics, object inputData) - { - var configDataPath = inputData as string; - if (string.IsNullOrEmpty(configDataPath)) - { - return inputData; - } - - if (engineIntrinsics == null) - { - throw PSTraceSource.NewArgumentNullException(nameof(engineIntrinsics)); - } - - return PsUtils.EvaluatePowerShellDataFileAsModuleManifest( - "ConfigurationData", - configDataPath, - engineIntrinsics.SessionState.Internal.ExecutionContext, - skipPathValidation: false); - } - } - - /// - /// - /// Represents a communication channel to a CIM server. - /// - /// - /// This is the main entry point of the Microsoft.Management.Infrastructure API. - /// All CIM operations are represented as methods of this class. - /// - /// - internal class CimDSCParser - { - private CimMofDeserializer _deserializer; - private CimMofDeserializer.OnClassNeeded _onClassNeeded; - /// - /// - internal CimDSCParser(CimMofDeserializer.OnClassNeeded onClassNeeded) - { - _deserializer = CimMofDeserializer.Create(); - _onClassNeeded = onClassNeeded; - } - - /// - /// - internal CimDSCParser(CimMofDeserializer.OnClassNeeded onClassNeeded, Microsoft.Management.Infrastructure.Serialization.MofDeserializerSchemaValidationOption validationOptions) - { - _deserializer = CimMofDeserializer.Create(); - _deserializer.SchemaValidationOption = validationOptions; - _onClassNeeded = onClassNeeded; - } - - /// - /// - /// - /// - [SuppressMessage("Microsoft.Design", "CA1045:DoNotPassTypesByReference", MessageId = "3#", Justification = "Have to return 2 things. Wrapping those 2 things in a class will result in a more, not less complexity")] - internal List ParseInstanceMof(string filePath) - { - uint offset = 0; - var buffer = GetFileContent(filePath); - try - { - var result = new List(_deserializer.DeserializeInstances(buffer, ref offset, _onClassNeeded, null)); - return result; - } - catch (CimException exception) - { - PSInvalidOperationException e = PSTraceSource.NewInvalidOperationException( - exception, ParserStrings.CimDeserializationError, filePath); - - e.SetErrorId("CimDeserializationError"); - throw e; - } - } - - /// - /// Read file content to byte array. - /// - /// - /// - internal static byte[] GetFileContent(string fullFilePath) - { - if (string.IsNullOrEmpty(fullFilePath)) - { - throw PSTraceSource.NewArgumentNullException(nameof(fullFilePath)); - } - - if (!File.Exists(fullFilePath)) - { - var errorMessage = string.Format(CultureInfo.CurrentCulture, ParserStrings.FileNotFound, fullFilePath); - throw PSTraceSource.NewArgumentException(nameof(fullFilePath), errorMessage); - } - - using (FileStream fs = File.OpenRead(fullFilePath)) - { - var bytes = new byte[fs.Length]; - fs.Read(bytes, 0, Convert.ToInt32(fs.Length)); - return bytes; - } - } - - internal List ParseSchemaMofFileBuffer(string mof) - { - uint offset = 0; -#if UNIX - // OMI only supports UTF-8 without BOM - var encoding = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false); -#else - // This is what we traditionally use with Windows - // DSC asked to keep it UTF-32 for Windows - var encoding = new UnicodeEncoding(); -#endif - - var buffer = encoding.GetBytes(mof); - - var result = new List(_deserializer.DeserializeClasses(buffer, ref offset, null, null, null, _onClassNeeded, null)); - return result; - } - - /// - /// - /// - /// - [SuppressMessage("Microsoft.Design", "CA1045:DoNotPassTypesByReference", MessageId = "3#", Justification = "Have to return 2 things. Wrapping those 2 things in a class will result in a more, not less complexity")] - internal List ParseSchemaMof(string filePath) - { - uint offset = 0; - var buffer = GetFileContent(filePath); - try - { - string fileNameDefiningClass = Path.GetFileNameWithoutExtension(filePath); - int dotIndex = fileNameDefiningClass.IndexOf('.'); - if (dotIndex != -1) - { - fileNameDefiningClass = fileNameDefiningClass.Substring(0, dotIndex); - } - - var result = new List(_deserializer.DeserializeClasses(buffer, ref offset, null, null, null, _onClassNeeded, null)); - foreach (CimClass c in result) - { - string superClassName = c.CimSuperClassName; - string className = c.CimSystemProperties.ClassName; - if ((superClassName != null) && (superClassName.Equals("OMI_BaseResource", StringComparison.OrdinalIgnoreCase))) - { - // Get the name of the file without schema.mof extension - if (!(className.Equals(fileNameDefiningClass, StringComparison.OrdinalIgnoreCase))) - { - PSInvalidOperationException e = PSTraceSource.NewInvalidOperationException( - ParserStrings.ClassNameNotSameAsDefiningFile, className, fileNameDefiningClass); - throw e; - } - } - } - - return result; - } - catch (CimException exception) - { - PSInvalidOperationException e = PSTraceSource.NewInvalidOperationException( - exception, ParserStrings.CimDeserializationError, filePath); - - e.SetErrorId("CimDeserializationError"); - throw e; - } - } - - /// - /// Make sure that the instance conforms to the the schema. - /// - /// - internal void ValidateInstanceText(string classText) - { - uint offset = 0; - byte[] bytes = null; - - if (Platform.IsLinux || Platform.IsMacOS) - { - bytes = System.Text.Encoding.UTF8.GetBytes(classText); - } - else - { - bytes = System.Text.Encoding.Unicode.GetBytes(classText); - } - - _deserializer.DeserializeInstances(bytes, ref offset, _onClassNeeded, null); - } - } -} - -namespace Microsoft.PowerShell.DesiredStateConfiguration.Internal +namespace Microsoft.PowerShell.DesiredStateConfiguration.Internal.Mof { /// /// @@ -940,6 +485,76 @@ private static CimClass MyClassCallback(string serverName, string namespaceName, return null; } + internal static bool CimPropertyIsInherited(string propertyName, CimClass parentClass) + { + if (parentClass == null) + { + return false; + } + else + { + foreach(var p in parentClass.CimClassProperties) + { + if (p.Name.Equals(propertyName, StringComparison.InvariantCultureIgnoreCase)) + { + return true; + } + } + + return CimPropertyIsInherited(propertyName, parentClass.CimSuperClass); + } + } + + internal static List PrepareCimMofForJsonConvertion(List classes) + { + var result = new List(); + foreach(var c in classes) + { + var cpso = new PSObject(); + cpso.Properties.Add(new PSNoteProperty("CimSystemProperties", c.CimSystemProperties)); + cpso.Properties.Add(new PSNoteProperty("CimSuperClassName", c.CimSuperClassName)); + cpso.Properties.Add(new PSNoteProperty("CimClassQualifiers", c.CimClassQualifiers)); + + var properties = new List(); + + foreach(var p in c.CimClassProperties) + { + if (!CimPropertyIsInherited(p.Name, c.CimSuperClass)) + { + properties.Add(p); + } + } + + cpso.Properties.Add(new PSNoteProperty("CimClassProperties", properties)); + + result.Add(cpso); + } + + return result; + } + + internal static void ConvertCimMofToJson(string mofPath) + { + var parser = new Microsoft.PowerShell.DesiredStateConfiguration.Mof.CimDSCParser(MyClassCallback); + List mofClasses = parser.ParseSchemaMof(mofPath); + var jsonClasses = PrepareCimMofForJsonConvertion(mofClasses); + + string jsonPath = mofPath.Substring(0, mofPath.LastIndexOf('.')) + ".json"; + using (var powerShell = System.Management.Automation.PowerShell.Create(RunspaceMode.CurrentRunspace)) + { + powerShell.AddCommand("ConvertTo-Json"); + powerShell.AddParameter("InputObject", jsonClasses); + powerShell.AddParameter("Depth", 100); + powerShell.AddParameter("EnumsAsStrings", true); + + powerShell.AddCommand("Out-File"); + powerShell.AddParameter("Path", jsonPath); + powerShell.AddParameter("Force", true); + + powerShell.Invoke(); + } + } + /// /// Import CIM classes from the given file. /// @@ -957,7 +572,7 @@ public static List ImportClasses(string path, Tuple m s_tracer.WriteLine("DSC ClassCache: importing file: {0}", path); - var parser = new Microsoft.PowerShell.DesiredStateConfiguration.CimDSCParser(MyClassCallback); + var parser = new Microsoft.PowerShell.DesiredStateConfiguration.Mof.CimDSCParser(MyClassCallback); List classes = null; try @@ -1218,7 +833,7 @@ public static List ImportInstances(string path) throw PSTraceSource.NewArgumentNullException(nameof(path)); } - var parser = new Microsoft.PowerShell.DesiredStateConfiguration.CimDSCParser(MyClassCallback); + var parser = new Microsoft.PowerShell.DesiredStateConfiguration.Mof.CimDSCParser(MyClassCallback); return parser.ParseInstanceMof(path); } @@ -1243,7 +858,7 @@ public static List ImportInstances(string path, int schemaValidatio throw new IndexOutOfRangeException("schemaValidationOption"); } - var parser = new Microsoft.PowerShell.DesiredStateConfiguration.CimDSCParser(MyClassCallback, (Microsoft.Management.Infrastructure.Serialization.MofDeserializerSchemaValidationOption)schemaValidationOption); + var parser = new Microsoft.PowerShell.DesiredStateConfiguration.Mof.CimDSCParser(MyClassCallback, (Microsoft.Management.Infrastructure.Serialization.MofDeserializerSchemaValidationOption)schemaValidationOption); return parser.ParseInstanceMof(path); } @@ -1260,7 +875,7 @@ public static void ValidateInstanceText(string instanceText) throw PSTraceSource.NewArgumentNullException(nameof(instanceText)); } - var parser = new Microsoft.PowerShell.DesiredStateConfiguration.CimDSCParser(MyClassCallback); + var parser = new Microsoft.PowerShell.DesiredStateConfiguration.Mof.CimDSCParser(MyClassCallback); parser.ValidateInstanceText(instanceText); } @@ -1461,11 +1076,11 @@ private static DynamicKeyword CreateKeywordFromCimClass(string moduleName, Versi // Copy the type name string. If it's an embedded instance, need to grab it from the ReferenceClassName bool referenceClassNameIsNullOrEmpty = string.IsNullOrEmpty(prop.ReferenceClassName); - if (prop.CimType == CimType.Instance && !referenceClassNameIsNullOrEmpty) + if (prop.CimType == Microsoft.Management.Infrastructure.CimType.Instance && !referenceClassNameIsNullOrEmpty) { keyProp.TypeConstraint = prop.ReferenceClassName; } - else if (prop.CimType == CimType.InstanceArray && !referenceClassNameIsNullOrEmpty) + else if (prop.CimType == Microsoft.Management.Infrastructure.CimType.InstanceArray && !referenceClassNameIsNullOrEmpty) { keyProp.TypeConstraint = prop.ReferenceClassName + "[]"; } @@ -2640,7 +2255,7 @@ private static bool ImportKeywordsFromScriptFile(string fileName, PSModuleInfo m } var result = false; - var parser = new CimDSCParser(MyClassCallback); + var parser = new Microsoft.PowerShell.DesiredStateConfiguration.Mof.CimDSCParser(MyClassCallback); const WildcardOptions wildcardOptions = WildcardOptions.IgnoreCase | WildcardOptions.CultureInvariant; IEnumerable patternList = SessionStateUtilities.CreateWildcardsFromStrings(module._declaredDscResourceExports, wildcardOptions); @@ -3121,7 +2736,7 @@ private static bool ImportKeywordsFromAssembly(PSModuleInfo module, { bool result = false; - var parser = new Microsoft.PowerShell.DesiredStateConfiguration.CimDSCParser(MyClassCallback); + var parser = new Microsoft.PowerShell.DesiredStateConfiguration.Mof.CimDSCParser(MyClassCallback); IEnumerable resourceDefinitions = assembly.GetTypes().Where(t => t.GetCustomAttributes().Any()); @@ -3151,7 +2766,7 @@ private static bool ImportKeywordsFromAssembly(PSModuleInfo module, } private static void ProcessMofForDynamicKeywords(PSModuleInfo module, ICollection resourcesFound, - Dictionary functionsToDefine, CimDSCParser parser, string mof, DSCResourceRunAsCredential runAsBehavior) + Dictionary functionsToDefine, Microsoft.PowerShell.DesiredStateConfiguration.Mof.CimDSCParser parser, string mof, DSCResourceRunAsCredential runAsBehavior) { foreach (var c in parser.ParseSchemaMofFileBuffer(mof)) { diff --git a/src/System.Management.Automation/engine/InitialSessionState.cs b/src/System.Management.Automation/engine/InitialSessionState.cs index 0d2e1274aa6..2b7f1dcd375 100644 --- a/src/System.Management.Automation/engine/InitialSessionState.cs +++ b/src/System.Management.Automation/engine/InitialSessionState.cs @@ -5321,6 +5321,7 @@ private static void InitializeCoreCmdletsAndProviders( { { "Add-History", new SessionStateCmdletEntry("Add-History", typeof(AddHistoryCommand), helpFile) }, { "Clear-History", new SessionStateCmdletEntry("Clear-History", typeof(ClearHistoryCommand), helpFile) }, + { "Convert-CimMofToJson", new SessionStateCmdletEntry("Convert-CimMofToJson", typeof(Microsoft.PowerShell.DesiredStateConfiguration.Internal.ConvertCimMofToJsonCommand), helpFile) }, { "Debug-Job", new SessionStateCmdletEntry("Debug-Job", typeof(DebugJobCommand), helpFile) }, #if !UNIX { "Disable-PSRemoting", new SessionStateCmdletEntry("Disable-PSRemoting", typeof(DisablePSRemotingCommand), helpFile) }, From 2e1c25bedbcbc0177ea4adb6d5468d69180d5316 Mon Sep 17 00:00:00 2001 From: anmenaga Date: Mon, 10 Aug 2020 23:39:08 -0700 Subject: [PATCH 02/64] Code cleanup --- .../DscSupport/ConvertCimMofToJsonCommand.cs | 2 +- .../DscSupport/JsonCimDSCParser.cs | 9 - .../DscSupport/JsonDscClassCache.cs | 579 +----------------- 3 files changed, 24 insertions(+), 566 deletions(-) diff --git a/src/System.Management.Automation/DscSupport/ConvertCimMofToJsonCommand.cs b/src/System.Management.Automation/DscSupport/ConvertCimMofToJsonCommand.cs index 697590cea72..88ea8cd3b96 100755 --- a/src/System.Management.Automation/DscSupport/ConvertCimMofToJsonCommand.cs +++ b/src/System.Management.Automation/DscSupport/ConvertCimMofToJsonCommand.cs @@ -29,7 +29,7 @@ public sealed class ConvertCimMofToJsonCommand : Cmdlet /// protected override void ProcessRecord() { - // Mof parser uses DSC_HOME env var which is normally set by PSDesiredStateConfiguration module + // Mof parser uses DSC_HOME env var which is usually set by PSDesiredStateConfiguration module // Because this cmlet can be run without loading PSDesiredStateConfiguration module, we are setting this env var here. string varName = "DSC_HOME"; if (string.IsNullOrEmpty(Environment.GetEnvironmentVariable(varName, EnvironmentVariableTarget.Process))) diff --git a/src/System.Management.Automation/DscSupport/JsonCimDSCParser.cs b/src/System.Management.Automation/DscSupport/JsonCimDSCParser.cs index 58eb720a411..71e8c8f9104 100755 --- a/src/System.Management.Automation/DscSupport/JsonCimDSCParser.cs +++ b/src/System.Management.Automation/DscSupport/JsonCimDSCParser.cs @@ -64,14 +64,5 @@ internal List ParseSchemaJson(string filePath) throw e; } } - - /// - /// Make sure that the instance conforms to the the schema. - /// - /// - internal void ValidateInstanceText(string classText) - { - throw new NotImplementedException("Instance parsing/validation is not yet suported by JSON-based parser"); - } } } diff --git a/src/System.Management.Automation/DscSupport/JsonDscClassCache.cs b/src/System.Management.Automation/DscSupport/JsonDscClassCache.cs index dd52be2666c..255c9ed882f 100755 --- a/src/System.Management.Automation/DscSupport/JsonDscClassCache.cs +++ b/src/System.Management.Automation/DscSupport/JsonDscClassCache.cs @@ -231,7 +231,6 @@ public static void Initialize(Collection errors, List moduleP if (Platform.IsLinux || Platform.IsMacOS) { - //WriteVerbose("Initialize / Platform.IsLinux || Platform.IsMacOS"); // // Load the base schema files. // @@ -244,10 +243,8 @@ public static void Initialize(Collection errors, List moduleP throw new DirectoryNotFoundException("Unable to find DSC schema store at " + dscConfigurationDirectory + ". Please ensure PS DSC for Linux is installed."); } - //WriteVerbose("ImportClasses : BaseRegistration/BaseResource.schema.json"); var resourceBaseFile = Path.Combine(dscConfigurationDirectory, "BaseRegistration/BaseResource.schema.json"); ImportClasses(resourceBaseFile, s_defaultModuleInfoForResource, errors); - //WriteVerbose("ImportClasses : BaseRegistration/MSFT_DSCMetaConfiguration.json"); var metaConfigFile = Path.Combine(dscConfigurationDirectory, "BaseRegistration/MSFT_DSCMetaConfiguration.json"); ImportClasses(metaConfigFile, s_defaultModuleInfoForResource, errors); @@ -267,7 +264,6 @@ public static void Initialize(Collection errors, List moduleP foreach (var schemaFile in Directory.EnumerateDirectories(resources).SelectMany(d => Directory.EnumerateFiles(d, "*.schema.json"))) { - WriteVerbose("ImportClasses : " + schemaFile); ImportClasses(schemaFile, s_defaultModuleInfoForResource, errors); } } @@ -471,12 +467,12 @@ private static Tuple GetModuleInfoHelper(string moduleFolderPat } - private static void WriteVerbose(string warning) + private static void WriteWarning(string warning) { var executionContext = System.Management.Automation.Runspaces.Runspace.DefaultRunspace.ExecutionContext; if (executionContext != null && executionContext.InternalHost != null && executionContext.InternalHost.UI != null) { - executionContext.InternalHost.UI.WriteVerboseLine(warning); + executionContext.InternalHost.UI.WriteWarningLine(warning); } } @@ -489,7 +485,7 @@ public static List ReadClassesFromJson(string jsonFilePath) { if (! jsonFilePath.EndsWith(".json", StringComparison.InvariantCultureIgnoreCase)) { - WriteVerbose(string.Format("Cannot parse non-JSON file {0}", jsonFilePath)); + WriteWarning(string.Format("Cannot parse non-JSON file {0}", jsonFilePath)); return null; } @@ -515,7 +511,7 @@ public static List ImportClasses(string path, Tuple m if (! path.EndsWith(".json", StringComparison.InvariantCultureIgnoreCase)) { - WriteVerbose(string.Format("Cannot parse non-JSON file {0}", path)); + WriteWarning(string.Format("Cannot parse non-JSON file {0}", path)); return null; } @@ -678,8 +674,9 @@ private static List> FindResourceInCach } /// + /// Returns cached classes /// - /// + /// Returns cached classes private static List GetCachedClasses() { return ClassCache.Values.ToList(); @@ -775,67 +772,6 @@ public static List GetCachedClassByModuleName(string moduleName) return (from filename in ByFileClassCache.Keys where string.Equals(Path.GetFileName(filename), moduleFileName, StringComparison.OrdinalIgnoreCase) select GetCachedClassByFileName(filename)).FirstOrDefault(); } -/*TODO-AM: - /// - /// Routine used to load a set of CIM instances from a .mof file using the - /// current set of cached classes for schema validation. - /// - /// The file to load the classes from. - /// - public static List ImportInstances(string path) - { - if (string.IsNullOrEmpty(path)) - { - throw PSTraceSource.NewArgumentNullException(nameof(path)); - } - - var parser = new Microsoft.PowerShell.DesiredStateConfiguration.CimDSCParser(); - - return parser.ParseInstanceMof(path); - } - - /// - /// Routine used to load a set of CIM instances from a .mof file using the - /// current set of cached classes for schema validation. - /// - /// - /// - /// - public static List ImportInstances(string path, int schemaValidationOption) - { - if (string.IsNullOrEmpty(path)) - { - throw PSTraceSource.NewArgumentNullException(nameof(path)); - } - - if (schemaValidationOption < (int)Microsoft.Management.Infrastructure.Serialization.MofDeserializerSchemaValidationOption.Default || - schemaValidationOption > (int)Microsoft.Management.Infrastructure.Serialization.MofDeserializerSchemaValidationOption.Ignore) - { - throw new IndexOutOfRangeException("schemaValidationOption"); - } - - var parser = new Microsoft.PowerShell.DesiredStateConfiguration.CimDSCParser(MyClassCallback, (Microsoft.Management.Infrastructure.Serialization.MofDeserializerSchemaValidationOption)schemaValidationOption); - - return parser.ParseInstanceMof(path); - }*/ - - /// - /// A routine that validates a string containing MOF instances against the - /// current set of cached classes. - /// - /// - public static void ValidateInstanceText(string instanceText) - { - if (string.IsNullOrEmpty(instanceText)) - { - throw PSTraceSource.NewArgumentNullException(nameof(instanceText)); - } - - var parser = new Microsoft.PowerShell.DesiredStateConfiguration.CimDSCParser(); - - parser.ValidateInstanceText(instanceText); - } - private static bool IsMagicProperty(string propertyName) { return System.Text.RegularExpressions.Regex.Match(propertyName, @@ -975,6 +911,7 @@ private static DynamicKeyword CreateKeywordFromCimClass(string moduleName, Versi // code above is the only place that references CimSuperClass // so to simplify things we just check superclass to be OMI_BaseResource // with assumption that current code will not work for multi-level class inheritance (which is never used in practice according to DSC team) + // this simplification allows us to avoid linking objects together using CimSuperClass field during deserialization from json schema if ((!string.IsNullOrEmpty(cimClass.CimSuperClassName)) && string.Equals("OMI_BaseResource", cimClass.CimSuperClassName, StringComparison.OrdinalIgnoreCase)) { isResourceType = true; @@ -1270,8 +1207,6 @@ private static void LoadDefaultCimKeywords(Dictionary funct // This function is called after parsing the Import-DscResource keyword and it's arguments, but before parsing // anything else. - // - // private static ParseError[] ImportResourcePostParse(DynamicKeywordStatementAst kwAst) { var elements = Ast.CopyElements(kwAst.CommandElements); @@ -1802,7 +1737,7 @@ private static void LoadPowerShellClassResourcesFromModule(PSModuleInfo primaryM // Ignore the module if we can't find the assembly. if (assembly != null) { - ImportKeywordsFromAssembly(moduleInfo, resourcesToImport, resourcesFound, functionsToDefine, assembly); + throw new NotImplementedException("ModuleType.Binary / ImportKeywordsFromAssembly not supported yet"); } } finally @@ -1873,18 +1808,14 @@ public static List ImportClassResourcesFromModule(PSModuleInfo moduleInf return resourcesImported; } - internal static PSObject[] GenerateJsonForAst(TypeDefinitionAst typeAst) + internal static PSObject[] GenerateJsonClassesForAst(TypeDefinitionAst typeAst) { var embeddedInstanceTypes = new List(); - var sb = new StringBuilder(); - var result = GenerateJsonForAst(typeAst, sb, embeddedInstanceTypes); + var result = GenerateJsonClassesForAst(typeAst, embeddedInstanceTypes); var visitedInstances = new List(); visitedInstances.Add(typeAst); - /*TODO-AM: add support for embeddedInstanceTypes - ProcessEmbeddedInstanceTypes(embeddedInstanceTypes, visitedInstances, sb);*/ - return result; } @@ -1932,74 +1863,19 @@ internal static string MapTypeNameToMofType(ITypeName typeName, string memberNam return "string"; } - /*private static void GenerateJsonForAst(TypeDefinitionAst typeAst, StringBuilder sb, List embeddedInstanceTypes) + private static PSObject[] GenerateJsonClassesForAst(TypeDefinitionAst typeAst, List embeddedInstanceTypes) { + // MOF-based implementation of this used to generate MOF string representing classes/typeAst and pass it to MMI/MOF deserializer to get CimClass array + // Here we are avoiding that roundtrip just constructing the resulting PSObjects + var className = typeAst.Name; - sb.AppendFormat(CultureInfo.InvariantCulture, "[ClassVersion(\"1.0.0\"), FriendlyName(\"{0}\")]\nclass {0}", className); + string cimSuperClassName = null; if (typeAst.Attributes.Any(a => a.TypeName.GetReflectionAttributeType() == typeof(DscResourceAttribute))) { - sb.Append(" : OMI_BaseResource"); - } - - sb.Append("\n{\n"); - - ProcessMembers(sb, embeddedInstanceTypes, typeAst, className); - - Queue bases = new Queue(); - foreach (var b in typeAst.BaseTypes) - { - bases.Enqueue(b); - } - - while (bases.Count > 0) - { - var b = bases.Dequeue(); - var tc = b as TypeConstraintAst; - - if (tc != null) - { - b = tc.TypeName.GetReflectionType(); - if (b == null) - { - var td = tc.TypeName as TypeName; - if (td != null && td._typeDefinitionAst != null) - { - ProcessMembers(sb, embeddedInstanceTypes, td._typeDefinitionAst, className); - foreach (var b1 in td._typeDefinitionAst.BaseTypes) - { - bases.Enqueue(b1); - } - } - - continue; - } - } - - var type = b as Type; - if (type != null) - { - ProcessMembers(type, sb, embeddedInstanceTypes, className); - var t = type.BaseType; - if (t != null) - { - bases.Enqueue(t); - } - } + cimSuperClassName = "OMI_BaseResource"; } - sb.Append("};"); - }*/ - - private static PSObject[] GenerateJsonForAst(TypeDefinitionAst typeAst, StringBuilder sb, List embeddedInstanceTypes) - { - var className = typeAst.Name; - /*TODO-AM: add base classes: - if (typeAst.Attributes.Any(a => a.TypeName.GetReflectionAttributeType() == typeof(DscResourceAttribute))) - { - sb.Append(" : OMI_BaseResource"); - }*/ - var _ClassVersion = new PSObject(); _ClassVersion.Properties.Add(new PSNoteProperty("Name", "ClassVersion")); _ClassVersion.Properties.Add(new PSNoteProperty("Value", "1.0.0")); @@ -2020,15 +1896,12 @@ private static PSObject[] GenerateJsonForAst(TypeDefinitionAst typeAst, StringBu _CimSystemProperties.Properties.Add(new PSNoteProperty("ClassName", className)); _CimSystemProperties.Properties.Add(new PSNoteProperty("Path", null)); - var _CimClassProperties = ProcessMembers(sb, embeddedInstanceTypes, typeAst, className).ToArray(); - + var _CimClassProperties = ProcessMembers(embeddedInstanceTypes, typeAst, className).ToArray(); var result = new PSObject(); - result.Properties.Add(new PSNoteProperty("CimSuperClassName", null)); //TODO-AM: this has to change based on parent class - result.Properties.Add(new PSNoteProperty("CimSuperClass", null)); //TODO-AM: this has to change based on parent class + result.Properties.Add(new PSNoteProperty("CimSuperClassName", cimSuperClassName)); result.Properties.Add(new PSNoteProperty("CimClassProperties", _CimClassProperties)); result.Properties.Add(new PSNoteProperty("CimClassQualifiers", _CimClassQualifiers)); - result.Properties.Add(new PSNoteProperty("CimClassMethods", new PSObject[0])); //TODO-AM: this has to change result.Properties.Add(new PSNoteProperty("CimSystemProperties", _CimSystemProperties)); Queue bases = new Queue(); @@ -2039,7 +1912,7 @@ private static PSObject[] GenerateJsonForAst(TypeDefinitionAst typeAst, StringBu if (bases.Count > 0) { - WriteVerbose(string.Format("BaseTypes count for type {0} is {1} and not implemented yet", className, bases.Count)); + WriteWarning(string.Format("BaseTypes count for type {0} is {1} and not implemented yet", className, bases.Count)); } return new PSObject[] {result}; @@ -2136,64 +2009,7 @@ public static bool GetResourceMethodsLinePosition(PSModuleInfo moduleInfo, strin return false; } - /*private static void ProcessMembers(StringBuilder sb, List embeddedInstanceTypes, TypeDefinitionAst typeDefinitionAst, string className) - { - foreach (var member in typeDefinitionAst.Members) - { - var property = member as PropertyMemberAst; - - if (property == null || property.IsStatic || - property.Attributes.All(a => a.TypeName.GetReflectionAttributeType() != typeof(DscPropertyAttribute))) - { - continue; - } - - var memberType = property.PropertyType == null - ? typeof(object) - : property.PropertyType.TypeName.GetReflectionType(); - - var attributes = new List(); - for (int i = 0; i < property.Attributes.Count; i++) - { - attributes.Add(property.Attributes[i].GetAttribute()); - } - - string mofType; - bool isArrayType; - string embeddedInstanceType; - string[] enumNames = null; - - if (memberType != null) - { - // TODO - validate type and name - mofType = MapTypeToMofType(memberType, member.Name, className, out isArrayType, - out embeddedInstanceType, - embeddedInstanceTypes); - if (memberType.IsEnum) - { - enumNames = Enum.GetNames(memberType); - } - } - else - { - // PropertyType can't be null, we used typeof(object) above in that case so we don't get here. - mofType = MapTypeNameToMofType(property.PropertyType.TypeName, member.Name, className, - out isArrayType, - out embeddedInstanceType, embeddedInstanceTypes, ref enumNames); - } - - string arrayAffix = isArrayType ? "[]" : string.Empty; - - sb.AppendFormat(CultureInfo.InvariantCulture, - " {0}{1} {2}{3};\n", - MapAttributesToMof(enumNames, attributes, embeddedInstanceType), - mofType, - member.Name, - arrayAffix); - } - }*/ - - private static List ProcessMembers(StringBuilder sb, List embeddedInstanceTypes, TypeDefinitionAst typeDefinitionAst, string className) + private static List ProcessMembers(List embeddedInstanceTypes, TypeDefinitionAst typeDefinitionAst, string className) { List result = new List(); @@ -2241,41 +2057,14 @@ private static List ProcessMembers(StringBuilder sb, List embe out embeddedInstanceType, embeddedInstanceTypes, ref enumNames); } - string arrayAffix = isArrayType ? "[]" : string.Empty; - - sb.AppendFormat(CultureInfo.InvariantCulture, - " {0}{1} {2}{3};\n", - MapAttributesToMof(enumNames, attributes, embeddedInstanceType), - mofType, - member.Name, - arrayAffix); - var propertyObject = new PSObject(); propertyObject.Properties.Add(new PSNoteProperty(@"Name", member.Name)); propertyObject.Properties.Add(new PSNoteProperty(@"Value", "null")); propertyObject.Properties.Add(new PSNoteProperty(@"CimType", mofType + (isArrayType ? "Array" : ""))); - //TODO-AM: fill-in rest of attributes propertyObject.Properties.Add(new PSNoteProperty(@"Flags", "Property, NullValue")); - propertyObject.Properties.Add(new PSNoteProperty(@"Qualifiers", new PSObject[0])); + propertyObject.Properties.Add(new PSNoteProperty(@"Qualifiers", new PSObject[0])); //TODO: mark Keys result.Add(propertyObject); - /* - { - "Name": "Path", - "Value": null, - "CimType": "String", - "Flags": "Property, Key, NullValue", - "Qualifiers": [ - { - "Name": "Key", - "Value": true, - "CimType": "Boolean", - "Flags": "DisableOverride, ToSubclass" - } - ], - "ReferenceClassName": null - } - */ } return result; @@ -2417,7 +2206,7 @@ private static bool ImportKeywordsFromScriptFile(string fileName, PSModuleInfo m } } - var classes = GenerateJsonForAst(resourceDefnAst); + var classes = GenerateJsonClassesForAst(resourceDefnAst); ProcessJsonForDynamicKeywords(module, resourcesFound, functionsToDefine, classes, runAsBehavior); } @@ -2444,73 +2233,6 @@ private static bool ImportKeywordsFromScriptFile(string fileName, PSModuleInfo m { typeof(char), "char16" }, }; - /*TODO-AM: private static bool AreQualifiersSame(CimReadOnlyKeyedCollection oldQualifier, CimReadOnlyKeyedCollection newQualifiers) - { - if (oldQualifier.Count != newQualifiers.Count) - { - return false; - } - - foreach (var qual in oldQualifier) - { - // Find the qualifier in new class - var newQual = newQualifiers[qual.Name]; - if (newQual == null) - { - return false; - } - - if ((qual.CimType != newQual.CimType) || - (qual.Flags != newQual.Flags)) - { - return false; - } - - if ((qual.Value == null && newQual.Value != null) || - (qual.Value != null && newQual.Value == null) || - (qual.Value != null && newQual.Value != null && - !string.Equals(qual.Value.ToString(), newQual.Value.ToString(), StringComparison.OrdinalIgnoreCase) - ) - ) - { - return false; - } - } - - return true; - } - - private static bool ArePropertiesSame(CimReadOnlyKeyedCollection oldProperties, CimReadOnlyKeyedCollection newProperties) - { - if (oldProperties.Count != newProperties.Count) - { - return false; - } - - foreach (var prop in oldProperties) - { - // Find the property in new class - var newProp = newProperties[prop.Name]; - if (newProp == null) - { - return false; - } - // flags and type should match - if ((prop.CimType != newProp.CimType) || - (prop.Flags != newProp.Flags)) - { - return false; - } - - if (!AreQualifiersSame(prop.Qualifiers, newProp.Qualifiers)) - { - return false; - } - } - - return true; - }*/ - private static bool IsSameNestedObject(dynamic oldClass, dynamic newClass) { // #1 both the classes should be nested class and not DSC resource @@ -2519,17 +2241,6 @@ private static bool IsSameNestedObject(dynamic oldClass, dynamic newClass) { return false; } - // #2 qualifier count, names, values and types should be same - /*TODO-AM: if (!AreQualifiersSame(oldClass.CimClassQualifiers, newClass.CimClassQualifiers)) - { - return false; - } - - // #3 property count, names, values, qualifiers and types should be same - if (!ArePropertiesSame(oldClass.CimClassProperties, newClass.CimClassProperties)) - { - return false; - }*/ return true; } @@ -2637,250 +2348,6 @@ internal static string MapTypeToMofType(Type type, string memberName, string cla } } - private static string MapAttributesToMof(string[] enumNames, IEnumerable customAttributes, string embeddedInstanceType) - { - var sb = new StringBuilder(); - - sb.Append("["); - bool needComma = false; - foreach (var attr in customAttributes) - { - var dscProperty = attr as DscPropertyAttribute; - if (dscProperty != null) - { - if (dscProperty.Key) - { - sb.AppendFormat(CultureInfo.InvariantCulture, "{0}key", needComma ? ", " : string.Empty); - needComma = true; - } - - if (dscProperty.Mandatory) - { - sb.AppendFormat(CultureInfo.InvariantCulture, "{0}required", needComma ? ", " : string.Empty); - needComma = true; - } - - if (dscProperty.NotConfigurable) - { - sb.AppendFormat(CultureInfo.InvariantCulture, "{0}read", needComma ? ", " : string.Empty); - needComma = true; - } - - continue; - } - - var validateSet = attr as ValidateSetAttribute; - if (validateSet != null) - { - bool valueMapComma = false; - StringBuilder sbValues = new StringBuilder(", Values{"); - sb.AppendFormat(CultureInfo.InvariantCulture, "{0}ValueMap{{", needComma ? ", " : string.Empty); - needComma = true; - - foreach (var value in validateSet.ValidValues) - { - sb.AppendFormat(CultureInfo.InvariantCulture, "{0}\"{1}\"", valueMapComma ? ", " : string.Empty, value); - sbValues.AppendFormat(CultureInfo.InvariantCulture, "{0}\"{1}\"", valueMapComma ? ", " : string.Empty, value); - valueMapComma = true; - } - - sb.Append("}"); - sb.Append(sbValues); - sb.Append("}"); - } - } - - // Default is write - skipped if we already have some attributes - if (sb.Length == 1) - { - sb.Append("write"); - needComma = true; - } - - if (enumNames != null) - { - sb.AppendFormat(CultureInfo.InvariantCulture, "{0}ValueMap{{", needComma ? ", " : string.Empty); - needComma = false; - foreach (var name in enumNames) - { - sb.AppendFormat(CultureInfo.InvariantCulture, "{0}\"{1}\"", needComma ? ", " : string.Empty, name); - needComma = true; - } - - sb.Append("}, Values{"); - needComma = false; - foreach (var name in enumNames) - { - sb.AppendFormat(CultureInfo.InvariantCulture, "{0}\"{1}\"", needComma ? ", " : string.Empty, name); - needComma = true; - } - - sb.Append("}"); - } - else if (embeddedInstanceType != null) - { - sb.AppendFormat(CultureInfo.InvariantCulture, "{0}EmbeddedInstance(\"{1}\")", needComma ? ", " : string.Empty, embeddedInstanceType); - } - - sb.Append("]"); - return sb.ToString(); - } - - /// - /// - /// - /// - public static string GenerateMofForType(Type type) - { - var embeddedInstanceTypes = new List(); - var sb = new StringBuilder(); - - GenerateMofForType(type, sb, embeddedInstanceTypes); - var visitedInstances = new List(); - visitedInstances.Add(type); - ProcessEmbeddedInstanceTypes(embeddedInstanceTypes, visitedInstances, sb); - - return sb.ToString(); - } - - private static void ProcessEmbeddedInstanceTypes(List embeddedInstanceTypes, List visitedInstances, StringBuilder sb) - { - StringBuilder nestedSb = null; - - while (embeddedInstanceTypes.Count > 0) - { - if (nestedSb == null) - { - nestedSb = new StringBuilder(); - } - else - { - nestedSb.Clear(); - } - - var batchedTypes = embeddedInstanceTypes.Where(x => !visitedInstances.Contains(x)).ToArray(); - embeddedInstanceTypes.Clear(); - - for (int i = batchedTypes.Length - 1; i >= 0; i--) - { - visitedInstances.Add(batchedTypes[i]); - var type = batchedTypes[i] as Type; - if (type != null) - { - GenerateMofForType(type, nestedSb, embeddedInstanceTypes); - } - else - { - GenerateJsonForAst((TypeDefinitionAst)batchedTypes[i], nestedSb, embeddedInstanceTypes); - } - - nestedSb.Append('\n'); - } - - sb.Insert(0, nestedSb.ToString()); - } - } - - private static void GenerateMofForType(Type type, StringBuilder sb, List embeddedInstanceTypes) - { - var className = type.Name; - // Friendly name is required by module validator to verify resource instance against the exclusive resource name list. - sb.AppendFormat(CultureInfo.InvariantCulture, "[ClassVersion(\"1.0.0\"), FriendlyName(\"{0}\")]\nclass {0}", className); - - if (type.GetCustomAttributes().Any()) - { - sb.Append(" : OMI_BaseResource"); - } - - sb.Append("\n{\n"); - - ProcessMembers(type, sb, embeddedInstanceTypes, className); - sb.Append("};"); - } - - private static void ProcessMembers(Type type, StringBuilder sb, List embeddedInstanceTypes, string className) - { - foreach (var member in type.GetMembers(BindingFlags.Instance | BindingFlags.Public).Where(m => m is PropertyInfo || m is FieldInfo)) - { - if (member.CustomAttributes.All(cad => cad.AttributeType != typeof(DscPropertyAttribute))) - { - continue; - } - - Type memberType; - var propertyInfo = member as PropertyInfo; - if (propertyInfo == null) - { - var fieldInfo = (FieldInfo)member; - memberType = fieldInfo.FieldType; - } - else - { - if (propertyInfo.GetSetMethod() == null) - { - continue; - } - - memberType = propertyInfo.PropertyType; - } - - // TODO - validate type and name - bool isArrayType; - string embeddedInstanceType; - string mofType = MapTypeToMofType(memberType, member.Name, className, out isArrayType, out embeddedInstanceType, - embeddedInstanceTypes); - string arrayAffix = isArrayType ? "[]" : string.Empty; - - var enumNames = memberType.IsEnum - ? Enum.GetNames(memberType) - : null; - sb.AppendFormat(CultureInfo.InvariantCulture, - " {0}{1} {2}{3};\n", - MapAttributesToMof(enumNames, member.GetCustomAttributes(true), embeddedInstanceType), - mofType, - member.Name, - arrayAffix); - } - } - - private static bool ImportKeywordsFromAssembly(PSModuleInfo module, - ICollection resourcesToImport, - ICollection resourcesFound, - Dictionary functionsToDefine, - Assembly assembly) - { - bool result = false; - - var parser = new Microsoft.PowerShell.DesiredStateConfiguration.CimDSCParser(); - - IEnumerable resourceDefinitions = - assembly.GetTypes().Where(t => t.GetCustomAttributes().Any()); - - foreach (var r in resourceDefinitions) - { - result = true; - bool skip = true; - - foreach (var toImport in resourcesToImport) - { - if ((WildcardPattern.Get(toImport, WildcardOptions.IgnoreCase)).IsMatch(r.Name)) - { - skip = false; - break; - } - } - - if (skip) continue; - - var mof = GenerateMofForType(r); - - /*TODO-AM: update and re-enable - ProcessJsonForDynamicKeywords(module, resourcesFound, functionsToDefine, parser, mof, DSCResourceRunAsCredential.Default);*/ - } - - return result; - } - private static void ProcessJsonForDynamicKeywords(PSModuleInfo module, ICollection resourcesFound, Dictionary functionsToDefine, PSObject[] classes, DSCResourceRunAsCredential runAsBehavior) { @@ -2915,7 +2382,7 @@ private static void ProcessJsonForDynamicKeywords(PSModuleInfo module, ICollecti } /// - /// Import the CIM functions from a module... + /// Import the CIM keywords from a module... /// /// /// From e086111f5f24a8b6b6ff96534117b30830cda9c0 Mon Sep 17 00:00:00 2001 From: anmenaga Date: Wed, 12 Aug 2020 13:26:28 -0700 Subject: [PATCH 03/64] Changed to default RunspaceMode.CurrentRunspace in internal class JsonDeserializerDeserializeClasses --- .../DscSupport/JsonDeserializer.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/System.Management.Automation/DscSupport/JsonDeserializer.cs b/src/System.Management.Automation/DscSupport/JsonDeserializer.cs index db38badf019..2fa4d977888 100755 --- a/src/System.Management.Automation/DscSupport/JsonDeserializer.cs +++ b/src/System.Management.Automation/DscSupport/JsonDeserializer.cs @@ -27,10 +27,10 @@ public static JsonDeserializer Create() /// /// Returns schema of Cim classes from specified json file /// - public IEnumerable DeserializeClasses(string json) + public IEnumerable DeserializeClasses(string json, RunspaceMode runspaceMode = RunspaceMode.CurrentRunspace) { IEnumerable result = null; - using (var powerShell = System.Management.Automation.PowerShell.Create(RunspaceMode.NewRunspace)) + using (var powerShell = System.Management.Automation.PowerShell.Create(runspaceMode)) { powerShell.AddCommand("ConvertFrom-Json"); powerShell.AddParameter("InputObject", json); From 93244901f3420fe9e31b954a86fb2cdc49b24218 Mon Sep 17 00:00:00 2001 From: anmenaga Date: Wed, 12 Aug 2020 13:29:43 -0700 Subject: [PATCH 04/64] Updated ReadCimSchemaMof in MofDscClassCache.cs --- .../DscSupport/MofDscClassCache.cs | 55 +++---------------- 1 file changed, 9 insertions(+), 46 deletions(-) diff --git a/src/System.Management.Automation/DscSupport/MofDscClassCache.cs b/src/System.Management.Automation/DscSupport/MofDscClassCache.cs index ba83b51e96b..fb885d347ab 100755 --- a/src/System.Management.Automation/DscSupport/MofDscClassCache.cs +++ b/src/System.Management.Automation/DscSupport/MofDscClassCache.cs @@ -505,54 +505,17 @@ internal static bool CimPropertyIsInherited(string propertyName, CimClass parent } } - internal static List PrepareCimMofForJsonConvertion(List classes) - { - var result = new List(); - foreach(var c in classes) - { - var cpso = new PSObject(); - cpso.Properties.Add(new PSNoteProperty("CimSystemProperties", c.CimSystemProperties)); - cpso.Properties.Add(new PSNoteProperty("CimSuperClassName", c.CimSuperClassName)); - cpso.Properties.Add(new PSNoteProperty("CimClassQualifiers", c.CimClassQualifiers)); - - var properties = new List(); - - foreach(var p in c.CimClassProperties) - { - if (!CimPropertyIsInherited(p.Name, c.CimSuperClass)) - { - properties.Add(p); - } - } - - cpso.Properties.Add(new PSNoteProperty("CimClassProperties", properties)); - - result.Add(cpso); - } - - return result; - } - - internal static void ConvertCimMofToJson(string mofPath) + /// + /// Reads CIM MOF schema file and returns classes defined in it + /// + /// + /// Path to CIM MOF schema file for reading + /// + /// List of classes from MOF schema file + public static List ReadCimSchemaMof(string mofPath) { var parser = new Microsoft.PowerShell.DesiredStateConfiguration.Mof.CimDSCParser(MyClassCallback); - List mofClasses = parser.ParseSchemaMof(mofPath); - var jsonClasses = PrepareCimMofForJsonConvertion(mofClasses); - - string jsonPath = mofPath.Substring(0, mofPath.LastIndexOf('.')) + ".json"; - using (var powerShell = System.Management.Automation.PowerShell.Create(RunspaceMode.CurrentRunspace)) - { - powerShell.AddCommand("ConvertTo-Json"); - powerShell.AddParameter("InputObject", jsonClasses); - powerShell.AddParameter("Depth", 100); - powerShell.AddParameter("EnumsAsStrings", true); - - powerShell.AddCommand("Out-File"); - powerShell.AddParameter("Path", jsonPath); - powerShell.AddParameter("Force", true); - - powerShell.Invoke(); - } + return parser.ParseSchemaMof(mofPath); } /// From 4a6f8181b8ad11d00f0a07d845dde6cbe81ba539 Mon Sep 17 00:00:00 2001 From: anmenaga Date: Wed, 12 Aug 2020 13:40:52 -0700 Subject: [PATCH 05/64] Moved convertion cmdlet from SMA to DSC module --- .../DscSupport/ConvertCimMofToJsonCommand.cs | 49 ------------------- .../engine/InitialSessionState.cs | 1 - 2 files changed, 50 deletions(-) delete mode 100755 src/System.Management.Automation/DscSupport/ConvertCimMofToJsonCommand.cs diff --git a/src/System.Management.Automation/DscSupport/ConvertCimMofToJsonCommand.cs b/src/System.Management.Automation/DscSupport/ConvertCimMofToJsonCommand.cs deleted file mode 100755 index 88ea8cd3b96..00000000000 --- a/src/System.Management.Automation/DscSupport/ConvertCimMofToJsonCommand.cs +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -using System; -using System.Collections; -using System.Collections.Generic; -using System.IO; -using System.Management.Automation; -using System.Text; - -namespace Microsoft.PowerShell.DesiredStateConfiguration.Internal -{ - /// - /// Convert-CimMofToJson cmdlet implementation - /// - [Cmdlet(VerbsData.Convert, "CimMofToJson")] - public sealed class ConvertCimMofToJsonCommand : Cmdlet - { - /// - /// Top level directory to serach for .mof files - /// - /// Test - [Parameter(ValueFromPipeline = true, Position = 0)] - [ValidateNotNullOrEmpty] - public string Directory { get; set; } - - /// - /// Main cmdlet method - /// - protected override void ProcessRecord() - { - // Mof parser uses DSC_HOME env var which is usually set by PSDesiredStateConfiguration module - // Because this cmlet can be run without loading PSDesiredStateConfiguration module, we are setting this env var here. - string varName = "DSC_HOME"; - if (string.IsNullOrEmpty(Environment.GetEnvironmentVariable(varName, EnvironmentVariableTarget.Process))) - { - var pshome = Utils.DefaultPowerShellAppBase; - var dsc_home = Path.Combine(pshome, "Modules", "PSDesiredStateConfiguration", "Configuration"); - Environment.SetEnvironmentVariable(varName, dsc_home, EnvironmentVariableTarget.Process); - } - - Mof.DscClassCache.Initialize(); - foreach(var mofPath in System.IO.Directory.GetFiles(this.Directory, "*.mof", SearchOption.AllDirectories)) - { - Mof.DscClassCache.ConvertCimMofToJson(mofPath); - } - } - } -} diff --git a/src/System.Management.Automation/engine/InitialSessionState.cs b/src/System.Management.Automation/engine/InitialSessionState.cs index 2b7f1dcd375..0d2e1274aa6 100644 --- a/src/System.Management.Automation/engine/InitialSessionState.cs +++ b/src/System.Management.Automation/engine/InitialSessionState.cs @@ -5321,7 +5321,6 @@ private static void InitializeCoreCmdletsAndProviders( { { "Add-History", new SessionStateCmdletEntry("Add-History", typeof(AddHistoryCommand), helpFile) }, { "Clear-History", new SessionStateCmdletEntry("Clear-History", typeof(ClearHistoryCommand), helpFile) }, - { "Convert-CimMofToJson", new SessionStateCmdletEntry("Convert-CimMofToJson", typeof(Microsoft.PowerShell.DesiredStateConfiguration.Internal.ConvertCimMofToJsonCommand), helpFile) }, { "Debug-Job", new SessionStateCmdletEntry("Debug-Job", typeof(DebugJobCommand), helpFile) }, #if !UNIX { "Disable-PSRemoting", new SessionStateCmdletEntry("Disable-PSRemoting", typeof(DisablePSRemotingCommand), helpFile) }, From 5b511137eea35f87aa35f8811318595554eb7b9e Mon Sep 17 00:00:00 2001 From: anmenaga Date: Wed, 12 Aug 2020 13:47:51 -0700 Subject: [PATCH 06/64] Fixed runspace usage of JsonDeserializer.DeserializeClasses --- .../DscSupport/JsonCimDSCParser.cs | 4 ++-- .../DscSupport/JsonDeserializer.cs | 3 ++- .../DscSupport/JsonDscClassCache.cs | 5 +++-- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/System.Management.Automation/DscSupport/JsonCimDSCParser.cs b/src/System.Management.Automation/DscSupport/JsonCimDSCParser.cs index 71e8c8f9104..741e4e2df31 100755 --- a/src/System.Management.Automation/DscSupport/JsonCimDSCParser.cs +++ b/src/System.Management.Automation/DscSupport/JsonCimDSCParser.cs @@ -24,7 +24,7 @@ internal CimDSCParser() _json_deserializer = JsonDeserializer.Create(); } - internal List ParseSchemaJson(string filePath) + internal List ParseSchemaJson(string filePath, bool useNewRunspace = false) { string json = System.IO.File.ReadAllText(filePath); try @@ -36,7 +36,7 @@ internal List ParseSchemaJson(string filePath) fileNameDefiningClass = fileNameDefiningClass.Substring(0, dotIndex); } - var result = new List(_json_deserializer.DeserializeClasses(json)); + var result = new List(_json_deserializer.DeserializeClasses(json, useNewRunspace)); foreach (dynamic c in result) { string superClassName = c.CimSuperClassName; diff --git a/src/System.Management.Automation/DscSupport/JsonDeserializer.cs b/src/System.Management.Automation/DscSupport/JsonDeserializer.cs index 2fa4d977888..fdd9dc1f56d 100755 --- a/src/System.Management.Automation/DscSupport/JsonDeserializer.cs +++ b/src/System.Management.Automation/DscSupport/JsonDeserializer.cs @@ -27,9 +27,10 @@ public static JsonDeserializer Create() /// /// Returns schema of Cim classes from specified json file /// - public IEnumerable DeserializeClasses(string json, RunspaceMode runspaceMode = RunspaceMode.CurrentRunspace) + public IEnumerable DeserializeClasses(string json, bool useNewRunspace = false) { IEnumerable result = null; + RunspaceMode runspaceMode = useNewRunspace ? RunspaceMode.NewRunspace : RunspaceMode.CurrentRunspace; using (var powerShell = System.Management.Automation.PowerShell.Create(runspaceMode)) { powerShell.AddCommand("ConvertFrom-Json"); diff --git a/src/System.Management.Automation/DscSupport/JsonDscClassCache.cs b/src/System.Management.Automation/DscSupport/JsonDscClassCache.cs index 255c9ed882f..dbfc5b23476 100755 --- a/src/System.Management.Automation/DscSupport/JsonDscClassCache.cs +++ b/src/System.Management.Automation/DscSupport/JsonDscClassCache.cs @@ -480,8 +480,9 @@ private static void WriteWarning(string warning) /// Parses json file without adding it to caches or creating dynamic keywords /// /// Path to json file + /// If True PowerShell will use a fresh runspace to do Json deserialization /// List of classes from json file - public static List ReadClassesFromJson(string jsonFilePath) + public static List ReadClassesFromJson(string jsonFilePath, bool useNewRunspace = false) { if (! jsonFilePath.EndsWith(".json", StringComparison.InvariantCultureIgnoreCase)) { @@ -490,7 +491,7 @@ public static List ReadClassesFromJson(string jsonFilePath) } var parser = new Microsoft.PowerShell.DesiredStateConfiguration.CimDSCParser(); - List classes = parser.ParseSchemaJson(jsonFilePath); + List classes = parser.ParseSchemaJson(jsonFilePath, useNewRunspace); return classes; } From 5fbe162bc9c485c3fd0f95c6be486f9d3ff161f9 Mon Sep 17 00:00:00 2001 From: anmenaga Date: Wed, 12 Aug 2020 14:52:56 -0700 Subject: [PATCH 07/64] Fixed PSModulePath handling for DeserializeClasses(RunspaceMode.NewRunspace) --- .../DscSupport/JsonDeserializer.cs | 23 +++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/src/System.Management.Automation/DscSupport/JsonDeserializer.cs b/src/System.Management.Automation/DscSupport/JsonDeserializer.cs index fdd9dc1f56d..4a7fb809543 100755 --- a/src/System.Management.Automation/DscSupport/JsonDeserializer.cs +++ b/src/System.Management.Automation/DscSupport/JsonDeserializer.cs @@ -5,6 +5,7 @@ using System.Collections; using System.Collections.Generic; using System.Management.Automation; +using System.Management.Automation.Runspaces; namespace Microsoft.PowerShell.DesiredStateConfiguration { @@ -30,8 +31,26 @@ public static JsonDeserializer Create() public IEnumerable DeserializeClasses(string json, bool useNewRunspace = false) { IEnumerable result = null; - RunspaceMode runspaceMode = useNewRunspace ? RunspaceMode.NewRunspace : RunspaceMode.CurrentRunspace; - using (var powerShell = System.Management.Automation.PowerShell.Create(runspaceMode)) + System.Management.Automation.PowerShell powerShell = null; + + if (useNewRunspace) + { + // currently using RunspaceMode.NewRunspace will reset PSModulePath env var for the entire process + // this is something we want to avoid in DSC GuestConfigAgent scenario, so we use following workaround + var s_iss = InitialSessionState.CreateDefault(); + s_iss.EnvironmentVariables.Add( + new SessionStateVariableEntry( + "PSModulePath", + Environment.GetEnvironmentVariable("PSModulePath"), + description: null)); + powerShell = System.Management.Automation.PowerShell.Create(s_iss); + } + else + { + powerShell = System.Management.Automation.PowerShell.Create(RunspaceMode.NewRunspace); + } + + using (powerShell) { powerShell.AddCommand("ConvertFrom-Json"); powerShell.AddParameter("InputObject", json); From 3cf7abe7ad9065da5931508c645f1cf8311a90e9 Mon Sep 17 00:00:00 2001 From: anmenaga Date: Wed, 12 Aug 2020 15:02:06 -0700 Subject: [PATCH 08/64] Fixed typo --- src/System.Management.Automation/DscSupport/JsonDeserializer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/System.Management.Automation/DscSupport/JsonDeserializer.cs b/src/System.Management.Automation/DscSupport/JsonDeserializer.cs index 4a7fb809543..11bcc381491 100755 --- a/src/System.Management.Automation/DscSupport/JsonDeserializer.cs +++ b/src/System.Management.Automation/DscSupport/JsonDeserializer.cs @@ -47,7 +47,7 @@ public IEnumerable DeserializeClasses(string json, bool useNewRunspace } else { - powerShell = System.Management.Automation.PowerShell.Create(RunspaceMode.NewRunspace); + powerShell = System.Management.Automation.PowerShell.Create(RunspaceMode.CurrentRunspace); } using (powerShell) From 06bcaff4285839709e6792efa60aa4e6b2f363fd Mon Sep 17 00:00:00 2001 From: anmenaga Date: Mon, 17 Aug 2020 12:39:26 -0700 Subject: [PATCH 09/64] Changed mof code to be using original namespace --- .../DscSupport/JsonCimDSCParser.cs | 2 +- .../DscSupport/JsonDeserializer.cs | 2 +- .../DscSupport/JsonDscClassCache.cs | 234 ++++++++++++++++-- .../DscSupport/MofCimDSCParser.cs | 4 +- .../DscSupport/MofDscClassCache.cs | 18 +- 5 files changed, 231 insertions(+), 29 deletions(-) diff --git a/src/System.Management.Automation/DscSupport/JsonCimDSCParser.cs b/src/System.Management.Automation/DscSupport/JsonCimDSCParser.cs index 741e4e2df31..fa5b2a2f60a 100755 --- a/src/System.Management.Automation/DscSupport/JsonCimDSCParser.cs +++ b/src/System.Management.Automation/DscSupport/JsonCimDSCParser.cs @@ -10,7 +10,7 @@ using System.Management.Automation; using System.Security; -namespace Microsoft.PowerShell.DesiredStateConfiguration +namespace Microsoft.PowerShell.DesiredStateConfiguration.Json { /// /// Class that does high level Cim schema parsing diff --git a/src/System.Management.Automation/DscSupport/JsonDeserializer.cs b/src/System.Management.Automation/DscSupport/JsonDeserializer.cs index 11bcc381491..1c0c6ea78f4 100755 --- a/src/System.Management.Automation/DscSupport/JsonDeserializer.cs +++ b/src/System.Management.Automation/DscSupport/JsonDeserializer.cs @@ -7,7 +7,7 @@ using System.Management.Automation; using System.Management.Automation.Runspaces; -namespace Microsoft.PowerShell.DesiredStateConfiguration +namespace Microsoft.PowerShell.DesiredStateConfiguration.Json { internal class JsonDeserializer { diff --git a/src/System.Management.Automation/DscSupport/JsonDscClassCache.cs b/src/System.Management.Automation/DscSupport/JsonDscClassCache.cs index dbfc5b23476..c262ed74904 100755 --- a/src/System.Management.Automation/DscSupport/JsonDscClassCache.cs +++ b/src/System.Management.Automation/DscSupport/JsonDscClassCache.cs @@ -19,7 +19,7 @@ using Microsoft.PowerShell.Commands; -namespace Microsoft.PowerShell.DesiredStateConfiguration.Internal +namespace Microsoft.PowerShell.DesiredStateConfiguration.Internal.Json { /// /// @@ -490,7 +490,7 @@ public static List ReadClassesFromJson(string jsonFilePath, bool useNe return null; } - var parser = new Microsoft.PowerShell.DesiredStateConfiguration.CimDSCParser(); + var parser = new Microsoft.PowerShell.DesiredStateConfiguration.Json.CimDSCParser(); List classes = parser.ParseSchemaJson(jsonFilePath, useNewRunspace); return classes; } @@ -518,7 +518,7 @@ public static List ImportClasses(string path, Tuple m s_tracer.WriteLine("DSC ClassCache: importing file: {0}", path); - var parser = new Microsoft.PowerShell.DesiredStateConfiguration.CimDSCParser(); + var parser = new Microsoft.PowerShell.DesiredStateConfiguration.Json.CimDSCParser(); List classes = null; try @@ -2160,7 +2160,7 @@ private static bool ImportKeywordsFromScriptFile(string fileName, PSModuleInfo m } var result = false; - var parser = new CimDSCParser(); + var parser = new Microsoft.PowerShell.DesiredStateConfiguration.Json.CimDSCParser(); const WildcardOptions wildcardOptions = WildcardOptions.IgnoreCase | WildcardOptions.CultureInvariant; IEnumerable patternList = SessionStateUtilities.CreateWildcardsFromStrings(module._declaredDscResourceExports, wildcardOptions); @@ -2246,6 +2246,208 @@ private static bool IsSameNestedObject(dynamic oldClass, dynamic newClass) return true; } + private static string MapAttributesToMof(string[] enumNames, IEnumerable customAttributes, string embeddedInstanceType) + { + var sb = new StringBuilder(); + + sb.Append("["); + bool needComma = false; + foreach (var attr in customAttributes) + { + var dscProperty = attr as DscPropertyAttribute; + if (dscProperty != null) + { + if (dscProperty.Key) + { + sb.AppendFormat(CultureInfo.InvariantCulture, "{0}key", needComma ? ", " : string.Empty); + needComma = true; + } + + if (dscProperty.Mandatory) + { + sb.AppendFormat(CultureInfo.InvariantCulture, "{0}required", needComma ? ", " : string.Empty); + needComma = true; + } + + if (dscProperty.NotConfigurable) + { + sb.AppendFormat(CultureInfo.InvariantCulture, "{0}read", needComma ? ", " : string.Empty); + needComma = true; + } + + continue; + } + + var validateSet = attr as ValidateSetAttribute; + if (validateSet != null) + { + bool valueMapComma = false; + StringBuilder sbValues = new StringBuilder(", Values{"); + sb.AppendFormat(CultureInfo.InvariantCulture, "{0}ValueMap{{", needComma ? ", " : string.Empty); + needComma = true; + + foreach (var value in validateSet.ValidValues) + { + sb.AppendFormat(CultureInfo.InvariantCulture, "{0}\"{1}\"", valueMapComma ? ", " : string.Empty, value); + sbValues.AppendFormat(CultureInfo.InvariantCulture, "{0}\"{1}\"", valueMapComma ? ", " : string.Empty, value); + valueMapComma = true; + } + + sb.Append("}"); + sb.Append(sbValues); + sb.Append("}"); + } + } + + // Default is write - skipped if we already have some attributes + if (sb.Length == 1) + { + sb.Append("write"); + needComma = true; + } + + if (enumNames != null) + { + sb.AppendFormat(CultureInfo.InvariantCulture, "{0}ValueMap{{", needComma ? ", " : string.Empty); + needComma = false; + foreach (var name in enumNames) + { + sb.AppendFormat(CultureInfo.InvariantCulture, "{0}\"{1}\"", needComma ? ", " : string.Empty, name); + needComma = true; + } + + sb.Append("}, Values{"); + needComma = false; + foreach (var name in enumNames) + { + sb.AppendFormat(CultureInfo.InvariantCulture, "{0}\"{1}\"", needComma ? ", " : string.Empty, name); + needComma = true; + } + + sb.Append("}"); + } + else if (embeddedInstanceType != null) + { + sb.AppendFormat(CultureInfo.InvariantCulture, "{0}EmbeddedInstance(\"{1}\")", needComma ? ", " : string.Empty, embeddedInstanceType); + } + + sb.Append("]"); + return sb.ToString(); + } + + /// + /// + /// + /// + public static string GenerateMofForType(Type type) + { + var embeddedInstanceTypes = new List(); + var sb = new StringBuilder(); + + GenerateMofForType(type, sb, embeddedInstanceTypes); + var visitedInstances = new List(); + visitedInstances.Add(type); + ProcessEmbeddedInstanceTypes(embeddedInstanceTypes, visitedInstances, sb); + + return sb.ToString(); + } + + private static void ProcessEmbeddedInstanceTypes(List embeddedInstanceTypes, List visitedInstances, StringBuilder sb) + { + StringBuilder nestedSb = null; + + while (embeddedInstanceTypes.Count > 0) + { + if (nestedSb == null) + { + nestedSb = new StringBuilder(); + } + else + { + nestedSb.Clear(); + } + + var batchedTypes = embeddedInstanceTypes.Where(x => !visitedInstances.Contains(x)).ToArray(); + embeddedInstanceTypes.Clear(); + + for (int i = batchedTypes.Length - 1; i >= 0; i--) + { + visitedInstances.Add(batchedTypes[i]); + var type = batchedTypes[i] as Type; + if (type != null) + { + GenerateMofForType(type, nestedSb, embeddedInstanceTypes); + } + + nestedSb.Append('\n'); + } + + sb.Insert(0, nestedSb.ToString()); + } + } + + private static void GenerateMofForType(Type type, StringBuilder sb, List embeddedInstanceTypes) + { + var className = type.Name; + // Friendly name is required by module validator to verify resource instance against the exclusive resource name list. + sb.AppendFormat(CultureInfo.InvariantCulture, "[ClassVersion(\"1.0.0\"), FriendlyName(\"{0}\")]\nclass {0}", className); + + if (type.GetCustomAttributes().Any()) + { + sb.Append(" : OMI_BaseResource"); + } + + sb.Append("\n{\n"); + + ProcessMembers(type, sb, embeddedInstanceTypes, className); + sb.Append("};"); + } + + private static void ProcessMembers(Type type, StringBuilder sb, List embeddedInstanceTypes, string className) + { + foreach (var member in type.GetMembers(BindingFlags.Instance | BindingFlags.Public).Where(m => m is PropertyInfo || m is FieldInfo)) + { + if (member.CustomAttributes.All(cad => cad.AttributeType != typeof(DscPropertyAttribute))) + { + continue; + } + + Type memberType; + var propertyInfo = member as PropertyInfo; + if (propertyInfo == null) + { + var fieldInfo = (FieldInfo)member; + memberType = fieldInfo.FieldType; + } + else + { + if (propertyInfo.GetSetMethod() == null) + { + continue; + } + + memberType = propertyInfo.PropertyType; + } + + // TODO - validate type and name + bool isArrayType; + string embeddedInstanceType; + string mofType = MapTypeToMofType(memberType, member.Name, className, out isArrayType, out embeddedInstanceType, + embeddedInstanceTypes); + string arrayAffix = isArrayType ? "[]" : string.Empty; + + var enumNames = memberType.IsEnum + ? Enum.GetNames(memberType) + : null; + sb.AppendFormat(CultureInfo.InvariantCulture, + " {0}{1} {2}{3};\n", + MapAttributesToMof(enumNames, member.GetCustomAttributes(true), embeddedInstanceType), + mofType, + member.Name, + arrayAffix); + } + } + internal static string MapTypeToMofType(Type type, string memberName, string className, out bool isArrayType, out string embeddedInstanceType, List embeddedInstanceTypes) { isArrayType = false; @@ -2949,7 +3151,7 @@ function Test-DependsOn if ($DependsOnVar -notmatch '^\[[a-z]\w*\][a-z_0-9][a-z_0-9\p{Zs}\.\\-]*$') { Update-ConfigurationErrorCount - Write-Error -ErrorRecord ([Microsoft.PowerShell.DesiredStateConfiguration.Internal.DscClassCache]::GetBadlyFormedRequiredResourceIdErrorRecord($DependsOnVar, $resourceId)) + Write-Error -ErrorRecord ([Microsoft.PowerShell.DesiredStateConfiguration.Json.Internal.DscClassCache]::GetBadlyFormedRequiredResourceIdErrorRecord($DependsOnVar, $resourceId)) } # Fix up DependsOn for nested names @@ -3025,7 +3227,7 @@ function Test-DependsOn if (Test-NodeResources $resourceId) { Update-ConfigurationErrorCount - Write-Error -ErrorRecord ([Microsoft.PowerShell.DesiredStateConfiguration.Internal.DscClassCache]::DuplicateResourceIdInNodeStatementErrorRecord($resourceId, (Get-PSCurrentConfigurationNode))) + Write-Error -ErrorRecord ([Microsoft.PowerShell.DesiredStateConfiguration.Json.Internal.DscClassCache]::DuplicateResourceIdInNodeStatementErrorRecord($resourceId, (Get-PSCurrentConfigurationNode))) } else { @@ -3041,7 +3243,7 @@ function Test-DependsOn if($null -ne $value['PsDscRunAsCredential']) { Update-ConfigurationErrorCount - Write-Error -ErrorRecord ([Microsoft.PowerShell.DesiredStateConfiguration.Internal.DscClassCache]::PsDscRunAsCredentialMergeErrorForCompositeResources($resourceId)) + Write-Error -ErrorRecord ([Microsoft.PowerShell.DesiredStateConfiguration.Json.Internal.DscClassCache]::PsDscRunAsCredentialMergeErrorForCompositeResources($resourceId)) } # Set the Value of RunAsCred to that of outer configuration else @@ -3060,14 +3262,14 @@ function Test-DependsOn if($value['RefreshMode'] -eq 'Pull' -and -not $value['ConfigurationSource']) { Update-ConfigurationErrorCount - Write-Error -ErrorRecord ([Microsoft.PowerShell.DesiredStateConfiguration.Internal.DscClassCache]::GetPullModeNeedConfigurationSource($resourceId)) + Write-Error -ErrorRecord ([Microsoft.PowerShell.DesiredStateConfiguration.Json.Internal.DscClassCache]::GetPullModeNeedConfigurationSource($resourceId)) } # Verify that RefreshMode is not Disabled for Partial configuration if($value['RefreshMode'] -eq 'Disabled') { Update-ConfigurationErrorCount - Write-Error -ErrorRecord ([Microsoft.PowerShell.DesiredStateConfiguration.Internal.DscClassCache]::DisabledRefreshModeNotValidForPartialConfig($resourceId)) + Write-Error -ErrorRecord ([Microsoft.PowerShell.DesiredStateConfiguration.Json.Internal.DscClassCache]::DisabledRefreshModeNotValidForPartialConfig($resourceId)) } if($null -ne $value['ConfigurationSource']) @@ -3088,7 +3290,7 @@ function Test-DependsOn if (($ExclusiveResource -notmatch '^[a-z][a-z_0-9]*\\[a-z][a-z_0-9]*$') -and ($ExclusiveResource -notmatch '^[a-z][a-z_0-9]*$') -and ($ExclusiveResource -notmatch '^[a-z][a-z_0-9]*\\\*$')) { Update-ConfigurationErrorCount - Write-Error -ErrorRecord ([Microsoft.PowerShell.DesiredStateConfiguration.Internal.DscClassCache]::GetBadlyFormedExclusiveResourceIdErrorRecord($ExclusiveResource, $resourceId)) + Write-Error -ErrorRecord ([Microsoft.PowerShell.DesiredStateConfiguration.Json.Internal.DscClassCache]::GetBadlyFormedExclusiveResourceIdErrorRecord($ExclusiveResource, $resourceId)) } } @@ -3116,7 +3318,7 @@ function Test-DependsOn { if ($OldMetaConfig -and (-not ($V1MetaConfigPropertyList -contains $key))) { - Write-Error -ErrorRecord ([Microsoft.PowerShell.DesiredStateConfiguration.Internal.DscClassCache]::InvalidLocalConfigurationManagerPropertyErrorRecord($key, ($V1MetaConfigPropertyList -join ', '))) + Write-Error -ErrorRecord ([Microsoft.PowerShell.DesiredStateConfiguration.Json.Internal.DscClassCache]::InvalidLocalConfigurationManagerPropertyErrorRecord($key, ($V1MetaConfigPropertyList -join ', '))) Update-ConfigurationErrorCount } # see if there is a list of allowed values for this property (similar to an enum) @@ -3126,7 +3328,7 @@ function Test-DependsOn { if(($null -eq $value[$key]) -and ($allowedValues -notcontains $value[$key])) { - Write-Error -ErrorRecord ([Microsoft.PowerShell.DesiredStateConfiguration.Internal.DscClassCache]::InvalidValueForPropertyErrorRecord($key, ""$($value[$key])"", $keywordData.Keyword, ($allowedValues -join ', '))) + Write-Error -ErrorRecord ([Microsoft.PowerShell.DesiredStateConfiguration.Json.Internal.DscClassCache]::InvalidValueForPropertyErrorRecord($key, ""$($value[$key])"", $keywordData.Keyword, ($allowedValues -join ', '))) Update-ConfigurationErrorCount } else @@ -3143,7 +3345,7 @@ function Test-DependsOn if($notAllowedValue) { $notAllowedValue = $notAllowedValue.Substring(0, $notAllowedValue.Length -2) - Write-Error -ErrorRecord ([Microsoft.PowerShell.DesiredStateConfiguration.Internal.DscClassCache]::UnsupportedValueForPropertyErrorRecord($key, $notAllowedValue, $keywordData.Keyword, ($allowedValues -join ', '))) + Write-Error -ErrorRecord ([Microsoft.PowerShell.DesiredStateConfiguration.Json.Internal.DscClassCache]::UnsupportedValueForPropertyErrorRecord($key, $notAllowedValue, $keywordData.Keyword, ($allowedValues -join ', '))) Update-ConfigurationErrorCount } } @@ -3156,7 +3358,7 @@ function Test-DependsOn $castedValue = $value[$key] -as [int] if((($castedValue -is [int]) -and (($castedValue -lt $keywordData.Properties[$key].Range.Item1) -or ($castedValue -gt $keywordData.Properties[$key].Range.Item2))) -or ($null -eq $castedValue)) { - Write-Error -ErrorRecord ([Microsoft.PowerShell.DesiredStateConfiguration.Internal.DscClassCache]::ValueNotInRangeErrorRecord($key, $keywordName, $value[$key], $keywordData.Properties[$key].Range.Item1, $keywordData.Properties[$key].Range.Item2)) + Write-Error -ErrorRecord ([Microsoft.PowerShell.DesiredStateConfiguration.Json.Internal.DscClassCache]::ValueNotInRangeErrorRecord($key, $keywordName, $value[$key], $keywordData.Properties[$key].Range.Item1, $keywordData.Properties[$key].Range.Item2)) Update-ConfigurationErrorCount } } @@ -3190,7 +3392,7 @@ function Test-DependsOn elseif ($keywordData.Properties[$key].Mandatory) { # If the property was mandatory but the user didn't provide a value, write and error. - Write-Error -ErrorRecord ([Microsoft.PowerShell.DesiredStateConfiguration.Internal.DscClassCache]::MissingValueForMandatoryPropertyErrorRecord($keywordData.Keyword, $keywordData.Properties[$key].TypeConstraint, $Key)) + Write-Error -ErrorRecord ([Microsoft.PowerShell.DesiredStateConfiguration.Json.Internal.DscClassCache]::MissingValueForMandatoryPropertyErrorRecord($keywordData.Keyword, $keywordData.Properties[$key].TypeConstraint, $Key)) Update-ConfigurationErrorCount } @@ -3243,7 +3445,7 @@ function Test-DependsOn if($canonicalizedValue['DebugMode'] -and @($canonicalizedValue['DebugMode']).Length -gt 1) { # we only allow one value for debug mode now. - Write-Error -ErrorRecord ([Microsoft.PowerShell.DesiredStateConfiguration.Internal.DscClassCache]::DebugModeShouldHaveOneValue()) + Write-Error -ErrorRecord ([Microsoft.PowerShell.DesiredStateConfiguration.Json.Internal.DscClassCache]::DebugModeShouldHaveOneValue()) Update-ConfigurationErrorCount } } diff --git a/src/System.Management.Automation/DscSupport/MofCimDSCParser.cs b/src/System.Management.Automation/DscSupport/MofCimDSCParser.cs index 64623d55d43..22584a8805e 100755 --- a/src/System.Management.Automation/DscSupport/MofCimDSCParser.cs +++ b/src/System.Management.Automation/DscSupport/MofCimDSCParser.cs @@ -22,7 +22,7 @@ using Microsoft.Management.Infrastructure.Serialization; using Microsoft.PowerShell.Commands; -namespace Microsoft.PowerShell.DesiredStateConfiguration.Internal.Mof +namespace Microsoft.PowerShell.DesiredStateConfiguration.Internal { /// /// @@ -272,7 +272,7 @@ private static object ConvertCimInstancePsCredential(string providerName, CimIns } } -namespace Microsoft.PowerShell.DesiredStateConfiguration.Mof +namespace Microsoft.PowerShell.DesiredStateConfiguration { /// /// To make it easier to specify -ConfigurationData parameter, we add an ArgumentTransformationAttribute here. diff --git a/src/System.Management.Automation/DscSupport/MofDscClassCache.cs b/src/System.Management.Automation/DscSupport/MofDscClassCache.cs index fb885d347ab..29ab3c1be62 100755 --- a/src/System.Management.Automation/DscSupport/MofDscClassCache.cs +++ b/src/System.Management.Automation/DscSupport/MofDscClassCache.cs @@ -22,7 +22,7 @@ using Microsoft.Management.Infrastructure.Serialization; using Microsoft.PowerShell.Commands; -namespace Microsoft.PowerShell.DesiredStateConfiguration.Internal.Mof +namespace Microsoft.PowerShell.DesiredStateConfiguration.Internal { /// /// @@ -514,7 +514,7 @@ internal static bool CimPropertyIsInherited(string propertyName, CimClass parent /// List of classes from MOF schema file public static List ReadCimSchemaMof(string mofPath) { - var parser = new Microsoft.PowerShell.DesiredStateConfiguration.Mof.CimDSCParser(MyClassCallback); + var parser = new Microsoft.PowerShell.DesiredStateConfiguration.CimDSCParser(MyClassCallback); return parser.ParseSchemaMof(mofPath); } @@ -535,7 +535,7 @@ public static List ImportClasses(string path, Tuple m s_tracer.WriteLine("DSC ClassCache: importing file: {0}", path); - var parser = new Microsoft.PowerShell.DesiredStateConfiguration.Mof.CimDSCParser(MyClassCallback); + var parser = new Microsoft.PowerShell.DesiredStateConfiguration.CimDSCParser(MyClassCallback); List classes = null; try @@ -796,7 +796,7 @@ public static List ImportInstances(string path) throw PSTraceSource.NewArgumentNullException(nameof(path)); } - var parser = new Microsoft.PowerShell.DesiredStateConfiguration.Mof.CimDSCParser(MyClassCallback); + var parser = new Microsoft.PowerShell.DesiredStateConfiguration.CimDSCParser(MyClassCallback); return parser.ParseInstanceMof(path); } @@ -821,7 +821,7 @@ public static List ImportInstances(string path, int schemaValidatio throw new IndexOutOfRangeException("schemaValidationOption"); } - var parser = new Microsoft.PowerShell.DesiredStateConfiguration.Mof.CimDSCParser(MyClassCallback, (Microsoft.Management.Infrastructure.Serialization.MofDeserializerSchemaValidationOption)schemaValidationOption); + var parser = new Microsoft.PowerShell.DesiredStateConfiguration.CimDSCParser(MyClassCallback, (Microsoft.Management.Infrastructure.Serialization.MofDeserializerSchemaValidationOption)schemaValidationOption); return parser.ParseInstanceMof(path); } @@ -838,7 +838,7 @@ public static void ValidateInstanceText(string instanceText) throw PSTraceSource.NewArgumentNullException(nameof(instanceText)); } - var parser = new Microsoft.PowerShell.DesiredStateConfiguration.Mof.CimDSCParser(MyClassCallback); + var parser = new Microsoft.PowerShell.DesiredStateConfiguration.CimDSCParser(MyClassCallback); parser.ValidateInstanceText(instanceText); } @@ -2218,7 +2218,7 @@ private static bool ImportKeywordsFromScriptFile(string fileName, PSModuleInfo m } var result = false; - var parser = new Microsoft.PowerShell.DesiredStateConfiguration.Mof.CimDSCParser(MyClassCallback); + var parser = new Microsoft.PowerShell.DesiredStateConfiguration.CimDSCParser(MyClassCallback); const WildcardOptions wildcardOptions = WildcardOptions.IgnoreCase | WildcardOptions.CultureInvariant; IEnumerable patternList = SessionStateUtilities.CreateWildcardsFromStrings(module._declaredDscResourceExports, wildcardOptions); @@ -2699,7 +2699,7 @@ private static bool ImportKeywordsFromAssembly(PSModuleInfo module, { bool result = false; - var parser = new Microsoft.PowerShell.DesiredStateConfiguration.Mof.CimDSCParser(MyClassCallback); + var parser = new Microsoft.PowerShell.DesiredStateConfiguration.CimDSCParser(MyClassCallback); IEnumerable resourceDefinitions = assembly.GetTypes().Where(t => t.GetCustomAttributes().Any()); @@ -2729,7 +2729,7 @@ private static bool ImportKeywordsFromAssembly(PSModuleInfo module, } private static void ProcessMofForDynamicKeywords(PSModuleInfo module, ICollection resourcesFound, - Dictionary functionsToDefine, Microsoft.PowerShell.DesiredStateConfiguration.Mof.CimDSCParser parser, string mof, DSCResourceRunAsCredential runAsBehavior) + Dictionary functionsToDefine, Microsoft.PowerShell.DesiredStateConfiguration.CimDSCParser parser, string mof, DSCResourceRunAsCredential runAsBehavior) { foreach (var c in parser.ParseSchemaMofFileBuffer(mof)) { From 5038ab771ffd9c04e0323ec24a6dd1c7fe325f47 Mon Sep 17 00:00:00 2001 From: anmenaga Date: Mon, 17 Aug 2020 12:47:51 -0700 Subject: [PATCH 10/64] Removed unused GenerateMofForType from JsonDscClassCache --- .../DscSupport/JsonDscClassCache.cs | 202 ------------------ 1 file changed, 202 deletions(-) diff --git a/src/System.Management.Automation/DscSupport/JsonDscClassCache.cs b/src/System.Management.Automation/DscSupport/JsonDscClassCache.cs index c262ed74904..d3d67cc3a62 100755 --- a/src/System.Management.Automation/DscSupport/JsonDscClassCache.cs +++ b/src/System.Management.Automation/DscSupport/JsonDscClassCache.cs @@ -2246,208 +2246,6 @@ private static bool IsSameNestedObject(dynamic oldClass, dynamic newClass) return true; } - private static string MapAttributesToMof(string[] enumNames, IEnumerable customAttributes, string embeddedInstanceType) - { - var sb = new StringBuilder(); - - sb.Append("["); - bool needComma = false; - foreach (var attr in customAttributes) - { - var dscProperty = attr as DscPropertyAttribute; - if (dscProperty != null) - { - if (dscProperty.Key) - { - sb.AppendFormat(CultureInfo.InvariantCulture, "{0}key", needComma ? ", " : string.Empty); - needComma = true; - } - - if (dscProperty.Mandatory) - { - sb.AppendFormat(CultureInfo.InvariantCulture, "{0}required", needComma ? ", " : string.Empty); - needComma = true; - } - - if (dscProperty.NotConfigurable) - { - sb.AppendFormat(CultureInfo.InvariantCulture, "{0}read", needComma ? ", " : string.Empty); - needComma = true; - } - - continue; - } - - var validateSet = attr as ValidateSetAttribute; - if (validateSet != null) - { - bool valueMapComma = false; - StringBuilder sbValues = new StringBuilder(", Values{"); - sb.AppendFormat(CultureInfo.InvariantCulture, "{0}ValueMap{{", needComma ? ", " : string.Empty); - needComma = true; - - foreach (var value in validateSet.ValidValues) - { - sb.AppendFormat(CultureInfo.InvariantCulture, "{0}\"{1}\"", valueMapComma ? ", " : string.Empty, value); - sbValues.AppendFormat(CultureInfo.InvariantCulture, "{0}\"{1}\"", valueMapComma ? ", " : string.Empty, value); - valueMapComma = true; - } - - sb.Append("}"); - sb.Append(sbValues); - sb.Append("}"); - } - } - - // Default is write - skipped if we already have some attributes - if (sb.Length == 1) - { - sb.Append("write"); - needComma = true; - } - - if (enumNames != null) - { - sb.AppendFormat(CultureInfo.InvariantCulture, "{0}ValueMap{{", needComma ? ", " : string.Empty); - needComma = false; - foreach (var name in enumNames) - { - sb.AppendFormat(CultureInfo.InvariantCulture, "{0}\"{1}\"", needComma ? ", " : string.Empty, name); - needComma = true; - } - - sb.Append("}, Values{"); - needComma = false; - foreach (var name in enumNames) - { - sb.AppendFormat(CultureInfo.InvariantCulture, "{0}\"{1}\"", needComma ? ", " : string.Empty, name); - needComma = true; - } - - sb.Append("}"); - } - else if (embeddedInstanceType != null) - { - sb.AppendFormat(CultureInfo.InvariantCulture, "{0}EmbeddedInstance(\"{1}\")", needComma ? ", " : string.Empty, embeddedInstanceType); - } - - sb.Append("]"); - return sb.ToString(); - } - - /// - /// - /// - /// - public static string GenerateMofForType(Type type) - { - var embeddedInstanceTypes = new List(); - var sb = new StringBuilder(); - - GenerateMofForType(type, sb, embeddedInstanceTypes); - var visitedInstances = new List(); - visitedInstances.Add(type); - ProcessEmbeddedInstanceTypes(embeddedInstanceTypes, visitedInstances, sb); - - return sb.ToString(); - } - - private static void ProcessEmbeddedInstanceTypes(List embeddedInstanceTypes, List visitedInstances, StringBuilder sb) - { - StringBuilder nestedSb = null; - - while (embeddedInstanceTypes.Count > 0) - { - if (nestedSb == null) - { - nestedSb = new StringBuilder(); - } - else - { - nestedSb.Clear(); - } - - var batchedTypes = embeddedInstanceTypes.Where(x => !visitedInstances.Contains(x)).ToArray(); - embeddedInstanceTypes.Clear(); - - for (int i = batchedTypes.Length - 1; i >= 0; i--) - { - visitedInstances.Add(batchedTypes[i]); - var type = batchedTypes[i] as Type; - if (type != null) - { - GenerateMofForType(type, nestedSb, embeddedInstanceTypes); - } - - nestedSb.Append('\n'); - } - - sb.Insert(0, nestedSb.ToString()); - } - } - - private static void GenerateMofForType(Type type, StringBuilder sb, List embeddedInstanceTypes) - { - var className = type.Name; - // Friendly name is required by module validator to verify resource instance against the exclusive resource name list. - sb.AppendFormat(CultureInfo.InvariantCulture, "[ClassVersion(\"1.0.0\"), FriendlyName(\"{0}\")]\nclass {0}", className); - - if (type.GetCustomAttributes().Any()) - { - sb.Append(" : OMI_BaseResource"); - } - - sb.Append("\n{\n"); - - ProcessMembers(type, sb, embeddedInstanceTypes, className); - sb.Append("};"); - } - - private static void ProcessMembers(Type type, StringBuilder sb, List embeddedInstanceTypes, string className) - { - foreach (var member in type.GetMembers(BindingFlags.Instance | BindingFlags.Public).Where(m => m is PropertyInfo || m is FieldInfo)) - { - if (member.CustomAttributes.All(cad => cad.AttributeType != typeof(DscPropertyAttribute))) - { - continue; - } - - Type memberType; - var propertyInfo = member as PropertyInfo; - if (propertyInfo == null) - { - var fieldInfo = (FieldInfo)member; - memberType = fieldInfo.FieldType; - } - else - { - if (propertyInfo.GetSetMethod() == null) - { - continue; - } - - memberType = propertyInfo.PropertyType; - } - - // TODO - validate type and name - bool isArrayType; - string embeddedInstanceType; - string mofType = MapTypeToMofType(memberType, member.Name, className, out isArrayType, out embeddedInstanceType, - embeddedInstanceTypes); - string arrayAffix = isArrayType ? "[]" : string.Empty; - - var enumNames = memberType.IsEnum - ? Enum.GetNames(memberType) - : null; - sb.AppendFormat(CultureInfo.InvariantCulture, - " {0}{1} {2}{3};\n", - MapAttributesToMof(enumNames, member.GetCustomAttributes(true), embeddedInstanceType), - mofType, - member.Name, - arrayAffix); - } - } - internal static string MapTypeToMofType(Type type, string memberName, string className, out bool isArrayType, out string embeddedInstanceType, List embeddedInstanceTypes) { isArrayType = false; From 05cf4e9652a860f5cabb4ac1b4021936eed211e5 Mon Sep 17 00:00:00 2001 From: anmenaga Date: Tue, 18 Aug 2020 15:49:25 -0700 Subject: [PATCH 11/64] Removed leftover code in MofDscClassCache.cs --- .../DscSupport/MofDscClassCache.cs | 24 ++----------------- 1 file changed, 2 insertions(+), 22 deletions(-) diff --git a/src/System.Management.Automation/DscSupport/MofDscClassCache.cs b/src/System.Management.Automation/DscSupport/MofDscClassCache.cs index 29ab3c1be62..c19d27660fa 100755 --- a/src/System.Management.Automation/DscSupport/MofDscClassCache.cs +++ b/src/System.Management.Automation/DscSupport/MofDscClassCache.cs @@ -485,26 +485,6 @@ private static CimClass MyClassCallback(string serverName, string namespaceName, return null; } - internal static bool CimPropertyIsInherited(string propertyName, CimClass parentClass) - { - if (parentClass == null) - { - return false; - } - else - { - foreach(var p in parentClass.CimClassProperties) - { - if (p.Name.Equals(propertyName, StringComparison.InvariantCultureIgnoreCase)) - { - return true; - } - } - - return CimPropertyIsInherited(propertyName, parentClass.CimSuperClass); - } - } - /// /// Reads CIM MOF schema file and returns classes defined in it /// @@ -1039,11 +1019,11 @@ private static DynamicKeyword CreateKeywordFromCimClass(string moduleName, Versi // Copy the type name string. If it's an embedded instance, need to grab it from the ReferenceClassName bool referenceClassNameIsNullOrEmpty = string.IsNullOrEmpty(prop.ReferenceClassName); - if (prop.CimType == Microsoft.Management.Infrastructure.CimType.Instance && !referenceClassNameIsNullOrEmpty) + if (prop.CimType == CimType.Instance && !referenceClassNameIsNullOrEmpty) { keyProp.TypeConstraint = prop.ReferenceClassName; } - else if (prop.CimType == Microsoft.Management.Infrastructure.CimType.InstanceArray && !referenceClassNameIsNullOrEmpty) + else if (prop.CimType == CimType.InstanceArray && !referenceClassNameIsNullOrEmpty) { keyProp.TypeConstraint = prop.ReferenceClassName + "[]"; } From 595530e050e1e17919e1ea39b1ea287cffc864ea Mon Sep 17 00:00:00 2001 From: anmenaga Date: Tue, 18 Aug 2020 18:04:20 -0700 Subject: [PATCH 12/64] Feedback 1 --- .../DscSupport/JsonCimDSCParser.cs | 10 +- .../DscSupport/JsonDeserializer.cs | 4 +- .../DscSupport/JsonDscClassCache.cs | 224 +++++------------- .../DscSupport/MofDscClassCache.cs | 2 +- 4 files changed, 69 insertions(+), 171 deletions(-) diff --git a/src/System.Management.Automation/DscSupport/JsonCimDSCParser.cs b/src/System.Management.Automation/DscSupport/JsonCimDSCParser.cs index fa5b2a2f60a..fe51803b3bd 100755 --- a/src/System.Management.Automation/DscSupport/JsonCimDSCParser.cs +++ b/src/System.Management.Automation/DscSupport/JsonCimDSCParser.cs @@ -13,7 +13,7 @@ namespace Microsoft.PowerShell.DesiredStateConfiguration.Json { /// - /// Class that does high level Cim schema parsing + /// Class that does high level Cim schema parsing. /// internal class CimDSCParser { @@ -37,11 +37,11 @@ internal List ParseSchemaJson(string filePath, bool useNewRunspace = f } var result = new List(_json_deserializer.DeserializeClasses(json, useNewRunspace)); - foreach (dynamic c in result) + foreach (dynamic classObject in result) { - string superClassName = c.CimSuperClassName; - string className = c.CimSystemProperties.ClassName; - if ((superClassName != null) && (superClassName.Equals("OMI_BaseResource", StringComparison.OrdinalIgnoreCase))) + string superClassName = classObject.CimSuperClassName; + string className = classObject.CimSystemProperties.ClassName; + if (superClassName?.Equals("OMI_BaseResource", StringComparison.OrdinalIgnoreCase) ?? false) { // Get the name of the file without schema.mof/json extension if (!(className.Equals(fileNameDefiningClass, StringComparison.OrdinalIgnoreCase))) diff --git a/src/System.Management.Automation/DscSupport/JsonDeserializer.cs b/src/System.Management.Automation/DscSupport/JsonDeserializer.cs index 1c0c6ea78f4..597987f97f6 100755 --- a/src/System.Management.Automation/DscSupport/JsonDeserializer.cs +++ b/src/System.Management.Automation/DscSupport/JsonDeserializer.cs @@ -14,7 +14,7 @@ internal class JsonDeserializer #region Constructors /// - /// Instantiates a default deserializer + /// Instantiates a default deserializer. /// public static JsonDeserializer Create() { @@ -26,7 +26,7 @@ public static JsonDeserializer Create() #region Methods /// - /// Returns schema of Cim classes from specified json file + /// Returns schema of Cim classes from specified json file. /// public IEnumerable DeserializeClasses(string json, bool useNewRunspace = false) { diff --git a/src/System.Management.Automation/DscSupport/JsonDscClassCache.cs b/src/System.Management.Automation/DscSupport/JsonDscClassCache.cs index d3d67cc3a62..1660c7b8e0c 100755 --- a/src/System.Management.Automation/DscSupport/JsonDscClassCache.cs +++ b/src/System.Management.Automation/DscSupport/JsonDscClassCache.cs @@ -22,26 +22,25 @@ namespace Microsoft.PowerShell.DesiredStateConfiguration.Internal.Json { /// + /// Class that defines Dsc cache entries. /// - [SuppressMessage("Microsoft.MSInternal", "CA903:InternalNamespaceShouldNotContainPublicTypes", - Justification = "Needed Internal use only")] internal class DscClassCacheEntry { /// /// Store the RunAs Credentials that this DSC resource will use. /// - public DSCResourceRunAsCredential DscResRunAsCred; + public DSCResourceRunAsCredential DscResRunAsCred {get; set;} /// /// If we have implicitly imported this resource, we will set this field to true. This will /// only happen to InBox resources. /// - public bool IsImportedImplicitly; + public bool IsImportedImplicitly {get; set;} /// /// A CimClass instance for this resource. /// - public PSObject CimClassInstance; + public PSObject CimClassInstance {get; set;} /// /// Initializes variables with default values. @@ -73,7 +72,7 @@ public static class DscClassCache private const string reservedProperties = "^(Require|Trigger|Notify|Before|After|Subscribe)$"; - private static PSTraceSource s_tracer = PSTraceSource.GetTracer("DSC", "DSC Class Cache"); + private static readonly PSTraceSource s_tracer = PSTraceSource.GetTracer("DSC", "DSC Class Cache"); // Constants for items in the module qualified name (Module\Version\ClassName) private const int IndexModuleName = 0; @@ -121,17 +120,7 @@ private static Dictionary ClassCache /// DSC classname to source module mapper. /// private static Dictionary> ByClassModuleCache - { - get - { - if (t_byClassModuleCache == null) - { - t_byClassModuleCache = new Dictionary>(StringComparer.OrdinalIgnoreCase); - } - - return t_byClassModuleCache; - } - } + => t_byClassModuleCache ??= new Dictionary>(StringComparer.OrdinalIgnoreCase); [ThreadStatic] private static Dictionary> t_byClassModuleCache; @@ -140,17 +129,7 @@ private static Dictionary> ByClassModuleCache /// DSC filename to defined class mapper. /// private static Dictionary> ByFileClassCache - { - get - { - if (t_byFileClassCache == null) - { - t_byFileClassCache = new Dictionary>(StringComparer.OrdinalIgnoreCase); - } - - return t_byFileClassCache; - } - } + => t_byFileClassCache ??= new Dictionary>(StringComparer.OrdinalIgnoreCase); [ThreadStatic] private static Dictionary> t_byFileClassCache; @@ -159,17 +138,7 @@ private static Dictionary> ByFileClassCache /// Filenames from which we have imported script dynamic keywords. /// private static HashSet ScriptKeywordFileCache - { - get - { - if (t_scriptKeywordFileCache == null) - { - t_scriptKeywordFileCache = new HashSet(StringComparer.OrdinalIgnoreCase); - } - - return t_scriptKeywordFileCache; - } - } + => t_scriptKeywordFileCache ??= new HashSet(StringComparer.OrdinalIgnoreCase); [ThreadStatic] private static HashSet t_scriptKeywordFileCache; @@ -177,12 +146,12 @@ private static HashSet ScriptKeywordFileCache /// /// Default ModuleName and ModuleVersion to use. /// - private static readonly Tuple s_defaultModuleInfoForResource = new Tuple("PSDesiredStateConfiguration", new Version("1.1")); + private static readonly Tuple s_defaultModuleInfoForResource = new Tuple("PSDesiredStateConfiguration", new Version(1, 1)); /// /// Default ModuleName and ModuleVersion to use for meta configuration resources. /// - internal static readonly Tuple DefaultModuleInfoForMetaConfigResource = new Tuple("PSDesiredStateConfigurationEngine", new Version("2.0")); + internal static readonly Tuple DefaultModuleInfoForMetaConfigResource = new Tuple("PSDesiredStateConfigurationEngine", new Version(2, 0)); /// /// A set of dynamic keywords that can be used in both configuration and meta configuration. @@ -217,7 +186,7 @@ private static bool CacheResourcesFromMultipleModuleVersions /// public static void Initialize() { - Initialize(null, null); + Initialize(errors: null, modulePathList: null); } /// @@ -227,13 +196,11 @@ public static void Initialize() /// List of module path from where DSC PS modules will be loaded. public static void Initialize(Collection errors, List modulePathList) { - s_tracer.WriteLine("Initializing DSC class cache force={0}"); + s_tracer.WriteLine("Initializing DSC class cache"); if (Platform.IsLinux || Platform.IsMacOS) { - // // Load the base schema files. - // ClearCache(); var dscConfigurationDirectory = Environment.GetEnvironmentVariable("DSC_HOME") ?? "/etc/opt/omi/conf/dsc/configuration"; @@ -243,26 +210,24 @@ public static void Initialize(Collection errors, List moduleP throw new DirectoryNotFoundException("Unable to find DSC schema store at " + dscConfigurationDirectory + ". Please ensure PS DSC for Linux is installed."); } - var resourceBaseFile = Path.Combine(dscConfigurationDirectory, "BaseRegistration/BaseResource.schema.json"); + var resourceBaseFile = Path.Join(dscConfigurationDirectory, "BaseRegistration", "BaseResource.schema.json"); ImportClasses(resourceBaseFile, s_defaultModuleInfoForResource, errors); - var metaConfigFile = Path.Combine(dscConfigurationDirectory, "BaseRegistration/MSFT_DSCMetaConfiguration.json"); + var metaConfigFile = Path.Join(dscConfigurationDirectory, "BaseRegistration", "MSFT_DSCMetaConfiguration.json"); ImportClasses(metaConfigFile, s_defaultModuleInfoForResource, errors); var allResourceRoots = new string[] { dscConfigurationDirectory }; - // // Load all of the system resource schema files, searching - // string resources; foreach (var resourceRoot in allResourceRoots) { - resources = Path.Combine(resourceRoot, "schema"); + resources = Path.Join(resourceRoot, "schema"); if (!Directory.Exists(resources)) { continue; } - foreach (var schemaFile in Directory.EnumerateDirectories(resources).SelectMany(d => Directory.EnumerateFiles(d, "*.schema.json"))) + foreach (var schemaFile in Directory.EnumerateFiles(resources, "*.schema.json", SearchOption.AllDirectories)) { ImportClasses(schemaFile, s_defaultModuleInfoForResource, errors); } @@ -274,47 +239,42 @@ public static void Initialize(Collection errors, List moduleP { // DSC SxS scenario var configSystemPath = Utils.DefaultPowerShellAppBase; - var systemResourceRoot = Path.Combine(configSystemPath, "Configuration"); - var inboxModulePath = "Modules\\PSDesiredStateConfiguration"; + var systemResourceRoot = Path.Join(configSystemPath, "Configuration"); + var inboxModulePath = Path.Join("Modules", "PSDesiredStateConfiguration"); if (!Directory.Exists(systemResourceRoot)) { configSystemPath = Platform.GetFolderPath(Environment.SpecialFolder.System); - systemResourceRoot = Path.Combine(configSystemPath, "Configuration"); + systemResourceRoot = Path.Join(configSystemPath, "Configuration"); inboxModulePath = InboxDscResourceModulePath; } var programFilesDirectory = Platform.GetFolderPath(Environment.SpecialFolder.ProgramFiles); - Debug.Assert(programFilesDirectory != null, "Program Files environment variable does not exist!"); - var customResourceRoot = Path.Combine(programFilesDirectory, "WindowsPowerShell\\Configuration"); - Debug.Assert(Directory.Exists(customResourceRoot), "%ProgramFiles%\\WindowsPowerShell\\Configuration Directory does not exist"); + var customResourceRoot = Path.Join(programFilesDirectory, "WindowsPowerShell", "Configuration"); var allResourceRoots = new string[] { systemResourceRoot, customResourceRoot }; - // + // Load the base schema files. - // ClearCache(); - var resourceBaseFile = Path.Combine(systemResourceRoot, "BaseRegistration\\BaseResource.schema.json"); + var resourceBaseFile = Path.Join(systemResourceRoot, "BaseRegistration", "BaseResource.schema.json"); ImportClasses(resourceBaseFile, s_defaultModuleInfoForResource, errors); - var metaConfigFile = Path.Combine(systemResourceRoot, "BaseRegistration\\MSFT_DSCMetaConfiguration.json"); + var metaConfigFile = Path.Join(systemResourceRoot, "BaseRegistration", "MSFT_DSCMetaConfiguration.json"); ImportClasses(metaConfigFile, s_defaultModuleInfoForResource, errors); - var metaConfigExtensionFile = Path.Combine(systemResourceRoot, "BaseRegistration\\MSFT_MetaConfigurationExtensionClasses.schema.json"); + var metaConfigExtensionFile = Path.Join(systemResourceRoot, "BaseRegistration", "MSFT_MetaConfigurationExtensionClasses.schema.json"); ImportClasses(metaConfigExtensionFile, DefaultModuleInfoForMetaConfigResource, errors); - // // Load all of the system resource schema files, searching - // string resources; foreach (var resourceRoot in allResourceRoots) { - resources = Path.Combine(resourceRoot, "Schema"); + resources = Path.Join(resourceRoot, "Schema"); if (!Directory.Exists(resources)) { continue; } - foreach (var schemaFile in Directory.EnumerateDirectories(resources).SelectMany(d => Directory.EnumerateFiles(d, "*.schema.json"))) + foreach (var schemaFile in Directory.EnumerateFiles(resources, "*.schema.json", SearchOption.AllDirectories)) { ImportClasses(schemaFile, s_defaultModuleInfoForResource, errors); } @@ -323,9 +283,9 @@ public static void Initialize(Collection errors, List moduleP // Load Regular and DSC PS modules bool importInBoxResourcesImplicitly = false; List modulePaths = new List(); - if (modulePathList == null || modulePathList.Count == 0) + if (modulePathList?.Count == 0) { - modulePaths.Add(Path.Combine(configSystemPath, inboxModulePath)); + modulePaths.Add(Path.Join(configSystemPath, inboxModulePath)); importInBoxResourcesImplicitly = true; } else @@ -362,7 +322,7 @@ private static void LoadDSCResourceIntoCache(Collection errors, List< { if (!Directory.Exists(moduleDir)) continue; - var dscResourcesPath = Path.Combine(moduleDir, "DscResources"); + var dscResourcesPath = Path.Join(moduleDir, "DscResources"); if (Directory.Exists(dscResourcesPath)) { foreach (string resourceDir in Directory.EnumerateDirectories(dscResourcesPath)) @@ -409,7 +369,7 @@ private static Tuple GetModuleInfoHelper(string moduleFolderPat moduleName = Path.GetFileName(moduleFolderPath); } - string manifestPath = Path.Combine(moduleFolderPath, moduleName + ".psd1"); + string manifestPath = Path.Join(moduleFolderPath, moduleName + ".psd1"); s_tracer.WriteLine("DSC GetModuleVersion: Try retrieving module version information from file: {0}.", manifestPath); if (!File.Exists(manifestPath)) @@ -435,7 +395,7 @@ private static Tuple GetModuleInfoHelper(string moduleFolderPat PsUtils.ManifestModuleVersionPropertyName); object versionValue = dataFileSetting["ModuleVersion"]; - if (versionValue != null) + if (versionValue is not null) { Version moduleVersion; if (LanguagePrimitives.TryConvertTo(versionValue, out moduleVersion)) @@ -477,14 +437,19 @@ private static void WriteWarning(string warning) } /// - /// Parses json file without adding it to caches or creating dynamic keywords + /// Parses json file without adding it to caches or creating dynamic keywords. /// /// Path to json file /// If True PowerShell will use a fresh runspace to do Json deserialization /// List of classes from json file public static List ReadClassesFromJson(string jsonFilePath, bool useNewRunspace = false) { - if (! jsonFilePath.EndsWith(".json", StringComparison.InvariantCultureIgnoreCase)) + if (string.IsNullOrEmpty(jsonFilePath)) + { + throw PSTraceSource.NewArgumentNullException(nameof(jsonFilePath)); + } + + if (!jsonFilePath.EndsWith(".json", StringComparison.OrdinalIgnoreCase)) { WriteWarning(string.Format("Cannot parse non-JSON file {0}", jsonFilePath)); return null; @@ -510,7 +475,7 @@ public static List ImportClasses(string path, Tuple m throw PSTraceSource.NewArgumentNullException(nameof(path)); } - if (! path.EndsWith(".json", StringComparison.InvariantCultureIgnoreCase)) + if (!path.EndsWith(".json", StringComparison.OrdinalIgnoreCase)) { WriteWarning(string.Format("Cannot parse non-JSON file {0}", path)); return null; @@ -554,7 +519,7 @@ public static List ImportClasses(string path, Tuple m // allow sharing of nested objects. if (!IsSameNestedObject(cimClass, c)) { - var files = string.Join(",", GetFileDefiningClass(className)); + var files = string.Join(',', GetFileDefiningClass(className)); PSInvalidOperationException e = PSTraceSource.NewInvalidOperationException( ParserStrings.DuplicateCimClassDefinition, className, path, files); @@ -595,7 +560,7 @@ public static List ImportClasses(string path, Tuple m foreach (dynamic c in classes) { sb.Append(c.CimSystemProperties.ClassName); - sb.Append(","); + sb.Append(','); } s_tracer.WriteLine("DSC ClassCache: loading file '{0}' added the following classes to the cache: {1}", path, sb.ToString()); @@ -786,7 +751,7 @@ private static string GetFriendlyName(dynamic cimClass) { foreach(dynamic qualifier in cimClass.CimClassQualifiers) { - if (qualifier.Name == "FriendlyName") + if (qualifier.Name.Equals("FriendlyName", StringComparison.OrdinalIgnoreCase)) { return qualifier.Value as string; } @@ -927,7 +892,7 @@ private static DynamicKeyword CreateKeywordFromCimClass(string moduleName, Versi foreach (var prop in cimClass.CimClassProperties) { // If the property is marked as readonly, skip it... - if ((prop.Flags != null) && prop.Flags.Contains("ReadOnly")) + if (prop.Flags?.Contains("ReadOnly")) { continue; } @@ -935,7 +900,7 @@ private static DynamicKeyword CreateKeywordFromCimClass(string moduleName, Versi // If the property has the Read qualifier, also skip it. foreach(var qualifier in prop.Qualifiers) { - if (qualifier.Name == "Read") + if (qualifier.Name.Equals("Read", StringComparison.OrdinalIgnoreCase)) { continue; } @@ -967,7 +932,7 @@ private static DynamicKeyword CreateKeywordFromCimClass(string moduleName, Versi keyProp.Name = prop.Name; // Set the mandatory flag if appropriate - if ((prop.Flags != null) && prop.Flags.Contains("Key")) + if (prop.Flags?.Contains("Key")) { keyProp.Mandatory = true; keyProp.IsKey = true; @@ -996,7 +961,7 @@ private static DynamicKeyword CreateKeywordFromCimClass(string moduleName, Versi { int count = qualifier.Value.Length; string[] values = new string[count]; - for(int i=0; i public static void LoadDefaultCimKeywords() { - LoadDefaultCimKeywords(null, null, null, false); + LoadDefaultCimKeywords(functionsToDefine: null, errors: null, modulePathList: null, cacheResourcesFromMultipleModuleVersions: false); } /// @@ -1115,7 +1080,7 @@ public static void LoadDefaultCimKeywords() /// List of module path from where DSC PS modules will be loaded. public static void LoadDefaultCimKeywords(List modulePathList) { - LoadDefaultCimKeywords(null, null, modulePathList, false); + LoadDefaultCimKeywords(functionsToDefine: null, errors: null, modulePathList, cacheResourcesFromMultipleModuleVersions: false); } /// @@ -1124,7 +1089,7 @@ public static void LoadDefaultCimKeywords(List modulePathList) /// Collection of any errors encountered while loading keywords. public static void LoadDefaultCimKeywords(Collection errors) { - LoadDefaultCimKeywords(null, errors, null, false); + LoadDefaultCimKeywords(functionsToDefine: null, errors, modulePathList: null, cacheResourcesFromMultipleModuleVersions: false); } /// @@ -1133,7 +1098,7 @@ public static void LoadDefaultCimKeywords(Collection errors) /// public static void LoadDefaultCimKeywords(Dictionary functionsToDefine) { - LoadDefaultCimKeywords(functionsToDefine, null, null, false); + LoadDefaultCimKeywords(functionsToDefine, errors: null, modulePathList: null, cacheResourcesFromMultipleModuleVersions: false); } /// @@ -1143,7 +1108,7 @@ public static void LoadDefaultCimKeywords(Dictionary functi /// Allow caching the resources from multiple versions of modules. public static void LoadDefaultCimKeywords(Collection errors, bool cacheResourcesFromMultipleModuleVersions) { - LoadDefaultCimKeywords(null, errors, null, cacheResourcesFromMultipleModuleVersions); + LoadDefaultCimKeywords(functionsToDefine: null, errors, modulePathList: null, cacheResourcesFromMultipleModuleVersions); } /// @@ -1242,9 +1207,7 @@ private static ParseError[] ImportResourcePostParse(DynamicKeywordStatementAst k var parameterBindingResult = binding.Value; if (boundParameterName.All(char.IsDigit)) { - errorList.Add(new ParseError(parameterBindingResult.Value.Extent, - "ImportDscResourcePositionalParamsNotSupported", - string.Format(CultureInfo.CurrentCulture, ParserStrings.ImportDscResourcePositionalParamsNotSupported))); + errorList.Add(new ParseError(parameterBindingResult.Value.Extent, "ImportDscResourcePositionalParamsNotSupported", string.Format(CultureInfo.CurrentCulture, ParserStrings.ImportDscResourcePositionalParamsNotSupported))); continue; } @@ -1262,17 +1225,13 @@ private static ParseError[] ImportResourcePostParse(DynamicKeywordStatementAst k } else { - errorList.Add(new ParseError(parameterBindingResult.Value.Extent, - "ImportDscResourceNeedParams", - string.Format(CultureInfo.CurrentCulture, ParserStrings.ImportDscResourceNeedParams))); + errorList.Add(new ParseError(parameterBindingResult.Value.Extent, "ImportDscResourceNeedParams", string.Format(CultureInfo.CurrentCulture, ParserStrings.ImportDscResourceNeedParams))); } } if (errorList.Count == 0 && moduleNameBindingResult == null && resourceNameBindingResult == null) { - errorList.Add(new ParseError(kwAst.Extent, - "ImportDscResourceNeedParams", - string.Format(CultureInfo.CurrentCulture, ParserStrings.ImportDscResourceNeedParams))); + errorList.Add(new ParseError(kwAst.Extent, "ImportDscResourceNeedParams", string.Format(CultureInfo.CurrentCulture, ParserStrings.ImportDscResourceNeedParams))); } // Check here if Version is specified but modulename is not specified @@ -1284,9 +1243,7 @@ private static ParseError[] ImportResourcePostParse(DynamicKeywordStatementAst k // once we have different error messages for 2 scenarios we can remove this check if (resourceNameBindingResult != null) { - errorList.Add(new ParseError(kwAst.Extent, - "ImportDscResourceNeedModuleNameWithModuleVersion", - string.Format(CultureInfo.CurrentCulture, ParserStrings.ImportDscResourceNeedParams))); + errorList.Add(new ParseError(kwAst.Extent, "ImportDscResourceNeedModuleNameWithModuleVersion", string.Format(CultureInfo.CurrentCulture, ParserStrings.ImportDscResourceNeedParams))); } } @@ -1582,7 +1539,7 @@ public static void LoadResourcesFromModule(IScriptExtent scriptExtent, foreach (var moduleInfo in modules) { - var dscResourcesPath = Path.Combine(moduleInfo.ModuleBase, "DscResources"); + var dscResourcesPath = Path.Join(moduleInfo.ModuleBase, "DscResources"); var resourcesFound = new List(); LoadPowerShellClassResourcesFromModule(moduleInfo, moduleInfo, resourcesToImport, resourcesFound, errorList, null, true, scriptExtent); @@ -1710,42 +1667,7 @@ private static void LoadPowerShellClassResourcesFromModule(PSModuleInfo primaryM if (moduleInfo.ModuleType == ModuleType.Binary) { -#if CORECLR throw PSTraceSource.NewArgumentException("isConfiguration", ParserStrings.ConfigurationNotSupportedInPowerShellCore); -#else - ResolveEventHandler reh = (sender, args) => CurrentDomain_ReflectionOnlyAssemblyResolve(sender, args, moduleInfo); - - try - { - AppDomain.CurrentDomain.ReflectionOnlyAssemblyResolve += reh; - var assembly = moduleInfo.ImplementingAssembly; - if (assembly == null && moduleInfo.Path != null) - { - try - { - var path = moduleInfo.Path; - - if (moduleInfo.RootModule != null && !Path.GetExtension(moduleInfo.Path).Equals(".dll", StringComparison.OrdinalIgnoreCase)) - { - path = moduleInfo.ModuleBase + "\\" + moduleInfo.RootModule; - } - - assembly = Assembly.ReflectionOnlyLoadFrom(path); - } - catch { } - } - - // Ignore the module if we can't find the assembly. - if (assembly != null) - { - throw new NotImplementedException("ModuleType.Binary / ImportKeywordsFromAssembly not supported yet"); - } - } - finally - { - AppDomain.CurrentDomain.ReflectionOnlyAssemblyResolve -= reh; - } -#endif } else { @@ -1753,7 +1675,7 @@ private static void LoadPowerShellClassResourcesFromModule(PSModuleInfo primaryM // handle RootModule and nestedModule together if (moduleInfo.RootModule != null) { - scriptPath = Path.Combine(moduleInfo.ModuleBase, moduleInfo.RootModule); + scriptPath = Path.Join(moduleInfo.ModuleBase, moduleInfo.RootModule); } else if (moduleInfo.Path != null) { @@ -1772,30 +1694,6 @@ private static void LoadPowerShellClassResourcesFromModule(PSModuleInfo primaryM } } -#if !CORECLR - private static Assembly CurrentDomain_ReflectionOnlyAssemblyResolve(object sender, ResolveEventArgs args, PSModuleInfo moduleInfo) - { - AssemblyName name = new AssemblyName(args.Name); - - if (moduleInfo != null && !string.IsNullOrEmpty(moduleInfo.Path)) - { - string asmToCheck = Path.GetDirectoryName(moduleInfo.Path) + "\\" + name.Name + ".dll"; - if (File.Exists(asmToCheck)) - { - return Assembly.ReflectionOnlyLoadFrom(asmToCheck); - } - - asmToCheck = Path.GetDirectoryName(moduleInfo.Path) + "\\" + name.Name + ".exe"; - if (File.Exists(asmToCheck)) - { - return Assembly.ReflectionOnlyLoadFrom(asmToCheck); - } - } - - return Assembly.ReflectionOnlyLoad(args.Name); - } -#endif - /// /// /// @@ -2428,8 +2326,8 @@ public static bool ImportCimKeywordsFromModule(PSModuleInfo module, string resou throw PSTraceSource.NewArgumentNullException(nameof(resourceName)); } - string dscResourcesPath = Path.Combine(module.ModuleBase, "DscResources"); - schemaFilePath = Path.Combine(Path.Combine(dscResourcesPath, resourceName), resourceName + ".schema.json"); + string dscResourcesPath = Path.Join(module.ModuleBase, "DscResources"); + schemaFilePath = Path.Join(dscResourcesPath, resourceName, resourceName + ".schema.json"); if (File.Exists(schemaFilePath)) { @@ -2540,7 +2438,7 @@ public static bool ImportScriptKeywordsFromModule(PSModuleInfo module, string re throw PSTraceSource.NewArgumentNullException(nameof(resourceName)); } - schemaFilePath = Path.Combine(Path.Combine(Path.Combine(module.ModuleBase, "DscResources"), resourceName), resourceName + ".Schema.psm1"); + schemaFilePath = Path.Join(module.ModuleBase, "DscResources", resourceName, resourceName + ".Schema.psm1"); if (File.Exists(schemaFilePath) && !s_currentImportingScriptFiles.Contains(schemaFilePath)) { diff --git a/src/System.Management.Automation/DscSupport/MofDscClassCache.cs b/src/System.Management.Automation/DscSupport/MofDscClassCache.cs index c19d27660fa..9ba1390340e 100755 --- a/src/System.Management.Automation/DscSupport/MofDscClassCache.cs +++ b/src/System.Management.Automation/DscSupport/MofDscClassCache.cs @@ -230,7 +230,7 @@ public static void Initialize() /// List of module path from where DSC PS modules will be loaded. public static void Initialize(Collection errors, List modulePathList) { - s_tracer.WriteLine("Initializing DSC class cache force={0}"); + s_tracer.WriteLine("Initializing DSC class cache"); if (Platform.IsLinux || Platform.IsMacOS) { From 3cf4c9e7e2538203a67cb34b8a498dc3212bc0aa Mon Sep 17 00:00:00 2001 From: anmenaga Date: Wed, 19 Aug 2020 00:17:10 -0700 Subject: [PATCH 13/64] Feedback 2 --- .../DscSupport/JsonCimDSCParser.cs | 6 +-- .../DscSupport/JsonDeserializer.cs | 5 +++ .../DscSupport/JsonDscClassCache.cs | 37 +++++++++---------- 3 files changed, 25 insertions(+), 23 deletions(-) diff --git a/src/System.Management.Automation/DscSupport/JsonCimDSCParser.cs b/src/System.Management.Automation/DscSupport/JsonCimDSCParser.cs index fe51803b3bd..5bd7be8f24e 100755 --- a/src/System.Management.Automation/DscSupport/JsonCimDSCParser.cs +++ b/src/System.Management.Automation/DscSupport/JsonCimDSCParser.cs @@ -24,19 +24,19 @@ internal CimDSCParser() _json_deserializer = JsonDeserializer.Create(); } - internal List ParseSchemaJson(string filePath, bool useNewRunspace = false) + internal IEnumerable ParseSchemaJson(string filePath, bool useNewRunspace = false) { string json = System.IO.File.ReadAllText(filePath); try { string fileNameDefiningClass = Path.GetFileNameWithoutExtension(filePath); - int dotIndex = fileNameDefiningClass.IndexOf('.'); + int dotIndex = fileNameDefiningClass.IndexOf(".schema", StringComparison.InvariantCultureIgnoreCase); if (dotIndex != -1) { fileNameDefiningClass = fileNameDefiningClass.Substring(0, dotIndex); } - var result = new List(_json_deserializer.DeserializeClasses(json, useNewRunspace)); + var result = _json_deserializer.DeserializeClasses(json, useNewRunspace); foreach (dynamic classObject in result) { string superClassName = classObject.CimSuperClassName; diff --git a/src/System.Management.Automation/DscSupport/JsonDeserializer.cs b/src/System.Management.Automation/DscSupport/JsonDeserializer.cs index 597987f97f6..43fe4805055 100755 --- a/src/System.Management.Automation/DscSupport/JsonDeserializer.cs +++ b/src/System.Management.Automation/DscSupport/JsonDeserializer.cs @@ -30,6 +30,11 @@ public static JsonDeserializer Create() /// public IEnumerable DeserializeClasses(string json, bool useNewRunspace = false) { + if (string.IsNullOrEmpty(json)) + { + throw new ArgumentNullException(nameof(json)); + } + IEnumerable result = null; System.Management.Automation.PowerShell powerShell = null; diff --git a/src/System.Management.Automation/DscSupport/JsonDscClassCache.cs b/src/System.Management.Automation/DscSupport/JsonDscClassCache.cs index 1660c7b8e0c..7264ac04c23 100755 --- a/src/System.Management.Automation/DscSupport/JsonDscClassCache.cs +++ b/src/System.Management.Automation/DscSupport/JsonDscClassCache.cs @@ -67,7 +67,7 @@ public DscClassCacheEntry(DSCResourceRunAsCredential aDSCResourceRunAsCredential Justification = "Needed Internal use only")] public static class DscClassCache { - private const string InboxDscResourceModulePath = "WindowsPowershell\\v1.0\\Modules\\PsDesiredStateConfiguration"; + private const string windowsInboxDscResourceModulePath = "WindowsPowershell\\v1.0\\Modules\\PsDesiredStateConfiguration"; private const string reservedDynamicKeywords = "^(Synchronization|Certificate|IIS|SQL)$"; private const string reservedProperties = "^(Require|Trigger|Notify|Before|After|Subscribe)$"; @@ -82,11 +82,11 @@ public static class DscClassCache // Create a list of classes which are not actual DSC resources similar to what we do inside PSDesiredStateConfiguration.psm1 private static readonly string[] s_hiddenResourceList = - { - "MSFT_BaseConfigurationProviderRegistration", - "MSFT_CimConfigurationProviderRegistration", - "MSFT_PSConfigurationProviderRegistration", - }; + { + "MSFT_BaseConfigurationProviderRegistration", + "MSFT_CimConfigurationProviderRegistration", + "MSFT_PSConfigurationProviderRegistration", + }; // Create a HashSet for fast lookup. According to MSDN, the time complexity of search for an element in a HashSet is O(1) private static readonly HashSet s_hiddenResourceCache = new HashSet(s_hiddenResourceList, @@ -128,11 +128,11 @@ private static Dictionary> ByClassModuleCache /// /// DSC filename to defined class mapper. /// - private static Dictionary> ByFileClassCache - => t_byFileClassCache ??= new Dictionary>(StringComparer.OrdinalIgnoreCase); + private static Dictionary> ByFileClassCache + => t_byFileClassCache ??= new Dictionary>(StringComparer.OrdinalIgnoreCase); [ThreadStatic] - private static Dictionary> t_byFileClassCache; + private static Dictionary> t_byFileClassCache; /// /// Filenames from which we have imported script dynamic keywords. @@ -232,8 +232,6 @@ public static void Initialize(Collection errors, List moduleP ImportClasses(schemaFile, s_defaultModuleInfoForResource, errors); } } - - // Linux DSC Modules are installed to the dscConfigurationDirectory, so no need to load them. } else { @@ -246,7 +244,7 @@ public static void Initialize(Collection errors, List moduleP { configSystemPath = Platform.GetFolderPath(Environment.SpecialFolder.System); systemResourceRoot = Path.Join(configSystemPath, "Configuration"); - inboxModulePath = InboxDscResourceModulePath; + inboxModulePath = windowsInboxDscResourceModulePath; } var programFilesDirectory = Platform.GetFolderPath(Environment.SpecialFolder.ProgramFiles); @@ -442,7 +440,7 @@ private static void WriteWarning(string warning) /// Path to json file /// If True PowerShell will use a fresh runspace to do Json deserialization /// List of classes from json file - public static List ReadClassesFromJson(string jsonFilePath, bool useNewRunspace = false) + public static IEnumerable ReadClassesFromJson(string jsonFilePath, bool useNewRunspace = false) { if (string.IsNullOrEmpty(jsonFilePath)) { @@ -456,8 +454,7 @@ public static List ReadClassesFromJson(string jsonFilePath, bool useNe } var parser = new Microsoft.PowerShell.DesiredStateConfiguration.Json.CimDSCParser(); - List classes = parser.ParseSchemaJson(jsonFilePath, useNewRunspace); - return classes; + return parser.ParseSchemaJson(jsonFilePath, useNewRunspace); } /// @@ -468,7 +465,7 @@ public static List ReadClassesFromJson(string jsonFilePath, bool useNe /// /// /// - public static List ImportClasses(string path, Tuple moduleInfo, Collection errors, bool importInBoxResourcesImplicitly = false) + public static IEnumerable ImportClasses(string path, Tuple moduleInfo, Collection errors, bool importInBoxResourcesImplicitly = false) { if (string.IsNullOrEmpty(path)) { @@ -485,7 +482,7 @@ public static List ImportClasses(string path, Tuple m var parser = new Microsoft.PowerShell.DesiredStateConfiguration.Json.CimDSCParser(); - List classes = null; + IEnumerable classes = null; try { classes = parser.ParseSchemaJson(path); @@ -709,14 +706,14 @@ public static string[] GetLoadedFiles() /// /// /// - public static List GetCachedClassByFileName(string fileName) + public static IEnumerable GetCachedClassByFileName(string fileName) { if (string.IsNullOrWhiteSpace(fileName)) { throw PSTraceSource.NewArgumentNullException(nameof(fileName)); } - List listCimClass; + IEnumerable listCimClass; ByFileClassCache.TryGetValue(fileName, out listCimClass); return listCimClass; } @@ -727,7 +724,7 @@ public static List GetCachedClassByFileName(string fileName) /// /// /// - public static List GetCachedClassByModuleName(string moduleName) + public static IEnumerable GetCachedClassByModuleName(string moduleName) { if (string.IsNullOrWhiteSpace(moduleName)) { From e02466047d8258b1d292b63460c63200354a46db Mon Sep 17 00:00:00 2001 From: anmenaga Date: Wed, 19 Aug 2020 14:51:23 -0700 Subject: [PATCH 14/64] Feedback 3 --- .../DscSupport/JsonDeserializer.cs | 10 +++++++--- .../DscSupport/JsonDscClassCache.cs | 6 +++--- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/src/System.Management.Automation/DscSupport/JsonDeserializer.cs b/src/System.Management.Automation/DscSupport/JsonDeserializer.cs index 43fe4805055..f02159423de 100755 --- a/src/System.Management.Automation/DscSupport/JsonDeserializer.cs +++ b/src/System.Management.Automation/DscSupport/JsonDeserializer.cs @@ -57,9 +57,13 @@ public IEnumerable DeserializeClasses(string json, bool useNewRunspace using (powerShell) { - powerShell.AddCommand("ConvertFrom-Json"); - powerShell.AddParameter("InputObject", json); - powerShell.AddParameter("Depth", 100); // maximum supported by cmdlet + const string convertFromJson = @"ConvertFrom-Json"; + const string inputObject = @"InputObject"; + const string depth = @"Depth"; + + powerShell.AddCommand(convertFromJson); + powerShell.AddParameter(inputObject, json); + powerShell.AddParameter(depth, 100); // maximum supported by cmdlet result = powerShell.Invoke(); } diff --git a/src/System.Management.Automation/DscSupport/JsonDscClassCache.cs b/src/System.Management.Automation/DscSupport/JsonDscClassCache.cs index 7264ac04c23..ea053b8ca6d 100755 --- a/src/System.Management.Automation/DscSupport/JsonDscClassCache.cs +++ b/src/System.Management.Automation/DscSupport/JsonDscClassCache.cs @@ -29,18 +29,18 @@ internal class DscClassCacheEntry /// /// Store the RunAs Credentials that this DSC resource will use. /// - public DSCResourceRunAsCredential DscResRunAsCred {get; set;} + public DSCResourceRunAsCredential DscResRunAsCred { get; set; } /// /// If we have implicitly imported this resource, we will set this field to true. This will /// only happen to InBox resources. /// - public bool IsImportedImplicitly {get; set;} + public bool IsImportedImplicitly { get; set; } /// /// A CimClass instance for this resource. /// - public PSObject CimClassInstance {get; set;} + public PSObject CimClassInstance { get; set; } /// /// Initializes variables with default values. From 08cffc3431c0b0830890065ce0c4228069fab6c3 Mon Sep 17 00:00:00 2001 From: anmenaga Date: Thu, 20 Aug 2020 11:09:03 -0700 Subject: [PATCH 15/64] Moved exception string to resources --- .../DscSupport/JsonDscClassCache.cs | 8 +++++--- .../resources/ParserStrings.resx | 3 +++ 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/System.Management.Automation/DscSupport/JsonDscClassCache.cs b/src/System.Management.Automation/DscSupport/JsonDscClassCache.cs index ea053b8ca6d..9b614c7576b 100755 --- a/src/System.Management.Automation/DscSupport/JsonDscClassCache.cs +++ b/src/System.Management.Automation/DscSupport/JsonDscClassCache.cs @@ -202,12 +202,14 @@ public static void Initialize(Collection errors, List moduleP { // Load the base schema files. ClearCache(); - var dscConfigurationDirectory = Environment.GetEnvironmentVariable("DSC_HOME") ?? - "/etc/opt/omi/conf/dsc/configuration"; + /*var dscConfigurationDirectory = Environment.GetEnvironmentVariable("DSC_HOME") ?? + "/etc/opt/omi/conf/dsc/configuration";*/ + + var dscConfigurationDirectory = "/etc/opt/omi/conf/dsc/configuration"; if (!Directory.Exists(dscConfigurationDirectory)) { - throw new DirectoryNotFoundException("Unable to find DSC schema store at " + dscConfigurationDirectory + ". Please ensure PS DSC for Linux is installed."); + throw new DirectoryNotFoundException(string.Format(ParserStrings.PsDscMissingSchemaStore, dscConfigurationDirectory)); } var resourceBaseFile = Path.Join(dscConfigurationDirectory, "BaseRegistration", "BaseResource.schema.json"); diff --git a/src/System.Management.Automation/resources/ParserStrings.resx b/src/System.Management.Automation/resources/ParserStrings.resx index f38b78f074b..b3d9d614fa3 100644 --- a/src/System.Management.Automation/resources/ParserStrings.resx +++ b/src/System.Management.Automation/resources/ParserStrings.resx @@ -1452,6 +1452,9 @@ ModuleVersion : Version of module to import. If used, ModuleName must represent Conflict in using PsDscRunAsCredential for Resource {0} because it already specifies PsDscRunAsCredential value. We can only use one PsDscRunAsCredential for the composite resource. + + Unable to find DSC schema store at "{0}". Please ensure PS DSC for Linux is installed. + {0} From 8034edf036ee1bc1bca4e96d7d2401778b41e46b Mon Sep 17 00:00:00 2001 From: anmenaga Date: Thu, 20 Aug 2020 11:10:33 -0700 Subject: [PATCH 16/64] Fixed exception message code --- .../DscSupport/JsonDscClassCache.cs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/System.Management.Automation/DscSupport/JsonDscClassCache.cs b/src/System.Management.Automation/DscSupport/JsonDscClassCache.cs index 9b614c7576b..273ba6160e5 100755 --- a/src/System.Management.Automation/DscSupport/JsonDscClassCache.cs +++ b/src/System.Management.Automation/DscSupport/JsonDscClassCache.cs @@ -202,10 +202,8 @@ public static void Initialize(Collection errors, List moduleP { // Load the base schema files. ClearCache(); - /*var dscConfigurationDirectory = Environment.GetEnvironmentVariable("DSC_HOME") ?? - "/etc/opt/omi/conf/dsc/configuration";*/ - - var dscConfigurationDirectory = "/etc/opt/omi/conf/dsc/configuration"; + var dscConfigurationDirectory = Environment.GetEnvironmentVariable("DSC_HOME") ?? + "/etc/opt/omi/conf/dsc/configuration"; if (!Directory.Exists(dscConfigurationDirectory)) { From 93d638f357d17433dedd2cc0fc05c3516abdeffa Mon Sep 17 00:00:00 2001 From: anmenaga Date: Thu, 20 Aug 2020 13:52:47 -0700 Subject: [PATCH 17/64] Added PSDscJsonSchemaSupport experimental feature --- .../DscSupport/JsonDscClassCache.cs | 59 +++++++++++++++++++ .../ExperimentalFeature.cs | 3 + .../resources/ParserStrings.resx | 3 + 3 files changed, 65 insertions(+) diff --git a/src/System.Management.Automation/DscSupport/JsonDscClassCache.cs b/src/System.Management.Automation/DscSupport/JsonDscClassCache.cs index 273ba6160e5..5e591023cb2 100755 --- a/src/System.Management.Automation/DscSupport/JsonDscClassCache.cs +++ b/src/System.Management.Automation/DscSupport/JsonDscClassCache.cs @@ -72,6 +72,8 @@ public static class DscClassCache private const string reservedProperties = "^(Require|Trigger|Notify|Before|After|Subscribe)$"; + private const string jsonSchemaSupportExperimentalFeatureName = "PSDscJsonSchemaSupport"; + private static readonly PSTraceSource s_tracer = PSTraceSource.GetTracer("DSC", "DSC Class Cache"); // Constants for items in the module qualified name (Module\Version\ClassName) @@ -595,6 +597,11 @@ public static string GetStringFromSecureString(SecureString value) /// public static void ClearCache() { + if (!ExperimentalFeature.IsEnabled(jsonSchemaSupportExperimentalFeatureName)) + { + throw new InvalidOperationException(ParserStrings.PsDscJsonSchemaSupportDisabled); + } + s_tracer.WriteLine("DSC class: clearing the cache and associated keywords."); ClassCache.Clear(); ByClassModuleCache.Clear(); @@ -652,6 +659,11 @@ private static List GetCachedClasses() /// List of cached cim classes. public static List GetCachedClassesForModule(PSModuleInfo module) { + if (!ExperimentalFeature.IsEnabled(jsonSchemaSupportExperimentalFeatureName)) + { + throw new InvalidOperationException(ParserStrings.PsDscJsonSchemaSupportDisabled); + } + List cachedClasses = new List(); var moduleQualifiedName = string.Format(CultureInfo.InvariantCulture, "{0}\\{1}", module.Name, module.Version.ToString()); foreach (var dscClassCacheEntry in ClassCache) @@ -672,6 +684,11 @@ public static List GetCachedClassesForModule(PSModuleInfo module) /// public static List GetFileDefiningClass(string className) { + if (!ExperimentalFeature.IsEnabled(jsonSchemaSupportExperimentalFeatureName)) + { + throw new InvalidOperationException(ParserStrings.PsDscJsonSchemaSupportDisabled); + } + List files = new List(); foreach (var pair in ByFileClassCache) { @@ -698,6 +715,11 @@ public static List GetFileDefiningClass(string className) /// public static string[] GetLoadedFiles() { + if (!ExperimentalFeature.IsEnabled(jsonSchemaSupportExperimentalFeatureName)) + { + throw new InvalidOperationException(ParserStrings.PsDscJsonSchemaSupportDisabled); + } + return ByFileClassCache.Keys.ToArray(); } @@ -708,6 +730,11 @@ public static string[] GetLoadedFiles() /// public static IEnumerable GetCachedClassByFileName(string fileName) { + if (!ExperimentalFeature.IsEnabled(jsonSchemaSupportExperimentalFeatureName)) + { + throw new InvalidOperationException(ParserStrings.PsDscJsonSchemaSupportDisabled); + } + if (string.IsNullOrWhiteSpace(fileName)) { throw PSTraceSource.NewArgumentNullException(nameof(fileName)); @@ -726,6 +753,11 @@ public static IEnumerable GetCachedClassByFileName(string fileName) /// public static IEnumerable GetCachedClassByModuleName(string moduleName) { + if (!ExperimentalFeature.IsEnabled(jsonSchemaSupportExperimentalFeatureName)) + { + throw new InvalidOperationException(ParserStrings.PsDscJsonSchemaSupportDisabled); + } + if (string.IsNullOrWhiteSpace(moduleName)) { throw PSTraceSource.NewArgumentNullException(nameof(moduleName)); @@ -767,6 +799,11 @@ private static string GetFriendlyName(dynamic cimClass) /// public static Collection GetCachedKeywords() { + if (!ExperimentalFeature.IsEnabled(jsonSchemaSupportExperimentalFeatureName)) + { + throw new InvalidOperationException(ParserStrings.PsDscJsonSchemaSupportDisabled); + } + Collection keywords = new Collection(); foreach (KeyValuePair cachedClass in ClassCache) @@ -1118,6 +1155,13 @@ public static void LoadDefaultCimKeywords(Collection errors, bool cac private static void LoadDefaultCimKeywords(Dictionary functionsToDefine, Collection errors, List modulePathList, bool cacheResourcesFromMultipleModuleVersions) { + if (!ExperimentalFeature.IsEnabled(jsonSchemaSupportExperimentalFeatureName)) + { + Exception exception = new InvalidOperationException(ParserStrings.PsDscJsonSchemaSupportDisabled); + errors.Add(exception); + return; + } + DynamicKeyword.Reset(); Initialize(errors, modulePathList); @@ -1699,6 +1743,11 @@ private static void LoadPowerShellClassResourcesFromModule(PSModuleInfo primaryM /// The list of resources imported from this module. public static List ImportClassResourcesFromModule(PSModuleInfo moduleInfo, ICollection resourcesToImport, Dictionary functionsToDefine) { + if (!ExperimentalFeature.IsEnabled(jsonSchemaSupportExperimentalFeatureName)) + { + throw new InvalidOperationException(ParserStrings.PsDscJsonSchemaSupportDisabled); + } + var resourcesImported = new List(); LoadPowerShellClassResourcesFromModule(moduleInfo, moduleInfo, resourcesToImport, resourcesImported, null, functionsToDefine); return resourcesImported; @@ -2313,6 +2362,11 @@ public static bool ImportCimKeywordsFromModule(PSModuleInfo module, string resou /// public static bool ImportCimKeywordsFromModule(PSModuleInfo module, string resourceName, out string schemaFilePath, Dictionary functionsToDefine, Collection errors) { + if (!ExperimentalFeature.IsEnabled(jsonSchemaSupportExperimentalFeatureName)) + { + throw new InvalidOperationException(ParserStrings.PsDscJsonSchemaSupportDisabled); + } + if (module == null) { throw PSTraceSource.NewArgumentNullException(nameof(module)); @@ -2425,6 +2479,11 @@ public static bool ImportScriptKeywordsFromModule(PSModuleInfo module, string re /// public static bool ImportScriptKeywordsFromModule(PSModuleInfo module, string resourceName, out string schemaFilePath, Dictionary functionsToDefine) { + if (!ExperimentalFeature.IsEnabled(jsonSchemaSupportExperimentalFeatureName)) + { + throw new InvalidOperationException(ParserStrings.PsDscJsonSchemaSupportDisabled); + } + if (module == null) { throw PSTraceSource.NewArgumentNullException(nameof(module)); diff --git a/src/System.Management.Automation/engine/ExperimentalFeature/ExperimentalFeature.cs b/src/System.Management.Automation/engine/ExperimentalFeature/ExperimentalFeature.cs index e44ea07962c..3a2356d1d0f 100644 --- a/src/System.Management.Automation/engine/ExperimentalFeature/ExperimentalFeature.cs +++ b/src/System.Management.Automation/engine/ExperimentalFeature/ExperimentalFeature.cs @@ -126,6 +126,9 @@ static ExperimentalFeature() new ExperimentalFeature( name: "PSNotApplyErrorActionToStderr", description: "Don't have $ErrorActionPreference affect stderr output"), + new ExperimentalFeature( + name: "PSDscJsonSchemaSupport", + description: "Support JSON-based APIs for DSC schema processing"), }; EngineExperimentalFeatures = new ReadOnlyCollection(engineFeatures); diff --git a/src/System.Management.Automation/resources/ParserStrings.resx b/src/System.Management.Automation/resources/ParserStrings.resx index b3d9d614fa3..07e8dba0d1b 100644 --- a/src/System.Management.Automation/resources/ParserStrings.resx +++ b/src/System.Management.Automation/resources/ParserStrings.resx @@ -1455,6 +1455,9 @@ ModuleVersion : Version of module to import. If used, ModuleName must represent Unable to find DSC schema store at "{0}". Please ensure PS DSC for Linux is installed. + + PSDscJsonSchemaSupport experimental feature is disabled; use Enable-ExperimentalFeature or update powershell.config.json to enable this feature. + {0} From bc74051afd8f00ce519f8b6bb5337e63bed64f08 Mon Sep 17 00:00:00 2001 From: anmenaga Date: Thu, 20 Aug 2020 17:36:31 -0700 Subject: [PATCH 18/64] Feedback 4 --- .../DscSupport/JsonCimDSCParser.cs | 2 +- .../DscSupport/JsonDscClassCache.cs | 41 +++++++++++-------- .../ExperimentalFeature.cs | 2 +- .../engine/Modules/ModuleIntrinsics.cs | 2 +- 4 files changed, 26 insertions(+), 21 deletions(-) diff --git a/src/System.Management.Automation/DscSupport/JsonCimDSCParser.cs b/src/System.Management.Automation/DscSupport/JsonCimDSCParser.cs index 5bd7be8f24e..8ade67722f5 100755 --- a/src/System.Management.Automation/DscSupport/JsonCimDSCParser.cs +++ b/src/System.Management.Automation/DscSupport/JsonCimDSCParser.cs @@ -26,7 +26,7 @@ internal CimDSCParser() internal IEnumerable ParseSchemaJson(string filePath, bool useNewRunspace = false) { - string json = System.IO.File.ReadAllText(filePath); + string json = File.ReadAllText(filePath); try { string fileNameDefiningClass = Path.GetFileNameWithoutExtension(filePath); diff --git a/src/System.Management.Automation/DscSupport/JsonDscClassCache.cs b/src/System.Management.Automation/DscSupport/JsonDscClassCache.cs index 5e591023cb2..1107c683c61 100755 --- a/src/System.Management.Automation/DscSupport/JsonDscClassCache.cs +++ b/src/System.Management.Automation/DscSupport/JsonDscClassCache.cs @@ -19,7 +19,7 @@ using Microsoft.PowerShell.Commands; -namespace Microsoft.PowerShell.DesiredStateConfiguration.Internal.Json +namespace Microsoft.PowerShell.DesiredStateConfiguration.Json { /// /// Class that defines Dsc cache entries. @@ -68,6 +68,7 @@ public DscClassCacheEntry(DSCResourceRunAsCredential aDSCResourceRunAsCredential public static class DscClassCache { private const string windowsInboxDscResourceModulePath = "WindowsPowershell\\v1.0\\Modules\\PsDesiredStateConfiguration"; + private const string reservedDynamicKeywords = "^(Synchronization|Certificate|IIS|SQL)$"; private const string reservedProperties = "^(Require|Trigger|Notify|Before|After|Subscribe)$"; @@ -204,8 +205,12 @@ public static void Initialize(Collection errors, List moduleP { // Load the base schema files. ClearCache(); - var dscConfigurationDirectory = Environment.GetEnvironmentVariable("DSC_HOME") ?? - "/etc/opt/omi/conf/dsc/configuration"; + var dscConfigurationDirectory = Environment.GetEnvironmentVariable("DSC_HOME"); + if (string.IsNullOrEmpty(dscConfigurationDirectory)) + { + // if DSC_HOME env var is not set, then use location of system-wide PS module directory (i.e. /usr/local/share/powershell/Modules) as backup + dscConfigurationDirectory = Path.Join(ModuleIntrinsics.GetSharedModulePath(), "PSDesiredStateConfiguration", "Configuration"); + } if (!Directory.Exists(dscConfigurationDirectory)) { @@ -455,7 +460,7 @@ public static IEnumerable ReadClassesFromJson(string jsonFilePath, boo return null; } - var parser = new Microsoft.PowerShell.DesiredStateConfiguration.Json.CimDSCParser(); + var parser = new CimDSCParser(); return parser.ParseSchemaJson(jsonFilePath, useNewRunspace); } @@ -482,7 +487,7 @@ public static IEnumerable ImportClasses(string path, Tuple classes = null; try @@ -2104,7 +2109,7 @@ private static bool ImportKeywordsFromScriptFile(string fileName, PSModuleInfo m } var result = false; - var parser = new Microsoft.PowerShell.DesiredStateConfiguration.Json.CimDSCParser(); + var parser = new CimDSCParser(); const WildcardOptions wildcardOptions = WildcardOptions.IgnoreCase | WildcardOptions.CultureInvariant; IEnumerable patternList = SessionStateUtilities.CreateWildcardsFromStrings(module._declaredDscResourceExports, wildcardOptions); @@ -2903,7 +2908,7 @@ function Test-DependsOn if ($DependsOnVar -notmatch '^\[[a-z]\w*\][a-z_0-9][a-z_0-9\p{Zs}\.\\-]*$') { Update-ConfigurationErrorCount - Write-Error -ErrorRecord ([Microsoft.PowerShell.DesiredStateConfiguration.Json.Internal.DscClassCache]::GetBadlyFormedRequiredResourceIdErrorRecord($DependsOnVar, $resourceId)) + Write-Error -ErrorRecord ([Microsoft.PowerShell.DesiredStateConfiguration.Json.DscClassCache]::GetBadlyFormedRequiredResourceIdErrorRecord($DependsOnVar, $resourceId)) } # Fix up DependsOn for nested names @@ -2979,7 +2984,7 @@ function Test-DependsOn if (Test-NodeResources $resourceId) { Update-ConfigurationErrorCount - Write-Error -ErrorRecord ([Microsoft.PowerShell.DesiredStateConfiguration.Json.Internal.DscClassCache]::DuplicateResourceIdInNodeStatementErrorRecord($resourceId, (Get-PSCurrentConfigurationNode))) + Write-Error -ErrorRecord ([Microsoft.PowerShell.DesiredStateConfiguration.Json.DscClassCache]::DuplicateResourceIdInNodeStatementErrorRecord($resourceId, (Get-PSCurrentConfigurationNode))) } else { @@ -2995,7 +3000,7 @@ function Test-DependsOn if($null -ne $value['PsDscRunAsCredential']) { Update-ConfigurationErrorCount - Write-Error -ErrorRecord ([Microsoft.PowerShell.DesiredStateConfiguration.Json.Internal.DscClassCache]::PsDscRunAsCredentialMergeErrorForCompositeResources($resourceId)) + Write-Error -ErrorRecord ([Microsoft.PowerShell.DesiredStateConfiguration.Json.DscClassCache]::PsDscRunAsCredentialMergeErrorForCompositeResources($resourceId)) } # Set the Value of RunAsCred to that of outer configuration else @@ -3014,14 +3019,14 @@ function Test-DependsOn if($value['RefreshMode'] -eq 'Pull' -and -not $value['ConfigurationSource']) { Update-ConfigurationErrorCount - Write-Error -ErrorRecord ([Microsoft.PowerShell.DesiredStateConfiguration.Json.Internal.DscClassCache]::GetPullModeNeedConfigurationSource($resourceId)) + Write-Error -ErrorRecord ([Microsoft.PowerShell.DesiredStateConfiguration.Json.DscClassCache]::GetPullModeNeedConfigurationSource($resourceId)) } # Verify that RefreshMode is not Disabled for Partial configuration if($value['RefreshMode'] -eq 'Disabled') { Update-ConfigurationErrorCount - Write-Error -ErrorRecord ([Microsoft.PowerShell.DesiredStateConfiguration.Json.Internal.DscClassCache]::DisabledRefreshModeNotValidForPartialConfig($resourceId)) + Write-Error -ErrorRecord ([Microsoft.PowerShell.DesiredStateConfiguration.Json.DscClassCache]::DisabledRefreshModeNotValidForPartialConfig($resourceId)) } if($null -ne $value['ConfigurationSource']) @@ -3042,7 +3047,7 @@ function Test-DependsOn if (($ExclusiveResource -notmatch '^[a-z][a-z_0-9]*\\[a-z][a-z_0-9]*$') -and ($ExclusiveResource -notmatch '^[a-z][a-z_0-9]*$') -and ($ExclusiveResource -notmatch '^[a-z][a-z_0-9]*\\\*$')) { Update-ConfigurationErrorCount - Write-Error -ErrorRecord ([Microsoft.PowerShell.DesiredStateConfiguration.Json.Internal.DscClassCache]::GetBadlyFormedExclusiveResourceIdErrorRecord($ExclusiveResource, $resourceId)) + Write-Error -ErrorRecord ([Microsoft.PowerShell.DesiredStateConfiguration.Json.DscClassCache]::GetBadlyFormedExclusiveResourceIdErrorRecord($ExclusiveResource, $resourceId)) } } @@ -3070,7 +3075,7 @@ function Test-DependsOn { if ($OldMetaConfig -and (-not ($V1MetaConfigPropertyList -contains $key))) { - Write-Error -ErrorRecord ([Microsoft.PowerShell.DesiredStateConfiguration.Json.Internal.DscClassCache]::InvalidLocalConfigurationManagerPropertyErrorRecord($key, ($V1MetaConfigPropertyList -join ', '))) + Write-Error -ErrorRecord ([Microsoft.PowerShell.DesiredStateConfiguration.Json.DscClassCache]::InvalidLocalConfigurationManagerPropertyErrorRecord($key, ($V1MetaConfigPropertyList -join ', '))) Update-ConfigurationErrorCount } # see if there is a list of allowed values for this property (similar to an enum) @@ -3080,7 +3085,7 @@ function Test-DependsOn { if(($null -eq $value[$key]) -and ($allowedValues -notcontains $value[$key])) { - Write-Error -ErrorRecord ([Microsoft.PowerShell.DesiredStateConfiguration.Json.Internal.DscClassCache]::InvalidValueForPropertyErrorRecord($key, ""$($value[$key])"", $keywordData.Keyword, ($allowedValues -join ', '))) + Write-Error -ErrorRecord ([Microsoft.PowerShell.DesiredStateConfiguration.Json.DscClassCache]::InvalidValueForPropertyErrorRecord($key, ""$($value[$key])"", $keywordData.Keyword, ($allowedValues -join ', '))) Update-ConfigurationErrorCount } else @@ -3097,7 +3102,7 @@ function Test-DependsOn if($notAllowedValue) { $notAllowedValue = $notAllowedValue.Substring(0, $notAllowedValue.Length -2) - Write-Error -ErrorRecord ([Microsoft.PowerShell.DesiredStateConfiguration.Json.Internal.DscClassCache]::UnsupportedValueForPropertyErrorRecord($key, $notAllowedValue, $keywordData.Keyword, ($allowedValues -join ', '))) + Write-Error -ErrorRecord ([Microsoft.PowerShell.DesiredStateConfiguration.Json.DscClassCache]::UnsupportedValueForPropertyErrorRecord($key, $notAllowedValue, $keywordData.Keyword, ($allowedValues -join ', '))) Update-ConfigurationErrorCount } } @@ -3110,7 +3115,7 @@ function Test-DependsOn $castedValue = $value[$key] -as [int] if((($castedValue -is [int]) -and (($castedValue -lt $keywordData.Properties[$key].Range.Item1) -or ($castedValue -gt $keywordData.Properties[$key].Range.Item2))) -or ($null -eq $castedValue)) { - Write-Error -ErrorRecord ([Microsoft.PowerShell.DesiredStateConfiguration.Json.Internal.DscClassCache]::ValueNotInRangeErrorRecord($key, $keywordName, $value[$key], $keywordData.Properties[$key].Range.Item1, $keywordData.Properties[$key].Range.Item2)) + Write-Error -ErrorRecord ([Microsoft.PowerShell.DesiredStateConfiguration.Json.DscClassCache]::ValueNotInRangeErrorRecord($key, $keywordName, $value[$key], $keywordData.Properties[$key].Range.Item1, $keywordData.Properties[$key].Range.Item2)) Update-ConfigurationErrorCount } } @@ -3144,7 +3149,7 @@ function Test-DependsOn elseif ($keywordData.Properties[$key].Mandatory) { # If the property was mandatory but the user didn't provide a value, write and error. - Write-Error -ErrorRecord ([Microsoft.PowerShell.DesiredStateConfiguration.Json.Internal.DscClassCache]::MissingValueForMandatoryPropertyErrorRecord($keywordData.Keyword, $keywordData.Properties[$key].TypeConstraint, $Key)) + Write-Error -ErrorRecord ([Microsoft.PowerShell.DesiredStateConfiguration.Json.DscClassCache]::MissingValueForMandatoryPropertyErrorRecord($keywordData.Keyword, $keywordData.Properties[$key].TypeConstraint, $Key)) Update-ConfigurationErrorCount } @@ -3197,7 +3202,7 @@ function Test-DependsOn if($canonicalizedValue['DebugMode'] -and @($canonicalizedValue['DebugMode']).Length -gt 1) { # we only allow one value for debug mode now. - Write-Error -ErrorRecord ([Microsoft.PowerShell.DesiredStateConfiguration.Json.Internal.DscClassCache]::DebugModeShouldHaveOneValue()) + Write-Error -ErrorRecord ([Microsoft.PowerShell.DesiredStateConfiguration.Json.DscClassCache]::DebugModeShouldHaveOneValue()) Update-ConfigurationErrorCount } } diff --git a/src/System.Management.Automation/engine/ExperimentalFeature/ExperimentalFeature.cs b/src/System.Management.Automation/engine/ExperimentalFeature/ExperimentalFeature.cs index 3a2356d1d0f..6059fa29a3c 100644 --- a/src/System.Management.Automation/engine/ExperimentalFeature/ExperimentalFeature.cs +++ b/src/System.Management.Automation/engine/ExperimentalFeature/ExperimentalFeature.cs @@ -128,7 +128,7 @@ static ExperimentalFeature() description: "Don't have $ErrorActionPreference affect stderr output"), new ExperimentalFeature( name: "PSDscJsonSchemaSupport", - description: "Support JSON-based APIs for DSC schema processing"), + description: "Support JSON-based DSC schema processing"), }; EngineExperimentalFeatures = new ReadOnlyCollection(engineFeatures); diff --git a/src/System.Management.Automation/engine/Modules/ModuleIntrinsics.cs b/src/System.Management.Automation/engine/Modules/ModuleIntrinsics.cs index b20e11e4745..caa7f98c946 100644 --- a/src/System.Management.Automation/engine/Modules/ModuleIntrinsics.cs +++ b/src/System.Management.Automation/engine/Modules/ModuleIntrinsics.cs @@ -1014,7 +1014,7 @@ internal static string GetPSHomeModulePath() /// It's known as "Program Files" module path in windows powershell. /// /// - private static string GetSharedModulePath() + internal static string GetSharedModulePath() { #if UNIX return Platform.SelectProductNameForDirectory(Platform.XDG_Type.SHARED_MODULES); From bda00c09b51500e52d74a4daa736a3f1e7d6d738 Mon Sep 17 00:00:00 2001 From: anmenaga Date: Fri, 21 Aug 2020 12:02:55 -0700 Subject: [PATCH 19/64] CodeFactor 1 --- .../DscSupport/MofDscClassCache.cs | 243 ++++++++++-------- 1 file changed, 129 insertions(+), 114 deletions(-) diff --git a/src/System.Management.Automation/DscSupport/MofDscClassCache.cs b/src/System.Management.Automation/DscSupport/MofDscClassCache.cs index 9ba1390340e..655fadeb643 100755 --- a/src/System.Management.Automation/DscSupport/MofDscClassCache.cs +++ b/src/System.Management.Automation/DscSupport/MofDscClassCache.cs @@ -450,21 +450,25 @@ private static Tuple GetModuleInfoHelper(string moduleFolderPat { s_tracer.WriteLine( "DSC GetModuleVersion: ModuleVersion value '{0}' cannot be converted to System.Version. Skip the module '{1}'.", - versionValue, moduleName); + versionValue, + moduleName); } } else { s_tracer.WriteLine( "DSC GetModuleVersion: Manifest file '{0}' does not contain ModuleVersion. Skip the module '{1}'.", - manifestPath, moduleName); + manifestPath, + moduleName); } } catch (PSInvalidOperationException ex) { s_tracer.WriteLine( "DSC GetModuleVersion: Error evaluating module manifest file '{0}', with error '{1}'. Skip the module '{2}'.", - manifestPath, ex, moduleName); + manifestPath, + ex, + moduleName); } return null; @@ -825,7 +829,8 @@ public static void ValidateInstanceText(string instanceText) private static bool IsMagicProperty(string propertyName) { - return System.Text.RegularExpressions.Regex.Match(propertyName, + return System.Text.RegularExpressions.Regex.Match( + propertyName, "^(ResourceId|SourceInfo|ModuleName|ModuleVersion|ConfigurationName)$", System.Text.RegularExpressions.RegexOptions.IgnoreCase).Success; } @@ -1071,7 +1076,9 @@ private static DynamicKeyword CreateKeywordFromCimClass(string moduleName, Versi { s_tracer.WriteLine( "DSC CreateDynamicKeywordFromClass: the count of values for qualifier 'Values' and 'ValueMap' doesn't match. count of 'Values': {0}, count of 'ValueMap': {1}. Skip the keyword '{2}'.", - keyProp.Values.Count, valueMap.Length, keyword.Keyword); + keyProp.Values.Count, + valueMap.Length, + keyword.Keyword); return null; } @@ -1111,11 +1118,9 @@ private static DynamicKeyword CreateKeywordFromCimClass(string moduleName, Versi private static void UpdateKnownRestriction(DynamicKeyword keyword) { if ( - string.Equals(keyword.ResourceName, "MSFT_DSCMetaConfigurationV2", - StringComparison.OrdinalIgnoreCase) + string.Equals(keyword.ResourceName, "MSFT_DSCMetaConfigurationV2", StringComparison.OrdinalIgnoreCase) || - string.Equals(keyword.ResourceName, "MSFT_DSCMetaConfiguration", - StringComparison.OrdinalIgnoreCase)) + string.Equals(keyword.ResourceName, "MSFT_DSCMetaConfiguration", StringComparison.OrdinalIgnoreCase)) { if (keyword.Properties["RefreshFrequencyMins"] != null) { @@ -1187,8 +1192,11 @@ public static void LoadDefaultCimKeywords(Collection errors, bool cac /// Collection of any errors encountered while loading keywords. /// List of module path from where DSC PS modules will be loaded. /// Allow caching the resources from multiple versions of modules. - private static void LoadDefaultCimKeywords(Dictionary functionsToDefine, Collection errors, - List modulePathList, bool cacheResourcesFromMultipleModuleVersions) + private static void LoadDefaultCimKeywords( + Dictionary functionsToDefine, + Collection errors, + List modulePathList, + bool cacheResourcesFromMultipleModuleVersions) { DynamicKeyword.Reset(); Initialize(errors, modulePathList); @@ -1278,9 +1286,7 @@ private static ParseError[] ImportResourcePostParse(DynamicKeywordStatementAst k var parameterBindingResult = binding.Value; if (boundParameterName.All(char.IsDigit)) { - errorList.Add(new ParseError(parameterBindingResult.Value.Extent, - "ImportDscResourcePositionalParamsNotSupported", - string.Format(CultureInfo.CurrentCulture, ParserStrings.ImportDscResourcePositionalParamsNotSupported))); + errorList.Add(new ParseError(parameterBindingResult.Value.Extent, "ImportDscResourcePositionalParamsNotSupported", string.Format(CultureInfo.CurrentCulture, ParserStrings.ImportDscResourcePositionalParamsNotSupported))); continue; } @@ -1298,17 +1304,16 @@ private static ParseError[] ImportResourcePostParse(DynamicKeywordStatementAst k } else { - errorList.Add(new ParseError(parameterBindingResult.Value.Extent, - "ImportDscResourceNeedParams", - string.Format(CultureInfo.CurrentCulture, ParserStrings.ImportDscResourceNeedParams))); + errorList.Add(new ParseError(parameterBindingResult.Value.Extent, "ImportDscResourceNeedParams", string.Format(CultureInfo.CurrentCulture, ParserStrings.ImportDscResourceNeedParams))); } } if (errorList.Count == 0 && moduleNameBindingResult == null && resourceNameBindingResult == null) { - errorList.Add(new ParseError(kwAst.Extent, - "ImportDscResourceNeedParams", - string.Format(CultureInfo.CurrentCulture, ParserStrings.ImportDscResourceNeedParams))); + errorList.Add(new ParseError( + kwAst.Extent, + "ImportDscResourceNeedParams", + string.Format(CultureInfo.CurrentCulture, ParserStrings.ImportDscResourceNeedParams))); } // Check here if Version is specified but modulename is not specified @@ -1320,9 +1325,10 @@ private static ParseError[] ImportResourcePostParse(DynamicKeywordStatementAst k // once we have different error messages for 2 scenarios we can remove this check if (resourceNameBindingResult != null) { - errorList.Add(new ParseError(kwAst.Extent, - "ImportDscResourceNeedModuleNameWithModuleVersion", - string.Format(CultureInfo.CurrentCulture, ParserStrings.ImportDscResourceNeedParams))); + errorList.Add(new ParseError( + kwAst.Extent, + "ImportDscResourceNeedModuleNameWithModuleVersion", + string.Format(CultureInfo.CurrentCulture, ParserStrings.ImportDscResourceNeedParams))); } } @@ -1333,9 +1339,10 @@ private static ParseError[] ImportResourcePostParse(DynamicKeywordStatementAst k if (!IsConstantValueVisitor.IsConstant(resourceNameBindingResult.Value, out resourceName, true, true) || !LanguagePrimitives.TryConvertTo(resourceName, out resourceNames)) { - errorList.Add(new ParseError(resourceNameBindingResult.Value.Extent, - "RequiresInvalidStringArgument", - string.Format(CultureInfo.CurrentCulture, ParserStrings.RequiresInvalidStringArgument, nameParam))); + errorList.Add(new ParseError( + resourceNameBindingResult.Value.Extent, + "RequiresInvalidStringArgument", + string.Format(CultureInfo.CurrentCulture, ParserStrings.RequiresInvalidStringArgument, nameParam))); } } @@ -1345,9 +1352,10 @@ private static ParseError[] ImportResourcePostParse(DynamicKeywordStatementAst k object moduleVer = null; if (!IsConstantValueVisitor.IsConstant(moduleVersionBindingResult.Value, out moduleVer, true, true)) { - errorList.Add(new ParseError(moduleVersionBindingResult.Value.Extent, - "RequiresArgumentMustBeConstant", - ParserStrings.RequiresArgumentMustBeConstant)); + errorList.Add(new ParseError( + moduleVersionBindingResult.Value.Extent, + "RequiresArgumentMustBeConstant", + ParserStrings.RequiresArgumentMustBeConstant)); } if (moduleVer is double) @@ -1360,9 +1368,10 @@ private static ParseError[] ImportResourcePostParse(DynamicKeywordStatementAst k if (!LanguagePrimitives.TryConvertTo(moduleVer, out moduleVersion)) { - errorList.Add(new ParseError(moduleVersionBindingResult.Value.Extent, - "RequiresVersionInvalid", - ParserStrings.RequiresVersionInvalid)); + errorList.Add(new ParseError( + moduleVersionBindingResult.Value.Extent, + "RequiresVersionInvalid", + ParserStrings.RequiresVersionInvalid)); } } @@ -1372,9 +1381,10 @@ private static ParseError[] ImportResourcePostParse(DynamicKeywordStatementAst k object moduleName = null; if (!IsConstantValueVisitor.IsConstant(moduleNameBindingResult.Value, out moduleName, true, true)) { - errorList.Add(new ParseError(moduleNameBindingResult.Value.Extent, - "RequiresArgumentMustBeConstant", - ParserStrings.RequiresArgumentMustBeConstant)); + errorList.Add(new ParseError( + moduleNameBindingResult.Value.Extent, + "RequiresArgumentMustBeConstant", + ParserStrings.RequiresArgumentMustBeConstant)); } if (LanguagePrimitives.TryConvertTo(moduleName, out moduleSpecifications)) @@ -1382,25 +1392,28 @@ private static ParseError[] ImportResourcePostParse(DynamicKeywordStatementAst k // if resourceNames are specified then we can not specify multiple modules name if (moduleSpecifications != null && moduleSpecifications.Length > 1 && resourceNames != null) { - errorList.Add(new ParseError(moduleNameBindingResult.Value.Extent, - "ImportDscResourceMultipleModulesNotSupportedWithName", - string.Format(CultureInfo.CurrentCulture, ParserStrings.ImportDscResourceMultipleModulesNotSupportedWithName))); + errorList.Add(new ParseError( + moduleNameBindingResult.Value.Extent, + "ImportDscResourceMultipleModulesNotSupportedWithName", + string.Format(CultureInfo.CurrentCulture, ParserStrings.ImportDscResourceMultipleModulesNotSupportedWithName))); } // if moduleversion is specified then we can not specify multiple modules name if (moduleSpecifications != null && moduleSpecifications.Length > 1 && moduleVersion != null) { - errorList.Add(new ParseError(moduleNameBindingResult.Value.Extent, - "ImportDscResourceMultipleModulesNotSupportedWithVersion", - string.Format(CultureInfo.CurrentCulture, ParserStrings.ImportDscResourceNeedParams))); + errorList.Add(new ParseError( + moduleNameBindingResult.Value.Extent, + "ImportDscResourceMultipleModulesNotSupportedWithVersion", + string.Format(CultureInfo.CurrentCulture, ParserStrings.ImportDscResourceNeedParams))); } // if moduleversion is specified then we can not specify another version in modulespecification object of ModuleName if (moduleSpecifications != null && (moduleSpecifications[0].Version != null || moduleSpecifications[0].MaximumVersion != null) && moduleVersion != null) { - errorList.Add(new ParseError(moduleNameBindingResult.Value.Extent, - "ImportDscResourceMultipleModuleVersionsNotSupported", - string.Format(CultureInfo.CurrentCulture, ParserStrings.ImportDscResourceNeedParams))); + errorList.Add(new ParseError( + moduleNameBindingResult.Value.Extent, + "ImportDscResourceMultipleModuleVersionsNotSupported", + string.Format(CultureInfo.CurrentCulture, ParserStrings.ImportDscResourceNeedParams))); } // If moduleVersion is specified we have only one module Name in valid scenario @@ -1412,9 +1425,10 @@ private static ParseError[] ImportResourcePostParse(DynamicKeywordStatementAst k } else { - errorList.Add(new ParseError(moduleNameBindingResult.Value.Extent, - "RequiresInvalidStringArgument", - string.Format(CultureInfo.CurrentCulture, ParserStrings.RequiresInvalidStringArgument, moduleNameParam))); + errorList.Add(new ParseError( + moduleNameBindingResult.Value.Extent, + "RequiresInvalidStringArgument", + string.Format(CultureInfo.CurrentCulture, ParserStrings.RequiresInvalidStringArgument, moduleNameParam))); } } @@ -1516,10 +1530,14 @@ private static ParseError[] CheckMandatoryPropertiesPresent(DynamicKeywordStatem int i = 0; foreach (string name in mandatoryPropertiesNames) { - errors[i] = new ParseError(extent, "MissingValueForMandatoryProperty", - string.Format(CultureInfo.CurrentCulture, ParserStrings.MissingValueForMandatoryProperty, - kwAst.Keyword.Keyword, kwAst.Keyword.Properties.First( - p => StringComparer.OrdinalIgnoreCase.Equals(p.Value.Name, name)).Value.TypeConstraint, name)); + errors[i] = new ParseError( + extent, + "MissingValueForMandatoryProperty", + string.Format( + CultureInfo.CurrentCulture, + ParserStrings.MissingValueForMandatoryProperty, + kwAst.Keyword.Keyword, + kwAst.Keyword.Properties.First(p => StringComparer.OrdinalIgnoreCase.Equals(p.Value.Name, name)).Value.TypeConstraint, name)); i++; } @@ -1536,10 +1554,11 @@ private static ParseError[] CheckMandatoryPropertiesPresent(DynamicKeywordStatem /// Module information, can be null. /// Name of the resource to be loaded from module. /// List of errors reported by the method. - public static void LoadResourcesFromModule(IScriptExtent scriptExtent, - ModuleSpecification[] moduleSpecifications, - string[] resourceNames, - List errorList) + public static void LoadResourcesFromModule( + IScriptExtent scriptExtent, + ModuleSpecification[] moduleSpecifications, + string[] resourceNames, + List errorList) { // get all required modules var modules = new Collection(); @@ -1574,11 +1593,13 @@ public static void LoadResourcesFromModule(IScriptExtent scriptExtent, { if (moduleInfos.Count > 1) { - errorList.Add(new ParseError(scriptExtent, - "MultipleModuleEntriesFoundDuringParse", - string.Format(CultureInfo.CurrentCulture, - ParserStrings.MultipleModuleEntriesFoundDuringParse, - moduleToImport.Name))); + errorList.Add(new ParseError( + scriptExtent, + "MultipleModuleEntriesFoundDuringParse", + string.Format( + CultureInfo.CurrentCulture, + ParserStrings.MultipleModuleEntriesFoundDuringParse, + moduleToImport.Name))); } else { @@ -1586,8 +1607,7 @@ public static void LoadResourcesFromModule(IScriptExtent scriptExtent, ? moduleToImport.Name : string.Format(CultureInfo.CurrentCulture, "<{0}, {1}>", moduleToImport.Name, moduleToImport.Version); - errorList.Add(new ParseError(scriptExtent, "ModuleNotFoundDuringParse", - string.Format(CultureInfo.CurrentCulture, ParserStrings.ModuleNotFoundDuringParse, moduleString))); + errorList.Add(new ParseError(scriptExtent, "ModuleNotFoundDuringParse", string.Format(CultureInfo.CurrentCulture, ParserStrings.ModuleNotFoundDuringParse, moduleString))); } return; @@ -1642,21 +1662,15 @@ public static void LoadResourcesFromModule(IScriptExtent scriptExtent, } catch (FileNotFoundException) { - errorList.Add(new ParseError(scriptExtent, - "SchemaFileNotFound", - string.Format(CultureInfo.CurrentCulture, ParserStrings.SchemaFileNotFound, schemaMofFilePath))); + errorList.Add(new ParseError(scriptExtent, "SchemaFileNotFound", string.Format(CultureInfo.CurrentCulture, ParserStrings.SchemaFileNotFound, schemaMofFilePath))); } catch (PSInvalidOperationException e) { - errorList.Add(new ParseError(scriptExtent, - e.ErrorRecord.FullyQualifiedErrorId, - e.Message)); + errorList.Add(new ParseError(scriptExtent, e.ErrorRecord.FullyQualifiedErrorId, e.Message)); } catch (Exception e) { - errorList.Add(new ParseError(scriptExtent, - "ExceptionParsingMOFFile", - string.Format(CultureInfo.CurrentCulture, ParserStrings.ExceptionParsingMOFFile, schemaMofFilePath, e.Message))); + errorList.Add(new ParseError(scriptExtent, "ExceptionParsingMOFFile", string.Format(CultureInfo.CurrentCulture, ParserStrings.ExceptionParsingMOFFile, schemaMofFilePath, e.Message))); } var schemaScriptFilePath = string.Empty; @@ -1667,16 +1681,12 @@ public static void LoadResourcesFromModule(IScriptExtent scriptExtent, } catch (FileNotFoundException) { - errorList.Add(new ParseError(scriptExtent, - "SchemaFileNotFound", - string.Format(CultureInfo.CurrentCulture, ParserStrings.SchemaFileNotFound, schemaScriptFilePath))); + errorList.Add(new ParseError(scriptExtent, "SchemaFileNotFound", string.Format(CultureInfo.CurrentCulture, ParserStrings.SchemaFileNotFound, schemaScriptFilePath))); } catch (Exception e) { // This shouldn't happen so just report the error as is - errorList.Add(new ParseError(scriptExtent, - "UnexpectedParseError", - string.Format(CultureInfo.CurrentCulture, e.ToString()))); + errorList.Add(new ParseError(scriptExtent, "UnexpectedParseError", string.Format(CultureInfo.CurrentCulture, e.ToString()))); } if (foundCimSchema || foundScriptSchema) @@ -1725,9 +1735,7 @@ public static void LoadResourcesFromModule(IScriptExtent scriptExtent, { if (!resourceNameToImport.Contains("*")) { - errorList.Add(new ParseError(scriptExtent, - "DscResourcesNotFoundDuringParsing", - string.Format(CultureInfo.CurrentCulture, ParserStrings.DscResourcesNotFoundDuringParsing, resourceNameToImport))); + errorList.Add(new ParseError(scriptExtent, "DscResourcesNotFoundDuringParsing", string.Format(CultureInfo.CurrentCulture, ParserStrings.DscResourcesNotFoundDuringParsing, resourceNameToImport))); } } } @@ -1768,7 +1776,9 @@ private static void LoadPowerShellClassResourcesFromModule(PSModuleInfo primaryM assembly = Assembly.ReflectionOnlyLoadFrom(path); } - catch { } + catch + { + } } // Ignore the module if we can't find the assembly. @@ -1994,7 +2004,7 @@ private static bool GetResourceMethodsLineNumber(TypeDefinitionAst typeDefinitio } // All 3 methods (Get/Set/Test) position should be found. - return (methodsLinePosition.Count == 3); + return methodsLinePosition.Count == 3; } /// @@ -2082,9 +2092,7 @@ private static void ProcessMembers(StringBuilder sb, List embeddedInstan if (memberType != null) { // TODO - validate type and name - mofType = MapTypeToMofType(memberType, member.Name, className, out isArrayType, - out embeddedInstanceType, - embeddedInstanceTypes); + mofType = MapTypeToMofType(memberType, member.Name, className, out isArrayType, out embeddedInstanceType, embeddedInstanceTypes); if (memberType.IsEnum) { enumNames = Enum.GetNames(memberType); @@ -2093,9 +2101,14 @@ private static void ProcessMembers(StringBuilder sb, List embeddedInstan else { // PropertyType can't be null, we used typeof(object) above in that case so we don't get here. - mofType = MapTypeNameToMofType(property.PropertyType.TypeName, member.Name, className, + mofType = MapTypeNameToMofType( + property.PropertyType.TypeName, + member.Name, + className, out isArrayType, - out embeddedInstanceType, embeddedInstanceTypes, ref enumNames); + out embeddedInstanceType, + embeddedInstanceTypes, + ref enumNames); } string arrayAffix = isArrayType ? "[]" : string.Empty; @@ -2161,20 +2174,22 @@ private static bool GetResourceDefinitionsFromModule(string fileName, out IEnume return false; } - resourceDefinitions = ast.FindAll(n => - { - var typeAst = n as TypeDefinitionAst; - if (typeAst != null) + resourceDefinitions = ast.FindAll( + n => { - for (int i = 0; i < typeAst.Attributes.Count; i++) + var typeAst = n as TypeDefinitionAst; + if (typeAst != null) { - var a = typeAst.Attributes[i]; - if (a.TypeName.GetReflectionAttributeType() == typeof(DscResourceAttribute)) return true; + for (int i = 0; i < typeAst.Attributes.Count; i++) + { + var a = typeAst.Attributes[i]; + if (a.TypeName.GetReflectionAttributeType() == typeof(DscResourceAttribute)) return true; + } } - } - return false; - }, false); + return false; + }, + false); return true; } @@ -2216,7 +2231,7 @@ private static bool ImportKeywordsFromScriptFile(string fileName, PSModuleInfo m bool skip = true; foreach (var toImport in resourcesToImport) { - if ((WildcardPattern.Get(toImport, WildcardOptions.IgnoreCase)).IsMatch(resourceDefnAst.Name)) + if (WildcardPattern.Get(toImport, WildcardOptions.IgnoreCase)).IsMatch(resourceDefnAst.Name) { skip = false; break; @@ -2655,8 +2670,7 @@ private static void ProcessMembers(Type type, StringBuilder sb, List emb // TODO - validate type and name bool isArrayType; string embeddedInstanceType; - string mofType = MapTypeToMofType(memberType, member.Name, className, out isArrayType, out embeddedInstanceType, - embeddedInstanceTypes); + string mofType = MapTypeToMofType(memberType, member.Name, className, out isArrayType, out embeddedInstanceType, embeddedInstanceTypes); string arrayAffix = isArrayType ? "[]" : string.Empty; var enumNames = memberType.IsEnum @@ -2671,11 +2685,12 @@ private static void ProcessMembers(Type type, StringBuilder sb, List emb } } - private static bool ImportKeywordsFromAssembly(PSModuleInfo module, - ICollection resourcesToImport, - ICollection resourcesFound, - Dictionary functionsToDefine, - Assembly assembly) + private static bool ImportKeywordsFromAssembly( + PSModuleInfo module, + ICollection resourcesToImport, + ICollection resourcesFound, + Dictionary functionsToDefine, + Assembly assembly) { bool result = false; @@ -2708,8 +2723,13 @@ private static bool ImportKeywordsFromAssembly(PSModuleInfo module, return result; } - private static void ProcessMofForDynamicKeywords(PSModuleInfo module, ICollection resourcesFound, - Dictionary functionsToDefine, Microsoft.PowerShell.DesiredStateConfiguration.CimDSCParser parser, string mof, DSCResourceRunAsCredential runAsBehavior) + private static void ProcessMofForDynamicKeywords( + PSModuleInfo module, + ICollection resourcesFound, + Dictionary functionsToDefine, + Microsoft.PowerShell.DesiredStateConfiguration.CimDSCParser parser, + string mof, + DSCResourceRunAsCredential runAsBehavior) { foreach (var c in parser.ParseSchemaMofFileBuffer(mof)) { @@ -2810,10 +2830,8 @@ public static bool ImportCimKeywordsFromModule(PSModuleInfo module, string resou } else if (Directory.Exists(dscResourcesPath)) { - // // Cannot find the schema file, then resourceName may be a friendly name, // try to search all DscResources' schemas under DscResources folder - // try { var dscResourceDirectories = Directory.GetDirectories(dscResourcesPath); @@ -2827,9 +2845,7 @@ public static bool ImportCimKeywordsFromModule(PSModuleInfo module, string resou var classes = GetCachedClassByFileName(tempSchemaFilepath) ?? ImportClasses(tempSchemaFilepath, new Tuple(module.Name, module.Version), errors); if (classes != null) { - // // search if class's friendly name is the given resourceName - // foreach (var c in classes) { var alias = GetFriendlyName(c); @@ -2846,9 +2862,7 @@ public static bool ImportCimKeywordsFromModule(PSModuleInfo module, string resou } catch (Exception) { - // // silent in case of exception - // } } @@ -2911,7 +2925,8 @@ public static bool ImportScriptKeywordsFromModule(PSModuleInfo module, string re // Parsing the file is all that needs to be done to add the keywords // BUGBUG - need to fix up how the module gets set. // BUGBUG - should fail somehow if errors is not empty - Token[] tokens; ParseError[] errors; + Token[] tokens; + ParseError[] errors; s_currentImportingScriptFiles.Add(schemaFilePath); Parser.ParseFile(schemaFilePath, out tokens, out errors); s_currentImportingScriptFiles.Remove(schemaFilePath); From 0b39821e5fe4442ee78dc46eba663a5484685389 Mon Sep 17 00:00:00 2001 From: anmenaga Date: Fri, 21 Aug 2020 12:09:33 -0700 Subject: [PATCH 20/64] CodeFactor 2 --- src/System.Management.Automation/DscSupport/MofDscClassCache.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/System.Management.Automation/DscSupport/MofDscClassCache.cs b/src/System.Management.Automation/DscSupport/MofDscClassCache.cs index 655fadeb643..d0167d9a7ef 100755 --- a/src/System.Management.Automation/DscSupport/MofDscClassCache.cs +++ b/src/System.Management.Automation/DscSupport/MofDscClassCache.cs @@ -2231,7 +2231,7 @@ private static bool ImportKeywordsFromScriptFile(string fileName, PSModuleInfo m bool skip = true; foreach (var toImport in resourcesToImport) { - if (WildcardPattern.Get(toImport, WildcardOptions.IgnoreCase)).IsMatch(resourceDefnAst.Name) + if ((WildcardPattern.Get(toImport, WildcardOptions.IgnoreCase)).IsMatch(resourceDefnAst.Name)) { skip = false; break; From 4737ece2430c6f8449bec4f308e6b94b26ad4c48 Mon Sep 17 00:00:00 2001 From: anmenaga Date: Fri, 21 Aug 2020 12:23:54 -0700 Subject: [PATCH 21/64] CodeFactor 3 --- .../DscSupport/MofDscClassCache.cs | 52 ++++++++----------- 1 file changed, 21 insertions(+), 31 deletions(-) diff --git a/src/System.Management.Automation/DscSupport/MofDscClassCache.cs b/src/System.Management.Automation/DscSupport/MofDscClassCache.cs index d0167d9a7ef..e7c7276b9cf 100755 --- a/src/System.Management.Automation/DscSupport/MofDscClassCache.cs +++ b/src/System.Management.Automation/DscSupport/MofDscClassCache.cs @@ -93,7 +93,8 @@ public static class DscClassCache }; // Create a HashSet for fast lookup. According to MSDN, the time complexity of search for an element in a HashSet is O(1) - private static readonly HashSet s_hiddenResourceCache = new HashSet(s_hiddenResourceList, + private static readonly HashSet s_hiddenResourceCache = new HashSet( + s_hiddenResourceList, StringComparer.OrdinalIgnoreCase); // a collection to hold current importing script based resource file @@ -234,9 +235,7 @@ public static void Initialize(Collection errors, List moduleP if (Platform.IsLinux || Platform.IsMacOS) { - // // Load the base schema files. - // ClearCache(); var dscConfigurationDirectory = Environment.GetEnvironmentVariable("DSC_HOME") ?? "/etc/opt/omi/conf/dsc/configuration"; @@ -254,9 +253,7 @@ public static void Initialize(Collection errors, List moduleP var allResourceRoots = new string[] { dscConfigurationDirectory }; - // // Load all of the system resource schema files, searching - // string resources; foreach (var resourceRoot in allResourceRoots) { @@ -293,9 +290,8 @@ public static void Initialize(Collection errors, List moduleP var customResourceRoot = Path.Combine(programFilesDirectory, "WindowsPowerShell\\Configuration"); Debug.Assert(Directory.Exists(customResourceRoot), "%ProgramFiles%\\WindowsPowerShell\\Configuration Directory does not exist"); var allResourceRoots = new string[] { systemResourceRoot, customResourceRoot }; - // + // Load the base schema files. - // ClearCache(); var resourceBaseFile = Path.Combine(systemResourceRoot, "BaseRegistration\\BaseResource.schema.mof"); ImportClasses(resourceBaseFile, s_defaultModuleInfoForResource, errors); @@ -306,9 +302,7 @@ public static void Initialize(Collection errors, List moduleP var metaConfigExtensionFile = Path.Combine(systemResourceRoot, "BaseRegistration\\MSFT_MetaConfigurationExtensionClasses.schema.mof"); ImportClasses(metaConfigExtensionFile, DefaultModuleInfoForMetaConfigResource, errors); - // // Load all of the system resource schema files, searching - // string resources; foreach (var resourceRoot in allResourceRoots) { @@ -928,9 +922,7 @@ private static DynamicKeyword CreateKeywordFromCimClass(string moduleName, Versi string alias = GetFriendlyName(cimClass); var keywordString = string.IsNullOrEmpty(alias) ? resourceName : alias; - // // Skip all of the base, meta, registration and other classes that are not intended to be used directly by a script author - // if (System.Text.RegularExpressions.Regex.Match(keywordString, "^OMI_Base|^OMI_.*Registration", System.Text.RegularExpressions.RegexOptions.IgnoreCase).Success) { return null; @@ -966,9 +958,7 @@ private static DynamicKeyword CreateKeywordFromCimClass(string moduleName, Versi // If it's a resource type, then a resource name is required. keyword.NameMode = isResourceType ? DynamicKeywordNameMode.NameRequired : DynamicKeywordNameMode.NoName; - // // Add the settable properties to the keyword object - // foreach (var prop in cimClass.CimClassProperties) { // If the property is marked as readonly, skip it... @@ -1091,7 +1081,8 @@ private static DynamicKeyword CreateKeywordFromCimClass(string moduleName, Versi { s_tracer.WriteLine( "DSC CreateDynamicKeywordFromClass: same string value '{0}' appears more than once in qualifier 'Values'. Skip the keyword '{1}'.", - key, keyword.Keyword); + key, + keyword.Keyword); return null; } @@ -1248,17 +1239,12 @@ private static void LoadDefaultCimKeywords( } } - // This function is called after parsing the Import-DscResource keyword and it's arguments, but before parsing - // anything else. - // - // + // This function is called after parsing the Import-DscResource keyword and it's arguments, but before parsing anything else. private static ParseError[] ImportResourcePostParse(DynamicKeywordStatementAst kwAst) { var elements = Ast.CopyElements(kwAst.CommandElements); - Diagnostics.Assert(elements[0] is StringConstantExpressionAst && - ((StringConstantExpressionAst)elements[0]).Value.Equals("Import-DscResource", StringComparison.OrdinalIgnoreCase), - "Incorrect ast for expected keyword"); + Diagnostics.Assert(elements[0] is StringConstantExpressionAst && ((StringConstantExpressionAst)elements[0]).Value.Equals("Import-DscResource", StringComparison.OrdinalIgnoreCase), "Incorrect ast for expected keyword"); var commandAst = new CommandAst(kwAst.Extent, elements, TokenKind.Unknown, null); const string nameParam = "Name"; @@ -1456,9 +1442,7 @@ private static ParseError[] ImportResourceCheckSemantics(DynamicKeywordStatement errorList = new List(); } - errorList.Add(new ParseError(kwAst.Extent, - "ImportDscResourceInsideNode", - string.Format(CultureInfo.CurrentCulture, ParserStrings.ImportDscResourceInsideNode))); + errorList.Add(new ParseError(kwAst.Extent, "ImportDscResourceInsideNode", string.Format(CultureInfo.CurrentCulture, ParserStrings.ImportDscResourceInsideNode))); break; } @@ -1537,7 +1521,8 @@ private static ParseError[] CheckMandatoryPropertiesPresent(DynamicKeywordStatem CultureInfo.CurrentCulture, ParserStrings.MissingValueForMandatoryProperty, kwAst.Keyword.Keyword, - kwAst.Keyword.Properties.First(p => StringComparer.OrdinalIgnoreCase.Equals(p.Value.Name, name)).Value.TypeConstraint, name)); + kwAst.Keyword.Properties.First(p => StringComparer.OrdinalIgnoreCase.Equals(p.Value.Name, name)).Value.TypeConstraint, + name)); i++; } @@ -1741,7 +1726,11 @@ public static void LoadResourcesFromModule( } } - private static void LoadPowerShellClassResourcesFromModule(PSModuleInfo primaryModuleInfo, PSModuleInfo moduleInfo, ICollection resourcesToImport, ICollection resourcesFound, + private static void LoadPowerShellClassResourcesFromModule( + PSModuleInfo primaryModuleInfo, + PSModuleInfo moduleInfo, + ICollection resourcesToImport, + ICollection resourcesFound, List errorList, Dictionary functionsToDefine = null, bool recurse = true, @@ -2113,7 +2102,8 @@ private static void ProcessMembers(StringBuilder sb, List embeddedInstan string arrayAffix = isArrayType ? "[]" : string.Empty; - sb.AppendFormat(CultureInfo.InvariantCulture, + sb.AppendFormat( + CultureInfo.InvariantCulture, " {0}{1} {2}{3};\n", MapAttributesToMof(enumNames, attributes, embeddedInstanceType), mofType, @@ -2167,8 +2157,7 @@ private static bool GetResourceDefinitionsFromModule(string fileName, out IEnume errorMessages.Add(error.ToString()); } - errorList.Add(new ParseError(extent, "FailToParseModuleScriptFile", - string.Format(CultureInfo.CurrentCulture, ParserStrings.FailToParseModuleScriptFile, fileName, string.Join(Environment.NewLine, errorMessages)))); + errorList.Add(new ParseError(extent, "FailToParseModuleScriptFile", string.Format(CultureInfo.CurrentCulture, ParserStrings.FailToParseModuleScriptFile, fileName, string.Join(Environment.NewLine, errorMessages)))); } return false; @@ -2676,7 +2665,8 @@ private static void ProcessMembers(Type type, StringBuilder sb, List emb var enumNames = memberType.IsEnum ? Enum.GetNames(memberType) : null; - sb.AppendFormat(CultureInfo.InvariantCulture, + sb.AppendFormat( + CultureInfo.InvariantCulture, " {0}{1} {2}{3};\n", MapAttributesToMof(enumNames, member.GetCustomAttributes(true), embeddedInstanceType), mofType, @@ -2706,7 +2696,7 @@ private static bool ImportKeywordsFromAssembly( foreach (var toImport in resourcesToImport) { - if ((WildcardPattern.Get(toImport, WildcardOptions.IgnoreCase)).IsMatch(r.Name)) + if (WildcardPattern.Get(toImport, WildcardOptions.IgnoreCase).IsMatch(r.Name)) { skip = false; break; From 6512f3832ab5f09b0786fc2f1f0d4790ebfdfb21 Mon Sep 17 00:00:00 2001 From: anmenaga Date: Fri, 21 Aug 2020 13:23:14 -0700 Subject: [PATCH 22/64] CodeFactor 4 --- .../DscSupport/JsonCimDSCParser.cs | 2 +- .../DscSupport/JsonDscClassCache.cs | 48 +++++++++++++------ .../DscSupport/MofCimDSCParser.cs | 8 +--- .../DscSupport/MofDscClassCache.cs | 6 +-- 4 files changed, 38 insertions(+), 26 deletions(-) diff --git a/src/System.Management.Automation/DscSupport/JsonCimDSCParser.cs b/src/System.Management.Automation/DscSupport/JsonCimDSCParser.cs index 8ade67722f5..42d340f70b8 100755 --- a/src/System.Management.Automation/DscSupport/JsonCimDSCParser.cs +++ b/src/System.Management.Automation/DscSupport/JsonCimDSCParser.cs @@ -17,7 +17,7 @@ namespace Microsoft.PowerShell.DesiredStateConfiguration.Json /// internal class CimDSCParser { - private JsonDeserializer _json_deserializer; + private readonly JsonDeserializer _json_deserializer; internal CimDSCParser() { diff --git a/src/System.Management.Automation/DscSupport/JsonDscClassCache.cs b/src/System.Management.Automation/DscSupport/JsonDscClassCache.cs index 1107c683c61..9fc14a87517 100755 --- a/src/System.Management.Automation/DscSupport/JsonDscClassCache.cs +++ b/src/System.Management.Automation/DscSupport/JsonDscClassCache.cs @@ -441,13 +441,23 @@ private static void WriteWarning(string warning) } } + /// + /// Parses json file without adding it to caches or creating dynamic keywords. + /// + /// Path to json file + /// List of classes from json file + public static IEnumerable ReadClassesFromJson(string jsonFilePath) + { + return ReadClassesFromJson(jsonFilePath, false); + } + /// /// Parses json file without adding it to caches or creating dynamic keywords. /// /// Path to json file /// If True PowerShell will use a fresh runspace to do Json deserialization /// List of classes from json file - public static IEnumerable ReadClassesFromJson(string jsonFilePath, bool useNewRunspace = false) + public static IEnumerable ReadClassesFromJson(string jsonFilePath, bool useNewRunspace) { if (string.IsNullOrEmpty(jsonFilePath)) { @@ -467,12 +477,24 @@ public static IEnumerable ReadClassesFromJson(string jsonFilePath, boo /// /// Import CIM classes from the given file. /// - /// - /// - /// - /// - /// - public static IEnumerable ImportClasses(string path, Tuple moduleInfo, Collection errors, bool importInBoxResourcesImplicitly = false) + /// Path to schema file + /// Module information + /// Error collection that will be shown to the user + /// Class objects from schema file + public static IEnumerable ImportClasses(string path, Tuple moduleInfo, Collection errors) + { + return ImportClasses(path, moduleInfo, errors, false); + } + + /// + /// Import CIM classes from the given file. + /// + /// Path to schema file + /// Module information + /// Error collection that will be shown to the user + /// Flag for implicitly imported resource + /// Class objects from schema file + public static IEnumerable ImportClasses(string path, Tuple moduleInfo, Collection errors, bool importInBoxResourcesImplicitly) { if (string.IsNullOrEmpty(path)) { @@ -817,7 +839,7 @@ public static Collection GetCachedKeywords() string moduleName = splittedName[IndexModuleName]; string moduleVersion = splittedName[IndexModuleVersion]; - var keyword = CreateKeywordFromCimClass(moduleName, Version.Parse(moduleVersion), cachedClass.Value.CimClassInstance, null, cachedClass.Value.DscResRunAsCred); + var keyword = CreateKeywordFromCimClass(moduleName, Version.Parse(moduleVersion), cachedClass.Value.CimClassInstance, cachedClass.Value.DscResRunAsCred); if (keyword != null) { keywords.Add(keyword); @@ -837,7 +859,7 @@ public static Collection GetCachedKeywords() /// To Specify RunAsBehavior of the class. private static void CreateAndRegisterKeywordFromCimClass(string moduleName, Version moduleVersion, PSObject cimClass, Dictionary functionsToDefine, DSCResourceRunAsCredential runAsBehavior) { - var keyword = CreateKeywordFromCimClass(moduleName, moduleVersion, cimClass, functionsToDefine, runAsBehavior); + var keyword = CreateKeywordFromCimClass(moduleName, moduleVersion, cimClass, runAsBehavior); if (keyword == null) { return; @@ -871,9 +893,8 @@ private static void CreateAndRegisterKeywordFromCimClass(string moduleName, Vers /// /// /// - /// If true, don't define the keywords, just create the functions. /// To specify RunAs behavior of the class. - private static DynamicKeyword CreateKeywordFromCimClass(string moduleName, Version moduleVersion, dynamic cimClass, Dictionary functionsToDefine, DSCResourceRunAsCredential runAsBehavior) + private static DynamicKeyword CreateKeywordFromCimClass(string moduleName, Version moduleVersion, dynamic cimClass, DSCResourceRunAsCredential runAsBehavior) { var resourceName = cimClass.CimSystemProperties.ClassName; string alias = GetFriendlyName(cimClass); @@ -1838,7 +1859,7 @@ private static PSObject[] GenerateJsonClassesForAst(TypeDefinitionAst typeAst, L _FriendlyName.Properties.Add(new PSNoteProperty("CimType", "String")); _FriendlyName.Properties.Add(new PSNoteProperty("Flags", "EnableOverride, Restricted")); - var _CimClassQualifiers = new PSObject[] {_ClassVersion, _FriendlyName}; + var _CimClassQualifiers = new [] {_ClassVersion, _FriendlyName}; var _CimSystemProperties = new PSObject(); _CimSystemProperties.Properties.Add(new PSNoteProperty("Namespace", null)); @@ -1865,7 +1886,7 @@ private static PSObject[] GenerateJsonClassesForAst(TypeDefinitionAst typeAst, L WriteWarning(string.Format("BaseTypes count for type {0} is {1} and not implemented yet", className, bases.Count)); } - return new PSObject[] {result}; + return new [] {result}; } /// @@ -2109,7 +2130,6 @@ private static bool ImportKeywordsFromScriptFile(string fileName, PSModuleInfo m } var result = false; - var parser = new CimDSCParser(); const WildcardOptions wildcardOptions = WildcardOptions.IgnoreCase | WildcardOptions.CultureInvariant; IEnumerable patternList = SessionStateUtilities.CreateWildcardsFromStrings(module._declaredDscResourceExports, wildcardOptions); diff --git a/src/System.Management.Automation/DscSupport/MofCimDSCParser.cs b/src/System.Management.Automation/DscSupport/MofCimDSCParser.cs index 22584a8805e..8177ec46033 100755 --- a/src/System.Management.Automation/DscSupport/MofCimDSCParser.cs +++ b/src/System.Management.Automation/DscSupport/MofCimDSCParser.cs @@ -328,9 +328,6 @@ internal CimDSCParser(CimMofDeserializer.OnClassNeeded onClassNeeded) { _deserializer = CimMofDeserializer.Create(); _onClassNeeded = onClassNeeded; - - //TODO-AM: this is for debugging: - _deserializer.SchemaValidationOption = MofDeserializerSchemaValidationOption.Ignore; } /// @@ -338,10 +335,7 @@ internal CimDSCParser(CimMofDeserializer.OnClassNeeded onClassNeeded) internal CimDSCParser(CimMofDeserializer.OnClassNeeded onClassNeeded, Microsoft.Management.Infrastructure.Serialization.MofDeserializerSchemaValidationOption validationOptions) { _deserializer = CimMofDeserializer.Create(); - //_deserializer.SchemaValidationOption = validationOptions; - //TODO-AM: this is for debugging: - _deserializer.SchemaValidationOption = MofDeserializerSchemaValidationOption.Ignore; - + _deserializer.SchemaValidationOption = validationOptions; _onClassNeeded = onClassNeeded; } diff --git a/src/System.Management.Automation/DscSupport/MofDscClassCache.cs b/src/System.Management.Automation/DscSupport/MofDscClassCache.cs index e7c7276b9cf..f47961f64c3 100755 --- a/src/System.Management.Automation/DscSupport/MofDscClassCache.cs +++ b/src/System.Management.Automation/DscSupport/MofDscClassCache.cs @@ -1256,9 +1256,7 @@ private static ParseError[] ImportResourcePostParse(DynamicKeywordStatementAst k var errorList = new List(); foreach (var bindingException in bindingResult.BindingExceptions.Values) { - errorList.Add(new ParseError(bindingException.CommandElement.Extent, - "ParameterBindingException", - bindingException.BindingException.Message)); + errorList.Add(new ParseError(bindingException.CommandElement.Extent, "ParameterBindingException", bindingException.BindingException.Message)); } ParameterBindingResult moduleNameBindingResult = null; @@ -2220,7 +2218,7 @@ private static bool ImportKeywordsFromScriptFile(string fileName, PSModuleInfo m bool skip = true; foreach (var toImport in resourcesToImport) { - if ((WildcardPattern.Get(toImport, WildcardOptions.IgnoreCase)).IsMatch(resourceDefnAst.Name)) + if (WildcardPattern.Get(toImport, WildcardOptions.IgnoreCase).IsMatch(resourceDefnAst.Name)) { skip = false; break; From dee8701e2b17d0dbf9bfce2de0021ab3e6a61e75 Mon Sep 17 00:00:00 2001 From: anmenaga Date: Fri, 21 Aug 2020 14:18:23 -0700 Subject: [PATCH 23/64] CodeFactor 5 --- .../DscSupport/MofCimDSCParser.cs | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/System.Management.Automation/DscSupport/MofCimDSCParser.cs b/src/System.Management.Automation/DscSupport/MofCimDSCParser.cs index 8177ec46033..448db86f8ef 100755 --- a/src/System.Management.Automation/DscSupport/MofCimDSCParser.cs +++ b/src/System.Management.Automation/DscSupport/MofCimDSCParser.cs @@ -158,14 +158,16 @@ public static object ConvertCimInstanceToObject(Type targetType, CimInstance ins throw invalidOperationException; } - if (member is FieldInfo) + var memberFieldInfo = member as FieldInfo; + if (memberFieldInfo != null) { - ((FieldInfo)member).SetValue(targetObject, targetValue); + memberFieldInfo.SetValue(targetObject, targetValue); } - if (member is PropertyInfo) + var memberPropertyInfo = member as PropertyInfo; + if (memberPropertyInfo != null) { - ((PropertyInfo)member).SetValue(targetObject, targetValue); + memberPropertyInfo.SetValue(targetObject, targetValue); } } } @@ -320,8 +322,8 @@ public override object Transform(EngineIntrinsics engineIntrinsics, object input /// internal class CimDSCParser { - private CimMofDeserializer _deserializer; - private CimMofDeserializer.OnClassNeeded _onClassNeeded; + private readonly CimMofDeserializer _deserializer; + private readonly CimMofDeserializer.OnClassNeeded _onClassNeeded; /// /// internal CimDSCParser(CimMofDeserializer.OnClassNeeded onClassNeeded) From c690d8f0e4e8e06c33a5456c5f4161cbdb9b4641 Mon Sep 17 00:00:00 2001 From: anmenaga Date: Fri, 21 Aug 2020 16:15:09 -0700 Subject: [PATCH 24/64] Revert "CodeFactor 5" This reverts commit dee8701e2b17d0dbf9bfce2de0021ab3e6a61e75. --- .../DscSupport/MofCimDSCParser.cs | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/src/System.Management.Automation/DscSupport/MofCimDSCParser.cs b/src/System.Management.Automation/DscSupport/MofCimDSCParser.cs index 448db86f8ef..8177ec46033 100755 --- a/src/System.Management.Automation/DscSupport/MofCimDSCParser.cs +++ b/src/System.Management.Automation/DscSupport/MofCimDSCParser.cs @@ -158,16 +158,14 @@ public static object ConvertCimInstanceToObject(Type targetType, CimInstance ins throw invalidOperationException; } - var memberFieldInfo = member as FieldInfo; - if (memberFieldInfo != null) + if (member is FieldInfo) { - memberFieldInfo.SetValue(targetObject, targetValue); + ((FieldInfo)member).SetValue(targetObject, targetValue); } - var memberPropertyInfo = member as PropertyInfo; - if (memberPropertyInfo != null) + if (member is PropertyInfo) { - memberPropertyInfo.SetValue(targetObject, targetValue); + ((PropertyInfo)member).SetValue(targetObject, targetValue); } } } @@ -322,8 +320,8 @@ public override object Transform(EngineIntrinsics engineIntrinsics, object input /// internal class CimDSCParser { - private readonly CimMofDeserializer _deserializer; - private readonly CimMofDeserializer.OnClassNeeded _onClassNeeded; + private CimMofDeserializer _deserializer; + private CimMofDeserializer.OnClassNeeded _onClassNeeded; /// /// internal CimDSCParser(CimMofDeserializer.OnClassNeeded onClassNeeded) From 2c9591b52b0053ca4509473534036aff8ed0607a Mon Sep 17 00:00:00 2001 From: anmenaga Date: Mon, 24 Aug 2020 03:18:19 -0700 Subject: [PATCH 25/64] Updated for a simplified schema --- .../DscSupport/JsonCimDSCParser.cs | 4 +- .../DscSupport/JsonDscClassCache.cs | 289 +++++++++--------- 2 files changed, 143 insertions(+), 150 deletions(-) diff --git a/src/System.Management.Automation/DscSupport/JsonCimDSCParser.cs b/src/System.Management.Automation/DscSupport/JsonCimDSCParser.cs index 42d340f70b8..637537efe76 100755 --- a/src/System.Management.Automation/DscSupport/JsonCimDSCParser.cs +++ b/src/System.Management.Automation/DscSupport/JsonCimDSCParser.cs @@ -39,8 +39,8 @@ internal IEnumerable ParseSchemaJson(string filePath, bool useNewRunsp var result = _json_deserializer.DeserializeClasses(json, useNewRunspace); foreach (dynamic classObject in result) { - string superClassName = classObject.CimSuperClassName; - string className = classObject.CimSystemProperties.ClassName; + string superClassName = classObject.SuperClassName; + string className = classObject.ClassName; if (superClassName?.Equals("OMI_BaseResource", StringComparison.OrdinalIgnoreCase) ?? false) { // Get the name of the file without schema.mof/json extension diff --git a/src/System.Management.Automation/DscSupport/JsonDscClassCache.cs b/src/System.Management.Automation/DscSupport/JsonDscClassCache.cs index 9fc14a87517..e0d517e32a7 100755 --- a/src/System.Management.Automation/DscSupport/JsonDscClassCache.cs +++ b/src/System.Management.Automation/DscSupport/JsonDscClassCache.cs @@ -531,7 +531,7 @@ public static IEnumerable ImportClasses(string path, Tuple ImportClasses(string path, Tuple GetFileDefiningClass(string className) { foreach(dynamic c in classList) { - if (string.Equals(c.CimSystemProperties.ClassName, className, StringComparison.OrdinalIgnoreCase)) + if (string.Equals(c.ClassName, className, StringComparison.OrdinalIgnoreCase)) { files.Add(file); } @@ -803,22 +803,7 @@ private static bool IsMagicProperty(string propertyName) private static string GetFriendlyName(dynamic cimClass) { - try - { - foreach(dynamic qualifier in cimClass.CimClassQualifiers) - { - if (qualifier.Name.Equals("FriendlyName", StringComparison.OrdinalIgnoreCase)) - { - return qualifier.Value as string; - } - } - } - catch (Microsoft.Management.Infrastructure.CimException) - { - // exception means no DSCAlias - } - - return null; + return cimClass.FriendlyName; } /// @@ -896,7 +881,7 @@ private static void CreateAndRegisterKeywordFromCimClass(string moduleName, Vers /// To specify RunAs behavior of the class. private static DynamicKeyword CreateKeywordFromCimClass(string moduleName, Version moduleVersion, dynamic cimClass, DSCResourceRunAsCredential runAsBehavior) { - var resourceName = cimClass.CimSystemProperties.ClassName; + var resourceName = cimClass.ClassName; string alias = GetFriendlyName(cimClass); var keywordString = string.IsNullOrEmpty(alias) ? resourceName : alias; @@ -938,7 +923,7 @@ private static DynamicKeyword CreateKeywordFromCimClass(string moduleName, Versi // so to simplify things we just check superclass to be OMI_BaseResource // with assumption that current code will not work for multi-level class inheritance (which is never used in practice according to DSC team) // this simplification allows us to avoid linking objects together using CimSuperClass field during deserialization from json schema - if ((!string.IsNullOrEmpty(cimClass.CimSuperClassName)) && string.Equals("OMI_BaseResource", cimClass.CimSuperClassName, StringComparison.OrdinalIgnoreCase)) + if ((!string.IsNullOrEmpty(cimClass.SuperClassName)) && string.Equals("OMI_BaseResource", cimClass.SuperClassName, StringComparison.OrdinalIgnoreCase)) { isResourceType = true; } @@ -946,106 +931,93 @@ private static DynamicKeyword CreateKeywordFromCimClass(string moduleName, Versi // If it's a resource type, then a resource name is required. keyword.NameMode = isResourceType ? DynamicKeywordNameMode.NameRequired : DynamicKeywordNameMode.NoName; - // // Add the settable properties to the keyword object - // - foreach (var prop in cimClass.CimClassProperties) + if (cimClass.ClassProperties != null) { - // If the property is marked as readonly, skip it... - if (prop.Flags?.Contains("ReadOnly")) + foreach (var prop in cimClass.ClassProperties) { - continue; - } + // If the property has the Read qualifier, skip it. + if (string.Equals(prop.Qualifiers?.Read?.ToString(), "True", StringComparison.OrdinalIgnoreCase)) + { + continue; + } - // If the property has the Read qualifier, also skip it. - foreach(var qualifier in prop.Qualifiers) - { - if (qualifier.Name.Equals("Read", StringComparison.OrdinalIgnoreCase)) + // If it's one of our magic properties, skip it + if (IsMagicProperty(prop.Name)) { continue; } - } - // If it's one of our magic properties, skip it - if (IsMagicProperty(prop.Name)) - { - continue; - } + if (runAsBehavior == DSCResourceRunAsCredential.NotSupported) + { + if (string.Equals(prop.Name, "PsDscRunAsCredential", StringComparison.OrdinalIgnoreCase)) + { + // skip adding PsDscRunAsCredential to the dynamic word for the dsc resource. + continue; + } + } - if (runAsBehavior == DSCResourceRunAsCredential.NotSupported) - { - if (string.Equals(prop.Name, "PsDscRunAsCredential", StringComparison.OrdinalIgnoreCase)) + // If it's one of our reserved properties, save it for error reporting + if (System.Text.RegularExpressions.Regex.Match(prop.Name, reservedProperties, System.Text.RegularExpressions.RegexOptions.IgnoreCase).Success) { - // skip adding PsDscRunAsCredential to the dynamic word for the dsc resource. + keyword.HasReservedProperties = true; continue; } - } - // If it's one of our reserved properties, save it for error reporting - if (System.Text.RegularExpressions.Regex.Match(prop.Name, reservedProperties, System.Text.RegularExpressions.RegexOptions.IgnoreCase).Success) - { - keyword.HasReservedProperties = true; - continue; - } - - // Otherwise, add it to the Keyword List. - var keyProp = new System.Management.Automation.Language.DynamicKeywordProperty(); - keyProp.Name = prop.Name; - // Set the mandatory flag if appropriate - if (prop.Flags?.Contains("Key")) - { - keyProp.Mandatory = true; - keyProp.IsKey = true; - } + // Otherwise, add it to the Keyword List. + var keyProp = new System.Management.Automation.Language.DynamicKeywordProperty(); + keyProp.Name = prop.Name; - // Copy the type name string. If it's an embedded instance, need to grab it from the ReferenceClassName - bool referenceClassNameIsNullOrEmpty = string.IsNullOrEmpty(prop.ReferenceClassName); - if (prop.CimType == "Instance" && !referenceClassNameIsNullOrEmpty) - { - keyProp.TypeConstraint = prop.ReferenceClassName; - } - else if (prop.CimType == "InstanceArray" && !referenceClassNameIsNullOrEmpty) - { - keyProp.TypeConstraint = prop.ReferenceClassName + "[]"; - } - else - { - keyProp.TypeConstraint = prop.CimType.ToString(); - } + // Copy the type name string. If it's an embedded instance, need to grab it from the ReferenceClassName + bool referenceClassNameIsNullOrEmpty = string.IsNullOrEmpty(prop.ReferenceClassName); + if (prop.CimType == "Instance" && !referenceClassNameIsNullOrEmpty) + { + keyProp.TypeConstraint = prop.ReferenceClassName; + } + else if (prop.CimType == "InstanceArray" && !referenceClassNameIsNullOrEmpty) + { + keyProp.TypeConstraint = prop.ReferenceClassName + "[]"; + } + else + { + keyProp.TypeConstraint = prop.CimType.ToString(); + } - string[] valueMap = null; - foreach (var qualifier in prop.Qualifiers) - { // Check to see if there is a Values attribute and save the list of allowed values if so. - if (string.Equals(qualifier.Name, "Values", StringComparison.OrdinalIgnoreCase) && qualifier.CimType == "StringArray") + var values = prop.Qualifiers?.Values; + if (values != null) { - int count = qualifier.Value.Length; - string[] values = new string[count]; - for(int i = 0; i < count; i++) + foreach(var val in values) { - values[i] = qualifier.Value[i].ToString(); + keyProp.Values.Add(val.ToString()); } - keyProp.Values.AddRange(values); } // Check to see if there is a ValueMap attribute and save the list of allowed values if so. - if (string.Equals(qualifier.Name, "ValueMap", StringComparison.OrdinalIgnoreCase) && qualifier.CimType == "StringArray") + var nativeValueMap = prop.Qualifiers?.ValueMap; + List valueMap = null; + if (nativeValueMap != null) { - int count = qualifier.Value.Length; - valueMap = new string[count]; - for(int i = 0; i < count; i++) + valueMap = new List(); + foreach(var val in nativeValueMap) { - valueMap[i] = qualifier.Value[i].ToString(); + valueMap.Add(val.ToString()); } } // Check to see if this property has the Required qualifier associated with it. - if (string.Equals(qualifier.Name, "Required", StringComparison.OrdinalIgnoreCase) && - qualifier.CimType == "Boolean" && (bool)qualifier.Value) + if (string.Equals(prop.Qualifiers?.Required?.ToString(), "True", StringComparison.OrdinalIgnoreCase)) { keyProp.Mandatory = true; } + // Check to see if this property has the Key qualifier associated with it. + if (string.Equals(prop.Qualifiers?.Key?.ToString(), "True", StringComparison.OrdinalIgnoreCase)) + { + keyProp.Mandatory = true; + keyProp.IsKey = true; + } + // set the property to mandatory is specified for the resource. if (runAsBehavior == DSCResourceRunAsCredential.Mandatory) { @@ -1054,36 +1026,36 @@ private static DynamicKeyword CreateKeywordFromCimClass(string moduleName, Versi keyProp.Mandatory = true; } } - } - if (valueMap != null && keyProp.Values.Count > 0) - { - if (valueMap.Length != keyProp.Values.Count) + if (valueMap != null && keyProp.Values.Count > 0) { - s_tracer.WriteLine( - "DSC CreateDynamicKeywordFromClass: the count of values for qualifier 'Values' and 'ValueMap' doesn't match. count of 'Values': {0}, count of 'ValueMap': {1}. Skip the keyword '{2}'.", - keyProp.Values.Count, valueMap.Length, keyword.Keyword); - return null; - } - - for (int index = 0; index < valueMap.Length; index++) - { - string key = keyProp.Values[index]; - string value = valueMap[index]; - - if (keyProp.ValueMap.ContainsKey(key)) + if (valueMap.Count != keyProp.Values.Count) { s_tracer.WriteLine( - "DSC CreateDynamicKeywordFromClass: same string value '{0}' appears more than once in qualifier 'Values'. Skip the keyword '{1}'.", - key, keyword.Keyword); + "DSC CreateDynamicKeywordFromClass: the count of values for qualifier 'Values' and 'ValueMap' doesn't match. count of 'Values': {0}, count of 'ValueMap': {1}. Skip the keyword '{2}'.", + keyProp.Values.Count, valueMap.Count, keyword.Keyword); return null; } - keyProp.ValueMap.Add(key, value); + for (int index = 0; index < valueMap.Count; index++) + { + string key = keyProp.Values[index]; + string value = valueMap[index]; + + if (keyProp.ValueMap.ContainsKey(key)) + { + s_tracer.WriteLine( + "DSC CreateDynamicKeywordFromClass: same string value '{0}' appears more than once in qualifier 'Values'. Skip the keyword '{1}'.", + key, keyword.Keyword); + return null; + } + + keyProp.ValueMap.Add(key, value); + } } - } - keyword.Properties.Add(prop.Name, keyProp); + keyword.Properties.Add(prop.Name, keyProp); + } } // update specific keyword with range constraints @@ -1199,7 +1171,7 @@ private static void LoadDefaultCimKeywords(Dictionary funct foreach (dynamic cimClass in GetCachedClasses()) { - var className = cimClass.CimClassInstance.CimSystemProperties.ClassName; + var className = cimClass.CimClassInstance.ClassName; var moduleInfo = ByClassModuleCache[className]; CreateAndRegisterKeywordFromCimClass(moduleInfo.Item1, moduleInfo.Item2, cimClass.CimClassInstance, functionsToDefine, cimClass.DscResRunAsCred); } @@ -1847,33 +1819,14 @@ private static PSObject[] GenerateJsonClassesForAst(TypeDefinitionAst typeAst, L cimSuperClassName = "OMI_BaseResource"; } - var _ClassVersion = new PSObject(); - _ClassVersion.Properties.Add(new PSNoteProperty("Name", "ClassVersion")); - _ClassVersion.Properties.Add(new PSNoteProperty("Value", "1.0.0")); - _ClassVersion.Properties.Add(new PSNoteProperty("CimType", "String")); - _ClassVersion.Properties.Add(new PSNoteProperty("Flags", "EnableOverride, Restricted")); - - var _FriendlyName = new PSObject(); - _FriendlyName.Properties.Add(new PSNoteProperty("Name", "FriendlyName")); - _FriendlyName.Properties.Add(new PSNoteProperty("Value", className)); - _FriendlyName.Properties.Add(new PSNoteProperty("CimType", "String")); - _FriendlyName.Properties.Add(new PSNoteProperty("Flags", "EnableOverride, Restricted")); - - var _CimClassQualifiers = new [] {_ClassVersion, _FriendlyName}; - - var _CimSystemProperties = new PSObject(); - _CimSystemProperties.Properties.Add(new PSNoteProperty("Namespace", null)); - _CimSystemProperties.Properties.Add(new PSNoteProperty("ServerName", null)); - _CimSystemProperties.Properties.Add(new PSNoteProperty("ClassName", className)); - _CimSystemProperties.Properties.Add(new PSNoteProperty("Path", null)); - var _CimClassProperties = ProcessMembers(embeddedInstanceTypes, typeAst, className).ToArray(); var result = new PSObject(); - result.Properties.Add(new PSNoteProperty("CimSuperClassName", cimSuperClassName)); - result.Properties.Add(new PSNoteProperty("CimClassProperties", _CimClassProperties)); - result.Properties.Add(new PSNoteProperty("CimClassQualifiers", _CimClassQualifiers)); - result.Properties.Add(new PSNoteProperty("CimSystemProperties", _CimSystemProperties)); + result.Properties.Add(new PSNoteProperty("ClassName", className)); + result.Properties.Add(new PSNoteProperty("ClassVersion", "1.0.0")); + result.Properties.Add(new PSNoteProperty("FriendlyName", className)); + result.Properties.Add(new PSNoteProperty("SuperClassName", cimSuperClassName)); + result.Properties.Add(new PSNoteProperty("ClassProperties", _CimClassProperties)); Queue bases = new Queue(); foreach (var b in typeAst.BaseTypes) @@ -2030,10 +1983,56 @@ private static List ProcessMembers(List embeddedInstanceTypes, var propertyObject = new PSObject(); propertyObject.Properties.Add(new PSNoteProperty(@"Name", member.Name)); - propertyObject.Properties.Add(new PSNoteProperty(@"Value", "null")); propertyObject.Properties.Add(new PSNoteProperty(@"CimType", mofType + (isArrayType ? "Array" : ""))); - propertyObject.Properties.Add(new PSNoteProperty(@"Flags", "Property, NullValue")); - propertyObject.Properties.Add(new PSNoteProperty(@"Qualifiers", new PSObject[0])); //TODO: mark Keys + + PSObject attributesPSObject = null; + foreach (var attr in attributes) + { + var dscProperty = attr as DscPropertyAttribute; + if (dscProperty != null) + { + if (attributesPSObject == null) + { + attributesPSObject = new PSObject(); + } + + if (dscProperty.Key) + { + attributesPSObject.Properties.Add(new PSNoteProperty("Key", true)); + } + + if (dscProperty.Mandatory) + { + attributesPSObject.Properties.Add(new PSNoteProperty("Required", true)); + } + + if (dscProperty.NotConfigurable) + { + attributesPSObject.Properties.Add(new PSNoteProperty("Read", true)); + } + + continue; + } + + var validateSet = attr as ValidateSetAttribute; + if (validateSet != null) + { + if (attributesPSObject == null) + { + attributesPSObject = new PSObject(); + } + + List ValueMap = new List(validateSet.ValidValues); + List Values = new List(validateSet.ValidValues); + attributesPSObject.Properties.Add(new PSNoteProperty("ValueMap", ValueMap)); + attributesPSObject.Properties.Add(new PSNoteProperty("Values", Values)); + } + } + + if (attributesPSObject != null) + { + propertyObject.Properties.Add(new PSNoteProperty(@"Qualifiers", attributesPSObject)); + } result.Add(propertyObject); } @@ -2206,8 +2205,8 @@ private static bool ImportKeywordsFromScriptFile(string fileName, PSModuleInfo m private static bool IsSameNestedObject(dynamic oldClass, dynamic newClass) { // #1 both the classes should be nested class and not DSC resource - if ((oldClass.CimSuperClassName != null && string.Equals("OMI_BaseResource", oldClass.CimSuperClassName, StringComparison.OrdinalIgnoreCase)) || - (newClass.CimSuperClassName != null && string.Equals("OMI_BaseResource", newClass.CimSuperClassName, StringComparison.OrdinalIgnoreCase))) + if ((oldClass.SuperClassName != null && string.Equals("OMI_BaseResource", oldClass.SuperClassName, StringComparison.OrdinalIgnoreCase)) || + (newClass.SuperClassName != null && string.Equals("OMI_BaseResource", newClass.SuperClassName, StringComparison.OrdinalIgnoreCase))) { return false; } @@ -2323,7 +2322,7 @@ private static void ProcessJsonForDynamicKeywords(PSModuleInfo module, ICollecti { foreach (dynamic c in classes) { - var className = c.CimSystemProperties.ClassName; + var className = c.ClassName; string alias = GetFriendlyName(c); var friendlyName = string.IsNullOrEmpty(alias) ? className : alias; if (!CacheResourcesFromMultipleModuleVersions) @@ -2425,10 +2424,8 @@ public static bool ImportCimKeywordsFromModule(PSModuleInfo module, string resou } else if (Directory.Exists(dscResourcesPath)) { - // // Cannot find the schema file, then resourceName may be a friendly name, // try to search all DscResources' schemas under DscResources folder - // try { var dscResourceDirectories = Directory.GetDirectories(dscResourcesPath); @@ -2442,9 +2439,7 @@ public static bool ImportCimKeywordsFromModule(PSModuleInfo module, string resou var classes = GetCachedClassByFileName(tempSchemaFilepath) ?? ImportClasses(tempSchemaFilepath, new Tuple(module.Name, module.Version), errors); if (classes != null) { - // // search if class's friendly name is the given resourceName - // foreach (var c in classes) { var alias = GetFriendlyName(c); @@ -2461,9 +2456,7 @@ public static bool ImportCimKeywordsFromModule(PSModuleInfo module, string resou } catch (Exception) { - // // silent in case of exception - // } } @@ -2477,7 +2470,7 @@ public static bool ImportCimKeywordsFromModule(PSModuleInfo module, string resou /// private static void ClearImplicitlyImportedFlagFromResourceInClassCache(PSModuleInfo module, dynamic cimClass) { - var className = cimClass.CimSystemProperties.ClassName; + var className = cimClass.ClassName; var alias = GetFriendlyName(cimClass); var friendlyName = string.IsNullOrEmpty(alias) ? className : alias; var moduleQualifiedResourceName = GetModuleQualifiedResourceName(module.Name, module.Version.ToString(), className, friendlyName); From cdeeb290095d26b9fbd920a0e886b97668cfeb38 Mon Sep 17 00:00:00 2001 From: anmenaga Date: Tue, 25 Aug 2020 11:09:25 -0700 Subject: [PATCH 26/64] Parser updates --- .../engine/parser/Parser.cs | 86 ++++++++++++++++++- 1 file changed, 84 insertions(+), 2 deletions(-) diff --git a/src/System.Management.Automation/engine/parser/Parser.cs b/src/System.Management.Automation/engine/parser/Parser.cs index e3245e91564..38c2b5d0ef3 100644 --- a/src/System.Management.Automation/engine/parser/Parser.cs +++ b/src/System.Management.Automation/engine/parser/Parser.cs @@ -2934,6 +2934,7 @@ private StatementAst ConfigurationStatementRule(IEnumerable custom // Runspaces.Runspace localRunspace = null; bool topLevel = false; + bool useJsonSchema = true; try { // At this point, we'll need a runspace to use to hold the metadata for the parse. If there is no @@ -2997,7 +2998,80 @@ private StatementAst ConfigurationStatementRule(IEnumerable custom { // Load the default CIM keywords Collection CIMKeywordErrors = new Collection(); - Microsoft.PowerShell.DesiredStateConfiguration.Internal.DscClassCache.LoadDefaultCimKeywords(CIMKeywordErrors); + if (ExperimentalFeature.IsEnabled("PSDscJsonSchemaSupport")) + { + // In addition to checking if experimental feature is enabled + // also check if v3.0 (or later) of PSDesiredStateConfiguration module is available + + // pre-v3 module is mof-based + // having a pre-v3 module pre-loaded gives user a way to force usage of mof-based APIs for dsc configuration compilation + + // First check if PSDesiredStateConfiguration is already loaded + // if pre-v3 is already loaded then use old mof-based APIs + // otherwise if v3.0.0 or later is already loaded then use new json-based APIS + // otherwise no version of the module is currently loaded - then try to load v3 and use json-based APIs + // if v3 can not be found/loaded then use old mof-based APIs + + p.AddCommand(new CmdletInfo("Get-Module", typeof(Microsoft.PowerShell.Commands.GetModuleCommand))); + p.AddParameter("Name", "PSDesiredStateConfiguration"); + + bool v3IsLoaded = false; + bool prev3IsLoaded = false; + foreach(var moduleInfo in p.Invoke()) + { + if (((Version)moduleInfo.Properties["Version"].Value).Major < 3) + { + prev3IsLoaded = true; + } + else + { + v3IsLoaded = true; + } + } + + p.Commands.Clear(); + + if (prev3IsLoaded) + { + useJsonSchema = false; + } + else + { + // if v3 is already loaded we don't need to do anything extra - just use json APIs + // if it is not loaded - try to load it + if (!v3IsLoaded) + { + p.AddCommand(new CmdletInfo("Import-Module", typeof(Microsoft.PowerShell.Commands.ImportModuleCommand))); + p.AddParameter("PassThru", true); + p.AddParameter("FullyQualifiedName", new Microsoft.PowerShell.Commands.ModuleSpecification() + { + Name = "PSDesiredStateConfiguration", + Version = new Version(3,0,0) + }); + + var newModuleInfo = p.Invoke(); + p.Commands.Clear(); + if (newModuleInfo.Count == 0) + { + // v3 of the module was not found/loaded successfully - use old mof APIs + useJsonSchema = false; + } + } + } + + if (useJsonSchema) + { + Microsoft.PowerShell.DesiredStateConfiguration.Json.DscClassCache.LoadDefaultCimKeywords(CIMKeywordErrors); + } + else + { + Microsoft.PowerShell.DesiredStateConfiguration.Internal.DscClassCache.LoadDefaultCimKeywords(CIMKeywordErrors); + } + } + else + { + Microsoft.PowerShell.DesiredStateConfiguration.Internal.DscClassCache.LoadDefaultCimKeywords(CIMKeywordErrors); + } // Report any errors encountered while loading CIM dynamic keywords. if (CIMKeywordErrors.Count > 0) @@ -3239,7 +3313,15 @@ private StatementAst ConfigurationStatementRule(IEnumerable custom // Clear out all of the cached classes and keywords. // They will need to be reloaded when the generated function is actually run. // - Microsoft.PowerShell.DesiredStateConfiguration.Internal.DscClassCache.ClearCache(); + if (useJsonSchema) + { + Microsoft.PowerShell.DesiredStateConfiguration.Json.DscClassCache.ClearCache(); + } + else + { + Microsoft.PowerShell.DesiredStateConfiguration.Internal.DscClassCache.ClearCache(); + } + System.Management.Automation.Language.DynamicKeyword.Reset(); } From 6419de09f840150ebf7d7d346b2df3da5ca3a240 Mon Sep 17 00:00:00 2001 From: anmenaga Date: Tue, 25 Aug 2020 12:48:51 -0700 Subject: [PATCH 27/64] Fixed null-check in windows code --- .../DscSupport/JsonDscClassCache.cs | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/src/System.Management.Automation/DscSupport/JsonDscClassCache.cs b/src/System.Management.Automation/DscSupport/JsonDscClassCache.cs index e0d517e32a7..02ce90ba3b4 100755 --- a/src/System.Management.Automation/DscSupport/JsonDscClassCache.cs +++ b/src/System.Management.Automation/DscSupport/JsonDscClassCache.cs @@ -295,16 +295,19 @@ public static void Initialize(Collection errors, List moduleP } else { - foreach (string moduleFolderPath in modulePathList) + if (modulePathList != null) { - if (!Directory.Exists(moduleFolderPath)) + foreach (string moduleFolderPath in modulePathList) { - continue; - } + if (!Directory.Exists(moduleFolderPath)) + { + continue; + } - foreach (string moduleDir in Directory.EnumerateDirectories(moduleFolderPath)) - { - modulePaths.Add(moduleDir); + foreach (string moduleDir in Directory.EnumerateDirectories(moduleFolderPath)) + { + modulePaths.Add(moduleDir); + } } } } From ce194851fd41f171f3d32a72faf30ec2e1ab7e75 Mon Sep 17 00:00:00 2001 From: anmenaga Date: Wed, 26 Aug 2020 13:54:03 -0700 Subject: [PATCH 28/64] Inverted bool condition in parser --- .../engine/parser/Parser.cs | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/System.Management.Automation/engine/parser/Parser.cs b/src/System.Management.Automation/engine/parser/Parser.cs index 38c2b5d0ef3..ee5ae91f0e7 100644 --- a/src/System.Management.Automation/engine/parser/Parser.cs +++ b/src/System.Management.Automation/engine/parser/Parser.cs @@ -2934,7 +2934,7 @@ private StatementAst ConfigurationStatementRule(IEnumerable custom // Runspaces.Runspace localRunspace = null; bool topLevel = false; - bool useJsonSchema = true; + bool useJsonSchema = false; try { // At this point, we'll need a runspace to use to hold the metadata for the parse. If there is no @@ -3009,7 +3009,7 @@ private StatementAst ConfigurationStatementRule(IEnumerable custom // First check if PSDesiredStateConfiguration is already loaded // if pre-v3 is already loaded then use old mof-based APIs // otherwise if v3.0.0 or later is already loaded then use new json-based APIS - // otherwise no version of the module is currently loaded - then try to load v3 and use json-based APIs + // otherwise no version of the module is currently loaded - then try to load v3 and use json-based APIs is loading is successful // if v3 can not be found/loaded then use old mof-based APIs p.AddCommand(new CmdletInfo("Get-Module", typeof(Microsoft.PowerShell.Commands.GetModuleCommand))); @@ -3031,15 +3031,15 @@ private StatementAst ConfigurationStatementRule(IEnumerable custom p.Commands.Clear(); - if (prev3IsLoaded) - { - useJsonSchema = false; - } - else + if (!prev3IsLoaded) { // if v3 is already loaded we don't need to do anything extra - just use json APIs // if it is not loaded - try to load it - if (!v3IsLoaded) + if (v3IsLoaded) + { + useJsonSchema = true; + } + else { p.AddCommand(new CmdletInfo("Import-Module", typeof(Microsoft.PowerShell.Commands.ImportModuleCommand))); p.AddParameter("PassThru", true); @@ -3051,10 +3051,10 @@ private StatementAst ConfigurationStatementRule(IEnumerable custom var newModuleInfo = p.Invoke(); p.Commands.Clear(); - if (newModuleInfo.Count == 0) + if (newModuleInfo.Count > 0) { - // v3 of the module was not found/loaded successfully - use old mof APIs - useJsonSchema = false; + // v3 of the module was found/loaded successfully - use new json APIs + useJsonSchema = true; } } } From 7869f1d72de9459854204ad100c48b93aadf825c Mon Sep 17 00:00:00 2001 From: anmenaga Date: Tue, 1 Sep 2020 11:04:29 -0700 Subject: [PATCH 29/64] Feedback 5 --- .../DscSupport/JsonCimDSCParser.cs | 4 +- .../DscSupport/JsonDeserializer.cs | 2 +- .../DscSupport/JsonDscClassCache.cs | 47 +++++++++---------- .../engine/parser/Parser.cs | 4 +- 4 files changed, 28 insertions(+), 29 deletions(-) diff --git a/src/System.Management.Automation/DscSupport/JsonCimDSCParser.cs b/src/System.Management.Automation/DscSupport/JsonCimDSCParser.cs index 637537efe76..9cf6472c19b 100755 --- a/src/System.Management.Automation/DscSupport/JsonCimDSCParser.cs +++ b/src/System.Management.Automation/DscSupport/JsonCimDSCParser.cs @@ -10,7 +10,7 @@ using System.Management.Automation; using System.Security; -namespace Microsoft.PowerShell.DesiredStateConfiguration.Json +namespace Microsoft.PowerShell.DesiredStateConfiguration.Internal.Json { /// /// Class that does high level Cim schema parsing. @@ -26,9 +26,9 @@ internal CimDSCParser() internal IEnumerable ParseSchemaJson(string filePath, bool useNewRunspace = false) { - string json = File.ReadAllText(filePath); try { + string json = File.ReadAllText(filePath); string fileNameDefiningClass = Path.GetFileNameWithoutExtension(filePath); int dotIndex = fileNameDefiningClass.IndexOf(".schema", StringComparison.InvariantCultureIgnoreCase); if (dotIndex != -1) diff --git a/src/System.Management.Automation/DscSupport/JsonDeserializer.cs b/src/System.Management.Automation/DscSupport/JsonDeserializer.cs index f02159423de..d0319aaaa55 100755 --- a/src/System.Management.Automation/DscSupport/JsonDeserializer.cs +++ b/src/System.Management.Automation/DscSupport/JsonDeserializer.cs @@ -7,7 +7,7 @@ using System.Management.Automation; using System.Management.Automation.Runspaces; -namespace Microsoft.PowerShell.DesiredStateConfiguration.Json +namespace Microsoft.PowerShell.DesiredStateConfiguration.Internal.Json { internal class JsonDeserializer { diff --git a/src/System.Management.Automation/DscSupport/JsonDscClassCache.cs b/src/System.Management.Automation/DscSupport/JsonDscClassCache.cs index 02ce90ba3b4..8cee010f693 100755 --- a/src/System.Management.Automation/DscSupport/JsonDscClassCache.cs +++ b/src/System.Management.Automation/DscSupport/JsonDscClassCache.cs @@ -19,7 +19,7 @@ using Microsoft.PowerShell.Commands; -namespace Microsoft.PowerShell.DesiredStateConfiguration.Json +namespace Microsoft.PowerShell.DesiredStateConfiguration.Internal.Json { /// /// Class that defines Dsc cache entries. @@ -914,15 +914,8 @@ private static DynamicKeyword CreateKeywordFromCimClass(string moduleName, Versi // see if it's a resource type i.e. it inherits from OMI_BaseResource bool isResourceType = false; - /*for (var classToCheck = cimClass; !string.IsNullOrEmpty(classToCheck.CimSuperClassName); classToCheck = classToCheck.CimSuperClass) - { - if (string.Equals("OMI_BaseResource", classToCheck.CimSuperClassName, StringComparison.OrdinalIgnoreCase) || string.Equals("OMI_MetaConfigurationResource", classToCheck.CimSuperClassName, StringComparison.OrdinalIgnoreCase)) - { - isResourceType = true; - break; - } - }*/ - // code above is the only place that references CimSuperClass + + // previous version of this code was the only place that referenced CimSuperClass // so to simplify things we just check superclass to be OMI_BaseResource // with assumption that current code will not work for multi-level class inheritance (which is never used in practice according to DSC team) // this simplification allows us to avoid linking objects together using CimSuperClass field during deserialization from json schema @@ -1076,6 +1069,12 @@ private static DynamicKeyword CreateKeywordFromCimClass(string moduleName, Versi /// private static void UpdateKnownRestriction(DynamicKeyword keyword) { + const int RefreshFrequencyMin = 30; + const int RefreshFrequencyMax = 44640; + + const int ConfigurationModeFrequencyMin = 15; + const int ConfigurationModeFrequencyMax = 44640; + if ( string.Equals(keyword.ResourceName, "MSFT_DSCMetaConfigurationV2", StringComparison.OrdinalIgnoreCase) @@ -1085,12 +1084,12 @@ private static void UpdateKnownRestriction(DynamicKeyword keyword) { if (keyword.Properties["RefreshFrequencyMins"] != null) { - keyword.Properties["RefreshFrequencyMins"].Range = new Tuple(30, 44640); + keyword.Properties["RefreshFrequencyMins"].Range = new Tuple(RefreshFrequencyMin, RefreshFrequencyMax); } if (keyword.Properties["ConfigurationModeFrequencyMins"] != null) { - keyword.Properties["ConfigurationModeFrequencyMins"].Range = new Tuple(15, 44640); + keyword.Properties["ConfigurationModeFrequencyMins"].Range = new Tuple(ConfigurationModeFrequencyMin, ConfigurationModeFrequencyMax); } if (keyword.Properties["DebugMode"] != null) @@ -2924,7 +2923,7 @@ function Test-DependsOn if ($DependsOnVar -notmatch '^\[[a-z]\w*\][a-z_0-9][a-z_0-9\p{Zs}\.\\-]*$') { Update-ConfigurationErrorCount - Write-Error -ErrorRecord ([Microsoft.PowerShell.DesiredStateConfiguration.Json.DscClassCache]::GetBadlyFormedRequiredResourceIdErrorRecord($DependsOnVar, $resourceId)) + Write-Error -ErrorRecord ([Microsoft.PowerShell.DesiredStateConfiguration.Internal.Json.DscClassCache]::GetBadlyFormedRequiredResourceIdErrorRecord($DependsOnVar, $resourceId)) } # Fix up DependsOn for nested names @@ -3000,7 +2999,7 @@ function Test-DependsOn if (Test-NodeResources $resourceId) { Update-ConfigurationErrorCount - Write-Error -ErrorRecord ([Microsoft.PowerShell.DesiredStateConfiguration.Json.DscClassCache]::DuplicateResourceIdInNodeStatementErrorRecord($resourceId, (Get-PSCurrentConfigurationNode))) + Write-Error -ErrorRecord ([Microsoft.PowerShell.DesiredStateConfiguration.Internal.Json.DscClassCache]::DuplicateResourceIdInNodeStatementErrorRecord($resourceId, (Get-PSCurrentConfigurationNode))) } else { @@ -3016,7 +3015,7 @@ function Test-DependsOn if($null -ne $value['PsDscRunAsCredential']) { Update-ConfigurationErrorCount - Write-Error -ErrorRecord ([Microsoft.PowerShell.DesiredStateConfiguration.Json.DscClassCache]::PsDscRunAsCredentialMergeErrorForCompositeResources($resourceId)) + Write-Error -ErrorRecord ([Microsoft.PowerShell.DesiredStateConfiguration.Internal.Json.DscClassCache]::PsDscRunAsCredentialMergeErrorForCompositeResources($resourceId)) } # Set the Value of RunAsCred to that of outer configuration else @@ -3035,14 +3034,14 @@ function Test-DependsOn if($value['RefreshMode'] -eq 'Pull' -and -not $value['ConfigurationSource']) { Update-ConfigurationErrorCount - Write-Error -ErrorRecord ([Microsoft.PowerShell.DesiredStateConfiguration.Json.DscClassCache]::GetPullModeNeedConfigurationSource($resourceId)) + Write-Error -ErrorRecord ([Microsoft.PowerShell.DesiredStateConfiguration.Internal.Json.DscClassCache]::GetPullModeNeedConfigurationSource($resourceId)) } # Verify that RefreshMode is not Disabled for Partial configuration if($value['RefreshMode'] -eq 'Disabled') { Update-ConfigurationErrorCount - Write-Error -ErrorRecord ([Microsoft.PowerShell.DesiredStateConfiguration.Json.DscClassCache]::DisabledRefreshModeNotValidForPartialConfig($resourceId)) + Write-Error -ErrorRecord ([Microsoft.PowerShell.DesiredStateConfiguration.Internal.Json.DscClassCache]::DisabledRefreshModeNotValidForPartialConfig($resourceId)) } if($null -ne $value['ConfigurationSource']) @@ -3063,7 +3062,7 @@ function Test-DependsOn if (($ExclusiveResource -notmatch '^[a-z][a-z_0-9]*\\[a-z][a-z_0-9]*$') -and ($ExclusiveResource -notmatch '^[a-z][a-z_0-9]*$') -and ($ExclusiveResource -notmatch '^[a-z][a-z_0-9]*\\\*$')) { Update-ConfigurationErrorCount - Write-Error -ErrorRecord ([Microsoft.PowerShell.DesiredStateConfiguration.Json.DscClassCache]::GetBadlyFormedExclusiveResourceIdErrorRecord($ExclusiveResource, $resourceId)) + Write-Error -ErrorRecord ([Microsoft.PowerShell.DesiredStateConfiguration.Internal.Json.DscClassCache]::GetBadlyFormedExclusiveResourceIdErrorRecord($ExclusiveResource, $resourceId)) } } @@ -3091,7 +3090,7 @@ function Test-DependsOn { if ($OldMetaConfig -and (-not ($V1MetaConfigPropertyList -contains $key))) { - Write-Error -ErrorRecord ([Microsoft.PowerShell.DesiredStateConfiguration.Json.DscClassCache]::InvalidLocalConfigurationManagerPropertyErrorRecord($key, ($V1MetaConfigPropertyList -join ', '))) + Write-Error -ErrorRecord ([Microsoft.PowerShell.DesiredStateConfiguration.Internal.Json.DscClassCache]::InvalidLocalConfigurationManagerPropertyErrorRecord($key, ($V1MetaConfigPropertyList -join ', '))) Update-ConfigurationErrorCount } # see if there is a list of allowed values for this property (similar to an enum) @@ -3101,7 +3100,7 @@ function Test-DependsOn { if(($null -eq $value[$key]) -and ($allowedValues -notcontains $value[$key])) { - Write-Error -ErrorRecord ([Microsoft.PowerShell.DesiredStateConfiguration.Json.DscClassCache]::InvalidValueForPropertyErrorRecord($key, ""$($value[$key])"", $keywordData.Keyword, ($allowedValues -join ', '))) + Write-Error -ErrorRecord ([Microsoft.PowerShell.DesiredStateConfiguration.Internal.Json.DscClassCache]::InvalidValueForPropertyErrorRecord($key, ""$($value[$key])"", $keywordData.Keyword, ($allowedValues -join ', '))) Update-ConfigurationErrorCount } else @@ -3118,7 +3117,7 @@ function Test-DependsOn if($notAllowedValue) { $notAllowedValue = $notAllowedValue.Substring(0, $notAllowedValue.Length -2) - Write-Error -ErrorRecord ([Microsoft.PowerShell.DesiredStateConfiguration.Json.DscClassCache]::UnsupportedValueForPropertyErrorRecord($key, $notAllowedValue, $keywordData.Keyword, ($allowedValues -join ', '))) + Write-Error -ErrorRecord ([Microsoft.PowerShell.DesiredStateConfiguration.Internal.Json.DscClassCache]::UnsupportedValueForPropertyErrorRecord($key, $notAllowedValue, $keywordData.Keyword, ($allowedValues -join ', '))) Update-ConfigurationErrorCount } } @@ -3131,7 +3130,7 @@ function Test-DependsOn $castedValue = $value[$key] -as [int] if((($castedValue -is [int]) -and (($castedValue -lt $keywordData.Properties[$key].Range.Item1) -or ($castedValue -gt $keywordData.Properties[$key].Range.Item2))) -or ($null -eq $castedValue)) { - Write-Error -ErrorRecord ([Microsoft.PowerShell.DesiredStateConfiguration.Json.DscClassCache]::ValueNotInRangeErrorRecord($key, $keywordName, $value[$key], $keywordData.Properties[$key].Range.Item1, $keywordData.Properties[$key].Range.Item2)) + Write-Error -ErrorRecord ([Microsoft.PowerShell.DesiredStateConfiguration.Internal.Json.DscClassCache]::ValueNotInRangeErrorRecord($key, $keywordName, $value[$key], $keywordData.Properties[$key].Range.Item1, $keywordData.Properties[$key].Range.Item2)) Update-ConfigurationErrorCount } } @@ -3165,7 +3164,7 @@ function Test-DependsOn elseif ($keywordData.Properties[$key].Mandatory) { # If the property was mandatory but the user didn't provide a value, write and error. - Write-Error -ErrorRecord ([Microsoft.PowerShell.DesiredStateConfiguration.Json.DscClassCache]::MissingValueForMandatoryPropertyErrorRecord($keywordData.Keyword, $keywordData.Properties[$key].TypeConstraint, $Key)) + Write-Error -ErrorRecord ([Microsoft.PowerShell.DesiredStateConfiguration.Internal.Json.DscClassCache]::MissingValueForMandatoryPropertyErrorRecord($keywordData.Keyword, $keywordData.Properties[$key].TypeConstraint, $Key)) Update-ConfigurationErrorCount } @@ -3218,7 +3217,7 @@ function Test-DependsOn if($canonicalizedValue['DebugMode'] -and @($canonicalizedValue['DebugMode']).Length -gt 1) { # we only allow one value for debug mode now. - Write-Error -ErrorRecord ([Microsoft.PowerShell.DesiredStateConfiguration.Json.DscClassCache]::DebugModeShouldHaveOneValue()) + Write-Error -ErrorRecord ([Microsoft.PowerShell.DesiredStateConfiguration.Internal.Json.DscClassCache]::DebugModeShouldHaveOneValue()) Update-ConfigurationErrorCount } } diff --git a/src/System.Management.Automation/engine/parser/Parser.cs b/src/System.Management.Automation/engine/parser/Parser.cs index ee5ae91f0e7..e7e7e6d1bc0 100644 --- a/src/System.Management.Automation/engine/parser/Parser.cs +++ b/src/System.Management.Automation/engine/parser/Parser.cs @@ -3061,7 +3061,7 @@ private StatementAst ConfigurationStatementRule(IEnumerable custom if (useJsonSchema) { - Microsoft.PowerShell.DesiredStateConfiguration.Json.DscClassCache.LoadDefaultCimKeywords(CIMKeywordErrors); + Microsoft.PowerShell.DesiredStateConfiguration.Internal.Json.DscClassCache.LoadDefaultCimKeywords(CIMKeywordErrors); } else { @@ -3315,7 +3315,7 @@ private StatementAst ConfigurationStatementRule(IEnumerable custom // if (useJsonSchema) { - Microsoft.PowerShell.DesiredStateConfiguration.Json.DscClassCache.ClearCache(); + Microsoft.PowerShell.DesiredStateConfiguration.Internal.Json.DscClassCache.ClearCache(); } else { From 6499eefc5c6d3a814d4d574e456b615e368da032 Mon Sep 17 00:00:00 2001 From: Andrew Menagarishvili Date: Tue, 1 Sep 2020 17:30:48 -0700 Subject: [PATCH 30/64] Updated default location of BaseRegistrations on Windows --- .../DscSupport/JsonDscClassCache.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/System.Management.Automation/DscSupport/JsonDscClassCache.cs b/src/System.Management.Automation/DscSupport/JsonDscClassCache.cs index 8cee010f693..6c282b74965 100755 --- a/src/System.Management.Automation/DscSupport/JsonDscClassCache.cs +++ b/src/System.Management.Automation/DscSupport/JsonDscClassCache.cs @@ -244,11 +244,12 @@ public static void Initialize(Collection errors, List moduleP { // DSC SxS scenario var configSystemPath = Utils.DefaultPowerShellAppBase; - var systemResourceRoot = Path.Join(configSystemPath, "Configuration"); + var systemResourceRoot = Environment.GetEnvironmentVariable("DSC_HOME"); var inboxModulePath = Path.Join("Modules", "PSDesiredStateConfiguration"); - if (!Directory.Exists(systemResourceRoot)) + if (string.IsNullOrEmpty(systemResourceRoot) || (!Directory.Exists(systemResourceRoot))) { + // if DSC_HOME env var is not set, then use system-wide Windows resource location (i.e. %WINDIR%\System32\Configuration) as backup configSystemPath = Platform.GetFolderPath(Environment.SpecialFolder.System); systemResourceRoot = Path.Join(configSystemPath, "Configuration"); inboxModulePath = windowsInboxDscResourceModulePath; From d72925f17d869cd0d8d12e8655e8464579f4bbbf Mon Sep 17 00:00:00 2001 From: anmenaga Date: Wed, 2 Sep 2020 00:33:20 -0700 Subject: [PATCH 31/64] feedback 6 --- .../DscSupport/JsonCimDSCParser.cs | 8 +-- .../DscSupport/JsonDeserializer.cs | 16 ++--- .../DscSupport/JsonDscClassCache.cs | 60 +++++++++---------- .../engine/parser/Parser.cs | 4 +- 4 files changed, 38 insertions(+), 50 deletions(-) diff --git a/src/System.Management.Automation/DscSupport/JsonCimDSCParser.cs b/src/System.Management.Automation/DscSupport/JsonCimDSCParser.cs index 9cf6472c19b..b50cdcbbb11 100755 --- a/src/System.Management.Automation/DscSupport/JsonCimDSCParser.cs +++ b/src/System.Management.Automation/DscSupport/JsonCimDSCParser.cs @@ -17,11 +17,11 @@ namespace Microsoft.PowerShell.DesiredStateConfiguration.Internal.Json /// internal class CimDSCParser { - private readonly JsonDeserializer _json_deserializer; + private readonly JsonDeserializer _jsonDeserializer; internal CimDSCParser() { - _json_deserializer = JsonDeserializer.Create(); + _jsonDeserializer = JsonDeserializer.Create(); } internal IEnumerable ParseSchemaJson(string filePath, bool useNewRunspace = false) @@ -36,12 +36,12 @@ internal IEnumerable ParseSchemaJson(string filePath, bool useNewRunsp fileNameDefiningClass = fileNameDefiningClass.Substring(0, dotIndex); } - var result = _json_deserializer.DeserializeClasses(json, useNewRunspace); + IEnumerable result = _jsonDeserializer.DeserializeClasses(json, useNewRunspace); foreach (dynamic classObject in result) { string superClassName = classObject.SuperClassName; string className = classObject.ClassName; - if (superClassName?.Equals("OMI_BaseResource", StringComparison.OrdinalIgnoreCase) ?? false) + if (string.Equals(superClassName, "OMI_BaseResource", StringComparison.OrdinalIgnoreCase)) { // Get the name of the file without schema.mof/json extension if (!(className.Equals(fileNameDefiningClass, StringComparison.OrdinalIgnoreCase))) diff --git a/src/System.Management.Automation/DscSupport/JsonDeserializer.cs b/src/System.Management.Automation/DscSupport/JsonDeserializer.cs index d0319aaaa55..f068c43ca6b 100755 --- a/src/System.Management.Automation/DscSupport/JsonDeserializer.cs +++ b/src/System.Management.Automation/DscSupport/JsonDeserializer.cs @@ -35,7 +35,6 @@ public IEnumerable DeserializeClasses(string json, bool useNewRunspace throw new ArgumentNullException(nameof(json)); } - IEnumerable result = null; System.Management.Automation.PowerShell powerShell = null; if (useNewRunspace) @@ -57,18 +56,11 @@ public IEnumerable DeserializeClasses(string json, bool useNewRunspace using (powerShell) { - const string convertFromJson = @"ConvertFrom-Json"; - const string inputObject = @"InputObject"; - const string depth = @"Depth"; - - powerShell.AddCommand(convertFromJson); - powerShell.AddParameter(inputObject, json); - powerShell.AddParameter(depth, 100); // maximum supported by cmdlet - - result = powerShell.Invoke(); + return powerShell.AddCommand("Microsoft.PowerShell.Utility\\ConvertFrom-Json") + .AddParameter("InputObject", json) + .AddParameter("Depth", 100) // maximum supported by cmdlet + .Invoke(); } - - return result; } #endregion Methods diff --git a/src/System.Management.Automation/DscSupport/JsonDscClassCache.cs b/src/System.Management.Automation/DscSupport/JsonDscClassCache.cs index 8cee010f693..6c3531c3641 100755 --- a/src/System.Management.Automation/DscSupport/JsonDscClassCache.cs +++ b/src/System.Management.Automation/DscSupport/JsonDscClassCache.cs @@ -16,6 +16,7 @@ using System.Runtime.InteropServices; using System.Security; using System.Text; +using System.Text.RegularExpressions; using Microsoft.PowerShell.Commands; @@ -26,6 +27,27 @@ namespace Microsoft.PowerShell.DesiredStateConfiguration.Internal.Json /// internal class DscClassCacheEntry { + /// + /// Initializes variables with default values. + /// + public DscClassCacheEntry() + : this(DSCResourceRunAsCredential.Default, isImportedImplicitly: false, cimClassInstance: null) + { + } + + /// + /// Initializes all values. + /// + /// + /// + /// + public DscClassCacheEntry(DSCResourceRunAsCredential dscResourceRunAsCredential, bool isImportedImplicitly, PSObject cimClassInstance) + { + DscResRunAsCred = dscResourceRunAsCredential; + IsImportedImplicitly = isImportedImplicitly; + CimClassInstance = cimClassInstance; + } + /// /// Store the RunAs Credentials that this DSC resource will use. /// @@ -41,24 +63,6 @@ internal class DscClassCacheEntry /// A CimClass instance for this resource. /// public PSObject CimClassInstance { get; set; } - - /// - /// Initializes variables with default values. - /// - public DscClassCacheEntry() : this(DSCResourceRunAsCredential.Default, false, null) { } - - /// - /// Initializes all values. - /// - /// - /// - /// - public DscClassCacheEntry(DSCResourceRunAsCredential aDSCResourceRunAsCredential, bool aIsImportedImplicitly, PSObject aCimClassInstance) - { - DscResRunAsCred = aDSCResourceRunAsCredential; - IsImportedImplicitly = aIsImportedImplicitly; - CimClassInstance = aCimClassInstance; - } } /// @@ -69,9 +73,9 @@ public static class DscClassCache { private const string windowsInboxDscResourceModulePath = "WindowsPowershell\\v1.0\\Modules\\PsDesiredStateConfiguration"; - private const string reservedDynamicKeywords = "^(Synchronization|Certificate|IIS|SQL)$"; + private static readonly Regex reservedDynamicKeywordRegex = new Regex("^(Synchronization|Certificate|IIS|SQL)$", RegexOptions.Compiled | RegexOptions.IgnoreCase); - private const string reservedProperties = "^(Require|Trigger|Notify|Before|After|Subscribe)$"; + private static readonly Regex reservedPropertiesRegex = new Regex("^(Require|Trigger|Notify|Before|After|Subscribe)$", RegexOptions.Compiled | RegexOptions.IgnoreCase); private const string jsonSchemaSupportExperimentalFeatureName = "PSDscJsonSchemaSupport"; @@ -83,17 +87,9 @@ public static class DscClassCache private const int IndexClassName = 2; private const int IndexFriendlyName = 3; - // Create a list of classes which are not actual DSC resources similar to what we do inside PSDesiredStateConfiguration.psm1 - private static readonly string[] s_hiddenResourceList = - { - "MSFT_BaseConfigurationProviderRegistration", - "MSFT_CimConfigurationProviderRegistration", - "MSFT_PSConfigurationProviderRegistration", - }; - // Create a HashSet for fast lookup. According to MSDN, the time complexity of search for an element in a HashSet is O(1) - private static readonly HashSet s_hiddenResourceCache = new HashSet(s_hiddenResourceList, - StringComparer.OrdinalIgnoreCase); + private static readonly HashSet s_hiddenResourceCache = + new HashSet(StringComparer.OrdinalIgnoreCase) { "MSFT_BaseConfigurationProviderRegistration", "MSFT_CimConfigurationProviderRegistration", "MSFT_PSConfigurationProviderRegistration" }; // a collection to hold current importing script based resource file // this prevent circular importing case when the script resource existing in the same module with resources it import-dscresource @@ -907,7 +903,7 @@ private static DynamicKeyword CreateKeywordFromCimClass(string moduleName, Versi }; // If it's one of reserved dynamic keyword, mark it - if (System.Text.RegularExpressions.Regex.Match(keywordString, reservedDynamicKeywords, System.Text.RegularExpressions.RegexOptions.IgnoreCase).Success) + if (reservedDynamicKeywordRegex.Match(keywordString).Success) { keyword.IsReservedKeyword = true; } @@ -954,7 +950,7 @@ private static DynamicKeyword CreateKeywordFromCimClass(string moduleName, Versi } // If it's one of our reserved properties, save it for error reporting - if (System.Text.RegularExpressions.Regex.Match(prop.Name, reservedProperties, System.Text.RegularExpressions.RegexOptions.IgnoreCase).Success) + if (reservedPropertiesRegex.Match(prop.Name).Success) { keyword.HasReservedProperties = true; continue; diff --git a/src/System.Management.Automation/engine/parser/Parser.cs b/src/System.Management.Automation/engine/parser/Parser.cs index e7e7e6d1bc0..a2751a43fe3 100644 --- a/src/System.Management.Automation/engine/parser/Parser.cs +++ b/src/System.Management.Automation/engine/parser/Parser.cs @@ -3017,9 +3017,9 @@ private StatementAst ConfigurationStatementRule(IEnumerable custom bool v3IsLoaded = false; bool prev3IsLoaded = false; - foreach(var moduleInfo in p.Invoke()) + foreach(PSModuleInfo moduleInfo in p.Invoke()) { - if (((Version)moduleInfo.Properties["Version"].Value).Major < 3) + if (moduleInfo.Version.Major < 3) { prev3IsLoaded = true; } From 880f5fecd8eefa416b363e9db06e3feda4e35764 Mon Sep 17 00:00:00 2001 From: anmenaga Date: Tue, 22 Sep 2020 12:23:39 -0700 Subject: [PATCH 32/64] Added GetCachedClass --- .../DscSupport/JsonDscClassCache.cs | 55 ++++++++----------- 1 file changed, 22 insertions(+), 33 deletions(-) diff --git a/src/System.Management.Automation/DscSupport/JsonDscClassCache.cs b/src/System.Management.Automation/DscSupport/JsonDscClassCache.cs index 4f93fca1201..c39a5192cfa 100755 --- a/src/System.Management.Automation/DscSupport/JsonDscClassCache.cs +++ b/src/System.Management.Automation/DscSupport/JsonDscClassCache.cs @@ -441,39 +441,6 @@ private static void WriteWarning(string warning) } } - /// - /// Parses json file without adding it to caches or creating dynamic keywords. - /// - /// Path to json file - /// List of classes from json file - public static IEnumerable ReadClassesFromJson(string jsonFilePath) - { - return ReadClassesFromJson(jsonFilePath, false); - } - - /// - /// Parses json file without adding it to caches or creating dynamic keywords. - /// - /// Path to json file - /// If True PowerShell will use a fresh runspace to do Json deserialization - /// List of classes from json file - public static IEnumerable ReadClassesFromJson(string jsonFilePath, bool useNewRunspace) - { - if (string.IsNullOrEmpty(jsonFilePath)) - { - throw PSTraceSource.NewArgumentNullException(nameof(jsonFilePath)); - } - - if (!jsonFilePath.EndsWith(".json", StringComparison.OrdinalIgnoreCase)) - { - WriteWarning(string.Format("Cannot parse non-JSON file {0}", jsonFilePath)); - return null; - } - - var parser = new CimDSCParser(); - return parser.ParseSchemaJson(jsonFilePath, useNewRunspace); - } - /// /// Import CIM classes from the given file. /// @@ -772,6 +739,28 @@ public static IEnumerable GetCachedClassByFileName(string fileName) return listCimClass; } + /// + /// Returns class declaration from cache. + /// + /// Module name + /// Module version + /// Name of the class + /// Friendly name of the resource + /// Class declaration from cache. + public static PSObject GetCachedClass(string moduleName, string moduleVersion, string className, string resourceName) + { + var moduleQualifiedResourceName = GetModuleQualifiedResourceName(moduleName, moduleVersion, className, string.IsNullOrEmpty(resourceName) ? className : resourceName); + DscClassCacheEntry classCacheEntry = null; + if(ClassCache.TryGetValue(moduleQualifiedResourceName, out classCacheEntry)) + { + return classCacheEntry?.CimClassInstance; + } + else + { + return null; + } + } + /// /// Returns the classes associated with the specified module name. /// Per PowerShell the module name is the base name of the schema file. From c60138626492bc28fed46c1c9bda503a3ef33bbd Mon Sep 17 00:00:00 2001 From: anmenaga Date: Tue, 29 Sep 2020 16:44:13 -0700 Subject: [PATCH 33/64] Updated GetCachedClass --- .../DscSupport/JsonDscClassCache.cs | 24 ++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/src/System.Management.Automation/DscSupport/JsonDscClassCache.cs b/src/System.Management.Automation/DscSupport/JsonDscClassCache.cs index c39a5192cfa..e5d67635613 100755 --- a/src/System.Management.Automation/DscSupport/JsonDscClassCache.cs +++ b/src/System.Management.Automation/DscSupport/JsonDscClassCache.cs @@ -499,6 +499,13 @@ public static IEnumerable ImportClasses(string path, Tuple GetCachedClassByFileName(string fileName) /// Class declaration from cache. public static PSObject GetCachedClass(string moduleName, string moduleVersion, string className, string resourceName) { + if (!ExperimentalFeature.IsEnabled(jsonSchemaSupportExperimentalFeatureName)) + { + throw new InvalidOperationException(ParserStrings.PsDscJsonSchemaSupportDisabled); + } + var moduleQualifiedResourceName = GetModuleQualifiedResourceName(moduleName, moduleVersion, className, string.IsNullOrEmpty(resourceName) ? className : resourceName); DscClassCacheEntry classCacheEntry = null; if(ClassCache.TryGetValue(moduleQualifiedResourceName, out classCacheEntry)) { - return classCacheEntry?.CimClassInstance; + return classCacheEntry.CimClassInstance; } else { + // if class was not found with current ResourceName then it may be a class with non-empty FriendlyName that caller does not know, so perform a broad search + string partialClassPath = string.Join('\\', moduleName, moduleVersion, className, string.Empty); + foreach(string key in ClassCache.Keys) + { + if (key.StartsWith(partialClassPath)) + { + return ClassCache[key].CimClassInstance; + } + } + return null; } } From 8a789daf33d08b2f6c0465f1ccf3b392ab6d5f26 Mon Sep 17 00:00:00 2001 From: anmenaga Date: Tue, 6 Oct 2020 12:10:31 -0700 Subject: [PATCH 34/64] Fixed embedded classes for class-based resources --- .../DscSupport/JsonDscClassCache.cs | 50 +++++++++++++++++-- 1 file changed, 45 insertions(+), 5 deletions(-) diff --git a/src/System.Management.Automation/DscSupport/JsonDscClassCache.cs b/src/System.Management.Automation/DscSupport/JsonDscClassCache.cs index e5d67635613..5292e556653 100755 --- a/src/System.Management.Automation/DscSupport/JsonDscClassCache.cs +++ b/src/System.Management.Automation/DscSupport/JsonDscClassCache.cs @@ -1761,17 +1761,55 @@ public static List ImportClassResourcesFromModule(PSModuleInfo moduleInf return resourcesImported; } - internal static PSObject[] GenerateJsonClassesForAst(TypeDefinitionAst typeAst) + internal static PSObject[] GenerateJsonClassesForAst(TypeDefinitionAst typeAst, PSModuleInfo module, DSCResourceRunAsCredential runAsBehavior) { var embeddedInstanceTypes = new List(); var result = GenerateJsonClassesForAst(typeAst, embeddedInstanceTypes); var visitedInstances = new List(); visitedInstances.Add(typeAst); + var classes = ProcessEmbeddedInstanceTypes(embeddedInstanceTypes, visitedInstances); + AddEmbeddedInstanceTypesToCaches(classes, module, runAsBehavior); return result; } + private static List ProcessEmbeddedInstanceTypes(List embeddedInstanceTypes, List visitedInstances) + { + var result = new List(); + while (embeddedInstanceTypes.Count > 0) + { + var batchedTypes = embeddedInstanceTypes.Where(x => !visitedInstances.Contains(x)).ToArray(); + embeddedInstanceTypes.Clear(); + + for (int i = batchedTypes.Length - 1; i >= 0; i--) + { + visitedInstances.Add(batchedTypes[i]); + var typeAst = batchedTypes[i] as TypeDefinitionAst; + if (typeAst != null) + { + var classes = GenerateJsonClassesForAst(typeAst, embeddedInstanceTypes); + result.AddRange(classes); + } + } + } + + return result; + } + + private static void AddEmbeddedInstanceTypesToCaches(IEnumerable classes, PSModuleInfo module, DSCResourceRunAsCredential runAsBehavior) + { + foreach(dynamic c in classes) + { + var className = c.ClassName; + string alias = GetFriendlyName(c); + var friendlyName = string.IsNullOrEmpty(alias) ? className : alias; + var moduleQualifiedResourceName = GetModuleQualifiedResourceName(module.Name, module.Version.ToString(), className, friendlyName); + ClassCache[moduleQualifiedResourceName] = new DscClassCacheEntry(runAsBehavior, false, c); + ByClassModuleCache[className] = new Tuple(module.Name, module.Version); + } + } + internal static string MapTypeNameToMofType(ITypeName typeName, string memberName, string className, out bool isArrayType, out string embeddedInstanceType, List embeddedInstanceTypes, ref string[] enumNames) { TypeName propTypeName; @@ -1810,10 +1848,8 @@ internal static string MapTypeNameToMofType(ITypeName typeName, string memberNam embeddedInstanceTypes.Add(propTypeName._typeDefinitionAst); } - // The type is obviously not a string, but in the mof, we represent - // it as string (really, embeddedinstance of the class type) embeddedInstanceType = propTypeName.Name.Replace('.', '_'); - return "string"; + return "Instance"; } private static PSObject[] GenerateJsonClassesForAst(TypeDefinitionAst typeAst, List embeddedInstanceTypes) @@ -1994,6 +2030,10 @@ private static List ProcessMembers(List embeddedInstanceTypes, var propertyObject = new PSObject(); propertyObject.Properties.Add(new PSNoteProperty(@"Name", member.Name)); propertyObject.Properties.Add(new PSNoteProperty(@"CimType", mofType + (isArrayType ? "Array" : ""))); + if (!string.IsNullOrEmpty(embeddedInstanceType)) + { + propertyObject.Properties.Add(new PSNoteProperty(@"ReferenceClassName", embeddedInstanceType)); + } PSObject attributesPSObject = null; foreach (var attr in attributes) @@ -2185,7 +2225,7 @@ private static bool ImportKeywordsFromScriptFile(string fileName, PSModuleInfo m } } - var classes = GenerateJsonClassesForAst(resourceDefnAst); + var classes = GenerateJsonClassesForAst(resourceDefnAst, module, runAsBehavior); ProcessJsonForDynamicKeywords(module, resourcesFound, functionsToDefine, classes, runAsBehavior); } From 8e2f174103b071a0ffe2569b4710c8b2b04af548 Mon Sep 17 00:00:00 2001 From: anmenaga Date: Fri, 20 Nov 2020 21:50:23 -0800 Subject: [PATCH 35/64] Fix SInt16Array(CIM)->int64[](.NET) mapping in LanguagePrimitives --- src/System.Management.Automation/engine/LanguagePrimitives.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/System.Management.Automation/engine/LanguagePrimitives.cs b/src/System.Management.Automation/engine/LanguagePrimitives.cs index 5929a5637e9..7d52a7fa8f0 100644 --- a/src/System.Management.Automation/engine/LanguagePrimitives.cs +++ b/src/System.Management.Automation/engine/LanguagePrimitives.cs @@ -1599,8 +1599,8 @@ public static string ConvertTypeNameToPSTypeName(string typeName) { "BooleanArray", "bool[]" }, { "UInt8Array", "byte[]" }, { "SInt8Array", "Sbyte[]" }, - { "UInt16Array", "uint16[]" }, - { "SInt16Array", "int64[]" }, + { "UInt16Array", "UInt16[]" }, + { "SInt16Array", "Int16[]" }, { "UInt32Array", "UInt32[]" }, { "SInt32Array", "Int32[]" }, { "UInt64Array", "UInt64[]" }, From ddb99d4d7f5dad08d6f0efb4f9fcaf994ca346c5 Mon Sep 17 00:00:00 2001 From: anmenaga Date: Sun, 22 Nov 2020 13:00:41 -0800 Subject: [PATCH 36/64] Fixed handling of duplicates for class-based resources --- .../DscSupport/JsonDscClassCache.cs | 40 +++++++++++++++---- 1 file changed, 32 insertions(+), 8 deletions(-) diff --git a/src/System.Management.Automation/DscSupport/JsonDscClassCache.cs b/src/System.Management.Automation/DscSupport/JsonDscClassCache.cs index 5292e556653..063dd2b2d94 100755 --- a/src/System.Management.Automation/DscSupport/JsonDscClassCache.cs +++ b/src/System.Management.Automation/DscSupport/JsonDscClassCache.cs @@ -31,7 +31,7 @@ internal class DscClassCacheEntry /// Initializes variables with default values. /// public DscClassCacheEntry() - : this(DSCResourceRunAsCredential.Default, isImportedImplicitly: false, cimClassInstance: null) + : this(DSCResourceRunAsCredential.Default, isImportedImplicitly: false, cimClassInstance: null, modulePath: string.Empty) { } @@ -41,11 +41,13 @@ public DscClassCacheEntry() /// /// /// - public DscClassCacheEntry(DSCResourceRunAsCredential dscResourceRunAsCredential, bool isImportedImplicitly, PSObject cimClassInstance) + /// + public DscClassCacheEntry(DSCResourceRunAsCredential dscResourceRunAsCredential, bool isImportedImplicitly, PSObject cimClassInstance, string modulePath) { DscResRunAsCred = dscResourceRunAsCredential; IsImportedImplicitly = isImportedImplicitly; CimClassInstance = cimClassInstance; + ModulePath = modulePath; } /// @@ -63,6 +65,11 @@ public DscClassCacheEntry(DSCResourceRunAsCredential dscResourceRunAsCredential, /// A CimClass instance for this resource. /// public PSObject CimClassInstance { get; set; } + + /// + /// Path of the implementing module for this resource. + /// + public string ModulePath { get; set; } } /// @@ -441,6 +448,15 @@ private static void WriteWarning(string warning) } } + private static void WriteError(string error) + { + var executionContext = System.Management.Automation.Runspaces.Runspace.DefaultRunspace.ExecutionContext; + if (executionContext != null && executionContext.InternalHost != null && executionContext.InternalHost.UI != null) + { + executionContext.InternalHost.UI.WriteErrorLine(error); + } + } + /// /// Import CIM classes from the given file. /// @@ -552,7 +568,7 @@ public static IEnumerable ImportClasses(string path, Tuple class string alias = GetFriendlyName(c); var friendlyName = string.IsNullOrEmpty(alias) ? className : alias; var moduleQualifiedResourceName = GetModuleQualifiedResourceName(module.Name, module.Version.ToString(), className, friendlyName); - ClassCache[moduleQualifiedResourceName] = new DscClassCacheEntry(runAsBehavior, false, c); + ClassCache[moduleQualifiedResourceName] = new DscClassCacheEntry(runAsBehavior, false, c, module.Path); ByClassModuleCache[className] = new Tuple(module.Name, module.Version); } } @@ -2393,10 +2409,18 @@ private static void ProcessJsonForDynamicKeywords(PSModuleInfo module, ICollecti } var moduleQualifiedResourceName = GetModuleQualifiedResourceName(module.Name, module.Version.ToString(), className, friendlyName); - ClassCache[moduleQualifiedResourceName] = new DscClassCacheEntry(runAsBehavior, false, c); - ByClassModuleCache[className] = new Tuple(module.Name, module.Version); - resourcesFound.Add(className); - CreateAndRegisterKeywordFromCimClass(module.Name, module.Version, c, functionsToDefine, runAsBehavior); + DscClassCacheEntry existingCacheEntry = null; + if (ClassCache.TryGetValue(moduleQualifiedResourceName, out existingCacheEntry)) + { + WriteError(string.Format(ParserStrings.DuplicateCimClassDefinition, className, module.Path, existingCacheEntry.ModulePath)); + } + else + { + ClassCache[moduleQualifiedResourceName] = new DscClassCacheEntry(runAsBehavior, false, c, module.Path); + ByClassModuleCache[className] = new Tuple(module.Name, module.Version); + resourcesFound.Add(className); + CreateAndRegisterKeywordFromCimClass(module.Name, module.Version, c, functionsToDefine, runAsBehavior); + } } } From 2b1e288e7d150aff22357e58105b209a560631a2 Mon Sep 17 00:00:00 2001 From: anmenaga Date: Sun, 22 Nov 2020 14:03:56 -0800 Subject: [PATCH 37/64] Updated error reporting for class resources --- .../DscSupport/JsonDscClassCache.cs | 47 ++++++++++--------- 1 file changed, 26 insertions(+), 21 deletions(-) diff --git a/src/System.Management.Automation/DscSupport/JsonDscClassCache.cs b/src/System.Management.Automation/DscSupport/JsonDscClassCache.cs index 063dd2b2d94..9cb60d3f968 100755 --- a/src/System.Management.Automation/DscSupport/JsonDscClassCache.cs +++ b/src/System.Management.Automation/DscSupport/JsonDscClassCache.cs @@ -448,15 +448,6 @@ private static void WriteWarning(string warning) } } - private static void WriteError(string error) - { - var executionContext = System.Management.Automation.Runspaces.Runspace.DefaultRunspace.ExecutionContext; - if (executionContext != null && executionContext.InternalHost != null && executionContext.InternalHost.UI != null) - { - executionContext.InternalHost.UI.WriteErrorLine(error); - } - } - /// /// Import CIM classes from the given file. /// @@ -1607,7 +1598,14 @@ public static void LoadResourcesFromModule(IScriptExtent scriptExtent, var dscResourcesPath = Path.Join(moduleInfo.ModuleBase, "DscResources"); var resourcesFound = new List(); - LoadPowerShellClassResourcesFromModule(moduleInfo, moduleInfo, resourcesToImport, resourcesFound, errorList, null, true, scriptExtent); + var exceptionList = new System.Collections.ObjectModel.Collection(); + LoadPowerShellClassResourcesFromModule(moduleInfo, moduleInfo, resourcesToImport, resourcesFound, exceptionList, null, true, scriptExtent); + foreach(Exception ex in exceptionList) + { + errorList.Add(new ParseError(scriptExtent, + "ClassResourcesLoadingFailed", + ex.Message)); + } if (Directory.Exists(dscResourcesPath)) { @@ -1720,7 +1718,7 @@ public static void LoadResourcesFromModule(IScriptExtent scriptExtent, } private static void LoadPowerShellClassResourcesFromModule(PSModuleInfo primaryModuleInfo, PSModuleInfo moduleInfo, ICollection resourcesToImport, ICollection resourcesFound, - List errorList, + Collection errorList, Dictionary functionsToDefine = null, bool recurse = true, IScriptExtent extent = null) @@ -1764,8 +1762,9 @@ private static void LoadPowerShellClassResourcesFromModule(PSModuleInfo primaryM /// /// /// + /// /// The list of resources imported from this module. - public static List ImportClassResourcesFromModule(PSModuleInfo moduleInfo, ICollection resourcesToImport, Dictionary functionsToDefine) + public static List ImportClassResourcesFromModule(PSModuleInfo moduleInfo, ICollection resourcesToImport, Dictionary functionsToDefine, Collection errors) { if (!ExperimentalFeature.IsEnabled(jsonSchemaSupportExperimentalFeatureName)) { @@ -1773,7 +1772,7 @@ public static List ImportClassResourcesFromModule(PSModuleInfo moduleInf } var resourcesImported = new List(); - LoadPowerShellClassResourcesFromModule(moduleInfo, moduleInfo, resourcesToImport, resourcesImported, null, functionsToDefine); + LoadPowerShellClassResourcesFromModule(moduleInfo, moduleInfo, resourcesToImport, resourcesImported, errors, functionsToDefine); return resourcesImported; } @@ -2113,7 +2112,7 @@ private static List ProcessMembers(List embeddedInstanceTypes, /// /// /// - private static bool GetResourceDefinitionsFromModule(string fileName, out IEnumerable resourceDefinitions, List errorList, IScriptExtent extent) + private static bool GetResourceDefinitionsFromModule(string fileName, out IEnumerable resourceDefinitions, Collection errorList, IScriptExtent extent) { resourceDefinitions = null; @@ -2150,9 +2149,10 @@ private static bool GetResourceDefinitionsFromModule(string fileName, out IEnume { errorMessages.Add(error.ToString()); } - - errorList.Add(new ParseError(extent, "FailToParseModuleScriptFile", - string.Format(CultureInfo.CurrentCulture, ParserStrings.FailToParseModuleScriptFile, fileName, string.Join(Environment.NewLine, errorMessages)))); + + PSInvalidOperationException e = PSTraceSource.NewInvalidOperationException(ParserStrings.FailToParseModuleScriptFile, fileName, string.Join(Environment.NewLine, errorMessages)); + e.SetErrorId("FailToParseModuleScriptFile"); + errorList.Add(e); } return false; @@ -2186,7 +2186,7 @@ private static bool GetResourceDefinitionsFromModule(string fileName, out IEnume /// /// /// - private static bool ImportKeywordsFromScriptFile(string fileName, PSModuleInfo module, ICollection resourcesToImport, ICollection resourcesFound, Dictionary functionsToDefine, List errorList, IScriptExtent extent) + private static bool ImportKeywordsFromScriptFile(string fileName, PSModuleInfo module, ICollection resourcesToImport, ICollection resourcesFound, Dictionary functionsToDefine, Collection errorList, IScriptExtent extent) { IEnumerable resourceDefinitions; if (!GetResourceDefinitionsFromModule(fileName, out resourceDefinitions, errorList, extent)) @@ -2243,7 +2243,7 @@ private static bool ImportKeywordsFromScriptFile(string fileName, PSModuleInfo m var classes = GenerateJsonClassesForAst(resourceDefnAst, module, runAsBehavior); - ProcessJsonForDynamicKeywords(module, resourcesFound, functionsToDefine, classes, runAsBehavior); + ProcessJsonForDynamicKeywords(module, resourcesFound, functionsToDefine, classes, runAsBehavior, errorList); } return result; @@ -2384,7 +2384,7 @@ internal static string MapTypeToMofType(Type type, string memberName, string cla } private static void ProcessJsonForDynamicKeywords(PSModuleInfo module, ICollection resourcesFound, - Dictionary functionsToDefine, PSObject[] classes, DSCResourceRunAsCredential runAsBehavior) + Dictionary functionsToDefine, PSObject[] classes, DSCResourceRunAsCredential runAsBehavior, Collection errors) { foreach (dynamic c in classes) { @@ -2412,7 +2412,12 @@ private static void ProcessJsonForDynamicKeywords(PSModuleInfo module, ICollecti DscClassCacheEntry existingCacheEntry = null; if (ClassCache.TryGetValue(moduleQualifiedResourceName, out existingCacheEntry)) { - WriteError(string.Format(ParserStrings.DuplicateCimClassDefinition, className, module.Path, existingCacheEntry.ModulePath)); + if (errors != null) + { + PSInvalidOperationException e = PSTraceSource.NewInvalidOperationException(ParserStrings.DuplicateCimClassDefinition, className, module.Path, existingCacheEntry.ModulePath); + e.SetErrorId("DuplicateCimClassDefinition"); + errors.Add(e); + } } else { From 97b3da918819b9bc98da41e52270c29cdf030d2d Mon Sep 17 00:00:00 2001 From: anmenaga Date: Sun, 22 Nov 2020 14:08:32 -0800 Subject: [PATCH 38/64] Removed unused GetResourceMethodsLinePosition --- .../DscSupport/JsonDscClassCache.cs | 55 ------------------- 1 file changed, 55 deletions(-) diff --git a/src/System.Management.Automation/DscSupport/JsonDscClassCache.cs b/src/System.Management.Automation/DscSupport/JsonDscClassCache.cs index 9cb60d3f968..94888ad4dd1 100755 --- a/src/System.Management.Automation/DscSupport/JsonDscClassCache.cs +++ b/src/System.Management.Automation/DscSupport/JsonDscClassCache.cs @@ -1939,61 +1939,6 @@ private static bool GetResourceMethodsLineNumber(TypeDefinitionAst typeDefinitio return (methodsLinePosition.Count == 3); } - /// - /// Gets the line no for DSC Class Resource Get/Set/Test methods. - /// - /// - /// - /// - /// - public static bool GetResourceMethodsLinePosition(PSModuleInfo moduleInfo, string resourceName, out Dictionary resourceMethodsLinePosition, out string resourceFilePath) - { - resourceMethodsLinePosition = null; - resourceFilePath = string.Empty; - if (moduleInfo == null || string.IsNullOrEmpty(resourceName)) - { - return false; - } - - IEnumerable resourceDefinitions; - List moduleFiles = new List(); - if (moduleInfo.RootModule != null) - { - moduleFiles.Add(moduleInfo.Path); - } - - if (moduleInfo.NestedModules != null) - { - foreach (var nestedModule in moduleInfo.NestedModules.Where(m => !string.IsNullOrEmpty(m.Path))) - { - moduleFiles.Add(nestedModule.Path); - } - } - - foreach (string moduleFile in moduleFiles) - { - if (GetResourceDefinitionsFromModule(moduleFile, out resourceDefinitions, null, null)) - { - foreach (var r in resourceDefinitions) - { - var resourceDefnAst = (TypeDefinitionAst)r; - if (!resourceName.Equals(resourceDefnAst.Name, StringComparison.OrdinalIgnoreCase)) - { - continue; - } - - if (GetResourceMethodsLineNumber(resourceDefnAst, out resourceMethodsLinePosition)) - { - resourceFilePath = moduleFile; - return true; - } - } - } - } - - return false; - } - private static List ProcessMembers(List embeddedInstanceTypes, TypeDefinitionAst typeDefinitionAst, string className) { List result = new List(); From 623dab4bcca85adb9c487b0f4f0d11ac80d6abd5 Mon Sep 17 00:00:00 2001 From: anmenaga Date: Sun, 22 Nov 2020 14:17:18 -0800 Subject: [PATCH 39/64] Renamed LoadResourcesFromModule --- .../DscSupport/JsonDscClassCache.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/System.Management.Automation/DscSupport/JsonDscClassCache.cs b/src/System.Management.Automation/DscSupport/JsonDscClassCache.cs index 94888ad4dd1..321a742fed5 100755 --- a/src/System.Management.Automation/DscSupport/JsonDscClassCache.cs +++ b/src/System.Management.Automation/DscSupport/JsonDscClassCache.cs @@ -1398,7 +1398,7 @@ private static ParseError[] ImportResourcePostParse(DynamicKeywordStatementAst k if (errorList.Count == 0) { // No errors, try to load the resources - LoadResourcesFromModule(kwAst.Extent, moduleSpecifications, resourceNames, errorList); + LoadResourcesFromModuleInImportResourcePostParse(kwAst.Extent, moduleSpecifications, resourceNames, errorList); } return errorList.ToArray(); @@ -1513,7 +1513,7 @@ private static ParseError[] CheckMandatoryPropertiesPresent(DynamicKeywordStatem /// Module information, can be null. /// Name of the resource to be loaded from module. /// List of errors reported by the method. - public static void LoadResourcesFromModule(IScriptExtent scriptExtent, + internal static void LoadResourcesFromModuleInImportResourcePostParse(IScriptExtent scriptExtent, ModuleSpecification[] moduleSpecifications, string[] resourceNames, List errorList) From 26b9ddf589031ca914b51e8fcbfc3d32716ce02b Mon Sep 17 00:00:00 2001 From: anmenaga Date: Sun, 22 Nov 2020 15:13:09 -0800 Subject: [PATCH 40/64] Fixed new code style violations that were breaking build --- .../DscSupport/JsonDscClassCache.cs | 7 +++---- .../DscSupport/MofCimDSCParser.cs | 2 ++ 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/System.Management.Automation/DscSupport/JsonDscClassCache.cs b/src/System.Management.Automation/DscSupport/JsonDscClassCache.cs index 321a742fed5..d786a7021e9 100755 --- a/src/System.Management.Automation/DscSupport/JsonDscClassCache.cs +++ b/src/System.Management.Automation/DscSupport/JsonDscClassCache.cs @@ -438,7 +438,6 @@ private static Tuple GetModuleInfoHelper(string moduleFolderPat return null; } - private static void WriteWarning(string warning) { var executionContext = System.Management.Automation.Runspaces.Runspace.DefaultRunspace.ExecutionContext; @@ -2840,7 +2839,7 @@ public static string GetDSCResourceUsageString(DynamicKeyword keyword) } } - usageString.Append("}"); + usageString.Append('}'); return usageString.ToString(); } @@ -2886,10 +2885,10 @@ private static StringBuilder FormatCimPropertyType(DynamicKeywordProperty prop, // We prepend optional property with "[" so close out it here. This way it is shown with [ ] to indication optional if (isOptionalProperty) { - formattedTypeString.Append("]"); + formattedTypeString.Append(']'); } - formattedTypeString.Append("\n"); + formattedTypeString.Append('\n'); return formattedTypeString; } diff --git a/src/System.Management.Automation/DscSupport/MofCimDSCParser.cs b/src/System.Management.Automation/DscSupport/MofCimDSCParser.cs index 8177ec46033..055f5f07d46 100755 --- a/src/System.Management.Automation/DscSupport/MofCimDSCParser.cs +++ b/src/System.Management.Automation/DscSupport/MofCimDSCParser.cs @@ -321,7 +321,9 @@ public override object Transform(EngineIntrinsics engineIntrinsics, object input internal class CimDSCParser { private CimMofDeserializer _deserializer; + private CimMofDeserializer.OnClassNeeded _onClassNeeded; + /// /// internal CimDSCParser(CimMofDeserializer.OnClassNeeded onClassNeeded) From 2eaf084612cff997e77866eb86d2c534271b3731 Mon Sep 17 00:00:00 2001 From: anmenaga Date: Mon, 23 Nov 2020 19:24:17 -0800 Subject: [PATCH 41/64] Removed module import in configuration parsing --- .../DscSupport/JsonDscClassCache.cs | 228 +++--------------- .../engine/parser/Parser.cs | 37 +-- 2 files changed, 35 insertions(+), 230 deletions(-) diff --git a/src/System.Management.Automation/DscSupport/JsonDscClassCache.cs b/src/System.Management.Automation/DscSupport/JsonDscClassCache.cs index d786a7021e9..088211a4441 100755 --- a/src/System.Management.Automation/DscSupport/JsonDscClassCache.cs +++ b/src/System.Management.Automation/DscSupport/JsonDscClassCache.cs @@ -204,158 +204,54 @@ public static void Initialize(Collection errors, List moduleP { s_tracer.WriteLine("Initializing DSC class cache"); - if (Platform.IsLinux || Platform.IsMacOS) + // Load the base schema files. + ClearCache(); + var dscConfigurationDirectory = Environment.GetEnvironmentVariable("DSC_HOME"); + if (string.IsNullOrEmpty(dscConfigurationDirectory)) { - // Load the base schema files. - ClearCache(); - var dscConfigurationDirectory = Environment.GetEnvironmentVariable("DSC_HOME"); - if (string.IsNullOrEmpty(dscConfigurationDirectory)) - { - // if DSC_HOME env var is not set, then use location of system-wide PS module directory (i.e. /usr/local/share/powershell/Modules) as backup - dscConfigurationDirectory = Path.Join(ModuleIntrinsics.GetSharedModulePath(), "PSDesiredStateConfiguration", "Configuration"); - } + var moduleInfos = ModuleCmdletBase.GetModuleIfAvailable(new Microsoft.PowerShell.Commands.ModuleSpecification() + { + Name = "PSDesiredStateConfiguration", + Version = new Version(3,0,0) + }); - if (!Directory.Exists(dscConfigurationDirectory)) + if (moduleInfos.Count > 0) { - throw new DirectoryNotFoundException(string.Format(ParserStrings.PsDscMissingSchemaStore, dscConfigurationDirectory)); + var moduleDirectory = Path.GetDirectoryName(moduleInfos[0].Path); + dscConfigurationDirectory = Path.Join(moduleDirectory, "Configuration"); } - - var resourceBaseFile = Path.Join(dscConfigurationDirectory, "BaseRegistration", "BaseResource.schema.json"); - ImportClasses(resourceBaseFile, s_defaultModuleInfoForResource, errors); - var metaConfigFile = Path.Join(dscConfigurationDirectory, "BaseRegistration", "MSFT_DSCMetaConfiguration.json"); - ImportClasses(metaConfigFile, s_defaultModuleInfoForResource, errors); - - var allResourceRoots = new string[] { dscConfigurationDirectory }; - - // Load all of the system resource schema files, searching - string resources; - foreach (var resourceRoot in allResourceRoots) + else { - resources = Path.Join(resourceRoot, "schema"); - if (!Directory.Exists(resources)) - { - continue; - } - - foreach (var schemaFile in Directory.EnumerateFiles(resources, "*.schema.json", SearchOption.AllDirectories)) - { - ImportClasses(schemaFile, s_defaultModuleInfoForResource, errors); - } + // when all else has failed use location of system-wide PS module directory (i.e. /usr/local/share/powershell/Modules) as backup + dscConfigurationDirectory = Path.Join(ModuleIntrinsics.GetSharedModulePath(), "PSDesiredStateConfiguration", "Configuration"); } } - else - { - // DSC SxS scenario - var configSystemPath = Utils.DefaultPowerShellAppBase; - var systemResourceRoot = Environment.GetEnvironmentVariable("DSC_HOME"); - var inboxModulePath = Path.Join("Modules", "PSDesiredStateConfiguration"); - - if (string.IsNullOrEmpty(systemResourceRoot) || (!Directory.Exists(systemResourceRoot))) - { - // if DSC_HOME env var is not set, then use system-wide Windows resource location (i.e. %WINDIR%\System32\Configuration) as backup - configSystemPath = Platform.GetFolderPath(Environment.SpecialFolder.System); - systemResourceRoot = Path.Join(configSystemPath, "Configuration"); - inboxModulePath = windowsInboxDscResourceModulePath; - } - var programFilesDirectory = Platform.GetFolderPath(Environment.SpecialFolder.ProgramFiles); - var customResourceRoot = Path.Join(programFilesDirectory, "WindowsPowerShell", "Configuration"); - var allResourceRoots = new string[] { systemResourceRoot, customResourceRoot }; - - // Load the base schema files. - ClearCache(); - var resourceBaseFile = Path.Join(systemResourceRoot, "BaseRegistration", "BaseResource.schema.json"); - ImportClasses(resourceBaseFile, s_defaultModuleInfoForResource, errors); + if (!Directory.Exists(dscConfigurationDirectory)) + { + throw new DirectoryNotFoundException(string.Format(ParserStrings.PsDscMissingSchemaStore, dscConfigurationDirectory)); + } - var metaConfigFile = Path.Join(systemResourceRoot, "BaseRegistration", "MSFT_DSCMetaConfiguration.json"); - ImportClasses(metaConfigFile, s_defaultModuleInfoForResource, errors); + var resourceBaseFile = Path.Join(dscConfigurationDirectory, "BaseRegistration", "BaseResource.schema.json"); + ImportClasses(resourceBaseFile, s_defaultModuleInfoForResource, errors); + var metaConfigFile = Path.Join(dscConfigurationDirectory, "BaseRegistration", "MSFT_DSCMetaConfiguration.json"); + ImportClasses(metaConfigFile, s_defaultModuleInfoForResource, errors); - var metaConfigExtensionFile = Path.Join(systemResourceRoot, "BaseRegistration", "MSFT_MetaConfigurationExtensionClasses.schema.json"); - ImportClasses(metaConfigExtensionFile, DefaultModuleInfoForMetaConfigResource, errors); + var allResourceRoots = new string[] { dscConfigurationDirectory }; - // Load all of the system resource schema files, searching - string resources; - foreach (var resourceRoot in allResourceRoots) + // Load all of the system resource schema files, searching + string resources; + foreach (var resourceRoot in allResourceRoots) + { + resources = Path.Join(resourceRoot, "schema"); + if (!Directory.Exists(resources)) { - resources = Path.Join(resourceRoot, "Schema"); - if (!Directory.Exists(resources)) - { - continue; - } - - foreach (var schemaFile in Directory.EnumerateFiles(resources, "*.schema.json", SearchOption.AllDirectories)) - { - ImportClasses(schemaFile, s_defaultModuleInfoForResource, errors); - } + continue; } - // Load Regular and DSC PS modules - bool importInBoxResourcesImplicitly = false; - List modulePaths = new List(); - if (modulePathList?.Count == 0) + foreach (var schemaFile in Directory.EnumerateFiles(resources, "*.schema.json", SearchOption.AllDirectories)) { - modulePaths.Add(Path.Join(configSystemPath, inboxModulePath)); - importInBoxResourcesImplicitly = true; - } - else - { - if (modulePathList != null) - { - foreach (string moduleFolderPath in modulePathList) - { - if (!Directory.Exists(moduleFolderPath)) - { - continue; - } - - foreach (string moduleDir in Directory.EnumerateDirectories(moduleFolderPath)) - { - modulePaths.Add(moduleDir); - } - } - } - } - - LoadDSCResourceIntoCache(errors, modulePaths, importInBoxResourcesImplicitly); - } - } - - /// - /// Load DSC resources into Cache from moduleFolderPath. - /// - /// Collection of any errors encountered during initialization. - /// Module path from where DSC PS modules will be loaded. - /// - /// if module is inbox. - /// - private static void LoadDSCResourceIntoCache(Collection errors, List modulePathList, bool importInBoxResourcesImplicitly) - { - foreach (string moduleDir in modulePathList) - { - if (!Directory.Exists(moduleDir)) continue; - - var dscResourcesPath = Path.Join(moduleDir, "DscResources"); - if (Directory.Exists(dscResourcesPath)) - { - foreach (string resourceDir in Directory.EnumerateDirectories(dscResourcesPath)) - { - IEnumerable schemaFiles = Directory.EnumerateFiles(resourceDir, "*.schema.json"); - if (!schemaFiles.Any()) - { - continue; - } - - Tuple moduleInfo = GetModuleInfoHelper(moduleDir, importInBoxResourcesImplicitly, isPsProviderModule: false); - if (moduleInfo == null) - { - continue; - } - - foreach (string schemaFile in schemaFiles) - { - ImportClasses(schemaFile, moduleInfo, errors, importInBoxResourcesImplicitly); - } - } + ImportClasses(schemaFile, s_defaultModuleInfoForResource, errors); } } } @@ -789,28 +685,6 @@ public static PSObject GetCachedClass(string moduleName, string moduleVersion, s } } - /// - /// Returns the classes associated with the specified module name. - /// Per PowerShell the module name is the base name of the schema file. - /// - /// - /// - public static IEnumerable GetCachedClassByModuleName(string moduleName) - { - if (!ExperimentalFeature.IsEnabled(jsonSchemaSupportExperimentalFeatureName)) - { - throw new InvalidOperationException(ParserStrings.PsDscJsonSchemaSupportDisabled); - } - - if (string.IsNullOrWhiteSpace(moduleName)) - { - throw PSTraceSource.NewArgumentNullException(nameof(moduleName)); - } - - var moduleFileName = moduleName + ".schema.json"; - return (from filename in ByFileClassCache.Keys where string.Equals(Path.GetFileName(filename), moduleFileName, StringComparison.OrdinalIgnoreCase) select GetCachedClassByFileName(filename)).FirstOrDefault(); - } - private static bool IsMagicProperty(string propertyName) { return System.Text.RegularExpressions.Regex.Match(propertyName, @@ -1902,42 +1776,6 @@ private static PSObject[] GenerateJsonClassesForAst(TypeDefinitionAst typeAst, L return new [] {result}; } - /// - /// Gets the line no for DSC Class Resource Get/Set/Test methods. - /// - /// - /// - private static bool GetResourceMethodsLineNumber(TypeDefinitionAst typeDefinitionAst, out Dictionary methodsLinePosition) - { - const string getMethodName = "Get"; - const string setMethodName = "Set"; - const string testMethodName = "Test"; - - methodsLinePosition = new Dictionary(); - foreach (var member in typeDefinitionAst.Members) - { - var functionMemberAst = member as FunctionMemberAst; - if (functionMemberAst != null) - { - if (functionMemberAst.Name.Equals(getMethodName, StringComparison.OrdinalIgnoreCase)) - { - methodsLinePosition[getMethodName] = functionMemberAst.NameExtent.StartLineNumber; - } - else if (functionMemberAst.Name.Equals(setMethodName, StringComparison.OrdinalIgnoreCase)) - { - methodsLinePosition[setMethodName] = functionMemberAst.NameExtent.StartLineNumber; - } - else if (functionMemberAst.Name.Equals(testMethodName, StringComparison.OrdinalIgnoreCase)) - { - methodsLinePosition[testMethodName] = functionMemberAst.NameExtent.StartLineNumber; - } - } - } - - // All 3 methods (Get/Set/Test) position should be found. - return (methodsLinePosition.Count == 3); - } - private static List ProcessMembers(List embeddedInstanceTypes, TypeDefinitionAst typeDefinitionAst, string className) { List result = new List(); diff --git a/src/System.Management.Automation/engine/parser/Parser.cs b/src/System.Management.Automation/engine/parser/Parser.cs index d535df231a3..fe167e1d667 100644 --- a/src/System.Management.Automation/engine/parser/Parser.cs +++ b/src/System.Management.Automation/engine/parser/Parser.cs @@ -3006,14 +3006,11 @@ private StatementAst ConfigurationStatementRule(IEnumerable custom // First check if PSDesiredStateConfiguration is already loaded // if pre-v3 is already loaded then use old mof-based APIs - // otherwise if v3.0.0 or later is already loaded then use new json-based APIS - // otherwise no version of the module is currently loaded - then try to load v3 and use json-based APIs is loading is successful - // if v3 can not be found/loaded then use old mof-based APIs + // otherwise use json-based APIs p.AddCommand(new CmdletInfo("Get-Module", typeof(Microsoft.PowerShell.Commands.GetModuleCommand))); p.AddParameter("Name", "PSDesiredStateConfiguration"); - bool v3IsLoaded = false; bool prev3IsLoaded = false; foreach(PSModuleInfo moduleInfo in p.Invoke()) { @@ -3021,41 +3018,11 @@ private StatementAst ConfigurationStatementRule(IEnumerable custom { prev3IsLoaded = true; } - else - { - v3IsLoaded = true; - } } p.Commands.Clear(); - if (!prev3IsLoaded) - { - // if v3 is already loaded we don't need to do anything extra - just use json APIs - // if it is not loaded - try to load it - if (v3IsLoaded) - { - useJsonSchema = true; - } - else - { - p.AddCommand(new CmdletInfo("Import-Module", typeof(Microsoft.PowerShell.Commands.ImportModuleCommand))); - p.AddParameter("PassThru", true); - p.AddParameter("FullyQualifiedName", new Microsoft.PowerShell.Commands.ModuleSpecification() - { - Name = "PSDesiredStateConfiguration", - Version = new Version(3,0,0) - }); - - var newModuleInfo = p.Invoke(); - p.Commands.Clear(); - if (newModuleInfo.Count > 0) - { - // v3 of the module was found/loaded successfully - use new json APIs - useJsonSchema = true; - } - } - } + useJsonSchema = !prev3IsLoaded; if (useJsonSchema) { From 795a8079d527fda606b14932e3b1c55716207f01 Mon Sep 17 00:00:00 2001 From: anmenaga Date: Mon, 7 Dec 2020 20:22:25 -0800 Subject: [PATCH 42/64] Class-based resource only support --- .../DscSupport/JsonDscClassCache.cs | 729 +++--------------- .../CommandCompletion/CompletionAnalysis.cs | 10 +- .../engine/parser/Parser.cs | 7 +- 3 files changed, 105 insertions(+), 641 deletions(-) diff --git a/src/System.Management.Automation/DscSupport/JsonDscClassCache.cs b/src/System.Management.Automation/DscSupport/JsonDscClassCache.cs index 088211a4441..21ebecc2530 100755 --- a/src/System.Management.Automation/DscSupport/JsonDscClassCache.cs +++ b/src/System.Management.Automation/DscSupport/JsonDscClassCache.cs @@ -78,8 +78,6 @@ public DscClassCacheEntry(DSCResourceRunAsCredential dscResourceRunAsCredential, Justification = "Needed Internal use only")] public static class DscClassCache { - private const string windowsInboxDscResourceModulePath = "WindowsPowershell\\v1.0\\Modules\\PsDesiredStateConfiguration"; - private static readonly Regex reservedDynamicKeywordRegex = new Regex("^(Synchronization|Certificate|IIS|SQL)$", RegexOptions.Compiled | RegexOptions.IgnoreCase); private static readonly Regex reservedPropertiesRegex = new Regex("^(Require|Trigger|Notify|Before|After|Subscribe)$", RegexOptions.Compiled | RegexOptions.IgnoreCase); @@ -98,10 +96,6 @@ public static class DscClassCache private static readonly HashSet s_hiddenResourceCache = new HashSet(StringComparer.OrdinalIgnoreCase) { "MSFT_BaseConfigurationProviderRegistration", "MSFT_CimConfigurationProviderRegistration", "MSFT_PSConfigurationProviderRegistration" }; - // a collection to hold current importing script based resource file - // this prevent circular importing case when the script resource existing in the same module with resources it import-dscresource - private static readonly HashSet s_currentImportingScriptFiles = new HashSet(StringComparer.OrdinalIgnoreCase); - /// /// DSC class cache for this runspace. /// Cache stores the DSCRunAsBehavior, cim class and boolean to indicate if an Inbox resource has been implicitly imported. @@ -123,47 +117,38 @@ private static Dictionary ClassCache private static Dictionary t_classCache; /// - /// DSC classname to source module mapper. + /// DSC class cache for GuestConfig. + /// It is similar to ClassCache, but maintains values between operations. /// - private static Dictionary> ByClassModuleCache - => t_byClassModuleCache ??= new Dictionary>(StringComparer.OrdinalIgnoreCase); - - [ThreadStatic] - private static Dictionary> t_byClassModuleCache; + private static Dictionary GuestConfigClassCache + { + get + { + if (t_guestConfigClassCache == null) + { + t_guestConfigClassCache = new Dictionary(StringComparer.OrdinalIgnoreCase); + } - /// - /// DSC filename to defined class mapper. - /// - private static Dictionary> ByFileClassCache - => t_byFileClassCache ??= new Dictionary>(StringComparer.OrdinalIgnoreCase); + return t_guestConfigClassCache; + } + } [ThreadStatic] - private static Dictionary> t_byFileClassCache; + private static Dictionary t_guestConfigClassCache; /// - /// Filenames from which we have imported script dynamic keywords. + /// DSC classname to source module mapper. /// - private static HashSet ScriptKeywordFileCache - => t_scriptKeywordFileCache ??= new HashSet(StringComparer.OrdinalIgnoreCase); + private static Dictionary> ByClassModuleCache + => t_byClassModuleCache ??= new Dictionary>(StringComparer.OrdinalIgnoreCase); [ThreadStatic] - private static HashSet t_scriptKeywordFileCache; + private static Dictionary> t_byClassModuleCache; /// /// Default ModuleName and ModuleVersion to use. /// - private static readonly Tuple s_defaultModuleInfoForResource = new Tuple("PSDesiredStateConfiguration", new Version(1, 1)); - - /// - /// Default ModuleName and ModuleVersion to use for meta configuration resources. - /// - internal static readonly Tuple DefaultModuleInfoForMetaConfigResource = new Tuple("PSDesiredStateConfigurationEngine", new Version(2, 0)); - - /// - /// A set of dynamic keywords that can be used in both configuration and meta configuration. - /// - internal static readonly HashSet SystemResourceNames = - new HashSet(StringComparer.OrdinalIgnoreCase) { "Node", "OMI_ConfigurationDocument" }; + private static readonly Tuple s_defaultModuleInfoForResource = new Tuple("PSDesiredStateConfiguration", new Version(3, 0)); /// /// When this property is set to true, DSC Cache will cache multiple versions of a resource. @@ -187,6 +172,9 @@ private static bool CacheResourcesFromMultipleModuleVersions } } + [ThreadStatic] + internal static bool NewApiIsUsed = false; + /// /// Initialize the class cache with the default classes in $ENV:SystemDirectory\Configuration. /// @@ -196,7 +184,7 @@ public static void Initialize() } /// - /// Initialize the class cache with the default classes in $ENV:SystemDirectory\Configuration. + /// Initialize the class cache with default classes that come with PSDesiredStateConfiguration module. /// /// Collection of any errors encountered during initialization. /// List of module path from where DSC PS modules will be loaded. @@ -212,11 +200,13 @@ public static void Initialize(Collection errors, List moduleP var moduleInfos = ModuleCmdletBase.GetModuleIfAvailable(new Microsoft.PowerShell.Commands.ModuleSpecification() { Name = "PSDesiredStateConfiguration", + // Version in the next line is actually MinimumVersion Version = new Version(3,0,0) }); if (moduleInfos.Count > 0) - { + { + // to be consistent with Import-Module behavior, we use the fist occurrence that we find in PSModulePath var moduleDirectory = Path.GetDirectoryName(moduleInfos[0].Path); dscConfigurationDirectory = Path.Join(moduleDirectory, "Configuration"); } @@ -233,149 +223,26 @@ public static void Initialize(Collection errors, List moduleP } var resourceBaseFile = Path.Join(dscConfigurationDirectory, "BaseRegistration", "BaseResource.schema.json"); - ImportClasses(resourceBaseFile, s_defaultModuleInfoForResource, errors); + ImportBaseClasses(resourceBaseFile, s_defaultModuleInfoForResource, errors, false); var metaConfigFile = Path.Join(dscConfigurationDirectory, "BaseRegistration", "MSFT_DSCMetaConfiguration.json"); - ImportClasses(metaConfigFile, s_defaultModuleInfoForResource, errors); - - var allResourceRoots = new string[] { dscConfigurationDirectory }; - - // Load all of the system resource schema files, searching - string resources; - foreach (var resourceRoot in allResourceRoots) - { - resources = Path.Join(resourceRoot, "schema"); - if (!Directory.Exists(resources)) - { - continue; - } - - foreach (var schemaFile in Directory.EnumerateFiles(resources, "*.schema.json", SearchOption.AllDirectories)) - { - ImportClasses(schemaFile, s_defaultModuleInfoForResource, errors); - } - } + ImportBaseClasses(metaConfigFile, s_defaultModuleInfoForResource, errors, false); } /// - /// Get the module name and module version. - /// - /// - /// Path to the module folder - /// - /// - /// if module is inbox and we are importing resources implicitly - /// - /// - /// Indicate a internal DSC module - /// - /// - private static Tuple GetModuleInfoHelper(string moduleFolderPath, bool importInBoxResourcesImplicitly, bool isPsProviderModule) - { - string moduleName = "PsDesiredStateConfiguration"; - if (!importInBoxResourcesImplicitly) - { - moduleName = Path.GetFileName(moduleFolderPath); - } - - string manifestPath = Path.Join(moduleFolderPath, moduleName + ".psd1"); - s_tracer.WriteLine("DSC GetModuleVersion: Try retrieving module version information from file: {0}.", manifestPath); - - if (!File.Exists(manifestPath)) - { - if (isPsProviderModule) - { - // Some internal PSProviders do not come with a .psd1 file, such - // as MSFT_LogResource. We don't report error in this case. - return new Tuple(moduleName, new Version("1.0")); - } - else - { - s_tracer.WriteLine("DSC GetModuleVersion: Manifest file '{0}' not exist.", manifestPath); - return null; - } - } - - try - { - Hashtable dataFileSetting = - PsUtils.GetModuleManifestProperties( - manifestPath, - PsUtils.ManifestModuleVersionPropertyName); - - object versionValue = dataFileSetting["ModuleVersion"]; - if (versionValue is not null) - { - Version moduleVersion; - if (LanguagePrimitives.TryConvertTo(versionValue, out moduleVersion)) - { - return new Tuple(moduleName, moduleVersion); - } - else - { - s_tracer.WriteLine( - "DSC GetModuleVersion: ModuleVersion value '{0}' cannot be converted to System.Version. Skip the module '{1}'.", - versionValue, moduleName); - } - } - else - { - s_tracer.WriteLine( - "DSC GetModuleVersion: Manifest file '{0}' does not contain ModuleVersion. Skip the module '{1}'.", - manifestPath, moduleName); - } - } - catch (PSInvalidOperationException ex) - { - s_tracer.WriteLine( - "DSC GetModuleVersion: Error evaluating module manifest file '{0}', with error '{1}'. Skip the module '{2}'.", - manifestPath, ex, moduleName); - } - - return null; - } - - private static void WriteWarning(string warning) - { - var executionContext = System.Management.Automation.Runspaces.Runspace.DefaultRunspace.ExecutionContext; - if (executionContext != null && executionContext.InternalHost != null && executionContext.InternalHost.UI != null) - { - executionContext.InternalHost.UI.WriteWarningLine(warning); - } - } - - /// - /// Import CIM classes from the given file. - /// - /// Path to schema file - /// Module information - /// Error collection that will be shown to the user - /// Class objects from schema file - public static IEnumerable ImportClasses(string path, Tuple moduleInfo, Collection errors) - { - return ImportClasses(path, moduleInfo, errors, false); - } - - /// - /// Import CIM classes from the given file. + /// Import base classes from the given file. /// /// Path to schema file /// Module information /// Error collection that will be shown to the user /// Flag for implicitly imported resource /// Class objects from schema file - public static IEnumerable ImportClasses(string path, Tuple moduleInfo, Collection errors, bool importInBoxResourcesImplicitly) + public static IEnumerable ImportBaseClasses(string path, Tuple moduleInfo, Collection errors, bool importInBoxResourcesImplicitly) { if (string.IsNullOrEmpty(path)) { throw PSTraceSource.NewArgumentNullException(nameof(path)); } - if (!path.EndsWith(".json", StringComparison.OrdinalIgnoreCase)) - { - WriteWarning(string.Format("Cannot parse non-JSON file {0}", path)); - return null; - } - s_tracer.WriteLine("DSC ClassCache: importing file: {0}", path); var parser = new CimDSCParser(); @@ -399,12 +266,11 @@ public static IEnumerable ImportClasses(string path, Tuple ImportClasses(string path, Tuple> resourceList = FindResourceInCache(moduleInfo.Item1, className, friendlyName); - if (resourceList.Count > 0 && !string.IsNullOrEmpty(resourceList[0].Key)) - { - ClassCache.Remove(resourceList[0].Key); - - // keyword is already defined and it is a Inbox resource, remove it - if (DynamicKeyword.ContainsKeyword(friendlyName) && resourceList[0].Value.IsImportedImplicitly) - { - DynamicKeyword.RemoveKeyword(friendlyName); - } - } + continue; } - ClassCache[moduleQualifiedResourceName] = new DscClassCacheEntry(DSCResourceRunAsCredential.Default, importInBoxResourcesImplicitly, c, path); + var classCacheEntry = new DscClassCacheEntry(DSCResourceRunAsCredential.NotSupported, importInBoxResourcesImplicitly, c, path); + ClassCache[moduleQualifiedResourceName] = classCacheEntry; + GuestConfigClassCache[moduleQualifiedResourceName] = classCacheEntry; ByClassModuleCache[className] = moduleInfo; } @@ -472,7 +318,6 @@ public static IEnumerable ImportClasses(string path, Tuple> FindResourceInCach } /// - /// Returns cached classes - /// - /// Returns cached classes - private static List GetCachedClasses() - { - return ClassCache.Values.ToList(); - } - - /// - /// Find cached cim classes defined under specified module. - /// - /// - /// List of cached cim classes. - public static List GetCachedClassesForModule(PSModuleInfo module) - { - if (!ExperimentalFeature.IsEnabled(jsonSchemaSupportExperimentalFeatureName)) - { - throw new InvalidOperationException(ParserStrings.PsDscJsonSchemaSupportDisabled); - } - - List cachedClasses = new List(); - var moduleQualifiedName = string.Format(CultureInfo.InvariantCulture, "{0}\\{1}", module.Name, module.Version.ToString()); - foreach (var dscClassCacheEntry in ClassCache) - { - if (dscClassCacheEntry.Key.StartsWith(moduleQualifiedName, StringComparison.OrdinalIgnoreCase)) - { - cachedClasses.Add(dscClassCacheEntry.Value.CimClassInstance); - } - } - - return cachedClasses; - } - - /// - /// Get the file that defined this class. - /// - /// - /// - public static List GetFileDefiningClass(string className) - { - if (!ExperimentalFeature.IsEnabled(jsonSchemaSupportExperimentalFeatureName)) - { - throw new InvalidOperationException(ParserStrings.PsDscJsonSchemaSupportDisabled); - } - - List files = new List(); - foreach (var pair in ByFileClassCache) - { - var file = pair.Key; - var classList = pair.Value; - if (classList != null) - { - foreach(dynamic c in classList) - { - if (string.Equals(c.ClassName, className, StringComparison.OrdinalIgnoreCase)) - { - files.Add(file); - } - } - } - } - - return files; - } - - /// - /// Get a list of files from which classes have been loaded. - /// - /// - public static string[] GetLoadedFiles() - { - if (!ExperimentalFeature.IsEnabled(jsonSchemaSupportExperimentalFeatureName)) - { - throw new InvalidOperationException(ParserStrings.PsDscJsonSchemaSupportDisabled); - } - - return ByFileClassCache.Keys.ToArray(); - } - - /// - /// Returns the classes that we loaded from the specified file name. - /// - /// - /// - public static IEnumerable GetCachedClassByFileName(string fileName) - { - if (!ExperimentalFeature.IsEnabled(jsonSchemaSupportExperimentalFeatureName)) - { - throw new InvalidOperationException(ParserStrings.PsDscJsonSchemaSupportDisabled); - } - - if (string.IsNullOrWhiteSpace(fileName)) - { - throw PSTraceSource.NewArgumentNullException(nameof(fileName)); - } - - IEnumerable listCimClass; - ByFileClassCache.TryGetValue(fileName, out listCimClass); - return listCimClass; - } - - /// - /// Returns class declaration from cache. + /// Returns class declaration from GuestConfigClassCache. /// /// Module name /// Module version /// Name of the class /// Friendly name of the resource /// Class declaration from cache. - public static PSObject GetCachedClass(string moduleName, string moduleVersion, string className, string resourceName) + public static PSObject GetGuestConfigCachedClass(string moduleName, string moduleVersion, string className, string resourceName) { if (!ExperimentalFeature.IsEnabled(jsonSchemaSupportExperimentalFeatureName)) { @@ -665,7 +406,7 @@ public static PSObject GetCachedClass(string moduleName, string moduleVersion, s var moduleQualifiedResourceName = GetModuleQualifiedResourceName(moduleName, moduleVersion, className, string.IsNullOrEmpty(resourceName) ? className : resourceName); DscClassCacheEntry classCacheEntry = null; - if(ClassCache.TryGetValue(moduleQualifiedResourceName, out classCacheEntry)) + if(GuestConfigClassCache.TryGetValue(moduleQualifiedResourceName, out classCacheEntry)) { return classCacheEntry.CimClassInstance; } @@ -673,11 +414,11 @@ public static PSObject GetCachedClass(string moduleName, string moduleVersion, s { // if class was not found with current ResourceName then it may be a class with non-empty FriendlyName that caller does not know, so perform a broad search string partialClassPath = string.Join('\\', moduleName, moduleVersion, className, string.Empty); - foreach(string key in ClassCache.Keys) + foreach(string key in GuestConfigClassCache.Keys) { if (key.StartsWith(partialClassPath)) { - return ClassCache[key].CimClassInstance; + return GuestConfigClassCache[key].CimClassInstance; } } @@ -685,6 +426,14 @@ public static PSObject GetCachedClass(string moduleName, string moduleVersion, s } } + /// + /// Clears GuestConfigClassCache. + /// + public static void ClearGuestConfigClassCache() + { + GuestConfigClassCache.Clear(); + } + private static bool IsMagicProperty(string propertyName) { return System.Text.RegularExpressions.Regex.Match(propertyName, @@ -700,7 +449,7 @@ private static string GetFriendlyName(dynamic cimClass) /// /// Method to get the cached classes in the form of DynamicKeyword. /// - public static Collection GetCachedKeywords() + public static Collection GetKeywordsFromCachedClasses() { if (!ExperimentalFeature.IsEnabled(jsonSchemaSupportExperimentalFeatureName)) { @@ -806,7 +555,7 @@ private static DynamicKeyword CreateKeywordFromCimClass(string moduleName, Versi // previous version of this code was the only place that referenced CimSuperClass // so to simplify things we just check superclass to be OMI_BaseResource // with assumption that current code will not work for multi-level class inheritance (which is never used in practice according to DSC team) - // this simplification allows us to avoid linking objects together using CimSuperClass field during deserialization from json schema + // this simplification allows us to avoid linking objects together using CimSuperClass field during deserialization if ((!string.IsNullOrEmpty(cimClass.SuperClassName)) && string.Equals("OMI_BaseResource", cimClass.SuperClassName, StringComparison.OrdinalIgnoreCase)) { isResourceType = true; @@ -988,23 +737,6 @@ private static void UpdateKnownRestriction(DynamicKeyword keyword) } } - /// - /// Load the default system CIM classes and create the corresponding keywords. - /// - public static void LoadDefaultCimKeywords() - { - LoadDefaultCimKeywords(functionsToDefine: null, errors: null, modulePathList: null, cacheResourcesFromMultipleModuleVersions: false); - } - - /// - /// Load the default system CIM classes and create the corresponding keywords. - /// - /// List of module path from where DSC PS modules will be loaded. - public static void LoadDefaultCimKeywords(List modulePathList) - { - LoadDefaultCimKeywords(functionsToDefine: null, errors: null, modulePathList, cacheResourcesFromMultipleModuleVersions: false); - } - /// /// Load the default system CIM classes and create the corresponding keywords. /// @@ -1049,7 +781,8 @@ private static void LoadDefaultCimKeywords(Dictionary funct errors.Add(exception); return; } - + + NewApiIsUsed = true; DynamicKeyword.Reset(); Initialize(errors, modulePathList); @@ -1059,7 +792,7 @@ private static void LoadDefaultCimKeywords(Dictionary funct // of the module, so it is ok if this property is not set during cache initialization. CacheResourcesFromMultipleModuleVersions = cacheResourcesFromMultipleModuleVersions; - foreach (dynamic cimClass in GetCachedClasses()) + foreach (dynamic cimClass in ClassCache.Values) { var className = cimClass.CimClassInstance.ClassName; var moduleInfo = ByClassModuleCache[className]; @@ -1468,8 +1201,6 @@ internal static void LoadResourcesFromModuleInImportResourcePostParse(IScriptExt foreach (var moduleInfo in modules) { - var dscResourcesPath = Path.Join(moduleInfo.ModuleBase, "DscResources"); - var resourcesFound = new List(); var exceptionList = new System.Collections.ObjectModel.Collection(); LoadPowerShellClassResourcesFromModule(moduleInfo, moduleInfo, resourcesToImport, resourcesFound, exceptionList, null, true, scriptExtent); @@ -1480,91 +1211,6 @@ internal static void LoadResourcesFromModuleInImportResourcePostParse(IScriptExt ex.Message)); } - if (Directory.Exists(dscResourcesPath)) - { - foreach (var resourceToImport in resourcesToImport) - { - bool foundResources = false; - foreach (var resourceDir in Directory.EnumerateDirectories(dscResourcesPath, resourceToImport)) - { - var resourceName = Path.GetFileName(resourceDir); - - bool foundCimSchema = false; - bool foundScriptSchema = false; - string schemaMofFilePath = string.Empty; - - try - { - foundCimSchema = ImportCimKeywordsFromModule(moduleInfo, resourceName, out schemaMofFilePath); - } - catch (FileNotFoundException) - { - errorList.Add(new ParseError(scriptExtent, - "SchemaFileNotFound", - string.Format(CultureInfo.CurrentCulture, ParserStrings.SchemaFileNotFound, schemaMofFilePath))); - } - catch (PSInvalidOperationException e) - { - errorList.Add(new ParseError(scriptExtent, - e.ErrorRecord.FullyQualifiedErrorId, - e.Message)); - } - catch (Exception e) - { - errorList.Add(new ParseError(scriptExtent, - "ExceptionParsingMOFFile", - string.Format(CultureInfo.CurrentCulture, ParserStrings.ExceptionParsingMOFFile, schemaMofFilePath, e.Message))); - } - - var schemaScriptFilePath = string.Empty; - - try - { - foundScriptSchema = ImportScriptKeywordsFromModule(moduleInfo, resourceName, out schemaScriptFilePath); - } - catch (FileNotFoundException) - { - errorList.Add(new ParseError(scriptExtent, - "SchemaFileNotFound", - string.Format(CultureInfo.CurrentCulture, ParserStrings.SchemaFileNotFound, schemaScriptFilePath))); - } - catch (Exception e) - { - // This shouldn't happen so just report the error as is - errorList.Add(new ParseError(scriptExtent, - "UnexpectedParseError", - string.Format(CultureInfo.CurrentCulture, e.ToString()))); - } - - if (foundCimSchema || foundScriptSchema) - { - foundResources = true; - } - } - - // - // resourceToImport may be the friendly name of the DSC resource - // - if (!foundResources) - { - try - { - string unused; - foundResources = ImportCimKeywordsFromModule(moduleInfo, resourceToImport, out unused); - } - catch (Exception) - { - } - } - - // resource name without wildcard (*) should be imported only once - if (!resourceToImport.Contains("*") && foundResources) - { - resourcesFound.Add(resourceToImport); - } - } - } - foreach (var resource in resourcesFound) { resourcesToImport.Remove(resource); @@ -1618,7 +1264,7 @@ private static void LoadPowerShellClassResourcesFromModule(PSModuleInfo primaryM scriptPath = moduleInfo.Path; } - ImportKeywordsFromScriptFile(scriptPath, primaryModuleInfo, resourcesToImport, resourcesFound, functionsToDefine, errorList, extent); + LoadPowerShellClassResourcesFromModule(scriptPath, primaryModuleInfo, resourcesToImport, resourcesFound, functionsToDefine, errorList, extent); } if (moduleInfo.NestedModules != null && recurse) @@ -1693,7 +1339,9 @@ private static void AddEmbeddedInstanceTypesToCaches(IEnumerable class string alias = GetFriendlyName(c); var friendlyName = string.IsNullOrEmpty(alias) ? className : alias; var moduleQualifiedResourceName = GetModuleQualifiedResourceName(module.Name, module.Version.ToString(), className, friendlyName); - ClassCache[moduleQualifiedResourceName] = new DscClassCacheEntry(runAsBehavior, false, c, module.Path); + var classCacheEntry = new DscClassCacheEntry(runAsBehavior, false, c, module.Path); + ClassCache[moduleQualifiedResourceName] = classCacheEntry; + GuestConfigClassCache[moduleQualifiedResourceName] = classCacheEntry; ByClassModuleCache[className] = new Tuple(module.Name, module.Version); } } @@ -1743,7 +1391,7 @@ internal static string MapTypeNameToMofType(ITypeName typeName, string memberNam private static PSObject[] GenerateJsonClassesForAst(TypeDefinitionAst typeAst, List embeddedInstanceTypes) { // MOF-based implementation of this used to generate MOF string representing classes/typeAst and pass it to MMI/MOF deserializer to get CimClass array - // Here we are avoiding that roundtrip just constructing the resulting PSObjects + // Here we are avoiding that roundtrip by constructing the resulting PSObjects directly var className = typeAst.Name; @@ -1755,24 +1403,44 @@ private static PSObject[] GenerateJsonClassesForAst(TypeDefinitionAst typeAst, L var _CimClassProperties = ProcessMembers(embeddedInstanceTypes, typeAst, className).ToArray(); - var result = new PSObject(); - result.Properties.Add(new PSNoteProperty("ClassName", className)); - result.Properties.Add(new PSNoteProperty("ClassVersion", "1.0.0")); - result.Properties.Add(new PSNoteProperty("FriendlyName", className)); - result.Properties.Add(new PSNoteProperty("SuperClassName", cimSuperClassName)); - result.Properties.Add(new PSNoteProperty("ClassProperties", _CimClassProperties)); - Queue bases = new Queue(); foreach (var b in typeAst.BaseTypes) { bases.Enqueue(b); } - if (bases.Count > 0) + while (bases.Count > 0) { - WriteWarning(string.Format("BaseTypes count for type {0} is {1} and not implemented yet", className, bases.Count)); + var b = bases.Dequeue(); + var tc = b as TypeConstraintAst; + + if (tc != null) + { + b = tc.TypeName.GetReflectionType(); + if (b == null) + { + var td = tc.TypeName as TypeName; + if (td != null && td._typeDefinitionAst != null) + { + ProcessMembers(embeddedInstanceTypes, td._typeDefinitionAst, className); + foreach (var b1 in td._typeDefinitionAst.BaseTypes) + { + bases.Enqueue(b1); + } + } + + continue; + } + } } + var result = new PSObject(); + result.Properties.Add(new PSNoteProperty("ClassName", className)); + result.Properties.Add(new PSNoteProperty("ClassVersion", "1.0.0")); + result.Properties.Add(new PSNoteProperty("FriendlyName", className)); + result.Properties.Add(new PSNoteProperty("SuperClassName", cimSuperClassName)); + result.Properties.Add(new PSNoteProperty("ClassProperties", _CimClassProperties)); + return new [] {result}; } @@ -1807,7 +1475,6 @@ private static List ProcessMembers(List embeddedInstanceTypes, if (memberType != null) { - // TODO - validate type and name mofType = MapTypeToMofType(memberType, member.Name, className, out isArrayType, out embeddedInstanceType, embeddedInstanceTypes); @@ -1909,15 +1576,6 @@ private static bool GetResourceDefinitionsFromModule(string fileName, out IEnume return false; } - // If script dynamic keywords has already been loaded from the file, don't load them again. - // The ScriptKeywordFile cache is always initialized from scratch by the top-level - // configuration statement so within a single compile, things shouldn't change. - if (!File.Exists(fileName) || ScriptKeywordFileCache.Contains(fileName)) - { - return false; - } - - // BUGBUG - need to fix up how the module gets set. Token[] tokens; ParseError[] errors; var ast = Parser.ParseFile(fileName, out tokens, out errors); @@ -1968,7 +1626,7 @@ private static bool GetResourceDefinitionsFromModule(string fileName, out IEnume /// /// /// - private static bool ImportKeywordsFromScriptFile(string fileName, PSModuleInfo module, ICollection resourcesToImport, ICollection resourcesFound, Dictionary functionsToDefine, Collection errorList, IScriptExtent extent) + private static bool LoadPowerShellClassResourcesFromModule(string fileName, PSModuleInfo module, ICollection resourcesToImport, ICollection resourcesFound, Dictionary functionsToDefine, Collection errorList, IScriptExtent extent) { IEnumerable resourceDefinitions; if (!GetResourceDefinitionsFromModule(fileName, out resourceDefinitions, errorList, extent)) @@ -2050,18 +1708,6 @@ private static bool ImportKeywordsFromScriptFile(string fileName, PSModuleInfo m { typeof(char), "char16" }, }; - private static bool IsSameNestedObject(dynamic oldClass, dynamic newClass) - { - // #1 both the classes should be nested class and not DSC resource - if ((oldClass.SuperClassName != null && string.Equals("OMI_BaseResource", oldClass.SuperClassName, StringComparison.OrdinalIgnoreCase)) || - (newClass.SuperClassName != null && string.Equals("OMI_BaseResource", newClass.SuperClassName, StringComparison.OrdinalIgnoreCase))) - { - return false; - } - - return true; - } - internal static string MapTypeToMofType(Type type, string memberName, string className, out bool isArrayType, out string embeddedInstanceType, List embeddedInstanceTypes) { isArrayType = false; @@ -2203,7 +1849,9 @@ private static void ProcessJsonForDynamicKeywords(PSModuleInfo module, ICollecti } else { - ClassCache[moduleQualifiedResourceName] = new DscClassCacheEntry(runAsBehavior, false, c, module.Path); + var classCacheEntry = new DscClassCacheEntry(runAsBehavior, false, c, module.Path); + ClassCache[moduleQualifiedResourceName] = classCacheEntry; + GuestConfigClassCache[moduleQualifiedResourceName] = classCacheEntry; ByClassModuleCache[className] = new Tuple(module.Name, module.Version); resourcesFound.Add(className); CreateAndRegisterKeywordFromCimClass(module.Name, module.Version, c, functionsToDefine, runAsBehavior); @@ -2211,193 +1859,6 @@ private static void ProcessJsonForDynamicKeywords(PSModuleInfo module, ICollecti } } - /// - /// Import the CIM keywords from a module... - /// - /// - /// - /// Full path of the loaded schema file... - /// - public static bool ImportCimKeywordsFromModule(PSModuleInfo module, string resourceName, out string schemaFilePath) - { - return ImportCimKeywordsFromModule(module, resourceName, out schemaFilePath, null); - } - - /// - /// Import the CIM functions from a module... - /// - /// - /// - /// Full path of the loaded schema file... - /// - /// - public static bool ImportCimKeywordsFromModule(PSModuleInfo module, string resourceName, out string schemaFilePath, Dictionary functionsToDefine) - { - return ImportCimKeywordsFromModule(module, resourceName, out schemaFilePath, functionsToDefine, null); - } - - /// - /// Import the CIM functions from a module... - /// - /// - /// - /// Full path of the loaded schema file... - /// - /// Error reported during deserialization. - /// - public static bool ImportCimKeywordsFromModule(PSModuleInfo module, string resourceName, out string schemaFilePath, Dictionary functionsToDefine, Collection errors) - { - if (!ExperimentalFeature.IsEnabled(jsonSchemaSupportExperimentalFeatureName)) - { - throw new InvalidOperationException(ParserStrings.PsDscJsonSchemaSupportDisabled); - } - - if (module == null) - { - throw PSTraceSource.NewArgumentNullException(nameof(module)); - } - - if (resourceName == null) - { - throw PSTraceSource.NewArgumentNullException(nameof(resourceName)); - } - - string dscResourcesPath = Path.Join(module.ModuleBase, "DscResources"); - schemaFilePath = Path.Join(dscResourcesPath, resourceName, resourceName + ".schema.json"); - - if (File.Exists(schemaFilePath)) - { - // If the file has already been loaded, don't load it again. - // The class cache is always initialized from scratch by the top-level - // configuration statement so within a single compile, things shouldn't - // change. - var classes = GetCachedClassByFileName(schemaFilePath) ?? ImportClasses(schemaFilePath, new Tuple(module.Name, module.Version), errors); - if (classes != null) - { - foreach (var c in classes) - { - CreateAndRegisterKeywordFromCimClass(module.Name, module.Version, c, functionsToDefine, DSCResourceRunAsCredential.Default); - ClearImplicitlyImportedFlagFromResourceInClassCache(module, c); - } - } - - return true; - } - else if (Directory.Exists(dscResourcesPath)) - { - // Cannot find the schema file, then resourceName may be a friendly name, - // try to search all DscResources' schemas under DscResources folder - try - { - var dscResourceDirectories = Directory.GetDirectories(dscResourcesPath); - foreach (var directory in dscResourceDirectories) - { - var schemaFiles = Directory.GetFiles(directory, "*.schema.json", SearchOption.TopDirectoryOnly); - if (schemaFiles.Length > 0) - { - Debug.Assert(schemaFiles.Length == 1, "A valid DSCResource module can have only one schema mof file"); - var tempSchemaFilepath = schemaFiles[0]; - var classes = GetCachedClassByFileName(tempSchemaFilepath) ?? ImportClasses(tempSchemaFilepath, new Tuple(module.Name, module.Version), errors); - if (classes != null) - { - // search if class's friendly name is the given resourceName - foreach (var c in classes) - { - var alias = GetFriendlyName(c); - if (string.Equals(alias, resourceName, StringComparison.OrdinalIgnoreCase)) - { - CreateAndRegisterKeywordFromCimClass(module.Name, module.Version, c, functionsToDefine, DSCResourceRunAsCredential.Default); - ClearImplicitlyImportedFlagFromResourceInClassCache(module, c); - return true; - } - } - } - } - } - } - catch (Exception) - { - // silent in case of exception - } - } - - return false; - } - - /// - /// Clear the 'IsImportedImplicitly' flag when explicitly importing a resource. - /// - /// - /// - private static void ClearImplicitlyImportedFlagFromResourceInClassCache(PSModuleInfo module, dynamic cimClass) - { - var className = cimClass.ClassName; - var alias = GetFriendlyName(cimClass); - var friendlyName = string.IsNullOrEmpty(alias) ? className : alias; - var moduleQualifiedResourceName = GetModuleQualifiedResourceName(module.Name, module.Version.ToString(), className, friendlyName); - ClassCache[moduleQualifiedResourceName].IsImportedImplicitly = false; - } - - /// - /// Imports configuration keywords from a .psm1 file. - /// - /// - /// - /// - public static bool ImportScriptKeywordsFromModule(PSModuleInfo module, string resourceName, out string schemaFilePath) - { - return ImportScriptKeywordsFromModule(module, resourceName, out schemaFilePath, null); - } - - /// - /// Imports configuration keywords from a .psm1 file. - /// - /// - /// - /// - /// - public static bool ImportScriptKeywordsFromModule(PSModuleInfo module, string resourceName, out string schemaFilePath, Dictionary functionsToDefine) - { - if (!ExperimentalFeature.IsEnabled(jsonSchemaSupportExperimentalFeatureName)) - { - throw new InvalidOperationException(ParserStrings.PsDscJsonSchemaSupportDisabled); - } - - if (module == null) - { - throw PSTraceSource.NewArgumentNullException(nameof(module)); - } - - if (resourceName == null) - { - throw PSTraceSource.NewArgumentNullException(nameof(resourceName)); - } - - schemaFilePath = Path.Join(module.ModuleBase, "DscResources", resourceName, resourceName + ".Schema.psm1"); - - if (File.Exists(schemaFilePath) && !s_currentImportingScriptFiles.Contains(schemaFilePath)) - { - // If script dynamic keywords has already been loaded from the file, don't load them again. - // The ScriptKeywordFile cache is always initialized from scratch by the top-level - // configuration statement so within a single compile, things shouldn't change. - if (!ScriptKeywordFileCache.Contains(schemaFilePath)) - { - // Parsing the file is all that needs to be done to add the keywords - // BUGBUG - need to fix up how the module gets set. - // BUGBUG - should fail somehow if errors is not empty - Token[] tokens; ParseError[] errors; - s_currentImportingScriptFiles.Add(schemaFilePath); - Parser.ParseFile(schemaFilePath, out tokens, out errors); - s_currentImportingScriptFiles.Remove(schemaFilePath); - ScriptKeywordFileCache.Add(schemaFilePath); - } - - return true; - } - - return false; - } - /// /// Returns an error record to use in the case of a malformed resource reference in the DependsOn list. /// diff --git a/src/System.Management.Automation/engine/CommandCompletion/CompletionAnalysis.cs b/src/System.Management.Automation/engine/CommandCompletion/CompletionAnalysis.cs index 46e2f5b518f..92ca8e25eb4 100644 --- a/src/System.Management.Automation/engine/CommandCompletion/CompletionAnalysis.cs +++ b/src/System.Management.Automation/engine/CommandCompletion/CompletionAnalysis.cs @@ -1721,7 +1721,15 @@ private List GetResultForIdentifierInConfiguration( foreach (var keyword in matchedResults) { - string usageString = Microsoft.PowerShell.DesiredStateConfiguration.Internal.DscClassCache.GetDSCResourceUsageString(keyword); + string usageString = string.Empty; + if (Microsoft.PowerShell.DesiredStateConfiguration.Internal.Json.DscClassCache.NewApiIsUsed) + { + Microsoft.PowerShell.DesiredStateConfiguration.Internal.Json.DscClassCache.GetDSCResourceUsageString(keyword); + } + else + { + Microsoft.PowerShell.DesiredStateConfiguration.Internal.DscClassCache.GetDSCResourceUsageString(keyword); + } if (results == null) { results = new List(); diff --git a/src/System.Management.Automation/engine/parser/Parser.cs b/src/System.Management.Automation/engine/parser/Parser.cs index fe167e1d667..7146bb1d0ca 100644 --- a/src/System.Management.Automation/engine/parser/Parser.cs +++ b/src/System.Management.Automation/engine/parser/Parser.cs @@ -2999,12 +2999,7 @@ private StatementAst ConfigurationStatementRule(IEnumerable custom if (ExperimentalFeature.IsEnabled("PSDscJsonSchemaSupport")) { // In addition to checking if experimental feature is enabled - // also check if v3.0 (or later) of PSDesiredStateConfiguration module is available - - // pre-v3 module is mof-based - // having a pre-v3 module pre-loaded gives user a way to force usage of mof-based APIs for dsc configuration compilation - - // First check if PSDesiredStateConfiguration is already loaded + // also check if PSDesiredStateConfiguration is already loaded // if pre-v3 is already loaded then use old mof-based APIs // otherwise use json-based APIs From 39f9f05688aca703bcf84a0235a9777f3bce6d01 Mon Sep 17 00:00:00 2001 From: anmenaga Date: Tue, 5 Jan 2021 14:34:45 -0800 Subject: [PATCH 43/64] Reverted splitting old code into separate files --- .../{MofDscClassCache.cs => CimDSCParser.cs} | 812 ++++++++++++++---- .../DscSupport/MofCimDSCParser.cs | 480 ----------- 2 files changed, 634 insertions(+), 658 deletions(-) rename src/System.Management.Automation/DscSupport/{MofDscClassCache.cs => CimDSCParser.cs} (82%) mode change 100755 => 100644 delete mode 100755 src/System.Management.Automation/DscSupport/MofCimDSCParser.cs diff --git a/src/System.Management.Automation/DscSupport/MofDscClassCache.cs b/src/System.Management.Automation/DscSupport/CimDSCParser.cs old mode 100755 new mode 100644 similarity index 82% rename from src/System.Management.Automation/DscSupport/MofDscClassCache.cs rename to src/System.Management.Automation/DscSupport/CimDSCParser.cs index 385807f7bf9..71091cfaaf9 --- a/src/System.Management.Automation/DscSupport/MofDscClassCache.cs +++ b/src/System.Management.Automation/DscSupport/CimDSCParser.cs @@ -22,6 +22,464 @@ using Microsoft.Management.Infrastructure.Serialization; using Microsoft.PowerShell.Commands; +namespace Microsoft.PowerShell.DesiredStateConfiguration.Internal +{ + /// + /// + [SuppressMessage("Microsoft.MSInternal", "CA903:InternalNamespaceShouldNotContainPublicTypes", + Justification = "Needed Internal use only")] + public static class DscRemoteOperationsClass + { + /// + /// Convert Cim Instance representing Resource desired state to Powershell Class Object. + /// + public static object ConvertCimInstanceToObject(Type targetType, CimInstance instance, string moduleName) + { + var className = instance.CimClass.CimSystemProperties.ClassName; + object targetObject = null; + string errorMessage; + + using (System.Management.Automation.PowerShell powerShell = System.Management.Automation.PowerShell.Create(RunspaceMode.CurrentRunspace)) + { + const string script = "param($targetType,$moduleName) & (Microsoft.PowerShell.Core\\Get-Module $moduleName) { New-Object $targetType } "; + + powerShell.AddScript(script); + powerShell.AddArgument(targetType); + powerShell.AddArgument(moduleName); + + Collection psExecutionResult = powerShell.Invoke(); + if (psExecutionResult.Count == 1) + { + targetObject = psExecutionResult[0].BaseObject; + } + else + { + Exception innerException = null; + if (powerShell.Streams.Error != null && powerShell.Streams.Error.Count > 0) + { + innerException = powerShell.Streams.Error[0].Exception; + } + + errorMessage = string.Format(CultureInfo.CurrentCulture, ParserStrings.InstantiatePSClassObjectFailed, className); + var invalidOperationException = new InvalidOperationException(errorMessage, innerException); + throw invalidOperationException; + } + } + + foreach (var property in instance.CimInstanceProperties) + { + if (property.Value != null) + { + MemberInfo[] memberInfo = targetType.GetMember(property.Name, BindingFlags.Public | BindingFlags.Instance); + + // verify property exists in corresponding class type + if (memberInfo == null + || memberInfo.Length > 1 + || (memberInfo[0] is not PropertyInfo && memberInfo[0] is not FieldInfo)) + { + errorMessage = string.Format(CultureInfo.CurrentCulture, ParserStrings.PropertyNotDeclaredInPSClass, new object[] { property.Name, className }); + var invalidOperationException = new InvalidOperationException(errorMessage); + throw invalidOperationException; + } + + var member = memberInfo[0]; + var memberType = (member is FieldInfo) + ? ((FieldInfo)member).FieldType + : ((PropertyInfo)member).PropertyType; + + object targetValue = null; + switch (property.CimType) + { + case CimType.Instance: + { + var cimPropertyInstance = property.Value as CimInstance; + if (cimPropertyInstance != null && + cimPropertyInstance.CimClass != null && + cimPropertyInstance.CimClass.CimSystemProperties != null && + string.Equals( + cimPropertyInstance.CimClass.CimSystemProperties.ClassName, + "MSFT_Credential", StringComparison.OrdinalIgnoreCase)) + { + targetValue = ConvertCimInstancePsCredential(moduleName, cimPropertyInstance); + } + else + { + targetValue = ConvertCimInstanceToObject(memberType, cimPropertyInstance, moduleName); + } + + if (targetValue == null) + { + return null; + } + } + + break; + case CimType.InstanceArray: + { + if (memberType == typeof(Hashtable)) + { + targetValue = ConvertCimInstanceHashtable(moduleName, (CimInstance[])property.Value); + } + else + { + var instanceArray = (CimInstance[])property.Value; + if (!memberType.IsArray) + { + errorMessage = string.Format(CultureInfo.CurrentCulture, ParserStrings.ExpectArrayTypeOfPropertyInPSClass, new object[] { property.Name, className }); + var invalidOperationException = new InvalidOperationException(errorMessage); + throw invalidOperationException; + } + + var elementType = memberType.GetElementType(); + var targetArray = Array.CreateInstance(elementType, instanceArray.Length); + for (int i = 0; i < instanceArray.Length; i++) + { + var obj = ConvertCimInstanceToObject(elementType, instanceArray[i], moduleName); + if (obj == null) + { + return null; + } + + targetArray.SetValue(obj, i); + } + + targetValue = targetArray; + } + } + + break; + default: + targetValue = LanguagePrimitives.ConvertTo(property.Value, memberType, CultureInfo.InvariantCulture); + break; + } + + if (targetValue == null) + { + errorMessage = string.Format(CultureInfo.CurrentCulture, ParserStrings.ConvertCimPropertyToObjectPropertyFailed, new object[] { property.Name, className }); + var invalidOperationException = new InvalidOperationException(errorMessage); + throw invalidOperationException; + } + + if (member is FieldInfo) + { + ((FieldInfo)member).SetValue(targetObject, targetValue); + } + + if (member is PropertyInfo) + { + ((PropertyInfo)member).SetValue(targetObject, targetValue); + } + } + } + + return targetObject; + } + + /// + /// Convert hashtable from Ciminstance to hashtable primitive type. + /// + /// + /// + /// + private static object ConvertCimInstanceHashtable(string providerName, CimInstance[] arrayInstance) + { + var result = new Hashtable(); + string errorMessage; + + try + { + foreach (var keyValuePair in arrayInstance) + { + var key = keyValuePair.CimInstanceProperties["Key"]; + var value = keyValuePair.CimInstanceProperties["Value"]; + + if (key == null || value == null) + { + errorMessage = string.Format(CultureInfo.CurrentCulture, ParserStrings.InvalidHashtable, providerName); + var invalidOperationException = new InvalidOperationException(errorMessage); + throw invalidOperationException; + } + + result.Add(LanguagePrimitives.ConvertTo(key.Value), LanguagePrimitives.ConvertTo(value.Value)); + } + } + catch (Exception exception) + { + errorMessage = string.Format(CultureInfo.CurrentCulture, ParserStrings.InvalidHashtable, providerName); + var invalidOperationException = new InvalidOperationException(errorMessage, exception); + throw invalidOperationException; + } + + return result; + } + /// + /// Convert CIM instance to PS Credential. + /// + /// + /// + /// + private static object ConvertCimInstancePsCredential(string providerName, CimInstance propertyInstance) + { + string errorMessage; + string userName; + string plainPassWord; + + try + { + userName = propertyInstance.CimInstanceProperties["UserName"].Value as string; + if (string.IsNullOrEmpty(userName)) + { + errorMessage = string.Format(CultureInfo.CurrentCulture, ParserStrings.InvalidUserName, providerName); + var invalidOperationException = new InvalidOperationException(errorMessage); + throw invalidOperationException; + } + } + catch (CimException exception) + { + errorMessage = string.Format(CultureInfo.CurrentCulture, ParserStrings.InvalidUserName, providerName); + var invalidOperationException = new InvalidOperationException(errorMessage, exception); + throw invalidOperationException; + } + + try + { + plainPassWord = propertyInstance.CimInstanceProperties["PassWord"].Value as string; + + // In future we might receive password in an encrypted format. Make sure we add + // the decryption login in this method. + if (string.IsNullOrEmpty(plainPassWord)) + { + errorMessage = string.Format(CultureInfo.CurrentCulture, ParserStrings.InvalidPassword, providerName); + var invalidOperationException = new InvalidOperationException(errorMessage); + throw invalidOperationException; + } + } + catch (CimException exception) + { + errorMessage = string.Format(CultureInfo.CurrentCulture, ParserStrings.InvalidPassword, providerName); + var invalidOperationException = new InvalidOperationException(errorMessage, exception); + throw invalidOperationException; + } + + // Extract the password into a SecureString. + var password = new SecureString(); + foreach (char t in plainPassWord) + { + password.AppendChar(t); + } + + password.MakeReadOnly(); + return new PSCredential(userName, password); + } + } +} + +namespace Microsoft.PowerShell.DesiredStateConfiguration +{ + /// + /// To make it easier to specify -ConfigurationData parameter, we add an ArgumentTransformationAttribute here. + /// When the input data is of type string and is valid path to a file that can be converted to hashtable, we do + /// the conversion and return the converted value. Otherwise, we just return the input data. + /// + [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property | AttributeTargets.Parameter, AllowMultiple = false)] + public sealed class ArgumentToConfigurationDataTransformationAttribute : ArgumentTransformationAttribute + { + /// + /// Convert a file of ConfigurationData into a hashtable. + /// + /// + /// + /// + public override object Transform(EngineIntrinsics engineIntrinsics, object inputData) + { + var configDataPath = inputData as string; + if (string.IsNullOrEmpty(configDataPath)) + { + return inputData; + } + + if (engineIntrinsics == null) + { + throw PSTraceSource.NewArgumentNullException(nameof(engineIntrinsics)); + } + + return PsUtils.EvaluatePowerShellDataFileAsModuleManifest( + "ConfigurationData", + configDataPath, + engineIntrinsics.SessionState.Internal.ExecutionContext, + skipPathValidation: false); + } + } + + /// + /// + /// Represents a communication channel to a CIM server. + /// + /// + /// This is the main entry point of the Microsoft.Management.Infrastructure API. + /// All CIM operations are represented as methods of this class. + /// + /// + internal class CimDSCParser + { + private CimMofDeserializer _deserializer; + private CimMofDeserializer.OnClassNeeded _onClassNeeded; + + /// + /// + internal CimDSCParser(CimMofDeserializer.OnClassNeeded onClassNeeded) + { + _deserializer = CimMofDeserializer.Create(); + _onClassNeeded = onClassNeeded; + } + + /// + /// + internal CimDSCParser(CimMofDeserializer.OnClassNeeded onClassNeeded, Microsoft.Management.Infrastructure.Serialization.MofDeserializerSchemaValidationOption validationOptions) + { + _deserializer = CimMofDeserializer.Create(); + _deserializer.SchemaValidationOption = validationOptions; + _onClassNeeded = onClassNeeded; + } + + /// + /// + /// + /// + [SuppressMessage("Microsoft.Design", "CA1045:DoNotPassTypesByReference", MessageId = "3#", Justification = "Have to return 2 things. Wrapping those 2 things in a class will result in a more, not less complexity")] + internal List ParseInstanceMof(string filePath) + { + uint offset = 0; + var buffer = GetFileContent(filePath); + try + { + var result = new List(_deserializer.DeserializeInstances(buffer, ref offset, _onClassNeeded, null)); + return result; + } + catch (CimException exception) + { + PSInvalidOperationException e = PSTraceSource.NewInvalidOperationException( + exception, ParserStrings.CimDeserializationError, filePath); + + e.SetErrorId("CimDeserializationError"); + throw e; + } + } + + /// + /// Read file content to byte array. + /// + /// + /// + internal static byte[] GetFileContent(string fullFilePath) + { + if (string.IsNullOrEmpty(fullFilePath)) + { + throw PSTraceSource.NewArgumentNullException(nameof(fullFilePath)); + } + + if (!File.Exists(fullFilePath)) + { + var errorMessage = string.Format(CultureInfo.CurrentCulture, ParserStrings.FileNotFound, fullFilePath); + throw PSTraceSource.NewArgumentException(nameof(fullFilePath), errorMessage); + } + + using (FileStream fs = File.OpenRead(fullFilePath)) + { + var bytes = new byte[fs.Length]; + fs.Read(bytes, 0, Convert.ToInt32(fs.Length)); + return bytes; + } + } + + internal List ParseSchemaMofFileBuffer(string mof) + { + uint offset = 0; +#if UNIX + // OMI only supports UTF-8 without BOM + var encoding = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false); +#else + // This is what we traditionally use with Windows + // DSC asked to keep it UTF-32 for Windows + var encoding = new UnicodeEncoding(); +#endif + + var buffer = encoding.GetBytes(mof); + + var result = new List(_deserializer.DeserializeClasses(buffer, ref offset, null, null, null, _onClassNeeded, null)); + return result; + } + + /// + /// + /// + /// + [SuppressMessage("Microsoft.Design", "CA1045:DoNotPassTypesByReference", MessageId = "3#", Justification = "Have to return 2 things. Wrapping those 2 things in a class will result in a more, not less complexity")] + internal List ParseSchemaMof(string filePath) + { + uint offset = 0; + var buffer = GetFileContent(filePath); + try + { + string fileNameDefiningClass = Path.GetFileNameWithoutExtension(filePath); + int dotIndex = fileNameDefiningClass.IndexOf('.'); + if (dotIndex != -1) + { + fileNameDefiningClass = fileNameDefiningClass.Substring(0, dotIndex); + } + + var result = new List(_deserializer.DeserializeClasses(buffer, ref offset, null, null, null, _onClassNeeded, null)); + foreach (CimClass c in result) + { + string superClassName = c.CimSuperClassName; + string className = c.CimSystemProperties.ClassName; + if ((superClassName != null) && (superClassName.Equals("OMI_BaseResource", StringComparison.OrdinalIgnoreCase))) + { + // Get the name of the file without schema.mof extension + if (!(className.Equals(fileNameDefiningClass, StringComparison.OrdinalIgnoreCase))) + { + PSInvalidOperationException e = PSTraceSource.NewInvalidOperationException( + ParserStrings.ClassNameNotSameAsDefiningFile, className, fileNameDefiningClass); + throw e; + } + } + } + + return result; + } + catch (CimException exception) + { + PSInvalidOperationException e = PSTraceSource.NewInvalidOperationException( + exception, ParserStrings.CimDeserializationError, filePath); + + e.SetErrorId("CimDeserializationError"); + throw e; + } + } + + /// + /// Make sure that the instance conforms to the schema. + /// + /// + internal void ValidateInstanceText(string classText) + { + uint offset = 0; + byte[] bytes = null; + + if (Platform.IsLinux || Platform.IsMacOS) + { + bytes = System.Text.Encoding.UTF8.GetBytes(classText); + } + else + { + bytes = System.Text.Encoding.Unicode.GetBytes(classText); + } + + _deserializer.DeserializeInstances(bytes, ref offset, _onClassNeeded, null); + } + } +} + namespace Microsoft.PowerShell.DesiredStateConfiguration.Internal { /// @@ -86,20 +544,19 @@ public static class DscClassCache // Create a list of classes which are not actual DSC resources similar to what we do inside PSDesiredStateConfiguration.psm1 private static readonly string[] s_hiddenResourceList = - { - "MSFT_BaseConfigurationProviderRegistration", - "MSFT_CimConfigurationProviderRegistration", - "MSFT_PSConfigurationProviderRegistration", - }; + { + "MSFT_BaseConfigurationProviderRegistration", + "MSFT_CimConfigurationProviderRegistration", + "MSFT_PSConfigurationProviderRegistration", + }; // Create a HashSet for fast lookup. According to MSDN, the time complexity of search for an element in a HashSet is O(1) - private static readonly HashSet s_hiddenResourceCache = new HashSet( - s_hiddenResourceList, - StringComparer.OrdinalIgnoreCase); + private static readonly HashSet s_hiddenResourceCache = + new(s_hiddenResourceList, StringComparer.OrdinalIgnoreCase); // a collection to hold current importing script based resource file // this prevent circular importing case when the script resource existing in the same module with resources it import-dscresource - private static readonly HashSet s_currentImportingScriptFiles = new HashSet(StringComparer.OrdinalIgnoreCase); + private static readonly HashSet s_currentImportingScriptFiles = new(StringComparer.OrdinalIgnoreCase); /// /// DSC class cache for this runspace. @@ -181,18 +638,20 @@ private static HashSet ScriptKeywordFileCache /// /// Default ModuleName and ModuleVersion to use. /// - private static readonly Tuple s_defaultModuleInfoForResource = new Tuple("PSDesiredStateConfiguration", new Version("1.1")); + private static readonly Tuple s_defaultModuleInfoForResource = + new("PSDesiredStateConfiguration", new Version("1.1")); /// /// Default ModuleName and ModuleVersion to use for meta configuration resources. /// - internal static readonly Tuple DefaultModuleInfoForMetaConfigResource = new Tuple("PSDesiredStateConfigurationEngine", new Version("2.0")); + internal static readonly Tuple DefaultModuleInfoForMetaConfigResource = + new("PSDesiredStateConfigurationEngine", new Version("2.0")); /// /// A set of dynamic keywords that can be used in both configuration and meta configuration. /// internal static readonly HashSet SystemResourceNames = - new HashSet(StringComparer.OrdinalIgnoreCase) { "Node", "OMI_ConfigurationDocument" }; + new(StringComparer.OrdinalIgnoreCase) { "Node", "OMI_ConfigurationDocument" }; /// /// When this property is set to true, DSC Cache will cache multiple versions of a resource. @@ -231,11 +690,13 @@ public static void Initialize() /// List of module path from where DSC PS modules will be loaded. public static void Initialize(Collection errors, List modulePathList) { - s_tracer.WriteLine("Initializing DSC class cache"); + s_tracer.WriteLine("Initializing DSC class cache force={0}"); if (Platform.IsLinux || Platform.IsMacOS) { + // // Load the base schema files. + // ClearCache(); var dscConfigurationDirectory = Environment.GetEnvironmentVariable("DSC_HOME") ?? "/etc/opt/omi/conf/dsc/configuration"; @@ -253,7 +714,9 @@ public static void Initialize(Collection errors, List moduleP var allResourceRoots = new string[] { dscConfigurationDirectory }; + // // Load all of the system resource schema files, searching + // string resources; foreach (var resourceRoot in allResourceRoots) { @@ -290,8 +753,9 @@ public static void Initialize(Collection errors, List moduleP var customResourceRoot = Path.Combine(programFilesDirectory, "WindowsPowerShell\\Configuration"); Debug.Assert(Directory.Exists(customResourceRoot), "%ProgramFiles%\\WindowsPowerShell\\Configuration Directory does not exist"); var allResourceRoots = new string[] { systemResourceRoot, customResourceRoot }; - + // // Load the base schema files. + // ClearCache(); var resourceBaseFile = Path.Combine(systemResourceRoot, "BaseRegistration\\BaseResource.schema.mof"); ImportClasses(resourceBaseFile, s_defaultModuleInfoForResource, errors); @@ -302,7 +766,9 @@ public static void Initialize(Collection errors, List moduleP var metaConfigExtensionFile = Path.Combine(systemResourceRoot, "BaseRegistration\\MSFT_MetaConfigurationExtensionClasses.schema.mof"); ImportClasses(metaConfigExtensionFile, DefaultModuleInfoForMetaConfigResource, errors); + // // Load all of the system resource schema files, searching + // string resources; foreach (var resourceRoot in allResourceRoots) { @@ -320,7 +786,7 @@ public static void Initialize(Collection errors, List moduleP // Load Regular and DSC PS modules bool importInBoxResourcesImplicitly = false; - List modulePaths = new List(); + List modulePaths = new(); if (modulePathList == null || modulePathList.Count == 0) { modulePaths.Add(Path.Combine(configSystemPath, inboxModulePath)); @@ -444,25 +910,21 @@ private static Tuple GetModuleInfoHelper(string moduleFolderPat { s_tracer.WriteLine( "DSC GetModuleVersion: ModuleVersion value '{0}' cannot be converted to System.Version. Skip the module '{1}'.", - versionValue, - moduleName); + versionValue, moduleName); } } else { s_tracer.WriteLine( "DSC GetModuleVersion: Manifest file '{0}' does not contain ModuleVersion. Skip the module '{1}'.", - manifestPath, - moduleName); + manifestPath, moduleName); } } catch (PSInvalidOperationException ex) { s_tracer.WriteLine( "DSC GetModuleVersion: Error evaluating module manifest file '{0}', with error '{1}'. Skip the module '{2}'.", - manifestPath, - ex, - moduleName); + manifestPath, ex, moduleName); } return null; @@ -484,7 +946,7 @@ private static CimClass MyClassCallback(string serverName, string namespaceName, } /// - /// Reads CIM MOF schema file and returns classes defined in it + /// Reads CIM MOF schema file and returns classes defined in it; this is used in MOF->JSON and MOF->PSClass convertion tools. /// /// /// Path to CIM MOF schema file for reading @@ -684,7 +1146,7 @@ private static List GetCachedClasses() /// List of cached cim classes. public static List GetCachedClassesForModule(PSModuleInfo module) { - List cachedClasses = new List(); + List cachedClasses = new(); var moduleQualifiedName = string.Format(CultureInfo.InvariantCulture, "{0}\\{1}", module.Name, module.Version.ToString()); foreach (var dscClassCacheEntry in ClassCache) { @@ -704,7 +1166,7 @@ private static List GetCachedClasses() /// public static List GetFileDefiningClass(string className) { - List files = new List(); + List files = new(); foreach (var pair in ByFileClassCache) { var file = pair.Key; @@ -823,8 +1285,7 @@ public static void ValidateInstanceText(string instanceText) private static bool IsMagicProperty(string propertyName) { - return System.Text.RegularExpressions.Regex.Match( - propertyName, + return System.Text.RegularExpressions.Regex.Match(propertyName, "^(ResourceId|SourceInfo|ModuleName|ModuleVersion|ConfigurationName)$", System.Text.RegularExpressions.RegexOptions.IgnoreCase).Success; } @@ -852,7 +1313,7 @@ private static string GetFriendlyName(CimClass cimClass) /// public static Collection GetCachedKeywords() { - Collection keywords = new Collection(); + Collection keywords = new(); foreach (KeyValuePair cachedClass in ClassCache) { @@ -922,7 +1383,9 @@ private static DynamicKeyword CreateKeywordFromCimClass(string moduleName, Versi string alias = GetFriendlyName(cimClass); var keywordString = string.IsNullOrEmpty(alias) ? resourceName : alias; + // // Skip all of the base, meta, registration and other classes that are not intended to be used directly by a script author + // if (System.Text.RegularExpressions.Regex.Match(keywordString, "^OMI_Base|^OMI_.*Registration", System.Text.RegularExpressions.RegexOptions.IgnoreCase).Success) { return null; @@ -958,7 +1421,9 @@ private static DynamicKeyword CreateKeywordFromCimClass(string moduleName, Versi // If it's a resource type, then a resource name is required. keyword.NameMode = isResourceType ? DynamicKeywordNameMode.NameRequired : DynamicKeywordNameMode.NoName; + // // Add the settable properties to the keyword object + // foreach (var prop in cimClass.CimClassProperties) { // If the property is marked as readonly, skip it... @@ -1066,9 +1531,7 @@ private static DynamicKeyword CreateKeywordFromCimClass(string moduleName, Versi { s_tracer.WriteLine( "DSC CreateDynamicKeywordFromClass: the count of values for qualifier 'Values' and 'ValueMap' doesn't match. count of 'Values': {0}, count of 'ValueMap': {1}. Skip the keyword '{2}'.", - keyProp.Values.Count, - valueMap.Length, - keyword.Keyword); + keyProp.Values.Count, valueMap.Length, keyword.Keyword); return null; } @@ -1081,8 +1544,7 @@ private static DynamicKeyword CreateKeywordFromCimClass(string moduleName, Versi { s_tracer.WriteLine( "DSC CreateDynamicKeywordFromClass: same string value '{0}' appears more than once in qualifier 'Values'. Skip the keyword '{1}'.", - key, - keyword.Keyword); + key, keyword.Keyword); return null; } @@ -1109,9 +1571,11 @@ private static DynamicKeyword CreateKeywordFromCimClass(string moduleName, Versi private static void UpdateKnownRestriction(DynamicKeyword keyword) { if ( - string.Equals(keyword.ResourceName, "MSFT_DSCMetaConfigurationV2", StringComparison.OrdinalIgnoreCase) + string.Equals(keyword.ResourceName, "MSFT_DSCMetaConfigurationV2", + StringComparison.OrdinalIgnoreCase) || - string.Equals(keyword.ResourceName, "MSFT_DSCMetaConfiguration", StringComparison.OrdinalIgnoreCase)) + string.Equals(keyword.ResourceName, "MSFT_DSCMetaConfiguration", + StringComparison.OrdinalIgnoreCase)) { if (keyword.Properties["RefreshFrequencyMins"] != null) { @@ -1183,11 +1647,8 @@ public static void LoadDefaultCimKeywords(Collection errors, bool cac /// Collection of any errors encountered while loading keywords. /// List of module path from where DSC PS modules will be loaded. /// Allow caching the resources from multiple versions of modules. - private static void LoadDefaultCimKeywords( - Dictionary functionsToDefine, - Collection errors, - List modulePathList, - bool cacheResourcesFromMultipleModuleVersions) + private static void LoadDefaultCimKeywords(Dictionary functionsToDefine, Collection errors, + List modulePathList, bool cacheResourcesFromMultipleModuleVersions) { DynamicKeyword.Reset(); Initialize(errors, modulePathList); @@ -1239,12 +1700,17 @@ private static void LoadDefaultCimKeywords( } } - // This function is called after parsing the Import-DscResource keyword and it's arguments, but before parsing anything else. + // This function is called after parsing the Import-DscResource keyword and it's arguments, but before parsing + // anything else. + // + // private static ParseError[] ImportResourcePostParse(DynamicKeywordStatementAst kwAst) { var elements = Ast.CopyElements(kwAst.CommandElements); - Diagnostics.Assert(elements[0] is StringConstantExpressionAst && ((StringConstantExpressionAst)elements[0]).Value.Equals("Import-DscResource", StringComparison.OrdinalIgnoreCase), "Incorrect ast for expected keyword"); + Diagnostics.Assert(elements[0] is StringConstantExpressionAst && + ((StringConstantExpressionAst)elements[0]).Value.Equals("Import-DscResource", StringComparison.OrdinalIgnoreCase), + "Incorrect ast for expected keyword"); var commandAst = new CommandAst(kwAst.Extent, elements, TokenKind.Unknown, null); const string nameParam = "Name"; @@ -1256,7 +1722,9 @@ private static ParseError[] ImportResourcePostParse(DynamicKeywordStatementAst k var errorList = new List(); foreach (var bindingException in bindingResult.BindingExceptions.Values) { - errorList.Add(new ParseError(bindingException.CommandElement.Extent, "ParameterBindingException", bindingException.BindingException.Message)); + errorList.Add(new ParseError(bindingException.CommandElement.Extent, + "ParameterBindingException", + bindingException.BindingException.Message)); } ParameterBindingResult moduleNameBindingResult = null; @@ -1270,7 +1738,9 @@ private static ParseError[] ImportResourcePostParse(DynamicKeywordStatementAst k var parameterBindingResult = binding.Value; if (boundParameterName.All(char.IsDigit)) { - errorList.Add(new ParseError(parameterBindingResult.Value.Extent, "ImportDscResourcePositionalParamsNotSupported", string.Format(CultureInfo.CurrentCulture, ParserStrings.ImportDscResourcePositionalParamsNotSupported))); + errorList.Add(new ParseError(parameterBindingResult.Value.Extent, + "ImportDscResourcePositionalParamsNotSupported", + string.Format(CultureInfo.CurrentCulture, ParserStrings.ImportDscResourcePositionalParamsNotSupported))); continue; } @@ -1288,16 +1758,17 @@ private static ParseError[] ImportResourcePostParse(DynamicKeywordStatementAst k } else { - errorList.Add(new ParseError(parameterBindingResult.Value.Extent, "ImportDscResourceNeedParams", string.Format(CultureInfo.CurrentCulture, ParserStrings.ImportDscResourceNeedParams))); + errorList.Add(new ParseError(parameterBindingResult.Value.Extent, + "ImportDscResourceNeedParams", + string.Format(CultureInfo.CurrentCulture, ParserStrings.ImportDscResourceNeedParams))); } } if (errorList.Count == 0 && moduleNameBindingResult == null && resourceNameBindingResult == null) { - errorList.Add(new ParseError( - kwAst.Extent, - "ImportDscResourceNeedParams", - string.Format(CultureInfo.CurrentCulture, ParserStrings.ImportDscResourceNeedParams))); + errorList.Add(new ParseError(kwAst.Extent, + "ImportDscResourceNeedParams", + string.Format(CultureInfo.CurrentCulture, ParserStrings.ImportDscResourceNeedParams))); } // Check here if Version is specified but modulename is not specified @@ -1309,10 +1780,9 @@ private static ParseError[] ImportResourcePostParse(DynamicKeywordStatementAst k // once we have different error messages for 2 scenarios we can remove this check if (resourceNameBindingResult != null) { - errorList.Add(new ParseError( - kwAst.Extent, - "ImportDscResourceNeedModuleNameWithModuleVersion", - string.Format(CultureInfo.CurrentCulture, ParserStrings.ImportDscResourceNeedParams))); + errorList.Add(new ParseError(kwAst.Extent, + "ImportDscResourceNeedModuleNameWithModuleVersion", + string.Format(CultureInfo.CurrentCulture, ParserStrings.ImportDscResourceNeedParams))); } } @@ -1323,10 +1793,9 @@ private static ParseError[] ImportResourcePostParse(DynamicKeywordStatementAst k if (!IsConstantValueVisitor.IsConstant(resourceNameBindingResult.Value, out resourceName, true, true) || !LanguagePrimitives.TryConvertTo(resourceName, out resourceNames)) { - errorList.Add(new ParseError( - resourceNameBindingResult.Value.Extent, - "RequiresInvalidStringArgument", - string.Format(CultureInfo.CurrentCulture, ParserStrings.RequiresInvalidStringArgument, nameParam))); + errorList.Add(new ParseError(resourceNameBindingResult.Value.Extent, + "RequiresInvalidStringArgument", + string.Format(CultureInfo.CurrentCulture, ParserStrings.RequiresInvalidStringArgument, nameParam))); } } @@ -1336,10 +1805,9 @@ private static ParseError[] ImportResourcePostParse(DynamicKeywordStatementAst k object moduleVer = null; if (!IsConstantValueVisitor.IsConstant(moduleVersionBindingResult.Value, out moduleVer, true, true)) { - errorList.Add(new ParseError( - moduleVersionBindingResult.Value.Extent, - "RequiresArgumentMustBeConstant", - ParserStrings.RequiresArgumentMustBeConstant)); + errorList.Add(new ParseError(moduleVersionBindingResult.Value.Extent, + "RequiresArgumentMustBeConstant", + ParserStrings.RequiresArgumentMustBeConstant)); } if (moduleVer is double) @@ -1352,10 +1820,9 @@ private static ParseError[] ImportResourcePostParse(DynamicKeywordStatementAst k if (!LanguagePrimitives.TryConvertTo(moduleVer, out moduleVersion)) { - errorList.Add(new ParseError( - moduleVersionBindingResult.Value.Extent, - "RequiresVersionInvalid", - ParserStrings.RequiresVersionInvalid)); + errorList.Add(new ParseError(moduleVersionBindingResult.Value.Extent, + "RequiresVersionInvalid", + ParserStrings.RequiresVersionInvalid)); } } @@ -1365,10 +1832,9 @@ private static ParseError[] ImportResourcePostParse(DynamicKeywordStatementAst k object moduleName = null; if (!IsConstantValueVisitor.IsConstant(moduleNameBindingResult.Value, out moduleName, true, true)) { - errorList.Add(new ParseError( - moduleNameBindingResult.Value.Extent, - "RequiresArgumentMustBeConstant", - ParserStrings.RequiresArgumentMustBeConstant)); + errorList.Add(new ParseError(moduleNameBindingResult.Value.Extent, + "RequiresArgumentMustBeConstant", + ParserStrings.RequiresArgumentMustBeConstant)); } if (LanguagePrimitives.TryConvertTo(moduleName, out moduleSpecifications)) @@ -1376,28 +1842,25 @@ private static ParseError[] ImportResourcePostParse(DynamicKeywordStatementAst k // if resourceNames are specified then we can not specify multiple modules name if (moduleSpecifications != null && moduleSpecifications.Length > 1 && resourceNames != null) { - errorList.Add(new ParseError( - moduleNameBindingResult.Value.Extent, - "ImportDscResourceMultipleModulesNotSupportedWithName", - string.Format(CultureInfo.CurrentCulture, ParserStrings.ImportDscResourceMultipleModulesNotSupportedWithName))); + errorList.Add(new ParseError(moduleNameBindingResult.Value.Extent, + "ImportDscResourceMultipleModulesNotSupportedWithName", + string.Format(CultureInfo.CurrentCulture, ParserStrings.ImportDscResourceMultipleModulesNotSupportedWithName))); } // if moduleversion is specified then we can not specify multiple modules name if (moduleSpecifications != null && moduleSpecifications.Length > 1 && moduleVersion != null) { - errorList.Add(new ParseError( - moduleNameBindingResult.Value.Extent, - "ImportDscResourceMultipleModulesNotSupportedWithVersion", - string.Format(CultureInfo.CurrentCulture, ParserStrings.ImportDscResourceNeedParams))); + errorList.Add(new ParseError(moduleNameBindingResult.Value.Extent, + "ImportDscResourceMultipleModulesNotSupportedWithVersion", + string.Format(CultureInfo.CurrentCulture, ParserStrings.ImportDscResourceNeedParams))); } // if moduleversion is specified then we can not specify another version in modulespecification object of ModuleName if (moduleSpecifications != null && (moduleSpecifications[0].Version != null || moduleSpecifications[0].MaximumVersion != null) && moduleVersion != null) { - errorList.Add(new ParseError( - moduleNameBindingResult.Value.Extent, - "ImportDscResourceMultipleModuleVersionsNotSupported", - string.Format(CultureInfo.CurrentCulture, ParserStrings.ImportDscResourceNeedParams))); + errorList.Add(new ParseError(moduleNameBindingResult.Value.Extent, + "ImportDscResourceMultipleModuleVersionsNotSupported", + string.Format(CultureInfo.CurrentCulture, ParserStrings.ImportDscResourceNeedParams))); } // If moduleVersion is specified we have only one module Name in valid scenario @@ -1409,10 +1872,9 @@ private static ParseError[] ImportResourcePostParse(DynamicKeywordStatementAst k } else { - errorList.Add(new ParseError( - moduleNameBindingResult.Value.Extent, - "RequiresInvalidStringArgument", - string.Format(CultureInfo.CurrentCulture, ParserStrings.RequiresInvalidStringArgument, moduleNameParam))); + errorList.Add(new ParseError(moduleNameBindingResult.Value.Extent, + "RequiresInvalidStringArgument", + string.Format(CultureInfo.CurrentCulture, ParserStrings.RequiresInvalidStringArgument, moduleNameParam))); } } @@ -1440,7 +1902,9 @@ private static ParseError[] ImportResourceCheckSemantics(DynamicKeywordStatement errorList = new List(); } - errorList.Add(new ParseError(kwAst.Extent, "ImportDscResourceInsideNode", string.Format(CultureInfo.CurrentCulture, ParserStrings.ImportDscResourceInsideNode))); + errorList.Add(new ParseError(kwAst.Extent, + "ImportDscResourceInsideNode", + string.Format(CultureInfo.CurrentCulture, ParserStrings.ImportDscResourceInsideNode))); break; } @@ -1460,7 +1924,7 @@ private static ParseError[] ImportResourceCheckSemantics(DynamicKeywordStatement // This function performs semantic checks for all DSC Resources keywords. private static ParseError[] CheckMandatoryPropertiesPresent(DynamicKeywordStatementAst kwAst) { - HashSet mandatoryPropertiesNames = new HashSet(StringComparer.OrdinalIgnoreCase); + HashSet mandatoryPropertiesNames = new(StringComparer.OrdinalIgnoreCase); foreach (var pair in kwAst.Keyword.Properties) { if (pair.Value.Mandatory) @@ -1512,15 +1976,10 @@ private static ParseError[] CheckMandatoryPropertiesPresent(DynamicKeywordStatem int i = 0; foreach (string name in mandatoryPropertiesNames) { - errors[i] = new ParseError( - extent, - "MissingValueForMandatoryProperty", - string.Format( - CultureInfo.CurrentCulture, - ParserStrings.MissingValueForMandatoryProperty, - kwAst.Keyword.Keyword, - kwAst.Keyword.Properties.First(p => StringComparer.OrdinalIgnoreCase.Equals(p.Value.Name, name)).Value.TypeConstraint, - name)); + errors[i] = new ParseError(extent, "MissingValueForMandatoryProperty", + string.Format(CultureInfo.CurrentCulture, ParserStrings.MissingValueForMandatoryProperty, + kwAst.Keyword.Keyword, kwAst.Keyword.Properties.First( + p => StringComparer.OrdinalIgnoreCase.Equals(p.Value.Name, name)).Value.TypeConstraint, name)); i++; } @@ -1537,11 +1996,10 @@ private static ParseError[] CheckMandatoryPropertiesPresent(DynamicKeywordStatem /// Module information, can be null. /// Name of the resource to be loaded from module. /// List of errors reported by the method. - public static void LoadResourcesFromModule( - IScriptExtent scriptExtent, - ModuleSpecification[] moduleSpecifications, - string[] resourceNames, - List errorList) + public static void LoadResourcesFromModule(IScriptExtent scriptExtent, + ModuleSpecification[] moduleSpecifications, + string[] resourceNames, + List errorList) { // get all required modules var modules = new Collection(); @@ -1576,13 +2034,11 @@ public static void LoadResourcesFromModule( { if (moduleInfos.Count > 1) { - errorList.Add(new ParseError( - scriptExtent, - "MultipleModuleEntriesFoundDuringParse", - string.Format( - CultureInfo.CurrentCulture, - ParserStrings.MultipleModuleEntriesFoundDuringParse, - moduleToImport.Name))); + errorList.Add(new ParseError(scriptExtent, + "MultipleModuleEntriesFoundDuringParse", + string.Format(CultureInfo.CurrentCulture, + ParserStrings.MultipleModuleEntriesFoundDuringParse, + moduleToImport.Name))); } else { @@ -1590,7 +2046,8 @@ public static void LoadResourcesFromModule( ? moduleToImport.Name : string.Format(CultureInfo.CurrentCulture, "<{0}, {1}>", moduleToImport.Name, moduleToImport.Version); - errorList.Add(new ParseError(scriptExtent, "ModuleNotFoundDuringParse", string.Format(CultureInfo.CurrentCulture, ParserStrings.ModuleNotFoundDuringParse, moduleString))); + errorList.Add(new ParseError(scriptExtent, "ModuleNotFoundDuringParse", + string.Format(CultureInfo.CurrentCulture, ParserStrings.ModuleNotFoundDuringParse, moduleString))); } return; @@ -1645,15 +2102,21 @@ public static void LoadResourcesFromModule( } catch (FileNotFoundException) { - errorList.Add(new ParseError(scriptExtent, "SchemaFileNotFound", string.Format(CultureInfo.CurrentCulture, ParserStrings.SchemaFileNotFound, schemaMofFilePath))); + errorList.Add(new ParseError(scriptExtent, + "SchemaFileNotFound", + string.Format(CultureInfo.CurrentCulture, ParserStrings.SchemaFileNotFound, schemaMofFilePath))); } catch (PSInvalidOperationException e) { - errorList.Add(new ParseError(scriptExtent, e.ErrorRecord.FullyQualifiedErrorId, e.Message)); + errorList.Add(new ParseError(scriptExtent, + e.ErrorRecord.FullyQualifiedErrorId, + e.Message)); } catch (Exception e) { - errorList.Add(new ParseError(scriptExtent, "ExceptionParsingMOFFile", string.Format(CultureInfo.CurrentCulture, ParserStrings.ExceptionParsingMOFFile, schemaMofFilePath, e.Message))); + errorList.Add(new ParseError(scriptExtent, + "ExceptionParsingMOFFile", + string.Format(CultureInfo.CurrentCulture, ParserStrings.ExceptionParsingMOFFile, schemaMofFilePath, e.Message))); } var schemaScriptFilePath = string.Empty; @@ -1664,12 +2127,16 @@ public static void LoadResourcesFromModule( } catch (FileNotFoundException) { - errorList.Add(new ParseError(scriptExtent, "SchemaFileNotFound", string.Format(CultureInfo.CurrentCulture, ParserStrings.SchemaFileNotFound, schemaScriptFilePath))); + errorList.Add(new ParseError(scriptExtent, + "SchemaFileNotFound", + string.Format(CultureInfo.CurrentCulture, ParserStrings.SchemaFileNotFound, schemaScriptFilePath))); } catch (Exception e) { // This shouldn't happen so just report the error as is - errorList.Add(new ParseError(scriptExtent, "UnexpectedParseError", string.Format(CultureInfo.CurrentCulture, e.ToString()))); + errorList.Add(new ParseError(scriptExtent, + "UnexpectedParseError", + string.Format(CultureInfo.CurrentCulture, e.ToString()))); } if (foundCimSchema || foundScriptSchema) @@ -1694,7 +2161,7 @@ public static void LoadResourcesFromModule( } // resource name without wildcard (*) should be imported only once - if (!resourceToImport.Contains("*") && foundResources) + if (!resourceToImport.Contains('*') && foundResources) { resourcesFound.Add(resourceToImport); } @@ -1716,19 +2183,17 @@ public static void LoadResourcesFromModule( { foreach (var resourceNameToImport in resourcesToImport) { - if (!resourceNameToImport.Contains("*")) + if (!resourceNameToImport.Contains('*')) { - errorList.Add(new ParseError(scriptExtent, "DscResourcesNotFoundDuringParsing", string.Format(CultureInfo.CurrentCulture, ParserStrings.DscResourcesNotFoundDuringParsing, resourceNameToImport))); + errorList.Add(new ParseError(scriptExtent, + "DscResourcesNotFoundDuringParsing", + string.Format(CultureInfo.CurrentCulture, ParserStrings.DscResourcesNotFoundDuringParsing, resourceNameToImport))); } } } } - private static void LoadPowerShellClassResourcesFromModule( - PSModuleInfo primaryModuleInfo, - PSModuleInfo moduleInfo, - ICollection resourcesToImport, - ICollection resourcesFound, + private static void LoadPowerShellClassResourcesFromModule(PSModuleInfo primaryModuleInfo, PSModuleInfo moduleInfo, ICollection resourcesToImport, ICollection resourcesFound, List errorList, Dictionary functionsToDefine = null, bool recurse = true, @@ -1763,9 +2228,7 @@ private static void LoadPowerShellClassResourcesFromModule( assembly = Assembly.ReflectionOnlyLoadFrom(path); } - catch - { - } + catch { } } // Ignore the module if we can't find the assembly. @@ -1913,7 +2376,7 @@ private static void GenerateMofForAst(TypeDefinitionAst typeAst, StringBuilder s ProcessMembers(sb, embeddedInstanceTypes, typeAst, className); - Queue bases = new Queue(); + Queue bases = new(); foreach (var b in typeAst.BaseTypes) { bases.Enqueue(b); @@ -1991,7 +2454,7 @@ private static bool GetResourceMethodsLineNumber(TypeDefinitionAst typeDefinitio } // All 3 methods (Get/Set/Test) position should be found. - return methodsLinePosition.Count == 3; + return (methodsLinePosition.Count == 3); } /// @@ -2011,7 +2474,7 @@ public static bool GetResourceMethodsLinePosition(PSModuleInfo moduleInfo, strin } IEnumerable resourceDefinitions; - List moduleFiles = new List(); + List moduleFiles = new(); if (moduleInfo.RootModule != null) { moduleFiles.Add(moduleInfo.Path); @@ -2079,7 +2542,9 @@ private static void ProcessMembers(StringBuilder sb, List embeddedInstan if (memberType != null) { // TODO - validate type and name - mofType = MapTypeToMofType(memberType, member.Name, className, out isArrayType, out embeddedInstanceType, embeddedInstanceTypes); + mofType = MapTypeToMofType(memberType, member.Name, className, out isArrayType, + out embeddedInstanceType, + embeddedInstanceTypes); if (memberType.IsEnum) { enumNames = Enum.GetNames(memberType); @@ -2088,20 +2553,14 @@ private static void ProcessMembers(StringBuilder sb, List embeddedInstan else { // PropertyType can't be null, we used typeof(object) above in that case so we don't get here. - mofType = MapTypeNameToMofType( - property.PropertyType.TypeName, - member.Name, - className, + mofType = MapTypeNameToMofType(property.PropertyType.TypeName, member.Name, className, out isArrayType, - out embeddedInstanceType, - embeddedInstanceTypes, - ref enumNames); + out embeddedInstanceType, embeddedInstanceTypes, ref enumNames); } string arrayAffix = isArrayType ? "[]" : string.Empty; - sb.AppendFormat( - CultureInfo.InvariantCulture, + sb.AppendFormat(CultureInfo.InvariantCulture, " {0}{1} {2}{3};\n", MapAttributesToMof(enumNames, attributes, embeddedInstanceType), mofType, @@ -2149,34 +2608,33 @@ private static bool GetResourceDefinitionsFromModule(string fileName, out IEnume { if (errorList != null && extent != null) { - List errorMessages = new List(); + List errorMessages = new(); foreach (var error in errors) { errorMessages.Add(error.ToString()); } - errorList.Add(new ParseError(extent, "FailToParseModuleScriptFile", string.Format(CultureInfo.CurrentCulture, ParserStrings.FailToParseModuleScriptFile, fileName, string.Join(Environment.NewLine, errorMessages)))); + errorList.Add(new ParseError(extent, "FailToParseModuleScriptFile", + string.Format(CultureInfo.CurrentCulture, ParserStrings.FailToParseModuleScriptFile, fileName, string.Join(Environment.NewLine, errorMessages)))); } return false; } - resourceDefinitions = ast.FindAll( - n => + resourceDefinitions = ast.FindAll(n => + { + var typeAst = n as TypeDefinitionAst; + if (typeAst != null) { - var typeAst = n as TypeDefinitionAst; - if (typeAst != null) + for (int i = 0; i < typeAst.Attributes.Count; i++) { - for (int i = 0; i < typeAst.Attributes.Count; i++) - { - var a = typeAst.Attributes[i]; - if (a.TypeName.GetReflectionAttributeType() == typeof(DscResourceAttribute)) return true; - } + var a = typeAst.Attributes[i]; + if (a.TypeName.GetReflectionAttributeType() == typeof(DscResourceAttribute)) return true; } + } - return false; - }, - false); + return false; + }, false); return true; } @@ -2200,7 +2658,7 @@ private static bool ImportKeywordsFromScriptFile(string fileName, PSModuleInfo m } var result = false; - var parser = new Microsoft.PowerShell.DesiredStateConfiguration.CimDSCParser(MyClassCallback); + var parser = new CimDSCParser(MyClassCallback); const WildcardOptions wildcardOptions = WildcardOptions.IgnoreCase | WildcardOptions.CultureInvariant; IEnumerable patternList = SessionStateUtilities.CreateWildcardsFromStrings(module._declaredDscResourceExports, wildcardOptions); @@ -2218,7 +2676,7 @@ private static bool ImportKeywordsFromScriptFile(string fileName, PSModuleInfo m bool skip = true; foreach (var toImport in resourcesToImport) { - if (WildcardPattern.Get(toImport, WildcardOptions.IgnoreCase).IsMatch(resourceDefnAst.Name)) + if ((WildcardPattern.Get(toImport, WildcardOptions.IgnoreCase)).IsMatch(resourceDefnAst.Name)) { skip = false; break; @@ -2255,7 +2713,7 @@ private static bool ImportKeywordsFromScriptFile(string fileName, PSModuleInfo m return result; } - private static readonly Dictionary s_mapPrimitiveDotNetTypeToMof = new Dictionary() + private static readonly Dictionary s_mapPrimitiveDotNetTypeToMof = new() { { typeof(sbyte), "sint8" }, { typeof(byte) , "uint8"}, @@ -2503,7 +2961,7 @@ private static string MapAttributesToMof(string[] enumNames, IEnumerable if (validateSet != null) { bool valueMapComma = false; - StringBuilder sbValues = new StringBuilder(", Values{"); + StringBuilder sbValues = new(", Values{"); sb.AppendFormat(CultureInfo.InvariantCulture, "{0}ValueMap{{", needComma ? ", " : string.Empty); needComma = true; @@ -2657,14 +3115,14 @@ private static void ProcessMembers(Type type, StringBuilder sb, List emb // TODO - validate type and name bool isArrayType; string embeddedInstanceType; - string mofType = MapTypeToMofType(memberType, member.Name, className, out isArrayType, out embeddedInstanceType, embeddedInstanceTypes); + string mofType = MapTypeToMofType(memberType, member.Name, className, out isArrayType, out embeddedInstanceType, + embeddedInstanceTypes); string arrayAffix = isArrayType ? "[]" : string.Empty; var enumNames = memberType.IsEnum ? Enum.GetNames(memberType) : null; - sb.AppendFormat( - CultureInfo.InvariantCulture, + sb.AppendFormat(CultureInfo.InvariantCulture, " {0}{1} {2}{3};\n", MapAttributesToMof(enumNames, member.GetCustomAttributes(true), embeddedInstanceType), mofType, @@ -2673,12 +3131,11 @@ private static void ProcessMembers(Type type, StringBuilder sb, List emb } } - private static bool ImportKeywordsFromAssembly( - PSModuleInfo module, - ICollection resourcesToImport, - ICollection resourcesFound, - Dictionary functionsToDefine, - Assembly assembly) + private static bool ImportKeywordsFromAssembly(PSModuleInfo module, + ICollection resourcesToImport, + ICollection resourcesFound, + Dictionary functionsToDefine, + Assembly assembly) { bool result = false; @@ -2694,7 +3151,7 @@ private static bool ImportKeywordsFromAssembly( foreach (var toImport in resourcesToImport) { - if (WildcardPattern.Get(toImport, WildcardOptions.IgnoreCase).IsMatch(r.Name)) + if ((WildcardPattern.Get(toImport, WildcardOptions.IgnoreCase)).IsMatch(r.Name)) { skip = false; break; @@ -2711,13 +3168,8 @@ private static bool ImportKeywordsFromAssembly( return result; } - private static void ProcessMofForDynamicKeywords( - PSModuleInfo module, - ICollection resourcesFound, - Dictionary functionsToDefine, - Microsoft.PowerShell.DesiredStateConfiguration.CimDSCParser parser, - string mof, - DSCResourceRunAsCredential runAsBehavior) + private static void ProcessMofForDynamicKeywords(PSModuleInfo module, ICollection resourcesFound, + Dictionary functionsToDefine, CimDSCParser parser, string mof, DSCResourceRunAsCredential runAsBehavior) { foreach (var c in parser.ParseSchemaMofFileBuffer(mof)) { @@ -2818,8 +3270,10 @@ public static bool ImportCimKeywordsFromModule(PSModuleInfo module, string resou } else if (Directory.Exists(dscResourcesPath)) { + // // Cannot find the schema file, then resourceName may be a friendly name, // try to search all DscResources' schemas under DscResources folder + // try { var dscResourceDirectories = Directory.GetDirectories(dscResourcesPath); @@ -2833,7 +3287,9 @@ public static bool ImportCimKeywordsFromModule(PSModuleInfo module, string resou var classes = GetCachedClassByFileName(tempSchemaFilepath) ?? ImportClasses(tempSchemaFilepath, new Tuple(module.Name, module.Version), errors); if (classes != null) { + // // search if class's friendly name is the given resourceName + // foreach (var c in classes) { var alias = GetFriendlyName(c); @@ -2850,7 +3306,9 @@ public static bool ImportCimKeywordsFromModule(PSModuleInfo module, string resou } catch (Exception) { + // // silent in case of exception + // } } @@ -2913,8 +3371,7 @@ public static bool ImportScriptKeywordsFromModule(PSModuleInfo module, string re // Parsing the file is all that needs to be done to add the keywords // BUGBUG - need to fix up how the module gets set. // BUGBUG - should fail somehow if errors is not empty - Token[] tokens; - ParseError[] errors; + Token[] tokens; ParseError[] errors; s_currentImportingScriptFiles.Add(schemaFilePath); Parser.ParseFile(schemaFilePath, out tokens, out errors); s_currentImportingScriptFiles.Remove(schemaFilePath); @@ -3220,7 +3677,7 @@ public static string GetDSCResourceUsageString(DynamicKeyword keyword) private static StringBuilder FormatCimPropertyType(DynamicKeywordProperty prop, bool isOptionalProperty) { string cimTypeName = prop.TypeConstraint; - StringBuilder formattedTypeString = new StringBuilder(); + StringBuilder formattedTypeString = new(); if (string.Equals(cimTypeName, "MSFT_Credential", StringComparison.OrdinalIgnoreCase)) { @@ -3268,8 +3725,7 @@ private static ScriptBlock CimKeywordImplementationFunction get { // The scriptblock cache will handle mutual exclusion - return s_cimKeywordImplementationFunction ?? - (s_cimKeywordImplementationFunction = ScriptBlock.Create(CimKeywordImplementationFunctionText)); + return s_cimKeywordImplementationFunction ??= ScriptBlock.Create(CimKeywordImplementationFunctionText); } } diff --git a/src/System.Management.Automation/DscSupport/MofCimDSCParser.cs b/src/System.Management.Automation/DscSupport/MofCimDSCParser.cs deleted file mode 100755 index 055f5f07d46..00000000000 --- a/src/System.Management.Automation/DscSupport/MofCimDSCParser.cs +++ /dev/null @@ -1,480 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -using System; -using System.Collections; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; -using System.Globalization; -using System.IO; -using System.Linq; -using System.Management.Automation; -using System.Management.Automation.Language; -using System.Reflection; -using System.Runtime.InteropServices; -using System.Security; -using System.Text; - -using Microsoft.Management.Infrastructure; -using Microsoft.Management.Infrastructure.Generic; -using Microsoft.Management.Infrastructure.Serialization; -using Microsoft.PowerShell.Commands; - -namespace Microsoft.PowerShell.DesiredStateConfiguration.Internal -{ - /// - /// - [SuppressMessage("Microsoft.MSInternal", "CA903:InternalNamespaceShouldNotContainPublicTypes", - Justification = "Needed Internal use only")] - public static class DscRemoteOperationsClass - { - /// - /// Convert Cim Instance representing Resource desired state to Powershell Class Object. - /// - public static object ConvertCimInstanceToObject(Type targetType, CimInstance instance, string moduleName) - { - var className = instance.CimClass.CimSystemProperties.ClassName; - object targetObject = null; - string errorMessage; - - using (System.Management.Automation.PowerShell powerShell = System.Management.Automation.PowerShell.Create(RunspaceMode.CurrentRunspace)) - { - string script = "param($targetType,$moduleName) & (Microsoft.PowerShell.Core\\Get-Module $moduleName) { New-Object $targetType } "; - - powerShell.AddScript(script); - powerShell.AddArgument(targetType); - powerShell.AddArgument(moduleName); - - Collection psExecutionResult = powerShell.Invoke(); - if (psExecutionResult.Count == 1) - { - targetObject = psExecutionResult[0].BaseObject; - } - else - { - Exception innerException = null; - if (powerShell.Streams.Error != null && powerShell.Streams.Error.Count > 0) - { - innerException = powerShell.Streams.Error[0].Exception; - } - - errorMessage = string.Format(CultureInfo.CurrentCulture, ParserStrings.InstantiatePSClassObjectFailed, className); - var invalidOperationException = new InvalidOperationException(errorMessage, innerException); - throw invalidOperationException; - } - } - - foreach (var property in instance.CimInstanceProperties) - { - if (property.Value != null) - { - MemberInfo[] memberInfo = targetType.GetMember(property.Name, BindingFlags.Public | BindingFlags.Instance); - - // verify property exists in corresponding class type - if (memberInfo == null || memberInfo.Length > 1 || !(memberInfo[0] is PropertyInfo || memberInfo[0] is FieldInfo)) - { - errorMessage = string.Format(CultureInfo.CurrentCulture, ParserStrings.PropertyNotDeclaredInPSClass, new object[] { property.Name, className }); - var invalidOperationException = new InvalidOperationException(errorMessage); - throw invalidOperationException; - } - - var member = memberInfo[0]; - var memberType = (member is FieldInfo) - ? ((FieldInfo)member).FieldType - : ((PropertyInfo)member).PropertyType; - - object targetValue = null; - switch (property.CimType) - { - case Microsoft.Management.Infrastructure.CimType.Instance: - { - var cimPropertyInstance = property.Value as CimInstance; - if (cimPropertyInstance != null && - cimPropertyInstance.CimClass != null && - cimPropertyInstance.CimClass.CimSystemProperties != null && - string.Equals( - cimPropertyInstance.CimClass.CimSystemProperties.ClassName, - "MSFT_Credential", StringComparison.OrdinalIgnoreCase)) - { - targetValue = ConvertCimInstancePsCredential(moduleName, cimPropertyInstance); - } - else - { - targetValue = ConvertCimInstanceToObject(memberType, cimPropertyInstance, moduleName); - } - - if (targetValue == null) - { - return null; - } - } - - break; - case Microsoft.Management.Infrastructure.CimType.InstanceArray: - { - if (memberType == typeof(Hashtable)) - { - targetValue = ConvertCimInstanceHashtable(moduleName, (CimInstance[])property.Value); - } - else - { - var instanceArray = (CimInstance[])property.Value; - if (!memberType.IsArray) - { - errorMessage = string.Format(CultureInfo.CurrentCulture, ParserStrings.ExpectArrayTypeOfPropertyInPSClass, new object[] { property.Name, className }); - var invalidOperationException = new InvalidOperationException(errorMessage); - throw invalidOperationException; - } - - var elementType = memberType.GetElementType(); - var targetArray = Array.CreateInstance(elementType, instanceArray.Length); - for (int i = 0; i < instanceArray.Length; i++) - { - var obj = ConvertCimInstanceToObject(elementType, instanceArray[i], moduleName); - if (obj == null) - { - return null; - } - - targetArray.SetValue(obj, i); - } - - targetValue = targetArray; - } - } - - break; - default: - targetValue = LanguagePrimitives.ConvertTo(property.Value, memberType, CultureInfo.InvariantCulture); - break; - } - - if (targetValue == null) - { - errorMessage = string.Format(CultureInfo.CurrentCulture, ParserStrings.ConvertCimPropertyToObjectPropertyFailed, new object[] { property.Name, className }); - var invalidOperationException = new InvalidOperationException(errorMessage); - throw invalidOperationException; - } - - if (member is FieldInfo) - { - ((FieldInfo)member).SetValue(targetObject, targetValue); - } - - if (member is PropertyInfo) - { - ((PropertyInfo)member).SetValue(targetObject, targetValue); - } - } - } - - return targetObject; - } - - /// - /// Convert hashtable from Ciminstance to hashtable primitive type. - /// - /// - /// - /// - private static object ConvertCimInstanceHashtable(string providerName, CimInstance[] arrayInstance) - { - var result = new Hashtable(); - string errorMessage; - - try - { - foreach (var keyValuePair in arrayInstance) - { - var key = keyValuePair.CimInstanceProperties["Key"]; - var value = keyValuePair.CimInstanceProperties["Value"]; - - if (key == null || value == null) - { - errorMessage = string.Format(CultureInfo.CurrentCulture, ParserStrings.InvalidHashtable, providerName); - var invalidOperationException = new InvalidOperationException(errorMessage); - throw invalidOperationException; - } - - result.Add(LanguagePrimitives.ConvertTo(key.Value), LanguagePrimitives.ConvertTo(value.Value)); - } - } - catch (Exception exception) - { - errorMessage = string.Format(CultureInfo.CurrentCulture, ParserStrings.InvalidHashtable, providerName); - var invalidOperationException = new InvalidOperationException(errorMessage, exception); - throw invalidOperationException; - } - - return result; - } - /// - /// Convert CIM instance to PS Credential. - /// - /// - /// - /// - private static object ConvertCimInstancePsCredential(string providerName, CimInstance propertyInstance) - { - string errorMessage; - string userName; - string plainPassWord; - - try - { - userName = propertyInstance.CimInstanceProperties["UserName"].Value as string; - if (string.IsNullOrEmpty(userName)) - { - errorMessage = string.Format(CultureInfo.CurrentCulture, ParserStrings.InvalidUserName, providerName); - var invalidOperationException = new InvalidOperationException(errorMessage); - throw invalidOperationException; - } - } - catch (CimException exception) - { - errorMessage = string.Format(CultureInfo.CurrentCulture, ParserStrings.InvalidUserName, providerName); - var invalidOperationException = new InvalidOperationException(errorMessage, exception); - throw invalidOperationException; - } - - try - { - plainPassWord = propertyInstance.CimInstanceProperties["PassWord"].Value as string; - - // In future we might receive password in an encrypted format. Make sure we add - // the decryption login in this method. - if (string.IsNullOrEmpty(plainPassWord)) - { - errorMessage = string.Format(CultureInfo.CurrentCulture, ParserStrings.InvalidPassword, providerName); - var invalidOperationException = new InvalidOperationException(errorMessage); - throw invalidOperationException; - } - } - catch (CimException exception) - { - errorMessage = string.Format(CultureInfo.CurrentCulture, ParserStrings.InvalidPassword, providerName); - var invalidOperationException = new InvalidOperationException(errorMessage, exception); - throw invalidOperationException; - } - - // Extract the password into a SecureString. - var password = new SecureString(); - foreach (char t in plainPassWord) - { - password.AppendChar(t); - } - - password.MakeReadOnly(); - return new PSCredential(userName, password); - } - } -} - -namespace Microsoft.PowerShell.DesiredStateConfiguration -{ - /// - /// To make it easier to specify -ConfigurationData parameter, we add an ArgumentTransformationAttribute here. - /// When the input data is of type string and is valid path to a file that can be converted to hashtable, we do - /// the conversion and return the converted value. Otherwise, we just return the input data. - /// - [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property | AttributeTargets.Parameter, AllowMultiple = false)] - public sealed class ArgumentToConfigurationDataTransformationAttribute : ArgumentTransformationAttribute - { - /// - /// Convert a file of ConfigurationData into a hashtable. - /// - /// - /// - /// - public override object Transform(EngineIntrinsics engineIntrinsics, object inputData) - { - var configDataPath = inputData as string; - if (string.IsNullOrEmpty(configDataPath)) - { - return inputData; - } - - if (engineIntrinsics == null) - { - throw PSTraceSource.NewArgumentNullException(nameof(engineIntrinsics)); - } - - return PsUtils.EvaluatePowerShellDataFileAsModuleManifest( - "ConfigurationData", - configDataPath, - engineIntrinsics.SessionState.Internal.ExecutionContext, - skipPathValidation: false); - } - } - - /// - /// - /// Represents a communication channel to a CIM server. - /// - /// - /// This is the main entry point of the Microsoft.Management.Infrastructure API. - /// All CIM operations are represented as methods of this class. - /// - /// - internal class CimDSCParser - { - private CimMofDeserializer _deserializer; - - private CimMofDeserializer.OnClassNeeded _onClassNeeded; - - /// - /// - internal CimDSCParser(CimMofDeserializer.OnClassNeeded onClassNeeded) - { - _deserializer = CimMofDeserializer.Create(); - _onClassNeeded = onClassNeeded; - } - - /// - /// - internal CimDSCParser(CimMofDeserializer.OnClassNeeded onClassNeeded, Microsoft.Management.Infrastructure.Serialization.MofDeserializerSchemaValidationOption validationOptions) - { - _deserializer = CimMofDeserializer.Create(); - _deserializer.SchemaValidationOption = validationOptions; - _onClassNeeded = onClassNeeded; - } - - /// - /// - /// - /// - [SuppressMessage("Microsoft.Design", "CA1045:DoNotPassTypesByReference", MessageId = "3#", Justification = "Have to return 2 things. Wrapping those 2 things in a class will result in a more, not less complexity")] - internal List ParseInstanceMof(string filePath) - { - uint offset = 0; - var buffer = GetFileContent(filePath); - try - { - var result = new List(_deserializer.DeserializeInstances(buffer, ref offset, _onClassNeeded, null)); - return result; - } - catch (CimException exception) - { - PSInvalidOperationException e = PSTraceSource.NewInvalidOperationException( - exception, ParserStrings.CimDeserializationError, filePath); - - e.SetErrorId("CimDeserializationError"); - throw e; - } - } - - /// - /// Read file content to byte array. - /// - /// - /// - internal static byte[] GetFileContent(string fullFilePath) - { - if (string.IsNullOrEmpty(fullFilePath)) - { - throw PSTraceSource.NewArgumentNullException(nameof(fullFilePath)); - } - - if (!File.Exists(fullFilePath)) - { - var errorMessage = string.Format(CultureInfo.CurrentCulture, ParserStrings.FileNotFound, fullFilePath); - throw PSTraceSource.NewArgumentException(nameof(fullFilePath), errorMessage); - } - - using (FileStream fs = File.OpenRead(fullFilePath)) - { - var bytes = new byte[fs.Length]; - fs.Read(bytes, 0, Convert.ToInt32(fs.Length)); - return bytes; - } - } - - internal List ParseSchemaMofFileBuffer(string mof) - { - uint offset = 0; -#if UNIX - // OMI only supports UTF-8 without BOM - var encoding = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false); -#else - // This is what we traditionally use with Windows - // DSC asked to keep it UTF-32 for Windows - var encoding = new UnicodeEncoding(); -#endif - - var buffer = encoding.GetBytes(mof); - - var result = new List(_deserializer.DeserializeClasses(buffer, ref offset, null, null, null, _onClassNeeded, null)); - return result; - } - - /// - /// - /// - /// - [SuppressMessage("Microsoft.Design", "CA1045:DoNotPassTypesByReference", MessageId = "3#", Justification = "Have to return 2 things. Wrapping those 2 things in a class will result in a more, not less complexity")] - internal List ParseSchemaMof(string filePath) - { - uint offset = 0; - var buffer = GetFileContent(filePath); - try - { - string fileNameDefiningClass = Path.GetFileNameWithoutExtension(filePath); - int dotIndex = fileNameDefiningClass.IndexOf('.'); - if (dotIndex != -1) - { - fileNameDefiningClass = fileNameDefiningClass.Substring(0, dotIndex); - } - - var result = new List(_deserializer.DeserializeClasses(buffer, ref offset, null, null, null, _onClassNeeded, null)); - foreach (CimClass c in result) - { - string superClassName = c.CimSuperClassName; - string className = c.CimSystemProperties.ClassName; - if ((superClassName != null) && (superClassName.Equals("OMI_BaseResource", StringComparison.OrdinalIgnoreCase))) - { - // Get the name of the file without schema.mof extension - if (!(className.Equals(fileNameDefiningClass, StringComparison.OrdinalIgnoreCase))) - { - PSInvalidOperationException e = PSTraceSource.NewInvalidOperationException( - ParserStrings.ClassNameNotSameAsDefiningFile, className, fileNameDefiningClass); - throw e; - } - } - } - - return result; - } - catch (CimException exception) - { - PSInvalidOperationException e = PSTraceSource.NewInvalidOperationException( - exception, ParserStrings.CimDeserializationError, filePath); - - e.SetErrorId("CimDeserializationError"); - throw e; - } - } - - /// - /// Make sure that the instance conforms to the the schema. - /// - /// - internal void ValidateInstanceText(string classText) - { - uint offset = 0; - byte[] bytes = null; - - if (Platform.IsLinux || Platform.IsMacOS) - { - bytes = System.Text.Encoding.UTF8.GetBytes(classText); - } - else - { - bytes = System.Text.Encoding.Unicode.GetBytes(classText); - } - - _deserializer.DeserializeInstances(bytes, ref offset, _onClassNeeded, null); - } - } -} From 9a98234b8129553b3609750b4cd9839c9c081106 Mon Sep 17 00:00:00 2001 From: anmenaga Date: Tue, 5 Jan 2021 15:11:02 -0800 Subject: [PATCH 44/64] Updated experimental feature name --- .../DscSupport/JsonDscClassCache.cs | 25 +++++++++++-------- .../ExperimentalFeature.cs | 4 +-- .../engine/parser/Parser.cs | 2 +- .../resources/ParserStrings.resx | 4 +-- 4 files changed, 19 insertions(+), 16 deletions(-) diff --git a/src/System.Management.Automation/DscSupport/JsonDscClassCache.cs b/src/System.Management.Automation/DscSupport/JsonDscClassCache.cs index 21ebecc2530..d84098638f3 100755 --- a/src/System.Management.Automation/DscSupport/JsonDscClassCache.cs +++ b/src/System.Management.Automation/DscSupport/JsonDscClassCache.cs @@ -82,7 +82,10 @@ public static class DscClassCache private static readonly Regex reservedPropertiesRegex = new Regex("^(Require|Trigger|Notify|Before|After|Subscribe)$", RegexOptions.Compiled | RegexOptions.IgnoreCase); - private const string jsonSchemaSupportExperimentalFeatureName = "PSDscJsonSchemaSupport"; + /// + /// Experimental feature name for DSC v3 + /// + public const string DscV3ExperimentalFeatureName = "PSDscV3Support"; private static readonly PSTraceSource s_tracer = PSTraceSource.GetTracer("DSC", "DSC Class Cache"); @@ -345,9 +348,9 @@ public static string GetStringFromSecureString(SecureString value) /// public static void ClearCache() { - if (!ExperimentalFeature.IsEnabled(jsonSchemaSupportExperimentalFeatureName)) + if (!ExperimentalFeature.IsEnabled(DscV3ExperimentalFeatureName)) { - throw new InvalidOperationException(ParserStrings.PsDscJsonSchemaSupportDisabled); + throw new InvalidOperationException(ParserStrings.PsDscV3SupportDisabled); } s_tracer.WriteLine("DSC class: clearing the cache and associated keywords."); @@ -399,9 +402,9 @@ private static List> FindResourceInCach /// Class declaration from cache. public static PSObject GetGuestConfigCachedClass(string moduleName, string moduleVersion, string className, string resourceName) { - if (!ExperimentalFeature.IsEnabled(jsonSchemaSupportExperimentalFeatureName)) + if (!ExperimentalFeature.IsEnabled(DscV3ExperimentalFeatureName)) { - throw new InvalidOperationException(ParserStrings.PsDscJsonSchemaSupportDisabled); + throw new InvalidOperationException(ParserStrings.PsDscV3SupportDisabled); } var moduleQualifiedResourceName = GetModuleQualifiedResourceName(moduleName, moduleVersion, className, string.IsNullOrEmpty(resourceName) ? className : resourceName); @@ -451,9 +454,9 @@ private static string GetFriendlyName(dynamic cimClass) /// public static Collection GetKeywordsFromCachedClasses() { - if (!ExperimentalFeature.IsEnabled(jsonSchemaSupportExperimentalFeatureName)) + if (!ExperimentalFeature.IsEnabled(DscV3ExperimentalFeatureName)) { - throw new InvalidOperationException(ParserStrings.PsDscJsonSchemaSupportDisabled); + throw new InvalidOperationException(ParserStrings.PsDscV3SupportDisabled); } Collection keywords = new Collection(); @@ -775,9 +778,9 @@ public static void LoadDefaultCimKeywords(Collection errors, bool cac private static void LoadDefaultCimKeywords(Dictionary functionsToDefine, Collection errors, List modulePathList, bool cacheResourcesFromMultipleModuleVersions) { - if (!ExperimentalFeature.IsEnabled(jsonSchemaSupportExperimentalFeatureName)) + if (!ExperimentalFeature.IsEnabled(DscV3ExperimentalFeatureName)) { - Exception exception = new InvalidOperationException(ParserStrings.PsDscJsonSchemaSupportDisabled); + Exception exception = new InvalidOperationException(ParserStrings.PsDscV3SupportDisabled); errors.Add(exception); return; } @@ -1285,9 +1288,9 @@ private static void LoadPowerShellClassResourcesFromModule(PSModuleInfo primaryM /// The list of resources imported from this module. public static List ImportClassResourcesFromModule(PSModuleInfo moduleInfo, ICollection resourcesToImport, Dictionary functionsToDefine, Collection errors) { - if (!ExperimentalFeature.IsEnabled(jsonSchemaSupportExperimentalFeatureName)) + if (!ExperimentalFeature.IsEnabled(DscV3ExperimentalFeatureName)) { - throw new InvalidOperationException(ParserStrings.PsDscJsonSchemaSupportDisabled); + throw new InvalidOperationException(ParserStrings.PsDscV3SupportDisabled); } var resourcesImported = new List(); diff --git a/src/System.Management.Automation/engine/ExperimentalFeature/ExperimentalFeature.cs b/src/System.Management.Automation/engine/ExperimentalFeature/ExperimentalFeature.cs index 5b88c9296c5..181568e2e59 100644 --- a/src/System.Management.Automation/engine/ExperimentalFeature/ExperimentalFeature.cs +++ b/src/System.Management.Automation/engine/ExperimentalFeature/ExperimentalFeature.cs @@ -124,8 +124,8 @@ static ExperimentalFeature() name: "PSNotApplyErrorActionToStderr", description: "Don't have $ErrorActionPreference affect stderr output"), new ExperimentalFeature( - name: "PSDscJsonSchemaSupport", - description: "Support JSON-based DSC schema processing"), + name: "PSDscV3Support", + description: "Support cross-platform DSC v3"), new ExperimentalFeature( name: "PSSubsystemPluginModel", description: "A plugin model for registering and un-registering PowerShell subsystems"), diff --git a/src/System.Management.Automation/engine/parser/Parser.cs b/src/System.Management.Automation/engine/parser/Parser.cs index 7146bb1d0ca..9f376ef3b9d 100644 --- a/src/System.Management.Automation/engine/parser/Parser.cs +++ b/src/System.Management.Automation/engine/parser/Parser.cs @@ -2996,7 +2996,7 @@ private StatementAst ConfigurationStatementRule(IEnumerable custom { // Load the default CIM keywords Collection CIMKeywordErrors = new Collection(); - if (ExperimentalFeature.IsEnabled("PSDscJsonSchemaSupport")) + if (ExperimentalFeature.IsEnabled(Microsoft.PowerShell.DesiredStateConfiguration.Internal.Json.DscClassCache.DscV3ExperimentalFeatureName)) { // In addition to checking if experimental feature is enabled // also check if PSDesiredStateConfiguration is already loaded diff --git a/src/System.Management.Automation/resources/ParserStrings.resx b/src/System.Management.Automation/resources/ParserStrings.resx index 07e8dba0d1b..b4ad891bfc8 100644 --- a/src/System.Management.Automation/resources/ParserStrings.resx +++ b/src/System.Management.Automation/resources/ParserStrings.resx @@ -1455,8 +1455,8 @@ ModuleVersion : Version of module to import. If used, ModuleName must represent Unable to find DSC schema store at "{0}". Please ensure PS DSC for Linux is installed. - - PSDscJsonSchemaSupport experimental feature is disabled; use Enable-ExperimentalFeature or update powershell.config.json to enable this feature. + + PSDscV3Support experimental feature is disabled; use Enable-ExperimentalFeature or update powershell.config.json to enable this feature. {0} From c2e82e7388aa9776f69288e9ae7c0be24f23905b Mon Sep 17 00:00:00 2001 From: anmenaga Date: Tue, 5 Jan 2021 15:22:14 -0800 Subject: [PATCH 45/64] Style fixes --- .../DscSupport/JsonDscClassCache.cs | 17 ++++++++--------- .../engine/parser/Parser.cs | 2 +- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/src/System.Management.Automation/DscSupport/JsonDscClassCache.cs b/src/System.Management.Automation/DscSupport/JsonDscClassCache.cs index d84098638f3..af2e4e603cb 100755 --- a/src/System.Management.Automation/DscSupport/JsonDscClassCache.cs +++ b/src/System.Management.Automation/DscSupport/JsonDscClassCache.cs @@ -409,7 +409,7 @@ public static PSObject GetGuestConfigCachedClass(string moduleName, string modul var moduleQualifiedResourceName = GetModuleQualifiedResourceName(moduleName, moduleVersion, className, string.IsNullOrEmpty(resourceName) ? className : resourceName); DscClassCacheEntry classCacheEntry = null; - if(GuestConfigClassCache.TryGetValue(moduleQualifiedResourceName, out classCacheEntry)) + if (GuestConfigClassCache.TryGetValue(moduleQualifiedResourceName, out classCacheEntry)) { return classCacheEntry.CimClassInstance; } @@ -417,7 +417,7 @@ public static PSObject GetGuestConfigCachedClass(string moduleName, string modul { // if class was not found with current ResourceName then it may be a class with non-empty FriendlyName that caller does not know, so perform a broad search string partialClassPath = string.Join('\\', moduleName, moduleVersion, className, string.Empty); - foreach(string key in GuestConfigClassCache.Keys) + foreach (string key in GuestConfigClassCache.Keys) { if (key.StartsWith(partialClassPath)) { @@ -623,7 +623,7 @@ private static DynamicKeyword CreateKeywordFromCimClass(string moduleName, Versi var values = prop.Qualifiers?.Values; if (values != null) { - foreach(var val in values) + foreach (var val in values) { keyProp.Values.Add(val.ToString()); } @@ -635,7 +635,7 @@ private static DynamicKeyword CreateKeywordFromCimClass(string moduleName, Versi if (nativeValueMap != null) { valueMap = new List(); - foreach(var val in nativeValueMap) + foreach (var val in nativeValueMap) { valueMap.Add(val.ToString()); } @@ -1207,7 +1207,7 @@ internal static void LoadResourcesFromModuleInImportResourcePostParse(IScriptExt var resourcesFound = new List(); var exceptionList = new System.Collections.ObjectModel.Collection(); LoadPowerShellClassResourcesFromModule(moduleInfo, moduleInfo, resourcesToImport, resourcesFound, exceptionList, null, true, scriptExtent); - foreach(Exception ex in exceptionList) + foreach (Exception ex in exceptionList) { errorList.Add(new ParseError(scriptExtent, "ClassResourcesLoadingFailed", @@ -1336,7 +1336,7 @@ private static List ProcessEmbeddedInstanceTypes(List embedded private static void AddEmbeddedInstanceTypesToCaches(IEnumerable classes, PSModuleInfo module, DSCResourceRunAsCredential runAsBehavior) { - foreach(dynamic c in classes) + foreach (dynamic c in classes) { var className = c.ClassName; string alias = GetFriendlyName(c); @@ -1496,7 +1496,7 @@ private static List ProcessMembers(List embeddedInstanceTypes, var propertyObject = new PSObject(); propertyObject.Properties.Add(new PSNoteProperty(@"Name", member.Name)); - propertyObject.Properties.Add(new PSNoteProperty(@"CimType", mofType + (isArrayType ? "Array" : ""))); + propertyObject.Properties.Add(new PSNoteProperty(@"CimType", mofType + (isArrayType ? "Array" : string.Empty))); if (!string.IsNullOrEmpty(embeddedInstanceType)) { propertyObject.Properties.Add(new PSNoteProperty(@"ReferenceClassName", embeddedInstanceType)); @@ -2203,8 +2203,7 @@ private static ScriptBlock CimKeywordImplementationFunction get { // The scriptblock cache will handle mutual exclusion - return s_cimKeywordImplementationFunction ?? - (s_cimKeywordImplementationFunction = ScriptBlock.Create(CimKeywordImplementationFunctionText)); + return s_cimKeywordImplementationFunction ??= ScriptBlock.Create(CimKeywordImplementationFunctionText); } } diff --git a/src/System.Management.Automation/engine/parser/Parser.cs b/src/System.Management.Automation/engine/parser/Parser.cs index 9f376ef3b9d..086786cff7f 100644 --- a/src/System.Management.Automation/engine/parser/Parser.cs +++ b/src/System.Management.Automation/engine/parser/Parser.cs @@ -3007,7 +3007,7 @@ private StatementAst ConfigurationStatementRule(IEnumerable custom p.AddParameter("Name", "PSDesiredStateConfiguration"); bool prev3IsLoaded = false; - foreach(PSModuleInfo moduleInfo in p.Invoke()) + foreach (PSModuleInfo moduleInfo in p.Invoke()) { if (moduleInfo.Version.Major < 3) { From a6ef5b6342a553d64f67044e5febc7c162a169fe Mon Sep 17 00:00:00 2001 From: Andrew Date: Tue, 5 Jan 2021 20:39:09 -0800 Subject: [PATCH 46/64] Fixed tab completion tests --- test/powershell/Host/TabCompletion/TabCompletion.Tests.ps1 | 1 + 1 file changed, 1 insertion(+) diff --git a/test/powershell/Host/TabCompletion/TabCompletion.Tests.ps1 b/test/powershell/Host/TabCompletion/TabCompletion.Tests.ps1 index c42f30e510a..0444c9ec2f1 100644 --- a/test/powershell/Host/TabCompletion/TabCompletion.Tests.ps1 +++ b/test/powershell/Host/TabCompletion/TabCompletion.Tests.ps1 @@ -1201,6 +1201,7 @@ dir -Recurse ` @{ inputStr = "configuration foo { File ab { Attributes =('Archive', "; expected = "'Hidden'" } @{ inputStr = "configuration foo { File ab { Attributes =('Archive', 'Hi"; expected = "Hidden" } ) + Import-Module -Name PSDesiredStateConfiguration -MaximumVersion 2.0.7 } It "Input '' should successfully complete" -TestCases $testCases -Skip:(!$IsWindows) { From 5a227312be53c19422852428df5851d92495aed9 Mon Sep 17 00:00:00 2001 From: Andrew Date: Wed, 6 Jan 2021 13:43:31 -0800 Subject: [PATCH 47/64] Fixed typo in CompletionAnalysis --- .../engine/CommandCompletion/CompletionAnalysis.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/System.Management.Automation/engine/CommandCompletion/CompletionAnalysis.cs b/src/System.Management.Automation/engine/CommandCompletion/CompletionAnalysis.cs index 92ca8e25eb4..0d10c4aa79e 100644 --- a/src/System.Management.Automation/engine/CommandCompletion/CompletionAnalysis.cs +++ b/src/System.Management.Automation/engine/CommandCompletion/CompletionAnalysis.cs @@ -1724,11 +1724,11 @@ private List GetResultForIdentifierInConfiguration( string usageString = string.Empty; if (Microsoft.PowerShell.DesiredStateConfiguration.Internal.Json.DscClassCache.NewApiIsUsed) { - Microsoft.PowerShell.DesiredStateConfiguration.Internal.Json.DscClassCache.GetDSCResourceUsageString(keyword); + usageString = Microsoft.PowerShell.DesiredStateConfiguration.Internal.Json.DscClassCache.GetDSCResourceUsageString(keyword); } else { - Microsoft.PowerShell.DesiredStateConfiguration.Internal.DscClassCache.GetDSCResourceUsageString(keyword); + usageString = Microsoft.PowerShell.DesiredStateConfiguration.Internal.DscClassCache.GetDSCResourceUsageString(keyword); } if (results == null) { From c1d935f25bfa7674db0588355550b87c9981ff7a Mon Sep 17 00:00:00 2001 From: Andrew Date: Mon, 11 Jan 2021 13:29:01 -0800 Subject: [PATCH 48/64] Updated exp feature --- build.psm1 | 10 ++++++++- .../DscSupport/JsonDscClassCache.cs | 22 +++++++++---------- .../ExperimentalFeature.cs | 4 ++-- .../engine/parser/Parser.cs | 2 +- .../resources/ParserStrings.resx | 4 ++-- .../TabCompletion/TabCompletion.Tests.ps1 | 1 - 6 files changed, 25 insertions(+), 18 deletions(-) diff --git a/build.psm1 b/build.psm1 index ce9ff123b1b..111487fdc45 100644 --- a/build.psm1 +++ b/build.psm1 @@ -587,7 +587,15 @@ Fix steps: $json = & $publishPath\pwsh -noprofile -command { $expFeatures = [System.Collections.Generic.List[string]]::new() - Get-ExperimentalFeature | ForEach-Object { $expFeatures.Add($_.Name) } + Get-ExperimentalFeature | ForEach-Object { + # Special case for DSC code in PS; + # this exp feature requires new DSC module that is not inbox, + # so we don't want default DSC use case be broken + if ($_.Name -ne "PS7DscSupport") + { + $expFeatures.Add($_.Name) + } + } # Make sure ExperimentalFeatures from modules in PSHome are added # https://github.com/PowerShell/PowerShell/issues/10550 diff --git a/src/System.Management.Automation/DscSupport/JsonDscClassCache.cs b/src/System.Management.Automation/DscSupport/JsonDscClassCache.cs index af2e4e603cb..be9af1745dc 100755 --- a/src/System.Management.Automation/DscSupport/JsonDscClassCache.cs +++ b/src/System.Management.Automation/DscSupport/JsonDscClassCache.cs @@ -85,7 +85,7 @@ public static class DscClassCache /// /// Experimental feature name for DSC v3 /// - public const string DscV3ExperimentalFeatureName = "PSDscV3Support"; + public const string DscExperimentalFeatureName = "PS7DscSupport"; private static readonly PSTraceSource s_tracer = PSTraceSource.GetTracer("DSC", "DSC Class Cache"); @@ -348,9 +348,9 @@ public static string GetStringFromSecureString(SecureString value) /// public static void ClearCache() { - if (!ExperimentalFeature.IsEnabled(DscV3ExperimentalFeatureName)) + if (!ExperimentalFeature.IsEnabled(DscExperimentalFeatureName)) { - throw new InvalidOperationException(ParserStrings.PsDscV3SupportDisabled); + throw new InvalidOperationException(ParserStrings.PS7DscSupportDisabled); } s_tracer.WriteLine("DSC class: clearing the cache and associated keywords."); @@ -402,9 +402,9 @@ private static List> FindResourceInCach /// Class declaration from cache. public static PSObject GetGuestConfigCachedClass(string moduleName, string moduleVersion, string className, string resourceName) { - if (!ExperimentalFeature.IsEnabled(DscV3ExperimentalFeatureName)) + if (!ExperimentalFeature.IsEnabled(DscExperimentalFeatureName)) { - throw new InvalidOperationException(ParserStrings.PsDscV3SupportDisabled); + throw new InvalidOperationException(ParserStrings.PS7DscSupportDisabled); } var moduleQualifiedResourceName = GetModuleQualifiedResourceName(moduleName, moduleVersion, className, string.IsNullOrEmpty(resourceName) ? className : resourceName); @@ -454,9 +454,9 @@ private static string GetFriendlyName(dynamic cimClass) /// public static Collection GetKeywordsFromCachedClasses() { - if (!ExperimentalFeature.IsEnabled(DscV3ExperimentalFeatureName)) + if (!ExperimentalFeature.IsEnabled(DscExperimentalFeatureName)) { - throw new InvalidOperationException(ParserStrings.PsDscV3SupportDisabled); + throw new InvalidOperationException(ParserStrings.PS7DscSupportDisabled); } Collection keywords = new Collection(); @@ -778,9 +778,9 @@ public static void LoadDefaultCimKeywords(Collection errors, bool cac private static void LoadDefaultCimKeywords(Dictionary functionsToDefine, Collection errors, List modulePathList, bool cacheResourcesFromMultipleModuleVersions) { - if (!ExperimentalFeature.IsEnabled(DscV3ExperimentalFeatureName)) + if (!ExperimentalFeature.IsEnabled(DscExperimentalFeatureName)) { - Exception exception = new InvalidOperationException(ParserStrings.PsDscV3SupportDisabled); + Exception exception = new InvalidOperationException(ParserStrings.PS7DscSupportDisabled); errors.Add(exception); return; } @@ -1288,9 +1288,9 @@ private static void LoadPowerShellClassResourcesFromModule(PSModuleInfo primaryM /// The list of resources imported from this module. public static List ImportClassResourcesFromModule(PSModuleInfo moduleInfo, ICollection resourcesToImport, Dictionary functionsToDefine, Collection errors) { - if (!ExperimentalFeature.IsEnabled(DscV3ExperimentalFeatureName)) + if (!ExperimentalFeature.IsEnabled(DscExperimentalFeatureName)) { - throw new InvalidOperationException(ParserStrings.PsDscV3SupportDisabled); + throw new InvalidOperationException(ParserStrings.PS7DscSupportDisabled); } var resourcesImported = new List(); diff --git a/src/System.Management.Automation/engine/ExperimentalFeature/ExperimentalFeature.cs b/src/System.Management.Automation/engine/ExperimentalFeature/ExperimentalFeature.cs index 181568e2e59..665548dbbb4 100644 --- a/src/System.Management.Automation/engine/ExperimentalFeature/ExperimentalFeature.cs +++ b/src/System.Management.Automation/engine/ExperimentalFeature/ExperimentalFeature.cs @@ -124,8 +124,8 @@ static ExperimentalFeature() name: "PSNotApplyErrorActionToStderr", description: "Don't have $ErrorActionPreference affect stderr output"), new ExperimentalFeature( - name: "PSDscV3Support", - description: "Support cross-platform DSC v3"), + name: "PS7DscSupport", + description: "Support cross-platform DSC"), new ExperimentalFeature( name: "PSSubsystemPluginModel", description: "A plugin model for registering and un-registering PowerShell subsystems"), diff --git a/src/System.Management.Automation/engine/parser/Parser.cs b/src/System.Management.Automation/engine/parser/Parser.cs index 086786cff7f..f3a74f28703 100644 --- a/src/System.Management.Automation/engine/parser/Parser.cs +++ b/src/System.Management.Automation/engine/parser/Parser.cs @@ -2996,7 +2996,7 @@ private StatementAst ConfigurationStatementRule(IEnumerable custom { // Load the default CIM keywords Collection CIMKeywordErrors = new Collection(); - if (ExperimentalFeature.IsEnabled(Microsoft.PowerShell.DesiredStateConfiguration.Internal.Json.DscClassCache.DscV3ExperimentalFeatureName)) + if (ExperimentalFeature.IsEnabled(Microsoft.PowerShell.DesiredStateConfiguration.Internal.Json.DscClassCache.DscExperimentalFeatureName)) { // In addition to checking if experimental feature is enabled // also check if PSDesiredStateConfiguration is already loaded diff --git a/src/System.Management.Automation/resources/ParserStrings.resx b/src/System.Management.Automation/resources/ParserStrings.resx index b4ad891bfc8..e251a1ef433 100644 --- a/src/System.Management.Automation/resources/ParserStrings.resx +++ b/src/System.Management.Automation/resources/ParserStrings.resx @@ -1455,8 +1455,8 @@ ModuleVersion : Version of module to import. If used, ModuleName must represent Unable to find DSC schema store at "{0}". Please ensure PS DSC for Linux is installed. - - PSDscV3Support experimental feature is disabled; use Enable-ExperimentalFeature or update powershell.config.json to enable this feature. + + PS7DscSupport experimental feature is disabled; use Enable-ExperimentalFeature or update powershell.config.json to enable this feature. {0} diff --git a/test/powershell/Host/TabCompletion/TabCompletion.Tests.ps1 b/test/powershell/Host/TabCompletion/TabCompletion.Tests.ps1 index 0444c9ec2f1..c42f30e510a 100644 --- a/test/powershell/Host/TabCompletion/TabCompletion.Tests.ps1 +++ b/test/powershell/Host/TabCompletion/TabCompletion.Tests.ps1 @@ -1201,7 +1201,6 @@ dir -Recurse ` @{ inputStr = "configuration foo { File ab { Attributes =('Archive', "; expected = "'Hidden'" } @{ inputStr = "configuration foo { File ab { Attributes =('Archive', 'Hi"; expected = "Hidden" } ) - Import-Module -Name PSDesiredStateConfiguration -MaximumVersion 2.0.7 } It "Input '' should successfully complete" -TestCases $testCases -Skip:(!$IsWindows) { From 21f0cdff953b1acb35a16c6b3a3b140afa73ba9e Mon Sep 17 00:00:00 2001 From: Andrew Date: Mon, 11 Jan 2021 14:46:30 -0800 Subject: [PATCH 49/64] Updated Get-ExperimentalFeature.Tests.ps1 --- .../Get-ExperimentalFeature.Tests.ps1 | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/test/powershell/engine/ExperimentalFeature/Get-ExperimentalFeature.Tests.ps1 b/test/powershell/engine/ExperimentalFeature/Get-ExperimentalFeature.Tests.ps1 index bcc358bcccd..c11002fbedb 100644 --- a/test/powershell/engine/ExperimentalFeature/Get-ExperimentalFeature.Tests.ps1 +++ b/test/powershell/engine/ExperimentalFeature/Get-ExperimentalFeature.Tests.ps1 @@ -174,7 +174,14 @@ Describe "Default enablement of Experimental Features" -Tags CI { (Join-Path -Path $PSHOME -ChildPath 'powershell.config.json') | Should -Exist foreach ($expFeature in Get-ExperimentalFeature) { - $expFeature.Enabled | Should -BeEnabled -Name $expFeature.Name + if ($expFeature.Name -ne "PS7DscSupport") + { + $expFeature.Enabled | Should -BeEnabled -Name $expFeature.Name + } + else + { + $expFeature.Enabled | Should -Not -BeEnabled -Name $expFeature.Name + } } } } From cd5c3be08a9693467c5ad0505ea35df8164fcc3a Mon Sep 17 00:00:00 2001 From: Andrew Date: Mon, 11 Jan 2021 14:57:48 -0800 Subject: [PATCH 50/64] Updated error message when PSDesiredStateConfiguration v3 module is missing --- src/System.Management.Automation/resources/ParserStrings.resx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/System.Management.Automation/resources/ParserStrings.resx b/src/System.Management.Automation/resources/ParserStrings.resx index e251a1ef433..dd9ce1b8c55 100644 --- a/src/System.Management.Automation/resources/ParserStrings.resx +++ b/src/System.Management.Automation/resources/ParserStrings.resx @@ -1453,7 +1453,7 @@ ModuleVersion : Version of module to import. If used, ModuleName must represent Conflict in using PsDscRunAsCredential for Resource {0} because it already specifies PsDscRunAsCredential value. We can only use one PsDscRunAsCredential for the composite resource. - Unable to find DSC schema store at "{0}". Please ensure PS DSC for Linux is installed. + Unable to find DSC schema store at "{0}". Please ensure PSDesiredStateConfiguration v3 module is installed. PS7DscSupport experimental feature is disabled; use Enable-ExperimentalFeature or update powershell.config.json to enable this feature. From ab4dca1887ea379b81194dea2e3c9bbe933663e4 Mon Sep 17 00:00:00 2001 From: Andrew Date: Mon, 11 Jan 2021 20:13:18 -0800 Subject: [PATCH 51/64] CodeFactor 1 --- .../DscSupport/CimDSCParser.cs | 6 +- .../DscSupport/JsonDscClassCache.cs | 214 +++++++++--------- .../CommandCompletion/CompletionAnalysis.cs | 1 + 3 files changed, 110 insertions(+), 111 deletions(-) diff --git a/src/System.Management.Automation/DscSupport/CimDSCParser.cs b/src/System.Management.Automation/DscSupport/CimDSCParser.cs index 71091cfaaf9..e112d912040 100644 --- a/src/System.Management.Automation/DscSupport/CimDSCParser.cs +++ b/src/System.Management.Automation/DscSupport/CimDSCParser.cs @@ -41,9 +41,9 @@ public static object ConvertCimInstanceToObject(Type targetType, CimInstance ins using (System.Management.Automation.PowerShell powerShell = System.Management.Automation.PowerShell.Create(RunspaceMode.CurrentRunspace)) { - const string script = "param($targetType,$moduleName) & (Microsoft.PowerShell.Core\\Get-Module $moduleName) { New-Object $targetType } "; + const string Script = "param($targetType,$moduleName) & (Microsoft.PowerShell.Core\\Get-Module $moduleName) { New-Object $targetType } "; - powerShell.AddScript(script); + powerShell.AddScript(Script); powerShell.AddArgument(targetType); powerShell.AddArgument(moduleName); @@ -951,7 +951,7 @@ private static CimClass MyClassCallback(string serverName, string namespaceName, /// /// Path to CIM MOF schema file for reading /// - /// List of classes from MOF schema file + /// List of classes from MOF schema file. public static List ReadCimSchemaMof(string mofPath) { var parser = new Microsoft.PowerShell.DesiredStateConfiguration.CimDSCParser(MyClassCallback); diff --git a/src/System.Management.Automation/DscSupport/JsonDscClassCache.cs b/src/System.Management.Automation/DscSupport/JsonDscClassCache.cs index be9af1745dc..41cdd1ed9f9 100755 --- a/src/System.Management.Automation/DscSupport/JsonDscClassCache.cs +++ b/src/System.Management.Automation/DscSupport/JsonDscClassCache.cs @@ -83,7 +83,7 @@ public static class DscClassCache private static readonly Regex reservedPropertiesRegex = new Regex("^(Require|Trigger|Notify|Before|After|Subscribe)$", RegexOptions.Compiled | RegexOptions.IgnoreCase); /// - /// Experimental feature name for DSC v3 + /// Experimental feature name for DSC v3. /// public const string DscExperimentalFeatureName = "PS7DscSupport"; @@ -203,8 +203,9 @@ public static void Initialize(Collection errors, List moduleP var moduleInfos = ModuleCmdletBase.GetModuleIfAvailable(new Microsoft.PowerShell.Commands.ModuleSpecification() { Name = "PSDesiredStateConfiguration", + // Version in the next line is actually MinimumVersion - Version = new Version(3,0,0) + Version = new Version(3, 0, 0) }); if (moduleInfos.Count > 0) @@ -439,9 +440,7 @@ public static void ClearGuestConfigClassCache() private static bool IsMagicProperty(string propertyName) { - return System.Text.RegularExpressions.Regex.Match(propertyName, - "^(ResourceId|SourceInfo|ModuleName|ModuleVersion|ConfigurationName)$", - System.Text.RegularExpressions.RegexOptions.IgnoreCase).Success; + return System.Text.RegularExpressions.Regex.Match(propertyName, "^(ResourceId|SourceInfo|ModuleName|ModuleVersion|ConfigurationName)$", System.Text.RegularExpressions.RegexOptions.IgnoreCase).Success; } private static string GetFriendlyName(dynamic cimClass) @@ -528,9 +527,7 @@ private static DynamicKeyword CreateKeywordFromCimClass(string moduleName, Versi string alias = GetFriendlyName(cimClass); var keywordString = string.IsNullOrEmpty(alias) ? resourceName : alias; - // // Skip all of the base, meta, registration and other classes that are not intended to be used directly by a script author - // if (System.Text.RegularExpressions.Regex.Match(keywordString, "^OMI_Base|^OMI_.*Registration", System.Text.RegularExpressions.RegexOptions.IgnoreCase).Success) { return null; @@ -669,7 +666,9 @@ private static DynamicKeyword CreateKeywordFromCimClass(string moduleName, Versi { s_tracer.WriteLine( "DSC CreateDynamicKeywordFromClass: the count of values for qualifier 'Values' and 'ValueMap' doesn't match. count of 'Values': {0}, count of 'ValueMap': {1}. Skip the keyword '{2}'.", - keyProp.Values.Count, valueMap.Count, keyword.Keyword); + keyProp.Values.Count, + valueMap.Count, + keyword.Keyword); return null; } @@ -682,7 +681,8 @@ private static DynamicKeyword CreateKeywordFromCimClass(string moduleName, Versi { s_tracer.WriteLine( "DSC CreateDynamicKeywordFromClass: same string value '{0}' appears more than once in qualifier 'Values'. Skip the keyword '{1}'.", - key, keyword.Keyword); + key, + keyword.Keyword); return null; } @@ -716,10 +716,12 @@ private static void UpdateKnownRestriction(DynamicKeyword keyword) const int ConfigurationModeFrequencyMax = 44640; if ( - string.Equals(keyword.ResourceName, "MSFT_DSCMetaConfigurationV2", + string.Equals(keyword.ResourceName, + "MSFT_DSCMetaConfigurationV2", StringComparison.OrdinalIgnoreCase) || - string.Equals(keyword.ResourceName, "MSFT_DSCMetaConfiguration", + string.Equals(keyword.ResourceName, + "MSFT_DSCMetaConfiguration", StringComparison.OrdinalIgnoreCase)) { if (keyword.Properties["RefreshFrequencyMins"] != null) @@ -775,8 +777,11 @@ public static void LoadDefaultCimKeywords(Collection errors, bool cac /// Collection of any errors encountered while loading keywords. /// List of module path from where DSC PS modules will be loaded. /// Allow caching the resources from multiple versions of modules. - private static void LoadDefaultCimKeywords(Dictionary functionsToDefine, Collection errors, - List modulePathList, bool cacheResourcesFromMultipleModuleVersions) + private static void LoadDefaultCimKeywords( + Dictionary functionsToDefine, + Collection errors, + List modulePathList, + bool cacheResourcesFromMultipleModuleVersions) { if (!ExperimentalFeature.IsEnabled(DscExperimentalFeatureName)) { @@ -856,9 +861,7 @@ private static ParseError[] ImportResourcePostParse(DynamicKeywordStatementAst k var errorList = new List(); foreach (var bindingException in bindingResult.BindingExceptions.Values) { - errorList.Add(new ParseError(bindingException.CommandElement.Extent, - "ParameterBindingException", - bindingException.BindingException.Message)); + errorList.Add(new ParseError(bindingException.CommandElement.Extent, "ParameterBindingException", bindingException.BindingException.Message)); } ParameterBindingResult moduleNameBindingResult = null; @@ -919,9 +922,7 @@ private static ParseError[] ImportResourcePostParse(DynamicKeywordStatementAst k if (!IsConstantValueVisitor.IsConstant(resourceNameBindingResult.Value, out resourceName, true, true) || !LanguagePrimitives.TryConvertTo(resourceName, out resourceNames)) { - errorList.Add(new ParseError(resourceNameBindingResult.Value.Extent, - "RequiresInvalidStringArgument", - string.Format(CultureInfo.CurrentCulture, ParserStrings.RequiresInvalidStringArgument, nameParam))); + errorList.Add(new ParseError(resourceNameBindingResult.Value.Extent, "RequiresInvalidStringArgument", string.Format(CultureInfo.CurrentCulture, ParserStrings.RequiresInvalidStringArgument, nameParam))); } } @@ -931,9 +932,7 @@ private static ParseError[] ImportResourcePostParse(DynamicKeywordStatementAst k object moduleVer = null; if (!IsConstantValueVisitor.IsConstant(moduleVersionBindingResult.Value, out moduleVer, true, true)) { - errorList.Add(new ParseError(moduleVersionBindingResult.Value.Extent, - "RequiresArgumentMustBeConstant", - ParserStrings.RequiresArgumentMustBeConstant)); + errorList.Add(new ParseError(moduleVersionBindingResult.Value.Extent, "RequiresArgumentMustBeConstant", ParserStrings.RequiresArgumentMustBeConstant)); } if (moduleVer is double) @@ -946,9 +945,7 @@ private static ParseError[] ImportResourcePostParse(DynamicKeywordStatementAst k if (!LanguagePrimitives.TryConvertTo(moduleVer, out moduleVersion)) { - errorList.Add(new ParseError(moduleVersionBindingResult.Value.Extent, - "RequiresVersionInvalid", - ParserStrings.RequiresVersionInvalid)); + errorList.Add(new ParseError(moduleVersionBindingResult.Value.Extent, "RequiresVersionInvalid", ParserStrings.RequiresVersionInvalid)); } } @@ -958,9 +955,7 @@ private static ParseError[] ImportResourcePostParse(DynamicKeywordStatementAst k object moduleName = null; if (!IsConstantValueVisitor.IsConstant(moduleNameBindingResult.Value, out moduleName, true, true)) { - errorList.Add(new ParseError(moduleNameBindingResult.Value.Extent, - "RequiresArgumentMustBeConstant", - ParserStrings.RequiresArgumentMustBeConstant)); + errorList.Add(new ParseError(moduleNameBindingResult.Value.Extent, "RequiresArgumentMustBeConstant", ParserStrings.RequiresArgumentMustBeConstant)); } if (LanguagePrimitives.TryConvertTo(moduleName, out moduleSpecifications)) @@ -968,25 +963,19 @@ private static ParseError[] ImportResourcePostParse(DynamicKeywordStatementAst k // if resourceNames are specified then we can not specify multiple modules name if (moduleSpecifications != null && moduleSpecifications.Length > 1 && resourceNames != null) { - errorList.Add(new ParseError(moduleNameBindingResult.Value.Extent, - "ImportDscResourceMultipleModulesNotSupportedWithName", - string.Format(CultureInfo.CurrentCulture, ParserStrings.ImportDscResourceMultipleModulesNotSupportedWithName))); + errorList.Add(new ParseError(moduleNameBindingResult.Value.Extent, "ImportDscResourceMultipleModulesNotSupportedWithName", string.Format(CultureInfo.CurrentCulture, ParserStrings.ImportDscResourceMultipleModulesNotSupportedWithName))); } // if moduleversion is specified then we can not specify multiple modules name if (moduleSpecifications != null && moduleSpecifications.Length > 1 && moduleVersion != null) { - errorList.Add(new ParseError(moduleNameBindingResult.Value.Extent, - "ImportDscResourceMultipleModulesNotSupportedWithVersion", - string.Format(CultureInfo.CurrentCulture, ParserStrings.ImportDscResourceNeedParams))); + errorList.Add(new ParseError(moduleNameBindingResult.Value.Extent, "ImportDscResourceMultipleModulesNotSupportedWithVersion", string.Format(CultureInfo.CurrentCulture, ParserStrings.ImportDscResourceNeedParams))); } // if moduleversion is specified then we can not specify another version in modulespecification object of ModuleName if (moduleSpecifications != null && (moduleSpecifications[0].Version != null || moduleSpecifications[0].MaximumVersion != null) && moduleVersion != null) { - errorList.Add(new ParseError(moduleNameBindingResult.Value.Extent, - "ImportDscResourceMultipleModuleVersionsNotSupported", - string.Format(CultureInfo.CurrentCulture, ParserStrings.ImportDscResourceNeedParams))); + errorList.Add(new ParseError(moduleNameBindingResult.Value.Extent, "ImportDscResourceMultipleModuleVersionsNotSupported", string.Format(CultureInfo.CurrentCulture, ParserStrings.ImportDscResourceNeedParams))); } // If moduleVersion is specified we have only one module Name in valid scenario @@ -998,9 +987,7 @@ private static ParseError[] ImportResourcePostParse(DynamicKeywordStatementAst k } else { - errorList.Add(new ParseError(moduleNameBindingResult.Value.Extent, - "RequiresInvalidStringArgument", - string.Format(CultureInfo.CurrentCulture, ParserStrings.RequiresInvalidStringArgument, moduleNameParam))); + errorList.Add(new ParseError(moduleNameBindingResult.Value.Extent, "RequiresInvalidStringArgument", string.Format(CultureInfo.CurrentCulture, ParserStrings.RequiresInvalidStringArgument, moduleNameParam))); } } @@ -1028,9 +1015,7 @@ private static ParseError[] ImportResourceCheckSemantics(DynamicKeywordStatement errorList = new List(); } - errorList.Add(new ParseError(kwAst.Extent, - "ImportDscResourceInsideNode", - string.Format(CultureInfo.CurrentCulture, ParserStrings.ImportDscResourceInsideNode))); + errorList.Add(new ParseError(kwAst.Extent, "ImportDscResourceInsideNode", string.Format(CultureInfo.CurrentCulture, ParserStrings.ImportDscResourceInsideNode))); break; } @@ -1102,10 +1087,15 @@ private static ParseError[] CheckMandatoryPropertiesPresent(DynamicKeywordStatem int i = 0; foreach (string name in mandatoryPropertiesNames) { - errors[i] = new ParseError(extent, "MissingValueForMandatoryProperty", - string.Format(CultureInfo.CurrentCulture, ParserStrings.MissingValueForMandatoryProperty, - kwAst.Keyword.Keyword, kwAst.Keyword.Properties.First( - p => StringComparer.OrdinalIgnoreCase.Equals(p.Value.Name, name)).Value.TypeConstraint, name)); + errors[i] = new ParseError( + extent, + "MissingValueForMandatoryProperty", + string.Format( + CultureInfo.CurrentCulture, + ParserStrings.MissingValueForMandatoryProperty, + kwAst.Keyword.Keyword, + kwAst.Keyword.Properties.First(p => StringComparer.OrdinalIgnoreCase.Equals(p.Value.Name, name)).Value.TypeConstraint, + name)); i++; } @@ -1122,10 +1112,11 @@ private static ParseError[] CheckMandatoryPropertiesPresent(DynamicKeywordStatem /// Module information, can be null. /// Name of the resource to be loaded from module. /// List of errors reported by the method. - internal static void LoadResourcesFromModuleInImportResourcePostParse(IScriptExtent scriptExtent, - ModuleSpecification[] moduleSpecifications, - string[] resourceNames, - List errorList) + internal static void LoadResourcesFromModuleInImportResourcePostParse( + IScriptExtent scriptExtent, + ModuleSpecification[] moduleSpecifications, + string[] resourceNames, + List errorList) { // get all required modules var modules = new Collection(); @@ -1160,11 +1151,13 @@ internal static void LoadResourcesFromModuleInImportResourcePostParse(IScriptExt { if (moduleInfos.Count > 1) { - errorList.Add(new ParseError(scriptExtent, - "MultipleModuleEntriesFoundDuringParse", - string.Format(CultureInfo.CurrentCulture, - ParserStrings.MultipleModuleEntriesFoundDuringParse, - moduleToImport.Name))); + errorList.Add( + new ParseError( + scriptExtent, + "MultipleModuleEntriesFoundDuringParse", + string.Format(CultureInfo.CurrentCulture, + ParserStrings.MultipleModuleEntriesFoundDuringParse, + moduleToImport.Name))); } else { @@ -1172,8 +1165,7 @@ internal static void LoadResourcesFromModuleInImportResourcePostParse(IScriptExt ? moduleToImport.Name : string.Format(CultureInfo.CurrentCulture, "<{0}, {1}>", moduleToImport.Name, moduleToImport.Version); - errorList.Add(new ParseError(scriptExtent, "ModuleNotFoundDuringParse", - string.Format(CultureInfo.CurrentCulture, ParserStrings.ModuleNotFoundDuringParse, moduleString))); + errorList.Add(new ParseError(scriptExtent, "ModuleNotFoundDuringParse", string.Format(CultureInfo.CurrentCulture, ParserStrings.ModuleNotFoundDuringParse, moduleString))); } return; @@ -1239,7 +1231,11 @@ internal static void LoadResourcesFromModuleInImportResourcePostParse(IScriptExt } } - private static void LoadPowerShellClassResourcesFromModule(PSModuleInfo primaryModuleInfo, PSModuleInfo moduleInfo, ICollection resourcesToImport, ICollection resourcesFound, + private static void LoadPowerShellClassResourcesFromModule( + PSModuleInfo primaryModuleInfo, + PSModuleInfo moduleInfo, + ICollection resourcesToImport, + ICollection resourcesFound, Collection errorList, Dictionary functionsToDefine = null, bool recurse = true, @@ -1280,11 +1276,12 @@ private static void LoadPowerShellClassResourcesFromModule(PSModuleInfo primaryM } /// + /// Import class resources from module. /// - /// - /// - /// - /// + /// Module information. + /// Collection of resources to import. + /// Functions to define. + /// List of errors to return. /// The list of resources imported from this module. public static List ImportClassResourcesFromModule(PSModuleInfo moduleInfo, ICollection resourcesToImport, Dictionary functionsToDefine, Collection errors) { @@ -1478,9 +1475,7 @@ private static List ProcessMembers(List embeddedInstanceTypes, if (memberType != null) { - mofType = MapTypeToMofType(memberType, member.Name, className, out isArrayType, - out embeddedInstanceType, - embeddedInstanceTypes); + mofType = MapTypeToMofType(memberType, member.Name, className, out isArrayType, out embeddedInstanceType, embeddedInstanceTypes); if (memberType.IsEnum) { enumNames = Enum.GetNames(memberType); @@ -1489,9 +1484,7 @@ private static List ProcessMembers(List embeddedInstanceTypes, else { // PropertyType can't be null, we used typeof(object) above in that case so we don't get here. - mofType = MapTypeNameToMofType(property.PropertyType.TypeName, member.Name, className, - out isArrayType, - out embeddedInstanceType, embeddedInstanceTypes, ref enumNames); + mofType = MapTypeNameToMofType(property.PropertyType.TypeName, member.Name, className, out isArrayType, out embeddedInstanceType, embeddedInstanceTypes, ref enumNames); } var propertyObject = new PSObject(); @@ -1655,7 +1648,7 @@ private static bool LoadPowerShellClassResourcesFromModule(string fileName, PSMo bool skip = true; foreach (var toImport in resourcesToImport) { - if ((WildcardPattern.Get(toImport, WildcardOptions.IgnoreCase)).IsMatch(resourceDefnAst.Name)) + if (WildcardPattern.Get(toImport, WildcardOptions.IgnoreCase).IsMatch(resourceDefnAst.Name)) { skip = false; break; @@ -1814,8 +1807,13 @@ internal static string MapTypeToMofType(Type type, string memberName, string cla } } - private static void ProcessJsonForDynamicKeywords(PSModuleInfo module, ICollection resourcesFound, - Dictionary functionsToDefine, PSObject[] classes, DSCResourceRunAsCredential runAsBehavior, Collection errors) + private static void ProcessJsonForDynamicKeywords( + PSModuleInfo module, + ICollection resourcesFound, + Dictionary functionsToDefine, + PSObject[] classes, + DSCResourceRunAsCredential runAsBehavior, + Collection errors) { foreach (dynamic c in classes) { @@ -1867,7 +1865,7 @@ private static void ProcessJsonForDynamicKeywords(PSModuleInfo module, ICollecti /// /// The malformed resource. /// The referencing resource instance. - /// + /// Generated error record. public static ErrorRecord GetBadlyFormedRequiredResourceIdErrorRecord(string badDependsOnReference, string definingResource) { PSInvalidOperationException e = PSTraceSource.NewInvalidOperationException( @@ -1882,7 +1880,7 @@ public static ErrorRecord GetBadlyFormedRequiredResourceIdErrorRecord(string bad /// /// The malformed resource. /// The referencing resource instance. - /// + /// Generated error record. public static ErrorRecord GetBadlyFormedExclusiveResourceIdErrorRecord(string badExclusiveResourcereference, string definingResource) { PSInvalidOperationException e = PSTraceSource.NewInvalidOperationException( @@ -1895,8 +1893,8 @@ public static ErrorRecord GetBadlyFormedExclusiveResourceIdErrorRecord(string ba /// /// If a partial configuration is in 'Pull' Mode, it needs a configuration source. /// - /// - /// + /// Resource id. + /// Generated error record. public static ErrorRecord GetPullModeNeedConfigurationSource(string resourceId) { PSInvalidOperationException e = PSTraceSource.NewInvalidOperationException( @@ -1909,8 +1907,8 @@ public static ErrorRecord GetPullModeNeedConfigurationSource(string resourceId) /// /// Refresh Mode can not be Disabled for the Partial Configurations. /// - /// - /// + /// Resource id. + /// Generated error record. public static ErrorRecord DisabledRefreshModeNotValidForPartialConfig(string resourceId) { PSInvalidOperationException e = PSTraceSource.NewInvalidOperationException( @@ -1938,8 +1936,8 @@ public static ErrorRecord DuplicateResourceIdInNodeStatementErrorRecord(string d /// /// Returns an error record to use in the case of a configuration name is invalid. /// - /// - /// + /// Configuration name. + /// Generated error record. public static ErrorRecord InvalidConfigurationNameErrorRecord(string configurationName) { PSInvalidOperationException e = PSTraceSource.NewInvalidOperationException( @@ -1952,11 +1950,11 @@ public static ErrorRecord InvalidConfigurationNameErrorRecord(string configurati /// /// Returns an error record to use in the case of the given value for a property is invalid. /// - /// - /// - /// - /// - /// + /// Property name. + /// Property value. + /// Keyword name. + /// Valid property values. + /// Generated error record. public static ErrorRecord InvalidValueForPropertyErrorRecord(string propertyName, string value, string keywordName, string validValues) { PSInvalidOperationException e = PSTraceSource.NewInvalidOperationException( @@ -1969,9 +1967,9 @@ public static ErrorRecord InvalidValueForPropertyErrorRecord(string propertyName /// /// Returns an error record to use in case the given property is not valid LocalConfigurationManager property. /// - /// - /// - /// + /// Property name. + /// Valid properties. + /// Generated error record. public static ErrorRecord InvalidLocalConfigurationManagerPropertyErrorRecord(string propertyName, string validProperties) { PSInvalidOperationException e = PSTraceSource.NewInvalidOperationException( @@ -1984,11 +1982,11 @@ public static ErrorRecord InvalidLocalConfigurationManagerPropertyErrorRecord(st /// /// Returns an error record to use in the case of the given value for a property is not supported. /// - /// - /// - /// - /// - /// + /// Property name. + /// Property value. + /// Keyword name. + /// Valid property values. + /// Generated error record. public static ErrorRecord UnsupportedValueForPropertyErrorRecord(string propertyName, string value, string keywordName, string validValues) { PSInvalidOperationException e = PSTraceSource.NewInvalidOperationException( @@ -2001,10 +1999,10 @@ public static ErrorRecord UnsupportedValueForPropertyErrorRecord(string property /// /// Returns an error record to use in the case of no value is provided for a mandatory property. /// - /// - /// - /// - /// + /// Keyword name. + /// Type name. + /// Property name. + /// Generated error record. public static ErrorRecord MissingValueForMandatoryPropertyErrorRecord(string keywordName, string typeName, string propertyName) { PSInvalidOperationException e = PSTraceSource.NewInvalidOperationException( @@ -2017,7 +2015,7 @@ public static ErrorRecord MissingValueForMandatoryPropertyErrorRecord(string key /// /// Returns an error record to use in the case of more than one values are provided for DebugMode property. /// - /// + /// Generated error record. public static ErrorRecord DebugModeShouldHaveOneValue() { PSInvalidOperationException e = PSTraceSource.NewInvalidOperationException( @@ -2030,12 +2028,12 @@ public static ErrorRecord DebugModeShouldHaveOneValue() /// /// Return an error to indicate a value is out of range for a dynamic keyword property. /// - /// - /// - /// - /// - /// - /// + /// Rroperty name. + /// Resource name. + /// Provided value. + /// Valid range lower bound. + /// Valid range upper bound. + /// Generated error record. public static ErrorRecord ValueNotInRangeErrorRecord(string property, string name, int providedValue, int lower, int upper) { PSInvalidOperationException e = PSTraceSource.NewInvalidOperationException( @@ -2049,7 +2047,7 @@ public static ErrorRecord ValueNotInRangeErrorRecord(string property, string nam /// Returns an error record to use when composite resource and its resource instances both has PsDscRunAsCredentials value. /// /// ResourceId of resource. - /// + /// Generated error record. public static ErrorRecord PsDscRunAsCredentialMergeErrorForCompositeResources(string resourceId) { PSInvalidOperationException e = PSTraceSource.NewInvalidOperationException( @@ -2149,9 +2147,9 @@ public static string GetDSCResourceUsageString(DynamicKeyword keyword) /// /// Format the type name of a CIM property in a presentable way. /// - /// - /// - /// + /// Dynamic keyword property + /// If this is optional property or not + /// CIM property type string. private static StringBuilder FormatCimPropertyType(DynamicKeywordProperty prop, bool isOptionalProperty) { string cimTypeName = prop.TypeConstraint; diff --git a/src/System.Management.Automation/engine/CommandCompletion/CompletionAnalysis.cs b/src/System.Management.Automation/engine/CommandCompletion/CompletionAnalysis.cs index 0d10c4aa79e..2722e248fec 100644 --- a/src/System.Management.Automation/engine/CommandCompletion/CompletionAnalysis.cs +++ b/src/System.Management.Automation/engine/CommandCompletion/CompletionAnalysis.cs @@ -1730,6 +1730,7 @@ private List GetResultForIdentifierInConfiguration( { usageString = Microsoft.PowerShell.DesiredStateConfiguration.Internal.DscClassCache.GetDSCResourceUsageString(keyword); } + if (results == null) { results = new List(); From 9901fa2e3a4a92a9f13ed586854fc37b21156966 Mon Sep 17 00:00:00 2001 From: Andrew Date: Mon, 11 Jan 2021 20:36:58 -0800 Subject: [PATCH 52/64] CodeFactor 2 --- .../DscSupport/CimDSCParser.cs | 2 +- .../DscSupport/JsonDscClassCache.cs | 162 +++++++----------- 2 files changed, 65 insertions(+), 99 deletions(-) diff --git a/src/System.Management.Automation/DscSupport/CimDSCParser.cs b/src/System.Management.Automation/DscSupport/CimDSCParser.cs index e112d912040..aa4eb6f22fe 100644 --- a/src/System.Management.Automation/DscSupport/CimDSCParser.cs +++ b/src/System.Management.Automation/DscSupport/CimDSCParser.cs @@ -949,7 +949,7 @@ private static CimClass MyClassCallback(string serverName, string namespaceName, /// Reads CIM MOF schema file and returns classes defined in it; this is used in MOF->JSON and MOF->PSClass convertion tools. /// /// - /// Path to CIM MOF schema file for reading + /// Path to CIM MOF schema file for reading. /// /// List of classes from MOF schema file. public static List ReadCimSchemaMof(string mofPath) diff --git a/src/System.Management.Automation/DscSupport/JsonDscClassCache.cs b/src/System.Management.Automation/DscSupport/JsonDscClassCache.cs index 41cdd1ed9f9..5cb739a41b4 100755 --- a/src/System.Management.Automation/DscSupport/JsonDscClassCache.cs +++ b/src/System.Management.Automation/DscSupport/JsonDscClassCache.cs @@ -38,10 +38,10 @@ public DscClassCacheEntry() /// /// Initializes all values. /// - /// - /// - /// - /// + /// Run as credential value. + /// Resource is imported implicitly. + /// Class definition. + /// Path of module defining the class. public DscClassCacheEntry(DSCResourceRunAsCredential dscResourceRunAsCredential, bool isImportedImplicitly, PSObject cimClassInstance, string modulePath) { DscResRunAsCred = dscResourceRunAsCredential; @@ -51,7 +51,7 @@ public DscClassCacheEntry(DSCResourceRunAsCredential dscResourceRunAsCredential, } /// - /// Store the RunAs Credentials that this DSC resource will use. + /// Gets or sets the RunAs Credentials that this DSC resource will use. /// public DSCResourceRunAsCredential DscResRunAsCred { get; set; } @@ -360,26 +360,11 @@ public static void ClearCache() CacheResourcesFromMultipleModuleVersions = false; } - /// - /// Returns module qualified resource name in "Module\Version\Class" format. - /// - /// - /// - /// - /// - /// private static string GetModuleQualifiedResourceName(string moduleName, string moduleVersion, string className, string resourceName) { return string.Format(CultureInfo.InvariantCulture, "{0}\\{1}\\{2}\\{3}", moduleName, moduleVersion, className, resourceName); } - /// - /// Finds resources in the that which matches the specified class and module name. - /// - /// Module name. - /// Resource type name. - /// Resource friendly name. - /// List of found resources in the form of Dictionary{moduleQualifiedName, cimClass}, otherwise empty list. private static List> FindResourceInCache(string moduleName, string className, string resourceName) { return (from cacheEntry in ClassCache @@ -514,13 +499,6 @@ private static void CreateAndRegisterKeywordFromCimClass(string moduleName, Vers } } - /// - /// A method to generate a keyword from a CIM class object. This is used for DSC. - /// - /// - /// - /// - /// To specify RunAs behavior of the class. private static DynamicKeyword CreateKeywordFromCimClass(string moduleName, Version moduleVersion, dynamic cimClass, DSCResourceRunAsCredential runAsBehavior) { var resourceName = cimClass.ClassName; @@ -716,11 +694,13 @@ private static void UpdateKnownRestriction(DynamicKeyword keyword) const int ConfigurationModeFrequencyMax = 44640; if ( - string.Equals(keyword.ResourceName, + string.Equals( + keyword.ResourceName, "MSFT_DSCMetaConfigurationV2", StringComparison.OrdinalIgnoreCase) || - string.Equals(keyword.ResourceName, + string.Equals( + keyword.ResourceName, "MSFT_DSCMetaConfiguration", StringComparison.OrdinalIgnoreCase)) { @@ -843,18 +823,18 @@ private static void LoadDefaultCimKeywords( // This function is called after parsing the Import-DscResource keyword and it's arguments, but before parsing // anything else. - private static ParseError[] ImportResourcePostParse(DynamicKeywordStatementAst kwAst) + private static ParseError[] ImportResourcePostParse(DynamicKeywordStatementAst ast) { - var elements = Ast.CopyElements(kwAst.CommandElements); + var elements = Ast.CopyElements(ast.CommandElements); Diagnostics.Assert(elements[0] is StringConstantExpressionAst && ((StringConstantExpressionAst)elements[0]).Value.Equals("Import-DscResource", StringComparison.OrdinalIgnoreCase), "Incorrect ast for expected keyword"); - var commandAst = new CommandAst(kwAst.Extent, elements, TokenKind.Unknown, null); + var commandAst = new CommandAst(ast.Extent, elements, TokenKind.Unknown, null); - const string nameParam = "Name"; - const string moduleNameParam = "ModuleName"; - const string moduleVersionParam = "ModuleVersion"; + const string NameParam = "Name"; + const string ModuleNameParam = "ModuleName"; + const string ModuleVersionParam = "ModuleVersion"; StaticBindingResult bindingResult = StaticParameterBinder.BindCommand(commandAst, false); @@ -879,15 +859,15 @@ private static ParseError[] ImportResourcePostParse(DynamicKeywordStatementAst k continue; } - if (nameParam.StartsWith(boundParameterName, StringComparison.OrdinalIgnoreCase)) + if (NameParam.StartsWith(boundParameterName, StringComparison.OrdinalIgnoreCase)) { resourceNameBindingResult = parameterBindingResult; } - else if (moduleNameParam.StartsWith(boundParameterName, StringComparison.OrdinalIgnoreCase)) + else if (ModuleNameParam.StartsWith(boundParameterName, StringComparison.OrdinalIgnoreCase)) { moduleNameBindingResult = parameterBindingResult; } - else if (moduleVersionParam.StartsWith(boundParameterName, StringComparison.OrdinalIgnoreCase)) + else if (ModuleVersionParam.StartsWith(boundParameterName, StringComparison.OrdinalIgnoreCase)) { moduleVersionBindingResult = parameterBindingResult; } @@ -899,7 +879,7 @@ private static ParseError[] ImportResourcePostParse(DynamicKeywordStatementAst k if (errorList.Count == 0 && moduleNameBindingResult == null && resourceNameBindingResult == null) { - errorList.Add(new ParseError(kwAst.Extent, "ImportDscResourceNeedParams", string.Format(CultureInfo.CurrentCulture, ParserStrings.ImportDscResourceNeedParams))); + errorList.Add(new ParseError(ast.Extent, "ImportDscResourceNeedParams", string.Format(CultureInfo.CurrentCulture, ParserStrings.ImportDscResourceNeedParams))); } // Check here if Version is specified but modulename is not specified @@ -911,7 +891,7 @@ private static ParseError[] ImportResourcePostParse(DynamicKeywordStatementAst k // once we have different error messages for 2 scenarios we can remove this check if (resourceNameBindingResult != null) { - errorList.Add(new ParseError(kwAst.Extent, "ImportDscResourceNeedModuleNameWithModuleVersion", string.Format(CultureInfo.CurrentCulture, ParserStrings.ImportDscResourceNeedParams))); + errorList.Add(new ParseError(ast.Extent, "ImportDscResourceNeedModuleNameWithModuleVersion", string.Format(CultureInfo.CurrentCulture, ParserStrings.ImportDscResourceNeedParams))); } } @@ -922,7 +902,7 @@ private static ParseError[] ImportResourcePostParse(DynamicKeywordStatementAst k if (!IsConstantValueVisitor.IsConstant(resourceNameBindingResult.Value, out resourceName, true, true) || !LanguagePrimitives.TryConvertTo(resourceName, out resourceNames)) { - errorList.Add(new ParseError(resourceNameBindingResult.Value.Extent, "RequiresInvalidStringArgument", string.Format(CultureInfo.CurrentCulture, ParserStrings.RequiresInvalidStringArgument, nameParam))); + errorList.Add(new ParseError(resourceNameBindingResult.Value.Extent, "RequiresInvalidStringArgument", string.Format(CultureInfo.CurrentCulture, ParserStrings.RequiresInvalidStringArgument, NameParam))); } } @@ -987,25 +967,25 @@ private static ParseError[] ImportResourcePostParse(DynamicKeywordStatementAst k } else { - errorList.Add(new ParseError(moduleNameBindingResult.Value.Extent, "RequiresInvalidStringArgument", string.Format(CultureInfo.CurrentCulture, ParserStrings.RequiresInvalidStringArgument, moduleNameParam))); + errorList.Add(new ParseError(moduleNameBindingResult.Value.Extent, "RequiresInvalidStringArgument", string.Format(CultureInfo.CurrentCulture, ParserStrings.RequiresInvalidStringArgument, ModuleNameParam))); } } if (errorList.Count == 0) { // No errors, try to load the resources - LoadResourcesFromModuleInImportResourcePostParse(kwAst.Extent, moduleSpecifications, resourceNames, errorList); + LoadResourcesFromModuleInImportResourcePostParse(ast.Extent, moduleSpecifications, resourceNames, errorList); } return errorList.ToArray(); } // This function performs semantic checks for Import-DscResource - private static ParseError[] ImportResourceCheckSemantics(DynamicKeywordStatementAst kwAst) + private static ParseError[] ImportResourceCheckSemantics(DynamicKeywordStatementAst ast) { List errorList = null; - var keywordAst = Ast.GetAncestorAst(kwAst.Parent); + var keywordAst = Ast.GetAncestorAst(ast.Parent); while (keywordAst != null) { if (keywordAst.Keyword.Keyword.Equals("Node")) @@ -1015,7 +995,7 @@ private static ParseError[] ImportResourceCheckSemantics(DynamicKeywordStatement errorList = new List(); } - errorList.Add(new ParseError(kwAst.Extent, "ImportDscResourceInsideNode", string.Format(CultureInfo.CurrentCulture, ParserStrings.ImportDscResourceInsideNode))); + errorList.Add(new ParseError(ast.Extent, "ImportDscResourceInsideNode", string.Format(CultureInfo.CurrentCulture, ParserStrings.ImportDscResourceInsideNode))); break; } @@ -1033,10 +1013,10 @@ private static ParseError[] ImportResourceCheckSemantics(DynamicKeywordStatement } // This function performs semantic checks for all DSC Resources keywords. - private static ParseError[] CheckMandatoryPropertiesPresent(DynamicKeywordStatementAst kwAst) + private static ParseError[] CheckMandatoryPropertiesPresent(DynamicKeywordStatementAst ast) { HashSet mandatoryPropertiesNames = new HashSet(StringComparer.OrdinalIgnoreCase); - foreach (var pair in kwAst.Keyword.Properties) + foreach (var pair in ast.Keyword.Properties) { if (pair.Value.Mandatory) { @@ -1048,9 +1028,9 @@ private static ParseError[] CheckMandatoryPropertiesPresent(DynamicKeywordStatem // every resource must have at least one Key property. HashtableAst hashtableAst = null; - foreach (var ast in kwAst.CommandElements) + foreach (var commandElementsAst in ast.CommandElements) { - hashtableAst = ast as HashtableAst; + hashtableAst = commandElementsAst as HashtableAst; if (hashtableAst != null) { break; @@ -1083,7 +1063,7 @@ private static ParseError[] CheckMandatoryPropertiesPresent(DynamicKeywordStatem if (mandatoryPropertiesNames.Count > 0) { ParseError[] errors = new ParseError[mandatoryPropertiesNames.Count]; - var extent = kwAst.CommandElements[0].Extent; + var extent = ast.CommandElements[0].Extent; int i = 0; foreach (string name in mandatoryPropertiesNames) { @@ -1093,8 +1073,8 @@ private static ParseError[] CheckMandatoryPropertiesPresent(DynamicKeywordStatem string.Format( CultureInfo.CurrentCulture, ParserStrings.MissingValueForMandatoryProperty, - kwAst.Keyword.Keyword, - kwAst.Keyword.Properties.First(p => StringComparer.OrdinalIgnoreCase.Equals(p.Value.Name, name)).Value.TypeConstraint, + ast.Keyword.Keyword, + ast.Keyword.Properties.First(p => StringComparer.OrdinalIgnoreCase.Equals(p.Value.Name, name)).Value.TypeConstraint, name)); i++; } @@ -1155,9 +1135,7 @@ internal static void LoadResourcesFromModuleInImportResourcePostParse( new ParseError( scriptExtent, "MultipleModuleEntriesFoundDuringParse", - string.Format(CultureInfo.CurrentCulture, - ParserStrings.MultipleModuleEntriesFoundDuringParse, - moduleToImport.Name))); + string.Format(CultureInfo.CurrentCulture, ParserStrings.MultipleModuleEntriesFoundDuringParse, moduleToImport.Name))); } else { @@ -1441,7 +1419,7 @@ private static PSObject[] GenerateJsonClassesForAst(TypeDefinitionAst typeAst, L result.Properties.Add(new PSNoteProperty("SuperClassName", cimSuperClassName)); result.Properties.Add(new PSNoteProperty("ClassProperties", _CimClassProperties)); - return new [] {result}; + return new[] { result }; } private static List ProcessMembers(List embeddedInstanceTypes, TypeDefinitionAst typeDefinitionAst, string className) @@ -1550,13 +1528,6 @@ private static List ProcessMembers(List embeddedInstanceTypes, return result; } - /// - /// - /// - /// - /// - /// - /// private static bool GetResourceDefinitionsFromModule(string fileName, out IEnumerable resourceDefinitions, Collection errorList, IScriptExtent extent) { resourceDefinitions = null; @@ -1594,34 +1565,26 @@ private static bool GetResourceDefinitionsFromModule(string fileName, out IEnume return false; } - resourceDefinitions = ast.FindAll(n => - { - var typeAst = n as TypeDefinitionAst; - if (typeAst != null) + resourceDefinitions = ast.FindAll( + n => { - for (int i = 0; i < typeAst.Attributes.Count; i++) + var typeAst = n as TypeDefinitionAst; + if (typeAst != null) { - var a = typeAst.Attributes[i]; - if (a.TypeName.GetReflectionAttributeType() == typeof(DscResourceAttribute)) return true; + for (int i = 0; i < typeAst.Attributes.Count; i++) + { + var a = typeAst.Attributes[i]; + if (a.TypeName.GetReflectionAttributeType() == typeof(DscResourceAttribute)) return true; + } } - } - return false; - }, false); + return false; + }, + false); return true; } - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// private static bool LoadPowerShellClassResourcesFromModule(string fileName, PSModuleInfo module, ICollection resourcesToImport, ICollection resourcesFound, Dictionary functionsToDefine, Collection errorList, IScriptExtent extent) { IEnumerable resourceDefinitions; @@ -1632,8 +1595,8 @@ private static bool LoadPowerShellClassResourcesFromModule(string fileName, PSMo var result = false; - const WildcardOptions wildcardOptions = WildcardOptions.IgnoreCase | WildcardOptions.CultureInvariant; - IEnumerable patternList = SessionStateUtilities.CreateWildcardsFromStrings(module._declaredDscResourceExports, wildcardOptions); + const WildcardOptions options = WildcardOptions.IgnoreCase | WildcardOptions.CultureInvariant; + IEnumerable patternList = SessionStateUtilities.CreateWildcardsFromStrings(module._declaredDscResourceExports, options); foreach (var r in resourceDefinitions) { @@ -1655,7 +1618,10 @@ private static bool LoadPowerShellClassResourcesFromModule(string fileName, PSMo } } - if (skip) continue; + if (skip) + { + continue; + } // Parse the Resource Attribute to see if RunAs behavior is specified for the resource. DSCResourceRunAsCredential runAsBehavior = DSCResourceRunAsCredential.Default; @@ -1688,16 +1654,16 @@ private static bool LoadPowerShellClassResourcesFromModule(string fileName, PSMo private static readonly Dictionary s_mapPrimitiveDotNetTypeToMof = new Dictionary() { { typeof(sbyte), "sint8" }, - { typeof(byte) , "uint8"}, - { typeof(short) , "sint16"}, - { typeof(ushort) , "uint16"}, - { typeof(int) , "sint32"}, - { typeof(uint) , "uint32"}, - { typeof(long) , "sint64"}, + { typeof(byte), "uint8" }, + { typeof(short), "sint16" }, + { typeof(ushort), "uint16" }, + { typeof(int), "sint32" }, + { typeof(uint), "uint32" }, + { typeof(long), "sint64" }, { typeof(ulong), "uint64" }, - { typeof(float) , "real32"}, - { typeof(double) , "real64"}, - { typeof(bool) , "boolean"}, + { typeof(float), "real32" }, + { typeof(double), "real64" }, + { typeof(bool), "boolean" }, { typeof(string), "string" }, { typeof(DateTime), "datetime" }, { typeof(PSCredential), "string" }, @@ -2147,8 +2113,8 @@ public static string GetDSCResourceUsageString(DynamicKeyword keyword) /// /// Format the type name of a CIM property in a presentable way. /// - /// Dynamic keyword property - /// If this is optional property or not + /// Dynamic keyword property. + /// If this is optional property or not. /// CIM property type string. private static StringBuilder FormatCimPropertyType(DynamicKeywordProperty prop, bool isOptionalProperty) { From 5a3cff12280e21bbe4cef94a650f96533dc669dd Mon Sep 17 00:00:00 2001 From: Andrew Date: Mon, 11 Jan 2021 21:03:53 -0800 Subject: [PATCH 53/64] CodeFactor 3 --- .../DscSupport/JsonDscClassCache.cs | 143 ++++++------------ 1 file changed, 48 insertions(+), 95 deletions(-) diff --git a/src/System.Management.Automation/DscSupport/JsonDscClassCache.cs b/src/System.Management.Automation/DscSupport/JsonDscClassCache.cs index 5cb739a41b4..b8970082192 100755 --- a/src/System.Management.Automation/DscSupport/JsonDscClassCache.cs +++ b/src/System.Management.Automation/DscSupport/JsonDscClassCache.cs @@ -28,7 +28,7 @@ namespace Microsoft.PowerShell.DesiredStateConfiguration.Internal.Json internal class DscClassCacheEntry { /// - /// Initializes variables with default values. + /// Initializes a new instance of the class. /// public DscClassCacheEntry() : this(DSCResourceRunAsCredential.Default, isImportedImplicitly: false, cimClassInstance: null, modulePath: string.Empty) @@ -56,8 +56,7 @@ public DscClassCacheEntry(DSCResourceRunAsCredential dscResourceRunAsCredential, public DSCResourceRunAsCredential DscResRunAsCred { get; set; } /// - /// If we have implicitly imported this resource, we will set this field to true. This will - /// only happen to InBox resources. + /// Gets or sets a value indicating if we have implicitly imported this resource. /// public bool IsImportedImplicitly { get; set; } @@ -67,12 +66,13 @@ public DscClassCacheEntry(DSCResourceRunAsCredential dscResourceRunAsCredential, public PSObject CimClassInstance { get; set; } /// - /// Path of the implementing module for this resource. + /// Gets or sets path of the implementing module for this resource. /// public string ModulePath { get; set; } } /// + /// DSC class cache for this runspace. /// [SuppressMessage("Microsoft.MSInternal", "CA903:InternalNamespaceShouldNotContainPublicTypes", Justification = "Needed Internal use only")] @@ -100,7 +100,7 @@ public static class DscClassCache new HashSet(StringComparer.OrdinalIgnoreCase) { "MSFT_BaseConfigurationProviderRegistration", "MSFT_CimConfigurationProviderRegistration", "MSFT_PSConfigurationProviderRegistration" }; /// - /// DSC class cache for this runspace. + /// Gets DSC class cache for this runspace. /// Cache stores the DSCRunAsBehavior, cim class and boolean to indicate if an Inbox resource has been implicitly imported. /// private static Dictionary ClassCache @@ -120,8 +120,7 @@ private static Dictionary ClassCache private static Dictionary t_classCache; /// - /// DSC class cache for GuestConfig. - /// It is similar to ClassCache, but maintains values between operations. + /// Gets DSC class cache for GuestConfig; it is similar to ClassCache, but maintains values between operations. /// private static Dictionary GuestConfigClassCache { @@ -235,11 +234,11 @@ public static void Initialize(Collection errors, List moduleP /// /// Import base classes from the given file. /// - /// Path to schema file - /// Module information - /// Error collection that will be shown to the user - /// Flag for implicitly imported resource - /// Class objects from schema file + /// Path to schema file. + /// Module information. + /// Error collection that will be shown to the user. + /// Flag for implicitly imported resource. + /// Class objects from schema file. public static IEnumerable ImportBaseClasses(string path, Tuple moduleInfo, Collection errors, bool importInBoxResourcesImplicitly) { if (string.IsNullOrEmpty(path)) @@ -381,10 +380,10 @@ private static List> FindResourceInCach /// /// Returns class declaration from GuestConfigClassCache. /// - /// Module name - /// Module version - /// Name of the class - /// Friendly name of the resource + /// Module name. + /// Module version. + /// Name of the class. + /// Friendly name of the resource. /// Class declaration from cache. public static PSObject GetGuestConfigCachedClass(string moduleName, string moduleVersion, string className, string resourceName) { @@ -436,6 +435,7 @@ private static string GetFriendlyName(dynamic cimClass) /// /// Method to get the cached classes in the form of DynamicKeyword. /// + /// Dynamic keyword collection. public static Collection GetKeywordsFromCachedClasses() { if (!ExperimentalFeature.IsEnabled(DscExperimentalFeatureName)) @@ -461,14 +461,6 @@ public static Collection GetKeywordsFromCachedClasses() return keywords; } - /// - /// A method to generate a keyword from a CIM class object and register it to DynamicKeyword table. - /// - /// - /// - /// - /// If true, don't define the keywords, just create the functions. - /// To Specify RunAsBehavior of the class. private static void CreateAndRegisterKeywordFromCimClass(string moduleName, Version moduleVersion, PSObject cimClass, Dictionary functionsToDefine, DSCResourceRunAsCredential runAsBehavior) { var keyword = CreateKeywordFromCimClass(moduleName, moduleVersion, cimClass, runAsBehavior); @@ -489,6 +481,7 @@ private static void CreateAndRegisterKeywordFromCimClass(string moduleName, Vers throw e; } } + // Add the dynamic keyword to the table DynamicKeyword.AddKeyword(keyword); @@ -678,13 +671,6 @@ private static DynamicKeyword CreateKeywordFromCimClass(string moduleName, Versi return keyword; } - /// - /// Update range restriction for meta configuration keywords - /// the restrictions are for - /// ConfigurationModeFrequency: 15-44640 - /// RefreshFrequency: 30-44640. - /// - /// private static void UpdateKnownRestriction(DynamicKeyword keyword) { const int RefreshFrequencyMin = 30; @@ -821,15 +807,10 @@ private static void LoadDefaultCimKeywords( } } - // This function is called after parsing the Import-DscResource keyword and it's arguments, but before parsing - // anything else. + // This function is called after parsing the Import-DscResource keyword and it's arguments, but before parsing anything else. private static ParseError[] ImportResourcePostParse(DynamicKeywordStatementAst ast) { var elements = Ast.CopyElements(ast.CommandElements); - - Diagnostics.Assert(elements[0] is StringConstantExpressionAst && - ((StringConstantExpressionAst)elements[0]).Value.Equals("Import-DscResource", StringComparison.OrdinalIgnoreCase), - "Incorrect ast for expected keyword"); var commandAst = new CommandAst(ast.Extent, elements, TokenKind.Unknown, null); const string NameParam = "Name"; @@ -1026,7 +1007,6 @@ private static ParseError[] CheckMandatoryPropertiesPresent(DynamicKeywordStatem // by design mandatoryPropertiesNames are not empty at this point: // every resource must have at least one Key property. - HashtableAst hashtableAst = null; foreach (var commandElementsAst in ast.CommandElements) { @@ -1179,9 +1159,7 @@ internal static void LoadResourcesFromModuleInImportResourcePostParse( LoadPowerShellClassResourcesFromModule(moduleInfo, moduleInfo, resourcesToImport, resourcesFound, exceptionList, null, true, scriptExtent); foreach (Exception ex in exceptionList) { - errorList.Add(new ParseError(scriptExtent, - "ClassResourcesLoadingFailed", - ex.Message)); + errorList.Add(new ParseError(scriptExtent, "ClassResourcesLoadingFailed", ex.Message)); } foreach (var resource in resourcesFound) @@ -1201,9 +1179,7 @@ internal static void LoadResourcesFromModuleInImportResourcePostParse( { if (!resourceNameToImport.Contains("*")) { - errorList.Add(new ParseError(scriptExtent, - "DscResourcesNotFoundDuringParsing", - string.Format(CultureInfo.CurrentCulture, ParserStrings.DscResourcesNotFoundDuringParsing, resourceNameToImport))); + errorList.Add(new ParseError(scriptExtent, "DscResourcesNotFoundDuringParsing", string.Format(CultureInfo.CurrentCulture, ParserStrings.DscResourcesNotFoundDuringParsing, resourceNameToImport))); } } } @@ -1231,7 +1207,6 @@ private static void LoadPowerShellClassResourcesFromModule( else { string scriptPath = null; - // handle RootModule and nestedModule together if (moduleInfo.RootModule != null) { scriptPath = Path.Join(moduleInfo.ModuleBase, moduleInfo.RootModule); @@ -1370,7 +1345,6 @@ private static PSObject[] GenerateJsonClassesForAst(TypeDefinitionAst typeAst, L { // MOF-based implementation of this used to generate MOF string representing classes/typeAst and pass it to MMI/MOF deserializer to get CimClass array // Here we are avoiding that roundtrip by constructing the resulting PSObjects directly - var className = typeAst.Name; string cimSuperClassName = null; @@ -1379,7 +1353,7 @@ private static PSObject[] GenerateJsonClassesForAst(TypeDefinitionAst typeAst, L cimSuperClassName = "OMI_BaseResource"; } - var _CimClassProperties = ProcessMembers(embeddedInstanceTypes, typeAst, className).ToArray(); + var cimClassProperties = ProcessMembers(embeddedInstanceTypes, typeAst, className).ToArray(); Queue bases = new Queue(); foreach (var b in typeAst.BaseTypes) @@ -1417,7 +1391,7 @@ private static PSObject[] GenerateJsonClassesForAst(TypeDefinitionAst typeAst, L result.Properties.Add(new PSNoteProperty("ClassVersion", "1.0.0")); result.Properties.Add(new PSNoteProperty("FriendlyName", className)); result.Properties.Add(new PSNoteProperty("SuperClassName", cimSuperClassName)); - result.Properties.Add(new PSNoteProperty("ClassProperties", _CimClassProperties)); + result.Properties.Add(new PSNoteProperty("ClassProperties", cimClassProperties)); return new[] { result }; } @@ -1510,10 +1484,10 @@ private static List ProcessMembers(List embeddedInstanceTypes, attributesPSObject = new PSObject(); } - List ValueMap = new List(validateSet.ValidValues); - List Values = new List(validateSet.ValidValues); - attributesPSObject.Properties.Add(new PSNoteProperty("ValueMap", ValueMap)); - attributesPSObject.Properties.Add(new PSNoteProperty("Values", Values)); + List valueMap = new List(validateSet.ValidValues); + List values = new List(validateSet.ValidValues); + attributesPSObject.Properties.Add(new PSNoteProperty("ValueMap", valueMap)); + attributesPSObject.Properties.Add(new PSNoteProperty("Values", values)); } } @@ -1574,7 +1548,10 @@ private static bool GetResourceDefinitionsFromModule(string fileName, out IEnume for (int i = 0; i < typeAst.Attributes.Count; i++) { var a = typeAst.Attributes[i]; - if (a.TypeName.GetReflectionAttributeType() == typeof(DscResourceAttribute)) return true; + if (a.TypeName.GetReflectionAttributeType() == typeof(DscResourceAttribute)) + { + return true; + } } } @@ -1706,7 +1683,9 @@ internal static string MapTypeToMofType(Type type, string memberName, string cla bool temp; var elementType = type.GetElementType(); if (!elementType.IsArray) + { return MapTypeToMofType(type.GetElementType(), memberName, className, out temp, out embeddedInstanceType, embeddedInstanceTypes); + } } else { @@ -1834,9 +1813,7 @@ private static void ProcessJsonForDynamicKeywords( /// Generated error record. public static ErrorRecord GetBadlyFormedRequiredResourceIdErrorRecord(string badDependsOnReference, string definingResource) { - PSInvalidOperationException e = PSTraceSource.NewInvalidOperationException( - ParserStrings.GetBadlyFormedRequiredResourceId, badDependsOnReference, definingResource); - + PSInvalidOperationException e = PSTraceSource.NewInvalidOperationException(ParserStrings.GetBadlyFormedRequiredResourceId, badDependsOnReference, definingResource); e.SetErrorId("GetBadlyFormedRequiredResourceId"); return e.ErrorRecord; } @@ -1849,9 +1826,7 @@ public static ErrorRecord GetBadlyFormedRequiredResourceIdErrorRecord(string bad /// Generated error record. public static ErrorRecord GetBadlyFormedExclusiveResourceIdErrorRecord(string badExclusiveResourcereference, string definingResource) { - PSInvalidOperationException e = PSTraceSource.NewInvalidOperationException( - ParserStrings.GetBadlyFormedExclusiveResourceId, badExclusiveResourcereference, definingResource); - + PSInvalidOperationException e = PSTraceSource.NewInvalidOperationException(ParserStrings.GetBadlyFormedExclusiveResourceId, badExclusiveResourcereference, definingResource); e.SetErrorId("GetBadlyFormedExclusiveResourceId"); return e.ErrorRecord; } @@ -1863,9 +1838,7 @@ public static ErrorRecord GetBadlyFormedExclusiveResourceIdErrorRecord(string ba /// Generated error record. public static ErrorRecord GetPullModeNeedConfigurationSource(string resourceId) { - PSInvalidOperationException e = PSTraceSource.NewInvalidOperationException( - ParserStrings.GetPullModeNeedConfigurationSource, resourceId); - + PSInvalidOperationException e = PSTraceSource.NewInvalidOperationException(ParserStrings.GetPullModeNeedConfigurationSource, resourceId); e.SetErrorId("GetPullModeNeedConfigurationSource"); return e.ErrorRecord; } @@ -1877,9 +1850,7 @@ public static ErrorRecord GetPullModeNeedConfigurationSource(string resourceId) /// Generated error record. public static ErrorRecord DisabledRefreshModeNotValidForPartialConfig(string resourceId) { - PSInvalidOperationException e = PSTraceSource.NewInvalidOperationException( - ParserStrings.DisabledRefreshModeNotValidForPartialConfig, resourceId); - + PSInvalidOperationException e = PSTraceSource.NewInvalidOperationException(ParserStrings.DisabledRefreshModeNotValidForPartialConfig, resourceId); e.SetErrorId("DisabledRefreshModeNotValidForPartialConfig"); return e.ErrorRecord; } @@ -1892,9 +1863,7 @@ public static ErrorRecord DisabledRefreshModeNotValidForPartialConfig(string res /// The error record to use. public static ErrorRecord DuplicateResourceIdInNodeStatementErrorRecord(string duplicateResourceId, string nodeName) { - PSInvalidOperationException e = PSTraceSource.NewInvalidOperationException( - ParserStrings.DuplicateResourceIdInNodeStatement, duplicateResourceId, nodeName); - + PSInvalidOperationException e = PSTraceSource.NewInvalidOperationException(ParserStrings.DuplicateResourceIdInNodeStatement, duplicateResourceId, nodeName); e.SetErrorId("DuplicateResourceIdInNodeStatement"); return e.ErrorRecord; } @@ -1906,9 +1875,7 @@ public static ErrorRecord DuplicateResourceIdInNodeStatementErrorRecord(string d /// Generated error record. public static ErrorRecord InvalidConfigurationNameErrorRecord(string configurationName) { - PSInvalidOperationException e = PSTraceSource.NewInvalidOperationException( - ParserStrings.InvalidConfigurationName, configurationName); - + PSInvalidOperationException e = PSTraceSource.NewInvalidOperationException(ParserStrings.InvalidConfigurationName, configurationName); e.SetErrorId("InvalidConfigurationName"); return e.ErrorRecord; } @@ -1923,9 +1890,7 @@ public static ErrorRecord InvalidConfigurationNameErrorRecord(string configurati /// Generated error record. public static ErrorRecord InvalidValueForPropertyErrorRecord(string propertyName, string value, string keywordName, string validValues) { - PSInvalidOperationException e = PSTraceSource.NewInvalidOperationException( - ParserStrings.InvalidValueForProperty, value, propertyName, keywordName, validValues); - + PSInvalidOperationException e = PSTraceSource.NewInvalidOperationException(ParserStrings.InvalidValueForProperty, value, propertyName, keywordName, validValues); e.SetErrorId("InvalidValueForProperty"); return e.ErrorRecord; } @@ -1938,9 +1903,7 @@ public static ErrorRecord InvalidValueForPropertyErrorRecord(string propertyName /// Generated error record. public static ErrorRecord InvalidLocalConfigurationManagerPropertyErrorRecord(string propertyName, string validProperties) { - PSInvalidOperationException e = PSTraceSource.NewInvalidOperationException( - ParserStrings.InvalidLocalConfigurationManagerProperty, propertyName, validProperties); - + PSInvalidOperationException e = PSTraceSource.NewInvalidOperationException(ParserStrings.InvalidLocalConfigurationManagerProperty, propertyName, validProperties); e.SetErrorId("InvalidLocalConfigurationManagerProperty"); return e.ErrorRecord; } @@ -1955,9 +1918,7 @@ public static ErrorRecord InvalidLocalConfigurationManagerPropertyErrorRecord(st /// Generated error record. public static ErrorRecord UnsupportedValueForPropertyErrorRecord(string propertyName, string value, string keywordName, string validValues) { - PSInvalidOperationException e = PSTraceSource.NewInvalidOperationException( - ParserStrings.UnsupportedValueForProperty, value, propertyName, keywordName, validValues); - + PSInvalidOperationException e = PSTraceSource.NewInvalidOperationException(ParserStrings.UnsupportedValueForProperty, value, propertyName, keywordName, validValues); e.SetErrorId("UnsupportedValueForProperty"); return e.ErrorRecord; } @@ -1971,9 +1932,7 @@ public static ErrorRecord UnsupportedValueForPropertyErrorRecord(string property /// Generated error record. public static ErrorRecord MissingValueForMandatoryPropertyErrorRecord(string keywordName, string typeName, string propertyName) { - PSInvalidOperationException e = PSTraceSource.NewInvalidOperationException( - ParserStrings.MissingValueForMandatoryProperty, keywordName, typeName, propertyName); - + PSInvalidOperationException e = PSTraceSource.NewInvalidOperationException(ParserStrings.MissingValueForMandatoryProperty, keywordName, typeName, propertyName); e.SetErrorId("MissingValueForMandatoryProperty"); return e.ErrorRecord; } @@ -1984,9 +1943,7 @@ public static ErrorRecord MissingValueForMandatoryPropertyErrorRecord(string key /// Generated error record. public static ErrorRecord DebugModeShouldHaveOneValue() { - PSInvalidOperationException e = PSTraceSource.NewInvalidOperationException( - ParserStrings.DebugModeShouldHaveOneValue); - + PSInvalidOperationException e = PSTraceSource.NewInvalidOperationException(ParserStrings.DebugModeShouldHaveOneValue); e.SetErrorId("DebugModeShouldHaveOneValue"); return e.ErrorRecord; } @@ -2002,9 +1959,7 @@ public static ErrorRecord DebugModeShouldHaveOneValue() /// Generated error record. public static ErrorRecord ValueNotInRangeErrorRecord(string property, string name, int providedValue, int lower, int upper) { - PSInvalidOperationException e = PSTraceSource.NewInvalidOperationException( - ParserStrings.ValueNotInRange, property, name, providedValue, lower, upper); - + PSInvalidOperationException e = PSTraceSource.NewInvalidOperationException(ParserStrings.ValueNotInRange, property, name, providedValue, lower, upper); e.SetErrorId("ValueNotInRange"); return e.ErrorRecord; } @@ -2016,9 +1971,7 @@ public static ErrorRecord ValueNotInRangeErrorRecord(string property, string nam /// Generated error record. public static ErrorRecord PsDscRunAsCredentialMergeErrorForCompositeResources(string resourceId) { - PSInvalidOperationException e = PSTraceSource.NewInvalidOperationException( - ParserStrings.PsDscRunAsCredentialMergeErrorForCompositeResources, resourceId); - + PSInvalidOperationException e = PSTraceSource.NewInvalidOperationException(ParserStrings.PsDscRunAsCredentialMergeErrorForCompositeResources, resourceId); e.SetErrorId("PsDscRunAsCredentialMergeErrorForCompositeResources"); return e.ErrorRecord; } @@ -2040,8 +1993,8 @@ public static ErrorRecord PsDscRunAsCredentialMergeErrorForCompositeResources(st /// [ DependsOn = [string[]] ] /// } /// - /// - /// + /// Dynamic keyword. + /// Usage string. public static string GetDSCResourceUsageString(DynamicKeyword keyword) { StringBuilder usageString; @@ -2086,7 +2039,7 @@ public static string GetDSCResourceUsageString(DynamicKeyword keyword) } var propVal = prop.Value; - if (listKeyProperties && propVal.IsKey || !listKeyProperties && !propVal.IsKey) + if ((listKeyProperties && propVal.IsKey) || (!listKeyProperties && !propVal.IsKey)) { usageString.Append(propVal.Mandatory ? " " : " [ "); usageString.Append(prop.Key); From af4bf0b1be591d59b9e275c4f0a9cae300fe6325 Mon Sep 17 00:00:00 2001 From: Andrew Date: Mon, 11 Jan 2021 21:12:58 -0800 Subject: [PATCH 54/64] CodeFactor 4 --- .../DscSupport/JsonCimDSCParser.cs | 2 +- .../DscSupport/JsonDeserializer.cs | 4 ++++ .../DscSupport/JsonDscClassCache.cs | 8 ++++---- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/System.Management.Automation/DscSupport/JsonCimDSCParser.cs b/src/System.Management.Automation/DscSupport/JsonCimDSCParser.cs index b50cdcbbb11..52bee99bdfe 100755 --- a/src/System.Management.Automation/DscSupport/JsonCimDSCParser.cs +++ b/src/System.Management.Automation/DscSupport/JsonCimDSCParser.cs @@ -44,7 +44,7 @@ internal IEnumerable ParseSchemaJson(string filePath, bool useNewRunsp if (string.Equals(superClassName, "OMI_BaseResource", StringComparison.OrdinalIgnoreCase)) { // Get the name of the file without schema.mof/json extension - if (!(className.Equals(fileNameDefiningClass, StringComparison.OrdinalIgnoreCase))) + if (!className.Equals(fileNameDefiningClass, StringComparison.OrdinalIgnoreCase)) { PSInvalidOperationException e = PSTraceSource.NewInvalidOperationException( ParserStrings.ClassNameNotSameAsDefiningFile, className, fileNameDefiningClass); diff --git a/src/System.Management.Automation/DscSupport/JsonDeserializer.cs b/src/System.Management.Automation/DscSupport/JsonDeserializer.cs index f068c43ca6b..5a48f747c0d 100755 --- a/src/System.Management.Automation/DscSupport/JsonDeserializer.cs +++ b/src/System.Management.Automation/DscSupport/JsonDeserializer.cs @@ -16,6 +16,7 @@ internal class JsonDeserializer /// /// Instantiates a default deserializer. /// + /// Default deserializer. public static JsonDeserializer Create() { return new JsonDeserializer(); @@ -28,6 +29,9 @@ public static JsonDeserializer Create() /// /// Returns schema of Cim classes from specified json file. /// + /// Json text to deserialize. + /// If a new runspace should be used. + /// Deserialized PSObjects. public IEnumerable DeserializeClasses(string json, bool useNewRunspace = false) { if (string.IsNullOrEmpty(json)) diff --git a/src/System.Management.Automation/DscSupport/JsonDscClassCache.cs b/src/System.Management.Automation/DscSupport/JsonDscClassCache.cs index b8970082192..529e079a636 100755 --- a/src/System.Management.Automation/DscSupport/JsonDscClassCache.cs +++ b/src/System.Management.Automation/DscSupport/JsonDscClassCache.cs @@ -36,7 +36,7 @@ public DscClassCacheEntry() } /// - /// Initializes all values. + /// Initializes a new instance of the class. /// /// Run as credential value. /// Resource is imported implicitly. @@ -61,7 +61,7 @@ public DscClassCacheEntry(DSCResourceRunAsCredential dscResourceRunAsCredential, public bool IsImportedImplicitly { get; set; } /// - /// A CimClass instance for this resource. + /// Gets or sets CimClass instance for this resource. /// public PSObject CimClassInstance { get; set; } @@ -719,8 +719,8 @@ public static void LoadDefaultCimKeywords(Collection errors) /// /// Load the default system CIM classes and create the corresponding keywords. - /// A dictionary to add the defined functions to, may be null. /// + /// A dictionary to add the defined functions to, may be null. public static void LoadDefaultCimKeywords(Dictionary functionsToDefine) { LoadDefaultCimKeywords(functionsToDefine, errors: null, modulePathList: null, cacheResourcesFromMultipleModuleVersions: false); @@ -2113,7 +2113,7 @@ private static StringBuilder FormatCimPropertyType(DynamicKeywordProperty prop, } /// - /// The scriptblock that implements the CIM keyword functionality. + /// Gets the scriptblock that implements the CIM keyword functionality. /// private static ScriptBlock CimKeywordImplementationFunction { From 33e04b34a6352e0f2b62749e0145d1be841fbd8a Mon Sep 17 00:00:00 2001 From: Andrew Date: Tue, 19 Jan 2021 20:17:55 -0800 Subject: [PATCH 55/64] feedback --- build.psm1 | 16 +++++----------- .../DscSupport/CimDSCParser.cs | 3 ++- .../CommandCompletion/CompletionAnalysis.cs | 12 +++--------- 3 files changed, 10 insertions(+), 21 deletions(-) diff --git a/build.psm1 b/build.psm1 index 6e3e16effdf..775b983dd3f 100644 --- a/build.psm1 +++ b/build.psm1 @@ -587,16 +587,10 @@ Fix steps: -not ($Runtime -like 'fxdependent*')) { $json = & $publishPath\pwsh -noprofile -command { - $expFeatures = [System.Collections.Generic.List[string]]::new() - Get-ExperimentalFeature | ForEach-Object { - # Special case for DSC code in PS; - # this exp feature requires new DSC module that is not inbox, - # so we don't want default DSC use case be broken - if ($_.Name -ne "PS7DscSupport") - { - $expFeatures.Add($_.Name) - } - } + # Special case for DSC code in PS; + # this exp feature requires new DSC module that is not inbox, + # so we don't want default DSC use case be broken + $expFeatures = Get-ExperimentalFeature | Where-Object Name -NE PS7DscSupport | ForEach-Object -MemberName Name # Make sure ExperimentalFeatures from modules in PSHome are added # https://github.com/PowerShell/PowerShell/issues/10550 @@ -606,7 +600,7 @@ Fix steps: } } - ConvertTo-Json $expFeatures.ToArray() + ConvertTo-Json $expFeatures } $config += @{ ExperimentalFeatures = ([string[]] ($json | ConvertFrom-Json)) } diff --git a/src/System.Management.Automation/DscSupport/CimDSCParser.cs b/src/System.Management.Automation/DscSupport/CimDSCParser.cs index aa4eb6f22fe..dda6a0a388f 100644 --- a/src/System.Management.Automation/DscSupport/CimDSCParser.cs +++ b/src/System.Management.Automation/DscSupport/CimDSCParser.cs @@ -946,7 +946,8 @@ private static CimClass MyClassCallback(string serverName, string namespaceName, } /// - /// Reads CIM MOF schema file and returns classes defined in it; this is used in MOF->JSON and MOF->PSClass convertion tools. + /// Reads CIM MOF schema file and returns classes defined in it. + /// This is used in MOF->JSON and MOF->PSClass convertion tools. /// /// /// Path to CIM MOF schema file for reading. diff --git a/src/System.Management.Automation/engine/CommandCompletion/CompletionAnalysis.cs b/src/System.Management.Automation/engine/CommandCompletion/CompletionAnalysis.cs index c8fc7135ee6..779ea8bf045 100644 --- a/src/System.Management.Automation/engine/CommandCompletion/CompletionAnalysis.cs +++ b/src/System.Management.Automation/engine/CommandCompletion/CompletionAnalysis.cs @@ -1721,15 +1721,9 @@ private static List GetResultForIdentifierInConfiguration( foreach (var keyword in matchedResults) { - string usageString = string.Empty; - if (Microsoft.PowerShell.DesiredStateConfiguration.Internal.Json.DscClassCache.NewApiIsUsed) - { - usageString = Microsoft.PowerShell.DesiredStateConfiguration.Internal.Json.DscClassCache.GetDSCResourceUsageString(keyword); - } - else - { - usageString = Microsoft.PowerShell.DesiredStateConfiguration.Internal.DscClassCache.GetDSCResourceUsageString(keyword); - } + string usageString = Microsoft.PowerShell.DesiredStateConfiguration.Internal.Json.DscClassCache.NewApiIsUsed + ? Microsoft.PowerShell.DesiredStateConfiguration.Internal.Json.DscClassCache.GetDSCResourceUsageString(keyword) + : Microsoft.PowerShell.DesiredStateConfiguration.Internal.DscClassCache.GetDSCResourceUsageString(keyword); if (results == null) { From 4a5661882f59b7832d4afa36ffd2f5361122233b Mon Sep 17 00:00:00 2001 From: Andrew Date: Wed, 20 Jan 2021 13:50:47 -0800 Subject: [PATCH 56/64] feedback 5 --- .../DscSupport/CimDSCParser.cs | 4 +--- .../DscSupport/JsonDscClassCache.cs | 10 +++++----- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/src/System.Management.Automation/DscSupport/CimDSCParser.cs b/src/System.Management.Automation/DscSupport/CimDSCParser.cs index dda6a0a388f..b0c41323c42 100644 --- a/src/System.Management.Automation/DscSupport/CimDSCParser.cs +++ b/src/System.Management.Automation/DscSupport/CimDSCParser.cs @@ -41,9 +41,7 @@ public static object ConvertCimInstanceToObject(Type targetType, CimInstance ins using (System.Management.Automation.PowerShell powerShell = System.Management.Automation.PowerShell.Create(RunspaceMode.CurrentRunspace)) { - const string Script = "param($targetType,$moduleName) & (Microsoft.PowerShell.Core\\Get-Module $moduleName) { New-Object $targetType } "; - - powerShell.AddScript(Script); + powerShell.AddScript("param($targetType,$moduleName) & (Microsoft.PowerShell.Core\\Get-Module $moduleName) { New-Object $targetType } "); powerShell.AddArgument(targetType); powerShell.AddArgument(moduleName); diff --git a/src/System.Management.Automation/DscSupport/JsonDscClassCache.cs b/src/System.Management.Automation/DscSupport/JsonDscClassCache.cs index 529e079a636..22cf1836aa1 100755 --- a/src/System.Management.Automation/DscSupport/JsonDscClassCache.cs +++ b/src/System.Management.Automation/DscSupport/JsonDscClassCache.cs @@ -78,9 +78,9 @@ public DscClassCacheEntry(DSCResourceRunAsCredential dscResourceRunAsCredential, Justification = "Needed Internal use only")] public static class DscClassCache { - private static readonly Regex reservedDynamicKeywordRegex = new Regex("^(Synchronization|Certificate|IIS|SQL)$", RegexOptions.Compiled | RegexOptions.IgnoreCase); + private static readonly HashSet reservedDynamicKeywords = new HashSet(new []{ "Synchronization","Certificate","IIS","SQL" }, StringComparer.OrdinalIgnoreCase); - private static readonly Regex reservedPropertiesRegex = new Regex("^(Require|Trigger|Notify|Before|After|Subscribe)$", RegexOptions.Compiled | RegexOptions.IgnoreCase); + private static readonly HashSet reservedProperties = new HashSet(new []{ "Require", "Trigger", "Notify", "Before", "After", "Subscribe" }, StringComparer.OrdinalIgnoreCase); /// /// Experimental feature name for DSC v3. @@ -209,7 +209,7 @@ public static void Initialize(Collection errors, List moduleP if (moduleInfos.Count > 0) { - // to be consistent with Import-Module behavior, we use the fist occurrence that we find in PSModulePath + // to be consistent with Import-Module behavior, we use the first occurrence that we find in PSModulePath var moduleDirectory = Path.GetDirectoryName(moduleInfos[0].Path); dscConfigurationDirectory = Path.Join(moduleDirectory, "Configuration"); } @@ -515,7 +515,7 @@ private static DynamicKeyword CreateKeywordFromCimClass(string moduleName, Versi }; // If it's one of reserved dynamic keyword, mark it - if (reservedDynamicKeywordRegex.Match(keywordString).Success) + if (reservedDynamicKeywords.Contains(keywordString)) { keyword.IsReservedKeyword = true; } @@ -562,7 +562,7 @@ private static DynamicKeyword CreateKeywordFromCimClass(string moduleName, Versi } // If it's one of our reserved properties, save it for error reporting - if (reservedPropertiesRegex.Match(prop.Name).Success) + if (reservedProperties.Contains(prop.Name)) { keyword.HasReservedProperties = true; continue; From a2aa5f9fc973ddc12dc246591bd010f5a05a5719 Mon Sep 17 00:00:00 2001 From: Andrew Date: Wed, 20 Jan 2021 15:28:13 -0800 Subject: [PATCH 57/64] feedback 6 --- .../DscSupport/JsonDscClassCache.cs | 70 +++++++++---------- .../engine/parser/Parser.cs | 13 ++-- 2 files changed, 42 insertions(+), 41 deletions(-) diff --git a/src/System.Management.Automation/DscSupport/JsonDscClassCache.cs b/src/System.Management.Automation/DscSupport/JsonDscClassCache.cs index 22cf1836aa1..c355a973e3a 100755 --- a/src/System.Management.Automation/DscSupport/JsonDscClassCache.cs +++ b/src/System.Management.Automation/DscSupport/JsonDscClassCache.cs @@ -78,16 +78,16 @@ public DscClassCacheEntry(DSCResourceRunAsCredential dscResourceRunAsCredential, Justification = "Needed Internal use only")] public static class DscClassCache { - private static readonly HashSet reservedDynamicKeywords = new HashSet(new []{ "Synchronization","Certificate","IIS","SQL" }, StringComparer.OrdinalIgnoreCase); + private static readonly HashSet _reservedDynamicKeywords = new HashSet(new []{ "Synchronization","Certificate","IIS","SQL" }, StringComparer.OrdinalIgnoreCase); - private static readonly HashSet reservedProperties = new HashSet(new []{ "Require", "Trigger", "Notify", "Before", "After", "Subscribe" }, StringComparer.OrdinalIgnoreCase); + private static readonly HashSet _reservedProperties = new HashSet(new []{ "Require", "Trigger", "Notify", "Before", "After", "Subscribe" }, StringComparer.OrdinalIgnoreCase); /// /// Experimental feature name for DSC v3. /// public const string DscExperimentalFeatureName = "PS7DscSupport"; - private static readonly PSTraceSource s_tracer = PSTraceSource.GetTracer("DSC", "DSC Class Cache"); + private static readonly PSTraceSource _tracer = PSTraceSource.GetTracer("DSC", "DSC Class Cache"); // Constants for items in the module qualified name (Module\Version\ClassName) private const int IndexModuleName = 0; @@ -96,7 +96,7 @@ public static class DscClassCache private const int IndexFriendlyName = 3; // Create a HashSet for fast lookup. According to MSDN, the time complexity of search for an element in a HashSet is O(1) - private static readonly HashSet s_hiddenResourceCache = + private static readonly HashSet _hiddenResourceCache = new HashSet(StringComparer.OrdinalIgnoreCase) { "MSFT_BaseConfigurationProviderRegistration", "MSFT_CimConfigurationProviderRegistration", "MSFT_PSConfigurationProviderRegistration" }; /// @@ -107,17 +107,17 @@ private static Dictionary ClassCache { get { - if (t_classCache == null) + if (_classCache == null) { - t_classCache = new Dictionary(StringComparer.OrdinalIgnoreCase); + _classCache = new Dictionary(StringComparer.OrdinalIgnoreCase); } - return t_classCache; + return _classCache; } } [ThreadStatic] - private static Dictionary t_classCache; + private static Dictionary _classCache; /// /// Gets DSC class cache for GuestConfig; it is similar to ClassCache, but maintains values between operations. @@ -126,31 +126,31 @@ private static Dictionary GuestConfigClassCache { get { - if (t_guestConfigClassCache == null) + if (_guestConfigClassCache == null) { - t_guestConfigClassCache = new Dictionary(StringComparer.OrdinalIgnoreCase); + _guestConfigClassCache = new Dictionary(StringComparer.OrdinalIgnoreCase); } - return t_guestConfigClassCache; + return _guestConfigClassCache; } } [ThreadStatic] - private static Dictionary t_guestConfigClassCache; + private static Dictionary _guestConfigClassCache; /// /// DSC classname to source module mapper. /// private static Dictionary> ByClassModuleCache - => t_byClassModuleCache ??= new Dictionary>(StringComparer.OrdinalIgnoreCase); + => _byClassModuleCache ??= new Dictionary>(StringComparer.OrdinalIgnoreCase); [ThreadStatic] - private static Dictionary> t_byClassModuleCache; + private static Dictionary> _byClassModuleCache; /// /// Default ModuleName and ModuleVersion to use. /// - private static readonly Tuple s_defaultModuleInfoForResource = new Tuple("PSDesiredStateConfiguration", new Version(3, 0)); + private static readonly Tuple _defaultModuleInfoForResource = new Tuple("PSDesiredStateConfiguration", new Version(3, 0)); /// /// When this property is set to true, DSC Cache will cache multiple versions of a resource. @@ -159,18 +159,18 @@ private static Dictionary> ByClassModuleCache /// because the Mof serializer does not support deserialization of classes with different versions. /// [ThreadStatic] - private static bool t_cacheResourcesFromMultipleModuleVersions; + private static bool _cacheResourcesFromMultipleModuleVersions; private static bool CacheResourcesFromMultipleModuleVersions { get { - return t_cacheResourcesFromMultipleModuleVersions; + return _cacheResourcesFromMultipleModuleVersions; } set { - t_cacheResourcesFromMultipleModuleVersions = value; + _cacheResourcesFromMultipleModuleVersions = value; } } @@ -192,7 +192,7 @@ public static void Initialize() /// List of module path from where DSC PS modules will be loaded. public static void Initialize(Collection errors, List modulePathList) { - s_tracer.WriteLine("Initializing DSC class cache"); + _tracer.WriteLine("Initializing DSC class cache"); // Load the base schema files. ClearCache(); @@ -226,9 +226,9 @@ public static void Initialize(Collection errors, List moduleP } var resourceBaseFile = Path.Join(dscConfigurationDirectory, "BaseRegistration", "BaseResource.schema.json"); - ImportBaseClasses(resourceBaseFile, s_defaultModuleInfoForResource, errors, false); + ImportBaseClasses(resourceBaseFile, _defaultModuleInfoForResource, errors, false); var metaConfigFile = Path.Join(dscConfigurationDirectory, "BaseRegistration", "MSFT_DSCMetaConfiguration.json"); - ImportBaseClasses(metaConfigFile, s_defaultModuleInfoForResource, errors, false); + ImportBaseClasses(metaConfigFile, _defaultModuleInfoForResource, errors, false); } /// @@ -246,7 +246,7 @@ public static IEnumerable ImportBaseClasses(string path, Tuple ImportBaseClasses(string path, Tuple ImportBaseClasses(string path, Tuple ImportBaseClasses(string path, Tuple custom { // Load the default CIM keywords Collection CIMKeywordErrors = new Collection(); - if (ExperimentalFeature.IsEnabled(Microsoft.PowerShell.DesiredStateConfiguration.Internal.Json.DscClassCache.DscExperimentalFeatureName)) + if (ExperimentalFeature.IsEnabled(Dsc.Json.DscClassCache.DscExperimentalFeatureName)) { // In addition to checking if experimental feature is enabled // also check if PSDesiredStateConfiguration is already loaded @@ -3021,16 +3022,16 @@ private StatementAst ConfigurationStatementRule(IEnumerable custom if (useJsonSchema) { - Microsoft.PowerShell.DesiredStateConfiguration.Internal.Json.DscClassCache.LoadDefaultCimKeywords(CIMKeywordErrors); + Dsc.Json.DscClassCache.LoadDefaultCimKeywords(CIMKeywordErrors); } else { - Microsoft.PowerShell.DesiredStateConfiguration.Internal.DscClassCache.LoadDefaultCimKeywords(CIMKeywordErrors); + Dsc.DscClassCache.LoadDefaultCimKeywords(CIMKeywordErrors); } } else { - Microsoft.PowerShell.DesiredStateConfiguration.Internal.DscClassCache.LoadDefaultCimKeywords(CIMKeywordErrors); + Dsc.DscClassCache.LoadDefaultCimKeywords(CIMKeywordErrors); } // Report any errors encountered while loading CIM dynamic keywords. @@ -3275,11 +3276,11 @@ private StatementAst ConfigurationStatementRule(IEnumerable custom // if (useJsonSchema) { - Microsoft.PowerShell.DesiredStateConfiguration.Internal.Json.DscClassCache.ClearCache(); + Dsc.Json.DscClassCache.ClearCache(); } else { - Microsoft.PowerShell.DesiredStateConfiguration.Internal.DscClassCache.ClearCache(); + Dsc.DscClassCache.ClearCache(); } System.Management.Automation.Language.DynamicKeyword.Reset(); From f028e496eb7ffb1b31d978afb3165941a26d95a8 Mon Sep 17 00:00:00 2001 From: Andrew Date: Wed, 20 Jan 2021 15:32:58 -0800 Subject: [PATCH 58/64] CodeFactor 5 --- .../DscSupport/JsonDscClassCache.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/System.Management.Automation/DscSupport/JsonDscClassCache.cs b/src/System.Management.Automation/DscSupport/JsonDscClassCache.cs index c355a973e3a..5438eef0fea 100755 --- a/src/System.Management.Automation/DscSupport/JsonDscClassCache.cs +++ b/src/System.Management.Automation/DscSupport/JsonDscClassCache.cs @@ -78,9 +78,9 @@ public DscClassCacheEntry(DSCResourceRunAsCredential dscResourceRunAsCredential, Justification = "Needed Internal use only")] public static class DscClassCache { - private static readonly HashSet _reservedDynamicKeywords = new HashSet(new []{ "Synchronization","Certificate","IIS","SQL" }, StringComparer.OrdinalIgnoreCase); + private static readonly HashSet _reservedDynamicKeywords = new HashSet(new[] { "Synchronization", "Certificate", "IIS", "SQL" }, StringComparer.OrdinalIgnoreCase); - private static readonly HashSet _reservedProperties = new HashSet(new []{ "Require", "Trigger", "Notify", "Before", "After", "Subscribe" }, StringComparer.OrdinalIgnoreCase); + private static readonly HashSet _reservedProperties = new HashSet(new[] { "Require", "Trigger", "Notify", "Before", "After", "Subscribe" }, StringComparer.OrdinalIgnoreCase); /// /// Experimental feature name for DSC v3. From 00981b86006dbfa1a266f71b4dbdb772b285243b Mon Sep 17 00:00:00 2001 From: Andrew Date: Thu, 21 Jan 2021 16:22:40 -0800 Subject: [PATCH 59/64] removed internal classversion property that is not used --- src/System.Management.Automation/DscSupport/JsonDscClassCache.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/System.Management.Automation/DscSupport/JsonDscClassCache.cs b/src/System.Management.Automation/DscSupport/JsonDscClassCache.cs index 5438eef0fea..72e349e31e2 100755 --- a/src/System.Management.Automation/DscSupport/JsonDscClassCache.cs +++ b/src/System.Management.Automation/DscSupport/JsonDscClassCache.cs @@ -1388,7 +1388,6 @@ private static PSObject[] GenerateJsonClassesForAst(TypeDefinitionAst typeAst, L var result = new PSObject(); result.Properties.Add(new PSNoteProperty("ClassName", className)); - result.Properties.Add(new PSNoteProperty("ClassVersion", "1.0.0")); result.Properties.Add(new PSNoteProperty("FriendlyName", className)); result.Properties.Add(new PSNoteProperty("SuperClassName", cimSuperClassName)); result.Properties.Add(new PSNoteProperty("ClassProperties", cimClassProperties)); From 7783bcf218573667c81fe29f9d7659d3452dc192 Mon Sep 17 00:00:00 2001 From: Andrew Date: Mon, 25 Jan 2021 13:21:29 -0800 Subject: [PATCH 60/64] Updated members according to naming convention --- .../DscSupport/JsonDscClassCache.cs | 75 ++++++++++--------- 1 file changed, 39 insertions(+), 36 deletions(-) diff --git a/src/System.Management.Automation/DscSupport/JsonDscClassCache.cs b/src/System.Management.Automation/DscSupport/JsonDscClassCache.cs index 72e349e31e2..12ae4024c33 100755 --- a/src/System.Management.Automation/DscSupport/JsonDscClassCache.cs +++ b/src/System.Management.Automation/DscSupport/JsonDscClassCache.cs @@ -78,16 +78,16 @@ public DscClassCacheEntry(DSCResourceRunAsCredential dscResourceRunAsCredential, Justification = "Needed Internal use only")] public static class DscClassCache { - private static readonly HashSet _reservedDynamicKeywords = new HashSet(new[] { "Synchronization", "Certificate", "IIS", "SQL" }, StringComparer.OrdinalIgnoreCase); + private static readonly HashSet s_reservedDynamicKeywords = new HashSet(new[] { "Synchronization", "Certificate", "IIS", "SQL" }, StringComparer.OrdinalIgnoreCase); - private static readonly HashSet _reservedProperties = new HashSet(new[] { "Require", "Trigger", "Notify", "Before", "After", "Subscribe" }, StringComparer.OrdinalIgnoreCase); + private static readonly HashSet s_reservedProperties = new HashSet(new[] { "Require", "Trigger", "Notify", "Before", "After", "Subscribe" }, StringComparer.OrdinalIgnoreCase); /// /// Experimental feature name for DSC v3. /// public const string DscExperimentalFeatureName = "PS7DscSupport"; - private static readonly PSTraceSource _tracer = PSTraceSource.GetTracer("DSC", "DSC Class Cache"); + private static readonly PSTraceSource s_tracer = PSTraceSource.GetTracer("DSC", "DSC Class Cache"); // Constants for items in the module qualified name (Module\Version\ClassName) private const int IndexModuleName = 0; @@ -96,7 +96,7 @@ public static class DscClassCache private const int IndexFriendlyName = 3; // Create a HashSet for fast lookup. According to MSDN, the time complexity of search for an element in a HashSet is O(1) - private static readonly HashSet _hiddenResourceCache = + private static readonly HashSet s_hiddenResourceCache = new HashSet(StringComparer.OrdinalIgnoreCase) { "MSFT_BaseConfigurationProviderRegistration", "MSFT_CimConfigurationProviderRegistration", "MSFT_PSConfigurationProviderRegistration" }; /// @@ -107,17 +107,17 @@ private static Dictionary ClassCache { get { - if (_classCache == null) + if (t_classCache == null) { - _classCache = new Dictionary(StringComparer.OrdinalIgnoreCase); + t_classCache = new Dictionary(StringComparer.OrdinalIgnoreCase); } - return _classCache; + return t_classCache; } } [ThreadStatic] - private static Dictionary _classCache; + private static Dictionary t_classCache; /// /// Gets DSC class cache for GuestConfig; it is similar to ClassCache, but maintains values between operations. @@ -126,31 +126,31 @@ private static Dictionary GuestConfigClassCache { get { - if (_guestConfigClassCache == null) + if (t_guestConfigClassCache == null) { - _guestConfigClassCache = new Dictionary(StringComparer.OrdinalIgnoreCase); + t_guestConfigClassCache = new Dictionary(StringComparer.OrdinalIgnoreCase); } - return _guestConfigClassCache; + return t_guestConfigClassCache; } } [ThreadStatic] - private static Dictionary _guestConfigClassCache; + private static Dictionary t_guestConfigClassCache; /// /// DSC classname to source module mapper. /// private static Dictionary> ByClassModuleCache - => _byClassModuleCache ??= new Dictionary>(StringComparer.OrdinalIgnoreCase); + => t_byClassModuleCache ??= new Dictionary>(StringComparer.OrdinalIgnoreCase); [ThreadStatic] - private static Dictionary> _byClassModuleCache; + private static Dictionary> t_byClassModuleCache; /// /// Default ModuleName and ModuleVersion to use. /// - private static readonly Tuple _defaultModuleInfoForResource = new Tuple("PSDesiredStateConfiguration", new Version(3, 0)); + private static readonly Tuple s_defaultModuleInfoForResource = new Tuple("PSDesiredStateConfiguration", new Version(3, 0)); /// /// When this property is set to true, DSC Cache will cache multiple versions of a resource. @@ -159,23 +159,26 @@ private static Dictionary> ByClassModuleCache /// because the Mof serializer does not support deserialization of classes with different versions. /// [ThreadStatic] - private static bool _cacheResourcesFromMultipleModuleVersions; + private static bool t_cacheResourcesFromMultipleModuleVersions; private static bool CacheResourcesFromMultipleModuleVersions { get { - return _cacheResourcesFromMultipleModuleVersions; + return t_cacheResourcesFromMultipleModuleVersions; } set { - _cacheResourcesFromMultipleModuleVersions = value; + t_cacheResourcesFromMultipleModuleVersions = value; } } + /// + /// Flag shows if PS7 DSC APIs were used. + /// [ThreadStatic] - internal static bool NewApiIsUsed = false; + public static bool NewApiIsUsed = false; /// /// Initialize the class cache with the default classes in $ENV:SystemDirectory\Configuration. @@ -192,7 +195,7 @@ public static void Initialize() /// List of module path from where DSC PS modules will be loaded. public static void Initialize(Collection errors, List modulePathList) { - _tracer.WriteLine("Initializing DSC class cache"); + s_tracer.WriteLine("Initializing DSC class cache"); // Load the base schema files. ClearCache(); @@ -226,9 +229,9 @@ public static void Initialize(Collection errors, List moduleP } var resourceBaseFile = Path.Join(dscConfigurationDirectory, "BaseRegistration", "BaseResource.schema.json"); - ImportBaseClasses(resourceBaseFile, _defaultModuleInfoForResource, errors, false); + ImportBaseClasses(resourceBaseFile, s_defaultModuleInfoForResource, errors, false); var metaConfigFile = Path.Join(dscConfigurationDirectory, "BaseRegistration", "MSFT_DSCMetaConfiguration.json"); - ImportBaseClasses(metaConfigFile, _defaultModuleInfoForResource, errors, false); + ImportBaseClasses(metaConfigFile, s_defaultModuleInfoForResource, errors, false); } /// @@ -246,7 +249,7 @@ public static IEnumerable ImportBaseClasses(string path, Tuple ImportBaseClasses(string path, Tuple ImportBaseClasses(string path, Tuple ImportBaseClasses(string path, Tuple Date: Mon, 25 Jan 2021 16:42:13 -0800 Subject: [PATCH 61/64] Updated NewApiIsUsed flag --- .../DscSupport/JsonDscClassCache.cs | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/src/System.Management.Automation/DscSupport/JsonDscClassCache.cs b/src/System.Management.Automation/DscSupport/JsonDscClassCache.cs index 12ae4024c33..820d2290324 100755 --- a/src/System.Management.Automation/DscSupport/JsonDscClassCache.cs +++ b/src/System.Management.Automation/DscSupport/JsonDscClassCache.cs @@ -174,11 +174,24 @@ private static bool CacheResourcesFromMultipleModuleVersions } } + [ThreadStatic] + private static bool t_newApiIsUsed = false; + /// /// Flag shows if PS7 DSC APIs were used. /// - [ThreadStatic] - public static bool NewApiIsUsed = false; + public static bool NewApiIsUsed + { + get + { + return t_newApiIsUsed; + } + + set + { + t_newApiIsUsed = value; + } + } /// /// Initialize the class cache with the default classes in $ENV:SystemDirectory\Configuration. From c0db81c05552adc6925a01021fadad59311b1beb Mon Sep 17 00:00:00 2001 From: Andrew Date: Wed, 27 Jan 2021 15:23:47 -0800 Subject: [PATCH 62/64] Import-DscResource fix --- .../DscSupport/JsonDscClassCache.cs | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/src/System.Management.Automation/DscSupport/JsonDscClassCache.cs b/src/System.Management.Automation/DscSupport/JsonDscClassCache.cs index 820d2290324..57c9860c379 100755 --- a/src/System.Management.Automation/DscSupport/JsonDscClassCache.cs +++ b/src/System.Management.Automation/DscSupport/JsonDscClassCache.cs @@ -99,6 +99,10 @@ public static class DscClassCache private static readonly HashSet s_hiddenResourceCache = new HashSet(StringComparer.OrdinalIgnoreCase) { "MSFT_BaseConfigurationProviderRegistration", "MSFT_CimConfigurationProviderRegistration", "MSFT_PSConfigurationProviderRegistration" }; + // A collection to prevent circular importing case when Import-DscResource does not have a module specified + [ThreadStatic] + private static readonly HashSet t_currentImportDscResourceInvocations = new(StringComparer.OrdinalIgnoreCase); + /// /// Gets DSC class cache for this runspace. /// Cache stores the DSCRunAsBehavior, cim class and boolean to indicate if an Inbox resource has been implicitly imported. @@ -373,6 +377,7 @@ public static void ClearCache() ClassCache.Clear(); ByClassModuleCache.Clear(); CacheResourcesFromMultipleModuleVersions = false; + t_currentImportDscResourceInvocations.Clear(); } private static string GetModuleQualifiedResourceName(string moduleName, string moduleVersion, string className, string resourceName) @@ -1149,11 +1154,17 @@ internal static void LoadResourcesFromModuleInImportResourcePostParse( else if (resourceNames != null) { // Lookup the required resources under available PowerShell modules when modulename is not specified - using (var powerShell = System.Management.Automation.PowerShell.Create(RunspaceMode.CurrentRunspace)) + // Make sure that this is not a circular import/parsing + var callLocation = string.Join(':', scriptExtent.File, scriptExtent.StartLineNumber, scriptExtent.StartColumnNumber, scriptExtent.Text); + if (!t_currentImportDscResourceInvocations.Contains(callLocation)) { - powerShell.AddCommand("Get-Module"); - powerShell.AddParameter("ListAvailable"); - modules = powerShell.Invoke(); + t_currentImportDscResourceInvocations.Add(callLocation); + using (var powerShell = System.Management.Automation.PowerShell.Create(RunspaceMode.CurrentRunspace)) + { + powerShell.AddCommand("Get-Module"); + powerShell.AddParameter("ListAvailable"); + modules = powerShell.Invoke(); + } } } From 0af0b68477a3125cd50c77df028b7be4ca9b3919 Mon Sep 17 00:00:00 2001 From: Andrew Date: Thu, 28 Jan 2021 14:10:44 -0800 Subject: [PATCH 63/64] feedback 7 --- build.psm1 | 2 +- .../DscSupport/CimDSCParser.cs | 2 +- .../DscSupport/JsonCimDSCParser.cs | 2 +- .../DscSupport/JsonDeserializer.cs | 2 +- .../DscSupport/JsonDscClassCache.cs | 26 +++++++++---------- .../CommandCompletion/CompletionAnalysis.cs | 4 +-- .../ExperimentalFeature.cs | 2 +- .../engine/parser/Parser.cs | 16 ++++++------ 8 files changed, 28 insertions(+), 28 deletions(-) diff --git a/build.psm1 b/build.psm1 index 775b983dd3f..b4548a66725 100644 --- a/build.psm1 +++ b/build.psm1 @@ -588,7 +588,7 @@ Fix steps: $json = & $publishPath\pwsh -noprofile -command { # Special case for DSC code in PS; - # this exp feature requires new DSC module that is not inbox, + # this experimental feature requires new DSC module that is not inbox, # so we don't want default DSC use case be broken $expFeatures = Get-ExperimentalFeature | Where-Object Name -NE PS7DscSupport | ForEach-Object -MemberName Name diff --git a/src/System.Management.Automation/DscSupport/CimDSCParser.cs b/src/System.Management.Automation/DscSupport/CimDSCParser.cs index b0c41323c42..2901148813b 100644 --- a/src/System.Management.Automation/DscSupport/CimDSCParser.cs +++ b/src/System.Management.Automation/DscSupport/CimDSCParser.cs @@ -945,7 +945,7 @@ private static CimClass MyClassCallback(string serverName, string namespaceName, /// /// Reads CIM MOF schema file and returns classes defined in it. - /// This is used in MOF->JSON and MOF->PSClass convertion tools. + /// This is used MOF->PSClass conversion tool. /// /// /// Path to CIM MOF schema file for reading. diff --git a/src/System.Management.Automation/DscSupport/JsonCimDSCParser.cs b/src/System.Management.Automation/DscSupport/JsonCimDSCParser.cs index 52bee99bdfe..c2221cb3f33 100755 --- a/src/System.Management.Automation/DscSupport/JsonCimDSCParser.cs +++ b/src/System.Management.Automation/DscSupport/JsonCimDSCParser.cs @@ -10,7 +10,7 @@ using System.Management.Automation; using System.Security; -namespace Microsoft.PowerShell.DesiredStateConfiguration.Internal.Json +namespace Microsoft.PowerShell.DesiredStateConfiguration.Internal.CrossPlatform { /// /// Class that does high level Cim schema parsing. diff --git a/src/System.Management.Automation/DscSupport/JsonDeserializer.cs b/src/System.Management.Automation/DscSupport/JsonDeserializer.cs index 5a48f747c0d..319560f0a09 100755 --- a/src/System.Management.Automation/DscSupport/JsonDeserializer.cs +++ b/src/System.Management.Automation/DscSupport/JsonDeserializer.cs @@ -7,7 +7,7 @@ using System.Management.Automation; using System.Management.Automation.Runspaces; -namespace Microsoft.PowerShell.DesiredStateConfiguration.Internal.Json +namespace Microsoft.PowerShell.DesiredStateConfiguration.Internal.CrossPlatform { internal class JsonDeserializer { diff --git a/src/System.Management.Automation/DscSupport/JsonDscClassCache.cs b/src/System.Management.Automation/DscSupport/JsonDscClassCache.cs index 57c9860c379..762c5726d42 100755 --- a/src/System.Management.Automation/DscSupport/JsonDscClassCache.cs +++ b/src/System.Management.Automation/DscSupport/JsonDscClassCache.cs @@ -20,7 +20,7 @@ using Microsoft.PowerShell.Commands; -namespace Microsoft.PowerShell.DesiredStateConfiguration.Internal.Json +namespace Microsoft.PowerShell.DesiredStateConfiguration.Internal.CrossPlatform { /// /// Class that defines Dsc cache entries. @@ -2188,7 +2188,7 @@ function Test-DependsOn if ($DependsOnVar -notmatch '^\[[a-z]\w*\][a-z_0-9][a-z_0-9\p{Zs}\.\\-]*$') { Update-ConfigurationErrorCount - Write-Error -ErrorRecord ([Microsoft.PowerShell.DesiredStateConfiguration.Internal.Json.DscClassCache]::GetBadlyFormedRequiredResourceIdErrorRecord($DependsOnVar, $resourceId)) + Write-Error -ErrorRecord ([Microsoft.PowerShell.DesiredStateConfiguration.Internal.CrossPlatform.DscClassCache]::GetBadlyFormedRequiredResourceIdErrorRecord($DependsOnVar, $resourceId)) } # Fix up DependsOn for nested names @@ -2264,7 +2264,7 @@ function Test-DependsOn if (Test-NodeResources $resourceId) { Update-ConfigurationErrorCount - Write-Error -ErrorRecord ([Microsoft.PowerShell.DesiredStateConfiguration.Internal.Json.DscClassCache]::DuplicateResourceIdInNodeStatementErrorRecord($resourceId, (Get-PSCurrentConfigurationNode))) + Write-Error -ErrorRecord ([Microsoft.PowerShell.DesiredStateConfiguration.Internal.CrossPlatform.DscClassCache]::DuplicateResourceIdInNodeStatementErrorRecord($resourceId, (Get-PSCurrentConfigurationNode))) } else { @@ -2280,7 +2280,7 @@ function Test-DependsOn if($null -ne $value['PsDscRunAsCredential']) { Update-ConfigurationErrorCount - Write-Error -ErrorRecord ([Microsoft.PowerShell.DesiredStateConfiguration.Internal.Json.DscClassCache]::PsDscRunAsCredentialMergeErrorForCompositeResources($resourceId)) + Write-Error -ErrorRecord ([Microsoft.PowerShell.DesiredStateConfiguration.Internal.CrossPlatform.DscClassCache]::PsDscRunAsCredentialMergeErrorForCompositeResources($resourceId)) } # Set the Value of RunAsCred to that of outer configuration else @@ -2299,14 +2299,14 @@ function Test-DependsOn if($value['RefreshMode'] -eq 'Pull' -and -not $value['ConfigurationSource']) { Update-ConfigurationErrorCount - Write-Error -ErrorRecord ([Microsoft.PowerShell.DesiredStateConfiguration.Internal.Json.DscClassCache]::GetPullModeNeedConfigurationSource($resourceId)) + Write-Error -ErrorRecord ([Microsoft.PowerShell.DesiredStateConfiguration.Internal.CrossPlatform.DscClassCache]::GetPullModeNeedConfigurationSource($resourceId)) } # Verify that RefreshMode is not Disabled for Partial configuration if($value['RefreshMode'] -eq 'Disabled') { Update-ConfigurationErrorCount - Write-Error -ErrorRecord ([Microsoft.PowerShell.DesiredStateConfiguration.Internal.Json.DscClassCache]::DisabledRefreshModeNotValidForPartialConfig($resourceId)) + Write-Error -ErrorRecord ([Microsoft.PowerShell.DesiredStateConfiguration.Internal.CrossPlatform.DscClassCache]::DisabledRefreshModeNotValidForPartialConfig($resourceId)) } if($null -ne $value['ConfigurationSource']) @@ -2327,7 +2327,7 @@ function Test-DependsOn if (($ExclusiveResource -notmatch '^[a-z][a-z_0-9]*\\[a-z][a-z_0-9]*$') -and ($ExclusiveResource -notmatch '^[a-z][a-z_0-9]*$') -and ($ExclusiveResource -notmatch '^[a-z][a-z_0-9]*\\\*$')) { Update-ConfigurationErrorCount - Write-Error -ErrorRecord ([Microsoft.PowerShell.DesiredStateConfiguration.Internal.Json.DscClassCache]::GetBadlyFormedExclusiveResourceIdErrorRecord($ExclusiveResource, $resourceId)) + Write-Error -ErrorRecord ([Microsoft.PowerShell.DesiredStateConfiguration.Internal.CrossPlatform.DscClassCache]::GetBadlyFormedExclusiveResourceIdErrorRecord($ExclusiveResource, $resourceId)) } } @@ -2355,7 +2355,7 @@ function Test-DependsOn { if ($OldMetaConfig -and (-not ($V1MetaConfigPropertyList -contains $key))) { - Write-Error -ErrorRecord ([Microsoft.PowerShell.DesiredStateConfiguration.Internal.Json.DscClassCache]::InvalidLocalConfigurationManagerPropertyErrorRecord($key, ($V1MetaConfigPropertyList -join ', '))) + Write-Error -ErrorRecord ([Microsoft.PowerShell.DesiredStateConfiguration.Internal.CrossPlatform.DscClassCache]::InvalidLocalConfigurationManagerPropertyErrorRecord($key, ($V1MetaConfigPropertyList -join ', '))) Update-ConfigurationErrorCount } # see if there is a list of allowed values for this property (similar to an enum) @@ -2365,7 +2365,7 @@ function Test-DependsOn { if(($null -eq $value[$key]) -and ($allowedValues -notcontains $value[$key])) { - Write-Error -ErrorRecord ([Microsoft.PowerShell.DesiredStateConfiguration.Internal.Json.DscClassCache]::InvalidValueForPropertyErrorRecord($key, ""$($value[$key])"", $keywordData.Keyword, ($allowedValues -join ', '))) + Write-Error -ErrorRecord ([Microsoft.PowerShell.DesiredStateConfiguration.Internal.CrossPlatform.DscClassCache]::InvalidValueForPropertyErrorRecord($key, ""$($value[$key])"", $keywordData.Keyword, ($allowedValues -join ', '))) Update-ConfigurationErrorCount } else @@ -2382,7 +2382,7 @@ function Test-DependsOn if($notAllowedValue) { $notAllowedValue = $notAllowedValue.Substring(0, $notAllowedValue.Length -2) - Write-Error -ErrorRecord ([Microsoft.PowerShell.DesiredStateConfiguration.Internal.Json.DscClassCache]::UnsupportedValueForPropertyErrorRecord($key, $notAllowedValue, $keywordData.Keyword, ($allowedValues -join ', '))) + Write-Error -ErrorRecord ([Microsoft.PowerShell.DesiredStateConfiguration.Internal.CrossPlatform.DscClassCache]::UnsupportedValueForPropertyErrorRecord($key, $notAllowedValue, $keywordData.Keyword, ($allowedValues -join ', '))) Update-ConfigurationErrorCount } } @@ -2395,7 +2395,7 @@ function Test-DependsOn $castedValue = $value[$key] -as [int] if((($castedValue -is [int]) -and (($castedValue -lt $keywordData.Properties[$key].Range.Item1) -or ($castedValue -gt $keywordData.Properties[$key].Range.Item2))) -or ($null -eq $castedValue)) { - Write-Error -ErrorRecord ([Microsoft.PowerShell.DesiredStateConfiguration.Internal.Json.DscClassCache]::ValueNotInRangeErrorRecord($key, $keywordName, $value[$key], $keywordData.Properties[$key].Range.Item1, $keywordData.Properties[$key].Range.Item2)) + Write-Error -ErrorRecord ([Microsoft.PowerShell.DesiredStateConfiguration.Internal.CrossPlatform.DscClassCache]::ValueNotInRangeErrorRecord($key, $keywordName, $value[$key], $keywordData.Properties[$key].Range.Item1, $keywordData.Properties[$key].Range.Item2)) Update-ConfigurationErrorCount } } @@ -2429,7 +2429,7 @@ function Test-DependsOn elseif ($keywordData.Properties[$key].Mandatory) { # If the property was mandatory but the user didn't provide a value, write and error. - Write-Error -ErrorRecord ([Microsoft.PowerShell.DesiredStateConfiguration.Internal.Json.DscClassCache]::MissingValueForMandatoryPropertyErrorRecord($keywordData.Keyword, $keywordData.Properties[$key].TypeConstraint, $Key)) + Write-Error -ErrorRecord ([Microsoft.PowerShell.DesiredStateConfiguration.Internal.CrossPlatform.DscClassCache]::MissingValueForMandatoryPropertyErrorRecord($keywordData.Keyword, $keywordData.Properties[$key].TypeConstraint, $Key)) Update-ConfigurationErrorCount } @@ -2482,7 +2482,7 @@ function Test-DependsOn if($canonicalizedValue['DebugMode'] -and @($canonicalizedValue['DebugMode']).Length -gt 1) { # we only allow one value for debug mode now. - Write-Error -ErrorRecord ([Microsoft.PowerShell.DesiredStateConfiguration.Internal.Json.DscClassCache]::DebugModeShouldHaveOneValue()) + Write-Error -ErrorRecord ([Microsoft.PowerShell.DesiredStateConfiguration.Internal.CrossPlatform.DscClassCache]::DebugModeShouldHaveOneValue()) Update-ConfigurationErrorCount } } diff --git a/src/System.Management.Automation/engine/CommandCompletion/CompletionAnalysis.cs b/src/System.Management.Automation/engine/CommandCompletion/CompletionAnalysis.cs index 779ea8bf045..a38bfa9218f 100644 --- a/src/System.Management.Automation/engine/CommandCompletion/CompletionAnalysis.cs +++ b/src/System.Management.Automation/engine/CommandCompletion/CompletionAnalysis.cs @@ -1721,8 +1721,8 @@ private static List GetResultForIdentifierInConfiguration( foreach (var keyword in matchedResults) { - string usageString = Microsoft.PowerShell.DesiredStateConfiguration.Internal.Json.DscClassCache.NewApiIsUsed - ? Microsoft.PowerShell.DesiredStateConfiguration.Internal.Json.DscClassCache.GetDSCResourceUsageString(keyword) + string usageString = Microsoft.PowerShell.DesiredStateConfiguration.Internal.CrossPlatform.DscClassCache.NewApiIsUsed + ? Microsoft.PowerShell.DesiredStateConfiguration.Internal.CrossPlatform.DscClassCache.GetDSCResourceUsageString(keyword) : Microsoft.PowerShell.DesiredStateConfiguration.Internal.DscClassCache.GetDSCResourceUsageString(keyword); if (results == null) diff --git a/src/System.Management.Automation/engine/ExperimentalFeature/ExperimentalFeature.cs b/src/System.Management.Automation/engine/ExperimentalFeature/ExperimentalFeature.cs index 3b7a0a60644..73cb117d687 100644 --- a/src/System.Management.Automation/engine/ExperimentalFeature/ExperimentalFeature.cs +++ b/src/System.Management.Automation/engine/ExperimentalFeature/ExperimentalFeature.cs @@ -125,7 +125,7 @@ static ExperimentalFeature() description: "Don't have $ErrorActionPreference affect stderr output"), new ExperimentalFeature( name: "PS7DscSupport", - description: "Support cross-platform DSC"), + description: "Support the cross-platform class-based DSC"), new ExperimentalFeature( name: "PSSubsystemPluginModel", description: "A plugin model for registering and un-registering PowerShell subsystems"), diff --git a/src/System.Management.Automation/engine/parser/Parser.cs b/src/System.Management.Automation/engine/parser/Parser.cs index e8ec00beb76..5b8ca433454 100644 --- a/src/System.Management.Automation/engine/parser/Parser.cs +++ b/src/System.Management.Automation/engine/parser/Parser.cs @@ -2933,7 +2933,7 @@ private StatementAst ConfigurationStatementRule(IEnumerable custom // Runspaces.Runspace localRunspace = null; bool topLevel = false; - bool useJsonSchema = false; + bool useCrossPlatformSchema = false; try { // At this point, we'll need a runspace to use to hold the metadata for the parse. If there is no @@ -2969,7 +2969,6 @@ private StatementAst ConfigurationStatementRule(IEnumerable custom ExpressionAst configurationBodyScriptBlock = null; - // Automatically import the PSDesiredStateConfiguration module at this point. PowerShell p = null; // Save the parser we're using so we can resume the current parse when we're done. @@ -2997,7 +2996,7 @@ private StatementAst ConfigurationStatementRule(IEnumerable custom { // Load the default CIM keywords Collection CIMKeywordErrors = new Collection(); - if (ExperimentalFeature.IsEnabled(Dsc.Json.DscClassCache.DscExperimentalFeatureName)) + if (ExperimentalFeature.IsEnabled(Dsc.CrossPlatform.DscClassCache.DscExperimentalFeatureName)) { // In addition to checking if experimental feature is enabled // also check if PSDesiredStateConfiguration is already loaded @@ -3013,16 +3012,17 @@ private StatementAst ConfigurationStatementRule(IEnumerable custom if (moduleInfo.Version.Major < 3) { prev3IsLoaded = true; + break; } } p.Commands.Clear(); - useJsonSchema = !prev3IsLoaded; + useCrossPlatformSchema = !prev3IsLoaded; - if (useJsonSchema) + if (useCrossPlatformSchema) { - Dsc.Json.DscClassCache.LoadDefaultCimKeywords(CIMKeywordErrors); + Dsc.CrossPlatform.DscClassCache.LoadDefaultCimKeywords(CIMKeywordErrors); } else { @@ -3274,9 +3274,9 @@ private StatementAst ConfigurationStatementRule(IEnumerable custom // Clear out all of the cached classes and keywords. // They will need to be reloaded when the generated function is actually run. // - if (useJsonSchema) + if (useCrossPlatformSchema) { - Dsc.Json.DscClassCache.ClearCache(); + Dsc.CrossPlatform.DscClassCache.ClearCache(); } else { From e02bf3afdb29d04ed29143a1bdb46726fe772070 Mon Sep 17 00:00:00 2001 From: Andrew Date: Tue, 2 Feb 2021 13:28:50 -0800 Subject: [PATCH 64/64] feedback 8 --- .../DscSupport/JsonDscClassCache.cs | 138 ++++++++---------- 1 file changed, 61 insertions(+), 77 deletions(-) diff --git a/src/System.Management.Automation/DscSupport/JsonDscClassCache.cs b/src/System.Management.Automation/DscSupport/JsonDscClassCache.cs index 762c5726d42..2fe4f7c2606 100755 --- a/src/System.Management.Automation/DscSupport/JsonDscClassCache.cs +++ b/src/System.Management.Automation/DscSupport/JsonDscClassCache.cs @@ -90,10 +90,10 @@ public static class DscClassCache private static readonly PSTraceSource s_tracer = PSTraceSource.GetTracer("DSC", "DSC Class Cache"); // Constants for items in the module qualified name (Module\Version\ClassName) - private const int IndexModuleName = 0; - private const int IndexModuleVersion = 1; - private const int IndexClassName = 2; - private const int IndexFriendlyName = 3; + private const int ModuleNameIndex = 0; + private const int ModuleVersionIndex = 1; + private const int ClassNameIndex = 2; + private const int FriendlyNameIndex = 3; // Create a HashSet for fast lookup. According to MSDN, the time complexity of search for an element in a HashSet is O(1) private static readonly HashSet s_hiddenResourceCache = @@ -109,15 +109,7 @@ public static class DscClassCache /// private static Dictionary ClassCache { - get - { - if (t_classCache == null) - { - t_classCache = new Dictionary(StringComparer.OrdinalIgnoreCase); - } - - return t_classCache; - } + get => t_classCache ??= new Dictionary(StringComparer.OrdinalIgnoreCase); } [ThreadStatic] @@ -128,15 +120,7 @@ private static Dictionary ClassCache /// private static Dictionary GuestConfigClassCache { - get - { - if (t_guestConfigClassCache == null) - { - t_guestConfigClassCache = new Dictionary(StringComparer.OrdinalIgnoreCase); - } - - return t_guestConfigClassCache; - } + get => t_guestConfigClassCache ??= new Dictionary(StringComparer.OrdinalIgnoreCase); } [ThreadStatic] @@ -389,9 +373,9 @@ private static List> FindResourceInCach { return (from cacheEntry in ClassCache let splittedName = cacheEntry.Key.Split(Utils.Separators.Backslash) - let cachedClassName = splittedName[IndexClassName] - let cachedModuleName = splittedName[IndexModuleName] - let cachedResourceName = splittedName[IndexFriendlyName] + let cachedClassName = splittedName[ClassNameIndex] + let cachedModuleName = splittedName[ModuleNameIndex] + let cachedResourceName = splittedName[FriendlyNameIndex] where (string.Equals(cachedResourceName, resourceName, StringComparison.OrdinalIgnoreCase) || (string.Equals(cachedClassName, className, StringComparison.OrdinalIgnoreCase) && string.Equals(cachedModuleName, moduleName, StringComparison.OrdinalIgnoreCase))) @@ -469,11 +453,11 @@ public static Collection GetKeywordsFromCachedClasses() foreach (KeyValuePair cachedClass in ClassCache) { string[] splittedName = cachedClass.Key.Split(Utils.Separators.Backslash); - string moduleName = splittedName[IndexModuleName]; - string moduleVersion = splittedName[IndexModuleVersion]; + string moduleName = splittedName[ModuleNameIndex]; + string moduleVersion = splittedName[ModuleVersionIndex]; var keyword = CreateKeywordFromCimClass(moduleName, Version.Parse(moduleVersion), cachedClass.Value.CimClassInstance, cachedClass.Value.DscResRunAsCred); - if (keyword != null) + if (keyword is not null) { keywords.Add(keyword); } @@ -485,7 +469,7 @@ public static Collection GetKeywordsFromCachedClasses() private static void CreateAndRegisterKeywordFromCimClass(string moduleName, Version moduleVersion, PSObject cimClass, Dictionary functionsToDefine, DSCResourceRunAsCredential runAsBehavior) { var keyword = CreateKeywordFromCimClass(moduleName, moduleVersion, cimClass, runAsBehavior); - if (keyword == null) + if (keyword is null) { return; } @@ -494,7 +478,7 @@ private static void CreateAndRegisterKeywordFromCimClass(string moduleName, Vers if (!CacheResourcesFromMultipleModuleVersions && DynamicKeyword.ContainsKeyword(keyword.Keyword)) { var oldKeyword = DynamicKeyword.GetKeyword(keyword.Keyword); - if (oldKeyword.ImplementingModule == null || + if (oldKeyword.ImplementingModule is null || !oldKeyword.ImplementingModule.Equals(moduleName, StringComparison.OrdinalIgnoreCase) || oldKeyword.ImplementingModuleVersion != moduleVersion) { var e = PSTraceSource.NewInvalidOperationException(ParserStrings.DuplicateKeywordDefinition, keyword.Keyword); @@ -610,7 +594,7 @@ private static DynamicKeyword CreateKeywordFromCimClass(string moduleName, Versi // Check to see if there is a Values attribute and save the list of allowed values if so. var values = prop.Qualifiers?.Values; - if (values != null) + if (values is not null) { foreach (var val in values) { @@ -621,7 +605,7 @@ private static DynamicKeyword CreateKeywordFromCimClass(string moduleName, Versi // Check to see if there is a ValueMap attribute and save the list of allowed values if so. var nativeValueMap = prop.Qualifiers?.ValueMap; List valueMap = null; - if (nativeValueMap != null) + if (nativeValueMap is not null) { valueMap = new List(); foreach (var val in nativeValueMap) @@ -652,7 +636,7 @@ private static DynamicKeyword CreateKeywordFromCimClass(string moduleName, Versi } } - if (valueMap != null && keyProp.Values.Count > 0) + if (valueMap is not null && keyProp.Values.Count > 0) { if (valueMap.Count != keyProp.Values.Count) { @@ -711,7 +695,7 @@ private static void UpdateKnownRestriction(DynamicKeyword keyword) "MSFT_DSCMetaConfiguration", StringComparison.OrdinalIgnoreCase)) { - if (keyword.Properties["RefreshFrequencyMins"] != null) + if (keyword.Properties["RefreshFrequencyMins"] is not null) { keyword.Properties["RefreshFrequencyMins"].Range = new Tuple(RefreshFrequencyMin, RefreshFrequencyMax); } @@ -721,7 +705,7 @@ private static void UpdateKnownRestriction(DynamicKeyword keyword) keyword.Properties["ConfigurationModeFrequencyMins"].Range = new Tuple(ConfigurationModeFrequencyMin, ConfigurationModeFrequencyMax); } - if (keyword.Properties["DebugMode"] != null) + if (keyword.Properties["DebugMode"] is not null) { keyword.Properties["DebugMode"].Values.Remove("ResourceScriptBreakAll"); keyword.Properties["DebugMode"].ValueMap.Remove("ResourceScriptBreakAll"); @@ -891,14 +875,14 @@ private static ParseError[] ImportResourcePostParse(DynamicKeywordStatementAst a // if resources and modules are both null we have already added this error in collection // we do not want to do this twice. since we are giving same error ImportDscResourceNeedParams in both cases // once we have different error messages for 2 scenarios we can remove this check - if (resourceNameBindingResult != null) + if (resourceNameBindingResult is not null) { errorList.Add(new ParseError(ast.Extent, "ImportDscResourceNeedModuleNameWithModuleVersion", string.Format(CultureInfo.CurrentCulture, ParserStrings.ImportDscResourceNeedParams))); } } string[] resourceNames = null; - if (resourceNameBindingResult != null) + if (resourceNameBindingResult is not null) { object resourceName = null; if (!IsConstantValueVisitor.IsConstant(resourceNameBindingResult.Value, out resourceName, true, true) || @@ -909,7 +893,7 @@ private static ParseError[] ImportResourcePostParse(DynamicKeywordStatementAst a } System.Version moduleVersion = null; - if (moduleVersionBindingResult != null) + if (moduleVersionBindingResult is not null) { object moduleVer = null; if (!IsConstantValueVisitor.IsConstant(moduleVersionBindingResult.Value, out moduleVer, true, true)) @@ -932,7 +916,7 @@ private static ParseError[] ImportResourcePostParse(DynamicKeywordStatementAst a } ModuleSpecification[] moduleSpecifications = null; - if (moduleNameBindingResult != null) + if (moduleNameBindingResult is not null) { object moduleName = null; if (!IsConstantValueVisitor.IsConstant(moduleNameBindingResult.Value, out moduleName, true, true)) @@ -943,26 +927,26 @@ private static ParseError[] ImportResourcePostParse(DynamicKeywordStatementAst a if (LanguagePrimitives.TryConvertTo(moduleName, out moduleSpecifications)) { // if resourceNames are specified then we can not specify multiple modules name - if (moduleSpecifications != null && moduleSpecifications.Length > 1 && resourceNames != null) + if (moduleSpecifications is not null && moduleSpecifications.Length > 1 && resourceNames is not null) { errorList.Add(new ParseError(moduleNameBindingResult.Value.Extent, "ImportDscResourceMultipleModulesNotSupportedWithName", string.Format(CultureInfo.CurrentCulture, ParserStrings.ImportDscResourceMultipleModulesNotSupportedWithName))); } // if moduleversion is specified then we can not specify multiple modules name - if (moduleSpecifications != null && moduleSpecifications.Length > 1 && moduleVersion != null) + if (moduleSpecifications is not null && moduleSpecifications.Length > 1 && moduleVersion is not null) { errorList.Add(new ParseError(moduleNameBindingResult.Value.Extent, "ImportDscResourceMultipleModulesNotSupportedWithVersion", string.Format(CultureInfo.CurrentCulture, ParserStrings.ImportDscResourceNeedParams))); } // if moduleversion is specified then we can not specify another version in modulespecification object of ModuleName - if (moduleSpecifications != null && (moduleSpecifications[0].Version != null || moduleSpecifications[0].MaximumVersion != null) && moduleVersion != null) + if (moduleSpecifications is not null && (moduleSpecifications[0].Version is not null || moduleSpecifications[0].MaximumVersion is not null) && moduleVersion is not null) { errorList.Add(new ParseError(moduleNameBindingResult.Value.Extent, "ImportDscResourceMultipleModuleVersionsNotSupported", string.Format(CultureInfo.CurrentCulture, ParserStrings.ImportDscResourceNeedParams))); } // If moduleVersion is specified we have only one module Name in valid scenario // So update it's version property in module specification object that will be used to load modules - if (moduleSpecifications != null && moduleSpecifications[0].Version == null && moduleSpecifications[0].MaximumVersion == null && moduleVersion != null) + if (moduleSpecifications is not null && moduleSpecifications[0].Version is null && moduleSpecifications[0].MaximumVersion is null && moduleVersion is not null) { moduleSpecifications[0].Version = moduleVersion; } @@ -988,11 +972,11 @@ private static ParseError[] ImportResourceCheckSemantics(DynamicKeywordStatement List errorList = null; var keywordAst = Ast.GetAncestorAst(ast.Parent); - while (keywordAst != null) + while (keywordAst is not null) { if (keywordAst.Keyword.Keyword.Equals("Node")) { - if (errorList == null) + if (errorList is null) { errorList = new List(); } @@ -1004,7 +988,7 @@ private static ParseError[] ImportResourceCheckSemantics(DynamicKeywordStatement keywordAst = Ast.GetAncestorAst(keywordAst.Parent); } - if (errorList != null) + if (errorList is not null) { return errorList.ToArray(); } @@ -1038,7 +1022,7 @@ private static ParseError[] CheckMandatoryPropertiesPresent(DynamicKeywordStatem } } - if (hashtableAst == null) + if (hashtableAst is null) { // nothing to validate return null; @@ -1050,7 +1034,7 @@ private static ParseError[] CheckMandatoryPropertiesPresent(DynamicKeywordStatem if (IsConstantValueVisitor.IsConstant(pair.Item1, out evalResultObject, forAttribute: false, forRequires: false)) { var presentName = evalResultObject as string; - if (presentName != null) + if (presentName is not null) { if (mandatoryPropertiesNames.Remove(presentName) && mandatoryPropertiesNames.Count == 0) { @@ -1101,19 +1085,19 @@ internal static void LoadResourcesFromModuleInImportResourcePostParse( { // get all required modules var modules = new Collection(); - if (moduleSpecifications != null) + if (moduleSpecifications is not null) { foreach (var moduleToImport in moduleSpecifications) { bool foundModule = false; var moduleInfos = ModuleCmdletBase.GetModuleIfAvailable(moduleToImport); - if (moduleInfos.Count >= 1 && (moduleToImport.Version != null || moduleToImport.Guid != null)) + if (moduleInfos.Count >= 1 && (moduleToImport.Version is not null || moduleToImport.Guid is not null)) { foreach (var psModuleInfo in moduleInfos) { if ((moduleToImport.Guid.HasValue && moduleToImport.Guid.Equals(psModuleInfo.Guid)) || - (moduleToImport.Version != null && + (moduleToImport.Version is not null && moduleToImport.Version.Equals(psModuleInfo.Version))) { modules.Add(psModuleInfo); @@ -1151,7 +1135,7 @@ internal static void LoadResourcesFromModuleInImportResourcePostParse( } } } - else if (resourceNames != null) + else if (resourceNames is not null) { // Lookup the required resources under available PowerShell modules when modulename is not specified // Make sure that this is not a circular import/parsing @@ -1170,7 +1154,7 @@ internal static void LoadResourcesFromModuleInImportResourcePostParse( // When ModuleName only specified, we need to import all resources from that module var resourcesToImport = new List(); - if (resourceNames == null || resourceNames.Length == 0) + if (resourceNames is null || resourceNames.Length == 0) { resourcesToImport.Add("*"); } @@ -1183,7 +1167,7 @@ internal static void LoadResourcesFromModuleInImportResourcePostParse( { var resourcesFound = new List(); var exceptionList = new System.Collections.ObjectModel.Collection(); - LoadPowerShellClassResourcesFromModule(moduleInfo, moduleInfo, resourcesToImport, resourcesFound, exceptionList, null, true, scriptExtent); + LoadPowerShellClassResourcesFromModule(primaryModuleInfo: moduleInfo, moduleInfo: moduleInfo, resourcesToImport: resourcesToImport, resourcesFound: resourcesFound, errorList: exceptionList, functionsToDefine: null, recurse: true, extent: scriptExtent); foreach (Exception ex in exceptionList) { errorList.Add(new ParseError(scriptExtent, "ClassResourcesLoadingFailed", ex.Message)); @@ -1204,7 +1188,7 @@ internal static void LoadResourcesFromModuleInImportResourcePostParse( { foreach (var resourceNameToImport in resourcesToImport) { - if (!resourceNameToImport.Contains("*")) + if (!resourceNameToImport.Contains('*')) { errorList.Add(new ParseError(scriptExtent, "DscResourcesNotFoundDuringParsing", string.Format(CultureInfo.CurrentCulture, ParserStrings.DscResourcesNotFoundDuringParsing, resourceNameToImport))); } @@ -1222,7 +1206,7 @@ private static void LoadPowerShellClassResourcesFromModule( bool recurse = true, IScriptExtent extent = null) { - if (primaryModuleInfo._declaredDscResourceExports == null || primaryModuleInfo._declaredDscResourceExports.Count == 0) + if (primaryModuleInfo._declaredDscResourceExports is null || primaryModuleInfo._declaredDscResourceExports.Count == 0) { return; } @@ -1234,11 +1218,11 @@ private static void LoadPowerShellClassResourcesFromModule( else { string scriptPath = null; - if (moduleInfo.RootModule != null) + if (moduleInfo.RootModule is not null) { scriptPath = Path.Join(moduleInfo.ModuleBase, moduleInfo.RootModule); } - else if (moduleInfo.Path != null) + else if (moduleInfo.Path is not null) { scriptPath = moduleInfo.Path; } @@ -1246,7 +1230,7 @@ private static void LoadPowerShellClassResourcesFromModule( LoadPowerShellClassResourcesFromModule(scriptPath, primaryModuleInfo, resourcesToImport, resourcesFound, functionsToDefine, errorList, extent); } - if (moduleInfo.NestedModules != null && recurse) + if (moduleInfo.NestedModules is not null && recurse) { foreach (var nestedModule in moduleInfo.NestedModules) { @@ -1300,7 +1284,7 @@ private static List ProcessEmbeddedInstanceTypes(List embedded { visitedInstances.Add(batchedTypes[i]); var typeAst = batchedTypes[i] as TypeDefinitionAst; - if (typeAst != null) + if (typeAst is not null) { var classes = GenerateJsonClassesForAst(typeAst, embeddedInstanceTypes); result.AddRange(classes); @@ -1330,7 +1314,7 @@ internal static string MapTypeNameToMofType(ITypeName typeName, string memberNam { TypeName propTypeName; var arrayTypeName = typeName as ArrayTypeName; - if (arrayTypeName != null) + if (arrayTypeName is not null) { isArrayType = true; propTypeName = arrayTypeName.ElementType as TypeName; @@ -1341,7 +1325,7 @@ internal static string MapTypeNameToMofType(ITypeName typeName, string memberNam propTypeName = typeName as TypeName; } - if (propTypeName == null || propTypeName._typeDefinitionAst == null) + if (propTypeName is null || propTypeName._typeDefinitionAst is null) { throw new NotSupportedException(string.Format( CultureInfo.InvariantCulture, @@ -1393,13 +1377,13 @@ private static PSObject[] GenerateJsonClassesForAst(TypeDefinitionAst typeAst, L var b = bases.Dequeue(); var tc = b as TypeConstraintAst; - if (tc != null) + if (tc is not null) { b = tc.TypeName.GetReflectionType(); - if (b == null) + if (b is null) { var td = tc.TypeName as TypeName; - if (td != null && td._typeDefinitionAst != null) + if (td is not null && td._typeDefinitionAst is not null) { ProcessMembers(embeddedInstanceTypes, td._typeDefinitionAst, className); foreach (var b1 in td._typeDefinitionAst.BaseTypes) @@ -1436,7 +1420,7 @@ private static List ProcessMembers(List embeddedInstanceTypes, continue; } - var memberType = property.PropertyType == null + var memberType = property.PropertyType is null ? typeof(object) : property.PropertyType.TypeName.GetReflectionType(); @@ -1477,9 +1461,9 @@ private static List ProcessMembers(List embeddedInstanceTypes, foreach (var attr in attributes) { var dscProperty = attr as DscPropertyAttribute; - if (dscProperty != null) + if (dscProperty is not null) { - if (attributesPSObject == null) + if (attributesPSObject is null) { attributesPSObject = new PSObject(); } @@ -1503,9 +1487,9 @@ private static List ProcessMembers(List embeddedInstanceTypes, } var validateSet = attr as ValidateSetAttribute; - if (validateSet != null) + if (validateSet is not null) { - if (attributesPSObject == null) + if (attributesPSObject is null) { attributesPSObject = new PSObject(); } @@ -1517,7 +1501,7 @@ private static List ProcessMembers(List embeddedInstanceTypes, } } - if (attributesPSObject != null) + if (attributesPSObject is not null) { propertyObject.Properties.Add(new PSNoteProperty(@"Qualifiers", attributesPSObject)); } @@ -1547,9 +1531,9 @@ private static bool GetResourceDefinitionsFromModule(string fileName, out IEnume ParseError[] errors; var ast = Parser.ParseFile(fileName, out tokens, out errors); - if (errors != null && errors.Length > 0) + if (errors is not null && errors.Length > 0) { - if (errorList != null && extent != null) + if (errorList is not null && extent is not null) { List errorMessages = new List(); foreach (var error in errors) @@ -1569,7 +1553,7 @@ private static bool GetResourceDefinitionsFromModule(string fileName, out IEnume n => { var typeAst = n as TypeDefinitionAst; - if (typeAst != null) + if (typeAst is not null) { for (int i = 0; i < typeAst.Attributes.Count; i++) { @@ -1735,7 +1719,7 @@ internal static string MapTypeToMofType(Type type, string memberName, string cla else if (!type.IsAbstract) { // Must have default constructor, at least 1 public property/field, and no base classes - if (type.GetConstructor(Type.EmptyTypes) == null) + if (type.GetConstructor(Type.EmptyTypes) is null) { missingDefaultConstructor = true; } @@ -1812,7 +1796,7 @@ private static void ProcessJsonForDynamicKeywords( DscClassCacheEntry existingCacheEntry = null; if (ClassCache.TryGetValue(moduleQualifiedResourceName, out existingCacheEntry)) { - if (errors != null) + if (errors is not null) { PSInvalidOperationException e = PSTraceSource.NewInvalidOperationException(ParserStrings.DuplicateCimClassDefinition, className, module.Path, existingCacheEntry.ModulePath); e.SetErrorId("DuplicateCimClassDefinition"); @@ -2122,7 +2106,7 @@ private static StringBuilder FormatCimPropertyType(DynamicKeywordProperty prop, } // Do the property values map - if (prop.ValueMap != null && prop.ValueMap.Count > 0) + if (prop.ValueMap is not null && prop.ValueMap.Count > 0) { formattedTypeString.Append(" { " + string.Join(" | ", prop.ValueMap.Keys.OrderBy(x => x)) + " }"); }