Jelajahi Sumber

feat(template): add PostgreSQL template

Thomas Zhang 3 bulan lalu
induk
melakukan
bbecc2a431

+ 2 - 0
internal/controller/component_controller.go

@@ -165,6 +165,8 @@ func (r *ComponentReconciler) getComponentReconciler(comp *v1alpha1.Component, s
 		return reconciler.NewQdrantReconciler(r.Client, r.Scheme, stack, comp)
 	case "neo4j":
 		return reconciler.NewNeo4jReconciler(r.Client, r.Scheme, stack, comp)
+	case "postgresql":
+		return reconciler.NewPostgreSQLReconciler(r.Client, r.Scheme, stack, comp)
 	default:
 		return reconciler.NewDefaultComponentReconciler(r.Client, r.Scheme, stack, comp)
 	}

+ 72 - 0
internal/reconciler/postgresql.go

@@ -0,0 +1,72 @@
+/*
+Copyright 2026 LocoStack.
+
+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 reconciler
+
+import (
+	"context"
+	"crypto/rand"
+	"encoding/hex"
+	"fmt"
+
+	"github.com/LocoStack/loco-operator/api/v1alpha1"
+	"k8s.io/apimachinery/pkg/runtime"
+	"sigs.k8s.io/controller-runtime/pkg/client"
+)
+
+const (
+	POSTGRES_DEFAULT_STORAGE_SIZE = "1Gi"
+	POSTGRES_PASSWORD_SECRET_KEY  = "password"
+)
+
+type PostgreSQLReconciler struct {
+	*DefaultComponentReconciler
+	client    client.Client
+	scheme    *runtime.Scheme
+	stack     *v1alpha1.Stack
+	component *v1alpha1.Component
+}
+
+func NewPostgreSQLReconciler(client client.Client, scheme *runtime.Scheme, stack *v1alpha1.Stack, component *v1alpha1.Component) *PostgreSQLReconciler {
+	return &PostgreSQLReconciler{
+		DefaultComponentReconciler: NewDefaultComponentReconciler(client, scheme, stack, component),
+		client:                     client,
+		scheme:                     scheme,
+		stack:                      stack,
+		component:                  component,
+	}
+}
+
+func (r *PostgreSQLReconciler) ReconcileComponent(ctx context.Context, tmpl *v1alpha1.Template, variables map[string]string) ([]client.Object, error) {
+	if err := r.ReconcileStorage(ctx, "postgresql", POSTGRES_DEFAULT_STORAGE_SIZE); err != nil {
+		return nil, fmt.Errorf("reconciling postgresql storage: %w", err)
+	}
+	if err := r.ReconcileKey(ctx, "postgresql", POSTGRES_PASSWORD_SECRET_KEY, genrateKey); err != nil {
+		return nil, fmt.Errorf("reconciling postgresql key: %w", err)
+	}
+	if _, err := r.DefaultComponentReconciler.ReconcileComponent(ctx, tmpl, variables); err != nil {
+		return nil, err
+	}
+	return nil, nil
+}
+
+func genrateKey() (string, error) {
+	b := make([]byte, 32)
+	if _, err := rand.Read(b); err != nil {
+		return "", err
+	}
+	return hex.EncodeToString(b), nil
+}

+ 64 - 0
pkg/templates/postgresql/template.go

@@ -0,0 +1,64 @@
+/*
+Copyright 2026 LocoStack.
+
+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 postgresql
+
+import (
+	"github.com/LocoStack/loco-operator/api/v1alpha1"
+	corev1 "k8s.io/api/core/v1"
+)
+
+const (
+	POSTGRESQL_DATA_PATH         = "/var/lib/postgresql"
+	POSTGRES_PASSWORD_SECRET_KEY = "password"
+)
+
+var PostgreSQLTemplate = v1alpha1.Template{
+	Name: "postgresql",
+	Spec: v1alpha1.TemplateSpec{
+		Runtime: v1alpha1.RuntimeSpec{
+			Name:  "postgresql",
+			Image: "postgres:18.4",
+			Args: []string{
+				"-p", "$(spec.runtime.port)",
+			},
+			Env: []corev1.EnvVar{
+				{Name: "POSTGRES_USER", Value: "locostack"},
+				{Name: "POSTGRES_PASSWORD", ValueFrom: &corev1.EnvVarSource{
+					SecretKeyRef: &corev1.SecretKeySelector{
+						LocalObjectReference: corev1.LocalObjectReference{Name: "$(metadata.name)-postgresql"},
+						Key:                  POSTGRES_PASSWORD_SECRET_KEY,
+					},
+				}},
+				{Name: "POSTGRES_DB", Value: "locostack"},
+			},
+			VolumeMounts: []corev1.VolumeMount{
+				{Name: "data", MountPath: POSTGRESQL_DATA_PATH},
+			},
+			Port: 5432,
+		},
+		Volumes: []corev1.Volume{
+			{
+				Name: "data",
+				VolumeSource: corev1.VolumeSource{
+					PersistentVolumeClaim: &corev1.PersistentVolumeClaimVolumeSource{
+						ClaimName: "$(metadata.name)-postgresql",
+					},
+				},
+			},
+		},
+	},
+}

+ 3 - 0
pkg/templates/templates.go

@@ -24,6 +24,7 @@ import (
 	"github.com/LocoStack/loco-operator/api/v1alpha1"
 	"github.com/LocoStack/loco-operator/pkg/templates/litellm"
 	"github.com/LocoStack/loco-operator/pkg/templates/neo4j"
+	"github.com/LocoStack/loco-operator/pkg/templates/postgresql"
 	"github.com/LocoStack/loco-operator/pkg/templates/qdrant"
 	corev1 "k8s.io/api/core/v1"
 )
@@ -246,4 +247,6 @@ func init() {
 	Manager.RegisterRuntime(qdrant.QdrantTemplate.Spec.Runtime)
 	Manager.RegisterTemplate(neo4j.Neo4jTemplate)
 	Manager.RegisterRuntime(neo4j.Neo4jTemplate.Spec.Runtime)
+	Manager.RegisterTemplate(postgresql.PostgreSQLTemplate)
+	Manager.RegisterRuntime(postgresql.PostgreSQLTemplate.Spec.Runtime)
 }