Skip to content

Commit 9e9c42a

Browse files
authored
Merge pull request #1474 from rabbitmq/zerpet/erl_inetrc_configuration
Support for Erlang INET configuration
2 parents c4d3837 + d92f51c commit 9e9c42a

22 files changed

+248
-29
lines changed

api/v1beta1/rabbitmqcluster_types.go

+8
Original file line numberDiff line numberDiff line change
@@ -385,6 +385,10 @@ type RabbitmqClusterConfigurationSpec struct {
385385
// For more information on env config, see https://www.rabbitmq.com/man/rabbitmq-env.conf.5.html
386386
// +kubebuilder:validation:MaxLength:=100000
387387
EnvConfig string `json:"envConfig,omitempty"`
388+
// Erlang Inet configuration to apply to the Erlang VM running rabbit.
389+
// See also: https://www.erlang.org/doc/apps/erts/inet_cfg.html
390+
// +kubebuilder:validation:MaxLength:=2000
391+
ErlangInetConfig string `json:"erlangInetConfig,omitempty"`
388392
}
389393

390394
// The settings for the persistent storage desired for each Pod in the RabbitmqCluster.
@@ -407,6 +411,10 @@ type RabbitmqClusterServiceSpec struct {
407411
Type corev1.ServiceType `json:"type,omitempty"`
408412
// Annotations to add to the Service.
409413
Annotations map[string]string `json:"annotations,omitempty"`
414+
// IPFamilyPolicy represents the dual-stack-ness requested or required by a Service
415+
// See also: https://pkg.go.dev/k8s.io/api/core/v1#IPFamilyPolicy
416+
// +kubebuilder:validation:Enum=SingleStack;PreferDualStack;RequireDualStack
417+
IPFamilyPolicy *corev1.IPFamilyPolicy `json:"ipFamilyPolicy,omitempty"`
410418
}
411419

412420
func (cluster *RabbitmqCluster) TLSEnabled() bool {

api/v1beta1/rabbitmqcluster_types_test.go

+18
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,24 @@ var _ = Describe("RabbitmqCluster", func() {
135135
Expect(apierrors.IsInvalid(k8sClient.Create(context.Background(), invalidService))).To(BeTrue())
136136
Expect(k8sClient.Create(context.Background(), invalidService)).To(MatchError(ContainSubstring("supported values: \"ClusterIP\", \"LoadBalancer\", \"NodePort\"")))
137137
})
138+
139+
By("checking the IP family policy", func() {
140+
invalidSvc := generateRabbitmqClusterObject("madeup-family-policy")
141+
policy := corev1.IPFamilyPolicy("my-awesome-policy")
142+
invalidSvc.Spec.Service.IPFamilyPolicy = &policy
143+
Expect(apierrors.IsInvalid(k8sClient.Create(context.Background(), invalidSvc))).To(BeTrue())
144+
})
145+
})
146+
147+
It("can be created with Erlang configuration", func() {
148+
created := generateRabbitmqClusterObject("erlang-configuration")
149+
erlangConfig := "{some_config, 123}."
150+
created.Spec.Rabbitmq.ErlangInetConfig = erlangConfig
151+
Expect(k8sClient.Create(context.Background(), created)).To(Succeed())
152+
153+
got := &RabbitmqCluster{}
154+
Expect(k8sClient.Get(context.Background(), getKey(created), got)).To(Succeed())
155+
Expect(got.Spec.Rabbitmq.ErlangInetConfig).To(Equal(erlangConfig))
138156
})
139157

