private static async Task WriteForDatabase(ZipArchive archive, JsonOperationContext context, LocalEndpointClient localEndpointClient, string databaseName, string path = null) { var endpointParameters = new Dictionary <string, Microsoft.Extensions.Primitives.StringValues> { { "database", new Microsoft.Extensions.Primitives.StringValues(databaseName) } }; foreach (var route in DebugInfoPackageUtils.Routes.Where(x => x.TypeOfRoute == RouteInformation.RouteType.Databases)) { try { var entry = archive.CreateEntry(DebugInfoPackageUtils.GetOutputPathFromRouteInformation(route, path ?? databaseName)); entry.ExternalAttributes = ((int)(FilePermissions.S_IRUSR | FilePermissions.S_IWUSR)) << 16; await using (var entryStream = entry.Open()) await using (var writer = new AsyncBlittableJsonTextWriter(context, entryStream)) { using (var endpointOutput = await localEndpointClient.InvokeAndReadObjectAsync(route, context, endpointParameters)) { context.Write(writer, endpointOutput); await writer.FlushAsync(); await entryStream.FlushAsync(); } } } catch (Exception e) { await DebugInfoPackageUtils.WriteExceptionAsZipEntryAsync(e, archive, path ?? databaseName); } } }
private async Task WriteServerWide(ZipArchive archive, JsonOperationContext context, LocalEndpointClient localEndpointClient, string prefix) { //theoretically this could be parallelized, //however ZipArchive allows only one archive entry to be open concurrently foreach (var route in DebugInfoPackageUtils.Routes.Where(x => x.TypeOfRoute == RouteInformation.RouteType.None)) { var entryRoute = DebugInfoPackageUtils.GetOutputPathFromRouteInformation(route, prefix); try { var entry = archive.CreateEntry(entryRoute); entry.ExternalAttributes = ((int)(FilePermissions.S_IRUSR | FilePermissions.S_IWUSR)) << 16; await using (var entryStream = entry.Open()) await using (var writer = new AsyncBlittableJsonTextWriter(context, entryStream)) using (var endpointOutput = await localEndpointClient.InvokeAndReadObjectAsync(route, context)) { context.Write(writer, endpointOutput); await writer.FlushAsync(); await entryStream.FlushAsync(); } } catch (Exception e) { await DebugInfoPackageUtils.WriteExceptionAsZipEntryAsync(e, archive, entryRoute); } } }
private static async Task WriteLogFile(ZipArchive archive) { var prefix = $"{_serverWidePrefix}/{DateTime.UtcNow:yyyy-MM-dd H:mm:ss}.txt"; try { var entry = archive.CreateEntry(prefix, CompressionLevel.Optimal); entry.ExternalAttributes = ((int)(FilePermissions.S_IRUSR | FilePermissions.S_IWUSR)) << 16; await using (var entryStream = entry.Open()) { LoggingSource.Instance.AttachPipeSink(entryStream); await Task.Delay(15000); LoggingSource.Instance.DetachPipeSink(); await entryStream.FlushAsync(); } } catch (Exception e) { LoggingSource.Instance.DetachPipeSink(); await DebugInfoPackageUtils.WriteExceptionAsZipEntryAsync(e, archive, prefix); } }
public async Task GetInfoPackage() { var contentDisposition = $"attachment; filename={DateTime.UtcNow:yyyy-MM-dd H:mm:ss} - Database [{Database.Name}].zip"; HttpContext.Response.Headers["Content-Disposition"] = contentDisposition; using (ServerStore.ContextPool.AllocateOperationContext(out JsonOperationContext context)) { await using (var ms = new MemoryStream()) { using (var archive = new ZipArchive(ms, ZipArchiveMode.Create, true)) { var localEndpointClient = new LocalEndpointClient(Server); var endpointParameters = new Dictionary <string, Microsoft.Extensions.Primitives.StringValues> { { "database", new Microsoft.Extensions.Primitives.StringValues(Database.Name) } }; var feature = HttpContext.Features.Get <IHttpAuthenticationFeature>() as RavenServer.AuthenticateConnection; Debug.Assert(feature != null); var routes = DebugInfoPackageUtils.GetAuthorizedRoutes(feature, Database.Name) .Where(x => x.TypeOfRoute == RouteInformation.RouteType.Databases); foreach (RouteInformation route in routes) { var entryName = DebugInfoPackageUtils.GetOutputPathFromRouteInformation(route, null); try { var entry = archive.CreateEntry(entryName); entry.ExternalAttributes = ((int)(FilePermissions.S_IRUSR | FilePermissions.S_IWUSR)) << 16; await using (var entryStream = entry.Open()) await using (var writer = new AsyncBlittableJsonTextWriter(context, entryStream)) { using (var endpointOutput = await localEndpointClient.InvokeAndReadObjectAsync(route, context, endpointParameters)) { context.Write(writer, endpointOutput); await writer.FlushAsync(); await entryStream.FlushAsync(); } } } catch (Exception e) { await DebugInfoPackageUtils.WriteExceptionAsZipEntryAsync(e, archive, entryName.Replace(".json", string.Empty)); } } } ms.Position = 0; await ms.CopyToAsync(ResponseBodyStream()); } } }
internal static async Task InvokeAndWriteToArchive(ZipArchive archive, JsonOperationContext jsonOperationContext, LocalEndpointClient localEndpointClient, RouteInformation route, string path, Dictionary <string, Microsoft.Extensions.Primitives.StringValues> endpointParameters = null, CancellationToken token = default) { try { var response = await localEndpointClient.InvokeAsync(route, endpointParameters); var entryName = DebugInfoPackageUtils.GetOutputPathFromRouteInformation(route, path, response.ContentType == "text/plain" ? "txt" : "json"); var entry = archive.CreateEntry(entryName); entry.ExternalAttributes = ((int)(FilePermissions.S_IRUSR | FilePermissions.S_IWUSR)) << 16; using (var entryStream = entry.Open()) { if (response.ContentType == "text/plain") { await response.Body.CopyToAsync(entryStream, token); } else { await using (var writer = new AsyncBlittableJsonTextWriter(jsonOperationContext, entryStream)) { var endpointOutput = await jsonOperationContext.ReadForMemoryAsync(response.Body, $"read/local endpoint/{route.Path}"); jsonOperationContext.Write(writer, endpointOutput); await writer.FlushAsync(); } } await entryStream.FlushAsync(token); } } catch (OperationCanceledException) { throw; } catch (Exception e) { //precaution, ideally this exception should never be thrown if (e is InvalidStartOfObjectException) { e = new InvalidOperationException("Expected to find a blittable object as a result of debug endpoint, but found something else (see inner exception for details). This should be investigated as all RavenDB endpoints are supposed to return an object.", e); } await DebugInfoPackageUtils.WriteExceptionAsZipEntryAsync(e, archive, DebugInfoPackageUtils.GetOutputPathFromRouteInformation(route, path, null)); } }
public async Task GetClusterWideInfoPackage() { var contentDisposition = $"attachment; filename={DateTime.UtcNow:yyyy-MM-dd H:mm:ss} Cluster Wide.zip"; HttpContext.Response.Headers["Content-Disposition"] = contentDisposition; HttpContext.Response.Headers["Content-Type"] = "application/zip"; var token = CreateOperationToken(); var operationId = GetLongQueryString("operationId", false) ?? ServerStore.Operations.GetNextOperationId(); await ServerStore.Operations.AddOperation(null, "Created debug package for all cluster nodes", Operations.Operations.OperationType.DebugPackage, async _ => { using (ServerStore.ContextPool.AllocateOperationContext(out TransactionOperationContext transactionOperationContext)) using (ServerStore.ContextPool.AllocateOperationContext(out JsonOperationContext jsonOperationContext)) using (transactionOperationContext.OpenReadTransaction()) { await using (var ms = new MemoryStream()) { using (var archive = new ZipArchive(ms, ZipArchiveMode.Create, true)) { var topology = ServerStore.GetClusterTopology(transactionOperationContext); foreach (var(tag, url) in topology.AllNodes) { try { await WriteDebugInfoPackageForNodeAsync(jsonOperationContext, archive, tag, url, Server.Certificate.Certificate); } catch (Exception e) { await DebugInfoPackageUtils.WriteExceptionAsZipEntryAsync(e, archive, $"Node - [{tag}]"); } } } ms.Position = 0; await ms.CopyToAsync(ResponseBodyStream(), token.Token); } } return(null); }, operationId, token : token); }
private async Task WriteDatabaseRecord(ZipArchive archive, string databaseName, JsonOperationContext jsonOperationContext, TransactionOperationContext transactionCtx, CancellationToken token = default) { var entryName = DebugInfoPackageUtils.GetOutputPathFromRouteInformation("/database-record", databaseName, "json"); try { var entry = archive.CreateEntry(entryName); entry.ExternalAttributes = ((int)(FilePermissions.S_IRUSR | FilePermissions.S_IWUSR)) << 16; await using (var entryStream = entry.Open()) await using (var writer = new AsyncBlittableJsonTextWriter(jsonOperationContext, entryStream)) { jsonOperationContext.Write(writer, GetDatabaseRecordForDebugPackage(transactionCtx, databaseName)); await writer.FlushAsync(); await entryStream.FlushAsync(token); } } catch (Exception e) { await DebugInfoPackageUtils.WriteExceptionAsZipEntryAsync(e, archive, entryName); } }
public async Task GetClusterWideInfoPackage() { var contentDisposition = $"attachment; filename={DateTime.UtcNow:yyyy-MM-dd H:mm:ss} Cluster Wide.zip"; HttpContext.Response.Headers["Content-Disposition"] = contentDisposition; HttpContext.Response.Headers["Content-Type"] = "application/zip"; var token = CreateOperationToken(); var operationId = GetLongQueryString("operationId", false) ?? ServerStore.Operations.GetNextOperationId(); await ServerStore.Operations.AddOperation(null, "Created debug package for all cluster nodes", Operations.Operations.OperationType.DebugPackage, async _ => { using (ServerStore.ContextPool.AllocateOperationContext(out TransactionOperationContext transactionOperationContext)) using (ServerStore.ContextPool.AllocateOperationContext(out JsonOperationContext jsonOperationContext)) using (transactionOperationContext.OpenReadTransaction()) { await using (var ms = new MemoryStream()) { using (var archive = new ZipArchive(ms, ZipArchiveMode.Create, true)) { var localEndpointClient = new LocalEndpointClient(Server); await using (var localMemoryStream = new MemoryStream()) { //assuming that if the name tag is empty var nodeName = $"Node - [{ServerStore.NodeTag ?? "Empty node tag"}]"; using (var localArchive = new ZipArchive(localMemoryStream, ZipArchiveMode.Create, true)) { await WriteServerWide(localArchive, jsonOperationContext, localEndpointClient, _serverWidePrefix); await WriteForAllLocalDatabases(localArchive, jsonOperationContext, localEndpointClient); await WriteLogFile(localArchive); } localMemoryStream.Position = 0; var entry = archive.CreateEntry($"{nodeName}.zip"); entry.ExternalAttributes = ((int)(FilePermissions.S_IRUSR | FilePermissions.S_IWUSR)) << 16; await using (var entryStream = entry.Open()) { await localMemoryStream.CopyToAsync(entryStream); await entryStream.FlushAsync(); } } var databaseNames = ServerStore.Cluster.GetDatabaseNames(transactionOperationContext); var topology = ServerStore.GetClusterTopology(transactionOperationContext); //this means no databases are defined in the cluster //in this case just output server-wide endpoints from all cluster nodes if (databaseNames.Count == 0) { foreach (var tagWithUrl in topology.AllNodes) { if (tagWithUrl.Value.Contains(ServerStore.GetNodeHttpServerUrl())) { continue; } try { await WriteDebugInfoPackageForNodeAsync( jsonOperationContext, archive, tag: tagWithUrl.Key, url: tagWithUrl.Value, certificate: Server.Certificate.Certificate, databaseNames: null); } catch (Exception e) { var entryName = $"Node - [{tagWithUrl.Key}]"; await DebugInfoPackageUtils.WriteExceptionAsZipEntryAsync(e, archive, entryName); } } } else { var nodeUrlToDatabaseNames = CreateUrlToDatabaseNamesMapping(transactionOperationContext, databaseNames); foreach (var urlToDatabaseNamesMap in nodeUrlToDatabaseNames) { if (urlToDatabaseNamesMap.Key.Contains(ServerStore.GetNodeHttpServerUrl())) { continue; //skip writing local data, we do it separately } try { await WriteDebugInfoPackageForNodeAsync( jsonOperationContext, archive, tag: urlToDatabaseNamesMap.Value.Item2, url: urlToDatabaseNamesMap.Key, databaseNames: urlToDatabaseNamesMap.Value.Item1, certificate: Server.Certificate.Certificate); } catch (Exception e) { var entryName = $"Node - [{urlToDatabaseNamesMap.Value.Item2}]"; await DebugInfoPackageUtils.WriteExceptionAsZipEntryAsync(e, archive, entryName); } } } } ms.Position = 0; await ms.CopyToAsync(ResponseBodyStream()); } } return(null); }, operationId, token : token); }