feat(dashboard): initial support for studio & pg-meta services
This commit is contained in:
parent
7d9e518f86
commit
0b551325b9
31 changed files with 2151 additions and 492 deletions
|
@ -178,7 +178,7 @@ func (r *APIGatewayReconciler) reconcileEnvoyConfig(
|
|||
}
|
||||
|
||||
_, err = controllerutil.CreateOrUpdate(ctx, r.Client, configMap, func() error {
|
||||
configMap.Labels = MergeLabels(objectLabels(gateway, "envoy", "api-gateway", supabase.Images.Postgrest.Tag), gateway.Labels)
|
||||
configMap.Labels = MergeLabels(objectLabels(gateway, "envoy", "api-gateway", supabase.Images.Envoy.Tag), gateway.Labels)
|
||||
|
||||
type nodeSpec struct {
|
||||
Cluster string
|
||||
|
@ -245,58 +245,15 @@ func (r *APIGatewayReconciler) reconileEnvoyDeployment(
|
|||
},
|
||||
}
|
||||
|
||||
envoySpec := gateway.Spec.Envoy
|
||||
|
||||
if envoySpec == nil {
|
||||
envoySpec = new(supabasev1alpha1.EnvoySpec)
|
||||
}
|
||||
|
||||
if envoySpec.WorkloadTemplate == nil {
|
||||
envoySpec.WorkloadTemplate = new(supabasev1alpha1.WorkloadTemplate)
|
||||
}
|
||||
|
||||
if envoySpec.WorkloadTemplate.Workload == nil {
|
||||
envoySpec.WorkloadTemplate.Workload = new(supabasev1alpha1.ContainerTemplate)
|
||||
}
|
||||
|
||||
var (
|
||||
image = supabase.Images.Envoy.String()
|
||||
podSecurityContext = envoySpec.WorkloadTemplate.SecurityContext
|
||||
pullPolicy = envoySpec.WorkloadTemplate.Workload.PullPolicy
|
||||
containerSecurityContext = envoySpec.WorkloadTemplate.Workload.SecurityContext
|
||||
envoySpec = gateway.Spec.Envoy
|
||||
serviceCfg = supabase.ServiceConfig.Envoy
|
||||
)
|
||||
|
||||
if img := envoySpec.WorkloadTemplate.Workload.Image; img != "" {
|
||||
image = img
|
||||
}
|
||||
|
||||
if podSecurityContext == nil {
|
||||
podSecurityContext = &corev1.PodSecurityContext{
|
||||
RunAsNonRoot: ptrOf(true),
|
||||
}
|
||||
}
|
||||
|
||||
if containerSecurityContext == nil {
|
||||
containerSecurityContext = &corev1.SecurityContext{
|
||||
Privileged: ptrOf(false),
|
||||
RunAsUser: ptrOf(int64(65532)),
|
||||
RunAsGroup: ptrOf(int64(65532)),
|
||||
RunAsNonRoot: ptrOf(true),
|
||||
AllowPrivilegeEscalation: ptrOf(false),
|
||||
ReadOnlyRootFilesystem: ptrOf(true),
|
||||
Capabilities: &corev1.Capabilities{
|
||||
Drop: []corev1.Capability{
|
||||
"ALL",
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
_, err := controllerutil.CreateOrUpdate(ctx, r.Client, envoyDeployment, func() error {
|
||||
envoyDeployment.Labels = MergeLabels(
|
||||
objectLabels(gateway, "envoy", "api-gateway", supabase.Images.Postgrest.Tag),
|
||||
envoyDeployment.Labels = envoySpec.WorkloadTemplate.MergeLabels(
|
||||
objectLabels(gateway, "envoy", "api-gateway", supabase.Images.Envoy.Tag),
|
||||
gateway.Labels,
|
||||
envoySpec.WorkloadTemplate.AdditionalLabels,
|
||||
)
|
||||
|
||||
if envoyDeployment.CreationTimestamp.IsZero() {
|
||||
|
@ -305,7 +262,7 @@ func (r *APIGatewayReconciler) reconileEnvoyDeployment(
|
|||
}
|
||||
}
|
||||
|
||||
envoyDeployment.Spec.Replicas = envoySpec.WorkloadTemplate.Replicas
|
||||
envoyDeployment.Spec.Replicas = envoySpec.WorkloadTemplate.ReplicaCount()
|
||||
|
||||
envoyDeployment.Spec.Template = corev1.PodTemplateSpec{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
|
@ -313,19 +270,16 @@ func (r *APIGatewayReconciler) reconileEnvoyDeployment(
|
|||
fmt.Sprintf("%s/%s", supabasev1alpha1.GroupVersion.Group, "config-hash"): configHash,
|
||||
fmt.Sprintf("%s/%s", supabasev1alpha1.GroupVersion.Group, "jwks-hash"): jwksHash,
|
||||
},
|
||||
Labels: MergeLabels(
|
||||
objectLabels(gateway, "envoy", "api-gateway", supabase.Images.Envoy.Tag),
|
||||
envoySpec.WorkloadTemplate.AdditionalLabels,
|
||||
),
|
||||
Labels: objectLabels(gateway, "envoy", "api-gateway", supabase.Images.Envoy.Tag),
|
||||
},
|
||||
Spec: corev1.PodSpec{
|
||||
ImagePullSecrets: envoySpec.WorkloadTemplate.Workload.ImagePullSecrets,
|
||||
ImagePullSecrets: envoySpec.WorkloadTemplate.PullSecrets(),
|
||||
AutomountServiceAccountToken: ptrOf(false),
|
||||
Containers: []corev1.Container{
|
||||
{
|
||||
Name: "envoy-proxy",
|
||||
Image: image,
|
||||
ImagePullPolicy: pullPolicy,
|
||||
Image: envoySpec.WorkloadTemplate.Image(supabase.Images.Envoy.String()),
|
||||
ImagePullPolicy: envoySpec.WorkloadTemplate.ImagePullPolicy(),
|
||||
Args: []string{"-c /etc/envoy/config.yaml"},
|
||||
Ports: []corev1.ContainerPort{
|
||||
{
|
||||
|
@ -362,18 +316,18 @@ func (r *APIGatewayReconciler) reconileEnvoyDeployment(
|
|||
},
|
||||
},
|
||||
},
|
||||
SecurityContext: containerSecurityContext,
|
||||
Resources: envoySpec.WorkloadTemplate.Workload.Resources,
|
||||
VolumeMounts: []corev1.VolumeMount{
|
||||
{
|
||||
SecurityContext: envoySpec.WorkloadTemplate.ContainerSecurityContext(serviceCfg.Defaults.UID, serviceCfg.Defaults.GID),
|
||||
Resources: envoySpec.WorkloadTemplate.Resources(),
|
||||
VolumeMounts: envoySpec.WorkloadTemplate.AdditionalVolumeMounts(
|
||||
corev1.VolumeMount{
|
||||
Name: "config",
|
||||
ReadOnly: true,
|
||||
MountPath: "/etc/envoy",
|
||||
},
|
||||
},
|
||||
),
|
||||
},
|
||||
},
|
||||
SecurityContext: podSecurityContext,
|
||||
SecurityContext: envoySpec.WorkloadTemplate.PodSecurityContext(),
|
||||
Volumes: []corev1.Volume{
|
||||
{
|
||||
Name: "config",
|
||||
|
@ -432,10 +386,10 @@ func (r *APIGatewayReconciler) reconcileEnvoyService(
|
|||
}
|
||||
|
||||
_, err := controllerutil.CreateOrUpdate(ctx, r.Client, envoyService, func() error {
|
||||
envoyService.Labels = MergeLabels(objectLabels(gateway, "envoy", "api-gateway", supabase.Images.Postgrest.Tag), gateway.Labels)
|
||||
envoyService.Labels = MergeLabels(objectLabels(gateway, "envoy", "api-gateway", supabase.Images.Envoy.Tag), gateway.Labels)
|
||||
|
||||
envoyService.Spec = corev1.ServiceSpec{
|
||||
Selector: selectorLabels(gateway, "postgrest"),
|
||||
Selector: selectorLabels(gateway, "envoy"),
|
||||
Ports: []corev1.ServicePort{
|
||||
{
|
||||
Name: "rest",
|
||||
|
|
|
@ -72,52 +72,11 @@ func (r *CoreAuthReconciler) reconcileAuthDeployment(
|
|||
authDeployment = &appsv1.Deployment{
|
||||
ObjectMeta: supabase.ServiceConfig.Auth.ObjectMeta(core),
|
||||
}
|
||||
authSpec = core.Spec.Auth
|
||||
svcCfg = supabase.ServiceConfig.Auth
|
||||
authSpec = core.Spec.Auth
|
||||
svcCfg = supabase.ServiceConfig.Auth
|
||||
namespacedClient = client.NewNamespacedClient(r.Client, core.Namespace)
|
||||
)
|
||||
|
||||
if authSpec.WorkloadTemplate == nil {
|
||||
authSpec.WorkloadTemplate = new(supabasev1alpha1.WorkloadTemplate)
|
||||
}
|
||||
|
||||
if authSpec.WorkloadTemplate.Workload == nil {
|
||||
authSpec.WorkloadTemplate.Workload = new(supabasev1alpha1.ContainerTemplate)
|
||||
}
|
||||
|
||||
var (
|
||||
image = supabase.Images.Gotrue.String()
|
||||
podSecurityContext = authSpec.WorkloadTemplate.SecurityContext
|
||||
pullPolicy = authSpec.WorkloadTemplate.Workload.PullPolicy
|
||||
containerSecurityContext = authSpec.WorkloadTemplate.Workload.SecurityContext
|
||||
namespacedClient = client.NewNamespacedClient(r.Client, core.Namespace)
|
||||
)
|
||||
|
||||
if img := authSpec.WorkloadTemplate.Workload.Image; img != "" {
|
||||
image = img
|
||||
}
|
||||
|
||||
if podSecurityContext == nil {
|
||||
podSecurityContext = &corev1.PodSecurityContext{
|
||||
RunAsNonRoot: ptrOf(true),
|
||||
}
|
||||
}
|
||||
|
||||
if containerSecurityContext == nil {
|
||||
containerSecurityContext = &corev1.SecurityContext{
|
||||
Privileged: ptrOf(false),
|
||||
RunAsUser: ptrOf(int64(1000)),
|
||||
RunAsGroup: ptrOf(int64(1000)),
|
||||
RunAsNonRoot: ptrOf(true),
|
||||
AllowPrivilegeEscalation: ptrOf(false),
|
||||
ReadOnlyRootFilesystem: ptrOf(true),
|
||||
Capabilities: &corev1.Capabilities{
|
||||
Drop: []corev1.Capability{
|
||||
"ALL",
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
databaseDSN, err := core.Spec.Database.GetDSN(ctx, namespacedClient)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -129,7 +88,7 @@ func (r *CoreAuthReconciler) reconcileAuthDeployment(
|
|||
}
|
||||
|
||||
_, err = controllerutil.CreateOrUpdate(ctx, r.Client, authDeployment, func() error {
|
||||
authDeployment.Labels = MergeLabels(
|
||||
authDeployment.Labels = authSpec.WorkloadTemplate.MergeLabels(
|
||||
objectLabels(core, "auth", "core", supabase.Images.Gotrue.Tag),
|
||||
core.Labels,
|
||||
)
|
||||
|
@ -153,24 +112,24 @@ func (r *CoreAuthReconciler) reconcileAuthDeployment(
|
|||
}
|
||||
|
||||
authEnv := append(authDbEnv,
|
||||
svcCfg.EnvKeys.ApiHost.Var(svcCfg.Defaults.ApiHost),
|
||||
svcCfg.EnvKeys.ApiPort.Var(svcCfg.Defaults.ApiPort),
|
||||
svcCfg.EnvKeys.ApiExternalUrl.Var(authSpec.APIExternalURL),
|
||||
svcCfg.EnvKeys.DBDriver.Var(svcCfg.Defaults.DbDriver),
|
||||
svcCfg.EnvKeys.SiteUrl.Var(authSpec.SiteURL),
|
||||
svcCfg.EnvKeys.ApiHost.Var(),
|
||||
svcCfg.EnvKeys.ApiPort.Var(),
|
||||
svcCfg.EnvKeys.ApiExternalUrl.Var(core.Spec.APIExternalURL),
|
||||
svcCfg.EnvKeys.DBDriver.Var(),
|
||||
svcCfg.EnvKeys.SiteUrl.Var(core.Spec.SiteURL),
|
||||
svcCfg.EnvKeys.AdditionalRedirectURLs.Var(authSpec.AdditionalRedirectUrls),
|
||||
svcCfg.EnvKeys.DisableSignup.Var(boolValueOf(authSpec.DisableSignup)),
|
||||
svcCfg.EnvKeys.JWTIssuer.Var(svcCfg.Defaults.JwtIssuer),
|
||||
svcCfg.EnvKeys.JWTAdminRoles.Var(svcCfg.Defaults.JwtAdminRoles),
|
||||
svcCfg.EnvKeys.JWTAudience.Var(svcCfg.Defaults.JwtAudience),
|
||||
svcCfg.EnvKeys.JwtDefaultGroup.Var(svcCfg.Defaults.JwtDefaultGroupName),
|
||||
svcCfg.EnvKeys.JWTIssuer.Var(),
|
||||
svcCfg.EnvKeys.JWTAdminRoles.Var(),
|
||||
svcCfg.EnvKeys.JWTAudience.Var(),
|
||||
svcCfg.EnvKeys.JwtDefaultGroup.Var(),
|
||||
svcCfg.EnvKeys.JwtExpiry.Var(ValueOrFallback(core.Spec.JWT.Expiry, supabase.ServiceConfig.JWT.Defaults.Expiry)),
|
||||
svcCfg.EnvKeys.JwtSecret.Var(core.Spec.JWT.SecretKeySelector()),
|
||||
svcCfg.EnvKeys.EmailSignupDisabled.Var(boolValueOf(authSpec.EmailSignupDisabled)),
|
||||
svcCfg.EnvKeys.AnonymousUsersEnabled.Var(boolValueOf(authSpec.AnonymousUsersEnabled)),
|
||||
)
|
||||
|
||||
authEnv = append(authEnv, authSpec.Providers.Vars(authSpec.APIExternalURL)...)
|
||||
authEnv = append(authEnv, authSpec.Providers.Vars(core.Spec.APIExternalURL)...)
|
||||
|
||||
if authDeployment.CreationTimestamp.IsZero() {
|
||||
authDeployment.Spec.Selector = &metav1.LabelSelector{
|
||||
|
@ -178,38 +137,38 @@ func (r *CoreAuthReconciler) reconcileAuthDeployment(
|
|||
}
|
||||
}
|
||||
|
||||
authDeployment.Spec.Replicas = authSpec.WorkloadTemplate.Replicas
|
||||
authDeployment.Spec.Replicas = authSpec.WorkloadTemplate.ReplicaCount()
|
||||
|
||||
authDeployment.Spec.Template = corev1.PodTemplateSpec{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Labels: objectLabels(core, "auth", "core", supabase.Images.Gotrue.Tag),
|
||||
},
|
||||
Spec: corev1.PodSpec{
|
||||
ImagePullSecrets: authSpec.WorkloadTemplate.Workload.ImagePullSecrets,
|
||||
ImagePullSecrets: authSpec.WorkloadTemplate.PullSecrets(),
|
||||
InitContainers: []corev1.Container{{
|
||||
Name: "migrations",
|
||||
Image: image,
|
||||
ImagePullPolicy: pullPolicy,
|
||||
Name: "supabase-auth-migrations",
|
||||
Image: authSpec.WorkloadTemplate.Image(supabase.Images.Gotrue.String()),
|
||||
ImagePullPolicy: authSpec.WorkloadTemplate.ImagePullPolicy(),
|
||||
Command: []string{"/usr/local/bin/auth"},
|
||||
Args: []string{"migrate"},
|
||||
Env: authEnv,
|
||||
SecurityContext: containerSecurityContext,
|
||||
Env: authSpec.WorkloadTemplate.MergeEnv(authEnv),
|
||||
SecurityContext: authSpec.WorkloadTemplate.ContainerSecurityContext(svcCfg.Defaults.UID, svcCfg.Defaults.GID),
|
||||
}},
|
||||
Containers: []corev1.Container{{
|
||||
Name: "supabase-auth",
|
||||
Image: image,
|
||||
ImagePullPolicy: pullPolicy,
|
||||
Image: authSpec.WorkloadTemplate.Image(supabase.Images.Gotrue.String()),
|
||||
ImagePullPolicy: authSpec.WorkloadTemplate.ImagePullPolicy(),
|
||||
Command: []string{"/usr/local/bin/auth"},
|
||||
Args: []string{"serve"},
|
||||
Env: MergeEnv(authEnv, authSpec.WorkloadTemplate.Workload.AdditionalEnv...),
|
||||
Env: authSpec.WorkloadTemplate.MergeEnv(authEnv),
|
||||
Ports: []corev1.ContainerPort{{
|
||||
Name: "api",
|
||||
ContainerPort: 9999,
|
||||
ContainerPort: svcCfg.Defaults.APIPort,
|
||||
Protocol: corev1.ProtocolTCP,
|
||||
}},
|
||||
SecurityContext: containerSecurityContext,
|
||||
Resources: authSpec.WorkloadTemplate.Workload.Resources,
|
||||
VolumeMounts: authSpec.WorkloadTemplate.Workload.VolumeMounts,
|
||||
SecurityContext: authSpec.WorkloadTemplate.ContainerSecurityContext(svcCfg.Defaults.UID, svcCfg.Defaults.GID),
|
||||
Resources: authSpec.WorkloadTemplate.Resources(),
|
||||
VolumeMounts: authSpec.WorkloadTemplate.AdditionalVolumeMounts(),
|
||||
ReadinessProbe: &corev1.Probe{
|
||||
InitialDelaySeconds: 5,
|
||||
PeriodSeconds: 3,
|
||||
|
@ -218,7 +177,7 @@ func (r *CoreAuthReconciler) reconcileAuthDeployment(
|
|||
ProbeHandler: corev1.ProbeHandler{
|
||||
HTTPGet: &corev1.HTTPGetAction{
|
||||
Path: "/health",
|
||||
Port: intstr.IntOrString{IntVal: 9999},
|
||||
Port: intstr.IntOrString{IntVal: svcCfg.Defaults.APIPort},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -229,12 +188,12 @@ func (r *CoreAuthReconciler) reconcileAuthDeployment(
|
|||
ProbeHandler: corev1.ProbeHandler{
|
||||
HTTPGet: &corev1.HTTPGetAction{
|
||||
Path: "/health",
|
||||
Port: intstr.IntOrString{IntVal: 9999},
|
||||
Port: intstr.IntOrString{IntVal: svcCfg.Defaults.APIPort},
|
||||
},
|
||||
},
|
||||
},
|
||||
}},
|
||||
SecurityContext: podSecurityContext,
|
||||
SecurityContext: authSpec.WorkloadTemplate.PodSecurityContext(),
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -257,12 +216,14 @@ func (r *CoreAuthReconciler) reconcileAuthService(
|
|||
}
|
||||
|
||||
_, err := controllerutil.CreateOrUpdate(ctx, r.Client, authService, func() error {
|
||||
authService.Labels = MergeLabels(
|
||||
authService.Labels = core.Spec.Postgrest.WorkloadTemplate.MergeLabels(
|
||||
objectLabels(core, "auth", "core", supabase.Images.Gotrue.Tag),
|
||||
core.Labels,
|
||||
)
|
||||
|
||||
authService.Labels[meta.SupabaseLabel.EnvoyCluster] = core.Name
|
||||
if _, ok := authService.Labels[meta.SupabaseLabel.EnvoyCluster]; !ok {
|
||||
authService.Labels[meta.SupabaseLabel.EnvoyCluster] = core.Name
|
||||
}
|
||||
|
||||
authService.Spec = corev1.ServiceSpec{
|
||||
Selector: selectorLabels(core, "auth"),
|
||||
|
|
|
@ -78,51 +78,13 @@ func (r *CorePostgrestReconiler) reconilePostgrestDeployment(
|
|||
postgrestSpec = core.Spec.Postgrest
|
||||
)
|
||||
|
||||
if postgrestSpec.WorkloadTemplate == nil {
|
||||
postgrestSpec.WorkloadTemplate = new(supabasev1alpha1.WorkloadTemplate)
|
||||
}
|
||||
|
||||
if postgrestSpec.WorkloadTemplate.Workload == nil {
|
||||
postgrestSpec.WorkloadTemplate.Workload = new(supabasev1alpha1.ContainerTemplate)
|
||||
}
|
||||
|
||||
var (
|
||||
image = supabase.Images.Postgrest.String()
|
||||
podSecurityContext = postgrestSpec.WorkloadTemplate.SecurityContext
|
||||
pullPolicy = postgrestSpec.WorkloadTemplate.Workload.PullPolicy
|
||||
containerSecurityContext = postgrestSpec.WorkloadTemplate.Workload.SecurityContext
|
||||
anonRole = ValueOrFallback(postgrestSpec.AnonRole, serviceCfg.Defaults.AnonRole)
|
||||
postgrestSchemas = ValueOrFallback(postgrestSpec.Schemas, serviceCfg.Defaults.Schemas)
|
||||
jwtSecretHash string
|
||||
namespacedClient = client.NewNamespacedClient(r.Client, core.Namespace)
|
||||
anonRole = ValueOrFallback(postgrestSpec.AnonRole, serviceCfg.Defaults.AnonRole)
|
||||
postgrestSchemas = ValueOrFallback(postgrestSpec.Schemas, serviceCfg.Defaults.Schemas)
|
||||
jwtSecretHash string
|
||||
namespacedClient = client.NewNamespacedClient(r.Client, core.Namespace)
|
||||
)
|
||||
|
||||
if img := postgrestSpec.WorkloadTemplate.Workload.Image; img != "" {
|
||||
image = img
|
||||
}
|
||||
|
||||
if podSecurityContext == nil {
|
||||
podSecurityContext = &corev1.PodSecurityContext{
|
||||
RunAsNonRoot: ptrOf(true),
|
||||
}
|
||||
}
|
||||
|
||||
if containerSecurityContext == nil {
|
||||
containerSecurityContext = &corev1.SecurityContext{
|
||||
Privileged: ptrOf(false),
|
||||
RunAsUser: ptrOf(int64(1000)),
|
||||
RunAsGroup: ptrOf(int64(1000)),
|
||||
RunAsNonRoot: ptrOf(true),
|
||||
AllowPrivilegeEscalation: ptrOf(false),
|
||||
ReadOnlyRootFilesystem: ptrOf(true),
|
||||
Capabilities: &corev1.Capabilities{
|
||||
Drop: []corev1.Capability{
|
||||
"ALL",
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
databaseDSN, err := core.Spec.Database.GetDSN(ctx, namespacedClient)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -140,7 +102,7 @@ func (r *CorePostgrestReconiler) reconilePostgrestDeployment(
|
|||
}
|
||||
|
||||
_, err = controllerutil.CreateOrUpdate(ctx, r.Client, postgrestDeployment, func() error {
|
||||
postgrestDeployment.Labels = MergeLabels(
|
||||
postgrestDeployment.Labels = postgrestSpec.WorkloadTemplate.MergeLabels(
|
||||
objectLabels(core, serviceCfg.Name, "core", supabase.Images.Postgrest.Tag),
|
||||
core.Labels,
|
||||
)
|
||||
|
@ -161,6 +123,7 @@ func (r *CorePostgrestReconiler) reconilePostgrestDeployment(
|
|||
Name: serviceCfg.EnvKeys.DBUri,
|
||||
Value: strings.TrimSuffix(fmt.Sprintf("postgres://%s:$(DB_CREDENTIALS_PASSWORD)@%s%s?%s", supabase.DBRoleAuthenticator, parsedDSN.Host, parsedDSN.Path, parsedDSN.Query().Encode()), "?"),
|
||||
},
|
||||
serviceCfg.EnvKeys.Host.Var(),
|
||||
serviceCfg.EnvKeys.JWTSecret.Var(core.Spec.JWT.JwksKeySelector()),
|
||||
serviceCfg.EnvKeys.Schemas.Var(postgrestSchemas),
|
||||
serviceCfg.EnvKeys.AnonRole.Var(anonRole),
|
||||
|
@ -168,7 +131,9 @@ func (r *CorePostgrestReconiler) reconilePostgrestDeployment(
|
|||
serviceCfg.EnvKeys.ExtraSearchPath.Var(serviceCfg.Defaults.ExtraSearchPath),
|
||||
serviceCfg.EnvKeys.AppSettingsJWTSecret.Var(core.Spec.JWT.SecretKeySelector()),
|
||||
serviceCfg.EnvKeys.AppSettingsJWTExpiry.Var(ValueOrFallback(core.Spec.JWT.Expiry, supabase.ServiceConfig.JWT.Defaults.Expiry)),
|
||||
serviceCfg.EnvKeys.AdminServerPort.Var(3001),
|
||||
serviceCfg.EnvKeys.AdminServerPort.Var((serviceCfg.Defaults.AdminPort)),
|
||||
serviceCfg.EnvKeys.MaxRows.Var(postgrestSpec.MaxRows),
|
||||
serviceCfg.EnvKeys.OpenAPIProxyURI.Var(fmt.Sprintf("%s/rest/v1", strings.TrimSuffix(core.Spec.APIExternalURL, "/"))),
|
||||
}
|
||||
|
||||
if postgrestDeployment.CreationTimestamp.IsZero() {
|
||||
|
@ -177,7 +142,7 @@ func (r *CorePostgrestReconiler) reconilePostgrestDeployment(
|
|||
}
|
||||
}
|
||||
|
||||
postgrestDeployment.Spec.Replicas = postgrestSpec.WorkloadTemplate.Replicas
|
||||
postgrestDeployment.Spec.Replicas = postgrestSpec.WorkloadTemplate.ReplicaCount()
|
||||
|
||||
postgrestDeployment.Spec.Template = corev1.PodTemplateSpec{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
|
@ -187,29 +152,29 @@ func (r *CorePostgrestReconiler) reconilePostgrestDeployment(
|
|||
Labels: objectLabels(core, serviceCfg.Name, "core", supabase.Images.Postgrest.Tag),
|
||||
},
|
||||
Spec: corev1.PodSpec{
|
||||
ImagePullSecrets: postgrestSpec.WorkloadTemplate.Workload.ImagePullSecrets,
|
||||
ImagePullSecrets: postgrestSpec.WorkloadTemplate.PullSecrets(),
|
||||
Containers: []corev1.Container{
|
||||
{
|
||||
Name: "supabase-rest",
|
||||
Image: image,
|
||||
ImagePullPolicy: pullPolicy,
|
||||
Image: postgrestSpec.WorkloadTemplate.Image(supabase.Images.Postgrest.String()),
|
||||
ImagePullPolicy: postgrestSpec.WorkloadTemplate.ImagePullPolicy(),
|
||||
Args: []string{"postgrest"},
|
||||
Env: MergeEnv(postgrestEnv, postgrestSpec.WorkloadTemplate.Workload.AdditionalEnv...),
|
||||
Env: postgrestSpec.WorkloadTemplate.MergeEnv(postgrestEnv),
|
||||
Ports: []corev1.ContainerPort{
|
||||
{
|
||||
Name: "rest",
|
||||
ContainerPort: 3000,
|
||||
ContainerPort: serviceCfg.Defaults.ServerPort,
|
||||
Protocol: corev1.ProtocolTCP,
|
||||
},
|
||||
{
|
||||
Name: "admin",
|
||||
ContainerPort: 3001,
|
||||
ContainerPort: serviceCfg.Defaults.AdminPort,
|
||||
Protocol: corev1.ProtocolTCP,
|
||||
},
|
||||
},
|
||||
SecurityContext: containerSecurityContext,
|
||||
Resources: postgrestSpec.WorkloadTemplate.Workload.Resources,
|
||||
VolumeMounts: postgrestSpec.WorkloadTemplate.Workload.VolumeMounts,
|
||||
SecurityContext: postgrestSpec.WorkloadTemplate.ContainerSecurityContext(serviceCfg.Defaults.UID, serviceCfg.Defaults.GID),
|
||||
Resources: postgrestSpec.WorkloadTemplate.Resources(),
|
||||
VolumeMounts: postgrestSpec.WorkloadTemplate.AdditionalVolumeMounts(),
|
||||
ReadinessProbe: &corev1.Probe{
|
||||
InitialDelaySeconds: 5,
|
||||
PeriodSeconds: 3,
|
||||
|
@ -218,7 +183,7 @@ func (r *CorePostgrestReconiler) reconilePostgrestDeployment(
|
|||
ProbeHandler: corev1.ProbeHandler{
|
||||
HTTPGet: &corev1.HTTPGetAction{
|
||||
Path: "/ready",
|
||||
Port: intstr.IntOrString{IntVal: 3001},
|
||||
Port: intstr.IntOrString{IntVal: serviceCfg.Defaults.AdminPort},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -229,13 +194,13 @@ func (r *CorePostgrestReconiler) reconilePostgrestDeployment(
|
|||
ProbeHandler: corev1.ProbeHandler{
|
||||
HTTPGet: &corev1.HTTPGetAction{
|
||||
Path: "/live",
|
||||
Port: intstr.IntOrString{IntVal: 3001},
|
||||
Port: intstr.IntOrString{IntVal: serviceCfg.Defaults.AdminPort},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
SecurityContext: podSecurityContext,
|
||||
SecurityContext: postgrestSpec.WorkloadTemplate.PodSecurityContext(),
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -258,12 +223,14 @@ func (r *CorePostgrestReconiler) reconcilePostgrestService(
|
|||
}
|
||||
|
||||
_, err := controllerutil.CreateOrUpdate(ctx, r.Client, postgrestService, func() error {
|
||||
postgrestService.Labels = MergeLabels(
|
||||
postgrestService.Labels = core.Spec.Postgrest.WorkloadTemplate.MergeLabels(
|
||||
objectLabels(core, supabase.ServiceConfig.Postgrest.Name, "core", supabase.Images.Postgrest.Tag),
|
||||
core.Labels,
|
||||
)
|
||||
|
||||
postgrestService.Labels[meta.SupabaseLabel.EnvoyCluster] = core.Name
|
||||
if _, ok := postgrestService.Labels[meta.SupabaseLabel.EnvoyCluster]; !ok {
|
||||
postgrestService.Labels[meta.SupabaseLabel.EnvoyCluster] = core.Name
|
||||
}
|
||||
|
||||
postgrestService.Spec = corev1.ServiceSpec{
|
||||
Selector: selectorLabels(core, supabase.ServiceConfig.Postgrest.Name),
|
||||
|
|
|
@ -40,19 +40,6 @@ type DashboardPGMetaReconciler struct {
|
|||
Scheme *runtime.Scheme
|
||||
}
|
||||
|
||||
// +kubebuilder:rbac:groups=supabase.k8s.icb4dc0.de,resources=dashboards,verbs=get;list;watch;create;update;patch;delete
|
||||
// +kubebuilder:rbac:groups=supabase.k8s.icb4dc0.de,resources=dashboards/status,verbs=get;update;patch
|
||||
// +kubebuilder:rbac:groups=supabase.k8s.icb4dc0.de,resources=dashboards/finalizers,verbs=update
|
||||
|
||||
// Reconcile is part of the main kubernetes reconciliation loop which aims to
|
||||
// move the current state of the cluster closer to the desired state.
|
||||
// TODO(user): Modify the Reconcile function to compare the state specified by
|
||||
// the Dashboard object against the actual cluster state, and then
|
||||
// perform operations to make the cluster state reflect the state specified by
|
||||
// the user.
|
||||
//
|
||||
// For more details, check Reconcile and its Result here:
|
||||
// - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.19.1/pkg/reconcile
|
||||
func (r *DashboardPGMetaReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
|
||||
var (
|
||||
dashboard supabasev1alpha1.Dashboard
|
||||
|
@ -101,47 +88,6 @@ func (r *DashboardPGMetaReconciler) reconcilePGMetaDeployment(
|
|||
pgMetaSpec = new(supabasev1alpha1.PGMetaSpec)
|
||||
}
|
||||
|
||||
if pgMetaSpec.WorkloadTemplate == nil {
|
||||
pgMetaSpec.WorkloadTemplate = new(supabasev1alpha1.WorkloadTemplate)
|
||||
}
|
||||
|
||||
if pgMetaSpec.WorkloadTemplate.Workload == nil {
|
||||
pgMetaSpec.WorkloadTemplate.Workload = new(supabasev1alpha1.ContainerTemplate)
|
||||
}
|
||||
|
||||
var (
|
||||
image = supabase.Images.PostgresMeta.String()
|
||||
podSecurityContext = pgMetaSpec.WorkloadTemplate.SecurityContext
|
||||
pullPolicy = pgMetaSpec.WorkloadTemplate.Workload.PullPolicy
|
||||
containerSecurityContext = pgMetaSpec.WorkloadTemplate.Workload.SecurityContext
|
||||
)
|
||||
|
||||
if img := pgMetaSpec.WorkloadTemplate.Workload.Image; img != "" {
|
||||
image = img
|
||||
}
|
||||
|
||||
if podSecurityContext == nil {
|
||||
podSecurityContext = &corev1.PodSecurityContext{
|
||||
RunAsNonRoot: ptrOf(true),
|
||||
}
|
||||
}
|
||||
|
||||
if containerSecurityContext == nil {
|
||||
containerSecurityContext = &corev1.SecurityContext{
|
||||
Privileged: ptrOf(false),
|
||||
RunAsUser: ptrOf(int64(1000)),
|
||||
RunAsGroup: ptrOf(int64(1000)),
|
||||
RunAsNonRoot: ptrOf(true),
|
||||
AllowPrivilegeEscalation: ptrOf(false),
|
||||
ReadOnlyRootFilesystem: ptrOf(true),
|
||||
Capabilities: &corev1.Capabilities{
|
||||
Drop: []corev1.Capability{
|
||||
"ALL",
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
dsnSecret := &corev1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: dashboard.Spec.DBSpec.DBCredentialsRef.Name,
|
||||
|
@ -153,7 +99,7 @@ func (r *DashboardPGMetaReconciler) reconcilePGMetaDeployment(
|
|||
}
|
||||
|
||||
_, err := controllerutil.CreateOrUpdate(ctx, r.Client, pgMetaDeployment, func() error {
|
||||
pgMetaDeployment.Labels = MergeLabels(
|
||||
pgMetaDeployment.Labels = pgMetaSpec.WorkloadTemplate.MergeLabels(
|
||||
objectLabels(dashboard, serviceCfg.Name, "dashboard", supabase.Images.PostgresMeta.Tag),
|
||||
dashboard.Labels,
|
||||
)
|
||||
|
@ -164,11 +110,12 @@ func (r *DashboardPGMetaReconciler) reconcilePGMetaDeployment(
|
|||
}
|
||||
}
|
||||
|
||||
pgMetaDeployment.Spec.Replicas = pgMetaSpec.WorkloadTemplate.Replicas
|
||||
pgMetaDeployment.Spec.Replicas = pgMetaSpec.WorkloadTemplate.ReplicaCount()
|
||||
|
||||
pgMetaEnv := []corev1.EnvVar{
|
||||
serviceCfg.EnvKeys.APIPort.Var(serviceCfg.Defaults.APIPort),
|
||||
serviceCfg.EnvKeys.DBHost.Var(dashboard.Spec.DBSpec.Host),
|
||||
serviceCfg.EnvKeys.DBName.Var(dashboard.Spec.DBSpec.DBName),
|
||||
serviceCfg.EnvKeys.DBPort.Var(dashboard.Spec.DBSpec.Port),
|
||||
serviceCfg.EnvKeys.DBUser.Var(dashboard.Spec.DBSpec.UserRef()),
|
||||
serviceCfg.EnvKeys.DBPassword.Var(dashboard.Spec.DBSpec.PasswordRef()),
|
||||
|
@ -179,20 +126,20 @@ func (r *DashboardPGMetaReconciler) reconcilePGMetaDeployment(
|
|||
Labels: objectLabels(dashboard, serviceCfg.Name, "dashboard", supabase.Images.PostgresMeta.Tag),
|
||||
},
|
||||
Spec: corev1.PodSpec{
|
||||
ImagePullSecrets: pgMetaSpec.WorkloadTemplate.Workload.ImagePullSecrets,
|
||||
ImagePullSecrets: pgMetaSpec.WorkloadTemplate.PullSecrets(),
|
||||
Containers: []corev1.Container{{
|
||||
Name: "supabase-meta",
|
||||
Image: image,
|
||||
ImagePullPolicy: pullPolicy,
|
||||
Env: MergeEnv(pgMetaEnv, pgMetaSpec.WorkloadTemplate.Workload.AdditionalEnv...),
|
||||
Image: pgMetaSpec.WorkloadTemplate.Image(supabase.Images.PostgresMeta.String()),
|
||||
ImagePullPolicy: pgMetaSpec.WorkloadTemplate.ImagePullPolicy(),
|
||||
Env: pgMetaSpec.WorkloadTemplate.MergeEnv(pgMetaEnv),
|
||||
Ports: []corev1.ContainerPort{{
|
||||
Name: "api",
|
||||
ContainerPort: int32(serviceCfg.Defaults.APIPort),
|
||||
ContainerPort: serviceCfg.Defaults.APIPort,
|
||||
Protocol: corev1.ProtocolTCP,
|
||||
}},
|
||||
SecurityContext: containerSecurityContext,
|
||||
Resources: pgMetaSpec.WorkloadTemplate.Workload.Resources,
|
||||
VolumeMounts: pgMetaSpec.WorkloadTemplate.Workload.VolumeMounts,
|
||||
SecurityContext: pgMetaSpec.WorkloadTemplate.ContainerSecurityContext(serviceCfg.Defaults.NodeUID, serviceCfg.Defaults.NodeGID),
|
||||
Resources: pgMetaSpec.WorkloadTemplate.Resources(),
|
||||
VolumeMounts: pgMetaSpec.WorkloadTemplate.AdditionalVolumeMounts(),
|
||||
ReadinessProbe: &corev1.Probe{
|
||||
InitialDelaySeconds: 5,
|
||||
PeriodSeconds: 3,
|
||||
|
@ -201,7 +148,7 @@ func (r *DashboardPGMetaReconciler) reconcilePGMetaDeployment(
|
|||
ProbeHandler: corev1.ProbeHandler{
|
||||
HTTPGet: &corev1.HTTPGetAction{
|
||||
Path: "/health",
|
||||
Port: intstr.IntOrString{IntVal: int32(serviceCfg.Defaults.APIPort)},
|
||||
Port: intstr.IntOrString{IntVal: serviceCfg.Defaults.APIPort},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -212,12 +159,12 @@ func (r *DashboardPGMetaReconciler) reconcilePGMetaDeployment(
|
|||
ProbeHandler: corev1.ProbeHandler{
|
||||
HTTPGet: &corev1.HTTPGetAction{
|
||||
Path: "/health",
|
||||
Port: intstr.IntOrString{IntVal: int32(serviceCfg.Defaults.APIPort)},
|
||||
Port: intstr.IntOrString{IntVal: serviceCfg.Defaults.APIPort},
|
||||
},
|
||||
},
|
||||
},
|
||||
}},
|
||||
SecurityContext: podSecurityContext,
|
||||
SecurityContext: pgMetaSpec.WorkloadTemplate.PodSecurityContext(),
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -239,15 +186,19 @@ func (r *DashboardPGMetaReconciler) reconcilePGMetaService(
|
|||
ObjectMeta: supabase.ServiceConfig.PGMeta.ObjectMeta(dashboard),
|
||||
}
|
||||
|
||||
if dashboard.Spec.PGMeta == nil {
|
||||
dashboard.Spec.PGMeta = new(supabasev1alpha1.PGMetaSpec)
|
||||
}
|
||||
|
||||
_, err := controllerutil.CreateOrPatch(ctx, r.Client, pgMetaService, func() error {
|
||||
pgMetaService.Labels = MergeLabels(
|
||||
pgMetaService.Labels = dashboard.Spec.PGMeta.WorkloadTemplate.MergeLabels(
|
||||
objectLabels(dashboard, supabase.ServiceConfig.PGMeta.Name, "dashboard", supabase.Images.PostgresMeta.Tag),
|
||||
dashboard.Labels,
|
||||
)
|
||||
|
||||
pgMetaService.Labels[meta.SupabaseLabel.EnvoyCluster] = dashboard.Name
|
||||
|
||||
apiPort := int32(supabase.ServiceConfig.PGMeta.Defaults.APIPort)
|
||||
if _, ok := pgMetaService.Labels[meta.SupabaseLabel.EnvoyCluster]; !ok {
|
||||
pgMetaService.Labels[meta.SupabaseLabel.EnvoyCluster] = dashboard.Name
|
||||
}
|
||||
|
||||
pgMetaService.Spec = corev1.ServiceSpec{
|
||||
Selector: selectorLabels(dashboard, supabase.ServiceConfig.PGMeta.Name),
|
||||
|
@ -256,8 +207,8 @@ func (r *DashboardPGMetaReconciler) reconcilePGMetaService(
|
|||
Name: "api",
|
||||
Protocol: corev1.ProtocolTCP,
|
||||
AppProtocol: ptrOf("http"),
|
||||
Port: apiPort,
|
||||
TargetPort: intstr.IntOrString{IntVal: apiPort},
|
||||
Port: supabase.ServiceConfig.PGMeta.Defaults.APIPort,
|
||||
TargetPort: intstr.IntOrString{IntVal: supabase.ServiceConfig.PGMeta.Defaults.APIPort},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
|
246
internal/controller/dashboard_studio_controller.go
Normal file
246
internal/controller/dashboard_studio_controller.go
Normal file
|
@ -0,0 +1,246 @@
|
|||
/*
|
||||
Copyright 2024 Peter Kurfer.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package controller
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
appsv1 "k8s.io/api/apps/v1"
|
||||
corev1 "k8s.io/api/core/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"
|
||||
ctrl "sigs.k8s.io/controller-runtime"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
|
||||
"sigs.k8s.io/controller-runtime/pkg/log"
|
||||
|
||||
supabasev1alpha1 "code.icb4dc0.de/prskr/supabase-operator/api/v1alpha1"
|
||||
"code.icb4dc0.de/prskr/supabase-operator/internal/meta"
|
||||
"code.icb4dc0.de/prskr/supabase-operator/internal/supabase"
|
||||
)
|
||||
|
||||
type DashboardStudioReconciler struct {
|
||||
client.Client
|
||||
Scheme *runtime.Scheme
|
||||
}
|
||||
|
||||
func (r *DashboardStudioReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
|
||||
var (
|
||||
dashboard supabasev1alpha1.Dashboard
|
||||
logger = log.FromContext(ctx)
|
||||
)
|
||||
|
||||
if err := r.Get(ctx, req.NamespacedName, &dashboard); client.IgnoreNotFound(err) != nil {
|
||||
logger.Error(err, "unable to fetch Dashboard")
|
||||
return ctrl.Result{}, err
|
||||
}
|
||||
|
||||
if err := r.reconcileStudioDeployment(ctx, &dashboard); err != nil {
|
||||
return ctrl.Result{}, err
|
||||
}
|
||||
|
||||
if err := r.reconcileStudioService(ctx, &dashboard); err != nil {
|
||||
return ctrl.Result{}, err
|
||||
}
|
||||
|
||||
return ctrl.Result{}, nil
|
||||
}
|
||||
|
||||
// SetupWithManager sets up the controller with the Manager.
|
||||
func (r *DashboardStudioReconciler) SetupWithManager(mgr ctrl.Manager) error {
|
||||
return ctrl.NewControllerManagedBy(mgr).
|
||||
For(&supabasev1alpha1.Dashboard{}).
|
||||
Owns(new(appsv1.Deployment)).
|
||||
Owns(new(corev1.Service)).
|
||||
Named("dashboard-studio").
|
||||
Complete(r)
|
||||
}
|
||||
|
||||
func (r *DashboardStudioReconciler) reconcileStudioDeployment(
|
||||
ctx context.Context,
|
||||
dashboard *supabasev1alpha1.Dashboard,
|
||||
) error {
|
||||
var (
|
||||
serviceCfg = supabase.ServiceConfig.Studio
|
||||
studioDeployment = &appsv1.Deployment{
|
||||
ObjectMeta: serviceCfg.ObjectMeta(dashboard),
|
||||
}
|
||||
studioSpec = dashboard.Spec.Studio
|
||||
gatewayServiceList corev1.ServiceList
|
||||
)
|
||||
|
||||
if studioSpec == nil {
|
||||
studioSpec = new(supabasev1alpha1.StudioSpec)
|
||||
}
|
||||
|
||||
err := r.List(
|
||||
ctx,
|
||||
&gatewayServiceList,
|
||||
client.InNamespace(dashboard.Namespace),
|
||||
client.MatchingLabels(studioSpec.GatewayServiceMatchLabels),
|
||||
)
|
||||
if err != nil {
|
||||
return fmt.Errorf("selecting gateway service: %w", err)
|
||||
}
|
||||
|
||||
if itemCount := len(gatewayServiceList.Items); itemCount != 1 {
|
||||
return fmt.Errorf("unexpected matches for gateway service: %d", itemCount)
|
||||
}
|
||||
|
||||
gatewayService := gatewayServiceList.Items[0]
|
||||
|
||||
_, err = controllerutil.CreateOrUpdate(ctx, r.Client, studioDeployment, func() error {
|
||||
studioDeployment.Labels = studioSpec.WorkloadTemplate.MergeLabels(
|
||||
objectLabels(dashboard, serviceCfg.Name, "dashboard", supabase.Images.Studio.Tag),
|
||||
dashboard.Labels,
|
||||
)
|
||||
|
||||
if studioDeployment.CreationTimestamp.IsZero() {
|
||||
studioDeployment.Spec.Selector = &metav1.LabelSelector{
|
||||
MatchLabels: selectorLabels(dashboard, serviceCfg.Name),
|
||||
}
|
||||
}
|
||||
|
||||
studioDeployment.Spec.Replicas = studioSpec.WorkloadTemplate.ReplicaCount()
|
||||
|
||||
studioEnv := []corev1.EnvVar{
|
||||
serviceCfg.EnvKeys.PGMetaURL.Var(fmt.Sprintf("http://%s.%s.svc:%d", supabase.ServiceConfig.PGMeta.ObjectName(dashboard), dashboard.Namespace, supabase.ServiceConfig.PGMeta.Defaults.APIPort)),
|
||||
serviceCfg.EnvKeys.Host.Var(),
|
||||
serviceCfg.EnvKeys.ApiUrl.Var(fmt.Sprintf("http://%s.%s.svc:8000", gatewayService.Name, gatewayService.Namespace)),
|
||||
serviceCfg.EnvKeys.DBPassword.Var(dashboard.Spec.DBSpec.PasswordRef()),
|
||||
serviceCfg.EnvKeys.APIExternalURL.Var(studioSpec.APIExternalURL),
|
||||
serviceCfg.EnvKeys.JwtSecret.Var(studioSpec.JWT.SecretKeySelector()),
|
||||
serviceCfg.EnvKeys.AnonKey.Var(studioSpec.JWT.AnonKeySelector()),
|
||||
serviceCfg.EnvKeys.ServiceKey.Var(studioSpec.JWT.ServiceKeySelector()),
|
||||
}
|
||||
|
||||
studioDeployment.Spec.Template = corev1.PodTemplateSpec{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Labels: objectLabels(dashboard, serviceCfg.Name, "dashboard", supabase.Images.Studio.Tag),
|
||||
},
|
||||
Spec: corev1.PodSpec{
|
||||
ImagePullSecrets: studioSpec.WorkloadTemplate.PullSecrets(),
|
||||
Containers: []corev1.Container{{
|
||||
Name: "supabase-studio",
|
||||
Image: studioSpec.WorkloadTemplate.Image(supabase.Images.Studio.String()),
|
||||
ImagePullPolicy: studioSpec.WorkloadTemplate.ImagePullPolicy(),
|
||||
Env: studioSpec.WorkloadTemplate.MergeEnv(studioEnv),
|
||||
Ports: []corev1.ContainerPort{{
|
||||
Name: "studio",
|
||||
ContainerPort: serviceCfg.Defaults.APIPort,
|
||||
Protocol: corev1.ProtocolTCP,
|
||||
}},
|
||||
SecurityContext: studioSpec.WorkloadTemplate.ContainerSecurityContext(serviceCfg.Defaults.NodeUID, serviceCfg.Defaults.NodeGID),
|
||||
Resources: studioSpec.WorkloadTemplate.Resources(),
|
||||
VolumeMounts: studioSpec.WorkloadTemplate.AdditionalVolumeMounts(corev1.VolumeMount{
|
||||
Name: "next-cache",
|
||||
MountPath: "/app/apps/studio/.next/cache",
|
||||
}),
|
||||
ReadinessProbe: &corev1.Probe{
|
||||
InitialDelaySeconds: 5,
|
||||
PeriodSeconds: 3,
|
||||
TimeoutSeconds: 1,
|
||||
SuccessThreshold: 2,
|
||||
ProbeHandler: corev1.ProbeHandler{
|
||||
HTTPGet: &corev1.HTTPGetAction{
|
||||
Path: "/api/profile",
|
||||
Port: intstr.IntOrString{IntVal: serviceCfg.Defaults.APIPort},
|
||||
},
|
||||
},
|
||||
},
|
||||
LivenessProbe: &corev1.Probe{
|
||||
InitialDelaySeconds: 10,
|
||||
PeriodSeconds: 5,
|
||||
TimeoutSeconds: 3,
|
||||
ProbeHandler: corev1.ProbeHandler{
|
||||
HTTPGet: &corev1.HTTPGetAction{
|
||||
Path: "/api/profile",
|
||||
Port: intstr.IntOrString{IntVal: serviceCfg.Defaults.APIPort},
|
||||
},
|
||||
},
|
||||
},
|
||||
}},
|
||||
SecurityContext: studioSpec.WorkloadTemplate.PodSecurityContext(),
|
||||
Volumes: []corev1.Volume{{
|
||||
Name: "next-cache",
|
||||
VolumeSource: corev1.VolumeSource{
|
||||
EmptyDir: &corev1.EmptyDirVolumeSource{
|
||||
Medium: corev1.StorageMediumDefault,
|
||||
SizeLimit: ptrOf(resource.MustParse("300Mi")),
|
||||
},
|
||||
},
|
||||
}},
|
||||
},
|
||||
}
|
||||
|
||||
if err := controllerutil.SetControllerReference(dashboard, studioDeployment, r.Scheme); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (r *DashboardStudioReconciler) reconcileStudioService(
|
||||
ctx context.Context,
|
||||
dashboard *supabasev1alpha1.Dashboard,
|
||||
) error {
|
||||
studioService := &corev1.Service{
|
||||
ObjectMeta: supabase.ServiceConfig.Studio.ObjectMeta(dashboard),
|
||||
}
|
||||
|
||||
if dashboard.Spec.Studio == nil {
|
||||
dashboard.Spec.Studio = new(supabasev1alpha1.StudioSpec)
|
||||
}
|
||||
|
||||
_, err := controllerutil.CreateOrPatch(ctx, r.Client, studioService, func() error {
|
||||
studioService.Labels = dashboard.Spec.Studio.WorkloadTemplate.MergeLabels(
|
||||
objectLabels(dashboard, supabase.ServiceConfig.Studio.Name, "dashboard", supabase.Images.Studio.Tag),
|
||||
dashboard.Labels,
|
||||
)
|
||||
|
||||
if _, ok := studioService.Labels[meta.SupabaseLabel.EnvoyCluster]; !ok {
|
||||
studioService.Labels[meta.SupabaseLabel.EnvoyCluster] = dashboard.Name
|
||||
}
|
||||
|
||||
studioService.Spec = corev1.ServiceSpec{
|
||||
Selector: selectorLabels(dashboard, supabase.ServiceConfig.Studio.Name),
|
||||
Ports: []corev1.ServicePort{
|
||||
{
|
||||
Name: "studio",
|
||||
Protocol: corev1.ProtocolTCP,
|
||||
AppProtocol: ptrOf("http"),
|
||||
Port: supabase.ServiceConfig.Studio.Defaults.APIPort,
|
||||
TargetPort: intstr.IntOrString{IntVal: supabase.ServiceConfig.Studio.Defaults.APIPort},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
if err := controllerutil.SetControllerReference(dashboard, studioService, r.Scheme); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
return err
|
||||
}
|
|
@ -6,6 +6,9 @@ package controller
|
|||
// +kubebuilder:rbac:groups=supabase.k8s.icb4dc0.de,resources=apigateways,verbs=get;list;watch;create;update;patch;delete
|
||||
// +kubebuilder:rbac:groups=supabase.k8s.icb4dc0.de,resources=apigateways/status,verbs=get;update;patch
|
||||
// +kubebuilder:rbac:groups=supabase.k8s.icb4dc0.de,resources=apigateways/finalizers,verbs=update
|
||||
// +kubebuilder:rbac:groups=supabase.k8s.icb4dc0.de,resources=dashboards,verbs=get;list;watch;create;update;patch;delete
|
||||
// +kubebuilder:rbac:groups=supabase.k8s.icb4dc0.de,resources=dashboards/status,verbs=get;update;patch
|
||||
// +kubebuilder:rbac:groups=supabase.k8s.icb4dc0.de,resources=dashboards/finalizers,verbs=update
|
||||
// +kubebuilder:rbac:groups=apps,resources=deployments,verbs=get;list;watch;create;update;patch;delete
|
||||
// +kubebuilder:rbac:groups="",resources=secrets;configmaps;services,verbs=get;list;watch;create;update;patch;delete
|
||||
// +kubebuilder:rbac:groups="",resources=events,verbs=create
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue