Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions deploy/crds/rocketmq_v1alpha1_broker_crd.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,31 @@ spec:
items:
type: object
type: array
affinity:

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new complex Kubernetes types (affinity, securityContext, tolerations, imagePullSecrets items) are declared as bare type: object/item type: object with no OpenAPI v3 properties. This provides no server-side validation, so malformed scheduling rules are accepted silently and only surface as Pod creation errors later. For a cleaner CRD (and better kubectl explain output), define the full schema via x-kubernetes-preserve-unknown-fields or the concrete property trees. Same applies to rocketmq_v1alpha1_nameservice_crd.yaml.

description: Affinity and anti-affinity scheduling
type: object

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The affinity field is declared as type: object with no structural schema. This allows any arbitrary YAML to pass CRD validation, deferring errors to the kube-scheduler at runtime. Consider using x-kubernetes-preserve-unknown-fields: true with a more specific schema, or at minimum document the expected structure. Same issue applies to securityContext, nodeSelector, and podAnnotations (all type: object), and to imagePullSecrets/tolerations (items: type: object). While this pattern is common in early-stage operators, it provides zero admission-time validation.

securityContext:
description: Defines privilege and access control settings for a Pod or Container.
type: object

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Description says 'Pod or Container', but the implementation only supports pod-level PodSecurityContext; container-level fields such as allowPrivilegeEscalation cannot be set here.

imagePullSecrets:
description: Pull an image from a private registry
items:
type: object
type: array
tolerations:
description: Taints and tolerations work together to ensure that pods are not scheduled onto inappropriate nodes
items:
type: object
type: array
nodeSelector:
description: The simplest recommended form of node selection constraint.
type: object
podAnnotations:
description: You can use annotations to attach arbitrary non-identifying metadata to objects.
type: object
priorityClassName:
description: Defines priority class's name
type: string
required:
- size
- replicaPerGroup
Expand Down
25 changes: 25 additions & 0 deletions deploy/crds/rocketmq_v1alpha1_nameservice_crd.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,31 @@ spec:
items:
type: object
type: array
affinity:
description: Affinity and anti-affinity scheduling
type: object

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same unstructured schema issue as the broker CRD — affinity, securityContext, nodeSelector, podAnnotations all accept arbitrary objects. Since these fields are duplicated across two CRDs, consider extracting a shared validation schema (or a shared struct with code-generated OpenAPI) to keep them in sync and avoid drift.

securityContext:
description: Defines privilege and access control settings for a Pod or Container.
type: object

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Description says 'Pod or Container', but the implementation only supports pod-level PodSecurityContext; container-level fields such as allowPrivilegeEscalation cannot be set here.

imagePullSecrets:
description: Pull an image from a private registry
items:
type: object
type: array
tolerations:
description: Taints and tolerations work together to ensure that pods are not scheduled onto inappropriate nodes
items:
type: object
type: array
nodeSelector:
description: The simplest recommended form of node selection constraint.
type: object
podAnnotations:
description: You can use annotations to attach arbitrary non-identifying metadata to objects.
type: object
priorityClassName:
description: Defines priority class's name
type: string
required:
- size
- nameServiceImage
Expand Down
180 changes: 180 additions & 0 deletions example/rocketmq_v1alpha1_rocketmq_feature_cluster.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

apiVersion: v1
kind: ConfigMap
metadata:
name: broker-config
data:
# BROKER_MEM sets the broker JVM, if set to "" then Xms = Xmx = max(min(1/2 ram, 1024MB), min(1/4 ram, 8GB))
BROKER_MEM: " -Xms2g -Xmx2g -Xmn1g "
broker-common.conf: |
# brokerClusterName, brokerName, brokerId are automatically generated by the operator and do not set it manually!!!
deleteWhen=04
fileReservedTime=48
flushDiskType=ASYNC_FLUSH
# set brokerRole to ASYNC_MASTER or SYNC_MASTER. DO NOT set to SLAVE because the replica instance will automatically be set!!!
brokerRole=ASYNC_MASTER

