├── config ├── prometheus │ ├── kustomization.yaml │ └── monitor.yaml ├── rbac │ ├── service_account.yaml │ ├── kustomization.yaml │ ├── role_binding.yaml │ ├── leader_election_role_binding.yaml │ ├── user_viewer_role.yaml │ ├── queue_viewer_role.yaml │ ├── vhost_viewer_role.yaml │ ├── policy_viewer_role.yaml │ ├── shovel_viewer_role.yaml │ ├── binding_viewer_role.yaml │ ├── exchange_viewer_role.yaml │ ├── federation_viewer_role.yaml │ ├── permission_viewer_role.yaml │ ├── superstream_viewer_role.yaml │ ├── operatorpolicy_viewer_role.yaml │ ├── topicpermission_viewer_role.yaml │ ├── user_editor_role.yaml │ ├── queue_editor_role.yaml │ ├── schemareplication_viewer_role.yaml │ ├── vhost_editor_role.yaml │ ├── shovel_editor_role.yaml │ ├── binding_editor_role.yaml │ ├── policy_editor_role.yaml │ ├── exchange_editor_role.yaml │ ├── federation_editor_role.yaml │ ├── permission_editor_role.yaml │ ├── superstream_editor_role.yaml │ ├── operatorpolicy_editor_role.yaml │ ├── topicpermission_editor_role.yaml │ ├── schemareplication_editor_role.yaml │ ├── leader_election_role.yaml │ └── role.yaml ├── certmanager │ ├── kustomization.yaml │ ├── kustomizeconfig.yaml │ └── certificate.yaml ├── schema.yml ├── crd │ ├── patches │ │ ├── cainjection_in_queues.yaml │ │ ├── cainjection_in_vhosts.yaml │ │ ├── cainjection_in_federations.yaml │ │ ├── cainjection_in_shovels.yaml │ │ ├── cainjection_in_superstreams.yaml │ │ ├── cainjection_in_topicpermissions.yaml │ │ ├── cainjection_in_users.yaml │ │ ├── cainjection_in_bindings.yaml │ │ ├── cainjection_in_policies.yaml │ │ ├── cainjection_in_exchanges.yaml │ │ ├── cainjection_in_permissions.yaml │ │ ├── cainjection_in_operatorpolicies.yaml │ │ ├── cainjection_in_schemareplications.yaml │ │ ├── webhook_in_queues.yaml │ │ ├── webhook_in_federations.yaml │ │ ├── webhook_in_superstreams.yaml │ │ ├── webhook_in_topicpermissions.yaml │ │ ├── webhook_in_shovels.yaml │ │ ├── webhook_in_users.yaml │ │ ├── webhook_in_bindings.yaml │ │ ├── webhook_in_policies.yaml │ │ ├── webhook_in_vhosts.yaml │ │ ├── webhook_in_exchanges.yaml │ │ ├── webhook_in_permissions.yaml │ │ ├── webhook_in_operatorpolicies.yaml │ │ └── webhook_in_schemareplications.yaml │ ├── kustomizeconfig.yaml │ └── kustomization.yaml ├── webhook │ ├── service.yaml │ ├── kustomization.yaml │ └── kustomizeconfig.yaml ├── default │ ├── base │ │ ├── webhookcainjection_patch.yaml │ │ ├── kustomization.yaml │ │ └── manager_webhook_patch.yaml │ ├── manager_webhook_patch.yaml │ ├── overlays │ │ ├── kind │ │ │ ├── kustomization.yaml │ │ │ └── manager_image_patch.yaml │ │ ├── dev │ │ │ ├── kustomization.yaml │ │ │ └── manager_image_patch.yaml │ │ └── cert-manager │ │ │ └── kustomization.yaml │ └── webhookcainjection_patch.yaml ├── manager │ ├── kustomization.yaml │ └── manager.yaml ├── ytt_overlays │ ├── never_pull.yml │ └── change_deployment_image.yml ├── namespace │ ├── kustomization.yaml │ └── namespace.yaml └── installation │ ├── kustomization.yaml │ └── cert-manager │ └── kustomization.yaml ├── docs ├── api │ └── autogen │ │ ├── config.yaml │ │ └── templates │ │ ├── type_members.tpl │ │ ├── gv_list.tpl │ │ ├── gv_details.tpl │ │ └── type.tpl ├── images │ ├── start-cert-support.jpg │ └── start-cert-internal-domain.jpg └── examples │ ├── superstream │ ├── superstream.yaml │ ├── rabbitmq.yaml │ └── README.md │ ├── vhosts │ ├── README.md │ ├── vhost-limits.yaml │ └── vhost.yaml │ ├── federations │ ├── federation-uri.yaml │ ├── federation.yaml │ ├── policy.yaml │ ├── vhosts.yaml │ ├── bindings.yaml │ ├── queues.yaml │ ├── exchanges.yaml │ └── README.md │ ├── shovels │ ├── shovel-secret.yaml │ ├── vhosts.yaml │ ├── queues.yaml │ └── shovel.yaml │ ├── non-operator-managed-rabbitmq │ ├── README.md │ └── queue.yaml │ ├── permissions │ ├── permission-user-reference.yaml │ └── permission.yaml │ ├── users │ ├── setUsernamewithGenPass.yaml │ ├── user.yaml │ ├── userWithPasswordHash.yaml │ ├── passwordlessUser.yaml │ ├── userPreDefinedCreds.yaml │ ├── publish-consume-user.yaml │ └── README.md │ ├── topic-exchanges │ ├── user.yaml │ ├── user-topic-permissions.yaml │ ├── topic-exchanges.yaml │ └── README.md │ ├── vault-support │ ├── rabbitmq-queue-b.yaml │ ├── rabbitmq-queue-d.yaml │ ├── rabbitmq-queue-a.yaml │ ├── rabbitmq-queue-c.yaml │ └── rabbitmq-clusters.yaml │ ├── exchanges │ ├── direct-exchange.yaml │ ├── fanout-exchange.yaml │ └── fanout-exchange-with-args.yaml │ ├── operator-policies │ └── operator-policy.yaml │ ├── queues │ ├── stream-queue.yaml │ └── quorum-queue.yaml │ ├── README.md │ ├── bindings │ └── binding.yaml │ └── policies │ └── policy.yaml ├── .github ├── release.yml ├── ISSUE_TEMPLATE │ ├── config.yaml │ ├── question.md │ ├── feature_request.md │ └── bug_report.md ├── dependabot.yml └── workflows │ ├── publish-versioned-api-ref.yml │ └── codeql-analysis.yml ├── .gitignore ├── pull_request_template.md ├── scripts ├── print-tag-version.bash └── print-previous-tag-version.bash ├── olm ├── bundle │ ├── metadata │ │ ├── dependencies.yaml │ │ └── annotations.yaml │ ├── templates │ │ ├── overlay-remove-ns.yaml │ │ └── topology-operator-namespace-scope-overlay.yml │ └── bundle.Dockerfile ├── assets │ ├── operator-group.yaml │ ├── subscription.yaml │ └── catalog-source.yaml └── catalog │ └── cool-catalog.Dockerfile ├── internal ├── random.go ├── topic_permissions.go ├── managedresource │ ├── managedresource_suite_test.go │ ├── managedresource_builder.go │ ├── superstream_exchange.go │ ├── superstream_binding.go │ └── superstream_partition.go ├── tools │ └── tools.go ├── internal_suite_test.go ├── permissions.go ├── queue_delete_options.go ├── permissions_test.go ├── policy.go ├── exchange_settings.go ├── topic_permissions_test.go ├── operatorpolicy.go ├── vhost_settings.go ├── federation_definition.go ├── queue_settings.go ├── schema_replication.go ├── exchange_settings_test.go ├── policy_test.go ├── queue_delete_options_test.go ├── operator_policy_test.go ├── binding.go ├── queue_settings_test.go └── schema_replication_test.go ├── system_tests ├── fixtures │ └── patch-test-ca.yaml └── rabbitmq_connection_test.go ├── NOTICE.txt ├── hack ├── NOTICE.go.txt └── generate-ordered-api-reference-list.sh ├── api ├── v1beta1 │ ├── topology_resource.go │ ├── schemareplication_types_test.go │ ├── conditions_test.go │ ├── suite_test.go │ ├── groupversion_info.go │ ├── conditions.go │ ├── user_webhook.go │ ├── vhost_webhook.go │ └── rabbitmq_cluster_reference.go └── v1alpha1 │ ├── suite_test.go │ ├── groupversion_info.go │ └── superstream_types_test.go ├── pkg └── generated │ ├── clientset │ └── versioned │ │ ├── doc.go │ │ ├── fake │ │ ├── doc.go │ │ └── register.go │ │ ├── scheme │ │ ├── doc.go │ │ └── register.go │ │ └── typed │ │ └── rabbitmq.com │ │ ├── v1alpha1 │ │ ├── fake │ │ │ ├── doc.go │ │ │ └── fake_rabbitmq.com_client.go │ │ ├── generated_expansion.go │ │ └── doc.go │ │ └── v1beta1 │ │ ├── doc.go │ │ ├── fake │ │ └── doc.go │ │ └── generated_expansion.go │ ├── listers │ └── rabbitmq.com │ │ └── v1alpha1 │ │ └── expansion_generated.go │ └── informers │ └── externalversions │ ├── internalinterfaces │ └── factory_interfaces.go │ └── rabbitmq.com │ ├── v1alpha1 │ └── interface.go │ └── interface.go ├── controllers ├── reconcile_func.go ├── common.go ├── exchange_controller.go ├── policy_controller.go └── operatorpolicy_controller.go ├── Dockerfile └── rabbitmqclient └── suite_test.go /config/prometheus/kustomization.yaml: -------------------------------------------------------------------------------- 1 | resources: 2 | - monitor.yaml 3 | -------------------------------------------------------------------------------- /docs/api/autogen/config.yaml: -------------------------------------------------------------------------------- 1 | render: 2 | kubernetesVersion: "1.22" 3 | -------------------------------------------------------------------------------- /config/rbac/service_account.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ServiceAccount 3 | metadata: 4 | name: operator 5 | -------------------------------------------------------------------------------- /.github/release.yml: -------------------------------------------------------------------------------- 1 | changelog: 2 | exclude: 3 | labels: 4 | - dependencies 5 | authors: 6 | - dependabot -------------------------------------------------------------------------------- /config/certmanager/kustomization.yaml: -------------------------------------------------------------------------------- 1 | resources: 2 | - certificate.yaml 3 | 4 | configurations: 5 | - kustomizeconfig.yaml 6 | -------------------------------------------------------------------------------- /config/schema.yml: -------------------------------------------------------------------------------- 1 | #@data/values-schema 2 | --- 3 | operator_image: "rabbitmqoperator/messaging-topology-operator:latest" 4 | -------------------------------------------------------------------------------- /docs/images/start-cert-support.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rabbitmq/messaging-topology-operator/HEAD/docs/images/start-cert-support.jpg -------------------------------------------------------------------------------- /docs/images/start-cert-internal-domain.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rabbitmq/messaging-topology-operator/HEAD/docs/images/start-cert-internal-domain.jpg -------------------------------------------------------------------------------- /docs/examples/superstream/superstream.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rabbitmq.com/v1alpha1 2 | kind: SuperStream 3 | metadata: 4 | name: orders 5 | spec: 6 | name: orders 7 | partitions: 3 8 | rabbitmqClusterReference: 9 | name: billing 10 | -------------------------------------------------------------------------------- /docs/examples/vhosts/README.md: -------------------------------------------------------------------------------- 1 | # Vhost examples 2 | 3 | This section contains 1 example for creating a RabbitMQ vhost. 4 | Note that setting default queue type `spec.defaultQueueType` is only supported by RabbitMQ server version `3.11.12` or above. 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | messaging-topology-operator 2 | vendor/ 3 | cover.out 4 | **/*.yaml-e 5 | .idea/ 6 | .envrc 7 | tags 8 | releases/ 9 | testbin/ 10 | bin/ 11 | tmp/ 12 | *.iml 13 | ## OLM 14 | olm/bundle/crds 15 | olm/bundle/manifests 16 | olm/catalog/cool-catalog/ -------------------------------------------------------------------------------- /config/rbac/kustomization.yaml: -------------------------------------------------------------------------------- 1 | namespace: rabbitmq-system 2 | namePrefix: messaging-topology- 3 | 4 | resources: 5 | - service_account.yaml 6 | - role.yaml 7 | - role_binding.yaml 8 | - leader_election_role.yaml 9 | - leader_election_role_binding.yaml 10 | -------------------------------------------------------------------------------- /config/crd/patches/cainjection_in_queues.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apiextensions.k8s.io/v1 2 | kind: CustomResourceDefinition 3 | metadata: 4 | annotations: 5 | cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME) 6 | name: queues.rabbitmq.com 7 | -------------------------------------------------------------------------------- /config/crd/patches/cainjection_in_vhosts.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apiextensions.k8s.io/v1 2 | kind: CustomResourceDefinition 3 | metadata: 4 | annotations: 5 | cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME) 6 | name: vhosts.rabbitmq.com 7 | -------------------------------------------------------------------------------- /config/crd/patches/cainjection_in_federations.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apiextensions.k8s.io/v1 2 | kind: CustomResourceDefinition 3 | metadata: 4 | annotations: 5 | cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME) 6 | name: federations.rabbitmq.com 7 | -------------------------------------------------------------------------------- /docs/api/autogen/templates/type_members.tpl: -------------------------------------------------------------------------------- 1 | {{- define "type_members" -}} 2 | {{- $field := . -}} 3 | {{- if eq $field.Name "metadata" -}} 4 | Refer to Kubernetes API documentation for fields of `metadata`. 5 | {{ else -}} 6 | {{ $field.Doc }} 7 | {{- end -}} 8 | {{- end -}} 9 | -------------------------------------------------------------------------------- /docs/examples/superstream/rabbitmq.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rabbitmq.com/v1beta1 2 | kind: RabbitmqCluster 3 | metadata: 4 | name: billing 5 | spec: 6 | replicas: 1 7 | image: rabbitmq:3.9-management 8 | rabbitmq: 9 | additionalPlugins: 10 | - rabbitmq_stream 11 | -------------------------------------------------------------------------------- /docs/examples/vhosts/vhost-limits.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rabbitmq.com/v1beta1 2 | kind: Vhost 3 | metadata: 4 | name: test-vhost 5 | spec: 6 | name: myvhost 7 | rabbitmqClusterReference: 8 | name: test 9 | limits: 10 | connections: 100 11 | queues: 50 12 | -------------------------------------------------------------------------------- /config/webhook/service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: webhook-service 5 | namespace: system 6 | spec: 7 | ports: 8 | - port: 443 9 | targetPort: 9443 10 | selector: 11 | app.kubernetes.io/name: messaging-topology-operator 12 | -------------------------------------------------------------------------------- /docs/examples/federations/federation-uri.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: Secret 4 | metadata: 5 | name: federation-uri 6 | type: Opaque 7 | data: 8 | uri: #uri to access the 'upstream' vhost; to learn about how to construct a valid uri, see: https://www.rabbitmq.com/uri-spec.html 9 | -------------------------------------------------------------------------------- /config/default/base/webhookcainjection_patch.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: admissionregistration.k8s.io/v1 3 | kind: ValidatingWebhookConfiguration 4 | metadata: 5 | name: validating-webhook-configuration 6 | annotations: 7 | cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME) 8 | -------------------------------------------------------------------------------- /pull_request_template.md: -------------------------------------------------------------------------------- 1 | This closes # 2 | 3 | **Note to reviewers:** remember to look at the commits in this PR and consider if they can be squashed 4 | **Note to contributors:** remember to re-generate client set if there are any API changes 5 | 6 | ## Summary Of Changes 7 | 8 | ## Additional Context 9 | -------------------------------------------------------------------------------- /scripts/print-tag-version.bash: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | if [ -n "$1" ]; then 4 | echo "BUNDLE_VERSION=$1" 5 | exit 0 6 | fi 7 | 8 | if [ "$GITHUB_REF_TYPE" != "tag" ]; then 9 | echo "BUNDLE_VERSION=0.0.0" 10 | exit 0 11 | fi 12 | 13 | printf "BUNDLE_VERSION=%s\n" "${GITHUB_REF_NAME:1}" 14 | -------------------------------------------------------------------------------- /olm/bundle/metadata/dependencies.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | dependencies: 3 | - type: olm.package 4 | value: 5 | packageName: rabbitmq-cluster-operator 6 | version: ">2.0.0" 7 | - type: olm.gvk 8 | value: 9 | group: rabbitmq.com 10 | kind: RabbitmqCluster 11 | version: v1beta1 12 | -------------------------------------------------------------------------------- /scripts/print-previous-tag-version.bash: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | if [ -n "$1" ]; then 4 | echo "PREVIOUS_VERSION=$1" 5 | exit 0 6 | fi 7 | 8 | set -e 9 | 10 | prev=$(gh release list --exclude-drafts --exclude-pre-releases --limit 2 --json tagName --jq '.[1].tagName') 11 | printf "PREVIOUS_VERSION=%s\n" "${prev}" 12 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yaml: -------------------------------------------------------------------------------- 1 | contact_links: 2 | - name: RabbitMQ Discussions 3 | url: https://github.com/rabbitmq/rabbitmq-server/discussions 4 | about: Please ask and answer questions here. 5 | - name: RabbitMQ Mailing list 6 | url: https://groups.google.com/g/rabbitmq-users 7 | about: If you prefer to ask/answer via email. 8 | -------------------------------------------------------------------------------- /config/crd/patches/cainjection_in_shovels.yaml: -------------------------------------------------------------------------------- 1 | # The following patch adds a directive for certmanager to inject CA into the CRD 2 | apiVersion: apiextensions.k8s.io 3 | kind: CustomResourceDefinition 4 | metadata: 5 | annotations: 6 | cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME) 7 | name: shovels.rabbitmq.com 8 | -------------------------------------------------------------------------------- /docs/examples/shovels/shovel-secret.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: Secret 4 | metadata: 5 | name: shovel-secret 6 | type: Opaque 7 | data: 8 | destUri: # encoded value for shovel destination URI; to learn about how to construct a valid uri, see: https://www.rabbitmq.com/uri-spec.html 9 | srcUri: # encoded value shove source URI 10 | -------------------------------------------------------------------------------- /config/manager/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | 4 | resources: 5 | - manager.yaml 6 | 7 | images: 8 | - name: controller 9 | newName: rabbitmqoperator/messaging-topology-operator-dev 10 | newTag: latest 11 | 12 | 13 | namespace: rabbitmq-system 14 | namePrefix: messaging-topology- 15 | -------------------------------------------------------------------------------- /config/crd/patches/cainjection_in_superstreams.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: superstreams.rabbitmq.com 8 | -------------------------------------------------------------------------------- /olm/bundle/templates/overlay-remove-ns.yaml: -------------------------------------------------------------------------------- 1 | #@ load("@ytt:overlay", "overlay") 2 | #@ svc = overlay.subset({"kind": "Service"}) 3 | #@ topology_operator = overlay.subset({"metadata": {"name": "messaging-topology-operator-webhook"}}) 4 | #@overlay/match by=overlay.and_op(svc, topology_operator),expects=1 5 | --- 6 | metadata: 7 | #@overlay/remove 8 | namespace: ns 9 | -------------------------------------------------------------------------------- /config/crd/patches/cainjection_in_topicpermissions.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: topicpermissions.rabbitmq.com 8 | -------------------------------------------------------------------------------- /docs/examples/superstream/README.md: -------------------------------------------------------------------------------- 1 | # SuperStream example 2 | 3 | This example creates a `SuperStream` object. This behaves effectively as a regular stream queue, split into 4 | a number of partitions. Messages published to the SuperStream will be routed to the different partitions 5 | either by pre-determined routing keys, or dynamically split between the partitions. 6 | -------------------------------------------------------------------------------- /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: messaging-topology-operator 12 | namespace: rabbitmq-system 13 | -------------------------------------------------------------------------------- /internal/random.go: -------------------------------------------------------------------------------- 1 | package internal 2 | 3 | import ( 4 | "crypto/rand" 5 | "encoding/base64" 6 | ) 7 | 8 | func RandomEncodedString(dataLen int) (string, error) { 9 | randomBytes := make([]byte, dataLen) 10 | if _, err := rand.Read(randomBytes); err != nil { 11 | return "", err 12 | } 13 | return base64.URLEncoding.EncodeToString(randomBytes), nil 14 | } 15 | -------------------------------------------------------------------------------- /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: messaging-topology-operator 12 | namespace: rabbitmq-system 13 | -------------------------------------------------------------------------------- /config/crd/patches/cainjection_in_users.yaml: -------------------------------------------------------------------------------- 1 | # The following patch adds a directive for certmanager to inject CA into the CRD 2 | # CRD conversion requires k8s 1.13 or later. 3 | apiVersion: apiextensions.k8s.io/v1 4 | kind: CustomResourceDefinition 5 | metadata: 6 | annotations: 7 | cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME) 8 | name: users.rabbitmq.com 9 | -------------------------------------------------------------------------------- /config/crd/patches/cainjection_in_bindings.yaml: -------------------------------------------------------------------------------- 1 | # The following patch adds a directive for certmanager to inject CA into the CRD 2 | # CRD conversion requires k8s 1.13 or later. 3 | apiVersion: apiextensions.k8s.io/v1 4 | kind: CustomResourceDefinition 5 | metadata: 6 | annotations: 7 | cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME) 8 | name: bindings.rabbitmq.com 9 | -------------------------------------------------------------------------------- /config/crd/patches/cainjection_in_policies.yaml: -------------------------------------------------------------------------------- 1 | # The following patch adds a directive for certmanager to inject CA into the CRD 2 | # CRD conversion requires k8s 1.13 or later. 3 | apiVersion: apiextensions.k8s.io/v1 4 | kind: CustomResourceDefinition 5 | metadata: 6 | annotations: 7 | cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME) 8 | name: policies.rabbitmq.com 9 | -------------------------------------------------------------------------------- /system_tests/fixtures/patch-test-ca.yaml: -------------------------------------------------------------------------------- 1 | spec: 2 | template: 3 | spec: 4 | containers: 5 | - name: manager 6 | volumeMounts: 7 | - mountPath: /etc/ssl/certs/testca.crt 8 | name: test-ca 9 | subPath: tls.crt 10 | volumes: 11 | - name: test-ca 12 | secret: 13 | defaultMode: 420 14 | secretName: %s 15 | -------------------------------------------------------------------------------- /config/crd/patches/cainjection_in_exchanges.yaml: -------------------------------------------------------------------------------- 1 | # The following patch adds a directive for certmanager to inject CA into the CRD 2 | # CRD conversion requires k8s 1.13 or later. 3 | apiVersion: apiextensions.k8s.io/v1 4 | kind: CustomResourceDefinition 5 | metadata: 6 | annotations: 7 | cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME) 8 | name: exchanges.rabbitmq.com 9 | -------------------------------------------------------------------------------- /config/crd/patches/cainjection_in_permissions.yaml: -------------------------------------------------------------------------------- 1 | # The following patch adds a directive for certmanager to inject CA into the CRD 2 | # CRD conversion requires k8s 1.13 or later. 3 | apiVersion: apiextensions.k8s.io/v1 4 | kind: CustomResourceDefinition 5 | metadata: 6 | annotations: 7 | cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME) 8 | name: permissions.rabbitmq.com 9 | -------------------------------------------------------------------------------- /config/crd/patches/cainjection_in_operatorpolicies.yaml: -------------------------------------------------------------------------------- 1 | # The following patch adds a directive for certmanager to inject CA into the CRD 2 | # CRD conversion requires k8s 1.13 or later. 3 | apiVersion: apiextensions.k8s.io/v1 4 | kind: CustomResourceDefinition 5 | metadata: 6 | annotations: 7 | cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME) 8 | name: operatorpolicies.rabbitmq.com 9 | -------------------------------------------------------------------------------- /config/crd/patches/cainjection_in_schemareplications.yaml: -------------------------------------------------------------------------------- 1 | # The following patch adds a directive for certmanager to inject CA into the CRD 2 | # CRD conversion requires k8s 1.13 or later. 3 | apiVersion: apiextensions.k8s.io/v1 4 | kind: CustomResourceDefinition 5 | metadata: 6 | annotations: 7 | cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME) 8 | name: schemareplications.rabbitmq.com 9 | -------------------------------------------------------------------------------- /config/webhook/kustomization.yaml: -------------------------------------------------------------------------------- 1 | resources: 2 | - manifests.yaml 3 | - service.yaml 4 | 5 | configurations: 6 | - kustomizeconfig.yaml 7 | 8 | patches: 9 | - patch: |- 10 | - op: replace 11 | path: /metadata/name 12 | value: topology.rabbitmq.com 13 | target: 14 | group: admissionregistration.k8s.io 15 | version: v1 16 | kind: ValidatingWebhookConfiguration 17 | name: .* 18 | -------------------------------------------------------------------------------- /config/ytt_overlays/never_pull.yml: -------------------------------------------------------------------------------- 1 | #@ load("@ytt:overlay", "overlay") 2 | 3 | #! Matching by Deployment is acceptable because we have only one Deployment :-) 4 | #@overlay/match by=overlay.subset({"kind": "Deployment"}) 5 | --- 6 | spec: 7 | template: 8 | spec: 9 | containers: 10 | #@overlay/match by=overlay.map_key("name") 11 | - name: manager 12 | imagePullPolicy: Never 13 | -------------------------------------------------------------------------------- /docs/examples/federations/federation.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: rabbitmq.com/v1beta1 3 | kind: Federation 4 | metadata: 5 | name: federation-example 6 | spec: 7 | name: "origin" 8 | vhost: "downstream" 9 | uriSecret: 10 | name: federation-uri 11 | ackMode: "on-confirm" 12 | deletionPolicy: retain # delete or retain; default to delete; 13 | rabbitmqClusterReference: 14 | name: example-rabbit 15 | -------------------------------------------------------------------------------- /docs/examples/federations/policy.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: rabbitmq.com/v1beta1 3 | kind: Policy 4 | metadata: 5 | name: federation-policy 6 | spec: 7 | name: federation-policy # name of the policy 8 | vhost: "downstream" 9 | pattern: "fanout" 10 | applyTo: exchanges 11 | definition: # policy definition 12 | federation-upstream: origin 13 | rabbitmqClusterReference: 14 | name: example-rabbit 15 | -------------------------------------------------------------------------------- /docs/examples/shovels/vhosts.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: rabbitmq.com/v1beta1 3 | kind: Vhost 4 | metadata: 5 | name: source-vhost 6 | spec: 7 | name: source 8 | rabbitmqClusterReference: 9 | name: example-rabbit 10 | --- 11 | apiVersion: rabbitmq.com/v1beta1 12 | kind: Vhost 13 | metadata: 14 | name: destination-vhost 15 | spec: 16 | name: destination 17 | rabbitmqClusterReference: 18 | name: example-rabbit 19 | -------------------------------------------------------------------------------- /config/rbac/user_viewer_role.yaml: -------------------------------------------------------------------------------- 1 | # permissions for end users to view users. 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRole 4 | metadata: 5 | name: user-viewer-role 6 | rules: 7 | - apiGroups: 8 | - rabbitmq.com 9 | resources: 10 | - users 11 | verbs: 12 | - get 13 | - list 14 | - watch 15 | - apiGroups: 16 | - rabbitmq.com 17 | resources: 18 | - users/status 19 | verbs: 20 | - get 21 | -------------------------------------------------------------------------------- /docs/examples/federations/vhosts.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: rabbitmq.com/v1beta1 3 | kind: Vhost 4 | metadata: 5 | name: upstream-vhost 6 | spec: 7 | name: upstream 8 | rabbitmqClusterReference: 9 | name: example-rabbit 10 | --- 11 | apiVersion: rabbitmq.com/v1beta1 12 | kind: Vhost 13 | metadata: 14 | name: downstream-vhost 15 | spec: 16 | name: downstream 17 | rabbitmqClusterReference: 18 | name: example-rabbit 19 | -------------------------------------------------------------------------------- /config/rbac/queue_viewer_role.yaml: -------------------------------------------------------------------------------- 1 | # permissions for end users to view queues. 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRole 4 | metadata: 5 | name: queue-viewer-role 6 | rules: 7 | - apiGroups: 8 | - rabbitmq.com 9 | resources: 10 | - queues 11 | verbs: 12 | - get 13 | - list 14 | - watch 15 | - apiGroups: 16 | - rabbitmq.com 17 | resources: 18 | - queues/status 19 | verbs: 20 | - get 21 | -------------------------------------------------------------------------------- /config/rbac/vhost_viewer_role.yaml: -------------------------------------------------------------------------------- 1 | # permissions for end users to view vhosts. 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRole 4 | metadata: 5 | name: vhost-viewer-role 6 | rules: 7 | - apiGroups: 8 | - rabbitmq.com 9 | resources: 10 | - vhosts 11 | verbs: 12 | - get 13 | - list 14 | - watch 15 | - apiGroups: 16 | - rabbitmq.com 17 | resources: 18 | - vhosts/status 19 | verbs: 20 | - get 21 | -------------------------------------------------------------------------------- /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 | selector: 15 | matchLabels: 16 | control-plane: controller-manager 17 | -------------------------------------------------------------------------------- /config/rbac/policy_viewer_role.yaml: -------------------------------------------------------------------------------- 1 | # permissions for end users to view policies. 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRole 4 | metadata: 5 | name: policy-viewer-role 6 | rules: 7 | - apiGroups: 8 | - rabbitmq.com 9 | resources: 10 | - policies 11 | verbs: 12 | - get 13 | - list 14 | - watch 15 | - apiGroups: 16 | - rabbitmq.com 17 | resources: 18 | - policies/status 19 | verbs: 20 | - get 21 | -------------------------------------------------------------------------------- /config/rbac/shovel_viewer_role.yaml: -------------------------------------------------------------------------------- 1 | # permissions for end users to view shovels. 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRole 4 | metadata: 5 | name: shovel-viewer-role 6 | rules: 7 | - apiGroups: 8 | - rabbitmq.com 9 | resources: 10 | - shovels 11 | verbs: 12 | - get 13 | - list 14 | - watch 15 | - apiGroups: 16 | - rabbitmq.com 17 | resources: 18 | - shovels/status 19 | verbs: 20 | - get 21 | -------------------------------------------------------------------------------- /config/rbac/binding_viewer_role.yaml: -------------------------------------------------------------------------------- 1 | # permissions for end users to view bindings. 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRole 4 | metadata: 5 | name: binding-viewer-role 6 | rules: 7 | - apiGroups: 8 | - rabbitmq.com 9 | resources: 10 | - bindings 11 | verbs: 12 | - get 13 | - list 14 | - watch 15 | - apiGroups: 16 | - rabbitmq.com 17 | resources: 18 | - bindings/status 19 | verbs: 20 | - get 21 | -------------------------------------------------------------------------------- /docs/api/autogen/templates/gv_list.tpl: -------------------------------------------------------------------------------- 1 | {{- define "gvList" -}} 2 | {{- $groupVersions := . -}} 3 | 4 | // Generated documentation. Please do not edit. 5 | :anchor_prefix: k8s-api 6 | 7 | [id="{p}-api-reference"] 8 | = API Reference 9 | 10 | .Packages 11 | {{- range $groupVersions }} 12 | - {{ asciidocRenderGVLink . }} 13 | {{- end }} 14 | 15 | {{ range $groupVersions }} 16 | {{ template "gvDetails" . }} 17 | {{ end }} 18 | 19 | {{- end -}} 20 | -------------------------------------------------------------------------------- /config/rbac/exchange_viewer_role.yaml: -------------------------------------------------------------------------------- 1 | # permissions for end users to view exchanges. 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRole 4 | metadata: 5 | name: exchange-viewer-role 6 | rules: 7 | - apiGroups: 8 | - rabbitmq.com 9 | resources: 10 | - exchanges 11 | verbs: 12 | - get 13 | - list 14 | - watch 15 | - apiGroups: 16 | - rabbitmq.com 17 | resources: 18 | - exchanges/status 19 | verbs: 20 | - get 21 | -------------------------------------------------------------------------------- /docs/examples/non-operator-managed-rabbitmq/README.md: -------------------------------------------------------------------------------- 1 | ## Supporting to any RabbitMQ clusters 2 | 3 | This is an example on how to create RabbitMQ topology objects in RabbitMQ clusters that's not Cluster Operator managed. 4 | The example first creates a Kubernetes secret object that contains keys 'username', 'password' and 'uri'. 5 | When creating a `queue.rabbitmq.com` resource, the secret is provided instead of a name reference 6 | to a RabbitmqCluster. 7 | 8 | -------------------------------------------------------------------------------- /config/crd/patches/webhook_in_queues.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apiextensions.k8s.io/v1 2 | kind: CustomResourceDefinition 3 | metadata: 4 | name: queues.rabbitmq.com 5 | spec: 6 | conversion: 7 | strategy: Webhook 8 | webhook: 9 | conversionReviewVersions: ["v1", "v1beta1"] 10 | clientConfig: 11 | caBundle: Cg== 12 | service: 13 | namespace: system 14 | name: webhook-service 15 | path: /convert 16 | -------------------------------------------------------------------------------- /config/rbac/federation_viewer_role.yaml: -------------------------------------------------------------------------------- 1 | # permissions for end users to view federations. 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRole 4 | metadata: 5 | name: federation-viewer-role 6 | rules: 7 | - apiGroups: 8 | - rabbitmq.com 9 | resources: 10 | - federations 11 | verbs: 12 | - get 13 | - list 14 | - watch 15 | - apiGroups: 16 | - rabbitmq.com 17 | resources: 18 | - federations/status 19 | verbs: 20 | - get 21 | -------------------------------------------------------------------------------- /config/rbac/permission_viewer_role.yaml: -------------------------------------------------------------------------------- 1 | # permissions for end users to view permissions. 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRole 4 | metadata: 5 | name: permission-viewer-role 6 | rules: 7 | - apiGroups: 8 | - rabbitmq.com 9 | resources: 10 | - permissions 11 | verbs: 12 | - get 13 | - list 14 | - watch 15 | - apiGroups: 16 | - rabbitmq.com 17 | resources: 18 | - permissions/status 19 | verbs: 20 | - get 21 | -------------------------------------------------------------------------------- /config/crd/patches/webhook_in_federations.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apiextensions.k8s.io/v1 2 | kind: CustomResourceDefinition 3 | metadata: 4 | name: federations.rabbitmq.com 5 | spec: 6 | conversion: 7 | strategy: Webhook 8 | webhook: 9 | conversionReviewVersions: ["v1", "v1beta1"] 10 | clientConfig: 11 | caBundle: Cg== 12 | service: 13 | namespace: system 14 | name: webhook-service 15 | path: /convert 16 | -------------------------------------------------------------------------------- /config/rbac/superstream_viewer_role.yaml: -------------------------------------------------------------------------------- 1 | # permissions for end users to view superstreams. 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRole 4 | metadata: 5 | name: superstream-viewer-role 6 | rules: 7 | - apiGroups: 8 | - rabbitmq.com 9 | resources: 10 | - superstreams 11 | verbs: 12 | - get 13 | - list 14 | - watch 15 | - apiGroups: 16 | - rabbitmq.com 17 | resources: 18 | - superstreams/status 19 | verbs: 20 | - get 21 | -------------------------------------------------------------------------------- /config/ytt_overlays/change_deployment_image.yml: -------------------------------------------------------------------------------- 1 | #@ load("@ytt:data", "data") 2 | #@ load("@ytt:overlay", "overlay") 3 | 4 | #! Matching by Deployment is acceptable because we have only one Deployment :-) 5 | #@overlay/match by=overlay.subset({"kind": "Deployment"}) 6 | --- 7 | spec: 8 | template: 9 | spec: 10 | containers: 11 | #@overlay/match by=overlay.map_key("name") 12 | - name: manager 13 | image: #@ data.values.operator_image 14 | -------------------------------------------------------------------------------- /olm/bundle/metadata/annotations.yaml: -------------------------------------------------------------------------------- 1 | annotations: 2 | operators.operatorframework.io.bundle.channel.default.v1: stable 3 | operators.operatorframework.io.bundle.channels.v1: stable 4 | operators.operatorframework.io.bundle.manifests.v1: manifests/ 5 | operators.operatorframework.io.bundle.mediatype.v1: registry+v1 6 | operators.operatorframework.io.bundle.metadata.v1: metadata/ 7 | operators.operatorframework.io.bundle.package.v1: rabbitmq-messaging-topology-operator 8 | -------------------------------------------------------------------------------- /config/rbac/operatorpolicy_viewer_role.yaml: -------------------------------------------------------------------------------- 1 | # permissions for end users to view operator policies. 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRole 4 | metadata: 5 | name: operatorpolicy-viewer-role 6 | rules: 7 | - apiGroups: 8 | - rabbitmq.com 9 | resources: 10 | - operatorpolicies 11 | verbs: 12 | - get 13 | - list 14 | - watch 15 | - apiGroups: 16 | - rabbitmq.com 17 | resources: 18 | - operatorpolicies/status 19 | verbs: 20 | - get 21 | -------------------------------------------------------------------------------- /config/rbac/topicpermission_viewer_role.yaml: -------------------------------------------------------------------------------- 1 | # permissions for end users to view topicpermissions. 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRole 4 | metadata: 5 | name: topicpermission-viewer-role 6 | rules: 7 | - apiGroups: 8 | - rabbitmq.com 9 | resources: 10 | - topicpermissions 11 | verbs: 12 | - get 13 | - list 14 | - watch 15 | - apiGroups: 16 | - rabbitmq.com 17 | resources: 18 | - topicpermissions/status 19 | verbs: 20 | - get 21 | -------------------------------------------------------------------------------- /config/rbac/user_editor_role.yaml: -------------------------------------------------------------------------------- 1 | # permissions for end users to edit users. 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRole 4 | metadata: 5 | name: user-editor-role 6 | rules: 7 | - apiGroups: 8 | - rabbitmq.com 9 | resources: 10 | - users 11 | verbs: 12 | - create 13 | - delete 14 | - get 15 | - list 16 | - patch 17 | - update 18 | - watch 19 | - apiGroups: 20 | - rabbitmq.com 21 | resources: 22 | - users/status 23 | verbs: 24 | - get 25 | -------------------------------------------------------------------------------- /internal/topic_permissions.go: -------------------------------------------------------------------------------- 1 | package internal 2 | 3 | import ( 4 | rabbithole "github.com/michaelklishin/rabbit-hole/v3" 5 | topology "github.com/rabbitmq/messaging-topology-operator/api/v1beta1" 6 | ) 7 | 8 | func GenerateTopicPermissions(p *topology.TopicPermission) rabbithole.TopicPermissions { 9 | return rabbithole.TopicPermissions{ 10 | Read: p.Spec.Permissions.Read, 11 | Write: p.Spec.Permissions.Write, 12 | Exchange: p.Spec.Permissions.Exchange, 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /config/certmanager/kustomizeconfig.yaml: -------------------------------------------------------------------------------- 1 | # This configuration is for teaching kustomize how to update name ref and var substitution 2 | nameReference: 3 | - kind: Issuer 4 | group: cert-manager.io 5 | fieldSpecs: 6 | - kind: Certificate 7 | group: cert-manager.io 8 | path: spec/issuerRef/name 9 | 10 | varReference: 11 | - kind: Certificate 12 | group: cert-manager.io 13 | path: spec/commonName 14 | - kind: Certificate 15 | group: cert-manager.io 16 | path: spec/dnsNames 17 | -------------------------------------------------------------------------------- /config/rbac/queue_editor_role.yaml: -------------------------------------------------------------------------------- 1 | # permissions for end users to edit queues. 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRole 4 | metadata: 5 | name: queue-editor-role 6 | rules: 7 | - apiGroups: 8 | - rabbitmq.com 9 | resources: 10 | - queues 11 | verbs: 12 | - create 13 | - delete 14 | - get 15 | - list 16 | - patch 17 | - update 18 | - watch 19 | - apiGroups: 20 | - rabbitmq.com 21 | resources: 22 | - queues/status 23 | verbs: 24 | - get 25 | -------------------------------------------------------------------------------- /config/rbac/schemareplication_viewer_role.yaml: -------------------------------------------------------------------------------- 1 | # permissions for end users to view schemareplications. 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRole 4 | metadata: 5 | name: schemareplication-viewer-role 6 | rules: 7 | - apiGroups: 8 | - rabbitmq.com 9 | resources: 10 | - schemareplications 11 | verbs: 12 | - get 13 | - list 14 | - watch 15 | - apiGroups: 16 | - rabbitmq.com 17 | resources: 18 | - schemareplications/status 19 | verbs: 20 | - get 21 | -------------------------------------------------------------------------------- /config/rbac/vhost_editor_role.yaml: -------------------------------------------------------------------------------- 1 | # permissions for end users to edit vhosts. 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRole 4 | metadata: 5 | name: vhost-editor-role 6 | rules: 7 | - apiGroups: 8 | - rabbitmq.com 9 | resources: 10 | - vhosts 11 | verbs: 12 | - create 13 | - delete 14 | - get 15 | - list 16 | - patch 17 | - update 18 | - watch 19 | - apiGroups: 20 | - rabbitmq.com 21 | resources: 22 | - vhosts/status 23 | verbs: 24 | - get 25 | -------------------------------------------------------------------------------- /docs/examples/permissions/permission-user-reference.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rabbitmq.com/v1beta1 2 | kind: Permission 3 | metadata: 4 | name: example-permission 5 | spec: 6 | vhost: "/" # name of a vhost 7 | userReference: 8 | name: "example-user" # name of a user.rabbitmq.com in the same namespace; must specify either spec.userReference or spec.user 9 | permissions: 10 | write: ".*" 11 | configure: ".*" 12 | read: ".*" 13 | rabbitmqClusterReference: 14 | name: sample 15 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "gomod" 4 | directory: "/" 5 | groups: 6 | all: 7 | patterns: 8 | - '*' 9 | schedule: 10 | interval: "daily" 11 | - package-ecosystem: "gomod" 12 | directory: "/internal/tools/" 13 | groups: 14 | tools: 15 | patterns: 16 | - '*' 17 | schedule: 18 | interval: "daily" 19 | - package-ecosystem: "github-actions" 20 | directory: "/" 21 | schedule: 22 | interval: "weekly" 23 | -------------------------------------------------------------------------------- /config/rbac/shovel_editor_role.yaml: -------------------------------------------------------------------------------- 1 | # permissions for end users to edit shovels. 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRole 4 | metadata: 5 | name: shovel-editor-role 6 | rules: 7 | - apiGroups: 8 | - rabbitmq.com 9 | resources: 10 | - shovels 11 | verbs: 12 | - create 13 | - delete 14 | - get 15 | - list 16 | - patch 17 | - update 18 | - watch 19 | - apiGroups: 20 | - rabbitmq.com 21 | resources: 22 | - shovels/status 23 | verbs: 24 | - get 25 | -------------------------------------------------------------------------------- /config/crd/patches/webhook_in_superstreams.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: superstreams.rabbitmq.com 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/rbac/binding_editor_role.yaml: -------------------------------------------------------------------------------- 1 | # permissions for end users to edit bindings. 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRole 4 | metadata: 5 | name: binding-editor-role 6 | rules: 7 | - apiGroups: 8 | - rabbitmq.com 9 | resources: 10 | - bindings 11 | verbs: 12 | - create 13 | - delete 14 | - get 15 | - list 16 | - patch 17 | - update 18 | - watch 19 | - apiGroups: 20 | - rabbitmq.com 21 | resources: 22 | - bindings/status 23 | verbs: 24 | - get 25 | -------------------------------------------------------------------------------- /config/rbac/policy_editor_role.yaml: -------------------------------------------------------------------------------- 1 | # permissions for end users to edit policies. 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRole 4 | metadata: 5 | name: policy-editor-role 6 | rules: 7 | - apiGroups: 8 | - rabbitmq.com 9 | resources: 10 | - policies 11 | verbs: 12 | - create 13 | - delete 14 | - get 15 | - list 16 | - patch 17 | - update 18 | - watch 19 | - apiGroups: 20 | - rabbitmq.com 21 | resources: 22 | - policies/status 23 | verbs: 24 | - get 25 | -------------------------------------------------------------------------------- /NOTICE.txt: -------------------------------------------------------------------------------- 1 | RabbitMQ Messaging Topology Kubernetes Operator 2 | Copyright 2021-2022 VMware, Inc. 3 | 4 | This product is licensed to you under the Mozilla Public License 2.0 license (the "License"). You may not use this product except in compliance with the Mozilla 2.0 License. 5 | 6 | This product may include a number of subcomponents with separate copyright notices and license terms. Your use of these subcomponents is subject to the terms and conditions of the subcomponent's license, as noted in the LICENSE file. 7 | -------------------------------------------------------------------------------- /config/rbac/exchange_editor_role.yaml: -------------------------------------------------------------------------------- 1 | # permissions for end users to edit exchanges. 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRole 4 | metadata: 5 | name: exchange-editor-role 6 | rules: 7 | - apiGroups: 8 | - rabbitmq.com 9 | resources: 10 | - exchanges 11 | verbs: 12 | - create 13 | - delete 14 | - get 15 | - list 16 | - patch 17 | - update 18 | - watch 19 | - apiGroups: 20 | - rabbitmq.com 21 | resources: 22 | - exchanges/status 23 | verbs: 24 | - get 25 | -------------------------------------------------------------------------------- /docs/examples/users/setUsernamewithGenPass.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Secret 3 | metadata: 4 | name: user-creds 5 | type: Opaque 6 | stringData: 7 | username: test 8 | --- 9 | apiVersion: rabbitmq.com/v1beta1 10 | kind: User 11 | metadata: 12 | name: set-username-gen-pass-example 13 | spec: 14 | tags: 15 | - management 16 | rabbitmqClusterReference: 17 | name: test # rabbitmqCluster must exist in the same namespace as this resource 18 | importCredentialsSecret: 19 | name: user-creds 20 | -------------------------------------------------------------------------------- /config/crd/patches/webhook_in_topicpermissions.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: topicpermissions.rabbitmq.com 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 | -------------------------------------------------------------------------------- /docs/api/autogen/templates/gv_details.tpl: -------------------------------------------------------------------------------- 1 | {{- define "gvDetails" -}} 2 | {{- $gv := . -}} 3 | [id="{{ asciidocGroupVersionID $gv | asciidocRenderAnchorID }}"] 4 | == {{ $gv.GroupVersionString }} 5 | 6 | {{ $gv.Doc }} 7 | 8 | {{- if $gv.Kinds }} 9 | .Resource Types 10 | {{- range $gv.SortedKinds }} 11 | - {{ $gv.TypeForKind . | asciidocRenderTypeLink }} 12 | {{- end }} 13 | {{ end }} 14 | 15 | === Definitions 16 | {{ range $gv.SortedTypes }} 17 | {{ template "type" . }} 18 | {{ end }} 19 | 20 | {{- end -}} 21 | -------------------------------------------------------------------------------- /config/rbac/federation_editor_role.yaml: -------------------------------------------------------------------------------- 1 | # permissions for end users to edit federations. 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRole 4 | metadata: 5 | name: federation-editor-role 6 | rules: 7 | - apiGroups: 8 | - rabbitmq.com 9 | resources: 10 | - federations 11 | verbs: 12 | - create 13 | - delete 14 | - get 15 | - list 16 | - patch 17 | - update 18 | - watch 19 | - apiGroups: 20 | - rabbitmq.com 21 | resources: 22 | - federations/status 23 | verbs: 24 | - get 25 | -------------------------------------------------------------------------------- /config/rbac/permission_editor_role.yaml: -------------------------------------------------------------------------------- 1 | # permissions for end users to edit permissions. 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRole 4 | metadata: 5 | name: permission-editor-role 6 | rules: 7 | - apiGroups: 8 | - rabbitmq.com 9 | resources: 10 | - permissions 11 | verbs: 12 | - create 13 | - delete 14 | - get 15 | - list 16 | - patch 17 | - update 18 | - watch 19 | - apiGroups: 20 | - rabbitmq.com 21 | resources: 22 | - permissions/status 23 | verbs: 24 | - get 25 | -------------------------------------------------------------------------------- /docs/examples/topic-exchanges/user.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Secret 3 | metadata: 4 | name: credentials-secret 5 | type: Opaque 6 | stringData: 7 | username: example 8 | password: whyareyoulookinghere 9 | --- 10 | apiVersion: rabbitmq.com/v1beta1 11 | kind: User 12 | metadata: 13 | name: topic-exchange-example 14 | spec: 15 | rabbitmqClusterReference: 16 | name: test # rabbitmqCluster must exist in the same namespace as this resource 17 | importCredentialsSecret: 18 | name: credentials-secret 19 | -------------------------------------------------------------------------------- /docs/examples/vault-support/rabbitmq-queue-b.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rabbitmq.com/v1beta1 2 | kind: Queue 3 | metadata: 4 | name: queue-b 5 | spec: 6 | name: queue-b # name of the queue 7 | type: quorum # without providing a queue type, rabbitmq creates a classic queue 8 | autoDelete: false 9 | durable: true # seting 'durable' to false means this queue won't survive a server restart 10 | rabbitmqClusterReference: 11 | name: cluster-b # rabbitmqCluster must exist in the same namespace as this resource 12 | 13 | -------------------------------------------------------------------------------- /docs/examples/vault-support/rabbitmq-queue-d.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rabbitmq.com/v1beta1 2 | kind: Queue 3 | metadata: 4 | name: queue-d 5 | spec: 6 | name: queue-d # name of the queue 7 | type: quorum # without providing a queue type, rabbitmq creates a classic queue 8 | autoDelete: false 9 | durable: true # seting 'durable' to false means this queue won't survive a server restart 10 | rabbitmqClusterReference: 11 | name: cluster-b # rabbitmqCluster must exist in the same namespace as this resource 12 | 13 | -------------------------------------------------------------------------------- /config/crd/patches/webhook_in_shovels.yaml: -------------------------------------------------------------------------------- 1 | # The following patch enables conversion webhook for CRD 2 | apiVersion: apiextensions.k8s.io/v1 3 | kind: CustomResourceDefinition 4 | metadata: 5 | name: shovels.rabbitmq.com 6 | spec: 7 | conversion: 8 | strategy: Webhook 9 | webhook: 10 | conversionReviewVersions: ["v1", "v1beta1"] 11 | clientConfig: 12 | caBundle: Cg== 13 | service: 14 | namespace: system 15 | name: webhook-service 16 | path: /convert 17 | 18 | -------------------------------------------------------------------------------- /config/default/base/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | 4 | namespace: rabbitmq-system 5 | 6 | resources: 7 | - ../../crd 8 | - ../../manager 9 | - ../../webhook 10 | 11 | patches: 12 | - path: manager_webhook_patch.yaml 13 | - path: webhookcainjection_patch.yaml 14 | # TODO: remove webhook ca injection? we remove it using sed in Makefile 15 | 16 | images: 17 | - name: controller 18 | newName: rabbitmqoperator/messaging-topology-operator-dev 19 | newTag: latest 20 | -------------------------------------------------------------------------------- /config/rbac/superstream_editor_role.yaml: -------------------------------------------------------------------------------- 1 | # permissions for end users to edit superstreams. 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRole 4 | metadata: 5 | name: superstream-editor-role 6 | rules: 7 | - apiGroups: 8 | - rabbitmq.com 9 | resources: 10 | - superstreams 11 | verbs: 12 | - create 13 | - delete 14 | - get 15 | - list 16 | - patch 17 | - update 18 | - watch 19 | - apiGroups: 20 | - rabbitmq.com 21 | resources: 22 | - superstreams/status 23 | verbs: 24 | - get 25 | -------------------------------------------------------------------------------- /docs/examples/vault-support/rabbitmq-queue-a.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rabbitmq.com/v1beta1 2 | kind: Queue 3 | metadata: 4 | name: queue-a 5 | spec: 6 | name: queue-a # name of the queue 7 | type: quorum # without providing a queue type, rabbitmq creates a classic queue 8 | autoDelete: false 9 | durable: true # seting 'durable' to false means this queue won't survive a server restart 10 | rabbitmqClusterReference: 11 | name: cluster-vault-a # rabbitmqCluster must exist in the same namespace as this resource 12 | 13 | -------------------------------------------------------------------------------- /docs/examples/vault-support/rabbitmq-queue-c.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rabbitmq.com/v1beta1 2 | kind: Queue 3 | metadata: 4 | name: queue-c 5 | spec: 6 | name: queue-c # name of the queue 7 | type: quorum # without providing a queue type, rabbitmq creates a classic queue 8 | autoDelete: false 9 | durable: true # seting 'durable' to false means this queue won't survive a server restart 10 | rabbitmqClusterReference: 11 | name: cluster-vault-a # rabbitmqCluster must exist in the same namespace as this resource 12 | 13 | -------------------------------------------------------------------------------- /hack/NOTICE.go.txt: -------------------------------------------------------------------------------- 1 | /* 2 | RabbitMQ Messaging Topology Kubernetes Operator 3 | Copyright 2021 VMware, Inc. 4 | 5 | This product is licensed to you under the Mozilla Public License 2.0 license (the "License"). You may not use this product except in compliance with the Mozilla 2.0 License. 6 | 7 | This product may include a number of subcomponents with separate copyright notices and license terms. Your use of these subcomponents is subject to the terms and conditions of the subcomponent's license, as noted in the LICENSE file. 8 | */ 9 | -------------------------------------------------------------------------------- /docs/examples/exchanges/direct-exchange.yaml: -------------------------------------------------------------------------------- 1 | # More on direct exchange and other exchange types, see: https://www.rabbitmq.com/tutorials/amqp-concepts.html#exchanges. 2 | --- 3 | apiVersion: rabbitmq.com/v1beta1 4 | kind: Exchange 5 | metadata: 6 | name: direct # name of the object in kubernetes 7 | spec: 8 | name: direct-exchange # name of the exchange 9 | vhost: "/test-vhost" # default to '/' if not provided 10 | type: direct 11 | autoDelete: false 12 | durable: true 13 | rabbitmqClusterReference: 14 | name: test 15 | -------------------------------------------------------------------------------- /config/rbac/operatorpolicy_editor_role.yaml: -------------------------------------------------------------------------------- 1 | # permissions for end users to edit operator policies. 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRole 4 | metadata: 5 | name: operatorpolicy-editor-role 6 | rules: 7 | - apiGroups: 8 | - rabbitmq.com 9 | resources: 10 | - operatorpolicies 11 | verbs: 12 | - create 13 | - delete 14 | - get 15 | - list 16 | - patch 17 | - update 18 | - watch 19 | - apiGroups: 20 | - rabbitmq.com 21 | resources: 22 | - operatorpolicies/status 23 | verbs: 24 | - get 25 | -------------------------------------------------------------------------------- /config/rbac/topicpermission_editor_role.yaml: -------------------------------------------------------------------------------- 1 | # permissions for end users to edit topicpermissions. 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRole 4 | metadata: 5 | name: topicpermission-editor-role 6 | rules: 7 | - apiGroups: 8 | - rabbitmq.com 9 | resources: 10 | - topicpermissions 11 | verbs: 12 | - create 13 | - delete 14 | - get 15 | - list 16 | - patch 17 | - update 18 | - watch 19 | - apiGroups: 20 | - rabbitmq.com 21 | resources: 22 | - topicpermissions/status 23 | verbs: 24 | - get 25 | -------------------------------------------------------------------------------- /config/rbac/schemareplication_editor_role.yaml: -------------------------------------------------------------------------------- 1 | # permissions for end users to edit schemareplications. 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRole 4 | metadata: 5 | name: schemareplication-editor-role 6 | rules: 7 | - apiGroups: 8 | - rabbitmq.com 9 | resources: 10 | - schemareplications 11 | verbs: 12 | - create 13 | - delete 14 | - get 15 | - list 16 | - patch 17 | - update 18 | - watch 19 | - apiGroups: 20 | - rabbitmq.com 21 | resources: 22 | - schemareplications/status 23 | verbs: 24 | - get 25 | -------------------------------------------------------------------------------- /internal/managedresource/managedresource_suite_test.go: -------------------------------------------------------------------------------- 1 | package managedresource_test 2 | 3 | import ( 4 | topology "github.com/rabbitmq/messaging-topology-operator/api/v1beta1" 5 | "testing" 6 | 7 | . "github.com/onsi/ginkgo/v2" 8 | . "github.com/onsi/gomega" 9 | ) 10 | 11 | func TestResource(t *testing.T) { 12 | RegisterFailHandler(Fail) 13 | RunSpecs(t, "ManagedResource Suite") 14 | } 15 | 16 | var testRabbitmqClusterReference = &topology.RabbitmqClusterReference{ 17 | Name: "test-rabbit", 18 | Namespace: "example-namespace", 19 | } 20 | -------------------------------------------------------------------------------- /olm/bundle/bundle.Dockerfile: -------------------------------------------------------------------------------- 1 | FROM scratch 2 | 3 | LABEL operators.operatorframework.io.bundle.mediatype.v1=plain 4 | LABEL operators.operatorframework.io.bundle.manifests.v1=manifests/ 5 | LABEL operators.operatorframework.io.bundle.metadata.v1=metadata/ 6 | LABEL operators.operatorframework.io.bundle.package.v1=rabbitmq-messaging-topology-operator 7 | LABEL operators.operatorframework.io.bundle.channels.v1=stable 8 | LABEL operators.operatorframework.io.bundle.channel.default.v1=stable 9 | 10 | COPY manifests /manifests/ 11 | COPY metadata /metadata/ 12 | -------------------------------------------------------------------------------- /config/crd/patches/webhook_in_users.yaml: -------------------------------------------------------------------------------- 1 | # The following patch enables conversion webhook for CRD 2 | # CRD conversion requires k8s 1.13 or later. 3 | apiVersion: apiextensions.k8s.io/v1 4 | kind: CustomResourceDefinition 5 | metadata: 6 | name: users.rabbitmq.com 7 | spec: 8 | conversion: 9 | strategy: Webhook 10 | webhook: 11 | conversionReviewVersions: ["v1", "v1beta1"] 12 | clientConfig: 13 | caBundle: Cg== 14 | service: 15 | namespace: system 16 | name: webhook-service 17 | path: /convert 18 | -------------------------------------------------------------------------------- /config/crd/patches/webhook_in_bindings.yaml: -------------------------------------------------------------------------------- 1 | # The following patch enables conversion webhook for CRD 2 | # CRD conversion requires k8s 1.13 or later. 3 | apiVersion: apiextensions.k8s.io/v1 4 | kind: CustomResourceDefinition 5 | metadata: 6 | name: bindings.rabbitmq.com 7 | spec: 8 | conversion: 9 | strategy: Webhook 10 | webhook: 11 | conversionReviewVersions: ["v1", "v1beta1"] 12 | clientConfig: 13 | caBundle: Cg== 14 | service: 15 | namespace: system 16 | name: webhook-service 17 | path: /convert 18 | -------------------------------------------------------------------------------- /config/crd/patches/webhook_in_policies.yaml: -------------------------------------------------------------------------------- 1 | # The following patch enables conversion webhook for CRD 2 | # CRD conversion requires k8s 1.13 or later. 3 | apiVersion: apiextensions.k8s.io/v1 4 | kind: CustomResourceDefinition 5 | metadata: 6 | name: policies.rabbitmq.com 7 | spec: 8 | conversion: 9 | strategy: Webhook 10 | webhook: 11 | conversionReviewVersions: ["v1", "v1beta1"] 12 | clientConfig: 13 | caBundle: Cg== 14 | service: 15 | namespace: system 16 | name: webhook-service 17 | path: /convert 18 | -------------------------------------------------------------------------------- /config/crd/patches/webhook_in_vhosts.yaml: -------------------------------------------------------------------------------- 1 | # The following patch enables conversion webhook for CRD 2 | # CRD conversion requires k8s 1.13 or later. 3 | apiVersion: apiextensions.k8s.io/v1 4 | kind: CustomResourceDefinition 5 | metadata: 6 | name: vhosts.rabbitmq.com 7 | spec: 8 | conversion: 9 | strategy: Webhook 10 | webhook: 11 | conversionReviewVersions: ["v1", "v1beta1"] 12 | clientConfig: 13 | caBundle: Cg== 14 | service: 15 | namespace: system 16 | name: webhook-service 17 | path: /convert 18 | -------------------------------------------------------------------------------- /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 | group: apiextensions.k8s.io 8 | path: spec/conversion/webhookClientConfig/service/name 9 | 10 | namespace: 11 | - kind: CustomResourceDefinition 12 | group: apiextensions.k8s.io 13 | path: spec/conversion/webhookClientConfig/service/namespace 14 | create: false 15 | 16 | varReference: 17 | - path: metadata/annotations 18 | -------------------------------------------------------------------------------- /config/crd/patches/webhook_in_exchanges.yaml: -------------------------------------------------------------------------------- 1 | # The following patch enables conversion webhook for CRD 2 | # CRD conversion requires k8s 1.13 or later. 3 | apiVersion: apiextensions.k8s.io/v1 4 | kind: CustomResourceDefinition 5 | metadata: 6 | name: exchanges.rabbitmq.com 7 | spec: 8 | conversion: 9 | strategy: Webhook 10 | webhook: 11 | conversionReviewVersions: ["v1", "v1beta1"] 12 | clientConfig: 13 | caBundle: Cg== 14 | service: 15 | namespace: system 16 | name: webhook-service 17 | path: /convert 18 | -------------------------------------------------------------------------------- /config/crd/patches/webhook_in_permissions.yaml: -------------------------------------------------------------------------------- 1 | # The following patch enables conversion webhook for CRD 2 | # CRD conversion requires k8s 1.13 or later. 3 | apiVersion: apiextensions.k8s.io/v1 4 | kind: CustomResourceDefinition 5 | metadata: 6 | name: permissions.rabbitmq.com 7 | spec: 8 | conversion: 9 | strategy: Webhook 10 | webhook: 11 | conversionReviewVersions: ["v1", "v1beta1"] 12 | clientConfig: 13 | caBundle: Cg== 14 | service: 15 | namespace: system 16 | name: webhook-service 17 | path: /convert 18 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/question.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Question 3 | about: Guidance to submit questions 4 | title: '' 5 | labels: question 6 | assignees: '' 7 | 8 | --- 9 | 10 | **For questions**, please start a discussion in [RabbitMQ Discussions](https://github.com/rabbitmq/rabbitmq-server/discussions) 11 | or send an email to [RabbitMQ Mailing list](https://groups.google.com/g/rabbitmq-users), stating that your question is regarding 12 | RabbitMQ Cluster Operator. 13 | 14 | There's also a `#kubernetes` channel in [RabbitMQ community Slack](https://rabbitmq-slack.herokuapp.com/). 15 | -------------------------------------------------------------------------------- /config/crd/patches/webhook_in_operatorpolicies.yaml: -------------------------------------------------------------------------------- 1 | # The following patch enables conversion webhook for CRD 2 | # CRD conversion requires k8s 1.13 or later. 3 | apiVersion: apiextensions.k8s.io/v1 4 | kind: CustomResourceDefinition 5 | metadata: 6 | name: operatorpolicies.rabbitmq.com 7 | spec: 8 | conversion: 9 | strategy: Webhook 10 | webhook: 11 | conversionReviewVersions: ["v1", "v1beta1"] 12 | clientConfig: 13 | caBundle: Cg== 14 | service: 15 | namespace: system 16 | name: webhook-service 17 | path: /convert 18 | -------------------------------------------------------------------------------- /config/crd/patches/webhook_in_schemareplications.yaml: -------------------------------------------------------------------------------- 1 | # The following patch enables conversion webhook for CRD 2 | # CRD conversion requires k8s 1.13 or later. 3 | apiVersion: apiextensions.k8s.io/v1 4 | kind: CustomResourceDefinition 5 | metadata: 6 | name: schemareplications.rabbitmq.com 7 | spec: 8 | conversion: 9 | strategy: Webhook 10 | webhook: 11 | conversionReviewVersions: ["v1", "v1beta1"] 12 | clientConfig: 13 | caBundle: Cg== 14 | service: 15 | namespace: system 16 | name: webhook-service 17 | path: /convert 18 | -------------------------------------------------------------------------------- /config/namespace/kustomization.yaml: -------------------------------------------------------------------------------- 1 | # RabbitMQ Messaging Topology Kubernetes Operator 2 | 3 | # Copyright 2021 VMware, Inc. 4 | # 5 | # This product is licensed to you under the Mozilla Public license, Version 2.0 (the "License"). You may not use this product except in compliance with the Mozilla Public License. 6 | # 7 | # This product may include a number of subcomponents with separate copyright notices and license terms. Your use of these subcomponents is subject to the terms and conditions of the subcomponent's license, as noted in the LICENSE file. 8 | 9 | resources: 10 | - namespace.yaml 11 | -------------------------------------------------------------------------------- /docs/examples/shovels/queues.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: rabbitmq.com/v1beta1 3 | kind: Queue 4 | metadata: 5 | name: source-queue 6 | spec: 7 | name: source-queue 8 | vhost: "source" 9 | autoDelete: false 10 | durable: true 11 | rabbitmqClusterReference: 12 | name: example-rabbit 13 | --- 14 | apiVersion: rabbitmq.com/v1beta1 15 | kind: Queue 16 | metadata: 17 | name: destination-queue 18 | spec: 19 | name: destination-queue 20 | vhost: "destination" 21 | type: quorum 22 | autoDelete: false 23 | durable: true 24 | rabbitmqClusterReference: 25 | name: example-rabbit 26 | -------------------------------------------------------------------------------- /docs/examples/federations/bindings.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: rabbitmq.com/v1beta1 3 | kind: Binding 4 | metadata: 5 | name: upstream-binding 6 | spec: 7 | vhost: "upstream" 8 | source: fanout 9 | destination: upstream-queue 10 | destinationType: queue 11 | rabbitmqClusterReference: 12 | name: example-rabbit 13 | --- 14 | apiVersion: rabbitmq.com/v1beta1 15 | kind: Binding 16 | metadata: 17 | name: downstream-binding 18 | spec: 19 | vhost: "downstream" 20 | source: fanout 21 | destination: downstream-queue 22 | destinationType: queue 23 | rabbitmqClusterReference: 24 | name: example-rabbit 25 | -------------------------------------------------------------------------------- /docs/examples/federations/queues.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: rabbitmq.com/v1beta1 3 | kind: Queue 4 | metadata: 5 | name: downstream-queue 6 | spec: 7 | name: downstream-queue 8 | vhost: "downstream" 9 | type: quorum 10 | autoDelete: false 11 | durable: true 12 | rabbitmqClusterReference: 13 | name: example-rabbit 14 | --- 15 | apiVersion: rabbitmq.com/v1beta1 16 | kind: Queue 17 | metadata: 18 | name: upstream-queue 19 | spec: 20 | name: upstream-queue 21 | vhost: "upstream" 22 | type: quorum 23 | autoDelete: false 24 | durable: true 25 | rabbitmqClusterReference: 26 | name: example-rabbit 27 | -------------------------------------------------------------------------------- /docs/examples/operator-policies/operator-policy.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rabbitmq.com/v1beta1 2 | kind: OperatorPolicy 3 | metadata: 4 | name: operator-policy-example 5 | spec: 6 | name: my-operator-policy # name of the operator policy 7 | vhost: "/" # default to '/' if not provided 8 | pattern: "^abc" # regex used to match queues 9 | applyTo: "queues" # apply to all types of 'queues' (default), 'classic_queue', quorum_queues', or 'streams' 10 | definition: # policy definition 11 | expires: 1800000 12 | rabbitmqClusterReference: 13 | name: test # rabbitmqCluster must exist in the same namespace as this resource 14 | -------------------------------------------------------------------------------- /internal/tools/tools.go: -------------------------------------------------------------------------------- 1 | //go:build tools 2 | 3 | package tools 4 | 5 | import ( 6 | _ "github.com/elastic/crd-ref-docs" 7 | _ "github.com/maxbrunsfeld/counterfeiter/v6" 8 | _ "github.com/onsi/ginkgo/v2/ginkgo" 9 | _ "github.com/sclevine/yj" 10 | _ "golang.org/x/vuln/cmd/govulncheck" 11 | _ "sigs.k8s.io/controller-runtime/tools/setup-envtest" 12 | _ "sigs.k8s.io/controller-tools/cmd/controller-gen" 13 | _ "sigs.k8s.io/kustomize/kustomize/v5" 14 | 15 | // These are required for the generated clients. 16 | _ "k8s.io/client-go/discovery/fake" 17 | _ "k8s.io/code-generator" 18 | _ "k8s.io/kube-openapi/cmd/openapi-gen" 19 | ) 20 | -------------------------------------------------------------------------------- /config/default/base/manager_webhook_patch.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: operator 5 | namespace: system 6 | spec: 7 | template: 8 | spec: 9 | containers: 10 | - name: manager 11 | ports: 12 | - containerPort: 9443 13 | name: webhook-server 14 | protocol: TCP 15 | volumeMounts: 16 | - mountPath: /tmp/k8s-webhook-server/serving-certs 17 | name: cert 18 | readOnly: true 19 | volumes: 20 | - name: cert 21 | secret: 22 | defaultMode: 420 23 | secretName: webhook-server-cert 24 | -------------------------------------------------------------------------------- /config/default/manager_webhook_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 | ports: 12 | - containerPort: 9443 13 | name: webhook-server 14 | protocol: TCP 15 | volumeMounts: 16 | - mountPath: /tmp/k8s-webhook-server/serving-certs 17 | name: cert 18 | readOnly: true 19 | volumes: 20 | - name: cert 21 | secret: 22 | defaultMode: 420 23 | secretName: webhook-server-cert 24 | -------------------------------------------------------------------------------- /docs/examples/users/user.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rabbitmq.com/v1beta1 2 | kind: User 3 | metadata: 4 | name: user-sample 5 | spec: 6 | tags: 7 | - management # available tags are 'management', 'policymaker', 'monitoring' and 'administrator' 8 | rabbitmqClusterReference: 9 | name: sample # rabbitmqCluster must exist in the same namespace as this resource 10 | # status: 11 | # conditions: 12 | # - lastTransitionTime: "" 13 | # status: "True" # true, false, or unknown 14 | # type: Ready 15 | # Reason: "SuccessfulCreateOrUpdate" # status false result in reason FailedCreateOrUpdate 16 | # Message: "" # set when status is false 17 | -------------------------------------------------------------------------------- /olm/assets/operator-group.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: Namespace 4 | metadata: 5 | name: ns-1 6 | spec: {} 7 | --- 8 | apiVersion: operators.coreos.com/v1alpha2 9 | kind: OperatorGroup 10 | metadata: 11 | name: my-group 12 | #! Subscriptions cannot install operators provided by CatalogSources 13 | #! that are not in the same namespace unless the CatalogSource is 14 | #! created in the olm namespace. 15 | #! https://olm.operatorframework.io/docs/troubleshooting/subscription/#a-subscription-in-namespace-x-cant-install-operators-from-a-catalogsource-in-namespace-y 16 | namespace: ns-1 17 | spec: 18 | targetNamespaces: 19 | - ns-1 20 | -------------------------------------------------------------------------------- /api/v1beta1/topology_resource.go: -------------------------------------------------------------------------------- 1 | package v1beta1 2 | 3 | import "sigs.k8s.io/controller-runtime/pkg/client" 4 | 5 | var _ TopologyResource = &Binding{} 6 | var _ TopologyResource = &Exchange{} 7 | var _ TopologyResource = &Federation{} 8 | var _ TopologyResource = &Permission{} 9 | var _ TopologyResource = &Queue{} 10 | var _ TopologyResource = &SchemaReplication{} 11 | var _ TopologyResource = &Shovel{} 12 | var _ TopologyResource = &User{} 13 | var _ TopologyResource = &Vhost{} 14 | 15 | // +k8s:deepcopy-gen=false 16 | type TopologyResource interface { 17 | client.Object 18 | RabbitReference() RabbitmqClusterReference 19 | SetStatusConditions([]Condition) 20 | } 21 | -------------------------------------------------------------------------------- /docs/examples/queues/stream-queue.yaml: -------------------------------------------------------------------------------- 1 | ## for more info about stream queue: https://www.rabbitmq.com/streams.html 2 | apiVersion: rabbitmq.com/v1beta1 3 | kind: Queue 4 | metadata: 5 | name: my-stream-queue 6 | spec: 7 | name: "my_stream_queue" # name of the queue 8 | type: stream # without providing a queue type, rabbitmq creates a classic queue 9 | durable: true # mandatory for stream queues 10 | arguments: 11 | x-max-length-bytes: 1000000000 ## setting the retention policy 12 | deletionPolicy: retain # delete or retain; default to delete; 13 | rabbitmqClusterReference: 14 | name: test # rabbitmqCluster must exist in the same namespace as this resource 15 | -------------------------------------------------------------------------------- /docs/examples/federations/exchanges.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: rabbitmq.com/v1beta1 3 | kind: Exchange 4 | metadata: 5 | name: fanout-upstream 6 | spec: 7 | name: fanout 8 | vhost: "upstream" # default to '/' if not provided 9 | type: fanout # default to 'direct' if not provided 10 | autoDelete: false 11 | durable: true 12 | rabbitmqClusterReference: 13 | name: example-rabbit 14 | --- 15 | apiVersion: rabbitmq.com/v1beta1 16 | kind: Exchange 17 | metadata: 18 | name: fanout-downstream 19 | spec: 20 | name: fanout 21 | vhost: "downstream" 22 | type: fanout 23 | autoDelete: false 24 | durable: true 25 | rabbitmqClusterReference: 26 | name: example-rabbit 27 | -------------------------------------------------------------------------------- /docs/examples/shovels/shovel.yaml: -------------------------------------------------------------------------------- 1 | # for more information, see: https://www.rabbitmq.com/shovel-dynamic.html 2 | --- 3 | apiVersion: rabbitmq.com/v1beta1 4 | kind: Shovel 5 | metadata: 6 | name: shovel-example 7 | spec: 8 | name: "shovel-example" 9 | uriSecret: 10 | name: shovel-secret 11 | srcQueue: "source-queue" 12 | srcConsumerArgs: # optional consume arguments 13 | x-priority: 10 14 | destQueue: "destination-queue" 15 | destPublishProperties: # optional map of properties to overwrite when shovelling messages 16 | delivery_mode: 2 17 | deletionPolicy: retain # delete or retain; default to delete; 18 | rabbitmqClusterReference: 19 | name: example-rabbit 20 | -------------------------------------------------------------------------------- /config/default/overlays/kind/kustomization.yaml: -------------------------------------------------------------------------------- 1 | # RabbitMQ Messaging Topology Kubernetes Operator 2 | 3 | # Copyright 2021 VMware, Inc. 4 | # 5 | # This product is licensed to you under the Mozilla Public license, Version 2.0 (the "License"). You may not use this product except in compliance with the Mozilla Public License. 6 | # 7 | # This product may include a number of subcomponents with separate copyright notices and license terms. Your use of these subcomponents is subject to the terms and conditions of the subcomponent's license, as noted in the LICENSE file. 8 | namespace: rabbitmq-system 9 | 10 | resources: 11 | - ../cert-manager 12 | 13 | patches: 14 | - path: manager_image_patch.yaml -------------------------------------------------------------------------------- /config/default/overlays/dev/kustomization.yaml: -------------------------------------------------------------------------------- 1 | # RabbitMQ Messaging Topology Kubernetes Operator 2 | 3 | # Copyright 2021 VMware, Inc. 4 | # 5 | # This product is licensed to you under the Mozilla Public license, Version 2.0 (the "License"). You may not use this product except in compliance with the Mozilla Public License. 6 | # 7 | # This product may include a number of subcomponents with separate copyright notices and license terms. Your use of these subcomponents is subject to the terms and conditions of the subcomponent's license, as noted in the LICENSE file. 8 | namespace: rabbitmq-system 9 | 10 | resources: 11 | - ../cert-manager 12 | 13 | patches: 14 | - path: manager_image_patch.yaml 15 | -------------------------------------------------------------------------------- /pkg/generated/clientset/versioned/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | RabbitMQ Messaging Topology Kubernetes Operator 3 | Copyright 2021 VMware, Inc. 4 | 5 | This product is licensed to you under the Mozilla Public License 2.0 license (the "License"). You may not use this product except in compliance with the Mozilla 2.0 License. 6 | 7 | This product may include a number of subcomponents with separate copyright notices and license terms. Your use of these subcomponents is subject to the terms and conditions of the subcomponent's license, as noted in the LICENSE file. 8 | */ 9 | 10 | // Code generated by client-gen. DO NOT EDIT. 11 | 12 | // This package has the automatically generated clientset. 13 | package versioned 14 | -------------------------------------------------------------------------------- /pkg/generated/clientset/versioned/fake/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | RabbitMQ Messaging Topology Kubernetes Operator 3 | Copyright 2021 VMware, Inc. 4 | 5 | This product is licensed to you under the Mozilla Public License 2.0 license (the "License"). You may not use this product except in compliance with the Mozilla 2.0 License. 6 | 7 | This product may include a number of subcomponents with separate copyright notices and license terms. Your use of these subcomponents is subject to the terms and conditions of the subcomponent's license, as noted in the LICENSE file. 8 | */ 9 | 10 | // Code generated by client-gen. DO NOT EDIT. 11 | 12 | // This package has the automatically generated fake clientset. 13 | package fake 14 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /config/default/webhookcainjection_patch.yaml: -------------------------------------------------------------------------------- 1 | # This patch add annotation to admission webhook config and 2 | # the variables $(CERTIFICATE_NAMESPACE) and $(CERTIFICATE_NAME) will be substituted by kustomize. 3 | apiVersion: admissionregistration.k8s.io/v1 4 | kind: MutatingWebhookConfiguration 5 | metadata: 6 | name: mutating-webhook-configuration 7 | annotations: 8 | cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME) 9 | --- 10 | apiVersion: admissionregistration.k8s.io/v1 11 | kind: ValidatingWebhookConfiguration 12 | metadata: 13 | name: validating-webhook-configuration 14 | annotations: 15 | cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME) 16 | -------------------------------------------------------------------------------- /olm/assets/subscription.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: operators.coreos.com/v1alpha1 3 | kind: Subscription 4 | metadata: 5 | name: sub-to-rmq-messaging-topology-operator 6 | #! Subscriptions cannot install operators provided by CatalogSources 7 | #! that are not in the same namespace unless the CatalogSource is 8 | #! created in the olm namespace. 9 | #! https://olm.operatorframework.io/docs/troubleshooting/subscription/#a-subscription-in-namespace-x-cant-install-operators-from-a-catalogsource-in-namespace-y 10 | namespace: ns-1 11 | spec: 12 | channel: preview 13 | name: rabbitmq-messaging-topology-operator 14 | source: cool-catalog 15 | sourceNamespace: ns-1 16 | installPlanApproval: Automatic 17 | -------------------------------------------------------------------------------- /pkg/generated/clientset/versioned/scheme/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | RabbitMQ Messaging Topology Kubernetes Operator 3 | Copyright 2021 VMware, Inc. 4 | 5 | This product is licensed to you under the Mozilla Public License 2.0 license (the "License"). You may not use this product except in compliance with the Mozilla 2.0 License. 6 | 7 | This product may include a number of subcomponents with separate copyright notices and license terms. Your use of these subcomponents is subject to the terms and conditions of the subcomponent's license, as noted in the LICENSE file. 8 | */ 9 | 10 | // Code generated by client-gen. DO NOT EDIT. 11 | 12 | // This package contains the scheme of the automatically generated clientset. 13 | package scheme 14 | -------------------------------------------------------------------------------- /pkg/generated/clientset/versioned/typed/rabbitmq.com/v1alpha1/fake/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | RabbitMQ Messaging Topology Kubernetes Operator 3 | Copyright 2021 VMware, Inc. 4 | 5 | This product is licensed to you under the Mozilla Public License 2.0 license (the "License"). You may not use this product except in compliance with the Mozilla 2.0 License. 6 | 7 | This product may include a number of subcomponents with separate copyright notices and license terms. Your use of these subcomponents is subject to the terms and conditions of the subcomponent's license, as noted in the LICENSE file. 8 | */ 9 | 10 | // Code generated by client-gen. DO NOT EDIT. 11 | 12 | // Package fake has the automatically generated clients. 13 | package fake 14 | -------------------------------------------------------------------------------- /pkg/generated/clientset/versioned/typed/rabbitmq.com/v1alpha1/generated_expansion.go: -------------------------------------------------------------------------------- 1 | /* 2 | RabbitMQ Messaging Topology Kubernetes Operator 3 | Copyright 2021 VMware, Inc. 4 | 5 | This product is licensed to you under the Mozilla Public License 2.0 license (the "License"). You may not use this product except in compliance with the Mozilla 2.0 License. 6 | 7 | This product may include a number of subcomponents with separate copyright notices and license terms. Your use of these subcomponents is subject to the terms and conditions of the subcomponent's license, as noted in the LICENSE file. 8 | */ 9 | 10 | // Code generated by client-gen. DO NOT EDIT. 11 | 12 | package v1alpha1 13 | 14 | type SuperStreamExpansion interface{} 15 | -------------------------------------------------------------------------------- /pkg/generated/clientset/versioned/typed/rabbitmq.com/v1beta1/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | RabbitMQ Messaging Topology Kubernetes Operator 3 | Copyright 2021 VMware, Inc. 4 | 5 | This product is licensed to you under the Mozilla Public License 2.0 license (the "License"). You may not use this product except in compliance with the Mozilla 2.0 License. 6 | 7 | This product may include a number of subcomponents with separate copyright notices and license terms. Your use of these subcomponents is subject to the terms and conditions of the subcomponent's license, as noted in the LICENSE file. 8 | */ 9 | 10 | // Code generated by client-gen. DO NOT EDIT. 11 | 12 | // This package has the automatically generated typed clients. 13 | package v1beta1 14 | -------------------------------------------------------------------------------- /pkg/generated/clientset/versioned/typed/rabbitmq.com/v1beta1/fake/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | RabbitMQ Messaging Topology Kubernetes Operator 3 | Copyright 2021 VMware, Inc. 4 | 5 | This product is licensed to you under the Mozilla Public License 2.0 license (the "License"). You may not use this product except in compliance with the Mozilla 2.0 License. 6 | 7 | This product may include a number of subcomponents with separate copyright notices and license terms. Your use of these subcomponents is subject to the terms and conditions of the subcomponent's license, as noted in the LICENSE file. 8 | */ 9 | 10 | // Code generated by client-gen. DO NOT EDIT. 11 | 12 | // Package fake has the automatically generated clients. 13 | package fake 14 | -------------------------------------------------------------------------------- /docs/examples/permissions/permission.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rabbitmq.com/v1beta1 2 | kind: Permission 3 | metadata: 4 | name: testuser-permission 5 | spec: 6 | vhost: "/" # name of a vhost 7 | user: "test-user" # name of a RabbitMQ user 8 | permissions: 9 | write: ".*" 10 | configure: ".*" 11 | read: ".*" 12 | rabbitmqClusterReference: 13 | name: sample # rabbitmqCluster must exist in the same namespace as this resource 14 | # status: 15 | # conditions: 16 | # - lastTransitionTime: "" 17 | # status: "True" # true, false, or unknown 18 | # type: Ready 19 | # Reason: "SuccessfulCreateOrUpdate" # status false result in reason FailedCreateOrUpdate 20 | # Message: "" # set when status is false 21 | 22 | -------------------------------------------------------------------------------- /pkg/generated/clientset/versioned/typed/rabbitmq.com/v1alpha1/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | RabbitMQ Messaging Topology Kubernetes Operator 3 | Copyright 2021 VMware, Inc. 4 | 5 | This product is licensed to you under the Mozilla Public License 2.0 license (the "License"). You may not use this product except in compliance with the Mozilla 2.0 License. 6 | 7 | This product may include a number of subcomponents with separate copyright notices and license terms. Your use of these subcomponents is subject to the terms and conditions of the subcomponent's license, as noted in the LICENSE file. 8 | */ 9 | 10 | // Code generated by client-gen. DO NOT EDIT. 11 | 12 | // This package has the automatically generated typed clients. 13 | package v1alpha1 14 | -------------------------------------------------------------------------------- /olm/bundle/templates/topology-operator-namespace-scope-overlay.yml: -------------------------------------------------------------------------------- 1 | #@ load("@ytt:data", "data") 2 | #@ load("@ytt:overlay", "overlay") 3 | #@ deployment = overlay.subset({"kind": "Deployment"}) 4 | #@ topology_operator = overlay.subset({"metadata": {"name": "messaging-topology-operator"}}) 5 | #@overlay/match by=overlay.and_op(deployment, topology_operator),expects="1+" 6 | --- 7 | spec: 8 | template: 9 | spec: 10 | containers: 11 | #@overlay/match by=overlay.subset({"name": "manager"}),expects="1+" 12 | - 13 | #@overlay/match missing_ok=True 14 | env: 15 | - name: OPERATOR_SCOPE_NAMESPACE 16 | valueFrom: 17 | fieldRef: 18 | fieldPath: metadata.annotations['olm.targetNamespaces'] 19 | -------------------------------------------------------------------------------- /config/webhook/kustomizeconfig.yaml: -------------------------------------------------------------------------------- 1 | nameReference: 2 | - kind: Service 3 | version: v1 4 | fieldSpecs: 5 | - kind: MutatingWebhookConfiguration 6 | group: admissionregistration.k8s.io 7 | path: webhooks/clientConfig/service/name 8 | - kind: ValidatingWebhookConfiguration 9 | group: admissionregistration.k8s.io 10 | path: webhooks/clientConfig/service/name 11 | 12 | namespace: 13 | - kind: MutatingWebhookConfiguration 14 | group: admissionregistration.k8s.io 15 | path: webhooks/clientConfig/service/namespace 16 | create: true 17 | - kind: ValidatingWebhookConfiguration 18 | group: admissionregistration.k8s.io 19 | path: webhooks/clientConfig/service/namespace 20 | create: true 21 | 22 | varReference: 23 | - path: metadata/annotations 24 | -------------------------------------------------------------------------------- /docs/examples/users/userWithPasswordHash.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Secret 3 | metadata: 4 | name: credentials-secret 5 | type: Opaque 6 | stringData: 7 | username: import-user-sample 8 | passwordHash: SjWbNXaNEwcoOOZWxG6J1HCF5P83lUavsCto+wh1s9zdOfoZ/CPv6l/SSdK3RC2+1QWmJGdYt5740j3ZLf/0RbpusNc= # SHA-512 hash of "some-password" 9 | --- 10 | apiVersion: rabbitmq.com/v1beta1 11 | kind: User 12 | metadata: 13 | name: import-user-sample 14 | spec: 15 | tags: 16 | - management # available tags are 'management', 'policymaker', 'monitoring' and 'administrator' 17 | - policymaker 18 | rabbitmqClusterReference: 19 | name: test # rabbitmqCluster must exist in the same namespace as this resource 20 | importCredentialsSecret: 21 | name: credentials-secret 22 | -------------------------------------------------------------------------------- /olm/assets/catalog-source.yaml: -------------------------------------------------------------------------------- 1 | #@ load("@ytt:data", "data") 2 | 3 | --- 4 | apiVersion: operators.coreos.com/v1alpha1 5 | kind: CatalogSource 6 | metadata: 7 | name: cool-catalog 8 | #! Subscriptions cannot install operators provided by CatalogSources 9 | #! that are not in the same namespace unless the CatalogSource is 10 | #! created in the olm namespace. 11 | #! https://olm.operatorframework.io/docs/troubleshooting/subscription/#a-subscription-in-namespace-x-cant-install-operators-from-a-catalogsource-in-namespace-y 12 | namespace: ns-1 13 | spec: 14 | sourceType: grpc 15 | image: #@ data.values.image 16 | displayName: Test catalog 17 | publisher: RabbitMQ Topology Operator Test 18 | updateStrategy: 19 | registryPoll: 20 | interval: 10m 21 | -------------------------------------------------------------------------------- /docs/examples/vhosts/vhost.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rabbitmq.com/v1beta1 2 | kind: Vhost 3 | metadata: 4 | name: test-vhost 5 | spec: 6 | name: test-vhost # vhost name; required and cannot be updated 7 | defaultQueueType: quorum # default queue type for this vhost; require RabbitMQ version 3.11.12 or above 8 | deletionPolicy: retain # delete or retain; default to delete; 9 | rabbitmqClusterReference: 10 | name: test # rabbitmqCluster must exist in the same namespace as this resource 11 | # status: 12 | # conditions: 13 | # - lastTransitionTime: "" 14 | # status: "True" # true, false, or unknown 15 | # type: Ready 16 | # Reason: "SuccessfulCreateOrUpdate" # status false result in reason FailedCreateOrUpdate 17 | # Message: "" # set when status is false 18 | -------------------------------------------------------------------------------- /config/certmanager/certificate.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: cert-manager.io/v1 2 | kind: Issuer 3 | metadata: 4 | name: selfsigned-issuer 5 | namespace: system 6 | spec: 7 | selfSigned: {} 8 | --- 9 | apiVersion: cert-manager.io/v1 10 | kind: Certificate 11 | metadata: 12 | name: serving-cert # this name should match the one appeared in kustomizeconfig.yaml 13 | namespace: system 14 | spec: 15 | # $(SERVICE_NAME) and $(SERVICE_NAMESPACE) will be substituted by kustomize 16 | dnsNames: 17 | - $(SERVICE_NAME).$(SERVICE_NAMESPACE).svc 18 | - $(SERVICE_NAME).$(SERVICE_NAMESPACE).svc.cluster.local 19 | issuerRef: 20 | kind: Issuer 21 | name: selfsigned-issuer 22 | secretName: webhook-server-cert # this secret will not be prefixed, since it's not managed by kustomize 23 | -------------------------------------------------------------------------------- /docs/examples/README.md: -------------------------------------------------------------------------------- 1 | ## Messaging Topology Operator examples 2 | 3 | This section contains examples on how to create RabbitMQ topology objects using the Messaging Topology Operator. 4 | 5 | ### Usage 6 | 7 | These examples are intended to showcase common use cases for the Messaging Topology Operator. Some examples can be used directly, but 8 | properties such as resources names and namespaces need to be edited for your own environment. For a more comprehensive guide on how 9 | to use the Operator, see the [Using Messaging Topology Operator](https://www.rabbitmq.com/kubernetes/operator/using-topology-operator.html) 10 | guide on rabbitmq.com and the Operator [API reference](https://github.com/rabbitmq/messaging-topology-operator/blob/main/docs/api/rabbitmq.com.ref.asciidoc). 11 | 12 | -------------------------------------------------------------------------------- /config/rbac/leader_election_role.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: Role 3 | metadata: 4 | name: leader-election-role 5 | labels: 6 | app.kubernetes.io/name: rabbitmq-cluster-operator 7 | app.kubernetes.io/component: rabbitmq-operator 8 | app.kubernetes.io/part-of: rabbitmq 9 | rules: 10 | - apiGroups: 11 | - coordination.k8s.io 12 | resources: 13 | - leases 14 | verbs: 15 | - get 16 | - list 17 | - watch 18 | - create 19 | - update 20 | - patch 21 | - delete 22 | - apiGroups: 23 | - "" 24 | resources: 25 | - events 26 | verbs: 27 | - create 28 | - apiGroups: 29 | - "" 30 | resources: 31 | - configmaps 32 | verbs: 33 | - get 34 | - list 35 | - watch 36 | - create 37 | - update 38 | - patch 39 | - delete 40 | -------------------------------------------------------------------------------- /docs/examples/non-operator-managed-rabbitmq/queue.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: Secret 4 | metadata: 5 | name: my-rabbitmq-creds 6 | type: Opaque 7 | stringData: 8 | username: a-user # an existing user 9 | password: a-secure-password 10 | uri: https://my.rabbit:15672 # uri for the management api; when scheme is not provided in uri, operator defalts to 'http' 11 | --- 12 | apiVersion: rabbitmq.com/v1beta1 13 | kind: Queue 14 | metadata: 15 | name: qq-example 16 | spec: 17 | name: qq 18 | type: quorum 19 | autoDelete: false 20 | durable: true 21 | rabbitmqClusterReference: 22 | connectionSecret: 23 | name: my-rabbitmq-creds # provided instead of a rabbitmqcluster name; secret must contain keys username, password and uri, and be in the same namespace as the object 24 | -------------------------------------------------------------------------------- /internal/managedresource/managedresource_builder.go: -------------------------------------------------------------------------------- 1 | package managedresource 2 | 3 | import ( 4 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 5 | "k8s.io/apimachinery/pkg/runtime" 6 | "sigs.k8s.io/controller-runtime/pkg/client" 7 | ) 8 | 9 | const ( 10 | AnnotationSuperStream = "rabbitmq.com/super-stream" 11 | AnnotationSuperStreamRoutingKey = "rabbitmq.com/super-stream-routing-key" 12 | ) 13 | 14 | type Builder struct { 15 | ObjectOwner metav1.Object 16 | Scheme *runtime.Scheme 17 | } 18 | 19 | type ResourceBuilder interface { 20 | Build() (client.Object, error) 21 | Update(client.Object) error 22 | ResourceType() string 23 | } 24 | 25 | func (builder *Builder) GenerateChildResourceName(suffix string) string { 26 | return builder.ObjectOwner.GetName() + suffix 27 | } 28 | -------------------------------------------------------------------------------- /config/namespace/namespace.yaml: -------------------------------------------------------------------------------- 1 | # RabbitMQ Messaging Topology Kubernetes Operator 2 | 3 | # Copyright 2021 VMware, Inc. 4 | # 5 | # This product is licensed to you under the Mozilla Public license, Version 2.0 (the "License"). You may not use this product except in compliance with the Mozilla Public License. 6 | # 7 | # This product may include a number of subcomponents with separate copyright notices and license terms. Your use of these subcomponents is subject to the terms and conditions of the subcomponent's license, as noted in the LICENSE file. 8 | 9 | apiVersion: v1 10 | kind: Namespace 11 | metadata: 12 | name: rabbitmq-system 13 | labels: 14 | app.kubernetes.io/name: rabbitmq-system 15 | app.kubernetes.io/component: rabbitmq-operator 16 | app.kubernetes.io/part-of: rabbitmq 17 | 18 | -------------------------------------------------------------------------------- /internal/internal_suite_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | RabbitMQ Messaging Topology Kubernetes Operator 3 | Copyright 2021 VMware, Inc. 4 | 5 | This product is licensed to you under the Mozilla Public License 2.0 license (the "License"). You may not use this product except in compliance with the Mozilla 2.0 License. 6 | 7 | This product may include a number of subcomponents with separate copyright notices and license terms. Your use of these subcomponents is subject to the terms and conditions of the subcomponent's license, as noted in the LICENSE file. 8 | */ 9 | 10 | package internal_test 11 | 12 | import ( 13 | "testing" 14 | 15 | . "github.com/onsi/ginkgo/v2" 16 | . "github.com/onsi/gomega" 17 | ) 18 | 19 | func TestResource(t *testing.T) { 20 | RegisterFailHandler(Fail) 21 | RunSpecs(t, "Internal Suite") 22 | } 23 | -------------------------------------------------------------------------------- /docs/examples/users/passwordlessUser.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Secret 3 | metadata: 4 | name: credentials-secret 5 | type: Opaque 6 | stringData: 7 | username: import-user-sample 8 | passwordHash: "" # The user will not have a valid password. Login attempts with any password will be rejected 9 | password: anythingreally # This value will be ignored, because `passwordHash` takes precedence 10 | --- 11 | apiVersion: rabbitmq.com/v1beta1 12 | kind: User 13 | metadata: 14 | name: import-user-sample 15 | spec: 16 | tags: 17 | - management # available tags are 'management', 'policymaker', 'monitoring' and 'administrator' 18 | - policymaker 19 | rabbitmqClusterReference: 20 | name: test # rabbitmqCluster must exist in the same namespace as this resource 21 | importCredentialsSecret: 22 | name: credentials-secret 23 | -------------------------------------------------------------------------------- /docs/examples/bindings/binding.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rabbitmq.com/v1beta1 2 | kind: Binding 3 | metadata: 4 | name: binding 5 | spec: 6 | vhost: "/test-vhost" # default to '/' if not provided 7 | source: test # an existing exchange 8 | destination: test # an existing queue or exchange 9 | destinationType: queue # can be 'queue' or 'exchange' 10 | routingKey: "a-routing-key" 11 | arguments: 12 | myargument: true 13 | anotherargument: 12345 14 | rabbitmqClusterReference: 15 | name: test # rabbitmqCluster must exist in the same namespace as this resource 16 | # status: 17 | # conditions: 18 | # - lastTransitionTime: "" 19 | # status: "True" # true, false, or unknown 20 | # type: Ready 21 | # Reason: "SuccessfulCreateOrUpdate" # status false result in reason FailedCreateOrUpdate 22 | # Message: "" # set when status is false 23 | -------------------------------------------------------------------------------- /docs/examples/policies/policy.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rabbitmq.com/v1beta1 2 | kind: Policy 3 | metadata: 4 | name: policy-example 5 | spec: 6 | name: transient # name of the policy 7 | vhost: "/a-vhost" # default to '/' if not provided 8 | pattern: "^amq." # regex used to match queues and exchanges 9 | applyTo: "queues" # set to 'queues', 'exchanges', or 'all' 10 | priority: 1 # defaults to 0 11 | definition: # policy definition 12 | expires: 1800000 13 | rabbitmqClusterReference: 14 | name: test # rabbitmqCluster must exist in the same namespace as this resource 15 | # status: 16 | # conditions: 17 | # - lastTransitionTime: "" 18 | # status: "True" # true, false, or unknown 19 | # type: Ready 20 | # Reason: "SuccessfulCreateOrUpdate" # status false result in reason FailedCreateOrUpdate 21 | # Message: "" # set when status is false 22 | -------------------------------------------------------------------------------- /docs/examples/topic-exchanges/user-topic-permissions.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: rabbitmq.com/v1beta1 3 | kind: TopicPermission 4 | metadata: 5 | name: permission-example-a 6 | spec: 7 | vhost: "/" # name of a vhost; required 8 | userReference: 9 | name: "topic-exchange-example" # name of a user.rabbitmq.com in the same namespace; must specify either spec.userReference or spec.user 10 | permissions: 11 | exchange: "topic-a" 12 | write: ".*" 13 | read: ".*" 14 | rabbitmqClusterReference: 15 | name: test 16 | --- 17 | apiVersion: rabbitmq.com/v1beta1 18 | kind: TopicPermission 19 | metadata: 20 | name: permission-example-b 21 | spec: 22 | vhost: "/" # name of a vhost; required 23 | user: "example" 24 | permissions: 25 | exchange: "topic-b" 26 | write: ".*" 27 | read: ".*" 28 | rabbitmqClusterReference: 29 | name: test 30 | -------------------------------------------------------------------------------- /config/default/overlays/dev/manager_image_patch.yaml: -------------------------------------------------------------------------------- 1 | # RabbitMQ Messaging Topology Kubernetes Operator 2 | 3 | # Copyright 2021 VMware, Inc. 4 | # 5 | # This product is licensed to you under the Mozilla Public license, Version 2.0 (the "License"). You may not use this product except in compliance with the Mozilla Public License. 6 | # 7 | # This product may include a number of subcomponents with separate copyright notices and license terms. Your use of these subcomponents is subject to the terms and conditions of the subcomponent's license, as noted in the LICENSE file. 8 | apiVersion: apps/v1 9 | kind: Deployment 10 | metadata: 11 | name: operator 12 | namespace: system 13 | spec: 14 | template: 15 | spec: 16 | containers: 17 | - image: ((operator_docker_image)) 18 | name: manager 19 | imagePullPolicy: Always 20 | args: ["--zap-devel"] 21 | -------------------------------------------------------------------------------- /config/default/overlays/kind/manager_image_patch.yaml: -------------------------------------------------------------------------------- 1 | # RabbitMQ Messaging Topology Kubernetes Operator 2 | 3 | # Copyright 2021 VMware, Inc. 4 | # 5 | # This product is licensed to you under the Mozilla Public license, Version 2.0 (the "License"). You may not use this product except in compliance with the Mozilla Public License. 6 | # 7 | # This product may include a number of subcomponents with separate copyright notices and license terms. Your use of these subcomponents is subject to the terms and conditions of the subcomponent's license, as noted in the LICENSE file. 8 | apiVersion: apps/v1 9 | kind: Deployment 10 | metadata: 11 | name: operator 12 | namespace: system 13 | spec: 14 | template: 15 | spec: 16 | containers: 17 | - image: ((operator_docker_image)) 18 | name: manager 19 | imagePullPolicy: IfNotPresent 20 | args: ["--zap-devel"] 21 | -------------------------------------------------------------------------------- /config/installation/kustomization.yaml: -------------------------------------------------------------------------------- 1 | # RabbitMQ Messaging Topology Kubernetes Operator 2 | 3 | # Copyright 2021 VMware, Inc. 4 | # 5 | # This product is licensed to you under the Mozilla Public license, Version 2.0 (the "License"). You may not use this product except in compliance with the Mozilla Public License. 6 | # 7 | # This product may include a number of subcomponents with separate copyright notices and license terms. Your use of these subcomponents is subject to the terms and conditions of the subcomponent's license, as noted in the LICENSE file. 8 | 9 | apiVersion: kustomize.config.k8s.io/v1beta1 10 | kind: Kustomization 11 | namespace: rabbitmq-system 12 | images: 13 | - name: rabbitmqoperator/messaging-topology-operator-dev 14 | newName: rabbitmqoperator/messaging-topology-operator 15 | newTag: latest 16 | 17 | resources: 18 | - ../namespace 19 | - ../rbac 20 | - ../default/base 21 | -------------------------------------------------------------------------------- /olm/catalog/cool-catalog.Dockerfile: -------------------------------------------------------------------------------- 1 | # The builder image is expected to contain 2 | # /bin/opm (with serve subcommand) 3 | FROM quay.io/operator-framework/opm:latest AS builder 4 | 5 | # Copy FBC root into image at /configs and pre-populate serve cache 6 | ADD cool-catalog /configs 7 | RUN ["/bin/opm", "serve", "/configs", "--cache-dir=/tmp/cache", "--cache-only"] 8 | 9 | FROM quay.io/operator-framework/opm:latest 10 | # The base image is expected to contain 11 | # /bin/opm (with serve subcommand) and /bin/grpc_health_probe 12 | 13 | # Configure the entrypoint and command 14 | ENTRYPOINT ["/bin/opm"] 15 | CMD ["serve", "/configs", "--cache-dir=/tmp/cache"] 16 | 17 | COPY --from=builder /configs /configs 18 | COPY --from=builder /tmp/cache /tmp/cache 19 | 20 | # Set FBC-specific label for the location of the FBC root directory 21 | # in the image 22 | LABEL operators.operatorframework.io.index.configs.v1=/configs 23 | -------------------------------------------------------------------------------- /docs/examples/exchanges/fanout-exchange.yaml: -------------------------------------------------------------------------------- 1 | # More on fanout exchange and other exchange types, see: https://www.rabbitmq.com/tutorials/amqp-concepts.html#exchanges. 2 | --- 3 | apiVersion: rabbitmq.com/v1beta1 4 | kind: Exchange 5 | metadata: 6 | name: fanout # name of the object in kubernetes 7 | spec: 8 | name: fanout-exchange # name of the exchange 9 | vhost: "/test-vhost" # default to '/' if not provided 10 | type: fanout # default to 'direct' if not provided 11 | autoDelete: false 12 | durable: true 13 | rabbitmqClusterReference: 14 | name: test # rabbitmqCluster must exist in the same namespace as this resource 15 | # status: 16 | # conditions: 17 | # - lastTransitionTime: "" 18 | # status: "True" # true, false, or unknown 19 | # type: Ready 20 | # Reason: "SuccessfulCreateOrUpdate" # status false result in reason FailedCreateOrUpdate 21 | # Message: "" # set when status is false 22 | -------------------------------------------------------------------------------- /docs/examples/users/userPreDefinedCreds.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Secret 3 | metadata: 4 | name: credentials-secret 5 | type: Opaque 6 | stringData: 7 | username: import-user-sample # Note that Messaging Topology Operator does not watch this secret. Updating this secret object won't update actual user credentials. 8 | password: whyareyoulookinghere # As a workaround, you can add a label or annotation to the User object to trigger a Reconile loop and credentials will be updated. 9 | --- 10 | apiVersion: rabbitmq.com/v1beta1 11 | kind: User 12 | metadata: 13 | name: import-user-sample 14 | spec: 15 | tags: 16 | - management # available tags are 'management', 'policymaker', 'monitoring' and 'administrator' 17 | - policymaker 18 | rabbitmqClusterReference: 19 | name: test # rabbitmqCluster must exist in the same namespace as this resource 20 | importCredentialsSecret: 21 | name: credentials-secret 22 | -------------------------------------------------------------------------------- /docs/examples/topic-exchanges/topic-exchanges.yaml: -------------------------------------------------------------------------------- 1 | # More on topic exchange and other exchange types, see: https://www.rabbitmq.com/tutorials/amqp-concepts.html#exchange-topic. 2 | --- 3 | apiVersion: rabbitmq.com/v1beta1 4 | kind: Exchange 5 | metadata: 6 | name: some-topic 7 | spec: 8 | name: topic-a # name of the exchange 9 | type: topic # default to 'direct' if not provided 10 | autoDelete: false 11 | durable: true 12 | rabbitmqClusterReference: 13 | name: test # rabbitmqCluster must exist in the same namespace as this resource 14 | --- 15 | apiVersion: rabbitmq.com/v1beta1 16 | kind: Exchange 17 | metadata: 18 | name: another-topic 19 | spec: 20 | name: topic-b # name of the exchange 21 | type: topic # default to 'direct' if not provided 22 | autoDelete: false 23 | durable: true 24 | rabbitmqClusterReference: 25 | name: test # rabbitmqCluster must exist in the same namespace as this resource 26 | -------------------------------------------------------------------------------- /config/installation/cert-manager/kustomization.yaml: -------------------------------------------------------------------------------- 1 | # RabbitMQ Messaging Topology Kubernetes Operator 2 | 3 | # Copyright 2021 VMware, Inc. 4 | # 5 | # This product is licensed to you under the Mozilla Public license, Version 2.0 (the "License"). You may not use this product except in compliance with the Mozilla Public License. 6 | # 7 | # This product may include a number of subcomponents with separate copyright notices and license terms. Your use of these subcomponents is subject to the terms and conditions of the subcomponent's license, as noted in the LICENSE file. 8 | 9 | apiVersion: kustomize.config.k8s.io/v1beta1 10 | kind: Kustomization 11 | namespace: rabbitmq-system 12 | images: 13 | - name: rabbitmqoperator/messaging-topology-operator-dev 14 | newName: rabbitmqoperator/messaging-topology-operator 15 | newTag: latest 16 | 17 | resources: 18 | - ../../namespace 19 | - ../../rbac 20 | - ../../default/overlays/cert-manager -------------------------------------------------------------------------------- /docs/examples/vault-support/rabbitmq-clusters.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rabbitmq.com/v1beta1 2 | kind: RabbitmqCluster 3 | metadata: 4 | name: cluster-vault-a 5 | annotations: 6 | rabbitmq.com/topology-allowed-namespaces: "*" 7 | spec: 8 | image: rabbitmq:3.9.7-management 9 | replicas: 1 10 | service: 11 | type: NodePort 12 | rabbitmq: 13 | additionalConfig: | 14 | loopback_users = none 15 | secretBackend: 16 | vault: 17 | role: rabbitmq-cluster 18 | defaultUserPath: secret/data/rabbitmq/cluster-vault-a/creds 19 | 20 | --- 21 | 22 | apiVersion: rabbitmq.com/v1beta1 23 | kind: RabbitmqCluster 24 | metadata: 25 | name: cluster-b 26 | annotations: 27 | rabbitmq.com/topology-allowed-namespaces: "*" 28 | spec: 29 | image: rabbitmq:3.9.7-management 30 | replicas: 1 31 | service: 32 | type: NodePort 33 | rabbitmq: 34 | additionalConfig: | 35 | loopback_users = none 36 | 37 | 38 | -------------------------------------------------------------------------------- /internal/permissions.go: -------------------------------------------------------------------------------- 1 | /* 2 | RabbitMQ Messaging Topology Kubernetes Operator 3 | Copyright 2021 VMware, Inc. 4 | 5 | This product is licensed to you under the Mozilla Public License 2.0 license (the "License"). You may not use this product except in compliance with the Mozilla 2.0 License. 6 | 7 | This product may include a number of subcomponents with separate copyright notices and license terms. Your use of these subcomponents is subject to the terms and conditions of the subcomponent's license, as noted in the LICENSE file. 8 | */ 9 | 10 | package internal 11 | 12 | import ( 13 | rabbithole "github.com/michaelklishin/rabbit-hole/v3" 14 | topology "github.com/rabbitmq/messaging-topology-operator/api/v1beta1" 15 | ) 16 | 17 | func GeneratePermissions(p *topology.Permission) rabbithole.Permissions { 18 | return rabbithole.Permissions{ 19 | Read: p.Spec.Permissions.Read, 20 | Write: p.Spec.Permissions.Write, 21 | Configure: p.Spec.Permissions.Configure, 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /pkg/generated/listers/rabbitmq.com/v1alpha1/expansion_generated.go: -------------------------------------------------------------------------------- 1 | /* 2 | RabbitMQ Messaging Topology Kubernetes Operator 3 | Copyright 2021 VMware, Inc. 4 | 5 | This product is licensed to you under the Mozilla Public License 2.0 license (the "License"). You may not use this product except in compliance with the Mozilla 2.0 License. 6 | 7 | This product may include a number of subcomponents with separate copyright notices and license terms. Your use of these subcomponents is subject to the terms and conditions of the subcomponent's license, as noted in the LICENSE file. 8 | */ 9 | 10 | // Code generated by lister-gen. DO NOT EDIT. 11 | 12 | package v1alpha1 13 | 14 | // SuperStreamListerExpansion allows custom methods to be added to 15 | // SuperStreamLister. 16 | type SuperStreamListerExpansion interface{} 17 | 18 | // SuperStreamNamespaceListerExpansion allows custom methods to be added to 19 | // SuperStreamNamespaceLister. 20 | type SuperStreamNamespaceListerExpansion interface{} 21 | -------------------------------------------------------------------------------- /docs/api/autogen/templates/type.tpl: -------------------------------------------------------------------------------- 1 | {{- define "type" -}} 2 | {{- $type := . -}} 3 | {{- if asciidocShouldRenderType $type -}} 4 | 5 | [id="{{ asciidocTypeID $type | asciidocRenderAnchorID }}"] 6 | ==== {{ $type.Name }} {{ if $type.IsAlias }}({{ asciidocRenderTypeLink $type.UnderlyingType }}) {{ end }} 7 | 8 | {{ $type.Doc }} 9 | 10 | {{ if $type.References -}} 11 | .Appears In: 12 | **** 13 | {{- range $type.SortedReferences }} 14 | - {{ asciidocRenderTypeLink . }} 15 | {{- end }} 16 | **** 17 | {{- end }} 18 | 19 | {{ if $type.Members -}} 20 | [cols="25a,75a", options="header"] 21 | |=== 22 | | Field | Description 23 | {{ if $type.GVK -}} 24 | | *`apiVersion`* __string__ | `{{ $type.GVK.Group }}/{{ $type.GVK.Version }}` 25 | | *`kind`* __string__ | `{{ $type.GVK.Kind }}` 26 | {{ end -}} 27 | 28 | {{ range $type.Members -}} 29 | | *`{{ .Name }}`* __{{ asciidocRenderType .Type }}__ | {{ template "type_members" . }} 30 | {{ end -}} 31 | |=== 32 | {{ end -}} 33 | 34 | {{- end -}} 35 | {{- end -}} 36 | -------------------------------------------------------------------------------- /controllers/reconcile_func.go: -------------------------------------------------------------------------------- 1 | package controllers 2 | 3 | import ( 4 | "context" 5 | topology "github.com/rabbitmq/messaging-topology-operator/api/v1beta1" 6 | "github.com/rabbitmq/messaging-topology-operator/rabbitmqclient" 7 | ) 8 | 9 | var _ ReconcileFunc = &BindingReconciler{} 10 | var _ ReconcileFunc = &ExchangeReconciler{} 11 | var _ ReconcileFunc = &FederationReconciler{} 12 | var _ ReconcileFunc = &PermissionReconciler{} 13 | var _ ReconcileFunc = &PolicyReconciler{} 14 | var _ ReconcileFunc = &OperatorPolicyReconciler{} 15 | var _ ReconcileFunc = &QueueReconciler{} 16 | var _ ReconcileFunc = &SchemaReplicationReconciler{} 17 | var _ ReconcileFunc = &ShovelReconciler{} 18 | var _ ReconcileFunc = &UserReconciler{} 19 | var _ ReconcileFunc = &VhostReconciler{} 20 | 21 | type ReconcileFunc interface { 22 | DeclareFunc(ctx context.Context, client rabbitmqclient.Client, resource topology.TopologyResource) error 23 | DeleteFunc(ctx context.Context, client rabbitmqclient.Client, resource topology.TopologyResource) error 24 | } 25 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: bug 6 | assignees: '' 7 | 8 | --- 9 | 10 | ## Describe the bug 11 | A clear and concise description of what the bug is. 12 | 13 | ## To Reproduce 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | Include any YAML or manifest necessary to reproduce the problem. 21 | 22 | **Expected behavior** 23 | A clear and concise description of what you expected to happen. 24 | 25 | ## Screenshots 26 | If applicable, add screenshots to help explain your problem. 27 | 28 | ## Version and environment information 29 | - Messaging Topology Operator: [e.g. 0.1.0 or commit if building from source] 30 | - RabbitMQ: [e.g. 3.8.0] 31 | - RabbitMQ Cluster Operator: [e.g. 1.1.0 or commit if building from source] 32 | - Kubernetes: [e.g. 1.18.0] 33 | - Cloud provider or hardware configuration: 34 | 35 | ## Additional context 36 | Add any other context about the problem here. 37 | -------------------------------------------------------------------------------- /docs/examples/exchanges/fanout-exchange-with-args.yaml: -------------------------------------------------------------------------------- 1 | # More on fanout exchange and other exchange types, see: https://www.rabbitmq.com/tutorials/amqp-concepts.html#exchanges. 2 | --- 3 | apiVersion: rabbitmq.com/v1beta1 4 | kind: Exchange 5 | metadata: 6 | name: fanout-alt-exchange # name of the object in kubernetes 7 | spec: 8 | name: fanout-alt-exchange # name of the exchange 9 | vhost: "/test-vhost" # default to '/' if not provided 10 | type: fanout # default to 'direct' if not provided 11 | autoDelete: false 12 | durable: true 13 | arguments: # additional arguments to provide towards the exchange. 14 | alternate-exchange: direct-exchange 15 | rabbitmqClusterReference: 16 | name: test # rabbitmqCluster must exist in the same namespace as this resource 17 | # status: 18 | # conditions: 19 | # - lastTransitionTime: "" 20 | # status: "True" # true, false, or unknown 21 | # type: Ready 22 | # Reason: "SuccessfulCreateOrUpdate" # status false result in reason FailedCreateOrUpdate 23 | # Message: "" # set when status is false 24 | -------------------------------------------------------------------------------- /docs/examples/users/publish-consume-user.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: Secret 4 | metadata: 5 | name: test-user-credentials 6 | type: Opaque 7 | stringData: 8 | username: test-user # Note that Messaging Topology Operator does not watch this secret. Updating this secret object won't update actual user credentials. 9 | password: verysecurepw # As a workaround, you can add a label or annotation to the User object to trigger a Reconile loop and credentials will be updated. 10 | --- 11 | apiVersion: rabbitmq.com/v1beta1 12 | kind: User 13 | metadata: 14 | name: test-user 15 | spec: 16 | rabbitmqClusterReference: 17 | name: test 18 | importCredentialsSecret: 19 | name: test-user-credentials 20 | --- 21 | apiVersion: rabbitmq.com/v1beta1 22 | kind: Permission 23 | metadata: 24 | name: testuser-permission 25 | spec: 26 | vhost: "test-vhost" 27 | user: "test-user" # name corresponds to the username we provided in "test-user-credentials" secret 28 | permissions: 29 | write: ".*" 30 | configure: "" 31 | read: ".*" 32 | rabbitmqClusterReference: 33 | name: test 34 | -------------------------------------------------------------------------------- /hack/generate-ordered-api-reference-list.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -euo pipefail 4 | 5 | wikiDir=$1 6 | 7 | generateVersionedEntry() { 8 | echo "* [$1](https://github.com/rabbitmq/messaging-topology-operator/wiki/API_Reference_$1)" 9 | } 10 | 11 | generateLatestEntry() { 12 | echo "* [Latest](https://github.com/rabbitmq/messaging-topology-operator/wiki/API_Reference)" 13 | } 14 | 15 | lead='^$' 16 | tail='^$' 17 | 18 | unorderedlistfile="$(mktemp)" 19 | orderedlistfile="$(mktemp)" 20 | 21 | apiVersions="$(find "$wikiDir/API_Reference_*" -printf "%f\n" | sed -r 's/API_Reference_(v[0-9]+.[0-9]+.[0-9]+).asciidoc/\1/' | sort -Vr )" 22 | for version in $apiVersions 23 | do 24 | generateVersionedEntry "$version" >> "$unorderedlistfile" 25 | done 26 | 27 | # Latest API version is a special case, that is always top of the list 28 | generateLatestEntry > "$orderedlistfile" 29 | cat "$unorderedlistfile" >> "$orderedlistfile" 30 | 31 | sed -e "/$lead/,/$tail/{ /$lead/{p; r $orderedlistfile 32 | }; /$tail/p; d }" "$wikiDir/Wiki_Sidebar.md" 33 | -------------------------------------------------------------------------------- /docs/examples/queues/quorum-queue.yaml: -------------------------------------------------------------------------------- 1 | # For more information about what/how to configure for quorum queues, see: https://www.rabbitmq.com/quorum-queues.html. 2 | # We recommend configuring queues through policies to manage them in groups and be able to update queue configurations later on. 3 | --- 4 | apiVersion: rabbitmq.com/v1beta1 5 | kind: Queue 6 | metadata: 7 | name: qq-example 8 | spec: 9 | name: qq # name of the queue 10 | vhost: "/test-vhost" # default to '/' if not provided 11 | type: quorum # without providing a queue type, rabbitmq creates a classic queue 12 | autoDelete: false 13 | durable: true # seting 'durable' to false means this queue won't survive a server restart 14 | deletionPolicy: retain # delete or retain; default to delete; 15 | rabbitmqClusterReference: 16 | name: test # rabbitmqCluster must exist in the same namespace as this resource 17 | # status: 18 | # conditions: 19 | # - lastTransitionTime: "" 20 | # status: "True" # true, false, or unknown 21 | # type: Ready 22 | # Reason: "SuccessfulCreateOrUpdate" # status false result in reason FailedCreateOrUpdate 23 | # Message: "" # set when status is false 24 | -------------------------------------------------------------------------------- /pkg/generated/clientset/versioned/typed/rabbitmq.com/v1beta1/generated_expansion.go: -------------------------------------------------------------------------------- 1 | /* 2 | RabbitMQ Messaging Topology Kubernetes Operator 3 | Copyright 2021 VMware, Inc. 4 | 5 | This product is licensed to you under the Mozilla Public License 2.0 license (the "License"). You may not use this product except in compliance with the Mozilla 2.0 License. 6 | 7 | This product may include a number of subcomponents with separate copyright notices and license terms. Your use of these subcomponents is subject to the terms and conditions of the subcomponent's license, as noted in the LICENSE file. 8 | */ 9 | 10 | // Code generated by client-gen. DO NOT EDIT. 11 | 12 | package v1beta1 13 | 14 | type BindingExpansion interface{} 15 | 16 | type ExchangeExpansion interface{} 17 | 18 | type FederationExpansion interface{} 19 | 20 | type PermissionExpansion interface{} 21 | 22 | type PolicyExpansion interface{} 23 | 24 | type OperatorPolicyExpansion interface{} 25 | 26 | type QueueExpansion interface{} 27 | 28 | type SchemaReplicationExpansion interface{} 29 | 30 | type ShovelExpansion interface{} 31 | 32 | type UserExpansion interface{} 33 | 34 | type VhostExpansion interface{} 35 | -------------------------------------------------------------------------------- /docs/examples/topic-exchanges/README.md: -------------------------------------------------------------------------------- 1 | # Topic Exchange Example 2 | 3 | In this example, we will create two topic exchanges, and a user that have read and write access to them in the default `/` vhost. 4 | 5 | Before creating any topology objects with Messaging Topology Operator, please deploy a RabbitmqCluster named `test`, or any name you see fit. You will need to modify the example manifests if the RabbitmqCluster name is something other than `test`. 6 | 7 | After the RabbitMQ cluster is successfully created, you can first create the two topic exchanges and a user by: 8 | 9 | ```bash 10 | kubectl apply -f topic-exchanges.yaml # create topic exchanges `topic-a` and `topic-b` in default vhost `/` 11 | kubectl apply -f user.yaml # create user `example` 12 | ``` 13 | 14 | Then you can grant user `example` permissions to both topic exchanges by: 15 | 16 | ```bash 17 | kubectl apply -f user-topic-permissions.yaml 18 | ``` 19 | 20 | This will create two `topicpermissions.rabbitmq.com` objects: one for managing user `example` permissions to `topic-a` and the other one manages user `example` permissions to `topic-b`. 21 | 22 | To clean up, you can run: 23 | 24 | ```bash 25 | kubectl delete -f user-topic-permissions.yaml -f user.yaml -f topic-exchanges.yaml 26 | ``` 27 | 28 | -------------------------------------------------------------------------------- /internal/queue_delete_options.go: -------------------------------------------------------------------------------- 1 | /* 2 | RabbitMQ Messaging Topology Kubernetes Operator 3 | Copyright 2021 VMware, Inc. 4 | 5 | This product is licensed to you under the Mozilla Public License 2.0 license (the "License"). You may not use this product except in compliance with the Mozilla 2.0 License. 6 | 7 | This product may include a number of subcomponents with separate copyright notices and license terms. Your use of these subcomponents is subject to the terms and conditions of the subcomponent's license, as noted in the LICENSE file. 8 | */ 9 | 10 | package internal 11 | 12 | import ( 13 | rabbithole "github.com/michaelklishin/rabbit-hole/v3" 14 | topology "github.com/rabbitmq/messaging-topology-operator/api/v1beta1" 15 | ) 16 | 17 | // GenerateQueueDeleteOptions generates rabbithole.QueueDeleteOptions for a given Queue 18 | // queue.Spec.Arguments (type k8s runtime.RawExtensions) is unmarshalled 19 | func GenerateQueueDeleteOptions(q *topology.Queue) (*rabbithole.QueueDeleteOptions, error) { 20 | 21 | return &rabbithole.QueueDeleteOptions{ 22 | // Set these values to false if q.Spec.Type = Quorum, not supported by the API 23 | IfEmpty: q.Spec.Type != "quorum" && q.Spec.DeleteIfEmpty, 24 | IfUnused: q.Spec.Type != "quorum" && q.Spec.DeleteIfUnused, 25 | }, nil 26 | } 27 | -------------------------------------------------------------------------------- /internal/permissions_test.go: -------------------------------------------------------------------------------- 1 | package internal_test 2 | 3 | import ( 4 | . "github.com/onsi/ginkgo/v2" 5 | . "github.com/onsi/gomega" 6 | topology "github.com/rabbitmq/messaging-topology-operator/api/v1beta1" 7 | . "github.com/rabbitmq/messaging-topology-operator/internal" 8 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 9 | ) 10 | 11 | var _ = Describe("GeneratePermissions", func() { 12 | var p *topology.Permission 13 | 14 | BeforeEach(func() { 15 | p = &topology.Permission{ 16 | ObjectMeta: metav1.ObjectMeta{ 17 | Name: "user-permissions", 18 | }, 19 | Spec: topology.PermissionSpec{ 20 | User: "a-user", 21 | Vhost: "/new-vhost", 22 | }, 23 | } 24 | }) 25 | 26 | It("sets 'Configure' correctly", func() { 27 | p.Spec.Permissions.Configure = ".*" 28 | rmqPermissions := GeneratePermissions(p) 29 | Expect(rmqPermissions.Configure).To(Equal(".*")) 30 | }) 31 | 32 | It("sets 'Write' correctly", func() { 33 | p.Spec.Permissions.Write = ".~" 34 | rmqPermissions := GeneratePermissions(p) 35 | Expect(rmqPermissions.Write).To(Equal(".~")) 36 | }) 37 | 38 | It("sets 'Read' correctly", func() { 39 | p.Spec.Permissions.Read = "^$" 40 | rmqPermissions := GeneratePermissions(p) 41 | Expect(rmqPermissions.Read).To(Equal("^$")) 42 | }) 43 | }) 44 | -------------------------------------------------------------------------------- /internal/policy.go: -------------------------------------------------------------------------------- 1 | /* 2 | RabbitMQ Messaging Topology Kubernetes Operator 3 | Copyright 2021 VMware, Inc. 4 | 5 | This product is licensed to you under the Mozilla Public License 2.0 license (the "License"). You may not use this product except in compliance with the Mozilla 2.0 License. 6 | 7 | This product may include a number of subcomponents with separate copyright notices and license terms. Your use of these subcomponents is subject to the terms and conditions of the subcomponent's license, as noted in the LICENSE file. 8 | */ 9 | 10 | package internal 11 | 12 | import ( 13 | "encoding/json" 14 | "fmt" 15 | rabbithole "github.com/michaelklishin/rabbit-hole/v3" 16 | topology "github.com/rabbitmq/messaging-topology-operator/api/v1beta1" 17 | ) 18 | 19 | func GeneratePolicy(p *topology.Policy) (*rabbithole.Policy, error) { 20 | definition := make(map[string]interface{}) 21 | if err := json.Unmarshal(p.Spec.Definition.Raw, &definition); err != nil { 22 | return nil, fmt.Errorf("failed to unmarshall policy definition: %v", err) 23 | } 24 | 25 | return &rabbithole.Policy{ 26 | Vhost: p.Spec.Vhost, 27 | Pattern: p.Spec.Pattern, 28 | ApplyTo: p.Spec.ApplyTo, 29 | Name: p.Spec.Name, 30 | Priority: p.Spec.Priority, 31 | Definition: definition, 32 | }, nil 33 | } 34 | -------------------------------------------------------------------------------- /internal/exchange_settings.go: -------------------------------------------------------------------------------- 1 | /* 2 | RabbitMQ Messaging Topology Kubernetes Operator 3 | Copyright 2021 VMware, Inc. 4 | 5 | This product is licensed to you under the Mozilla Public License 2.0 license (the "License"). You may not use this product except in compliance with the Mozilla 2.0 License. 6 | 7 | This product may include a number of subcomponents with separate copyright notices and license terms. Your use of these subcomponents is subject to the terms and conditions of the subcomponent's license, as noted in the LICENSE file. 8 | */ 9 | 10 | package internal 11 | 12 | import ( 13 | "encoding/json" 14 | "fmt" 15 | rabbithole "github.com/michaelklishin/rabbit-hole/v3" 16 | topology "github.com/rabbitmq/messaging-topology-operator/api/v1beta1" 17 | ) 18 | 19 | func GenerateExchangeSettings(e *topology.Exchange) (*rabbithole.ExchangeSettings, error) { 20 | arguments := make(map[string]interface{}) 21 | if e.Spec.Arguments != nil { 22 | if err := json.Unmarshal(e.Spec.Arguments.Raw, &arguments); err != nil { 23 | return nil, fmt.Errorf("failed to unmarshall exchange arguments: %v", err) 24 | } 25 | } 26 | 27 | return &rabbithole.ExchangeSettings{ 28 | Durable: e.Spec.Durable, 29 | AutoDelete: e.Spec.AutoDelete, 30 | Type: e.Spec.Type, 31 | Arguments: arguments, 32 | }, nil 33 | } 34 | -------------------------------------------------------------------------------- /internal/topic_permissions_test.go: -------------------------------------------------------------------------------- 1 | package internal_test 2 | 3 | import ( 4 | . "github.com/onsi/ginkgo/v2" 5 | . "github.com/onsi/gomega" 6 | topology "github.com/rabbitmq/messaging-topology-operator/api/v1beta1" 7 | . "github.com/rabbitmq/messaging-topology-operator/internal" 8 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 9 | ) 10 | 11 | var _ = Describe("GenerateTopicPermissions", func() { 12 | var p *topology.TopicPermission 13 | 14 | BeforeEach(func() { 15 | p = &topology.TopicPermission{ 16 | ObjectMeta: metav1.ObjectMeta{ 17 | Name: "perm", 18 | }, 19 | Spec: topology.TopicPermissionSpec{ 20 | User: "a-user", 21 | Vhost: "/new-vhost", 22 | }, 23 | } 24 | }) 25 | 26 | It("sets 'Exchange' correctly", func() { 27 | p.Spec.Permissions.Exchange = "a-random-exchange" 28 | rmqPermissions := GenerateTopicPermissions(p) 29 | Expect(rmqPermissions.Exchange).To(Equal("a-random-exchange")) 30 | }) 31 | 32 | It("sets 'Write' correctly", func() { 33 | p.Spec.Permissions.Write = ".~" 34 | rmqPermissions := GenerateTopicPermissions(p) 35 | Expect(rmqPermissions.Write).To(Equal(".~")) 36 | }) 37 | 38 | It("sets 'Read' correctly", func() { 39 | p.Spec.Permissions.Read = "^$" 40 | rmqPermissions := GenerateTopicPermissions(p) 41 | Expect(rmqPermissions.Read).To(Equal("^$")) 42 | }) 43 | }) 44 | -------------------------------------------------------------------------------- /internal/operatorpolicy.go: -------------------------------------------------------------------------------- 1 | /* 2 | RabbitMQ Messaging Topology Kubernetes Operator 3 | Copyright 2021 VMware, Inc. 4 | 5 | This product is licensed to you under the Mozilla Public License 2.0 license (the "License"). You may not use this product except in compliance with the Mozilla 2.0 License. 6 | 7 | This product may include a number of subcomponents with separate copyright notices and license terms. Your use of these subcomponents is subject to the terms and conditions of the subcomponent's license, as noted in the LICENSE file. 8 | */ 9 | 10 | package internal 11 | 12 | import ( 13 | "encoding/json" 14 | "fmt" 15 | 16 | rabbithole "github.com/michaelklishin/rabbit-hole/v3" 17 | topology "github.com/rabbitmq/messaging-topology-operator/api/v1beta1" 18 | ) 19 | 20 | func GenerateOperatorPolicy(p *topology.OperatorPolicy) (*rabbithole.OperatorPolicy, error) { 21 | definition := make(map[string]interface{}) 22 | if err := json.Unmarshal(p.Spec.Definition.Raw, &definition); err != nil { 23 | return nil, fmt.Errorf("failed to unmarshall policy definition: %v", err) 24 | } 25 | 26 | return &rabbithole.OperatorPolicy{ 27 | Vhost: p.Spec.Vhost, 28 | Pattern: p.Spec.Pattern, 29 | ApplyTo: p.Spec.ApplyTo, 30 | Name: p.Spec.Name, 31 | Priority: p.Spec.Priority, 32 | Definition: definition, 33 | }, nil 34 | } 35 | -------------------------------------------------------------------------------- /pkg/generated/clientset/versioned/typed/rabbitmq.com/v1alpha1/fake/fake_rabbitmq.com_client.go: -------------------------------------------------------------------------------- 1 | /* 2 | RabbitMQ Messaging Topology Kubernetes Operator 3 | Copyright 2021 VMware, Inc. 4 | 5 | This product is licensed to you under the Mozilla Public License 2.0 license (the "License"). You may not use this product except in compliance with the Mozilla 2.0 License. 6 | 7 | This product may include a number of subcomponents with separate copyright notices and license terms. Your use of these subcomponents is subject to the terms and conditions of the subcomponent's license, as noted in the LICENSE file. 8 | */ 9 | 10 | // Code generated by client-gen. DO NOT EDIT. 11 | 12 | package fake 13 | 14 | import ( 15 | v1alpha1 "github.com/rabbitmq/messaging-topology-operator/pkg/generated/clientset/versioned/typed/rabbitmq.com/v1alpha1" 16 | rest "k8s.io/client-go/rest" 17 | testing "k8s.io/client-go/testing" 18 | ) 19 | 20 | type FakeRabbitmqV1alpha1 struct { 21 | *testing.Fake 22 | } 23 | 24 | func (c *FakeRabbitmqV1alpha1) SuperStreams(namespace string) v1alpha1.SuperStreamInterface { 25 | return &FakeSuperStreams{c, namespace} 26 | } 27 | 28 | // RESTClient returns a RESTClient that is used to communicate 29 | // with API server by this client implementation. 30 | func (c *FakeRabbitmqV1alpha1) RESTClient() rest.Interface { 31 | var ret *rest.RESTClient 32 | return ret 33 | } 34 | -------------------------------------------------------------------------------- /api/v1beta1/schemareplication_types_test.go: -------------------------------------------------------------------------------- 1 | package v1beta1 2 | 3 | import ( 4 | "context" 5 | . "github.com/onsi/ginkgo/v2" 6 | . "github.com/onsi/gomega" 7 | corev1 "k8s.io/api/core/v1" 8 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 9 | "k8s.io/apimachinery/pkg/types" 10 | ) 11 | 12 | var _ = Describe("schemaReplication spec", func() { 13 | It("creates a schemaReplication", func() { 14 | replication := SchemaReplication{ 15 | ObjectMeta: metav1.ObjectMeta{ 16 | Name: "replication", 17 | Namespace: "default", 18 | }, 19 | Spec: SchemaReplicationSpec{ 20 | RabbitmqClusterReference: RabbitmqClusterReference{ 21 | Name: "some-cluster", 22 | }, 23 | UpstreamSecret: &corev1.LocalObjectReference{ 24 | Name: "a-secret", 25 | }, 26 | Endpoints: "abc.rmq.com:1234", 27 | }} 28 | Expect(k8sClient.Create(context.Background(), &replication)).To(Succeed()) 29 | 30 | fetched := &SchemaReplication{} 31 | Expect(k8sClient.Get(context.Background(), types.NamespacedName{ 32 | Name: replication.Name, 33 | Namespace: replication.Namespace, 34 | }, fetched)).To(Succeed()) 35 | Expect(fetched.Spec.RabbitmqClusterReference).To(Equal(RabbitmqClusterReference{ 36 | Name: "some-cluster", 37 | })) 38 | Expect(fetched.Spec.UpstreamSecret.Name).To(Equal("a-secret")) 39 | Expect(fetched.Spec.Endpoints).To(Equal("abc.rmq.com:1234")) 40 | }) 41 | }) 42 | -------------------------------------------------------------------------------- /internal/vhost_settings.go: -------------------------------------------------------------------------------- 1 | /* 2 | RabbitMQ Messaging Topology Kubernetes Operator 3 | Copyright 2021 VMware, Inc. 4 | 5 | This product is licensed to you under the Mozilla Public License 2.0 license (the "License"). You may not use this product except in compliance with the Mozilla 2.0 License. 6 | 7 | This product may include a number of subcomponents with separate copyright notices and license terms. Your use of these subcomponents is subject to the terms and conditions of the subcomponent's license, as noted in the LICENSE file. 8 | */ 9 | 10 | package internal 11 | 12 | import ( 13 | rabbithole "github.com/michaelklishin/rabbit-hole/v3" 14 | topology "github.com/rabbitmq/messaging-topology-operator/api/v1beta1" 15 | ) 16 | 17 | func GenerateVhostSettings(v *topology.Vhost) *rabbithole.VhostSettings { 18 | return &rabbithole.VhostSettings{ 19 | Tracing: v.Spec.Tracing, 20 | Tags: v.Spec.Tags, 21 | DefaultQueueType: v.Spec.DefaultQueueType, 22 | } 23 | } 24 | 25 | func GenerateVhostLimits(limits *topology.VhostLimits) rabbithole.VhostLimitsValues { 26 | vhostLimitsValues := rabbithole.VhostLimitsValues{} 27 | if limits != nil { 28 | if limits.Connections != nil { 29 | vhostLimitsValues["max-connections"] = int(*limits.Connections) 30 | } 31 | if limits.Queues != nil { 32 | vhostLimitsValues["max-queues"] = int(*limits.Queues) 33 | } 34 | } 35 | return vhostLimitsValues 36 | } 37 | -------------------------------------------------------------------------------- /internal/federation_definition.go: -------------------------------------------------------------------------------- 1 | /* 2 | RabbitMQ Messaging Topology Kubernetes Operator 3 | Copyright 2021 VMware, Inc. 4 | 5 | This product is licensed to you under the Mozilla Public License 2.0 license (the "License"). You may not use this product except in compliance with the Mozilla 2.0 License. 6 | 7 | This product may include a number of subcomponents with separate copyright notices and license terms. Your use of these subcomponents is subject to the terms and conditions of the subcomponent's license, as noted in the LICENSE file. 8 | */ 9 | 10 | package internal 11 | 12 | import ( 13 | rabbithole "github.com/michaelklishin/rabbit-hole/v3" 14 | topology "github.com/rabbitmq/messaging-topology-operator/api/v1beta1" 15 | "strings" 16 | ) 17 | 18 | func GenerateFederationDefinition(f *topology.Federation, uri string) rabbithole.FederationDefinition { 19 | return rabbithole.FederationDefinition{ 20 | Uri: strings.Split(uri, ","), 21 | Expires: f.Spec.Expires, 22 | MessageTTL: int32(f.Spec.MessageTTL), 23 | MaxHops: f.Spec.MaxHops, 24 | PrefetchCount: f.Spec.PrefetchCount, 25 | ReconnectDelay: f.Spec.ReconnectDelay, 26 | AckMode: f.Spec.AckMode, 27 | TrustUserId: f.Spec.TrustUserId, 28 | Exchange: f.Spec.Exchange, 29 | Queue: f.Spec.Queue, 30 | QueueType: f.Spec.QueueType, 31 | ResourceCleanupMode: f.Spec.ResourceCleanupMode, 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /config/default/overlays/cert-manager/kustomization.yaml: -------------------------------------------------------------------------------- 1 | # RabbitMQ Messaging Topology Kubernetes Operator 2 | 3 | # Copyright 2021 VMware, Inc. 4 | # 5 | # This product is licensed to you under the Mozilla Public license, Version 2.0 (the "License"). You may not use this product except in compliance with the Mozilla Public License. 6 | # 7 | # This product may include a number of subcomponents with separate copyright notices and license terms. Your use of these subcomponents is subject to the terms and conditions of the subcomponent's license, as noted in the LICENSE file. 8 | namespace: rabbitmq-system 9 | 10 | resources: 11 | - ../../base 12 | - ../../../certmanager 13 | 14 | vars: 15 | - name: SERVICE_NAMESPACE # namespace of the service 16 | objref: 17 | kind: Service 18 | version: v1 19 | name: webhook-service 20 | fieldref: 21 | fieldpath: metadata.namespace 22 | - name: SERVICE_NAME 23 | objref: 24 | kind: Service 25 | version: v1 26 | name: webhook-service 27 | - name: CERTIFICATE_NAMESPACE # namespace of the certificate CR 28 | objref: 29 | kind: Certificate 30 | group: cert-manager.io 31 | version: v1 32 | name: serving-cert # this name should match the one in certificate.yaml 33 | fieldref: 34 | fieldpath: metadata.namespace 35 | - name: CERTIFICATE_NAME 36 | objref: 37 | kind: Certificate 38 | group: cert-manager.io 39 | version: v1 40 | name: serving-cert # this name should match the one in certificate.yaml 41 | -------------------------------------------------------------------------------- /config/manager/manager.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: operator 5 | namespace: system 6 | labels: 7 | app.kubernetes.io/name: messaging-topology-operator 8 | app.kubernetes.io/component: rabbitmq-operator 9 | app.kubernetes.io/part-of: rabbitmq 10 | spec: 11 | selector: 12 | matchLabels: 13 | app.kubernetes.io/name: messaging-topology-operator 14 | replicas: 1 15 | template: 16 | metadata: 17 | labels: 18 | app.kubernetes.io/name: messaging-topology-operator 19 | app.kubernetes.io/component: rabbitmq-operator 20 | app.kubernetes.io/part-of: rabbitmq 21 | spec: 22 | serviceAccountName: messaging-topology-operator 23 | containers: 24 | - command: 25 | - /manager 26 | image: controller:latest 27 | imagePullPolicy: Always 28 | name: manager 29 | resources: 30 | limits: 31 | cpu: 300m 32 | memory: 500Mi 33 | requests: 34 | cpu: 100m 35 | memory: 100Mi 36 | env: 37 | - name: OPERATOR_NAMESPACE 38 | valueFrom: 39 | fieldRef: 40 | fieldPath: metadata.namespace 41 | securityContext: 42 | allowPrivilegeEscalation: false 43 | capabilities: 44 | drop: 45 | - ALL 46 | runAsNonRoot: true 47 | seccompProfile: 48 | type: RuntimeDefault 49 | terminationGracePeriodSeconds: 10 50 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | ARG GO_TAG=1.25 2 | # Build the manager binary 3 | FROM --platform=$BUILDPLATFORM golang:${GO_TAG} AS builder 4 | 5 | WORKDIR /workspace 6 | # Copy the Go Modules manifests 7 | COPY go.mod go.mod 8 | COPY go.sum go.sum 9 | # cache deps before building and copying source so that we don't need to re-download as much 10 | # and so that source changes don't invalidate our downloaded layer 11 | RUN go mod download 12 | 13 | # Copy the go source 14 | COPY main.go main.go 15 | COPY api/ api/ 16 | COPY controllers/ controllers/ 17 | COPY internal/ internal/ 18 | COPY rabbitmqclient/ rabbitmqclient/ 19 | 20 | # Build 21 | ARG TARGETOS 22 | ARG TARGETARCH 23 | ENV GOOS=$TARGETOS 24 | ENV GOARCH=$TARGETARCH 25 | 26 | ARG FIPS_MODE=off 27 | ENV GOFIPS140=$FIPS_MODE 28 | 29 | # Build 30 | RUN CGO_ENABLED=0 GO111MODULE=on go build -a -tags timetzdata -o manager main.go 31 | 32 | # --------------------------------------- 33 | FROM alpine:latest AS etc-builder 34 | 35 | RUN echo "messaging-topology-operator:x:1001:" > /etc/group && \ 36 | echo "messaging-topology-operator:x:1001:1001::/home/messaging-topology-operator:/usr/sbin/nologin" > /etc/passwd 37 | 38 | RUN apk add -U --no-cache ca-certificates 39 | 40 | # --------------------------------------- 41 | FROM scratch 42 | WORKDIR / 43 | COPY --from=builder /workspace/manager . 44 | COPY --from=etc-builder /etc/passwd /etc/group /etc/ 45 | COPY --from=etc-builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt 46 | USER 1001:1001 47 | 48 | ENTRYPOINT ["/manager"] 49 | -------------------------------------------------------------------------------- /api/v1beta1/conditions_test.go: -------------------------------------------------------------------------------- 1 | package v1beta1 2 | 3 | import ( 4 | . "github.com/onsi/ginkgo/v2" 5 | . "github.com/onsi/gomega" 6 | corev1 "k8s.io/api/core/v1" 7 | ) 8 | 9 | var _ = Describe("Conditions", func() { 10 | Describe("Ready", func() { 11 | It("returns 'Ready' condition set to true", func() { 12 | c := Ready(nil) 13 | Expect(string(c.Type)).To(Equal("Ready")) 14 | Expect(c.Status).To(Equal(corev1.ConditionTrue)) 15 | Expect(c.Reason).To(Equal("SuccessfulCreateOrUpdate")) 16 | Expect(c.LastTransitionTime.IsZero()).To(BeFalse()) 17 | }) 18 | }) 19 | Describe("NotReady", func() { 20 | It("returns 'Ready' condition set to false", func() { 21 | c := NotReady("fail to declare queue", nil) 22 | Expect(string(c.Type)).To(Equal("Ready")) 23 | Expect(c.Status).To(Equal(corev1.ConditionFalse)) 24 | Expect(c.Reason).To(Equal("FailedCreateOrUpdate")) 25 | Expect(c.Message).To(Equal("fail to declare queue")) 26 | Expect(c.LastTransitionTime.IsZero()).To(BeFalse()) 27 | }) 28 | }) 29 | Context("LastTransitionTime", func() { 30 | It("changes only if status changes", func() { 31 | c1 := Ready(nil) 32 | Expect(c1.LastTransitionTime.IsZero()).To(BeFalse()) 33 | c2 := Ready([]Condition{ 34 | {Type: "I'm some other type"}, 35 | c1, 36 | }) 37 | Expect(c2.LastTransitionTime.Time).To(BeTemporally("==", c1.LastTransitionTime.Time)) 38 | c3 := NotReady("some message", []Condition{c2}) 39 | Expect(c3.LastTransitionTime.Time).To(BeTemporally(">", c2.LastTransitionTime.Time)) 40 | }) 41 | }) 42 | }) 43 | -------------------------------------------------------------------------------- /internal/queue_settings.go: -------------------------------------------------------------------------------- 1 | /* 2 | RabbitMQ Messaging Topology Kubernetes Operator 3 | Copyright 2021 VMware, Inc. 4 | 5 | This product is licensed to you under the Mozilla Public License 2.0 license (the "License"). You may not use this product except in compliance with the Mozilla 2.0 License. 6 | 7 | This product may include a number of subcomponents with separate copyright notices and license terms. Your use of these subcomponents is subject to the terms and conditions of the subcomponent's license, as noted in the LICENSE file. 8 | */ 9 | 10 | package internal 11 | 12 | import ( 13 | "encoding/json" 14 | "fmt" 15 | rabbithole "github.com/michaelklishin/rabbit-hole/v3" 16 | topology "github.com/rabbitmq/messaging-topology-operator/api/v1beta1" 17 | ) 18 | 19 | // GenerateQueueSettings generates rabbithole.QueueSettings for a given Queue 20 | // queue.Spec.Arguments (type k8s runtime.RawExtensions) is unmarshalled 21 | // Unmarshall stores float64, for JSON numbers 22 | // See: https://golang.org/pkg/encoding/json/#Unmarshal 23 | func GenerateQueueSettings(q *topology.Queue) (*rabbithole.QueueSettings, error) { 24 | arguments := make(map[string]interface{}) 25 | if q.Spec.Arguments != nil { 26 | if err := json.Unmarshal(q.Spec.Arguments.Raw, &arguments); err != nil { 27 | return nil, fmt.Errorf("failed to unmarshall queue arguments: %v", err) 28 | } 29 | } 30 | 31 | return &rabbithole.QueueSettings{ 32 | Type: q.Spec.Type, 33 | Durable: q.Spec.Durable, 34 | AutoDelete: q.Spec.AutoDelete, 35 | Arguments: arguments, 36 | }, nil 37 | } 38 | -------------------------------------------------------------------------------- /pkg/generated/informers/externalversions/internalinterfaces/factory_interfaces.go: -------------------------------------------------------------------------------- 1 | /* 2 | RabbitMQ Messaging Topology Kubernetes Operator 3 | Copyright 2021 VMware, Inc. 4 | 5 | This product is licensed to you under the Mozilla Public License 2.0 license (the "License"). You may not use this product except in compliance with the Mozilla 2.0 License. 6 | 7 | This product may include a number of subcomponents with separate copyright notices and license terms. Your use of these subcomponents is subject to the terms and conditions of the subcomponent's license, as noted in the LICENSE file. 8 | */ 9 | 10 | // Code generated by informer-gen. DO NOT EDIT. 11 | 12 | package internalinterfaces 13 | 14 | import ( 15 | time "time" 16 | 17 | versioned "github.com/rabbitmq/messaging-topology-operator/pkg/generated/clientset/versioned" 18 | v1 "k8s.io/apimachinery/pkg/apis/meta/v1" 19 | runtime "k8s.io/apimachinery/pkg/runtime" 20 | cache "k8s.io/client-go/tools/cache" 21 | ) 22 | 23 | // NewInformerFunc takes versioned.Interface and time.Duration to return a SharedIndexInformer. 24 | type NewInformerFunc func(versioned.Interface, time.Duration) cache.SharedIndexInformer 25 | 26 | // SharedInformerFactory a small interface to allow for adding an informer without an import cycle 27 | type SharedInformerFactory interface { 28 | Start(stopCh <-chan struct{}) 29 | InformerFor(obj runtime.Object, newFunc NewInformerFunc) cache.SharedIndexInformer 30 | } 31 | 32 | // TweakListOptionsFunc is a function that transforms a v1.ListOptions. 33 | type TweakListOptionsFunc func(*v1.ListOptions) 34 | -------------------------------------------------------------------------------- /api/v1alpha1/suite_test.go: -------------------------------------------------------------------------------- 1 | package v1alpha1 2 | 3 | import ( 4 | "path/filepath" 5 | "testing" 6 | 7 | . "github.com/onsi/ginkgo/v2" 8 | . "github.com/onsi/gomega" 9 | 10 | "k8s.io/client-go/kubernetes/scheme" 11 | "k8s.io/client-go/rest" 12 | "sigs.k8s.io/controller-runtime/pkg/client" 13 | "sigs.k8s.io/controller-runtime/pkg/envtest" 14 | logf "sigs.k8s.io/controller-runtime/pkg/log" 15 | "sigs.k8s.io/controller-runtime/pkg/log/zap" 16 | ) 17 | 18 | var cfg *rest.Config 19 | var k8sClient client.Client 20 | var testEnv *envtest.Environment 21 | 22 | func TestAPIs(t *testing.T) { 23 | RegisterFailHandler(Fail) 24 | RunSpecs(t, "v1alpha1 Suite") 25 | } 26 | 27 | var _ = BeforeSuite(func() { 28 | 29 | logf.SetLogger(zap.New(zap.UseDevMode(true), zap.WriteTo(GinkgoWriter))) 30 | 31 | By("bootstrapping test environment") 32 | testEnv = &envtest.Environment{ 33 | CRDDirectoryPaths: []string{filepath.Join("..", "..", "config", "crd", "bases")}, 34 | } 35 | 36 | testEnv.ControlPlane.GetAPIServer().Configure().Set("bind-address", "127.0.0.1") 37 | 38 | err := SchemeBuilder.AddToScheme(scheme.Scheme) 39 | Expect(err).NotTo(HaveOccurred()) 40 | 41 | cfg, err = testEnv.Start() 42 | Expect(err).ToNot(HaveOccurred()) 43 | Expect(cfg).ToNot(BeNil()) 44 | 45 | k8sClient, err = client.New(cfg, client.Options{Scheme: scheme.Scheme}) 46 | Expect(err).ToNot(HaveOccurred()) 47 | Expect(k8sClient).ToNot(BeNil()) 48 | }) 49 | 50 | var _ = AfterSuite(func() { 51 | By("tearing down the test environment") 52 | err := testEnv.Stop() 53 | Expect(err).ToNot(HaveOccurred()) 54 | }) 55 | -------------------------------------------------------------------------------- /pkg/generated/informers/externalversions/rabbitmq.com/v1alpha1/interface.go: -------------------------------------------------------------------------------- 1 | /* 2 | RabbitMQ Messaging Topology Kubernetes Operator 3 | Copyright 2021 VMware, Inc. 4 | 5 | This product is licensed to you under the Mozilla Public License 2.0 license (the "License"). You may not use this product except in compliance with the Mozilla 2.0 License. 6 | 7 | This product may include a number of subcomponents with separate copyright notices and license terms. Your use of these subcomponents is subject to the terms and conditions of the subcomponent's license, as noted in the LICENSE file. 8 | */ 9 | 10 | // Code generated by informer-gen. DO NOT EDIT. 11 | 12 | package v1alpha1 13 | 14 | import ( 15 | internalinterfaces "github.com/rabbitmq/messaging-topology-operator/pkg/generated/informers/externalversions/internalinterfaces" 16 | ) 17 | 18 | // Interface provides access to all the informers in this group version. 19 | type Interface interface { 20 | // SuperStreams returns a SuperStreamInformer. 21 | SuperStreams() SuperStreamInformer 22 | } 23 | 24 | type version struct { 25 | factory internalinterfaces.SharedInformerFactory 26 | namespace string 27 | tweakListOptions internalinterfaces.TweakListOptionsFunc 28 | } 29 | 30 | // New returns a new Interface. 31 | func New(f internalinterfaces.SharedInformerFactory, namespace string, tweakListOptions internalinterfaces.TweakListOptionsFunc) Interface { 32 | return &version{factory: f, namespace: namespace, tweakListOptions: tweakListOptions} 33 | } 34 | 35 | // SuperStreams returns a SuperStreamInformer. 36 | func (v *version) SuperStreams() SuperStreamInformer { 37 | return &superStreamInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} 38 | } 39 | -------------------------------------------------------------------------------- /internal/schema_replication.go: -------------------------------------------------------------------------------- 1 | /* 2 | RabbitMQ Messaging Topology Kubernetes Operator 3 | Copyright 2021 VMware, Inc. 4 | 5 | This product is licensed to you under the Mozilla Public License 2.0 license (the "License"). You may not use this product except in compliance with the Mozilla 2.0 License. 6 | 7 | This product may include a number of subcomponents with separate copyright notices and license terms. Your use of these subcomponents is subject to the terms and conditions of the subcomponent's license, as noted in the LICENSE file. 8 | */ 9 | 10 | package internal 11 | 12 | import ( 13 | "fmt" 14 | corev1 "k8s.io/api/core/v1" 15 | "strings" 16 | ) 17 | 18 | type UpstreamEndpoints struct { 19 | Username string `json:"username"` 20 | Password string `json:"password"` 21 | Endpoints []string `json:"endpoints"` 22 | } 23 | 24 | func GenerateSchemaReplicationParameters(secret *corev1.Secret, endpoints string) (UpstreamEndpoints, error) { 25 | username, ok := secret.Data["username"] 26 | if !ok { 27 | return UpstreamEndpoints{}, fmt.Errorf("could not find username in secret %s", secret.Name) 28 | } 29 | password, ok := secret.Data["password"] 30 | if !ok { 31 | return UpstreamEndpoints{}, fmt.Errorf("could not find password in secret %s", secret.Name) 32 | } 33 | 34 | if endpoints == "" { 35 | endpointsFromSecret, ok := secret.Data["endpoints"] 36 | if !ok { 37 | return UpstreamEndpoints{}, fmt.Errorf("could not find endpoints in secret %s or from spec.endpoints", secret.Name) 38 | } 39 | endpoints = string(endpointsFromSecret) 40 | } 41 | 42 | endpointsList := strings.Split(endpoints, ",") 43 | 44 | return UpstreamEndpoints{ 45 | Username: string(username), 46 | Password: string(password), 47 | Endpoints: endpointsList, 48 | }, nil 49 | 50 | } 51 | -------------------------------------------------------------------------------- /docs/examples/users/README.md: -------------------------------------------------------------------------------- 1 | # User examples 2 | 3 | This section contains the examples for creating RabbitMQ users. 4 | 5 | Messaging Topology Operator creates users with generated credentials by default. To create RabbitMQ users with provided credentials, you can reference a kubernetes secret object with the following keys in its Data field: 6 | 7 | * `username` – Must be present or the import will fail. 8 | * `passwordHash` – The SHA-512 hash of the password, as described in [RabbitMQ Docs](https://www.rabbitmq.com/docs/passwords). If the hash is an empty string, a passwordless user will be created. 9 | * `password` – Plain-text password. Will be used only if the `passwordHash` key is missing. 10 | 11 | See [userPreDefinedCreds.yaml](./userPreDefinedCreds.yaml), [userWithPasswordHash.yaml](userWithPasswordHash.yaml), [passwordlessUser.yaml](passwordlessUser.yaml) and [publish-consume-user.yaml](./publish-consume-user.yaml) as examples. 12 | 13 | From [Messaging Topology Operator v1.10.0](https://github.com/rabbitmq/messaging-topology-operator/releases/tag/v1.10.1), you can provide a username and reply on the Operator to generate its password for you. 14 | See [setUsernamewithGenPass.yaml](./setUsernamewithGenPass.yaml) as an example. 15 | 16 | Note that Messaging Topology Operator does not watch the provided secret and updating the secret object won't update actual user credentials. 17 | The User controller will generate a new secret from the provided secret named by appending the suffix `-user-credentials` to the username. For example, if your User is named `user-test` the controller will generate a secret `user-test-user-credentials`. 18 | If you wish to update User credentials, you can update the secret generated by the controller and then add a label or annotation to the User object to trigger a reconcile loop. 19 | -------------------------------------------------------------------------------- /rabbitmqclient/suite_test.go: -------------------------------------------------------------------------------- 1 | package rabbitmqclient_test 2 | 3 | import ( 4 | "crypto/tls" 5 | . "github.com/onsi/ginkgo/v2" 6 | . "github.com/onsi/gomega" 7 | 8 | "github.com/onsi/gomega/ghttp" 9 | "github.com/rabbitmq/messaging-topology-operator/internal/testutils" 10 | "net/url" 11 | "strconv" 12 | "testing" 13 | ) 14 | 15 | func TestResource(t *testing.T) { 16 | RegisterFailHandler(Fail) 17 | RunSpecs(t, "Rabbitmqclient Suite") 18 | } 19 | 20 | func mockRabbitMQServer() *ghttp.Server { 21 | return ghttp.NewServer() 22 | } 23 | 24 | func mockRabbitMQTLSServer() (*ghttp.Server, string, string, string) { 25 | fakeRabbitMQServer := ghttp.NewUnstartedServer() 26 | 27 | // create cert files 28 | serverCertPath, serverCertFile := testutils.CreateCertFile(1, "server.crt") 29 | serverKeyPath, serverKeyFile := testutils.CreateCertFile(1, "server.key") 30 | caCertPath, caCertFile := testutils.CreateCertFile(1, "ca.crt") 31 | 32 | // generate and write cert and key to file 33 | _, _ = testutils.CreateCertificateChain(1, "127.0.0.1", caCertFile, serverCertFile, serverKeyFile) 34 | 35 | cert, err := tls.LoadX509KeyPair(serverCertPath, serverKeyPath) 36 | ExpectWithOffset(1, err).NotTo(HaveOccurred()) 37 | fakeRabbitMQServer.HTTPTestServer.TLS = &tls.Config{Certificates: []tls.Certificate{cert}} 38 | fakeRabbitMQServer.HTTPTestServer.StartTLS() 39 | return fakeRabbitMQServer, serverCertPath, serverKeyPath, caCertPath 40 | } 41 | 42 | func mockRabbitMQURLPort(fakeRabbitMQServer *ghttp.Server) (*url.URL, int, error) { 43 | fakeRabbitMQURL, err := url.Parse(fakeRabbitMQServer.URL()) 44 | if err != nil { 45 | return nil, 0, err 46 | } 47 | fakeRabbitMQPort, err := strconv.Atoi(fakeRabbitMQURL.Port()) 48 | if err != nil { 49 | return nil, 0, err 50 | } 51 | return fakeRabbitMQURL, fakeRabbitMQPort, nil 52 | } 53 | -------------------------------------------------------------------------------- /api/v1beta1/suite_test.go: -------------------------------------------------------------------------------- 1 | package v1beta1 2 | 3 | import ( 4 | "errors" 5 | "path/filepath" 6 | "sigs.k8s.io/controller-runtime/pkg/webhook/admission" 7 | "testing" 8 | 9 | . "github.com/onsi/ginkgo/v2" 10 | . "github.com/onsi/gomega" 11 | 12 | "k8s.io/client-go/kubernetes/scheme" 13 | "k8s.io/client-go/rest" 14 | "sigs.k8s.io/controller-runtime/pkg/client" 15 | "sigs.k8s.io/controller-runtime/pkg/envtest" 16 | logf "sigs.k8s.io/controller-runtime/pkg/log" 17 | "sigs.k8s.io/controller-runtime/pkg/log/zap" 18 | ) 19 | 20 | var cfg *rest.Config 21 | var k8sClient client.Client 22 | var testEnv *envtest.Environment 23 | 24 | func TestAPIs(t *testing.T) { 25 | RegisterFailHandler(Fail) 26 | RunSpecs(t, "v1beta1 Suite") 27 | } 28 | 29 | var _ = BeforeSuite(func() { 30 | 31 | logf.SetLogger(zap.New(zap.UseDevMode(true), zap.WriteTo(GinkgoWriter))) 32 | 33 | By("bootstrapping test environment") 34 | testEnv = &envtest.Environment{ 35 | CRDDirectoryPaths: []string{filepath.Join("..", "..", "config", "crd", "bases")}, 36 | } 37 | 38 | testEnv.ControlPlane.GetAPIServer().Configure().Set("bind-address", "127.0.0.1") 39 | 40 | err := SchemeBuilder.AddToScheme(scheme.Scheme) 41 | Expect(err).NotTo(HaveOccurred()) 42 | 43 | cfg, err = testEnv.Start() 44 | Expect(err).ToNot(HaveOccurred()) 45 | Expect(cfg).ToNot(BeNil()) 46 | 47 | k8sClient, err = client.New(cfg, client.Options{Scheme: scheme.Scheme}) 48 | Expect(err).ToNot(HaveOccurred()) 49 | Expect(k8sClient).ToNot(BeNil()) 50 | }) 51 | 52 | var _ = AfterSuite(func() { 53 | By("tearing down the test environment") 54 | err := testEnv.Stop() 55 | Expect(err).ToNot(HaveOccurred()) 56 | }) 57 | 58 | func ignoreNilWarning(w admission.Warnings, e error) error { 59 | if w != nil { 60 | return errors.New("warning should be nil") 61 | 62 | } 63 | return e 64 | } 65 | -------------------------------------------------------------------------------- /controllers/common.go: -------------------------------------------------------------------------------- 1 | package controllers 2 | 3 | // common error messages shared across controllers 4 | const ( 5 | failedStatusUpdate = "failed to update object status" 6 | failedMarshalSpec = "failed to marshal spec" 7 | failedGenerateRabbitClient = "failed to generate http rabbitClient" 8 | failedParseClusterRef = "failed to retrieve cluster from reference" 9 | failedRetrieveSysCertPool = "failed to retrieve system trusted certs" 10 | noSuchRabbitDeletion = "RabbitmqCluster is already gone: cannot find its connection secret" 11 | ) 12 | 13 | // names for each of the controllers 14 | const ( 15 | VhostControllerName = "vhost-controller" 16 | QueueControllerName = "queue-controller" 17 | ExchangeControllerName = "exchange-controller" 18 | BindingControllerName = "binding-controller" 19 | UserControllerName = "user-controller" 20 | PolicyControllerName = "policy-controller" 21 | OperatorPolicyControllerName = "operator-policy-controller" 22 | PermissionControllerName = "permission-controller" 23 | SchemaReplicationControllerName = "schema-replication-controller" 24 | FederationControllerName = "federation-controller" 25 | ShovelControllerName = "shovel-controller" 26 | SuperStreamControllerName = "super-stream-controller" 27 | TopicPermissionControllerName = "topic-permission-controller" 28 | ) 29 | 30 | // names for environment variables 31 | const ( 32 | KubernetesInternalDomainEnvVar = "MESSAGING_DOMAIN_NAME" 33 | OperatorNamespaceEnvVar = "OPERATOR_NAMESPACE" 34 | EnableWebhooksEnvVar = "ENABLE_WEBHOOKS" 35 | ControllerSyncPeriodEnvVar = "SYNC_PERIOD" 36 | ConnectUsingPlainHTTPEnvVar = "CONNECT_USING_PLAIN_HTTP" 37 | MaxConcurrentReconciles = "MAX_CONCURRENT_RECONCILES" 38 | ) 39 | -------------------------------------------------------------------------------- /internal/managedresource/superstream_exchange.go: -------------------------------------------------------------------------------- 1 | package managedresource 2 | 3 | import ( 4 | "fmt" 5 | topology "github.com/rabbitmq/messaging-topology-operator/api/v1beta1" 6 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 7 | "sigs.k8s.io/controller-runtime/pkg/client" 8 | "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" 9 | ) 10 | 11 | const ( 12 | superStreamExchangeSuffix = "-exchange" 13 | ) 14 | 15 | type SuperStreamExchangeBuilder struct { 16 | *Builder 17 | vhost string 18 | rabbitmqCluster *topology.RabbitmqClusterReference 19 | } 20 | 21 | func (builder *Builder) SuperStreamExchange(vhost string, rabbitmqCluster *topology.RabbitmqClusterReference) *SuperStreamExchangeBuilder { 22 | return &SuperStreamExchangeBuilder{builder, vhost, rabbitmqCluster} 23 | } 24 | 25 | func (builder *SuperStreamExchangeBuilder) Build() (client.Object, error) { 26 | return &topology.Exchange{ 27 | ObjectMeta: metav1.ObjectMeta{ 28 | Name: builder.GenerateChildResourceName(superStreamExchangeSuffix), 29 | Namespace: builder.ObjectOwner.GetNamespace(), 30 | Labels: map[string]string{ 31 | AnnotationSuperStream: builder.ObjectOwner.GetName(), 32 | }, 33 | }, 34 | }, nil 35 | } 36 | 37 | func (builder *SuperStreamExchangeBuilder) Update(object client.Object) error { 38 | exchange := object.(*topology.Exchange) 39 | exchange.Spec.Durable = true 40 | exchange.Spec.Name = builder.ObjectOwner.GetName() 41 | exchange.Spec.Vhost = builder.vhost 42 | exchange.Spec.RabbitmqClusterReference = *builder.rabbitmqCluster 43 | 44 | if err := controllerutil.SetControllerReference(builder.ObjectOwner, object, builder.Scheme); err != nil { 45 | return fmt.Errorf("failed setting controller reference: %w", err) 46 | } 47 | 48 | return nil 49 | } 50 | 51 | func (builder *SuperStreamExchangeBuilder) ResourceType() string { return "Exchange" } 52 | -------------------------------------------------------------------------------- /internal/exchange_settings_test.go: -------------------------------------------------------------------------------- 1 | package internal_test 2 | 3 | import ( 4 | . "github.com/onsi/ginkgo/v2" 5 | . "github.com/onsi/gomega" 6 | topology "github.com/rabbitmq/messaging-topology-operator/api/v1beta1" 7 | "github.com/rabbitmq/messaging-topology-operator/internal" 8 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 9 | "k8s.io/apimachinery/pkg/runtime" 10 | ) 11 | 12 | var _ = Describe("GenerateExchangeSettings", func() { 13 | var e *topology.Exchange 14 | 15 | BeforeEach(func() { 16 | e = &topology.Exchange{ 17 | ObjectMeta: metav1.ObjectMeta{ 18 | Name: "exchange", 19 | }, 20 | Spec: topology.ExchangeSpec{ 21 | Type: "fanout", 22 | Durable: true, 23 | AutoDelete: true, 24 | }, 25 | } 26 | }) 27 | 28 | It("sets the type according to exchange.spec", func() { 29 | settings, err := internal.GenerateExchangeSettings(e) 30 | Expect(err).NotTo(HaveOccurred()) 31 | Expect(settings.Type).To(Equal("fanout")) 32 | }) 33 | 34 | It("sets AutoDelete according to exchange.spec", func() { 35 | settings, err := internal.GenerateExchangeSettings(e) 36 | Expect(err).NotTo(HaveOccurred()) 37 | Expect(settings.AutoDelete).To(BeTrue()) 38 | }) 39 | 40 | It("sets Durable according to exchange.spec", func() { 41 | settings, err := internal.GenerateExchangeSettings(e) 42 | Expect(err).NotTo(HaveOccurred()) 43 | Expect(settings.Durable).To(BeTrue()) 44 | }) 45 | 46 | When("exchange arguments are provided", func() { 47 | It("generates the correct exchange arguments", func() { 48 | e.Spec.Arguments = &runtime.RawExtension{ 49 | Raw: []byte(`{"alternate-exchange": "alt-exchange"}`), 50 | } 51 | settings, err := internal.GenerateExchangeSettings(e) 52 | Expect(err).NotTo(HaveOccurred()) 53 | Expect(settings.Arguments).To(HaveLen(1)) 54 | Expect(settings.Arguments).To(HaveKeyWithValue("alternate-exchange", "alt-exchange")) 55 | }) 56 | }) 57 | 58 | }) 59 | -------------------------------------------------------------------------------- /api/v1beta1/groupversion_info.go: -------------------------------------------------------------------------------- 1 | /* 2 | RabbitMQ Messaging Topology Kubernetes Operator 3 | Copyright 2021 VMware, Inc. 4 | 5 | This product is licensed to you under the Mozilla Public License 2.0 license (the "License"). You may not use this product except in compliance with the Mozilla 2.0 License. 6 | 7 | This product may include a number of subcomponents with separate copyright notices and license terms. Your use of these subcomponents is subject to the terms and conditions of the subcomponent's license, as noted in the LICENSE file. 8 | */ 9 | 10 | // Package v1beta1 contains API Schema definitions for the rabbitmq.com v1beta1 API group 11 | // +kubebuilder:object:generate=true 12 | // +groupName=rabbitmq.com 13 | package v1beta1 14 | 15 | import ( 16 | "k8s.io/apimachinery/pkg/runtime/schema" 17 | "sigs.k8s.io/controller-runtime/pkg/scheme" 18 | ) 19 | 20 | var ( 21 | // GroupVersion is group version used to register these objects 22 | GroupVersion = schema.GroupVersion{Group: "rabbitmq.com", Version: "v1beta1"} 23 | 24 | // SchemeGroupVersion is group version used to register these objects 25 | // added for generated clientset 26 | SchemeGroupVersion = GroupVersion 27 | 28 | // SchemeBuilder is used to add go types to the GroupVersionKind scheme 29 | SchemeBuilder = &scheme.Builder{GroupVersion: GroupVersion} 30 | 31 | // AddToScheme adds the types in this group-version to the given scheme. 32 | AddToScheme = SchemeBuilder.AddToScheme 33 | ) 34 | 35 | // Kind takes an unqualified kind and returns back a Group qualified GroupKind 36 | // added for generated clientset 37 | func Kind(kind string) schema.GroupKind { 38 | return GroupVersion.WithKind(kind).GroupKind() 39 | } 40 | 41 | // Resource takes an unqualified resource and returns a Group qualified GroupResource 42 | // added for generated clientset 43 | func Resource(resource string) schema.GroupResource { 44 | return GroupVersion.WithResource(resource).GroupResource() 45 | } 46 | -------------------------------------------------------------------------------- /api/v1alpha1/groupversion_info.go: -------------------------------------------------------------------------------- 1 | /* 2 | RabbitMQ Messaging Topology Kubernetes Operator 3 | Copyright 2021 VMware, Inc. 4 | 5 | This product is licensed to you under the Mozilla Public License 2.0 license (the "License"). You may not use this product except in compliance with the Mozilla 2.0 License. 6 | 7 | This product may include a number of subcomponents with separate copyright notices and license terms. Your use of these subcomponents is subject to the terms and conditions of the subcomponent's license, as noted in the LICENSE file. 8 | */ 9 | 10 | // Package v1alpha1 contains API Schema definitions for the rabbitmq.com v1alpha1 API group 11 | // +kubebuilder:object:generate=true 12 | // +groupName=rabbitmq.com 13 | package v1alpha1 14 | 15 | import ( 16 | "k8s.io/apimachinery/pkg/runtime/schema" 17 | "sigs.k8s.io/controller-runtime/pkg/scheme" 18 | ) 19 | 20 | var ( 21 | // GroupVersion is group version used to register these objects 22 | GroupVersion = schema.GroupVersion{Group: "rabbitmq.com", Version: "v1alpha1"} 23 | 24 | // SchemeGroupVersion is group version used to register these objects 25 | // added for generated clientset 26 | SchemeGroupVersion = GroupVersion 27 | 28 | // SchemeBuilder is used to add go types to the GroupVersionKind scheme 29 | SchemeBuilder = &scheme.Builder{GroupVersion: GroupVersion} 30 | 31 | // AddToScheme adds the types in this group-version to the given scheme. 32 | AddToScheme = SchemeBuilder.AddToScheme 33 | ) 34 | 35 | // Kind takes an unqualified kind and returns back a Group qualified GroupKind 36 | // added for generated clientset 37 | func Kind(kind string) schema.GroupKind { 38 | return GroupVersion.WithKind(kind).GroupKind() 39 | } 40 | 41 | // Resource takes an unqualified resource and returns a Group qualified GroupResource 42 | // added for generated clientset 43 | func Resource(resource string) schema.GroupResource { 44 | return GroupVersion.WithResource(resource).GroupResource() 45 | } 46 | -------------------------------------------------------------------------------- /docs/examples/federations/README.md: -------------------------------------------------------------------------------- 1 | # Federation Example 2 | 3 | In this example, we will create federation between exchanges from two different vhosts in the same RabbitmqCluster. 4 | 5 | Before creating any topology objects with Messaging Topology Operator, please deploy a RabbitmqCluster named `example-rabbit`, or any name you see fit. 6 | You will need to enable the `rabbitmq_federation` plugin and here is an example from the [cluster operator repo](https://github.com/rabbitmq/cluster-operator/blob/main/docs/examples/plugins/rabbitmq.yaml) about enabling additional plugins. 7 | 8 | After the RabbitMQ cluster is successfully created, you need to get username and password of the default user for this RabbitMQ cluster: 9 | 10 | ```bash 11 | kubectl get secret example-rabbit-default-user -o jsonpath='{.data.username}' | base64 --decode 12 | kubectl get secret example-rabbit-user -o jsonpath='{.data.password}' | base64 --decode 13 | ``` 14 | Save the username and password, because we need both later to construct federation upstream URI. 15 | 16 | This example includes (please create in order): 17 | 18 | 1. two vhosts: 'upstream' and 'downstream' 19 | 1. a Kubernetes secret 'federation-uri' containing the federation upstream URI 20 | 1. queue 'upstream-queue' in 'upstream' vhost 21 | 1. queue 'downstream-queue' in 'downstream-vhost' 22 | 1. fanout exchanges in both 'upstream' and 'downstream' vhost 23 | 1. bindings between fanout exchanges to 'upstream-queue' in 'upstream' vhost and 'downstream-queue' in 'downstream-vhost' 24 | 1. a federation upstream named 'origin' in 'downstream' vhost 25 | 1. a policy between federation upstream 'origin' and the fanout exchange in 'downstream' vhost 26 | 27 | After all topology objects are created, messages published to the fanout exchange in "upstream" will be published to the 28 | 'upstream-queue' in 'upstream-vhost' and federated to the 'downstream-queue' in 'downstream' vhost. 29 | 30 | Learn [more about RabbitMQ Federation](https://www.rabbitmq.com/federation.html). 31 | -------------------------------------------------------------------------------- /api/v1beta1/conditions.go: -------------------------------------------------------------------------------- 1 | package v1beta1 2 | 3 | import ( 4 | corev1 "k8s.io/api/core/v1" 5 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 6 | ) 7 | 8 | const ready ConditionType = "Ready" 9 | 10 | type ConditionType string 11 | 12 | type Condition struct { 13 | // Type indicates the scope of the custom resource status addressed by the condition. 14 | Type ConditionType `json:"type"` 15 | // True, False, or Unknown 16 | Status corev1.ConditionStatus `json:"status"` 17 | // The last time this Condition status changed. 18 | LastTransitionTime metav1.Time `json:"lastTransitionTime,omitempty"` 19 | // One word, camel-case reason for current status of the condition. 20 | Reason string `json:"reason,omitempty"` 21 | // Full text reason for current status of the condition. 22 | Message string `json:"message,omitempty"` 23 | } 24 | 25 | // Ready indicates that the last Create/Update operator on the CR was successful. 26 | func Ready(lastConditions []Condition) Condition { 27 | time := lastTransitionTime(corev1.ConditionTrue, lastConditions) 28 | return Condition{ 29 | Type: ready, 30 | Status: corev1.ConditionTrue, 31 | LastTransitionTime: time, 32 | Reason: "SuccessfulCreateOrUpdate", 33 | } 34 | } 35 | 36 | // NotReady indicates that the last Create/Update operator on the CR failed. 37 | func NotReady(msg string, lastConditions []Condition) Condition { 38 | time := lastTransitionTime(corev1.ConditionFalse, lastConditions) 39 | return Condition{ 40 | Type: ready, 41 | Status: corev1.ConditionFalse, 42 | LastTransitionTime: time, 43 | Reason: "FailedCreateOrUpdate", 44 | Message: msg, 45 | } 46 | } 47 | 48 | func lastTransitionTime(newStatus corev1.ConditionStatus, lastConditions []Condition) metav1.Time { 49 | for _, lastCondition := range lastConditions { 50 | if lastCondition.Type == ready && lastCondition.Status == newStatus { 51 | return lastCondition.LastTransitionTime 52 | } 53 | } 54 | return metav1.Now() 55 | } 56 | -------------------------------------------------------------------------------- /pkg/generated/clientset/versioned/fake/register.go: -------------------------------------------------------------------------------- 1 | /* 2 | RabbitMQ Messaging Topology Kubernetes Operator 3 | Copyright 2021 VMware, Inc. 4 | 5 | This product is licensed to you under the Mozilla Public License 2.0 license (the "License"). You may not use this product except in compliance with the Mozilla 2.0 License. 6 | 7 | This product may include a number of subcomponents with separate copyright notices and license terms. Your use of these subcomponents is subject to the terms and conditions of the subcomponent's license, as noted in the LICENSE file. 8 | */ 9 | 10 | // Code generated by client-gen. DO NOT EDIT. 11 | 12 | package fake 13 | 14 | import ( 15 | rabbitmqv1alpha1 "github.com/rabbitmq/messaging-topology-operator/api/v1alpha1" 16 | rabbitmqv1beta1 "github.com/rabbitmq/messaging-topology-operator/api/v1beta1" 17 | v1 "k8s.io/apimachinery/pkg/apis/meta/v1" 18 | runtime "k8s.io/apimachinery/pkg/runtime" 19 | schema "k8s.io/apimachinery/pkg/runtime/schema" 20 | serializer "k8s.io/apimachinery/pkg/runtime/serializer" 21 | utilruntime "k8s.io/apimachinery/pkg/util/runtime" 22 | ) 23 | 24 | var scheme = runtime.NewScheme() 25 | var codecs = serializer.NewCodecFactory(scheme) 26 | 27 | var localSchemeBuilder = runtime.SchemeBuilder{ 28 | rabbitmqv1alpha1.AddToScheme, 29 | rabbitmqv1beta1.AddToScheme, 30 | } 31 | 32 | // AddToScheme adds all types of this clientset into the given scheme. This allows composition 33 | // of clientsets, like in: 34 | // 35 | // import ( 36 | // "k8s.io/client-go/kubernetes" 37 | // clientsetscheme "k8s.io/client-go/kubernetes/scheme" 38 | // aggregatorclientsetscheme "k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset/scheme" 39 | // ) 40 | // 41 | // kclientset, _ := kubernetes.NewForConfig(c) 42 | // _ = aggregatorclientsetscheme.AddToScheme(clientsetscheme.Scheme) 43 | // 44 | // After this, RawExtensions in Kubernetes types will serialize kube-aggregator types 45 | // correctly. 46 | var AddToScheme = localSchemeBuilder.AddToScheme 47 | 48 | func init() { 49 | v1.AddToGroupVersion(scheme, schema.GroupVersion{Version: "v1"}) 50 | utilruntime.Must(AddToScheme(scheme)) 51 | } 52 | -------------------------------------------------------------------------------- /.github/workflows/publish-versioned-api-ref.yml: -------------------------------------------------------------------------------- 1 | name: "Publish Versioned API Reference Wiki page" 2 | 3 | on: 4 | push: 5 | tags: 6 | - 'v[0-9]+.[0-9]+.[0-9]+' 7 | 8 | jobs: 9 | publish-versioned-api-reference: 10 | name: Publish Versioned API Reference Wiki 11 | runs-on: ubuntu-latest 12 | 13 | steps: 14 | - name: Checkout operator codebase 15 | uses: actions/checkout@v6 16 | with: 17 | path: messaging-topology-operator 18 | 19 | - name: Get the version 20 | id: get_version 21 | run: echo VERSION="${GITHUB_REF#refs/tags/}" >> $GITHUB_OUTPUT 22 | 23 | - name: Checkout wiki codebase 24 | uses: actions/checkout@v6 25 | with: 26 | repository: ${{ github.repository }}.wiki 27 | path: wiki 28 | 29 | - uses: actions/setup-go@v6 30 | name: Install Go 31 | with: 32 | go-version-file: messaging-topology-operator/go.mod 33 | 34 | - name: Generate API reference 35 | run: make -C messaging-topology-operator api-reference 36 | 37 | - name: Push to wiki 38 | # User and email as documented here: https://github.com/marketplace/actions/checkout#push-a-commit-using-the-built-in-token 39 | run: | 40 | cd wiki 41 | git config --local user.email "41898282+github-actions[bot]@users.noreply.github.com" 42 | git config --local user.name "github-actions[bot]" 43 | # Add the versioned API Reference to the Wiki 44 | cp ../messaging-topology-operator/docs/api/rabbitmq.com.ref.asciidoc ./API_Reference_${{ steps.get_version.outputs.VERSION }}.asciidoc 45 | # Regenerate the ordered list of API Reference docs for the sidebar 46 | export REGENERATED_SIDEBAR="$(../messaging-topology-operator/hack/generate-ordered-api-reference-list.sh .)" 47 | echo "$REGENERATED_SIDEBAR" > Wiki_Sidebar.md 48 | git add ./API_Reference_${{ steps.get_version.outputs.VERSION }}.asciidoc 49 | git add ./Wiki_Sidebar.md 50 | git commit -m "Publish version ${{ steps.get_version.outputs.VERSION }} API Reference" 51 | git push 52 | -------------------------------------------------------------------------------- /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/rabbitmq.com_queues.yaml 6 | - bases/rabbitmq.com_exchanges.yaml 7 | - bases/rabbitmq.com_bindings.yaml 8 | - bases/rabbitmq.com_users.yaml 9 | - bases/rabbitmq.com_vhosts.yaml 10 | - bases/rabbitmq.com_policies.yaml 11 | - bases/rabbitmq.com_operatorpolicies.yaml 12 | - bases/rabbitmq.com_permissions.yaml 13 | - bases/rabbitmq.com_schemareplications.yaml 14 | - bases/rabbitmq.com_federations.yaml 15 | - bases/rabbitmq.com_shovels.yaml 16 | - bases/rabbitmq.com_superstreams.yaml 17 | - bases/rabbitmq.com_topicpermissions.yaml 18 | # +kubebuilder:scaffold:crdkustomizeresource 19 | 20 | #patchesStrategicMerge: 21 | #- patches/webhook_in_bindings.yaml 22 | #- patches/webhook_in_queues.yaml 23 | #- patches/webhook_in_exchanges.yaml 24 | #- patches/webhook_in_vhosts.yaml 25 | #- patches/webhook_in_policies.yaml 26 | #- patches/webhook_in_operatorpolicies.yaml 27 | #- patches/webhook_in_users.yaml 28 | #- patches/webhook_in_permissions.yaml 29 | #- patches/webhook_in_schemareplications.yaml 30 | #- patches/webhook_in_federations.yaml 31 | #- patches/webhook_in_shovels.yaml 32 | #- patches/webhook_in_superstreams.yaml 33 | #- patches/webhook_in_topicpermissions.yaml 34 | # +kubebuilder:scaffold:crdkustomizewebhookpatch 35 | 36 | #- patches/cainjection_in_bindings.yaml 37 | #- patches/cainjection_in_queues.yaml 38 | #- patches/cainjection_in_exchanges.yaml 39 | #- patches/cainjection_in_vhosts.yaml 40 | #- patches/cainjection_in_policies.yaml 41 | #- patches/cainjection_in_operatorpolicies.yaml 42 | #- patches/cainjection_in_users.yaml 43 | #- patches/cainjection_in_permissions.yaml 44 | #- patches/cainjection_in_schemareplications.yaml 45 | #- patches/cainjection_in_federations.yaml 46 | #- patches/cainjection_in_shovels.yaml 47 | #- patches/cainjection_in_superstreams.yaml 48 | #- patches/cainjection_in_topicpermissions.yaml 49 | # +kubebuilder:scaffold:crdkustomizecainjectionpatch 50 | 51 | configurations: 52 | - kustomizeconfig.yaml 53 | -------------------------------------------------------------------------------- /pkg/generated/clientset/versioned/scheme/register.go: -------------------------------------------------------------------------------- 1 | /* 2 | RabbitMQ Messaging Topology Kubernetes Operator 3 | Copyright 2021 VMware, Inc. 4 | 5 | This product is licensed to you under the Mozilla Public License 2.0 license (the "License"). You may not use this product except in compliance with the Mozilla 2.0 License. 6 | 7 | This product may include a number of subcomponents with separate copyright notices and license terms. Your use of these subcomponents is subject to the terms and conditions of the subcomponent's license, as noted in the LICENSE file. 8 | */ 9 | 10 | // Code generated by client-gen. DO NOT EDIT. 11 | 12 | package scheme 13 | 14 | import ( 15 | rabbitmqv1alpha1 "github.com/rabbitmq/messaging-topology-operator/api/v1alpha1" 16 | rabbitmqv1beta1 "github.com/rabbitmq/messaging-topology-operator/api/v1beta1" 17 | v1 "k8s.io/apimachinery/pkg/apis/meta/v1" 18 | runtime "k8s.io/apimachinery/pkg/runtime" 19 | schema "k8s.io/apimachinery/pkg/runtime/schema" 20 | serializer "k8s.io/apimachinery/pkg/runtime/serializer" 21 | utilruntime "k8s.io/apimachinery/pkg/util/runtime" 22 | ) 23 | 24 | var Scheme = runtime.NewScheme() 25 | var Codecs = serializer.NewCodecFactory(Scheme) 26 | var ParameterCodec = runtime.NewParameterCodec(Scheme) 27 | var localSchemeBuilder = runtime.SchemeBuilder{ 28 | rabbitmqv1alpha1.AddToScheme, 29 | rabbitmqv1beta1.AddToScheme, 30 | } 31 | 32 | // AddToScheme adds all types of this clientset into the given scheme. This allows composition 33 | // of clientsets, like in: 34 | // 35 | // import ( 36 | // "k8s.io/client-go/kubernetes" 37 | // clientsetscheme "k8s.io/client-go/kubernetes/scheme" 38 | // aggregatorclientsetscheme "k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset/scheme" 39 | // ) 40 | // 41 | // kclientset, _ := kubernetes.NewForConfig(c) 42 | // _ = aggregatorclientsetscheme.AddToScheme(clientsetscheme.Scheme) 43 | // 44 | // After this, RawExtensions in Kubernetes types will serialize kube-aggregator types 45 | // correctly. 46 | var AddToScheme = localSchemeBuilder.AddToScheme 47 | 48 | func init() { 49 | v1.AddToGroupVersion(Scheme, schema.GroupVersion{Version: "v1"}) 50 | utilruntime.Must(AddToScheme(Scheme)) 51 | } 52 | -------------------------------------------------------------------------------- /internal/policy_test.go: -------------------------------------------------------------------------------- 1 | package internal_test 2 | 3 | import ( 4 | . "github.com/onsi/ginkgo/v2" 5 | . "github.com/onsi/gomega" 6 | topology "github.com/rabbitmq/messaging-topology-operator/api/v1beta1" 7 | . "github.com/rabbitmq/messaging-topology-operator/internal" 8 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 9 | "k8s.io/apimachinery/pkg/runtime" 10 | ) 11 | 12 | var _ = Describe("GeneratePolicy", func() { 13 | var p *topology.Policy 14 | 15 | BeforeEach(func() { 16 | p = &topology.Policy{ 17 | ObjectMeta: metav1.ObjectMeta{ 18 | Name: "new-policy", 19 | }, 20 | Spec: topology.PolicySpec{ 21 | Name: "new-p", 22 | Vhost: "/new-vhost", 23 | ApplyTo: "exchanges", 24 | Pattern: "exchange-name", 25 | Priority: 5, 26 | Definition: &runtime.RawExtension{Raw: []byte(`{"key":"value"}`)}, 27 | }, 28 | } 29 | }) 30 | 31 | It("sets policy name according to policySpec", func() { 32 | generated, err := GeneratePolicy(p) 33 | Expect(err).NotTo(HaveOccurred()) 34 | Expect(generated.Name).To(Equal("new-p")) 35 | }) 36 | 37 | It("sets policy vhost according to policySpec", func() { 38 | generated, err := GeneratePolicy(p) 39 | Expect(err).NotTo(HaveOccurred()) 40 | Expect(generated.Vhost).To(Equal("/new-vhost")) 41 | }) 42 | 43 | It("sets 'ApplyTo' according to policySpec", func() { 44 | generated, err := GeneratePolicy(p) 45 | Expect(err).NotTo(HaveOccurred()) 46 | Expect(generated.ApplyTo).To(Equal("exchanges")) 47 | }) 48 | 49 | It("sets 'priority' according to policySpec", func() { 50 | generated, err := GeneratePolicy(p) 51 | Expect(err).NotTo(HaveOccurred()) 52 | Expect(generated.Priority).To(Equal(5)) 53 | }) 54 | 55 | It("sets 'pattern' according to policySpec", func() { 56 | generated, err := GeneratePolicy(p) 57 | Expect(err).NotTo(HaveOccurred()) 58 | Expect(generated.Pattern).To(Equal("exchange-name")) 59 | }) 60 | 61 | It("sets definition according to policySpec", func() { 62 | generated, err := GeneratePolicy(p) 63 | Expect(err).NotTo(HaveOccurred()) 64 | Expect(generated.Definition).Should(HaveLen(1)) 65 | Expect(generated.Definition).Should(HaveKeyWithValue("key", "value")) 66 | }) 67 | }) 68 | -------------------------------------------------------------------------------- /pkg/generated/informers/externalversions/rabbitmq.com/interface.go: -------------------------------------------------------------------------------- 1 | /* 2 | RabbitMQ Messaging Topology Kubernetes Operator 3 | Copyright 2021 VMware, Inc. 4 | 5 | This product is licensed to you under the Mozilla Public License 2.0 license (the "License"). You may not use this product except in compliance with the Mozilla 2.0 License. 6 | 7 | This product may include a number of subcomponents with separate copyright notices and license terms. Your use of these subcomponents is subject to the terms and conditions of the subcomponent's license, as noted in the LICENSE file. 8 | */ 9 | 10 | // Code generated by informer-gen. DO NOT EDIT. 11 | 12 | package rabbitmq 13 | 14 | import ( 15 | internalinterfaces "github.com/rabbitmq/messaging-topology-operator/pkg/generated/informers/externalversions/internalinterfaces" 16 | v1alpha1 "github.com/rabbitmq/messaging-topology-operator/pkg/generated/informers/externalversions/rabbitmq.com/v1alpha1" 17 | v1beta1 "github.com/rabbitmq/messaging-topology-operator/pkg/generated/informers/externalversions/rabbitmq.com/v1beta1" 18 | ) 19 | 20 | // Interface provides access to each of this group's versions. 21 | type Interface interface { 22 | // V1alpha1 provides access to shared informers for resources in V1alpha1. 23 | V1alpha1() v1alpha1.Interface 24 | // V1beta1 provides access to shared informers for resources in V1beta1. 25 | V1beta1() v1beta1.Interface 26 | } 27 | 28 | type group struct { 29 | factory internalinterfaces.SharedInformerFactory 30 | namespace string 31 | tweakListOptions internalinterfaces.TweakListOptionsFunc 32 | } 33 | 34 | // New returns a new Interface. 35 | func New(f internalinterfaces.SharedInformerFactory, namespace string, tweakListOptions internalinterfaces.TweakListOptionsFunc) Interface { 36 | return &group{factory: f, namespace: namespace, tweakListOptions: tweakListOptions} 37 | } 38 | 39 | // V1alpha1 returns a new v1alpha1.Interface. 40 | func (g *group) V1alpha1() v1alpha1.Interface { 41 | return v1alpha1.New(g.factory, g.namespace, g.tweakListOptions) 42 | } 43 | 44 | // V1beta1 returns a new v1beta1.Interface. 45 | func (g *group) V1beta1() v1beta1.Interface { 46 | return v1beta1.New(g.factory, g.namespace, g.tweakListOptions) 47 | } 48 | -------------------------------------------------------------------------------- /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 | - "" 9 | resources: 10 | - events 11 | verbs: 12 | - create 13 | - get 14 | - patch 15 | - apiGroups: 16 | - "" 17 | resources: 18 | - secrets 19 | verbs: 20 | - create 21 | - get 22 | - list 23 | - watch 24 | - apiGroups: 25 | - "" 26 | resources: 27 | - services 28 | verbs: 29 | - get 30 | - list 31 | - watch 32 | - apiGroups: 33 | - rabbitmq.com 34 | resources: 35 | - bindings 36 | - exchanges 37 | - federations 38 | - operatorpolicies 39 | - permissions 40 | - policies 41 | - queues 42 | - schemareplications 43 | - shovels 44 | - superstreams 45 | - topicpermissions 46 | - users 47 | - vhosts 48 | verbs: 49 | - create 50 | - delete 51 | - get 52 | - list 53 | - patch 54 | - update 55 | - watch 56 | - apiGroups: 57 | - rabbitmq.com 58 | resources: 59 | - bindings/finalizers 60 | - exchanges/finalizers 61 | - federations/finalizers 62 | - operatorpolicies/finalizers 63 | - permissions/finalizers 64 | - policies/finalizers 65 | - queues/finalizers 66 | - schemareplications/finalizers 67 | - shovels/finalizers 68 | - superstreams/finalizers 69 | - topicpermissions/finalizers 70 | - users/finalizers 71 | - vhosts/finalizers 72 | verbs: 73 | - update 74 | - apiGroups: 75 | - rabbitmq.com 76 | resources: 77 | - bindings/status 78 | - exchanges/status 79 | - federations/status 80 | - operatorpolicies/status 81 | - permissions/status 82 | - policies/status 83 | - queues/status 84 | - schemareplications/status 85 | - shovels/status 86 | - superstreams/status 87 | - topicpermissions/status 88 | - users/status 89 | - vhosts/status 90 | verbs: 91 | - get 92 | - patch 93 | - update 94 | - apiGroups: 95 | - rabbitmq.com 96 | resources: 97 | - rabbitmqclusters 98 | verbs: 99 | - get 100 | - list 101 | - watch 102 | - apiGroups: 103 | - rabbitmq.com 104 | resources: 105 | - rabbitmqclusters/status 106 | verbs: 107 | - get 108 | -------------------------------------------------------------------------------- /internal/queue_delete_options_test.go: -------------------------------------------------------------------------------- 1 | package internal_test 2 | 3 | import ( 4 | . "github.com/onsi/ginkgo/v2" 5 | . "github.com/onsi/gomega" 6 | topology "github.com/rabbitmq/messaging-topology-operator/api/v1beta1" 7 | "github.com/rabbitmq/messaging-topology-operator/internal" 8 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 9 | ) 10 | 11 | var _ = Describe("GenerateQueueDeleteOptionsQuorum", func() { 12 | var q *topology.Queue 13 | 14 | BeforeEach(func() { 15 | q = &topology.Queue{ 16 | ObjectMeta: metav1.ObjectMeta{ 17 | Name: "a-queue", 18 | }, 19 | Spec: topology.QueueSpec{ 20 | Type: "quorum", 21 | AutoDelete: false, 22 | Durable: true, 23 | DeleteIfEmpty: true, 24 | DeleteIfUnused: false, 25 | }, 26 | } 27 | }) 28 | 29 | It("sets QueueDeleteOptions.IfEmpty to false because we handle a quorum queue", func() { 30 | options, err := internal.GenerateQueueDeleteOptions(q) 31 | Expect(err).NotTo(HaveOccurred()) 32 | Expect(options.IfEmpty).To(BeFalse()) 33 | }) 34 | 35 | It("sets QueueDeleteOptions.IfUnused to false because we handle a quorum queue", func() { 36 | options, err := internal.GenerateQueueDeleteOptions(q) 37 | Expect(err).NotTo(HaveOccurred()) 38 | Expect(options.IfUnused).To(BeFalse()) 39 | }) 40 | 41 | }) 42 | 43 | var _ = Describe("GenerateQueueDeleteOptionsClassic", func() { 44 | var q *topology.Queue 45 | 46 | BeforeEach(func() { 47 | q = &topology.Queue{ 48 | ObjectMeta: metav1.ObjectMeta{ 49 | Name: "a-queue", 50 | }, 51 | Spec: topology.QueueSpec{ 52 | Type: "classic", 53 | AutoDelete: false, 54 | Durable: true, 55 | DeleteIfEmpty: true, 56 | DeleteIfUnused: false, 57 | }, 58 | } 59 | }) 60 | 61 | It("sets QueueDeleteOptions.IfEmpty according to queue.spec", func() { 62 | options, err := internal.GenerateQueueDeleteOptions(q) 63 | Expect(err).NotTo(HaveOccurred()) 64 | Expect(options.IfEmpty).To(BeTrue()) 65 | }) 66 | 67 | It("sets QueueDeleteOptions.IfUnused according to queue.spec", func() { 68 | options, err := internal.GenerateQueueDeleteOptions(q) 69 | Expect(err).NotTo(HaveOccurred()) 70 | Expect(options.IfUnused).To(BeFalse()) 71 | }) 72 | 73 | }) 74 | -------------------------------------------------------------------------------- /api/v1beta1/user_webhook.go: -------------------------------------------------------------------------------- 1 | package v1beta1 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | apierrors "k8s.io/apimachinery/pkg/api/errors" 7 | "k8s.io/apimachinery/pkg/runtime" 8 | "k8s.io/apimachinery/pkg/util/validation/field" 9 | ctrl "sigs.k8s.io/controller-runtime" 10 | "sigs.k8s.io/controller-runtime/pkg/webhook" 11 | "sigs.k8s.io/controller-runtime/pkg/webhook/admission" 12 | ) 13 | 14 | func (u *User) SetupWebhookWithManager(mgr ctrl.Manager) error { 15 | return ctrl.NewWebhookManagedBy(mgr). 16 | WithValidator(u). 17 | For(u). 18 | Complete() 19 | } 20 | 21 | // +kubebuilder:webhook:verbs=create;update,path=/validate-rabbitmq-com-v1beta1-user,mutating=false,failurePolicy=fail,groups=rabbitmq.com,resources=users,versions=v1beta1,name=vuser.kb.io,sideEffects=none,admissionReviewVersions=v1 22 | 23 | var _ webhook.CustomValidator = &User{} 24 | 25 | // ValidateCreate - either rabbitmqClusterReference.name or 26 | // rabbitmqClusterReference.connectionSecret must be provided but not both 27 | func (u *User) ValidateCreate(_ context.Context, obj runtime.Object) (warnings admission.Warnings, err error) { 28 | user, ok := obj.(*User) 29 | if !ok { 30 | return nil, fmt.Errorf("expected a RabbitMQ user but got a %T", obj) 31 | } 32 | return nil, u.Spec.RabbitmqClusterReference.validate(user.RabbitReference()) 33 | } 34 | 35 | // ValidateUpdate returns error type 'forbidden' for updates on rabbitmqClusterReference 36 | func (u *User) ValidateUpdate(_ context.Context, oldObj, newObj runtime.Object) (warnings admission.Warnings, err error) { 37 | oldUser, ok := oldObj.(*User) 38 | if !ok { 39 | return nil, apierrors.NewBadRequest(fmt.Sprintf("expected a user but got a %T", oldObj)) 40 | } 41 | 42 | newUser, ok := newObj.(*User) 43 | if !ok { 44 | return nil, apierrors.NewBadRequest(fmt.Sprintf("expected a user but got a %T", newObj)) 45 | } 46 | 47 | if !oldUser.Spec.RabbitmqClusterReference.Matches(&newUser.Spec.RabbitmqClusterReference) { 48 | return nil, apierrors.NewForbidden(newUser.GroupResource(), newUser.Name, 49 | field.Forbidden(field.NewPath("spec", "rabbitmqClusterReference"), "update on rabbitmqClusterReference is forbidden")) 50 | } 51 | return nil, nil 52 | } 53 | 54 | func (u *User) ValidateDelete(_ context.Context, obj runtime.Object) (warnings admission.Warnings, err error) { 55 | return nil, nil 56 | } 57 | -------------------------------------------------------------------------------- /internal/operator_policy_test.go: -------------------------------------------------------------------------------- 1 | package internal_test 2 | 3 | import ( 4 | . "github.com/onsi/ginkgo/v2" 5 | . "github.com/onsi/gomega" 6 | topology "github.com/rabbitmq/messaging-topology-operator/api/v1beta1" 7 | . "github.com/rabbitmq/messaging-topology-operator/internal" 8 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 9 | "k8s.io/apimachinery/pkg/runtime" 10 | ) 11 | 12 | var _ = Describe("GenerateOperatorPolicy", func() { 13 | var p *topology.OperatorPolicy 14 | 15 | BeforeEach(func() { 16 | p = &topology.OperatorPolicy{ 17 | ObjectMeta: metav1.ObjectMeta{ 18 | Name: "new-operatorPolicy", 19 | }, 20 | Spec: topology.OperatorPolicySpec{ 21 | Name: "new-p", 22 | Vhost: "/new-vhost", 23 | ApplyTo: "queues", 24 | Pattern: "queue-name", 25 | Priority: 5, 26 | Definition: &runtime.RawExtension{Raw: []byte(`{"key":"value"}`)}, 27 | }, 28 | } 29 | }) 30 | 31 | It("sets operatorPolicy name according to operatorPolicySpec", func() { 32 | generated, err := GenerateOperatorPolicy(p) 33 | Expect(err).NotTo(HaveOccurred()) 34 | Expect(generated.Name).To(Equal("new-p")) 35 | }) 36 | 37 | It("sets operatorPolicy vhost according to operatorPolicySpec", func() { 38 | generated, err := GenerateOperatorPolicy(p) 39 | Expect(err).NotTo(HaveOccurred()) 40 | Expect(generated.Vhost).To(Equal("/new-vhost")) 41 | }) 42 | 43 | It("sets 'ApplyTo' according to operatorPolicySpec", func() { 44 | generated, err := GenerateOperatorPolicy(p) 45 | Expect(err).NotTo(HaveOccurred()) 46 | Expect(generated.ApplyTo).To(Equal("queues")) 47 | }) 48 | 49 | It("sets 'priority' according to operatorPolicySpec", func() { 50 | generated, err := GenerateOperatorPolicy(p) 51 | Expect(err).NotTo(HaveOccurred()) 52 | Expect(generated.Priority).To(Equal(5)) 53 | }) 54 | 55 | It("sets 'pattern' according to operatorPolicySpec", func() { 56 | generated, err := GenerateOperatorPolicy(p) 57 | Expect(err).NotTo(HaveOccurred()) 58 | Expect(generated.Pattern).To(Equal("queue-name")) 59 | }) 60 | 61 | It("sets definition according to operatorPolicySpec", func() { 62 | generated, err := GenerateOperatorPolicy(p) 63 | Expect(err).NotTo(HaveOccurred()) 64 | Expect(generated.Definition).Should(HaveLen(1)) 65 | Expect(generated.Definition).Should(HaveKeyWithValue("key", "value")) 66 | }) 67 | }) 68 | -------------------------------------------------------------------------------- /controllers/exchange_controller.go: -------------------------------------------------------------------------------- 1 | /* 2 | RabbitMQ Messaging Topology Kubernetes Operator 3 | Copyright 2021 VMware, Inc. 4 | 5 | This product is licensed to you under the Mozilla Public License 2.0 license (the "License"). You may not use this product except in compliance with the Mozilla 2.0 License. 6 | 7 | This product may include a number of subcomponents with separate copyright notices and license terms. Your use of these subcomponents is subject to the terms and conditions of the subcomponent's license, as noted in the LICENSE file. 8 | */ 9 | 10 | package controllers 11 | 12 | import ( 13 | "context" 14 | "errors" 15 | "fmt" 16 | 17 | topology "github.com/rabbitmq/messaging-topology-operator/api/v1beta1" 18 | "github.com/rabbitmq/messaging-topology-operator/internal" 19 | "github.com/rabbitmq/messaging-topology-operator/rabbitmqclient" 20 | ctrl "sigs.k8s.io/controller-runtime" 21 | ) 22 | 23 | // +kubebuilder:rbac:groups=rabbitmq.com,resources=exchanges,verbs=get;list;watch;create;update;patch;delete 24 | // +kubebuilder:rbac:groups=rabbitmq.com,resources=exchanges/finalizers,verbs=update 25 | // +kubebuilder:rbac:groups=rabbitmq.com,resources=exchanges/status,verbs=get;update;patch 26 | 27 | type ExchangeReconciler struct{} 28 | 29 | func (r *ExchangeReconciler) DeclareFunc(_ context.Context, client rabbitmqclient.Client, obj topology.TopologyResource) error { 30 | exchange := obj.(*topology.Exchange) 31 | settings, err := internal.GenerateExchangeSettings(exchange) 32 | if err != nil { 33 | return fmt.Errorf("failed to generate exchange settings: %w", err) 34 | } 35 | return validateResponse(client.DeclareExchange(exchange.Spec.Vhost, exchange.Spec.Name, *settings)) 36 | } 37 | 38 | // DeleteFunc deletes exchange from rabbitmq server 39 | // if server responds with '404' Not Found, it logs and does not requeue on error 40 | func (r *ExchangeReconciler) DeleteFunc(ctx context.Context, client rabbitmqclient.Client, obj topology.TopologyResource) error { 41 | logger := ctrl.LoggerFrom(ctx) 42 | exchange := obj.(*topology.Exchange) 43 | err := validateResponseForDeletion(client.DeleteExchange(exchange.Spec.Vhost, exchange.Spec.Name)) 44 | if errors.Is(err, NotFound) { 45 | logger.Info("cannot find exchange in rabbitmq server; already deleted", "exchange", exchange.Spec.Name) 46 | } else if err != nil { 47 | return err 48 | } 49 | return nil 50 | } 51 | -------------------------------------------------------------------------------- /internal/binding.go: -------------------------------------------------------------------------------- 1 | /* 2 | RabbitMQ Messaging Topology Kubernetes Operator 3 | Copyright 2021 VMware, Inc. 4 | 5 | This product is licensed to you under the Mozilla Public License 2.0 license (the "License"). You may not use this product except in compliance with the Mozilla 2.0 License. 6 | 7 | This product may include a number of subcomponents with separate copyright notices and license terms. Your use of these subcomponents is subject to the terms and conditions of the subcomponent's license, as noted in the LICENSE file. 8 | */ 9 | 10 | package internal 11 | 12 | import ( 13 | "encoding/json" 14 | "fmt" 15 | rabbithole "github.com/michaelklishin/rabbit-hole/v3" 16 | topology "github.com/rabbitmq/messaging-topology-operator/api/v1beta1" 17 | "strings" 18 | ) 19 | 20 | func GenerateBindingInfo(binding *topology.Binding) (*rabbithole.BindingInfo, error) { 21 | arguments := make(map[string]interface{}) 22 | if binding.Spec.Arguments != nil { 23 | if err := json.Unmarshal(binding.Spec.Arguments.Raw, &arguments); err != nil { 24 | return nil, fmt.Errorf("failed to unmarshall binding arguments: %v", err) 25 | } 26 | } 27 | 28 | return &rabbithole.BindingInfo{ 29 | Vhost: binding.Spec.Vhost, 30 | Source: binding.Spec.Source, 31 | Destination: binding.Spec.Destination, 32 | DestinationType: binding.Spec.DestinationType, 33 | RoutingKey: binding.Spec.RoutingKey, 34 | Arguments: arguments, 35 | }, nil 36 | } 37 | 38 | // Generate binding properties key which is necessary when deleting a binding 39 | // Binding properties key is: 40 | // when routing key and argument are not provided, properties key is "~" 41 | // when routing key is set and no argument is provided, properties key is the routing key itself 42 | // if routing key has character '~', it's replaced by '%7E' 43 | // when arguments are provided, properties key is the routing key (could be empty) plus the hash of arguments 44 | // the hash function used is 'erlang:phash2' and it's erlang specific; GeneratePropertiesKey returns empty 45 | // string if arguments are provided (deletion not supported) 46 | 47 | func GeneratePropertiesKey(binding *topology.Binding) string { 48 | if binding.Spec.RoutingKey == "" { 49 | return "~" 50 | } 51 | if binding.Spec.Arguments == nil { 52 | return strings.ReplaceAll(binding.Spec.RoutingKey, "~", "%7E") 53 | } 54 | 55 | return "" 56 | } 57 | -------------------------------------------------------------------------------- /controllers/policy_controller.go: -------------------------------------------------------------------------------- 1 | /* 2 | RabbitMQ Messaging Topology Kubernetes Operator 3 | Copyright 2021 VMware, Inc. 4 | 5 | This product is licensed to you under the Mozilla Public License 2.0 license (the "License"). You may not use this product except in compliance with the Mozilla 2.0 License. 6 | 7 | This product may include a number of subcomponents with separate copyright notices and license terms. Your use of these subcomponents is subject to the terms and conditions of the subcomponent's license, as noted in the LICENSE file. 8 | */ 9 | 10 | package controllers 11 | 12 | import ( 13 | "context" 14 | "errors" 15 | "fmt" 16 | 17 | topology "github.com/rabbitmq/messaging-topology-operator/api/v1beta1" 18 | "github.com/rabbitmq/messaging-topology-operator/internal" 19 | "github.com/rabbitmq/messaging-topology-operator/rabbitmqclient" 20 | ctrl "sigs.k8s.io/controller-runtime" 21 | ) 22 | 23 | // +kubebuilder:rbac:groups=rabbitmq.com,resources=policies,verbs=get;list;watch;create;update;patch;delete 24 | // +kubebuilder:rbac:groups=rabbitmq.com,resources=policies/finalizers,verbs=update 25 | // +kubebuilder:rbac:groups=rabbitmq.com,resources=policies/status,verbs=get;update;patch 26 | 27 | type PolicyReconciler struct{} 28 | 29 | // DeclareFunc creates or updates a given policy using rabbithole client.PutPolicy 30 | func (r *PolicyReconciler) DeclareFunc(_ context.Context, client rabbitmqclient.Client, obj topology.TopologyResource) error { 31 | policy := obj.(*topology.Policy) 32 | generatePolicy, err := internal.GeneratePolicy(policy) 33 | if err != nil { 34 | return fmt.Errorf("failed to generate Policy: %w", err) 35 | } 36 | return validateResponse(client.PutPolicy(policy.Spec.Vhost, policy.Spec.Name, *generatePolicy)) 37 | } 38 | 39 | // DeleteFunc deletes policy from rabbitmq server 40 | // if server responds with '404' Not Found, it logs and does not requeue on error 41 | func (r *PolicyReconciler) DeleteFunc(ctx context.Context, client rabbitmqclient.Client, obj topology.TopologyResource) error { 42 | logger := ctrl.LoggerFrom(ctx) 43 | policy := obj.(*topology.Policy) 44 | err := validateResponseForDeletion(client.DeletePolicy(policy.Spec.Vhost, policy.Spec.Name)) 45 | if errors.Is(err, NotFound) { 46 | logger.Info("cannot find policy in rabbitmq server; already deleted", "policy", policy.Spec.Name) 47 | } else if err != nil { 48 | return err 49 | } 50 | return nil 51 | } 52 | -------------------------------------------------------------------------------- /internal/managedresource/superstream_binding.go: -------------------------------------------------------------------------------- 1 | package managedresource 2 | 3 | import ( 4 | "fmt" 5 | topology "github.com/rabbitmq/messaging-topology-operator/api/v1beta1" 6 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 7 | "k8s.io/apimachinery/pkg/runtime" 8 | "sigs.k8s.io/controller-runtime/pkg/client" 9 | "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" 10 | ) 11 | 12 | type SuperStreamBindingBuilder struct { 13 | *Builder 14 | partitionIndex int 15 | routingKey string 16 | vhost string 17 | rabbitmqCluster *topology.RabbitmqClusterReference 18 | } 19 | 20 | func (builder *Builder) SuperStreamBinding(partitionIndex int, routingKey, vhost string, rabbitmqCluster *topology.RabbitmqClusterReference) *SuperStreamBindingBuilder { 21 | return &SuperStreamBindingBuilder{builder, partitionIndex, routingKey, vhost, rabbitmqCluster} 22 | } 23 | 24 | func (builder *SuperStreamBindingBuilder) partitionSuffix() string { 25 | return fmt.Sprintf("-binding-%d", builder.partitionIndex) 26 | } 27 | 28 | func (builder *SuperStreamBindingBuilder) Build() (client.Object, error) { 29 | return &topology.Binding{ 30 | ObjectMeta: metav1.ObjectMeta{ 31 | Name: builder.GenerateChildResourceName(builder.partitionSuffix()), 32 | Namespace: builder.ObjectOwner.GetNamespace(), 33 | Labels: map[string]string{ 34 | AnnotationSuperStream: builder.ObjectOwner.GetName(), 35 | AnnotationSuperStreamRoutingKey: builder.routingKey, 36 | }, 37 | }, 38 | }, nil 39 | } 40 | 41 | func (builder *SuperStreamBindingBuilder) Update(object client.Object) error { 42 | binding := object.(*topology.Binding) 43 | 44 | binding.Spec.Source = builder.ObjectOwner.GetName() 45 | binding.Spec.DestinationType = "queue" 46 | binding.Spec.Destination = fmt.Sprintf("%s-%s", builder.ObjectOwner.GetName(), builder.routingKey) 47 | binding.Spec.RoutingKey = builder.routingKey 48 | binding.Spec.Vhost = builder.vhost 49 | binding.Spec.RabbitmqClusterReference = *builder.rabbitmqCluster 50 | 51 | argumentString := fmt.Sprintf(`{"x-stream-partition-order": %d}`, builder.partitionIndex) 52 | binding.Spec.Arguments = &runtime.RawExtension{Raw: []byte(argumentString)} 53 | 54 | if err := controllerutil.SetControllerReference(builder.ObjectOwner, object, builder.Scheme); err != nil { 55 | return fmt.Errorf("failed setting controller reference: %w", err) 56 | } 57 | 58 | return nil 59 | } 60 | 61 | func (builder *SuperStreamBindingBuilder) ResourceType() string { return "Binding" } 62 | -------------------------------------------------------------------------------- /internal/managedresource/superstream_partition.go: -------------------------------------------------------------------------------- 1 | package managedresource 2 | 3 | import ( 4 | "fmt" 5 | topology "github.com/rabbitmq/messaging-topology-operator/api/v1beta1" 6 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 7 | "sigs.k8s.io/controller-runtime/pkg/client" 8 | "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" 9 | "strings" 10 | ) 11 | 12 | type SuperStreamPartitionBuilder struct { 13 | *Builder 14 | vhost string 15 | routingKey string 16 | partitionIndex int 17 | rabbitmqCluster *topology.RabbitmqClusterReference 18 | } 19 | 20 | func (builder *Builder) SuperStreamPartition(partitionIndex int, routingKey, vhost string, rabbitmqCluster *topology.RabbitmqClusterReference) *SuperStreamPartitionBuilder { 21 | return &SuperStreamPartitionBuilder{builder, vhost, routingKey, partitionIndex, rabbitmqCluster} 22 | } 23 | 24 | func partitionSuffix(partitionIndex int) string { 25 | return fmt.Sprintf("-partition-%d", partitionIndex) 26 | } 27 | 28 | func (builder *SuperStreamPartitionBuilder) Build() (client.Object, error) { 29 | return &topology.Queue{ 30 | ObjectMeta: metav1.ObjectMeta{ 31 | Name: builder.GenerateChildResourceName(partitionSuffix(builder.partitionIndex)), 32 | Namespace: builder.ObjectOwner.GetNamespace(), 33 | Labels: map[string]string{ 34 | AnnotationSuperStream: builder.ObjectOwner.GetName(), 35 | AnnotationSuperStreamRoutingKey: builder.routingKey, 36 | }, 37 | }, 38 | }, nil 39 | } 40 | 41 | func (builder *SuperStreamPartitionBuilder) Update(object client.Object) error { 42 | partition := object.(*topology.Queue) 43 | partition.Spec.Name = RoutingKeyToPartitionName(builder.ObjectOwner.GetName(), builder.routingKey) 44 | partition.Spec.Durable = true 45 | partition.Spec.Type = "stream" 46 | partition.Spec.Vhost = builder.vhost 47 | partition.Spec.RabbitmqClusterReference = *builder.rabbitmqCluster 48 | 49 | if err := controllerutil.SetControllerReference(builder.ObjectOwner, object, builder.Scheme); err != nil { 50 | return fmt.Errorf("failed setting controller reference: %w", err) 51 | } 52 | 53 | return nil 54 | } 55 | 56 | func (builder *SuperStreamPartitionBuilder) ResourceType() string { return "Partition" } 57 | 58 | func RoutingKeyToPartitionName(parentObjectName, routingKey string) string { 59 | return fmt.Sprintf("%s-%s", parentObjectName, routingKey) 60 | } 61 | 62 | func PartitionNameToRoutingKey(parentObjectName, partitionName string) string { 63 | return strings.TrimPrefix(partitionName, fmt.Sprintf("%s-", parentObjectName)) 64 | } 65 | -------------------------------------------------------------------------------- /controllers/operatorpolicy_controller.go: -------------------------------------------------------------------------------- 1 | /* 2 | RabbitMQ Messaging Topology Kubernetes Operator 3 | Copyright 2021 VMware, Inc. 4 | 5 | This product is licensed to you under the Mozilla Public License 2.0 license (the "License"). You may not use this product except in compliance with the Mozilla 2.0 License. 6 | 7 | This product may include a number of subcomponents with separate copyright notices and license terms. Your use of these subcomponents is subject to the terms and conditions of the subcomponent's license, as noted in the LICENSE file. 8 | */ 9 | 10 | package controllers 11 | 12 | import ( 13 | "context" 14 | "errors" 15 | "fmt" 16 | 17 | topology "github.com/rabbitmq/messaging-topology-operator/api/v1beta1" 18 | "github.com/rabbitmq/messaging-topology-operator/internal" 19 | "github.com/rabbitmq/messaging-topology-operator/rabbitmqclient" 20 | ctrl "sigs.k8s.io/controller-runtime" 21 | ) 22 | 23 | // +kubebuilder:rbac:groups=rabbitmq.com,resources=operatorpolicies,verbs=get;list;watch;create;update;patch;delete 24 | // +kubebuilder:rbac:groups=rabbitmq.com,resources=operatorpolicies/finalizers,verbs=update 25 | // +kubebuilder:rbac:groups=rabbitmq.com,resources=operatorpolicies/status,verbs=get;update;patch 26 | 27 | type OperatorPolicyReconciler struct{} 28 | 29 | // DeclareFunc creates or updates a given operator policy using rabbithole client.PutOperatorPolicy 30 | func (r *OperatorPolicyReconciler) DeclareFunc(_ context.Context, client rabbitmqclient.Client, obj topology.TopologyResource) error { 31 | policy := obj.(*topology.OperatorPolicy) 32 | generateOperatorPolicy, err := internal.GenerateOperatorPolicy(policy) 33 | if err != nil { 34 | return fmt.Errorf("failed to generate OperatorPolicy: %w", err) 35 | } 36 | return validateResponse(client.PutOperatorPolicy(policy.Spec.Vhost, policy.Spec.Name, *generateOperatorPolicy)) 37 | } 38 | 39 | // DeleteFunc deletes operator policy from rabbitmq server 40 | // if server responds with '404' Not Found, it logs and does not requeue on error 41 | func (r *OperatorPolicyReconciler) DeleteFunc(ctx context.Context, client rabbitmqclient.Client, obj topology.TopologyResource) error { 42 | logger := ctrl.LoggerFrom(ctx) 43 | policy := obj.(*topology.OperatorPolicy) 44 | err := validateResponseForDeletion(client.DeleteOperatorPolicy(policy.Spec.Vhost, policy.Spec.Name)) 45 | if errors.Is(err, NotFound) { 46 | logger.Info("cannot find operator policy in rabbitmq server; already deleted", "operatorpolicy", policy.Spec.Name) 47 | } else if err != nil { 48 | return err 49 | } 50 | return nil 51 | } 52 | -------------------------------------------------------------------------------- /internal/queue_settings_test.go: -------------------------------------------------------------------------------- 1 | package internal_test 2 | 3 | import ( 4 | . "github.com/onsi/ginkgo/v2" 5 | . "github.com/onsi/gomega" 6 | topology "github.com/rabbitmq/messaging-topology-operator/api/v1beta1" 7 | "github.com/rabbitmq/messaging-topology-operator/internal" 8 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 9 | "k8s.io/apimachinery/pkg/runtime" 10 | ) 11 | 12 | var _ = Describe("GenerateQueueSettings", func() { 13 | var q *topology.Queue 14 | 15 | BeforeEach(func() { 16 | q = &topology.Queue{ 17 | ObjectMeta: metav1.ObjectMeta{ 18 | Name: "a-queue", 19 | }, 20 | Spec: topology.QueueSpec{ 21 | Type: "quorum", 22 | AutoDelete: false, 23 | Durable: true, 24 | }, 25 | } 26 | }) 27 | 28 | It("sets QueueSettings.Type according to queue.spec", func() { 29 | settings, err := internal.GenerateQueueSettings(q) 30 | Expect(err).NotTo(HaveOccurred()) 31 | Expect(settings.Type).To(Equal("quorum")) 32 | }) 33 | 34 | It("sets QueueSettings.AutoDelete according to queue.spec", func() { 35 | settings, err := internal.GenerateQueueSettings(q) 36 | Expect(err).NotTo(HaveOccurred()) 37 | Expect(settings.AutoDelete).To(BeFalse()) 38 | }) 39 | 40 | It("sets QueueSettings.Durable according to queue.spec", func() { 41 | settings, err := internal.GenerateQueueSettings(q) 42 | Expect(err).NotTo(HaveOccurred()) 43 | Expect(settings.Durable).To(BeTrue()) 44 | }) 45 | 46 | When("queue arguments are provided", func() { 47 | It("generates the correct queue arguments", func() { 48 | q.Spec.Arguments = &runtime.RawExtension{ 49 | Raw: []byte(`{"x-delivery-limit": 10000, 50 | "x-max-in-memory-length": 500, 51 | "x-max-in-memory-bytes": 5000, 52 | "x-max-length": 300, 53 | "x-max-length-bytes": 60000, 54 | "x-dead-letter-exchange": "test", 55 | "x-single-active-consumer": true 56 | }`)} 57 | settings, err := internal.GenerateQueueSettings(q) 58 | Expect(err).NotTo(HaveOccurred()) 59 | Expect(settings.Arguments).Should(SatisfyAll( 60 | HaveLen(7), 61 | // GenerateQueueSettings Unmarshal queue.Spec.Arguments 62 | // Unmarshall stores float64 for JSON numbers 63 | HaveKeyWithValue("x-delivery-limit", float64(10000)), 64 | HaveKeyWithValue("x-max-in-memory-length", float64(500)), 65 | HaveKeyWithValue("x-max-in-memory-bytes", float64(5000)), 66 | HaveKeyWithValue("x-max-length", float64(300)), 67 | HaveKeyWithValue("x-max-length-bytes", float64(60000)), 68 | HaveKeyWithValue("x-dead-letter-exchange", "test"), 69 | HaveKeyWithValue("x-single-active-consumer", true), 70 | )) 71 | }) 72 | }) 73 | }) 74 | -------------------------------------------------------------------------------- /api/v1alpha1/superstream_types_test.go: -------------------------------------------------------------------------------- 1 | package v1alpha1 2 | 3 | import ( 4 | "context" 5 | . "github.com/onsi/ginkgo/v2" 6 | . "github.com/onsi/gomega" 7 | topologyv1beta1 "github.com/rabbitmq/messaging-topology-operator/api/v1beta1" 8 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 9 | "k8s.io/apimachinery/pkg/types" 10 | ) 11 | 12 | var _ = Describe("SuperStream spec", func() { 13 | var ( 14 | namespace = "default" 15 | ctx = context.Background() 16 | ) 17 | 18 | It("creates a superstream with default settings", func() { 19 | expectedSpec := SuperStreamSpec{ 20 | Name: "test-super-stream", 21 | Vhost: "/", 22 | Partitions: 3, 23 | RabbitmqClusterReference: topologyv1beta1.RabbitmqClusterReference{ 24 | Name: "some-cluster", 25 | }, 26 | } 27 | 28 | superStream := SuperStream{ 29 | ObjectMeta: metav1.ObjectMeta{ 30 | Name: "test-super-stream", 31 | Namespace: namespace, 32 | }, 33 | Spec: SuperStreamSpec{ 34 | Name: "test-super-stream", 35 | RabbitmqClusterReference: topologyv1beta1.RabbitmqClusterReference{ 36 | Name: "some-cluster", 37 | }, 38 | }, 39 | } 40 | Expect(k8sClient.Create(ctx, &superStream)).To(Succeed()) 41 | fetchedSuperStream := &SuperStream{} 42 | Expect(k8sClient.Get(ctx, types.NamespacedName{ 43 | Name: superStream.Name, 44 | Namespace: superStream.Namespace, 45 | }, fetchedSuperStream)).To(Succeed()) 46 | Expect(fetchedSuperStream.Spec).To(Equal(expectedSpec)) 47 | }) 48 | 49 | It("creates a superstream with specified settings", func() { 50 | expectedSpec := SuperStreamSpec{ 51 | Name: "test-super-stream2", 52 | Vhost: "test-vhost", 53 | Partitions: 5, 54 | RabbitmqClusterReference: topologyv1beta1.RabbitmqClusterReference{ 55 | Name: "some-cluster", 56 | }, 57 | } 58 | 59 | superStream := SuperStream{ 60 | ObjectMeta: metav1.ObjectMeta{ 61 | Name: "test-super-stream2", 62 | Namespace: namespace, 63 | }, 64 | Spec: SuperStreamSpec{ 65 | Name: "test-super-stream2", 66 | Vhost: "test-vhost", 67 | Partitions: 5, 68 | RabbitmqClusterReference: topologyv1beta1.RabbitmqClusterReference{ 69 | Name: "some-cluster", 70 | }, 71 | }, 72 | } 73 | Expect(k8sClient.Create(ctx, &superStream)).To(Succeed()) 74 | fetchedSuperStream := &SuperStream{} 75 | Expect(k8sClient.Get(ctx, types.NamespacedName{ 76 | Name: superStream.Name, 77 | Namespace: superStream.Namespace, 78 | }, fetchedSuperStream)).To(Succeed()) 79 | Expect(fetchedSuperStream.Spec).To(Equal(expectedSpec)) 80 | }) 81 | 82 | }) 83 | -------------------------------------------------------------------------------- /api/v1beta1/vhost_webhook.go: -------------------------------------------------------------------------------- 1 | package v1beta1 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | apierrors "k8s.io/apimachinery/pkg/api/errors" 7 | "k8s.io/apimachinery/pkg/runtime" 8 | "k8s.io/apimachinery/pkg/util/validation/field" 9 | ctrl "sigs.k8s.io/controller-runtime" 10 | "sigs.k8s.io/controller-runtime/pkg/webhook" 11 | "sigs.k8s.io/controller-runtime/pkg/webhook/admission" 12 | ) 13 | 14 | func (v *Vhost) SetupWebhookWithManager(mgr ctrl.Manager) error { 15 | return ctrl.NewWebhookManagedBy(mgr). 16 | WithValidator(v). 17 | For(v). 18 | Complete() 19 | } 20 | 21 | // +kubebuilder:webhook:verbs=create;update,path=/validate-rabbitmq-com-v1beta1-vhost,mutating=false,failurePolicy=fail,groups=rabbitmq.com,resources=vhosts,versions=v1beta1,name=vvhost.kb.io,sideEffects=none,admissionReviewVersions=v1 22 | 23 | var _ webhook.CustomValidator = &Vhost{} 24 | 25 | // ValidateCreate 26 | // 27 | // Either rabbitmqClusterReference.name or 28 | // rabbitmqClusterReference.connectionSecret must be provided but not both 29 | func (v *Vhost) ValidateCreate(_ context.Context, obj runtime.Object) (warnings admission.Warnings, err error) { 30 | vhost, ok := obj.(*Vhost) 31 | if !ok { 32 | return nil, fmt.Errorf("expected a RabbitMQ vhost but got a %T", obj) 33 | } 34 | return nil, v.Spec.RabbitmqClusterReference.validate(vhost.RabbitReference()) 35 | } 36 | 37 | // ValidateUpdate returns error type 'forbidden' for updates on vhost name and rabbitmqClusterReference 38 | func (v *Vhost) ValidateUpdate(_ context.Context, oldObj, newObj runtime.Object) (warnings admission.Warnings, err error) { 39 | oldVhost, ok := oldObj.(*Vhost) 40 | if !ok { 41 | return nil, apierrors.NewBadRequest(fmt.Sprintf("expected a vhost but got a %T", oldObj)) 42 | } 43 | 44 | newVhost, ok := newObj.(*Vhost) 45 | if !ok { 46 | return nil, apierrors.NewBadRequest(fmt.Sprintf("expected a vhost but got a %T", newObj)) 47 | } 48 | 49 | const detailMsg = "updates on name and rabbitmqClusterReference are all forbidden" 50 | if newVhost.Spec.Name != oldVhost.Spec.Name { 51 | return nil, apierrors.NewForbidden(newVhost.GroupResource(), newVhost.Name, 52 | field.Forbidden(field.NewPath("spec", "name"), detailMsg)) 53 | } 54 | 55 | if !oldVhost.Spec.RabbitmqClusterReference.Matches(&newVhost.Spec.RabbitmqClusterReference) { 56 | return nil, apierrors.NewForbidden(newVhost.GroupResource(), newVhost.Name, 57 | field.Forbidden(field.NewPath("spec", "rabbitmqClusterReference"), detailMsg)) 58 | } 59 | 60 | return nil, nil 61 | } 62 | 63 | func (v *Vhost) ValidateDelete(_ context.Context, obj runtime.Object) (warnings admission.Warnings, err error) { 64 | return nil, nil 65 | } 66 | -------------------------------------------------------------------------------- /api/v1beta1/rabbitmq_cluster_reference.go: -------------------------------------------------------------------------------- 1 | package v1beta1 2 | 3 | import ( 4 | "errors" 5 | corev1 "k8s.io/api/core/v1" 6 | "k8s.io/apimachinery/pkg/runtime/schema" 7 | "sigs.k8s.io/controller-runtime/pkg/webhook/admission" 8 | ) 9 | 10 | type RabbitmqClusterReference struct { 11 | // The name of the RabbitMQ cluster to reference. 12 | // Have to set either name or connectionSecret, but not both. 13 | // +kubebuilder:validation:Optional 14 | Name string `json:"name,omitempty"` 15 | // The namespace of the RabbitMQ cluster to reference. 16 | // Defaults to the namespace of the requested resource if omitted. 17 | // +kubebuilder:validation:Optional 18 | Namespace string `json:"namespace,omitempty"` 19 | // Secret contains the http management uri for the RabbitMQ cluster. 20 | // The Secret must contain the key `uri`, `username` and `password` or operator will error. 21 | // Have to set either name or connectionSecret, but not both. 22 | // +kubebuilder:validation:Optional 23 | ConnectionSecret *corev1.LocalObjectReference `json:"connectionSecret,omitempty"` 24 | } 25 | 26 | func (r *RabbitmqClusterReference) Matches(new *RabbitmqClusterReference) bool { 27 | if new.Name != r.Name || new.Namespace != r.Namespace { 28 | return false 29 | } 30 | 31 | // when connectionSecret has been updated 32 | if new.ConnectionSecret != nil && r.ConnectionSecret != nil && *new.ConnectionSecret != *r.ConnectionSecret { 33 | return false 34 | } 35 | 36 | // when connectionSecret is removed 37 | if new.ConnectionSecret == nil && r.ConnectionSecret != nil { 38 | return false 39 | } 40 | 41 | // when connectionSecret is added 42 | if new.ConnectionSecret != nil && r.ConnectionSecret == nil { 43 | return false 44 | } 45 | 46 | return true 47 | } 48 | 49 | // ValidateOnCreate validates RabbitmqClusterReference on resources create 50 | // either rabbitmqClusterReference.name or rabbitmqClusterReference.connectionSecret must be provided but not both; else it errors 51 | func (r *RabbitmqClusterReference) ValidateOnCreate(_ schema.GroupResource, _ string) (admission.Warnings, error) { 52 | // TODO: make this function private when we deprecate or promote v1alpha1 SuperStreamController 53 | return nil, r.validate(*r) 54 | } 55 | 56 | func (r *RabbitmqClusterReference) validate(ref RabbitmqClusterReference) error { 57 | if ref.Name != "" && ref.ConnectionSecret != nil { 58 | return errors.New("invalid RabbitmqClusterReference: do not provide both name and connectionSecret") 59 | } 60 | 61 | if ref.Name == "" && ref.ConnectionSecret == nil { 62 | return errors.New("invalid RabbitmqClusterReference: must provide either name or connectionSecret") 63 | } 64 | return nil 65 | } 66 | -------------------------------------------------------------------------------- /internal/schema_replication_test.go: -------------------------------------------------------------------------------- 1 | package internal_test 2 | 3 | import ( 4 | . "github.com/onsi/ginkgo/v2" 5 | . "github.com/onsi/gomega" 6 | "github.com/rabbitmq/messaging-topology-operator/internal" 7 | corev1 "k8s.io/api/core/v1" 8 | ) 9 | 10 | var _ = Describe("GenerateSchemaReplicationParameters", func() { 11 | var secret corev1.Secret 12 | 13 | BeforeEach(func() { 14 | secret = corev1.Secret{ 15 | Type: corev1.SecretTypeOpaque, 16 | Data: map[string][]byte{ 17 | "username": []byte("a-random-user"), 18 | "password": []byte("a-random-password"), 19 | }, 20 | } 21 | }) 22 | When("endpoints are provided as function parameter", func() { 23 | It("generates expected replication parameters", func() { 24 | parameters, err := internal.GenerateSchemaReplicationParameters(&secret, "a.endpoints.local:5672,b.endpoints.local:5672,c.endpoints.local:5672") 25 | Expect(err).NotTo(HaveOccurred()) 26 | Expect(parameters.Username).To(Equal("a-random-user")) 27 | Expect(parameters.Password).To(Equal("a-random-password")) 28 | Expect(parameters.Endpoints).To(Equal([]string{ 29 | "a.endpoints.local:5672", 30 | "b.endpoints.local:5672", 31 | "c.endpoints.local:5672", 32 | })) 33 | }) 34 | }) 35 | 36 | When("endpoints are provided in the secret object and as function parameter", func() { 37 | It("endpoints parameter takes precedence over endpoints from secret object", func() { 38 | secret.Data["endpoints"] = []byte("some-url-value:1234") 39 | parameters, err := internal.GenerateSchemaReplicationParameters(&secret, "a.endpoints.local:5672,b.endpoints.local:5672,c.endpoints.local:5672") 40 | Expect(err).NotTo(HaveOccurred()) 41 | Expect(parameters.Username).To(Equal("a-random-user")) 42 | Expect(parameters.Password).To(Equal("a-random-password")) 43 | Expect(parameters.Endpoints).To(Equal([]string{ 44 | "a.endpoints.local:5672", 45 | "b.endpoints.local:5672", 46 | "c.endpoints.local:5672", 47 | })) 48 | }) 49 | }) 50 | 51 | When("endpoints are provided only in the secret object", func() { 52 | It("generates expected replication parameters", func() { 53 | secret.Data["endpoints"] = []byte("a.endpoints.local:5672,b.endpoints.local:5672,c.endpoints.local:5672") 54 | parameters, err := internal.GenerateSchemaReplicationParameters(&secret, "") 55 | Expect(err).NotTo(HaveOccurred()) 56 | Expect(parameters.Username).To(Equal("a-random-user")) 57 | Expect(parameters.Password).To(Equal("a-random-password")) 58 | Expect(parameters.Endpoints).To(Equal([]string{ 59 | "a.endpoints.local:5672", 60 | "b.endpoints.local:5672", 61 | "c.endpoints.local:5672", 62 | })) 63 | }) 64 | }) 65 | }) 66 | -------------------------------------------------------------------------------- /.github/workflows/codeql-analysis.yml: -------------------------------------------------------------------------------- 1 | 2 | name: "CodeQL" 3 | 4 | on: 5 | push: 6 | branches: [ main ] 7 | pull_request: 8 | # The branches below must be a subset of the branches above 9 | branches: [ main ] 10 | schedule: 11 | - cron: '22 2 * * 0' 12 | 13 | jobs: 14 | analyze: 15 | name: Analyze 16 | # Dependabot only has read permissions on push i.e. after merging a PR, and that causes failures because it needs 17 | # 'write' permissions to upload the scan report to GitHub. This is a workaround to stop runs after merging a 18 | # Dependabot PR, and always run when it comes from a pull request. 19 | if: ${{ github.actor != 'dependabot[bot]' || github.event_name == 'pull_request' }} 20 | runs-on: ubuntu-latest 21 | 22 | strategy: 23 | fail-fast: false 24 | matrix: 25 | # https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed 26 | language: [ 'go' ] 27 | 28 | steps: 29 | - name: Checkout repository 30 | uses: actions/checkout@v6 31 | 32 | # Manually install the right version of Go 33 | # See https://github.com/github/codeql-action/issues/1842 and https://github.com/github/codeql/issues/13992 34 | - name: Install Go 35 | uses: actions/setup-go@v6 36 | with: 37 | go-version-file: go.mod 38 | 39 | # Initializes the CodeQL tools for scanning. 40 | - name: Initialize CodeQL 41 | uses: github/codeql-action/init@v4 42 | with: 43 | languages: ${{ matrix.language }} 44 | # If you wish to specify custom queries, you can do so here or in a config file. 45 | # By default, queries listed here will override any specified in a config file. 46 | # Prefix the list here with "+" to use these queries and those in the config file. 47 | # queries: ./path/to/local/query, your-org/your-repo/queries@main 48 | 49 | # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). 50 | # If this step fails, then you should remove it and run the build manually (see below) 51 | - name: Autobuild 52 | uses: github/codeql-action/autobuild@v4 53 | 54 | # ℹ️ Command-line programs to run using the OS shell. 55 | # 📚 https://git.io/JvXDl 56 | 57 | # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines 58 | # and modify them (or add more) to build your code if your project 59 | # uses a compiled language 60 | 61 | #- run: | 62 | # make bootstrap 63 | # make release 64 | 65 | - name: Perform CodeQL Analysis 66 | uses: github/codeql-action/analyze@v4 67 | -------------------------------------------------------------------------------- /system_tests/rabbitmq_connection_test.go: -------------------------------------------------------------------------------- 1 | package system_tests 2 | 3 | import ( 4 | "context" 5 | 6 | rabbithole "github.com/michaelklishin/rabbit-hole/v3" 7 | . "github.com/onsi/ginkgo/v2" 8 | . "github.com/onsi/gomega" 9 | topology "github.com/rabbitmq/messaging-topology-operator/api/v1beta1" 10 | corev1 "k8s.io/api/core/v1" 11 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 12 | "sigs.k8s.io/controller-runtime/pkg/client" 13 | ) 14 | 15 | var _ = Describe("RabbitMQ connection using provided connection secret", func() { 16 | var ( 17 | namespace = MustHaveEnv("NAMESPACE") 18 | ctx = context.Background() 19 | q *topology.Queue 20 | secret *corev1.Secret 21 | ) 22 | 23 | BeforeEach(func() { 24 | endpoint, err := managementURI(ctx, clientSet, rmq.Namespace, rmq.Name) 25 | Expect(err).NotTo(HaveOccurred(), "failed to get management uri") 26 | user, pass, err := getUsernameAndPassword(ctx, clientSet, rmq.Namespace, rmq.Name) 27 | Expect(err).NotTo(HaveOccurred(), "failed to get user and pass") 28 | 29 | secret = &corev1.Secret{ 30 | ObjectMeta: metav1.ObjectMeta{ 31 | Name: "uri-secret", 32 | Namespace: namespace, 33 | }, 34 | StringData: map[string]string{ 35 | "username": user, 36 | "password": pass, 37 | "uri": endpoint, 38 | }, 39 | } 40 | Expect(k8sClient.Create(ctx, secret, &client.CreateOptions{})).To(Succeed()) 41 | }) 42 | 43 | AfterEach(func() { 44 | Expect(k8sClient.Delete(ctx, secret)).To(Succeed()) 45 | }) 46 | 47 | It("succeeds creating an object in a RabbitMQ cluster configured with connection URI", func() { 48 | By("declaring queue") 49 | q = &topology.Queue{ 50 | ObjectMeta: metav1.ObjectMeta{ 51 | Name: "connection-test", 52 | Namespace: namespace, 53 | }, 54 | Spec: topology.QueueSpec{ 55 | Name: "connection-test", 56 | RabbitmqClusterReference: topology.RabbitmqClusterReference{ 57 | ConnectionSecret: &corev1.LocalObjectReference{Name: secret.Name}, 58 | }, 59 | }, 60 | } 61 | Expect(k8sClient.Create(ctx, q, &client.CreateOptions{})).To(Succeed()) 62 | var qInfo *rabbithole.DetailedQueueInfo 63 | Eventually(func() error { 64 | var err error 65 | qInfo, err = rabbitClient.GetQueue("/", q.Name) 66 | return err 67 | }, 20, 2).Should(BeNil()) 68 | 69 | Expect(qInfo.Name).To(Equal(q.Name)) 70 | Expect(qInfo.Vhost).To(Equal("/")) 71 | 72 | By("deleting queue") 73 | Expect(k8sClient.Delete(ctx, q)).To(Succeed()) 74 | var err error 75 | Eventually(func() error { 76 | _, err = rabbitClient.GetQueue(q.Spec.Vhost, q.Name) 77 | return err 78 | }, 30).Should(HaveOccurred()) 79 | Expect(err.Error()).To(ContainSubstring("Object Not Found")) 80 | }) 81 | }) 82 | --------------------------------------------------------------------------------