github.com/hack0072008/kafka-go@v1.0.1/describegroups.go (about)

     1  package kafka
     2  
     3  import (
     4  	"bufio"
     5  	"bytes"
     6  	"context"
     7  	"fmt"
     8  	"net"
     9  
    10  	"github.com/hack0072008/kafka-go/protocol/describegroups"
    11  )
    12  
    13  // DescribeGroupsRequest is a request to the DescribeGroups API.
    14  type DescribeGroupsRequest struct {
    15  	// Addr is the address of the kafka broker to send the request to.
    16  	Addr net.Addr
    17  
    18  	// GroupIDs is a slice of groups to get details for.
    19  	GroupIDs []string
    20  }
    21  
    22  // DescribeGroupsResponse is a response from the DescribeGroups API.
    23  type DescribeGroupsResponse struct {
    24  	// Groups is a slice of details for the requested groups.
    25  	Groups []DescribeGroupsResponseGroup
    26  }
    27  
    28  // DescribeGroupsResponseGroup contains the response details for a single group.
    29  type DescribeGroupsResponseGroup struct {
    30  	// Error is set to a non-nil value if there was an error fetching the details
    31  	// for this group.
    32  	Error error
    33  
    34  	// GroupID is the ID of the group.
    35  	GroupID string
    36  
    37  	// GroupState is a description of the group state.
    38  	GroupState string
    39  
    40  	// Members contains details about each member of the group.
    41  	Members []DescribeGroupsResponseMember
    42  }
    43  
    44  // MemberInfo represents the membership information for a single group member.
    45  type DescribeGroupsResponseMember struct {
    46  	// MemberID is the ID of the group member.
    47  	MemberID string
    48  
    49  	// ClientID is the ID of the client that the group member is using.
    50  	ClientID string
    51  
    52  	// ClientHost is the host of the client that the group member is connecting from.
    53  	ClientHost string
    54  
    55  	// MemberMetadata contains metadata about this group member.
    56  	MemberMetadata DescribeGroupsResponseMemberMetadata
    57  
    58  	// MemberAssignments contains the topic partitions that this member is assigned to.
    59  	MemberAssignments DescribeGroupsResponseAssignments
    60  }
    61  
    62  // GroupMemberMetadata stores metadata associated with a group member.
    63  type DescribeGroupsResponseMemberMetadata struct {
    64  	// Version is the version of the metadata.
    65  	Version int
    66  
    67  	// Topics is the list of topics that the member is assigned to.
    68  	Topics []string
    69  
    70  	// UserData is the user data for the member.
    71  	UserData []byte
    72  
    73  	// OwnedPartitions contains the partitions owned by this group member; only set if
    74  	// consumers are using a cooperative rebalancing assignor protocol.
    75  	OwnedPartitions []DescribeGroupsResponseMemberMetadataOwnedPartition
    76  }
    77  
    78  type DescribeGroupsResponseMemberMetadataOwnedPartition struct {
    79  	// Topic is the name of the topic.
    80  	Topic string
    81  
    82  	// Partitions is the partitions that are owned by the group in the topic.
    83  	Partitions []int
    84  }
    85  
    86  // GroupMemberAssignmentsInfo stores the topic partition assignment data for a group member.
    87  type DescribeGroupsResponseAssignments struct {
    88  	// Version is the version of the assignments data.
    89  	Version int
    90  
    91  	// Topics contains the details of the partition assignments for each topic.
    92  	Topics []GroupMemberTopic
    93  
    94  	// UserData is the user data for the member.
    95  	UserData []byte
    96  }
    97  
    98  // GroupMemberTopic is a mapping from a topic to a list of partitions in the topic. It is used
    99  // to represent the topic partitions that have been assigned to a group member.
   100  type GroupMemberTopic struct {
   101  	// Topic is the name of the topic.
   102  	Topic string
   103  
   104  	// Partitions is a slice of partition IDs that this member is assigned to in the topic.
   105  	Partitions []int
   106  }
   107  
   108  // DescribeGroups calls the Kafka DescribeGroups API to get information about one or more
   109  // consumer groups. See https://kafka.apache.org/protocol#The_Messages_DescribeGroups for
   110  // more information.
   111  func (c *Client) DescribeGroups(
   112  	ctx context.Context,
   113  	req *DescribeGroupsRequest,
   114  ) (*DescribeGroupsResponse, error) {
   115  	protoResp, err := c.roundTrip(
   116  		ctx,
   117  		req.Addr,
   118  		&describegroups.Request{
   119  			Groups: req.GroupIDs,
   120  		},
   121  	)
   122  	if err != nil {
   123  		return nil, err
   124  	}
   125  	apiResp := protoResp.(*describegroups.Response)
   126  	resp := &DescribeGroupsResponse{}
   127  
   128  	for _, apiGroup := range apiResp.Groups {
   129  		group := DescribeGroupsResponseGroup{
   130  			Error:      makeError(apiGroup.ErrorCode, ""),
   131  			GroupID:    apiGroup.GroupID,
   132  			GroupState: apiGroup.GroupState,
   133  		}
   134  
   135  		for _, member := range apiGroup.Members {
   136  			decodedMetadata, err := decodeMemberMetadata(member.MemberMetadata)
   137  			if err != nil {
   138  				return nil, err
   139  			}
   140  			decodedAssignments, err := decodeMemberAssignments(member.MemberAssignment)
   141  			if err != nil {
   142  				return nil, err
   143  			}
   144  
   145  			group.Members = append(group.Members, DescribeGroupsResponseMember{
   146  				MemberID:          member.MemberID,
   147  				ClientID:          member.ClientID,
   148  				ClientHost:        member.ClientHost,
   149  				MemberAssignments: decodedAssignments,
   150  				MemberMetadata:    decodedMetadata,
   151  			})
   152  		}
   153  		resp.Groups = append(resp.Groups, group)
   154  	}
   155  
   156  	return resp, nil
   157  }
   158  
   159  // readFrom decodes an owned partition item from the member metadata.
   160  func (t *DescribeGroupsResponseMemberMetadataOwnedPartition) readFrom(r *bufio.Reader, size int) (remain int, err error) {
   161  	if remain, err = readString(r, size, &t.Topic); err != nil {
   162  		return
   163  	}
   164  	partitions := []int32{}
   165  
   166  	if remain, err = readInt32Array(r, remain, &partitions); err != nil {
   167  		return
   168  	}
   169  	for _, partition := range partitions {
   170  		t.Partitions = append(t.Partitions, int(partition))
   171  	}
   172  
   173  	return
   174  }
   175  
   176  // decodeMemberMetadata converts raw metadata bytes to a
   177  // DescribeGroupsResponseMemberMetadata struct.
   178  //
   179  // See https://github.com/apache/kafka/blob/2.4/clients/src/main/java/org/apache/kafka/clients/consumer/internals/ConsumerProtocol.java#L49
   180  // for protocol details.
   181  func decodeMemberMetadata(rawMetadata []byte) (DescribeGroupsResponseMemberMetadata, error) {
   182  	mm := DescribeGroupsResponseMemberMetadata{}
   183  
   184  	if len(rawMetadata) == 0 {
   185  		return mm, nil
   186  	}
   187  
   188  	buf := bytes.NewBuffer(rawMetadata)
   189  	bufReader := bufio.NewReader(buf)
   190  	remain := len(rawMetadata)
   191  
   192  	var err error
   193  	var version16 int16
   194  
   195  	if remain, err = readInt16(bufReader, remain, &version16); err != nil {
   196  		return mm, err
   197  	}
   198  	mm.Version = int(version16)
   199  
   200  	if remain, err = readStringArray(bufReader, remain, &mm.Topics); err != nil {
   201  		return mm, err
   202  	}
   203  	if remain, err = readBytes(bufReader, remain, &mm.UserData); err != nil {
   204  		return mm, err
   205  	}
   206  
   207  	if mm.Version == 1 && remain > 0 {
   208  		fn := func(r *bufio.Reader, size int) (fnRemain int, fnErr error) {
   209  			op := DescribeGroupsResponseMemberMetadataOwnedPartition{}
   210  			if fnRemain, fnErr = readString(r, size, &op.Topic); fnErr != nil {
   211  				return
   212  			}
   213  
   214  			ps := []int32{}
   215  			if fnRemain, fnErr = readInt32Array(r, fnRemain, &ps); fnErr != nil {
   216  				return
   217  			}
   218  
   219  			for _, p := range ps {
   220  				op.Partitions = append(op.Partitions, int(p))
   221  			}
   222  
   223  			mm.OwnedPartitions = append(mm.OwnedPartitions, op)
   224  			return
   225  		}
   226  
   227  		if remain, err = readArrayWith(bufReader, remain, fn); err != nil {
   228  			return mm, err
   229  		}
   230  	}
   231  
   232  	if remain != 0 {
   233  		return mm, fmt.Errorf("Got non-zero number of bytes remaining: %d", remain)
   234  	}
   235  
   236  	return mm, nil
   237  }
   238  
   239  // decodeMemberAssignments converts raw assignment bytes to a DescribeGroupsResponseAssignments
   240  // struct.
   241  //
   242  // See https://github.com/apache/kafka/blob/2.4/clients/src/main/java/org/apache/kafka/clients/consumer/internals/ConsumerProtocol.java#L49
   243  // for protocol details.
   244  func decodeMemberAssignments(rawAssignments []byte) (DescribeGroupsResponseAssignments, error) {
   245  	ma := DescribeGroupsResponseAssignments{}
   246  
   247  	if len(rawAssignments) == 0 {
   248  		return ma, nil
   249  	}
   250  
   251  	buf := bytes.NewBuffer(rawAssignments)
   252  	bufReader := bufio.NewReader(buf)
   253  	remain := len(rawAssignments)
   254  
   255  	var err error
   256  	var version16 int16
   257  
   258  	if remain, err = readInt16(bufReader, remain, &version16); err != nil {
   259  		return ma, err
   260  	}
   261  	ma.Version = int(version16)
   262  
   263  	fn := func(r *bufio.Reader, size int) (fnRemain int, fnErr error) {
   264  		item := GroupMemberTopic{}
   265  
   266  		if fnRemain, fnErr = readString(r, size, &item.Topic); fnErr != nil {
   267  			return
   268  		}
   269  
   270  		partitions := []int32{}
   271  
   272  		if fnRemain, fnErr = readInt32Array(r, fnRemain, &partitions); fnErr != nil {
   273  			return
   274  		}
   275  		for _, partition := range partitions {
   276  			item.Partitions = append(item.Partitions, int(partition))
   277  		}
   278  
   279  		ma.Topics = append(ma.Topics, item)
   280  		return
   281  	}
   282  	if remain, err = readArrayWith(bufReader, remain, fn); err != nil {
   283  		return ma, err
   284  	}
   285  
   286  	if remain, err = readBytes(bufReader, remain, &ma.UserData); err != nil {
   287  		return ma, err
   288  	}
   289  
   290  	if remain != 0 {
   291  		return ma, fmt.Errorf("Got non-zero number of bytes remaining: %d", remain)
   292  	}
   293  
   294  	return ma, nil
   295  }
   296  
   297  // readInt32Array reads an array of int32s. It's adapted from the implementation of
   298  // readStringArray.
   299  func readInt32Array(r *bufio.Reader, sz int, v *[]int32) (remain int, err error) {
   300  	var content []int32
   301  	fn := func(r *bufio.Reader, size int) (fnRemain int, fnErr error) {
   302  		var value int32
   303  		if fnRemain, fnErr = readInt32(r, size, &value); fnErr != nil {
   304  			return
   305  		}
   306  		content = append(content, value)
   307  		return
   308  	}
   309  	if remain, err = readArrayWith(r, sz, fn); err != nil {
   310  		return
   311  	}
   312  
   313  	*v = content
   314  	return
   315  }