---
apiVersion: rocketmq.apache.org/v1alpha1
kind: Broker
metadata:
# name of broker cluster
name: broker
spec:
# size is the number of the broker cluster, each broker cluster contains a master broker and [replicaPerGroup] replica brokers.
size: 1
# nameServers is the [ip:port] list of name service
nameServers: ""
# replicaPerGroup is the number of each broker cluster
replicaPerGroup: 0
# brokerImage is the customized docker image repo of the RocketMQ broker
brokerImage: apacherocketmq/rocketmq-broker:4.5.0-alpine-operator-0.3.0
# imagePullPolicy is the image pull policy
imagePullPolicy: Always
# resources describes the compute resource requirements and limits
resources:
requests:
memory: "2048Mi"
cpu: "250m"
limits:
memory: "12288Mi"
cpu: "500m"
# allowRestart defines whether allow pod restart
allowRestart: true
# storageMode can be EmptyDir, HostPath, StorageClass
storageMode: EmptyDir
# hostPath is the local path to store data
hostPath: /tmp/data/rocketmq/broker
# scalePodName is [Broker name]-[broker group number]-master-0
scalePodName: broker-0-master-0
# env defines custom env, e.g. BROKER_MEM
env:
- name: BROKER_MEM
valueFrom:
configMapKeyRef:
name: broker-config
key: BROKER_MEM
# volumes defines the broker.conf
volumes:
- name: broker-config
configMap:
name: broker-config
items:
- key: broker-common.conf
path: broker-common.conf
# volumeClaimTemplates defines the storageClass
volumeClaimTemplates:
- metadata:
name: broker-storage
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 8Gi
selector:
matchLabels:
app: broker-storage-pv
---
apiVersion: rocketmq.apache.org/v1alpha1
kind: NameService
metadata:
name: name-service
spec:
# size is the the name service instance number of the name service cluster
size: 1
# nameServiceImage is the customized docker image repo of the RocketMQ name service
nameServiceImage: apacherocketmq/rocketmq-nameserver:4.5.0-alpine-operator-0.3.0
# imagePullPolicy is the image pull policy
imagePullPolicy: Always
# hostNetwork can be true or false
hostNetwork: true
podAnnotations:
prometheus.io/path: /metrics
prometheus.io/port: "5557"
prometheus.io/scrape: "true"
securityContext:
allowPrivilegeEscalation: true

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

allowPrivilegeEscalation is a container-level SecurityContext field, but the CRD and controller implement pod-level SecurityContext (*corev1.PodSecurityContext). This field will be silently ignored when applied to the NameService StatefulSet pod template.

runAsUser: 0

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The NameService securityContext sets allowPrivilegeEscalation: true, runAsUser: 0, runAsGroup: 0. This example is intended as a copyable template, and running the name server as root with privilege escalation enabled is a poor security pattern for an Apache-shipped reference. Consider a non-root runAsUser (the alpine image supports a dedicated UID) and allowPrivilegeEscalation: false, or at minimum document the risk and make it opt-in.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Example configures NameService to run as root (runAsUser: 0). Consider demonstrating a non-root, least-privilege security context instead.

runAsGroup: 0
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: kubernetes.io/hostname
operator: In
values:
- azure-k8s-7
- azure-k8s-1
- azure-k8s-3
- azure-k8s-2
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The example sets allowPrivilegeEscalation: true and runAsUser: 0 / runAsGroup: 0, which runs the NameService container as root with privilege escalation enabled. This is a security anti-pattern and should not be in an example file that users will copy. If root is truly required for RocketMQ, add a comment explaining why and recommend dropping to a non-root user where possible. At minimum, set allowPrivilegeEscalation: false.

