mirror of
https://github.com/gitpod-io/gitpod.git
synced 2025-12-08 17:36:30 +00:00
* Uniformly use proxy to get the configuration of configcat * Add configcat enabled in workspace info api * enabled configcat in ide-service
821 lines
24 KiB
Go
821 lines
24 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 common
|
|
|
|
import (
|
|
"crypto/sha256"
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
"math/rand"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"github.com/gitpod-io/gitpod/common-go/baseserver"
|
|
config "github.com/gitpod-io/gitpod/installer/pkg/config/v1"
|
|
"github.com/gitpod-io/gitpod/installer/pkg/config/v1/experimental"
|
|
|
|
appsv1 "k8s.io/api/apps/v1"
|
|
corev1 "k8s.io/api/core/v1"
|
|
networkingv1 "k8s.io/api/networking/v1"
|
|
"k8s.io/apimachinery/pkg/api/resource"
|
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
"k8s.io/apimachinery/pkg/runtime"
|
|
"k8s.io/apimachinery/pkg/util/intstr"
|
|
"k8s.io/utils/pointer"
|
|
"sigs.k8s.io/yaml"
|
|
)
|
|
|
|
// getProxyServerEnvvar get the proxy server envvars in both upper and lowercase form for maximum compatiblity
|
|
func getProxyServerEnvvar(cfg *config.Config, envvarName string, key string) []corev1.EnvVar {
|
|
env := corev1.EnvVar{
|
|
Name: strings.ToUpper(envvarName),
|
|
ValueFrom: &corev1.EnvVarSource{
|
|
SecretKeyRef: &corev1.SecretKeySelector{
|
|
LocalObjectReference: corev1.LocalObjectReference{
|
|
Name: cfg.HTTPProxy.Name,
|
|
},
|
|
Key: key,
|
|
Optional: pointer.Bool(true),
|
|
},
|
|
},
|
|
}
|
|
|
|
return []corev1.EnvVar{
|
|
env,
|
|
func() corev1.EnvVar {
|
|
envLower := env.DeepCopy()
|
|
envLower.Name = strings.ToLower(envvarName)
|
|
|
|
return *envLower
|
|
}(),
|
|
}
|
|
}
|
|
|
|
func DefaultLabels(component string) map[string]string {
|
|
return map[string]string{
|
|
"app": "gitpod",
|
|
"component": component,
|
|
}
|
|
}
|
|
|
|
func MergeEnv(envs ...[]corev1.EnvVar) (res []corev1.EnvVar) {
|
|
for _, e := range envs {
|
|
res = append(res, e...)
|
|
}
|
|
return
|
|
}
|
|
|
|
func ProxyEnv(cfg *config.Config) []corev1.EnvVar {
|
|
if cfg.HTTPProxy == nil {
|
|
return []corev1.EnvVar{}
|
|
}
|
|
|
|
// The hard-coded values are the gRPC service names and the licence server
|
|
noProxyValue := "ws-manager,wsdaemon,$(CUSTOM_NO_PROXY)"
|
|
|
|
return MergeEnv(
|
|
getProxyServerEnvvar(cfg, "HTTP_PROXY", "httpProxy"),
|
|
getProxyServerEnvvar(cfg, "HTTPS_PROXY", "httpsProxy"),
|
|
getProxyServerEnvvar(cfg, "CUSTOM_NO_PROXY", "noProxy"),
|
|
[]corev1.EnvVar{
|
|
// This must come after the CUSTOM_NO_PROXY definition
|
|
{Name: "NO_PROXY", Value: noProxyValue},
|
|
{Name: "no_proxy", Value: noProxyValue},
|
|
},
|
|
)
|
|
}
|
|
|
|
func DefaultEnv(cfg *config.Config) []corev1.EnvVar {
|
|
logLevel := "info"
|
|
if cfg.Observability.LogLevel != "" {
|
|
logLevel = string(cfg.Observability.LogLevel)
|
|
}
|
|
|
|
return MergeEnv(
|
|
[]corev1.EnvVar{
|
|
{Name: "GITPOD_DOMAIN", Value: cfg.Domain},
|
|
{Name: "GITPOD_INSTALLATION_SHORTNAME", Value: cfg.Metadata.InstallationShortname},
|
|
{Name: "GITPOD_REGION", Value: cfg.Metadata.Region},
|
|
{Name: "HOST_URL", Value: "https://" + cfg.Domain},
|
|
{Name: "KUBE_NAMESPACE", ValueFrom: &corev1.EnvVarSource{
|
|
FieldRef: &corev1.ObjectFieldSelector{
|
|
FieldPath: "metadata.namespace",
|
|
},
|
|
}},
|
|
{Name: "KUBE_DOMAIN", Value: "svc.cluster.local"},
|
|
{Name: "LOG_LEVEL", Value: strings.ToLower(logLevel)},
|
|
},
|
|
ProxyEnv(cfg),
|
|
)
|
|
}
|
|
|
|
func WorkspaceTracingEnv(context *RenderContext, component string) (res []corev1.EnvVar) {
|
|
var tracing *experimental.Tracing
|
|
|
|
_ = context.WithExperimental(func(cfg *experimental.Config) error {
|
|
if cfg.Workspace != nil {
|
|
tracing = cfg.Workspace.Tracing
|
|
}
|
|
return nil
|
|
})
|
|
|
|
return tracingEnv(context, component, tracing)
|
|
}
|
|
|
|
func WebappTracingEnv(context *RenderContext, component string) (res []corev1.EnvVar) {
|
|
var tracing *experimental.Tracing
|
|
|
|
_ = context.WithExperimental(func(cfg *experimental.Config) error {
|
|
if cfg.WebApp != nil {
|
|
tracing = cfg.WebApp.Tracing
|
|
}
|
|
return nil
|
|
})
|
|
|
|
return tracingEnv(context, component, tracing)
|
|
}
|
|
|
|
func tracingEnv(context *RenderContext, component string, tracing *experimental.Tracing) (res []corev1.EnvVar) {
|
|
// For OpenTelemetry (OTEL) environment variable specification, see https://opentelemetry.io/docs/reference/specification/protocol/exporter/
|
|
|
|
if context.Config.Observability.Tracing == nil {
|
|
res = append(res, corev1.EnvVar{Name: "JAEGER_DISABLED", Value: "true"})
|
|
res = append(res, corev1.EnvVar{Name: "OTEL_SDK_DISABLED", Value: "true"})
|
|
return
|
|
}
|
|
|
|
if ep := context.Config.Observability.Tracing.Endpoint; ep != nil {
|
|
res = append(res, corev1.EnvVar{Name: "JAEGER_ENDPOINT", Value: *ep})
|
|
res = append(res, corev1.EnvVar{Name: "OTEL_EXPORTER_OTLP_ENDPOINT", Value: *ep})
|
|
} else if v := context.Config.Observability.Tracing.AgentHost; v != nil {
|
|
res = append(res, corev1.EnvVar{Name: "JAEGER_AGENT_HOST", Value: *v})
|
|
} else {
|
|
// TODO(cw): think about proper error handling here.
|
|
// Returning an error would be the appropriate thing to do,
|
|
// but would make env var composition more cumbersome.
|
|
}
|
|
|
|
if context.Config.Observability.Tracing.SecretName != nil {
|
|
res = append(res, corev1.EnvVar{
|
|
Name: "JAEGER_USER",
|
|
ValueFrom: &corev1.EnvVarSource{SecretKeyRef: &corev1.SecretKeySelector{
|
|
LocalObjectReference: corev1.LocalObjectReference{Name: *context.Config.Observability.Tracing.SecretName},
|
|
Key: "JAEGER_USER",
|
|
}},
|
|
})
|
|
|
|
res = append(res, corev1.EnvVar{
|
|
Name: "JAEGER_PASSWORD",
|
|
ValueFrom: &corev1.EnvVarSource{SecretKeyRef: &corev1.SecretKeySelector{
|
|
LocalObjectReference: corev1.LocalObjectReference{Name: *context.Config.Observability.Tracing.SecretName},
|
|
Key: "JAEGER_PASSWORD",
|
|
}},
|
|
})
|
|
}
|
|
|
|
res = append(res, corev1.EnvVar{Name: "JAEGER_SERVICE_NAME", Value: component})
|
|
res = append(res, corev1.EnvVar{Name: "OTEL_SERVICE_NAME", Value: component})
|
|
|
|
jaegerTags := []string{}
|
|
if context.Config.Metadata.InstallationShortname != "" {
|
|
jaegerTags = append(jaegerTags, fmt.Sprintf("cluster=%v", context.Config.Metadata.InstallationShortname))
|
|
}
|
|
|
|
if context.Config.Metadata.Region != "" {
|
|
jaegerTags = append(jaegerTags, fmt.Sprintf("region=%v", context.Config.Metadata.Region))
|
|
}
|
|
|
|
if len(jaegerTags) > 0 {
|
|
res = append(res,
|
|
corev1.EnvVar{Name: "JAEGER_TAGS", Value: strings.Join(jaegerTags, ",")},
|
|
// https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/resource/sdk.md#specifying-resource-information-via-an-environment-variable
|
|
corev1.EnvVar{Name: "OTEL_RESOURCE_ATTRIBUTES", Value: strings.Join(jaegerTags, ",")},
|
|
)
|
|
}
|
|
|
|
samplerType := experimental.TracingSampleTypeConst
|
|
samplerParam := "1"
|
|
|
|
if tracing != nil {
|
|
if tracing.SamplerType != nil {
|
|
samplerType = *tracing.SamplerType
|
|
}
|
|
if tracing.SamplerParam != nil {
|
|
samplerParam = strconv.FormatFloat(*tracing.SamplerParam, 'f', -1, 64)
|
|
}
|
|
}
|
|
|
|
res = append(res,
|
|
corev1.EnvVar{Name: "JAEGER_SAMPLER_TYPE", Value: string(samplerType)},
|
|
corev1.EnvVar{Name: "JAEGER_SAMPLER_PARAM", Value: samplerParam},
|
|
|
|
corev1.EnvVar{Name: "OTEL_TRACES_SAMPLER", Value: string(samplerType)},
|
|
corev1.EnvVar{Name: "OTEL_TRACES_SAMPLER_ARG", Value: samplerParam},
|
|
)
|
|
|
|
return
|
|
}
|
|
|
|
func AnalyticsEnv(cfg *config.Config) (res []corev1.EnvVar) {
|
|
if cfg.Analytics == nil {
|
|
return
|
|
}
|
|
|
|
return []corev1.EnvVar{{
|
|
Name: "GITPOD_ANALYTICS_WRITER",
|
|
Value: cfg.Analytics.Writer,
|
|
}, {
|
|
Name: "GITPOD_ANALYTICS_SEGMENT_KEY",
|
|
Value: cfg.Analytics.SegmentKey,
|
|
}}
|
|
}
|
|
|
|
func MessageBusEnv(cfg *config.Config) (res []corev1.EnvVar) {
|
|
clusterObj := corev1.LocalObjectReference{Name: InClusterMessageQueueName}
|
|
tlsObj := corev1.LocalObjectReference{Name: InClusterMessageQueueTLS}
|
|
|
|
credsSecret := clusterObj
|
|
if cfg.MessageBus != nil && cfg.MessageBus.Credentials != nil {
|
|
credsSecret = corev1.LocalObjectReference{Name: cfg.MessageBus.Credentials.Name}
|
|
}
|
|
|
|
return []corev1.EnvVar{{
|
|
Name: "MESSAGEBUS_USERNAME",
|
|
ValueFrom: &corev1.EnvVarSource{SecretKeyRef: &corev1.SecretKeySelector{
|
|
LocalObjectReference: clusterObj,
|
|
Key: "username",
|
|
}},
|
|
}, {
|
|
Name: "MESSAGEBUS_PASSWORD",
|
|
ValueFrom: &corev1.EnvVarSource{SecretKeyRef: &corev1.SecretKeySelector{
|
|
LocalObjectReference: credsSecret,
|
|
Key: "rabbitmq-password",
|
|
}},
|
|
}, {
|
|
Name: "MESSAGEBUS_CA",
|
|
ValueFrom: &corev1.EnvVarSource{SecretKeyRef: &corev1.SecretKeySelector{
|
|
LocalObjectReference: tlsObj,
|
|
Key: "ca.crt",
|
|
}},
|
|
}, {
|
|
Name: "MESSAGEBUS_CERT",
|
|
ValueFrom: &corev1.EnvVarSource{SecretKeyRef: &corev1.SecretKeySelector{
|
|
LocalObjectReference: tlsObj,
|
|
Key: "tls.crt",
|
|
}},
|
|
}, {
|
|
Name: "MESSAGEBUS_KEY",
|
|
ValueFrom: &corev1.EnvVarSource{SecretKeyRef: &corev1.SecretKeySelector{
|
|
LocalObjectReference: tlsObj,
|
|
Key: "tls.key",
|
|
}},
|
|
}}
|
|
}
|
|
|
|
func DatabaseEnv(cfg *config.Config) (res []corev1.EnvVar) {
|
|
var (
|
|
secretRef corev1.LocalObjectReference
|
|
envvars []corev1.EnvVar
|
|
)
|
|
|
|
if pointer.BoolDeref(cfg.Database.InCluster, false) {
|
|
secretRef = corev1.LocalObjectReference{Name: InClusterDbSecret}
|
|
envvars = append(envvars,
|
|
corev1.EnvVar{
|
|
Name: "DB_HOST",
|
|
ValueFrom: &corev1.EnvVarSource{SecretKeyRef: &corev1.SecretKeySelector{
|
|
LocalObjectReference: secretRef,
|
|
Key: "host",
|
|
}},
|
|
},
|
|
corev1.EnvVar{
|
|
Name: "DB_PORT",
|
|
ValueFrom: &corev1.EnvVarSource{SecretKeyRef: &corev1.SecretKeySelector{
|
|
LocalObjectReference: secretRef,
|
|
Key: "port",
|
|
}},
|
|
},
|
|
)
|
|
} else if cfg.Database.External != nil && cfg.Database.External.Certificate.Name != "" {
|
|
// External DB
|
|
secretRef = corev1.LocalObjectReference{Name: cfg.Database.External.Certificate.Name}
|
|
envvars = append(envvars,
|
|
corev1.EnvVar{
|
|
Name: "DB_HOST",
|
|
ValueFrom: &corev1.EnvVarSource{SecretKeyRef: &corev1.SecretKeySelector{
|
|
LocalObjectReference: secretRef,
|
|
Key: "host",
|
|
}},
|
|
},
|
|
corev1.EnvVar{
|
|
Name: "DB_PORT",
|
|
ValueFrom: &corev1.EnvVarSource{SecretKeyRef: &corev1.SecretKeySelector{
|
|
LocalObjectReference: secretRef,
|
|
Key: "port",
|
|
}},
|
|
},
|
|
)
|
|
} else if cfg.Database.CloudSQL != nil && cfg.Database.CloudSQL.ServiceAccount.Name != "" {
|
|
// GCP
|
|
secretRef = corev1.LocalObjectReference{Name: cfg.Database.CloudSQL.ServiceAccount.Name}
|
|
envvars = append(envvars,
|
|
corev1.EnvVar{
|
|
Name: "DB_HOST",
|
|
Value: "cloudsqlproxy",
|
|
},
|
|
corev1.EnvVar{
|
|
Name: "DB_PORT",
|
|
Value: "3306",
|
|
},
|
|
)
|
|
} else {
|
|
panic("invalid database configuration")
|
|
}
|
|
|
|
envvars = append(envvars,
|
|
corev1.EnvVar{
|
|
Name: "DB_PASSWORD",
|
|
ValueFrom: &corev1.EnvVarSource{SecretKeyRef: &corev1.SecretKeySelector{
|
|
LocalObjectReference: secretRef,
|
|
Key: "password",
|
|
}},
|
|
},
|
|
corev1.EnvVar{
|
|
Name: "DB_USERNAME",
|
|
ValueFrom: &corev1.EnvVarSource{SecretKeyRef: &corev1.SecretKeySelector{
|
|
LocalObjectReference: secretRef,
|
|
Key: "username",
|
|
}},
|
|
},
|
|
corev1.EnvVar{
|
|
Name: "DB_ENCRYPTION_KEYS",
|
|
ValueFrom: &corev1.EnvVarSource{SecretKeyRef: &corev1.SecretKeySelector{
|
|
LocalObjectReference: secretRef,
|
|
Key: "encryptionKeys",
|
|
}},
|
|
},
|
|
)
|
|
|
|
if cfg.Database.SSL != nil && cfg.Database.SSL.CaCert != nil {
|
|
secretRef = corev1.LocalObjectReference{Name: cfg.Database.SSL.CaCert.Name}
|
|
envvars = append(envvars, corev1.EnvVar{
|
|
Name: DBCaCertEnvVarName,
|
|
ValueFrom: &corev1.EnvVarSource{SecretKeyRef: &corev1.SecretKeySelector{
|
|
LocalObjectReference: secretRef,
|
|
Key: DBCaFileName,
|
|
}},
|
|
})
|
|
}
|
|
|
|
return envvars
|
|
}
|
|
|
|
func DatabaseEnvSecret(cfg config.Config) (corev1.Volume, corev1.VolumeMount, string) {
|
|
var secretName string
|
|
|
|
if pointer.BoolDeref(cfg.Database.InCluster, false) {
|
|
secretName = InClusterDbSecret
|
|
} else if cfg.Database.External != nil && cfg.Database.External.Certificate.Name != "" {
|
|
// External DB
|
|
secretName = cfg.Database.External.Certificate.Name
|
|
|
|
} else if cfg.Database.CloudSQL != nil && cfg.Database.CloudSQL.ServiceAccount.Name != "" {
|
|
// GCP
|
|
secretName = cfg.Database.CloudSQL.ServiceAccount.Name
|
|
|
|
} else {
|
|
panic("invalid database configuration")
|
|
}
|
|
|
|
volume := corev1.Volume{
|
|
Name: "database-config",
|
|
VolumeSource: corev1.VolumeSource{
|
|
Secret: &corev1.SecretVolumeSource{
|
|
SecretName: secretName,
|
|
},
|
|
},
|
|
}
|
|
|
|
mount := corev1.VolumeMount{
|
|
Name: "database-config",
|
|
MountPath: DatabaseConfigMountPath,
|
|
ReadOnly: true,
|
|
}
|
|
|
|
return volume, mount, DatabaseConfigMountPath
|
|
}
|
|
|
|
func ConfigcatEnv(ctx *RenderContext) []corev1.EnvVar {
|
|
var sdkKey string
|
|
_ = ctx.WithExperimental(func(cfg *experimental.Config) error {
|
|
if cfg.WebApp != nil && cfg.WebApp.ConfigcatKey != "" {
|
|
sdkKey = cfg.WebApp.ConfigcatKey
|
|
}
|
|
return nil
|
|
})
|
|
|
|
if sdkKey == "" {
|
|
return nil
|
|
}
|
|
|
|
return []corev1.EnvVar{
|
|
{
|
|
Name: "CONFIGCAT_SDK_KEY",
|
|
Value: "gitpod",
|
|
},
|
|
{
|
|
Name: "CONFIGCAT_BASE_URL",
|
|
Value: "https://" + ctx.Config.Domain + "/configcat",
|
|
},
|
|
}
|
|
}
|
|
|
|
func ConfigcatProxyEnv(ctx *RenderContext) []corev1.EnvVar {
|
|
var (
|
|
sdkKey string
|
|
baseUrl string
|
|
pollInterval string
|
|
)
|
|
_ = ctx.WithExperimental(func(cfg *experimental.Config) error {
|
|
if cfg.WebApp != nil && cfg.WebApp.ConfigcatKey != "" {
|
|
sdkKey = cfg.WebApp.ConfigcatKey
|
|
}
|
|
if cfg.WebApp != nil && cfg.WebApp.ProxyConfig != nil && cfg.WebApp.ProxyConfig.Configcat != nil {
|
|
baseUrl = cfg.WebApp.ProxyConfig.Configcat.BaseUrl
|
|
pollInterval = cfg.WebApp.ProxyConfig.Configcat.PollInterval
|
|
}
|
|
return nil
|
|
})
|
|
|
|
if sdkKey == "" {
|
|
return nil
|
|
}
|
|
|
|
return []corev1.EnvVar{
|
|
{
|
|
Name: "CONFIGCAT_SDK_KEY",
|
|
Value: sdkKey,
|
|
},
|
|
{
|
|
Name: "CONFIGCAT_BASE_URL",
|
|
Value: baseUrl,
|
|
},
|
|
{
|
|
Name: "CONFIGCAT_POLL_INTERVAL",
|
|
Value: pollInterval,
|
|
},
|
|
}
|
|
}
|
|
|
|
func DatabaseWaiterContainer(ctx *RenderContext) *corev1.Container {
|
|
return &corev1.Container{
|
|
Name: "database-waiter",
|
|
Image: ctx.ImageName(ctx.Config.Repository, "service-waiter", ctx.VersionManifest.Components.ServiceWaiter.Version),
|
|
Args: []string{
|
|
"-v",
|
|
"database",
|
|
},
|
|
SecurityContext: &corev1.SecurityContext{
|
|
Privileged: pointer.Bool(false),
|
|
AllowPrivilegeEscalation: pointer.Bool(false),
|
|
RunAsUser: pointer.Int64(31001),
|
|
},
|
|
Env: MergeEnv(
|
|
DatabaseEnv(&ctx.Config),
|
|
ProxyEnv(&ctx.Config),
|
|
),
|
|
}
|
|
}
|
|
|
|
func MessageBusWaiterContainer(ctx *RenderContext) *corev1.Container {
|
|
return &corev1.Container{
|
|
Name: "msgbus-waiter",
|
|
Image: ctx.ImageName(ctx.Config.Repository, "service-waiter", ctx.VersionManifest.Components.ServiceWaiter.Version),
|
|
Args: []string{
|
|
"-v",
|
|
"messagebus",
|
|
},
|
|
SecurityContext: &corev1.SecurityContext{
|
|
Privileged: pointer.Bool(false),
|
|
AllowPrivilegeEscalation: pointer.Bool(false),
|
|
RunAsUser: pointer.Int64(31001),
|
|
},
|
|
Env: MergeEnv(
|
|
MessageBusEnv(&ctx.Config),
|
|
ProxyEnv(&ctx.Config),
|
|
),
|
|
}
|
|
}
|
|
|
|
func KubeRBACProxyContainer(ctx *RenderContext) *corev1.Container {
|
|
return KubeRBACProxyContainerWithConfig(ctx)
|
|
}
|
|
|
|
func KubeRBACProxyContainerWithConfig(ctx *RenderContext) *corev1.Container {
|
|
return &corev1.Container{
|
|
Name: "kube-rbac-proxy",
|
|
Image: ctx.ImageName(ThirdPartyContainerRepo(ctx.Config.Repository, KubeRBACProxyRepo), KubeRBACProxyImage, KubeRBACProxyTag),
|
|
Args: []string{
|
|
"--logtostderr",
|
|
fmt.Sprintf("--insecure-listen-address=[$(IP)]:%d", baseserver.BuiltinMetricsPort),
|
|
fmt.Sprintf("--upstream=http://127.0.0.1:%d/", baseserver.BuiltinMetricsPort),
|
|
},
|
|
Ports: []corev1.ContainerPort{
|
|
{Name: baseserver.BuiltinMetricsPortName, ContainerPort: baseserver.BuiltinMetricsPort},
|
|
},
|
|
Env: MergeEnv(
|
|
[]corev1.EnvVar{
|
|
{
|
|
Name: "IP",
|
|
ValueFrom: &corev1.EnvVarSource{
|
|
FieldRef: &corev1.ObjectFieldSelector{
|
|
FieldPath: "status.podIP",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
ProxyEnv(&ctx.Config),
|
|
),
|
|
Resources: corev1.ResourceRequirements{Requests: corev1.ResourceList{
|
|
corev1.ResourceCPU: resource.MustParse("1m"),
|
|
corev1.ResourceMemory: resource.MustParse("30Mi"),
|
|
}},
|
|
TerminationMessagePolicy: corev1.TerminationMessageFallbackToLogsOnError,
|
|
SecurityContext: &corev1.SecurityContext{
|
|
AllowPrivilegeEscalation: pointer.Bool(false),
|
|
RunAsUser: pointer.Int64(65532),
|
|
RunAsGroup: pointer.Int64(65532),
|
|
RunAsNonRoot: pointer.Bool(true),
|
|
},
|
|
}
|
|
}
|
|
|
|
func IsDatabaseMigrationDisabled(ctx *RenderContext) bool {
|
|
disableMigration := false
|
|
_ = ctx.WithExperimental(func(cfg *experimental.Config) error {
|
|
if cfg.WebApp != nil {
|
|
disableMigration = cfg.WebApp.DisableMigration
|
|
}
|
|
return nil
|
|
})
|
|
return disableMigration
|
|
}
|
|
|
|
func Replicas(ctx *RenderContext, component string) *int32 {
|
|
replicas := int32(1)
|
|
|
|
if ctx.Config.Components != nil && ctx.Config.Components.PodConfig[component] != nil {
|
|
if ctx.Config.Components.PodConfig[component].Replicas != nil {
|
|
replicas = *ctx.Config.Components.PodConfig[component].Replicas
|
|
}
|
|
}
|
|
|
|
return &replicas
|
|
}
|
|
|
|
func ResourceRequirements(ctx *RenderContext, component, containerName string, defaults corev1.ResourceRequirements) corev1.ResourceRequirements {
|
|
resources := defaults
|
|
|
|
if ctx.Config.Components != nil && ctx.Config.Components.PodConfig[component] != nil {
|
|
if ctx.Config.Components.PodConfig[component].Resources[containerName] != nil {
|
|
resources = *ctx.Config.Components.PodConfig[component].Resources[containerName]
|
|
}
|
|
}
|
|
|
|
return resources
|
|
}
|
|
|
|
// ObjectHash marshals the objects to YAML and produces a sha256 hash of the output.
|
|
// This function is useful for restarting pods when the config changes.
|
|
// Takes an error as argument to make calling it more conventient. If that error is not nil,
|
|
// it's passed right through
|
|
func ObjectHash(objs []runtime.Object, err error) (string, error) {
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
hash := sha256.New()
|
|
for _, o := range objs {
|
|
b, err := yaml.Marshal(o)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
_, _ = hash.Write(b)
|
|
}
|
|
return fmt.Sprintf("%x", hash.Sum(nil)), nil
|
|
}
|
|
|
|
var (
|
|
TCPProtocol = func() *corev1.Protocol {
|
|
tcpProtocol := corev1.ProtocolTCP
|
|
return &tcpProtocol
|
|
}()
|
|
PrometheusIngressRule = networkingv1.NetworkPolicyIngressRule{
|
|
Ports: []networkingv1.NetworkPolicyPort{
|
|
{
|
|
Protocol: TCPProtocol,
|
|
Port: &intstr.IntOrString{IntVal: baseserver.BuiltinMetricsPort},
|
|
},
|
|
},
|
|
From: []networkingv1.NetworkPolicyPeer{
|
|
{
|
|
// todo(sje): add these labels to the prometheus instance
|
|
PodSelector: &metav1.LabelSelector{
|
|
MatchLabels: map[string]string{
|
|
"app": "prometheus",
|
|
"component": "server",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
)
|
|
|
|
var DeploymentStrategy = appsv1.DeploymentStrategy{
|
|
Type: appsv1.RollingUpdateDeploymentStrategyType,
|
|
RollingUpdate: &appsv1.RollingUpdateDeployment{
|
|
MaxSurge: &intstr.IntOrString{IntVal: 1},
|
|
MaxUnavailable: &intstr.IntOrString{IntVal: 0},
|
|
},
|
|
}
|
|
|
|
// TODO(cw): find a better way to do this. Those values must exist in the appropriate places already.
|
|
var (
|
|
TypeMetaNamespace = metav1.TypeMeta{
|
|
APIVersion: "v1",
|
|
Kind: "Namespace",
|
|
}
|
|
TypeMetaStatefulSet = metav1.TypeMeta{
|
|
APIVersion: "apps/v1",
|
|
Kind: "StatefulSet",
|
|
}
|
|
TypeMetaConfigmap = metav1.TypeMeta{
|
|
APIVersion: "v1",
|
|
Kind: "ConfigMap",
|
|
}
|
|
TypeMetaServiceAccount = metav1.TypeMeta{
|
|
APIVersion: "v1",
|
|
Kind: "ServiceAccount",
|
|
}
|
|
TypeMetaPod = metav1.TypeMeta{
|
|
APIVersion: "v1",
|
|
Kind: "Pod",
|
|
}
|
|
TypeMetaDaemonset = metav1.TypeMeta{
|
|
APIVersion: "apps/v1",
|
|
Kind: "DaemonSet",
|
|
}
|
|
TypeMetaService = metav1.TypeMeta{
|
|
APIVersion: "v1",
|
|
Kind: "Service",
|
|
}
|
|
TypeMetaClusterRole = metav1.TypeMeta{
|
|
APIVersion: "rbac.authorization.k8s.io/v1",
|
|
Kind: "ClusterRole",
|
|
}
|
|
TypeMetaClusterRoleBinding = metav1.TypeMeta{
|
|
APIVersion: "rbac.authorization.k8s.io/v1",
|
|
Kind: "ClusterRoleBinding",
|
|
}
|
|
TypeMetaRoleBinding = metav1.TypeMeta{
|
|
APIVersion: "rbac.authorization.k8s.io/v1",
|
|
Kind: "RoleBinding",
|
|
}
|
|
TypeMetaRole = metav1.TypeMeta{
|
|
APIVersion: "rbac.authorization.k8s.io/v1",
|
|
Kind: "Role",
|
|
}
|
|
TypeMetaNetworkPolicy = metav1.TypeMeta{
|
|
APIVersion: "networking.k8s.io/v1",
|
|
Kind: "NetworkPolicy",
|
|
}
|
|
TypeMetaDeployment = metav1.TypeMeta{
|
|
APIVersion: "apps/v1",
|
|
Kind: "Deployment",
|
|
}
|
|
TypeMetaCertificate = metav1.TypeMeta{
|
|
APIVersion: "cert-manager.io/v1",
|
|
Kind: "Certificate",
|
|
}
|
|
TypeMetaCertificateIssuer = metav1.TypeMeta{
|
|
APIVersion: "cert-manager.io/v1",
|
|
Kind: "Issuer",
|
|
}
|
|
TypeMetaSecret = metav1.TypeMeta{
|
|
APIVersion: "v1",
|
|
Kind: "Secret",
|
|
}
|
|
TypeMetaPodSecurityPolicy = metav1.TypeMeta{
|
|
APIVersion: "policy/v1beta1",
|
|
Kind: "PodSecurityPolicy",
|
|
}
|
|
TypeMetaResourceQuota = metav1.TypeMeta{
|
|
APIVersion: "v1",
|
|
Kind: "ResourceQuota",
|
|
}
|
|
TypeMetaBatchJob = metav1.TypeMeta{
|
|
APIVersion: "batch/v1",
|
|
Kind: "Job",
|
|
}
|
|
TypeMetaBatchCronJob = metav1.TypeMeta{
|
|
APIVersion: "batch/v1",
|
|
Kind: "CronJob",
|
|
}
|
|
TypeMetaCertificateClusterIssuer = metav1.TypeMeta{
|
|
APIVersion: "cert-manager.io/v1",
|
|
Kind: "ClusterIssuer",
|
|
}
|
|
TypeMetaBundle = metav1.TypeMeta{
|
|
APIVersion: "trust.cert-manager.io/v1alpha1",
|
|
Kind: "Bundle",
|
|
}
|
|
)
|
|
|
|
// validCookieChars contains all characters which may occur in an HTTP Cookie value (unicode \u0021 through \u007E),
|
|
// without the characters , ; and / ... I did not find more details about permissible characters in RFC2965, so I took
|
|
// this list of permissible chars from Wikipedia.
|
|
//
|
|
// The tokens we produce here (e.g. owner token or CLI API token) are likely placed in cookies or transmitted via HTTP.
|
|
// To make the lifes of downstream users easier we'll try and play nice here w.r.t. to the characters used.
|
|
var validCookieChars = []byte("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_-.")
|
|
|
|
// RandomString produces a cryptographically secure random string of length N.
|
|
// The string contains alphanumeric characters and _ (underscore), - (dash) and . (dot)
|
|
func RandomString(length int) (string, error) {
|
|
b := make([]byte, length)
|
|
n, err := rand.Read(b)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
if n != length {
|
|
return "", io.ErrShortWrite
|
|
}
|
|
|
|
lrsc := len(validCookieChars)
|
|
for i, c := range b {
|
|
b[i] = validCookieChars[int(c)%lrsc]
|
|
}
|
|
return string(b), nil
|
|
}
|
|
|
|
// ThirdPartyContainerRepo returns the container registry to use for third-party containers.
|
|
// If config registry is set to the Gitpod registry, the third-party registry is returned. If
|
|
// config registry is different, that repository is returned and deployment expected to mirror
|
|
// the images to their registry
|
|
func ThirdPartyContainerRepo(configRegistry string, thirdPartyRegistry string) string {
|
|
configRegistry = strings.TrimSuffix(configRegistry, "/")
|
|
|
|
if configRegistry == GitpodContainerRegistry {
|
|
return thirdPartyRegistry
|
|
}
|
|
|
|
return configRegistry
|
|
}
|
|
|
|
// ToJSONString returns the serialized JSON string of an object
|
|
func ToJSONString(input interface{}) ([]byte, error) {
|
|
return json.MarshalIndent(input, "", " ")
|
|
}
|
|
|
|
func NodeNameEnv(context *RenderContext) []corev1.EnvVar {
|
|
return []corev1.EnvVar{{
|
|
Name: "NODENAME",
|
|
ValueFrom: &corev1.EnvVarSource{
|
|
FieldRef: &corev1.ObjectFieldSelector{FieldPath: "spec.nodeName"},
|
|
},
|
|
}}
|
|
}
|
|
|
|
func NodeIPEnv(context *RenderContext) []corev1.EnvVar {
|
|
return []corev1.EnvVar{{
|
|
Name: "NODE_IP",
|
|
ValueFrom: &corev1.EnvVarSource{
|
|
FieldRef: &corev1.ObjectFieldSelector{FieldPath: "status.hostIP"},
|
|
},
|
|
}}
|
|
}
|
|
|
|
// ExperimentalWebappConfig extracts webapp experimental config from the render context.
|
|
// When the experimental config is not defined, the result will be nil.
|
|
func ExperimentalWebappConfig(ctx *RenderContext) *experimental.WebAppConfig {
|
|
var experimentalCfg *experimental.Config
|
|
_ = ctx.WithExperimental(func(ucfg *experimental.Config) error {
|
|
experimentalCfg = ucfg
|
|
return nil
|
|
})
|
|
|
|
if experimentalCfg == nil || experimentalCfg.WebApp == nil {
|
|
return nil
|
|
}
|
|
|
|
return experimentalCfg.WebApp
|
|
}
|
|
|
|
// WithLocalWsManager returns true if the installed application cluster should connect to a local ws-manager
|
|
func WithLocalWsManager(ctx *RenderContext) bool {
|
|
return ctx.Config.Kind == config.InstallationFull
|
|
}
|