github.com/crowdsecurity/crowdsec@v1.6.1/pkg/acquisition/modules/kafka/kafka_test.go (about)

     1  package kafkaacquisition
     2  
     3  import (
     4  	"context"
     5  	"net"
     6  	"runtime"
     7  	"strconv"
     8  	"testing"
     9  	"time"
    10  
    11  	"github.com/segmentio/kafka-go"
    12  	log "github.com/sirupsen/logrus"
    13  	"github.com/stretchr/testify/require"
    14  	"gopkg.in/tomb.v2"
    15  
    16  	"github.com/crowdsecurity/go-cs-lib/cstest"
    17  
    18  	"github.com/crowdsecurity/crowdsec/pkg/acquisition/configuration"
    19  	"github.com/crowdsecurity/crowdsec/pkg/types"
    20  )
    21  
    22  func TestConfigure(t *testing.T) {
    23  	tests := []struct {
    24  		config      string
    25  		expectedErr string
    26  	}{
    27  		{
    28  			config: `
    29  foobar: bla
    30  source: kafka`,
    31  			expectedErr: "line 2: field foobar not found in type kafkaacquisition.KafkaConfiguration",
    32  		},
    33  		{
    34  			config:      `source: kafka`,
    35  			expectedErr: "cannot create a kafka reader with an empty list of broker addresses",
    36  		},
    37  		{
    38  			config: `
    39  source: kafka
    40  brokers:
    41    - bla
    42  timeout: 5`,
    43  			expectedErr: "cannot create a kafka reader with am empty topic",
    44  		},
    45  		{
    46  			config: `
    47  source: kafka
    48  brokers:
    49    - bla
    50  topic: toto
    51  timeout: aa`,
    52  			expectedErr: "cannot create kafka dialer: strconv.Atoi: parsing \"aa\": invalid syntax",
    53  		},
    54  		{
    55  			config: `
    56  source: kafka
    57  brokers:
    58    - localhost:9092
    59  topic: crowdsec`,
    60  			expectedErr: "",
    61  		},
    62  		{
    63  			config: `
    64  source: kafka
    65  brokers:
    66    - localhost:9092
    67  topic: crowdsec
    68  partition: 1
    69  group_id: crowdsec`,
    70  			expectedErr: "cannote create kafka reader: cannot specify both group_id and partition",
    71  		},
    72  	}
    73  
    74  	subLogger := log.WithFields(log.Fields{
    75  		"type": "kafka",
    76  	})
    77  	for _, test := range tests {
    78  		k := KafkaSource{}
    79  		err := k.Configure([]byte(test.config), subLogger, configuration.METRICS_NONE)
    80  		cstest.AssertErrorContains(t, err, test.expectedErr)
    81  	}
    82  }
    83  
    84  func writeToKafka(w *kafka.Writer, logs []string) {
    85  
    86  	for idx, log := range logs {
    87  		err := w.WriteMessages(context.Background(), kafka.Message{
    88  			Key: []byte(strconv.Itoa(idx)),
    89  			// create an arbitrary message payload for the value
    90  			Value: []byte(log),
    91  		})
    92  		if err != nil {
    93  			panic("could not write message " + err.Error())
    94  		}
    95  	}
    96  }
    97  
    98  func createTopic(topic string, broker string) {
    99  	conn, err := kafka.Dial("tcp", broker)
   100  	if err != nil {
   101  		panic(err)
   102  	}
   103  	defer conn.Close()
   104  
   105  	controller, err := conn.Controller()
   106  	if err != nil {
   107  		panic(err)
   108  	}
   109  	var controllerConn *kafka.Conn
   110  	controllerConn, err = kafka.Dial("tcp", net.JoinHostPort(controller.Host, strconv.Itoa(controller.Port)))
   111  	if err != nil {
   112  		panic(err)
   113  	}
   114  	defer controllerConn.Close()
   115  
   116  	topicConfigs := []kafka.TopicConfig{
   117  		{
   118  			Topic:             topic,
   119  			NumPartitions:     1,
   120  			ReplicationFactor: 1,
   121  		},
   122  	}
   123  
   124  	err = controllerConn.CreateTopics(topicConfigs...)
   125  	if err != nil {
   126  		panic(err)
   127  	}
   128  }
   129  
   130  func TestStreamingAcquisition(t *testing.T) {
   131  	if runtime.GOOS == "windows" {
   132  		t.Skip("Skipping test on windows")
   133  	}
   134  	tests := []struct {
   135  		name          string
   136  		logs          []string
   137  		expectedLines int
   138  		expectedErr   string
   139  	}{
   140  		{
   141  			name: "valid msgs",
   142  			logs: []string{
   143  				"message 1",
   144  				"message 2",
   145  				"message 3",
   146  			},
   147  			expectedLines: 3,
   148  		},
   149  	}
   150  
   151  	subLogger := log.WithFields(log.Fields{
   152  		"type": "kafka",
   153  	})
   154  
   155  	createTopic("crowdsecplaintext", "localhost:9092")
   156  
   157  	w := kafka.NewWriter(kafka.WriterConfig{
   158  		Brokers: []string{"localhost:9092"},
   159  		Topic:   "crowdsecplaintext",
   160  	})
   161  	if w == nil {
   162  		log.Fatalf("Unable to setup a kafka producer")
   163  	}
   164  
   165  	for _, ts := range tests {
   166  		ts := ts
   167  		t.Run(ts.name, func(t *testing.T) {
   168  			k := KafkaSource{}
   169  			err := k.Configure([]byte(`
   170  source: kafka
   171  brokers:
   172    - localhost:9092
   173  topic: crowdsecplaintext`), subLogger, configuration.METRICS_NONE)
   174  			if err != nil {
   175  				t.Fatalf("could not configure kafka source : %s", err)
   176  			}
   177  			tomb := tomb.Tomb{}
   178  			out := make(chan types.Event)
   179  			err = k.StreamingAcquisition(out, &tomb)
   180  			cstest.AssertErrorContains(t, err, ts.expectedErr)
   181  
   182  			actualLines := 0
   183  			go writeToKafka(w, ts.logs)
   184  		READLOOP:
   185  			for {
   186  				select {
   187  				case <-out:
   188  					actualLines++
   189  				case <-time.After(2 * time.Second):
   190  					break READLOOP
   191  				}
   192  			}
   193  			require.Equal(t, ts.expectedLines, actualLines)
   194  			tomb.Kill(nil)
   195  			tomb.Wait()
   196  		})
   197  	}
   198  
   199  }
   200  
   201  func TestStreamingAcquisitionWithSSL(t *testing.T) {
   202  	if runtime.GOOS == "windows" {
   203  		t.Skip("Skipping test on windows")
   204  	}
   205  	tests := []struct {
   206  		name          string
   207  		logs          []string
   208  		expectedLines int
   209  		expectedErr   string
   210  	}{
   211  		{
   212  			name: "valid msgs",
   213  			logs: []string{
   214  				"message 1",
   215  				"message 2",
   216  			},
   217  			expectedLines: 2,
   218  		},
   219  	}
   220  
   221  	subLogger := log.WithFields(log.Fields{
   222  		"type": "kafka",
   223  	})
   224  
   225  	createTopic("crowdsecssl", "localhost:9092")
   226  
   227  	w2 := kafka.NewWriter(kafka.WriterConfig{
   228  		Brokers: []string{"localhost:9092"},
   229  		Topic:   "crowdsecssl",
   230  	})
   231  	if w2 == nil {
   232  		log.Fatalf("Unable to setup a kafka producer")
   233  	}
   234  
   235  	for _, ts := range tests {
   236  		ts := ts
   237  		t.Run(ts.name, func(t *testing.T) {
   238  			k := KafkaSource{}
   239  			err := k.Configure([]byte(`
   240  source: kafka
   241  brokers:
   242    - localhost:9093
   243  topic: crowdsecssl
   244  tls:
   245    insecure_skip_verify: true
   246    client_cert: ./testdata/kafkaClient.certificate.pem
   247    client_key: ./testdata/kafkaClient.key
   248    ca_cert: ./testdata/snakeoil-ca-1.crt
   249    `), subLogger, configuration.METRICS_NONE)
   250  			if err != nil {
   251  				t.Fatalf("could not configure kafka source : %s", err)
   252  			}
   253  			tomb := tomb.Tomb{}
   254  			out := make(chan types.Event)
   255  			err = k.StreamingAcquisition(out, &tomb)
   256  			cstest.AssertErrorContains(t, err, ts.expectedErr)
   257  
   258  			actualLines := 0
   259  			go writeToKafka(w2, ts.logs)
   260  		READLOOP:
   261  			for {
   262  				select {
   263  				case <-out:
   264  					actualLines++
   265  				case <-time.After(2 * time.Second):
   266  					break READLOOP
   267  				}
   268  			}
   269  			require.Equal(t, ts.expectedLines, actualLines)
   270  			tomb.Kill(nil)
   271  			tomb.Wait()
   272  		})
   273  	}
   274  
   275  }