diff --git a/.werft/jobs/build/installer/installer.ts b/.werft/jobs/build/installer/installer.ts index 912ff5f9543b3a..7a0fdd11b2aba1 100644 --- a/.werft/jobs/build/installer/installer.ts +++ b/.werft/jobs/build/installer/installer.ts @@ -235,9 +235,7 @@ EOF`); } private configurePublicAPIServer(slice: string) { - exec(`yq w -i ${this.options.installerConfigPath} experimental.webapp.publicApi.enabled true`, { - slice: slice, - }); + exec(`yq w -i ${this.options.installerConfigPath} experimental.webapp.publicApi.enabled true`, { slice: slice }); } private configureUsage(slice: string) { diff --git a/components/public-api/go/config/config.go b/components/public-api/go/config/config.go index d5c9e9c31f24cb..319c3e1040a123 100644 --- a/components/public-api/go/config/config.go +++ b/components/public-api/go/config/config.go @@ -11,5 +11,12 @@ type Configuration struct { BillingServiceAddress string `json:"billingServiceAddress,omitempty"` + // StripeWebhookSigningSecretPath is a filepath to a secret used to validate incoming webhooks from Stripe + StripeWebhookSigningSecretPath string `json:"stripeWebhookSigningSecretPath"` + Server *baseserver.Configuration `json:"server,omitempty"` } + +type StripeSecret struct { + WebhookSigningKey string `json:"signingKey"` +} diff --git a/install/installer/pkg/components/public-api-server/configmap.go b/install/installer/pkg/components/public-api-server/configmap.go index 13c49b1747d544..60427fafa830f4 100644 --- a/install/installer/pkg/components/public-api-server/configmap.go +++ b/install/installer/pkg/components/public-api-server/configmap.go @@ -6,7 +6,10 @@ package public_api_server import ( "fmt" + "github.com/gitpod-io/gitpod/installer/pkg/config/v1/experimental" + "k8s.io/utils/pointer" "net" + "path/filepath" "strconv" "github.com/gitpod-io/gitpod/common-go/baseserver" @@ -24,9 +27,17 @@ const ( ) func configmap(ctx *common.RenderContext) ([]runtime.Object, error) { + var stripeSecretPath string + + _ = ctx.WithExperimental(func(cfg *experimental.Config) error { + _, _, stripeSecretPath, _ = getStripeConfig(cfg) + return nil + }) + cfg := config.Configuration{ - GitpodServiceURL: fmt.Sprintf("wss://%s/api/v1", ctx.Config.Domain), - BillingServiceAddress: net.JoinHostPort(usage.Component, strconv.Itoa(usage.GRPCServicePort)), + GitpodServiceURL: fmt.Sprintf("wss://%s/api/v1", ctx.Config.Domain), + StripeWebhookSigningSecretPath: stripeSecretPath, + BillingServiceAddress: net.JoinHostPort(usage.Component, strconv.Itoa(usage.GRPCServicePort)), Server: &baseserver.Configuration{ Services: baseserver.ServicesConfiguration{ GRPC: &baseserver.ServerConfiguration{ @@ -59,3 +70,35 @@ func configmap(ctx *common.RenderContext) ([]runtime.Object, error) { }, }, nil } + +func getStripeConfig(cfg *experimental.Config) (corev1.Volume, corev1.VolumeMount, string, bool) { + var volume corev1.Volume + var mount corev1.VolumeMount + var path string + + if cfg == nil || cfg.WebApp == nil || cfg.WebApp.PublicAPI == nil || cfg.WebApp.PublicAPI.StripeSecretName == "" { + return volume, mount, path, false + } + + stripeSecret := cfg.WebApp.PublicAPI.StripeSecretName + + volume = corev1.Volume{ + Name: "stripe-secret", + VolumeSource: corev1.VolumeSource{ + Secret: &corev1.SecretVolumeSource{ + SecretName: stripeSecret, + Optional: pointer.Bool(true), + }, + }, + } + + mount = corev1.VolumeMount{ + Name: "stripe-secret", + MountPath: stripeSecretMountPath, + ReadOnly: true, + } + + path = filepath.Join(secretsDirectory, stripeSecretMountPath) + + return volume, mount, path, true +} diff --git a/install/installer/pkg/components/public-api-server/configmap_test.go b/install/installer/pkg/components/public-api-server/configmap_test.go index 662eef736a841b..85ed40c04d1d10 100644 --- a/install/installer/pkg/components/public-api-server/configmap_test.go +++ b/install/installer/pkg/components/public-api-server/configmap_test.go @@ -5,6 +5,7 @@ package public_api_server import ( "fmt" + "github.com/gitpod-io/gitpod/installer/pkg/config/v1/experimental" "testing" "github.com/gitpod-io/gitpod/common-go/baseserver" @@ -22,9 +23,16 @@ func TestConfigMap(t *testing.T) { require.Len(t, objs, 1, "must only render one configmap") + var stripeSecretPath string + _ = ctx.WithExperimental(func(ucfg *experimental.Config) error { + _, _, stripeSecretPath, _ = getStripeConfig(ucfg) + return nil + }) + expectedConfiguration := config.Configuration{ - GitpodServiceURL: "wss://test.domain.everything.awesome.is/api/v1", - BillingServiceAddress: "usage:9001", + GitpodServiceURL: "wss://test.domain.everything.awesome.is/api/v1", + BillingServiceAddress: "usage:9001", + StripeWebhookSigningSecretPath: stripeSecretPath, Server: &baseserver.Configuration{ Services: baseserver.ServicesConfiguration{ GRPC: &baseserver.ServerConfiguration{ diff --git a/install/installer/pkg/components/public-api-server/constants.go b/install/installer/pkg/components/public-api-server/constants.go index 6218c054caf526..39e70130ec2aae 100644 --- a/install/installer/pkg/components/public-api-server/constants.go +++ b/install/installer/pkg/components/public-api-server/constants.go @@ -13,4 +13,7 @@ const ( HTTPContainerPort = 9002 HTTPServicePort = 9002 HTTPPortName = "http" + + secretsDirectory = "secrets" + stripeSecretMountPath = "stripe-secret" ) diff --git a/install/installer/pkg/components/public-api-server/deployment.go b/install/installer/pkg/components/public-api-server/deployment.go index b1d560bdd0b13b..0b3262f797057b 100644 --- a/install/installer/pkg/components/public-api-server/deployment.go +++ b/install/installer/pkg/components/public-api-server/deployment.go @@ -5,6 +5,7 @@ package public_api_server import ( "fmt" + "github.com/gitpod-io/gitpod/installer/pkg/config/v1/experimental" "github.com/gitpod-io/gitpod/common-go/baseserver" @@ -31,6 +32,38 @@ func deployment(ctx *common.RenderContext) ([]runtime.Object, error) { return nil, err } + volumes := []corev1.Volume{ + { + Name: configmapVolume, + VolumeSource: corev1.VolumeSource{ + ConfigMap: &corev1.ConfigMapVolumeSource{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: Component, + }, + }, + }, + }, + } + volumeMounts := []corev1.VolumeMount{ + { + Name: configmapVolume, + ReadOnly: true, + MountPath: configMountPath, + SubPath: configJSONFilename, + }, + } + + _ = ctx.WithExperimental(func(cfg *experimental.Config) error { + volume, mount, _, ok := getStripeConfig(cfg) + if !ok { + return nil + } + + volumes = append(volumes, volume) + volumeMounts = append(volumeMounts, mount) + return nil + }) + labels := common.CustomizeLabel(ctx, Component, common.TypeMetaDeployment) return []runtime.Object{ &appsv1.Deployment{ @@ -115,29 +148,11 @@ func deployment(ctx *common.RenderContext) ([]runtime.Object, error) { SuccessThreshold: 1, TimeoutSeconds: 1, }, - VolumeMounts: []corev1.VolumeMount{ - { - Name: configmapVolume, - ReadOnly: true, - MountPath: configMountPath, - SubPath: configJSONFilename, - }, - }, + VolumeMounts: volumeMounts, }, *common.KubeRBACProxyContainerWithConfig(ctx), }, - Volumes: []corev1.Volume{ - { - Name: configmapVolume, - VolumeSource: corev1.VolumeSource{ - ConfigMap: &corev1.ConfigMapVolumeSource{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: Component, - }, - }, - }, - }, - }, + Volumes: volumes, }, }, }, diff --git a/install/installer/pkg/components/public-api-server/deployment_test.go b/install/installer/pkg/components/public-api-server/deployment_test.go index 4ece6c7068214b..1047c7fa8588c2 100644 --- a/install/installer/pkg/components/public-api-server/deployment_test.go +++ b/install/installer/pkg/components/public-api-server/deployment_test.go @@ -5,6 +5,7 @@ package public_api_server import ( corev1 "k8s.io/api/core/v1" + "k8s.io/utils/pointer" "testing" "github.com/stretchr/testify/require" @@ -42,14 +43,25 @@ func TestDeployment_ServerArguments(t *testing.T) { `--json-log=true`, }, apiContainer.Args) - require.Equal(t, []corev1.Volume{{ - Name: configmapVolume, - VolumeSource: corev1.VolumeSource{ - ConfigMap: &corev1.ConfigMapVolumeSource{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: Component, + require.Equal(t, []corev1.Volume{ + { + Name: configmapVolume, + VolumeSource: corev1.VolumeSource{ + ConfigMap: &corev1.ConfigMapVolumeSource{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: Component, + }, }, }, }, - }}, dpl.Spec.Template.Spec.Volumes, "must bind config as a volume") + { + Name: "stripe-secret", + VolumeSource: corev1.VolumeSource{ + Secret: &corev1.SecretVolumeSource{ + SecretName: "stripe-webhook-secret", + Optional: pointer.Bool(true), + }, + }, + }, + }, dpl.Spec.Template.Spec.Volumes, "must bind config as a volume") } diff --git a/install/installer/pkg/components/public-api-server/objects_test.go b/install/installer/pkg/components/public-api-server/objects_test.go index 11fd69948de2b0..8b8ee59085ef38 100644 --- a/install/installer/pkg/components/public-api-server/objects_test.go +++ b/install/installer/pkg/components/public-api-server/objects_test.go @@ -36,7 +36,10 @@ func renderContextWithPublicAPIEnabled(t *testing.T) *common.RenderContext { Domain: "test.domain.everything.awesome.is", Experimental: &experimental.Config{ WebApp: &experimental.WebAppConfig{ - PublicAPI: &experimental.PublicAPIConfig{Enabled: true}, + PublicAPI: &experimental.PublicAPIConfig{ + Enabled: true, + StripeSecretName: "stripe-webhook-secret", + }, }, }, }, versions.Manifest{ diff --git a/install/installer/pkg/config/v1/experimental/experimental.go b/install/installer/pkg/config/v1/experimental/experimental.go index 3f76d2ea186e31..9966bff6c829c8 100644 --- a/install/installer/pkg/config/v1/experimental/experimental.go +++ b/install/installer/pkg/config/v1/experimental/experimental.go @@ -231,6 +231,8 @@ type ProxyConfig struct { type PublicAPIConfig struct { Enabled bool `json:"enabled"` + // Name of the kubernetes secret to use for Stripe secrets + StripeSecretName string `json:"stripeSecretName"` } type UsageConfig struct {