Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support GCP CloudSQL proxy #6606

Merged
merged 5 commits into from
Nov 9, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
128 changes: 79 additions & 49 deletions installer/pkg/common/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -138,64 +138,94 @@ func MessageBusEnv(_ *config.Config) (res []corev1.EnvVar) {
}

func DatabaseEnv(cfg *config.Config) (res []corev1.EnvVar) {
var name string
var (
secretRef corev1.LocalObjectReference
envvars []corev1.EnvVar
)

if pointer.BoolDeref(cfg.Database.InCluster, false) {
// Cluster provided internally
name = InClusterDbSecret
} else if cfg.Database.RDS != nil && cfg.Database.RDS.Certificate.Name != "" {
// AWS
name = cfg.Database.RDS.Certificate.Name
secretRef = corev1.LocalObjectReference{Name: InClusterDbSecret}
envvars = append(envvars,
corev1.EnvVar{
Name: "DB_HOST",
ValueFrom: &corev1.EnvVarSource{SecretKeyRef: &corev1.SecretKeySelector{
LocalObjectReference: secretRef,
Key: "host",
}},
},
corev1.EnvVar{
Name: "DB_PORT",
ValueFrom: &corev1.EnvVarSource{SecretKeyRef: &corev1.SecretKeySelector{
LocalObjectReference: secretRef,
Key: "port",
}},
},
)
} else if cfg.Database.External != nil && cfg.Database.External.Certificate.Name != "" {
// External DB
secretRef = corev1.LocalObjectReference{Name: cfg.Database.External.Certificate.Name}
envvars = append(envvars,
corev1.EnvVar{
Name: "DB_HOST",
ValueFrom: &corev1.EnvVarSource{SecretKeyRef: &corev1.SecretKeySelector{
LocalObjectReference: secretRef,
Key: "host",
}},
},
corev1.EnvVar{
Name: "DB_PORT",
ValueFrom: &corev1.EnvVarSource{SecretKeyRef: &corev1.SecretKeySelector{
LocalObjectReference: secretRef,
Key: "port",
}},
},
)
} else if cfg.Database.CloudSQL != nil && cfg.Database.CloudSQL.ServiceAccount.Name != "" {
// GCP
name = cfg.Database.CloudSQL.ServiceAccount.Name
secretRef = corev1.LocalObjectReference{Name: cfg.Database.CloudSQL.ServiceAccount.Name}
envvars = append(envvars,
corev1.EnvVar{
Name: "DB_HOST",
Value: "cloudsqlproxy",
},
corev1.EnvVar{
Name: "DB_PORT",
Value: "3306",
},
)
} else {
panic("invalid database configuration")
}

obj := corev1.LocalObjectReference{Name: name}
envvars = append(envvars,
corev1.EnvVar{
Name: "DB_PASSWORD",
ValueFrom: &corev1.EnvVarSource{SecretKeyRef: &corev1.SecretKeySelector{
LocalObjectReference: secretRef,
Key: "password",
}},
},
corev1.EnvVar{
Name: "DB_USERNAME",
ValueFrom: &corev1.EnvVarSource{SecretKeyRef: &corev1.SecretKeySelector{
LocalObjectReference: secretRef,
Key: "username",
}},
},
corev1.EnvVar{
Name: "DB_ENCRYPTION_KEYS",
ValueFrom: &corev1.EnvVarSource{SecretKeyRef: &corev1.SecretKeySelector{
LocalObjectReference: secretRef,
Key: "encryptionKeys",
}},
},
corev1.EnvVar{
Name: "DB_DELETED_ENTRIES_GC_ENABLED",
Value: "false",
},
)

return []corev1.EnvVar{{
Name: "DB_HOST",
ValueFrom: &corev1.EnvVarSource{SecretKeyRef: &corev1.SecretKeySelector{
LocalObjectReference: obj,
Key: "host",
}},
}, {
Name: "DB_PORT",
ValueFrom: &corev1.EnvVarSource{SecretKeyRef: &corev1.SecretKeySelector{
LocalObjectReference: obj,
Key: "port",
}},
}, {
Name: "DB_PASSWORD",
ValueFrom: &corev1.EnvVarSource{SecretKeyRef: &corev1.SecretKeySelector{
LocalObjectReference: obj,
Key: "password",
}},
}, {
Name: "DB_USERNAME",
ValueFrom: &corev1.EnvVarSource{SecretKeyRef: &corev1.SecretKeySelector{
LocalObjectReference: obj,
Key: "username",
}},
}, {
// todo(sje): conditional
Name: "DB_DELETED_ENTRIES_GC_ENABLED",
Value: "false",
}, {
Name: "DB_ENCRYPTION_KEYS",
ValueFrom: &corev1.EnvVarSource{SecretKeyRef: &corev1.SecretKeySelector{
LocalObjectReference: obj,
Key: "encryptionKeys",
}},
//ValueFrom: &corev1.EnvVarSource{SecretKeyRef: &corev1.SecretKeySelector{
// LocalObjectReference: corev1.LocalObjectReference{
// Name: "",
// },
// Key: "keys",
//}},
}}
return envvars
}

