Skip to content

Commit fb8b9b5

Browse files
committed
fix: use the broker for any admin on BrokerConfig
When operating on Broker configuration via the Admin API, the request _must_ be sent to the specific broker that the change applies to, _not_ just (as usual) to the Controller.
1 parent a20d267 commit fb8b9b5

File tree

4 files changed

+196
-18
lines changed

4 files changed

+196
-18
lines changed

admin.go

+45-2
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@ package sarama
22

33
import (
44
"errors"
5+
"fmt"
56
"math/rand"
7+
"strconv"
68
"sync"
79
)
810

@@ -226,6 +228,16 @@ func (ca *clusterAdmin) DescribeCluster() (brokers []*Broker, controllerID int32
226228
return response.Brokers, response.ControllerID, nil
227229
}
228230

231+
func (ca *clusterAdmin) findBroker(id int32) (*Broker, error) {
232+
brokers := ca.client.Brokers()
233+
for _, b := range brokers {
234+
if b.ID() == id {
235+
return b, nil
236+
}
237+
}
238+
return nil, fmt.Errorf("could not find broker id %d", id)
239+
}
240+
229241
func (ca *clusterAdmin) findAnyBroker() (*Broker, error) {
230242
brokers := ca.client.Brokers()
231243
if len(brokers) > 0 {
@@ -432,6 +444,13 @@ func (ca *clusterAdmin) DeleteRecords(topic string, partitionOffsets map[int32]i
432444
return nil
433445
}
434446

447+
// Returns a bool indicating whether the resource request needs to go to a
448+
// specific broker
449+
func dependsOnSpecificNode(resource ConfigResource) bool {
450+
return (resource.Type == BrokerResource && resource.Name != "") ||
451+
resource.Type == BrokerLoggerResource
452+
}
453+
435454
func (ca *clusterAdmin) DescribeConfig(resource ConfigResource) ([]ConfigEntry, error) {
436455

437456
var entries []ConfigEntry
@@ -442,11 +461,23 @@ func (ca *clusterAdmin) DescribeConfig(resource ConfigResource) ([]ConfigEntry,
442461
Resources: resources,
443462
}
444463

445-
b, err := ca.Controller()
464+
var (
465+
b *Broker
466+
err error
467+
)
468+
469+
// DescribeConfig of broker/broker logger must be sent to the broker in question
470+
if dependsOnSpecificNode(resource) {
471+
id, _ := strconv.Atoi(resource.Name)
472+
b, err = ca.findBroker(int32(id))
473+
} else {
474+
b, err = ca.findAnyBroker()
475+
}
446476
if err != nil {
447477
return nil, err
448478
}
449479

480+
_ = b.Open(ca.client.Config())
450481
rsp, err := b.DescribeConfigs(request)
451482
if err != nil {
452483
return nil, err
@@ -479,11 +510,23 @@ func (ca *clusterAdmin) AlterConfig(resourceType ConfigResourceType, name string
479510
ValidateOnly: validateOnly,
480511
}
481512

482-
b, err := ca.Controller()
513+
var (
514+
b *Broker
515+
err error
516+
)
517+
518+
// AlterConfig of broker/broker logger must be sent to the broker in question
519+
if dependsOnSpecificNode(ConfigResource{Name: name, Type: resourceType}) {
520+
id, _ := strconv.Atoi(name)
521+
b, err = ca.findBroker(int32(id))
522+
} else {
523+
b, err = ca.findAnyBroker()
524+
}
483525
if err != nil {
484526
return err
485527
}
486528

529+
_ = b.Open(ca.client.Config())
487530
rsp, err := b.AlterConfigs(request)
488531
if err != nil {
489532
return err

admin_test.go

+113
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@ package sarama
22

33
import (
44
"errors"
5+
"fmt"
6+
"io/ioutil"
7+
"log"
8+
"os"
59
"strings"
610
"testing"
711
)
@@ -511,6 +515,61 @@ func TestClusterAdminDescribeConfig(t *testing.T) {
511515
}
512516
}
513517

518+
// TestClusterAdminDescribeBrokerConfig ensures that a describe broker config
519+
// is sent to the broker in the resource struct, _not_ the controller
520+
func TestClusterAdminDescribeBrokerConfig(t *testing.T) {
521+
Logger = log.New(os.Stdout, fmt.Sprintf("[%s] ", t.Name()), log.LstdFlags)
522+
defer func() { Logger = log.New(ioutil.Discard, "[Sarama] ", log.LstdFlags) }()
523+
524+
controllerBroker := NewMockBroker(t, 1)
525+
defer controllerBroker.Close()
526+
configBroker := NewMockBroker(t, 2)
527+
defer configBroker.Close()
528+
529+
controllerBroker.SetHandlerByMap(map[string]MockResponse{
530+
"MetadataRequest": NewMockMetadataResponse(t).
531+
SetController(controllerBroker.BrokerID()).
532+
SetBroker(controllerBroker.Addr(), controllerBroker.BrokerID()).
533+
SetBroker(configBroker.Addr(), configBroker.BrokerID()),
534+
})
535+
536+
configBroker.SetHandlerByMap(map[string]MockResponse{
537+
"MetadataRequest": NewMockMetadataResponse(t).
538+
SetController(controllerBroker.BrokerID()).
539+
SetBroker(controllerBroker.Addr(), controllerBroker.BrokerID()).
540+
SetBroker(configBroker.Addr(), configBroker.BrokerID()),
541+
"DescribeConfigsRequest": NewMockDescribeConfigsResponse(t),
542+
})
543+
544+
config := NewConfig()
545+
config.Version = V1_0_0_0
546+
admin, err := NewClusterAdmin(
547+
[]string{
548+
controllerBroker.Addr(),
549+
configBroker.Addr(),
550+
}, config)
551+
if err != nil {
552+
t.Fatal(err)
553+
}
554+
555+
for _, resourceType := range []ConfigResourceType{BrokerResource, BrokerLoggerResource} {
556+
resource := ConfigResource{Name: "2", Type: resourceType}
557+
entries, err := admin.DescribeConfig(resource)
558+
if err != nil {
559+
t.Fatal(err)
560+
}
561+
562+
if len(entries) <= 0 {
563+
t.Fatal(errors.New("no resource present"))
564+
}
565+
}
566+
567+
err = admin.Close()
568+
if err != nil {
569+
t.Fatal(err)
570+
}
571+
}
572+
514573
func TestClusterAdminAlterConfig(t *testing.T) {
515574
seedBroker := NewMockBroker(t, 1)
516575
defer seedBroker.Close()
@@ -544,6 +603,60 @@ func TestClusterAdminAlterConfig(t *testing.T) {
544603
}
545604
}
546605

606+
func TestClusterAdminAlterBrokerConfig(t *testing.T) {
607+
controllerBroker := NewMockBroker(t, 1)
608+
defer controllerBroker.Close()
609+
configBroker := NewMockBroker(t, 2)
610+
defer configBroker.Close()
611+
612+
controllerBroker.SetHandlerByMap(map[string]MockResponse{
613+
"MetadataRequest": NewMockMetadataResponse(t).
614+
SetController(controllerBroker.BrokerID()).
615+
SetBroker(controllerBroker.Addr(), controllerBroker.BrokerID()).
616+
SetBroker(configBroker.Addr(), configBroker.BrokerID()),
617+
})
618+
configBroker.SetHandlerByMap(map[string]MockResponse{
619+
"MetadataRequest": NewMockMetadataResponse(t).
620+
SetController(controllerBroker.BrokerID()).
621+
SetBroker(controllerBroker.Addr(), controllerBroker.BrokerID()).
622+
SetBroker(configBroker.Addr(), configBroker.BrokerID()),
623+
"AlterConfigsRequest": NewMockAlterConfigsResponse(t),
624+
})
625+
626+
config := NewConfig()
627+
config.Version = V1_0_0_0
628+
admin, err := NewClusterAdmin(
629+
[]string{
630+
controllerBroker.Addr(),
631+
configBroker.Addr(),
632+
}, config)
633+
if err != nil {
634+
t.Fatal(err)
635+
}
636+
637+
var value string
638+
entries := make(map[string]*string)
639+
value = "3"
640+
entries["min.insync.replicas"] = &value
641+
642+
for _, resourceType := range []ConfigResourceType{BrokerResource, BrokerLoggerResource} {
643+
resource := ConfigResource{Name: "2", Type: resourceType}
644+
err = admin.AlterConfig(
645+
resource.Type,
646+
resource.Name,
647+
entries,
648+
false)
649+
if err != nil {
650+
t.Fatal(err)
651+
}
652+
}
653+
654+
err = admin.Close()
655+
if err != nil {
656+
t.Fatal(err)
657+
}
658+
}
659+
547660
func TestClusterAdminCreateAcl(t *testing.T) {
548661
seedBroker := NewMockBroker(t, 1)
549662
defer seedBroker.Close()

config_resource_type.go

+11-15
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,18 @@
11
package sarama
22

3-
//ConfigResourceType is a type for config resource
3+
// ConfigResourceType is a type for resources that have configs.
44
type ConfigResourceType int8
55

6-
// Taken from :
7-
// https://cwiki.apache.org/confluence/display/KAFKA/KIP-133%3A+Describe+and+Alter+Configs+Admin+APIs#KIP-133:DescribeandAlterConfigsAdminAPIs-WireFormattypes
6+
// Taken from:
7+
// https://github.com/apache/kafka/blob/ed7c071e07f1f90e4c2895582f61ca090ced3c42/clients/src/main/java/org/apache/kafka/common/config/ConfigResource.java#L32-L55
88

99
const (
10-
//UnknownResource constant type
11-
UnknownResource ConfigResourceType = iota
12-
//AnyResource constant type
13-
AnyResource
14-
//TopicResource constant type
15-
TopicResource
16-
//GroupResource constant type
17-
GroupResource
18-
//ClusterResource constant type
19-
ClusterResource
20-
//BrokerResource constant type
21-
BrokerResource
10+
// UnknownResource constant type
11+
UnknownResource ConfigResourceType = 0
12+
// TopicResource constant type
13+
TopicResource ConfigResourceType = 2
14+
// BrokerResource constant type
15+
BrokerResource ConfigResourceType = 4
16+
// BrokerLoggerResource constant type
17+
BrokerLoggerResource ConfigResourceType = 8
2218
)

mockresponses.go

+27-1
Original file line numberDiff line numberDiff line change
@@ -736,6 +736,32 @@ func (mr *MockDescribeConfigsResponse) For(reqBody versionedDecoder) encoder {
736736
for _, r := range req.Resources {
737737
var configEntries []*ConfigEntry
738738
switch r.Type {
739+
case BrokerResource:
740+
configEntries = append(configEntries,
741+
&ConfigEntry{
742+
Name: "min.insync.replicas",
743+
Value: "2",
744+
ReadOnly: false,
745+
Default: false,
746+
},
747+
)
748+
res.Resources = append(res.Resources, &ResourceResponse{
749+
Name: r.Name,
750+
Configs: configEntries,
751+
})
752+
case BrokerLoggerResource:
753+
configEntries = append(configEntries,
754+
&ConfigEntry{
755+
Name: "kafka.controller.KafkaController",
756+
Value: "DEBUG",
757+
ReadOnly: false,
758+
Default: false,
759+
},
760+
)
761+
res.Resources = append(res.Resources, &ResourceResponse{
762+
Name: r.Name,
763+
Configs: configEntries,
764+
})
739765
case TopicResource:
740766
configEntries = append(configEntries,
741767
&ConfigEntry{Name: "max.message.bytes",
@@ -777,7 +803,7 @@ func (mr *MockAlterConfigsResponse) For(reqBody versionedDecoder) encoder {
777803

778804
for _, r := range req.Resources {
779805
res.Resources = append(res.Resources, &AlterConfigsResourceResponse{Name: r.Name,
780-
Type: TopicResource,
806+
Type: r.Type,
781807
ErrorMsg: "",
782808
})
783809
}

0 commit comments

Comments
 (0)