podAffinityTerm:
labelSelector:
matchExpressions:
- key: app
operator: In
values:
- name_service
topologyKey: kubernetes.io/hostname
priorityClassName: ""
imagePullSecrets:
- name: registry-secret
nodeSelector:
azure: "true"
tolerations:
- key: "key1"
operator: "Equal"
value: "value1"
effect: "NoSchedule"
- key: "key2"
operator: "Equal"
value: "value1"
effect: "NoExecute"
# Set DNS policy for the pod.
# Defaults to "ClusterFirst".
# Valid values are 'ClusterFirstWithHostNet', 'ClusterFirst', 'Default' or 'None'.
# DNS parameters given in DNSConfig will be merged with the policy selected with DNSPolicy.
# To have DNS options set along with hostNetwork, you have to specify DNS policy
# explicitly to 'ClusterFirstWithHostNet'.
dnsPolicy: ClusterFirstWithHostNet
# resources describes the compute resource requirements and limits
resources:
requests:
memory: "512Mi"
cpu: "250m"
limits:
memory: "1024Mi"
cpu: "500m"
# storageMode can be EmptyDir, HostPath, StorageClass
storageMode: EmptyDir
# hostPath is the local path to store data
hostPath: /data/rocketmq/nameserver
# volumeClaimTemplates defines the storageClass
volumeClaimTemplates:
- metadata:
name: namesrv-storage
spec:
accessModes:
- ReadWriteOnce
storageClassName: rocketmq-storage
resources:
requests:
storage: 1Gi
15 changes: 15 additions & 0 deletions pkg/apis/rocketmq/v1alpha1/broker_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,21 @@ type BrokerSpec struct {
VolumeClaimTemplates []corev1.PersistentVolumeClaim `json:"volumeClaimTemplates"`

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No test changes detected alongside source modifications. Consider adding tests to cover the changes.

// The name of pod where the metadata from
ScalePodName string `json:"scalePodName"`
// Affinity, affinity and anti-affinity scheduling

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

New pointer/slice/map fields (Affinity, SecurityContext, ImagePullSecrets, Tolerations, NodeSelector, PodAnnotations) were added to BrokerSpec, but the PR does not include updates to zz_generated_deepcopy.go. If the generated DeepCopyInto is not regenerated, these fields are shallow-copied: Affinity/SecurityContext pointers and NodeSelector/PodAnnotations maps will be shared between the informer cache and the object handed to the reconciler. Any mutation corrupts the shared cache and can cause cross-reconcile state corruption. Run the operator-sdk/k8s codegen to regenerate deepcopy for both broker_types.go and nameservice_types.go.

Affinity *corev1.Affinity `json:"affinity,omitempty"`
// SecurityContext defines privilege and access control settings for a Pod or Container.
SecurityContext *corev1.PodSecurityContext `json:"securityContext,omitempty"`
// ImagePullSecrets pull an image from a private registry
ImagePullSecrets []corev1.LocalObjectReference `json:"imagePullSecrets,omitempty"`
// Tolerations taints and tolerations work together to ensure that pods are not scheduled onto inappropriate nodes
Tolerations []corev1.Toleration `json:"tolerations,omitempty"`
// NodeSelector is the simplest recommended form of node selection constraint.
NodeSelector map[string]string `json:"nodeSelector,omitempty"`
// PodAnnotations you can use annotations to attach arbitrary non-identifying metadata to objects.
PodAnnotations map[string]string `json:"podAnnotations,omitempty"`
// PriorityClassName defines priority class's name
PriorityClassName string `json:"priorityClassName,omitempty"`
Comment on lines +68 to +72

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems null checks are required for them, would define them as pointer be better?

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No test coverage is added for the new fields. The broker and nameservice controller tests should verify that: (1) a CR with these fields produces a StatefulSet with the correct PodSpec, (2) nil/empty values produce the same StatefulSet as before (backward compat), and (3) updates to these fields trigger a rolling update. Without tests, regressions in the reconciliation logic are likely to go undetected.


}

// BrokerStatus defines the observed state of Broker
Expand Down
14 changes: 14 additions & 0 deletions pkg/apis/rocketmq/v1alpha1/nameservice_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,20 @@ type NameServiceSpec struct {
HostPath string `json:"hostPath"`
// VolumeClaimTemplates defines the StorageClass
VolumeClaimTemplates []corev1.PersistentVolumeClaim `json:"volumeClaimTemplates"`
// Affinity, affinity and anti-affinity scheduling
Affinity *corev1.Affinity `json:"affinity,omitempty"`
// SecurityContext defines privilege and access control settings for a Pod or Container.
SecurityContext *corev1.PodSecurityContext `json:"securityContext,omitempty"`
// ImagePullSecrets pull an image from a private registry
ImagePullSecrets []corev1.LocalObjectReference `json:"imagePullSecrets,omitempty"`
// Tolerations taints and tolerations work together to ensure that pods are not scheduled onto inappropriate nodes
Tolerations []corev1.Toleration `json:"tolerations,omitempty"`
// NodeSelector is the simplest recommended form of node selection constraint.
NodeSelector map[string]string `json:"nodeSelector,omitempty"`
// PodAnnotations you can use annotations to attach arbitrary non-identifying metadata to objects.
PodAnnotations map[string]string `json:"podAnnotations,omitempty"`
// PriorityClassName defines priority class's name
PriorityClassName string `json:"priorityClassName,omitempty"`
Comment on lines +61 to +65

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems null checks are required for them, would define them as pointer be better?

}

