diff --git a/api/v1beta1/rabbitmqcluster_types.go b/api/v1beta1/rabbitmqcluster_types.go index bac7df940..8bcf63ccb 100644 --- a/api/v1beta1/rabbitmqcluster_types.go +++ b/api/v1beta1/rabbitmqcluster_types.go @@ -246,6 +246,9 @@ type RabbitmqClusterConfigurationSpec struct { // Modify to add to the rabbitmq.conf file in addition to default configurations set by the operator. Modify this property on an existing RabbitmqCluster will trigger a StatefulSet rolling restart and will cause rabbitmq downtime. // +kubebuilder:validation:MaxLength:=2000 AdditionalConfig string `json:"additionalConfig,omitempty"` + // Specify any rabbitmq advanced.config configurations + // +kubebuilder:validation:MaxLength:=100000 + AdvancedConfig string `json:"advancedConfig,omitempty"` } // The settings for the persistent storage desired for each Pod in the RabbitmqCluster. diff --git a/config/crd/bases/rabbitmq.com_rabbitmqclusters.yaml b/config/crd/bases/rabbitmq.com_rabbitmqclusters.yaml index 4304b03a5..069a7533c 100644 --- a/config/crd/bases/rabbitmq.com_rabbitmqclusters.yaml +++ b/config/crd/bases/rabbitmq.com_rabbitmqclusters.yaml @@ -38,14 +38,10 @@ spec: description: RabbitmqCluster is the Schema for the rabbitmqclusters API properties: apiVersion: - description: 'APIVersion defines the versioned schema of this representation - of an object. Servers should convert recognized schemas to the latest - internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' type: string kind: - description: 'Kind is a string value representing the REST resource this - object represents. Servers may infer this from the endpoint the client - submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' type: string metadata: type: object @@ -635,7 +631,7 @@ spec: clientService: properties: metadata: - description: It is used in ClientService, and StatefulSet + description: It is used in ClientService and StatefulSet properties: annotations: additionalProperties: @@ -762,6 +758,23 @@ spec: description: 'Map of string keys and values that can be used to organize and categorize (scope and select) objects. May match selectors of replication controllers and services. More info: http://kubernetes.io/docs/user-guide/labels' type: object type: object + type: object + statefulSet: + properties: + metadata: + description: It is used in ClientService and StatefulSet + properties: + annotations: + additionalProperties: + type: string + description: 'Annotations is an unstructured key value map stored with a resource that may be set by external tools to store and retrieve arbitrary metadata. They are not queryable and should be preserved when modifying objects. More info: http://kubernetes.io/docs/user-guide/annotations' + type: object + labels: + additionalProperties: + type: string + description: 'Map of string keys and values that can be used to organize and categorize (scope and select) objects. May match selectors of replication controllers and services. More info: http://kubernetes.io/docs/user-guide/labels' + type: object + type: object spec: description: Spec defines the desired identities of pods in this set. properties: @@ -809,7 +822,7 @@ spec: description: template is the object that describes the pod that will be created if insufficient replicas are detected. Each pod stamped out by the StatefulSet will fulfill this Template, but have a unique identity from the rest of the StatefulSet. properties: metadata: - description: EmbeddedObjectMeta contains a subset of the fields included in k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta Only fields which are relevant to embedded resources are included. It is used in StatefulSet, PersistentVolumeClaim, and PodTemplate + description: EmbeddedObjectMeta contains a subset of the fields included in k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta Only fields which are relevant to embedded resources are included. It is used in PersistentVolumeClaim and PodTemplate properties: annotations: additionalProperties: @@ -4432,7 +4445,7 @@ spec: description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' type: string metadata: - description: EmbeddedObjectMeta contains a subset of the fields included in k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta Only fields which are relevant to embedded resources are included. It is used in StatefulSet, PersistentVolumeClaim, and PodTemplate + description: EmbeddedObjectMeta contains a subset of the fields included in k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta Only fields which are relevant to embedded resources are included. It is used in PersistentVolumeClaim and PodTemplate properties: annotations: additionalProperties: @@ -4581,6 +4594,10 @@ spec: type: string maxItems: 100 type: array + advancedConfig: + description: Specify any rabbitmq advanced.config configurations + maxLength: 100000 + type: string type: object replicas: description: Replicas is the number of nodes in the RabbitMQ cluster. diff --git a/internal/resource/configmap.go b/internal/resource/configmap.go index 4565936fd..7b057889d 100644 --- a/internal/resource/configmap.go +++ b/internal/resource/configmap.go @@ -96,6 +96,9 @@ func (builder *ServerConfigMapBuilder) Update(object runtime.Object) error { } } + if builder.Instance.Spec.Rabbitmq.AdvancedConfig != "" { + configMap.Data["advanced.config"] = builder.Instance.Spec.Rabbitmq.AdvancedConfig + } configMap.Data["rabbitmq.conf"] = rmqConfBuilder.String() return nil } diff --git a/internal/resource/configmap_test.go b/internal/resource/configmap_test.go index bdd501faf..7f5d1502b 100644 --- a/internal/resource/configmap_test.go +++ b/internal/resource/configmap_test.go @@ -60,7 +60,7 @@ var _ = Describe("GenerateServerConfigMap", func() { "rabbitmq_management]." plugins, ok := configMap.Data["enabled_plugins"] - Expect(ok).To(BeTrue()) + Expect(ok).To(BeTrue(), "key 'enabled_plugins' should be present") Expect(plugins).To(Equal(expectedEnabledPlugins)) }) @@ -120,7 +120,7 @@ cluster_name = ` + builder.Instance.Name + "\n" Expect(configMapBuilder.Update(configMap)).To(Succeed()) rabbitmqConf, ok := configMap.Data["rabbitmq.conf"] - Expect(ok).To(BeTrue()) + Expect(ok).To(BeTrue(), "key 'rabbitmq.conf' should be present") Expect(rabbitmqConf).To(Equal(defaultRabbitmqConf)) }) @@ -143,10 +143,22 @@ my-config-property-1 = better-value` Expect(configMapBuilder.Update(configMap)).To(Succeed()) rabbitmqConf, ok := configMap.Data["rabbitmq.conf"] - Expect(ok).To(BeTrue()) + Expect(ok).To(BeTrue(), "key 'rabbitmq.conf' should be present") Expect(rabbitmqConf).To(Equal(expectedRabbitmqConf)) }) + It("sets data.advancedConfig when provided", func() { + instance.Spec.Rabbitmq.AdvancedConfig = ` +[ + {rabbit, [{auth_backends, [rabbit_auth_backend_ldap]}]} +].` + Expect(configMapBuilder.Update(configMap)).To(Succeed()) + advancedConfig, ok := configMap.Data["advanced.config"] + Expect(ok).To(BeTrue(), "key 'advanced.config' should be present") + Expect(advancedConfig).To(Equal("\n[\n {rabbit, [{auth_backends, [rabbit_auth_backend_ldap]}]}\n].")) + + }) + Context("TLS", func() { It("adds TLS config when TLS is enabled", func() { instance = rabbitmqv1beta1.RabbitmqCluster{ @@ -162,7 +174,7 @@ my-config-property-1 = better-value` Expect(configMapBuilder.Update(configMap)).To(Succeed()) rabbitmqConf, ok := configMap.Data["rabbitmq.conf"] - Expect(ok).To(BeTrue()) + Expect(ok).To(BeTrue(), "key 'rabbitmq.conf' should be present") Expect(rabbitmqConf).To(ContainSubstring(` ssl_options.certfile=/etc/rabbitmq-tls/tls.crt ssl_options.keyfile=/etc/rabbitmq-tls/tls.key @@ -187,7 +199,7 @@ listeners.ssl.default=5671 Expect(configMapBuilder.Update(configMap)).To(Succeed()) rabbitmqConf, ok := configMap.Data["rabbitmq.conf"] - Expect(ok).To(BeTrue()) + Expect(ok).To(BeTrue(), "key 'rabbitmq.conf' should be present") Expect(rabbitmqConf).To(ContainSubstring(` ssl_options.certfile=/etc/rabbitmq-tls/tls.crt ssl_options.keyfile=/etc/rabbitmq-tls/tls.key diff --git a/internal/resource/statefulset.go b/internal/resource/statefulset.go index 18b995b40..9f525869a 100644 --- a/internal/resource/statefulset.go +++ b/internal/resource/statefulset.go @@ -448,6 +448,7 @@ func (builder *StatefulSetBuilder) podTemplateSpec(annotations, labels map[strin Image: builder.Instance.Spec.Image, Command: []string{ "sh", "-c", "cp /tmp/rabbitmq/rabbitmq.conf /etc/rabbitmq/rabbitmq.conf && echo '' >> /etc/rabbitmq/rabbitmq.conf ; " + + "cp /tmp/rabbitmq/advanced.config /etc/rabbitmq/advanced.config ; " + "cp /tmp/erlang-cookie-secret/.erlang.cookie /var/lib/rabbitmq/.erlang.cookie " + "&& chown 999:999 /var/lib/rabbitmq/.erlang.cookie " + "&& chmod 600 /var/lib/rabbitmq/.erlang.cookie ; " + diff --git a/internal/resource/statefulset_test.go b/internal/resource/statefulset_test.go index fa4180437..3c37b54a1 100644 --- a/internal/resource/statefulset_test.go +++ b/internal/resource/statefulset_test.go @@ -918,6 +918,7 @@ var _ = Describe("StatefulSet", func() { container := extractContainer(initContainers, "copy-config") Expect(container.Command).To(Equal([]string{ "sh", "-c", "cp /tmp/rabbitmq/rabbitmq.conf /etc/rabbitmq/rabbitmq.conf && echo '' >> /etc/rabbitmq/rabbitmq.conf ; " + + "cp /tmp/rabbitmq/advanced.config /etc/rabbitmq/advanced.config ; " + "cp /tmp/erlang-cookie-secret/.erlang.cookie /var/lib/rabbitmq/.erlang.cookie " + "&& chown 999:999 /var/lib/rabbitmq/.erlang.cookie " + "&& chmod 600 /var/lib/rabbitmq/.erlang.cookie ; " + diff --git a/system_tests/system_tests.go b/system_tests/system_tests.go index 9788b07f3..01831f339 100644 --- a/system_tests/system_tests.go +++ b/system_tests/system_tests.go @@ -174,6 +174,25 @@ cluster_keepalive_interval = 10000` Expect(string(output)).Should(ContainSubstring("cluster_keepalive_interval = 10000")) Expect(string(output)).Should(ContainSubstring("cluster_partition_handling = ignore")) }) + + By("updating the advanced.config file when advancedConfig are modifed", func() { + Expect(updateRabbitmqCluster(rmqClusterClient, cluster.Name, cluster.Namespace, func(cluster *rabbitmqv1beta1.RabbitmqCluster) { + cluster.Spec.Rabbitmq.AdvancedConfig = `[ + {rabbit, [{auth_backends, [rabbit_auth_backend_ldap]}]} +].` + })).To(Succeed()) + + // wait for statefulSet to be restarted + waitForRabbitmqUpdate(cluster) + + output, err := kubectlExec(namespace, + statefulSetPodName(cluster, 0), + "cat", + "/etc/rabbitmq/advanced.config", + ) + Expect(err).NotTo(HaveOccurred()) + Expect(string(output)).Should(ContainSubstring("[\n {rabbit, [{auth_backends, [rabbit_auth_backend_ldap]}]}\n].")) + }) }) })