From 64117cf7374d58737be09e6f8131ffd55bfd61c7 Mon Sep 17 00:00:00 2001 From: Simon Emms Date: Tue, 19 Oct 2021 10:41:37 +0100 Subject: [PATCH] [installer]: general fixes for the Helm dependencies --- installer/BUILD.yaml | 1 + .../components/docker-registry/constants.go | 5 ++- .../pkg/components/docker-registry/secret.go | 27 +++++++++++- .../image-builder-mk3/deployment.go | 2 +- installer/pkg/components/mysql/configmap.go | 36 +++++++++++++-- installer/pkg/components/mysql/constants.go | 6 ++- installer/pkg/components/mysql/helm.go | 8 ++-- .../init/00-create-and-init-sessions-db.sql | 16 +++++++ .../mysql/init/01-recreate-gitpod-db.sql | 18 ++++++++ installer/pkg/components/mysql/objects.go | 14 ++++++ installer/pkg/components/mysql/secret.go | 18 +++++--- installer/pkg/components/mysql/service.go | 44 +++++++++++++++++++ installer/pkg/components/rabbitmq/helm.go | 20 ++++++--- installer/pkg/components/rabbitmq/objects.go | 1 + .../pkg/components/rabbitmq/rolebinding.go | 33 ++++++++++++++ .../components/registry-facade/daemonset.go | 2 +- .../pkg/components/ws-manager/tlssecret.go | 6 +-- .../third_party/charts/mysql/values.yaml | 1 - 18 files changed, 232 insertions(+), 26 deletions(-) create mode 100644 installer/pkg/components/mysql/init/00-create-and-init-sessions-db.sql create mode 100644 installer/pkg/components/mysql/init/01-recreate-gitpod-db.sql create mode 100644 installer/pkg/components/mysql/service.go create mode 100644 installer/pkg/components/rabbitmq/rolebinding.go diff --git a/installer/BUILD.yaml b/installer/BUILD.yaml index a6b2e99d4681e4..5db71d741a7d77 100644 --- a/installer/BUILD.yaml +++ b/installer/BUILD.yaml @@ -10,6 +10,7 @@ packages: - "pkg/components/**/*.crt" - "pkg/components/**/*.key" - "pkg/components/**/*.pem" + - "pkg/components/**/*.sql" - "third_party/**/*" deps: - components/common-go:lib diff --git a/installer/pkg/components/docker-registry/constants.go b/installer/pkg/components/docker-registry/constants.go index e59be9491b8b5b..267b8cff2f784e 100644 --- a/installer/pkg/components/docker-registry/constants.go +++ b/installer/pkg/components/docker-registry/constants.go @@ -5,6 +5,7 @@ package dockerregistry const ( - BuiltInRegistrySecret = "builtin-registry-auth" - Component = "docker-registry" + BuiltInRegistryAuth = "builtin-registry-auth" + BuiltInRegistryCerts = "builtin-registry-certs" + Component = "docker-registry" ) diff --git a/installer/pkg/components/docker-registry/secret.go b/installer/pkg/components/docker-registry/secret.go index ab54097edef9a3..d9cadeb4879f6e 100644 --- a/installer/pkg/components/docker-registry/secret.go +++ b/installer/pkg/components/docker-registry/secret.go @@ -7,6 +7,10 @@ package dockerregistry import ( "encoding/base64" "encoding/json" + "fmt" + v1 "github.com/jetstack/cert-manager/pkg/apis/certmanager/v1" + cmmeta "github.com/jetstack/cert-manager/pkg/apis/meta/v1" + "time" "github.com/gitpod-io/gitpod/installer/pkg/common" corev1 "k8s.io/api/core/v1" @@ -45,10 +49,12 @@ func secret(ctx *common.RenderContext) ([]runtime.Object, error) { return nil, err } + oneYear := &metav1.Duration{Duration: time.Hour * 24 * 365} + return []runtime.Object{&corev1.Secret{ TypeMeta: common.TypeMetaSecret, ObjectMeta: metav1.ObjectMeta{ - Name: BuiltInRegistrySecret, + Name: BuiltInRegistryAuth, Namespace: ctx.Namespace, Labels: common.DefaultLabels(Component), }, @@ -58,5 +64,24 @@ func secret(ctx *common.RenderContext) ([]runtime.Object, error) { "user": []byte(user), "password": []byte(password), }, + }, &v1.Certificate{ + TypeMeta: common.TypeMetaCertificate, + ObjectMeta: metav1.ObjectMeta{ + Name: BuiltInRegistryCerts, + Namespace: ctx.Namespace, + Labels: common.DefaultLabels(Component), + }, + Spec: v1.CertificateSpec{ + Duration: oneYear, + SecretName: BuiltInRegistryCerts, + IssuerRef: cmmeta.ObjectReference{ + Name: common.CertManagerCAIssuer, + Kind: "Issuer", + Group: "cert-manager.io", + }, + DNSNames: []string{ + fmt.Sprintf("registry.%s.svc.cluster.local", ctx.Namespace), + }, + }, }}, nil } diff --git a/installer/pkg/components/image-builder-mk3/deployment.go b/installer/pkg/components/image-builder-mk3/deployment.go index 90968996a602d8..b8e99c3a32790f 100644 --- a/installer/pkg/components/image-builder-mk3/deployment.go +++ b/installer/pkg/components/image-builder-mk3/deployment.go @@ -53,7 +53,7 @@ func deployment(ctx *common.RenderContext) ([]runtime.Object, error) { volumes = append(volumes, corev1.Volume{ Name: "pull-secret", VolumeSource: corev1.VolumeSource{Secret: &corev1.SecretVolumeSource{ - SecretName: dockerregistry.BuiltInRegistrySecret, + SecretName: dockerregistry.BuiltInRegistryAuth, }}, }) } diff --git a/installer/pkg/components/mysql/configmap.go b/installer/pkg/components/mysql/configmap.go index 622f9c7c3286f1..40a55754321245 100644 --- a/installer/pkg/components/mysql/configmap.go +++ b/installer/pkg/components/mysql/configmap.go @@ -5,15 +5,45 @@ package mysql 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" + "strings" ) +//go:embed init/*.sql +var initScriptFiles embed.FS + func configmap(ctx *common.RenderContext) ([]runtime.Object, error) { - // todo(sje): work out how best to load the db init scripts - I'm thinking generate at buildtime to a single .sql file then embed it here - var initScripts []byte + if !enabled(ctx) { + return nil, nil + } + + 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) + // Replace variables in the script + fileStr = strings.Replace(fileStr, "__GITPOD_DB_NAME__", Database, -1) + + // Add the file name for debugging purposes + initScriptData += fmt.Sprintf("-- %s\n\n%s", script.Name(), fileStr) + } return []runtime.Object{ &corev1.ConfigMap{ @@ -24,7 +54,7 @@ func configmap(ctx *common.RenderContext) ([]runtime.Object, error) { Labels: common.DefaultLabels(Component), }, Data: map[string]string{ - "init.sql": string(initScripts), + "init.sql": initScriptData, }, }, }, nil diff --git a/installer/pkg/components/mysql/constants.go b/installer/pkg/components/mysql/constants.go index a6237f715ca306..d4b1a97af68556 100644 --- a/installer/pkg/components/mysql/constants.go +++ b/installer/pkg/components/mysql/constants.go @@ -7,8 +7,12 @@ package mysql import "github.com/gitpod-io/gitpod/installer/pkg/common" const ( - Component = "mysql" + Component = "db" // mysql is used by the Helm package InClusterDbSecret = common.InClusterDbSecret + Port = 3306 SQLInitScripts = "db-init-scripts" SQLPasswordName = "db-password" + Username = "gitpod" + Database = "gitpod" + initScriptDir = "init" ) diff --git a/installer/pkg/components/mysql/helm.go b/installer/pkg/components/mysql/helm.go index cb5d63e4abe47c..c3f187c6d0b030 100644 --- a/installer/pkg/components/mysql/helm.go +++ b/installer/pkg/components/mysql/helm.go @@ -9,17 +9,19 @@ import ( "github.com/gitpod-io/gitpod/installer/pkg/helm" "github.com/gitpod-io/gitpod/installer/third_party/charts" "helm.sh/helm/v3/pkg/cli/values" - "k8s.io/utils/pointer" ) var Helm = common.CompositeHelmFunc( helm.ImportTemplate(charts.MySQL(), helm.TemplateConfig{}, func(cfg *common.RenderContext) (*common.HelmConfig, error) { return &common.HelmConfig{ - Enabled: pointer.BoolDeref(cfg.Config.Database.InCluster, false), + Enabled: enabled(cfg), Values: &values.Options{ Values: []string{ helm.KeyValue("mysql.auth.existingSecret", SQLPasswordName), - helm.KeyValue("mysql.initdbScriptsConfigMap", SQLPasswordName), + helm.KeyValue("mysql.auth.database", Database), + helm.KeyValue("mysql.auth.username", Username), + helm.KeyValue("mysql.initdbScriptsConfigMap", SQLInitScripts), + helm.KeyValue("mysql.serviceAccount.name", Component), }, }, }, nil diff --git a/installer/pkg/components/mysql/init/00-create-and-init-sessions-db.sql b/installer/pkg/components/mysql/init/00-create-and-init-sessions-db.sql new file mode 100644 index 00000000000000..ffe04f00e855bd --- /dev/null +++ b/installer/pkg/components/mysql/init/00-create-and-init-sessions-db.sql @@ -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; + +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`) +); diff --git a/installer/pkg/components/mysql/init/01-recreate-gitpod-db.sql b/installer/pkg/components/mysql/init/01-recreate-gitpod-db.sql new file mode 100644 index 00000000000000..7d96d79840de13 --- /dev/null +++ b/installer/pkg/components/mysql/init/01-recreate-gitpod-db.sql @@ -0,0 +1,18 @@ +-- 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 + +-- @gitpodDB contains name of the DB the script manipulates, and is replaced by the file reader +SET +@gitpodDB = IFNULL(@gitpodDB, '`__GITPOD_DB_NAME__`'); + +SET +@statementStr = CONCAT('DROP DATABASE IF EXISTS ', @gitpodDB); +PREPARE statement FROM @statementStr; +EXECUTE statement; + +SET +@statementStr = CONCAT('CREATE DATABASE ', @gitpodDB, ' CHARSET utf8mb4'); +PREPARE statement FROM @statementStr; +EXECUTE statement; diff --git a/installer/pkg/components/mysql/objects.go b/installer/pkg/components/mysql/objects.go index c02d450bba5af3..3607d6d22dd72c 100644 --- a/installer/pkg/components/mysql/objects.go +++ b/installer/pkg/components/mysql/objects.go @@ -6,9 +6,23 @@ package mysql import ( "github.com/gitpod-io/gitpod/installer/pkg/common" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/utils/pointer" ) +func enabled(cfg *common.RenderContext) bool { + return pointer.BoolDeref(cfg.Config.Database.InCluster, false) +} + var Objects = common.CompositeRenderFunc( configmap, secrets, + service, + common.CompositeRenderFunc(func(cfg *common.RenderContext) ([]runtime.Object, error) { + if !enabled(cfg) { + return nil, nil + } + + return common.DefaultServiceAccount(Component)(cfg) + }), ) diff --git a/installer/pkg/components/mysql/secret.go b/installer/pkg/components/mysql/secret.go index c14cece9252261..a68bfdb148c94d 100644 --- a/installer/pkg/components/mysql/secret.go +++ b/installer/pkg/components/mysql/secret.go @@ -5,18 +5,23 @@ package mysql import ( + "fmt" "github.com/gitpod-io/gitpod/installer/pkg/common" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" - "k8s.io/utils/pointer" ) func secrets(ctx *common.RenderContext) ([]runtime.Object, error) { - if !pointer.BoolDeref(ctx.Config.Database.InCluster, false) { + if !enabled(ctx) { return nil, nil } + rootPassword, err := common.RandomString(20) + if err != nil { + return nil, err + } + password, err := common.RandomString(20) if err != nil { return nil, err @@ -30,7 +35,8 @@ func secrets(ctx *common.RenderContext) ([]runtime.Object, error) { Labels: common.DefaultLabels(Component), }, Data: map[string][]byte{ - "mysql-root-password": []byte(password), + "mysql-root-password": []byte(rootPassword), + "mysql-password": []byte(password), }, }, &corev1.Secret{ TypeMeta: common.TypeMetaSecret, @@ -40,9 +46,11 @@ func secrets(ctx *common.RenderContext) ([]runtime.Object, error) { Labels: common.DefaultLabels(Component), }, Data: map[string][]byte{ - "host": []byte("db"), - "port": []byte("3306"), + "database": []byte(Database), + "host": []byte(Component), + "port": []byte(fmt.Sprintf("%d", Port)), "password": []byte(password), + "username": []byte(Username), }, }}, nil } diff --git a/installer/pkg/components/mysql/service.go b/installer/pkg/components/mysql/service.go new file mode 100644 index 00000000000000..14c1a7936ca799 --- /dev/null +++ b/installer/pkg/components/mysql/service.go @@ -0,0 +1,44 @@ +// 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 mysql + +import ( + "github.com/gitpod-io/gitpod/installer/pkg/common" + 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" +) + +// service this doesn't use the common.GenerateService function +// because it's more complex than this caters for +func service(ctx *common.RenderContext) ([]runtime.Object, error) { + if !enabled(ctx) { + return nil, nil + } + + labels := common.DefaultLabels(Component) + + return []runtime.Object{&corev1.Service{ + TypeMeta: common.TypeMetaService, + ObjectMeta: metav1.ObjectMeta{ + Name: Component, + Namespace: ctx.Namespace, + Labels: labels, + }, + Spec: corev1.ServiceSpec{ + Ports: []corev1.ServicePort{{ + Protocol: *common.TCPProtocol, + Port: Port, + TargetPort: intstr.IntOrString{IntVal: Port}, + }}, + // todo(sje): selector is different if using CloudSQLProxy + Selector: map[string]string{ + "app.kubernetes.io/name": "mysql", + }, + Type: corev1.ServiceTypeClusterIP, + }, + }}, nil +} diff --git a/installer/pkg/components/rabbitmq/helm.go b/installer/pkg/components/rabbitmq/helm.go index 83e71a45cc73a6..3e7ce04c93fa78 100644 --- a/installer/pkg/components/rabbitmq/helm.go +++ b/installer/pkg/components/rabbitmq/helm.go @@ -11,6 +11,7 @@ import ( "github.com/gitpod-io/gitpod/installer/pkg/helm" "github.com/gitpod-io/gitpod/installer/third_party/charts" "helm.sh/helm/v3/pkg/cli/values" + "sigs.k8s.io/yaml" "strings" ) @@ -105,7 +106,8 @@ type config struct { } func generateParameters(username string, password string, input []parameter) ([]parameter, error) { - var params []parameter + // Ensures this defaults to [] not null when marshalled to JSON + params := make([]parameter, 0) for _, item := range input { // Sort out default values @@ -156,10 +158,7 @@ func generateParameters(username string, password string, input []parameter) ([] var Helm = common.CompositeHelmFunc( helm.ImportTemplate(charts.RabbitMQ(), helm.TemplateConfig{}, func(cfg *common.RenderContext) (*common.HelmConfig, error) { - username, err := common.RandomString(20) - if err != nil { - return nil, err - } + username := "gitpod" password, err := common.RandomString(20) if err != nil { @@ -252,6 +251,16 @@ var Helm = common.CompositeHelmFunc( return nil, err } + shovelsTemplate, err := yaml.Marshal(parameters) + if err != nil { + return nil, err + } + + shovelsTemplateFileName, err := helm.KeyFileValue("shovelsTemplate", shovelsTemplate) + if err != nil { + return nil, err + } + return &common.HelmConfig{ Enabled: true, Values: &values.Options{ @@ -266,6 +275,7 @@ var Helm = common.CompositeHelmFunc( // This is too complex to be sent as a string FileValues: []string{ loadDefinitionFilename, + shovelsTemplateFileName, }, }, }, nil diff --git a/installer/pkg/components/rabbitmq/objects.go b/installer/pkg/components/rabbitmq/objects.go index 4387107942fa65..c8f6b2cfb76dc9 100644 --- a/installer/pkg/components/rabbitmq/objects.go +++ b/installer/pkg/components/rabbitmq/objects.go @@ -7,5 +7,6 @@ package rabbitmq import "github.com/gitpod-io/gitpod/installer/pkg/common" var Objects = common.CompositeRenderFunc( + rolebinding, secrets, ) diff --git a/installer/pkg/components/rabbitmq/rolebinding.go b/installer/pkg/components/rabbitmq/rolebinding.go new file mode 100644 index 00000000000000..1881e96544a5f6 --- /dev/null +++ b/installer/pkg/components/rabbitmq/rolebinding.go @@ -0,0 +1,33 @@ +// 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 rabbitmq + +import ( + "fmt" + "github.com/gitpod-io/gitpod/installer/pkg/common" + v1 "k8s.io/api/rbac/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" +) + +func rolebinding(ctx *common.RenderContext) ([]runtime.Object, error) { + return []runtime.Object{&v1.RoleBinding{ + TypeMeta: common.TypeMetaRoleBinding, + ObjectMeta: metav1.ObjectMeta{ + Name: Component, + Namespace: ctx.Namespace, + Labels: common.DefaultLabels(Component), + }, + Subjects: []v1.Subject{{ + Kind: "ServiceAccount", + Name: Component, + }}, + RoleRef: v1.RoleRef{ + Kind: "ClusterRole", + Name: fmt.Sprintf("%s-ns-psp:restricted-root-user", ctx.Namespace), + APIGroup: "rbac.authorization.k8s.io", + }, + }}, nil +} diff --git a/installer/pkg/components/registry-facade/daemonset.go b/installer/pkg/components/registry-facade/daemonset.go index 6ffe19322e18cb..de76f2c3fe81e7 100644 --- a/installer/pkg/components/registry-facade/daemonset.go +++ b/installer/pkg/components/registry-facade/daemonset.go @@ -46,7 +46,7 @@ func daemonset(ctx *common.RenderContext) ([]runtime.Object, error) { Name: name, VolumeSource: corev1.VolumeSource{ Secret: &corev1.SecretVolumeSource{ - SecretName: dockerregistry.BuiltInRegistrySecret, + SecretName: dockerregistry.BuiltInRegistryAuth, }, }, }) diff --git a/installer/pkg/components/ws-manager/tlssecret.go b/installer/pkg/components/ws-manager/tlssecret.go index 665235a1e15098..c8e22203b1888e 100644 --- a/installer/pkg/components/ws-manager/tlssecret.go +++ b/installer/pkg/components/ws-manager/tlssecret.go @@ -33,7 +33,7 @@ func tlssecret(ctx *common.RenderContext) ([]runtime.Object, error) { } sixMonths := &metav1.Duration{Duration: time.Hour * 4380} - issuer := "ca-issuer" + issuer := common.CertManagerCAIssuer return []runtime.Object{ &certmanagerv1.Certificate{ @@ -49,7 +49,7 @@ func tlssecret(ctx *common.RenderContext) ([]runtime.Object, error) { DNSNames: serverAltNames, IssuerRef: cmmeta.ObjectReference{ Name: issuer, - Kind: "Issuer", + Kind: "ClusterIssuer", Group: "cert-manager.io", }, }, @@ -67,7 +67,7 @@ func tlssecret(ctx *common.RenderContext) ([]runtime.Object, error) { DNSNames: clientAltNames, IssuerRef: cmmeta.ObjectReference{ Name: issuer, - Kind: "Issuer", + Kind: "ClusterIssuer", Group: "cert-manager.io", }, }, diff --git a/installer/third_party/charts/mysql/values.yaml b/installer/third_party/charts/mysql/values.yaml index 43f7b4326d5cdf..3eb935a0c87fad 100644 --- a/installer/third_party/charts/mysql/values.yaml +++ b/installer/third_party/charts/mysql/values.yaml @@ -18,6 +18,5 @@ mysql: memory: 128Mi serviceAccount: create: false - name: db volumePermissions: enabled: true \ No newline at end of file