// NameServiceStatus defines the observed state of NameService
Expand Down
7 changes: 7 additions & 0 deletions pkg/controller/broker/broker_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -401,8 +401,15 @@ func (r *ReconcileBroker) getBrokerStatefulSet(broker *rocketmqv1alpha1.Broker,
Template: corev1.PodTemplateSpec{
ObjectMeta: metav1.ObjectMeta{
Labels: ls,
Annotations: broker.Spec.PodAnnotations,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new pod-scheduling fields are wired into getBrokerStatefulSet (Annotations, Affinity, SecurityContext, ImagePullSecrets, Tolerations, NodeSelector, PriorityClassName) and similarly into nameservice_controller.go, but no test changes accompany this PR. There is no coverage verifying that CR-spec values propagate to the rendered StatefulSet PodTemplateSpec, nor that nil/empty values (e.g. unset Affinity, empty PriorityClassName) render safely. Add reconciler tests asserting these fields pass through correctly.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Annotations: broker.Spec.PodAnnotations directly assigns the spec map to the pod template. If a user later removes all annotations from the spec (setting it to nil or {}), the existing annotations on the live StatefulSet's pod template won't be cleared during an update unless the controller explicitly handles this in its update/reconciliation path. Verify that the update logic in updateBrokerStatefulSet (or equivalent) properly propagates annotation removal, not just addition.

},

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All six new PodSpec fields (Affinity, SecurityContext, ImagePullSecrets, Tolerations, NodeSelector, PriorityClassName) are assigned directly from the CR spec with no nil-guard. This is safe for the Kubernetes API (nil/empty values are valid), but it means any change to these fields will trigger a StatefulSet rolling update. For fields like nodeSelector or tolerations that an operator might want to change frequently, consider documenting this behavior so users understand the pod churn implications.

Spec: corev1.PodSpec{
Affinity: broker.Spec.Affinity,
SecurityContext: broker.Spec.SecurityContext,
ImagePullSecrets: broker.Spec.ImagePullSecrets,
Tolerations: broker.Spec.Tolerations,
NodeSelector: broker.Spec.NodeSelector,
PriorityClassName: broker.Spec.PriorityClassName,
Comment on lines +407 to +412

@AdheipSingh AdheipSingh Nov 15, 2020

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You need to write functions for each of them and handle nil value scenarios, by directly adding this you are forcing the user at all times to specify these values, the operator will crash if you don't pass in tolerations.
@liuruiyiyang since these values will go to both broker/nameserver we can create single functions which can be used both in broker and nameserver

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1 This pr added features that help enrich operator, hope that corresponding null check would be supplemented.

Containers: []corev1.Container{{
Resources: broker.Spec.Resources,
Image: broker.Spec.BrokerImage,
Expand Down
7 changes: 7 additions & 0 deletions pkg/controller/nameservice/nameservice_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -316,10 +316,17 @@ func (r *ReconcileNameService) statefulSetForNameService(nameService *rocketmqv1
Template: corev1.PodTemplateSpec{
ObjectMeta: metav1.ObjectMeta{
Labels: ls,
Annotations: nameService.Spec.PodAnnotations,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same annotation propagation concern as the broker controller: Annotations: nameService.Spec.PodAnnotations is a direct assignment. If the existing StatefulSet's pod template has annotations set by a mutating webhook or by a previous spec version, they will be silently overwritten on reconciliation. Consider merging annotations (existing + spec) rather than replacing, or document that spec annotations are authoritative.

},
Spec: corev1.PodSpec{
HostNetwork: nameService.Spec.HostNetwork,
DNSPolicy: nameService.Spec.DNSPolicy,
Affinity: nameService.Spec.Affinity,
SecurityContext: nameService.Spec.SecurityContext,
ImagePullSecrets: nameService.Spec.ImagePullSecrets,
Tolerations: nameService.Spec.Tolerations,
NodeSelector: nameService.Spec.NodeSelector,
PriorityClassName: nameService.Spec.PriorityClassName,
Comment on lines +324 to +329

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same here, we need to write functions to get values to handle nil/empty scenarios

Containers: []corev1.Container{{
Resources: nameService.Spec.Resources,
Image: nameService.Spec.NameServiceImage,
Expand Down