public async Task <HttpResponseMessage> DatabasesPut(string id) { if (IsSystemDatabase(id)) { return(GetMessageWithString("System database document cannot be changed!", HttpStatusCode.Forbidden)); } MessageWithStatusCode databaseNameFormat = CheckNameFormat(id, Database.Configuration.DataDirectory); if (databaseNameFormat.Message != null) { return(GetMessageWithString(databaseNameFormat.Message, databaseNameFormat.ErrorCode)); } Etag etag = GetEtag(); string error = CheckExistingDatabaseName(id, etag); if (error != null) { return(GetMessageWithString(error, HttpStatusCode.BadRequest)); } var dbDoc = await ReadJsonObjectAsync <DatabaseDocument>(); string bundles; if (dbDoc.Settings.TryGetValue(Constants.ActiveBundles, out bundles) && bundles.Contains("Encryption")) { if (dbDoc.SecuredSettings == null || !dbDoc.SecuredSettings.ContainsKey(Constants.EncryptionKeySetting) || !dbDoc.SecuredSettings.ContainsKey(Constants.AlgorithmTypeSetting)) { return(GetMessageWithString(string.Format("Failed to create '{0}' database, because of invalid encryption configuration.", id), HttpStatusCode.BadRequest)); } } //TODO: check if paths in document are legal DatabasesLandlord.Protect(dbDoc); var json = RavenJObject.FromObject(dbDoc); json.Remove("Id"); var metadata = (etag != null) ? ReadInnerHeaders.FilterHeadersToObject() : new RavenJObject(); var docKey = Constants.Database.Prefix + id; var putResult = Database.Documents.Put(docKey, etag, json, metadata, null); return((etag == null) ? GetEmptyMessage() : GetMessageWithObject(putResult)); }
public async Task <HttpResponseMessage> DatabasesPut(string id) { Etag etag = GetEtag(); if (etag == null) { Tuple <string, HttpStatusCode> databaseNameFormat = CheckDatabaseNameFormat(id); if (databaseNameFormat.Item1 != null) { return(GetMessageWithString(databaseNameFormat.Item1, databaseNameFormat.Item2)); } } var docKey = "Raven/Databases/" + id; string error = CheckDatbaseName(id, etag); if (error != null) { return(GetMessageWithString(string.Format(error, id), HttpStatusCode.BadRequest)); } var dbDoc = await ReadJsonObjectAsync <DatabaseDocument>(); if (dbDoc.Settings.ContainsKey("Bundles") && dbDoc.Settings["Bundles"].Contains("Encryption")) { if (!dbDoc.SecuredSettings.ContainsKey(Constants.EncryptionKeySetting) || !dbDoc.SecuredSettings.ContainsKey(Constants.AlgorithmTypeSetting)) { return(GetMessageWithString(string.Format("Failed to create '{0}' database, becuase of not valid encryption configuration.", id), HttpStatusCode.BadRequest)); } } DatabasesLandlord.Protect(dbDoc); var json = RavenJObject.FromObject(dbDoc); json.Remove("Id"); var metadata = (etag != null) ? InnerHeaders.FilterHeadersToObject() : new RavenJObject(); var putResult = Database.Documents.Put(docKey, etag, json, metadata, null); return((etag == null) ? GetEmptyMessage() : GetMessageWithObject(putResult)); }
public async Task <HttpResponseMessage> Put(string id) { if (IsSystemDatabase(id)) { return(GetMessageWithString("System database document cannot be changed!", HttpStatusCode.Forbidden)); } MessageWithStatusCode nameFormatErrorMessage; if (IsValidName(id, Database.Configuration.DataDirectory, out nameFormatErrorMessage) == false) { return(GetMessageWithString(nameFormatErrorMessage.Message, nameFormatErrorMessage.ErrorCode)); } Etag etag = GetEtag(); string error = CheckExistingDatabaseName(id, etag); if (error != null) { return(GetMessageWithString(error, HttpStatusCode.BadRequest)); } var dbDoc = await ReadJsonObjectAsync <DatabaseDocument>().ConfigureAwait(false); string bundles; if (dbDoc.Settings.TryGetValue(Constants.ActiveBundles, out bundles) && bundles.Contains("Encryption")) { if (dbDoc.SecuredSettings == null || !dbDoc.SecuredSettings.ContainsKey(Constants.EncryptionKeySetting) || !dbDoc.SecuredSettings.ContainsKey(Constants.AlgorithmTypeSetting)) { return(GetMessageWithString(string.Format("Failed to create '{0}' database, because of invalid encryption configuration.", id), HttpStatusCode.BadRequest)); } } //TODO: check if paths in document are legal if (dbDoc.IsClusterDatabase() && ClusterManager.IsActive()) { string dataDir; if (dbDoc.Settings.TryGetValue("Raven/DataDir", out dataDir) == false || string.IsNullOrEmpty(dataDir)) { return(GetMessageWithString(string.Format("Failed to create '{0}' database, because 'Raven/DataDir' setting is missing.", id), HttpStatusCode.BadRequest)); } dataDir = dataDir.ToFullPath(SystemConfiguration.DataDirectory); // if etag is not null, it means we want to update existing database if (Directory.Exists(dataDir) && etag == null) { return(GetMessageWithString(string.Format("Failed to create '{0}' database, because data directory '{1}' exists and it is forbidden to create non-empty cluster-wide databases.", id, dataDir), HttpStatusCode.BadRequest)); } var changesAppliedMre = new ManualResetEventSlim(false); Etag newEtag = null; var documentKey = Constants.Database.Prefix + id; Action <DocumentDatabase, DocumentChangeNotification, RavenJObject> onDocumentAction = (database, notification, jObject) => { if (notification.Type == DocumentChangeTypes.Put && notification.Id == documentKey) { newEtag = notification.Etag; changesAppliedMre.Set(); } }; Database.Notifications.OnDocumentChange += onDocumentAction; try { await ClusterManager.Client.SendDatabaseUpdateAsync(id, dbDoc).ConfigureAwait(false); changesAppliedMre.Wait(TimeSpan.FromSeconds(15)); } finally { Database.Notifications.OnDocumentChange -= onDocumentAction; } var clusterPutResult = new PutResult { ETag = newEtag, Key = documentKey }; return((etag == null) ? GetEmptyMessage() : GetMessageWithObject(clusterPutResult)); } DatabasesLandlord.Protect(dbDoc); var json = RavenJObject.FromObject(dbDoc); json.Remove("Id"); var metadata = (etag != null) ? ReadInnerHeaders.FilterHeadersToObject() : new RavenJObject(); var docKey = Constants.Database.Prefix + id; var putResult = Database.Documents.Put(docKey, etag, json, metadata, null); return((etag == null) ? GetEmptyMessage() : GetMessageWithObject(putResult)); }
public async Task <HttpResponseMessage> Restore() { if (EnsureSystemDatabase() == false) { return(GetMessageWithString("Restore is only possible from the system database", HttpStatusCode.BadRequest)); } var restoreStatus = new RestoreStatus { Messages = new List <string>() }; var restoreRequest = await ReadJsonObjectAsync <DatabaseRestoreRequest>(); DatabaseDocument databaseDocument = null; var databaseDocumentPath = MaintenanceActions.FindDatabaseDocument(restoreRequest.BackupLocation); if (File.Exists(databaseDocumentPath)) { var databaseDocumentText = File.ReadAllText(databaseDocumentPath); databaseDocument = RavenJObject.Parse(databaseDocumentText).JsonDeserialization <DatabaseDocument>(); } var databaseName = !string.IsNullOrWhiteSpace(restoreRequest.DatabaseName) ? restoreRequest.DatabaseName : databaseDocument == null ? null : databaseDocument.Id; if (string.IsNullOrWhiteSpace(databaseName)) { var errorMessage = (databaseDocument == null || String.IsNullOrWhiteSpace(databaseDocument.Id)) ? "Database.Document file is invalid - database name was not found and not supplied in the request (Id property is missing or null). This is probably a bug - should never happen." : "A database name must be supplied if the restore location does not contain a valid Database.Document file"; restoreStatus.Messages.Add(errorMessage); DatabasesLandlord.SystemDatabase.Documents.Put(RestoreStatus.RavenRestoreStatusDocumentKey, null, RavenJObject.FromObject(new { restoreStatus }), new RavenJObject(), null); return(GetMessageWithString(errorMessage, HttpStatusCode.BadRequest)); } if (databaseName == Constants.SystemDatabase) { return(GetMessageWithString("Cannot do an online restore for the <system> database", HttpStatusCode.BadRequest)); } var existingDatabase = Database.Documents.GetDocumentMetadata("Raven/Databases/" + databaseName, null); if (existingDatabase != null) { return(GetMessageWithString("Cannot do an online restore for an existing database, delete the database " + databaseName + " and restore again.", HttpStatusCode.BadRequest)); } var ravenConfiguration = new RavenConfiguration { DatabaseName = databaseName, IsTenantDatabase = true }; if (databaseDocument != null) { foreach (var setting in databaseDocument.Settings) { ravenConfiguration.Settings[setting.Key] = setting.Value; } } if (File.Exists(Path.Combine(restoreRequest.BackupLocation, BackupMethods.Filename))) { ravenConfiguration.DefaultStorageTypeName = typeof(Raven.Storage.Voron.TransactionalStorage).AssemblyQualifiedName; } else if (Directory.Exists(Path.Combine(restoreRequest.BackupLocation, "new"))) { ravenConfiguration.DefaultStorageTypeName = typeof(Raven.Storage.Esent.TransactionalStorage).AssemblyQualifiedName; } ravenConfiguration.CustomizeValuesForTenant(databaseName); ravenConfiguration.Initialize(); string documentDataDir; ravenConfiguration.DataDirectory = ResolveTenantDataDirectory(restoreRequest.DatabaseLocation, databaseName, out documentDataDir); restoreRequest.DatabaseLocation = ravenConfiguration.DataDirectory; DatabasesLandlord.SystemDatabase.Documents.Delete(RestoreStatus.RavenRestoreStatusDocumentKey, null, null); bool defrag; if (bool.TryParse(GetQueryStringValue("defrag"), out defrag)) { restoreRequest.Defrag = defrag; } var task = Task.Factory.StartNew(() => { MaintenanceActions.Restore(ravenConfiguration, restoreRequest, msg => { restoreStatus.Messages.Add(msg); DatabasesLandlord.SystemDatabase.Documents.Put(RestoreStatus.RavenRestoreStatusDocumentKey, null, RavenJObject.FromObject(restoreStatus), new RavenJObject(), null); }); if (databaseDocument == null) { return; } databaseDocument.Settings[Constants.RavenDataDir] = documentDataDir; if (restoreRequest.IndexesLocation != null) { databaseDocument.Settings[Constants.RavenIndexPath] = restoreRequest.IndexesLocation; } if (restoreRequest.JournalsLocation != null) { databaseDocument.Settings[Constants.RavenTxJournalPath] = restoreRequest.JournalsLocation; } databaseDocument.Id = databaseName; DatabasesLandlord.Protect(databaseDocument); DatabasesLandlord.SystemDatabase.Documents.Put("Raven/Databases/" + databaseName, null, RavenJObject.FromObject(databaseDocument), new RavenJObject(), null); restoreStatus.Messages.Add("The new database was created"); DatabasesLandlord.SystemDatabase.Documents.Put(RestoreStatus.RavenRestoreStatusDocumentKey, null, RavenJObject.FromObject(restoreStatus), new RavenJObject(), null); }, TaskCreationOptions.LongRunning); long id; Database.Tasks.AddTask(task, new object(), new TaskActions.PendingTaskDescription { StartTime = SystemTime.UtcNow, TaskType = TaskActions.PendingTaskType.RestoreDatabase, Payload = "Restoring database " + databaseName + " from " + restoreRequest.BackupLocation }, out id); return(GetMessageWithObject(new { OperationId = id })); }
public async Task <HttpResponseMessage> PeriodicExportSettingsPut() { if (Database == null) { return(GetEmptyMessage(HttpStatusCode.NotFound)); } if (Database.Configuration.Studio.AllowNonAdminUsersToSetupPeriodicExport == false) { return(GetMessageWithString("You can set periodic export settings using this endpoint only if AllowNonAdminUsersToSetupPeriodicExport config is enabled", HttpStatusCode.Unauthorized)); } var docKey = Constants.Database.Prefix + DatabaseName; var databaseDocument = SystemDatabase.Documents.Get(docKey, null); var dbDoc = databaseDocument.DataAsJson.JsonDeserialization <DatabaseDocument>(); DatabasesLandlord.Unprotect(dbDoc); dbDoc.SecuredSettings.Remove(Constants.PeriodicExport.AwsSecretKey); dbDoc.SecuredSettings.Remove(Constants.PeriodicExport.AzureStorageKey); dbDoc.Settings.Remove(Constants.PeriodicExport.AwsAccessKey); dbDoc.Settings.Remove(Constants.PeriodicExport.AzureStorageAccount); var newConfiguration = await ReadJsonAsync().ConfigureAwait(false); var awsAccessKey = newConfiguration.Value <string>(Constants.PeriodicExport.AwsAccessKey); var awsSecretKey = newConfiguration.Value <string>(Constants.PeriodicExport.AwsSecretKey); var azureStorageAccount = newConfiguration.Value <string>(Constants.PeriodicExport.AzureStorageAccount); var azureStorageKey = newConfiguration.Value <string>(Constants.PeriodicExport.AzureStorageKey); if (awsAccessKey != null) { dbDoc.Settings[Constants.PeriodicExport.AwsAccessKey] = awsAccessKey; } if (awsSecretKey != null) { dbDoc.SecuredSettings[Constants.PeriodicExport.AwsSecretKey] = awsSecretKey; } if (azureStorageAccount != null) { dbDoc.Settings[Constants.PeriodicExport.AzureStorageAccount] = azureStorageAccount; } if (azureStorageKey != null) { dbDoc.Settings[Constants.PeriodicExport.AzureStorageKey] = azureStorageKey; } DatabasesLandlord.Protect(dbDoc); var json = RavenJObject.FromObject(dbDoc); json.Remove("Id"); SystemDatabase.Documents.Put(docKey, databaseDocument.Etag, json, new RavenJObject(), null); return(GetEmptyMessage()); }
public async Task <HttpResponseMessage> Restore() { if (EnsureSystemDatabase() == false) { return(GetMessageWithString("Restore is only possible from the system database", HttpStatusCode.BadRequest)); } var restoreStatus = new RestoreStatus { State = RestoreStatusState.Running, Messages = new List <string>() }; var restoreRequest = await ReadJsonObjectAsync <DatabaseRestoreRequest>(); DatabaseDocument databaseDocument = null; var databaseDocumentPath = MaintenanceActions.FindDatabaseDocument(restoreRequest.BackupLocation); if (File.Exists(databaseDocumentPath)) { var databaseDocumentText = File.ReadAllText(databaseDocumentPath); databaseDocument = RavenJObject.Parse(databaseDocumentText).JsonDeserialization <DatabaseDocument>(); } var databaseName = !string.IsNullOrWhiteSpace(restoreRequest.DatabaseName) ? restoreRequest.DatabaseName : databaseDocument == null ? null : databaseDocument.Id; if (string.IsNullOrWhiteSpace(databaseName)) { var errorMessage = (databaseDocument == null || String.IsNullOrWhiteSpace(databaseDocument.Id)) ? "Database.Document file is invalid - database name was not found and not supplied in the request (Id property is missing or null). This is probably a bug - should never happen." : "A database name must be supplied if the restore location does not contain a valid Database.Document file"; restoreStatus.Messages.Add(errorMessage); DatabasesLandlord.SystemDatabase.Documents.Put(RestoreStatus.RavenRestoreStatusDocumentKey, null, RavenJObject.FromObject(new { restoreStatus }), new RavenJObject(), null); return(GetMessageWithString(errorMessage, HttpStatusCode.BadRequest)); } if (databaseName == Constants.SystemDatabase) { return(GetMessageWithString("Cannot do an online restore for the <system> database", HttpStatusCode.BadRequest)); } var existingDatabase = Database.Documents.GetDocumentMetadata("Raven/Databases/" + databaseName, null); if (existingDatabase != null) { return(GetMessageWithString("Cannot do an online restore for an existing database, delete the database " + databaseName + " and restore again.", HttpStatusCode.BadRequest)); } var ravenConfiguration = new RavenConfiguration { DatabaseName = databaseName, IsTenantDatabase = true }; if (databaseDocument != null) { foreach (var setting in databaseDocument.Settings) { ravenConfiguration.Settings[setting.Key] = setting.Value; } } if (File.Exists(Path.Combine(restoreRequest.BackupLocation, BackupMethods.Filename))) { ravenConfiguration.DefaultStorageTypeName = typeof(Raven.Storage.Voron.TransactionalStorage).AssemblyQualifiedName; } else if (Directory.Exists(Path.Combine(restoreRequest.BackupLocation, "new"))) { ravenConfiguration.DefaultStorageTypeName = typeof(Raven.Storage.Esent.TransactionalStorage).AssemblyQualifiedName; } ravenConfiguration.CustomizeValuesForDatabaseTenant(databaseName); ravenConfiguration.Initialize(); string documentDataDir; ravenConfiguration.DataDirectory = ResolveTenantDataDirectory(restoreRequest.DatabaseLocation, databaseName, out documentDataDir); restoreRequest.DatabaseLocation = ravenConfiguration.DataDirectory; string anotherRestoreResourceName; if (IsAnotherRestoreInProgress(out anotherRestoreResourceName)) { if (restoreRequest.RestoreStartTimeout.HasValue) { try { using (var cts = new CancellationTokenSource()) { cts.CancelAfter(TimeSpan.FromSeconds(restoreRequest.RestoreStartTimeout.Value)); var token = cts.Token; do { await Task.Delay(500, token); }while (IsAnotherRestoreInProgress(out anotherRestoreResourceName)); } } catch (OperationCanceledException) { return(GetMessageWithString(string.Format("Another restore is still in progress (resource name = {0}). Waited {1} seconds for other restore to complete.", anotherRestoreResourceName, restoreRequest.RestoreStartTimeout.Value), HttpStatusCode.ServiceUnavailable)); } } else { return(GetMessageWithString(string.Format("Another restore is in progress (resource name = {0})", anotherRestoreResourceName), HttpStatusCode.ServiceUnavailable)); } } Database.Documents.Put(RestoreInProgress.RavenRestoreInProgressDocumentKey, null, RavenJObject.FromObject(new RestoreInProgress { Resource = databaseName }), new RavenJObject(), null); DatabasesLandlord.SystemDatabase.Documents.Delete(RestoreStatus.RavenRestoreStatusDocumentKey, null, null); bool defrag; if (bool.TryParse(GetQueryStringValue("defrag"), out defrag)) { restoreRequest.Defrag = defrag; } var task = Task.Factory.StartNew(() => { try { MaintenanceActions.Restore(ravenConfiguration, restoreRequest, msg => { restoreStatus.Messages.Add(msg); DatabasesLandlord.SystemDatabase.Documents.Put(RestoreStatus.RavenRestoreStatusDocumentKey, null, RavenJObject.FromObject(restoreStatus), new RavenJObject(), null); }); if (databaseDocument == null) { return; } databaseDocument.Settings[Constants.RavenDataDir] = documentDataDir; if (restoreRequest.IndexesLocation != null) { databaseDocument.Settings[Constants.RavenIndexPath] = restoreRequest.IndexesLocation; } if (restoreRequest.JournalsLocation != null) { databaseDocument.Settings[Constants.RavenTxJournalPath] = restoreRequest.JournalsLocation; } bool replicationBundleRemoved = false; if (restoreRequest.DisableReplicationDestinations) { replicationBundleRemoved = TryRemoveReplicationBundle(databaseDocument); } databaseDocument.Id = databaseName; DatabasesLandlord.Protect(databaseDocument); DatabasesLandlord .SystemDatabase .Documents .Put("Raven/Databases/" + databaseName, null, RavenJObject.FromObject(databaseDocument), new RavenJObject(), null); restoreStatus.Messages.Add("The new database was created"); restoreStatus.State = RestoreStatusState.Completed; DatabasesLandlord.SystemDatabase.Documents.Put(RestoreStatus.RavenRestoreStatusDocumentKey, null, RavenJObject.FromObject(restoreStatus), new RavenJObject(), null); if (restoreRequest.GenerateNewDatabaseId) { GenerateNewDatabaseId(databaseName); } if (replicationBundleRemoved) { AddReplicationBundleAndDisableReplicationDestinations(databaseName); } } catch (Exception e) { restoreStatus.State = RestoreStatusState.Faulted; restoreStatus.Messages.Add("Unable to restore database " + e.Message); DatabasesLandlord.SystemDatabase.Documents.Put(RestoreStatus.RavenRestoreStatusDocumentKey, null, RavenJObject.FromObject(restoreStatus), new RavenJObject(), null); throw; } finally { Database.Documents.Delete(RestoreInProgress.RavenRestoreInProgressDocumentKey, null, null); } }, TaskCreationOptions.LongRunning); long id; Database.Tasks.AddTask(task, new TaskBasedOperationState(task), new TaskActions.PendingTaskDescription { StartTime = SystemTime.UtcNow, TaskType = TaskActions.PendingTaskType.RestoreDatabase, Payload = "Restoring database " + databaseName + " from " + restoreRequest.BackupLocation }, out id); return(GetMessageWithObject(new { OperationId = id })); }
public async Task <HttpResponseMessage> Put(string id) { if (IsSystemDatabase(id)) { return(GetMessageWithString("System database document cannot be changed!", HttpStatusCode.Forbidden)); } MessageWithStatusCode nameFormatErrorMessage; if (IsValidName(id, Database.Configuration.Core.DataDirectory, out nameFormatErrorMessage) == false) { return(GetMessageWithString(nameFormatErrorMessage.Message, nameFormatErrorMessage.ErrorCode)); } Etag etag = GetEtag(); string error = CheckExistingDatabaseName(id, etag); if (error != null) { return(GetMessageWithString(error, HttpStatusCode.BadRequest)); } var dbDoc = await ReadJsonObjectAsync <DatabaseDocument>().ConfigureAwait(false); string bundles; if (dbDoc.Settings.TryGetValue(RavenConfiguration.GetKey(x => x.Core._ActiveBundlesString), out bundles) && bundles.Contains("Encryption")) { if (dbDoc.SecuredSettings == null || !dbDoc.SecuredSettings.ContainsKey(RavenConfiguration.GetKey(x => x.Encryption.EncryptionKey)) || !dbDoc.SecuredSettings.ContainsKey(RavenConfiguration.GetKey(x => x.Encryption.AlgorithmType))) { return(GetMessageWithString(string.Format("Failed to create '{0}' database, because of invalid encryption configuration.", id), HttpStatusCode.BadRequest)); } } //TODO: check if paths in document are legal if (dbDoc.IsClusterDatabase() && ClusterManager.IsActive()) { string dataDir; if (dbDoc.Settings.TryGetValue("Raven/DataDir", out dataDir) == false || string.IsNullOrEmpty(dataDir)) { return(GetMessageWithString(string.Format("Failed to create '{0}' database, because 'Raven/DataDir' setting is missing.", id), HttpStatusCode.BadRequest)); } dataDir = dataDir.ToFullPath(SystemConfiguration.Core.DataDirectory); if (Directory.Exists(dataDir)) { return(GetMessageWithString(string.Format("Failed to create '{0}' database, because data directory '{1}' exists and it is forbidden to create non-empty cluster-wide databases.", id, dataDir), HttpStatusCode.BadRequest)); } await ClusterManager.Client.SendDatabaseUpdateAsync(id, dbDoc).ConfigureAwait(false); return(GetEmptyMessage()); } DatabasesLandlord.Protect(dbDoc); var json = RavenJObject.FromObject(dbDoc); json.Remove("Id"); var metadata = (etag != null) ? ReadInnerHeaders.FilterHeadersToObject() : new RavenJObject(); var docKey = Constants.Database.Prefix + id; var putResult = Database.Documents.Put(docKey, etag, json, metadata, null); return((etag == null) ? GetEmptyMessage() : GetMessageWithObject(putResult)); }