示例#1
0
        /// <summary>
        ///     Build a <see cref="PrometheusServiceMonitorSpecV1"/> for the specified server.
        /// </summary>
        /// <param name="server">
        ///     A <see cref="DatabaseServer"/> representing the target server.
        /// </param>
        /// <returns>
        ///     The configured <see cref="PrometheusServiceMonitorSpecV1"/>.
        /// </returns>
        public PrometheusServiceMonitorSpecV1 ServiceMonitor(DatabaseServer server)
        {
            if (server == null)
            {
                throw new ArgumentNullException(nameof(server));
            }

            string baseName = Names.BaseName(server);

            return(new PrometheusServiceMonitorSpecV1
            {
                JobLabel = baseName,
                Selector = new LabelSelectorV1
                {
                    MatchLabels = new Dictionary <string, string>
                    {
                        ["cloud.dimensiondata.daas.server-id"] = server.Id,
                        ["cloud.dimensiondata.daas.service-type"] = "internal"
                    }
                },
                EndPoints = new List <PrometheusServiceMonitorEndPointV1>
                {
                    new PrometheusServiceMonitorEndPointV1
                    {
                        Port = "prometheus-exporter"
                    }
                }
            });
        }
示例#2
0
        /// <summary>
        ///     Build an externally-facing <see cref="ServiceSpecV1"/> for the specified server.
        /// </summary>
        /// <param name="server">
        ///     A <see cref="DatabaseServer"/> representing the target server.
        /// </param>
        /// <returns>
        ///     The configured <see cref="ServiceSpecV1"/>.
        /// </returns>
        public ServiceSpecV1 ExternalService(DatabaseServer server)
        {
            if (server == null)
            {
                throw new ArgumentNullException(nameof(server));
            }

            string baseName = Names.BaseName(server);

            var spec = new ServiceSpecV1
            {
                Type     = "NodePort",
                Ports    = new List <ServicePortV1>(),
                Selector = new Dictionary <string, string>
                {
                    ["k8s-app"] = baseName
                }
            };

            switch (server.Kind)
            {
            case DatabaseServerKind.SqlServer:
            {
                spec.Ports.Add(new ServicePortV1
                    {
                        Name     = "sql-server",
                        Port     = 1433,
                        Protocol = "TCP"
                    });

                break;
            }

            case DatabaseServerKind.RavenDB:
            {
                spec.Ports.Add(new ServicePortV1
                    {
                        Name     = "http",
                        Port     = 8080,
                        Protocol = "TCP"
                    });
                spec.Ports.Add(new ServicePortV1
                    {
                        Name     = "tcp",
                        Port     = 38888,
                        Protocol = "TCP"
                    });

                break;
            }

            default:
            {
                throw new NotSupportedException($"Unsupported server type ({server.Kind}).");
            }
            }

            return(spec);
        }
示例#3
0
        /// <summary>
        ///     Create a new internally-facing <see cref="ServiceV1"/> for the specified database server.
        /// </summary>
        /// <param name="server">
        ///     A <see cref="DatabaseServer"/> representing the target server.
        /// </param>
        /// <param name="kubeNamespace">
        ///     An optional target Kubernetes namespace.
        /// </param>
        /// <returns>
        ///     The configured <see cref="ServiceV1"/>.
        /// </returns>
        public ServiceV1 InternalService(DatabaseServer server, string kubeNamespace = null)
        {
            if (server == null)
            {
                throw new ArgumentNullException(nameof(server));
            }

            return(Service(
                       name: Names.BaseName(server),
                       kubeNamespace: kubeNamespace,
                       spec: Specs.InternalService(server),
                       labels: new Dictionary <string, string>
            {
                ["k8s-app"] = Names.BaseName(server),
                ["cloud.dimensiondata.daas.server-id"] = server.Id,
                ["cloud.dimensiondata.daas.service-type"] = "internal"
            }
                       ));
        }
