From 348af77e9b264d9c0173bfb3bc02c8b4e43afe02 Mon Sep 17 00:00:00 2001 From: mgoerens Date: Tue, 15 Nov 2022 12:07:26 +0100 Subject: [PATCH] Move to a multi controller setup This commit introduces a major refactoring of the project. It introduces two new APIs and their respective controller, for managing the Heisenbrdige and MautrixSignal bridges. Reconciliation logic has therefore been moved to separate controllers and packages. fixes #47 --- PROJECT | 18 + apis/synapse/v1alpha1/heisenbridge_types.go | 102 ++ apis/synapse/v1alpha1/mautrixsignal_types.go | 102 ++ apis/synapse/v1alpha1/synapse_types.go | 108 +- .../synapse/v1alpha1/zz_generated.deepcopy.go | 308 ++++- bundle.Dockerfile | 2 +- ...ynapse-operator.clusterserviceversion.yaml | 114 +- .../synapse.opdev.io_heisenbridges.yaml | 95 ++ .../synapse.opdev.io_mautrixsignals.yaml | 98 ++ .../manifests/synapse.opdev.io_synapses.yaml | 102 +- bundle/metadata/annotations.yaml | 2 +- .../bases/synapse.opdev.io_heisenbridges.yaml | 90 ++ .../synapse.opdev.io_mautrixsignals.yaml | 93 ++ .../crd/bases/synapse.opdev.io_synapses.yaml | 96 +- config/crd/kustomization.yaml | 6 + .../cainjection_in_synapse_heisenbridges.yaml | 7 + ...cainjection_in_synapse_mautrixsignals.yaml | 7 + .../webhook_in_synapse_heisenbridges.yaml | 16 + .../webhook_in_synapse_mautrixsignals.yaml | 16 + ...ynapse-operator.clusterserviceversion.yaml | 10 + config/rbac/role.yaml | 52 + .../synapse_heisenbridge_editor_role.yaml | 31 + .../synapse_heisenbridge_viewer_role.yaml | 27 + .../synapse_mautrixsignal_editor_role.yaml | 31 + .../synapse_mautrixsignal_viewer_role.yaml | 27 + config/samples/kustomization.yaml | 2 + .../synapse_v1alpha1_heisenbridge.yaml | 13 + .../synapse_v1alpha1_mautrixsignal.yaml | 13 + config/samples/synapse_v1alpha1_synapse.yaml | 2 +- .../heisenbridge/heisenbridge_controller.go | 222 +++ .../synapse_heisenbridge_configmap.go | 97 +- .../synapse_heisenbridge_deployment.go | 43 +- .../synapse_heisenbridge_service.go | 27 +- .../mautrixsignal_configmap.go} | 113 +- .../mautrixsignal/mautrixsignal_controller.go | 264 ++++ .../mautrixsignal_controller_test.go | 1222 +++++++++++++++++ .../mautrixsignal_deployment.go} | 33 +- .../mautrixsignal_pvc.go} | 27 +- .../mautrixsignal_service.go} | 29 +- .../mautrixsignal_serviceaccount.go} | 51 +- .../mautrixsignal/mautrixsignal_test.go | 543 ++++++++ .../signald_deployment.go} | 31 +- .../signald_pvc.go} | 27 +- .../synapse/mautrixsignal/suite_test.go | 34 + controllers/synapse/reconcile.go | 95 -- .../synapse/{ => synapse}/suite_test.go | 4 +- .../{ => synapse}/synapse_configmap.go | 127 +- .../{ => synapse}/synapse_controller.go | 206 +-- .../{ => synapse}/synapse_controller_test.go | 1145 +++++++-------- .../{ => synapse}/synapse_deployment.go | 32 +- .../{ => synapse}/synapse_postgrescluster.go | 57 +- .../synapse/{ => synapse}/synapse_pvc.go | 23 +- .../synapse/{ => synapse}/synapse_service.go | 23 +- .../{ => synapse}/synapse_serviceaccount.go | 43 +- controllers/synapse/synapse/synapse_test.go | 543 ++++++++ controllers/synapse/synapse_test.go | 1086 +++++++-------- go.mod | 4 +- helpers/reconcile/reconcile.go | 102 ++ helpers/reconcileresults/reconcileresults.go | 8 + helpers/utils/bridge_utils.go | 36 + .../utils}/configmap_utils.go | 54 +- helpers/utils/synapse_utils.go | 17 + .../synapse => helpers/utils}/tests_utils.go | 14 +- .../synapse => helpers/utils}/utils.go | 8 +- main.go | 21 +- 65 files changed, 5986 insertions(+), 2015 deletions(-) create mode 100644 apis/synapse/v1alpha1/heisenbridge_types.go create mode 100644 apis/synapse/v1alpha1/mautrixsignal_types.go create mode 100644 bundle/manifests/synapse.opdev.io_heisenbridges.yaml create mode 100644 bundle/manifests/synapse.opdev.io_mautrixsignals.yaml create mode 100644 config/crd/bases/synapse.opdev.io_heisenbridges.yaml create mode 100644 config/crd/bases/synapse.opdev.io_mautrixsignals.yaml create mode 100644 config/crd/patches/cainjection_in_synapse_heisenbridges.yaml create mode 100644 config/crd/patches/cainjection_in_synapse_mautrixsignals.yaml create mode 100644 config/crd/patches/webhook_in_synapse_heisenbridges.yaml create mode 100644 config/crd/patches/webhook_in_synapse_mautrixsignals.yaml create mode 100644 config/rbac/synapse_heisenbridge_editor_role.yaml create mode 100644 config/rbac/synapse_heisenbridge_viewer_role.yaml create mode 100644 config/rbac/synapse_mautrixsignal_editor_role.yaml create mode 100644 config/rbac/synapse_mautrixsignal_viewer_role.yaml create mode 100644 config/samples/synapse_v1alpha1_heisenbridge.yaml create mode 100644 config/samples/synapse_v1alpha1_mautrixsignal.yaml create mode 100644 controllers/synapse/heisenbridge/heisenbridge_controller.go rename controllers/synapse/{ => heisenbridge}/synapse_heisenbridge_configmap.go (64%) rename controllers/synapse/{ => heisenbridge}/synapse_heisenbridge_deployment.go (69%) rename controllers/synapse/{ => heisenbridge}/synapse_heisenbridge_service.go (68%) rename controllers/synapse/{synapse_mautrixsignal_configmap.go => mautrixsignal/mautrixsignal_configmap.go} (86%) create mode 100644 controllers/synapse/mautrixsignal/mautrixsignal_controller.go create mode 100644 controllers/synapse/mautrixsignal/mautrixsignal_controller_test.go rename controllers/synapse/{synapse_mautrixsignal_deployment.go => mautrixsignal/mautrixsignal_deployment.go} (81%) rename controllers/synapse/{synapse_mautrixsignal_pvc.go => mautrixsignal/mautrixsignal_pvc.go} (69%) rename controllers/synapse/{synapse_mautrixsignal_service.go => mautrixsignal/mautrixsignal_service.go} (67%) rename controllers/synapse/{synapse_mautrixsignal_serviceaccount.go => mautrixsignal/mautrixsignal_serviceaccount.go} (62%) create mode 100644 controllers/synapse/mautrixsignal/mautrixsignal_test.go rename controllers/synapse/{synapse_signald_deployment.go => mautrixsignal/signald_deployment.go} (72%) rename controllers/synapse/{synapse_signald_pvc.go => mautrixsignal/signald_pvc.go} (69%) create mode 100644 controllers/synapse/mautrixsignal/suite_test.go delete mode 100644 controllers/synapse/reconcile.go rename controllers/synapse/{ => synapse}/suite_test.go (93%) rename controllers/synapse/{ => synapse}/synapse_configmap.go (97%) rename controllers/synapse/{ => synapse}/synapse_controller.go (64%) rename controllers/synapse/{ => synapse}/synapse_controller_test.go (53%) rename controllers/synapse/{ => synapse}/synapse_deployment.go (86%) rename controllers/synapse/{ => synapse}/synapse_postgrescluster.go (81%) rename controllers/synapse/{ => synapse}/synapse_pvc.go (76%) rename controllers/synapse/{ => synapse}/synapse_service.go (76%) rename controllers/synapse/{ => synapse}/synapse_serviceaccount.go (70%) create mode 100644 controllers/synapse/synapse/synapse_test.go create mode 100644 helpers/reconcile/reconcile.go create mode 100644 helpers/utils/bridge_utils.go rename {controllers/synapse => helpers/utils}/configmap_utils.go (73%) create mode 100644 helpers/utils/synapse_utils.go rename {controllers/synapse => helpers/utils}/tests_utils.go (95%) rename {controllers/synapse => helpers/utils}/utils.go (80%) diff --git a/PROJECT b/PROJECT index 318f3c3..50b2f08 100644 --- a/PROJECT +++ b/PROJECT @@ -17,4 +17,22 @@ resources: kind: Synapse path: github.com/opdev/synapse-operator/apis/synapse/v1alpha1 version: v1alpha1 +- api: + crdVersion: v1 + namespaced: true + controller: true + domain: opdev.io + group: synapse + kind: MautrixSignal + path: github.com/opdev/synapse-operator/apis/synapse/v1alpha1 + version: v1alpha1 +- api: + crdVersion: v1 + namespaced: true + controller: true + domain: opdev.io + group: synapse + kind: Heisenbridge + path: github.com/opdev/synapse-operator/apis/synapse/v1alpha1 + version: v1alpha1 version: "3" diff --git a/apis/synapse/v1alpha1/heisenbridge_types.go b/apis/synapse/v1alpha1/heisenbridge_types.go new file mode 100644 index 0000000..06f4c60 --- /dev/null +++ b/apis/synapse/v1alpha1/heisenbridge_types.go @@ -0,0 +1,102 @@ +/* +Copyright 2021. + +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 v1alpha1 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// HeisenbridgeSpec defines the desired state of Heisenbridge. The user can +// either: +// - enable the bridge, without specifying additional configuration options. +// The bridge will be deployed with a default configuration. +// - enable the bridge and specify an existing ConfigMap by its Name and +// Namespace containing a heisenbridge.yaml. +type HeisenbridgeSpec struct { + // Holds information about the ConfigMap containing the heisenbridge.yaml + // configuration file to be used as input for the configuration of the + // Heisenbridge IRC Bridge. + ConfigMap HeisenbridgeConfigMap `json:"configMap,omitempty"` + + // +kubebuilder:default:=0 + + // Controls the verbosity of the Heisenbrige: + // * 0 corresponds to normal level of logs + // * 1 corresponds to "-v" + // * 2 corresponds to "-vv" + // * 3 corresponds to "-vvv" + VerboseLevel int `json:"verboseLevel,omitempty"` + + // +kubebuilder:validation:Required + + // Name of the Synapse instance, living in the same namespace. + Synapse HeisenbridgeSynapseSpec `json:"synapse"` +} + +type HeisenbridgeSynapseSpec struct { + // Name of the Synapse instance + Name string `json:"name,omitempty"` + + // Namespace of the Synapse instance + // TODO: Complete + Namespace string `json:"namespace,omitempty"` +} + +type HeisenbridgeConfigMap struct { + // +kubebuilder:validation:Required + + // Name of the ConfigMap in the given Namespace. + Name string `json:"name"` + + // Namespace in which the ConfigMap is living. If left empty, the + // Heisenbridge namespace is used. + Namespace string `json:"namespace,omitempty"` +} + +// HeisenbridgeStatus defines the observed state of Heisenbridge +type HeisenbridgeStatus struct { + // State of the Heisenbridge instance + State string `json:"state,omitempty"` + + // Reason for the current Heisenbridge State + Reason string `json:"reason,omitempty"` +} + +//+kubebuilder:object:root=true +//+kubebuilder:subresource:status + +// Heisenbridge is the Schema for the heisenbridges API +type Heisenbridge struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec HeisenbridgeSpec `json:"spec,omitempty"` + Status HeisenbridgeStatus `json:"status,omitempty"` +} + +//+kubebuilder:object:root=true + +// HeisenbridgeList contains a list of Heisenbridge +type HeisenbridgeList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []Heisenbridge `json:"items"` +} + +func init() { + SchemeBuilder.Register(&Heisenbridge{}, &HeisenbridgeList{}) +} diff --git a/apis/synapse/v1alpha1/mautrixsignal_types.go b/apis/synapse/v1alpha1/mautrixsignal_types.go new file mode 100644 index 0000000..012cea1 --- /dev/null +++ b/apis/synapse/v1alpha1/mautrixsignal_types.go @@ -0,0 +1,102 @@ +/* +Copyright 2021. + +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 v1alpha1 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// MautrixSignalSpec defines the desired state of MautrixSignal. The user can +// either: +// - enable the bridge, without specifying additional configuration options. +// The bridge will be deployed with a default configuration. +// - enable the bridge and specify an existing ConfigMap by its Name and +// Namespace containing a config.yaml file. +type MautrixSignalSpec struct { + // Holds information about the ConfigMap containing the config.yaml + // configuration file to be used as input for the configuration of the + // mautrix-signal bridge. + ConfigMap MautrixSignalConfigMap `json:"configMap,omitempty"` + + // +kubebuilder:validation:Required + + // Name of the Synapse instance, living in the same namespace. + Synapse MautrixSignalSynapseSpec `json:"synapse"` +} + +type MautrixSignalSynapseSpec struct { + // +kubebuilder:validation:Required + + // Name of the Synapse instance + Name string `json:"name"` + + // Namespace of the Synapse instance + // TODO: Complete + Namespace string `json:"namespace,omitempty"` +} + +type MautrixSignalConfigMap struct { + // +kubebuilder:validation:Required + + // Name of the ConfigMap in the given Namespace. + Name string `json:"name"` + + // Namespace in which the ConfigMap is living. If left empty, the Synapse + // namespace is used. + Namespace string `json:"namespace,omitempty"` +} + +// MautrixSignalStatus defines the observed state of MautrixSignal +type MautrixSignalStatus struct { + // State of the MautrixSignal instance + State string `json:"state,omitempty"` + + // Reason for the current MautrixSignal State + Reason string `json:"reason,omitempty"` + + // Information related to the Synapse instance associated with this bridge + Synapse MautrixSignalStatusSynapse `json:"synapse,omitempty"` +} + +type MautrixSignalStatusSynapse struct { + ServerName string `json:"serverName,omitempty"` +} + +//+kubebuilder:object:root=true +//+kubebuilder:subresource:status + +// MautrixSignal is the Schema for the mautrixsignals API +type MautrixSignal struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec MautrixSignalSpec `json:"spec,omitempty"` + Status MautrixSignalStatus `json:"status,omitempty"` +} + +//+kubebuilder:object:root=true + +// MautrixSignalList contains a list of MautrixSignal +type MautrixSignalList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []MautrixSignal `json:"items"` +} + +func init() { + SchemeBuilder.Register(&MautrixSignal{}, &MautrixSignalList{}) +} diff --git a/apis/synapse/v1alpha1/synapse_types.go b/apis/synapse/v1alpha1/synapse_types.go index 85ebfa4..8a9d324 100644 --- a/apis/synapse/v1alpha1/synapse_types.go +++ b/apis/synapse/v1alpha1/synapse_types.go @@ -36,9 +36,6 @@ type SynapseSpec struct { // the creation of a configuration file from scratch. Homeserver SynapseHomeserver `json:"homeserver"` - // Configuration options for optional matrix bridges - Bridges SynapseBridges `json:"bridges,omitempty"` - // +kubebuilder:default:=false // Set to true to create a new PostreSQL instance. The homeserver.yaml @@ -80,97 +77,54 @@ type SynapseHomeserverValues struct { ReportStats bool `json:"reportStats"` } -type SynapseBridges struct { - // Configuration options for the IRC bridge Heisenbridge. The user can - // either: - // * disable the deployment of the bridge. - // * enable the bridge, without specifying additional configuration - // options. The bridge will be deployed with a default configuration. - // * enable the bridge and specify an existing ConfigMap by its Name and - // Namespace containing a heisenbridge.yaml. - Heisenbridge SynapseHeisenbridge `json:"heisenbridge,omitempty"` - - // Configuration options for the mautrix-signal bridge. The user can - // either: - // * disable the deployment of the bridge. - // * enable the bridge, without specifying additional configuration - // options. The bridge will be deployed with a default configuration. - // * enable the bridge and specify an existing ConfigMap by its Name and - // Namespace containing a config.yaml file. - MautrixSignal SynapseMautrixSignal `json:"mautrixSignal,omitempty"` -} +// 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 -type SynapseHeisenbridge struct { - // +kubebuilder:default:=false + // Connection information to the external PostgreSQL Database + DatabaseConnectionInfo SynapseStatusDatabaseConnectionInfo `json:"databaseConnectionInfo,omitempty"` - // Whether to deploy Heisenbridge or not - Enabled bool `json:"enabled,omitempty"` + // Holds configuration information for Synapse + HomeserverConfiguration SynapseStatusHomeserverConfiguration `json:"homeserverConfiguration,omitempty"` - // Holds information about the ConfigMap containing the heisenbridge.yaml - // configuration file to be used as input for the configuration of the - // Heisenbridge IRC Bridge. - ConfigMap SynapseHeisenbridgeConfigMap `json:"configMap,omitempty"` + // Information on the bridges deployed alongside Synapse + Bridges SynapseStatusBridges `json:"bridges,omitempty"` - // +kubebuilder:default:=0 + // State of the Synapse instance + State string `json:"state,omitempty"` + + // Reason for the current Synapse State + Reason string `json:"reason,omitempty"` - // Controls the verbosity of the Heisenbrige: - // * 0 corresponds to normal level of logs - // * 1 corresponds to "-v" - // * 2 corresponds to "-vv" - // * 3 corresponds to "-vvv" - VerboseLevel int `json:"verboseLevel,omitempty"` + // +kubebuilder:default:=false + NeedsReconcile bool `json:"needsReconcile,omitempty"` } -type SynapseHeisenbridgeConfigMap struct { - // +kubebuilder:validation:Required +type SynapseStatusBridges struct { + // Information on the Heisenbridge (IRC Bridge). + Heisenbridge SynapseStatusBridgesHeisenbridge `json:"heisenbridge,omitempty"` - // Name of the ConfigMap in the given Namespace. - Name string `json:"name"` - - // Namespace in which the ConfigMap is living. If left empty, the Synapse - // namespace is used. - Namespace string `json:"namespace,omitempty"` + // Information on the mautrix-signal bridge. + MautrixSignal SynapseStatusBridgesMautrixSignal `json:"mautrixsignal,omitempty"` } -type SynapseMautrixSignal struct { +type SynapseStatusBridgesHeisenbridge struct { // +kubebuilder:default:=false - // Whether to deploy mautrix-signal or not + // Whether a Heisenbridge has been deployed for this Synapse instance Enabled bool `json:"enabled,omitempty"` - // Holds information about the ConfigMap containing the config.yaml - // configuration file to be used as input for the configuration of the - // mautrix-signal Bridge. - ConfigMap SynapseMautrixSignalConfigMap `json:"configMap,omitempty"` + // Name of the Heisenbridge object + Name string `json:"name,omitempty"` } -type SynapseMautrixSignalConfigMap struct { - // +kubebuilder:validation:Required - - // Name of the ConfigMap in the given Namespace. - Name string `json:"name"` - - // Namespace in which the ConfigMap is living. If left empty, the Synapse - // namespace is used. - Namespace string `json:"namespace,omitempty"` -} - -// 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"` - - // State of the Synapse instance - State string `json:"state,omitempty"` +type SynapseStatusBridgesMautrixSignal struct { + // Whether a mautrix-signal has been deployed for this Synapse instance + Enabled bool `json:"enabled,omitempty"` - // Reason for the current Synapse State - Reason string `json:"reason,omitempty"` + // Name of the mautrix-signal bridge object + Name string `json:"name,omitempty"` } type SynapseStatusDatabaseConnectionInfo struct { diff --git a/apis/synapse/v1alpha1/zz_generated.deepcopy.go b/apis/synapse/v1alpha1/zz_generated.deepcopy.go index 40915b6..77917c8 100644 --- a/apis/synapse/v1alpha1/zz_generated.deepcopy.go +++ b/apis/synapse/v1alpha1/zz_generated.deepcopy.go @@ -26,26 +26,26 @@ import ( ) // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *Synapse) DeepCopyInto(out *Synapse) { +func (in *Heisenbridge) DeepCopyInto(out *Heisenbridge) { *out = *in out.TypeMeta = in.TypeMeta in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) - in.Spec.DeepCopyInto(&out.Spec) + out.Spec = in.Spec out.Status = in.Status } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Synapse. -func (in *Synapse) DeepCopy() *Synapse { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Heisenbridge. +func (in *Heisenbridge) DeepCopy() *Heisenbridge { if in == nil { return nil } - out := new(Synapse) + out := new(Heisenbridge) in.DeepCopyInto(out) return out } // DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *Synapse) DeepCopyObject() runtime.Object { +func (in *Heisenbridge) DeepCopyObject() runtime.Object { if c := in.DeepCopy(); c != nil { return c } @@ -53,53 +53,263 @@ func (in *Synapse) DeepCopyObject() runtime.Object { } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *SynapseBridges) DeepCopyInto(out *SynapseBridges) { +func (in *HeisenbridgeConfigMap) DeepCopyInto(out *HeisenbridgeConfigMap) { *out = *in - out.Heisenbridge = in.Heisenbridge - out.MautrixSignal = in.MautrixSignal } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SynapseBridges. -func (in *SynapseBridges) DeepCopy() *SynapseBridges { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HeisenbridgeConfigMap. +func (in *HeisenbridgeConfigMap) DeepCopy() *HeisenbridgeConfigMap { + if in == nil { + return nil + } + out := new(HeisenbridgeConfigMap) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *HeisenbridgeList) DeepCopyInto(out *HeisenbridgeList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]Heisenbridge, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HeisenbridgeList. +func (in *HeisenbridgeList) DeepCopy() *HeisenbridgeList { if in == nil { return nil } - out := new(SynapseBridges) + out := new(HeisenbridgeList) in.DeepCopyInto(out) return out } +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *HeisenbridgeList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *SynapseHeisenbridge) DeepCopyInto(out *SynapseHeisenbridge) { +func (in *HeisenbridgeSpec) DeepCopyInto(out *HeisenbridgeSpec) { *out = *in out.ConfigMap = in.ConfigMap + out.Synapse = in.Synapse } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SynapseHeisenbridge. -func (in *SynapseHeisenbridge) DeepCopy() *SynapseHeisenbridge { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HeisenbridgeSpec. +func (in *HeisenbridgeSpec) DeepCopy() *HeisenbridgeSpec { if in == nil { return nil } - out := new(SynapseHeisenbridge) + out := new(HeisenbridgeSpec) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *SynapseHeisenbridgeConfigMap) DeepCopyInto(out *SynapseHeisenbridgeConfigMap) { +func (in *HeisenbridgeStatus) DeepCopyInto(out *HeisenbridgeStatus) { *out = *in } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SynapseHeisenbridgeConfigMap. -func (in *SynapseHeisenbridgeConfigMap) DeepCopy() *SynapseHeisenbridgeConfigMap { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HeisenbridgeStatus. +func (in *HeisenbridgeStatus) DeepCopy() *HeisenbridgeStatus { if in == nil { return nil } - out := new(SynapseHeisenbridgeConfigMap) + out := new(HeisenbridgeStatus) in.DeepCopyInto(out) return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *HeisenbridgeSynapseSpec) DeepCopyInto(out *HeisenbridgeSynapseSpec) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HeisenbridgeSynapseSpec. +func (in *HeisenbridgeSynapseSpec) DeepCopy() *HeisenbridgeSynapseSpec { + if in == nil { + return nil + } + out := new(HeisenbridgeSynapseSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *MautrixSignal) DeepCopyInto(out *MautrixSignal) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + out.Spec = in.Spec + out.Status = in.Status +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MautrixSignal. +func (in *MautrixSignal) DeepCopy() *MautrixSignal { + if in == nil { + return nil + } + out := new(MautrixSignal) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *MautrixSignal) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *MautrixSignalConfigMap) DeepCopyInto(out *MautrixSignalConfigMap) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MautrixSignalConfigMap. +func (in *MautrixSignalConfigMap) DeepCopy() *MautrixSignalConfigMap { + if in == nil { + return nil + } + out := new(MautrixSignalConfigMap) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *MautrixSignalList) DeepCopyInto(out *MautrixSignalList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]MautrixSignal, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MautrixSignalList. +func (in *MautrixSignalList) DeepCopy() *MautrixSignalList { + if in == nil { + return nil + } + out := new(MautrixSignalList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *MautrixSignalList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *MautrixSignalSpec) DeepCopyInto(out *MautrixSignalSpec) { + *out = *in + out.ConfigMap = in.ConfigMap + out.Synapse = in.Synapse +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MautrixSignalSpec. +func (in *MautrixSignalSpec) DeepCopy() *MautrixSignalSpec { + if in == nil { + return nil + } + out := new(MautrixSignalSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *MautrixSignalStatus) DeepCopyInto(out *MautrixSignalStatus) { + *out = *in + out.Synapse = in.Synapse +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MautrixSignalStatus. +func (in *MautrixSignalStatus) DeepCopy() *MautrixSignalStatus { + if in == nil { + return nil + } + out := new(MautrixSignalStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *MautrixSignalStatusSynapse) DeepCopyInto(out *MautrixSignalStatusSynapse) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MautrixSignalStatusSynapse. +func (in *MautrixSignalStatusSynapse) DeepCopy() *MautrixSignalStatusSynapse { + if in == nil { + return nil + } + out := new(MautrixSignalStatusSynapse) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *MautrixSignalSynapseSpec) DeepCopyInto(out *MautrixSignalSynapseSpec) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MautrixSignalSynapseSpec. +func (in *MautrixSignalSynapseSpec) DeepCopy() *MautrixSignalSynapseSpec { + if in == nil { + return nil + } + out := new(MautrixSignalSynapseSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Synapse) DeepCopyInto(out *Synapse) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + out.Status = in.Status +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Synapse. +func (in *Synapse) DeepCopy() *Synapse { + if in == nil { + return nil + } + out := new(Synapse) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *Synapse) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *SynapseHomeserver) DeepCopyInto(out *SynapseHomeserver) { *out = *in @@ -188,66 +398,82 @@ func (in *SynapseList) DeepCopyObject() runtime.Object { } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *SynapseMautrixSignal) DeepCopyInto(out *SynapseMautrixSignal) { +func (in *SynapseSpec) DeepCopyInto(out *SynapseSpec) { *out = *in - out.ConfigMap = in.ConfigMap + in.Homeserver.DeepCopyInto(&out.Homeserver) } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SynapseMautrixSignal. -func (in *SynapseMautrixSignal) DeepCopy() *SynapseMautrixSignal { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SynapseSpec. +func (in *SynapseSpec) DeepCopy() *SynapseSpec { if in == nil { return nil } - out := new(SynapseMautrixSignal) + out := new(SynapseSpec) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *SynapseMautrixSignalConfigMap) DeepCopyInto(out *SynapseMautrixSignalConfigMap) { +func (in *SynapseStatus) DeepCopyInto(out *SynapseStatus) { *out = *in + out.DatabaseConnectionInfo = in.DatabaseConnectionInfo + out.HomeserverConfiguration = in.HomeserverConfiguration + out.Bridges = in.Bridges } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SynapseMautrixSignalConfigMap. -func (in *SynapseMautrixSignalConfigMap) DeepCopy() *SynapseMautrixSignalConfigMap { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SynapseStatus. +func (in *SynapseStatus) DeepCopy() *SynapseStatus { if in == nil { return nil } - out := new(SynapseMautrixSignalConfigMap) + out := new(SynapseStatus) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *SynapseSpec) DeepCopyInto(out *SynapseSpec) { +func (in *SynapseStatusBridges) DeepCopyInto(out *SynapseStatusBridges) { *out = *in - in.Homeserver.DeepCopyInto(&out.Homeserver) - out.Bridges = in.Bridges + out.Heisenbridge = in.Heisenbridge + out.MautrixSignal = in.MautrixSignal } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SynapseSpec. -func (in *SynapseSpec) DeepCopy() *SynapseSpec { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SynapseStatusBridges. +func (in *SynapseStatusBridges) DeepCopy() *SynapseStatusBridges { if in == nil { return nil } - out := new(SynapseSpec) + out := new(SynapseStatusBridges) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *SynapseStatus) DeepCopyInto(out *SynapseStatus) { +func (in *SynapseStatusBridgesHeisenbridge) DeepCopyInto(out *SynapseStatusBridgesHeisenbridge) { *out = *in - out.DatabaseConnectionInfo = in.DatabaseConnectionInfo - out.HomeserverConfiguration = in.HomeserverConfiguration } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SynapseStatus. -func (in *SynapseStatus) DeepCopy() *SynapseStatus { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SynapseStatusBridgesHeisenbridge. +func (in *SynapseStatusBridgesHeisenbridge) DeepCopy() *SynapseStatusBridgesHeisenbridge { if in == nil { return nil } - out := new(SynapseStatus) + out := new(SynapseStatusBridgesHeisenbridge) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *SynapseStatusBridgesMautrixSignal) DeepCopyInto(out *SynapseStatusBridgesMautrixSignal) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SynapseStatusBridgesMautrixSignal. +func (in *SynapseStatusBridgesMautrixSignal) DeepCopy() *SynapseStatusBridgesMautrixSignal { + if in == nil { + return nil + } + out := new(SynapseStatusBridgesMautrixSignal) in.DeepCopyInto(out) return out } diff --git a/bundle.Dockerfile b/bundle.Dockerfile index c9eba78..6a29a86 100644 --- a/bundle.Dockerfile +++ b/bundle.Dockerfile @@ -6,7 +6,7 @@ LABEL operators.operatorframework.io.bundle.manifests.v1=manifests/ LABEL operators.operatorframework.io.bundle.metadata.v1=metadata/ LABEL operators.operatorframework.io.bundle.package.v1=synapse-operator LABEL operators.operatorframework.io.bundle.channels.v1=alpha -LABEL operators.operatorframework.io.metrics.builder=operator-sdk-v1.14.0+git +LABEL operators.operatorframework.io.metrics.builder=operator-sdk-v1.25.0 LABEL operators.operatorframework.io.metrics.mediatype.v1=metrics+v1 LABEL operators.operatorframework.io.metrics.project_layout=go.kubebuilder.io/v3 diff --git a/bundle/manifests/synapse-operator.clusterserviceversion.yaml b/bundle/manifests/synapse-operator.clusterserviceversion.yaml index 2aece18..c85ef5e 100644 --- a/bundle/manifests/synapse-operator.clusterserviceversion.yaml +++ b/bundle/manifests/synapse-operator.clusterserviceversion.yaml @@ -4,6 +4,44 @@ metadata: annotations: alm-examples: |- [ + { + "apiVersion": "synapse.opdev.io/v1alpha1", + "kind": "Heisenbridge", + "metadata": { + "labels": { + "app.kubernetes.io/created-by": "synapse-operator", + "app.kubernetes.io/instance": "heisenbridge-sample", + "app.kubernetes.io/managed-by": "kustomize", + "app.kubernetes.io/name": "heisenbridge", + "app.kubernetes.io/part-of": "synapse-operator" + }, + "name": "heisenbridge-sample" + }, + "spec": { + "synapse": { + "name": "synapse-sample" + } + } + }, + { + "apiVersion": "synapse.opdev.io/v1alpha1", + "kind": "MautrixSignal", + "metadata": { + "labels": { + "app.kubernetes.io/created-by": "synapse-operator", + "app.kubernetes.io/instance": "mautrixsignal-sample", + "app.kubernetes.io/managed-by": "kustomize", + "app.kubernetes.io/name": "mautrixsignal", + "app.kubernetes.io/part-of": "synapse-operator" + }, + "name": "mautrixsignal-sample" + }, + "spec": { + "synapse": { + "name": "synapse-sample" + } + } + }, { "apiVersion": "synapse.opdev.io/v1alpha1", "kind": "Synapse", @@ -15,21 +53,31 @@ metadata: "homeserver": { "values": { "reportStats": true, - "serverName": "example.com" + "serverName": "my.matrix.host" } } } } ] capabilities: Basic Install - operators.operatorframework.io/builder: operator-sdk-v1.14.0+git + operators.operatorframework.io/builder: operator-sdk-v1.25.0 operators.operatorframework.io/project_layout: go.kubebuilder.io/v3 - name: synapse-operator.v0.1.0 + name: synapse-operator.v0.2.0 namespace: placeholder spec: apiservicedefinitions: {} customresourcedefinitions: owned: + - description: Heisenbridge is the Schema for the heisenbridges API + displayName: Heisenbridge + kind: Heisenbridge + name: heisenbridges.synapse.opdev.io + version: v1alpha1 + - description: MautrixSignal is the Schema for the mautrixsignals API + displayName: Mautrix Signal + kind: MautrixSignal + name: mautrixsignals.synapse.opdev.io + version: v1alpha1 - description: Synapse is the Schema for the synapses API displayName: Synapse kind: Synapse @@ -46,6 +94,58 @@ spec: spec: clusterPermissions: - rules: + - apiGroups: + - synapse.opdev.io + resources: + - heisenbridges + verbs: + - create + - delete + - get + - list + - patch + - update + - watch + - apiGroups: + - synapse.opdev.io + resources: + - heisenbridges/finalizers + verbs: + - update + - apiGroups: + - synapse.opdev.io + resources: + - heisenbridges/status + verbs: + - get + - patch + - update + - apiGroups: + - synapse.opdev.io + resources: + - mautrixsignals + verbs: + - create + - delete + - get + - list + - patch + - update + - watch + - apiGroups: + - synapse.opdev.io + resources: + - mautrixsignals/finalizers + verbs: + - update + - apiGroups: + - synapse.opdev.io + resources: + - mautrixsignals/status + verbs: + - get + - patch + - update - apiGroups: - synapse.opdev.io resources: @@ -86,7 +186,9 @@ spec: - create serviceAccountName: synapse-operator-controller-manager deployments: - - name: synapse-operator-controller-manager + - label: + control-plane: controller-manager + name: synapse-operator-controller-manager spec: replicas: 1 selector: @@ -117,7 +219,7 @@ spec: - --leader-elect command: - /manager - image: quay.io/opdev/synapse-operator:v0.1.0 + image: quay.io/opdev/synapse-operator:v0.2.0 livenessProbe: httpGet: path: /healthz @@ -200,4 +302,4 @@ spec: maturity: alpha provider: name: Opdev - version: 0.1.0 + version: 0.2.0 diff --git a/bundle/manifests/synapse.opdev.io_heisenbridges.yaml b/bundle/manifests/synapse.opdev.io_heisenbridges.yaml new file mode 100644 index 0000000..32decd3 --- /dev/null +++ b/bundle/manifests/synapse.opdev.io_heisenbridges.yaml @@ -0,0 +1,95 @@ +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.10.0 + creationTimestamp: null + name: heisenbridges.synapse.opdev.io +spec: + group: synapse.opdev.io + names: + kind: Heisenbridge + listKind: HeisenbridgeList + plural: heisenbridges + singular: heisenbridge + scope: Namespaced + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + description: Heisenbridge is the Schema for the heisenbridges API + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: 'HeisenbridgeSpec defines the desired state of Heisenbridge. + The user can either: - enable the bridge, without specifying additional + configuration options. The bridge will be deployed with a default configuration. + - enable the bridge and specify an existing ConfigMap by its Name and + Namespace containing a heisenbridge.yaml.' + properties: + configMap: + description: Holds information about the ConfigMap containing the + heisenbridge.yaml configuration file to be used as input for the + configuration of the Heisenbridge IRC Bridge. + properties: + name: + description: Name of the ConfigMap in the given Namespace. + type: string + namespace: + description: Namespace in which the ConfigMap is living. If left + empty, the Heisenbridge namespace is used. + type: string + required: + - name + type: object + synapse: + description: Name of the Synapse instance, living in the same namespace. + properties: + name: + description: Name of the Synapse instance + type: string + namespace: + description: 'Namespace of the Synapse instance TODO: Complete' + type: string + type: object + verboseLevel: + default: 0 + description: 'Controls the verbosity of the Heisenbrige: * 0 corresponds + to normal level of logs * 1 corresponds to "-v" * 2 corresponds + to "-vv" * 3 corresponds to "-vvv"' + type: integer + required: + - synapse + type: object + status: + description: HeisenbridgeStatus defines the observed state of Heisenbridge + properties: + reason: + description: Reason for the current Heisenbridge State + type: string + state: + description: State of the Heisenbridge instance + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: null + storedVersions: null diff --git a/bundle/manifests/synapse.opdev.io_mautrixsignals.yaml b/bundle/manifests/synapse.opdev.io_mautrixsignals.yaml new file mode 100644 index 0000000..dfdc262 --- /dev/null +++ b/bundle/manifests/synapse.opdev.io_mautrixsignals.yaml @@ -0,0 +1,98 @@ +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.10.0 + creationTimestamp: null + name: mautrixsignals.synapse.opdev.io +spec: + group: synapse.opdev.io + names: + kind: MautrixSignal + listKind: MautrixSignalList + plural: mautrixsignals + singular: mautrixsignal + scope: Namespaced + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + description: MautrixSignal is the Schema for the mautrixsignals API + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: 'MautrixSignalSpec defines the desired state of MautrixSignal. + The user can either: - enable the bridge, without specifying additional + configuration options. The bridge will be deployed with a default configuration. + - enable the bridge and specify an existing ConfigMap by its Name and + Namespace containing a config.yaml file.' + properties: + configMap: + description: Holds information about the ConfigMap containing the + config.yaml configuration file to be used as input for the configuration + of the mautrix-signal bridge. + properties: + name: + description: Name of the ConfigMap in the given Namespace. + type: string + namespace: + description: Namespace in which the ConfigMap is living. If left + empty, the Synapse namespace is used. + type: string + required: + - name + type: object + synapse: + description: Name of the Synapse instance, living in the same namespace. + properties: + name: + description: Name of the Synapse instance + type: string + namespace: + description: 'Namespace of the Synapse instance TODO: Complete' + type: string + required: + - name + type: object + required: + - synapse + type: object + status: + description: MautrixSignalStatus defines the observed state of MautrixSignal + properties: + reason: + description: Reason for the current MautrixSignal State + type: string + state: + description: State of the MautrixSignal instance + type: string + synapse: + description: Information related to the Synapse instance associated + with this bridge + properties: + serverName: + type: string + type: object + type: object + type: object + served: true + storage: true + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: null + storedVersions: null diff --git a/bundle/manifests/synapse.opdev.io_synapses.yaml b/bundle/manifests/synapse.opdev.io_synapses.yaml index 8d1d27e..454850c 100644 --- a/bundle/manifests/synapse.opdev.io_synapses.yaml +++ b/bundle/manifests/synapse.opdev.io_synapses.yaml @@ -2,7 +2,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.6.1 + controller-gen.kubebuilder.io/version: v0.10.0 creationTimestamp: null name: synapses.synapse.opdev.io spec: @@ -34,72 +34,6 @@ spec: spec: description: SynapseSpec defines the desired state of Synapse properties: - bridges: - description: Configuration options for optional matrix bridges - properties: - heisenbridge: - description: 'Configuration options for the IRC bridge Heisenbridge. - The user can either: * disable the deployment of the bridge. - * enable the bridge, without specifying additional configuration options. - The bridge will be deployed with a default configuration. * - enable the bridge and specify an existing ConfigMap by its Name - and Namespace containing a heisenbridge.yaml.' - properties: - configMap: - description: Holds information about the ConfigMap containing - the heisenbridge.yaml configuration file to be used as input - for the configuration of the Heisenbridge IRC Bridge. - properties: - name: - description: Name of the ConfigMap in the given Namespace. - type: string - namespace: - description: Namespace in which the ConfigMap is living. - If left empty, the Synapse namespace is used. - type: string - required: - - name - type: object - enabled: - default: false - description: Whether to deploy Heisenbridge or not - type: boolean - verboseLevel: - default: 0 - description: 'Controls the verbosity of the Heisenbrige: * - 0 corresponds to normal level of logs * 1 corresponds to - "-v" * 2 corresponds to "-vv" * 3 corresponds to "-vvv"' - type: integer - type: object - mautrixSignal: - description: 'Configuration options for the mautrix-signal bridge. - The user can either: * disable the deployment of the bridge. - * enable the bridge, without specifying additional configuration options. - The bridge will be deployed with a default configuration. * - enable the bridge and specify an existing ConfigMap by its Name - and Namespace containing a config.yaml file.' - properties: - configMap: - description: Holds information about the ConfigMap containing - the config.yaml configuration file to be used as input for - the configuration of the mautrix-signal Bridge. - properties: - name: - description: Name of the ConfigMap in the given Namespace. - type: string - namespace: - description: Namespace in which the ConfigMap is living. - If left empty, the Synapse namespace is used. - type: string - required: - - name - type: object - enabled: - default: false - description: Whether to deploy mautrix-signal or not - type: boolean - type: object - type: object createNewPostgreSQL: default: false description: Set to true to create a new PostreSQL instance. The homeserver.yaml @@ -153,6 +87,33 @@ spec: status: description: SynapseStatus defines the observed state of Synapse properties: + bridges: + description: Information on the bridges deployed alongside Synapse + properties: + heisenbridge: + description: Information on the Heisenbridge (IRC Bridge). + properties: + enabled: + default: false + description: Whether a Heisenbridge has been deployed for + this Synapse instance + type: boolean + name: + description: Name of the Heisenbridge object + type: string + type: object + mautrixsignal: + description: Information on the mautrix-signal bridge. + properties: + enabled: + description: Whether a mautrix-signal has been deployed for + this Synapse instance + type: boolean + name: + description: Name of the mautrix-signal bridge object + type: string + type: object + type: object databaseConnectionInfo: description: Connection information to the external PostgreSQL Database properties: @@ -183,6 +144,9 @@ spec: description: The public-facing domain of the server type: string type: object + needsReconcile: + default: false + type: boolean reason: description: Reason for the current Synapse State type: string @@ -201,5 +165,5 @@ status: acceptedNames: kind: "" plural: "" - conditions: [] - storedVersions: [] + conditions: null + storedVersions: null diff --git a/bundle/metadata/annotations.yaml b/bundle/metadata/annotations.yaml index 9dba3c9..a85279d 100644 --- a/bundle/metadata/annotations.yaml +++ b/bundle/metadata/annotations.yaml @@ -5,7 +5,7 @@ annotations: operators.operatorframework.io.bundle.metadata.v1: metadata/ operators.operatorframework.io.bundle.package.v1: synapse-operator operators.operatorframework.io.bundle.channels.v1: alpha - operators.operatorframework.io.metrics.builder: operator-sdk-v1.14.0+git + operators.operatorframework.io.metrics.builder: operator-sdk-v1.25.0 operators.operatorframework.io.metrics.mediatype.v1: metrics+v1 operators.operatorframework.io.metrics.project_layout: go.kubebuilder.io/v3 diff --git a/config/crd/bases/synapse.opdev.io_heisenbridges.yaml b/config/crd/bases/synapse.opdev.io_heisenbridges.yaml new file mode 100644 index 0000000..1578fbb --- /dev/null +++ b/config/crd/bases/synapse.opdev.io_heisenbridges.yaml @@ -0,0 +1,90 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.10.0 + creationTimestamp: null + name: heisenbridges.synapse.opdev.io +spec: + group: synapse.opdev.io + names: + kind: Heisenbridge + listKind: HeisenbridgeList + plural: heisenbridges + singular: heisenbridge + scope: Namespaced + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + description: Heisenbridge is the Schema for the heisenbridges API + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: 'HeisenbridgeSpec defines the desired state of Heisenbridge. + The user can either: - enable the bridge, without specifying additional + configuration options. The bridge will be deployed with a default configuration. + - enable the bridge and specify an existing ConfigMap by its Name and + Namespace containing a heisenbridge.yaml.' + properties: + configMap: + description: Holds information about the ConfigMap containing the + heisenbridge.yaml configuration file to be used as input for the + configuration of the Heisenbridge IRC Bridge. + properties: + name: + description: Name of the ConfigMap in the given Namespace. + type: string + namespace: + description: Namespace in which the ConfigMap is living. If left + empty, the Heisenbridge namespace is used. + type: string + required: + - name + type: object + synapse: + description: Name of the Synapse instance, living in the same namespace. + properties: + name: + description: Name of the Synapse instance + type: string + namespace: + description: 'Namespace of the Synapse instance TODO: Complete' + type: string + type: object + verboseLevel: + default: 0 + description: 'Controls the verbosity of the Heisenbrige: * 0 corresponds + to normal level of logs * 1 corresponds to "-v" * 2 corresponds + to "-vv" * 3 corresponds to "-vvv"' + type: integer + required: + - synapse + type: object + status: + description: HeisenbridgeStatus defines the observed state of Heisenbridge + properties: + reason: + description: Reason for the current Heisenbridge State + type: string + state: + description: State of the Heisenbridge instance + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} diff --git a/config/crd/bases/synapse.opdev.io_mautrixsignals.yaml b/config/crd/bases/synapse.opdev.io_mautrixsignals.yaml new file mode 100644 index 0000000..df70552 --- /dev/null +++ b/config/crd/bases/synapse.opdev.io_mautrixsignals.yaml @@ -0,0 +1,93 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.10.0 + creationTimestamp: null + name: mautrixsignals.synapse.opdev.io +spec: + group: synapse.opdev.io + names: + kind: MautrixSignal + listKind: MautrixSignalList + plural: mautrixsignals + singular: mautrixsignal + scope: Namespaced + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + description: MautrixSignal is the Schema for the mautrixsignals API + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: 'MautrixSignalSpec defines the desired state of MautrixSignal. + The user can either: - enable the bridge, without specifying additional + configuration options. The bridge will be deployed with a default configuration. + - enable the bridge and specify an existing ConfigMap by its Name and + Namespace containing a config.yaml file.' + properties: + configMap: + description: Holds information about the ConfigMap containing the + config.yaml configuration file to be used as input for the configuration + of the mautrix-signal bridge. + properties: + name: + description: Name of the ConfigMap in the given Namespace. + type: string + namespace: + description: Namespace in which the ConfigMap is living. If left + empty, the Synapse namespace is used. + type: string + required: + - name + type: object + synapse: + description: Name of the Synapse instance, living in the same namespace. + properties: + name: + description: Name of the Synapse instance + type: string + namespace: + description: 'Namespace of the Synapse instance TODO: Complete' + type: string + required: + - name + type: object + required: + - synapse + type: object + status: + description: MautrixSignalStatus defines the observed state of MautrixSignal + properties: + reason: + description: Reason for the current MautrixSignal State + type: string + state: + description: State of the MautrixSignal instance + type: string + synapse: + description: Information related to the Synapse instance associated + with this bridge + properties: + serverName: + type: string + type: object + type: object + type: object + served: true + storage: true + subresources: + status: {} diff --git a/config/crd/bases/synapse.opdev.io_synapses.yaml b/config/crd/bases/synapse.opdev.io_synapses.yaml index bb67b29..28d01cd 100644 --- a/config/crd/bases/synapse.opdev.io_synapses.yaml +++ b/config/crd/bases/synapse.opdev.io_synapses.yaml @@ -35,72 +35,6 @@ spec: spec: description: SynapseSpec defines the desired state of Synapse properties: - bridges: - description: Configuration options for optional matrix bridges - properties: - heisenbridge: - description: 'Configuration options for the IRC bridge Heisenbridge. - The user can either: * disable the deployment of the bridge. - * enable the bridge, without specifying additional configuration - options. The bridge will be deployed with a default configuration. - * enable the bridge and specify an existing ConfigMap by its - Name and Namespace containing a heisenbridge.yaml.' - properties: - configMap: - description: Holds information about the ConfigMap containing - the heisenbridge.yaml configuration file to be used as input - for the configuration of the Heisenbridge IRC Bridge. - properties: - name: - description: Name of the ConfigMap in the given Namespace. - type: string - namespace: - description: Namespace in which the ConfigMap is living. - If left empty, the Synapse namespace is used. - type: string - required: - - name - type: object - enabled: - default: false - description: Whether to deploy Heisenbridge or not - type: boolean - verboseLevel: - default: 0 - description: 'Controls the verbosity of the Heisenbrige: * - 0 corresponds to normal level of logs * 1 corresponds to - "-v" * 2 corresponds to "-vv" * 3 corresponds to "-vvv"' - type: integer - type: object - mautrixSignal: - description: 'Configuration options for the mautrix-signal bridge. - The user can either: * disable the deployment of the bridge. - * enable the bridge, without specifying additional configuration - options. The bridge will be deployed with a default configuration. - * enable the bridge and specify an existing ConfigMap by its - Name and Namespace containing a config.yaml file.' - properties: - configMap: - description: Holds information about the ConfigMap containing - the config.yaml configuration file to be used as input for - the configuration of the mautrix-signal Bridge. - properties: - name: - description: Name of the ConfigMap in the given Namespace. - type: string - namespace: - description: Namespace in which the ConfigMap is living. - If left empty, the Synapse namespace is used. - type: string - required: - - name - type: object - enabled: - default: false - description: Whether to deploy mautrix-signal or not - type: boolean - type: object - type: object createNewPostgreSQL: default: false description: Set to true to create a new PostreSQL instance. The homeserver.yaml @@ -149,6 +83,33 @@ spec: status: description: SynapseStatus defines the observed state of Synapse properties: + bridges: + description: Information on the bridges deployed alongside Synapse + properties: + heisenbridge: + description: Information on the Heisenbridge (IRC Bridge). + properties: + enabled: + default: false + description: Whether a Heisenbridge has been deployed for + this Synapse instance + type: boolean + name: + description: Name of the Heisenbridge object + type: string + type: object + mautrixsignal: + description: Information on the mautrix-signal bridge. + properties: + enabled: + description: Whether a mautrix-signal has been deployed for + this Synapse instance + type: boolean + name: + description: Name of the mautrix-signal bridge object + type: string + type: object + type: object databaseConnectionInfo: description: Connection information to the external PostgreSQL Database properties: @@ -179,6 +140,9 @@ spec: description: The public-facing domain of the server type: string type: object + needsReconcile: + default: false + type: boolean reason: description: Reason for the current Synapse State type: string diff --git a/config/crd/kustomization.yaml b/config/crd/kustomization.yaml index d7cfc2e..4438c1a 100644 --- a/config/crd/kustomization.yaml +++ b/config/crd/kustomization.yaml @@ -3,6 +3,8 @@ # It should be run by config/default resources: - bases/synapse.opdev.io_synapses.yaml +- bases/synapse.opdev.io_mautrixsignals.yaml +- bases/synapse.opdev.io_heisenbridges.yaml #+kubebuilder:scaffold:crdkustomizeresource patches: @@ -17,11 +19,15 @@ patchesStrategicMerge: # [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix. # patches here are for enabling the conversion webhook for each CRD #- patches/webhook_in_synapses.yaml +#- patches/webhook_in_mautrixsignals.yaml +#- patches/webhook_in_heisenbridges.yaml #+kubebuilder:scaffold:crdkustomizewebhookpatch # [CERTMANAGER] To enable cert-manager, uncomment all the sections with [CERTMANAGER] prefix. # patches here are for enabling the CA injection for each CRD #- patches/cainjection_in_synapses.yaml +#- patches/cainjection_in_mautrixsignals.yaml +#- patches/cainjection_in_heisenbridges.yaml #+kubebuilder:scaffold:crdkustomizecainjectionpatch # the following config is for teaching kustomize how to do kustomization for CRDs. diff --git a/config/crd/patches/cainjection_in_synapse_heisenbridges.yaml b/config/crd/patches/cainjection_in_synapse_heisenbridges.yaml new file mode 100644 index 0000000..d048b0c --- /dev/null +++ b/config/crd/patches/cainjection_in_synapse_heisenbridges.yaml @@ -0,0 +1,7 @@ +# The following patch adds a directive for certmanager to inject CA into the CRD +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME) + name: heisenbridges.synapse.opdev.io diff --git a/config/crd/patches/cainjection_in_synapse_mautrixsignals.yaml b/config/crd/patches/cainjection_in_synapse_mautrixsignals.yaml new file mode 100644 index 0000000..4bff4e0 --- /dev/null +++ b/config/crd/patches/cainjection_in_synapse_mautrixsignals.yaml @@ -0,0 +1,7 @@ +# The following patch adds a directive for certmanager to inject CA into the CRD +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME) + name: mautrixsignals.synapse.opdev.io diff --git a/config/crd/patches/webhook_in_synapse_heisenbridges.yaml b/config/crd/patches/webhook_in_synapse_heisenbridges.yaml new file mode 100644 index 0000000..c889d34 --- /dev/null +++ b/config/crd/patches/webhook_in_synapse_heisenbridges.yaml @@ -0,0 +1,16 @@ +# The following patch enables a conversion webhook for the CRD +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + name: heisenbridges.synapse.opdev.io +spec: + conversion: + strategy: Webhook + webhook: + clientConfig: + service: + namespace: system + name: webhook-service + path: /convert + conversionReviewVersions: + - v1 diff --git a/config/crd/patches/webhook_in_synapse_mautrixsignals.yaml b/config/crd/patches/webhook_in_synapse_mautrixsignals.yaml new file mode 100644 index 0000000..4f3fadd --- /dev/null +++ b/config/crd/patches/webhook_in_synapse_mautrixsignals.yaml @@ -0,0 +1,16 @@ +# The following patch enables a conversion webhook for the CRD +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + name: mautrixsignals.synapse.opdev.io +spec: + conversion: + strategy: Webhook + webhook: + clientConfig: + service: + namespace: system + name: webhook-service + path: /convert + conversionReviewVersions: + - v1 diff --git a/config/manifests/bases/synapse-operator.clusterserviceversion.yaml b/config/manifests/bases/synapse-operator.clusterserviceversion.yaml index 3845cac..f8c7fa5 100644 --- a/config/manifests/bases/synapse-operator.clusterserviceversion.yaml +++ b/config/manifests/bases/synapse-operator.clusterserviceversion.yaml @@ -10,6 +10,16 @@ spec: apiservicedefinitions: {} customresourcedefinitions: owned: + - description: Heisenbridge is the Schema for the heisenbridges API + displayName: Heisenbridge + kind: Heisenbridge + name: heisenbridges.synapse.opdev.io + version: v1alpha1 + - description: MautrixSignal is the Schema for the mautrixsignals API + displayName: Mautrix Signal + kind: MautrixSignal + name: mautrixsignals.synapse.opdev.io + version: v1alpha1 - description: Synapse is the Schema for the synapses API displayName: Synapse kind: Synapse diff --git a/config/rbac/role.yaml b/config/rbac/role.yaml index b8f2b0a..ae59655 100644 --- a/config/rbac/role.yaml +++ b/config/rbac/role.yaml @@ -5,6 +5,58 @@ metadata: creationTimestamp: null name: manager-role rules: +- apiGroups: + - synapse.opdev.io + resources: + - heisenbridges + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - synapse.opdev.io + resources: + - heisenbridges/finalizers + verbs: + - update +- apiGroups: + - synapse.opdev.io + resources: + - heisenbridges/status + verbs: + - get + - patch + - update +- apiGroups: + - synapse.opdev.io + resources: + - mautrixsignals + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - synapse.opdev.io + resources: + - mautrixsignals/finalizers + verbs: + - update +- apiGroups: + - synapse.opdev.io + resources: + - mautrixsignals/status + verbs: + - get + - patch + - update - apiGroups: - synapse.opdev.io resources: diff --git a/config/rbac/synapse_heisenbridge_editor_role.yaml b/config/rbac/synapse_heisenbridge_editor_role.yaml new file mode 100644 index 0000000..8bd42d4 --- /dev/null +++ b/config/rbac/synapse_heisenbridge_editor_role.yaml @@ -0,0 +1,31 @@ +# permissions for end users to edit heisenbridges. +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: + app.kubernetes.io/name: clusterrole + app.kubernetes.io/instance: heisenbridge-editor-role + app.kubernetes.io/component: rbac + app.kubernetes.io/created-by: synapse-operator + app.kubernetes.io/part-of: synapse-operator + app.kubernetes.io/managed-by: kustomize + name: heisenbridge-editor-role +rules: +- apiGroups: + - synapse.opdev.io + resources: + - heisenbridges + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - synapse.opdev.io + resources: + - heisenbridges/status + verbs: + - get diff --git a/config/rbac/synapse_heisenbridge_viewer_role.yaml b/config/rbac/synapse_heisenbridge_viewer_role.yaml new file mode 100644 index 0000000..223501c --- /dev/null +++ b/config/rbac/synapse_heisenbridge_viewer_role.yaml @@ -0,0 +1,27 @@ +# permissions for end users to view heisenbridges. +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: + app.kubernetes.io/name: clusterrole + app.kubernetes.io/instance: heisenbridge-viewer-role + app.kubernetes.io/component: rbac + app.kubernetes.io/created-by: synapse-operator + app.kubernetes.io/part-of: synapse-operator + app.kubernetes.io/managed-by: kustomize + name: heisenbridge-viewer-role +rules: +- apiGroups: + - synapse.opdev.io + resources: + - heisenbridges + verbs: + - get + - list + - watch +- apiGroups: + - synapse.opdev.io + resources: + - heisenbridges/status + verbs: + - get diff --git a/config/rbac/synapse_mautrixsignal_editor_role.yaml b/config/rbac/synapse_mautrixsignal_editor_role.yaml new file mode 100644 index 0000000..edacdf5 --- /dev/null +++ b/config/rbac/synapse_mautrixsignal_editor_role.yaml @@ -0,0 +1,31 @@ +# permissions for end users to edit mautrixsignals. +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: + app.kubernetes.io/name: clusterrole + app.kubernetes.io/instance: mautrixsignal-editor-role + app.kubernetes.io/component: rbac + app.kubernetes.io/created-by: synapse-operator + app.kubernetes.io/part-of: synapse-operator + app.kubernetes.io/managed-by: kustomize + name: mautrixsignal-editor-role +rules: +- apiGroups: + - synapse.opdev.io + resources: + - mautrixsignals + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - synapse.opdev.io + resources: + - mautrixsignals/status + verbs: + - get diff --git a/config/rbac/synapse_mautrixsignal_viewer_role.yaml b/config/rbac/synapse_mautrixsignal_viewer_role.yaml new file mode 100644 index 0000000..d86d935 --- /dev/null +++ b/config/rbac/synapse_mautrixsignal_viewer_role.yaml @@ -0,0 +1,27 @@ +# permissions for end users to view mautrixsignals. +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: + app.kubernetes.io/name: clusterrole + app.kubernetes.io/instance: mautrixsignal-viewer-role + app.kubernetes.io/component: rbac + app.kubernetes.io/created-by: synapse-operator + app.kubernetes.io/part-of: synapse-operator + app.kubernetes.io/managed-by: kustomize + name: mautrixsignal-viewer-role +rules: +- apiGroups: + - synapse.opdev.io + resources: + - mautrixsignals + verbs: + - get + - list + - watch +- apiGroups: + - synapse.opdev.io + resources: + - mautrixsignals/status + verbs: + - get diff --git a/config/samples/kustomization.yaml b/config/samples/kustomization.yaml index 2fbaf58..00091a4 100644 --- a/config/samples/kustomization.yaml +++ b/config/samples/kustomization.yaml @@ -1,4 +1,6 @@ ## Append samples you want in your CSV to this file as resources ## resources: - synapse_v1alpha1_synapse.yaml +- synapse_v1alpha1_mautrixsignal.yaml +- synapse_v1alpha1_heisenbridge.yaml #+kubebuilder:scaffold:manifestskustomizesamples diff --git a/config/samples/synapse_v1alpha1_heisenbridge.yaml b/config/samples/synapse_v1alpha1_heisenbridge.yaml new file mode 100644 index 0000000..e30d004 --- /dev/null +++ b/config/samples/synapse_v1alpha1_heisenbridge.yaml @@ -0,0 +1,13 @@ +apiVersion: synapse.opdev.io/v1alpha1 +kind: Heisenbridge +metadata: + labels: + app.kubernetes.io/name: heisenbridge + app.kubernetes.io/instance: heisenbridge-sample + app.kubernetes.io/part-of: synapse-operator + app.kubernetes.io/managed-by: kustomize + app.kubernetes.io/created-by: synapse-operator + name: heisenbridge-sample +spec: + synapse: + name: synapse-sample diff --git a/config/samples/synapse_v1alpha1_mautrixsignal.yaml b/config/samples/synapse_v1alpha1_mautrixsignal.yaml new file mode 100644 index 0000000..49e45cc --- /dev/null +++ b/config/samples/synapse_v1alpha1_mautrixsignal.yaml @@ -0,0 +1,13 @@ +apiVersion: synapse.opdev.io/v1alpha1 +kind: MautrixSignal +metadata: + labels: + app.kubernetes.io/name: mautrixsignal + app.kubernetes.io/instance: mautrixsignal-sample + app.kubernetes.io/part-of: synapse-operator + app.kubernetes.io/managed-by: kustomize + app.kubernetes.io/created-by: synapse-operator + name: mautrixsignal-sample +spec: + synapse: + name: synapse-sample diff --git a/config/samples/synapse_v1alpha1_synapse.yaml b/config/samples/synapse_v1alpha1_synapse.yaml index e7479b0..1031cae 100644 --- a/config/samples/synapse_v1alpha1_synapse.yaml +++ b/config/samples/synapse_v1alpha1_synapse.yaml @@ -6,5 +6,5 @@ spec: createNewPostgreSQL: false homeserver: values: - serverName: example.com + serverName: my.matrix.host reportStats: true diff --git a/controllers/synapse/heisenbridge/heisenbridge_controller.go b/controllers/synapse/heisenbridge/heisenbridge_controller.go new file mode 100644 index 0000000..4c22644 --- /dev/null +++ b/controllers/synapse/heisenbridge/heisenbridge_controller.go @@ -0,0 +1,222 @@ +/* +Copyright 2021. + +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" + "reflect" + "strings" + + k8serrors "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/types" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + ctrllog "sigs.k8s.io/controller-runtime/pkg/log" + + synapsev1alpha1 "github.com/opdev/synapse-operator/apis/synapse/v1alpha1" + reconc "github.com/opdev/synapse-operator/helpers/reconcileresults" + "github.com/opdev/synapse-operator/helpers/utils" +) + +// HeisenbridgeReconciler reconciles a Heisenbridge object +type HeisenbridgeReconciler struct { + client.Client + Scheme *runtime.Scheme +} + +func GetHeisenbridgeServiceFQDN(h synapsev1alpha1.Heisenbridge) string { + return strings.Join([]string{h.Name, h.Namespace, "svc", "cluster", "local"}, ".") +} + +//+kubebuilder:rbac:groups=synapse.opdev.io,resources=heisenbridges,verbs=get;list;watch;create;update;patch;delete +//+kubebuilder:rbac:groups=synapse.opdev.io,resources=heisenbridges/status,verbs=get;update;patch +//+kubebuilder:rbac:groups=synapse.opdev.io,resources=heisenbridges/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 Heisenbridge 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.13.0/pkg/reconcile +func (r *HeisenbridgeReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { + log := ctrllog.FromContext(ctx) + + var h synapsev1alpha1.Heisenbridge // The Heisenbridge object being reconciled + + // Load the Heisenbridge by name + if err := r.Get(ctx, req.NamespacedName, &h); err != nil { + if k8serrors.IsNotFound(err) { + // we'll ignore not-found errors, since they can't be fixed by an immediate + // requeue (we'll need to wait for a new notification), and we can get them + // on deleted requests. + log.Error( + err, + "Cannot find Heisenbridge - has it been deleted ?", + "Heisenbridge Name", h.Name, + "Heisenbridge Namespace", h.Namespace, + ) + return ctrl.Result{}, nil + } + log.Error( + err, + "Error fetching Heisenbridge", + "Heisenbridge Name", h.Name, + "Heisenbridge Namespace", h.Namespace, + ) + return ctrl.Result{}, client.IgnoreNotFound(err) + } + + // Build mautrix-signal status + s, err := r.fetchSynapseInstance(ctx, h) + if err != nil { + if k8serrors.IsNotFound(err) { + log.Error( + err, + "Cannot find Synapse instance", + "Synapse Name", h.Spec.Synapse.Name, + "Synapse Namespace", utils.ComputeNamespace(h.Namespace, h.Spec.Synapse.Namespace), + ) + } else { + log.Error( + err, + "Error getting Synapse server name", + "Synapse Name", h.Spec.Synapse.Name, + "Synapse Namespace", utils.ComputeNamespace(h.Namespace, h.Spec.Synapse.Namespace), + ) + } + return ctrl.Result{}, err + } + + if r, err := r.triggerSynapseReconciliation(&s, ctx); reconc.ShouldHaltOrRequeue(r, err) { + return reconc.Evaluate(r, err) + } + + // The list of subreconcilers for Heisenbridge will be built next. + // Heisenbridge is composed of a ConfigMap, a Service and a Deployment. + var subreconcilersForHeisenbridge []reconc.SubreconcilerFuncs + + // The user may specify a ConfigMap, containing the heisenbridge.yaml + // config file, under Spec.Bridges.Heisenbridge.ConfigMap + if h.Spec.ConfigMap.Name != "" { + // If the user provided a custom Heisenbridge configuration via a + // ConfigMap, we need to validate that the ConfigMap exists, and + // create a copy. We also need to edit the heisenbridge + // configuration. + subreconcilersForHeisenbridge = []reconc.SubreconcilerFuncs{ + r.copyInputHeisenbridgeConfigMap, + r.configureHeisenbridgeConfigMap, + } + } else { + // If the user hasn't provided a ConfigMap with a custom + // heisenbridge.yaml, we create a new ConfigMap with a default + // heisenbridge.yaml. + subreconcilersForHeisenbridge = []reconc.SubreconcilerFuncs{ + r.reconcileHeisenbridgeConfigMap, + } + } + + // Reconcile Heisenbridge resources: Service and Deployment + subreconcilersForHeisenbridge = append( + subreconcilersForHeisenbridge, + r.reconcileHeisenbridgeService, + r.reconcileHeisenbridgeDeployment, + ) + + for _, f := range subreconcilersForHeisenbridge { + if r, err := f(&h, ctx); reconc.ShouldHaltOrRequeue(r, err) { + return reconc.Evaluate(r, err) + } + } + + return ctrl.Result{}, nil +} + +func (r *HeisenbridgeReconciler) fetchSynapseInstance( + ctx context.Context, + h synapsev1alpha1.Heisenbridge, +) (synapsev1alpha1.Synapse, error) { + // Validate Synapse instance exists + s := &synapsev1alpha1.Synapse{} + keyForSynapse := types.NamespacedName{ + Name: h.Spec.Synapse.Name, + Namespace: utils.ComputeNamespace(h.Namespace, h.Spec.Synapse.Namespace), + } + if err := r.Get(ctx, keyForSynapse, s); err != nil { + return synapsev1alpha1.Synapse{}, err + } + + return *s, nil +} + +func (r *HeisenbridgeReconciler) triggerSynapseReconciliation(i interface{}, ctx context.Context) (*ctrl.Result, error) { + s := i.(*synapsev1alpha1.Synapse) + s.Status.NeedsReconcile = true + + current := &synapsev1alpha1.Synapse{} + if err := r.Get( + ctx, + types.NamespacedName{Name: s.Name, Namespace: s.Namespace}, + current, + ); err != nil { + return reconc.RequeueWithError(err) + } + + if !reflect.DeepEqual(s.Status, current.Status) { + if err := r.Status().Patch(ctx, s, client.MergeFrom(current)); err != nil { + return reconc.RequeueWithError(err) + } + } + + return reconc.ContinueReconciling() +} + +func (r *HeisenbridgeReconciler) setFailedState(ctx context.Context, h *synapsev1alpha1.Heisenbridge, reason string) error { + h.Status.State = "FAILED" + h.Status.Reason = reason + + return r.updateHeisenbridgeStatus(ctx, h) +} + +func (r *HeisenbridgeReconciler) updateHeisenbridgeStatus(ctx context.Context, h *synapsev1alpha1.Heisenbridge) error { + current := &synapsev1alpha1.Heisenbridge{} + if err := r.Get( + ctx, + types.NamespacedName{Name: h.Name, Namespace: h.Namespace}, + current, + ); err != nil { + return err + } + + if !reflect.DeepEqual(h.Status, current.Status) { + if err := r.Status().Patch(ctx, h, client.MergeFrom(current)); err != nil { + return err + } + } + + return nil +} + +// SetupWithManager sets up the controller with the Manager. +func (r *HeisenbridgeReconciler) SetupWithManager(mgr ctrl.Manager) error { + return ctrl.NewControllerManagedBy(mgr). + For(&synapsev1alpha1.Heisenbridge{}). + Complete(r) +} diff --git a/controllers/synapse/synapse_heisenbridge_configmap.go b/controllers/synapse/heisenbridge/synapse_heisenbridge_configmap.go similarity index 64% rename from controllers/synapse/synapse_heisenbridge_configmap.go rename to controllers/synapse/heisenbridge/synapse_heisenbridge_configmap.go index e16f4ff..2c6ee26 100644 --- a/controllers/synapse/synapse_heisenbridge_configmap.go +++ b/controllers/synapse/heisenbridge/synapse_heisenbridge_configmap.go @@ -24,11 +24,12 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" ctrl "sigs.k8s.io/controller-runtime" - "sigs.k8s.io/controller-runtime/pkg/client" ctrllog "sigs.k8s.io/controller-runtime/pkg/log" synapsev1alpha1 "github.com/opdev/synapse-operator/apis/synapse/v1alpha1" + "github.com/opdev/synapse-operator/helpers/reconcile" reconc "github.com/opdev/synapse-operator/helpers/reconcileresults" + "github.com/opdev/synapse-operator/helpers/utils" ) // reconcileHeisenbridgeConfigMap is a function of type subreconcilerFuncs, to @@ -36,14 +37,21 @@ import ( // // It reconciles the heisenbridge ConfigMap to its desired state. It is called // only if the user hasn't provided its own ConfigMap for heisenbridge -func (r *SynapseReconciler) reconcileHeisenbridgeConfigMap(synapse *synapsev1alpha1.Synapse, ctx context.Context) (*ctrl.Result, error) { - objectMetaHeisenbridge := setObjectMeta(r.GetHeisenbridgeResourceName(*synapse), synapse.Namespace, map[string]string{}) - if err := r.reconcileResource( +func (r *HeisenbridgeReconciler) reconcileHeisenbridgeConfigMap(i interface{}, ctx context.Context) (*ctrl.Result, error) { + h := i.(*synapsev1alpha1.Heisenbridge) + + objectMetaHeisenbridge := reconcile.SetObjectMeta(h.Name, h.Namespace, map[string]string{}) + + desiredConfigMap, err := r.configMapForHeisenbridge(h, objectMetaHeisenbridge) + if err != nil { + return reconc.RequeueWithError(err) + } + + if err := reconcile.ReconcileResource( ctx, - r.configMapForHeisenbridge, - synapse, + r.Client, + desiredConfigMap, &corev1.ConfigMap{}, - objectMetaHeisenbridge, ); err != nil { return reconc.RequeueWithError(err) } @@ -52,10 +60,10 @@ func (r *SynapseReconciler) reconcileHeisenbridgeConfigMap(synapse *synapsev1alp } // configMapForSynapse returns a synapse ConfigMap object -func (r *SynapseReconciler) configMapForHeisenbridge(s *synapsev1alpha1.Synapse, objectMeta metav1.ObjectMeta) (client.Object, error) { +func (r *HeisenbridgeReconciler) configMapForHeisenbridge(h *synapsev1alpha1.Heisenbridge, objectMeta metav1.ObjectMeta) (*corev1.ConfigMap, error) { heisenbridgeYaml := ` id: heisenbridge -url: http://` + r.GetHeisenbridgeServiceFQDN(*s) + `:9898 +url: http://` + GetHeisenbridgeServiceFQDN(*h) + `:9898 as_token: EUFqSPQusV4mXkPKbwdHyIhthELQ1Xf9S5lSEzTrrlb0uz0ZJRHhwEljT71ByObe hs_token: If6r2GGlsNN4MnoW3djToADNdq0JuIJ1WNM4rKHO73WuG5QvVubj1Q4JHrmQBcS6 rate_limited: false @@ -74,7 +82,7 @@ namespaces: } // Set Synapse instance as the owner and controller - if err := ctrl.SetControllerReference(s, cm, r.Scheme); err != nil { + if err := ctrl.SetControllerReference(h, cm, r.Scheme); err != nil { return &corev1.ConfigMap{}, err } @@ -86,14 +94,13 @@ namespaces: // // It creates a copy of the user-provided ConfigMap for heisenbridge, defined // in synapse.Spec.Bridges.Heisenbridge.ConfigMap -func (r *SynapseReconciler) copyInputHeisenbridgeConfigMap(synapse *synapsev1alpha1.Synapse, ctx context.Context) (*ctrl.Result, error) { +func (r *HeisenbridgeReconciler) copyInputHeisenbridgeConfigMap(i interface{}, ctx context.Context) (*ctrl.Result, error) { + h := i.(*synapsev1alpha1.Heisenbridge) + log := ctrllog.FromContext(ctx) - inputConfigMapName := synapse.Spec.Bridges.Heisenbridge.ConfigMap.Name - inputConfigMapNamespace := r.getConfigMapNamespace( - *synapse, - synapse.Spec.Bridges.Heisenbridge.ConfigMap.Namespace, - ) + inputConfigMapName := h.Spec.ConfigMap.Name + inputConfigMapNamespace := utils.ComputeNamespace(h.Namespace, h.Spec.ConfigMap.Namespace) keyForConfigMap := types.NamespacedName{ Name: inputConfigMapName, Namespace: inputConfigMapNamespace, @@ -102,8 +109,8 @@ func (r *SynapseReconciler) copyInputHeisenbridgeConfigMap(synapse *synapsev1alp // Get and check the input ConfigMap for Heisenbridge if err := r.Get(ctx, keyForConfigMap, &corev1.ConfigMap{}); err != nil { reason := "ConfigMap " + inputConfigMapName + " does not exist in namespace " + inputConfigMapNamespace - if err := r.setFailedState(ctx, synapse, reason); err != nil { - log.Error(err, "Error updating Synapse State") + if err := r.setFailedState(ctx, h, reason); err != nil { + log.Error(err, "Error updating Heisenbridge State") } log.Error( @@ -118,16 +125,20 @@ func (r *SynapseReconciler) copyInputHeisenbridgeConfigMap(synapse *synapsev1alp return reconc.RequeueWithDelayAndError(time.Duration(30), err) } - objectMetaHeisenbridge := setObjectMeta(r.GetHeisenbridgeResourceName(*synapse), synapse.Namespace, map[string]string{}) + objectMetaHeisenbridge := reconcile.SetObjectMeta(h.Name, h.Namespace, map[string]string{}) + + desiredConfigMap, err := r.configMapForHeisenbridgeCopy(h, objectMetaHeisenbridge) + if err != nil { + return reconc.RequeueWithError(err) + } // Create a copy of the inputHeisenbridgeConfigMap defined in Spec.Bridges.Heisenbridge.ConfigMap // Here we use the configMapForHeisenbridgeCopy function as createResourceFunc - if err := r.reconcileResource( + if err := reconcile.ReconcileResource( ctx, - r.configMapForHeisenbridgeCopy, - synapse, + r.Client, + desiredConfigMap, &corev1.ConfigMap{}, - objectMetaHeisenbridge, ); err != nil { return reconc.RequeueWithError(err) } @@ -140,22 +151,27 @@ func (r *SynapseReconciler) copyInputHeisenbridgeConfigMap(synapse *synapsev1alp // // The ConfigMap returned by configMapForHeisenbridgeCopy is a copy of the ConfigMap // defined in Spec.Bridges.Heisenbridge.ConfigMap. -func (r *SynapseReconciler) configMapForHeisenbridgeCopy( - s *synapsev1alpha1.Synapse, +func (r *HeisenbridgeReconciler) configMapForHeisenbridgeCopy( + h *synapsev1alpha1.Heisenbridge, objectMeta metav1.ObjectMeta, -) (client.Object, error) { +) (*corev1.ConfigMap, error) { var copyConfigMap *corev1.ConfigMap - sourceConfigMapName := s.Spec.Bridges.Heisenbridge.ConfigMap.Name - sourceConfigMapNamespace := r.getConfigMapNamespace(*s, s.Spec.Bridges.Heisenbridge.ConfigMap.Namespace) + sourceConfigMapName := h.Name + sourceConfigMapNamespace := utils.ComputeNamespace(h.Namespace, h.Spec.ConfigMap.Namespace) - copyConfigMap, err := r.getConfigMapCopy(sourceConfigMapName, sourceConfigMapNamespace, objectMeta) + copyConfigMap, err := utils.GetConfigMapCopy( + r.Client, + sourceConfigMapName, + sourceConfigMapNamespace, + objectMeta, + ) if err != nil { return &corev1.ConfigMap{}, err } // Set Synapse instance as the owner and controller - if err := ctrl.SetControllerReference(s, copyConfigMap, r.Scheme); err != nil { + if err := ctrl.SetControllerReference(h, copyConfigMap, r.Scheme); err != nil { return &corev1.ConfigMap{}, err } @@ -167,17 +183,20 @@ func (r *SynapseReconciler) configMapForHeisenbridgeCopy( // // Following the previous copy of the user-provided ConfigMap, it edits the // content of the copy to ensure that heisenbridge is correctly configured. -func (r *SynapseReconciler) configureHeisenbridgeConfigMap(synapse *synapsev1alpha1.Synapse, ctx context.Context) (*ctrl.Result, error) { +func (r *HeisenbridgeReconciler) configureHeisenbridgeConfigMap(i interface{}, ctx context.Context) (*ctrl.Result, error) { + h := i.(*synapsev1alpha1.Heisenbridge) + keyForConfigMap := types.NamespacedName{ - Name: r.GetHeisenbridgeResourceName(*synapse), - Namespace: synapse.Namespace, + Name: h.Name, + Namespace: h.Namespace, } // Configure correct URL in Heisenbridge ConfigMap - if err := r.updateConfigMap( + if err := utils.UpdateConfigMap( ctx, + r.Client, keyForConfigMap, - *synapse, + h, r.updateHeisenbridgeWithURL, "heisenbridge.yaml", ); err != nil { @@ -192,10 +211,12 @@ func (r *SynapseReconciler) configureHeisenbridgeConfigMap(synapse *synapsev1alp // // It configures the correct Heisenbridge URL, needed for Synapse to reach the // bridge. -func (r *SynapseReconciler) updateHeisenbridgeWithURL( - s synapsev1alpha1.Synapse, +func (r *HeisenbridgeReconciler) updateHeisenbridgeWithURL( + i interface{}, heisenbridge map[string]interface{}, ) error { - heisenbridge["url"] = "http://" + r.GetHeisenbridgeServiceFQDN(s) + ":9898" + h := i.(synapsev1alpha1.Heisenbridge) + + heisenbridge["url"] = "http://" + GetHeisenbridgeServiceFQDN(h) + ":9898" return nil } diff --git a/controllers/synapse/synapse_heisenbridge_deployment.go b/controllers/synapse/heisenbridge/synapse_heisenbridge_deployment.go similarity index 69% rename from controllers/synapse/synapse_heisenbridge_deployment.go rename to controllers/synapse/heisenbridge/synapse_heisenbridge_deployment.go index af17ade..c976e09 100644 --- a/controllers/synapse/synapse_heisenbridge_deployment.go +++ b/controllers/synapse/heisenbridge/synapse_heisenbridge_deployment.go @@ -23,30 +23,38 @@ import ( corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ctrl "sigs.k8s.io/controller-runtime" - "sigs.k8s.io/controller-runtime/pkg/client" synapsev1alpha1 "github.com/opdev/synapse-operator/apis/synapse/v1alpha1" + "github.com/opdev/synapse-operator/helpers/reconcile" reconc "github.com/opdev/synapse-operator/helpers/reconcileresults" + "github.com/opdev/synapse-operator/helpers/utils" ) // labelsForSynapse returns the labels for selecting the resources // belonging to the given synapse CR name. func labelsForHeisenbridge(name string) map[string]string { - return map[string]string{"app": "heisenbridge", "synapse_cr": name} + return map[string]string{"app": "heisenbridge", "heisenbridge_cr": name} } // reconcileHeisenbridgeDeployment is a function of type subreconcilerFuncs, to // be called in the main reconciliation loop. // // It reconciles the Deployment for Heisenbridge to its desired state. -func (r *SynapseReconciler) reconcileHeisenbridgeDeployment(synapse *synapsev1alpha1.Synapse, ctx context.Context) (*ctrl.Result, error) { - objectMetaHeisenbridge := setObjectMeta(r.GetHeisenbridgeResourceName(*synapse), synapse.Namespace, map[string]string{}) - if err := r.reconcileResource( +func (r *HeisenbridgeReconciler) reconcileHeisenbridgeDeployment(i interface{}, ctx context.Context) (*ctrl.Result, error) { + h := i.(*synapsev1alpha1.Heisenbridge) + + objectMetaHeisenbridge := reconcile.SetObjectMeta(h.Name, h.Namespace, map[string]string{}) + + desiredDeployment, err := r.deploymentForHeisenbridge(h, objectMetaHeisenbridge) + if err != nil { + return reconc.RequeueWithError(err) + } + + if err := reconcile.ReconcileResource( ctx, - r.deploymentForHeisenbridge, - synapse, + r.Client, + desiredDeployment, &appsv1.Deployment{}, - objectMetaHeisenbridge, ); err != nil { return reconc.RequeueWithError(err) } @@ -55,11 +63,11 @@ func (r *SynapseReconciler) reconcileHeisenbridgeDeployment(synapse *synapsev1al } // deploymentForHeisenbridge returns a Heisenbridge Deployment object -func (r *SynapseReconciler) deploymentForHeisenbridge(s *synapsev1alpha1.Synapse, objectMeta metav1.ObjectMeta) (client.Object, error) { - ls := labelsForHeisenbridge(s.Name) +func (r *HeisenbridgeReconciler) deploymentForHeisenbridge(h *synapsev1alpha1.Heisenbridge, objectMeta metav1.ObjectMeta) (*appsv1.Deployment, error) { + ls := labelsForHeisenbridge(h.Name) replicas := int32(1) - command := r.craftHeisenbridgeCommad(*s) + command := r.craftHeisenbridgeCommad(*h) // The created Heisenbridge ConfigMap Name share the same name as the // Heisenbridge Deployment heisenbridgeConfigMapName := objectMeta.Name @@ -103,34 +111,37 @@ func (r *SynapseReconciler) deploymentForHeisenbridge(s *synapsev1alpha1.Synapse }, } // Set Synapse instance as the owner and controller - if err := ctrl.SetControllerReference(s, dep, r.Scheme); err != nil { + if err := ctrl.SetControllerReference(h, dep, r.Scheme); err != nil { return &appsv1.Deployment{}, err } return dep, nil } -func (r *SynapseReconciler) craftHeisenbridgeCommad(s synapsev1alpha1.Synapse) []string { +func (r *HeisenbridgeReconciler) craftHeisenbridgeCommad(h synapsev1alpha1.Heisenbridge) []string { command := []string{ "python", "-m", "heisenbridge", } - if s.Spec.Bridges.Heisenbridge.VerboseLevel > 0 { + if h.Spec.VerboseLevel > 0 { verbosity := "-" - for i := 1; i <= s.Spec.Bridges.Heisenbridge.VerboseLevel; i++ { + for i := 1; i <= h.Spec.VerboseLevel; i++ { verbosity = verbosity + "v" } command = append(command, verbosity) } + SynapseName := h.Spec.Synapse.Name + SynapseNamespace := utils.ComputeNamespace(h.Namespace, h.Spec.Synapse.Namespace) + command = append( command, "-c", "/data-heisenbridge/heisenbridge.yaml", "-l", "0.0.0.0", - "http://"+r.GetSynapseServiceFQDN(s)+":8008", + "http://"+utils.ComputeFQDN(SynapseName, SynapseNamespace)+":8008", ) return command diff --git a/controllers/synapse/synapse_heisenbridge_service.go b/controllers/synapse/heisenbridge/synapse_heisenbridge_service.go similarity index 68% rename from controllers/synapse/synapse_heisenbridge_service.go rename to controllers/synapse/heisenbridge/synapse_heisenbridge_service.go index 05b4346..31fabbe 100644 --- a/controllers/synapse/synapse_heisenbridge_service.go +++ b/controllers/synapse/heisenbridge/synapse_heisenbridge_service.go @@ -23,9 +23,9 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/util/intstr" ctrl "sigs.k8s.io/controller-runtime" - "sigs.k8s.io/controller-runtime/pkg/client" synapsev1alpha1 "github.com/opdev/synapse-operator/apis/synapse/v1alpha1" + "github.com/opdev/synapse-operator/helpers/reconcile" reconc "github.com/opdev/synapse-operator/helpers/reconcileresults" ) @@ -33,14 +33,21 @@ import ( // called in the main reconciliation loop. // // It reconciles the Service for Heisenbridge to its desired state. -func (r *SynapseReconciler) reconcileHeisenbridgeService(synapse *synapsev1alpha1.Synapse, ctx context.Context) (*ctrl.Result, error) { - objectMetaHeisenbridge := setObjectMeta(r.GetHeisenbridgeResourceName(*synapse), synapse.Namespace, map[string]string{}) - if err := r.reconcileResource( +func (r *HeisenbridgeReconciler) reconcileHeisenbridgeService(i interface{}, ctx context.Context) (*ctrl.Result, error) { + h := i.(*synapsev1alpha1.Heisenbridge) + + objectMetaHeisenbridge := reconcile.SetObjectMeta(h.Name, h.Namespace, map[string]string{}) + + desiredService, err := r.serviceForHeisenbridge(h, objectMetaHeisenbridge) + if err != nil { + return reconc.RequeueWithError(err) + } + + if err := reconcile.ReconcileResource( ctx, - r.serviceForHeisenbridge, - synapse, + r.Client, + desiredService, &corev1.Service{}, - objectMetaHeisenbridge, ); err != nil { return reconc.RequeueWithError(err) } @@ -49,7 +56,7 @@ func (r *SynapseReconciler) reconcileHeisenbridgeService(synapse *synapsev1alpha } // serviceForSynapse returns a Heisenbridge Service object -func (r *SynapseReconciler) serviceForHeisenbridge(s *synapsev1alpha1.Synapse, objectMeta metav1.ObjectMeta) (client.Object, error) { +func (r *HeisenbridgeReconciler) serviceForHeisenbridge(h *synapsev1alpha1.Heisenbridge, objectMeta metav1.ObjectMeta) (*corev1.Service, error) { service := &corev1.Service{ ObjectMeta: objectMeta, Spec: corev1.ServiceSpec{ @@ -59,12 +66,12 @@ func (r *SynapseReconciler) serviceForHeisenbridge(s *synapsev1alpha1.Synapse, o Port: 9898, TargetPort: intstr.FromInt(9898), }}, - Selector: labelsForHeisenbridge(s.Name), + Selector: labelsForHeisenbridge(h.Name), Type: corev1.ServiceTypeClusterIP, }, } // Set Synapse instance as the owner and controller - if err := ctrl.SetControllerReference(s, service, r.Scheme); err != nil { + if err := ctrl.SetControllerReference(h, service, r.Scheme); err != nil { return &corev1.Service{}, err } return service, nil diff --git a/controllers/synapse/synapse_mautrixsignal_configmap.go b/controllers/synapse/mautrixsignal/mautrixsignal_configmap.go similarity index 86% rename from controllers/synapse/synapse_mautrixsignal_configmap.go rename to controllers/synapse/mautrixsignal/mautrixsignal_configmap.go index 014b510..e9115a5 100644 --- a/controllers/synapse/synapse_mautrixsignal_configmap.go +++ b/controllers/synapse/mautrixsignal/mautrixsignal_configmap.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package synapse +package mautrixsignal import ( "context" @@ -25,11 +25,12 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" ctrl "sigs.k8s.io/controller-runtime" - "sigs.k8s.io/controller-runtime/pkg/client" ctrllog "sigs.k8s.io/controller-runtime/pkg/log" synapsev1alpha1 "github.com/opdev/synapse-operator/apis/synapse/v1alpha1" + "github.com/opdev/synapse-operator/helpers/reconcile" reconc "github.com/opdev/synapse-operator/helpers/reconcileresults" + "github.com/opdev/synapse-operator/helpers/utils" ) // reconcileMautrixSignalConfigMap is a function of type subreconcilerFuncs, to @@ -38,14 +39,21 @@ import ( // It reconciles the mautrix-signal ConfigMap to its desired state. It is // called only if the user hasn't provided its own ConfigMap for // mautrix-signal. -func (r *SynapseReconciler) reconcileMautrixSignalConfigMap(synapse *synapsev1alpha1.Synapse, ctx context.Context) (*ctrl.Result, error) { - objectMetaMautrixSignal := setObjectMeta(r.GetMautrixSignalResourceName(*synapse), synapse.Namespace, map[string]string{}) - if err := r.reconcileResource( +func (r *MautrixSignalReconciler) reconcileMautrixSignalConfigMap(i interface{}, ctx context.Context) (*ctrl.Result, error) { + ms := i.(*synapsev1alpha1.MautrixSignal) + + objectMetaMautrixSignal := reconcile.SetObjectMeta(ms.Name, ms.Namespace, map[string]string{}) + + desiredConfigMap, err := r.configMapForMautrixSignal(ms, objectMetaMautrixSignal) + if err != nil { + return reconc.RequeueWithError(err) + } + + if err := reconcile.ReconcileResource( ctx, - r.configMapForMautrixSignal, - synapse, + r.Client, + desiredConfigMap, &corev1.ConfigMap{}, - objectMetaMautrixSignal, ); err != nil { return reconc.RequeueWithError(err) } @@ -54,14 +62,16 @@ func (r *SynapseReconciler) reconcileMautrixSignalConfigMap(synapse *synapsev1al } // configMapForSynapse returns a synapse ConfigMap object -func (r *SynapseReconciler) configMapForMautrixSignal(s *synapsev1alpha1.Synapse, objectMeta metav1.ObjectMeta) (client.Object, error) { - synapseServerName := s.Status.HomeserverConfiguration.ServerName +func (r *MautrixSignalReconciler) configMapForMautrixSignal(ms *synapsev1alpha1.MautrixSignal, objectMeta metav1.ObjectMeta) (*corev1.ConfigMap, error) { + synapseName := ms.Spec.Synapse.Name + synapseNamespace := utils.ComputeNamespace(ms.Namespace, ms.Spec.Synapse.Namespace) + synapseServerName := ms.Status.Synapse.ServerName configYaml := ` # Homeserver details homeserver: # The address that this appservice can use to connect to the homeserver. - address: http://` + r.GetSynapseServiceFQDN(*s) + `:8008 + address: http://` + utils.ComputeFQDN(synapseName, synapseNamespace) + `:8008 # The domain of the homeserver (for MXIDs, etc). domain: ` + synapseServerName + ` # Whether or not to verify the SSL certificate of the homeserver. @@ -86,7 +96,7 @@ homeserver: # Changing these values requires regeneration of the registration. appservice: # The address that the homeserver can use to connect to this appservice. - address: http://` + r.GetMautrixSignalServiceFQDN(*s) + `:29328 + address: http://` + utils.ComputeFQDN(ms.Name, ms.Namespace) + `:29328 # When using https:// the TLS certificate and key files for the address. tls_cert: false tls_key: false @@ -369,7 +379,7 @@ logging: } // Set Synapse instance as the owner and controller - if err := ctrl.SetControllerReference(s, cm, r.Scheme); err != nil { + if err := ctrl.SetControllerReference(ms, cm, r.Scheme); err != nil { return &corev1.ConfigMap{}, err } @@ -381,24 +391,23 @@ logging: // // It creates a copy of the user-provided ConfigMap for mautrix-signal, defined // in synapse.Spec.Bridges.MautrixSignal.ConfigMap -func (r *SynapseReconciler) copyInputMautrixSignalConfigMap(synapse *synapsev1alpha1.Synapse, ctx context.Context) (*ctrl.Result, error) { +func (r *MautrixSignalReconciler) copyInputMautrixSignalConfigMap(i interface{}, ctx context.Context) (*ctrl.Result, error) { + ms := i.(*synapsev1alpha1.MautrixSignal) + log := ctrllog.FromContext(ctx) - inputConfigMapName := synapse.Spec.Bridges.MautrixSignal.ConfigMap.Name - inputConfigMapNamespace := r.getConfigMapNamespace( - *synapse, - synapse.Spec.Bridges.MautrixSignal.ConfigMap.Namespace, - ) + inputConfigMapName := ms.Spec.ConfigMap.Name + inputConfigMapNamespace := utils.ComputeNamespace(ms.Namespace, ms.Spec.ConfigMap.Namespace) keyForInputConfigMap := types.NamespacedName{ Name: inputConfigMapName, Namespace: inputConfigMapNamespace, } - // Get and check the input ConfigMap for Heisenbridge + // Get and check the input ConfigMap for MautrixSignal if err := r.Get(ctx, keyForInputConfigMap, &corev1.ConfigMap{}); err != nil { reason := "ConfigMap " + inputConfigMapName + " does not exist in namespace " + inputConfigMapNamespace - if err := r.setFailedState(ctx, synapse, reason); err != nil { - log.Error(err, "Error updating Synapse State") + if err := r.setFailedState(ctx, ms, reason); err != nil { + log.Error(err, "Error updating mautrix-signal State") } log.Error( @@ -413,16 +422,20 @@ func (r *SynapseReconciler) copyInputMautrixSignalConfigMap(synapse *synapsev1al return reconc.RequeueWithDelayAndError(time.Duration(30), err) } - objectMetaMautrixSignal := setObjectMeta(r.GetMautrixSignalResourceName(*synapse), synapse.Namespace, map[string]string{}) + objectMetaMautrixSignal := reconcile.SetObjectMeta(ms.Name, ms.Namespace, map[string]string{}) + + desiredConfigMap, err := r.configMapForMautrixSignalCopy(ms, objectMetaMautrixSignal) + if err != nil { + return reconc.RequeueWithError(err) + } // Create a copy of the inputMautrixSignalConfigMap defined in Spec.Bridges.MautrixSignal.ConfigMap // Here we use the createdMautrixSignalConfigMap function as createResourceFunc - if err := r.reconcileResource( + if err := reconcile.ReconcileResource( ctx, - r.configMapForMautrixSignalCopy, - synapse, + r.Client, + desiredConfigMap, &corev1.ConfigMap{}, - objectMetaMautrixSignal, ); err != nil { return reconc.RequeueWithError(err) } @@ -435,22 +448,27 @@ func (r *SynapseReconciler) copyInputMautrixSignalConfigMap(synapse *synapsev1al // // The ConfigMap returned by configMapForMautrixSignalCopy is a copy of the ConfigMap // defined in Spec.Bridges.MautrixSignal.ConfigMap. -func (r *SynapseReconciler) configMapForMautrixSignalCopy( - s *synapsev1alpha1.Synapse, +func (r *MautrixSignalReconciler) configMapForMautrixSignalCopy( + ms *synapsev1alpha1.MautrixSignal, objectMeta metav1.ObjectMeta, -) (client.Object, error) { +) (*corev1.ConfigMap, error) { var copyConfigMap *corev1.ConfigMap - sourceConfigMapName := s.Spec.Bridges.MautrixSignal.ConfigMap.Name - sourceConfigMapNamespace := r.getConfigMapNamespace(*s, s.Spec.Bridges.MautrixSignal.ConfigMap.Namespace) + sourceConfigMapName := ms.Spec.ConfigMap.Name + sourceConfigMapNamespace := utils.ComputeNamespace(ms.Namespace, ms.Spec.ConfigMap.Namespace) - copyConfigMap, err := r.getConfigMapCopy(sourceConfigMapName, sourceConfigMapNamespace, objectMeta) + copyConfigMap, err := utils.GetConfigMapCopy( + r.Client, + sourceConfigMapName, + sourceConfigMapNamespace, + objectMeta, + ) if err != nil { return &corev1.ConfigMap{}, err } // Set Synapse instance as the owner and controller - if err := ctrl.SetControllerReference(s, copyConfigMap, r.Scheme); err != nil { + if err := ctrl.SetControllerReference(ms, copyConfigMap, r.Scheme); err != nil { return &corev1.ConfigMap{}, err } @@ -462,17 +480,20 @@ func (r *SynapseReconciler) configMapForMautrixSignalCopy( // // Following the previous copy of the user-provided ConfigMap, it edits the // content of the copy to ensure that mautrix-signal is correctly configured. -func (r *SynapseReconciler) configureMautrixSignalConfigMap(synapse *synapsev1alpha1.Synapse, ctx context.Context) (*ctrl.Result, error) { +func (r *MautrixSignalReconciler) configureMautrixSignalConfigMap(i interface{}, ctx context.Context) (*ctrl.Result, error) { + ms := i.(*synapsev1alpha1.MautrixSignal) + keyForConfigMap := types.NamespacedName{ - Name: r.GetMautrixSignalResourceName(*synapse), - Namespace: synapse.Namespace, + Name: ms.Name, + Namespace: ms.Namespace, } // Correct data in mautrix-signal ConfigMap - if err := r.updateConfigMap( + if err := utils.UpdateConfigMap( ctx, + r.Client, keyForConfigMap, - *synapse, + *ms, r.updateMautrixSignalData, "config.yaml", ); err != nil { @@ -488,11 +509,15 @@ func (r *SynapseReconciler) configureMautrixSignalConfigMap(synapse *synapsev1al // It configures the user-provided config.yaml with the correct values. Among // other things, it ensures that the bridge can reach the Synapse homeserver // and knows the correct path to the signald socket. -func (r *SynapseReconciler) updateMautrixSignalData( - s synapsev1alpha1.Synapse, +func (r *MautrixSignalReconciler) updateMautrixSignalData( + i interface{}, config map[string]interface{}, ) error { - synapseServerName := s.Status.HomeserverConfiguration.ServerName + ms := i.(synapsev1alpha1.MautrixSignal) + + synapseName := ms.Spec.Synapse.Name + synapseNamespace := utils.ComputeNamespace(ms.Namespace, ms.Spec.Synapse.Namespace) + synapseServerName := ms.Status.Synapse.ServerName // Update the homeserver section so that the bridge can reach Synapse configHomeserver, ok := config["homeserver"].(map[interface{}]interface{}) @@ -500,7 +525,7 @@ func (r *SynapseReconciler) updateMautrixSignalData( err := errors.New("cannot parse mautrix-signal config.yaml: error parsing 'homeserver' section") return err } - configHomeserver["address"] = "http://" + r.GetSynapseServiceFQDN(s) + ":8008" + configHomeserver["address"] = "http://" + utils.ComputeFQDN(synapseName, synapseNamespace) + ":8008" configHomeserver["domain"] = synapseServerName config["homeserver"] = configHomeserver @@ -510,7 +535,7 @@ func (r *SynapseReconciler) updateMautrixSignalData( err := errors.New("cannot parse mautrix-signal config.yaml: error parsing 'appservice' section") return err } - configAppservice["address"] = "http://" + r.GetMautrixSignalServiceFQDN(s) + ":29328" + configAppservice["address"] = "http://" + utils.ComputeFQDN(ms.Name, ms.Namespace) + ":29328" config["appservice"] = configAppservice // Update the path to the signal socket path diff --git a/controllers/synapse/mautrixsignal/mautrixsignal_controller.go b/controllers/synapse/mautrixsignal/mautrixsignal_controller.go new file mode 100644 index 0000000..dee735e --- /dev/null +++ b/controllers/synapse/mautrixsignal/mautrixsignal_controller.go @@ -0,0 +1,264 @@ +/* +Copyright 2021. + +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 mautrixsignal + +import ( + "context" + "reflect" + "strings" + + k8serrors "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/types" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + ctrllog "sigs.k8s.io/controller-runtime/pkg/log" + + synapsev1alpha1 "github.com/opdev/synapse-operator/apis/synapse/v1alpha1" + reconc "github.com/opdev/synapse-operator/helpers/reconcileresults" + "github.com/opdev/synapse-operator/helpers/utils" +) + +// MautrixSignalReconciler reconciles a MautrixSignal object +type MautrixSignalReconciler struct { + client.Client + Scheme *runtime.Scheme +} + +func GetSignaldResourceName(ms synapsev1alpha1.MautrixSignal) string { + return strings.Join([]string{ms.Name, "signald"}, "-") +} + +func GetMautrixSignalServiceFQDN(ms synapsev1alpha1.MautrixSignal) string { + return strings.Join([]string{ms.Name, ms.Namespace, "svc", "cluster", "local"}, ".") +} + +//+kubebuilder:rbac:groups=synapse.opdev.io,resources=mautrixsignals,verbs=get;list;watch;create;update;patch;delete +//+kubebuilder:rbac:groups=synapse.opdev.io,resources=mautrixsignals/status,verbs=get;update;patch +//+kubebuilder:rbac:groups=synapse.opdev.io,resources=mautrixsignals/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 MautrixSignal 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.13.0/pkg/reconcile +func (r *MautrixSignalReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { + log := ctrllog.FromContext(ctx) + + var ms synapsev1alpha1.MautrixSignal // The mautrix-signal object being reconciled + + // Load the mautrix-signal by name + if err := r.Get(ctx, req.NamespacedName, &ms); err != nil { + if k8serrors.IsNotFound(err) { + // we'll ignore not-found errors, since they can't be fixed by an immediate + // requeue (we'll need to wait for a new notification), and we can get them + // on deleted requests. + log.Error( + err, + "Cannot find mautrix-signal - has it been deleted ?", + "mautrix-signal Name", ms.Name, + "mautrix-signal Namespace", ms.Namespace, + ) + return ctrl.Result{}, nil + } + log.Error( + err, + "Error fetching mautrix-signal", + "mautrix-signal Name", ms.Name, + "mautrix-signal Namespace", ms.Namespace, + ) + return ctrl.Result{}, client.IgnoreNotFound(err) + } + + // Build mautrix-signal status + s, err := r.fetchSynapseInstance(ctx, ms) + if err != nil { + if k8serrors.IsNotFound(err) { + log.Error( + err, + "Cannot find Synapse instance", + "Synapse Name", ms.Spec.Synapse.Name, + "Synapse Namespace", utils.ComputeNamespace(ms.Namespace, ms.Spec.Synapse.Namespace), + ) + } else { + log.Error( + err, + "Error getting Synapse server name", + "Synapse Name", ms.Spec.Synapse.Name, + "Synapse Namespace", utils.ComputeNamespace(ms.Namespace, ms.Spec.Synapse.Namespace), + ) + } + return ctrl.Result{}, err + } + + // Get Synapse Status + // if !isSynapseRunning(s) { + // err = errors.New("Synapse is not ready") + // log.Error( + // err, + // "Synapse is not ready", + // "Synapse Name", ms.Spec.Synapse.Name, + // "Synapse Namespace", utils.ComputeNamespace(ms.Namespace, ms.Spec.Synapse.Namespace), + // ) + + // return ctrl.Result{}, err + // } + + // Get Synapse ServerName + ms.Status.Synapse.ServerName, err = utils.GetSynapseServerName(s) + if err != nil { + log.Error( + err, + "Error getting Synapse ServerName", + "Synapse Name", ms.Spec.Synapse.Name, + "Synapse Namespace", utils.ComputeNamespace(ms.Namespace, ms.Spec.Synapse.Namespace), + ) + } + + if r, err := r.triggerSynapseReconciliation(&s, ctx); reconc.ShouldHaltOrRequeue(r, err) { + return reconc.Evaluate(r, err) + } + + if err := r.updateMautrixSignalStatus(ctx, &ms); err != nil { + log.Error(err, "Error updating mautrix-signal Status") + return ctrl.Result{}, err + } + + // The list of subreconcilers for mautrix-signal will be built next. + // mautrix-signal is composed of a ConfigMap, a Service, a SA, a RB, + // a PVC and a Deployment. + // In addition, a Deployment and a PVC are needed for signald. + var subreconcilersForMautrixSignal []reconc.SubreconcilerFuncs + + // The user may specify a ConfigMap, containing the config.yaml config + // file, under Spec.Bridges.MautrixSignal.ConfigMap + if ms.Spec.ConfigMap.Name != "" { + // If the user provided a custom mautrix-signal configuration via a + // ConfigMap, we need to validate that the ConfigMap exists, and + // create a copy. We also need to edit the mautrix-signal + // configuration. + subreconcilersForMautrixSignal = []reconc.SubreconcilerFuncs{ + r.copyInputMautrixSignalConfigMap, + r.configureMautrixSignalConfigMap, + } + + } else { + // If the user hasn't provided a ConfigMap with a custom + // config.yaml, we create a new ConfigMap with a default + // config.yaml. + subreconcilersForMautrixSignal = []reconc.SubreconcilerFuncs{ + r.reconcileMautrixSignalConfigMap, + } + } + + // Reconcile signald resources: PVC and Deployment + // Reconcile mautrix-signal resources: Service, SA, RB, PVC and Deployment + subreconcilersForMautrixSignal = append( + subreconcilersForMautrixSignal, + r.reconcileSignaldPVC, + r.reconcileSignaldDeployment, + r.reconcileMautrixSignalService, + r.reconcileMautrixSignalServiceAccount, + r.reconcileMautrixSignalRoleBinding, + r.reconcileMautrixSignalPVC, + r.reconcileMautrixSignalDeployment, + ) + + for _, f := range subreconcilersForMautrixSignal { + if r, err := f(&ms, ctx); reconc.ShouldHaltOrRequeue(r, err) { + return reconc.Evaluate(r, err) + } + } + + return ctrl.Result{}, nil +} + +func (r *MautrixSignalReconciler) fetchSynapseInstance( + ctx context.Context, + ms synapsev1alpha1.MautrixSignal, +) (synapsev1alpha1.Synapse, error) { + // Validate Synapse instance exists + s := &synapsev1alpha1.Synapse{} + keyForSynapse := types.NamespacedName{ + Name: ms.Spec.Synapse.Name, + Namespace: utils.ComputeNamespace(ms.Namespace, ms.Spec.Synapse.Namespace), + } + if err := r.Get(ctx, keyForSynapse, s); err != nil { + return synapsev1alpha1.Synapse{}, err + } + + return *s, nil +} + +func (r *MautrixSignalReconciler) triggerSynapseReconciliation(i interface{}, ctx context.Context) (*ctrl.Result, error) { + s := i.(*synapsev1alpha1.Synapse) + s.Status.NeedsReconcile = true + + current := &synapsev1alpha1.Synapse{} + if err := r.Get( + ctx, + types.NamespacedName{Name: s.Name, Namespace: s.Namespace}, + current, + ); err != nil { + return reconc.RequeueWithError(err) + } + + if !reflect.DeepEqual(s.Status, current.Status) { + if err := r.Status().Patch(ctx, s, client.MergeFrom(current)); err != nil { + return reconc.RequeueWithError(err) + } + } + + return reconc.ContinueReconciling() +} + +func (r *MautrixSignalReconciler) setFailedState(ctx context.Context, ms *synapsev1alpha1.MautrixSignal, reason string) error { + ms.Status.State = "FAILED" + ms.Status.Reason = reason + + return r.updateMautrixSignalStatus(ctx, ms) +} + +func (r *MautrixSignalReconciler) updateMautrixSignalStatus(ctx context.Context, ms *synapsev1alpha1.MautrixSignal) error { + current := &synapsev1alpha1.MautrixSignal{} + if err := r.Get( + ctx, + types.NamespacedName{Name: ms.Name, Namespace: ms.Namespace}, + current, + ); err != nil { + return err + } + + if !reflect.DeepEqual(ms.Status, current.Status) { + if err := r.Status().Patch(ctx, ms, client.MergeFrom(current)); err != nil { + return err + } + } + + return nil +} + +// SetupWithManager sets up the controller with the Manager. +func (r *MautrixSignalReconciler) SetupWithManager(mgr ctrl.Manager) error { + return ctrl.NewControllerManagedBy(mgr). + For(&synapsev1alpha1.MautrixSignal{}). + Complete(r) +} diff --git a/controllers/synapse/mautrixsignal/mautrixsignal_controller_test.go b/controllers/synapse/mautrixsignal/mautrixsignal_controller_test.go new file mode 100644 index 0000000..518d15c --- /dev/null +++ b/controllers/synapse/mautrixsignal/mautrixsignal_controller_test.go @@ -0,0 +1,1222 @@ +package mautrixsignal + +// import ( +// "context" +// "path/filepath" + +// // "strconv" +// "time" + +// . "github.com/onsi/ginkgo/v2" +// . "github.com/onsi/gomega" + +// // appsv1 "k8s.io/api/apps/v1" +// // corev1 "k8s.io/api/core/v1" +// // rbacv1 "k8s.io/api/rbac/v1" + +// "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" +// "k8s.io/client-go/kubernetes/scheme" +// ctrl "sigs.k8s.io/controller-runtime" +// "sigs.k8s.io/controller-runtime/pkg/client" +// "sigs.k8s.io/controller-runtime/pkg/envtest" +// logf "sigs.k8s.io/controller-runtime/pkg/log" +// "sigs.k8s.io/controller-runtime/pkg/log/zap" + +// synapsev1alpha1 "github.com/opdev/synapse-operator/apis/synapse/v1alpha1" +// ) + +// var _ = Describe("Integration tests for the Synapse controller", Ordered, Label("integration"), func() { +// // Define utility constants for object names and testing timeouts/durations and intervals. +// const ( +// MautrixSignalName = "test-mautrixsignal" +// MautrixSignalNamespace = "default" +// InputConfigMapName = "test-configmap" + +// timeout = time.Second * 2 +// duration = time.Second * 2 +// interval = time.Millisecond * 250 +// ) + +// var k8sClient client.Client +// var testEnv *envtest.Environment +// var ctx context.Context +// var cancel context.CancelFunc + +// // var deleteResource func(client.Object, types.NamespacedName, bool) +// // var checkSubresourceAbsence func(string) +// // var checkResourcePresence func(client.Object, types.NamespacedName, metav1.OwnerReference) + +// // Common function to start envTest +// var startenvTest = func() { +// cfg, err := testEnv.Start() +// Expect(err).NotTo(HaveOccurred()) +// Expect(cfg).NotTo(BeNil()) + +// Expect(synapsev1alpha1.AddToScheme(scheme.Scheme)).NotTo(HaveOccurred()) + +// //+kubebuilder:scaffold:scheme + +// k8sClient, err = client.New(cfg, client.Options{Scheme: scheme.Scheme}) +// Expect(err).NotTo(HaveOccurred()) +// Expect(k8sClient).NotTo(BeNil()) + +// k8sManager, err := ctrl.NewManager(cfg, ctrl.Options{ +// Scheme: scheme.Scheme, +// }) +// Expect(err).ToNot(HaveOccurred()) + +// err = (&MautrixSignalReconciler{ +// Client: k8sManager.GetClient(), +// Scheme: k8sManager.GetScheme(), +// }).SetupWithManager(k8sManager) +// Expect(err).ToNot(HaveOccurred()) + +// // deleteResource = utils.DeleteResourceFunc(k8sClient, ctx, timeout, interval) +// // checkSubresourceAbsence = utils.CheckSubresourceAbsenceFunc(MautrixSignalName, MautrixSignalNamespace, k8sClient, ctx, timeout, interval) +// // checkResourcePresence = utils.CheckResourcePresenceFunc(k8sClient, ctx, timeout, interval) + +// go func() { +// defer GinkgoRecover() +// Expect(k8sManager.Start(ctx)).ToNot(HaveOccurred(), "failed to run manager") +// }() +// } + +// Context("When a corectly configured Kubernetes cluster is present", func() { +// var _ = 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, +// } + +// startenvTest() +// }) + +// var _ = AfterAll(func() { +// cancel() +// By("tearing down the test environment") +// Expect(testEnv.Stop()).NotTo(HaveOccurred()) +// }) + +// Context("Validating MautixSignal CRD Schema", func() { +// var obj map[string]interface{} + +// BeforeEach(func() { +// obj = map[string]interface{}{ +// "apiVersion": "synapse.opdev.io/v1alpha1", +// "kind": "MautrixSignal", +// "metadata": map[string]interface{}{ +// "name": MautrixSignalName, +// "namespace": MautrixSignalNamespace, +// }, +// } +// }) + +// DescribeTable("Creating a misconfigured MautrixSignal instance", +// func(mautrixsignal map[string]interface{}) { +// // Augment base mautrixsignal obj with additional fields +// for key, value := range mautrixsignal { +// obj[key] = value +// } +// // Create Unstructured object from mautrixsignal obj +// u := unstructured.Unstructured{Object: obj} +// Expect(k8sClient.Create(ctx, &u)).ShouldNot(Succeed()) +// }, +// Entry("when MautrixSignal spec is missing", map[string]interface{}{}), +// Entry("when MautrixSignal spec is empty", map[string]interface{}{ +// "spec": map[string]interface{}{}, +// }), +// Entry("when MautrixSignal spec is missing Synapse reference", map[string]interface{}{ +// "spec": map[string]interface{}{ +// "configMap": map[string]interface{}{ +// "name": "dummy", +// }, +// }, +// }), +// Entry("when MautrixSignal spec Synapse doesn't has a name", map[string]interface{}{ +// "spec": map[string]interface{}{ +// "synapse": map[string]interface{}{ +// "namespase": "dummy", +// }, +// }, +// }), +// Entry("when MautrixSignal spec ConfigMap doesn't specify a Name", map[string]interface{}{ +// "spec": map[string]interface{}{ +// "configMap": map[string]interface{}{ +// "namespace": "dummy", +// }, +// "synapse": map[string]interface{}{ +// "name": "dummy", +// }, +// }, +// }), +// // This should not work but passes +// PEntry("when MautrixSignal spec possesses an invalid field", map[string]interface{}{ +// "spec": map[string]interface{}{ +// "synapse": map[string]interface{}{ +// "name": "dummy", +// }, +// "invalidSpecFiels": "random", +// }, +// }), +// ) + +// DescribeTable("Creating a correct MautrixSignal instance", +// func(mautrixsignal map[string]interface{}) { +// // Augment base mautrixsignal obj with additional fields +// for key, value := range mautrixsignal { +// obj[key] = value +// } +// // Create Unstructured object from mautrixsignal obj +// u := unstructured.Unstructured{Object: obj} +// // Use DryRun option to avoid cleaning up resources +// opt := client.CreateOptions{DryRun: []string{"All"}} +// Expect(k8sClient.Create(ctx, &u, &opt)).Should(Succeed()) +// }, +// Entry( +// "when the Configuration file is provided via a ConfigMap", +// map[string]interface{}{ +// "spec": map[string]interface{}{ +// "configMap": map[string]interface{}{ +// "name": "dummy", +// "namespace": "dummy", +// }, +// "synapse": map[string]interface{}{ +// "name": "dummy", +// "namespace": "dummy", +// }, +// }, +// }, +// ), +// Entry( +// "when optional Synapse Namespace and ConfigMap Namespace are missing", +// map[string]interface{}{ +// "spec": map[string]interface{}{ +// "configMap": map[string]interface{}{ +// "name": "dummy", +// }, +// "synapse": map[string]interface{}{ +// "name": "dummy", +// }, +// }, +// }, +// ), +// ) +// }) + +// Context("When creating a valid Synapse instance", func() { +// var synapse *synapsev1alpha1.Synapse +// var createdConfigMap *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 expectedOwnerReference metav1.OwnerReference +// var synapseSpec synapsev1alpha1.SynapseSpec + +// var initSynapseVariables = func() { +// // Init variables +// synapseLookupKey = types.NamespacedName{Name: SynapseName, Namespace: SynapseNamespace} +// createdConfigMap = &corev1.ConfigMap{} +// createdPVC = &corev1.PersistentVolumeClaim{} +// createdDeployment = &appsv1.Deployment{} +// createdService = &corev1.Service{} +// createdServiceAccount = &corev1.ServiceAccount{} +// createdRoleBinding = &rbacv1.RoleBinding{} +// // The OwnerReference UID must be set after the Synapse instance has been +// // created. See the JustBeforeEach node. +// expectedOwnerReference = metav1.OwnerReference{ +// Kind: "Synapse", +// APIVersion: "synapse.opdev.io/v1alpha1", +// Name: SynapseName, +// Controller: utils.BoolAddr(true), +// BlockOwnerDeletion: utils.BoolAddr(true), +// } +// } + +// var createSynapseInstance = func() { +// By("Creating the Synapse instance") +// synapse = &synapsev1alpha1.Synapse{ +// ObjectMeta: metav1.ObjectMeta{ +// Name: SynapseName, +// Namespace: SynapseNamespace, +// }, +// Spec: synapseSpec, +// } +// Expect(k8sClient.Create(ctx, synapse)).Should(Succeed()) + +// By("Verifying that the Synapse object was created") +// Eventually(func() bool { +// err := k8sClient.Get(ctx, synapseLookupKey, synapse) +// return err == nil +// }, timeout, interval).Should(BeTrue()) + +// expectedOwnerReference.UID = synapse.GetUID() +// } + +// var cleanupSynapseResources = func() { +// By("Cleaning up Synapse CR") +// Expect(k8sClient.Delete(ctx, synapse)).Should(Succeed()) + +// // Child resources must be manually deleted as the controllers responsible of +// // their lifecycle are not running. +// By("Cleaning up Synapse ConfigMap") +// deleteResource(createdConfigMap, synapseLookupKey, false) + +// By("Cleaning up Synapse PVC") +// deleteResource(createdPVC, synapseLookupKey, true) + +// By("Cleaning up Synapse Deployment") +// deleteResource(createdDeployment, synapseLookupKey, false) + +// By("Cleaning up Synapse Service") +// deleteResource(createdService, synapseLookupKey, false) + +// By("Cleaning up Synapse RoleBinding") +// deleteResource(createdRoleBinding, synapseLookupKey, false) + +// By("Cleaning up Synapse ServiceAccount") +// deleteResource(createdServiceAccount, synapseLookupKey, false) +// } + +// When("Specifying the Synapse configuration via Values", func() { +// BeforeAll(func() { +// initSynapseVariables() + +// synapseSpec = synapsev1alpha1.SynapseSpec{ +// Homeserver: synapsev1alpha1.SynapseHomeserver{ +// Values: &synapsev1alpha1.SynapseHomeserverValues{ +// ServerName: ServerName, +// ReportStats: ReportStats, +// }, +// }, +// } + +// createSynapseInstance() +// }) + +// AfterAll(func() { +// cleanupSynapseResources() +// }) + +// It("Should should update the Synapse Status", func() { +// expectedStatus := synapsev1alpha1.SynapseStatus{ +// State: "RUNNING", +// Reason: "", +// HomeserverConfiguration: synapsev1alpha1.SynapseStatusHomeserverConfiguration{ +// ServerName: ServerName, +// ReportStats: ReportStats, +// }, +// } +// // Status may need some time to be updated +// Eventually(func() synapsev1alpha1.SynapseStatus { +// _ = k8sClient.Get(ctx, synapseLookupKey, synapse) +// return synapse.Status +// }, timeout, interval).Should(Equal(expectedStatus)) +// }) + +// It("Should create a Synapse ConfigMap", func() { +// checkResourcePresence(createdConfigMap, synapseLookupKey, expectedOwnerReference) +// }) + +// It("Should create a Synapse PVC", func() { +// checkResourcePresence(createdPVC, synapseLookupKey, expectedOwnerReference) +// }) + +// It("Should create a Synapse Deployment", func() { +// By("Checking that a Synapse Deployment exists and is correctly configured") +// checkResourcePresence(createdDeployment, synapseLookupKey, expectedOwnerReference) + +// By("Checking that initContainers contains the required environment variables") +// envVars := []corev1.EnvVar{{ +// Name: "SYNAPSE_SERVER_NAME", +// Value: ServerName, +// }, { +// Name: "SYNAPSE_REPORT_STATS", +// Value: utils.BoolToYesNo(ReportStats), +// }} +// Expect(createdDeployment.Spec.Template.Spec.InitContainers[0].Env).Should(ContainElements(envVars)) +// }) + +// It("Should create a Synapse Service", func() { +// checkResourcePresence(createdService, synapseLookupKey, expectedOwnerReference) +// }) + +// It("Should create a Synapse ServiceAccount", func() { +// checkResourcePresence(createdServiceAccount, synapseLookupKey, expectedOwnerReference) +// }) + +// It("Should create a Synapse RoleBinding", func() { +// checkResourcePresence(createdRoleBinding, synapseLookupKey, expectedOwnerReference) +// }) +// }) + +// When("Specifying the Synapse configuration via a ConfigMap", func() { +// var inputConfigMap *corev1.ConfigMap +// var inputConfigmapData map[string]string + +// var createSynapseConfigMap = func() { +// By("Creating a ConfigMap containing a basic homeserver.yaml") +// // Populate the ConfigMap with the minimum data needed +// inputConfigMap = &corev1.ConfigMap{ +// ObjectMeta: metav1.ObjectMeta{ +// Name: InputConfigMapName, +// Namespace: SynapseNamespace, +// }, +// Data: inputConfigmapData, +// } +// Expect(k8sClient.Create(ctx, inputConfigMap)).Should(Succeed()) +// } + +// var cleanupSynapseConfigMap = func() { +// By("Cleaning up ConfigMap") +// Expect(k8sClient.Delete(ctx, inputConfigMap)).Should(Succeed()) +// } + +// When("Creating a simple Synapse instance", func() { +// BeforeAll(func() { +// initSynapseVariables() + +// 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, +// }, +// }, +// } + +// createSynapseConfigMap() +// createSynapseInstance() +// }) + +// AfterAll(func() { +// cleanupSynapseResources() +// cleanupSynapseConfigMap() +// }) + +// It("Should should update the Synapse Status", func() { +// expectedStatus := synapsev1alpha1.SynapseStatus{ +// State: "RUNNING", +// Reason: "", +// HomeserverConfiguration: synapsev1alpha1.SynapseStatusHomeserverConfiguration{ +// ServerName: ServerName, +// ReportStats: ReportStats, +// }, +// } +// // Status may need some time to be updated +// Eventually(func() synapsev1alpha1.SynapseStatus { +// _ = k8sClient.Get(ctx, synapseLookupKey, synapse) +// return synapse.Status +// }, timeout, interval).Should(Equal(expectedStatus)) +// }) + +// It("Should create a Synapse ConfigMap", func() { +// checkResourcePresence(createdConfigMap, synapseLookupKey, expectedOwnerReference) +// }) + +// It("Should create a Synapse PVC", func() { +// checkResourcePresence(createdPVC, synapseLookupKey, expectedOwnerReference) +// }) + +// It("Should create a Synapse Deployment", func() { +// By("Checking that a Synapse Deployment exists and is correctly configured") +// checkResourcePresence(createdDeployment, synapseLookupKey, expectedOwnerReference) + +// By("Checking that initContainers contains the required environment variables") +// envVars := []corev1.EnvVar{{ +// Name: "SYNAPSE_SERVER_NAME", +// Value: ServerName, +// }, { +// Name: "SYNAPSE_REPORT_STATS", +// Value: utils.BoolToYesNo(ReportStats), +// }} +// Expect(createdDeployment.Spec.Template.Spec.InitContainers[0].Env).Should(ContainElements(envVars)) +// }) + +// It("Should create a Synapse Service", func() { +// checkResourcePresence(createdService, synapseLookupKey, expectedOwnerReference) +// }) + +// It("Should create a Synapse ServiceAccount", func() { +// checkResourcePresence(createdServiceAccount, synapseLookupKey, expectedOwnerReference) +// }) + +// It("Should create a Synapse RoleBinding", func() { +// checkResourcePresence(createdRoleBinding, synapseLookupKey, expectedOwnerReference) +// }) +// }) + +// 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, +// } + +// 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")) +// g.Expect(synapse.Status.DatabaseConnectionInfo.Password).Should(Equal(string(base64encode("VerySecureSyn@psePassword!")))) +// 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()) + +// cm_data, ok := createdConfigMap.Data["homeserver.yaml"] +// g.Expect(ok).Should(BeTrue()) + +// homeserver := make(map[string]interface{}) +// g.Expect(yaml.Unmarshal([]byte(cm_data), 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 ( +// // heisenbridgePort = 9898 +// // ) + +// // var createdHeisenbridgeDeployment *appsv1.Deployment +// // var createdHeisenbridgeService *corev1.Service +// // var createdHeisenbridgeConfigMap *corev1.ConfigMap +// // var heisenbridgeLookupKey types.NamespacedName + +// // var initHeisenbridgeVariables = func() { +// // // Init vars +// // createdHeisenbridgeDeployment = &appsv1.Deployment{} +// // createdHeisenbridgeService = &corev1.Service{} +// // createdHeisenbridgeConfigMap = &corev1.ConfigMap{} + +// // heisenbridgeLookupKey = types.NamespacedName{Name: SynapseName + "-heisenbridge", Namespace: SynapseNamespace} +// // } + +// // var cleanupHeisenbridgeResources = func() { +// // By("Cleaning up the Heisenbridge Deployment") +// // deleteResource(createdHeisenbridgeDeployment, heisenbridgeLookupKey, false) + +// // By("Cleaning up the Heisenbridge Service") +// // deleteResource(createdHeisenbridgeService, heisenbridgeLookupKey, false) + +// // By("Cleaning up the Heisenbridge ConfigMap") +// // deleteResource(createdHeisenbridgeConfigMap, heisenbridgeLookupKey, false) +// // } + +// // When("Using the default configuration", func() { +// // BeforeAll(func() { +// // initSynapseVariables() +// // initHeisenbridgeVariables() + +// // 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, +// // }, +// // }, +// // Bridges: synapsev1alpha1.SynapseBridges{ +// // Heisenbridge: synapsev1alpha1.SynapseHeisenbridge{ +// // Enabled: true, +// // }, +// // }, +// // } + +// // createSynapseConfigMap() +// // createSynapseInstance() +// // }) + +// // AfterAll(func() { +// // // Cleanup Heisenbridge resources +// // cleanupSynapseResources() +// // cleanupSynapseConfigMap() +// // cleanupHeisenbridgeResources() +// // }) + +// // It("Should create a ConfigMap for Heisenbridge", func() { +// // checkResourcePresence(createdHeisenbridgeConfigMap, heisenbridgeLookupKey, expectedOwnerReference) +// // }) + +// // It("Should create a Deployment for Heisenbridge", func() { +// // checkResourcePresence(createdHeisenbridgeDeployment, heisenbridgeLookupKey, expectedOwnerReference) +// // }) + +// // It("Should create a Service for Heisenbridge", func() { +// // checkResourcePresence(createdHeisenbridgeService, heisenbridgeLookupKey, expectedOwnerReference) +// // }) + +// // It("Should update the Synapse homeserver.yaml", func() { +// // Eventually(func(g Gomega) { +// // g.Expect(k8sClient.Get(ctx, +// // types.NamespacedName{Name: SynapseName, Namespace: SynapseNamespace}, +// // createdConfigMap, +// // )).Should(Succeed()) + +// // cm_data, ok := createdConfigMap.Data["homeserver.yaml"] +// // g.Expect(ok).Should(BeTrue()) + +// // homeserver := make(map[string]interface{}) +// // g.Expect(yaml.Unmarshal([]byte(cm_data), homeserver)).Should(Succeed()) + +// // _, ok = homeserver["app_service_config_files"] +// // g.Expect(ok).Should(BeTrue()) + +// // g.Expect(homeserver["app_service_config_files"]).Should(ContainElement("/data-heisenbridge/heisenbridge.yaml")) +// // }, timeout, interval).Should(Succeed()) +// // }) +// // }) + +// // When("The user provides an input ConfigMap", func() { +// // var inputHeisenbridgeConfigMap *corev1.ConfigMap +// // var inputHeisenbridgeConfigMapData map[string]string + +// // const InputHeisenbridgeConfigMapName = "heisenbridge-input" +// // const heisenbridgeFQDN = SynapseName + "-heisenbridge." + SynapseNamespace + ".svc.cluster.local" + +// // BeforeAll(func() { +// // initSynapseVariables() +// // initHeisenbridgeVariables() + +// // 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, +// // }, +// // }, +// // Bridges: synapsev1alpha1.SynapseBridges{ +// // Heisenbridge: synapsev1alpha1.SynapseHeisenbridge{ +// // Enabled: true, +// // ConfigMap: synapsev1alpha1.SynapseHeisenbridgeConfigMap{ +// // Name: InputHeisenbridgeConfigMapName, +// // }, +// // }, +// // }, +// // } + +// // By("Creating a ConfigMap containing a basic heisenbridge.yaml") +// // // Incomplete heisenbridge.yaml, containing only the +// // // required data for our tests. In particular, we +// // // will test if the URL has been correctly updated +// // inputHeisenbridgeConfigMapData = map[string]string{ +// // "heisenbridge.yaml": "url: http://10.217.5.134:" + strconv.Itoa(heisenbridgePort), +// // } + +// // inputHeisenbridgeConfigMap = &corev1.ConfigMap{ +// // ObjectMeta: metav1.ObjectMeta{ +// // Name: InputHeisenbridgeConfigMapName, +// // Namespace: SynapseNamespace, +// // }, +// // Data: inputHeisenbridgeConfigMapData, +// // } +// // Expect(k8sClient.Create(ctx, inputHeisenbridgeConfigMap)).Should(Succeed()) + +// // createSynapseConfigMap() +// // createSynapseInstance() +// // }) + +// // AfterAll(func() { +// // // Cleanup Heisenbridge resources +// // By("Cleaning up the Heisenbridge ConfigMap") +// // heisenbridgeConfigMapLookupKey := types.NamespacedName{ +// // Name: InputHeisenbridgeConfigMapName, +// // Namespace: SynapseNamespace, +// // } + +// // deleteResource(inputHeisenbridgeConfigMap, heisenbridgeConfigMapLookupKey, false) + +// // cleanupSynapseResources() +// // cleanupSynapseConfigMap() +// // cleanupHeisenbridgeResources() +// // }) + +// // It("Should create a ConfigMap for Heisenbridge", func() { +// // checkResourcePresence(createdHeisenbridgeConfigMap, heisenbridgeLookupKey, expectedOwnerReference) +// // }) + +// // It("Should create a Deployment for Heisenbridge", func() { +// // checkResourcePresence(createdHeisenbridgeDeployment, heisenbridgeLookupKey, expectedOwnerReference) +// // }) + +// // It("Should create a Service for Heisenbridge", func() { +// // checkResourcePresence(createdHeisenbridgeService, heisenbridgeLookupKey, expectedOwnerReference) +// // }) + +// // It("Should add url value to the created Heisenbridge ConfigMap", func() { +// // Eventually(func(g Gomega) { +// // g.Expect(k8sClient.Get(ctx, heisenbridgeLookupKey, inputHeisenbridgeConfigMap)).Should(Succeed()) + +// // cm_data, ok := inputHeisenbridgeConfigMap.Data["heisenbridge.yaml"] +// // g.Expect(ok).Should(BeTrue()) + +// // heisenbridge := make(map[string]interface{}) +// // g.Expect(yaml.Unmarshal([]byte(cm_data), heisenbridge)).Should(Succeed()) + +// // _, ok = heisenbridge["url"] +// // g.Expect(ok).Should(BeTrue()) + +// // g.Expect(heisenbridge["url"]).To(Equal("http://" + heisenbridgeFQDN + ":" + strconv.Itoa(heisenbridgePort))) +// // }, timeout, interval).Should(Succeed()) +// // }) +// // }) +// // }) + +// // When("Enabling the mautrix-signal", func() { +// // const ( +// // mautrixSignalPort = 29328 +// // ) + +// // var createdSignaldDeployment *appsv1.Deployment +// // var createdSignaldPVC *corev1.PersistentVolumeClaim +// // var createdMautrixSignalServiceAccount *corev1.ServiceAccount +// // var createdMautrixSignalRoleBinding *rbacv1.RoleBinding +// // var createdMautrixSignalDeployment *appsv1.Deployment +// // var createdMautrixSignalPVC *corev1.PersistentVolumeClaim +// // var createdMautrixSignalService *corev1.Service +// // var createdMautrixSignalConfigMap *corev1.ConfigMap +// // var mautrixSignalLookupKey types.NamespacedName +// // var signaldLookupKey types.NamespacedName + +// // var initMautrixSignalVariables = func() { +// // // Init vars +// // createdSignaldDeployment = &appsv1.Deployment{} +// // createdSignaldPVC = &corev1.PersistentVolumeClaim{} +// // createdMautrixSignalServiceAccount = &corev1.ServiceAccount{} +// // createdMautrixSignalRoleBinding = &rbacv1.RoleBinding{} +// // createdMautrixSignalDeployment = &appsv1.Deployment{} +// // createdMautrixSignalPVC = &corev1.PersistentVolumeClaim{} +// // createdMautrixSignalService = &corev1.Service{} +// // createdMautrixSignalConfigMap = &corev1.ConfigMap{} + +// // signaldLookupKey = types.NamespacedName{Name: SynapseName + "-signald", Namespace: SynapseNamespace} +// // mautrixSignalLookupKey = types.NamespacedName{Name: SynapseName + "-mautrixsignal", Namespace: SynapseNamespace} +// // } + +// // var cleanupMautrixSignalResources = func() { +// // By("Cleaning up the signald Deployment") +// // deleteResource(createdSignaldDeployment, signaldLookupKey, false) + +// // By("Cleaning up the mautrix-signal Deployment") +// // deleteResource(createdMautrixSignalDeployment, mautrixSignalLookupKey, false) + +// // By("Cleaning up the signald PVC") +// // deleteResource(createdSignaldPVC, signaldLookupKey, true) + +// // By("Cleaning up the mautrix-signal PVC") +// // deleteResource(createdMautrixSignalPVC, mautrixSignalLookupKey, true) + +// // By("Cleaning up the mautrix-signal Service") +// // deleteResource(createdMautrixSignalService, mautrixSignalLookupKey, false) + +// // By("Cleaning up the mautrix-signal ConfigMap") +// // deleteResource(createdMautrixSignalConfigMap, mautrixSignalLookupKey, false) + +// // By("Cleaning up mautrix-signal RoleBinding") +// // deleteResource(createdMautrixSignalRoleBinding, mautrixSignalLookupKey, false) + +// // By("Cleaning up mautrix-signal ServiceAccount") +// // deleteResource(createdMautrixSignalServiceAccount, mautrixSignalLookupKey, false) +// // } + +// // When("Using the default configuration", func() { +// // BeforeAll(func() { +// // initSynapseVariables() +// // initMautrixSignalVariables() + +// // 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, +// // }, +// // }, +// // Bridges: synapsev1alpha1.SynapseBridges{ +// // MautrixSignal: synapsev1alpha1.SynapseMautrixSignal{ +// // Enabled: true, +// // }, +// // }, +// // } + +// // createSynapseConfigMap() +// // createSynapseInstance() +// // }) + +// // AfterAll(func() { +// // // Cleanup mautrix-signal resources +// // cleanupSynapseResources() +// // cleanupSynapseConfigMap() +// // cleanupMautrixSignalResources() +// // }) + +// // It("Should create a Deployment for signald", func() { +// // checkResourcePresence(createdSignaldDeployment, signaldLookupKey, expectedOwnerReference) +// // }) + +// // It("Should create a PVC for signald", func() { +// // checkResourcePresence(createdSignaldPVC, signaldLookupKey, expectedOwnerReference) +// // }) + +// // It("Should create a ConfigMap for mautrix-signal", func() { +// // checkResourcePresence(createdMautrixSignalConfigMap, mautrixSignalLookupKey, expectedOwnerReference) +// // }) + +// // It("Should create a Role Binding for mautrix-signal", func() { +// // checkResourcePresence(createdMautrixSignalRoleBinding, mautrixSignalLookupKey, expectedOwnerReference) +// // }) + +// // It("Should create a Service Account for mautrix-signal", func() { +// // checkResourcePresence(createdMautrixSignalServiceAccount, mautrixSignalLookupKey, expectedOwnerReference) +// // }) + +// // It("Should create a Deployment for mautrix-signal", func() { +// // checkResourcePresence(createdMautrixSignalDeployment, mautrixSignalLookupKey, expectedOwnerReference) +// // }) + +// // It("Should create a PVC for mautrix-signal", func() { +// // checkResourcePresence(createdMautrixSignalPVC, mautrixSignalLookupKey, expectedOwnerReference) +// // }) + +// // It("Should create a Service for mautrix-signal", func() { +// // checkResourcePresence(createdMautrixSignalService, mautrixSignalLookupKey, expectedOwnerReference) +// // }) + +// // It("Should update the Synapse homeserver.yaml", func() { +// // Eventually(func(g Gomega) { +// // g.Expect(k8sClient.Get(ctx, +// // types.NamespacedName{Name: SynapseName, Namespace: SynapseNamespace}, +// // createdConfigMap, +// // )).Should(Succeed()) + +// // cm_data, ok := createdConfigMap.Data["homeserver.yaml"] +// // g.Expect(ok).Should(BeTrue()) + +// // homeserver := make(map[string]interface{}) +// // g.Expect(yaml.Unmarshal([]byte(cm_data), homeserver)).Should(Succeed()) + +// // _, ok = homeserver["app_service_config_files"] +// // g.Expect(ok).Should(BeTrue()) + +// // g.Expect(homeserver["app_service_config_files"]).Should(ContainElement("/data-mautrixsignal/registration.yaml")) +// // }, timeout, interval).Should(Succeed()) +// // }) +// // }) + +// // When("The user provides an input ConfigMap", func() { +// // var inputMautrixSignalConfigMap *corev1.ConfigMap +// // var inputMautrixSignalConfigMapData map[string]string + +// // const InputMautrixSignalConfigMapName = "mautrix-signal-input" +// // const mautrixSignalFQDN = SynapseName + "-mautrixsignal." + SynapseNamespace + ".svc.cluster.local" +// // const synapseFQDN = SynapseName + "." + SynapseNamespace + ".svc.cluster.local" + +// // BeforeAll(func() { +// // initSynapseVariables() +// // initMautrixSignalVariables() + +// // 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, +// // }, +// // }, +// // Bridges: synapsev1alpha1.SynapseBridges{ +// // MautrixSignal: synapsev1alpha1.SynapseMautrixSignal{ +// // Enabled: true, +// // ConfigMap: synapsev1alpha1.SynapseMautrixSignalConfigMap{ +// // Name: InputMautrixSignalConfigMapName, +// // }, +// // }, +// // }, +// // } + +// // By("Creating a ConfigMap containing a basic config.yaml") +// // // Incomplete config.yaml, containing only the +// // // required data for our tests. We test that those +// // // values are correctly updated +// // configYaml := ` +// // homeserver: +// // address: http://localhost:8008 +// // domain: mydomain.com +// // appservice: +// // address: http://localhost:29328 +// // signal: +// // socket_path: /var/run/signald/signald.sock +// // bridge: +// // permissions: +// // "*": "relay" +// // "mydomain.com": "user" +// // "@admin:mydomain.com": "admin" +// // logging: +// // handlers: +// // file: +// // filename: ./mautrix-signal.log` + +// // inputMautrixSignalConfigMapData = map[string]string{ +// // "config.yaml": configYaml, +// // } + +// // inputMautrixSignalConfigMap = &corev1.ConfigMap{ +// // ObjectMeta: metav1.ObjectMeta{ +// // Name: InputMautrixSignalConfigMapName, +// // Namespace: SynapseNamespace, +// // }, +// // Data: inputMautrixSignalConfigMapData, +// // } +// // Expect(k8sClient.Create(ctx, inputMautrixSignalConfigMap)).Should(Succeed()) + +// // createSynapseConfigMap() +// // createSynapseInstance() +// // }) + +// // AfterAll(func() { +// // // Cleanup mautrix-signal resources +// // By("Cleaning up the mautrix-signal ConfigMap") +// // mautrixSignalConfigMapLookupKey := types.NamespacedName{ +// // Name: InputMautrixSignalConfigMapName, +// // Namespace: SynapseNamespace, +// // } + +// // deleteResource(inputMautrixSignalConfigMap, mautrixSignalConfigMapLookupKey, false) + +// // cleanupSynapseResources() +// // cleanupSynapseConfigMap() +// // cleanupMautrixSignalResources() +// // }) + +// // It("Should create a Deployment for signald", func() { +// // checkResourcePresence(createdMautrixSignalDeployment, signaldLookupKey, expectedOwnerReference) +// // }) + +// // It("Should create a PVC for signald", func() { +// // checkResourcePresence(createdSignaldPVC, signaldLookupKey, expectedOwnerReference) +// // }) + +// // It("Should create a ConfigMap for mautrix-signal", func() { +// // checkResourcePresence(createdMautrixSignalConfigMap, mautrixSignalLookupKey, expectedOwnerReference) +// // }) + +// // It("Should create a Role Binding for mautrix-signal", func() { +// // checkResourcePresence(createdMautrixSignalRoleBinding, mautrixSignalLookupKey, expectedOwnerReference) +// // }) + +// // It("Should create a Service Account for mautrix-signal", func() { +// // checkResourcePresence(createdMautrixSignalServiceAccount, mautrixSignalLookupKey, expectedOwnerReference) +// // }) + +// // It("Should create a Deployment for mautrix-signal", func() { +// // checkResourcePresence(createdMautrixSignalDeployment, mautrixSignalLookupKey, expectedOwnerReference) +// // }) + +// // It("Should create a PVC for mautrix-signal", func() { +// // checkResourcePresence(createdMautrixSignalPVC, mautrixSignalLookupKey, expectedOwnerReference) +// // }) + +// // It("Should create a Service for mautrix-signal", func() { +// // checkResourcePresence(createdMautrixSignalService, mautrixSignalLookupKey, expectedOwnerReference) +// // }) + +// // It("Should overwrite necessary values in the created mautrix-signal ConfigMap", func() { +// // Eventually(func(g Gomega) { +// // By("Verifying that the mautrixsignal ConfigMap exists") +// // g.Expect(k8sClient.Get(ctx, mautrixSignalLookupKey, inputMautrixSignalConfigMap)).Should(Succeed()) + +// // cm_data, ok := inputMautrixSignalConfigMap.Data["config.yaml"] +// // g.Expect(ok).Should(BeTrue()) + +// // config := make(map[string]interface{}) +// // g.Expect(yaml.Unmarshal([]byte(cm_data), config)).Should(Succeed()) + +// // By("Verifying that the homeserver configuration has been updated") +// // configHomeserver, ok := config["homeserver"].(map[interface{}]interface{}) +// // g.Expect(ok).Should(BeTrue()) +// // g.Expect(configHomeserver["address"]).To(Equal("http://" + synapseFQDN + ":8008")) +// // g.Expect(configHomeserver["domain"]).To(Equal(ServerName)) + +// // By("Verifying that the appservice configuration has been updated") +// // configAppservice, ok := config["appservice"].(map[interface{}]interface{}) +// // g.Expect(ok).Should(BeTrue()) +// // g.Expect(configAppservice["address"]).To(Equal("http://" + mautrixSignalFQDN + ":" + strconv.Itoa(mautrixSignalPort))) + +// // By("Verifying that the signal configuration has been updated") +// // configSignal, ok := config["signal"].(map[interface{}]interface{}) +// // g.Expect(ok).Should(BeTrue()) +// // g.Expect(configSignal["socket_path"]).To(Equal("/signald/signald.sock")) + +// // By("Verifying that the permissions have been updated") +// // configBridge, ok := config["bridge"].(map[interface{}]interface{}) +// // g.Expect(ok).Should(BeTrue()) +// // configBridgePermissions, ok := configBridge["permissions"].(map[interface{}]interface{}) +// // g.Expect(ok).Should(BeTrue()) +// // g.Expect(configBridgePermissions).Should(HaveKeyWithValue("*", "relay")) +// // g.Expect(configBridgePermissions).Should(HaveKeyWithValue(ServerName, "user")) +// // g.Expect(configBridgePermissions).Should(HaveKeyWithValue("@admin:"+ServerName, "admin")) + +// // By("Verifying that the log configuration file path have been updated") +// // configLogging, ok := config["logging"].(map[interface{}]interface{}) +// // g.Expect(ok).Should(BeTrue()) +// // configLoggingHandlers, ok := configLogging["handlers"].(map[interface{}]interface{}) +// // g.Expect(ok).Should(BeTrue()) +// // configLoggingHandlersFile, ok := configLoggingHandlers["file"].(map[interface{}]interface{}) +// // g.Expect(ok).Should(BeTrue()) +// // g.Expect(configLoggingHandlersFile["filename"]).To(Equal("/data/mautrix-signal.log")) +// // }, timeout, interval).Should(Succeed()) +// // }) +// // }) +// // }) + +// }) +// }) + +// Context("When creating an incorrect Synapse instance", func() { +// var synapse *synapsev1alpha1.Synapse + +// BeforeEach(func() { +// By("Creating a Synapse instance which refers an absent ConfigMap") +// synapse = &synapsev1alpha1.Synapse{ +// ObjectMeta: metav1.ObjectMeta{ +// Name: SynapseName, +// Namespace: SynapseNamespace, +// }, +// Spec: synapsev1alpha1.SynapseSpec{ +// Homeserver: synapsev1alpha1.SynapseHomeserver{ +// ConfigMap: &synapsev1alpha1.SynapseHomeserverConfigMap{ +// Name: InputConfigMapName, +// }, +// }, +// }, +// } +// Expect(k8sClient.Create(ctx, synapse)).Should(Succeed()) +// }) + +// AfterEach(func() { +// By("Cleaning up Synapse CR") +// Expect(k8sClient.Delete(ctx, synapse)).Should(Succeed()) +// }) + +// It("Should get in a failed state and not create child objects", func() { +// reason := "ConfigMap " + InputConfigMapName + " does not exist in namespace " + SynapseNamespace +// checkSubresourceAbsence(reason) +// }) +// }) +// }) + +// 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, +// } + +// 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 + +// BeforeAll(func() { +// 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." +// checkSubresourceAbsence(reason) +// }) +// }) +// }) +// }) diff --git a/controllers/synapse/synapse_mautrixsignal_deployment.go b/controllers/synapse/mautrixsignal/mautrixsignal_deployment.go similarity index 81% rename from controllers/synapse/synapse_mautrixsignal_deployment.go rename to controllers/synapse/mautrixsignal/mautrixsignal_deployment.go index 83d9016..74ee5a3 100644 --- a/controllers/synapse/synapse_mautrixsignal_deployment.go +++ b/controllers/synapse/mautrixsignal/mautrixsignal_deployment.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package synapse +package mautrixsignal import ( "context" @@ -23,30 +23,37 @@ import ( corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ctrl "sigs.k8s.io/controller-runtime" - "sigs.k8s.io/controller-runtime/pkg/client" synapsev1alpha1 "github.com/opdev/synapse-operator/apis/synapse/v1alpha1" + "github.com/opdev/synapse-operator/helpers/reconcile" reconc "github.com/opdev/synapse-operator/helpers/reconcileresults" ) // labelsForMautrixSignal returns the labels for selecting the resources // belonging to the given synapse CR name. func labelsForMautrixSignal(name string) map[string]string { - return map[string]string{"app": "mautrix-signal", "synapse_cr": name} + return map[string]string{"app": "mautrix-signal", "mautrixsignal_cr": name} } // reconcileMautrixSignalDeployment is a function of type subreconcilerFuncs, // to be called in the main reconciliation loop. // // It reconciles the Deployment for mautrix-signal to its desired state. -func (r *SynapseReconciler) reconcileMautrixSignalDeployment(synapse *synapsev1alpha1.Synapse, ctx context.Context) (*ctrl.Result, error) { - objectMetaMautrixSignal := setObjectMeta(r.GetMautrixSignalResourceName(*synapse), synapse.Namespace, map[string]string{}) - if err := r.reconcileResource( +func (r *MautrixSignalReconciler) reconcileMautrixSignalDeployment(i interface{}, ctx context.Context) (*ctrl.Result, error) { + ms := i.(*synapsev1alpha1.MautrixSignal) + + objectMetaMautrixSignal := reconcile.SetObjectMeta(ms.Name, ms.Namespace, map[string]string{}) + + desiredDeployment, err := r.deploymentForMautrixSignal(ms, objectMetaMautrixSignal) + if err != nil { + return reconc.RequeueWithError(err) + } + + if err := reconcile.ReconcileResource( ctx, - r.deploymentForMautrixSignal, - synapse, + r.Client, + desiredDeployment, &appsv1.Deployment{}, - objectMetaMautrixSignal, ); err != nil { return reconc.RequeueWithError(err) } @@ -55,8 +62,8 @@ func (r *SynapseReconciler) reconcileMautrixSignalDeployment(synapse *synapsev1a } // deploymentForMautrixSignal returns a Deployment object for the mautrix-signal bridge -func (r *SynapseReconciler) deploymentForMautrixSignal(s *synapsev1alpha1.Synapse, objectMeta metav1.ObjectMeta) (client.Object, error) { - ls := labelsForMautrixSignal(s.Name) +func (r *MautrixSignalReconciler) deploymentForMautrixSignal(ms *synapsev1alpha1.MautrixSignal, objectMeta metav1.ObjectMeta) (*appsv1.Deployment, error) { + ls := labelsForMautrixSignal(ms.Name) replicas := int32(1) // The associated mautrix-signal objects (ConfigMap, PVC, SA) share the @@ -66,7 +73,7 @@ func (r *SynapseReconciler) deploymentForMautrixSignal(s *synapsev1alpha1.Synaps mautrixSignalServiceAccountName := objectMeta.Name // The Signald PVC name is the Synapse object name with "-signald" appended - SignaldPVCName := r.GetSignaldResourceName(*s) + SignaldPVCName := GetSignaldResourceName(*ms) dep := &appsv1.Deployment{ ObjectMeta: objectMeta, @@ -141,7 +148,7 @@ func (r *SynapseReconciler) deploymentForMautrixSignal(s *synapsev1alpha1.Synaps }, } // Set Synapse instance as the owner and controller - if err := ctrl.SetControllerReference(s, dep, r.Scheme); err != nil { + if err := ctrl.SetControllerReference(ms, dep, r.Scheme); err != nil { return &appsv1.Deployment{}, err } return dep, nil diff --git a/controllers/synapse/synapse_mautrixsignal_pvc.go b/controllers/synapse/mautrixsignal/mautrixsignal_pvc.go similarity index 69% rename from controllers/synapse/synapse_mautrixsignal_pvc.go rename to controllers/synapse/mautrixsignal/mautrixsignal_pvc.go index 668ec34..d8f039c 100644 --- a/controllers/synapse/synapse_mautrixsignal_pvc.go +++ b/controllers/synapse/mautrixsignal/mautrixsignal_pvc.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package synapse +package mautrixsignal import ( "context" @@ -23,9 +23,9 @@ import ( "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ctrl "sigs.k8s.io/controller-runtime" - "sigs.k8s.io/controller-runtime/pkg/client" synapsev1alpha1 "github.com/opdev/synapse-operator/apis/synapse/v1alpha1" + "github.com/opdev/synapse-operator/helpers/reconcile" reconc "github.com/opdev/synapse-operator/helpers/reconcileresults" ) @@ -33,14 +33,21 @@ import ( // called in the main reconciliation loop. // // It reconciles the PVC for mautrix-signal to its desired state. -func (r *SynapseReconciler) reconcileMautrixSignalPVC(synapse *synapsev1alpha1.Synapse, ctx context.Context) (*ctrl.Result, error) { - objectMetaMautrixSignal := setObjectMeta(r.GetMautrixSignalResourceName(*synapse), synapse.Namespace, map[string]string{}) - if err := r.reconcileResource( +func (r *MautrixSignalReconciler) reconcileMautrixSignalPVC(i interface{}, ctx context.Context) (*ctrl.Result, error) { + ms := i.(*synapsev1alpha1.MautrixSignal) + + objectMetaMautrixSignal := reconcile.SetObjectMeta(ms.Name, ms.Namespace, map[string]string{}) + + desiredPVC, err := r.persistentVolumeClaimForMautrixSignal(ms, objectMetaMautrixSignal) + if err != nil { + return reconc.RequeueWithError(err) + } + + if err := reconcile.ReconcileResource( ctx, - r.persistentVolumeClaimForMautrixSignal, - synapse, + r.Client, + desiredPVC, &corev1.PersistentVolumeClaim{}, - objectMetaMautrixSignal, ); err != nil { return reconc.RequeueWithError(err) } @@ -49,7 +56,7 @@ func (r *SynapseReconciler) reconcileMautrixSignalPVC(synapse *synapsev1alpha1.S } // persistentVolumeClaimForMautrixSignal returns a mautrix-signal PVC object -func (r *SynapseReconciler) persistentVolumeClaimForMautrixSignal(s *synapsev1alpha1.Synapse, objectMeta metav1.ObjectMeta) (client.Object, error) { +func (r *MautrixSignalReconciler) persistentVolumeClaimForMautrixSignal(ms *synapsev1alpha1.MautrixSignal, objectMeta metav1.ObjectMeta) (*corev1.PersistentVolumeClaim, error) { pvcmode := corev1.PersistentVolumeFilesystem pvc := &corev1.PersistentVolumeClaim{ @@ -66,7 +73,7 @@ func (r *SynapseReconciler) persistentVolumeClaimForMautrixSignal(s *synapsev1al } // Set Synapse instance as the owner and controller - if err := ctrl.SetControllerReference(s, pvc, r.Scheme); err != nil { + if err := ctrl.SetControllerReference(ms, pvc, r.Scheme); err != nil { return &corev1.PersistentVolumeClaim{}, err } return pvc, nil diff --git a/controllers/synapse/synapse_mautrixsignal_service.go b/controllers/synapse/mautrixsignal/mautrixsignal_service.go similarity index 67% rename from controllers/synapse/synapse_mautrixsignal_service.go rename to controllers/synapse/mautrixsignal/mautrixsignal_service.go index 3d913b8..f1d113d 100644 --- a/controllers/synapse/synapse_mautrixsignal_service.go +++ b/controllers/synapse/mautrixsignal/mautrixsignal_service.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package synapse +package mautrixsignal import ( "context" @@ -23,9 +23,9 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/util/intstr" ctrl "sigs.k8s.io/controller-runtime" - "sigs.k8s.io/controller-runtime/pkg/client" synapsev1alpha1 "github.com/opdev/synapse-operator/apis/synapse/v1alpha1" + "github.com/opdev/synapse-operator/helpers/reconcile" reconc "github.com/opdev/synapse-operator/helpers/reconcileresults" ) @@ -33,14 +33,21 @@ import ( // be called in the main reconciliation loop. // // It reconciles the Service for mautrix-signal to its desired state. -func (r *SynapseReconciler) reconcileMautrixSignalService(synapse *synapsev1alpha1.Synapse, ctx context.Context) (*ctrl.Result, error) { - objectMetaMautrixSignal := setObjectMeta(r.GetMautrixSignalResourceName(*synapse), synapse.Namespace, map[string]string{}) - if err := r.reconcileResource( +func (r *MautrixSignalReconciler) reconcileMautrixSignalService(i interface{}, ctx context.Context) (*ctrl.Result, error) { + ms := i.(*synapsev1alpha1.MautrixSignal) + + objectMetaMautrixSignal := reconcile.SetObjectMeta(ms.Name, ms.Namespace, map[string]string{}) + + desiredService, err := r.serviceForMautrixSignal(ms, objectMetaMautrixSignal) + if err != nil { + return reconc.RequeueWithError(err) + } + + if err := reconcile.ReconcileResource( ctx, - r.serviceForMautrixSignal, - synapse, + r.Client, + desiredService, &corev1.Service{}, - objectMetaMautrixSignal, ); err != nil { return reconc.RequeueWithError(err) } @@ -49,7 +56,7 @@ func (r *SynapseReconciler) reconcileMautrixSignalService(synapse *synapsev1alph } // serviceForMautrixSignal returns a mautrix-signal Service object -func (r *SynapseReconciler) serviceForMautrixSignal(s *synapsev1alpha1.Synapse, objectMeta metav1.ObjectMeta) (client.Object, error) { +func (r *MautrixSignalReconciler) serviceForMautrixSignal(ms *synapsev1alpha1.MautrixSignal, objectMeta metav1.ObjectMeta) (*corev1.Service, error) { service := &corev1.Service{ ObjectMeta: objectMeta, Spec: corev1.ServiceSpec{ @@ -59,12 +66,12 @@ func (r *SynapseReconciler) serviceForMautrixSignal(s *synapsev1alpha1.Synapse, Port: 29328, TargetPort: intstr.FromInt(29328), }}, - Selector: labelsForMautrixSignal(s.Name), + Selector: labelsForMautrixSignal(ms.Name), Type: corev1.ServiceTypeClusterIP, }, } // Set Synapse instance as the owner and controller - if err := ctrl.SetControllerReference(s, service, r.Scheme); err != nil { + if err := ctrl.SetControllerReference(ms, service, r.Scheme); err != nil { return &corev1.Service{}, err } return service, nil diff --git a/controllers/synapse/synapse_mautrixsignal_serviceaccount.go b/controllers/synapse/mautrixsignal/mautrixsignal_serviceaccount.go similarity index 62% rename from controllers/synapse/synapse_mautrixsignal_serviceaccount.go rename to controllers/synapse/mautrixsignal/mautrixsignal_serviceaccount.go index 34223a2..1586da0 100644 --- a/controllers/synapse/synapse_mautrixsignal_serviceaccount.go +++ b/controllers/synapse/mautrixsignal/mautrixsignal_serviceaccount.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package synapse +package mautrixsignal import ( "context" @@ -26,6 +26,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" synapsev1alpha1 "github.com/opdev/synapse-operator/apis/synapse/v1alpha1" + "github.com/opdev/synapse-operator/helpers/reconcile" reconc "github.com/opdev/synapse-operator/helpers/reconcileresults" ) @@ -33,14 +34,21 @@ import ( // subreconcilerFuncs, to be called in the main reconciliation loop. // // It reconciles the ServiceAccount for mautrix-signal to its desired state. -func (r *SynapseReconciler) reconcileMautrixSignalServiceAccount(synapse *synapsev1alpha1.Synapse, ctx context.Context) (*ctrl.Result, error) { - objectMetaMautrixSignal := setObjectMeta(r.GetMautrixSignalResourceName(*synapse), synapse.Namespace, map[string]string{}) - if err := r.reconcileResource( +func (r *MautrixSignalReconciler) reconcileMautrixSignalServiceAccount(i interface{}, ctx context.Context) (*ctrl.Result, error) { + ms := i.(*synapsev1alpha1.MautrixSignal) + + objectMetaMautrixSignal := reconcile.SetObjectMeta(ms.Name, ms.Namespace, map[string]string{}) + + desiredServiceAccount, err := r.serviceAccountForMautrixSignal(ms, objectMetaMautrixSignal) + if err != nil { + return reconc.RequeueWithError(err) + } + + if err := reconcile.ReconcileResource( ctx, - r.serviceAccountForMautrixSignal, - synapse, + r.Client, + desiredServiceAccount, &corev1.ServiceAccount{}, - objectMetaMautrixSignal, ); err != nil { return reconc.RequeueWithError(err) } @@ -49,14 +57,16 @@ func (r *SynapseReconciler) reconcileMautrixSignalServiceAccount(synapse *synaps } // serviceAccountForMautrixSignal returns a ServiceAccount object for running the mautrix-signal bridge -func (r *SynapseReconciler) serviceAccountForMautrixSignal(s *synapsev1alpha1.Synapse, objectMeta metav1.ObjectMeta) (client.Object, error) { +func (r *MautrixSignalReconciler) serviceAccountForMautrixSignal(i interface{}, objectMeta metav1.ObjectMeta) (client.Object, error) { + ms := i.(*synapsev1alpha1.MautrixSignal) + // TODO: https://github.com/opdev/synapse-operator/issues/19 sa := &corev1.ServiceAccount{ ObjectMeta: objectMeta, } // Set Synapse instance as the owner and controller - if err := ctrl.SetControllerReference(s, sa, r.Scheme); err != nil { + if err := ctrl.SetControllerReference(ms, sa, r.Scheme); err != nil { return &corev1.ServiceAccount{}, err } return sa, nil @@ -66,14 +76,21 @@ func (r *SynapseReconciler) serviceAccountForMautrixSignal(s *synapsev1alpha1.Sy // to be called in the main reconciliation loop. // // It reconciles the RoleBinding for mautrix-signal to its desired state. -func (r *SynapseReconciler) reconcileMautrixSignalRoleBinding(synapse *synapsev1alpha1.Synapse, ctx context.Context) (*ctrl.Result, error) { - objectMetaMautrixSignal := setObjectMeta(r.GetMautrixSignalResourceName(*synapse), synapse.Namespace, map[string]string{}) - if err := r.reconcileResource( +func (r *MautrixSignalReconciler) reconcileMautrixSignalRoleBinding(i interface{}, ctx context.Context) (*ctrl.Result, error) { + ms := i.(*synapsev1alpha1.MautrixSignal) + + objectMetaMautrixSignal := reconcile.SetObjectMeta(ms.Name, ms.Namespace, map[string]string{}) + + desiredRoleBinding, err := r.roleBindingForMautrixSignal(ms, objectMetaMautrixSignal) + if err != nil { + return reconc.RequeueWithError(err) + } + + if err := reconcile.ReconcileResource( ctx, - r.roleBindingForMautrixSignal, - synapse, + r.Client, + desiredRoleBinding, &rbacv1.RoleBinding{}, - objectMetaMautrixSignal, ); err != nil { return reconc.RequeueWithError(err) } @@ -82,7 +99,7 @@ func (r *SynapseReconciler) reconcileMautrixSignalRoleBinding(synapse *synapsev1 } // roleBindingForMautrixSignal returns a RoleBinding object for the mautrix-signal bridge -func (r *SynapseReconciler) roleBindingForMautrixSignal(s *synapsev1alpha1.Synapse, objectMeta metav1.ObjectMeta) (client.Object, error) { +func (r *MautrixSignalReconciler) roleBindingForMautrixSignal(ms *synapsev1alpha1.MautrixSignal, objectMeta metav1.ObjectMeta) (*rbacv1.RoleBinding, error) { // TODO: https://github.com/opdev/synapse-operator/issues/19 rb := &rbacv1.RoleBinding{ ObjectMeta: objectMeta, @@ -99,7 +116,7 @@ func (r *SynapseReconciler) roleBindingForMautrixSignal(s *synapsev1alpha1.Synap } // Set Synapse instance as the owner and controller - if err := ctrl.SetControllerReference(s, rb, r.Scheme); err != nil { + if err := ctrl.SetControllerReference(ms, rb, r.Scheme); err != nil { return &rbacv1.RoleBinding{}, err } return rb, nil diff --git a/controllers/synapse/mautrixsignal/mautrixsignal_test.go b/controllers/synapse/mautrixsignal/mautrixsignal_test.go new file mode 100644 index 0000000..67bf7be --- /dev/null +++ b/controllers/synapse/mautrixsignal/mautrixsignal_test.go @@ -0,0 +1,543 @@ +// +//This file contains unit tests for the Synapse package +// + +package mautrixsignal + +// import ( +// "context" + +// . "github.com/onsi/ginkgo/v2" +// . "github.com/onsi/gomega" +// "gopkg.in/yaml.v2" + +// pgov1beta1 "github.com/crunchydata/postgres-operator/pkg/apis/postgres-operator.crunchydata.com/v1beta1" +// synapsev1alpha1 "github.com/opdev/synapse-operator/apis/synapse/v1alpha1" +// "github.com/opdev/synapse-operator/helpers/utils" +// corev1 "k8s.io/api/core/v1" +// ) + +// var _ = Describe("Unit tests for Synapse package", Label("unit"), func() { +// // Testing ParseHomeserverConfigMap +// Context("When parsing the homeserver ConfigMap", func() { +// var r SynapseReconciler +// var ctx context.Context +// var s synapsev1alpha1.Synapse +// var cm corev1.ConfigMap +// var data map[string]string + +// BeforeEach(func() { +// // Init variables needed for each test +// r = SynapseReconciler{} +// ctx = context.Background() +// s = synapsev1alpha1.Synapse{} +// }) + +// Context("Extracting the server name and report stats value from ConfigMap", func() { +// JustBeforeEach(func() { +// cm = corev1.ConfigMap{ +// Data: data, +// } +// }) + +// When("when server name is valid and report stat set to true", func() { +// BeforeEach(func() { +// data = map[string]string{ +// "homeserver.yaml": "server_name: my-server-name\nreport_stats: true", +// } +// }) + +// It("should accordingly update the Synapse Status", func() { +// Expect(r.ParseHomeserverConfigMap(ctx, &s, cm)).Should(Succeed()) +// Expect(s.Status.HomeserverConfiguration.ServerName).Should(Equal("my-server-name")) +// Expect(s.Status.HomeserverConfiguration.ReportStats).Should(BeTrue()) +// }) +// }) + +// When("when server name is valid and report stat set to false", func() { +// BeforeEach(func() { +// data = map[string]string{ +// "homeserver.yaml": "server_name: my-server-name\nreport_stats: false", +// } +// }) + +// It("should accordingly update the Synapse Status", func() { +// Expect(r.ParseHomeserverConfigMap(ctx, &s, cm)).Should(Succeed()) +// Expect(s.Status.HomeserverConfiguration.ServerName).Should(Equal("my-server-name")) +// Expect(s.Status.HomeserverConfiguration.ReportStats).Should(BeFalse()) +// }) +// }) + +// When("when 'homeserver.yaml' is not present in the ConfigMap data", func() { +// BeforeEach(func() { +// data = map[string]string{ +// "not-homeserver.yaml": "server_name: my-server-name\nreport_stats: true", +// } +// }) + +// It("should fail to parse the ConfigMap data", func() { +// Expect(r.ParseHomeserverConfigMap(ctx, &s, cm)).ShouldNot(Succeed()) +// }) +// }) + +// When("when 'server_name' is not present in the 'homeserver.yaml'", func() { +// BeforeEach(func() { +// data = map[string]string{ +// "homeserver.yaml": "not_server_name: my-server-name\nreport_stats: true", +// } +// }) + +// It("should fail to parse the ConfigMap data", func() { +// Expect(r.ParseHomeserverConfigMap(ctx, &s, cm)).ShouldNot(Succeed()) +// }) +// }) + +// When("when 'server_name' is not a valid string", func() { +// BeforeEach(func() { +// data = map[string]string{ +// "homeserver.yaml": "server_name: 1234567890\nreport_stats: true", +// } +// }) + +// It("should fail to parse the ConfigMap data", func() { +// Expect(r.ParseHomeserverConfigMap(ctx, &s, cm)).ShouldNot(Succeed()) +// }) +// }) + +// When("when 'report_stats' is not present in the 'homeserver.yaml'", func() { +// BeforeEach(func() { +// data = map[string]string{ +// "homeserver.yaml": "server_name: my-server-name\nnot_report_stats: true", +// } +// }) + +// It("should fail to parse the ConfigMap data", func() { +// Expect(r.ParseHomeserverConfigMap(ctx, &s, cm)).ShouldNot(Succeed()) +// }) +// }) + +// When("when 'report_stats' is not a valid bool", func() { +// BeforeEach(func() { +// data = map[string]string{ +// "homeserver.yaml": "server_name: my-server-name\nreport_stats: not-a-bool", +// } +// }) + +// It("should fail to parse the ConfigMap data", func() { +// Expect(r.ParseHomeserverConfigMap(ctx, &s, cm)).ShouldNot(Succeed()) +// }) +// }) + +// When("when 'homeserver.yaml' is not valid YAML", func() { +// BeforeEach(func() { +// data = map[string]string{ +// "homeserver.yaml": "not valid YAML!", +// } +// }) + +// It("should fail to parse the ConfigMap data", func() { +// Expect(r.ParseHomeserverConfigMap(ctx, &s, cm)).ShouldNot(Succeed()) +// }) +// }) +// }) +// }) + +// 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}.