mirror of
https://github.com/gitpod-io/gitpod.git
synced 2025-12-08 17:36:30 +00:00
If dryRun enables on StartWorkspace/StopWorkspace/TakeSnapshot, return gRPC FailedPrecondition status code and error message `under maintenance mode`. Signed-off-by: JenTing Hsiao <hsiaoairplane@gmail.com>
340 lines
12 KiB
Go
340 lines
12 KiB
Go
// 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 wsmanager
|
|
|
|
import (
|
|
"fmt"
|
|
"path/filepath"
|
|
"time"
|
|
|
|
wsdaemon "github.com/gitpod-io/gitpod/installer/pkg/components/ws-daemon"
|
|
"sigs.k8s.io/yaml"
|
|
|
|
"github.com/gitpod-io/gitpod/common-go/grpc"
|
|
"github.com/gitpod-io/gitpod/common-go/util"
|
|
storageconfig "github.com/gitpod-io/gitpod/content-service/api/config"
|
|
"github.com/gitpod-io/gitpod/installer/pkg/common"
|
|
configv1 "github.com/gitpod-io/gitpod/installer/pkg/config/v1"
|
|
"github.com/gitpod-io/gitpod/installer/pkg/config/v1/experimental"
|
|
"github.com/gitpod-io/gitpod/ws-manager/api/config"
|
|
|
|
corev1 "k8s.io/api/core/v1"
|
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
"k8s.io/apimachinery/pkg/runtime"
|
|
)
|
|
|
|
func configmap(ctx *common.RenderContext) ([]runtime.Object, error) {
|
|
cfgTpls := ctx.Config.Workspace.Templates
|
|
if cfgTpls == nil {
|
|
cfgTpls = &configv1.WorkspaceTemplates{}
|
|
}
|
|
templatesCfg, tpls, err := buildWorkspaceTemplates(ctx, cfgTpls, "")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
quantityString := func(idx corev1.ResourceList, key corev1.ResourceName) string {
|
|
q, ok := idx[key]
|
|
if !ok {
|
|
return ""
|
|
}
|
|
return (&q).String()
|
|
}
|
|
|
|
timeoutAfterClose := util.Duration(2 * time.Minute)
|
|
if ctx.Config.Workspace.TimeoutAfterClose != nil {
|
|
timeoutAfterClose = *ctx.Config.Workspace.TimeoutAfterClose
|
|
}
|
|
|
|
var customCASecret string
|
|
if ctx.Config.CustomCACert != nil {
|
|
customCASecret = ctx.Config.CustomCACert.Name
|
|
}
|
|
|
|
classes := map[string]*config.WorkspaceClass{
|
|
config.DefaultWorkspaceClass: {
|
|
Name: config.DefaultWorkspaceClass,
|
|
Container: config.ContainerConfiguration{
|
|
Requests: &config.ResourceRequestConfiguration{
|
|
CPU: quantityString(ctx.Config.Workspace.Resources.Requests, corev1.ResourceCPU),
|
|
Memory: quantityString(ctx.Config.Workspace.Resources.Requests, corev1.ResourceMemory),
|
|
EphemeralStorage: quantityString(ctx.Config.Workspace.Resources.Requests, corev1.ResourceEphemeralStorage),
|
|
},
|
|
Limits: &config.ResourceLimitConfiguration{
|
|
CPU: &config.CpuResourceLimit{
|
|
MinLimit: quantityString(ctx.Config.Workspace.Resources.Limits, corev1.ResourceCPU),
|
|
BurstLimit: quantityString(ctx.Config.Workspace.Resources.Limits, corev1.ResourceCPU),
|
|
},
|
|
Memory: quantityString(ctx.Config.Workspace.Resources.Limits, corev1.ResourceMemory),
|
|
EphemeralStorage: quantityString(ctx.Config.Workspace.Resources.Limits, corev1.ResourceEphemeralStorage),
|
|
Storage: quantityString(ctx.Config.Workspace.Resources.Limits, corev1.ResourceStorage),
|
|
},
|
|
},
|
|
Templates: templatesCfg,
|
|
PrebuildPVC: config.PVCConfiguration{
|
|
Size: ctx.Config.Workspace.PrebuildPVC.Size,
|
|
StorageClass: ctx.Config.Workspace.PrebuildPVC.StorageClass,
|
|
SnapshotClass: ctx.Config.Workspace.PrebuildPVC.SnapshotClass,
|
|
},
|
|
PVC: config.PVCConfiguration{
|
|
Size: ctx.Config.Workspace.PVC.Size,
|
|
StorageClass: ctx.Config.Workspace.PVC.StorageClass,
|
|
SnapshotClass: ctx.Config.Workspace.PVC.SnapshotClass,
|
|
},
|
|
},
|
|
}
|
|
|
|
installationShortNameSuffix := ""
|
|
if ctx.Config.Metadata.InstallationShortname != "" && ctx.Config.Metadata.InstallationShortname != configv1.InstallationShortNameOldDefault {
|
|
installationShortNameSuffix = "-" + ctx.Config.Metadata.InstallationShortname
|
|
}
|
|
|
|
var schedulerName string
|
|
gitpodHostURL := "https://" + ctx.Config.Domain
|
|
workspaceClusterHost := fmt.Sprintf("ws%s.%s", installationShortNameSuffix, ctx.Config.Domain)
|
|
workspaceURLTemplate := fmt.Sprintf("https://{{ .Prefix }}.ws%s.%s", installationShortNameSuffix, ctx.Config.Domain)
|
|
workspacePortURLTemplate := fmt.Sprintf("https://{{ .WorkspacePort }}-{{ .Prefix }}.ws%s.%s", installationShortNameSuffix, ctx.Config.Domain)
|
|
|
|
rateLimits := map[string]grpc.RateLimit{}
|
|
|
|
err = ctx.WithExperimental(func(ucfg *experimental.Config) error {
|
|
if ucfg.Workspace == nil {
|
|
return nil
|
|
}
|
|
for k, c := range ucfg.Workspace.WorkspaceClasses {
|
|
tplsCfg, ctpls, err := buildWorkspaceTemplates(ctx, &configv1.WorkspaceTemplates{
|
|
Default: c.Templates.Default,
|
|
Prebuild: c.Templates.Prebuild,
|
|
ImageBuild: c.Templates.ImageBuild,
|
|
Regular: c.Templates.Regular,
|
|
}, k)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
classes[k] = &config.WorkspaceClass{
|
|
Name: c.Name,
|
|
Container: config.ContainerConfiguration{
|
|
Requests: &config.ResourceRequestConfiguration{
|
|
CPU: quantityString(c.Resources.Requests, corev1.ResourceCPU),
|
|
Memory: quantityString(c.Resources.Requests, corev1.ResourceMemory),
|
|
EphemeralStorage: quantityString(c.Resources.Requests, corev1.ResourceEphemeralStorage),
|
|
},
|
|
Limits: &config.ResourceLimitConfiguration{
|
|
CPU: &config.CpuResourceLimit{
|
|
MinLimit: c.Resources.Limits.Cpu.MinLimit,
|
|
BurstLimit: c.Resources.Limits.Cpu.BurstLimit,
|
|
},
|
|
Memory: c.Resources.Limits.Memory,
|
|
EphemeralStorage: c.Resources.Limits.EphemeralStorage,
|
|
Storage: c.Resources.Limits.Storage,
|
|
},
|
|
},
|
|
Templates: tplsCfg,
|
|
PrebuildPVC: config.PVCConfiguration(c.PrebuildPVC),
|
|
PVC: config.PVCConfiguration(c.PVC),
|
|
}
|
|
for tmpl_n, tmpl_v := range ctpls {
|
|
if _, ok := tpls[tmpl_n]; ok {
|
|
return fmt.Errorf("duplicate workspace template %q in workspace class %q", tmpl_n, k)
|
|
}
|
|
tpls[tmpl_n] = tmpl_v
|
|
}
|
|
}
|
|
schedulerName = ucfg.Workspace.SchedulerName
|
|
if ucfg.Workspace.HostURL != "" {
|
|
gitpodHostURL = ucfg.Workspace.HostURL
|
|
}
|
|
if ucfg.Workspace.WorkspaceClusterHost != "" {
|
|
workspaceClusterHost = ucfg.Workspace.WorkspaceClusterHost
|
|
}
|
|
if ucfg.Workspace.WorkspaceURLTemplate != "" {
|
|
workspaceURLTemplate = ucfg.Workspace.WorkspaceURLTemplate
|
|
}
|
|
if ucfg.Workspace.WorkspacePortURLTemplate != "" {
|
|
workspacePortURLTemplate = ucfg.Workspace.WorkspacePortURLTemplate
|
|
}
|
|
rateLimits = ucfg.Workspace.WSManagerRateLimits
|
|
|
|
return nil
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var imageBuilderTLS struct {
|
|
CA string `json:"ca"`
|
|
Certificate string `json:"crt"`
|
|
PrivateKey string `json:"key"`
|
|
}
|
|
if ctx.Config.Kind == configv1.InstallationWorkspace {
|
|
// Image builder TLS is only enabled in workspace clusters. This check
|
|
// can be removed once image-builder-mk3 has been removed from application clusters
|
|
// (https://github.com/gitpod-io/gitpod/issues/7845).
|
|
imageBuilderTLS = struct {
|
|
CA string `json:"ca"`
|
|
Certificate string `json:"crt"`
|
|
PrivateKey string `json:"key"`
|
|
}{
|
|
CA: "/image-builder-mk3-tls-certs/ca.crt",
|
|
Certificate: "/image-builder-mk3-tls-certs/tls.crt",
|
|
PrivateKey: "/image-builder-mk3-tls-certs/tls.key",
|
|
}
|
|
}
|
|
|
|
wsmcfg := config.ServiceConfiguration{
|
|
Manager: config.Configuration{
|
|
Namespace: ctx.Namespace,
|
|
SchedulerName: schedulerName,
|
|
SeccompProfile: fmt.Sprintf("workspace_default_%s.json", ctx.VersionManifest.Version),
|
|
WorkspaceDaemon: config.WorkspaceDaemonConfiguration{
|
|
Port: 8080,
|
|
TLS: struct {
|
|
Authority string `json:"ca"`
|
|
Certificate string `json:"crt"`
|
|
PrivateKey string `json:"key"`
|
|
}{
|
|
Authority: "/ws-daemon-tls-certs/ca.crt",
|
|
Certificate: "/ws-daemon-tls-certs/tls.crt",
|
|
PrivateKey: "/ws-daemon-tls-certs/tls.key",
|
|
},
|
|
},
|
|
WorkspaceClasses: classes,
|
|
HeartbeatInterval: util.Duration(30 * time.Second),
|
|
GitpodHostURL: gitpodHostURL,
|
|
WorkspaceClusterHost: workspaceClusterHost,
|
|
InitProbe: config.InitProbeConfiguration{
|
|
Timeout: (1 * time.Second).String(),
|
|
},
|
|
WorkspaceURLTemplate: workspaceURLTemplate,
|
|
WorkspacePortURLTemplate: workspacePortURLTemplate,
|
|
WorkspaceHostPath: wsdaemon.HostWorkingArea,
|
|
Timeouts: config.WorkspaceTimeoutConfiguration{
|
|
AfterClose: timeoutAfterClose,
|
|
HeadlessWorkspace: util.Duration(1 * time.Hour),
|
|
Initialization: util.Duration(30 * time.Minute),
|
|
RegularWorkspace: util.Duration(30 * time.Minute),
|
|
MaxLifetime: ctx.Config.Workspace.MaxLifetime,
|
|
TotalStartup: util.Duration(1 * time.Hour),
|
|
ContentFinalization: util.Duration(1 * time.Hour),
|
|
Stopping: util.Duration(1 * time.Hour),
|
|
Interrupted: util.Duration(5 * time.Minute),
|
|
},
|
|
//EventTraceLog: "", // todo(sje): make conditional based on config
|
|
ReconnectionInterval: util.Duration(30 * time.Second),
|
|
RegistryFacadeHost: fmt.Sprintf("reg.%s:%d", ctx.Config.Domain, common.RegistryFacadeServicePort),
|
|
WorkspaceCACertSecret: customCASecret,
|
|
},
|
|
Content: struct {
|
|
Storage storageconfig.StorageConfig `json:"storage"`
|
|
}{Storage: common.StorageConfig(ctx)},
|
|
RPCServer: struct {
|
|
Addr string `json:"addr"`
|
|
TLS struct {
|
|
CA string `json:"ca"`
|
|
Certificate string `json:"crt"`
|
|
PrivateKey string `json:"key"`
|
|
} `json:"tls"`
|
|
RateLimits map[string]grpc.RateLimit `json:"ratelimits"`
|
|
}{
|
|
Addr: fmt.Sprintf(":%d", RPCPort),
|
|
TLS: struct {
|
|
CA string `json:"ca"`
|
|
Certificate string `json:"crt"`
|
|
PrivateKey string `json:"key"`
|
|
}{
|
|
CA: "/certs/ca.crt",
|
|
Certificate: "/certs/tls.crt",
|
|
PrivateKey: "/certs/tls.key",
|
|
},
|
|
RateLimits: rateLimits,
|
|
},
|
|
ImageBuilderProxy: struct {
|
|
TargetAddr string "json:\"targetAddr\""
|
|
TLS struct {
|
|
CA string `json:"ca"`
|
|
Certificate string `json:"crt"`
|
|
PrivateKey string `json:"key"`
|
|
} `json:"tls"`
|
|
}{
|
|
TargetAddr: fmt.Sprintf("%s.%s.svc.cluster.local:%d", common.ImageBuilderComponent, ctx.Namespace, common.ImageBuilderRPCPort),
|
|
TLS: imageBuilderTLS,
|
|
},
|
|
PProf: struct {
|
|
Addr string `json:"addr"`
|
|
}{Addr: common.LocalhostPprofAddr()},
|
|
Prometheus: struct {
|
|
Addr string `json:"addr"`
|
|
}{Addr: common.LocalhostPrometheusAddr()},
|
|
}
|
|
|
|
fc, err := common.ToJSONString(wsmcfg)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to marshal ws-manager config: %w", err)
|
|
}
|
|
|
|
res := []runtime.Object{
|
|
&corev1.ConfigMap{
|
|
TypeMeta: common.TypeMetaConfigmap,
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: Component,
|
|
Namespace: ctx.Namespace,
|
|
Labels: common.CustomizeLabel(ctx, Component, common.TypeMetaConfigmap),
|
|
Annotations: common.CustomizeAnnotation(ctx, Component, common.TypeMetaConfigmap),
|
|
},
|
|
Data: map[string]string{
|
|
"config.json": string(fc),
|
|
},
|
|
},
|
|
&corev1.ConfigMap{
|
|
TypeMeta: common.TypeMetaConfigmap,
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: WorkspaceTemplateConfigMap,
|
|
Namespace: ctx.Namespace,
|
|
Labels: common.DefaultLabels(Component),
|
|
},
|
|
Data: tpls,
|
|
},
|
|
}
|
|
return res, nil
|
|
}
|
|
|
|
func buildWorkspaceTemplates(ctx *common.RenderContext, cfgTpls *configv1.WorkspaceTemplates, className string) (config.WorkspacePodTemplateConfiguration, map[string]string, error) {
|
|
var (
|
|
cfg config.WorkspacePodTemplateConfiguration
|
|
tpls = make(map[string]string)
|
|
)
|
|
if cfgTpls == nil {
|
|
cfgTpls = new(configv1.WorkspaceTemplates)
|
|
}
|
|
|
|
ops := []struct {
|
|
Name string
|
|
Path *string
|
|
Tpl *corev1.Pod
|
|
}{
|
|
{Name: "default", Path: &cfg.DefaultPath, Tpl: cfgTpls.Default},
|
|
{Name: "imagebuild", Path: &cfg.ImagebuildPath, Tpl: cfgTpls.ImageBuild},
|
|
{Name: "prebuild", Path: &cfg.PrebuildPath, Tpl: cfgTpls.Prebuild},
|
|
{Name: "regular", Path: &cfg.RegularPath, Tpl: cfgTpls.Regular},
|
|
}
|
|
for _, op := range ops {
|
|
if op.Tpl == nil {
|
|
continue
|
|
}
|
|
fc, err := yaml.Marshal(op.Tpl)
|
|
if err != nil {
|
|
return cfg, nil, fmt.Errorf("unable to marshal %s workspace template: %w", op.Name, err)
|
|
}
|
|
fn := op.Name + ".yaml"
|
|
if className != "" {
|
|
fn = className + "-" + fn
|
|
}
|
|
*op.Path = filepath.Join(WorkspaceTemplatePath, fn)
|
|
tpls[fn] = string(fc)
|
|
}
|
|
|
|
return cfg, tpls, nil
|
|
}
|