func DatabaseWaiterContainer(ctx *RenderContext) *corev1.Container {
Expand Down
6 changes: 3 additions & 3 deletions installer/pkg/components/components.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,13 @@ import (
"github.com/gitpod-io/gitpod/installer/pkg/components/cluster"
contentservice "github.com/gitpod-io/gitpod/installer/pkg/components/content-service"
"github.com/gitpod-io/gitpod/installer/pkg/components/dashboard"
"github.com/gitpod-io/gitpod/installer/pkg/components/database"
dockerregistry "github.com/gitpod-io/gitpod/installer/pkg/components/docker-registry"
"github.com/gitpod-io/gitpod/installer/pkg/components/gitpod"
imagebuildermk3 "github.com/gitpod-io/gitpod/installer/pkg/components/image-builder-mk3"
jaegeroperator "github.com/gitpod-io/gitpod/installer/pkg/components/jaeger-operator"
"github.com/gitpod-io/gitpod/installer/pkg/components/migrations"
"github.com/gitpod-io/gitpod/installer/pkg/components/minio"
"github.com/gitpod-io/gitpod/installer/pkg/components/mysql"
openvsxproxy "github.com/gitpod-io/gitpod/installer/pkg/components/openvsx-proxy"
"github.com/gitpod-io/gitpod/installer/pkg/components/proxy"
"github.com/gitpod-io/gitpod/installer/pkg/components/rabbitmq"
Expand All @@ -35,9 +35,9 @@ var MetaObjects = common.CompositeRenderFunc(
contentservice.Objects,
proxy.Objects,
dashboard.Objects,
database.Objects,
imagebuildermk3.Objects,
migrations.Objects,
mysql.Objects,
openvsxproxy.Objects,
rabbitmq.Objects,
server.Objects,
Expand All @@ -62,8 +62,8 @@ var FullObjects = common.CompositeRenderFunc(
)

var MetaHelmDependencies = common.CompositeHelmFunc(
database.Helm,
jaegeroperator.Helm,
mysql.Helm,
minio.Helm,
rabbitmq.Helm,
)
Expand Down
54 changes: 54 additions & 0 deletions installer/pkg/components/database/cloudsql/configmap.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// Copyright (c) 2021 Gitpod GmbH. All rights reserved.
// Licensed under the GNU Affero General Public License (AGPL).
// See License-AGPL.txt in the project root for license information.

package cloudsql

import (
"embed"
"fmt"
"github.com/gitpod-io/gitpod/installer/pkg/common"
"io/fs"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
)

//go:embed init/*.sql
var initScriptFiles embed.FS

func configmap(ctx *common.RenderContext) ([]runtime.Object, error) {
initScripts, err := fs.ReadDir(initScriptFiles, initScriptDir)
if err != nil {
return nil, err
}

initScriptData := ""

for _, script := range initScripts {
file, err := fs.ReadFile(initScriptFiles, fmt.Sprintf("%s/%s", initScriptDir, script.Name()))

if err != nil {
return nil, err
}

fileStr := string(file)

// Add the file name for debugging purposes
initScriptData += fmt.Sprintf("-- %s\n\n%s", script.Name(), fileStr)
}

return []runtime.Object{
&corev1.ConfigMap{
TypeMeta: common.TypeMetaConfigmap,
ObjectMeta: metav1.ObjectMeta{
Name: SQLInitScripts,
Namespace: ctx.Namespace,
Labels: common.DefaultLabels(Component),
},
Data: map[string]string{
"init.sql": initScriptData,
},
},
}, nil
}
17 changes: 17 additions & 0 deletions installer/pkg/components/database/cloudsql/constants.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// Copyright (c) 2021 Gitpod GmbH. All rights reserved.
// Licensed under the GNU Affero General Public License (AGPL).
// See License-AGPL.txt in the project root for license information.

package cloudsql

const (
Component = "cloudsqlproxy"
dbSessionsImage = "mysql"
dbSessionsTag = "5.7.34"
ImageRepo = "b.gcr.io/cloudsql-docker"
ImageName = "gce-proxy"
ImageVersion = "1.11"
initScriptDir = "init"
Port = 3306
SQLInitScripts = "db-init-scripts"
)
91 changes: 91 additions & 0 deletions installer/pkg/components/database/cloudsql/deployment.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
// Copyright (c) 2021 Gitpod GmbH. All rights reserved.
// Licensed under the GNU Affero General Public License (AGPL).
// See License-AGPL.txt in the project root for license information.

package cloudsql

import (
"fmt"
"github.com/gitpod-io/gitpod/installer/pkg/common"
appsv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/util/intstr"
"k8s.io/utils/pointer"
)

func deployment(ctx *common.RenderContext) ([]runtime.Object, error) {
labels := common.DefaultLabels(Component)

return []runtime.Object{
&appsv1.Deployment{
TypeMeta: common.TypeMetaDeployment,
ObjectMeta: metav1.ObjectMeta{
Name: fmt.Sprintf("%s-cloud-sql-proxy", Component),
Namespace: ctx.Namespace,
Labels: labels,
},
Spec: appsv1.DeploymentSpec{
Strategy: appsv1.DeploymentStrategy{
Type: appsv1.RollingUpdateDeploymentStrategyType,
RollingUpdate: &appsv1.RollingUpdateDeployment{
MaxUnavailable: &intstr.IntOrString{IntVal: 0},
MaxSurge: &intstr.IntOrString{IntVal: 1},
},
},
Selector: &metav1.LabelSelector{MatchLabels: labels},
// todo(sje): receive config value
Replicas: pointer.Int32(1),
Template: corev1.PodTemplateSpec{
ObjectMeta: metav1.ObjectMeta{
Name: Component,
Namespace: ctx.Namespace,
Labels: labels,
},
Spec: corev1.PodSpec{
Affinity: &corev1.Affinity{},
ServiceAccountName: Component,
EnableServiceLinks: pointer.Bool(false),
DNSPolicy: "ClusterFirst",
RestartPolicy: "Always",
TerminationGracePeriodSeconds: pointer.Int64(30),
Volumes: []corev1.Volume{{
Name: "cloudsql",
VolumeSource: corev1.VolumeSource{EmptyDir: &corev1.EmptyDirVolumeSource{}},
}, {
Name: "gcloud-sql-token",
VolumeSource: corev1.VolumeSource{Secret: &corev1.SecretVolumeSource{
SecretName: ctx.Config.Database.CloudSQL.ServiceAccount.Name,
}},
}},
Containers: []corev1.Container{{
Name: "cloud-sql-proxy",
SecurityContext: &corev1.SecurityContext{
Privileged: pointer.Bool(false),
RunAsNonRoot: pointer.Bool(false),
},
Image: common.ImageName(ImageRepo, ImageName, ImageVersion),
Command: []string{
"/cloud_sql_proxy",
"-dir=/cloudsql",
fmt.Sprintf("-instances=%s=tcp:0.0.0.0:%d", ctx.Config.Database.CloudSQL.Instance, Port),
"-credential_file=/credentials/credentials.json",
},
Ports: []corev1.ContainerPort{{
ContainerPort: Port,
}},
VolumeMounts: []corev1.VolumeMount{{
MountPath: "/cloudsql",
Name: "cloudsql",
}, {
MountPath: "/credentials",
Name: "gcloud-sql-token",
}},
}},
},
},
},
},
}, nil
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
-- Copyright (c) 2020 Gitpod GmbH. All rights reserved.
-- Licensed under the MIT License. See License-MIT.txt in the project root for license information.

-- must be idempotent

CREATE DATABASE IF NOT EXISTS `gitpod-sessions` CHARSET utf8mb4;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could - not necessarily in this PR - move this script (these scripts) to something like pkg/components/database/init

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Possible, but the init scripts are subtly different.


USE `gitpod-sessions`;

CREATE TABLE IF NOT EXISTS sessions (
`session_id` varchar(128) COLLATE utf8mb4_bin NOT NULL,
`expires` int(11) unsigned NOT NULL,
`data` text COLLATE utf8mb4_bin,
`_lastModified` timestamp(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6),
PRIMARY KEY (`session_id`)
);
Loading