rgw: support kafka 4.x brokers

librdkafka submodule

Vendors librdkafka v2.12.1 as a submodule (default) and teaches the
notification teuthology task to set up Kafka 4.x KRaft brokers. The
distro-shipped librdkafka 1.6.1 cannot SCRAM-auth against Kafka 4.x
brokers (#75900); the bundled version fixes that. Build with
WITH_SYSTEM_RDKAFKA=ON to opt back into the distro library (floor 2.6.1).
follwinf changes needed with teh submodule:
* build kafka_stub only in case of system librdkafka
* use the same header location in submodule and system
* supress compilation warning to avoid -WError issues

2.12.1 matches Fedora(44)'s current librdkafka-devel, so the vendored copy
tracks a version that distro packagers have already vetted rather than
whichever release happens to be latest upstream.

The teuthology version matrix picks one of {3.9.2, 4.1, 4.2, 4.3} per run.
3.9.2 covers the last Zookeeper-based release for the migration cohort.
4.1/4.2/4.3 track Apache's current three supported minor lines and are
resolved to the latest patch at test time via dlcdn.apache.org/kafka/, so
new patches within a supported minor need no PR update. A new minor line
(4.4) requires renaming one YAML.
* new java version (17) is needed for kafka 4.2 and up (we wil use it on
  all version)
* kerberos task need to run before the kafka install task

After pulling this branch, fetch the new submodule:

    git submodule update --init src/librdkafka

Fixes: https://tracker.ceph.com/issues/77336

Signed-off-by: ShreeJejurikar <shreemj8@gmail.com>
Co-authored-by: Yuval Lifshitz <ylifshit@ibm.com>
This commit is contained in:
ShreeJejurikar 2026-06-20 16:28:49 +05:30 committed by Yuval Lifshitz
parent 179b4aeed1
commit efe54523a4
22 changed files with 251 additions and 48 deletions

3
.gitmodules vendored
View File

@ -85,3 +85,6 @@
[submodule "src/lss"] [submodule "src/lss"]
path = src/lss path = src/lss
url = https://chromium.googlesource.com/linux-syscall-support url = https://chromium.googlesource.com/linux-syscall-support
[submodule "src/librdkafka"]
path = src/librdkafka
url = https://github.com/confluentinc/librdkafka.git

View File

@ -587,6 +587,7 @@ option(WITH_RADOSGW "RADOS Gateway is enabled" ON)
option(WITH_RADOSGW_BEAST_OPENSSL "RADOS Gateway's Beast frontend uses OpenSSL" ON) option(WITH_RADOSGW_BEAST_OPENSSL "RADOS Gateway's Beast frontend uses OpenSSL" ON)
option(WITH_RADOSGW_AMQP_ENDPOINT "RADOS Gateway's pubsub support for AMQP push endpoint" ON) option(WITH_RADOSGW_AMQP_ENDPOINT "RADOS Gateway's pubsub support for AMQP push endpoint" ON)
option(WITH_RADOSGW_KAFKA_ENDPOINT "RADOS Gateway's pubsub support for Kafka push endpoint" ON) option(WITH_RADOSGW_KAFKA_ENDPOINT "RADOS Gateway's pubsub support for Kafka push endpoint" ON)
option(WITH_SYSTEM_RDKAFKA "build against system librdkafka instead of the bundled submodule" OFF)
option(WITH_RADOSGW_LUA_PACKAGES "RADOS Gateway's support for dynamically adding lua packagess" ON) option(WITH_RADOSGW_LUA_PACKAGES "RADOS Gateway's support for dynamically adding lua packagess" ON)
option(WITH_RADOSGW_DBSTORE "DBStore backend for RADOS Gateway" ON) option(WITH_RADOSGW_DBSTORE "DBStore backend for RADOS Gateway" ON)
option(WITH_RADOSGW_MOTR "CORTX-Motr backend for RADOS Gateway" OFF) option(WITH_RADOSGW_MOTR "CORTX-Motr backend for RADOS Gateway" OFF)

View File

@ -125,6 +125,7 @@
# not x86_64 # not x86_64
%bcond_with system_qat %bcond_with system_qat
%endif %endif
%bcond_with system_rdkafka
%if 0%{?fedora} || 0%{?suse_version} || 0%{?rhel} || 0%{?openEuler} %if 0%{?fedora} || 0%{?suse_version} || 0%{?rhel} || 0%{?openEuler}
%global weak_deps 1 %global weak_deps 1
%endif %endif
@ -338,7 +339,11 @@ BuildRequires: pkgconfig(yaml-cpp) >= 0.6
BuildRequires: pkgconfig(librabbitmq) BuildRequires: pkgconfig(librabbitmq)
%endif %endif
%if 0%{with kafka_endpoint} %if 0%{with kafka_endpoint}
BuildRequires: pkgconfig(rdkafka) >= 1.1.0 %if 0%{with system_rdkafka}
BuildRequires: librdkafka-devel >= 2.11
%else
BuildRequires: cyrus-sasl-devel
%endif
%endif %endif
%if 0%{with lua_packages} %if 0%{with lua_packages}
Requires: lua-devel Requires: lua-devel
@ -1163,6 +1168,9 @@ Requires: mailcap
%if 0%{?weak_deps} %if 0%{?weak_deps}
Recommends: gawk Recommends: gawk
%endif %endif
%if 0%{with kafka_endpoint} && !0%{with system_rdkafka}
Provides: bundled(librdkafka) = 2.12.1
%endif
%description radosgw %description radosgw
RADOS is a distributed object store used by the Ceph distributed RADOS is a distributed object store used by the Ceph distributed
storage system. This package provides a REST gateway to the storage system. This package provides a REST gateway to the
@ -1822,6 +1830,9 @@ cmake .. \
%if 0%{with system_pmdk} %if 0%{with system_pmdk}
-DWITH_SYSTEM_PMDK:BOOL=ON \ -DWITH_SYSTEM_PMDK:BOOL=ON \
%endif %endif
%if 0%{with system_rdkafka}
-DWITH_SYSTEM_RDKAFKA:BOOL=ON \
%endif
%if 0%{without jaeger} %if 0%{without jaeger}
-DWITH_JAEGER:BOOL=OFF \ -DWITH_JAEGER:BOOL=OFF \
%endif %endif

3
debian/control vendored
View File

@ -76,7 +76,8 @@ Build-Depends: automake,
libgrpc++-dev, libgrpc++-dev,
protobuf-compiler-grpc, protobuf-compiler-grpc,
libutf8proc-dev (>= 2.2.0), libutf8proc-dev (>= 2.2.0),
librdkafka-dev (>= 1.1.0), librdkafka-dev (>= 2.11) <pkg.ceph.system-rdkafka>,
libsasl2-dev <!pkg.ceph.system-rdkafka>,
libthrift-dev (>= 0.13.0), libthrift-dev (>= 0.13.0),
libyaml-cpp-dev (>= 0.6), libyaml-cpp-dev (>= 0.6),
libzstd-dev <pkg.ceph.check>, libzstd-dev <pkg.ceph.check>,

3
debian/rules vendored
View File

@ -18,6 +18,9 @@ endif
ifneq ($(filter pkg.ceph.crimson,$(DEB_BUILD_PROFILES)),) ifneq ($(filter pkg.ceph.crimson,$(DEB_BUILD_PROFILES)),)
extraopts += -DWITH_CRIMSON=ON extraopts += -DWITH_CRIMSON=ON
endif endif
ifneq ($(filter pkg.ceph.system-rdkafka,$(DEB_BUILD_PROFILES)),)
extraopts += -DWITH_SYSTEM_RDKAFKA=ON
endif
extraopts += -DWITH_JAEGER=ON extraopts += -DWITH_JAEGER=ON
extraopts += -DWITH_SYSTEM_JERASURE=ON extraopts += -DWITH_SYSTEM_JERASURE=ON

View File

@ -172,6 +172,10 @@ HTTP
Kafka Kafka
~~~~~ ~~~~~
Supported broker versions: Kafka 3.9 and Kafka 4.x. Both Zookeeper-based
(3.x) and KRaft (4.x) brokers are supported.
After recovering from a broker failure, a persistent topic will try After recovering from a broker failure, a persistent topic will try
to resend all notifications in batches. If the topic is configured on to resend all notifications in batches. If the topic is configured on
the broker with a segment size smaller than our default (1MB), sending the broker with a segment size smaller than our default (1MB), sending

View File

@ -10,9 +10,9 @@ overrides:
ceph: ceph:
extra_system_packages: extra_system_packages:
rpm: rpm:
- java - java-21-openjdk-headless
deb: deb:
- default-jre - openjdk-21-jre-headless
ceph: ceph:
conf: conf:
global: global:

View File

@ -0,0 +1,3 @@
tasks:
- kerberos:
client.0:

View File

@ -0,0 +1 @@
../.qa/

View File

@ -0,0 +1,4 @@
tasks:
- kafka:
client.0:
kafka_version: 3.9.2

View File

@ -0,0 +1,4 @@
tasks:
- kafka:
client.0:
kafka_version: "4.1"

View File

@ -0,0 +1,4 @@
tasks:
- kafka:
client.0:
kafka_version: "4.2"

View File

@ -0,0 +1,4 @@
tasks:
- kafka:
client.0:
kafka_version: "4.3"

View File

@ -0,0 +1,5 @@
tasks:
- notification-tests:
client.0:
extra_attr: ["kafka_test", "kafka_security_test"]
rgw_server: client.0

View File

@ -1,10 +0,0 @@
tasks:
- kerberos:
client.0:
- kafka:
client.0:
kafka_version: 3.9.2
- notification-tests:
client.0:
extra_attr: ["kafka_test", "kafka_security_test"]
rgw_server: client.0

View File

@ -3,8 +3,10 @@ Deploy and configure Kafka for Teuthology
""" """
import contextlib import contextlib
import logging import logging
import re
import time import time
import os import os
import urllib.request
from teuthology import misc as teuthology from teuthology import misc as teuthology
from teuthology import contextutil from teuthology import contextutil
@ -26,8 +28,38 @@ def get_kafka_version(config):
kafka_version = client_config.get('kafka_version') kafka_version = client_config.get('kafka_version')
return kafka_version return kafka_version
def is_kraft_mode(version_str):
"""Kafka 4.0+ removed Zookeeper and uses KRaft consensus instead."""
return int(version_str.split('.')[0]) >= 4
def resolve_kafka_version(version):
"""
Resolve a minor version like "4.3" to the current patch (e.g. "4.3.1")
by querying Apache's live mirror. A fully-qualified version (three
dotted components, e.g. "3.9.2") is returned unchanged, so it can
still be used as a literal patch pin.
Raises RuntimeError if the minor line is not on dlcdn.apache.org.
"""
if version.count('.') >= 2:
return version
with urllib.request.urlopen(
'https://dlcdn.apache.org/kafka/', timeout=30) as resp:
html = resp.read().decode()
pattern = re.compile(r'href="(' + re.escape(version) + r'\.\d+)/"')
matches = pattern.findall(html)
if not matches:
raise RuntimeError(
"Kafka {v}.x not found on dlcdn.apache.org "
"the minor line may have been dropped from Apache's "
"supported set.".format(v=version)
)
return max(matches, key=lambda v: tuple(int(p) for p in v.split('.')))
kafka_prefix = 'kafka_2.13-' kafka_prefix = 'kafka_2.13-'
KRAFT_CONTROLLER_PORT = 9097
def get_kafka_dir(ctx, config): def get_kafka_dir(ctx, config):
kafka_version = get_kafka_version(config) kafka_version = get_kafka_version(config)
current_version = kafka_prefix + kafka_version current_version = kafka_prefix + kafka_version
@ -55,6 +87,7 @@ def install_kafka(ctx, config):
(remote,) = ctx.cluster.only(client).remotes.keys() (remote,) = ctx.cluster.only(client).remotes.keys()
test_dir=teuthology.get_testdir(ctx) test_dir=teuthology.get_testdir(ctx)
current_version = get_kafka_version(config) current_version = get_kafka_version(config)
kraft = is_kraft_mode(current_version)
kafka_file = kafka_prefix + current_version + '.tgz' kafka_file = kafka_prefix + current_version + '.tgz'
@ -104,7 +137,25 @@ def install_kafka(ctx, config):
) )
client_kerberos = kerberos.get(client) if kerberos else None client_kerberos = kerberos.get(client) if kerberos else None
broker_conf(ctx, client, kafka_dir, client_kerberos) broker_conf(ctx, client, kafka_dir, client_kerberos, kraft)
if kraft:
# Kafka 4.x KRaft mode requires storage to be formatted before
# the broker starts. --standalone auto-bootstraps a single-node
# KRaft cluster.
uuid_result = remote.sh(
'cd {tdir}/bin && ./kafka-storage.sh random-uuid'.format(
tdir=kafka_dir)
).strip()
ctx.cluster.only(client).run(
args=[
'cd', '{tdir}/bin'.format(tdir=kafka_dir), run.Raw('&&'),
'./kafka-storage.sh', 'format',
'-t', uuid_result,
'-c', '{tdir}/config/server.properties'.format(tdir=kafka_dir),
'--standalone',
],
)
try: try:
yield yield
@ -126,20 +177,41 @@ def install_kafka(ctx, config):
) )
def broker_conf(ctx, client, kafka_dir, kerberos): def broker_conf(ctx, client, kafka_dir, kerberos, kraft):
"""writing a custom server.properties config file""" """writing a custom server.properties config file"""
(remote,) = ctx.cluster.only(client).remotes.keys() (remote,) = ctx.cluster.only(client).remotes.keys()
ip = remote.ip_address ip = remote.ip_address
if kraft:
identity = (
"node.id=1\n"
"process.roles=broker,controller\n"
)
controller_listener = ",CONTROLLER://0.0.0.0:{controller}".format(
controller=KRAFT_CONTROLLER_PORT)
controller_protocol = ",CONTROLLER:PLAINTEXT"
consensus_config = (
"controller.listener.names=CONTROLLER\n"
"controller.quorum.bootstrap.servers=localhost:{controller}\n"
).format(controller=KRAFT_CONTROLLER_PORT)
else:
identity = "broker.id=0\n"
controller_listener = ""
controller_protocol = ""
consensus_config = (
"zookeeper.connect=localhost:2181\n"
"zookeeper.connection.timeout.ms=18000\n"
)
conf = ( conf = (
"broker.id=0\n" identity +
"listeners=PLAINTEXT://0.0.0.0:{plaintext},SSL://0.0.0.0:{ssl}," "listeners=PLAINTEXT://0.0.0.0:{plaintext},SSL://0.0.0.0:{ssl},"
"SASL_SSL://0.0.0.0:{sasl_ssl},SASL_PLAINTEXT://0.0.0.0:{sasl_plaintext}," "SASL_SSL://0.0.0.0:{sasl_ssl},SASL_PLAINTEXT://0.0.0.0:{sasl_plaintext},"
"MTLS://0.0.0.0:{mtls}\n" "MTLS://0.0.0.0:{mtls}" + controller_listener + "\n"
"advertised.listeners=PLAINTEXT://{ip}:{plaintext},SSL://{ip}:{ssl}," "advertised.listeners=PLAINTEXT://{ip}:{plaintext},SSL://{ip}:{ssl},"
"SASL_SSL://{ip}:{sasl_ssl},SASL_PLAINTEXT://{ip}:{sasl_plaintext}," "SASL_SSL://{ip}:{sasl_ssl},SASL_PLAINTEXT://{ip}:{sasl_plaintext},"
"MTLS://{ip}:{mtls}\n" "MTLS://{ip}:{mtls}\n"
"listener.security.protocol.map=PLAINTEXT:PLAINTEXT,SSL:SSL," "listener.security.protocol.map=PLAINTEXT:PLAINTEXT,SSL:SSL,"
"SASL_SSL:SASL_SSL,SASL_PLAINTEXT:SASL_PLAINTEXT,MTLS:SSL\n" "SASL_SSL:SASL_SSL,SASL_PLAINTEXT:SASL_PLAINTEXT,MTLS:SSL"
+ controller_protocol + "\n"
"inter.broker.listener.name=PLAINTEXT\n" "inter.broker.listener.name=PLAINTEXT\n"
"log.dirs={tdir}/data/kafka-logs\n" "log.dirs={tdir}/data/kafka-logs\n"
"num.network.threads=3\n" "num.network.threads=3\n"
@ -155,8 +227,7 @@ def broker_conf(ctx, client, kafka_dir, kerberos):
"log.retention.hours=168\n" "log.retention.hours=168\n"
"log.segment.bytes=1073741824\n" "log.segment.bytes=1073741824\n"
"log.retention.check.interval.ms=300000\n" "log.retention.check.interval.ms=300000\n"
"zookeeper.connect=localhost:2181\n" + consensus_config +
"zookeeper.connection.timeout.ms=18000\n"
"group.initial.rebalance.delay.ms=0\n" "group.initial.rebalance.delay.ms=0\n"
# SSL configuration # SSL configuration
"ssl.keystore.location={tdir}/server.keystore.jks\n" "ssl.keystore.location={tdir}/server.keystore.jks\n"
@ -237,22 +308,24 @@ def broker_conf(ctx, client, kafka_dir, kerberos):
@contextlib.contextmanager @contextlib.contextmanager
def run_kafka(ctx,config): def run_kafka(ctx,config):
""" """
This includes two parts: Starts the Kafka broker. In Zookeeper mode (3.x) also starts Zookeeper
1. Starting Zookeeper service first; KRaft mode (4.x) embeds the controller in the broker process.
2. Starting Kafka service
""" """
assert isinstance(config, dict) assert isinstance(config, dict)
log.info('Bringing up Zookeeper and Kafka services...') kraft = is_kraft_mode(get_kafka_version(config))
log.info('Bringing up Kafka%s services...',
'' if kraft else ' and Zookeeper')
for (client,_) in config.items(): for (client,_) in config.items():
(remote,) = ctx.cluster.only(client).remotes.keys() (remote,) = ctx.cluster.only(client).remotes.keys()
ctx.cluster.only(client).run( if not kraft:
args=['cd', '{tdir}/bin'.format(tdir=get_kafka_dir(ctx, config)), run.Raw('&&'), ctx.cluster.only(client).run(
'./zookeeper-server-start.sh', args=['cd', '{tdir}/bin'.format(tdir=get_kafka_dir(ctx, config)), run.Raw('&&'),
'{tir}/config/zookeeper.properties'.format(tir=get_kafka_dir(ctx, config)), './zookeeper-server-start.sh',
run.Raw('&'), 'exit' '{tir}/config/zookeeper.properties'.format(tir=get_kafka_dir(ctx, config)),
], run.Raw('&'), 'exit'
) ],
)
ctx.cluster.only(client).run( ctx.cluster.only(client).run(
args=['cd', '{tdir}/bin'.format(tdir=get_kafka_dir(ctx, config)), run.Raw('&&'), args=['cd', '{tdir}/bin'.format(tdir=get_kafka_dir(ctx, config)), run.Raw('&&'),
@ -265,28 +338,30 @@ def run_kafka(ctx,config):
try: try:
yield yield
finally: finally:
log.info('Stopping Zookeeper and Kafka Services...') log.info('Stopping Kafka%s services...',
'' if kraft else ' and Zookeeper')
for (client, _) in config.items(): for (client, _) in config.items():
(remote,) = ctx.cluster.only(client).remotes.keys() (remote,) = ctx.cluster.only(client).remotes.keys()
ctx.cluster.only(client).run( ctx.cluster.only(client).run(
args=['cd', '{tdir}/bin'.format(tdir=get_kafka_dir(ctx, config)), run.Raw('&&'), args=['cd', '{tdir}/bin'.format(tdir=get_kafka_dir(ctx, config)), run.Raw('&&'),
'./kafka-server-stop.sh', './kafka-server-stop.sh',
'{tir}/config/server.properties'.format(tir=get_kafka_dir(ctx, config)), '{tir}/config/server.properties'.format(tir=get_kafka_dir(ctx, config)),
], ],
) )
time.sleep(5) time.sleep(5)
ctx.cluster.only(client).run( if not kraft:
args=['cd', '{tdir}/bin'.format(tdir=get_kafka_dir(ctx, config)), run.Raw('&&'), ctx.cluster.only(client).run(
'./zookeeper-server-stop.sh', args=['cd', '{tdir}/bin'.format(tdir=get_kafka_dir(ctx, config)), run.Raw('&&'),
'{tir}/config/zookeeper.properties'.format(tir=get_kafka_dir(ctx, config)), './zookeeper-server-stop.sh',
], '{tir}/config/zookeeper.properties'.format(tir=get_kafka_dir(ctx, config)),
) ],
)
time.sleep(5) time.sleep(5)
ctx.cluster.only(client).run(args=['killall', '-9', 'java']) ctx.cluster.only(client).run(args=['killall', '-9', 'java'])
@ -342,7 +417,12 @@ def task(ctx,config):
tasks: tasks:
- kafka: - kafka:
client.0: client.0:
kafka_version: 2.6.0 kafka_version: "4.3"
A minor version like "4.3" is resolved to the current patch at test
time via dlcdn.apache.org. A full version like "3.9.2" is used as-is.
Kafka 4.x is started in KRaft mode automatically; 3.x is started with
a colocated Zookeeper. Pick whichever version your suite needs.
""" """
assert config is None or isinstance(config, list) \ assert config is None or isinstance(config, list) \
or isinstance(config, dict), \ or isinstance(config, dict), \
@ -355,6 +435,14 @@ def task(ctx,config):
if isinstance(config, list): if isinstance(config, list):
config = dict.fromkeys(config) config = dict.fromkeys(config)
for client_config in config.values():
if client_config and 'kafka_version' in client_config:
raw = client_config['kafka_version']
resolved = resolve_kafka_version(raw)
if resolved != raw:
log.info("Kafka version resolved: %s -> %s", raw, resolved)
client_config['kafka_version'] = resolved
ctx.kafka_dir = get_kafka_dir(ctx, config) ctx.kafka_dir = get_kafka_dir(ctx, config)
log.debug('Kafka config is %s', config) log.debug('Kafka config is %s', config)

View File

@ -12,10 +12,21 @@ from teuthology.orchestra import run
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
# This task currently supports Kafka 3.x (Zookeeper) only. KRaft (4.x)
# requires a different multi-controller bootstrap for the two-broker HA
# topology this task spins up. The suite yaml at
# qa/suites/rgw/notifications/tasks/kafka_failover/test_kafka.yaml pins
# kafka_version: 3.9.2; the runtime check below fails fast if anyone
# changes that to a 4.x version without updating the task.
def get_kafka_version(config): def get_kafka_version(config):
for client, client_config in config.items(): for client, client_config in config.items():
if 'kafka_version' in client_config: if 'kafka_version' in client_config:
kafka_version = client_config.get('kafka_version') kafka_version = client_config.get('kafka_version')
if int(kafka_version.split('.')[0]) >= 4:
raise RuntimeError(
"kafka_failover task does not yet support Kafka 4.x (KRaft); "
"pin kafka_version to a 3.x release or extend this task."
)
return kafka_version return kafka_version
kafka_prefix = 'kafka_2.13-' kafka_prefix = 'kafka_2.13-'

1
src/librdkafka Submodule

@ -0,0 +1 @@
Subproject commit e1db7eaa517f0a6438bc846a9c49ede73b9ea211

View File

@ -472,7 +472,35 @@ if(WITH_RADOSGW_AMQP_ENDPOINT)
find_package(RabbitMQ REQUIRED) find_package(RabbitMQ REQUIRED)
endif() endif()
if(WITH_RADOSGW_KAFKA_ENDPOINT) if(WITH_RADOSGW_KAFKA_ENDPOINT)
find_package(RDKafka 1.1.0 REQUIRED) set(rdkafka_min_version 2.11)
if(WITH_SYSTEM_RDKAFKA)
find_package(RDKafka ${rdkafka_min_version} REQUIRED)
elseif(EXISTS "${CMAKE_SOURCE_DIR}/src/librdkafka/CMakeLists.txt")
message(STATUS "Building librdkafka as submodule")
set(WITH_SSL ON)
set(WITH_SASL ON)
set(RDKAFKA_BUILD_EXAMPLES OFF)
set(RDKAFKA_BUILD_TESTS OFF)
set(RDKAFKA_BUILD_STATIC ON)
add_subdirectory(${CMAKE_SOURCE_DIR}/src/librdkafka
${CMAKE_BINARY_DIR}/src/librdkafka
EXCLUDE_FROM_ALL)
target_compile_options(rdkafka PRIVATE -w)
if(NOT TARGET RDKafka::RDKafka)
add_library(RDKafka::RDKafka ALIAS rdkafka)
endif()
# create a compatibility symlink, so that rgw_a can include <librdkafka/rdkafka.h>
# both for the submodule and system librdkafka
set(rdkafka_compat_dir "${CMAKE_BINARY_DIR}/src/librdkafka-compat")
file(MAKE_DIRECTORY "${rdkafka_compat_dir}")
execute_process(COMMAND ${CMAKE_COMMAND} -E create_symlink
"${CMAKE_SOURCE_DIR}/src/librdkafka/src"
"${rdkafka_compat_dir}/librdkafka")
target_include_directories(rdkafka PUBLIC "$<BUILD_INTERFACE:${rdkafka_compat_dir}>")
else()
message(STATUS "src/librdkafka submodule not present; falling back to system librdkafka")
find_package(RDKafka ${rdkafka_min_version} REQUIRED)
endif()
endif() endif()
target_link_libraries(rgw_a target_link_libraries(rgw_a

View File

@ -85,7 +85,13 @@ if(WITH_RADOSGW)
list(APPEND rgw_libs amqp_mock) list(APPEND rgw_libs amqp_mock)
endif() endif()
if(WITH_RADOSGW_KAFKA_ENDPOINT) if(WITH_RADOSGW_KAFKA_ENDPOINT)
list(APPEND rgw_libs kafka_stub) # kafka_stub provides fake rd_kafka_* symbols for tests; needed
# whenever we link against a dynamic system librdkafka (either
# opted-in via WITH_SYSTEM_RDKAFKA, or as a fallback when the
# bundled submodule is not checked out).
if(WITH_SYSTEM_RDKAFKA OR NOT EXISTS "${CMAKE_SOURCE_DIR}/src/librdkafka/CMakeLists.txt")
list(APPEND rgw_libs kafka_stub)
endif()
endif() endif()
add_subdirectory(rgw) add_subdirectory(rgw)
endif(WITH_RADOSGW) endif(WITH_RADOSGW)

View File

@ -21,10 +21,14 @@ if(WITH_RADOSGW_AMQP_ENDPOINT)
endif() endif()
if(WITH_RADOSGW_KAFKA_ENDPOINT) if(WITH_RADOSGW_KAFKA_ENDPOINT)
# kafka stub library # kafka stub library built whenever RGW links against a dynamic
set(kafka_stub_src # system librdkafka (WITH_SYSTEM_RDKAFKA=ON, or the fallback path
kafka_stub.cc) # when the bundled submodule is not checked out).
add_library(kafka_stub STATIC ${kafka_stub_src}) if(WITH_SYSTEM_RDKAFKA OR NOT EXISTS "${CMAKE_SOURCE_DIR}/src/librdkafka/CMakeLists.txt")
set(kafka_stub_src
kafka_stub.cc)
add_library(kafka_stub STATIC ${kafka_stub_src})
endif()
endif() endif()
if(WITH_RADOSGW_LUA_PACKAGES) if(WITH_RADOSGW_LUA_PACKAGES)

View File

@ -59,6 +59,33 @@ After running `vstart.sh`, Zookeeper, and Kafka services you're ready to run the
BNTESTS_CONF=bntests.conf python -m pytest -s /path/to/ceph/src/test/rgw/bucket_notification/test_bn.py -v -m 'kafka_test' BNTESTS_CONF=bntests.conf python -m pytest -s /path/to/ceph/src/test/rgw/bucket_notification/test_bn.py -v -m 'kafka_test'
------------------------------
Kafka 4.x setup (KRaft mode)
------------------------------
Kafka 4.0 removed Zookeeper. The broker uses its own KRaft consensus,
so there is no separate Zookeeper process to start.
First-time setup (per Kafka installation), format the storage::
KAFKA_CLUSTER_ID=$(bin/kafka-storage.sh random-uuid)
bin/kafka-storage.sh format -t $KAFKA_CLUSTER_ID -c config/server.properties --standalone
The ``--standalone`` flag is required for single-node test setups.
Then start the broker::
bin/kafka-server-start.sh config/server.properties
For security tests, the same ``server.properties`` edits in the section
below apply. The KRaft controller listener (e.g. ``CONTROLLER://:9097``)
must remain in the ``listeners`` line on a port outside the 9092-9096
range, since those are reserved by the security listeners. Do not set
``controller.quorum.voters``; the ``--standalone`` flag passed to
``kafka-storage.sh format`` writes the voter set into the bootstrap
metadata automatically, and Kafka 4.1.1+ rejects the combination of
``--standalone`` with an explicit ``controller.quorum.voters``.
-------------------- --------------------
Kafka Security Tests Kafka Security Tests
-------------------- --------------------