Skip to content

Commit efaa1fe

Browse files
authored
Add host and port to default-user secret. (#755)
Provides additional fields in the context of the Provisioned Service Binding spec https://k8s-service-bindings.github.io/spec/#provisioned-service - The `host` is the k8s dns name of the service. - When TLS is configured, the `port` field is the AMQPS port, otherwise the AMQP port. - When additional plugins providing messaging protocols are enabled, the relevant ports are also added as fields to the binding with appropriate names (`mqtt-port`, `stomp-port`, `stream-port`, `web-mqtt-port`, `web-stomp-port`), using the TLS ports when TLS is enabled.
1 parent 0e88776 commit efaa1fe

File tree

7 files changed

+252
-12
lines changed

7 files changed

+252
-12
lines changed

api/v1beta1/rabbitmqcluster_status.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ type RabbitmqClusterSecretReference struct {
3939
Name string `json:"name"`
4040
// Namespace of the Secret containing the default user credentials
4141
Namespace string `json:"namespace"`
42-
// Key-value pairs in the Secret corresponding to `username` and `password`
42+
// Key-value pairs in the Secret corresponding to `username`, `password`, `host`, and `port`
4343
Keys map[string]string `json:"keys"`
4444
}
4545

config/crd/bases/rabbitmq.com_rabbitmqclusters.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3904,7 +3904,7 @@ spec:
39043904
keys:
39053905
additionalProperties:
39063906
type: string
3907-
description: Key-value pairs in the Secret corresponding to `username` and `password`
3907+
description: Key-value pairs in the Secret corresponding to `username`, `password`, `host`, and `port`
39083908
type: object
39093909
name:
39103910
description: Name of the Secret containing the default user credentials

controllers/rabbitmqcluster_controller_test.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -452,6 +452,20 @@ var _ = Describe("RabbitmqClusterController", func() {
452452
}, 3).Should(HaveKeyWithValue("foo", "bar"))
453453
})
454454

