@@ -12,11 +12,12 @@ import (
12
12
"gopkg.in/yaml.v2"
13
13
"io/ioutil"
14
14
"os"
15
+ "regexp"
15
16
"strings"
16
17
"text/template"
17
18
)
18
19
19
- const version string = "1.0.3 "
20
+ const version string = "1.0.4 "
20
21
21
22
var (
22
23
broker string
@@ -42,9 +43,10 @@ type arrFlags []string
42
43
43
44
// Spec contains the full structure of the manifest
44
45
type Spec struct {
45
- Topics []Topic `yaml:"topics" json:"topics"`
46
- Acls []Acl `yaml:"acls" json:"acls"`
47
- Connection Connection `yaml:"connection,omitempty" json:"connection,omitempty"`
46
+ Topics []Topic `yaml:"topics" json:"topics"`
47
+ Acls []Acl `yaml:"acls" json:"acls"`
48
+ ConsumerGroups []ConsumerGroup `yaml:"consumer-groups,omitempty" json:"consumer-groups,omitempty"`
49
+ Connection Connection `yaml:"connection,omitempty" json:"connection,omitempty"`
48
50
}
49
51
50
52
// Topic describes single topic
@@ -54,6 +56,16 @@ type Topic struct {
54
56
ReplicationFactor int `yaml:"replication_factor" json:"replication_factor"`
55
57
Configs map [string ]string `yaml:"configs" json:"configs"`
56
58
State string `yaml:"state,omitempty" json:"state,omitempty"`
59
+ PatternType string `yaml:"patternType,omitempty" json:"patternType,omitempty"`
60
+ Matched []string `yaml:"matched,omitempty" json:"matched,omitempty"`
61
+ }
62
+
63
+ // ConsumerGroup describes a consumer group to be deleted
64
+ type ConsumerGroup struct {
65
+ Name string `yaml:"name" json:"name"`
66
+ State string `yaml:"state,omitempty" json:"state,omitempty"`
67
+ PatternType string `yaml:"patternType,omitempty" json:"patternType,omitempty"`
68
+ Matched []string `yaml:"matched,omitempty" json:"matched,omitempty"`
57
69
}
58
70
59
71
// Acl describes single ACL
@@ -351,11 +363,52 @@ func applySpecFile() error {
351
363
352
364
// Iterate over topics
353
365
for _ , topic := range spec .Topics {
366
+
354
367
if topic .State == "absent" {
355
- fmt .Printf ("TASK [TOPIC : Delete topic %s] %s\n " , topic .Name , strings .Repeat ("*" , 52 ))
368
+ topic .PatternType = strings .ToLower (topic .PatternType )
369
+ if topic .PatternType == "prefixed" || topic .PatternType == "match" {
370
+ // Delete topics by pattern
371
+ fmt .Printf ("TASK [TOPIC : Delete topics %s by %s] %s\n " , topic .PatternType , topic .Name , strings .Repeat ("*" , 42 ))
372
+ var currentState = Ok
373
+ var currentError = ""
374
+ for currentTopicName , _ := range currentTopics {
375
+ var matched = false
376
+ if topic .PatternType == "prefixed" {
377
+ matched = strings .HasPrefix (currentTopicName , topic .Name )
378
+ } else if topic .PatternType == "match" {
379
+ matched , _ = regexp .MatchString (topic .Name , currentTopicName )
380
+ }
381
+ if matched {
382
+ topic .Matched = append (topic .Matched , currentTopicName )
383
+ err := deleteTopic (currentTopicName , admin )
384
+ if err != nil {
385
+ numError ++
386
+ currentState = Error
387
+ currentError = err .Error ()
388
+ if errorStop {
389
+ printResult (Error , broker , err .Error (), topic )
390
+ break
391
+ }
392
+ } else {
393
+ numChanged ++
394
+ if currentState != Error {
395
+ currentState = Changed
396
+ }
397
+ }
398
+ }
399
+ }
400
+ printResult (currentState , broker , currentError , topic )
401
+ if currentState == Ok {
402
+ numOk ++
403
+ }
404
+ continue
405
+ } else {
406
+ fmt .Printf ("TASK [TOPIC : Delete topic %s] %s\n " , topic .Name , strings .Repeat ("*" , 52 ))
407
+ }
356
408
} else {
357
409
topic .State = "present"
358
410
}
411
+
359
412
currentTopic , found := currentTopics [topic .Name ]
360
413
361
414
if ! found {
@@ -466,65 +519,125 @@ func applySpecFile() error {
466
519
}
467
520
}
468
521
469
- // Get current ACLs from broker
470
- currentAcls , err := listAllAcls (admin )
471
- if err != nil {
472
- return err
473
- }
522
+ if len (spec .ConsumerGroups ) > 0 {
523
+ // Get current consumer-groups from broker
524
+ currentGroups , err := (* admin ).ListConsumerGroups ()
525
+ if err != nil {
526
+ return errors .New ("Can't list consumer-groups: " + err .Error ())
527
+ }
474
528
475
- // Iterate over ACLs
476
- breakLoop := false
477
- for _ , acl := range spec .Acls {
478
- principal := acl .Principal
479
- for _ , permission := range acl .Permissions {
480
- resource := permission .Resource
481
- for i , rule := range append (permission .Allow , permission .Deny ... ) {
482
- sacl := SingleACL {
483
- Principal : principal ,
484
- Resource : resource ,
485
- Operation : getOperation (rule ),
486
- Host : getHost (rule ),
529
+ // Iterate over consumer-groups
530
+ for _ , group := range spec .ConsumerGroups {
531
+ if group .State != "absent" {
532
+ return errors .New ("Consumer-groups support only state=absent" )
533
+ }
534
+ group .PatternType = strings .ToLower (group .PatternType )
535
+ if group .PatternType != "prefixed" && group .PatternType != "match" {
536
+ group .PatternType = "literal"
537
+ }
538
+
539
+ // Delete consumer-groups by pattern
540
+ fmt .Printf ("TASK [CONSUMER-GROUP : Delete consumer-group %s by %s] %s\n " , group .PatternType , group .Name , strings .Repeat ("*" , 25 ))
541
+ var currentState = Ok
542
+ var currentError = ""
543
+ for currentGroupName , _ := range currentGroups {
544
+ var matched = false
545
+ if group .PatternType == "prefixed" {
546
+ matched = strings .HasPrefix (currentGroupName , group .Name )
547
+ } else if group .PatternType == "match" {
548
+ matched , _ = regexp .MatchString (group .Name , currentGroupName )
549
+ } else if currentGroupName == group .Name {
550
+ matched = true
487
551
}
488
- if permission .State == "absent" {
489
- sacl .State = "absent"
490
- } else {
491
- sacl .State = "present"
492
- // Host can be unset, we'll treat this as * for creating
493
- if sacl .Host == "" {
494
- sacl .Host = "*"
552
+
553
+ if matched {
554
+ group .Matched = append (group .Matched , currentGroupName )
555
+ err := DeleteConsumerGroup (currentGroupName , admin )
556
+ if err != nil {
557
+ numError ++
558
+ currentState = Error
559
+ currentError = err .Error ()
560
+ if errorStop {
561
+ printResult (Error , broker , err .Error (), group )
562
+ break
563
+ }
564
+ } else {
565
+ numChanged ++
566
+ if currentState != Error {
567
+ currentState = Changed
568
+ }
495
569
}
496
570
}
497
- if i < len (permission .Allow ) {
498
- sacl .PermissionType = "ALLOW"
499
- } else {
500
- sacl .PermissionType = "DENY"
501
- }
571
+ }
572
+ printResult (currentState , broker , currentError , group )
573
+ if currentState == Ok {
574
+ numOk ++
575
+ }
576
+ }
577
+ }
502
578
503
- result , err := alignAcl (admin , & currentAcls , sacl )
504
- if result == Ok {
505
- printResult (Ok , broker , "" , sacl )
506
- numOk ++
507
- } else if err != nil {
508
- printResult (Error , broker , err .Error (), sacl )
509
- numError ++
510
- if errorStop {
511
- breakLoop = true
512
- break
579
+ if len (spec .Acls ) > 0 {
580
+
581
+ // Get current ACLs from broker
582
+ currentAcls , err := listAllAcls (admin )
583
+ if err != nil {
584
+ return err
585
+ }
586
+
587
+ // Iterate over ACLs
588
+ breakLoop := false
589
+ for _ , acl := range spec .Acls {
590
+ principal := acl .Principal
591
+ for _ , permission := range acl .Permissions {
592
+ resource := permission .Resource
593
+ for i , rule := range append (permission .Allow , permission .Deny ... ) {
594
+ sacl := SingleACL {
595
+ Principal : principal ,
596
+ Resource : resource ,
597
+ Operation : getOperation (rule ),
598
+ Host : getHost (rule ),
513
599
}
514
- } else {
515
- printResult (result , broker , "" , sacl )
516
- numChanged ++
600
+ if permission .State == "absent" {
601
+ sacl .State = "absent"
602
+ } else {
603
+ sacl .State = "present"
604
+ // Host can be unset, we'll treat this as * for creating
605
+ if sacl .Host == "" {
606
+ sacl .Host = "*"
607
+ }
608
+ }
609
+ if i < len (permission .Allow ) {
610
+ sacl .PermissionType = "ALLOW"
611
+ } else {
612
+ sacl .PermissionType = "DENY"
613
+ }
614
+
615
+ result , err := alignAcl (admin , & currentAcls , sacl )
616
+ if result == Ok {
617
+ printResult (Ok , broker , "" , sacl )
618
+ numOk ++
619
+ } else if err != nil {
620
+ printResult (Error , broker , err .Error (), sacl )
621
+ numError ++
622
+ if errorStop {
623
+ breakLoop = true
624
+ break
625
+ }
626
+ } else {
627
+ printResult (result , broker , "" , sacl )
628
+ numChanged ++
629
+ }
630
+ }
631
+ if breakLoop {
632
+ break
517
633
}
518
634
}
519
635
if breakLoop {
520
636
break
521
637
}
522
- }
523
- if breakLoop {
524
- break
638
+
525
639
}
526
640
}
527
-
528
641
printSummary (broker , numOk , numChanged , numError )
529
642
if numError > 0 {
530
643
return errors .New ("" )
@@ -704,6 +817,11 @@ func deleteTopic(topic string, admin *sarama.ClusterAdmin) error {
704
817
return err
705
818
}
706
819
820
+ func DeleteConsumerGroup (group string , admin * sarama.ClusterAdmin ) error {
821
+ err := (* admin ).DeleteConsumerGroup (group )
822
+ return err
823
+ }
824
+
707
825
func printResult (color string , broker string , msg string , debug interface {}) {
708
826
var status string
709
827
switch color {
0 commit comments