Example #1
0
        /// <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);
        }
Example #2
0
        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."));
            }
        }
Example #3
0
        /// <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);
        }