455+
When("the plugin configuration is updated", func() {
456+
It("updates the secret port configuration", func() {
457+
Expect(updateWithRetry(cluster, func(r *rabbitmqv1beta1.RabbitmqCluster) {
458+
r.Spec.Rabbitmq.AdditionalPlugins = []rabbitmqv1beta1.Plugin{"rabbitmq_stream"}
459+
})).To(Succeed())
460+
461+
Eventually(func() map[string][]byte {
462+
secret, err := clientSet.CoreV1().Secrets(cluster.Namespace).Get(ctx, cluster.ChildResourceName("default-user"), metav1.GetOptions{})
463+
Expect(err).NotTo(HaveOccurred())
464+
return secret.Data
465+
}).Should(HaveKeyWithValue("stream-port", []byte("5552")))
466+
})
467+
})
468+
455469
When("instance annotations are updated", func() {
456470
annotationKey := "anno-key"
457471
annotationValue := "anno-value"

docs/api/rabbitmq.com.ref.asciidoc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -243,7 +243,7 @@ Reference to the Kubernetes Secret containing the credentials of the default use
243243
| Field | Description
244244
| *`name`* __string__ | Name of the Secret containing the default user credentials
245245
| *`namespace`* __string__ | Namespace of the Secret containing the default user credentials
246-
| *`keys`* __object (keys:string, values:string)__ | Key-value pairs in the Secret corresponding to `username` and `password`
246+
| *`keys`* __object (keys:string, values:string)__ | Key-value pairs in the Secret corresponding to `username`, `password`, `host`, and `port`
247247
|===
248248

249249

internal/resource/default_user_secret.go

Lines changed: 74 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ import (
2121
"gopkg.in/ini.v1"
2222
corev1 "k8s.io/api/core/v1"
2323
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
24+
25+
"github.com/rabbitmq/cluster-operator/api/v1beta1"
2426
)
2527

2628
const (
@@ -53,22 +55,28 @@ func (builder *DefaultUserSecretBuilder) Build() (client.Object, error) {
5355
return nil, err
5456
}
5557

56-
return &corev1.Secret{
58+
host := fmt.Sprintf("%s.%s.svc.cluster.local", builder.Instance.Name, builder.Instance.Namespace)
59+
60+
// Default user secret implements the service binding Provisioned Service
61+
// See: https://k8s-service-bindings.github.io/spec/#provisioned-service
62+
secret := &corev1.Secret{
5763
ObjectMeta: metav1.ObjectMeta{
5864
Name: builder.Instance.ChildResourceName(DefaultUserSecretName),
5965
Namespace: builder.Instance.Namespace,
6066
},
6167
Type: corev1.SecretTypeOpaque,
62-
// Default user secret implements the service binding Provisioned Service
63-
// See: https://k8s-service-bindings.github.io/spec/#provisioned-service
6468
Data: map[string][]byte{
65-
"provider": []byte(bindingProvider),
66-
"type": []byte(bindingType),
6769
"username": []byte(username),
6870
"password": []byte(password),
6971
"default_user.conf": defaultUserConf,
72+
"provider": []byte(bindingProvider),
73+
"type": []byte(bindingType),
74+
"host": []byte(host),
7075
},
71-
}, nil
76+
}
77+
builder.updatePorts(secret)
78+
79+
return secret, nil
7280
}
7381

7482
func (builder *DefaultUserSecretBuilder) UpdateMayRequireStsRecreate() bool {
@@ -79,6 +87,7 @@ func (builder *DefaultUserSecretBuilder) Update(object client.Object) error {
7987
secret := object.(*corev1.Secret)
8088
secret.Labels = metadata.GetLabels(builder.Instance.Name, builder.Instance.Labels)
8189
secret.Annotations = metadata.ReconcileAndFilterAnnotations(secret.GetAnnotations(), builder.Instance.Annotations)
90+
builder.updatePorts(secret)
8291

8392
if err := controllerutil.SetControllerReference(builder.Instance, secret, builder.Scheme); err != nil {
8493
return fmt.Errorf("failed setting controller reference: %v", err)
@@ -87,6 +96,65 @@ func (builder *DefaultUserSecretBuilder) Update(object client.Object) error {
8796
return nil
8897
}
8998

99+
func (builder *DefaultUserSecretBuilder) updatePorts(secret *corev1.Secret) {
100+
const (
101+
AMQPPort = "5672"
102+
AMQPSPort = "5671"
103+
)
104+
portNames := map[v1beta1.Plugin]string{
105+
"rabbitmq_mqtt": "mqtt-port",
106+
"rabbitmq_stomp": "stomp-port",
107+
"rabbitmq_stream": "stream-port",
108+
"rabbitmq_web_mqtt": "web-mqtt-port",
109+
"rabbitmq_web_stomp": "web-stomp-port",
110+
}
111+
TLSPort := map[string]string{
112+
"mqtt-port": "8883",
113+
"stomp-port": "61614",
114+
"stream-port": "5551",
115+
"web-mqtt-port": "15676",
116+
"web-stomp-port": "15673",
117+
}
118+
port := map[string]string{
119+
"mqtt-port": "1883",
120+
"stomp-port": "61613",
121+
"stream-port": "5552",
122+
"web-mqtt-port": "15675",
123+
"web-stomp-port": "15674",
124+
}
125+
126+
if builder.Instance.Spec.TLS.SecretName != "" {
127+
secret.Data["port"] = []byte(AMQPSPort)
128+
129+
for plugin, portName := range portNames {
130+
if builder.pluginEnabled(plugin) {
131+
secret.Data[portName] = []byte(TLSPort[portName])
132+
} else {
133+
delete(secret.Data, portName)
134+
}
135+
}
136+
} else {
137+
secret.Data["port"] = []byte(AMQPPort)
138+
139+
for plugin, portName := range portNames {
140+
if builder.pluginEnabled(plugin) {
141+
secret.Data[portName] = []byte(port[portName])
142+
} else {
143+
delete(secret.Data, portName)
144+
}
145+
}
146+
}
147+
}
148+
149+
func (builder *DefaultUserSecretBuilder) pluginEnabled(plugin v1beta1.Plugin) bool {
150+
for _, value := range builder.Instance.Spec.Rabbitmq.AdditionalPlugins {
151+
if value == plugin {
152+
return true
153+
}
154+
}
155+
return false
156+
}
157+
90158
func generateDefaultUserConf(username, password string) ([]byte, error) {
91159
ini.PrettySection = false // Remove trailing new line because default_user.conf has only a default section.
92160
cfg, err := ini.Load([]byte{})

internal/resource/default_user_secret_test.go

Lines changed: 152 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,8 @@ var _ = Describe("DefaultUserSecret", func() {
5555
It("creates the necessary default-user secret", func() {
5656
var username []byte
5757
var password []byte
58+
var host []byte
59+
var port []byte
5860
var ok bool
5961

6062
obj, err := defaultUserSecretBuilder.Build()
@@ -86,6 +88,19 @@ var _ = Describe("DefaultUserSecret", func() {
8688
Expect(len(decodedPassword)).To(Equal(24))
8789
})
8890

91+
By("Setting a host that corresponds to the service address", func() {
92+
host, ok = secret.Data["host"]
93+
Expect(ok).NotTo(BeFalse(), "Failed to find a key \"host\" in the generated Secret")
94+
expectedHost := "a name.a namespace.svc.cluster.local"
95+
Expect(host).To(BeEquivalentTo(expectedHost))
96+
})
97+
98+
By("Setting a port that corresponds to the amqp port", func() {
99+
port, ok = secret.Data["port"]
100+
Expect(ok).NotTo(BeFalse(), "Failed to find a key \"port\" in the generated Secret")
101+
Expect(port).To(BeEquivalentTo("5672"))
102+
})
103+
89104
By("creating a default_user.conf file that contains the correct sysctl config format to be parsed by RabbitMQ", func() {
90105
defaultUserConf, ok := secret.Data["default_user.conf"]
91106
Expect(ok).NotTo(BeFalse(), "Failed to find a key \"default_user.conf\" in the generated Secret")
@@ -114,6 +129,99 @@ var _ = Describe("DefaultUserSecret", func() {
114129
})
115130
})
116131

132+
Context("when MQTT, STOMP, streams, WebMQTT, and WebSTOMP are enabled", func() {
133+
It("adds the MQTT, STOMP, stream, WebMQTT, and WebSTOMP ports to the user secret", func() {
134+
var port []byte
135+
136+
instance.Spec.Rabbitmq.AdditionalPlugins = []rabbitmqv1beta1.Plugin{
137+
"rabbitmq_mqtt",
138+
"rabbitmq_stomp",
139+
"rabbitmq_stream",
140+
"rabbitmq_web_mqtt",
141+
"rabbitmq_web_stomp",
142+
}
143+
144+
obj, err := defaultUserSecretBuilder.Build()
145+
Expect(err).NotTo(HaveOccurred())
146+
secret = obj.(*corev1.Secret)
147+
148+
port, ok := secret.Data["mqtt-port"]
149+
Expect(ok).NotTo(BeFalse(), "Failed to find key \"mqtt-port\" in the generated Secret")
150+
Expect(port).To(BeEquivalentTo("1883"))
151+
152+
port, ok = secret.Data["stomp-port"]
153+
Expect(ok).NotTo(BeFalse(), "Failed to find key \"stomp-port\" in the generated Secret")
154+
Expect(port).To(BeEquivalentTo("61613"))
155+
156+
port, ok = secret.Data["stream-port"]
157+
Expect(ok).NotTo(BeFalse(), "Failed to find key \"stream-port\" in the generated Secret")
158+
Expect(port).To(BeEquivalentTo("5552"))
159+
160+
port, ok = secret.Data["web-mqtt-port"]
161+
Expect(ok).NotTo(BeFalse(), "Failed to find key \"web-mqtt-port\" in the generated Secret")
162+
Expect(port).To(BeEquivalentTo("15675"))
163+
164+
port, ok = secret.Data["web-stomp-port"]
165+
Expect(ok).NotTo(BeFalse(), "Failed to find key \"web-stomp-port\" in the generated Secret")
166+
Expect(port).To(BeEquivalentTo("15674"))
167+
})
168+
})
169+
170+
Context("when TLS is enabled", func() {
171+
It("Uses the AMQPS port in the user secret", func() {
172+
var port []byte
173+
174+
instance.Spec.TLS.SecretName = "tls-secret"
175+
176+
obj, err := defaultUserSecretBuilder.Build()
177+
Expect(err).NotTo(HaveOccurred())
178+
secret = obj.(*corev1.Secret)
179+
180+
port, ok := secret.Data["port"]
181+
Expect(ok).NotTo(BeFalse(), "Failed to find key \"port\" in the generated Secret")
182+
Expect(port).To(BeEquivalentTo("5671"))
183+
})
184+
185+
Context("when MQTT, STOMP, streams, WebMQTT, and WebSTOMP are enabled", func() {
186+
It("adds the MQTTS, STOMPS, streams, WebMQTTS, and WebSTOMPS ports to the user secret", func() {
187+
var port []byte
188+
189+
instance.Spec.TLS.SecretName = "tls-secret"
190+
instance.Spec.Rabbitmq.AdditionalPlugins = []rabbitmqv1beta1.Plugin{
191+
"rabbitmq_mqtt",
192+
"rabbitmq_stomp",
193+
"rabbitmq_stream",
194+
"rabbitmq_web_mqtt",
195+
"rabbitmq_web_stomp",
196+
}
197+
198+
obj, err := defaultUserSecretBuilder.Build()
199+
Expect(err).NotTo(HaveOccurred())
200+
secret = obj.(*corev1.Secret)
201+
202+
port, ok := secret.Data["mqtt-port"]
203+
Expect(ok).NotTo(BeFalse(), "Failed to find key \"mqtt-port\" in the generated Secret")
204+
Expect(port).To(BeEquivalentTo("8883"))
205+
206+
port, ok = secret.Data["stomp-port"]
207+
Expect(ok).NotTo(BeFalse(), "Failed to find key \"stomp-port\" in the generated Secret")
208+
Expect(port).To(BeEquivalentTo("61614"))
209+
210+
port, ok = secret.Data["stream-port"]
211+
Expect(ok).NotTo(BeFalse(), "Failed to find key \"stream-port\" in the generated Secret")
212+
Expect(port).To(BeEquivalentTo("5551"))
213+
214+
port, ok = secret.Data["web-mqtt-port"]
215+
Expect(ok).NotTo(BeFalse(), "Failed to find key \"web-mqtt-port\" in the generated Secret")
216+
Expect(port).To(BeEquivalentTo("15676"))
217+
218+
port, ok = secret.Data["web-stomp-port"]
219+
Expect(ok).NotTo(BeFalse(), "Failed to find key \"web-stomp-port\" in the generated Secret")
220+
Expect(port).To(BeEquivalentTo("15673"))
221+
})
222+
})
223+
})
224+
117225
Context("Update with instance labels", func() {
118226
It("Updates the secret", func() {
119227
instance = rabbitmqv1beta1.RabbitmqCluster{
@@ -136,6 +244,7 @@ var _ = Describe("DefaultUserSecret", func() {
136244
"this-was-the-previous-label": "should-be-deleted",
137245
},
138246
},
247+
Data: map[string][]byte{},
139248
}
140249
err := defaultUserSecretBuilder.Update(secret)
141250
Expect(err).NotTo(HaveOccurred())
@@ -184,6 +293,7 @@ var _ = Describe("DefaultUserSecret", func() {
184293
"k8s.io/name": "should-stay",
185294
},
186295
},
296+
Data: map[string][]byte{},
187297
}
188298
err := defaultUserSecretBuilder.Update(secret)
189299
Expect(err).NotTo(HaveOccurred())
@@ -203,8 +313,49 @@ var _ = Describe("DefaultUserSecret", func() {
203313
})
204314
})
205315

316+
Context("When plugins or TLS are updated", func() {
317+
It("updates the secret with the only enabled ports", func() {
318+
instance = rabbitmqv1beta1.RabbitmqCluster{
319+
ObjectMeta: metav1.ObjectMeta{
320+
Name: "rabbit-labelled",
321+
},
322+
}
323+
instance.Spec.Rabbitmq.AdditionalPlugins = []rabbitmqv1beta1.Plugin{
324+
"rabbitmq_mqtt",
325+
"rabbitmq_stream",
326+
}
327+
instance.Spec.TLS.SecretName = "tls-secret"
328+
secret = &corev1.Secret{
329+
Data: map[string][]byte{
330+
"port": []byte("5672"),
331+
"mqtt-port": []byte("1883"),
332+
"web-mqtt-port": []byte("15675"),
333+
},
334+
}
335+
err := defaultUserSecretBuilder.Update(secret)
336+
Expect(err).NotTo(HaveOccurred())
337+
338+
port, ok := secret.Data["port"]
339+
Expect(ok).NotTo(BeFalse())
340+
Expect(port).To(BeEquivalentTo("5671"))
341+
342+
port, ok = secret.Data["mqtt-port"]
343+
Expect(ok).NotTo(BeFalse())
344+
Expect(port).To(BeEquivalentTo("8883"))
345+
346+
port, ok = secret.Data["stream-port"]
347+
Expect(ok).NotTo(BeFalse())
348+
Expect(port).To(BeEquivalentTo("5551"))
349+
350+
_, ok = secret.Data["web-mqtt-port"]
351+
Expect(ok).To(BeFalse())
352+
})
353+
})
354+
206355
It("sets owner reference", func() {
207-
secret = &corev1.Secret{}
356+
secret = &corev1.Secret{
357+
Data: map[string][]byte{},
358+
}
208359
instance = rabbitmqv1beta1.RabbitmqCluster{
209360
ObjectMeta: metav1.ObjectMeta{
210361
Name: "rabbit1",

system_tests/system_test.go

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -151,10 +151,10 @@ var _ = Describe("Operator", func() {
151151
})
152152

153153
It("keeps rabbitmq server related configurations up-to-date", func() {
154-
By("updating enabled plugins when additionalPlugins are modified", func() {
154+
By("updating enabled plugins and the secret ports when additionalPlugins are modified", func() {
155155
// modify rabbitmqcluster.spec.rabbitmq.additionalPlugins
156156
Expect(updateRabbitmqCluster(ctx, rmqClusterClient, cluster.Name, cluster.Namespace, func(cluster *rabbitmqv1beta1.RabbitmqCluster) {
157-
cluster.Spec.Rabbitmq.AdditionalPlugins = []rabbitmqv1beta1.Plugin{"rabbitmq_top"}
157+
cluster.Spec.Rabbitmq.AdditionalPlugins = []rabbitmqv1beta1.Plugin{"rabbitmq_top", "rabbitmq_mqtt"}
158158
})).To(Succeed())
159159

160160
getConfigMapAnnotations := func() map[string]string {
@@ -168,6 +168,12 @@ var _ = Describe("Operator", func() {
168168
Eventually(getConfigMapAnnotations, 60, 1).Should(
169169
Not(HaveKey("rabbitmq.com/pluginsUpdatedAt")), "plugins ConfigMap annotation should have been removed")
170170

171+
Eventually(func() map[string][]byte {
172+
secret, err := clientSet.CoreV1().Secrets(cluster.Namespace).Get(ctx, cluster.ChildResourceName("default-user"), metav1.GetOptions{})
173+
Expect(err).NotTo(HaveOccurred())
174+
return secret.Data
175+
}).Should(HaveKeyWithValue("mqtt-port", []byte("1883")))
176+
171177
_, err := kubectlExec(namespace,
172178
statefulSetPodName(cluster, 0),
173179
"rabbitmq",
@@ -177,6 +183,7 @@ var _ = Describe("Operator", func() {
177183
"rabbitmq_peer_discovery_k8s",
178184
"rabbitmq_prometheus",
179185
"rabbitmq_top",
186+
"rabbitmq_mqtt",
180187
)
181188
Expect(err).ToNot(HaveOccurred())
182189
})

0 commit comments

Comments
 (0)