示例#4
0
        /// <summary>
        ///     Create a new <see cref="PersistentVolumeClaimV1"/> for the specified database server.
        /// </summary>
        /// <param name="server">
        ///     A <see cref="DatabaseServer"/> representing the target server.
        /// </param>
        /// <param name="kubeNamespace">
        ///     An optional target Kubernetes namespace.
        /// </param>
        /// <returns>
        ///     The configured <see cref="PersistentVolumeClaimV1"/>.
        /// </returns>
        public PersistentVolumeClaimV1 DataVolumeClaim(DatabaseServer server, string kubeNamespace = null)
        {
            if (server == null)
            {
                throw new ArgumentNullException(nameof(server));
            }

            return(DataVolumeClaim(
                       name: Names.DataVolumeClaim(server),
                       kubeNamespace: kubeNamespace,
                       spec: Specs.DataVolumeClaim(server),
                       labels: new Dictionary <string, string>
            {
                ["k8s-app"] = Names.BaseName(server),
                ["cloud.dimensiondata.daas.server-id"] = server.Id,
                ["cloud.dimensiondata.daas.volume-type"] = "data"
            }
                       ));
        }
示例#5
0
        /// <summary>
        ///     Create a new <see cref="DeploymentV1Beta1"/> for the specified database server.
        /// </summary>
        /// <param name="server">
        ///     A <see cref="DatabaseServer"/> representing the target server.
        /// </param>
        /// <param name="kubeNamespace">
        ///     An optional target Kubernetes namespace.
        /// </param>
        /// <returns>
        ///     The configured <see cref="DeploymentV1Beta1"/>.
        /// </returns>
        public DeploymentV1Beta1 Deployment(DatabaseServer server, string kubeNamespace = null)
        {
            if (server == null)
            {
                throw new ArgumentNullException(nameof(server));
            }

            string baseName = Names.BaseName(server);

            return(Deployment(
                       name: baseName,
                       kubeNamespace: kubeNamespace,
                       spec: Specs.Deployment(server),
                       labels: new Dictionary <string, string>
            {
                ["k8s-app"] = baseName,
                ["cloud.dimensiondata.daas.server-id"] = server.Id
            }
                       ));
        }
示例#6
0
        /// <summary>
        ///     Create a new <see cref="PrometheusServiceMonitorV1"/> for the specified database server.
        /// </summary>
        /// <param name="server">
        ///     A <see cref="DatabaseServer"/> representing the target server.
        /// </param>
        /// <param name="kubeNamespace">
        ///     An optional target Kubernetes namespace.
        /// </param>
        /// <returns>
        ///     The configured <see cref="PrometheusServiceMonitorV1"/>.
        /// </returns>
        public PrometheusServiceMonitorV1 ServiceMonitor(DatabaseServer server, string kubeNamespace = null)
        {
            if (server == null)
            {
                throw new ArgumentNullException(nameof(server));
            }

            string baseName = Names.BaseName(server);

            return(ServiceMonitor(
                       name: $"{baseName}-monitor",
                       kubeNamespace: kubeNamespace,
                       spec: Specs.ServiceMonitor(server),
                       labels: new Dictionary <string, string>
            {
                ["k8s-app"] = baseName,
                ["cloud.dimensiondata.daas.server-id"] = server.Id,
                ["cloud.dimensiondata.daas.monitor-type"] = "database-server"
            }
                       ));
        }