140158
Describe("ChildResourceName", func() {

api/v1beta1/zz_generated.deepcopy.go

+5-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

config/crd/bases/rabbitmq.com_rabbitmqclusters.yaml

+12-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ apiVersion: apiextensions.k8s.io/v1
1010
kind: CustomResourceDefinition
1111
metadata:
1212
annotations:
13-
controller-gen.kubebuilder.io/version: v0.12.1
13+
controller-gen.kubebuilder.io/version: v0.13.0
1414
name: rabbitmqclusters.rabbitmq.com
1515
spec:
1616
group: rabbitmq.com
@@ -4127,6 +4127,10 @@ spec:
41274127
description: Modify to add to the rabbitmq-env.conf file. Modifying this property on an existing RabbitmqCluster will trigger a StatefulSet rolling restart and will cause rabbitmq downtime. For more information on env config, see https://www.rabbitmq.com/man/rabbitmq-env.conf.5.html
41284128
maxLength: 100000
41294129
type: string
4130+
erlangInetConfig:
4131+
description: 'Erlang Inet configuration to apply to the Erlang VM running rabbit. See also: https://www.erlang.org/doc/apps/erts/inet_cfg.html'
4132+
maxLength: 2000
4133+
type: string
41304134
type: object
41314135
replicas:
41324136
default: 1
@@ -4233,6 +4237,13 @@ spec:
42334237
type: string
42344238
description: Annotations to add to the Service.
42354239
type: object
4240+
ipFamilyPolicy:
4241+
description: 'IPFamilyPolicy represents the dual-stack-ness requested or required by a Service See also: https://pkg.go.dev/k8s.io/api/core/v1#IPFamilyPolicy'
4242+
enum:
4243+
- SingleStack
4244+
- PreferDualStack
4245+
- RequireDualStack
4246+
type: string
42364247
type:
42374248
default: ClusterIP
42384249
description: 'Type of Service to create for the cluster. Must be one of: ClusterIP, LoadBalancer, NodePort. For more info see https://pkg.go.dev/k8s.io/api/core/v1#ServiceType'

controllers/rabbitmqcluster_controller.go

+3
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,8 @@ func (r *RabbitmqClusterReconciler) Reconcile(ctx context.Context, req ctrl.Requ
151151

152152
logger.Info("Start reconciling")
153153

154+
// FIXME: marshalling is expensive. We are marshalling only for the sake of logging.
155+
// Implement Stringer interface instead
154156
instanceSpec, err := json.Marshal(rabbitmqCluster.Spec)
155157
if err != nil {
156158
logger.Error(err, "Failed to marshal cluster spec")
@@ -290,6 +292,7 @@ func (r *RabbitmqClusterReconciler) updateStatusConditions(ctx context.Context,
290292

291293
if !reflect.DeepEqual(rmq.Status.Conditions, oldConditions) {
292294
if err = r.Status().Update(ctx, rmq); err != nil {
295+
// FIXME: must fetch again to avoid the conflict
293296
if k8serrors.IsConflict(err) {
294297
logger.Info("failed to update status because of conflict; requeueing...",
295298
"namespace", rmq.Namespace,

controllers/rabbitmqcluster_controller_test.go

+18
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ package controllers_test
1313
import (
1414
"context"
1515
"fmt"
16+
"k8s.io/utils/ptr"
1617
"time"
1718

1819
"k8s.io/utils/pointer"
@@ -339,6 +340,23 @@ var _ = Describe("RabbitmqClusterController", func() {
339340
Expect(clientSvc.Spec.Type).Should(Equal(corev1.ServiceTypeLoadBalancer))
340341
Expect(clientSvc.Annotations).Should(HaveKeyWithValue("annotations", "cr-annotation"))
341342
})
343+
344+
It("creates the service with the expected IP family policy", func() {
345+
cluster = &rabbitmqv1beta1.RabbitmqCluster{
346+
ObjectMeta: metav1.ObjectMeta{Name: "rabbit-with-ip-family", Namespace: defaultNamespace},
347+
}
348+
cluster.Spec.Service.IPFamilyPolicy = ptr.To(corev1.IPFamilyPolicyPreferDualStack)
349+
350+
Expect(client.Create(ctx, cluster)).To(Succeed())
351+
352+
clientSvc := service(ctx, cluster, "")
353+
Expect(clientSvc.Spec.IPFamilyPolicy).ToNot(BeNil())
354+
Expect(clientSvc.Spec.IPFamilyPolicy).To(BeEquivalentTo(ptr.To("PreferDualStack")))
355+
356+
headlessSvc := service(ctx, cluster, "nodes")
357+
Expect(headlessSvc.Spec.IPFamilyPolicy).ToNot(BeNil())
358+
Expect(headlessSvc.Spec.IPFamilyPolicy).To(BeEquivalentTo(ptr.To("PreferDualStack")))
359+
})
342360
})
343361

344362
Context("Resource requirements configurations", func() {

docs/api/rabbitmq.com.ref.asciidoc

+2
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,7 @@ RabbitMQ-related configuration.
154154
| *`additionalConfig`* __string__ | Modify to add to the rabbitmq.conf file in addition to default configurations set by the operator. Modifying this property on an existing RabbitmqCluster will trigger a StatefulSet rolling restart and will cause rabbitmq downtime. For more information on this config, see https://www.rabbitmq.com/configure.html#config-file
155155
| *`advancedConfig`* __string__ | Specify any rabbitmq advanced.config configurations to apply to the cluster. For more information on advanced config, see https://www.rabbitmq.com/configure.html#advanced-config-file
156156
| *`envConfig`* __string__ | Modify to add to the rabbitmq-env.conf file. Modifying this property on an existing RabbitmqCluster will trigger a StatefulSet rolling restart and will cause rabbitmq downtime. For more information on env config, see https://www.rabbitmq.com/man/rabbitmq-env.conf.5.html
157+
| *`erlangInetConfig`* __string__ | Erlang Inet configuration to apply to the Erlang VM running rabbit. See also: https://www.erlang.org/doc/apps/erts/inet_cfg.html
157158
|===
158159

159160

@@ -283,6 +284,7 @@ Settable attributes for the Service resource.
283284
| Field | Description
284285
| *`type`* __link:https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#servicetype-v1-core[$$ServiceType$$]__ | Type of Service to create for the cluster. Must be one of: ClusterIP, LoadBalancer, NodePort. For more info see https://pkg.go.dev/k8s.io/api/core/v1#ServiceType
285286
| *`annotations`* __object (keys:string, values:string)__ | Annotations to add to the Service.
287+
| *`ipFamilyPolicy`* __link:https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#ipfamilypolicy-v1-core[$$IPFamilyPolicy$$]__ | IPFamilyPolicy represents the dual-stack-ness requested or required by a Service See also: https://pkg.go.dev/k8s.io/api/core/v1#IPFamilyPolicy
286288
|===
287289

288290

docs/examples/ipv6/.ci-skip

Whitespace-only changes.

docs/examples/ipv6/README.md

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# IPv6 example
2+
3+
This example shows the required configuration to force the Erlang VM to use
4+
IPv6. RabbitMQ relies on Erlang's INET module for network interaction.
5+
6+
You can deploy this example using:
7+
8+
```shell
9+
kubectl apply -f rabbitmq.yaml
10+
```

docs/examples/ipv6/rabbitmq.yaml

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
---
2+
apiVersion: v1
3+
kind: Namespace
4+
metadata:
5+
name: rabbits
6+
labels:
7+
app: rabbitmq
8+
---
9+
apiVersion: rabbitmq.com/v1beta1
10+
kind: RabbitmqCluster
11+
metadata:
12+
name: rabbit-ipv6
13+
namespace: rabbits
14+
labels:
15+
app: rabbitmq
16+
spec:
17+
resources:
18+
requests: {}
19+
limits: {}
20+
rabbitmq:
21+
erlangInetConfig: |
22+
{inet6, true}.
23+
envConfig: |
24+
SERVER_ADDITIONAL_ERL_ARGS="-kernel inetrc '/etc/rabbitmq/erl_inetrc' -proto_dist inet6_tcp"
25+
RABBITMQ_CTL_ERL_ARGS="-proto_dist inet6_tcp"
26+
additionalConfig: |
27+
cluster_formation.k8s.host = kubernetes.default.svc.cluster.local
28+
replicas: 3
29+
service:
30+
ipFamilyPolicy: "PreferDualStack"

go.mod

+1-1
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ require (
2323
k8s.io/apimachinery v0.28.2
2424
k8s.io/client-go v0.28.1
2525
k8s.io/klog/v2 v2.100.1
26-
k8s.io/utils v0.0.0-20230505201702-9f6742963106
26+
k8s.io/utils v0.0.0-20230726121419-3b25d923346b
2727
sigs.k8s.io/controller-runtime v0.15.1
2828
sigs.k8s.io/controller-runtime/tools/setup-envtest v0.0.0-20220217024943-cfd92767d28e
2929
sigs.k8s.io/controller-tools v0.13.0

go.sum

+2-2
Original file line numberDiff line numberDiff line change
@@ -566,8 +566,8 @@ k8s.io/klog/v2 v2.100.1 h1:7WCHKK6K8fNhTqfBhISHQ97KrnJNFZMcQvKp7gP/tmg=
566566
k8s.io/klog/v2 v2.100.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0=
567567
k8s.io/kube-openapi v0.0.0-20230816210353-14e408962443 h1:CAIciCnJnSOQxPd0xvpV6JU3D4AJvnYbImPpFpO9Hnw=
568568
k8s.io/kube-openapi v0.0.0-20230816210353-14e408962443/go.mod h1:wZK2AVp1uHCp4VamDVgBP2COHZjqD1T68Rf0CM3YjSM=
569-
k8s.io/utils v0.0.0-20230505201702-9f6742963106 h1:EObNQ3TW2D+WptiYXlApGNLVy0zm/JIBVY9i+M4wpAU=
570-
k8s.io/utils v0.0.0-20230505201702-9f6742963106/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
569+
k8s.io/utils v0.0.0-20230726121419-3b25d923346b h1:sgn3ZU783SCgtaSJjpcVVlRqd6GSnlTLKgpAAttJvpI=
570+
k8s.io/utils v0.0.0-20230726121419-3b25d923346b/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
571571
sigs.k8s.io/controller-runtime v0.15.1 h1:9UvgKD4ZJGcj24vefUFgZFP3xej/3igL9BsOUTb/+4c=
572572
sigs.k8s.io/controller-runtime v0.15.1/go.mod h1:7ngYvp1MLT+9GeZ+6lH3LOlcHkp/+tzA/fmHa4iq9kk=
573573
sigs.k8s.io/controller-runtime/tools/setup-envtest v0.0.0-20220217024943-cfd92767d28e h1:Z4+OH6QT2Xy2aqyN5BjyBEGnINrFzkHlMqLCyrE0A+g=

internal/resource/configmap.go

+2-1
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ func (builder *ServerConfigMapBuilder) Update(object client.Object) error {
9898
return err
9999
}
100100

101-
userConfiguration := ini.Empty(ini.LoadOptions{})
101+
userConfiguration := ini.Empty()
102102
userConfigurationSection := userConfiguration.Section("")
103103

104104
if builder.Instance.TLSEnabled() {
@@ -241,6 +241,7 @@ func (builder *ServerConfigMapBuilder) Update(object client.Object) error {
241241

242242
updateProperty(configMap.Data, "advanced.config", rmqProperties.AdvancedConfig)
243243
updateProperty(configMap.Data, "rabbitmq-env.conf", rmqProperties.EnvConfig)
244+
updateProperty(configMap.Data, "erl_inetrc", rmqProperties.ErlangInetConfig)
244245

245246
if err := controllerutil.SetControllerReference(builder.Instance, configMap, builder.Scheme); err != nil {
246247
return fmt.Errorf("failed setting controller reference: %w", err)

internal/resource/configmap_test.go

+21-1
Original file line numberDiff line numberDiff line change
@@ -183,7 +183,7 @@ var _ = Describe("GenerateServerConfigMap", func() {
183183
Expect(configMap.Data).To(HaveKeyWithValue("advanced.config", "[my-awesome-config]."))
184184
})
185185

186-
It("does set data.advancedConfig when empty", func() {
186+
It("does not set data.advancedConfig when empty", func() {
187187
instance.Spec.Rabbitmq.AdvancedConfig = ""
188188
Expect(configMapBuilder.Update(configMap)).To(Succeed())
189189
Expect(configMap.Data).ToNot(HaveKey("advanced.config"))
@@ -559,6 +559,26 @@ CONSOLE_LOG=new`
559559
Expect(configMapBuilder.Update(configMap)).To(Succeed())
560560
Expect(configMap.Annotations).To(BeEmpty())
561561
})
562+
563+
Context("Erlang INET configuration", func() {
564+
It("sets erlangInetRc key", func() {
565+
instance.Spec.Rabbitmq.ErlangInetConfig = "{any-config, is-set}."
566+
Expect(configMapBuilder.Update(configMap)).To(Succeed())
567+
Expect(configMap.Data).To(HaveKeyWithValue("erl_inetrc", "{any-config, is-set}."))
568+
})
569+
570+
When("erlangInetRc is removed", func() {
571+
It("deletes the key", func() {
572+
instance.Spec.Rabbitmq.ErlangInetConfig = "any string is set, rabbit will do validation"
573+
Expect(configMapBuilder.Update(configMap)).To(Succeed())
574+
Expect(configMap.Data).To(HaveKey("erl_inetrc"))
575+
576+
instance.Spec.Rabbitmq.ErlangInetConfig = ""
577+
Expect(configMapBuilder.Update(configMap)).To(Succeed())
578+
Expect(configMap.Data).ToNot(HaveKey("erl_inetrc"))
579+
})
580+
})
581+
})
562582
})
563583

564584
Context("UpdateMayRequireStsRecreate", func() {

internal/resource/headless_service.go

+3-2
Original file line numberDiff line numberDiff line change
@@ -59,17 +59,18 @@ func (builder *HeadlessServiceBuilder) Update(object client.Object) error {
5959
{
6060
Protocol: corev1.ProtocolTCP,
6161
Port: 4369,
62-
TargetPort: intstr.FromInt(4369),
62+
TargetPort: intstr.FromInt32(4369),
6363
Name: "epmd",
6464
},
6565
{
6666
Protocol: corev1.ProtocolTCP,
6767
Port: 25672,
68-
TargetPort: intstr.FromInt(25672),
68+
TargetPort: intstr.FromInt32(25672),
6969
Name: "cluster-rpc", // aka distribution port
7070
},
7171
},
7272
PublishNotReadyAddresses: true,
73+
IPFamilyPolicy: builder.Instance.Spec.Service.IPFamilyPolicy,
7374
}
7475

7576
if err := controllerutil.SetControllerReference(builder.Instance, service, builder.Scheme); err != nil {

internal/resource/headless_service_test.go

+34-2
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import (
1919
"k8s.io/apimachinery/pkg/runtime"
2020
"k8s.io/apimachinery/pkg/util/intstr"
2121
defaultscheme "k8s.io/client-go/kubernetes/scheme"
22+
"k8s.io/utils/ptr"
2223
)
2324

2425
var _ = Describe("HeadlessService", func() {
@@ -170,11 +171,11 @@ var _ = Describe("HeadlessService", func() {
170171
},
171172
},
172173
}
173-
err := serviceBuilder.Update(service)
174-
Expect(err).NotTo(HaveOccurred())
175174
})
176175

177176
It("sets the required Spec", func() {
177+
Expect(serviceBuilder.Update(service)).To(Succeed())
178+
178179
expectedSpec := corev1.ServiceSpec{
179180
Type: corev1.ServiceTypeClusterIP,
180181
ClusterIP: "None",
@@ -198,7 +199,38 @@ var _ = Describe("HeadlessService", func() {
198199
},
199200
PublishNotReadyAddresses: true,
200201
}
202+
Expect(service.Spec).To(Equal(expectedSpec))
203+
})
204+
205+
It("sets the IP family", func() {
206+
dualStack := ptr.To(corev1.IPFamilyPolicyPreferDualStack)
207+
instance.Spec.Service.IPFamilyPolicy = dualStack
208+
Expect(serviceBuilder.Update(service)).To(Succeed())
201209

210+
expectedSpec := corev1.ServiceSpec{
211+
Type: corev1.ServiceTypeClusterIP,
212+
ClusterIP: "None",
213+
Selector: map[string]string{
214+
"app.kubernetes.io/name": "rabbit-spec",
215+
},
216+
SessionAffinity: corev1.ServiceAffinityNone,
217+
Ports: []corev1.ServicePort{
218+
{
219+
Protocol: corev1.ProtocolTCP,
220+
Port: 4369,
221+
TargetPort: intstr.FromInt32(4369),
222+
Name: "epmd",
223+
},
224+
{
225+
Protocol: corev1.ProtocolTCP,
226+
Port: 25672,
227+
TargetPort: intstr.FromInt32(25672),
228+
Name: "cluster-rpc",
229+
},
230+
},
231+
PublishNotReadyAddresses: true,
232+
IPFamilyPolicy: dualStack,
233+
}
202234
Expect(service.Spec).To(Equal(expectedSpec))
203235
})
204236
})

internal/resource/service.go

+1
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ func (builder *ServiceBuilder) Update(object client.Object) error {
5858
service.Labels = metadata.GetLabels(builder.Instance.Name, builder.Instance.Labels)
5959
service.Spec.Type = builder.Instance.Spec.Service.Type
6060
service.Spec.Selector = metadata.LabelSelector(builder.Instance.Name)
61+
service.Spec.IPFamilyPolicy = builder.Instance.Spec.Service.IPFamilyPolicy
6162

6263
service.Spec.Ports = builder.updatePorts(service.Spec.Ports)
6364

0 commit comments

Comments
 (0)