Ejemplo n.º 1
0
        /// <summary>
        ///     Ensure that an externally-facing Service resource does not exist for the specified database server.
        /// </summary>
        public async Task EnsureExternalServiceAbsent()
        {
            RequireCurrentState();

            ServiceV1 existingExternalService = await FindExternalService();

            if (existingExternalService != null)
            {
                Log.LogInformation("Deleting external service {ServiceName} for server {ServerId}...",
                                   existingExternalService.Metadata.Name,
                                   State.Id
                                   );

                StatusV1 result = await KubeClient.ServicesV1().Delete(
                    name: existingExternalService.Metadata.Name,
                    kubeNamespace: KubeOptions.KubeNamespace
                    );

                if (result.Status != "Success" && result.Reason != "NotFound")
                {
                    Log.LogError("Failed to delete external service {ServiceName} for server {ServerId} (Message:{FailureMessage}, Reason:{FailureReason}).",
                                 existingExternalService.Metadata.Name,
                                 State.Id,
                                 result.Message,
                                 result.Reason
                                 );
                }

                Log.LogInformation("Deleted external service {ServiceName} for server {ServerId}.",
                                   existingExternalService.Metadata.Name,
                                   State.Id
                                   );
            }
        }
Ejemplo n.º 2
0
        /// <summary>
        ///     Ensure that an externally-facing Service resource exists for the specified database server.
        /// </summary>
        /// <returns>
        ///     A <see cref="Task"/> representing the operation.
        /// </returns>
        public async Task EnsureExternalServicePresent()
        {
            RequireCurrentState();

            ServiceV1 existingExternalService = await FindExternalService();

            if (existingExternalService == null)
            {
                Log.LogInformation("Creating external service for server {ServerId}...",
                                   State.Id
                                   );

                ServiceV1 createdService = await KubeClient.ServicesV1().Create(
                    KubeResources.ExternalService(State,
                                                  kubeNamespace: KubeOptions.KubeNamespace
                                                  )
                    );

                Log.LogInformation("Successfully created external service {ServiceName} for server {ServerId}.",
                                   createdService.Metadata.Name,
                                   State.Id
                                   );
            }
            else
            {
                Log.LogInformation("Found existing external service {ServiceName} for server {ServerId}.",
                                   existingExternalService.Metadata.Name,
                                   State.Id
                                   );
            }
        }
Ejemplo n.º 3
0
        /// <summary>
        ///     Find the server's associated externally-facing Service (if it exists).
        /// </summary>
        /// <returns>
        ///     The Service, or <c>null</c> if it was not found.
        /// </returns>
        public async Task <ServiceV1> FindExternalService()
        {
            RequireCurrentState();

            List <ServiceV1> matchingServices = await KubeClient.ServicesV1().List(
                labelSelector: $"cloud.dimensiondata.daas.server-id = {State.Id},cloud.dimensiondata.daas.service-type = external",
                kubeNamespace: KubeOptions.KubeNamespace
                );

            if (matchingServices.Count == 0)
            {
                return(null);
            }

            return(matchingServices[matchingServices.Count - 1]);
        }
Ejemplo n.º 4
0
        /// <summary>
        ///     Get the public TCP port number on which the database server is accessible.
        /// </summary>
        /// <param name="server">
        ///     A <see cref="DatabaseServer"/> describing the server.
        /// </param>
        /// <param name="kubeNamespace">
        ///     An optional target Kubernetes namespace.
        /// </param>
        /// <returns>
        ///     The port, or <c>null</c> if the externally-facing service for the server cannot be found.
        /// </returns>
        public async Task <int?> GetPublicPort()
        {
            RequireCurrentState();

            List <ServiceV1> matchingServices = await KubeClient.ServicesV1().List(
                labelSelector: $"cloud.dimensiondata.daas.server-id = {State.Id}, cloud.dimensiondata.daas.service-type = external",
                kubeNamespace: KubeOptions.KubeNamespace
                );

            if (matchingServices.Count == 0)
            {
                return(null);
            }

            ServiceV1 externalService = matchingServices[matchingServices.Count - 1];

            return(externalService.Spec.Ports[0].NodePort);
        }
Ejemplo n.º 5
0
        /// <summary>
        ///     Determine the connection string for the specified server.
        /// </summary>
        /// <param name="serverId">
        ///     The Id of the target server.
        /// </param>
        /// <returns>
        ///     The base UR.
        /// </returns>
        async Task <Uri> GetServerBaseAddress(string serverId)
        {
            if (String.IsNullOrWhiteSpace(serverId))
            {
                throw new ArgumentException("Argument cannot be null, empty, or entirely composed of whitespace: 'serverId'.", nameof(serverId));
            }

            Log.LogInformation("Determining connection string for server {ServerId}...",
                               serverId
                               );

            DatabaseServer targetServer = await GetServer(serverId);

            List <ServiceV1> matchingServices = await KubeClient.ServicesV1().List(
                labelSelector: $"cloud.dimensiondata.daas.server-id = {serverId},cloud.dimensiondata.daas.service-type = internal",
                kubeNamespace: KubeOptions.KubeNamespace
                );

            if (matchingServices.Count == 0)
            {
                Log.LogWarning("Cannot determine connection string for server {ServerId} (server's associated Kubernetes Service not found).",
                               serverId
                               );

                throw RespondWith(NotFound(new
                {
                    Reason     = "EndPointNotFound",
                    Id         = serverId,
                    EntityType = "DatabaseServer",
                    Message    = $"Cannot determine base address for server '{targetServer.Id}'."
                }));
            }

            ServiceV1 serverService = matchingServices[matchingServices.Count - 1];
            string    serverFQDN    = $"{serverService.Metadata.Name}.{serverService.Metadata.Namespace}.svc.cluster.local";
            int       serverPort    = serverService.Spec.Ports[0].Port;

            Log.LogInformation("Database proxy will connect to RavenDB server '{ServerFQDN}' on {ServerPort}.", serverFQDN, serverPort);

            return(new Uri($"http://{serverFQDN}:{serverPort}"));
        }
Ejemplo n.º 6
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);
        }