/// <summary> /// Creates a plugin from the discovered plugin. /// We firstly check the cache for the operation claims for the given request key. /// If there is a valid cache entry, and it does contain the requested operation claim, then we start the plugin, and if need be update the cache value itself. /// If there is a valid cache entry, and it does NOT contain the requested operation claim, then we return a null. /// If there is no valid cache entry or an invalid one, we start the plugin as normally, return an active plugin even if the requested claim is not available, and write a cache entry. /// </summary> /// <param name="result">plugin discovery result</param> /// <param name="requestedOperationClaim">The requested operation claim</param> /// <param name="requestKey">plugin request key</param> /// <param name="packageSourceRepository">package source repository</param> /// <param name="serviceIndex">service index</param> /// <param name="cancellationToken">cancellation token</param> /// <returns>A plugin creation result, null if the requested plugin cannot handle the given operation claim</returns> private async Task <Tuple <bool, PluginCreationResult> > TryCreatePluginAsync( PluginDiscoveryResult result, OperationClaim requestedOperationClaim, PluginRequestKey requestKey, string packageSourceRepository, JObject serviceIndex, CancellationToken cancellationToken) { PluginCreationResult pluginCreationResult = null; var cacheEntry = new PluginCacheEntry(_pluginsCacheDirectory.Value, result.PluginFile.Path, requestKey.PackageSourceRepository); return(await ConcurrencyUtilities.ExecuteWithFileLockedAsync( cacheEntry.CacheFileName, action : async lockedToken => { if (cacheEntry.OperationClaims == null || cacheEntry.OperationClaims.Contains(requestedOperationClaim)) { if (result.PluginFile.State.Value == PluginFileState.Valid) { var plugin = await _pluginFactory.GetOrCreateAsync( result.PluginFile.Path, PluginConstants.PluginArguments, new RequestHandlers(), _connectionOptions, cancellationToken); var utilities = await PerformOneTimePluginInitializationAsync(plugin, cancellationToken); // We still make the GetOperationClaims call even if we have the operation claims cached. This is a way to self-update the cache. var operationClaims = await _pluginOperationClaims.GetOrAdd( requestKey, key => new Lazy <Task <IReadOnlyList <OperationClaim> > >(() => GetPluginOperationClaimsAsync( plugin, packageSourceRepository, serviceIndex, cancellationToken))).Value; if (!EqualityUtility.SequenceEqualWithNullCheck(operationClaims, cacheEntry.OperationClaims)) { cacheEntry.OperationClaims = operationClaims; await cacheEntry.UpdateCacheFileAsync(); } pluginCreationResult = new PluginCreationResult( plugin, utilities.Value, operationClaims); } else { pluginCreationResult = new PluginCreationResult(result.Message); } } return new Tuple <bool, PluginCreationResult>(pluginCreationResult != null, pluginCreationResult); }, token : cancellationToken )); }
public void PluginCacheEntry_DoesNotThrowWithNoFile() { using (var testDirectory = TestDirectory.Create()) { var entry = new PluginCacheEntry(testDirectory.Path, "a", "b"); entry.LoadFromFile(); Assert.Null(entry.OperationClaims); } }
public void PluginCacheEntry_UsesShorterPaths() { using (var testDirectory = TestDirectory.Create()) { var pluginPath = @"C:\Users\Roki2\.nuget\plugins\netfx\CredentialProvider.Microsoft\CredentialProvider.Microsoft.exe"; var url = @"https:\\nugetsspecialfeed.pkgs.visualstudio.com\packaging\ea8caa50-9cf8-4ed7-b410-5bca3b71ec1c\nuget\v3\index.json"; var entry = new PluginCacheEntry(testDirectory.Path, pluginPath, url); entry.LoadFromFile(); Assert.Equal(86, entry.CacheFileName.Length - testDirectory.Path.Length); // This makes it about as long as http cache which is more important. // The http cache is 40 + 1 + [1,32] + packageName Assert.True(200 > entry.CacheFileName.Length, "The cache file should be short"); } }
public async Task PluginCacheEntry_DoesNotDeleteAnOpenedFile() { var list = new List <OperationClaim>() { OperationClaim.Authentication }; using (var testDirectory = TestDirectory.Create()) { var entry = new PluginCacheEntry(testDirectory.Path, "a", "b"); entry.LoadFromFile(); entry.OperationClaims = list; await entry.UpdateCacheFileAsync(); var CacheFileName = Path.Combine( Path.Combine(testDirectory.Path, CachingUtility.RemoveInvalidFileNameChars(CachingUtility.ComputeHash("a", false))), CachingUtility.RemoveInvalidFileNameChars(CachingUtility.ComputeHash("b", false)) + ".dat"); Assert.True(File.Exists(CacheFileName)); using (var fileStream = new FileStream( CacheFileName, FileMode.Open, FileAccess.ReadWrite, FileShare.None, CachingUtility.BufferSize, useAsync: true)) { list.Add(OperationClaim.DownloadPackage); entry.OperationClaims = list; await entry.UpdateCacheFileAsync(); // this should not update } entry.LoadFromFile(); Assert.True(EqualityUtility.SequenceEqualWithNullCheck(entry.OperationClaims, new List <OperationClaim>() { OperationClaim.Authentication })); } }
public async Task PluginCacheEntry_RoundTripsValuesAsync(string[] values) { var list = new List <OperationClaim>(); foreach (var val in values) { Enum.TryParse(val, out OperationClaim result); list.Add(result); } using (var testDirectory = TestDirectory.Create()) { var entry = new PluginCacheEntry(testDirectory.Path, "a", "b"); entry.LoadFromFile(); entry.OperationClaims = list; await entry.UpdateCacheFileAsync(); var newEntry = new PluginCacheEntry(testDirectory.Path, "a", "b"); newEntry.LoadFromFile(); Assert.True(EqualityUtility.SequenceEqualWithNullCheck(entry.OperationClaims, newEntry.OperationClaims)); } }
internal PluginManagerTest( string pluginFilePath, PluginFileState pluginFileState, IReadOnlyList <OperationClaim> operationClaims, Mock <IEnvironmentVariableReader> mockEnvironmentVariableReader = null) { _testDirectory = TestDirectory.Create(); if (mockEnvironmentVariableReader != null) { _reader = mockEnvironmentVariableReader; } else { _reader = new Mock <IEnvironmentVariableReader>(MockBehavior.Strict); _reader.Setup(x => x.GetEnvironmentVariable( It.Is <string>(value => value == EnvironmentVariableConstants.PluginPaths))) .Returns(pluginFilePath); _reader.Setup(x => x.GetEnvironmentVariable( It.Is <string>(value => value == EnvironmentVariableConstants.DesktopPluginPaths))) .Returns((string)null); _reader.Setup(x => x.GetEnvironmentVariable( It.Is <string>(value => value == EnvironmentVariableConstants.CorePluginPaths))) .Returns((string)null); _reader.Setup(x => x.GetEnvironmentVariable( It.Is <string>(value => value == EnvironmentVariableConstants.RequestTimeout))) .Returns("RequestTimeout"); _reader.Setup(x => x.GetEnvironmentVariable( It.Is <string>(value => value == EnvironmentVariableConstants.IdleTimeout))) .Returns("IdleTimeout"); _reader.Setup(x => x.GetEnvironmentVariable( It.Is <string>(value => value == EnvironmentVariableConstants.HandshakeTimeout))) .Returns("HandshakeTimeout"); } _pluginDiscoverer = new Mock <IPluginDiscoverer>(MockBehavior.Strict); _pluginDiscoverer.Setup(x => x.Dispose()); _pluginDiscoverer.Setup(x => x.DiscoverAsync(It.IsAny <CancellationToken>())) .ReturnsAsync(new[] { new PluginDiscoveryResult(new PluginFile(pluginFilePath, new Lazy <PluginFileState>(() => pluginFileState))) }); _connection = new Mock <IConnection>(MockBehavior.Strict); _connection.Setup(x => x.Dispose()); _connection.SetupGet(x => x.Options) .Returns(ConnectionOptions.CreateDefault()); _connection.SetupGet(x => x.ProtocolVersion) .Returns(ProtocolConstants.CurrentVersion); _connection.Setup(x => x.SendRequestAndReceiveResponseAsync <MonitorNuGetProcessExitRequest, MonitorNuGetProcessExitResponse>( It.Is <MessageMethod>(m => m == MessageMethod.MonitorNuGetProcessExit), It.IsNotNull <MonitorNuGetProcessExitRequest>(), It.IsAny <CancellationToken>())) .ReturnsAsync(new MonitorNuGetProcessExitResponse(MessageResponseCode.Success)); _connection.Setup(x => x.SendRequestAndReceiveResponseAsync <InitializeRequest, InitializeResponse>( It.Is <MessageMethod>(m => m == MessageMethod.Initialize), It.IsNotNull <InitializeRequest>(), It.IsAny <CancellationToken>())) .ReturnsAsync(new InitializeResponse(MessageResponseCode.Success)); _connection.Setup(x => x.SendRequestAndReceiveResponseAsync <GetOperationClaimsRequest, GetOperationClaimsResponse>( It.Is <MessageMethod>(m => m == MessageMethod.GetOperationClaims), It.Is <GetOperationClaimsRequest>(g => g.PackageSourceRepository == null), It.IsAny <CancellationToken>())) .ReturnsAsync(new GetOperationClaimsResponse(operationClaims)); _plugin = new Mock <IPlugin>(MockBehavior.Strict); _plugin.Setup(x => x.Dispose()); _plugin.SetupGet(x => x.Connection) .Returns(_connection.Object); _plugin.SetupGet(x => x.Id) .Returns("id"); _factory = new Mock <IPluginFactory>(MockBehavior.Strict); _factory.Setup(x => x.Dispose()); _factory.Setup(x => x.GetOrCreateAsync( It.Is <string>(p => p == pluginFilePath), It.IsNotNull <IEnumerable <string> >(), It.IsNotNull <IRequestHandlers>(), It.IsNotNull <ConnectionOptions>(), It.IsAny <CancellationToken>())) .ReturnsAsync(_plugin.Object); PluginManager = new PluginManager( _reader.Object, new Lazy <IPluginDiscoverer>(() => _pluginDiscoverer.Object), (TimeSpan idleTimeout) => _factory.Object, new Lazy <string>(() => _testDirectory.Path)); PluginCacheEntry = new PluginCacheEntry(_testDirectory.Path, pluginFilePath, requestKey: "Source-Agnostic"); Plugin = _plugin.Object; }