Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
gitpod-io
GitHub Repository: gitpod-io/gitpod
Path: blob/main/install/installer/pkg/components/ws-manager-mk2/configmap.go
2501 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 wsmanagermk2
6
7
import (
8
"fmt"
9
"path/filepath"
10
"time"
11
12
wsdaemon "github.com/gitpod-io/gitpod/installer/pkg/components/ws-daemon"
13
"sigs.k8s.io/yaml"
14
15
"github.com/gitpod-io/gitpod/common-go/grpc"
16
"github.com/gitpod-io/gitpod/common-go/util"
17
storageconfig "github.com/gitpod-io/gitpod/content-service/api/config"
18
"github.com/gitpod-io/gitpod/installer/pkg/common"
19
configv1 "github.com/gitpod-io/gitpod/installer/pkg/config/v1"
20
"github.com/gitpod-io/gitpod/installer/pkg/config/v1/experimental"
21
"github.com/gitpod-io/gitpod/ws-manager/api/config"
22
23
corev1 "k8s.io/api/core/v1"
24
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
25
"k8s.io/apimachinery/pkg/runtime"
26
)
27
28
func configmap(ctx *common.RenderContext) ([]runtime.Object, error) {
29
cfgTpls := ctx.Config.Workspace.Templates
30
if cfgTpls == nil {
31
cfgTpls = &configv1.WorkspaceTemplates{}
32
}
33
templatesCfg, tpls, err := buildWorkspaceTemplates(ctx, cfgTpls, "")
34
if err != nil {
35
return nil, err
36
}
37
38
quantityString := func(idx corev1.ResourceList, key corev1.ResourceName) string {
39
q, ok := idx[key]
40
if !ok {
41
return ""
42
}
43
return (&q).String()
44
}
45
46
timeoutAfterClose := util.Duration(2 * time.Minute)
47
if ctx.Config.Workspace.TimeoutAfterClose != nil {
48
timeoutAfterClose = *ctx.Config.Workspace.TimeoutAfterClose
49
}
50
51
classes := map[string]*config.WorkspaceClass{
52
config.DefaultWorkspaceClass: {
53
Name: config.DefaultWorkspaceClass,
54
Container: config.ContainerConfiguration{
55
Requests: &config.ResourceRequestConfiguration{
56
CPU: quantityString(ctx.Config.Workspace.Resources.Requests, corev1.ResourceCPU),
57
Memory: quantityString(ctx.Config.Workspace.Resources.Requests, corev1.ResourceMemory),
58
EphemeralStorage: quantityString(ctx.Config.Workspace.Resources.Requests, corev1.ResourceEphemeralStorage),
59
},
60
Limits: &config.ResourceLimitConfiguration{
61
CPU: &config.CpuResourceLimit{
62
MinLimit: quantityString(ctx.Config.Workspace.Resources.Limits, corev1.ResourceCPU),
63
BurstLimit: quantityString(ctx.Config.Workspace.Resources.Limits, corev1.ResourceCPU),
64
},
65
Memory: quantityString(ctx.Config.Workspace.Resources.Limits, corev1.ResourceMemory),
66
EphemeralStorage: quantityString(ctx.Config.Workspace.Resources.Limits, corev1.ResourceEphemeralStorage),
67
Storage: quantityString(ctx.Config.Workspace.Resources.Limits, corev1.ResourceStorage),
68
},
69
},
70
Templates: templatesCfg,
71
},
72
}
73
var preferredWorkspaceClass string
74
75
installationShortNameSuffix := ""
76
if ctx.Config.Metadata.InstallationShortname != "" && ctx.Config.Metadata.InstallationShortname != configv1.InstallationShortNameOldDefault {
77
installationShortNameSuffix = "-" + ctx.Config.Metadata.InstallationShortname
78
}
79
80
var schedulerName string
81
gitpodHostURL := "https://" + ctx.Config.Domain
82
workspaceClusterHost := fmt.Sprintf("ws%s.%s", installationShortNameSuffix, ctx.Config.Domain)
83
workspaceURLTemplate := fmt.Sprintf("https://{{ .Prefix }}.ws%s.%s", installationShortNameSuffix, ctx.Config.Domain)
84
workspacePortURLTemplate := fmt.Sprintf("https://{{ .WorkspacePort }}-{{ .Prefix }}.ws%s.%s", installationShortNameSuffix, ctx.Config.Domain)
85
hostWorkingArea := wsdaemon.HostWorkingAreaMk2
86
87
rateLimits := map[string]grpc.RateLimit{}
88
89
err = ctx.WithExperimental(func(ucfg *experimental.Config) error {
90
if ucfg.Workspace == nil {
91
return nil
92
}
93
for k, c := range ucfg.Workspace.WorkspaceClasses {
94
tplsCfg, ctpls, err := buildWorkspaceTemplates(ctx, &configv1.WorkspaceTemplates{
95
Default: c.Templates.Default,
96
Prebuild: c.Templates.Prebuild,
97
ImageBuild: c.Templates.ImageBuild,
98
Regular: c.Templates.Regular,
99
}, k)
100
if err != nil {
101
return err
102
}
103
classes[k] = &config.WorkspaceClass{
104
Name: c.Name,
105
Description: c.Description,
106
Container: config.ContainerConfiguration{
107
Requests: &config.ResourceRequestConfiguration{
108
CPU: quantityString(c.Resources.Requests, corev1.ResourceCPU),
109
Memory: quantityString(c.Resources.Requests, corev1.ResourceMemory),
110
EphemeralStorage: quantityString(c.Resources.Requests, corev1.ResourceEphemeralStorage),
111
},
112
Limits: &config.ResourceLimitConfiguration{
113
CPU: &config.CpuResourceLimit{
114
MinLimit: c.Resources.Limits.Cpu.MinLimit,
115
BurstLimit: c.Resources.Limits.Cpu.BurstLimit,
116
},
117
Memory: c.Resources.Limits.Memory,
118
EphemeralStorage: c.Resources.Limits.EphemeralStorage,
119
Storage: c.Resources.Limits.Storage,
120
},
121
},
122
Templates: tplsCfg,
123
}
124
for tmpl_n, tmpl_v := range ctpls {
125
if _, ok := tpls[tmpl_n]; ok {
126
return fmt.Errorf("duplicate workspace template %q in workspace class %q", tmpl_n, k)
127
}
128
tpls[tmpl_n] = tmpl_v
129
}
130
}
131
preferredWorkspaceClass = ucfg.Workspace.PreferredWorkspaceClass
132
if preferredWorkspaceClass == "" {
133
// if no preferred workspace class is set, use a random one (maps have no order, there is no "first")
134
for _, k := range ucfg.Workspace.WorkspaceClasses {
135
preferredWorkspaceClass = k.Name
136
break
137
}
138
}
139
140
schedulerName = ucfg.Workspace.SchedulerName
141
if ucfg.Workspace.HostURL != "" {
142
gitpodHostURL = ucfg.Workspace.HostURL
143
}
144
if ucfg.Workspace.WorkspaceClusterHost != "" {
145
workspaceClusterHost = ucfg.Workspace.WorkspaceClusterHost
146
}
147
if ucfg.Workspace.WorkspaceURLTemplate != "" {
148
workspaceURLTemplate = ucfg.Workspace.WorkspaceURLTemplate
149
}
150
if ucfg.Workspace.WorkspacePortURLTemplate != "" {
151
workspacePortURLTemplate = ucfg.Workspace.WorkspacePortURLTemplate
152
}
153
rateLimits = ucfg.Workspace.WSManagerRateLimits
154
155
return nil
156
})
157
if err != nil {
158
return nil, err
159
}
160
161
var imageBuilderTLS struct {
162
CA string `json:"ca"`
163
Certificate string `json:"crt"`
164
PrivateKey string `json:"key"`
165
}
166
if ctx.Config.Kind == configv1.InstallationWorkspace {
167
// Image builder TLS is only enabled in workspace clusters. This check
168
// can be removed once image-builder-mk3 has been removed from application clusters
169
// (https://github.com/gitpod-io/gitpod/issues/7845).
170
imageBuilderTLS = struct {
171
CA string `json:"ca"`
172
Certificate string `json:"crt"`
173
PrivateKey string `json:"key"`
174
}{
175
CA: "/image-builder-mk3-tls-certs/ca.crt",
176
Certificate: "/image-builder-mk3-tls-certs/tls.crt",
177
PrivateKey: "/image-builder-mk3-tls-certs/tls.key",
178
}
179
}
180
181
wsmcfg := config.ServiceConfiguration{
182
Manager: config.Configuration{
183
Namespace: ctx.Namespace,
184
SecretsNamespace: common.WorkspaceSecretsNamespace,
185
SchedulerName: schedulerName,
186
SeccompProfile: fmt.Sprintf("workspace_default_%s.json", ctx.VersionManifest.Version),
187
WorkspaceDaemon: config.WorkspaceDaemonConfiguration{
188
Port: 8080,
189
TLS: struct {
190
Authority string `json:"ca"`
191
Certificate string `json:"crt"`
192
PrivateKey string `json:"key"`
193
}{
194
Authority: "/ws-daemon-tls-certs/ca.crt",
195
Certificate: "/ws-daemon-tls-certs/tls.crt",
196
PrivateKey: "/ws-daemon-tls-certs/tls.key",
197
},
198
},
199
WorkspaceClasses: classes,
200
PreferredWorkspaceClass: preferredWorkspaceClass,
201
HeartbeatInterval: util.Duration(30 * time.Second),
202
GitpodHostURL: gitpodHostURL,
203
WorkspaceClusterHost: workspaceClusterHost,
204
InitProbe: config.InitProbeConfiguration{
205
Timeout: (1 * time.Second).String(),
206
},
207
WorkspaceURLTemplate: workspaceURLTemplate,
208
WorkspacePortURLTemplate: workspacePortURLTemplate,
209
WorkspaceHostPath: hostWorkingArea,
210
Timeouts: config.WorkspaceTimeoutConfiguration{
211
AfterClose: timeoutAfterClose,
212
HeadlessWorkspace: util.Duration(1 * time.Hour),
213
Initialization: util.Duration(30 * time.Minute),
214
RegularWorkspace: util.Duration(30 * time.Minute),
215
MaxLifetime: ctx.Config.Workspace.MaxLifetime,
216
TotalStartup: util.Duration(1 * time.Hour),
217
ContentFinalization: util.Duration(1 * time.Hour),
218
Stopping: util.Duration(1 * time.Hour),
219
Interrupted: util.Duration(5 * time.Minute),
220
},
221
//EventTraceLog: "", // todo(sje): make conditional based on config
222
ReconnectionInterval: util.Duration(30 * time.Second),
223
RegistryFacadeHost: fmt.Sprintf("reg.%s:%d", ctx.Config.Domain, common.RegistryFacadeServicePort),
224
WorkspaceMaxConcurrentReconciles: 25,
225
TimeoutMaxConcurrentReconciles: 15,
226
},
227
Content: struct {
228
Storage storageconfig.StorageConfig `json:"storage"`
229
}{Storage: common.StorageConfig(ctx)},
230
RPCServer: struct {
231
Addr string `json:"addr"`
232
TLS struct {
233
CA string `json:"ca"`
234
Certificate string `json:"crt"`
235
PrivateKey string `json:"key"`
236
} `json:"tls"`
237
RateLimits map[string]grpc.RateLimit `json:"ratelimits"`
238
}{
239
Addr: fmt.Sprintf(":%d", RPCPort),
240
TLS: struct {
241
CA string `json:"ca"`
242
Certificate string `json:"crt"`
243
PrivateKey string `json:"key"`
244
}{
245
CA: "/certs/ca.crt",
246
Certificate: "/certs/tls.crt",
247
PrivateKey: "/certs/tls.key",
248
},
249
RateLimits: rateLimits,
250
},
251
ImageBuilderProxy: struct {
252
TargetAddr string "json:\"targetAddr\""
253
TLS struct {
254
CA string `json:"ca"`
255
Certificate string `json:"crt"`
256
PrivateKey string `json:"key"`
257
} `json:"tls"`
258
}{
259
TargetAddr: fmt.Sprintf("%s.%s.svc.cluster.local:%d", common.ImageBuilderComponent, ctx.Namespace, common.ImageBuilderRPCPort),
260
TLS: imageBuilderTLS,
261
},
262
PProf: struct {
263
Addr string `json:"addr"`
264
}{Addr: common.LocalhostPprofAddr()},
265
Prometheus: struct {
266
Addr string `json:"addr"`
267
}{Addr: common.LocalhostPrometheusAddr()},
268
Health: struct {
269
Addr string `json:"addr"`
270
}{Addr: fmt.Sprintf(":%d", HealthPort)},
271
}
272
273
if ctx.Config.CustomCACert != nil {
274
wsmcfg.Manager.EnableCustomSSLCertificate = true
275
}
276
277
if ctx.Config.SSHGatewayCAKey != nil {
278
wsmcfg.Manager.SSHGatewayCAPublicKeyFile = "/mnt/ca-key/ca.pem"
279
}
280
281
fc, err := common.ToJSONString(wsmcfg)
282
if err != nil {
283
return nil, fmt.Errorf("failed to marshal ws-manager config: %w", err)
284
}
285
286
res := []runtime.Object{
287
&corev1.ConfigMap{
288
TypeMeta: common.TypeMetaConfigmap,
289
ObjectMeta: metav1.ObjectMeta{
290
Name: Component,
291
Namespace: ctx.Namespace,
292
Labels: common.CustomizeLabel(ctx, Component, common.TypeMetaConfigmap),
293
Annotations: common.CustomizeAnnotation(ctx, Component, common.TypeMetaConfigmap),
294
},
295
Data: map[string]string{
296
"config.json": string(fc),
297
},
298
},
299
&corev1.ConfigMap{
300
TypeMeta: common.TypeMetaConfigmap,
301
ObjectMeta: metav1.ObjectMeta{
302
Name: WorkspaceTemplateConfigMap,
303
Namespace: ctx.Namespace,
304
Labels: common.DefaultLabels(Component),
305
},
306
Data: tpls,
307
},
308
}
309
return res, nil
310
}
311
312
func buildWorkspaceTemplates(ctx *common.RenderContext, cfgTpls *configv1.WorkspaceTemplates, className string) (config.WorkspacePodTemplateConfiguration, map[string]string, error) {
313
var (
314
cfg config.WorkspacePodTemplateConfiguration
315
tpls = make(map[string]string)
316
)
317
if cfgTpls == nil {
318
cfgTpls = new(configv1.WorkspaceTemplates)
319
}
320
321
ops := []struct {
322
Name string
323
Path *string
324
Tpl *corev1.Pod
325
}{
326
{Name: "default", Path: &cfg.DefaultPath, Tpl: cfgTpls.Default},
327
{Name: "imagebuild", Path: &cfg.ImagebuildPath, Tpl: cfgTpls.ImageBuild},
328
{Name: "prebuild", Path: &cfg.PrebuildPath, Tpl: cfgTpls.Prebuild},
329
{Name: "regular", Path: &cfg.RegularPath, Tpl: cfgTpls.Regular},
330
}
331
for _, op := range ops {
332
if op.Tpl == nil {
333
continue
334
}
335
fc, err := yaml.Marshal(op.Tpl)
336
if err != nil {
337
return cfg, nil, fmt.Errorf("unable to marshal %s workspace template: %w", op.Name, err)
338
}
339
fn := op.Name + ".yaml"
340
if className != "" {
341
fn = className + "-" + fn
342
}
343
*op.Path = filepath.Join(WorkspaceTemplatePath, fn)
344
tpls[fn] = string(fc)
345
}
346
347
return cfg, tpls, nil
348
}
349
350