Files
upstream-seaweedfs/weed/mq/kafka/protocol/group_introspection.go
upstream-mirror 89f5be005d
Some checks failed
go: build dev binaries / cleanup (push) Successful in 4s
Codespell / Check for spelling errors (push) Failing after 3s
docker: build dev containers / build-rust-binaries (amd64, x86_64-unknown-linux-musl) (push) Failing after 3s
docker: build dev containers / build-dev-containers (push) Has been skipped
docker: build dev containers / build-rust-binaries (arm64, true, aarch64-unknown-linux-musl) (push) Failing after 3s
End to End / FUSE Mount (push) Failing after 2s
EC Integration Tests / EC Integration Tests (push) Failing after 30s
FUSE DLM Integration Tests / FUSE DLM Integration Tests (push) Failing after 3s
FUSE Volume Server Failover Tests / FUSE Volume Server Failover (push) Failing after 3s
FUSE Integration Tests / FUSE Integration Testing (push) Failing after 2s
FUSE P2P Peer Chunk Sharing Integration Tests / FUSE P2P Peer Chunk Sharing (push) Failing after 3s
go: build binary / Go Vet (push) Failing after 2s
go: build binary / Go Vet 32-bit (push) Failing after 3s
go: build binary / Build (push) Failing after 4s
go: build binary / Build cross-platform (freebsd/amd64) (push) Failing after 3s
go: build binary / Build cross-platform (windows/amd64) (push) Failing after 3s
go: build binary / Build cross-platform (darwin/arm64) (push) Failing after 3s
go: build binary / Build cross-platform (windows/arm64) (push) Failing after 3s
go: build binary / Test (push) Failing after 2s
go: build binary / Test 32-bit (push) Failing after 3s
helm: lint and test charts / lint-test (push) Failing after 3s
Java Client Integration Tests / Java Integration Tests (11) (push) Failing after 3s
Java Client Integration Tests / Java Integration Tests (17) (push) Failing after 3s
go: build dev binaries / build_dev_linux_windows (amd64, linux) (push) Failing after 2s
go: build dev binaries / build_dev_linux_windows (amd64, windows) (push) Failing after 3s
go: build dev binaries / build_dev_darwin (amd64, darwin) (push) Has been skipped
go: build dev binaries / build_dev_darwin (arm64, darwin) (push) Has been skipped
Java Client Unit Tests / Java Unit Tests (11) (push) Failing after 3s
Java Client Unit Tests / Java Unit Tests (17) (push) Failing after 2s
Java Client Unit Tests / Java Unit Tests (21) (push) Failing after 3s
Java Client Unit Tests / Java Unit Tests (8) (push) Failing after 3s
Kafka Quick Test (Load Test with Schema Registry) / Kafka Client Load Test (Quick) (push) Failing after 3s
Kafka Gateway Tests / Kafka Unit Tests (unit-tests-1) (push) Failing after 11s
Kafka Gateway Tests / Kafka Integration Tests (Critical) (integration-1) (push) Failing after 3s
Kafka Gateway Tests / Kafka End-to-End Tests (with SMQ) (e2e-1) (push) Failing after 3s
Kafka Gateway Tests / Kafka Consumer Group Tests (Highly Isolated) (consumer-group-1) (push) Failing after 2s
Kafka Gateway Tests / Kafka Client Compatibility (with SMQ) (client-compat-1) (push) Failing after 3s
Kafka Gateway Tests / Kafka SMQ Integration Tests (Full Stack) (smq-integration-1) (push) Failing after 3s
Kafka Gateway Tests / Kafka Protocol Tests (Isolated) (protocol-1) (push) Failing after 3s
KMS Tests / KMS Provider Integration Tests (push) Failing after 2s
KMS Tests / S3 KMS End-to-End Tests (push) Failing after 3s
Master Cold Start Tests / Master Cold Start Tests (push) Failing after 4s
Metadata Subscribe Integration Tests / Metadata Subscribe Integration Tests (push) Failing after 3s
Multi-Master Tests / Multi-Master Failover Tests (push) Failing after 3s
Performance / CPU and Heap Profile (push) Failing after 2s
Performance / S3 Read/Write Benchmark (rust volume) (push) Failing after 3s
pjdfstest POSIX Compliance / pjdfstest (push) Failing after 2s
Plugin Worker Integration Tests / Plugin Worker: erasure_coding (push) Failing after 3s
Plugin Worker Integration Tests / Plugin Worker: vacuum (push) Failing after 3s
Rust Volume Server Tests / Rust Integration Tests (push) Failing after 3s
Rust Volume Server Tests / Go Tests with Rust Volume (grpc - Shard 1) (push) Failing after 2s
Rust Volume Server Tests / Go Tests with Rust Volume (http - Shard 1) (push) Failing after 3s
Rust Volume Server Tests / Go Tests with Rust Volume (grpc - Shard 2) (push) Failing after 3s
Rust Volume Server Tests / Go Tests with Rust Volume (http - Shard 2) (push) Failing after 3s
Rust Volume Server Tests / Go Tests with Rust Volume (grpc - Shard 3) (push) Failing after 3s
S3 IAM Integration Tests / S3 IAM Distributed Tests (push) Failing after 2s
S3 IAM Integration Tests / S3 IAM Performance Tests (push) Failing after 3s
Performance / Throughput Benchmark (go volume) (push) Failing after 3s
Performance / Throughput Benchmark (rust volume) (push) Failing after 2s
Performance / S3 Read/Write Benchmark (go volume) (push) Failing after 3s
Plugin Worker Integration Tests / Plugin Worker: volume_balance (push) Failing after 3s
PostgreSQL Gateway Tests / PostgreSQL Basic Tests (push) Failing after 3s
Rust Volume Server Tests / Rust Unit Tests (push) Failing after 2s
Rust Volume Server Tests / Go Tests with Rust Volume (http - Shard 3) (push) Failing after 3s
Rust Plugin Worker Tests / Rust Plugin Worker Build and Unit Tests (push) Failing after 3s
rust: build dev volume server binaries / cleanup (push) Successful in 3s
S3 ETag and ACL Tests / S3 ETag + ACL Integration Tests (push) Failing after 3s
S3 IAM Integration Tests / IAM Unit Tests (push) Failing after 3s
S3 IAM Integration Tests / S3 IAM Integration Tests (advanced) (push) Failing after 3s
S3 IAM Integration Tests / S3 IAM Integration Tests (basic) (push) Failing after 2s
S3 IAM Integration Tests / S3 IAM Integration Tests (group) (push) Failing after 3s
S3 IAM Integration Tests / S3 IAM Integration Tests (policy-enforcement) (push) Failing after 2s
S3 IAM Integration Tests / S3 IAM Integration Tests (sts) (push) Failing after 4s
S3 Keycloak Integration Tests / S3 Keycloak Integration Tests (push) Failing after 3s
S3 PyArrow Parquet Tests / PyArrow Parquet Tests (Python 3.11) (push) Failing after 3s
S3 PyArrow Parquet Tests / Go Unit Tests (Implicit Directory) (push) Has been cancelled
S3 PyArrow Parquet Tests / PyArrow Parquet Tests (Python 3.12) (push) Has been cancelled
rust: build dev volume server binaries / build-rust-volume-dev-darwin (darwin-amd64, x86_64-apple-darwin) (push) Has been cancelled
rust: build dev volume server binaries / build-rust-volume-dev-darwin (darwin-arm64, aarch64-apple-darwin) (push) Has been cancelled
rust: build dev volume server binaries / build-rust-volume-dev-linux (linux-amd64, x86_64-unknown-linux-gnu) (push) Has been cancelled
S3 PyArrow Parquet Tests / PyArrow Parquet Tests (Python 3.9) (push) Has been cancelled
S3 Proxy Signature Tests / S3 Proxy Signature Verification Tests (push) Has been cancelled
S3 SDK V2 Route Disambiguation Tests / S3 SDK V2 Routing Tests (push) Has been cancelled
S3 SSE Tests / S3 SSE Integration Tests (quick) (push) Has been cancelled
S3 SSE Tests / S3 SSE Compatibility Test (push) Has been cancelled
S3 SSE Tests / S3 SSE Metadata Persistence Test (push) Has been cancelled
S3 SSE Tests / S3 SSE Copy Operations Test (push) Has been cancelled
S3 SSE Tests / S3 SSE Multipart Upload Test (push) Has been cancelled
S3 SSE Tests / S3 SSE Performance Test (push) Has been cancelled
S3 SSE Tests / S3 Volume Encryption Test (push) Has been cancelled
S3 SSE Tests / S3 SSE Integration Tests (comprehensive) (push) Has been cancelled
Spark Integration Tests / Spark Integration Tests (push) Failing after 2s
Telemetry Integration Tests / telemetry-integration-test (push) Failing after 3s
Volume Server Integration Tests / Volume Server Integration Tests (grpc - Shard 1) (push) Failing after 3s
Volume Server Integration Tests / Volume Server Integration Tests (http - Shard 1) (push) Failing after 3s
Ceph S3 tests / Basic S3 tests (KV store) (push) Has been cancelled
Ceph S3 tests / S3 Versioning & Object Lock tests (push) Has been cancelled
S3 Policy Integration Tests / S3 Policy Unit Tests (push) Failing after 3s
Ceph S3 tests / S3 CORS tests (push) Has been cancelled
Ceph S3 tests / SeaweedFS Custom S3 Copy tests (push) Has been cancelled
Ceph S3 tests / Basic S3 tests (SQL store) (push) Has been cancelled
test s3 over https using aws-cli / awscli-tests (push) Has been cancelled
TLS Rotation Integration Tests / TLS Rotation Integration Tests (push) Has been cancelled
Vacuum Integration Tests / Vacuum Integration Tests (push) Has been cancelled
S3 Policy Integration Tests / S3 Policy Variables Integration Tests (push) Failing after 3s
S3 Policy Integration Tests / S3 Policy Enforcement Integration Tests (advanced-policy) (push) Failing after 3s
S3 Policy Integration Tests / S3 Policy Enforcement Integration Tests (basic-policy) (push) Failing after 2s
S3 Policy Integration Tests / S3 Policy Enforcement Integration Tests (contextual-policy) (push) Failing after 4s
S3 Policy Integration Tests / Trusted Proxy Detection Tests (push) Failing after 2s
S3 Policy Integration Tests / S3 Policy Shell Integration Tests (push) Failing after 3s
Samba on FUSE Integration / samba-integration (push) Failing after 2s
SFTP Integration Tests / SFTP Integration Testing (push) Failing after 3s
terraform: validate and test modules / fmt, validate, plan-level tests (push) Failing after 3s
terraform: validate and test modules / local cluster smoke test (real weed) (push) Failing after 2s
Volume Server Integration Tests / Volume Server Integration Tests (grpc - Shard 3) (push) Failing after 3s
Volume Server Integration Tests / Volume Server Integration Tests (grpc - Shard 2) (push) Failing after 3s
Volume Server Integration Tests / Volume Server Integration Tests (http - Shard 2) (push) Failing after 2s
Volume Server Integration Tests / Volume Server Integration Tests (http - Shard 3) (push) Failing after 5s
mount: windows conformance / WinFsp conformance (push) Has been cancelled
mount: windows / Mount on Windows (push) Has been cancelled
snapshot of https://github.com/seaweedfs/seaweedfs.git (master, 2026-09-16; blobs > 131072B omitted)
2026-09-16 03:23:42 +05:00

