winget-cli

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

DscResourcesMap.cs (9224B)


      1 // -----------------------------------------------------------------------------
      2 // <copyright file="DscResourcesMap.cs" company="Microsoft Corporation">
      3 //     Copyright (c) Microsoft Corporation. Licensed under the MIT License.
      4 // </copyright>
      5 // -----------------------------------------------------------------------------
      6 
      7 namespace Microsoft.Management.Configuration.Processor.PowerShell.Helpers
      8 {
      9     using System;
     10     using System.Collections.Generic;
     11     using System.Linq;
     12     using Microsoft.Management.Configuration.Processor.Helpers;
     13     using Microsoft.Management.Configuration.Processor.PowerShell.DscResourcesInfo;
     14 
     15     /// <summary>
     16     /// DscResources in the system. Get-DscResource looks the entire PSModulePath to find resources every time, so
     17     /// calling it multiples times is expensive. This class will store them in memory per configuration set.
     18     /// The resources are stored in a dictionary in the following hierarchy.
     19     /// - Module name
     20     ///   - Resource name
     21     ///     - Module Version
     22     ///       - DscResourceInfo obj.
     23     /// There are some resources that don't have a module name. These are modules that live under
     24     /// C:\WINDOWS\system32\WindowsPowershell\v1.0\Modules\PsDesiredStateConfiguration\DscResources.
     25     /// This is not currently used. For now, we let PowerShell handle all the versioning. In order to avoid
     26     /// calls to Get-DscResource every time a unit is set, this class needs to be updated to handle min and max
     27     /// version. As it is, it just does RequiredVersion.
     28     /// </summary>
     29     internal sealed class DscResourcesMap
     30     {
     31         private readonly Dictionary<string, ModuleDscResources> resources = new Dictionary<string, ModuleDscResources>();
     32 
     33         /// <summary>
     34         /// Initializes a new instance of the <see cref="DscResourcesMap"/> class.
     35         /// </summary>
     36         /// <param name="resources">DSC Resources.</param>
     37         public DscResourcesMap(IReadOnlyList<DscResourceInfoInternal> resources)
     38         {
     39             foreach (var resource in resources)
     40             {
     41                 if (string.IsNullOrEmpty(resource.Name))
     42                 {
     43                     throw new ArgumentException(nameof(resource.Name));
     44                 }
     45 
     46                 if (resource.Version is null)
     47                 {
     48                     throw new ArgumentException(nameof(resource.Version));
     49                 }
     50 
     51                 this.Insert(resource);
     52             }
     53         }
     54 
     55         /// <summary>
     56         /// Looks for a DSC resource in the specified module.
     57         /// </summary>
     58         /// <param name="dscResourceName">DSC resource name.</param>
     59         /// <param name="moduleName">Optional module name.</param>
     60         /// <param name="version">Optional module version.</param>
     61         /// <returns>DscResourceInfoInternal. Null if not found.</returns>
     62         public DscResourceInfoInternal? GetResource(string dscResourceName, string? moduleName, Version? version)
     63         {
     64             string normalizedResourceName = StringHelpers.Normalize(dscResourceName);
     65             string? normalizedModuleName = null;
     66             if (moduleName is not null)
     67             {
     68                 normalizedModuleName = StringHelpers.Normalize(moduleName);
     69             }
     70 
     71             if (normalizedModuleName is null)
     72             {
     73                 foreach (var module in this.resources.Values)
     74                 {
     75                     var resourceInfo = module.GetDscResourceInfo(normalizedResourceName, version);
     76                     if (resourceInfo is not null)
     77                     {
     78                         return resourceInfo;
     79                     }
     80                 }
     81             }
     82             else
     83             {
     84                 if (this.resources.TryGetValue(normalizedModuleName, out ModuleDscResources? module))
     85                 {
     86                     return module.GetDscResourceInfo(normalizedResourceName, version);
     87                 }
     88             }
     89 
     90             return null;
     91         }
     92 
     93         /// <summary>
     94         /// Does a DSC resource exists.
     95         /// </summary>
     96         /// <param name="dscResourceName">DSC resource name.</param>
     97         /// <param name="moduleName">Optional module name.</param>
     98         /// <param name="version">Optional module version.</param>
     99         /// <returns>True if resource exists.</returns>
    100         public bool Exists(string dscResourceName, string? moduleName, Version? version)
    101         {
    102             return this.GetResource(dscResourceName, moduleName, version) != null;
    103         }
    104 
    105         private void Insert(DscResourceInfoInternal dscResourceInfo)
    106         {
    107             string? normalizedModuleName = dscResourceInfo.NormalizedModuleName;
    108 
    109             // There are some resources that doesn't have module, use empty as key.
    110             string moduleKey = string.Empty;
    111             if (normalizedModuleName is not null)
    112             {
    113                 moduleKey = normalizedModuleName;
    114             }
    115 
    116             if (!this.resources.ContainsKey(moduleKey))
    117             {
    118                 this.resources.Add(moduleKey, new ModuleDscResources(moduleKey, dscResourceInfo));
    119             }
    120             else
    121             {
    122                 this.resources[moduleKey].AddResource(moduleKey, dscResourceInfo);
    123             }
    124         }
    125 
    126         /// <summary>
    127         /// Represents a map versions for a DscResource.
    128         /// </summary>
    129         private class DscResourceVersions : Dictionary<Version, DscResourceInfoInternal>
    130         {
    131             private readonly string resourceName;
    132 
    133             public DscResourceVersions(DscResourceInfoInternal dscResourceInfo)
    134             {
    135                 this.resourceName = dscResourceInfo.NormalizedName;
    136                 this.AddVersion(dscResourceInfo);
    137             }
    138 
    139             /// <summary>
    140             /// Adds a resource version.
    141             /// </summary>
    142             /// <param name="dscResourceInfo">DscResourceInfo.</param>
    143             public void AddVersion(DscResourceInfoInternal dscResourceInfo)
    144             {
    145                 if (this.resourceName != dscResourceInfo.NormalizedName)
    146                 {
    147                     throw new ArgumentException(dscResourceInfo.NormalizedName);
    148                 }
    149 
    150                 // There are some system resources without version or module name.
    151                 if (dscResourceInfo.Version is null)
    152                 {
    153                     this.Add(new Version(), dscResourceInfo);
    154                 }
    155                 else
    156                 {
    157                     // Get-DscResource will fail if the same module with same
    158                     // resources and same version exists in the module path
    159                     // so this shouldn't happen. Either way, we should throw there or here.
    160                     this.Add(dscResourceInfo.Version, dscResourceInfo);
    161                 }
    162             }
    163 
    164             public DscResourceInfoInternal? GetDscResourceInfo(Version? version)
    165             {
    166                 if (version is null)
    167                 {
    168                     // Sort keys, get the latest.
    169                     var newestVersion = this.Keys.OrderByDescending(v => v).First();
    170                     return this[newestVersion];
    171                 }
    172 
    173                 if (this.TryGetValue(version, out DscResourceInfoInternal? resourceInfo))
    174                 {
    175                     return resourceInfo;
    176                 }
    177 
    178                 return null;
    179             }
    180         }
    181 
    182         /// <summary>
    183         /// Represent a map of resources for a module.
    184         /// </summary>
    185         private class ModuleDscResources : Dictionary<string, DscResourceVersions>
    186         {
    187             private readonly string normalizedModuleName;
    188 
    189             public ModuleDscResources(string normalizedModuleName, DscResourceInfoInternal dscResourceInfo)
    190             {
    191                 this.normalizedModuleName = normalizedModuleName;
    192                 this.AddResource(this.normalizedModuleName, dscResourceInfo);
    193             }
    194 
    195             public void AddResource(string normalizedModuleName, DscResourceInfoInternal dscResourceInfo)
    196             {
    197                 if (this.normalizedModuleName != normalizedModuleName)
    198                 {
    199                     throw new ArgumentException(nameof(normalizedModuleName));
    200                 }
    201 
    202                 var normalizedResourceName = dscResourceInfo.NormalizedName;
    203                 if (!this.ContainsKey(normalizedResourceName))
    204                 {
    205                     this.Add(normalizedResourceName, new DscResourceVersions(dscResourceInfo));
    206                 }
    207                 else
    208                 {
    209                     this[normalizedResourceName].AddVersion(dscResourceInfo);
    210                 }
    211             }
    212 
    213             public DscResourceInfoInternal? GetDscResourceInfo(string dscResourceName, Version? version)
    214             {
    215                 if (this.TryGetValue(dscResourceName, out DscResourceVersions? versions))
    216                 {
    217                     return versions.GetDscResourceInfo(version);
    218                 }
    219 
    220                 return null;
    221             }
    222         }
    223     }
    224 }