Path: blob/main/install/installer/pkg/components/ws-manager-mk2/configmap.go
2501 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 wsmanagermk256import (7"fmt"8"path/filepath"9"time"1011wsdaemon "github.com/gitpod-io/gitpod/installer/pkg/components/ws-daemon"12"sigs.k8s.io/yaml"1314"github.com/gitpod-io/gitpod/common-go/grpc"15"github.com/gitpod-io/gitpod/common-go/util"16storageconfig "github.com/gitpod-io/gitpod/content-service/api/config"17"github.com/gitpod-io/gitpod/installer/pkg/common"18configv1 "github.com/gitpod-io/gitpod/installer/pkg/config/v1"19"github.com/gitpod-io/gitpod/installer/pkg/config/v1/experimental"20"github.com/gitpod-io/gitpod/ws-manager/api/config"2122corev1 "k8s.io/api/core/v1"23metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"24"k8s.io/apimachinery/pkg/runtime"25)2627func configmap(ctx *common.RenderContext) ([]runtime.Object, error) {28cfgTpls := ctx.Config.Workspace.Templates29if cfgTpls == nil {30cfgTpls = &configv1.WorkspaceTemplates{}31}32templatesCfg, tpls, err := buildWorkspaceTemplates(ctx, cfgTpls, "")33if err != nil {34return nil, err35}3637quantityString := func(idx corev1.ResourceList, key corev1.ResourceName) string {38q, ok := idx[key]39if !ok {40return ""41}42return (&q).String()43}4445timeoutAfterClose := util.Duration(2 * time.Minute)46if ctx.Config.Workspace.TimeoutAfterClose != nil {47timeoutAfterClose = *ctx.Config.Workspace.TimeoutAfterClose48}4950classes := map[string]*config.WorkspaceClass{51config.DefaultWorkspaceClass: {52Name: config.DefaultWorkspaceClass,53Container: config.ContainerConfiguration{54Requests: &config.ResourceRequestConfiguration{55CPU: quantityString(ctx.Config.Workspace.Resources.Requests, corev1.ResourceCPU),56Memory: quantityString(ctx.Config.Workspace.Resources.Requests, corev1.ResourceMemory),57EphemeralStorage: quantityString(ctx.Config.Workspace.Resources.Requests, corev1.ResourceEphemeralStorage),58},59Limits: &config.ResourceLimitConfiguration{60CPU: &config.CpuResourceLimit{61MinLimit: quantityString(ctx.Config.Workspace.Resources.Limits, corev1.ResourceCPU),62BurstLimit: quantityString(ctx.Config.Workspace.Resources.Limits, corev1.ResourceCPU),63},64Memory: quantityString(ctx.Config.Workspace.Resources.Limits, corev1.ResourceMemory),65EphemeralStorage: quantityString(ctx.Config.Workspace.Resources.Limits, corev1.ResourceEphemeralStorage),66Storage: quantityString(ctx.Config.Workspace.Resources.Limits, corev1.ResourceStorage),67},68},69Templates: templatesCfg,70},71}72var preferredWorkspaceClass string7374installationShortNameSuffix := ""75if ctx.Config.Metadata.InstallationShortname != "" && ctx.Config.Metadata.InstallationShortname != configv1.InstallationShortNameOldDefault {76installationShortNameSuffix = "-" + ctx.Config.Metadata.InstallationShortname77}7879var schedulerName string80gitpodHostURL := "https://" + ctx.Config.Domain81workspaceClusterHost := fmt.Sprintf("ws%s.%s", installationShortNameSuffix, ctx.Config.Domain)82workspaceURLTemplate := fmt.Sprintf("https://{{ .Prefix }}.ws%s.%s", installationShortNameSuffix, ctx.Config.Domain)83workspacePortURLTemplate := fmt.Sprintf("https://{{ .WorkspacePort }}-{{ .Prefix }}.ws%s.%s", installationShortNameSuffix, ctx.Config.Domain)84hostWorkingArea := wsdaemon.HostWorkingAreaMk28586rateLimits := map[string]grpc.RateLimit{}8788err = ctx.WithExperimental(func(ucfg *experimental.Config) error {89if ucfg.Workspace == nil {90return nil91}92for k, c := range ucfg.Workspace.WorkspaceClasses {93tplsCfg, ctpls, err := buildWorkspaceTemplates(ctx, &configv1.WorkspaceTemplates{94Default: c.Templates.Default,95Prebuild: c.Templates.Prebuild,96ImageBuild: c.Templates.ImageBuild,97Regular: c.Templates.Regular,98}, k)99if err != nil {100return err101}102classes[k] = &config.WorkspaceClass{103Name: c.Name,104Description: c.Description,105Container: config.ContainerConfiguration{106Requests: &config.ResourceRequestConfiguration{107CPU: quantityString(c.Resources.Requests, corev1.ResourceCPU),108Memory: quantityString(c.Resources.Requests, corev1.ResourceMemory),109EphemeralStorage: quantityString(c.Resources.Requests, corev1.ResourceEphemeralStorage),110},111Limits: &config.ResourceLimitConfiguration{112CPU: &config.CpuResourceLimit{113MinLimit: c.Resources.Limits.Cpu.MinLimit,114BurstLimit: c.Resources.Limits.Cpu.BurstLimit,115},116Memory: c.Resources.Limits.Memory,117EphemeralStorage: c.Resources.Limits.EphemeralStorage,118Storage: c.Resources.Limits.Storage,119},120},121Templates: tplsCfg,122}123for tmpl_n, tmpl_v := range ctpls {124if _, ok := tpls[tmpl_n]; ok {125return fmt.Errorf("duplicate workspace template %q in workspace class %q", tmpl_n, k)126}127tpls[tmpl_n] = tmpl_v128}129}130preferredWorkspaceClass = ucfg.Workspace.PreferredWorkspaceClass131if preferredWorkspaceClass == "" {132// if no preferred workspace class is set, use a random one (maps have no order, there is no "first")133for _, k := range ucfg.Workspace.WorkspaceClasses {134preferredWorkspaceClass = k.Name135break136}137}138139schedulerName = ucfg.Workspace.SchedulerName140if ucfg.Workspace.HostURL != "" {141gitpodHostURL = ucfg.Workspace.HostURL142}143if ucfg.Workspace.WorkspaceClusterHost != "" {144workspaceClusterHost = ucfg.Workspace.WorkspaceClusterHost145}146if ucfg.Workspace.WorkspaceURLTemplate != "" {147workspaceURLTemplate = ucfg.Workspace.WorkspaceURLTemplate148}149if ucfg.Workspace.WorkspacePortURLTemplate != "" {150workspacePortURLTemplate = ucfg.Workspace.WorkspacePortURLTemplate151}152rateLimits = ucfg.Workspace.WSManagerRateLimits153154return nil155})156if err != nil {157return nil, err158}159160var imageBuilderTLS struct {161CA string `json:"ca"`162Certificate string `json:"crt"`163PrivateKey string `json:"key"`164}165if ctx.Config.Kind == configv1.InstallationWorkspace {166// Image builder TLS is only enabled in workspace clusters. This check167// can be removed once image-builder-mk3 has been removed from application clusters168// (https://github.com/gitpod-io/gitpod/issues/7845).169imageBuilderTLS = struct {170CA string `json:"ca"`171Certificate string `json:"crt"`172PrivateKey string `json:"key"`173}{174CA: "/image-builder-mk3-tls-certs/ca.crt",175Certificate: "/image-builder-mk3-tls-certs/tls.crt",176PrivateKey: "/image-builder-mk3-tls-certs/tls.key",177}178}179180wsmcfg := config.ServiceConfiguration{181Manager: config.Configuration{182Namespace: ctx.Namespace,183SecretsNamespace: common.WorkspaceSecretsNamespace,184SchedulerName: schedulerName,185SeccompProfile: fmt.Sprintf("workspace_default_%s.json", ctx.VersionManifest.Version),186WorkspaceDaemon: config.WorkspaceDaemonConfiguration{187Port: 8080,188TLS: struct {189Authority string `json:"ca"`190Certificate string `json:"crt"`191PrivateKey string `json:"key"`192}{193Authority: "/ws-daemon-tls-certs/ca.crt",194Certificate: "/ws-daemon-tls-certs/tls.crt",195PrivateKey: "/ws-daemon-tls-certs/tls.key",196},197},198WorkspaceClasses: classes,199PreferredWorkspaceClass: preferredWorkspaceClass,200HeartbeatInterval: util.Duration(30 * time.Second),201GitpodHostURL: gitpodHostURL,202WorkspaceClusterHost: workspaceClusterHost,203InitProbe: config.InitProbeConfiguration{204Timeout: (1 * time.Second).String(),205},206WorkspaceURLTemplate: workspaceURLTemplate,207WorkspacePortURLTemplate: workspacePortURLTemplate,208WorkspaceHostPath: hostWorkingArea,209Timeouts: config.WorkspaceTimeoutConfiguration{210AfterClose: timeoutAfterClose,211HeadlessWorkspace: util.Duration(1 * time.Hour),212Initialization: util.Duration(30 * time.Minute),213RegularWorkspace: util.Duration(30 * time.Minute),214MaxLifetime: ctx.Config.Workspace.MaxLifetime,215TotalStartup: util.Duration(1 * time.Hour),216ContentFinalization: util.Duration(1 * time.Hour),217Stopping: util.Duration(1 * time.Hour),218Interrupted: util.Duration(5 * time.Minute),219},220//EventTraceLog: "", // todo(sje): make conditional based on config221ReconnectionInterval: util.Duration(30 * time.Second),222RegistryFacadeHost: fmt.Sprintf("reg.%s:%d", ctx.Config.Domain, common.RegistryFacadeServicePort),223WorkspaceMaxConcurrentReconciles: 25,224TimeoutMaxConcurrentReconciles: 15,225},226Content: struct {227Storage storageconfig.StorageConfig `json:"storage"`228}{Storage: common.StorageConfig(ctx)},229RPCServer: struct {230Addr string `json:"addr"`231TLS struct {232CA string `json:"ca"`233Certificate string `json:"crt"`234PrivateKey string `json:"key"`235} `json:"tls"`236RateLimits map[string]grpc.RateLimit `json:"ratelimits"`237}{238Addr: fmt.Sprintf(":%d", RPCPort),239TLS: struct {240CA string `json:"ca"`241Certificate string `json:"crt"`242PrivateKey string `json:"key"`243}{244CA: "/certs/ca.crt",245Certificate: "/certs/tls.crt",246PrivateKey: "/certs/tls.key",247},248RateLimits: rateLimits,249},250ImageBuilderProxy: struct {251TargetAddr string "json:\"targetAddr\""252TLS struct {253CA string `json:"ca"`254Certificate string `json:"crt"`255PrivateKey string `json:"key"`256} `json:"tls"`257}{258TargetAddr: fmt.Sprintf("%s.%s.svc.cluster.local:%d", common.ImageBuilderComponent, ctx.Namespace, common.ImageBuilderRPCPort),259TLS: imageBuilderTLS,260},261PProf: struct {262Addr string `json:"addr"`263}{Addr: common.LocalhostPprofAddr()},264Prometheus: struct {265Addr string `json:"addr"`266}{Addr: common.LocalhostPrometheusAddr()},267Health: struct {268Addr string `json:"addr"`269}{Addr: fmt.Sprintf(":%d", HealthPort)},270}271272if ctx.Config.CustomCACert != nil {273wsmcfg.Manager.EnableCustomSSLCertificate = true274}275276if ctx.Config.SSHGatewayCAKey != nil {277wsmcfg.Manager.SSHGatewayCAPublicKeyFile = "/mnt/ca-key/ca.pem"278}279280fc, err := common.ToJSONString(wsmcfg)281if err != nil {282return nil, fmt.Errorf("failed to marshal ws-manager config: %w", err)283}284285res := []runtime.Object{286&corev1.ConfigMap{287TypeMeta: common.TypeMetaConfigmap,288ObjectMeta: metav1.ObjectMeta{289Name: Component,290Namespace: ctx.Namespace,291Labels: common.CustomizeLabel(ctx, Component, common.TypeMetaConfigmap),292Annotations: common.CustomizeAnnotation(ctx, Component, common.TypeMetaConfigmap),293},294Data: map[string]string{295"config.json": string(fc),296},297},298&corev1.ConfigMap{299TypeMeta: common.TypeMetaConfigmap,300ObjectMeta: metav1.ObjectMeta{301Name: WorkspaceTemplateConfigMap,302Namespace: ctx.Namespace,303Labels: common.DefaultLabels(Component),304},305Data: tpls,306},307}308return res, nil309}310311func buildWorkspaceTemplates(ctx *common.RenderContext, cfgTpls *configv1.WorkspaceTemplates, className string) (config.WorkspacePodTemplateConfiguration, map[string]string, error) {312var (313cfg config.WorkspacePodTemplateConfiguration314tpls = make(map[string]string)315)316if cfgTpls == nil {317cfgTpls = new(configv1.WorkspaceTemplates)318}319320ops := []struct {321Name string322Path *string323Tpl *corev1.Pod324}{325{Name: "default", Path: &cfg.DefaultPath, Tpl: cfgTpls.Default},326{Name: "imagebuild", Path: &cfg.ImagebuildPath, Tpl: cfgTpls.ImageBuild},327{Name: "prebuild", Path: &cfg.PrebuildPath, Tpl: cfgTpls.Prebuild},328{Name: "regular", Path: &cfg.RegularPath, Tpl: cfgTpls.Regular},329}330for _, op := range ops {331if op.Tpl == nil {332continue333}334fc, err := yaml.Marshal(op.Tpl)335if err != nil {336return cfg, nil, fmt.Errorf("unable to marshal %s workspace template: %w", op.Name, err)337}338fn := op.Name + ".yaml"339if className != "" {340fn = className + "-" + fn341}342*op.Path = filepath.Join(WorkspaceTemplatePath, fn)343tpls[fn] = string(fc)344}345346return cfg, tpls, nil347}348349350