ConfigurationUnitProcessorBase.cs (12718B)
1 // ----------------------------------------------------------------------------- 2 // <copyright file="ConfigurationUnitProcessorBase.cs" company="Microsoft Corporation"> 3 // Copyright (c) Microsoft Corporation. Licensed under the MIT License. 4 // </copyright> 5 // ----------------------------------------------------------------------------- 6 7 namespace Microsoft.Management.Configuration.Processor.Unit 8 { 9 using System; 10 using System.Collections.Generic; 11 using System.ComponentModel; 12 using System.Runtime.CompilerServices; 13 using Microsoft.Management.Configuration; 14 using Microsoft.Management.Configuration.Processor.Exceptions; 15 using Microsoft.Management.Configuration.Processor.Extensions; 16 using Microsoft.Management.Configuration.Processor.Factory; 17 using Microsoft.Management.Configuration.Processor.Helpers; 18 using Windows.Foundation.Collections; 19 20 /// <summary> 21 /// IConfigurationUnitProcessor base implementation. 22 /// </summary> 23 internal abstract partial class ConfigurationUnitProcessorBase 24 { 25 private readonly ConfigurationUnitInternal unitInternal; 26 private readonly bool isLimitMode; 27 28 private bool isTestInvoked = false; 29 private bool isApplyInvoked = false; 30 31 /// <summary> 32 /// Initializes a new instance of the <see cref="ConfigurationUnitProcessorBase"/> class. 33 /// </summary> 34 /// <param name="unitInternal">UnitInternal.</param> 35 /// <param name="isLimitMode">Whether it is under limit mode.</param> 36 internal ConfigurationUnitProcessorBase(ConfigurationUnitInternal unitInternal, bool isLimitMode) 37 { 38 this.unitInternal = unitInternal; 39 this.isLimitMode = isLimitMode; 40 } 41 42 /// <summary> 43 /// Gets the configuration unit that the processor was created for. 44 /// </summary> 45 public ConfigurationUnit Unit => this.unitInternal.Unit; 46 47 /// <summary> 48 /// Gets or initializes the set processor factory. 49 /// </summary> 50 internal ConfigurationSetProcessorFactoryBase? SetProcessorFactory { get; init; } 51 52 /// <summary> 53 /// Gets the internal configuration unit. 54 /// </summary> 55 protected ConfigurationUnitInternal UnitInternal => this.unitInternal; 56 57 /// <summary> 58 /// Gets the current system state for the configuration unit. 59 /// Calls Get on the DSC resource. 60 /// </summary> 61 /// <returns>A <see cref="IGetSettingsResult"/>.</returns> 62 public IGetSettingsResult GetSettings() 63 { 64 this.OnDiagnostics(DiagnosticLevel.Verbose, $"Invoking `Get` for resource: {this.unitInternal.QualifiedName}..."); 65 66 this.CheckLimitMode(ConfigurationUnitIntent.Inform); 67 var result = new GetSettingsResult(this.Unit); 68 69 try 70 { 71 result.Settings = this.GetSettingsInternal(); 72 } 73 catch (Exception e) 74 { 75 this.ExtractExceptionInformation(e, result.InternalResult); 76 } 77 78 this.OnDiagnostics(DiagnosticLevel.Verbose, $"... done invoking `Get`."); 79 return result; 80 } 81 82 /// <summary> 83 /// Determines if the system is already in the state described by the configuration unit. 84 /// Calls Test on the DSC resource. 85 /// </summary> 86 /// <returns>A <see cref="ITestSettingsResult"/>.</returns> 87 public ITestSettingsResult TestSettings() 88 { 89 this.OnDiagnostics(DiagnosticLevel.Verbose, $"Invoking `Test` for resource: {this.unitInternal.QualifiedName}..."); 90 91 if (this.Unit.Intent == ConfigurationUnitIntent.Inform) 92 { 93 this.OnDiagnostics(DiagnosticLevel.Error, "`Test` should not be called on a unit with intent of `Inform`"); 94 throw new NotSupportedException(); 95 } 96 97 this.CheckLimitMode(ConfigurationUnitIntent.Assert); 98 var result = new TestSettingsResult(this.Unit); 99 result.TestResult = ConfigurationTestResult.Failed; 100 try 101 { 102 bool testResult = this.TestSettingsInternal(); 103 104 result.TestResult = testResult ? ConfigurationTestResult.Positive : ConfigurationTestResult.Negative; 105 } 106 catch (Exception e) 107 { 108 this.ExtractExceptionInformation(e, result.InternalResult); 109 } 110 111 this.OnDiagnostics(DiagnosticLevel.Verbose, $"... done invoking `Test`."); 112 return result; 113 } 114 115 /// <summary> 116 /// Applies the state described in the configuration unit. 117 /// Calls Set in the DSC resource. 118 /// </summary> 119 /// <returns>A <see cref="IApplySettingsResult"/>.</returns> 120 public IApplySettingsResult ApplySettings() 121 { 122 this.OnDiagnostics(DiagnosticLevel.Verbose, $"Invoking `Apply` for resource: {this.unitInternal.QualifiedName}..."); 123 124 if (this.Unit.Intent == ConfigurationUnitIntent.Inform || 125 this.Unit.Intent == ConfigurationUnitIntent.Assert) 126 { 127 this.OnDiagnostics(DiagnosticLevel.Error, $"`Apply` should not be called on a unit with intent of `{this.Unit.Intent}`"); 128 throw new NotSupportedException(); 129 } 130 131 this.CheckLimitMode(ConfigurationUnitIntent.Apply); 132 var result = new ApplySettingsResult(this.Unit); 133 try 134 { 135 result.RebootRequired = this.ApplySettingsInternal(); 136 } 137 catch (Exception e) 138 { 139 this.ExtractExceptionInformation(e, result.InternalResult); 140 } 141 142 this.OnDiagnostics(DiagnosticLevel.Verbose, $"... done invoking `Apply`."); 143 return result; 144 } 145 146 /// <summary> 147 /// Gets the current settings for all the instances of a configuration unit. 148 /// </summary> 149 /// <returns>A <see cref="IGetAllSettingsResult"/>.</returns> 150 public IGetAllSettingsResult GetAllSettings() 151 { 152 this.OnDiagnostics(DiagnosticLevel.Verbose, $"Invoking `GetAllSettings` for resource: {this.unitInternal.QualifiedName}..."); 153 154 this.CheckLimitMode(ConfigurationUnitIntent.Inform); 155 var result = new GetAllSettingsResult(this.Unit); 156 157 try 158 { 159 result.Settings = this.GetAllSettingsInternal(); 160 } 161 catch (Exception e) 162 { 163 this.ExtractExceptionInformation(e, result.InternalResult); 164 } 165 166 this.OnDiagnostics(DiagnosticLevel.Verbose, $"... done invoking `GetAllSettings`."); 167 return result; 168 } 169 170 /// <summary> 171 /// Gets all configuration units for the given unit type. 172 /// Returned units may be of types other than the one passed in. 173 /// </summary> 174 /// <returns>A <see cref="IGetAllUnitsResult"/>.</returns> 175 public IGetAllUnitsResult GetAllUnits() 176 { 177 this.OnDiagnostics(DiagnosticLevel.Verbose, $"Invoking `GetAllUnits` for resource: {this.unitInternal.QualifiedName}..."); 178 179 this.CheckLimitMode(ConfigurationUnitIntent.Inform); 180 var result = new GetAllUnitsResult(this.Unit); 181 182 try 183 { 184 result.Units = this.GetAllUnitsInternal(); 185 } 186 catch (Exception e) 187 { 188 this.ExtractExceptionInformation(e, result.InternalResult); 189 } 190 191 this.OnDiagnostics(DiagnosticLevel.Verbose, $"... done invoking `GetAllUnits`."); 192 return result; 193 } 194 195 /// <summary> 196 /// Gets the current settings. 197 /// </summary> 198 /// <returns>The current settings.</returns> 199 protected abstract ValueSet GetSettingsInternal(); 200 201 /// <summary> 202 /// Tests the current settings. 203 /// </summary> 204 /// <returns>A boolean indicating whether the settings are in the desired state.</returns> 205 protected abstract bool TestSettingsInternal(); 206 207 /// <summary> 208 /// Applies the desired settings. 209 /// </summary> 210 /// <returns>A boolean indicating whether a reboot is required.</returns> 211 protected abstract bool ApplySettingsInternal(); 212 213 /// <summary> 214 /// Gets the current settings for all the instances of a configuration unit. 215 /// Derive from IGetAllSettingsConfigurationUnitProcessor and implement an override to support this. 216 /// </summary> 217 /// <returns>The settings as ValueSets.</returns> 218 protected virtual IList<ValueSet>? GetAllSettingsInternal() 219 { 220 throw new NotImplementedException("Configuration unit processor did not implement GetAllSettingsInternal."); 221 } 222 223 /// <summary> 224 /// Gets all configuration units for the given unit type. 225 /// Returned units may be of types other than the one passed in. 226 /// Derive from IGetAllUnitsConfigurationUnitProcessor and implement an override to support this. 227 /// </summary> 228 /// <returns>The configuration units.</returns> 229 protected virtual IList<ConfigurationUnit>? GetAllUnitsInternal() 230 { 231 throw new NotImplementedException("Configuration unit processor did not implement GetAllUnitsInternal."); 232 } 233 234 /// <summary> 235 /// Sends diagnostics if appropriate. 236 /// </summary> 237 /// <param name="level">The level of this diagnostic message.</param> 238 /// <param name="message">The diagnostic message.</param> 239 protected void OnDiagnostics(DiagnosticLevel level, string message) 240 { 241 this.SetProcessorFactory?.OnDiagnostics(level, message); 242 } 243 244 private void ExtractExceptionInformation(Exception e, ConfigurationUnitResultInformation resultInformation) 245 { 246 this.OnDiagnostics(DiagnosticLevel.Verbose, e.ToString()); 247 248 IConfigurationUnitResultException? configurationUnitResultException = e as IConfigurationUnitResultException; 249 if (configurationUnitResultException != null) 250 { 251 resultInformation.ResultCode = e; 252 resultInformation.Description = configurationUnitResultException.Description; 253 resultInformation.Details = configurationUnitResultException.Details; 254 resultInformation.ResultSource = configurationUnitResultException.ResultSource; 255 } 256 else 257 { 258 var inner = e.GetMostInnerException(); 259 resultInformation.ResultCode = inner; 260 resultInformation.Description = e.Message; 261 resultInformation.Details = e.ToString(); 262 resultInformation.ResultSource = ConfigurationUnitResultSource.Internal; 263 } 264 } 265 266 [MethodImpl(MethodImplOptions.Synchronized)] 267 private void CheckLimitMode(ConfigurationUnitIntent intent) 268 { 269 if (!this.isLimitMode) 270 { 271 return; 272 } 273 274 if (intent == ConfigurationUnitIntent.Unknown) 275 { 276 throw new InvalidEnumArgumentException(nameof(ConfigurationUnitIntent.Unknown)); 277 } 278 279 if (intent == ConfigurationUnitIntent.Assert) 280 { 281 if (this.isTestInvoked) 282 { 283 this.OnDiagnostics(DiagnosticLevel.Error, "TestSettings is already invoked in limit mode."); 284 throw new InvalidOperationException("TestSettings is already invoked in limit mode."); 285 } 286 else 287 { 288 this.isTestInvoked = true; 289 } 290 } 291 292 if (intent == ConfigurationUnitIntent.Apply) 293 { 294 if (this.isApplyInvoked) 295 { 296 this.OnDiagnostics(DiagnosticLevel.Error, "ApplySettings is already invoked in limit mode."); 297 throw new InvalidOperationException("ApplySettings is already invoked in limit mode."); 298 } 299 else 300 { 301 this.isApplyInvoked = true; 302 } 303 } 304 305 // Get is always allowed now. 306 } 307 } 308 }