448 lines
12 KiB
Go

package protocol
import (
"encoding/binary"
"fmt"
)
// handleDescribeGroups handles DescribeGroups API (key 15)
func (h *Handler) handleDescribeGroups(correlationID uint32, apiVersion uint16, requestBody []byte) ([]byte, error) {
// Parse request
request, err := h.parseDescribeGroupsRequest(requestBody, apiVersion)
if err != nil {
return nil, fmt.Errorf("parse DescribeGroups request: %w", err)
}
// Build response
response := DescribeGroupsResponse{
ThrottleTimeMs: 0,
Groups: make([]DescribeGroupsGroup, 0, len(request.GroupIDs)),
}
// Get group information for each requested group
for _, groupID := range request.GroupIDs {
group := h.describeGroup(groupID)
response.Groups = append(response.Groups, group)
}
return h.buildDescribeGroupsResponse(response, correlationID, apiVersion), nil
}
// handleListGroups handles ListGroups API (key 16)
func (h *Handler) handleListGroups(correlationID uint32, apiVersion uint16, requestBody []byte) ([]byte, error) {
// Parse request (ListGroups has minimal request structure)
request, err := h.parseListGroupsRequest(requestBody, apiVersion)
if err != nil {
return nil, fmt.Errorf("parse ListGroups request: %w", err)
}
// Build response
response := ListGroupsResponse{
ThrottleTimeMs: 0,
ErrorCode: 0,
Groups: h.listAllGroups(request.StatesFilter),
}
return h.buildListGroupsResponse(response, correlationID, apiVersion), nil
}
// describeGroup gets detailed information about a specific group
func (h *Handler) describeGroup(groupID string) DescribeGroupsGroup {
// Get group information from coordinator
if h.groupCoordinator == nil {
return DescribeGroupsGroup{
ErrorCode: 15, // GROUP_COORDINATOR_NOT_AVAILABLE
GroupID: groupID,
State: "Dead",
}
}
group := h.groupCoordinator.GetGroup(groupID)
if group == nil {
return DescribeGroupsGroup{
ErrorCode: 25, // UNKNOWN_GROUP_ID
GroupID: groupID,
State: "Dead",
ProtocolType: "",
Protocol: "",
Members: []DescribeGroupsMember{},
}
}
// Convert group to response format
members := make([]DescribeGroupsMember, 0, len(group.Members))
for memberID, member := range group.Members {
// Convert assignment to bytes (simplified)
var assignmentBytes []byte
if len(member.Assignment) > 0 {
// In a real implementation, this would serialize the assignment properly
assignmentBytes = []byte(fmt.Sprintf("assignment:%d", len(member.Assignment)))
}
members = append(members, DescribeGroupsMember{
MemberID: memberID,
GroupInstanceID: member.GroupInstanceID, // Now supports static membership
ClientID: member.ClientID,
ClientHost: member.ClientHost,
MemberMetadata: member.Metadata,
MemberAssignment: assignmentBytes,
})
}
// Convert group state to string
var stateStr string
switch group.State {
case 0: // Assuming 0 is Empty
stateStr = "Empty"
case 1: // Assuming 1 is PreparingRebalance
stateStr = "PreparingRebalance"
case 2: // Assuming 2 is CompletingRebalance
stateStr = "CompletingRebalance"
case 3: // Assuming 3 is Stable
stateStr = "Stable"
default:
stateStr = "Dead"
}
return DescribeGroupsGroup{
ErrorCode: 0,
GroupID: groupID,
State: stateStr,
ProtocolType: "consumer", // Default protocol type
Protocol: group.Protocol,
Members: members,
AuthorizedOps: []int32{}, // Empty for now
}
}
// listAllGroups gets a list of all consumer groups
func (h *Handler) listAllGroups(statesFilter []string) []ListGroupsGroup {
if h.groupCoordinator == nil {
return []ListGroupsGroup{}
}
allGroupIDs := h.groupCoordinator.ListGroups()
groups := make([]ListGroupsGroup, 0, len(allGroupIDs))
for _, groupID := range allGroupIDs {
// Get the full group details
group := h.groupCoordinator.GetGroup(groupID)
if group == nil {
continue
}
// Convert group state to string
var stateStr string
switch group.State {
case 0:
stateStr = "Empty"
case 1:
stateStr = "PreparingRebalance"
case 2:
stateStr = "CompletingRebalance"
case 3:
stateStr = "Stable"
default:
stateStr = "Dead"
}
// Apply state filter if provided
if len(statesFilter) > 0 {
matchesFilter := false
for _, state := range statesFilter {
if stateStr == state {
matchesFilter = true
break
}
}
if !matchesFilter {
continue
}
}
groups = append(groups, ListGroupsGroup{
GroupID: group.ID,
ProtocolType: "consumer", // Default protocol type
GroupState: stateStr,
})
}
return groups
}
// Request/Response structures
type DescribeGroupsRequest struct {
GroupIDs []string
IncludeAuthorizedOps bool
}
type DescribeGroupsResponse struct {
ThrottleTimeMs int32
Groups []DescribeGroupsGroup
}
type DescribeGroupsGroup struct {
ErrorCode int16
GroupID string
State string
ProtocolType string
Protocol string
Members []DescribeGroupsMember
AuthorizedOps []int32
}
type DescribeGroupsMember struct {
MemberID string
GroupInstanceID *string
ClientID string
ClientHost string
MemberMetadata []byte
MemberAssignment []byte
}
type ListGroupsRequest struct {
StatesFilter []string
}
type ListGroupsResponse struct {
ThrottleTimeMs int32
ErrorCode int16
Groups []ListGroupsGroup
}
type ListGroupsGroup struct {
GroupID string
ProtocolType string
GroupState string
}
// Parsing functions
func (h *Handler) parseDescribeGroupsRequest(data []byte, apiVersion uint16) (*DescribeGroupsRequest, error) {
offset := 0
request := &DescribeGroupsRequest{}
// Skip client_id if present (depends on version)
if len(data) < 4 {
return nil, fmt.Errorf("request too short")
}
// Group IDs array
groupCount := binary.BigEndian.Uint32(data[offset : offset+4])
offset += 4
request.GroupIDs = make([]string, groupCount)
for i := uint32(0); i < groupCount; i++ {
if offset+2 > len(data) {
return nil, fmt.Errorf("invalid group ID at index %d", i)
}
groupIDLen := binary.BigEndian.Uint16(data[offset : offset+2])
offset += 2
if offset+int(groupIDLen) > len(data) {
return nil, fmt.Errorf("group ID too long at index %d", i)
}
request.GroupIDs[i] = string(data[offset : offset+int(groupIDLen)])
offset += int(groupIDLen)
}
// Include authorized operations (v3+)
if apiVersion >= 3 && offset < len(data) {
request.IncludeAuthorizedOps = data[offset] != 0
}
return request, nil
}
func (h *Handler) parseListGroupsRequest(data []byte, apiVersion uint16) (*ListGroupsRequest, error) {
request := &ListGroupsRequest{}
// ListGroups v4+ includes states filter
if apiVersion >= 4 && len(data) >= 4 {
offset := 0
statesCount := binary.BigEndian.Uint32(data[offset : offset+4])
offset += 4
if statesCount > 0 {
request.StatesFilter = make([]string, statesCount)
for i := uint32(0); i < statesCount; i++ {
if offset+2 > len(data) {
break
}
stateLen := binary.BigEndian.Uint16(data[offset : offset+2])
offset += 2
if offset+int(stateLen) > len(data) {
break
}
request.StatesFilter[i] = string(data[offset : offset+int(stateLen)])
offset += int(stateLen)
}
}
}
return request, nil
}
// Response building functions
func (h *Handler) buildDescribeGroupsResponse(response DescribeGroupsResponse, correlationID uint32, apiVersion uint16) []byte {
buf := make([]byte, 0, 1024)
// Correlation ID
correlationIDBytes := make([]byte, 4)
binary.BigEndian.PutUint32(correlationIDBytes, correlationID)
buf = append(buf, correlationIDBytes...)
// Throttle time (v1+)
if apiVersion >= 1 {
throttleBytes := make([]byte, 4)
binary.BigEndian.PutUint32(throttleBytes, uint32(response.ThrottleTimeMs))
buf = append(buf, throttleBytes...)
}
// Groups array
groupCountBytes := make([]byte, 4)
binary.BigEndian.PutUint32(groupCountBytes, uint32(len(response.Groups)))
buf = append(buf, groupCountBytes...)
for _, group := range response.Groups {
// Error code
buf = append(buf, byte(group.ErrorCode>>8), byte(group.ErrorCode))
// Group ID
groupIDLen := uint16(len(group.GroupID))
buf = append(buf, byte(groupIDLen>>8), byte(groupIDLen))
buf = append(buf, []byte(group.GroupID)...)
// State
stateLen := uint16(len(group.State))
buf = append(buf, byte(stateLen>>8), byte(stateLen))
buf = append(buf, []byte(group.State)...)
// Protocol type
protocolTypeLen := uint16(len(group.ProtocolType))
buf = append(buf, byte(protocolTypeLen>>8), byte(protocolTypeLen))
buf = append(buf, []byte(group.ProtocolType)...)
// Protocol
protocolLen := uint16(len(group.Protocol))
buf = append(buf, byte(protocolLen>>8), byte(protocolLen))
buf = append(buf, []byte(group.Protocol)...)
// Members array
memberCountBytes := make([]byte, 4)
binary.BigEndian.PutUint32(memberCountBytes, uint32(len(group.Members)))
buf = append(buf, memberCountBytes...)
for _, member := range group.Members {
// Member ID
memberIDLen := uint16(len(member.MemberID))
buf = append(buf, byte(memberIDLen>>8), byte(memberIDLen))
buf = append(buf, []byte(member.MemberID)...)
// Group instance ID (v4+, nullable)
if apiVersion >= 4 {
if member.GroupInstanceID != nil {
instanceIDLen := uint16(len(*member.GroupInstanceID))
buf = append(buf, byte(instanceIDLen>>8), byte(instanceIDLen))
buf = append(buf, []byte(*member.GroupInstanceID)...)
} else {
buf = append(buf, 0xFF, 0xFF) // null
}
}
// Client ID
clientIDLen := uint16(len(member.ClientID))
buf = append(buf, byte(clientIDLen>>8), byte(clientIDLen))
buf = append(buf, []byte(member.ClientID)...)
// Client host
clientHostLen := uint16(len(member.ClientHost))
buf = append(buf, byte(clientHostLen>>8), byte(clientHostLen))
buf = append(buf, []byte(member.ClientHost)...)
// Member metadata
metadataLen := uint32(len(member.MemberMetadata))
metadataLenBytes := make([]byte, 4)
binary.BigEndian.PutUint32(metadataLenBytes, metadataLen)
buf = append(buf, metadataLenBytes...)
buf = append(buf, member.MemberMetadata...)
// Member assignment
assignmentLen := uint32(len(member.MemberAssignment))
assignmentLenBytes := make([]byte, 4)
binary.BigEndian.PutUint32(assignmentLenBytes, assignmentLen)
buf = append(buf, assignmentLenBytes...)
buf = append(buf, member.MemberAssignment...)
}
// Authorized operations (v3+)
if apiVersion >= 3 {
opsCountBytes := make([]byte, 4)
binary.BigEndian.PutUint32(opsCountBytes, uint32(len(group.AuthorizedOps)))
buf = append(buf, opsCountBytes...)
for _, op := range group.AuthorizedOps {
opBytes := make([]byte, 4)
binary.BigEndian.PutUint32(opBytes, uint32(op))
buf = append(buf, opBytes...)
}
}
}
return buf
}
func (h *Handler) buildListGroupsResponse(response ListGroupsResponse, correlationID uint32, apiVersion uint16) []byte {
buf := make([]byte, 0, 512)
// Correlation ID
correlationIDBytes := make([]byte, 4)
binary.BigEndian.PutUint32(correlationIDBytes, correlationID)
buf = append(buf, correlationIDBytes...)
// Throttle time (v1+)
if apiVersion >= 1 {
throttleBytes := make([]byte, 4)
binary.BigEndian.PutUint32(throttleBytes, uint32(response.ThrottleTimeMs))
buf = append(buf, throttleBytes...)
}
// Error code
buf = append(buf, byte(response.ErrorCode>>8), byte(response.ErrorCode))
// Groups array
groupCountBytes := make([]byte, 4)
binary.BigEndian.PutUint32(groupCountBytes, uint32(len(response.Groups)))
buf = append(buf, groupCountBytes...)
for _, group := range response.Groups {
// Group ID
groupIDLen := uint16(len(group.GroupID))
buf = append(buf, byte(groupIDLen>>8), byte(groupIDLen))
buf = append(buf, []byte(group.GroupID)...)
// Protocol type
protocolTypeLen := uint16(len(group.ProtocolType))
buf = append(buf, byte(protocolTypeLen>>8), byte(protocolTypeLen))
buf = append(buf, []byte(group.ProtocolType)...)
// Group state (v4+)
if apiVersion >= 4 {
groupStateLen := uint16(len(group.GroupState))
buf = append(buf, byte(groupStateLen>>8), byte(groupStateLen))
buf = append(buf, []byte(group.GroupState)...)
}
}
return buf
}