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