Path: blob/main/install/installer/pkg/components/spicedb/deployment.go
2506 views
// Copyright (c) 2021 Gitpod GmbH. All rights reserved.1// Licensed under the GNU Affero General Public License (AGPL).2// See License.AGPL.txt in the project root for license information.34package spicedb56import (7"errors"8"fmt"9"strings"1011"github.com/gitpod-io/gitpod/common-go/baseserver"12"github.com/gitpod-io/gitpod/installer/pkg/cluster"13"github.com/gitpod-io/gitpod/installer/pkg/common"1415appsv1 "k8s.io/api/apps/v1"16corev1 "k8s.io/api/core/v1"17v1 "k8s.io/api/core/v1"18"k8s.io/apimachinery/pkg/api/resource"19metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"20"k8s.io/apimachinery/pkg/runtime"21"k8s.io/apimachinery/pkg/util/intstr"22"k8s.io/utils/pointer"23)2425func deployment(ctx *common.RenderContext) ([]runtime.Object, error) {26labels := common.CustomizeLabel(ctx, Component, common.TypeMetaDeployment)2728cfg := getExperimentalSpiceDBConfig(ctx)29if cfg == nil || !cfg.Enabled {30return nil, nil31}3233if cfg.SecretRef == "" {34return nil, errors.New("missing configuration for spicedb.secretRef")35}3637bootstrapVolume, bootstrapVolumeMount, bootstrapFiles, contentHash, err := getBootstrapConfig(ctx)38if err != nil {39return nil, fmt.Errorf("failed to get bootstrap config: %w", err)40}4142replicas := common.Replicas(ctx, Component)4344return []runtime.Object{45&appsv1.Deployment{46TypeMeta: common.TypeMetaDeployment,47ObjectMeta: metav1.ObjectMeta{48Name: Component,49Namespace: ctx.Namespace,50Labels: labels,51Annotations: common.CustomizeAnnotation(ctx, Component, common.TypeMetaDeployment),52},53Spec: appsv1.DeploymentSpec{54Selector: &metav1.LabelSelector{MatchLabels: common.DefaultLabels(Component)},55Replicas: replicas,56Strategy: common.DeploymentStrategy,57Template: corev1.PodTemplateSpec{58ObjectMeta: metav1.ObjectMeta{59Name: Component,60Namespace: ctx.Namespace,61Labels: labels,62Annotations: common.CustomizeAnnotation(ctx, Component, common.TypeMetaDeployment, func() map[string]string {63return map[string]string{64common.AnnotationConfigChecksum: contentHash,65}66}),67},68Spec: corev1.PodSpec{69Affinity: cluster.WithNodeAffinityHostnameAntiAffinity(Component, cluster.AffinityLabelMeta),70TopologySpreadConstraints: cluster.WithHostnameTopologySpread(Component),71PriorityClassName: common.SystemNodeCritical,72ServiceAccountName: Component,73EnableServiceLinks: pointer.Bool(false),74DNSPolicy: corev1.DNSClusterFirst,75RestartPolicy: corev1.RestartPolicyAlways,76TerminationGracePeriodSeconds: pointer.Int64(30),77SecurityContext: &corev1.PodSecurityContext{78RunAsNonRoot: pointer.Bool(false),79},80InitContainers: []corev1.Container{81dbWaiter(ctx),82},83Containers: []corev1.Container{84{85Name: ContainerName,86Image: ctx.ImageName(common.ThirdPartyContainerRepo(ctx.Config.Repository, RegistryRepo), RegistryImage, ImageTag),87ImagePullPolicy: corev1.PullIfNotPresent,88Args: (func() []string {89args := []string{90"serve",91"--log-format=json",92"--log-level=error",93"--datastore-engine=mysql",94"--datastore-conn-max-open=100",95"--telemetry-endpoint=", // disable telemetry to https://telemetry.authzed.com96fmt.Sprintf("--datastore-bootstrap-files=%s", strings.Join(bootstrapFiles, ",")),97"--dispatch-cluster-enabled=true",98"--datastore-bootstrap-overwrite=true",99fmt.Sprintf("--metrics-addr=127.0.0.1:%d", baseserver.BuiltinMetricsPort),100}101102// Dispatching only makes sense, when we have more than one replica103if *replicas > 1 {104args = append(args, fmt.Sprintf("--dispatch-upstream-addr=kubernetes:///spicedb:%d", ContainerDispatchPort))105}106107return args108})(),109Env: common.CustomizeEnvvar(ctx, Component, common.MergeEnv(110common.DefaultEnv(&ctx.Config),111spicedbEnvVars(ctx),112)),113Ports: []corev1.ContainerPort{114{115ContainerPort: ContainerGRPCPort,116Name: ContainerGRPCName,117Protocol: *common.TCPProtocol,118},119{120ContainerPort: ContainerHTTPPort,121Name: ContainerHTTPName,122Protocol: *common.TCPProtocol,123},124{125ContainerPort: ContainerDashboardPort,126Name: ContainerDashboardName,127Protocol: *common.TCPProtocol,128},129{130ContainerPort: ContainerDispatchPort,131Name: ContainerDispatchName,132Protocol: *common.TCPProtocol,133},134{135ContainerPort: ContainerPrometheusPort,136Name: ContainterPrometheusName,137Protocol: *common.TCPProtocol,138},139},140Resources: common.ResourceRequirements(ctx, Component, ContainerName, corev1.ResourceRequirements{141Requests: corev1.ResourceList{142"cpu": resource.MustParse("1"),143"memory": resource.MustParse("500M"),144},145}),146SecurityContext: &corev1.SecurityContext{147RunAsGroup: pointer.Int64(65532),148RunAsNonRoot: pointer.Bool(true),149RunAsUser: pointer.Int64(65532),150},151// Compare issue https://linear.app/gitpod/issue/EXP-906/spicedb-deployment-fails-in-gitpod-dedicated:152// - this should be a single grpc_health_probe-based readiness probe153// - but it started failing (with k8s 1.27.7 ?)154// - to unblock container startup, we split into readiness and liveness probes155ReadinessProbe: &corev1.Probe{156ProbeHandler: corev1.ProbeHandler{157// Exec: &v1.ExecAction{158// Command: []string{"grpc_health_probe", "-v", fmt.Sprintf("-addr=localhost:%d", ContainerGRPCPort)},159// },160TCPSocket: &v1.TCPSocketAction{161Port: intstr.FromInt(ContainerGRPCPort),162},163},164InitialDelaySeconds: 1,165// try again every 2 seconds166PeriodSeconds: 2,167// fail after 30 * 2 + 1 = 61168FailureThreshold: 30,169SuccessThreshold: 1,170TimeoutSeconds: 1,171},172// Because we can't test readiness properly to not block startup, we use a liveness probe to test whether the cluster has come up173LivenessProbe: &corev1.Probe{174ProbeHandler: corev1.ProbeHandler{175Exec: &v1.ExecAction{176Command: []string{"grpc_health_probe", "-v", fmt.Sprintf("-addr=localhost:%d", ContainerGRPCPort)},177},178},179InitialDelaySeconds: 10,180PeriodSeconds: 10,181FailureThreshold: 3,182SuccessThreshold: 1,183TimeoutSeconds: 1,184},185VolumeMounts: []v1.VolumeMount{186bootstrapVolumeMount,187},188},189*common.KubeRBACProxyContainer(ctx),190},191Tolerations: common.WithTolerationWorkspaceComponentNotReady(ctx),192Volumes: []v1.Volume{193bootstrapVolume,194},195},196},197},198},199}, nil200}201202func dbEnvVars(ctx *common.RenderContext) []corev1.EnvVar {203return common.DatabaseEnv(&ctx.Config)204}205206func dbWaiter(ctx *common.RenderContext) v1.Container {207databaseWaiter := common.DatabaseMigrationWaiterContainer(ctx)208// Use updated env-vars, which in the case cloud-sql-proxy override default db conf209210databaseWaiter.Env = dbEnvVars(ctx)211212return *databaseWaiter213}214215func spicedbEnvVars(ctx *common.RenderContext) []corev1.EnvVar {216cfg := getExperimentalSpiceDBConfig(ctx)217if cfg == nil {218return nil219}220221return common.MergeEnv(222dbEnvVars(ctx),223[]corev1.EnvVar{224{225Name: "SPICEDB_DATASTORE_CONN_URI",226Value: "$(DB_USERNAME):$(DB_PASSWORD)@tcp($(DB_HOST):$(DB_PORT))/authorization?parseTime=true",227},228{229Name: "SPICEDB_GRPC_PRESHARED_KEY",230ValueFrom: &corev1.EnvVarSource{231SecretKeyRef: &corev1.SecretKeySelector{232LocalObjectReference: corev1.LocalObjectReference{233Name: cfg.SecretRef,234},235Key: SecretPresharedKeyName,236},237},238},239},240)241}242243244