/// <summary> /// Retrieve and validate the target database server. /// </summary> /// <param name="serverId"> /// The target server Id. /// </param> /// <returns> /// A <see cref="DatabaseServer"/> representing the database server. /// </returns> async Task <DatabaseServer> GetServer(string serverId) { if (String.IsNullOrWhiteSpace(serverId)) { throw new ArgumentException("Argument cannot be null, empty, or entirely composed of whitespace: 'serverId'.", nameof(serverId)); } DatabaseServer targetServer = await DocumentSession.LoadAsync <DatabaseServer>(serverId); if (targetServer == null) { Log.LogWarning("Cannot determine connection string for server {ServerId} (server not found).", serverId ); throw RespondWith(NotFound(new { Reason = "NotFound", Id = serverId, EntityType = "DatabaseServer", Message = $"Database Server not found with Id '{serverId}'." })); } if (targetServer.Kind != DatabaseServerKind.RavenDB) { Log.LogWarning("Target server {ServerId} is not a RavenDB server (actual server type is {ServerKind}).", serverId, targetServer.Kind ); throw RespondWith(BadRequest(new { Reason = "NotSupported", Id = serverId, EntityType = "DatabaseServer", RequiredServerKind = DatabaseServerKind.RavenDB, ActualServerKind = targetServer.Kind, Message = $"Database Server '{serverId}' is not a RavenDB server." })); } return(targetServer); }
public async Task <IActionResult> InitializeServerConfiguration(string serverId) { if (String.IsNullOrWhiteSpace(serverId)) { throw new ArgumentException("Argument cannot be null, empty, or entirely composed of whitespace: 'serverId'.", nameof(serverId)); } Log.LogInformation("Initialising configuration for server {ServerId}...", serverId); DatabaseServer targetServer = await DocumentSession.LoadAsync <DatabaseServer>(serverId); using (CancellationTokenSource cancellationSource = new CancellationTokenSource()) { Uri baseAddress = await GetServerBaseAddress(serverId); cancellationSource.CancelAfter( TimeSpan.FromSeconds(30) ); Log.LogInformation("Using (deliberately) insecure setup mode for server {ServerId}...", serverId); HttpResponseMessage response = await HttpClient.PostAsJsonAsync( Requests.StartUnsecuredSetup.WithBaseUri(baseAddress), postBody : new { PublicServerUrl = baseAddress.AbsoluteUri, Port = 8080, Addresses = new string[] { "127.0.0.1" } }, cancellationToken : cancellationSource.Token ); using (response) { response.EnsureSuccessStatusCode(); } Log.LogInformation("Committing changes to configuration for server {ServerId}...", serverId); response = await HttpClient.PostAsync( Requests.CompleteSetup.WithBaseUri(baseAddress), cancellationToken : cancellationSource.Token ); using (response) { response.EnsureSuccessStatusCode(); } while (true) { using (CancellationTokenSource aliveCheckCancellationSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationSource.Token)) { aliveCheckCancellationSource.CancelAfter( TimeSpan.FromSeconds(1) ); try { Log.LogDebug("Checking to see if server {ServerId} has restarted...", serverId); response = await HttpClient.GetAsync( Requests.IsServerAlive.WithBaseUri(baseAddress), cancellationToken : cancellationSource.Token ); using (response) { response.EnsureSuccessStatusCode(); break; } } catch (OperationCanceledException timedOut) { if (timedOut.CancellationToken == cancellationSource.Token) { throw new TimeoutException("Timed out after waiting 30 seconds for RavenDB server to restart.", timedOut); } } } } Log.LogInformation("Configuration has been initialised for server {ServerId}.", serverId); return(Ok("Server configuration initialised.")); } }
/// <summary> /// Determine the connection string for the specified <see cref="SqlRequest"/>. /// </summary> /// <param name="request"> /// The <see cref="SqlRequest"/> being executed. /// </param> /// <returns> /// The connection string. /// </returns> async Task <string> GetConnectionString(SqlRequest request) { if (request == null) { throw new ArgumentNullException(nameof(request)); } Log.LogInformation("Determining connection string for database {DatabaseId} in server {ServerId}...", request.DatabaseId, request.ServerId ); DatabaseServer targetServer = await DocumentSession.LoadAsync <DatabaseServer>(request.ServerId); if (targetServer == null) { Log.LogWarning("Cannot determine connection string for database {DatabaseId} in server {ServerId} (server not found).", request.DatabaseId, request.ServerId ); throw RespondWith(Ok(new SqlResult { ResultCode = -1, Errors = { new SqlError { Kind = SqlErrorKind.Infrastructure, Message = $"Unable to determine connection settings for database {request.DatabaseId} in server {request.ServerId} (server not found)." } } })); } List <ServiceV1> matchingServices = await KubeClient.ServicesV1().List( labelSelector: $"cloud.dimensiondata.daas.server-id = {targetServer.Id},cloud.dimensiondata.daas.service-type = internal", kubeNamespace: KubeOptions.KubeNamespace ); if (matchingServices.Count == 0) { Log.LogWarning("Cannot determine connection string for database {DatabaseId} in server {ServerId} (server's associated Kubernetes Service not found).", request.DatabaseId, request.ServerId ); throw RespondWith(Ok(new SqlResult { ResultCode = -1, Errors = { new SqlError { Kind = SqlErrorKind.Infrastructure, Message = $"Unable to determine connection settings for database {request.DatabaseId} in server {request.ServerId} (server's associated Kubernetes Service not found)." } } })); } ServiceV1 serverService = matchingServices[matchingServices.Count - 1]; (string serverFQDN, int?serverPort) = serverService.GetHostAndPort(portName: "sql-server"); if (serverPort == null) { Log.LogWarning("Cannot determine connection string for database {DatabaseId} in server {ServerId} (cannot find the port named 'sql-server' on server's associated Kubernetes Service).", request.DatabaseId, request.ServerId ); throw RespondWith(Ok(new SqlResult { ResultCode = -1, Errors = { new SqlError { Kind = SqlErrorKind.Infrastructure, Message = $"Unable to determine connection settings for database {request.DatabaseId} in server {request.ServerId} (cannot find the port named 'sql-server' on server's associated Kubernetes Service)." } } })); } Log.LogInformation("Database proxy will connect to SQL Server '{ServerFQDN}' on {ServerPort}.", serverFQDN, serverPort); var connectionStringBuilder = new SqlClient.SqlConnectionStringBuilder { DataSource = $"tcp:{serverFQDN},{serverPort}", }; var serverSettings = targetServer.GetSettings <SqlServerSettings>(); if (request.DatabaseId != MasterDatabaseId) { DatabaseInstance targetDatabase = await DocumentSession.LoadAsync <DatabaseInstance>(request.DatabaseId); if (targetDatabase == null) { Log.LogWarning("Cannot determine connection string for database {DatabaseId} in server {ServerId} (database not found).", request.DatabaseId, request.ServerId ); throw RespondWith(Ok(new SqlResult { ResultCode = -1, Errors = { new SqlError { Kind = SqlErrorKind.Infrastructure, Message = $"Unable to determine connection settings for database {request.DatabaseId} in server {request.ServerId} (database not found)." } } })); } connectionStringBuilder.InitialCatalog = targetDatabase.Name; if (request.ExecuteAsAdminUser) { connectionStringBuilder.UserID = "sa"; connectionStringBuilder.Password = serverSettings.AdminPassword; } else { connectionStringBuilder.UserID = targetDatabase.DatabaseUser; connectionStringBuilder.Password = targetDatabase.DatabasePassword; } } else { connectionStringBuilder.InitialCatalog = "master"; connectionStringBuilder.UserID = "sa"; connectionStringBuilder.Password = serverSettings.AdminPassword; } Log.LogInformation("Successfully determined connection string for database {DatabaseId} ({DatabaseName}) in server {ServerId} ({ServerSqlName}).", request.DatabaseId, connectionStringBuilder.InitialCatalog, request.ServerId, connectionStringBuilder.DataSource ); return(connectionStringBuilder.ConnectionString); }