示例#7
0
        /// <summary>
        ///     Create a new <see cref="SecretV1"/> for the specified database server's credentials.
        /// </summary>
        /// <param name="server">
        ///     A <see cref="DatabaseServer"/> representing the target server.
        /// </param>
        /// <param name="serverCertificate">
        ///     <see cref="CertificateCredentials"/> representing the server certificate.
        /// </param>
        /// <param name="kubeNamespace">
        ///     An optional target Kubernetes namespace.
        /// </param>
        /// <returns>
        ///     The configured <see cref="SecretV1"/>.
        /// </returns>
        public SecretV1 CredentialsSecret(DatabaseServer server, CertificateCredentials serverCertificate, string kubeNamespace = null)
        {
            if (server == null)
            {
                throw new ArgumentNullException(nameof(server));
            }

            if (serverCertificate == null)
            {
                throw new ArgumentNullException(nameof(serverCertificate));
            }

            var secretData = new Dictionary <string, string>();

            if (!String.IsNullOrWhiteSpace(serverCertificate.IssuingCACertificateContent))
            {
                secretData["ca.crt"] = Convert.ToBase64String(
                    Encoding.ASCII.GetBytes(serverCertificate.IssuingCACertificateContent)
                    );
            }

            secretData["server.crt"] = Convert.ToBase64String(
                Encoding.ASCII.GetBytes(serverCertificate.CertificateContent)
                );
            secretData["server.key"] = Convert.ToBase64String(
                Encoding.ASCII.GetBytes(serverCertificate.PrivateKey)
                );

            string pfxPassword;

            byte[] passwordBytes = new byte[16];
            using (RandomNumberGenerator random = RandomNumberGenerator.Create())
            {
                random.GetBytes(passwordBytes);
            }
            pfxPassword = Convert.ToBase64String(passwordBytes);

            secretData["server.pfx"] = Convert.ToBase64String(
                serverCertificate.ToPfx(pfxPassword)
                );
            secretData["pfx-password"] = Convert.ToBase64String(
                Encoding.ASCII.GetBytes(pfxPassword)
                );

            if (server.Settings is SqlServerSettings sqlServerSettings && !String.IsNullOrWhiteSpace(sqlServerSettings.AdminPassword))
            {
                secretData["sa-password"] = Convert.ToBase64String(
                    Encoding.ASCII.GetBytes(sqlServerSettings.AdminPassword)
                    );
            }

            return(CredentialsSecret(
                       name: Names.CredentialsSecret(server),
                       kubeNamespace: kubeNamespace,
                       data: secretData,
                       labels: new Dictionary <string, string>
            {
                ["k8s-app"] = Names.BaseName(server),
                ["cloud.dimensiondata.daas.server-id"] = server.Id,
                ["cloud.dimensiondata.daas.secret-type"] = "credentials"
            }
                       ));
        }
