├── .dockerignore ├── .github └── workflows │ ├── container-images.yml │ └── tests.yaml ├── .gitignore ├── Dockerfile ├── LICENSE ├── Makefile ├── PROJECT ├── README.md ├── apis └── synapse │ └── v1alpha1 │ ├── groupversion_info.go │ ├── heisenbridge_types.go │ ├── mautrixsignal_types.go │ ├── synapse_types.go │ └── zz_generated.deepcopy.go ├── bundle.Dockerfile ├── bundle ├── manifests │ ├── synapse-operator-controller-manager-metrics-service_v1_service.yaml │ ├── synapse-operator-manager-config_v1_configmap.yaml │ ├── synapse-operator-metrics-reader_rbac.authorization.k8s.io_v1_clusterrole.yaml │ ├── synapse-operator.clusterserviceversion.yaml │ ├── synapse.opdev.io_heisenbridges.yaml │ ├── synapse.opdev.io_mautrixsignals.yaml │ └── synapse.opdev.io_synapses.yaml ├── metadata │ └── annotations.yaml └── tests │ └── scorecard │ └── config.yaml ├── config ├── crd │ ├── bases │ │ ├── synapse.opdev.io_heisenbridges.yaml │ │ ├── synapse.opdev.io_mautrixsignals.yaml │ │ └── synapse.opdev.io_synapses.yaml │ ├── kustomization.yaml │ ├── kustomizeconfig.yaml │ └── patches │ │ ├── cainjection_in_synapse_heisenbridges.yaml │ │ ├── cainjection_in_synapse_mautrixsignals.yaml │ │ ├── cainjection_in_synapses.yaml │ │ ├── oneofhomeserver_in_synapses.yaml │ │ ├── webhook_in_synapse_heisenbridges.yaml │ │ ├── webhook_in_synapse_mautrixsignals.yaml │ │ └── webhook_in_synapses.yaml ├── default │ ├── kustomization.yaml │ ├── manager_auth_proxy_patch.yaml │ └── manager_config_patch.yaml ├── manager │ ├── controller_manager_config.yaml │ ├── kustomization.yaml │ └── manager.yaml ├── manifests │ ├── bases │ │ └── synapse-operator.clusterserviceversion.yaml │ └── kustomization.yaml ├── prometheus │ ├── kustomization.yaml │ └── monitor.yaml ├── rbac │ ├── auth_proxy_client_clusterrole.yaml │ ├── auth_proxy_role.yaml │ ├── auth_proxy_role_binding.yaml │ ├── auth_proxy_service.yaml │ ├── kustomization.yaml │ ├── leader_election_role.yaml │ ├── leader_election_role_binding.yaml │ ├── role.yaml │ ├── role_binding.yaml │ ├── service_account.yaml │ ├── synapse_editor_role.yaml │ ├── synapse_heisenbridge_editor_role.yaml │ ├── synapse_heisenbridge_viewer_role.yaml │ ├── synapse_mautrixsignal_editor_role.yaml │ ├── synapse_mautrixsignal_viewer_role.yaml │ └── synapse_viewer_role.yaml ├── samples │ ├── kustomization.yaml │ ├── synapse_v1alpha1_heisenbridge.yaml │ ├── synapse_v1alpha1_mautrixsignal.yaml │ └── synapse_v1alpha1_synapse.yaml └── scorecard │ ├── bases │ └── config.yaml │ ├── kustomization.yaml │ └── patches │ ├── basic.config.yaml │ └── olm.config.yaml ├── controllers └── synapse │ ├── heisenbridge │ ├── heisenbridge_configmap.go │ ├── heisenbridge_controller.go │ ├── heisenbridge_controller_test.go │ ├── heisenbridge_deployment.go │ ├── heisenbridge_service.go │ ├── heisenbridge_test.go │ └── suite_test.go │ ├── mautrixsignal │ ├── mautrixsignal_configmap.go │ ├── mautrixsignal_controller.go │ ├── mautrixsignal_controller_test.go │ ├── mautrixsignal_deployment.go │ ├── mautrixsignal_pvc.go │ ├── mautrixsignal_service.go │ ├── mautrixsignal_serviceaccount.go │ ├── mautrixsignal_test.go │ └── suite_test.go │ └── synapse │ ├── suite_test.go │ ├── synapse_configmap.go │ ├── synapse_controller.go │ ├── synapse_controller_test.go │ ├── synapse_deployment.go │ ├── synapse_postgrescluster.go │ ├── synapse_pvc.go │ ├── synapse_service.go │ ├── synapse_serviceaccount.go │ └── synapse_test.go ├── examples ├── 01-my-first-synapse-deployment │ └── synapse.yaml ├── 02-using-existing-configmap │ ├── homeserver.yaml │ └── synapse.yaml ├── 03-deploying-postgresql │ └── synapse.yaml ├── 04-deploying-heisenbridge │ ├── A-default-configuration │ │ ├── heisenbridge.yaml │ │ └── synapse.yaml │ └── B-using-existing-configmap │ │ ├── heisenbridge.yaml │ │ ├── heisenbridge_config.yaml │ │ └── synapse.yaml ├── 05-deploying-mautrixsignal │ ├── A-default-configuration │ │ ├── mautrixsignal.yaml │ │ └── synapse.yaml │ └── B-using-existing-configmap │ │ ├── config.yaml │ │ ├── mautrixsignal.yaml │ │ └── synapse.yaml └── README.md ├── go.mod ├── go.sum ├── hack └── boilerplate.go.txt ├── helpers ├── reconcile │ └── reconcile.go └── utils │ ├── bridge_utils.go │ ├── configmap_utils.go │ ├── synapse_utils.go │ ├── tests_utils.go │ └── utils.go ├── install └── synapse-operator.yaml ├── internal └── templates │ ├── heisenbridge_configmap.yaml │ ├── heisenbridge_deployment.yaml │ ├── mautrixsignal_configmap.yaml │ ├── mautrixsignal_deployment.yaml │ ├── pvc.yaml │ ├── rolebinding.yaml │ ├── service.yaml │ ├── serviceaccount.yaml │ ├── synapse_configmap.yaml │ ├── synapse_deployment.yaml │ └── templates.go └── main.go /.dockerignore: -------------------------------------------------------------------------------- 1 | # More info: https://docs.docker.com/engine/reference/builder/#dockerignore-file 2 | # Ignore build and test binaries. 3 | bin/ 4 | testbin/ 5 | -------------------------------------------------------------------------------- /.github/workflows/container-images.yml: -------------------------------------------------------------------------------- 1 | name: Build and push container images on Quay.io 2 | 3 | on: 4 | push: 5 | # Publish semver tags as releases. 6 | tags: [ 'v*.*.*' ] 7 | 8 | jobs: 9 | build-and-push: 10 | 11 | runs-on: ubuntu-latest 12 | 13 | steps: 14 | - name: Checkout repository 15 | uses: actions/checkout@v3 16 | 17 | - name: Login to Quay.io 18 | uses: docker/login-action@v2 19 | with: 20 | registry: quay.io 21 | username: ${{ secrets.QUAY_ROBOT_NAME }} 22 | password: ${{ secrets.QUAY_ROBOT_TOKEN }} 23 | 24 | - name: Build and Push controller image 25 | run: make docker-build && make docker-push 26 | 27 | - name: Build and Push controller image 28 | run: make bundle-build && make bundle-push 29 | -------------------------------------------------------------------------------- /.github/workflows/tests.yaml: -------------------------------------------------------------------------------- 1 | name: Run unit and integration tests 2 | 3 | on: 4 | push: 5 | branches: [ "master" ] 6 | pull_request: 7 | branches: [ "master" ] 8 | 9 | jobs: 10 | 11 | build: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v3 15 | 16 | - name: Set up Go 17 | uses: actions/setup-go@v3 18 | with: 19 | go-version: 1.23 20 | 21 | - name: Run test suite 22 | run: make test 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.exe~ 4 | *.dll 5 | *.so 6 | *.dylib 7 | 8 | bin/ 9 | 10 | # Test binary, built with `go test -c` 11 | *.test 12 | 13 | # Output of the go coverage tool, specifically when used with LiteIDE 14 | *.out 15 | 16 | # Dependency directories (remove the comment below to include it) 17 | # vendor/ 18 | 19 | .vscode/ 20 | 21 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # Build the manager binary 2 | FROM golang:1.23 as builder 3 | 4 | WORKDIR /workspace 5 | # Copy the Go Modules manifests 6 | COPY go.mod go.mod 7 | COPY go.sum go.sum 8 | # cache deps before building and copying source so that we don't need to re-download as much 9 | # and so that source changes don't invalidate our downloaded layer 10 | RUN go mod download 11 | 12 | # Copy the go source 13 | COPY main.go main.go 14 | COPY apis/ apis/ 15 | COPY controllers/ controllers/ 16 | COPY helpers/ helpers/ 17 | COPY internal/ internal/ 18 | 19 | # Build 20 | RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -a -o manager main.go 21 | 22 | # Use distroless as minimal base image to package the manager binary 23 | # Refer to https://github.com/GoogleContainerTools/distroless for more details 24 | FROM gcr.io/distroless/static:nonroot 25 | WORKDIR / 26 | COPY --from=builder /workspace/manager . 27 | USER 65532:65532 28 | 29 | ENTRYPOINT ["/manager"] 30 | -------------------------------------------------------------------------------- /PROJECT: -------------------------------------------------------------------------------- 1 | domain: opdev.io 2 | layout: 3 | - go.kubebuilder.io/v3 4 | multigroup: true 5 | plugins: 6 | manifests.sdk.operatorframework.io/v2: {} 7 | scorecard.sdk.operatorframework.io/v2: {} 8 | projectName: synapse-operator 9 | repo: github.com/opdev/synapse-operator 10 | resources: 11 | - api: 12 | crdVersion: v1 13 | namespaced: true 14 | controller: true 15 | domain: opdev.io 16 | group: synapse 17 | kind: Synapse 18 | path: github.com/opdev/synapse-operator/apis/synapse/v1alpha1 19 | version: v1alpha1 20 | - api: 21 | crdVersion: v1 22 | namespaced: true 23 | controller: true 24 | domain: opdev.io 25 | group: synapse 26 | kind: MautrixSignal 27 | path: github.com/opdev/synapse-operator/apis/synapse/v1alpha1 28 | version: v1alpha1 29 | - api: 30 | crdVersion: v1 31 | namespaced: true 32 | controller: true 33 | domain: opdev.io 34 | group: synapse 35 | kind: Heisenbridge 36 | path: github.com/opdev/synapse-operator/apis/synapse/v1alpha1 37 | version: v1alpha1 38 | version: "3" 39 | -------------------------------------------------------------------------------- /apis/synapse/v1alpha1/groupversion_info.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Package v1alpha1 contains API Schema definitions for the synapse v1alpha1 API group 18 | // +kubebuilder:object:generate=true 19 | // +groupName=synapse.opdev.io 20 | package v1alpha1 21 | 22 | import ( 23 | "k8s.io/apimachinery/pkg/runtime/schema" 24 | "sigs.k8s.io/controller-runtime/pkg/scheme" 25 | ) 26 | 27 | var ( 28 | // GroupVersion is group version used to register these objects 29 | GroupVersion = schema.GroupVersion{Group: "synapse.opdev.io", Version: "v1alpha1"} 30 | 31 | // SchemeBuilder is used to add go types to the GroupVersionKind scheme 32 | SchemeBuilder = &scheme.Builder{GroupVersion: GroupVersion} 33 | 34 | // AddToScheme adds the types in this group-version to the given scheme. 35 | AddToScheme = SchemeBuilder.AddToScheme 36 | ) 37 | -------------------------------------------------------------------------------- /apis/synapse/v1alpha1/heisenbridge_types.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package v1alpha1 18 | 19 | import ( 20 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 21 | ) 22 | 23 | // HeisenbridgeSpec defines the desired state of Heisenbridge. The user can 24 | // either: 25 | // - enable the bridge, without specifying additional configuration options. 26 | // The bridge will be deployed with a default configuration. 27 | // - enable the bridge and specify an existing ConfigMap by its Name and 28 | // Namespace containing a heisenbridge.yaml. 29 | type HeisenbridgeSpec struct { 30 | // Holds information about the ConfigMap containing the heisenbridge.yaml 31 | // configuration file to be used as input for the configuration of the 32 | // Heisenbridge IRC Bridge. 33 | ConfigMap HeisenbridgeConfigMap `json:"configMap,omitempty"` 34 | 35 | // +kubebuilder:default:=0 36 | 37 | // Controls the verbosity of the Heisenbrige: 38 | // * 0 corresponds to normal level of logs 39 | // * 1 corresponds to "-v" 40 | // * 2 corresponds to "-vv" 41 | // * 3 corresponds to "-vvv" 42 | VerboseLevel int `json:"verboseLevel,omitempty"` 43 | 44 | // +kubebuilder:validation:Required 45 | 46 | // Name of the Synapse instance, living in the same namespace. 47 | Synapse HeisenbridgeSynapseSpec `json:"synapse"` 48 | } 49 | 50 | type HeisenbridgeSynapseSpec struct { 51 | // +kubebuilder:validation:Required 52 | 53 | // Name of the Synapse instance 54 | Name string `json:"name"` 55 | 56 | // Namespace of the Synapse instance 57 | // TODO: Complete 58 | Namespace string `json:"namespace,omitempty"` 59 | } 60 | 61 | type HeisenbridgeConfigMap struct { 62 | // +kubebuilder:validation:Required 63 | 64 | // Name of the ConfigMap in the given Namespace. 65 | Name string `json:"name"` 66 | 67 | // Namespace in which the ConfigMap is living. If left empty, the 68 | // Heisenbridge namespace is used. 69 | Namespace string `json:"namespace,omitempty"` 70 | } 71 | 72 | // HeisenbridgeStatus defines the observed state of Heisenbridge 73 | type HeisenbridgeStatus struct { 74 | // State of the Heisenbridge instance 75 | State string `json:"state,omitempty"` 76 | 77 | // Reason for the current Heisenbridge State 78 | Reason string `json:"reason,omitempty"` 79 | } 80 | 81 | //+kubebuilder:object:root=true 82 | //+kubebuilder:subresource:status 83 | 84 | // Heisenbridge is the Schema for the heisenbridges API 85 | type Heisenbridge struct { 86 | metav1.TypeMeta `json:",inline"` 87 | metav1.ObjectMeta `json:"metadata,omitempty"` 88 | 89 | // +kubebuilder:validation:Required 90 | Spec HeisenbridgeSpec `json:"spec"` 91 | Status HeisenbridgeStatus `json:"status,omitempty"` 92 | } 93 | 94 | //+kubebuilder:object:root=true 95 | 96 | // HeisenbridgeList contains a list of Heisenbridge 97 | type HeisenbridgeList struct { 98 | metav1.TypeMeta `json:",inline"` 99 | metav1.ListMeta `json:"metadata,omitempty"` 100 | Items []Heisenbridge `json:"items"` 101 | } 102 | 103 | func init() { 104 | SchemeBuilder.Register(&Heisenbridge{}, &HeisenbridgeList{}) 105 | } 106 | 107 | func (h *Heisenbridge) GetSynapseName() string { 108 | return h.Spec.Synapse.Name 109 | } 110 | 111 | func (h *Heisenbridge) GetSynapseNamespace() string { 112 | return h.Spec.Synapse.Namespace 113 | } 114 | -------------------------------------------------------------------------------- /apis/synapse/v1alpha1/mautrixsignal_types.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package v1alpha1 18 | 19 | import ( 20 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 21 | ) 22 | 23 | // MautrixSignalSpec defines the desired state of MautrixSignal. The user can 24 | // either: 25 | // - enable the bridge, without specifying additional configuration options. 26 | // The bridge will be deployed with a default configuration. 27 | // - enable the bridge and specify an existing ConfigMap by its Name and 28 | // Namespace containing a config.yaml file. 29 | type MautrixSignalSpec struct { 30 | // Holds information about the ConfigMap containing the config.yaml 31 | // configuration file to be used as input for the configuration of the 32 | // mautrix-signal bridge. 33 | ConfigMap MautrixSignalConfigMap `json:"configMap,omitempty"` 34 | 35 | // +kubebuilder:validation:Required 36 | 37 | // Name of the Synapse instance, living in the same namespace. 38 | Synapse MautrixSignalSynapseSpec `json:"synapse"` 39 | } 40 | 41 | type MautrixSignalSynapseSpec struct { 42 | // +kubebuilder:validation:Required 43 | 44 | // Name of the Synapse instance 45 | Name string `json:"name"` 46 | 47 | // Namespace of the Synapse instance 48 | // TODO: Complete 49 | Namespace string `json:"namespace,omitempty"` 50 | } 51 | 52 | type MautrixSignalConfigMap struct { 53 | // +kubebuilder:validation:Required 54 | 55 | // Name of the ConfigMap in the given Namespace. 56 | Name string `json:"name"` 57 | 58 | // Namespace in which the ConfigMap is living. If left empty, the Synapse 59 | // namespace is used. 60 | Namespace string `json:"namespace,omitempty"` 61 | } 62 | 63 | // MautrixSignalStatus defines the observed state of MautrixSignal 64 | type MautrixSignalStatus struct { 65 | // State of the MautrixSignal instance 66 | State string `json:"state,omitempty"` 67 | 68 | // Reason for the current MautrixSignal State 69 | Reason string `json:"reason,omitempty"` 70 | 71 | // Information related to the Synapse instance associated with this bridge 72 | Synapse MautrixSignalStatusSynapse `json:"synapse,omitempty"` 73 | 74 | // +kubebuilder:default:=false 75 | 76 | // Values is set to true if deploying on OpenShift 77 | IsOpenshift bool `json:"isOpenshift,omitempty"` 78 | } 79 | 80 | type MautrixSignalStatusSynapse struct { 81 | ServerName string `json:"serverName,omitempty"` 82 | } 83 | 84 | //+kubebuilder:object:root=true 85 | //+kubebuilder:subresource:status 86 | 87 | // MautrixSignal is the Schema for the mautrixsignals API 88 | type MautrixSignal struct { 89 | metav1.TypeMeta `json:",inline"` 90 | metav1.ObjectMeta `json:"metadata,omitempty"` 91 | 92 | // +kubebuilder:validation:Required 93 | Spec MautrixSignalSpec `json:"spec"` 94 | Status MautrixSignalStatus `json:"status,omitempty"` 95 | } 96 | 97 | //+kubebuilder:object:root=true 98 | 99 | // MautrixSignalList contains a list of MautrixSignal 100 | type MautrixSignalList struct { 101 | metav1.TypeMeta `json:",inline"` 102 | metav1.ListMeta `json:"metadata,omitempty"` 103 | Items []MautrixSignal `json:"items"` 104 | } 105 | 106 | func init() { 107 | SchemeBuilder.Register(&MautrixSignal{}, &MautrixSignalList{}) 108 | } 109 | 110 | func (ms *MautrixSignal) GetSynapseName() string { 111 | return ms.Spec.Synapse.Name 112 | } 113 | 114 | func (ms *MautrixSignal) GetSynapseNamespace() string { 115 | return ms.Spec.Synapse.Namespace 116 | } 117 | -------------------------------------------------------------------------------- /apis/synapse/v1alpha1/synapse_types.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package v1alpha1 18 | 19 | import ( 20 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 21 | ) 22 | 23 | // EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN! 24 | // NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized. 25 | 26 | // SynapseSpec defines the desired state of Synapse 27 | type SynapseSpec struct { 28 | // INSERT ADDITIONAL SPEC FIELDS - desired state of cluster 29 | // Important: Run "make" to regenerate code after modifying this file 30 | 31 | // +kubebuilder:validation:Required 32 | 33 | // Holds information related to the homeserver.yaml configuration file. 34 | // The user can either specify an existing ConfigMap by its Name and 35 | // Namespace containing a homeserver.yaml, or provide a set of values for 36 | // the creation of a configuration file from scratch. 37 | Homeserver SynapseHomeserver `json:"homeserver"` 38 | 39 | // +kubebuilder:default:=false 40 | 41 | // Set to true to create a new PostreSQL instance. The homeserver.yaml 42 | // 'database' section will be overwritten. 43 | CreateNewPostgreSQL bool `json:"createNewPostgreSQL,omitempty"` 44 | 45 | // +kubebuilder:default:=false 46 | 47 | // Set to true if deploying on OpenShift 48 | IsOpenshift bool `json:"isOpenshift,omitempty"` 49 | } 50 | 51 | type SynapseHomeserver struct { 52 | // Holds information about the ConfigMap containing the homeserver.yaml 53 | // configuration file to be used as input for the configuration of the 54 | // Synapse server. 55 | ConfigMap *SynapseHomeserverConfigMap `json:"configMap,omitempty"` 56 | 57 | // Holds the required values for the creation of a homeserver.yaml 58 | // configuration file by the Synapse Operator 59 | Values *SynapseHomeserverValues `json:"values,omitempty"` 60 | } 61 | 62 | type SynapseHomeserverConfigMap struct { 63 | // +kubebuilder:validation:Required 64 | 65 | // Name of the ConfigMap in the given Namespace. 66 | Name string `json:"name"` 67 | 68 | // Namespace in which the ConfigMap is living. If left empty, the Synapse 69 | // namespace is used. 70 | Namespace string `json:"namespace,omitempty"` 71 | } 72 | 73 | type SynapseHomeserverValues struct { 74 | // +kubebuilder:validation:Required 75 | 76 | // The public-facing domain of the server 77 | ServerName string `json:"serverName"` 78 | 79 | // +kubebuilder:validation:Required 80 | 81 | // Whether or not to report anonymized homeserver usage statistics 82 | ReportStats bool `json:"reportStats"` 83 | } 84 | 85 | // SynapseStatus defines the observed state of Synapse 86 | type SynapseStatus struct { 87 | // INSERT ADDITIONAL STATUS FIELD - define observed state of cluster 88 | // Important: Run "make" to regenerate code after modifying this file 89 | 90 | // Connection information to the external PostgreSQL Database 91 | DatabaseConnectionInfo SynapseStatusDatabaseConnectionInfo `json:"databaseConnectionInfo,omitempty"` 92 | 93 | // Holds configuration information for Synapse 94 | HomeserverConfiguration SynapseStatusHomeserverConfiguration `json:"homeserverConfiguration,omitempty"` 95 | 96 | // Information on the bridges deployed alongside Synapse 97 | Bridges SynapseStatusBridges `json:"bridges,omitempty"` 98 | 99 | // State of the Synapse instance 100 | State string `json:"state,omitempty"` 101 | 102 | // Reason for the current Synapse State 103 | Reason string `json:"reason,omitempty"` 104 | 105 | // +kubebuilder:default:=false 106 | NeedsReconcile bool `json:"needsReconcile,omitempty"` 107 | } 108 | 109 | type SynapseStatusBridges struct { 110 | // Information on the Heisenbridge (IRC Bridge). 111 | Heisenbridge SynapseStatusBridgesHeisenbridge `json:"heisenbridge,omitempty"` 112 | 113 | // Information on the mautrix-signal bridge. 114 | MautrixSignal SynapseStatusBridgesMautrixSignal `json:"mautrixsignal,omitempty"` 115 | } 116 | 117 | type SynapseStatusBridgesHeisenbridge struct { 118 | // +kubebuilder:default:=false 119 | 120 | // Whether a Heisenbridge has been deployed for this Synapse instance 121 | Enabled bool `json:"enabled,omitempty"` 122 | 123 | // Name of the Heisenbridge object 124 | Name string `json:"name,omitempty"` 125 | } 126 | 127 | type SynapseStatusBridgesMautrixSignal struct { 128 | // Whether a mautrix-signal has been deployed for this Synapse instance 129 | Enabled bool `json:"enabled,omitempty"` 130 | 131 | // Name of the mautrix-signal bridge object 132 | Name string `json:"name,omitempty"` 133 | } 134 | 135 | type SynapseStatusDatabaseConnectionInfo struct { 136 | // Endpoint to connect to the PostgreSQL database 137 | ConnectionURL string `json:"connectionURL,omitempty"` 138 | 139 | // Name of the database to connect to 140 | DatabaseName string `json:"databaseName,omitempty"` 141 | 142 | // User allowed to query the given database 143 | User string `json:"user,omitempty"` 144 | 145 | // Base64 encoded password 146 | Password string `json:"password,omitempty"` 147 | 148 | // State of the PostgreSQL database 149 | State string `json:"State,omitempty"` 150 | } 151 | 152 | type SynapseStatusHomeserverConfiguration struct { 153 | // The public-facing domain of the server 154 | ServerName string `json:"serverName,omitempty"` 155 | 156 | // Whether or not to report anonymized homeserver usage statistics 157 | ReportStats bool `json:"reportStats,omitempty"` 158 | } 159 | 160 | //+kubebuilder:object:root=true 161 | //+kubebuilder:subresource:status 162 | 163 | // Synapse is the Schema for the synapses API 164 | type Synapse struct { 165 | metav1.TypeMeta `json:",inline"` 166 | metav1.ObjectMeta `json:"metadata,omitempty"` 167 | 168 | // +kubebuilder:validation:Required 169 | Spec SynapseSpec `json:"spec"` 170 | Status SynapseStatus `json:"status,omitempty"` 171 | } 172 | 173 | //+kubebuilder:object:root=true 174 | 175 | // SynapseList contains a list of Synapse 176 | type SynapseList struct { 177 | metav1.TypeMeta `json:",inline"` 178 | metav1.ListMeta `json:"metadata,omitempty"` 179 | Items []Synapse `json:"items"` 180 | } 181 | 182 | func init() { 183 | SchemeBuilder.Register(&Synapse{}, &SynapseList{}) 184 | } 185 | -------------------------------------------------------------------------------- /bundle.Dockerfile: -------------------------------------------------------------------------------- 1 | FROM scratch 2 | 3 | # Core bundle labels. 4 | LABEL operators.operatorframework.io.bundle.mediatype.v1=registry+v1 5 | LABEL operators.operatorframework.io.bundle.manifests.v1=manifests/ 6 | LABEL operators.operatorframework.io.bundle.metadata.v1=metadata/ 7 | LABEL operators.operatorframework.io.bundle.package.v1=synapse-operator 8 | LABEL operators.operatorframework.io.bundle.channels.v1=alpha 9 | LABEL operators.operatorframework.io.metrics.builder=operator-sdk-v1.29.0 10 | LABEL operators.operatorframework.io.metrics.mediatype.v1=metrics+v1 11 | LABEL operators.operatorframework.io.metrics.project_layout=go.kubebuilder.io/v3 12 | 13 | # Labels for testing. 14 | LABEL operators.operatorframework.io.test.mediatype.v1=scorecard+v1 15 | LABEL operators.operatorframework.io.test.config.v1=tests/scorecard/ 16 | 17 | # Copy files to locations specified by labels. 18 | COPY bundle/manifests /manifests/ 19 | COPY bundle/metadata /metadata/ 20 | COPY bundle/tests/scorecard /tests/scorecard/ 21 | -------------------------------------------------------------------------------- /bundle/manifests/synapse-operator-controller-manager-metrics-service_v1_service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | creationTimestamp: null 5 | labels: 6 | control-plane: controller-manager 7 | name: synapse-operator-controller-manager-metrics-service 8 | spec: 9 | ports: 10 | - name: https 11 | port: 8443 12 | protocol: TCP 13 | targetPort: https 14 | selector: 15 | control-plane: controller-manager 16 | status: 17 | loadBalancer: {} 18 | -------------------------------------------------------------------------------- /bundle/manifests/synapse-operator-manager-config_v1_configmap.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | data: 3 | controller_manager_config.yaml: | 4 | apiVersion: controller-runtime.sigs.k8s.io/v1alpha1 5 | kind: ControllerManagerConfig 6 | health: 7 | healthProbeBindAddress: :8081 8 | metrics: 9 | bindAddress: 127.0.0.1:8080 10 | webhook: 11 | port: 9443 12 | leaderElection: 13 | leaderElect: true 14 | resourceName: 8d311e9b.opdev.io 15 | kind: ConfigMap 16 | metadata: 17 | name: synapse-operator-manager-config 18 | -------------------------------------------------------------------------------- /bundle/manifests/synapse-operator-metrics-reader_rbac.authorization.k8s.io_v1_clusterrole.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: ClusterRole 3 | metadata: 4 | creationTimestamp: null 5 | name: synapse-operator-metrics-reader 6 | rules: 7 | - nonResourceURLs: 8 | - /metrics 9 | verbs: 10 | - get 11 | -------------------------------------------------------------------------------- /bundle/manifests/synapse.opdev.io_heisenbridges.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apiextensions.k8s.io/v1 2 | kind: CustomResourceDefinition 3 | metadata: 4 | annotations: 5 | controller-gen.kubebuilder.io/version: v0.16.1 6 | creationTimestamp: null 7 | name: heisenbridges.synapse.opdev.io 8 | spec: 9 | group: synapse.opdev.io 10 | names: 11 | kind: Heisenbridge 12 | listKind: HeisenbridgeList 13 | plural: heisenbridges 14 | singular: heisenbridge 15 | scope: Namespaced 16 | versions: 17 | - name: v1alpha1 18 | schema: 19 | openAPIV3Schema: 20 | description: Heisenbridge is the Schema for the heisenbridges API 21 | properties: 22 | apiVersion: 23 | description: |- 24 | APIVersion defines the versioned schema of this representation of an object. 25 | Servers should convert recognized schemas to the latest internal value, and 26 | may reject unrecognized values. 27 | More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources 28 | type: string 29 | kind: 30 | description: |- 31 | Kind is a string value representing the REST resource this object represents. 32 | Servers may infer this from the endpoint the client submits requests to. 33 | Cannot be updated. 34 | In CamelCase. 35 | More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds 36 | type: string 37 | metadata: 38 | type: object 39 | spec: 40 | description: |- 41 | HeisenbridgeSpec defines the desired state of Heisenbridge. The user can 42 | either: 43 | - enable the bridge, without specifying additional configuration options. 44 | The bridge will be deployed with a default configuration. 45 | - enable the bridge and specify an existing ConfigMap by its Name and 46 | Namespace containing a heisenbridge.yaml. 47 | properties: 48 | configMap: 49 | description: |- 50 | Holds information about the ConfigMap containing the heisenbridge.yaml 51 | configuration file to be used as input for the configuration of the 52 | Heisenbridge IRC Bridge. 53 | properties: 54 | name: 55 | description: Name of the ConfigMap in the given Namespace. 56 | type: string 57 | namespace: 58 | description: |- 59 | Namespace in which the ConfigMap is living. If left empty, the 60 | Heisenbridge namespace is used. 61 | type: string 62 | required: 63 | - name 64 | type: object 65 | synapse: 66 | description: Name of the Synapse instance, living in the same namespace. 67 | properties: 68 | name: 69 | description: Name of the Synapse instance 70 | type: string 71 | namespace: 72 | description: Namespace of the Synapse instance 73 | type: string 74 | required: 75 | - name 76 | type: object 77 | verboseLevel: 78 | default: 0 79 | description: |- 80 | Controls the verbosity of the Heisenbrige: 81 | * 0 corresponds to normal level of logs 82 | * 1 corresponds to "-v" 83 | * 2 corresponds to "-vv" 84 | * 3 corresponds to "-vvv" 85 | type: integer 86 | required: 87 | - synapse 88 | type: object 89 | status: 90 | description: HeisenbridgeStatus defines the observed state of Heisenbridge 91 | properties: 92 | reason: 93 | description: Reason for the current Heisenbridge State 94 | type: string 95 | state: 96 | description: State of the Heisenbridge instance 97 | type: string 98 | type: object 99 | required: 100 | - spec 101 | type: object 102 | served: true 103 | storage: true 104 | subresources: 105 | status: {} 106 | status: 107 | acceptedNames: 108 | kind: "" 109 | plural: "" 110 | conditions: null 111 | storedVersions: null 112 | -------------------------------------------------------------------------------- /bundle/manifests/synapse.opdev.io_mautrixsignals.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apiextensions.k8s.io/v1 2 | kind: CustomResourceDefinition 3 | metadata: 4 | annotations: 5 | controller-gen.kubebuilder.io/version: v0.16.1 6 | creationTimestamp: null 7 | name: mautrixsignals.synapse.opdev.io 8 | spec: 9 | group: synapse.opdev.io 10 | names: 11 | kind: MautrixSignal 12 | listKind: MautrixSignalList 13 | plural: mautrixsignals 14 | singular: mautrixsignal 15 | scope: Namespaced 16 | versions: 17 | - name: v1alpha1 18 | schema: 19 | openAPIV3Schema: 20 | description: MautrixSignal is the Schema for the mautrixsignals API 21 | properties: 22 | apiVersion: 23 | description: |- 24 | APIVersion defines the versioned schema of this representation of an object. 25 | Servers should convert recognized schemas to the latest internal value, and 26 | may reject unrecognized values. 27 | More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources 28 | type: string 29 | kind: 30 | description: |- 31 | Kind is a string value representing the REST resource this object represents. 32 | Servers may infer this from the endpoint the client submits requests to. 33 | Cannot be updated. 34 | In CamelCase. 35 | More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds 36 | type: string 37 | metadata: 38 | type: object 39 | spec: 40 | description: |- 41 | MautrixSignalSpec defines the desired state of MautrixSignal. The user can 42 | either: 43 | - enable the bridge, without specifying additional configuration options. 44 | The bridge will be deployed with a default configuration. 45 | - enable the bridge and specify an existing ConfigMap by its Name and 46 | Namespace containing a config.yaml file. 47 | properties: 48 | configMap: 49 | description: |- 50 | Holds information about the ConfigMap containing the config.yaml 51 | configuration file to be used as input for the configuration of the 52 | mautrix-signal bridge. 53 | properties: 54 | name: 55 | description: Name of the ConfigMap in the given Namespace. 56 | type: string 57 | namespace: 58 | description: |- 59 | Namespace in which the ConfigMap is living. If left empty, the Synapse 60 | namespace is used. 61 | type: string 62 | required: 63 | - name 64 | type: object 65 | synapse: 66 | description: Name of the Synapse instance, living in the same namespace. 67 | properties: 68 | name: 69 | description: Name of the Synapse instance 70 | type: string 71 | namespace: 72 | description: Namespace of the Synapse instance 73 | type: string 74 | required: 75 | - name 76 | type: object 77 | required: 78 | - synapse 79 | type: object 80 | status: 81 | description: MautrixSignalStatus defines the observed state of MautrixSignal 82 | properties: 83 | isOpenshift: 84 | default: false 85 | description: Values is set to true if deploying on OpenShift 86 | type: boolean 87 | reason: 88 | description: Reason for the current MautrixSignal State 89 | type: string 90 | state: 91 | description: State of the MautrixSignal instance 92 | type: string 93 | synapse: 94 | description: Information related to the Synapse instance associated 95 | with this bridge 96 | properties: 97 | serverName: 98 | type: string 99 | type: object 100 | type: object 101 | required: 102 | - spec 103 | type: object 104 | served: true 105 | storage: true 106 | subresources: 107 | status: {} 108 | status: 109 | acceptedNames: 110 | kind: "" 111 | plural: "" 112 | conditions: null 113 | storedVersions: null 114 | -------------------------------------------------------------------------------- /bundle/manifests/synapse.opdev.io_synapses.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apiextensions.k8s.io/v1 2 | kind: CustomResourceDefinition 3 | metadata: 4 | annotations: 5 | controller-gen.kubebuilder.io/version: v0.16.1 6 | creationTimestamp: null 7 | name: synapses.synapse.opdev.io 8 | spec: 9 | group: synapse.opdev.io 10 | names: 11 | kind: Synapse 12 | listKind: SynapseList 13 | plural: synapses 14 | singular: synapse 15 | scope: Namespaced 16 | versions: 17 | - name: v1alpha1 18 | schema: 19 | openAPIV3Schema: 20 | description: Synapse is the Schema for the synapses API 21 | properties: 22 | apiVersion: 23 | description: |- 24 | APIVersion defines the versioned schema of this representation of an object. 25 | Servers should convert recognized schemas to the latest internal value, and 26 | may reject unrecognized values. 27 | More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources 28 | type: string 29 | kind: 30 | description: |- 31 | Kind is a string value representing the REST resource this object represents. 32 | Servers may infer this from the endpoint the client submits requests to. 33 | Cannot be updated. 34 | In CamelCase. 35 | More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds 36 | type: string 37 | metadata: 38 | type: object 39 | spec: 40 | description: SynapseSpec defines the desired state of Synapse 41 | properties: 42 | createNewPostgreSQL: 43 | default: false 44 | description: |- 45 | Set to true to create a new PostreSQL instance. The homeserver.yaml 46 | 'database' section will be overwritten. 47 | type: boolean 48 | homeserver: 49 | description: |- 50 | Holds information related to the homeserver.yaml configuration file. 51 | The user can either specify an existing ConfigMap by its Name and 52 | Namespace containing a homeserver.yaml, or provide a set of values for 53 | the creation of a configuration file from scratch. 54 | oneOf: 55 | - required: 56 | - configMap 57 | - required: 58 | - values 59 | properties: 60 | configMap: 61 | description: |- 62 | Holds information about the ConfigMap containing the homeserver.yaml 63 | configuration file to be used as input for the configuration of the 64 | Synapse server. 65 | properties: 66 | name: 67 | description: Name of the ConfigMap in the given Namespace. 68 | type: string 69 | namespace: 70 | description: |- 71 | Namespace in which the ConfigMap is living. If left empty, the Synapse 72 | namespace is used. 73 | type: string 74 | required: 75 | - name 76 | type: object 77 | values: 78 | description: |- 79 | Holds the required values for the creation of a homeserver.yaml 80 | configuration file by the Synapse Operator 81 | properties: 82 | reportStats: 83 | description: Whether or not to report anonymized homeserver 84 | usage statistics 85 | type: boolean 86 | serverName: 87 | description: The public-facing domain of the server 88 | type: string 89 | required: 90 | - reportStats 91 | - serverName 92 | type: object 93 | type: object 94 | isOpenshift: 95 | default: false 96 | description: Set to true if deploying on OpenShift 97 | type: boolean 98 | required: 99 | - homeserver 100 | type: object 101 | status: 102 | description: SynapseStatus defines the observed state of Synapse 103 | properties: 104 | bridges: 105 | description: Information on the bridges deployed alongside Synapse 106 | properties: 107 | heisenbridge: 108 | description: Information on the Heisenbridge (IRC Bridge). 109 | properties: 110 | enabled: 111 | default: false 112 | description: Whether a Heisenbridge has been deployed for 113 | this Synapse instance 114 | type: boolean 115 | name: 116 | description: Name of the Heisenbridge object 117 | type: string 118 | type: object 119 | mautrixsignal: 120 | description: Information on the mautrix-signal bridge. 121 | properties: 122 | enabled: 123 | description: Whether a mautrix-signal has been deployed for 124 | this Synapse instance 125 | type: boolean 126 | name: 127 | description: Name of the mautrix-signal bridge object 128 | type: string 129 | type: object 130 | type: object 131 | databaseConnectionInfo: 132 | description: Connection information to the external PostgreSQL Database 133 | properties: 134 | State: 135 | description: State of the PostgreSQL database 136 | type: string 137 | connectionURL: 138 | description: Endpoint to connect to the PostgreSQL database 139 | type: string 140 | databaseName: 141 | description: Name of the database to connect to 142 | type: string 143 | password: 144 | description: Base64 encoded password 145 | type: string 146 | user: 147 | description: User allowed to query the given database 148 | type: string 149 | type: object 150 | homeserverConfiguration: 151 | description: Holds configuration information for Synapse 152 | properties: 153 | reportStats: 154 | description: Whether or not to report anonymized homeserver usage 155 | statistics 156 | type: boolean 157 | serverName: 158 | description: The public-facing domain of the server 159 | type: string 160 | type: object 161 | needsReconcile: 162 | default: false 163 | type: boolean 164 | reason: 165 | description: Reason for the current Synapse State 166 | type: string 167 | state: 168 | description: State of the Synapse instance 169 | type: string 170 | type: object 171 | required: 172 | - spec 173 | type: object 174 | served: true 175 | storage: true 176 | subresources: 177 | status: {} 178 | status: 179 | acceptedNames: 180 | kind: "" 181 | plural: "" 182 | conditions: null 183 | storedVersions: null 184 | -------------------------------------------------------------------------------- /bundle/metadata/annotations.yaml: -------------------------------------------------------------------------------- 1 | annotations: 2 | # Core bundle annotations. 3 | operators.operatorframework.io.bundle.mediatype.v1: registry+v1 4 | operators.operatorframework.io.bundle.manifests.v1: manifests/ 5 | operators.operatorframework.io.bundle.metadata.v1: metadata/ 6 | operators.operatorframework.io.bundle.package.v1: synapse-operator 7 | operators.operatorframework.io.bundle.channels.v1: alpha 8 | operators.operatorframework.io.metrics.builder: operator-sdk-v1.29.0 9 | operators.operatorframework.io.metrics.mediatype.v1: metrics+v1 10 | operators.operatorframework.io.metrics.project_layout: go.kubebuilder.io/v3 11 | 12 | # Annotations for testing. 13 | operators.operatorframework.io.test.mediatype.v1: scorecard+v1 14 | operators.operatorframework.io.test.config.v1: tests/scorecard/ 15 | -------------------------------------------------------------------------------- /bundle/tests/scorecard/config.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: scorecard.operatorframework.io/v1alpha3 2 | kind: Configuration 3 | metadata: 4 | name: config 5 | stages: 6 | - parallel: true 7 | tests: 8 | - entrypoint: 9 | - scorecard-test 10 | - basic-check-spec 11 | image: quay.io/operator-framework/scorecard-test:v1.12.0 12 | labels: 13 | suite: basic 14 | test: basic-check-spec-test 15 | storage: 16 | spec: 17 | mountPath: {} 18 | - entrypoint: 19 | - scorecard-test 20 | - olm-bundle-validation 21 | image: quay.io/operator-framework/scorecard-test:v1.12.0 22 | labels: 23 | suite: olm 24 | test: olm-bundle-validation-test 25 | storage: 26 | spec: 27 | mountPath: {} 28 | - entrypoint: 29 | - scorecard-test 30 | - olm-crds-have-validation 31 | image: quay.io/operator-framework/scorecard-test:v1.12.0 32 | labels: 33 | suite: olm 34 | test: olm-crds-have-validation-test 35 | storage: 36 | spec: 37 | mountPath: {} 38 | - entrypoint: 39 | - scorecard-test 40 | - olm-crds-have-resources 41 | image: quay.io/operator-framework/scorecard-test:v1.12.0 42 | labels: 43 | suite: olm 44 | test: olm-crds-have-resources-test 45 | storage: 46 | spec: 47 | mountPath: {} 48 | - entrypoint: 49 | - scorecard-test 50 | - olm-spec-descriptors 51 | image: quay.io/operator-framework/scorecard-test:v1.12.0 52 | labels: 53 | suite: olm 54 | test: olm-spec-descriptors-test 55 | storage: 56 | spec: 57 | mountPath: {} 58 | - entrypoint: 59 | - scorecard-test 60 | - olm-status-descriptors 61 | image: quay.io/operator-framework/scorecard-test:v1.12.0 62 | labels: 63 | suite: olm 64 | test: olm-status-descriptors-test 65 | storage: 66 | spec: 67 | mountPath: {} 68 | storage: 69 | spec: 70 | mountPath: {} 71 | -------------------------------------------------------------------------------- /config/crd/bases/synapse.opdev.io_heisenbridges.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: apiextensions.k8s.io/v1 3 | kind: CustomResourceDefinition 4 | metadata: 5 | annotations: 6 | controller-gen.kubebuilder.io/version: v0.16.1 7 | name: heisenbridges.synapse.opdev.io 8 | spec: 9 | group: synapse.opdev.io 10 | names: 11 | kind: Heisenbridge 12 | listKind: HeisenbridgeList 13 | plural: heisenbridges 14 | singular: heisenbridge 15 | scope: Namespaced 16 | versions: 17 | - name: v1alpha1 18 | schema: 19 | openAPIV3Schema: 20 | description: Heisenbridge is the Schema for the heisenbridges API 21 | properties: 22 | apiVersion: 23 | description: |- 24 | APIVersion defines the versioned schema of this representation of an object. 25 | Servers should convert recognized schemas to the latest internal value, and 26 | may reject unrecognized values. 27 | More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources 28 | type: string 29 | kind: 30 | description: |- 31 | Kind is a string value representing the REST resource this object represents. 32 | Servers may infer this from the endpoint the client submits requests to. 33 | Cannot be updated. 34 | In CamelCase. 35 | More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds 36 | type: string 37 | metadata: 38 | type: object 39 | spec: 40 | description: |- 41 | HeisenbridgeSpec defines the desired state of Heisenbridge. The user can 42 | either: 43 | - enable the bridge, without specifying additional configuration options. 44 | The bridge will be deployed with a default configuration. 45 | - enable the bridge and specify an existing ConfigMap by its Name and 46 | Namespace containing a heisenbridge.yaml. 47 | properties: 48 | configMap: 49 | description: |- 50 | Holds information about the ConfigMap containing the heisenbridge.yaml 51 | configuration file to be used as input for the configuration of the 52 | Heisenbridge IRC Bridge. 53 | properties: 54 | name: 55 | description: Name of the ConfigMap in the given Namespace. 56 | type: string 57 | namespace: 58 | description: |- 59 | Namespace in which the ConfigMap is living. If left empty, the 60 | Heisenbridge namespace is used. 61 | type: string 62 | required: 63 | - name 64 | type: object 65 | synapse: 66 | description: Name of the Synapse instance, living in the same namespace. 67 | properties: 68 | name: 69 | description: Name of the Synapse instance 70 | type: string 71 | namespace: 72 | description: Namespace of the Synapse instance 73 | type: string 74 | required: 75 | - name 76 | type: object 77 | verboseLevel: 78 | default: 0 79 | description: |- 80 | Controls the verbosity of the Heisenbrige: 81 | * 0 corresponds to normal level of logs 82 | * 1 corresponds to "-v" 83 | * 2 corresponds to "-vv" 84 | * 3 corresponds to "-vvv" 85 | type: integer 86 | required: 87 | - synapse 88 | type: object 89 | status: 90 | description: HeisenbridgeStatus defines the observed state of Heisenbridge 91 | properties: 92 | reason: 93 | description: Reason for the current Heisenbridge State 94 | type: string 95 | state: 96 | description: State of the Heisenbridge instance 97 | type: string 98 | type: object 99 | required: 100 | - spec 101 | type: object 102 | served: true 103 | storage: true 104 | subresources: 105 | status: {} 106 | -------------------------------------------------------------------------------- /config/crd/bases/synapse.opdev.io_mautrixsignals.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: apiextensions.k8s.io/v1 3 | kind: CustomResourceDefinition 4 | metadata: 5 | annotations: 6 | controller-gen.kubebuilder.io/version: v0.16.1 7 | name: mautrixsignals.synapse.opdev.io 8 | spec: 9 | group: synapse.opdev.io 10 | names: 11 | kind: MautrixSignal 12 | listKind: MautrixSignalList 13 | plural: mautrixsignals 14 | singular: mautrixsignal 15 | scope: Namespaced 16 | versions: 17 | - name: v1alpha1 18 | schema: 19 | openAPIV3Schema: 20 | description: MautrixSignal is the Schema for the mautrixsignals API 21 | properties: 22 | apiVersion: 23 | description: |- 24 | APIVersion defines the versioned schema of this representation of an object. 25 | Servers should convert recognized schemas to the latest internal value, and 26 | may reject unrecognized values. 27 | More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources 28 | type: string 29 | kind: 30 | description: |- 31 | Kind is a string value representing the REST resource this object represents. 32 | Servers may infer this from the endpoint the client submits requests to. 33 | Cannot be updated. 34 | In CamelCase. 35 | More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds 36 | type: string 37 | metadata: 38 | type: object 39 | spec: 40 | description: |- 41 | MautrixSignalSpec defines the desired state of MautrixSignal. The user can 42 | either: 43 | - enable the bridge, without specifying additional configuration options. 44 | The bridge will be deployed with a default configuration. 45 | - enable the bridge and specify an existing ConfigMap by its Name and 46 | Namespace containing a config.yaml file. 47 | properties: 48 | configMap: 49 | description: |- 50 | Holds information about the ConfigMap containing the config.yaml 51 | configuration file to be used as input for the configuration of the 52 | mautrix-signal bridge. 53 | properties: 54 | name: 55 | description: Name of the ConfigMap in the given Namespace. 56 | type: string 57 | namespace: 58 | description: |- 59 | Namespace in which the ConfigMap is living. If left empty, the Synapse 60 | namespace is used. 61 | type: string 62 | required: 63 | - name 64 | type: object 65 | synapse: 66 | description: Name of the Synapse instance, living in the same namespace. 67 | properties: 68 | name: 69 | description: Name of the Synapse instance 70 | type: string 71 | namespace: 72 | description: Namespace of the Synapse instance 73 | type: string 74 | required: 75 | - name 76 | type: object 77 | required: 78 | - synapse 79 | type: object 80 | status: 81 | description: MautrixSignalStatus defines the observed state of MautrixSignal 82 | properties: 83 | isOpenshift: 84 | default: false 85 | description: Values is set to true if deploying on OpenShift 86 | type: boolean 87 | reason: 88 | description: Reason for the current MautrixSignal State 89 | type: string 90 | state: 91 | description: State of the MautrixSignal instance 92 | type: string 93 | synapse: 94 | description: Information related to the Synapse instance associated 95 | with this bridge 96 | properties: 97 | serverName: 98 | type: string 99 | type: object 100 | type: object 101 | required: 102 | - spec 103 | type: object 104 | served: true 105 | storage: true 106 | subresources: 107 | status: {} 108 | -------------------------------------------------------------------------------- /config/crd/bases/synapse.opdev.io_synapses.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: apiextensions.k8s.io/v1 3 | kind: CustomResourceDefinition 4 | metadata: 5 | annotations: 6 | controller-gen.kubebuilder.io/version: v0.16.1 7 | name: synapses.synapse.opdev.io 8 | spec: 9 | group: synapse.opdev.io 10 | names: 11 | kind: Synapse 12 | listKind: SynapseList 13 | plural: synapses 14 | singular: synapse 15 | scope: Namespaced 16 | versions: 17 | - name: v1alpha1 18 | schema: 19 | openAPIV3Schema: 20 | description: Synapse is the Schema for the synapses API 21 | properties: 22 | apiVersion: 23 | description: |- 24 | APIVersion defines the versioned schema of this representation of an object. 25 | Servers should convert recognized schemas to the latest internal value, and 26 | may reject unrecognized values. 27 | More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources 28 | type: string 29 | kind: 30 | description: |- 31 | Kind is a string value representing the REST resource this object represents. 32 | Servers may infer this from the endpoint the client submits requests to. 33 | Cannot be updated. 34 | In CamelCase. 35 | More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds 36 | type: string 37 | metadata: 38 | type: object 39 | spec: 40 | description: SynapseSpec defines the desired state of Synapse 41 | properties: 42 | createNewPostgreSQL: 43 | default: false 44 | description: |- 45 | Set to true to create a new PostreSQL instance. The homeserver.yaml 46 | 'database' section will be overwritten. 47 | type: boolean 48 | homeserver: 49 | description: |- 50 | Holds information related to the homeserver.yaml configuration file. 51 | The user can either specify an existing ConfigMap by its Name and 52 | Namespace containing a homeserver.yaml, or provide a set of values for 53 | the creation of a configuration file from scratch. 54 | properties: 55 | configMap: 56 | description: |- 57 | Holds information about the ConfigMap containing the homeserver.yaml 58 | configuration file to be used as input for the configuration of the 59 | Synapse server. 60 | properties: 61 | name: 62 | description: Name of the ConfigMap in the given Namespace. 63 | type: string 64 | namespace: 65 | description: |- 66 | Namespace in which the ConfigMap is living. If left empty, the Synapse 67 | namespace is used. 68 | type: string 69 | required: 70 | - name 71 | type: object 72 | values: 73 | description: |- 74 | Holds the required values for the creation of a homeserver.yaml 75 | configuration file by the Synapse Operator 76 | properties: 77 | reportStats: 78 | description: Whether or not to report anonymized homeserver 79 | usage statistics 80 | type: boolean 81 | serverName: 82 | description: The public-facing domain of the server 83 | type: string 84 | required: 85 | - reportStats 86 | - serverName 87 | type: object 88 | type: object 89 | isOpenshift: 90 | default: false 91 | description: Set to true if deploying on OpenShift 92 | type: boolean 93 | required: 94 | - homeserver 95 | type: object 96 | status: 97 | description: SynapseStatus defines the observed state of Synapse 98 | properties: 99 | bridges: 100 | description: Information on the bridges deployed alongside Synapse 101 | properties: 102 | heisenbridge: 103 | description: Information on the Heisenbridge (IRC Bridge). 104 | properties: 105 | enabled: 106 | default: false 107 | description: Whether a Heisenbridge has been deployed for 108 | this Synapse instance 109 | type: boolean 110 | name: 111 | description: Name of the Heisenbridge object 112 | type: string 113 | type: object 114 | mautrixsignal: 115 | description: Information on the mautrix-signal bridge. 116 | properties: 117 | enabled: 118 | description: Whether a mautrix-signal has been deployed for 119 | this Synapse instance 120 | type: boolean 121 | name: 122 | description: Name of the mautrix-signal bridge object 123 | type: string 124 | type: object 125 | type: object 126 | databaseConnectionInfo: 127 | description: Connection information to the external PostgreSQL Database 128 | properties: 129 | State: 130 | description: State of the PostgreSQL database 131 | type: string 132 | connectionURL: 133 | description: Endpoint to connect to the PostgreSQL database 134 | type: string 135 | databaseName: 136 | description: Name of the database to connect to 137 | type: string 138 | password: 139 | description: Base64 encoded password 140 | type: string 141 | user: 142 | description: User allowed to query the given database 143 | type: string 144 | type: object 145 | homeserverConfiguration: 146 | description: Holds configuration information for Synapse 147 | properties: 148 | reportStats: 149 | description: Whether or not to report anonymized homeserver usage 150 | statistics 151 | type: boolean 152 | serverName: 153 | description: The public-facing domain of the server 154 | type: string 155 | type: object 156 | needsReconcile: 157 | default: false 158 | type: boolean 159 | reason: 160 | description: Reason for the current Synapse State 161 | type: string 162 | state: 163 | description: State of the Synapse instance 164 | type: string 165 | type: object 166 | required: 167 | - spec 168 | type: object 169 | served: true 170 | storage: true 171 | subresources: 172 | status: {} 173 | -------------------------------------------------------------------------------- /config/crd/kustomization.yaml: -------------------------------------------------------------------------------- 1 | # This kustomization.yaml is not intended to be run by itself, 2 | # since it depends on service name and namespace that are out of this kustomize package. 3 | # It should be run by config/default 4 | resources: 5 | - bases/synapse.opdev.io_synapses.yaml 6 | - bases/synapse.opdev.io_mautrixsignals.yaml 7 | - bases/synapse.opdev.io_heisenbridges.yaml 8 | #+kubebuilder:scaffold:crdkustomizeresource 9 | 10 | patches: 11 | - path: patches/oneofhomeserver_in_synapses.yaml 12 | target: 13 | group: apiextensions.k8s.io 14 | version: v1 15 | kind: CustomResourceDefinition 16 | name: synapses.synapse.opdev.io 17 | 18 | patchesStrategicMerge: 19 | # [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix. 20 | # patches here are for enabling the conversion webhook for each CRD 21 | #- patches/webhook_in_synapses.yaml 22 | #- patches/webhook_in_mautrixsignals.yaml 23 | #- patches/webhook_in_heisenbridges.yaml 24 | #+kubebuilder:scaffold:crdkustomizewebhookpatch 25 | 26 | # [CERTMANAGER] To enable cert-manager, uncomment all the sections with [CERTMANAGER] prefix. 27 | # patches here are for enabling the CA injection for each CRD 28 | #- patches/cainjection_in_synapses.yaml 29 | #- patches/cainjection_in_mautrixsignals.yaml 30 | #- patches/cainjection_in_heisenbridges.yaml 31 | #+kubebuilder:scaffold:crdkustomizecainjectionpatch 32 | 33 | # the following config is for teaching kustomize how to do kustomization for CRDs. 34 | configurations: 35 | - kustomizeconfig.yaml 36 | -------------------------------------------------------------------------------- /config/crd/kustomizeconfig.yaml: -------------------------------------------------------------------------------- 1 | # This file is for teaching kustomize how to substitute name and namespace reference in CRD 2 | nameReference: 3 | - kind: Service 4 | version: v1 5 | fieldSpecs: 6 | - kind: CustomResourceDefinition 7 | version: v1 8 | group: apiextensions.k8s.io 9 | path: spec/conversion/webhook/clientConfig/service/name 10 | 11 | namespace: 12 | - kind: CustomResourceDefinition 13 | version: v1 14 | group: apiextensions.k8s.io 15 | path: spec/conversion/webhook/clientConfig/service/namespace 16 | create: false 17 | 18 | varReference: 19 | - path: metadata/annotations 20 | -------------------------------------------------------------------------------- /config/crd/patches/cainjection_in_synapse_heisenbridges.yaml: -------------------------------------------------------------------------------- 1 | # The following patch adds a directive for certmanager to inject CA into the CRD 2 | apiVersion: apiextensions.k8s.io/v1 3 | kind: CustomResourceDefinition 4 | metadata: 5 | annotations: 6 | cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME) 7 | name: heisenbridges.synapse.opdev.io 8 | -------------------------------------------------------------------------------- /config/crd/patches/cainjection_in_synapse_mautrixsignals.yaml: -------------------------------------------------------------------------------- 1 | # The following patch adds a directive for certmanager to inject CA into the CRD 2 | apiVersion: apiextensions.k8s.io/v1 3 | kind: CustomResourceDefinition 4 | metadata: 5 | annotations: 6 | cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME) 7 | name: mautrixsignals.synapse.opdev.io 8 | -------------------------------------------------------------------------------- /config/crd/patches/cainjection_in_synapses.yaml: -------------------------------------------------------------------------------- 1 | # The following patch adds a directive for certmanager to inject CA into the CRD 2 | apiVersion: apiextensions.k8s.io/v1 3 | kind: CustomResourceDefinition 4 | metadata: 5 | annotations: 6 | cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME) 7 | name: synapses.synapse.opdev.io 8 | -------------------------------------------------------------------------------- /config/crd/patches/oneofhomeserver_in_synapses.yaml: -------------------------------------------------------------------------------- 1 | - op: replace 2 | path: /spec/versions/0/schema/openAPIV3Schema/properties/spec/properties/homeserver/oneOf 3 | value: 4 | - required: [configMap] 5 | - required: [values] -------------------------------------------------------------------------------- /config/crd/patches/webhook_in_synapse_heisenbridges.yaml: -------------------------------------------------------------------------------- 1 | # The following patch enables a conversion webhook for the CRD 2 | apiVersion: apiextensions.k8s.io/v1 3 | kind: CustomResourceDefinition 4 | metadata: 5 | name: heisenbridges.synapse.opdev.io 6 | spec: 7 | conversion: 8 | strategy: Webhook 9 | webhook: 10 | clientConfig: 11 | service: 12 | namespace: system 13 | name: webhook-service 14 | path: /convert 15 | conversionReviewVersions: 16 | - v1 17 | -------------------------------------------------------------------------------- /config/crd/patches/webhook_in_synapse_mautrixsignals.yaml: -------------------------------------------------------------------------------- 1 | # The following patch enables a conversion webhook for the CRD 2 | apiVersion: apiextensions.k8s.io/v1 3 | kind: CustomResourceDefinition 4 | metadata: 5 | name: mautrixsignals.synapse.opdev.io 6 | spec: 7 | conversion: 8 | strategy: Webhook 9 | webhook: 10 | clientConfig: 11 | service: 12 | namespace: system 13 | name: webhook-service 14 | path: /convert 15 | conversionReviewVersions: 16 | - v1 17 | -------------------------------------------------------------------------------- /config/crd/patches/webhook_in_synapses.yaml: -------------------------------------------------------------------------------- 1 | # The following patch enables a conversion webhook for the CRD 2 | apiVersion: apiextensions.k8s.io/v1 3 | kind: CustomResourceDefinition 4 | metadata: 5 | name: synapses.synapse.opdev.io 6 | spec: 7 | conversion: 8 | strategy: Webhook 9 | webhook: 10 | clientConfig: 11 | service: 12 | namespace: system 13 | name: webhook-service 14 | path: /convert 15 | conversionReviewVersions: 16 | - v1 17 | -------------------------------------------------------------------------------- /config/default/kustomization.yaml: -------------------------------------------------------------------------------- 1 | # Adds namespace to all resources. 2 | namespace: synapse-operator-system 3 | 4 | # Value of this field is prepended to the 5 | # names of all resources, e.g. a deployment named 6 | # "wordpress" becomes "alices-wordpress". 7 | # Note that it should also match with the prefix (text before '-') of the namespace 8 | # field above. 9 | namePrefix: synapse-operator- 10 | 11 | # Labels to add to all resources and selectors. 12 | #commonLabels: 13 | # someName: someValue 14 | 15 | bases: 16 | - ../crd 17 | - ../rbac 18 | - ../manager 19 | # [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix including the one in 20 | # crd/kustomization.yaml 21 | #- ../webhook 22 | # [CERTMANAGER] To enable cert-manager, uncomment all sections with 'CERTMANAGER'. 'WEBHOOK' components are required. 23 | #- ../certmanager 24 | # [PROMETHEUS] To enable prometheus monitor, uncomment all sections with 'PROMETHEUS'. 25 | #- ../prometheus 26 | 27 | patchesStrategicMerge: 28 | # Protect the /metrics endpoint by putting it behind auth. 29 | # If you want your controller-manager to expose the /metrics 30 | # endpoint w/o any authn/z, please comment the following line. 31 | - manager_auth_proxy_patch.yaml 32 | 33 | # Mount the controller config file for loading manager configurations 34 | # through a ComponentConfig type 35 | #- manager_config_patch.yaml 36 | 37 | # [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix including the one in 38 | # crd/kustomization.yaml 39 | #- manager_webhook_patch.yaml 40 | 41 | # [CERTMANAGER] To enable cert-manager, uncomment all sections with 'CERTMANAGER'. 42 | # Uncomment 'CERTMANAGER' sections in crd/kustomization.yaml to enable the CA injection in the admission webhooks. 43 | # 'CERTMANAGER' needs to be enabled to use ca injection 44 | #- webhookcainjection_patch.yaml 45 | 46 | # the following config is for teaching kustomize how to do var substitution 47 | vars: 48 | # [CERTMANAGER] To enable cert-manager, uncomment all sections with 'CERTMANAGER' prefix. 49 | #- name: CERTIFICATE_NAMESPACE # namespace of the certificate CR 50 | # objref: 51 | # kind: Certificate 52 | # group: cert-manager.io 53 | # version: v1 54 | # name: serving-cert # this name should match the one in certificate.yaml 55 | # fieldref: 56 | # fieldpath: metadata.namespace 57 | #- name: CERTIFICATE_NAME 58 | # objref: 59 | # kind: Certificate 60 | # group: cert-manager.io 61 | # version: v1 62 | # name: serving-cert # this name should match the one in certificate.yaml 63 | #- name: SERVICE_NAMESPACE # namespace of the service 64 | # objref: 65 | # kind: Service 66 | # version: v1 67 | # name: webhook-service 68 | # fieldref: 69 | # fieldpath: metadata.namespace 70 | #- name: SERVICE_NAME 71 | # objref: 72 | # kind: Service 73 | # version: v1 74 | # name: webhook-service 75 | -------------------------------------------------------------------------------- /config/default/manager_auth_proxy_patch.yaml: -------------------------------------------------------------------------------- 1 | # This patch inject a sidecar container which is a HTTP proxy for the 2 | # controller manager, it performs RBAC authorization against the Kubernetes API using SubjectAccessReviews. 3 | apiVersion: apps/v1 4 | kind: Deployment 5 | metadata: 6 | name: controller-manager 7 | namespace: system 8 | spec: 9 | template: 10 | spec: 11 | containers: 12 | - name: kube-rbac-proxy 13 | image: gcr.io/kubebuilder/kube-rbac-proxy:v0.8.0 14 | args: 15 | - "--secure-listen-address=0.0.0.0:8443" 16 | - "--upstream=http://127.0.0.1:8080/" 17 | - "--logtostderr=true" 18 | - "--v=10" 19 | ports: 20 | - containerPort: 8443 21 | protocol: TCP 22 | name: https 23 | - name: manager 24 | args: 25 | - "--health-probe-bind-address=:8081" 26 | - "--metrics-bind-address=127.0.0.1:8080" 27 | - "--leader-elect" 28 | -------------------------------------------------------------------------------- /config/default/manager_config_patch.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: controller-manager 5 | namespace: system 6 | spec: 7 | template: 8 | spec: 9 | containers: 10 | - name: manager 11 | args: 12 | - "--config=controller_manager_config.yaml" 13 | volumeMounts: 14 | - name: manager-config 15 | mountPath: /controller_manager_config.yaml 16 | subPath: controller_manager_config.yaml 17 | volumes: 18 | - name: manager-config 19 | configMap: 20 | name: manager-config 21 | -------------------------------------------------------------------------------- /config/manager/controller_manager_config.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: controller-runtime.sigs.k8s.io/v1alpha1 2 | kind: ControllerManagerConfig 3 | health: 4 | healthProbeBindAddress: :8081 5 | metrics: 6 | bindAddress: 127.0.0.1:8080 7 | webhook: 8 | port: 9443 9 | leaderElection: 10 | leaderElect: true 11 | resourceName: 8d311e9b.opdev.io 12 | -------------------------------------------------------------------------------- /config/manager/kustomization.yaml: -------------------------------------------------------------------------------- 1 | resources: 2 | - manager.yaml 3 | 4 | generatorOptions: 5 | disableNameSuffixHash: true 6 | 7 | configMapGenerator: 8 | - files: 9 | - controller_manager_config.yaml 10 | name: manager-config 11 | apiVersion: kustomize.config.k8s.io/v1beta1 12 | kind: Kustomization 13 | images: 14 | - name: controller 15 | newName: quay.io/opdev/synapse-operator 16 | newTag: v0.6.1 17 | -------------------------------------------------------------------------------- /config/manager/manager.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Namespace 3 | metadata: 4 | labels: 5 | control-plane: controller-manager 6 | name: system 7 | --- 8 | apiVersion: apps/v1 9 | kind: Deployment 10 | metadata: 11 | name: controller-manager 12 | namespace: system 13 | labels: 14 | control-plane: controller-manager 15 | spec: 16 | selector: 17 | matchLabels: 18 | control-plane: controller-manager 19 | replicas: 1 20 | template: 21 | metadata: 22 | labels: 23 | control-plane: controller-manager 24 | spec: 25 | securityContext: 26 | runAsNonRoot: true 27 | containers: 28 | - command: 29 | - /manager 30 | args: 31 | - --leader-elect 32 | image: controller:latest 33 | name: manager 34 | securityContext: 35 | allowPrivilegeEscalation: false 36 | livenessProbe: 37 | httpGet: 38 | path: /healthz 39 | port: 8081 40 | initialDelaySeconds: 15 41 | periodSeconds: 20 42 | readinessProbe: 43 | httpGet: 44 | path: /readyz 45 | port: 8081 46 | initialDelaySeconds: 5 47 | periodSeconds: 10 48 | resources: 49 | limits: 50 | cpu: 200m 51 | memory: 100Mi 52 | requests: 53 | cpu: 100m 54 | memory: 20Mi 55 | serviceAccountName: controller-manager 56 | terminationGracePeriodSeconds: 10 57 | -------------------------------------------------------------------------------- /config/manifests/bases/synapse-operator.clusterserviceversion.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: operators.coreos.com/v1alpha1 2 | kind: ClusterServiceVersion 3 | metadata: 4 | annotations: 5 | alm-examples: '[]' 6 | capabilities: Basic Install 7 | categories: Developer Tools 8 | containerImage: quay.io/opdev/synapse-operator 9 | description: Deploys and manages the lifecycle of Synapse servers and their associated 10 | components (bridges, databases, ...). Synapse is the reference Matrix homeserver 11 | implementation. 12 | repository: https://github.com/opdev/synapse-operator 13 | name: synapse-operator.v0.0.0 14 | namespace: placeholder 15 | spec: 16 | apiservicedefinitions: {} 17 | customresourcedefinitions: 18 | owned: 19 | - description: Heisenbridge is the Schema for the heisenbridges API 20 | displayName: Heisenbridge 21 | kind: Heisenbridge 22 | name: heisenbridges.synapse.opdev.io 23 | version: v1alpha1 24 | - description: MautrixSignal is the Schema for the mautrixsignals API 25 | displayName: Mautrix Signal 26 | kind: MautrixSignal 27 | name: mautrixsignals.synapse.opdev.io 28 | version: v1alpha1 29 | - description: Synapse is the Schema for the synapses API 30 | displayName: Synapse 31 | kind: Synapse 32 | name: synapses.synapse.opdev.io 33 | version: v1alpha1 34 | description: Deploys and manages the lifecycle of Synapse servers and their associated 35 | components (bridges, databases, ...). Synapse is the reference Matrix homeserver 36 | implementation. 37 | displayName: Synapse Operator 38 | icon: 39 | - base64data: iVBORw0KGgoAAAANSUhEUgAAAeAAAADMCAMAAACcE4BeAAAAM1BMVEUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACjBUbJAAAAEHRSTlMAECAwQFBgcICPn6+/z9/vIxqCigAACNVJREFUeNrt3duCoyoQBVAQRNTy8v9fex7O9HQuFFARiDp7P84kaWUpIKCq/T1eIZeMD2AqAAMYATBybuCVHuNQVNeMe2JcH4Bxzt72fAYwgBEAIwBGAIwAGAEwAmAAIwBGAIwAGAEwAmAEwABGAIwAGAEwAmAEwAiAAQxgACMARgCMABgBMAJgBMAARgCMABjpfCAdgG8TG3hgxm4BDGAAAxjAAAYwgG99CVG/hwvgb6ZB4QMYwAAGMIABDGAAAxjAAAYwgAEMYAADGMDXANY2EA3g2wB/PQAGMIABDGAAAxjAAAYwgAEM4NrA1vmRiGj23napYQDviYho8r43n/05Ywfvp/9f1Dd77501XwPurB+JaPa9vilwN9BzeW2z4/bVjOtL4S5jLzuU/LyEkPaNRte1Bn7aH7oo8PObLYmIaHwo8DlY3HOo0BxDM5ncQyn8xx4Pl8BPmcdND37ree/+fnGM7vnb/lDo7/3ExMrz7Zcf04c/3xUDDrybln7Lji1reiW2y57/4dCOpnT/aL29I9fusvz9IkX2XKl+Zf4vMdmg1/CfZWoyvQU/PaoWwD5aUM/bMMZLdUy0YG7NBlpdA+CO2P9LzSYx27OFSyB8WC+qAbBZUifT7yYnP7uvsXrariKixdQGdtv+MTB3rM/Beiv8WdMA2G7pgv4RzvjsvrG9LT3u0gx1gYMblA2slvRGxyvoQdUHdjll9WefXV7JMg1x+uwPZKoJPO3HgA1ziHeZFTSp+sCZZl7gu2/Bisds+35IuDjwtB8EVsOeB9dLWuuiwCa3tLp836dG+6jvw66VBvb7YWBFWZU0U0H3qjpwl13qgmMhuDkf+/6WaWHgfi8AzBTgSyU9p1qfasCCVrEXdYBfWyG9fuy7r7oGsN5KAHOHCaU/s+r6wPUyZXUyZPVBWeB5LwLM/c6QrKCtujLwyyncH/qttQKw3QsB62QlPWe2YtcCft4groLeJmd+JqfcxFaapjwwlQLmNmuJH9yLujjw+jQ+mTcWqYYtdrR8PNkQ+PC2FwPmBrR87AzfutbAG/ne2sFTlt7ke2t7P0W6TiZ5Ak/vvYxuyR0QkLRqJDs2pcB6iZXA/MEQVnlg+r0k0y51QfM4ZWQpY5DR5F8lMMf7qYG5C8iFr6Bn1RT4ZfRYR7u820u9yh0Pc6IOYxqhPt6cnhKYK1rPXSdvuinw0qn8i5r3YUhmCGNNFDCnsX4DmLyztlPGWufnUQys2EqaPrxCKgkcGFeMDEsEhpldohOrROPsY864SVFgYlYmCYCZAa0lPFY9qpbAwf6cyx+DZAvQRJsotpfR5xVrMeCFPZ0kt48OgiZg0U2Bh/yK8uXiJ9Fu2mhBmYPFWgo40p0V3R8sGKrLXjlaBHgVVJT77gTtpo3+YXUK4C3WGoqAdfZkyqCaAjtJZ5br/U3RyTAvaYKbAm/Rk0l2h3/uaKxgUW4J4I1b2Sq6fBs+HGxVuc1/JeD4hKzwEQ5565G2ri0wOycpql1sQWBqB5zozQqB82ZEJbcHlAB23KcXyd4VBHZ7M+DUhKz0ISw5kyGTagxsRCeSkpzvHwEzlxtVgJ0qC5wxnbPq1sDqVMA9V5nWAF5VaWCVXCAju3uqOTDVBNb9uAoK5jiwLw+cWngmLJICwOsZgDvr/LwKj/zjwF154MSA1qKaA9M3gY31nvJmeioAp0v7kyfdUaErpIsDd33qlK0PPFYBjg1oDerswGMJYDPM8iW0FYBdFeDIgNasTg/sDwPb6bP10RWAbR1gdtZh0/cHdh+vfq8ArCoBcwNao7o7sDtwc0N54K0WsCnVxboYcEf7fiZgqgW8FJhGuiDwgXvPrgXsiw1zXAo4/85TcpcGNkWWclwOOPfO02U0rSb86wDHpwwXfVPgnOUsK/m+ExTrOYFTjyC6KTAlZN3jYxOvDGzlfYk7APeRKvn9wZAXBk6v6Wg+H9wEmNntbewOFOsZgad0SzTfEJg5gWd9qFhPCJy1rrK/H/AsWy9zWeC8ldGNV1W2ABZOnV0WOPPeBrobsBXu5VWBs8dyhpsBD8KWzF8TOP+JY03vTWoALFsKdllgwVzKci9gaenSJYHDq+227dCsw3WBY1u7XRGYmSzrs9fx/zvA/YHpQv814IUb1aAjA1q3BKYDwNO3gD27Covpe03/bBtsj0z4r18CNpFBq+HAgNZ1gfkFUcsRYK7YKgPrJTbsTOzZfRPgSTJQOR5assPdsV8ZeIwSMpU03QbYC3oZ7tiarH3fp/7PWG9nh0bANlEJDx8PaF0DWPAsv2k/DPyYNsDMJPCc6jdu5ibAHTOc8zarEnujms27XP4G8JhsY7mnpN1lNolb5vD0pkPtSLbUhU4C3Gf0kplK2t8EmF+IttHovfeeexFpDHg6BzAzCTxnHY3mHsDdfjz2k9m5FsBz1kUQU0mv+hbAio4Dv3c5u1MA51TQkUp6vAewOQ7sP5mfqw/cZVXQka3tbwGsxsPAY/agZlPg/FEq7gVa+hbAWvRWyiVzO+nrwJJxZuaz8y2ARfcWTjZzGiG5irE2cH4FHTkeh1sAC4Sd0qF/1vmz7M2AF1Gtm/eWw6sCK5N3e/9qmEGqXsmFKwN7Yb8p91W01wROvMrl5yuau7YMX1DEf7QusBE3qiQd0LrYMzqSry+dOv5Q5+byLX0LWFZBRyppfkDrVk/ZWQcdPTfYW126gb4BPH5wYctU0ux94ULgzr6HHw01gU/z/QGb+WkTfODoNg9d6tfiD993fiL6+9NERN730X3JWH2uQ9uhI9tobWK5JPOlQsAnSdc/PKByo9k7o5AbASMARgAMYATACIARACMARgCMABjACIARACMARgCMABgBMIABDGAEwAiAEQAjAEYAjAAYwAiAEQAjAEYAjAAYATCAAQxgBMAIgBEAIwBGAIwAGMAIgBEAIwBGAIwAGAEwgBEAIwBGAIwAGAEwAmAAAxjACIARACPfA17pMQ6Fc824J8b1ATj3NaHIBc7Zl5eoAhjACICRswL/B97uAw3HlRdgAAAAAElFTkSuQmCC 40 | mediatype: image/png 41 | install: 42 | spec: 43 | deployments: null 44 | strategy: "" 45 | installModes: 46 | - supported: false 47 | type: OwnNamespace 48 | - supported: false 49 | type: SingleNamespace 50 | - supported: false 51 | type: MultiNamespace 52 | - supported: true 53 | type: AllNamespaces 54 | keywords: 55 | - synapse 56 | - matrix 57 | links: 58 | - name: Synapse Operator repository 59 | url: https://github.com/opdev/synapse-operator 60 | - name: Matrix project 61 | url: https://matrix.org 62 | - name: Synapse repository 63 | url: https://github.com/matrix-org/synapse 64 | - name: Heisenbridge repository 65 | url: https://github.com/hifi/heisenbridge 66 | - name: mautrix-signal repository 67 | url: https://github.com/mautrix/signal 68 | maintainers: 69 | - email: mgoerens@redhat.com 70 | name: Matthias Goerens 71 | maturity: alpha 72 | minKubeVersion: 1.24.0 73 | provider: 74 | name: Community 75 | version: 0.0.0 76 | -------------------------------------------------------------------------------- /config/manifests/kustomization.yaml: -------------------------------------------------------------------------------- 1 | # These resources constitute the fully configured set of manifests 2 | # used to generate the 'manifests/' directory in a bundle. 3 | resources: 4 | - bases/synapse-operator.clusterserviceversion.yaml 5 | - ../default 6 | - ../samples 7 | - ../scorecard 8 | 9 | # [WEBHOOK] To enable webhooks, uncomment all the sections with [WEBHOOK] prefix. 10 | # Do NOT uncomment sections with prefix [CERTMANAGER], as OLM does not support cert-manager. 11 | # These patches remove the unnecessary "cert" volume and its manager container volumeMount. 12 | #patchesJson6902: 13 | #- target: 14 | # group: apps 15 | # version: v1 16 | # kind: Deployment 17 | # name: controller-manager 18 | # namespace: system 19 | # patch: |- 20 | # # Remove the manager container's "cert" volumeMount, since OLM will create and mount a set of certs. 21 | # # Update the indices in this path if adding or removing containers/volumeMounts in the manager's Deployment. 22 | # - op: remove 23 | # path: /spec/template/spec/containers/1/volumeMounts/0 24 | # # Remove the "cert" volume, since OLM will create and mount a set of certs. 25 | # # Update the indices in this path if adding or removing volumes in the manager's Deployment. 26 | # - op: remove 27 | # path: /spec/template/spec/volumes/0 28 | -------------------------------------------------------------------------------- /config/prometheus/kustomization.yaml: -------------------------------------------------------------------------------- 1 | resources: 2 | - monitor.yaml 3 | -------------------------------------------------------------------------------- /config/prometheus/monitor.yaml: -------------------------------------------------------------------------------- 1 | 2 | # Prometheus Monitor Service (Metrics) 3 | apiVersion: monitoring.coreos.com/v1 4 | kind: ServiceMonitor 5 | metadata: 6 | labels: 7 | control-plane: controller-manager 8 | name: controller-manager-metrics-monitor 9 | namespace: system 10 | spec: 11 | endpoints: 12 | - path: /metrics 13 | port: https 14 | scheme: https 15 | bearerTokenFile: /var/run/secrets/kubernetes.io/serviceaccount/token 16 | tlsConfig: 17 | insecureSkipVerify: true 18 | selector: 19 | matchLabels: 20 | control-plane: controller-manager 21 | -------------------------------------------------------------------------------- /config/rbac/auth_proxy_client_clusterrole.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: ClusterRole 3 | metadata: 4 | name: metrics-reader 5 | rules: 6 | - nonResourceURLs: 7 | - "/metrics" 8 | verbs: 9 | - get 10 | -------------------------------------------------------------------------------- /config/rbac/auth_proxy_role.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: ClusterRole 3 | metadata: 4 | name: proxy-role 5 | rules: 6 | - apiGroups: 7 | - authentication.k8s.io 8 | resources: 9 | - tokenreviews 10 | verbs: 11 | - create 12 | - apiGroups: 13 | - authorization.k8s.io 14 | resources: 15 | - subjectaccessreviews 16 | verbs: 17 | - create 18 | -------------------------------------------------------------------------------- /config/rbac/auth_proxy_role_binding.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: ClusterRoleBinding 3 | metadata: 4 | name: proxy-rolebinding 5 | roleRef: 6 | apiGroup: rbac.authorization.k8s.io 7 | kind: ClusterRole 8 | name: proxy-role 9 | subjects: 10 | - kind: ServiceAccount 11 | name: controller-manager 12 | namespace: system 13 | -------------------------------------------------------------------------------- /config/rbac/auth_proxy_service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | labels: 5 | control-plane: controller-manager 6 | name: controller-manager-metrics-service 7 | namespace: system 8 | spec: 9 | ports: 10 | - name: https 11 | port: 8443 12 | protocol: TCP 13 | targetPort: https 14 | selector: 15 | control-plane: controller-manager 16 | -------------------------------------------------------------------------------- /config/rbac/kustomization.yaml: -------------------------------------------------------------------------------- 1 | resources: 2 | # All RBAC will be applied under this service account in 3 | # the deployment namespace. You may comment out this resource 4 | # if your manager will use a service account that exists at 5 | # runtime. Be sure to update RoleBinding and ClusterRoleBinding 6 | # subjects if changing service account names. 7 | - service_account.yaml 8 | - role.yaml 9 | - role_binding.yaml 10 | - leader_election_role.yaml 11 | - leader_election_role_binding.yaml 12 | # Comment the following 4 lines if you want to disable 13 | # the auth proxy (https://github.com/brancz/kube-rbac-proxy) 14 | # which protects your /metrics endpoint. 15 | - auth_proxy_service.yaml 16 | - auth_proxy_role.yaml 17 | - auth_proxy_role_binding.yaml 18 | - auth_proxy_client_clusterrole.yaml 19 | -------------------------------------------------------------------------------- /config/rbac/leader_election_role.yaml: -------------------------------------------------------------------------------- 1 | # permissions to do leader election. 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: Role 4 | metadata: 5 | name: leader-election-role 6 | rules: 7 | - apiGroups: 8 | - "" 9 | resources: 10 | - configmaps 11 | verbs: 12 | - get 13 | - list 14 | - watch 15 | - create 16 | - update 17 | - patch 18 | - delete 19 | - apiGroups: 20 | - coordination.k8s.io 21 | resources: 22 | - leases 23 | verbs: 24 | - get 25 | - list 26 | - watch 27 | - create 28 | - update 29 | - patch 30 | - delete 31 | - apiGroups: 32 | - "" 33 | resources: 34 | - events 35 | verbs: 36 | - create 37 | - patch 38 | -------------------------------------------------------------------------------- /config/rbac/leader_election_role_binding.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: RoleBinding 3 | metadata: 4 | name: leader-election-rolebinding 5 | roleRef: 6 | apiGroup: rbac.authorization.k8s.io 7 | kind: Role 8 | name: leader-election-role 9 | subjects: 10 | - kind: ServiceAccount 11 | name: controller-manager 12 | namespace: system 13 | -------------------------------------------------------------------------------- /config/rbac/role.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRole 4 | metadata: 5 | name: manager-role 6 | rules: 7 | - apiGroups: 8 | - apps 9 | resources: 10 | - deployments 11 | verbs: 12 | - create 13 | - delete 14 | - get 15 | - list 16 | - patch 17 | - update 18 | - watch 19 | - apiGroups: 20 | - "" 21 | resources: 22 | - configmaps 23 | - persistentvolumeclaims 24 | - serviceaccounts 25 | - services 26 | verbs: 27 | - create 28 | - delete 29 | - get 30 | - list 31 | - patch 32 | - update 33 | - watch 34 | - apiGroups: 35 | - postgres-operator.crunchydata.com 36 | resources: 37 | - postgresclusters 38 | verbs: 39 | - create 40 | - delete 41 | - get 42 | - list 43 | - patch 44 | - update 45 | - watch 46 | - apiGroups: 47 | - rbac.authorization.k8s.io 48 | resources: 49 | - rolebindings 50 | verbs: 51 | - create 52 | - delete 53 | - get 54 | - list 55 | - patch 56 | - update 57 | - watch 58 | - apiGroups: 59 | - security.openshift.io 60 | resourceNames: 61 | - anyuid 62 | resources: 63 | - securitycontextconstraints 64 | verbs: 65 | - use 66 | - apiGroups: 67 | - synapse.opdev.io 68 | resources: 69 | - heisenbridges 70 | - mautrixsignals 71 | - synapses 72 | verbs: 73 | - create 74 | - delete 75 | - get 76 | - list 77 | - patch 78 | - update 79 | - watch 80 | - apiGroups: 81 | - synapse.opdev.io 82 | resources: 83 | - heisenbridges/finalizers 84 | - mautrixsignals/finalizers 85 | - synapses/finalizers 86 | verbs: 87 | - update 88 | - apiGroups: 89 | - synapse.opdev.io 90 | resources: 91 | - heisenbridges/status 92 | - mautrixsignals/status 93 | - synapses/status 94 | verbs: 95 | - get 96 | - patch 97 | - update 98 | -------------------------------------------------------------------------------- /config/rbac/role_binding.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: ClusterRoleBinding 3 | metadata: 4 | name: manager-rolebinding 5 | roleRef: 6 | apiGroup: rbac.authorization.k8s.io 7 | kind: ClusterRole 8 | name: manager-role 9 | subjects: 10 | - kind: ServiceAccount 11 | name: controller-manager 12 | namespace: system 13 | -------------------------------------------------------------------------------- /config/rbac/service_account.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ServiceAccount 3 | metadata: 4 | name: controller-manager 5 | namespace: system 6 | -------------------------------------------------------------------------------- /config/rbac/synapse_editor_role.yaml: -------------------------------------------------------------------------------- 1 | # permissions for end users to edit synapses. 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRole 4 | metadata: 5 | name: synapse-editor-role 6 | rules: 7 | - apiGroups: 8 | - synapse.opdev.io 9 | resources: 10 | - synapses 11 | verbs: 12 | - create 13 | - delete 14 | - get 15 | - list 16 | - patch 17 | - update 18 | - watch 19 | - apiGroups: 20 | - synapse.opdev.io 21 | resources: 22 | - synapses/status 23 | verbs: 24 | - get 25 | -------------------------------------------------------------------------------- /config/rbac/synapse_heisenbridge_editor_role.yaml: -------------------------------------------------------------------------------- 1 | # permissions for end users to edit heisenbridges. 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRole 4 | metadata: 5 | labels: 6 | app.kubernetes.io/name: clusterrole 7 | app.kubernetes.io/instance: heisenbridge-editor-role 8 | app.kubernetes.io/component: rbac 9 | app.kubernetes.io/created-by: synapse-operator 10 | app.kubernetes.io/part-of: synapse-operator 11 | app.kubernetes.io/managed-by: kustomize 12 | name: heisenbridge-editor-role 13 | rules: 14 | - apiGroups: 15 | - synapse.opdev.io 16 | resources: 17 | - heisenbridges 18 | verbs: 19 | - create 20 | - delete 21 | - get 22 | - list 23 | - patch 24 | - update 25 | - watch 26 | - apiGroups: 27 | - synapse.opdev.io 28 | resources: 29 | - heisenbridges/status 30 | verbs: 31 | - get 32 | -------------------------------------------------------------------------------- /config/rbac/synapse_heisenbridge_viewer_role.yaml: -------------------------------------------------------------------------------- 1 | # permissions for end users to view heisenbridges. 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRole 4 | metadata: 5 | labels: 6 | app.kubernetes.io/name: clusterrole 7 | app.kubernetes.io/instance: heisenbridge-viewer-role 8 | app.kubernetes.io/component: rbac 9 | app.kubernetes.io/created-by: synapse-operator 10 | app.kubernetes.io/part-of: synapse-operator 11 | app.kubernetes.io/managed-by: kustomize 12 | name: heisenbridge-viewer-role 13 | rules: 14 | - apiGroups: 15 | - synapse.opdev.io 16 | resources: 17 | - heisenbridges 18 | verbs: 19 | - get 20 | - list 21 | - watch 22 | - apiGroups: 23 | - synapse.opdev.io 24 | resources: 25 | - heisenbridges/status 26 | verbs: 27 | - get 28 | -------------------------------------------------------------------------------- /config/rbac/synapse_mautrixsignal_editor_role.yaml: -------------------------------------------------------------------------------- 1 | # permissions for end users to edit mautrixsignals. 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRole 4 | metadata: 5 | labels: 6 | app.kubernetes.io/name: clusterrole 7 | app.kubernetes.io/instance: mautrixsignal-editor-role 8 | app.kubernetes.io/component: rbac 9 | app.kubernetes.io/created-by: synapse-operator 10 | app.kubernetes.io/part-of: synapse-operator 11 | app.kubernetes.io/managed-by: kustomize 12 | name: mautrixsignal-editor-role 13 | rules: 14 | - apiGroups: 15 | - synapse.opdev.io 16 | resources: 17 | - mautrixsignals 18 | verbs: 19 | - create 20 | - delete 21 | - get 22 | - list 23 | - patch 24 | - update 25 | - watch 26 | - apiGroups: 27 | - synapse.opdev.io 28 | resources: 29 | - mautrixsignals/status 30 | verbs: 31 | - get 32 | -------------------------------------------------------------------------------- /config/rbac/synapse_mautrixsignal_viewer_role.yaml: -------------------------------------------------------------------------------- 1 | # permissions for end users to view mautrixsignals. 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRole 4 | metadata: 5 | labels: 6 | app.kubernetes.io/name: clusterrole 7 | app.kubernetes.io/instance: mautrixsignal-viewer-role 8 | app.kubernetes.io/component: rbac 9 | app.kubernetes.io/created-by: synapse-operator 10 | app.kubernetes.io/part-of: synapse-operator 11 | app.kubernetes.io/managed-by: kustomize 12 | name: mautrixsignal-viewer-role 13 | rules: 14 | - apiGroups: 15 | - synapse.opdev.io 16 | resources: 17 | - mautrixsignals 18 | verbs: 19 | - get 20 | - list 21 | - watch 22 | - apiGroups: 23 | - synapse.opdev.io 24 | resources: 25 | - mautrixsignals/status 26 | verbs: 27 | - get 28 | -------------------------------------------------------------------------------- /config/rbac/synapse_viewer_role.yaml: -------------------------------------------------------------------------------- 1 | # permissions for end users to view synapses. 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRole 4 | metadata: 5 | name: synapse-viewer-role 6 | rules: 7 | - apiGroups: 8 | - synapse.opdev.io 9 | resources: 10 | - synapses 11 | verbs: 12 | - get 13 | - list 14 | - watch 15 | - apiGroups: 16 | - synapse.opdev.io 17 | resources: 18 | - synapses/status 19 | verbs: 20 | - get 21 | -------------------------------------------------------------------------------- /config/samples/kustomization.yaml: -------------------------------------------------------------------------------- 1 | ## Append samples you want in your CSV to this file as resources ## 2 | resources: 3 | - synapse_v1alpha1_synapse.yaml 4 | - synapse_v1alpha1_mautrixsignal.yaml 5 | - synapse_v1alpha1_heisenbridge.yaml 6 | #+kubebuilder:scaffold:manifestskustomizesamples 7 | -------------------------------------------------------------------------------- /config/samples/synapse_v1alpha1_heisenbridge.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: synapse.opdev.io/v1alpha1 2 | kind: Heisenbridge 3 | metadata: 4 | labels: 5 | app.kubernetes.io/name: heisenbridge 6 | app.kubernetes.io/instance: heisenbridge-sample 7 | app.kubernetes.io/part-of: synapse-operator 8 | app.kubernetes.io/managed-by: kustomize 9 | app.kubernetes.io/created-by: synapse-operator 10 | name: heisenbridge-sample 11 | spec: 12 | synapse: 13 | name: synapse-sample 14 | -------------------------------------------------------------------------------- /config/samples/synapse_v1alpha1_mautrixsignal.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: synapse.opdev.io/v1alpha1 2 | kind: MautrixSignal 3 | metadata: 4 | labels: 5 | app.kubernetes.io/name: mautrixsignal 6 | app.kubernetes.io/instance: mautrixsignal-sample 7 | app.kubernetes.io/part-of: synapse-operator 8 | app.kubernetes.io/managed-by: kustomize 9 | app.kubernetes.io/created-by: synapse-operator 10 | name: mautrixsignal-sample 11 | spec: 12 | synapse: 13 | name: synapse-sample 14 | -------------------------------------------------------------------------------- /config/samples/synapse_v1alpha1_synapse.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: synapse.opdev.io/v1alpha1 2 | kind: Synapse 3 | metadata: 4 | name: synapse-sample 5 | spec: 6 | createNewPostgreSQL: false 7 | homeserver: 8 | values: 9 | serverName: my.matrix.host 10 | reportStats: true 11 | -------------------------------------------------------------------------------- /config/scorecard/bases/config.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: scorecard.operatorframework.io/v1alpha3 2 | kind: Configuration 3 | metadata: 4 | name: config 5 | stages: 6 | - parallel: true 7 | tests: [] 8 | -------------------------------------------------------------------------------- /config/scorecard/kustomization.yaml: -------------------------------------------------------------------------------- 1 | resources: 2 | - bases/config.yaml 3 | patchesJson6902: 4 | - path: patches/basic.config.yaml 5 | target: 6 | group: scorecard.operatorframework.io 7 | version: v1alpha3 8 | kind: Configuration 9 | name: config 10 | - path: patches/olm.config.yaml 11 | target: 12 | group: scorecard.operatorframework.io 13 | version: v1alpha3 14 | kind: Configuration 15 | name: config 16 | #+kubebuilder:scaffold:patchesJson6902 17 | -------------------------------------------------------------------------------- /config/scorecard/patches/basic.config.yaml: -------------------------------------------------------------------------------- 1 | - op: add 2 | path: /stages/0/tests/- 3 | value: 4 | entrypoint: 5 | - scorecard-test 6 | - basic-check-spec 7 | image: quay.io/operator-framework/scorecard-test:v1.12.0 8 | labels: 9 | suite: basic 10 | test: basic-check-spec-test 11 | -------------------------------------------------------------------------------- /config/scorecard/patches/olm.config.yaml: -------------------------------------------------------------------------------- 1 | - op: add 2 | path: /stages/0/tests/- 3 | value: 4 | entrypoint: 5 | - scorecard-test 6 | - olm-bundle-validation 7 | image: quay.io/operator-framework/scorecard-test:v1.12.0 8 | labels: 9 | suite: olm 10 | test: olm-bundle-validation-test 11 | - op: add 12 | path: /stages/0/tests/- 13 | value: 14 | entrypoint: 15 | - scorecard-test 16 | - olm-crds-have-validation 17 | image: quay.io/operator-framework/scorecard-test:v1.12.0 18 | labels: 19 | suite: olm 20 | test: olm-crds-have-validation-test 21 | - op: add 22 | path: /stages/0/tests/- 23 | value: 24 | entrypoint: 25 | - scorecard-test 26 | - olm-crds-have-resources 27 | image: quay.io/operator-framework/scorecard-test:v1.12.0 28 | labels: 29 | suite: olm 30 | test: olm-crds-have-resources-test 31 | - op: add 32 | path: /stages/0/tests/- 33 | value: 34 | entrypoint: 35 | - scorecard-test 36 | - olm-spec-descriptors 37 | image: quay.io/operator-framework/scorecard-test:v1.12.0 38 | labels: 39 | suite: olm 40 | test: olm-spec-descriptors-test 41 | - op: add 42 | path: /stages/0/tests/- 43 | value: 44 | entrypoint: 45 | - scorecard-test 46 | - olm-status-descriptors 47 | image: quay.io/operator-framework/scorecard-test:v1.12.0 48 | labels: 49 | suite: olm 50 | test: olm-status-descriptors-test 51 | -------------------------------------------------------------------------------- /controllers/synapse/heisenbridge/heisenbridge_configmap.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package heisenbridge 18 | 19 | import ( 20 | "context" 21 | "fmt" 22 | 23 | corev1 "k8s.io/api/core/v1" 24 | "k8s.io/apimachinery/pkg/types" 25 | ctrl "sigs.k8s.io/controller-runtime" 26 | "sigs.k8s.io/controller-runtime/pkg/client" 27 | 28 | "github.com/opdev/subreconciler" 29 | synapsev1alpha1 "github.com/opdev/synapse-operator/apis/synapse/v1alpha1" 30 | "github.com/opdev/synapse-operator/helpers/reconcile" 31 | "github.com/opdev/synapse-operator/helpers/utils" 32 | "github.com/opdev/synapse-operator/internal/templates" 33 | ) 34 | 35 | // reconcileHeisenbridgeConfigMap is a function of type FnWithRequest, to 36 | // be called in the main reconciliation loop. 37 | // 38 | // It reconciles the heisenbridge ConfigMap to its desired state. It is called 39 | // only if the user hasn't provided its own ConfigMap for heisenbridge 40 | func (r *HeisenbridgeReconciler) reconcileHeisenbridgeConfigMap(ctx context.Context, req ctrl.Request) (*ctrl.Result, error) { 41 | h := &synapsev1alpha1.Heisenbridge{} 42 | if r, err := utils.GetResource(ctx, r.Client, req, h); subreconciler.ShouldHaltOrRequeue(r, err) { 43 | return r, err 44 | } 45 | 46 | desiredConfigMap, err := r.configMapForHeisenbridge(h) 47 | if err != nil { 48 | return subreconciler.RequeueWithError(err) 49 | } 50 | 51 | if err := reconcile.ReconcileResource( 52 | ctx, 53 | r.Client, 54 | desiredConfigMap, 55 | &corev1.ConfigMap{}, 56 | ); err != nil { 57 | return subreconciler.RequeueWithError(err) 58 | } 59 | 60 | return subreconciler.ContinueReconciling() 61 | } 62 | 63 | // configMapForSynapse returns a synapse ConfigMap object 64 | func (r *HeisenbridgeReconciler) configMapForHeisenbridge(h *synapsev1alpha1.Heisenbridge) (*corev1.ConfigMap, error) { 65 | type configmapExtraValues struct { 66 | synapsev1alpha1.Heisenbridge 67 | HeisenbridgeFQDN string 68 | } 69 | 70 | extraValues := configmapExtraValues{ 71 | Heisenbridge: *h, 72 | HeisenbridgeFQDN: utils.ComputeFQDN(h.Name, h.Namespace), 73 | } 74 | 75 | cm, err := templates.ResourceFromTemplate[configmapExtraValues, corev1.ConfigMap](&extraValues, "heisenbridge_configmap") 76 | if err != nil { 77 | return nil, fmt.Errorf("could not get template: %v", err) 78 | } 79 | 80 | // Set Heisenbridge instance as the owner and controller 81 | if err := ctrl.SetControllerReference(h, cm, r.Scheme); err != nil { 82 | return &corev1.ConfigMap{}, err 83 | } 84 | 85 | return cm, nil 86 | } 87 | 88 | // configureHeisenbridgeConfigMap is a function of type FnWithRequest, to 89 | // be called in the main reconciliation loop. 90 | // 91 | // Following the previous copy of the user-provided ConfigMap, it edits the 92 | // content of the copy to ensure that heisenbridge is correctly configured. 93 | func (r *HeisenbridgeReconciler) configureHeisenbridgeConfigMap(ctx context.Context, req ctrl.Request) (*ctrl.Result, error) { 94 | h := &synapsev1alpha1.Heisenbridge{} 95 | if r, err := utils.GetResource(ctx, r.Client, req, h); subreconciler.ShouldHaltOrRequeue(r, err) { 96 | return r, err 97 | } 98 | 99 | keyForConfigMap := types.NamespacedName{ 100 | Name: h.Name, 101 | Namespace: h.Namespace, 102 | } 103 | 104 | // Configure correct URL in Heisenbridge ConfigMap 105 | if err := utils.UpdateConfigMap( 106 | ctx, 107 | r.Client, 108 | keyForConfigMap, 109 | h, 110 | r.updateHeisenbridgeWithURL, 111 | "heisenbridge.yaml", 112 | ); err != nil { 113 | return subreconciler.RequeueWithError(err) 114 | } 115 | 116 | return subreconciler.ContinueReconciling() 117 | } 118 | 119 | // updateHeisenbridgeWithURL is a function of type updateDataFunc function to 120 | // be passed as an argument in a call to updateConfigMap. 121 | // 122 | // It configures the correct Heisenbridge URL, needed for Synapse to reach the 123 | // bridge. 124 | func (r *HeisenbridgeReconciler) updateHeisenbridgeWithURL( 125 | obj client.Object, 126 | heisenbridge map[string]interface{}, 127 | ) error { 128 | h := obj.(*synapsev1alpha1.Heisenbridge) 129 | 130 | heisenbridge["url"] = "http://" + utils.ComputeFQDN(h.Name, h.Namespace) + ":9898" 131 | return nil 132 | } 133 | -------------------------------------------------------------------------------- /controllers/synapse/heisenbridge/heisenbridge_controller.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package heisenbridge 18 | 19 | import ( 20 | "context" 21 | 22 | "k8s.io/apimachinery/pkg/runtime" 23 | ctrl "sigs.k8s.io/controller-runtime" 24 | "sigs.k8s.io/controller-runtime/pkg/client" 25 | 26 | "github.com/opdev/subreconciler" 27 | synapsev1alpha1 "github.com/opdev/synapse-operator/apis/synapse/v1alpha1" 28 | "github.com/opdev/synapse-operator/helpers/utils" 29 | ) 30 | 31 | // HeisenbridgeReconciler reconciles a Heisenbridge object 32 | type HeisenbridgeReconciler struct { 33 | client.Client 34 | Scheme *runtime.Scheme 35 | } 36 | 37 | //+kubebuilder:rbac:groups=synapse.opdev.io,resources=heisenbridges,verbs=get;list;watch;create;update;patch;delete 38 | //+kubebuilder:rbac:groups=synapse.opdev.io,resources=heisenbridges/status,verbs=get;update;patch 39 | //+kubebuilder:rbac:groups=synapse.opdev.io,resources=heisenbridges/finalizers,verbs=update 40 | 41 | // Reconcile is part of the main kubernetes reconciliation loop which aims to 42 | // move the current state of the cluster closer to the desired state. 43 | // TODO(user): Modify the Reconcile function to compare the state specified by 44 | // the Heisenbridge object against the actual cluster state, and then 45 | // perform operations to make the cluster state reflect the state specified by 46 | // the user. 47 | // 48 | // For more details, check Reconcile and its Result here: 49 | // - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.13.0/pkg/reconcile 50 | func (r *HeisenbridgeReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { 51 | var h synapsev1alpha1.Heisenbridge // The Heisenbridge object being reconciled 52 | if r, err := utils.GetResource(ctx, r.Client, req, &h); subreconciler.ShouldHaltOrRequeue(r, err) { 53 | return subreconciler.Evaluate(r, err) 54 | } 55 | 56 | // The list of subreconcilers for Heisenbridge. 57 | var subreconcilersForHeisenbridge []subreconciler.FnWithRequest 58 | 59 | // We need to trigger a Synapse reconciliation so that it becomes aware of 60 | // the Heisenbridge. 61 | subreconcilersForHeisenbridge = []subreconciler.FnWithRequest{ 62 | utils.HandleDelete(r.Client, &synapsev1alpha1.Heisenbridge{}), 63 | utils.AddFinalizer(r.Client, &synapsev1alpha1.Heisenbridge{}), 64 | utils.TriggerSynapseReconciliation(r.Client, &synapsev1alpha1.Heisenbridge{}), 65 | } 66 | 67 | // The user may specify a ConfigMap, containing the heisenbridge.yaml 68 | // config file, under Spec.Bridges.Heisenbridge.ConfigMap 69 | if h.Spec.ConfigMap.Name != "" { 70 | // If the user provided a custom Heisenbridge configuration via a 71 | // ConfigMap, we need to validate that the ConfigMap exists, and 72 | // create a copy. We also need to edit the heisenbridge 73 | // configuration. 74 | subreconcilersForHeisenbridge = append( 75 | subreconcilersForHeisenbridge, 76 | utils.CopyInputConfigMap(r.Client, r.Scheme, &synapsev1alpha1.Heisenbridge{}), 77 | r.configureHeisenbridgeConfigMap, 78 | ) 79 | } else { 80 | // If the user hasn't provided a ConfigMap with a custom 81 | // heisenbridge.yaml, we create a new ConfigMap with a default 82 | // heisenbridge.yaml. 83 | subreconcilersForHeisenbridge = append( 84 | subreconcilersForHeisenbridge, 85 | r.reconcileHeisenbridgeConfigMap, 86 | ) 87 | } 88 | 89 | // Reconcile Heisenbridge resources: Service and Deployment 90 | subreconcilersForHeisenbridge = append( 91 | subreconcilersForHeisenbridge, 92 | r.reconcileHeisenbridgeService, 93 | r.reconcileHeisenbridgeDeployment, 94 | ) 95 | 96 | // Run all subreconcilers sequentially 97 | for _, f := range subreconcilersForHeisenbridge { 98 | if r, err := f(ctx, req); subreconciler.ShouldHaltOrRequeue(r, err) { 99 | return subreconciler.Evaluate(r, err) 100 | } 101 | } 102 | 103 | return subreconciler.Evaluate(subreconciler.DoNotRequeue()) 104 | } 105 | 106 | // SetupWithManager sets up the controller with the Manager. 107 | func (r *HeisenbridgeReconciler) SetupWithManager(mgr ctrl.Manager) error { 108 | return ctrl.NewControllerManagedBy(mgr). 109 | For(&synapsev1alpha1.Heisenbridge{}). 110 | Complete(r) 111 | } 112 | -------------------------------------------------------------------------------- /controllers/synapse/heisenbridge/heisenbridge_deployment.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package heisenbridge 18 | 19 | import ( 20 | "context" 21 | "fmt" 22 | 23 | appsv1 "k8s.io/api/apps/v1" 24 | ctrl "sigs.k8s.io/controller-runtime" 25 | 26 | "github.com/opdev/subreconciler" 27 | synapsev1alpha1 "github.com/opdev/synapse-operator/apis/synapse/v1alpha1" 28 | "github.com/opdev/synapse-operator/helpers/reconcile" 29 | "github.com/opdev/synapse-operator/helpers/utils" 30 | "github.com/opdev/synapse-operator/internal/templates" 31 | ) 32 | 33 | // labelsForSynapse returns the labels for selecting the resources 34 | // belonging to the given synapse CR name. 35 | func labelsForHeisenbridge(name string) map[string]string { 36 | return map[string]string{"app": "heisenbridge", "heisenbridge_cr": name} 37 | } 38 | 39 | // reconcileHeisenbridgeDeployment is a function of type FnWithRequest, to 40 | // be called in the main reconciliation loop. 41 | // 42 | // It reconciles the Deployment for Heisenbridge to its desired state. 43 | func (r *HeisenbridgeReconciler) reconcileHeisenbridgeDeployment(ctx context.Context, req ctrl.Request) (*ctrl.Result, error) { 44 | h := &synapsev1alpha1.Heisenbridge{} 45 | if r, err := utils.GetResource(ctx, r.Client, req, h); subreconciler.ShouldHaltOrRequeue(r, err) { 46 | return r, err 47 | } 48 | 49 | desiredDeployment, err := r.deploymentForHeisenbridge(h) 50 | if err != nil { 51 | return subreconciler.RequeueWithError(err) 52 | } 53 | 54 | if err := reconcile.ReconcileResource( 55 | ctx, 56 | r.Client, 57 | desiredDeployment, 58 | &appsv1.Deployment{}, 59 | ); err != nil { 60 | return subreconciler.RequeueWithError(err) 61 | } 62 | 63 | return subreconciler.ContinueReconciling() 64 | } 65 | 66 | // deploymentForHeisenbridge returns a Heisenbridge Deployment object 67 | func (r *HeisenbridgeReconciler) deploymentForHeisenbridge(h *synapsev1alpha1.Heisenbridge) (*appsv1.Deployment, error) { 68 | type deploymentExtraValues struct { 69 | synapsev1alpha1.Heisenbridge 70 | Labels map[string]string 71 | Command []string 72 | } 73 | 74 | extraValues := deploymentExtraValues{ 75 | Heisenbridge: *h, 76 | Labels: labelsForHeisenbridge(h.Name), 77 | Command: r.craftHeisenbridgeCommad(*h), 78 | } 79 | 80 | dep, err := templates.ResourceFromTemplate[deploymentExtraValues, appsv1.Deployment](&extraValues, "heisenbridge_deployment") 81 | if err != nil { 82 | return nil, fmt.Errorf("could not get template: %v", err) 83 | } 84 | 85 | // Set Heisenbridge instance as the owner and controller 86 | if err := ctrl.SetControllerReference(h, dep, r.Scheme); err != nil { 87 | return &appsv1.Deployment{}, err 88 | } 89 | return dep, nil 90 | } 91 | 92 | func (r *HeisenbridgeReconciler) craftHeisenbridgeCommad(h synapsev1alpha1.Heisenbridge) []string { 93 | command := []string{ 94 | "python", 95 | "-m", 96 | "heisenbridge", 97 | } 98 | 99 | if h.Spec.VerboseLevel > 0 { 100 | verbosity := "-" 101 | for i := 1; i <= h.Spec.VerboseLevel; i++ { 102 | verbosity = verbosity + "v" 103 | } 104 | command = append(command, verbosity) 105 | } 106 | 107 | SynapseName := h.Spec.Synapse.Name 108 | SynapseNamespace := utils.ComputeNamespace(h.Namespace, h.Spec.Synapse.Namespace) 109 | 110 | command = append( 111 | command, 112 | "-c", 113 | "/data-heisenbridge/heisenbridge.yaml", 114 | "-l", 115 | "0.0.0.0", 116 | "http://"+utils.ComputeFQDN(SynapseName, SynapseNamespace)+":8008", 117 | ) 118 | 119 | return command 120 | } 121 | -------------------------------------------------------------------------------- /controllers/synapse/heisenbridge/heisenbridge_service.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package heisenbridge 18 | 19 | import ( 20 | "context" 21 | "fmt" 22 | 23 | corev1 "k8s.io/api/core/v1" 24 | ctrl "sigs.k8s.io/controller-runtime" 25 | 26 | "github.com/opdev/subreconciler" 27 | synapsev1alpha1 "github.com/opdev/synapse-operator/apis/synapse/v1alpha1" 28 | "github.com/opdev/synapse-operator/helpers/reconcile" 29 | "github.com/opdev/synapse-operator/helpers/utils" 30 | "github.com/opdev/synapse-operator/internal/templates" 31 | ) 32 | 33 | // reconcileHeisenbridgeService is a function of type FnWithRequest, to be 34 | // called in the main reconciliation loop. 35 | // 36 | // It reconciles the Service for Heisenbridge to its desired state. 37 | func (r *HeisenbridgeReconciler) reconcileHeisenbridgeService(ctx context.Context, req ctrl.Request) (*ctrl.Result, error) { 38 | h := &synapsev1alpha1.Heisenbridge{} 39 | if r, err := utils.GetResource(ctx, r.Client, req, h); subreconciler.ShouldHaltOrRequeue(r, err) { 40 | return r, err 41 | } 42 | 43 | desiredService, err := r.serviceForHeisenbridge(h) 44 | if err != nil { 45 | return subreconciler.RequeueWithError(err) 46 | } 47 | 48 | if err := reconcile.ReconcileResource( 49 | ctx, 50 | r.Client, 51 | desiredService, 52 | &corev1.Service{}, 53 | ); err != nil { 54 | return subreconciler.RequeueWithError(err) 55 | } 56 | 57 | return subreconciler.ContinueReconciling() 58 | } 59 | 60 | // serviceForSynapse returns a Heisenbridge Service object 61 | func (r *HeisenbridgeReconciler) serviceForHeisenbridge(h *synapsev1alpha1.Heisenbridge) (*corev1.Service, error) { 62 | type serviceExtraValues struct { 63 | synapsev1alpha1.Heisenbridge 64 | Labels map[string]string 65 | PortName string 66 | Port int 67 | TargetPort int 68 | } 69 | 70 | extraValues := serviceExtraValues{ 71 | Heisenbridge: *h, 72 | Labels: labelsForHeisenbridge(h.Name), 73 | PortName: "heisenbridge", 74 | Port: 9898, 75 | TargetPort: 9898, 76 | } 77 | 78 | service, err := templates.ResourceFromTemplate[serviceExtraValues, corev1.Service](&extraValues, "service") 79 | if err != nil { 80 | return nil, fmt.Errorf("could not get template: %v", err) 81 | } 82 | 83 | // Set Heisenbridge instance as the owner and controller 84 | if err := ctrl.SetControllerReference(h, service, r.Scheme); err != nil { 85 | return &corev1.Service{}, err 86 | } 87 | return service, nil 88 | } 89 | -------------------------------------------------------------------------------- /controllers/synapse/heisenbridge/heisenbridge_test.go: -------------------------------------------------------------------------------- 1 | // 2 | //This file contains unit tests for the heisenbridge package 3 | // 4 | 5 | package heisenbridge 6 | -------------------------------------------------------------------------------- /controllers/synapse/heisenbridge/suite_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package heisenbridge 18 | 19 | import ( 20 | "testing" 21 | 22 | . "github.com/onsi/ginkgo/v2" 23 | . "github.com/onsi/gomega" 24 | //+kubebuilder:scaffold:imports 25 | ) 26 | 27 | // These tests use Ginkgo (BDD-style Go testing framework). Refer to 28 | // http://onsi.github.io/ginkgo/ to learn more about Ginkgo. 29 | 30 | func TestAPIs(t *testing.T) { 31 | RegisterFailHandler(Fail) 32 | 33 | RunSpecs(t, "Heisenbridge Controller Suite") 34 | } 35 | -------------------------------------------------------------------------------- /controllers/synapse/mautrixsignal/mautrixsignal_configmap.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package mautrixsignal 18 | 19 | import ( 20 | "context" 21 | "errors" 22 | "fmt" 23 | 24 | corev1 "k8s.io/api/core/v1" 25 | "k8s.io/apimachinery/pkg/types" 26 | ctrl "sigs.k8s.io/controller-runtime" 27 | "sigs.k8s.io/controller-runtime/pkg/client" 28 | 29 | "github.com/opdev/subreconciler" 30 | synapsev1alpha1 "github.com/opdev/synapse-operator/apis/synapse/v1alpha1" 31 | "github.com/opdev/synapse-operator/helpers/reconcile" 32 | "github.com/opdev/synapse-operator/helpers/utils" 33 | "github.com/opdev/synapse-operator/internal/templates" 34 | ) 35 | 36 | // reconcileMautrixSignalConfigMap is a function of type FnWithRequest, to 37 | // be called in the main reconciliation loop. 38 | // 39 | // It reconciles the mautrix-signal ConfigMap to its desired state. It is 40 | // called only if the user hasn't provided its own ConfigMap for 41 | // mautrix-signal. 42 | func (r *MautrixSignalReconciler) reconcileMautrixSignalConfigMap(ctx context.Context, req ctrl.Request) (*ctrl.Result, error) { 43 | ms := &synapsev1alpha1.MautrixSignal{} 44 | if r, err := utils.GetResource(ctx, r.Client, req, ms); subreconciler.ShouldHaltOrRequeue(r, err) { 45 | return r, err 46 | } 47 | 48 | desiredConfigMap, err := r.configMapForMautrixSignal(ms) 49 | if err != nil { 50 | return subreconciler.RequeueWithError(err) 51 | } 52 | 53 | if err := reconcile.ReconcileResource( 54 | ctx, 55 | r.Client, 56 | desiredConfigMap, 57 | &corev1.ConfigMap{}, 58 | ); err != nil { 59 | return subreconciler.RequeueWithError(err) 60 | } 61 | 62 | return subreconciler.ContinueReconciling() 63 | } 64 | 65 | // configMapForSynapse returns a synapse ConfigMap object 66 | func (r *MautrixSignalReconciler) configMapForMautrixSignal(ms *synapsev1alpha1.MautrixSignal) (*corev1.ConfigMap, error) { 67 | synapseName := ms.Spec.Synapse.Name 68 | synapseNamespace := utils.ComputeNamespace(ms.Namespace, ms.Spec.Synapse.Namespace) 69 | 70 | type configmapExtraValues struct { 71 | synapsev1alpha1.MautrixSignal 72 | SynapseFQDN string 73 | MautrixsignalFQDN string 74 | } 75 | 76 | extraValues := configmapExtraValues{ 77 | MautrixSignal: *ms, 78 | SynapseFQDN: utils.ComputeFQDN(synapseName, synapseNamespace), 79 | MautrixsignalFQDN: utils.ComputeFQDN(ms.Name, ms.Namespace), 80 | } 81 | 82 | cm, err := templates.ResourceFromTemplate[configmapExtraValues, corev1.ConfigMap](&extraValues, "mautrixsignal_configmap") 83 | if err != nil { 84 | return nil, fmt.Errorf("could not get template: %v", err) 85 | } 86 | 87 | // Set MautrixSignal instance as the owner and controller 88 | if err := ctrl.SetControllerReference(ms, cm, r.Scheme); err != nil { 89 | return &corev1.ConfigMap{}, err 90 | } 91 | 92 | return cm, nil 93 | } 94 | 95 | // configureMautrixSignalConfigMap is a function of type FnWithRequest, to 96 | // be called in the main reconciliation loop. 97 | // 98 | // Following the previous copy of the user-provided ConfigMap, it edits the 99 | // content of the copy to ensure that mautrix-signal is correctly configured. 100 | func (r *MautrixSignalReconciler) configureMautrixSignalConfigMap(ctx context.Context, req ctrl.Request) (*ctrl.Result, error) { 101 | ms := &synapsev1alpha1.MautrixSignal{} 102 | if r, err := utils.GetResource(ctx, r.Client, req, ms); subreconciler.ShouldHaltOrRequeue(r, err) { 103 | return r, err 104 | } 105 | 106 | keyForConfigMap := types.NamespacedName{ 107 | Name: ms.Name, 108 | Namespace: ms.Namespace, 109 | } 110 | 111 | // Correct data in mautrix-signal ConfigMap 112 | if err := utils.UpdateConfigMap( 113 | ctx, 114 | r.Client, 115 | keyForConfigMap, 116 | ms, 117 | r.updateMautrixSignalData, 118 | "config.yaml", 119 | ); err != nil { 120 | return subreconciler.RequeueWithError(err) 121 | } 122 | 123 | return subreconciler.ContinueReconciling() 124 | } 125 | 126 | // updateMautrixSignalData is a function of type updateDataFunc function to 127 | // be passed as an argument in a call to updateConfigMap. 128 | // 129 | // It configures the user-provided config.yaml with the correct values. Among 130 | // other things, it ensures that the bridge can reach the Synapse homeserver 131 | // and knows the correct path to the signald socket. 132 | func (r *MautrixSignalReconciler) updateMautrixSignalData( 133 | obj client.Object, 134 | config map[string]interface{}, 135 | ) error { 136 | ms := obj.(*synapsev1alpha1.MautrixSignal) 137 | 138 | synapseName := ms.Spec.Synapse.Name 139 | synapseNamespace := utils.ComputeNamespace(ms.Namespace, ms.Spec.Synapse.Namespace) 140 | synapseServerName := ms.Status.Synapse.ServerName 141 | 142 | // Update the homeserver section so that the bridge can reach Synapse 143 | configHomeserver, ok := config["homeserver"].(map[interface{}]interface{}) 144 | if !ok { 145 | err := errors.New("cannot parse mautrix-signal config.yaml: error parsing 'homeserver' section") 146 | return err 147 | } 148 | configHomeserver["address"] = "http://" + utils.ComputeFQDN(synapseName, synapseNamespace) + ":8008" 149 | configHomeserver["domain"] = synapseServerName 150 | config["homeserver"] = configHomeserver 151 | 152 | // Update the appservice section so that Synapse can reach the bridge 153 | configAppservice, ok := config["appservice"].(map[interface{}]interface{}) 154 | if !ok { 155 | err := errors.New("cannot parse mautrix-signal config.yaml: error parsing 'appservice' section") 156 | return err 157 | } 158 | configAppservice["address"] = "http://" + utils.ComputeFQDN(ms.Name, ms.Namespace) + ":29328" 159 | config["appservice"] = configAppservice 160 | 161 | // Update the path to the signal socket path 162 | configSignal, ok := config["signal"].(map[interface{}]interface{}) 163 | if !ok { 164 | err := errors.New("cannot parse mautrix-signal config.yaml: error parsing 'signal' section") 165 | return err 166 | } 167 | configSignal["socket_path"] = "/signald/signald.sock" 168 | config["signal"] = configSignal 169 | 170 | // Update persmissions to use the correct domain name 171 | configBridge, ok := config["bridge"].(map[interface{}]interface{}) 172 | if !ok { 173 | err := errors.New("cannot parse mautrix-signal config.yaml: error parsing 'bridge' section") 174 | return err 175 | } 176 | configBridge["permissions"] = map[string]string{ 177 | "*": "relay", 178 | synapseServerName: "user", 179 | "@admin:" + synapseServerName: "admin", 180 | } 181 | config["bridge"] = configBridge 182 | 183 | // Update the path to the log file 184 | configLogging, ok := config["logging"].(map[interface{}]interface{}) 185 | if !ok { 186 | err := errors.New("cannot parse mautrix-signal config.yaml: error parsing 'logging' section") 187 | return err 188 | } 189 | configLoggingHandlers, ok := configLogging["handlers"].(map[interface{}]interface{}) 190 | if !ok { 191 | err := errors.New("cannot parse mautrix-signal config.yaml: error parsing 'logging/handlers' section") 192 | return err 193 | } 194 | configLoggingHandlersFile, ok := configLoggingHandlers["file"].(map[interface{}]interface{}) 195 | if !ok { 196 | err := errors.New("cannot parse mautrix-signal config.yaml: error parsing 'logging/handlers/file' section") 197 | return err 198 | } 199 | configLoggingHandlersFile["filename"] = "/data/mautrix-signal.log" 200 | configLoggingHandlers["file"] = configLoggingHandlersFile 201 | configLogging["handlers"] = configLoggingHandlers 202 | config["logging"] = configLogging 203 | 204 | return nil 205 | } 206 | -------------------------------------------------------------------------------- /controllers/synapse/mautrixsignal/mautrixsignal_controller.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package mautrixsignal 18 | 19 | import ( 20 | "context" 21 | 22 | "k8s.io/apimachinery/pkg/runtime" 23 | ctrl "sigs.k8s.io/controller-runtime" 24 | "sigs.k8s.io/controller-runtime/pkg/client" 25 | ctrllog "sigs.k8s.io/controller-runtime/pkg/log" 26 | 27 | "github.com/opdev/subreconciler" 28 | synapsev1alpha1 "github.com/opdev/synapse-operator/apis/synapse/v1alpha1" 29 | "github.com/opdev/synapse-operator/helpers/utils" 30 | ) 31 | 32 | // MautrixSignalReconciler reconciles a MautrixSignal object 33 | type MautrixSignalReconciler struct { 34 | client.Client 35 | Scheme *runtime.Scheme 36 | } 37 | 38 | //+kubebuilder:rbac:groups=synapse.opdev.io,resources=mautrixsignals,verbs=get;list;watch;create;update;patch;delete 39 | //+kubebuilder:rbac:groups=synapse.opdev.io,resources=mautrixsignals/status,verbs=get;update;patch 40 | //+kubebuilder:rbac:groups=synapse.opdev.io,resources=mautrixsignals/finalizers,verbs=update 41 | 42 | // Reconcile is part of the main kubernetes reconciliation loop which aims to 43 | // move the current state of the cluster closer to the desired state. 44 | // TODO(user): Modify the Reconcile function to compare the state specified by 45 | // the MautrixSignal object against the actual cluster state, and then 46 | // perform operations to make the cluster state reflect the state specified by 47 | // the user. 48 | // 49 | // For more details, check Reconcile and its Result here: 50 | // - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.13.0/pkg/reconcile 51 | func (r *MautrixSignalReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { 52 | var ms synapsev1alpha1.MautrixSignal // The mautrix-signal object being reconciled 53 | if r, err := utils.GetResource(ctx, r.Client, req, &ms); subreconciler.ShouldHaltOrRequeue(r, err) { 54 | return subreconciler.Evaluate(r, err) 55 | } 56 | 57 | // The list of subreconcilers for mautrix-signal. 58 | var subreconcilersForMautrixSignal []subreconciler.FnWithRequest 59 | 60 | // We need to trigger a Synapse reconciliation so that it becomes aware of 61 | // the MautrixSignal. We also need to complete the MautrixSignal Status. 62 | subreconcilersForMautrixSignal = []subreconciler.FnWithRequest{ 63 | utils.HandleDelete(r.Client, &synapsev1alpha1.MautrixSignal{}), 64 | utils.AddFinalizer(r.Client, &synapsev1alpha1.MautrixSignal{}), 65 | utils.TriggerSynapseReconciliation(r.Client, &synapsev1alpha1.MautrixSignal{}), 66 | r.buildMautrixSignalStatus, 67 | } 68 | 69 | // The user may specify a ConfigMap, containing the config.yaml config 70 | // file, under Spec.Bridges.MautrixSignal.ConfigMap 71 | if ms.Spec.ConfigMap.Name != "" { 72 | // If the user provided a custom mautrix-signal configuration via a 73 | // ConfigMap, we need to validate that the ConfigMap exists, and 74 | // create a copy. We also need to edit the mautrix-signal 75 | // configuration. 76 | subreconcilersForMautrixSignal = append( 77 | subreconcilersForMautrixSignal, 78 | utils.CopyInputConfigMap(r.Client, r.Scheme, &synapsev1alpha1.MautrixSignal{}), 79 | r.configureMautrixSignalConfigMap, 80 | ) 81 | } else { 82 | // If the user hasn't provided a ConfigMap with a custom 83 | // config.yaml, we create a new ConfigMap with a default 84 | // config.yaml. 85 | subreconcilersForMautrixSignal = append( 86 | subreconcilersForMautrixSignal, 87 | r.reconcileMautrixSignalConfigMap, 88 | ) 89 | } 90 | 91 | // SA and RB are only necessary if we're running on OpenShift 92 | if ms.Status.IsOpenshift { 93 | subreconcilersForMautrixSignal = append( 94 | subreconcilersForMautrixSignal, 95 | r.reconcileMautrixSignalServiceAccount, 96 | r.reconcileMautrixSignalRoleBinding, 97 | ) 98 | } 99 | 100 | // Reconcile mautrix-signal resources: Service, PVC and Deployment 101 | subreconcilersForMautrixSignal = append( 102 | subreconcilersForMautrixSignal, 103 | r.reconcileMautrixSignalService, 104 | r.reconcileMautrixSignalPVC, 105 | r.reconcileMautrixSignalDeployment, 106 | ) 107 | 108 | // Run all subreconcilers sequentially 109 | for _, f := range subreconcilersForMautrixSignal { 110 | if r, err := f(ctx, req); subreconciler.ShouldHaltOrRequeue(r, err) { 111 | return subreconciler.Evaluate(r, err) 112 | } 113 | } 114 | 115 | return subreconciler.Evaluate(subreconciler.DoNotRequeue()) 116 | } 117 | 118 | func (r *MautrixSignalReconciler) buildMautrixSignalStatus(ctx context.Context, req ctrl.Request) (*ctrl.Result, error) { 119 | log := ctrllog.FromContext(ctx) 120 | 121 | ms := &synapsev1alpha1.MautrixSignal{} 122 | if r, err := utils.GetResource(ctx, r.Client, req, ms); subreconciler.ShouldHaltOrRequeue(r, err) { 123 | return r, err 124 | } 125 | 126 | s := synapsev1alpha1.Synapse{} 127 | if err := utils.FetchSynapseInstance(ctx, r.Client, ms, &s); err != nil { 128 | log.Error(err, "Error fetching Synapse instance") 129 | return subreconciler.RequeueWithError(err) 130 | } 131 | 132 | // Get Synapse ServerName 133 | serverName, err := utils.GetSynapseServerName(s) 134 | if err != nil { 135 | log.Error( 136 | err, 137 | "Error getting Synapse ServerName", 138 | "Synapse Name", ms.Spec.Synapse.Name, 139 | "Synapse Namespace", utils.ComputeNamespace(ms.Namespace, ms.Spec.Synapse.Namespace), 140 | ) 141 | return subreconciler.RequeueWithError(err) 142 | } 143 | ms.Status.Synapse.ServerName = serverName 144 | 145 | ms.Status.IsOpenshift = s.Spec.IsOpenshift 146 | 147 | err = utils.UpdateResourceStatus(ctx, r.Client, ms, &synapsev1alpha1.MautrixSignal{}) 148 | if err != nil { 149 | log.Error(err, "Error updating mautrix-signal Status") 150 | return subreconciler.RequeueWithError(err) 151 | } 152 | 153 | return subreconciler.ContinueReconciling() 154 | } 155 | 156 | // SetupWithManager sets up the controller with the Manager. 157 | func (r *MautrixSignalReconciler) SetupWithManager(mgr ctrl.Manager) error { 158 | return ctrl.NewControllerManagedBy(mgr). 159 | For(&synapsev1alpha1.MautrixSignal{}). 160 | Complete(r) 161 | } 162 | -------------------------------------------------------------------------------- /controllers/synapse/mautrixsignal/mautrixsignal_deployment.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package mautrixsignal 18 | 19 | import ( 20 | "context" 21 | "fmt" 22 | 23 | appsv1 "k8s.io/api/apps/v1" 24 | ctrl "sigs.k8s.io/controller-runtime" 25 | 26 | "github.com/opdev/subreconciler" 27 | synapsev1alpha1 "github.com/opdev/synapse-operator/apis/synapse/v1alpha1" 28 | "github.com/opdev/synapse-operator/helpers/reconcile" 29 | "github.com/opdev/synapse-operator/helpers/utils" 30 | "github.com/opdev/synapse-operator/internal/templates" 31 | ) 32 | 33 | // labelsForMautrixSignal returns the labels for selecting the resources 34 | // belonging to the given synapse CR name. 35 | func labelsForMautrixSignal(name string) map[string]string { 36 | return map[string]string{"app": "mautrix-signal", "mautrixsignal_cr": name} 37 | } 38 | 39 | // reconcileMautrixSignalDeployment is a function of type FnWithRequest, 40 | // to be called in the main reconciliation loop. 41 | // 42 | // It reconciles the Deployment for mautrix-signal to its desired state. 43 | func (r *MautrixSignalReconciler) reconcileMautrixSignalDeployment(ctx context.Context, req ctrl.Request) (*ctrl.Result, error) { 44 | ms := &synapsev1alpha1.MautrixSignal{} 45 | if r, err := utils.GetResource(ctx, r.Client, req, ms); subreconciler.ShouldHaltOrRequeue(r, err) { 46 | return r, err 47 | } 48 | 49 | desiredDeployment, err := r.deploymentForMautrixSignal(ms) 50 | if err != nil { 51 | return subreconciler.RequeueWithError(err) 52 | } 53 | 54 | if err := reconcile.ReconcileResource( 55 | ctx, 56 | r.Client, 57 | desiredDeployment, 58 | &appsv1.Deployment{}, 59 | ); err != nil { 60 | return subreconciler.RequeueWithError(err) 61 | } 62 | 63 | return subreconciler.ContinueReconciling() 64 | } 65 | 66 | // deploymentForMautrixSignal returns a Deployment object for the mautrix-signal bridge 67 | func (r *MautrixSignalReconciler) deploymentForMautrixSignal(ms *synapsev1alpha1.MautrixSignal) (*appsv1.Deployment, error) { 68 | type deploymentExtraValues struct { 69 | synapsev1alpha1.MautrixSignal 70 | Labels map[string]string 71 | } 72 | 73 | extraValues := deploymentExtraValues{ 74 | MautrixSignal: *ms, 75 | Labels: labelsForMautrixSignal(ms.Name), 76 | } 77 | 78 | dep, err := templates.ResourceFromTemplate[deploymentExtraValues, appsv1.Deployment](&extraValues, "mautrixsignal_deployment") 79 | if err != nil { 80 | return nil, fmt.Errorf("could not get template: %v", err) 81 | } 82 | 83 | // Set MautrixSignal instance as the owner and controller 84 | if err := ctrl.SetControllerReference(ms, dep, r.Scheme); err != nil { 85 | return &appsv1.Deployment{}, err 86 | } 87 | return dep, nil 88 | } 89 | -------------------------------------------------------------------------------- /controllers/synapse/mautrixsignal/mautrixsignal_pvc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package mautrixsignal 18 | 19 | import ( 20 | "context" 21 | "fmt" 22 | 23 | corev1 "k8s.io/api/core/v1" 24 | ctrl "sigs.k8s.io/controller-runtime" 25 | 26 | "github.com/opdev/subreconciler" 27 | synapsev1alpha1 "github.com/opdev/synapse-operator/apis/synapse/v1alpha1" 28 | "github.com/opdev/synapse-operator/helpers/reconcile" 29 | "github.com/opdev/synapse-operator/helpers/utils" 30 | "github.com/opdev/synapse-operator/internal/templates" 31 | ) 32 | 33 | // reconcileMautrixSignalPVC is a function of type FnWithRequest, to be 34 | // called in the main reconciliation loop. 35 | // 36 | // It reconciles the PVC for mautrix-signal to its desired state. 37 | func (r *MautrixSignalReconciler) reconcileMautrixSignalPVC(ctx context.Context, req ctrl.Request) (*ctrl.Result, error) { 38 | ms := &synapsev1alpha1.MautrixSignal{} 39 | if r, err := utils.GetResource(ctx, r.Client, req, ms); subreconciler.ShouldHaltOrRequeue(r, err) { 40 | return r, err 41 | } 42 | 43 | desiredPVC, err := r.persistentVolumeClaimForMautrixSignal(ms) 44 | if err != nil { 45 | return subreconciler.RequeueWithError(err) 46 | } 47 | 48 | if err := reconcile.ReconcileResource( 49 | ctx, 50 | r.Client, 51 | desiredPVC, 52 | &corev1.PersistentVolumeClaim{}, 53 | ); err != nil { 54 | return subreconciler.RequeueWithError(err) 55 | } 56 | 57 | return subreconciler.ContinueReconciling() 58 | } 59 | 60 | // persistentVolumeClaimForMautrixSignal returns a mautrix-signal PVC object 61 | func (r *MautrixSignalReconciler) persistentVolumeClaimForMautrixSignal(ms *synapsev1alpha1.MautrixSignal) (*corev1.PersistentVolumeClaim, error) { 62 | pvc, err := templates.ResourceFromTemplate[synapsev1alpha1.MautrixSignal, corev1.PersistentVolumeClaim](ms, "pvc") 63 | if err != nil { 64 | return nil, fmt.Errorf("could not get template: %v", err) 65 | } 66 | 67 | // Set MautrixSignal instance as the owner and controller 68 | if err := ctrl.SetControllerReference(ms, pvc, r.Scheme); err != nil { 69 | return &corev1.PersistentVolumeClaim{}, err 70 | } 71 | return pvc, nil 72 | } 73 | -------------------------------------------------------------------------------- /controllers/synapse/mautrixsignal/mautrixsignal_service.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package mautrixsignal 18 | 19 | import ( 20 | "context" 21 | "fmt" 22 | 23 | corev1 "k8s.io/api/core/v1" 24 | ctrl "sigs.k8s.io/controller-runtime" 25 | 26 | "github.com/opdev/subreconciler" 27 | synapsev1alpha1 "github.com/opdev/synapse-operator/apis/synapse/v1alpha1" 28 | "github.com/opdev/synapse-operator/helpers/reconcile" 29 | "github.com/opdev/synapse-operator/helpers/utils" 30 | "github.com/opdev/synapse-operator/internal/templates" 31 | ) 32 | 33 | // reconcileMautrixSignalService is a function of type FnWithRequest, to 34 | // be called in the main reconciliation loop. 35 | // 36 | // It reconciles the Service for mautrix-signal to its desired state. 37 | func (r *MautrixSignalReconciler) reconcileMautrixSignalService(ctx context.Context, req ctrl.Request) (*ctrl.Result, error) { 38 | ms := &synapsev1alpha1.MautrixSignal{} 39 | if r, err := utils.GetResource(ctx, r.Client, req, ms); subreconciler.ShouldHaltOrRequeue(r, err) { 40 | return r, err 41 | } 42 | 43 | desiredService, err := r.serviceForMautrixSignal(ms) 44 | if err != nil { 45 | return subreconciler.RequeueWithError(err) 46 | } 47 | 48 | if err := reconcile.ReconcileResource( 49 | ctx, 50 | r.Client, 51 | desiredService, 52 | &corev1.Service{}, 53 | ); err != nil { 54 | return subreconciler.RequeueWithError(err) 55 | } 56 | 57 | return subreconciler.ContinueReconciling() 58 | } 59 | 60 | // serviceForMautrixSignal returns a mautrix-signal Service object 61 | func (r *MautrixSignalReconciler) serviceForMautrixSignal(ms *synapsev1alpha1.MautrixSignal) (*corev1.Service, error) { 62 | type serviceExtraValues struct { 63 | synapsev1alpha1.MautrixSignal 64 | Labels map[string]string 65 | PortName string 66 | Port int 67 | TargetPort int 68 | } 69 | 70 | extraValues := serviceExtraValues{ 71 | MautrixSignal: *ms, 72 | Labels: labelsForMautrixSignal(ms.Name), 73 | PortName: "mautrix-signal", 74 | Port: 29328, 75 | TargetPort: 29328, 76 | } 77 | 78 | service, err := templates.ResourceFromTemplate[serviceExtraValues, corev1.Service](&extraValues, "service") 79 | if err != nil { 80 | return nil, fmt.Errorf("could not get template: %v", err) 81 | } 82 | 83 | // Set MautrixSignal instance as the owner and controller 84 | if err := ctrl.SetControllerReference(ms, service, r.Scheme); err != nil { 85 | return &corev1.Service{}, err 86 | } 87 | return service, nil 88 | } 89 | -------------------------------------------------------------------------------- /controllers/synapse/mautrixsignal/mautrixsignal_serviceaccount.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package mautrixsignal 18 | 19 | import ( 20 | "context" 21 | "fmt" 22 | 23 | corev1 "k8s.io/api/core/v1" 24 | rbacv1 "k8s.io/api/rbac/v1" 25 | ctrl "sigs.k8s.io/controller-runtime" 26 | "sigs.k8s.io/controller-runtime/pkg/client" 27 | 28 | "github.com/opdev/subreconciler" 29 | synapsev1alpha1 "github.com/opdev/synapse-operator/apis/synapse/v1alpha1" 30 | "github.com/opdev/synapse-operator/helpers/reconcile" 31 | "github.com/opdev/synapse-operator/helpers/utils" 32 | "github.com/opdev/synapse-operator/internal/templates" 33 | ) 34 | 35 | // reconcileMautrixSignalServiceAccount is a function of type 36 | // FnWithRequest, to be called in the main reconciliation loop. 37 | // 38 | // It reconciles the ServiceAccount for mautrix-signal to its desired state. 39 | func (r *MautrixSignalReconciler) reconcileMautrixSignalServiceAccount(ctx context.Context, req ctrl.Request) (*ctrl.Result, error) { 40 | ms := &synapsev1alpha1.MautrixSignal{} 41 | if r, err := utils.GetResource(ctx, r.Client, req, ms); subreconciler.ShouldHaltOrRequeue(r, err) { 42 | return r, err 43 | } 44 | 45 | desiredServiceAccount, err := r.serviceAccountForMautrixSignal(ms) 46 | if err != nil { 47 | return subreconciler.RequeueWithError(err) 48 | } 49 | 50 | if err := reconcile.ReconcileResource( 51 | ctx, 52 | r.Client, 53 | desiredServiceAccount, 54 | &corev1.ServiceAccount{}, 55 | ); err != nil { 56 | return subreconciler.RequeueWithError(err) 57 | } 58 | 59 | return subreconciler.ContinueReconciling() 60 | } 61 | 62 | // serviceAccountForMautrixSignal returns a ServiceAccount object for running the mautrix-signal bridge 63 | func (r *MautrixSignalReconciler) serviceAccountForMautrixSignal(ms *synapsev1alpha1.MautrixSignal) (client.Object, error) { 64 | // TODO: https://github.com/opdev/synapse-operator/issues/19 65 | sa, err := templates.ResourceFromTemplate[synapsev1alpha1.MautrixSignal, corev1.ServiceAccount](ms, "serviceaccount") 66 | if err != nil { 67 | return nil, fmt.Errorf("could not get template: %v", err) 68 | } 69 | 70 | // Set MautrixSignal instance as the owner and controller 71 | if err := ctrl.SetControllerReference(ms, sa, r.Scheme); err != nil { 72 | return &corev1.ServiceAccount{}, err 73 | } 74 | return sa, nil 75 | } 76 | 77 | // reconcileMautrixSignalRoleBinding is a function of type FnWithRequest, 78 | // to be called in the main reconciliation loop. 79 | // 80 | // It reconciles the RoleBinding for mautrix-signal to its desired state. 81 | func (r *MautrixSignalReconciler) reconcileMautrixSignalRoleBinding(ctx context.Context, req ctrl.Request) (*ctrl.Result, error) { 82 | ms := &synapsev1alpha1.MautrixSignal{} 83 | if r, err := utils.GetResource(ctx, r.Client, req, ms); subreconciler.ShouldHaltOrRequeue(r, err) { 84 | return r, err 85 | } 86 | 87 | desiredRoleBinding, err := r.roleBindingForMautrixSignal(ms) 88 | if err != nil { 89 | return subreconciler.RequeueWithError(err) 90 | } 91 | 92 | if err := reconcile.ReconcileResource( 93 | ctx, 94 | r.Client, 95 | desiredRoleBinding, 96 | &rbacv1.RoleBinding{}, 97 | ); err != nil { 98 | return subreconciler.RequeueWithError(err) 99 | } 100 | 101 | return subreconciler.ContinueReconciling() 102 | } 103 | 104 | // roleBindingForMautrixSignal returns a RoleBinding object for the mautrix-signal bridge 105 | func (r *MautrixSignalReconciler) roleBindingForMautrixSignal(ms *synapsev1alpha1.MautrixSignal) (*rbacv1.RoleBinding, error) { 106 | // TODO: https://github.com/opdev/synapse-operator/issues/19 107 | rb, err := templates.ResourceFromTemplate[synapsev1alpha1.MautrixSignal, rbacv1.RoleBinding](ms, "rolebinding") 108 | if err != nil { 109 | return nil, fmt.Errorf("could not get template: %v", err) 110 | } 111 | 112 | // Set MautrixSignal instance as the owner and controller 113 | if err := ctrl.SetControllerReference(ms, rb, r.Scheme); err != nil { 114 | return &rbacv1.RoleBinding{}, err 115 | } 116 | return rb, nil 117 | } 118 | -------------------------------------------------------------------------------- /controllers/synapse/mautrixsignal/mautrixsignal_test.go: -------------------------------------------------------------------------------- 1 | // 2 | //This file contains unit tests for the mautrixsignal package 3 | // 4 | 5 | package mautrixsignal 6 | -------------------------------------------------------------------------------- /controllers/synapse/mautrixsignal/suite_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package mautrixsignal 18 | 19 | import ( 20 | "testing" 21 | 22 | . "github.com/onsi/ginkgo/v2" 23 | . "github.com/onsi/gomega" 24 | //+kubebuilder:scaffold:imports 25 | ) 26 | 27 | // These tests use Ginkgo (BDD-style Go testing framework). Refer to 28 | // http://onsi.github.io/ginkgo/ to learn more about Ginkgo. 29 | 30 | func TestAPIs(t *testing.T) { 31 | RegisterFailHandler(Fail) 32 | 33 | RunSpecs(t, "MautrixSignal Controller Suite") 34 | } 35 | -------------------------------------------------------------------------------- /controllers/synapse/synapse/suite_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package synapse 18 | 19 | import ( 20 | "testing" 21 | 22 | . "github.com/onsi/ginkgo/v2" 23 | . "github.com/onsi/gomega" 24 | //+kubebuilder:scaffold:imports 25 | ) 26 | 27 | // These tests use Ginkgo (BDD-style Go testing framework). Refer to 28 | // http://onsi.github.io/ginkgo/ to learn more about Ginkgo. 29 | 30 | func TestAPIs(t *testing.T) { 31 | RegisterFailHandler(Fail) 32 | 33 | RunSpecs(t, "Synapse Controller Suite") 34 | } 35 | -------------------------------------------------------------------------------- /controllers/synapse/synapse/synapse_deployment.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package synapse 18 | 19 | import ( 20 | "context" 21 | "fmt" 22 | 23 | appsv1 "k8s.io/api/apps/v1" 24 | ctrl "sigs.k8s.io/controller-runtime" 25 | 26 | "github.com/opdev/subreconciler" 27 | synapsev1alpha1 "github.com/opdev/synapse-operator/apis/synapse/v1alpha1" 28 | "github.com/opdev/synapse-operator/helpers/reconcile" 29 | "github.com/opdev/synapse-operator/helpers/utils" 30 | "github.com/opdev/synapse-operator/internal/templates" 31 | ) 32 | 33 | // reconcileSynapseDeployment is a function of type FnWithRequest, to be 34 | // called in the main reconciliation loop. 35 | // 36 | // It reconciles the Deployment for Synapse to its desired state. 37 | func (r *SynapseReconciler) reconcileSynapseDeployment(ctx context.Context, req ctrl.Request) (*ctrl.Result, error) { 38 | s := &synapsev1alpha1.Synapse{} 39 | if r, err := utils.GetResource(ctx, r.Client, req, s); subreconciler.ShouldHaltOrRequeue(r, err) { 40 | return r, err 41 | } 42 | 43 | depl, err := r.deploymentForSynapse(s) 44 | if err != nil { 45 | return subreconciler.RequeueWithError(err) 46 | } 47 | 48 | if err := reconcile.ReconcileResource( 49 | ctx, 50 | r.Client, 51 | depl, 52 | &appsv1.Deployment{}, 53 | ); err != nil { 54 | return subreconciler.RequeueWithError(err) 55 | } 56 | 57 | return subreconciler.ContinueReconciling() 58 | } 59 | 60 | // deploymentForSynapse returns a synapse Deployment object 61 | func (r *SynapseReconciler) deploymentForSynapse(s *synapsev1alpha1.Synapse) (*appsv1.Deployment, error) { 62 | type deploymentExtraValues struct { 63 | synapsev1alpha1.Synapse 64 | Labels map[string]string 65 | } 66 | 67 | extraValues := deploymentExtraValues{ 68 | Synapse: *s, 69 | Labels: labelsForSynapse(s.Name), 70 | } 71 | 72 | dep, err := templates.ResourceFromTemplate[deploymentExtraValues, appsv1.Deployment](&extraValues, "synapse_deployment") 73 | if err != nil { 74 | return nil, fmt.Errorf("could not get template: %v", err) 75 | } 76 | 77 | // Set Synapse instance as the owner and controller 78 | if err := ctrl.SetControllerReference(s, dep, r.Scheme); err != nil { 79 | return &appsv1.Deployment{}, err 80 | } 81 | 82 | return dep, nil 83 | } 84 | -------------------------------------------------------------------------------- /controllers/synapse/synapse/synapse_postgrescluster.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package synapse 18 | 19 | import ( 20 | "context" 21 | b64 "encoding/base64" 22 | "errors" 23 | "time" 24 | 25 | corev1 "k8s.io/api/core/v1" 26 | "k8s.io/apimachinery/pkg/api/resource" 27 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 28 | "k8s.io/apimachinery/pkg/types" 29 | ctrl "sigs.k8s.io/controller-runtime" 30 | ctrllog "sigs.k8s.io/controller-runtime/pkg/log" 31 | 32 | pgov1beta1 "github.com/crunchydata/postgres-operator/pkg/apis/postgres-operator.crunchydata.com/v1beta1" 33 | subreconciler "github.com/opdev/subreconciler" 34 | synapsev1alpha1 "github.com/opdev/synapse-operator/apis/synapse/v1alpha1" 35 | "github.com/opdev/synapse-operator/helpers/reconcile" 36 | "github.com/opdev/synapse-operator/helpers/utils" 37 | ) 38 | 39 | // reconcilePostgresClusterCR is a function of type FnWithRequest, to be 40 | // called in the main reconciliation loop. 41 | // 42 | // It reconciles the PostgresCluster CR to its desired state, and requeues 43 | // until the PostgreSQL cluster is up. 44 | func (r *SynapseReconciler) reconcilePostgresClusterCR(ctx context.Context, req ctrl.Request) (*ctrl.Result, error) { 45 | log := ctrllog.FromContext(ctx) 46 | 47 | s := &synapsev1alpha1.Synapse{} 48 | if r, err := utils.GetResource(ctx, r.Client, req, s); subreconciler.ShouldHaltOrRequeue(r, err) { 49 | return r, err 50 | } 51 | 52 | createdPostgresCluster := pgov1beta1.PostgresCluster{} 53 | postgresClusterObjectMeta := reconcile.SetObjectMeta( 54 | GetPostgresClusterResourceName(*s), 55 | s.Namespace, 56 | map[string]string{}, 57 | ) 58 | keyForPostgresCluster := types.NamespacedName{ 59 | Name: GetPostgresClusterResourceName(*s), 60 | Namespace: s.Namespace, 61 | } 62 | 63 | desiredPostgresCluster, err := r.postgresClusterForSynapse(s, postgresClusterObjectMeta) 64 | if err != nil { 65 | return subreconciler.RequeueWithError(err) 66 | } 67 | 68 | // Create PostgresCluster for Synapse 69 | if err := reconcile.ReconcileResource( 70 | ctx, 71 | r.Client, 72 | desiredPostgresCluster, 73 | &createdPostgresCluster, 74 | ); err != nil { 75 | return subreconciler.RequeueWithError(err) 76 | } 77 | 78 | // Wait for PostgresCluster to be up 79 | // TODO: can be removed ? 80 | if err := r.Get(ctx, keyForPostgresCluster, &createdPostgresCluster); err != nil { 81 | return subreconciler.RequeueWithError(err) 82 | } 83 | if !r.isPostgresClusterReady(createdPostgresCluster) { 84 | s.Status.DatabaseConnectionInfo.State = "NOT READY" 85 | err = utils.UpdateResourceStatus(ctx, r.Client, s, &synapsev1alpha1.Synapse{}) 86 | if err != nil { 87 | log.Error(err, "Error updating Synapse State") 88 | } 89 | 90 | err = errors.New("postgreSQL Database not ready yet") 91 | return subreconciler.RequeueWithDelayAndError(time.Duration(5), err) 92 | } 93 | 94 | return subreconciler.ContinueReconciling() 95 | } 96 | 97 | // postgresClusterForSynapse returns a PostgresCluster object 98 | func (r *SynapseReconciler) postgresClusterForSynapse(s *synapsev1alpha1.Synapse, objectMeta metav1.ObjectMeta) (*pgov1beta1.PostgresCluster, error) { 99 | postgresCluster := &pgov1beta1.PostgresCluster{ 100 | ObjectMeta: objectMeta, 101 | Spec: pgov1beta1.PostgresClusterSpec{ 102 | Image: "registry.developers.crunchydata.com/crunchydata/crunchy-postgres:ubi8-14.5-1", 103 | PostgresVersion: 14, 104 | InstanceSets: []pgov1beta1.PostgresInstanceSetSpec{{ 105 | Name: "instance1", 106 | DataVolumeClaimSpec: corev1.PersistentVolumeClaimSpec{ 107 | AccessModes: []corev1.PersistentVolumeAccessMode{"ReadWriteOnce"}, 108 | Resources: corev1.VolumeResourceRequirements{ 109 | Requests: corev1.ResourceList{ 110 | "storage": *resource.NewQuantity(1*1024*1024*1024, resource.BinarySI), 111 | }, 112 | }, 113 | }, 114 | }}, 115 | Backups: pgov1beta1.Backups{ 116 | PGBackRest: pgov1beta1.PGBackRestArchive{ 117 | Image: "registry.developers.crunchydata.com/crunchydata/crunchy-pgbackrest:ubi8-2.40-1", 118 | Repos: []pgov1beta1.PGBackRestRepo{{ 119 | Name: "repo1", 120 | Volume: &pgov1beta1.RepoPVC{ 121 | VolumeClaimSpec: corev1.PersistentVolumeClaimSpec{ 122 | AccessModes: []corev1.PersistentVolumeAccessMode{"ReadWriteOnce"}, 123 | Resources: corev1.VolumeResourceRequirements{ 124 | Requests: corev1.ResourceList{ 125 | "storage": *resource.NewQuantity(1*1024*1024*1024, resource.BinarySI), 126 | }, 127 | }, 128 | }, 129 | }, 130 | }}, 131 | }, 132 | }, 133 | Users: []pgov1beta1.PostgresUserSpec{{ 134 | Name: "synapse", 135 | Databases: []pgov1beta1.PostgresIdentifier{"dummy"}, 136 | }}, 137 | // See https://github.com/opdev/synapse-operator/issues/12 138 | DatabaseInitSQL: &pgov1beta1.DatabaseInitSQL{ 139 | Name: objectMeta.Name, 140 | Key: "createdb.sql", 141 | }, 142 | }, 143 | } 144 | 145 | // Set Synapse instance as the owner and controller 146 | if err := ctrl.SetControllerReference(s, postgresCluster, r.Scheme); err != nil { 147 | return &pgov1beta1.PostgresCluster{}, err 148 | } 149 | return postgresCluster, nil 150 | } 151 | 152 | func (r *SynapseReconciler) isPostgresClusterReady(p pgov1beta1.PostgresCluster) bool { 153 | var status_found bool 154 | 155 | // Going through instance Specs 156 | for _, instance_spec := range p.Spec.InstanceSets { 157 | status_found = false 158 | for _, instance_status := range p.Status.InstanceSets { 159 | if instance_status.Name == instance_spec.Name { 160 | desired_replicas := *instance_spec.Replicas 161 | if instance_status.Replicas != desired_replicas || 162 | instance_status.ReadyReplicas != desired_replicas || 163 | instance_status.UpdatedReplicas != desired_replicas { 164 | return false 165 | } 166 | // Found instance in Status, breaking out of for loop 167 | status_found = true 168 | break 169 | } 170 | } 171 | 172 | // Instance found in spec, but not in status 173 | if !status_found { 174 | return false 175 | } 176 | } 177 | 178 | // All instances have the correct number of replicas 179 | return true 180 | } 181 | 182 | // reconcilePostgresClusterConfigMap is a function of type FnWithRequest, 183 | // to be called in the main reconciliation loop. 184 | // 185 | // It reconciles the PostgresCluster ConfigMap to its desired state. 186 | func (r *SynapseReconciler) reconcilePostgresClusterConfigMap(ctx context.Context, req ctrl.Request) (*ctrl.Result, error) { 187 | s := &synapsev1alpha1.Synapse{} 188 | if r, err := utils.GetResource(ctx, r.Client, req, s); subreconciler.ShouldHaltOrRequeue(r, err) { 189 | return r, err 190 | } 191 | 192 | postgresClusterObjectMeta := reconcile.SetObjectMeta( 193 | GetPostgresClusterResourceName(*s), 194 | s.Namespace, 195 | map[string]string{}, 196 | ) 197 | 198 | desiredConfigMap, err := r.configMapForPostgresCluster(s, postgresClusterObjectMeta) 199 | if err != nil { 200 | return subreconciler.RequeueWithError(err) 201 | } 202 | 203 | // Create ConfigMap for PostgresCluster 204 | if err := reconcile.ReconcileResource( 205 | ctx, 206 | r.Client, 207 | desiredConfigMap, 208 | &corev1.ConfigMap{}, 209 | ); err != nil { 210 | return subreconciler.RequeueWithError(err) 211 | } 212 | 213 | return subreconciler.ContinueReconciling() 214 | } 215 | 216 | // configMapForPostgresCluster returns a ConfigMap object 217 | func (r *SynapseReconciler) configMapForPostgresCluster(s *synapsev1alpha1.Synapse, objectMeta metav1.ObjectMeta) (*corev1.ConfigMap, error) { 218 | configMap := &corev1.ConfigMap{ 219 | ObjectMeta: objectMeta, 220 | Data: map[string]string{"createdb.sql": "CREATE DATABASE synapse LOCALE 'C' ENCODING 'UTF-8' TEMPLATE template0;"}, 221 | } 222 | 223 | if err := ctrl.SetControllerReference(s, configMap, r.Scheme); err != nil { 224 | return &corev1.ConfigMap{}, err 225 | } 226 | 227 | return configMap, nil 228 | } 229 | 230 | func base64encode(to_encode string) []byte { 231 | return []byte(b64.StdEncoding.EncodeToString([]byte(to_encode))) 232 | } 233 | 234 | func base64decode(to_decode []byte) string { 235 | decoded_bytes, _ := b64.StdEncoding.DecodeString(string(to_decode)) 236 | return string(decoded_bytes) 237 | } 238 | -------------------------------------------------------------------------------- /controllers/synapse/synapse/synapse_pvc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package synapse 18 | 19 | import ( 20 | "context" 21 | "fmt" 22 | 23 | corev1 "k8s.io/api/core/v1" 24 | ctrl "sigs.k8s.io/controller-runtime" 25 | 26 | subreconciler "github.com/opdev/subreconciler" 27 | synapsev1alpha1 "github.com/opdev/synapse-operator/apis/synapse/v1alpha1" 28 | "github.com/opdev/synapse-operator/helpers/reconcile" 29 | "github.com/opdev/synapse-operator/helpers/utils" 30 | "github.com/opdev/synapse-operator/internal/templates" 31 | ) 32 | 33 | // reconcileSynapsePVC is a function of type FnWithRequest, to be called 34 | // in the main reconciliation loop. 35 | // 36 | // It reconciles the PVC for synapse to its desired state. 37 | func (r *SynapseReconciler) reconcileSynapsePVC(ctx context.Context, req ctrl.Request) (*ctrl.Result, error) { 38 | s := &synapsev1alpha1.Synapse{} 39 | if r, err := utils.GetResource(ctx, r.Client, req, s); subreconciler.ShouldHaltOrRequeue(r, err) { 40 | return r, err 41 | } 42 | 43 | desiredPVC, err := r.persistentVolumeClaimForSynapse(s) 44 | if err != nil { 45 | return subreconciler.RequeueWithError(err) 46 | } 47 | 48 | if err := reconcile.ReconcileResource( 49 | ctx, 50 | r.Client, 51 | desiredPVC, 52 | &corev1.PersistentVolumeClaim{}, 53 | ); err != nil { 54 | return subreconciler.RequeueWithError(err) 55 | } 56 | 57 | return subreconciler.ContinueReconciling() 58 | } 59 | 60 | // persistentVolumeClaimForSynapse returns a synapse PVC object 61 | func (r *SynapseReconciler) persistentVolumeClaimForSynapse(s *synapsev1alpha1.Synapse) (*corev1.PersistentVolumeClaim, error) { 62 | pvc, err := templates.ResourceFromTemplate[synapsev1alpha1.Synapse, corev1.PersistentVolumeClaim](s, "pvc") 63 | if err != nil { 64 | return nil, fmt.Errorf("could not get template: %v", err) 65 | } 66 | 67 | // Set Synapse instance as the owner and controller 68 | if err := ctrl.SetControllerReference(s, pvc, r.Scheme); err != nil { 69 | return &corev1.PersistentVolumeClaim{}, err 70 | } 71 | return pvc, nil 72 | } 73 | -------------------------------------------------------------------------------- /controllers/synapse/synapse/synapse_service.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package synapse 18 | 19 | import ( 20 | "context" 21 | "fmt" 22 | 23 | corev1 "k8s.io/api/core/v1" 24 | ctrl "sigs.k8s.io/controller-runtime" 25 | 26 | subreconciler "github.com/opdev/subreconciler" 27 | synapsev1alpha1 "github.com/opdev/synapse-operator/apis/synapse/v1alpha1" 28 | "github.com/opdev/synapse-operator/helpers/reconcile" 29 | "github.com/opdev/synapse-operator/helpers/utils" 30 | "github.com/opdev/synapse-operator/internal/templates" 31 | ) 32 | 33 | // reconcileSynapseService is a function of type FnWithRequest, to be 34 | // called in the main reconciliation loop. 35 | // 36 | // It reconciles the Service for synapse to its desired state. 37 | func (r *SynapseReconciler) reconcileSynapseService(ctx context.Context, req ctrl.Request) (*ctrl.Result, error) { 38 | s := &synapsev1alpha1.Synapse{} 39 | if r, err := utils.GetResource(ctx, r.Client, req, s); subreconciler.ShouldHaltOrRequeue(r, err) { 40 | return r, err 41 | } 42 | 43 | // objectMetaForSynapse := reconcile.SetObjectMeta(s.Name, s.Namespace, map[string]string{}) 44 | 45 | desiredService, err := r.serviceForSynapse(s) 46 | if err != nil { 47 | return subreconciler.RequeueWithError(err) 48 | } 49 | 50 | if err := reconcile.ReconcileResource( 51 | ctx, 52 | r.Client, 53 | desiredService, 54 | &corev1.Service{}, 55 | ); err != nil { 56 | return subreconciler.RequeueWithError(err) 57 | } 58 | return subreconciler.ContinueReconciling() 59 | } 60 | 61 | // serviceForSynapse returns a synapse Service object 62 | func (r *SynapseReconciler) serviceForSynapse(s *synapsev1alpha1.Synapse) (*corev1.Service, error) { 63 | type serviceExtraValues struct { 64 | synapsev1alpha1.Synapse 65 | Labels map[string]string 66 | PortName string 67 | Port int 68 | TargetPort int 69 | } 70 | 71 | extraValues := serviceExtraValues{ 72 | Synapse: *s, 73 | Labels: labelsForSynapse(s.Name), 74 | PortName: "synapse-unsecure", 75 | Port: 8008, 76 | TargetPort: 8008, 77 | } 78 | 79 | service, err := templates.ResourceFromTemplate[serviceExtraValues, corev1.Service](&extraValues, "service") 80 | if err != nil { 81 | return nil, fmt.Errorf("could not get template: %v", err) 82 | } 83 | 84 | // Set Synapse instance as the owner and controller 85 | if err := ctrl.SetControllerReference(s, service, r.Scheme); err != nil { 86 | return &corev1.Service{}, err 87 | } 88 | return service, nil 89 | } 90 | -------------------------------------------------------------------------------- /controllers/synapse/synapse/synapse_serviceaccount.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package synapse 18 | 19 | import ( 20 | "context" 21 | "fmt" 22 | 23 | corev1 "k8s.io/api/core/v1" 24 | rbacv1 "k8s.io/api/rbac/v1" 25 | ctrl "sigs.k8s.io/controller-runtime" 26 | 27 | subreconciler "github.com/opdev/subreconciler" 28 | synapsev1alpha1 "github.com/opdev/synapse-operator/apis/synapse/v1alpha1" 29 | "github.com/opdev/synapse-operator/helpers/reconcile" 30 | "github.com/opdev/synapse-operator/helpers/utils" 31 | "github.com/opdev/synapse-operator/internal/templates" 32 | ) 33 | 34 | // reconcileSynapseServiceAccount is a function of type FnWithRequest, to 35 | // be called in the main reconciliation loop. 36 | // 37 | // It reconciles the ServiceAccount for synapse to its desired state. 38 | func (r *SynapseReconciler) reconcileSynapseServiceAccount(ctx context.Context, req ctrl.Request) (*ctrl.Result, error) { 39 | s := &synapsev1alpha1.Synapse{} 40 | if r, err := utils.GetResource(ctx, r.Client, req, s); subreconciler.ShouldHaltOrRequeue(r, err) { 41 | return r, err 42 | } 43 | 44 | desiredServiceAccount, err := r.serviceAccountForSynapse(s) 45 | if err != nil { 46 | return subreconciler.RequeueWithError(err) 47 | } 48 | 49 | if err := reconcile.ReconcileResource( 50 | ctx, 51 | r.Client, 52 | desiredServiceAccount, 53 | &corev1.ServiceAccount{}, 54 | ); err != nil { 55 | return subreconciler.RequeueWithError(err) 56 | } 57 | return subreconciler.ContinueReconciling() 58 | } 59 | 60 | // serviceAccountForSynapse returns a synapse ServiceAccount object 61 | func (r *SynapseReconciler) serviceAccountForSynapse(s *synapsev1alpha1.Synapse) (*corev1.ServiceAccount, error) { 62 | // TODO: https://github.com/opdev/synapse-operator/issues/19 63 | sa, err := templates.ResourceFromTemplate[synapsev1alpha1.Synapse, corev1.ServiceAccount](s, "serviceaccount") 64 | if err != nil { 65 | return nil, fmt.Errorf("could not get template: %v", err) 66 | } 67 | 68 | // Set Synapse instance as the owner and controller 69 | if err := ctrl.SetControllerReference(s, sa, r.Scheme); err != nil { 70 | return &corev1.ServiceAccount{}, err 71 | } 72 | return sa, nil 73 | } 74 | 75 | // reconcileSynapseRoleBinding is a function of type FnWithRequest, to be 76 | // called in the main reconciliation loop. 77 | // 78 | // It reconciles the RoleBinding for synapse to its desired state. 79 | func (r *SynapseReconciler) reconcileSynapseRoleBinding(ctx context.Context, req ctrl.Request) (*ctrl.Result, error) { 80 | s := &synapsev1alpha1.Synapse{} 81 | if r, err := utils.GetResource(ctx, r.Client, req, s); subreconciler.ShouldHaltOrRequeue(r, err) { 82 | return r, err 83 | } 84 | 85 | desiredRoleBinding, err := r.roleBindingForSynapse(s) 86 | if err != nil { 87 | return subreconciler.RequeueWithError(err) 88 | } 89 | 90 | if err := reconcile.ReconcileResource( 91 | ctx, 92 | r.Client, 93 | desiredRoleBinding, 94 | &rbacv1.RoleBinding{}, 95 | ); err != nil { 96 | return subreconciler.RequeueWithError(err) 97 | } 98 | return subreconciler.ContinueReconciling() 99 | } 100 | 101 | // roleBindingForSynapse returns a synapse RoleBinding object 102 | func (r *SynapseReconciler) roleBindingForSynapse(s *synapsev1alpha1.Synapse) (*rbacv1.RoleBinding, error) { 103 | // TODO: https://github.com/opdev/synapse-operator/issues/19 104 | rb, err := templates.ResourceFromTemplate[synapsev1alpha1.Synapse, rbacv1.RoleBinding](s, "rolebinding") 105 | if err != nil { 106 | return nil, fmt.Errorf("could not get template: %v", err) 107 | } 108 | 109 | // Set Synapse instance as the owner and controller 110 | if err := ctrl.SetControllerReference(s, rb, r.Scheme); err != nil { 111 | return &rbacv1.RoleBinding{}, err 112 | } 113 | return rb, nil 114 | } 115 | -------------------------------------------------------------------------------- /examples/01-my-first-synapse-deployment/synapse.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: synapse.opdev.io/v1alpha1 2 | kind: Synapse 3 | metadata: 4 | name: my-first-synapse-deployment 5 | spec: 6 | homeserver: 7 | values: 8 | serverName: example.com 9 | reportStats: true 10 | isOpenshift: true 11 | -------------------------------------------------------------------------------- /examples/02-using-existing-configmap/synapse.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: synapse.opdev.io/v1alpha1 2 | kind: Synapse 3 | metadata: 4 | name: using-existing-configmap 5 | spec: 6 | homeserver: 7 | configMap: 8 | name: my-custom-homeserver 9 | -------------------------------------------------------------------------------- /examples/03-deploying-postgresql/synapse.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: synapse.opdev.io/v1alpha1 2 | kind: Synapse 3 | metadata: 4 | name: synapse-with-postgresql 5 | spec: 6 | homeserver: 7 | values: 8 | serverName: example.com 9 | reportStats: true 10 | createNewPostgreSQL: true 11 | -------------------------------------------------------------------------------- /examples/04-deploying-heisenbridge/A-default-configuration/heisenbridge.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: synapse.opdev.io/v1alpha1 2 | kind: Heisenbridge 3 | metadata: 4 | name: heisenbridge-for-synapse 5 | spec: 6 | synapse: 7 | name: synapse-with-heisenbridge -------------------------------------------------------------------------------- /examples/04-deploying-heisenbridge/A-default-configuration/synapse.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: synapse.opdev.io/v1alpha1 2 | kind: Synapse 3 | metadata: 4 | name: synapse-with-heisenbridge 5 | spec: 6 | homeserver: 7 | values: 8 | serverName: example.com 9 | reportStats: true 10 | -------------------------------------------------------------------------------- /examples/04-deploying-heisenbridge/B-using-existing-configmap/heisenbridge.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: synapse.opdev.io/v1alpha1 2 | kind: Heisenbridge 3 | metadata: 4 | name: heisenbridge-for-synapse 5 | spec: 6 | configMap: 7 | name: my-custom-heisenbridge 8 | synapse: 9 | name: synapse-with-heisenbridge 10 | -------------------------------------------------------------------------------- /examples/04-deploying-heisenbridge/B-using-existing-configmap/heisenbridge_config.yaml: -------------------------------------------------------------------------------- 1 | id: heisenbridge 2 | url: http://10.217.5.134:9898 3 | as_token: EUFqSPQusV4mXkPKbwdHyIhthELQ1Xf9S5lSEzTrrlb0uz0ZJRHhwEljT71ByObe 4 | hs_token: If6r2GGlsNN4MnoW3djToADNdq0JuIJ1WNM4rKHO73WuG5QvVubj1Q4JHrmQBcS6 5 | rate_limited: false 6 | sender_localpart: heisenbridge 7 | namespaces: 8 | users: 9 | - regex: '@irc_.*' 10 | exclusive: true 11 | aliases: [] 12 | rooms: [] 13 | -------------------------------------------------------------------------------- /examples/04-deploying-heisenbridge/B-using-existing-configmap/synapse.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: synapse.opdev.io/v1alpha1 2 | kind: Synapse 3 | metadata: 4 | name: synapse-with-heisenbridge 5 | spec: 6 | homeserver: 7 | values: 8 | serverName: example.com 9 | reportStats: true -------------------------------------------------------------------------------- /examples/05-deploying-mautrixsignal/A-default-configuration/mautrixsignal.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: synapse.opdev.io/v1alpha1 2 | kind: MautrixSignal 3 | metadata: 4 | name: mautrixsignal-for-synapse 5 | spec: 6 | synapse: 7 | name: synapse-with-mautrixsignal 8 | -------------------------------------------------------------------------------- /examples/05-deploying-mautrixsignal/A-default-configuration/synapse.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: synapse.opdev.io/v1alpha1 2 | kind: Synapse 3 | metadata: 4 | name: synapse-with-mautrixsignal 5 | spec: 6 | homeserver: 7 | values: 8 | serverName: my.matrix.host 9 | reportStats: true -------------------------------------------------------------------------------- /examples/05-deploying-mautrixsignal/B-using-existing-configmap/mautrixsignal.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: synapse.opdev.io/v1alpha1 2 | kind: MautrixSignal 3 | metadata: 4 | name: mautrixsignal-for-synapse 5 | spec: 6 | configMap: 7 | name: my-custom-mautrixignal-config 8 | synapse: 9 | name: synapse-with-mautrixsignal -------------------------------------------------------------------------------- /examples/05-deploying-mautrixsignal/B-using-existing-configmap/synapse.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: synapse.opdev.io/v1alpha1 2 | kind: Synapse 3 | metadata: 4 | name: synapse-with-mautrixsignal 5 | spec: 6 | homeserver: 7 | values: 8 | serverName: my.matrix.host 9 | reportStats: true -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/opdev/synapse-operator 2 | 3 | go 1.23.0 4 | 5 | toolchain go1.23.4 6 | 7 | require ( 8 | github.com/crunchydata/postgres-operator v1.3.3-0.20220202164906-1c3cc3597c95 9 | github.com/imdario/mergo v0.3.16 10 | github.com/onsi/ginkgo/v2 v2.22.2 11 | github.com/onsi/gomega v1.36.2 12 | github.com/opdev/subreconciler v0.0.0-20230302151718-c4c8b5ec17c5 13 | gopkg.in/yaml.v2 v2.4.0 14 | k8s.io/api v0.32.1 15 | k8s.io/apiextensions-apiserver v0.32.1 16 | k8s.io/apimachinery v0.32.1 17 | k8s.io/client-go v0.32.1 18 | sigs.k8s.io/controller-runtime v0.20.0 19 | ) 20 | 21 | require ( 22 | github.com/beorn7/perks v1.0.1 // indirect 23 | github.com/cespare/xxhash/v2 v2.3.0 // indirect 24 | github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect 25 | github.com/emicklei/go-restful/v3 v3.12.1 // indirect 26 | github.com/evanphx/json-patch v5.6.0+incompatible // indirect 27 | github.com/evanphx/json-patch/v5 v5.9.0 // indirect 28 | github.com/fsnotify/fsnotify v1.8.0 // indirect 29 | github.com/fxamacker/cbor/v2 v2.7.0 // indirect 30 | github.com/go-logr/logr v1.4.2 // indirect 31 | github.com/go-logr/zapr v1.3.0 // indirect 32 | github.com/go-openapi/jsonpointer v0.21.0 // indirect 33 | github.com/go-openapi/jsonreference v0.21.0 // indirect 34 | github.com/go-openapi/swag v0.23.0 // indirect 35 | github.com/go-task/slim-sprig/v3 v3.0.0 // indirect 36 | github.com/gogo/protobuf v1.3.2 // indirect 37 | github.com/golang/protobuf v1.5.4 // indirect 38 | github.com/google/btree v1.1.3 // indirect 39 | github.com/google/gnostic-models v0.6.9 // indirect 40 | github.com/google/go-cmp v0.6.0 // indirect 41 | github.com/google/gofuzz v1.2.0 // indirect 42 | github.com/google/pprof v0.0.0-20250121033306-997b0b79cac0 // indirect 43 | github.com/google/uuid v1.6.0 // indirect 44 | github.com/josharian/intern v1.0.0 // indirect 45 | github.com/json-iterator/go v1.1.12 // indirect 46 | github.com/klauspost/compress v1.17.11 // indirect 47 | github.com/mailru/easyjson v0.9.0 // indirect 48 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect 49 | github.com/modern-go/reflect2 v1.0.2 // indirect 50 | github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect 51 | github.com/pkg/errors v0.9.1 // indirect 52 | github.com/prometheus/client_golang v1.20.5 // indirect 53 | github.com/prometheus/client_model v0.6.1 // indirect 54 | github.com/prometheus/common v0.62.0 // indirect 55 | github.com/prometheus/procfs v0.15.1 // indirect 56 | github.com/spf13/pflag v1.0.5 // indirect 57 | github.com/x448/float16 v0.8.4 // indirect 58 | go.uber.org/multierr v1.11.0 // indirect 59 | go.uber.org/zap v1.27.0 // indirect 60 | golang.org/x/net v0.34.0 // indirect 61 | golang.org/x/oauth2 v0.25.0 // indirect 62 | golang.org/x/sync v0.10.0 // indirect 63 | golang.org/x/sys v0.29.0 // indirect 64 | golang.org/x/term v0.28.0 // indirect 65 | golang.org/x/text v0.21.0 // indirect 66 | golang.org/x/time v0.9.0 // indirect 67 | golang.org/x/tools v0.29.0 // indirect 68 | gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect 69 | google.golang.org/protobuf v1.36.3 // indirect 70 | gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect 71 | gopkg.in/inf.v0 v0.9.1 // indirect 72 | gopkg.in/yaml.v3 v3.0.1 // indirect 73 | k8s.io/klog/v2 v2.130.1 // indirect 74 | k8s.io/kube-openapi v0.0.0-20241212222426-2c72e554b1e7 // indirect 75 | k8s.io/utils v0.0.0-20241210054802-24370beab758 // indirect 76 | sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 // indirect 77 | sigs.k8s.io/structured-merge-diff/v4 v4.5.0 // indirect 78 | sigs.k8s.io/yaml v1.4.0 // indirect 79 | ) 80 | -------------------------------------------------------------------------------- /hack/boilerplate.go.txt: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ -------------------------------------------------------------------------------- /helpers/reconcile/reconcile.go: -------------------------------------------------------------------------------- 1 | package reconcile 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/imdario/mergo" 7 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 8 | "sigs.k8s.io/controller-runtime/pkg/client" 9 | ctrllog "sigs.k8s.io/controller-runtime/pkg/log" 10 | 11 | k8serrors "k8s.io/apimachinery/pkg/api/errors" 12 | "k8s.io/apimachinery/pkg/types" 13 | ) 14 | 15 | func SetObjectMeta(name string, namespace string, labels map[string]string) metav1.ObjectMeta { 16 | objectMeta := metav1.ObjectMeta{ 17 | Name: name, 18 | Namespace: namespace, 19 | Labels: labels, 20 | } 21 | return objectMeta 22 | } 23 | 24 | // Generic function to reconcile a Kubernetes resource 25 | // `current` should be an empty resource (e.g. &appsv1.Deployment{}). It is 26 | // populated by the actual current state of the resource in the initial GET 27 | // request. 28 | func ReconcileResource( 29 | ctx context.Context, 30 | rclient client.Client, 31 | desired client.Object, 32 | current client.Object, 33 | ) error { 34 | log := ctrllog.FromContext(ctx) 35 | log.Info( 36 | "Reconciling child resource", 37 | "Kind", desired.GetObjectKind().GroupVersionKind().Kind, 38 | "Name", desired.GetName(), 39 | "Namespace", desired.GetNamespace(), 40 | ) 41 | 42 | key := types.NamespacedName{Name: desired.GetName(), Namespace: desired.GetNamespace()} 43 | if err := rclient.Get(ctx, key, current); err != nil { 44 | if !k8serrors.IsNotFound(err) { 45 | log.Error( 46 | err, 47 | "Error reading child resource", 48 | "Kind", desired.GetObjectKind().GroupVersionKind().Kind, 49 | "Name", desired.GetName(), 50 | "Namespace", desired.GetNamespace(), 51 | ) 52 | return err 53 | } 54 | 55 | log.Info( 56 | "Creating a new child resource", 57 | "Kind", desired.GetObjectKind().GroupVersionKind().Kind, 58 | "Name", desired.GetName(), 59 | "Namespace", desired.GetNamespace(), 60 | ) 61 | 62 | err = rclient.Create(ctx, desired) 63 | if err != nil { 64 | log.Error( 65 | err, 66 | "Failed to create a new child resource", 67 | "Kind", desired.GetObjectKind().GroupVersionKind().Kind, 68 | "Name", desired.GetName(), 69 | "Namespace", desired.GetNamespace(), 70 | ) 71 | return err 72 | } 73 | } else { 74 | log.Info( 75 | "Patching existing child resource", 76 | "Kind", desired.GetObjectKind().GroupVersionKind().Kind, 77 | "Name", desired.GetName(), 78 | "Namespace", desired.GetNamespace(), 79 | ) 80 | 81 | // This ensures that the resource is patched only if there is a 82 | // difference between desired and current. 83 | patchDiff := client.MergeFrom(current.DeepCopyObject().(client.Object)) 84 | if err := mergo.Merge(current, desired, mergo.WithOverride); err != nil { 85 | log.Error(err, "Error in merge") 86 | return err 87 | } 88 | 89 | if err := rclient.Patch(ctx, current, patchDiff); err != nil { 90 | log.Error( 91 | err, 92 | "Failed to patch child resource", 93 | "Kind", desired.GetObjectKind().GroupVersionKind().Kind, 94 | "Name", desired.GetName(), 95 | "Namespace", desired.GetNamespace(), 96 | ) 97 | return err 98 | } 99 | } 100 | 101 | log.Info( 102 | "Finished reconciling resource", 103 | "Kind", desired.GetObjectKind().GroupVersionKind().Kind, 104 | "Name", desired.GetName(), 105 | "Namespace", desired.GetNamespace(), 106 | ) 107 | return nil 108 | } 109 | -------------------------------------------------------------------------------- /helpers/utils/bridge_utils.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package utils 18 | 19 | import ( 20 | "context" 21 | "errors" 22 | "reflect" 23 | "strings" 24 | 25 | "github.com/opdev/subreconciler" 26 | synapsev1alpha1 "github.com/opdev/synapse-operator/apis/synapse/v1alpha1" 27 | "k8s.io/apimachinery/pkg/types" 28 | ctrl "sigs.k8s.io/controller-runtime" 29 | "sigs.k8s.io/controller-runtime/pkg/client" 30 | "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" 31 | ctrllog "sigs.k8s.io/controller-runtime/pkg/log" 32 | ) 33 | 34 | const synapseBridgeFinalizer = "synapse.opdev.io/finalizer" 35 | 36 | func ComputeFQDN(name string, namespace string) string { 37 | return strings.Join([]string{name, namespace, "svc", "cluster", "local"}, ".") 38 | } 39 | 40 | func GetSynapseServerName(s synapsev1alpha1.Synapse) (string, error) { 41 | if s.Status.HomeserverConfiguration.ServerName != "" { 42 | return s.Status.HomeserverConfiguration.ServerName, nil 43 | } 44 | err := errors.New("ServerName not yet populated") 45 | return "", err 46 | } 47 | 48 | func UpdateSynapseStatus(ctx context.Context, kubeClient client.Client, s *synapsev1alpha1.Synapse) error { 49 | current := &synapsev1alpha1.Synapse{} 50 | 51 | if err := kubeClient.Get( 52 | ctx, 53 | types.NamespacedName{Name: s.Name, Namespace: s.Namespace}, 54 | current, 55 | ); err != nil { 56 | return err 57 | } 58 | 59 | if !reflect.DeepEqual(s.Status, current.Status) { 60 | if err := kubeClient.Status().Patch(ctx, s, client.MergeFrom(current)); err != nil { 61 | return err 62 | } 63 | } 64 | 65 | return nil 66 | } 67 | 68 | // Matrix Bridges should implement the Bridge interface 69 | type Bridge interface { 70 | client.Object // *synapsev1alpha1.Heisenbridge | *synapsev1alpha1.MautrixSignal 71 | GetSynapseName() string 72 | GetSynapseNamespace() string 73 | } 74 | 75 | func FetchSynapseInstance( 76 | ctx context.Context, 77 | kubeClient client.Client, 78 | resource Bridge, 79 | s *synapsev1alpha1.Synapse, 80 | ) error { 81 | // Validate Synapse instance exists 82 | keyForSynapse := types.NamespacedName{ 83 | Name: resource.GetSynapseName(), 84 | Namespace: ComputeNamespace(resource.GetNamespace(), resource.GetSynapseNamespace()), 85 | } 86 | return kubeClient.Get(ctx, keyForSynapse, s) 87 | } 88 | 89 | // TriggerSynapseReconciliation returns a function of type subreconciler.FnWithRequest 90 | // Bridges should trigger the reconciliation of their associated Synapse server 91 | // so that Synapse can add the bridge as an application service in its configuration. 92 | func TriggerSynapseReconciliation(kubeClient client.Client, resource Bridge) func(context.Context, ctrl.Request) (*ctrl.Result, error) { 93 | return func(ctx context.Context, req ctrl.Request) (*ctrl.Result, error) { 94 | log := ctrllog.FromContext(ctx) 95 | 96 | if r, err := GetResource(ctx, kubeClient, req, resource); subreconciler.ShouldHaltOrRequeue(r, err) { 97 | return r, err 98 | } 99 | 100 | s := synapsev1alpha1.Synapse{} 101 | if err := FetchSynapseInstance(ctx, kubeClient, resource, &s); err != nil { 102 | log.Error(err, "Error getting Synapse instance") 103 | return subreconciler.RequeueWithError(err) 104 | } 105 | 106 | s.Status.NeedsReconcile = true 107 | 108 | if err := UpdateSynapseStatus(ctx, kubeClient, &s); err != nil { 109 | return subreconciler.RequeueWithError(err) 110 | } 111 | 112 | return subreconciler.ContinueReconciling() 113 | } 114 | } 115 | 116 | // HandleDelete returns a function of type subreconciler.FnWithRequest 117 | // 118 | // Bridges need to trigger the reconciliation of their associated Synapse homeserver 119 | // so that Synapse can remove the bridge from the list of application services in its configuration. 120 | func HandleDelete(kubeClient client.Client, resource Bridge) func(context.Context, ctrl.Request) (*ctrl.Result, error) { 121 | return func(ctx context.Context, req ctrl.Request) (*ctrl.Result, error) { 122 | log := ctrllog.FromContext(ctx) 123 | 124 | if r, err := GetResource(ctx, kubeClient, req, resource); subreconciler.ShouldHaltOrRequeue(r, err) { 125 | return r, err 126 | } 127 | 128 | // Check if resource is marked to be deleted 129 | if resource.GetDeletionTimestamp() != nil { 130 | if controllerutil.ContainsFinalizer(resource, synapseBridgeFinalizer) { 131 | // Trigger the reconciliation of the associated Synapse instance 132 | s := synapsev1alpha1.Synapse{} 133 | if err := FetchSynapseInstance(ctx, kubeClient, resource, &s); err != nil { 134 | log.Error(err, "Error getting Synapse instance. Continuing clean-up without triggering reconciliation") 135 | } else { 136 | s.Status.NeedsReconcile = true 137 | 138 | if err := UpdateSynapseStatus(ctx, kubeClient, &s); err != nil { 139 | log.Error(err, "Error updating Synapse Status") 140 | return subreconciler.RequeueWithError(err) 141 | } 142 | } 143 | 144 | // Remove finalizer 145 | if ok := controllerutil.RemoveFinalizer(resource, synapseBridgeFinalizer); !ok { 146 | err := errors.New("error removing finalizer") 147 | log.Error(err, "Error removing finalizer") 148 | return subreconciler.RequeueWithError(err) 149 | } 150 | 151 | if err := kubeClient.Update(ctx, resource); err != nil { 152 | log.Error(err, "error updating Status") 153 | return subreconciler.RequeueWithError(err) 154 | } 155 | } 156 | return subreconciler.DoNotRequeue() 157 | } 158 | return subreconciler.ContinueReconciling() 159 | } 160 | } 161 | 162 | // AddFinalizer returns a function of type subreconciler.FnWithRequest 163 | func AddFinalizer(kubeClient client.Client, resource Bridge) func(context.Context, ctrl.Request) (*ctrl.Result, error) { 164 | return func(ctx context.Context, req ctrl.Request) (*ctrl.Result, error) { 165 | log := ctrllog.FromContext(ctx) 166 | 167 | if r, err := GetResource(ctx, kubeClient, req, resource); subreconciler.ShouldHaltOrRequeue(r, err) { 168 | return r, err 169 | } 170 | 171 | if !controllerutil.ContainsFinalizer(resource, synapseBridgeFinalizer) { 172 | log.Info("Adding Finalizer for Memcached") 173 | if ok := controllerutil.AddFinalizer(resource, synapseBridgeFinalizer); !ok { 174 | err := errors.New("error adding finalizer") 175 | log.Error(err, "Failed to add finalizer into the custom resource") 176 | return subreconciler.RequeueWithError(err) 177 | } 178 | 179 | if err := kubeClient.Update(ctx, resource); err != nil { 180 | log.Error(err, "Failed to update custom resource to add finalizer") 181 | return subreconciler.RequeueWithError(err) 182 | } 183 | } 184 | 185 | return subreconciler.ContinueReconciling() 186 | } 187 | } 188 | -------------------------------------------------------------------------------- /helpers/utils/configmap_utils.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package utils 18 | 19 | /* This file puts together generic functions for ConfiMap manipulation */ 20 | import ( 21 | "context" 22 | "errors" 23 | 24 | "gopkg.in/yaml.v2" 25 | corev1 "k8s.io/api/core/v1" 26 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 27 | "k8s.io/apimachinery/pkg/types" 28 | "sigs.k8s.io/controller-runtime/pkg/client" 29 | ) 30 | 31 | // Defines a function type. Functions of the updateDataFunc type implements the 32 | // logic to update the data of a configmap, defined by the 'data' argument. 33 | type updateDataFunc func(obj client.Object, data map[string]interface{}) error 34 | 35 | // A generic function to update an existing ConfigMap. It takes as arguments: 36 | // * The context 37 | // * The key (name and namespace) of the ConfigMap to update 38 | // * The Synapse object being reconciled 39 | // * The function to be called to actually update the ConfigMap's content 40 | // * The name of the file to update in the ConfigMap 41 | func UpdateConfigMap( 42 | ctx context.Context, 43 | client client.Client, 44 | key types.NamespacedName, 45 | obj client.Object, 46 | updateData updateDataFunc, 47 | filename string, 48 | ) error { 49 | cm := &corev1.ConfigMap{} 50 | 51 | // Get latest ConfigMap version 52 | if err := client.Get(ctx, key, cm); err != nil { 53 | return err 54 | } 55 | 56 | if err := UpdateConfigMapData(cm, obj, updateData, filename); err != nil { 57 | return err 58 | } 59 | 60 | // Update ConfigMap 61 | if err := client.Update(ctx, cm); err != nil { 62 | return err 63 | } 64 | 65 | return nil 66 | } 67 | 68 | func UpdateConfigMapData( 69 | cm *corev1.ConfigMap, 70 | obj client.Object, 71 | updateData updateDataFunc, 72 | filename string, 73 | ) error { 74 | // Load file to update from ConfigMap 75 | data, err := LoadYAMLFileFromConfigMapData(*cm, filename) 76 | if err != nil { 77 | return err 78 | } 79 | 80 | // Update the content of the file 81 | if err := updateData(obj, data); err != nil { 82 | return err 83 | } 84 | 85 | // Write new content into ConfigMap data 86 | if err := writeYAMLFileToConfigMapData(cm, filename, data); err != nil { 87 | return err 88 | } 89 | 90 | return nil 91 | } 92 | 93 | func loadFileFromConfigMapData( 94 | configMap corev1.ConfigMap, 95 | filename string, 96 | ) (string, error) { 97 | content, ok := configMap.Data[filename] 98 | if !ok { 99 | err := errors.New("missing " + filename + " in ConfigMap " + configMap.Name) 100 | return "", err 101 | } 102 | 103 | return content, nil 104 | } 105 | 106 | func LoadYAMLFileFromConfigMapData( 107 | configMap corev1.ConfigMap, 108 | filename string, 109 | ) (map[string]interface{}, error) { 110 | yamlContent := map[string]interface{}{} 111 | 112 | content, err := loadFileFromConfigMapData(configMap, filename) 113 | if err != nil { 114 | return yamlContent, err 115 | } 116 | if err := yaml.Unmarshal([]byte(content), yamlContent); err != nil { 117 | return yamlContent, err 118 | } 119 | 120 | return yamlContent, nil 121 | } 122 | 123 | func writeFileToConfigMapData( 124 | configMap *corev1.ConfigMap, 125 | filename string, 126 | content string, 127 | ) { 128 | configMap.Data = map[string]string{filename: content} 129 | } 130 | 131 | func writeYAMLFileToConfigMapData( 132 | configMap *corev1.ConfigMap, 133 | filename string, 134 | yamlContent map[string]interface{}, 135 | ) error { 136 | bytesContent, err := yaml.Marshal(yamlContent) 137 | if err != nil { 138 | return err 139 | } 140 | 141 | writeFileToConfigMapData(configMap, filename, string(bytesContent)) 142 | return nil 143 | } 144 | 145 | // getConfigMapCopy is a generic function which creates a copy of a given 146 | // source ConfigMap. The resulting copy is a ConfigMap with similar data, and 147 | // with metadata set by the 'copyConfigMapObjectMeta' argument. 148 | func GetConfigMapCopy( 149 | client client.Client, 150 | sourceConfigMapName string, 151 | sourceConfigMapNamespace string, 152 | copyConfigMapObjectMeta metav1.ObjectMeta, 153 | ) (*corev1.ConfigMap, error) { 154 | sourceConfigMap := &corev1.ConfigMap{} 155 | 156 | ctx := context.TODO() 157 | 158 | // Get sourceConfigMap 159 | if err := client.Get( 160 | ctx, 161 | types.NamespacedName{Name: sourceConfigMapName, Namespace: sourceConfigMapNamespace}, 162 | sourceConfigMap, 163 | ); err != nil { 164 | return &corev1.ConfigMap{}, err 165 | } 166 | 167 | // Create a copy of the source ConfigMap with the same content. 168 | copyConfigMap := &corev1.ConfigMap{ 169 | ObjectMeta: copyConfigMapObjectMeta, 170 | Data: sourceConfigMap.Data, 171 | } 172 | 173 | return copyConfigMap, nil 174 | } 175 | 176 | // ConfigMap that are created by the user could be living in a different 177 | // namespace as Synapse. getConfigMapNamespace provides a way to default to the 178 | // Synapse namespace if none is provided. 179 | func ComputeNamespace(defaultNamespace string, newNamespace string) string { 180 | if newNamespace != "" { 181 | return newNamespace 182 | } 183 | return defaultNamespace 184 | } 185 | -------------------------------------------------------------------------------- /helpers/utils/synapse_utils.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package utils 18 | 19 | import ( 20 | "context" 21 | "errors" 22 | "time" 23 | 24 | "github.com/opdev/subreconciler" 25 | synapsev1alpha1 "github.com/opdev/synapse-operator/apis/synapse/v1alpha1" 26 | "github.com/opdev/synapse-operator/helpers/reconcile" 27 | corev1 "k8s.io/api/core/v1" 28 | k8serrors "k8s.io/apimachinery/pkg/api/errors" 29 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 30 | "k8s.io/apimachinery/pkg/runtime" 31 | "k8s.io/apimachinery/pkg/types" 32 | ctrl "sigs.k8s.io/controller-runtime" 33 | "sigs.k8s.io/controller-runtime/pkg/client" 34 | ctrllog "sigs.k8s.io/controller-runtime/pkg/log" 35 | ) 36 | 37 | func GetResource( 38 | ctx context.Context, 39 | kubeClient client.Client, 40 | req ctrl.Request, 41 | resource client.Object, 42 | ) (*ctrl.Result, error) { 43 | log := ctrllog.FromContext(ctx) 44 | 45 | if err := kubeClient.Get(ctx, req.NamespacedName, resource); err != nil { 46 | if k8serrors.IsNotFound(err) { 47 | // we'll ignore not-found errors, since they can't be fixed by an immediate 48 | // requeue (we'll need to wait for a new notification), and we can get them 49 | // on deleted requests. 50 | log.Error( 51 | err, 52 | "Cannot find resource - has it been deleted ?", 53 | "Name", resource.GetName(), 54 | "Namespace", resource.GetNamespace(), 55 | ) 56 | return subreconciler.DoNotRequeue() 57 | } 58 | log.Error( 59 | err, 60 | "Error fetching resource", 61 | "Name", resource.GetName(), 62 | "Namespace", resource.GetNamespace(), 63 | ) 64 | 65 | return subreconciler.RequeueWithError(err) 66 | } 67 | 68 | return subreconciler.ContinueReconciling() 69 | } 70 | 71 | func UpdateResourceStatus(ctx context.Context, kubeClient client.Client, resource client.Object, current client.Object) error { 72 | if err := kubeClient.Get( 73 | ctx, 74 | types.NamespacedName{Name: resource.GetName(), Namespace: resource.GetNamespace()}, 75 | current, 76 | ); err != nil { 77 | return err 78 | } 79 | 80 | if err := kubeClient.Status().Patch(ctx, resource, client.MergeFrom(current)); err != nil { 81 | return err 82 | } 83 | 84 | return nil 85 | } 86 | 87 | // CopyInputConfigMap returns a function of type FnWithRequest, to 88 | // be called in the main reconciliation loop. 89 | // 90 | // It creates a copy of the user-provided ConfigMap. 91 | func CopyInputConfigMap(kubeClient client.Client, runtimeScheme *runtime.Scheme, resource client.Object) func(context.Context, ctrl.Request) (*ctrl.Result, error) { 92 | return func(ctx context.Context, req ctrl.Request) (*ctrl.Result, error) { 93 | log := ctrllog.FromContext(ctx) 94 | 95 | if r, err := GetResource(ctx, kubeClient, req, resource); subreconciler.ShouldHaltOrRequeue(r, err) { 96 | return r, err 97 | } 98 | 99 | var keyForInputConfigMap types.NamespacedName 100 | if err := getInputConfigMapInfo(resource, &keyForInputConfigMap); err != nil { 101 | return nil, err 102 | } 103 | 104 | // Get and check the input ConfigMap for MautrixSignal 105 | if err := kubeClient.Get(ctx, keyForInputConfigMap, &corev1.ConfigMap{}); err != nil { 106 | reason := "ConfigMap " + keyForInputConfigMap.Name + " does not exist in namespace " + keyForInputConfigMap.Namespace 107 | SetFailedState(ctx, kubeClient, resource, reason) 108 | 109 | log.Error( 110 | err, 111 | "Failed to get ConfigMap", 112 | "ConfigMap.Namespace", 113 | keyForInputConfigMap.Namespace, 114 | "ConfigMap.Name", 115 | keyForInputConfigMap.Name, 116 | ) 117 | 118 | return subreconciler.RequeueWithDelayAndError(time.Duration(30), err) 119 | } 120 | 121 | objectMeta := reconcile.SetObjectMeta(resource.GetName(), resource.GetNamespace(), map[string]string{}) 122 | 123 | desiredConfigMap, err := configMapForCopy(ctx, objectMeta, kubeClient, resource, runtimeScheme) 124 | if err != nil { 125 | return subreconciler.RequeueWithError(err) 126 | } 127 | 128 | // Create a copy of the inputConfigMap 129 | if err := reconcile.ReconcileResource( 130 | ctx, 131 | kubeClient, 132 | desiredConfigMap, 133 | &corev1.ConfigMap{}, 134 | ); err != nil { 135 | return subreconciler.RequeueWithError(err) 136 | } 137 | 138 | return subreconciler.ContinueReconciling() 139 | } 140 | } 141 | 142 | // The ConfigMap returned by configMapForCopy is a copy of the user-defined 143 | // ConfigMap. 144 | func configMapForCopy( 145 | ctx context.Context, 146 | objectMeta metav1.ObjectMeta, 147 | kubeClient client.Client, 148 | resource client.Object, 149 | runtimeScheme *runtime.Scheme, 150 | ) (*corev1.ConfigMap, error) { 151 | var copyConfigMap *corev1.ConfigMap 152 | 153 | var keyForInputConfigMap types.NamespacedName 154 | if err := getInputConfigMapInfo(resource, &keyForInputConfigMap); err != nil { 155 | return nil, err 156 | } 157 | 158 | copyConfigMap, err := GetConfigMapCopy( 159 | kubeClient, 160 | keyForInputConfigMap.Name, 161 | keyForInputConfigMap.Namespace, 162 | objectMeta, 163 | ) 164 | if err != nil { 165 | return &corev1.ConfigMap{}, err 166 | } 167 | 168 | // Set owner references 169 | if err := ctrl.SetControllerReference(resource, copyConfigMap, runtimeScheme); err != nil { 170 | return &corev1.ConfigMap{}, err 171 | } 172 | 173 | return copyConfigMap, nil 174 | } 175 | 176 | func SetFailedState(ctx context.Context, kubeClient client.Client, resource client.Object, reason string) { 177 | log := ctrllog.FromContext(ctx) 178 | var err error 179 | 180 | switch v := resource.(type) { 181 | case *synapsev1alpha1.Synapse: 182 | v.Status.State = "FAILED" 183 | v.Status.Reason = reason 184 | 185 | err = UpdateResourceStatus(ctx, kubeClient, v, &synapsev1alpha1.Synapse{}) 186 | case *synapsev1alpha1.Heisenbridge: 187 | v.Status.State = "FAILED" 188 | v.Status.Reason = reason 189 | 190 | err = UpdateResourceStatus(ctx, kubeClient, v, &synapsev1alpha1.Heisenbridge{}) 191 | case *synapsev1alpha1.MautrixSignal: 192 | v.Status.State = "FAILED" 193 | v.Status.Reason = reason 194 | 195 | err = UpdateResourceStatus(ctx, kubeClient, v, &synapsev1alpha1.MautrixSignal{}) 196 | default: 197 | err = errors.New("error in type assertion") 198 | } 199 | 200 | if err != nil { 201 | log.Error(err, "Error updating mautrix-signal State") 202 | } 203 | } 204 | 205 | func getInputConfigMapInfo(resource client.Object, keys *types.NamespacedName) error { 206 | var inputConfigMapName, inputConfigMapNamespace string 207 | 208 | switch v := resource.(type) { 209 | case *synapsev1alpha1.Synapse: 210 | inputConfigMapName = v.Spec.Homeserver.ConfigMap.Name 211 | inputConfigMapNamespace = ComputeNamespace(resource.GetNamespace(), v.Spec.Homeserver.ConfigMap.Namespace) 212 | case *synapsev1alpha1.Heisenbridge: 213 | inputConfigMapName = v.Spec.ConfigMap.Name 214 | inputConfigMapNamespace = ComputeNamespace(resource.GetNamespace(), v.Spec.ConfigMap.Namespace) 215 | case *synapsev1alpha1.MautrixSignal: 216 | inputConfigMapName = v.Spec.ConfigMap.Name 217 | inputConfigMapNamespace = ComputeNamespace(resource.GetNamespace(), v.Spec.ConfigMap.Namespace) 218 | default: 219 | err := errors.New("error in type assertion") 220 | return err 221 | } 222 | 223 | keys.Name = inputConfigMapName 224 | keys.Namespace = inputConfigMapNamespace 225 | 226 | return nil 227 | } 228 | -------------------------------------------------------------------------------- /helpers/utils/tests_utils.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "context" 5 | "time" 6 | 7 | . "github.com/onsi/ginkgo/v2" //lint:ignore ST1001 Ginkgo and gomega are usually dot-imported 8 | . "github.com/onsi/gomega" //lint:ignore ST1001 Ginkgo and gomega are usually dot-imported 9 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 10 | "k8s.io/apimachinery/pkg/types" 11 | "sigs.k8s.io/controller-runtime/pkg/client" 12 | 13 | "github.com/opdev/synapse-operator/apis/synapse/v1alpha1" 14 | ) 15 | 16 | // Helper function for struct construction requiring a boolean pointer 17 | func BoolAddr(b bool) *bool { 18 | boolVar := b 19 | return &boolVar 20 | } 21 | 22 | func Convert(i interface{}) interface{} { 23 | switch x := i.(type) { 24 | case map[interface{}]interface{}: 25 | m2 := map[string]interface{}{} 26 | for k, v := range x { 27 | m2[k.(string)] = Convert(v) 28 | } 29 | return m2 30 | case []interface{}: 31 | for i, v := range x { 32 | x[i] = Convert(v) 33 | } 34 | } 35 | return i 36 | } 37 | 38 | func DeleteResourceFunc( 39 | k8sClient client.Client, 40 | ctx context.Context, 41 | timeout time.Duration, 42 | interval time.Duration, 43 | ) func(client.Object, types.NamespacedName, bool) { 44 | return func(resource client.Object, lookupKey types.NamespacedName, removeFinalizers bool) { 45 | // Using 'Eventually' to eliminate race conditions where the Synapse 46 | // Operator didn't have time to create a sub resource. 47 | Eventually(func() bool { 48 | err := k8sClient.Get(ctx, lookupKey, resource) 49 | return err == nil 50 | }, timeout, interval).Should(BeTrue()) 51 | 52 | if removeFinalizers { 53 | // Manually remove the finalizers 54 | resource.SetFinalizers([]string{}) 55 | Expect(k8sClient.Update(ctx, resource)).Should(Succeed()) 56 | } 57 | 58 | // Deleting 59 | Expect(k8sClient.Delete(ctx, resource)).Should(Succeed()) 60 | 61 | // Check that the resource was successfully removed 62 | Eventually(func() bool { 63 | err := k8sClient.Get(ctx, lookupKey, resource) 64 | return err == nil 65 | }, timeout, interval).Should(BeFalse()) 66 | } 67 | } 68 | 69 | func CheckSubresourceAbsenceFunc( 70 | k8sClient client.Client, 71 | ctx context.Context, 72 | timeout time.Duration, 73 | interval time.Duration, 74 | ) func(types.NamespacedName, ...client.Object) { 75 | return func(lookupKey types.NamespacedName, subResources ...client.Object) { 76 | By("Checking sub-resources have not been created") 77 | Consistently(func(g Gomega) { 78 | for _, resource := range subResources { 79 | g.Expect(k8sClient.Get(ctx, lookupKey, resource)).ShouldNot(Succeed()) 80 | } 81 | }, timeout, interval).Should(Succeed()) 82 | } 83 | } 84 | 85 | func CheckStatusFunc( 86 | k8sClient client.Client, 87 | ctx context.Context, 88 | timeout time.Duration, 89 | interval time.Duration, 90 | ) func(string, string, types.NamespacedName, client.Object) { 91 | return func(expectedState string, expectedReason string, lookupKey types.NamespacedName, object client.Object) { 92 | By("Checking the Status State and Reason") 93 | // Status may need some time to be updated 94 | Eventually(func(g Gomega) { 95 | var currentState, currentReason string 96 | 97 | switch resource := object.(type) { 98 | case *v1alpha1.Synapse: 99 | g.Expect(k8sClient.Get(ctx, lookupKey, resource)).Should(Succeed()) 100 | currentState = resource.Status.State 101 | currentReason = resource.Status.Reason 102 | case *v1alpha1.Heisenbridge: 103 | g.Expect(k8sClient.Get(ctx, lookupKey, resource)).Should(Succeed()) 104 | currentState = resource.Status.State 105 | currentReason = resource.Status.Reason 106 | case *v1alpha1.MautrixSignal: 107 | g.Expect(k8sClient.Get(ctx, lookupKey, resource)).Should(Succeed()) 108 | currentState = resource.Status.State 109 | currentReason = resource.Status.Reason 110 | } 111 | g.Expect(currentState).To(Equal(expectedState)) 112 | g.Expect(currentReason).To(Equal(expectedReason)) 113 | }, timeout, interval).Should(Succeed()) 114 | } 115 | } 116 | 117 | func CheckResourcePresenceFunc( 118 | k8sClient client.Client, 119 | ctx context.Context, 120 | timeout time.Duration, 121 | interval time.Duration, 122 | ) func(client.Object, types.NamespacedName, metav1.OwnerReference) { 123 | return func(resource client.Object, lookupKey types.NamespacedName, expectedOwnerReference metav1.OwnerReference) { 124 | Eventually(func() bool { 125 | err := k8sClient.Get(ctx, lookupKey, resource) 126 | return err == nil 127 | }, timeout, interval).Should(BeTrue()) 128 | 129 | Expect(resource.GetOwnerReferences()).To(ContainElement(expectedOwnerReference)) 130 | } 131 | 132 | } 133 | -------------------------------------------------------------------------------- /helpers/utils/utils.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package utils 18 | 19 | import "gopkg.in/yaml.v2" 20 | 21 | func ConvertStructToMap(in interface{}) (map[string]interface{}, error) { 22 | var intermediate []byte 23 | var out map[string]interface{} 24 | intermediate, err := yaml.Marshal(in) 25 | if err != nil { 26 | return nil, err 27 | } 28 | if err := yaml.Unmarshal(intermediate, &out); err != nil { 29 | return nil, err 30 | } 31 | 32 | return out, nil 33 | } 34 | 35 | func BoolToYesNo(report_stats bool) string { 36 | if report_stats { 37 | return "yes" 38 | } 39 | return "no" 40 | } 41 | 42 | func BoolToString(b bool) string { 43 | if b { 44 | return "true" 45 | } 46 | return "false" 47 | } 48 | -------------------------------------------------------------------------------- /internal/templates/heisenbridge_configmap.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ConfigMap 3 | metadata: 4 | name: {{ .Values.Name }} 5 | namespace: {{ .Values.Namespace }} 6 | data: 7 | heisenbridge.yaml: | 8 | id: heisenbridge 9 | url: http://{{ .Values.HeisenbridgeFQDN }}:9898 10 | as_token: EUFqSPQusV4mXkPKbwdHyIhthELQ1Xf9S5lSEzTrrlb0uz0ZJRHhwEljT71ByObe 11 | hs_token: If6r2GGlsNN4MnoW3djToADNdq0JuIJ1WNM4rKHO73WuG5QvVubj1Q4JHrmQBcS6 12 | rate_limited: false 13 | sender_localpart: heisenbridge 14 | namespaces: 15 | users: 16 | - regex: '@irc_.*' 17 | exclusive: true 18 | aliases: [] 19 | rooms: [] 20 | 21 | -------------------------------------------------------------------------------- /internal/templates/heisenbridge_deployment.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: {{ .Values.Name }} 5 | namespace: {{ .Values.Namespace }} 6 | spec: 7 | replicas: 1 8 | selector: 9 | matchLabels: 10 | {{- range $key, $val := .Values.Labels }} 11 | {{ $key }}: {{ $val }} 12 | {{- end }} 13 | template: 14 | metadata: 15 | labels: 16 | {{- range $key, $val := .Values.Labels }} 17 | {{ $key }}: {{ $val }} 18 | {{- end }} 19 | spec: 20 | containers: 21 | - name: heisenbridge 22 | image: hif1/heisenbridge:1.15 23 | command: 24 | {{- range .Values.Command }} 25 | - {{ . }} 26 | {{- end }} 27 | ports: 28 | - containerPort: 9898 29 | protocol: TCP 30 | volumeMounts: 31 | - mountPath: /data-heisenbridge 32 | name: data-heisenbridge 33 | volumes: 34 | - configMap: 35 | defaultMode: 420 36 | name: {{ .Values.Name }} 37 | name: data-heisenbridge -------------------------------------------------------------------------------- /internal/templates/mautrixsignal_deployment.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: {{ .Values.Name }} 5 | namespace: {{ .Values.Namespace }} 6 | spec: 7 | replicas: 1 8 | selector: 9 | matchLabels: 10 | {{- range $key, $val := .Values.Labels }} 11 | {{ $key }}: {{ $val }} 12 | {{- end }} 13 | template: 14 | metadata: 15 | labels: 16 | {{- range $key, $val := .Values.Labels }} 17 | {{ $key }}: {{ $val }} 18 | {{- end }} 19 | spec: 20 | {{if .Values.Status.IsOpenshift -}} 21 | serviceAccountName: {{ .Values.Name }} 22 | {{- end }} 23 | # The init container is responsible of copying the 24 | # config.yaml from the read-only ConfigMap to the 25 | # mautrixsignal-data volume. The mautrixsignal process 26 | # needs read & write access to the config.yaml file. 27 | initContainers: 28 | - image: registry.access.redhat.com/ubi8/ubi-minimal:8.10 29 | name: initconfig 30 | args: 31 | - if [ ! -f /data/config.yaml ]; then cp /input/config.yaml /data/config.yaml; fi 32 | command: 33 | - bin/sh 34 | - -c 35 | volumeMounts: 36 | - mountPath: /input 37 | name: config 38 | - mountPath: /data 39 | name: mautrixsignal-data 40 | containers: 41 | - image: dock.mau.dev/mautrix/signal:v0.8.3 42 | name: mautrix-signal 43 | volumeMounts: 44 | - mountPath: /data 45 | name: mautrixsignal-data 46 | volumes: 47 | - name: config 48 | configMap: 49 | name: {{ .Values.Name }} 50 | - name: mautrixsignal-data 51 | persistentVolumeClaim: 52 | claimName: {{ .Values.Name }} -------------------------------------------------------------------------------- /internal/templates/pvc.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: PersistentVolumeClaim 3 | metadata: 4 | name: {{ .Values.Name }} 5 | namespace: {{ .Values.Namespace }} 6 | spec: 7 | accessModes: 8 | - ReadWriteOnce 9 | volumeMode: Filesystem 10 | resources: 11 | requests: 12 | storage: 5Gi -------------------------------------------------------------------------------- /internal/templates/rolebinding.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: RoleBinding 3 | metadata: 4 | name: {{ .Values.Name }} 5 | namespace: {{ .Values.Namespace }} 6 | roleRef: 7 | apiGroup: rbac.authorization.k8s.io 8 | kind: ClusterRole 9 | name: system:openshift:scc:anyuid 10 | subjects: 11 | - kind: ServiceAccount 12 | name: {{ .Values.Name }} 13 | namespace: {{ .Values.Namespace }} -------------------------------------------------------------------------------- /internal/templates/service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: {{ .Values.Name }} 5 | namespace: {{ .Values.Namespace }} 6 | spec: 7 | ports: 8 | - name: {{ .Values.PortName }} 9 | port: {{ .Values.Port }} 10 | protocol: TCP 11 | targetPort: {{ .Values.TargetPort }} 12 | selector: 13 | {{- range $key, $val := .Values.Labels }} 14 | {{ $key }}: {{ $val }} 15 | {{- end }} 16 | type: ClusterIP -------------------------------------------------------------------------------- /internal/templates/serviceaccount.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ServiceAccount 3 | metadata: 4 | name: {{ .Values.Name }} 5 | namespace: {{ .Values.Namespace }} 6 | -------------------------------------------------------------------------------- /internal/templates/synapse_configmap.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ConfigMap 3 | metadata: 4 | name: {{ .Values.Name }} 5 | namespace: {{ .Values.Namespace }} 6 | data: 7 | homeserver.yaml: | 8 | # Configuration file for Synapse. 9 | # 10 | # This is a YAML file: see [1] for a quick introduction. Note in particular 11 | # that *indentation is important*: all the elements of a list or dictionary 12 | # should have the same indentation. 13 | # 14 | # [1] https://docs.ansible.com/ansible/latest/reference_appendices/YAMLSyntax.html 15 | # 16 | # For more information on how to configure Synapse, including a complete accounting of 17 | # each option, go to docs/usage/configuration/config_documentation.md or 18 | # https://matrix-org.github.io/synapse/latest/usage/configuration/config_documentation.html 19 | server_name: "{{ .Values.Spec.Homeserver.Values.ServerName }}" 20 | pid_file: /homeserver.pid 21 | listeners: 22 | - port: 8008 23 | tls: false 24 | type: http 25 | x_forwarded: true 26 | resources: 27 | - names: [client, federation] 28 | compress: false 29 | database: 30 | name: sqlite3 31 | args: 32 | database: /data/homeserver.db 33 | log_config: "/data/{{ .Values.Spec.Homeserver.Values.ServerName }}.log.config" 34 | media_store_path: "/data/media_store" 35 | registration_shared_secret: "{{ .Values.RegistrationSharedSecret }}" 36 | report_stats: {{ if .Values.Spec.Homeserver.Values.ReportStats }}yes{{ else }}no{{ end }} 37 | macaroon_secret_key: "{{ .Values.MacaroonSecretKey }}" 38 | form_secret: "{{ .Values.FormSecret }}" 39 | signing_key_path: "data/{{ .Values.Spec.Homeserver.Values.ServerName }}.signing.key" 40 | trusted_key_servers: 41 | - server_name: "matrix.org" -------------------------------------------------------------------------------- /internal/templates/synapse_deployment.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: {{ .Values.Name }} 5 | namespace: {{ .Values.Namespace }} 6 | spec: 7 | replicas: 1 8 | selector: 9 | matchLabels: 10 | {{- range $key, $val := .Values.Labels }} 11 | {{ $key }}: {{ $val }} 12 | {{- end }} 13 | template: 14 | metadata: 15 | labels: 16 | {{- range $key, $val := .Values.Labels }} 17 | {{ $key }}: {{ $val }} 18 | {{- end }} 19 | spec: 20 | {{if .Values.Spec.IsOpenshift -}} 21 | serviceAccountName: {{ .Values.Name }} 22 | {{- end }} 23 | initContainers: 24 | - image: "matrixdotorg/synapse:v1.129.0" 25 | name: "synapse-generate" 26 | args: [ "generate" ] 27 | env: 28 | - name: SYNAPSE_CONFIG_PATH 29 | value: /data-homeserver/homeserver.yaml 30 | - name: SYNAPSE_SERVER_NAME 31 | value: {{ .Values.Status.HomeserverConfiguration.ServerName }} 32 | - name: SYNAPSE_REPORT_STATS 33 | value: {{ if .Values.Status.HomeserverConfiguration.ReportStats }}"yes"{{ else }}"no"{{ end }} 34 | volumeMounts: 35 | - name: homeserver 36 | mountPath: /data-homeserver 37 | - name: data-pv 38 | mountPath: /data 39 | {{ if .Values.Status.Bridges.MautrixSignal.Enabled -}} 40 | - image: registry.access.redhat.com/ubi8/ubi-minimal:8.10 41 | name: "fix-mautrixsignal-permissions" 42 | args: 43 | - chmod 644 /data-mautrixsignal/registration.yaml 44 | command: 45 | - bin/sh 46 | - -c 47 | volumeMounts: 48 | - name: data-mautrixsignal 49 | mountPath: /data-mautrixsignal 50 | {{- end }} 51 | containers: 52 | - image: "matrixdotorg/synapse:v1.129.0" 53 | name: "synapse" 54 | env: 55 | - name: SYNAPSE_CONFIG_PATH 56 | value: /data-homeserver/homeserver.yaml 57 | volumeMounts: 58 | - name: homeserver 59 | mountPath: /data-homeserver 60 | - name: data-pv 61 | mountPath: /data 62 | {{ if .Values.Status.Bridges.Heisenbridge.Enabled -}} 63 | - name: data-heisenbridge 64 | mountPath: /data-heisenbridge 65 | {{- end }} 66 | {{ if .Values.Status.Bridges.MautrixSignal.Enabled -}} 67 | - name: data-mautrixsignal 68 | mountPath: /data-mautrixsignal 69 | {{- end }} 70 | ports: 71 | - containerPort: 8008 72 | volumes: 73 | - name: homeserver 74 | configMap: 75 | name: {{ .Values.Name }} 76 | - name: data-pv 77 | persistentVolumeClaim: 78 | claimName: {{ .Values.Name }} 79 | {{ if .Values.Status.Bridges.Heisenbridge.Enabled -}} 80 | - name: data-heisenbridge 81 | configMap: 82 | name: {{ .Values.Status.Bridges.Heisenbridge.Name }} 83 | {{- end }} 84 | {{ if .Values.Status.Bridges.MautrixSignal.Enabled -}} 85 | - name: data-mautrixsignal 86 | persistentVolumeClaim: 87 | claimName: {{ .Values.Status.Bridges.MautrixSignal.Name }} 88 | {{- end }} 89 | -------------------------------------------------------------------------------- /internal/templates/templates.go: -------------------------------------------------------------------------------- 1 | package templates 2 | 3 | import ( 4 | "bytes" 5 | "embed" 6 | "fmt" 7 | "html/template" 8 | 9 | "k8s.io/apimachinery/pkg/util/yaml" 10 | ) 11 | 12 | //go:embed *.yaml 13 | var templates embed.FS 14 | 15 | func ResourceFromTemplate[T any, R any](t *T, name string) (*R, error) { 16 | resYaml, err := templates.ReadFile(fmt.Sprintf("%s.yaml", name)) 17 | if err != nil { 18 | return nil, fmt.Errorf("could not read %s template: %v", name, err) 19 | } 20 | tmpl, err := template.New(name).Parse(string(resYaml)) 21 | if err != nil { 22 | return nil, fmt.Errorf("could not parse %s template: %v", name, err) 23 | } 24 | var buf bytes.Buffer 25 | 26 | err = tmpl.Execute(&buf, struct { 27 | Values *T 28 | }{ 29 | Values: t, 30 | }) 31 | if err != nil { 32 | return nil, fmt.Errorf("could not execute %s template: %v", name, err) 33 | } 34 | 35 | res := new(R) 36 | decoder := yaml.NewYAMLOrJSONDecoder(&buf, 10) 37 | if err := decoder.Decode(res); err != nil { 38 | return nil, fmt.Errorf("could not decode resource %T: %v", res, err) 39 | } 40 | 41 | return res, nil 42 | } 43 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package main 18 | 19 | import ( 20 | "flag" 21 | "os" 22 | 23 | // Import all Kubernetes client auth plugins (e.g. Azure, GCP, OIDC, etc.) 24 | // to ensure that exec-entrypoint and run can make use of them. 25 | _ "k8s.io/client-go/plugin/pkg/client/auth" 26 | 27 | "k8s.io/apimachinery/pkg/runtime" 28 | utilruntime "k8s.io/apimachinery/pkg/util/runtime" 29 | clientgoscheme "k8s.io/client-go/kubernetes/scheme" 30 | ctrl "sigs.k8s.io/controller-runtime" 31 | "sigs.k8s.io/controller-runtime/pkg/healthz" 32 | "sigs.k8s.io/controller-runtime/pkg/log/zap" 33 | "sigs.k8s.io/controller-runtime/pkg/metrics/server" 34 | "sigs.k8s.io/controller-runtime/pkg/webhook" 35 | 36 | pgov1beta1 "github.com/crunchydata/postgres-operator/pkg/apis/postgres-operator.crunchydata.com/v1beta1" 37 | 38 | synapsev1alpha1 "github.com/opdev/synapse-operator/apis/synapse/v1alpha1" 39 | heisenbridgecontroller "github.com/opdev/synapse-operator/controllers/synapse/heisenbridge" 40 | mautrixsignalcontroller "github.com/opdev/synapse-operator/controllers/synapse/mautrixsignal" 41 | synapsecontroller "github.com/opdev/synapse-operator/controllers/synapse/synapse" 42 | //+kubebuilder:scaffold:imports 43 | ) 44 | 45 | var ( 46 | scheme = runtime.NewScheme() 47 | setupLog = ctrl.Log.WithName("setup") 48 | ) 49 | 50 | func init() { 51 | utilruntime.Must(clientgoscheme.AddToScheme(scheme)) 52 | 53 | utilruntime.Must(synapsev1alpha1.AddToScheme(scheme)) 54 | utilruntime.Must(pgov1beta1.AddToScheme(scheme)) 55 | //+kubebuilder:scaffold:scheme 56 | } 57 | 58 | func main() { 59 | var metricsAddr string 60 | var enableLeaderElection bool 61 | var probeAddr string 62 | flag.StringVar(&metricsAddr, "metrics-bind-address", ":8080", "The address the metric endpoint binds to.") 63 | flag.StringVar(&probeAddr, "health-probe-bind-address", ":8081", "The address the probe endpoint binds to.") 64 | flag.BoolVar(&enableLeaderElection, "leader-elect", false, 65 | "Enable leader election for controller manager. "+ 66 | "Enabling this will ensure there is only one active controller manager.") 67 | opts := zap.Options{ 68 | Development: true, 69 | } 70 | opts.BindFlags(flag.CommandLine) 71 | flag.Parse() 72 | 73 | ctrl.SetLogger(zap.New(zap.UseFlagOptions(&opts))) 74 | 75 | mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{ 76 | Scheme: scheme, 77 | Metrics: server.Options{ 78 | BindAddress: metricsAddr, 79 | }, 80 | WebhookServer: webhook.NewServer(webhook.Options{ 81 | Port: 9443, 82 | }), 83 | HealthProbeBindAddress: probeAddr, 84 | LeaderElection: enableLeaderElection, 85 | LeaderElectionID: "8d311e9b.opdev.io", 86 | }) 87 | if err != nil { 88 | setupLog.Error(err, "unable to start manager") 89 | os.Exit(1) 90 | } 91 | 92 | if err = (&synapsecontroller.SynapseReconciler{ 93 | Client: mgr.GetClient(), 94 | Scheme: mgr.GetScheme(), 95 | }).SetupWithManager(mgr); err != nil { 96 | setupLog.Error(err, "unable to create controller", "controller", "Synapse") 97 | os.Exit(1) 98 | } 99 | if err = (&mautrixsignalcontroller.MautrixSignalReconciler{ 100 | Client: mgr.GetClient(), 101 | Scheme: mgr.GetScheme(), 102 | }).SetupWithManager(mgr); err != nil { 103 | setupLog.Error(err, "unable to create controller", "controller", "MautrixSignal") 104 | os.Exit(1) 105 | } 106 | if err = (&heisenbridgecontroller.HeisenbridgeReconciler{ 107 | Client: mgr.GetClient(), 108 | Scheme: mgr.GetScheme(), 109 | }).SetupWithManager(mgr); err != nil { 110 | setupLog.Error(err, "unable to create controller", "controller", "Heisenbridge") 111 | os.Exit(1) 112 | } 113 | //+kubebuilder:scaffold:builder 114 | 115 | if err := mgr.AddHealthzCheck("healthz", healthz.Ping); err != nil { 116 | setupLog.Error(err, "unable to set up health check") 117 | os.Exit(1) 118 | } 119 | if err := mgr.AddReadyzCheck("readyz", healthz.Ping); err != nil { 120 | setupLog.Error(err, "unable to set up ready check") 121 | os.Exit(1) 122 | } 123 | 124 | setupLog.Info("starting manager") 125 | if err := mgr.Start(ctrl.SetupSignalHandler()); err != nil { 126 | setupLog.Error(err, "problem running manager") 127 | os.Exit(1) 128 | } 129 | } 130 | --------------------------------------------------------------------------------