From ac14149b122fa2ada088e0fb1532b6b24a1a3c25 Mon Sep 17 00:00:00 2001 From: Matthias Goerens Date: Fri, 28 Nov 2025 13:08:33 +0100 Subject: [PATCH] Remove ability to deploy PostreSQL DB Users should rather deploy a Postgres DB in another way (postgres-operator or other) and provide their homeserver.yaml with database connection information already filled out. Signed-off-by: Matthias Goerens --- README.md | 5 - api/synapse/v1alpha1/synapse_types.go | 29 -- api/synapse/v1alpha1/zz_generated.deepcopy.go | 16 - ...ynapse-operator.clusterserviceversion.yaml | 15 +- .../manifests/synapse.opdev.io_synapses.yaml | 25 - cmd/main.go | 3 - .../crd/bases/synapse.opdev.io_synapses.yaml | 25 - config/rbac/role.yaml | 12 - config/samples/synapse_v1alpha1_synapse.yaml | 1 - examples/03-deploying-postgresql/synapse.yaml | 10 - examples/README.md | 58 --- go.mod | 3 +- go.sum | 4 - install/synapse-operator.yaml | 37 -- .../synapse/synapse/synapse_configmap.go | 109 ----- .../synapse/synapse/synapse_controller.go | 149 ------ .../synapse/synapse_controller_test.go | 318 +------------ .../synapse/synapse_postgrescluster.go | 247 ---------- .../synapse/synapse/synapse_test.go | 437 ------------------ 19 files changed, 3 insertions(+), 1500 deletions(-) delete mode 100644 examples/03-deploying-postgresql/synapse.yaml delete mode 100644 internal/controller/synapse/synapse/synapse_postgrescluster.go diff --git a/README.md b/README.md index d1ed3a9..18bbec3 100644 --- a/README.md +++ b/README.md @@ -139,15 +139,10 @@ directory. ## Notes and pre-requisites -- The [postgres-operator](https://github.com/CrunchyData/postgres-operator) - needs to be installed. Specifically the `PostgresCluster` CRD is required. - This is only required if you intend to deploy a PostgreSQL instance alongside - Synapse. - Tested on OpenShift 4.9.0 # Related links - [Matrix project homepage](https://matrix.org/) - [Synape Repository](https://github.com/matrix-org/synapse/) -- [postgres-operator](https://github.com/CrunchyData/postgres-operator) - [Heisenbridge](https://github.com/hifi/heisenbridge) diff --git a/api/synapse/v1alpha1/synapse_types.go b/api/synapse/v1alpha1/synapse_types.go index a03bf28..dfd6cb5 100644 --- a/api/synapse/v1alpha1/synapse_types.go +++ b/api/synapse/v1alpha1/synapse_types.go @@ -38,12 +38,6 @@ type SynapseSpec struct { // +kubebuilder:default:=false - // Set to true to create a new PostreSQL instance. The homeserver.yaml - // 'database' section will be overwritten. - CreateNewPostgreSQL bool `json:"createNewPostgreSQL,omitempty"` - - // +kubebuilder:default:=false - // Set to true if deploying on OpenShift IsOpenshift bool `json:"isOpenshift,omitempty"` } @@ -89,12 +83,6 @@ type SynapseHomeserverValues struct { // SynapseStatus defines the observed state of Synapse. type SynapseStatus struct { - // INSERT ADDITIONAL STATUS FIELD - define observed state of cluster - // Important: Run "make" to regenerate code after modifying this file - - // Connection information to the external PostgreSQL Database - DatabaseConnectionInfo SynapseStatusDatabaseConnectionInfo `json:"databaseConnectionInfo,omitempty"` - // Holds configuration information for Synapse HomeserverConfiguration SynapseStatusHomeserverConfiguration `json:"homeserverConfiguration,omitempty"` @@ -137,23 +125,6 @@ type SynapseStatusBridgesMautrixSignal struct { Name string `json:"name,omitempty"` } -type SynapseStatusDatabaseConnectionInfo struct { - // Endpoint to connect to the PostgreSQL database - ConnectionURL string `json:"connectionURL,omitempty"` - - // Name of the database to connect to - DatabaseName string `json:"databaseName,omitempty"` - - // User allowed to query the given database - User string `json:"user,omitempty"` - - // Base64 encoded password - Password string `json:"password,omitempty"` - - // State of the PostgreSQL database - State string `json:"State,omitempty"` -} - type SynapseStatusHomeserverConfiguration struct { // The public-facing domain of the server ServerName string `json:"serverName,omitempty"` diff --git a/api/synapse/v1alpha1/zz_generated.deepcopy.go b/api/synapse/v1alpha1/zz_generated.deepcopy.go index 25d73ea..3c5f903 100644 --- a/api/synapse/v1alpha1/zz_generated.deepcopy.go +++ b/api/synapse/v1alpha1/zz_generated.deepcopy.go @@ -420,7 +420,6 @@ func (in *SynapseSpec) DeepCopy() *SynapseSpec { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *SynapseStatus) DeepCopyInto(out *SynapseStatus) { *out = *in - out.DatabaseConnectionInfo = in.DatabaseConnectionInfo out.HomeserverConfiguration = in.HomeserverConfiguration out.Bridges = in.Bridges } @@ -482,21 +481,6 @@ func (in *SynapseStatusBridgesMautrixSignal) DeepCopy() *SynapseStatusBridgesMau return out } -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *SynapseStatusDatabaseConnectionInfo) DeepCopyInto(out *SynapseStatusDatabaseConnectionInfo) { - *out = *in -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SynapseStatusDatabaseConnectionInfo. -func (in *SynapseStatusDatabaseConnectionInfo) DeepCopy() *SynapseStatusDatabaseConnectionInfo { - if in == nil { - return nil - } - out := new(SynapseStatusDatabaseConnectionInfo) - in.DeepCopyInto(out) - return out -} - // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *SynapseStatusHomeserverConfiguration) DeepCopyInto(out *SynapseStatusHomeserverConfiguration) { *out = *in diff --git a/bundle/manifests/synapse-operator.clusterserviceversion.yaml b/bundle/manifests/synapse-operator.clusterserviceversion.yaml index 1cd2bce..a10a503 100644 --- a/bundle/manifests/synapse-operator.clusterserviceversion.yaml +++ b/bundle/manifests/synapse-operator.clusterserviceversion.yaml @@ -47,7 +47,6 @@ metadata: "name": "synapse-sample" }, "spec": { - "createNewPostgreSQL": false, "homeserver": { "values": { "reportStats": true, @@ -60,7 +59,7 @@ metadata: capabilities: Basic Install categories: Developer Tools containerImage: quay.io/opdev/synapse-operator - createdAt: "2025-08-15T10:29:51Z" + createdAt: "2025-11-28T12:00:17Z" description: Deploys and manages the lifecycle of Synapse servers and their associated components (bridges, databases, ...). Synapse is the reference Matrix homeserver implementation. @@ -126,18 +125,6 @@ spec: - patch - update - watch - - apiGroups: - - postgres-operator.crunchydata.com - resources: - - postgresclusters - verbs: - - create - - delete - - get - - list - - patch - - update - - watch - apiGroups: - rbac.authorization.k8s.io resources: diff --git a/bundle/manifests/synapse.opdev.io_synapses.yaml b/bundle/manifests/synapse.opdev.io_synapses.yaml index 49a37b1..6e503e0 100644 --- a/bundle/manifests/synapse.opdev.io_synapses.yaml +++ b/bundle/manifests/synapse.opdev.io_synapses.yaml @@ -39,12 +39,6 @@ spec: spec: description: SynapseSpec defines the desired state of Synapse. properties: - createNewPostgreSQL: - default: false - description: |- - Set to true to create a new PostreSQL instance. The homeserver.yaml - 'database' section will be overwritten. - type: boolean homeserver: description: |- Holds information related to the homeserver.yaml configuration file. @@ -133,25 +127,6 @@ spec: type: string type: object type: object - databaseConnectionInfo: - description: Connection information to the external PostgreSQL Database - properties: - State: - description: State of the PostgreSQL database - type: string - connectionURL: - description: Endpoint to connect to the PostgreSQL database - type: string - databaseName: - description: Name of the database to connect to - type: string - password: - description: Base64 encoded password - type: string - user: - description: User allowed to query the given database - type: string - type: object homeserverConfiguration: description: Holds configuration information for Synapse properties: diff --git a/cmd/main.go b/cmd/main.go index 83396c5..4135831 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -37,8 +37,6 @@ import ( metricsserver "sigs.k8s.io/controller-runtime/pkg/metrics/server" "sigs.k8s.io/controller-runtime/pkg/webhook" - pgov1beta1 "github.com/crunchydata/postgres-operator/pkg/apis/postgres-operator.crunchydata.com/v1beta1" - synapsev1alpha1 "github.com/opdev/synapse-operator/api/synapse/v1alpha1" heisenbridgecontroller "github.com/opdev/synapse-operator/internal/controller/synapse/heisenbridge" mautrixsignalcontroller "github.com/opdev/synapse-operator/internal/controller/synapse/mautrixsignal" @@ -55,7 +53,6 @@ func init() { utilruntime.Must(clientgoscheme.AddToScheme(scheme)) utilruntime.Must(synapsev1alpha1.AddToScheme(scheme)) - utilruntime.Must(pgov1beta1.AddToScheme(scheme)) // +kubebuilder:scaffold:scheme } diff --git a/config/crd/bases/synapse.opdev.io_synapses.yaml b/config/crd/bases/synapse.opdev.io_synapses.yaml index aa873f7..2f738ea 100644 --- a/config/crd/bases/synapse.opdev.io_synapses.yaml +++ b/config/crd/bases/synapse.opdev.io_synapses.yaml @@ -39,12 +39,6 @@ spec: spec: description: SynapseSpec defines the desired state of Synapse. properties: - createNewPostgreSQL: - default: false - description: |- - Set to true to create a new PostreSQL instance. The homeserver.yaml - 'database' section will be overwritten. - type: boolean homeserver: description: |- Holds information related to the homeserver.yaml configuration file. @@ -128,25 +122,6 @@ spec: type: string type: object type: object - databaseConnectionInfo: - description: Connection information to the external PostgreSQL Database - properties: - State: - description: State of the PostgreSQL database - type: string - connectionURL: - description: Endpoint to connect to the PostgreSQL database - type: string - databaseName: - description: Name of the database to connect to - type: string - password: - description: Base64 encoded password - type: string - user: - description: User allowed to query the given database - type: string - type: object homeserverConfiguration: description: Holds configuration information for Synapse properties: diff --git a/config/rbac/role.yaml b/config/rbac/role.yaml index 8593fd9..f0a7bf5 100644 --- a/config/rbac/role.yaml +++ b/config/rbac/role.yaml @@ -31,18 +31,6 @@ rules: - patch - update - watch -- apiGroups: - - postgres-operator.crunchydata.com - resources: - - postgresclusters - verbs: - - create - - delete - - get - - list - - patch - - update - - watch - apiGroups: - rbac.authorization.k8s.io resources: diff --git a/config/samples/synapse_v1alpha1_synapse.yaml b/config/samples/synapse_v1alpha1_synapse.yaml index 3296dec..8eba465 100644 --- a/config/samples/synapse_v1alpha1_synapse.yaml +++ b/config/samples/synapse_v1alpha1_synapse.yaml @@ -6,7 +6,6 @@ metadata: app.kubernetes.io/managed-by: kustomize name: synapse-sample spec: - createNewPostgreSQL: false homeserver: values: serverName: my.matrix.host diff --git a/examples/03-deploying-postgresql/synapse.yaml b/examples/03-deploying-postgresql/synapse.yaml deleted file mode 100644 index a5cab2a..0000000 --- a/examples/03-deploying-postgresql/synapse.yaml +++ /dev/null @@ -1,10 +0,0 @@ -apiVersion: synapse.opdev.io/v1alpha1 -kind: Synapse -metadata: - name: synapse-with-postgresql -spec: - homeserver: - values: - serverName: example.com - reportStats: true - createNewPostgreSQL: true diff --git a/examples/README.md b/examples/README.md index d75f7a2..46e5729 100644 --- a/examples/README.md +++ b/examples/README.md @@ -78,7 +78,6 @@ metadata: resourceVersion: "98298" uid: 086652b1-9ba9-40ed-9223-1336db12681d spec: - createNewPostgreSQL: false homeserver: values: reportStats: true @@ -126,63 +125,6 @@ $ kubectl delete configmap my-custom-homeserver configmap "my-custom-homeserver" deleted ``` -## [DEPRECATED] Deploying a PostgreSQL instance for Synapse - -> [!WARNING] -> This feature is not tested on recent versions of the Synapse Operator and -> will be removed in the future - -> [!NOTE] -> *Pre-requisite:* The deployment of a PostgreSQL instance relies on the -> [postgres-operator](https://github.com/CrunchyData/postgres-operator). Make -> sure it is running on your cluster if you want to deploy a PostgreSQL -> instance for Synapse. - -The `03-deploying-postgresql` directory provides an example of a `Synapse` -resource requesting for a new PostgreSQL instance to be deployed: - -```shell -$ kubectl apply -f examples/03-deploying-postgresql/synapse.yaml -synapse.synapse.opdev.io/synapse-with-postgresql created - -$ kubectl get pods,replicaset,deployment,statefulset,jobs,pods,service,configmap -NAME READY STATUS RESTARTS AGE -pod/synapse-with-postgresql-5d859d7b57-wrdl9 1/1 Running 0 77s -pod/synapse-with-postgresql-backup-n29s--1-7c7zh 0/1 Completed 0 87s -pod/synapse-with-postgresql-instance1-b7tj-0 3/3 Running 0 98s -pod/synapse-with-postgresql-repo-host-0 1/1 Running 0 98s - -NAME DESIRED CURRENT READY AGE -replicaset.apps/synapse-with-postgresql-5d859d7b57 1 1 1 78s - -NAME READY UP-TO-DATE AVAILABLE AGE -deployment.apps/synapse-with-postgresql 1/1 1 1 78s - -NAME READY AGE -statefulset.apps/synapse-with-postgresql-instance1-b7tj 1/1 98s -statefulset.apps/synapse-with-postgresql-repo-host 1/1 98s - -NAME COMPLETIONS DURATION AGE -job.batch/synapse-with-postgresql-backup-n29s 1/1 27s 87s - -NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE -service/synapse-with-postgresql ClusterIP 10.217.4.222 8008/TCP 78s -service/synapse-with-postgresql-ha ClusterIP 10.217.5.159 5432/TCP 99s -service/synapse-with-postgresql-ha-config ClusterIP None 98s -service/synapse-with-postgresql-pods ClusterIP None 99s -service/synapse-with-postgresql-primary ClusterIP None 5432/TCP 99s -service/synapse-with-postgresql-replicas ClusterIP 10.217.5.24 5432/TCP 99s - -NAME DATA AGE -configmap/kube-root-ca.crt 1 6d19h -configmap/openshift-service-ca.crt 1 6d19h -configmap/synapse-with-postgresql 1 99s -configmap/synapse-with-postgresql-config 1 99s -configmap/synapse-with-postgresql-instance1-b7tj-config 1 98s -configmap/synapse-with-postgresql-pgbackrest-config 3 98s -configmap/synapse-with-postgresql-ssh-config 2 98s -``` - ## Deploying a bridge The synapse Operator supports the deployment of: diff --git a/go.mod b/go.mod index 894f6dc..4afe904 100644 --- a/go.mod +++ b/go.mod @@ -4,13 +4,11 @@ go 1.24.0 require ( dario.cat/mergo v1.0.2 - github.com/crunchydata/postgres-operator v1.3.3-0.20220202164906-1c3cc3597c95 github.com/onsi/ginkgo/v2 v2.26.0 github.com/onsi/gomega v1.38.2 github.com/opdev/subreconciler v0.0.3 gopkg.in/yaml.v3 v3.0.1 k8s.io/api v0.34.1 - k8s.io/apiextensions-apiserver v0.34.1 k8s.io/apimachinery v0.34.1 k8s.io/client-go v0.34.1 sigs.k8s.io/controller-runtime v0.22.2 @@ -93,6 +91,7 @@ require ( google.golang.org/protobuf v1.36.7 // indirect gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect + k8s.io/apiextensions-apiserver v0.34.1 // indirect k8s.io/apiserver v0.34.1 // indirect k8s.io/component-base v0.34.1 // indirect k8s.io/klog/v2 v2.130.1 // indirect diff --git a/go.sum b/go.sum index 3924597..90ac586 100644 --- a/go.sum +++ b/go.sum @@ -16,8 +16,6 @@ github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UF github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/crunchydata/postgres-operator v1.3.3-0.20220202164906-1c3cc3597c95 h1:lsrJP13Tvx+gBc0ox7GncEAgf6U+cgnRdXrvOG5QYok= -github.com/crunchydata/postgres-operator v1.3.3-0.20220202164906-1c3cc3597c95/go.mod h1:NhNMzmVvvdtwj28qJTd6bYztWth1xRATvBI9yuGIHCc= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -263,8 +261,6 @@ gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gotest.tools/v3 v3.1.0 h1:rVV8Tcg/8jHUkPUorwjaMTtemIMVXfIPKiOqnhEhakk= -gotest.tools/v3 v3.1.0/go.mod h1:fHy7eyTmJFO5bQbUsEGQ1v4m2J3Jz9eWL54TP2/ZuYQ= k8s.io/api v0.34.1 h1:jC+153630BMdlFukegoEL8E/yT7aLyQkIVuwhmwDgJM= k8s.io/api v0.34.1/go.mod h1:SB80FxFtXn5/gwzCoN6QCtPD7Vbu5w2n1S0J5gFfTYk= k8s.io/apiextensions-apiserver v0.34.1 h1:NNPBva8FNAPt1iSVwIE0FsdrVriRXMsaWFMqJbII2CI= diff --git a/install/synapse-operator.yaml b/install/synapse-operator.yaml index e4f7210..8f5e227 100644 --- a/install/synapse-operator.yaml +++ b/install/synapse-operator.yaml @@ -259,12 +259,6 @@ spec: spec: description: SynapseSpec defines the desired state of Synapse. properties: - createNewPostgreSQL: - default: false - description: |- - Set to true to create a new PostreSQL instance. The homeserver.yaml - 'database' section will be overwritten. - type: boolean homeserver: description: |- Holds information related to the homeserver.yaml configuration file. @@ -353,25 +347,6 @@ spec: type: string type: object type: object - databaseConnectionInfo: - description: Connection information to the external PostgreSQL Database - properties: - State: - description: State of the PostgreSQL database - type: string - connectionURL: - description: Endpoint to connect to the PostgreSQL database - type: string - databaseName: - description: Name of the database to connect to - type: string - password: - description: Base64 encoded password - type: string - user: - description: User allowed to query the given database - type: string - type: object homeserverConfiguration: description: Holds configuration information for Synapse properties: @@ -486,18 +461,6 @@ rules: - patch - update - watch -- apiGroups: - - postgres-operator.crunchydata.com - resources: - - postgresclusters - verbs: - - create - - delete - - get - - list - - patch - - update - - watch - apiGroups: - rbac.authorization.k8s.io resources: diff --git a/internal/controller/synapse/synapse/synapse_configmap.go b/internal/controller/synapse/synapse/synapse_configmap.go index 8d20d02..7aa2dd3 100644 --- a/internal/controller/synapse/synapse/synapse_configmap.go +++ b/internal/controller/synapse/synapse/synapse_configmap.go @@ -23,8 +23,6 @@ import ( "fmt" "io" "math/big" - "strconv" - "strings" "time" corev1 "k8s.io/api/core/v1" @@ -219,113 +217,6 @@ func (r *SynapseReconciler) ParseHomeserverConfigMap( return nil } -// updateSynapseConfigMapForPostgresCluster is a function of type -// FnWithRequest, to be called in the main reconciliation loop. -// -// It configures the 'database' section of homeserver.yaml to allow Synapse to -// connect to the newly created PostgresCluster instance. -func (r *SynapseReconciler) updateSynapseConfigMapForPostgresCluster( - ctx context.Context, - req ctrl.Request, -) (*ctrl.Result, error) { - s := &synapsev1alpha1.Synapse{} - if r, err := utils.GetResource(ctx, r.Client, req, s); subreconciler.ShouldHaltOrRequeue(r, err) { - return r, err - } - - keyForSynapse := types.NamespacedName{ - Name: s.Name, - Namespace: s.Namespace, - } - - if err := utils.UpdateConfigMap( - ctx, - r.Client, - keyForSynapse, - s, - r.updateHomeserverWithPostgreSQLInfos, - "homeserver.yaml", - ); err != nil { - return subreconciler.RequeueWithError(err) - } - - return subreconciler.ContinueReconciling() -} - -func (r *SynapseReconciler) updateHomeserverWithPostgreSQLInfos( - obj client.Object, - homeserver map[string]any, -) error { - s := obj.(*synapsev1alpha1.Synapse) - - databaseData, err := r.fetchDatabaseDataFromSynapseStatus(*s) - if err != nil { - return err - } - - // Save new database section of homeserver.yaml - homeserver["database"] = databaseData - return nil -} - -func (r *SynapseReconciler) fetchDatabaseDataFromSynapseStatus(s synapsev1alpha1.Synapse) (map[string]any, error) { - databaseData := HomeserverPgsqlDatabase{} - - // Check if s.Status.DatabaseConnectionInfo contains necessary information - if s.Status.DatabaseConnectionInfo == (synapsev1alpha1.SynapseStatusDatabaseConnectionInfo{}) { - err := errors.New("missing DatabaseConnectionInfo in Synapse status") - return map[string]any{}, err - } - - if s.Status.DatabaseConnectionInfo.User == "" { - err := errors.New("missing User in DatabaseConnectionInfo") - return map[string]any{}, err - } - - if s.Status.DatabaseConnectionInfo.Password == "" { - err := errors.New("missing Password in DatabaseConnectionInfo") - return map[string]any{}, err - } - decodedPassword := base64decode([]byte(s.Status.DatabaseConnectionInfo.Password)) - - if s.Status.DatabaseConnectionInfo.DatabaseName == "" { - err := errors.New("missing DatabaseName in DatabaseConnectionInfo") - return map[string]any{}, err - } - - if s.Status.DatabaseConnectionInfo.ConnectionURL == "" { - err := errors.New("missing ConnectionURL in DatabaseConnectionInfo") - return map[string]any{}, err - } - connectionURL := strings.Split(s.Status.DatabaseConnectionInfo.ConnectionURL, ":") - if len(connectionURL) < 2 { - err := errors.New("error parsing the Connection URL with value: " + s.Status.DatabaseConnectionInfo.ConnectionURL) - return map[string]any{}, err - } - port, err := strconv.ParseInt(connectionURL[1], 10, 64) - if err != nil { - return map[string]any{}, err - } - - // Populate databaseData - databaseData.Name = "psycopg2" - databaseData.Args.User = s.Status.DatabaseConnectionInfo.User - databaseData.Args.Password = decodedPassword - databaseData.Args.Database = s.Status.DatabaseConnectionInfo.DatabaseName - databaseData.Args.Host = connectionURL[0] - databaseData.Args.Port = port - databaseData.Args.CpMin = 5 - databaseData.Args.CpMax = 10 - - // Convert databaseData into a map[string]any - databaseDataMap, err := utils.ConvertStructToMap(databaseData) - if err != nil { - return map[string]any{}, err - } - - return databaseDataMap, nil -} - // updateSynapseConfigMapForBridges is a function of type // FnWithRequest, to be called in the main reconciliation loop. // diff --git a/internal/controller/synapse/synapse/synapse_controller.go b/internal/controller/synapse/synapse/synapse_controller.go index 6cac8ad..1d45a1e 100644 --- a/internal/controller/synapse/synapse/synapse_controller.go +++ b/internal/controller/synapse/synapse/synapse_controller.go @@ -18,19 +18,15 @@ package synapse import ( "context" - "errors" - "strings" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/types" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" logf "sigs.k8s.io/controller-runtime/pkg/log" - pgov1beta1 "github.com/crunchydata/postgres-operator/pkg/apis/postgres-operator.crunchydata.com/v1beta1" subreconciler "github.com/opdev/subreconciler" synapsev1alpha1 "github.com/opdev/synapse-operator/api/synapse/v1alpha1" "github.com/opdev/synapse-operator/helpers/utils" @@ -42,41 +38,20 @@ type SynapseReconciler struct { Scheme *runtime.Scheme } -type HomeserverPgsqlDatabase struct { - Name string `yaml:"name"` - TxnLimit int64 `yaml:"txn_limit"` - Args struct { - User string `yaml:"user"` - Password string `yaml:"password"` - Database string `yaml:"database"` - Host string `yaml:"host"` - Port int64 `yaml:"port"` - CpMin int64 `yaml:"cp_min"` - CpMax int64 `yaml:"cp_max"` - } -} - // +kubebuilder:rbac:groups=synapse.opdev.io,resources=synapses,verbs=get;list;watch;create;update;patch;delete // +kubebuilder:rbac:groups=synapse.opdev.io,resources=synapses/status,verbs=get;update;patch // +kubebuilder:rbac:groups=synapse.opdev.io,resources=synapses/finalizers,verbs=update // +kubebuilder:rbac:groups=core,resources=services;persistentvolumeclaims;configmaps;serviceaccounts,verbs=get;list;watch;create;update;patch;delete // +kubebuilder:rbac:groups=apps,resources=deployments,verbs=get;list;watch;create;update;patch;delete // +kubebuilder:rbac:groups=rbac.authorization.k8s.io,resources=rolebindings,verbs=get;list;watch;create;update;patch;delete -// +kubebuilder:rbac:groups=postgres-operator.crunchydata.com,resources=postgresclusters,verbs=get;list;watch;create;update;patch;delete // +kubebuilder:rbac:groups=security.openshift.io,resourceNames=anyuid,resources=securitycontextconstraints,verbs=use -func GetPostgresClusterResourceName(synapse synapsev1alpha1.Synapse) string { - return strings.Join([]string{synapse.Name, "pgsql"}, "-") -} - // Reconcile is part of the main kubernetes reconciliation loop which aims to // move the current state of the cluster closer to the desired state. // // For more details, check Reconcile and its Result here: // - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.20.4/pkg/reconcile func (r *SynapseReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { - log := logf.FromContext(ctx) - var synapse synapsev1alpha1.Synapse // The Synapse object being reconciled if r, err := utils.GetResource(ctx, r.Client, req, &synapse); subreconciler.ShouldHaltOrRequeue(r, err) { return subreconciler.Evaluate(r, err) @@ -113,28 +88,6 @@ func (r *SynapseReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ct r.updateSynapseConfigMapForBridges, ) - if synapse.Spec.CreateNewPostgreSQL { - if !r.isPostgresOperatorInstalled(ctx) { - reason := "Cannot create PostgreSQL instance for synapse. Postgres-operator is not installed." - utils.SetFailedState(ctx, r.Client, &synapse, reason) - - err := errors.New("cannot create PostgreSQL instance for synapse. Potsgres-operator is not installed") - log.Error(err, "Cannot create PostgreSQL instance for synapse. Potsgres-operator is not installed.") - return subreconciler.Evaluate(subreconciler.DoNotRequeue()) - } - - // Reconcile the PostgresCluster CR and ConfigMap. - // Also update the Synapse Status and ConfigMap with database - // connection information. - subreconcilersForSynapse = append( - subreconcilersForSynapse, - r.reconcilePostgresClusterConfigMap, - r.reconcilePostgresClusterCR, - r.updateSynapseStatusWithPostgreSQLInfos, - r.updateSynapseConfigMapForPostgresCluster, - ) - } - // SA and RB are only necessary if we're running on OpenShift if synapse.Spec.IsOpenshift { subreconcilersForSynapse = append( @@ -199,108 +152,6 @@ func (r *SynapseReconciler) setStatusHomeserverConfiguration( return subreconciler.ContinueReconciling() } -func (r *SynapseReconciler) isPostgresOperatorInstalled(ctx context.Context) bool { - err := r.List(ctx, &pgov1beta1.PostgresClusterList{}) - return err == nil -} - -// updateSynapseStatusWithPostgreSQLInfos is a function of type -// FnWithRequest, to be called in the main reconciliation loop. -// -// It parses the PostgresCluster Secret and updates the Synapse status with the -// database connection information. -func (r *SynapseReconciler) updateSynapseStatusWithPostgreSQLInfos( - ctx context.Context, - req ctrl.Request, -) (*ctrl.Result, error) { - log := logf.FromContext(ctx) - - s := &synapsev1alpha1.Synapse{} - if r, err := utils.GetResource(ctx, r.Client, req, s); subreconciler.ShouldHaltOrRequeue(r, err) { - return r, err - } - - var postgresSecret corev1.Secret - - keyForPostgresClusterSecret := types.NamespacedName{ - Name: GetPostgresClusterResourceName(*s) + "-pguser-synapse", - Namespace: s.Namespace, - } - - // Get PostgresCluster Secret containing information for the synapse user - if err := r.Get(ctx, keyForPostgresClusterSecret, &postgresSecret); err != nil { - return subreconciler.RequeueWithError(err) - } - - // Locally updates the Synapse Status - if err := r.updateSynapseStatusDatabase(s, postgresSecret); err != nil { - return subreconciler.RequeueWithError(err) - } - - // Actually sends an API request to update the Status - err := utils.UpdateResourceStatus(ctx, r.Client, s, &synapsev1alpha1.Synapse{}) - if err != nil { - log.Error(err, "Error updating Synapse Status") - return subreconciler.RequeueWithError(err) - } - - return subreconciler.ContinueReconciling() -} - -func (r *SynapseReconciler) updateSynapseStatusDatabase( - s *synapsev1alpha1.Synapse, - postgresSecret corev1.Secret, -) error { - var postgresSecretData = postgresSecret.Data - - host, ok := postgresSecretData["host"] - if !ok { - err := errors.New("missing host in PostgreSQL Secret") - // log.Error(err, "Missing host in PostgreSQL Secret") - return err - } - - port, ok := postgresSecretData["port"] - if !ok { - err := errors.New("missing port in PostgreSQL Secret") - // log.Error(err, "Missing port in PostgreSQL Secret") - return err - } - - // See https://github.com/opdev/synapse-operator/issues/12 - // databaseName, ok := postgresSecretData["dbname"] - _, ok = postgresSecretData["dbname"] - if !ok { - err := errors.New("missing dbname in PostgreSQL Secret") - // log.Error(err, "Missing dbname in PostgreSQL Secret") - return err - } - - user, ok := postgresSecretData["user"] - if !ok { - err := errors.New("missing user in PostgreSQL Secret") - // log.Error(err, "Missing user in PostgreSQL Secret") - return err - } - - password, ok := postgresSecretData["password"] - if !ok { - err := errors.New("missing password in PostgreSQL Secret") - // log.Error(err, "Missing password in PostgreSQL Secret") - return err - } - - s.Status.DatabaseConnectionInfo.ConnectionURL = string(host) + ":" + string(port) - // See https://github.com/opdev/synapse-operator/issues/12 - // s.Status.DatabaseConnectionInfo.DatabaseName = string(databaseName) - s.Status.DatabaseConnectionInfo.DatabaseName = "synapse" - s.Status.DatabaseConnectionInfo.User = string(user) - s.Status.DatabaseConnectionInfo.Password = string(base64encode(string(password))) - s.Status.DatabaseConnectionInfo.State = "READY" - - return nil -} - // setSynapseStatusAsRunning is a function of type FnWithRequest, to be // called in the main reconciliation loop. // diff --git a/internal/controller/synapse/synapse/synapse_controller_test.go b/internal/controller/synapse/synapse/synapse_controller_test.go index aafef94..48f900b 100644 --- a/internal/controller/synapse/synapse/synapse_controller_test.go +++ b/internal/controller/synapse/synapse/synapse_controller_test.go @@ -18,10 +18,7 @@ package synapse import ( "context" - "encoding/json" "fmt" - "io" - "net/http" "os" "path/filepath" "runtime" @@ -34,7 +31,6 @@ import ( appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" rbacv1 "k8s.io/api/rbac/v1" - v1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/types" @@ -48,7 +44,6 @@ import ( "sigs.k8s.io/controller-runtime/pkg/manager" "sigs.k8s.io/controller-runtime/pkg/metrics/server" - pgov1beta1 "github.com/crunchydata/postgres-operator/pkg/apis/postgres-operator.crunchydata.com/v1beta1" synapsev1alpha1 "github.com/opdev/synapse-operator/api/synapse/v1alpha1" "github.com/opdev/synapse-operator/helpers/utils" ) @@ -90,8 +85,6 @@ var _ = Describe("Integration tests for the Synapse controller", Ordered, Label( Expect(cfg).NotTo(BeNil()) Expect(synapsev1alpha1.AddToScheme(scheme.Scheme)).NotTo(HaveOccurred()) - Expect(pgov1beta1.AddToScheme(scheme.Scheme)).NotTo(HaveOccurred()) - // +kubebuilder:scaffold:scheme k8sClient, err = client.New(cfg, client.Options{Scheme: scheme.Scheme}) @@ -132,39 +125,6 @@ var _ = Describe("Integration tests for the Synapse controller", Ordered, Label( ctx, cancel = context.WithCancel(context.TODO()) - By("Getting latest version of the PostgresCluster CRD") - postgresOperatorVersion := "5.2.0" - postgresClusterURL := "https://raw.githubusercontent.com/redhat-openshift-ecosystem/" + - "community-operators-prod/main/operators/postgresql/" + postgresOperatorVersion + - "/manifests/postgresclusters.postgres-operator.crunchydata.com.crd.yaml" - - resp, err := http.Get(postgresClusterURL) - Expect(err).ShouldNot(HaveOccurred()) - - // The CRD is downloaded as a YAML document. The CustomResourceDefinition - // struct defined in the v1 package only possess json tags. In order to - // successfully Unmarshal the CRD Document into a - // CustomResourceDefinition object, it is necessary to first transform the - // YAML document into a intermediate JSON document. - defer resp.Body.Close() //nolint:errcheck - yamlBody, err := io.ReadAll(resp.Body) - Expect(err).ShouldNot(HaveOccurred()) - - // Unmarshal the YAML document into an intermediate map - var mapBody any - Expect(yaml.Unmarshal(yamlBody, &mapBody)).ShouldNot(HaveOccurred()) - - // The map has to be converted. See https://stackoverflow.com/a/40737676/6133648 - mapBody = utils.Convert(mapBody) - - // Marshal the map into an intermediate JSON document - jsonBody, err := json.Marshal(mapBody) - Expect(err).ShouldNot(HaveOccurred()) - - // Unmarshall the JSON document into the final CustomResourceDefinition object. - var PostgresClusterCRD v1.CustomResourceDefinition - Expect(json.Unmarshal(jsonBody, &PostgresClusterCRD)).ShouldNot(HaveOccurred()) - By("bootstrapping test environment") testEnv = &envtest.Environment{ CRDDirectoryPaths: []string{ @@ -172,7 +132,6 @@ var _ = Describe("Integration tests for the Synapse controller", Ordered, Label( filepath.Join("..", "..", "..", "..", "bundle", "manifests", "synapse.opdev.io_heisenbridges.yaml"), filepath.Join("..", "..", "..", "..", "bundle", "manifests", "synapse.opdev.io_mautrixsignals.yaml"), }, - CRDs: []*v1.CustomResourceDefinition{&PostgresClusterCRD}, ErrorIfCRDPathMissing: true, BinaryAssetsDirectory: filepath.Join("..", "..", "..", "bin", "k8s", fmt.Sprintf("1.31.0-%s-%s", runtime.GOOS, runtime.GOARCH)), @@ -217,7 +176,7 @@ var _ = Describe("Integration tests for the Synapse controller", Ordered, Label( "spec": map[string]any{}, }), Entry("when Synapse spec is missing Homeserver", map[string]any{ - "spec": map[string]any{"createNewPostgreSQL": true}, + "spec": map[string]any{"IsOpenshift": true}, }), Entry("when Synapse spec Homeserver is empty", map[string]any{ "spec": map[string]any{ @@ -329,18 +288,6 @@ var _ = Describe("Integration tests for the Synapse controller", Ordered, Label( }, }, ), - Entry( - "when optional CreateNewPostgreSQL and ConfigMap Namespace are missing", - map[string]any{ - "spec": map[string]any{ - "homeserver": map[string]any{ - "configMap": map[string]any{ - "name": InputConfigMapName, - }, - }, - }, - }, - ), ) }) @@ -678,151 +625,6 @@ var _ = Describe("Integration tests for the Synapse controller", Ordered, Label( }) }) - When("Requesting a new PostgreSQL instance to be created for Synapse", func() { - var createdPostgresCluster *pgov1beta1.PostgresCluster - var postgresSecret corev1.Secret - var postgresLookupKeys types.NamespacedName - - BeforeAll(func() { - initSynapseVariables() - - postgresLookupKeys = types.NamespacedName{ - Name: synapseLookupKey.Name + "-pgsql", - Namespace: synapseLookupKey.Namespace, - } - - // Init variable - createdPostgresCluster = &pgov1beta1.PostgresCluster{} - - inputConfigmapData = map[string]string{ - "homeserver.yaml": "server_name: " + ServerName + "\n" + - "report_stats: " + strconv.FormatBool(ReportStats), - } - - synapseSpec = synapsev1alpha1.SynapseSpec{ - Homeserver: synapsev1alpha1.SynapseHomeserver{ - ConfigMap: &synapsev1alpha1.SynapseHomeserverConfigMap{ - Name: InputConfigMapName, - }, - }, - CreateNewPostgreSQL: true, - IsOpenshift: true, - } - - createSynapseConfigMap() - createSynapseInstance() - }) - - doPostgresControllerJob := func() { - // The postgres-operator is responsible for creating a Secret holding - // information on how to connect to the synapse Database with the synapse - // user. As this controller is not running during our integration tests, - // we have to manually create this secret here. - postgresSecret = corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: SynapseName + "-pgsql-pguser-synapse", - Namespace: SynapseNamespace, - }, - Data: map[string][]byte{ - "host": []byte("hostname.postgresql.url"), - "port": []byte("5432"), - "dbname": []byte("synapse"), - "user": []byte("synapse"), - "password": []byte("VerySecureSyn@psePassword!"), - }, - } - Expect(k8sClient.Create(ctx, &postgresSecret)).Should(Succeed()) - - // The portgres-operator is responsible for updating the PostgresCluster - // status, with the number of Pods being ready. This is used a part of - // the 'isPostgresClusterReady' method in the Synapse controller. - createdPostgresCluster.Status.InstanceSets = []pgov1beta1.PostgresInstanceSetStatus{{ - Name: "instance1", - Replicas: 1, - ReadyReplicas: 1, - UpdatedReplicas: 1, - }} - Expect(k8sClient.Status().Update(ctx, createdPostgresCluster)).Should(Succeed()) - } - - AfterAll(func() { - By("Cleaning up the Synapse PostgresCluster") - deleteResource(createdPostgresCluster, postgresLookupKeys, false) - - cleanupSynapseResources() - cleanupSynapseConfigMap() - }) - - It("Should create a PostgresCluster for Synapse", func() { - By("Checking that a Synapse PostgresCluster exists") - checkResourcePresence(createdPostgresCluster, postgresLookupKeys, expectedOwnerReference) - }) - - It("Should update the Synapse status", func() { - By("Checking that the controller detects the Database as not ready") - Expect(k8sClient.Get(ctx, synapseLookupKey, synapse)).Should(Succeed()) - Expect(synapse.Status.DatabaseConnectionInfo.State).Should(Equal("NOT READY")) - - // Once the PostgresCluster has been created, we simulate the - // postgres-operator reconciliation. - By("Simulating the postgres-operator controller job") - doPostgresControllerJob() - - By("Checking that the Synapse Status is correctly updated") - Eventually(func(g Gomega) { - g.Expect(k8sClient.Get(ctx, synapseLookupKey, synapse)).Should(Succeed()) - - g.Expect(synapse.Status.DatabaseConnectionInfo.ConnectionURL).Should(Equal("hostname.postgresql.url:5432")) - g.Expect(synapse.Status.DatabaseConnectionInfo.DatabaseName).Should(Equal("synapse")) - g.Expect(synapse.Status.DatabaseConnectionInfo.User).Should(Equal("synapse")) - encodedPassword := string(base64encode("VerySecureSyn@psePassword!")) - g.Expect(synapse.Status.DatabaseConnectionInfo.Password).Should(Equal(encodedPassword)) - g.Expect(synapse.Status.DatabaseConnectionInfo.State).Should(Equal("READY")) - }, timeout, interval).Should(Succeed()) - }) - - It("Should update the ConfigMap Data", func() { - Eventually(func(g Gomega) { - // Fetching database section of the homeserver.yaml configuration file - g.Expect(k8sClient.Get(ctx, - types.NamespacedName{Name: SynapseName, Namespace: SynapseNamespace}, - createdConfigMap, - )).Should(Succeed()) - - ConfigMapData, ok := createdConfigMap.Data["homeserver.yaml"] - g.Expect(ok).Should(BeTrue()) - - homeserver := make(map[string]any) - g.Expect(yaml.Unmarshal([]byte(ConfigMapData), homeserver)).Should(Succeed()) - - _, ok = homeserver["database"] - g.Expect(ok).Should(BeTrue()) - - marshalled_homeserver_database, err := yaml.Marshal(homeserver["database"]) - g.Expect(err).ShouldNot(HaveOccurred()) - - var hs_database HomeserverPgsqlDatabase - g.Expect(yaml.Unmarshal(marshalled_homeserver_database, &hs_database)).Should(Succeed()) - - // hs_database, ok := homeserver["database"].(HomeserverPgsqlDatabase) - // g.Expect(ok).Should(BeTrue()) - - // Testing that the database section is correctly configured for using - // the PostgreSQL DB - g.Expect(hs_database.Name).Should(Equal("psycopg2")) - g.Expect(hs_database.Args.Host).Should(Equal("hostname.postgresql.url")) - - g.Expect(hs_database.Args.Port).Should(Equal(int64(5432))) - g.Expect(hs_database.Args.Database).Should(Equal("synapse")) - g.Expect(hs_database.Args.User).Should(Equal("synapse")) - g.Expect(hs_database.Args.Password).Should(Equal("VerySecureSyn@psePassword!")) - - g.Expect(hs_database.Args.CpMin).Should(Equal(int64(5))) - g.Expect(hs_database.Args.CpMax).Should(Equal(int64(10))) - }, timeout, interval).Should(Succeed()) - }) - }) - When("Enabling the Heisenbridge", func() { const ( heisenbridgeName = "test-heisenbridge" @@ -1176,124 +978,6 @@ var _ = Describe("Integration tests for the Synapse controller", Ordered, Label( }) }) }) - - Context("When the Kubernetes cluster is missing the postgres-operator", func() { - BeforeAll(func() { - logf.SetLogger(zap.New(zap.WriteTo(GinkgoWriter), zap.UseDevMode(true))) - - ctx, cancel = context.WithCancel(context.TODO()) - - By("bootstrapping test environment") - testEnv = &envtest.Environment{ - CRDDirectoryPaths: []string{ - filepath.Join("..", "..", "..", "..", "bundle", "manifests", "synapse.opdev.io_synapses.yaml"), - }, - ErrorIfCRDPathMissing: true, - BinaryAssetsDirectory: filepath.Join("..", "..", "..", "bin", "k8s", - fmt.Sprintf("1.31.0-%s-%s", runtime.GOOS, runtime.GOARCH)), - AttachControlPlaneOutput: true, - } - - startenvTest() - }) - - AfterAll(func() { - cancel() - By("tearing down the test environment") - err := testEnv.Stop() - Expect(err).NotTo(HaveOccurred()) - }) - - When("Requesting a new PostgreSQL instance to be created for Synapse", func() { - var synapse *synapsev1alpha1.Synapse - var configMap *corev1.ConfigMap - - var createdPVC *corev1.PersistentVolumeClaim - var createdDeployment *appsv1.Deployment - var createdService *corev1.Service - var createdServiceAccount *corev1.ServiceAccount - var createdRoleBinding *rbacv1.RoleBinding - var synapseLookupKey types.NamespacedName - - var createdPostgresCluster *pgov1beta1.PostgresCluster - var postgresLookupKeys types.NamespacedName - - var initSynapseVariables = func() { - // Init variables - synapseLookupKey = types.NamespacedName{Name: SynapseName, Namespace: SynapseNamespace} - createdPVC = &corev1.PersistentVolumeClaim{} - createdDeployment = &appsv1.Deployment{} - createdService = &corev1.Service{} - createdServiceAccount = &corev1.ServiceAccount{} - createdRoleBinding = &rbacv1.RoleBinding{} - } - - BeforeAll(func() { - initSynapseVariables() - - postgresLookupKeys = types.NamespacedName{ - Name: synapseLookupKey.Name + "-pgsql", - Namespace: synapseLookupKey.Namespace, - } - - createdPostgresCluster = &pgov1beta1.PostgresCluster{} - - configMap = &corev1.ConfigMap{ - ObjectMeta: metav1.ObjectMeta{ - Name: InputConfigMapName, - Namespace: SynapseNamespace, - }, - Data: map[string]string{ - "homeserver.yaml": "server_name: " + ServerName + "\n" + - "report_stats: " + strconv.FormatBool(ReportStats), - }, - } - Expect(k8sClient.Create(ctx, configMap)).Should(Succeed()) - - By("Creating the Synapse instance") - synapse = &synapsev1alpha1.Synapse{ - ObjectMeta: metav1.ObjectMeta{ - Name: SynapseName, - Namespace: SynapseNamespace, - }, - Spec: synapsev1alpha1.SynapseSpec{ - Homeserver: synapsev1alpha1.SynapseHomeserver{ - ConfigMap: &synapsev1alpha1.SynapseHomeserverConfigMap{ - Name: InputConfigMapName, - }, - }, - CreateNewPostgreSQL: true, - }, - } - Expect(k8sClient.Create(ctx, synapse)).Should(Succeed()) - }) - - AfterAll(func() { - By("Cleaning up ConfigMap") - Expect(k8sClient.Delete(ctx, configMap)).Should(Succeed()) - - By("Cleaning up Synapse CR") - Expect(k8sClient.Delete(ctx, synapse)).Should(Succeed()) - }) - - It("Should not create Synapse sub-resources", func() { - reason := "Cannot create PostgreSQL instance for synapse. Postgres-operator is not installed." - checkStatus("FAILED", reason, synapseLookupKey, synapse) - checkSubresourceAbsence( - synapseLookupKey, - createdPVC, - createdDeployment, - createdService, - createdServiceAccount, - createdRoleBinding, - ) - checkSubresourceAbsence( - postgresLookupKeys, - createdPostgresCluster, - ) - }) - }) - }) }) // getFirstFoundEnvTestBinaryDir locates the first binary in the specified path. diff --git a/internal/controller/synapse/synapse/synapse_postgrescluster.go b/internal/controller/synapse/synapse/synapse_postgrescluster.go deleted file mode 100644 index eb3b843..0000000 --- a/internal/controller/synapse/synapse/synapse_postgrescluster.go +++ /dev/null @@ -1,247 +0,0 @@ -/* -Copyright 2025. - -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 synapse - -import ( - "context" - b64 "encoding/base64" - "errors" - "time" - - corev1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/api/resource" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/types" - ctrl "sigs.k8s.io/controller-runtime" - logf "sigs.k8s.io/controller-runtime/pkg/log" - - pgov1beta1 "github.com/crunchydata/postgres-operator/pkg/apis/postgres-operator.crunchydata.com/v1beta1" - subreconciler "github.com/opdev/subreconciler" - synapsev1alpha1 "github.com/opdev/synapse-operator/api/synapse/v1alpha1" - "github.com/opdev/synapse-operator/helpers/reconcile" - "github.com/opdev/synapse-operator/helpers/utils" -) - -// reconcilePostgresClusterCR is a function of type FnWithRequest, to be -// called in the main reconciliation loop. -// -// It reconciles the PostgresCluster CR to its desired state, and requeues -// until the PostgreSQL cluster is up. -func (r *SynapseReconciler) reconcilePostgresClusterCR(ctx context.Context, req ctrl.Request) (*ctrl.Result, error) { - log := logf.FromContext(ctx) - - s := &synapsev1alpha1.Synapse{} - if r, err := utils.GetResource(ctx, r.Client, req, s); subreconciler.ShouldHaltOrRequeue(r, err) { - return r, err - } - - createdPostgresCluster := pgov1beta1.PostgresCluster{} - postgresClusterObjectMeta := reconcile.SetObjectMeta( - GetPostgresClusterResourceName(*s), - s.Namespace, - map[string]string{}, - ) - keyForPostgresCluster := types.NamespacedName{ - Name: GetPostgresClusterResourceName(*s), - Namespace: s.Namespace, - } - - desiredPostgresCluster, err := r.postgresClusterForSynapse(s, postgresClusterObjectMeta) - if err != nil { - return subreconciler.RequeueWithError(err) - } - - // Create PostgresCluster for Synapse - if err := reconcile.ReconcileResource( - ctx, - r.Client, - desiredPostgresCluster, - &createdPostgresCluster, - ); err != nil { - return subreconciler.RequeueWithError(err) - } - - // Wait for PostgresCluster to be up - // TODO: can be removed ? - if err := r.Get(ctx, keyForPostgresCluster, &createdPostgresCluster); err != nil { - return subreconciler.RequeueWithError(err) - } - if !r.isPostgresClusterReady(createdPostgresCluster) { - s.Status.DatabaseConnectionInfo.State = "NOT READY" - err = utils.UpdateResourceStatus(ctx, r.Client, s, &synapsev1alpha1.Synapse{}) - if err != nil { - log.Error(err, "Error updating Synapse State") - } - - err = errors.New("postgreSQL Database not ready yet") - return subreconciler.RequeueWithDelayAndError(time.Duration(5), err) - } - - return subreconciler.ContinueReconciling() -} - -// postgresClusterForSynapse returns a PostgresCluster object -func (r *SynapseReconciler) postgresClusterForSynapse( - s *synapsev1alpha1.Synapse, - objectMeta metav1.ObjectMeta, -) (*pgov1beta1.PostgresCluster, error) { - postgresCluster := &pgov1beta1.PostgresCluster{ - ObjectMeta: objectMeta, - Spec: pgov1beta1.PostgresClusterSpec{ - Image: "registry.developers.crunchydata.com/crunchydata/crunchy-postgres:ubi8-14.5-1", - PostgresVersion: 14, - InstanceSets: []pgov1beta1.PostgresInstanceSetSpec{{ - Name: "instance1", - DataVolumeClaimSpec: corev1.PersistentVolumeClaimSpec{ - AccessModes: []corev1.PersistentVolumeAccessMode{"ReadWriteOnce"}, - Resources: corev1.VolumeResourceRequirements{ - Requests: corev1.ResourceList{ - "storage": *resource.NewQuantity(1*1024*1024*1024, resource.BinarySI), - }, - }, - }, - }}, - Backups: pgov1beta1.Backups{ - PGBackRest: pgov1beta1.PGBackRestArchive{ - Image: "registry.developers.crunchydata.com/crunchydata/crunchy-pgbackrest:ubi8-2.40-1", - Repos: []pgov1beta1.PGBackRestRepo{{ - Name: "repo1", - Volume: &pgov1beta1.RepoPVC{ - VolumeClaimSpec: corev1.PersistentVolumeClaimSpec{ - AccessModes: []corev1.PersistentVolumeAccessMode{"ReadWriteOnce"}, - Resources: corev1.VolumeResourceRequirements{ - Requests: corev1.ResourceList{ - "storage": *resource.NewQuantity(1*1024*1024*1024, resource.BinarySI), - }, - }, - }, - }, - }}, - }, - }, - Users: []pgov1beta1.PostgresUserSpec{{ - Name: "synapse", - Databases: []pgov1beta1.PostgresIdentifier{"dummy"}, - }}, - // See https://github.com/opdev/synapse-operator/issues/12 - DatabaseInitSQL: &pgov1beta1.DatabaseInitSQL{ - Name: objectMeta.Name, - Key: "createdb.sql", - }, - }, - } - - // Set Synapse instance as the owner and controller - if err := ctrl.SetControllerReference(s, postgresCluster, r.Scheme); err != nil { - return &pgov1beta1.PostgresCluster{}, err - } - return postgresCluster, nil -} - -func (r *SynapseReconciler) isPostgresClusterReady(p pgov1beta1.PostgresCluster) bool { - var status_found bool - - // Going through instance Specs - for _, instance_spec := range p.Spec.InstanceSets { - status_found = false - for _, instance_status := range p.Status.InstanceSets { - if instance_status.Name == instance_spec.Name { - desired_replicas := *instance_spec.Replicas - if instance_status.Replicas != desired_replicas || - instance_status.ReadyReplicas != desired_replicas || - instance_status.UpdatedReplicas != desired_replicas { - return false - } - // Found instance in Status, breaking out of for loop - status_found = true - break - } - } - - // Instance found in spec, but not in status - if !status_found { - return false - } - } - - // All instances have the correct number of replicas - return true -} - -// reconcilePostgresClusterConfigMap is a function of type FnWithRequest, -// to be called in the main reconciliation loop. -// -// It reconciles the PostgresCluster ConfigMap to its desired state. -func (r *SynapseReconciler) reconcilePostgresClusterConfigMap( - ctx context.Context, - req ctrl.Request, -) (*ctrl.Result, error) { - s := &synapsev1alpha1.Synapse{} - if r, err := utils.GetResource(ctx, r.Client, req, s); subreconciler.ShouldHaltOrRequeue(r, err) { - return r, err - } - - postgresClusterObjectMeta := reconcile.SetObjectMeta( - GetPostgresClusterResourceName(*s), - s.Namespace, - map[string]string{}, - ) - - desiredConfigMap, err := r.configMapForPostgresCluster(s, postgresClusterObjectMeta) - if err != nil { - return subreconciler.RequeueWithError(err) - } - - // Create ConfigMap for PostgresCluster - if err := reconcile.ReconcileResource( - ctx, - r.Client, - desiredConfigMap, - &corev1.ConfigMap{}, - ); err != nil { - return subreconciler.RequeueWithError(err) - } - - return subreconciler.ContinueReconciling() -} - -// configMapForPostgresCluster returns a ConfigMap object -func (r *SynapseReconciler) configMapForPostgresCluster( - s *synapsev1alpha1.Synapse, - objectMeta metav1.ObjectMeta, -) (*corev1.ConfigMap, error) { - createQuery := "CREATE DATABASE synapse LOCALE 'C' ENCODING 'UTF-8' TEMPLATE template0;" - configMap := &corev1.ConfigMap{ - ObjectMeta: objectMeta, - Data: map[string]string{"createdb.sql": createQuery}, - } - - if err := ctrl.SetControllerReference(s, configMap, r.Scheme); err != nil { - return &corev1.ConfigMap{}, err - } - - return configMap, nil -} - -func base64encode(to_encode string) []byte { - return []byte(b64.StdEncoding.EncodeToString([]byte(to_encode))) -} - -func base64decode(to_decode []byte) string { - decoded_bytes, _ := b64.StdEncoding.DecodeString(string(to_decode)) - return string(decoded_bytes) -} diff --git a/internal/controller/synapse/synapse/synapse_test.go b/internal/controller/synapse/synapse/synapse_test.go index a222b56..7599af8 100644 --- a/internal/controller/synapse/synapse/synapse_test.go +++ b/internal/controller/synapse/synapse/synapse_test.go @@ -9,11 +9,8 @@ import ( . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" - "gopkg.in/yaml.v3" - pgov1beta1 "github.com/crunchydata/postgres-operator/pkg/apis/postgres-operator.crunchydata.com/v1beta1" synapsev1alpha1 "github.com/opdev/synapse-operator/api/synapse/v1alpha1" - "github.com/opdev/synapse-operator/helpers/utils" corev1 "k8s.io/api/core/v1" ) @@ -141,438 +138,4 @@ var _ = Describe("Unit tests for Synapse package", Label("unit"), func() { }) }) }) - - Context("When checking if the PostreSQL Cluster is ready", func() { - var postgresCluster pgov1beta1.PostgresCluster - var postgresInstanceSetSpec []pgov1beta1.PostgresInstanceSetSpec - var postgresInstanceSetStatus []pgov1beta1.PostgresInstanceSetStatus - var r SynapseReconciler - var repl int32 - - BeforeEach(func() { - r = SynapseReconciler{} - - // Default PostgresCluster state, to be overwritten in BeforeEach - repl = int32(1) - postgresInstanceSetSpec = []pgov1beta1.PostgresInstanceSetSpec{{ - Name: "instance1", - Replicas: &repl, - }} - postgresInstanceSetStatus = []pgov1beta1.PostgresInstanceSetStatus{{ - Name: "instance1", - ReadyReplicas: 1, - Replicas: 1, - UpdatedReplicas: 1, - }} - }) - - JustBeforeEach(func() { - postgresCluster.Spec.InstanceSets = postgresInstanceSetSpec - postgresCluster.Status.InstanceSets = postgresInstanceSetStatus - }) - - When("when all replicas are ready and have the desired specification", func() { - // No BeforeEach, use default PostgresCluster state - It("Should consider the PostgreSQL cluster as ready", func() { - Expect(r.isPostgresClusterReady(postgresCluster)).Should(BeTrue()) - }) - }) - - When("when some replicas are not ready", func() { - BeforeEach(func() { - postgresInstanceSetStatus = []pgov1beta1.PostgresInstanceSetStatus{{ - Name: "instance1", - ReadyReplicas: 0, - Replicas: 1, - UpdatedReplicas: 1, - }} - }) - - It("Should not consider the PostgreSQL cluster as ready", func() { - Expect(r.isPostgresClusterReady(postgresCluster)).Should(BeFalse()) - }) - }) - - When("when some replicas are not updated", func() { - BeforeEach(func() { - postgresInstanceSetStatus = []pgov1beta1.PostgresInstanceSetStatus{{ - Name: "instance1", - ReadyReplicas: 1, - Replicas: 1, - UpdatedReplicas: 0, - }} - }) - - It("Should not consider the PostgreSQL cluster as ready", func() { - Expect(r.isPostgresClusterReady(postgresCluster)).Should(BeFalse()) - }) - }) - - When("when no replicas exist", func() { - BeforeEach(func() { - postgresInstanceSetStatus = []pgov1beta1.PostgresInstanceSetStatus{{ - Name: "instance1", - ReadyReplicas: 0, - Replicas: 0, - UpdatedReplicas: 0, - }} - }) - - It("Should not consider the PostgreSQL cluster as ready", func() { - Expect(r.isPostgresClusterReady(postgresCluster)).Should(BeFalse()) - }) - }) - - When("when no enough replicas exist", func() { - BeforeEach(func() { - repl = int32(2) - postgresInstanceSetSpec = []pgov1beta1.PostgresInstanceSetSpec{{ - Name: "instance1", - Replicas: &repl, - }} - }) - - It("Should not consider the PostgreSQL cluster as ready", func() { - Expect(r.isPostgresClusterReady(postgresCluster)).Should(BeFalse()) - }) - }) - - When("when an instance is present in spec but not in status", func() { - BeforeEach(func() { - repl = int32(1) - postgresInstanceSetSpec = []pgov1beta1.PostgresInstanceSetSpec{{ - Name: "instance1", - Replicas: &repl, - }, { - Name: "instance2", - Replicas: &repl, - }} - }) - - It("Should not consider the PostgreSQL cluster as ready", func() { - Expect(r.isPostgresClusterReady(postgresCluster)).Should(BeFalse()) - }) - }) - - }) - - Context("When updating the Synapse Status with PostgreSQL database information", func() { - var r SynapseReconciler - var s synapsev1alpha1.Synapse - var synapseDatabaseInfo synapsev1alpha1.SynapseStatusDatabaseConnectionInfo - var postgresSecret corev1.Secret - var postgresSecretData map[string][]byte - - // Re-usable test for checking different happy paths - check_happy_path := func() { - By("Updating the Synapse Status") - Expect(r.updateSynapseStatusDatabase(&s, postgresSecret)).Should(Succeed()) - - By("Checking that the Database information in the Synapse Status are correct") - Expect(s.Status.DatabaseConnectionInfo.ConnectionURL).Should(Equal("unittestdb-primary.unittest-postgres.svc:5432")) - Expect(s.Status.DatabaseConnectionInfo.DatabaseName).Should(Equal("synapse")) - Expect(s.Status.DatabaseConnectionInfo.User).Should(Equal("synapse")) - Expect(s.Status.DatabaseConnectionInfo.Password).Should(Equal(string(base64encode("iOycqrF;EbyqUo7Z2oma}.