示例#8
0
        /// <summary>
        ///     Build a <see cref="DeploymentSpecV1Beta1"/> for the specified server.
        /// </summary>
        /// <param name="server">
        ///     A <see cref="DatabaseServer"/> representing the target server.
        /// </param>
        /// <returns>
        ///     The configured <see cref="DeploymentSpecV1Beta1"/>.
        /// </returns>
        public DeploymentSpecV1Beta1 Deployment(DatabaseServer server)
        {
            if (server == null)
            {
                throw new ArgumentNullException(nameof(server));
            }

            string baseName = Names.BaseName(server);

            var deploymentSpec = new DeploymentSpecV1Beta1
            {
                Replicas        = 1,
                MinReadySeconds = 30,
                Strategy        = new DeploymentStrategyV1Beta1
                {
                    Type = "Recreate" // Shut down the old instance before starting the new one
                },
                Selector = new LabelSelectorV1
                {
                    MatchLabels = new Dictionary <string, string>
                    {
                        ["k8s-app"] = baseName
                    }
                },
                Template = new PodTemplateSpecV1
                {
                    Metadata = new ObjectMetaV1
                    {
                        Labels = new Dictionary <string, string>
                        {
                            ["k8s-app"] = baseName,
                            ["cloud.dimensiondata.daas.server-id"]   = server.Id,
                            ["cloud.dimensiondata.daas.server-kind"] = server.Kind.ToString()
                        }
                    },
                    Spec = new PodSpecV1
                    {
                        TerminationGracePeriodSeconds = 60,
                        ImagePullSecrets = new List <LocalObjectReferenceV1>
                        {
                            new LocalObjectReferenceV1
                            {
                                Name = "daas-registry"
                            }
                        },
                        Containers = new List <ContainerV1>(),
                        Volumes    = new List <VolumeV1>
                        {
                            new VolumeV1
                            {
                                Name = "data",
                                PersistentVolumeClaim = new PersistentVolumeClaimVolumeSourceV1
                                {
                                    ClaimName = Names.DataVolumeClaim(server)
                                }
                            }
                        }
                    }
                }
            };

            PodSpecV1 podSpec = deploymentSpec.Template.Spec;

            switch (server.Kind)
            {
            case DatabaseServerKind.SqlServer:
            {
                // SQL Server
                podSpec.Containers.Add(new ContainerV1
                    {
                        Name      = "sql-server",
                        Image     = ProvisioningOptions.Images.SQL,
                        Resources = new ResourceRequirementsV1
                        {
                            Requests = new Dictionary <string, string>
                            {
                                ["memory"] = "4Gi" // SQL Server for Linux requires at least 4 GB of RAM
                            },
                            Limits = new Dictionary <string, string>
                            {
                                ["memory"] = "6Gi" // If you're using more than 6 GB of RAM, then you should probably host stand-alone
                            }
                        },
                        Env = new List <EnvVarV1>
                        {
                            new EnvVarV1
                            {
                                Name  = "ACCEPT_EULA",
                                Value = "Y"
                            },
                            new EnvVarV1
                            {
                                Name      = "SA_PASSWORD",
                                ValueFrom = new EnvVarSourceV1
                                {
                                    SecretKeyRef = new SecretKeySelectorV1
                                    {
                                        Name = Names.CredentialsSecret(server),
                                        Key  = "sa-password"
                                    }
                                }
                            }
                        },
                        Ports = new List <ContainerPortV1>
                        {
                            new ContainerPortV1
                            {
                                ContainerPort = 1433
                            }
                        },
                        VolumeMounts = new List <VolumeMountV1>
                        {
                            new VolumeMountV1
                            {
                                Name      = "data",
                                SubPath   = baseName,
                                MountPath = "/var/opt/mssql"
                            }
                        }
                    });

                // Prometheus exporter
                podSpec.Containers.Add(new ContainerV1
                    {
                        Name  = "prometheus-exporter",
                        Image = ProvisioningOptions.Images.SQLExporter,
                        Env   = new List <EnvVarV1>
                        {
                            new EnvVarV1
                            {
                                Name  = "SERVER",
                                Value = "127.0.0.1",
                            },
                            new EnvVarV1
                            {
                                Name  = "USERNAME",
                                Value = "sa"
                            },
                            new EnvVarV1
                            {
                                Name      = "PASSWORD",
                                ValueFrom = new EnvVarSourceV1
                                {
                                    SecretKeyRef = new SecretKeySelectorV1
                                    {
                                        Name = Names.CredentialsSecret(server),
                                        Key  = "sa-password"
                                    }
                                }
                            },
                            new EnvVarV1
                            {
                                Name  = "DEBUG",
                                Value = "app"
                            }
                        },
                        Ports = new List <ContainerPortV1>
                        {
                            new ContainerPortV1
                            {
                                ContainerPort = 4000
                            }
                        }
                    });

                break;
            }

            case DatabaseServerKind.RavenDB:
            {
                podSpec.Containers.Add(new ContainerV1
                    {
                        Name      = "ravendb",
                        Image     = ProvisioningOptions.Images.RavenDB,
                        Resources = new ResourceRequirementsV1
                        {
                            Requests = new Dictionary <string, string>
                            {
                                ["memory"] = "1Gi"
                            },
                            Limits = new Dictionary <string, string>
                            {
                                ["memory"] = "3Gi"
                            }
                        },
                        Env = new List <EnvVarV1>
                        {
                            new EnvVarV1
                            {
                                Name  = "UNSECURED_ACCESS_ALLOWED",
                                Value = "PublicNetwork"
                            }
                        },
                        Ports = new List <ContainerPortV1>
                        {
                            new ContainerPortV1
                            {
                                Name          = "http",
                                ContainerPort = 8080
                            },
                            new ContainerPortV1
                            {
                                Name          = "tcp",
                                ContainerPort = 38888
                            }
                        },
                        VolumeMounts = new List <VolumeMountV1>
                        {
                            new VolumeMountV1
                            {
                                Name      = "data",
                                SubPath   = baseName,
                                MountPath = "/databases"
                            }
                        }
                    });

                break;
            }

            default:
            {
                throw new NotSupportedException($"Unsupported server kind ({server.Kind}).");
            }
            }

            return(deploymentSpec);
        }